From a1dedf0d26b5bc53fd3c29e9dec909a30cc43f0f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 20 Jun 2022 15:40:52 +0200 Subject: [PATCH 001/219] Bump version to v12.0.2.1 --- CHANGELOG.md | 16 ++++++++++++---- RELEASENOTES.md | 8 +++----- tasmota/include/tasmota_version.h | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 259e7efd9..bab39c676 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,18 +3,26 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development -## [12.0.1.2] +## [12.0.2.1] ### Added -- Command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found ### Changed - ### Fixed -- MQTT rc -4 on TLS connections regression from v12.0.0 (#15809) ### Removed +## [Released] + +## [12.0.2] 20220620 +- Release Paul + +## [12.0.1.2] 20220620 +### Added +- Command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found + +### Fixed +- MQTT rc -4 on connections regression from v12.0.0 (#15809) ## [Released] diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f6f443c67..92a45f93f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -72,7 +72,7 @@ Latest released binaries can be downloaded from - http://ota.tasmota.com/tasmota/release Historical binaries can be downloaded from -- http://ota.tasmota.com/tasmota/release-12.0.0 +- http://ota.tasmota.com/tasmota/release-12.0.2 The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` @@ -97,7 +97,7 @@ Latest released binaries can be downloaded from - http://ota.tasmota.com/tasmota32/release Historical binaries can be downloaded from -- http://ota.tasmota.com/tasmota32/release-12.0.0 +- http://ota.tasmota.com/tasmota32/release-12.0.2 The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota32/release/tasmota32.bin`` @@ -107,15 +107,13 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v12.0.1.2 +## Changelog v12.0.2.1 ### Added -- Command ``DnsTimeout 100..20000`` to change default DNS timeout from 1000 msec blocking if no DNS server found ### Breaking Changed ### Changed ### Fixed -- MQTT rc -4 on TLS connections regression from v12.0.0 [#15809](https://github.com/arendst/Tasmota/issues/15809) ### Removed diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index 2ff5a7c29..1ff29778d 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0C000102; // 12.0.1.2 +const uint32_t VERSION = 0x0C000201; // 12.0.2.1 #endif // _TASMOTA_VERSION_H_ From 174734855899ca4a864fdf3e98287e64ea34151c Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:44:11 +0200 Subject: [PATCH 002/219] Fixed ESP32 SendMail not working over ethernet Fixed ESP32 SendMail not working over ethernet (#15794) --- CHANGELOG.md | 3 +-- RELEASENOTES.md | 1 + lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab39c676..06cb69b50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed ### Fixed +- ESP32 SendMail not working over ethernet (#15794) ### Removed @@ -24,8 +25,6 @@ All notable changes to this project will be documented in this file. ### Fixed - MQTT rc -4 on connections regression from v12.0.0 (#15809) -## [Released] - ## [12.0.1] 20220617 - Release Paul diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 92a45f93f..1a0505579 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -115,5 +115,6 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo ### Changed ### Fixed +- ESP32 SendMail not working over ethernet [#15794](https://github.com/arendst/Tasmota/issues/15794) ### Removed diff --git a/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp b/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp index f1acdf53f..f36788385 100755 --- a/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp +++ b/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp @@ -5417,7 +5417,8 @@ bool ESP_Mail_Client::ethLinkUp() #if defined(ESP32) char *ip = strP(esp_mail_str_328); if (strcmp(ETH.localIP().toString().c_str(), ip) != 0) - ret = ETH.linkUp(); +// ret = ETH.linkUp(); + ret = true; delS(ip); #endif return ret; From f76400ba03d63edc46c2190a0f9367ca967704ee Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 00:32:56 -0500 Subject: [PATCH 003/219] Initial implementation of SM2335 LED driver. --- BUILDS.md | 1 + tasmota/include/tasmota_configurations.h | 4 + tasmota/include/tasmota_template.h | 7 + tasmota/language/af_AF.h | 2 + tasmota/language/bg_BG.h | 2 + tasmota/language/cs_CZ.h | 2 + tasmota/language/de_DE.h | 2 + tasmota/language/el_GR.h | 2 + tasmota/language/en_GB.h | 2 + tasmota/language/es_ES.h | 2 + tasmota/language/fr_FR.h | 2 + tasmota/language/fy_NL.h | 2 + tasmota/language/he_HE.h | 2 + tasmota/language/hu_HU.h | 2 + tasmota/language/it_IT.h | 2 + tasmota/language/ko_KO.h | 2 + tasmota/language/nl_NL.h | 2 + tasmota/language/pl_PL.h | 2 + tasmota/language/pt_BR.h | 2 + tasmota/language/pt_PT.h | 2 + tasmota/language/ro_RO.h | 2 + tasmota/language/ru_RU.h | 2 + tasmota/language/sk_SK.h | 2 + tasmota/language/sv_SE.h | 2 + tasmota/language/tr_TR.h | 2 + tasmota/language/uk_UA.h | 2 + tasmota/language/vi_VN.h | 2 + tasmota/language/zh_CN.h | 2 + tasmota/language/zh_TW.h | 2 + tasmota/my_user_config.h | 1 + tasmota/tasmota_support/support_features.ino | 4 +- tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino | 245 ++++++++++++++++++ tools/decode-status.py | 2 +- 33 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino diff --git a/BUILDS.md b/BUILDS.md index 89f522bce..576c8be6f 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -65,6 +65,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_MY92X1 | - | x / - | x | x | - | x | | USE_SM16716 | - | x / - | x | x | - | x | | USE_SM2135 | - | x / - | x | x | - | x | +| USE_SM2335 | - | x / - | x | x | - | x | | USE_BP5758D | - | x / - | x | x | - | x | | USE_SONOFF_L1 | - | x / - | x | x | - | x | | USE_ELECTRIQ_MOODL | - | x / - | x | x | - | x | diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index e0d00cb59..eb8af48f5 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -69,6 +69,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -403,6 +404,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -552,6 +554,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -697,6 +700,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 80cfa3fe8..70021178e 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -189,6 +189,7 @@ enum UserSelectablePins { GPIO_SDIO_CMD, GPIO_SDIO_CLK, GPIO_SDIO_D0, GPIO_SDIO_D1, GPIO_SDIO_D2, GPIO_SDIO_D3, // SD Card SDIO interface, including 1-bit and 4-bit modes GPIO_FLOWRATEMETER_IN, // Flowrate Meter GPIO_BP5758D_CLK, GPIO_BP5758D_DAT, // BP5758D PWM controller + GPIO_SM2335_CLK, GPIO_SM2335_DAT, // SM2335 PWM controller GPIO_SENSOR_END }; enum ProgramSelectablePins { @@ -422,6 +423,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SDIO_D3 "|" D_SENSOR_FLOWRATEMETER "|" D_SENSOR_BP5758D_CLK "|" D_SENSOR_BP5758D_DAT "|" + D_SENSOR_SM2335_CLK "|" D_SENSOR_SM2335_DAT "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -433,6 +435,7 @@ const char kSensorNamesFixed[] PROGMEM = #define MAX_WEBCAM_DATA 8 #define MAX_WEBCAM_HSD 3 #define MAX_SM2135_DAT 7 +#define MAX_SM2335_DAT 16 const uint16_t kGpioNiceList[] PROGMEM = { GPIO_NONE, // Not used @@ -663,6 +666,10 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SM2135_CLK), // SM2135 CLOCK AGPIO(GPIO_SM2135_DAT) + MAX_SM2135_DAT, // SM2135 DATA #endif // USE_SM2135 +#ifdef USE_SM2335 + AGPIO(GPIO_SM2335_CLK), // SM2335 CLOCK + AGPIO(GPIO_SM2335_DAT) + MAX_SM2335_DAT, // SM2335 DATA +#endif // USE_SM2335 #ifdef USE_BP5758D AGPIO(GPIO_BP5758D_CLK), // BP5758D CLOCK AGPIO(GPIO_BP5758D_DAT), // BP5758D DATA diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index d07ea47f8..a29ad6816 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "Diep slaap" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 155da5507..6498e414e 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index d57e42a24..e03d86fe0 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index da558a560..4148f0f84 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index bc9cae4d1..5cd5b3344 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 6f5b040ea..1be50befb 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index c730b9bdf..a517d24cc 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 2718ed619..d48ec0cb4 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 RX" #define D_SENSOR_SM2135_CLK "SM2135 CLK" #define D_SENSOR_SM2135_DAT "SM2135 DAT" +#define D_SENSOR_SM2335_CLK "SM2335 CLK" +#define D_SENSOR_SM2335_DAT "SM2335 DAT" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "Hibernation" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index 4890eb1fd..202fd7a53 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 7207d6197..0e3cefa3c 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index b964ef793..21c1ec2e2 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index c8d2085bc..e98b1a790 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -771,6 +771,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 - RX" #define D_SENSOR_SM2135_CLK "SM2135 - CLK" #define D_SENSOR_SM2135_DAT "SM2135 - DATI" +#define D_SENSOR_SM2335_CLK "SM2335 CLK" +#define D_SENSOR_SM2335_DAT "SM2335 DATI" #define D_SENSOR_BP5758D_CLK "BP5758D - CLK" #define D_SENSOR_BP5758D_DAT "BP5758D - DATI" #define D_SENSOR_DEEPSLEEP "Sleep profondo" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index aa4232772..5f5b167d1 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 4060e3cd7..9ca818f66 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 6bf3b66d2..0d596765e 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "Głęboko uśpiony" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index fc8f54ad7..9e3455a3b 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index d02cfb137..8b1acbe0b 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index b6fb28845..ef5923f05 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index d69309c34..735fb4b37 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 3820d88e8..41fabdaf3 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index e5ae351bb..66b34580d 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 942936615..150eaff3f 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index a0e5c5eaf..36f192931 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 8833d1046..9ef9fd9f0 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 7db58cadb..4ca980d70 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 7f067f023..277b3ca1c 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -773,6 +773,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 1ce6bf230..fcfad2090 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -556,6 +556,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb (+0k# code) #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index aae6e897c..2e6203aa8 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -815,7 +815,9 @@ void ResponseAppendFeatures(void) #if defined(USE_I2C) && defined(USE_HYT) feature8 |= 0x20000000; // xsns_97_hyt.ino #endif -// feature8 |= 0x40000000; +#if defined(USE_LIGHT) && defined(USE_SM2335) + feature8 |= 0x40000000; // xlgt_09_sm2335.ino +#endif // feature8 |= 0x80000000; } diff --git a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino new file mode 100644 index 000000000..f41913245 --- /dev/null +++ b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino @@ -0,0 +1,245 @@ +/* + xlgt_09_sm2335.ino - sm2335 five channel led support for Tasmota + + Copyright (C) 2021 Theo Arends and Cossid + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_LIGHT +#ifdef USE_SM2335 +/*********************************************************************************************\ + * SM2335 RGBCW Led bulbs like the SwitchBot Color Bulb + * + * SwitchBot Color Bulb + * {"NAME":"SwitchBot Color Bulb","GPIO":[0,0,0,0,9129,9088,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +\*********************************************************************************************/ + +#define XLGT_09 9 + +// 11 = identification | 0 = reserved | 00 = Standby | 000 = start at OUT1/5 +#define SM2335_ADDR_STANDBY 0xC0 // 11000000 0xC0 +// 11 = identification | 0 = reserved | 01 = 3 channels (RGB) | 000 = start at OUT1/5 +#define SM2335_ADDR_START_3CH 0xC8 // 11001000 0xC8 +// 11 = identification | 0 = reserved | 10 = 2 channels (CW) | 000 = start at OUT1/5 +#define SM2335_ADDR_START_2CH 0xD0 // 11010000 0xD0 +// 11 = identification | 0 = reserved | 11 = 5 channels (RGB+CW) | 000 = start at OUT1/5 +#define SM2335_ADDR_START_5CH 0xD8 // 11011000 0xD8 + +// Current values +#define SM2335_RGB_10MA_CW_5MA 0x0 // 0000 RGB 10mA | CW 5mA +#define SM2335_RGB_20MA_CW_10MA 0x1 // 0001 RGB 20mA | CW 10mA +#define SM2335_RGB_30MA_CW_15MA 0x2 // 0010 RGB 30mA | CW 15mA +#define SM2335_RGB_40MA_CW_20MA 0x3 // 0011 RGB 40mA | CW 20mA +#define SM2335_RGB_50MA_CW_25MA 0x4 // 0100 RGB 50mA | CW 25mA +#define SM2335_RGB_60MA_CW_30MA 0x5 // 0101 RGB 60mA | CW 30mA +#define SM2335_RGB_70MA_CW_35MA 0x6 // 0110 RGB 70mA | CW 35mA +#define SM2335_RGB_80MA_CW_40MA 0x7 // 0111 RGB 80mA | CW 40mA +#define SM2335_RGB_90MA_CW_45MA 0x8 // 1000 RGB 90mA | CW 45mA +#define SM2335_RGB_100MA_CW_50MA 0x9 // 1001 RGB 100mA | CW 50mA +#define SM2335_RGB_110MA_CW_55MA 0xA // 1010 RGB 110mA | CW 55mA +#define SM2335_RGB_120MA_CW_60MA 0xB // 1011 RGB 120mA | CW 60mA +#define SM2335_RGB_130MA_CW_65MA 0xC // 1100 RGB 130mA | CW 65mA +#define SM2335_RGB_140MA_CW_70MA 0xD // 1101 RGB 140mA | CW 70mA +#define SM2335_RGB_150MA_CW_75MA 0xE // 1110 RGB 150mA | CW 75mA +#define SM2335_RGB_160MA_CW_80MA 0xF // 1111 RGB 160mA | CW 80mA + +enum Sm2335Current { SM2335_10_5, SM2335_20_10, SM2335_30_15, SM2335_40_20, SM2335_50_25, + SM2335_60_30, SM2335_70_35, SM2335_80_40, SM2335_90_45, SM2335_100_50, SM2335_110_55, + SM2335_120_60, SM2335_130_65, SM2335_140_70, SM2335_150_75, SM2335_160_80 }; + +struct SM2335 { + uint8_t clk = 0; + uint8_t data = 0; + uint8_t currentValue; + uint8_t currentEnum = SM2335_50_25; +} Sm2335; + +/*********************************************************************************************\ + * SM2335 code - inspired by Bp5758d/SM2135 +\*********************************************************************************************/ +const uint8_t SM2335_DELAY = 2; + +void SM2335Init(void) { + pinMode(Sm2335.data, OUTPUT); + pinMode(Sm2335.clk, OUTPUT); + SM2335Stop(); +} + +void SM2335Write(uint8_t value) { + for (int bit_idx = 7; bit_idx >= 0; bit_idx--) { + bool bit = bitRead(value, bit_idx); + digitalWrite(Sm2335.data, bit); + delayMicroseconds(SM2335_DELAY); + digitalWrite(Sm2335.clk, HIGH); + delayMicroseconds(SM2335_DELAY); + digitalWrite(Sm2335.clk, LOW); + delayMicroseconds(SM2335_DELAY); + } + // Wait for ACK + pinMode(Sm2335.data, INPUT); + digitalWrite(Sm2335.clk, HIGH); + delayMicroseconds(SM2335_DELAY); + digitalWrite(Sm2335.clk, LOW); + delayMicroseconds(SM2335_DELAY); + pinMode(Sm2335.data, OUTPUT); +} + +void SM2335Start(uint16_t addr) { + digitalWrite(Sm2335.data, LOW); + delayMicroseconds(SM2335_DELAY); + digitalWrite(Sm2335.clk, LOW); + delayMicroseconds(SM2335_DELAY); + SM2335Write(addr); +} + +void SM2335Stop(void) { + digitalWrite(Sm2335.clk, HIGH); + delayMicroseconds(SM2335_DELAY); + digitalWrite(Sm2335.data, HIGH); + delayMicroseconds(SM2335_DELAY); +} + +/********************************************************************************************/ + +bool SM2335SetChannels(void) { + uint16_t *cur_col_10 = (uint16_t*)XdrvMailbox.command; + + // If we receive 0 for all channels, we'll assume that the lightbulb is off, and activate SM2335's standby mode. + if (cur_col_10[0] == 0 && cur_col_10[1] == 0 && cur_col_10[2] == 0 && cur_col_10[3] == 0 && cur_col_10[4] == 0) { + SM2335Start(SM2335_ADDR_STANDBY); + SM2335Write(0); // Current + SM2335Write(0); // Red 1/2 + SM2335Write(0); // Red 2/2 + SM2335Write(0); // Green 1/2 + SM2335Write(0); // Green 2/2 + SM2335Write(0); // Blue 1/2 + SM2335Write(0); // Blue 2/2 + SM2335Write(0); // Cold White 1/2 + SM2335Write(0); // Cold White 2/2 + SM2335Write(0); // Warm White 1/2 + SM2335Write(0); // WarmW hite 2/2 + SM2335Stop(); + return true; + } + + // Write the header activating all 5 channels + SM2335Start(SM2335_ADDR_START_5CH); + // Set the current defined in ModuleSelected. + SM2335Write(Sm2335.currentValue); + // Set RGB and CW grayscale. + SM2335Write((uint8_t)(cur_col_10[0] >> 8)); // Red 1/2 + SM2335Write((uint8_t)(cur_col_10[0] & 0xFF)); // Red 2/2 + SM2335Write((uint8_t)(cur_col_10[1] >> 8)); // Green 1/2 + SM2335Write((uint8_t)(cur_col_10[1] & 0xFF)); // Green 2/2 + SM2335Write((uint8_t)(cur_col_10[2] >> 8)); // Blue 1/2 + SM2335Write((uint8_t)(cur_col_10[2] & 0xFF)); // Blue 2/2 + SM2335Write((uint8_t)(cur_col_10[3] >> 8)); // Cold White 1/2 + SM2335Write((uint8_t)(cur_col_10[3] & 0xFF)); // Cold White 2/2 + SM2335Write((uint8_t)(cur_col_10[4] >> 8)); // Warm White 1/2 + SM2335Write((uint8_t)(cur_col_10[4] & 0xFF)); // Warm White 2/2 + + SM2335Stop(); + return true; +} + +void SM2335ModuleSelected(void) +{ + if (PinUsed(GPIO_SM2335_CLK) && PinUsed(GPIO_SM2335_DAT, GPIO_ANY)) { + Sm2335.clk = Pin(GPIO_SM2335_CLK); + Sm2335.data = Pin(GPIO_SM2335_DAT, GPIO_ANY); + + // See #define MAX_SM2335_DAT 16 in tasmota_template.h + Sm2335.currentEnum = GetPin(Sm2335.data) - AGPIO(GPIO_SM2335_DAT); // 0 .. 15 + + switch (Sm2335.currentEnum) { + case SM2335_10_5: + Sm2335.currentValue = (SM2335_RGB_10MA_CW_5MA << 4) | SM2335_RGB_10MA_CW_5MA; + break; + case SM2335_20_10: + Sm2335.currentValue = (SM2335_RGB_20MA_CW_10MA << 4) | SM2335_RGB_20MA_CW_10MA; + break; + case SM2335_30_15: + Sm2335.currentValue = (SM2335_RGB_30MA_CW_15MA << 4) | SM2335_RGB_30MA_CW_15MA; + break; + case SM2335_40_20: + Sm2335.currentValue = (SM2335_RGB_30MA_CW_15MA << 4) | SM2335_RGB_30MA_CW_15MA; + break; + case SM2335_50_25: + Sm2335.currentValue = (SM2335_RGB_50MA_CW_25MA << 4) | SM2335_RGB_50MA_CW_25MA; + break; + case SM2335_60_30: + Sm2335.currentValue = (SM2335_RGB_60MA_CW_30MA << 4) | SM2335_RGB_60MA_CW_30MA; + break; + case SM2335_70_35: + Sm2335.currentValue = (SM2335_RGB_70MA_CW_35MA << 4) | SM2335_RGB_70MA_CW_35MA; + break; + case SM2335_80_40: + Sm2335.currentValue = (SM2335_RGB_80MA_CW_40MA << 4) | SM2335_RGB_80MA_CW_40MA; + break; + case SM2335_90_45: + Sm2335.currentValue = (SM2335_RGB_90MA_CW_45MA << 4) | SM2335_RGB_90MA_CW_45MA; + break; + case SM2335_100_50: + Sm2335.currentValue = (SM2335_RGB_100MA_CW_50MA << 4) | SM2335_RGB_100MA_CW_50MA; + break; + case SM2335_110_55: + Sm2335.currentValue = (SM2335_RGB_110MA_CW_55MA << 4) | SM2335_RGB_110MA_CW_55MA; + break; + case SM2335_120_60: + Sm2335.currentValue = (SM2335_RGB_120MA_CW_60MA << 4) | SM2335_RGB_120MA_CW_60MA; + break; + case SM2335_130_65: + Sm2335.currentValue = (SM2335_RGB_130MA_CW_65MA << 4) | SM2335_RGB_130MA_CW_65MA; + break; + case SM2335_140_70: + Sm2335.currentValue = (SM2335_RGB_140MA_CW_70MA << 4) | SM2335_RGB_140MA_CW_70MA; + break; + case SM2335_150_75: + Sm2335.currentValue = (SM2335_RGB_150MA_CW_75MA << 4) | SM2335_RGB_150MA_CW_75MA; + break; + case SM2335_160_80: + Sm2335.currentValue = (SM2335_RGB_160MA_CW_80MA << 4) | SM2335_RGB_160MA_CW_80MA; + break; + } + + SM2335Init(); + + TasmotaGlobal.light_type = LT_RGBWC; + TasmotaGlobal.light_driver = XLGT_09; + AddLog(LOG_LEVEL_DEBUG, PSTR("LGT: SM2335 Found")); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt09(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = SM2335SetChannels(); + break; + case FUNC_MODULE_INIT: + SM2335ModuleSelected(); + break; + } + return result; +} + +#endif // USE_SM2335 +#endif // USE_LIGHT diff --git a/tools/decode-status.py b/tools/decode-status.py index cde2a575e..5a7bba7d6 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -280,7 +280,7 @@ a_features = [[ "USE_HDC2010","USE_LSC_MCSL","USE_SONOFF_SPM","USE_SHIFT595", "USE_SDM230","USE_CM110x","USE_BL6523","USE_ADE7880", "USE_PCF85363","USE_DS3502","USE_IMPROV","USE_FLOWRATEMETER", - "USE_BP5758D","USE_HYT","","" + "USE_BP5758D","USE_HYT","USE_SM2335","" ],[ "","","","", "","","","", From 996d614027fff68156303776462b9cd589818902 Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 00:50:44 -0500 Subject: [PATCH 004/219] SM2335 - Fix an argument size. --- tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino index f41913245..347067a04 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino @@ -96,7 +96,7 @@ void SM2335Write(uint8_t value) { pinMode(Sm2335.data, OUTPUT); } -void SM2335Start(uint16_t addr) { +void SM2335Start(uint8_t addr) { digitalWrite(Sm2335.data, LOW); delayMicroseconds(SM2335_DELAY); digitalWrite(Sm2335.clk, LOW); From 6f7ff480f889b42768a716d34c2df6552075c042 Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 00:51:21 -0500 Subject: [PATCH 005/219] SM2335 - Fix a typo. --- tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino index 347067a04..f8ecd89a0 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino @@ -129,7 +129,7 @@ bool SM2335SetChannels(void) { SM2335Write(0); // Cold White 1/2 SM2335Write(0); // Cold White 2/2 SM2335Write(0); // Warm White 1/2 - SM2335Write(0); // WarmW hite 2/2 + SM2335Write(0); // Warm White 2/2 SM2335Stop(); return true; } From ffcf90bdfe75b00a12be2c6b516161b97b9e0c8f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:53:58 +0200 Subject: [PATCH 006/219] Refactor sendmail --- .../lib_mail/.github/FUNDING.yml | 24 +- .../lib_mail/.github/stale.yml | 34 +- lib/{lib_div => libesp32}/lib_mail/LICENSE | 42 +- lib/{lib_div => libesp32}/lib_mail/README.md | 0 .../lib_mail/_config.yml | 0 .../examples/Copy_Messages/Copy_Messsages.ino | 294 +- .../Delete_Messages/Delete_Messsages.ino | 284 +- .../ESP32_Ethernet_Send_Text.ino | 0 .../lan8720_modified_board.png | Bin .../lan8720_modified_schematic.png | Bin .../Read_Email_Access_Token.ino | 740 +- .../Read_Single_Email/Read_Single_Email.ino | 728 +- .../Read_Single_Email_Loop.ino | 752 +- .../examples/Search_Emails/Search_Emails.ino | 804 +- .../Send_Access_Token/Send_Access_Token.ino | 406 +- .../Send_Attachment_Blob.ino | 536 +- .../examples/Send_Attachment_Blob/image.h | 3702 ++-- .../Send_Attachment_File.ino | 854 +- .../Send_Embedded_Message.ino | 406 +- .../Send_Enriched_Text/Send_Enriched_Text.ino | 374 +- .../Send_Flash_Message_with_Inline_Image.ino | 0 .../lib_mail/examples/Send_HTML/Send_HTML.ino | 442 +- .../Send_Parallel_Attachment.ino | 458 +- .../examples/Send_Parallel_Attachment/data.h | 3912 ++-- .../Send_RFC822_Attachment.ino | 504 +- .../examples/Send_RFC822_Attachment/image.h | 3702 ++-- .../Send_Reuse_Session/Send_Reuse_Session.ino | 490 +- .../lib_mail/examples/Send_Text/Send_Text.ino | 438 +- .../Send_Text_Flowed/Send_Text_Flowed.ino | 458 +- .../lib_mail/examples/Set_Flags/Set_Flags.ino | 726 +- .../lib_mail/keywords.txt | 252 +- .../lib_mail/library.json | 32 +- .../lib_mail/library.properties | 34 +- .../lib_mail/media/images/ArduinoIDE.png | Bin .../lib_mail/media/images/esp-mail-client.png | Bin .../lib_mail/media/images/esp-mail-client.svg | 0 .../lib_mail/src/ESP_Mail_Client.cpp | 17096 ++++++++-------- .../lib_mail/src/ESP_Mail_Client.h | 5640 ++--- .../lib_mail/src/ESP_Mail_FS.h | 0 .../lib_mail/src/README.md | 4062 ++-- .../lib_mail/src/SDK_Version_Common.h | 0 .../lib_mail/src/extras/ESPTimeHelper.cpp | 570 +- .../lib_mail/src/extras/ESPTimeHelper.h | 398 +- .../lib_mail/src/extras/MIMEInfo.h | 130 +- .../lib_mail/src/extras/RFC2047.cpp | 0 .../lib_mail/src/extras/RFC2047.h | 0 .../src/wcs/esp32/ESP_Mail_HTTPClient32.cpp | 466 +- .../src/wcs/esp32/ESP_Mail_HTTPClient32.h | 284 +- .../lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp | 0 .../lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h | 0 .../src/wcs/esp32/esp_mail_ssl_client32.cpp | 0 .../src/wcs/esp32/esp_mail_ssl_client32.h | 0 .../wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp | 0 .../src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h | 0 .../wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp | 0 .../wcs/esp8266/ESP_Mail_CertStoreBearSSL.h | 0 .../src/wcs/esp8266/ESP_Mail_HTTPClient.cpp | 520 +- .../src/wcs/esp8266/ESP_Mail_HTTPClient.h | 296 +- .../lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp | 0 .../lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h | 0 platformio_tasmota_env32.ini | 2 +- tasmota/include/sendemail_ESP8266.h | 32 - tasmota/my_user_config.h | 20 +- .../xdrv_01_1_webserver_mail.ino} | 91 +- .../xdrv_01_2_webserver_esp32_mail.ino} | 39 +- ..._webserver.ino => xdrv_01_9_webserver.ino} | 10 +- .../tasmota_xdrv_driver/xdrv_10_scripter.ino | 8 +- 67 files changed, 25564 insertions(+), 25528 deletions(-) rename lib/{lib_div => libesp32}/lib_mail/.github/FUNDING.yml (98%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/.github/stale.yml (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/LICENSE (98%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/README.md (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/_config.yml (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Copy_Messages/Copy_Messsages.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Delete_Messages/Delete_Messsages.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/ESP32_Ethernet_Send_Text/ESP32_Ethernet_Send_Text.ino (100%) rename lib/{lib_div => libesp32}/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_board.png (100%) rename lib/{lib_div => libesp32}/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_schematic.png (100%) rename lib/{lib_div => libesp32}/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Search_Emails/Search_Emails.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Attachment_Blob/image.h (99%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Flash_Message_with_Inline_Image/Send_Flash_Message_with_Inline_Image.ino (100%) rename lib/{lib_div => libesp32}/lib_mail/examples/Send_HTML/Send_HTML.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Parallel_Attachment/data.h (98%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_RFC822_Attachment/image.h (99%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Text/Send_Text.ino (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/examples/Set_Flags/Set_Flags.ino (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/keywords.txt (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/library.json (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/library.properties (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/media/images/ArduinoIDE.png (100%) rename lib/{lib_div => libesp32}/lib_mail/media/images/esp-mail-client.png (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/media/images/esp-mail-client.svg (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/ESP_Mail_Client.cpp (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/ESP_Mail_Client.h (97%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/ESP_Mail_FS.h (100%) rename lib/{lib_div => libesp32}/lib_mail/src/README.md (94%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/SDK_Version_Common.h (100%) rename lib/{lib_div => libesp32}/lib_mail/src/extras/ESPTimeHelper.cpp (95%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/extras/ESPTimeHelper.h (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/extras/MIMEInfo.h (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/extras/RFC2047.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/extras/RFC2047.h (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp (95%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.h (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.h (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp (95%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h (96%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp (100%) mode change 100755 => 100644 rename lib/{lib_div => libesp32}/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h (100%) mode change 100755 => 100644 delete mode 100644 tasmota/include/sendemail_ESP8266.h rename tasmota/{tasmota_support/sendemail_ESP8266.ino => tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino} (82%) rename tasmota/{tasmota_support/sendemail_ESP32.ino => tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino} (89%) rename tasmota/tasmota_xdrv_driver/{xdrv_01_webserver.ino => xdrv_01_9_webserver.ino} (99%) diff --git a/lib/lib_div/lib_mail/.github/FUNDING.yml b/lib/libesp32/lib_mail/.github/FUNDING.yml old mode 100755 new mode 100644 similarity index 98% rename from lib/lib_div/lib_mail/.github/FUNDING.yml rename to lib/libesp32/lib_mail/.github/FUNDING.yml index be11a746d..c6f438926 --- a/lib/lib_div/lib_mail/.github/FUNDING.yml +++ b/lib/libesp32/lib_mail/.github/FUNDING.yml @@ -1,12 +1,12 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: ["https://www.paypal.me/mobizt"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ["https://www.paypal.me/mobizt"] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/lib/lib_div/lib_mail/.github/stale.yml b/lib/libesp32/lib_mail/.github/stale.yml old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/.github/stale.yml rename to lib/libesp32/lib_mail/.github/stale.yml index cbcef3e96..73494a333 --- a/lib/lib_div/lib_mail/.github/stale.yml +++ b/lib/libesp32/lib_mail/.github/stale.yml @@ -1,17 +1,17 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 20 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 5 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security -# Label to use when marking an issue as stale -staleLabel: wontfix -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 20 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 5 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/lib/lib_div/lib_mail/LICENSE b/lib/libesp32/lib_mail/LICENSE old mode 100755 new mode 100644 similarity index 98% rename from lib/lib_div/lib_mail/LICENSE rename to lib/libesp32/lib_mail/LICENSE index 80d1893d3..4ba9966fd --- a/lib/lib_div/lib_mail/LICENSE +++ b/lib/libesp32/lib_mail/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2021 mobizt - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2021 mobizt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/lib_div/lib_mail/README.md b/lib/libesp32/lib_mail/README.md old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/README.md rename to lib/libesp32/lib_mail/README.md diff --git a/lib/lib_div/lib_mail/_config.yml b/lib/libesp32/lib_mail/_config.yml old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/_config.yml rename to lib/libesp32/lib_mail/_config.yml diff --git a/lib/lib_div/lib_mail/examples/Copy_Messages/Copy_Messsages.ino b/lib/libesp32/lib_mail/examples/Copy_Messages/Copy_Messsages.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Copy_Messages/Copy_Messsages.ino rename to lib/libesp32/lib_mail/examples/Copy_Messages/Copy_Messsages.ino index 81022a136..539a0956a --- a/lib/lib_div/lib_mail/examples/Copy_Messages/Copy_Messsages.ino +++ b/lib/libesp32/lib_mail/examples/Copy_Messages/Copy_Messsages.ino @@ -1,147 +1,147 @@ -/** - * This example showed how to copy messages from the opened mailbox folder to other folder. - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* Define the MessageList class to add the message to copy */ - MessageList toCopy; - - /* Add message uid to copy to the list */ - toCopy.add(3); - toCopy.add(4); - - //imap.createFolder("test"); - - /* Copy all messages in the list to the folder "test" */ - if (imap.deleteMessages(&toCopy, "test")) - Serial.println("Messages copied"); - - /* Delete all messages in the list from the opened folder (move to trash) */ - //imap.deleteMessages(&toCopy); - - //imap.deleteolder("test"); -} - -void loop() -{ - -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} +/** + * This example showed how to copy messages from the opened mailbox folder to other folder. + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* Define the MessageList class to add the message to copy */ + MessageList toCopy; + + /* Add message uid to copy to the list */ + toCopy.add(3); + toCopy.add(4); + + //imap.createFolder("test"); + + /* Copy all messages in the list to the folder "test" */ + if (imap.deleteMessages(&toCopy, "test")) + Serial.println("Messages copied"); + + /* Delete all messages in the list from the opened folder (move to trash) */ + //imap.deleteMessages(&toCopy); + + //imap.deleteolder("test"); +} + +void loop() +{ + +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} diff --git a/lib/lib_div/lib_mail/examples/Delete_Messages/Delete_Messsages.ino b/lib/libesp32/lib_mail/examples/Delete_Messages/Delete_Messsages.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Delete_Messages/Delete_Messsages.ino rename to lib/libesp32/lib_mail/examples/Delete_Messages/Delete_Messsages.ino index 93a629a43..f52e3c708 --- a/lib/lib_div/lib_mail/examples/Delete_Messages/Delete_Messsages.ino +++ b/lib/libesp32/lib_mail/examples/Delete_Messages/Delete_Messsages.ino @@ -1,142 +1,142 @@ -/** - * This example showed how to delete messages from the opened mailbox folder. - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* Define the MessageList class to add the message to delete */ - MessageList toDelete; - /* Add message uid to delete to the list */ - toDelete.add(10); - toDelete.add(12); - - /* Delete all messages in the list (move to trash) */ - if(imap.deleteMessages(&toDelete)) - Serial.println("Messages deeted"); - - /* Delete all messages permanently by assign the second param to true*/ - //imap.deleteMessages(&toDelete, true); -} - -void loop() -{ - -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} +/** + * This example showed how to delete messages from the opened mailbox folder. + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* Define the MessageList class to add the message to delete */ + MessageList toDelete; + /* Add message uid to delete to the list */ + toDelete.add(10); + toDelete.add(12); + + /* Delete all messages in the list (move to trash) */ + if(imap.deleteMessages(&toDelete)) + Serial.println("Messages deeted"); + + /* Delete all messages permanently by assign the second param to true*/ + //imap.deleteMessages(&toDelete, true); +} + +void loop() +{ + +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} diff --git a/lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/ESP32_Ethernet_Send_Text.ino b/lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/ESP32_Ethernet_Send_Text.ino similarity index 100% rename from lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/ESP32_Ethernet_Send_Text.ino rename to lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/ESP32_Ethernet_Send_Text.ino diff --git a/lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_board.png b/lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_board.png similarity index 100% rename from lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_board.png rename to lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_board.png diff --git a/lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_schematic.png b/lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_schematic.png similarity index 100% rename from lib/lib_div/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_schematic.png rename to lib/libesp32/lib_mail/examples/ESP32_Ethernet_Send_Text/modified_LAN8720_board_images/lan8720_modified_schematic.png diff --git a/lib/lib_div/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino b/lib/libesp32/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino rename to lib/libesp32/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino index 6e9bd1465..6778e6cbe --- a/lib/lib_div/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino +++ b/lib/libesp32/lib_mail/examples/Read_Email_Access_Token/Read_Email_Access_Token.ino @@ -1,370 +1,370 @@ -/** - * This example will log in with the SASL XOAUTH2 mechanisme using OAuth2.0 access token. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The user Email for OAuth2.0 access token */ -#define AUTHOR_EMAIL "################" - -/** The OAuth2.0 access token - * The generation, exchange and refresh of the access token are not available - * in this library. - * - * To test this using GMail, get the OAuth2.0 access token from this web site - * https://developers.google.com/oauthplayground/ - * - * 1. Select the following scope (in Step 1) from Gmail API V1 - * https://mail.google.com/ - * https://mail.google.com/ - * - * 2. Click Authorize APIs button. - * 3. Cick Exchangeauthorization code for tokens. - * 4. From the response, look at access_token from the JSON payload node. - * 5. Copy that access token and paste to the AUTHOR_ACCESS_TOKEN value. - * - * The token will be expired in 3600 seconds (1 Hr). - * The AUTHOR_EMAIL above is the Email address that you granted to access the Gmail services. -*/ -#define AUTHOR_ACCESS_TOKEN "################" - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status); - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* Print all messages from the message list */ -void printMessages(IMAPSession &imap); - -/* Print all rfc822 messages included in the message */ -void printRFC822Messages(IMAP_MSG_Item &msg); - -/* Print all attachments info from the message */ -void printAttacements(IMAP_MSG_Item &msg); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Set the callback function to get the reading results */ - imap.callback(imapCallback); - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.accessToken = AUTHOR_ACCESS_TOKEN; - - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Message UID to fetch or read e.g. 100 */ - config.fetch.uid = "100"; - - /* Set seen flag*/ - //config.fetch.set_seen = true; - - /* Search criteria */ - config.search.criteria = ""; - - /* Also search the unseen message */ - config.search.unseen_msg = true; - - /* Set the storage to save the downloaded files and attachments */ - config.storage.saved_path = "/email_data"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - config.storage.type = esp_mail_file_storage_type_flash; - - /** Set to download heades, text and html messaeges, - * attachments and inline images respectively. - */ - config.download.header = true; - config.download.text = true; - config.download.html = true; - config.download.attachment = true; - config.download.inlineImg = true; - - /** Set to enable the results i.e. html and text messaeges - * which the content stored in the IMAPSession object is limited - * by the option config.limit.msg_size. - * The whole message can be download through config.download.text - * or config.download.html which not depends on these enable options. - */ - config.enable.html = true; - config.enable.text = true; - - /* Set to enable the sort the result by message UID in the ascending order */ - config.enable.recent_sort = true; - - /* Set to report the download progress via the default serial port */ - config.enable.download_status = true; - - /* Set the limit of number of messages in the search results */ - config.limit.search = 5; - - /** Set the maximum size of message stored in - * IMAPSession object in byte - */ - config.limit.msg_size = 512; - - /** Set the maximum attachments and inline images files size - * that can be downloaded in byte. - * The file which its size is largger than this limit may be saved - * as truncated file. - */ - config.limit.attachment_size = 1024 * 1024 * 5; - - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* {Optional] */ - printSelectedMailboxInfo(imap); - - /* Read or search the Email and close the session */ - MailClient.readMail(&imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); -} - -void loop() -{ - -} - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Show the result when reading finished */ - if (status.success()) - { - /* Print the result */ - printMessages(imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - Serial.printf("Free Heap: %d", ESP.getFreeHeap()); - } -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} - -void printSelectedMailboxInfo(IMAPSession &imap) -{ - /* Declare the selected folder info class to get the info of selected mailbox folder */ - SelectedFolderInfo sFolder = imap.selectedFolder(); - - /* Show the mailbox info */ - Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); - Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); - for (size_t i = 0; i < sFolder.flagCount(); i++) - Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); -} - -void printRFC822Messages(IMAP_MSG_Item &msg) -{ - Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); - for (size_t j = 0; j < msg.rfc822.size(); j++) - { - IMAP_MSG_Item rfc822 = msg.rfc822[j]; - Serial.printf("%d. \n", j + 1); - Serial.printf("Messsage ID: %s\n", rfc822.messageID); - Serial.printf("From: %s\n", rfc822.from); - Serial.printf("Sender: %s\n", rfc822.sender); - Serial.printf("To: %s\n", rfc822.to); - Serial.printf("CC: %s\n", rfc822.cc); - Serial.printf("Subject: %s\n", rfc822.subject); - Serial.printf("Date: %s\n", rfc822.date); - Serial.printf("Reply-To: %s\n", rfc822.reply_to); - Serial.printf("Return-Path: %s\n", rfc822.return_path); - Serial.printf("Comment: %s\n", rfc822.comment); - Serial.printf("Keyword: %s\n", rfc822.keyword); - Serial.printf("Text Message: %s\n", rfc822.text.content); - Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", rfc822.html.content); - Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); - - if (rfc822.attachments.size() > 0) - printAttacements(rfc822); - } -} - -void printAttacements(IMAP_MSG_Item &msg) -{ - Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); - for (size_t j = 0; j < msg.attachments.size(); j++) - { - IMAP_Attach_Item att = msg.attachments[j]; - /** att.type can be - * esp_mail_att_type_none or 0 - * esp_mail_att_type_attachment or 1 - * esp_mail_att_type_inline or 2 - */ - Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); - } - Serial.println(); -} - -void printMessages(IMAPSession &imap) -{ - /* Get the message list from the message list data */ - IMAP_MSG_List msgList = imap.data(); - - for (size_t i = 0; i < msgList.msgItems.size(); i++) - { - /* Iterate to get each message data through the message item data */ - IMAP_MSG_Item msg = msgList.msgItems[i]; - - Serial.println("################################"); - Serial.printf("Messsage Number: %s\n", msg.msgNo); - Serial.printf("Messsage UID: %s\n", msg.UID); - Serial.printf("Messsage ID: %s\n", msg.ID); - Serial.printf("Accept Language: %s\n", msg.acceptLang); - Serial.printf("Content Language: %s\n", msg.contentLang); - Serial.printf("From: %s\n", msg.from); - Serial.printf("From Charset: %s\n", msg.fromCharset); - Serial.printf("To: %s\n", msg.to); - Serial.printf("To Charset: %s\n", msg.toCharset); - Serial.printf("CC: %s\n", msg.cc); - Serial.printf("CC Charset: %s\n", msg.ccCharset); - Serial.printf("Date: %s\n", msg.date); - Serial.printf("Subject: %s\n", msg.subject); - Serial.printf("Subject Charset: %s\n", msg.subjectCharset); - - /* If the result contains the message info (Fetch mode) */ - if (!imap.headerOnly()) - { - Serial.printf("Text Message: %s\n", msg.text.content); - Serial.printf("Text Message Charset: %s\n", msg.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", msg.html.content); - Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); - - if (msg.attachments.size() > 0) - printAttacements(msg); - - if (msg.rfc822.size() > 0) - printRFC822Messages(msg); - } - - Serial.println(); - } -} +/** + * This example will log in with the SASL XOAUTH2 mechanisme using OAuth2.0 access token. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The user Email for OAuth2.0 access token */ +#define AUTHOR_EMAIL "################" + +/** The OAuth2.0 access token + * The generation, exchange and refresh of the access token are not available + * in this library. + * + * To test this using GMail, get the OAuth2.0 access token from this web site + * https://developers.google.com/oauthplayground/ + * + * 1. Select the following scope (in Step 1) from Gmail API V1 + * https://mail.google.com/ + * https://mail.google.com/ + * + * 2. Click Authorize APIs button. + * 3. Cick Exchangeauthorization code for tokens. + * 4. From the response, look at access_token from the JSON payload node. + * 5. Copy that access token and paste to the AUTHOR_ACCESS_TOKEN value. + * + * The token will be expired in 3600 seconds (1 Hr). + * The AUTHOR_EMAIL above is the Email address that you granted to access the Gmail services. +*/ +#define AUTHOR_ACCESS_TOKEN "################" + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status); + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* Print all messages from the message list */ +void printMessages(IMAPSession &imap); + +/* Print all rfc822 messages included in the message */ +void printRFC822Messages(IMAP_MSG_Item &msg); + +/* Print all attachments info from the message */ +void printAttacements(IMAP_MSG_Item &msg); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Set the callback function to get the reading results */ + imap.callback(imapCallback); + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.accessToken = AUTHOR_ACCESS_TOKEN; + + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Message UID to fetch or read e.g. 100 */ + config.fetch.uid = "100"; + + /* Set seen flag*/ + //config.fetch.set_seen = true; + + /* Search criteria */ + config.search.criteria = ""; + + /* Also search the unseen message */ + config.search.unseen_msg = true; + + /* Set the storage to save the downloaded files and attachments */ + config.storage.saved_path = "/email_data"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + config.storage.type = esp_mail_file_storage_type_flash; + + /** Set to download heades, text and html messaeges, + * attachments and inline images respectively. + */ + config.download.header = true; + config.download.text = true; + config.download.html = true; + config.download.attachment = true; + config.download.inlineImg = true; + + /** Set to enable the results i.e. html and text messaeges + * which the content stored in the IMAPSession object is limited + * by the option config.limit.msg_size. + * The whole message can be download through config.download.text + * or config.download.html which not depends on these enable options. + */ + config.enable.html = true; + config.enable.text = true; + + /* Set to enable the sort the result by message UID in the ascending order */ + config.enable.recent_sort = true; + + /* Set to report the download progress via the default serial port */ + config.enable.download_status = true; + + /* Set the limit of number of messages in the search results */ + config.limit.search = 5; + + /** Set the maximum size of message stored in + * IMAPSession object in byte + */ + config.limit.msg_size = 512; + + /** Set the maximum attachments and inline images files size + * that can be downloaded in byte. + * The file which its size is largger than this limit may be saved + * as truncated file. + */ + config.limit.attachment_size = 1024 * 1024 * 5; + + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* {Optional] */ + printSelectedMailboxInfo(imap); + + /* Read or search the Email and close the session */ + MailClient.readMail(&imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); +} + +void loop() +{ + +} + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Show the result when reading finished */ + if (status.success()) + { + /* Print the result */ + printMessages(imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + Serial.printf("Free Heap: %d", ESP.getFreeHeap()); + } +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} + +void printSelectedMailboxInfo(IMAPSession &imap) +{ + /* Declare the selected folder info class to get the info of selected mailbox folder */ + SelectedFolderInfo sFolder = imap.selectedFolder(); + + /* Show the mailbox info */ + Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); + Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); + for (size_t i = 0; i < sFolder.flagCount(); i++) + Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); +} + +void printRFC822Messages(IMAP_MSG_Item &msg) +{ + Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); + for (size_t j = 0; j < msg.rfc822.size(); j++) + { + IMAP_MSG_Item rfc822 = msg.rfc822[j]; + Serial.printf("%d. \n", j + 1); + Serial.printf("Messsage ID: %s\n", rfc822.messageID); + Serial.printf("From: %s\n", rfc822.from); + Serial.printf("Sender: %s\n", rfc822.sender); + Serial.printf("To: %s\n", rfc822.to); + Serial.printf("CC: %s\n", rfc822.cc); + Serial.printf("Subject: %s\n", rfc822.subject); + Serial.printf("Date: %s\n", rfc822.date); + Serial.printf("Reply-To: %s\n", rfc822.reply_to); + Serial.printf("Return-Path: %s\n", rfc822.return_path); + Serial.printf("Comment: %s\n", rfc822.comment); + Serial.printf("Keyword: %s\n", rfc822.keyword); + Serial.printf("Text Message: %s\n", rfc822.text.content); + Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", rfc822.html.content); + Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); + + if (rfc822.attachments.size() > 0) + printAttacements(rfc822); + } +} + +void printAttacements(IMAP_MSG_Item &msg) +{ + Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); + for (size_t j = 0; j < msg.attachments.size(); j++) + { + IMAP_Attach_Item att = msg.attachments[j]; + /** att.type can be + * esp_mail_att_type_none or 0 + * esp_mail_att_type_attachment or 1 + * esp_mail_att_type_inline or 2 + */ + Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); + } + Serial.println(); +} + +void printMessages(IMAPSession &imap) +{ + /* Get the message list from the message list data */ + IMAP_MSG_List msgList = imap.data(); + + for (size_t i = 0; i < msgList.msgItems.size(); i++) + { + /* Iterate to get each message data through the message item data */ + IMAP_MSG_Item msg = msgList.msgItems[i]; + + Serial.println("################################"); + Serial.printf("Messsage Number: %s\n", msg.msgNo); + Serial.printf("Messsage UID: %s\n", msg.UID); + Serial.printf("Messsage ID: %s\n", msg.ID); + Serial.printf("Accept Language: %s\n", msg.acceptLang); + Serial.printf("Content Language: %s\n", msg.contentLang); + Serial.printf("From: %s\n", msg.from); + Serial.printf("From Charset: %s\n", msg.fromCharset); + Serial.printf("To: %s\n", msg.to); + Serial.printf("To Charset: %s\n", msg.toCharset); + Serial.printf("CC: %s\n", msg.cc); + Serial.printf("CC Charset: %s\n", msg.ccCharset); + Serial.printf("Date: %s\n", msg.date); + Serial.printf("Subject: %s\n", msg.subject); + Serial.printf("Subject Charset: %s\n", msg.subjectCharset); + + /* If the result contains the message info (Fetch mode) */ + if (!imap.headerOnly()) + { + Serial.printf("Text Message: %s\n", msg.text.content); + Serial.printf("Text Message Charset: %s\n", msg.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", msg.html.content); + Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); + + if (msg.attachments.size() > 0) + printAttacements(msg); + + if (msg.rfc822.size() > 0) + printRFC822Messages(msg); + } + + Serial.println(); + } +} diff --git a/lib/lib_div/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino b/lib/libesp32/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino rename to lib/libesp32/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino index 20d039097..31fa6348c --- a/lib/lib_div/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino +++ b/lib/libesp32/lib_mail/examples/Read_Single_Email/Read_Single_Email.ino @@ -1,364 +1,364 @@ -/** - * This example will fetch or read the Email which the known message UID - * was used for fetching. - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status); - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* Print all messages from the message list */ -void printMessages(IMAPSession &imap); - -/* Print all rfc822 messages included in the message */ -void printRFC822Messages(IMAP_MSG_Item &msg); - -/* Print all attachments info from the message */ -void printAttacements(IMAP_MSG_Item &msg); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Set the callback function to get the reading results */ - imap.callback(imapCallback); - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** ######################################################## - * Some properties of IMAP_Config and ESP_Mail_Session data - * accept the pointer to constant char i.e. const char*. - * - * You may assign a string literal to that properties like - * below example. - * - * config.storage.saved_path = String("/email_data").c_str(); - * - * String folder = "INBOX"; - * imap.selectFolder(folder.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Message UID to fetch or read e.g. 100 */ - config.fetch.uid = "100"; - - /* Set seen flag */ - //config.fetch.set_seen = true; - - /* Search criteria */ - config.search.criteria = ""; - - /* Also search the unseen message */ - config.search.unseen_msg = true; - - /* Set the storage to save the downloaded files and attachments */ - config.storage.saved_path = "/email_data"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - config.storage.type = esp_mail_file_storage_type_flash; - - /** Set to download heades, text and html messaeges, - * attachments and inline images respectively. - */ - config.download.header = true; - config.download.text = true; - config.download.html = true; - config.download.attachment = true; - config.download.inlineImg = true; - - /** Set to enable the results i.e. html and text messaeges - * which the content stored in the IMAPSession object is limited - * by the option config.limit.msg_size. - * The whole message can be download through config.download.text - * or config.download.html which not depends on these enable options. - */ - config.enable.html = true; - config.enable.text = true; - - /* Set to enable the sort the result by message UID in the ascending order */ - config.enable.recent_sort = true; - - /* Set to report the download progress via the default serial port */ - config.enable.download_status = true; - - /* Set the limit of number of messages in the search results */ - config.limit.search = 5; - - /** Set the maximum size of message stored in - * IMAPSession object in byte - */ - config.limit.msg_size = 512; - - /** Set the maximum attachments and inline images files size - * that can be downloaded in byte. - * The file which its size is largger than this limit may be saved - * as truncated file. - */ - config.limit.attachment_size = 1024 * 1024 * 5; - - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* {Optional] */ - printSelectedMailboxInfo(imap); - - /* Read or search the Email and close the session */ - MailClient.readMail(&imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); -} - -void loop() -{ - -} - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Show the result when reading finished */ - if (status.success()) - { - /* Print the result */ - printMessages(imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - Serial.printf("Free Heap: %d", ESP.getFreeHeap()); - } -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} - -void printSelectedMailboxInfo(IMAPSession &imap) -{ - /* Declare the selected folder info class to get the info of selected mailbox folder */ - SelectedFolderInfo sFolder = imap.selectedFolder(); - - /* Show the mailbox info */ - Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); - Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); - for (size_t i = 0; i < sFolder.flagCount(); i++) - Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); -} - -void printRFC822Messages(IMAP_MSG_Item &msg) -{ - Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); - for (size_t j = 0; j < msg.rfc822.size(); j++) - { - IMAP_MSG_Item rfc822 = msg.rfc822[j]; - Serial.printf("%d. \n", j + 1); - Serial.printf("Messsage ID: %s\n", rfc822.messageID); - Serial.printf("From: %s\n", rfc822.from); - Serial.printf("Sender: %s\n", rfc822.sender); - Serial.printf("To: %s\n", rfc822.to); - Serial.printf("CC: %s\n", rfc822.cc); - Serial.printf("Subject: %s\n", rfc822.subject); - Serial.printf("Date: %s\n", rfc822.date); - Serial.printf("Reply-To: %s\n", rfc822.reply_to); - Serial.printf("Return-Path: %s\n", rfc822.return_path); - Serial.printf("Comment: %s\n", rfc822.comment); - Serial.printf("Keyword: %s\n", rfc822.keyword); - Serial.printf("Text Message: %s\n", rfc822.text.content); - Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", rfc822.html.content); - Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); - - if (rfc822.attachments.size() > 0) - printAttacements(rfc822); - } -} - -void printAttacements(IMAP_MSG_Item &msg) -{ - Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); - for (size_t j = 0; j < msg.attachments.size(); j++) - { - IMAP_Attach_Item att = msg.attachments[j]; - /** att.type can be - * esp_mail_att_type_none or 0 - * esp_mail_att_type_attachment or 1 - * esp_mail_att_type_inline or 2 - */ - Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); - } - Serial.println(); -} - -void printMessages(IMAPSession &imap) -{ - /* Get the message list from the message list data */ - IMAP_MSG_List msgList = imap.data(); - - for (size_t i = 0; i < msgList.msgItems.size(); i++) - { - /* Iterate to get each message data through the message item data */ - IMAP_MSG_Item msg = msgList.msgItems[i]; - - Serial.println("################################"); - Serial.printf("Messsage Number: %s\n", msg.msgNo); - Serial.printf("Messsage UID: %s\n", msg.UID); - Serial.printf("Messsage ID: %s\n", msg.ID); - Serial.printf("Accept Language: %s\n", msg.acceptLang); - Serial.printf("Content Language: %s\n", msg.contentLang); - Serial.printf("From: %s\n", msg.from); - Serial.printf("From Charset: %s\n", msg.fromCharset); - Serial.printf("To: %s\n", msg.to); - Serial.printf("To Charset: %s\n", msg.toCharset); - Serial.printf("CC: %s\n", msg.cc); - Serial.printf("CC Charset: %s\n", msg.ccCharset); - Serial.printf("Date: %s\n", msg.date); - Serial.printf("Subject: %s\n", msg.subject); - Serial.printf("Subject Charset: %s\n", msg.subjectCharset); - - /* If the result contains the message info (Fetch mode) */ - if (!imap.headerOnly()) - { - Serial.printf("Text Message: %s\n", msg.text.content); - Serial.printf("Text Message Charset: %s\n", msg.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", msg.html.content); - Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); - - if (msg.attachments.size() > 0) - printAttacements(msg); - - if (msg.rfc822.size() > 0) - printRFC822Messages(msg); - } - - Serial.println(); - } -} +/** + * This example will fetch or read the Email which the known message UID + * was used for fetching. + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status); + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* Print all messages from the message list */ +void printMessages(IMAPSession &imap); + +/* Print all rfc822 messages included in the message */ +void printRFC822Messages(IMAP_MSG_Item &msg); + +/* Print all attachments info from the message */ +void printAttacements(IMAP_MSG_Item &msg); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Set the callback function to get the reading results */ + imap.callback(imapCallback); + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** ######################################################## + * Some properties of IMAP_Config and ESP_Mail_Session data + * accept the pointer to constant char i.e. const char*. + * + * You may assign a string literal to that properties like + * below example. + * + * config.storage.saved_path = String("/email_data").c_str(); + * + * String folder = "INBOX"; + * imap.selectFolder(folder.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Message UID to fetch or read e.g. 100 */ + config.fetch.uid = "100"; + + /* Set seen flag */ + //config.fetch.set_seen = true; + + /* Search criteria */ + config.search.criteria = ""; + + /* Also search the unseen message */ + config.search.unseen_msg = true; + + /* Set the storage to save the downloaded files and attachments */ + config.storage.saved_path = "/email_data"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + config.storage.type = esp_mail_file_storage_type_flash; + + /** Set to download heades, text and html messaeges, + * attachments and inline images respectively. + */ + config.download.header = true; + config.download.text = true; + config.download.html = true; + config.download.attachment = true; + config.download.inlineImg = true; + + /** Set to enable the results i.e. html and text messaeges + * which the content stored in the IMAPSession object is limited + * by the option config.limit.msg_size. + * The whole message can be download through config.download.text + * or config.download.html which not depends on these enable options. + */ + config.enable.html = true; + config.enable.text = true; + + /* Set to enable the sort the result by message UID in the ascending order */ + config.enable.recent_sort = true; + + /* Set to report the download progress via the default serial port */ + config.enable.download_status = true; + + /* Set the limit of number of messages in the search results */ + config.limit.search = 5; + + /** Set the maximum size of message stored in + * IMAPSession object in byte + */ + config.limit.msg_size = 512; + + /** Set the maximum attachments and inline images files size + * that can be downloaded in byte. + * The file which its size is largger than this limit may be saved + * as truncated file. + */ + config.limit.attachment_size = 1024 * 1024 * 5; + + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* {Optional] */ + printSelectedMailboxInfo(imap); + + /* Read or search the Email and close the session */ + MailClient.readMail(&imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); +} + +void loop() +{ + +} + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Show the result when reading finished */ + if (status.success()) + { + /* Print the result */ + printMessages(imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + Serial.printf("Free Heap: %d", ESP.getFreeHeap()); + } +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} + +void printSelectedMailboxInfo(IMAPSession &imap) +{ + /* Declare the selected folder info class to get the info of selected mailbox folder */ + SelectedFolderInfo sFolder = imap.selectedFolder(); + + /* Show the mailbox info */ + Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); + Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); + for (size_t i = 0; i < sFolder.flagCount(); i++) + Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); +} + +void printRFC822Messages(IMAP_MSG_Item &msg) +{ + Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); + for (size_t j = 0; j < msg.rfc822.size(); j++) + { + IMAP_MSG_Item rfc822 = msg.rfc822[j]; + Serial.printf("%d. \n", j + 1); + Serial.printf("Messsage ID: %s\n", rfc822.messageID); + Serial.printf("From: %s\n", rfc822.from); + Serial.printf("Sender: %s\n", rfc822.sender); + Serial.printf("To: %s\n", rfc822.to); + Serial.printf("CC: %s\n", rfc822.cc); + Serial.printf("Subject: %s\n", rfc822.subject); + Serial.printf("Date: %s\n", rfc822.date); + Serial.printf("Reply-To: %s\n", rfc822.reply_to); + Serial.printf("Return-Path: %s\n", rfc822.return_path); + Serial.printf("Comment: %s\n", rfc822.comment); + Serial.printf("Keyword: %s\n", rfc822.keyword); + Serial.printf("Text Message: %s\n", rfc822.text.content); + Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", rfc822.html.content); + Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); + + if (rfc822.attachments.size() > 0) + printAttacements(rfc822); + } +} + +void printAttacements(IMAP_MSG_Item &msg) +{ + Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); + for (size_t j = 0; j < msg.attachments.size(); j++) + { + IMAP_Attach_Item att = msg.attachments[j]; + /** att.type can be + * esp_mail_att_type_none or 0 + * esp_mail_att_type_attachment or 1 + * esp_mail_att_type_inline or 2 + */ + Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); + } + Serial.println(); +} + +void printMessages(IMAPSession &imap) +{ + /* Get the message list from the message list data */ + IMAP_MSG_List msgList = imap.data(); + + for (size_t i = 0; i < msgList.msgItems.size(); i++) + { + /* Iterate to get each message data through the message item data */ + IMAP_MSG_Item msg = msgList.msgItems[i]; + + Serial.println("################################"); + Serial.printf("Messsage Number: %s\n", msg.msgNo); + Serial.printf("Messsage UID: %s\n", msg.UID); + Serial.printf("Messsage ID: %s\n", msg.ID); + Serial.printf("Accept Language: %s\n", msg.acceptLang); + Serial.printf("Content Language: %s\n", msg.contentLang); + Serial.printf("From: %s\n", msg.from); + Serial.printf("From Charset: %s\n", msg.fromCharset); + Serial.printf("To: %s\n", msg.to); + Serial.printf("To Charset: %s\n", msg.toCharset); + Serial.printf("CC: %s\n", msg.cc); + Serial.printf("CC Charset: %s\n", msg.ccCharset); + Serial.printf("Date: %s\n", msg.date); + Serial.printf("Subject: %s\n", msg.subject); + Serial.printf("Subject Charset: %s\n", msg.subjectCharset); + + /* If the result contains the message info (Fetch mode) */ + if (!imap.headerOnly()) + { + Serial.printf("Text Message: %s\n", msg.text.content); + Serial.printf("Text Message Charset: %s\n", msg.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", msg.html.content); + Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); + + if (msg.attachments.size() > 0) + printAttacements(msg); + + if (msg.rfc822.size() > 0) + printRFC822Messages(msg); + } + + Serial.println(); + } +} diff --git a/lib/lib_div/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino b/lib/libesp32/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino rename to lib/libesp32/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino index af374fdbe..b6b484a29 --- a/lib/lib_div/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino +++ b/lib/libesp32/lib_mail/examples/Read_Single_Email_Loop/Read_Single_Email_Loop.ino @@ -1,376 +1,376 @@ -/** - * This example will repeatedly fetch or read the Email via the loop function - * using the predict next message UID as the starting count down message UID. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status); - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* Print all messages from the message list */ -void printMessages(IMAPSession &imap); - -/* Print all rfc822 messages included in the message */ -void printRFC822Messages(IMAP_MSG_Item &msg); - -/* Print all attachments info from the message */ -void printAttacements(IMAP_MSG_Item &msg); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - -unsigned long readMillis = 0; - -int nextMsgUID = 0; -int msgUID = 0; -int sign = -1; - -/* Declare the session config data */ -ESP_Mail_Session session; - -/* Setup the configuration for searching or fetching operation and its result */ -IMAP_Config config; - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Set the callback function to get the reading results */ - imap.callback(imapCallback); - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - - /* Message UID to fetch or read */ - config.fetch.uid = ""; - - /* Search criteria */ - config.search.criteria = ""; - - /* Also search the unseen message */ - config.search.unseen_msg = true; - - /* Set the storage to save the downloaded files and attachments */ - config.storage.saved_path = "/email_data"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - config.storage.type = esp_mail_file_storage_type_flash; - - /** Set to download heades, text and html messaeges, - * attachments and inline images respectively. - */ - config.download.header = false; - config.download.text = false; - config.download.html = false; - config.download.attachment = false; - config.download.inlineImg = false; - - /** Set to enable the results i.e. html and text messaeges - * which the content stored in the IMAPSession object is limited - * by the option config.limit.msg_size. - * The whole message can be download through config.download.text - * or config.download.html which not depends on these enable options. - */ - config.enable.html = true; - config.enable.text = true; - - /* Set to enable the sort the result by message UID in the ascending order */ - config.enable.recent_sort = true; - - /* Set to report the download progress via the default serial port */ - config.enable.download_status = true; - - /* Set the limit of number of messages in the search results */ - config.limit.search = 5; - - /** Set the maximum size of message stored in - * IMAPSession object in byte - */ - config.limit.msg_size = 512; - - /** Set the maximum attachments and inline images files size - * that can be downloaded in byte. - * The file which its size is largger than this limit may be saved - * as truncated file. - */ - config.limit.attachment_size = 1024 * 1024 * 5; - - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* {Optional] */ - printSelectedMailboxInfo(imap); -} - -void loop() -{ - if (millis() - readMillis > 10000 || readMillis == 0) - { - readMillis = millis(); - - if (msgUID == 0) - sign = 1; - else if (msgUID >= nextMsgUID) - sign = -1; - - msgUID += sign; - - String uid = String(msgUID); - - /* Message UID to fetch or read */ - config.fetch.uid = uid.c_str(); - - /* Set seen flag */ - //config.fetch.set_seen = true; - - /** Read or search the Email and keep the TCP session to open - * The second parameter is for close the session. - */ - MailClient.readMail(&imap, false); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - } -} - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Show the result when reading finished */ - if (status.success()) - { - /* Print the result */ - printMessages(imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - Serial.printf("Free Heap: %d", ESP.getFreeHeap()); - } -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} - -void printSelectedMailboxInfo(IMAPSession &imap) -{ - /* Declare the selected folder info class to get the info of selected mailbox folder */ - SelectedFolderInfo sFolder = imap.selectedFolder(); - - nextMsgUID = sFolder.nextUID(); - msgUID = nextMsgUID; - - /* Show the mailbox info */ - Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); - Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); - for (size_t i = 0; i < sFolder.flagCount(); i++) - Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); -} - -void printRFC822Messages(IMAP_MSG_Item &msg) -{ - Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); - for (size_t j = 0; j < msg.rfc822.size(); j++) - { - IMAP_MSG_Item rfc822 = msg.rfc822[j]; - Serial.printf("%d. \n", j + 1); - Serial.printf("Messsage ID: %s\n", rfc822.messageID); - Serial.printf("From: %s\n", rfc822.from); - Serial.printf("Sender: %s\n", rfc822.sender); - Serial.printf("To: %s\n", rfc822.to); - Serial.printf("CC: %s\n", rfc822.cc); - Serial.printf("Subject: %s\n", rfc822.subject); - Serial.printf("Date: %s\n", rfc822.date); - Serial.printf("Reply-To: %s\n", rfc822.reply_to); - Serial.printf("Return-Path: %s\n", rfc822.return_path); - Serial.printf("Comment: %s\n", rfc822.comment); - Serial.printf("Keyword: %s\n", rfc822.keyword); - Serial.printf("Text Message: %s\n", rfc822.text.content); - Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", rfc822.html.content); - Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); - - if (rfc822.attachments.size() > 0) - printAttacements(rfc822); - } -} - -void printAttacements(IMAP_MSG_Item &msg) -{ - Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); - for (size_t j = 0; j < msg.attachments.size(); j++) - { - IMAP_Attach_Item att = msg.attachments[j]; - /** att.type can be - * esp_mail_att_type_none or 0 - * esp_mail_att_type_attachment or 1 - * esp_mail_att_type_inline or 2 - */ - Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); - } - Serial.println(); -} - -void printMessages(IMAPSession &imap) -{ - /* Get the message list from the message list data */ - IMAP_MSG_List msgList = imap.data(); - - for (size_t i = 0; i < msgList.msgItems.size(); i++) - { - /* Iterate to get each message data through the message item data */ - IMAP_MSG_Item msg = msgList.msgItems[i]; - - Serial.println("################################"); - Serial.printf("Messsage Number: %s\n", msg.msgNo); - Serial.printf("Messsage UID: %s\n", msg.UID); - Serial.printf("Messsage ID: %s\n", msg.ID); - Serial.printf("Accept Language: %s\n", msg.acceptLang); - Serial.printf("Content Language: %s\n", msg.contentLang); - Serial.printf("From: %s\n", msg.from); - Serial.printf("From Charset: %s\n", msg.fromCharset); - Serial.printf("To: %s\n", msg.to); - Serial.printf("To Charset: %s\n", msg.toCharset); - Serial.printf("CC: %s\n", msg.cc); - Serial.printf("CC Charset: %s\n", msg.ccCharset); - Serial.printf("Date: %s\n", msg.date); - Serial.printf("Subject: %s\n", msg.subject); - Serial.printf("Subject Charset: %s\n", msg.subjectCharset); - - /* If the result contains the message info (Fetch mode) */ - if (!imap.headerOnly()) - { - Serial.printf("Text Message: %s\n", msg.text.content); - Serial.printf("Text Message Charset: %s\n", msg.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", msg.html.content); - Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); - - if (msg.attachments.size() > 0) - printAttacements(msg); - - if (msg.rfc822.size() > 0) - printRFC822Messages(msg); - } - - Serial.println(); - } -} +/** + * This example will repeatedly fetch or read the Email via the loop function + * using the predict next message UID as the starting count down message UID. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status); + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* Print all messages from the message list */ +void printMessages(IMAPSession &imap); + +/* Print all rfc822 messages included in the message */ +void printRFC822Messages(IMAP_MSG_Item &msg); + +/* Print all attachments info from the message */ +void printAttacements(IMAP_MSG_Item &msg); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + +unsigned long readMillis = 0; + +int nextMsgUID = 0; +int msgUID = 0; +int sign = -1; + +/* Declare the session config data */ +ESP_Mail_Session session; + +/* Setup the configuration for searching or fetching operation and its result */ +IMAP_Config config; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Set the callback function to get the reading results */ + imap.callback(imapCallback); + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + + /* Message UID to fetch or read */ + config.fetch.uid = ""; + + /* Search criteria */ + config.search.criteria = ""; + + /* Also search the unseen message */ + config.search.unseen_msg = true; + + /* Set the storage to save the downloaded files and attachments */ + config.storage.saved_path = "/email_data"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + config.storage.type = esp_mail_file_storage_type_flash; + + /** Set to download heades, text and html messaeges, + * attachments and inline images respectively. + */ + config.download.header = false; + config.download.text = false; + config.download.html = false; + config.download.attachment = false; + config.download.inlineImg = false; + + /** Set to enable the results i.e. html and text messaeges + * which the content stored in the IMAPSession object is limited + * by the option config.limit.msg_size. + * The whole message can be download through config.download.text + * or config.download.html which not depends on these enable options. + */ + config.enable.html = true; + config.enable.text = true; + + /* Set to enable the sort the result by message UID in the ascending order */ + config.enable.recent_sort = true; + + /* Set to report the download progress via the default serial port */ + config.enable.download_status = true; + + /* Set the limit of number of messages in the search results */ + config.limit.search = 5; + + /** Set the maximum size of message stored in + * IMAPSession object in byte + */ + config.limit.msg_size = 512; + + /** Set the maximum attachments and inline images files size + * that can be downloaded in byte. + * The file which its size is largger than this limit may be saved + * as truncated file. + */ + config.limit.attachment_size = 1024 * 1024 * 5; + + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* {Optional] */ + printSelectedMailboxInfo(imap); +} + +void loop() +{ + if (millis() - readMillis > 10000 || readMillis == 0) + { + readMillis = millis(); + + if (msgUID == 0) + sign = 1; + else if (msgUID >= nextMsgUID) + sign = -1; + + msgUID += sign; + + String uid = String(msgUID); + + /* Message UID to fetch or read */ + config.fetch.uid = uid.c_str(); + + /* Set seen flag */ + //config.fetch.set_seen = true; + + /** Read or search the Email and keep the TCP session to open + * The second parameter is for close the session. + */ + MailClient.readMail(&imap, false); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + } +} + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Show the result when reading finished */ + if (status.success()) + { + /* Print the result */ + printMessages(imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + Serial.printf("Free Heap: %d", ESP.getFreeHeap()); + } +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} + +void printSelectedMailboxInfo(IMAPSession &imap) +{ + /* Declare the selected folder info class to get the info of selected mailbox folder */ + SelectedFolderInfo sFolder = imap.selectedFolder(); + + nextMsgUID = sFolder.nextUID(); + msgUID = nextMsgUID; + + /* Show the mailbox info */ + Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); + Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); + for (size_t i = 0; i < sFolder.flagCount(); i++) + Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); +} + +void printRFC822Messages(IMAP_MSG_Item &msg) +{ + Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); + for (size_t j = 0; j < msg.rfc822.size(); j++) + { + IMAP_MSG_Item rfc822 = msg.rfc822[j]; + Serial.printf("%d. \n", j + 1); + Serial.printf("Messsage ID: %s\n", rfc822.messageID); + Serial.printf("From: %s\n", rfc822.from); + Serial.printf("Sender: %s\n", rfc822.sender); + Serial.printf("To: %s\n", rfc822.to); + Serial.printf("CC: %s\n", rfc822.cc); + Serial.printf("Subject: %s\n", rfc822.subject); + Serial.printf("Date: %s\n", rfc822.date); + Serial.printf("Reply-To: %s\n", rfc822.reply_to); + Serial.printf("Return-Path: %s\n", rfc822.return_path); + Serial.printf("Comment: %s\n", rfc822.comment); + Serial.printf("Keyword: %s\n", rfc822.keyword); + Serial.printf("Text Message: %s\n", rfc822.text.content); + Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", rfc822.html.content); + Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); + + if (rfc822.attachments.size() > 0) + printAttacements(rfc822); + } +} + +void printAttacements(IMAP_MSG_Item &msg) +{ + Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); + for (size_t j = 0; j < msg.attachments.size(); j++) + { + IMAP_Attach_Item att = msg.attachments[j]; + /** att.type can be + * esp_mail_att_type_none or 0 + * esp_mail_att_type_attachment or 1 + * esp_mail_att_type_inline or 2 + */ + Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); + } + Serial.println(); +} + +void printMessages(IMAPSession &imap) +{ + /* Get the message list from the message list data */ + IMAP_MSG_List msgList = imap.data(); + + for (size_t i = 0; i < msgList.msgItems.size(); i++) + { + /* Iterate to get each message data through the message item data */ + IMAP_MSG_Item msg = msgList.msgItems[i]; + + Serial.println("################################"); + Serial.printf("Messsage Number: %s\n", msg.msgNo); + Serial.printf("Messsage UID: %s\n", msg.UID); + Serial.printf("Messsage ID: %s\n", msg.ID); + Serial.printf("Accept Language: %s\n", msg.acceptLang); + Serial.printf("Content Language: %s\n", msg.contentLang); + Serial.printf("From: %s\n", msg.from); + Serial.printf("From Charset: %s\n", msg.fromCharset); + Serial.printf("To: %s\n", msg.to); + Serial.printf("To Charset: %s\n", msg.toCharset); + Serial.printf("CC: %s\n", msg.cc); + Serial.printf("CC Charset: %s\n", msg.ccCharset); + Serial.printf("Date: %s\n", msg.date); + Serial.printf("Subject: %s\n", msg.subject); + Serial.printf("Subject Charset: %s\n", msg.subjectCharset); + + /* If the result contains the message info (Fetch mode) */ + if (!imap.headerOnly()) + { + Serial.printf("Text Message: %s\n", msg.text.content); + Serial.printf("Text Message Charset: %s\n", msg.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", msg.html.content); + Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); + + if (msg.attachments.size() > 0) + printAttacements(msg); + + if (msg.rfc822.size() > 0) + printRFC822Messages(msg); + } + + Serial.println(); + } +} diff --git a/lib/lib_div/lib_mail/examples/Search_Emails/Search_Emails.ino b/lib/libesp32/lib_mail/examples/Search_Emails/Search_Emails.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Search_Emails/Search_Emails.ino rename to lib/libesp32/lib_mail/examples/Search_Emails/Search_Emails.ino index f644ffe94..e82c84b6f --- a/lib/lib_div/lib_mail/examples/Search_Emails/Search_Emails.ino +++ b/lib/libesp32/lib_mail/examples/Search_Emails/Search_Emails.ino @@ -1,402 +1,402 @@ -/** - * This example will search all Emails in the opened mailbox folder. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status); - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* Print all messages from the message list */ -void printMessages(IMAPSession &imap); - -/* Print all rfc822 messages included in the message */ -void printRFC822Messages(IMAP_MSG_Item &msg); - -/* Print all attachments info from the message */ -void printAttacements(IMAP_MSG_Item &msg); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Set the callback function to get the reading results */ - imap.callback(imapCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - /** ######################################################## - * Some properties of IMAP_Config and ESP_Mail_Session data - * accept the pointer to constant char i.e. const char*. - * - * You may assign a string literal to that properties like - * below example. - * - * config.search.criteria = String("UID SEARCH ALL").c_str(); - * - * String folder = "INBOX"; - * imap.selectFolder(folder.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Message UID to fetch or read */ - config.fetch.uid = ""; - - /** Search criteria - * - * A search key can also be a parenthesized list of one or more search keys - * (e.g., for use with the OR and NOT keys). - * - * Since IMAP protocol uses Polish notation, the search criteria which in the polish notation form can be. - * - * To search the message from "someone@email.com" with the subject "my subject" since 1 Jan 2021, your search criteria can be - * UID SEARCH (OR SUBJECT "my subject" FROM "someone@email.com") SINCE "Fri, 1 Jan 2021 21:52:25 -0800" - * - * To search the message from "mail1@domain.com" or from "mail2@domain.com", the search criteria will be - * UID SEARCH OR FROM mail1@domain.com FROM mail2@domain.com - * - * For more details on using parentheses, AND, OR and NOT search keys in search criteria. - * https://www.limilabs.com/blog/imap-search-requires-parentheses - * - * - */ - config.search.criteria = "UID SEARCH ALL"; - - /* Also search the unseen message */ - config.search.unseen_msg = true; - - /* Set the storage to save the downloaded files and attachments */ - config.storage.saved_path = "/email_data"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - config.storage.type = esp_mail_file_storage_type_flash; - - /** Set to download heades, text and html messaeges, - * attachments and inline images respectively. - */ - config.download.header = true; - config.download.text = true; - config.download.html = true; - config.download.attachment = true; - config.download.inlineImg = true; - - /** Set to enable the results i.e. html and text messaeges - * which the content stored in the IMAPSession object is limited - * by the option config.limit.msg_size. - * The whole message can be download through config.download.text - * or config.download.html which not depends on these enable options. - */ - config.enable.html = true; - config.enable.text = true; - - /* Set to enable the sort the result by message UID in the ascending order */ - config.enable.recent_sort = true; - - /* Set to report the download progress via the default serial port */ - config.enable.download_status = true; - - /* Set the limit of number of messages in the search results */ - config.limit.search = 5; - - /** Set the maximum size of message stored in - * IMAPSession object in byte - */ - config.limit.msg_size = 512; - - /** Set the maximum attachments and inline images files size - * that can be downloaded in byte. - * The file which its size is largger than this limit may be saved - * as truncated file. - */ - config.limit.attachment_size = 1024 * 1024 * 5; - - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* {Optional] */ - printSelectedMailboxInfo(imap); - - /** Read or search the Email and keep the TCP session to open - * The second parameter is for close the session. - */ - MailClient.readMail(&imap, false); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - - /** Open or select other mailbox folder - * The folder that previousely opened will be closed - */ - if (imap.selectFolder("Junk")) - { - /* {Optional] */ - printSelectedMailboxInfo(imap); - - /* Config to search all messages in the opened mailboax (Search mode) */ - config.search.criteria = "UID SEARCH ALL"; // or "UID SEARCH NEW" for recent received messages - - /* No message UID provide for fetching */ - config.fetch.uid = ""; - - /* Search the Email and close the session */ - MailClient.readMail(&imap); - } - - /* Close the seeion in case the session is still open */ - imap.closeSession(); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); -} - -void loop() -{ -} - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Show the result when reading finished */ - if (status.success()) - { - /* Print the result */ - printMessages(imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - } -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} - -void printSelectedMailboxInfo(IMAPSession &imap) -{ - /* Declare the selected folder info class to get the info of selected mailbox folder */ - SelectedFolderInfo sFolder = imap.selectedFolder(); - - /* Show the mailbox info */ - Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); - Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); - for (size_t i = 0; i < sFolder.flagCount(); i++) - Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); -} - -void printRFC822Messages(IMAP_MSG_Item &msg) -{ - Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); - for (size_t j = 0; j < msg.rfc822.size(); j++) - { - IMAP_MSG_Item rfc822 = msg.rfc822[j]; - Serial.printf("%d. \n", j + 1); - Serial.printf("Messsage ID: %s\n", rfc822.messageID); - Serial.printf("From: %s\n", rfc822.from); - Serial.printf("Sender: %s\n", rfc822.sender); - Serial.printf("To: %s\n", rfc822.to); - Serial.printf("CC: %s\n", rfc822.cc); - Serial.printf("Subject: %s\n", rfc822.subject); - Serial.printf("Date: %s\n", rfc822.date); - Serial.printf("Reply-To: %s\n", rfc822.reply_to); - Serial.printf("Return-Path: %s\n", rfc822.return_path); - Serial.printf("Comment: %s\n", rfc822.comment); - Serial.printf("Keyword: %s\n", rfc822.keyword); - Serial.printf("Text Message: %s\n", rfc822.text.content); - Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", rfc822.html.content); - Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); - - if (rfc822.attachments.size() > 0) - printAttacements(rfc822); - } -} - -void printAttacements(IMAP_MSG_Item &msg) -{ - Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); - for (size_t j = 0; j < msg.attachments.size(); j++) - { - IMAP_Attach_Item att = msg.attachments[j]; - /** att.type can be - * esp_mail_att_type_none or 0 - * esp_mail_att_type_attachment or 1 - * esp_mail_att_type_inline or 2 - */ - Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); - } - Serial.println(); -} - -void printMessages(IMAPSession &imap) -{ - /* Get the message list from the message list data */ - IMAP_MSG_List msgList = imap.data(); - - for (size_t i = 0; i < msgList.msgItems.size(); i++) - { - /* Iterate to get each message data through the message item data */ - IMAP_MSG_Item msg = msgList.msgItems[i]; - - Serial.println("################################"); - Serial.printf("Messsage Number: %s\n", msg.msgNo); - Serial.printf("Messsage UID: %s\n", msg.UID); - Serial.printf("Messsage ID: %s\n", msg.ID); - Serial.printf("Accept Language: %s\n", msg.acceptLang); - Serial.printf("Content Language: %s\n", msg.contentLang); - Serial.printf("From: %s\n", msg.from); - Serial.printf("From Charset: %s\n", msg.fromCharset); - Serial.printf("To: %s\n", msg.to); - Serial.printf("To Charset: %s\n", msg.toCharset); - Serial.printf("CC: %s\n", msg.cc); - Serial.printf("CC Charset: %s\n", msg.ccCharset); - Serial.printf("Date: %s\n", msg.date); - Serial.printf("Subject: %s\n", msg.subject); - Serial.printf("Subject Charset: %s\n", msg.subjectCharset); - - /* If the result contains the message info (Fetch mode) */ - if (!imap.headerOnly()) - { - Serial.printf("Text Message: %s\n", msg.text.content); - Serial.printf("Text Message Charset: %s\n", msg.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", msg.html.content); - Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); - - if (msg.attachments.size() > 0) - printAttacements(msg); - - if (msg.rfc822.size() > 0) - printRFC822Messages(msg); - } - - Serial.println(); - } -} +/** + * This example will search all Emails in the opened mailbox folder. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status); + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* Print all messages from the message list */ +void printMessages(IMAPSession &imap); + +/* Print all rfc822 messages included in the message */ +void printRFC822Messages(IMAP_MSG_Item &msg); + +/* Print all attachments info from the message */ +void printAttacements(IMAP_MSG_Item &msg); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Set the callback function to get the reading results */ + imap.callback(imapCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + /** ######################################################## + * Some properties of IMAP_Config and ESP_Mail_Session data + * accept the pointer to constant char i.e. const char*. + * + * You may assign a string literal to that properties like + * below example. + * + * config.search.criteria = String("UID SEARCH ALL").c_str(); + * + * String folder = "INBOX"; + * imap.selectFolder(folder.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Message UID to fetch or read */ + config.fetch.uid = ""; + + /** Search criteria + * + * A search key can also be a parenthesized list of one or more search keys + * (e.g., for use with the OR and NOT keys). + * + * Since IMAP protocol uses Polish notation, the search criteria which in the polish notation form can be. + * + * To search the message from "someone@email.com" with the subject "my subject" since 1 Jan 2021, your search criteria can be + * UID SEARCH (OR SUBJECT "my subject" FROM "someone@email.com") SINCE "Fri, 1 Jan 2021 21:52:25 -0800" + * + * To search the message from "mail1@domain.com" or from "mail2@domain.com", the search criteria will be + * UID SEARCH OR FROM mail1@domain.com FROM mail2@domain.com + * + * For more details on using parentheses, AND, OR and NOT search keys in search criteria. + * https://www.limilabs.com/blog/imap-search-requires-parentheses + * + * + */ + config.search.criteria = "UID SEARCH ALL"; + + /* Also search the unseen message */ + config.search.unseen_msg = true; + + /* Set the storage to save the downloaded files and attachments */ + config.storage.saved_path = "/email_data"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + config.storage.type = esp_mail_file_storage_type_flash; + + /** Set to download heades, text and html messaeges, + * attachments and inline images respectively. + */ + config.download.header = true; + config.download.text = true; + config.download.html = true; + config.download.attachment = true; + config.download.inlineImg = true; + + /** Set to enable the results i.e. html and text messaeges + * which the content stored in the IMAPSession object is limited + * by the option config.limit.msg_size. + * The whole message can be download through config.download.text + * or config.download.html which not depends on these enable options. + */ + config.enable.html = true; + config.enable.text = true; + + /* Set to enable the sort the result by message UID in the ascending order */ + config.enable.recent_sort = true; + + /* Set to report the download progress via the default serial port */ + config.enable.download_status = true; + + /* Set the limit of number of messages in the search results */ + config.limit.search = 5; + + /** Set the maximum size of message stored in + * IMAPSession object in byte + */ + config.limit.msg_size = 512; + + /** Set the maximum attachments and inline images files size + * that can be downloaded in byte. + * The file which its size is largger than this limit may be saved + * as truncated file. + */ + config.limit.attachment_size = 1024 * 1024 * 5; + + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* {Optional] */ + printSelectedMailboxInfo(imap); + + /** Read or search the Email and keep the TCP session to open + * The second parameter is for close the session. + */ + MailClient.readMail(&imap, false); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + + /** Open or select other mailbox folder + * The folder that previousely opened will be closed + */ + if (imap.selectFolder("Junk")) + { + /* {Optional] */ + printSelectedMailboxInfo(imap); + + /* Config to search all messages in the opened mailboax (Search mode) */ + config.search.criteria = "UID SEARCH ALL"; // or "UID SEARCH NEW" for recent received messages + + /* No message UID provide for fetching */ + config.fetch.uid = ""; + + /* Search the Email and close the session */ + MailClient.readMail(&imap); + } + + /* Close the seeion in case the session is still open */ + imap.closeSession(); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); +} + +void loop() +{ +} + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Show the result when reading finished */ + if (status.success()) + { + /* Print the result */ + printMessages(imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + } +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} + +void printSelectedMailboxInfo(IMAPSession &imap) +{ + /* Declare the selected folder info class to get the info of selected mailbox folder */ + SelectedFolderInfo sFolder = imap.selectedFolder(); + + /* Show the mailbox info */ + Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); + Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); + for (size_t i = 0; i < sFolder.flagCount(); i++) + Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); +} + +void printRFC822Messages(IMAP_MSG_Item &msg) +{ + Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); + for (size_t j = 0; j < msg.rfc822.size(); j++) + { + IMAP_MSG_Item rfc822 = msg.rfc822[j]; + Serial.printf("%d. \n", j + 1); + Serial.printf("Messsage ID: %s\n", rfc822.messageID); + Serial.printf("From: %s\n", rfc822.from); + Serial.printf("Sender: %s\n", rfc822.sender); + Serial.printf("To: %s\n", rfc822.to); + Serial.printf("CC: %s\n", rfc822.cc); + Serial.printf("Subject: %s\n", rfc822.subject); + Serial.printf("Date: %s\n", rfc822.date); + Serial.printf("Reply-To: %s\n", rfc822.reply_to); + Serial.printf("Return-Path: %s\n", rfc822.return_path); + Serial.printf("Comment: %s\n", rfc822.comment); + Serial.printf("Keyword: %s\n", rfc822.keyword); + Serial.printf("Text Message: %s\n", rfc822.text.content); + Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", rfc822.html.content); + Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); + + if (rfc822.attachments.size() > 0) + printAttacements(rfc822); + } +} + +void printAttacements(IMAP_MSG_Item &msg) +{ + Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); + for (size_t j = 0; j < msg.attachments.size(); j++) + { + IMAP_Attach_Item att = msg.attachments[j]; + /** att.type can be + * esp_mail_att_type_none or 0 + * esp_mail_att_type_attachment or 1 + * esp_mail_att_type_inline or 2 + */ + Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); + } + Serial.println(); +} + +void printMessages(IMAPSession &imap) +{ + /* Get the message list from the message list data */ + IMAP_MSG_List msgList = imap.data(); + + for (size_t i = 0; i < msgList.msgItems.size(); i++) + { + /* Iterate to get each message data through the message item data */ + IMAP_MSG_Item msg = msgList.msgItems[i]; + + Serial.println("################################"); + Serial.printf("Messsage Number: %s\n", msg.msgNo); + Serial.printf("Messsage UID: %s\n", msg.UID); + Serial.printf("Messsage ID: %s\n", msg.ID); + Serial.printf("Accept Language: %s\n", msg.acceptLang); + Serial.printf("Content Language: %s\n", msg.contentLang); + Serial.printf("From: %s\n", msg.from); + Serial.printf("From Charset: %s\n", msg.fromCharset); + Serial.printf("To: %s\n", msg.to); + Serial.printf("To Charset: %s\n", msg.toCharset); + Serial.printf("CC: %s\n", msg.cc); + Serial.printf("CC Charset: %s\n", msg.ccCharset); + Serial.printf("Date: %s\n", msg.date); + Serial.printf("Subject: %s\n", msg.subject); + Serial.printf("Subject Charset: %s\n", msg.subjectCharset); + + /* If the result contains the message info (Fetch mode) */ + if (!imap.headerOnly()) + { + Serial.printf("Text Message: %s\n", msg.text.content); + Serial.printf("Text Message Charset: %s\n", msg.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", msg.html.content); + Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); + + if (msg.attachments.size() > 0) + printAttacements(msg); + + if (msg.rfc822.size() > 0) + printRFC822Messages(msg); + } + + Serial.println(); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino b/lib/libesp32/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino rename to lib/libesp32/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino index 4fdc48102..bdebd82a5 --- a/lib/lib_div/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino +++ b/lib/libesp32/lib_mail/examples/Send_Access_Token/Send_Access_Token.ino @@ -1,203 +1,203 @@ - - -/** - *This example will log in with the SASL XOAUTH2 mechanisme using OAuth2.0 access token. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - - -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The user Email for OAuth2.0 access token */ -#define AUTHOR_EMAIL "################" - -/** The OAuth2.0 access token - * The generation, exchange and refresh of the access token are not available - * in this library. - * - * To test this using GMail, get the OAuth2.0 access token from this web site - * https://developers.google.com/oauthplayground/ - * - * You can use the ESP Signer library to generate OAuth2.0 access token - * The library is available here https://github.com/mobizt/ESP-Signer - * - * 1. Select the following scope (in Step 1) from Gmail API V1 - * https://mail.google.com/ - * https://mail.google.com/ - * - * 2. Click Authorize APIs button. - * 3. Cick Exchangeauthorization code for tokens. - * 4. From the response, look at access_token from the JSON payload node. - * 5. Copy that access token and paste to the AUTHOR_ACCESS_TOKEN value. - * - * The token will be expired in 3600 seconds (1 Hr). - * The AUTHOR_EMAIL above is the Email address that you granted to access the Gmail services. -*/ -#define AUTHOR_ACCESS_TOKEN "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.accessToken = AUTHOR_ACCESS_TOKEN; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending Email using Access token"; - message.addRecipient("Admin", "####@#####_dot_com"); - - message.text.content = "This is simple plain text message"; - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.text.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + *This example will log in with the SASL XOAUTH2 mechanisme using OAuth2.0 access token. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + + +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The user Email for OAuth2.0 access token */ +#define AUTHOR_EMAIL "################" + +/** The OAuth2.0 access token + * The generation, exchange and refresh of the access token are not available + * in this library. + * + * To test this using GMail, get the OAuth2.0 access token from this web site + * https://developers.google.com/oauthplayground/ + * + * You can use the ESP Signer library to generate OAuth2.0 access token + * The library is available here https://github.com/mobizt/ESP-Signer + * + * 1. Select the following scope (in Step 1) from Gmail API V1 + * https://mail.google.com/ + * https://mail.google.com/ + * + * 2. Click Authorize APIs button. + * 3. Cick Exchangeauthorization code for tokens. + * 4. From the response, look at access_token from the JSON payload node. + * 5. Copy that access token and paste to the AUTHOR_ACCESS_TOKEN value. + * + * The token will be expired in 3600 seconds (1 Hr). + * The AUTHOR_EMAIL above is the Email address that you granted to access the Gmail services. +*/ +#define AUTHOR_ACCESS_TOKEN "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.accessToken = AUTHOR_ACCESS_TOKEN; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending Email using Access token"; + message.addRecipient("Admin", "####@#####_dot_com"); + + message.text.content = "This is simple plain text message"; + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.text.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino b/lib/libesp32/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino rename to lib/libesp32/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino index f6a3c7467..7ff054a80 --- a/lib/lib_div/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino +++ b/lib/libesp32/lib_mail/examples/Send_Attachment_Blob/Send_Attachment_Blob.ino @@ -1,268 +1,268 @@ - - -/** - * This example will send the Email with attachments and - * inline images stored in heap and flash memories. - * - * The html and text version messages will be sent. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -/* This is for attachment data */ -#include "image.h" - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT esp_mail_smtp_port_587 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Enable the chunked data transfer with pipelining for large message if server supported */ - message.enable.chunking = true; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - - message.subject = "Test sending Email with attachments and inline images"; - message.addRecipient("user1", "####@#####_dot_com"); - - message.html.content = "This message contains 3 inline images and 1 attachment file.

"; - - /** The HTML text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "utf-8"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - message.text.content = "This message contains 3 inline images and 1 attachment file.\r\nThe inline images were not shown in the plain text message."; - message.text.charSet = "utf-8"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* The attachment data item */ - SMTP_Attachment att; - - /** Set the inline image info e.g. - * file name, MIME type, BLOB data, BLOB data size, - * transfer encoding (should be base64 for inline image) - */ - att.descr.filename = "firebase_logo.png"; - att.descr.mime = "image/png"; - att.blob.data = firebase_png; - att.blob.size = sizeof(firebase_png); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add inline image to the message */ - message.addInlineImage(att); - - /** Set the inline image info e.g. - * file name, MIME type, BLOB data, BLOB data size. - * The default transfer encoding is base64. - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "tree.gif"; - att.descr.mime = "image/gif"; - att.blob.data = tree_gif; - att.blob.size = sizeof(tree_gif); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add inline image to the message */ - message.addInlineImage(att); - - /** Set the inline image info e.g. - * file name, MIME type, BLOB data, BLOB data size. - * The default transfer encoding is base64. - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "bird.gif"; - att.descr.mime = "image/gif"; - att.blob.data = bird_gif; - att.blob.size = sizeof(bird_gif); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add inline image to the message */ - message.addInlineImage(att); - - /* Prepare the attachment data (from ram) */ - uint8_t *a = new uint8_t[512]; - int j = 0; - - for (int i = 0; i < 512; i++) - { - a[i] = j; - j++; - if (j > 255) - j = 0; - } - - /** Set the attachment info e.g. - * file name, MIME type, BLOB data, BLOB data size. - * The default transfer encoding is base64. - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "test.dat"; - att.descr.mime = "application/octet-stream"; - att.blob.data = a; - att.blob.size = 512; - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - /* Add attachment to the message */ - message.addAttachment(att); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending the Email and close the session */ - if (!MailClient.sendMail(&smtp, &message, true)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email with attachments and + * inline images stored in heap and flash memories. + * + * The html and text version messages will be sent. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +/* This is for attachment data */ +#include "image.h" + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT esp_mail_smtp_port_587 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Enable the chunked data transfer with pipelining for large message if server supported */ + message.enable.chunking = true; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + + message.subject = "Test sending Email with attachments and inline images"; + message.addRecipient("user1", "####@#####_dot_com"); + + message.html.content = "This message contains 3 inline images and 1 attachment file.

"; + + /** The HTML text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "utf-8"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + message.text.content = "This message contains 3 inline images and 1 attachment file.\r\nThe inline images were not shown in the plain text message."; + message.text.charSet = "utf-8"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* The attachment data item */ + SMTP_Attachment att; + + /** Set the inline image info e.g. + * file name, MIME type, BLOB data, BLOB data size, + * transfer encoding (should be base64 for inline image) + */ + att.descr.filename = "firebase_logo.png"; + att.descr.mime = "image/png"; + att.blob.data = firebase_png; + att.blob.size = sizeof(firebase_png); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add inline image to the message */ + message.addInlineImage(att); + + /** Set the inline image info e.g. + * file name, MIME type, BLOB data, BLOB data size. + * The default transfer encoding is base64. + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "tree.gif"; + att.descr.mime = "image/gif"; + att.blob.data = tree_gif; + att.blob.size = sizeof(tree_gif); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add inline image to the message */ + message.addInlineImage(att); + + /** Set the inline image info e.g. + * file name, MIME type, BLOB data, BLOB data size. + * The default transfer encoding is base64. + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "bird.gif"; + att.descr.mime = "image/gif"; + att.blob.data = bird_gif; + att.blob.size = sizeof(bird_gif); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add inline image to the message */ + message.addInlineImage(att); + + /* Prepare the attachment data (from ram) */ + uint8_t *a = new uint8_t[512]; + int j = 0; + + for (int i = 0; i < 512; i++) + { + a[i] = j; + j++; + if (j > 255) + j = 0; + } + + /** Set the attachment info e.g. + * file name, MIME type, BLOB data, BLOB data size. + * The default transfer encoding is base64. + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "test.dat"; + att.descr.mime = "application/octet-stream"; + att.blob.data = a; + att.blob.size = 512; + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + /* Add attachment to the message */ + message.addAttachment(att); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending the Email and close the session */ + if (!MailClient.sendMail(&smtp, &message, true)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Attachment_Blob/image.h b/lib/libesp32/lib_mail/examples/Send_Attachment_Blob/image.h old mode 100755 new mode 100644 similarity index 99% rename from lib/lib_div/lib_mail/examples/Send_Attachment_Blob/image.h rename to lib/libesp32/lib_mail/examples/Send_Attachment_Blob/image.h index 5ddd6829b..80ce0a0e7 --- a/lib/lib_div/lib_mail/examples/Send_Attachment_Blob/image.h +++ b/lib/libesp32/lib_mail/examples/Send_Attachment_Blob/image.h @@ -1,1851 +1,1851 @@ -#include - -static const uint8_t firebase_png[] PROGMEM = { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, - 0x70, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, - 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, - 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, - 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x55, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5E, 0xED, 0xDD, 0x59, - 0x90, 0x1D, 0xF5, 0x81, 0xE7, 0x7B, 0x3F, 0xF4, 0xC3, 0x84, 0x91, 0x40, 0x6E, 0xEF, 0xC6, 0x8B, - 0x30, 0x92, 0x4A, 0x1B, 0x58, 0x98, 0xCD, 0xA0, 0xA5, 0x4A, 0x12, 0x20, 0x6C, 0x9A, 0xB6, 0xBA, - 0x01, 0x63, 0x4F, 0x3F, 0x8C, 0x1E, 0x26, 0xC2, 0xF7, 0xE5, 0xC6, 0x25, 0xE2, 0xDE, 0x97, 0x3B, - 0x7D, 0x67, 0xAC, 0x6E, 0x63, 0x63, 0x83, 0x8D, 0xBC, 0xB1, 0x99, 0xA5, 0x10, 0x08, 0xED, 0x52, - 0x69, 0xAD, 0x92, 0xA0, 0xBB, 0xAB, 0xA7, 0xED, 0x6E, 0x77, 0xB7, 0x81, 0x42, 0x9B, 0x99, 0x89, - 0x89, 0xDB, 0x9A, 0xB9, 0x0E, 0x2F, 0x2C, 0x76, 0x61, 0x16, 0x49, 0x85, 0xA4, 0xFC, 0xDF, 0xFF, - 0x3F, 0x4F, 0xE6, 0xA9, 0x3C, 0x79, 0x7E, 0xE7, 0x9C, 0xCC, 0x3C, 0x99, 0x75, 0xCE, 0xC9, 0xFA, - 0x7E, 0x22, 0x7E, 0xA1, 0x6E, 0x2F, 0x42, 0xC2, 0x94, 0xE2, 0xFF, 0x8D, 0x7F, 0x65, 0x9E, 0xF7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x28, 0x82, 0x79, 0x6E, 0xCD, 0x80, 0xBF, 0xC3, 0x5F, 0x58, 0x12, 0xFC, - 0x4B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x1F, 0x73, 0x78, 0xCD, 0x3A, 0xEF, 0xB9, 0x35, 0x63, 0xE6, - 0xB9, 0x9B, 0x4D, 0x75, 0xCF, 0xAE, 0xF1, 0xE7, 0x3D, 0x7B, 0xD3, 0xA0, 0x0B, 0x92, 0xE0, 0x3F, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x98, 0x91, 0x35, 0xB3, 0xEB, 0xC2, 0x23, 0x12, 0x1F, 0xD1, - 0x79, 0x87, 0xD7, 0x6C, 0x08, 0xFE, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x8E, 0xFB, 0x16, 0x2B, - 0xEF, 0xD9, 0x35, 0xE3, 0x95, 0xE0, 0xB8, 0xD1, 0x98, 0x03, 0x57, 0x19, 0xB3, 0x7B, 0xAE, 0x31, - 0x3B, 0x3E, 0x65, 0xCC, 0xB6, 0x8F, 0x1A, 0xB3, 0xF3, 0xD3, 0xC6, 0x0C, 0x2D, 0x34, 0x66, 0x78, - 0xD9, 0x64, 0x84, 0x3C, 0x7B, 0xD3, 0x60, 0xF0, 0x5F, 0x07, 0x00, 0x00, 0x00, 0x80, 0xE4, 0xAA, - 0x37, 0x1F, 0x2E, 0x3C, 0x76, 0x7C, 0xC2, 0x98, 0xED, 0x1F, 0xAB, 0x84, 0x87, 0xDA, 0x2E, 0x1B, - 0x26, 0x87, 0x6F, 0xA8, 0x84, 0xC8, 0xA1, 0x9B, 0xD6, 0x06, 0x3F, 0x05, 0x00, 0x00, 0x00, 0x00, - 0xB4, 0xE6, 0x9E, 0xF9, 0xF0, 0xE3, 0x63, 0xCF, 0xA2, 0x4A, 0x78, 0x34, 0x8B, 0x8F, 0x70, 0xDB, - 0x3F, 0x6E, 0xCC, 0xC8, 0x0A, 0xF7, 0xAD, 0x58, 0xE3, 0x66, 0xB4, 0x7F, 0x56, 0xF0, 0x53, 0x01, - 0x00, 0x00, 0x00, 0x40, 0x73, 0xDE, 0x73, 0x37, 0x9F, 0x34, 0xFB, 0xAF, 0x48, 0x1E, 0x1F, 0xE1, - 0x76, 0x5E, 0x62, 0xCC, 0xE1, 0xD5, 0x76, 0x37, 0xAE, 0x0F, 0x7E, 0x2A, 0x00, 0x00, 0x00, 0x00, - 0x68, 0xCC, 0x3D, 0x78, 0x6E, 0x0E, 0xAF, 0x4A, 0x1F, 0x1F, 0xFE, 0x3E, 0x62, 0xCC, 0xBE, 0xCF, - 0x18, 0xEF, 0xF0, 0x4D, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x68, 0xCD, 0xFF, 0x8C, 0x8F, 0xBD, - 0x97, 0x67, 0x8B, 0x0F, 0xB7, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x20, 0x19, 0x1B, - 0x20, 0xEB, 0xCD, 0xAE, 0x4F, 0xC7, 0xE2, 0xA2, 0xD5, 0x82, 0xF8, 0x08, 0xB7, 0x67, 0xB1, 0x31, - 0xCF, 0xDE, 0x64, 0xDC, 0x6D, 0x4A, 0xF0, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x40, 0x3D, 0xEF, 0xB9, - 0x9B, 0x07, 0x75, 0x64, 0x34, 0x5A, 0x2C, 0x3E, 0xC2, 0x1D, 0x1A, 0xE0, 0xB5, 0xBC, 0x00, 0x00, - 0x00, 0x00, 0x9A, 0xF3, 0x0E, 0x2E, 0x1B, 0xD5, 0xA1, 0xA1, 0x26, 0xC2, 0x23, 0xDC, 0xD0, 0x02, - 0x6E, 0x41, 0x00, 0x00, 0x00, 0x00, 0x34, 0xE7, 0x1D, 0xB8, 0x76, 0x5C, 0xC7, 0x46, 0x7C, 0x22, - 0x3A, 0xE2, 0x1B, 0x59, 0x6E, 0xBC, 0xC3, 0x37, 0x8E, 0x06, 0x3F, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x4C, 0x72, 0x6F, 0xAE, 0x32, 0xFB, 0x96, 0xD8, 0x78, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x0D, 0xB5, - 0x5D, 0x73, 0x2A, 0xB7, 0x20, 0xCF, 0xAD, 0x19, 0x08, 0xFE, 0x12, 0x00, 0x00, 0x00, 0x00, 0x50, - 0xE1, 0xBF, 0x01, 0x6B, 0x77, 0x9F, 0x8D, 0x07, 0x15, 0x1D, 0xE1, 0x44, 0x68, 0x34, 0xDB, 0xF0, - 0xF5, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xE7, 0x7F, 0x02, 0xFA, 0xAE, 0xB9, 0x36, 0x1C, - 0x54, 0x78, 0xB8, 0x89, 0xC0, 0x68, 0xB5, 0x5D, 0x97, 0x1A, 0xF3, 0xEC, 0x8D, 0xC6, 0x3C, 0xB7, - 0x9A, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x93, 0x6C, 0x80, 0xAC, 0xD7, 0xE1, 0xE1, 0x26, 0xE2, - 0xA2, 0xE5, 0x3E, 0x5C, 0xD9, 0x81, 0xAB, 0x8C, 0xF7, 0xEC, 0x0D, 0x27, 0x83, 0xBF, 0x0C, 0x00, - 0x00, 0x00, 0x00, 0xBC, 0xE7, 0x3D, 0xDE, 0xF0, 0x8A, 0x06, 0x6F, 0xC0, 0x52, 0x71, 0xD1, 0x6A, - 0x41, 0x7C, 0xB8, 0xED, 0xF8, 0x44, 0xE5, 0x16, 0xE4, 0xF0, 0x8D, 0xEB, 0x82, 0xBF, 0x14, 0x00, - 0x00, 0x00, 0x80, 0xE9, 0xCE, 0x3B, 0x70, 0xFD, 0xC9, 0xDC, 0xE3, 0x23, 0x1C, 0xB7, 0x20, 0x00, - 0x00, 0x00, 0x00, 0xA2, 0xCC, 0xFE, 0x2B, 0x6D, 0x2C, 0x14, 0x10, 0x1F, 0x6E, 0xEE, 0x16, 0xE4, - 0xF0, 0x2A, 0x77, 0x0B, 0xB2, 0x3E, 0xF8, 0xCB, 0x01, 0x00, 0x00, 0x00, 0x98, 0xAE, 0x6C, 0x18, - 0x2C, 0x31, 0x7B, 0x2E, 0xB3, 0xB1, 0x50, 0x40, 0x7C, 0x84, 0xDB, 0x77, 0xB9, 0x7B, 0x23, 0xD6, - 0xB8, 0x7B, 0xDD, 0x6F, 0xF0, 0x97, 0x05, 0x00, 0x00, 0x00, 0x30, 0x1D, 0x99, 0x43, 0x37, 0xAD, - 0x9D, 0x7C, 0x03, 0x96, 0x8A, 0x8B, 0x56, 0x13, 0xC1, 0x11, 0xDF, 0xF6, 0x8F, 0x71, 0x0B, 0x02, - 0x00, 0x00, 0x00, 0x20, 0x78, 0x03, 0xD6, 0xF6, 0x8F, 0xDB, 0x50, 0x50, 0x71, 0xD1, 0x6A, 0x22, - 0x36, 0x1A, 0x8D, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB3, 0x37, 0x0D, 0xEA, 0xB8, 0x68, - 0x35, 0x11, 0x19, 0xAD, 0x76, 0xA8, 0xDF, 0x3D, 0x90, 0x3E, 0x18, 0xFC, 0xA5, 0x01, 0x00, 0x00, - 0x00, 0x4C, 0x37, 0xDE, 0x81, 0x6B, 0x46, 0x75, 0x60, 0x34, 0x9B, 0x88, 0x8B, 0x56, 0xDB, 0xFA, - 0x21, 0x63, 0x86, 0xE6, 0x57, 0x5E, 0xCB, 0x3B, 0xB2, 0x66, 0x76, 0xF0, 0x97, 0x07, 0x00, 0x00, - 0x00, 0x30, 0x9D, 0x98, 0x03, 0x57, 0xDB, 0x40, 0x50, 0x91, 0xD1, 0x68, 0x22, 0x2E, 0x5A, 0xCD, - 0xC5, 0x47, 0x38, 0x6E, 0x41, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC9, 0xDD, 0x44, 0x98, 0x7D, 0x9F, - 0xB1, 0x91, 0xA0, 0x42, 0x43, 0x4D, 0xC4, 0x45, 0xAB, 0x45, 0xE3, 0xC3, 0x6D, 0xF7, 0xDC, 0xCA, - 0x2D, 0xC8, 0x73, 0xAB, 0x07, 0x82, 0x5F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC0, 0x3C, 0xB7, - 0x66, 0xC0, 0xEC, 0x9E, 0x67, 0x43, 0x41, 0xC5, 0x46, 0x7C, 0x22, 0x2E, 0x5A, 0x2D, 0x1E, 0x1F, - 0xE1, 0x86, 0xAF, 0x73, 0x0F, 0xA4, 0x8F, 0x06, 0xBF, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x81, - 0xF7, 0xEC, 0x4D, 0x77, 0x99, 0x9D, 0x97, 0xD8, 0x58, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x8B, 0x56, - 0x53, 0xE1, 0x11, 0x6E, 0xD7, 0xA7, 0xB9, 0x05, 0x01, 0x00, 0x00, 0x00, 0xA6, 0x1B, 0xEF, 0xF0, - 0x8D, 0x1B, 0x74, 0x70, 0x44, 0x27, 0xE2, 0xA2, 0xD5, 0x54, 0x74, 0xC4, 0xC7, 0x2D, 0x08, 0x00, - 0x00, 0x00, 0x30, 0xBD, 0x78, 0x07, 0xAF, 0x6F, 0xF1, 0x06, 0x2C, 0x11, 0x17, 0xAD, 0xA6, 0x62, - 0x43, 0xCD, 0x7D, 0xF6, 0x88, 0xBB, 0x05, 0x39, 0x7C, 0xE3, 0xBA, 0xE0, 0x97, 0x03, 0x00, 0x00, - 0x00, 0xA0, 0xCC, 0xBC, 0x03, 0xD7, 0x9D, 0xD4, 0xE1, 0xE1, 0x26, 0xE2, 0xA2, 0xD5, 0x54, 0x68, - 0x34, 0xDB, 0xFE, 0x2B, 0xDD, 0x1B, 0xB1, 0x4E, 0x06, 0xBF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x65, - 0xD6, 0xF8, 0x0D, 0x58, 0x22, 0x2E, 0x5A, 0x4D, 0x05, 0x46, 0xAB, 0xB9, 0x5B, 0x90, 0xC3, 0x37, - 0x70, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x94, 0x9D, 0xFF, 0x06, 0xAC, 0xA1, 0x05, 0x36, 0x1E, 0x3A, - 0x14, 0x1F, 0xFE, 0x3E, 0x68, 0x5C, 0x04, 0x79, 0x87, 0x6E, 0x18, 0x37, 0xA3, 0xFD, 0xB3, 0x82, - 0x5F, 0x1A, 0x00, 0x00, 0x00, 0x80, 0xB2, 0x71, 0xB7, 0x0E, 0x66, 0xD7, 0x1C, 0x1B, 0x10, 0x1D, - 0x8C, 0x0F, 0x37, 0xF7, 0xD7, 0x3D, 0xB4, 0xD2, 0x98, 0xC3, 0xAB, 0xD7, 0x07, 0xBF, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x65, 0x63, 0x03, 0x64, 0xBD, 0xD9, 0x7E, 0x71, 0x25, 0x00, 0x3A, 0x15, 0x1F, - 0xE1, 0xF6, 0x5E, 0xC6, 0x2D, 0x08, 0x00, 0x00, 0x00, 0x50, 0x66, 0xDE, 0xC8, 0xCA, 0xA1, 0xAE, - 0x88, 0x0F, 0xB7, 0xE0, 0x16, 0xC4, 0x3B, 0xBC, 0x7A, 0x43, 0xF0, 0xCB, 0x03, 0x00, 0x00, 0x00, - 0x50, 0x26, 0xDE, 0xC1, 0x6B, 0xC7, 0xBA, 0x22, 0x3E, 0xC2, 0xED, 0xEE, 0xAB, 0x3C, 0x90, 0x3E, - 0xB2, 0x66, 0x76, 0xF0, 0x4B, 0x04, 0x00, 0x00, 0x00, 0x50, 0x16, 0x66, 0xFF, 0x67, 0x75, 0x5C, - 0xB4, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0xD1, 0x11, 0xDF, 0xC8, 0x0A, 0xE3, 0x1D, 0x5A, 0x3D, 0x18, - 0xFC, 0x12, 0x01, 0x00, 0x00, 0x00, 0x94, 0x81, 0xBB, 0x65, 0x30, 0xFB, 0x2E, 0xD7, 0x81, 0xD1, - 0x6C, 0x32, 0x2C, 0x92, 0x4C, 0xC4, 0x86, 0x1A, 0xB7, 0x20, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x98, - 0xE7, 0x56, 0x0F, 0x98, 0x5D, 0x97, 0xEA, 0xC8, 0x68, 0x34, 0x19, 0x16, 0x49, 0x26, 0x42, 0xA3, - 0xD9, 0x0E, 0x7E, 0xCE, 0x78, 0x87, 0x6F, 0x18, 0x0D, 0x7E, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x7A, - 0x9D, 0xFF, 0x06, 0xAC, 0x1D, 0x9F, 0xD0, 0xA1, 0xA1, 0x26, 0xC3, 0x22, 0xC9, 0x44, 0x60, 0xB4, - 0xDA, 0xCE, 0x4B, 0x82, 0x5B, 0x90, 0xD5, 0x03, 0xC1, 0x2F, 0x17, 0x00, 0x00, 0x00, 0x40, 0x2F, - 0xF3, 0x9E, 0xBD, 0x61, 0x50, 0x86, 0x86, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0x71, 0x91, 0x74, 0x07, - 0xAF, 0x35, 0xDE, 0xE1, 0x55, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE0, 0x1D, 0xBC, 0x6E, - 0x54, 0xC6, 0x46, 0x7C, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, 0x6D, 0xF9, 0x80, 0x31, 0x3B, - 0x67, 0xBB, 0x0F, 0x26, 0x34, 0xE6, 0xD0, 0xAA, 0xB5, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, 0x40, - 0xAF, 0xF2, 0x0E, 0x5C, 0x33, 0x2E, 0x83, 0x23, 0x3A, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, - 0x5C, 0x7C, 0x84, 0xDB, 0x77, 0x85, 0xFB, 0x5C, 0x90, 0x93, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, - 0x40, 0x2F, 0x72, 0x9F, 0x36, 0xDE, 0xF2, 0x0D, 0x58, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, - 0x45, 0xE3, 0xC3, 0x6D, 0xDB, 0xC7, 0x2A, 0xB7, 0x20, 0x87, 0x57, 0xAD, 0x0B, 0x7E, 0xE9, 0x00, - 0x00, 0x00, 0x00, 0x7A, 0x8D, 0xFF, 0x06, 0xAC, 0xDD, 0x73, 0xEB, 0xA3, 0x23, 0x9C, 0x0C, 0x8B, - 0x24, 0x13, 0x51, 0x91, 0x74, 0xF1, 0xF8, 0x08, 0xC7, 0x2D, 0x08, 0x00, 0x00, 0x00, 0xD0, 0xDB, - 0xCC, 0xE1, 0x1B, 0xD7, 0x35, 0x7C, 0x05, 0xAF, 0x0C, 0x8B, 0x24, 0x13, 0x51, 0x91, 0x74, 0x2A, - 0x3C, 0xC2, 0x6D, 0xB5, 0xBF, 0xA6, 0x43, 0x03, 0xEE, 0x26, 0x64, 0x7D, 0xF0, 0xCB, 0x07, 0x00, - 0x00, 0x00, 0xD0, 0x4B, 0xFC, 0x57, 0xF0, 0xF6, 0x42, 0x7C, 0x84, 0xDB, 0xB3, 0xD8, 0x7D, 0x3A, - 0xFA, 0xB8, 0xFB, 0xD6, 0xB1, 0xE0, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x57, 0x78, 0xC3, 0xCB, - 0xEB, 0xDF, 0x80, 0x25, 0xC3, 0x22, 0xC9, 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x0D, 0x35, 0x6E, 0x41, - 0x00, 0x00, 0x00, 0x80, 0xDE, 0xE5, 0x1D, 0xBC, 0x76, 0xAC, 0x67, 0xE2, 0x23, 0xDC, 0x9E, 0x45, - 0xC6, 0x3B, 0xB4, 0x6A, 0xDC, 0x8C, 0xF4, 0xCF, 0x0E, 0x7E, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x7A, - 0x81, 0xD9, 0x7F, 0x45, 0x6F, 0xC5, 0x87, 0xBF, 0xF7, 0x1B, 0x33, 0xB2, 0xCC, 0x46, 0xC8, 0xCA, - 0xC1, 0xE0, 0xB7, 0x01, 0x00, 0x00, 0x00, 0xA0, 0xDB, 0x99, 0xC3, 0x37, 0x2E, 0x71, 0xB7, 0x09, - 0x3D, 0x17, 0x1F, 0x6E, 0xEE, 0xCD, 0x5D, 0x87, 0x57, 0xD9, 0x10, 0xE9, 0xCD, 0x5B, 0x10, 0xF7, - 0xEB, 0xB6, 0x1B, 0x08, 0xC6, 0x4D, 0x0E, 0x00, 0x00, 0x00, 0xCA, 0xCF, 0x1C, 0xBA, 0x69, 0xAD, - 0xFF, 0x06, 0x2C, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x5A, 0x10, 0x1F, - 0xE1, 0x7A, 0xEC, 0x16, 0xC4, 0xFF, 0xCC, 0x95, 0x91, 0xFE, 0xF5, 0xDE, 0xA1, 0x81, 0x93, 0xFE, - 0x73, 0x2C, 0x91, 0xF9, 0xFF, 0x9A, 0xFD, 0xF7, 0x78, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0xA5, 0x55, - 0x79, 0x03, 0xD6, 0x47, 0x6D, 0x10, 0xA8, 0xB8, 0x68, 0x35, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, - 0xD5, 0x62, 0xF1, 0xE1, 0xB6, 0xF3, 0x53, 0xC1, 0x2D, 0xC8, 0xEA, 0x81, 0xE0, 0xB7, 0xD4, 0xB5, - 0xCC, 0xA1, 0xFE, 0xB5, 0xDE, 0xC8, 0xC0, 0x78, 0x3C, 0x3C, 0xE2, 0xAB, 0xFC, 0x67, 0xFA, 0xD7, - 0x06, 0xFF, 0x35, 0x00, 0x00, 0x00, 0xA0, 0x3C, 0xBC, 0x67, 0x6F, 0x18, 0xD4, 0x71, 0xD1, 0x6A, - 0x22, 0x2A, 0x92, 0x4E, 0xC6, 0x45, 0xAB, 0x89, 0xF8, 0x08, 0x77, 0xF0, 0x1A, 0xE3, 0x1D, 0x5E, - 0x39, 0x1A, 0xFC, 0x96, 0xBA, 0x92, 0x19, 0xE9, 0x5F, 0x57, 0x8D, 0x8C, 0x91, 0xE5, 0xFE, 0x43, - 0xF4, 0x66, 0xC7, 0xC7, 0x6B, 0xE7, 0xFE, 0x35, 0xF7, 0xEF, 0xF9, 0xFF, 0x99, 0x7E, 0x37, 0x3E, - 0xF1, 0x1D, 0x00, 0x00, 0x00, 0xE5, 0xE2, 0x1D, 0xB8, 0x7A, 0x54, 0x07, 0x46, 0xB3, 0x89, 0xA8, - 0x48, 0x3A, 0x19, 0x17, 0xAD, 0x26, 0xA2, 0x23, 0xBA, 0x2E, 0xBF, 0x05, 0xB1, 0x21, 0x31, 0xBB, - 0x7A, 0xF3, 0x31, 0x34, 0xBF, 0xF2, 0x6B, 0xDE, 0x3C, 0x4B, 0xCF, 0xFD, 0x7B, 0x43, 0x7D, 0x7E, - 0x80, 0x78, 0x23, 0x2B, 0x78, 0xCB, 0x17, 0x00, 0x00, 0x00, 0xCA, 0xC5, 0xEC, 0xBF, 0xD2, 0x86, - 0x81, 0x8A, 0x8C, 0x46, 0x13, 0x51, 0x91, 0x74, 0x32, 0x2E, 0x5A, 0x2D, 0x12, 0x1A, 0xCD, 0xE6, - 0x6E, 0x41, 0x0E, 0xAD, 0x1A, 0x0B, 0x7E, 0x5B, 0x5D, 0xC5, 0xC6, 0xC7, 0x90, 0x1F, 0x1F, 0xBB, - 0x3E, 0xAD, 0xA3, 0xA3, 0x66, 0x17, 0x55, 0xB6, 0xF3, 0x92, 0x4A, 0x84, 0x0C, 0xAF, 0xE8, 0xEA, - 0x9B, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x31, 0xFF, 0x81, 0xE8, 0xBD, 0x97, 0xDB, 0x38, 0x50, 0xA1, - 0xA1, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x9A, 0x08, 0x8D, 0x46, 0x73, 0xCF, 0xB1, 0xB8, - 0x5B, 0x90, 0xC3, 0xAB, 0xBA, 0xEA, 0xDB, 0x96, 0x6C, 0x44, 0x0C, 0xF8, 0xF1, 0xE1, 0xDE, 0xD8, - 0x25, 0x83, 0x23, 0xBA, 0x20, 0x3E, 0x62, 0x11, 0xE2, 0x7E, 0x8E, 0xE0, 0xA7, 0x03, 0x00, 0x00, - 0x00, 0x7A, 0x97, 0x79, 0x6E, 0xF5, 0x80, 0x7F, 0x30, 0x96, 0xB1, 0x11, 0x9F, 0x88, 0x8A, 0xA4, - 0x93, 0x71, 0xD1, 0x6A, 0x22, 0x32, 0x5A, 0x6D, 0xDF, 0x12, 0xE3, 0x1D, 0x5E, 0x75, 0x32, 0xF8, - 0xED, 0x75, 0x05, 0xEF, 0xD0, 0xC0, 0xA8, 0x39, 0x70, 0x55, 0x10, 0x18, 0xCD, 0x16, 0x8B, 0x8F, - 0x70, 0xFB, 0xAF, 0xE4, 0x16, 0x04, 0x00, 0x00, 0x00, 0xE5, 0xE0, 0x3D, 0x7B, 0xC3, 0x5D, 0x66, - 0xC7, 0xA7, 0x6C, 0x24, 0xA8, 0xE0, 0x88, 0x4E, 0x44, 0x45, 0xD2, 0xC9, 0xB8, 0x68, 0x35, 0x11, - 0x17, 0x49, 0x16, 0xDC, 0x82, 0x78, 0x87, 0x56, 0xDD, 0x15, 0xFC, 0x16, 0x3B, 0xAA, 0x7A, 0xFB, - 0xE1, 0x1E, 0x30, 0x97, 0xD1, 0x11, 0x4E, 0x84, 0x47, 0xB8, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, - 0x00, 0x80, 0x72, 0xF0, 0x0E, 0xDF, 0xB8, 0x41, 0x07, 0x47, 0x74, 0x22, 0x2A, 0x92, 0x4E, 0xC6, - 0x45, 0xAB, 0x89, 0xB0, 0x48, 0xB3, 0x3D, 0x0B, 0x8D, 0x37, 0xB2, 0x72, 0xBC, 0x1B, 0x3E, 0x4B, - 0x23, 0xD9, 0xED, 0x87, 0x88, 0x8E, 0xF8, 0xB8, 0x05, 0x01, 0x00, 0x00, 0x40, 0x19, 0x78, 0x07, - 0xAF, 0x6B, 0xF1, 0x06, 0x2C, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, 0xD5, 0x44, 0x50, 0xA4, 0xDA, - 0x1F, 0x57, 0xFE, 0xDA, 0x23, 0x2B, 0xEC, 0x56, 0xAE, 0x0F, 0x7E, 0x9B, 0x1D, 0xE1, 0x3E, 0xC7, - 0xC3, 0xBF, 0xFD, 0x70, 0x7F, 0x1F, 0x65, 0x78, 0xB8, 0x89, 0xD8, 0x50, 0xF3, 0x7F, 0x4F, 0xFD, - 0xC6, 0x1C, 0xE4, 0xB3, 0x41, 0x00, 0x00, 0x00, 0xD0, 0xC3, 0xBC, 0x03, 0x9F, 0x3B, 0x59, 0x1F, - 0x1D, 0xE1, 0x22, 0x31, 0x91, 0x76, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xD9, 0xF8, 0x08, - 0xD7, 0x05, 0xB7, 0x20, 0xFE, 0xA7, 0x9A, 0xBB, 0x07, 0xFC, 0x65, 0x78, 0xB8, 0x89, 0xD0, 0x68, - 0xB6, 0xBD, 0x97, 0xB9, 0x5B, 0x90, 0xAE, 0x7A, 0xBE, 0x05, 0x00, 0x00, 0x00, 0x48, 0xA5, 0xF1, - 0x1B, 0xB0, 0x44, 0x54, 0x24, 0x9D, 0x8C, 0x8B, 0x56, 0x53, 0x41, 0x91, 0x66, 0x91, 0xF8, 0x70, - 0x73, 0xBF, 0x8E, 0xE1, 0xA5, 0xEE, 0x13, 0xC5, 0x37, 0x04, 0xBF, 0xD5, 0x29, 0x65, 0xC2, 0x0F, - 0x1D, 0x74, 0x7F, 0x2F, 0xF3, 0x88, 0x0F, 0x37, 0xF7, 0x7B, 0xF2, 0x9F, 0x05, 0x59, 0xCE, 0x87, - 0x13, 0x02, 0x00, 0x00, 0xA0, 0xF7, 0xF8, 0x6F, 0xC0, 0x72, 0x1F, 0x8A, 0x57, 0xB6, 0xF8, 0x08, - 0xB7, 0x6B, 0x8E, 0x31, 0x87, 0x56, 0xBA, 0x43, 0xFB, 0x94, 0x7F, 0x90, 0x5F, 0xF3, 0xDB, 0x0F, - 0x11, 0x17, 0x49, 0xB7, 0x67, 0xB1, 0xF1, 0x86, 0x97, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x80, 0xDE, - 0x63, 0x0E, 0xDD, 0xB4, 0xD6, 0xFF, 0x60, 0xBC, 0x32, 0xC6, 0x47, 0x38, 0x77, 0x0B, 0x72, 0x68, - 0xE5, 0x60, 0xF0, 0x5B, 0x9E, 0x12, 0xCD, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0xDD, 0x33, 0x17, 0xDA, - 0xDF, 0x93, 0xFD, 0x7B, 0x66, 0x7F, 0x4F, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0xE7, 0x98, 0xC3, - 0x37, 0xAE, 0xF7, 0x5F, 0x5B, 0x5B, 0xD6, 0xF8, 0x70, 0x0B, 0x6F, 0x41, 0x0E, 0xF7, 0x2F, 0x09, - 0x7E, 0xDB, 0x85, 0x72, 0xCF, 0x9C, 0xF8, 0xB7, 0x1F, 0xEE, 0x66, 0x29, 0xEF, 0xF8, 0x08, 0xB7, - 0x7B, 0x9E, 0xF1, 0x46, 0x96, 0x8F, 0x9B, 0xA1, 0xCE, 0xBF, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, - 0xCC, 0x1B, 0x59, 0x39, 0x54, 0xEA, 0xF8, 0x08, 0x77, 0xE0, 0x6A, 0x77, 0x0B, 0x32, 0x25, 0xAF, - 0xB0, 0x35, 0x23, 0xFD, 0xEB, 0xCD, 0xC8, 0x72, 0xFB, 0xD7, 0xB5, 0xBF, 0xC6, 0x22, 0xE2, 0xC3, - 0x6D, 0xF3, 0xFB, 0xC2, 0x5B, 0x90, 0x8E, 0xBE, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, 0xC5, 0xDB, - 0x7F, 0xED, 0x58, 0xE9, 0xE3, 0xC3, 0x6D, 0xC7, 0x27, 0xC3, 0x67, 0x41, 0x0A, 0xFD, 0x20, 0x3F, - 0xFF, 0xF6, 0x63, 0x64, 0x60, 0xBC, 0xFE, 0xF6, 0x43, 0x44, 0x45, 0xD2, 0xC5, 0xE3, 0x23, 0x1C, - 0xB7, 0x20, 0x00, 0x00, 0x00, 0xE8, 0x35, 0x66, 0xDF, 0x12, 0x1D, 0x15, 0x49, 0x27, 0xE3, 0xA2, - 0xD5, 0x54, 0x50, 0xA4, 0x99, 0x08, 0x8C, 0x56, 0x73, 0x37, 0x06, 0x53, 0x70, 0x0B, 0xA2, 0x6F, - 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8F, 0x70, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0x97, 0x98, - 0x91, 0x35, 0xB3, 0xDD, 0xE7, 0x4A, 0xC8, 0xB0, 0x48, 0x32, 0x19, 0x17, 0xAD, 0x16, 0x8F, 0x89, - 0xB4, 0x13, 0x71, 0xD1, 0x6A, 0xEE, 0xA0, 0xEE, 0xB6, 0xED, 0x63, 0x95, 0x5B, 0x90, 0x43, 0xC5, - 0x7C, 0x90, 0x9F, 0xBE, 0xFD, 0x10, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0xE2, 0xE3, 0x16, 0x04, 0x00, - 0x00, 0x00, 0xBD, 0xC2, 0x8C, 0xAC, 0x1E, 0x30, 0x3B, 0x2F, 0xD1, 0x71, 0xD1, 0x6A, 0x32, 0x2E, - 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0x30, 0x3E, 0xC2, 0xED, 0xFD, 0x8C, 0xBB, - 0x05, 0x29, 0xE4, 0x15, 0xB6, 0xEE, 0xF3, 0x46, 0xCC, 0xF0, 0xF5, 0xF6, 0xAF, 0x33, 0x45, 0xF1, - 0x11, 0xEE, 0xE0, 0xE7, 0x6C, 0x84, 0xAC, 0xE8, 0xC8, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x89, - 0x99, 0xC3, 0xAB, 0xD7, 0x9B, 0xED, 0x17, 0xEB, 0xC0, 0x68, 0x36, 0x19, 0x17, 0xAD, 0xA6, 0x82, - 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x5A, 0x3C, 0x3E, 0xDC, 0xB6, 0x7E, 0x38, 0x7C, 0x16, 0x24, 0xD7, - 0x57, 0xD8, 0xDA, 0x9F, 0x6F, 0xB6, 0xFF, 0xDA, 0x5D, 0xF7, 0x4A, 0xE3, 0xA9, 0x8C, 0x0F, 0xB7, - 0x9D, 0xB3, 0xED, 0xEF, 0x67, 0x85, 0xFB, 0x3D, 0x4D, 0xF9, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x79, 0x87, 0x57, 0x6F, 0x90, 0x81, 0xD1, 0x6C, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, - 0x89, 0xB8, 0x68, 0x35, 0x15, 0x1F, 0xE1, 0x82, 0x5B, 0x10, 0xF7, 0x2D, 0x53, 0xC1, 0xDF, 0x8A, - 0xB6, 0x79, 0x87, 0x06, 0x06, 0x27, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8C, 0x24, 0x73, - 0xB7, 0x20, 0xC3, 0x2B, 0xA6, 0xF4, 0xB3, 0x4E, 0x00, 0x00, 0x00, 0x80, 0x54, 0xBC, 0x83, 0x9F, - 0x1B, 0x95, 0x91, 0xD1, 0x68, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0x35, - 0x15, 0x1D, 0xD1, 0xB9, 0x5F, 0x97, 0x7F, 0x63, 0xB0, 0x32, 0x97, 0x87, 0xB7, 0x6B, 0x6F, 0x3F, - 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8B, 0xA4, 0xE3, 0x16, 0x04, 0x00, 0x00, 0x00, 0xDD, 0xCE, 0xDB, - 0x7F, 0xD5, 0xB8, 0x0C, 0x0D, 0xB5, 0xBA, 0xB0, 0x48, 0x32, 0x15, 0x14, 0x69, 0x26, 0xE2, 0xA2, - 0xD5, 0x54, 0x70, 0xA8, 0x0D, 0x2D, 0x30, 0xDE, 0xC8, 0xCA, 0xF1, 0x3C, 0x6E, 0x41, 0xFC, 0xDB, - 0x8F, 0x03, 0xD7, 0xDA, 0x9F, 0x57, 0x44, 0x45, 0xD2, 0xA9, 0xA8, 0x48, 0x3B, 0xF7, 0x96, 0xAF, - 0xE1, 0x65, 0xDC, 0x82, 0x34, 0x60, 0xC6, 0xFE, 0xC3, 0x2C, 0xF3, 0xF2, 0xBF, 0x5F, 0x67, 0x4E, - 0x7C, 0x65, 0x7D, 0x64, 0x6B, 0xDD, 0xBF, 0x1E, 0xFC, 0x47, 0x00, 0x00, 0x00, 0x50, 0x14, 0x77, - 0xF0, 0x4E, 0xFC, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, - 0x0A, 0x8D, 0x46, 0x73, 0xBF, 0xC6, 0x1C, 0x6E, 0x41, 0xDC, 0xA7, 0xAB, 0xFB, 0xB7, 0x1F, 0xEE, - 0x59, 0x1A, 0x15, 0x16, 0x49, 0xA6, 0x62, 0x22, 0xCB, 0xB6, 0x7F, 0xD4, 0xFE, 0x7E, 0x96, 0xDB, - 0x2D, 0x2D, 0xF4, 0xB3, 0x4E, 0x7A, 0x8D, 0x0B, 0x0C, 0xEF, 0xC4, 0x97, 0x07, 0xCD, 0x2F, 0xBE, - 0x62, 0xE4, 0x4E, 0x7C, 0xC9, 0xF8, 0xFF, 0x3E, 0x21, 0x02, 0x00, 0x00, 0x50, 0x1C, 0xFF, 0x0D, - 0x58, 0xBB, 0xE6, 0xE8, 0xE0, 0x88, 0xAE, 0x2E, 0x2C, 0x92, 0x4C, 0x05, 0x45, 0x9A, 0x89, 0xB8, - 0x68, 0x35, 0x15, 0x19, 0xAD, 0xE6, 0xBF, 0xC2, 0x76, 0xE5, 0x78, 0x3B, 0xDF, 0xB6, 0xE4, 0x1D, - 0x1A, 0x18, 0x35, 0xFB, 0xAF, 0xB4, 0x3F, 0x9F, 0x08, 0x8B, 0x24, 0x53, 0x21, 0x91, 0x69, 0x33, - 0x2B, 0xDB, 0x7F, 0x85, 0xF1, 0x86, 0x97, 0x4F, 0xC9, 0x27, 0xBE, 0xF7, 0x02, 0x73, 0xFC, 0xDF, - 0x2F, 0xF1, 0x7E, 0xF1, 0xE5, 0xF1, 0xDA, 0xE0, 0xB8, 0xDD, 0x98, 0xE3, 0x5F, 0xF4, 0xC3, 0x23, - 0xFA, 0xAF, 0xFB, 0xFF, 0x39, 0xFB, 0x9F, 0x0F, 0xFE, 0xAB, 0x00, 0x00, 0x00, 0xC8, 0x93, 0x39, - 0x7C, 0xE3, 0xBA, 0x96, 0xAF, 0xE0, 0x95, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, - 0xAB, 0xA9, 0xB8, 0x48, 0xBA, 0xE1, 0xEB, 0x8D, 0xFB, 0x16, 0xAA, 0xE0, 0x6F, 0x4B, 0x2A, 0x36, - 0x5C, 0x06, 0xEC, 0xB2, 0xDF, 0x7E, 0xC8, 0x90, 0xC8, 0xB2, 0x20, 0x3E, 0xDC, 0xB8, 0x05, 0xA9, - 0xAA, 0x89, 0x0F, 0x17, 0x1D, 0x2F, 0xD9, 0xFF, 0xAD, 0x5E, 0xB0, 0xB1, 0xF8, 0xFC, 0x92, 0xC9, - 0xBD, 0x70, 0xB5, 0xFD, 0xD7, 0x57, 0x55, 0xFE, 0x7D, 0x22, 0x04, 0x00, 0x00, 0xA0, 0x38, 0xFE, - 0x2B, 0x78, 0x55, 0x74, 0x84, 0x93, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, - 0xA9, 0xA8, 0x48, 0xBC, 0x59, 0x95, 0x07, 0xC7, 0xDD, 0xB7, 0x50, 0x65, 0xB8, 0x05, 0xF1, 0x86, - 0x57, 0x64, 0xBF, 0xFD, 0x90, 0x21, 0x91, 0x65, 0x91, 0xF8, 0x08, 0xC7, 0x2D, 0x48, 0x6D, 0x7C, - 0x1C, 0xB9, 0xB1, 0x36, 0x3A, 0xD4, 0x5C, 0x98, 0x1C, 0xFD, 0x42, 0x25, 0x42, 0x4E, 0x7C, 0x85, - 0x1B, 0x24, 0x00, 0x00, 0x80, 0xBC, 0x79, 0x07, 0x97, 0x35, 0x7E, 0x03, 0x96, 0x8C, 0x8B, 0x56, - 0x53, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0x85, 0x1F, 0x16, 0x68, 0xE7, - 0x6E, 0x41, 0x46, 0x06, 0x86, 0x82, 0xBF, 0x35, 0x89, 0xB4, 0x75, 0xFB, 0x21, 0x43, 0x22, 0xCB, - 0x44, 0x7C, 0xB8, 0x6D, 0xFD, 0xD0, 0xB4, 0xBE, 0x05, 0xA9, 0x89, 0x0F, 0x77, 0xEB, 0xA1, 0x82, - 0xA3, 0xD1, 0x5C, 0xAC, 0xF8, 0x37, 0x26, 0x5F, 0x29, 0xE4, 0xD3, 0xF2, 0x01, 0x00, 0x00, 0xA6, - 0x2D, 0x6F, 0xFF, 0x35, 0x63, 0xC4, 0x47, 0xB0, 0xED, 0x1F, 0xB7, 0x87, 0x75, 0xFF, 0x16, 0x24, - 0xF1, 0x81, 0xDD, 0x1B, 0x59, 0x31, 0xE6, 0x3F, 0xC4, 0xAF, 0x02, 0xA3, 0xD9, 0x64, 0x48, 0x64, - 0x99, 0x08, 0x8F, 0xE8, 0xF6, 0x2C, 0xB2, 0x51, 0xB5, 0x7C, 0x2C, 0xF8, 0xE5, 0x4E, 0x1B, 0x6D, - 0xC5, 0x47, 0xB8, 0x63, 0x7F, 0xE2, 0x3F, 0x94, 0x1E, 0xFC, 0x94, 0x00, 0x00, 0x00, 0xC8, 0x83, - 0xD9, 0xF7, 0x19, 0xE2, 0xC3, 0xED, 0x99, 0x60, 0xFB, 0xAF, 0x32, 0xDE, 0xF0, 0x40, 0xA2, 0x6F, - 0xBD, 0xB1, 0xA1, 0xB2, 0xCE, 0xBF, 0xFD, 0x70, 0x7F, 0xCF, 0x54, 0x64, 0x34, 0x9A, 0x0C, 0x89, - 0x2C, 0x13, 0xC1, 0x11, 0x9F, 0xFB, 0x7B, 0xEB, 0xDF, 0x82, 0x2C, 0xCF, 0xF5, 0x13, 0xDF, 0xBB, - 0x59, 0x2E, 0xF1, 0xE1, 0xF6, 0xC2, 0xD5, 0xEE, 0xDB, 0xB0, 0x4E, 0x06, 0x3F, 0x2D, 0x00, 0x00, - 0x00, 0xDA, 0x65, 0x0E, 0xDF, 0xB8, 0xC4, 0x0C, 0x2D, 0x9C, 0xBE, 0xF1, 0x11, 0x46, 0x47, 0xF0, - 0x7F, 0x7B, 0x6E, 0xDB, 0x3F, 0xEE, 0xBE, 0x0D, 0x2B, 0xD1, 0x2D, 0x88, 0x37, 0xBC, 0xE2, 0x64, - 0xEA, 0xDB, 0x0F, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, 0xD1, 0xDC, 0x2D, 0xC8, 0xF0, 0xB2, 0x69, - 0x71, 0x90, 0xCE, 0x2D, 0x3E, 0xC2, 0x1D, 0xBB, 0xC5, 0x04, 0x3F, 0x35, 0x00, 0x00, 0x00, 0xDA, - 0x65, 0x0E, 0xAD, 0x5A, 0x5B, 0xF3, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, - 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0xAB, 0x8F, 0x0F, 0x3F, 0x3C, 0xC2, 0x6D, 0xB2, 0xDB, 0x7B, - 0x85, 0x39, 0x3F, 0x3C, 0xD0, 0xF4, 0xC0, 0x9E, 0xE9, 0xF6, 0x43, 0x86, 0x44, 0x96, 0x89, 0xC8, - 0x68, 0x36, 0xF7, 0xF7, 0x79, 0x1A, 0xDC, 0x82, 0xE4, 0x1E, 0x1F, 0xCF, 0x7F, 0xC6, 0x98, 0x23, - 0x37, 0x18, 0xF3, 0xF2, 0x9D, 0x7C, 0x9E, 0x0A, 0x00, 0x00, 0x40, 0x1E, 0xFC, 0x37, 0x60, 0x6D, - 0xFB, 0xC8, 0xF4, 0x8D, 0x8F, 0x68, 0x78, 0xB8, 0xB9, 0xF8, 0x70, 0xDB, 0xFC, 0x21, 0xF7, 0x6D, - 0x58, 0xEE, 0x16, 0xA4, 0xE1, 0x81, 0xDD, 0xBF, 0xFD, 0xD8, 0xB3, 0xD0, 0xFE, 0x7C, 0x22, 0x34, - 0xD4, 0x64, 0x48, 0x64, 0x99, 0x08, 0x8C, 0x24, 0x1B, 0x9A, 0x5F, 0xEA, 0x5B, 0x90, 0x42, 0xE2, - 0xC3, 0x6D, 0xEC, 0x3A, 0x17, 0x20, 0xD3, 0xE6, 0xDB, 0xD7, 0x00, 0x00, 0x00, 0x0A, 0xE5, 0x1D, - 0x5A, 0x3D, 0x38, 0xED, 0xE2, 0x23, 0x12, 0x20, 0xD5, 0xE8, 0x88, 0xC4, 0xC7, 0xF9, 0x70, 0x43, - 0x97, 0x9B, 0x73, 0x07, 0xF5, 0x2D, 0xC8, 0xF9, 0x91, 0xFE, 0xF5, 0x66, 0x78, 0x99, 0xFD, 0xB9, - 0xDC, 0xAF, 0x49, 0xC4, 0x46, 0x7C, 0x32, 0x24, 0xB2, 0x4C, 0x84, 0x45, 0xD2, 0xB9, 0xDF, 0xF7, - 0xF0, 0xF5, 0xEE, 0x16, 0xA4, 0xAD, 0x4F, 0x7C, 0xEF, 0x46, 0x85, 0xC5, 0x87, 0xDB, 0x0B, 0x57, - 0x19, 0x73, 0xE2, 0xCE, 0xD2, 0xFD, 0x3D, 0x03, 0x00, 0x00, 0xE8, 0x08, 0x6F, 0xDF, 0x55, 0xA3, - 0x3A, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xC5, 0xC2, 0x22, 0xC9, 0x64, 0x54, 0x24, 0x5D, 0x24, - 0x3E, 0x82, 0xD5, 0xDC, 0x78, 0x84, 0xD1, 0xF1, 0x74, 0x64, 0x9B, 0x3F, 0x64, 0xCE, 0x1F, 0x5C, - 0x6E, 0xCE, 0x0D, 0xF7, 0xDF, 0x15, 0xFC, 0xED, 0xF2, 0x99, 0xA1, 0xFE, 0x59, 0xE7, 0x47, 0x56, - 0x8C, 0x9B, 0xDD, 0x7D, 0xF6, 0x60, 0x2F, 0x62, 0x23, 0x3E, 0x19, 0x12, 0x59, 0x26, 0xA2, 0x22, - 0xED, 0x76, 0xCF, 0x35, 0xDE, 0xC8, 0xB2, 0x71, 0xF7, 0x7B, 0x08, 0x7E, 0x3B, 0x3D, 0xAF, 0xD0, - 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x9F, 0x07, 0x02, 0x00, 0x00, 0x90, 0x07, 0xB3, 0xEF, 0x0A, - 0x7B, 0xB8, 0x57, 0x81, 0xD1, 0x6C, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, 0x48, - 0xBA, 0x48, 0x78, 0x44, 0x6F, 0x3E, 0xE2, 0xB7, 0x1E, 0x7E, 0x78, 0xBC, 0xCF, 0x9C, 0x7B, 0x6A, - 0x72, 0xE7, 0x77, 0xCE, 0x77, 0xB7, 0x20, 0x35, 0x07, 0xF6, 0xF3, 0x07, 0xFB, 0xD7, 0x7B, 0xC3, - 0xCB, 0x8C, 0xE7, 0x6E, 0x3F, 0x5A, 0x05, 0x88, 0x0C, 0x89, 0x2C, 0x13, 0x31, 0x91, 0x76, 0x9B, - 0x66, 0xD8, 0x1F, 0xED, 0xAF, 0xA9, 0x44, 0xB7, 0x20, 0x53, 0x11, 0x1F, 0x6E, 0xDE, 0xD1, 0x5B, - 0xA6, 0xDD, 0x6B, 0x8C, 0x01, 0x00, 0x00, 0x72, 0x67, 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x8B, 0xED, - 0x01, 0x5F, 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x58, 0x24, 0x99, 0x8C, 0x8A, 0xA4, - 0x8B, 0xC4, 0x47, 0x83, 0x00, 0x09, 0xE3, 0xE3, 0x5C, 0x24, 0x3E, 0xCE, 0x6E, 0x0C, 0xF6, 0xD4, - 0xFB, 0xCD, 0xF9, 0x03, 0xCB, 0x8D, 0x8B, 0x0E, 0xFF, 0xEF, 0x99, 0x0D, 0x91, 0x73, 0xC3, 0x2B, - 0xC6, 0xBD, 0x5D, 0x7D, 0xF6, 0xBF, 0x7F, 0x91, 0x3F, 0xFF, 0x50, 0xAF, 0x42, 0x44, 0x86, 0x44, - 0x96, 0x89, 0x98, 0x48, 0x3B, 0x17, 0x1F, 0xE1, 0x4A, 0x72, 0x0B, 0x32, 0x55, 0xF1, 0xE1, 0xEF, - 0xE8, 0xE7, 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xBB, 0xCC, 0xC8, 0xEA, 0x01, 0xB3, 0xEB, 0x52, - 0x7B, 0xC8, 0x57, 0xA1, 0xA1, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, - 0x4B, 0x1F, 0x1F, 0xD5, 0xF0, 0x78, 0xD2, 0x6D, 0x96, 0xBF, 0x73, 0x3B, 0xE6, 0x9B, 0xB3, 0x07, - 0x2A, 0xB7, 0x20, 0x2E, 0x44, 0xCE, 0x1F, 0x5C, 0x66, 0xFF, 0xFB, 0x7F, 0xDC, 0x3C, 0x40, 0x64, - 0x48, 0x64, 0x99, 0x88, 0x89, 0xB4, 0x8B, 0xC6, 0x87, 0x9B, 0xFB, 0xB5, 0x1E, 0xBC, 0xB6, 0xA7, - 0x6F, 0x41, 0xA6, 0x34, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0xEC, 0x5F, 0x33, 0xF8, 0xCB, 0x03, 0x00, - 0x00, 0x20, 0x0B, 0xEF, 0xD0, 0xAA, 0xBB, 0xCC, 0xF6, 0x4F, 0xD8, 0x83, 0xBE, 0x8A, 0x8D, 0xF8, - 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, 0x74, 0xB1, 0xF8, 0x08, 0x02, 0x24, - 0x49, 0x7C, 0xBC, 0x6B, 0xE3, 0xE3, 0xDD, 0xC1, 0x59, 0x93, 0xDB, 0xF8, 0x7E, 0x73, 0x6E, 0xEF, - 0xF5, 0xE6, 0xDC, 0x81, 0x81, 0x51, 0xF7, 0xED, 0x58, 0xE7, 0xB7, 0x5F, 0x62, 0xFF, 0xBB, 0x36, - 0x3E, 0xDC, 0x54, 0x84, 0xC8, 0x90, 0xC8, 0x32, 0x11, 0x13, 0x69, 0x17, 0x8F, 0x8F, 0x70, 0x3B, - 0x3E, 0xE9, 0x02, 0xC4, 0xBD, 0xE5, 0x6B, 0x76, 0xF0, 0x8F, 0x43, 0xCF, 0x98, 0xF2, 0xF8, 0x70, - 0x1B, 0x5B, 0x66, 0xCC, 0x89, 0xAF, 0xAC, 0x0D, 0x7E, 0x09, 0x00, 0x00, 0x00, 0xC8, 0xC2, 0x3B, - 0xBC, 0x7A, 0x83, 0x8E, 0x8D, 0xF8, 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, - 0x74, 0x3A, 0x3E, 0xEA, 0x02, 0xC4, 0x86, 0x47, 0xF8, 0xCC, 0x47, 0x3C, 0x3E, 0x26, 0xDC, 0x9E, - 0xA8, 0xCC, 0xFD, 0xFF, 0x67, 0xB7, 0x7C, 0xDA, 0x4C, 0x3C, 0xD3, 0x6F, 0xCE, 0x0D, 0xD9, 0x10, - 0x79, 0xCA, 0xFD, 0x77, 0x2F, 0xD2, 0x11, 0xB2, 0x49, 0x85, 0x44, 0x96, 0x89, 0x98, 0x48, 0x3B, - 0x15, 0x1E, 0xD1, 0x1D, 0xBC, 0xD6, 0xBD, 0x96, 0x77, 0x30, 0xF8, 0xC7, 0xA1, 0x27, 0x74, 0x24, - 0x3E, 0xDC, 0x5E, 0xBC, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0xB4, 0xCB, 0x3B, 0x78, 0x4D, 0x82, - 0x37, 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0xE1, 0x11, - 0x2E, 0x1E, 0x1F, 0xE1, 0xED, 0x47, 0xE4, 0x99, 0x0F, 0x17, 0x1F, 0xD1, 0xF0, 0x98, 0x78, 0x3C, - 0x98, 0xFB, 0xBF, 0x1F, 0x9B, 0x65, 0x4E, 0x3F, 0x72, 0x9D, 0x39, 0xF3, 0xF0, 0x25, 0x7E, 0x80, - 0x9C, 0xF3, 0x1F, 0x58, 0x8F, 0x05, 0x88, 0x8B, 0x8F, 0x5C, 0x02, 0x44, 0xC4, 0x44, 0xDA, 0xA9, - 0xE0, 0x88, 0xAF, 0xC7, 0x6E, 0x41, 0x3A, 0x16, 0x1F, 0xC1, 0xBC, 0x5F, 0xDC, 0x39, 0x14, 0xFC, - 0x52, 0x00, 0x00, 0x00, 0x90, 0x85, 0xB7, 0xFF, 0xEA, 0x93, 0x3A, 0x3A, 0xC2, 0xA9, 0xA0, 0x48, - 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x78, 0xB8, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, - 0xDE, 0x7E, 0xD4, 0x7E, 0xEB, 0x55, 0x34, 0x3E, 0xCE, 0xD8, 0xE8, 0x38, 0xF3, 0xF8, 0xFB, 0x82, - 0x55, 0x22, 0xE4, 0xF4, 0xFD, 0x17, 0x99, 0x53, 0xF7, 0x5E, 0x6C, 0x4E, 0xDD, 0x7D, 0x91, 0x39, - 0x6B, 0xFF, 0x33, 0x75, 0xB7, 0x20, 0x36, 0x3C, 0xDC, 0xDA, 0x0F, 0x10, 0x11, 0x13, 0x69, 0xA7, - 0x62, 0xA3, 0xD1, 0x7A, 0xE4, 0x16, 0xA4, 0xD3, 0xF1, 0xE1, 0xE6, 0x1D, 0xFB, 0x53, 0xDE, 0x84, - 0x05, 0x00, 0x00, 0xD0, 0x8E, 0xE6, 0x6F, 0xC0, 0x52, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, - 0x46, 0x45, 0xD2, 0xC5, 0xA2, 0x23, 0xBA, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDC, 0x7E, 0xD4, 0x7C, - 0xDB, 0x55, 0x18, 0x1F, 0x8F, 0xD9, 0xF0, 0xB0, 0x3B, 0x1D, 0xFC, 0x78, 0xE6, 0x91, 0xF7, 0xF9, - 0xE1, 0x11, 0xEE, 0xF4, 0x7D, 0x36, 0x42, 0x36, 0x4E, 0x46, 0x88, 0xF7, 0x74, 0x25, 0x3E, 0xAA, - 0x01, 0x92, 0x39, 0x42, 0x44, 0x4C, 0xA4, 0x9D, 0x8A, 0x8C, 0x66, 0xDB, 0x7E, 0x71, 0xD7, 0xDF, - 0x82, 0x74, 0x43, 0x7C, 0xF8, 0x3B, 0x76, 0x0B, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xC8, 0xCA, 0x7F, - 0x03, 0xD6, 0xEE, 0x79, 0xF6, 0xE0, 0x5F, 0xFE, 0xF8, 0x70, 0x3F, 0x36, 0xBF, 0xFD, 0xB0, 0xF1, - 0xE1, 0x02, 0x24, 0x88, 0x0F, 0x17, 0x1E, 0xA7, 0x1F, 0x9D, 0xDC, 0xA9, 0x7B, 0x67, 0xD5, 0x04, - 0x88, 0xDB, 0xBB, 0x3F, 0x0E, 0x02, 0xE4, 0xA9, 0x0B, 0xED, 0xCF, 0x67, 0xE3, 0x23, 0x1E, 0x21, - 0x32, 0x30, 0x9A, 0x4D, 0xC4, 0x44, 0xDA, 0xA9, 0xC0, 0x48, 0xB2, 0x7D, 0x57, 0x18, 0x6F, 0x78, - 0x79, 0x57, 0x7E, 0xD0, 0x5E, 0xD7, 0xC4, 0x87, 0x9B, 0x7B, 0x13, 0xD6, 0xCB, 0x77, 0xF6, 0xDC, - 0x43, 0xFB, 0x00, 0x00, 0x00, 0x5D, 0xC1, 0x1C, 0x5A, 0xB5, 0xD6, 0xEC, 0x9C, 0x6D, 0x0F, 0xFF, - 0x25, 0x8E, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDD, 0x7E, 0xB8, 0xD7, 0xEC, 0x46, 0x6F, - 0x3F, 0xE2, 0xF1, 0x71, 0xFA, 0x81, 0xDA, 0xDB, 0x8F, 0x70, 0xEE, 0x16, 0xE4, 0xDC, 0x93, 0x76, - 0x1B, 0x2F, 0xF2, 0x23, 0xA4, 0x26, 0x40, 0x5C, 0x50, 0xA4, 0x8A, 0x10, 0x11, 0x13, 0x69, 0xA7, - 0xC2, 0x22, 0xE9, 0xB6, 0x7D, 0x24, 0xB8, 0x05, 0x59, 0x3A, 0x10, 0xFC, 0xE3, 0xD1, 0x15, 0xBA, - 0x2A, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0xB3, 0xAB, 0xFE, 0x1E, 0x01, 0x00, 0x00, 0xF4, - 0x0C, 0x73, 0x78, 0xF5, 0x7A, 0xB3, 0xF5, 0xC3, 0x36, 0x00, 0x4A, 0x1C, 0x1F, 0xC1, 0xD4, 0xED, - 0xC7, 0x59, 0x3F, 0x40, 0x6C, 0x7C, 0xB8, 0xDB, 0x8F, 0xC8, 0xB7, 0x5E, 0x55, 0xC3, 0xE3, 0xC7, - 0x95, 0xA9, 0xDB, 0x0F, 0x7F, 0x5F, 0xBF, 0xD0, 0xBC, 0xFB, 0x48, 0x25, 0x40, 0xCE, 0xB5, 0x75, - 0x0B, 0x22, 0x62, 0x22, 0xED, 0x54, 0x54, 0xA4, 0x9D, 0xBB, 0x05, 0x39, 0xB8, 0xAC, 0x6B, 0x6E, - 0x41, 0xBA, 0x2E, 0x3E, 0xDC, 0x78, 0x13, 0x16, 0x00, 0x00, 0x40, 0x76, 0xDE, 0xC8, 0x8A, 0xA1, - 0xD2, 0xC7, 0x47, 0xC3, 0xDB, 0x8F, 0x59, 0xD5, 0xF8, 0xA8, 0xDE, 0x7E, 0x84, 0xF1, 0x11, 0x84, - 0x87, 0x1F, 0x1F, 0x3F, 0xD2, 0xB7, 0x1F, 0x2E, 0x3E, 0xDC, 0x4E, 0x7F, 0xEB, 0x42, 0x73, 0xB6, - 0xAD, 0x5B, 0x10, 0x11, 0x13, 0x69, 0xA7, 0x62, 0x22, 0xCB, 0xB6, 0xD9, 0x18, 0x1D, 0x5E, 0xD6, - 0x15, 0xB7, 0x20, 0x5D, 0x19, 0x1F, 0xC1, 0xBC, 0x13, 0x5F, 0xEE, 0xA9, 0xD7, 0x16, 0x03, 0x00, - 0x00, 0x74, 0x0D, 0x6F, 0xDF, 0x55, 0x63, 0x65, 0xBF, 0xF9, 0x70, 0xAB, 0xBF, 0xFD, 0x88, 0x04, - 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x70, 0x44, 0x02, 0xE4, 0x94, 0x7B, 0xE8, 0xDC, 0xED, 0xDB, 0xE2, - 0xF6, 0x23, 0x88, 0x8F, 0x70, 0x13, 0x0F, 0x64, 0xBD, 0x05, 0x11, 0x31, 0x91, 0x76, 0x2A, 0x24, - 0x32, 0xED, 0x82, 0xCA, 0xF6, 0x5C, 0xD6, 0xF1, 0x5B, 0x90, 0x6E, 0x8E, 0x0F, 0x37, 0xEF, 0xF8, - 0x9F, 0x75, 0xE5, 0xB3, 0x32, 0x00, 0x00, 0x00, 0x5D, 0xCF, 0xEC, 0xBD, 0xDC, 0x86, 0x40, 0x89, - 0xE3, 0x23, 0xC1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xC1, 0xB7, 0x5E, 0x9D, 0x0A, 0x03, 0xE4, 0x07, - 0xAD, 0xE3, 0xC3, 0xED, 0xF4, 0x3D, 0x2D, 0x6E, 0x41, 0xE4, 0x44, 0x4C, 0xA4, 0x9D, 0x0C, 0x89, - 0x2C, 0x0B, 0xE2, 0xC3, 0xCD, 0xFD, 0xFD, 0xF5, 0x6F, 0x41, 0x96, 0xAF, 0x0B, 0xFE, 0x31, 0x99, - 0x52, 0xDD, 0x1E, 0x1F, 0xFE, 0x8E, 0xFF, 0x29, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x48, 0xCB, 0xBD, - 0x72, 0xD5, 0xEC, 0x59, 0x64, 0x63, 0x40, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0xBA, 0xA0, - 0x48, 0x33, 0x11, 0x1A, 0x8D, 0x16, 0x0F, 0x90, 0xE0, 0xF6, 0xE3, 0x6C, 0x92, 0xDB, 0x8F, 0x87, - 0xED, 0xEE, 0x69, 0x1D, 0x1F, 0xE1, 0xC2, 0x5B, 0x90, 0x64, 0x6F, 0xC4, 0x12, 0x31, 0x91, 0x76, - 0x32, 0x24, 0xB2, 0x2C, 0x12, 0x1F, 0xE1, 0x86, 0x16, 0x1A, 0xEF, 0xE0, 0xD2, 0x93, 0xC1, 0x3F, - 0x2A, 0x53, 0xA6, 0x27, 0xE2, 0xC3, 0xED, 0xC8, 0x6A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0x90, 0x96, - 0xFF, 0x0A, 0xDE, 0x9D, 0x9F, 0xB2, 0x41, 0xA0, 0xA2, 0x22, 0xE9, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, - 0x8A, 0xA4, 0x13, 0x91, 0xA1, 0x66, 0x83, 0xA3, 0x1A, 0x1F, 0x36, 0x3C, 0x12, 0xDF, 0x7E, 0x44, - 0xBF, 0xF5, 0xEA, 0xFB, 0xB1, 0xDB, 0x0F, 0x11, 0x1D, 0xD1, 0x25, 0xBF, 0x05, 0x11, 0x31, 0x91, - 0x76, 0x32, 0x24, 0xB2, 0x4C, 0xC4, 0x87, 0x9B, 0xFB, 0x7B, 0x3D, 0xC5, 0xB7, 0x20, 0x3D, 0x13, - 0x1F, 0x3F, 0xBF, 0xDC, 0x98, 0xB1, 0x15, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x20, 0x2D, 0x73, 0x78, - 0xE5, 0x7A, 0xB3, 0xED, 0xA3, 0x36, 0x0A, 0x54, 0x58, 0x24, 0x99, 0x88, 0x8B, 0x56, 0xAB, 0x0B, - 0x8A, 0x34, 0x8B, 0x45, 0x46, 0xB3, 0x45, 0x03, 0xC4, 0xAE, 0x12, 0x1F, 0x36, 0x0E, 0xC2, 0x00, - 0x69, 0xF4, 0xDA, 0xDD, 0x46, 0xB7, 0x1F, 0x22, 0x38, 0xD4, 0x26, 0x7E, 0x50, 0x09, 0x90, 0x9A, - 0x67, 0x41, 0x82, 0x10, 0xA9, 0xDC, 0x82, 0x88, 0x98, 0x48, 0x3B, 0x19, 0x12, 0x59, 0x26, 0xC2, - 0x23, 0xBA, 0x29, 0xBC, 0x05, 0xE9, 0xA9, 0xF8, 0x70, 0x7B, 0xF1, 0x3A, 0x63, 0x7F, 0xBD, 0x77, - 0x05, 0xBF, 0x7C, 0x00, 0x00, 0x00, 0x24, 0xE1, 0x1D, 0x5E, 0xB9, 0x41, 0x87, 0x45, 0x92, 0x89, - 0xB8, 0x68, 0x35, 0x19, 0x15, 0x49, 0x17, 0x0B, 0x8C, 0x66, 0x8B, 0xC5, 0x47, 0xE5, 0xF6, 0x23, - 0x12, 0x1F, 0xCD, 0x6E, 0x3F, 0xC2, 0x00, 0x89, 0xDE, 0x7E, 0x88, 0xD0, 0x68, 0xB8, 0x6F, 0x5C, - 0x68, 0xCE, 0x3E, 0xD1, 0xE0, 0x16, 0x64, 0x93, 0x88, 0x89, 0xB4, 0x93, 0x21, 0x91, 0x65, 0x22, - 0x38, 0xE2, 0x73, 0x7F, 0xDF, 0x0F, 0x5E, 0x57, 0xF8, 0x2D, 0x48, 0xCF, 0xC5, 0x47, 0x30, 0xEF, - 0xC4, 0x9D, 0x1B, 0x82, 0xDF, 0x02, 0x00, 0x00, 0x00, 0x92, 0xF0, 0x0E, 0x5E, 0x3D, 0xAA, 0xE3, - 0xA2, 0xD5, 0x44, 0x5C, 0xB4, 0x5A, 0x5D, 0x50, 0xA4, 0x59, 0x2C, 0x30, 0x9A, 0xAD, 0x41, 0x7C, - 0xF8, 0xB7, 0x1F, 0x2E, 0x3E, 0xD4, 0xED, 0x47, 0x10, 0x1F, 0xD5, 0xDB, 0x8F, 0x87, 0xEC, 0xBE, - 0x91, 0x21, 0x3E, 0x82, 0x85, 0xB7, 0x20, 0xE7, 0x9F, 0x72, 0x7F, 0xDD, 0x20, 0x42, 0x9E, 0x9E, - 0x69, 0x7F, 0x2D, 0x2E, 0x20, 0x82, 0xA9, 0xB8, 0x68, 0x35, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, - 0xD1, 0x76, 0xCD, 0x31, 0xDE, 0xF0, 0xD2, 0x71, 0x33, 0xD4, 0x3F, 0x2B, 0xF8, 0xC7, 0x26, 0x57, - 0xBD, 0x1A, 0x1F, 0x7E, 0x80, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x74, 0xBC, 0xFD, 0x9F, 0x1D, - 0xD7, 0x81, 0xD1, 0x6C, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xC5, 0x02, 0xA3, 0xD5, 0xEA, - 0x02, 0x24, 0x88, 0x8F, 0xF8, 0xED, 0x47, 0x10, 0x1F, 0xF2, 0xD9, 0x8F, 0xFB, 0x83, 0xDB, 0x0F, - 0x11, 0x17, 0x89, 0x16, 0xBB, 0x05, 0x39, 0x1F, 0xC4, 0x47, 0x5B, 0x01, 0x22, 0x43, 0x22, 0xCB, - 0x44, 0x64, 0x34, 0x9B, 0x7B, 0x66, 0xC5, 0xDD, 0x82, 0x1C, 0x5C, 0x9A, 0xFB, 0x07, 0xEF, 0xF5, - 0x72, 0x7C, 0xB8, 0x79, 0xC7, 0xFE, 0x64, 0x3C, 0xF8, 0xAD, 0x00, 0x00, 0x00, 0xA0, 0x15, 0x33, - 0xDA, 0x3F, 0x2B, 0xFD, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x8B, 0xC5, 0x45, - 0x92, 0xC5, 0xE3, 0x23, 0x7A, 0xFB, 0x11, 0xC4, 0x47, 0xCD, 0x9B, 0xAF, 0x1A, 0xDD, 0x7E, 0xA8, - 0xB0, 0x48, 0xB1, 0x33, 0xDF, 0xBB, 0x70, 0xF2, 0x59, 0x90, 0xA7, 0x6C, 0x7C, 0xB4, 0x13, 0x21, - 0x32, 0x24, 0xB2, 0x4C, 0x04, 0x46, 0x92, 0x15, 0x70, 0x0B, 0xD2, 0xEB, 0xF1, 0xE1, 0xEF, 0xC8, - 0x4D, 0xC6, 0x8C, 0xFD, 0x87, 0x42, 0x6E, 0x86, 0x00, 0x00, 0x00, 0x4A, 0xC7, 0x7F, 0x03, 0xD6, - 0xAE, 0x4F, 0xDB, 0x48, 0x50, 0xA1, 0xA1, 0x26, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, - 0x8B, 0x56, 0xB3, 0xD1, 0x31, 0x19, 0x20, 0x17, 0x55, 0x02, 0x24, 0xB8, 0xFD, 0xA8, 0xBE, 0x76, - 0xB7, 0x7A, 0xFB, 0xF1, 0xBE, 0xC6, 0xB7, 0x1F, 0x22, 0x28, 0xB2, 0xEC, 0xEC, 0x63, 0x2E, 0x42, - 0x2A, 0x01, 0xE2, 0xDF, 0x82, 0x64, 0x89, 0x10, 0x19, 0x12, 0x59, 0x26, 0xC2, 0x22, 0xE9, 0x72, - 0xBE, 0x05, 0x29, 0x45, 0x7C, 0xB8, 0xBD, 0x34, 0xC0, 0x9B, 0xB0, 0x00, 0x00, 0x00, 0x92, 0x32, - 0x87, 0x57, 0xAD, 0x4B, 0xFE, 0x0A, 0x5E, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x61, - 0x91, 0x74, 0xF1, 0xF8, 0xC8, 0x74, 0xFB, 0xA1, 0x63, 0x22, 0xFD, 0x66, 0x9A, 0x33, 0xF7, 0xCF, - 0x34, 0x67, 0x9F, 0x9C, 0x8C, 0x90, 0xD4, 0x01, 0x22, 0x43, 0x22, 0xCB, 0x44, 0x54, 0xA4, 0xDD, - 0xCE, 0xD9, 0xC6, 0x1B, 0xBE, 0xBE, 0xED, 0x5B, 0x90, 0xD2, 0xC4, 0x87, 0x1B, 0x6F, 0xC2, 0x02, - 0x00, 0x00, 0x48, 0xCE, 0xB8, 0x57, 0xF0, 0xCA, 0xD8, 0x88, 0x4F, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, - 0x48, 0x3A, 0x11, 0x16, 0x49, 0xE6, 0x6E, 0x3F, 0xEC, 0x8F, 0x7E, 0x7C, 0xC4, 0x6E, 0x3F, 0xEA, - 0x9F, 0xFD, 0x68, 0x70, 0xFB, 0x71, 0x5F, 0xFB, 0xDF, 0x7A, 0x55, 0xD9, 0xCC, 0xEA, 0xDE, 0x0D, - 0x6E, 0x41, 0xCE, 0x35, 0xBA, 0x05, 0x51, 0xE1, 0xE1, 0x26, 0x43, 0x22, 0xCB, 0x44, 0x4C, 0x64, - 0xDA, 0x7B, 0x8D, 0x39, 0x70, 0xB5, 0x7B, 0x2D, 0xEF, 0x60, 0xF0, 0x8F, 0x50, 0x6A, 0xA5, 0x8A, - 0x0F, 0xB7, 0xE7, 0xAF, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x49, 0x79, 0x07, 0xAF, 0x4B, 0xF0, - 0x06, 0x2C, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x51, 0x91, 0x66, 0xE1, 0xCD, 0x47, - 0x83, 0xDB, 0x8F, 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0x78, 0x30, 0xAF, 0x6F, 0xBD, 0x9A, 0x8C, 0x0F, - 0xB7, 0x4C, 0xB7, 0x20, 0x32, 0x24, 0xB2, 0x4C, 0x85, 0x44, 0x96, 0xD9, 0xF8, 0x70, 0xDB, 0xFE, - 0x71, 0xF7, 0x6D, 0x58, 0xC6, 0x8C, 0x5C, 0x9B, 0xFA, 0x13, 0xC0, 0x4B, 0x17, 0x1F, 0xC1, 0xBC, - 0x13, 0x77, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x24, 0xBC, 0x7D, 0x57, 0x8E, 0xE9, 0xE8, 0x08, - 0x27, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, 0x8A, 0xA4, 0xF3, 0x6F, 0x3F, 0x82, 0xF8, - 0x88, 0xDD, 0x7E, 0xA8, 0x67, 0x3F, 0xEA, 0x3F, 0x74, 0xD0, 0xC6, 0x47, 0x2E, 0xB7, 0x1F, 0xB5, - 0xF1, 0x11, 0xEE, 0xDD, 0x47, 0x5A, 0x3C, 0x0B, 0xD2, 0x0B, 0xF1, 0xF1, 0x74, 0xB0, 0xFD, 0xE9, - 0x6F, 0x41, 0xCA, 0x1A, 0x1F, 0x6E, 0xDE, 0xB1, 0x5B, 0xA6, 0xE4, 0x83, 0x1A, 0x01, 0x00, 0x00, - 0x7A, 0x9E, 0xD9, 0x7B, 0x99, 0x8D, 0x06, 0x15, 0x1E, 0x6E, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, - 0xD2, 0xC5, 0x82, 0x22, 0xED, 0xC2, 0xF0, 0x68, 0x70, 0xFB, 0x51, 0xF7, 0xEC, 0x47, 0x10, 0x20, - 0x95, 0xDB, 0x0F, 0x1B, 0x1F, 0xB9, 0xDC, 0x7E, 0xE8, 0xF8, 0x70, 0x3B, 0x7D, 0xEF, 0x4C, 0x73, - 0xAE, 0xD1, 0x2D, 0x88, 0x1F, 0x1D, 0x3D, 0x12, 0x1F, 0xF6, 0xFF, 0xF6, 0xB6, 0x7F, 0xDC, 0x05, - 0x48, 0xE2, 0x5B, 0x90, 0x32, 0xC7, 0x87, 0xBF, 0x23, 0x37, 0x99, 0xE0, 0xB7, 0x0A, 0x00, 0x00, - 0x80, 0x46, 0xCC, 0xE1, 0xFE, 0x25, 0x66, 0x68, 0xBE, 0x0D, 0x87, 0x72, 0xC4, 0x47, 0xCB, 0xDB, - 0x0F, 0x11, 0x1F, 0x95, 0xDB, 0x0F, 0x1B, 0x1E, 0xB9, 0xDC, 0x7E, 0xE8, 0xF0, 0x88, 0x2E, 0xBC, - 0x05, 0x69, 0xF8, 0x2C, 0x88, 0x0C, 0x89, 0x2C, 0x53, 0x21, 0x91, 0x65, 0x22, 0x3E, 0xEC, 0x8F, - 0xFE, 0xF6, 0x7C, 0xC6, 0x9C, 0x3F, 0x70, 0xFD, 0x50, 0xF0, 0x8F, 0x53, 0x43, 0xA5, 0x8F, 0x0F, - 0x37, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x9A, 0xFF, 0x0A, 0x5E, 0xF9, 0x06, 0x2C, 0x11, 0x17, - 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x50, 0xA4, 0x9A, 0x8D, 0x8F, 0x30, 0x3C, 0xC2, 0xF8, 0x48, - 0x7A, 0xFB, 0xF1, 0x63, 0x1B, 0x1E, 0x2E, 0x40, 0x7E, 0xD8, 0xEE, 0xED, 0x87, 0x0E, 0x8E, 0xF8, - 0xDC, 0x2D, 0x48, 0xE3, 0x67, 0x41, 0x54, 0x48, 0x64, 0x99, 0x0A, 0x89, 0x2C, 0x6B, 0x12, 0x1F, - 0x4F, 0xBD, 0xD7, 0x9C, 0xDF, 0xF2, 0x21, 0x73, 0x7E, 0xFF, 0x32, 0x73, 0x76, 0xFF, 0xD2, 0x86, - 0x07, 0xEF, 0x69, 0x11, 0x1F, 0x6E, 0x2F, 0x5E, 0xE7, 0x02, 0x64, 0x5D, 0xF0, 0xDB, 0x06, 0x00, - 0x00, 0x80, 0xE2, 0xBF, 0x01, 0x6B, 0xEB, 0x87, 0x6C, 0x40, 0xF4, 0x7E, 0x7C, 0x64, 0xB9, 0xFD, - 0xA8, 0xC6, 0x87, 0xBB, 0xFD, 0xF8, 0x56, 0x3B, 0xB7, 0x1F, 0x3A, 0x36, 0x1A, 0x6D, 0xE2, 0x21, - 0x75, 0x0B, 0x32, 0xC3, 0x9F, 0x0E, 0x8A, 0x34, 0x53, 0x21, 0x91, 0x65, 0x2D, 0xE2, 0xC3, 0x6D, - 0xE3, 0x05, 0xE6, 0xFC, 0xEE, 0x25, 0xE6, 0xDC, 0xDE, 0x65, 0xF2, 0x01, 0xEC, 0x69, 0x13, 0x1F, - 0x6E, 0xCF, 0x5F, 0x69, 0xCC, 0x89, 0x3B, 0x73, 0xFF, 0x94, 0x78, 0x00, 0x00, 0x80, 0x52, 0xF1, - 0x0E, 0xAD, 0x1C, 0xEC, 0xF9, 0xF8, 0xC8, 0x78, 0xFB, 0x71, 0xEA, 0x51, 0x1B, 0x1D, 0xB9, 0xDC, - 0x7E, 0xE8, 0xC8, 0x68, 0xB6, 0xD3, 0xF7, 0xC4, 0x6F, 0x41, 0x2A, 0xF1, 0xD1, 0x7E, 0x80, 0xA8, - 0x90, 0xC8, 0xB2, 0x64, 0xF1, 0x71, 0xEE, 0x49, 0xBB, 0x4D, 0x1F, 0x76, 0x01, 0x62, 0xCE, 0x0E, - 0xD5, 0xDE, 0x82, 0x4C, 0xAB, 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x6F, 0xC2, 0x02, 0x00, 0x00, - 0x68, 0xC6, 0xDB, 0x77, 0x45, 0xE4, 0x15, 0xBC, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xA9, - 0xA0, 0x48, 0xB3, 0x20, 0x3E, 0x82, 0xA5, 0xBB, 0xFD, 0x88, 0xC4, 0x47, 0x5B, 0xB7, 0x1F, 0x3A, - 0x30, 0x92, 0x6C, 0xE2, 0x81, 0xE8, 0x2D, 0x88, 0x8D, 0x8F, 0xB6, 0x23, 0x44, 0x85, 0x44, 0x96, - 0x25, 0x8F, 0x8F, 0xB3, 0x83, 0x76, 0x4F, 0x5C, 0x60, 0xDE, 0xDD, 0xB1, 0xC4, 0x4C, 0xEC, 0x5E, - 0x5E, 0x3D, 0x7C, 0x4F, 0xC7, 0xF8, 0x70, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x2D, 0x98, 0x7D, - 0xF6, 0x70, 0xD7, 0xEB, 0xF1, 0x91, 0xE6, 0xF6, 0x23, 0x8C, 0x8F, 0x5C, 0x6E, 0x3F, 0x74, 0x58, - 0x24, 0x5D, 0xE5, 0x16, 0x64, 0xA6, 0x8D, 0x10, 0x1B, 0x20, 0x4F, 0xB5, 0x1B, 0x20, 0x2A, 0x24, - 0xB2, 0x2C, 0x43, 0x7C, 0x3C, 0x7E, 0x81, 0x39, 0x33, 0xF8, 0x41, 0x17, 0x20, 0xE6, 0xF4, 0x8E, - 0xA5, 0x03, 0xD3, 0x35, 0x3E, 0xFC, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x00, 0x34, 0x62, - 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x0B, 0x75, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, - 0x66, 0x91, 0xF8, 0x08, 0xA6, 0x6E, 0x3F, 0xAA, 0x01, 0x52, 0x73, 0xFB, 0x31, 0xAB, 0xFE, 0xF6, - 0xE3, 0x9B, 0x2A, 0x2E, 0x5A, 0x4D, 0x47, 0x45, 0x9A, 0xBD, 0xF3, 0xD7, 0x33, 0xCC, 0x99, 0x1F, - 0x4E, 0x06, 0x48, 0xF6, 0x5B, 0x10, 0x15, 0x12, 0x59, 0x96, 0x31, 0x3E, 0x1E, 0x9D, 0x61, 0x4E, - 0xFF, 0xD8, 0xEE, 0x99, 0x45, 0x66, 0xE2, 0xC0, 0xAA, 0x93, 0xD3, 0x36, 0x3E, 0xDC, 0x8E, 0xAC, - 0x32, 0xE6, 0xF8, 0x1D, 0x4B, 0x82, 0x2F, 0x31, 0x00, 0x00, 0x00, 0x44, 0x99, 0x91, 0xFE, 0x01, - 0xB3, 0xF3, 0x12, 0x1D, 0x18, 0xCD, 0x26, 0xA3, 0x22, 0xE9, 0x54, 0x50, 0xA4, 0x59, 0x2C, 0x3E, - 0x12, 0xDC, 0x7E, 0x4C, 0xB8, 0x00, 0x79, 0xDC, 0xDD, 0x7E, 0xC4, 0xE2, 0xC3, 0xED, 0x7B, 0x59, - 0xBE, 0xF5, 0x4A, 0x07, 0x45, 0x9A, 0xB9, 0xF8, 0xF0, 0x77, 0xF7, 0x0C, 0x7B, 0x90, 0x6F, 0xE7, - 0x16, 0x44, 0x85, 0x44, 0x96, 0xB5, 0x17, 0x1F, 0xA7, 0x1E, 0x76, 0x01, 0x32, 0xDB, 0x98, 0x63, - 0x77, 0x9A, 0x69, 0x1B, 0x1F, 0x3F, 0xBF, 0xCC, 0x98, 0x17, 0x3F, 0x67, 0xCC, 0x89, 0x2F, 0xAD, - 0x0D, 0xBE, 0xC4, 0x00, 0x00, 0x00, 0x10, 0xE5, 0x1D, 0x5A, 0x75, 0x97, 0xD9, 0x7E, 0xB1, 0x8E, - 0x8C, 0x46, 0x93, 0x51, 0x91, 0x74, 0x2A, 0x28, 0xD2, 0x2C, 0x16, 0x1F, 0x2A, 0x40, 0x1A, 0x3E, - 0xFB, 0x61, 0xE3, 0x23, 0x97, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0x56, 0x8D, 0x8F, 0x60, 0x67, 0xBE, - 0x5F, 0x09, 0x90, 0x73, 0xD1, 0x5B, 0x90, 0x20, 0x44, 0x74, 0x74, 0x84, 0x53, 0x21, 0x91, 0x65, - 0x39, 0xC4, 0xC7, 0xD3, 0x1F, 0x33, 0xDE, 0xD1, 0xDB, 0xA7, 0x77, 0x7C, 0xB8, 0x3D, 0xFF, 0x59, - 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x88, 0x19, 0x59, 0xB9, 0x5E, 0x46, 0x46, 0xA3, 0xC9, 0xA8, - 0x48, 0x3A, 0x15, 0x14, 0x69, 0x56, 0x1F, 0x1E, 0xD5, 0xF8, 0xB0, 0xE1, 0x31, 0x79, 0xFB, 0x11, - 0xF9, 0xD6, 0xAB, 0xEA, 0xED, 0xC7, 0x45, 0x95, 0xDB, 0x0F, 0xBB, 0xF6, 0x6E, 0x3F, 0x74, 0x50, - 0xA4, 0x59, 0x3C, 0x3E, 0xFC, 0x65, 0xBA, 0x05, 0x51, 0x21, 0x91, 0x65, 0xC4, 0x47, 0xFB, 0x0B, - 0xE2, 0x23, 0x98, 0xF7, 0x8B, 0x3B, 0x5B, 0x7E, 0x30, 0x23, 0x00, 0x00, 0xC0, 0xB4, 0xE4, 0x1D, - 0xB8, 0x7A, 0x54, 0x86, 0x86, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, 0x66, 0xB1, 0xF8, 0x88, - 0x07, 0x88, 0x5D, 0xF5, 0x5B, 0xAF, 0xC2, 0x00, 0xA9, 0x3E, 0x78, 0x6E, 0xE3, 0xC3, 0x06, 0x48, - 0xFB, 0xB7, 0x1F, 0x3A, 0x28, 0xD2, 0x4C, 0xC6, 0x47, 0xB0, 0xF0, 0x16, 0xC4, 0xBD, 0x92, 0xB7, - 0xF5, 0x2D, 0x88, 0x0A, 0x89, 0x2C, 0x23, 0x3E, 0xDA, 0x5F, 0x6D, 0x7C, 0xF8, 0x01, 0x72, 0xF4, - 0xD6, 0xB1, 0xE0, 0x4B, 0x0C, 0x00, 0x00, 0x00, 0x51, 0xDE, 0xFE, 0x2B, 0x4F, 0xCA, 0xD8, 0x88, - 0x4F, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x78, 0x88, 0xF8, 0x08, 0x6F, 0x3F, 0x6A, - 0x1E, 0x3C, 0xF7, 0x6F, 0x3F, 0x2E, 0x9A, 0xBC, 0xFD, 0x08, 0xE3, 0x23, 0x0C, 0x90, 0xFB, 0xD3, - 0xDC, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x1D, 0x35, 0x73, 0xB7, 0x20, 0x8F, 0x4D, 0xDE, 0x82, - 0xF8, 0x11, 0x62, 0x63, 0xA3, 0x3E, 0x40, 0x54, 0x48, 0x64, 0x19, 0xF1, 0xD1, 0xFE, 0xEA, 0xE3, - 0xC3, 0xDF, 0xB1, 0x2F, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x00, 0x25, 0xD1, 0x1B, 0xB0, 0x64, 0x54, - 0x24, 0x9D, 0x0A, 0x8A, 0x34, 0x8B, 0x85, 0x47, 0x93, 0xF8, 0xA8, 0x3E, 0x78, 0x5E, 0xBD, 0xFD, - 0xB0, 0xF1, 0x11, 0xDE, 0x7E, 0xC4, 0xE3, 0xE3, 0x21, 0xBB, 0xBB, 0x55, 0x68, 0xA8, 0xE9, 0xA0, - 0x48, 0x33, 0x19, 0x1C, 0x62, 0xA7, 0xBF, 0x3B, 0x23, 0x78, 0x16, 0xA4, 0xC1, 0xB7, 0x62, 0x3D, - 0xAD, 0x42, 0x22, 0xCB, 0x88, 0x8F, 0xF6, 0x27, 0xC2, 0x23, 0x1C, 0x6F, 0xC2, 0x02, 0x00, 0x00, - 0xA8, 0xE7, 0xBF, 0x01, 0x6B, 0xD7, 0x1C, 0x1D, 0x1D, 0xE1, 0x64, 0x54, 0x24, 0x9D, 0x0A, 0x8A, - 0x34, 0x8B, 0x85, 0x47, 0x38, 0x15, 0x20, 0xF1, 0xD7, 0xEE, 0x3E, 0x39, 0x19, 0x1F, 0xF2, 0xCD, - 0x57, 0x89, 0x6F, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0A, 0x8D, 0x66, 0x7B, 0x37, 0x7E, 0x0B, 0x12, - 0x46, 0x88, 0x8B, 0x8F, 0x70, 0x32, 0x2A, 0x92, 0x8E, 0xF8, 0x68, 0x7F, 0x22, 0x3A, 0xA2, 0x7B, - 0xF1, 0x7A, 0x63, 0x5E, 0xBE, 0xB3, 0xE6, 0x53, 0xE1, 0x01, 0x00, 0x00, 0xA6, 0x3D, 0x73, 0xA8, - 0x7F, 0xAD, 0xD9, 0xF1, 0x49, 0x1D, 0x1E, 0x6E, 0x32, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, 0xC5, - 0xA2, 0x23, 0xBA, 0x78, 0x7C, 0x44, 0x6F, 0x3F, 0x82, 0xF8, 0x98, 0xBC, 0xFD, 0x10, 0xDF, 0x7A, - 0x95, 0xF8, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xA9, 0xC0, 0x68, 0x35, 0x77, 0x0B, 0x52, 0xF7, 0xE1, - 0x84, 0x36, 0x3A, 0xF2, 0x09, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xE0, 0x88, 0xEF, 0x85, 0xAB, 0x79, - 0x13, 0x16, 0x00, 0x00, 0x40, 0x9C, 0xFF, 0x06, 0xAC, 0xAD, 0x1F, 0xEC, 0xB9, 0xF8, 0x68, 0x74, - 0xFB, 0xE1, 0xE2, 0xE3, 0x6C, 0x2C, 0x3E, 0xB2, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x17, - 0x49, 0xF7, 0xEE, 0x8F, 0x2B, 0x01, 0xE2, 0xBF, 0x96, 0xF7, 0x29, 0x1B, 0x1F, 0x6E, 0xD1, 0x08, - 0x91, 0x71, 0xD1, 0x6A, 0xC4, 0x47, 0xFB, 0x13, 0xB1, 0xD1, 0x60, 0xDE, 0x89, 0x2F, 0x0D, 0x06, - 0x5F, 0x6A, 0x00, 0x00, 0x00, 0x70, 0xBC, 0xE1, 0xE5, 0x43, 0x3D, 0x15, 0x1F, 0x6E, 0xF1, 0xF8, - 0x88, 0xDE, 0x7E, 0xB8, 0xF8, 0x68, 0x75, 0xFB, 0xF1, 0xA0, 0x9D, 0x0C, 0x8E, 0xE8, 0x74, 0x50, - 0xA4, 0x99, 0x8A, 0x8A, 0x34, 0x3B, 0xFD, 0xED, 0xE0, 0x16, 0xE4, 0x49, 0x1B, 0x20, 0x36, 0x0A, - 0xEA, 0x02, 0x24, 0x75, 0x84, 0x10, 0x1F, 0xED, 0x4F, 0x87, 0x46, 0xA3, 0x79, 0x47, 0x6F, 0x1D, - 0x0D, 0xBE, 0xD4, 0x00, 0x00, 0x00, 0xE0, 0x78, 0x7B, 0xAF, 0xA8, 0x7F, 0x05, 0xAF, 0x8C, 0x8A, - 0xA4, 0x53, 0x41, 0x91, 0x66, 0x22, 0x38, 0xA2, 0xB3, 0xD1, 0xE1, 0x7E, 0xAC, 0xBB, 0xFD, 0xB0, - 0xF1, 0x71, 0x6E, 0xE3, 0x45, 0xC9, 0x6E, 0x3F, 0xEE, 0x6B, 0x75, 0xFB, 0xA1, 0x83, 0x22, 0xCD, - 0x54, 0x50, 0x64, 0xD9, 0xBB, 0xF6, 0xD0, 0x1F, 0x06, 0x48, 0x7B, 0xB7, 0x20, 0xC4, 0x47, 0xFB, - 0xD3, 0x91, 0xD1, 0x74, 0xC7, 0x6E, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x51, 0x66, 0xEF, 0xE2, - 0xDE, 0x89, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x08, 0x03, 0x24, 0x88, 0x8F, 0x78, 0x80, 0xD4, - 0xC5, 0x47, 0xCB, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0xA6, 0x42, 0x22, 0xDB, 0x2E, 0x30, 0xA7, 0xBF, - 0x6D, 0xA3, 0x60, 0x70, 0x32, 0x42, 0x6A, 0x02, 0xC4, 0x85, 0x45, 0xA2, 0x08, 0x21, 0x3E, 0xDA, - 0x9F, 0x88, 0x8B, 0x24, 0x73, 0x6F, 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x1B, 0x00, 0x00, - 0xC0, 0xF4, 0x66, 0x46, 0xFA, 0x67, 0xD7, 0xBC, 0x82, 0x57, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, - 0x33, 0x11, 0x1B, 0xF1, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, 0xDF, 0x7E, 0xD8, 0xF0, 0x10, 0xB7, 0x1F, - 0xF2, 0x43, 0x07, 0x9B, 0xDE, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x76, 0x41, 0x75, - 0xE1, 0x2D, 0xC8, 0xB9, 0x4C, 0xB7, 0x20, 0xC4, 0x47, 0xFB, 0x13, 0x61, 0x91, 0x74, 0x63, 0xCB, - 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xC8, 0x7F, 0x05, 0x6F, 0xF8, 0x06, 0x2C, 0x19, 0x15, 0x49, - 0xA7, 0x82, 0x22, 0xCD, 0x62, 0xA1, 0xD1, 0x68, 0x61, 0x78, 0x84, 0xF1, 0xE1, 0xF6, 0xD4, 0x85, - 0x35, 0xDF, 0x7A, 0x15, 0x0F, 0x90, 0x53, 0x2E, 0x40, 0xC2, 0xDB, 0x8F, 0x1F, 0xD9, 0xC9, 0xF0, - 0x70, 0xD3, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x6C, 0x32, 0x3E, 0xDC, 0x4E, 0x7D, 0xD3, 0xC5, - 0x41, 0x96, 0x5B, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xA8, 0x48, 0xB3, 0x17, 0x3F, 0xC7, 0x9B, 0xB0, - 0x00, 0x00, 0x00, 0x42, 0xFE, 0x1B, 0xB0, 0xB6, 0x7D, 0xC4, 0x1E, 0xEE, 0x55, 0x54, 0x24, 0x9D, - 0x0A, 0x8A, 0x34, 0x13, 0xA1, 0xA1, 0x66, 0xA3, 0xC3, 0xFD, 0x58, 0x7B, 0xFB, 0x61, 0xE3, 0x23, - 0xCD, 0xED, 0xC7, 0xB7, 0x1A, 0xDD, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x56, 0x1B, - 0x1F, 0xE1, 0x26, 0x7E, 0x94, 0xF6, 0x16, 0x84, 0xF8, 0x68, 0x7F, 0x22, 0x28, 0x32, 0x8C, 0x37, - 0x61, 0x01, 0x00, 0x00, 0x04, 0xBC, 0x91, 0x81, 0x0D, 0x3A, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, - 0xC5, 0x22, 0xA3, 0xD9, 0xC2, 0xF0, 0x88, 0xC4, 0x47, 0xA2, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x68, - 0xD7, 0xA3, 0xF1, 0xE1, 0xD6, 0xF2, 0x16, 0x84, 0xF8, 0xC8, 0x79, 0x3A, 0x26, 0xB2, 0xCC, 0x3B, - 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0x38, 0xDE, 0x81, 0xAB, 0x47, 0x75, 0x58, 0x24, 0x99, - 0x0A, 0x8A, 0x34, 0x13, 0x91, 0xD1, 0x62, 0xD5, 0x00, 0x09, 0xE3, 0xA3, 0xED, 0xDB, 0x0F, 0x1D, - 0x14, 0x69, 0xA6, 0x43, 0x22, 0xCB, 0x74, 0x78, 0x44, 0x17, 0xDE, 0x82, 0xB8, 0x78, 0x68, 0x7C, - 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x12, 0x59, 0xE7, 0x1D, 0xBB, 0x65, 0x3C, 0xF8, 0x92, 0x03, - 0x00, 0x00, 0x98, 0xDE, 0xBC, 0x7D, 0x57, 0x8C, 0xEB, 0xB8, 0x68, 0x35, 0x15, 0x14, 0x69, 0xA6, - 0x03, 0xA3, 0xE1, 0x6A, 0x6E, 0x3F, 0x6C, 0x7C, 0x34, 0xBB, 0xFD, 0x08, 0xE3, 0xA3, 0xE5, 0xED, - 0x87, 0x0E, 0x8A, 0x34, 0xD3, 0x21, 0x91, 0x65, 0x3A, 0x38, 0xE2, 0xF3, 0x6F, 0x41, 0x1E, 0x6F, - 0x76, 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x11, 0x6D, 0xCD, 0xBD, 0x09, 0x6B, 0xEC, 0x8B, 0xB3, - 0x82, 0x2F, 0x3B, 0x00, 0x00, 0x80, 0xE9, 0xCB, 0x0C, 0x2D, 0xB0, 0x07, 0x7C, 0x15, 0x18, 0xCD, - 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x60, 0x24, 0x58, 0x18, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xB9, 0x1F, - 0xC9, 0x6E, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0E, 0x89, 0x2C, 0xD3, 0xB1, 0xD1, 0x68, 0x67, 0xBE, - 0x2F, 0x9E, 0x05, 0xF1, 0x43, 0x24, 0x12, 0x1E, 0x6E, 0xC4, 0x47, 0x86, 0x89, 0x78, 0xC8, 0x63, - 0x63, 0xCB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x80, 0xFF, 0x06, 0xAC, 0x9D, 0xB3, 0xED, 0xE1, 0x5E, - 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0x33, 0x1D, 0x17, 0x4D, 0x17, 0xB9, 0xF9, 0xF0, 0xE3, 0x43, - 0xDC, 0x7E, 0x4C, 0xB8, 0x3D, 0x1E, 0xB9, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xE5, 0x89, 0x0F, - 0x7F, 0x77, 0xAB, 0x5B, 0x90, 0x4A, 0x68, 0x18, 0x1B, 0x19, 0xC4, 0x47, 0xD6, 0x89, 0x70, 0xC8, - 0x65, 0x8B, 0x8D, 0x79, 0xF1, 0x5A, 0xE3, 0xFD, 0xE2, 0x4B, 0x77, 0x05, 0x5F, 0x7A, 0x00, 0x00, - 0x00, 0xD3, 0x93, 0x0D, 0x90, 0x75, 0x66, 0xC7, 0x27, 0xEC, 0x21, 0x5F, 0x85, 0x86, 0x9A, 0x0A, - 0x8A, 0x34, 0x13, 0x71, 0x91, 0x64, 0x61, 0x7C, 0xC4, 0x6E, 0x3F, 0x12, 0x3F, 0xFB, 0xF1, 0xCD, - 0x12, 0xC5, 0x47, 0xB0, 0x33, 0xDF, 0x8B, 0x3E, 0x0B, 0x52, 0x89, 0x8C, 0x70, 0x2E, 0x40, 0xC2, - 0xF0, 0x20, 0x3E, 0x92, 0x4E, 0x85, 0x43, 0x1E, 0xB3, 0xF1, 0xE1, 0x66, 0x7F, 0xED, 0xDE, 0x89, - 0x3B, 0x36, 0x04, 0x5F, 0x7A, 0x00, 0x00, 0x00, 0xD3, 0x93, 0xFF, 0x0A, 0x5E, 0x19, 0x1A, 0x6A, - 0x2A, 0x28, 0xD2, 0x4C, 0x84, 0x45, 0xAB, 0xD9, 0xF0, 0xA8, 0xC6, 0x87, 0x0D, 0x8F, 0xC9, 0xDB, - 0x8F, 0xD8, 0xB7, 0x5E, 0x45, 0x6F, 0x3F, 0xEC, 0x1A, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, - 0x12, 0x59, 0xA6, 0xC3, 0x22, 0xF1, 0xAA, 0xB7, 0x20, 0x36, 0x40, 0x36, 0xBA, 0xC0, 0x08, 0x82, - 0x23, 0x12, 0x1E, 0xC4, 0x47, 0xD2, 0xA9, 0x70, 0xC8, 0x63, 0x41, 0x7C, 0x04, 0xF3, 0x4E, 0xFC, - 0x39, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x9B, 0x77, 0xF0, 0xBA, 0x84, 0x6F, 0xC0, 0x52, 0x41, - 0x91, 0x66, 0xB1, 0xB0, 0x48, 0x3A, 0x17, 0x20, 0xE1, 0xCD, 0x87, 0x5D, 0xF5, 0x5B, 0xAF, 0xE4, - 0xB3, 0x1F, 0x17, 0xB5, 0xB8, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x04, 0x45, - 0x86, 0x9D, 0xFE, 0xCE, 0x7B, 0xFD, 0xB0, 0x08, 0x23, 0xA4, 0x1A, 0x1C, 0x2E, 0x3E, 0x82, 0x28, - 0x21, 0x3E, 0x5A, 0x4D, 0x85, 0x43, 0x1E, 0xAB, 0x8D, 0x0F, 0x3F, 0x40, 0x8E, 0x7D, 0x81, 0x37, - 0x61, 0x01, 0x00, 0x80, 0xE9, 0xCD, 0xDB, 0xFB, 0xD9, 0x31, 0x1D, 0x1C, 0xD1, 0xA9, 0xA0, 0x48, - 0xB3, 0x58, 0x54, 0x24, 0x5D, 0x2C, 0x3E, 0xC2, 0xDB, 0x8F, 0xBA, 0xF8, 0x88, 0xDE, 0x7E, 0x84, - 0xF1, 0x11, 0x06, 0xC8, 0x86, 0xF0, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xE9, 0x90, 0xC8, 0x32, 0x1D, - 0x13, 0xA9, 0xF7, 0x57, 0xEF, 0xF5, 0xF7, 0xEE, 0xA3, 0x93, 0x01, 0x12, 0x9F, 0x1F, 0x27, 0x2E, - 0x3C, 0x88, 0x8F, 0x06, 0x53, 0xE1, 0x90, 0xC7, 0xEA, 0xE3, 0xC3, 0xDF, 0x91, 0x1B, 0x4C, 0xF0, - 0xA5, 0x07, 0x00, 0x00, 0x30, 0x3D, 0x99, 0x3D, 0xF6, 0x50, 0x24, 0xA3, 0x23, 0x9C, 0x0A, 0x8A, - 0x34, 0x8B, 0x45, 0x45, 0xD2, 0x35, 0x88, 0x8F, 0xEA, 0x83, 0xE7, 0xEA, 0xF6, 0x23, 0x1E, 0x1F, - 0x0F, 0xD9, 0xDD, 0x5D, 0xEE, 0xF8, 0x70, 0x73, 0xB7, 0x20, 0x7E, 0x60, 0xD8, 0x9D, 0x7B, 0xD2, - 0x86, 0x87, 0xDD, 0xB9, 0x68, 0x7C, 0xD8, 0xF0, 0x38, 0x6B, 0xC3, 0x83, 0xF8, 0x88, 0x4F, 0x85, - 0x43, 0x1E, 0x8B, 0x45, 0x47, 0x74, 0x2F, 0xAD, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xFA, 0x32, - 0x87, 0xFB, 0x97, 0x98, 0xDD, 0xF3, 0xEC, 0x81, 0x5F, 0x85, 0x87, 0x9B, 0x0A, 0x8A, 0x34, 0x8B, - 0x45, 0x45, 0x9A, 0xD9, 0xE8, 0xA8, 0x0B, 0x90, 0x26, 0xDF, 0x7A, 0x25, 0xDF, 0x7C, 0x75, 0xBF, - 0xBB, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x45, 0xE2, 0x23, - 0x9C, 0xBB, 0x05, 0xF1, 0x43, 0x63, 0xD0, 0xC6, 0x88, 0x0D, 0x90, 0xB3, 0xB1, 0x6F, 0xB9, 0x7A, - 0xF7, 0x31, 0xE2, 0xA3, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x88, 0x8E, 0xE8, 0x5E, 0xBC, 0xD6, 0x06, - 0xC8, 0x1D, 0xEB, 0x82, 0x2F, 0x41, 0x00, 0x00, 0x80, 0xE9, 0xC5, 0x7F, 0x05, 0x6F, 0xC3, 0x37, - 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x15, 0x49, 0xF7, 0x8C, 0x88, 0x8F, 0xE8, 0xED, 0x87, 0x8B, - 0x8F, 0x9A, 0x00, 0x11, 0xDF, 0x7A, 0xE5, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, - 0x26, 0x42, 0x22, 0xCB, 0x44, 0x7C, 0xBC, 0xBD, 0xFE, 0xBD, 0xE6, 0xD4, 0x3D, 0x36, 0x42, 0x5C, - 0x68, 0xB8, 0xE0, 0x70, 0x21, 0x12, 0xFC, 0xE8, 0xFE, 0xB5, 0x09, 0x17, 0x1F, 0x36, 0x3C, 0x4E, - 0x3F, 0x42, 0x7C, 0x54, 0xA6, 0xC2, 0x21, 0x8F, 0x89, 0xE0, 0x88, 0xCF, 0xFD, 0xBE, 0x4E, 0xDC, - 0xB1, 0x3E, 0xF8, 0x12, 0x04, 0x00, 0x00, 0x98, 0x5E, 0xFC, 0x37, 0x60, 0x6D, 0xF9, 0x80, 0x3D, - 0xF4, 0x77, 0x5F, 0x7C, 0x34, 0xBA, 0xFD, 0x50, 0xAF, 0xDD, 0xD5, 0xB7, 0x1F, 0xED, 0x7F, 0xEB, - 0x95, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0x0D, 0xE2, 0x23, 0xDC, 0x99, 0x07, 0x6D, 0x6C, - 0x3C, 0x3E, 0xA3, 0x12, 0x22, 0x41, 0x78, 0x4C, 0x3C, 0x7A, 0x81, 0x7F, 0xEB, 0x41, 0x7C, 0x84, - 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x6C, 0x34, 0x98, 0x77, 0xE2, 0x0E, 0xDE, 0x84, 0x05, 0x00, 0x00, - 0xA6, 0x27, 0xEF, 0xD0, 0xCA, 0xC1, 0xAE, 0x8B, 0x8F, 0x5C, 0x6E, 0x3F, 0x2E, 0x6A, 0xFB, 0xF6, - 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0xD6, 0xD7, 0xEC, 0xBF, 0xF6, - 0x4D, 0x17, 0x1D, 0x33, 0xFC, 0x9D, 0xB1, 0xE1, 0xE1, 0xE6, 0x7F, 0xCB, 0x55, 0x18, 0x1F, 0x4F, - 0x11, 0x1F, 0xC5, 0x4C, 0x87, 0x46, 0xA3, 0x79, 0xC7, 0xBE, 0x70, 0x32, 0xF8, 0x12, 0x04, 0x00, - 0x00, 0x98, 0x5E, 0xBC, 0xBD, 0x57, 0xC4, 0x5E, 0xC1, 0xAB, 0x82, 0x22, 0xCD, 0x44, 0x54, 0x24, - 0x5D, 0x24, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0xA3, 0xFE, 0xD9, 0x0F, 0x75, 0xFB, 0x61, 0xE3, 0xE3, - 0xBE, 0xF6, 0x6E, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x4B, 0x10, 0x1F, 0x6F, 0x7D, - 0xED, 0x02, 0xF3, 0xD6, 0x7F, 0xB9, 0xC0, 0x9C, 0xFA, 0xA1, 0x0D, 0x0D, 0x77, 0xE3, 0xF1, 0x63, - 0x1B, 0x1F, 0x41, 0x78, 0xBC, 0xF3, 0x90, 0xFD, 0x91, 0xF8, 0x28, 0x68, 0x3A, 0x32, 0x9A, 0xEE, - 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x4C, 0x4F, 0x66, 0xAF, 0x3D, 0xF0, 0x75, 0x53, 0x7C, - 0x04, 0x01, 0xD2, 0xEC, 0xF6, 0x23, 0x1E, 0x20, 0x75, 0xF1, 0xF1, 0x60, 0x7B, 0x0F, 0x9E, 0xEB, - 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x96, 0x22, 0x3E, 0xDE, 0xFC, 0xCF, 0xF6, 0xC7, 0xBB, 0x6D, - 0x84, 0x04, 0xD1, 0xE1, 0xEF, 0x41, 0xE2, 0x43, 0x87, 0x43, 0x1E, 0x13, 0x71, 0x91, 0x64, 0x47, - 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xFD, 0x98, 0xD1, 0xFE, 0x59, 0x66, 0x68, 0x81, 0x0D, - 0x80, 0xEE, 0x8A, 0x0F, 0xF7, 0xA3, 0xBA, 0xFD, 0x50, 0xCF, 0x7E, 0xD4, 0x7E, 0xE8, 0xA0, 0x0D, - 0x8F, 0x87, 0xDB, 0xBB, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x2C, 0x65, 0x7C, 0xB8, - 0xFD, 0xE1, 0xFF, 0xB1, 0xFF, 0xFF, 0x77, 0x66, 0x98, 0xB7, 0x1F, 0x98, 0x69, 0x67, 0xE3, 0x63, - 0x23, 0xF1, 0x51, 0xCC, 0x44, 0x58, 0x24, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x2F, 0xAD, 0x0D, 0xBE, - 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0xFF, 0x0D, 0x58, 0x3B, 0x67, 0xDB, 0x08, 0x50, 0x41, 0x91, - 0x66, 0x22, 0x2A, 0x92, 0x2E, 0x1A, 0x1F, 0x41, 0x80, 0x34, 0xBB, 0xFD, 0x70, 0xF1, 0x11, 0x0F, - 0x90, 0x6A, 0x7C, 0xB4, 0x79, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, 0x64, 0x59, 0xC6, 0xF8, - 0x78, 0xE3, 0x2F, 0x67, 0x98, 0xF1, 0xFF, 0x3C, 0xC3, 0xBC, 0xF5, 0x23, 0xFB, 0x7B, 0x7A, 0x92, - 0xF8, 0x28, 0x66, 0x22, 0x2A, 0xD2, 0xEC, 0xF9, 0x2B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x7E, - 0xBC, 0x43, 0xAB, 0xEE, 0x32, 0xDB, 0x3E, 0x6A, 0x43, 0x40, 0x45, 0x45, 0xD2, 0x89, 0xA8, 0x48, - 0x3A, 0x11, 0x1F, 0xEE, 0xC7, 0xD4, 0xB7, 0x1F, 0x61, 0x7C, 0xB8, 0xDB, 0x8F, 0x7B, 0xB3, 0xDD, - 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x4E, 0x7C, 0xFC, 0xA7, 0x19, 0xE6, 0xF7, - 0xFF, 0xF7, 0x0C, 0xF3, 0xE6, 0x03, 0xC4, 0x47, 0x31, 0x13, 0x41, 0x91, 0x61, 0xDE, 0x2F, 0xEE, - 0x18, 0x0A, 0xBE, 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0x33, 0xD2, 0xBF, 0x5E, 0x47, 0x45, 0xD2, - 0x89, 0xA8, 0x48, 0xBA, 0x78, 0x7C, 0xB8, 0x85, 0xE1, 0x11, 0xC6, 0x47, 0xAB, 0xDB, 0x0F, 0xFF, - 0x5B, 0xAF, 0x82, 0xF8, 0x70, 0xFB, 0xA1, 0x9D, 0x88, 0x8B, 0x56, 0xD3, 0x21, 0x91, 0x65, 0x22, - 0x24, 0xB2, 0x2C, 0x87, 0xF8, 0x78, 0xE3, 0xDB, 0x36, 0x3E, 0x5E, 0x22, 0x3E, 0xF2, 0x9F, 0x8E, - 0x89, 0x2C, 0xF3, 0x8E, 0xDE, 0x3C, 0x16, 0x7C, 0x29, 0x02, 0x00, 0x00, 0x4C, 0x0F, 0xDE, 0x81, - 0xAB, 0x46, 0x75, 0x58, 0x24, 0x99, 0x88, 0x8A, 0xA4, 0x6B, 0x10, 0x1F, 0xEE, 0xC7, 0x54, 0xB7, - 0x1F, 0xD1, 0xF8, 0x70, 0xB7, 0x1F, 0xDF, 0x4A, 0x7F, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, - 0x64, 0x19, 0xF1, 0x91, 0xC3, 0x54, 0x38, 0xE4, 0x31, 0x1D, 0x12, 0x99, 0xC7, 0x9B, 0xB0, 0x00, - 0x00, 0xC0, 0x74, 0xE3, 0xED, 0xFF, 0xEC, 0x49, 0x1D, 0x17, 0xAD, 0x26, 0xA2, 0x22, 0xE9, 0xE2, - 0xE1, 0x11, 0x2E, 0x0C, 0x8F, 0x30, 0x3E, 0x9A, 0xDD, 0x7E, 0x3C, 0x76, 0x91, 0x39, 0xFD, 0xA8, - 0x0D, 0x8E, 0x36, 0x6F, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, - 0x0A, 0x87, 0x3C, 0x26, 0x02, 0xA2, 0xDD, 0xB9, 0x37, 0x61, 0x1D, 0xBF, 0x63, 0x49, 0xF0, 0xE5, - 0x08, 0x00, 0x00, 0x50, 0x7E, 0x66, 0x68, 0xBE, 0x8D, 0x02, 0x15, 0x18, 0xCD, 0x26, 0xA2, 0x22, - 0xE9, 0x54, 0x78, 0x44, 0x96, 0xE8, 0xF6, 0x23, 0x88, 0x8F, 0x76, 0x6F, 0x3F, 0x74, 0x48, 0x64, - 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0x26, 0xE2, 0x21, 0x8F, 0xBD, - 0x78, 0x1D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x87, 0xFF, 0x06, 0xAC, 0x5D, 0x9F, 0xB6, 0x61, - 0xA0, 0x22, 0xA3, 0xD1, 0x44, 0x54, 0x24, 0x9D, 0x08, 0x8E, 0xEA, 0x92, 0xDE, 0x7E, 0x3C, 0x6E, - 0x03, 0x24, 0x8C, 0x8F, 0x68, 0x80, 0xFC, 0x80, 0xF8, 0x20, 0x3E, 0x54, 0x38, 0xE4, 0x31, 0x11, - 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x3E, 0xCC, 0xA1, 0xFE, 0xB5, - 0x66, 0xC7, 0xC7, 0x6D, 0x1C, 0xA8, 0xD0, 0x50, 0x13, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0x62, 0x53, - 0xB7, 0x1F, 0xD1, 0x00, 0xF1, 0xE3, 0xA3, 0xD1, 0xED, 0xC7, 0x37, 0x75, 0x68, 0xA8, 0xE9, 0x90, - 0xC8, 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x44, 0x43, 0xCE, - 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xDA, 0xF0, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0x36, 0x10, 0x54, - 0x6C, 0xC4, 0x27, 0xA2, 0x22, 0xE9, 0x44, 0x6C, 0xD4, 0x2C, 0xC1, 0xED, 0x87, 0xFB, 0xD6, 0xAB, - 0x89, 0xC7, 0x2E, 0x9C, 0xBC, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xC9, 0x6F, 0x3F, 0x74, 0x48, - 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0xA6, 0x83, 0x21, 0xEF, - 0x79, 0x47, 0xFF, 0x64, 0x34, 0xF8, 0x92, 0x04, 0x00, 0x00, 0x28, 0x37, 0x6F, 0x78, 0xE9, 0x90, - 0x8E, 0x8D, 0xF8, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8E, 0xF8, 0xE2, 0x01, 0xA2, 0x9E, 0xFD, 0x78, - 0xDC, 0xC6, 0x47, 0x9B, 0xB7, 0x1F, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x11, 0x1F, 0x39, - 0x4C, 0x85, 0x43, 0x1E, 0xD3, 0xB1, 0x50, 0xC8, 0x8E, 0x7D, 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, - 0xE9, 0xC1, 0xDB, 0xBB, 0x24, 0xC1, 0x2B, 0x78, 0x45, 0x54, 0x24, 0x9D, 0x8A, 0x8D, 0xE8, 0x6C, - 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, 0x47, 0xEC, 0x5B, 0xAF, 0x9E, 0xB8, 0x70, 0xF2, - 0xF6, 0xC3, 0x2E, 0xCB, 0xED, 0x87, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, - 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x24, 0x14, 0xB6, 0x45, 0x95, 0x37, 0x61, 0xBD, 0x7C, 0xE7, 0xEC, - 0xE0, 0xCB, 0x12, 0x00, 0x00, 0xA0, 0xBC, 0xCC, 0x1E, 0x7B, 0xF8, 0x91, 0xD1, 0x11, 0x4E, 0x44, - 0x45, 0xD2, 0xA9, 0xE0, 0x88, 0x2F, 0x1A, 0x20, 0x76, 0xD5, 0x6F, 0xBD, 0x8A, 0x06, 0x88, 0x8B, - 0x0F, 0xFF, 0xF6, 0xE3, 0xC2, 0xFA, 0xDB, 0x8F, 0x87, 0xEC, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, - 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xB3, - 0x5F, 0x7F, 0x6E, 0x2F, 0x7E, 0xCE, 0x05, 0xC8, 0x40, 0xF0, 0x65, 0x09, 0x00, 0x00, 0x50, 0x4E, - 0x66, 0xA4, 0x7F, 0x76, 0xF3, 0x57, 0xF0, 0x8A, 0xA8, 0x48, 0x3A, 0x15, 0x1B, 0xF1, 0xC5, 0xE2, - 0x23, 0xBC, 0xFD, 0xA8, 0x7D, 0xF0, 0xDC, 0xC6, 0x47, 0xF4, 0xF6, 0x23, 0xFE, 0xAD, 0x57, 0xF7, - 0xB7, 0xBE, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, - 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x16, 0xC4, 0x87, 0x1F, 0x20, 0xD7, 0xF0, 0x26, 0x2C, 0x00, 0x00, - 0x50, 0x7E, 0xFE, 0x2B, 0x78, 0x1B, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, 0xA9, 0xD8, 0x88, 0xAF, - 0x41, 0x7C, 0x54, 0x1F, 0x3C, 0x77, 0xB3, 0xF1, 0x51, 0x73, 0xFB, 0x11, 0x7F, 0xF0, 0xDC, 0xDD, - 0x7E, 0xDC, 0xAD, 0xA3, 0x23, 0x9C, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, - 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0xB5, 0x48, 0x7C, 0x04, 0xF3, 0x4E, 0x7C, 0x69, 0x30, - 0xF8, 0xD2, 0x04, 0x00, 0x00, 0x28, 0x27, 0xFF, 0x0D, 0x58, 0x5B, 0x3F, 0x64, 0xA3, 0x61, 0x8A, - 0xE3, 0xC3, 0x4D, 0x05, 0x48, 0xCD, 0xB7, 0x5E, 0xD9, 0xF8, 0x70, 0xB7, 0x1F, 0x41, 0x7C, 0x64, - 0xB9, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x56, 0x1F, 0x1F, 0x6E, 0xDE, 0xF1, 0x3F, 0xE5, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0xDC, 0xBC, 0x91, 0x81, 0x0D, 0x1D, 0x89, 0x0F, 0xB7, 0x78, 0x7C, 0x54, 0x6F, 0x3F, 0x2E, - 0xAC, 0xC6, 0xC7, 0xE4, 0xED, 0x47, 0x2C, 0x3E, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, - 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xF1, 0xE1, - 0xEF, 0xD8, 0x2D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x94, 0x9B, 0x77, 0xE0, 0xAA, 0xD8, 0x1B, 0xB0, - 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x8C, 0x46, 0xB3, 0xD1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0x44, 0x02, - 0x24, 0x8C, 0x0F, 0xF9, 0xB9, 0x1F, 0xDF, 0x69, 0x7C, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, - 0x64, 0x59, 0x34, 0x3A, 0x88, 0x8F, 0x8C, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x11, - 0x1D, 0xD1, 0xB9, 0x37, 0x61, 0x8D, 0x7D, 0x71, 0x56, 0xF0, 0xE5, 0x09, 0x00, 0x00, 0x50, 0x3E, - 0xDE, 0xBE, 0x25, 0xE3, 0x53, 0x1E, 0x1F, 0x6E, 0xF1, 0xF8, 0x48, 0x7B, 0xFB, 0xF1, 0xA0, 0x9D, - 0x08, 0x0F, 0x37, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x52, 0xC4, 0x47, 0x18, 0x20, 0x2E, - 0x3E, 0xFE, 0x40, 0x7C, 0x44, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x38, 0xE2, - 0x1B, 0x5B, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB9, 0x4D, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, - 0xA9, 0xC0, 0x68, 0x36, 0x1B, 0x1D, 0xEE, 0x47, 0x75, 0xFB, 0x11, 0x0D, 0x90, 0xA6, 0xB7, 0x1F, - 0xF7, 0xE9, 0xDB, 0x0F, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x22, 0xF1, 0x11, 0xAE, 0x3E, - 0x3E, 0xEC, 0x5C, 0x78, 0x84, 0xB7, 0x1F, 0xC4, 0x47, 0x6C, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, - 0xA2, 0x26, 0x62, 0x43, 0xED, 0xC5, 0x6B, 0x8C, 0xF7, 0x8B, 0x2F, 0xDD, 0x15, 0x7C, 0x79, 0x02, - 0x00, 0x00, 0x94, 0x8B, 0xFF, 0x06, 0xAC, 0x9D, 0x9F, 0xD2, 0x51, 0x91, 0x74, 0xF1, 0xB8, 0x48, - 0xB2, 0x30, 0x3C, 0xC2, 0xF8, 0xF0, 0x03, 0x64, 0x66, 0x35, 0x3E, 0x6A, 0xDF, 0x7C, 0x95, 0xFC, - 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0x76, 0x24, 0x3E, 0xE2, - 0x37, 0x1F, 0x6F, 0x04, 0xF1, 0xF1, 0x07, 0xE2, 0xA3, 0xA0, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x34, - 0x9A, 0xCC, 0x3B, 0x71, 0xC7, 0x86, 0xE0, 0x4B, 0x14, 0x00, 0x00, 0xA0, 0x5C, 0x6C, 0x80, 0xAC, - 0x33, 0xDB, 0x2F, 0xD6, 0x61, 0x91, 0x64, 0x2A, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0xF6, - 0xF6, 0xC3, 0xC6, 0x47, 0x83, 0x67, 0x3F, 0xEA, 0x3E, 0x74, 0xD0, 0xBD, 0xF9, 0x4A, 0xDC, 0x7E, - 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x22, 0x3E, 0xA2, 0x37, 0x1F, 0x61, 0x7C, 0x84, - 0x37, 0x1F, 0xC4, 0x47, 0x38, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x91, 0xD1, 0x6C, - 0xDE, 0x89, 0x3F, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x9C, 0xFC, 0x57, 0xF0, 0xAA, 0xB0, 0x48, - 0xB2, 0x78, 0x58, 0x24, 0x5D, 0x18, 0x1E, 0x61, 0x7C, 0x3C, 0x3D, 0xB3, 0xE6, 0xF6, 0xA3, 0xEE, - 0xD9, 0x0F, 0x1B, 0x20, 0xA7, 0xA2, 0x01, 0xF2, 0x23, 0xBB, 0x1E, 0x8C, 0x0F, 0xB7, 0xBA, 0x00, - 0x69, 0x10, 0x1F, 0xE3, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0xC0, - 0x68, 0x35, 0xEF, 0xD8, 0x17, 0xC6, 0x83, 0x2F, 0x51, 0x00, 0x00, 0x80, 0x72, 0xF1, 0x0E, 0x5C, - 0x33, 0x2A, 0xE3, 0xA2, 0xD5, 0x54, 0x58, 0x24, 0x99, 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, - 0xF1, 0x11, 0xBD, 0xFD, 0x88, 0xC5, 0x47, 0xDD, 0xED, 0xC7, 0xB7, 0x6A, 0x6F, 0x3F, 0x74, 0x48, - 0x64, 0x99, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x56, 0x17, 0x1F, 0xEA, 0x8D, 0x57, 0xC4, 0x47, - 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x71, 0x91, 0x68, 0x47, 0x56, 0xF3, 0x26, - 0x2C, 0x00, 0x00, 0x50, 0x4E, 0xDE, 0xDE, 0x25, 0x63, 0x32, 0x30, 0x9A, 0x2D, 0x1E, 0x15, 0x69, - 0x16, 0x86, 0x47, 0x18, 0x1F, 0x69, 0x6F, 0x3F, 0x7E, 0x68, 0x57, 0xB6, 0xF8, 0x70, 0x37, 0x1F, - 0x2E, 0x3E, 0xDC, 0x43, 0xE7, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, - 0xA8, 0x48, 0xB3, 0x97, 0x56, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x4E, 0x66, 0xCF, 0x42, 0x1D, - 0x19, 0x8D, 0xA6, 0xA2, 0x22, 0xE5, 0x2A, 0x01, 0x32, 0xB3, 0xED, 0xDB, 0x0F, 0x1D, 0x12, 0x59, - 0x26, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0xB5, 0x8C, 0x8F, 0xBF, 0x9C, 0xFC, 0xAC, 0x8F, 0x3F, - 0xDC, 0x4B, 0x7C, 0x14, 0x33, 0x15, 0x09, 0x45, 0x4D, 0x04, 0x45, 0xDA, 0xBD, 0x78, 0x8D, 0x0D, - 0x90, 0x3B, 0xD6, 0x05, 0x5F, 0xA6, 0x00, 0x00, 0x00, 0xE5, 0x60, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, - 0xE6, 0xE8, 0xD0, 0x50, 0x13, 0x31, 0x91, 0x6A, 0xD5, 0xDB, 0x8F, 0x20, 0x3E, 0x9A, 0xDD, 0x7E, - 0x84, 0xF1, 0xD1, 0xE0, 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, - 0x0D, 0xE3, 0x23, 0x08, 0x90, 0xE8, 0x07, 0x0D, 0x12, 0x1F, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, - 0xA2, 0x26, 0x62, 0x22, 0xCB, 0x9E, 0xB7, 0x7F, 0x3F, 0x4E, 0xDC, 0xB1, 0x3E, 0xF8, 0x52, 0x05, - 0x00, 0x00, 0x28, 0x07, 0xFF, 0x15, 0xBC, 0x49, 0xDF, 0x80, 0xA5, 0x82, 0x22, 0xC3, 0xFC, 0xF8, - 0x88, 0xDD, 0x7E, 0x54, 0x03, 0xA4, 0xD5, 0xED, 0xC7, 0x3D, 0xBD, 0x1B, 0x1F, 0x95, 0xD7, 0xED, - 0x12, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x24, 0xDA, 0x18, 0x6F, - 0xC2, 0x02, 0x00, 0x00, 0xA5, 0xE3, 0xBF, 0x01, 0x6B, 0xEB, 0x07, 0x75, 0x70, 0x44, 0x27, 0x42, - 0x22, 0xF5, 0xC2, 0x9B, 0x8F, 0x26, 0xB7, 0x1F, 0x13, 0x2E, 0x40, 0x1E, 0x8B, 0xDC, 0x7E, 0x84, - 0xF1, 0xE1, 0xF6, 0xBD, 0xCA, 0xB7, 0x5E, 0xE9, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x26, 0xC2, - 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x11, 0xD1, 0xCE, - 0xBC, 0x63, 0x5F, 0x38, 0x19, 0x7C, 0xA9, 0x02, 0x00, 0x00, 0x94, 0x83, 0x37, 0xBC, 0x62, 0x50, - 0x06, 0x47, 0x74, 0x2A, 0x26, 0xB2, 0x2C, 0x8C, 0x8F, 0xD8, 0xED, 0x47, 0xE2, 0x67, 0x3F, 0xBE, - 0xC9, 0xB7, 0x5D, 0x25, 0x9B, 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, - 0x2A, 0x12, 0x8A, 0x9A, 0x0E, 0x88, 0xB6, 0x77, 0x64, 0xB5, 0x09, 0xBE, 0x54, 0x01, 0x00, 0x00, - 0xCA, 0xC1, 0xDB, 0x7B, 0x79, 0xF3, 0x57, 0xF0, 0xAA, 0x90, 0x48, 0xBB, 0x4D, 0x6E, 0x93, 0xE1, - 0x31, 0x79, 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xFC, 0xF6, 0x43, 0x87, 0x44, - 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0x0D, 0xE3, 0xC3, 0x3D, 0x70, 0x1E, 0x8B, 0x8F, - 0xF1, 0xFF, 0x32, 0xCB, 0x9C, 0xFF, 0xD7, 0x5B, 0x83, 0xF8, 0x58, 0x65, 0x0F, 0xFA, 0x2A, 0x28, - 0xD2, 0x4C, 0x84, 0x43, 0x1E, 0x93, 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x84, 0x43, 0x5E, 0x3B, 0xB2, 0x92, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF1, 0xF6, 0x2C, - 0x1A, 0x97, 0xE1, 0xE1, 0xA6, 0x62, 0x22, 0xCB, 0x6C, 0x78, 0x44, 0x6F, 0x3F, 0xAA, 0xDF, 0x7A, - 0x15, 0x06, 0x48, 0xCD, 0x6B, 0x77, 0x2F, 0x94, 0xB7, 0x1F, 0xEF, 0x7C, 0x23, 0xAF, 0xDB, 0x0F, - 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x2D, 0x6D, 0x7C, 0x9C, 0xFB, 0xE9, 0x17, 0x2A, 0xF1, 0x71, - 0xD4, 0xFE, 0x28, 0x83, 0x22, 0xCD, 0x44, 0x38, 0xE4, 0x31, 0x19, 0x0E, 0x79, 0x4C, 0x85, 0x43, - 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x44, 0x34, 0xE4, 0x39, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xCA, 0xC4, - 0x0C, 0xF5, 0xCF, 0x32, 0x43, 0x7D, 0x53, 0x1A, 0x1F, 0xE1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xCD, - 0x9E, 0xFD, 0x70, 0xF1, 0x71, 0x7F, 0x5E, 0xB7, 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, - 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x82, 0x21, 0xEF, 0xB9, 0xFF, - 0x6D, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0x0B, 0xFF, 0x0D, 0x58, 0x3B, 0x3E, 0x39, 0xE5, 0xF1, - 0x51, 0x7D, 0xF0, 0x5C, 0xDD, 0x7E, 0xC4, 0x1E, 0x3C, 0x7F, 0xE7, 0x41, 0x1B, 0x1F, 0x5F, 0x57, - 0x31, 0x91, 0x76, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x62, 0xA1, 0xA0, 0x79, 0x27, 0xEE, 0xE0, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0x1C, 0xBC, 0xE1, 0xFE, 0xBB, 0xCC, 0xB6, 0x8F, 0x14, 0x13, 0x1F, 0xCF, 0x54, 0xE2, 0xA3, - 0x2E, 0x40, 0xE2, 0xAF, 0xDD, 0x75, 0xB7, 0x1F, 0x41, 0x7C, 0xC4, 0x6F, 0x3F, 0xDE, 0x79, 0xD8, - 0xC6, 0xC7, 0x77, 0xF3, 0xF8, 0xD6, 0x2B, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, - 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xA1, 0x50, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, - 0x82, 0x2F, 0x59, 0x00, 0x00, 0x80, 0xDE, 0xE6, 0xBF, 0x82, 0xB7, 0xA8, 0xF8, 0x08, 0x02, 0xA4, - 0xE1, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0x8A, 0xC7, 0x47, 0x2E, 0xB7, 0x1F, 0x22, 0x24, - 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0xA6, - 0x23, 0xA1, 0xD0, 0x1D, 0xB9, 0x91, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF0, 0x0E, 0x5C, 0x33, - 0xF9, 0x06, 0x2C, 0x19, 0x12, 0x59, 0x36, 0x19, 0x1F, 0x8D, 0x6E, 0x3F, 0xAA, 0xAF, 0xDD, 0xAD, - 0xDE, 0x7E, 0x5C, 0x14, 0xBB, 0xFD, 0xB0, 0xE1, 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, - 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x38, 0x98, - 0x8A, 0xBD, 0x34, 0x60, 0xCC, 0xF1, 0x3B, 0x96, 0x04, 0x5F, 0xB6, 0x00, 0x00, 0x00, 0xBD, 0xCB, - 0xDB, 0x77, 0xC5, 0xC9, 0x42, 0xE2, 0xA3, 0xAD, 0xDB, 0x8F, 0x20, 0x3E, 0x1E, 0x68, 0xF7, 0xF6, - 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, - 0x50, 0xD4, 0x44, 0x18, 0x4C, 0xD5, 0xDC, 0x9B, 0xB0, 0x4E, 0x7C, 0x69, 0x6D, 0xF0, 0x65, 0x0B, - 0x00, 0x00, 0xD0, 0xBB, 0xCC, 0xEE, 0xBE, 0x48, 0x3C, 0xB4, 0xBB, 0xDA, 0xF8, 0x70, 0x3F, 0xAA, - 0xDB, 0x8F, 0xFA, 0x67, 0x3F, 0xA2, 0xB7, 0x1F, 0x41, 0x7C, 0xB8, 0xDD, 0xDB, 0xCE, 0xED, 0x87, - 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, - 0xA8, 0x89, 0x28, 0x98, 0xCA, 0xBD, 0x70, 0x25, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xCF, 0x1C, - 0xE8, 0x5F, 0x62, 0x76, 0xCE, 0x0E, 0xE2, 0xA1, 0xDD, 0x45, 0xE2, 0x23, 0x08, 0x90, 0x66, 0xB7, - 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xE1, 0x6E, 0x3F, 0x64, 0x58, 0x24, 0x99, 0x08, 0x89, 0x2C, - 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x20, - 0xE8, 0xC0, 0xBC, 0xE3, 0xB7, 0x0F, 0x05, 0x5F, 0xBA, 0x00, 0x00, 0x00, 0xBD, 0xC9, 0x1C, 0x5C, - 0xB6, 0xD6, 0x6C, 0xFF, 0x58, 0x10, 0x10, 0xED, 0xAC, 0x3E, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0x43, - 0x3D, 0xFB, 0x51, 0xF9, 0xD0, 0xC1, 0x0B, 0x27, 0x03, 0xE4, 0x21, 0xBB, 0xCC, 0xB7, 0x1F, 0x22, - 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0xA6, 0x63, 0xA0, 0x13, 0xF3, 0x8E, 0xDE, 0xC2, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x33, 0x23, - 0xCB, 0xD7, 0x9B, 0xCD, 0xEF, 0xB3, 0xB1, 0xA0, 0xA2, 0x22, 0xE9, 0x62, 0xF1, 0xE1, 0x16, 0x86, - 0x47, 0x18, 0x1F, 0xB1, 0xDB, 0x8F, 0xBA, 0x67, 0x3F, 0x1E, 0xB5, 0xF1, 0xE1, 0x02, 0xA4, 0xED, - 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, - 0x45, 0x42, 0x51, 0xD3, 0x21, 0xD0, 0xB1, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, - 0xE6, 0x1D, 0xB8, 0x7E, 0xA8, 0x36, 0x26, 0xD2, 0x4E, 0xC7, 0x87, 0xFB, 0xB1, 0xE9, 0xED, 0x47, - 0x2C, 0x3E, 0xEA, 0x6E, 0x3F, 0xEE, 0xC9, 0x72, 0xFB, 0x21, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, - 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x00, 0x3A, 0x3D, 0xF7, - 0x26, 0xAC, 0x97, 0xEF, 0x9C, 0x1D, 0x7C, 0xF9, 0x02, 0x00, 0x00, 0xF4, 0x1E, 0x6F, 0xEF, 0x65, - 0xA3, 0xF5, 0x51, 0x91, 0x74, 0xB1, 0xF0, 0x08, 0x17, 0x86, 0x47, 0x18, 0x1F, 0x4D, 0x6F, 0x3F, - 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x4E, 0x06, 0x46, 0xB3, 0x89, 0x90, 0xC8, - 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xFC, 0x77, 0xC3, 0x5E, 0xFC, 0x9C, 0x31, 0xC7, 0x6E, 0x1B, 0x08, 0xBE, 0x7C, 0x01, 0x00, 0x00, - 0x7A, 0x8F, 0xD9, 0x63, 0x0F, 0x5B, 0x32, 0x2E, 0x5A, 0x4D, 0x84, 0x47, 0x64, 0xC9, 0x6E, 0x3F, - 0x2A, 0xF1, 0xD1, 0xFE, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, - 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xFE, 0xDD, 0x32, 0xDE, 0x84, 0x05, 0x00, - 0x00, 0x7A, 0x99, 0x19, 0xE9, 0x9F, 0x6D, 0x76, 0xCF, 0xB3, 0xC1, 0xA0, 0x02, 0xA3, 0xD9, 0x74, - 0x74, 0xF8, 0x4B, 0x7C, 0xFB, 0x31, 0xD3, 0x9C, 0x09, 0xE3, 0xA3, 0xAD, 0xDB, 0x0F, 0x11, 0x12, - 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xFE, 0x2E, 0x9B, 0x77, 0xE2, 0xF6, 0xC1, 0xE0, 0x4B, 0x18, 0x00, 0x00, 0xA0, 0xB7, 0xD8, - 0x00, 0x19, 0x48, 0xFF, 0x06, 0x2C, 0x11, 0x1D, 0xB1, 0xA9, 0xDB, 0x8F, 0x6A, 0x80, 0x84, 0xF1, - 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, - 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7F, 0xB7, 0xCD, 0x3B, 0xFA, 0x85, 0xD1, 0xE0, 0x4B, - 0x18, 0x00, 0x00, 0xA0, 0xB7, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0x3E, 0x60, 0xA3, 0x41, 0x85, - 0x86, 0x9A, 0x0E, 0x8E, 0xEA, 0x12, 0xDC, 0x7E, 0x4C, 0x3C, 0x31, 0xD3, 0x4C, 0x3C, 0x16, 0xB9, - 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0xFB, 0xC4, 0x47, 0x65, 0x22, 0x1C, 0xF2, 0x98, 0x0C, 0x87, 0x3C, - 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0xB0, 0xDF, 0x95, 0x3B, 0x76, 0x0B, 0x6F, - 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xC9, 0x1B, 0x59, 0xB1, 0xA1, 0x3E, 0x32, 0x1A, 0x4D, 0x04, 0x47, - 0x7C, 0xF1, 0x00, 0xA9, 0x7B, 0xF6, 0xC3, 0xC6, 0x47, 0xB3, 0xDB, 0x8F, 0x6F, 0xA8, 0xD0, 0x50, - 0x13, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, - 0x14, 0x35, 0x71, 0xC8, 0xEF, 0xE6, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xAB, 0xBC, 0x7D, 0x57, - 0x24, 0x7C, 0x03, 0x96, 0x88, 0x8D, 0xE8, 0x6C, 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, - 0x47, 0xE4, 0x5B, 0xAF, 0x06, 0x6D, 0x7C, 0x44, 0x6F, 0x3F, 0xEC, 0x6A, 0x6E, 0x3F, 0x36, 0x24, - 0xBD, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, - 0x54, 0x24, 0x14, 0x35, 0x71, 0xC0, 0xEF, 0xF6, 0x8D, 0x2D, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0xA0, - 0x37, 0x79, 0x7B, 0x2F, 0x1B, 0xAF, 0x8F, 0x8D, 0xF8, 0x62, 0xB1, 0xA1, 0x16, 0x0D, 0x10, 0xBB, - 0xEA, 0xB7, 0x5E, 0x85, 0x01, 0x62, 0xE3, 0x63, 0xF2, 0xF6, 0x63, 0x66, 0x1B, 0xB7, 0x1F, 0x22, - 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0x26, 0x0E, 0xF7, 0xBD, 0xB0, 0x17, 0xAF, 0x31, 0xDE, 0x2F, 0x6E, 0xBF, 0x2B, 0xF8, 0x32, 0x06, - 0x00, 0x00, 0xE8, 0x1D, 0xAD, 0xDF, 0x80, 0x15, 0x0B, 0x0D, 0xB5, 0x58, 0x7C, 0x84, 0xB7, 0x1F, - 0x93, 0x0F, 0x9E, 0xDB, 0xF8, 0x88, 0xDF, 0x7E, 0x84, 0xF1, 0x11, 0x06, 0x48, 0xA2, 0xDB, 0x0F, - 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, - 0x51, 0x13, 0x07, 0xFB, 0x1E, 0x9A, 0x77, 0xFC, 0x8E, 0x0D, 0xC1, 0x97, 0x31, 0x00, 0x00, 0x40, - 0x6F, 0xF0, 0xDF, 0x80, 0xB5, 0x73, 0xB6, 0x8D, 0x08, 0x15, 0x1E, 0x6E, 0xB1, 0xD0, 0x50, 0x6B, - 0x10, 0x1F, 0xD5, 0x07, 0xCF, 0x37, 0xCE, 0xAC, 0xBF, 0xFD, 0x08, 0xE2, 0xA3, 0x7A, 0xFB, 0xF1, - 0xA0, 0xDD, 0xD7, 0x55, 0x70, 0x44, 0x27, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, - 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0x50, 0xDF, 0x4B, 0xF3, 0x8E, 0xDE, 0xCA, - 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x31, 0x23, 0xCB, 0xD7, 0x35, 0x7E, 0x05, 0x6F, 0x2C, 0x34, - 0x1A, 0x4D, 0x05, 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x41, 0x7C, 0xC8, 0x37, - 0x5F, 0x7D, 0xB7, 0xD5, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, - 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7D, 0xAF, 0xCD, 0x3B, 0xFA, 0xF9, 0xF1, - 0xE0, 0x4B, 0x19, 0x00, 0x00, 0xA0, 0x37, 0xD8, 0x00, 0x59, 0x5F, 0x1F, 0x1E, 0x29, 0xE2, 0xC3, - 0x2D, 0x1E, 0x1F, 0xD5, 0xDB, 0x8F, 0xC9, 0xF8, 0xA8, 0x79, 0xF3, 0x55, 0xEA, 0xDB, 0x0F, 0x11, - 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, - 0xD3, 0x87, 0xF9, 0x9E, 0x9C, 0x7B, 0x13, 0xD6, 0xD8, 0x17, 0x67, 0x05, 0x5F, 0xCE, 0x00, 0x00, - 0x00, 0xDD, 0xCF, 0x3B, 0x70, 0xD5, 0x50, 0xBB, 0xF1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0xD4, 0xDD, - 0x7E, 0x5C, 0x98, 0xE1, 0xF6, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, - 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBE, 0x97, 0x37, 0xB6, 0x9C, 0x37, 0x61, - 0x01, 0x00, 0x80, 0xDE, 0xE2, 0xED, 0xBD, 0x7C, 0x2C, 0x73, 0x7C, 0xB8, 0xC5, 0xE3, 0xC3, 0x0F, - 0x90, 0x19, 0x39, 0xDD, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xC0, 0xF7, 0xFA, 0x78, 0x13, 0x16, 0x00, 0x00, - 0xE8, 0x35, 0x66, 0x68, 0x81, 0x0D, 0x89, 0xEC, 0xF1, 0xE1, 0x7E, 0xAC, 0xBD, 0xFD, 0xB0, 0xF1, - 0x11, 0xFB, 0xF6, 0xAB, 0xA6, 0xB7, 0x1F, 0xF7, 0x35, 0xBA, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, - 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x78, 0x2F, - 0xC3, 0x9E, 0xBF, 0x9C, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, 0x61, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, - 0x2E, 0xB5, 0x11, 0x91, 0x21, 0x3E, 0xDC, 0xC2, 0xF0, 0x08, 0xE3, 0xE3, 0xE9, 0x19, 0x35, 0xB7, - 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xF1, 0x80, 0x1D, 0xF1, 0x91, 0xCF, 0x64, 0x38, 0xE4, 0x31, - 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x07, 0xF7, 0x12, 0xCD, 0x3B, 0xFE, 0x67, 0xBC, - 0x09, 0x0B, 0x00, 0x00, 0xF4, 0x06, 0xFF, 0x15, 0xBC, 0xFE, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, - 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, 0xF1, 0xD1, 0xE0, 0xD9, 0x0F, 0xF9, 0xA1, 0x83, 0xF7, - 0xAA, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, - 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x87, 0xF6, 0x32, 0xCD, 0x3B, 0xFA, 0xF9, 0x93, 0xC1, 0x97, 0x34, - 0x00, 0x00, 0x40, 0x77, 0xF3, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0xF5, 0x71, 0x91, 0x64, 0x61, 0x78, - 0x84, 0xF1, 0x11, 0xBB, 0xFD, 0xA8, 0x7B, 0xF6, 0xC3, 0x06, 0xC8, 0x29, 0x17, 0x20, 0x4D, 0x6F, - 0x3F, 0x44, 0x48, 0x64, 0x99, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1F, 0xD8, 0x4B, 0xB7, 0x23, 0xAB, 0x4C, 0xF0, 0x25, 0x0D, 0x00, 0x00, 0xD0, - 0xDD, 0xBC, 0xE1, 0x65, 0x83, 0x32, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0x12, 0x20, 0x33, - 0xF4, 0xED, 0x47, 0x2C, 0x3E, 0x5A, 0xDF, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, - 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA8, 0x97, 0x75, 0x63, 0xCB, - 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0D, 0xDE, 0xDE, 0xCB, 0x46, 0xEB, 0xE2, 0x22, 0xC9, 0xAA, - 0xB7, 0x1F, 0x41, 0x7C, 0x24, 0xBD, 0xFD, 0x08, 0x03, 0xE4, 0x07, 0x76, 0xC4, 0x47, 0x7B, 0x93, - 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0xBA, 0x7F, - 0x5D, 0x68, 0xCC, 0x0B, 0x57, 0x1B, 0x73, 0xFC, 0x8E, 0x75, 0xC1, 0x97, 0x35, 0x00, 0x00, 0x40, - 0xF7, 0xF2, 0xF6, 0x2C, 0x18, 0x97, 0x81, 0xD1, 0x6C, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0x59, 0x6F, - 0x3F, 0xEE, 0x89, 0xDE, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2E, 0x3E, 0xDC, 0x7E, 0x6E, - 0xFF, 0x99, 0x38, 0x71, 0xDB, 0xFA, 0xE0, 0xCB, 0x1A, 0x00, 0x00, 0xA0, 0x3B, 0x99, 0xA1, 0xFE, - 0x59, 0x66, 0xF7, 0xDC, 0xFA, 0xC0, 0x68, 0xB5, 0xF0, 0xE6, 0x23, 0x97, 0xDB, 0x0F, 0x11, 0x12, - 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x2E, 0x8C, 0x8F, 0x60, 0xDE, 0x89, 0xDB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, - 0x6E, 0x66, 0x64, 0xE9, 0x80, 0xD9, 0xF1, 0x71, 0x1D, 0x19, 0xCD, 0x16, 0xC6, 0x47, 0xDB, 0xB7, - 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, - 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x5D, 0x2C, 0x3E, 0xFC, 0x00, 0x39, 0x7A, 0x33, 0x6F, 0xC2, - 0x02, 0x00, 0x00, 0xDD, 0xCD, 0x1B, 0x5E, 0x7E, 0x97, 0xD9, 0xFA, 0x21, 0x1D, 0x19, 0x8D, 0x16, - 0x8F, 0x8F, 0x66, 0xB7, 0x1F, 0x61, 0x7C, 0xC8, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, - 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, - 0xC4, 0x87, 0xBF, 0x23, 0x37, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xDD, 0xFC, 0x57, 0xF0, 0xAA, - 0xC8, 0x68, 0x34, 0x1B, 0x1D, 0x8D, 0x6E, 0x3F, 0xAA, 0x01, 0xD2, 0xEA, 0xF6, 0xE3, 0x1B, 0xC4, - 0x47, 0xA6, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, - 0x65, 0x9D, 0x0A, 0x8F, 0x70, 0x2F, 0xF5, 0xBB, 0x07, 0xD1, 0x97, 0x04, 0x5F, 0xDE, 0x00, 0x00, - 0x00, 0xDD, 0xC7, 0x3B, 0x70, 0x75, 0xF2, 0x37, 0x60, 0x45, 0xE3, 0xC3, 0x86, 0x47, 0xA3, 0xDB, - 0x8F, 0x09, 0x17, 0x20, 0x8F, 0x45, 0x6E, 0x3F, 0xC2, 0xF8, 0x70, 0xDB, 0xE0, 0xBE, 0xF5, 0x4A, - 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xD1, 0x11, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x3F, 0x5F, 0x1B, - 0x7C, 0x79, 0x03, 0x00, 0x00, 0x74, 0x1F, 0x6F, 0xDF, 0xE5, 0x27, 0x65, 0x6C, 0xC4, 0x17, 0xC4, - 0x47, 0xA3, 0xDB, 0x8F, 0xC4, 0xCF, 0x7E, 0x7C, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, - 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xC1, - 0x11, 0x1F, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xDD, 0x2E, 0xD1, 0x1B, 0xB0, 0xE2, 0xF1, 0x51, 0x73, - 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xED, 0xED, 0x47, 0x4E, 0xDF, 0x7A, 0x25, - 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, - 0xB2, 0x4E, 0xC5, 0x46, 0x83, 0x79, 0xC7, 0x6F, 0x1F, 0x0A, 0xBE, 0xBC, 0x01, 0x00, 0x00, 0xBA, - 0x8B, 0x39, 0x70, 0xFD, 0x12, 0xB3, 0xF3, 0x53, 0x3A, 0x3A, 0xC2, 0x45, 0xE2, 0xA3, 0x1A, 0x20, - 0x76, 0xD5, 0x6F, 0xBD, 0x0A, 0x03, 0xA4, 0xE6, 0xB5, 0xBB, 0x33, 0xC5, 0xED, 0xC7, 0xCC, 0x7C, - 0x6E, 0x3F, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, - 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0xC8, 0x68, 0x36, 0xEF, 0xC8, 0x9A, 0xB1, 0xE0, 0x4B, 0x1C, 0x00, - 0x00, 0xA0, 0xBB, 0x98, 0x83, 0xCB, 0xD6, 0x9A, 0xED, 0x1F, 0xD5, 0xE1, 0xE1, 0x16, 0x09, 0x8F, - 0x68, 0x7C, 0x84, 0xB7, 0x1F, 0x35, 0x0F, 0x9E, 0x37, 0x7A, 0xF6, 0xC3, 0x0F, 0x10, 0x1B, 0x1F, - 0xDF, 0xCD, 0xE1, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, - 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x08, 0x8C, 0x96, 0x3B, 0xBA, 0x86, 0x37, 0x61, 0x01, - 0x00, 0x80, 0xEE, 0xE4, 0xBF, 0x01, 0x6B, 0xF3, 0xAC, 0x4C, 0xF1, 0x51, 0x7D, 0xF0, 0x5C, 0xDD, - 0x7E, 0x04, 0xF1, 0x51, 0xB9, 0xFD, 0xB0, 0xF1, 0xF1, 0xA0, 0xDD, 0xD7, 0x45, 0x50, 0xA4, 0x99, - 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, - 0xCB, 0x3A, 0x15, 0x17, 0x49, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB7, 0xF2, 0x0E, 0x7C, 0x6E, - 0x28, 0x49, 0x7C, 0xD4, 0x05, 0x48, 0xFC, 0xB5, 0xBB, 0xEE, 0xF6, 0x23, 0x88, 0x8F, 0xDA, 0x37, - 0x5F, 0xD9, 0xF0, 0xC8, 0xE3, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x0A, 0x8B, 0xA4, 0x7B, 0xE1, 0x5A, 0x63, - 0x8E, 0xDD, 0x36, 0x10, 0x7C, 0x99, 0x03, 0x00, 0x00, 0x74, 0x0F, 0x6F, 0xEF, 0x65, 0xF5, 0xAF, - 0xE0, 0x8D, 0x44, 0x87, 0x8C, 0x8F, 0xE8, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0xAA, 0xDE, - 0x7E, 0x04, 0xF1, 0xD1, 0xEE, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, - 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x15, 0x69, 0xF6, 0xFC, 0x67, 0x79, - 0x13, 0x16, 0x00, 0x00, 0xE8, 0x4E, 0x66, 0x8F, 0x3D, 0xEC, 0xB4, 0x88, 0x0F, 0x37, 0x75, 0xFB, - 0x51, 0x7D, 0xED, 0x6E, 0xF5, 0xF6, 0xE3, 0xC2, 0xC8, 0xED, 0x47, 0x10, 0x1F, 0xED, 0xDE, 0x7E, - 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x62, 0x07, - 0xF4, 0x32, 0x4F, 0x05, 0x45, 0x86, 0x79, 0x27, 0x6E, 0x1B, 0x0C, 0xBE, 0xCC, 0x01, 0x00, 0x00, - 0xBA, 0x83, 0x19, 0xE9, 0x9F, 0x5D, 0xF3, 0x0A, 0xDE, 0x48, 0x70, 0x44, 0x97, 0xFE, 0xF6, 0x63, - 0xE6, 0x64, 0x80, 0x3C, 0x60, 0xA7, 0xC2, 0x22, 0xC9, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, - 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0x90, 0xC8, 0x3A, 0xEF, - 0xC8, 0xE7, 0x47, 0x83, 0x2F, 0x75, 0x00, 0x00, 0x80, 0xEE, 0x60, 0x46, 0x96, 0x0E, 0x54, 0xDF, - 0x80, 0x15, 0x09, 0x8E, 0xF8, 0xD4, 0xED, 0x47, 0xFD, 0xB3, 0x1F, 0xE1, 0xED, 0x47, 0x24, 0x3E, - 0xDC, 0xEE, 0xCD, 0x78, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, - 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x44, 0x44, 0x5B, 0xE3, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0xDB, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0xDE, 0x2F, 0xA3, 0x23, 0x5C, 0xB3, 0xDB, 0x8F, - 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0xC8, 0x7A, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, - 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x05, 0x44, 0xBB, 0x73, 0x6F, - 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x3B, 0x00, 0x00, 0x40, 0xE7, 0x79, 0x23, 0xCB, 0x36, - 0xA8, 0xE8, 0x88, 0x4E, 0xDD, 0x7E, 0xD4, 0x3F, 0xFB, 0xE1, 0x3E, 0x74, 0xD0, 0xBD, 0x7A, 0x37, - 0x12, 0x20, 0xEE, 0x43, 0x07, 0xB3, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, - 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xF1, 0x90, 0xC7, 0x5E, 0xBC, - 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, 0xEE, 0xE2, 0xED, 0xBD, 0x62, 0x54, 0x45, 0x47, 0xB8, 0x66, - 0xB7, 0x1F, 0xD5, 0x67, 0x3F, 0xFC, 0xD7, 0xEE, 0x06, 0xF1, 0xE1, 0xD6, 0xCE, 0xED, 0x87, 0x08, - 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, - 0x3A, 0x15, 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x2E, 0xDE, 0xDE, - 0xC5, 0xE3, 0x2A, 0x3C, 0xC2, 0x35, 0xBD, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC5, 0x87, 0xBA, 0xFD, - 0xB8, 0x27, 0xE5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x0D, 0x39, 0xCF, 0x3B, 0xB6, 0x96, 0x37, 0x61, - 0x01, 0x00, 0x80, 0xEE, 0xE1, 0xBF, 0x01, 0x2B, 0x12, 0x1C, 0xD1, 0xB5, 0xBC, 0xFD, 0xB0, 0xF1, - 0x31, 0xF1, 0xD8, 0x8C, 0xDA, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x60, 0xA7, 0x22, 0xA3, 0xD1, 0x44, - 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0x89, 0x58, 0x28, 0x62, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0E, 0xFE, - 0x1B, 0xB0, 0x76, 0x7C, 0x52, 0xC6, 0x87, 0x5B, 0xCB, 0xDB, 0x8F, 0x30, 0x3E, 0xDA, 0xBD, 0xFD, - 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, - 0x7A, 0x59, 0x27, 0x42, 0xA1, 0xA8, 0x79, 0x47, 0x6F, 0x1E, 0x0F, 0xBE, 0xE4, 0x01, 0x00, 0x00, - 0x3A, 0xCB, 0x8C, 0x2C, 0x5F, 0x67, 0xB6, 0x7D, 0xA4, 0x75, 0x7C, 0xC8, 0xDB, 0x8F, 0x19, 0xF9, - 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, - 0xC4, 0x21, 0xBD, 0xAC, 0x13, 0x91, 0x50, 0xE8, 0xDC, 0x9B, 0xB0, 0xC6, 0xBE, 0x38, 0x2B, 0xF8, - 0xB2, 0x07, 0x00, 0x00, 0xE8, 0x1C, 0x1B, 0x20, 0xEB, 0x55, 0x7C, 0xB8, 0xA9, 0xDB, 0x0F, 0x3F, - 0x40, 0x44, 0x7C, 0x64, 0xBE, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, - 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA1, 0xE8, 0xF1, 0x26, 0x2C, 0x00, - 0x00, 0xD0, 0x2D, 0xBC, 0x03, 0x57, 0x0D, 0x35, 0x8C, 0x0F, 0x1B, 0x1E, 0xF2, 0xF6, 0xE3, 0x09, - 0x1B, 0x1F, 0x2E, 0x40, 0x1E, 0x8D, 0xDC, 0x7E, 0x84, 0xF1, 0xE1, 0xB6, 0x81, 0xF8, 0xA8, 0x9B, - 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xA9, - 0x38, 0x98, 0x8A, 0xBD, 0x70, 0xB5, 0xF1, 0x7E, 0x71, 0xFB, 0x5D, 0xC1, 0x97, 0x3D, 0x00, 0x00, - 0x40, 0xE7, 0x78, 0x7B, 0x2F, 0x1B, 0x6B, 0x18, 0x20, 0x6E, 0x91, 0xDB, 0x0F, 0x17, 0x1F, 0x67, - 0x07, 0x67, 0xD4, 0xDE, 0x7E, 0xA8, 0x67, 0x3F, 0xBE, 0x21, 0x62, 0x23, 0x3E, 0x11, 0x1E, 0x6E, - 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2A, - 0x0C, 0xA6, 0x70, 0xDE, 0xF1, 0xDB, 0x36, 0x04, 0x5F, 0xF6, 0x00, 0x00, 0x00, 0x9D, 0x63, 0x86, - 0xE6, 0xEB, 0xF8, 0xA8, 0xB9, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC6, 0x47, 0x1E, 0xB7, 0x1F, 0x22, - 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, - 0xEB, 0x44, 0x10, 0x4C, 0xF5, 0xBC, 0xE3, 0x7F, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x67, 0x99, - 0x91, 0xFE, 0xD9, 0x66, 0xE7, 0x25, 0x3A, 0x40, 0xEC, 0xA2, 0xDF, 0x7A, 0x75, 0xEE, 0xC9, 0x19, - 0xE6, 0xAC, 0x8D, 0x8F, 0xC9, 0xDB, 0x8F, 0x19, 0xFA, 0xD9, 0x8F, 0x56, 0xB7, 0x1F, 0x22, 0x3C, - 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, - 0x44, 0x0C, 0x74, 0x62, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x74, 0x9C, 0xFF, 0x0A, 0xDE, 0xD8, 0x1B, - 0xB0, 0x6A, 0xBE, 0xF5, 0xCA, 0xEE, 0x9C, 0x0B, 0x10, 0x17, 0x1F, 0xF1, 0xDB, 0x8F, 0x68, 0x7C, - 0x84, 0x01, 0x72, 0x7F, 0x8B, 0xDB, 0x0F, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, - 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x22, 0x04, 0x3A, 0xB6, 0x97, 0x56, 0x99, - 0xE0, 0x4B, 0x1F, 0x00, 0x00, 0xA0, 0x33, 0xFC, 0x37, 0x60, 0x6D, 0xFE, 0xE3, 0x86, 0xF1, 0x51, - 0xB9, 0xFD, 0xB0, 0xF1, 0xD1, 0xE0, 0xF6, 0xC3, 0xC5, 0x47, 0xF5, 0xF6, 0xE3, 0x41, 0xBB, 0xAF, - 0x8B, 0xE8, 0x08, 0x27, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, - 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x45, 0x40, 0x27, 0x37, 0xB6, 0x8C, 0x37, 0x61, 0x01, 0x00, - 0x80, 0xCE, 0xF2, 0x86, 0x97, 0x0D, 0x36, 0xBC, 0xFD, 0x08, 0xE2, 0xA3, 0xE6, 0xF6, 0x23, 0x88, - 0x8F, 0x9A, 0xDB, 0x8F, 0xF0, 0xD9, 0x8F, 0xEF, 0x36, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, - 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA0, - 0xD3, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0xF2, 0x07, 0x00, 0x00, 0x98, 0x7A, - 0xDE, 0xDE, 0xCB, 0x46, 0x1B, 0xDE, 0x7E, 0x6C, 0xBC, 0xA0, 0x26, 0x3E, 0xEA, 0x3E, 0x74, 0x30, - 0xE9, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x75, 0xF8, 0xEF, 0xF8, 0x16, 0xD8, 0x5F, 0x9B, 0xFD, 0xDF, 0xE1, - 0xC4, 0x6D, 0xEB, 0x83, 0x2F, 0x7F, 0x00, 0x00, 0x80, 0xA9, 0xE7, 0xED, 0x9E, 0x3F, 0xAE, 0x6F, - 0x3F, 0x6C, 0x7C, 0x84, 0xDF, 0x7A, 0x55, 0x73, 0xFB, 0x31, 0x33, 0xDD, 0xED, 0x87, 0x08, 0x0F, - 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0xB1, 0x78, 0x20, 0x14, 0x39, 0x71, 0x48, 0x2F, 0xEB, - 0xE4, 0xE1, 0xBF, 0xD3, 0xB3, 0xF1, 0x11, 0xCC, 0x3B, 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x80, - 0xCE, 0x30, 0x43, 0xFD, 0xB3, 0xCC, 0xEE, 0xB9, 0x32, 0x3E, 0x72, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, - 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, - 0x0F, 0xFF, 0x9D, 0xDE, 0x64, 0x7C, 0xF8, 0x01, 0x72, 0xF4, 0xE6, 0x93, 0xC1, 0x1F, 0x01, 0x00, - 0x00, 0x00, 0x53, 0xCB, 0x7F, 0x03, 0xD6, 0xF6, 0x8B, 0x6B, 0x03, 0x24, 0x8C, 0x0F, 0xF9, 0xEC, - 0x47, 0x83, 0xDB, 0x8F, 0x7B, 0xC5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, - 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x6A, 0xE3, - 0xC3, 0xDF, 0x91, 0x1B, 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0C, 0x33, 0xB2, 0x7C, 0x9D, 0xD9, - 0xFA, 0xC1, 0xC8, 0xED, 0x87, 0x8D, 0x8F, 0xD8, 0xED, 0x47, 0xED, 0x9B, 0xAF, 0x44, 0x7C, 0x3C, - 0x60, 0x47, 0x7C, 0x14, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x11, 0x1F, 0x6E, 0x2F, 0xF5, 0xF3, 0x26, 0x2C, 0x00, 0x00, 0xD0, - 0x19, 0xE6, 0xE0, 0xF2, 0xF5, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xEC, 0xC7, - 0xE9, 0x78, 0x80, 0xB8, 0x0F, 0x1D, 0x8C, 0xDF, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, - 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, - 0x84, 0x47, 0x38, 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x00, 0x00, 0x00, 0x60, - 0xEA, 0x78, 0xFB, 0xAE, 0x1E, 0xAD, 0x89, 0x8F, 0x24, 0xCF, 0x7E, 0xB8, 0x35, 0xBA, 0xFD, 0x10, - 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, - 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x88, 0x8E, 0xE8, 0xDC, 0x3F, 0x67, 0xBC, 0x09, 0x0B, 0x00, - 0x00, 0x74, 0x82, 0xB7, 0xF7, 0xF2, 0x93, 0x7E, 0x7C, 0x34, 0xBA, 0xFD, 0x88, 0xC6, 0x87, 0xBA, - 0xFD, 0xF8, 0x76, 0xE4, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, - 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x38, 0xC4, 0xBC, - 0xE3, 0xB7, 0x0F, 0x05, 0x7F, 0x0C, 0x00, 0x00, 0x00, 0x4C, 0x1D, 0x6F, 0xE7, 0x9C, 0xF4, 0xB7, - 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x8E, 0xF8, 0xC8, 0x71, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x36, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, - 0x82, 0x3F, 0x06, 0x00, 0x00, 0x00, 0xA6, 0x86, 0x39, 0x70, 0xFD, 0x12, 0x6F, 0xFB, 0x27, 0xB2, - 0xDF, 0x7E, 0xDC, 0x13, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, - 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x87, 0x46, 0xC3, - 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xD5, 0xCC, 0xC1, 0x65, 0x6B, 0xBD, 0xAD, 0x1F, 0x6E, 0xEF, - 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, - 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x30, 0x5A, 0xCD, 0xBD, 0x09, 0xEB, 0xF8, - 0x1D, 0x4B, 0x82, 0x3F, 0x0E, 0x00, 0x00, 0x00, 0x8A, 0x77, 0xFE, 0xE0, 0xD2, 0xF5, 0xDE, 0xA6, - 0x0B, 0x33, 0xDE, 0x7E, 0x10, 0x1F, 0xF9, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, - 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC4, 0x45, 0x92, 0xBD, 0x70, 0x0D, 0x6F, 0xC2, - 0x02, 0x00, 0x00, 0x53, 0xEB, 0xFC, 0xFE, 0x6B, 0x06, 0xB3, 0xDD, 0x7E, 0xCC, 0x90, 0xE1, 0xE1, - 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, - 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x8B, 0xA4, 0x7B, 0xFE, 0x0A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xA6, - 0xD6, 0xF9, 0xDD, 0x8B, 0x47, 0xA3, 0xB7, 0x1F, 0xD5, 0x00, 0x89, 0xC5, 0x47, 0xDD, 0xED, 0xC7, - 0xDD, 0xC4, 0x47, 0xFB, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, - 0xE4, 0xE1, 0xBF, 0xD3, 0x13, 0x51, 0x91, 0x72, 0xDE, 0xB1, 0xB5, 0xBC, 0x09, 0x0B, 0x00, 0x00, - 0x4C, 0x1D, 0x6F, 0xF7, 0x42, 0x79, 0xFB, 0x31, 0xE1, 0x02, 0xE4, 0xD1, 0xC8, 0xED, 0x47, 0x18, - 0x1F, 0x6E, 0xF7, 0xEB, 0x6F, 0xBD, 0x22, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x07, 0x45, 0xDA, 0x79, 0x47, 0x3E, - 0x3F, 0x1A, 0xFC, 0x71, 0x00, 0x00, 0x00, 0x50, 0x2C, 0x33, 0xD2, 0x3F, 0xDB, 0xDB, 0x31, 0x27, - 0xDD, 0xB3, 0x1F, 0x0F, 0xCE, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, - 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x98, 0xC8, 0xB4, 0xA3, - 0x6B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x1A, 0x66, 0xFF, 0xD2, 0x81, 0xF3, 0x5B, 0x3E, 0x6C, - 0x03, 0x24, 0xF2, 0xAD, 0x57, 0xCD, 0x6E, 0x3F, 0x1E, 0xB2, 0xF1, 0x21, 0x6E, 0x3F, 0x88, 0x8F, - 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, - 0xD3, 0x13, 0x11, 0xD1, 0xCE, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0x73, 0x76, 0xF0, 0xC7, 0x02, 0x00, - 0x00, 0x40, 0x71, 0xCE, 0xED, 0x5F, 0x7A, 0xD7, 0xF9, 0x4D, 0xEF, 0x9B, 0x7C, 0xF6, 0xA3, 0xE6, - 0xC1, 0xF3, 0x19, 0xB5, 0xCF, 0x7E, 0xB8, 0xF8, 0x78, 0xA0, 0xFE, 0xF6, 0x83, 0xF8, 0x48, 0x33, - 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, - 0x11, 0x10, 0xED, 0xCE, 0xBD, 0x09, 0xEB, 0xD8, 0x6D, 0x03, 0xC1, 0x1F, 0x0B, 0x00, 0x00, 0x00, - 0xC5, 0x39, 0x7F, 0x60, 0xD9, 0x86, 0x9A, 0x07, 0xCF, 0xA3, 0xB7, 0x1F, 0xD1, 0xF8, 0x78, 0xD8, - 0x86, 0x87, 0x0B, 0x90, 0xEF, 0xD4, 0xDE, 0x7E, 0x10, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x21, 0x8F, 0xF1, - 0x26, 0x2C, 0x00, 0x00, 0x30, 0x55, 0xCE, 0xEF, 0x5E, 0x32, 0xEA, 0xC7, 0x47, 0x83, 0xDB, 0x0F, - 0x17, 0x1F, 0xA7, 0xC2, 0xF8, 0x70, 0xB7, 0x1F, 0x7F, 0x4D, 0x7C, 0x64, 0x9B, 0x0A, 0x87, 0x3C, - 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x87, 0x1C, - 0xE7, 0x9D, 0xB8, 0x6D, 0x30, 0xF8, 0x63, 0x01, 0x00, 0x00, 0xA0, 0x38, 0xE7, 0x76, 0x2E, 0x3A, - 0x59, 0x73, 0xFB, 0x11, 0xC4, 0x47, 0xF5, 0xD9, 0x8F, 0x30, 0x3E, 0x62, 0xB7, 0x1F, 0xC4, 0x47, - 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, - 0xE9, 0xE9, 0x68, 0xC8, 0x73, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0xDE, 0xF9, - 0xED, 0x73, 0xAA, 0xF1, 0x51, 0xF7, 0xA1, 0x83, 0x8F, 0xCC, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, - 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, - 0xE9, 0x60, 0xC8, 0x7D, 0x47, 0x3F, 0xCF, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB1, 0xCE, 0xEE, 0x5F, - 0x3A, 0x70, 0x6E, 0xCB, 0x27, 0x63, 0xB7, 0x1F, 0x33, 0x2B, 0xCF, 0x7E, 0x44, 0xE3, 0xC3, 0xED, - 0xDE, 0xCA, 0xED, 0x07, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, - 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x14, 0x8A, 0x9A, 0x7B, 0x13, 0xD6, 0xD8, 0x17, - 0x67, 0x05, 0x7F, 0x3C, 0x00, 0x00, 0x00, 0xE4, 0xEF, 0xDC, 0xDE, 0xE5, 0xEB, 0xCE, 0x6D, 0xFA, - 0x48, 0xEC, 0xF6, 0x63, 0x46, 0x35, 0x3E, 0xAA, 0x01, 0xF2, 0x23, 0x3B, 0xE2, 0x23, 0xE5, 0x54, - 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x44, - 0x24, 0x14, 0xB9, 0x17, 0xAF, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x58, 0xE7, 0xF7, 0x2E, 0x5F, - 0x5F, 0xF7, 0xEC, 0xC7, 0x8F, 0x27, 0x03, 0xA4, 0x7A, 0xFB, 0xF1, 0xED, 0x0B, 0x88, 0x8F, 0x54, - 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, - 0x13, 0x81, 0x50, 0xF4, 0x5E, 0xB8, 0xCA, 0x78, 0xBF, 0xB8, 0xFD, 0xAE, 0xE0, 0x8F, 0x07, 0x00, - 0x00, 0x80, 0xFC, 0x9D, 0xDB, 0x7D, 0xD5, 0x50, 0xCD, 0x9B, 0xAF, 0xC2, 0xF8, 0x88, 0x06, 0xC8, - 0x8F, 0x66, 0x10, 0x1F, 0xA9, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x60, 0x8A, 0xE6, 0x1D, 0xBF, 0x6D, 0x43, 0xF0, 0xC7, - 0x03, 0x00, 0x00, 0x40, 0xFE, 0xCE, 0xED, 0xB8, 0x6C, 0x6C, 0xF2, 0xF6, 0xE3, 0x02, 0x79, 0xFB, - 0xF1, 0x76, 0xE4, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x06, 0x53, 0x35, 0xDE, 0x84, 0x05, 0x00, - 0x00, 0x0A, 0x75, 0x76, 0xDB, 0xFC, 0xE0, 0xF6, 0xC3, 0xC6, 0x87, 0x78, 0xF6, 0xE3, 0xED, 0x1F, - 0x4C, 0xDE, 0x7E, 0x10, 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, - 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0xA3, 0x60, 0x2A, 0xE7, 0x1D, 0xBD, 0x79, 0x3C, 0xF8, - 0xE3, 0x01, 0x00, 0x00, 0x20, 0x5F, 0x66, 0xA8, 0x7F, 0xF6, 0xD9, 0xCD, 0x97, 0x04, 0xF1, 0x51, - 0x7B, 0xFB, 0x71, 0x2A, 0xBC, 0xFD, 0xF8, 0x66, 0xE5, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, - 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x04, - 0x53, 0xBE, 0x97, 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x8C, 0xB3, 0x43, 0x4B, 0x07, 0xDE, - 0xDD, 0xF8, 0xE1, 0x6A, 0x80, 0x9C, 0xB6, 0x01, 0x52, 0xF3, 0xEC, 0xC7, 0xF7, 0x2B, 0xB7, 0x1F, - 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, - 0xF0, 0xDF, 0xE9, 0x89, 0x10, 0xE8, 0xD4, 0xC6, 0x96, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x0C, - 0x1B, 0x20, 0xEB, 0x27, 0x06, 0xDF, 0x57, 0x73, 0xFB, 0x11, 0x7D, 0xFE, 0xC3, 0xDD, 0x7E, 0x10, - 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, - 0x7F, 0xA7, 0x27, 0x22, 0xA0, 0x93, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0x63, - 0x02, 0x00, 0x00, 0x20, 0x3F, 0xEF, 0xEE, 0x59, 0x3A, 0xE8, 0xDF, 0x7E, 0x88, 0xF8, 0x70, 0xB7, - 0x1F, 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, - 0xF2, 0xF0, 0xDF, 0xE9, 0x89, 0x00, 0xE8, 0xF4, 0xDC, 0x3F, 0x17, 0x27, 0x6E, 0x5B, 0x1F, 0xFC, - 0x31, 0x01, 0x00, 0x00, 0x90, 0x9F, 0x89, 0x6D, 0x8B, 0x47, 0xEB, 0xDE, 0x7C, 0x15, 0xDE, 0x7E, - 0xDC, 0x4D, 0x7C, 0x34, 0x9F, 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xE9, 0x65, - 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xE2, 0xF0, 0xDF, 0x25, 0xF3, 0x8E, 0xAF, 0xE5, 0x4D, 0x58, 0x00, - 0x00, 0x20, 0x7F, 0x13, 0x5B, 0xFA, 0xC6, 0xE5, 0xC3, 0xE7, 0xF7, 0xBB, 0x6F, 0xBD, 0x22, 0x3E, - 0x1A, 0x4F, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, - 0x4E, 0x4F, 0x1F, 0xFC, 0xBB, 0x65, 0xDE, 0xD1, 0x9B, 0x4F, 0x06, 0x7F, 0x4C, 0x00, 0x00, 0x00, - 0xE4, 0xC3, 0x0C, 0xF5, 0xCF, 0x7A, 0xF7, 0x99, 0x39, 0x95, 0xF8, 0xB0, 0x8B, 0x3E, 0x7C, 0xFE, - 0xD6, 0x5F, 0x11, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFD, 0x5D, 0xB5, 0x97, 0x56, 0x9A, 0xE0, 0x8F, 0x0A, - 0x00, 0x00, 0x80, 0x7C, 0xB8, 0x37, 0x60, 0x4D, 0x6C, 0xFC, 0x98, 0x0D, 0x90, 0x0B, 0xCC, 0xE9, - 0x47, 0xEC, 0xDC, 0xED, 0x87, 0x0B, 0x90, 0xFB, 0x6D, 0x80, 0x10, 0x1F, 0x0D, 0xA6, 0xC2, 0x21, - 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xFB, - 0xDD, 0x38, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x7C, 0xDD, 0xC4, 0x13, 0x1F, - 0x30, 0xA7, 0xFD, 0x00, 0x09, 0xBE, 0xFD, 0xEA, 0xE1, 0x99, 0xFE, 0xED, 0x07, 0xF1, 0xA1, 0xA6, - 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, - 0x0E, 0xFA, 0xDD, 0x3A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x74, 0x7D, 0x78, - 0xFB, 0x71, 0xCA, 0xCD, 0xDD, 0x7E, 0x7C, 0x67, 0x06, 0xF1, 0x21, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF9, 0xDD, 0x3C, - 0xF7, 0xCF, 0x3D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x79, 0x9A, 0xD8, 0x79, 0xD5, 0x68, 0xF5, 0xDB, - 0xAF, 0xDC, 0x0D, 0x88, 0x7B, 0xF6, 0xE3, 0x6B, 0xC4, 0x47, 0xFD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, 0x01, 0xBF, 0x07, 0xC6, - 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xAE, 0x26, 0xB6, 0x5D, 0x76, 0xB2, 0x7A, 0x03, 0xF2, 0xF0, 0x0C, - 0xF3, 0xF6, 0x7D, 0xC1, 0xED, 0x87, 0x8D, 0x8F, 0x3F, 0x10, 0x1F, 0xC1, 0x54, 0x38, 0xE4, 0x31, - 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0xE1, 0xBE, 0x17, - 0xE6, 0x1D, 0x59, 0x33, 0x16, 0xFC, 0x71, 0x01, 0x00, 0x00, 0xD0, 0xBE, 0x09, 0xFF, 0x0D, 0x58, - 0x93, 0xB7, 0x1F, 0x6F, 0x7E, 0x6D, 0xF2, 0xE6, 0xE3, 0x0D, 0xE2, 0xC3, 0x4E, 0x85, 0x43, 0x1E, - 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xEC, 0x7B, - 0x66, 0x47, 0x6E, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x20, 0x1F, 0x13, 0x43, 0xD7, 0x2F, 0x99, 0x78, - 0xFA, 0x13, 0xE6, 0xCC, 0xA3, 0x95, 0xCF, 0xFF, 0x70, 0xB7, 0x1F, 0x95, 0xF8, 0xB8, 0xC0, 0xBF, - 0xF9, 0x78, 0x83, 0xF8, 0x28, 0x68, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, - 0xDF, 0xE9, 0x89, 0x03, 0x7D, 0xAF, 0xED, 0xA5, 0x7E, 0xF7, 0x20, 0xFA, 0x92, 0xE0, 0x8F, 0x0D, - 0x00, 0x00, 0x80, 0xEC, 0xCE, 0x0C, 0x2D, 0x5B, 0x3B, 0xF1, 0xE4, 0x87, 0x2B, 0x9F, 0x01, 0xF2, - 0x60, 0xE5, 0xB5, 0xBB, 0xD5, 0xDB, 0x8F, 0xAF, 0xD9, 0xF8, 0xF8, 0x47, 0xE2, 0x23, 0xFF, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF3, 0xBD, 0x38, - 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x03, 0x00, 0x00, 0x20, 0x3B, 0xF7, 0x06, - 0xAC, 0x89, 0xC7, 0x2F, 0xF2, 0x03, 0xE4, 0xED, 0x6F, 0x55, 0x9E, 0xFB, 0x20, 0x3E, 0xDC, 0x54, - 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, - 0x41, 0xBE, 0x57, 0xE7, 0xBE, 0x26, 0x78, 0x13, 0x16, 0x00, 0x00, 0xC8, 0xC3, 0xD9, 0x5D, 0xD7, - 0x0C, 0x4E, 0x3C, 0x36, 0xC3, 0x9C, 0x71, 0xCF, 0x7E, 0x04, 0x6F, 0xBD, 0xFA, 0xC3, 0x7A, 0xE2, - 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, - 0xF1, 0x3D, 0xBB, 0xF9, 0xFE, 0xBC, 0xE3, 0xB7, 0x0D, 0x05, 0x7F, 0x6C, 0x00, 0x00, 0x00, 0x64, - 0xF7, 0xEE, 0xB6, 0xC5, 0xA3, 0x13, 0x8F, 0xCF, 0x30, 0xEF, 0x7C, 0xBB, 0x72, 0xFB, 0xF1, 0xE6, - 0x5F, 0x5D, 0x44, 0x7C, 0x14, 0x32, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, - 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x57, 0x89, 0x0F, 0x3F, 0x40, 0x8E, 0xDC, 0xCC, 0x9B, 0xB0, - 0x00, 0x00, 0x40, 0xFB, 0xCE, 0x6D, 0x5F, 0x60, 0x26, 0x1E, 0xB9, 0xC0, 0xBC, 0xE5, 0x5E, 0xBB, - 0xFB, 0xD7, 0xC4, 0x47, 0x31, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, - 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0xE1, 0xEF, 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, - 0x00, 0xB4, 0xC7, 0x0C, 0x5D, 0x3B, 0xFB, 0xDC, 0xB6, 0x4B, 0xCD, 0x3B, 0xDF, 0xB4, 0x01, 0xF2, - 0x75, 0xE2, 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, - 0xA7, 0x0E, 0xF1, 0xBD, 0xBA, 0x58, 0x7C, 0xB8, 0xBD, 0xB4, 0xC2, 0x98, 0x97, 0xBF, 0x38, 0x3B, - 0xF8, 0xE3, 0x03, 0x00, 0x00, 0x20, 0xBD, 0xB3, 0xFB, 0x97, 0x0E, 0xBC, 0xFB, 0xD8, 0x87, 0xCC, - 0xDB, 0x77, 0x5F, 0x64, 0xCE, 0x13, 0x1F, 0x05, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x89, 0xF8, 0x70, 0x7B, 0xE1, 0x6A, 0x63, - 0x8E, 0xDD, 0x36, 0x10, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDE, 0x99, 0x5D, 0x9F, 0xBB, 0xEB, - 0xD4, 0x86, 0x0F, 0x98, 0xF3, 0xFF, 0x44, 0x7C, 0xE4, 0x3F, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, - 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x27, 0xC2, 0x23, 0x98, 0xF7, 0xAF, - 0x4B, 0xCC, 0xE9, 0x9F, 0xDD, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x76, 0xAF, 0x3D, 0x70, 0xED, - 0x86, 0xF1, 0xDD, 0xAB, 0xCD, 0xD9, 0x17, 0xBF, 0x44, 0x7C, 0xE4, 0x3A, 0x15, 0x09, 0x45, 0x4D, - 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0xA7, 0xC3, 0xC3, 0xED, - 0xD4, 0xB3, 0xF3, 0xCC, 0xEF, 0x77, 0xCC, 0x31, 0xBF, 0xDB, 0x77, 0xD3, 0x60, 0xF0, 0xC7, 0x07, - 0x00, 0x00, 0x40, 0x7A, 0xAF, 0xEF, 0xB9, 0x75, 0xF8, 0xF7, 0x07, 0x6F, 0x35, 0xBF, 0xDF, 0xB7, - 0xD2, 0xBC, 0x79, 0xE0, 0x32, 0x73, 0xFE, 0x9F, 0x55, 0x54, 0x24, 0x9D, 0x08, 0x87, 0x3C, 0x26, - 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, - 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0x4E, 0x87, 0xC7, 0x99, 0xBF, 0xEB, 0x33, 0xE3, 0xBB, - 0xE6, 0x9A, 0xDF, 0x6D, 0xB3, 0xF1, 0xE1, 0xB6, 0x67, 0x39, 0xAF, 0xE2, 0x05, 0x00, 0x00, 0xD9, - 0xFD, 0xF3, 0xFF, 0xFE, 0x17, 0x17, 0xFE, 0x76, 0xFB, 0xEA, 0xFB, 0x7E, 0xB7, 0x6D, 0xF1, 0x5B, - 0xBF, 0xDF, 0xB9, 0xD8, 0xB8, 0xBD, 0x7D, 0xE8, 0x72, 0xFF, 0x5B, 0x2D, 0x74, 0x64, 0x34, 0x9A, - 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0xAE, 0x3E, 0x3C, 0xCE, 0xFE, - 0x43, 0x9F, 0x79, 0x73, 0xEF, 0x64, 0x78, 0xBC, 0xBE, 0x75, 0xCE, 0xF8, 0xEF, 0xB6, 0x5D, 0xBA, - 0x7E, 0xB4, 0xFF, 0x3D, 0x7F, 0x14, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDD, 0x7F, 0xBF, 0xFB, - 0x33, 0x17, 0xBF, 0xB6, 0x79, 0xF1, 0x96, 0x30, 0x42, 0xC6, 0x77, 0x2F, 0x36, 0xA7, 0xFF, 0xC6, - 0x1E, 0xD4, 0x65, 0x6C, 0xC4, 0x27, 0xC2, 0x21, 0x8F, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, 0xDF, - 0xAB, 0xAB, 0x0D, 0x8F, 0xF3, 0xFF, 0x34, 0xDF, 0xBC, 0xB5, 0x3F, 0x72, 0xE3, 0x61, 0xF7, 0xEA, - 0x33, 0x73, 0x7E, 0x34, 0x3E, 0xF4, 0xA9, 0x59, 0xC1, 0x1F, 0x17, 0x00, 0x00, 0x00, 0xF9, 0xF9, - 0xEF, 0xDF, 0x5D, 0xF2, 0x99, 0xD7, 0xB6, 0x2C, 0xFA, 0x59, 0x18, 0x22, 0x6F, 0xEC, 0xB9, 0xCC, - 0x4C, 0xFC, 0xBD, 0x0B, 0x02, 0x15, 0x1E, 0x6E, 0xB1, 0x68, 0xC8, 0x6B, 0x32, 0x1C, 0xF2, 0x98, - 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, - 0x3A, 0xC4, 0xF7, 0xEA, 0x26, 0xC3, 0xC3, 0xFB, 0xE7, 0xF9, 0xE6, 0x9D, 0x91, 0x79, 0x35, 0xE1, - 0xF1, 0xDA, 0xE6, 0x4B, 0xF7, 0x8F, 0x6F, 0xE9, 0xE3, 0xB5, 0xBB, 0x00, 0x00, 0xA0, 0x78, 0xFF, - 0xEB, 0xA1, 0xCB, 0xBE, 0xF8, 0xBB, 0x6D, 0x8B, 0x7E, 0x19, 0x86, 0x88, 0x7B, 0x3E, 0xE4, 0xEC, - 0x4F, 0x5D, 0x1C, 0x10, 0x1F, 0xF5, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, - 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0x51, 0x7D, 0xC0, 0x3C, 0x08, 0x8F, 0xD7, - 0xB7, 0x5E, 0x3A, 0xF6, 0xDA, 0x96, 0x4B, 0x79, 0xDD, 0x2E, 0x00, 0x00, 0x98, 0x5A, 0xA3, 0xFD, - 0xFD, 0x7F, 0xF4, 0xAB, 0x27, 0x2E, 0xFB, 0x3F, 0xA2, 0xCF, 0x87, 0xBC, 0x35, 0xEC, 0x1E, 0x54, - 0x17, 0xD1, 0x90, 0xD7, 0x64, 0x38, 0xE4, 0x31, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x55, 0xC2, 0x23, 0xFE, - 0x80, 0xB9, 0x8D, 0x8E, 0x5F, 0xBE, 0xBE, 0x7D, 0xEE, 0xBA, 0xE0, 0x8F, 0x00, 0x00, 0x00, 0x80, - 0xCE, 0xF8, 0xD5, 0x23, 0x57, 0x7E, 0xE0, 0x37, 0x4F, 0x2E, 0xDE, 0x30, 0x19, 0x22, 0x8B, 0xCC, - 0xE9, 0xBF, 0xB9, 0xCC, 0x78, 0xFF, 0x2A, 0x02, 0xA2, 0x9D, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, - 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, - 0xDF, 0xAB, 0x6B, 0xFC, 0x80, 0x39, 0xCF, 0x79, 0x00, 0x00, 0x80, 0xAE, 0x72, 0xF2, 0x91, 0xC5, - 0xF3, 0x5F, 0x7D, 0x66, 0xF1, 0x0E, 0x17, 0x20, 0x6E, 0xE3, 0xBB, 0x17, 0x99, 0x33, 0x7F, 0x67, - 0x0F, 0xE2, 0x2A, 0x26, 0xD2, 0x4E, 0x86, 0x43, 0x1E, 0x8B, 0x47, 0x43, 0x5E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x73, 0xEA, 0x01, - 0xF3, 0xDF, 0x6D, 0xBD, 0x74, 0xF0, 0xBF, 0x7D, 0x67, 0xDE, 0x07, 0x82, 0x2F, 0x73, 0x00, 0x00, - 0x80, 0xEE, 0xF3, 0xAB, 0x8D, 0x8B, 0xD6, 0xBC, 0xFA, 0xCC, 0xC2, 0x9F, 0x87, 0x21, 0xF2, 0xC6, - 0x9E, 0xC5, 0xE6, 0xDD, 0x7F, 0xB0, 0x87, 0x7D, 0x15, 0x16, 0x49, 0x26, 0xC3, 0x21, 0x8F, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, - 0x83, 0x7C, 0xAF, 0xCD, 0xFB, 0xE7, 0x05, 0x75, 0x0F, 0x98, 0xDB, 0x8D, 0xFE, 0x7F, 0x8F, 0x5E, - 0x3A, 0x27, 0xF8, 0xB2, 0x06, 0x00, 0x00, 0xE8, 0x6E, 0xFE, 0xF3, 0x21, 0x83, 0x8B, 0xFF, 0xE2, - 0x77, 0xDB, 0x16, 0xFE, 0x32, 0x0C, 0x91, 0x37, 0x0F, 0x2C, 0x36, 0x67, 0x7F, 0x6A, 0x0F, 0xFE, - 0x2A, 0x32, 0x1A, 0x4D, 0x86, 0x43, 0x1E, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, - 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x87, 0xF9, 0x5E, 0xDB, 0xE9, 0xBF, 0xE9, 0xAB, - 0x79, 0xC0, 0xFC, 0xB5, 0x67, 0xE6, 0x1C, 0xE3, 0x01, 0x73, 0x00, 0x00, 0xD0, 0xB3, 0x6C, 0x88, - 0xFC, 0xBB, 0x57, 0x37, 0x2F, 0xBA, 0xE7, 0x77, 0xDB, 0x16, 0xBD, 0x15, 0x86, 0xC8, 0xDB, 0x87, - 0x12, 0x3E, 0xA8, 0x2E, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xC3, 0x7C, 0x2F, 0x6D, 0xE2, 0xEF, 0xE7, 0xF3, - 0x80, 0x39, 0x00, 0x00, 0x28, 0xAF, 0x93, 0xF7, 0x2E, 0xFC, 0xC8, 0x2B, 0x9B, 0x16, 0x3F, 0x1A, - 0x46, 0x88, 0x7B, 0x3E, 0xA4, 0xE9, 0x83, 0xEA, 0x32, 0x1C, 0xF2, 0x98, 0x0A, 0x87, 0x3C, 0xA6, - 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x3E, 0xD0, 0xF7, 0xCA, - 0xCE, 0xFE, 0xC3, 0x7C, 0xF3, 0xE6, 0x5E, 0xF7, 0xED, 0x56, 0x95, 0xF8, 0xE0, 0x01, 0x73, 0x00, - 0x00, 0x50, 0x6A, 0xFF, 0xE3, 0xFE, 0xCF, 0x2C, 0x7A, 0x75, 0xF3, 0xA2, 0xE7, 0xA2, 0x21, 0x32, - 0xF1, 0xF7, 0x36, 0x0C, 0x88, 0x8F, 0x84, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, - 0x7D, 0xA8, 0xEF, 0x85, 0x9D, 0xFF, 0xA7, 0x05, 0xE6, 0xED, 0x83, 0x7D, 0x41, 0x78, 0x04, 0x37, - 0x1F, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0xA6, 0x8B, 0x5F, 0x3E, 0xB6, 0xF8, 0x96, 0xD7, 0xB7, 0x2E, - 0x7C, 0x39, 0x0C, 0x11, 0xF7, 0xA0, 0xBA, 0xFF, 0x7C, 0x88, 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, - 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xF6, - 0xDD, 0x3E, 0xF7, 0x80, 0xF9, 0xA9, 0x67, 0xA3, 0xE1, 0xE1, 0xC7, 0x07, 0x0F, 0x98, 0x03, 0x00, - 0x80, 0xE9, 0xC7, 0x3D, 0xA8, 0xFE, 0xDB, 0xA7, 0x17, 0x7F, 0xF5, 0xF5, 0xAD, 0x8B, 0x7E, 0xE3, - 0x22, 0xE4, 0x77, 0x3B, 0x16, 0x9A, 0x37, 0xF7, 0x2F, 0x32, 0xE7, 0xFE, 0xD1, 0x1D, 0xEC, 0x55, - 0x44, 0x64, 0x5D, 0x3C, 0x1A, 0xF2, 0x9A, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, - 0xFC, 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xED, 0xAB, 0x3C, 0x60, 0x3E, 0x19, 0x1E, 0xAF, 0x3D, 0x33, - 0x97, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x7E, 0xF5, 0xC8, 0xBC, 0x0F, 0xFC, 0xE6, 0xE9, 0x85, 0xF7, - 0xBD, 0xBE, 0x75, 0xC1, 0x5B, 0x2E, 0x42, 0xDC, 0xDE, 0x1E, 0x59, 0x6C, 0xBC, 0x7F, 0x51, 0x31, - 0x91, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, - 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xCD, 0xAB, 0x3C, 0x60, 0x1E, 0x3E, 0xE7, 0x61, 0xC3, 0x63, 0xCB, - 0x5C, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x88, 0x1B, 0xFB, 0x4F, 0x9F, 0xB9, 0xF8, 0x95, 0xCD, 0x0B, - 0xB7, 0x84, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCD, 0xA9, 0xE7, 0xDA, 0x09, 0x11, 0x15, 0x0E, 0x79, - 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC0, 0xEF, - 0xD6, 0xD5, 0x3E, 0x60, 0x3E, 0xD7, 0xBC, 0xBE, 0x75, 0xEE, 0xB8, 0xFD, 0x91, 0x07, 0xCC, 0x01, - 0x00, 0x00, 0x9A, 0xF9, 0x9F, 0x0F, 0x2D, 0x5A, 0xFA, 0xEA, 0xE6, 0x85, 0x3F, 0x0B, 0x43, 0x64, - 0x7C, 0x68, 0x91, 0x39, 0x33, 0xEA, 0x0E, 0xFE, 0x2A, 0x32, 0x1A, 0x2D, 0x1E, 0x0D, 0x79, 0x4D, - 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC8, 0xEF, 0xC6, - 0xD5, 0x3F, 0x60, 0x6E, 0xB7, 0x75, 0x0E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA4, 0xF1, 0xBF, 0x1E, - 0x5A, 0xF0, 0xC5, 0xD7, 0xB6, 0x2E, 0xF8, 0x65, 0x18, 0x22, 0x6F, 0xEC, 0x5D, 0x64, 0xCE, 0xFE, - 0xC4, 0x45, 0x80, 0x0A, 0x8E, 0xE8, 0xE2, 0xD1, 0x90, 0xD7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, - 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x07, 0xFD, 0x6E, 0x9B, 0x7E, 0xC0, 0x7C, 0x2E, 0x0F, - 0x98, 0x03, 0x00, 0x00, 0x64, 0xE5, 0x1E, 0x54, 0xFF, 0xF5, 0xC6, 0x45, 0x7F, 0x19, 0x7D, 0x3E, - 0xC4, 0x3D, 0xA8, 0x7E, 0xFE, 0x67, 0x2A, 0x3C, 0xDC, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0x61, 0xBF, 0xDB, 0xC6, 0x03, 0xE6, - 0x00, 0x00, 0x00, 0x05, 0x72, 0x0F, 0xAA, 0xFF, 0xFA, 0xC9, 0x85, 0x1B, 0xC2, 0x08, 0x71, 0x7B, - 0xE7, 0x70, 0xFC, 0xF9, 0x10, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xD8, 0xEF, 0xA6, 0xF1, 0x80, 0x39, 0x00, 0x00, 0xC0, 0x14, - 0x3A, 0xF9, 0xC8, 0xE2, 0xF9, 0xBF, 0xDD, 0xB4, 0x60, 0x47, 0x18, 0x21, 0xE1, 0x83, 0xEA, 0x3A, - 0x1C, 0xF2, 0x58, 0x3C, 0x10, 0x8A, 0x9C, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, - 0x03, 0x7F, 0xB7, 0x8C, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x3A, 0xE8, 0x57, 0x1B, 0x17, 0xAD, 0x79, - 0x65, 0xD3, 0x82, 0x9F, 0x57, 0x42, 0x64, 0x81, 0x19, 0x1F, 0x5A, 0x68, 0xCE, 0x8C, 0xBA, 0x43, - 0xBC, 0x8A, 0x88, 0xAC, 0x8B, 0x07, 0x42, 0x91, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, - 0x3D, 0x7D, 0xE8, 0xEF, 0x86, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x40, 0x97, 0xA8, 0x7C, 0x90, 0xE1, - 0xC2, 0xAF, 0xBE, 0xB6, 0x75, 0xFE, 0x2F, 0x5D, 0x84, 0xB8, 0xBD, 0xB9, 0x7F, 0xA1, 0x39, 0xFB, - 0x13, 0x77, 0xA0, 0x57, 0x41, 0x91, 0x66, 0xF1, 0x40, 0x28, 0x72, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, - 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFE, 0x9D, 0x5E, 0xA3, 0x07, 0xCC, 0x4F, 0x7E, 0xE7, 0x53, 0xF3, - 0x83, 0x2F, 0x01, 0x00, 0x00, 0x00, 0x74, 0xC2, 0x60, 0xFF, 0xA7, 0xFE, 0xDD, 0xAB, 0x9B, 0x16, - 0xDC, 0xF3, 0xFA, 0xD6, 0xF9, 0x6F, 0x85, 0x21, 0xF2, 0xF6, 0x88, 0x7B, 0x50, 0x5D, 0x85, 0x45, - 0x92, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFF, - 0x9D, 0x9E, 0x7A, 0xC0, 0xFC, 0xB7, 0x1B, 0xE7, 0xDE, 0x10, 0xFC, 0x23, 0x0F, 0x00, 0x00, 0x80, - 0x6E, 0xF0, 0xFF, 0x7E, 0x63, 0xF1, 0x87, 0x7F, 0xF3, 0xD4, 0x82, 0x47, 0xC3, 0x08, 0x71, 0x3B, - 0xF5, 0xDC, 0x22, 0xE3, 0xFD, 0x8B, 0x8A, 0x8C, 0x46, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, - 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xFE, 0x3B, 0xB9, 0xF8, 0x03, 0xE6, 0xEE, 0x39, 0x8F, - 0x57, 0x9F, 0xBE, 0xF4, 0x3F, 0x06, 0xFF, 0x88, 0x03, 0x00, 0x00, 0xA0, 0x1B, 0xFD, 0x8F, 0xFB, - 0x17, 0x2C, 0x7A, 0x75, 0xF3, 0x82, 0x9F, 0x85, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCC, 0xE9, 0xBF, - 0x75, 0x07, 0x61, 0x15, 0x1C, 0xD1, 0xA9, 0x48, 0x28, 0x6A, 0xB1, 0x03, 0x7A, 0x99, 0x27, 0x0F, - 0xFF, 0x9D, 0x9E, 0x0E, 0x80, 0x4E, 0xAD, 0xEE, 0x01, 0xF3, 0xCD, 0x73, 0xDE, 0xB2, 0x3F, 0xF2, - 0x80, 0x39, 0x00, 0x00, 0x40, 0x2F, 0xF9, 0xE5, 0xC3, 0xF3, 0x6F, 0x79, 0x75, 0xCB, 0x82, 0x97, - 0xC3, 0x10, 0x71, 0x0F, 0xAA, 0x4F, 0xFC, 0x57, 0x77, 0xF8, 0x27, 0x3E, 0xA6, 0x6C, 0xF2, 0xF0, - 0xDF, 0xE9, 0xE9, 0x08, 0xE8, 0xC4, 0x1A, 0x3D, 0x60, 0x3E, 0xBE, 0xA5, 0x6F, 0x76, 0xF0, 0x8F, - 0x31, 0x00, 0x00, 0x00, 0x7A, 0x49, 0xF8, 0xA0, 0x7A, 0xF4, 0xF9, 0x10, 0xF7, 0xA0, 0xFA, 0xB9, - 0x7F, 0x74, 0x21, 0x40, 0x7C, 0x14, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x74, 0x08, 0x4C, 0xF5, 0x1A, - 0x3D, 0x60, 0xFE, 0xEB, 0xC7, 0x3E, 0x7D, 0x55, 0xF0, 0x8F, 0x2E, 0x00, 0x00, 0x00, 0x7A, 0x99, - 0xFB, 0x20, 0xC3, 0xDF, 0x6C, 0x9C, 0x7F, 0x5F, 0xED, 0x83, 0xEA, 0x0B, 0xCD, 0xF9, 0x9F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x63, 0x60, 0xAA, 0x17, - 0x7F, 0xC0, 0xFC, 0xF5, 0x6D, 0x73, 0x4E, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x40, 0x49, 0xFD, 0xDB, - 0x83, 0x7D, 0x7D, 0xAF, 0x6C, 0x5E, 0xB0, 0xA5, 0x12, 0x21, 0xF3, 0xCD, 0xEF, 0x77, 0xCD, 0x37, - 0xA7, 0x9E, 0x5B, 0x68, 0xBC, 0x7F, 0x51, 0xC1, 0x90, 0xE7, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, - 0xFF, 0x4E, 0x4F, 0xC7, 0xC0, 0x54, 0x8E, 0x07, 0xCC, 0x01, 0x00, 0x00, 0xA6, 0xB1, 0xFF, 0xF9, - 0xD0, 0xBC, 0xA5, 0xAF, 0x6E, 0xEE, 0xFB, 0x99, 0x8B, 0x90, 0x30, 0x44, 0xCE, 0x8C, 0xBA, 0x03, - 0xB4, 0x8A, 0x87, 0x76, 0x17, 0x3B, 0xA0, 0x97, 0x79, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x20, 0x98, - 0xAA, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xAA, 0x7E, 0xF5, 0x58, 0xDF, 0x5F, 0x54, 0x3E, 0xC8, - 0xB0, 0x12, 0x22, 0xE3, 0x43, 0xF6, 0xC0, 0xF8, 0x13, 0x77, 0x98, 0x56, 0x21, 0x91, 0x65, 0xB1, - 0x03, 0x7A, 0x99, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x82, 0xA9, 0x18, 0x0F, 0x98, 0x03, 0x00, - 0x00, 0x40, 0x72, 0x0F, 0xAA, 0xFF, 0x7A, 0x63, 0xDF, 0x5F, 0xBE, 0xBE, 0xB5, 0xEF, 0xAD, 0x30, - 0x44, 0xDE, 0xDC, 0x6F, 0x0F, 0x90, 0x6D, 0x3F, 0x1F, 0x22, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, - 0x77, 0x7A, 0x3A, 0x0C, 0x8A, 0x1E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x20, 0x11, 0xFF, 0x41, 0xF5, - 0xA7, 0xFA, 0x1E, 0x0D, 0x23, 0xC4, 0xCD, 0x3D, 0xA8, 0x9E, 0xED, 0xF9, 0x10, 0x71, 0x48, 0x2F, - 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x71, 0x50, 0xF4, 0xCE, 0xFC, 0xDD, 0x7C, 0x1E, 0x30, 0x07, - 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x41, 0xF5, 0x57, 0x9F, 0x99, 0x3F, 0x12, 0x46, 0x48, 0xFA, 0x07, - 0xD5, 0xC5, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x41, 0x91, 0x73, 0xCF, 0x79, - 0xF0, 0x80, 0x39, 0x00, 0x00, 0x00, 0xDA, 0xF2, 0xAB, 0x27, 0xFA, 0xD6, 0x44, 0x1F, 0x54, 0x77, - 0xCF, 0x87, 0xB4, 0x7E, 0x50, 0x3D, 0x76, 0x40, 0x2F, 0xF3, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x81, - 0x50, 0xD4, 0xCE, 0xFD, 0x94, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x90, 0xA3, 0xCA, 0x07, 0x19, 0xF6, - 0x7D, 0x35, 0xFA, 0xA0, 0xBA, 0x7B, 0x3E, 0x44, 0x3F, 0xA8, 0x1E, 0x3B, 0xA0, 0x97, 0x79, 0xF2, - 0xF0, 0xDF, 0xE9, 0xE9, 0x48, 0x28, 0x62, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x42, 0xB9, 0xE7, - 0x43, 0x5E, 0xDD, 0xD4, 0x77, 0x4F, 0xF4, 0x41, 0xF5, 0xB7, 0x0E, 0x46, 0x1F, 0x54, 0x17, 0x87, - 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0A, 0x79, 0x2F, 0x7C, 0xC0, 0x3C, 0xFA, 0x9C, - 0x87, 0x1D, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA0, 0x18, 0x47, 0xBF, 0xB1, 0xF8, 0xC3, 0xAF, 0x6C, - 0xEE, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0x3B, 0x87, 0xEC, 0xC1, 0xF4, 0x5F, 0xC4, 0x41, 0xBD, 0x8C, - 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x42, 0xDE, 0x53, 0x0F, 0x98, 0xFF, 0xFA, 0xE1, 0x4B, 0x6F, - 0x0E, 0xFE, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xE3, 0x3F, 0xA8, 0xEE, 0x9E, 0x0F, 0xD9, 0xDE, - 0x67, 0xDC, 0x7E, 0xBF, 0xB3, 0xCF, 0x9C, 0x7A, 0xCE, 0x1E, 0x54, 0xD5, 0xA1, 0xBD, 0x2C, 0x93, - 0x87, 0xFF, 0x4E, 0xAF, 0x3E, 0x14, 0xF2, 0x9E, 0x7A, 0xC0, 0xFC, 0xD7, 0x83, 0x73, 0xFE, 0xB7, - 0xE0, 0x1F, 0x05, 0x00, 0x00, 0x00, 0x60, 0xEA, 0xFC, 0xF2, 0xE1, 0xB9, 0xB7, 0xBC, 0xB6, 0xB5, - 0xEF, 0x97, 0x61, 0x88, 0x8C, 0xEF, 0x9E, 0x6F, 0x26, 0xFE, 0xAB, 0x3D, 0x18, 0xAB, 0x03, 0x7C, - 0x2F, 0x4F, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0C, 0x79, 0x4D, 0x3D, 0x60, 0xFE, 0xDA, 0xA6, 0x39, - 0x77, 0xF3, 0x80, 0x39, 0x00, 0x00, 0x00, 0x3A, 0xCA, 0x7F, 0x50, 0xFD, 0xC9, 0xBE, 0xAF, 0xBE, - 0xBE, 0x65, 0xDE, 0x5B, 0x61, 0x88, 0xBC, 0xB9, 0x6F, 0xBE, 0x39, 0xFB, 0x13, 0x7B, 0x48, 0x56, - 0x87, 0xF9, 0x5E, 0x9B, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x1A, 0xF2, 0x98, 0x7A, 0xC0, 0xFC, 0xD5, - 0xCD, 0x73, 0x37, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xBA, 0x8A, 0x7B, 0x50, 0xFD, 0xB7, 0x9B, - 0xFA, 0xEE, 0x09, 0x23, 0xC4, 0xED, 0xED, 0x61, 0xF7, 0xA0, 0xBA, 0x38, 0xD4, 0xF7, 0xCA, 0xE4, - 0xE1, 0xBF, 0xD3, 0xD3, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0xC9, 0x3D, - 0x1F, 0xF2, 0xCA, 0xA6, 0xBE, 0x2D, 0xD1, 0x10, 0x71, 0xCF, 0x87, 0xF4, 0xDC, 0x83, 0xEA, 0xF2, - 0xF0, 0xDF, 0xE9, 0xE9, 0x78, 0x68, 0x77, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x9E, 0x57, 0xF9, - 0x20, 0xC3, 0x79, 0x35, 0x0F, 0xAA, 0x9F, 0xFE, 0x5B, 0x7B, 0xE0, 0x55, 0x87, 0xFD, 0x6E, 0x9B, - 0x3C, 0xFC, 0x77, 0x7A, 0xF5, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xA5, 0xF3, - 0xAB, 0xC7, 0xFA, 0xFE, 0x22, 0xFE, 0xA0, 0x7A, 0x57, 0x3F, 0x1F, 0x22, 0x0F, 0xFF, 0x9D, 0x9E, - 0x0E, 0x88, 0xAC, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0x94, 0x9A, 0x7B, 0x50, 0xFD, 0xB5, 0x2D, - 0xF3, 0xBE, 0x1E, 0x7F, 0x50, 0xFD, 0xDC, 0x3F, 0xDA, 0xC3, 0xB5, 0x8A, 0x80, 0x4E, 0x4D, 0x1E, - 0xFE, 0x3B, 0x3D, 0x1D, 0x11, 0x59, 0xC6, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x98, 0x56, 0xDC, 0x83, - 0xEA, 0xBF, 0x79, 0xAA, 0xEF, 0xC7, 0x61, 0x84, 0xB8, 0x75, 0xCD, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, - 0xE9, 0xE9, 0x90, 0x48, 0xBB, 0x46, 0x0F, 0x98, 0xBF, 0xB6, 0xE5, 0xD2, 0x81, 0xE0, 0x7F, 0x1A, - 0x00, 0x00, 0x00, 0xA0, 0xBC, 0xFC, 0x0F, 0x32, 0xDC, 0xD4, 0x37, 0x12, 0x46, 0x48, 0xF8, 0x41, - 0x86, 0x1D, 0x7B, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x13, 0x69, 0xA7, 0x1E, 0x30, 0x7F, - 0xE5, 0xC9, 0x39, 0xB7, 0x07, 0xFF, 0x53, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x87, 0x7B, 0x50, 0xFD, - 0xB5, 0x2D, 0xF3, 0x8E, 0x85, 0x21, 0xE2, 0x9E, 0x0F, 0x39, 0x33, 0x6A, 0x0F, 0xDF, 0x2A, 0x12, - 0x8A, 0x9A, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x26, 0xD2, 0x4C, 0x3D, 0x60, 0xFE, 0xCA, 0xA6, 0xB9, - 0xFF, 0x57, 0xF0, 0xB7, 0x1E, 0x00, 0x00, 0x00, 0x98, 0x9E, 0xC2, 0x0F, 0x32, 0x8C, 0x3E, 0xA8, - 0xFE, 0xC6, 0x9E, 0x29, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x14, 0x49, 0x17, 0x7F, - 0xC0, 0xDC, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x98, 0xF0, 0x83, 0x0C, 0xE3, 0x0F, 0xAA, - 0x17, 0xF6, 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2A, 0x92, 0x8C, 0x07, 0xCC, 0x01, 0x00, - 0x00, 0x80, 0x0C, 0x5E, 0x79, 0x72, 0xCE, 0xA5, 0xF1, 0x0F, 0x32, 0x74, 0x0F, 0xAA, 0xE7, 0xFA, - 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2C, 0x5A, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, - 0x1C, 0xFC, 0x7A, 0x63, 0xDF, 0x55, 0xAF, 0x6E, 0x9E, 0xF7, 0x4F, 0x61, 0x84, 0x84, 0x0F, 0xAA, - 0xCB, 0xA0, 0x48, 0x33, 0x79, 0xF8, 0xEF, 0xF4, 0xEA, 0xC3, 0x22, 0xC9, 0x78, 0xC0, 0x1C, 0x00, - 0x00, 0x00, 0xC8, 0xD9, 0x2F, 0x1F, 0x9E, 0x7B, 0x4B, 0xFC, 0x83, 0x0C, 0x33, 0x3F, 0xA8, 0x2E, - 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x8B, 0x66, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, 0x40, 0xFE, - 0x07, 0x19, 0x6E, 0xEE, 0xFB, 0x3F, 0xE3, 0xCF, 0x87, 0xA4, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, - 0x3D, 0x1D, 0x18, 0x8D, 0xA6, 0x1E, 0x30, 0xFF, 0xED, 0xC6, 0x4B, 0xBF, 0xC5, 0x03, 0xE6, 0x00, - 0x00, 0x00, 0x40, 0x01, 0xC2, 0x07, 0xD5, 0xC3, 0x08, 0x71, 0x7B, 0xEB, 0x40, 0x82, 0x07, 0xD5, - 0xE5, 0xE1, 0xBF, 0xD3, 0xD3, 0x91, 0xA1, 0xE6, 0x9E, 0xF3, 0x88, 0x3F, 0x60, 0xFE, 0xCA, 0x53, - 0x73, 0xB6, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x00, 0x30, 0x05, 0xDC, 0x07, 0x19, 0xBE, 0xBE, 0x75, - 0xDE, 0xFE, 0x68, 0x88, 0xBC, 0x73, 0xA8, 0xC1, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xD0, - 0x88, 0x4F, 0x3D, 0x60, 0xFE, 0xDA, 0xE6, 0x39, 0x3F, 0xE1, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, - 0x03, 0xDC, 0x07, 0x19, 0xC6, 0x1F, 0x54, 0x3F, 0xFD, 0xB7, 0xF6, 0xF0, 0x5E, 0x82, 0xF8, 0x70, - 0x0F, 0x98, 0xD7, 0x3C, 0xE7, 0xC1, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x40, 0x77, 0x88, 0x7F, 0x90, - 0xE1, 0xF8, 0xEE, 0x3E, 0x33, 0xF1, 0xF7, 0xEE, 0x20, 0xAF, 0x02, 0xA0, 0x93, 0xAB, 0x0F, 0x8D, - 0xF8, 0xDC, 0x03, 0xE6, 0x6F, 0x0C, 0xD5, 0x3F, 0x60, 0x3E, 0xDA, 0xFF, 0x9E, 0x3F, 0x0A, 0x7E, - 0xBB, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xCD, 0x7F, 0x50, 0x7D, 0xCB, 0xBC, 0xAF, 0x57, 0x1E, 0x54, - 0xB7, 0x07, 0x78, 0xBB, 0x37, 0xF7, 0xF5, 0xD9, 0x03, 0xBD, 0x3B, 0xD8, 0xAB, 0x18, 0x98, 0xEA, - 0xD5, 0xC7, 0x46, 0x74, 0xEE, 0x83, 0x04, 0x79, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xE8, 0x31, 0xEE, - 0x41, 0xF5, 0x57, 0x36, 0xCD, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0xDB, 0xC3, 0xF3, 0xED, 0x01, 0x5F, - 0x45, 0xC1, 0x54, 0x4D, 0x47, 0x87, 0x1B, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x00, 0x25, 0xF0, 0x6F, - 0x1B, 0xFA, 0xFA, 0x5E, 0xDB, 0x32, 0xF7, 0x27, 0x61, 0x84, 0xFC, 0x7E, 0xE7, 0x3C, 0x73, 0xEA, - 0xD9, 0xF9, 0xF6, 0xC0, 0xAF, 0x02, 0xA1, 0xC8, 0x35, 0x0E, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, - 0x00, 0x4A, 0xE6, 0x57, 0x4F, 0x5C, 0xBA, 0xC6, 0x86, 0xC8, 0xB1, 0x68, 0x88, 0x9C, 0xF9, 0x3B, - 0x17, 0x01, 0x2A, 0x16, 0xF2, 0x5E, 0x7D, 0x78, 0xB8, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0x50, - 0x62, 0xEE, 0x01, 0xEE, 0xDF, 0x3E, 0x39, 0xF7, 0xAB, 0xAF, 0x6F, 0x9B, 0x37, 0x1E, 0x86, 0x88, - 0x7B, 0x50, 0xBD, 0xD8, 0xE7, 0x43, 0xEA, 0xC3, 0x83, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x69, - 0xA4, 0xF2, 0x41, 0x86, 0x73, 0xEF, 0x79, 0x7D, 0xCB, 0xDC, 0x9A, 0x07, 0xD5, 0xCF, 0xFD, 0x34, - 0xEF, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, 0xA6, 0xB1, 0x57, 0x9E, 0x9C, - 0x73, 0xA9, 0x7A, 0x50, 0x3D, 0x9F, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, - 0xE0, 0x7B, 0x6D, 0x47, 0xDF, 0x40, 0xE5, 0x83, 0x0C, 0x27, 0x43, 0xE4, 0x8D, 0x3D, 0x7D, 0xFE, - 0x33, 0x22, 0xD9, 0xDE, 0x9A, 0x55, 0x79, 0xB8, 0xDC, 0x3D, 0xE3, 0x11, 0xBF, 0xF1, 0xE0, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x57, 0x9E, 0x99, 0x73, 0xDB, 0xEB, 0xDB, 0xE7, 0x9E, 0x8C, - 0x86, 0x48, 0x18, 0x23, 0xEF, 0x1C, 0x9A, 0x6F, 0x4E, 0xFF, 0xCD, 0x7C, 0xFF, 0x79, 0x91, 0xF8, - 0x0D, 0x89, 0xFB, 0xFF, 0xDD, 0xBF, 0xEE, 0xFE, 0xFD, 0x77, 0x0E, 0xF5, 0xD5, 0x3C, 0xDF, 0x11, - 0xCE, 0x3D, 0x60, 0x6E, 0x7F, 0xEE, 0x75, 0xC1, 0x5F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xDC, - 0x1B, 0xB3, 0x5E, 0x79, 0x66, 0xEE, 0x70, 0x3C, 0x44, 0x5A, 0x2E, 0x16, 0x1D, 0x95, 0xCD, 0x19, - 0xFA, 0xF5, 0xA3, 0x97, 0xDE, 0x1C, 0xFC, 0xD4, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xB9, 0x87, 0xD5, - 0x7F, 0xB3, 0x71, 0xDE, 0x9D, 0xAF, 0x3E, 0x3D, 0xEF, 0xA1, 0x57, 0x37, 0xCF, 0x7D, 0x59, 0x46, - 0x47, 0xB8, 0x20, 0x38, 0x5E, 0xDB, 0x3C, 0xF7, 0xD8, 0xAB, 0xCF, 0xCC, 0x7D, 0xDA, 0xDD, 0x76, - 0xFC, 0xB7, 0xEF, 0xCC, 0xFB, 0x40, 0xF0, 0x53, 0x01, 0x00, 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x35, - 0xB9, 0xFE, 0x07, 0x1B, 0xEE, 0xE8, 0x1B, 0x88, 0xEF, 0xDF, 0x36, 0xCC, 0xEE, 0x0B, 0xFE, 0x63, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xD0, 0x15, 0xDE, 0xF3, 0x9E, 0xFF, 0x1F, 0x5B, 0xE2, 0x94, 0x1D, 0x86, - 0x86, 0x23, 0x09, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; - -static const uint8_t tree_gif[] PROGMEM = { - - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x28, 0x00, 0x3C, 0x00, 0x83, 0x0B, 0x00, 0x00, 0xFF, 0xFF, - 0x00, 0x21, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x94, 0x00, 0x00, 0xB5, 0x00, 0x00, - 0xDE, 0x00, 0x00, 0xFF, 0x00, 0x6B, 0x00, 0x00, 0xFF, 0x33, 0x33, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, - 0xFF, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x00, 0xCC, 0x00, 0x00, 0x21, 0xFF, 0x0B, - 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0xA0, 0x0F, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, - 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, - 0x8C, 0xCA, 0xE8, 0x85, 0xE8, 0xA5, 0xAC, 0x67, 0xEA, 0x8A, 0xE5, 0x2B, 0x2F, 0xE3, 0x31, 0xCE, - 0xAF, 0x41, 0x1C, 0xB8, 0x1B, 0x24, 0x80, 0x04, 0xAF, 0x87, 0x02, 0x26, 0x88, 0xA1, 0x07, 0x60, - 0xB9, 0x44, 0x82, 0x04, 0x02, 0x80, 0x61, 0x30, 0x70, 0x6E, 0x0C, 0x86, 0x03, 0x60, 0x20, 0xB0, - 0x6A, 0xA8, 0xD3, 0x03, 0x21, 0xEC, 0xAD, 0x18, 0x12, 0xE8, 0xF4, 0xB1, 0x4C, 0x51, 0xA0, 0x07, - 0xEA, 0x07, 0x5B, 0xF2, 0x70, 0x13, 0x76, 0xD4, 0xA8, 0xBC, 0xBC, 0xEA, 0x1F, 0x06, 0x77, 0x51, - 0x73, 0x50, 0x0A, 0x80, 0x7F, 0x04, 0x4C, 0x4D, 0x65, 0x06, 0x0A, 0x3B, 0x3B, 0x62, 0x5A, 0x53, - 0x65, 0x05, 0x02, 0x04, 0x68, 0x0C, 0x6A, 0x00, 0x5D, 0x5E, 0x0F, 0x0F, 0x6A, 0x98, 0x6A, 0x09, - 0x7B, 0x56, 0x9D, 0x09, 0x0C, 0xA7, 0xA7, 0x7F, 0x06, 0x9D, 0xA3, 0x44, 0x02, 0x01, 0x03, 0x90, - 0x0C, 0x87, 0x03, 0x06, 0x02, 0x05, 0x56, 0x06, 0x01, 0x02, 0x53, 0xB1, 0xB3, 0x04, 0x79, 0xAF, - 0x55, 0x48, 0x51, 0xB0, 0x87, 0xC0, 0x7F, 0x07, 0x95, 0x05, 0x85, 0x44, 0xB6, 0x00, 0x05, 0x53, - 0x96, 0x6A, 0x60, 0x02, 0x0A, 0x48, 0x89, 0x00, 0x07, 0xA1, 0x69, 0x7D, 0x2B, 0x3D, 0x9D, 0x41, - 0xA1, 0x04, 0xA0, 0xDD, 0x9D, 0x38, 0x02, 0xE1, 0x6A, 0x07, 0x62, 0x0C, 0x77, 0xE6, 0xAD, 0x2F, - 0x05, 0x01, 0xB6, 0x90, 0x07, 0xA8, 0xA7, 0x54, 0xB5, 0xBC, 0x06, 0x33, 0x50, 0x53, 0x3A, 0x00, - 0x39, 0x62, 0x10, 0x6B, 0xDF, 0xBE, 0x19, 0x03, 0x18, 0x40, 0xA9, 0x15, 0x4B, 0xCC, 0x0E, 0x77, - 0x3B, 0x04, 0xC4, 0x02, 0x38, 0x43, 0x21, 0x96, 0x2C, 0xD3, 0xD6, 0x81, 0x01, 0x26, 0x2A, 0xFB, - 0xDE, 0x07, 0x7C, 0x0A, 0xB7, 0x71, 0x1B, 0x79, 0xEE, 0xC5, 0x83, 0x72, 0xDD, 0x84, 0xBC, 0x4B, - 0x13, 0xA4, 0x13, 0x15, 0x17, 0xAB, 0x18, 0x78, 0x4A, 0x43, 0x40, 0x81, 0x43, 0x76, 0x8E, 0xB6, - 0x04, 0x7C, 0x19, 0x02, 0x98, 0xC4, 0x86, 0x35, 0xBD, 0xE1, 0x1C, 0x90, 0x4D, 0x62, 0xBF, 0x14, - 0xFC, 0x7E, 0x02, 0x1A, 0x60, 0xD3, 0xD1, 0x9D, 0x3B, 0x3A, 0x09, 0xFC, 0x44, 0xB1, 0xD0, 0x96, - 0x8E, 0x43, 0x8D, 0x9E, 0xDA, 0xDB, 0x22, 0x11, 0x8A, 0x42, 0x10, 0x5C, 0x00, 0x1A, 0x2D, 0x38, - 0xF2, 0x0E, 0xA0, 0x4A, 0x03, 0x4E, 0xA2, 0xF0, 0xF4, 0x80, 0x17, 0x95, 0x8C, 0x23, 0xD1, 0xD8, - 0x12, 0x00, 0x92, 0x03, 0xAB, 0x99, 0x23, 0x71, 0x3E, 0x8D, 0xEB, 0x6E, 0x18, 0x07, 0x66, 0x23, - 0x05, 0x3E, 0x72, 0xE4, 0x06, 0x0D, 0x81, 0x02, 0xE4, 0x36, 0x5D, 0xD9, 0xA5, 0xC0, 0xAD, 0x59, - 0xA2, 0x86, 0x6E, 0xD6, 0xC4, 0x73, 0xEB, 0x15, 0xA5, 0xC5, 0xDE, 0x14, 0xF4, 0x62, 0x07, 0xC0, - 0xE9, 0x9D, 0xCC, 0x58, 0x02, 0x14, 0xA0, 0x84, 0x45, 0x83, 0x68, 0x7A, 0x8D, 0x81, 0x66, 0xC3, - 0xE9, 0xD0, 0xA6, 0x3E, 0x03, 0xF3, 0x80, 0x7D, 0x90, 0xD8, 0x58, 0xA2, 0x59, 0x2D, 0x90, 0x68, - 0xD6, 0x64, 0x17, 0x76, 0x5A, 0x23, 0xBF, 0x13, 0x58, 0x89, 0x7A, 0xC3, 0xF0, 0x8F, 0xB6, 0xB8, - 0x4B, 0xD3, 0x74, 0x52, 0x50, 0x92, 0xC2, 0x03, 0xD1, 0x78, 0xD7, 0x89, 0x4C, 0x23, 0x26, 0x6E, - 0x82, 0xCC, 0x15, 0x5E, 0x05, 0x08, 0x30, 0x06, 0x8A, 0x75, 0x64, 0xFA, 0x42, 0xF1, 0x12, 0xAD, - 0x60, 0xDE, 0x51, 0x0A, 0x94, 0xBA, 0x5E, 0x2C, 0x8C, 0x86, 0xDD, 0x3D, 0xDE, 0x8F, 0x0A, 0x70, - 0x51, 0x5F, 0xFE, 0xF2, 0x84, 0xD1, 0xD0, 0xA6, 0x68, 0xB7, 0x96, 0x62, 0x14, 0x0C, 0xC1, 0xB7, - 0x90, 0x68, 0x72, 0xD1, 0x7C, 0xD9, 0x3D, 0xA3, 0x94, 0x35, 0xDE, 0x54, 0x06, 0x92, 0x51, 0x50, - 0x68, 0xC2, 0xC5, 0x44, 0xB7, 0x30, 0x70, 0xDE, 0x6A, 0x49, 0x69, 0x86, 0xC5, 0x2D, 0xA3, 0x29, - 0x04, 0x05, 0x2F, 0xD9, 0xC4, 0x02, 0x0C, 0x6C, 0x0C, 0x48, 0xB0, 0x94, 0x01, 0xDA, 0x3C, 0xF2, - 0x56, 0x21, 0xFA, 0xE8, 0xA3, 0xA1, 0x55, 0x12, 0xE9, 0xF4, 0x56, 0x42, 0xF8, 0x50, 0x00, 0x40, - 0x08, 0x08, 0xD4, 0x88, 0x80, 0x05, 0x33, 0x66, 0x10, 0xA2, 0x0C, 0xDC, 0xA0, 0xB0, 0xA3, 0x0B, - 0x71, 0xCD, 0xB1, 0x80, 0x75, 0x69, 0x20, 0x41, 0xE4, 0x91, 0x6B, 0x64, 0x10, 0x01, 0x00, 0x21, - 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, 0x00, - 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, 0x8C, - 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0x31, 0x1C, 0x34, - 0x1B, 0x24, 0x4C, 0x82, 0xE7, 0x28, 0x5E, 0x02, 0x78, 0x7A, 0x30, 0x8E, 0x47, 0x62, 0x48, 0x20, - 0x60, 0xD8, 0x06, 0x4A, 0x8E, 0xC1, 0x70, 0x60, 0x0C, 0x04, 0xD1, 0xCD, 0x60, 0x60, 0x3B, 0x10, - 0xBA, 0x59, 0x8B, 0x21, 0x41, 0x2E, 0x0F, 0xC3, 0x16, 0xF2, 0xC0, 0xAC, 0x40, 0x4F, 0x1E, 0x80, - 0x04, 0x81, 0x70, 0xD8, 0x0A, 0x14, 0x0F, 0xF7, 0x42, 0x00, 0xA0, 0xD6, 0x09, 0x0A, 0x81, 0x81, - 0x68, 0x00, 0x85, 0x85, 0x7F, 0x07, 0x07, 0x0A, 0x06, 0x7A, 0x06, 0x00, 0x74, 0x74, 0x5E, 0x8A, - 0x36, 0x68, 0x05, 0x7C, 0x66, 0x3D, 0x64, 0x58, 0x61, 0x0F, 0x0F, 0x66, 0x64, 0x99, 0x64, 0x79, - 0x9C, 0x9E, 0xA0, 0x48, 0x4E, 0x9D, 0xA3, 0x4A, 0x02, 0x01, 0x37, 0x90, 0x55, 0x04, 0x5C, 0x02, - 0x05, 0x59, 0x06, 0x01, 0x02, 0x4F, 0x89, 0x55, 0x76, 0xAC, 0x50, 0xAB, 0x0A, 0xAD, 0x7F, 0xB1, - 0x75, 0x07, 0x02, 0x04, 0x05, 0x00, 0xBE, 0x39, 0x06, 0x77, 0x05, 0x36, 0x04, 0x9F, 0x5B, 0xCC, - 0x00, 0x4A, 0x82, 0x81, 0x07, 0x9F, 0x65, 0x86, 0x85, 0x40, 0x9D, 0x0A, 0x9F, 0x0C, 0xD0, 0x9F, - 0x00, 0x9D, 0x39, 0x02, 0xDE, 0x66, 0xB0, 0x73, 0xE2, 0x09, 0xE4, 0xAA, 0x2F, 0x05, 0x01, 0xCC, - 0x5E, 0xA7, 0x56, 0x75, 0xD2, 0xB8, 0x8C, 0x2F, 0x4C, 0x36, 0x06, 0xB1, 0x73, 0xE1, 0x88, 0xC9, - 0x92, 0x45, 0x43, 0x96, 0x00, 0x2E, 0x37, 0xE8, 0x79, 0x99, 0x53, 0xEC, 0x86, 0x0D, 0x06, 0x05, - 0xF3, 0x51, 0x61, 0x57, 0xE6, 0x9E, 0xBF, 0x35, 0x0C, 0xDE, 0x81, 0xE8, 0x64, 0x10, 0xFB, 0x5B, - 0xB6, 0x6C, 0xF5, 0x56, 0x94, 0x22, 0x13, 0xA7, 0xE2, 0x9C, 0x04, 0xDF, 0xCA, 0x74, 0xB2, 0xB2, - 0xC2, 0x40, 0x2A, 0x92, 0x72, 0x20, 0x45, 0x52, 0xE0, 0x45, 0x1A, 0x01, 0x2B, 0xCA, 0x3E, 0xC4, - 0x3A, 0x38, 0x60, 0xDB, 0x23, 0x86, 0xD6, 0x14, 0x0C, 0x20, 0xC0, 0x84, 0xCB, 0x8A, 0x7C, 0x3C, - 0x1F, 0x25, 0x94, 0xA9, 0x80, 0x21, 0x17, 0xA2, 0x5B, 0x4E, 0x34, 0x91, 0xE5, 0x2F, 0x99, 0x4C, - 0xA0, 0x89, 0xEC, 0x5C, 0xC1, 0xB5, 0x89, 0xC3, 0x15, 0x2B, 0xCC, 0x10, 0xD6, 0xF9, 0x38, 0x67, - 0xA8, 0xB1, 0x01, 0xE5, 0x40, 0xD4, 0x7B, 0x42, 0xF1, 0x63, 0x02, 0x66, 0x02, 0x12, 0xA4, 0xFA, - 0x90, 0x2A, 0x54, 0xB6, 0x44, 0xEB, 0xDA, 0x96, 0x19, 0x9A, 0x53, 0x43, 0x01, 0xBB, 0x64, 0x84, - 0xAE, 0xC3, 0xBB, 0xB0, 0xCC, 0x31, 0x40, 0x5D, 0x35, 0xD8, 0x62, 0x82, 0x6B, 0x68, 0xAC, 0xA6, - 0x43, 0xF1, 0xD2, 0x01, 0x20, 0xE9, 0x8A, 0x25, 0x05, 0x96, 0x36, 0xD8, 0x8A, 0xD7, 0xD8, 0x46, - 0xD0, 0x85, 0xEB, 0x7E, 0x72, 0x31, 0x10, 0x54, 0x9F, 0x85, 0x00, 0xF1, 0xA6, 0x1C, 0x94, 0x2C, - 0x34, 0xB2, 0x17, 0x9F, 0x5B, 0xB8, 0x14, 0x80, 0xFC, 0x41, 0x28, 0x63, 0xC7, 0x03, 0x9A, 0x4A, - 0x32, 0x9C, 0x2C, 0x2B, 0x2E, 0x40, 0x72, 0x63, 0x59, 0x48, 0x85, 0x47, 0x8D, 0xD8, 0xC8, 0xD9, - 0xE2, 0xF0, 0x55, 0x99, 0x4A, 0xE3, 0x82, 0x07, 0x41, 0x3F, 0x25, 0x92, 0xAE, 0xD7, 0x4C, 0x3C, - 0xE7, 0xAC, 0x02, 0x04, 0x58, 0xC4, 0xC4, 0xAD, 0x9C, 0x1B, 0xB1, 0x3F, 0xE1, 0x42, 0x3D, 0x4B, - 0x9E, 0x85, 0xCB, 0x57, 0xA6, 0x30, 0x4B, 0x97, 0x88, 0x26, 0xA2, 0x03, 0x7F, 0x8B, 0x72, 0x2D, - 0x90, 0x79, 0x02, 0xFD, 0x59, 0x36, 0xB2, 0xF3, 0x41, 0x2A, 0x65, 0xAD, 0x75, 0x64, 0x3A, 0x0C, - 0x48, 0xB4, 0x15, 0x35, 0x13, 0xE0, 0xC2, 0xC0, 0x2D, 0xB1, 0x15, 0x95, 0x0C, 0x13, 0x97, 0x59, - 0x73, 0x90, 0x01, 0xF5, 0xAC, 0x36, 0x8D, 0x79, 0x12, 0x40, 0xB8, 0x45, 0x22, 0xB8, 0xF8, 0xC4, - 0x0C, 0x7D, 0xB3, 0x31, 0x96, 0xCF, 0x00, 0x01, 0x76, 0x41, 0x80, 0x4F, 0x0B, 0x80, 0x78, 0x4A, - 0x24, 0x5B, 0x8C, 0x18, 0xDB, 0x8A, 0x0A, 0xF0, 0xC3, 0x15, 0x88, 0x08, 0xF1, 0xC5, 0xC7, 0x3C, - 0x13, 0x40, 0x94, 0x01, 0x81, 0x13, 0x20, 0xA0, 0x23, 0x02, 0x27, 0xE0, 0x58, 0x63, 0x08, 0xD9, - 0xE8, 0x41, 0x81, 0x5B, 0x42, 0x7A, 0x57, 0x86, 0x12, 0x46, 0x26, 0x79, 0x46, 0x06, 0x11, 0x00, - 0x00, 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x3C, 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, - 0xE4, 0x8C, 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0xA1, - 0x1C, 0x34, 0x1B, 0x24, 0x4A, 0x82, 0xE7, 0x27, 0x85, 0x50, 0x08, 0x0C, 0x3D, 0x12, 0xBC, 0xC4, - 0xA3, 0x18, 0x12, 0x08, 0x14, 0x86, 0xC1, 0x80, 0xC9, 0x31, 0x18, 0x0E, 0x84, 0x81, 0x80, 0xBA, - 0x91, 0x46, 0xB1, 0x51, 0x06, 0xD7, 0x62, 0x40, 0x9A, 0x91, 0xE2, 0x71, 0xA5, 0x37, 0x40, 0x33, - 0xDE, 0xE9, 0xF1, 0xE3, 0xA1, 0x20, 0x10, 0x0E, 0x52, 0x01, 0x63, 0xA9, 0x5E, 0x0C, 0x85, 0x03, - 0x76, 0x7A, 0x7D, 0x0B, 0x4E, 0x03, 0x75, 0x78, 0x77, 0x78, 0x06, 0x84, 0x51, 0x37, 0x8A, 0x77, - 0x02, 0x51, 0x6A, 0x05, 0x02, 0x04, 0x67, 0x67, 0x5B, 0x72, 0x47, 0x98, 0x98, 0x7C, 0x5C, 0x73, - 0x66, 0x58, 0x07, 0x8B, 0x0F, 0x00, 0x9F, 0x45, 0x02, 0x01, 0x03, 0xA3, 0x0C, 0x89, 0x03, 0x06, - 0x02, 0x00, 0x5C, 0x06, 0x01, 0x92, 0x52, 0x07, 0xAE, 0x59, 0x03, 0x00, 0xBD, 0xBD, 0x4C, 0x02, - 0x05, 0xAB, 0xB9, 0x70, 0x0C, 0xAC, 0x96, 0x00, 0x52, 0x45, 0x36, 0xC1, 0x51, 0x97, 0x0C, 0x66, - 0x5E, 0x00, 0x8C, 0x4C, 0x0A, 0x05, 0x5F, 0x68, 0x98, 0x4E, 0x56, 0x40, 0x7F, 0x3D, 0x67, 0x97, - 0x9D, 0x73, 0x39, 0x02, 0x74, 0x9C, 0x48, 0xA4, 0x76, 0x76, 0x66, 0x73, 0xA8, 0x2C, 0x05, 0x50, - 0x02, 0xA3, 0xA3, 0x59, 0x78, 0x5E, 0x92, 0xD4, 0x2C, 0xDB, 0xB0, 0x59, 0x76, 0x07, 0x00, 0x78, - 0x16, 0x09, 0x80, 0xF5, 0xE4, 0x05, 0xC1, 0x81, 0x5F, 0xB0, 0x10, 0x00, 0xA0, 0xEE, 0xC0, 0xC0, - 0x03, 0x8E, 0x68, 0x68, 0xB1, 0x72, 0xE5, 0x92, 0xAF, 0x5E, 0xF6, 0x0C, 0x10, 0xF0, 0x86, 0x62, - 0xFB, 0xCE, 0xC1, 0x03, 0x98, 0x00, 0x74, 0x42, 0x42, 0xE7, 0xC5, 0xB9, 0x4E, 0xFF, 0xC2, 0x9D, - 0x99, 0xA3, 0x60, 0x0A, 0x0A, 0x03, 0xED, 0xC0, 0xDD, 0xF1, 0x47, 0xCF, 0xCB, 0x2E, 0x97, 0x20, - 0x92, 0x0D, 0x64, 0x85, 0xA5, 0x65, 0x43, 0x2C, 0x3C, 0x19, 0x10, 0x30, 0x94, 0x2F, 0x27, 0xBE, - 0x9D, 0x87, 0x78, 0xCE, 0x54, 0x47, 0xC0, 0x98, 0xC6, 0x9D, 0x39, 0x2F, 0xC6, 0xB2, 0xF1, 0x87, - 0x29, 0x96, 0x62, 0xC6, 0xB6, 0x69, 0xE2, 0xC0, 0x2B, 0x4A, 0x2C, 0x58, 0x49, 0x47, 0xDA, 0x11, - 0x6A, 0x69, 0xC0, 0xB8, 0x10, 0x22, 0x1F, 0xDC, 0x0A, 0xF4, 0x6D, 0x64, 0x02, 0x06, 0xB1, 0x04, - 0x28, 0x39, 0xBB, 0xA1, 0xDD, 0x49, 0x4C, 0xE9, 0xD4, 0xB9, 0x4D, 0x0A, 0xA2, 0x40, 0x81, 0x91, - 0x81, 0x68, 0x2E, 0x35, 0x43, 0xA0, 0xC0, 0x50, 0x05, 0x1F, 0x6A, 0x39, 0xB9, 0x65, 0x47, 0x4A, - 0xBF, 0xBC, 0xF4, 0xF0, 0x04, 0xE3, 0x98, 0xA1, 0x96, 0x02, 0x5B, 0x5E, 0x71, 0xA5, 0x53, 0xA8, - 0x4E, 0x68, 0x16, 0x2B, 0x01, 0xFC, 0x42, 0x29, 0x4A, 0x21, 0xF4, 0xE8, 0x87, 0x0A, 0xA5, 0x04, - 0xDA, 0xDC, 0x94, 0x95, 0x6A, 0x03, 0xC2, 0x5A, 0x6E, 0xF0, 0x66, 0xC8, 0x0E, 0xC0, 0x99, 0x20, - 0xDF, 0x16, 0x93, 0xAC, 0x91, 0x64, 0x16, 0x0B, 0xED, 0x14, 0x70, 0xF2, 0xC2, 0x6B, 0x75, 0x27, - 0x68, 0x81, 0xDA, 0x90, 0xB4, 0x0B, 0xDC, 0x74, 0xC8, 0x8B, 0x67, 0xB0, 0xBC, 0x75, 0x2B, 0xCC, - 0x5D, 0xA1, 0x00, 0xD8, 0x09, 0xC4, 0x3D, 0x23, 0x92, 0xB0, 0x6B, 0x29, 0xDA, 0x6A, 0x55, 0x12, - 0x46, 0x7A, 0x41, 0xA5, 0x81, 0xF8, 0x62, 0x25, 0xE8, 0x4E, 0x2A, 0x60, 0x22, 0x52, 0x05, 0xB4, - 0xB4, 0x94, 0x14, 0xAC, 0x12, 0x05, 0xBF, 0xCD, 0xB4, 0x60, 0xA7, 0x6F, 0x66, 0x65, 0xE7, 0x57, - 0xC7, 0xA3, 0xC8, 0x67, 0x85, 0x16, 0x5B, 0x49, 0xF0, 0x84, 0x2A, 0x48, 0x19, 0x92, 0x47, 0x30, - 0x4E, 0x00, 0x60, 0xC9, 0x57, 0x71, 0x79, 0x03, 0x51, 0x30, 0xD3, 0x4C, 0xA0, 0xC0, 0x4E, 0xA4, - 0x1C, 0x45, 0x51, 0x2C, 0x7E, 0x39, 0xB8, 0x18, 0x3E, 0x04, 0xB5, 0x04, 0x14, 0x6C, 0xB3, 0xF8, - 0xF1, 0xC7, 0x3C, 0x00, 0xAA, 0xC6, 0xCB, 0x45, 0xFC, 0x21, 0xD5, 0x52, 0x72, 0x17, 0x95, 0x88, - 0xD8, 0x09, 0x08, 0x00, 0x80, 0x00, 0x02, 0x28, 0x94, 0x28, 0xC1, 0x8C, 0x20, 0x20, 0xD1, 0x5D, - 0x02, 0x27, 0xE8, 0x88, 0x82, 0x5B, 0x84, 0x2C, 0xE0, 0xD6, 0x19, 0x4C, 0x1C, 0xA9, 0xA4, 0x19, - 0x1B, 0x44, 0x00, 0x00, 0x21, 0xFE, 0x19, 0x42, 0x6F, 0x79, 0x27, 0x73, 0x20, 0x48, 0x61, 0x70, - 0x70, 0x79, 0x20, 0x48, 0x6F, 0x6C, 0x69, 0x64, 0x61, 0x79, 0x73, 0x20, 0x50, 0x61, 0x67, 0x65, - 0x00, 0x3B - -}; - -static const uint8_t bird_gif[] PROGMEM = { - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x74, 0x00, 0x4E, 0x00, 0xCC, 0x13, 0x00, 0xFF, 0xFF, 0xFF, - 0xEF, 0xFF, 0xFF, 0xBD, 0xE7, 0xE7, 0xD6, 0xEF, 0xEF, 0x63, 0x63, 0x63, 0xAD, 0xAD, 0xAD, 0xCE, - 0xCE, 0xCE, 0xD6, 0xDE, 0xDE, 0x4A, 0x4A, 0x4A, 0x7B, 0x84, 0x84, 0x94, 0x9C, 0x9C, 0xE7, 0xE7, - 0xE7, 0x84, 0x94, 0x94, 0xB5, 0xC6, 0xC6, 0x73, 0x7B, 0x7B, 0x31, 0x31, 0x31, 0xF7, 0xF7, 0xF7, - 0x18, 0x10, 0x10, 0xCE, 0x63, 0x39, 0x6B, 0x39, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xFF, 0x0B, - 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x4B, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0xAE, 0xEC, 0x68, 0x2C, - 0x6D, 0x2C, 0xCF, 0x74, 0x6D, 0x03, 0x46, 0x84, 0x40, 0x77, 0xEF, 0xFF, 0x40, 0x51, 0x42, 0x41, - 0x0C, 0x1A, 0x8F, 0x48, 0x13, 0xC1, 0xD0, 0x20, 0x00, 0x0E, 0x8E, 0x42, 0x72, 0x4A, 0xAD, 0x21, - 0x0E, 0xCD, 0x05, 0xE1, 0x80, 0xA8, 0x7A, 0xBF, 0x29, 0xC2, 0xA0, 0x91, 0x28, 0x34, 0x02, 0x5D, - 0xB0, 0x5A, 0xFD, 0x08, 0x34, 0x14, 0x84, 0x00, 0x7A, 0x4D, 0xF7, 0x22, 0xDC, 0x89, 0xB8, 0xC1, - 0xB9, 0x80, 0xD5, 0xFF, 0x40, 0x0B, 0x77, 0x0C, 0x0F, 0x05, 0x01, 0x0C, 0x0A, 0x0B, 0x0F, 0x08, - 0x06, 0x80, 0x8E, 0x37, 0x7B, 0x01, 0x09, 0x11, 0x03, 0x68, 0x5C, 0x4C, 0x0A, 0x8F, 0x9A, 0x33, - 0x06, 0x09, 0x01, 0x0E, 0x71, 0x68, 0x88, 0x92, 0x07, 0x9B, 0xA6, 0x2C, 0x0A, 0x86, 0x07, 0x95, - 0x05, 0x04, 0xA1, 0x5D, 0x8D, 0xA7, 0xB2, 0x26, 0x09, 0x06, 0x72, 0x72, 0x0E, 0x94, 0x01, 0xA9, - 0x04, 0x08, 0x52, 0xB3, 0xC0, 0x00, 0x09, 0x95, 0xB7, 0x0D, 0x86, 0x01, 0x04, 0x0A, 0xC3, 0x09, - 0x34, 0x43, 0xC1, 0x54, 0x0E, 0xB7, 0xD2, 0x87, 0xAE, 0xBB, 0x99, 0x32, 0x7B, 0x0E, 0xCF, 0x49, - 0x0B, 0xD1, 0xD3, 0x72, 0x0C, 0xDE, 0x57, 0x33, 0xBE, 0xCC, 0xDB, 0x47, 0x05, 0x0C, 0xDF, 0xD3, - 0x05, 0xD7, 0x31, 0x0C, 0xE1, 0xA5, 0xE7, 0x46, 0xBE, 0xEB, 0xD2, 0x04, 0x05, 0xBF, 0x2C, 0x5C, - 0x4D, 0xF3, 0x46, 0x0B, 0x11, 0x56, 0xD9, 0x3B, 0x14, 0x01, 0x14, 0x0F, 0x16, 0x04, 0x1A, 0x30, - 0x28, 0x00, 0x81, 0xA1, 0x3F, 0x1F, 0xAD, 0x02, 0x10, 0x5B, 0x87, 0x6F, 0x0E, 0x8B, 0x02, 0x0E, - 0x04, 0x5C, 0x41, 0x10, 0xF0, 0x61, 0x8F, 0x25, 0x03, 0x26, 0xDA, 0x43, 0xC4, 0x42, 0x8B, 0x00, - 0x05, 0x0E, 0x7C, 0xFF, 0xED, 0xF0, 0x68, 0x83, 0x8B, 0x44, 0x91, 0xDF, 0x06, 0xAC, 0x54, 0x01, - 0x21, 0xA1, 0x80, 0x07, 0x0F, 0x1A, 0x38, 0x70, 0xC7, 0x72, 0x46, 0xC2, 0x97, 0x03, 0x3F, 0xC5, - 0x52, 0xE1, 0x80, 0x81, 0x00, 0x01, 0x19, 0x1B, 0x3C, 0x38, 0xD8, 0x53, 0xC6, 0x81, 0x38, 0x21, - 0x61, 0x4A, 0xEB, 0xB7, 0x42, 0x01, 0x82, 0xA3, 0x58, 0x7D, 0xC9, 0x6B, 0x1A, 0x63, 0x0B, 0xD0, - 0x81, 0x11, 0x08, 0x74, 0x44, 0x61, 0xE0, 0x01, 0xD6, 0xA3, 0x05, 0x16, 0x2D, 0xE5, 0xDA, 0xC2, - 0x80, 0xB7, 0x90, 0x41, 0xCB, 0xA4, 0x39, 0xC1, 0xEF, 0x2C, 0x56, 0xAA, 0x6C, 0x57, 0x38, 0xA8, - 0x14, 0x35, 0xA8, 0x35, 0x14, 0x10, 0x10, 0x34, 0xB0, 0x9B, 0xD5, 0x4F, 0xDE, 0x14, 0x06, 0x8E, - 0xF5, 0x1D, 0xC8, 0x25, 0x85, 0x83, 0xC1, 0x84, 0x05, 0x20, 0x32, 0x30, 0xF4, 0x30, 0x2D, 0x39, - 0x8B, 0x07, 0x32, 0x42, 0xA1, 0x00, 0x72, 0xE4, 0x45, 0x08, 0x7C, 0x59, 0x36, 0x91, 0x18, 0x73, - 0xE6, 0x6F, 0x79, 0x38, 0x2B, 0x88, 0x7C, 0x54, 0xE7, 0xE0, 0x2D, 0xA3, 0x4B, 0x78, 0x7A, 0x79, - 0x7A, 0x1A, 0xCE, 0x04, 0x4C, 0x85, 0xAC, 0x66, 0x7D, 0x96, 0x80, 0xB9, 0xD8, 0x22, 0x4A, 0x4B, - 0x14, 0x00, 0x97, 0xF1, 0xD3, 0x12, 0x3B, 0x79, 0x9F, 0x45, 0xC0, 0x13, 0xB8, 0xB7, 0xE1, 0xC5, - 0x19, 0x33, 0xEB, 0x53, 0xD3, 0xA8, 0x72, 0x01, 0x0D, 0x44, 0x97, 0x5C, 0x90, 0x7B, 0x9B, 0x01, - 0x05, 0xA6, 0x89, 0xFB, 0x3D, 0x54, 0x20, 0x81, 0xDA, 0x8C, 0xD7, 0x91, 0x16, 0x22, 0x91, 0xF8, - 0x80, 0x9F, 0x86, 0x08, 0x1E, 0xF4, 0xF2, 0xD5, 0x7D, 0x96, 0x18, 0xCC, 0x47, 0xA3, 0xDB, 0x7B, - 0xCA, 0x60, 0x40, 0xCA, 0xF4, 0x58, 0x81, 0xE2, 0x42, 0x04, 0x11, 0x4C, 0x80, 0x53, 0x7C, 0x3F, - 0xC9, 0xB1, 0xC7, 0xFF, 0x7A, 0xC1, 0x18, 0xA0, 0x0E, 0x7E, 0xC4, 0x49, 0xA5, 0x99, 0x67, 0xD7, - 0xA5, 0xB4, 0x15, 0x04, 0x93, 0x48, 0xA0, 0xA1, 0x81, 0x7C, 0x49, 0xB3, 0xC7, 0x38, 0xF6, 0x11, - 0x13, 0x52, 0x7E, 0x12, 0xAE, 0x63, 0xC0, 0x55, 0x00, 0x82, 0x62, 0xD8, 0x08, 0x0B, 0x24, 0xA0, - 0xA1, 0x86, 0x57, 0x3C, 0xF0, 0xE0, 0x2D, 0x05, 0x68, 0x67, 0x4A, 0x27, 0xB7, 0x0C, 0x80, 0x95, - 0x7E, 0x41, 0x11, 0x80, 0x5E, 0x85, 0xBF, 0x9D, 0xD0, 0xC0, 0x04, 0x2F, 0x2E, 0x62, 0xCB, 0x34, - 0x03, 0xE4, 0xB1, 0xA2, 0x23, 0x5E, 0x41, 0xC8, 0x63, 0x50, 0x0E, 0x9A, 0x75, 0x5D, 0x68, 0x5B, - 0x9D, 0xA0, 0x45, 0x04, 0x38, 0x2D, 0x72, 0xCC, 0x34, 0x7B, 0x24, 0xF2, 0x88, 0x5B, 0x39, 0xEA, - 0x18, 0xE1, 0x78, 0xD2, 0x58, 0x05, 0x20, 0x6C, 0x2B, 0xD4, 0x78, 0x64, 0x89, 0x6E, 0xB8, 0xF2, - 0xC2, 0x1F, 0x4D, 0xBE, 0x44, 0x22, 0x99, 0x72, 0xE4, 0x01, 0x60, 0x19, 0x31, 0x68, 0xB1, 0xC4, - 0x78, 0x50, 0xF4, 0xE2, 0x9B, 0x43, 0x5E, 0x80, 0x99, 0xE3, 0x8E, 0x6C, 0x4E, 0xE3, 0x63, 0x7A, - 0xD9, 0x55, 0xD6, 0x82, 0x4E, 0x04, 0x30, 0xD0, 0xC0, 0x01, 0x7C, 0x2A, 0xE4, 0xA3, 0xA2, 0x47, - 0xDC, 0x37, 0xE8, 0x9C, 0xE3, 0xC9, 0x64, 0x9D, 0x72, 0x70, 0x54, 0x29, 0x03, 0x04, 0xDF, 0xE5, - 0xE1, 0x4A, 0x19, 0x06, 0xB0, 0xD9, 0xC4, 0x66, 0x48, 0x7C, 0x27, 0xCD, 0x88, 0x98, 0x92, 0x89, - 0xE2, 0x75, 0x69, 0x2D, 0x69, 0xC3, 0x02, 0x07, 0xA4, 0x03, 0x8A, 0x2B, 0x8E, 0xC2, 0x04, 0x05, - 0x03, 0x48, 0x3C, 0x17, 0x5E, 0xAB, 0x7E, 0x61, 0x04, 0x20, 0x03, 0x33, 0x21, 0x01, 0xC1, 0x02, - 0xDF, 0x81, 0x52, 0x8B, 0x34, 0xAD, 0xC8, 0x6A, 0x43, 0x3E, 0x48, 0x9E, 0xF5, 0x24, 0x45, 0xAF, - 0xF2, 0x96, 0x9D, 0xFF, 0x03, 0xF5, 0x51, 0xD1, 0xA2, 0x03, 0x90, 0xCA, 0xF1, 0x94, 0x3E, 0x37, - 0x68, 0x81, 0xA4, 0x98, 0xC0, 0x9A, 0xF8, 0xC0, 0x8F, 0xCA, 0x39, 0xA0, 0xCD, 0x1F, 0x0B, 0x10, - 0x21, 0x0D, 0x03, 0xB8, 0x7D, 0x74, 0x64, 0x98, 0xD2, 0x16, 0x8A, 0x0C, 0xBA, 0xCA, 0x29, 0xF5, - 0x48, 0x8B, 0x53, 0xA1, 0x19, 0x03, 0xA8, 0x18, 0x85, 0xB2, 0x2A, 0xB9, 0x63, 0x06, 0x45, 0x06, - 0x80, 0x48, 0x05, 0xC9, 0x6E, 0x9C, 0x4F, 0xE1, 0xA3, 0xC2, 0x77, 0xF0, 0x16, 0x95, 0x4B, 0xB7, - 0xF4, 0x12, 0xEA, 0x57, 0x59, 0x14, 0xB2, 0x46, 0x08, 0xB8, 0x8E, 0x08, 0x72, 0x06, 0xB3, 0x3E, - 0xA6, 0xF2, 0x28, 0x46, 0x8B, 0x2C, 0xEB, 0xED, 0x03, 0xF3, 0x56, 0x5C, 0xF0, 0x40, 0xE6, 0xED, - 0xA6, 0x9C, 0x7C, 0xD9, 0x02, 0xA2, 0x48, 0xCA, 0x98, 0x25, 0xD6, 0xCE, 0x4E, 0x14, 0x2B, 0x18, - 0xE7, 0xC0, 0xE5, 0xAE, 0x93, 0x00, 0xC2, 0x45, 0x01, 0xB3, 0xC0, 0x8C, 0x74, 0xFA, 0xF7, 0x80, - 0x84, 0xAC, 0xAE, 0x6C, 0xCF, 0x00, 0x04, 0xA4, 0x47, 0x2C, 0x02, 0xCE, 0x6A, 0xF2, 0x06, 0x9D, - 0x98, 0xE5, 0x3C, 0xB0, 0x98, 0xB5, 0x4D, 0xD3, 0x40, 0x04, 0x05, 0x28, 0x57, 0x40, 0x04, 0x0A, - 0x50, 0x2A, 0x4B, 0x02, 0x1F, 0x53, 0xBD, 0x74, 0x54, 0x59, 0xBF, 0x1B, 0x5A, 0x41, 0xAC, 0x65, - 0xA7, 0xF0, 0x33, 0x0C, 0x58, 0x6D, 0xB6, 0x69, 0x68, 0x4F, 0x2B, 0x87, 0x55, 0xB7, 0x38, 0x50, - 0xC8, 0x5D, 0x7E, 0x8A, 0x1D, 0x0C, 0x04, 0x8F, 0xCD, 0x1D, 0x94, 0xDD, 0x4F, 0xA1, 0xEC, 0xA1, - 0x7C, 0xAD, 0x8D, 0xC5, 0x52, 0x4D, 0xBE, 0x0A, 0x3E, 0x9E, 0x55, 0x32, 0xDA, 0xA3, 0x37, 0x03, - 0xBD, 0x44, 0x3D, 0x8F, 0x55, 0x34, 0x3B, 0x6E, 0x4F, 0x03, 0x99, 0x73, 0x89, 0x93, 0xE5, 0xFE, - 0x74, 0x13, 0xB7, 0xCB, 0xE6, 0xA4, 0x4B, 0x63, 0x90, 0x65, 0xED, 0x16, 0x25, 0x77, 0xE9, 0x54, - 0x67, 0x11, 0xDB, 0x89, 0x44, 0xB3, 0x6E, 0xF6, 0x89, 0x7E, 0x37, 0x05, 0x47, 0xE7, 0xB2, 0xFB, - 0xF5, 0x2D, 0x70, 0xDD, 0x80, 0x37, 0xDE, 0x0A, 0x8E, 0xFB, 0x06, 0x1C, 0x00, 0x4D, 0xE0, 0x1E, - 0x80, 0x0C, 0x73, 0xFB, 0x47, 0x00, 0xE8, 0x2C, 0x89, 0x6E, 0xCF, 0x0C, 0x66, 0x87, 0x64, 0x55, - 0x73, 0x87, 0xB5, 0xD2, 0x39, 0xF2, 0x54, 0xA3, 0xAD, 0x24, 0xEF, 0x09, 0xC4, 0x2E, 0x47, 0x0C, - 0x83, 0x4F, 0x44, 0x6E, 0x25, 0xA9, 0x0C, 0x8F, 0x43, 0xE0, 0xD3, 0xB4, 0x30, 0x50, 0x54, 0xF9, - 0x41, 0x57, 0xC9, 0x63, 0xE6, 0x03, 0x00, 0xC1, 0xED, 0xE9, 0xAB, 0x60, 0x4F, 0x62, 0x94, 0x41, - 0x2A, 0xFE, 0x01, 0x56, 0x11, 0x10, 0xB3, 0x65, 0x4F, 0x11, 0xCC, 0x37, 0x4A, 0x10, 0x2C, 0x8E, - 0x3C, 0x80, 0x40, 0x3A, 0x30, 0x20, 0x02, 0x96, 0x10, 0x3F, 0x13, 0x64, 0xA7, 0x1E, 0x17, 0x53, - 0x90, 0x02, 0x28, 0x87, 0x00, 0xB2, 0xC1, 0xE0, 0x29, 0x08, 0x24, 0x50, 0xC9, 0xFE, 0x37, 0x3C, - 0x28, 0x30, 0x67, 0x75, 0x01, 0x9C, 0x20, 0x4E, 0x0A, 0xE0, 0xAC, 0xAF, 0x21, 0x90, 0x3E, 0x0D, - 0x4C, 0x41, 0xBB, 0x42, 0x13, 0x05, 0x63, 0xA4, 0x64, 0x81, 0x43, 0x28, 0x40, 0xED, 0x44, 0x00, - 0x01, 0x2C, 0x78, 0x29, 0x85, 0x2C, 0x38, 0x56, 0x62, 0x1A, 0xC0, 0x1D, 0x35, 0x84, 0x00, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, - 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, - 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, - 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, - 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, - 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, - 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, - 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, - 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, - 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, - 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, - 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, - 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, - 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, - 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, - 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, - 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, - 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, - 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, - 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, - 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, - 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, - 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, - 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, - 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, - 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, - 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, - 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, - 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, - 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, - 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, - 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, - 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, - 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, - 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, - 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, - 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, - 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, - 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, - 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, - 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, - 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, - 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, - 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, - 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, - 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, - 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, - 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, - 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, - 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, - 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, - 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, - 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, - 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, - 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, - 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, - 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, - 0x0D, 0x21, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, - 0x00, 0x6A, 0x00, 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, - 0xAE, 0x6C, 0xEB, 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, - 0x0A, 0xC7, 0x03, 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, - 0xA4, 0x10, 0xA8, 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, - 0x9E, 0x18, 0x02, 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, - 0xD4, 0x81, 0x01, 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, - 0x0D, 0x08, 0x05, 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, - 0x04, 0x06, 0x10, 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, - 0x93, 0x09, 0x0C, 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, - 0x0C, 0x39, 0x9A, 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, - 0xAB, 0x5B, 0x4A, 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, - 0x0D, 0x0D, 0x07, 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, - 0x0E, 0x7B, 0x04, 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, - 0x92, 0x0C, 0x0E, 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, - 0x5B, 0x03, 0x0E, 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, - 0x98, 0xBE, 0x73, 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, - 0xB0, 0x6A, 0xA3, 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, - 0xBC, 0xA5, 0x0C, 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, - 0x04, 0x32, 0x53, 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, - 0x67, 0x8E, 0xA2, 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, - 0x33, 0xA0, 0x2B, 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, - 0x0E, 0x10, 0xC0, 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, - 0x0A, 0x08, 0xF8, 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, - 0x1D, 0x8A, 0x47, 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, - 0x22, 0x22, 0x82, 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, - 0x6B, 0x50, 0xE0, 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, - 0xA3, 0x2D, 0x60, 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, - 0x94, 0x14, 0x7F, 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, - 0x05, 0x30, 0x4A, 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, - 0x53, 0x20, 0xC1, 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, - 0x10, 0x03, 0x80, 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, - 0x26, 0x0F, 0x28, 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, - 0xC6, 0x0A, 0x02, 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, - 0xA0, 0xC0, 0x28, 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, - 0x42, 0x01, 0x5A, 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, - 0x88, 0x33, 0x14, 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, - 0x6A, 0xC4, 0x80, 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, - 0x28, 0xC2, 0xE2, 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, - 0x21, 0xA4, 0x0C, 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, - 0x4A, 0x93, 0x29, 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, - 0x94, 0x0C, 0x38, 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, - 0x56, 0x74, 0x54, 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, - 0x99, 0x83, 0x8A, 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, - 0x10, 0x02, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, - 0x6A, 0x00, 0x2E, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x00, 0x40, 0x62, 0xB9, 0x28, 0x08, 0x41, 0x28, - 0x4B, 0xE9, 0xBE, 0x70, 0x2C, 0xCF, 0x74, 0x6D, 0xDF, 0xE5, 0xF1, 0x3C, 0x69, 0x52, 0x24, 0x0F, - 0x82, 0xE1, 0x60, 0x00, 0x26, 0x5A, 0xB8, 0xA4, 0x72, 0xC9, 0x94, 0xE9, 0x22, 0x12, 0xC9, 0x64, - 0xF2, 0x18, 0x04, 0xAE, 0xD7, 0x01, 0xB0, 0xD0, 0xEC, 0x7A, 0xBF, 0x31, 0x48, 0x61, 0x12, 0x95, - 0x32, 0x02, 0x8E, 0x06, 0x36, 0x70, 0x50, 0x21, 0xC1, 0xF0, 0x78, 0x72, 0x91, 0x20, 0x4B, 0x55, - 0x85, 0xF5, 0xB5, 0x81, 0xE0, 0xCA, 0xFF, 0x80, 0x32, 0x0D, 0x0F, 0x53, 0x11, 0x11, 0x04, 0x07, - 0x7A, 0x57, 0x0E, 0x04, 0x6F, 0x81, 0x8F, 0x7F, 0x0B, 0x2A, 0x06, 0x94, 0x6A, 0x8A, 0x01, 0x06, - 0x04, 0x05, 0x07, 0x0B, 0x0B, 0x44, 0x94, 0xA0, 0x43, 0x24, 0x90, 0xA4, 0x37, 0x05, 0x04, 0x96, - 0x97, 0x58, 0x45, 0x09, 0x0A, 0x0C, 0x0C, 0x0A, 0x05, 0x0D, 0xB3, 0x05, 0x0A, 0x09, 0x0C, 0x05, - 0x06, 0xA3, 0x4D, 0x9F, 0xA0, 0x0B, 0xBB, 0xA5, 0x33, 0x6D, 0x08, 0x0C, 0x89, 0xAA, 0xC7, 0x8A, - 0x07, 0xAE, 0x3E, 0xC0, 0x32, 0x10, 0x07, 0x3F, 0x2A, 0x0E, 0x8C, 0x2A, 0x08, 0xD6, 0x29, 0x0E, - 0xB9, 0xCD, 0xC1, 0x22, 0x10, 0x45, 0xD2, 0xB8, 0x07, 0x56, 0x97, 0x22, 0xC8, 0x01, 0x0D, 0x0C, - 0x3E, 0x9C, 0x25, 0xCF, 0xA7, 0x04, 0xD9, 0xE2, 0xC8, 0x03, 0xD0, 0x09, 0x04, 0xD6, 0x09, 0xBA, - 0xDC, 0x30, 0x10, 0x0B, 0x06, 0xB6, 0x2A, 0x04, 0x12, 0xDC, 0x52, 0x40, 0x90, 0x60, 0x81, 0x5C, - 0x07, 0x20, 0xA8, 0x1A, 0xD0, 0x20, 0x01, 0x35, 0x07, 0x3C, 0x1C, 0x18, 0x30, 0x87, 0xCC, 0x00, - 0x03, 0x02, 0x0F, 0x12, 0x34, 0xD8, 0xA6, 0xCF, 0x44, 0xA7, 0x8F, 0x9D, 0x0E, 0x88, 0xAC, 0x75, - 0x6B, 0x22, 0xC5, 0x4B, 0x44, 0x4E, 0xFF, 0x26, 0x63, 0x80, 0x20, 0xE3, 0x81, 0x8E, 0x5D, 0x0E, - 0xA4, 0x33, 0xA6, 0x92, 0x8D, 0x03, 0x04, 0x35, 0x2F, 0x35, 0xC0, 0x98, 0xE0, 0x25, 0x4C, 0x26, - 0x07, 0x7C, 0xE4, 0xBC, 0x82, 0x40, 0xE1, 0x50, 0x3D, 0xCA, 0x52, 0x38, 0xFA, 0x89, 0xE3, 0x47, - 0xCE, 0x1F, 0x00, 0x8E, 0xAA, 0x02, 0xC2, 0x80, 0xE9, 0x92, 0x86, 0x2A, 0x0D, 0x3C, 0xA0, 0x29, - 0x55, 0xD1, 0x00, 0x8C, 0x06, 0xAC, 0x26, 0x31, 0x80, 0xC0, 0x24, 0x32, 0x04, 0x09, 0xBA, 0x9A, - 0x1B, 0x94, 0x80, 0xA3, 0x58, 0x18, 0x5A, 0xCF, 0x1C, 0x13, 0x78, 0x69, 0x9C, 0xDA, 0x2C, 0x04, - 0x22, 0x34, 0x78, 0x6B, 0x03, 0x82, 0x02, 0x44, 0x97, 0x5E, 0xD5, 0x1D, 0x60, 0xF7, 0xEE, 0xB9, - 0x07, 0x61, 0xF9, 0xD6, 0x58, 0xC0, 0x20, 0xDB, 0x1A, 0x82, 0x0B, 0x09, 0x17, 0xBE, 0xFB, 0x55, - 0x81, 0xE2, 0x1B, 0x99, 0x10, 0x39, 0xF6, 0x2A, 0x99, 0xB0, 0x61, 0x3D, 0x0E, 0x2F, 0xDF, 0x18, - 0x94, 0x87, 0xF3, 0x00, 0x01, 0x02, 0x24, 0x7F, 0x7E, 0x8C, 0x60, 0xA9, 0x68, 0x17, 0x6D, 0xCC, - 0x62, 0x91, 0x8C, 0x1A, 0x75, 0xE7, 0x2B, 0x30, 0xBA, 0xF2, 0xF1, 0xF9, 0xDA, 0x45, 0x26, 0xAE, - 0x59, 0x4E, 0xD7, 0x4E, 0xED, 0x19, 0xF7, 0x8C, 0xA3, 0x5A, 0xF7, 0xF6, 0x16, 0xF1, 0xC3, 0x68, - 0x70, 0xE1, 0xC3, 0x8B, 0xAF, 0xA1, 0x31, 0xF4, 0xB4, 0xD2, 0xD7, 0x10, 0x60, 0xE9, 0x21, 0x3C, - 0x3C, 0xFA, 0x64, 0xE3, 0x32, 0x6A, 0xD2, 0x56, 0xF0, 0xC0, 0x0F, 0x5F, 0x3A, 0xA9, 0x02, 0x70, - 0xEF, 0x4E, 0x5C, 0x3A, 0x68, 0xD7, 0xE5, 0x54, 0x72, 0xB7, 0xF2, 0xD5, 0xBC, 0xD5, 0xA0, 0x34, - 0xD7, 0xDB, 0x6E, 0xAF, 0x5A, 0x95, 0x03, 0xDE, 0x2F, 0x98, 0x73, 0xDB, 0x70, 0x01, 0xC4, 0xF2, - 0x96, 0x45, 0xB3, 0x41, 0x67, 0x5B, 0xFF, 0x7F, 0xE6, 0x00, 0x20, 0x51, 0x0C, 0xF2, 0xD0, 0x47, - 0x9C, 0x7A, 0xEA, 0x91, 0x07, 0x20, 0x4C, 0x05, 0xC8, 0xA5, 0x9E, 0x70, 0xA7, 0x31, 0x78, 0x92, - 0x24, 0x89, 0xB9, 0x70, 0x4C, 0x26, 0x41, 0x04, 0xA4, 0x40, 0x03, 0x19, 0x32, 0x72, 0xC8, 0x85, - 0x1D, 0xB9, 0x92, 0x20, 0x7F, 0xEE, 0x9D, 0x94, 0xE1, 0x49, 0x0C, 0xDD, 0x84, 0x16, 0x79, 0x11, - 0xF4, 0x11, 0x0B, 0x42, 0x62, 0x41, 0x70, 0xCB, 0x73, 0x1E, 0x0E, 0xE5, 0x20, 0x70, 0x7B, 0x10, - 0x40, 0x58, 0x5E, 0xC4, 0xE4, 0x23, 0x42, 0x02, 0x86, 0x34, 0xA2, 0x98, 0x24, 0x0A, 0x00, 0x19, - 0x63, 0x4E, 0xFD, 0x10, 0x43, 0x93, 0x32, 0x79, 0xF1, 0x90, 0x42, 0x88, 0x2E, 0x2C, 0xF0, 0x80, - 0x21, 0x0F, 0x30, 0x00, 0x5F, 0x30, 0x62, 0xF4, 0xB1, 0x61, 0x90, 0x47, 0x39, 0x18, 0xCB, 0x3F, - 0xEF, 0xF8, 0x60, 0xC0, 0x2F, 0x36, 0x3C, 0xB1, 0x22, 0x53, 0x99, 0x20, 0x90, 0x08, 0x9A, 0x5D, - 0x79, 0xD9, 0x80, 0x00, 0x29, 0xB4, 0xD6, 0x04, 0x59, 0x2C, 0xFC, 0x24, 0x13, 0x2A, 0x78, 0x76, - 0xF5, 0x95, 0x03, 0x0C, 0xA0, 0xA6, 0xA3, 0x17, 0x6E, 0x3D, 0xB2, 0xC0, 0x0F, 0x0A, 0x10, 0xB9, - 0xDA, 0x39, 0x08, 0x08, 0x90, 0x86, 0x00, 0x4E, 0x2E, 0x17, 0x86, 0x3B, 0xA5, 0x4D, 0xAA, 0x87, - 0x42, 0xAF, 0x0C, 0xE2, 0x00, 0xA6, 0x63, 0x8A, 0xF6, 0xA8, 0x0A, 0x91, 0x06, 0xF0, 0x4B, 0x9E, - 0xCE, 0x29, 0xF2, 0x17, 0x4B, 0x96, 0x16, 0x50, 0xA9, 0x9F, 0x9A, 0x02, 0x50, 0x00, 0x23, 0x81, - 0x9E, 0x1A, 0x41, 0x94, 0x47, 0x31, 0x10, 0xE6, 0x01, 0xD3, 0x98, 0xA5, 0x10, 0x2A, 0x2D, 0xF1, - 0x21, 0x46, 0x40, 0xB5, 0xDA, 0x8A, 0xD8, 0x02, 0x0D, 0x21, 0x9A, 0x80, 0x99, 0x98, 0xE0, 0x63, - 0xCE, 0x10, 0x01, 0xC8, 0x7A, 0xC2, 0x79, 0x11, 0x50, 0x65, 0x26, 0x2B, 0x9F, 0x64, 0xF9, 0xD1, - 0xE8, 0x93, 0x29, 0x32, 0x30, 0x84, 0x03, 0x0A, 0x28, 0x04, 0x2C, 0x46, 0x0B, 0x5C, 0x91, 0xAE, - 0x22, 0x64, 0x21, 0xB0, 0x13, 0x30, 0x99, 0xDC, 0xB4, 0xD3, 0xB3, 0xE9, 0x00, 0x50, 0x6A, 0xB2, - 0x00, 0xB4, 0xD1, 0x42, 0x50, 0x42, 0x00, 0xF6, 0x55, 0x51, 0xAA, 0x6A, 0x82, 0x09, 0x22, 0x08, - 0x38, 0xB0, 0x4D, 0xB7, 0x88, 0x2C, 0x60, 0x0D, 0x8B, 0xF8, 0xBE, 0x90, 0x89, 0x02, 0x0E, 0xE0, - 0x03, 0x81, 0xC0, 0x99, 0xDC, 0xAA, 0x2A, 0x44, 0xB3, 0xF8, 0xE9, 0x16, 0x04, 0x08, 0xD8, 0xC2, - 0x1C, 0x97, 0x0D, 0xCB, 0xE0, 0xC9, 0x28, 0x3A, 0x28, 0xF3, 0x5F, 0x6B, 0xBF, 0xD9, 0x72, 0xC4, - 0x62, 0x1D, 0x7F, 0x1B, 0x72, 0x0D, 0xCA, 0xBC, 0xD4, 0x86, 0x2D, 0x3E, 0xB9, 0xBC, 0x5C, 0x08, - 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, 0x6A, 0x00, - 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, 0xAE, 0x6C, 0xEB, - 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, 0x0A, 0xC7, 0x03, - 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, 0xA4, 0x10, 0xA8, - 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, 0x9E, 0x18, 0x02, - 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, 0xD4, 0x81, 0x01, - 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, 0x0D, 0x08, 0x05, - 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, 0x04, 0x06, 0x10, - 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, 0x93, 0x09, 0x0C, - 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, 0x0C, 0x39, 0x9A, - 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, 0xAB, 0x5B, 0x4A, - 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, 0x0D, 0x0D, 0x07, - 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, 0x0E, 0x7B, 0x04, - 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, 0x92, 0x0C, 0x0E, - 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, 0x5B, 0x03, 0x0E, - 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, 0x98, 0xBE, 0x73, - 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, 0xB0, 0x6A, 0xA3, - 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, 0xBC, 0xA5, 0x0C, - 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, 0x04, 0x32, 0x53, - 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, 0x67, 0x8E, 0xA2, - 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, 0x33, 0xA0, 0x2B, - 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, 0x0E, 0x10, 0xC0, - 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, 0x0A, 0x08, 0xF8, - 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, 0x1D, 0x8A, 0x47, - 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, 0x22, 0x22, 0x82, - 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, 0x6B, 0x50, 0xE0, - 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, 0xA3, 0x2D, 0x60, - 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, 0x94, 0x14, 0x7F, - 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, 0x05, 0x30, 0x4A, - 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, 0x53, 0x20, 0xC1, - 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, 0x10, 0x03, 0x80, - 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, 0x26, 0x0F, 0x28, - 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, 0xC6, 0x0A, 0x02, - 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, 0xA0, 0xC0, 0x28, - 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, 0x42, 0x01, 0x5A, - 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, 0x88, 0x33, 0x14, - 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, 0x6A, 0xC4, 0x80, - 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, 0x28, 0xC2, 0xE2, - 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, 0x21, 0xA4, 0x0C, - 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, 0x4A, 0x93, 0x29, - 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, 0x94, 0x0C, 0x38, - 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, 0x56, 0x74, 0x54, - 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, 0x99, 0x83, 0x8A, - 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, 0x10, 0x02, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, - 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, - 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, - 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, - 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, - 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, - 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, - 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, - 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, - 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, - 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, - 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, - 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, - 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, - 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, - 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, - 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, - 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, - 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, - 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, - 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, - 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, - 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, - 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, - 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, - 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, - 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, - 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, - 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, - 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, - 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, - 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, - 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, - 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, - 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, - 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, - 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, - 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, - 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, - 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, - 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, - 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, - 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, - 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, - 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, - 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, - 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, - 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, - 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, - 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, - 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, - 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, - 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, - 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, - 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, - 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, - 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, - 0x0D, 0x21, 0x00, 0x00, 0x3B}; +#include + +static const uint8_t firebase_png[] PROGMEM = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, + 0x70, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, + 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, + 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x55, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5E, 0xED, 0xDD, 0x59, + 0x90, 0x1D, 0xF5, 0x81, 0xE7, 0x7B, 0x3F, 0xF4, 0xC3, 0x84, 0x91, 0x40, 0x6E, 0xEF, 0xC6, 0x8B, + 0x30, 0x92, 0x4A, 0x1B, 0x58, 0x98, 0xCD, 0xA0, 0xA5, 0x4A, 0x12, 0x20, 0x6C, 0x9A, 0xB6, 0xBA, + 0x01, 0x63, 0x4F, 0x3F, 0x8C, 0x1E, 0x26, 0xC2, 0xF7, 0xE5, 0xC6, 0x25, 0xE2, 0xDE, 0x97, 0x3B, + 0x7D, 0x67, 0xAC, 0x6E, 0x63, 0x63, 0x83, 0x8D, 0xBC, 0xB1, 0x99, 0xA5, 0x10, 0x08, 0xED, 0x52, + 0x69, 0xAD, 0x92, 0xA0, 0xBB, 0xAB, 0xA7, 0xED, 0x6E, 0x77, 0xB7, 0x81, 0x42, 0x9B, 0x99, 0x89, + 0x89, 0xDB, 0x9A, 0xB9, 0x0E, 0x2F, 0x2C, 0x76, 0x61, 0x16, 0x49, 0x85, 0xA4, 0xFC, 0xDF, 0xFF, + 0x3F, 0x4F, 0xE6, 0xA9, 0x3C, 0x79, 0x7E, 0xE7, 0x9C, 0xCC, 0x3C, 0x99, 0x75, 0xCE, 0xC9, 0xFA, + 0x7E, 0x22, 0x7E, 0xA1, 0x6E, 0x2F, 0x42, 0xC2, 0x94, 0xE2, 0xFF, 0x8D, 0x7F, 0x65, 0x9E, 0xF7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x82, 0x79, 0x6E, 0xCD, 0x80, 0xBF, 0xC3, 0x5F, 0x58, 0x12, 0xFC, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x1F, 0x73, 0x78, 0xCD, 0x3A, 0xEF, 0xB9, 0x35, 0x63, 0xE6, + 0xB9, 0x9B, 0x4D, 0x75, 0xCF, 0xAE, 0xF1, 0xE7, 0x3D, 0x7B, 0xD3, 0xA0, 0x0B, 0x92, 0xE0, 0x3F, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x98, 0x91, 0x35, 0xB3, 0xEB, 0xC2, 0x23, 0x12, 0x1F, 0xD1, + 0x79, 0x87, 0xD7, 0x6C, 0x08, 0xFE, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x8E, 0xFB, 0x16, 0x2B, + 0xEF, 0xD9, 0x35, 0xE3, 0x95, 0xE0, 0xB8, 0xD1, 0x98, 0x03, 0x57, 0x19, 0xB3, 0x7B, 0xAE, 0x31, + 0x3B, 0x3E, 0x65, 0xCC, 0xB6, 0x8F, 0x1A, 0xB3, 0xF3, 0xD3, 0xC6, 0x0C, 0x2D, 0x34, 0x66, 0x78, + 0xD9, 0x64, 0x84, 0x3C, 0x7B, 0xD3, 0x60, 0xF0, 0x5F, 0x07, 0x00, 0x00, 0x00, 0x80, 0xE4, 0xAA, + 0x37, 0x1F, 0x2E, 0x3C, 0x76, 0x7C, 0xC2, 0x98, 0xED, 0x1F, 0xAB, 0x84, 0x87, 0xDA, 0x2E, 0x1B, + 0x26, 0x87, 0x6F, 0xA8, 0x84, 0xC8, 0xA1, 0x9B, 0xD6, 0x06, 0x3F, 0x05, 0x00, 0x00, 0x00, 0x00, + 0xB4, 0xE6, 0x9E, 0xF9, 0xF0, 0xE3, 0x63, 0xCF, 0xA2, 0x4A, 0x78, 0x34, 0x8B, 0x8F, 0x70, 0xDB, + 0x3F, 0x6E, 0xCC, 0xC8, 0x0A, 0xF7, 0xAD, 0x58, 0xE3, 0x66, 0xB4, 0x7F, 0x56, 0xF0, 0x53, 0x01, + 0x00, 0x00, 0x00, 0x40, 0x73, 0xDE, 0x73, 0x37, 0x9F, 0x34, 0xFB, 0xAF, 0x48, 0x1E, 0x1F, 0xE1, + 0x76, 0x5E, 0x62, 0xCC, 0xE1, 0xD5, 0x76, 0x37, 0xAE, 0x0F, 0x7E, 0x2A, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xCC, 0x3D, 0x78, 0x6E, 0x0E, 0xAF, 0x4A, 0x1F, 0x1F, 0xFE, 0x3E, 0x62, 0xCC, 0xBE, 0xCF, + 0x18, 0xEF, 0xF0, 0x4D, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x68, 0xCD, 0xFF, 0x8C, 0x8F, 0xBD, + 0x97, 0x67, 0x8B, 0x0F, 0xB7, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x20, 0x19, 0x1B, + 0x20, 0xEB, 0xCD, 0xAE, 0x4F, 0xC7, 0xE2, 0xA2, 0xD5, 0x82, 0xF8, 0x08, 0xB7, 0x67, 0xB1, 0x31, + 0xCF, 0xDE, 0x64, 0xDC, 0x6D, 0x4A, 0xF0, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x40, 0x3D, 0xEF, 0xB9, + 0x9B, 0x07, 0x75, 0x64, 0x34, 0x5A, 0x2C, 0x3E, 0xC2, 0x1D, 0x1A, 0xE0, 0xB5, 0xBC, 0x00, 0x00, + 0x00, 0x00, 0x9A, 0xF3, 0x0E, 0x2E, 0x1B, 0xD5, 0xA1, 0xA1, 0x26, 0xC2, 0x23, 0xDC, 0xD0, 0x02, + 0x6E, 0x41, 0x00, 0x00, 0x00, 0x00, 0x34, 0xE7, 0x1D, 0xB8, 0x76, 0x5C, 0xC7, 0x46, 0x7C, 0x22, + 0x3A, 0xE2, 0x1B, 0x59, 0x6E, 0xBC, 0xC3, 0x37, 0x8E, 0x06, 0x3F, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x4C, 0x72, 0x6F, 0xAE, 0x32, 0xFB, 0x96, 0xD8, 0x78, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x0D, 0xB5, + 0x5D, 0x73, 0x2A, 0xB7, 0x20, 0xCF, 0xAD, 0x19, 0x08, 0xFE, 0x12, 0x00, 0x00, 0x00, 0x00, 0x50, + 0xE1, 0xBF, 0x01, 0x6B, 0x77, 0x9F, 0x8D, 0x07, 0x15, 0x1D, 0xE1, 0x44, 0x68, 0x34, 0xDB, 0xF0, + 0xF5, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xE7, 0x7F, 0x02, 0xFA, 0xAE, 0xB9, 0x36, 0x1C, + 0x54, 0x78, 0xB8, 0x89, 0xC0, 0x68, 0xB5, 0x5D, 0x97, 0x1A, 0xF3, 0xEC, 0x8D, 0xC6, 0x3C, 0xB7, + 0x9A, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x93, 0x6C, 0x80, 0xAC, 0xD7, 0xE1, 0xE1, 0x26, 0xE2, + 0xA2, 0xE5, 0x3E, 0x5C, 0xD9, 0x81, 0xAB, 0x8C, 0xF7, 0xEC, 0x0D, 0x27, 0x83, 0xBF, 0x0C, 0x00, + 0x00, 0x00, 0x00, 0xBC, 0xE7, 0x3D, 0xDE, 0xF0, 0x8A, 0x06, 0x6F, 0xC0, 0x52, 0x71, 0xD1, 0x6A, + 0x41, 0x7C, 0xB8, 0xED, 0xF8, 0x44, 0xE5, 0x16, 0xE4, 0xF0, 0x8D, 0xEB, 0x82, 0xBF, 0x14, 0x00, + 0x00, 0x00, 0x80, 0xE9, 0xCE, 0x3B, 0x70, 0xFD, 0xC9, 0xDC, 0xE3, 0x23, 0x1C, 0xB7, 0x20, 0x00, + 0x00, 0x00, 0x00, 0xA2, 0xCC, 0xFE, 0x2B, 0x6D, 0x2C, 0x14, 0x10, 0x1F, 0x6E, 0xEE, 0x16, 0xE4, + 0xF0, 0x2A, 0x77, 0x0B, 0xB2, 0x3E, 0xF8, 0xCB, 0x01, 0x00, 0x00, 0x00, 0x98, 0xAE, 0x6C, 0x18, + 0x2C, 0x31, 0x7B, 0x2E, 0xB3, 0xB1, 0x50, 0x40, 0x7C, 0x84, 0xDB, 0x77, 0xB9, 0x7B, 0x23, 0xD6, + 0xB8, 0x7B, 0xDD, 0x6F, 0xF0, 0x97, 0x05, 0x00, 0x00, 0x00, 0x30, 0x1D, 0x99, 0x43, 0x37, 0xAD, + 0x9D, 0x7C, 0x03, 0x96, 0x8A, 0x8B, 0x56, 0x13, 0xC1, 0x11, 0xDF, 0xF6, 0x8F, 0x71, 0x0B, 0x02, + 0x00, 0x00, 0x00, 0x20, 0x78, 0x03, 0xD6, 0xF6, 0x8F, 0xDB, 0x50, 0x50, 0x71, 0xD1, 0x6A, 0x22, + 0x36, 0x1A, 0x8D, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB3, 0x37, 0x0D, 0xEA, 0xB8, 0x68, + 0x35, 0x11, 0x19, 0xAD, 0x76, 0xA8, 0xDF, 0x3D, 0x90, 0x3E, 0x18, 0xFC, 0xA5, 0x01, 0x00, 0x00, + 0x00, 0x4C, 0x37, 0xDE, 0x81, 0x6B, 0x46, 0x75, 0x60, 0x34, 0x9B, 0x88, 0x8B, 0x56, 0xDB, 0xFA, + 0x21, 0x63, 0x86, 0xE6, 0x57, 0x5E, 0xCB, 0x3B, 0xB2, 0x66, 0x76, 0xF0, 0x97, 0x07, 0x00, 0x00, + 0x00, 0x30, 0x9D, 0x98, 0x03, 0x57, 0xDB, 0x40, 0x50, 0x91, 0xD1, 0x68, 0x22, 0x2E, 0x5A, 0xCD, + 0xC5, 0x47, 0x38, 0x6E, 0x41, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC9, 0xDD, 0x44, 0x98, 0x7D, 0x9F, + 0xB1, 0x91, 0xA0, 0x42, 0x43, 0x4D, 0xC4, 0x45, 0xAB, 0x45, 0xE3, 0xC3, 0x6D, 0xF7, 0xDC, 0xCA, + 0x2D, 0xC8, 0x73, 0xAB, 0x07, 0x82, 0x5F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC0, 0x3C, 0xB7, + 0x66, 0xC0, 0xEC, 0x9E, 0x67, 0x43, 0x41, 0xC5, 0x46, 0x7C, 0x22, 0x2E, 0x5A, 0x2D, 0x1E, 0x1F, + 0xE1, 0x86, 0xAF, 0x73, 0x0F, 0xA4, 0x8F, 0x06, 0xBF, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x81, + 0xF7, 0xEC, 0x4D, 0x77, 0x99, 0x9D, 0x97, 0xD8, 0x58, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x8B, 0x56, + 0x53, 0xE1, 0x11, 0x6E, 0xD7, 0xA7, 0xB9, 0x05, 0x01, 0x00, 0x00, 0x00, 0xA6, 0x1B, 0xEF, 0xF0, + 0x8D, 0x1B, 0x74, 0x70, 0x44, 0x27, 0xE2, 0xA2, 0xD5, 0x54, 0x74, 0xC4, 0xC7, 0x2D, 0x08, 0x00, + 0x00, 0x00, 0x30, 0xBD, 0x78, 0x07, 0xAF, 0x6F, 0xF1, 0x06, 0x2C, 0x11, 0x17, 0xAD, 0xA6, 0x62, + 0x43, 0xCD, 0x7D, 0xF6, 0x88, 0xBB, 0x05, 0x39, 0x7C, 0xE3, 0xBA, 0xE0, 0x97, 0x03, 0x00, 0x00, + 0x00, 0xA0, 0xCC, 0xBC, 0x03, 0xD7, 0x9D, 0xD4, 0xE1, 0xE1, 0x26, 0xE2, 0xA2, 0xD5, 0x54, 0x68, + 0x34, 0xDB, 0xFE, 0x2B, 0xDD, 0x1B, 0xB1, 0x4E, 0x06, 0xBF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xD6, 0xF8, 0x0D, 0x58, 0x22, 0x2E, 0x5A, 0x4D, 0x05, 0x46, 0xAB, 0xB9, 0x5B, 0x90, 0xC3, 0x37, + 0x70, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x94, 0x9D, 0xFF, 0x06, 0xAC, 0xA1, 0x05, 0x36, 0x1E, 0x3A, + 0x14, 0x1F, 0xFE, 0x3E, 0x68, 0x5C, 0x04, 0x79, 0x87, 0x6E, 0x18, 0x37, 0xA3, 0xFD, 0xB3, 0x82, + 0x5F, 0x1A, 0x00, 0x00, 0x00, 0x80, 0xB2, 0x71, 0xB7, 0x0E, 0x66, 0xD7, 0x1C, 0x1B, 0x10, 0x1D, + 0x8C, 0x0F, 0x37, 0xF7, 0xD7, 0x3D, 0xB4, 0xD2, 0x98, 0xC3, 0xAB, 0xD7, 0x07, 0xBF, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x65, 0x63, 0x03, 0x64, 0xBD, 0xD9, 0x7E, 0x71, 0x25, 0x00, 0x3A, 0x15, 0x1F, + 0xE1, 0xF6, 0x5E, 0xC6, 0x2D, 0x08, 0x00, 0x00, 0x00, 0x50, 0x66, 0xDE, 0xC8, 0xCA, 0xA1, 0xAE, + 0x88, 0x0F, 0xB7, 0xE0, 0x16, 0xC4, 0x3B, 0xBC, 0x7A, 0x43, 0xF0, 0xCB, 0x03, 0x00, 0x00, 0x00, + 0x50, 0x26, 0xDE, 0xC1, 0x6B, 0xC7, 0xBA, 0x22, 0x3E, 0xC2, 0xED, 0xEE, 0xAB, 0x3C, 0x90, 0x3E, + 0xB2, 0x66, 0x76, 0xF0, 0x4B, 0x04, 0x00, 0x00, 0x00, 0x50, 0x16, 0x66, 0xFF, 0x67, 0x75, 0x5C, + 0xB4, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0xD1, 0x11, 0xDF, 0xC8, 0x0A, 0xE3, 0x1D, 0x5A, 0x3D, 0x18, + 0xFC, 0x12, 0x01, 0x00, 0x00, 0x00, 0x94, 0x81, 0xBB, 0x65, 0x30, 0xFB, 0x2E, 0xD7, 0x81, 0xD1, + 0x6C, 0x32, 0x2C, 0x92, 0x4C, 0xC4, 0x86, 0x1A, 0xB7, 0x20, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x98, + 0xE7, 0x56, 0x0F, 0x98, 0x5D, 0x97, 0xEA, 0xC8, 0x68, 0x34, 0x19, 0x16, 0x49, 0x26, 0x42, 0xA3, + 0xD9, 0x0E, 0x7E, 0xCE, 0x78, 0x87, 0x6F, 0x18, 0x0D, 0x7E, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x7A, + 0x9D, 0xFF, 0x06, 0xAC, 0x1D, 0x9F, 0xD0, 0xA1, 0xA1, 0x26, 0xC3, 0x22, 0xC9, 0x44, 0x60, 0xB4, + 0xDA, 0xCE, 0x4B, 0x82, 0x5B, 0x90, 0xD5, 0x03, 0xC1, 0x2F, 0x17, 0x00, 0x00, 0x00, 0x40, 0x2F, + 0xF3, 0x9E, 0xBD, 0x61, 0x50, 0x86, 0x86, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0x71, 0x91, 0x74, 0x07, + 0xAF, 0x35, 0xDE, 0xE1, 0x55, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE0, 0x1D, 0xBC, 0x6E, + 0x54, 0xC6, 0x46, 0x7C, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, 0x6D, 0xF9, 0x80, 0x31, 0x3B, + 0x67, 0xBB, 0x0F, 0x26, 0x34, 0xE6, 0xD0, 0xAA, 0xB5, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, 0x40, + 0xAF, 0xF2, 0x0E, 0x5C, 0x33, 0x2E, 0x83, 0x23, 0x3A, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, + 0x5C, 0x7C, 0x84, 0xDB, 0x77, 0x85, 0xFB, 0x5C, 0x90, 0x93, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, + 0x40, 0x2F, 0x72, 0x9F, 0x36, 0xDE, 0xF2, 0x0D, 0x58, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, + 0x45, 0xE3, 0xC3, 0x6D, 0xDB, 0xC7, 0x2A, 0xB7, 0x20, 0x87, 0x57, 0xAD, 0x0B, 0x7E, 0xE9, 0x00, + 0x00, 0x00, 0x00, 0x7A, 0x8D, 0xFF, 0x06, 0xAC, 0xDD, 0x73, 0xEB, 0xA3, 0x23, 0x9C, 0x0C, 0x8B, + 0x24, 0x13, 0x51, 0x91, 0x74, 0xF1, 0xF8, 0x08, 0xC7, 0x2D, 0x08, 0x00, 0x00, 0x00, 0xD0, 0xDB, + 0xCC, 0xE1, 0x1B, 0xD7, 0x35, 0x7C, 0x05, 0xAF, 0x0C, 0x8B, 0x24, 0x13, 0x51, 0x91, 0x74, 0x2A, + 0x3C, 0xC2, 0x6D, 0xB5, 0xBF, 0xA6, 0x43, 0x03, 0xEE, 0x26, 0x64, 0x7D, 0xF0, 0xCB, 0x07, 0x00, + 0x00, 0x00, 0xD0, 0x4B, 0xFC, 0x57, 0xF0, 0xF6, 0x42, 0x7C, 0x84, 0xDB, 0xB3, 0xD8, 0x7D, 0x3A, + 0xFA, 0xB8, 0xFB, 0xD6, 0xB1, 0xE0, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x57, 0x78, 0xC3, 0xCB, + 0xEB, 0xDF, 0x80, 0x25, 0xC3, 0x22, 0xC9, 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x0D, 0x35, 0x6E, 0x41, + 0x00, 0x00, 0x00, 0x80, 0xDE, 0xE5, 0x1D, 0xBC, 0x76, 0xAC, 0x67, 0xE2, 0x23, 0xDC, 0x9E, 0x45, + 0xC6, 0x3B, 0xB4, 0x6A, 0xDC, 0x8C, 0xF4, 0xCF, 0x0E, 0x7E, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x7A, + 0x81, 0xD9, 0x7F, 0x45, 0x6F, 0xC5, 0x87, 0xBF, 0xF7, 0x1B, 0x33, 0xB2, 0xCC, 0x46, 0xC8, 0xCA, + 0xC1, 0xE0, 0xB7, 0x01, 0x00, 0x00, 0x00, 0xA0, 0xDB, 0x99, 0xC3, 0x37, 0x2E, 0x71, 0xB7, 0x09, + 0x3D, 0x17, 0x1F, 0x6E, 0xEE, 0xCD, 0x5D, 0x87, 0x57, 0xD9, 0x10, 0xE9, 0xCD, 0x5B, 0x10, 0xF7, + 0xEB, 0xB6, 0x1B, 0x08, 0xC6, 0x4D, 0x0E, 0x00, 0x00, 0x00, 0xCA, 0xCF, 0x1C, 0xBA, 0x69, 0xAD, + 0xFF, 0x06, 0x2C, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x5A, 0x10, 0x1F, + 0xE1, 0x7A, 0xEC, 0x16, 0xC4, 0xFF, 0xCC, 0x95, 0x91, 0xFE, 0xF5, 0xDE, 0xA1, 0x81, 0x93, 0xFE, + 0x73, 0x2C, 0x91, 0xF9, 0xFF, 0x9A, 0xFD, 0xF7, 0x78, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0xA5, 0x55, + 0x79, 0x03, 0xD6, 0x47, 0x6D, 0x10, 0xA8, 0xB8, 0x68, 0x35, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, + 0xD5, 0x62, 0xF1, 0xE1, 0xB6, 0xF3, 0x53, 0xC1, 0x2D, 0xC8, 0xEA, 0x81, 0xE0, 0xB7, 0xD4, 0xB5, + 0xCC, 0xA1, 0xFE, 0xB5, 0xDE, 0xC8, 0xC0, 0x78, 0x3C, 0x3C, 0xE2, 0xAB, 0xFC, 0x67, 0xFA, 0xD7, + 0x06, 0xFF, 0x35, 0x00, 0x00, 0x00, 0xA0, 0x3C, 0xBC, 0x67, 0x6F, 0x18, 0xD4, 0x71, 0xD1, 0x6A, + 0x22, 0x2A, 0x92, 0x4E, 0xC6, 0x45, 0xAB, 0x89, 0xF8, 0x08, 0x77, 0xF0, 0x1A, 0xE3, 0x1D, 0x5E, + 0x39, 0x1A, 0xFC, 0x96, 0xBA, 0x92, 0x19, 0xE9, 0x5F, 0x57, 0x8D, 0x8C, 0x91, 0xE5, 0xFE, 0x43, + 0xF4, 0x66, 0xC7, 0xC7, 0x6B, 0xE7, 0xFE, 0x35, 0xF7, 0xEF, 0xF9, 0xFF, 0x99, 0x7E, 0x37, 0x3E, + 0xF1, 0x1D, 0x00, 0x00, 0x00, 0xE5, 0xE2, 0x1D, 0xB8, 0x7A, 0x54, 0x07, 0x46, 0xB3, 0x89, 0xA8, + 0x48, 0x3A, 0x19, 0x17, 0xAD, 0x26, 0xA2, 0x23, 0xBA, 0x2E, 0xBF, 0x05, 0xB1, 0x21, 0x31, 0xBB, + 0x7A, 0xF3, 0x31, 0x34, 0xBF, 0xF2, 0x6B, 0xDE, 0x3C, 0x4B, 0xCF, 0xFD, 0x7B, 0x43, 0x7D, 0x7E, + 0x80, 0x78, 0x23, 0x2B, 0x78, 0xCB, 0x17, 0x00, 0x00, 0x00, 0xCA, 0xC5, 0xEC, 0xBF, 0xD2, 0x86, + 0x81, 0x8A, 0x8C, 0x46, 0x13, 0x51, 0x91, 0x74, 0x32, 0x2E, 0x5A, 0x2D, 0x12, 0x1A, 0xCD, 0xE6, + 0x6E, 0x41, 0x0E, 0xAD, 0x1A, 0x0B, 0x7E, 0x5B, 0x5D, 0xC5, 0xC6, 0xC7, 0x90, 0x1F, 0x1F, 0xBB, + 0x3E, 0xAD, 0xA3, 0xA3, 0x66, 0x17, 0x55, 0xB6, 0xF3, 0x92, 0x4A, 0x84, 0x0C, 0xAF, 0xE8, 0xEA, + 0x9B, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x31, 0xFF, 0x81, 0xE8, 0xBD, 0x97, 0xDB, 0x38, 0x50, 0xA1, + 0xA1, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x9A, 0x08, 0x8D, 0x46, 0x73, 0xCF, 0xB1, 0xB8, + 0x5B, 0x90, 0xC3, 0xAB, 0xBA, 0xEA, 0xDB, 0x96, 0x6C, 0x44, 0x0C, 0xF8, 0xF1, 0xE1, 0xDE, 0xD8, + 0x25, 0x83, 0x23, 0xBA, 0x20, 0x3E, 0x62, 0x11, 0xE2, 0x7E, 0x8E, 0xE0, 0xA7, 0x03, 0x00, 0x00, + 0x00, 0x7A, 0x97, 0x79, 0x6E, 0xF5, 0x80, 0x7F, 0x30, 0x96, 0xB1, 0x11, 0x9F, 0x88, 0x8A, 0xA4, + 0x93, 0x71, 0xD1, 0x6A, 0x22, 0x32, 0x5A, 0x6D, 0xDF, 0x12, 0xE3, 0x1D, 0x5E, 0x75, 0x32, 0xF8, + 0xED, 0x75, 0x05, 0xEF, 0xD0, 0xC0, 0xA8, 0x39, 0x70, 0x55, 0x10, 0x18, 0xCD, 0x16, 0x8B, 0x8F, + 0x70, 0xFB, 0xAF, 0xE4, 0x16, 0x04, 0x00, 0x00, 0x00, 0xE5, 0xE0, 0x3D, 0x7B, 0xC3, 0x5D, 0x66, + 0xC7, 0xA7, 0x6C, 0x24, 0xA8, 0xE0, 0x88, 0x4E, 0x44, 0x45, 0xD2, 0xC9, 0xB8, 0x68, 0x35, 0x11, + 0x17, 0x49, 0x16, 0xDC, 0x82, 0x78, 0x87, 0x56, 0xDD, 0x15, 0xFC, 0x16, 0x3B, 0xAA, 0x7A, 0xFB, + 0xE1, 0x1E, 0x30, 0x97, 0xD1, 0x11, 0x4E, 0x84, 0x47, 0xB8, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, + 0x00, 0x80, 0x72, 0xF0, 0x0E, 0xDF, 0xB8, 0x41, 0x07, 0x47, 0x74, 0x22, 0x2A, 0x92, 0x4E, 0xC6, + 0x45, 0xAB, 0x89, 0xB0, 0x48, 0xB3, 0x3D, 0x0B, 0x8D, 0x37, 0xB2, 0x72, 0xBC, 0x1B, 0x3E, 0x4B, + 0x23, 0xD9, 0xED, 0x87, 0x88, 0x8E, 0xF8, 0xB8, 0x05, 0x01, 0x00, 0x00, 0x40, 0x19, 0x78, 0x07, + 0xAF, 0x6B, 0xF1, 0x06, 0x2C, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, 0xD5, 0x44, 0x50, 0xA4, 0xDA, + 0x1F, 0x57, 0xFE, 0xDA, 0x23, 0x2B, 0xEC, 0x56, 0xAE, 0x0F, 0x7E, 0x9B, 0x1D, 0xE1, 0x3E, 0xC7, + 0xC3, 0xBF, 0xFD, 0x70, 0x7F, 0x1F, 0x65, 0x78, 0xB8, 0x89, 0xD8, 0x50, 0xF3, 0x7F, 0x4F, 0xFD, + 0xC6, 0x1C, 0xE4, 0xB3, 0x41, 0x00, 0x00, 0x00, 0xD0, 0xC3, 0xBC, 0x03, 0x9F, 0x3B, 0x59, 0x1F, + 0x1D, 0xE1, 0x22, 0x31, 0x91, 0x76, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xD9, 0xF8, 0x08, + 0xD7, 0x05, 0xB7, 0x20, 0xFE, 0xA7, 0x9A, 0xBB, 0x07, 0xFC, 0x65, 0x78, 0xB8, 0x89, 0xD0, 0x68, + 0xB6, 0xBD, 0x97, 0xB9, 0x5B, 0x90, 0xAE, 0x7A, 0xBE, 0x05, 0x00, 0x00, 0x00, 0x48, 0xA5, 0xF1, + 0x1B, 0xB0, 0x44, 0x54, 0x24, 0x9D, 0x8C, 0x8B, 0x56, 0x53, 0x41, 0x91, 0x66, 0x91, 0xF8, 0x70, + 0x73, 0xBF, 0x8E, 0xE1, 0xA5, 0xEE, 0x13, 0xC5, 0x37, 0x04, 0xBF, 0xD5, 0x29, 0x65, 0xC2, 0x0F, + 0x1D, 0x74, 0x7F, 0x2F, 0xF3, 0x88, 0x0F, 0x37, 0xF7, 0x7B, 0xF2, 0x9F, 0x05, 0x59, 0xCE, 0x87, + 0x13, 0x02, 0x00, 0x00, 0xA0, 0xF7, 0xF8, 0x6F, 0xC0, 0x72, 0x1F, 0x8A, 0x57, 0xB6, 0xF8, 0x08, + 0xB7, 0x6B, 0x8E, 0x31, 0x87, 0x56, 0xBA, 0x43, 0xFB, 0x94, 0x7F, 0x90, 0x5F, 0xF3, 0xDB, 0x0F, + 0x11, 0x17, 0x49, 0xB7, 0x67, 0xB1, 0xF1, 0x86, 0x97, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x80, 0xDE, + 0x63, 0x0E, 0xDD, 0xB4, 0xD6, 0xFF, 0x60, 0xBC, 0x32, 0xC6, 0x47, 0x38, 0x77, 0x0B, 0x72, 0x68, + 0xE5, 0x60, 0xF0, 0x5B, 0x9E, 0x12, 0xCD, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0xDD, 0x33, 0x17, 0xDA, + 0xDF, 0x93, 0xFD, 0x7B, 0x66, 0x7F, 0x4F, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0xE7, 0x98, 0xC3, + 0x37, 0xAE, 0xF7, 0x5F, 0x5B, 0x5B, 0xD6, 0xF8, 0x70, 0x0B, 0x6F, 0x41, 0x0E, 0xF7, 0x2F, 0x09, + 0x7E, 0xDB, 0x85, 0x72, 0xCF, 0x9C, 0xF8, 0xB7, 0x1F, 0xEE, 0x66, 0x29, 0xEF, 0xF8, 0x08, 0xB7, + 0x7B, 0x9E, 0xF1, 0x46, 0x96, 0x8F, 0x9B, 0xA1, 0xCE, 0xBF, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, + 0xCC, 0x1B, 0x59, 0x39, 0x54, 0xEA, 0xF8, 0x08, 0x77, 0xE0, 0x6A, 0x77, 0x0B, 0x32, 0x25, 0xAF, + 0xB0, 0x35, 0x23, 0xFD, 0xEB, 0xCD, 0xC8, 0x72, 0xFB, 0xD7, 0xB5, 0xBF, 0xC6, 0x22, 0xE2, 0xC3, + 0x6D, 0xF3, 0xFB, 0xC2, 0x5B, 0x90, 0x8E, 0xBE, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, 0xC5, 0xDB, + 0x7F, 0xED, 0x58, 0xE9, 0xE3, 0xC3, 0x6D, 0xC7, 0x27, 0xC3, 0x67, 0x41, 0x0A, 0xFD, 0x20, 0x3F, + 0xFF, 0xF6, 0x63, 0x64, 0x60, 0xBC, 0xFE, 0xF6, 0x43, 0x44, 0x45, 0xD2, 0xC5, 0xE3, 0x23, 0x1C, + 0xB7, 0x20, 0x00, 0x00, 0x00, 0xE8, 0x35, 0x66, 0xDF, 0x12, 0x1D, 0x15, 0x49, 0x27, 0xE3, 0xA2, + 0xD5, 0x54, 0x50, 0xA4, 0x99, 0x08, 0x8C, 0x56, 0x73, 0x37, 0x06, 0x53, 0x70, 0x0B, 0xA2, 0x6F, + 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8F, 0x70, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0x97, 0x98, + 0x91, 0x35, 0xB3, 0xDD, 0xE7, 0x4A, 0xC8, 0xB0, 0x48, 0x32, 0x19, 0x17, 0xAD, 0x16, 0x8F, 0x89, + 0xB4, 0x13, 0x71, 0xD1, 0x6A, 0xEE, 0xA0, 0xEE, 0xB6, 0xED, 0x63, 0x95, 0x5B, 0x90, 0x43, 0xC5, + 0x7C, 0x90, 0x9F, 0xBE, 0xFD, 0x10, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0xE2, 0xE3, 0x16, 0x04, 0x00, + 0x00, 0x00, 0xBD, 0xC2, 0x8C, 0xAC, 0x1E, 0x30, 0x3B, 0x2F, 0xD1, 0x71, 0xD1, 0x6A, 0x32, 0x2E, + 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0x30, 0x3E, 0xC2, 0xED, 0xFD, 0x8C, 0xBB, + 0x05, 0x29, 0xE4, 0x15, 0xB6, 0xEE, 0xF3, 0x46, 0xCC, 0xF0, 0xF5, 0xF6, 0xAF, 0x33, 0x45, 0xF1, + 0x11, 0xEE, 0xE0, 0xE7, 0x6C, 0x84, 0xAC, 0xE8, 0xC8, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x89, + 0x99, 0xC3, 0xAB, 0xD7, 0x9B, 0xED, 0x17, 0xEB, 0xC0, 0x68, 0x36, 0x19, 0x17, 0xAD, 0xA6, 0x82, + 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x5A, 0x3C, 0x3E, 0xDC, 0xB6, 0x7E, 0x38, 0x7C, 0x16, 0x24, 0xD7, + 0x57, 0xD8, 0xDA, 0x9F, 0x6F, 0xB6, 0xFF, 0xDA, 0x5D, 0xF7, 0x4A, 0xE3, 0xA9, 0x8C, 0x0F, 0xB7, + 0x9D, 0xB3, 0xED, 0xEF, 0x67, 0x85, 0xFB, 0x3D, 0x4D, 0xF9, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x79, 0x87, 0x57, 0x6F, 0x90, 0x81, 0xD1, 0x6C, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, + 0x89, 0xB8, 0x68, 0x35, 0x15, 0x1F, 0xE1, 0x82, 0x5B, 0x10, 0xF7, 0x2D, 0x53, 0xC1, 0xDF, 0x8A, + 0xB6, 0x79, 0x87, 0x06, 0x06, 0x27, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8C, 0x24, 0x73, + 0xB7, 0x20, 0xC3, 0x2B, 0xA6, 0xF4, 0xB3, 0x4E, 0x00, 0x00, 0x00, 0x80, 0x54, 0xBC, 0x83, 0x9F, + 0x1B, 0x95, 0x91, 0xD1, 0x68, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0x35, + 0x15, 0x1D, 0xD1, 0xB9, 0x5F, 0x97, 0x7F, 0x63, 0xB0, 0x32, 0x97, 0x87, 0xB7, 0x6B, 0x6F, 0x3F, + 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8B, 0xA4, 0xE3, 0x16, 0x04, 0x00, 0x00, 0x00, 0xDD, 0xCE, 0xDB, + 0x7F, 0xD5, 0xB8, 0x0C, 0x0D, 0xB5, 0xBA, 0xB0, 0x48, 0x32, 0x15, 0x14, 0x69, 0x26, 0xE2, 0xA2, + 0xD5, 0x54, 0x70, 0xA8, 0x0D, 0x2D, 0x30, 0xDE, 0xC8, 0xCA, 0xF1, 0x3C, 0x6E, 0x41, 0xFC, 0xDB, + 0x8F, 0x03, 0xD7, 0xDA, 0x9F, 0x57, 0x44, 0x45, 0xD2, 0xA9, 0xA8, 0x48, 0x3B, 0xF7, 0x96, 0xAF, + 0xE1, 0x65, 0xDC, 0x82, 0x34, 0x60, 0xC6, 0xFE, 0xC3, 0x2C, 0xF3, 0xF2, 0xBF, 0x5F, 0x67, 0x4E, + 0x7C, 0x65, 0x7D, 0x64, 0x6B, 0xDD, 0xBF, 0x1E, 0xFC, 0x47, 0x00, 0x00, 0x00, 0x50, 0x14, 0x77, + 0xF0, 0x4E, 0xFC, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, + 0x0A, 0x8D, 0x46, 0x73, 0xBF, 0xC6, 0x1C, 0x6E, 0x41, 0xDC, 0xA7, 0xAB, 0xFB, 0xB7, 0x1F, 0xEE, + 0x59, 0x1A, 0x15, 0x16, 0x49, 0xA6, 0x62, 0x22, 0xCB, 0xB6, 0x7F, 0xD4, 0xFE, 0x7E, 0x96, 0xDB, + 0x2D, 0x2D, 0xF4, 0xB3, 0x4E, 0x7A, 0x8D, 0x0B, 0x0C, 0xEF, 0xC4, 0x97, 0x07, 0xCD, 0x2F, 0xBE, + 0x62, 0xE4, 0x4E, 0x7C, 0xC9, 0xF8, 0xFF, 0x3E, 0x21, 0x02, 0x00, 0x00, 0x50, 0x1C, 0xFF, 0x0D, + 0x58, 0xBB, 0xE6, 0xE8, 0xE0, 0x88, 0xAE, 0x2E, 0x2C, 0x92, 0x4C, 0x05, 0x45, 0x9A, 0x89, 0xB8, + 0x68, 0x35, 0x15, 0x19, 0xAD, 0xE6, 0xBF, 0xC2, 0x76, 0xE5, 0x78, 0x3B, 0xDF, 0xB6, 0xE4, 0x1D, + 0x1A, 0x18, 0x35, 0xFB, 0xAF, 0xB4, 0x3F, 0x9F, 0x08, 0x8B, 0x24, 0x53, 0x21, 0x91, 0x69, 0x33, + 0x2B, 0xDB, 0x7F, 0x85, 0xF1, 0x86, 0x97, 0x4F, 0xC9, 0x27, 0xBE, 0xF7, 0x02, 0x73, 0xFC, 0xDF, + 0x2F, 0xF1, 0x7E, 0xF1, 0xE5, 0xF1, 0xDA, 0xE0, 0xB8, 0xDD, 0x98, 0xE3, 0x5F, 0xF4, 0xC3, 0x23, + 0xFA, 0xAF, 0xFB, 0xFF, 0x39, 0xFB, 0x9F, 0x0F, 0xFE, 0xAB, 0x00, 0x00, 0x00, 0xC8, 0x93, 0x39, + 0x7C, 0xE3, 0xBA, 0x96, 0xAF, 0xE0, 0x95, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, + 0xAB, 0xA9, 0xB8, 0x48, 0xBA, 0xE1, 0xEB, 0x8D, 0xFB, 0x16, 0xAA, 0xE0, 0x6F, 0x4B, 0x2A, 0x36, + 0x5C, 0x06, 0xEC, 0xB2, 0xDF, 0x7E, 0xC8, 0x90, 0xC8, 0xB2, 0x20, 0x3E, 0xDC, 0xB8, 0x05, 0xA9, + 0xAA, 0x89, 0x0F, 0x17, 0x1D, 0x2F, 0xD9, 0xFF, 0xAD, 0x5E, 0xB0, 0xB1, 0xF8, 0xFC, 0x92, 0xC9, + 0xBD, 0x70, 0xB5, 0xFD, 0xD7, 0x57, 0x55, 0xFE, 0x7D, 0x22, 0x04, 0x00, 0x00, 0xA0, 0x38, 0xFE, + 0x2B, 0x78, 0x55, 0x74, 0x84, 0x93, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, + 0xA9, 0xA8, 0x48, 0xBC, 0x59, 0x95, 0x07, 0xC7, 0xDD, 0xB7, 0x50, 0x65, 0xB8, 0x05, 0xF1, 0x86, + 0x57, 0x64, 0xBF, 0xFD, 0x90, 0x21, 0x91, 0x65, 0x91, 0xF8, 0x08, 0xC7, 0x2D, 0x48, 0x6D, 0x7C, + 0x1C, 0xB9, 0xB1, 0x36, 0x3A, 0xD4, 0x5C, 0x98, 0x1C, 0xFD, 0x42, 0x25, 0x42, 0x4E, 0x7C, 0x85, + 0x1B, 0x24, 0x00, 0x00, 0x80, 0xBC, 0x79, 0x07, 0x97, 0x35, 0x7E, 0x03, 0x96, 0x8C, 0x8B, 0x56, + 0x53, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0x85, 0x1F, 0x16, 0x68, 0xE7, + 0x6E, 0x41, 0x46, 0x06, 0x86, 0x82, 0xBF, 0x35, 0x89, 0xB4, 0x75, 0xFB, 0x21, 0x43, 0x22, 0xCB, + 0x44, 0x7C, 0xB8, 0x6D, 0xFD, 0xD0, 0xB4, 0xBE, 0x05, 0xA9, 0x89, 0x0F, 0x77, 0xEB, 0xA1, 0x82, + 0xA3, 0xD1, 0x5C, 0xAC, 0xF8, 0x37, 0x26, 0x5F, 0x29, 0xE4, 0xD3, 0xF2, 0x01, 0x00, 0x00, 0xA6, + 0x2D, 0x6F, 0xFF, 0x35, 0x63, 0xC4, 0x47, 0xB0, 0xED, 0x1F, 0xB7, 0x87, 0x75, 0xFF, 0x16, 0x24, + 0xF1, 0x81, 0xDD, 0x1B, 0x59, 0x31, 0xE6, 0x3F, 0xC4, 0xAF, 0x02, 0xA3, 0xD9, 0x64, 0x48, 0x64, + 0x99, 0x08, 0x8F, 0xE8, 0xF6, 0x2C, 0xB2, 0x51, 0xB5, 0x7C, 0x2C, 0xF8, 0xE5, 0x4E, 0x1B, 0x6D, + 0xC5, 0x47, 0xB8, 0x63, 0x7F, 0xE2, 0x3F, 0x94, 0x1E, 0xFC, 0x94, 0x00, 0x00, 0x00, 0xC8, 0x83, + 0xD9, 0xF7, 0x19, 0xE2, 0xC3, 0xED, 0x99, 0x60, 0xFB, 0xAF, 0x32, 0xDE, 0xF0, 0x40, 0xA2, 0x6F, + 0xBD, 0xB1, 0xA1, 0xB2, 0xCE, 0xBF, 0xFD, 0x70, 0x7F, 0xCF, 0x54, 0x64, 0x34, 0x9A, 0x0C, 0x89, + 0x2C, 0x13, 0xC1, 0x11, 0x9F, 0xFB, 0x7B, 0xEB, 0xDF, 0x82, 0x2C, 0xCF, 0xF5, 0x13, 0xDF, 0xBB, + 0x59, 0x2E, 0xF1, 0xE1, 0xF6, 0xC2, 0xD5, 0xEE, 0xDB, 0xB0, 0x4E, 0x06, 0x3F, 0x2D, 0x00, 0x00, + 0x00, 0xDA, 0x65, 0x0E, 0xDF, 0xB8, 0xC4, 0x0C, 0x2D, 0x9C, 0xBE, 0xF1, 0x11, 0x46, 0x47, 0xF0, + 0x7F, 0x7B, 0x6E, 0xDB, 0x3F, 0xEE, 0xBE, 0x0D, 0x2B, 0xD1, 0x2D, 0x88, 0x37, 0xBC, 0xE2, 0x64, + 0xEA, 0xDB, 0x0F, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, 0xD1, 0xDC, 0x2D, 0xC8, 0xF0, 0xB2, 0x69, + 0x71, 0x90, 0xCE, 0x2D, 0x3E, 0xC2, 0x1D, 0xBB, 0xC5, 0x04, 0x3F, 0x35, 0x00, 0x00, 0x00, 0xDA, + 0x65, 0x0E, 0xAD, 0x5A, 0x5B, 0xF3, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, + 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0xAB, 0x8F, 0x0F, 0x3F, 0x3C, 0xC2, 0x6D, 0xB2, 0xDB, 0x7B, + 0x85, 0x39, 0x3F, 0x3C, 0xD0, 0xF4, 0xC0, 0x9E, 0xE9, 0xF6, 0x43, 0x86, 0x44, 0x96, 0x89, 0xC8, + 0x68, 0x36, 0xF7, 0xF7, 0x79, 0x1A, 0xDC, 0x82, 0xE4, 0x1E, 0x1F, 0xCF, 0x7F, 0xC6, 0x98, 0x23, + 0x37, 0x18, 0xF3, 0xF2, 0x9D, 0x7C, 0x9E, 0x0A, 0x00, 0x00, 0x40, 0x1E, 0xFC, 0x37, 0x60, 0x6D, + 0xFB, 0xC8, 0xF4, 0x8D, 0x8F, 0x68, 0x78, 0xB8, 0xB9, 0xF8, 0x70, 0xDB, 0xFC, 0x21, 0xF7, 0x6D, + 0x58, 0xEE, 0x16, 0xA4, 0xE1, 0x81, 0xDD, 0xBF, 0xFD, 0xD8, 0xB3, 0xD0, 0xFE, 0x7C, 0x22, 0x34, + 0xD4, 0x64, 0x48, 0x64, 0x99, 0x08, 0x8C, 0x24, 0x1B, 0x9A, 0x5F, 0xEA, 0x5B, 0x90, 0x42, 0xE2, + 0xC3, 0x6D, 0xEC, 0x3A, 0x17, 0x20, 0xD3, 0xE6, 0xDB, 0xD7, 0x00, 0x00, 0x00, 0x0A, 0xE5, 0x1D, + 0x5A, 0x3D, 0x38, 0xED, 0xE2, 0x23, 0x12, 0x20, 0xD5, 0xE8, 0x88, 0xC4, 0xC7, 0xF9, 0x70, 0x43, + 0x97, 0x9B, 0x73, 0x07, 0xF5, 0x2D, 0xC8, 0xF9, 0x91, 0xFE, 0xF5, 0x66, 0x78, 0x99, 0xFD, 0xB9, + 0xDC, 0xAF, 0x49, 0xC4, 0x46, 0x7C, 0x32, 0x24, 0xB2, 0x4C, 0x84, 0x45, 0xD2, 0xB9, 0xDF, 0xF7, + 0xF0, 0xF5, 0xEE, 0x16, 0xA4, 0xAD, 0x4F, 0x7C, 0xEF, 0x46, 0x85, 0xC5, 0x87, 0xDB, 0x0B, 0x57, + 0x19, 0x73, 0xE2, 0xCE, 0xD2, 0xFD, 0x3D, 0x03, 0x00, 0x00, 0xE8, 0x08, 0x6F, 0xDF, 0x55, 0xA3, + 0x3A, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xC5, 0xC2, 0x22, 0xC9, 0x64, 0x54, 0x24, 0x5D, 0x24, + 0x3E, 0x82, 0xD5, 0xDC, 0x78, 0x84, 0xD1, 0xF1, 0x74, 0x64, 0x9B, 0x3F, 0x64, 0xCE, 0x1F, 0x5C, + 0x6E, 0xCE, 0x0D, 0xF7, 0xDF, 0x15, 0xFC, 0xED, 0xF2, 0x99, 0xA1, 0xFE, 0x59, 0xE7, 0x47, 0x56, + 0x8C, 0x9B, 0xDD, 0x7D, 0xF6, 0x60, 0x2F, 0x62, 0x23, 0x3E, 0x19, 0x12, 0x59, 0x26, 0xA2, 0x22, + 0xED, 0x76, 0xCF, 0x35, 0xDE, 0xC8, 0xB2, 0x71, 0xF7, 0x7B, 0x08, 0x7E, 0x3B, 0x3D, 0xAF, 0xD0, + 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x9F, 0x07, 0x02, 0x00, 0x00, 0x90, 0x07, 0xB3, 0xEF, 0x0A, + 0x7B, 0xB8, 0x57, 0x81, 0xD1, 0x6C, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, 0x48, + 0xBA, 0x48, 0x78, 0x44, 0x6F, 0x3E, 0xE2, 0xB7, 0x1E, 0x7E, 0x78, 0xBC, 0xCF, 0x9C, 0x7B, 0x6A, + 0x72, 0xE7, 0x77, 0xCE, 0x77, 0xB7, 0x20, 0x35, 0x07, 0xF6, 0xF3, 0x07, 0xFB, 0xD7, 0x7B, 0xC3, + 0xCB, 0x8C, 0xE7, 0x6E, 0x3F, 0x5A, 0x05, 0x88, 0x0C, 0x89, 0x2C, 0x13, 0x31, 0x91, 0x76, 0x9B, + 0x66, 0xD8, 0x1F, 0xED, 0xAF, 0xA9, 0x44, 0xB7, 0x20, 0x53, 0x11, 0x1F, 0x6E, 0xDE, 0xD1, 0x5B, + 0xA6, 0xDD, 0x6B, 0x8C, 0x01, 0x00, 0x00, 0x72, 0x67, 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x8B, 0xED, + 0x01, 0x5F, 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x58, 0x24, 0x99, 0x8C, 0x8A, 0xA4, + 0x8B, 0xC4, 0x47, 0x83, 0x00, 0x09, 0xE3, 0xE3, 0x5C, 0x24, 0x3E, 0xCE, 0x6E, 0x0C, 0xF6, 0xD4, + 0xFB, 0xCD, 0xF9, 0x03, 0xCB, 0x8D, 0x8B, 0x0E, 0xFF, 0xEF, 0x99, 0x0D, 0x91, 0x73, 0xC3, 0x2B, + 0xC6, 0xBD, 0x5D, 0x7D, 0xF6, 0xBF, 0x7F, 0x91, 0x3F, 0xFF, 0x50, 0xAF, 0x42, 0x44, 0x86, 0x44, + 0x96, 0x89, 0x98, 0x48, 0x3B, 0x17, 0x1F, 0xE1, 0x4A, 0x72, 0x0B, 0x32, 0x55, 0xF1, 0xE1, 0xEF, + 0xE8, 0xE7, 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xBB, 0xCC, 0xC8, 0xEA, 0x01, 0xB3, 0xEB, 0x52, + 0x7B, 0xC8, 0x57, 0xA1, 0xA1, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, + 0x4B, 0x1F, 0x1F, 0xD5, 0xF0, 0x78, 0xD2, 0x6D, 0x96, 0xBF, 0x73, 0x3B, 0xE6, 0x9B, 0xB3, 0x07, + 0x2A, 0xB7, 0x20, 0x2E, 0x44, 0xCE, 0x1F, 0x5C, 0x66, 0xFF, 0xFB, 0x7F, 0xDC, 0x3C, 0x40, 0x64, + 0x48, 0x64, 0x99, 0x88, 0x89, 0xB4, 0x8B, 0xC6, 0x87, 0x9B, 0xFB, 0xB5, 0x1E, 0xBC, 0xB6, 0xA7, + 0x6F, 0x41, 0xA6, 0x34, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0xEC, 0x5F, 0x33, 0xF8, 0xCB, 0x03, 0x00, + 0x00, 0x20, 0x0B, 0xEF, 0xD0, 0xAA, 0xBB, 0xCC, 0xF6, 0x4F, 0xD8, 0x83, 0xBE, 0x8A, 0x8D, 0xF8, + 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, 0x74, 0xB1, 0xF8, 0x08, 0x02, 0x24, + 0x49, 0x7C, 0xBC, 0x6B, 0xE3, 0xE3, 0xDD, 0xC1, 0x59, 0x93, 0xDB, 0xF8, 0x7E, 0x73, 0x6E, 0xEF, + 0xF5, 0xE6, 0xDC, 0x81, 0x81, 0x51, 0xF7, 0xED, 0x58, 0xE7, 0xB7, 0x5F, 0x62, 0xFF, 0xBB, 0x36, + 0x3E, 0xDC, 0x54, 0x84, 0xC8, 0x90, 0xC8, 0x32, 0x11, 0x13, 0x69, 0x17, 0x8F, 0x8F, 0x70, 0x3B, + 0x3E, 0xE9, 0x02, 0xC4, 0xBD, 0xE5, 0x6B, 0x76, 0xF0, 0x8F, 0x43, 0xCF, 0x98, 0xF2, 0xF8, 0x70, + 0x1B, 0x5B, 0x66, 0xCC, 0x89, 0xAF, 0xAC, 0x0D, 0x7E, 0x09, 0x00, 0x00, 0x00, 0xC8, 0xC2, 0x3B, + 0xBC, 0x7A, 0x83, 0x8E, 0x8D, 0xF8, 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, + 0x74, 0x3A, 0x3E, 0xEA, 0x02, 0xC4, 0x86, 0x47, 0xF8, 0xCC, 0x47, 0x3C, 0x3E, 0x26, 0xDC, 0x9E, + 0xA8, 0xCC, 0xFD, 0xFF, 0x67, 0xB7, 0x7C, 0xDA, 0x4C, 0x3C, 0xD3, 0x6F, 0xCE, 0x0D, 0xD9, 0x10, + 0x79, 0xCA, 0xFD, 0x77, 0x2F, 0xD2, 0x11, 0xB2, 0x49, 0x85, 0x44, 0x96, 0x89, 0x98, 0x48, 0x3B, + 0x15, 0x1E, 0xD1, 0x1D, 0xBC, 0xD6, 0xBD, 0x96, 0x77, 0x30, 0xF8, 0xC7, 0xA1, 0x27, 0x74, 0x24, + 0x3E, 0xDC, 0x5E, 0xBC, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0xB4, 0xCB, 0x3B, 0x78, 0x4D, 0x82, + 0x37, 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0xE1, 0x11, + 0x2E, 0x1E, 0x1F, 0xE1, 0xED, 0x47, 0xE4, 0x99, 0x0F, 0x17, 0x1F, 0xD1, 0xF0, 0x98, 0x78, 0x3C, + 0x98, 0xFB, 0xBF, 0x1F, 0x9B, 0x65, 0x4E, 0x3F, 0x72, 0x9D, 0x39, 0xF3, 0xF0, 0x25, 0x7E, 0x80, + 0x9C, 0xF3, 0x1F, 0x58, 0x8F, 0x05, 0x88, 0x8B, 0x8F, 0x5C, 0x02, 0x44, 0xC4, 0x44, 0xDA, 0xA9, + 0xE0, 0x88, 0xAF, 0xC7, 0x6E, 0x41, 0x3A, 0x16, 0x1F, 0xC1, 0xBC, 0x5F, 0xDC, 0x39, 0x14, 0xFC, + 0x52, 0x00, 0x00, 0x00, 0x90, 0x85, 0xB7, 0xFF, 0xEA, 0x93, 0x3A, 0x3A, 0xC2, 0xA9, 0xA0, 0x48, + 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x78, 0xB8, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, + 0xDE, 0x7E, 0xD4, 0x7E, 0xEB, 0x55, 0x34, 0x3E, 0xCE, 0xD8, 0xE8, 0x38, 0xF3, 0xF8, 0xFB, 0x82, + 0x55, 0x22, 0xE4, 0xF4, 0xFD, 0x17, 0x99, 0x53, 0xF7, 0x5E, 0x6C, 0x4E, 0xDD, 0x7D, 0x91, 0x39, + 0x6B, 0xFF, 0x33, 0x75, 0xB7, 0x20, 0x36, 0x3C, 0xDC, 0xDA, 0x0F, 0x10, 0x11, 0x13, 0x69, 0xA7, + 0x62, 0xA3, 0xD1, 0x7A, 0xE4, 0x16, 0xA4, 0xD3, 0xF1, 0xE1, 0xE6, 0x1D, 0xFB, 0x53, 0xDE, 0x84, + 0x05, 0x00, 0x00, 0xD0, 0x8E, 0xE6, 0x6F, 0xC0, 0x52, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, + 0x46, 0x45, 0xD2, 0xC5, 0xA2, 0x23, 0xBA, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDC, 0x7E, 0xD4, 0x7C, + 0xDB, 0x55, 0x18, 0x1F, 0x8F, 0xD9, 0xF0, 0xB0, 0x3B, 0x1D, 0xFC, 0x78, 0xE6, 0x91, 0xF7, 0xF9, + 0xE1, 0x11, 0xEE, 0xF4, 0x7D, 0x36, 0x42, 0x36, 0x4E, 0x46, 0x88, 0xF7, 0x74, 0x25, 0x3E, 0xAA, + 0x01, 0x92, 0x39, 0x42, 0x44, 0x4C, 0xA4, 0x9D, 0x8A, 0x8C, 0x66, 0xDB, 0x7E, 0x71, 0xD7, 0xDF, + 0x82, 0x74, 0x43, 0x7C, 0xF8, 0x3B, 0x76, 0x0B, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xC8, 0xCA, 0x7F, + 0x03, 0xD6, 0xEE, 0x79, 0xF6, 0xE0, 0x5F, 0xFE, 0xF8, 0x70, 0x3F, 0x36, 0xBF, 0xFD, 0xB0, 0xF1, + 0xE1, 0x02, 0x24, 0x88, 0x0F, 0x17, 0x1E, 0xA7, 0x1F, 0x9D, 0xDC, 0xA9, 0x7B, 0x67, 0xD5, 0x04, + 0x88, 0xDB, 0xBB, 0x3F, 0x0E, 0x02, 0xE4, 0xA9, 0x0B, 0xED, 0xCF, 0x67, 0xE3, 0x23, 0x1E, 0x21, + 0x32, 0x30, 0x9A, 0x4D, 0xC4, 0x44, 0xDA, 0xA9, 0xC0, 0x48, 0xB2, 0x7D, 0x57, 0x18, 0x6F, 0x78, + 0x79, 0x57, 0x7E, 0xD0, 0x5E, 0xD7, 0xC4, 0x87, 0x9B, 0x7B, 0x13, 0xD6, 0xCB, 0x77, 0xF6, 0xDC, + 0x43, 0xFB, 0x00, 0x00, 0x00, 0x5D, 0xC1, 0x1C, 0x5A, 0xB5, 0xD6, 0xEC, 0x9C, 0x6D, 0x0F, 0xFF, + 0x25, 0x8E, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDD, 0x7E, 0xB8, 0xD7, 0xEC, 0x46, 0x6F, + 0x3F, 0xE2, 0xF1, 0x71, 0xFA, 0x81, 0xDA, 0xDB, 0x8F, 0x70, 0xEE, 0x16, 0xE4, 0xDC, 0x93, 0x76, + 0x1B, 0x2F, 0xF2, 0x23, 0xA4, 0x26, 0x40, 0x5C, 0x50, 0xA4, 0x8A, 0x10, 0x11, 0x13, 0x69, 0xA7, + 0xC2, 0x22, 0xE9, 0xB6, 0x7D, 0x24, 0xB8, 0x05, 0x59, 0x3A, 0x10, 0xFC, 0xE3, 0xD1, 0x15, 0xBA, + 0x2A, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0xB3, 0xAB, 0xFE, 0x1E, 0x01, 0x00, 0x00, 0xF4, + 0x0C, 0x73, 0x78, 0xF5, 0x7A, 0xB3, 0xF5, 0xC3, 0x36, 0x00, 0x4A, 0x1C, 0x1F, 0xC1, 0xD4, 0xED, + 0xC7, 0x59, 0x3F, 0x40, 0x6C, 0x7C, 0xB8, 0xDB, 0x8F, 0xC8, 0xB7, 0x5E, 0x55, 0xC3, 0xE3, 0xC7, + 0x95, 0xA9, 0xDB, 0x0F, 0x7F, 0x5F, 0xBF, 0xD0, 0xBC, 0xFB, 0x48, 0x25, 0x40, 0xCE, 0xB5, 0x75, + 0x0B, 0x22, 0x62, 0x22, 0xED, 0x54, 0x54, 0xA4, 0x9D, 0xBB, 0x05, 0x39, 0xB8, 0xAC, 0x6B, 0x6E, + 0x41, 0xBA, 0x2E, 0x3E, 0xDC, 0x78, 0x13, 0x16, 0x00, 0x00, 0x40, 0x76, 0xDE, 0xC8, 0x8A, 0xA1, + 0xD2, 0xC7, 0x47, 0xC3, 0xDB, 0x8F, 0x59, 0xD5, 0xF8, 0xA8, 0xDE, 0x7E, 0x84, 0xF1, 0x11, 0x84, + 0x87, 0x1F, 0x1F, 0x3F, 0xD2, 0xB7, 0x1F, 0x2E, 0x3E, 0xDC, 0x4E, 0x7F, 0xEB, 0x42, 0x73, 0xB6, + 0xAD, 0x5B, 0x10, 0x11, 0x13, 0x69, 0xA7, 0x62, 0x22, 0xCB, 0xB6, 0xD9, 0x18, 0x1D, 0x5E, 0xD6, + 0x15, 0xB7, 0x20, 0x5D, 0x19, 0x1F, 0xC1, 0xBC, 0x13, 0x5F, 0xEE, 0xA9, 0xD7, 0x16, 0x03, 0x00, + 0x00, 0x74, 0x0D, 0x6F, 0xDF, 0x55, 0x63, 0x65, 0xBF, 0xF9, 0x70, 0xAB, 0xBF, 0xFD, 0x88, 0x04, + 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x70, 0x44, 0x02, 0xE4, 0x94, 0x7B, 0xE8, 0xDC, 0xED, 0xDB, 0xE2, + 0xF6, 0x23, 0x88, 0x8F, 0x70, 0x13, 0x0F, 0x64, 0xBD, 0x05, 0x11, 0x31, 0x91, 0x76, 0x2A, 0x24, + 0x32, 0xED, 0x82, 0xCA, 0xF6, 0x5C, 0xD6, 0xF1, 0x5B, 0x90, 0x6E, 0x8E, 0x0F, 0x37, 0xEF, 0xF8, + 0x9F, 0x75, 0xE5, 0xB3, 0x32, 0x00, 0x00, 0x00, 0x5D, 0xCF, 0xEC, 0xBD, 0xDC, 0x86, 0x40, 0x89, + 0xE3, 0x23, 0xC1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xC1, 0xB7, 0x5E, 0x9D, 0x0A, 0x03, 0xE4, 0x07, + 0xAD, 0xE3, 0xC3, 0xED, 0xF4, 0x3D, 0x2D, 0x6E, 0x41, 0xE4, 0x44, 0x4C, 0xA4, 0x9D, 0x0C, 0x89, + 0x2C, 0x0B, 0xE2, 0xC3, 0xCD, 0xFD, 0xFD, 0xF5, 0x6F, 0x41, 0x96, 0xAF, 0x0B, 0xFE, 0x31, 0x99, + 0x52, 0xDD, 0x1E, 0x1F, 0xFE, 0x8E, 0xFF, 0x29, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x48, 0xCB, 0xBD, + 0x72, 0xD5, 0xEC, 0x59, 0x64, 0x63, 0x40, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0xBA, 0xA0, + 0x48, 0x33, 0x11, 0x1A, 0x8D, 0x16, 0x0F, 0x90, 0xE0, 0xF6, 0xE3, 0x6C, 0x92, 0xDB, 0x8F, 0x87, + 0xED, 0xEE, 0x69, 0x1D, 0x1F, 0xE1, 0xC2, 0x5B, 0x90, 0x64, 0x6F, 0xC4, 0x12, 0x31, 0x91, 0x76, + 0x32, 0x24, 0xB2, 0x2C, 0x12, 0x1F, 0xE1, 0x86, 0x16, 0x1A, 0xEF, 0xE0, 0xD2, 0x93, 0xC1, 0x3F, + 0x2A, 0x53, 0xA6, 0x27, 0xE2, 0xC3, 0xED, 0xC8, 0x6A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0x90, 0x96, + 0xFF, 0x0A, 0xDE, 0x9D, 0x9F, 0xB2, 0x41, 0xA0, 0xA2, 0x22, 0xE9, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, + 0x8A, 0xA4, 0x13, 0x91, 0xA1, 0x66, 0x83, 0xA3, 0x1A, 0x1F, 0x36, 0x3C, 0x12, 0xDF, 0x7E, 0x44, + 0xBF, 0xF5, 0xEA, 0xFB, 0xB1, 0xDB, 0x0F, 0x11, 0x1D, 0xD1, 0x25, 0xBF, 0x05, 0x11, 0x31, 0x91, + 0x76, 0x32, 0x24, 0xB2, 0x4C, 0xC4, 0x87, 0x9B, 0xFB, 0x7B, 0x3D, 0xC5, 0xB7, 0x20, 0x3D, 0x13, + 0x1F, 0x3F, 0xBF, 0xDC, 0x98, 0xB1, 0x15, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x20, 0x2D, 0x73, 0x78, + 0xE5, 0x7A, 0xB3, 0xED, 0xA3, 0x36, 0x0A, 0x54, 0x58, 0x24, 0x99, 0x88, 0x8B, 0x56, 0xAB, 0x0B, + 0x8A, 0x34, 0x8B, 0x45, 0x46, 0xB3, 0x45, 0x03, 0xC4, 0xAE, 0x12, 0x1F, 0x36, 0x0E, 0xC2, 0x00, + 0x69, 0xF4, 0xDA, 0xDD, 0x46, 0xB7, 0x1F, 0x22, 0x38, 0xD4, 0x26, 0x7E, 0x50, 0x09, 0x90, 0x9A, + 0x67, 0x41, 0x82, 0x10, 0xA9, 0xDC, 0x82, 0x88, 0x98, 0x48, 0x3B, 0x19, 0x12, 0x59, 0x26, 0xC2, + 0x23, 0xBA, 0x29, 0xBC, 0x05, 0xE9, 0xA9, 0xF8, 0x70, 0x7B, 0xF1, 0x3A, 0x63, 0x7F, 0xBD, 0x77, + 0x05, 0xBF, 0x7C, 0x00, 0x00, 0x00, 0x24, 0xE1, 0x1D, 0x5E, 0xB9, 0x41, 0x87, 0x45, 0x92, 0x89, + 0xB8, 0x68, 0x35, 0x19, 0x15, 0x49, 0x17, 0x0B, 0x8C, 0x66, 0x8B, 0xC5, 0x47, 0xE5, 0xF6, 0x23, + 0x12, 0x1F, 0xCD, 0x6E, 0x3F, 0xC2, 0x00, 0x89, 0xDE, 0x7E, 0x88, 0xD0, 0x68, 0xB8, 0x6F, 0x5C, + 0x68, 0xCE, 0x3E, 0xD1, 0xE0, 0x16, 0x64, 0x93, 0x88, 0x89, 0xB4, 0x93, 0x21, 0x91, 0x65, 0x22, + 0x38, 0xE2, 0x73, 0x7F, 0xDF, 0x0F, 0x5E, 0x57, 0xF8, 0x2D, 0x48, 0xCF, 0xC5, 0x47, 0x30, 0xEF, + 0xC4, 0x9D, 0x1B, 0x82, 0xDF, 0x02, 0x00, 0x00, 0x00, 0x92, 0xF0, 0x0E, 0x5E, 0x3D, 0xAA, 0xE3, + 0xA2, 0xD5, 0x44, 0x5C, 0xB4, 0x5A, 0x5D, 0x50, 0xA4, 0x59, 0x2C, 0x30, 0x9A, 0xAD, 0x41, 0x7C, + 0xF8, 0xB7, 0x1F, 0x2E, 0x3E, 0xD4, 0xED, 0x47, 0x10, 0x1F, 0xD5, 0xDB, 0x8F, 0x87, 0xEC, 0xBE, + 0x91, 0x21, 0x3E, 0x82, 0x85, 0xB7, 0x20, 0xE7, 0x9F, 0x72, 0x7F, 0xDD, 0x20, 0x42, 0x9E, 0x9E, + 0x69, 0x7F, 0x2D, 0x2E, 0x20, 0x82, 0xA9, 0xB8, 0x68, 0x35, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, + 0xD1, 0x76, 0xCD, 0x31, 0xDE, 0xF0, 0xD2, 0x71, 0x33, 0xD4, 0x3F, 0x2B, 0xF8, 0xC7, 0x26, 0x57, + 0xBD, 0x1A, 0x1F, 0x7E, 0x80, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x74, 0xBC, 0xFD, 0x9F, 0x1D, + 0xD7, 0x81, 0xD1, 0x6C, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xC5, 0x02, 0xA3, 0xD5, 0xEA, + 0x02, 0x24, 0x88, 0x8F, 0xF8, 0xED, 0x47, 0x10, 0x1F, 0xF2, 0xD9, 0x8F, 0xFB, 0x83, 0xDB, 0x0F, + 0x11, 0x17, 0x89, 0x16, 0xBB, 0x05, 0x39, 0x1F, 0xC4, 0x47, 0x5B, 0x01, 0x22, 0x43, 0x22, 0xCB, + 0x44, 0x64, 0x34, 0x9B, 0x7B, 0x66, 0xC5, 0xDD, 0x82, 0x1C, 0x5C, 0x9A, 0xFB, 0x07, 0xEF, 0xF5, + 0x72, 0x7C, 0xB8, 0x79, 0xC7, 0xFE, 0x64, 0x3C, 0xF8, 0xAD, 0x00, 0x00, 0x00, 0xA0, 0x15, 0x33, + 0xDA, 0x3F, 0x2B, 0xFD, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x8B, 0xC5, 0x45, + 0x92, 0xC5, 0xE3, 0x23, 0x7A, 0xFB, 0x11, 0xC4, 0x47, 0xCD, 0x9B, 0xAF, 0x1A, 0xDD, 0x7E, 0xA8, + 0xB0, 0x48, 0xB1, 0x33, 0xDF, 0xBB, 0x70, 0xF2, 0x59, 0x90, 0xA7, 0x6C, 0x7C, 0xB4, 0x13, 0x21, + 0x32, 0x24, 0xB2, 0x4C, 0x04, 0x46, 0x92, 0x15, 0x70, 0x0B, 0xD2, 0xEB, 0xF1, 0xE1, 0xEF, 0xC8, + 0x4D, 0xC6, 0x8C, 0xFD, 0x87, 0x42, 0x6E, 0x86, 0x00, 0x00, 0x00, 0x4A, 0xC7, 0x7F, 0x03, 0xD6, + 0xAE, 0x4F, 0xDB, 0x48, 0x50, 0xA1, 0xA1, 0x26, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, + 0x8B, 0x56, 0xB3, 0xD1, 0x31, 0x19, 0x20, 0x17, 0x55, 0x02, 0x24, 0xB8, 0xFD, 0xA8, 0xBE, 0x76, + 0xB7, 0x7A, 0xFB, 0xF1, 0xBE, 0xC6, 0xB7, 0x1F, 0x22, 0x28, 0xB2, 0xEC, 0xEC, 0x63, 0x2E, 0x42, + 0x2A, 0x01, 0xE2, 0xDF, 0x82, 0x64, 0x89, 0x10, 0x19, 0x12, 0x59, 0x26, 0xC2, 0x22, 0xE9, 0x72, + 0xBE, 0x05, 0x29, 0x45, 0x7C, 0xB8, 0xBD, 0x34, 0xC0, 0x9B, 0xB0, 0x00, 0x00, 0x00, 0x92, 0x32, + 0x87, 0x57, 0xAD, 0x4B, 0xFE, 0x0A, 0x5E, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x61, + 0x91, 0x74, 0xF1, 0xF8, 0xC8, 0x74, 0xFB, 0xA1, 0x63, 0x22, 0xFD, 0x66, 0x9A, 0x33, 0xF7, 0xCF, + 0x34, 0x67, 0x9F, 0x9C, 0x8C, 0x90, 0xD4, 0x01, 0x22, 0x43, 0x22, 0xCB, 0x44, 0x54, 0xA4, 0xDD, + 0xCE, 0xD9, 0xC6, 0x1B, 0xBE, 0xBE, 0xED, 0x5B, 0x90, 0xD2, 0xC4, 0x87, 0x1B, 0x6F, 0xC2, 0x02, + 0x00, 0x00, 0x48, 0xCE, 0xB8, 0x57, 0xF0, 0xCA, 0xD8, 0x88, 0x4F, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, + 0x48, 0x3A, 0x11, 0x16, 0x49, 0xE6, 0x6E, 0x3F, 0xEC, 0x8F, 0x7E, 0x7C, 0xC4, 0x6E, 0x3F, 0xEA, + 0x9F, 0xFD, 0x68, 0x70, 0xFB, 0x71, 0x5F, 0xFB, 0xDF, 0x7A, 0x55, 0xD9, 0xCC, 0xEA, 0xDE, 0x0D, + 0x6E, 0x41, 0xCE, 0x35, 0xBA, 0x05, 0x51, 0xE1, 0xE1, 0x26, 0x43, 0x22, 0xCB, 0x44, 0x4C, 0x64, + 0xDA, 0x7B, 0x8D, 0x39, 0x70, 0xB5, 0x7B, 0x2D, 0xEF, 0x60, 0xF0, 0x8F, 0x50, 0x6A, 0xA5, 0x8A, + 0x0F, 0xB7, 0xE7, 0xAF, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x49, 0x79, 0x07, 0xAF, 0x4B, 0xF0, + 0x06, 0x2C, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x51, 0x91, 0x66, 0xE1, 0xCD, 0x47, + 0x83, 0xDB, 0x8F, 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0x78, 0x30, 0xAF, 0x6F, 0xBD, 0x9A, 0x8C, 0x0F, + 0xB7, 0x4C, 0xB7, 0x20, 0x32, 0x24, 0xB2, 0x4C, 0x85, 0x44, 0x96, 0xD9, 0xF8, 0x70, 0xDB, 0xFE, + 0x71, 0xF7, 0x6D, 0x58, 0xC6, 0x8C, 0x5C, 0x9B, 0xFA, 0x13, 0xC0, 0x4B, 0x17, 0x1F, 0xC1, 0xBC, + 0x13, 0x77, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x24, 0xBC, 0x7D, 0x57, 0x8E, 0xE9, 0xE8, 0x08, + 0x27, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, 0x8A, 0xA4, 0xF3, 0x6F, 0x3F, 0x82, 0xF8, + 0x88, 0xDD, 0x7E, 0xA8, 0x67, 0x3F, 0xEA, 0x3F, 0x74, 0xD0, 0xC6, 0x47, 0x2E, 0xB7, 0x1F, 0xB5, + 0xF1, 0x11, 0xEE, 0xDD, 0x47, 0x5A, 0x3C, 0x0B, 0xD2, 0x0B, 0xF1, 0xF1, 0x74, 0xB0, 0xFD, 0xE9, + 0x6F, 0x41, 0xCA, 0x1A, 0x1F, 0x6E, 0xDE, 0xB1, 0x5B, 0xA6, 0xE4, 0x83, 0x1A, 0x01, 0x00, 0x00, + 0x7A, 0x9E, 0xD9, 0x7B, 0x99, 0x8D, 0x06, 0x15, 0x1E, 0x6E, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, + 0xD2, 0xC5, 0x82, 0x22, 0xED, 0xC2, 0xF0, 0x68, 0x70, 0xFB, 0x51, 0xF7, 0xEC, 0x47, 0x10, 0x20, + 0x95, 0xDB, 0x0F, 0x1B, 0x1F, 0xB9, 0xDC, 0x7E, 0xE8, 0xF8, 0x70, 0x3B, 0x7D, 0xEF, 0x4C, 0x73, + 0xAE, 0xD1, 0x2D, 0x88, 0x1F, 0x1D, 0x3D, 0x12, 0x1F, 0xF6, 0xFF, 0xF6, 0xB6, 0x7F, 0xDC, 0x05, + 0x48, 0xE2, 0x5B, 0x90, 0x32, 0xC7, 0x87, 0xBF, 0x23, 0x37, 0x99, 0xE0, 0xB7, 0x0A, 0x00, 0x00, + 0x80, 0x46, 0xCC, 0xE1, 0xFE, 0x25, 0x66, 0x68, 0xBE, 0x0D, 0x87, 0x72, 0xC4, 0x47, 0xCB, 0xDB, + 0x0F, 0x11, 0x1F, 0x95, 0xDB, 0x0F, 0x1B, 0x1E, 0xB9, 0xDC, 0x7E, 0xE8, 0xF0, 0x88, 0x2E, 0xBC, + 0x05, 0x69, 0xF8, 0x2C, 0x88, 0x0C, 0x89, 0x2C, 0x53, 0x21, 0x91, 0x65, 0x22, 0x3E, 0xEC, 0x8F, + 0xFE, 0xF6, 0x7C, 0xC6, 0x9C, 0x3F, 0x70, 0xFD, 0x50, 0xF0, 0x8F, 0x53, 0x43, 0xA5, 0x8F, 0x0F, + 0x37, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x9A, 0xFF, 0x0A, 0x5E, 0xF9, 0x06, 0x2C, 0x11, 0x17, + 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x50, 0xA4, 0x9A, 0x8D, 0x8F, 0x30, 0x3C, 0xC2, 0xF8, 0x48, + 0x7A, 0xFB, 0xF1, 0x63, 0x1B, 0x1E, 0x2E, 0x40, 0x7E, 0xD8, 0xEE, 0xED, 0x87, 0x0E, 0x8E, 0xF8, + 0xDC, 0x2D, 0x48, 0xE3, 0x67, 0x41, 0x54, 0x48, 0x64, 0x99, 0x0A, 0x89, 0x2C, 0x6B, 0x12, 0x1F, + 0x4F, 0xBD, 0xD7, 0x9C, 0xDF, 0xF2, 0x21, 0x73, 0x7E, 0xFF, 0x32, 0x73, 0x76, 0xFF, 0xD2, 0x86, + 0x07, 0xEF, 0x69, 0x11, 0x1F, 0x6E, 0x2F, 0x5E, 0xE7, 0x02, 0x64, 0x5D, 0xF0, 0xDB, 0x06, 0x00, + 0x00, 0x80, 0xE2, 0xBF, 0x01, 0x6B, 0xEB, 0x87, 0x6C, 0x40, 0xF4, 0x7E, 0x7C, 0x64, 0xB9, 0xFD, + 0xA8, 0xC6, 0x87, 0xBB, 0xFD, 0xF8, 0x56, 0x3B, 0xB7, 0x1F, 0x3A, 0x36, 0x1A, 0x6D, 0xE2, 0x21, + 0x75, 0x0B, 0x32, 0xC3, 0x9F, 0x0E, 0x8A, 0x34, 0x53, 0x21, 0x91, 0x65, 0x2D, 0xE2, 0xC3, 0x6D, + 0xE3, 0x05, 0xE6, 0xFC, 0xEE, 0x25, 0xE6, 0xDC, 0xDE, 0x65, 0xF2, 0x01, 0xEC, 0x69, 0x13, 0x1F, + 0x6E, 0xCF, 0x5F, 0x69, 0xCC, 0x89, 0x3B, 0x73, 0xFF, 0x94, 0x78, 0x00, 0x00, 0x80, 0x52, 0xF1, + 0x0E, 0xAD, 0x1C, 0xEC, 0xF9, 0xF8, 0xC8, 0x78, 0xFB, 0x71, 0xEA, 0x51, 0x1B, 0x1D, 0xB9, 0xDC, + 0x7E, 0xE8, 0xC8, 0x68, 0xB6, 0xD3, 0xF7, 0xC4, 0x6F, 0x41, 0x2A, 0xF1, 0xD1, 0x7E, 0x80, 0xA8, + 0x90, 0xC8, 0xB2, 0x64, 0xF1, 0x71, 0xEE, 0x49, 0xBB, 0x4D, 0x1F, 0x76, 0x01, 0x62, 0xCE, 0x0E, + 0xD5, 0xDE, 0x82, 0x4C, 0xAB, 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x6F, 0xC2, 0x02, 0x00, 0x00, + 0x68, 0xC6, 0xDB, 0x77, 0x45, 0xE4, 0x15, 0xBC, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xA9, + 0xA0, 0x48, 0xB3, 0x20, 0x3E, 0x82, 0xA5, 0xBB, 0xFD, 0x88, 0xC4, 0x47, 0x5B, 0xB7, 0x1F, 0x3A, + 0x30, 0x92, 0x6C, 0xE2, 0x81, 0xE8, 0x2D, 0x88, 0x8D, 0x8F, 0xB6, 0x23, 0x44, 0x85, 0x44, 0x96, + 0x25, 0x8F, 0x8F, 0xB3, 0x83, 0x76, 0x4F, 0x5C, 0x60, 0xDE, 0xDD, 0xB1, 0xC4, 0x4C, 0xEC, 0x5E, + 0x5E, 0x3D, 0x7C, 0x4F, 0xC7, 0xF8, 0x70, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x2D, 0x98, 0x7D, + 0xF6, 0x70, 0xD7, 0xEB, 0xF1, 0x91, 0xE6, 0xF6, 0x23, 0x8C, 0x8F, 0x5C, 0x6E, 0x3F, 0x74, 0x58, + 0x24, 0x5D, 0xE5, 0x16, 0x64, 0xA6, 0x8D, 0x10, 0x1B, 0x20, 0x4F, 0xB5, 0x1B, 0x20, 0x2A, 0x24, + 0xB2, 0x2C, 0x43, 0x7C, 0x3C, 0x7E, 0x81, 0x39, 0x33, 0xF8, 0x41, 0x17, 0x20, 0xE6, 0xF4, 0x8E, + 0xA5, 0x03, 0xD3, 0x35, 0x3E, 0xFC, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x00, 0x34, 0x62, + 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x0B, 0x75, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, + 0x66, 0x91, 0xF8, 0x08, 0xA6, 0x6E, 0x3F, 0xAA, 0x01, 0x52, 0x73, 0xFB, 0x31, 0xAB, 0xFE, 0xF6, + 0xE3, 0x9B, 0x2A, 0x2E, 0x5A, 0x4D, 0x47, 0x45, 0x9A, 0xBD, 0xF3, 0xD7, 0x33, 0xCC, 0x99, 0x1F, + 0x4E, 0x06, 0x48, 0xF6, 0x5B, 0x10, 0x15, 0x12, 0x59, 0x96, 0x31, 0x3E, 0x1E, 0x9D, 0x61, 0x4E, + 0xFF, 0xD8, 0xEE, 0x99, 0x45, 0x66, 0xE2, 0xC0, 0xAA, 0x93, 0xD3, 0x36, 0x3E, 0xDC, 0x8E, 0xAC, + 0x32, 0xE6, 0xF8, 0x1D, 0x4B, 0x82, 0x2F, 0x31, 0x00, 0x00, 0x00, 0x44, 0x99, 0x91, 0xFE, 0x01, + 0xB3, 0xF3, 0x12, 0x1D, 0x18, 0xCD, 0x26, 0xA3, 0x22, 0xE9, 0x54, 0x50, 0xA4, 0x59, 0x2C, 0x3E, + 0x12, 0xDC, 0x7E, 0x4C, 0xB8, 0x00, 0x79, 0xDC, 0xDD, 0x7E, 0xC4, 0xE2, 0xC3, 0xED, 0x7B, 0x59, + 0xBE, 0xF5, 0x4A, 0x07, 0x45, 0x9A, 0xB9, 0xF8, 0xF0, 0x77, 0xF7, 0x0C, 0x7B, 0x90, 0x6F, 0xE7, + 0x16, 0x44, 0x85, 0x44, 0x96, 0xB5, 0x17, 0x1F, 0xA7, 0x1E, 0x76, 0x01, 0x32, 0xDB, 0x98, 0x63, + 0x77, 0x9A, 0x69, 0x1B, 0x1F, 0x3F, 0xBF, 0xCC, 0x98, 0x17, 0x3F, 0x67, 0xCC, 0x89, 0x2F, 0xAD, + 0x0D, 0xBE, 0xC4, 0x00, 0x00, 0x00, 0x10, 0xE5, 0x1D, 0x5A, 0x75, 0x97, 0xD9, 0x7E, 0xB1, 0x8E, + 0x8C, 0x46, 0x93, 0x51, 0x91, 0x74, 0x2A, 0x28, 0xD2, 0x2C, 0x16, 0x1F, 0x2A, 0x40, 0x1A, 0x3E, + 0xFB, 0x61, 0xE3, 0x23, 0x97, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0x56, 0x8D, 0x8F, 0x60, 0x67, 0xBE, + 0x5F, 0x09, 0x90, 0x73, 0xD1, 0x5B, 0x90, 0x20, 0x44, 0x74, 0x74, 0x84, 0x53, 0x21, 0x91, 0x65, + 0x39, 0xC4, 0xC7, 0xD3, 0x1F, 0x33, 0xDE, 0xD1, 0xDB, 0xA7, 0x77, 0x7C, 0xB8, 0x3D, 0xFF, 0x59, + 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x88, 0x19, 0x59, 0xB9, 0x5E, 0x46, 0x46, 0xA3, 0xC9, 0xA8, + 0x48, 0x3A, 0x15, 0x14, 0x69, 0x56, 0x1F, 0x1E, 0xD5, 0xF8, 0xB0, 0xE1, 0x31, 0x79, 0xFB, 0x11, + 0xF9, 0xD6, 0xAB, 0xEA, 0xED, 0xC7, 0x45, 0x95, 0xDB, 0x0F, 0xBB, 0xF6, 0x6E, 0x3F, 0x74, 0x50, + 0xA4, 0x59, 0x3C, 0x3E, 0xFC, 0x65, 0xBA, 0x05, 0x51, 0x21, 0x91, 0x65, 0xC4, 0x47, 0xFB, 0x0B, + 0xE2, 0x23, 0x98, 0xF7, 0x8B, 0x3B, 0x5B, 0x7E, 0x30, 0x23, 0x00, 0x00, 0xC0, 0xB4, 0xE4, 0x1D, + 0xB8, 0x7A, 0x54, 0x86, 0x86, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, 0x66, 0xB1, 0xF8, 0x88, + 0x07, 0x88, 0x5D, 0xF5, 0x5B, 0xAF, 0xC2, 0x00, 0xA9, 0x3E, 0x78, 0x6E, 0xE3, 0xC3, 0x06, 0x48, + 0xFB, 0xB7, 0x1F, 0x3A, 0x28, 0xD2, 0x4C, 0xC6, 0x47, 0xB0, 0xF0, 0x16, 0xC4, 0xBD, 0x92, 0xB7, + 0xF5, 0x2D, 0x88, 0x0A, 0x89, 0x2C, 0x23, 0x3E, 0xDA, 0x5F, 0x6D, 0x7C, 0xF8, 0x01, 0x72, 0xF4, + 0xD6, 0xB1, 0xE0, 0x4B, 0x0C, 0x00, 0x00, 0x00, 0x51, 0xDE, 0xFE, 0x2B, 0x4F, 0xCA, 0xD8, 0x88, + 0x4F, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x78, 0x88, 0xF8, 0x08, 0x6F, 0x3F, 0x6A, + 0x1E, 0x3C, 0xF7, 0x6F, 0x3F, 0x2E, 0x9A, 0xBC, 0xFD, 0x08, 0xE3, 0x23, 0x0C, 0x90, 0xFB, 0xD3, + 0xDC, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x1D, 0x35, 0x73, 0xB7, 0x20, 0x8F, 0x4D, 0xDE, 0x82, + 0xF8, 0x11, 0x62, 0x63, 0xA3, 0x3E, 0x40, 0x54, 0x48, 0x64, 0x19, 0xF1, 0xD1, 0xFE, 0xEA, 0xE3, + 0xC3, 0xDF, 0xB1, 0x2F, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x00, 0x25, 0xD1, 0x1B, 0xB0, 0x64, 0x54, + 0x24, 0x9D, 0x0A, 0x8A, 0x34, 0x8B, 0x85, 0x47, 0x93, 0xF8, 0xA8, 0x3E, 0x78, 0x5E, 0xBD, 0xFD, + 0xB0, 0xF1, 0x11, 0xDE, 0x7E, 0xC4, 0xE3, 0xE3, 0x21, 0xBB, 0xBB, 0x55, 0x68, 0xA8, 0xE9, 0xA0, + 0x48, 0x33, 0x19, 0x1C, 0x62, 0xA7, 0xBF, 0x3B, 0x23, 0x78, 0x16, 0xA4, 0xC1, 0xB7, 0x62, 0x3D, + 0xAD, 0x42, 0x22, 0xCB, 0x88, 0x8F, 0xF6, 0x27, 0xC2, 0x23, 0x1C, 0x6F, 0xC2, 0x02, 0x00, 0x00, + 0xA8, 0xE7, 0xBF, 0x01, 0x6B, 0xD7, 0x1C, 0x1D, 0x1D, 0xE1, 0x64, 0x54, 0x24, 0x9D, 0x0A, 0x8A, + 0x34, 0x8B, 0x85, 0x47, 0x38, 0x15, 0x20, 0xF1, 0xD7, 0xEE, 0x3E, 0x39, 0x19, 0x1F, 0xF2, 0xCD, + 0x57, 0x89, 0x6F, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0A, 0x8D, 0x66, 0x7B, 0x37, 0x7E, 0x0B, 0x12, + 0x46, 0x88, 0x8B, 0x8F, 0x70, 0x32, 0x2A, 0x92, 0x8E, 0xF8, 0x68, 0x7F, 0x22, 0x3A, 0xA2, 0x7B, + 0xF1, 0x7A, 0x63, 0x5E, 0xBE, 0xB3, 0xE6, 0x53, 0xE1, 0x01, 0x00, 0x00, 0xA6, 0x3D, 0x73, 0xA8, + 0x7F, 0xAD, 0xD9, 0xF1, 0x49, 0x1D, 0x1E, 0x6E, 0x32, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, 0xC5, + 0xA2, 0x23, 0xBA, 0x78, 0x7C, 0x44, 0x6F, 0x3F, 0x82, 0xF8, 0x98, 0xBC, 0xFD, 0x10, 0xDF, 0x7A, + 0x95, 0xF8, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xA9, 0xC0, 0x68, 0x35, 0x77, 0x0B, 0x52, 0xF7, 0xE1, + 0x84, 0x36, 0x3A, 0xF2, 0x09, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xE0, 0x88, 0xEF, 0x85, 0xAB, 0x79, + 0x13, 0x16, 0x00, 0x00, 0x40, 0x9C, 0xFF, 0x06, 0xAC, 0xAD, 0x1F, 0xEC, 0xB9, 0xF8, 0x68, 0x74, + 0xFB, 0xE1, 0xE2, 0xE3, 0x6C, 0x2C, 0x3E, 0xB2, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x17, + 0x49, 0xF7, 0xEE, 0x8F, 0x2B, 0x01, 0xE2, 0xBF, 0x96, 0xF7, 0x29, 0x1B, 0x1F, 0x6E, 0xD1, 0x08, + 0x91, 0x71, 0xD1, 0x6A, 0xC4, 0x47, 0xFB, 0x13, 0xB1, 0xD1, 0x60, 0xDE, 0x89, 0x2F, 0x0D, 0x06, + 0x5F, 0x6A, 0x00, 0x00, 0x00, 0x70, 0xBC, 0xE1, 0xE5, 0x43, 0x3D, 0x15, 0x1F, 0x6E, 0xF1, 0xF8, + 0x88, 0xDE, 0x7E, 0xB8, 0xF8, 0x68, 0x75, 0xFB, 0xF1, 0xA0, 0x9D, 0x0C, 0x8E, 0xE8, 0x74, 0x50, + 0xA4, 0x99, 0x8A, 0x8A, 0x34, 0x3B, 0xFD, 0xED, 0xE0, 0x16, 0xE4, 0x49, 0x1B, 0x20, 0x36, 0x0A, + 0xEA, 0x02, 0x24, 0x75, 0x84, 0x10, 0x1F, 0xED, 0x4F, 0x87, 0x46, 0xA3, 0x79, 0x47, 0x6F, 0x1D, + 0x0D, 0xBE, 0xD4, 0x00, 0x00, 0x00, 0xE0, 0x78, 0x7B, 0xAF, 0xA8, 0x7F, 0x05, 0xAF, 0x8C, 0x8A, + 0xA4, 0x53, 0x41, 0x91, 0x66, 0x22, 0x38, 0xA2, 0xB3, 0xD1, 0xE1, 0x7E, 0xAC, 0xBB, 0xFD, 0xB0, + 0xF1, 0x71, 0x6E, 0xE3, 0x45, 0xC9, 0x6E, 0x3F, 0xEE, 0x6B, 0x75, 0xFB, 0xA1, 0x83, 0x22, 0xCD, + 0x54, 0x50, 0x64, 0xD9, 0xBB, 0xF6, 0xD0, 0x1F, 0x06, 0x48, 0x7B, 0xB7, 0x20, 0xC4, 0x47, 0xFB, + 0xD3, 0x91, 0xD1, 0x74, 0xC7, 0x6E, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x51, 0x66, 0xEF, 0xE2, + 0xDE, 0x89, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x08, 0x03, 0x24, 0x88, 0x8F, 0x78, 0x80, 0xD4, + 0xC5, 0x47, 0xCB, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0xA6, 0x42, 0x22, 0xDB, 0x2E, 0x30, 0xA7, 0xBF, + 0x6D, 0xA3, 0x60, 0x70, 0x32, 0x42, 0x6A, 0x02, 0xC4, 0x85, 0x45, 0xA2, 0x08, 0x21, 0x3E, 0xDA, + 0x9F, 0x88, 0x8B, 0x24, 0x73, 0x6F, 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x1B, 0x00, 0x00, + 0xC0, 0xF4, 0x66, 0x46, 0xFA, 0x67, 0xD7, 0xBC, 0x82, 0x57, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, + 0x33, 0x11, 0x1B, 0xF1, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, 0xDF, 0x7E, 0xD8, 0xF0, 0x10, 0xB7, 0x1F, + 0xF2, 0x43, 0x07, 0x9B, 0xDE, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x76, 0x41, 0x75, + 0xE1, 0x2D, 0xC8, 0xB9, 0x4C, 0xB7, 0x20, 0xC4, 0x47, 0xFB, 0x13, 0x61, 0x91, 0x74, 0x63, 0xCB, + 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xC8, 0x7F, 0x05, 0x6F, 0xF8, 0x06, 0x2C, 0x19, 0x15, 0x49, + 0xA7, 0x82, 0x22, 0xCD, 0x62, 0xA1, 0xD1, 0x68, 0x61, 0x78, 0x84, 0xF1, 0xE1, 0xF6, 0xD4, 0x85, + 0x35, 0xDF, 0x7A, 0x15, 0x0F, 0x90, 0x53, 0x2E, 0x40, 0xC2, 0xDB, 0x8F, 0x1F, 0xD9, 0xC9, 0xF0, + 0x70, 0xD3, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x6C, 0x32, 0x3E, 0xDC, 0x4E, 0x7D, 0xD3, 0xC5, + 0x41, 0x96, 0x5B, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xA8, 0x48, 0xB3, 0x17, 0x3F, 0xC7, 0x9B, 0xB0, + 0x00, 0x00, 0x00, 0x42, 0xFE, 0x1B, 0xB0, 0xB6, 0x7D, 0xC4, 0x1E, 0xEE, 0x55, 0x54, 0x24, 0x9D, + 0x0A, 0x8A, 0x34, 0x13, 0xA1, 0xA1, 0x66, 0xA3, 0xC3, 0xFD, 0x58, 0x7B, 0xFB, 0x61, 0xE3, 0x23, + 0xCD, 0xED, 0xC7, 0xB7, 0x1A, 0xDD, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x56, 0x1B, + 0x1F, 0xE1, 0x26, 0x7E, 0x94, 0xF6, 0x16, 0x84, 0xF8, 0x68, 0x7F, 0x22, 0x28, 0x32, 0x8C, 0x37, + 0x61, 0x01, 0x00, 0x00, 0x04, 0xBC, 0x91, 0x81, 0x0D, 0x3A, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, + 0xC5, 0x22, 0xA3, 0xD9, 0xC2, 0xF0, 0x88, 0xC4, 0x47, 0xA2, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x68, + 0xD7, 0xA3, 0xF1, 0xE1, 0xD6, 0xF2, 0x16, 0x84, 0xF8, 0xC8, 0x79, 0x3A, 0x26, 0xB2, 0xCC, 0x3B, + 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0x38, 0xDE, 0x81, 0xAB, 0x47, 0x75, 0x58, 0x24, 0x99, + 0x0A, 0x8A, 0x34, 0x13, 0x91, 0xD1, 0x62, 0xD5, 0x00, 0x09, 0xE3, 0xA3, 0xED, 0xDB, 0x0F, 0x1D, + 0x14, 0x69, 0xA6, 0x43, 0x22, 0xCB, 0x74, 0x78, 0x44, 0x17, 0xDE, 0x82, 0xB8, 0x78, 0x68, 0x7C, + 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x12, 0x59, 0xE7, 0x1D, 0xBB, 0x65, 0x3C, 0xF8, 0x92, 0x03, + 0x00, 0x00, 0x98, 0xDE, 0xBC, 0x7D, 0x57, 0x8C, 0xEB, 0xB8, 0x68, 0x35, 0x15, 0x14, 0x69, 0xA6, + 0x03, 0xA3, 0xE1, 0x6A, 0x6E, 0x3F, 0x6C, 0x7C, 0x34, 0xBB, 0xFD, 0x08, 0xE3, 0xA3, 0xE5, 0xED, + 0x87, 0x0E, 0x8A, 0x34, 0xD3, 0x21, 0x91, 0x65, 0x3A, 0x38, 0xE2, 0xF3, 0x6F, 0x41, 0x1E, 0x6F, + 0x76, 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x11, 0x6D, 0xCD, 0xBD, 0x09, 0x6B, 0xEC, 0x8B, 0xB3, + 0x82, 0x2F, 0x3B, 0x00, 0x00, 0x80, 0xE9, 0xCB, 0x0C, 0x2D, 0xB0, 0x07, 0x7C, 0x15, 0x18, 0xCD, + 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x60, 0x24, 0x58, 0x18, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xB9, 0x1F, + 0xC9, 0x6E, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0E, 0x89, 0x2C, 0xD3, 0xB1, 0xD1, 0x68, 0x67, 0xBE, + 0x2F, 0x9E, 0x05, 0xF1, 0x43, 0x24, 0x12, 0x1E, 0x6E, 0xC4, 0x47, 0x86, 0x89, 0x78, 0xC8, 0x63, + 0x63, 0xCB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x80, 0xFF, 0x06, 0xAC, 0x9D, 0xB3, 0xED, 0xE1, 0x5E, + 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0x33, 0x1D, 0x17, 0x4D, 0x17, 0xB9, 0xF9, 0xF0, 0xE3, 0x43, + 0xDC, 0x7E, 0x4C, 0xB8, 0x3D, 0x1E, 0xB9, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xE5, 0x89, 0x0F, + 0x7F, 0x77, 0xAB, 0x5B, 0x90, 0x4A, 0x68, 0x18, 0x1B, 0x19, 0xC4, 0x47, 0xD6, 0x89, 0x70, 0xC8, + 0x65, 0x8B, 0x8D, 0x79, 0xF1, 0x5A, 0xE3, 0xFD, 0xE2, 0x4B, 0x77, 0x05, 0x5F, 0x7A, 0x00, 0x00, + 0x00, 0xD3, 0x93, 0x0D, 0x90, 0x75, 0x66, 0xC7, 0x27, 0xEC, 0x21, 0x5F, 0x85, 0x86, 0x9A, 0x0A, + 0x8A, 0x34, 0x13, 0x71, 0x91, 0x64, 0x61, 0x7C, 0xC4, 0x6E, 0x3F, 0x12, 0x3F, 0xFB, 0xF1, 0xCD, + 0x12, 0xC5, 0x47, 0xB0, 0x33, 0xDF, 0x8B, 0x3E, 0x0B, 0x52, 0x89, 0x8C, 0x70, 0x2E, 0x40, 0xC2, + 0xF0, 0x20, 0x3E, 0x92, 0x4E, 0x85, 0x43, 0x1E, 0xB3, 0xF1, 0xE1, 0x66, 0x7F, 0xED, 0xDE, 0x89, + 0x3B, 0x36, 0x04, 0x5F, 0x7A, 0x00, 0x00, 0x00, 0xD3, 0x93, 0xFF, 0x0A, 0x5E, 0x19, 0x1A, 0x6A, + 0x2A, 0x28, 0xD2, 0x4C, 0x84, 0x45, 0xAB, 0xD9, 0xF0, 0xA8, 0xC6, 0x87, 0x0D, 0x8F, 0xC9, 0xDB, + 0x8F, 0xD8, 0xB7, 0x5E, 0x45, 0x6F, 0x3F, 0xEC, 0x1A, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, + 0x12, 0x59, 0xA6, 0xC3, 0x22, 0xF1, 0xAA, 0xB7, 0x20, 0x36, 0x40, 0x36, 0xBA, 0xC0, 0x08, 0x82, + 0x23, 0x12, 0x1E, 0xC4, 0x47, 0xD2, 0xA9, 0x70, 0xC8, 0x63, 0x41, 0x7C, 0x04, 0xF3, 0x4E, 0xFC, + 0x39, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x9B, 0x77, 0xF0, 0xBA, 0x84, 0x6F, 0xC0, 0x52, 0x41, + 0x91, 0x66, 0xB1, 0xB0, 0x48, 0x3A, 0x17, 0x20, 0xE1, 0xCD, 0x87, 0x5D, 0xF5, 0x5B, 0xAF, 0xE4, + 0xB3, 0x1F, 0x17, 0xB5, 0xB8, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x04, 0x45, + 0x86, 0x9D, 0xFE, 0xCE, 0x7B, 0xFD, 0xB0, 0x08, 0x23, 0xA4, 0x1A, 0x1C, 0x2E, 0x3E, 0x82, 0x28, + 0x21, 0x3E, 0x5A, 0x4D, 0x85, 0x43, 0x1E, 0xAB, 0x8D, 0x0F, 0x3F, 0x40, 0x8E, 0x7D, 0x81, 0x37, + 0x61, 0x01, 0x00, 0x80, 0xE9, 0xCD, 0xDB, 0xFB, 0xD9, 0x31, 0x1D, 0x1C, 0xD1, 0xA9, 0xA0, 0x48, + 0xB3, 0x58, 0x54, 0x24, 0x5D, 0x2C, 0x3E, 0xC2, 0xDB, 0x8F, 0xBA, 0xF8, 0x88, 0xDE, 0x7E, 0x84, + 0xF1, 0x11, 0x06, 0xC8, 0x86, 0xF0, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xE9, 0x90, 0xC8, 0x32, 0x1D, + 0x13, 0xA9, 0xF7, 0x57, 0xEF, 0xF5, 0xF7, 0xEE, 0xA3, 0x93, 0x01, 0x12, 0x9F, 0x1F, 0x27, 0x2E, + 0x3C, 0x88, 0x8F, 0x06, 0x53, 0xE1, 0x90, 0xC7, 0xEA, 0xE3, 0xC3, 0xDF, 0x91, 0x1B, 0x4C, 0xF0, + 0xA5, 0x07, 0x00, 0x00, 0x30, 0x3D, 0x99, 0x3D, 0xF6, 0x50, 0x24, 0xA3, 0x23, 0x9C, 0x0A, 0x8A, + 0x34, 0x8B, 0x45, 0x45, 0xD2, 0x35, 0x88, 0x8F, 0xEA, 0x83, 0xE7, 0xEA, 0xF6, 0x23, 0x1E, 0x1F, + 0x0F, 0xD9, 0xDD, 0x5D, 0xEE, 0xF8, 0x70, 0x73, 0xB7, 0x20, 0x7E, 0x60, 0xD8, 0x9D, 0x7B, 0xD2, + 0x86, 0x87, 0xDD, 0xB9, 0x68, 0x7C, 0xD8, 0xF0, 0x38, 0x6B, 0xC3, 0x83, 0xF8, 0x88, 0x4F, 0x85, + 0x43, 0x1E, 0x8B, 0x45, 0x47, 0x74, 0x2F, 0xAD, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xFA, 0x32, + 0x87, 0xFB, 0x97, 0x98, 0xDD, 0xF3, 0xEC, 0x81, 0x5F, 0x85, 0x87, 0x9B, 0x0A, 0x8A, 0x34, 0x8B, + 0x45, 0x45, 0x9A, 0xD9, 0xE8, 0xA8, 0x0B, 0x90, 0x26, 0xDF, 0x7A, 0x25, 0xDF, 0x7C, 0x75, 0xBF, + 0xBB, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x45, 0xE2, 0x23, + 0x9C, 0xBB, 0x05, 0xF1, 0x43, 0x63, 0xD0, 0xC6, 0x88, 0x0D, 0x90, 0xB3, 0xB1, 0x6F, 0xB9, 0x7A, + 0xF7, 0x31, 0xE2, 0xA3, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x88, 0x8E, 0xE8, 0x5E, 0xBC, 0xD6, 0x06, + 0xC8, 0x1D, 0xEB, 0x82, 0x2F, 0x41, 0x00, 0x00, 0x80, 0xE9, 0xC5, 0x7F, 0x05, 0x6F, 0xC3, 0x37, + 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x15, 0x49, 0xF7, 0x8C, 0x88, 0x8F, 0xE8, 0xED, 0x87, 0x8B, + 0x8F, 0x9A, 0x00, 0x11, 0xDF, 0x7A, 0xE5, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, + 0x26, 0x42, 0x22, 0xCB, 0x44, 0x7C, 0xBC, 0xBD, 0xFE, 0xBD, 0xE6, 0xD4, 0x3D, 0x36, 0x42, 0x5C, + 0x68, 0xB8, 0xE0, 0x70, 0x21, 0x12, 0xFC, 0xE8, 0xFE, 0xB5, 0x09, 0x17, 0x1F, 0x36, 0x3C, 0x4E, + 0x3F, 0x42, 0x7C, 0x54, 0xA6, 0xC2, 0x21, 0x8F, 0x89, 0xE0, 0x88, 0xCF, 0xFD, 0xBE, 0x4E, 0xDC, + 0xB1, 0x3E, 0xF8, 0x12, 0x04, 0x00, 0x00, 0x98, 0x5E, 0xFC, 0x37, 0x60, 0x6D, 0xF9, 0x80, 0x3D, + 0xF4, 0x77, 0x5F, 0x7C, 0x34, 0xBA, 0xFD, 0x50, 0xAF, 0xDD, 0xD5, 0xB7, 0x1F, 0xED, 0x7F, 0xEB, + 0x95, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0x0D, 0xE2, 0x23, 0xDC, 0x99, 0x07, 0x6D, 0x6C, + 0x3C, 0x3E, 0xA3, 0x12, 0x22, 0x41, 0x78, 0x4C, 0x3C, 0x7A, 0x81, 0x7F, 0xEB, 0x41, 0x7C, 0x84, + 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x6C, 0x34, 0x98, 0x77, 0xE2, 0x0E, 0xDE, 0x84, 0x05, 0x00, 0x00, + 0xA6, 0x27, 0xEF, 0xD0, 0xCA, 0xC1, 0xAE, 0x8B, 0x8F, 0x5C, 0x6E, 0x3F, 0x2E, 0x6A, 0xFB, 0xF6, + 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0xD6, 0xD7, 0xEC, 0xBF, 0xF6, + 0x4D, 0x17, 0x1D, 0x33, 0xFC, 0x9D, 0xB1, 0xE1, 0xE1, 0xE6, 0x7F, 0xCB, 0x55, 0x18, 0x1F, 0x4F, + 0x11, 0x1F, 0xC5, 0x4C, 0x87, 0x46, 0xA3, 0x79, 0xC7, 0xBE, 0x70, 0x32, 0xF8, 0x12, 0x04, 0x00, + 0x00, 0x98, 0x5E, 0xBC, 0xBD, 0x57, 0xC4, 0x5E, 0xC1, 0xAB, 0x82, 0x22, 0xCD, 0x44, 0x54, 0x24, + 0x5D, 0x24, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0xA3, 0xFE, 0xD9, 0x0F, 0x75, 0xFB, 0x61, 0xE3, 0xE3, + 0xBE, 0xF6, 0x6E, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x4B, 0x10, 0x1F, 0x6F, 0x7D, + 0xED, 0x02, 0xF3, 0xD6, 0x7F, 0xB9, 0xC0, 0x9C, 0xFA, 0xA1, 0x0D, 0x0D, 0x77, 0xE3, 0xF1, 0x63, + 0x1B, 0x1F, 0x41, 0x78, 0xBC, 0xF3, 0x90, 0xFD, 0x91, 0xF8, 0x28, 0x68, 0x3A, 0x32, 0x9A, 0xEE, + 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x4C, 0x4F, 0x66, 0xAF, 0x3D, 0xF0, 0x75, 0x53, 0x7C, + 0x04, 0x01, 0xD2, 0xEC, 0xF6, 0x23, 0x1E, 0x20, 0x75, 0xF1, 0xF1, 0x60, 0x7B, 0x0F, 0x9E, 0xEB, + 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x96, 0x22, 0x3E, 0xDE, 0xFC, 0xCF, 0xF6, 0xC7, 0xBB, 0x6D, + 0x84, 0x04, 0xD1, 0xE1, 0xEF, 0x41, 0xE2, 0x43, 0x87, 0x43, 0x1E, 0x13, 0x71, 0x91, 0x64, 0x47, + 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xFD, 0x98, 0xD1, 0xFE, 0x59, 0x66, 0x68, 0x81, 0x0D, + 0x80, 0xEE, 0x8A, 0x0F, 0xF7, 0xA3, 0xBA, 0xFD, 0x50, 0xCF, 0x7E, 0xD4, 0x7E, 0xE8, 0xA0, 0x0D, + 0x8F, 0x87, 0xDB, 0xBB, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x2C, 0x65, 0x7C, 0xB8, + 0xFD, 0xE1, 0xFF, 0xB1, 0xFF, 0xFF, 0x77, 0x66, 0x98, 0xB7, 0x1F, 0x98, 0x69, 0x67, 0xE3, 0x63, + 0x23, 0xF1, 0x51, 0xCC, 0x44, 0x58, 0x24, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x2F, 0xAD, 0x0D, 0xBE, + 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0xFF, 0x0D, 0x58, 0x3B, 0x67, 0xDB, 0x08, 0x50, 0x41, 0x91, + 0x66, 0x22, 0x2A, 0x92, 0x2E, 0x1A, 0x1F, 0x41, 0x80, 0x34, 0xBB, 0xFD, 0x70, 0xF1, 0x11, 0x0F, + 0x90, 0x6A, 0x7C, 0xB4, 0x79, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, 0x64, 0x59, 0xC6, 0xF8, + 0x78, 0xE3, 0x2F, 0x67, 0x98, 0xF1, 0xFF, 0x3C, 0xC3, 0xBC, 0xF5, 0x23, 0xFB, 0x7B, 0x7A, 0x92, + 0xF8, 0x28, 0x66, 0x22, 0x2A, 0xD2, 0xEC, 0xF9, 0x2B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x7E, + 0xBC, 0x43, 0xAB, 0xEE, 0x32, 0xDB, 0x3E, 0x6A, 0x43, 0x40, 0x45, 0x45, 0xD2, 0x89, 0xA8, 0x48, + 0x3A, 0x11, 0x1F, 0xEE, 0xC7, 0xD4, 0xB7, 0x1F, 0x61, 0x7C, 0xB8, 0xDB, 0x8F, 0x7B, 0xB3, 0xDD, + 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x4E, 0x7C, 0xFC, 0xA7, 0x19, 0xE6, 0xF7, + 0xFF, 0xF7, 0x0C, 0xF3, 0xE6, 0x03, 0xC4, 0x47, 0x31, 0x13, 0x41, 0x91, 0x61, 0xDE, 0x2F, 0xEE, + 0x18, 0x0A, 0xBE, 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0x33, 0xD2, 0xBF, 0x5E, 0x47, 0x45, 0xD2, + 0x89, 0xA8, 0x48, 0xBA, 0x78, 0x7C, 0xB8, 0x85, 0xE1, 0x11, 0xC6, 0x47, 0xAB, 0xDB, 0x0F, 0xFF, + 0x5B, 0xAF, 0x82, 0xF8, 0x70, 0xFB, 0xA1, 0x9D, 0x88, 0x8B, 0x56, 0xD3, 0x21, 0x91, 0x65, 0x22, + 0x24, 0xB2, 0x2C, 0x87, 0xF8, 0x78, 0xE3, 0xDB, 0x36, 0x3E, 0x5E, 0x22, 0x3E, 0xF2, 0x9F, 0x8E, + 0x89, 0x2C, 0xF3, 0x8E, 0xDE, 0x3C, 0x16, 0x7C, 0x29, 0x02, 0x00, 0x00, 0x4C, 0x0F, 0xDE, 0x81, + 0xAB, 0x46, 0x75, 0x58, 0x24, 0x99, 0x88, 0x8A, 0xA4, 0x6B, 0x10, 0x1F, 0xEE, 0xC7, 0x54, 0xB7, + 0x1F, 0xD1, 0xF8, 0x70, 0xB7, 0x1F, 0xDF, 0x4A, 0x7F, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, + 0x64, 0x19, 0xF1, 0x91, 0xC3, 0x54, 0x38, 0xE4, 0x31, 0x1D, 0x12, 0x99, 0xC7, 0x9B, 0xB0, 0x00, + 0x00, 0xC0, 0x74, 0xE3, 0xED, 0xFF, 0xEC, 0x49, 0x1D, 0x17, 0xAD, 0x26, 0xA2, 0x22, 0xE9, 0xE2, + 0xE1, 0x11, 0x2E, 0x0C, 0x8F, 0x30, 0x3E, 0x9A, 0xDD, 0x7E, 0x3C, 0x76, 0x91, 0x39, 0xFD, 0xA8, + 0x0D, 0x8E, 0x36, 0x6F, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, + 0x0A, 0x87, 0x3C, 0x26, 0x02, 0xA2, 0xDD, 0xB9, 0x37, 0x61, 0x1D, 0xBF, 0x63, 0x49, 0xF0, 0xE5, + 0x08, 0x00, 0x00, 0x50, 0x7E, 0x66, 0x68, 0xBE, 0x8D, 0x02, 0x15, 0x18, 0xCD, 0x26, 0xA2, 0x22, + 0xE9, 0x54, 0x78, 0x44, 0x96, 0xE8, 0xF6, 0x23, 0x88, 0x8F, 0x76, 0x6F, 0x3F, 0x74, 0x48, 0x64, + 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0x26, 0xE2, 0x21, 0x8F, 0xBD, + 0x78, 0x1D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x87, 0xFF, 0x06, 0xAC, 0x5D, 0x9F, 0xB6, 0x61, + 0xA0, 0x22, 0xA3, 0xD1, 0x44, 0x54, 0x24, 0x9D, 0x08, 0x8E, 0xEA, 0x92, 0xDE, 0x7E, 0x3C, 0x6E, + 0x03, 0x24, 0x8C, 0x8F, 0x68, 0x80, 0xFC, 0x80, 0xF8, 0x20, 0x3E, 0x54, 0x38, 0xE4, 0x31, 0x11, + 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x3E, 0xCC, 0xA1, 0xFE, 0xB5, + 0x66, 0xC7, 0xC7, 0x6D, 0x1C, 0xA8, 0xD0, 0x50, 0x13, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0x62, 0x53, + 0xB7, 0x1F, 0xD1, 0x00, 0xF1, 0xE3, 0xA3, 0xD1, 0xED, 0xC7, 0x37, 0x75, 0x68, 0xA8, 0xE9, 0x90, + 0xC8, 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x44, 0x43, 0xCE, + 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xDA, 0xF0, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0x36, 0x10, 0x54, + 0x6C, 0xC4, 0x27, 0xA2, 0x22, 0xE9, 0x44, 0x6C, 0xD4, 0x2C, 0xC1, 0xED, 0x87, 0xFB, 0xD6, 0xAB, + 0x89, 0xC7, 0x2E, 0x9C, 0xBC, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xC9, 0x6F, 0x3F, 0x74, 0x48, + 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0xA6, 0x83, 0x21, 0xEF, + 0x79, 0x47, 0xFF, 0x64, 0x34, 0xF8, 0x92, 0x04, 0x00, 0x00, 0x28, 0x37, 0x6F, 0x78, 0xE9, 0x90, + 0x8E, 0x8D, 0xF8, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8E, 0xF8, 0xE2, 0x01, 0xA2, 0x9E, 0xFD, 0x78, + 0xDC, 0xC6, 0x47, 0x9B, 0xB7, 0x1F, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x11, 0x1F, 0x39, + 0x4C, 0x85, 0x43, 0x1E, 0xD3, 0xB1, 0x50, 0xC8, 0x8E, 0x7D, 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, + 0xE9, 0xC1, 0xDB, 0xBB, 0x24, 0xC1, 0x2B, 0x78, 0x45, 0x54, 0x24, 0x9D, 0x8A, 0x8D, 0xE8, 0x6C, + 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, 0x47, 0xEC, 0x5B, 0xAF, 0x9E, 0xB8, 0x70, 0xF2, + 0xF6, 0xC3, 0x2E, 0xCB, 0xED, 0x87, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, + 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x24, 0x14, 0xB6, 0x45, 0x95, 0x37, 0x61, 0xBD, 0x7C, 0xE7, 0xEC, + 0xE0, 0xCB, 0x12, 0x00, 0x00, 0xA0, 0xBC, 0xCC, 0x1E, 0x7B, 0xF8, 0x91, 0xD1, 0x11, 0x4E, 0x44, + 0x45, 0xD2, 0xA9, 0xE0, 0x88, 0x2F, 0x1A, 0x20, 0x76, 0xD5, 0x6F, 0xBD, 0x8A, 0x06, 0x88, 0x8B, + 0x0F, 0xFF, 0xF6, 0xE3, 0xC2, 0xFA, 0xDB, 0x8F, 0x87, 0xEC, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, + 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xB3, + 0x5F, 0x7F, 0x6E, 0x2F, 0x7E, 0xCE, 0x05, 0xC8, 0x40, 0xF0, 0x65, 0x09, 0x00, 0x00, 0x50, 0x4E, + 0x66, 0xA4, 0x7F, 0x76, 0xF3, 0x57, 0xF0, 0x8A, 0xA8, 0x48, 0x3A, 0x15, 0x1B, 0xF1, 0xC5, 0xE2, + 0x23, 0xBC, 0xFD, 0xA8, 0x7D, 0xF0, 0xDC, 0xC6, 0x47, 0xF4, 0xF6, 0x23, 0xFE, 0xAD, 0x57, 0xF7, + 0xB7, 0xBE, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, + 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x16, 0xC4, 0x87, 0x1F, 0x20, 0xD7, 0xF0, 0x26, 0x2C, 0x00, 0x00, + 0x50, 0x7E, 0xFE, 0x2B, 0x78, 0x1B, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, 0xA9, 0xD8, 0x88, 0xAF, + 0x41, 0x7C, 0x54, 0x1F, 0x3C, 0x77, 0xB3, 0xF1, 0x51, 0x73, 0xFB, 0x11, 0x7F, 0xF0, 0xDC, 0xDD, + 0x7E, 0xDC, 0xAD, 0xA3, 0x23, 0x9C, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, + 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0xB5, 0x48, 0x7C, 0x04, 0xF3, 0x4E, 0x7C, 0x69, 0x30, + 0xF8, 0xD2, 0x04, 0x00, 0x00, 0x28, 0x27, 0xFF, 0x0D, 0x58, 0x5B, 0x3F, 0x64, 0xA3, 0x61, 0x8A, + 0xE3, 0xC3, 0x4D, 0x05, 0x48, 0xCD, 0xB7, 0x5E, 0xD9, 0xF8, 0x70, 0xB7, 0x1F, 0x41, 0x7C, 0x64, + 0xB9, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x56, 0x1F, 0x1F, 0x6E, 0xDE, 0xF1, 0x3F, 0xE5, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0xDC, 0xBC, 0x91, 0x81, 0x0D, 0x1D, 0x89, 0x0F, 0xB7, 0x78, 0x7C, 0x54, 0x6F, 0x3F, 0x2E, + 0xAC, 0xC6, 0xC7, 0xE4, 0xED, 0x47, 0x2C, 0x3E, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, + 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xF1, 0xE1, + 0xEF, 0xD8, 0x2D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x94, 0x9B, 0x77, 0xE0, 0xAA, 0xD8, 0x1B, 0xB0, + 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x8C, 0x46, 0xB3, 0xD1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0x44, 0x02, + 0x24, 0x8C, 0x0F, 0xF9, 0xB9, 0x1F, 0xDF, 0x69, 0x7C, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, + 0x64, 0x59, 0x34, 0x3A, 0x88, 0x8F, 0x8C, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x11, + 0x1D, 0xD1, 0xB9, 0x37, 0x61, 0x8D, 0x7D, 0x71, 0x56, 0xF0, 0xE5, 0x09, 0x00, 0x00, 0x50, 0x3E, + 0xDE, 0xBE, 0x25, 0xE3, 0x53, 0x1E, 0x1F, 0x6E, 0xF1, 0xF8, 0x48, 0x7B, 0xFB, 0xF1, 0xA0, 0x9D, + 0x08, 0x0F, 0x37, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x52, 0xC4, 0x47, 0x18, 0x20, 0x2E, + 0x3E, 0xFE, 0x40, 0x7C, 0x44, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x38, 0xE2, + 0x1B, 0x5B, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB9, 0x4D, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, + 0xA9, 0xC0, 0x68, 0x36, 0x1B, 0x1D, 0xEE, 0x47, 0x75, 0xFB, 0x11, 0x0D, 0x90, 0xA6, 0xB7, 0x1F, + 0xF7, 0xE9, 0xDB, 0x0F, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x22, 0xF1, 0x11, 0xAE, 0x3E, + 0x3E, 0xEC, 0x5C, 0x78, 0x84, 0xB7, 0x1F, 0xC4, 0x47, 0x6C, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, + 0xA2, 0x26, 0x62, 0x43, 0xED, 0xC5, 0x6B, 0x8C, 0xF7, 0x8B, 0x2F, 0xDD, 0x15, 0x7C, 0x79, 0x02, + 0x00, 0x00, 0x94, 0x8B, 0xFF, 0x06, 0xAC, 0x9D, 0x9F, 0xD2, 0x51, 0x91, 0x74, 0xF1, 0xB8, 0x48, + 0xB2, 0x30, 0x3C, 0xC2, 0xF8, 0xF0, 0x03, 0x64, 0x66, 0x35, 0x3E, 0x6A, 0xDF, 0x7C, 0x95, 0xFC, + 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0x76, 0x24, 0x3E, 0xE2, + 0x37, 0x1F, 0x6F, 0x04, 0xF1, 0xF1, 0x07, 0xE2, 0xA3, 0xA0, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x34, + 0x9A, 0xCC, 0x3B, 0x71, 0xC7, 0x86, 0xE0, 0x4B, 0x14, 0x00, 0x00, 0xA0, 0x5C, 0x6C, 0x80, 0xAC, + 0x33, 0xDB, 0x2F, 0xD6, 0x61, 0x91, 0x64, 0x2A, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0xF6, + 0xF6, 0xC3, 0xC6, 0x47, 0x83, 0x67, 0x3F, 0xEA, 0x3E, 0x74, 0xD0, 0xBD, 0xF9, 0x4A, 0xDC, 0x7E, + 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x22, 0x3E, 0xA2, 0x37, 0x1F, 0x61, 0x7C, 0x84, + 0x37, 0x1F, 0xC4, 0x47, 0x38, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x91, 0xD1, 0x6C, + 0xDE, 0x89, 0x3F, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x9C, 0xFC, 0x57, 0xF0, 0xAA, 0xB0, 0x48, + 0xB2, 0x78, 0x58, 0x24, 0x5D, 0x18, 0x1E, 0x61, 0x7C, 0x3C, 0x3D, 0xB3, 0xE6, 0xF6, 0xA3, 0xEE, + 0xD9, 0x0F, 0x1B, 0x20, 0xA7, 0xA2, 0x01, 0xF2, 0x23, 0xBB, 0x1E, 0x8C, 0x0F, 0xB7, 0xBA, 0x00, + 0x69, 0x10, 0x1F, 0xE3, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0xC0, + 0x68, 0x35, 0xEF, 0xD8, 0x17, 0xC6, 0x83, 0x2F, 0x51, 0x00, 0x00, 0x80, 0x72, 0xF1, 0x0E, 0x5C, + 0x33, 0x2A, 0xE3, 0xA2, 0xD5, 0x54, 0x58, 0x24, 0x99, 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, + 0xF1, 0x11, 0xBD, 0xFD, 0x88, 0xC5, 0x47, 0xDD, 0xED, 0xC7, 0xB7, 0x6A, 0x6F, 0x3F, 0x74, 0x48, + 0x64, 0x99, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x56, 0x17, 0x1F, 0xEA, 0x8D, 0x57, 0xC4, 0x47, + 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x71, 0x91, 0x68, 0x47, 0x56, 0xF3, 0x26, + 0x2C, 0x00, 0x00, 0x50, 0x4E, 0xDE, 0xDE, 0x25, 0x63, 0x32, 0x30, 0x9A, 0x2D, 0x1E, 0x15, 0x69, + 0x16, 0x86, 0x47, 0x18, 0x1F, 0x69, 0x6F, 0x3F, 0x7E, 0x68, 0x57, 0xB6, 0xF8, 0x70, 0x37, 0x1F, + 0x2E, 0x3E, 0xDC, 0x43, 0xE7, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, + 0xA8, 0x48, 0xB3, 0x97, 0x56, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x4E, 0x66, 0xCF, 0x42, 0x1D, + 0x19, 0x8D, 0xA6, 0xA2, 0x22, 0xE5, 0x2A, 0x01, 0x32, 0xB3, 0xED, 0xDB, 0x0F, 0x1D, 0x12, 0x59, + 0x26, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0xB5, 0x8C, 0x8F, 0xBF, 0x9C, 0xFC, 0xAC, 0x8F, 0x3F, + 0xDC, 0x4B, 0x7C, 0x14, 0x33, 0x15, 0x09, 0x45, 0x4D, 0x04, 0x45, 0xDA, 0xBD, 0x78, 0x8D, 0x0D, + 0x90, 0x3B, 0xD6, 0x05, 0x5F, 0xA6, 0x00, 0x00, 0x00, 0xE5, 0x60, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, + 0xE6, 0xE8, 0xD0, 0x50, 0x13, 0x31, 0x91, 0x6A, 0xD5, 0xDB, 0x8F, 0x20, 0x3E, 0x9A, 0xDD, 0x7E, + 0x84, 0xF1, 0xD1, 0xE0, 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, + 0x0D, 0xE3, 0x23, 0x08, 0x90, 0xE8, 0x07, 0x0D, 0x12, 0x1F, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, + 0xA2, 0x26, 0x62, 0x22, 0xCB, 0x9E, 0xB7, 0x7F, 0x3F, 0x4E, 0xDC, 0xB1, 0x3E, 0xF8, 0x52, 0x05, + 0x00, 0x00, 0x28, 0x07, 0xFF, 0x15, 0xBC, 0x49, 0xDF, 0x80, 0xA5, 0x82, 0x22, 0xC3, 0xFC, 0xF8, + 0x88, 0xDD, 0x7E, 0x54, 0x03, 0xA4, 0xD5, 0xED, 0xC7, 0x3D, 0xBD, 0x1B, 0x1F, 0x95, 0xD7, 0xED, + 0x12, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x24, 0xDA, 0x18, 0x6F, + 0xC2, 0x02, 0x00, 0x00, 0xA5, 0xE3, 0xBF, 0x01, 0x6B, 0xEB, 0x07, 0x75, 0x70, 0x44, 0x27, 0x42, + 0x22, 0xF5, 0xC2, 0x9B, 0x8F, 0x26, 0xB7, 0x1F, 0x13, 0x2E, 0x40, 0x1E, 0x8B, 0xDC, 0x7E, 0x84, + 0xF1, 0xE1, 0xF6, 0xBD, 0xCA, 0xB7, 0x5E, 0xE9, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x26, 0xC2, + 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x11, 0xD1, 0xCE, + 0xBC, 0x63, 0x5F, 0x38, 0x19, 0x7C, 0xA9, 0x02, 0x00, 0x00, 0x94, 0x83, 0x37, 0xBC, 0x62, 0x50, + 0x06, 0x47, 0x74, 0x2A, 0x26, 0xB2, 0x2C, 0x8C, 0x8F, 0xD8, 0xED, 0x47, 0xE2, 0x67, 0x3F, 0xBE, + 0xC9, 0xB7, 0x5D, 0x25, 0x9B, 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, + 0x2A, 0x12, 0x8A, 0x9A, 0x0E, 0x88, 0xB6, 0x77, 0x64, 0xB5, 0x09, 0xBE, 0x54, 0x01, 0x00, 0x00, + 0xCA, 0xC1, 0xDB, 0x7B, 0x79, 0xF3, 0x57, 0xF0, 0xAA, 0x90, 0x48, 0xBB, 0x4D, 0x6E, 0x93, 0xE1, + 0x31, 0x79, 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xFC, 0xF6, 0x43, 0x87, 0x44, + 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0x0D, 0xE3, 0xC3, 0x3D, 0x70, 0x1E, 0x8B, 0x8F, + 0xF1, 0xFF, 0x32, 0xCB, 0x9C, 0xFF, 0xD7, 0x5B, 0x83, 0xF8, 0x58, 0x65, 0x0F, 0xFA, 0x2A, 0x28, + 0xD2, 0x4C, 0x84, 0x43, 0x1E, 0x93, 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x84, 0x43, 0x5E, 0x3B, 0xB2, 0x92, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF1, 0xF6, 0x2C, + 0x1A, 0x97, 0xE1, 0xE1, 0xA6, 0x62, 0x22, 0xCB, 0x6C, 0x78, 0x44, 0x6F, 0x3F, 0xAA, 0xDF, 0x7A, + 0x15, 0x06, 0x48, 0xCD, 0x6B, 0x77, 0x2F, 0x94, 0xB7, 0x1F, 0xEF, 0x7C, 0x23, 0xAF, 0xDB, 0x0F, + 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x2D, 0x6D, 0x7C, 0x9C, 0xFB, 0xE9, 0x17, 0x2A, 0xF1, 0x71, + 0xD4, 0xFE, 0x28, 0x83, 0x22, 0xCD, 0x44, 0x38, 0xE4, 0x31, 0x19, 0x0E, 0x79, 0x4C, 0x85, 0x43, + 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x44, 0x34, 0xE4, 0x39, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xCA, 0xC4, + 0x0C, 0xF5, 0xCF, 0x32, 0x43, 0x7D, 0x53, 0x1A, 0x1F, 0xE1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xCD, + 0x9E, 0xFD, 0x70, 0xF1, 0x71, 0x7F, 0x5E, 0xB7, 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, + 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x82, 0x21, 0xEF, 0xB9, 0xFF, + 0x6D, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0x0B, 0xFF, 0x0D, 0x58, 0x3B, 0x3E, 0x39, 0xE5, 0xF1, + 0x51, 0x7D, 0xF0, 0x5C, 0xDD, 0x7E, 0xC4, 0x1E, 0x3C, 0x7F, 0xE7, 0x41, 0x1B, 0x1F, 0x5F, 0x57, + 0x31, 0x91, 0x76, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x62, 0xA1, 0xA0, 0x79, 0x27, 0xEE, 0xE0, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0x1C, 0xBC, 0xE1, 0xFE, 0xBB, 0xCC, 0xB6, 0x8F, 0x14, 0x13, 0x1F, 0xCF, 0x54, 0xE2, 0xA3, + 0x2E, 0x40, 0xE2, 0xAF, 0xDD, 0x75, 0xB7, 0x1F, 0x41, 0x7C, 0xC4, 0x6F, 0x3F, 0xDE, 0x79, 0xD8, + 0xC6, 0xC7, 0x77, 0xF3, 0xF8, 0xD6, 0x2B, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, + 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xA1, 0x50, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, + 0x82, 0x2F, 0x59, 0x00, 0x00, 0x80, 0xDE, 0xE6, 0xBF, 0x82, 0xB7, 0xA8, 0xF8, 0x08, 0x02, 0xA4, + 0xE1, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0x8A, 0xC7, 0x47, 0x2E, 0xB7, 0x1F, 0x22, 0x24, + 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0xA6, + 0x23, 0xA1, 0xD0, 0x1D, 0xB9, 0x91, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF0, 0x0E, 0x5C, 0x33, + 0xF9, 0x06, 0x2C, 0x19, 0x12, 0x59, 0x36, 0x19, 0x1F, 0x8D, 0x6E, 0x3F, 0xAA, 0xAF, 0xDD, 0xAD, + 0xDE, 0x7E, 0x5C, 0x14, 0xBB, 0xFD, 0xB0, 0xE1, 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, + 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x38, 0x98, + 0x8A, 0xBD, 0x34, 0x60, 0xCC, 0xF1, 0x3B, 0x96, 0x04, 0x5F, 0xB6, 0x00, 0x00, 0x00, 0xBD, 0xCB, + 0xDB, 0x77, 0xC5, 0xC9, 0x42, 0xE2, 0xA3, 0xAD, 0xDB, 0x8F, 0x20, 0x3E, 0x1E, 0x68, 0xF7, 0xF6, + 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, + 0x50, 0xD4, 0x44, 0x18, 0x4C, 0xD5, 0xDC, 0x9B, 0xB0, 0x4E, 0x7C, 0x69, 0x6D, 0xF0, 0x65, 0x0B, + 0x00, 0x00, 0xD0, 0xBB, 0xCC, 0xEE, 0xBE, 0x48, 0x3C, 0xB4, 0xBB, 0xDA, 0xF8, 0x70, 0x3F, 0xAA, + 0xDB, 0x8F, 0xFA, 0x67, 0x3F, 0xA2, 0xB7, 0x1F, 0x41, 0x7C, 0xB8, 0xDD, 0xDB, 0xCE, 0xED, 0x87, + 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, + 0xA8, 0x89, 0x28, 0x98, 0xCA, 0xBD, 0x70, 0x25, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xCF, 0x1C, + 0xE8, 0x5F, 0x62, 0x76, 0xCE, 0x0E, 0xE2, 0xA1, 0xDD, 0x45, 0xE2, 0x23, 0x08, 0x90, 0x66, 0xB7, + 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xE1, 0x6E, 0x3F, 0x64, 0x58, 0x24, 0x99, 0x08, 0x89, 0x2C, + 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x20, + 0xE8, 0xC0, 0xBC, 0xE3, 0xB7, 0x0F, 0x05, 0x5F, 0xBA, 0x00, 0x00, 0x00, 0xBD, 0xC9, 0x1C, 0x5C, + 0xB6, 0xD6, 0x6C, 0xFF, 0x58, 0x10, 0x10, 0xED, 0xAC, 0x3E, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0x43, + 0x3D, 0xFB, 0x51, 0xF9, 0xD0, 0xC1, 0x0B, 0x27, 0x03, 0xE4, 0x21, 0xBB, 0xCC, 0xB7, 0x1F, 0x22, + 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0xA6, 0x63, 0xA0, 0x13, 0xF3, 0x8E, 0xDE, 0xC2, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x33, 0x23, + 0xCB, 0xD7, 0x9B, 0xCD, 0xEF, 0xB3, 0xB1, 0xA0, 0xA2, 0x22, 0xE9, 0x62, 0xF1, 0xE1, 0x16, 0x86, + 0x47, 0x18, 0x1F, 0xB1, 0xDB, 0x8F, 0xBA, 0x67, 0x3F, 0x1E, 0xB5, 0xF1, 0xE1, 0x02, 0xA4, 0xED, + 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, + 0x45, 0x42, 0x51, 0xD3, 0x21, 0xD0, 0xB1, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, + 0xE6, 0x1D, 0xB8, 0x7E, 0xA8, 0x36, 0x26, 0xD2, 0x4E, 0xC7, 0x87, 0xFB, 0xB1, 0xE9, 0xED, 0x47, + 0x2C, 0x3E, 0xEA, 0x6E, 0x3F, 0xEE, 0xC9, 0x72, 0xFB, 0x21, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, + 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x00, 0x3A, 0x3D, 0xF7, + 0x26, 0xAC, 0x97, 0xEF, 0x9C, 0x1D, 0x7C, 0xF9, 0x02, 0x00, 0x00, 0xF4, 0x1E, 0x6F, 0xEF, 0x65, + 0xA3, 0xF5, 0x51, 0x91, 0x74, 0xB1, 0xF0, 0x08, 0x17, 0x86, 0x47, 0x18, 0x1F, 0x4D, 0x6F, 0x3F, + 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x4E, 0x06, 0x46, 0xB3, 0x89, 0x90, 0xC8, + 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xFC, 0x77, 0xC3, 0x5E, 0xFC, 0x9C, 0x31, 0xC7, 0x6E, 0x1B, 0x08, 0xBE, 0x7C, 0x01, 0x00, 0x00, + 0x7A, 0x8F, 0xD9, 0x63, 0x0F, 0x5B, 0x32, 0x2E, 0x5A, 0x4D, 0x84, 0x47, 0x64, 0xC9, 0x6E, 0x3F, + 0x2A, 0xF1, 0xD1, 0xFE, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, + 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xFE, 0xDD, 0x32, 0xDE, 0x84, 0x05, 0x00, + 0x00, 0x7A, 0x99, 0x19, 0xE9, 0x9F, 0x6D, 0x76, 0xCF, 0xB3, 0xC1, 0xA0, 0x02, 0xA3, 0xD9, 0x74, + 0x74, 0xF8, 0x4B, 0x7C, 0xFB, 0x31, 0xD3, 0x9C, 0x09, 0xE3, 0xA3, 0xAD, 0xDB, 0x0F, 0x11, 0x12, + 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xFE, 0x2E, 0x9B, 0x77, 0xE2, 0xF6, 0xC1, 0xE0, 0x4B, 0x18, 0x00, 0x00, 0xA0, 0xB7, 0xD8, + 0x00, 0x19, 0x48, 0xFF, 0x06, 0x2C, 0x11, 0x1D, 0xB1, 0xA9, 0xDB, 0x8F, 0x6A, 0x80, 0x84, 0xF1, + 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, + 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7F, 0xB7, 0xCD, 0x3B, 0xFA, 0x85, 0xD1, 0xE0, 0x4B, + 0x18, 0x00, 0x00, 0xA0, 0xB7, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0x3E, 0x60, 0xA3, 0x41, 0x85, + 0x86, 0x9A, 0x0E, 0x8E, 0xEA, 0x12, 0xDC, 0x7E, 0x4C, 0x3C, 0x31, 0xD3, 0x4C, 0x3C, 0x16, 0xB9, + 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0xFB, 0xC4, 0x47, 0x65, 0x22, 0x1C, 0xF2, 0x98, 0x0C, 0x87, 0x3C, + 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0xB0, 0xDF, 0x95, 0x3B, 0x76, 0x0B, 0x6F, + 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xC9, 0x1B, 0x59, 0xB1, 0xA1, 0x3E, 0x32, 0x1A, 0x4D, 0x04, 0x47, + 0x7C, 0xF1, 0x00, 0xA9, 0x7B, 0xF6, 0xC3, 0xC6, 0x47, 0xB3, 0xDB, 0x8F, 0x6F, 0xA8, 0xD0, 0x50, + 0x13, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, + 0x14, 0x35, 0x71, 0xC8, 0xEF, 0xE6, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xAB, 0xBC, 0x7D, 0x57, + 0x24, 0x7C, 0x03, 0x96, 0x88, 0x8D, 0xE8, 0x6C, 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, + 0x47, 0xE4, 0x5B, 0xAF, 0x06, 0x6D, 0x7C, 0x44, 0x6F, 0x3F, 0xEC, 0x6A, 0x6E, 0x3F, 0x36, 0x24, + 0xBD, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, + 0x54, 0x24, 0x14, 0x35, 0x71, 0xC0, 0xEF, 0xF6, 0x8D, 0x2D, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0xA0, + 0x37, 0x79, 0x7B, 0x2F, 0x1B, 0xAF, 0x8F, 0x8D, 0xF8, 0x62, 0xB1, 0xA1, 0x16, 0x0D, 0x10, 0xBB, + 0xEA, 0xB7, 0x5E, 0x85, 0x01, 0x62, 0xE3, 0x63, 0xF2, 0xF6, 0x63, 0x66, 0x1B, 0xB7, 0x1F, 0x22, + 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0x26, 0x0E, 0xF7, 0xBD, 0xB0, 0x17, 0xAF, 0x31, 0xDE, 0x2F, 0x6E, 0xBF, 0x2B, 0xF8, 0x32, 0x06, + 0x00, 0x00, 0xE8, 0x1D, 0xAD, 0xDF, 0x80, 0x15, 0x0B, 0x0D, 0xB5, 0x58, 0x7C, 0x84, 0xB7, 0x1F, + 0x93, 0x0F, 0x9E, 0xDB, 0xF8, 0x88, 0xDF, 0x7E, 0x84, 0xF1, 0x11, 0x06, 0x48, 0xA2, 0xDB, 0x0F, + 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, + 0x51, 0x13, 0x07, 0xFB, 0x1E, 0x9A, 0x77, 0xFC, 0x8E, 0x0D, 0xC1, 0x97, 0x31, 0x00, 0x00, 0x40, + 0x6F, 0xF0, 0xDF, 0x80, 0xB5, 0x73, 0xB6, 0x8D, 0x08, 0x15, 0x1E, 0x6E, 0xB1, 0xD0, 0x50, 0x6B, + 0x10, 0x1F, 0xD5, 0x07, 0xCF, 0x37, 0xCE, 0xAC, 0xBF, 0xFD, 0x08, 0xE2, 0xA3, 0x7A, 0xFB, 0xF1, + 0xA0, 0xDD, 0xD7, 0x55, 0x70, 0x44, 0x27, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, + 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0x50, 0xDF, 0x4B, 0xF3, 0x8E, 0xDE, 0xCA, + 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x31, 0x23, 0xCB, 0xD7, 0x35, 0x7E, 0x05, 0x6F, 0x2C, 0x34, + 0x1A, 0x4D, 0x05, 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x41, 0x7C, 0xC8, 0x37, + 0x5F, 0x7D, 0xB7, 0xD5, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, + 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7D, 0xAF, 0xCD, 0x3B, 0xFA, 0xF9, 0xF1, + 0xE0, 0x4B, 0x19, 0x00, 0x00, 0xA0, 0x37, 0xD8, 0x00, 0x59, 0x5F, 0x1F, 0x1E, 0x29, 0xE2, 0xC3, + 0x2D, 0x1E, 0x1F, 0xD5, 0xDB, 0x8F, 0xC9, 0xF8, 0xA8, 0x79, 0xF3, 0x55, 0xEA, 0xDB, 0x0F, 0x11, + 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, + 0xD3, 0x87, 0xF9, 0x9E, 0x9C, 0x7B, 0x13, 0xD6, 0xD8, 0x17, 0x67, 0x05, 0x5F, 0xCE, 0x00, 0x00, + 0x00, 0xDD, 0xCF, 0x3B, 0x70, 0xD5, 0x50, 0xBB, 0xF1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0xD4, 0xDD, + 0x7E, 0x5C, 0x98, 0xE1, 0xF6, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, + 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBE, 0x97, 0x37, 0xB6, 0x9C, 0x37, 0x61, + 0x01, 0x00, 0x80, 0xDE, 0xE2, 0xED, 0xBD, 0x7C, 0x2C, 0x73, 0x7C, 0xB8, 0xC5, 0xE3, 0xC3, 0x0F, + 0x90, 0x19, 0x39, 0xDD, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xC0, 0xF7, 0xFA, 0x78, 0x13, 0x16, 0x00, 0x00, + 0xE8, 0x35, 0x66, 0x68, 0x81, 0x0D, 0x89, 0xEC, 0xF1, 0xE1, 0x7E, 0xAC, 0xBD, 0xFD, 0xB0, 0xF1, + 0x11, 0xFB, 0xF6, 0xAB, 0xA6, 0xB7, 0x1F, 0xF7, 0x35, 0xBA, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, + 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x78, 0x2F, + 0xC3, 0x9E, 0xBF, 0x9C, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, 0x61, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, + 0x2E, 0xB5, 0x11, 0x91, 0x21, 0x3E, 0xDC, 0xC2, 0xF0, 0x08, 0xE3, 0xE3, 0xE9, 0x19, 0x35, 0xB7, + 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xF1, 0x80, 0x1D, 0xF1, 0x91, 0xCF, 0x64, 0x38, 0xE4, 0x31, + 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x07, 0xF7, 0x12, 0xCD, 0x3B, 0xFE, 0x67, 0xBC, + 0x09, 0x0B, 0x00, 0x00, 0xF4, 0x06, 0xFF, 0x15, 0xBC, 0xFE, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, + 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, 0xF1, 0xD1, 0xE0, 0xD9, 0x0F, 0xF9, 0xA1, 0x83, 0xF7, + 0xAA, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, + 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x87, 0xF6, 0x32, 0xCD, 0x3B, 0xFA, 0xF9, 0x93, 0xC1, 0x97, 0x34, + 0x00, 0x00, 0x40, 0x77, 0xF3, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0xF5, 0x71, 0x91, 0x64, 0x61, 0x78, + 0x84, 0xF1, 0x11, 0xBB, 0xFD, 0xA8, 0x7B, 0xF6, 0xC3, 0x06, 0xC8, 0x29, 0x17, 0x20, 0x4D, 0x6F, + 0x3F, 0x44, 0x48, 0x64, 0x99, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1F, 0xD8, 0x4B, 0xB7, 0x23, 0xAB, 0x4C, 0xF0, 0x25, 0x0D, 0x00, 0x00, 0xD0, + 0xDD, 0xBC, 0xE1, 0x65, 0x83, 0x32, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0x12, 0x20, 0x33, + 0xF4, 0xED, 0x47, 0x2C, 0x3E, 0x5A, 0xDF, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, + 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA8, 0x97, 0x75, 0x63, 0xCB, + 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0D, 0xDE, 0xDE, 0xCB, 0x46, 0xEB, 0xE2, 0x22, 0xC9, 0xAA, + 0xB7, 0x1F, 0x41, 0x7C, 0x24, 0xBD, 0xFD, 0x08, 0x03, 0xE4, 0x07, 0x76, 0xC4, 0x47, 0x7B, 0x93, + 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0xBA, 0x7F, + 0x5D, 0x68, 0xCC, 0x0B, 0x57, 0x1B, 0x73, 0xFC, 0x8E, 0x75, 0xC1, 0x97, 0x35, 0x00, 0x00, 0x40, + 0xF7, 0xF2, 0xF6, 0x2C, 0x18, 0x97, 0x81, 0xD1, 0x6C, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0x59, 0x6F, + 0x3F, 0xEE, 0x89, 0xDE, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2E, 0x3E, 0xDC, 0x7E, 0x6E, + 0xFF, 0x99, 0x38, 0x71, 0xDB, 0xFA, 0xE0, 0xCB, 0x1A, 0x00, 0x00, 0xA0, 0x3B, 0x99, 0xA1, 0xFE, + 0x59, 0x66, 0xF7, 0xDC, 0xFA, 0xC0, 0x68, 0xB5, 0xF0, 0xE6, 0x23, 0x97, 0xDB, 0x0F, 0x11, 0x12, + 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x2E, 0x8C, 0x8F, 0x60, 0xDE, 0x89, 0xDB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, + 0x6E, 0x66, 0x64, 0xE9, 0x80, 0xD9, 0xF1, 0x71, 0x1D, 0x19, 0xCD, 0x16, 0xC6, 0x47, 0xDB, 0xB7, + 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, + 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x5D, 0x2C, 0x3E, 0xFC, 0x00, 0x39, 0x7A, 0x33, 0x6F, 0xC2, + 0x02, 0x00, 0x00, 0xDD, 0xCD, 0x1B, 0x5E, 0x7E, 0x97, 0xD9, 0xFA, 0x21, 0x1D, 0x19, 0x8D, 0x16, + 0x8F, 0x8F, 0x66, 0xB7, 0x1F, 0x61, 0x7C, 0xC8, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, + 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, + 0xC4, 0x87, 0xBF, 0x23, 0x37, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xDD, 0xFC, 0x57, 0xF0, 0xAA, + 0xC8, 0x68, 0x34, 0x1B, 0x1D, 0x8D, 0x6E, 0x3F, 0xAA, 0x01, 0xD2, 0xEA, 0xF6, 0xE3, 0x1B, 0xC4, + 0x47, 0xA6, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, + 0x65, 0x9D, 0x0A, 0x8F, 0x70, 0x2F, 0xF5, 0xBB, 0x07, 0xD1, 0x97, 0x04, 0x5F, 0xDE, 0x00, 0x00, + 0x00, 0xDD, 0xC7, 0x3B, 0x70, 0x75, 0xF2, 0x37, 0x60, 0x45, 0xE3, 0xC3, 0x86, 0x47, 0xA3, 0xDB, + 0x8F, 0x09, 0x17, 0x20, 0x8F, 0x45, 0x6E, 0x3F, 0xC2, 0xF8, 0x70, 0xDB, 0xE0, 0xBE, 0xF5, 0x4A, + 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xD1, 0x11, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x3F, 0x5F, 0x1B, + 0x7C, 0x79, 0x03, 0x00, 0x00, 0x74, 0x1F, 0x6F, 0xDF, 0xE5, 0x27, 0x65, 0x6C, 0xC4, 0x17, 0xC4, + 0x47, 0xA3, 0xDB, 0x8F, 0xC4, 0xCF, 0x7E, 0x7C, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, + 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xC1, + 0x11, 0x1F, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xDD, 0x2E, 0xD1, 0x1B, 0xB0, 0xE2, 0xF1, 0x51, 0x73, + 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xED, 0xED, 0x47, 0x4E, 0xDF, 0x7A, 0x25, + 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, + 0xB2, 0x4E, 0xC5, 0x46, 0x83, 0x79, 0xC7, 0x6F, 0x1F, 0x0A, 0xBE, 0xBC, 0x01, 0x00, 0x00, 0xBA, + 0x8B, 0x39, 0x70, 0xFD, 0x12, 0xB3, 0xF3, 0x53, 0x3A, 0x3A, 0xC2, 0x45, 0xE2, 0xA3, 0x1A, 0x20, + 0x76, 0xD5, 0x6F, 0xBD, 0x0A, 0x03, 0xA4, 0xE6, 0xB5, 0xBB, 0x33, 0xC5, 0xED, 0xC7, 0xCC, 0x7C, + 0x6E, 0x3F, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, + 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0xC8, 0x68, 0x36, 0xEF, 0xC8, 0x9A, 0xB1, 0xE0, 0x4B, 0x1C, 0x00, + 0x00, 0xA0, 0xBB, 0x98, 0x83, 0xCB, 0xD6, 0x9A, 0xED, 0x1F, 0xD5, 0xE1, 0xE1, 0x16, 0x09, 0x8F, + 0x68, 0x7C, 0x84, 0xB7, 0x1F, 0x35, 0x0F, 0x9E, 0x37, 0x7A, 0xF6, 0xC3, 0x0F, 0x10, 0x1B, 0x1F, + 0xDF, 0xCD, 0xE1, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, + 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x08, 0x8C, 0x96, 0x3B, 0xBA, 0x86, 0x37, 0x61, 0x01, + 0x00, 0x80, 0xEE, 0xE4, 0xBF, 0x01, 0x6B, 0xF3, 0xAC, 0x4C, 0xF1, 0x51, 0x7D, 0xF0, 0x5C, 0xDD, + 0x7E, 0x04, 0xF1, 0x51, 0xB9, 0xFD, 0xB0, 0xF1, 0xF1, 0xA0, 0xDD, 0xD7, 0x45, 0x50, 0xA4, 0x99, + 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, + 0xCB, 0x3A, 0x15, 0x17, 0x49, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB7, 0xF2, 0x0E, 0x7C, 0x6E, + 0x28, 0x49, 0x7C, 0xD4, 0x05, 0x48, 0xFC, 0xB5, 0xBB, 0xEE, 0xF6, 0x23, 0x88, 0x8F, 0xDA, 0x37, + 0x5F, 0xD9, 0xF0, 0xC8, 0xE3, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x0A, 0x8B, 0xA4, 0x7B, 0xE1, 0x5A, 0x63, + 0x8E, 0xDD, 0x36, 0x10, 0x7C, 0x99, 0x03, 0x00, 0x00, 0x74, 0x0F, 0x6F, 0xEF, 0x65, 0xF5, 0xAF, + 0xE0, 0x8D, 0x44, 0x87, 0x8C, 0x8F, 0xE8, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0xAA, 0xDE, + 0x7E, 0x04, 0xF1, 0xD1, 0xEE, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, + 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x15, 0x69, 0xF6, 0xFC, 0x67, 0x79, + 0x13, 0x16, 0x00, 0x00, 0xE8, 0x4E, 0x66, 0x8F, 0x3D, 0xEC, 0xB4, 0x88, 0x0F, 0x37, 0x75, 0xFB, + 0x51, 0x7D, 0xED, 0x6E, 0xF5, 0xF6, 0xE3, 0xC2, 0xC8, 0xED, 0x47, 0x10, 0x1F, 0xED, 0xDE, 0x7E, + 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x62, 0x07, + 0xF4, 0x32, 0x4F, 0x05, 0x45, 0x86, 0x79, 0x27, 0x6E, 0x1B, 0x0C, 0xBE, 0xCC, 0x01, 0x00, 0x00, + 0xBA, 0x83, 0x19, 0xE9, 0x9F, 0x5D, 0xF3, 0x0A, 0xDE, 0x48, 0x70, 0x44, 0x97, 0xFE, 0xF6, 0x63, + 0xE6, 0x64, 0x80, 0x3C, 0x60, 0xA7, 0xC2, 0x22, 0xC9, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, + 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0x90, 0xC8, 0x3A, 0xEF, + 0xC8, 0xE7, 0x47, 0x83, 0x2F, 0x75, 0x00, 0x00, 0x80, 0xEE, 0x60, 0x46, 0x96, 0x0E, 0x54, 0xDF, + 0x80, 0x15, 0x09, 0x8E, 0xF8, 0xD4, 0xED, 0x47, 0xFD, 0xB3, 0x1F, 0xE1, 0xED, 0x47, 0x24, 0x3E, + 0xDC, 0xEE, 0xCD, 0x78, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, + 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x44, 0x44, 0x5B, 0xE3, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0xDB, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0xDE, 0x2F, 0xA3, 0x23, 0x5C, 0xB3, 0xDB, 0x8F, + 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0xC8, 0x7A, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, + 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x05, 0x44, 0xBB, 0x73, 0x6F, + 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x3B, 0x00, 0x00, 0x40, 0xE7, 0x79, 0x23, 0xCB, 0x36, + 0xA8, 0xE8, 0x88, 0x4E, 0xDD, 0x7E, 0xD4, 0x3F, 0xFB, 0xE1, 0x3E, 0x74, 0xD0, 0xBD, 0x7A, 0x37, + 0x12, 0x20, 0xEE, 0x43, 0x07, 0xB3, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, + 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xF1, 0x90, 0xC7, 0x5E, 0xBC, + 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, 0xEE, 0xE2, 0xED, 0xBD, 0x62, 0x54, 0x45, 0x47, 0xB8, 0x66, + 0xB7, 0x1F, 0xD5, 0x67, 0x3F, 0xFC, 0xD7, 0xEE, 0x06, 0xF1, 0xE1, 0xD6, 0xCE, 0xED, 0x87, 0x08, + 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, + 0x3A, 0x15, 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x2E, 0xDE, 0xDE, + 0xC5, 0xE3, 0x2A, 0x3C, 0xC2, 0x35, 0xBD, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC5, 0x87, 0xBA, 0xFD, + 0xB8, 0x27, 0xE5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x0D, 0x39, 0xCF, 0x3B, 0xB6, 0x96, 0x37, 0x61, + 0x01, 0x00, 0x80, 0xEE, 0xE1, 0xBF, 0x01, 0x2B, 0x12, 0x1C, 0xD1, 0xB5, 0xBC, 0xFD, 0xB0, 0xF1, + 0x31, 0xF1, 0xD8, 0x8C, 0xDA, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x60, 0xA7, 0x22, 0xA3, 0xD1, 0x44, + 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0x89, 0x58, 0x28, 0x62, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0E, 0xFE, + 0x1B, 0xB0, 0x76, 0x7C, 0x52, 0xC6, 0x87, 0x5B, 0xCB, 0xDB, 0x8F, 0x30, 0x3E, 0xDA, 0xBD, 0xFD, + 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, + 0x7A, 0x59, 0x27, 0x42, 0xA1, 0xA8, 0x79, 0x47, 0x6F, 0x1E, 0x0F, 0xBE, 0xE4, 0x01, 0x00, 0x00, + 0x3A, 0xCB, 0x8C, 0x2C, 0x5F, 0x67, 0xB6, 0x7D, 0xA4, 0x75, 0x7C, 0xC8, 0xDB, 0x8F, 0x19, 0xF9, + 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, + 0xC4, 0x21, 0xBD, 0xAC, 0x13, 0x91, 0x50, 0xE8, 0xDC, 0x9B, 0xB0, 0xC6, 0xBE, 0x38, 0x2B, 0xF8, + 0xB2, 0x07, 0x00, 0x00, 0xE8, 0x1C, 0x1B, 0x20, 0xEB, 0x55, 0x7C, 0xB8, 0xA9, 0xDB, 0x0F, 0x3F, + 0x40, 0x44, 0x7C, 0x64, 0xBE, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, + 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA1, 0xE8, 0xF1, 0x26, 0x2C, 0x00, + 0x00, 0xD0, 0x2D, 0xBC, 0x03, 0x57, 0x0D, 0x35, 0x8C, 0x0F, 0x1B, 0x1E, 0xF2, 0xF6, 0xE3, 0x09, + 0x1B, 0x1F, 0x2E, 0x40, 0x1E, 0x8D, 0xDC, 0x7E, 0x84, 0xF1, 0xE1, 0xB6, 0x81, 0xF8, 0xA8, 0x9B, + 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xA9, + 0x38, 0x98, 0x8A, 0xBD, 0x70, 0xB5, 0xF1, 0x7E, 0x71, 0xFB, 0x5D, 0xC1, 0x97, 0x3D, 0x00, 0x00, + 0x40, 0xE7, 0x78, 0x7B, 0x2F, 0x1B, 0x6B, 0x18, 0x20, 0x6E, 0x91, 0xDB, 0x0F, 0x17, 0x1F, 0x67, + 0x07, 0x67, 0xD4, 0xDE, 0x7E, 0xA8, 0x67, 0x3F, 0xBE, 0x21, 0x62, 0x23, 0x3E, 0x11, 0x1E, 0x6E, + 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2A, + 0x0C, 0xA6, 0x70, 0xDE, 0xF1, 0xDB, 0x36, 0x04, 0x5F, 0xF6, 0x00, 0x00, 0x00, 0x9D, 0x63, 0x86, + 0xE6, 0xEB, 0xF8, 0xA8, 0xB9, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC6, 0x47, 0x1E, 0xB7, 0x1F, 0x22, + 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, + 0xEB, 0x44, 0x10, 0x4C, 0xF5, 0xBC, 0xE3, 0x7F, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x67, 0x99, + 0x91, 0xFE, 0xD9, 0x66, 0xE7, 0x25, 0x3A, 0x40, 0xEC, 0xA2, 0xDF, 0x7A, 0x75, 0xEE, 0xC9, 0x19, + 0xE6, 0xAC, 0x8D, 0x8F, 0xC9, 0xDB, 0x8F, 0x19, 0xFA, 0xD9, 0x8F, 0x56, 0xB7, 0x1F, 0x22, 0x3C, + 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, + 0x44, 0x0C, 0x74, 0x62, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x74, 0x9C, 0xFF, 0x0A, 0xDE, 0xD8, 0x1B, + 0xB0, 0x6A, 0xBE, 0xF5, 0xCA, 0xEE, 0x9C, 0x0B, 0x10, 0x17, 0x1F, 0xF1, 0xDB, 0x8F, 0x68, 0x7C, + 0x84, 0x01, 0x72, 0x7F, 0x8B, 0xDB, 0x0F, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, + 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x22, 0x04, 0x3A, 0xB6, 0x97, 0x56, 0x99, + 0xE0, 0x4B, 0x1F, 0x00, 0x00, 0xA0, 0x33, 0xFC, 0x37, 0x60, 0x6D, 0xFE, 0xE3, 0x86, 0xF1, 0x51, + 0xB9, 0xFD, 0xB0, 0xF1, 0xD1, 0xE0, 0xF6, 0xC3, 0xC5, 0x47, 0xF5, 0xF6, 0xE3, 0x41, 0xBB, 0xAF, + 0x8B, 0xE8, 0x08, 0x27, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, + 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x45, 0x40, 0x27, 0x37, 0xB6, 0x8C, 0x37, 0x61, 0x01, 0x00, + 0x80, 0xCE, 0xF2, 0x86, 0x97, 0x0D, 0x36, 0xBC, 0xFD, 0x08, 0xE2, 0xA3, 0xE6, 0xF6, 0x23, 0x88, + 0x8F, 0x9A, 0xDB, 0x8F, 0xF0, 0xD9, 0x8F, 0xEF, 0x36, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, + 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA0, + 0xD3, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0xF2, 0x07, 0x00, 0x00, 0x98, 0x7A, + 0xDE, 0xDE, 0xCB, 0x46, 0x1B, 0xDE, 0x7E, 0x6C, 0xBC, 0xA0, 0x26, 0x3E, 0xEA, 0x3E, 0x74, 0x30, + 0xE9, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x75, 0xF8, 0xEF, 0xF8, 0x16, 0xD8, 0x5F, 0x9B, 0xFD, 0xDF, 0xE1, + 0xC4, 0x6D, 0xEB, 0x83, 0x2F, 0x7F, 0x00, 0x00, 0x80, 0xA9, 0xE7, 0xED, 0x9E, 0x3F, 0xAE, 0x6F, + 0x3F, 0x6C, 0x7C, 0x84, 0xDF, 0x7A, 0x55, 0x73, 0xFB, 0x31, 0x33, 0xDD, 0xED, 0x87, 0x08, 0x0F, + 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0xB1, 0x78, 0x20, 0x14, 0x39, 0x71, 0x48, 0x2F, 0xEB, + 0xE4, 0xE1, 0xBF, 0xD3, 0xB3, 0xF1, 0x11, 0xCC, 0x3B, 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x80, + 0xCE, 0x30, 0x43, 0xFD, 0xB3, 0xCC, 0xEE, 0xB9, 0x32, 0x3E, 0x72, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, + 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, + 0x0F, 0xFF, 0x9D, 0xDE, 0x64, 0x7C, 0xF8, 0x01, 0x72, 0xF4, 0xE6, 0x93, 0xC1, 0x1F, 0x01, 0x00, + 0x00, 0x00, 0x53, 0xCB, 0x7F, 0x03, 0xD6, 0xF6, 0x8B, 0x6B, 0x03, 0x24, 0x8C, 0x0F, 0xF9, 0xEC, + 0x47, 0x83, 0xDB, 0x8F, 0x7B, 0xC5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, + 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x6A, 0xE3, + 0xC3, 0xDF, 0x91, 0x1B, 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0C, 0x33, 0xB2, 0x7C, 0x9D, 0xD9, + 0xFA, 0xC1, 0xC8, 0xED, 0x87, 0x8D, 0x8F, 0xD8, 0xED, 0x47, 0xED, 0x9B, 0xAF, 0x44, 0x7C, 0x3C, + 0x60, 0x47, 0x7C, 0x14, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x11, 0x1F, 0x6E, 0x2F, 0xF5, 0xF3, 0x26, 0x2C, 0x00, 0x00, 0xD0, + 0x19, 0xE6, 0xE0, 0xF2, 0xF5, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xEC, 0xC7, + 0xE9, 0x78, 0x80, 0xB8, 0x0F, 0x1D, 0x8C, 0xDF, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, + 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, + 0x84, 0x47, 0x38, 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x00, 0x00, 0x00, 0x60, + 0xEA, 0x78, 0xFB, 0xAE, 0x1E, 0xAD, 0x89, 0x8F, 0x24, 0xCF, 0x7E, 0xB8, 0x35, 0xBA, 0xFD, 0x10, + 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, + 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x88, 0x8E, 0xE8, 0xDC, 0x3F, 0x67, 0xBC, 0x09, 0x0B, 0x00, + 0x00, 0x74, 0x82, 0xB7, 0xF7, 0xF2, 0x93, 0x7E, 0x7C, 0x34, 0xBA, 0xFD, 0x88, 0xC6, 0x87, 0xBA, + 0xFD, 0xF8, 0x76, 0xE4, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, + 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x38, 0xC4, 0xBC, + 0xE3, 0xB7, 0x0F, 0x05, 0x7F, 0x0C, 0x00, 0x00, 0x00, 0x4C, 0x1D, 0x6F, 0xE7, 0x9C, 0xF4, 0xB7, + 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x8E, 0xF8, 0xC8, 0x71, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x36, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, + 0x82, 0x3F, 0x06, 0x00, 0x00, 0x00, 0xA6, 0x86, 0x39, 0x70, 0xFD, 0x12, 0x6F, 0xFB, 0x27, 0xB2, + 0xDF, 0x7E, 0xDC, 0x13, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, + 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x87, 0x46, 0xC3, + 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xD5, 0xCC, 0xC1, 0x65, 0x6B, 0xBD, 0xAD, 0x1F, 0x6E, 0xEF, + 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, + 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x30, 0x5A, 0xCD, 0xBD, 0x09, 0xEB, 0xF8, + 0x1D, 0x4B, 0x82, 0x3F, 0x0E, 0x00, 0x00, 0x00, 0x8A, 0x77, 0xFE, 0xE0, 0xD2, 0xF5, 0xDE, 0xA6, + 0x0B, 0x33, 0xDE, 0x7E, 0x10, 0x1F, 0xF9, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, + 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC4, 0x45, 0x92, 0xBD, 0x70, 0x0D, 0x6F, 0xC2, + 0x02, 0x00, 0x00, 0x53, 0xEB, 0xFC, 0xFE, 0x6B, 0x06, 0xB3, 0xDD, 0x7E, 0xCC, 0x90, 0xE1, 0xE1, + 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, + 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x8B, 0xA4, 0x7B, 0xFE, 0x0A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xA6, + 0xD6, 0xF9, 0xDD, 0x8B, 0x47, 0xA3, 0xB7, 0x1F, 0xD5, 0x00, 0x89, 0xC5, 0x47, 0xDD, 0xED, 0xC7, + 0xDD, 0xC4, 0x47, 0xFB, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, + 0xE4, 0xE1, 0xBF, 0xD3, 0x13, 0x51, 0x91, 0x72, 0xDE, 0xB1, 0xB5, 0xBC, 0x09, 0x0B, 0x00, 0x00, + 0x4C, 0x1D, 0x6F, 0xF7, 0x42, 0x79, 0xFB, 0x31, 0xE1, 0x02, 0xE4, 0xD1, 0xC8, 0xED, 0x47, 0x18, + 0x1F, 0x6E, 0xF7, 0xEB, 0x6F, 0xBD, 0x22, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x07, 0x45, 0xDA, 0x79, 0x47, 0x3E, + 0x3F, 0x1A, 0xFC, 0x71, 0x00, 0x00, 0x00, 0x50, 0x2C, 0x33, 0xD2, 0x3F, 0xDB, 0xDB, 0x31, 0x27, + 0xDD, 0xB3, 0x1F, 0x0F, 0xCE, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, + 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x98, 0xC8, 0xB4, 0xA3, + 0x6B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x1A, 0x66, 0xFF, 0xD2, 0x81, 0xF3, 0x5B, 0x3E, 0x6C, + 0x03, 0x24, 0xF2, 0xAD, 0x57, 0xCD, 0x6E, 0x3F, 0x1E, 0xB2, 0xF1, 0x21, 0x6E, 0x3F, 0x88, 0x8F, + 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, + 0xD3, 0x13, 0x11, 0xD1, 0xCE, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0x73, 0x76, 0xF0, 0xC7, 0x02, 0x00, + 0x00, 0x40, 0x71, 0xCE, 0xED, 0x5F, 0x7A, 0xD7, 0xF9, 0x4D, 0xEF, 0x9B, 0x7C, 0xF6, 0xA3, 0xE6, + 0xC1, 0xF3, 0x19, 0xB5, 0xCF, 0x7E, 0xB8, 0xF8, 0x78, 0xA0, 0xFE, 0xF6, 0x83, 0xF8, 0x48, 0x33, + 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, + 0x11, 0x10, 0xED, 0xCE, 0xBD, 0x09, 0xEB, 0xD8, 0x6D, 0x03, 0xC1, 0x1F, 0x0B, 0x00, 0x00, 0x00, + 0xC5, 0x39, 0x7F, 0x60, 0xD9, 0x86, 0x9A, 0x07, 0xCF, 0xA3, 0xB7, 0x1F, 0xD1, 0xF8, 0x78, 0xD8, + 0x86, 0x87, 0x0B, 0x90, 0xEF, 0xD4, 0xDE, 0x7E, 0x10, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x21, 0x8F, 0xF1, + 0x26, 0x2C, 0x00, 0x00, 0x30, 0x55, 0xCE, 0xEF, 0x5E, 0x32, 0xEA, 0xC7, 0x47, 0x83, 0xDB, 0x0F, + 0x17, 0x1F, 0xA7, 0xC2, 0xF8, 0x70, 0xB7, 0x1F, 0x7F, 0x4D, 0x7C, 0x64, 0x9B, 0x0A, 0x87, 0x3C, + 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x87, 0x1C, + 0xE7, 0x9D, 0xB8, 0x6D, 0x30, 0xF8, 0x63, 0x01, 0x00, 0x00, 0xA0, 0x38, 0xE7, 0x76, 0x2E, 0x3A, + 0x59, 0x73, 0xFB, 0x11, 0xC4, 0x47, 0xF5, 0xD9, 0x8F, 0x30, 0x3E, 0x62, 0xB7, 0x1F, 0xC4, 0x47, + 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, + 0xE9, 0xE9, 0x68, 0xC8, 0x73, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0xDE, 0xF9, + 0xED, 0x73, 0xAA, 0xF1, 0x51, 0xF7, 0xA1, 0x83, 0x8F, 0xCC, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, + 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, + 0xE9, 0x60, 0xC8, 0x7D, 0x47, 0x3F, 0xCF, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB1, 0xCE, 0xEE, 0x5F, + 0x3A, 0x70, 0x6E, 0xCB, 0x27, 0x63, 0xB7, 0x1F, 0x33, 0x2B, 0xCF, 0x7E, 0x44, 0xE3, 0xC3, 0xED, + 0xDE, 0xCA, 0xED, 0x07, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, + 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x14, 0x8A, 0x9A, 0x7B, 0x13, 0xD6, 0xD8, 0x17, + 0x67, 0x05, 0x7F, 0x3C, 0x00, 0x00, 0x00, 0xE4, 0xEF, 0xDC, 0xDE, 0xE5, 0xEB, 0xCE, 0x6D, 0xFA, + 0x48, 0xEC, 0xF6, 0x63, 0x46, 0x35, 0x3E, 0xAA, 0x01, 0xF2, 0x23, 0x3B, 0xE2, 0x23, 0xE5, 0x54, + 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x44, + 0x24, 0x14, 0xB9, 0x17, 0xAF, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x58, 0xE7, 0xF7, 0x2E, 0x5F, + 0x5F, 0xF7, 0xEC, 0xC7, 0x8F, 0x27, 0x03, 0xA4, 0x7A, 0xFB, 0xF1, 0xED, 0x0B, 0x88, 0x8F, 0x54, + 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, + 0x13, 0x81, 0x50, 0xF4, 0x5E, 0xB8, 0xCA, 0x78, 0xBF, 0xB8, 0xFD, 0xAE, 0xE0, 0x8F, 0x07, 0x00, + 0x00, 0x80, 0xFC, 0x9D, 0xDB, 0x7D, 0xD5, 0x50, 0xCD, 0x9B, 0xAF, 0xC2, 0xF8, 0x88, 0x06, 0xC8, + 0x8F, 0x66, 0x10, 0x1F, 0xA9, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x60, 0x8A, 0xE6, 0x1D, 0xBF, 0x6D, 0x43, 0xF0, 0xC7, + 0x03, 0x00, 0x00, 0x40, 0xFE, 0xCE, 0xED, 0xB8, 0x6C, 0x6C, 0xF2, 0xF6, 0xE3, 0x02, 0x79, 0xFB, + 0xF1, 0x76, 0xE4, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x06, 0x53, 0x35, 0xDE, 0x84, 0x05, 0x00, + 0x00, 0x0A, 0x75, 0x76, 0xDB, 0xFC, 0xE0, 0xF6, 0xC3, 0xC6, 0x87, 0x78, 0xF6, 0xE3, 0xED, 0x1F, + 0x4C, 0xDE, 0x7E, 0x10, 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, + 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0xA3, 0x60, 0x2A, 0xE7, 0x1D, 0xBD, 0x79, 0x3C, 0xF8, + 0xE3, 0x01, 0x00, 0x00, 0x20, 0x5F, 0x66, 0xA8, 0x7F, 0xF6, 0xD9, 0xCD, 0x97, 0x04, 0xF1, 0x51, + 0x7B, 0xFB, 0x71, 0x2A, 0xBC, 0xFD, 0xF8, 0x66, 0xE5, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, + 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x04, + 0x53, 0xBE, 0x97, 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x8C, 0xB3, 0x43, 0x4B, 0x07, 0xDE, + 0xDD, 0xF8, 0xE1, 0x6A, 0x80, 0x9C, 0xB6, 0x01, 0x52, 0xF3, 0xEC, 0xC7, 0xF7, 0x2B, 0xB7, 0x1F, + 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, + 0xF0, 0xDF, 0xE9, 0x89, 0x10, 0xE8, 0xD4, 0xC6, 0x96, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x0C, + 0x1B, 0x20, 0xEB, 0x27, 0x06, 0xDF, 0x57, 0x73, 0xFB, 0x11, 0x7D, 0xFE, 0xC3, 0xDD, 0x7E, 0x10, + 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, + 0x7F, 0xA7, 0x27, 0x22, 0xA0, 0x93, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0x63, + 0x02, 0x00, 0x00, 0x20, 0x3F, 0xEF, 0xEE, 0x59, 0x3A, 0xE8, 0xDF, 0x7E, 0x88, 0xF8, 0x70, 0xB7, + 0x1F, 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, + 0xF2, 0xF0, 0xDF, 0xE9, 0x89, 0x00, 0xE8, 0xF4, 0xDC, 0x3F, 0x17, 0x27, 0x6E, 0x5B, 0x1F, 0xFC, + 0x31, 0x01, 0x00, 0x00, 0x90, 0x9F, 0x89, 0x6D, 0x8B, 0x47, 0xEB, 0xDE, 0x7C, 0x15, 0xDE, 0x7E, + 0xDC, 0x4D, 0x7C, 0x34, 0x9F, 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xE9, 0x65, + 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xE2, 0xF0, 0xDF, 0x25, 0xF3, 0x8E, 0xAF, 0xE5, 0x4D, 0x58, 0x00, + 0x00, 0x20, 0x7F, 0x13, 0x5B, 0xFA, 0xC6, 0xE5, 0xC3, 0xE7, 0xF7, 0xBB, 0x6F, 0xBD, 0x22, 0x3E, + 0x1A, 0x4F, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, + 0x4E, 0x4F, 0x1F, 0xFC, 0xBB, 0x65, 0xDE, 0xD1, 0x9B, 0x4F, 0x06, 0x7F, 0x4C, 0x00, 0x00, 0x00, + 0xE4, 0xC3, 0x0C, 0xF5, 0xCF, 0x7A, 0xF7, 0x99, 0x39, 0x95, 0xF8, 0xB0, 0x8B, 0x3E, 0x7C, 0xFE, + 0xD6, 0x5F, 0x11, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFD, 0x5D, 0xB5, 0x97, 0x56, 0x9A, 0xE0, 0x8F, 0x0A, + 0x00, 0x00, 0x80, 0x7C, 0xB8, 0x37, 0x60, 0x4D, 0x6C, 0xFC, 0x98, 0x0D, 0x90, 0x0B, 0xCC, 0xE9, + 0x47, 0xEC, 0xDC, 0xED, 0x87, 0x0B, 0x90, 0xFB, 0x6D, 0x80, 0x10, 0x1F, 0x0D, 0xA6, 0xC2, 0x21, + 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xFB, + 0xDD, 0x38, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x7C, 0xDD, 0xC4, 0x13, 0x1F, + 0x30, 0xA7, 0xFD, 0x00, 0x09, 0xBE, 0xFD, 0xEA, 0xE1, 0x99, 0xFE, 0xED, 0x07, 0xF1, 0xA1, 0xA6, + 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, + 0x0E, 0xFA, 0xDD, 0x3A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x74, 0x7D, 0x78, + 0xFB, 0x71, 0xCA, 0xCD, 0xDD, 0x7E, 0x7C, 0x67, 0x06, 0xF1, 0x21, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF9, 0xDD, 0x3C, + 0xF7, 0xCF, 0x3D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x79, 0x9A, 0xD8, 0x79, 0xD5, 0x68, 0xF5, 0xDB, + 0xAF, 0xDC, 0x0D, 0x88, 0x7B, 0xF6, 0xE3, 0x6B, 0xC4, 0x47, 0xFD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, 0x01, 0xBF, 0x07, 0xC6, + 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xAE, 0x26, 0xB6, 0x5D, 0x76, 0xB2, 0x7A, 0x03, 0xF2, 0xF0, 0x0C, + 0xF3, 0xF6, 0x7D, 0xC1, 0xED, 0x87, 0x8D, 0x8F, 0x3F, 0x10, 0x1F, 0xC1, 0x54, 0x38, 0xE4, 0x31, + 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0xE1, 0xBE, 0x17, + 0xE6, 0x1D, 0x59, 0x33, 0x16, 0xFC, 0x71, 0x01, 0x00, 0x00, 0xD0, 0xBE, 0x09, 0xFF, 0x0D, 0x58, + 0x93, 0xB7, 0x1F, 0x6F, 0x7E, 0x6D, 0xF2, 0xE6, 0xE3, 0x0D, 0xE2, 0xC3, 0x4E, 0x85, 0x43, 0x1E, + 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xEC, 0x7B, + 0x66, 0x47, 0x6E, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x20, 0x1F, 0x13, 0x43, 0xD7, 0x2F, 0x99, 0x78, + 0xFA, 0x13, 0xE6, 0xCC, 0xA3, 0x95, 0xCF, 0xFF, 0x70, 0xB7, 0x1F, 0x95, 0xF8, 0xB8, 0xC0, 0xBF, + 0xF9, 0x78, 0x83, 0xF8, 0x28, 0x68, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, + 0xDF, 0xE9, 0x89, 0x03, 0x7D, 0xAF, 0xED, 0xA5, 0x7E, 0xF7, 0x20, 0xFA, 0x92, 0xE0, 0x8F, 0x0D, + 0x00, 0x00, 0x80, 0xEC, 0xCE, 0x0C, 0x2D, 0x5B, 0x3B, 0xF1, 0xE4, 0x87, 0x2B, 0x9F, 0x01, 0xF2, + 0x60, 0xE5, 0xB5, 0xBB, 0xD5, 0xDB, 0x8F, 0xAF, 0xD9, 0xF8, 0xF8, 0x47, 0xE2, 0x23, 0xFF, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF3, 0xBD, 0x38, + 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x03, 0x00, 0x00, 0x20, 0x3B, 0xF7, 0x06, + 0xAC, 0x89, 0xC7, 0x2F, 0xF2, 0x03, 0xE4, 0xED, 0x6F, 0x55, 0x9E, 0xFB, 0x20, 0x3E, 0xDC, 0x54, + 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, + 0x41, 0xBE, 0x57, 0xE7, 0xBE, 0x26, 0x78, 0x13, 0x16, 0x00, 0x00, 0xC8, 0xC3, 0xD9, 0x5D, 0xD7, + 0x0C, 0x4E, 0x3C, 0x36, 0xC3, 0x9C, 0x71, 0xCF, 0x7E, 0x04, 0x6F, 0xBD, 0xFA, 0xC3, 0x7A, 0xE2, + 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, + 0xF1, 0x3D, 0xBB, 0xF9, 0xFE, 0xBC, 0xE3, 0xB7, 0x0D, 0x05, 0x7F, 0x6C, 0x00, 0x00, 0x00, 0x64, + 0xF7, 0xEE, 0xB6, 0xC5, 0xA3, 0x13, 0x8F, 0xCF, 0x30, 0xEF, 0x7C, 0xBB, 0x72, 0xFB, 0xF1, 0xE6, + 0x5F, 0x5D, 0x44, 0x7C, 0x14, 0x32, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, + 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x57, 0x89, 0x0F, 0x3F, 0x40, 0x8E, 0xDC, 0xCC, 0x9B, 0xB0, + 0x00, 0x00, 0x40, 0xFB, 0xCE, 0x6D, 0x5F, 0x60, 0x26, 0x1E, 0xB9, 0xC0, 0xBC, 0xE5, 0x5E, 0xBB, + 0xFB, 0xD7, 0xC4, 0x47, 0x31, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, + 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0xE1, 0xEF, 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, + 0x00, 0xB4, 0xC7, 0x0C, 0x5D, 0x3B, 0xFB, 0xDC, 0xB6, 0x4B, 0xCD, 0x3B, 0xDF, 0xB4, 0x01, 0xF2, + 0x75, 0xE2, 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, + 0xA7, 0x0E, 0xF1, 0xBD, 0xBA, 0x58, 0x7C, 0xB8, 0xBD, 0xB4, 0xC2, 0x98, 0x97, 0xBF, 0x38, 0x3B, + 0xF8, 0xE3, 0x03, 0x00, 0x00, 0x20, 0xBD, 0xB3, 0xFB, 0x97, 0x0E, 0xBC, 0xFB, 0xD8, 0x87, 0xCC, + 0xDB, 0x77, 0x5F, 0x64, 0xCE, 0x13, 0x1F, 0x05, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x89, 0xF8, 0x70, 0x7B, 0xE1, 0x6A, 0x63, + 0x8E, 0xDD, 0x36, 0x10, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDE, 0x99, 0x5D, 0x9F, 0xBB, 0xEB, + 0xD4, 0x86, 0x0F, 0x98, 0xF3, 0xFF, 0x44, 0x7C, 0xE4, 0x3F, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, + 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x27, 0xC2, 0x23, 0x98, 0xF7, 0xAF, + 0x4B, 0xCC, 0xE9, 0x9F, 0xDD, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x76, 0xAF, 0x3D, 0x70, 0xED, + 0x86, 0xF1, 0xDD, 0xAB, 0xCD, 0xD9, 0x17, 0xBF, 0x44, 0x7C, 0xE4, 0x3A, 0x15, 0x09, 0x45, 0x4D, + 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0xA7, 0xC3, 0xC3, 0xED, + 0xD4, 0xB3, 0xF3, 0xCC, 0xEF, 0x77, 0xCC, 0x31, 0xBF, 0xDB, 0x77, 0xD3, 0x60, 0xF0, 0xC7, 0x07, + 0x00, 0x00, 0x40, 0x7A, 0xAF, 0xEF, 0xB9, 0x75, 0xF8, 0xF7, 0x07, 0x6F, 0x35, 0xBF, 0xDF, 0xB7, + 0xD2, 0xBC, 0x79, 0xE0, 0x32, 0x73, 0xFE, 0x9F, 0x55, 0x54, 0x24, 0x9D, 0x08, 0x87, 0x3C, 0x26, + 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, + 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0x4E, 0x87, 0xC7, 0x99, 0xBF, 0xEB, 0x33, 0xE3, 0xBB, + 0xE6, 0x9A, 0xDF, 0x6D, 0xB3, 0xF1, 0xE1, 0xB6, 0x67, 0x39, 0xAF, 0xE2, 0x05, 0x00, 0x00, 0xD9, + 0xFD, 0xF3, 0xFF, 0xFE, 0x17, 0x17, 0xFE, 0x76, 0xFB, 0xEA, 0xFB, 0x7E, 0xB7, 0x6D, 0xF1, 0x5B, + 0xBF, 0xDF, 0xB9, 0xD8, 0xB8, 0xBD, 0x7D, 0xE8, 0x72, 0xFF, 0x5B, 0x2D, 0x74, 0x64, 0x34, 0x9A, + 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0xAE, 0x3E, 0x3C, 0xCE, 0xFE, + 0x43, 0x9F, 0x79, 0x73, 0xEF, 0x64, 0x78, 0xBC, 0xBE, 0x75, 0xCE, 0xF8, 0xEF, 0xB6, 0x5D, 0xBA, + 0x7E, 0xB4, 0xFF, 0x3D, 0x7F, 0x14, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDD, 0x7F, 0xBF, 0xFB, + 0x33, 0x17, 0xBF, 0xB6, 0x79, 0xF1, 0x96, 0x30, 0x42, 0xC6, 0x77, 0x2F, 0x36, 0xA7, 0xFF, 0xC6, + 0x1E, 0xD4, 0x65, 0x6C, 0xC4, 0x27, 0xC2, 0x21, 0x8F, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, 0xDF, + 0xAB, 0xAB, 0x0D, 0x8F, 0xF3, 0xFF, 0x34, 0xDF, 0xBC, 0xB5, 0x3F, 0x72, 0xE3, 0x61, 0xF7, 0xEA, + 0x33, 0x73, 0x7E, 0x34, 0x3E, 0xF4, 0xA9, 0x59, 0xC1, 0x1F, 0x17, 0x00, 0x00, 0x00, 0xF9, 0xF9, + 0xEF, 0xDF, 0x5D, 0xF2, 0x99, 0xD7, 0xB6, 0x2C, 0xFA, 0x59, 0x18, 0x22, 0x6F, 0xEC, 0xB9, 0xCC, + 0x4C, 0xFC, 0xBD, 0x0B, 0x02, 0x15, 0x1E, 0x6E, 0xB1, 0x68, 0xC8, 0x6B, 0x32, 0x1C, 0xF2, 0x98, + 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, + 0x3A, 0xC4, 0xF7, 0xEA, 0x26, 0xC3, 0xC3, 0xFB, 0xE7, 0xF9, 0xE6, 0x9D, 0x91, 0x79, 0x35, 0xE1, + 0xF1, 0xDA, 0xE6, 0x4B, 0xF7, 0x8F, 0x6F, 0xE9, 0xE3, 0xB5, 0xBB, 0x00, 0x00, 0xA0, 0x78, 0xFF, + 0xEB, 0xA1, 0xCB, 0xBE, 0xF8, 0xBB, 0x6D, 0x8B, 0x7E, 0x19, 0x86, 0x88, 0x7B, 0x3E, 0xE4, 0xEC, + 0x4F, 0x5D, 0x1C, 0x10, 0x1F, 0xF5, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, + 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0x51, 0x7D, 0xC0, 0x3C, 0x08, 0x8F, 0xD7, + 0xB7, 0x5E, 0x3A, 0xF6, 0xDA, 0x96, 0x4B, 0x79, 0xDD, 0x2E, 0x00, 0x00, 0x98, 0x5A, 0xA3, 0xFD, + 0xFD, 0x7F, 0xF4, 0xAB, 0x27, 0x2E, 0xFB, 0x3F, 0xA2, 0xCF, 0x87, 0xBC, 0x35, 0xEC, 0x1E, 0x54, + 0x17, 0xD1, 0x90, 0xD7, 0x64, 0x38, 0xE4, 0x31, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x55, 0xC2, 0x23, 0xFE, + 0x80, 0xB9, 0x8D, 0x8E, 0x5F, 0xBE, 0xBE, 0x7D, 0xEE, 0xBA, 0xE0, 0x8F, 0x00, 0x00, 0x00, 0x80, + 0xCE, 0xF8, 0xD5, 0x23, 0x57, 0x7E, 0xE0, 0x37, 0x4F, 0x2E, 0xDE, 0x30, 0x19, 0x22, 0x8B, 0xCC, + 0xE9, 0xBF, 0xB9, 0xCC, 0x78, 0xFF, 0x2A, 0x02, 0xA2, 0x9D, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, + 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, + 0xDF, 0xAB, 0x6B, 0xFC, 0x80, 0x39, 0xCF, 0x79, 0x00, 0x00, 0x80, 0xAE, 0x72, 0xF2, 0x91, 0xC5, + 0xF3, 0x5F, 0x7D, 0x66, 0xF1, 0x0E, 0x17, 0x20, 0x6E, 0xE3, 0xBB, 0x17, 0x99, 0x33, 0x7F, 0x67, + 0x0F, 0xE2, 0x2A, 0x26, 0xD2, 0x4E, 0x86, 0x43, 0x1E, 0x8B, 0x47, 0x43, 0x5E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x73, 0xEA, 0x01, + 0xF3, 0xDF, 0x6D, 0xBD, 0x74, 0xF0, 0xBF, 0x7D, 0x67, 0xDE, 0x07, 0x82, 0x2F, 0x73, 0x00, 0x00, + 0x80, 0xEE, 0xF3, 0xAB, 0x8D, 0x8B, 0xD6, 0xBC, 0xFA, 0xCC, 0xC2, 0x9F, 0x87, 0x21, 0xF2, 0xC6, + 0x9E, 0xC5, 0xE6, 0xDD, 0x7F, 0xB0, 0x87, 0x7D, 0x15, 0x16, 0x49, 0x26, 0xC3, 0x21, 0x8F, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, + 0x83, 0x7C, 0xAF, 0xCD, 0xFB, 0xE7, 0x05, 0x75, 0x0F, 0x98, 0xDB, 0x8D, 0xFE, 0x7F, 0x8F, 0x5E, + 0x3A, 0x27, 0xF8, 0xB2, 0x06, 0x00, 0x00, 0xE8, 0x6E, 0xFE, 0xF3, 0x21, 0x83, 0x8B, 0xFF, 0xE2, + 0x77, 0xDB, 0x16, 0xFE, 0x32, 0x0C, 0x91, 0x37, 0x0F, 0x2C, 0x36, 0x67, 0x7F, 0x6A, 0x0F, 0xFE, + 0x2A, 0x32, 0x1A, 0x4D, 0x86, 0x43, 0x1E, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, + 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x87, 0xF9, 0x5E, 0xDB, 0xE9, 0xBF, 0xE9, 0xAB, + 0x79, 0xC0, 0xFC, 0xB5, 0x67, 0xE6, 0x1C, 0xE3, 0x01, 0x73, 0x00, 0x00, 0xD0, 0xB3, 0x6C, 0x88, + 0xFC, 0xBB, 0x57, 0x37, 0x2F, 0xBA, 0xE7, 0x77, 0xDB, 0x16, 0xBD, 0x15, 0x86, 0xC8, 0xDB, 0x87, + 0x12, 0x3E, 0xA8, 0x2E, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xC3, 0x7C, 0x2F, 0x6D, 0xE2, 0xEF, 0xE7, 0xF3, + 0x80, 0x39, 0x00, 0x00, 0x28, 0xAF, 0x93, 0xF7, 0x2E, 0xFC, 0xC8, 0x2B, 0x9B, 0x16, 0x3F, 0x1A, + 0x46, 0x88, 0x7B, 0x3E, 0xA4, 0xE9, 0x83, 0xEA, 0x32, 0x1C, 0xF2, 0x98, 0x0A, 0x87, 0x3C, 0xA6, + 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x3E, 0xD0, 0xF7, 0xCA, + 0xCE, 0xFE, 0xC3, 0x7C, 0xF3, 0xE6, 0x5E, 0xF7, 0xED, 0x56, 0x95, 0xF8, 0xE0, 0x01, 0x73, 0x00, + 0x00, 0x50, 0x6A, 0xFF, 0xE3, 0xFE, 0xCF, 0x2C, 0x7A, 0x75, 0xF3, 0xA2, 0xE7, 0xA2, 0x21, 0x32, + 0xF1, 0xF7, 0x36, 0x0C, 0x88, 0x8F, 0x84, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, + 0x7D, 0xA8, 0xEF, 0x85, 0x9D, 0xFF, 0xA7, 0x05, 0xE6, 0xED, 0x83, 0x7D, 0x41, 0x78, 0x04, 0x37, + 0x1F, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0xA6, 0x8B, 0x5F, 0x3E, 0xB6, 0xF8, 0x96, 0xD7, 0xB7, 0x2E, + 0x7C, 0x39, 0x0C, 0x11, 0xF7, 0xA0, 0xBA, 0xFF, 0x7C, 0x88, 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, + 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xF6, + 0xDD, 0x3E, 0xF7, 0x80, 0xF9, 0xA9, 0x67, 0xA3, 0xE1, 0xE1, 0xC7, 0x07, 0x0F, 0x98, 0x03, 0x00, + 0x80, 0xE9, 0xC7, 0x3D, 0xA8, 0xFE, 0xDB, 0xA7, 0x17, 0x7F, 0xF5, 0xF5, 0xAD, 0x8B, 0x7E, 0xE3, + 0x22, 0xE4, 0x77, 0x3B, 0x16, 0x9A, 0x37, 0xF7, 0x2F, 0x32, 0xE7, 0xFE, 0xD1, 0x1D, 0xEC, 0x55, + 0x44, 0x64, 0x5D, 0x3C, 0x1A, 0xF2, 0x9A, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, + 0xFC, 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xED, 0xAB, 0x3C, 0x60, 0x3E, 0x19, 0x1E, 0xAF, 0x3D, 0x33, + 0x97, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x7E, 0xF5, 0xC8, 0xBC, 0x0F, 0xFC, 0xE6, 0xE9, 0x85, 0xF7, + 0xBD, 0xBE, 0x75, 0xC1, 0x5B, 0x2E, 0x42, 0xDC, 0xDE, 0x1E, 0x59, 0x6C, 0xBC, 0x7F, 0x51, 0x31, + 0x91, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, + 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xCD, 0xAB, 0x3C, 0x60, 0x1E, 0x3E, 0xE7, 0x61, 0xC3, 0x63, 0xCB, + 0x5C, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x88, 0x1B, 0xFB, 0x4F, 0x9F, 0xB9, 0xF8, 0x95, 0xCD, 0x0B, + 0xB7, 0x84, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCD, 0xA9, 0xE7, 0xDA, 0x09, 0x11, 0x15, 0x0E, 0x79, + 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC0, 0xEF, + 0xD6, 0xD5, 0x3E, 0x60, 0x3E, 0xD7, 0xBC, 0xBE, 0x75, 0xEE, 0xB8, 0xFD, 0x91, 0x07, 0xCC, 0x01, + 0x00, 0x00, 0x9A, 0xF9, 0x9F, 0x0F, 0x2D, 0x5A, 0xFA, 0xEA, 0xE6, 0x85, 0x3F, 0x0B, 0x43, 0x64, + 0x7C, 0x68, 0x91, 0x39, 0x33, 0xEA, 0x0E, 0xFE, 0x2A, 0x32, 0x1A, 0x2D, 0x1E, 0x0D, 0x79, 0x4D, + 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC8, 0xEF, 0xC6, + 0xD5, 0x3F, 0x60, 0x6E, 0xB7, 0x75, 0x0E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA4, 0xF1, 0xBF, 0x1E, + 0x5A, 0xF0, 0xC5, 0xD7, 0xB6, 0x2E, 0xF8, 0x65, 0x18, 0x22, 0x6F, 0xEC, 0x5D, 0x64, 0xCE, 0xFE, + 0xC4, 0x45, 0x80, 0x0A, 0x8E, 0xE8, 0xE2, 0xD1, 0x90, 0xD7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, + 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x07, 0xFD, 0x6E, 0x9B, 0x7E, 0xC0, 0x7C, 0x2E, 0x0F, + 0x98, 0x03, 0x00, 0x00, 0x64, 0xE5, 0x1E, 0x54, 0xFF, 0xF5, 0xC6, 0x45, 0x7F, 0x19, 0x7D, 0x3E, + 0xC4, 0x3D, 0xA8, 0x7E, 0xFE, 0x67, 0x2A, 0x3C, 0xDC, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0x61, 0xBF, 0xDB, 0xC6, 0x03, 0xE6, + 0x00, 0x00, 0x00, 0x05, 0x72, 0x0F, 0xAA, 0xFF, 0xFA, 0xC9, 0x85, 0x1B, 0xC2, 0x08, 0x71, 0x7B, + 0xE7, 0x70, 0xFC, 0xF9, 0x10, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xD8, 0xEF, 0xA6, 0xF1, 0x80, 0x39, 0x00, 0x00, 0xC0, 0x14, + 0x3A, 0xF9, 0xC8, 0xE2, 0xF9, 0xBF, 0xDD, 0xB4, 0x60, 0x47, 0x18, 0x21, 0xE1, 0x83, 0xEA, 0x3A, + 0x1C, 0xF2, 0x58, 0x3C, 0x10, 0x8A, 0x9C, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, + 0x03, 0x7F, 0xB7, 0x8C, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x3A, 0xE8, 0x57, 0x1B, 0x17, 0xAD, 0x79, + 0x65, 0xD3, 0x82, 0x9F, 0x57, 0x42, 0x64, 0x81, 0x19, 0x1F, 0x5A, 0x68, 0xCE, 0x8C, 0xBA, 0x43, + 0xBC, 0x8A, 0x88, 0xAC, 0x8B, 0x07, 0x42, 0x91, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, + 0x3D, 0x7D, 0xE8, 0xEF, 0x86, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x40, 0x97, 0xA8, 0x7C, 0x90, 0xE1, + 0xC2, 0xAF, 0xBE, 0xB6, 0x75, 0xFE, 0x2F, 0x5D, 0x84, 0xB8, 0xBD, 0xB9, 0x7F, 0xA1, 0x39, 0xFB, + 0x13, 0x77, 0xA0, 0x57, 0x41, 0x91, 0x66, 0xF1, 0x40, 0x28, 0x72, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, + 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFE, 0x9D, 0x5E, 0xA3, 0x07, 0xCC, 0x4F, 0x7E, 0xE7, 0x53, 0xF3, + 0x83, 0x2F, 0x01, 0x00, 0x00, 0x00, 0x74, 0xC2, 0x60, 0xFF, 0xA7, 0xFE, 0xDD, 0xAB, 0x9B, 0x16, + 0xDC, 0xF3, 0xFA, 0xD6, 0xF9, 0x6F, 0x85, 0x21, 0xF2, 0xF6, 0x88, 0x7B, 0x50, 0x5D, 0x85, 0x45, + 0x92, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFF, + 0x9D, 0x9E, 0x7A, 0xC0, 0xFC, 0xB7, 0x1B, 0xE7, 0xDE, 0x10, 0xFC, 0x23, 0x0F, 0x00, 0x00, 0x80, + 0x6E, 0xF0, 0xFF, 0x7E, 0x63, 0xF1, 0x87, 0x7F, 0xF3, 0xD4, 0x82, 0x47, 0xC3, 0x08, 0x71, 0x3B, + 0xF5, 0xDC, 0x22, 0xE3, 0xFD, 0x8B, 0x8A, 0x8C, 0x46, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, + 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xFE, 0x3B, 0xB9, 0xF8, 0x03, 0xE6, 0xEE, 0x39, 0x8F, + 0x57, 0x9F, 0xBE, 0xF4, 0x3F, 0x06, 0xFF, 0x88, 0x03, 0x00, 0x00, 0xA0, 0x1B, 0xFD, 0x8F, 0xFB, + 0x17, 0x2C, 0x7A, 0x75, 0xF3, 0x82, 0x9F, 0x85, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCC, 0xE9, 0xBF, + 0x75, 0x07, 0x61, 0x15, 0x1C, 0xD1, 0xA9, 0x48, 0x28, 0x6A, 0xB1, 0x03, 0x7A, 0x99, 0x27, 0x0F, + 0xFF, 0x9D, 0x9E, 0x0E, 0x80, 0x4E, 0xAD, 0xEE, 0x01, 0xF3, 0xCD, 0x73, 0xDE, 0xB2, 0x3F, 0xF2, + 0x80, 0x39, 0x00, 0x00, 0x40, 0x2F, 0xF9, 0xE5, 0xC3, 0xF3, 0x6F, 0x79, 0x75, 0xCB, 0x82, 0x97, + 0xC3, 0x10, 0x71, 0x0F, 0xAA, 0x4F, 0xFC, 0x57, 0x77, 0xF8, 0x27, 0x3E, 0xA6, 0x6C, 0xF2, 0xF0, + 0xDF, 0xE9, 0xE9, 0x08, 0xE8, 0xC4, 0x1A, 0x3D, 0x60, 0x3E, 0xBE, 0xA5, 0x6F, 0x76, 0xF0, 0x8F, + 0x31, 0x00, 0x00, 0x00, 0x7A, 0x49, 0xF8, 0xA0, 0x7A, 0xF4, 0xF9, 0x10, 0xF7, 0xA0, 0xFA, 0xB9, + 0x7F, 0x74, 0x21, 0x40, 0x7C, 0x14, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x74, 0x08, 0x4C, 0xF5, 0x1A, + 0x3D, 0x60, 0xFE, 0xEB, 0xC7, 0x3E, 0x7D, 0x55, 0xF0, 0x8F, 0x2E, 0x00, 0x00, 0x00, 0x7A, 0x99, + 0xFB, 0x20, 0xC3, 0xDF, 0x6C, 0x9C, 0x7F, 0x5F, 0xED, 0x83, 0xEA, 0x0B, 0xCD, 0xF9, 0x9F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x63, 0x60, 0xAA, 0x17, + 0x7F, 0xC0, 0xFC, 0xF5, 0x6D, 0x73, 0x4E, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x40, 0x49, 0xFD, 0xDB, + 0x83, 0x7D, 0x7D, 0xAF, 0x6C, 0x5E, 0xB0, 0xA5, 0x12, 0x21, 0xF3, 0xCD, 0xEF, 0x77, 0xCD, 0x37, + 0xA7, 0x9E, 0x5B, 0x68, 0xBC, 0x7F, 0x51, 0xC1, 0x90, 0xE7, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, + 0xFF, 0x4E, 0x4F, 0xC7, 0xC0, 0x54, 0x8E, 0x07, 0xCC, 0x01, 0x00, 0x00, 0xA6, 0xB1, 0xFF, 0xF9, + 0xD0, 0xBC, 0xA5, 0xAF, 0x6E, 0xEE, 0xFB, 0x99, 0x8B, 0x90, 0x30, 0x44, 0xCE, 0x8C, 0xBA, 0x03, + 0xB4, 0x8A, 0x87, 0x76, 0x17, 0x3B, 0xA0, 0x97, 0x79, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x20, 0x98, + 0xAA, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xAA, 0x7E, 0xF5, 0x58, 0xDF, 0x5F, 0x54, 0x3E, 0xC8, + 0xB0, 0x12, 0x22, 0xE3, 0x43, 0xF6, 0xC0, 0xF8, 0x13, 0x77, 0x98, 0x56, 0x21, 0x91, 0x65, 0xB1, + 0x03, 0x7A, 0x99, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x82, 0xA9, 0x18, 0x0F, 0x98, 0x03, 0x00, + 0x00, 0x40, 0x72, 0x0F, 0xAA, 0xFF, 0x7A, 0x63, 0xDF, 0x5F, 0xBE, 0xBE, 0xB5, 0xEF, 0xAD, 0x30, + 0x44, 0xDE, 0xDC, 0x6F, 0x0F, 0x90, 0x6D, 0x3F, 0x1F, 0x22, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, + 0x77, 0x7A, 0x3A, 0x0C, 0x8A, 0x1E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x20, 0x11, 0xFF, 0x41, 0xF5, + 0xA7, 0xFA, 0x1E, 0x0D, 0x23, 0xC4, 0xCD, 0x3D, 0xA8, 0x9E, 0xED, 0xF9, 0x10, 0x71, 0x48, 0x2F, + 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x71, 0x50, 0xF4, 0xCE, 0xFC, 0xDD, 0x7C, 0x1E, 0x30, 0x07, + 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x41, 0xF5, 0x57, 0x9F, 0x99, 0x3F, 0x12, 0x46, 0x48, 0xFA, 0x07, + 0xD5, 0xC5, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x41, 0x91, 0x73, 0xCF, 0x79, + 0xF0, 0x80, 0x39, 0x00, 0x00, 0x00, 0xDA, 0xF2, 0xAB, 0x27, 0xFA, 0xD6, 0x44, 0x1F, 0x54, 0x77, + 0xCF, 0x87, 0xB4, 0x7E, 0x50, 0x3D, 0x76, 0x40, 0x2F, 0xF3, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x81, + 0x50, 0xD4, 0xCE, 0xFD, 0x94, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x90, 0xA3, 0xCA, 0x07, 0x19, 0xF6, + 0x7D, 0x35, 0xFA, 0xA0, 0xBA, 0x7B, 0x3E, 0x44, 0x3F, 0xA8, 0x1E, 0x3B, 0xA0, 0x97, 0x79, 0xF2, + 0xF0, 0xDF, 0xE9, 0xE9, 0x48, 0x28, 0x62, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x42, 0xB9, 0xE7, + 0x43, 0x5E, 0xDD, 0xD4, 0x77, 0x4F, 0xF4, 0x41, 0xF5, 0xB7, 0x0E, 0x46, 0x1F, 0x54, 0x17, 0x87, + 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0A, 0x79, 0x2F, 0x7C, 0xC0, 0x3C, 0xFA, 0x9C, + 0x87, 0x1D, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA0, 0x18, 0x47, 0xBF, 0xB1, 0xF8, 0xC3, 0xAF, 0x6C, + 0xEE, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0x3B, 0x87, 0xEC, 0xC1, 0xF4, 0x5F, 0xC4, 0x41, 0xBD, 0x8C, + 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x42, 0xDE, 0x53, 0x0F, 0x98, 0xFF, 0xFA, 0xE1, 0x4B, 0x6F, + 0x0E, 0xFE, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xE3, 0x3F, 0xA8, 0xEE, 0x9E, 0x0F, 0xD9, 0xDE, + 0x67, 0xDC, 0x7E, 0xBF, 0xB3, 0xCF, 0x9C, 0x7A, 0xCE, 0x1E, 0x54, 0xD5, 0xA1, 0xBD, 0x2C, 0x93, + 0x87, 0xFF, 0x4E, 0xAF, 0x3E, 0x14, 0xF2, 0x9E, 0x7A, 0xC0, 0xFC, 0xD7, 0x83, 0x73, 0xFE, 0xB7, + 0xE0, 0x1F, 0x05, 0x00, 0x00, 0x00, 0x60, 0xEA, 0xFC, 0xF2, 0xE1, 0xB9, 0xB7, 0xBC, 0xB6, 0xB5, + 0xEF, 0x97, 0x61, 0x88, 0x8C, 0xEF, 0x9E, 0x6F, 0x26, 0xFE, 0xAB, 0x3D, 0x18, 0xAB, 0x03, 0x7C, + 0x2F, 0x4F, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0C, 0x79, 0x4D, 0x3D, 0x60, 0xFE, 0xDA, 0xA6, 0x39, + 0x77, 0xF3, 0x80, 0x39, 0x00, 0x00, 0x00, 0x3A, 0xCA, 0x7F, 0x50, 0xFD, 0xC9, 0xBE, 0xAF, 0xBE, + 0xBE, 0x65, 0xDE, 0x5B, 0x61, 0x88, 0xBC, 0xB9, 0x6F, 0xBE, 0x39, 0xFB, 0x13, 0x7B, 0x48, 0x56, + 0x87, 0xF9, 0x5E, 0x9B, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x1A, 0xF2, 0x98, 0x7A, 0xC0, 0xFC, 0xD5, + 0xCD, 0x73, 0x37, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xBA, 0x8A, 0x7B, 0x50, 0xFD, 0xB7, 0x9B, + 0xFA, 0xEE, 0x09, 0x23, 0xC4, 0xED, 0xED, 0x61, 0xF7, 0xA0, 0xBA, 0x38, 0xD4, 0xF7, 0xCA, 0xE4, + 0xE1, 0xBF, 0xD3, 0xD3, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0xC9, 0x3D, + 0x1F, 0xF2, 0xCA, 0xA6, 0xBE, 0x2D, 0xD1, 0x10, 0x71, 0xCF, 0x87, 0xF4, 0xDC, 0x83, 0xEA, 0xF2, + 0xF0, 0xDF, 0xE9, 0xE9, 0x78, 0x68, 0x77, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x9E, 0x57, 0xF9, + 0x20, 0xC3, 0x79, 0x35, 0x0F, 0xAA, 0x9F, 0xFE, 0x5B, 0x7B, 0xE0, 0x55, 0x87, 0xFD, 0x6E, 0x9B, + 0x3C, 0xFC, 0x77, 0x7A, 0xF5, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xA5, 0xF3, + 0xAB, 0xC7, 0xFA, 0xFE, 0x22, 0xFE, 0xA0, 0x7A, 0x57, 0x3F, 0x1F, 0x22, 0x0F, 0xFF, 0x9D, 0x9E, + 0x0E, 0x88, 0xAC, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0x94, 0x9A, 0x7B, 0x50, 0xFD, 0xB5, 0x2D, + 0xF3, 0xBE, 0x1E, 0x7F, 0x50, 0xFD, 0xDC, 0x3F, 0xDA, 0xC3, 0xB5, 0x8A, 0x80, 0x4E, 0x4D, 0x1E, + 0xFE, 0x3B, 0x3D, 0x1D, 0x11, 0x59, 0xC6, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x98, 0x56, 0xDC, 0x83, + 0xEA, 0xBF, 0x79, 0xAA, 0xEF, 0xC7, 0x61, 0x84, 0xB8, 0x75, 0xCD, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, + 0xE9, 0xE9, 0x90, 0x48, 0xBB, 0x46, 0x0F, 0x98, 0xBF, 0xB6, 0xE5, 0xD2, 0x81, 0xE0, 0x7F, 0x1A, + 0x00, 0x00, 0x00, 0xA0, 0xBC, 0xFC, 0x0F, 0x32, 0xDC, 0xD4, 0x37, 0x12, 0x46, 0x48, 0xF8, 0x41, + 0x86, 0x1D, 0x7B, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x13, 0x69, 0xA7, 0x1E, 0x30, 0x7F, + 0xE5, 0xC9, 0x39, 0xB7, 0x07, 0xFF, 0x53, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x87, 0x7B, 0x50, 0xFD, + 0xB5, 0x2D, 0xF3, 0x8E, 0x85, 0x21, 0xE2, 0x9E, 0x0F, 0x39, 0x33, 0x6A, 0x0F, 0xDF, 0x2A, 0x12, + 0x8A, 0x9A, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x26, 0xD2, 0x4C, 0x3D, 0x60, 0xFE, 0xCA, 0xA6, 0xB9, + 0xFF, 0x57, 0xF0, 0xB7, 0x1E, 0x00, 0x00, 0x00, 0x98, 0x9E, 0xC2, 0x0F, 0x32, 0x8C, 0x3E, 0xA8, + 0xFE, 0xC6, 0x9E, 0x29, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x14, 0x49, 0x17, 0x7F, + 0xC0, 0xDC, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x98, 0xF0, 0x83, 0x0C, 0xE3, 0x0F, 0xAA, + 0x17, 0xF6, 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2A, 0x92, 0x8C, 0x07, 0xCC, 0x01, 0x00, + 0x00, 0x80, 0x0C, 0x5E, 0x79, 0x72, 0xCE, 0xA5, 0xF1, 0x0F, 0x32, 0x74, 0x0F, 0xAA, 0xE7, 0xFA, + 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2C, 0x5A, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, + 0x1C, 0xFC, 0x7A, 0x63, 0xDF, 0x55, 0xAF, 0x6E, 0x9E, 0xF7, 0x4F, 0x61, 0x84, 0x84, 0x0F, 0xAA, + 0xCB, 0xA0, 0x48, 0x33, 0x79, 0xF8, 0xEF, 0xF4, 0xEA, 0xC3, 0x22, 0xC9, 0x78, 0xC0, 0x1C, 0x00, + 0x00, 0x00, 0xC8, 0xD9, 0x2F, 0x1F, 0x9E, 0x7B, 0x4B, 0xFC, 0x83, 0x0C, 0x33, 0x3F, 0xA8, 0x2E, + 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x8B, 0x66, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, 0x40, 0xFE, + 0x07, 0x19, 0x6E, 0xEE, 0xFB, 0x3F, 0xE3, 0xCF, 0x87, 0xA4, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, + 0x3D, 0x1D, 0x18, 0x8D, 0xA6, 0x1E, 0x30, 0xFF, 0xED, 0xC6, 0x4B, 0xBF, 0xC5, 0x03, 0xE6, 0x00, + 0x00, 0x00, 0x40, 0x01, 0xC2, 0x07, 0xD5, 0xC3, 0x08, 0x71, 0x7B, 0xEB, 0x40, 0x82, 0x07, 0xD5, + 0xE5, 0xE1, 0xBF, 0xD3, 0xD3, 0x91, 0xA1, 0xE6, 0x9E, 0xF3, 0x88, 0x3F, 0x60, 0xFE, 0xCA, 0x53, + 0x73, 0xB6, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x00, 0x30, 0x05, 0xDC, 0x07, 0x19, 0xBE, 0xBE, 0x75, + 0xDE, 0xFE, 0x68, 0x88, 0xBC, 0x73, 0xA8, 0xC1, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xD0, + 0x88, 0x4F, 0x3D, 0x60, 0xFE, 0xDA, 0xE6, 0x39, 0x3F, 0xE1, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, + 0x03, 0xDC, 0x07, 0x19, 0xC6, 0x1F, 0x54, 0x3F, 0xFD, 0xB7, 0xF6, 0xF0, 0x5E, 0x82, 0xF8, 0x70, + 0x0F, 0x98, 0xD7, 0x3C, 0xE7, 0xC1, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x40, 0x77, 0x88, 0x7F, 0x90, + 0xE1, 0xF8, 0xEE, 0x3E, 0x33, 0xF1, 0xF7, 0xEE, 0x20, 0xAF, 0x02, 0xA0, 0x93, 0xAB, 0x0F, 0x8D, + 0xF8, 0xDC, 0x03, 0xE6, 0x6F, 0x0C, 0xD5, 0x3F, 0x60, 0x3E, 0xDA, 0xFF, 0x9E, 0x3F, 0x0A, 0x7E, + 0xBB, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xCD, 0x7F, 0x50, 0x7D, 0xCB, 0xBC, 0xAF, 0x57, 0x1E, 0x54, + 0xB7, 0x07, 0x78, 0xBB, 0x37, 0xF7, 0xF5, 0xD9, 0x03, 0xBD, 0x3B, 0xD8, 0xAB, 0x18, 0x98, 0xEA, + 0xD5, 0xC7, 0x46, 0x74, 0xEE, 0x83, 0x04, 0x79, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xE8, 0x31, 0xEE, + 0x41, 0xF5, 0x57, 0x36, 0xCD, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0xDB, 0xC3, 0xF3, 0xED, 0x01, 0x5F, + 0x45, 0xC1, 0x54, 0x4D, 0x47, 0x87, 0x1B, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x00, 0x25, 0xF0, 0x6F, + 0x1B, 0xFA, 0xFA, 0x5E, 0xDB, 0x32, 0xF7, 0x27, 0x61, 0x84, 0xFC, 0x7E, 0xE7, 0x3C, 0x73, 0xEA, + 0xD9, 0xF9, 0xF6, 0xC0, 0xAF, 0x02, 0xA1, 0xC8, 0x35, 0x0E, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, + 0x00, 0x4A, 0xE6, 0x57, 0x4F, 0x5C, 0xBA, 0xC6, 0x86, 0xC8, 0xB1, 0x68, 0x88, 0x9C, 0xF9, 0x3B, + 0x17, 0x01, 0x2A, 0x16, 0xF2, 0x5E, 0x7D, 0x78, 0xB8, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0x50, + 0x62, 0xEE, 0x01, 0xEE, 0xDF, 0x3E, 0x39, 0xF7, 0xAB, 0xAF, 0x6F, 0x9B, 0x37, 0x1E, 0x86, 0x88, + 0x7B, 0x50, 0xBD, 0xD8, 0xE7, 0x43, 0xEA, 0xC3, 0x83, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x69, + 0xA4, 0xF2, 0x41, 0x86, 0x73, 0xEF, 0x79, 0x7D, 0xCB, 0xDC, 0x9A, 0x07, 0xD5, 0xCF, 0xFD, 0x34, + 0xEF, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, 0xA6, 0xB1, 0x57, 0x9E, 0x9C, + 0x73, 0xA9, 0x7A, 0x50, 0x3D, 0x9F, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, + 0xE0, 0x7B, 0x6D, 0x47, 0xDF, 0x40, 0xE5, 0x83, 0x0C, 0x27, 0x43, 0xE4, 0x8D, 0x3D, 0x7D, 0xFE, + 0x33, 0x22, 0xD9, 0xDE, 0x9A, 0x55, 0x79, 0xB8, 0xDC, 0x3D, 0xE3, 0x11, 0xBF, 0xF1, 0xE0, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x57, 0x9E, 0x99, 0x73, 0xDB, 0xEB, 0xDB, 0xE7, 0x9E, 0x8C, + 0x86, 0x48, 0x18, 0x23, 0xEF, 0x1C, 0x9A, 0x6F, 0x4E, 0xFF, 0xCD, 0x7C, 0xFF, 0x79, 0x91, 0xF8, + 0x0D, 0x89, 0xFB, 0xFF, 0xDD, 0xBF, 0xEE, 0xFE, 0xFD, 0x77, 0x0E, 0xF5, 0xD5, 0x3C, 0xDF, 0x11, + 0xCE, 0x3D, 0x60, 0x6E, 0x7F, 0xEE, 0x75, 0xC1, 0x5F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xDC, + 0x1B, 0xB3, 0x5E, 0x79, 0x66, 0xEE, 0x70, 0x3C, 0x44, 0x5A, 0x2E, 0x16, 0x1D, 0x95, 0xCD, 0x19, + 0xFA, 0xF5, 0xA3, 0x97, 0xDE, 0x1C, 0xFC, 0xD4, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xB9, 0x87, 0xD5, + 0x7F, 0xB3, 0x71, 0xDE, 0x9D, 0xAF, 0x3E, 0x3D, 0xEF, 0xA1, 0x57, 0x37, 0xCF, 0x7D, 0x59, 0x46, + 0x47, 0xB8, 0x20, 0x38, 0x5E, 0xDB, 0x3C, 0xF7, 0xD8, 0xAB, 0xCF, 0xCC, 0x7D, 0xDA, 0xDD, 0x76, + 0xFC, 0xB7, 0xEF, 0xCC, 0xFB, 0x40, 0xF0, 0x53, 0x01, 0x00, 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x35, + 0xB9, 0xFE, 0x07, 0x1B, 0xEE, 0xE8, 0x1B, 0x88, 0xEF, 0xDF, 0x36, 0xCC, 0xEE, 0x0B, 0xFE, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xD0, 0x15, 0xDE, 0xF3, 0x9E, 0xFF, 0x1F, 0x5B, 0xE2, 0x94, 0x1D, 0x86, + 0x86, 0x23, 0x09, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; + +static const uint8_t tree_gif[] PROGMEM = { + + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x28, 0x00, 0x3C, 0x00, 0x83, 0x0B, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x21, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x94, 0x00, 0x00, 0xB5, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0xFF, 0x00, 0x6B, 0x00, 0x00, 0xFF, 0x33, 0x33, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x00, 0xCC, 0x00, 0x00, 0x21, 0xFF, 0x0B, + 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0xA0, 0x0F, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, + 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, + 0x8C, 0xCA, 0xE8, 0x85, 0xE8, 0xA5, 0xAC, 0x67, 0xEA, 0x8A, 0xE5, 0x2B, 0x2F, 0xE3, 0x31, 0xCE, + 0xAF, 0x41, 0x1C, 0xB8, 0x1B, 0x24, 0x80, 0x04, 0xAF, 0x87, 0x02, 0x26, 0x88, 0xA1, 0x07, 0x60, + 0xB9, 0x44, 0x82, 0x04, 0x02, 0x80, 0x61, 0x30, 0x70, 0x6E, 0x0C, 0x86, 0x03, 0x60, 0x20, 0xB0, + 0x6A, 0xA8, 0xD3, 0x03, 0x21, 0xEC, 0xAD, 0x18, 0x12, 0xE8, 0xF4, 0xB1, 0x4C, 0x51, 0xA0, 0x07, + 0xEA, 0x07, 0x5B, 0xF2, 0x70, 0x13, 0x76, 0xD4, 0xA8, 0xBC, 0xBC, 0xEA, 0x1F, 0x06, 0x77, 0x51, + 0x73, 0x50, 0x0A, 0x80, 0x7F, 0x04, 0x4C, 0x4D, 0x65, 0x06, 0x0A, 0x3B, 0x3B, 0x62, 0x5A, 0x53, + 0x65, 0x05, 0x02, 0x04, 0x68, 0x0C, 0x6A, 0x00, 0x5D, 0x5E, 0x0F, 0x0F, 0x6A, 0x98, 0x6A, 0x09, + 0x7B, 0x56, 0x9D, 0x09, 0x0C, 0xA7, 0xA7, 0x7F, 0x06, 0x9D, 0xA3, 0x44, 0x02, 0x01, 0x03, 0x90, + 0x0C, 0x87, 0x03, 0x06, 0x02, 0x05, 0x56, 0x06, 0x01, 0x02, 0x53, 0xB1, 0xB3, 0x04, 0x79, 0xAF, + 0x55, 0x48, 0x51, 0xB0, 0x87, 0xC0, 0x7F, 0x07, 0x95, 0x05, 0x85, 0x44, 0xB6, 0x00, 0x05, 0x53, + 0x96, 0x6A, 0x60, 0x02, 0x0A, 0x48, 0x89, 0x00, 0x07, 0xA1, 0x69, 0x7D, 0x2B, 0x3D, 0x9D, 0x41, + 0xA1, 0x04, 0xA0, 0xDD, 0x9D, 0x38, 0x02, 0xE1, 0x6A, 0x07, 0x62, 0x0C, 0x77, 0xE6, 0xAD, 0x2F, + 0x05, 0x01, 0xB6, 0x90, 0x07, 0xA8, 0xA7, 0x54, 0xB5, 0xBC, 0x06, 0x33, 0x50, 0x53, 0x3A, 0x00, + 0x39, 0x62, 0x10, 0x6B, 0xDF, 0xBE, 0x19, 0x03, 0x18, 0x40, 0xA9, 0x15, 0x4B, 0xCC, 0x0E, 0x77, + 0x3B, 0x04, 0xC4, 0x02, 0x38, 0x43, 0x21, 0x96, 0x2C, 0xD3, 0xD6, 0x81, 0x01, 0x26, 0x2A, 0xFB, + 0xDE, 0x07, 0x7C, 0x0A, 0xB7, 0x71, 0x1B, 0x79, 0xEE, 0xC5, 0x83, 0x72, 0xDD, 0x84, 0xBC, 0x4B, + 0x13, 0xA4, 0x13, 0x15, 0x17, 0xAB, 0x18, 0x78, 0x4A, 0x43, 0x40, 0x81, 0x43, 0x76, 0x8E, 0xB6, + 0x04, 0x7C, 0x19, 0x02, 0x98, 0xC4, 0x86, 0x35, 0xBD, 0xE1, 0x1C, 0x90, 0x4D, 0x62, 0xBF, 0x14, + 0xFC, 0x7E, 0x02, 0x1A, 0x60, 0xD3, 0xD1, 0x9D, 0x3B, 0x3A, 0x09, 0xFC, 0x44, 0xB1, 0xD0, 0x96, + 0x8E, 0x43, 0x8D, 0x9E, 0xDA, 0xDB, 0x22, 0x11, 0x8A, 0x42, 0x10, 0x5C, 0x00, 0x1A, 0x2D, 0x38, + 0xF2, 0x0E, 0xA0, 0x4A, 0x03, 0x4E, 0xA2, 0xF0, 0xF4, 0x80, 0x17, 0x95, 0x8C, 0x23, 0xD1, 0xD8, + 0x12, 0x00, 0x92, 0x03, 0xAB, 0x99, 0x23, 0x71, 0x3E, 0x8D, 0xEB, 0x6E, 0x18, 0x07, 0x66, 0x23, + 0x05, 0x3E, 0x72, 0xE4, 0x06, 0x0D, 0x81, 0x02, 0xE4, 0x36, 0x5D, 0xD9, 0xA5, 0xC0, 0xAD, 0x59, + 0xA2, 0x86, 0x6E, 0xD6, 0xC4, 0x73, 0xEB, 0x15, 0xA5, 0xC5, 0xDE, 0x14, 0xF4, 0x62, 0x07, 0xC0, + 0xE9, 0x9D, 0xCC, 0x58, 0x02, 0x14, 0xA0, 0x84, 0x45, 0x83, 0x68, 0x7A, 0x8D, 0x81, 0x66, 0xC3, + 0xE9, 0xD0, 0xA6, 0x3E, 0x03, 0xF3, 0x80, 0x7D, 0x90, 0xD8, 0x58, 0xA2, 0x59, 0x2D, 0x90, 0x68, + 0xD6, 0x64, 0x17, 0x76, 0x5A, 0x23, 0xBF, 0x13, 0x58, 0x89, 0x7A, 0xC3, 0xF0, 0x8F, 0xB6, 0xB8, + 0x4B, 0xD3, 0x74, 0x52, 0x50, 0x92, 0xC2, 0x03, 0xD1, 0x78, 0xD7, 0x89, 0x4C, 0x23, 0x26, 0x6E, + 0x82, 0xCC, 0x15, 0x5E, 0x05, 0x08, 0x30, 0x06, 0x8A, 0x75, 0x64, 0xFA, 0x42, 0xF1, 0x12, 0xAD, + 0x60, 0xDE, 0x51, 0x0A, 0x94, 0xBA, 0x5E, 0x2C, 0x8C, 0x86, 0xDD, 0x3D, 0xDE, 0x8F, 0x0A, 0x70, + 0x51, 0x5F, 0xFE, 0xF2, 0x84, 0xD1, 0xD0, 0xA6, 0x68, 0xB7, 0x96, 0x62, 0x14, 0x0C, 0xC1, 0xB7, + 0x90, 0x68, 0x72, 0xD1, 0x7C, 0xD9, 0x3D, 0xA3, 0x94, 0x35, 0xDE, 0x54, 0x06, 0x92, 0x51, 0x50, + 0x68, 0xC2, 0xC5, 0x44, 0xB7, 0x30, 0x70, 0xDE, 0x6A, 0x49, 0x69, 0x86, 0xC5, 0x2D, 0xA3, 0x29, + 0x04, 0x05, 0x2F, 0xD9, 0xC4, 0x02, 0x0C, 0x6C, 0x0C, 0x48, 0xB0, 0x94, 0x01, 0xDA, 0x3C, 0xF2, + 0x56, 0x21, 0xFA, 0xE8, 0xA3, 0xA1, 0x55, 0x12, 0xE9, 0xF4, 0x56, 0x42, 0xF8, 0x50, 0x00, 0x40, + 0x08, 0x08, 0xD4, 0x88, 0x80, 0x05, 0x33, 0x66, 0x10, 0xA2, 0x0C, 0xDC, 0xA0, 0xB0, 0xA3, 0x0B, + 0x71, 0xCD, 0xB1, 0x80, 0x75, 0x69, 0x20, 0x41, 0xE4, 0x91, 0x6B, 0x64, 0x10, 0x01, 0x00, 0x21, + 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, 0x00, + 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, 0x8C, + 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0x31, 0x1C, 0x34, + 0x1B, 0x24, 0x4C, 0x82, 0xE7, 0x28, 0x5E, 0x02, 0x78, 0x7A, 0x30, 0x8E, 0x47, 0x62, 0x48, 0x20, + 0x60, 0xD8, 0x06, 0x4A, 0x8E, 0xC1, 0x70, 0x60, 0x0C, 0x04, 0xD1, 0xCD, 0x60, 0x60, 0x3B, 0x10, + 0xBA, 0x59, 0x8B, 0x21, 0x41, 0x2E, 0x0F, 0xC3, 0x16, 0xF2, 0xC0, 0xAC, 0x40, 0x4F, 0x1E, 0x80, + 0x04, 0x81, 0x70, 0xD8, 0x0A, 0x14, 0x0F, 0xF7, 0x42, 0x00, 0xA0, 0xD6, 0x09, 0x0A, 0x81, 0x81, + 0x68, 0x00, 0x85, 0x85, 0x7F, 0x07, 0x07, 0x0A, 0x06, 0x7A, 0x06, 0x00, 0x74, 0x74, 0x5E, 0x8A, + 0x36, 0x68, 0x05, 0x7C, 0x66, 0x3D, 0x64, 0x58, 0x61, 0x0F, 0x0F, 0x66, 0x64, 0x99, 0x64, 0x79, + 0x9C, 0x9E, 0xA0, 0x48, 0x4E, 0x9D, 0xA3, 0x4A, 0x02, 0x01, 0x37, 0x90, 0x55, 0x04, 0x5C, 0x02, + 0x05, 0x59, 0x06, 0x01, 0x02, 0x4F, 0x89, 0x55, 0x76, 0xAC, 0x50, 0xAB, 0x0A, 0xAD, 0x7F, 0xB1, + 0x75, 0x07, 0x02, 0x04, 0x05, 0x00, 0xBE, 0x39, 0x06, 0x77, 0x05, 0x36, 0x04, 0x9F, 0x5B, 0xCC, + 0x00, 0x4A, 0x82, 0x81, 0x07, 0x9F, 0x65, 0x86, 0x85, 0x40, 0x9D, 0x0A, 0x9F, 0x0C, 0xD0, 0x9F, + 0x00, 0x9D, 0x39, 0x02, 0xDE, 0x66, 0xB0, 0x73, 0xE2, 0x09, 0xE4, 0xAA, 0x2F, 0x05, 0x01, 0xCC, + 0x5E, 0xA7, 0x56, 0x75, 0xD2, 0xB8, 0x8C, 0x2F, 0x4C, 0x36, 0x06, 0xB1, 0x73, 0xE1, 0x88, 0xC9, + 0x92, 0x45, 0x43, 0x96, 0x00, 0x2E, 0x37, 0xE8, 0x79, 0x99, 0x53, 0xEC, 0x86, 0x0D, 0x06, 0x05, + 0xF3, 0x51, 0x61, 0x57, 0xE6, 0x9E, 0xBF, 0x35, 0x0C, 0xDE, 0x81, 0xE8, 0x64, 0x10, 0xFB, 0x5B, + 0xB6, 0x6C, 0xF5, 0x56, 0x94, 0x22, 0x13, 0xA7, 0xE2, 0x9C, 0x04, 0xDF, 0xCA, 0x74, 0xB2, 0xB2, + 0xC2, 0x40, 0x2A, 0x92, 0x72, 0x20, 0x45, 0x52, 0xE0, 0x45, 0x1A, 0x01, 0x2B, 0xCA, 0x3E, 0xC4, + 0x3A, 0x38, 0x60, 0xDB, 0x23, 0x86, 0xD6, 0x14, 0x0C, 0x20, 0xC0, 0x84, 0xCB, 0x8A, 0x7C, 0x3C, + 0x1F, 0x25, 0x94, 0xA9, 0x80, 0x21, 0x17, 0xA2, 0x5B, 0x4E, 0x34, 0x91, 0xE5, 0x2F, 0x99, 0x4C, + 0xA0, 0x89, 0xEC, 0x5C, 0xC1, 0xB5, 0x89, 0xC3, 0x15, 0x2B, 0xCC, 0x10, 0xD6, 0xF9, 0x38, 0x67, + 0xA8, 0xB1, 0x01, 0xE5, 0x40, 0xD4, 0x7B, 0x42, 0xF1, 0x63, 0x02, 0x66, 0x02, 0x12, 0xA4, 0xFA, + 0x90, 0x2A, 0x54, 0xB6, 0x44, 0xEB, 0xDA, 0x96, 0x19, 0x9A, 0x53, 0x43, 0x01, 0xBB, 0x64, 0x84, + 0xAE, 0xC3, 0xBB, 0xB0, 0xCC, 0x31, 0x40, 0x5D, 0x35, 0xD8, 0x62, 0x82, 0x6B, 0x68, 0xAC, 0xA6, + 0x43, 0xF1, 0xD2, 0x01, 0x20, 0xE9, 0x8A, 0x25, 0x05, 0x96, 0x36, 0xD8, 0x8A, 0xD7, 0xD8, 0x46, + 0xD0, 0x85, 0xEB, 0x7E, 0x72, 0x31, 0x10, 0x54, 0x9F, 0x85, 0x00, 0xF1, 0xA6, 0x1C, 0x94, 0x2C, + 0x34, 0xB2, 0x17, 0x9F, 0x5B, 0xB8, 0x14, 0x80, 0xFC, 0x41, 0x28, 0x63, 0xC7, 0x03, 0x9A, 0x4A, + 0x32, 0x9C, 0x2C, 0x2B, 0x2E, 0x40, 0x72, 0x63, 0x59, 0x48, 0x85, 0x47, 0x8D, 0xD8, 0xC8, 0xD9, + 0xE2, 0xF0, 0x55, 0x99, 0x4A, 0xE3, 0x82, 0x07, 0x41, 0x3F, 0x25, 0x92, 0xAE, 0xD7, 0x4C, 0x3C, + 0xE7, 0xAC, 0x02, 0x04, 0x58, 0xC4, 0xC4, 0xAD, 0x9C, 0x1B, 0xB1, 0x3F, 0xE1, 0x42, 0x3D, 0x4B, + 0x9E, 0x85, 0xCB, 0x57, 0xA6, 0x30, 0x4B, 0x97, 0x88, 0x26, 0xA2, 0x03, 0x7F, 0x8B, 0x72, 0x2D, + 0x90, 0x79, 0x02, 0xFD, 0x59, 0x36, 0xB2, 0xF3, 0x41, 0x2A, 0x65, 0xAD, 0x75, 0x64, 0x3A, 0x0C, + 0x48, 0xB4, 0x15, 0x35, 0x13, 0xE0, 0xC2, 0xC0, 0x2D, 0xB1, 0x15, 0x95, 0x0C, 0x13, 0x97, 0x59, + 0x73, 0x90, 0x01, 0xF5, 0xAC, 0x36, 0x8D, 0x79, 0x12, 0x40, 0xB8, 0x45, 0x22, 0xB8, 0xF8, 0xC4, + 0x0C, 0x7D, 0xB3, 0x31, 0x96, 0xCF, 0x00, 0x01, 0x76, 0x41, 0x80, 0x4F, 0x0B, 0x80, 0x78, 0x4A, + 0x24, 0x5B, 0x8C, 0x18, 0xDB, 0x8A, 0x0A, 0xF0, 0xC3, 0x15, 0x88, 0x08, 0xF1, 0xC5, 0xC7, 0x3C, + 0x13, 0x40, 0x94, 0x01, 0x81, 0x13, 0x20, 0xA0, 0x23, 0x02, 0x27, 0xE0, 0x58, 0x63, 0x08, 0xD9, + 0xE8, 0x41, 0x81, 0x5B, 0x42, 0x7A, 0x57, 0x86, 0x12, 0x46, 0x26, 0x79, 0x46, 0x06, 0x11, 0x00, + 0x00, 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x3C, 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, + 0xE4, 0x8C, 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0xA1, + 0x1C, 0x34, 0x1B, 0x24, 0x4A, 0x82, 0xE7, 0x27, 0x85, 0x50, 0x08, 0x0C, 0x3D, 0x12, 0xBC, 0xC4, + 0xA3, 0x18, 0x12, 0x08, 0x14, 0x86, 0xC1, 0x80, 0xC9, 0x31, 0x18, 0x0E, 0x84, 0x81, 0x80, 0xBA, + 0x91, 0x46, 0xB1, 0x51, 0x06, 0xD7, 0x62, 0x40, 0x9A, 0x91, 0xE2, 0x71, 0xA5, 0x37, 0x40, 0x33, + 0xDE, 0xE9, 0xF1, 0xE3, 0xA1, 0x20, 0x10, 0x0E, 0x52, 0x01, 0x63, 0xA9, 0x5E, 0x0C, 0x85, 0x03, + 0x76, 0x7A, 0x7D, 0x0B, 0x4E, 0x03, 0x75, 0x78, 0x77, 0x78, 0x06, 0x84, 0x51, 0x37, 0x8A, 0x77, + 0x02, 0x51, 0x6A, 0x05, 0x02, 0x04, 0x67, 0x67, 0x5B, 0x72, 0x47, 0x98, 0x98, 0x7C, 0x5C, 0x73, + 0x66, 0x58, 0x07, 0x8B, 0x0F, 0x00, 0x9F, 0x45, 0x02, 0x01, 0x03, 0xA3, 0x0C, 0x89, 0x03, 0x06, + 0x02, 0x00, 0x5C, 0x06, 0x01, 0x92, 0x52, 0x07, 0xAE, 0x59, 0x03, 0x00, 0xBD, 0xBD, 0x4C, 0x02, + 0x05, 0xAB, 0xB9, 0x70, 0x0C, 0xAC, 0x96, 0x00, 0x52, 0x45, 0x36, 0xC1, 0x51, 0x97, 0x0C, 0x66, + 0x5E, 0x00, 0x8C, 0x4C, 0x0A, 0x05, 0x5F, 0x68, 0x98, 0x4E, 0x56, 0x40, 0x7F, 0x3D, 0x67, 0x97, + 0x9D, 0x73, 0x39, 0x02, 0x74, 0x9C, 0x48, 0xA4, 0x76, 0x76, 0x66, 0x73, 0xA8, 0x2C, 0x05, 0x50, + 0x02, 0xA3, 0xA3, 0x59, 0x78, 0x5E, 0x92, 0xD4, 0x2C, 0xDB, 0xB0, 0x59, 0x76, 0x07, 0x00, 0x78, + 0x16, 0x09, 0x80, 0xF5, 0xE4, 0x05, 0xC1, 0x81, 0x5F, 0xB0, 0x10, 0x00, 0xA0, 0xEE, 0xC0, 0xC0, + 0x03, 0x8E, 0x68, 0x68, 0xB1, 0x72, 0xE5, 0x92, 0xAF, 0x5E, 0xF6, 0x0C, 0x10, 0xF0, 0x86, 0x62, + 0xFB, 0xCE, 0xC1, 0x03, 0x98, 0x00, 0x74, 0x42, 0x42, 0xE7, 0xC5, 0xB9, 0x4E, 0xFF, 0xC2, 0x9D, + 0x99, 0xA3, 0x60, 0x0A, 0x0A, 0x03, 0xED, 0xC0, 0xDD, 0xF1, 0x47, 0xCF, 0xCB, 0x2E, 0x97, 0x20, + 0x92, 0x0D, 0x64, 0x85, 0xA5, 0x65, 0x43, 0x2C, 0x3C, 0x19, 0x10, 0x30, 0x94, 0x2F, 0x27, 0xBE, + 0x9D, 0x87, 0x78, 0xCE, 0x54, 0x47, 0xC0, 0x98, 0xC6, 0x9D, 0x39, 0x2F, 0xC6, 0xB2, 0xF1, 0x87, + 0x29, 0x96, 0x62, 0xC6, 0xB6, 0x69, 0xE2, 0xC0, 0x2B, 0x4A, 0x2C, 0x58, 0x49, 0x47, 0xDA, 0x11, + 0x6A, 0x69, 0xC0, 0xB8, 0x10, 0x22, 0x1F, 0xDC, 0x0A, 0xF4, 0x6D, 0x64, 0x02, 0x06, 0xB1, 0x04, + 0x28, 0x39, 0xBB, 0xA1, 0xDD, 0x49, 0x4C, 0xE9, 0xD4, 0xB9, 0x4D, 0x0A, 0xA2, 0x40, 0x81, 0x91, + 0x81, 0x68, 0x2E, 0x35, 0x43, 0xA0, 0xC0, 0x50, 0x05, 0x1F, 0x6A, 0x39, 0xB9, 0x65, 0x47, 0x4A, + 0xBF, 0xBC, 0xF4, 0xF0, 0x04, 0xE3, 0x98, 0xA1, 0x96, 0x02, 0x5B, 0x5E, 0x71, 0xA5, 0x53, 0xA8, + 0x4E, 0x68, 0x16, 0x2B, 0x01, 0xFC, 0x42, 0x29, 0x4A, 0x21, 0xF4, 0xE8, 0x87, 0x0A, 0xA5, 0x04, + 0xDA, 0xDC, 0x94, 0x95, 0x6A, 0x03, 0xC2, 0x5A, 0x6E, 0xF0, 0x66, 0xC8, 0x0E, 0xC0, 0x99, 0x20, + 0xDF, 0x16, 0x93, 0xAC, 0x91, 0x64, 0x16, 0x0B, 0xED, 0x14, 0x70, 0xF2, 0xC2, 0x6B, 0x75, 0x27, + 0x68, 0x81, 0xDA, 0x90, 0xB4, 0x0B, 0xDC, 0x74, 0xC8, 0x8B, 0x67, 0xB0, 0xBC, 0x75, 0x2B, 0xCC, + 0x5D, 0xA1, 0x00, 0xD8, 0x09, 0xC4, 0x3D, 0x23, 0x92, 0xB0, 0x6B, 0x29, 0xDA, 0x6A, 0x55, 0x12, + 0x46, 0x7A, 0x41, 0xA5, 0x81, 0xF8, 0x62, 0x25, 0xE8, 0x4E, 0x2A, 0x60, 0x22, 0x52, 0x05, 0xB4, + 0xB4, 0x94, 0x14, 0xAC, 0x12, 0x05, 0xBF, 0xCD, 0xB4, 0x60, 0xA7, 0x6F, 0x66, 0x65, 0xE7, 0x57, + 0xC7, 0xA3, 0xC8, 0x67, 0x85, 0x16, 0x5B, 0x49, 0xF0, 0x84, 0x2A, 0x48, 0x19, 0x92, 0x47, 0x30, + 0x4E, 0x00, 0x60, 0xC9, 0x57, 0x71, 0x79, 0x03, 0x51, 0x30, 0xD3, 0x4C, 0xA0, 0xC0, 0x4E, 0xA4, + 0x1C, 0x45, 0x51, 0x2C, 0x7E, 0x39, 0xB8, 0x18, 0x3E, 0x04, 0xB5, 0x04, 0x14, 0x6C, 0xB3, 0xF8, + 0xF1, 0xC7, 0x3C, 0x00, 0xAA, 0xC6, 0xCB, 0x45, 0xFC, 0x21, 0xD5, 0x52, 0x72, 0x17, 0x95, 0x88, + 0xD8, 0x09, 0x08, 0x00, 0x80, 0x00, 0x02, 0x28, 0x94, 0x28, 0xC1, 0x8C, 0x20, 0x20, 0xD1, 0x5D, + 0x02, 0x27, 0xE8, 0x88, 0x82, 0x5B, 0x84, 0x2C, 0xE0, 0xD6, 0x19, 0x4C, 0x1C, 0xA9, 0xA4, 0x19, + 0x1B, 0x44, 0x00, 0x00, 0x21, 0xFE, 0x19, 0x42, 0x6F, 0x79, 0x27, 0x73, 0x20, 0x48, 0x61, 0x70, + 0x70, 0x79, 0x20, 0x48, 0x6F, 0x6C, 0x69, 0x64, 0x61, 0x79, 0x73, 0x20, 0x50, 0x61, 0x67, 0x65, + 0x00, 0x3B + +}; + +static const uint8_t bird_gif[] PROGMEM = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x74, 0x00, 0x4E, 0x00, 0xCC, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xBD, 0xE7, 0xE7, 0xD6, 0xEF, 0xEF, 0x63, 0x63, 0x63, 0xAD, 0xAD, 0xAD, 0xCE, + 0xCE, 0xCE, 0xD6, 0xDE, 0xDE, 0x4A, 0x4A, 0x4A, 0x7B, 0x84, 0x84, 0x94, 0x9C, 0x9C, 0xE7, 0xE7, + 0xE7, 0x84, 0x94, 0x94, 0xB5, 0xC6, 0xC6, 0x73, 0x7B, 0x7B, 0x31, 0x31, 0x31, 0xF7, 0xF7, 0xF7, + 0x18, 0x10, 0x10, 0xCE, 0x63, 0x39, 0x6B, 0x39, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xFF, 0x0B, + 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x4B, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0xAE, 0xEC, 0x68, 0x2C, + 0x6D, 0x2C, 0xCF, 0x74, 0x6D, 0x03, 0x46, 0x84, 0x40, 0x77, 0xEF, 0xFF, 0x40, 0x51, 0x42, 0x41, + 0x0C, 0x1A, 0x8F, 0x48, 0x13, 0xC1, 0xD0, 0x20, 0x00, 0x0E, 0x8E, 0x42, 0x72, 0x4A, 0xAD, 0x21, + 0x0E, 0xCD, 0x05, 0xE1, 0x80, 0xA8, 0x7A, 0xBF, 0x29, 0xC2, 0xA0, 0x91, 0x28, 0x34, 0x02, 0x5D, + 0xB0, 0x5A, 0xFD, 0x08, 0x34, 0x14, 0x84, 0x00, 0x7A, 0x4D, 0xF7, 0x22, 0xDC, 0x89, 0xB8, 0xC1, + 0xB9, 0x80, 0xD5, 0xFF, 0x40, 0x0B, 0x77, 0x0C, 0x0F, 0x05, 0x01, 0x0C, 0x0A, 0x0B, 0x0F, 0x08, + 0x06, 0x80, 0x8E, 0x37, 0x7B, 0x01, 0x09, 0x11, 0x03, 0x68, 0x5C, 0x4C, 0x0A, 0x8F, 0x9A, 0x33, + 0x06, 0x09, 0x01, 0x0E, 0x71, 0x68, 0x88, 0x92, 0x07, 0x9B, 0xA6, 0x2C, 0x0A, 0x86, 0x07, 0x95, + 0x05, 0x04, 0xA1, 0x5D, 0x8D, 0xA7, 0xB2, 0x26, 0x09, 0x06, 0x72, 0x72, 0x0E, 0x94, 0x01, 0xA9, + 0x04, 0x08, 0x52, 0xB3, 0xC0, 0x00, 0x09, 0x95, 0xB7, 0x0D, 0x86, 0x01, 0x04, 0x0A, 0xC3, 0x09, + 0x34, 0x43, 0xC1, 0x54, 0x0E, 0xB7, 0xD2, 0x87, 0xAE, 0xBB, 0x99, 0x32, 0x7B, 0x0E, 0xCF, 0x49, + 0x0B, 0xD1, 0xD3, 0x72, 0x0C, 0xDE, 0x57, 0x33, 0xBE, 0xCC, 0xDB, 0x47, 0x05, 0x0C, 0xDF, 0xD3, + 0x05, 0xD7, 0x31, 0x0C, 0xE1, 0xA5, 0xE7, 0x46, 0xBE, 0xEB, 0xD2, 0x04, 0x05, 0xBF, 0x2C, 0x5C, + 0x4D, 0xF3, 0x46, 0x0B, 0x11, 0x56, 0xD9, 0x3B, 0x14, 0x01, 0x14, 0x0F, 0x16, 0x04, 0x1A, 0x30, + 0x28, 0x00, 0x81, 0xA1, 0x3F, 0x1F, 0xAD, 0x02, 0x10, 0x5B, 0x87, 0x6F, 0x0E, 0x8B, 0x02, 0x0E, + 0x04, 0x5C, 0x41, 0x10, 0xF0, 0x61, 0x8F, 0x25, 0x03, 0x26, 0xDA, 0x43, 0xC4, 0x42, 0x8B, 0x00, + 0x05, 0x0E, 0x7C, 0xFF, 0xED, 0xF0, 0x68, 0x83, 0x8B, 0x44, 0x91, 0xDF, 0x06, 0xAC, 0x54, 0x01, + 0x21, 0xA1, 0x80, 0x07, 0x0F, 0x1A, 0x38, 0x70, 0xC7, 0x72, 0x46, 0xC2, 0x97, 0x03, 0x3F, 0xC5, + 0x52, 0xE1, 0x80, 0x81, 0x00, 0x01, 0x19, 0x1B, 0x3C, 0x38, 0xD8, 0x53, 0xC6, 0x81, 0x38, 0x21, + 0x61, 0x4A, 0xEB, 0xB7, 0x42, 0x01, 0x82, 0xA3, 0x58, 0x7D, 0xC9, 0x6B, 0x1A, 0x63, 0x0B, 0xD0, + 0x81, 0x11, 0x08, 0x74, 0x44, 0x61, 0xE0, 0x01, 0xD6, 0xA3, 0x05, 0x16, 0x2D, 0xE5, 0xDA, 0xC2, + 0x80, 0xB7, 0x90, 0x41, 0xCB, 0xA4, 0x39, 0xC1, 0xEF, 0x2C, 0x56, 0xAA, 0x6C, 0x57, 0x38, 0xA8, + 0x14, 0x35, 0xA8, 0x35, 0x14, 0x10, 0x10, 0x34, 0xB0, 0x9B, 0xD5, 0x4F, 0xDE, 0x14, 0x06, 0x8E, + 0xF5, 0x1D, 0xC8, 0x25, 0x85, 0x83, 0xC1, 0x84, 0x05, 0x20, 0x32, 0x30, 0xF4, 0x30, 0x2D, 0x39, + 0x8B, 0x07, 0x32, 0x42, 0xA1, 0x00, 0x72, 0xE4, 0x45, 0x08, 0x7C, 0x59, 0x36, 0x91, 0x18, 0x73, + 0xE6, 0x6F, 0x79, 0x38, 0x2B, 0x88, 0x7C, 0x54, 0xE7, 0xE0, 0x2D, 0xA3, 0x4B, 0x78, 0x7A, 0x79, + 0x7A, 0x1A, 0xCE, 0x04, 0x4C, 0x85, 0xAC, 0x66, 0x7D, 0x96, 0x80, 0xB9, 0xD8, 0x22, 0x4A, 0x4B, + 0x14, 0x00, 0x97, 0xF1, 0xD3, 0x12, 0x3B, 0x79, 0x9F, 0x45, 0xC0, 0x13, 0xB8, 0xB7, 0xE1, 0xC5, + 0x19, 0x33, 0xEB, 0x53, 0xD3, 0xA8, 0x72, 0x01, 0x0D, 0x44, 0x97, 0x5C, 0x90, 0x7B, 0x9B, 0x01, + 0x05, 0xA6, 0x89, 0xFB, 0x3D, 0x54, 0x20, 0x81, 0xDA, 0x8C, 0xD7, 0x91, 0x16, 0x22, 0x91, 0xF8, + 0x80, 0x9F, 0x86, 0x08, 0x1E, 0xF4, 0xF2, 0xD5, 0x7D, 0x96, 0x18, 0xCC, 0x47, 0xA3, 0xDB, 0x7B, + 0xCA, 0x60, 0x40, 0xCA, 0xF4, 0x58, 0x81, 0xE2, 0x42, 0x04, 0x11, 0x4C, 0x80, 0x53, 0x7C, 0x3F, + 0xC9, 0xB1, 0xC7, 0xFF, 0x7A, 0xC1, 0x18, 0xA0, 0x0E, 0x7E, 0xC4, 0x49, 0xA5, 0x99, 0x67, 0xD7, + 0xA5, 0xB4, 0x15, 0x04, 0x93, 0x48, 0xA0, 0xA1, 0x81, 0x7C, 0x49, 0xB3, 0xC7, 0x38, 0xF6, 0x11, + 0x13, 0x52, 0x7E, 0x12, 0xAE, 0x63, 0xC0, 0x55, 0x00, 0x82, 0x62, 0xD8, 0x08, 0x0B, 0x24, 0xA0, + 0xA1, 0x86, 0x57, 0x3C, 0xF0, 0xE0, 0x2D, 0x05, 0x68, 0x67, 0x4A, 0x27, 0xB7, 0x0C, 0x80, 0x95, + 0x7E, 0x41, 0x11, 0x80, 0x5E, 0x85, 0xBF, 0x9D, 0xD0, 0xC0, 0x04, 0x2F, 0x2E, 0x62, 0xCB, 0x34, + 0x03, 0xE4, 0xB1, 0xA2, 0x23, 0x5E, 0x41, 0xC8, 0x63, 0x50, 0x0E, 0x9A, 0x75, 0x5D, 0x68, 0x5B, + 0x9D, 0xA0, 0x45, 0x04, 0x38, 0x2D, 0x72, 0xCC, 0x34, 0x7B, 0x24, 0xF2, 0x88, 0x5B, 0x39, 0xEA, + 0x18, 0xE1, 0x78, 0xD2, 0x58, 0x05, 0x20, 0x6C, 0x2B, 0xD4, 0x78, 0x64, 0x89, 0x6E, 0xB8, 0xF2, + 0xC2, 0x1F, 0x4D, 0xBE, 0x44, 0x22, 0x99, 0x72, 0xE4, 0x01, 0x60, 0x19, 0x31, 0x68, 0xB1, 0xC4, + 0x78, 0x50, 0xF4, 0xE2, 0x9B, 0x43, 0x5E, 0x80, 0x99, 0xE3, 0x8E, 0x6C, 0x4E, 0xE3, 0x63, 0x7A, + 0xD9, 0x55, 0xD6, 0x82, 0x4E, 0x04, 0x30, 0xD0, 0xC0, 0x01, 0x7C, 0x2A, 0xE4, 0xA3, 0xA2, 0x47, + 0xDC, 0x37, 0xE8, 0x9C, 0xE3, 0xC9, 0x64, 0x9D, 0x72, 0x70, 0x54, 0x29, 0x03, 0x04, 0xDF, 0xE5, + 0xE1, 0x4A, 0x19, 0x06, 0xB0, 0xD9, 0xC4, 0x66, 0x48, 0x7C, 0x27, 0xCD, 0x88, 0x98, 0x92, 0x89, + 0xE2, 0x75, 0x69, 0x2D, 0x69, 0xC3, 0x02, 0x07, 0xA4, 0x03, 0x8A, 0x2B, 0x8E, 0xC2, 0x04, 0x05, + 0x03, 0x48, 0x3C, 0x17, 0x5E, 0xAB, 0x7E, 0x61, 0x04, 0x20, 0x03, 0x33, 0x21, 0x01, 0xC1, 0x02, + 0xDF, 0x81, 0x52, 0x8B, 0x34, 0xAD, 0xC8, 0x6A, 0x43, 0x3E, 0x48, 0x9E, 0xF5, 0x24, 0x45, 0xAF, + 0xF2, 0x96, 0x9D, 0xFF, 0x03, 0xF5, 0x51, 0xD1, 0xA2, 0x03, 0x90, 0xCA, 0xF1, 0x94, 0x3E, 0x37, + 0x68, 0x81, 0xA4, 0x98, 0xC0, 0x9A, 0xF8, 0xC0, 0x8F, 0xCA, 0x39, 0xA0, 0xCD, 0x1F, 0x0B, 0x10, + 0x21, 0x0D, 0x03, 0xB8, 0x7D, 0x74, 0x64, 0x98, 0xD2, 0x16, 0x8A, 0x0C, 0xBA, 0xCA, 0x29, 0xF5, + 0x48, 0x8B, 0x53, 0xA1, 0x19, 0x03, 0xA8, 0x18, 0x85, 0xB2, 0x2A, 0xB9, 0x63, 0x06, 0x45, 0x06, + 0x80, 0x48, 0x05, 0xC9, 0x6E, 0x9C, 0x4F, 0xE1, 0xA3, 0xC2, 0x77, 0xF0, 0x16, 0x95, 0x4B, 0xB7, + 0xF4, 0x12, 0xEA, 0x57, 0x59, 0x14, 0xB2, 0x46, 0x08, 0xB8, 0x8E, 0x08, 0x72, 0x06, 0xB3, 0x3E, + 0xA6, 0xF2, 0x28, 0x46, 0x8B, 0x2C, 0xEB, 0xED, 0x03, 0xF3, 0x56, 0x5C, 0xF0, 0x40, 0xE6, 0xED, + 0xA6, 0x9C, 0x7C, 0xD9, 0x02, 0xA2, 0x48, 0xCA, 0x98, 0x25, 0xD6, 0xCE, 0x4E, 0x14, 0x2B, 0x18, + 0xE7, 0xC0, 0xE5, 0xAE, 0x93, 0x00, 0xC2, 0x45, 0x01, 0xB3, 0xC0, 0x8C, 0x74, 0xFA, 0xF7, 0x80, + 0x84, 0xAC, 0xAE, 0x6C, 0xCF, 0x00, 0x04, 0xA4, 0x47, 0x2C, 0x02, 0xCE, 0x6A, 0xF2, 0x06, 0x9D, + 0x98, 0xE5, 0x3C, 0xB0, 0x98, 0xB5, 0x4D, 0xD3, 0x40, 0x04, 0x05, 0x28, 0x57, 0x40, 0x04, 0x0A, + 0x50, 0x2A, 0x4B, 0x02, 0x1F, 0x53, 0xBD, 0x74, 0x54, 0x59, 0xBF, 0x1B, 0x5A, 0x41, 0xAC, 0x65, + 0xA7, 0xF0, 0x33, 0x0C, 0x58, 0x6D, 0xB6, 0x69, 0x68, 0x4F, 0x2B, 0x87, 0x55, 0xB7, 0x38, 0x50, + 0xC8, 0x5D, 0x7E, 0x8A, 0x1D, 0x0C, 0x04, 0x8F, 0xCD, 0x1D, 0x94, 0xDD, 0x4F, 0xA1, 0xEC, 0xA1, + 0x7C, 0xAD, 0x8D, 0xC5, 0x52, 0x4D, 0xBE, 0x0A, 0x3E, 0x9E, 0x55, 0x32, 0xDA, 0xA3, 0x37, 0x03, + 0xBD, 0x44, 0x3D, 0x8F, 0x55, 0x34, 0x3B, 0x6E, 0x4F, 0x03, 0x99, 0x73, 0x89, 0x93, 0xE5, 0xFE, + 0x74, 0x13, 0xB7, 0xCB, 0xE6, 0xA4, 0x4B, 0x63, 0x90, 0x65, 0xED, 0x16, 0x25, 0x77, 0xE9, 0x54, + 0x67, 0x11, 0xDB, 0x89, 0x44, 0xB3, 0x6E, 0xF6, 0x89, 0x7E, 0x37, 0x05, 0x47, 0xE7, 0xB2, 0xFB, + 0xF5, 0x2D, 0x70, 0xDD, 0x80, 0x37, 0xDE, 0x0A, 0x8E, 0xFB, 0x06, 0x1C, 0x00, 0x4D, 0xE0, 0x1E, + 0x80, 0x0C, 0x73, 0xFB, 0x47, 0x00, 0xE8, 0x2C, 0x89, 0x6E, 0xCF, 0x0C, 0x66, 0x87, 0x64, 0x55, + 0x73, 0x87, 0xB5, 0xD2, 0x39, 0xF2, 0x54, 0xA3, 0xAD, 0x24, 0xEF, 0x09, 0xC4, 0x2E, 0x47, 0x0C, + 0x83, 0x4F, 0x44, 0x6E, 0x25, 0xA9, 0x0C, 0x8F, 0x43, 0xE0, 0xD3, 0xB4, 0x30, 0x50, 0x54, 0xF9, + 0x41, 0x57, 0xC9, 0x63, 0xE6, 0x03, 0x00, 0xC1, 0xED, 0xE9, 0xAB, 0x60, 0x4F, 0x62, 0x94, 0x41, + 0x2A, 0xFE, 0x01, 0x56, 0x11, 0x10, 0xB3, 0x65, 0x4F, 0x11, 0xCC, 0x37, 0x4A, 0x10, 0x2C, 0x8E, + 0x3C, 0x80, 0x40, 0x3A, 0x30, 0x20, 0x02, 0x96, 0x10, 0x3F, 0x13, 0x64, 0xA7, 0x1E, 0x17, 0x53, + 0x90, 0x02, 0x28, 0x87, 0x00, 0xB2, 0xC1, 0xE0, 0x29, 0x08, 0x24, 0x50, 0xC9, 0xFE, 0x37, 0x3C, + 0x28, 0x30, 0x67, 0x75, 0x01, 0x9C, 0x20, 0x4E, 0x0A, 0xE0, 0xAC, 0xAF, 0x21, 0x90, 0x3E, 0x0D, + 0x4C, 0x41, 0xBB, 0x42, 0x13, 0x05, 0x63, 0xA4, 0x64, 0x81, 0x43, 0x28, 0x40, 0xED, 0x44, 0x00, + 0x01, 0x2C, 0x78, 0x29, 0x85, 0x2C, 0x38, 0x56, 0x62, 0x1A, 0xC0, 0x1D, 0x35, 0x84, 0x00, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, + 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, + 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, + 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, + 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, + 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, + 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, + 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, + 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, + 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, + 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, + 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, + 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, + 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, + 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, + 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, + 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, + 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, + 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, + 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, + 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, + 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, + 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, + 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, + 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, + 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, + 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, + 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, + 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, + 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, + 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, + 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, + 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, + 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, + 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, + 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, + 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, + 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, + 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, + 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, + 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, + 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, + 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, + 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, + 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, + 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, + 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, + 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, + 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, + 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, + 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, + 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, + 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, + 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, + 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, + 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, + 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, + 0x0D, 0x21, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, + 0x00, 0x6A, 0x00, 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, + 0xAE, 0x6C, 0xEB, 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, + 0x0A, 0xC7, 0x03, 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, + 0xA4, 0x10, 0xA8, 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, + 0x9E, 0x18, 0x02, 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, + 0xD4, 0x81, 0x01, 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, + 0x0D, 0x08, 0x05, 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, + 0x04, 0x06, 0x10, 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, + 0x93, 0x09, 0x0C, 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, + 0x0C, 0x39, 0x9A, 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, + 0xAB, 0x5B, 0x4A, 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, + 0x0D, 0x0D, 0x07, 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, + 0x0E, 0x7B, 0x04, 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, + 0x92, 0x0C, 0x0E, 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, + 0x5B, 0x03, 0x0E, 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, + 0x98, 0xBE, 0x73, 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, + 0xB0, 0x6A, 0xA3, 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, + 0xBC, 0xA5, 0x0C, 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, + 0x04, 0x32, 0x53, 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, + 0x67, 0x8E, 0xA2, 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, + 0x33, 0xA0, 0x2B, 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, + 0x0E, 0x10, 0xC0, 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, + 0x0A, 0x08, 0xF8, 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, + 0x1D, 0x8A, 0x47, 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, + 0x22, 0x22, 0x82, 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, + 0x6B, 0x50, 0xE0, 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, + 0xA3, 0x2D, 0x60, 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, + 0x94, 0x14, 0x7F, 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, + 0x05, 0x30, 0x4A, 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, + 0x53, 0x20, 0xC1, 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, + 0x10, 0x03, 0x80, 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, + 0x26, 0x0F, 0x28, 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, + 0xC6, 0x0A, 0x02, 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, + 0xA0, 0xC0, 0x28, 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, + 0x42, 0x01, 0x5A, 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, + 0x88, 0x33, 0x14, 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, + 0x6A, 0xC4, 0x80, 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, + 0x28, 0xC2, 0xE2, 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, + 0x21, 0xA4, 0x0C, 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, + 0x4A, 0x93, 0x29, 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, + 0x94, 0x0C, 0x38, 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, + 0x56, 0x74, 0x54, 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, + 0x99, 0x83, 0x8A, 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, + 0x10, 0x02, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, + 0x6A, 0x00, 0x2E, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x00, 0x40, 0x62, 0xB9, 0x28, 0x08, 0x41, 0x28, + 0x4B, 0xE9, 0xBE, 0x70, 0x2C, 0xCF, 0x74, 0x6D, 0xDF, 0xE5, 0xF1, 0x3C, 0x69, 0x52, 0x24, 0x0F, + 0x82, 0xE1, 0x60, 0x00, 0x26, 0x5A, 0xB8, 0xA4, 0x72, 0xC9, 0x94, 0xE9, 0x22, 0x12, 0xC9, 0x64, + 0xF2, 0x18, 0x04, 0xAE, 0xD7, 0x01, 0xB0, 0xD0, 0xEC, 0x7A, 0xBF, 0x31, 0x48, 0x61, 0x12, 0x95, + 0x32, 0x02, 0x8E, 0x06, 0x36, 0x70, 0x50, 0x21, 0xC1, 0xF0, 0x78, 0x72, 0x91, 0x20, 0x4B, 0x55, + 0x85, 0xF5, 0xB5, 0x81, 0xE0, 0xCA, 0xFF, 0x80, 0x32, 0x0D, 0x0F, 0x53, 0x11, 0x11, 0x04, 0x07, + 0x7A, 0x57, 0x0E, 0x04, 0x6F, 0x81, 0x8F, 0x7F, 0x0B, 0x2A, 0x06, 0x94, 0x6A, 0x8A, 0x01, 0x06, + 0x04, 0x05, 0x07, 0x0B, 0x0B, 0x44, 0x94, 0xA0, 0x43, 0x24, 0x90, 0xA4, 0x37, 0x05, 0x04, 0x96, + 0x97, 0x58, 0x45, 0x09, 0x0A, 0x0C, 0x0C, 0x0A, 0x05, 0x0D, 0xB3, 0x05, 0x0A, 0x09, 0x0C, 0x05, + 0x06, 0xA3, 0x4D, 0x9F, 0xA0, 0x0B, 0xBB, 0xA5, 0x33, 0x6D, 0x08, 0x0C, 0x89, 0xAA, 0xC7, 0x8A, + 0x07, 0xAE, 0x3E, 0xC0, 0x32, 0x10, 0x07, 0x3F, 0x2A, 0x0E, 0x8C, 0x2A, 0x08, 0xD6, 0x29, 0x0E, + 0xB9, 0xCD, 0xC1, 0x22, 0x10, 0x45, 0xD2, 0xB8, 0x07, 0x56, 0x97, 0x22, 0xC8, 0x01, 0x0D, 0x0C, + 0x3E, 0x9C, 0x25, 0xCF, 0xA7, 0x04, 0xD9, 0xE2, 0xC8, 0x03, 0xD0, 0x09, 0x04, 0xD6, 0x09, 0xBA, + 0xDC, 0x30, 0x10, 0x0B, 0x06, 0xB6, 0x2A, 0x04, 0x12, 0xDC, 0x52, 0x40, 0x90, 0x60, 0x81, 0x5C, + 0x07, 0x20, 0xA8, 0x1A, 0xD0, 0x20, 0x01, 0x35, 0x07, 0x3C, 0x1C, 0x18, 0x30, 0x87, 0xCC, 0x00, + 0x03, 0x02, 0x0F, 0x12, 0x34, 0xD8, 0xA6, 0xCF, 0x44, 0xA7, 0x8F, 0x9D, 0x0E, 0x88, 0xAC, 0x75, + 0x6B, 0x22, 0xC5, 0x4B, 0x44, 0x4E, 0xFF, 0x26, 0x63, 0x80, 0x20, 0xE3, 0x81, 0x8E, 0x5D, 0x0E, + 0xA4, 0x33, 0xA6, 0x92, 0x8D, 0x03, 0x04, 0x35, 0x2F, 0x35, 0xC0, 0x98, 0xE0, 0x25, 0x4C, 0x26, + 0x07, 0x7C, 0xE4, 0xBC, 0x82, 0x40, 0xE1, 0x50, 0x3D, 0xCA, 0x52, 0x38, 0xFA, 0x89, 0xE3, 0x47, + 0xCE, 0x1F, 0x00, 0x8E, 0xAA, 0x02, 0xC2, 0x80, 0xE9, 0x92, 0x86, 0x2A, 0x0D, 0x3C, 0xA0, 0x29, + 0x55, 0xD1, 0x00, 0x8C, 0x06, 0xAC, 0x26, 0x31, 0x80, 0xC0, 0x24, 0x32, 0x04, 0x09, 0xBA, 0x9A, + 0x1B, 0x94, 0x80, 0xA3, 0x58, 0x18, 0x5A, 0xCF, 0x1C, 0x13, 0x78, 0x69, 0x9C, 0xDA, 0x2C, 0x04, + 0x22, 0x34, 0x78, 0x6B, 0x03, 0x82, 0x02, 0x44, 0x97, 0x5E, 0xD5, 0x1D, 0x60, 0xF7, 0xEE, 0xB9, + 0x07, 0x61, 0xF9, 0xD6, 0x58, 0xC0, 0x20, 0xDB, 0x1A, 0x82, 0x0B, 0x09, 0x17, 0xBE, 0xFB, 0x55, + 0x81, 0xE2, 0x1B, 0x99, 0x10, 0x39, 0xF6, 0x2A, 0x99, 0xB0, 0x61, 0x3D, 0x0E, 0x2F, 0xDF, 0x18, + 0x94, 0x87, 0xF3, 0x00, 0x01, 0x02, 0x24, 0x7F, 0x7E, 0x8C, 0x60, 0xA9, 0x68, 0x17, 0x6D, 0xCC, + 0x62, 0x91, 0x8C, 0x1A, 0x75, 0xE7, 0x2B, 0x30, 0xBA, 0xF2, 0xF1, 0xF9, 0xDA, 0x45, 0x26, 0xAE, + 0x59, 0x4E, 0xD7, 0x4E, 0xED, 0x19, 0xF7, 0x8C, 0xA3, 0x5A, 0xF7, 0xF6, 0x16, 0xF1, 0xC3, 0x68, + 0x70, 0xE1, 0xC3, 0x8B, 0xAF, 0xA1, 0x31, 0xF4, 0xB4, 0xD2, 0xD7, 0x10, 0x60, 0xE9, 0x21, 0x3C, + 0x3C, 0xFA, 0x64, 0xE3, 0x32, 0x6A, 0xD2, 0x56, 0xF0, 0xC0, 0x0F, 0x5F, 0x3A, 0xA9, 0x02, 0x70, + 0xEF, 0x4E, 0x5C, 0x3A, 0x68, 0xD7, 0xE5, 0x54, 0x72, 0xB7, 0xF2, 0xD5, 0xBC, 0xD5, 0xA0, 0x34, + 0xD7, 0xDB, 0x6E, 0xAF, 0x5A, 0x95, 0x03, 0xDE, 0x2F, 0x98, 0x73, 0xDB, 0x70, 0x01, 0xC4, 0xF2, + 0x96, 0x45, 0xB3, 0x41, 0x67, 0x5B, 0xFF, 0x7F, 0xE6, 0x00, 0x20, 0x51, 0x0C, 0xF2, 0xD0, 0x47, + 0x9C, 0x7A, 0xEA, 0x91, 0x07, 0x20, 0x4C, 0x05, 0xC8, 0xA5, 0x9E, 0x70, 0xA7, 0x31, 0x78, 0x92, + 0x24, 0x89, 0xB9, 0x70, 0x4C, 0x26, 0x41, 0x04, 0xA4, 0x40, 0x03, 0x19, 0x32, 0x72, 0xC8, 0x85, + 0x1D, 0xB9, 0x92, 0x20, 0x7F, 0xEE, 0x9D, 0x94, 0xE1, 0x49, 0x0C, 0xDD, 0x84, 0x16, 0x79, 0x11, + 0xF4, 0x11, 0x0B, 0x42, 0x62, 0x41, 0x70, 0xCB, 0x73, 0x1E, 0x0E, 0xE5, 0x20, 0x70, 0x7B, 0x10, + 0x40, 0x58, 0x5E, 0xC4, 0xE4, 0x23, 0x42, 0x02, 0x86, 0x34, 0xA2, 0x98, 0x24, 0x0A, 0x00, 0x19, + 0x63, 0x4E, 0xFD, 0x10, 0x43, 0x93, 0x32, 0x79, 0xF1, 0x90, 0x42, 0x88, 0x2E, 0x2C, 0xF0, 0x80, + 0x21, 0x0F, 0x30, 0x00, 0x5F, 0x30, 0x62, 0xF4, 0xB1, 0x61, 0x90, 0x47, 0x39, 0x18, 0xCB, 0x3F, + 0xEF, 0xF8, 0x60, 0xC0, 0x2F, 0x36, 0x3C, 0xB1, 0x22, 0x53, 0x99, 0x20, 0x90, 0x08, 0x9A, 0x5D, + 0x79, 0xD9, 0x80, 0x00, 0x29, 0xB4, 0xD6, 0x04, 0x59, 0x2C, 0xFC, 0x24, 0x13, 0x2A, 0x78, 0x76, + 0xF5, 0x95, 0x03, 0x0C, 0xA0, 0xA6, 0xA3, 0x17, 0x6E, 0x3D, 0xB2, 0xC0, 0x0F, 0x0A, 0x10, 0xB9, + 0xDA, 0x39, 0x08, 0x08, 0x90, 0x86, 0x00, 0x4E, 0x2E, 0x17, 0x86, 0x3B, 0xA5, 0x4D, 0xAA, 0x87, + 0x42, 0xAF, 0x0C, 0xE2, 0x00, 0xA6, 0x63, 0x8A, 0xF6, 0xA8, 0x0A, 0x91, 0x06, 0xF0, 0x4B, 0x9E, + 0xCE, 0x29, 0xF2, 0x17, 0x4B, 0x96, 0x16, 0x50, 0xA9, 0x9F, 0x9A, 0x02, 0x50, 0x00, 0x23, 0x81, + 0x9E, 0x1A, 0x41, 0x94, 0x47, 0x31, 0x10, 0xE6, 0x01, 0xD3, 0x98, 0xA5, 0x10, 0x2A, 0x2D, 0xF1, + 0x21, 0x46, 0x40, 0xB5, 0xDA, 0x8A, 0xD8, 0x02, 0x0D, 0x21, 0x9A, 0x80, 0x99, 0x98, 0xE0, 0x63, + 0xCE, 0x10, 0x01, 0xC8, 0x7A, 0xC2, 0x79, 0x11, 0x50, 0x65, 0x26, 0x2B, 0x9F, 0x64, 0xF9, 0xD1, + 0xE8, 0x93, 0x29, 0x32, 0x30, 0x84, 0x03, 0x0A, 0x28, 0x04, 0x2C, 0x46, 0x0B, 0x5C, 0x91, 0xAE, + 0x22, 0x64, 0x21, 0xB0, 0x13, 0x30, 0x99, 0xDC, 0xB4, 0xD3, 0xB3, 0xE9, 0x00, 0x50, 0x6A, 0xB2, + 0x00, 0xB4, 0xD1, 0x42, 0x50, 0x42, 0x00, 0xF6, 0x55, 0x51, 0xAA, 0x6A, 0x82, 0x09, 0x22, 0x08, + 0x38, 0xB0, 0x4D, 0xB7, 0x88, 0x2C, 0x60, 0x0D, 0x8B, 0xF8, 0xBE, 0x90, 0x89, 0x02, 0x0E, 0xE0, + 0x03, 0x81, 0xC0, 0x99, 0xDC, 0xAA, 0x2A, 0x44, 0xB3, 0xF8, 0xE9, 0x16, 0x04, 0x08, 0xD8, 0xC2, + 0x1C, 0x97, 0x0D, 0xCB, 0xE0, 0xC9, 0x28, 0x3A, 0x28, 0xF3, 0x5F, 0x6B, 0xBF, 0xD9, 0x72, 0xC4, + 0x62, 0x1D, 0x7F, 0x1B, 0x72, 0x0D, 0xCA, 0xBC, 0xD4, 0x86, 0x2D, 0x3E, 0xB9, 0xBC, 0x5C, 0x08, + 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, 0x6A, 0x00, + 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, 0xAE, 0x6C, 0xEB, + 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, 0x0A, 0xC7, 0x03, + 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, 0xA4, 0x10, 0xA8, + 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, 0x9E, 0x18, 0x02, + 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, 0xD4, 0x81, 0x01, + 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, 0x0D, 0x08, 0x05, + 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, 0x04, 0x06, 0x10, + 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, 0x93, 0x09, 0x0C, + 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, 0x0C, 0x39, 0x9A, + 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, 0xAB, 0x5B, 0x4A, + 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, 0x0D, 0x0D, 0x07, + 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, 0x0E, 0x7B, 0x04, + 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, 0x92, 0x0C, 0x0E, + 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, 0x5B, 0x03, 0x0E, + 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, 0x98, 0xBE, 0x73, + 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, 0xB0, 0x6A, 0xA3, + 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, 0xBC, 0xA5, 0x0C, + 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, 0x04, 0x32, 0x53, + 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, 0x67, 0x8E, 0xA2, + 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, 0x33, 0xA0, 0x2B, + 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, 0x0E, 0x10, 0xC0, + 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, 0x0A, 0x08, 0xF8, + 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, 0x1D, 0x8A, 0x47, + 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, 0x22, 0x22, 0x82, + 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, 0x6B, 0x50, 0xE0, + 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, 0xA3, 0x2D, 0x60, + 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, 0x94, 0x14, 0x7F, + 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, 0x05, 0x30, 0x4A, + 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, 0x53, 0x20, 0xC1, + 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, 0x10, 0x03, 0x80, + 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, 0x26, 0x0F, 0x28, + 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, 0xC6, 0x0A, 0x02, + 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, 0xA0, 0xC0, 0x28, + 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, 0x42, 0x01, 0x5A, + 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, 0x88, 0x33, 0x14, + 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, 0x6A, 0xC4, 0x80, + 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, 0x28, 0xC2, 0xE2, + 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, 0x21, 0xA4, 0x0C, + 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, 0x4A, 0x93, 0x29, + 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, 0x94, 0x0C, 0x38, + 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, 0x56, 0x74, 0x54, + 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, 0x99, 0x83, 0x8A, + 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, 0x10, 0x02, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, + 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, + 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, + 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, + 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, + 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, + 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, + 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, + 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, + 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, + 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, + 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, + 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, + 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, + 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, + 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, + 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, + 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, + 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, + 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, + 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, + 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, + 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, + 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, + 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, + 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, + 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, + 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, + 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, + 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, + 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, + 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, + 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, + 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, + 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, + 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, + 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, + 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, + 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, + 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, + 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, + 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, + 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, + 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, + 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, + 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, + 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, + 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, + 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, + 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, + 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, + 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, + 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, + 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, + 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, + 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, + 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, + 0x0D, 0x21, 0x00, 0x00, 0x3B}; diff --git a/lib/lib_div/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino b/lib/libesp32/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino rename to lib/libesp32/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino index 8130262fa..3dcd0fc9e --- a/lib/lib_div/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino +++ b/lib/libesp32/lib_mail/examples/Send_Attachment_File/Send_Attachment_File.ino @@ -1,427 +1,427 @@ - - -/** - * This example will send the Email with attachments and - * inline images stored in flash and SD card. - * - * The html and text version messages will be sent. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -//The file systems for flash and sd memory can be changed in ESP_Mail_FS.h. - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT esp_mail_smtp_port_587 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - static uint8_t buf[512]; - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - Serial.println("Mounting SD Card..."); - -#if defined(ESP32) - if (SD.begin()) // MailClient.sdBegin(14,2,15,13) for TTGO T8 v1.7 or 1.8 -#elif defined(ESP8266) - if (SD.begin(15)) -#endif - { - - if (SD.exists("/orange.png")) - SD.remove("/orange.png"); - if (SD.exists("/bin1.dat")) - SD.remove("/bin1.dat"); - - Serial.println("Preparing SD file attachments..."); - - const char *orangeImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAoUlEQVR42u3RMQ0AMAgAsCFgftHLiQpsENJaaFT+fqwRQoQgRAhChCBECEKECBGCECEIEYIQIQgRghCECEGIEIQIQYgQhCBECEKEIEQIQoQgBCFCECIEIUIQIgQhCBGCECEIEYIQIQhBiBCECEGIEIQIQQhChCBECEKEIEQIQhAiBCFCECIEIUIQghAhCBGCECEIEYIQIUKEIEQIQoQg5LoBGi/oCaOpTXoAAAAASUVORK5CYII="; - - File file = SD.open("/orange.png", FILE_WRITE); - file.print(orangeImg); - file.close(); - - file = SD.open("/bin1.dat", FILE_WRITE); - - buf[0] = 'H'; - buf[1] = 'E'; - buf[2] = 'A'; - buf[3] = 'D'; - file.write(buf, 4); - - size_t i; - - for (i = 0; i < 4; i++) - { - memset(buf, i + 1, 512); - file.write(buf, 512); - } - - buf[0] = 'T'; - buf[1] = 'A'; - buf[2] = 'I'; - buf[3] = 'L'; - file.write(buf, 4); - file.close(); - } - else - { - Serial.println("SD Card Monting Failed"); - } - - Serial.println("Mounting SPIFFS..."); - -#if defined(ESP32) - if (SPIFFS.begin(true)) -#elif defined(ESP8266) - if (SPIFFS.begin()) -#endif - { - //SPIFFS.format(); - - if (SPIFFS.exists("/green.png")) - SPIFFS.remove("/green.png"); - if (SPIFFS.exists("/bin2.dat")) - SPIFFS.remove("/bin2.dat"); - - Serial.println("Preparing SPIFFS attachments..."); - - const char *greenImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAoUlEQVR42u3RAQ0AMAgAoJviyWxtAtNYwzmoQGT/eqwRQoQgRAhChCBECEKECBGCECEIEYIQIQgRghCECEGIEIQIQYgQhCBECEKEIEQIQoQgBCFCECIEIUIQIgQhCBGCECEIEYIQIQhBiBCECEGIEIQIQQhChCBECEKEIEQIQhAiBCFCECIEIUIQghAhCBGCECEIEYIQIUKEIEQIQoQg5LoBBaDPbQYiMoMAAAAASUVORK5CYII="; - -#if defined(ESP32) - File file = SPIFFS.open("/green.png", FILE_WRITE); -#elif defined(ESP8266) - File file = SPIFFS.open("/green.png", "w"); -#endif - - file.print(greenImg); - file.close(); - -#if defined(ESP32) - file = SPIFFS.open("/bin2.dat", FILE_WRITE); -#elif defined(ESP8266) - file = SPIFFS.open("/bin2.dat", "w"); -#endif - - buf[0] = 'H'; - buf[1] = 'E'; - buf[2] = 'L'; - buf[3] = 'L'; - buf[4] = 'O'; - file.write(buf, 5); - - size_t i; - for (i = 0; i < 4; i++) - { - memset(buf, i + 1, 512); - file.write(buf, 512); - } - - buf[0] = 'G'; - buf[1] = 'O'; - buf[2] = 'O'; - buf[3] = 'D'; - buf[4] = 'B'; - buf[5] = 'Y'; - buf[6] = 'E'; - file.write(buf, 7); - file.close(); - } - else - { - Serial.println("SPIFFS Monting Failed"); - } - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** ######################################################## - * Some properties of SMTPSession data and parameters pass to - * SMTP_Message class accept the pointer to constant char - * i.e. const char*. - * - * You may assign a string literal to that properties or function - * like below example. - * - * session.login.user_domain = "mydomain.net"; - * session.login.user_domain = String("mydomain.net").c_str(); - * - * or - * - * String doman = "mydomain.net"; - * session.login.user_domain = domain.c_str(); - * - * And - * - * String name = "Jack " + String("dawson"); - * String email = "jack_dawson" + String(123) + "@mail.com"; - * - * message.addRecipient(name.c_str(), email.c_str()); - * - * message.addHeader(String("Message-ID: ").c_str()); - * - * or - * - * String header = "Message-ID: "; - * message.addHeader(header.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Enable the chunked data transfer with pipelining for large message if server supported */ - message.enable.chunking = true; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - - message.subject = "Test sending Email with attachments and inline images from SD card and Flash"; - message.addRecipient("user1", "####@#####_dot_com"); - - /** Two alternative content versions are sending in this example e.g. plain text and html */ - String htmlMsg = "This message contains 2 inline images and 2 attachment files.

"; - message.html.content = htmlMsg.c_str(); - - /** The HTML text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "utf-8"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - message.text.content = "This message contains 2 inline images and 2 attachment files.\r\nThe inline images were not shown in the plain text message."; - message.text.charSet = "utf-8"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* The attachment data item */ - SMTP_Attachment att; - - /** Set the inline image info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - att.descr.filename = "orange.png"; - att.descr.mime = "image/png"; - att.file.path = "/orange.png"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - att.file.storage_type = esp_mail_file_storage_type_sd; - - /* Need to be base64 transfer encoding for inline image */ - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /** The orange.png file is already base64 encoded file. - * Then set the content encoding to match the transfer encoding - * which no encoding was taken place prior to sending. - */ - att.descr.content_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add inline image to the message */ - message.addInlineImage(att); - - /** Set the attachment info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "bin1.dat"; - att.descr.mime = "application/octet-stream"; //binary data - att.file.path = "/bin1.dat"; - att.file.storage_type = esp_mail_file_storage_type_sd; - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add attachment to the message */ - message.addAttachment(att); - - /** Set the inline image info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "green.png"; - att.descr.mime = "image/png"; - att.file.path = "/green.png"; - att.file.storage_type = esp_mail_file_storage_type_flash; - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - att.descr.content_encoding = Content_Transfer_Encoding::enc_base64; - message.addInlineImage(att); - - /** Set the attachment info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "bin2.dat"; - att.descr.mime = "application/octet-stream"; - att.file.path = "/bin2.dat"; - att.file.storage_type = esp_mail_file_storage_type_flash; - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - message.addAttachment(att); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending the Email and close the session */ - if (!MailClient.sendMail(&smtp, &message, true)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email with attachments and + * inline images stored in flash and SD card. + * + * The html and text version messages will be sent. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//The file systems for flash and sd memory can be changed in ESP_Mail_FS.h. + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT esp_mail_smtp_port_587 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + static uint8_t buf[512]; + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + Serial.println("Mounting SD Card..."); + +#if defined(ESP32) + if (SD.begin()) // MailClient.sdBegin(14,2,15,13) for TTGO T8 v1.7 or 1.8 +#elif defined(ESP8266) + if (SD.begin(15)) +#endif + { + + if (SD.exists("/orange.png")) + SD.remove("/orange.png"); + if (SD.exists("/bin1.dat")) + SD.remove("/bin1.dat"); + + Serial.println("Preparing SD file attachments..."); + + const char *orangeImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAoUlEQVR42u3RMQ0AMAgAsCFgftHLiQpsENJaaFT+fqwRQoQgRAhChCBECEKECBGCECEIEYIQIQgRghCECEGIEIQIQYgQhCBECEKEIEQIQoQgBCFCECIEIUIQIgQhCBGCECEIEYIQIQhBiBCECEGIEIQIQQhChCBECEKEIEQIQhAiBCFCECIEIUIQghAhCBGCECEIEYIQIUKEIEQIQoQg5LoBGi/oCaOpTXoAAAAASUVORK5CYII="; + + File file = SD.open("/orange.png", FILE_WRITE); + file.print(orangeImg); + file.close(); + + file = SD.open("/bin1.dat", FILE_WRITE); + + buf[0] = 'H'; + buf[1] = 'E'; + buf[2] = 'A'; + buf[3] = 'D'; + file.write(buf, 4); + + size_t i; + + for (i = 0; i < 4; i++) + { + memset(buf, i + 1, 512); + file.write(buf, 512); + } + + buf[0] = 'T'; + buf[1] = 'A'; + buf[2] = 'I'; + buf[3] = 'L'; + file.write(buf, 4); + file.close(); + } + else + { + Serial.println("SD Card Monting Failed"); + } + + Serial.println("Mounting SPIFFS..."); + +#if defined(ESP32) + if (SPIFFS.begin(true)) +#elif defined(ESP8266) + if (SPIFFS.begin()) +#endif + { + //SPIFFS.format(); + + if (SPIFFS.exists("/green.png")) + SPIFFS.remove("/green.png"); + if (SPIFFS.exists("/bin2.dat")) + SPIFFS.remove("/bin2.dat"); + + Serial.println("Preparing SPIFFS attachments..."); + + const char *greenImg = "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAoUlEQVR42u3RAQ0AMAgAoJviyWxtAtNYwzmoQGT/eqwRQoQgRAhChCBECEKECBGCECEIEYIQIQgRghCECEGIEIQIQYgQhCBECEKEIEQIQoQgBCFCECIEIUIQIgQhCBGCECEIEYIQIQhBiBCECEGIEIQIQQhChCBECEKEIEQIQhAiBCFCECIEIUIQghAhCBGCECEIEYIQIUKEIEQIQoQg5LoBBaDPbQYiMoMAAAAASUVORK5CYII="; + +#if defined(ESP32) + File file = SPIFFS.open("/green.png", FILE_WRITE); +#elif defined(ESP8266) + File file = SPIFFS.open("/green.png", "w"); +#endif + + file.print(greenImg); + file.close(); + +#if defined(ESP32) + file = SPIFFS.open("/bin2.dat", FILE_WRITE); +#elif defined(ESP8266) + file = SPIFFS.open("/bin2.dat", "w"); +#endif + + buf[0] = 'H'; + buf[1] = 'E'; + buf[2] = 'L'; + buf[3] = 'L'; + buf[4] = 'O'; + file.write(buf, 5); + + size_t i; + for (i = 0; i < 4; i++) + { + memset(buf, i + 1, 512); + file.write(buf, 512); + } + + buf[0] = 'G'; + buf[1] = 'O'; + buf[2] = 'O'; + buf[3] = 'D'; + buf[4] = 'B'; + buf[5] = 'Y'; + buf[6] = 'E'; + file.write(buf, 7); + file.close(); + } + else + { + Serial.println("SPIFFS Monting Failed"); + } + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** ######################################################## + * Some properties of SMTPSession data and parameters pass to + * SMTP_Message class accept the pointer to constant char + * i.e. const char*. + * + * You may assign a string literal to that properties or function + * like below example. + * + * session.login.user_domain = "mydomain.net"; + * session.login.user_domain = String("mydomain.net").c_str(); + * + * or + * + * String doman = "mydomain.net"; + * session.login.user_domain = domain.c_str(); + * + * And + * + * String name = "Jack " + String("dawson"); + * String email = "jack_dawson" + String(123) + "@mail.com"; + * + * message.addRecipient(name.c_str(), email.c_str()); + * + * message.addHeader(String("Message-ID: ").c_str()); + * + * or + * + * String header = "Message-ID: "; + * message.addHeader(header.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Enable the chunked data transfer with pipelining for large message if server supported */ + message.enable.chunking = true; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + + message.subject = "Test sending Email with attachments and inline images from SD card and Flash"; + message.addRecipient("user1", "####@#####_dot_com"); + + /** Two alternative content versions are sending in this example e.g. plain text and html */ + String htmlMsg = "This message contains 2 inline images and 2 attachment files.

"; + message.html.content = htmlMsg.c_str(); + + /** The HTML text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "utf-8"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + message.text.content = "This message contains 2 inline images and 2 attachment files.\r\nThe inline images were not shown in the plain text message."; + message.text.charSet = "utf-8"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* The attachment data item */ + SMTP_Attachment att; + + /** Set the inline image info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + att.descr.filename = "orange.png"; + att.descr.mime = "image/png"; + att.file.path = "/orange.png"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + att.file.storage_type = esp_mail_file_storage_type_sd; + + /* Need to be base64 transfer encoding for inline image */ + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /** The orange.png file is already base64 encoded file. + * Then set the content encoding to match the transfer encoding + * which no encoding was taken place prior to sending. + */ + att.descr.content_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add inline image to the message */ + message.addInlineImage(att); + + /** Set the attachment info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "bin1.dat"; + att.descr.mime = "application/octet-stream"; //binary data + att.file.path = "/bin1.dat"; + att.file.storage_type = esp_mail_file_storage_type_sd; + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add attachment to the message */ + message.addAttachment(att); + + /** Set the inline image info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "green.png"; + att.descr.mime = "image/png"; + att.file.path = "/green.png"; + att.file.storage_type = esp_mail_file_storage_type_flash; + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + att.descr.content_encoding = Content_Transfer_Encoding::enc_base64; + message.addInlineImage(att); + + /** Set the attachment info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "bin2.dat"; + att.descr.mime = "application/octet-stream"; + att.file.path = "/bin2.dat"; + att.file.storage_type = esp_mail_file_storage_type_flash; + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + message.addAttachment(att); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending the Email and close the session */ + if (!MailClient.sendMail(&smtp, &message, true)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino b/lib/libesp32/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino rename to lib/libesp32/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino index aa9c85554..8cc3b5e36 --- a/lib/lib_div/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino +++ b/lib/libesp32/lib_mail/examples/Send_Embedded_Message/Send_Embedded_Message.ino @@ -1,203 +1,203 @@ - - -/** - * This example will send the Email which the - * message html and text body will be embedded as - * attachment or inline content. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending message as embedded files"; - message.addRecipient("Admin", "####@#####_dot_com"); - - message.html.content = "This is html message"; - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "utf-8"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - /* Enable to send this message body as file */ - message.html.embed.enable = true; - - /* The name of embedded file */ - message.html.embed.filename = "test.html"; - - /** The embedded type - * esp_mail_smtp_embed_message_type_attachment or 0 - * esp_mail_smtp_embed_message_type_inline or 1 - */ - message.html.embed.type = esp_mail_smtp_embed_message_type_attachment; - - - - message.text.content = "This is simple plain text message"; - message.text.charSet = "utf-8"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; - message.text.embed.enable = true; - message.text.embed.filename = "test.txt"; - message.text.embed.type = esp_mail_smtp_embed_message_type_inline; - - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email which the + * message html and text body will be embedded as + * attachment or inline content. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending message as embedded files"; + message.addRecipient("Admin", "####@#####_dot_com"); + + message.html.content = "This is html message"; + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "utf-8"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + /* Enable to send this message body as file */ + message.html.embed.enable = true; + + /* The name of embedded file */ + message.html.embed.filename = "test.html"; + + /** The embedded type + * esp_mail_smtp_embed_message_type_attachment or 0 + * esp_mail_smtp_embed_message_type_inline or 1 + */ + message.html.embed.type = esp_mail_smtp_embed_message_type_attachment; + + + + message.text.content = "This is simple plain text message"; + message.text.charSet = "utf-8"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; + message.text.embed.enable = true; + message.text.embed.filename = "test.txt"; + message.text.embed.type = esp_mail_smtp_embed_message_type_inline; + + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino b/lib/libesp32/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino rename to lib/libesp32/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino index caeee7b64..be8f2578d --- a/lib/lib_div/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino +++ b/lib/libesp32/lib_mail/examples/Send_Enriched_Text/Send_Enriched_Text.ino @@ -1,187 +1,187 @@ - - -/** - * This example will send the Email in enriched text version. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending enriched text Email"; - message.addRecipient("Someone", "####@#####_dot_com"); - - message.text.content = "This is enriched as defined in RFC 1896\r\n\r\nIsn't it cool?"; - - message.text.content_type = "text/enriched"; - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.text.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email in enriched text version. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending enriched text Email"; + message.addRecipient("Someone", "####@#####_dot_com"); + + message.text.content = "This is enriched as defined in RFC 1896\r\n\r\nIsn't it cool?"; + + message.text.content_type = "text/enriched"; + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.text.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Flash_Message_with_Inline_Image/Send_Flash_Message_with_Inline_Image.ino b/lib/libesp32/lib_mail/examples/Send_Flash_Message_with_Inline_Image/Send_Flash_Message_with_Inline_Image.ino similarity index 100% rename from lib/lib_div/lib_mail/examples/Send_Flash_Message_with_Inline_Image/Send_Flash_Message_with_Inline_Image.ino rename to lib/libesp32/lib_mail/examples/Send_Flash_Message_with_Inline_Image/Send_Flash_Message_with_Inline_Image.ino diff --git a/lib/lib_div/lib_mail/examples/Send_HTML/Send_HTML.ino b/lib/libesp32/lib_mail/examples/Send_HTML/Send_HTML.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_HTML/Send_HTML.ino rename to lib/libesp32/lib_mail/examples/Send_HTML/Send_HTML.ino index 641009c1a..81b0a5998 --- a/lib/lib_div/lib_mail/examples/Send_HTML/Send_HTML.ino +++ b/lib/libesp32/lib_mail/examples/Send_HTML/Send_HTML.ino @@ -1,221 +1,221 @@ - - -/** - * This example will send the Email in - * the html version. - * - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** ######################################################## - * Some properties of SMTPSession data and parameters pass to - * SMTP_Message class accept the pointer to constant char - * i.e. const char*. - * - * You may assign a string literal to that properties or function - * like below example. - * - * session.login.user_domain = "mydomain.net"; - * session.login.user_domain = String("mydomain.net").c_str(); - * - * or - * - * String doman = "mydomain.net"; - * session.login.user_domain = domain.c_str(); - * - * And - * - * String name = "Jack " + String("dawson"); - * String email = "jack_dawson" + String(123) + "@mail.com"; - * - * message.addRecipient(name.c_str(), email.c_str()); - * - * message.addHeader(String("Message-ID: ").c_str()); - * - * or - * - * String header = "Message-ID: "; - * message.addHeader(header.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending html Email"; - message.addRecipient("Admin", "####@#####_dot_com"); - - String htmlMsg = "

This is the html text message.

The message was sent via ESP device.

"; - message.html.content = htmlMsg.c_str(); - - /** The html text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email in + * the html version. + * + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** ######################################################## + * Some properties of SMTPSession data and parameters pass to + * SMTP_Message class accept the pointer to constant char + * i.e. const char*. + * + * You may assign a string literal to that properties or function + * like below example. + * + * session.login.user_domain = "mydomain.net"; + * session.login.user_domain = String("mydomain.net").c_str(); + * + * or + * + * String doman = "mydomain.net"; + * session.login.user_domain = domain.c_str(); + * + * And + * + * String name = "Jack " + String("dawson"); + * String email = "jack_dawson" + String(123) + "@mail.com"; + * + * message.addRecipient(name.c_str(), email.c_str()); + * + * message.addHeader(String("Message-ID: ").c_str()); + * + * or + * + * String header = "Message-ID: "; + * message.addHeader(header.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending html Email"; + message.addRecipient("Admin", "####@#####_dot_com"); + + String htmlMsg = "

This is the html text message.

The message was sent via ESP device.

"; + message.html.content = htmlMsg.c_str(); + + /** The html text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino b/lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino rename to lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino index 27678b415..65f6d3536 --- a/lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino +++ b/lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/Send_Parallel_Attachment.ino @@ -1,229 +1,229 @@ - - -/** - * This example will send the Email with media as parallen attachments - * e.g. audio and images and play or display them simultaneously on the Email client. - * - * This depends on the Mail client supports. - * - * The html and text version messages will be sent. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -//The file systems for flash and sd memory can be changed in ESP_Mail_FS.h. - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -/* This is for attachment data */ -#include "data.h" - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT esp_mail_smtp_port_587 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Enable the chunked data transfer with pipelining for large message if server supported */ - message.enable.chunking = true; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - - message.subject = "Test sending Email with parallel attachments"; - message.addRecipient("user1", "####@#####_dot_com"); - - message.html.content = "This message contains image and audio file which will play on the Mail client in parallel or simultaneously (depends on the client supports)."; - - /** The HTML text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "utf-8"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - message.text.content = "This message contains image and audio file which will play on the Mail client in parallel or simultaneously (depends on the client supports)."; - message.text.charSet = "us-ascii"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* The attachment data item */ - SMTP_Attachment att; - - /** Set the attachment info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - att.descr.filename = "haun.png"; - att.descr.mime = "image/png"; - att.blob.data = shaun_png; - att.blob.size = sizeof(shaun_png); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - message.addParallelAttachment(att); - - /** Set the attachment info e.g. - * file name, MIME type, file path, file storage type, - * transfer encoding and content encoding - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "mu_law.wav"; - att.descr.mime = "audio/basic"; - att.blob.data = mu_law_wave; - att.blob.size = sizeof(mu_law_wave); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - message.addParallelAttachment(att); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending the Email and close the session */ - if (!MailClient.sendMail(&smtp, &message, true)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email with media as parallen attachments + * e.g. audio and images and play or display them simultaneously on the Email client. + * + * This depends on the Mail client supports. + * + * The html and text version messages will be sent. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +//The file systems for flash and sd memory can be changed in ESP_Mail_FS.h. + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +/* This is for attachment data */ +#include "data.h" + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT esp_mail_smtp_port_587 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Enable the chunked data transfer with pipelining for large message if server supported */ + message.enable.chunking = true; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + + message.subject = "Test sending Email with parallel attachments"; + message.addRecipient("user1", "####@#####_dot_com"); + + message.html.content = "This message contains image and audio file which will play on the Mail client in parallel or simultaneously (depends on the client supports)."; + + /** The HTML text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "utf-8"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + message.text.content = "This message contains image and audio file which will play on the Mail client in parallel or simultaneously (depends on the client supports)."; + message.text.charSet = "us-ascii"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* The attachment data item */ + SMTP_Attachment att; + + /** Set the attachment info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + att.descr.filename = "haun.png"; + att.descr.mime = "image/png"; + att.blob.data = shaun_png; + att.blob.size = sizeof(shaun_png); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + message.addParallelAttachment(att); + + /** Set the attachment info e.g. + * file name, MIME type, file path, file storage type, + * transfer encoding and content encoding + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "mu_law.wav"; + att.descr.mime = "audio/basic"; + att.blob.data = mu_law_wave; + att.blob.size = sizeof(mu_law_wave); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + message.addParallelAttachment(att); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending the Email and close the session */ + if (!MailClient.sendMail(&smtp, &message, true)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/data.h b/lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/data.h old mode 100755 new mode 100644 similarity index 98% rename from lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/data.h rename to lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/data.h index c7a88aeab..0b73e9752 --- a/lib/lib_div/lib_mail/examples/Send_Parallel_Attachment/data.h +++ b/lib/libesp32/lib_mail/examples/Send_Parallel_Attachment/data.h @@ -1,1957 +1,1957 @@ -#include - -static const uint8_t mu_law_wave[] PROGMEM = { -0x52, 0x49, 0x46, 0x46, 0x84, 0x5D, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, -0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x40, 0x1F, 0x00, 0x00, -0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0x00, 0x5D, -0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5D, 0x00, 0x00, 0xFB, 0xFD, 0xFF, 0xFE, 0xFF, 0x7F, -0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7C, 0x7D, -0x7C, 0x7C, 0x7D, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7E, 0xFF, 0x7F, -0x7D, 0x7C, 0x7B, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7C, 0x7C, 0x7B, -0x7B, 0x7B, 0x7A, 0x7A, 0x7B, 0x7A, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x7C, 0x7E, -0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x79, 0x79, 0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7E, 0xFF, 0xFE, 0xFE, -0xFF, 0x7E, 0x7C, 0x7D, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x79, 0x78, 0x79, -0x7A, 0x79, 0x78, 0x78, 0x78, 0x77, 0x78, 0x78, 0x79, 0x79, 0x78, 0x77, 0x77, 0x78, 0x78, 0x79, -0x7A, 0x79, 0x79, 0x78, 0x78, 0x7A, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, -0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF8, -0xF7, 0xF7, 0xF8, 0xF9, 0xF9, 0xFB, 0xFC, 0xFC, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFE, -0xFF, 0xFF, 0xFE, 0x7E, 0x7D, 0x7D, 0x7C, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x7C, -0x7B, 0x7A, 0x79, 0x79, 0x79, 0x77, 0x77, 0x78, 0x79, 0x7B, 0x7C, 0x7B, 0x7B, 0x7C, 0x7B, 0x7B, -0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x79, -0x78, 0x77, 0x77, 0x79, 0x79, 0x79, 0x7B, 0x7B, 0x7A, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7C, 0x7C, -0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x79, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x78, 0x78, 0x7A, 0x7A, 0x79, -0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFC, 0xFB, 0xFB, 0xFC, -0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFE, 0xFF, 0x7E, 0x7E, 0xFF, 0xFF, 0x7E, 0x7E, 0x7D, 0x7B, -0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, -0x7C, 0x7B, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7C, 0x79, 0x79, 0x7A, -0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7C, 0x7C, 0x7C, 0x7E, 0x7D, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0xFE, -0xFD, 0xFD, 0xFB, 0xFB, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, -0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7D, 0x7E, 0x7D, 0x7B, 0x7B, 0x7B, 0x79, 0x78, 0x77, -0x78, 0x78, 0x77, 0x77, 0x77, 0x78, 0x78, 0x76, 0x76, 0x75, 0x73, 0x73, 0x73, 0x72, 0x73, 0x73, -0x74, 0x74, 0x74, 0x75, 0x77, 0x78, 0x78, 0x77, 0x76, 0x77, 0x76, 0x76, 0x78, 0x79, 0x79, 0x79, -0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7E, -0x7E, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD, 0xFE, 0xFF, 0xFD, 0xFE, 0x7F, 0xFF, -0x7F, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7F, -0xFF, 0xFF, 0xFE, 0xFE, 0x7F, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7C, 0x7D, -0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFF, 0xFE, 0xFC, 0xFD, 0xFC, 0xFC, 0xFD, -0xFC, 0xFB, 0xFB, 0xFC, 0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, -0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7D, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7D, 0x7B, 0x7B, 0x79, -0x78, 0x78, 0x78, 0x76, 0x74, 0x74, 0x73, 0x72, 0x75, 0x77, 0x77, 0x76, 0x75, 0x75, 0x76, 0x77, -0x77, 0x76, 0x74, 0x74, 0x74, 0x74, 0x76, 0x76, 0x76, 0x77, 0x75, 0x76, 0x78, 0x79, 0x77, 0x77, -0x79, 0x7B, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7D, 0x7F, 0x7E, 0xFF, 0xFF, 0x7F, 0xFE, 0xFC, 0xFE, -0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFD, 0xFC, 0xFE, 0xFF, 0x7E, 0x7F, 0x7F, 0x7D, 0x7E, 0xFF, 0x7F, -0x7F, 0xFF, 0x7F, 0x7E, 0x7D, 0x7E, 0xFF, 0x7E, 0x7F, 0x7F, 0x7C, 0x7D, 0x7F, 0x7D, 0x7E, 0x7E, -0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7B, 0x7D, 0x7B, 0x7A, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7C, -0x7D, 0x7E, 0x7F, 0xFF, 0x7F, 0x7F, 0x7F, 0xFF, 0xFD, 0xFD, 0xFD, 0xFC, 0xFB, 0xFB, 0xFB, 0xFB, -0xFA, 0xFB, 0xFB, 0xFB, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0x7E, 0x7E, 0x7D, 0x7B, 0x7B, 0x7B, -0x78, 0x79, 0x78, 0x75, 0x76, 0x76, 0x76, 0x76, 0x74, 0x74, 0x73, 0x73, 0x74, 0x73, 0x72, 0x73, -0x75, 0x75, 0x74, 0x76, 0x74, 0x72, 0x73, 0x74, 0x75, 0x75, 0x76, 0x77, 0x77, 0x78, 0x77, 0x77, -0x77, 0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x7A, 0x79, 0x7B, 0x7C, 0x7B, 0x7B, -0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7C, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, -0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, -0xFE, 0x7E, 0x7E, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7E, 0xFF, 0x7E, 0x7E, 0x7E, 0x7D, -0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFC, 0xFE, 0x7F, 0x7E, 0x7E, 0x7F, 0xFF, 0xFF, -0x7F, 0x7E, 0x7D, 0x7D, 0x7D, 0x7B, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, -0x7D, 0x7C, 0x7C, 0x7E, 0x7E, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, -0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFB, -0xFB, 0xFB, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, -0x7E, 0x7E, 0x7E, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7F, -0x7D, 0x7B, 0x7C, 0x7C, 0x7D, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7E, -0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x77, 0x77, 0x76, 0x74, 0x73, -0x72, 0x73, 0x74, 0x75, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, -0x7C, 0x7D, 0x7E, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7D, 0x7C, 0x7B, -0x7A, 0x7B, 0x7B, 0x7A, 0x7A, 0x79, 0x79, 0x7A, 0x79, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x79, 0x79, -0x79, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7C, 0x7B, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7F, -0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7D, 0x7D, -0x7D, 0x7D, 0x7E, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7F, 0x7F, 0x7E, 0x7F, 0x7E, 0x7E, 0x7F, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7F, 0x7F, 0x7E, -0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, -0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, -0x7D, 0x7E, 0xFF, 0x7E, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, -0x7A, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7C, 0x7C, 0x7A, 0x79, 0x79, 0x78, 0x78, -0x78, 0x77, 0x77, 0x76, 0x75, 0x74, 0x74, 0x74, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x76, 0x75, -0x74, 0x75, 0x77, 0x78, 0x78, 0x7A, 0x7B, 0x7D, 0xFF, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, -0xFC, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, -0xF8, 0xFA, 0xF8, 0xF6, 0xF7, 0xF7, 0xF8, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFB, -0xFB, 0xFB, 0xFD, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, -0x7F, 0x7E, 0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0xFD, 0xFE, 0xFF, -0xFE, 0xFE, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7E, 0x7F, -0x7D, 0x7D, 0x7E, 0x7E, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x79, 0x77, 0x78, 0x79, 0x78, 0x77, 0x76, -0x76, 0x75, 0x73, 0x74, 0x73, 0x71, 0x70, 0x70, 0x71, 0x72, 0x71, 0x71, 0x70, 0x6F, 0x6F, 0x6F, -0x70, 0x71, 0x71, 0x72, 0x73, 0x74, 0x74, 0x74, 0x76, 0x77, 0x76, 0x78, 0x79, 0x79, 0x79, 0x78, -0x7A, 0x7B, 0x7B, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, -0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7D, -0x7D, 0x7D, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7C, 0x7A, 0x78, 0x79, 0x78, 0x77, -0x76, 0x78, 0x79, 0x78, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7A, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, -0x7E, 0xFF, 0xFD, 0xFC, 0xFC, 0xFA, 0xF9, 0xFA, 0xFB, 0xFA, 0xF9, 0xF8, 0xF8, 0xF9, 0xFA, 0xF9, -0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF7, 0xF9, 0xF9, 0xF8, 0xF8, 0xFA, 0xFB, 0xFA, 0xFB, 0xFD, 0xFD, -0xFC, 0xFD, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFF, 0x7F, 0x7F, 0x7E, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, -0x7A, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x75, 0x76, 0x77, 0x76, 0x75, 0x76, 0x76, 0x75, 0x76, 0x77, -0x78, 0x79, 0x7B, 0x7B, 0x7B, 0x7A, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7A, 0x7B, 0x7B, 0x7A, -0x7B, 0x7D, 0x7E, 0x7B, 0x7C, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFC, 0xFD, 0xFE, 0xFF, -0xFE, 0xFF, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7D, 0x7F, 0x7F, 0x7C, 0x7B, -0x7A, 0x78, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7C, 0x7C, 0x7A, 0x7B, 0x7C, 0x7C, 0x7B, 0x7C, 0x7C, -0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x79, 0x7A, 0x79, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, -0x7D, 0xFE, 0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFA, 0xF8, 0xF8, 0xF9, 0xF8, 0xF5, 0xF4, 0xF7, 0xF8, -0xF8, 0xF8, 0xF9, 0xFB, 0xFC, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7B, -0x7A, 0x7B, 0x7B, 0x7A, 0x78, 0x78, 0x77, 0x76, 0x76, 0x75, 0x76, 0x76, 0x76, 0x77, 0x75, 0x75, -0x76, 0x75, 0x76, 0x76, 0x78, 0x78, 0x77, 0x78, 0x76, 0x74, 0x75, 0x77, 0x79, 0x7A, 0x7A, 0x7B, -0x7C, 0x7D, 0x7D, 0x7D, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, -0xFE, 0x7F, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0xFF, 0xFD, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, -0xFC, 0xFD, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7C, -0x7B, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x7A, 0x7C, 0x7A, 0x7B, 0x7C, 0x7C, 0x7E, 0x7E, 0xFF, -0xFF, 0x7F, 0x7F, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, -0x7C, 0x7D, 0xFF, 0x7E, 0x7C, 0x7D, 0xFF, 0xFF, 0xFF, 0xFD, 0xFD, 0xFF, 0x7E, 0x7F, 0x7E, 0x7E, -0x7F, 0x7E, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x79, -0x77, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7C, 0x7D, 0x7D, 0x7E, 0xFD, 0xFD, 0xFE, 0xFC, 0xFC, 0xFB, -0xFB, 0xFC, 0xFB, 0xFB, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, -0xFF, 0xFF, 0xFD, 0xFC, 0xFD, 0xFD, 0xFC, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7D, 0x7C, 0x7D, 0x7E, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, -0x7B, 0x7C, 0x7D, 0x7B, 0x7B, 0x7C, 0x7A, 0x78, 0x78, 0x78, 0x77, 0x75, 0x75, 0x75, 0x73, 0x72, -0x72, 0x72, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x74, 0x74, -0x75, 0x76, 0x76, 0x76, 0x77, 0x78, 0x77, 0x78, 0x79, 0x77, 0x78, 0x78, 0x79, 0x7A, 0x7A, 0x7A, -0x7A, 0x79, 0x77, 0x76, 0x76, 0x78, 0x78, 0x77, 0x77, 0x77, 0x75, 0x74, 0x74, 0x75, 0x75, 0x76, -0x79, 0x79, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7D, 0xFF, 0xFD, 0xFD, 0xFE, 0xFF, 0xFE, 0xFC, -0xF9, 0xF9, 0xFB, 0xFA, 0xF9, 0xF8, 0xF8, 0xF8, 0xF6, 0xF4, 0xF6, 0xF8, 0xF7, 0xF7, 0xF9, 0xF9, -0xF9, 0xF8, 0xF8, 0xFB, 0xFA, 0xF9, 0xFA, 0xFB, 0xFC, 0xFE, 0xFC, 0xFD, 0xFF, 0x7E, 0x7F, 0x7E, -0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, 0x7A, 0x7A, 0x79, 0x79, 0x78, 0x77, 0x78, -0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, 0x7F, 0xFE, 0xFE, 0xFD, 0xFD, -0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xFC, 0xFB, 0xFC, -0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, -0x7C, 0x7B, 0x7B, 0x7C, 0x79, 0x79, 0x79, 0x78, 0x76, 0x75, 0x76, 0x78, 0x77, 0x76, 0x76, 0x77, -0x76, 0x74, 0x74, 0x75, 0x75, 0x75, 0x74, 0x74, 0x76, 0x77, 0x78, 0x7A, 0x7C, 0x7B, 0x7A, 0x7D, -0x7E, 0x7E, 0x7C, 0x7D, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFE, 0xFF, -0xFF, 0x7F, 0x7E, 0x7F, 0xFF, 0xFE, 0xFD, 0xFF, 0xFE, 0xFC, 0xFB, 0xFC, 0xFE, 0xFD, 0xFC, 0xFE, -0xFF, 0xFE, 0xFD, 0xFF, 0x7E, 0x7F, 0xFE, 0xFC, 0xFD, 0xFE, 0xFD, 0xFD, 0xFE, 0x7F, 0x7E, 0xFF, -0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, -0x7E, 0x7D, 0x7E, 0xFF, 0xFF, 0xFE, 0xFD, 0xFF, 0x7D, 0x7A, 0x79, 0x79, 0x7A, 0x7C, 0x7B, 0x7B, -0x7D, 0x7E, 0x7E, 0x7D, 0xFF, 0xFF, 0x7F, 0x7F, 0x7E, 0x7B, 0x7A, 0x7A, 0x7A, 0x78, 0x77, 0x77, -0x77, 0x78, 0x78, 0x76, 0x76, 0x76, 0x76, 0x74, 0x74, 0x73, 0x74, 0x74, 0x75, 0x74, 0x72, 0x74, -0x76, 0x74, 0x74, 0x76, 0x76, 0x74, 0x73, 0x73, 0x72, 0x73, 0x74, 0x76, 0x77, 0x75, 0x73, 0x75, -0x77, 0x78, 0x75, 0x75, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7B, -0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7D, 0x7C, 0x7D, 0x7B, 0x7C, 0x7F, -0xFF, 0xFF, 0x7E, 0x7D, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x79, 0x77, 0x77, -0x78, 0x76, 0x76, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x7B, 0x7C, 0x7B, 0x7C, 0x7D, 0xFF, -0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFB, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, -0xFA, 0xF9, 0xF9, 0xF7, 0xF7, 0xF7, 0xF4, 0xF4, 0xF4, 0xF2, 0xEF, 0xEE, 0xEF, 0xF2, 0xF2, 0xF1, -0xF2, 0xF5, 0xF5, 0xF3, 0xF1, 0xF2, 0xF5, 0xF7, 0xF8, 0xFA, 0xFB, 0xFC, 0xFC, 0xFB, 0xFC, 0xFC, -0xFD, 0xFF, 0x7F, 0x7E, 0x7C, 0x7A, 0x7A, 0x78, 0x77, 0x76, 0x75, 0x75, 0x74, 0x71, 0x70, 0x71, -0x71, 0x71, 0x70, 0x6F, 0x71, 0x71, 0x71, 0x73, 0x75, 0x77, 0x77, 0x76, 0x79, 0x7A, 0x79, 0x79, -0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7F, 0xFF, 0xFE, 0xFF, 0x7E, 0x7D, 0x7E, 0x7E, 0x7D, 0x7D, -0x7E, 0x7C, 0x7C, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x77, 0x75, 0x75, 0x76, 0x78, 0x79, 0x78, -0x7A, 0x7C, 0x7C, 0x7A, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7B, 0x7D, 0x7D, 0x7C, -0x7C, 0x7E, 0xFF, 0x7F, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0xFF, 0x7F, 0x7E, -0x7D, 0x7D, 0xFF, 0xFF, 0x7F, 0xFF, 0xFC, 0xFC, 0xFD, 0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xFB, 0xFB, -0xF9, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0x7F, 0x7F, 0x7E, 0x7D, 0x7E, 0x7D, 0x7B, 0x7C, 0x7B, -0x7B, 0x7C, 0x7C, 0x7A, 0x78, 0x78, 0x7B, 0x7A, 0x77, 0x75, 0x76, 0x75, 0x76, 0x76, 0x76, 0x76, -0x78, 0x79, 0x78, 0x75, 0x75, 0x76, 0x77, 0x7A, 0x7D, 0xFF, 0xFC, 0xFB, 0xFA, 0xFA, 0xFB, 0xFA, -0xFB, 0xFB, 0xFC, 0xFD, 0xFD, 0x7E, 0x78, 0x76, 0x72, 0x70, 0x71, 0x75, 0x77, 0x77, 0x79, 0x7A, -0x7A, 0x7A, 0x7B, 0xFF, 0xFB, 0xFA, 0xF9, 0xFB, 0xFD, 0xFE, 0x7E, 0x7F, 0xFF, 0xFC, 0xF6, 0xF3, -0xF7, 0xFC, 0xFF, 0x7A, 0x73, 0x6E, 0x6C, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6F, 0x74, 0x73, 0x72, -0x75, 0x7A, 0x7D, 0x7D, 0xFF, 0xFF, 0x7C, 0x7C, 0x7D, 0x7B, 0x7A, 0x79, 0x7B, 0xFF, 0xFF, 0x7D, -0x7C, 0xFF, 0xFE, 0x7D, 0xFC, 0xF3, 0xF6, 0xFC, 0xFE, 0xFE, 0xFF, 0x78, 0x75, 0x79, 0x7A, 0x7E, -0xF8, 0xF6, 0xF6, 0xF2, 0xEF, 0xEE, 0xF0, 0xF7, 0xFD, 0xFE, 0xFD, 0x7B, 0x77, 0x7C, 0xFF, 0xFB, -0xFD, 0x7D, 0xFF, 0x7C, 0x78, 0x7A, 0x7A, 0xFF, 0xF9, 0xF5, 0xF1, 0xF4, 0xFA, 0xFC, 0xFC, 0xFA, -0xF8, 0xFA, 0xFB, 0xFA, 0xFB, 0xFC, 0xFD, 0xFF, 0x7D, 0x7B, 0x7D, 0x7E, 0x7A, 0x76, 0x76, 0x75, -0x72, 0x75, 0x7B, 0x7D, 0x7A, 0x7A, 0x7D, 0xFF, 0x7C, 0x7C, 0xFF, 0x7D, 0x7A, 0x7B, 0x7C, 0x7E, -0xFC, 0xFC, 0x7F, 0xFE, 0xFD, 0xFE, 0xFC, 0xF8, 0xF9, 0xFA, 0xFB, 0xFE, 0x7E, 0x79, 0x72, 0x72, -0x74, 0x76, 0x78, 0x76, 0x78, 0x7A, 0x75, 0x76, 0x77, 0x75, 0x73, 0x73, 0x73, 0x73, 0x74, 0x73, -0x73, 0x77, 0x78, 0x79, 0x79, 0x7A, 0x79, 0x73, 0x73, 0x7B, 0x7C, 0x7C, 0xFC, 0xFC, 0xFE, 0x7F, -0x76, 0x73, 0x76, 0x73, 0x73, 0x78, 0x7C, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0x7D, 0x7B, 0x7B, -0x7C, 0x7A, 0x78, 0x7B, 0x7B, 0x7D, 0x7F, 0x7A, 0x77, 0x73, 0x6F, 0x70, 0x72, 0x73, 0x72, 0x72, -0x73, 0x74, 0x74, 0x76, 0x7D, 0xFE, 0x7D, 0x7C, 0x7C, 0x78, 0x76, 0x77, 0x79, 0x7B, 0xFF, 0xFD, -0xFE, 0x7E, 0x7B, 0x7A, 0x78, 0x78, 0x7B, 0x7C, 0x7C, 0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x79, 0x7B, -0xFF, 0xFA, 0xF9, 0xFD, 0x7E, 0x7D, 0x7C, 0x7B, 0x7E, 0xFE, 0xFC, 0xFF, 0x7E, 0xFE, 0xFD, 0xFE, -0xFD, 0xFC, 0xFB, 0xFC, 0xFC, 0xFD, 0xF9, 0xF3, 0xF2, 0xF3, 0xF4, 0xF6, 0xF6, 0xF8, 0xFA, 0xFC, -0xFB, 0xF7, 0xF9, 0xFC, 0xFC, 0xFD, 0xFD, 0x7E, 0x7C, 0x7F, 0xFF, 0x7D, 0x7A, 0x7A, 0x7B, 0x78, -0x78, 0x78, 0x75, 0x75, 0x76, 0x79, 0x7A, 0x79, 0x7C, 0x7F, 0xFD, 0xFF, 0x7E, 0xFF, 0x7D, 0x79, -0x77, 0x78, 0x79, 0x78, 0x79, 0x7C, 0x7D, 0x7F, 0x7E, 0x7E, 0xFE, 0xFC, 0xF8, 0xF7, 0xFA, 0xFB, -0xFC, 0xFD, 0xFF, 0x7D, 0x7E, 0x7F, 0xFF, 0x7D, 0x7B, 0x7D, 0xFE, 0xF9, 0xF5, 0xF6, 0xF6, 0xF4, -0xF6, 0xFA, 0xFE, 0x7D, 0x79, 0x78, 0x77, 0x77, 0x7A, 0x7E, 0xFE, 0xFF, 0xFF, 0xFA, 0xFA, 0xFE, -0x7C, 0x75, 0x71, 0x72, 0x74, 0x75, 0x74, 0x75, 0x75, 0x77, 0x79, 0x79, 0x77, 0x74, 0x74, 0x74, -0x72, 0x71, 0x70, 0x71, 0x70, 0x6F, 0x72, 0x73, 0x72, 0x73, 0x71, 0x70, 0x73, 0x78, 0x7A, 0x78, -0x77, 0x77, 0x76, 0x74, 0x73, 0x72, 0x70, 0x71, 0x75, 0x78, 0x7B, 0x7F, 0xFF, 0xFE, 0xFB, 0xFD, -0x7E, 0xFF, 0xFD, 0x7F, 0x7C, 0x7B, 0x7F, 0xFD, 0xFF, 0xFE, 0xFC, 0xFE, 0xFD, 0xFC, 0xFA, 0xF7, -0xF7, 0xFB, 0xFD, 0xFE, 0x7E, 0x7C, 0x7B, 0x7A, 0x7B, 0x79, 0x75, 0x75, 0x76, 0x78, 0x7D, 0xFF, -0x7E, 0x7D, 0x7E, 0x7C, 0x77, 0x75, 0x76, 0x76, 0x78, 0x7E, 0xFD, 0xFB, 0xFC, 0xFD, 0xFE, 0x7C, -0x78, 0x78, 0x7A, 0x7B, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7A, 0x78, 0x79, 0x7B, 0x7A, 0x7D, 0xFC, -0xFD, 0x7E, 0xFF, 0xFB, 0xF7, 0xF6, 0xF7, 0xF7, 0xF6, 0xFA, 0xFF, 0xFE, 0xFD, 0xFF, 0x7D, 0xFF, -0xFC, 0xFB, 0xF9, 0xFD, 0xFE, 0xFD, 0x7E, 0x79, 0x76, 0x71, 0x6F, 0x6E, 0x6E, 0x6F, 0x72, 0x76, -0x7A, 0x7E, 0xFF, 0xFF, 0x7C, 0x7B, 0x7C, 0x7B, 0x7A, 0x79, 0x7A, 0x7E, 0xFF, 0xFF, 0x7E, 0x7D, -0x7D, 0x7E, 0x7C, 0x78, 0x79, 0x7A, 0x7B, 0xFF, 0xFF, 0x7F, 0x7E, 0x7B, 0x79, 0x79, 0x76, 0x72, -0x73, 0x7B, 0xFF, 0xFE, 0xFB, 0xFA, 0xFA, 0xFC, 0x7D, 0x78, 0x77, 0x78, 0x79, 0x79, 0x78, 0x79, -0x77, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7A, 0x7B, 0x7F, 0xFD, 0xFE, 0xFF, 0x7F, 0x7E, 0x7E, 0x7C, -0x78, 0x78, 0x7B, 0x7B, 0x7A, 0x7C, 0x7F, 0xFE, 0xFD, 0xFE, 0x7E, 0xFF, 0xFD, 0xFC, 0xF9, 0xF5, -0xF2, 0xF0, 0xF2, 0xF3, 0xF0, 0xF1, 0xF2, 0xF1, 0xF4, 0xF7, 0xF8, 0xFC, 0xFC, 0xF6, 0xF3, 0xF4, -0xF5, 0xF5, 0xF6, 0xFB, 0xFF, 0xFF, 0xFF, 0x7D, 0x7D, 0x7E, 0x7D, 0x79, 0x75, 0x74, 0x75, 0x72, -0x72, 0x75, 0x75, 0x74, 0x77, 0x7A, 0x7A, 0x7A, 0x7A, 0x77, 0x74, 0x73, 0x72, 0x74, 0x75, 0x76, -0x77, 0x74, 0x74, 0x77, 0x79, 0x79, 0x79, 0x78, 0x77, 0x74, 0x70, 0x6F, 0x70, 0x6F, 0x6E, 0x6E, -0x6E, 0x6F, 0x6F, 0x72, 0x75, 0x72, 0x74, 0x79, 0x7B, 0x7B, 0x7B, 0x7D, 0xFF, 0x7E, 0x7F, 0xFE, -0xFE, 0x7F, 0x7D, 0x7D, 0xFF, 0xFD, 0xFE, 0x7E, 0x7D, 0x7D, 0x7E, 0x7C, 0x79, 0x77, 0x75, 0x76, -0x77, 0x78, 0x78, 0x74, 0x75, 0x7A, 0x7F, 0xFB, 0xF9, 0xF9, 0xF8, 0xFD, 0x7E, 0x7B, 0x76, 0x78, -0x7B, 0x7C, 0x7E, 0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x7C, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0x7D, 0x7B, -0x7B, 0x79, 0x79, 0x7A, 0x7D, 0xFF, 0x7F, 0x7F, 0xFF, 0xFD, 0xFB, 0xFD, 0xFE, 0xFE, 0xFF, 0x7D, -0x7C, 0x7F, 0xFB, 0xF7, 0xF6, 0xFC, 0x7F, 0xFF, 0xFE, 0xFB, 0xFD, 0xFE, 0xFC, 0xFD, 0xFE, 0x7C, -0x76, 0x74, 0x74, 0x79, 0x79, 0x76, 0x77, 0x79, 0x7C, 0x7D, 0x7B, 0x7B, 0x7B, 0x7E, 0xFD, 0xFB, -0xF9, 0xF9, 0xFE, 0x7C, 0x79, 0x79, 0x77, 0x76, 0x79, 0x7B, 0x7D, 0x7C, 0x7C, 0x7D, 0x7C, 0x78, -0x75, 0x74, 0x72, 0x72, 0x73, 0x71, 0x70, 0x73, 0x78, 0x7C, 0x7D, 0x7E, 0xFF, 0x7F, 0x7F, 0x7D, -0x7A, 0x78, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x7B, 0xFF, 0xFF, 0xFD, 0xFC, 0xFC, 0xFA, 0xF9, -0xF8, 0xF9, 0xFE, 0x7E, 0xFF, 0xFE, 0xFE, 0x7D, 0x7A, 0x78, 0x75, 0x75, 0x77, 0x7A, 0x7A, 0x79, -0x7B, 0x7D, 0xFF, 0xFD, 0xFC, 0xFC, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, 0xFB, 0xFA, 0xF9, 0xF7, -0xF8, 0xFA, 0xF8, 0xF9, 0xFC, 0xFD, 0xFD, 0xFC, 0xF9, 0xF7, 0xF8, 0xFC, 0x7E, 0x7B, 0x7A, 0x7A, -0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x77, 0x72, 0x72, 0x74, 0x76, 0x7B, 0x7C, 0x7B, 0x7D, 0x7E, 0x7D, -0x7D, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x78, 0x78, 0x79, 0x79, 0x79, 0x78, 0x79, 0x7B, 0x7C, 0x7C, -0x7A, 0x7A, 0x77, 0x75, 0x75, 0x74, 0x73, 0x70, 0x6E, 0x6E, 0x6E, 0x6F, 0x72, 0x75, 0x77, 0x7A, -0x7D, 0x7D, 0x7D, 0x7E, 0xFF, 0x7D, 0x7A, 0x7A, 0x7A, 0x7B, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, -0x7D, 0x7B, 0x78, 0x75, 0x73, 0x74, 0x75, 0x75, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7D, 0xFE, 0xFC, -0xF9, 0xF8, 0xF8, 0xF8, 0xF6, 0xF5, 0xF6, 0xF8, 0xFB, 0xFC, 0xFB, 0xFD, 0xFF, 0x7E, 0x7D, 0x7C, -0x7C, 0x7B, 0x7C, 0x7B, 0x79, 0x79, 0x7A, 0x79, 0x76, 0x74, 0x72, 0x72, 0x73, 0x74, 0x75, 0x77, -0x78, 0x78, 0x78, 0x77, 0x76, 0x75, 0x75, 0x78, 0x7A, 0x79, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0x7F, -0xFE, 0xFC, 0xFC, 0xFC, 0xFA, 0xF8, 0xF7, 0xF7, 0xF8, 0xF7, 0xF5, 0xF5, 0xF5, 0xF4, 0xF5, 0xF6, -0xF7, 0xF8, 0xF9, 0xFB, 0xFE, 0x7F, 0x7D, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7D, 0x7A, 0x7A, 0x7A, -0x7A, 0x79, 0x79, 0x79, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x7A, 0x7A, 0x79, 0x77, 0x76, -0x77, 0x77, 0x77, 0x77, 0x78, 0x7A, 0x7B, 0x7D, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7B, -0x78, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0x7F, 0x7F, 0xFE, -0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFE, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, -0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x76, 0x77, 0x78, 0x77, 0x79, -0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7D, 0x7B, 0x7B, 0x7D, 0x7C, 0x7D, 0xFF, 0x7E, -0xFF, 0xFD, 0xFB, 0xF9, 0xF8, 0xF9, 0xF9, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF3, 0xF5, 0xF6, 0xF6, -0xF7, 0xF9, 0xFB, 0xFB, 0xFA, 0xFA, 0xFB, 0xFC, 0xFB, 0xFD, 0xFD, 0xFB, 0xFB, 0xFB, 0xFA, 0xF8, -0xF8, 0xF9, 0xFB, 0xFD, 0x7E, 0x7B, 0x78, 0x78, 0x77, 0x77, 0x77, 0x76, 0x76, 0x76, 0x75, 0x75, -0x73, 0x71, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6E, 0x6E, 0x6F, 0x6F, 0x6E, 0x6E, 0x6E, 0x6D, -0x6D, 0x6E, 0x6D, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x75, 0x79, 0x7C, 0x7D, 0x7E, 0xFF, -0xFF, 0xFF, 0xFD, 0xFC, 0xFA, 0xF9, 0xFB, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, 0x7E, 0x7B, 0x7A, -0x79, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C, 0x7B, 0x79, 0x79, 0x78, 0x78, 0x79, 0x7A, 0x7C, -0x7E, 0x7E, 0x7E, 0x7C, 0x7B, 0x7A, 0x7A, 0x7A, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7D, 0xFF, 0xFD, -0xFC, 0xFB, 0xFA, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFB, 0xFB, -0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFE, 0xFD, 0xFC, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, 0xF7, -0xF6, 0xF7, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0x7E, 0x7D, 0x7C, 0x7C, -0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, -0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7B, 0x78, 0x77, 0x76, 0x75, 0x75, 0x75, 0x76, 0x76, 0x75, 0x76, -0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7B, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, -0xFD, 0xFB, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFF, 0x7F, 0x7E, 0x7C, 0x7C, 0x7B, -0x7A, 0x7A, 0x7A, 0x78, 0x78, 0x77, 0x77, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7B, -0x7A, 0x7B, 0x7C, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7A, 0x7B, 0x7F, 0xFD, 0x7D, 0x79, 0xFE, -0xFA, 0xFE, 0xFC, 0xF8, 0xFC, 0xFF, 0xFF, 0xFE, 0xFB, 0xFC, 0x7F, 0xFF, 0x7F, 0x7C, 0x7D, 0x7D, -0x7E, 0x7E, 0x7B, 0x7A, 0x7A, 0x7B, 0x7C, 0x7E, 0x7E, 0x7C, 0x7B, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, -0x7D, 0x7E, 0x7F, 0x7E, 0x7F, 0xFF, 0xFE, 0xFD, 0xFE, 0xFF, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7E, -0x7D, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x7A, 0x7A, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x75, 0x74, 0x73, -0x74, 0x75, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x77, 0x76, 0x77, 0x77, 0x79, 0x7B, 0x7C, 0x7D, -0x7D, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFE, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFB, 0xFC, 0xFD, -0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7E, -0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, -0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, -0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x7E, -0x7D, 0x7C, 0x7B, 0x7B, 0x79, 0x77, 0x77, 0x78, 0x76, 0x76, 0x78, 0x7A, 0x78, 0x78, 0x78, 0x77, -0x77, 0x76, 0x75, 0x76, 0x75, 0x75, 0x77, 0x77, 0x76, 0x74, 0x74, 0x75, 0x76, 0x75, 0x76, 0x76, -0x75, 0x76, 0x76, 0x75, 0x74, 0x73, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x78, 0x7A, 0x7A, 0x7B, -0x7C, 0x7B, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFD, 0xFD, 0xFD, 0xFF, 0x7F, 0xFE, 0xFF, 0xFF, 0xFE, -0xFE, 0xFE, 0x7F, 0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0xFD, 0xFD, -0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0xFF, 0xFE, 0xFC, 0xFA, 0xF9, 0xF8, 0xF8, 0xF8, 0xF9, 0xFA, -0xFD, 0xFE, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF, 0x7F, 0xFF, 0x7E, 0x7D, 0x7B, 0x79, 0x79, 0x7B, 0x7C, -0x7C, 0x7D, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7B, 0x7B, 0x7A, 0x77, 0x78, 0x75, 0x75, 0x76, 0x76, -0x78, 0x75, 0x76, 0x77, 0x73, 0x75, 0x73, 0x77, 0x76, 0x73, 0x78, 0x73, 0x78, 0x77, 0x75, 0x7B, -0x70, 0x7E, 0x68, 0x5B, 0xE4, 0xD9, 0x77, 0x66, 0x5F, 0x6B, 0xEB, 0x78, 0x6B, 0xEC, 0xE9, 0x67, -0x5A, 0x62, 0xF8, 0xE2, 0xF1, 0x6B, 0x6F, 0x72, 0x6E, 0x6D, 0x72, 0xEF, 0xE7, 0xF7, 0x6D, 0x6A, -0x70, 0x7B, 0xFC, 0xF5, 0xED, 0xEF, 0x7D, 0x71, 0x70, 0xFF, 0xEE, 0xEF, 0xF8, 0xFC, 0x7B, 0x73, -0x6F, 0x71, 0x7E, 0xF2, 0xF2, 0x76, 0x67, 0x68, 0x78, 0xF7, 0xF5, 0xFC, 0x7E, 0x7B, 0x73, 0x70, -0x79, 0xF4, 0xEB, 0xEC, 0xF8, 0x7C, 0x7B, 0xFF, 0xF6, 0xEF, 0xEF, 0xF4, 0xF8, 0xFE, 0x7D, 0xFE, -0xF8, 0xF4, 0xF4, 0xF9, 0xFE, 0x7E, 0x7C, 0x7C, 0xFD, 0xF8, 0xF8, 0xFD, 0x7D, 0x7A, 0x79, 0x7C, -0xFF, 0xFD, 0xFB, 0xFC, 0x7D, 0x7B, 0x7D, 0x7F, 0xFD, 0xFD, 0xFF, 0x7E, 0x7C, 0x7A, 0x79, 0x7C, -0x7E, 0x7D, 0x7D, 0x7B, 0x78, 0x79, 0x7B, 0x7A, 0x78, 0x79, 0x77, 0x76, 0x78, 0x71, 0x70, 0x78, -0x7E, 0xF6, 0xFE, 0x67, 0x68, 0xF4, 0xEF, 0x74, 0x69, 0x6A, 0xFF, 0x7E, 0x6E, 0xF2, 0xFB, 0x69, -0x66, 0x69, 0xDD, 0xD9, 0x71, 0x7B, 0x74, 0x5F, 0x69, 0x61, 0x6A, 0xE6, 0xEC, 0x66, 0x55, 0x50, -0x5C, 0xFB, 0xED, 0xF5, 0xFA, 0x6F, 0x6E, 0x7B, 0xE8, 0xCF, 0xC7, 0xC6, 0xCA, 0xCB, 0xDB, 0x3A, -0x2E, 0x49, 0xBD, 0xB7, 0xC6, 0x4D, 0x41, 0x4B, 0x49, 0x4D, 0xD9, 0xBF, 0xC2, 0xFD, 0x3F, 0x3D, -0x51, 0xDF, 0xCD, 0xCC, 0xD5, 0xF4, 0x50, 0x46, 0x4D, 0xF0, 0xCB, 0xCA, 0xDA, 0x61, 0x51, 0x52, -0x5B, 0xE6, 0xD0, 0xCE, 0xDC, 0x5D, 0x53, 0x51, 0x6A, 0xF9, 0x48, 0x67, 0xC2, 0xE0, 0x5F, 0x5B, -0x4C, 0x71, 0xFA, 0x6E, 0xCE, 0xCF, 0x7A, 0x64, 0x79, 0xD8, 0xCE, 0xD1, 0xCD, 0xC3, 0xC5, 0xCD, -0xC7, 0xDC, 0x42, 0x3E, 0x53, 0xD2, 0xC0, 0xDE, 0x44, 0x48, 0x58, 0x53, 0x59, 0xFA, 0xD9, 0xD6, -0x5E, 0x43, 0x47, 0x61, 0xE1, 0xD8, 0xEA, 0x64, 0x46, 0x3C, 0x51, 0x7F, 0x77, 0xD0, 0xF6, 0x4B, -0x4B, 0x4A, 0x6F, 0xCA, 0xC6, 0xC9, 0xC7, 0xC2, 0xC8, 0xBD, 0xB0, 0xAB, 0xBB, 0x1F, 0x19, 0x5F, -0xA3, 0xA5, 0xBA, 0x30, 0x30, 0x47, 0x31, 0x37, 0xBC, 0xAB, 0xB8, 0x3D, 0x2B, 0x30, 0x4E, 0xBD, -0xB4, 0xC4, 0x5D, 0x3F, 0x38, 0x41, 0x6E, 0xC8, 0xC1, 0xDC, 0x4B, 0x3B, 0x3F, 0xE5, 0xBF, 0xBC, -0xC8, 0xE5, 0xDE, 0xC0, 0xAE, 0xAD, 0xAF, 0xC1, 0x20, 0x1A, 0x63, 0xA8, 0xA7, 0xB5, 0x36, 0x2E, -0x3D, 0x32, 0x3F, 0xBD, 0xB1, 0xC1, 0x39, 0x2E, 0x42, 0xE8, 0xCA, 0xC3, 0xDB, 0x5F, 0x47, 0x39, -0x47, 0xD5, 0xCB, 0xD1, 0x70, 0x4B, 0x4D, 0xF0, 0xC1, 0xB3, 0xAE, 0xB4, 0xBE, 0xB8, 0xB8, 0x34, -0x1C, 0x27, 0xB6, 0xA3, 0xAC, 0x4F, 0x2C, 0x32, 0x3B, 0x3E, 0xEB, 0xB9, 0xBB, 0x45, 0x2E, 0x39, -0x60, 0xC3, 0xBB, 0xD8, 0x4D, 0x45, 0x41, 0x57, 0xD6, 0xDE, 0x79, 0x5D, 0x50, 0x5B, 0xDB, 0xBE, -0xB4, 0xB3, 0xBC, 0xC4, 0xB6, 0xAC, 0xC2, 0x26, 0x1A, 0x30, 0xAB, 0xA9, 0xBB, 0x61, 0x33, 0x30, -0x36, 0x39, 0xCF, 0xB9, 0x64, 0x41, 0x42, 0x3E, 0x5E, 0xD3, 0xCF, 0xC4, 0xEC, 0x47, 0x56, 0x5C, -0x50, 0x57, 0x62, 0xDF, 0xD2, 0xD3, 0xC4, 0xB6, 0xB3, 0xBA, 0xB8, 0xAD, 0xAF, 0x3C, 0x1C, 0x1E, -0xCB, 0xA1, 0xA9, 0xFE, 0x2D, 0x2C, 0x39, 0x3D, 0x64, 0xBE, 0xD5, 0x4B, 0x42, 0x3A, 0x40, 0xEC, -0xC4, 0xBE, 0xD1, 0x4A, 0x3E, 0x47, 0x62, 0xE4, 0xD7, 0xD6, 0xDC, 0xDE, 0xCD, 0xB6, 0xAD, 0xAE, -0xAC, 0xAD, 0xE6, 0x22, 0x19, 0x2E, 0xA6, 0x9E, 0xB2, 0x39, 0x28, 0x2D, 0x39, 0x4E, 0xC2, 0xBE, -0x5B, 0x3E, 0x3B, 0x3C, 0x58, 0xC6, 0xBD, 0xC7, 0x60, 0x3F, 0x3F, 0x51, 0xED, 0xCF, 0xC8, 0xD1, -0xE0, 0xD9, 0xBC, 0xAC, 0xAB, 0xAB, 0xA9, 0xC2, 0x25, 0x17, 0x22, 0xAE, 0x9D, 0xA9, 0x47, 0x26, -0x29, 0x34, 0x47, 0xC5, 0xBB, 0x77, 0x3F, 0x3A, 0x38, 0x4E, 0xC6, 0xBC, 0xBE, 0xE6, 0x3D, 0x39, -0x43, 0x67, 0xCE, 0xC7, 0xD6, 0xED, 0xD9, 0xBD, 0xAB, 0xAB, 0xAE, 0xA9, 0xB4, 0x30, 0x1A, 0x1B, -0xD5, 0x9D, 0xA3, 0xDA, 0x2A, 0x26, 0x31, 0x3C, 0x6C, 0xBF, 0xCE, 0x5E, 0x42, 0x32, 0x3C, 0xD6, -0xBC, 0xB8, 0xCC, 0x3F, 0x37, 0x3B, 0x51, 0xCC, 0xC1, 0xCC, 0xE2, 0xDE, 0xBD, 0xAC, 0xAA, 0xA9, -0xA7, 0xC5, 0x26, 0x18, 0x1F, 0xB7, 0x9D, 0xA6, 0xDF, 0x2B, 0x26, 0x2D, 0x37, 0xEB, 0xBA, 0xCB, -0x5E, 0x3C, 0x30, 0x44, 0xC9, 0xBB, 0xB8, 0xCF, 0x3D, 0x34, 0x3B, 0x5F, 0xC1, 0xBE, 0xCE, 0xD6, -0xCE, 0xB8, 0xAB, 0xAA, 0xA4, 0xA8, 0x42, 0x1C, 0x18, 0x31, 0xA4, 0x9F, 0xAF, 0x48, 0x29, 0x28, -0x2C, 0x37, 0xCC, 0xBA, 0xC7, 0x57, 0x30, 0x31, 0x5A, 0xC6, 0xB6, 0xB7, 0x5F, 0x38, 0x34, 0x3D, -0xE2, 0xBD, 0xBD, 0xC7, 0xD2, 0xCA, 0xB2, 0xAB, 0xA7, 0xA2, 0xB3, 0x29, 0x18, 0x1C, 0xD2, 0x9F, -0xA5, 0xBC, 0x3A, 0x2A, 0x29, 0x2A, 0x3E, 0xC3, 0xBB, 0xC1, 0x43, 0x2D, 0x3A, 0xF5, 0xC1, 0xB5, -0xBE, 0x4F, 0x36, 0x32, 0x3F, 0xD2, 0xBB, 0xBA, 0xBE, 0xC5, 0xBF, 0xB1, 0xAB, 0xA4, 0xA7, 0x75, -0x1F, 0x18, 0x27, 0xAE, 0xA2, 0xAD, 0xD6, 0x36, 0x2B, 0x28, 0x2C, 0x4E, 0xC2, 0xB8, 0xC9, 0x35, -0x2F, 0x42, 0x71, 0xBA, 0xB4, 0xD2, 0x44, 0x34, 0x34, 0x4C, 0xCD, 0xBE, 0xBB, 0xB9, 0xBC, 0xB3, -0xAE, 0xAB, 0xA3, 0xB0, 0x2C, 0x1A, 0x1C, 0x5F, 0xA4, 0xA9, 0xBA, 0x6F, 0x34, 0x28, 0x26, 0x32, -0x77, 0xB9, 0xB3, 0x5E, 0x32, 0x38, 0x40, 0xD8, 0xB6, 0xBE, 0xF4, 0x40, 0x34, 0x3A, 0x5B, 0xC7, -0xB7, 0xB4, 0xB7, 0xB4, 0xB2, 0xAE, 0xA5, 0xAB, 0x3B, 0x1D, 0x1B, 0x34, 0xAE, 0xAA, 0xB4, 0xC7, -0x42, 0x2C, 0x25, 0x2B, 0x43, 0xCF, 0xB5, 0xBE, 0x3F, 0x38, 0x3D, 0x4D, 0xC4, 0xBD, 0xCF, 0x5D, -0x3E, 0x38, 0x43, 0xE4, 0xBF, 0xB4, 0xAF, 0xB0, 0xAF, 0xAE, 0xA8, 0xA7, 0xF6, 0x21, 0x1A, 0x27, -0xBA, 0xAC, 0xB5, 0xB9, 0xE2, 0x36, 0x26, 0x25, 0x33, 0x4E, 0xBA, 0xB2, 0x64, 0x41, 0x3F, 0x3D, -0xE6, 0xC6, 0xCD, 0xD4, 0x50, 0x3D, 0x3C, 0x48, 0xD4, 0xB9, 0xAF, 0xAD, 0xAE, 0xAE, 0xAA, 0xA7, -0xC0, 0x2A, 0x1B, 0x1F, 0x65, 0xAF, 0xB5, 0xB5, 0xBF, 0x4C, 0x2C, 0x23, 0x2A, 0x37, 0xE6, 0xAF, -0xBC, 0x5B, 0x4B, 0x3B, 0x49, 0xCF, 0xCE, 0xD0, 0xDE, 0x4F, 0x40, 0x3F, 0x57, 0xC7, 0xB3, 0xAA, -0xA9, 0xAC, 0xAE, 0xA9, 0xAE, 0x3B, 0x1E, 0x1E, 0x37, 0xBA, 0xB7, 0xB7, 0xB5, 0xC9, 0x3A, 0x26, -0x27, 0x2E, 0x37, 0xC1, 0xB1, 0xCA, 0xE8, 0x49, 0x3E, 0x62, 0x78, 0xEF, 0xD7, 0x69, 0x4F, 0x44, -0x48, 0xFB, 0xC5, 0xAF, 0xA9, 0xA8, 0xAA, 0xAB, 0xAA, 0xCF, 0x26, 0x1C, 0x22, 0x4A, 0xBD, 0xBB, -0xAF, 0xB1, 0xD7, 0x31, 0x25, 0x27, 0x28, 0x38, 0xB9, 0xB6, 0xC3, 0xCB, 0x4B, 0x45, 0x4E, 0x47, -0x62, 0xE0, 0xF9, 0x5C, 0x4C, 0x5C, 0xE4, 0xBF, 0xAD, 0xA8, 0xA5, 0xA7, 0xA9, 0xB2, 0x36, 0x20, -0x1E, 0x29, 0x56, 0xCC, 0xB8, 0xAC, 0xB5, 0xFA, 0x2F, 0x28, 0x25, 0x25, 0x45, 0xBE, 0xBF, 0xB8, -0xC5, 0x5F, 0x54, 0x41, 0x43, 0x4F, 0x56, 0x75, 0x70, 0xFE, 0xE2, 0xD3, 0xB9, 0xAD, 0xA8, 0xA6, -0xA6, 0xA8, 0xCA, 0x2D, 0x22, 0x21, 0x2E, 0x47, 0x77, 0xB4, 0xAD, 0xB7, 0xDC, 0x39, 0x2D, 0x24, -0x28, 0x40, 0x5A, 0xC7, 0xB5, 0xBD, 0xC0, 0xDB, 0x4E, 0x4C, 0x40, 0x44, 0x4E, 0x56, 0xDA, 0xCC, -0xC0, 0xB1, 0xAC, 0xA7, 0xA5, 0xA6, 0xAD, 0x4F, 0x2A, 0x23, 0x25, 0x2F, 0x39, 0x6C, 0xB6, 0xB1, -0xB7, 0xD6, 0x43, 0x2F, 0x26, 0x2C, 0x35, 0x3A, 0xDF, 0xC7, 0xBE, 0xBA, 0xCB, 0xD4, 0x6E, 0x48, -0x44, 0x3E, 0x4A, 0x6C, 0xD6, 0xB9, 0xB0, 0xA8, 0xA4, 0xA5, 0xA4, 0xB5, 0x42, 0x2C, 0x23, 0x27, -0x2B, 0x2E, 0x55, 0xC2, 0xB9, 0xB6, 0xC5, 0xF4, 0x3C, 0x32, 0x34, 0x2F, 0x37, 0x4A, 0x5B, 0xCF, -0xC9, 0xC6, 0xC0, 0xCA, 0xCF, 0xF4, 0x52, 0x51, 0x4E, 0xF8, 0xC4, 0xB7, 0xAD, 0xA9, 0xA6, 0xA7, -0xB8, 0x5D, 0x38, 0x2F, 0x2E, 0x2B, 0x2C, 0x35, 0x42, 0x5D, 0x7D, 0xE3, 0xDF, 0x6F, 0x73, 0x5A, -0x4D, 0x55, 0x4F, 0x4F, 0x58, 0x5C, 0x6E, 0xFA, 0xE2, 0xD4, 0xD4, 0xD6, 0xD9, 0xD6, 0xCC, 0xC6, -0xBF, 0xBB, 0xB8, 0xB6, 0xBC, 0xC9, 0xDC, 0x60, 0x59, 0x53, 0x4A, 0x49, 0x46, 0x45, 0x46, 0x43, -0x45, 0x45, 0x49, 0x56, 0x5A, 0x5D, 0x6E, 0x72, 0x7C, 0x74, 0x6D, 0x7A, 0x7A, 0xFC, 0xED, 0xE8, -0xEB, 0xFF, 0xFD, 0xEC, 0xDE, 0xD2, 0xCC, 0xC7, 0xC5, 0xCA, 0xD2, 0xD9, 0xDB, 0xD4, 0xCF, 0xD1, -0xD9, 0xE7, 0xFC, 0x6E, 0x60, 0x57, 0x50, 0x4C, 0x49, 0x47, 0x46, 0x45, 0x47, 0x4A, 0x4D, 0x4F, -0x4F, 0x53, 0x5A, 0x5F, 0x62, 0x60, 0x62, 0x6B, 0x77, 0xEC, 0xD8, 0xCC, 0xC7, 0xC7, 0xCB, 0xCF, -0xD3, 0xD3, 0xCE, 0xCB, 0xCA, 0xCC, 0xCF, 0xD4, 0xD9, 0xDF, 0xEB, 0xFE, 0x6A, 0x58, 0x4D, 0x46, -0x43, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x52, 0x54, 0x55, 0x54, 0x57, 0x5C, 0x61, 0x72, 0xE5, -0xD4, 0xCC, 0xCB, 0xCD, 0xD0, 0xD4, 0xD3, 0xD1, 0xD0, 0xCF, 0xD1, 0xD2, 0xD4, 0xD6, 0xD9, 0xDB, -0xDB, 0xDD, 0xEC, 0x6B, 0x59, 0x53, 0x54, 0x57, 0x59, 0x58, 0x57, 0x5A, 0x5C, 0x5A, 0x55, 0x52, -0x52, 0x51, 0x51, 0x57, 0x61, 0xFC, 0xE3, 0xDF, 0xE0, 0xE4, 0xE3, 0xE0, 0xDF, 0xDE, 0xDD, 0xDE, -0xDD, 0xDF, 0xE0, 0xDF, 0xDC, 0xD9, 0xDC, 0xE7, 0x7E, 0x6B, 0x6D, 0x7C, 0xEE, 0xE6, 0xE7, 0xEA, -0xEC, 0xF5, 0x7A, 0x6A, 0x61, 0x5A, 0x51, 0x4F, 0x50, 0x55, 0x5D, 0x62, 0x63, 0x60, 0x5F, 0x61, -0x65, 0x68, 0x6D, 0x75, 0xFA, 0xF1, 0xF1, 0xEE, 0xE6, 0xDD, 0xDB, 0xDE, 0xEB, 0x79, 0x6B, 0x6F, -0xF7, 0xE2, 0xDC, 0xDB, 0xDA, 0xDA, 0xDC, 0xDD, 0xDE, 0xE0, 0xF1, 0x6B, 0x5F, 0x5F, 0x66, 0x6B, -0x6A, 0x65, 0x5E, 0x5B, 0x5A, 0x59, 0x5A, 0x5C, 0x5E, 0x5E, 0x5E, 0x5E, 0x68, 0xFC, 0xE8, 0xE5, -0xEB, 0xFB, 0x71, 0x6D, 0x78, 0xEE, 0xE2, 0xDE, 0xDD, 0xDC, 0xDE, 0xDF, 0xDC, 0xD9, 0xD9, 0xE0, -0xED, 0xF0, 0xEC, 0xE4, 0xE2, 0xE6, 0xEE, 0x7C, 0x6D, 0x65, 0x5E, 0x5E, 0x62, 0x64, 0x61, 0x5C, -0x5B, 0x5F, 0x70, 0xF7, 0xF8, 0x78, 0x6B, 0x65, 0x61, 0x69, 0xFA, 0xE9, 0xE5, 0xE2, 0xE5, 0xEA, -0xED, 0xEB, 0xE5, 0xE5, 0xED, 0xF9, 0xFC, 0xEF, 0xE3, 0xDE, 0xDE, 0xE1, 0xE9, 0xF4, 0x74, 0x6A, -0x6B, 0x6F, 0x6F, 0x67, 0x5E, 0x5C, 0x5F, 0x6A, 0x6F, 0x6C, 0x66, 0x5F, 0x5D, 0x5E, 0x64, 0x6F, -0xFE, 0xEF, 0xEC, 0xF2, 0xFD, 0x7E, 0xFB, 0xF6, 0xFF, 0x6F, 0x6A, 0x6A, 0x74, 0xF4, 0xE9, 0xE4, -0xE7, 0xEE, 0x7D, 0x6A, 0x69, 0x6F, 0xFE, 0xF7, 0x7B, 0x6B, 0x68, 0x6E, 0x79, 0x78, 0x6F, 0x69, -0x62, 0x5F, 0x60, 0x66, 0x72, 0xF6, 0xEB, 0xEC, 0xF3, 0xFC, 0xFA, 0xF2, 0xF2, 0xFE, 0x70, 0x69, -0x69, 0x71, 0xF7, 0xE8, 0xE3, 0xE4, 0xED, 0x76, 0x69, 0x6A, 0x6F, 0x7E, 0xFB, 0x7D, 0x75, 0x73, -0x7C, 0xFB, 0xFC, 0x7D, 0x73, 0x6D, 0x6A, 0x69, 0x6F, 0xFD, 0xEC, 0xE8, 0xED, 0xF6, 0xF7, 0xF1, -0xED, 0xEF, 0xF6, 0x7E, 0x76, 0x78, 0xFB, 0xEC, 0xE2, 0xDE, 0xE0, 0xEB, 0xFE, 0x73, 0x73, 0x7E, -0xFA, 0xFC, 0xFF, 0x7B, 0x7D, 0xFC, 0xFE, 0x7B, 0x74, 0x6F, 0x6D, 0x6C, 0x6E, 0x77, 0xFA, 0xEE, -0xED, 0xF6, 0x7A, 0x73, 0x77, 0x7B, 0x7C, 0x78, 0x6E, 0x6B, 0x6F, 0x7C, 0xEF, 0xE7, 0xE2, 0xE6, -0xF2, 0x7A, 0x71, 0x75, 0x7D, 0xFE, 0xFD, 0xFD, 0xFB, 0xF5, 0xF7, 0xFE, 0x77, 0x6E, 0x6A, 0x67, -0x67, 0x6C, 0x76, 0xF7, 0xEE, 0xEF, 0xFA, 0x7B, 0x78, 0x7B, 0xFF, 0x7F, 0x78, 0x6F, 0x6C, 0x6C, -0x72, 0xFE, 0xEF, 0xEC, 0xEF, 0xFD, 0x76, 0x70, 0x71, 0x75, 0x7A, 0x7E, 0x7E, 0xFF, 0xFC, 0xFD, -0x7C, 0x73, 0x6D, 0x69, 0x65, 0x63, 0x65, 0x6B, 0x79, 0xFB, 0xFD, 0x78, 0x70, 0x70, 0x74, 0x79, -0x7C, 0x7B, 0x79, 0x77, 0x78, 0x7F, 0xF4, 0xED, 0xED, 0xF2, 0xFB, 0x7A, 0x74, 0x74, 0x76, 0x7A, -0x7F, 0xFB, 0xF5, 0xF4, 0xF6, 0xFA, 0x7D, 0x75, 0x6E, 0x6B, 0x6B, 0x6F, 0x7D, 0xF3, 0xEF, 0xF4, -0x7E, 0x78, 0x7A, 0xFF, 0xFA, 0xF7, 0xF5, 0xF4, 0xF4, 0xF1, 0xED, 0xE9, 0xE6, 0xE8, 0xEE, 0xF8, -0x7F, 0x7C, 0x7A, 0x7A, 0x7B, 0x7D, 0xFD, 0xF8, 0xF8, 0xF9, 0xFC, 0x7D, 0x74, 0x6C, 0x69, 0x69, -0x6C, 0x76, 0xFA, 0xF6, 0xFD, 0x78, 0x71, 0x6F, 0x6F, 0x6F, 0x71, 0x74, 0x77, 0x7C, 0xFD, 0xF5, -0xEC, 0xE9, 0xEA, 0xEF, 0xFB, 0x7A, 0x74, 0x73, 0x73, 0x73, 0x73, 0x78, 0x7E, 0xFE, 0xFD, 0xFE, -0x7B, 0x74, 0x6D, 0x68, 0x66, 0x69, 0x6F, 0x7B, 0x7F, 0x7C, 0x77, 0x73, 0x72, 0x6F, 0x6D, 0x6D, -0x6D, 0x6D, 0x6E, 0x72, 0x7A, 0xFA, 0xF1, 0xEE, 0xEF, 0xF6, 0xFD, 0x7D, 0x7B, 0x7A, 0x78, 0x78, -0x79, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7D, 0x77, 0x70, 0x6E, 0x71, 0x79, 0x7E, 0x7E, 0x7E, 0x7D, -0x7E, 0x7D, 0x7C, 0x7A, 0x78, 0x76, 0x74, 0x73, 0x75, 0x7A, 0xFC, 0xF6, 0xF4, 0xF4, 0xF6, 0xF6, -0xF6, 0xF9, 0xF9, 0xF7, 0xF5, 0xF3, 0xF1, 0xF1, 0xF3, 0xF5, 0xF7, 0xFA, 0x7B, 0x6F, 0x6D, 0x6D, -0x6F, 0x6F, 0x6E, 0x6E, 0x70, 0x70, 0x71, 0x74, 0x78, 0x7C, 0x7D, 0x7E, 0x7D, 0xFF, 0xFB, 0xF7, -0xF4, 0xF4, 0xF6, 0xF8, 0xFB, 0xFD, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x7F, 0xFF, 0xFC, 0xF8, -0xF9, 0xFD, 0x7D, 0x7C, 0x7F, 0xFE, 0x7E, 0x7C, 0x7A, 0x79, 0x77, 0x75, 0x75, 0x76, 0x79, 0x76, -0x71, 0x6F, 0x6F, 0x71, 0x76, 0x78, 0x78, 0x78, 0x7B, 0x7D, 0x7A, 0x77, 0x76, 0x76, 0x78, 0x79, -0x78, 0x79, 0x7C, 0xFD, 0xF4, 0xF1, 0xF2, 0xF5, 0xF9, 0xFB, 0x7F, 0x79, 0x76, 0x76, 0x78, 0x77, -0x77, 0x7B, 0xFF, 0xFA, 0xF6, 0xF6, 0xF7, 0xF6, 0xF6, 0xF7, 0xF9, 0xFA, 0xF9, 0xF6, 0xF6, 0xF9, -0xFB, 0xFE, 0x7F, 0x7E, 0x7C, 0x79, 0x7A, 0xFE, 0xF5, 0xEF, 0xEE, 0xEF, 0xF1, 0xF3, 0xF4, 0xFB, -0x79, 0x73, 0x72, 0x6E, 0x6B, 0x68, 0x68, 0x6C, 0x73, 0x7A, 0x7C, 0x7B, 0x7B, 0x7D, 0x7D, 0xFD, -0xF6, 0xF0, 0xEE, 0xEF, 0xF6, 0xFB, 0xFF, 0x7D, 0x7F, 0x7D, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, 0x7B, -0x7A, 0xFF, 0xFD, 0xFF, 0x7E, 0x7A, 0x75, 0x74, 0x71, 0x6F, 0x6F, 0x70, 0x77, 0x7A, 0x78, 0x75, -0x72, 0x75, 0x73, 0x6F, 0x6F, 0x70, 0x75, 0x77, 0x76, 0x78, 0x75, 0x78, 0x7B, 0x7B, 0x7F, 0x7B, -0x7E, 0x7F, 0x7E, 0x7E, 0x77, 0x79, 0x73, 0x71, 0x71, 0x6B, 0x6F, 0x60, 0x5B, 0x6F, 0x72, 0x69, -0x71, 0x72, 0x7E, 0xFC, 0xF7, 0xEE, 0xF4, 0xFB, 0x7E, 0x7E, 0xFC, 0xF9, 0xFD, 0x7F, 0xFC, 0x72, -0x78, 0x64, 0x53, 0x71, 0xDF, 0xF0, 0xEB, 0xE9, 0xF0, 0x7C, 0x7B, 0xEA, 0xF6, 0x7E, 0x79, 0x6C, -0x79, 0x5F, 0x5B, 0xEC, 0xE2, 0xEF, 0xED, 0xF1, 0xF7, 0xFE, 0x7C, 0xFE, 0x75, 0x6E, 0x75, 0x79, -0x7A, 0x72, 0x6E, 0x75, 0x7A, 0x7B, 0x77, 0x77, 0xFE, 0xF8, 0xF6, 0xF5, 0xEB, 0xE9, 0xEC, 0xE8, -0xEE, 0xEC, 0xED, 0x77, 0xFD, 0x61, 0x51, 0x66, 0xE7, 0xDC, 0xD8, 0xD3, 0xD3, 0x6C, 0x52, 0x63, -0x6F, 0x73, 0x7F, 0xFB, 0xF4, 0x79, 0x7B, 0xF1, 0xED, 0xEC, 0xFA, 0x76, 0x7A, 0x74, 0x6B, 0x6B, -0x75, 0x7C, 0x7C, 0x79, 0xFE, 0xF6, 0xFE, 0x7A, 0x71, 0x75, 0xFF, 0x70, 0x6D, 0x6E, 0x69, 0x66, -0x5E, 0x62, 0xFA, 0xEA, 0xE5, 0xE0, 0xE1, 0xE5, 0xEE, 0xF1, 0xE5, 0xE4, 0xEA, 0xE8, 0xE7, 0xE7, -0xE3, 0xE7, 0xE8, 0xE8, 0xEF, 0xF7, 0x78, 0x6E, 0x6D, 0x60, 0x5A, 0x57, 0x54, 0x53, 0x51, 0x50, -0x52, 0x54, 0x57, 0x59, 0x5D, 0x67, 0x70, 0x7B, 0xF5, 0xE5, 0xDA, 0xD5, 0xD2, 0xCD, 0xC4, 0xBE, -0xBB, 0xB7, 0xB5, 0xBB, 0xCB, 0xDE, 0xE9, 0x56, 0x3F, 0x3A, 0x39, 0x3B, 0x3B, 0x3A, 0x3F, 0x4E, -0x5D, 0x69, 0xF2, 0xDD, 0xD9, 0xE2, 0xF3, 0xF4, 0x72, 0x5C, 0x53, 0x4E, 0x4D, 0x4E, 0x4E, 0x4F, -0x58, 0x7E, 0xDA, 0xCB, 0xBF, 0xBA, 0xB7, 0xB4, 0xB0, 0xAE, 0xB2, 0xBE, 0xD7, 0x70, 0x4E, 0x3B, -0x32, 0x30, 0x33, 0x35, 0x35, 0x3A, 0x49, 0x5F, 0x7E, 0xEF, 0xE1, 0xD2, 0xD1, 0xE6, 0xEF, 0xF7, -0x5F, 0x4F, 0x48, 0x48, 0x4B, 0x48, 0x4B, 0x55, 0x72, 0xE1, 0xD2, 0xC2, 0xBA, 0xB6, 0xB3, 0xB0, -0xAD, 0xAE, 0xBA, 0xD0, 0x64, 0x4C, 0x3C, 0x30, 0x2E, 0x2F, 0x33, 0x35, 0x3A, 0x47, 0x6C, 0xE6, -0xEB, 0xD0, 0xC6, 0xCC, 0xD6, 0xE3, 0xEF, 0x69, 0x4E, 0x4B, 0x4B, 0x47, 0x46, 0x48, 0x4F, 0x61, -0xFE, 0xD1, 0xBE, 0xB8, 0xB5, 0xB2, 0xAF, 0xAD, 0xAE, 0xBA, 0xCD, 0x5F, 0x47, 0x3B, 0x2F, 0x2D, -0x2E, 0x30, 0x35, 0x3B, 0x47, 0x6C, 0xF4, 0xE2, 0xCB, 0xC7, 0xCD, 0xD7, 0xDD, 0xE3, 0x6E, 0x4E, -0x4A, 0x4B, 0x48, 0x47, 0x4A, 0x5B, 0xE4, 0xD3, 0xC0, 0xB7, 0xB3, 0xB0, 0xAF, 0xAC, 0xAC, 0xB3, -0xC4, 0x7D, 0x4C, 0x3E, 0x30, 0x2C, 0x2E, 0x31, 0x35, 0x3A, 0x45, 0x62, 0xFA, 0xEB, 0xCE, 0xCA, -0xD2, 0xDA, 0xE7, 0xE9, 0x6D, 0x4C, 0x49, 0x49, 0x47, 0x48, 0x4C, 0x68, 0xD7, 0xCA, 0xBC, 0xB5, -0xB2, 0xB0, 0xAE, 0xAC, 0xAE, 0xB9, 0xCE, 0x5D, 0x45, 0x39, 0x2F, 0x2D, 0x2E, 0x31, 0x36, 0x3B, -0x47, 0x5C, 0x6E, 0xE4, 0xD3, 0xD3, 0xD4, 0xDA, 0xDE, 0xEB, 0x5B, 0x4D, 0x4A, 0x47, 0x48, 0x49, -0x54, 0xEB, 0xCE, 0xBE, 0xB8, 0xB7, 0xB3, 0xB3, 0xB5, 0xB3, 0xBB, 0xC9, 0xC8, 0xED, 0x47, 0x42, -0x3A, 0x36, 0x37, 0x34, 0x39, 0x3E, 0x3F, 0x47, 0x4E, 0x65, 0xDE, 0xEC, 0xDC, 0xCE, 0xD9, 0xEB, -0x5F, 0x54, 0x51, 0x47, 0x46, 0x4C, 0x58, 0xFC, 0xD8, 0xC4, 0xBB, 0xBA, 0xB8, 0xB5, 0xB2, 0xAF, -0xB0, 0xB6, 0xBD, 0xCC, 0x66, 0x47, 0x3B, 0x34, 0x32, 0x32, 0x34, 0x38, 0x3B, 0x3F, 0x4B, 0x58, -0x69, 0xE9, 0xD9, 0xD4, 0xDB, 0x77, 0x5C, 0x59, 0x4F, 0x4A, 0x4B, 0x51, 0x69, 0xE0, 0xCE, 0xC2, -0xBD, 0xBC, 0xBA, 0xB8, 0xB7, 0xB4, 0xB2, 0xB7, 0xC3, 0xCE, 0xD4, 0x67, 0x44, 0x3D, 0x3C, 0x3B, -0x38, 0x38, 0x3B, 0x3F, 0x42, 0x46, 0x4E, 0x5F, 0x6A, 0x6B, 0xFD, 0xF7, 0x70, 0x5C, 0x54, 0x58, -0x58, 0x53, 0x5B, 0x73, 0xE5, 0xD2, 0xCA, 0xC6, 0xC2, 0xBF, 0xBE, 0xBE, 0xBD, 0xBB, 0xBC, 0xC3, -0xCA, 0xD0, 0xE2, 0x5F, 0x4E, 0x4A, 0x44, 0x3E, 0x3D, 0x3D, 0x3F, 0x42, 0x44, 0x4B, 0x55, 0x57, -0x5F, 0x69, 0x68, 0x63, 0x58, 0x56, 0x5B, 0x56, 0x57, 0x66, 0x79, 0xE8, 0xD9, 0xCF, 0xCB, 0xCB, -0xC8, 0xC4, 0xC5, 0xC6, 0xC9, 0xC9, 0xC6, 0xCD, 0xD4, 0xD8, 0xE2, 0xF1, 0x66, 0x5C, 0x57, 0x4C, -0x49, 0x49, 0x48, 0x49, 0x49, 0x4B, 0x51, 0x51, 0x4F, 0x53, 0x56, 0x58, 0x58, 0x54, 0x58, 0x5F, -0x5E, 0x5B, 0x63, 0xF2, 0xE5, 0xE5, 0xD7, 0xCF, 0xD2, 0xD6, 0xD1, 0xCF, 0xD6, 0xD6, 0xD2, 0xCF, -0xCE, 0xD0, 0xD8, 0xDB, 0xDB, 0xE5, 0x6D, 0x67, 0x65, 0x5C, 0x58, 0x54, 0x59, 0x5D, 0x57, 0x57, -0x5B, 0x5B, 0x59, 0x56, 0x57, 0x5A, 0x56, 0x52, 0x5A, 0x5C, 0x5D, 0x7D, 0xF1, 0xF3, 0xE7, 0xE3, -0xDF, 0xDB, 0xDA, 0xE7, 0xEC, 0xDE, 0xE5, 0xE9, 0xE6, 0xE4, 0xD6, 0xDF, 0xF8, 0xDE, 0xE9, 0x7C, -0xEE, 0xFB, 0x7A, 0x6B, 0x65, 0xFF, 0x79, 0x69, 0x6B, 0x63, 0x65, 0x62, 0x56, 0x5A, 0x60, 0x55, -0x58, 0x62, 0x5E, 0x64, 0x79, 0x7C, 0x78, 0xED, 0xE1, 0xEC, 0xF0, 0xE4, 0xF0, 0x6E, 0x7C, 0xED, -0xDD, 0xDB, 0xFE, 0x73, 0xED, 0xF0, 0xFF, 0xF9, 0xDE, 0xDC, 0x76, 0x6E, 0xFB, 0x7B, 0xFB, 0x6F, -0x6F, 0xEF, 0x7B, 0x6E, 0xF8, 0xE9, 0x7E, 0x5B, 0x5F, 0xF3, 0xFD, 0x67, 0x74, 0xEA, 0xEF, 0x77, -0x6D, 0x7C, 0xE8, 0xE2, 0xEE, 0xFB, 0xE6, 0xED, 0x66, 0x6C, 0x7B, 0x7C, 0xF4, 0xEF, 0xE2, 0xE6, -0x72, 0x70, 0xF0, 0xEE, 0x6E, 0x6B, 0xFB, 0xF5, 0xF6, 0x71, 0x6A, 0x6C, 0x65, 0x67, 0x5F, 0x5D, -0x6B, 0x62, 0x65, 0xFD, 0xED, 0xE9, 0xF2, 0xED, 0xE3, 0xE7, 0xE4, 0xE8, 0xED, 0xEF, 0x66, 0x5E, -0x79, 0x7E, 0x78, 0xF5, 0xFA, 0x79, 0x7A, 0xF5, 0xE7, 0xE2, 0xE4, 0xF2, 0xFF, 0x73, 0x66, 0x6B, -0x70, 0xFD, 0xEF, 0x6F, 0x67, 0x5F, 0x5C, 0x6B, 0x6E, 0x78, 0x77, 0x6D, 0xF1, 0xF4, 0xF8, 0xE5, -0xE9, 0xEB, 0xEC, 0xEF, 0xEF, 0xF0, 0xE7, 0xED, 0x6B, 0x6F, 0xF0, 0xFF, 0x6A, 0x6F, 0x7E, 0x6F, -0x69, 0xED, 0xDF, 0xFF, 0x7C, 0xEB, 0xF3, 0xF8, 0x7E, 0x7A, 0xFB, 0x6E, 0x5E, 0x60, 0x70, 0x7A, -0xFC, 0xF4, 0xFF, 0xEF, 0xF5, 0x72, 0xF1, 0xEC, 0xEA, 0xE1, 0xE9, 0xED, 0xF2, 0xFC, 0xE7, 0xF0, -0x68, 0x76, 0xEF, 0xF4, 0xF9, 0x77, 0x79, 0xFB, 0xFC, 0xF6, 0xEE, 0xE9, 0xF2, 0x6D, 0x70, 0x6F, -0x6F, 0xFE, 0x6A, 0x69, 0x7F, 0x6F, 0x6F, 0x79, 0x78, 0x77, 0x6E, 0xFE, 0xEE, 0xF0, 0xEF, 0x7E, -0xF4, 0xE9, 0xF8, 0xFD, 0x7C, 0x77, 0x7D, 0x78, 0x7D, 0xFE, 0x79, 0x72, 0x6E, 0x72, 0x7B, 0x73, -0x6B, 0x74, 0x76, 0x6C, 0x72, 0x70, 0x75, 0x6D, 0x5A, 0x5C, 0x6A, 0x77, 0xFE, 0x71, 0x7A, 0xF2, -0xE9, 0xE7, 0xEA, 0xDE, 0xE6, 0xF5, 0xEB, 0xF5, 0xEE, 0xEA, 0xF3, 0xEE, 0xF4, 0xEE, 0xF5, 0x73, -0xF4, 0xF6, 0x73, 0x7C, 0xEC, 0xE9, 0x72, 0x67, 0x6C, 0x70, 0x6E, 0x69, 0x6F, 0x68, 0x5D, 0x64, -0x71, 0x75, 0x6B, 0x6D, 0xF9, 0x79, 0x6F, 0xF4, 0xEA, 0xF5, 0x79, 0x7B, 0x7D, 0xFA, 0xF3, 0xF7, -0xF4, 0xF4, 0xF9, 0x7F, 0x77, 0x7F, 0xF5, 0xFD, 0x71, 0x71, 0x7B, 0x78, 0x7A, 0x73, 0x68, 0x71, -0x7A, 0x6B, 0x6C, 0x6E, 0x69, 0x6E, 0x71, 0x6C, 0x69, 0x6C, 0x7A, 0x7D, 0xF6, 0xEB, 0xF4, 0xF2, -0xF4, 0x78, 0xF8, 0xF1, 0xF6, 0xFE, 0x71, 0x75, 0x7C, 0x7B, 0x7D, 0x7E, 0xFB, 0x7E, 0x77, 0x76, -0xFF, 0xF7, 0x79, 0x70, 0x6E, 0x6C, 0x71, 0x6F, 0x6C, 0x6A, 0x70, 0x6E, 0x5F, 0x65, 0x6C, 0x66, -0x6D, 0x7A, 0xF1, 0x78, 0x6B, 0xED, 0xFB, 0x6C, 0x7C, 0x74, 0xF1, 0xEC, 0x76, 0xF9, 0xF8, 0x75, -0x71, 0x7A, 0xEC, 0xFA, 0x6C, 0x76, 0x7C, 0x7F, 0x6E, 0x6A, 0x76, 0x6C, 0x76, 0xF2, 0x7C, 0x76, -0x66, 0x62, 0x7D, 0xF9, 0x78, 0x69, 0x6A, 0xF8, 0x78, 0x6D, 0xFC, 0xF8, 0xF3, 0xF7, 0x7D, 0xEF, -0xF1, 0x7C, 0xF1, 0xE9, 0xEB, 0xEE, 0xF3, 0xFD, 0x73, 0x7D, 0xFA, 0x74, 0x78, 0x7A, 0x6E, 0x70, -0x76, 0x73, 0x6F, 0x6D, 0x6C, 0x68, 0x6D, 0x7D, 0x6E, 0x64, 0x6D, 0xFC, 0xFA, 0x71, 0x6F, 0xFF, -0x70, 0x70, 0xF0, 0xEE, 0xEF, 0xEB, 0xEC, 0xEC, 0xED, 0xF0, 0xEE, 0xE9, 0xEB, 0xF8, 0xFB, 0xED, -0xE9, 0xF7, 0xFF, 0xED, 0xEE, 0xEF, 0xF3, 0x6E, 0x70, 0xF3, 0xFF, 0x6F, 0x6F, 0x6F, 0x79, 0x7C, -0x79, 0x7E, 0x77, 0x6E, 0x6E, 0x7B, 0x7C, 0x6B, 0x72, 0xF6, 0xFF, 0x7D, 0x7D, 0x72, 0x78, 0xFB, -0xFD, 0xFB, 0xFA, 0x7D, 0x7A, 0x6D, 0x68, 0x74, 0x75, 0xF1, 0xE7, 0x7B, 0x76, 0x6B, 0x66, 0xFD, -0x73, 0x68, 0x6B, 0x73, 0x7C, 0x62, 0x64, 0x70, 0x64, 0x6D, 0x79, 0x70, 0x70, 0x74, 0xF8, 0x7C, -0x7C, 0xF8, 0x6B, 0x70, 0xF9, 0x7D, 0xF1, 0xF0, 0xFF, 0x7F, 0xF8, 0xED, 0xF2, 0xFC, 0xFF, 0x76, -0x7B, 0x7C, 0xFE, 0xFE, 0x6C, 0x7C, 0xED, 0x79, 0x77, 0x71, 0x69, 0x76, 0x7A, 0x76, 0x7A, 0x6F, -0x6E, 0x6E, 0x73, 0xFF, 0x78, 0x76, 0x7F, 0x7A, 0x6F, 0x78, 0xFE, 0x77, 0xFB, 0xEE, 0xEE, 0xF1, -0x7A, 0xFB, 0xEE, 0x75, 0x70, 0xFE, 0x7D, 0x72, 0x6D, 0x6F, 0x7B, 0xFB, 0xF9, 0x7C, 0x7E, 0x7A, -0x70, 0x76, 0x7B, 0xF7, 0xF5, 0x70, 0x74, 0xFF, 0x78, 0xFA, 0xFC, 0x79, 0x78, 0x6D, 0x7A, 0xFA, -0x77, 0xFC, 0xF4, 0xF1, 0xF1, 0xF7, 0xEB, 0xED, 0xFB, 0xF7, 0xEF, 0xF2, 0x78, 0x79, 0xF0, 0xF3, -0xFC, 0xFC, 0x7E, 0x77, 0xFF, 0xFC, 0x78, 0xF6, 0xF9, 0x6F, 0x7A, 0x78, 0x6F, 0xFF, 0xF9, 0x7E, -0x76, 0x72, 0x76, 0x78, 0x79, 0xFD, 0xFA, 0x7A, 0x7E, 0xF1, 0xFB, 0x7A, 0xF2, 0xEF, 0xFF, 0x74, -0x75, 0x7E, 0xFF, 0xF6, 0xE9, 0xF4, 0x71, 0x74, 0x74, 0x7C, 0xF8, 0x79, 0x75, 0x74, 0x73, 0xF9, -0xFE, 0xFF, 0xFA, 0x6A, 0x6C, 0xFE, 0x70, 0x72, 0x7C, 0x7D, 0x70, 0x67, 0x6F, 0x76, 0x7B, 0xF0, -0x7E, 0x6E, 0x6E, 0x76, 0xF1, 0xF2, 0x7A, 0x72, 0x7B, 0xFD, 0x79, 0x77, 0x6E, 0x6A, 0x6D, 0x6E, -0x74, 0x72, 0x77, 0xF5, 0x7C, 0x6D, 0x6C, 0x6D, 0x70, 0x76, 0xFD, 0xFC, 0xFF, 0xFE, 0xFE, 0xFA, -0x7D, 0x77, 0x74, 0x6F, 0xFB, 0xFA, 0x71, 0xFC, 0xF8, 0xFE, 0xEB, 0xE5, 0xEE, 0x7F, 0xF6, 0xED, -0xFA, 0x78, 0xF6, 0xF1, 0xF8, 0xF6, 0x79, 0x70, 0x7C, 0x75, 0x78, 0xF6, 0xFA, 0xFC, 0xFB, 0x7B, -0x7D, 0xF9, 0xF7, 0xF7, 0x7E, 0x6B, 0x68, 0xFA, 0xE6, 0xEE, 0x7B, 0x7B, 0x79, 0x7E, 0xFB, 0xF2, -0xEA, 0xEE, 0xF1, 0xF6, 0x79, 0xFB, 0xF0, 0xFB, 0xFF, 0xF0, 0xEE, 0xF4, 0xEF, 0xFB, 0x6F, 0x6C, -0x72, 0xFF, 0x71, 0x6C, 0x6A, 0x6D, 0xF4, 0xFB, 0x7C, 0xF3, 0xFA, 0xFC, 0x6F, 0x73, 0xFB, 0x72, -0x7D, 0x78, 0x76, 0xEF, 0xF3, 0x7C, 0x71, 0xFA, 0xF2, 0xFB, 0xEF, 0xF0, 0xEA, 0xFD, 0x6B, 0xED, -0xF2, 0x77, 0x6E, 0x6D, 0xFD, 0x64, 0xF3, 0xDF, 0x5C, 0x61, 0xED, 0xF2, 0x7E, 0x66, 0x73, 0x6E, -0x66, 0xFF, 0x6F, 0x72, 0x70, 0x66, 0xF3, 0xF7, 0x6F, 0x72, 0x75, 0xED, 0xEF, 0xF4, 0xED, 0xEF, -0xEF, 0x6E, 0x6F, 0xF7, 0x72, 0x7E, 0x78, 0x6F, 0xF9, 0x77, 0xFC, 0xF4, 0x6C, 0x66, 0x6A, 0x76, -0x65, 0x5C, 0x6D, 0xFF, 0xF8, 0xEA, 0xE2, 0xEF, 0x65, 0x70, 0xEB, 0xE8, 0xE2, 0xDF, 0xDA, 0xD9, -0xE3, 0xF0, 0xFA, 0xE6, 0xDD, 0xDF, 0xDF, 0xE0, 0xE4, 0xFC, 0x63, 0x60, 0x64, 0x63, 0x66, 0x78, -0x65, 0x57, 0x60, 0x67, 0x64, 0x67, 0x5F, 0x6E, 0xF3, 0x7F, 0x75, 0xFE, 0xEA, 0xF4, 0xF8, 0xE0, -0xE0, 0xE4, 0xE3, 0xE7, 0xEA, 0xFB, 0x6F, 0x6E, 0x75, 0x72, 0x68, 0x6E, 0x77, 0x74, 0x6D, 0x73, -0xF9, 0x74, 0xF2, 0xE2, 0xEF, 0xDE, 0xDD, 0x79, 0xFD, 0xF4, 0x6F, 0x6A, 0x6E, 0xFE, 0x7A, 0x68, -0x60, 0x63, 0x6B, 0x66, 0x67, 0x76, 0xFE, 0x78, 0x65, 0x63, 0x7A, 0xFF, 0x72, 0x72, 0x74, 0x7D, -0x7C, 0x6B, 0x72, 0xEF, 0xF6, 0x73, 0x74, 0x7C, 0x74, 0x68, 0x68, 0x73, 0xFA, 0xF6, 0x6F, 0x67, -0x6F, 0x6D, 0x6A, 0x69, 0x68, 0x74, 0x6D, 0x64, 0x6F, 0x75, 0x77, 0xFA, 0x7F, 0xF5, 0xEF, 0x78, -0x77, 0x7D, 0x7A, 0xFE, 0x77, 0x73, 0xF8, 0xF4, 0xFE, 0x6F, 0x71, 0xF7, 0x74, 0x79, 0xEF, 0x7B, -0xF9, 0xFA, 0x68, 0x71, 0x7F, 0x76, 0x73, 0x71, 0x7D, 0x7D, 0x65, 0x5D, 0x69, 0x6E, 0x5F, 0x67, -0x6F, 0x6D, 0xFF, 0x7F, 0x6E, 0x6F, 0x73, 0x77, 0xF2, 0xDE, 0xD4, 0xD0, 0xD0, 0xCF, 0xCE, 0xD0, -0xD2, 0xD6, 0xDA, 0xDB, 0xE7, 0x6D, 0x5B, 0x52, 0x4C, 0x47, 0x44, 0x43, 0x44, 0x47, 0x49, 0x4B, -0x4E, 0x52, 0x56, 0x5A, 0x5D, 0x64, 0x75, 0xE6, 0xCF, 0xC5, 0xBF, 0xBD, 0xBB, 0xB7, 0xB5, 0xB1, -0xAF, 0xB5, 0xC1, 0xDF, 0x51, 0x44, 0x3A, 0x33, 0x33, 0x37, 0x3D, 0x44, 0x48, 0x4D, 0x51, 0x54, -0x53, 0x4D, 0x49, 0x44, 0x43, 0x45, 0x46, 0x4C, 0x54, 0x65, 0xE4, 0xCF, 0xC4, 0xBB, 0xB4, 0xAF, -0xAD, 0xAC, 0xAA, 0xAB, 0xB5, 0xD1, 0x49, 0x3B, 0x35, 0x2F, 0x2D, 0x32, 0x3D, 0x4E, 0x57, 0x51, -0x4F, 0x50, 0x4C, 0x42, 0x3D, 0x3F, 0x46, 0x4A, 0x4F, 0x5E, 0xEC, 0xD9, 0xDA, 0xD2, 0xC8, 0xBE, -0xB7, 0xB0, 0xAD, 0xAB, 0xA9, 0xA8, 0xAD, 0xC4, 0x48, 0x36, 0x31, 0x2E, 0x2D, 0x30, 0x3E, 0x67, -0xE2, 0x63, 0x47, 0x43, 0x42, 0x39, 0x36, 0x3B, 0x46, 0x6B, 0xE1, 0xDB, 0xCC, 0xC8, 0xCC, 0xD7, -0xDD, 0xC6, 0xBA, 0xB2, 0xAC, 0xAA, 0xA7, 0xA7, 0xB0, 0x69, 0x32, 0x2D, 0x2D, 0x2C, 0x2D, 0x39, -0xF1, 0xC2, 0xCA, 0x5E, 0x3D, 0x38, 0x34, 0x2E, 0x2F, 0x3E, 0x69, 0xCC, 0xC3, 0xBD, 0xBD, 0xC2, -0xCD, 0xEC, 0xF4, 0xCD, 0xC0, 0xB7, 0xAC, 0xA7, 0xA4, 0xA4, 0xB7, 0x3A, 0x2A, 0x29, 0x2A, 0x2B, -0x30, 0x56, 0xBA, 0xB6, 0xCD, 0x4A, 0x33, 0x2C, 0x2C, 0x2B, 0x34, 0x65, 0xC3, 0xBB, 0xBB, 0xBC, -0xC1, 0xDA, 0x7F, 0x6A, 0x75, 0xD1, 0xC2, 0xB8, 0xAC, 0xA6, 0xA5, 0xA4, 0xB0, 0x35, 0x23, 0x25, -0x29, 0x2F, 0x38, 0x73, 0xB3, 0xAF, 0xC8, 0x3E, 0x2E, 0x29, 0x26, 0x2C, 0x3D, 0xDF, 0xB8, 0xB5, -0xBB, 0xC5, 0xE7, 0x5A, 0x53, 0x68, 0xD9, 0xC8, 0xBA, 0xB3, 0xAD, 0xA8, 0xA7, 0xA6, 0xAF, 0x34, -0x1F, 0x24, 0x2E, 0x3B, 0x48, 0xDA, 0xB3, 0xB0, 0xD6, 0x33, 0x29, 0x29, 0x29, 0x2F, 0x5B, 0xC3, -0xB9, 0xB8, 0xC1, 0xDB, 0x53, 0x46, 0x4E, 0xEE, 0xCB, 0xC5, 0xBF, 0xBA, 0xB6, 0xAE, 0xAA, 0xA8, -0xAA, 0xDB, 0x26, 0x20, 0x2B, 0x3E, 0x5E, 0xEE, 0xC1, 0xB6, 0xC1, 0x3F, 0x2A, 0x29, 0x2A, 0x2F, -0x4F, 0xCA, 0xC0, 0xBE, 0xC5, 0xDC, 0x51, 0x44, 0x4B, 0x7D, 0xCB, 0xC5, 0xC5, 0xC2, 0xBF, 0xBD, -0xB1, 0xA9, 0xA8, 0xAA, 0xCB, 0x28, 0x20, 0x2C, 0x44, 0x7E, 0xDE, 0xC8, 0xBB, 0xC6, 0x3F, 0x2B, -0x29, 0x2E, 0x35, 0x4C, 0xCB, 0xBF, 0xC3, 0xCA, 0xE6, 0x4E, 0x43, 0x44, 0x6A, 0xC8, 0xC3, 0xC3, -0xC8, 0xC8, 0xBE, 0xB5, 0xAB, 0xA7, 0xA8, 0xBC, 0x2D, 0x1F, 0x29, 0x42, 0xD7, 0xD0, 0xCD, 0xBF, -0xC5, 0x4C, 0x2E, 0x2B, 0x2F, 0x36, 0x4A, 0xD7, 0xBE, 0xBD, 0xD5, 0x74, 0x50, 0x42, 0x45, 0x53, -0xD1, 0xBE, 0xBE, 0xC5, 0xD2, 0xCD, 0xBC, 0xAE, 0xA7, 0xA7, 0xB5, 0x36, 0x21, 0x28, 0x41, 0xD1, -0xC9, 0xD4, 0xCB, 0xCB, 0x51, 0x31, 0x2B, 0x2F, 0x39, 0x47, 0xE9, 0xC5, 0xC0, 0xCD, 0xED, 0x50, -0x3F, 0x3F, 0x52, 0xCD, 0xBB, 0xBC, 0xC6, 0xDE, 0xE7, 0xCB, 0xB5, 0xA8, 0xA4, 0xA9, 0xCC, 0x28, -0x20, 0x2E, 0x6F, 0xC2, 0xCF, 0xF7, 0xDA, 0xEB, 0x3F, 0x2E, 0x2D, 0x34, 0x3E, 0x5D, 0xCD, 0xBF, -0xC7, 0x73, 0x53, 0x43, 0x3C, 0x49, 0xDD, 0xBC, 0xB7, 0xC2, 0xE4, 0x62, 0xEC, 0xBD, 0xAA, 0xA3, -0xA4, 0xB3, 0x32, 0x1F, 0x26, 0x42, 0xC3, 0xC0, 0xF2, 0x68, 0x73, 0x4D, 0x37, 0x2D, 0x2F, 0x39, -0x47, 0xDD, 0xC2, 0xBE, 0xD2, 0x4A, 0x3E, 0x3B, 0x42, 0xEB, 0xBE, 0xB5, 0xB9, 0xD5, 0x54, 0x54, -0xCB, 0xAD, 0xA3, 0xA2, 0xAC, 0x4D, 0x23, 0x21, 0x34, 0xCF, 0xBA, 0xD0, 0x5E, 0x73, 0x5D, 0x43, -0x34, 0x30, 0x38, 0x3C, 0x46, 0xCE, 0xB9, 0xBE, 0x5F, 0x3E, 0x3B, 0x3E, 0x62, 0xC0, 0xB5, 0xB6, -0xC9, 0x54, 0x48, 0xE3, 0xB1, 0xA4, 0xA0, 0xA6, 0xC3, 0x2B, 0x1E, 0x29, 0x5D, 0xB9, 0xBD, 0x72, -0x50, 0x5B, 0x50, 0x3C, 0x30, 0x30, 0x36, 0x3F, 0x7C, 0xC0, 0xBA, 0xD0, 0x45, 0x3A, 0x38, 0x46, -0xCB, 0xB7, 0xB5, 0xC4, 0x5A, 0x48, 0x62, 0xBA, 0xA8, 0xA1, 0xA3, 0xB0, 0x3C, 0x20, 0x20, 0x39, -0xBD, 0xB1, 0xC7, 0x4F, 0x4A, 0x4E, 0x48, 0x35, 0x2E, 0x31, 0x39, 0x56, 0xC0, 0xB8, 0xC2, 0x4D, -0x39, 0x37, 0x3E, 0xE2, 0xBC, 0xB6, 0xBB, 0xDC, 0x4B, 0x4A, 0xD3, 0xAE, 0xA3, 0xA0, 0xA8, 0xD5, -0x29, 0x1F, 0x2B, 0xE3, 0xB3, 0xBA, 0x65, 0x47, 0x4C, 0x4F, 0x3D, 0x2F, 0x2D, 0x30, 0x41, 0xCB, -0xB7, 0xBA, 0x76, 0x3D, 0x38, 0x39, 0x51, 0xC5, 0xB6, 0xB6, 0xCC, 0x50, 0x46, 0x7E, 0xB4, 0xA6, -0xA1, 0xA4, 0xB8, 0x35, 0x20, 0x25, 0x48, 0xB8, 0xB5, 0xDB, 0x47, 0x47, 0x52, 0x4A, 0x34, 0x2D, -0x2E, 0x39, 0xEC, 0xBA, 0xB6, 0xCF, 0x3F, 0x38, 0x37, 0x41, 0xD0, 0xB8, 0xB4, 0xBF, 0x6A, 0x4A, -0x5B, 0xBC, 0xA8, 0xA1, 0xA2, 0xAF, 0x3E, 0x22, 0x24, 0x3E, 0xBA, 0xB3, 0xD4, 0x45, 0x41, 0x4F, -0x52, 0x37, 0x2D, 0x2D, 0x36, 0x65, 0xBB, 0xB6, 0xC9, 0x46, 0x38, 0x35, 0x3D, 0xDC, 0xB7, 0xB0, -0xBA, 0xF0, 0x48, 0x4E, 0xC1, 0xAA, 0xA2, 0xA1, 0xAB, 0x5E, 0x27, 0x21, 0x32, 0xC7, 0xB4, 0xC8, -0x48, 0x3E, 0x4B, 0x58, 0x3D, 0x2E, 0x2C, 0x2F, 0x48, 0xBF, 0xB5, 0xBF, 0x4C, 0x36, 0x34, 0x3A, -0x67, 0xBC, 0xB2, 0xB7, 0xD6, 0x4C, 0x4A, 0xCE, 0xAD, 0xA3, 0xA0, 0xA7, 0xD8, 0x29, 0x20, 0x2F, -0xD2, 0xB5, 0xC4, 0x4C, 0x40, 0x4B, 0x64, 0x44, 0x2F, 0x2C, 0x2D, 0x3F, 0xC4, 0xB4, 0xB9, 0x5F, -0x39, 0x36, 0x39, 0x5D, 0xBE, 0xB3, 0xB4, 0xC8, 0x59, 0x4B, 0xDC, 0xAF, 0xA3, 0x9F, 0xA5, 0xC5, -0x2C, 0x21, 0x2C, 0xFB, 0xB9, 0xC1, 0x53, 0x42, 0x4A, 0x5F, 0x49, 0x31, 0x2C, 0x2C, 0x3A, 0xCF, -0xB6, 0xBA, 0xF7, 0x3E, 0x39, 0x3A, 0x4C, 0xCA, 0xB7, 0xB4, 0xBF, 0x79, 0x4D, 0xE5, 0xB1, 0xA4, -0x9F, 0xA4, 0xC6, 0x2B, 0x20, 0x2C, 0xEC, 0xB7, 0xC1, 0x57, 0x43, 0x49, 0x5C, 0x47, 0x31, 0x2C, -0x2C, 0x39, 0xD3, 0xB7, 0xBA, 0xDC, 0x46, 0x3C, 0x39, 0x47, 0xCE, 0xB9, 0xB5, 0xBD, 0xDE, 0x5E, -0xD3, 0xB0, 0xA4, 0xA0, 0xA6, 0xCF, 0x2A, 0x21, 0x2D, 0xF6, 0xBB, 0xC8, 0x55, 0x44, 0x48, 0x51, -0x3F, 0x2F, 0x2C, 0x2D, 0x3B, 0xD8, 0xBC, 0xBF, 0xE7, 0x4E, 0x45, 0x3C, 0x44, 0xEB, 0xBE, 0xB5, -0xBA, 0xCA, 0xE3, 0xC9, 0xAF, 0xA5, 0xA0, 0xA7, 0xE6, 0x28, 0x22, 0x2E, 0xEA, 0xBD, 0xC8, 0x5D, -0x46, 0x4A, 0x4D, 0x3B, 0x2F, 0x2D, 0x2F, 0x3F, 0xD0, 0xBE, 0xC3, 0xE6, 0x56, 0x4B, 0x43, 0x4C, -0xFA, 0xC8, 0xBB, 0xBB, 0xC3, 0xC8, 0xBD, 0xAE, 0xA7, 0xA3, 0xAA, 0x6C, 0x2A, 0x25, 0x30, 0x5A, -0xCD, 0xD3, 0x5F, 0x4D, 0x50, 0x4C, 0x3B, 0x2F, 0x2D, 0x2F, 0x3D, 0xE6, 0xCC, 0xD3, 0xDA, 0xDB, -0x79, 0x55, 0x56, 0x68, 0xE0, 0xCA, 0xC2, 0xC0, 0xBC, 0xB1, 0xAB, 0xA6, 0xA5, 0xAE, 0x53, 0x2C, -0x29, 0x30, 0x41, 0x59, 0x62, 0x53, 0x57, 0x7F, 0x5D, 0x40, 0x35, 0x2E, 0x2F, 0x3D, 0x58, 0x78, -0xE5, 0xCD, 0xC8, 0xCF, 0xDA, 0xEC, 0x7B, 0xE4, 0xD1, 0xCA, 0xC4, 0xBA, 0xB0, 0xAC, 0xAA, 0xAB, -0xBA, 0x4F, 0x39, 0x39, 0x3D, 0x3F, 0x42, 0x3D, 0x3C, 0x45, 0x51, 0x52, 0x4C, 0x42, 0x3B, 0x3E, -0x4A, 0x4C, 0x4D, 0x57, 0x69, 0xEE, 0xDE, 0xE4, 0xFD, 0xED, 0xDC, 0xD3, 0xCD, 0xCA, 0xC3, 0xBD, -0xB9, 0xB6, 0xB8, 0xC3, 0xCE, 0xC8, 0xC1, 0xC5, 0xCE, 0xF9, 0x4A, 0x3F, 0x3F, 0x41, 0x3F, 0x3D, -0x39, 0x38, 0x3B, 0x3C, 0x3A, 0x3C, 0x43, 0x4C, 0x52, 0x58, 0x5B, 0x59, 0x65, 0xDC, 0xCB, 0xC3, -0xBD, 0xB8, 0xB6, 0xB2, 0xAF, 0xB2, 0xB6, 0xB2, 0xB0, 0xB6, 0xBF, 0xD7, 0x4F, 0x40, 0x3F, 0x3D, -0x3A, 0x35, 0x2F, 0x2D, 0x2F, 0x32, 0x33, 0x34, 0x39, 0x3E, 0x46, 0x51, 0x59, 0x5C, 0x6F, 0xD8, -0xC6, 0xBD, 0xB8, 0xB4, 0xB2, 0xAF, 0xAC, 0xAD, 0xAF, 0xAE, 0xAE, 0xB2, 0xBC, 0xCF, 0x55, 0x42, -0x3E, 0x3B, 0x36, 0x31, 0x2D, 0x2A, 0x2B, 0x2F, 0x32, 0x33, 0x36, 0x3B, 0x40, 0x4A, 0x57, 0x69, -0xEE, 0xD6, 0xC8, 0xBF, 0xB9, 0xB3, 0xB1, 0xAF, 0xAC, 0xAB, 0xAE, 0xAF, 0xAF, 0xB1, 0xB9, 0xC6, -0x75, 0x46, 0x3D, 0x3A, 0x36, 0x30, 0x2D, 0x2B, 0x2A, 0x2D, 0x31, 0x34, 0x36, 0x39, 0x3E, 0x46, -0x56, 0x77, 0xE3, 0xD4, 0xCA, 0xC3, 0xBD, 0xB6, 0xB1, 0xAF, 0xAC, 0xAB, 0xAC, 0xAF, 0xAF, 0xB0, -0xB5, 0xBE, 0xDA, 0x4E, 0x3E, 0x3A, 0x36, 0x31, 0x2D, 0x2C, 0x2B, 0x2C, 0x2F, 0x32, 0x34, 0x38, -0x3D, 0x43, 0x4D, 0x66, 0xE7, 0xD6, 0xCB, 0xC5, 0xC0, 0xBA, 0xB3, 0xAF, 0xAD, 0xAB, 0xAC, 0xAE, -0xAF, 0xAF, 0xB3, 0xBB, 0xCD, 0x5A, 0x40, 0x3A, 0x36, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, 0x2F, 0x31, -0x32, 0x38, 0x3D, 0x41, 0x4B, 0x5C, 0xF8, 0xD8, 0xCA, 0xC3, 0xBF, 0xBA, 0xB4, 0xB0, 0xAD, 0xAB, -0xAB, 0xAE, 0xAF, 0xB0, 0xB3, 0xBA, 0xC9, 0x68, 0x46, 0x3C, 0x37, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, -0x2F, 0x30, 0x31, 0x36, 0x3C, 0x3F, 0x49, 0x58, 0x70, 0xDD, 0xCA, 0xC2, 0xBE, 0xB9, 0xB3, 0xB0, -0xAD, 0xAA, 0xAB, 0xAE, 0xAF, 0xAF, 0xB4, 0xBC, 0xCB, 0x61, 0x46, 0x3D, 0x36, 0x30, 0x2E, 0x2C, -0x2B, 0x2D, 0x2F, 0x2F, 0x31, 0x37, 0x3B, 0x3F, 0x4B, 0x5C, 0xFF, 0xD8, 0xC8, 0xC1, 0xBC, 0xB6, -0xB2, 0xAF, 0xAC, 0xAA, 0xAB, 0xAD, 0xAE, 0xAF, 0xB5, 0xBD, 0xCF, 0x5C, 0x45, 0x3C, 0x35, 0x2F, -0x2D, 0x2C, 0x2C, 0x2D, 0x2F, 0x2F, 0x32, 0x37, 0x3B, 0x3F, 0x4B, 0x5F, 0xEE, 0xD4, 0xC8, 0xC1, -0xBC, 0xB5, 0xB0, 0xAE, 0xAB, 0xAA, 0xAB, 0xAD, 0xAF, 0xB1, 0xB7, 0xC0, 0xD9, 0x53, 0x42, 0x3B, -0x34, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x30, 0x33, 0x37, 0x3C, 0x43, 0x4D, 0x5E, 0xEC, 0xD0, -0xC7, 0xC0, 0xBB, 0xB6, 0xB0, 0xAE, 0xAB, 0xAA, 0xAC, 0xAE, 0xAF, 0xB3, 0xB9, 0xC5, 0xE2, 0x4E, -0x3F, 0x39, 0x32, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x2F, 0x33, 0x39, 0x3D, 0x44, 0x4F, 0x65, -0xE3, 0xCE, 0xC5, 0xBF, 0xBA, 0xB4, 0xAF, 0xAD, 0xAA, 0xAA, 0xAC, 0xAE, 0xB0, 0xB5, 0xBB, 0xC8, -0xF8, 0x4C, 0x3E, 0x38, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x35, 0x39, 0x3E, 0x4A, -0x5C, 0xF9, 0xD8, 0xCB, 0xC2, 0xBC, 0xB7, 0xB2, 0xAE, 0xAB, 0xA9, 0xAA, 0xAC, 0xAE, 0xB2, 0xB8, -0xBE, 0xCE, 0x72, 0x48, 0x3B, 0x34, 0x2F, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x31, 0x36, 0x3A, -0x3E, 0x4A, 0x5F, 0xEA, 0xD0, 0xC9, 0xC1, 0xBB, 0xB5, 0xB0, 0xAE, 0xAB, 0xA9, 0xAB, 0xAC, 0xAE, -0xB4, 0xBB, 0xC4, 0xD6, 0x60, 0x45, 0x39, 0x30, 0x2D, 0x2C, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x32, -0x35, 0x3A, 0x43, 0x51, 0x6A, 0xDF, 0xCB, 0xC3, 0xBD, 0xB8, 0xB2, 0xAE, 0xAC, 0xAA, 0xAA, 0xAB, -0xAC, 0xAF, 0xB7, 0xBD, 0xCC, 0xF1, 0x51, 0x3F, 0x36, 0x2F, 0x2D, 0x2B, 0x2A, 0x2C, 0x2D, 0x2E, -0x2F, 0x33, 0x37, 0x3B, 0x45, 0x58, 0x77, 0xD5, 0xC8, 0xC0, 0xBB, 0xB6, 0xB1, 0xAE, 0xAB, 0xA9, -0xAA, 0xAB, 0xAC, 0xB1, 0xB9, 0xC0, 0xD3, 0x6A, 0x4A, 0x3C, 0x33, 0x2E, 0x2D, 0x2B, 0x2B, 0x2C, -0x2D, 0x2E, 0x31, 0x34, 0x38, 0x3F, 0x4C, 0x5D, 0xEA, 0xCE, 0xC6, 0xBE, 0xB8, 0xB4, 0xAF, 0xAD, -0xAB, 0xAA, 0xAA, 0xAB, 0xAD, 0xB2, 0xB9, 0xC3, 0xD8, 0x61, 0x47, 0x3A, 0x32, 0x2E, 0x2C, 0x2B, -0x2C, 0x2C, 0x2D, 0x2F, 0x31, 0x34, 0x38, 0x3F, 0x4D, 0x62, 0xDD, 0xCC, 0xC3, 0xBC, 0xB7, 0xB2, -0xAE, 0xAC, 0xAA, 0xAA, 0xAA, 0xAB, 0xAE, 0xB3, 0xBA, 0xC6, 0xE1, 0x55, 0x41, 0x37, 0x30, 0x2D, -0x2B, 0x2A, 0x2B, 0x2C, 0x2D, 0x2F, 0x31, 0x34, 0x39, 0x40, 0x4D, 0x6A, 0xD8, 0xC9, 0xC1, 0xBC, -0xB7, 0xB2, 0xAE, 0xAC, 0xAA, 0xAA, 0xAB, 0xAC, 0xAF, 0xB7, 0xBB, 0xC6, 0x79, 0x55, 0x44, 0x37, -0x30, 0x2D, 0x2C, 0x2B, 0x2C, 0x2D, 0x2D, 0x2F, 0x33, 0x35, 0x39, 0x42, 0x51, 0x6C, 0xDA, 0xCA, -0xC4, 0xBE, 0xBA, 0xB6, 0xB0, 0xAD, 0xAC, 0xAC, 0xAC, 0xAD, 0xAF, 0xB4, 0xB9, 0xC0, 0xD0, 0x65, -0x47, 0x3A, 0x32, 0x2F, 0x2C, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x31, 0x36, 0x3A, 0x3F, 0x4A, 0x5E, -0xDC, 0xCC, 0xC4, 0xBE, 0xBA, 0xB6, 0xB2, 0xAF, 0xAE, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB8, 0xBE, -0xCA, 0xE6, 0x4F, 0x3F, 0x38, 0x31, 0x2E, 0x2C, 0x2C, 0x2C, 0x2D, 0x2F, 0x31, 0x35, 0x3A, 0x3F, -0x4B, 0x64, 0xDD, 0xCB, 0xC3, 0xBD, 0xBA, 0xB7, 0xB4, 0xB1, 0xAF, 0xAE, 0xAE, 0xB0, 0xB3, 0xB5, -0xB9, 0xBE, 0xC6, 0xD3, 0x74, 0x4E, 0x40, 0x3A, 0x35, 0x32, 0x30, 0x2F, 0x2F, 0x30, 0x33, 0x36, -0x3A, 0x3F, 0x48, 0x52, 0x6C, 0xDF, 0xCE, 0xC7, 0xC1, 0xBE, 0xBB, 0xB9, 0xB8, 0xB7, 0xB6, 0xB6, -0xB7, 0xB9, 0xBB, 0xBE, 0xC4, 0xCA, 0xD9, 0x6C, 0x50, 0x45, 0x3D, 0x3A, 0x37, 0x35, 0x35, 0x35, -0x36, 0x37, 0x39, 0x3C, 0x3F, 0x46, 0x4F, 0x5E, 0xF1, 0xD9, 0xCF, 0xCC, 0xC9, 0xC5, 0xC1, 0xBE, -0xBC, 0xBC, 0xBC, 0xBD, 0xBF, 0xC2, 0xC4, 0xC7, 0xCC, 0xD5, 0xE8, 0x64, 0x52, 0x4A, 0x45, 0x41, -0x3F, 0x3E, 0x3D, 0x3D, 0x3E, 0x41, 0x45, 0x49, 0x4D, 0x53, 0x5C, 0x71, 0xE9, 0xDD, 0xD6, 0xD1, -0xCE, 0xCB, 0xC9, 0xC8, 0xC8, 0xC7, 0xC7, 0xC8, 0xC9, 0xCC, 0xCF, 0xD4, 0xDA, 0xE4, 0xF7, 0x69, -0x5B, 0x53, 0x4F, 0x4E, 0x4D, 0x4D, 0x4D, 0x4E, 0x51, 0x58, 0x5F, 0x68, 0x6F, 0x77, 0xFC, 0xEE, -0xE8, 0xE5, 0xE1, 0xDD, 0xDE, 0xDE, 0xDE, 0xE2, 0xE6, 0xE6, 0xEB, 0xF0, 0xED, 0xEA, 0xEC, 0xEE, -0xF8, 0x6A, 0x5C, 0x57, 0x55, 0x56, 0x58, 0x59, 0x58, 0x58, 0x5B, 0x5E, 0x63, 0x71, 0xF7, 0xEE, -0xE9, 0xE3, 0xE4, 0xE8, 0xE7, 0xEA, 0xEE, 0xEE, 0xF2, 0xFB, 0xFF, 0x7B, 0x74, 0x6E, 0x6B, 0x65, -0x60, 0x65, 0x67, 0x61, 0x60, 0x61, 0x5E, 0x5E, 0x62, 0x69, 0x70, 0x7F, 0xF6, 0xF2, 0xEC, 0xE7, -0xE6, 0xE3, 0xDF, 0xDE, 0xDE, 0xDD, 0xDE, 0xE0, 0xE5, 0xEC, 0xF6, 0x7F, 0x78, 0x75, 0x70, 0x6B, -0x64, 0x5E, 0x5A, 0x58, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E, 0x61, 0x67, 0x6C, 0x75, 0xF8, 0xEC, -0xE6, 0xE0, 0xDD, 0xDD, 0xDE, 0xDF, 0xE0, 0xE2, 0xE2, 0xE4, 0xE6, 0xE9, 0xEE, 0xF9, 0x7D, 0x75, -0x6E, 0x6A, 0x65, 0x61, 0x5E, 0x5C, 0x5B, 0x5C, 0x5D, 0x5E, 0x60, 0x63, 0x66, 0x6C, 0x76, 0xFA, -0xEE, 0xE9, 0xE4, 0xE1, 0xDF, 0xDD, 0xDE, 0xDF, 0xE2, 0xE6, 0xEA, 0xEE, 0xF4, 0xFA, 0x7E, 0x7A, -0x75, 0x72, 0x74, 0x74, 0x71, 0x70, 0x6D, 0x6A, 0x6A, 0x6B, 0x6D, 0x70, 0x74, 0x78, 0x7A, 0x7D, -0xFA, 0xF0, 0xEA, 0xE5, 0xE3, 0xE2, 0xE0, 0xDF, 0xE1, 0xE3, 0xE4, 0xE8, 0xEB, 0xEE, 0xF4, 0xFB, -0x7E, 0x75, 0x6D, 0x6B, 0x6A, 0x69, 0x68, 0x67, 0x65, 0x64, 0x63, 0x64, 0x66, 0x69, 0x6D, 0x72, -0x77, 0x7B, 0xFE, 0xF7, 0xF0, 0xEB, 0xE8, 0xE7, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE8, 0xEA, 0xED, -0xF3, 0xFC, 0x79, 0x72, 0x6F, 0x6C, 0x6B, 0x6B, 0x69, 0x66, 0x64, 0x63, 0x62, 0x63, 0x67, 0x6C, -0x73, 0x7C, 0xFE, 0xFD, 0xFA, 0xF4, 0xEE, 0xEA, 0xE5, 0xE1, 0xE1, 0xE2, 0xE5, 0xE9, 0xED, 0xEF, -0xF1, 0xF2, 0xF4, 0xF8, 0xFF, 0x76, 0x6E, 0x6A, 0x68, 0x67, 0x66, 0x65, 0x64, 0x62, 0x61, 0x61, -0x63, 0x67, 0x6B, 0x72, 0x7E, 0xFA, 0xF5, 0xF1, 0xEE, 0xED, 0xEC, 0xE9, 0xE8, 0xE7, 0xE7, 0xE9, -0xEC, 0xF0, 0xF9, 0x7D, 0x77, 0x73, 0x70, 0x6F, 0x6E, 0x6C, 0x69, 0x67, 0x65, 0x66, 0x66, 0x68, -0x69, 0x69, 0x6A, 0x6C, 0x6E, 0x75, 0x7F, 0xF8, 0xF1, 0xED, 0xEB, 0xEA, 0xEA, 0xEB, 0xEB, 0xEB, -0xEA, 0xE9, 0xEA, 0xEC, 0xEF, 0xF4, 0xFB, 0x7D, 0x77, 0x73, 0x71, 0x6F, 0x6D, 0x6B, 0x69, 0x67, -0x66, 0x67, 0x68, 0x69, 0x6B, 0x6D, 0x6F, 0x74, 0x79, 0x7F, 0xF9, 0xF3, 0xEF, 0xEE, 0xEE, 0xEE, -0xEE, 0xED, 0xED, 0xED, 0xEE, 0xEC, 0xEC, 0xF1, 0xF3, 0xF6, 0xFD, 0xFF, 0x7D, 0x78, 0x78, 0x76, -0x70, 0x6F, 0x6E, 0x6D, 0x6D, 0x6E, 0x6F, 0x71, 0x73, 0x77, 0x7B, 0x7D, 0xFF, 0xFB, 0xF8, 0xF8, -0xFA, 0xFB, 0xFE, 0x7E, 0x7D, 0x7A, 0x79, 0x7A, 0x76, 0x76, 0x76, 0x72, 0x70, 0x6F, 0x6D, 0x6E, -0x6D, 0x6B, 0x6E, 0x65, 0x66, 0x57, 0x41, 0x59, 0xC7, 0x62, 0x57, 0xCD, 0x64, 0x6B, 0xCC, 0x6D, -0xF6, 0xD1, 0x69, 0x6E, 0xDA, 0x7D, 0x61, 0xEE, 0xF1, 0xFD, 0xFF, 0x6E, 0xE7, 0x74, 0x66, 0xE9, -0x6A, 0xFC, 0xF2, 0x64, 0xEE, 0x6F, 0x6B, 0x7B, 0x69, 0xF2, 0x68, 0x74, 0xEC, 0x5F, 0xED, 0xFC, -0x5F, 0xDE, 0x72, 0x65, 0xDC, 0x51, 0x4C, 0xE8, 0x5F, 0xEF, 0xD7, 0x4E, 0xF3, 0xF9, 0x52, 0xC3, -0xE2, 0x56, 0xC9, 0x5B, 0x59, 0xE4, 0x50, 0x53, 0x47, 0x66, 0xF9, 0x47, 0xC9, 0xCB, 0x59, 0xD4, -0x55, 0x58, 0xD3, 0x5C, 0xF9, 0xCE, 0xD7, 0x6D, 0x61, 0xDB, 0xE4, 0xE8, 0x70, 0x79, 0xCD, 0x6B, -0x5D, 0xEC, 0x66, 0xDC, 0x6C, 0x5F, 0xD3, 0xF9, 0xFB, 0x66, 0x55, 0xDE, 0xFB, 0x64, 0xD7, 0xDA, -0xF1, 0xF1, 0xF1, 0xE5, 0xDB, 0xF4, 0x75, 0xDF, 0xE7, 0xFD, 0x79, 0xEC, 0xD9, 0xF0, 0x6E, 0xF5, -0xE4, 0xE5, 0x64, 0x74, 0xDF, 0x7D, 0x68, 0x75, 0xF0, 0xFD, 0x7B, 0xFC, 0x7B, 0xDE, 0xEF, 0x64, -0xDF, 0xE8, 0xFF, 0xE9, 0xFF, 0xE2, 0xE1, 0x76, 0xF6, 0xEF, 0xEC, 0xF9, 0x6D, 0x7C, 0x73, 0x6D, -0x69, 0x67, 0x76, 0x75, 0x73, 0x64, 0x66, 0xFD, 0x60, 0x5F, 0x77, 0x67, 0x70, 0x74, 0x60, 0x78, -0x7A, 0x5C, 0x67, 0x79, 0x6C, 0x77, 0x63, 0x63, 0xEF, 0x6C, 0x66, 0x73, 0x6D, 0xF8, 0x72, 0x74, -0xE6, 0xFB, 0x7B, 0xF3, 0xF6, 0xF1, 0xFE, 0x7B, 0xF5, 0xF1, 0x6D, 0x63, 0x75, 0x6E, 0x60, 0x64, -0x67, 0x67, 0x68, 0x67, 0x67, 0x6C, 0x64, 0x5B, 0x65, 0x6E, 0x69, 0x72, 0x76, 0x74, 0xFE, 0xFD, -0xFF, 0xF6, 0xF3, 0x7C, 0x7A, 0xFC, 0xFE, 0xFB, 0x7E, 0x75, 0xF5, 0xEE, 0xFC, 0xF7, 0xF3, 0xFF, -0xFE, 0x74, 0x6B, 0x6F, 0x6D, 0x6A, 0x6C, 0x6A, 0x7A, 0xF9, 0x71, 0x7C, 0xF5, 0x7C, 0x7B, 0x7F, -0xFE, 0x72, 0x6E, 0xFF, 0x6F, 0x66, 0x6B, 0x6A, 0x6E, 0x76, 0x71, 0x7C, 0x7A, 0x6E, 0x6C, 0x69, -0x6C, 0x6D, 0x63, 0x65, 0x6A, 0x66, 0x63, 0x65, 0x6C, 0x68, 0x64, 0x69, 0x6A, 0x6E, 0x79, 0x7B, -0x78, 0x6D, 0x6E, 0x79, 0x77, 0xFA, 0xED, 0xEF, 0xEF, 0xEC, 0xE8, 0xEF, 0xFB, 0xF6, 0xEF, 0xEE, -0xF7, 0xF9, 0xF3, 0xF7, 0xF1, 0xF3, 0xFD, 0xFD, 0x7A, 0x7B, 0xFE, 0x79, 0x7C, 0x7B, 0x6E, 0x72, -0x7A, 0x72, 0x71, 0x79, 0x79, 0x6F, 0x6F, 0x78, 0x74, 0x72, 0x7B, 0x72, 0x78, 0xF6, 0xF7, 0xF9, -0x78, 0x79, 0xEE, 0xF5, 0x74, 0x76, 0x7A, 0xFE, 0x7E, 0x75, 0x7A, 0x7E, 0x74, 0x70, 0x7D, 0xF8, -0xF8, 0x7D, 0x77, 0xF8, 0xF0, 0x7F, 0x7E, 0xF2, 0xED, 0xF0, 0xF4, 0xEB, 0xE9, 0xFE, 0x74, 0xEF, -0xE1, 0xE0, 0xDC, 0xD9, 0xD7, 0xD3, 0xD7, 0xDD, 0xD9, 0xD9, 0xDF, 0xE6, 0xEB, 0xF2, 0x78, 0x65, -0x61, 0x65, 0x5F, 0x5A, 0x5B, 0x5B, 0x5A, 0x5C, 0x5B, 0x5D, 0x5E, 0x5C, 0x5B, 0x5B, 0x5B, 0x5C, -0x5E, 0x6B, 0xEA, 0xD9, 0xD2, 0xD1, 0xCD, 0xC8, 0xC6, 0xC5, 0xC5, 0xC3, 0xC3, 0xC6, 0xCA, 0xD2, -0xE8, 0x67, 0x51, 0x49, 0x42, 0x3D, 0x3D, 0x3E, 0x3F, 0x41, 0x44, 0x49, 0x4E, 0x50, 0x55, 0x5A, -0x5F, 0x6F, 0xEF, 0xDA, 0xCD, 0xCA, 0xC8, 0xC4, 0xBF, 0xBD, 0xBB, 0xB9, 0xB7, 0xB6, 0xBA, 0xC2, -0xCC, 0xE2, 0x56, 0x43, 0x3B, 0x39, 0x37, 0x36, 0x37, 0x3B, 0x3E, 0x42, 0x47, 0x4B, 0x51, 0x5A, -0x5B, 0x5E, 0x67, 0x7A, 0xED, 0xE8, 0xDB, 0xCF, 0xCB, 0xC5, 0xBD, 0xB9, 0xB6, 0xB2, 0xB0, 0xB4, -0xB9, 0xBF, 0xCD, 0xFC, 0x49, 0x3B, 0x37, 0x33, 0x31, 0x32, 0x33, 0x38, 0x3E, 0x45, 0x4F, 0x5F, -0x74, 0xF0, 0xEF, 0xF1, 0xF4, 0xFF, 0x77, 0x72, 0xFB, 0xE4, 0xD9, 0xCE, 0xC3, 0xBA, 0xB6, 0xB2, -0xB0, 0xB2, 0xB6, 0xBB, 0xC8, 0xE4, 0x51, 0x3E, 0x38, 0x33, 0x2F, 0x2F, 0x31, 0x35, 0x3A, 0x3E, -0x49, 0x5D, 0xFE, 0xE4, 0xDD, 0xD9, 0xD9, 0xE1, 0xEB, 0xEE, 0xEE, 0xE7, 0xDD, 0xD2, 0xC7, 0xBD, -0xB9, 0xB5, 0xB2, 0xB5, 0xB8, 0xBB, 0xC6, 0xDA, 0x5F, 0x45, 0x3D, 0x37, 0x33, 0x31, 0x31, 0x35, -0x39, 0x3D, 0x45, 0x53, 0x6F, 0xE3, 0xD9, 0xD0, 0xCF, 0xD2, 0xD5, 0xD9, 0xD9, 0xD8, 0xD4, 0xCE, -0xC8, 0xC0, 0xBD, 0xB9, 0xB8, 0xBB, 0xBD, 0xC1, 0xCD, 0xDF, 0x5F, 0x4A, 0x41, 0x3B, 0x37, 0x36, -0x35, 0x36, 0x3A, 0x3D, 0x44, 0x4E, 0x5F, 0xEC, 0xDB, 0xD6, 0xD2, 0xD4, 0xD6, 0xDA, 0xDD, 0xDB, -0xD8, 0xD2, 0xCD, 0xC9, 0xC3, 0xBF, 0xBE, 0xBF, 0xC3, 0xC4, 0xCA, 0xD6, 0xED, 0x5E, 0x50, 0x48, -0x3F, 0x3C, 0x3B, 0x3A, 0x3A, 0x3D, 0x40, 0x4A, 0x55, 0x6A, 0xE6, 0xDC, 0xD8, 0xD6, 0xD8, 0xD8, -0xDB, 0xDE, 0xDF, 0xE2, 0xDE, 0xDC, 0xD9, 0xD0, 0xCB, 0xC8, 0xC8, 0xCC, 0xCD, 0xCD, 0xD1, 0xDB, -0xF2, 0x67, 0x5B, 0x51, 0x49, 0x44, 0x42, 0x41, 0x42, 0x44, 0x49, 0x4E, 0x55, 0x5C, 0x6A, 0x7E, -0xF3, 0xED, 0xED, 0xEF, 0xEC, 0xED, 0xF4, 0xF7, 0xF0, 0xEB, 0xE4, 0xDF, 0xDA, 0xD2, 0xCD, 0xCC, -0xCC, 0xCF, 0xCF, 0xCE, 0xD3, 0xDB, 0xE7, 0x7C, 0x69, 0x5E, 0x54, 0x4F, 0x4E, 0x4D, 0x4E, 0x4F, -0x52, 0x58, 0x5B, 0x5E, 0x63, 0x67, 0x6B, 0x6B, 0x6A, 0x6B, 0x6C, 0x6E, 0x6F, 0x73, 0x7D, 0xF3, -0xEA, 0xE1, 0xDC, 0xD8, 0xD3, 0xD1, 0xCF, 0xCE, 0xD1, 0xD2, 0xD2, 0xD6, 0xDB, 0xE2, 0xEF, 0xFD, -0x73, 0x66, 0x5F, 0x5C, 0x5A, 0x59, 0x58, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58, 0x5A, 0x5C, 0x5D, -0x61, 0x65, 0x6A, 0x74, 0x7E, 0xF4, 0xEA, 0xE2, 0xDD, 0xDA, 0xD7, 0xD5, 0xD2, 0xD1, 0xD0, 0xD1, -0xD3, 0xD3, 0xD4, 0xD7, 0xDB, 0xE0, 0xE8, 0xEF, 0x7B, 0x69, 0x5F, 0x5B, 0x57, 0x53, 0x50, 0x4F, -0x4F, 0x4F, 0x4F, 0x50, 0x52, 0x56, 0x59, 0x5C, 0x60, 0x68, 0x6E, 0x7A, 0xFB, 0xF1, 0xEB, 0xE7, -0xE2, 0xDE, 0xDC, 0xDA, 0xD7, 0xD5, 0xD3, 0xD3, 0xD3, 0xD5, 0xD5, 0xD8, 0xDC, 0xE4, 0xF2, 0x78, -0x6A, 0x5E, 0x59, 0x55, 0x52, 0x50, 0x4F, 0x4F, 0x50, 0x52, 0x55, 0x58, 0x5A, 0x5D, 0x61, 0x64, -0x69, 0x6E, 0x74, 0xFF, 0xF6, 0xEE, 0xE9, 0xE4, 0xDF, 0xDC, 0xD8, 0xD5, 0xD2, 0xD0, 0xD1, 0xD1, -0xD2, 0xD6, 0xD7, 0xDA, 0xDF, 0xE8, 0xF8, 0x71, 0x69, 0x60, 0x5C, 0x58, 0x56, 0x54, 0x53, 0x53, -0x53, 0x55, 0x56, 0x58, 0x5A, 0x5C, 0x5F, 0x63, 0x69, 0x71, 0xFF, 0xEF, 0xEA, 0xE6, 0xE1, 0xDF, -0xDE, 0xDB, 0xDA, 0xDA, 0xD7, 0xD4, 0xD1, 0xD1, 0xD2, 0xD4, 0xD7, 0xD8, 0xDB, 0xE1, 0xED, 0x78, -0x69, 0x61, 0x5A, 0x53, 0x50, 0x4F, 0x4E, 0x4F, 0x4F, 0x51, 0x55, 0x59, 0x5D, 0x60, 0x65, 0x6C, -0x6F, 0x76, 0x7E, 0xFC, 0xF5, 0xF2, 0xF2, 0xED, 0xE7, 0xE1, 0xDD, 0xD9, 0xD4, 0xCE, 0xCB, 0xCA, -0xC8, 0xCA, 0xCF, 0xD0, 0xD7, 0xE6, 0x7C, 0x5E, 0x53, 0x4F, 0x4C, 0x4A, 0x4A, 0x4A, 0x4C, 0x4E, -0x4F, 0x51, 0x57, 0x5D, 0x62, 0x66, 0x69, 0x6D, 0x6D, 0x67, 0x63, 0x65, 0x6B, 0x70, 0x75, 0xFE, -0xEA, 0xDC, 0xD3, 0xCE, 0xC9, 0xC2, 0xBE, 0xBC, 0xBC, 0xC1, 0xC8, 0xCB, 0xDC, 0x69, 0x50, 0x46, -0x42, 0x3F, 0x3D, 0x3D, 0x3F, 0x43, 0x4B, 0x4F, 0x55, 0x61, 0x6F, 0xFC, 0xF0, 0xFA, 0xFD, 0x7B, -0x63, 0x5A, 0x54, 0x52, 0x54, 0x58, 0x62, 0x7D, 0xDE, 0xCC, 0xC4, 0xBC, 0xB6, 0xB1, 0xAE, 0xB2, -0xBA, 0xC1, 0xDA, 0x5A, 0x45, 0x38, 0x33, 0x32, 0x33, 0x36, 0x3A, 0x3E, 0x4B, 0x66, 0xEA, 0xD6, -0xCE, 0xCF, 0xD3, 0xE0, 0x6E, 0x5C, 0x51, 0x4D, 0x4B, 0x4A, 0x4F, 0x5B, 0x7A, 0xD6, 0xC6, 0xBA, -0xB1, 0xAE, 0xAA, 0xAA, 0xAF, 0xBA, 0xD1, 0x4E, 0x3D, 0x31, 0x2B, 0x2B, 0x2C, 0x30, 0x38, 0x3E, -0x4E, 0xEA, 0xCD, 0xC7, 0xC8, 0xCB, 0xCE, 0xDD, 0x64, 0x4F, 0x48, 0x48, 0x47, 0x46, 0x4D, 0x62, -0xDD, 0xCA, 0xBD, 0xB1, 0xAC, 0xA9, 0xA7, 0xAA, 0xB2, 0xC4, 0x51, 0x3A, 0x2F, 0x29, 0x28, 0x2A, -0x2E, 0x39, 0x45, 0x5A, 0xDB, 0xC8, 0xC2, 0xC5, 0xCD, 0xD5, 0xE8, 0x5B, 0x4C, 0x46, 0x47, 0x4C, -0x4E, 0x59, 0xF3, 0xD3, 0xC6, 0xBE, 0xB5, 0xAD, 0xAA, 0xA6, 0xA8, 0xB2, 0xC5, 0x54, 0x38, 0x2F, -0x28, 0x26, 0x2A, 0x2F, 0x3A, 0x4B, 0x73, 0xCF, 0xC5, 0xC4, 0xC9, 0xD9, 0xF8, 0x5F, 0x4C, 0x47, -0x47, 0x48, 0x4F, 0x5A, 0x74, 0xD9, 0xCE, 0xC7, 0xBF, 0xBA, 0xB0, 0xAC, 0xAA, 0xA9, 0xB0, 0xC5, -0x5C, 0x3B, 0x30, 0x2B, 0x27, 0x29, 0x2E, 0x39, 0x49, 0x6C, 0xD2, 0xC7, 0xC5, 0xCA, 0xDA, 0x7A, -0x5E, 0x4D, 0x48, 0x49, 0x4B, 0x52, 0x5E, 0x70, 0xDC, 0xCE, 0xC9, 0xC4, 0xBE, 0xB5, 0xAE, 0xAC, -0xAA, 0xAD, 0xBB, 0xE1, 0x43, 0x35, 0x2E, 0x29, 0x28, 0x2B, 0x32, 0x3D, 0x4E, 0x7E, 0xD2, 0xC9, -0xC7, 0xCC, 0xDF, 0xFE, 0x61, 0x4F, 0x4C, 0x4B, 0x4C, 0x54, 0x5D, 0x72, 0xDA, 0xCE, 0xC7, 0xC0, -0xB9, 0xB0, 0xAD, 0xAA, 0xAA, 0xB1, 0xC5, 0x5D, 0x3D, 0x33, 0x2C, 0x29, 0x29, 0x2D, 0x35, 0x3F, -0x4F, 0x78, 0xD7, 0xCA, 0xC7, 0xCD, 0xD9, 0xE3, 0x71, 0x5C, 0x54, 0x4D, 0x4E, 0x55, 0x5A, 0x6E, -0xE3, 0xD3, 0xC8, 0xBE, 0xB6, 0xAF, 0xAC, 0xAA, 0xAC, 0xB7, 0xCE, 0x53, 0x3D, 0x34, 0x2D, 0x2A, -0x2A, 0x2E, 0x35, 0x3E, 0x4A, 0x5C, 0xE5, 0xCD, 0xC8, 0xCB, 0xCE, 0xD5, 0xE2, 0xF7, 0x66, 0x57, -0x54, 0x56, 0x5B, 0x6E, 0xE7, 0xD5, 0xC9, 0xBE, 0xB6, 0xB1, 0xAE, 0xAE, 0xB4, 0xBE, 0xD3, 0x5C, -0x46, 0x3C, 0x34, 0x30, 0x2F, 0x32, 0x38, 0x3E, 0x45, 0x4E, 0x69, 0xDC, 0xCF, 0xCD, 0xCF, 0xD3, -0xD9, 0xDF, 0xEF, 0x6F, 0x68, 0x67, 0x6F, 0xF7, 0xE7, 0xDC, 0xD2, 0xC8, 0xC1, 0xBE, 0xBC, 0xBD, -0xC3, 0xCB, 0xD5, 0xEA, 0x6A, 0x58, 0x4D, 0x47, 0x43, 0x43, 0x44, 0x47, 0x4A, 0x4D, 0x52, 0x5D, -0x6D, 0x78, 0x77, 0x78, 0x76, 0x73, 0x75, 0x72, 0x72, 0x78, 0x7E, 0xF6, 0xEB, 0xE3, 0xDC, 0xD6, -0xD2, 0xCF, 0xCE, 0xCE, 0xCD, 0xCF, 0xD2, 0xD4, 0xD7, 0xDA, 0xDD, 0xE3, 0xEF, 0x7A, 0x6C, 0x65, -0x5F, 0x5B, 0x57, 0x53, 0x4F, 0x4E, 0x4D, 0x4C, 0x4A, 0x4A, 0x4A, 0x4B, 0x4D, 0x4F, 0x53, 0x59, -0x5F, 0x6C, 0xFE, 0xEC, 0xE3, 0xDE, 0xDC, 0xDA, 0xD9, 0xD8, 0xD7, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, -0xD8, 0xDA, 0xDC, 0xDE, 0xE3, 0xEC, 0xFB, 0x6F, 0x63, 0x5B, 0x55, 0x4F, 0x4D, 0x4C, 0x4B, 0x4C, -0x4C, 0x4D, 0x4F, 0x53, 0x58, 0x5E, 0x67, 0x73, 0xFB, 0xEE, 0xE8, 0xE3, 0xDF, 0xDC, 0xDA, 0xD9, -0xD8, 0xD8, 0xD8, 0xD9, 0xDB, 0xDD, 0xDF, 0xE1, 0xE5, 0xE9, 0xEE, 0xF6, 0x7C, 0x6E, 0x66, 0x5F, -0x5C, 0x5A, 0x58, 0x56, 0x56, 0x57, 0x58, 0x5A, 0x5D, 0x60, 0x66, 0x6D, 0x74, 0x7C, 0xFB, 0xF4, -0xEE, 0xEA, 0xE7, 0xE5, 0xE4, 0xE4, 0xE4, 0xE7, 0xEA, 0xEC, 0xED, 0xED, 0xEC, 0xED, 0xEC, 0xED, -0xEE, 0xF1, 0xF9, 0x7F, 0x79, 0x75, 0x70, 0x6D, 0x6C, 0x6C, 0x6E, 0x73, 0x79, 0xFE, 0xF9, 0xF3, -0xEE, 0xEB, 0xE9, 0xE8, 0xE6, 0xE5, 0xE6, 0xE8, 0xEA, 0xED, 0xF1, 0xF8, 0xFD, 0x7E, 0x7E, 0xFE, -0xFC, 0xF9, 0xF5, 0xF5, 0xF6, 0xFB, 0x7C, 0x77, 0x71, 0x6E, 0x6C, 0x6A, 0x69, 0x69, 0x6B, 0x6E, -0x70, 0x75, 0x7A, 0xFE, 0xF6, 0xF1, 0xEE, 0xEC, 0xEB, 0xEB, 0xEC, 0xEE, 0xF1, 0xF5, 0xFB, 0x7D, -0x7B, 0x7A, 0x7C, 0xFE, 0xFA, 0xF4, 0xF1, 0xF0, 0xF0, 0xF3, 0xF7, 0xFB, 0xFD, 0xFF, 0xFF, 0x7F, -0x7F, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0xFD, 0xFC, 0xFA, 0xF5, 0xF4, 0xF1, 0xEF, 0xEF, 0xEF, 0xF0, -0xF4, 0xF8, 0xFD, 0x7D, 0x78, 0x77, 0x78, 0x7A, 0x7C, 0x7C, 0x7E, 0x7C, 0x7A, 0x79, 0x76, 0x77, -0x78, 0x77, 0x7B, 0x7E, 0xFF, 0xFD, 0xFD, 0xFE, 0xFF, 0xFF, 0x7F, 0x7F, 0xFF, 0x7E, 0xFE, 0xFB, -0xFA, 0xF9, 0xFA, 0xFB, 0xFE, 0x7F, 0x7C, 0x79, 0x78, 0x77, 0x77, 0x7A, 0x7C, 0x7D, 0x7E, 0x7B, -0x77, 0x74, 0x71, 0x6F, 0x6E, 0x6E, 0x6F, 0x6F, 0x70, 0x71, 0x73, 0x74, 0x76, 0x76, 0x75, 0x76, -0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x77, 0x74, 0x72, 0x70, 0x6F, 0x6E, 0x6E, 0x6E, 0x70, 0x74, -0x78, 0x7B, 0x7D, 0x7D, 0x7C, 0x7C, 0x7A, 0x76, 0x72, 0x71, 0x70, 0x70, 0x70, 0x71, 0x72, 0x72, -0x74, 0x76, 0x78, 0x7B, 0x7F, 0xFE, 0xFC, 0xF9, 0xF6, 0xF5, 0xF7, 0xFC, 0xFF, 0x7C, 0x79, 0x78, -0x77, 0x77, 0x77, 0x7A, 0x7E, 0xFE, 0xFC, 0xFA, 0xF8, 0xF8, 0xFA, 0xFC, 0xFE, 0x7D, 0x7A, 0x76, -0x74, 0x74, 0x73, 0x75, 0x76, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0xFF, 0xFB, 0xF9, 0xF8, 0xF9, 0xFD, -0x7E, 0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x78, 0x7C, 0x7F, 0xFB, 0xF6, 0xF5, 0xF2, 0xF2, 0xF3, -0xF4, 0xF7, 0xF8, 0xFA, 0xFC, 0xFE, 0xFF, 0xFF, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFA, -0xF7, 0xF6, 0xF8, 0xFC, 0x7F, 0x7C, 0x79, 0x72, 0x6F, 0x6E, 0x6E, 0x6E, 0x6F, 0x73, 0x77, 0x77, -0x7B, 0x7D, 0x7E, 0x7F, 0x7E, 0x7C, 0x7A, 0x77, 0x75, 0x71, 0x6F, 0x6E, 0x6D, 0x6E, 0x6F, 0x70, -0x73, 0x74, 0x77, 0x79, 0x7C, 0x7D, 0x7D, 0x7D, 0x7B, 0x7A, 0x79, 0x76, 0x73, 0x70, 0x6F, 0x71, -0x73, 0x78, 0x7B, 0x7F, 0xFB, 0xF8, 0xF5, 0xF3, 0xF5, 0xF6, 0xF6, 0xF8, 0xF9, 0xFC, 0xFE, 0xFF, -0x7F, 0xFF, 0xFD, 0xFC, 0xFC, 0xFB, 0xF7, 0xF6, 0xF6, 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF7, 0xF9, -0xFA, 0xFC, 0xFF, 0xFF, 0x7F, 0x7F, 0xFE, 0xFD, 0xFE, 0xFD, 0xFB, 0xFB, 0xFC, 0xFE, 0x7E, 0x7C, -0x7A, 0x79, 0x77, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7C, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, -0x7D, 0x7E, 0x7C, 0x7E, 0x7F, 0x7E, 0x7E, 0xFF, 0xFD, 0xFC, 0xFB, 0xF8, 0xF8, 0xFA, 0xFA, 0xFC, -0xFE, 0x7D, 0x78, 0x75, 0x72, 0x70, 0x70, 0x73, 0x75, 0x76, 0x78, 0x79, 0x79, 0x7A, 0x79, 0x78, -0x77, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7D, 0x7F, 0xFC, 0xF8, 0xF6, -0xF4, 0xF2, 0xF3, 0xF7, 0xF9, 0xFB, 0x7E, 0x78, 0x75, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, -0x75, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x78, 0x78, 0x78, 0x77, 0x75, 0x75, 0x76, 0x79, 0x7B, -0x7C, 0x7E, 0xFE, 0xFC, 0xF9, 0xF6, 0xF5, 0xF6, 0xF7, 0xF9, 0xFC, 0xFE, 0x7C, 0x79, 0x77, 0x76, -0x75, 0x74, 0x74, 0x75, 0x74, 0x76, 0x77, 0x78, 0x79, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x78, 0x78, -0x77, 0x76, 0x77, 0x77, 0x77, 0x78, 0x7A, 0x7C, 0xFF, 0xFD, 0xFA, 0xF8, 0xF6, 0xF7, 0xF8, 0xF7, -0xF8, 0xF8, 0xF8, 0xF9, 0xFA, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFB, 0xFD, 0xFD, 0xFC, 0xFD, 0xFE, -0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x78, 0x79, 0x7B, 0x7C, 0x7C, 0x7C, -0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x71, 0x70, 0x6F, 0x6F, 0x6E, -0x6D, 0x6C, 0x6C, 0x6D, 0x6E, 0x6E, 0x6E, 0x6F, 0x6E, 0x6F, 0x70, 0x70, 0x72, 0x75, 0x78, 0x7B, -0x7B, 0x7C, 0x7D, 0xFF, 0xFD, 0xFC, 0xFA, 0xFA, 0xF9, 0xF7, 0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF9, -0xFA, 0xFA, 0xFA, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFB, -0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFF, 0x7F, 0xFC, 0xFA, 0xFB, 0xFA, 0xFA, 0xF9, -0xFA, 0xFC, 0xFC, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, 0xFD, 0xFC, 0xFD, 0xFA, 0xFA, 0xF9, 0xF8, -0xFA, 0xF8, 0xFA, 0xFA, 0xFB, 0xFE, 0xFB, 0xFF, 0xFD, 0xFD, 0xFF, 0xFB, 0xFF, 0xFA, 0xFC, 0x7F, -0xFC, 0x70, 0x6A, 0x64, 0x61, 0x67, 0x6C, 0x73, 0x61, 0x60, 0x5A, 0x47, 0x53, 0xDF, 0x7D, 0x6F, -0x71, 0x69, 0x7F, 0x72, 0x70, 0x68, 0x5A, 0x5B, 0x59, 0x55, 0x5A, 0x5D, 0x4E, 0x46, 0x51, 0x77, -0xF6, 0xEC, 0xE8, 0xE6, 0xE5, 0xE4, 0xE1, 0xE5, 0xFD, 0x60, 0x65, 0xE6, 0xDB, 0xDD, 0xE1, 0xE4, -0xE2, 0xDC, 0xD4, 0xCF, 0xCF, 0xCF, 0xD1, 0xD2, 0xCF, 0xCD, 0xCC, 0xCD, 0xD0, 0xD4, 0xD7, 0xD5, -0xCE, 0xCD, 0xD3, 0xD9, 0xDB, 0xDA, 0xD9, 0xDC, 0xE2, 0xE5, 0xF1, 0xEE, 0xD7, 0xD2, 0xD9, 0xDC, -0xDD, 0xD9, 0xDC, 0xE7, 0xDF, 0xD9, 0xDD, 0x74, 0x62, 0xFC, 0xE9, 0xF2, 0x6C, 0x5D, 0x5D, 0x61, -0x6B, 0x6C, 0x5C, 0x5D, 0x70, 0x64, 0x5B, 0x6D, 0x78, 0x61, 0x57, 0x58, 0x65, 0x62, 0x53, 0x51, -0x69, 0xF5, 0x59, 0x53, 0x69, 0x68, 0x54, 0x4F, 0x67, 0xFA, 0x5B, 0x58, 0x5D, 0x60, 0x5D, 0x4E, -0x53, 0x61, 0x5A, 0x5F, 0x63, 0x61, 0x5E, 0x4C, 0x4C, 0x5A, 0x6B, 0xF7, 0x58, 0x47, 0x51, 0x6D, -0x68, 0x5A, 0x55, 0x53, 0x4A, 0x4E, 0xEE, 0xE1, 0x56, 0x46, 0x4D, 0x74, 0x70, 0x59, 0x53, 0x4C, -0x4E, 0x62, 0xF2, 0xF3, 0x5A, 0x4A, 0x4F, 0x6D, 0x6B, 0x57, 0x63, 0xED, 0x79, 0x69, 0x62, 0x5D, -0x6C, 0x67, 0x5E, 0xF5, 0xEA, 0x69, 0x63, 0x76, 0xFA, 0x6A, 0x59, 0x5D, 0xFB, 0xF6, 0x72, 0xFB, -0x71, 0x5D, 0x65, 0x7D, 0xFC, 0x79, 0xFB, 0xE9, 0xE2, 0xDD, 0xE3, 0xF9, 0xFA, 0xF3, 0xEA, 0xEC, -0x7D, 0xF1, 0xE5, 0xF2, 0x78, 0x6D, 0x6D, 0xED, 0xE4, 0xE5, 0xE4, 0xF6, 0x7A, 0xF8, 0xE5, 0xDC, -0xEA, 0xDF, 0xCF, 0xD3, 0xE7, 0x6C, 0x78, 0xCF, 0xC9, 0xD5, 0x7E, 0x64, 0x7E, 0xD7, 0xCF, 0xD7, -0xE0, 0xE8, 0xDC, 0xCB, 0xC5, 0xCC, 0xE6, 0x73, 0xE9, 0xD2, 0xC9, 0xCA, 0xD3, 0xED, 0x68, 0xF3, -0xD9, 0xDA, 0xDC, 0xDA, 0xD7, 0xDB, 0xE1, 0xE1, 0xEA, 0x69, 0x59, 0x60, 0xEE, 0xE0, 0xDC, 0xDF, -0xE5, 0xDD, 0xE1, 0xED, 0xED, 0xF4, 0xF6, 0xF2, 0xEE, 0xED, 0xFC, 0x6F, 0x64, 0x5C, 0x5C, 0x61, -0x76, 0xEA, 0xE1, 0xEB, 0x73, 0x66, 0x60, 0x69, 0xFB, 0x7E, 0x70, 0x76, 0xFF, 0x78, 0x73, 0xEB, -0xE2, 0x6F, 0x5C, 0x69, 0xE9, 0xDB, 0xDE, 0x73, 0x5E, 0x6D, 0xED, 0xEB, 0xF4, 0xFF, 0x77, 0xFD, -0xE7, 0xE2, 0xFB, 0x66, 0x69, 0xFE, 0xFA, 0x6B, 0x63, 0x69, 0x74, 0x74, 0x66, 0x63, 0x6F, 0xFB, -0xFD, 0x73, 0x71, 0x7B, 0xF4, 0xE8, 0xE2, 0xE8, 0xF2, 0xEB, 0xDE, 0xDC, 0xDE, 0xE2, 0xE7, 0xE5, -0xDF, 0xDF, 0xE8, 0xFC, 0x6D, 0x6A, 0x66, 0x5C, 0x53, 0x51, 0x56, 0x5B, 0x59, 0x56, 0x56, 0x5A, -0x5E, 0x5A, 0x53, 0x53, 0x59, 0x5D, 0x5F, 0x64, 0x74, 0xE9, 0xD6, 0xCB, 0xCB, 0xD3, 0xDC, 0xD4, -0xC8, 0xC1, 0xBF, 0xBE, 0xBE, 0xC5, 0xCE, 0xDC, 0xEA, 0x68, 0x4B, 0x3E, 0x3B, 0x3B, 0x3D, 0x3E, -0x41, 0x45, 0x47, 0x48, 0x4A, 0x4C, 0x4A, 0x47, 0x49, 0x4F, 0x5E, 0x6D, 0xF5, 0xDB, 0xCC, 0xC5, -0xC1, 0xBF, 0xBE, 0xBC, 0xBA, 0xB7, 0xB2, 0xAF, 0xAE, 0xB7, 0xDA, 0x43, 0x38, 0x35, 0x32, 0x31, -0x33, 0x3A, 0x46, 0x54, 0x5D, 0x56, 0x4A, 0x44, 0x43, 0x46, 0x44, 0x3F, 0x40, 0x4B, 0x61, 0x7B, -0xFD, 0xE7, 0xD4, 0xCA, 0xC5, 0xBF, 0xBB, 0xB6, 0xB1, 0xAF, 0xAD, 0xAC, 0xAC, 0xB4, 0xD8, 0x3C, -0x2D, 0x2B, 0x2D, 0x2F, 0x37, 0x41, 0x54, 0x76, 0xFF, 0x68, 0x54, 0x46, 0x40, 0x44, 0x49, 0x4A, -0x49, 0x4C, 0x5C, 0xFB, 0xE9, 0xE5, 0xDB, 0xCE, 0xC8, 0xC3, 0xBE, 0xBA, 0xB6, 0xB1, 0xAE, 0xAB, -0xA9, 0xAB, 0xBD, 0x3F, 0x2A, 0x26, 0x2A, 0x31, 0x3A, 0x43, 0x58, 0xE9, 0xDB, 0xE8, 0x5E, 0x46, -0x3C, 0x3D, 0x48, 0x55, 0x56, 0x55, 0x61, 0xF3, 0xE8, 0xE6, 0xDC, 0xCF, 0xC9, 0xC0, 0xB9, 0xB4, -0xB3, 0xB3, 0xAF, 0xAA, 0xA8, 0xAF, 0x75, 0x2C, 0x22, 0x24, 0x2D, 0x3F, 0x56, 0x72, 0xE0, 0xCF, -0xD4, 0x59, 0x3F, 0x38, 0x38, 0x3E, 0x4C, 0x68, 0xE8, 0xE3, 0xEA, 0x7E, 0x67, 0x64, 0x79, 0xDC, -0xCB, 0xBF, 0xB7, 0xB1, 0xAF, 0xAE, 0xAC, 0xAA, 0xB1, 0x56, 0x29, 0x1F, 0x20, 0x2B, 0x45, 0xD1, -0xC4, 0xC7, 0xCB, 0xD8, 0x4B, 0x33, 0x2E, 0x34, 0x42, 0x6D, 0xCD, 0xC5, 0xC8, 0xD1, 0xE0, 0x69, -0x52, 0x52, 0x6D, 0xD4, 0xC6, 0xBB, 0xB1, 0xAC, 0xAB, 0xAA, 0xA8, 0xAE, 0x68, 0x28, 0x1D, 0x1E, -0x2B, 0x53, 0xBC, 0xB6, 0xBD, 0xCA, 0xE1, 0x4E, 0x33, 0x2A, 0x2C, 0x3B, 0x7D, 0xBF, 0xBB, 0xC1, -0xD5, 0xED, 0x6A, 0x4F, 0x4C, 0x58, 0x7E, 0xD6, 0xC7, 0xBB, 0xB2, 0xAD, 0xA9, 0xA6, 0xA9, 0xC3, -0x2D, 0x1D, 0x1B, 0x23, 0x41, 0xBC, 0xAF, 0xB5, 0xC2, 0xDE, 0x4F, 0x3B, 0x2D, 0x27, 0x2D, 0x49, -0xC3, 0xB5, 0xB9, 0xCB, 0x64, 0x54, 0x56, 0x51, 0x58, 0x75, 0xE3, 0xD1, 0xCA, 0xC1, 0xB6, 0xAB, -0xA6, 0xA3, 0xA6, 0xC0, 0x2B, 0x1C, 0x1A, 0x24, 0x4D, 0xB0, 0xAA, 0xB0, 0xC3, 0xF0, 0x46, 0x38, -0x2D, 0x28, 0x2B, 0x4C, 0xBC, 0xB2, 0xB6, 0xD1, 0x4B, 0x46, 0x4F, 0x58, 0x61, 0xEE, 0xE1, 0xDB, -0xCF, 0xCB, 0xBD, 0xAE, 0xA7, 0xA3, 0xA2, 0xB6, 0x2C, 0x1B, 0x19, 0x21, 0x52, 0xAB, 0xA5, 0xAE, -0xC9, 0x4F, 0x3B, 0x37, 0x32, 0x2A, 0x2A, 0x3D, 0xC9, 0xB2, 0xB1, 0xCA, 0x44, 0x3D, 0x49, 0x5F, -0xDD, 0xD8, 0x77, 0x6E, 0xED, 0xDB, 0xC7, 0xB7, 0xAB, 0xA5, 0xA3, 0xA6, 0xC8, 0x26, 0x1A, 0x1A, -0x27, 0xC5, 0xA4, 0xA3, 0xAF, 0xE9, 0x3B, 0x33, 0x34, 0x31, 0x2D, 0x31, 0x57, 0xBF, 0xB3, 0xBA, -0x78, 0x41, 0x3E, 0x4E, 0xDC, 0xCE, 0xD4, 0x78, 0x5B, 0x67, 0xDC, 0xC4, 0xB7, 0xAC, 0xA6, 0xA5, -0xA5, 0xBA, 0x2C, 0x1B, 0x19, 0x23, 0xD8, 0xA3, 0x9F, 0xAB, 0xDA, 0x34, 0x2D, 0x30, 0x33, 0x33, -0x37, 0x54, 0xC1, 0xB7, 0xBD, 0x6E, 0x41, 0x3D, 0x4A, 0xD8, 0xCA, 0xD0, 0xEB, 0x56, 0x54, 0x74, -0xCE, 0xBD, 0xAF, 0xA8, 0xA6, 0xA5, 0xAE, 0x3B, 0x1E, 0x19, 0x1E, 0x43, 0xA8, 0x9E, 0xA6, 0xC8, -0x33, 0x29, 0x2C, 0x32, 0x39, 0x3D, 0x4E, 0xCD, 0xBE, 0xC2, 0xFF, 0x40, 0x3D, 0x48, 0xE0, 0xC4, -0xCB, 0xEA, 0x4F, 0x48, 0x59, 0xD7, 0xBE, 0xB5, 0xAD, 0xA9, 0xA9, 0xA7, 0xB3, 0x34, 0x1D, 0x19, -0x20, 0xEE, 0xA2, 0x9D, 0xA8, 0x60, 0x2A, 0x26, 0x2D, 0x3B, 0x4A, 0x4E, 0x64, 0xCE, 0xC6, 0xCF, -0x57, 0x42, 0x47, 0x67, 0xCB, 0xC3, 0xD4, 0x5C, 0x48, 0x47, 0x63, 0xCA, 0xBA, 0xB2, 0xAE, 0xAC, -0xAC, 0xAB, 0xB3, 0x3D, 0x1F, 0x1B, 0x20, 0x51, 0xA5, 0x9E, 0xA8, 0x6C, 0x2A, 0x24, 0x2B, 0x3E, -0x5E, 0x6C, 0x66, 0xFA, 0xE0, 0xE4, 0x60, 0x4B, 0x4E, 0x71, 0xD2, 0xC8, 0xD8, 0x5A, 0x4A, 0x49, -0x6E, 0xC6, 0xB9, 0xB3, 0xB1, 0xAF, 0xAE, 0xAC, 0xAF, 0x48, 0x21, 0x1B, 0x1F, 0x4C, 0xA5, 0x9D, -0xA7, 0x64, 0x28, 0x23, 0x2C, 0x4A, 0xD6, 0xD9, 0x61, 0x59, 0x6C, 0xEF, 0xF8, 0x5B, 0x5B, 0xEE, -0xD5, 0xCD, 0xDB, 0x5C, 0x4D, 0x4C, 0x6E, 0xC8, 0xBB, 0xB8, 0xB8, 0xB6, 0xB3, 0xAD, 0xA8, 0xBC, -0x2D, 0x1D, 0x1B, 0x2B, 0xB7, 0x9F, 0xA1, 0xB9, 0x33, 0x23, 0x28, 0x3A, 0xEB, 0xCF, 0x77, 0x50, -0x56, 0x6E, 0xE2, 0x73, 0x55, 0x5D, 0xE6, 0xCF, 0xD9, 0x5B, 0x4B, 0x47, 0x59, 0xD2, 0xC3, 0xBD, -0xBD, 0xBC, 0xB6, 0xAE, 0xA9, 0xAB, 0x68, 0x24, 0x1B, 0x1E, 0x3C, 0xAA, 0x9E, 0xA6, 0xDE, 0x2A, -0x23, 0x2C, 0x47, 0xD1, 0xCE, 0x72, 0x55, 0x58, 0x6A, 0x78, 0x5D, 0x5B, 0xF9, 0xDA, 0xD3, 0xDE, -0x5A, 0x49, 0x4A, 0x5F, 0xD1, 0xC0, 0xBD, 0xBC, 0xB9, 0xB3, 0xAD, 0xA9, 0xB1, 0x3C, 0x1F, 0x1C, -0x24, 0xE1, 0xA4, 0x9F, 0xAD, 0x44, 0x26, 0x25, 0x32, 0x6E, 0xC3, 0xCD, 0x64, 0x4F, 0x4F, 0x62, -0x70, 0x5C, 0x68, 0xE1, 0xD4, 0xD3, 0xF6, 0x4E, 0x45, 0x4B, 0x78, 0xCA, 0xBE, 0xBC, 0xBC, 0xB8, -0xB3, 0xAC, 0xA9, 0xBC, 0x2F, 0x1E, 0x1C, 0x2A, 0xBC, 0xA0, 0xA1, 0xB7, 0x35, 0x24, 0x28, 0x3B, -0xD8, 0xC4, 0xDC, 0x53, 0x4B, 0x4F, 0x67, 0x6D, 0x65, 0xEA, 0xDB, 0xD5, 0xDA, 0x5E, 0x4B, 0x48, -0x53, 0xDB, 0xC4, 0xBD, 0xBD, 0xBB, 0xB5, 0xAE, 0xA8, 0xAB, 0x75, 0x25, 0x1B, 0x1E, 0x3D, 0xAA, -0x9E, 0xA6, 0xDC, 0x2B, 0x25, 0x2E, 0x4E, 0xCC, 0xCC, 0x66, 0x4B, 0x4C, 0x5A, 0x74, 0x6C, 0x6D, -0xDF, 0xD6, 0xD6, 0xED, 0x4D, 0x43, 0x46, 0x59, 0xD0, 0xBF, 0xBC, 0xBB, 0xB8, 0xB3, 0xAD, 0xA9, -0xB3, 0x38, 0x1E, 0x1B, 0x24, 0xCF, 0xA2, 0x9F, 0xAF, 0x3E, 0x26, 0x27, 0x36, 0x6A, 0xCD, 0xE6, -0x4F, 0x4D, 0x55, 0x6E, 0xEF, 0x63, 0x7B, 0xDA, 0xDB, 0xDF, 0x5F, 0x49, 0x47, 0x4F, 0xE8, 0xC5, -0xBD, 0xBC, 0xBB, 0xB6, 0xAF, 0xAA, 0xAA, 0xCD, 0x29, 0x1C, 0x1D, 0x34, 0xAE, 0x9F, 0xA4, 0xC7, -0x2E, 0x25, 0x2C, 0x41, 0xDB, 0xD2, 0x75, 0x5B, 0x59, 0x60, 0x6E, 0x5B, 0x5C, 0xDF, 0xD0, 0xD8, -0x74, 0x4B, 0x43, 0x4B, 0x6E, 0xCC, 0xBF, 0xBD, 0xBE, 0xBC, 0xB5, 0xAD, 0xA8, 0xAD, 0x4B, 0x22, -0x1C, 0x22, 0x65, 0xA6, 0x9F, 0xAC, 0x4C, 0x2A, 0x28, 0x33, 0x50, 0xDE, 0xF2, 0x65, 0x7C, 0xF8, -0x79, 0x5A, 0x4B, 0x5E, 0xD7, 0xCF, 0xD6, 0x5F, 0x45, 0x43, 0x4C, 0xFB, 0xC9, 0xBF, 0xBD, 0xBC, -0xB8, 0xB2, 0xAD, 0xAA, 0xB7, 0x38, 0x20, 0x1D, 0x29, 0xC6, 0xA5, 0xA4, 0xB8, 0x3C, 0x2A, 0x2C, -0x39, 0x53, 0x7F, 0x6F, 0x79, 0xE8, 0xF9, 0x61, 0x4D, 0x4B, 0xFF, 0xD0, 0xD3, 0xEC, 0x4F, 0x45, -0x49, 0x58, 0xDF, 0xC7, 0xBF, 0xBD, 0xBB, 0xB5, 0xAF, 0xA9, 0xAA, 0xCA, 0x2C, 0x1D, 0x1E, 0x36, -0xB1, 0xA2, 0xA8, 0xCE, 0x33, 0x2B, 0x30, 0x3E, 0x4F, 0x5A, 0x6D, 0xD8, 0xD2, 0xF5, 0x4F, 0x46, -0x54, 0xD4, 0xCA, 0xD7, 0x65, 0x48, 0x43, 0x4C, 0x61, 0xD7, 0xC4, 0xBF, 0xBD, 0xB7, 0xB1, 0xAD, -0xAA, 0xB0, 0x48, 0x23, 0x1D, 0x24, 0x5D, 0xAA, 0xA4, 0xB0, 0x56, 0x30, 0x2E, 0x34, 0x3E, 0x47, -0x52, 0xE3, 0xC9, 0xCE, 0x65, 0x45, 0x43, 0x6A, 0xCE, 0xCD, 0xDE, 0x58, 0x49, 0x49, 0x4F, 0x6D, -0xD1, 0xC0, 0xBB, 0xB6, 0xAF, 0xAE, 0xAA, 0xAB, 0xCF, 0x2B, 0x1D, 0x1E, 0x36, 0xB2, 0xA4, 0xA9, -0xC7, 0x3B, 0x2F, 0x31, 0x37, 0x3D, 0x46, 0x6E, 0xC7, 0xC5, 0xE5, 0x4C, 0x41, 0x50, 0xD5, 0xCB, -0xDF, 0x58, 0x4C, 0x49, 0x4E, 0x62, 0xE4, 0xC8, 0xBD, 0xB9, 0xB4, 0xAF, 0xAD, 0xA9, 0xB2, 0x41, -0x22, 0x1D, 0x26, 0xEB, 0xAA, 0xA6, 0xB3, 0x5D, 0x38, 0x32, 0x32, 0x34, 0x39, 0x4D, 0xCC, 0xBD, -0xC7, 0x60, 0x44, 0x47, 0x6D, 0xDC, 0xE2, 0xEF, 0x63, 0x58, 0x50, 0x4D, 0x5A, 0xDB, 0xC3, 0xBA, -0xB3, 0xAF, 0xAD, 0xAC, 0xAE, 0xD3, 0x2D, 0x1F, 0x20, 0x38, 0xB9, 0xA9, 0xAD, 0xC4, 0x4C, 0x39, -0x33, 0x30, 0x32, 0x3C, 0xFA, 0xBE, 0xBE, 0xD5, 0x51, 0x46, 0x4F, 0x6F, 0x7B, 0x65, 0x68, 0x76, -0x60, 0x54, 0x52, 0x61, 0xCF, 0xBF, 0xB7, 0xAE, 0xAC, 0xAB, 0xAB, 0xBC, 0x39, 0x22, 0x1E, 0x2B, -0xE7, 0xAF, 0xAC, 0xB6, 0xD1, 0x4E, 0x3A, 0x2F, 0x2D, 0x31, 0x45, 0xCC, 0xBC, 0xC2, 0xDA, 0x58, -0x4D, 0x55, 0x52, 0x52, 0x68, 0xED, 0xF4, 0x66, 0x55, 0x59, 0xEF, 0xCE, 0xBF, 0xB5, 0xAD, 0xAB, -0xAB, 0xAE, 0xCC, 0x31, 0x22, 0x22, 0x2F, 0xE8, 0xB6, 0xB1, 0xB8, 0xC4, 0x73, 0x3D, 0x2F, 0x2B, -0x2F, 0x3F, 0xDD, 0xC0, 0xBF, 0xC7, 0xD8, 0x73, 0x55, 0x46, 0x44, 0x53, 0x7B, 0xEE, 0xED, 0xF2, -0xED, 0xDC, 0xCF, 0xC1, 0xB3, 0xAC, 0xA9, 0xA9, 0xB2, 0x58, 0x2C, 0x24, 0x28, 0x36, 0x5C, 0xC7, -0xBA, 0xB6, 0xB7, 0xCA, 0x49, 0x34, 0x2D, 0x2E, 0x37, 0x49, 0xE0, 0xC4, 0xBD, 0xBE, 0xC7, 0xEA, -0x4C, 0x42, 0x44, 0x49, 0x50, 0x69, 0xDD, 0xD0, 0xCB, 0xC6, 0xBD, 0xB3, 0xAD, 0xAB, 0xAB, 0xBA, -0x49, 0x2F, 0x2B, 0x2D, 0x32, 0x3A, 0x4B, 0xD6, 0xBC, 0xB6, 0xBD, 0xD7, 0x51, 0x3D, 0x36, 0x34, -0x37, 0x3E, 0x52, 0xDE, 0xC8, 0xC3, 0xC7, 0xCE, 0xDE, 0x72, 0x56, 0x4F, 0x53, 0x5D, 0xFF, 0xDA, -0xC9, 0xBD, 0xB7, 0xB4, 0xB2, 0xB6, 0xC6, 0x62, 0x47, 0x3F, 0x3D, 0x3B, 0x3B, 0x40, 0x4E, 0x6F, -0xEC, 0x7B, 0x67, 0x5E, 0x5B, 0x59, 0x55, 0x5A, 0x65, 0x5E, 0x5A, 0x5F, 0x61, 0x5E, 0x63, 0x65, -0x61, 0x6D, 0xF5, 0xFA, 0x7A, 0x77, 0x77, 0xFF, 0xEB, 0xDC, 0xD5, 0xCE, 0xCA, 0xC8, 0xC8, 0xCD, -0xD9, 0xEC, 0x76, 0x70, 0xFD, 0xED, 0xEA, 0xEE, 0x7D, 0x66, 0x59, 0x53, 0x50, 0x4F, 0x50, 0x54, -0x56, 0x58, 0x59, 0x5A, 0x5A, 0x5C, 0x5F, 0x6B, 0xF6, 0xE5, 0xE1, 0xE7, 0xEF, 0xF5, 0xF2, 0xEA, -0xDF, 0xD8, 0xCE, 0xC8, 0xC4, 0xC5, 0xCB, 0xD4, 0xDF, 0xE9, 0xE7, 0xE1, 0xDD, 0xDB, 0xDE, 0xEE, -0x68, 0x57, 0x4F, 0x4D, 0x4D, 0x4E, 0x4F, 0x4F, 0x52, 0x54, 0x53, 0x51, 0x53, 0x57, 0x5E, 0x6F, -0xFA, 0xEF, 0xEF, 0xF3, 0xF1, 0xF1, 0xEE, 0xE6, 0xDE, 0xD6, 0xCF, 0xCD, 0xCE, 0xD3, 0xDA, 0xDC, -0xDF, 0xDF, 0xD9, 0xD5, 0xD4, 0xD7, 0xE0, 0xFE, 0x6B, 0x5F, 0x5A, 0x58, 0x56, 0x54, 0x54, 0x53, -0x53, 0x50, 0x4E, 0x4F, 0x52, 0x56, 0x5C, 0x64, 0x67, 0x66, 0x69, 0x6C, 0x6D, 0x74, 0xFA, 0xEC, -0xDF, 0xD7, 0xD6, 0xDD, 0xE6, 0xF3, 0x7B, 0xF7, 0xE8, 0xDC, 0xD3, 0xD0, 0xD2, 0xD9, 0xE1, 0xE7, -0xEB, 0xF6, 0x7B, 0x74, 0x6F, 0x6B, 0x66, 0x5E, 0x59, 0x55, 0x53, 0x52, 0x56, 0x59, 0x5C, 0x61, -0x5F, 0x64, 0x65, 0x62, 0x6F, 0x7B, 0xFC, 0xE4, 0xDC, 0xD9, 0xE2, 0x7E, 0xFF, 0xFE, 0xFF, 0xEE, -0xDF, 0xD8, 0xD6, 0xD7, 0xDB, 0xDD, 0xDD, 0xE8, 0xE7, 0xE3, 0xEA, 0xE7, 0xF9, 0x72, 0x7C, 0x5F, -0x56, 0x58, 0x58, 0x56, 0x57, 0x57, 0x53, 0x54, 0x4A, 0x42, 0x4F, 0x67, 0x6A, 0xE0, 0xD4, 0xD8, -0xD7, 0xDD, 0xFD, 0xFE, 0xE4, 0x7B, 0xFF, 0xD9, 0xDF, 0xDE, 0xD7, 0xDA, 0xCF, 0xCF, 0xD3, 0xCD, -0xD3, 0xDF, 0xDD, 0xDE, 0xF3, 0x6D, 0x5F, 0x55, 0x54, 0x55, 0x51, 0x51, 0x50, 0x4F, 0x51, 0x53, -0x57, 0x59, 0x60, 0x73, 0xFB, 0xE7, 0xE1, 0xEA, 0xF4, 0x7D, 0x6D, 0x6F, 0x7F, 0x78, 0xF5, 0xE7, -0xEA, 0xE0, 0xDE, 0xD9, 0xCD, 0xCB, 0xC5, 0xC2, 0xC5, 0xCB, 0xDB, 0xDD, 0xEC, 0x53, 0x4C, 0x45, -0x42, 0x49, 0x48, 0x49, 0x4D, 0x51, 0x5A, 0x55, 0x55, 0x5D, 0x64, 0xF4, 0xED, 0xF5, 0xEB, 0xF2, -0x7A, 0x6D, 0x60, 0x5E, 0x6E, 0xF3, 0xF7, 0xE9, 0xDF, 0xDD, 0xD4, 0xCD, 0xC8, 0xC1, 0xBC, 0xB8, -0xB5, 0xB8, 0xC7, 0xF5, 0x52, 0x3E, 0x38, 0x37, 0x36, 0x3D, 0x4B, 0x59, 0xFD, 0xDF, 0xE3, 0xFE, -0x5C, 0x54, 0x52, 0x4E, 0x50, 0x55, 0x5D, 0xFF, 0xF2, 0xF1, 0xE9, 0xFB, 0xFD, 0x74, 0x5E, 0x62, -0x7A, 0xE3, 0xD2, 0xCA, 0xC0, 0xB9, 0xB1, 0xAD, 0xAE, 0xBC, 0x68, 0x3E, 0x31, 0x2D, 0x2E, 0x30, -0x3B, 0x5B, 0xD4, 0xC7, 0xC9, 0xD6, 0x65, 0x4A, 0x46, 0x3E, 0x3D, 0x47, 0x51, 0x7F, 0xD5, 0xD0, -0xCE, 0xD2, 0xE2, 0x6E, 0x57, 0x55, 0x61, 0xEA, 0xCD, 0xBE, 0xB7, 0xB2, 0xAD, 0xAB, 0xB0, 0xCE, -0x40, 0x2F, 0x2B, 0x2D, 0x2F, 0x38, 0x59, 0xCA, 0xBD, 0xBE, 0xCF, 0x70, 0x45, 0x39, 0x3A, 0x3B, -0x3F, 0x5D, 0xD9, 0xCB, 0xC4, 0xC5, 0xCF, 0xEA, 0x62, 0x52, 0x50, 0x5C, 0x7E, 0xD1, 0xBE, 0xB5, -0xAF, 0xAE, 0xAB, 0xAC, 0xC2, 0x44, 0x2F, 0x2A, 0x2D, 0x32, 0x38, 0x55, 0xC6, 0xBA, 0xBC, 0xCF, -0x5B, 0x40, 0x36, 0x34, 0x38, 0x40, 0x66, 0xCD, 0xC4, 0xC2, 0xC6, 0xD1, 0xFE, 0x52, 0x4C, 0x4F, -0x5C, 0xEF, 0xCF, 0xC1, 0xB7, 0xAF, 0xAD, 0xAB, 0xAD, 0xC9, 0x3C, 0x2D, 0x29, 0x2D, 0x35, 0x3D, -0xF1, 0xBD, 0xB8, 0xBF, 0xE1, 0x4C, 0x3C, 0x32, 0x31, 0x39, 0x47, 0xF7, 0xCB, 0xC4, 0xC2, 0xC7, -0xD9, 0x6F, 0x54, 0x4D, 0x4F, 0x5D, 0xEF, 0xCF, 0xC1, 0xB8, 0xAF, 0xAD, 0xAB, 0xAC, 0xC9, 0x3A, -0x2C, 0x29, 0x2D, 0x37, 0x44, 0xD8, 0xB9, 0xB7, 0xC4, 0x6C, 0x44, 0x38, 0x31, 0x30, 0x39, 0x4E, -0xDF, 0xCA, 0xC5, 0xC6, 0xCD, 0xEF, 0x5E, 0x5A, 0x53, 0x55, 0x64, 0xE8, 0xCD, 0xC4, 0xBB, 0xB0, -0xAC, 0xAA, 0xAC, 0xD0, 0x36, 0x2C, 0x2B, 0x2E, 0x39, 0x4A, 0xCB, 0xB6, 0xB7, 0xC8, 0x5D, 0x43, -0x39, 0x31, 0x32, 0x3D, 0x58, 0xD9, 0xCD, 0xCA, 0xC9, 0xD6, 0x73, 0x62, 0x63, 0x62, 0x68, 0x72, -0xE9, 0xCE, 0xC6, 0xBB, 0xAF, 0xAD, 0xAB, 0xAE, 0xD7, 0x38, 0x2D, 0x2C, 0x2F, 0x3B, 0x4F, 0xCA, -0xB6, 0xB8, 0xCB, 0x5D, 0x41, 0x39, 0x32, 0x33, 0x3E, 0x59, 0xDC, 0xCF, 0xCD, 0xCB, 0xD8, 0x63, -0x53, 0x5A, 0x5E, 0x61, 0x6F, 0xEA, 0xCF, 0xC6, 0xBC, 0xB0, 0xAD, 0xAB, 0xAE, 0xEE, 0x32, 0x2C, -0x2C, 0x30, 0x3B, 0x50, 0xC9, 0xB6, 0xB9, 0xCF, 0x5D, 0x47, 0x3A, 0x32, 0x33, 0x3F, 0x63, 0xDB, -0xD5, 0xCC, 0xC8, 0xD7, 0x66, 0x56, 0x58, 0x5C, 0x5A, 0x5B, 0x7E, 0xCE, 0xC4, 0xBB, 0xB0, 0xAD, -0xAB, 0xAF, 0x7B, 0x2F, 0x2C, 0x2E, 0x31, 0x39, 0x52, 0xC6, 0xB6, 0xBA, 0xD1, 0x5C, 0x4E, 0x3E, -0x32, 0x33, 0x3F, 0x64, 0xEA, 0xFD, 0xCE, 0xC2, 0xD0, 0x6C, 0x57, 0x5D, 0x64, 0x56, 0x53, 0x6C, -0xCE, 0xC4, 0xBD, 0xB1, 0xAD, 0xAA, 0xAE, 0x6F, 0x31, 0x2F, 0x31, 0x31, 0x37, 0x4F, 0xC5, 0xB6, -0xBA, 0xD2, 0xFC, 0x60, 0x45, 0x36, 0x35, 0x41, 0x56, 0xF8, 0xF2, 0xE4, 0xC6, 0xC8, 0x7C, 0x5A, -0x6A, 0x6A, 0x57, 0x52, 0x5B, 0xDA, 0xC6, 0xC0, 0xB6, 0xAE, 0xAA, 0xAC, 0xDD, 0x33, 0x30, 0x35, -0x31, 0x30, 0x42, 0xCE, 0xBA, 0xBA, 0xCC, 0xD9, 0xD9, 0x53, 0x3A, 0x35, 0x3E, 0x4D, 0x52, 0x60, -0xD6, 0xC5, 0xD2, 0x71, 0xE7, 0x7C, 0x55, 0x53, 0x51, 0x5B, 0xEE, 0xDC, 0xD3, 0xBE, 0xB2, 0xB0, -0xAD, 0xAE, 0x67, 0x31, 0x38, 0x39, 0x2F, 0x32, 0x49, 0xD5, 0xBE, 0xBF, 0xCF, 0xCD, 0xCA, 0x5C, -0x3D, 0x3C, 0x41, 0x41, 0x47, 0x5D, 0xE1, 0xC9, 0xC7, 0xD4, 0xDD, 0xFC, 0x51, 0x4C, 0x4C, 0x4C, -0x5F, 0xE2, 0xDC, 0xCA, 0xBF, 0xBD, 0xB9, 0xB5, 0xAF, 0xBA, 0x43, 0x35, 0x44, 0x3E, 0x2F, 0x36, -0x53, 0xD4, 0xC8, 0xCC, 0xD1, 0xC3, 0xC5, 0x5E, 0x41, 0x45, 0x49, 0x42, 0x44, 0x55, 0xE9, 0xD0, -0xD1, 0xDA, 0xD6, 0xD7, 0xFD, 0x5A, 0x58, 0x5B, 0x5A, 0x5B, 0x5F, 0x6C, 0x7A, 0xE1, 0xD3, 0xD2, -0xCC, 0xC8, 0xC4, 0xBF, 0xBD, 0xBA, 0xC2, 0x49, 0x3C, 0x4C, 0x41, 0x35, 0x3B, 0x52, 0xF3, 0xD6, -0xD1, 0xCE, 0xC3, 0xC6, 0x7A, 0x53, 0x57, 0x4E, 0x44, 0x46, 0x4F, 0x64, 0xE8, 0xDF, 0xDD, 0xD4, -0xD6, 0xEC, 0x56, 0x49, 0x54, 0x5E, 0x4F, 0x57, 0xFA, 0xE1, 0xDC, 0xE0, 0xE3, 0xD6, 0xD0, 0xDC, -0xD9, 0xC9, 0xC2, 0xBD, 0xC5, 0x58, 0x50, 0xE6, 0x4B, 0x37, 0x3F, 0x4F, 0x4D, 0x52, 0x6D, 0xDD, -0xC8, 0xC8, 0xDC, 0xDC, 0xD1, 0x72, 0x4A, 0x4A, 0x4E, 0x4B, 0x4E, 0x57, 0x6A, 0xDF, 0xD7, 0xDB, -0xD5, 0xCF, 0xDA, 0xF4, 0x74, 0x67, 0x5E, 0x5E, 0x5E, 0x66, 0xF9, 0xED, 0xEC, 0xE8, 0xE4, 0xE8, -0xF4, 0xF7, 0x60, 0x52, 0x6D, 0xF3, 0x5B, 0x66, 0xE7, 0xE1, 0xE0, 0xDF, 0xDF, 0xD8, 0xD1, 0xEA, -0x6B, 0xD9, 0xD1, 0xE5, 0xE6, 0xCC, 0xC4, 0xE7, 0x52, 0xEA, 0xE4, 0x4C, 0x48, 0x59, 0x5D, 0x59, -0x5D, 0x60, 0xF7, 0xDF, 0x79, 0x6A, 0xDF, 0xDC, 0xFE, 0xF8, 0xE2, 0xF0, 0x6F, 0x6B, 0x61, 0x5F, -0x62, 0x5D, 0x5E, 0x6C, 0x76, 0x70, 0xF7, 0xE5, 0xE6, 0xE7, 0xE5, 0xEA, 0xF4, 0x7D, 0x6C, 0x66, -0x68, 0x66, 0x66, 0x73, 0xF8, 0xF3, 0xF4, 0xF6, 0xFB, 0x7E, 0x7A, 0x7E, 0xF6, 0xEF, 0xED, 0xE9, -0xE9, 0xEF, 0xFB, 0x78, 0x6E, 0x69, 0x64, 0x63, 0x68, 0x6E, 0x74, 0x7B, 0xFA, 0xF6, 0xF3, 0xFF, -0x6B, 0x69, 0x69, 0x60, 0x5F, 0x65, 0x6C, 0x73, 0x7C, 0xF3, 0xEB, 0xE9, 0xEA, 0xE8, 0xE6, 0xE6, -0xE7, 0xE4, 0xDE, 0xDD, 0xE2, 0xE2, 0xE1, 0xE9, 0xF8, 0x77, 0x6B, 0x62, 0x5D, 0x5B, 0x5A, 0x59, -0x5A, 0x5B, 0x5D, 0x5E, 0x5E, 0x5E, 0x5F, 0x62, 0x67, 0x6D, 0x78, 0xFA, 0xF2, 0xEE, 0xEF, 0xF3, -0xF7, 0xF8, 0xFA, 0xFE, 0xFA, 0xF3, 0xF1, 0xEF, 0xEA, 0xE7, 0xE5, 0xE6, 0xE9, 0xEF, 0xFB, 0x79, -0x70, 0x6C, 0x6A, 0x6C, 0x6F, 0x77, 0x7C, 0xFD, 0xFB, 0x7F, 0x73, 0x6C, 0x69, 0x69, 0x6C, 0x73, -0xFA, 0xED, 0xE9, 0xE5, 0xE1, 0xE4, 0xE8, 0xE8, 0xE7, 0xE7, 0xE5, 0xE2, 0xE0, 0xDD, 0xDC, 0xE1, -0xE3, 0xE2, 0xEC, 0x7C, 0x6C, 0x63, 0x5D, 0x5B, 0x59, 0x58, 0x5A, 0x5B, 0x5B, 0x5C, 0x5F, 0x60, -0x5F, 0x60, 0x65, 0x6A, 0x75, 0xF8, 0xEC, 0xE7, 0xE6, 0xE7, 0xEA, 0xF0, 0xF8, 0xF9, 0xF4, 0xED, -0xE9, 0xE5, 0xE3, 0xE3, 0xE6, 0xEB, 0xF6, 0x76, 0x6C, 0x67, 0x62, 0x5F, 0x61, 0x64, 0x69, 0x6F, -0x73, 0x73, 0x75, 0x75, 0x70, 0x6E, 0x6F, 0x6E, 0x6E, 0x75, 0xFE, 0xF3, 0xEC, 0xE9, 0xE9, 0xE9, -0xED, 0xFA, 0x7A, 0x74, 0x71, 0x72, 0x73, 0x74, 0x7A, 0xFF, 0xFF, 0x7B, 0x72, 0x6E, 0x6D, 0x6C, -0x6C, 0x6E, 0x73, 0x7C, 0xFC, 0xF7, 0xF0, 0xEB, 0xE6, 0xE0, 0xDD, 0xDB, 0xDB, 0xDB, 0xDC, 0xDF, -0xE2, 0xE3, 0xE8, 0xF2, 0x7D, 0x6C, 0x61, 0x5D, 0x5A, 0x58, 0x59, 0x5C, 0x5E, 0x61, 0x66, 0x69, -0x6A, 0x6B, 0x6C, 0x6B, 0x6C, 0x75, 0xFE, 0xF7, 0xEE, 0xEA, 0xE8, 0xE6, 0xE9, 0xEC, 0xEB, 0xEB, -0xEE, 0xF0, 0xF2, 0xF6, 0xF9, 0xFD, 0x7F, 0xFF, 0x7C, 0x74, 0x6D, 0x69, 0x66, 0x64, 0x64, 0x68, -0x6B, 0x6D, 0x75, 0x7D, 0xFC, 0xF8, 0xFA, 0xFC, 0xFA, 0xF7, 0xF5, 0xF0, 0xEB, 0xE6, 0xE4, 0xE5, -0xE9, 0xED, 0xF2, 0xFC, 0x7A, 0x74, 0x6E, 0x6C, 0x6A, 0x69, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6E, -0x6D, 0x6E, 0x70, 0x71, 0x74, 0x79, 0x7B, 0x7E, 0xFB, 0xF6, 0xF8, 0xF9, 0xF6, 0xF5, 0xF8, 0xFA, -0xF7, 0xF0, 0xEE, 0xF0, 0xF7, 0xFC, 0x7D, 0x73, 0x6C, 0x6B, 0x6B, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, -0x6C, 0x6D, 0x6D, 0x6E, 0x71, 0x71, 0x71, 0x72, 0x77, 0x7D, 0xFC, 0xF7, 0xF3, 0xED, 0xEB, 0xEB, -0xEE, 0xF4, 0xF9, 0xFE, 0x79, 0x77, 0x7B, 0x7F, 0x7F, 0xFF, 0xFF, 0x7B, 0x77, 0x76, 0x77, 0x79, -0x79, 0x7A, 0x7C, 0xFF, 0xFC, 0xFC, 0xFA, 0xF8, 0xF9, 0xF9, 0xFA, 0xFD, 0xFE, 0xFD, 0xFA, 0xF8, -0xF5, 0xF2, 0xF1, 0xF2, 0xF4, 0xF8, 0xFE, 0x7C, 0x79, 0x77, 0x77, 0x7A, 0x7E, 0x7F, 0x7C, 0x79, -0x78, 0x75, 0x73, 0x72, 0x71, 0x73, 0x75, 0x76, 0x78, 0x7D, 0xFD, 0xFB, 0xFC, 0xFF, 0x7D, 0x79, -0x76, 0x76, 0x77, 0x7C, 0xFB, 0xF5, 0xF3, 0xF3, 0xF3, 0xF7, 0x7D, 0x74, 0x6F, 0x6D, 0x6B, 0x6C, -0x6E, 0x73, 0x77, 0x77, 0x77, 0x7A, 0x7A, 0x76, 0x73, 0x72, 0x6F, 0x6D, 0x6B, 0x6C, 0x6F, 0x76, -0x7A, 0x7D, 0xFE, 0xFC, 0x7E, 0x7A, 0x75, 0x71, 0x6E, 0x6D, 0x6E, 0x71, 0x77, 0x7D, 0xFE, 0xFC, -0xFC, 0xFE, 0xFF, 0x7D, 0x7B, 0x7C, 0x7E, 0xFF, 0xFF, 0xFC, 0xF9, 0xF7, 0xF6, 0xF8, 0xFA, 0xFC, -0xFF, 0x7E, 0x7F, 0xFF, 0xFD, 0xFB, 0xF7, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFD, 0x7D, 0x7A, 0x7A, -0x7D, 0x7E, 0x7F, 0xFF, 0xFE, 0xFE, 0x7E, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0xFF, 0xFD, 0xFD, 0xFD, -0xFB, 0xF8, 0xF7, 0xF9, 0xFD, 0x7C, 0x79, 0x78, 0x78, 0x77, 0x77, 0x76, 0x75, 0x75, 0x75, 0x75, -0x75, 0x74, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F, 0xFD, 0xFB, 0xFF, 0x7A, 0x79, 0x7D, 0xFE, 0xFD, -0xFD, 0xFC, 0xFD, 0xFA, 0xF5, 0xF2, 0xF3, 0xF6, 0xF8, 0xF9, 0xFC, 0xFF, 0xFD, 0xF8, 0xF5, 0xF5, -0xF6, 0xFA, 0xFC, 0xFD, 0x7F, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x77, 0x7B, 0x7E, 0x7E, 0x7E, 0x7F, -0x7E, 0x7C, 0x79, 0x76, 0x76, 0x78, 0x79, 0x78, 0x7B, 0x7E, 0xFF, 0x7F, 0x7C, 0x7A, 0x78, 0x75, -0x73, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7B, 0x7B, 0x79, 0x79, 0x78, 0x77, 0x78, 0x74, 0x71, 0x72, -0x73, 0x76, 0x77, 0x77, 0x79, 0x79, 0x77, 0x76, 0x75, 0x76, 0x78, 0x7B, 0x7F, 0xFC, 0xFB, 0xFB, -0xFA, 0xFB, 0xFE, 0x7C, 0x79, 0x79, 0x78, 0x78, 0x7B, 0xFF, 0xFD, 0xFD, 0xFC, 0xFC, 0xFE, 0x7E, -0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x78, 0x79, 0x7A, 0x7C, 0x7C, 0x7C, 0x7B, 0x77, 0x78, 0x7A, -0x7A, 0x7A, 0x7C, 0x7E, 0xFF, 0x7F, 0x7F, 0xFF, 0xFF, 0x7F, 0x7E, 0x7F, 0x7E, 0x7C, 0x7A, 0x7A, -0x79, 0x79, 0x7B, 0x7E, 0x7E, 0x7F, 0xFD, 0xFC, 0xFD, 0xFD, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, -0xFF, 0x7E, 0x7D, 0x7F, 0xFF, 0xFF, 0xFE, 0xFC, 0xFB, 0xFC, 0xFC, 0xFE, 0x7E, 0x7F, 0x7E, 0x7B, -0x7C, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x77, 0x7A, -0x7C, 0x7D, 0x7E, 0x7E, 0x7C, 0x79, 0x76, 0x77, 0x79, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0x7D, -0x7D, 0x7C, 0x7A, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7C, 0x7B, 0x7C, -0x7C, 0x7C, 0x7D, 0xFF, 0xFC, 0xFB, 0xFB, 0xFC, 0xFF, 0x7C, 0x79, 0x77, 0x74, 0x75, 0x77, 0x7A, -0x7D, 0x7E, 0xFF, 0xFD, 0xFD, 0xFE, 0x7E, 0x7D, 0x7D, 0x7B, 0x7A, 0x7B, 0x7D, 0x7E, 0xFE, 0xFD, -0xFF, 0x7F, 0x7D, 0x7B, 0x7A, 0x78, 0x76, 0x76, 0x79, 0x7C, 0x7E, 0xFF, 0xFF, 0x7F, 0x7E, 0x7C, -0x7A, 0x79, 0x78, 0x78, 0x77, 0x78, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7B, 0x7A, -0x7B, 0x7C, 0x7D, 0xFE, 0xFC, 0xFA, 0xF9, 0xFA, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFA, 0xF8, 0xF7, -0xF6, 0xF7, 0xF9, 0xFB, 0xFE, 0x7E, 0x7C, 0x7A, 0x79, 0x79, 0x7B, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, -0x7E, 0x7C, 0x7B, 0x79, 0x7A, 0x7C, 0x7C, 0x7E, 0x7E, 0x7F, 0xFF, 0x7F, 0x7E, 0x7C, 0x7B, 0x79, -0x77, 0x76, 0x76, 0x78, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7B, -0x7D, 0x79, 0x79, 0x7E, 0x79, 0x78, 0x79, 0x78, 0x7A, 0x78, 0x7D, 0x7D, 0x7C, 0xFF, 0x7D, 0x7F, -0x7E, 0x7B, 0x7A, 0x76, 0x79, 0x76, 0x78, 0x7B, 0x79, 0xFF, 0x7D, 0xFB, 0xFD, 0x7D, 0xF9, 0x7B, -0xFF, 0x79, 0x75, 0xFF, 0x6E, 0x7F, 0x71, 0x73, 0x79, 0x51, 0x60, 0xE2, 0x6F, 0x7D, 0xF7, 0xF5, -0x77, 0x72, 0xE4, 0x50, 0x4C, 0xDE, 0xFB, 0x66, 0xE5, 0xF6, 0x6F, 0x6C, 0xE7, 0xDC, 0x6E, 0xFD, -0xE6, 0x7E, 0xE9, 0x77, 0x62, 0xE3, 0xE9, 0x6C, 0x71, 0xEA, 0xDE, 0x75, 0x7E, 0xE8, 0x75, 0xF8, -0x7A, 0xFC, 0xF2, 0x65, 0xF8, 0xEE, 0xF9, 0xF4, 0x73, 0xE4, 0xF9, 0x6E, 0xE5, 0x75, 0x67, 0x5A, -0x50, 0x57, 0x5A, 0x7D, 0x6C, 0x64, 0xDA, 0xED, 0x64, 0xEA, 0xE9, 0xF5, 0x7E, 0x6F, 0xFC, 0xF2, -0x66, 0x63, 0xF7, 0x7D, 0x78, 0xF6, 0xF8, 0xE1, 0xE2, 0xF9, 0xEB, 0xE4, 0xED, 0xFE, 0xFA, 0xF6, -0xF1, 0xF2, 0x6F, 0xFE, 0xE8, 0xFF, 0x7F, 0xEA, 0xE8, 0xED, 0xEE, 0xEA, 0xEB, 0xED, 0xF8, 0xEF, -0xE8, 0x7F, 0xF5, 0xE6, 0xF7, 0xF2, 0xF3, 0xF2, 0xDF, 0xEB, 0xF5, 0xE3, 0xEC, 0xF2, 0xEF, 0xFC, -0xED, 0xEF, 0xFD, 0xED, 0xF0, 0x7C, 0x77, 0xFE, 0xF1, 0x77, 0x79, 0xEF, 0xF6, 0xF8, 0xFB, 0x72, -0x75, 0x79, 0x78, 0x70, 0x6E, 0x7C, 0x7D, 0x74, 0x79, 0x77, 0x6E, 0x71, 0x7D, 0x6F, 0x6B, 0x7D, -0xFE, 0x76, 0x7A, 0x7D, 0x7F, 0x75, 0x6C, 0x7B, 0xF7, 0x7A, 0x7B, 0x7E, 0x74, 0x79, 0x75, 0x70, -0x7C, 0x79, 0xFD, 0xF6, 0x6E, 0x6E, 0x7B, 0x6B, 0x67, 0x6D, 0x6E, 0x6A, 0x67, 0x6A, 0x6D, 0x6F, -0x6B, 0x67, 0x6D, 0x6C, 0x65, 0x63, 0x5E, 0x65, 0x6A, 0x5F, 0x68, 0x6C, 0x60, 0x6C, 0x7D, 0x6D, -0x6E, 0x7D, 0x7C, 0xFF, 0xEE, 0xEB, 0xEC, 0xE3, 0xDE, 0xE1, 0xE4, 0xE0, 0xE8, 0xF8, 0xEE, 0xFA, -0x6D, 0x69, 0x5C, 0x58, 0x59, 0x53, 0x50, 0x4E, 0x4D, 0x4E, 0x4F, 0x4F, 0x53, 0x51, 0x51, 0x54, -0x55, 0x56, 0x5A, 0x5E, 0x69, 0xF7, 0xE2, 0xD8, 0xCF, 0xCE, 0xCD, 0xCC, 0xCE, 0xCE, 0xCA, 0xC7, -0xC2, 0xBF, 0xBE, 0xBD, 0xC2, 0xD5, 0xF1, 0x62, 0x4B, 0x3F, 0x3D, 0x3D, 0x3E, 0x40, 0x42, 0x4A, -0x51, 0x51, 0x51, 0x55, 0x56, 0x4F, 0x4D, 0x4E, 0x50, 0x52, 0x55, 0x5C, 0x69, 0xFA, 0xE4, 0xD9, -0xCD, 0xC9, 0xC5, 0xC1, 0xBF, 0xBB, 0xBB, 0xBD, 0xB9, 0xB7, 0xB5, 0xB7, 0xC8, 0xDD, 0x61, 0x43, -0x3C, 0x38, 0x38, 0x3D, 0x40, 0x46, 0x4E, 0x5A, 0x60, 0x5D, 0x5B, 0x5B, 0x5A, 0x58, 0x56, 0x58, -0x5C, 0x5E, 0x5E, 0x64, 0x6B, 0x6D, 0xF9, 0xED, 0xE5, 0xDC, 0xD9, 0xD2, 0xCC, 0xC5, 0xBF, 0xBF, -0xBA, 0xB7, 0xB6, 0xB1, 0xB7, 0xCB, 0xFA, 0x49, 0x3A, 0x35, 0x31, 0x33, 0x3A, 0x42, 0x4C, 0x5C, -0xFD, 0xE1, 0xEE, 0x68, 0x66, 0x5C, 0x51, 0x4F, 0x4D, 0x55, 0x5F, 0x5D, 0x64, 0x71, 0xFA, 0xED, -0xEF, 0xEB, 0xDE, 0xD3, 0xCC, 0xC3, 0xBE, 0xBD, 0xB9, 0xB8, 0xB5, 0xAF, 0xB7, 0xCC, 0x60, 0x3F, -0x39, 0x34, 0x2F, 0x31, 0x3A, 0x47, 0x56, 0x6F, 0xE3, 0xDA, 0xD5, 0xEC, 0x56, 0x50, 0x4E, 0x4B, -0x4B, 0x4C, 0x57, 0x7B, 0xE7, 0xE0, 0xDC, 0xD5, 0xD6, 0xDE, 0xEC, 0xEF, 0xDE, 0xD7, 0xCC, 0xC1, -0xBD, 0xB8, 0xB5, 0xB4, 0xB0, 0xB6, 0xCB, 0x58, 0x3E, 0x38, 0x34, 0x2F, 0x2F, 0x38, 0x45, 0x59, -0x7D, 0xDD, 0xD1, 0xCE, 0xDD, 0x63, 0x53, 0x4E, 0x4C, 0x4A, 0x4B, 0x53, 0x69, 0xE7, 0xDB, 0xD5, -0xD0, 0xCE, 0xD3, 0xDC, 0xE4, 0xEA, 0xE8, 0xE2, 0xD5, 0xC8, 0xC0, 0xBB, 0xB8, 0xB5, 0xB2, 0xB9, -0xD1, 0x54, 0x40, 0x3A, 0x34, 0x2F, 0x2F, 0x36, 0x3F, 0x4C, 0x5B, 0x7E, 0xDE, 0xD4, 0xDB, 0xFF, -0x62, 0x58, 0x55, 0x53, 0x4F, 0x52, 0x5D, 0x6F, 0xF0, 0xE4, 0xDE, 0xDA, 0xD8, 0xDC, 0xE1, 0xE5, -0xDF, 0xDC, 0xD6, 0xCC, 0xC6, 0xC1, 0xBE, 0xBB, 0xB8, 0xBC, 0xCE, 0x67, 0x4B, 0x44, 0x3E, 0x38, -0x35, 0x37, 0x3D, 0x45, 0x4B, 0x53, 0x61, 0xFA, 0xE4, 0xEC, 0x7F, 0x79, 0x78, 0x79, 0x70, 0x68, -0x68, 0x70, 0x7B, 0x7D, 0x7D, 0xF9, 0xEE, 0xEA, 0xE8, 0xE6, 0xE1, 0xDB, 0xD6, 0xCF, 0xCB, 0xC8, -0xC5, 0xC1, 0xBE, 0xBF, 0xC7, 0xD5, 0xF9, 0x5F, 0x55, 0x4A, 0x42, 0x3E, 0x3E, 0x41, 0x45, 0x48, -0x4C, 0x56, 0x5F, 0x6F, 0xF8, 0xEF, 0xE7, 0xE4, 0xE6, 0xE2, 0xEA, 0xF5, 0xFD, 0x75, 0x75, 0x79, -0x75, 0x76, 0x7E, 0xFA, 0xF1, 0xE9, 0xE0, 0xDA, 0xD3, 0xCF, 0xCC, 0xCA, 0xC7, 0xC4, 0xC5, 0xCC, -0xD3, 0xDF, 0xFF, 0x69, 0x58, 0x4C, 0x47, 0x43, 0x42, 0x42, 0x43, 0x46, 0x4C, 0x55, 0x59, 0x62, -0x77, 0xFD, 0xEE, 0xEC, 0xF3, 0xEC, 0xF2, 0x76, 0x76, 0x6F, 0x6E, 0x79, 0x79, 0x7F, 0xF3, 0xED, -0xE4, 0xDD, 0xDB, 0xD7, 0xD3, 0xD0, 0xCE, 0xCC, 0xCB, 0xCA, 0xCC, 0xD1, 0xD6, 0xDD, 0xEA, 0x7E, -0x62, 0x56, 0x4F, 0x4C, 0x49, 0x48, 0x49, 0x4C, 0x50, 0x56, 0x58, 0x5C, 0x67, 0x6F, 0x6C, 0x6E, -0x6D, 0x6A, 0x6F, 0x6D, 0x6B, 0x6E, 0x6D, 0x76, 0xFA, 0xF9, 0xF1, 0xEB, 0xE7, 0xE1, 0xDE, 0xDD, -0xDB, 0xD8, 0xD7, 0xD4, 0xD2, 0xD0, 0xCF, 0xD4, 0xD8, 0xDA, 0xDF, 0xEA, 0xF8, 0x6D, 0x60, 0x5C, -0x58, 0x56, 0x56, 0x56, 0x57, 0x59, 0x5B, 0x5B, 0x5A, 0x5C, 0x5F, 0x5F, 0x5F, 0x63, 0x66, 0x6B, -0x72, 0x77, 0x7E, 0xF7, 0xF0, 0xEC, 0xE9, 0xE9, 0xE8, 0xE8, 0xE8, 0xE8, 0xEA, 0xE7, 0xE6, 0xE5, -0xE3, 0xE1, 0xDD, 0xDA, 0xD9, 0xD8, 0xDD, 0xDF, 0xDD, 0xE0, 0xE7, 0xEE, 0xFE, 0x74, 0x6C, 0x62, -0x5C, 0x59, 0x56, 0x54, 0x52, 0x51, 0x51, 0x52, 0x55, 0x58, 0x59, 0x5D, 0x62, 0x68, 0x70, 0x75, -0x7D, 0xF9, 0xF8, 0xF4, 0xF5, 0xF9, 0xF5, 0xF3, 0xF3, 0xF2, 0xF2, 0xEF, 0xED, 0xEB, 0xEA, 0xE6, -0xE3, 0xE0, 0xDC, 0xDA, 0xD7, 0xD8, 0xDB, 0xDB, 0xDA, 0xDC, 0xDF, 0xE8, 0xF6, 0x7F, 0x6E, 0x60, -0x5B, 0x58, 0x56, 0x54, 0x53, 0x52, 0x53, 0x59, 0x5C, 0x5E, 0x63, 0x68, 0x6F, 0x7B, 0x7C, 0xFF, -0xFC, 0xFC, 0xFB, 0xFC, 0xFD, 0xF8, 0xF7, 0xF6, 0xF4, 0xF5, 0xF3, 0xF0, 0xEF, 0xED, 0xEB, 0xE8, -0xE4, 0xE0, 0xDD, 0xDA, 0xD9, 0xDA, 0xDC, 0xDC, 0xDC, 0xE0, 0xE9, 0xF8, 0x76, 0x6B, 0x61, 0x5C, -0x5A, 0x5A, 0x5A, 0x5B, 0x5C, 0x5E, 0x62, 0x67, 0x6B, 0x6D, 0x6F, 0x72, 0x75, 0x76, 0x76, 0x75, -0x74, 0x79, 0x7E, 0x7B, 0x78, 0x7D, 0xFB, 0xFB, 0xFD, 0xFA, 0xF6, 0xF5, 0xF2, 0xEF, 0xEC, 0xE9, -0xE4, 0xDF, 0xDE, 0xDE, 0xE0, 0xE7, 0xEA, 0xEC, 0xF2, 0xFE, 0x76, 0x6F, 0x6F, 0x6D, 0x6A, 0x6A, -0x6B, 0x6C, 0x6C, 0x69, 0x67, 0x67, 0x66, 0x65, 0x64, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6B, 0x6E, -0x71, 0x76, 0x7A, 0x7D, 0xFE, 0xFC, 0xFA, 0xF8, 0xF4, 0xF0, 0xEE, 0xEC, 0xEA, 0xE9, 0xE8, 0xE9, -0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xED, 0xEB, 0xE9, 0xEA, 0xE9, 0xE9, 0xEA, 0xEC, 0xF0, 0xF6, 0xFD, -0x78, 0x71, 0x6D, 0x69, 0x67, 0x66, 0x65, 0x65, 0x66, 0x67, 0x66, 0x66, 0x67, 0x67, 0x66, 0x66, -0x69, 0x6B, 0x6D, 0x73, 0x7A, 0xFE, 0xF7, 0xF3, 0xEF, 0xEE, 0xEE, 0xEC, 0xEB, 0xEA, 0xE8, 0xE7, -0xE5, 0xE3, 0xE1, 0xE1, 0xE1, 0xE3, 0xE6, 0xE8, 0xEB, 0xEF, 0xF8, 0x7E, 0x75, 0x6F, 0x6B, 0x69, -0x67, 0x66, 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, -0x70, 0x6F, 0x6F, 0x6F, 0x72, 0x75, 0x79, 0xFF, 0xFB, 0xF6, 0xF2, 0xEF, 0xEE, 0xED, 0xEE, 0xF0, -0xF3, 0xF6, 0xF8, 0xFC, 0xFF, 0x7D, 0x79, 0x76, 0x72, 0x6E, 0x6D, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, -0x6E, 0x71, 0x76, 0x7B, 0xFF, 0xFE, 0xFD, 0xFC, 0xFD, 0x7F, 0x7B, 0x78, 0x75, 0x74, 0x75, 0x77, -0x7A, 0x7E, 0xFC, 0xF9, 0xF6, 0xF5, 0xF4, 0xF4, 0xF5, 0xF7, 0xF8, 0xF8, 0xF7, 0xF6, 0xF5, 0xF5, -0xF6, 0xF8, 0xFA, 0xFC, 0x7F, 0x7B, 0x79, 0x78, 0x77, 0x77, 0x79, 0x7D, 0xFD, 0xF9, 0xF4, 0xF2, -0xF1, 0xF1, 0xF3, 0xF5, 0xFA, 0xFE, 0x7F, 0xFF, 0xFD, 0xFC, 0xFB, 0xF7, 0xF5, 0xF4, 0xF4, 0xF6, -0xF8, 0xFB, 0xFD, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7E, 0x7B, 0x79, 0x77, -0x76, 0x75, 0x74, 0x74, 0x75, 0x78, 0x7C, 0x7F, 0xFE, 0xFD, 0xFE, 0xFF, 0x7E, 0x7C, 0x7B, 0x79, -0x79, 0x7A, 0x7D, 0xFE, 0xFC, 0xFA, 0xF8, 0xF8, 0xF9, 0xFB, 0xFD, 0x7F, 0x7B, 0x78, 0x76, 0x75, -0x74, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x7A, 0x7C, -0x7E, 0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7C, 0x7A, 0x78, 0x77, 0x77, 0x76, 0x79, 0x7B, 0x7E, 0xFF, -0xFE, 0xFD, 0xFD, 0xFF, 0x7E, 0x7C, 0x7C, 0x7B, 0x79, 0x78, 0x78, 0x78, 0x77, 0x75, 0x74, 0x74, -0x74, 0x74, 0x75, 0x76, 0x79, 0x7C, 0x7E, 0xFF, 0xFC, 0xF8, 0xF7, 0xF6, 0xF7, 0xF7, 0xF7, 0xF9, -0xFC, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7F, 0xFF, 0xFC, 0xFB, 0xFA, 0xF8, 0xF8, 0xF8, 0xF6, 0xF5, -0xF5, 0xF4, 0xF4, 0xF5, 0xF7, 0xF8, 0xFA, 0xFD, 0x7F, 0x7E, 0x7D, 0x7C, 0x7C, 0x7D, 0x7E, 0xFE, -0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF9, 0xF9, 0xFA, 0xFB, 0xFD, 0xFF, 0x7E, 0x7D, 0x79, 0x77, 0x75, -0x72, 0x71, 0x70, 0x70, 0x70, 0x71, 0x71, 0x70, 0x71, 0x72, 0x71, 0x72, 0x71, 0x72, 0x73, 0x72, -0x72, 0x71, 0x71, 0x71, 0x72, 0x74, 0x74, 0x76, 0x77, 0x77, 0x77, 0x78, 0x77, 0x78, 0x78, 0x78, -0x7A, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFD, 0xFF, 0x7E, 0x7D, 0x7B, 0x7C, 0x7B, -0x7B, 0x7B, 0x7B, 0x7B, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7B, 0x7C, 0x7E, 0xFF, 0xFD, -0xFB, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0xFF, 0x7F, 0x7D, 0x7C, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7D, -0x7F, 0x7F, 0xFF, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, -0xFF, 0xFF, 0xFF, 0xFD, 0xFA, 0xF9, 0xF7, 0xF5, 0xF4, 0xF3, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, -0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xFA, 0xFC, 0xFE, 0xFE, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, -0x7F, 0x7D, 0x7C, 0x7D, 0x7B, 0x7A, 0x7D, 0x7B, 0x7B, 0x7C, 0x7B, 0x7B, 0x7B, 0x79, 0x78, 0x77, -0x77, 0x76, 0x75, 0x76, 0x75, 0x74, 0x74, 0x75, 0x74, 0x73, 0x74, 0x74, 0x74, 0x74, 0x73, 0x74, -0x72, 0x70, 0x70, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x71, 0x72, 0x73, 0x73, 0x76, -0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7C, 0x7B, 0x7D, 0x7E, 0x7D, 0x7F, 0x7D, 0x7C, 0x7B, 0x7B, 0x7C, -0x7B, 0x7B, 0x7A, 0x78, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0xFF, 0x7D, 0x7E, 0x7C, 0x7C, 0x7B, 0x78, -0x7C, 0x7A, 0x7C, 0x7D, 0x7B, 0x7E, 0x7C, 0x7F, 0x7D, 0x7A, 0x7E, 0x78, 0x7B, 0x6F, 0x68, 0x5C, -0x4C, 0x58, 0xDD, 0xE1, 0xEB, 0xE1, 0xE9, 0xEF, 0xF8, 0xE9, 0xE1, 0xED, 0xF9, 0xFC, 0xF2, 0xED, -0x7D, 0x76, 0xFB, 0xF9, 0x7C, 0x70, 0x6F, 0x78, 0x79, 0x6D, 0x6D, 0x76, 0x7D, 0xFE, 0x7A, 0x79, -0xFC, 0xFD, 0xFD, 0xFA, 0xFB, 0xFA, 0xFC, 0xFF, 0xFC, 0xFC, 0xFE, 0xFD, 0xFE, 0xFB, 0xFB, 0xFE, -0xFE, 0x7E, 0x7D, 0x7C, 0x7C, 0x7B, 0x78, 0x7D, 0x7D, 0xFF, 0xFA, 0xFF, 0xF9, 0xF9, 0xFA, 0xF7, -0x7F, 0xF9, 0x7A, 0x7A, 0x6F, 0x51, 0x52, 0x6E, 0x71, 0xF1, 0xD8, 0xD3, 0xDB, 0xEF, 0xE2, 0xDB, -0xED, 0xF6, 0xF3, 0xEF, 0xF4, 0x6A, 0x74, 0x73, 0x57, 0x5E, 0x77, 0x6E, 0xFD, 0xF4, 0xFC, 0x7D, -0x73, 0xE9, 0xDE, 0xE2, 0xDE, 0xE3, 0xE5, 0xE8, 0xFA, 0xF7, 0xFA, 0x6F, 0x6C, 0x6A, 0x6B, 0x6A, -0x66, 0x6B, 0x75, 0x70, 0x74, 0xFA, 0xFB, 0xFD, 0xF8, 0xF3, 0xF2, 0xFB, 0xF5, 0xED, 0xEF, 0xEB, -0xE5, 0xDF, 0xDA, 0xD7, 0xD5, 0xD3, 0xD3, 0xD4, 0xD8, 0xE0, 0xEE, 0x76, 0x5D, 0x53, 0x4D, 0x49, -0x46, 0x43, 0x42, 0x43, 0x43, 0x45, 0x48, 0x4B, 0x4F, 0x57, 0x60, 0x6D, 0xF7, 0xE3, 0xDB, 0xD4, -0xCF, 0xCB, 0xC8, 0xC4, 0xBE, 0xBC, 0xB9, 0xB4, 0xB6, 0xBC, 0xC1, 0xC3, 0xD5, 0x4F, 0x4B, 0x43, -0x36, 0x33, 0x34, 0x32, 0x31, 0x36, 0x3B, 0x3F, 0x48, 0x60, 0xDF, 0xD2, 0xC9, 0xC1, 0xC3, 0xC6, -0xC7, 0xCF, 0xE3, 0x67, 0x56, 0x47, 0x3E, 0x3F, 0x3C, 0x3B, 0x43, 0x48, 0x4F, 0xFA, 0xD5, 0xC5, -0xBD, 0xB7, 0xB1, 0xAE, 0xAC, 0xAE, 0xB1, 0xB5, 0xC0, 0xD7, 0x5D, 0x40, 0x38, 0x31, 0x2E, 0x2D, -0x2C, 0x2E, 0x32, 0x37, 0x3F, 0x4F, 0xF8, 0xD0, 0xC4, 0xBC, 0xBB, 0xBB, 0xBB, 0xBE, 0xC7, 0xD0, -0xF1, 0x56, 0x48, 0x40, 0x3D, 0x3B, 0x3D, 0x42, 0x49, 0x63, 0xDA, 0xCB, 0xBC, 0xB6, 0xB0, 0xAE, -0xAF, 0xAF, 0xB4, 0xBC, 0xC6, 0xFE, 0x4C, 0x3E, 0x34, 0x30, 0x2E, 0x2D, 0x2E, 0x30, 0x35, 0x3C, -0x46, 0x62, 0xDE, 0xCA, 0xBE, 0xBD, 0xBB, 0xBA, 0xBD, 0xC3, 0xCB, 0xDC, 0x6B, 0x59, 0x51, 0x52, -0x60, 0xFF, 0xD5, 0xC4, 0xBE, 0xBC, 0xBA, 0xB9, 0xBB, 0xC3, 0xC8, 0xD6, 0x5A, 0x4C, 0x42, 0x39, -0x36, 0x35, 0x32, 0x34, 0x36, 0x39, 0x41, 0x47, 0x58, 0xE0, 0xDA, 0xC9, 0xC1, 0xC4, 0xC0, 0xC0, -0xC6, 0xCA, 0xD1, 0xDC, 0xF3, 0x6E, 0xFC, 0xE8, 0xDB, 0xCB, 0xC2, 0xBE, 0xBD, 0xBE, 0xBD, 0xC0, -0xCD, 0xCE, 0xEC, 0x4E, 0x4C, 0x40, 0x38, 0x39, 0x37, 0x35, 0x38, 0x38, 0x3B, 0x43, 0x49, 0x54, -0xEA, 0xDB, 0xCE, 0xC5, 0xC5, 0xC3, 0xC1, 0xC7, 0xCA, 0xCF, 0xDB, 0xE9, 0xFD, 0xF7, 0xE9, 0xDD, -0xCE, 0xC6, 0xC1, 0xBF, 0xC0, 0xBF, 0xC3, 0xCF, 0xD0, 0xE6, 0x51, 0x4E, 0x43, 0x3A, 0x3B, 0x38, -0x35, 0x38, 0x38, 0x3A, 0x3F, 0x44, 0x4E, 0x5F, 0xEF, 0xD7, 0xCD, 0xC8, 0xC6, 0xC4, 0xC6, 0xC9, -0xCC, 0xD4, 0xDD, 0xE6, 0xE1, 0xDA, 0xD7, 0xCA, 0xC3, 0xC4, 0xC4, 0xC1, 0xC3, 0xCC, 0xCF, 0xD8, -0x60, 0x54, 0x4E, 0x3F, 0x3D, 0x3D, 0x39, 0x3A, 0x3C, 0x3B, 0x3D, 0x47, 0x48, 0x51, 0x79, 0xFA, -0xDD, 0xCD, 0xCF, 0xCC, 0xC8, 0xCD, 0xCE, 0xCE, 0xD7, 0xDC, 0xDC, 0xD7, 0xD4, 0xCC, 0xC3, 0xC1, -0xBF, 0xBF, 0xC2, 0xC1, 0xCE, 0xDB, 0xDD, 0x54, 0x4C, 0x49, 0x3C, 0x3B, 0x3B, 0x38, 0x3A, 0x3B, -0x3C, 0x3E, 0x48, 0x4C, 0x55, 0xF3, 0xEC, 0xDB, 0xCD, 0xD1, 0xCD, 0xCB, 0xD2, 0xD1, 0xD6, 0xDD, -0xE1, 0xE2, 0xD7, 0xD3, 0xCA, 0xC0, 0xBE, 0xBE, 0xBF, 0xBE, 0xC1, 0xCF, 0xD0, 0xED, 0x4E, 0x4D, -0x41, 0x3A, 0x3A, 0x37, 0x35, 0x38, 0x3A, 0x3A, 0x3F, 0x49, 0x4A, 0x62, 0xE7, 0xEA, 0xCE, 0xCB, -0xCF, 0xC8, 0xCB, 0xD1, 0xCF, 0xD7, 0xE2, 0xE5, 0xE5, 0xDF, 0xD5, 0xCC, 0xC5, 0xBE, 0xBE, 0xC0, -0xBC, 0xC0, 0xCD, 0xC8, 0xDC, 0x58, 0x5B, 0x47, 0x3C, 0x3D, 0x39, 0x36, 0x39, 0x3A, 0x39, 0x3E, -0x47, 0x49, 0x59, 0xEA, 0xEC, 0xD0, 0xC9, 0xCD, 0xC7, 0xC8, 0xCE, 0xCD, 0xD2, 0xDC, 0xDF, 0xDF, -0xDD, 0xD6, 0xCB, 0xC6, 0xBF, 0xBF, 0xC4, 0xBD, 0xC4, 0xD1, 0xC9, 0xEF, 0x55, 0x5D, 0x43, 0x3D, -0x3E, 0x38, 0x37, 0x3A, 0x3A, 0x3A, 0x3E, 0x45, 0x48, 0x56, 0x74, 0xF8, 0xD8, 0xCF, 0xD0, 0xCB, -0xCB, 0xCE, 0xCE, 0xD2, 0xD9, 0xDC, 0xDC, 0xD7, 0xD3, 0xCA, 0xC4, 0xC0, 0xBF, 0xC2, 0xBE, 0xC3, -0xCF, 0xCB, 0xE5, 0x58, 0x5C, 0x45, 0x3D, 0x3E, 0x39, 0x37, 0x39, 0x3A, 0x3A, 0x3D, 0x45, 0x46, -0x52, 0x79, 0x73, 0xD7, 0xCD, 0xD1, 0xC8, 0xC8, 0xCD, 0xCA, 0xCF, 0xD5, 0xD7, 0xDC, 0xDA, 0xD4, -0xCD, 0xC7, 0xC1, 0xC0, 0xC5, 0xBF, 0xC2, 0xD1, 0xCA, 0xDA, 0x5A, 0x68, 0x4C, 0x3E, 0x42, 0x3B, -0x38, 0x3B, 0x3A, 0x3A, 0x3D, 0x42, 0x44, 0x4B, 0x62, 0x66, 0xF1, 0xD0, 0xD6, 0xCD, 0xC8, 0xCE, -0xCA, 0xCB, 0xD1, 0xCF, 0xD2, 0xD2, 0xCE, 0xC9, 0xC5, 0xC1, 0xBF, 0xC5, 0xC1, 0xC0, 0xD3, 0xCE, -0xD6, 0x57, 0x5F, 0x4E, 0x3E, 0x41, 0x3D, 0x39, 0x3B, 0x3B, 0x3A, 0x3D, 0x42, 0x44, 0x4C, 0x5A, -0x5E, 0xEC, 0xDD, 0xDC, 0xCE, 0xD0, 0xD1, 0xCD, 0xD4, 0xD4, 0xD4, 0xDA, 0xD3, 0xCE, 0xCB, 0xC4, -0xBF, 0xBE, 0xC2, 0xBE, 0xBF, 0xCD, 0xCB, 0xD5, 0x5B, 0x5F, 0x4D, 0x3E, 0x3F, 0x3B, 0x37, 0x39, -0x3A, 0x39, 0x3C, 0x3F, 0x42, 0x4B, 0x59, 0x61, 0xEA, 0xDA, 0xD6, 0xCD, 0xCD, 0xCE, 0xCD, 0xCF, -0xD3, 0xD6, 0xDA, 0xD7, 0xD2, 0xCE, 0xC7, 0xC2, 0xBF, 0xC0, 0xC1, 0xBE, 0xC8, 0xCE, 0xCB, 0x79, -0x5A, 0x5C, 0x41, 0x3F, 0x3E, 0x38, 0x39, 0x3A, 0x39, 0x3A, 0x3E, 0x42, 0x45, 0x51, 0x62, 0x65, -0xDD, 0xD5, 0xD7, 0xCA, 0xCC, 0xCE, 0xCA, 0xCE, 0xD0, 0xCF, 0xCF, 0xCD, 0xC9, 0xC4, 0xBF, 0xBD, -0xBE, 0xBF, 0xBD, 0xC5, 0xCE, 0xCB, 0xF8, 0x58, 0x5B, 0x43, 0x3E, 0x3E, 0x39, 0x38, 0x3A, 0x39, -0x3A, 0x3E, 0x44, 0x45, 0x51, 0x6E, 0x69, 0xDC, 0xCF, 0xD7, 0xCA, 0xCB, 0xD0, 0xCC, 0xD1, 0xD5, -0xD5, 0xD5, 0xCF, 0xCB, 0xC5, 0xBF, 0xBC, 0xBD, 0xBF, 0xBB, 0xC3, 0xCC, 0xC8, 0xEF, 0x5B, 0x5D, -0x41, 0x3D, 0x3D, 0x37, 0x37, 0x38, 0x37, 0x38, 0x3B, 0x41, 0x43, 0x4E, 0x6B, 0x67, 0xDC, 0xCE, -0xD4, 0xC9, 0xC8, 0xCE, 0xCA, 0xCE, 0xD5, 0xD4, 0xD5, 0xD3, 0xCE, 0xC7, 0xC3, 0xBE, 0xBE, 0xC3, -0xBD, 0xC2, 0xD0, 0xC9, 0xDE, 0x57, 0x61, 0x47, 0x3D, 0x3F, 0x39, 0x36, 0x38, 0x38, 0x38, 0x3A, -0x41, 0x40, 0x48, 0x6A, 0x5C, 0xF7, 0xCD, 0xDB, 0xCD, 0xC6, 0xD1, 0xCC, 0xCC, 0xD6, 0xD5, 0xD3, -0xCF, 0xCE, 0xC6, 0xC0, 0xBF, 0xBE, 0xBF, 0xBE, 0xC0, 0xCC, 0xCB, 0xDB, 0x5D, 0x5B, 0x49, 0x3E, -0x3E, 0x39, 0x37, 0x38, 0x38, 0x38, 0x3B, 0x40, 0x42, 0x4A, 0x63, 0x66, 0xE5, 0xCE, 0xD0, 0xCA, -0xC7, 0xCA, 0xCA, 0xCC, 0xCE, 0xD1, 0xCF, 0xCC, 0xCC, 0xC4, 0xBF, 0xBF, 0xBE, 0xC0, 0xBE, 0xC2, -0xCE, 0xCC, 0xE2, 0x58, 0x59, 0x47, 0x3D, 0x3D, 0x39, 0x37, 0x39, 0x39, 0x39, 0x3C, 0x40, 0x44, -0x4D, 0x5D, 0x6F, 0xE0, 0xD4, 0xCF, 0xCC, 0xCB, 0xCB, 0xCD, 0xCF, 0xCF, 0xD4, 0xD1, 0xCC, 0xCA, -0xC4, 0xBF, 0xBD, 0xBF, 0xBF, 0xBE, 0xC7, 0xCF, 0xCE, 0x79, 0x52, 0x51, 0x41, 0x3C, 0x3B, 0x38, -0x36, 0x38, 0x39, 0x38, 0x3D, 0x44, 0x44, 0x55, 0x70, 0x76, 0xD5, 0xCF, 0xCF, 0xC9, 0xCB, 0xCB, -0xCD, 0xD1, 0xD1, 0xD9, 0xD2, 0xCD, 0xCD, 0xC4, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xC9, 0xCF, 0xD0, -0x6A, 0x53, 0x4F, 0x3F, 0x3C, 0x3B, 0x38, 0x37, 0x38, 0x39, 0x3A, 0x3E, 0x44, 0x4A, 0x56, 0x75, -0xE7, 0xD7, 0xCD, 0xCB, 0xCA, 0xC9, 0xCA, 0xCD, 0xCE, 0xCF, 0xD3, 0xCE, 0xCA, 0xC7, 0xC0, 0xBE, -0xBE, 0xBF, 0xBF, 0xC1, 0xCD, 0xD2, 0xDB, 0x5C, 0x4F, 0x4A, 0x3E, 0x3C, 0x3B, 0x39, 0x38, 0x3A, -0x3A, 0x3C, 0x43, 0x46, 0x4E, 0x71, 0x7A, 0xDD, 0xCE, 0xD0, 0xCB, 0xCA, 0xCD, 0xCC, 0xD1, 0xD2, -0xD5, 0xD7, 0xCC, 0xCC, 0xC7, 0xBE, 0xBF, 0xBF, 0xBE, 0xC0, 0xC6, 0xCD, 0xD5, 0xEE, 0x54, 0x4D, -0x45, 0x3C, 0x3B, 0x39, 0x37, 0x38, 0x39, 0x3A, 0x3E, 0x44, 0x4A, 0x59, 0x71, 0xE8, 0xD4, 0xCE, -0xCC, 0xC9, 0xCB, 0xCB, 0xCE, 0xD2, 0xD4, 0xD7, 0xD2, 0xCD, 0xCB, 0xC4, 0xBF, 0xBF, 0xC0, 0xC0, -0xC0, 0xC9, 0xD1, 0xD8, 0x73, 0x52, 0x4B, 0x42, 0x3C, 0x3A, 0x39, 0x38, 0x38, 0x39, 0x3C, 0x3F, -0x41, 0x4E, 0x5D, 0x63, 0xDF, 0xD3, 0xD1, 0xCD, 0xCB, 0xCC, 0xD1, 0xD1, 0xD3, 0xDE, 0xDA, 0xD1, -0xCF, 0xCC, 0xC4, 0xBF, 0xC2, 0xC2, 0xBF, 0xC6, 0xCD, 0xD2, 0xE3, 0x63, 0x4F, 0x4A, 0x42, 0x3C, -0x3B, 0x3A, 0x39, 0x3A, 0x3B, 0x3F, 0x42, 0x47, 0x5A, 0x62, 0x7C, 0xD5, 0xD2, 0xCF, 0xCC, 0xCD, -0xCE, 0xD5, 0xD6, 0xD9, 0xE0, 0xD9, 0xD2, 0xCE, 0xCB, 0xC4, 0xBF, 0xC2, 0xC2, 0xBF, 0xC6, 0xCD, -0xD4, 0xE2, 0x67, 0x4F, 0x4B, 0x43, 0x3D, 0x3C, 0x3B, 0x3B, 0x3B, 0x3C, 0x42, 0x43, 0x49, 0x60, -0x6B, 0xF5, 0xD4, 0xD2, 0xD0, 0xD2, 0xD2, 0xD4, 0xDF, 0xDD, 0xDF, 0xEA, 0xDC, 0xD5, 0xD0, 0xCB, -0xC4, 0xC0, 0xC2, 0xBF, 0xC0, 0xC7, 0xC9, 0xD0, 0xDD, 0x7A, 0x57, 0x52, 0x48, 0x3F, 0x3F, 0x3D, -0x3C, 0x3C, 0x3D, 0x42, 0x44, 0x49, 0x5B, 0x64, 0x7C, 0xDE, 0xDB, 0xDB, 0xDC, 0xDA, 0xDF, 0xEE, -0xE9, 0xE6, 0xE8, 0xE3, 0xDB, 0xD0, 0xCD, 0xCC, 0xC5, 0xC3, 0xC5, 0xC2, 0xC3, 0xC8, 0xCD, 0xD1, -0xDE, 0x6F, 0x5B, 0x50, 0x4A, 0x44, 0x40, 0x3F, 0x3E, 0x3E, 0x42, 0x43, 0x47, 0x4F, 0x58, 0x5F, -0x69, 0xF6, 0xED, 0xF7, 0xEB, 0xE8, 0xEA, 0xEC, 0xE8, 0xE0, 0xE0, 0xDD, 0xD7, 0xD0, 0xCE, 0xCC, -0xC8, 0xC7, 0xC6, 0xC4, 0xC8, 0xC9, 0xCB, 0xD2, 0xDA, 0xEF, 0x71, 0x5B, 0x4F, 0x4E, 0x49, 0x45, -0x46, 0x45, 0x47, 0x47, 0x4A, 0x4E, 0x4E, 0x54, 0x5A, 0x5D, 0x62, 0x65, 0x70, 0x6C, 0x69, 0xFA, -0x7A, 0x7A, 0xE6, 0xE2, 0xDF, 0xDA, 0xD5, 0xCF, 0xD1, 0xD1, 0xCB, 0xCE, 0xD4, 0xCE, 0xD1, 0xD9, -0xDB, 0xE0, 0xE1, 0xEE, 0x6A, 0x74, 0x6C, 0x5A, 0x5B, 0x5A, 0x56, 0x56, 0x53, 0x56, 0x57, 0x53, -0x57, 0x5B, 0x59, 0x59, 0x5D, 0x65, 0x60, 0x61, 0x6F, 0x73, 0xF7, 0xEF, 0xEA, 0xDD, 0xDF, 0xDC, -0xD5, 0xD8, 0xDC, 0xDC, 0xDA, 0xDF, 0xE5, 0xE1, 0xEB, 0xED, 0xED, 0xFC, 0xFF, 0x70, 0x6C, 0x6C, -0x66, 0x5F, 0x5F, 0x62, 0x5F, 0x60, 0x60, 0x5A, 0x5F, 0x64, 0x5C, 0x64, 0x6E, 0x5F, 0x5E, 0x69, -0x6B, 0x7B, 0xF0, 0xFA, 0xE9, 0xE4, 0xE8, 0xDF, 0xE6, 0xEB, 0xE1, 0xE2, 0xE6, 0xE3, 0xE8, 0xED, -0xE5, 0xFC, 0x71, 0xE9, 0xFE, 0x7A, 0xEC, 0x73, 0x7E, 0x77, 0x64, 0xFD, 0x74, 0x66, 0x7B, 0x6C, -0x6B, 0x72, 0x6A, 0x79, 0x73, 0x71, 0xF4, 0x79, 0xFC, 0xEF, 0xF9, 0xEB, 0xF1, 0xF0, 0xE2, 0xE3, -0xE0, 0xE2, 0xE3, 0xEB, 0xFC, 0xEA, 0xF8, 0x6F, 0x70, 0x69, 0x77, 0x6C, 0x6D, 0xEF, 0x6B, 0x67, -0x6D, 0x65, 0x76, 0x75, 0x6F, 0x7E, 0x69, 0x67, 0x6F, 0x68, 0x6F, 0x73, 0x68, 0x69, 0x68, 0x69, -0x75, 0xFA, 0xEE, 0xF2, 0xEA, 0xDE, 0xE2, 0xE7, 0xE5, 0xEB, 0xF3, 0xED, 0xE8, 0xF7, 0x7F, 0xFA, -0x74, 0x6A, 0x68, 0x65, 0x66, 0x68, 0x64, 0x65, 0x68, 0x64, 0x6D, 0x77, 0x72, 0x70, 0x69, 0x77, -0x78, 0x64, 0x6B, 0x68, 0x60, 0x66, 0x67, 0x67, 0x61, 0x6D, 0xEB, 0xE8, 0xEC, 0xEB, 0xEA, 0xF7, -0xF5, 0xE2, 0xE5, 0xF6, 0xF8, 0xF0, 0x7E, 0x77, 0xFA, 0x76, 0x77, 0x7D, 0x6E, 0x74, 0x74, 0x72, -0x72, 0x6D, 0x75, 0x74, 0x74, 0x75, 0x76, 0xEF, 0xEE, 0xEF, 0xEA, 0xFA, 0x7F, 0xF2, 0xF6, 0xF7, -0xEF, 0xEA, 0xE4, 0xE5, 0xE7, 0xE3, 0xE9, 0xEC, 0xE8, 0xEB, 0xF0, 0xF8, 0xFC, 0x7F, 0x73, 0x73, -0x70, 0x72, 0x75, 0x69, 0x67, 0x6A, 0x6C, 0x6D, 0x6C, 0x70, 0x6D, 0x6D, 0x78, 0x72, 0x6F, 0x6E, -0x6C, 0x6F, 0x77, 0xF7, 0xEF, 0xF1, 0xF6, 0xF4, 0xF1, 0xFC, 0xF8, 0xEE, 0xF0, 0xEE, 0xEF, 0xEF, -0xEC, 0xEE, 0xF3, 0xF4, 0x76, 0x69, 0x77, 0xFA, 0x70, 0x75, 0x7E, 0x6E, 0x6D, 0x6E, 0x6E, 0x6E, -0x6D, 0x70, 0x74, 0x79, 0xFE, 0x7D, 0x78, 0x7C, 0x7A, 0x75, 0x78, 0x75, 0x70, 0x7C, 0xFC, 0xFB, -0xF4, 0xF3, 0xED, 0xEC, 0xF6, 0xF1, 0xEF, 0xF6, 0xF0, 0xFB, 0x7A, 0x7C, 0x74, 0x79, 0x74, 0x6C, -0x6C, 0x63, 0x67, 0x6C, 0x62, 0x66, 0x67, 0x68, 0x6C, 0x6A, 0x7D, 0x78, 0x6B, 0x7B, 0x71, 0x75, -0xFB, 0x75, 0x76, 0x7E, 0xEE, 0xED, 0xFA, 0xE8, 0xEE, 0x7F, 0xE4, 0xE9, 0xED, 0xE1, 0xEC, 0xEE, -0xE6, 0xF6, 0xFE, 0xF7, 0xFF, 0x7B, 0x72, 0x7C, 0x7F, 0x6B, 0x6C, 0x66, 0x67, 0x71, 0x66, 0x72, -0xFF, 0x69, 0x7B, 0xF7, 0xFF, 0xF3, 0xF6, 0xF3, 0xF2, 0xEE, 0xED, 0xFF, 0xF6, 0xED, 0xFB, 0xF2, -0xE9, 0xF0, 0xEC, 0xE6, 0xF4, 0xF2, 0xF5, 0x7C, 0xED, 0x7E, 0x6B, 0x7B, 0x77, 0xFE, 0x7A, 0x69, -0x68, 0x60, 0x65, 0x66, 0x61, 0x71, 0x74, 0x74, 0xFC, 0x7E, 0xFA, 0xFF, 0x7E, 0xF9, 0xFB, 0xEE, -0xED, 0xEF, 0xEF, 0xF4, 0xEC, 0xEC, 0xF2, 0xF0, 0xEF, 0xED, 0xF4, 0xF6, 0xEF, 0xFD, 0xF8, 0xF1, -0x77, 0x74, 0x7A, 0x70, 0x6F, 0x79, 0x76, 0x6A, 0x68, 0x6C, 0x6C, 0x69, 0x65, 0x66, 0x6E, 0x73, -0x6C, 0x6D, 0x7A, 0x79, 0x7A, 0xFE, 0x73, 0x72, 0x7A, 0x7E, 0xF3, 0xEF, 0xEE, 0xF1, 0xF6, 0xEC, -0xEF, 0xFA, 0xEE, 0xF5, 0xFE, 0xF2, 0xF0, 0xF7, 0xFA, 0xF9, 0xFB, 0xFB, 0x7A, 0x6B, 0x6B, 0x72, -0x6C, 0x6A, 0x76, 0x74, 0x6E, 0x72, 0x6C, 0x6B, 0x6F, 0x6B, 0x6E, 0x71, 0x6D, 0x75, 0x76, 0x70, -0x78, 0x7C, 0xFA, 0xF2, 0xF7, 0xF1, 0xED, 0xF3, 0xF4, 0xF6, 0xFA, 0xF0, 0xEF, 0xFD, 0x7B, 0xFF, -0x78, 0x6C, 0x70, 0x72, 0x6E, 0x73, 0x6D, 0x6D, 0x6D, 0x65, 0x65, 0x69, 0x6F, 0x70, 0x6B, 0x6E, -0x71, 0x6E, 0x6E, 0x6F, 0x7A, 0x7D, 0x7E, 0xEF, 0xF4, 0x7D, 0xFB, 0xF3, 0xEC, 0xEB, 0xF0, 0xEF, -0xEE, 0xEE, 0xF3, 0xF0, 0xED, 0xFF, 0xFF, 0xF3, 0x7E, 0xFB, 0xFF, 0x73, 0x7E, 0x7A, 0x76, 0x6F, -0x6C, 0x7A, 0x74, 0x6F, 0xFF, 0x7B, 0x79, 0xF7, 0xF5, 0xFB, 0xF6, 0xF3, 0x7E, 0x7D, 0xFA, 0xFD, -0xF6, 0xF5, 0x7D, 0x7B, 0x7A, 0xFF, 0xFB, 0x7D, 0x75, 0x76, 0xF3, 0xF3, 0x78, 0xFE, 0x76, 0x6B, -0x7B, 0xFB, 0x75, 0x69, 0x6C, 0xF6, 0xF6, 0x7B, 0x6F, 0x69, 0x6E, 0x72, 0x70, 0x74, 0x78, 0xFF, -0x7F, 0xFD, 0xF5, 0xF8, 0xF5, 0xF9, 0x75, 0x74, 0x7D, 0xF7, 0xFB, 0x70, 0x7E, 0xE9, 0xEF, 0xF7, -0xE6, 0xED, 0x74, 0x7A, 0xF7, 0x7D, 0x71, 0x77, 0xFE, 0xF6, 0xFC, 0x6B, 0x6B, 0x72, 0x69, 0x6D, -0x7B, 0x7C, 0xFD, 0x6F, 0x6B, 0xFC, 0x7F, 0x73, 0xF9, 0xF5, 0x70, 0x6E, 0xFD, 0xFF, 0x7C, 0xFB, -0x70, 0x76, 0xEF, 0x7D, 0x75, 0xFB, 0xF6, 0xEF, 0xF6, 0x7C, 0x79, 0x76, 0xFE, 0x7D, 0x73, 0xFD, -0xFE, 0x76, 0x6E, 0x66, 0x6B, 0x75, 0x6D, 0x7D, 0xF2, 0x6D, 0x75, 0xED, 0x7A, 0x6D, 0x7E, 0xF9, -0xFE, 0x6E, 0x78, 0xF4, 0x6E, 0x73, 0xF8, 0x74, 0xFF, 0xFE, 0x77, 0xF6, 0x7C, 0x71, 0x7F, 0x7E, -0x7E, 0x79, 0x78, 0x7C, 0x6E, 0x6F, 0x70, 0x6D, 0xFD, 0xFA, 0x6C, 0x6B, 0x71, 0x70, 0x7B, 0x7A, -0x7C, 0xFD, 0x6D, 0x75, 0xED, 0xF8, 0xFC, 0xF7, 0xFB, 0xEE, 0xEF, 0x79, 0xFF, 0xED, 0xF8, 0x7A, -0xF1, 0xED, 0xF7, 0xFC, 0xF8, 0xF1, 0xF8, 0x79, 0x7D, 0xF6, 0x78, 0x6E, 0xFD, 0xFB, 0x72, 0x6E, -0x6C, 0x74, 0xFE, 0xFF, 0xF7, 0xFE, 0x6D, 0x75, 0x79, 0x6F, 0x7C, 0x7D, 0x76, 0x78, 0x7A, 0x7F, -0x75, 0x7D, 0xF7, 0xF8, 0xEF, 0x7B, 0x7A, 0xF3, 0xFF, 0xEF, 0xF8, 0xFE, 0xF6, 0x6E, 0xF4, 0xED, -0x6F, 0x7C, 0x74, 0x75, 0xF2, 0x7C, 0x75, 0xF9, 0xEE, 0xFF, 0x70, 0x7C, 0x7F, 0xFD, 0xFD, 0xFA, -0x79, 0x6C, 0x7C, 0x7F, 0xFD, 0xF3, 0x78, 0xFE, 0xF7, 0xFC, 0xED, 0xF1, 0xFD, 0xFE, 0x7D, 0xF4, -0xED, 0xF4, 0xFA, 0xFD, 0x70, 0x73, 0xF6, 0xFF, 0x7D, 0xF7, 0x79, 0x72, 0x78, 0x7D, 0x77, 0x6E, -0x71, 0x6C, 0x6A, 0x7D, 0x74, 0x71, 0x7D, 0x6E, 0x79, 0xF8, 0x70, 0x7C, 0xFE, 0x6D, 0x6F, 0x6E, -0x6D, 0x7D, 0xFD, 0xFB, 0xFA, 0x7C, 0xFD, 0xFA, 0xF7, 0xF4, 0xFD, 0x7B, 0x76, 0x7D, 0xF3, 0xF5, -0xFD, 0xF5, 0xED, 0x7A, 0x6D, 0xF5, 0x7C, 0x76, 0xEF, 0x77, 0x6E, 0xFF, 0x74, 0x79, 0xFD, 0x76, -0x7A, 0x7E, 0x7B, 0x7D, 0x78, 0x73, 0x7A, 0xFB, 0xFB, 0x7E, 0xFB, 0xF2, 0xF8, 0xF9, 0xFB, 0xFE, -0xF5, 0xF6, 0xF9, 0x7E, 0x73, 0xF7, 0xFA, 0x74, 0xFC, 0x7B, 0x70, 0xFF, 0xFC, 0x7E, 0x7C, 0xFE, -0xFF, 0x7C, 0xFC, 0x7E, 0x79, 0xFF, 0x7C, 0x79, 0x79, 0x79, 0x7A, 0x79, 0x7A, 0x7C, 0x7A, 0x75, -0x72, 0x7C, 0xFC, 0xFE, 0xFE, 0xFC, 0xF9, 0xFE, 0xFE, 0xFA, 0x7C, 0xFD, 0xFC, 0x77, 0x7B, 0x7A, -0x73, 0x78, 0x7A, 0x78, 0x7B, 0x77, 0x6E, 0x70, 0x7B, 0x7A, 0x72, 0x74, 0x76, 0x75, 0x77, 0x75, -0x70, 0x6F, 0x6F, 0x6F, 0x74, 0x77, 0x72, 0x6F, 0x70, 0x79, 0x79, 0x6F, 0x7E, 0xFA, 0x72, 0x79, -0x79, 0x78, 0xFE, 0x78, 0xFF, 0xFE, 0x76, 0xFF, 0xFF, 0x7D, 0xFF, 0x7E, 0x7B, 0x78, 0x7C, 0x75, -0x75, 0xFF, 0xFF, 0xF8, 0xF6, 0xFD, 0xF5, 0xF4, 0xF6, 0xFC, 0x7C, 0x7C, 0x7B, 0xFC, 0xFD, 0xFD, -0xFD, 0x77, 0x7A, 0x7F, 0x7E, 0xFC, 0xFA, 0xF7, 0xFF, 0xFE, 0xF1, 0xFD, 0xFD, 0xF0, 0xF9, 0xF8, -0xEE, 0xEF, 0xF8, 0x7E, 0x79, 0x77, 0x6C, 0x66, 0x6A, 0x68, 0x67, 0x71, 0x74, 0x77, 0x7E, 0x7B, -0xFD, 0xFD, 0x7F, 0xFF, 0x79, 0xFE, 0xFE, 0x75, 0x78, 0x7A, 0x74, 0x70, 0x76, 0x77, 0x75, 0x7C, -0x7C, 0x79, 0x78, 0x77, 0x78, 0x7C, 0x7E, 0x77, 0x74, 0x79, 0x77, 0x78, 0x78, 0x77, 0x7B, 0x78, -0x77, 0x77, 0x7A, 0xFE, 0x7D, 0xFE, 0xF6, 0xFB, 0xFC, 0xF8, 0xF6, 0xFA, 0xFD, 0xFB, 0xFC, 0x7D, -0x7B, 0xFB, 0xF5, 0xFC, 0xFF, 0xFD, 0xFE, 0xFF, 0x7F, 0xFC, 0xF8, 0xFF, 0x7F, 0xF7, 0xF9, 0xFA, -0xF9, 0xFB, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xFD, 0x7E, 0xF8, 0xF7, 0xEF, 0xE0, 0xE1, 0xF7, 0x6E, -0x69, 0x65, 0x5C, 0x5B, 0x61, 0x69, 0x6E, 0x76, 0xF8, 0xEF, 0xF2, 0xEB, 0xE3, 0xDE, 0xE2, 0xFA, -0x66, 0x5B, 0x5F, 0x6E, 0x6E, 0xFB, 0xED, 0xED, 0xEC, 0xF8, 0x7E, 0x7B, 0x6E, 0x67, 0x60, 0x65, -0x6E, 0x6F, 0x78, 0xFB, 0x7E, 0x7D, 0xFA, 0xF9, 0xF7, 0xF5, 0xF7, 0xEE, 0xEB, 0xF3, 0xFA, 0x7A, -0x6D, 0x69, 0x6A, 0x69, 0x64, 0x69, 0x6E, 0x6F, 0x78, 0xFB, 0xFB, 0x7C, 0xF8, 0xF1, 0xFB, 0xF4, -0xF9, 0x7D, 0xF8, 0x7D, 0xFD, 0xF3, 0x7C, 0xFD, 0xF4, 0x7A, 0x6F, 0x79, 0xFF, 0x75, 0x78, 0xFA, -0xFB, 0xFE, 0xFB, 0xF9, 0xFE, 0xFF, 0xFF, 0xFC, 0xF5, 0xFD, 0x79, 0xFF, 0xF9, 0xFA, 0xFF, 0xFF, -0x7B, 0x74, 0x75, 0x6F, 0x71, 0x7D, 0x74, 0x75, 0x79, 0x71, 0x77, 0x7E, 0x7E, 0x7C, 0x74, 0x71, -0x77, 0x7A, 0x7F, 0xFB, 0x72, 0x69, 0x74, 0x74, 0x6A, 0x74, 0x7E, 0x79, 0x76, 0x74, 0x7F, 0x7C, -0x76, 0xF8, 0xF8, 0x7E, 0x7D, 0x7E, 0xFE, 0x77, 0x7B, 0xF8, 0x7B, 0xFF, 0xF3, 0xFC, 0xFF, 0xFE, -0xFD, 0xF5, 0xF6, 0xF3, 0xF2, 0xFA, 0xF8, 0xF3, 0xFB, 0x7E, 0xFA, 0xFA, 0x7E, 0xFE, 0xFC, 0x7C, -0x78, 0x7D, 0xFB, 0xF3, 0xFD, 0x74, 0xF7, 0xF5, 0x76, 0xF9, 0xF8, 0xFF, 0xF5, 0x76, 0x7B, 0xFA, -0x77, 0xFD, 0x7C, 0x77, 0x7D, 0x6F, 0x7D, 0xF7, 0xFC, 0xFD, 0x7B, 0xF5, 0xF8, 0x7A, 0xF4, 0xFB, -0x77, 0x7B, 0x76, 0x74, 0x7D, 0x7E, 0x72, 0x71, 0x70, 0x70, 0x77, 0x70, 0x7B, 0xFD, 0x6F, 0x72, -0x7D, 0x7A, 0x71, 0x79, 0xF8, 0x77, 0x6F, 0x74, 0x7A, 0x78, 0x7C, 0xF6, 0x7C, 0xFB, 0x7D, 0x6C, -0xEF, 0xFC, 0x6D, 0xF1, 0xF5, 0x7D, 0x79, 0xFA, 0xF5, 0x75, 0xF4, 0x7C, 0xFC, 0xE6, 0x68, 0x78, -0xE4, 0x6E, 0x77, 0xF2, 0x7E, 0x70, 0x69, 0xF9, 0xF7, 0x6C, 0x74, 0xF3, 0xEE, 0x79, 0x7E, 0xFB, -0x7A, 0xF6, 0x6C, 0x6B, 0xED, 0x77, 0x6A, 0x70, 0x6F, 0xFC, 0x7E, 0x79, 0xF2, 0xEE, 0xF8, 0x7D, -0xEF, 0xEB, 0x7E, 0x72, 0x7D, 0x74, 0x6B, 0xFA, 0xE5, 0xE7, 0xE4, 0xEB, 0xF8, 0xEC, 0xEA, 0xF3, -0xFB, 0xFA, 0xEF, 0xF6, 0x7C, 0xFF, 0x7B, 0x72, 0x78, 0x7C, 0x6F, 0x6E, 0xFF, 0xFF, 0x75, 0x74, -0x74, 0x76, 0x6C, 0x6A, 0x7F, 0x78, 0x6D, 0x74, 0x6E, 0x76, 0x7B, 0x69, 0x6B, 0x79, 0x72, 0x6A, -0x6D, 0x77, 0xFD, 0xFC, 0x6F, 0x7C, 0xEE, 0x76, 0x69, 0x74, 0xFB, 0x74, 0x6A, 0x7D, 0xF0, 0xFF, -0x6B, 0x71, 0xEF, 0x7D, 0x74, 0xF8, 0x75, 0x74, 0x77, 0x6C, 0x6E, 0x71, 0x73, 0x72, 0x70, 0xF4, -0xF0, 0x79, 0x7B, 0xFB, 0xF3, 0xF4, 0x78, 0x79, 0xF7, 0xFC, 0x6E, 0x70, 0xF8, 0xF8, 0x79, 0x6D, -0x70, 0xFB, 0x6E, 0x64, 0x6B, 0x72, 0x6F, 0x65, 0x71, 0xFC, 0x71, 0x76, 0x6E, 0x71, 0xF9, 0xFF, -0xFD, 0x73, 0x7A, 0xFF, 0x66, 0x70, 0xFE, 0x79, 0xFD, 0x7D, 0xF1, 0xF6, 0x76, 0x79, 0x6D, 0x79, -0xFF, 0x72, 0x7F, 0xF8, 0xF0, 0xFF, 0x71, 0xEC, 0xE9, 0x7C, 0x7C, 0xF1, 0xF7, 0xF7, 0xF5, 0xFA, -0xF1, 0x7C, 0x70, 0x7D, 0xFB, 0xF4, 0x7C, 0x7D, 0xF6, 0xFD, 0xEF, 0xF6, 0xEE, 0xE1, 0xEE, 0xF4, -0xF3, 0xEE, 0xF4, 0x78, 0xEC, 0xF7, 0x72, 0x74, 0x6E, 0xF5, 0x6A, 0x7B, 0xDF, 0x69, 0x7F, 0xE7, -0x6F, 0x7C, 0x68, 0xF4, 0xEE, 0x5C, 0x72, 0x6B, 0x6D, 0xEC, 0x60, 0x7E, 0xEB, 0x66, 0x6D, 0x6A, -0x7A, 0xFF, 0x6A, 0x6E, 0x74, 0xED, 0x79, 0x6B, 0xED, 0xFA, 0xFC, 0x70, 0x65, 0xF6, 0x75, 0x67, -0x78, 0x78, 0xF4, 0xFF, 0x5F, 0xF9, 0xE3, 0x6D, 0xF1, 0x7A, 0x70, 0xDB, 0x6A, 0xF9, 0xDC, 0x66, -0xDB, 0xF8, 0x65, 0xD6, 0x6A, 0xF2, 0xE6, 0x5E, 0xE1, 0x76, 0x6F, 0xF5, 0x65, 0xEF, 0x5F, 0x66, -0x6F, 0x5A, 0xFB, 0x5E, 0x65, 0x7E, 0x60, 0xF2, 0x5D, 0x72, 0xE8, 0x5A, 0xEA, 0x6E, 0x62, 0xE4, -0x5E, 0x6F, 0x74, 0x67, 0xEA, 0x64, 0x77, 0xF3, 0x69, 0xF1, 0x6B, 0x6E, 0xEB, 0x74, 0x74, 0x74, -0xF6, 0xF5, 0x66, 0xF5, 0xF7, 0x6C, 0xE9, 0x70, 0x6F, 0xE4, 0x6E, 0xF0, 0xF4, 0x63, 0xE1, 0xFE, -0x6D, 0xE4, 0x6D, 0xF2, 0xEF, 0x6C, 0xE8, 0x7B, 0xFC, 0xED, 0x74, 0xEB, 0xFE, 0xFC, 0xEF, 0x6D, -0xEC, 0xFB, 0x71, 0xEB, 0x77, 0xF3, 0xF8, 0x6F, 0xE9, 0xFE, 0xFC, 0xEE, 0x6F, 0xEB, 0xED, 0x7A, -0xEE, 0xF4, 0xF3, 0xF8, 0xF1, 0xEE, 0x79, 0xF4, 0xF3, 0xFC, 0xF1, 0x7C, 0xF4, 0xF9, 0x76, 0xE8, -0xF8, 0x75, 0xE3, 0x7B, 0x6E, 0xE0, 0x75, 0x72, 0xE3, 0x6C, 0xF8, 0xE7, 0x6D, 0xEA, 0xF4, 0x75, -0xEA, 0x71, 0xFA, 0xED, 0x6A, 0xF2, 0xFB, 0x70, 0xEB, 0x6F, 0x71, 0xEE, 0x6C, 0xF6, 0xF2, 0x68, -0xF3, 0xF8, 0x6D, 0x7A, 0xFC, 0x75, 0x6D, 0xF3, 0x6E, 0x6E, 0xEE, 0x61, 0x7C, 0xFE, 0x60, 0xF2, -0x6D, 0x78, 0x7C, 0x63, 0xEB, 0x64, 0x71, 0xED, 0x5C, 0xEB, 0x6F, 0x69, 0xEA, 0x5D, 0xE7, 0xF8, -0x5E, 0xE6, 0x6F, 0xFA, 0x79, 0x72, 0xE8, 0x5E, 0xE9, 0xFB, 0x5F, 0xDE, 0x61, 0xFA, 0xE3, 0x5A, -0xE3, 0x79, 0x62, 0xDF, 0x64, 0x76, 0xE4, 0x63, 0xFE, 0xE9, 0x64, 0xF8, 0xE6, 0x5F, 0xF2, 0xE8, -0x62, 0xE7, 0xFB, 0x6A, 0xF0, 0x77, 0xF9, 0x77, 0xE9, 0x78, 0x59, 0xE3, 0x71, 0x6C, 0xDF, 0x5F, -0xEF, 0xE9, 0x5F, 0xFA, 0x76, 0xF9, 0x7D, 0x67, 0xEE, 0x7B, 0x6F, 0x7E, 0x6B, 0x6D, 0xF7, 0x6E, -0x6B, 0xF0, 0x63, 0xF0, 0x75, 0x57, 0xD4, 0x59, 0x57, 0xCD, 0x4F, 0xEE, 0xDE, 0x4B, 0xCF, 0x68, -0x5B, 0xD4, 0x50, 0xE0, 0xEF, 0x54, 0xD5, 0x57, 0x6A, 0xD3, 0x54, 0x79, 0xDF, 0x5E, 0xFC, 0xE6, -0x5F, 0x71, 0xE0, 0x64, 0x76, 0xFC, 0x5D, 0xEA, 0xF8, 0x58, 0x79, 0xDB, 0x68, 0x59, 0xDF, 0x76, -0x64, 0xDA, 0x63, 0x5A, 0xD9, 0xE0, 0x51, 0xF3, 0xD3, 0x4D, 0xDE, 0xCF, 0x3F, 0xDA, 0xC2, 0x41, -0x6A, 0xC3, 0x50, 0x55, 0xC6, 0x5A, 0x4B, 0xC2, 0x7A, 0x46, 0xCA, 0xDF, 0x4C, 0xD8, 0xDE, 0x4F, -0xE2, 0xD2, 0x4D, 0x6B, 0xCD, 0x5C, 0x56, 0xD8, 0xEB, 0x54, 0xEF, 0xDE, 0x4F, 0x78, 0xD7, 0x54, -0x5B, 0xDA, 0xFA, 0x56, 0xF6, 0xE9, 0x58, 0xF1, 0xE5, 0x59, 0x6D, 0xE8, 0x7B, 0x64, 0x7E, 0xE2, -0x60, 0x6D, 0xDC, 0x63, 0x67, 0xDC, 0x6F, 0x62, 0xE2, 0x7B, 0x60, 0xEC, 0xF3, 0x67, 0x77, 0xF1, -0x7C, 0x75, 0xF0, 0x79, 0x6F, 0xED, 0x76, 0x6B, 0xF3, 0xFF, 0x74, 0xF4, 0xF8, 0x6B, 0x6C, 0xE6, -0xE7, 0x61, 0x79, 0xDD, 0x6F, 0x77, 0xDE, 0x6D, 0x6E, 0xE4, 0x7E, 0xF9, 0xE8, 0x6D, 0x6F, 0xDE, -0xEF, 0x5C, 0xEF, 0xDB, 0x64, 0x6B, 0xDF, 0xF9, 0x6C, 0xF4, 0xF5, 0x72, 0xF5, 0xF7, 0x66, 0x7D, -0xE9, 0x7D, 0x70, 0x79, 0xF6, 0x7B, 0x6C, 0xF3, 0xF2, 0x6A, 0x76, 0xED, 0x79, 0x6B, 0xF2, 0xF1, -0x6B, 0x78, 0xEF, 0x73, 0x74, 0xED, 0x73, 0x6A, 0xEB, 0xF2, 0x64, 0x6F, 0xE2, 0xF8, 0x5E, 0xF3, -0xE8, 0x64, 0x74, 0xEF, 0x7B, 0x77, 0x74, 0x78, 0x6D, 0xF8, 0xEA, 0x63, 0x68, 0xEC, 0xEF, 0x6E, -0x63, 0xEE, 0xFA, 0x5F, 0xE5, 0xEB, 0x58, 0x73, 0xD1, 0xF3, 0x4D, 0xF2, 0xCD, 0x5D, 0x54, 0xDF, -0xDD, 0x61, 0x5B, 0xD9, 0xDF, 0x50, 0xF0, 0xD3, 0x5F, 0x5D, 0xE4, 0xDF, 0x6E, 0x5B, 0xED, 0xD8, -0x68, 0x56, 0xEA, 0xD3, 0x70, 0x52, 0xF0, 0xD3, 0x74, 0x57, 0xF5, 0xD8, 0x6A, 0x58, 0xE4, 0xE2, -0x5C, 0x7A, 0xD8, 0x77, 0x5A, 0xE8, 0xD9, 0x73, 0x5F, 0x7D, 0xE3, 0xEC, 0x73, 0x72, 0x6E, 0xF6, -0xDF, 0xF7, 0x62, 0x6B, 0xE5, 0xDD, 0x6B, 0x58, 0xF7, 0xDB, 0x73, 0x5E, 0x75, 0x7E, 0xF6, 0xFE, -0x66, 0x71, 0x7B, 0x7E, 0xEE, 0x6C, 0x6B, 0xEB, 0x76, 0x6C, 0xEA, 0x7A, 0x67, 0xE5, 0xE8, 0x5F, -0x75, 0xDE, 0xEE, 0x6F, 0x7A, 0xED, 0xE8, 0xF8, 0xFB, 0xF4, 0x78, 0xF1, 0xE4, 0x6E, 0x62, 0xEB, -0xEC, 0x65, 0x6D, 0x7E, 0x6B, 0x69, 0x6E, 0x72, 0x78, 0x68, 0x5E, 0x74, 0xE9, 0x6F, 0x5E, 0x72, -0xEB, 0x7A, 0x6F, 0xF1, 0x78, 0x75, 0xE5, 0xE6, 0xE2, 0xDD, 0xE0, 0xD6, 0xD1, 0xD5, 0xD1, 0xD3, -0xDE, 0xE0, 0xDD, 0xE9, 0x68, 0x57, 0x52, 0x50, 0x4B, 0x48, 0x48, 0x48, 0x48, 0x4B, 0x51, 0x56, -0x56, 0x5D, 0x68, 0x6A, 0x69, 0x7C, 0xE8, 0xED, 0xED, 0xDC, 0xD5, 0xCD, 0xCB, 0xCD, 0xC5, 0xBE, -0xBE, 0xBC, 0xBC, 0xBF, 0xC5, 0xD2, 0x72, 0x52, 0x48, 0x40, 0x3C, 0x39, 0x39, 0x3B, 0x3D, 0x40, -0x4A, 0x5A, 0x6C, 0x7E, 0xDF, 0xCF, 0xCE, 0xDB, 0x6F, 0x63, 0x6E, 0x60, 0x4D, 0x47, 0x4D, 0x5E, -0x7C, 0xFA, 0xE4, 0xCD, 0xC1, 0xBD, 0xBC, 0xBA, 0xB7, 0xB4, 0xB5, 0xBE, 0xD7, 0xEB, 0xF2, 0x4E, -0x3B, 0x36, 0x37, 0x39, 0x38, 0x39, 0x3D, 0x48, 0x58, 0xFE, 0xDA, 0xD6, 0xD8, 0xCF, 0xC8, 0xCD, -0xF3, 0x5A, 0x59, 0x58, 0x4E, 0x47, 0x48, 0x4F, 0x5F, 0xF2, 0xD9, 0xCE, 0xC6, 0xBF, 0xBB, 0xBA, -0xB9, 0xB7, 0xB5, 0xB7, 0xC0, 0xD9, 0x68, 0x57, 0x49, 0x3B, 0x35, 0x35, 0x38, 0x3A, 0x3B, 0x3F, -0x4B, 0x60, 0xE7, 0xD8, 0xD5, 0xD4, 0xCE, 0xCA, 0xCF, 0xF9, 0x59, 0x51, 0x4E, 0x4C, 0x48, 0x46, -0x49, 0x56, 0xFB, 0xD9, 0xD1, 0xC9, 0xBE, 0xB8, 0xB6, 0xB5, 0xB3, 0xB2, 0xB6, 0xC3, 0xE5, 0x60, -0x4E, 0x3F, 0x37, 0x32, 0x34, 0x37, 0x39, 0x3C, 0x41, 0x4E, 0x6F, 0xDA, 0xD2, 0xD4, 0xD1, 0xCA, -0xC9, 0xD7, 0x6F, 0x59, 0x4D, 0x47, 0x47, 0x48, 0x45, 0x46, 0x52, 0xEE, 0xD7, 0xD0, 0xC7, 0xBC, -0xB5, 0xB3, 0xB3, 0xB1, 0xB0, 0xB7, 0xCA, 0x68, 0x4F, 0x45, 0x3B, 0x32, 0x2E, 0x30, 0x35, 0x39, -0x3D, 0x44, 0x52, 0xEC, 0xD0, 0xCF, 0xD3, 0xD0, 0xCD, 0xCF, 0xE3, 0x63, 0x5B, 0x59, 0x52, 0x4D, -0x4C, 0x4F, 0x5A, 0x6D, 0xE5, 0xD2, 0xC8, 0xBE, 0xB9, 0xB5, 0xB3, 0xB1, 0xAF, 0xB1, 0xBD, 0xE3, -0x53, 0x4B, 0x40, 0x35, 0x2E, 0x2F, 0x34, 0x39, 0x3B, 0x3F, 0x4C, 0x75, 0xDA, 0xD6, 0xD2, 0xCE, -0xCE, 0xD0, 0xD9, 0xEF, 0x62, 0x56, 0x55, 0x55, 0x4F, 0x4E, 0x52, 0x61, 0xEF, 0xDB, 0xCC, 0xBF, -0xB9, 0xB5, 0xB3, 0xB0, 0xAE, 0xB4, 0xC8, 0x63, 0x4D, 0x45, 0x39, 0x2E, 0x2C, 0x2F, 0x35, 0x38, -0x3B, 0x43, 0x5A, 0xDD, 0xCE, 0xCD, 0xCC, 0xCB, 0xCD, 0xD2, 0xDE, 0x76, 0x5A, 0x52, 0x53, 0x51, -0x4E, 0x4F, 0x5C, 0x7C, 0xE2, 0xD2, 0xC5, 0xBB, 0xB5, 0xB2, 0xB0, 0xAE, 0xAE, 0xB6, 0xCF, 0x55, -0x48, 0x3F, 0x34, 0x2C, 0x2C, 0x2F, 0x36, 0x39, 0x3D, 0x4A, 0x7D, 0xCE, 0xCA, 0xCB, 0xCB, 0xCB, -0xCD, 0xD9, 0x76, 0x59, 0x4F, 0x4C, 0x4C, 0x4A, 0x4C, 0x54, 0x68, 0xEC, 0xD8, 0xCB, 0xBF, 0xB8, -0xB3, 0xB0, 0xAE, 0xAD, 0xAE, 0xBC, 0xF5, 0x4C, 0x43, 0x39, 0x2E, 0x2A, 0x2D, 0x32, 0x37, 0x3B, -0x44, 0x5F, 0xD5, 0xC8, 0xC7, 0xC9, 0xCB, 0xCE, 0xD3, 0xE9, 0x5A, 0x4D, 0x4D, 0x4D, 0x4B, 0x4B, -0x50, 0x63, 0xF6, 0xDE, 0xCE, 0xC5, 0xBD, 0xB7, 0xB2, 0xB0, 0xAF, 0xAD, 0xAF, 0xBF, 0x5E, 0x45, -0x40, 0x37, 0x2C, 0x29, 0x2D, 0x34, 0x38, 0x3C, 0x48, 0x77, 0xCD, 0xC6, 0xC7, 0xCB, 0xCB, 0xCF, -0xE9, 0x59, 0x4E, 0x4B, 0x47, 0x44, 0x47, 0x4D, 0x57, 0x69, 0xEB, 0xD7, 0xCA, 0xBF, 0xB9, 0xB3, -0xAF, 0xAE, 0xAD, 0xAC, 0xB5, 0xDE, 0x49, 0x43, 0x3B, 0x2D, 0x28, 0x2A, 0x30, 0x36, 0x3A, 0x44, -0x6A, 0xCD, 0xC2, 0xC2, 0xC6, 0xCA, 0xCC, 0xD8, 0x60, 0x4C, 0x48, 0x46, 0x44, 0x43, 0x49, 0x54, -0x68, 0xEC, 0xD8, 0xCB, 0xC2, 0xBC, 0xB8, 0xB2, 0xAF, 0xAE, 0xAE, 0xAE, 0xBA, 0x74, 0x45, 0x3F, -0x36, 0x2C, 0x29, 0x2C, 0x32, 0x37, 0x3E, 0x4E, 0xE3, 0xC7, 0xC2, 0xC5, 0xC6, 0xC8, 0xD2, 0x71, -0x52, 0x4D, 0x49, 0x42, 0x41, 0x47, 0x51, 0x60, 0x7B, 0xDE, 0xCE, 0xC6, 0xC0, 0xBE, 0xB9, 0xB2, -0xAF, 0xAF, 0xAE, 0xAE, 0xBC, 0x6B, 0x47, 0x3F, 0x34, 0x2C, 0x2A, 0x2D, 0x32, 0x39, 0x3F, 0x4F, -0xDF, 0xC5, 0xC4, 0xCD, 0xCA, 0xC7, 0xDB, 0x5A, 0x4E, 0x4D, 0x48, 0x42, 0x40, 0x45, 0x54, 0x6F, -0x74, 0xEC, 0xCD, 0xC4, 0xC4, 0xC1, 0xBB, 0xB3, 0xAF, 0xB0, 0xAE, 0xAE, 0xBC, 0x6F, 0x4A, 0x3F, -0x34, 0x2C, 0x2B, 0x2E, 0x34, 0x3B, 0x42, 0x56, 0xCF, 0xBF, 0xC2, 0xCB, 0xC6, 0xC6, 0xE6, 0x52, -0x4B, 0x47, 0x45, 0x3F, 0x3D, 0x45, 0x5E, 0x7A, 0x72, 0xDB, 0xC7, 0xC3, 0xC5, 0xC2, 0xBC, 0xB5, -0xB1, 0xB3, 0xB0, 0xAE, 0xB9, 0xF0, 0x4C, 0x43, 0x38, 0x2E, 0x2B, 0x2D, 0x34, 0x3A, 0x3E, 0x4F, -0xD3, 0xC1, 0xC4, 0xCD, 0xCC, 0xC8, 0xDE, 0x4D, 0x45, 0x47, 0x47, 0x41, 0x3D, 0x47, 0x6D, 0xF7, -0x79, 0xDA, 0xC9, 0xC6, 0xC8, 0xC9, 0xC0, 0xB8, 0xB4, 0xB5, 0xB0, 0xAC, 0xB1, 0xCF, 0x5A, 0x4E, -0x3E, 0x30, 0x2B, 0x2D, 0x33, 0x38, 0x3B, 0x48, 0xDC, 0xC4, 0xC5, 0xCA, 0xCB, 0xCB, 0xD9, 0x50, -0x46, 0x4B, 0x46, 0x3E, 0x3F, 0x48, 0x55, 0x76, 0x7E, 0xE3, 0xCA, 0xC9, 0xCE, 0xCB, 0xC6, 0xBE, -0xBA, 0xB8, 0xB4, 0xAE, 0xAE, 0xBD, 0xEA, 0x62, 0x48, 0x35, 0x2D, 0x2C, 0x30, 0x35, 0x37, 0x3F, -0x6F, 0xCB, 0xCA, 0xC9, 0xC7, 0xCE, 0xD9, 0x60, 0x46, 0x46, 0x48, 0x40, 0x40, 0x49, 0x52, 0x69, -0xED, 0xE1, 0xD1, 0xCC, 0xD0, 0xD2, 0xD0, 0xCD, 0xC2, 0xBD, 0xB9, 0xB2, 0xAE, 0xAF, 0xBA, 0xE0, -0x67, 0x4C, 0x34, 0x2D, 0x2E, 0x30, 0x34, 0x37, 0x41, 0xF4, 0xCD, 0xCC, 0xC7, 0xC5, 0xD0, 0xED, -0x64, 0x4D, 0x45, 0x47, 0x43, 0x44, 0x4F, 0x51, 0x69, 0xD9, 0xDD, 0xD1, 0xCB, 0xD1, 0xCF, 0xD2, -0xCD, 0xBF, 0xBC, 0xB8, 0xB0, 0xAD, 0xAE, 0xBC, 0xD9, 0x76, 0x48, 0x32, 0x2D, 0x2F, 0x30, 0x32, -0x38, 0x49, 0xE6, 0xD1, 0xCB, 0xC1, 0xC1, 0xD2, 0x7A, 0x6F, 0x51, 0x42, 0x43, 0x45, 0x4A, 0x4D, -0x4E, 0x72, 0xD7, 0xDB, 0xD8, 0xCE, 0xCF, 0xD7, 0xDD, 0xD3, 0xC6, 0xBF, 0xBB, 0xB3, 0xAD, 0xAE, -0xB8, 0xCD, 0xE2, 0x52, 0x35, 0x2E, 0x2F, 0x2F, 0x2F, 0x35, 0x43, 0x62, 0xE5, 0xCF, 0xC1, 0xC0, -0xD1, 0xE1, 0xDB, 0x5E, 0x46, 0x49, 0x4D, 0x4A, 0x49, 0x51, 0x6A, 0xEE, 0xEB, 0xDE, 0xD0, 0xD4, -0xDF, 0xDC, 0xD7, 0xCF, 0xC5, 0xBE, 0xB7, 0xB1, 0xAE, 0xAF, 0xBC, 0xD3, 0xE7, 0x48, 0x31, 0x2E, -0x2F, 0x2E, 0x2F, 0x36, 0x44, 0x5B, 0xF4, 0xCC, 0xBF, 0xC2, 0xD4, 0xD1, 0xCF, 0x59, 0x4B, 0x51, -0x4B, 0x46, 0x44, 0x4C, 0x56, 0x51, 0x66, 0xE5, 0xE6, 0xE0, 0xDF, 0xD6, 0xD1, 0xCD, 0xC0, 0xBB, -0xB7, 0xB0, 0xAD, 0xB0, 0xBD, 0xCE, 0xEF, 0x44, 0x31, 0x2F, 0x30, 0x2E, 0x2E, 0x39, 0x46, 0x56, -0xF0, 0xC7, 0xBD, 0xC2, 0xC9, 0xC8, 0xCC, 0x7A, 0x50, 0x57, 0x4F, 0x42, 0x42, 0x4A, 0x4C, 0x4C, -0x5D, 0xEF, 0xEE, 0xE3, 0xD6, 0xD1, 0xD2, 0xCC, 0xC0, 0xBC, 0xBA, 0xB3, 0xAE, 0xAF, 0xBA, 0xC9, -0xD6, 0x4F, 0x36, 0x30, 0x30, 0x2E, 0x2D, 0x34, 0x3E, 0x45, 0x5C, 0xCF, 0xC1, 0xC0, 0xC1, 0xC0, -0xC9, 0xD9, 0xF5, 0x54, 0x4A, 0x47, 0x40, 0x41, 0x42, 0x45, 0x4E, 0x53, 0x68, 0xE8, 0xE1, 0xD1, -0xCD, 0xCC, 0xC5, 0xBF, 0xBB, 0xB8, 0xB4, 0xAF, 0xB0, 0xBD, 0xCB, 0xD8, 0x4B, 0x36, 0x34, 0x33, -0x2E, 0x2E, 0x37, 0x3D, 0x44, 0x62, 0xCD, 0xC4, 0xC0, 0xBE, 0xBF, 0xC5, 0xCF, 0xEE, 0x66, 0x4F, -0x45, 0x43, 0x40, 0x41, 0x44, 0x47, 0x51, 0x5B, 0x71, 0xDD, 0xD8, 0xCE, 0xC8, 0xC6, 0xC1, 0xBD, -0xB9, 0xB7, 0xB2, 0xAF, 0xB9, 0xC3, 0xCC, 0x71, 0x40, 0x37, 0x36, 0x31, 0x2E, 0x33, 0x3A, 0x3D, -0x4A, 0xE6, 0xCF, 0xC7, 0xBE, 0xBF, 0xC2, 0xCA, 0xD3, 0xE6, 0x50, 0x4C, 0x4B, 0x3E, 0x3E, 0x43, -0x41, 0x47, 0x4D, 0x59, 0x75, 0xE4, 0xD0, 0xCC, 0xC9, 0xC3, 0xBF, 0xBD, 0xBB, 0xB8, 0xB3, 0xB3, -0xBB, 0xC3, 0xCD, 0x6A, 0x42, 0x3A, 0x38, 0x31, 0x30, 0x36, 0x39, 0x3C, 0x4B, 0x7A, 0xDB, 0xCB, -0xC1, 0xC3, 0xC5, 0xC5, 0xD5, 0xE5, 0x6B, 0x4E, 0x4C, 0x43, 0x40, 0x45, 0x42, 0x48, 0x50, 0x55, -0x6C, 0xE8, 0xD7, 0xCD, 0xCC, 0xC7, 0xC3, 0xC0, 0xBD, 0xBC, 0xB8, 0xB2, 0xB5, 0xBD, 0xC2, 0xD0, -0x5D, 0x45, 0x3C, 0x38, 0x32, 0x32, 0x37, 0x37, 0x3C, 0x4C, 0x5E, 0xEE, 0xCD, 0xC5, 0xC8, 0xC6, -0xC5, 0xD0, 0xE2, 0xF9, 0x5D, 0x4D, 0x47, 0x46, 0x42, 0x41, 0x47, 0x4B, 0x4E, 0x63, 0xEA, 0xE3, -0xD3, 0xC9, 0xCB, 0xC8, 0xC0, 0xBF, 0xBD, 0xBA, 0xB5, 0xB6, 0xBC, 0xC1, 0xCC, 0x7E, 0x4B, 0x3F, -0x3B, 0x35, 0x32, 0x37, 0x37, 0x3A, 0x45, 0x50, 0x6D, 0xD7, 0xC8, 0xC6, 0xC7, 0xC2, 0xCA, 0xD7, -0xDB, 0x6D, 0x59, 0x4F, 0x48, 0x48, 0x45, 0x46, 0x4D, 0x4F, 0x5E, 0xFF, 0xE6, 0xD7, 0xD4, 0xCF, -0xD1, 0xD5, 0xD0, 0xD3, 0xCD, 0xC6, 0xC1, 0xBA, 0xB8, 0xBB, 0xC2, 0xC4, 0xCD, 0x58, 0x4C, 0x48, -0x39, 0x36, 0x39, 0x36, 0x37, 0x3E, 0x44, 0x4B, 0x66, 0xDC, 0xD4, 0xCA, 0xC3, 0xC5, 0xC7, 0xCA, -0xCE, 0xDD, 0x6D, 0x5F, 0x4F, 0x46, 0x47, 0x46, 0x43, 0x49, 0x4E, 0x53, 0x65, 0xF1, 0xE4, 0xD9, -0xCF, 0xCE, 0xCD, 0xC6, 0xC3, 0xC0, 0xBA, 0xB7, 0xB7, 0xBD, 0xC4, 0xC5, 0xFD, 0x4A, 0x4C, 0x3B, -0x33, 0x37, 0x34, 0x32, 0x3A, 0x3F, 0x47, 0x5B, 0xDC, 0xCB, 0xC8, 0xBE, 0xBC, 0xC3, 0xC0, 0xC7, -0xDE, 0xEC, 0x5B, 0x49, 0x45, 0x3F, 0x3E, 0x3F, 0x40, 0x49, 0x4E, 0x5B, 0xEF, 0xDF, 0xD4, 0xCC, -0xCA, 0xC7, 0xC5, 0xC3, 0xBF, 0xBD, 0xBB, 0xB8, 0xB8, 0xC0, 0xCB, 0xCA, 0x68, 0x45, 0x4D, 0x3D, -0x35, 0x3C, 0x3A, 0x38, 0x41, 0x49, 0x4D, 0x69, 0xDB, 0xD3, 0xCD, 0xC5, 0xC6, 0xCC, 0xCB, 0xD0, -0xEB, 0x76, 0x5C, 0x4C, 0x48, 0x44, 0x41, 0x41, 0x44, 0x49, 0x4E, 0x5D, 0x7E, 0xE6, 0xD4, 0xCE, -0xCE, 0xCB, 0xCB, 0xCE, 0xCE, 0xCA, 0xC9, 0xC6, 0xBE, 0xBC, 0xC6, 0xCA, 0xC4, 0xE0, 0x58, 0x77, -0x4A, 0x3D, 0x45, 0x3D, 0x39, 0x3F, 0x41, 0x40, 0x4C, 0x61, 0x6D, 0xE7, 0xCE, 0xCC, 0xCC, 0xC9, -0xCA, 0xD1, 0xDB, 0xE2, 0x66, 0x54, 0x50, 0x48, 0x45, 0x47, 0x47, 0x4B, 0x51, 0x5A, 0x71, 0xEB, -0xDD, 0xD5, 0xD2, 0xCF, 0xCC, 0xCC, 0xCA, 0xC4, 0xC3, 0xBF, 0xB9, 0xBD, 0xCB, 0xC0, 0xC9, 0x56, -0x71, 0x5A, 0x3A, 0x3F, 0x3E, 0x34, 0x3A, 0x3E, 0x3C, 0x42, 0x54, 0x62, 0x73, 0xCF, 0xC8, 0xCC, -0xC3, 0xC3, 0xCD, 0xCF, 0xDD, 0x6D, 0x56, 0x4D, 0x48, 0x42, 0x42, 0x44, 0x45, 0x4D, 0x57, 0x61, -0xED, 0xDB, 0xD5, 0xCD, 0xCD, 0xCD, 0xCD, 0xD0, 0xCD, 0xD1, 0xCA, 0xC4, 0xC7, 0xBA, 0xC2, 0xD8, -0xBB, 0xD7, 0x53, 0xCC, 0x4E, 0x3E, 0x57, 0x3E, 0x3B, 0x43, 0x3F, 0x3E, 0x46, 0x52, 0x50, 0x60, -0xD6, 0xD9, 0xD4, 0xC6, 0xCA, 0xCF, 0xCB, 0xD5, 0xF4, 0xFB, 0x5D, 0x4E, 0x4C, 0x4B, 0x46, 0x45, -0x4F, 0x4E, 0x4D, 0x7E, 0x6F, 0x71, 0xD4, 0xE1, 0xDF, 0xCE, 0xD9, 0xD5, 0xCD, 0xCD, 0xC5, 0xBF, -0xB9, 0xBA, 0xC6, 0xBA, 0xBF, 0x6C, 0xCE, 0xFB, 0x3D, 0x4D, 0x40, 0x35, 0x3C, 0x3B, 0x38, 0x3E, -0x47, 0x4B, 0x52, 0xDF, 0xD8, 0xD6, 0xC3, 0xC6, 0xCB, 0xC5, 0xCD, 0xDD, 0xE4, 0x6B, 0x50, 0x4D, -0x49, 0x44, 0x45, 0x47, 0x48, 0x49, 0x50, 0x5D, 0x5C, 0xEE, 0xDD, 0xE7, 0xD1, 0xD5, 0xDC, 0xD0, -0xDA, 0xD8, 0xD1, 0xCD, 0xC6, 0xBF, 0xBA, 0xC1, 0xC4, 0xBA, 0xD3, 0xE1, 0xCC, 0x4D, 0x49, 0x4F, -0x3B, 0x3B, 0x3E, 0x3A, 0x3C, 0x42, 0x4A, 0x4C, 0x6D, 0xD9, 0xDC, 0xC9, 0xC4, 0xCA, 0xC6, 0xC8, -0xD0, 0xD8, 0xE5, 0x68, 0x58, 0x53, 0x4C, 0x4A, 0x4B, 0x4C, 0x4E, 0x56, 0x5F, 0x69, 0xEA, 0xE0, -0xE8, 0xD5, 0xDD, 0xE7, 0xD9, 0xFB, 0xF9, 0xF5, 0x64, 0x75, 0x6D, 0xF5, 0xE5, 0xD8, 0xC8, 0xC4, -0xBB, 0xBF, 0xC4, 0xB7, 0xCC, 0xD6, 0xC3, 0x56, 0x52, 0x5F, 0x3D, 0x3E, 0x3E, 0x39, 0x3A, 0x3D, -0x41, 0x41, 0x4F, 0x6B, 0x63, 0xD6, 0xCD, 0xD1, 0xC7, 0xC8, 0xCE, 0xCE, 0xD6, 0xE8, 0x71, 0x67, -0x55, 0x4F, 0x52, 0x4D, 0x4F, 0x56, 0x56, 0x5E, 0x6D, 0x7E, 0xF5, 0xDF, 0xE3, 0xEE, 0xDC, 0x7B, -0x73, 0xED, 0x5C, 0x6A, 0x69, 0x5B, 0x70, 0x6D, 0xEF, 0xE1, 0xCF, 0xC8, 0xC4, 0xB9, 0xC5, 0xC2, -0xB6, 0xDC, 0xCD, 0xC3, 0x49, 0x60, 0x57, 0x3A, 0x40, 0x3D, 0x38, 0x3A, 0x3E, 0x3F, 0x3E, 0x53, -0x58, 0x59, 0xD3, 0xD7, 0xD2, 0xC4, 0xC9, 0xCA, 0xC8, 0xCD, 0xD8, 0xDE, 0xEE, 0x5F, 0x5D, 0x59, -0x4F, 0x54, 0x56, 0x54, 0x5D, 0x69, 0x6D, 0xF2, 0xE1, 0xDF, 0xDB, 0xD8, 0xDD, 0xE8, 0xEB, 0x71, -0x5D, 0x66, 0x56, 0x52, 0x62, 0x52, 0x5C, 0x74, 0x63, 0xE2, 0xDD, 0xD4, 0xCA, 0xC9, 0xBF, 0xC0, -0xBB, 0xBB, 0xCC, 0xBB, 0xC8, 0x68, 0xC8, 0x58, 0x44, 0x5D, 0x3C, 0x3B, 0x40, 0x3A, 0x3B, 0x3F, -0x44, 0x43, 0x52, 0x7A, 0x62, 0xD4, 0xCB, 0xD1, 0xC3, 0xC4, 0xC8, 0xC5, 0xCA, 0xD0, 0xD9, 0xE1, -0x6C, 0x5C, 0x5B, 0x4E, 0x4D, 0x50, 0x4C, 0x50, 0x58, 0x58, 0x63, 0x79, 0xFE, 0xED, 0xE1, 0xE7, -0xEE, 0xE6, 0xFB, 0x6D, 0x7A, 0x61, 0x5D, 0x62, 0x59, 0x5B, 0x5E, 0x5C, 0x64, 0x69, 0x72, 0xFE, -0xF1, 0xE6, 0xE2, 0xDB, 0xD9, 0xD6, 0xD3, 0xD3, 0xD0, 0xD0, 0xCF, 0xCE, 0xCD, 0xCD, 0xDA, 0xDB, -0xDA, 0x64, 0x69, 0x63, 0x4C, 0x4F, 0x4D, 0x46, 0x4A, 0x4A, 0x48, 0x4D, 0x52, 0x52, 0x5C, 0x70, -0x71, 0xEB, 0xDC, 0xDE, 0xD7, 0xD4, 0xD8, 0xD5, 0xD7, 0xDD, 0xDF, 0xE7, 0xFD, 0x71, 0x69, 0x5D, -0x5A, 0x5A, 0x57, 0x56, 0x5B, 0x59, 0x5A, 0x69, 0x62, 0x6A, 0xF5, 0x73, 0xF1, 0xE7, 0xF4, 0xE6, -0xE6, 0xEE, 0xEA, 0xED, 0xF5, 0xF1, 0xED, 0xF0, 0xEA, 0xE4, 0xE7, 0xDF, 0xDE, 0xDF, 0xDC, 0xDE, -0xDF, 0xE1, 0xE8, 0xEE, 0xFD, 0x77, 0x6B, 0x64, 0x61, 0x5C, 0x5C, 0x5C, 0x5B, 0x5F, 0x61, 0x67, -0x6E, 0x78, 0xF8, 0xF1, 0xEA, 0xE5, 0xE4, 0xE2, 0xE1, 0xE5, 0xEA, 0xED, 0xFA, 0x77, 0x6E, 0x66, -0x62, 0x5D, 0x5C, 0x5A, 0x59, 0x59, 0x59, 0x5B, 0x5C, 0x5D, 0x64, 0x64, 0x6C, 0x77, 0x7C, 0xEE, -0xEB, 0xE0, 0xDB, 0xD7, 0xCF, 0xCF, 0xCA, 0xC8, 0xC5, 0xC4, 0xCD, 0xC7, 0xCB, 0xE8, 0xD3, 0xFD, -0x55, 0x67, 0x4B, 0x45, 0x48, 0x40, 0x3F, 0x42, 0x43, 0x43, 0x4A, 0x50, 0x50, 0x6C, 0xF5, 0xF2, -0xD4, 0xD2, 0xD1, 0xCB, 0xCC, 0xCE, 0xCE, 0xD0, 0xDA, 0xDC, 0xDF, 0xFD, 0x7D, 0x6F, 0x5E, 0x5F, -0x5D, 0x59, 0x5A, 0x5C, 0x5C, 0x5F, 0x68, 0x68, 0x6F, 0xFC, 0xFC, 0xEF, 0xE9, 0xEB, 0xE9, 0xE9, -0xED, 0xEF, 0xF2, 0xF8, 0xFE, 0xFE, 0x7C, 0x7C, 0x7E, 0x79, 0xFE, 0xFB, 0xFE, 0xF7, 0xF6, 0xF9, -0xF6, 0xF8, 0xFD, 0xFF, 0x7C, 0x73, 0x70, 0x6E, 0x6A, 0x6A, 0x69, 0x68, 0x6A, 0x6B, 0x6C, 0x6F, -0x74, 0x79, 0xFF, 0xFA, 0xF8, 0xF2, 0xF4, 0xF6, 0xF3, 0xFE, 0xFB, 0x74, 0x62, 0x69, 0x5F, 0x5A, -0x66, 0x5B, 0x5D, 0x70, 0x61, 0x78, 0xED, 0xF4, 0xDB, 0xD7, 0xD0, 0xCB, 0xC9, 0xC3, 0xCC, 0xC9, -0xC4, 0xDD, 0xD2, 0xD7, 0x59, 0x6E, 0x57, 0x45, 0x4B, 0x45, 0x3F, 0x43, 0x44, 0x43, 0x49, 0x51, -0x4F, 0x5E, 0xFB, 0x70, 0xDE, 0xD8, 0xDC, 0xD1, 0xD2, 0xD7, 0xD4, 0xD6, 0xDD, 0xDF, 0xDF, 0xF1, -0xF2, 0xF2, 0x6F, 0x73, 0x72, 0x69, 0x6D, 0x6F, 0x6D, 0x72, 0x7B, 0x79, 0x7E, 0xF8, 0xFD, 0xF9, -0xF6, 0xFD, 0xFA, 0xFA, 0xFD, 0xFB, 0xFB, 0xFD, 0xFA, 0xF6, 0xF8, 0xF3, 0xF2, 0xF4, 0xEF, 0xF1, -0xF4, 0xF2, 0xF6, 0xF9, 0xF8, 0xF9, 0xFD, 0xFD, 0xFC, 0x7F, 0x7E, 0x7D, 0x79, 0x76, 0x74, 0x71, -0x6F, 0x6E, 0x6C, 0x6C, 0x6D, 0x6B, 0x6C, 0x6D, 0x6C, 0x71, 0x71, 0x70, 0x7E, 0x79, 0x7C, 0xFA, -0x7B, 0xFD, 0xFC, 0x7A, 0xFE, 0x7C, 0x78, 0x7B, 0x76, 0x76, 0x78, 0x77, 0x77, 0x7C, 0x7E, 0xFF, -0xF2, 0xF3, 0xED, 0xE7, 0xE8, 0xE0, 0xDE, 0xDC, 0xD7, 0xD8, 0xD3, 0xD4, 0xDA, 0xD7, 0xDF, 0xEB, -0xED, 0x6A, 0x60, 0x5C, 0x52, 0x4F, 0x4D, 0x4B, 0x4A, 0x4B, 0x4B, 0x4D, 0x50, 0x54, 0x5B, 0x66, -0x6F, 0xF1, 0xE6, 0xDF, 0xDA, 0xD8, 0xD8, 0xD7, 0xD9, 0xDB, 0xDE, 0xE2, 0xEA, 0xF1, 0xFA, 0x78, -0x70, 0x6D, 0x69, 0x68, 0x66, 0x65, 0x65, 0x66, 0x66, 0x67, 0x6A, 0x6B, 0x6C, 0x70, 0x74, 0x79, -0xFB, 0xF5, 0xEF, 0xEB, 0xE9, 0xE6, 0xE3, 0xE2, 0xE1, 0xE0, 0xE0, 0xE0, 0xE1, 0xE3, 0xE3, 0xE6, -0xE9, 0xEB, 0xEF, 0xF7, 0x7F, 0x75, 0x6E, 0x6A, 0x66, 0x62, 0x61, 0x5F, 0x5F, 0x60, 0x5F, 0x62, -0x65, 0x66, 0x6C, 0x6E, 0x72, 0x7D, 0xFE, 0xF6, 0xEF, 0xED, 0xE9, 0xE7, 0xE4, 0xE3, 0xE0, 0xDF, -0xE1, 0xDF, 0xE1, 0xE3, 0xE3, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xED, 0xF5, 0xF6, 0xF9, 0x75, -0x76, 0x6E, 0x66, 0x65, 0x5E, 0x5B, 0x5A, 0x56, 0x55, 0x54, 0x53, 0x54, 0x55, 0x57, 0x59, 0x5D, -0x60, 0x66, 0x70, 0x79, 0xFA, 0xF0, 0xEE, 0xEC, 0xED, 0xED, 0xF0, 0xF5, 0xF8, 0xFF, 0xFD, 0x7E, -0x7D, 0xFE, 0x7E, 0xFD, 0xF8, 0xF6, 0xEF, 0xED, 0xEB, 0xE8, 0xE4, 0xE3, 0xE0, 0xDF, 0xDE, 0xDC, -0xDD, 0xDC, 0xDD, 0xE1, 0xE2, 0xE9, 0xF3, 0x7A, 0x65, 0x61, 0x5B, 0x56, 0x56, 0x51, 0x50, 0x52, -0x50, 0x55, 0x58, 0x59, 0x5F, 0x68, 0x70, 0xFC, 0xEE, 0xE9, 0xE4, 0xE1, 0xE4, 0xE4, 0xE7, 0xED, -0xEE, 0xF8, 0xFF, 0x7D, 0x76, 0x76, 0x7A, 0x7B, 0xFF, 0xF9, 0xF7, 0xF1, 0xED, 0xED, 0xEB, 0xE9, -0xEA, 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF2, 0xF3, 0xF0, 0xF1, 0xF3, 0xEF, 0xF3, 0xF5, -0xF4, 0xFB, 0xFC, 0xFC, 0x7C, 0x7A, 0x76, 0x70, 0x6F, 0x6F, 0x6C, 0x6C, 0x6C, 0x68, 0x6B, 0x6B, -0x68, 0x6D, 0x6B, 0x6B, 0x6F, 0x6E, 0x70, 0x77, 0x76, 0x7A, 0x7F, 0xFF, 0xFD, 0xF6, 0xF7, 0xF5, -0xEF, 0xF4, 0xF1, 0xEF, 0xF2, 0xEE, 0xF0, 0xF1, 0xEE, 0xF0, 0xF1, 0xF1, 0xF2, 0xF6, 0xF9, 0xFA, -0x7F, 0x7E, 0x7A, 0x76, 0x7A, 0x73, 0x72, 0x74, 0x6F, 0x73, 0x75, 0x73, 0x75, 0x74, 0x76, 0x73, -0x77, 0x7A, 0x76, 0x7E, 0x78, 0x76, 0x7E, 0x76, 0x7A, 0x79, 0x74, 0x7D, 0x77, 0x77, 0x79, 0x74, -0x76, 0x77, 0x7A, 0x79, 0x7C, 0xFF, 0x79, 0xFC, 0xFA, 0x7E, 0xF4, 0xF9, 0xFC, 0xF6, 0x7E, 0xFF, -0xFF, 0x7B, 0x7C, 0x77, 0x75, 0x76, 0x78, 0x76, 0x74, 0x7B, 0x74, 0x75, 0x7E, 0x74, 0x7B, 0x7C, -0x72, 0x7A, 0x77, 0x74, 0x7A, 0x75, 0x72, 0x73, 0x72, 0x6E, 0x6E, 0x70, 0x6D, 0x70, 0x71, 0x6E, -0x75, 0x73, 0x72, 0x79, 0x78, 0x7B, 0xFF, 0xFF, 0xFD, 0xFA, 0xFC, 0xFB, 0xF7, 0xFD, 0xFE, 0xFA, -0x7D, 0x7D, 0xFE, 0x79, 0x7B, 0x7C, 0x7B, 0x7E, 0x7E, 0xFD, 0xFA, 0xFA, 0xF7, 0xF8, 0xF9, 0xF9, -0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x7D, 0x7B, 0x77, 0x78, 0x74, 0x71, 0x74, 0x6F, 0x6F, 0x71, 0x6D, -0x6F, 0x71, 0x6F, 0x73, 0x74, 0x77, 0x7B, 0x7D, 0x7F, 0xFF, 0xFC, 0xFE, 0xFB, 0xF8, 0xFB, 0xF7, -0xF9, 0xFE, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0x7E, 0x7C, 0x7E, 0x7B, 0x7B, 0x7E, 0x7C, 0xFF, 0xFE, -0xFE, 0xFB, 0xFE, 0xFF, 0xFD, 0xFE, 0x7E, 0x7F, 0x7A, 0x78, 0x79, 0x75, 0x74, 0x75, 0x73, 0x75, -0x77, 0x77, 0x79, 0x7B, 0x7D, 0xFF, 0xFA, 0xF8, 0xF4, 0xF2, 0xF5, 0xF1, 0xF2, 0xF6, 0xF3, 0xF6, -0xFA, 0xFB, 0xFC, 0x7D, 0x7A, 0x7A, 0x74, 0x77, 0x78, 0x74, 0x7A, 0x77, 0x7A, 0xFF, 0x7F, 0xF9, -0xFB, 0xFB, 0xF9, 0xFF, 0x7F, 0x7C, 0x7A, 0x77, 0x72, 0x73, 0x6E, 0x6E, 0x6E, 0x6D, 0x70, 0x70, -0x75, 0x7D, 0x7B, 0xFE, 0xFD, 0xFF, 0xFC, 0xFF, 0x7E, 0xFF, 0x7E, 0x7A, 0x79, 0x76, 0x6F, 0x72, -0x72, 0x6F, 0x73, 0x6F, 0x6E, 0x74, 0x72, 0x74, 0x7C, 0x7C, 0xFF, 0xF9, 0xFA, 0xF9, 0xF5, 0xF7, -0xF7, 0xF5, 0xFA, 0xFA, 0xF7, 0xFA, 0xF9, 0xF8, 0xFC, 0xF9, 0xF8, 0xFA, 0xF6, 0xF9, 0xFC, 0xFA, -0xFD, 0xFE, 0xFD, 0xFF, 0x7E, 0x7E, 0x7D, 0x7A, 0x7C, 0x7B, 0x79, 0x7C, 0x7B, 0x7B, 0xFF, 0x7E, -0xFF, 0xFC, 0xFE, 0xFE, 0xFB, 0xFD, 0x7C, 0x7F, 0x7F, 0x76, 0x78, 0x78, 0x74, 0x78, 0x78, 0x78, -0x7B, 0x7A, 0x7A, 0x79, 0x77, 0x78, 0x79, 0x78, 0x7A, 0x7B, 0x7A, 0x7C, 0x7F, 0x7D, 0xFF, 0xFC, -0xFD, 0xFA, 0xFB, 0xFD, 0xFE, 0xFF, 0x7F, 0x7D, 0x7C, 0x7A, 0x7B, 0x7D, 0x7D, 0x7E, 0x7E, 0x7D, -0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7F, 0xFF, 0xFE, 0xFC, 0xFC, 0xFB, -0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xFB, 0xFB, 0xFD, 0xFF, 0x7D, 0x7B, 0x7B, 0x7A, 0x77, 0x77, 0x78, -0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7B, 0x7D, 0x7E, 0xFF, 0xFE, 0xFE, 0xFD, 0xFC, 0xFC, 0xFC, 0xFC, -0xFE, 0xFF, 0xFF, 0x7E, 0x7B, 0x78, 0x77, 0x76, 0x74, 0x73, 0x73, 0x71, 0x72, 0x73, 0x73, 0x74, -0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x7A, 0x7D, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0x7F, 0x7E, 0x7D, 0x7D, 0x7C, 0x7A, 0x79, 0x79, 0x78, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7A, -0x79, 0x7B, 0x7B, 0x7B, 0x7C, 0x7A, 0x7A, 0x79, 0x78, 0x78, 0x76, 0x76, 0x75, 0x73, 0x73, 0x73, -0x74, 0x77, 0x79, 0x7A, 0x7D, 0x7F, 0xFE, 0xFD, 0xFD, 0xFD, 0xFC, 0xFC, 0xFD, 0xFD, 0xFE, 0xFF, -0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4C, 0x49, 0x53, 0x54, 0x4A, 0x00, -0x00, 0x00, 0x49, 0x4E, 0x46, 0x4F, 0x49, 0x53, 0x46, 0x54, 0x3E, 0x00, 0x00, 0x00, 0x46, 0x69, -0x6C, 0x65, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x6F, -0x6C, 0x64, 0x57, 0x61, 0x76, 0x65, 0x2E, 0x20, 0x20, 0x47, 0x6F, 0x6C, 0x64, 0x57, 0x61, 0x76, -0x65, 0x20, 0x63, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, -0x43, 0x68, 0x72, 0x69, 0x73, 0x20, 0x43, 0x72, 0x61, 0x69, 0x67, 0x00 - -}; - -static const uint8_t shaun_png[] PROGMEM = { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x57, 0x08, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x0C, 0xE0, - 0x57, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, - 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x42, 0x8A, 0x00, - 0x00, 0x42, 0x8A, 0x01, 0x34, 0xA8, 0x6C, 0x25, 0x00, 0x00, 0x00, 0x09, 0x76, 0x70, 0x41, 0x67, - 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x57, 0x00, 0x73, 0x4D, 0x3B, 0xD6, 0x00, 0x00, 0x1B, - 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0xED, 0x9D, 0x79, 0x74, 0x14, 0x55, 0xBE, 0xC7, 0x3F, - 0x55, 0xDD, 0x49, 0x67, 0x5F, 0x3A, 0x7B, 0x42, 0x82, 0x61, 0x49, 0x02, 0x81, 0xB0, 0x29, 0x32, - 0x22, 0x3C, 0x16, 0x41, 0x45, 0x10, 0x15, 0x7D, 0x13, 0x65, 0xD0, 0x81, 0xE3, 0x7B, 0xCC, 0xD3, - 0x51, 0x04, 0xF5, 0xE1, 0xB8, 0xCE, 0xE6, 0x28, 0x22, 0x23, 0x33, 0x2C, 0x2A, 0x22, 0xEE, 0x02, - 0x22, 0x0C, 0x2A, 0x83, 0x28, 0x7B, 0x20, 0xC8, 0x92, 0x10, 0xD9, 0x97, 0x40, 0x08, 0x81, 0x90, - 0x3D, 0x64, 0x21, 0xE9, 0x4E, 0x3A, 0x5D, 0xF7, 0xFD, 0x71, 0xBB, 0x93, 0x0E, 0x10, 0xD2, 0x59, - 0x3A, 0x01, 0xCF, 0xFB, 0x9E, 0xD3, 0xE7, 0xA4, 0x6E, 0xA5, 0x6E, 0xDD, 0xFA, 0xFD, 0xEA, 0xFE, - 0xEE, 0xEF, 0xFE, 0xB6, 0x52, 0xB8, 0xFE, 0xA1, 0x03, 0x7A, 0x01, 0x43, 0x81, 0x9B, 0x80, 0x6A, - 0x60, 0x3F, 0xA0, 0x02, 0x8F, 0x00, 0xB7, 0x03, 0xA1, 0x80, 0x00, 0x72, 0x81, 0xAD, 0xC0, 0xC7, - 0xC0, 0x6E, 0xA0, 0xAE, 0xB3, 0x07, 0xFF, 0x4B, 0x43, 0x30, 0xF0, 0x67, 0x20, 0x1B, 0xB0, 0x20, - 0x89, 0xAE, 0x01, 0x95, 0x40, 0xB9, 0xED, 0xF8, 0x6A, 0xBF, 0x3C, 0xDB, 0x75, 0x81, 0xCD, 0xF4, - 0xAF, 0x03, 0x8C, 0x40, 0x2C, 0xD0, 0x13, 0x88, 0x00, 0x0C, 0x9D, 0xF9, 0xC0, 0x4A, 0x27, 0xDD, - 0xD7, 0x13, 0x88, 0x07, 0xFA, 0x03, 0x51, 0x36, 0x22, 0xE4, 0x03, 0x87, 0x80, 0x83, 0x48, 0x62, - 0x1B, 0x81, 0x85, 0x40, 0x72, 0x2B, 0xC7, 0xA9, 0x01, 0x5F, 0x00, 0x2F, 0x03, 0x35, 0xB6, 0xE3, - 0x0A, 0xDB, 0xDF, 0xDE, 0xC0, 0x48, 0x60, 0x22, 0x30, 0x08, 0xC9, 0x78, 0x3D, 0x92, 0xD1, 0x99, - 0xC0, 0x0F, 0xC0, 0x5A, 0xE0, 0x5C, 0x47, 0x13, 0xA6, 0xA3, 0x19, 0xA2, 0x07, 0x46, 0x00, 0x4F, - 0x22, 0x45, 0x4D, 0xD0, 0x65, 0x63, 0xB8, 0x84, 0x14, 0x47, 0xEF, 0x03, 0x09, 0xC0, 0x4B, 0x48, - 0xD1, 0x44, 0x40, 0x40, 0x00, 0x31, 0x31, 0x31, 0x54, 0x56, 0x56, 0x92, 0x93, 0x93, 0x83, 0xD5, - 0x6A, 0x25, 0x3A, 0x3A, 0x9A, 0x89, 0x13, 0x27, 0x32, 0x60, 0xC0, 0x00, 0xAC, 0x56, 0x2B, 0xBB, - 0x77, 0xEF, 0x66, 0xDD, 0xBA, 0x75, 0x14, 0x16, 0x16, 0xDA, 0xFB, 0xAB, 0x03, 0x4E, 0x23, 0x67, - 0x4D, 0x9D, 0x8D, 0xC0, 0xFB, 0x90, 0x22, 0xF0, 0x1E, 0xC0, 0xAB, 0x89, 0x71, 0x6A, 0xC0, 0x11, - 0xE0, 0x0D, 0xE0, 0x6B, 0xE4, 0xEC, 0xFC, 0xC5, 0xC1, 0x07, 0x78, 0x0D, 0x28, 0xA5, 0x69, 0x51, - 0x63, 0xFF, 0x99, 0x80, 0x2A, 0xFB, 0xF1, 0xC0, 0x81, 0x03, 0xC5, 0xA6, 0x4D, 0x9B, 0x44, 0x49, - 0x49, 0x89, 0xC8, 0xCA, 0xCA, 0x12, 0xAF, 0xBF, 0xFE, 0xBA, 0x78, 0xFC, 0xF1, 0xC7, 0x45, 0x5A, - 0x5A, 0x9A, 0xD0, 0x34, 0x4D, 0xD8, 0x51, 0x57, 0x57, 0x27, 0xB6, 0x6E, 0xDD, 0x2A, 0xFA, 0xF7, - 0xEF, 0xDF, 0x5C, 0xFF, 0x8D, 0x7E, 0x8A, 0xA2, 0x08, 0x55, 0x55, 0xAF, 0x76, 0xAE, 0x02, 0x78, - 0x0E, 0xF9, 0x22, 0x75, 0x08, 0x3A, 0x6A, 0x86, 0x78, 0x02, 0x7F, 0x05, 0x9E, 0x02, 0xDC, 0x00, - 0x14, 0x45, 0x21, 0x24, 0x24, 0x84, 0xE8, 0xE8, 0x68, 0xDC, 0xDD, 0xDD, 0xB9, 0x70, 0xE1, 0x02, - 0xB9, 0xB9, 0xB9, 0xD4, 0xD5, 0x35, 0x5E, 0x87, 0xBD, 0xBC, 0xBC, 0x58, 0xB1, 0x62, 0x05, 0x13, - 0x26, 0x4C, 0xA8, 0x6F, 0x13, 0x42, 0x20, 0x84, 0x40, 0x55, 0xD5, 0xAB, 0xDE, 0x6C, 0xD7, 0xAE, - 0x5D, 0x3C, 0xFC, 0xF0, 0xC3, 0xE4, 0xE4, 0xE4, 0x34, 0xFD, 0xE0, 0x8A, 0x42, 0x7C, 0x7C, 0x3C, - 0xF7, 0xDC, 0x73, 0x0F, 0xFD, 0xFA, 0xF5, 0xC3, 0xC3, 0xC3, 0x83, 0xEC, 0xEC, 0x6C, 0x36, 0x6C, - 0xD8, 0x40, 0x6A, 0x6A, 0x2A, 0x35, 0x35, 0x35, 0xF6, 0x7F, 0xAD, 0xB4, 0x8D, 0xFB, 0x73, 0xC0, - 0xDA, 0x41, 0xF4, 0x72, 0x39, 0x7E, 0x8F, 0x7C, 0xEB, 0x05, 0x20, 0xC2, 0xC2, 0xC2, 0xC4, 0x2B, - 0xAF, 0xBC, 0x22, 0x0E, 0x1E, 0x3C, 0x28, 0x2E, 0x5E, 0xBC, 0x28, 0x2A, 0x2A, 0x2A, 0x44, 0x56, - 0x56, 0x96, 0x58, 0xBC, 0x78, 0xB1, 0x48, 0x48, 0x48, 0x68, 0xF4, 0x96, 0xC6, 0xC4, 0xC4, 0x88, - 0x33, 0x67, 0xCE, 0x88, 0x96, 0xE2, 0xE3, 0x8F, 0x3F, 0x16, 0x77, 0xDE, 0x79, 0xA7, 0x78, 0xFE, - 0xF9, 0xE7, 0xC5, 0xBB, 0xEF, 0xBE, 0x2B, 0xDE, 0x7E, 0xFB, 0x6D, 0xF1, 0xE0, 0x83, 0x0F, 0x0A, - 0xA3, 0xD1, 0x28, 0xDC, 0xDC, 0xDC, 0xC4, 0xD4, 0xA9, 0x53, 0xC5, 0xE9, 0xD3, 0xA7, 0xAF, 0xB8, - 0xAE, 0xA2, 0xA2, 0x42, 0x2C, 0x58, 0xB0, 0x40, 0x04, 0x07, 0x07, 0x3B, 0x8E, 0xA3, 0x00, 0x58, - 0x0D, 0xFC, 0x0E, 0x08, 0xEF, 0x6C, 0x62, 0xB6, 0x15, 0x7D, 0x80, 0x33, 0xF6, 0x87, 0x8B, 0x8D, - 0x8D, 0x15, 0xEB, 0xD7, 0xAF, 0x6F, 0x24, 0x6A, 0x1C, 0x91, 0x91, 0x91, 0x21, 0x06, 0x0F, 0x1E, - 0x2C, 0x00, 0x61, 0x30, 0x18, 0xC4, 0xAF, 0x7F, 0xFD, 0x6B, 0x51, 0x51, 0x51, 0xD1, 0x62, 0x86, - 0x58, 0xAD, 0x56, 0x61, 0x36, 0x9B, 0x1B, 0xDD, 0xA7, 0xA6, 0xA6, 0x46, 0xEC, 0xD8, 0xB1, 0x43, - 0xAC, 0x5A, 0xB5, 0x4A, 0x94, 0x97, 0x97, 0x37, 0x79, 0xAD, 0xA6, 0x69, 0x62, 0xF1, 0xE2, 0xC5, - 0xC2, 0xC3, 0xC3, 0xE3, 0x72, 0x11, 0x66, 0x05, 0x76, 0x02, 0x43, 0x3A, 0x9B, 0xA8, 0x6D, 0xC1, - 0xDB, 0xF6, 0x07, 0xF2, 0xF5, 0xF5, 0x15, 0x2B, 0x56, 0xAC, 0x68, 0x96, 0x98, 0xC7, 0x8E, 0x1D, - 0x13, 0xF3, 0xE7, 0xCF, 0x17, 0x1B, 0x36, 0x6C, 0x10, 0xA5, 0xA5, 0xA5, 0x2D, 0x66, 0x46, 0x7B, - 0xA0, 0xB2, 0xB2, 0x52, 0x4C, 0x98, 0x30, 0xA1, 0xA9, 0x75, 0xE7, 0x00, 0x52, 0x31, 0x68, 0x77, - 0xB8, 0x7A, 0x0D, 0x89, 0x02, 0x36, 0xDA, 0x07, 0x3F, 0x79, 0xF2, 0x64, 0x3E, 0xFC, 0xF0, 0x43, - 0x0C, 0x86, 0x4E, 0x55, 0xF5, 0x9D, 0xC6, 0xC1, 0x83, 0x07, 0x59, 0xBB, 0x76, 0x2D, 0x16, 0x8B, - 0x85, 0x94, 0x94, 0x14, 0x52, 0x53, 0x53, 0xB1, 0x5A, 0xEB, 0x97, 0x91, 0xF7, 0x91, 0xDA, 0xE2, - 0x0D, 0xB5, 0xAE, 0x8C, 0x07, 0xCC, 0x80, 0xF0, 0xF4, 0xF4, 0x14, 0xEB, 0xD6, 0xAD, 0xEB, 0x94, - 0xB7, 0xBD, 0x3D, 0x50, 0x54, 0x54, 0x24, 0x92, 0x93, 0x93, 0x1D, 0x67, 0x49, 0x36, 0xD0, 0xBD, - 0xB3, 0x09, 0xDC, 0x12, 0x84, 0x00, 0xAB, 0xEC, 0x0F, 0x10, 0x1F, 0x1F, 0x2F, 0x72, 0x73, 0x73, - 0x3B, 0x9B, 0xAE, 0x6D, 0xC2, 0x96, 0x2D, 0x5B, 0x84, 0x8F, 0x8F, 0x8F, 0xA3, 0x6A, 0x7E, 0x47, - 0x7B, 0x13, 0x4D, 0x6D, 0x7B, 0x17, 0x57, 0x85, 0x37, 0x72, 0x53, 0x35, 0xC9, 0xDE, 0xD0, 0xAD, - 0x5B, 0x37, 0x8C, 0x46, 0xA3, 0x8B, 0x6E, 0xD7, 0x31, 0xF0, 0xF5, 0xF5, 0x45, 0xAF, 0xAF, 0xDF, - 0x92, 0xA8, 0x80, 0x47, 0x7B, 0xDF, 0xC3, 0x55, 0x0C, 0xF9, 0x4F, 0xE0, 0x37, 0xD8, 0xD6, 0xA8, - 0x84, 0x84, 0x04, 0x9E, 0x7E, 0xFA, 0x69, 0x3C, 0x3C, 0xDA, 0x7D, 0xFC, 0x1D, 0x8A, 0x5D, 0xBB, - 0x76, 0x51, 0x56, 0x56, 0x66, 0x3F, 0xAC, 0x42, 0xDA, 0xCC, 0xDA, 0x15, 0xAE, 0xD8, 0x81, 0x1A, - 0x81, 0xC7, 0xB1, 0x19, 0xE9, 0x12, 0x12, 0x12, 0xF8, 0xEC, 0xB3, 0xCF, 0xB8, 0xF9, 0xE6, 0x9B, - 0x5D, 0x44, 0xA6, 0x8E, 0x43, 0x5E, 0x5E, 0x23, 0xFA, 0x0B, 0x5C, 0xF7, 0x42, 0xB7, 0x2B, 0x46, - 0x21, 0x6D, 0x52, 0x42, 0xAF, 0xD7, 0x8B, 0x25, 0x4B, 0x96, 0x74, 0xB6, 0xE8, 0x6F, 0x37, 0xEC, - 0xDD, 0xBB, 0x57, 0x84, 0x86, 0x86, 0x3A, 0x2E, 0xEC, 0xB3, 0xDB, 0x9B, 0x78, 0xAE, 0xE0, 0x70, - 0x12, 0x72, 0x0D, 0xA1, 0x6B, 0xD7, 0xAE, 0x8C, 0x1D, 0x3B, 0xD6, 0x85, 0xBC, 0xEF, 0x58, 0x24, - 0x25, 0x25, 0x31, 0x64, 0x48, 0xA3, 0x3D, 0xE1, 0x2D, 0xB4, 0xB3, 0x94, 0x71, 0x05, 0x43, 0x22, - 0xED, 0x7F, 0x44, 0x47, 0x47, 0x13, 0x12, 0x12, 0xE2, 0x1A, 0xEA, 0x74, 0x02, 0x0C, 0x06, 0x03, - 0x89, 0x89, 0x89, 0x8E, 0x4D, 0x51, 0x48, 0x3B, 0x5D, 0xBB, 0xC1, 0x15, 0x0C, 0xA9, 0xEF, 0x53, - 0xA7, 0xD3, 0x35, 0x69, 0x00, 0xBC, 0x51, 0xE1, 0xEB, 0xEB, 0xEB, 0x78, 0x68, 0xE0, 0x06, 0x98, - 0x21, 0xF5, 0xCE, 0x88, 0xBC, 0xBC, 0x3C, 0x47, 0xAD, 0xE4, 0x17, 0x81, 0xD2, 0xD2, 0x52, 0xC7, - 0xC3, 0x6A, 0xDA, 0xD9, 0x57, 0xE2, 0x0A, 0x86, 0x1C, 0x46, 0xEE, 0xCE, 0xC9, 0xCA, 0xCA, 0x22, - 0x35, 0x35, 0xD5, 0x65, 0xC4, 0xE9, 0x68, 0x54, 0x55, 0x55, 0x91, 0x91, 0x91, 0xE1, 0xD8, 0x94, - 0x8D, 0x64, 0xCA, 0x75, 0x8D, 0x70, 0xE0, 0x67, 0x6C, 0x9A, 0xC8, 0xD0, 0xA1, 0x43, 0x45, 0x76, - 0x76, 0x76, 0x67, 0x2B, 0x48, 0xED, 0x82, 0x1F, 0x7E, 0xF8, 0x41, 0xF8, 0xF9, 0xF9, 0x39, 0x6A, - 0x59, 0x4F, 0x74, 0x36, 0xB1, 0x9D, 0xC5, 0x73, 0x48, 0xA3, 0x9B, 0x00, 0xC4, 0xC8, 0x91, 0x23, - 0xC5, 0x8F, 0x3F, 0xFE, 0x28, 0xCC, 0x66, 0xB3, 0xCB, 0x88, 0x65, 0xB5, 0x5A, 0x5D, 0xCA, 0x8C, - 0xAC, 0xAC, 0x2C, 0x31, 0x6C, 0xD8, 0x30, 0x47, 0x66, 0x64, 0x01, 0x71, 0x9D, 0x4D, 0x68, 0x67, - 0x61, 0x04, 0xD6, 0x39, 0x0C, 0x5E, 0xF8, 0xF8, 0xF8, 0x88, 0x05, 0x0B, 0x16, 0xB8, 0x84, 0x58, - 0x99, 0x99, 0x99, 0x62, 0xFA, 0xF4, 0xE9, 0x62, 0xD9, 0xB2, 0x65, 0xC2, 0x64, 0x32, 0x5D, 0x71, - 0x5E, 0xD3, 0x34, 0x71, 0xEE, 0xDC, 0x39, 0x91, 0x97, 0x97, 0xD7, 0xAA, 0xFE, 0x4F, 0x9E, 0x3C, - 0x29, 0x46, 0x8C, 0x18, 0x71, 0xB9, 0x09, 0xFE, 0xF5, 0xCE, 0x26, 0xB2, 0xB3, 0x08, 0x46, 0x5A, - 0x79, 0x1B, 0x31, 0x04, 0x10, 0x49, 0x49, 0x49, 0x22, 0x27, 0x27, 0xA7, 0x5D, 0x99, 0x61, 0xB5, - 0x5A, 0xC5, 0x8C, 0x19, 0x33, 0x04, 0x20, 0xBC, 0xBC, 0xBC, 0xC4, 0xA3, 0x8F, 0x3E, 0x2A, 0x36, - 0x6F, 0xDE, 0x2C, 0xCE, 0x9F, 0x3F, 0x2F, 0x0A, 0x0B, 0x0B, 0xC5, 0xE1, 0xC3, 0x87, 0xC5, 0xDC, - 0xB9, 0x73, 0x45, 0x42, 0x42, 0x82, 0x18, 0x33, 0x66, 0x8C, 0x28, 0x28, 0x28, 0x68, 0xF1, 0x3D, - 0x96, 0x2E, 0x5D, 0x7A, 0x39, 0x33, 0x34, 0x60, 0x3B, 0xF0, 0xDF, 0x40, 0x37, 0x64, 0x38, 0xD1, - 0x75, 0x87, 0x28, 0xA4, 0xEF, 0x79, 0x2F, 0x36, 0x93, 0x3B, 0x57, 0x09, 0x26, 0x98, 0x35, 0x6B, - 0x56, 0xBB, 0x8A, 0xAE, 0x6D, 0xDB, 0xB6, 0x89, 0xD0, 0xD0, 0x50, 0xE1, 0xED, 0x69, 0x10, 0xE1, - 0xC1, 0x01, 0x02, 0x10, 0x9E, 0x5E, 0x5E, 0xA2, 0x5B, 0xB7, 0xEE, 0xA2, 0x57, 0xAF, 0x5E, 0x22, - 0x38, 0x24, 0x44, 0x28, 0x8A, 0x22, 0x00, 0xE1, 0xE6, 0xE6, 0x26, 0x3E, 0xFA, 0xE8, 0xA3, 0x16, - 0xDF, 0x63, 0xCD, 0x9A, 0x35, 0x42, 0xAF, 0xD7, 0x5F, 0xCD, 0x51, 0x55, 0x87, 0x14, 0x5D, 0x0B, - 0x80, 0x5B, 0x01, 0xF7, 0xB6, 0x12, 0xB1, 0x3D, 0x38, 0x1B, 0x0A, 0x4C, 0x07, 0xE6, 0x21, 0x0D, - 0x8A, 0x5D, 0x70, 0xD0, 0xCD, 0x75, 0x3A, 0x1D, 0xE1, 0xE1, 0xE1, 0x24, 0x25, 0x25, 0x61, 0x36, - 0x9B, 0x49, 0x4D, 0x4D, 0xC5, 0x64, 0x32, 0xD1, 0xB7, 0x6F, 0x5F, 0x7C, 0x7C, 0x7C, 0x00, 0x19, - 0xB4, 0x50, 0x50, 0x50, 0x40, 0x59, 0x59, 0x19, 0xBE, 0xBE, 0xBE, 0x28, 0x8A, 0x73, 0x7E, 0xB3, - 0xEC, 0xEC, 0x6C, 0x9E, 0x7E, 0xFA, 0x69, 0x8E, 0x1D, 0x3B, 0xC6, 0x8C, 0x87, 0xC7, 0x31, 0x77, - 0xC6, 0x14, 0x02, 0xFD, 0x7C, 0xA8, 0xAA, 0x36, 0x51, 0x5A, 0x5A, 0x42, 0x65, 0x45, 0x39, 0x81, - 0x3E, 0x06, 0xEE, 0xFC, 0x55, 0x7F, 0x86, 0x0D, 0x48, 0x20, 0xFD, 0xE8, 0x69, 0xB2, 0xB3, 0xCF, - 0x72, 0xC7, 0x1D, 0x77, 0xB4, 0xC8, 0xF2, 0xBC, 0x7E, 0xFD, 0x7A, 0x36, 0x6C, 0xD8, 0x80, 0x9F, - 0x9F, 0x1F, 0x3A, 0x9D, 0x0E, 0x8B, 0xA5, 0x5E, 0xD3, 0x55, 0x91, 0xC1, 0x78, 0x83, 0x81, 0xFB, - 0x81, 0xDE, 0x40, 0x11, 0xD2, 0xE8, 0xA8, 0xB5, 0x03, 0x6D, 0x5B, 0x04, 0x37, 0xA4, 0x68, 0x4A, - 0x45, 0xBE, 0x29, 0x8D, 0x66, 0x42, 0x97, 0x2E, 0x5D, 0xC4, 0x23, 0x8F, 0x3C, 0x22, 0x3E, 0xF9, - 0xE4, 0x13, 0x71, 0xEC, 0xD8, 0x31, 0x51, 0x55, 0x55, 0x25, 0x5E, 0x7A, 0xE9, 0x25, 0x01, 0x08, - 0x9D, 0x4E, 0x27, 0x06, 0x0E, 0x1A, 0x24, 0x66, 0xCF, 0x9E, 0x2D, 0xE6, 0xCF, 0x9F, 0x2F, 0x66, - 0xCE, 0x9C, 0x29, 0xFA, 0xF4, 0xE9, 0x23, 0x7A, 0xF7, 0xEE, 0x2D, 0xD6, 0xAC, 0x59, 0xE3, 0xD4, - 0x02, 0x9D, 0x99, 0x99, 0x29, 0xC6, 0x8D, 0x1B, 0x27, 0x95, 0x86, 0x9B, 0x13, 0x45, 0xEE, 0x86, - 0x25, 0x42, 0x64, 0xAC, 0x16, 0x5A, 0xFA, 0x2A, 0x51, 0xB6, 0xED, 0x63, 0x71, 0xFA, 0x9B, 0x05, - 0xE2, 0xF8, 0xD7, 0xF3, 0xC5, 0x85, 0x0D, 0xEF, 0x0B, 0xCB, 0xDE, 0x15, 0xA2, 0x74, 0xFB, 0x27, - 0x62, 0xFC, 0xB0, 0x41, 0x02, 0x10, 0x77, 0xDF, 0x7D, 0xB7, 0x38, 0x7A, 0xF4, 0x68, 0xA3, 0xFE, - 0x34, 0x4D, 0x13, 0xFB, 0xF7, 0xEF, 0x17, 0x6F, 0xBD, 0xF5, 0x96, 0xD8, 0xBD, 0x7B, 0xB7, 0xB0, - 0x58, 0x2C, 0xC2, 0x62, 0xB1, 0x88, 0x6D, 0xDB, 0xB6, 0x89, 0xB8, 0xB8, 0x38, 0xE1, 0xEE, 0xEE, - 0x2E, 0xDE, 0x7B, 0xEF, 0x3D, 0xB1, 0x69, 0xD3, 0x26, 0xF1, 0xDC, 0x73, 0xCF, 0x89, 0x7E, 0xFD, - 0xFA, 0x09, 0x77, 0x77, 0xF7, 0xAB, 0xCD, 0x9A, 0x12, 0xE0, 0x03, 0x64, 0x10, 0x60, 0x87, 0xC5, - 0xBD, 0x45, 0x20, 0x67, 0x44, 0x99, 0xE3, 0x60, 0x74, 0x3A, 0x9D, 0x48, 0x4A, 0x4A, 0x12, 0x6F, - 0xBE, 0xF9, 0xA6, 0x38, 0x76, 0xEC, 0x98, 0xB0, 0x58, 0x2C, 0xF5, 0x0F, 0x6C, 0x32, 0x99, 0xC4, - 0xA4, 0x49, 0x93, 0x04, 0x20, 0xBA, 0x84, 0x1A, 0x85, 0xA7, 0xC1, 0xAD, 0xD1, 0x83, 0x78, 0xB8, - 0xBB, 0x09, 0x9D, 0xAA, 0x8A, 0xA0, 0xA0, 0x20, 0xF1, 0xDA, 0x6B, 0xAF, 0x89, 0xB3, 0x67, 0xCF, - 0x5E, 0x35, 0x10, 0xA2, 0xA2, 0xA2, 0x42, 0xAC, 0x5C, 0xB9, 0x52, 0x0C, 0x18, 0x30, 0x40, 0x00, - 0xE2, 0x96, 0xDE, 0xDD, 0xC5, 0xC1, 0x95, 0x6F, 0x0B, 0xB1, 0xFF, 0x6B, 0x21, 0xD2, 0xBE, 0x92, - 0xBF, 0xF4, 0x55, 0x42, 0xEC, 0x5F, 0x25, 0xDB, 0xD2, 0x57, 0xC9, 0xB6, 0xFD, 0x5F, 0x8B, 0xC3, - 0x5F, 0xFD, 0x5D, 0x0C, 0x4E, 0xEC, 0x21, 0x00, 0xD1, 0xA3, 0x47, 0x0F, 0xF1, 0x87, 0x3F, 0xFC, - 0x41, 0xAC, 0x5A, 0xB5, 0x4A, 0xAC, 0x5C, 0xB9, 0x52, 0xCC, 0x9A, 0x35, 0x4B, 0xC4, 0xC4, 0xC4, - 0x08, 0x40, 0x84, 0x87, 0x87, 0x8B, 0xFB, 0xEE, 0xBB, 0x4F, 0x4C, 0x9C, 0x38, 0xB1, 0xDE, 0x98, - 0x38, 0x79, 0xF2, 0xE4, 0xFA, 0x60, 0x0B, 0x4D, 0xD3, 0x44, 0x7E, 0x7E, 0xBE, 0x58, 0xBE, 0x7C, - 0xB9, 0x18, 0x3F, 0x7E, 0xBC, 0xF0, 0xF5, 0xF5, 0xBD, 0x1A, 0x63, 0xCE, 0x21, 0x8D, 0x8F, 0xC1, - 0x2D, 0x21, 0x6C, 0x4B, 0x39, 0xA8, 0x20, 0x83, 0x9E, 0x5F, 0x07, 0x86, 0x39, 0x5E, 0x1F, 0x17, - 0x17, 0xC7, 0xF4, 0xE9, 0xD3, 0x49, 0x4E, 0x4E, 0x26, 0x32, 0x32, 0xF2, 0x8A, 0x0B, 0x77, 0xEC, - 0xD8, 0xC1, 0x84, 0x09, 0x13, 0xF0, 0xD4, 0x2B, 0x2C, 0xFF, 0xDB, 0x33, 0x58, 0xEA, 0xEA, 0x48, - 0x3B, 0x7A, 0x9A, 0xD2, 0x8A, 0x4B, 0x04, 0x07, 0xF8, 0x31, 0xA8, 0x57, 0x37, 0x52, 0x7F, 0x3E, - 0xCE, 0x3F, 0x96, 0xAF, 0xA7, 0xEC, 0x52, 0x35, 0x71, 0xF1, 0xF1, 0xDC, 0x39, 0x76, 0x2C, 0x03, - 0x07, 0x0E, 0x24, 0x28, 0x28, 0x88, 0xAA, 0xAA, 0x2A, 0x4E, 0x9C, 0x38, 0xC1, 0x96, 0x2D, 0x5B, - 0xD8, 0xB3, 0x67, 0x0F, 0x96, 0xDA, 0x5A, 0xEE, 0xBA, 0xAD, 0x3F, 0x73, 0x9F, 0x99, 0x42, 0xAF, - 0x6E, 0xD1, 0xA0, 0x39, 0x21, 0x21, 0x54, 0x95, 0x13, 0x67, 0xCE, 0xF3, 0xA7, 0x25, 0xAB, 0xF8, - 0x76, 0x7B, 0x1A, 0x55, 0xE6, 0x1A, 0xDB, 0x23, 0x08, 0x00, 0x02, 0x7C, 0xBD, 0x49, 0xEA, 0x19, - 0xC3, 0xD1, 0xAC, 0xF3, 0x14, 0x97, 0x55, 0xA2, 0x28, 0x0A, 0xE1, 0xE1, 0xE1, 0x24, 0x27, 0x27, - 0x33, 0x7B, 0xF6, 0x6C, 0xC2, 0xC2, 0xC2, 0xAE, 0xE8, 0xB2, 0xBA, 0xBA, 0x9A, 0xD4, 0xD4, 0x54, - 0x96, 0x2E, 0x5D, 0xCA, 0xF7, 0xDF, 0x7F, 0x4F, 0x65, 0x65, 0xA5, 0xE3, 0x69, 0x2B, 0xB0, 0x03, - 0xF8, 0x13, 0x90, 0x82, 0x13, 0x62, 0xAC, 0x25, 0x0C, 0x71, 0x03, 0x26, 0x03, 0x7F, 0x41, 0xAE, - 0x13, 0x80, 0xB4, 0xED, 0x4C, 0x99, 0x32, 0x85, 0x99, 0x33, 0x67, 0xD2, 0xA3, 0x47, 0x8F, 0xAB, - 0x5E, 0x28, 0x84, 0x60, 0xC6, 0x8C, 0x19, 0x2C, 0x58, 0xB0, 0x80, 0xA9, 0xF7, 0x8E, 0x64, 0xC9, - 0x2B, 0xBF, 0xB3, 0x79, 0xDE, 0x84, 0xA4, 0x85, 0x22, 0x87, 0x52, 0x67, 0xA9, 0x63, 0xEB, 0xDE, - 0x43, 0xFC, 0x73, 0xE5, 0xF7, 0x6C, 0x4F, 0x3F, 0x42, 0x65, 0xB5, 0x59, 0x0E, 0x52, 0x51, 0x11, - 0x42, 0x3E, 0x8B, 0x5E, 0xA7, 0x92, 0xD8, 0x2D, 0x9A, 0x69, 0x13, 0x47, 0x32, 0xE5, 0x9E, 0xFF, - 0x20, 0xD0, 0xDF, 0xD7, 0x39, 0x66, 0x38, 0x30, 0xA5, 0xDA, 0x64, 0x66, 0xD7, 0x81, 0x13, 0x6C, - 0xD9, 0x77, 0x98, 0xAC, 0xDC, 0x02, 0x54, 0x45, 0xA1, 0x67, 0x4C, 0x04, 0x63, 0x6E, 0x4D, 0x62, - 0x50, 0xAF, 0x6E, 0x7C, 0xBA, 0x6E, 0x3B, 0x4F, 0xCE, 0x59, 0x4A, 0x78, 0x44, 0x24, 0x5F, 0x7E, - 0xF9, 0x25, 0x43, 0x87, 0x0E, 0x45, 0xA7, 0xBB, 0xF6, 0x72, 0x6B, 0x36, 0x9B, 0xD9, 0xB6, 0x6D, - 0x1B, 0x0B, 0x17, 0x2E, 0x64, 0xD3, 0xA6, 0x4D, 0x8E, 0x81, 0x76, 0x20, 0xE3, 0xBA, 0xDE, 0x04, - 0x96, 0xD0, 0xCC, 0xCE, 0xDE, 0x59, 0x86, 0xF8, 0x02, 0xFF, 0x0B, 0x3C, 0x83, 0x0C, 0x09, 0x05, - 0x20, 0x31, 0x31, 0x91, 0x3F, 0xFE, 0xF1, 0x8F, 0xDC, 0x7B, 0xEF, 0xBD, 0xB8, 0xBB, 0x37, 0xAD, - 0x60, 0x9C, 0x39, 0x73, 0x86, 0x31, 0x63, 0xC7, 0x92, 0x9B, 0x73, 0x96, 0x7F, 0xCD, 0x7B, 0x9E, - 0xBB, 0x6E, 0x1F, 0x08, 0xD6, 0x26, 0x88, 0xA8, 0x53, 0xA9, 0xAA, 0x32, 0xF1, 0xF3, 0xF1, 0x33, - 0xEC, 0x3C, 0x70, 0x82, 0xE3, 0xD9, 0xB9, 0x54, 0x5C, 0xAA, 0xC6, 0xD3, 0xC3, 0x40, 0x4C, 0x78, - 0x30, 0x83, 0x13, 0xBB, 0x33, 0xA4, 0x6F, 0x1C, 0xE1, 0x21, 0x81, 0x36, 0xE1, 0x20, 0x9C, 0x67, - 0x86, 0xE3, 0x53, 0xAB, 0x2A, 0x08, 0xD0, 0xEA, 0xAC, 0xA0, 0x80, 0xAA, 0xD3, 0xD5, 0x4F, 0x96, - 0xBC, 0xA2, 0x52, 0xEE, 0x7A, 0xF2, 0xAF, 0x1C, 0x3D, 0x9B, 0xC7, 0x8A, 0xE5, 0xCB, 0x99, 0x34, - 0x69, 0x92, 0xD3, 0x5D, 0x57, 0x56, 0x56, 0xB2, 0x62, 0xC5, 0x0A, 0xE6, 0xCD, 0x9B, 0xC7, 0x89, - 0x13, 0x27, 0x1C, 0x4F, 0xD5, 0x00, 0xCB, 0x80, 0x57, 0x81, 0xE2, 0xB6, 0x30, 0x24, 0x10, 0x29, - 0xA2, 0xFE, 0x0B, 0x9B, 0xF6, 0xA4, 0xD7, 0xEB, 0x79, 0xE0, 0x81, 0x07, 0xF8, 0xF3, 0x9F, 0xFF, - 0x4C, 0x7C, 0x7C, 0x7C, 0xB3, 0x1D, 0x2C, 0x59, 0xB2, 0x84, 0xE9, 0xD3, 0xA7, 0x33, 0x6C, 0x40, - 0x02, 0xDF, 0xBE, 0xF3, 0x02, 0x01, 0xBE, 0xDE, 0xD7, 0x26, 0xA4, 0x9D, 0x60, 0x80, 0xB0, 0x6A, - 0x68, 0x9A, 0x86, 0xAA, 0x28, 0x28, 0x3A, 0x1D, 0x28, 0x8A, 0x9C, 0x11, 0xAD, 0x61, 0x84, 0xB3, - 0x50, 0x14, 0x5E, 0x5A, 0xB4, 0x9C, 0xBF, 0x2D, 0x5B, 0x43, 0x72, 0x72, 0x32, 0x9F, 0x7E, 0xFA, - 0x29, 0x6E, 0x6E, 0x6E, 0x2D, 0xEA, 0x22, 0x33, 0x33, 0x93, 0xD7, 0x5F, 0x7F, 0x9D, 0x15, 0x2B, - 0x56, 0x38, 0xCE, 0x16, 0x0D, 0x19, 0x01, 0x39, 0x13, 0x99, 0xCB, 0x72, 0x05, 0x9A, 0x33, 0x2E, - 0x86, 0x00, 0xFF, 0x40, 0xAA, 0xB5, 0x7A, 0x90, 0x22, 0xEA, 0x85, 0x17, 0x5E, 0x60, 0xC9, 0x92, - 0x25, 0x4E, 0x31, 0xC3, 0x6C, 0x36, 0xB3, 0x6E, 0xDD, 0x3A, 0x00, 0x26, 0x0C, 0xBF, 0x99, 0x00, - 0x7F, 0x9F, 0xE6, 0x89, 0x29, 0x90, 0x33, 0xC8, 0xAA, 0xA1, 0x00, 0x3A, 0x55, 0x95, 0xAA, 0xB0, - 0xA6, 0x81, 0xD5, 0xEA, 0x5A, 0x66, 0x00, 0xA8, 0x0A, 0x13, 0x86, 0x0F, 0x22, 0xC0, 0xD7, 0x8B, - 0x94, 0x94, 0x14, 0x32, 0x33, 0x33, 0x5B, 0xDC, 0x45, 0xCF, 0x9E, 0x3D, 0x59, 0xBC, 0x78, 0x31, - 0x73, 0xE6, 0xCC, 0x71, 0xF4, 0x09, 0xA9, 0xC0, 0x43, 0xC8, 0x7D, 0x4B, 0xC4, 0x55, 0x6F, 0x7D, - 0x8D, 0x3E, 0x8D, 0x34, 0xEC, 0x2D, 0x54, 0x80, 0x90, 0x90, 0x10, 0xDE, 0x79, 0xE7, 0x1D, 0x5E, - 0x7D, 0xF5, 0x55, 0xFC, 0xFD, 0xFD, 0x9D, 0x1A, 0xD8, 0xF1, 0xE3, 0xC7, 0xF9, 0x69, 0xF7, 0x6E, - 0x42, 0x02, 0xFD, 0x18, 0x3B, 0xA4, 0x9F, 0x7D, 0xFD, 0xBC, 0xBE, 0xA1, 0x69, 0xF4, 0xED, 0x11, - 0xC3, 0x2D, 0x89, 0x3D, 0xB8, 0x70, 0xE1, 0x02, 0x5B, 0xB7, 0x6E, 0x6D, 0x55, 0x37, 0x5E, 0x5E, - 0x5E, 0x3C, 0xF5, 0xD4, 0x53, 0x7C, 0xF0, 0xC1, 0x07, 0xC4, 0xC6, 0xC6, 0x3A, 0x9E, 0xBA, 0x1F, - 0x98, 0x03, 0x5C, 0x41, 0xC4, 0xA6, 0x18, 0xE2, 0x89, 0x4C, 0x1D, 0x98, 0x8C, 0x4D, 0xAC, 0x45, - 0x45, 0x45, 0xB1, 0x68, 0xD1, 0x22, 0xA6, 0x4D, 0x9B, 0xD6, 0xA2, 0xE9, 0xBB, 0x7D, 0xFB, 0x76, - 0x8A, 0x8B, 0x8A, 0xB8, 0xB5, 0x4F, 0x4F, 0xE2, 0xBB, 0x46, 0xB6, 0x6C, 0x01, 0xEE, 0x2C, 0x08, - 0xF0, 0xF6, 0xF6, 0x64, 0xDC, 0xD0, 0x01, 0x00, 0x6C, 0xD8, 0xB0, 0x01, 0xB3, 0xD9, 0xDC, 0xAA, - 0xAE, 0x54, 0x55, 0x65, 0xE2, 0xC4, 0x89, 0x2C, 0x5D, 0xBA, 0xF4, 0x72, 0xA5, 0xE7, 0x11, 0xE4, - 0xBA, 0xDC, 0xC8, 0xC1, 0x75, 0x35, 0x86, 0x28, 0xC0, 0xFF, 0x20, 0xC5, 0x94, 0x0A, 0x92, 0x19, - 0xEF, 0xBE, 0xFB, 0x2E, 0x0F, 0x3D, 0xF4, 0x90, 0xD3, 0xBB, 0x68, 0x90, 0xE2, 0x6A, 0xD3, 0xA6, - 0x4D, 0x00, 0x8C, 0x19, 0x92, 0x84, 0x87, 0xE7, 0x8D, 0x11, 0x42, 0x0A, 0x80, 0x80, 0x11, 0x83, - 0x12, 0x09, 0xF2, 0xF7, 0x21, 0x2D, 0x2D, 0x9D, 0xAC, 0xAC, 0xAC, 0x36, 0x75, 0x37, 0x6A, 0xD4, - 0x28, 0x16, 0x2D, 0x5A, 0x44, 0x54, 0x54, 0x94, 0xBD, 0x49, 0x87, 0xCC, 0x0A, 0xB8, 0xDF, 0xF1, - 0xFF, 0xAE, 0xC6, 0x90, 0x31, 0xC8, 0x0D, 0x8D, 0x01, 0xC0, 0x68, 0x34, 0x32, 0x6F, 0xDE, 0xBC, - 0x46, 0xF9, 0x19, 0xCE, 0x22, 0x3B, 0x3B, 0x9B, 0xF4, 0xFD, 0xFB, 0x09, 0xF2, 0xF7, 0x61, 0xF8, - 0x80, 0x5E, 0x37, 0x86, 0xB8, 0xB2, 0x43, 0x68, 0xC4, 0xC5, 0x44, 0x30, 0x20, 0x3E, 0x96, 0xFC, - 0xFC, 0x3C, 0x76, 0xED, 0xDA, 0xD5, 0xE6, 0x2E, 0xC7, 0x8E, 0x1D, 0xCB, 0x1B, 0x6F, 0xBC, 0xE1, - 0xE8, 0x06, 0xF6, 0x43, 0x66, 0x89, 0xDD, 0x64, 0x6F, 0xB8, 0x9C, 0x21, 0x91, 0x48, 0x51, 0x15, - 0x0A, 0xD2, 0xA9, 0xFF, 0xF2, 0xCB, 0x2F, 0xF3, 0xD0, 0x43, 0x0F, 0xB5, 0x6A, 0x00, 0xE9, 0xE9, - 0xE9, 0xE4, 0x5D, 0xC8, 0xA3, 0x4F, 0xF7, 0x18, 0x7A, 0x44, 0x87, 0x3B, 0xBF, 0x18, 0xAB, 0x2A, - 0xA8, 0xAA, 0x4C, 0xCC, 0x01, 0xD0, 0xA9, 0x52, 0xBB, 0x6A, 0x2D, 0x54, 0x05, 0x54, 0x15, 0xAB, - 0xA6, 0x61, 0xD5, 0xB4, 0xFA, 0xFE, 0xAF, 0x09, 0x01, 0x5E, 0x5E, 0x9E, 0x0C, 0x1F, 0xD8, 0x1B, - 0x80, 0x9D, 0x3B, 0x77, 0xA2, 0xB5, 0x83, 0xB8, 0x4D, 0x4E, 0x4E, 0x66, 0xFA, 0xF4, 0xE9, 0x8E, - 0x4D, 0xFD, 0x90, 0x41, 0xDB, 0x2A, 0x34, 0x96, 0x5F, 0x0A, 0x32, 0x21, 0xE5, 0x36, 0x7B, 0xC3, - 0x23, 0x8F, 0x3C, 0xC2, 0xF4, 0xE9, 0xD3, 0x5B, 0x1D, 0xA8, 0x20, 0xDD, 0xB7, 0x82, 0xDB, 0xFA, - 0xC5, 0xE3, 0xE3, 0xED, 0x09, 0x5A, 0x33, 0x0C, 0x51, 0x14, 0xAC, 0x9A, 0x46, 0xFA, 0xA1, 0x93, - 0xFC, 0x7B, 0xE7, 0x7E, 0x4E, 0x9D, 0xCB, 0xC7, 0xDB, 0xD3, 0xC0, 0x6D, 0x49, 0xF1, 0x8C, 0xBB, - 0x7D, 0x20, 0xA1, 0x41, 0x01, 0x2D, 0x5F, 0x83, 0x14, 0x85, 0x53, 0x39, 0x79, 0xFC, 0x6B, 0xCB, - 0x5E, 0xD2, 0x8F, 0x67, 0x61, 0xB5, 0x6A, 0x24, 0x76, 0x8F, 0xE6, 0xFE, 0x11, 0xB7, 0xD0, 0x37, - 0xEE, 0xA6, 0x6B, 0xAB, 0x99, 0x8A, 0xC2, 0xAD, 0x7D, 0x7B, 0xE2, 0xAE, 0xD7, 0x91, 0x91, 0x91, - 0x41, 0x49, 0x49, 0x49, 0x9B, 0xA3, 0x68, 0xDC, 0xDC, 0xDC, 0x78, 0xE6, 0x99, 0x67, 0x48, 0x49, - 0x49, 0x61, 0xEF, 0xDE, 0xBD, 0xF6, 0xE6, 0xC9, 0xC0, 0x72, 0x60, 0xBF, 0x23, 0x43, 0xFA, 0x03, - 0x53, 0xED, 0x07, 0x7D, 0xFA, 0xF4, 0xE1, 0xC5, 0x17, 0x5F, 0xC4, 0xCB, 0xCB, 0xCB, 0xE9, 0x9B, - 0x39, 0xA2, 0xAC, 0xAC, 0x8C, 0xFD, 0xFB, 0xF7, 0xA3, 0x53, 0x15, 0x86, 0xF4, 0xED, 0x09, 0x8A, - 0x4A, 0x73, 0x91, 0xFB, 0xB5, 0x96, 0x3A, 0x16, 0xAE, 0xFC, 0x9E, 0x39, 0x1F, 0x7F, 0x43, 0xE1, - 0xC5, 0xF2, 0xFA, 0xF6, 0x65, 0xDF, 0x6C, 0x65, 0xD8, 0x80, 0x04, 0xDE, 0x79, 0x76, 0x2A, 0xFD, - 0x13, 0x62, 0x9D, 0x67, 0x8A, 0xA2, 0xF0, 0x7D, 0xEA, 0x7E, 0x9E, 0x7D, 0xE7, 0x53, 0x8E, 0x9D, - 0x69, 0x50, 0xFB, 0xBF, 0xDE, 0xBC, 0x9B, 0x0F, 0xD7, 0x6E, 0xE6, 0x2F, 0xFF, 0x93, 0xCC, 0xA3, - 0xE3, 0xFF, 0x03, 0xB5, 0xA9, 0xD9, 0xA7, 0x69, 0xF4, 0x8E, 0xED, 0x42, 0xD7, 0x88, 0x10, 0x4E, - 0x67, 0x65, 0x71, 0xF2, 0xE4, 0xC9, 0x76, 0x09, 0x6B, 0x8A, 0x8A, 0x8A, 0x62, 0xE6, 0xCC, 0x99, - 0x4C, 0x9D, 0x3A, 0xD5, 0xAE, 0x2C, 0x44, 0x00, 0x8F, 0x02, 0x19, 0xF6, 0x17, 0x44, 0x05, 0xA6, - 0x61, 0x33, 0x89, 0xB8, 0xBB, 0xBB, 0x33, 0x6B, 0xD6, 0xAC, 0x26, 0x4D, 0x21, 0xCE, 0xE0, 0xEC, - 0xD9, 0xB3, 0x64, 0x9E, 0x3A, 0x45, 0x44, 0x70, 0x20, 0xBD, 0xBB, 0x75, 0x01, 0xD1, 0x0C, 0x11, - 0x55, 0x95, 0x55, 0x9B, 0x7E, 0xE2, 0xD5, 0xF7, 0xBE, 0x6A, 0xC4, 0x0C, 0x2F, 0x2F, 0x2F, 0xFC, - 0xFC, 0xFD, 0xD9, 0x96, 0x7E, 0x94, 0x67, 0xDE, 0xFE, 0x88, 0x82, 0xE2, 0x8B, 0xCE, 0x89, 0x2F, - 0x55, 0xE5, 0xF0, 0xE9, 0x1C, 0x66, 0xBC, 0xFD, 0x71, 0x23, 0x66, 0x18, 0x0C, 0x06, 0xC2, 0xC2, - 0xC2, 0x28, 0x2C, 0xAB, 0xE2, 0xF9, 0x7F, 0x7C, 0xCE, 0x96, 0x7D, 0x87, 0x9B, 0x16, 0x5F, 0x42, - 0x10, 0x1E, 0xE4, 0x4F, 0xBF, 0xB8, 0x9B, 0xA8, 0xBA, 0x74, 0x89, 0xB4, 0xB4, 0xB4, 0x36, 0x33, - 0xC3, 0x8E, 0x71, 0xE3, 0xC6, 0x31, 0x7C, 0xF8, 0x70, 0xC7, 0xA6, 0x7B, 0x80, 0x04, 0xFB, 0x48, - 0x12, 0x80, 0xFB, 0xEC, 0x67, 0x46, 0x8C, 0x18, 0xC1, 0x03, 0x0F, 0x3C, 0xD0, 0xA6, 0x1B, 0x1E, - 0x3D, 0x7A, 0x94, 0xD2, 0x92, 0x12, 0xE2, 0xBB, 0x46, 0x12, 0x19, 0x62, 0xBC, 0xB6, 0xB8, 0x52, - 0x14, 0xCA, 0x2B, 0x2E, 0xF1, 0xFE, 0xEA, 0x8D, 0x54, 0x99, 0x1A, 0xD4, 0xCB, 0xBE, 0x7D, 0xFB, - 0xB2, 0x72, 0xE5, 0x4A, 0x36, 0x6F, 0xDE, 0xCC, 0x13, 0x4F, 0x3C, 0xC1, 0x4F, 0x87, 0x32, 0xF9, - 0x2E, 0x25, 0x4D, 0xAE, 0x09, 0xCD, 0x41, 0x08, 0xBE, 0x58, 0xBF, 0x83, 0xCC, 0x9C, 0x86, 0x78, - 0x5C, 0xA3, 0xD1, 0xC8, 0xFC, 0xF9, 0xF3, 0x49, 0x49, 0x49, 0xE1, 0xBD, 0xF7, 0xDE, 0x45, 0xE8, - 0xDD, 0xF9, 0x60, 0xCD, 0x26, 0x2C, 0xB5, 0x4D, 0x47, 0xF2, 0xE8, 0xDD, 0xDD, 0xE4, 0x0C, 0x07, - 0xF6, 0xEC, 0xD9, 0xE3, 0x98, 0xB0, 0xD3, 0x26, 0xF8, 0xF9, 0xF9, 0x31, 0x79, 0xF2, 0x64, 0xC7, - 0x68, 0xFA, 0x58, 0x60, 0xBC, 0x9D, 0x21, 0xE3, 0xB1, 0xCD, 0x0E, 0x83, 0xC1, 0xC0, 0xB4, 0x69, - 0xD3, 0x9C, 0xDE, 0xF8, 0x35, 0x85, 0x43, 0x87, 0x0E, 0x01, 0x90, 0xD8, 0x3D, 0x06, 0x2F, 0x8F, - 0x66, 0xD4, 0x5D, 0x45, 0xE1, 0x6C, 0x5E, 0x11, 0xC7, 0xCE, 0x9C, 0x6F, 0xD4, 0xFC, 0xE4, 0x93, - 0x4F, 0x32, 0x7E, 0xFC, 0x78, 0x06, 0x0C, 0x18, 0xC0, 0x8B, 0x2F, 0xBE, 0x48, 0x74, 0xD7, 0x9B, - 0x48, 0xFD, 0xF9, 0x78, 0xD3, 0x76, 0x30, 0x07, 0x98, 0xCC, 0x35, 0xEC, 0x3D, 0x72, 0xAA, 0x51, - 0xDB, 0x98, 0x31, 0x63, 0x78, 0xFC, 0xF1, 0xC7, 0x89, 0x8B, 0x8B, 0xE3, 0xB1, 0xC7, 0x1E, 0x63, - 0xC2, 0x84, 0x09, 0xA4, 0x1F, 0x3B, 0x4D, 0x71, 0x59, 0xE5, 0x35, 0x66, 0x9D, 0x42, 0xBF, 0xB8, - 0x9B, 0x70, 0xD7, 0xEB, 0x38, 0x7E, 0xFC, 0x38, 0xE5, 0xE5, 0xE5, 0xCD, 0xDE, 0xDB, 0x59, 0x8C, - 0x1A, 0x35, 0x8A, 0xEE, 0xDD, 0xEB, 0x73, 0x7E, 0x74, 0xC0, 0x44, 0x15, 0xB9, 0x5B, 0xBC, 0xDB, - 0xDE, 0xDA, 0xB7, 0x6F, 0x5F, 0x46, 0x8F, 0x1E, 0xDD, 0xA6, 0x1B, 0x59, 0x2C, 0x16, 0x8E, 0x1E, - 0x3D, 0x2A, 0xFB, 0xEB, 0x11, 0xD3, 0xBC, 0x46, 0xA3, 0x80, 0xB9, 0xD6, 0x42, 0xAD, 0xA5, 0xF1, - 0xDB, 0x27, 0x1C, 0xB4, 0x32, 0x21, 0x04, 0x08, 0x41, 0xB5, 0xB9, 0x06, 0xAD, 0x39, 0x6D, 0x4D, - 0x01, 0xAB, 0x55, 0xC3, 0x5C, 0xD3, 0xF8, 0xCD, 0xB7, 0x58, 0x2C, 0xF5, 0x9A, 0x92, 0xA6, 0x69, - 0xD4, 0xD6, 0xD6, 0x52, 0x6B, 0xA9, 0xA3, 0xCE, 0x6A, 0x6D, 0xDA, 0xAA, 0x27, 0x34, 0x62, 0x23, - 0x43, 0x09, 0x35, 0xFA, 0x93, 0x9D, 0x9D, 0x4D, 0x4A, 0x4A, 0x0A, 0x15, 0x15, 0x15, 0x8D, 0xC6, - 0xD6, 0x52, 0x54, 0x57, 0x57, 0x93, 0x9E, 0x9E, 0xCE, 0xFB, 0xEF, 0xBF, 0x7F, 0x79, 0xE0, 0x5D, - 0x92, 0x1E, 0x48, 0x44, 0x66, 0xCA, 0x02, 0x70, 0xF7, 0xDD, 0x77, 0x13, 0x1C, 0xDC, 0x22, 0x9F, - 0xCA, 0x15, 0xA8, 0xA8, 0xA8, 0xE0, 0xCC, 0x99, 0x33, 0x78, 0xB8, 0xEB, 0xE9, 0x19, 0x13, 0x4E, - 0xB3, 0x1B, 0x10, 0x21, 0x08, 0x0F, 0x0A, 0x20, 0xD4, 0xE8, 0x47, 0x45, 0x55, 0x83, 0x75, 0x7A, - 0xD1, 0xA2, 0x45, 0x44, 0x44, 0x44, 0x10, 0x15, 0x15, 0xC5, 0x87, 0x1F, 0x7E, 0xC8, 0xE9, 0xAC, - 0xD3, 0x3C, 0x3C, 0xA2, 0x9F, 0xB4, 0xCC, 0x5E, 0x6B, 0x61, 0x17, 0xE0, 0xE5, 0x61, 0xA0, 0x47, - 0x74, 0x38, 0xBB, 0x0E, 0x36, 0x58, 0x5C, 0x37, 0x6E, 0xDC, 0xC8, 0xDC, 0xB9, 0x73, 0x19, 0x33, - 0x66, 0x0C, 0x3B, 0x76, 0xEC, 0xE0, 0xBB, 0xEF, 0xD6, 0x31, 0xA0, 0x47, 0x24, 0x81, 0xBE, 0xDE, - 0x4D, 0x0F, 0x51, 0x40, 0xA8, 0xD1, 0x9F, 0xAE, 0x11, 0x21, 0xA4, 0x1E, 0x38, 0xC1, 0x6F, 0x7F, - 0xFB, 0x5B, 0x12, 0x12, 0x12, 0x18, 0x32, 0x64, 0x08, 0x83, 0x07, 0x0F, 0xA6, 0x7B, 0xF7, 0xEE, - 0x44, 0x45, 0x45, 0x11, 0x18, 0x18, 0x88, 0xC1, 0x60, 0x40, 0xA7, 0xD3, 0xD5, 0x6F, 0x9E, 0x35, - 0x4D, 0xC3, 0x62, 0xB1, 0x50, 0x5D, 0x5D, 0x4D, 0x71, 0x71, 0x31, 0xA7, 0x4F, 0x9F, 0x26, 0x2D, - 0x2D, 0x8D, 0x94, 0x94, 0x14, 0x32, 0x32, 0x32, 0x28, 0x2E, 0xBE, 0xC2, 0xE8, 0x9B, 0xAB, 0x47, - 0xAA, 0xB9, 0x46, 0x00, 0x6F, 0x6F, 0x6F, 0x46, 0x8C, 0x18, 0xD1, 0x26, 0x66, 0x00, 0x14, 0x14, - 0x14, 0x90, 0x97, 0x9F, 0x4F, 0x90, 0xBF, 0x2F, 0x51, 0xA1, 0xC6, 0xE6, 0x37, 0x84, 0x9A, 0xA0, - 0x4B, 0x58, 0x10, 0xE3, 0x6E, 0x1F, 0xC8, 0x3F, 0x97, 0xAF, 0xAF, 0x6F, 0x3E, 0x7C, 0xF8, 0x30, - 0x0F, 0x3F, 0xFC, 0x30, 0xEE, 0xEE, 0xEE, 0x94, 0x57, 0x54, 0x10, 0x66, 0xF4, 0x63, 0xC2, 0xF0, - 0x41, 0x4E, 0x8D, 0x41, 0x75, 0xD3, 0x31, 0x69, 0xF4, 0xAD, 0xAC, 0xD9, 0xBA, 0x87, 0x4B, 0x36, - 0xBF, 0x4A, 0x65, 0x65, 0x25, 0xAF, 0xBD, 0xF6, 0x1A, 0x73, 0xE6, 0xCC, 0xA1, 0xBA, 0xBA, 0x1A, - 0x10, 0x3C, 0x74, 0xC7, 0x6D, 0xF8, 0xF8, 0x78, 0x35, 0xCD, 0x60, 0x21, 0xF0, 0xF1, 0xF2, 0x20, - 0xBE, 0x6B, 0x24, 0xA9, 0x07, 0x4E, 0x50, 0x5E, 0x5E, 0xCE, 0x9E, 0x3D, 0x7B, 0xD8, 0xB3, 0x67, - 0x0F, 0x3A, 0x9D, 0x0E, 0x1F, 0x1F, 0x1F, 0x42, 0x42, 0x42, 0x88, 0x88, 0x88, 0x20, 0x28, 0x28, - 0x08, 0x5F, 0x5F, 0x5F, 0x0C, 0x06, 0x03, 0x9A, 0xA6, 0x51, 0x5D, 0x5D, 0x4D, 0x59, 0x59, 0x19, - 0x85, 0x85, 0x85, 0x14, 0x14, 0x14, 0x50, 0x52, 0x52, 0xD2, 0x94, 0x09, 0x46, 0x43, 0x66, 0xF6, - 0xBE, 0xAA, 0x47, 0x3A, 0xE8, 0x55, 0x80, 0x2E, 0x5D, 0xBA, 0x90, 0x90, 0x90, 0xD0, 0x6A, 0x46, - 0x98, 0xCD, 0x66, 0x32, 0x32, 0x32, 0x58, 0xB8, 0x70, 0x21, 0x45, 0x85, 0x45, 0x0C, 0x4C, 0x88, - 0x25, 0xC8, 0xDF, 0xD7, 0xA9, 0x0D, 0xA1, 0xAA, 0x53, 0x99, 0xF9, 0xC8, 0x3D, 0x1C, 0x3C, 0x79, - 0x96, 0x6D, 0xE9, 0x47, 0xEA, 0xDB, 0x4D, 0x26, 0x13, 0x26, 0x93, 0x09, 0x1F, 0x4F, 0x0F, 0x66, - 0x3F, 0x76, 0x1F, 0x37, 0xF7, 0xEE, 0xEE, 0x9C, 0xDA, 0x6B, 0xD5, 0xB8, 0xEB, 0xB6, 0xFE, 0x3C, - 0x9D, 0x7C, 0x37, 0xF3, 0x3E, 0x5F, 0x47, 0x8D, 0x6D, 0xE1, 0xB6, 0x5A, 0xAD, 0x54, 0x56, 0x56, - 0xA2, 0x53, 0x55, 0x7E, 0x33, 0x6E, 0x38, 0x53, 0xC6, 0x0D, 0x6B, 0x76, 0x7C, 0x8A, 0x4E, 0x27, - 0x35, 0xC5, 0xCB, 0x6F, 0x61, 0xB5, 0x52, 0x5E, 0x5E, 0x4E, 0x79, 0x79, 0x39, 0xA7, 0x4E, 0x9D, - 0xA2, 0x15, 0xA8, 0x46, 0x46, 0x79, 0xAE, 0x04, 0xD6, 0x00, 0xE7, 0xF5, 0xC8, 0x48, 0x09, 0x40, - 0x9A, 0x8C, 0x5B, 0x2A, 0xAE, 0xEA, 0xEA, 0xEA, 0x38, 0x7F, 0xFE, 0x3C, 0x5B, 0xB7, 0x6E, 0x65, - 0xCD, 0x9A, 0x35, 0xEC, 0xDA, 0xB5, 0xAB, 0x5E, 0x2E, 0x46, 0x04, 0x07, 0xE0, 0xED, 0x61, 0x70, - 0x6E, 0x87, 0xAE, 0x09, 0x6E, 0x8A, 0x0A, 0xE5, 0xE3, 0x3F, 0x3D, 0xC9, 0x3F, 0x97, 0xAF, 0x67, - 0xDD, 0xCE, 0x74, 0x8A, 0x4A, 0x2B, 0x70, 0x73, 0xD3, 0xD3, 0x3B, 0xB6, 0x0B, 0xBF, 0x7B, 0x70, - 0x0C, 0xF7, 0x8F, 0xBA, 0x15, 0x55, 0x55, 0x9C, 0x36, 0xC1, 0xB8, 0xBB, 0xB9, 0xF1, 0xF2, 0xE3, - 0x0F, 0xD2, 0xA3, 0x4B, 0x38, 0x1F, 0x7D, 0xB7, 0x8D, 0x53, 0xE7, 0xF2, 0xD1, 0x34, 0x8D, 0xE8, - 0xB0, 0x20, 0x92, 0xEF, 0x1C, 0xCA, 0xB4, 0x89, 0xA3, 0xF1, 0x6F, 0xCE, 0x37, 0x63, 0x43, 0xD7, - 0x88, 0x10, 0x74, 0x3A, 0x15, 0xAB, 0x54, 0x28, 0x34, 0xA4, 0x93, 0xC9, 0x1B, 0x59, 0xC0, 0xC6, - 0x59, 0x33, 0x42, 0x1D, 0x32, 0x0E, 0xE1, 0x14, 0x32, 0x38, 0xE4, 0x47, 0x64, 0xD8, 0x54, 0x99, - 0xFD, 0x1F, 0xF4, 0x38, 0xD8, 0xE5, 0x2D, 0x16, 0x0B, 0x07, 0x0F, 0x1E, 0x24, 0x2A, 0x2A, 0x0A, - 0x3F, 0x3F, 0x3F, 0x0C, 0x06, 0x03, 0xAA, 0xCD, 0x17, 0xA1, 0x69, 0x1A, 0x75, 0x75, 0x75, 0x98, - 0x4C, 0x26, 0x4A, 0x4B, 0x4B, 0xC9, 0xCE, 0xCE, 0xE6, 0xC0, 0x81, 0x03, 0xEC, 0xDE, 0xBD, 0x9B, - 0x7D, 0xFB, 0xF6, 0x71, 0xEE, 0xDC, 0xB9, 0x2B, 0xEA, 0x94, 0x44, 0x86, 0x04, 0xE2, 0xEE, 0xD6, - 0x82, 0x68, 0x7D, 0x4D, 0xD0, 0x35, 0x32, 0x84, 0xB7, 0x9E, 0x99, 0xC2, 0xAC, 0xC9, 0xE3, 0x29, - 0xBC, 0x58, 0x8E, 0xC1, 0xDD, 0x8D, 0xE8, 0xB0, 0x20, 0x7C, 0x7D, 0xBC, 0x6D, 0x8E, 0x29, 0xE7, - 0xBB, 0x43, 0x08, 0x3C, 0x0D, 0x6E, 0x4C, 0xBD, 0x6F, 0x34, 0x93, 0x46, 0x0F, 0x21, 0xBF, 0xF8, - 0x22, 0x56, 0x4D, 0x10, 0x16, 0xE4, 0x4F, 0xA0, 0xBF, 0xAF, 0xA4, 0xA2, 0x53, 0x8B, 0xB3, 0x5C, - 0xE3, 0x3C, 0xDD, 0xDD, 0xB9, 0x24, 0xD5, 0xF2, 0x3A, 0xE0, 0x0F, 0xC8, 0x52, 0x4E, 0x7D, 0x80, - 0x18, 0x64, 0x5C, 0x5A, 0x28, 0x32, 0x11, 0x54, 0x8F, 0xD4, 0x9A, 0xAA, 0x6D, 0xC4, 0x2E, 0x45, - 0xC6, 0x6F, 0x1D, 0x47, 0x56, 0x27, 0x3A, 0x8B, 0x2C, 0x6C, 0x73, 0xC5, 0xCD, 0xF5, 0x38, 0x64, - 0x92, 0x6E, 0xDC, 0xB8, 0x91, 0x3D, 0x7B, 0xF6, 0x10, 0x1A, 0x1A, 0x4A, 0x68, 0x68, 0x28, 0x46, - 0xA3, 0x11, 0x2F, 0x2F, 0x2F, 0x54, 0x55, 0xA5, 0xB6, 0xB6, 0x96, 0xCA, 0xCA, 0x4A, 0x8A, 0x8B, - 0x8B, 0x29, 0x2C, 0x2C, 0xA4, 0xA4, 0xA4, 0x04, 0x93, 0xC9, 0x74, 0x4D, 0x6D, 0x23, 0x24, 0xD0, - 0x5F, 0x6A, 0x58, 0x2D, 0x31, 0x77, 0x68, 0x02, 0x9D, 0xA2, 0x10, 0x15, 0x16, 0x44, 0x54, 0x78, - 0x70, 0x3D, 0x61, 0x5B, 0x6D, 0xB6, 0x17, 0x80, 0xD0, 0xF0, 0xF3, 0xF1, 0xC2, 0xCF, 0xD7, 0xBB, - 0xA1, 0xBF, 0x96, 0x68, 0x49, 0x02, 0x42, 0x02, 0xFD, 0xF0, 0xF5, 0xF6, 0xB0, 0x33, 0xC4, 0x1D, - 0x39, 0x4B, 0x76, 0xD8, 0x7E, 0x20, 0x67, 0x89, 0x3B, 0x52, 0xFC, 0xEB, 0x6C, 0xC7, 0x16, 0x1B, - 0xF3, 0x9C, 0xAE, 0x6C, 0xA7, 0x07, 0x7E, 0x02, 0x46, 0x83, 0xD4, 0x0A, 0xCA, 0xCA, 0xCA, 0x28, - 0x2B, 0x2B, 0xE3, 0xE4, 0xC9, 0x93, 0x2D, 0x7D, 0xF4, 0x3A, 0xE0, 0x02, 0xD2, 0xCB, 0xE8, 0x09, - 0x10, 0xE4, 0xEF, 0xD3, 0x7A, 0xA3, 0x60, 0x4B, 0x89, 0xE6, 0xCA, 0xFE, 0x84, 0x20, 0xD0, 0xD7, - 0x9B, 0x40, 0x3F, 0x1F, 0xF2, 0x8A, 0xCB, 0xEC, 0xAD, 0x51, 0x97, 0xFF, 0x17, 0xD2, 0x6F, 0xDE, - 0x26, 0xE8, 0x91, 0x36, 0xF9, 0x67, 0x80, 0xE1, 0xC8, 0xCD, 0xA1, 0x0F, 0xCE, 0xC9, 0xC4, 0x3A, - 0xE4, 0xB4, 0xCB, 0x46, 0xCA, 0xC1, 0x6D, 0xC0, 0x79, 0x64, 0x15, 0xB7, 0xAE, 0x00, 0x7E, 0xDE, - 0xED, 0x9A, 0xED, 0xD5, 0x89, 0x10, 0x78, 0x7B, 0x7A, 0x60, 0xF4, 0xF3, 0x71, 0x6C, 0x0C, 0x72, - 0xC5, 0x9D, 0xF4, 0x48, 0xB9, 0xF6, 0x7B, 0x64, 0x40, 0x57, 0x37, 0xE4, 0xBE, 0xA4, 0x2B, 0xD2, - 0x14, 0x1F, 0x8C, 0x9C, 0x86, 0x3A, 0xA4, 0x65, 0xB0, 0x0C, 0x29, 0x0F, 0xCF, 0x03, 0x27, 0x69, - 0x90, 0x87, 0x17, 0x91, 0x6F, 0x48, 0x4F, 0x1C, 0xEA, 0x61, 0x79, 0x7A, 0x18, 0x3A, 0xAF, 0x88, - 0x60, 0x7B, 0x42, 0x80, 0x9B, 0x5E, 0x87, 0xBF, 0x4F, 0x23, 0x43, 0x6B, 0xDB, 0x4C, 0x19, 0x4D, - 0xC0, 0xBE, 0xE2, 0xD6, 0x21, 0x6B, 0x1E, 0xE6, 0x03, 0x76, 0x4F, 0x8C, 0x62, 0x3B, 0xEF, 0xB8, - 0xCD, 0xAE, 0xE3, 0xDA, 0x26, 0x5B, 0x9D, 0xFD, 0xFF, 0x55, 0x45, 0xC1, 0x5D, 0xAF, 0xBB, 0xB1, - 0x9C, 0x52, 0xD7, 0x22, 0x94, 0x5E, 0x87, 0x6F, 0xE3, 0x19, 0xEF, 0x47, 0xC3, 0x8B, 0xDA, 0x7E, - 0xF7, 0xB9, 0xC6, 0x39, 0xC1, 0x2F, 0xB4, 0xD6, 0x60, 0x6B, 0xA0, 0x2A, 0x0A, 0x9E, 0x86, 0x46, - 0xB1, 0x67, 0x9E, 0xB8, 0x80, 0x21, 0xED, 0x9D, 0x63, 0x58, 0x3F, 0x1F, 0x84, 0x10, 0xD2, 0x3B, - 0xF7, 0x4B, 0x10, 0x59, 0x00, 0x8A, 0x82, 0xA1, 0xB1, 0x0A, 0x6F, 0xD7, 0xA4, 0xDA, 0x15, 0xED, - 0xCD, 0x90, 0x1A, 0xA0, 0x16, 0x40, 0x13, 0x82, 0x6A, 0x73, 0xED, 0x2F, 0x46, 0x64, 0x01, 0x97, - 0x07, 0x78, 0xDC, 0x30, 0x0C, 0x31, 0xD9, 0x0F, 0x2A, 0xAA, 0x4C, 0x6D, 0xE8, 0xEA, 0x3A, 0x83, - 0x10, 0xD2, 0x2A, 0xDC, 0x00, 0x0D, 0x17, 0xBC, 0x6E, 0xED, 0xCD, 0x10, 0x13, 0xB2, 0xCE, 0x09, - 0x00, 0x45, 0xA5, 0xE5, 0xAE, 0x8F, 0x32, 0xEC, 0x20, 0x08, 0x21, 0x30, 0x35, 0x36, 0xE7, 0x9B, - 0x70, 0x41, 0x35, 0x39, 0x57, 0x30, 0xA4, 0xBE, 0x70, 0xC0, 0x85, 0xE2, 0x8B, 0x88, 0x1B, 0x21, - 0x30, 0xCE, 0x09, 0x58, 0x35, 0x8D, 0x4B, 0xD5, 0x8D, 0x66, 0xFC, 0x0D, 0xC1, 0x90, 0x1A, 0x1C, - 0x82, 0x88, 0xCF, 0xE6, 0x15, 0x61, 0x32, 0xB7, 0x79, 0xF3, 0xDA, 0xF9, 0x50, 0xA0, 0xA6, 0xB6, - 0x8E, 0x92, 0xF2, 0x4B, 0x8E, 0xAD, 0xC5, 0xDC, 0x00, 0x22, 0x0B, 0xE4, 0x66, 0x11, 0x80, 0x9C, - 0xFC, 0x62, 0xF9, 0x10, 0x6D, 0x89, 0xA9, 0xBA, 0x2E, 0xA0, 0x70, 0xC9, 0x64, 0x96, 0xAE, 0xDE, - 0x06, 0xB4, 0x7B, 0xF1, 0x32, 0x70, 0x5D, 0x69, 0x8D, 0x5A, 0x80, 0xFC, 0x92, 0x32, 0x4E, 0x9D, - 0xCB, 0x77, 0x2E, 0x28, 0xE1, 0x7A, 0x86, 0xA2, 0x50, 0x50, 0x5A, 0x4E, 0x51, 0x43, 0x34, 0x8C, - 0x40, 0x5A, 0x2B, 0xDA, 0x1D, 0xAE, 0x60, 0xC8, 0x49, 0x64, 0x26, 0x2A, 0x55, 0xA6, 0x2B, 0x03, - 0x0D, 0x6E, 0x48, 0x28, 0x0A, 0xA7, 0xCE, 0xE5, 0x53, 0x7E, 0xA9, 0xDE, 0xBD, 0x5C, 0x89, 0xF4, - 0x69, 0xB4, 0x3B, 0x5C, 0xC1, 0x90, 0x73, 0xC8, 0xCF, 0x4E, 0x00, 0xB0, 0x65, 0xDF, 0x61, 0xAA, - 0xAB, 0xCD, 0x9D, 0xBF, 0x41, 0x54, 0x14, 0x19, 0x92, 0xDA, 0x9A, 0x28, 0x4C, 0x21, 0xD8, 0x77, - 0xE4, 0x14, 0x96, 0xBA, 0xFA, 0x35, 0x3C, 0x1F, 0x69, 0xC3, 0x6B, 0x77, 0xB8, 0x82, 0x21, 0x26, - 0xE4, 0x57, 0x6E, 0x00, 0x48, 0x3F, 0x96, 0xC5, 0xE1, 0x53, 0x39, 0xAD, 0x23, 0x84, 0x1D, 0x8A, - 0xD2, 0x40, 0xD0, 0x96, 0xAE, 0x47, 0x0A, 0xA0, 0xD3, 0x51, 0x71, 0xA9, 0x9A, 0x9D, 0xE9, 0x47, - 0x39, 0x9E, 0xD5, 0xC2, 0x4F, 0x82, 0x28, 0x0A, 0xE5, 0x95, 0x97, 0xD8, 0x91, 0x71, 0xDC, 0xB1, - 0xF5, 0x20, 0x36, 0x29, 0xD0, 0xDE, 0x70, 0xD5, 0x67, 0x18, 0x36, 0xDB, 0x06, 0x1C, 0x52, 0x52, - 0x5E, 0xC9, 0xAA, 0x4D, 0xBB, 0x19, 0xDC, 0xA7, 0xF5, 0x51, 0x90, 0xCB, 0x37, 0xEC, 0x64, 0xF7, - 0xA1, 0x4C, 0x46, 0xDF, 0xD2, 0x87, 0x61, 0x03, 0x7B, 0x39, 0x9F, 0xE8, 0xA9, 0x28, 0x54, 0x9B, - 0x6B, 0xF8, 0xF1, 0xA7, 0x03, 0x2C, 0x59, 0xB3, 0x91, 0xED, 0xFB, 0x8F, 0xD2, 0x3B, 0x36, 0x9A, - 0xB5, 0xF3, 0x9E, 0x27, 0x2A, 0x2C, 0xC8, 0xB9, 0x3D, 0x92, 0xAA, 0xB0, 0xF7, 0xC8, 0x69, 0x0E, - 0x64, 0x66, 0xDB, 0x5B, 0x84, 0xED, 0xF9, 0x5C, 0x62, 0xE7, 0x73, 0x55, 0x8D, 0x8E, 0x12, 0x64, - 0xAC, 0x70, 0x1F, 0x80, 0x0B, 0xC5, 0xA5, 0x8C, 0x1D, 0xD2, 0x8F, 0x90, 0xA0, 0x80, 0x96, 0x6D, - 0x14, 0x6D, 0x8B, 0xE9, 0xF4, 0xD7, 0xDF, 0xE7, 0xDB, 0x94, 0x34, 0xD6, 0x6E, 0xDF, 0xC7, 0xB6, - 0xB4, 0x23, 0x18, 0xFD, 0x7C, 0xE8, 0xD9, 0x35, 0xB2, 0xE9, 0x98, 0x5C, 0xDB, 0xB5, 0x85, 0x17, - 0xCB, 0xF9, 0xDF, 0x7F, 0x7C, 0xC6, 0x6B, 0xEF, 0x7F, 0xC5, 0xB1, 0xEC, 0x5C, 0x2C, 0x75, 0x56, - 0xAA, 0xCC, 0x35, 0xDC, 0x3F, 0x62, 0x30, 0x51, 0xE1, 0xCE, 0x31, 0xA4, 0xCE, 0x6A, 0xE5, 0x8D, - 0x65, 0xFF, 0x62, 0xEF, 0xE1, 0xFA, 0x25, 0xE3, 0x3C, 0x32, 0x13, 0xB9, 0xB8, 0xD9, 0x8B, 0x5B, - 0x01, 0x57, 0xD5, 0xDF, 0xAB, 0x45, 0x3A, 0xAA, 0xAA, 0x01, 0xB2, 0x2F, 0x14, 0xB1, 0x74, 0xED, - 0x16, 0xB4, 0x56, 0x84, 0x61, 0x6A, 0x9A, 0x46, 0x8D, 0x45, 0x7A, 0x40, 0xCD, 0x35, 0xB5, 0xA4, - 0x1E, 0x38, 0xC1, 0xB4, 0x3F, 0x2D, 0xE6, 0x93, 0xEF, 0xB6, 0x35, 0xBD, 0x2E, 0xD9, 0x66, 0xC6, - 0x4B, 0x0B, 0x97, 0xB3, 0x74, 0xED, 0x16, 0xCC, 0x0E, 0xA1, 0xA2, 0x31, 0xE1, 0xC1, 0x44, 0x85, - 0x19, 0x9B, 0x8F, 0xC4, 0x07, 0xD0, 0xA9, 0xEC, 0x3B, 0x72, 0x8A, 0x75, 0x3B, 0xD2, 0x1D, 0x5B, - 0xBF, 0x47, 0xFA, 0xD2, 0x5D, 0x02, 0x57, 0x16, 0x44, 0xDC, 0x82, 0x8C, 0xAA, 0x00, 0xE0, 0xCB, - 0xEF, 0x77, 0xB0, 0xEB, 0xE0, 0x49, 0xB9, 0x0E, 0x38, 0x0B, 0x21, 0x08, 0x33, 0x06, 0x70, 0xD7, - 0x6D, 0xFD, 0xEB, 0x9B, 0xBC, 0x3D, 0x0D, 0x18, 0xDC, 0xDD, 0x78, 0xE5, 0xDD, 0x15, 0xEC, 0xFC, - 0xF9, 0xC4, 0xD5, 0xD7, 0x26, 0x45, 0x61, 0xD5, 0xC6, 0x9F, 0xF8, 0xF4, 0xDF, 0xDB, 0x19, 0x79, - 0x73, 0x62, 0xFD, 0xF5, 0xC1, 0x01, 0xBE, 0x3C, 0x3F, 0xE5, 0x5E, 0x19, 0x6B, 0xEC, 0x44, 0xF4, - 0xA3, 0xD9, 0x5C, 0xC3, 0xA2, 0xAF, 0x7E, 0xA0, 0xE8, 0x62, 0x85, 0xBD, 0xB5, 0x14, 0x17, 0x7F, - 0xD8, 0xC5, 0x95, 0x65, 0x85, 0x2C, 0x48, 0xD1, 0x75, 0x2F, 0xE0, 0x51, 0x65, 0xAA, 0xA1, 0xE8, - 0x62, 0x05, 0xF7, 0x0C, 0x1D, 0x88, 0x87, 0xC1, 0xF9, 0xA2, 0x39, 0x8A, 0xAA, 0x92, 0xD4, 0xB3, - 0x2B, 0x25, 0x65, 0x95, 0x14, 0x94, 0x94, 0x73, 0xEF, 0xF0, 0x9B, 0x99, 0xFD, 0xD8, 0x44, 0xBE, - 0x4D, 0x49, 0x27, 0x33, 0x27, 0x8F, 0x7B, 0x87, 0x0D, 0xC2, 0xE0, 0xD8, 0x9F, 0xA2, 0x90, 0x5B, - 0x58, 0xC2, 0xEF, 0xDF, 0x5A, 0x86, 0x5E, 0xA7, 0x63, 0xD9, 0x1F, 0x9F, 0xE0, 0x81, 0x91, 0xB7, - 0x12, 0xDF, 0x35, 0x82, 0x19, 0x0F, 0x8F, 0x63, 0xDC, 0xED, 0x03, 0x9D, 0xDB, 0x16, 0xE9, 0x54, - 0x56, 0x6F, 0xDA, 0xCD, 0x9C, 0x8F, 0xBF, 0x71, 0xD4, 0xAE, 0xBE, 0x44, 0x7E, 0x15, 0xC1, 0x65, - 0xF6, 0x20, 0x57, 0xD7, 0x79, 0x3A, 0x87, 0x0C, 0x33, 0x1A, 0x0C, 0x70, 0xE6, 0x42, 0x21, 0x46, - 0x3F, 0x6F, 0x7E, 0x95, 0x14, 0xD7, 0x22, 0x2D, 0xD8, 0xD7, 0xDB, 0x8B, 0x3B, 0x6F, 0xEB, 0xCF, - 0x83, 0xA3, 0x87, 0x30, 0x69, 0xF4, 0x10, 0x92, 0xE2, 0x63, 0x11, 0x42, 0xE3, 0x93, 0x75, 0xDB, - 0xE9, 0xD3, 0x3D, 0x9A, 0xC4, 0x9E, 0x31, 0x0D, 0x6F, 0xBC, 0x4E, 0xE5, 0x8B, 0x7F, 0xA7, 0xF0, - 0xD9, 0xFA, 0x14, 0x5E, 0xFD, 0xAF, 0x87, 0xB8, 0x7F, 0xD4, 0xAD, 0x04, 0xFA, 0x7B, 0x73, 0x4B, - 0xDF, 0x38, 0x62, 0x22, 0x42, 0x9C, 0x13, 0x09, 0xAA, 0xCA, 0x89, 0xEC, 0x5C, 0x9E, 0x7A, 0x6B, - 0x19, 0xB9, 0x85, 0xF5, 0xB1, 0xB7, 0x39, 0xC0, 0xB3, 0x34, 0x91, 0x5F, 0xDE, 0x5E, 0x70, 0x35, - 0x43, 0x34, 0xE4, 0x46, 0x71, 0x14, 0x10, 0xAA, 0x69, 0x82, 0x03, 0x27, 0xCF, 0xD2, 0x3F, 0x3E, - 0x96, 0xEE, 0x31, 0x11, 0x2D, 0x5A, 0xE0, 0xDD, 0x74, 0x3A, 0x8C, 0x81, 0x7E, 0x78, 0xB8, 0xCB, - 0x0C, 0xE0, 0xB8, 0xAE, 0x91, 0x6C, 0xDE, 0x7B, 0x88, 0xFC, 0x92, 0x8B, 0xDC, 0x3F, 0xE2, 0x96, - 0xFA, 0x05, 0xBE, 0xB6, 0xD6, 0xC2, 0x5F, 0x96, 0x7E, 0x8D, 0x97, 0x87, 0x81, 0x37, 0x9F, 0xFE, - 0x0D, 0x3E, 0x9E, 0x86, 0x86, 0x88, 0x13, 0x67, 0xEE, 0xA7, 0x28, 0x94, 0x55, 0x56, 0xF1, 0xEC, - 0xDF, 0x3F, 0x61, 0x6B, 0x5A, 0x7D, 0x04, 0xA5, 0x15, 0xF9, 0x71, 0x81, 0xD5, 0x2E, 0xA6, 0x57, - 0x87, 0xD4, 0x2E, 0x3F, 0x8D, 0xAC, 0x04, 0x51, 0x09, 0x50, 0x50, 0x2A, 0x35, 0x9F, 0xA3, 0xA7, - 0x72, 0x5A, 0xB6, 0x9E, 0x40, 0x43, 0x05, 0x07, 0x21, 0x08, 0x31, 0xFA, 0xF3, 0xDB, 0x09, 0x23, - 0x38, 0x73, 0xA1, 0x88, 0xCA, 0x2A, 0x53, 0xFD, 0x5E, 0xA5, 0xFC, 0x52, 0x35, 0x39, 0xF9, 0xC5, - 0xFC, 0xE7, 0xD8, 0xDB, 0x08, 0x0B, 0x0E, 0x68, 0xB1, 0x56, 0x67, 0xAE, 0xAD, 0xE5, 0xAF, 0x4B, - 0x57, 0xB3, 0x7A, 0xF3, 0x1E, 0xC7, 0x33, 0xDF, 0x21, 0x4B, 0x2E, 0xB9, 0x1C, 0x1D, 0x55, 0xE5, - 0x78, 0x35, 0xF0, 0x1E, 0x36, 0xEB, 0xE8, 0xCF, 0x27, 0xB2, 0x99, 0x31, 0xF7, 0x23, 0x72, 0x72, - 0x0B, 0x5B, 0xBF, 0x61, 0x14, 0x82, 0x31, 0x43, 0xFA, 0xD1, 0x2D, 0x2A, 0x94, 0x3A, 0x87, 0x7C, - 0x91, 0x3A, 0xAB, 0x95, 0x40, 0x3F, 0x1F, 0xC6, 0xDE, 0x9A, 0xD4, 0x32, 0x5B, 0xAC, 0xA2, 0x60, - 0xAE, 0xB5, 0x30, 0xE7, 0xA3, 0xB5, 0x2C, 0xFC, 0x6A, 0x83, 0x74, 0x3F, 0x4B, 0x1C, 0x01, 0x5E, - 0xC1, 0x21, 0xDC, 0xD3, 0x95, 0xE8, 0xA8, 0x5A, 0x81, 0x1A, 0x90, 0x81, 0x0C, 0x13, 0xEA, 0x05, - 0x90, 0x95, 0x5B, 0xC8, 0xC9, 0xB3, 0x17, 0xB8, 0xBD, 0x7F, 0x82, 0x73, 0xE5, 0x36, 0x2E, 0x87, - 0x00, 0x7F, 0x1F, 0x4F, 0x06, 0x27, 0xF6, 0x20, 0xD4, 0xE8, 0x57, 0x2F, 0xB2, 0xF4, 0x3A, 0x1D, - 0x91, 0x21, 0x46, 0x6E, 0xE9, 0xDD, 0x1D, 0x37, 0x9D, 0x93, 0x8F, 0xA7, 0xAA, 0x54, 0x56, 0x9B, - 0xF9, 0xDB, 0xD2, 0xD5, 0xCC, 0xFD, 0xFC, 0xBB, 0xFA, 0xC0, 0x6C, 0x64, 0xE0, 0xDF, 0x13, 0xC8, - 0x60, 0xC2, 0x0E, 0x41, 0x47, 0x16, 0x6F, 0xAC, 0x46, 0x7E, 0x65, 0x73, 0x10, 0x32, 0x16, 0x96, - 0xCC, 0x73, 0xF9, 0x1C, 0x3A, 0x95, 0xC3, 0xA0, 0x84, 0x6E, 0x84, 0xB6, 0x54, 0xBC, 0x20, 0x23, - 0x41, 0x02, 0xFC, 0x7D, 0x1A, 0x6D, 0x10, 0xDD, 0xF4, 0x3A, 0xE2, 0x6E, 0x8A, 0x72, 0x9E, 0x19, - 0x3A, 0x95, 0xF3, 0xF9, 0xC5, 0xBC, 0xF0, 0xCF, 0xCF, 0x79, 0x77, 0xF5, 0x8F, 0xD4, 0x5A, 0xEA, - 0xA3, 0x3E, 0x8B, 0x90, 0x45, 0x62, 0xBE, 0xE9, 0x40, 0x1A, 0x75, 0x78, 0x35, 0xCD, 0x8B, 0x48, - 0xA6, 0x0C, 0xC4, 0x96, 0x42, 0x77, 0x26, 0xB7, 0x90, 0x9D, 0x3F, 0x1F, 0x27, 0x36, 0x3C, 0x84, - 0xD8, 0xE8, 0xF0, 0x6B, 0xEF, 0xBE, 0xAF, 0x86, 0xAB, 0xF1, 0xD0, 0x49, 0x93, 0x88, 0x26, 0x20, - 0x25, 0xED, 0x08, 0x4F, 0xCE, 0x59, 0xCA, 0xB7, 0xDB, 0xD3, 0xB0, 0x36, 0x6C, 0x16, 0x0B, 0x90, - 0xCC, 0x58, 0xD1, 0xC1, 0xF4, 0xE9, 0x94, 0xF2, 0xA6, 0x85, 0xC8, 0x4F, 0x6B, 0xF7, 0xC5, 0x16, - 0x72, 0x5A, 0x50, 0x5A, 0xCE, 0x86, 0x9F, 0x7E, 0xA6, 0xA6, 0xA6, 0x96, 0x5E, 0x37, 0x45, 0xC9, - 0x04, 0x1A, 0x57, 0xC1, 0x66, 0xA4, 0xCC, 0x2D, 0x28, 0xE1, 0xEF, 0x9F, 0x7F, 0xC7, 0xEC, 0x05, - 0x5F, 0x34, 0xCA, 0xD2, 0x45, 0x2A, 0x21, 0x4F, 0x22, 0xF3, 0x35, 0x3A, 0x3C, 0x20, 0xA0, 0xB3, - 0xEA, 0xCD, 0x16, 0x21, 0x2D, 0xC2, 0x5D, 0x90, 0x5F, 0x8D, 0x56, 0x4D, 0xE6, 0x5A, 0x76, 0x64, - 0x1C, 0x27, 0xF5, 0xE7, 0xE3, 0xF8, 0x79, 0x79, 0x10, 0x13, 0x1E, 0x8C, 0xA1, 0xB9, 0x64, 0xD1, - 0x96, 0x40, 0x95, 0x96, 0xE2, 0xC2, 0xD2, 0x32, 0xBE, 0xF8, 0xF7, 0x0E, 0x9E, 0x7D, 0xE7, 0x13, - 0x56, 0x6D, 0xFC, 0xC9, 0x1E, 0xCD, 0x0E, 0x92, 0xF8, 0xDB, 0x91, 0xC5, 0x13, 0x5A, 0x57, 0xFE, - 0xA7, 0x1D, 0xD0, 0xD9, 0x5E, 0x8A, 0x40, 0xA4, 0x68, 0xF8, 0x3D, 0x0E, 0xDF, 0x3E, 0xF7, 0x34, - 0xB8, 0x33, 0x6C, 0x40, 0x2F, 0xA6, 0x8C, 0x1B, 0xCE, 0xE8, 0xC1, 0x7D, 0x09, 0x0F, 0x09, 0x40, - 0x51, 0x75, 0x36, 0x95, 0xD7, 0xC9, 0x1C, 0x11, 0x45, 0xB1, 0x79, 0x2A, 0x15, 0x6A, 0x6B, 0x6A, - 0x38, 0x95, 0x93, 0xCF, 0xBA, 0x9D, 0xE9, 0x7C, 0xB5, 0xF1, 0x27, 0x0E, 0x9C, 0x3C, 0x7B, 0x79, - 0x48, 0x4F, 0x39, 0xF0, 0x21, 0xF2, 0x23, 0x98, 0x2E, 0x71, 0xCD, 0x3A, 0x8B, 0xCE, 0x66, 0x08, - 0x48, 0x17, 0xC0, 0x9D, 0xC0, 0x8B, 0xC8, 0x62, 0xC4, 0xF5, 0xB3, 0xD6, 0x4D, 0xAF, 0x23, 0xBE, - 0x6B, 0x24, 0x23, 0x6F, 0xEE, 0xC3, 0xA8, 0x5B, 0x12, 0xE9, 0xD3, 0x3D, 0x86, 0x88, 0xE0, 0x40, - 0xBC, 0x3C, 0xDC, 0x1B, 0xAA, 0xCB, 0x35, 0x82, 0x00, 0x4D, 0x50, 0x6B, 0xB1, 0x50, 0x56, 0x51, - 0xC5, 0x99, 0x0B, 0x85, 0xEC, 0x3B, 0x7A, 0x9A, 0x2D, 0xFB, 0x0E, 0xB3, 0xFB, 0x50, 0x26, 0xF9, - 0x25, 0x65, 0x97, 0xE7, 0xB3, 0xD4, 0x21, 0x3F, 0xA5, 0x3A, 0x17, 0xF9, 0x01, 0xCC, 0x4E, 0x0F, - 0x9D, 0xBD, 0x1E, 0x18, 0x62, 0x47, 0x28, 0xB2, 0xB4, 0xC7, 0x34, 0xA4, 0x7A, 0xDC, 0x68, 0x6C, - 0xEE, 0x6E, 0x7A, 0x42, 0x8D, 0xFE, 0x74, 0x8B, 0x0C, 0x25, 0x36, 0x2A, 0x8C, 0x2E, 0xA1, 0x46, - 0x82, 0xFC, 0x7D, 0xF0, 0xF2, 0x34, 0xA0, 0x2A, 0x2A, 0xE6, 0x5A, 0x0B, 0x15, 0x97, 0xAA, 0xB9, - 0x50, 0x7C, 0x91, 0x9C, 0xFC, 0x62, 0xCE, 0xE4, 0x16, 0x72, 0xBE, 0xB0, 0x84, 0x8A, 0xAA, 0xAB, - 0x26, 0x15, 0xD5, 0x21, 0x93, 0x2C, 0x97, 0x02, 0x5F, 0x21, 0x8D, 0x86, 0xFF, 0x8F, 0xAB, 0x40, - 0x41, 0xA6, 0x44, 0xBC, 0x88, 0xDC, 0xB7, 0xD4, 0xD0, 0x82, 0xEF, 0xA2, 0x3B, 0xF1, 0x2B, 0x03, - 0x7E, 0x40, 0x32, 0x3D, 0xB4, 0xB3, 0x1F, 0xB6, 0x29, 0x02, 0x5C, 0xAF, 0x08, 0x45, 0xD6, 0x06, - 0xBE, 0x0B, 0xF8, 0x15, 0x72, 0xEF, 0xE2, 0x6C, 0x32, 0x91, 0x1D, 0x16, 0xA4, 0x23, 0xE9, 0x38, - 0x32, 0xA1, 0x68, 0x13, 0x72, 0x66, 0x54, 0x75, 0xF6, 0xC3, 0x35, 0x85, 0xEB, 0x99, 0x21, 0x76, - 0xE8, 0x90, 0x16, 0xE3, 0x3E, 0x48, 0xAB, 0x71, 0x5F, 0xA0, 0x07, 0x92, 0x61, 0x01, 0x34, 0xE4, - 0xF5, 0xD5, 0x22, 0x09, 0x5D, 0x8C, 0xF4, 0xEA, 0x1D, 0x45, 0xA6, 0x1C, 0x67, 0x20, 0x23, 0x44, - 0x2A, 0x5B, 0x76, 0xDB, 0xCE, 0xC1, 0xFF, 0x01, 0xCE, 0x34, 0xF5, 0xEC, 0x2D, 0xA9, 0x9C, 0xA8, - 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3A, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x00, 0x32, 0x30, 0x31, 0x32, 0x2D, 0x30, 0x31, 0x2D, 0x33, 0x31, 0x54, 0x31, - 0x33, 0x3A, 0x34, 0x31, 0x3A, 0x31, 0x38, 0x2D, 0x30, 0x38, 0x3A, 0x30, 0x30, 0xED, 0x4F, 0xCC, - 0x0D, 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3A, 0x6D, 0x6F, - 0x64, 0x69, 0x66, 0x79, 0x00, 0x32, 0x30, 0x31, 0x32, 0x2D, 0x30, 0x31, 0x2D, 0x33, 0x31, 0x54, - 0x31, 0x33, 0x3A, 0x34, 0x31, 0x3A, 0x31, 0x38, 0x2D, 0x30, 0x38, 0x3A, 0x30, 0x30, 0x9C, 0x12, - 0x74, 0xB1, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, - 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2E, - 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, +#include + +static const uint8_t mu_law_wave[] PROGMEM = { +0x52, 0x49, 0x46, 0x46, 0x84, 0x5D, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, +0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x40, 0x1F, 0x00, 0x00, +0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0x00, 0x5D, +0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x5D, 0x00, 0x00, 0xFB, 0xFD, 0xFF, 0xFE, 0xFF, 0x7F, +0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7C, 0x7D, +0x7C, 0x7C, 0x7D, 0x7F, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7E, 0xFF, 0x7F, +0x7D, 0x7C, 0x7B, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7C, 0x7C, 0x7B, +0x7B, 0x7B, 0x7A, 0x7A, 0x7B, 0x7A, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x7C, 0x7E, +0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x79, 0x79, 0x7B, 0x7B, 0x7B, 0x7C, 0x7C, 0x7E, 0xFF, 0xFE, 0xFE, +0xFF, 0x7E, 0x7C, 0x7D, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x79, 0x78, 0x79, +0x7A, 0x79, 0x78, 0x78, 0x78, 0x77, 0x78, 0x78, 0x79, 0x79, 0x78, 0x77, 0x77, 0x78, 0x78, 0x79, +0x7A, 0x79, 0x79, 0x78, 0x78, 0x7A, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, +0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFC, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF8, +0xF7, 0xF7, 0xF8, 0xF9, 0xF9, 0xFB, 0xFC, 0xFC, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFE, +0xFF, 0xFF, 0xFE, 0x7E, 0x7D, 0x7D, 0x7C, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x7C, +0x7B, 0x7A, 0x79, 0x79, 0x79, 0x77, 0x77, 0x78, 0x79, 0x7B, 0x7C, 0x7B, 0x7B, 0x7C, 0x7B, 0x7B, +0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x79, +0x78, 0x77, 0x77, 0x79, 0x79, 0x79, 0x7B, 0x7B, 0x7A, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7C, 0x7C, +0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x79, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x78, 0x78, 0x7A, 0x7A, 0x79, +0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFC, 0xFB, 0xFB, 0xFC, +0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFE, 0xFF, 0x7E, 0x7E, 0xFF, 0xFF, 0x7E, 0x7E, 0x7D, 0x7B, +0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, +0x7C, 0x7B, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7C, 0x79, 0x79, 0x7A, +0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7C, 0x7C, 0x7C, 0x7E, 0x7D, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0xFE, +0xFD, 0xFD, 0xFB, 0xFB, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, +0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7D, 0x7E, 0x7D, 0x7B, 0x7B, 0x7B, 0x79, 0x78, 0x77, +0x78, 0x78, 0x77, 0x77, 0x77, 0x78, 0x78, 0x76, 0x76, 0x75, 0x73, 0x73, 0x73, 0x72, 0x73, 0x73, +0x74, 0x74, 0x74, 0x75, 0x77, 0x78, 0x78, 0x77, 0x76, 0x77, 0x76, 0x76, 0x78, 0x79, 0x79, 0x79, +0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7E, +0x7E, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD, 0xFE, 0xFF, 0xFD, 0xFE, 0x7F, 0xFF, +0x7F, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7D, 0x7F, +0xFF, 0xFF, 0xFE, 0xFE, 0x7F, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7C, 0x7D, +0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFF, 0xFE, 0xFC, 0xFD, 0xFC, 0xFC, 0xFD, +0xFC, 0xFB, 0xFB, 0xFC, 0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, +0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7D, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7D, 0x7B, 0x7B, 0x79, +0x78, 0x78, 0x78, 0x76, 0x74, 0x74, 0x73, 0x72, 0x75, 0x77, 0x77, 0x76, 0x75, 0x75, 0x76, 0x77, +0x77, 0x76, 0x74, 0x74, 0x74, 0x74, 0x76, 0x76, 0x76, 0x77, 0x75, 0x76, 0x78, 0x79, 0x77, 0x77, +0x79, 0x7B, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7D, 0x7F, 0x7E, 0xFF, 0xFF, 0x7F, 0xFE, 0xFC, 0xFE, +0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFD, 0xFC, 0xFE, 0xFF, 0x7E, 0x7F, 0x7F, 0x7D, 0x7E, 0xFF, 0x7F, +0x7F, 0xFF, 0x7F, 0x7E, 0x7D, 0x7E, 0xFF, 0x7E, 0x7F, 0x7F, 0x7C, 0x7D, 0x7F, 0x7D, 0x7E, 0x7E, +0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7B, 0x7D, 0x7B, 0x7A, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7C, +0x7D, 0x7E, 0x7F, 0xFF, 0x7F, 0x7F, 0x7F, 0xFF, 0xFD, 0xFD, 0xFD, 0xFC, 0xFB, 0xFB, 0xFB, 0xFB, +0xFA, 0xFB, 0xFB, 0xFB, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0x7E, 0x7E, 0x7D, 0x7B, 0x7B, 0x7B, +0x78, 0x79, 0x78, 0x75, 0x76, 0x76, 0x76, 0x76, 0x74, 0x74, 0x73, 0x73, 0x74, 0x73, 0x72, 0x73, +0x75, 0x75, 0x74, 0x76, 0x74, 0x72, 0x73, 0x74, 0x75, 0x75, 0x76, 0x77, 0x77, 0x78, 0x77, 0x77, +0x77, 0x78, 0x78, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x79, 0x79, 0x7A, 0x79, 0x7B, 0x7C, 0x7B, 0x7B, +0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7C, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, +0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, +0xFE, 0x7E, 0x7E, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7E, 0xFF, 0x7E, 0x7E, 0x7E, 0x7D, +0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFC, 0xFE, 0x7F, 0x7E, 0x7E, 0x7F, 0xFF, 0xFF, +0x7F, 0x7E, 0x7D, 0x7D, 0x7D, 0x7B, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, 0x7D, 0x7C, 0x7C, +0x7D, 0x7C, 0x7C, 0x7E, 0x7E, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, +0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFB, +0xFB, 0xFB, 0xFA, 0xFB, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, +0x7E, 0x7E, 0x7E, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7F, +0x7D, 0x7B, 0x7C, 0x7C, 0x7D, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7E, +0x7F, 0x7E, 0x7D, 0x7C, 0x7B, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x77, 0x77, 0x76, 0x74, 0x73, +0x72, 0x73, 0x74, 0x75, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, +0x7C, 0x7D, 0x7E, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7D, 0x7C, 0x7B, +0x7A, 0x7B, 0x7B, 0x7A, 0x7A, 0x79, 0x79, 0x7A, 0x79, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x79, 0x79, +0x79, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7C, 0x7B, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7F, +0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7D, 0x7D, +0x7D, 0x7D, 0x7E, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7F, 0x7F, 0x7E, 0x7F, 0x7E, 0x7E, 0x7F, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7F, 0x7F, 0x7E, +0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, +0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, +0x7D, 0x7E, 0xFF, 0x7E, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, +0x7A, 0x79, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7C, 0x7C, 0x7A, 0x79, 0x79, 0x78, 0x78, +0x78, 0x77, 0x77, 0x76, 0x75, 0x74, 0x74, 0x74, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x76, 0x75, +0x74, 0x75, 0x77, 0x78, 0x78, 0x7A, 0x7B, 0x7D, 0xFF, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, +0xFC, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, +0xF8, 0xFA, 0xF8, 0xF6, 0xF7, 0xF7, 0xF8, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFB, +0xFB, 0xFB, 0xFD, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, +0x7F, 0x7E, 0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0xFD, 0xFE, 0xFF, +0xFE, 0xFE, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7E, 0x7F, +0x7D, 0x7D, 0x7E, 0x7E, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x79, 0x77, 0x78, 0x79, 0x78, 0x77, 0x76, +0x76, 0x75, 0x73, 0x74, 0x73, 0x71, 0x70, 0x70, 0x71, 0x72, 0x71, 0x71, 0x70, 0x6F, 0x6F, 0x6F, +0x70, 0x71, 0x71, 0x72, 0x73, 0x74, 0x74, 0x74, 0x76, 0x77, 0x76, 0x78, 0x79, 0x79, 0x79, 0x78, +0x7A, 0x7B, 0x7B, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, +0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7D, +0x7D, 0x7D, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7C, 0x7A, 0x78, 0x79, 0x78, 0x77, +0x76, 0x78, 0x79, 0x78, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7A, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, +0x7E, 0xFF, 0xFD, 0xFC, 0xFC, 0xFA, 0xF9, 0xFA, 0xFB, 0xFA, 0xF9, 0xF8, 0xF8, 0xF9, 0xFA, 0xF9, +0xF9, 0xF9, 0xF9, 0xF9, 0xF8, 0xF7, 0xF9, 0xF9, 0xF8, 0xF8, 0xFA, 0xFB, 0xFA, 0xFB, 0xFD, 0xFD, +0xFC, 0xFD, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFF, 0x7F, 0x7F, 0x7E, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, +0x7A, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x75, 0x76, 0x77, 0x76, 0x75, 0x76, 0x76, 0x75, 0x76, 0x77, +0x78, 0x79, 0x7B, 0x7B, 0x7B, 0x7A, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7A, 0x7B, 0x7B, 0x7A, +0x7B, 0x7D, 0x7E, 0x7B, 0x7C, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFC, 0xFD, 0xFE, 0xFF, +0xFE, 0xFF, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7B, 0x7D, 0x7F, 0x7F, 0x7C, 0x7B, +0x7A, 0x78, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7C, 0x7C, 0x7A, 0x7B, 0x7C, 0x7C, 0x7B, 0x7C, 0x7C, +0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x79, 0x7A, 0x79, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, +0x7D, 0xFE, 0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFA, 0xF8, 0xF8, 0xF9, 0xF8, 0xF5, 0xF4, 0xF7, 0xF8, +0xF8, 0xF8, 0xF9, 0xFB, 0xFC, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7B, +0x7A, 0x7B, 0x7B, 0x7A, 0x78, 0x78, 0x77, 0x76, 0x76, 0x75, 0x76, 0x76, 0x76, 0x77, 0x75, 0x75, +0x76, 0x75, 0x76, 0x76, 0x78, 0x78, 0x77, 0x78, 0x76, 0x74, 0x75, 0x77, 0x79, 0x7A, 0x7A, 0x7B, +0x7C, 0x7D, 0x7D, 0x7D, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, +0xFE, 0x7F, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0xFF, 0xFD, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, +0xFC, 0xFD, 0xFE, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0x7E, 0x7D, 0x7C, +0x7B, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7A, 0x7A, 0x7C, 0x7A, 0x7B, 0x7C, 0x7C, 0x7E, 0x7E, 0xFF, +0xFF, 0x7F, 0x7F, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, +0x7C, 0x7D, 0xFF, 0x7E, 0x7C, 0x7D, 0xFF, 0xFF, 0xFF, 0xFD, 0xFD, 0xFF, 0x7E, 0x7F, 0x7E, 0x7E, +0x7F, 0x7E, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x79, +0x77, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7C, 0x7D, 0x7D, 0x7E, 0xFD, 0xFD, 0xFE, 0xFC, 0xFC, 0xFB, +0xFB, 0xFC, 0xFB, 0xFB, 0xFA, 0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, +0xFF, 0xFF, 0xFD, 0xFC, 0xFD, 0xFD, 0xFC, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0x7E, 0x7D, 0x7D, 0x7C, 0x7D, 0x7E, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, +0x7B, 0x7C, 0x7D, 0x7B, 0x7B, 0x7C, 0x7A, 0x78, 0x78, 0x78, 0x77, 0x75, 0x75, 0x75, 0x73, 0x72, +0x72, 0x72, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x75, 0x75, 0x74, 0x74, +0x75, 0x76, 0x76, 0x76, 0x77, 0x78, 0x77, 0x78, 0x79, 0x77, 0x78, 0x78, 0x79, 0x7A, 0x7A, 0x7A, +0x7A, 0x79, 0x77, 0x76, 0x76, 0x78, 0x78, 0x77, 0x77, 0x77, 0x75, 0x74, 0x74, 0x75, 0x75, 0x76, +0x79, 0x79, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7D, 0xFF, 0xFD, 0xFD, 0xFE, 0xFF, 0xFE, 0xFC, +0xF9, 0xF9, 0xFB, 0xFA, 0xF9, 0xF8, 0xF8, 0xF8, 0xF6, 0xF4, 0xF6, 0xF8, 0xF7, 0xF7, 0xF9, 0xF9, +0xF9, 0xF8, 0xF8, 0xFB, 0xFA, 0xF9, 0xFA, 0xFB, 0xFC, 0xFE, 0xFC, 0xFD, 0xFF, 0x7E, 0x7F, 0x7E, +0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7B, 0x7B, 0x7A, 0x79, 0x7A, 0x7A, 0x79, 0x79, 0x78, 0x77, 0x78, +0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7D, 0x7E, 0x7D, 0x7D, 0x7F, 0xFE, 0xFE, 0xFD, 0xFD, +0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xFC, 0xFB, 0xFC, +0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, +0x7C, 0x7B, 0x7B, 0x7C, 0x79, 0x79, 0x79, 0x78, 0x76, 0x75, 0x76, 0x78, 0x77, 0x76, 0x76, 0x77, +0x76, 0x74, 0x74, 0x75, 0x75, 0x75, 0x74, 0x74, 0x76, 0x77, 0x78, 0x7A, 0x7C, 0x7B, 0x7A, 0x7D, +0x7E, 0x7E, 0x7C, 0x7D, 0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFE, 0xFF, +0xFF, 0x7F, 0x7E, 0x7F, 0xFF, 0xFE, 0xFD, 0xFF, 0xFE, 0xFC, 0xFB, 0xFC, 0xFE, 0xFD, 0xFC, 0xFE, +0xFF, 0xFE, 0xFD, 0xFF, 0x7E, 0x7F, 0xFE, 0xFC, 0xFD, 0xFE, 0xFD, 0xFD, 0xFE, 0x7F, 0x7E, 0xFF, +0xFF, 0x7F, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, +0x7E, 0x7D, 0x7E, 0xFF, 0xFF, 0xFE, 0xFD, 0xFF, 0x7D, 0x7A, 0x79, 0x79, 0x7A, 0x7C, 0x7B, 0x7B, +0x7D, 0x7E, 0x7E, 0x7D, 0xFF, 0xFF, 0x7F, 0x7F, 0x7E, 0x7B, 0x7A, 0x7A, 0x7A, 0x78, 0x77, 0x77, +0x77, 0x78, 0x78, 0x76, 0x76, 0x76, 0x76, 0x74, 0x74, 0x73, 0x74, 0x74, 0x75, 0x74, 0x72, 0x74, +0x76, 0x74, 0x74, 0x76, 0x76, 0x74, 0x73, 0x73, 0x72, 0x73, 0x74, 0x76, 0x77, 0x75, 0x73, 0x75, +0x77, 0x78, 0x75, 0x75, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7B, +0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7D, 0x7C, 0x7D, 0x7B, 0x7C, 0x7F, +0xFF, 0xFF, 0x7E, 0x7D, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x79, 0x77, 0x77, +0x78, 0x76, 0x76, 0x78, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x7B, 0x7C, 0x7B, 0x7C, 0x7D, 0xFF, +0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFB, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, +0xFA, 0xF9, 0xF9, 0xF7, 0xF7, 0xF7, 0xF4, 0xF4, 0xF4, 0xF2, 0xEF, 0xEE, 0xEF, 0xF2, 0xF2, 0xF1, +0xF2, 0xF5, 0xF5, 0xF3, 0xF1, 0xF2, 0xF5, 0xF7, 0xF8, 0xFA, 0xFB, 0xFC, 0xFC, 0xFB, 0xFC, 0xFC, +0xFD, 0xFF, 0x7F, 0x7E, 0x7C, 0x7A, 0x7A, 0x78, 0x77, 0x76, 0x75, 0x75, 0x74, 0x71, 0x70, 0x71, +0x71, 0x71, 0x70, 0x6F, 0x71, 0x71, 0x71, 0x73, 0x75, 0x77, 0x77, 0x76, 0x79, 0x7A, 0x79, 0x79, +0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7F, 0xFF, 0xFE, 0xFF, 0x7E, 0x7D, 0x7E, 0x7E, 0x7D, 0x7D, +0x7E, 0x7C, 0x7C, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x77, 0x75, 0x75, 0x76, 0x78, 0x79, 0x78, +0x7A, 0x7C, 0x7C, 0x7A, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7A, 0x79, 0x7A, 0x7B, 0x7D, 0x7D, 0x7C, +0x7C, 0x7E, 0xFF, 0x7F, 0x7D, 0x7E, 0x7F, 0x7D, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0xFF, 0x7F, 0x7E, +0x7D, 0x7D, 0xFF, 0xFF, 0x7F, 0xFF, 0xFC, 0xFC, 0xFD, 0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xFB, 0xFB, +0xF9, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0x7F, 0x7F, 0x7E, 0x7D, 0x7E, 0x7D, 0x7B, 0x7C, 0x7B, +0x7B, 0x7C, 0x7C, 0x7A, 0x78, 0x78, 0x7B, 0x7A, 0x77, 0x75, 0x76, 0x75, 0x76, 0x76, 0x76, 0x76, +0x78, 0x79, 0x78, 0x75, 0x75, 0x76, 0x77, 0x7A, 0x7D, 0xFF, 0xFC, 0xFB, 0xFA, 0xFA, 0xFB, 0xFA, +0xFB, 0xFB, 0xFC, 0xFD, 0xFD, 0x7E, 0x78, 0x76, 0x72, 0x70, 0x71, 0x75, 0x77, 0x77, 0x79, 0x7A, +0x7A, 0x7A, 0x7B, 0xFF, 0xFB, 0xFA, 0xF9, 0xFB, 0xFD, 0xFE, 0x7E, 0x7F, 0xFF, 0xFC, 0xF6, 0xF3, +0xF7, 0xFC, 0xFF, 0x7A, 0x73, 0x6E, 0x6C, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6F, 0x74, 0x73, 0x72, +0x75, 0x7A, 0x7D, 0x7D, 0xFF, 0xFF, 0x7C, 0x7C, 0x7D, 0x7B, 0x7A, 0x79, 0x7B, 0xFF, 0xFF, 0x7D, +0x7C, 0xFF, 0xFE, 0x7D, 0xFC, 0xF3, 0xF6, 0xFC, 0xFE, 0xFE, 0xFF, 0x78, 0x75, 0x79, 0x7A, 0x7E, +0xF8, 0xF6, 0xF6, 0xF2, 0xEF, 0xEE, 0xF0, 0xF7, 0xFD, 0xFE, 0xFD, 0x7B, 0x77, 0x7C, 0xFF, 0xFB, +0xFD, 0x7D, 0xFF, 0x7C, 0x78, 0x7A, 0x7A, 0xFF, 0xF9, 0xF5, 0xF1, 0xF4, 0xFA, 0xFC, 0xFC, 0xFA, +0xF8, 0xFA, 0xFB, 0xFA, 0xFB, 0xFC, 0xFD, 0xFF, 0x7D, 0x7B, 0x7D, 0x7E, 0x7A, 0x76, 0x76, 0x75, +0x72, 0x75, 0x7B, 0x7D, 0x7A, 0x7A, 0x7D, 0xFF, 0x7C, 0x7C, 0xFF, 0x7D, 0x7A, 0x7B, 0x7C, 0x7E, +0xFC, 0xFC, 0x7F, 0xFE, 0xFD, 0xFE, 0xFC, 0xF8, 0xF9, 0xFA, 0xFB, 0xFE, 0x7E, 0x79, 0x72, 0x72, +0x74, 0x76, 0x78, 0x76, 0x78, 0x7A, 0x75, 0x76, 0x77, 0x75, 0x73, 0x73, 0x73, 0x73, 0x74, 0x73, +0x73, 0x77, 0x78, 0x79, 0x79, 0x7A, 0x79, 0x73, 0x73, 0x7B, 0x7C, 0x7C, 0xFC, 0xFC, 0xFE, 0x7F, +0x76, 0x73, 0x76, 0x73, 0x73, 0x78, 0x7C, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0x7D, 0x7B, 0x7B, +0x7C, 0x7A, 0x78, 0x7B, 0x7B, 0x7D, 0x7F, 0x7A, 0x77, 0x73, 0x6F, 0x70, 0x72, 0x73, 0x72, 0x72, +0x73, 0x74, 0x74, 0x76, 0x7D, 0xFE, 0x7D, 0x7C, 0x7C, 0x78, 0x76, 0x77, 0x79, 0x7B, 0xFF, 0xFD, +0xFE, 0x7E, 0x7B, 0x7A, 0x78, 0x78, 0x7B, 0x7C, 0x7C, 0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x79, 0x7B, +0xFF, 0xFA, 0xF9, 0xFD, 0x7E, 0x7D, 0x7C, 0x7B, 0x7E, 0xFE, 0xFC, 0xFF, 0x7E, 0xFE, 0xFD, 0xFE, +0xFD, 0xFC, 0xFB, 0xFC, 0xFC, 0xFD, 0xF9, 0xF3, 0xF2, 0xF3, 0xF4, 0xF6, 0xF6, 0xF8, 0xFA, 0xFC, +0xFB, 0xF7, 0xF9, 0xFC, 0xFC, 0xFD, 0xFD, 0x7E, 0x7C, 0x7F, 0xFF, 0x7D, 0x7A, 0x7A, 0x7B, 0x78, +0x78, 0x78, 0x75, 0x75, 0x76, 0x79, 0x7A, 0x79, 0x7C, 0x7F, 0xFD, 0xFF, 0x7E, 0xFF, 0x7D, 0x79, +0x77, 0x78, 0x79, 0x78, 0x79, 0x7C, 0x7D, 0x7F, 0x7E, 0x7E, 0xFE, 0xFC, 0xF8, 0xF7, 0xFA, 0xFB, +0xFC, 0xFD, 0xFF, 0x7D, 0x7E, 0x7F, 0xFF, 0x7D, 0x7B, 0x7D, 0xFE, 0xF9, 0xF5, 0xF6, 0xF6, 0xF4, +0xF6, 0xFA, 0xFE, 0x7D, 0x79, 0x78, 0x77, 0x77, 0x7A, 0x7E, 0xFE, 0xFF, 0xFF, 0xFA, 0xFA, 0xFE, +0x7C, 0x75, 0x71, 0x72, 0x74, 0x75, 0x74, 0x75, 0x75, 0x77, 0x79, 0x79, 0x77, 0x74, 0x74, 0x74, +0x72, 0x71, 0x70, 0x71, 0x70, 0x6F, 0x72, 0x73, 0x72, 0x73, 0x71, 0x70, 0x73, 0x78, 0x7A, 0x78, +0x77, 0x77, 0x76, 0x74, 0x73, 0x72, 0x70, 0x71, 0x75, 0x78, 0x7B, 0x7F, 0xFF, 0xFE, 0xFB, 0xFD, +0x7E, 0xFF, 0xFD, 0x7F, 0x7C, 0x7B, 0x7F, 0xFD, 0xFF, 0xFE, 0xFC, 0xFE, 0xFD, 0xFC, 0xFA, 0xF7, +0xF7, 0xFB, 0xFD, 0xFE, 0x7E, 0x7C, 0x7B, 0x7A, 0x7B, 0x79, 0x75, 0x75, 0x76, 0x78, 0x7D, 0xFF, +0x7E, 0x7D, 0x7E, 0x7C, 0x77, 0x75, 0x76, 0x76, 0x78, 0x7E, 0xFD, 0xFB, 0xFC, 0xFD, 0xFE, 0x7C, +0x78, 0x78, 0x7A, 0x7B, 0x7E, 0x7E, 0x7D, 0x7D, 0x7C, 0x7A, 0x78, 0x79, 0x7B, 0x7A, 0x7D, 0xFC, +0xFD, 0x7E, 0xFF, 0xFB, 0xF7, 0xF6, 0xF7, 0xF7, 0xF6, 0xFA, 0xFF, 0xFE, 0xFD, 0xFF, 0x7D, 0xFF, +0xFC, 0xFB, 0xF9, 0xFD, 0xFE, 0xFD, 0x7E, 0x79, 0x76, 0x71, 0x6F, 0x6E, 0x6E, 0x6F, 0x72, 0x76, +0x7A, 0x7E, 0xFF, 0xFF, 0x7C, 0x7B, 0x7C, 0x7B, 0x7A, 0x79, 0x7A, 0x7E, 0xFF, 0xFF, 0x7E, 0x7D, +0x7D, 0x7E, 0x7C, 0x78, 0x79, 0x7A, 0x7B, 0xFF, 0xFF, 0x7F, 0x7E, 0x7B, 0x79, 0x79, 0x76, 0x72, +0x73, 0x7B, 0xFF, 0xFE, 0xFB, 0xFA, 0xFA, 0xFC, 0x7D, 0x78, 0x77, 0x78, 0x79, 0x79, 0x78, 0x79, +0x77, 0x73, 0x74, 0x76, 0x77, 0x79, 0x7A, 0x7B, 0x7F, 0xFD, 0xFE, 0xFF, 0x7F, 0x7E, 0x7E, 0x7C, +0x78, 0x78, 0x7B, 0x7B, 0x7A, 0x7C, 0x7F, 0xFE, 0xFD, 0xFE, 0x7E, 0xFF, 0xFD, 0xFC, 0xF9, 0xF5, +0xF2, 0xF0, 0xF2, 0xF3, 0xF0, 0xF1, 0xF2, 0xF1, 0xF4, 0xF7, 0xF8, 0xFC, 0xFC, 0xF6, 0xF3, 0xF4, +0xF5, 0xF5, 0xF6, 0xFB, 0xFF, 0xFF, 0xFF, 0x7D, 0x7D, 0x7E, 0x7D, 0x79, 0x75, 0x74, 0x75, 0x72, +0x72, 0x75, 0x75, 0x74, 0x77, 0x7A, 0x7A, 0x7A, 0x7A, 0x77, 0x74, 0x73, 0x72, 0x74, 0x75, 0x76, +0x77, 0x74, 0x74, 0x77, 0x79, 0x79, 0x79, 0x78, 0x77, 0x74, 0x70, 0x6F, 0x70, 0x6F, 0x6E, 0x6E, +0x6E, 0x6F, 0x6F, 0x72, 0x75, 0x72, 0x74, 0x79, 0x7B, 0x7B, 0x7B, 0x7D, 0xFF, 0x7E, 0x7F, 0xFE, +0xFE, 0x7F, 0x7D, 0x7D, 0xFF, 0xFD, 0xFE, 0x7E, 0x7D, 0x7D, 0x7E, 0x7C, 0x79, 0x77, 0x75, 0x76, +0x77, 0x78, 0x78, 0x74, 0x75, 0x7A, 0x7F, 0xFB, 0xF9, 0xF9, 0xF8, 0xFD, 0x7E, 0x7B, 0x76, 0x78, +0x7B, 0x7C, 0x7E, 0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x7C, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0x7D, 0x7B, +0x7B, 0x79, 0x79, 0x7A, 0x7D, 0xFF, 0x7F, 0x7F, 0xFF, 0xFD, 0xFB, 0xFD, 0xFE, 0xFE, 0xFF, 0x7D, +0x7C, 0x7F, 0xFB, 0xF7, 0xF6, 0xFC, 0x7F, 0xFF, 0xFE, 0xFB, 0xFD, 0xFE, 0xFC, 0xFD, 0xFE, 0x7C, +0x76, 0x74, 0x74, 0x79, 0x79, 0x76, 0x77, 0x79, 0x7C, 0x7D, 0x7B, 0x7B, 0x7B, 0x7E, 0xFD, 0xFB, +0xF9, 0xF9, 0xFE, 0x7C, 0x79, 0x79, 0x77, 0x76, 0x79, 0x7B, 0x7D, 0x7C, 0x7C, 0x7D, 0x7C, 0x78, +0x75, 0x74, 0x72, 0x72, 0x73, 0x71, 0x70, 0x73, 0x78, 0x7C, 0x7D, 0x7E, 0xFF, 0x7F, 0x7F, 0x7D, +0x7A, 0x78, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x7B, 0xFF, 0xFF, 0xFD, 0xFC, 0xFC, 0xFA, 0xF9, +0xF8, 0xF9, 0xFE, 0x7E, 0xFF, 0xFE, 0xFE, 0x7D, 0x7A, 0x78, 0x75, 0x75, 0x77, 0x7A, 0x7A, 0x79, +0x7B, 0x7D, 0xFF, 0xFD, 0xFC, 0xFC, 0xFB, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, 0xFB, 0xFA, 0xF9, 0xF7, +0xF8, 0xFA, 0xF8, 0xF9, 0xFC, 0xFD, 0xFD, 0xFC, 0xF9, 0xF7, 0xF8, 0xFC, 0x7E, 0x7B, 0x7A, 0x7A, +0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x77, 0x72, 0x72, 0x74, 0x76, 0x7B, 0x7C, 0x7B, 0x7D, 0x7E, 0x7D, +0x7D, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x78, 0x78, 0x79, 0x79, 0x79, 0x78, 0x79, 0x7B, 0x7C, 0x7C, +0x7A, 0x7A, 0x77, 0x75, 0x75, 0x74, 0x73, 0x70, 0x6E, 0x6E, 0x6E, 0x6F, 0x72, 0x75, 0x77, 0x7A, +0x7D, 0x7D, 0x7D, 0x7E, 0xFF, 0x7D, 0x7A, 0x7A, 0x7A, 0x7B, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, +0x7D, 0x7B, 0x78, 0x75, 0x73, 0x74, 0x75, 0x75, 0x78, 0x79, 0x7A, 0x79, 0x79, 0x7D, 0xFE, 0xFC, +0xF9, 0xF8, 0xF8, 0xF8, 0xF6, 0xF5, 0xF6, 0xF8, 0xFB, 0xFC, 0xFB, 0xFD, 0xFF, 0x7E, 0x7D, 0x7C, +0x7C, 0x7B, 0x7C, 0x7B, 0x79, 0x79, 0x7A, 0x79, 0x76, 0x74, 0x72, 0x72, 0x73, 0x74, 0x75, 0x77, +0x78, 0x78, 0x78, 0x77, 0x76, 0x75, 0x75, 0x78, 0x7A, 0x79, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0x7F, +0xFE, 0xFC, 0xFC, 0xFC, 0xFA, 0xF8, 0xF7, 0xF7, 0xF8, 0xF7, 0xF5, 0xF5, 0xF5, 0xF4, 0xF5, 0xF6, +0xF7, 0xF8, 0xF9, 0xFB, 0xFE, 0x7F, 0x7D, 0x7C, 0x7D, 0x7E, 0x7E, 0x7E, 0x7D, 0x7A, 0x7A, 0x7A, +0x7A, 0x79, 0x79, 0x79, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7A, 0x7A, 0x7A, 0x79, 0x77, 0x76, +0x77, 0x77, 0x77, 0x77, 0x78, 0x7A, 0x7B, 0x7D, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0x7E, 0x7B, +0x78, 0x78, 0x79, 0x79, 0x7A, 0x7B, 0x7C, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0xFF, 0x7F, 0x7F, 0xFE, +0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFC, 0xFE, 0x7F, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, +0x7B, 0x7A, 0x7B, 0x7B, 0x7B, 0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x76, 0x77, 0x78, 0x77, 0x79, +0x7A, 0x7A, 0x7B, 0x7C, 0x7C, 0x7B, 0x7A, 0x7B, 0x7D, 0x7B, 0x7B, 0x7D, 0x7C, 0x7D, 0xFF, 0x7E, +0xFF, 0xFD, 0xFB, 0xF9, 0xF8, 0xF9, 0xF9, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF3, 0xF5, 0xF6, 0xF6, +0xF7, 0xF9, 0xFB, 0xFB, 0xFA, 0xFA, 0xFB, 0xFC, 0xFB, 0xFD, 0xFD, 0xFB, 0xFB, 0xFB, 0xFA, 0xF8, +0xF8, 0xF9, 0xFB, 0xFD, 0x7E, 0x7B, 0x78, 0x78, 0x77, 0x77, 0x77, 0x76, 0x76, 0x76, 0x75, 0x75, +0x73, 0x71, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6E, 0x6E, 0x6F, 0x6F, 0x6E, 0x6E, 0x6E, 0x6D, +0x6D, 0x6E, 0x6D, 0x6D, 0x6E, 0x6F, 0x70, 0x72, 0x73, 0x74, 0x75, 0x79, 0x7C, 0x7D, 0x7E, 0xFF, +0xFF, 0xFF, 0xFD, 0xFC, 0xFA, 0xF9, 0xFB, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, 0x7E, 0x7B, 0x7A, +0x79, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7C, 0x7C, 0x7B, 0x79, 0x79, 0x78, 0x78, 0x79, 0x7A, 0x7C, +0x7E, 0x7E, 0x7E, 0x7C, 0x7B, 0x7A, 0x7A, 0x7A, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7D, 0xFF, 0xFD, +0xFC, 0xFB, 0xFA, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFE, 0xFE, 0xFC, 0xFB, 0xFB, 0xFB, +0xFB, 0xFC, 0xFD, 0xFD, 0xFD, 0xFE, 0xFD, 0xFC, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF9, 0xF8, 0xF7, +0xF6, 0xF7, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0x7E, 0x7D, 0x7C, 0x7C, +0x7C, 0x7A, 0x7A, 0x79, 0x79, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, +0x7C, 0x7C, 0x7D, 0x7D, 0x7C, 0x7B, 0x78, 0x77, 0x76, 0x75, 0x75, 0x75, 0x76, 0x76, 0x75, 0x76, +0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7B, 0x7D, 0x7D, 0x7E, 0x7F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, +0xFD, 0xFB, 0xFB, 0xFB, 0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFF, 0x7F, 0x7E, 0x7C, 0x7C, 0x7B, +0x7A, 0x7A, 0x7A, 0x78, 0x78, 0x77, 0x77, 0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7B, 0x7C, 0x7D, 0x7B, +0x7A, 0x7B, 0x7C, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7A, 0x7B, 0x7F, 0xFD, 0x7D, 0x79, 0xFE, +0xFA, 0xFE, 0xFC, 0xF8, 0xFC, 0xFF, 0xFF, 0xFE, 0xFB, 0xFC, 0x7F, 0xFF, 0x7F, 0x7C, 0x7D, 0x7D, +0x7E, 0x7E, 0x7B, 0x7A, 0x7A, 0x7B, 0x7C, 0x7E, 0x7E, 0x7C, 0x7B, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, +0x7D, 0x7E, 0x7F, 0x7E, 0x7F, 0xFF, 0xFE, 0xFD, 0xFE, 0xFF, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7E, +0x7D, 0x7D, 0x7C, 0x7B, 0x7A, 0x79, 0x7A, 0x7A, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x75, 0x74, 0x73, +0x74, 0x75, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x77, 0x76, 0x77, 0x77, 0x79, 0x7B, 0x7C, 0x7D, +0x7D, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFE, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFB, 0xFC, 0xFD, +0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0x7F, 0x7D, 0x7E, +0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, +0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, +0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F, 0x7E, +0x7D, 0x7C, 0x7B, 0x7B, 0x79, 0x77, 0x77, 0x78, 0x76, 0x76, 0x78, 0x7A, 0x78, 0x78, 0x78, 0x77, +0x77, 0x76, 0x75, 0x76, 0x75, 0x75, 0x77, 0x77, 0x76, 0x74, 0x74, 0x75, 0x76, 0x75, 0x76, 0x76, +0x75, 0x76, 0x76, 0x75, 0x74, 0x73, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x78, 0x7A, 0x7A, 0x7B, +0x7C, 0x7B, 0x7D, 0x7E, 0x7D, 0x7D, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFD, 0xFD, 0xFD, 0xFF, 0x7F, 0xFE, 0xFF, 0xFF, 0xFE, +0xFE, 0xFE, 0x7F, 0x7E, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0xFD, 0xFD, +0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0xFF, 0xFE, 0xFC, 0xFA, 0xF9, 0xF8, 0xF8, 0xF8, 0xF9, 0xFA, +0xFD, 0xFE, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF, 0x7F, 0xFF, 0x7E, 0x7D, 0x7B, 0x79, 0x79, 0x7B, 0x7C, +0x7C, 0x7D, 0x7E, 0x7F, 0x7E, 0x7D, 0x7D, 0x7B, 0x7B, 0x7A, 0x77, 0x78, 0x75, 0x75, 0x76, 0x76, +0x78, 0x75, 0x76, 0x77, 0x73, 0x75, 0x73, 0x77, 0x76, 0x73, 0x78, 0x73, 0x78, 0x77, 0x75, 0x7B, +0x70, 0x7E, 0x68, 0x5B, 0xE4, 0xD9, 0x77, 0x66, 0x5F, 0x6B, 0xEB, 0x78, 0x6B, 0xEC, 0xE9, 0x67, +0x5A, 0x62, 0xF8, 0xE2, 0xF1, 0x6B, 0x6F, 0x72, 0x6E, 0x6D, 0x72, 0xEF, 0xE7, 0xF7, 0x6D, 0x6A, +0x70, 0x7B, 0xFC, 0xF5, 0xED, 0xEF, 0x7D, 0x71, 0x70, 0xFF, 0xEE, 0xEF, 0xF8, 0xFC, 0x7B, 0x73, +0x6F, 0x71, 0x7E, 0xF2, 0xF2, 0x76, 0x67, 0x68, 0x78, 0xF7, 0xF5, 0xFC, 0x7E, 0x7B, 0x73, 0x70, +0x79, 0xF4, 0xEB, 0xEC, 0xF8, 0x7C, 0x7B, 0xFF, 0xF6, 0xEF, 0xEF, 0xF4, 0xF8, 0xFE, 0x7D, 0xFE, +0xF8, 0xF4, 0xF4, 0xF9, 0xFE, 0x7E, 0x7C, 0x7C, 0xFD, 0xF8, 0xF8, 0xFD, 0x7D, 0x7A, 0x79, 0x7C, +0xFF, 0xFD, 0xFB, 0xFC, 0x7D, 0x7B, 0x7D, 0x7F, 0xFD, 0xFD, 0xFF, 0x7E, 0x7C, 0x7A, 0x79, 0x7C, +0x7E, 0x7D, 0x7D, 0x7B, 0x78, 0x79, 0x7B, 0x7A, 0x78, 0x79, 0x77, 0x76, 0x78, 0x71, 0x70, 0x78, +0x7E, 0xF6, 0xFE, 0x67, 0x68, 0xF4, 0xEF, 0x74, 0x69, 0x6A, 0xFF, 0x7E, 0x6E, 0xF2, 0xFB, 0x69, +0x66, 0x69, 0xDD, 0xD9, 0x71, 0x7B, 0x74, 0x5F, 0x69, 0x61, 0x6A, 0xE6, 0xEC, 0x66, 0x55, 0x50, +0x5C, 0xFB, 0xED, 0xF5, 0xFA, 0x6F, 0x6E, 0x7B, 0xE8, 0xCF, 0xC7, 0xC6, 0xCA, 0xCB, 0xDB, 0x3A, +0x2E, 0x49, 0xBD, 0xB7, 0xC6, 0x4D, 0x41, 0x4B, 0x49, 0x4D, 0xD9, 0xBF, 0xC2, 0xFD, 0x3F, 0x3D, +0x51, 0xDF, 0xCD, 0xCC, 0xD5, 0xF4, 0x50, 0x46, 0x4D, 0xF0, 0xCB, 0xCA, 0xDA, 0x61, 0x51, 0x52, +0x5B, 0xE6, 0xD0, 0xCE, 0xDC, 0x5D, 0x53, 0x51, 0x6A, 0xF9, 0x48, 0x67, 0xC2, 0xE0, 0x5F, 0x5B, +0x4C, 0x71, 0xFA, 0x6E, 0xCE, 0xCF, 0x7A, 0x64, 0x79, 0xD8, 0xCE, 0xD1, 0xCD, 0xC3, 0xC5, 0xCD, +0xC7, 0xDC, 0x42, 0x3E, 0x53, 0xD2, 0xC0, 0xDE, 0x44, 0x48, 0x58, 0x53, 0x59, 0xFA, 0xD9, 0xD6, +0x5E, 0x43, 0x47, 0x61, 0xE1, 0xD8, 0xEA, 0x64, 0x46, 0x3C, 0x51, 0x7F, 0x77, 0xD0, 0xF6, 0x4B, +0x4B, 0x4A, 0x6F, 0xCA, 0xC6, 0xC9, 0xC7, 0xC2, 0xC8, 0xBD, 0xB0, 0xAB, 0xBB, 0x1F, 0x19, 0x5F, +0xA3, 0xA5, 0xBA, 0x30, 0x30, 0x47, 0x31, 0x37, 0xBC, 0xAB, 0xB8, 0x3D, 0x2B, 0x30, 0x4E, 0xBD, +0xB4, 0xC4, 0x5D, 0x3F, 0x38, 0x41, 0x6E, 0xC8, 0xC1, 0xDC, 0x4B, 0x3B, 0x3F, 0xE5, 0xBF, 0xBC, +0xC8, 0xE5, 0xDE, 0xC0, 0xAE, 0xAD, 0xAF, 0xC1, 0x20, 0x1A, 0x63, 0xA8, 0xA7, 0xB5, 0x36, 0x2E, +0x3D, 0x32, 0x3F, 0xBD, 0xB1, 0xC1, 0x39, 0x2E, 0x42, 0xE8, 0xCA, 0xC3, 0xDB, 0x5F, 0x47, 0x39, +0x47, 0xD5, 0xCB, 0xD1, 0x70, 0x4B, 0x4D, 0xF0, 0xC1, 0xB3, 0xAE, 0xB4, 0xBE, 0xB8, 0xB8, 0x34, +0x1C, 0x27, 0xB6, 0xA3, 0xAC, 0x4F, 0x2C, 0x32, 0x3B, 0x3E, 0xEB, 0xB9, 0xBB, 0x45, 0x2E, 0x39, +0x60, 0xC3, 0xBB, 0xD8, 0x4D, 0x45, 0x41, 0x57, 0xD6, 0xDE, 0x79, 0x5D, 0x50, 0x5B, 0xDB, 0xBE, +0xB4, 0xB3, 0xBC, 0xC4, 0xB6, 0xAC, 0xC2, 0x26, 0x1A, 0x30, 0xAB, 0xA9, 0xBB, 0x61, 0x33, 0x30, +0x36, 0x39, 0xCF, 0xB9, 0x64, 0x41, 0x42, 0x3E, 0x5E, 0xD3, 0xCF, 0xC4, 0xEC, 0x47, 0x56, 0x5C, +0x50, 0x57, 0x62, 0xDF, 0xD2, 0xD3, 0xC4, 0xB6, 0xB3, 0xBA, 0xB8, 0xAD, 0xAF, 0x3C, 0x1C, 0x1E, +0xCB, 0xA1, 0xA9, 0xFE, 0x2D, 0x2C, 0x39, 0x3D, 0x64, 0xBE, 0xD5, 0x4B, 0x42, 0x3A, 0x40, 0xEC, +0xC4, 0xBE, 0xD1, 0x4A, 0x3E, 0x47, 0x62, 0xE4, 0xD7, 0xD6, 0xDC, 0xDE, 0xCD, 0xB6, 0xAD, 0xAE, +0xAC, 0xAD, 0xE6, 0x22, 0x19, 0x2E, 0xA6, 0x9E, 0xB2, 0x39, 0x28, 0x2D, 0x39, 0x4E, 0xC2, 0xBE, +0x5B, 0x3E, 0x3B, 0x3C, 0x58, 0xC6, 0xBD, 0xC7, 0x60, 0x3F, 0x3F, 0x51, 0xED, 0xCF, 0xC8, 0xD1, +0xE0, 0xD9, 0xBC, 0xAC, 0xAB, 0xAB, 0xA9, 0xC2, 0x25, 0x17, 0x22, 0xAE, 0x9D, 0xA9, 0x47, 0x26, +0x29, 0x34, 0x47, 0xC5, 0xBB, 0x77, 0x3F, 0x3A, 0x38, 0x4E, 0xC6, 0xBC, 0xBE, 0xE6, 0x3D, 0x39, +0x43, 0x67, 0xCE, 0xC7, 0xD6, 0xED, 0xD9, 0xBD, 0xAB, 0xAB, 0xAE, 0xA9, 0xB4, 0x30, 0x1A, 0x1B, +0xD5, 0x9D, 0xA3, 0xDA, 0x2A, 0x26, 0x31, 0x3C, 0x6C, 0xBF, 0xCE, 0x5E, 0x42, 0x32, 0x3C, 0xD6, +0xBC, 0xB8, 0xCC, 0x3F, 0x37, 0x3B, 0x51, 0xCC, 0xC1, 0xCC, 0xE2, 0xDE, 0xBD, 0xAC, 0xAA, 0xA9, +0xA7, 0xC5, 0x26, 0x18, 0x1F, 0xB7, 0x9D, 0xA6, 0xDF, 0x2B, 0x26, 0x2D, 0x37, 0xEB, 0xBA, 0xCB, +0x5E, 0x3C, 0x30, 0x44, 0xC9, 0xBB, 0xB8, 0xCF, 0x3D, 0x34, 0x3B, 0x5F, 0xC1, 0xBE, 0xCE, 0xD6, +0xCE, 0xB8, 0xAB, 0xAA, 0xA4, 0xA8, 0x42, 0x1C, 0x18, 0x31, 0xA4, 0x9F, 0xAF, 0x48, 0x29, 0x28, +0x2C, 0x37, 0xCC, 0xBA, 0xC7, 0x57, 0x30, 0x31, 0x5A, 0xC6, 0xB6, 0xB7, 0x5F, 0x38, 0x34, 0x3D, +0xE2, 0xBD, 0xBD, 0xC7, 0xD2, 0xCA, 0xB2, 0xAB, 0xA7, 0xA2, 0xB3, 0x29, 0x18, 0x1C, 0xD2, 0x9F, +0xA5, 0xBC, 0x3A, 0x2A, 0x29, 0x2A, 0x3E, 0xC3, 0xBB, 0xC1, 0x43, 0x2D, 0x3A, 0xF5, 0xC1, 0xB5, +0xBE, 0x4F, 0x36, 0x32, 0x3F, 0xD2, 0xBB, 0xBA, 0xBE, 0xC5, 0xBF, 0xB1, 0xAB, 0xA4, 0xA7, 0x75, +0x1F, 0x18, 0x27, 0xAE, 0xA2, 0xAD, 0xD6, 0x36, 0x2B, 0x28, 0x2C, 0x4E, 0xC2, 0xB8, 0xC9, 0x35, +0x2F, 0x42, 0x71, 0xBA, 0xB4, 0xD2, 0x44, 0x34, 0x34, 0x4C, 0xCD, 0xBE, 0xBB, 0xB9, 0xBC, 0xB3, +0xAE, 0xAB, 0xA3, 0xB0, 0x2C, 0x1A, 0x1C, 0x5F, 0xA4, 0xA9, 0xBA, 0x6F, 0x34, 0x28, 0x26, 0x32, +0x77, 0xB9, 0xB3, 0x5E, 0x32, 0x38, 0x40, 0xD8, 0xB6, 0xBE, 0xF4, 0x40, 0x34, 0x3A, 0x5B, 0xC7, +0xB7, 0xB4, 0xB7, 0xB4, 0xB2, 0xAE, 0xA5, 0xAB, 0x3B, 0x1D, 0x1B, 0x34, 0xAE, 0xAA, 0xB4, 0xC7, +0x42, 0x2C, 0x25, 0x2B, 0x43, 0xCF, 0xB5, 0xBE, 0x3F, 0x38, 0x3D, 0x4D, 0xC4, 0xBD, 0xCF, 0x5D, +0x3E, 0x38, 0x43, 0xE4, 0xBF, 0xB4, 0xAF, 0xB0, 0xAF, 0xAE, 0xA8, 0xA7, 0xF6, 0x21, 0x1A, 0x27, +0xBA, 0xAC, 0xB5, 0xB9, 0xE2, 0x36, 0x26, 0x25, 0x33, 0x4E, 0xBA, 0xB2, 0x64, 0x41, 0x3F, 0x3D, +0xE6, 0xC6, 0xCD, 0xD4, 0x50, 0x3D, 0x3C, 0x48, 0xD4, 0xB9, 0xAF, 0xAD, 0xAE, 0xAE, 0xAA, 0xA7, +0xC0, 0x2A, 0x1B, 0x1F, 0x65, 0xAF, 0xB5, 0xB5, 0xBF, 0x4C, 0x2C, 0x23, 0x2A, 0x37, 0xE6, 0xAF, +0xBC, 0x5B, 0x4B, 0x3B, 0x49, 0xCF, 0xCE, 0xD0, 0xDE, 0x4F, 0x40, 0x3F, 0x57, 0xC7, 0xB3, 0xAA, +0xA9, 0xAC, 0xAE, 0xA9, 0xAE, 0x3B, 0x1E, 0x1E, 0x37, 0xBA, 0xB7, 0xB7, 0xB5, 0xC9, 0x3A, 0x26, +0x27, 0x2E, 0x37, 0xC1, 0xB1, 0xCA, 0xE8, 0x49, 0x3E, 0x62, 0x78, 0xEF, 0xD7, 0x69, 0x4F, 0x44, +0x48, 0xFB, 0xC5, 0xAF, 0xA9, 0xA8, 0xAA, 0xAB, 0xAA, 0xCF, 0x26, 0x1C, 0x22, 0x4A, 0xBD, 0xBB, +0xAF, 0xB1, 0xD7, 0x31, 0x25, 0x27, 0x28, 0x38, 0xB9, 0xB6, 0xC3, 0xCB, 0x4B, 0x45, 0x4E, 0x47, +0x62, 0xE0, 0xF9, 0x5C, 0x4C, 0x5C, 0xE4, 0xBF, 0xAD, 0xA8, 0xA5, 0xA7, 0xA9, 0xB2, 0x36, 0x20, +0x1E, 0x29, 0x56, 0xCC, 0xB8, 0xAC, 0xB5, 0xFA, 0x2F, 0x28, 0x25, 0x25, 0x45, 0xBE, 0xBF, 0xB8, +0xC5, 0x5F, 0x54, 0x41, 0x43, 0x4F, 0x56, 0x75, 0x70, 0xFE, 0xE2, 0xD3, 0xB9, 0xAD, 0xA8, 0xA6, +0xA6, 0xA8, 0xCA, 0x2D, 0x22, 0x21, 0x2E, 0x47, 0x77, 0xB4, 0xAD, 0xB7, 0xDC, 0x39, 0x2D, 0x24, +0x28, 0x40, 0x5A, 0xC7, 0xB5, 0xBD, 0xC0, 0xDB, 0x4E, 0x4C, 0x40, 0x44, 0x4E, 0x56, 0xDA, 0xCC, +0xC0, 0xB1, 0xAC, 0xA7, 0xA5, 0xA6, 0xAD, 0x4F, 0x2A, 0x23, 0x25, 0x2F, 0x39, 0x6C, 0xB6, 0xB1, +0xB7, 0xD6, 0x43, 0x2F, 0x26, 0x2C, 0x35, 0x3A, 0xDF, 0xC7, 0xBE, 0xBA, 0xCB, 0xD4, 0x6E, 0x48, +0x44, 0x3E, 0x4A, 0x6C, 0xD6, 0xB9, 0xB0, 0xA8, 0xA4, 0xA5, 0xA4, 0xB5, 0x42, 0x2C, 0x23, 0x27, +0x2B, 0x2E, 0x55, 0xC2, 0xB9, 0xB6, 0xC5, 0xF4, 0x3C, 0x32, 0x34, 0x2F, 0x37, 0x4A, 0x5B, 0xCF, +0xC9, 0xC6, 0xC0, 0xCA, 0xCF, 0xF4, 0x52, 0x51, 0x4E, 0xF8, 0xC4, 0xB7, 0xAD, 0xA9, 0xA6, 0xA7, +0xB8, 0x5D, 0x38, 0x2F, 0x2E, 0x2B, 0x2C, 0x35, 0x42, 0x5D, 0x7D, 0xE3, 0xDF, 0x6F, 0x73, 0x5A, +0x4D, 0x55, 0x4F, 0x4F, 0x58, 0x5C, 0x6E, 0xFA, 0xE2, 0xD4, 0xD4, 0xD6, 0xD9, 0xD6, 0xCC, 0xC6, +0xBF, 0xBB, 0xB8, 0xB6, 0xBC, 0xC9, 0xDC, 0x60, 0x59, 0x53, 0x4A, 0x49, 0x46, 0x45, 0x46, 0x43, +0x45, 0x45, 0x49, 0x56, 0x5A, 0x5D, 0x6E, 0x72, 0x7C, 0x74, 0x6D, 0x7A, 0x7A, 0xFC, 0xED, 0xE8, +0xEB, 0xFF, 0xFD, 0xEC, 0xDE, 0xD2, 0xCC, 0xC7, 0xC5, 0xCA, 0xD2, 0xD9, 0xDB, 0xD4, 0xCF, 0xD1, +0xD9, 0xE7, 0xFC, 0x6E, 0x60, 0x57, 0x50, 0x4C, 0x49, 0x47, 0x46, 0x45, 0x47, 0x4A, 0x4D, 0x4F, +0x4F, 0x53, 0x5A, 0x5F, 0x62, 0x60, 0x62, 0x6B, 0x77, 0xEC, 0xD8, 0xCC, 0xC7, 0xC7, 0xCB, 0xCF, +0xD3, 0xD3, 0xCE, 0xCB, 0xCA, 0xCC, 0xCF, 0xD4, 0xD9, 0xDF, 0xEB, 0xFE, 0x6A, 0x58, 0x4D, 0x46, +0x43, 0x44, 0x46, 0x48, 0x4A, 0x4C, 0x4E, 0x52, 0x54, 0x55, 0x54, 0x57, 0x5C, 0x61, 0x72, 0xE5, +0xD4, 0xCC, 0xCB, 0xCD, 0xD0, 0xD4, 0xD3, 0xD1, 0xD0, 0xCF, 0xD1, 0xD2, 0xD4, 0xD6, 0xD9, 0xDB, +0xDB, 0xDD, 0xEC, 0x6B, 0x59, 0x53, 0x54, 0x57, 0x59, 0x58, 0x57, 0x5A, 0x5C, 0x5A, 0x55, 0x52, +0x52, 0x51, 0x51, 0x57, 0x61, 0xFC, 0xE3, 0xDF, 0xE0, 0xE4, 0xE3, 0xE0, 0xDF, 0xDE, 0xDD, 0xDE, +0xDD, 0xDF, 0xE0, 0xDF, 0xDC, 0xD9, 0xDC, 0xE7, 0x7E, 0x6B, 0x6D, 0x7C, 0xEE, 0xE6, 0xE7, 0xEA, +0xEC, 0xF5, 0x7A, 0x6A, 0x61, 0x5A, 0x51, 0x4F, 0x50, 0x55, 0x5D, 0x62, 0x63, 0x60, 0x5F, 0x61, +0x65, 0x68, 0x6D, 0x75, 0xFA, 0xF1, 0xF1, 0xEE, 0xE6, 0xDD, 0xDB, 0xDE, 0xEB, 0x79, 0x6B, 0x6F, +0xF7, 0xE2, 0xDC, 0xDB, 0xDA, 0xDA, 0xDC, 0xDD, 0xDE, 0xE0, 0xF1, 0x6B, 0x5F, 0x5F, 0x66, 0x6B, +0x6A, 0x65, 0x5E, 0x5B, 0x5A, 0x59, 0x5A, 0x5C, 0x5E, 0x5E, 0x5E, 0x5E, 0x68, 0xFC, 0xE8, 0xE5, +0xEB, 0xFB, 0x71, 0x6D, 0x78, 0xEE, 0xE2, 0xDE, 0xDD, 0xDC, 0xDE, 0xDF, 0xDC, 0xD9, 0xD9, 0xE0, +0xED, 0xF0, 0xEC, 0xE4, 0xE2, 0xE6, 0xEE, 0x7C, 0x6D, 0x65, 0x5E, 0x5E, 0x62, 0x64, 0x61, 0x5C, +0x5B, 0x5F, 0x70, 0xF7, 0xF8, 0x78, 0x6B, 0x65, 0x61, 0x69, 0xFA, 0xE9, 0xE5, 0xE2, 0xE5, 0xEA, +0xED, 0xEB, 0xE5, 0xE5, 0xED, 0xF9, 0xFC, 0xEF, 0xE3, 0xDE, 0xDE, 0xE1, 0xE9, 0xF4, 0x74, 0x6A, +0x6B, 0x6F, 0x6F, 0x67, 0x5E, 0x5C, 0x5F, 0x6A, 0x6F, 0x6C, 0x66, 0x5F, 0x5D, 0x5E, 0x64, 0x6F, +0xFE, 0xEF, 0xEC, 0xF2, 0xFD, 0x7E, 0xFB, 0xF6, 0xFF, 0x6F, 0x6A, 0x6A, 0x74, 0xF4, 0xE9, 0xE4, +0xE7, 0xEE, 0x7D, 0x6A, 0x69, 0x6F, 0xFE, 0xF7, 0x7B, 0x6B, 0x68, 0x6E, 0x79, 0x78, 0x6F, 0x69, +0x62, 0x5F, 0x60, 0x66, 0x72, 0xF6, 0xEB, 0xEC, 0xF3, 0xFC, 0xFA, 0xF2, 0xF2, 0xFE, 0x70, 0x69, +0x69, 0x71, 0xF7, 0xE8, 0xE3, 0xE4, 0xED, 0x76, 0x69, 0x6A, 0x6F, 0x7E, 0xFB, 0x7D, 0x75, 0x73, +0x7C, 0xFB, 0xFC, 0x7D, 0x73, 0x6D, 0x6A, 0x69, 0x6F, 0xFD, 0xEC, 0xE8, 0xED, 0xF6, 0xF7, 0xF1, +0xED, 0xEF, 0xF6, 0x7E, 0x76, 0x78, 0xFB, 0xEC, 0xE2, 0xDE, 0xE0, 0xEB, 0xFE, 0x73, 0x73, 0x7E, +0xFA, 0xFC, 0xFF, 0x7B, 0x7D, 0xFC, 0xFE, 0x7B, 0x74, 0x6F, 0x6D, 0x6C, 0x6E, 0x77, 0xFA, 0xEE, +0xED, 0xF6, 0x7A, 0x73, 0x77, 0x7B, 0x7C, 0x78, 0x6E, 0x6B, 0x6F, 0x7C, 0xEF, 0xE7, 0xE2, 0xE6, +0xF2, 0x7A, 0x71, 0x75, 0x7D, 0xFE, 0xFD, 0xFD, 0xFB, 0xF5, 0xF7, 0xFE, 0x77, 0x6E, 0x6A, 0x67, +0x67, 0x6C, 0x76, 0xF7, 0xEE, 0xEF, 0xFA, 0x7B, 0x78, 0x7B, 0xFF, 0x7F, 0x78, 0x6F, 0x6C, 0x6C, +0x72, 0xFE, 0xEF, 0xEC, 0xEF, 0xFD, 0x76, 0x70, 0x71, 0x75, 0x7A, 0x7E, 0x7E, 0xFF, 0xFC, 0xFD, +0x7C, 0x73, 0x6D, 0x69, 0x65, 0x63, 0x65, 0x6B, 0x79, 0xFB, 0xFD, 0x78, 0x70, 0x70, 0x74, 0x79, +0x7C, 0x7B, 0x79, 0x77, 0x78, 0x7F, 0xF4, 0xED, 0xED, 0xF2, 0xFB, 0x7A, 0x74, 0x74, 0x76, 0x7A, +0x7F, 0xFB, 0xF5, 0xF4, 0xF6, 0xFA, 0x7D, 0x75, 0x6E, 0x6B, 0x6B, 0x6F, 0x7D, 0xF3, 0xEF, 0xF4, +0x7E, 0x78, 0x7A, 0xFF, 0xFA, 0xF7, 0xF5, 0xF4, 0xF4, 0xF1, 0xED, 0xE9, 0xE6, 0xE8, 0xEE, 0xF8, +0x7F, 0x7C, 0x7A, 0x7A, 0x7B, 0x7D, 0xFD, 0xF8, 0xF8, 0xF9, 0xFC, 0x7D, 0x74, 0x6C, 0x69, 0x69, +0x6C, 0x76, 0xFA, 0xF6, 0xFD, 0x78, 0x71, 0x6F, 0x6F, 0x6F, 0x71, 0x74, 0x77, 0x7C, 0xFD, 0xF5, +0xEC, 0xE9, 0xEA, 0xEF, 0xFB, 0x7A, 0x74, 0x73, 0x73, 0x73, 0x73, 0x78, 0x7E, 0xFE, 0xFD, 0xFE, +0x7B, 0x74, 0x6D, 0x68, 0x66, 0x69, 0x6F, 0x7B, 0x7F, 0x7C, 0x77, 0x73, 0x72, 0x6F, 0x6D, 0x6D, +0x6D, 0x6D, 0x6E, 0x72, 0x7A, 0xFA, 0xF1, 0xEE, 0xEF, 0xF6, 0xFD, 0x7D, 0x7B, 0x7A, 0x78, 0x78, +0x79, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7D, 0x77, 0x70, 0x6E, 0x71, 0x79, 0x7E, 0x7E, 0x7E, 0x7D, +0x7E, 0x7D, 0x7C, 0x7A, 0x78, 0x76, 0x74, 0x73, 0x75, 0x7A, 0xFC, 0xF6, 0xF4, 0xF4, 0xF6, 0xF6, +0xF6, 0xF9, 0xF9, 0xF7, 0xF5, 0xF3, 0xF1, 0xF1, 0xF3, 0xF5, 0xF7, 0xFA, 0x7B, 0x6F, 0x6D, 0x6D, +0x6F, 0x6F, 0x6E, 0x6E, 0x70, 0x70, 0x71, 0x74, 0x78, 0x7C, 0x7D, 0x7E, 0x7D, 0xFF, 0xFB, 0xF7, +0xF4, 0xF4, 0xF6, 0xF8, 0xFB, 0xFD, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x7F, 0xFF, 0xFC, 0xF8, +0xF9, 0xFD, 0x7D, 0x7C, 0x7F, 0xFE, 0x7E, 0x7C, 0x7A, 0x79, 0x77, 0x75, 0x75, 0x76, 0x79, 0x76, +0x71, 0x6F, 0x6F, 0x71, 0x76, 0x78, 0x78, 0x78, 0x7B, 0x7D, 0x7A, 0x77, 0x76, 0x76, 0x78, 0x79, +0x78, 0x79, 0x7C, 0xFD, 0xF4, 0xF1, 0xF2, 0xF5, 0xF9, 0xFB, 0x7F, 0x79, 0x76, 0x76, 0x78, 0x77, +0x77, 0x7B, 0xFF, 0xFA, 0xF6, 0xF6, 0xF7, 0xF6, 0xF6, 0xF7, 0xF9, 0xFA, 0xF9, 0xF6, 0xF6, 0xF9, +0xFB, 0xFE, 0x7F, 0x7E, 0x7C, 0x79, 0x7A, 0xFE, 0xF5, 0xEF, 0xEE, 0xEF, 0xF1, 0xF3, 0xF4, 0xFB, +0x79, 0x73, 0x72, 0x6E, 0x6B, 0x68, 0x68, 0x6C, 0x73, 0x7A, 0x7C, 0x7B, 0x7B, 0x7D, 0x7D, 0xFD, +0xF6, 0xF0, 0xEE, 0xEF, 0xF6, 0xFB, 0xFF, 0x7D, 0x7F, 0x7D, 0x7A, 0x7A, 0x7B, 0x7C, 0x7D, 0x7B, +0x7A, 0xFF, 0xFD, 0xFF, 0x7E, 0x7A, 0x75, 0x74, 0x71, 0x6F, 0x6F, 0x70, 0x77, 0x7A, 0x78, 0x75, +0x72, 0x75, 0x73, 0x6F, 0x6F, 0x70, 0x75, 0x77, 0x76, 0x78, 0x75, 0x78, 0x7B, 0x7B, 0x7F, 0x7B, +0x7E, 0x7F, 0x7E, 0x7E, 0x77, 0x79, 0x73, 0x71, 0x71, 0x6B, 0x6F, 0x60, 0x5B, 0x6F, 0x72, 0x69, +0x71, 0x72, 0x7E, 0xFC, 0xF7, 0xEE, 0xF4, 0xFB, 0x7E, 0x7E, 0xFC, 0xF9, 0xFD, 0x7F, 0xFC, 0x72, +0x78, 0x64, 0x53, 0x71, 0xDF, 0xF0, 0xEB, 0xE9, 0xF0, 0x7C, 0x7B, 0xEA, 0xF6, 0x7E, 0x79, 0x6C, +0x79, 0x5F, 0x5B, 0xEC, 0xE2, 0xEF, 0xED, 0xF1, 0xF7, 0xFE, 0x7C, 0xFE, 0x75, 0x6E, 0x75, 0x79, +0x7A, 0x72, 0x6E, 0x75, 0x7A, 0x7B, 0x77, 0x77, 0xFE, 0xF8, 0xF6, 0xF5, 0xEB, 0xE9, 0xEC, 0xE8, +0xEE, 0xEC, 0xED, 0x77, 0xFD, 0x61, 0x51, 0x66, 0xE7, 0xDC, 0xD8, 0xD3, 0xD3, 0x6C, 0x52, 0x63, +0x6F, 0x73, 0x7F, 0xFB, 0xF4, 0x79, 0x7B, 0xF1, 0xED, 0xEC, 0xFA, 0x76, 0x7A, 0x74, 0x6B, 0x6B, +0x75, 0x7C, 0x7C, 0x79, 0xFE, 0xF6, 0xFE, 0x7A, 0x71, 0x75, 0xFF, 0x70, 0x6D, 0x6E, 0x69, 0x66, +0x5E, 0x62, 0xFA, 0xEA, 0xE5, 0xE0, 0xE1, 0xE5, 0xEE, 0xF1, 0xE5, 0xE4, 0xEA, 0xE8, 0xE7, 0xE7, +0xE3, 0xE7, 0xE8, 0xE8, 0xEF, 0xF7, 0x78, 0x6E, 0x6D, 0x60, 0x5A, 0x57, 0x54, 0x53, 0x51, 0x50, +0x52, 0x54, 0x57, 0x59, 0x5D, 0x67, 0x70, 0x7B, 0xF5, 0xE5, 0xDA, 0xD5, 0xD2, 0xCD, 0xC4, 0xBE, +0xBB, 0xB7, 0xB5, 0xBB, 0xCB, 0xDE, 0xE9, 0x56, 0x3F, 0x3A, 0x39, 0x3B, 0x3B, 0x3A, 0x3F, 0x4E, +0x5D, 0x69, 0xF2, 0xDD, 0xD9, 0xE2, 0xF3, 0xF4, 0x72, 0x5C, 0x53, 0x4E, 0x4D, 0x4E, 0x4E, 0x4F, +0x58, 0x7E, 0xDA, 0xCB, 0xBF, 0xBA, 0xB7, 0xB4, 0xB0, 0xAE, 0xB2, 0xBE, 0xD7, 0x70, 0x4E, 0x3B, +0x32, 0x30, 0x33, 0x35, 0x35, 0x3A, 0x49, 0x5F, 0x7E, 0xEF, 0xE1, 0xD2, 0xD1, 0xE6, 0xEF, 0xF7, +0x5F, 0x4F, 0x48, 0x48, 0x4B, 0x48, 0x4B, 0x55, 0x72, 0xE1, 0xD2, 0xC2, 0xBA, 0xB6, 0xB3, 0xB0, +0xAD, 0xAE, 0xBA, 0xD0, 0x64, 0x4C, 0x3C, 0x30, 0x2E, 0x2F, 0x33, 0x35, 0x3A, 0x47, 0x6C, 0xE6, +0xEB, 0xD0, 0xC6, 0xCC, 0xD6, 0xE3, 0xEF, 0x69, 0x4E, 0x4B, 0x4B, 0x47, 0x46, 0x48, 0x4F, 0x61, +0xFE, 0xD1, 0xBE, 0xB8, 0xB5, 0xB2, 0xAF, 0xAD, 0xAE, 0xBA, 0xCD, 0x5F, 0x47, 0x3B, 0x2F, 0x2D, +0x2E, 0x30, 0x35, 0x3B, 0x47, 0x6C, 0xF4, 0xE2, 0xCB, 0xC7, 0xCD, 0xD7, 0xDD, 0xE3, 0x6E, 0x4E, +0x4A, 0x4B, 0x48, 0x47, 0x4A, 0x5B, 0xE4, 0xD3, 0xC0, 0xB7, 0xB3, 0xB0, 0xAF, 0xAC, 0xAC, 0xB3, +0xC4, 0x7D, 0x4C, 0x3E, 0x30, 0x2C, 0x2E, 0x31, 0x35, 0x3A, 0x45, 0x62, 0xFA, 0xEB, 0xCE, 0xCA, +0xD2, 0xDA, 0xE7, 0xE9, 0x6D, 0x4C, 0x49, 0x49, 0x47, 0x48, 0x4C, 0x68, 0xD7, 0xCA, 0xBC, 0xB5, +0xB2, 0xB0, 0xAE, 0xAC, 0xAE, 0xB9, 0xCE, 0x5D, 0x45, 0x39, 0x2F, 0x2D, 0x2E, 0x31, 0x36, 0x3B, +0x47, 0x5C, 0x6E, 0xE4, 0xD3, 0xD3, 0xD4, 0xDA, 0xDE, 0xEB, 0x5B, 0x4D, 0x4A, 0x47, 0x48, 0x49, +0x54, 0xEB, 0xCE, 0xBE, 0xB8, 0xB7, 0xB3, 0xB3, 0xB5, 0xB3, 0xBB, 0xC9, 0xC8, 0xED, 0x47, 0x42, +0x3A, 0x36, 0x37, 0x34, 0x39, 0x3E, 0x3F, 0x47, 0x4E, 0x65, 0xDE, 0xEC, 0xDC, 0xCE, 0xD9, 0xEB, +0x5F, 0x54, 0x51, 0x47, 0x46, 0x4C, 0x58, 0xFC, 0xD8, 0xC4, 0xBB, 0xBA, 0xB8, 0xB5, 0xB2, 0xAF, +0xB0, 0xB6, 0xBD, 0xCC, 0x66, 0x47, 0x3B, 0x34, 0x32, 0x32, 0x34, 0x38, 0x3B, 0x3F, 0x4B, 0x58, +0x69, 0xE9, 0xD9, 0xD4, 0xDB, 0x77, 0x5C, 0x59, 0x4F, 0x4A, 0x4B, 0x51, 0x69, 0xE0, 0xCE, 0xC2, +0xBD, 0xBC, 0xBA, 0xB8, 0xB7, 0xB4, 0xB2, 0xB7, 0xC3, 0xCE, 0xD4, 0x67, 0x44, 0x3D, 0x3C, 0x3B, +0x38, 0x38, 0x3B, 0x3F, 0x42, 0x46, 0x4E, 0x5F, 0x6A, 0x6B, 0xFD, 0xF7, 0x70, 0x5C, 0x54, 0x58, +0x58, 0x53, 0x5B, 0x73, 0xE5, 0xD2, 0xCA, 0xC6, 0xC2, 0xBF, 0xBE, 0xBE, 0xBD, 0xBB, 0xBC, 0xC3, +0xCA, 0xD0, 0xE2, 0x5F, 0x4E, 0x4A, 0x44, 0x3E, 0x3D, 0x3D, 0x3F, 0x42, 0x44, 0x4B, 0x55, 0x57, +0x5F, 0x69, 0x68, 0x63, 0x58, 0x56, 0x5B, 0x56, 0x57, 0x66, 0x79, 0xE8, 0xD9, 0xCF, 0xCB, 0xCB, +0xC8, 0xC4, 0xC5, 0xC6, 0xC9, 0xC9, 0xC6, 0xCD, 0xD4, 0xD8, 0xE2, 0xF1, 0x66, 0x5C, 0x57, 0x4C, +0x49, 0x49, 0x48, 0x49, 0x49, 0x4B, 0x51, 0x51, 0x4F, 0x53, 0x56, 0x58, 0x58, 0x54, 0x58, 0x5F, +0x5E, 0x5B, 0x63, 0xF2, 0xE5, 0xE5, 0xD7, 0xCF, 0xD2, 0xD6, 0xD1, 0xCF, 0xD6, 0xD6, 0xD2, 0xCF, +0xCE, 0xD0, 0xD8, 0xDB, 0xDB, 0xE5, 0x6D, 0x67, 0x65, 0x5C, 0x58, 0x54, 0x59, 0x5D, 0x57, 0x57, +0x5B, 0x5B, 0x59, 0x56, 0x57, 0x5A, 0x56, 0x52, 0x5A, 0x5C, 0x5D, 0x7D, 0xF1, 0xF3, 0xE7, 0xE3, +0xDF, 0xDB, 0xDA, 0xE7, 0xEC, 0xDE, 0xE5, 0xE9, 0xE6, 0xE4, 0xD6, 0xDF, 0xF8, 0xDE, 0xE9, 0x7C, +0xEE, 0xFB, 0x7A, 0x6B, 0x65, 0xFF, 0x79, 0x69, 0x6B, 0x63, 0x65, 0x62, 0x56, 0x5A, 0x60, 0x55, +0x58, 0x62, 0x5E, 0x64, 0x79, 0x7C, 0x78, 0xED, 0xE1, 0xEC, 0xF0, 0xE4, 0xF0, 0x6E, 0x7C, 0xED, +0xDD, 0xDB, 0xFE, 0x73, 0xED, 0xF0, 0xFF, 0xF9, 0xDE, 0xDC, 0x76, 0x6E, 0xFB, 0x7B, 0xFB, 0x6F, +0x6F, 0xEF, 0x7B, 0x6E, 0xF8, 0xE9, 0x7E, 0x5B, 0x5F, 0xF3, 0xFD, 0x67, 0x74, 0xEA, 0xEF, 0x77, +0x6D, 0x7C, 0xE8, 0xE2, 0xEE, 0xFB, 0xE6, 0xED, 0x66, 0x6C, 0x7B, 0x7C, 0xF4, 0xEF, 0xE2, 0xE6, +0x72, 0x70, 0xF0, 0xEE, 0x6E, 0x6B, 0xFB, 0xF5, 0xF6, 0x71, 0x6A, 0x6C, 0x65, 0x67, 0x5F, 0x5D, +0x6B, 0x62, 0x65, 0xFD, 0xED, 0xE9, 0xF2, 0xED, 0xE3, 0xE7, 0xE4, 0xE8, 0xED, 0xEF, 0x66, 0x5E, +0x79, 0x7E, 0x78, 0xF5, 0xFA, 0x79, 0x7A, 0xF5, 0xE7, 0xE2, 0xE4, 0xF2, 0xFF, 0x73, 0x66, 0x6B, +0x70, 0xFD, 0xEF, 0x6F, 0x67, 0x5F, 0x5C, 0x6B, 0x6E, 0x78, 0x77, 0x6D, 0xF1, 0xF4, 0xF8, 0xE5, +0xE9, 0xEB, 0xEC, 0xEF, 0xEF, 0xF0, 0xE7, 0xED, 0x6B, 0x6F, 0xF0, 0xFF, 0x6A, 0x6F, 0x7E, 0x6F, +0x69, 0xED, 0xDF, 0xFF, 0x7C, 0xEB, 0xF3, 0xF8, 0x7E, 0x7A, 0xFB, 0x6E, 0x5E, 0x60, 0x70, 0x7A, +0xFC, 0xF4, 0xFF, 0xEF, 0xF5, 0x72, 0xF1, 0xEC, 0xEA, 0xE1, 0xE9, 0xED, 0xF2, 0xFC, 0xE7, 0xF0, +0x68, 0x76, 0xEF, 0xF4, 0xF9, 0x77, 0x79, 0xFB, 0xFC, 0xF6, 0xEE, 0xE9, 0xF2, 0x6D, 0x70, 0x6F, +0x6F, 0xFE, 0x6A, 0x69, 0x7F, 0x6F, 0x6F, 0x79, 0x78, 0x77, 0x6E, 0xFE, 0xEE, 0xF0, 0xEF, 0x7E, +0xF4, 0xE9, 0xF8, 0xFD, 0x7C, 0x77, 0x7D, 0x78, 0x7D, 0xFE, 0x79, 0x72, 0x6E, 0x72, 0x7B, 0x73, +0x6B, 0x74, 0x76, 0x6C, 0x72, 0x70, 0x75, 0x6D, 0x5A, 0x5C, 0x6A, 0x77, 0xFE, 0x71, 0x7A, 0xF2, +0xE9, 0xE7, 0xEA, 0xDE, 0xE6, 0xF5, 0xEB, 0xF5, 0xEE, 0xEA, 0xF3, 0xEE, 0xF4, 0xEE, 0xF5, 0x73, +0xF4, 0xF6, 0x73, 0x7C, 0xEC, 0xE9, 0x72, 0x67, 0x6C, 0x70, 0x6E, 0x69, 0x6F, 0x68, 0x5D, 0x64, +0x71, 0x75, 0x6B, 0x6D, 0xF9, 0x79, 0x6F, 0xF4, 0xEA, 0xF5, 0x79, 0x7B, 0x7D, 0xFA, 0xF3, 0xF7, +0xF4, 0xF4, 0xF9, 0x7F, 0x77, 0x7F, 0xF5, 0xFD, 0x71, 0x71, 0x7B, 0x78, 0x7A, 0x73, 0x68, 0x71, +0x7A, 0x6B, 0x6C, 0x6E, 0x69, 0x6E, 0x71, 0x6C, 0x69, 0x6C, 0x7A, 0x7D, 0xF6, 0xEB, 0xF4, 0xF2, +0xF4, 0x78, 0xF8, 0xF1, 0xF6, 0xFE, 0x71, 0x75, 0x7C, 0x7B, 0x7D, 0x7E, 0xFB, 0x7E, 0x77, 0x76, +0xFF, 0xF7, 0x79, 0x70, 0x6E, 0x6C, 0x71, 0x6F, 0x6C, 0x6A, 0x70, 0x6E, 0x5F, 0x65, 0x6C, 0x66, +0x6D, 0x7A, 0xF1, 0x78, 0x6B, 0xED, 0xFB, 0x6C, 0x7C, 0x74, 0xF1, 0xEC, 0x76, 0xF9, 0xF8, 0x75, +0x71, 0x7A, 0xEC, 0xFA, 0x6C, 0x76, 0x7C, 0x7F, 0x6E, 0x6A, 0x76, 0x6C, 0x76, 0xF2, 0x7C, 0x76, +0x66, 0x62, 0x7D, 0xF9, 0x78, 0x69, 0x6A, 0xF8, 0x78, 0x6D, 0xFC, 0xF8, 0xF3, 0xF7, 0x7D, 0xEF, +0xF1, 0x7C, 0xF1, 0xE9, 0xEB, 0xEE, 0xF3, 0xFD, 0x73, 0x7D, 0xFA, 0x74, 0x78, 0x7A, 0x6E, 0x70, +0x76, 0x73, 0x6F, 0x6D, 0x6C, 0x68, 0x6D, 0x7D, 0x6E, 0x64, 0x6D, 0xFC, 0xFA, 0x71, 0x6F, 0xFF, +0x70, 0x70, 0xF0, 0xEE, 0xEF, 0xEB, 0xEC, 0xEC, 0xED, 0xF0, 0xEE, 0xE9, 0xEB, 0xF8, 0xFB, 0xED, +0xE9, 0xF7, 0xFF, 0xED, 0xEE, 0xEF, 0xF3, 0x6E, 0x70, 0xF3, 0xFF, 0x6F, 0x6F, 0x6F, 0x79, 0x7C, +0x79, 0x7E, 0x77, 0x6E, 0x6E, 0x7B, 0x7C, 0x6B, 0x72, 0xF6, 0xFF, 0x7D, 0x7D, 0x72, 0x78, 0xFB, +0xFD, 0xFB, 0xFA, 0x7D, 0x7A, 0x6D, 0x68, 0x74, 0x75, 0xF1, 0xE7, 0x7B, 0x76, 0x6B, 0x66, 0xFD, +0x73, 0x68, 0x6B, 0x73, 0x7C, 0x62, 0x64, 0x70, 0x64, 0x6D, 0x79, 0x70, 0x70, 0x74, 0xF8, 0x7C, +0x7C, 0xF8, 0x6B, 0x70, 0xF9, 0x7D, 0xF1, 0xF0, 0xFF, 0x7F, 0xF8, 0xED, 0xF2, 0xFC, 0xFF, 0x76, +0x7B, 0x7C, 0xFE, 0xFE, 0x6C, 0x7C, 0xED, 0x79, 0x77, 0x71, 0x69, 0x76, 0x7A, 0x76, 0x7A, 0x6F, +0x6E, 0x6E, 0x73, 0xFF, 0x78, 0x76, 0x7F, 0x7A, 0x6F, 0x78, 0xFE, 0x77, 0xFB, 0xEE, 0xEE, 0xF1, +0x7A, 0xFB, 0xEE, 0x75, 0x70, 0xFE, 0x7D, 0x72, 0x6D, 0x6F, 0x7B, 0xFB, 0xF9, 0x7C, 0x7E, 0x7A, +0x70, 0x76, 0x7B, 0xF7, 0xF5, 0x70, 0x74, 0xFF, 0x78, 0xFA, 0xFC, 0x79, 0x78, 0x6D, 0x7A, 0xFA, +0x77, 0xFC, 0xF4, 0xF1, 0xF1, 0xF7, 0xEB, 0xED, 0xFB, 0xF7, 0xEF, 0xF2, 0x78, 0x79, 0xF0, 0xF3, +0xFC, 0xFC, 0x7E, 0x77, 0xFF, 0xFC, 0x78, 0xF6, 0xF9, 0x6F, 0x7A, 0x78, 0x6F, 0xFF, 0xF9, 0x7E, +0x76, 0x72, 0x76, 0x78, 0x79, 0xFD, 0xFA, 0x7A, 0x7E, 0xF1, 0xFB, 0x7A, 0xF2, 0xEF, 0xFF, 0x74, +0x75, 0x7E, 0xFF, 0xF6, 0xE9, 0xF4, 0x71, 0x74, 0x74, 0x7C, 0xF8, 0x79, 0x75, 0x74, 0x73, 0xF9, +0xFE, 0xFF, 0xFA, 0x6A, 0x6C, 0xFE, 0x70, 0x72, 0x7C, 0x7D, 0x70, 0x67, 0x6F, 0x76, 0x7B, 0xF0, +0x7E, 0x6E, 0x6E, 0x76, 0xF1, 0xF2, 0x7A, 0x72, 0x7B, 0xFD, 0x79, 0x77, 0x6E, 0x6A, 0x6D, 0x6E, +0x74, 0x72, 0x77, 0xF5, 0x7C, 0x6D, 0x6C, 0x6D, 0x70, 0x76, 0xFD, 0xFC, 0xFF, 0xFE, 0xFE, 0xFA, +0x7D, 0x77, 0x74, 0x6F, 0xFB, 0xFA, 0x71, 0xFC, 0xF8, 0xFE, 0xEB, 0xE5, 0xEE, 0x7F, 0xF6, 0xED, +0xFA, 0x78, 0xF6, 0xF1, 0xF8, 0xF6, 0x79, 0x70, 0x7C, 0x75, 0x78, 0xF6, 0xFA, 0xFC, 0xFB, 0x7B, +0x7D, 0xF9, 0xF7, 0xF7, 0x7E, 0x6B, 0x68, 0xFA, 0xE6, 0xEE, 0x7B, 0x7B, 0x79, 0x7E, 0xFB, 0xF2, +0xEA, 0xEE, 0xF1, 0xF6, 0x79, 0xFB, 0xF0, 0xFB, 0xFF, 0xF0, 0xEE, 0xF4, 0xEF, 0xFB, 0x6F, 0x6C, +0x72, 0xFF, 0x71, 0x6C, 0x6A, 0x6D, 0xF4, 0xFB, 0x7C, 0xF3, 0xFA, 0xFC, 0x6F, 0x73, 0xFB, 0x72, +0x7D, 0x78, 0x76, 0xEF, 0xF3, 0x7C, 0x71, 0xFA, 0xF2, 0xFB, 0xEF, 0xF0, 0xEA, 0xFD, 0x6B, 0xED, +0xF2, 0x77, 0x6E, 0x6D, 0xFD, 0x64, 0xF3, 0xDF, 0x5C, 0x61, 0xED, 0xF2, 0x7E, 0x66, 0x73, 0x6E, +0x66, 0xFF, 0x6F, 0x72, 0x70, 0x66, 0xF3, 0xF7, 0x6F, 0x72, 0x75, 0xED, 0xEF, 0xF4, 0xED, 0xEF, +0xEF, 0x6E, 0x6F, 0xF7, 0x72, 0x7E, 0x78, 0x6F, 0xF9, 0x77, 0xFC, 0xF4, 0x6C, 0x66, 0x6A, 0x76, +0x65, 0x5C, 0x6D, 0xFF, 0xF8, 0xEA, 0xE2, 0xEF, 0x65, 0x70, 0xEB, 0xE8, 0xE2, 0xDF, 0xDA, 0xD9, +0xE3, 0xF0, 0xFA, 0xE6, 0xDD, 0xDF, 0xDF, 0xE0, 0xE4, 0xFC, 0x63, 0x60, 0x64, 0x63, 0x66, 0x78, +0x65, 0x57, 0x60, 0x67, 0x64, 0x67, 0x5F, 0x6E, 0xF3, 0x7F, 0x75, 0xFE, 0xEA, 0xF4, 0xF8, 0xE0, +0xE0, 0xE4, 0xE3, 0xE7, 0xEA, 0xFB, 0x6F, 0x6E, 0x75, 0x72, 0x68, 0x6E, 0x77, 0x74, 0x6D, 0x73, +0xF9, 0x74, 0xF2, 0xE2, 0xEF, 0xDE, 0xDD, 0x79, 0xFD, 0xF4, 0x6F, 0x6A, 0x6E, 0xFE, 0x7A, 0x68, +0x60, 0x63, 0x6B, 0x66, 0x67, 0x76, 0xFE, 0x78, 0x65, 0x63, 0x7A, 0xFF, 0x72, 0x72, 0x74, 0x7D, +0x7C, 0x6B, 0x72, 0xEF, 0xF6, 0x73, 0x74, 0x7C, 0x74, 0x68, 0x68, 0x73, 0xFA, 0xF6, 0x6F, 0x67, +0x6F, 0x6D, 0x6A, 0x69, 0x68, 0x74, 0x6D, 0x64, 0x6F, 0x75, 0x77, 0xFA, 0x7F, 0xF5, 0xEF, 0x78, +0x77, 0x7D, 0x7A, 0xFE, 0x77, 0x73, 0xF8, 0xF4, 0xFE, 0x6F, 0x71, 0xF7, 0x74, 0x79, 0xEF, 0x7B, +0xF9, 0xFA, 0x68, 0x71, 0x7F, 0x76, 0x73, 0x71, 0x7D, 0x7D, 0x65, 0x5D, 0x69, 0x6E, 0x5F, 0x67, +0x6F, 0x6D, 0xFF, 0x7F, 0x6E, 0x6F, 0x73, 0x77, 0xF2, 0xDE, 0xD4, 0xD0, 0xD0, 0xCF, 0xCE, 0xD0, +0xD2, 0xD6, 0xDA, 0xDB, 0xE7, 0x6D, 0x5B, 0x52, 0x4C, 0x47, 0x44, 0x43, 0x44, 0x47, 0x49, 0x4B, +0x4E, 0x52, 0x56, 0x5A, 0x5D, 0x64, 0x75, 0xE6, 0xCF, 0xC5, 0xBF, 0xBD, 0xBB, 0xB7, 0xB5, 0xB1, +0xAF, 0xB5, 0xC1, 0xDF, 0x51, 0x44, 0x3A, 0x33, 0x33, 0x37, 0x3D, 0x44, 0x48, 0x4D, 0x51, 0x54, +0x53, 0x4D, 0x49, 0x44, 0x43, 0x45, 0x46, 0x4C, 0x54, 0x65, 0xE4, 0xCF, 0xC4, 0xBB, 0xB4, 0xAF, +0xAD, 0xAC, 0xAA, 0xAB, 0xB5, 0xD1, 0x49, 0x3B, 0x35, 0x2F, 0x2D, 0x32, 0x3D, 0x4E, 0x57, 0x51, +0x4F, 0x50, 0x4C, 0x42, 0x3D, 0x3F, 0x46, 0x4A, 0x4F, 0x5E, 0xEC, 0xD9, 0xDA, 0xD2, 0xC8, 0xBE, +0xB7, 0xB0, 0xAD, 0xAB, 0xA9, 0xA8, 0xAD, 0xC4, 0x48, 0x36, 0x31, 0x2E, 0x2D, 0x30, 0x3E, 0x67, +0xE2, 0x63, 0x47, 0x43, 0x42, 0x39, 0x36, 0x3B, 0x46, 0x6B, 0xE1, 0xDB, 0xCC, 0xC8, 0xCC, 0xD7, +0xDD, 0xC6, 0xBA, 0xB2, 0xAC, 0xAA, 0xA7, 0xA7, 0xB0, 0x69, 0x32, 0x2D, 0x2D, 0x2C, 0x2D, 0x39, +0xF1, 0xC2, 0xCA, 0x5E, 0x3D, 0x38, 0x34, 0x2E, 0x2F, 0x3E, 0x69, 0xCC, 0xC3, 0xBD, 0xBD, 0xC2, +0xCD, 0xEC, 0xF4, 0xCD, 0xC0, 0xB7, 0xAC, 0xA7, 0xA4, 0xA4, 0xB7, 0x3A, 0x2A, 0x29, 0x2A, 0x2B, +0x30, 0x56, 0xBA, 0xB6, 0xCD, 0x4A, 0x33, 0x2C, 0x2C, 0x2B, 0x34, 0x65, 0xC3, 0xBB, 0xBB, 0xBC, +0xC1, 0xDA, 0x7F, 0x6A, 0x75, 0xD1, 0xC2, 0xB8, 0xAC, 0xA6, 0xA5, 0xA4, 0xB0, 0x35, 0x23, 0x25, +0x29, 0x2F, 0x38, 0x73, 0xB3, 0xAF, 0xC8, 0x3E, 0x2E, 0x29, 0x26, 0x2C, 0x3D, 0xDF, 0xB8, 0xB5, +0xBB, 0xC5, 0xE7, 0x5A, 0x53, 0x68, 0xD9, 0xC8, 0xBA, 0xB3, 0xAD, 0xA8, 0xA7, 0xA6, 0xAF, 0x34, +0x1F, 0x24, 0x2E, 0x3B, 0x48, 0xDA, 0xB3, 0xB0, 0xD6, 0x33, 0x29, 0x29, 0x29, 0x2F, 0x5B, 0xC3, +0xB9, 0xB8, 0xC1, 0xDB, 0x53, 0x46, 0x4E, 0xEE, 0xCB, 0xC5, 0xBF, 0xBA, 0xB6, 0xAE, 0xAA, 0xA8, +0xAA, 0xDB, 0x26, 0x20, 0x2B, 0x3E, 0x5E, 0xEE, 0xC1, 0xB6, 0xC1, 0x3F, 0x2A, 0x29, 0x2A, 0x2F, +0x4F, 0xCA, 0xC0, 0xBE, 0xC5, 0xDC, 0x51, 0x44, 0x4B, 0x7D, 0xCB, 0xC5, 0xC5, 0xC2, 0xBF, 0xBD, +0xB1, 0xA9, 0xA8, 0xAA, 0xCB, 0x28, 0x20, 0x2C, 0x44, 0x7E, 0xDE, 0xC8, 0xBB, 0xC6, 0x3F, 0x2B, +0x29, 0x2E, 0x35, 0x4C, 0xCB, 0xBF, 0xC3, 0xCA, 0xE6, 0x4E, 0x43, 0x44, 0x6A, 0xC8, 0xC3, 0xC3, +0xC8, 0xC8, 0xBE, 0xB5, 0xAB, 0xA7, 0xA8, 0xBC, 0x2D, 0x1F, 0x29, 0x42, 0xD7, 0xD0, 0xCD, 0xBF, +0xC5, 0x4C, 0x2E, 0x2B, 0x2F, 0x36, 0x4A, 0xD7, 0xBE, 0xBD, 0xD5, 0x74, 0x50, 0x42, 0x45, 0x53, +0xD1, 0xBE, 0xBE, 0xC5, 0xD2, 0xCD, 0xBC, 0xAE, 0xA7, 0xA7, 0xB5, 0x36, 0x21, 0x28, 0x41, 0xD1, +0xC9, 0xD4, 0xCB, 0xCB, 0x51, 0x31, 0x2B, 0x2F, 0x39, 0x47, 0xE9, 0xC5, 0xC0, 0xCD, 0xED, 0x50, +0x3F, 0x3F, 0x52, 0xCD, 0xBB, 0xBC, 0xC6, 0xDE, 0xE7, 0xCB, 0xB5, 0xA8, 0xA4, 0xA9, 0xCC, 0x28, +0x20, 0x2E, 0x6F, 0xC2, 0xCF, 0xF7, 0xDA, 0xEB, 0x3F, 0x2E, 0x2D, 0x34, 0x3E, 0x5D, 0xCD, 0xBF, +0xC7, 0x73, 0x53, 0x43, 0x3C, 0x49, 0xDD, 0xBC, 0xB7, 0xC2, 0xE4, 0x62, 0xEC, 0xBD, 0xAA, 0xA3, +0xA4, 0xB3, 0x32, 0x1F, 0x26, 0x42, 0xC3, 0xC0, 0xF2, 0x68, 0x73, 0x4D, 0x37, 0x2D, 0x2F, 0x39, +0x47, 0xDD, 0xC2, 0xBE, 0xD2, 0x4A, 0x3E, 0x3B, 0x42, 0xEB, 0xBE, 0xB5, 0xB9, 0xD5, 0x54, 0x54, +0xCB, 0xAD, 0xA3, 0xA2, 0xAC, 0x4D, 0x23, 0x21, 0x34, 0xCF, 0xBA, 0xD0, 0x5E, 0x73, 0x5D, 0x43, +0x34, 0x30, 0x38, 0x3C, 0x46, 0xCE, 0xB9, 0xBE, 0x5F, 0x3E, 0x3B, 0x3E, 0x62, 0xC0, 0xB5, 0xB6, +0xC9, 0x54, 0x48, 0xE3, 0xB1, 0xA4, 0xA0, 0xA6, 0xC3, 0x2B, 0x1E, 0x29, 0x5D, 0xB9, 0xBD, 0x72, +0x50, 0x5B, 0x50, 0x3C, 0x30, 0x30, 0x36, 0x3F, 0x7C, 0xC0, 0xBA, 0xD0, 0x45, 0x3A, 0x38, 0x46, +0xCB, 0xB7, 0xB5, 0xC4, 0x5A, 0x48, 0x62, 0xBA, 0xA8, 0xA1, 0xA3, 0xB0, 0x3C, 0x20, 0x20, 0x39, +0xBD, 0xB1, 0xC7, 0x4F, 0x4A, 0x4E, 0x48, 0x35, 0x2E, 0x31, 0x39, 0x56, 0xC0, 0xB8, 0xC2, 0x4D, +0x39, 0x37, 0x3E, 0xE2, 0xBC, 0xB6, 0xBB, 0xDC, 0x4B, 0x4A, 0xD3, 0xAE, 0xA3, 0xA0, 0xA8, 0xD5, +0x29, 0x1F, 0x2B, 0xE3, 0xB3, 0xBA, 0x65, 0x47, 0x4C, 0x4F, 0x3D, 0x2F, 0x2D, 0x30, 0x41, 0xCB, +0xB7, 0xBA, 0x76, 0x3D, 0x38, 0x39, 0x51, 0xC5, 0xB6, 0xB6, 0xCC, 0x50, 0x46, 0x7E, 0xB4, 0xA6, +0xA1, 0xA4, 0xB8, 0x35, 0x20, 0x25, 0x48, 0xB8, 0xB5, 0xDB, 0x47, 0x47, 0x52, 0x4A, 0x34, 0x2D, +0x2E, 0x39, 0xEC, 0xBA, 0xB6, 0xCF, 0x3F, 0x38, 0x37, 0x41, 0xD0, 0xB8, 0xB4, 0xBF, 0x6A, 0x4A, +0x5B, 0xBC, 0xA8, 0xA1, 0xA2, 0xAF, 0x3E, 0x22, 0x24, 0x3E, 0xBA, 0xB3, 0xD4, 0x45, 0x41, 0x4F, +0x52, 0x37, 0x2D, 0x2D, 0x36, 0x65, 0xBB, 0xB6, 0xC9, 0x46, 0x38, 0x35, 0x3D, 0xDC, 0xB7, 0xB0, +0xBA, 0xF0, 0x48, 0x4E, 0xC1, 0xAA, 0xA2, 0xA1, 0xAB, 0x5E, 0x27, 0x21, 0x32, 0xC7, 0xB4, 0xC8, +0x48, 0x3E, 0x4B, 0x58, 0x3D, 0x2E, 0x2C, 0x2F, 0x48, 0xBF, 0xB5, 0xBF, 0x4C, 0x36, 0x34, 0x3A, +0x67, 0xBC, 0xB2, 0xB7, 0xD6, 0x4C, 0x4A, 0xCE, 0xAD, 0xA3, 0xA0, 0xA7, 0xD8, 0x29, 0x20, 0x2F, +0xD2, 0xB5, 0xC4, 0x4C, 0x40, 0x4B, 0x64, 0x44, 0x2F, 0x2C, 0x2D, 0x3F, 0xC4, 0xB4, 0xB9, 0x5F, +0x39, 0x36, 0x39, 0x5D, 0xBE, 0xB3, 0xB4, 0xC8, 0x59, 0x4B, 0xDC, 0xAF, 0xA3, 0x9F, 0xA5, 0xC5, +0x2C, 0x21, 0x2C, 0xFB, 0xB9, 0xC1, 0x53, 0x42, 0x4A, 0x5F, 0x49, 0x31, 0x2C, 0x2C, 0x3A, 0xCF, +0xB6, 0xBA, 0xF7, 0x3E, 0x39, 0x3A, 0x4C, 0xCA, 0xB7, 0xB4, 0xBF, 0x79, 0x4D, 0xE5, 0xB1, 0xA4, +0x9F, 0xA4, 0xC6, 0x2B, 0x20, 0x2C, 0xEC, 0xB7, 0xC1, 0x57, 0x43, 0x49, 0x5C, 0x47, 0x31, 0x2C, +0x2C, 0x39, 0xD3, 0xB7, 0xBA, 0xDC, 0x46, 0x3C, 0x39, 0x47, 0xCE, 0xB9, 0xB5, 0xBD, 0xDE, 0x5E, +0xD3, 0xB0, 0xA4, 0xA0, 0xA6, 0xCF, 0x2A, 0x21, 0x2D, 0xF6, 0xBB, 0xC8, 0x55, 0x44, 0x48, 0x51, +0x3F, 0x2F, 0x2C, 0x2D, 0x3B, 0xD8, 0xBC, 0xBF, 0xE7, 0x4E, 0x45, 0x3C, 0x44, 0xEB, 0xBE, 0xB5, +0xBA, 0xCA, 0xE3, 0xC9, 0xAF, 0xA5, 0xA0, 0xA7, 0xE6, 0x28, 0x22, 0x2E, 0xEA, 0xBD, 0xC8, 0x5D, +0x46, 0x4A, 0x4D, 0x3B, 0x2F, 0x2D, 0x2F, 0x3F, 0xD0, 0xBE, 0xC3, 0xE6, 0x56, 0x4B, 0x43, 0x4C, +0xFA, 0xC8, 0xBB, 0xBB, 0xC3, 0xC8, 0xBD, 0xAE, 0xA7, 0xA3, 0xAA, 0x6C, 0x2A, 0x25, 0x30, 0x5A, +0xCD, 0xD3, 0x5F, 0x4D, 0x50, 0x4C, 0x3B, 0x2F, 0x2D, 0x2F, 0x3D, 0xE6, 0xCC, 0xD3, 0xDA, 0xDB, +0x79, 0x55, 0x56, 0x68, 0xE0, 0xCA, 0xC2, 0xC0, 0xBC, 0xB1, 0xAB, 0xA6, 0xA5, 0xAE, 0x53, 0x2C, +0x29, 0x30, 0x41, 0x59, 0x62, 0x53, 0x57, 0x7F, 0x5D, 0x40, 0x35, 0x2E, 0x2F, 0x3D, 0x58, 0x78, +0xE5, 0xCD, 0xC8, 0xCF, 0xDA, 0xEC, 0x7B, 0xE4, 0xD1, 0xCA, 0xC4, 0xBA, 0xB0, 0xAC, 0xAA, 0xAB, +0xBA, 0x4F, 0x39, 0x39, 0x3D, 0x3F, 0x42, 0x3D, 0x3C, 0x45, 0x51, 0x52, 0x4C, 0x42, 0x3B, 0x3E, +0x4A, 0x4C, 0x4D, 0x57, 0x69, 0xEE, 0xDE, 0xE4, 0xFD, 0xED, 0xDC, 0xD3, 0xCD, 0xCA, 0xC3, 0xBD, +0xB9, 0xB6, 0xB8, 0xC3, 0xCE, 0xC8, 0xC1, 0xC5, 0xCE, 0xF9, 0x4A, 0x3F, 0x3F, 0x41, 0x3F, 0x3D, +0x39, 0x38, 0x3B, 0x3C, 0x3A, 0x3C, 0x43, 0x4C, 0x52, 0x58, 0x5B, 0x59, 0x65, 0xDC, 0xCB, 0xC3, +0xBD, 0xB8, 0xB6, 0xB2, 0xAF, 0xB2, 0xB6, 0xB2, 0xB0, 0xB6, 0xBF, 0xD7, 0x4F, 0x40, 0x3F, 0x3D, +0x3A, 0x35, 0x2F, 0x2D, 0x2F, 0x32, 0x33, 0x34, 0x39, 0x3E, 0x46, 0x51, 0x59, 0x5C, 0x6F, 0xD8, +0xC6, 0xBD, 0xB8, 0xB4, 0xB2, 0xAF, 0xAC, 0xAD, 0xAF, 0xAE, 0xAE, 0xB2, 0xBC, 0xCF, 0x55, 0x42, +0x3E, 0x3B, 0x36, 0x31, 0x2D, 0x2A, 0x2B, 0x2F, 0x32, 0x33, 0x36, 0x3B, 0x40, 0x4A, 0x57, 0x69, +0xEE, 0xD6, 0xC8, 0xBF, 0xB9, 0xB3, 0xB1, 0xAF, 0xAC, 0xAB, 0xAE, 0xAF, 0xAF, 0xB1, 0xB9, 0xC6, +0x75, 0x46, 0x3D, 0x3A, 0x36, 0x30, 0x2D, 0x2B, 0x2A, 0x2D, 0x31, 0x34, 0x36, 0x39, 0x3E, 0x46, +0x56, 0x77, 0xE3, 0xD4, 0xCA, 0xC3, 0xBD, 0xB6, 0xB1, 0xAF, 0xAC, 0xAB, 0xAC, 0xAF, 0xAF, 0xB0, +0xB5, 0xBE, 0xDA, 0x4E, 0x3E, 0x3A, 0x36, 0x31, 0x2D, 0x2C, 0x2B, 0x2C, 0x2F, 0x32, 0x34, 0x38, +0x3D, 0x43, 0x4D, 0x66, 0xE7, 0xD6, 0xCB, 0xC5, 0xC0, 0xBA, 0xB3, 0xAF, 0xAD, 0xAB, 0xAC, 0xAE, +0xAF, 0xAF, 0xB3, 0xBB, 0xCD, 0x5A, 0x40, 0x3A, 0x36, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, 0x2F, 0x31, +0x32, 0x38, 0x3D, 0x41, 0x4B, 0x5C, 0xF8, 0xD8, 0xCA, 0xC3, 0xBF, 0xBA, 0xB4, 0xB0, 0xAD, 0xAB, +0xAB, 0xAE, 0xAF, 0xB0, 0xB3, 0xBA, 0xC9, 0x68, 0x46, 0x3C, 0x37, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, +0x2F, 0x30, 0x31, 0x36, 0x3C, 0x3F, 0x49, 0x58, 0x70, 0xDD, 0xCA, 0xC2, 0xBE, 0xB9, 0xB3, 0xB0, +0xAD, 0xAA, 0xAB, 0xAE, 0xAF, 0xAF, 0xB4, 0xBC, 0xCB, 0x61, 0x46, 0x3D, 0x36, 0x30, 0x2E, 0x2C, +0x2B, 0x2D, 0x2F, 0x2F, 0x31, 0x37, 0x3B, 0x3F, 0x4B, 0x5C, 0xFF, 0xD8, 0xC8, 0xC1, 0xBC, 0xB6, +0xB2, 0xAF, 0xAC, 0xAA, 0xAB, 0xAD, 0xAE, 0xAF, 0xB5, 0xBD, 0xCF, 0x5C, 0x45, 0x3C, 0x35, 0x2F, +0x2D, 0x2C, 0x2C, 0x2D, 0x2F, 0x2F, 0x32, 0x37, 0x3B, 0x3F, 0x4B, 0x5F, 0xEE, 0xD4, 0xC8, 0xC1, +0xBC, 0xB5, 0xB0, 0xAE, 0xAB, 0xAA, 0xAB, 0xAD, 0xAF, 0xB1, 0xB7, 0xC0, 0xD9, 0x53, 0x42, 0x3B, +0x34, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x30, 0x33, 0x37, 0x3C, 0x43, 0x4D, 0x5E, 0xEC, 0xD0, +0xC7, 0xC0, 0xBB, 0xB6, 0xB0, 0xAE, 0xAB, 0xAA, 0xAC, 0xAE, 0xAF, 0xB3, 0xB9, 0xC5, 0xE2, 0x4E, +0x3F, 0x39, 0x32, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x2F, 0x33, 0x39, 0x3D, 0x44, 0x4F, 0x65, +0xE3, 0xCE, 0xC5, 0xBF, 0xBA, 0xB4, 0xAF, 0xAD, 0xAA, 0xAA, 0xAC, 0xAE, 0xB0, 0xB5, 0xBB, 0xC8, +0xF8, 0x4C, 0x3E, 0x38, 0x31, 0x2E, 0x2C, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x35, 0x39, 0x3E, 0x4A, +0x5C, 0xF9, 0xD8, 0xCB, 0xC2, 0xBC, 0xB7, 0xB2, 0xAE, 0xAB, 0xA9, 0xAA, 0xAC, 0xAE, 0xB2, 0xB8, +0xBE, 0xCE, 0x72, 0x48, 0x3B, 0x34, 0x2F, 0x2E, 0x2C, 0x2B, 0x2C, 0x2D, 0x2F, 0x31, 0x36, 0x3A, +0x3E, 0x4A, 0x5F, 0xEA, 0xD0, 0xC9, 0xC1, 0xBB, 0xB5, 0xB0, 0xAE, 0xAB, 0xA9, 0xAB, 0xAC, 0xAE, +0xB4, 0xBB, 0xC4, 0xD6, 0x60, 0x45, 0x39, 0x30, 0x2D, 0x2C, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x32, +0x35, 0x3A, 0x43, 0x51, 0x6A, 0xDF, 0xCB, 0xC3, 0xBD, 0xB8, 0xB2, 0xAE, 0xAC, 0xAA, 0xAA, 0xAB, +0xAC, 0xAF, 0xB7, 0xBD, 0xCC, 0xF1, 0x51, 0x3F, 0x36, 0x2F, 0x2D, 0x2B, 0x2A, 0x2C, 0x2D, 0x2E, +0x2F, 0x33, 0x37, 0x3B, 0x45, 0x58, 0x77, 0xD5, 0xC8, 0xC0, 0xBB, 0xB6, 0xB1, 0xAE, 0xAB, 0xA9, +0xAA, 0xAB, 0xAC, 0xB1, 0xB9, 0xC0, 0xD3, 0x6A, 0x4A, 0x3C, 0x33, 0x2E, 0x2D, 0x2B, 0x2B, 0x2C, +0x2D, 0x2E, 0x31, 0x34, 0x38, 0x3F, 0x4C, 0x5D, 0xEA, 0xCE, 0xC6, 0xBE, 0xB8, 0xB4, 0xAF, 0xAD, +0xAB, 0xAA, 0xAA, 0xAB, 0xAD, 0xB2, 0xB9, 0xC3, 0xD8, 0x61, 0x47, 0x3A, 0x32, 0x2E, 0x2C, 0x2B, +0x2C, 0x2C, 0x2D, 0x2F, 0x31, 0x34, 0x38, 0x3F, 0x4D, 0x62, 0xDD, 0xCC, 0xC3, 0xBC, 0xB7, 0xB2, +0xAE, 0xAC, 0xAA, 0xAA, 0xAA, 0xAB, 0xAE, 0xB3, 0xBA, 0xC6, 0xE1, 0x55, 0x41, 0x37, 0x30, 0x2D, +0x2B, 0x2A, 0x2B, 0x2C, 0x2D, 0x2F, 0x31, 0x34, 0x39, 0x40, 0x4D, 0x6A, 0xD8, 0xC9, 0xC1, 0xBC, +0xB7, 0xB2, 0xAE, 0xAC, 0xAA, 0xAA, 0xAB, 0xAC, 0xAF, 0xB7, 0xBB, 0xC6, 0x79, 0x55, 0x44, 0x37, +0x30, 0x2D, 0x2C, 0x2B, 0x2C, 0x2D, 0x2D, 0x2F, 0x33, 0x35, 0x39, 0x42, 0x51, 0x6C, 0xDA, 0xCA, +0xC4, 0xBE, 0xBA, 0xB6, 0xB0, 0xAD, 0xAC, 0xAC, 0xAC, 0xAD, 0xAF, 0xB4, 0xB9, 0xC0, 0xD0, 0x65, +0x47, 0x3A, 0x32, 0x2F, 0x2C, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x31, 0x36, 0x3A, 0x3F, 0x4A, 0x5E, +0xDC, 0xCC, 0xC4, 0xBE, 0xBA, 0xB6, 0xB2, 0xAF, 0xAE, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB8, 0xBE, +0xCA, 0xE6, 0x4F, 0x3F, 0x38, 0x31, 0x2E, 0x2C, 0x2C, 0x2C, 0x2D, 0x2F, 0x31, 0x35, 0x3A, 0x3F, +0x4B, 0x64, 0xDD, 0xCB, 0xC3, 0xBD, 0xBA, 0xB7, 0xB4, 0xB1, 0xAF, 0xAE, 0xAE, 0xB0, 0xB3, 0xB5, +0xB9, 0xBE, 0xC6, 0xD3, 0x74, 0x4E, 0x40, 0x3A, 0x35, 0x32, 0x30, 0x2F, 0x2F, 0x30, 0x33, 0x36, +0x3A, 0x3F, 0x48, 0x52, 0x6C, 0xDF, 0xCE, 0xC7, 0xC1, 0xBE, 0xBB, 0xB9, 0xB8, 0xB7, 0xB6, 0xB6, +0xB7, 0xB9, 0xBB, 0xBE, 0xC4, 0xCA, 0xD9, 0x6C, 0x50, 0x45, 0x3D, 0x3A, 0x37, 0x35, 0x35, 0x35, +0x36, 0x37, 0x39, 0x3C, 0x3F, 0x46, 0x4F, 0x5E, 0xF1, 0xD9, 0xCF, 0xCC, 0xC9, 0xC5, 0xC1, 0xBE, +0xBC, 0xBC, 0xBC, 0xBD, 0xBF, 0xC2, 0xC4, 0xC7, 0xCC, 0xD5, 0xE8, 0x64, 0x52, 0x4A, 0x45, 0x41, +0x3F, 0x3E, 0x3D, 0x3D, 0x3E, 0x41, 0x45, 0x49, 0x4D, 0x53, 0x5C, 0x71, 0xE9, 0xDD, 0xD6, 0xD1, +0xCE, 0xCB, 0xC9, 0xC8, 0xC8, 0xC7, 0xC7, 0xC8, 0xC9, 0xCC, 0xCF, 0xD4, 0xDA, 0xE4, 0xF7, 0x69, +0x5B, 0x53, 0x4F, 0x4E, 0x4D, 0x4D, 0x4D, 0x4E, 0x51, 0x58, 0x5F, 0x68, 0x6F, 0x77, 0xFC, 0xEE, +0xE8, 0xE5, 0xE1, 0xDD, 0xDE, 0xDE, 0xDE, 0xE2, 0xE6, 0xE6, 0xEB, 0xF0, 0xED, 0xEA, 0xEC, 0xEE, +0xF8, 0x6A, 0x5C, 0x57, 0x55, 0x56, 0x58, 0x59, 0x58, 0x58, 0x5B, 0x5E, 0x63, 0x71, 0xF7, 0xEE, +0xE9, 0xE3, 0xE4, 0xE8, 0xE7, 0xEA, 0xEE, 0xEE, 0xF2, 0xFB, 0xFF, 0x7B, 0x74, 0x6E, 0x6B, 0x65, +0x60, 0x65, 0x67, 0x61, 0x60, 0x61, 0x5E, 0x5E, 0x62, 0x69, 0x70, 0x7F, 0xF6, 0xF2, 0xEC, 0xE7, +0xE6, 0xE3, 0xDF, 0xDE, 0xDE, 0xDD, 0xDE, 0xE0, 0xE5, 0xEC, 0xF6, 0x7F, 0x78, 0x75, 0x70, 0x6B, +0x64, 0x5E, 0x5A, 0x58, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E, 0x61, 0x67, 0x6C, 0x75, 0xF8, 0xEC, +0xE6, 0xE0, 0xDD, 0xDD, 0xDE, 0xDF, 0xE0, 0xE2, 0xE2, 0xE4, 0xE6, 0xE9, 0xEE, 0xF9, 0x7D, 0x75, +0x6E, 0x6A, 0x65, 0x61, 0x5E, 0x5C, 0x5B, 0x5C, 0x5D, 0x5E, 0x60, 0x63, 0x66, 0x6C, 0x76, 0xFA, +0xEE, 0xE9, 0xE4, 0xE1, 0xDF, 0xDD, 0xDE, 0xDF, 0xE2, 0xE6, 0xEA, 0xEE, 0xF4, 0xFA, 0x7E, 0x7A, +0x75, 0x72, 0x74, 0x74, 0x71, 0x70, 0x6D, 0x6A, 0x6A, 0x6B, 0x6D, 0x70, 0x74, 0x78, 0x7A, 0x7D, +0xFA, 0xF0, 0xEA, 0xE5, 0xE3, 0xE2, 0xE0, 0xDF, 0xE1, 0xE3, 0xE4, 0xE8, 0xEB, 0xEE, 0xF4, 0xFB, +0x7E, 0x75, 0x6D, 0x6B, 0x6A, 0x69, 0x68, 0x67, 0x65, 0x64, 0x63, 0x64, 0x66, 0x69, 0x6D, 0x72, +0x77, 0x7B, 0xFE, 0xF7, 0xF0, 0xEB, 0xE8, 0xE7, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE8, 0xEA, 0xED, +0xF3, 0xFC, 0x79, 0x72, 0x6F, 0x6C, 0x6B, 0x6B, 0x69, 0x66, 0x64, 0x63, 0x62, 0x63, 0x67, 0x6C, +0x73, 0x7C, 0xFE, 0xFD, 0xFA, 0xF4, 0xEE, 0xEA, 0xE5, 0xE1, 0xE1, 0xE2, 0xE5, 0xE9, 0xED, 0xEF, +0xF1, 0xF2, 0xF4, 0xF8, 0xFF, 0x76, 0x6E, 0x6A, 0x68, 0x67, 0x66, 0x65, 0x64, 0x62, 0x61, 0x61, +0x63, 0x67, 0x6B, 0x72, 0x7E, 0xFA, 0xF5, 0xF1, 0xEE, 0xED, 0xEC, 0xE9, 0xE8, 0xE7, 0xE7, 0xE9, +0xEC, 0xF0, 0xF9, 0x7D, 0x77, 0x73, 0x70, 0x6F, 0x6E, 0x6C, 0x69, 0x67, 0x65, 0x66, 0x66, 0x68, +0x69, 0x69, 0x6A, 0x6C, 0x6E, 0x75, 0x7F, 0xF8, 0xF1, 0xED, 0xEB, 0xEA, 0xEA, 0xEB, 0xEB, 0xEB, +0xEA, 0xE9, 0xEA, 0xEC, 0xEF, 0xF4, 0xFB, 0x7D, 0x77, 0x73, 0x71, 0x6F, 0x6D, 0x6B, 0x69, 0x67, +0x66, 0x67, 0x68, 0x69, 0x6B, 0x6D, 0x6F, 0x74, 0x79, 0x7F, 0xF9, 0xF3, 0xEF, 0xEE, 0xEE, 0xEE, +0xEE, 0xED, 0xED, 0xED, 0xEE, 0xEC, 0xEC, 0xF1, 0xF3, 0xF6, 0xFD, 0xFF, 0x7D, 0x78, 0x78, 0x76, +0x70, 0x6F, 0x6E, 0x6D, 0x6D, 0x6E, 0x6F, 0x71, 0x73, 0x77, 0x7B, 0x7D, 0xFF, 0xFB, 0xF8, 0xF8, +0xFA, 0xFB, 0xFE, 0x7E, 0x7D, 0x7A, 0x79, 0x7A, 0x76, 0x76, 0x76, 0x72, 0x70, 0x6F, 0x6D, 0x6E, +0x6D, 0x6B, 0x6E, 0x65, 0x66, 0x57, 0x41, 0x59, 0xC7, 0x62, 0x57, 0xCD, 0x64, 0x6B, 0xCC, 0x6D, +0xF6, 0xD1, 0x69, 0x6E, 0xDA, 0x7D, 0x61, 0xEE, 0xF1, 0xFD, 0xFF, 0x6E, 0xE7, 0x74, 0x66, 0xE9, +0x6A, 0xFC, 0xF2, 0x64, 0xEE, 0x6F, 0x6B, 0x7B, 0x69, 0xF2, 0x68, 0x74, 0xEC, 0x5F, 0xED, 0xFC, +0x5F, 0xDE, 0x72, 0x65, 0xDC, 0x51, 0x4C, 0xE8, 0x5F, 0xEF, 0xD7, 0x4E, 0xF3, 0xF9, 0x52, 0xC3, +0xE2, 0x56, 0xC9, 0x5B, 0x59, 0xE4, 0x50, 0x53, 0x47, 0x66, 0xF9, 0x47, 0xC9, 0xCB, 0x59, 0xD4, +0x55, 0x58, 0xD3, 0x5C, 0xF9, 0xCE, 0xD7, 0x6D, 0x61, 0xDB, 0xE4, 0xE8, 0x70, 0x79, 0xCD, 0x6B, +0x5D, 0xEC, 0x66, 0xDC, 0x6C, 0x5F, 0xD3, 0xF9, 0xFB, 0x66, 0x55, 0xDE, 0xFB, 0x64, 0xD7, 0xDA, +0xF1, 0xF1, 0xF1, 0xE5, 0xDB, 0xF4, 0x75, 0xDF, 0xE7, 0xFD, 0x79, 0xEC, 0xD9, 0xF0, 0x6E, 0xF5, +0xE4, 0xE5, 0x64, 0x74, 0xDF, 0x7D, 0x68, 0x75, 0xF0, 0xFD, 0x7B, 0xFC, 0x7B, 0xDE, 0xEF, 0x64, +0xDF, 0xE8, 0xFF, 0xE9, 0xFF, 0xE2, 0xE1, 0x76, 0xF6, 0xEF, 0xEC, 0xF9, 0x6D, 0x7C, 0x73, 0x6D, +0x69, 0x67, 0x76, 0x75, 0x73, 0x64, 0x66, 0xFD, 0x60, 0x5F, 0x77, 0x67, 0x70, 0x74, 0x60, 0x78, +0x7A, 0x5C, 0x67, 0x79, 0x6C, 0x77, 0x63, 0x63, 0xEF, 0x6C, 0x66, 0x73, 0x6D, 0xF8, 0x72, 0x74, +0xE6, 0xFB, 0x7B, 0xF3, 0xF6, 0xF1, 0xFE, 0x7B, 0xF5, 0xF1, 0x6D, 0x63, 0x75, 0x6E, 0x60, 0x64, +0x67, 0x67, 0x68, 0x67, 0x67, 0x6C, 0x64, 0x5B, 0x65, 0x6E, 0x69, 0x72, 0x76, 0x74, 0xFE, 0xFD, +0xFF, 0xF6, 0xF3, 0x7C, 0x7A, 0xFC, 0xFE, 0xFB, 0x7E, 0x75, 0xF5, 0xEE, 0xFC, 0xF7, 0xF3, 0xFF, +0xFE, 0x74, 0x6B, 0x6F, 0x6D, 0x6A, 0x6C, 0x6A, 0x7A, 0xF9, 0x71, 0x7C, 0xF5, 0x7C, 0x7B, 0x7F, +0xFE, 0x72, 0x6E, 0xFF, 0x6F, 0x66, 0x6B, 0x6A, 0x6E, 0x76, 0x71, 0x7C, 0x7A, 0x6E, 0x6C, 0x69, +0x6C, 0x6D, 0x63, 0x65, 0x6A, 0x66, 0x63, 0x65, 0x6C, 0x68, 0x64, 0x69, 0x6A, 0x6E, 0x79, 0x7B, +0x78, 0x6D, 0x6E, 0x79, 0x77, 0xFA, 0xED, 0xEF, 0xEF, 0xEC, 0xE8, 0xEF, 0xFB, 0xF6, 0xEF, 0xEE, +0xF7, 0xF9, 0xF3, 0xF7, 0xF1, 0xF3, 0xFD, 0xFD, 0x7A, 0x7B, 0xFE, 0x79, 0x7C, 0x7B, 0x6E, 0x72, +0x7A, 0x72, 0x71, 0x79, 0x79, 0x6F, 0x6F, 0x78, 0x74, 0x72, 0x7B, 0x72, 0x78, 0xF6, 0xF7, 0xF9, +0x78, 0x79, 0xEE, 0xF5, 0x74, 0x76, 0x7A, 0xFE, 0x7E, 0x75, 0x7A, 0x7E, 0x74, 0x70, 0x7D, 0xF8, +0xF8, 0x7D, 0x77, 0xF8, 0xF0, 0x7F, 0x7E, 0xF2, 0xED, 0xF0, 0xF4, 0xEB, 0xE9, 0xFE, 0x74, 0xEF, +0xE1, 0xE0, 0xDC, 0xD9, 0xD7, 0xD3, 0xD7, 0xDD, 0xD9, 0xD9, 0xDF, 0xE6, 0xEB, 0xF2, 0x78, 0x65, +0x61, 0x65, 0x5F, 0x5A, 0x5B, 0x5B, 0x5A, 0x5C, 0x5B, 0x5D, 0x5E, 0x5C, 0x5B, 0x5B, 0x5B, 0x5C, +0x5E, 0x6B, 0xEA, 0xD9, 0xD2, 0xD1, 0xCD, 0xC8, 0xC6, 0xC5, 0xC5, 0xC3, 0xC3, 0xC6, 0xCA, 0xD2, +0xE8, 0x67, 0x51, 0x49, 0x42, 0x3D, 0x3D, 0x3E, 0x3F, 0x41, 0x44, 0x49, 0x4E, 0x50, 0x55, 0x5A, +0x5F, 0x6F, 0xEF, 0xDA, 0xCD, 0xCA, 0xC8, 0xC4, 0xBF, 0xBD, 0xBB, 0xB9, 0xB7, 0xB6, 0xBA, 0xC2, +0xCC, 0xE2, 0x56, 0x43, 0x3B, 0x39, 0x37, 0x36, 0x37, 0x3B, 0x3E, 0x42, 0x47, 0x4B, 0x51, 0x5A, +0x5B, 0x5E, 0x67, 0x7A, 0xED, 0xE8, 0xDB, 0xCF, 0xCB, 0xC5, 0xBD, 0xB9, 0xB6, 0xB2, 0xB0, 0xB4, +0xB9, 0xBF, 0xCD, 0xFC, 0x49, 0x3B, 0x37, 0x33, 0x31, 0x32, 0x33, 0x38, 0x3E, 0x45, 0x4F, 0x5F, +0x74, 0xF0, 0xEF, 0xF1, 0xF4, 0xFF, 0x77, 0x72, 0xFB, 0xE4, 0xD9, 0xCE, 0xC3, 0xBA, 0xB6, 0xB2, +0xB0, 0xB2, 0xB6, 0xBB, 0xC8, 0xE4, 0x51, 0x3E, 0x38, 0x33, 0x2F, 0x2F, 0x31, 0x35, 0x3A, 0x3E, +0x49, 0x5D, 0xFE, 0xE4, 0xDD, 0xD9, 0xD9, 0xE1, 0xEB, 0xEE, 0xEE, 0xE7, 0xDD, 0xD2, 0xC7, 0xBD, +0xB9, 0xB5, 0xB2, 0xB5, 0xB8, 0xBB, 0xC6, 0xDA, 0x5F, 0x45, 0x3D, 0x37, 0x33, 0x31, 0x31, 0x35, +0x39, 0x3D, 0x45, 0x53, 0x6F, 0xE3, 0xD9, 0xD0, 0xCF, 0xD2, 0xD5, 0xD9, 0xD9, 0xD8, 0xD4, 0xCE, +0xC8, 0xC0, 0xBD, 0xB9, 0xB8, 0xBB, 0xBD, 0xC1, 0xCD, 0xDF, 0x5F, 0x4A, 0x41, 0x3B, 0x37, 0x36, +0x35, 0x36, 0x3A, 0x3D, 0x44, 0x4E, 0x5F, 0xEC, 0xDB, 0xD6, 0xD2, 0xD4, 0xD6, 0xDA, 0xDD, 0xDB, +0xD8, 0xD2, 0xCD, 0xC9, 0xC3, 0xBF, 0xBE, 0xBF, 0xC3, 0xC4, 0xCA, 0xD6, 0xED, 0x5E, 0x50, 0x48, +0x3F, 0x3C, 0x3B, 0x3A, 0x3A, 0x3D, 0x40, 0x4A, 0x55, 0x6A, 0xE6, 0xDC, 0xD8, 0xD6, 0xD8, 0xD8, +0xDB, 0xDE, 0xDF, 0xE2, 0xDE, 0xDC, 0xD9, 0xD0, 0xCB, 0xC8, 0xC8, 0xCC, 0xCD, 0xCD, 0xD1, 0xDB, +0xF2, 0x67, 0x5B, 0x51, 0x49, 0x44, 0x42, 0x41, 0x42, 0x44, 0x49, 0x4E, 0x55, 0x5C, 0x6A, 0x7E, +0xF3, 0xED, 0xED, 0xEF, 0xEC, 0xED, 0xF4, 0xF7, 0xF0, 0xEB, 0xE4, 0xDF, 0xDA, 0xD2, 0xCD, 0xCC, +0xCC, 0xCF, 0xCF, 0xCE, 0xD3, 0xDB, 0xE7, 0x7C, 0x69, 0x5E, 0x54, 0x4F, 0x4E, 0x4D, 0x4E, 0x4F, +0x52, 0x58, 0x5B, 0x5E, 0x63, 0x67, 0x6B, 0x6B, 0x6A, 0x6B, 0x6C, 0x6E, 0x6F, 0x73, 0x7D, 0xF3, +0xEA, 0xE1, 0xDC, 0xD8, 0xD3, 0xD1, 0xCF, 0xCE, 0xD1, 0xD2, 0xD2, 0xD6, 0xDB, 0xE2, 0xEF, 0xFD, +0x73, 0x66, 0x5F, 0x5C, 0x5A, 0x59, 0x58, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58, 0x5A, 0x5C, 0x5D, +0x61, 0x65, 0x6A, 0x74, 0x7E, 0xF4, 0xEA, 0xE2, 0xDD, 0xDA, 0xD7, 0xD5, 0xD2, 0xD1, 0xD0, 0xD1, +0xD3, 0xD3, 0xD4, 0xD7, 0xDB, 0xE0, 0xE8, 0xEF, 0x7B, 0x69, 0x5F, 0x5B, 0x57, 0x53, 0x50, 0x4F, +0x4F, 0x4F, 0x4F, 0x50, 0x52, 0x56, 0x59, 0x5C, 0x60, 0x68, 0x6E, 0x7A, 0xFB, 0xF1, 0xEB, 0xE7, +0xE2, 0xDE, 0xDC, 0xDA, 0xD7, 0xD5, 0xD3, 0xD3, 0xD3, 0xD5, 0xD5, 0xD8, 0xDC, 0xE4, 0xF2, 0x78, +0x6A, 0x5E, 0x59, 0x55, 0x52, 0x50, 0x4F, 0x4F, 0x50, 0x52, 0x55, 0x58, 0x5A, 0x5D, 0x61, 0x64, +0x69, 0x6E, 0x74, 0xFF, 0xF6, 0xEE, 0xE9, 0xE4, 0xDF, 0xDC, 0xD8, 0xD5, 0xD2, 0xD0, 0xD1, 0xD1, +0xD2, 0xD6, 0xD7, 0xDA, 0xDF, 0xE8, 0xF8, 0x71, 0x69, 0x60, 0x5C, 0x58, 0x56, 0x54, 0x53, 0x53, +0x53, 0x55, 0x56, 0x58, 0x5A, 0x5C, 0x5F, 0x63, 0x69, 0x71, 0xFF, 0xEF, 0xEA, 0xE6, 0xE1, 0xDF, +0xDE, 0xDB, 0xDA, 0xDA, 0xD7, 0xD4, 0xD1, 0xD1, 0xD2, 0xD4, 0xD7, 0xD8, 0xDB, 0xE1, 0xED, 0x78, +0x69, 0x61, 0x5A, 0x53, 0x50, 0x4F, 0x4E, 0x4F, 0x4F, 0x51, 0x55, 0x59, 0x5D, 0x60, 0x65, 0x6C, +0x6F, 0x76, 0x7E, 0xFC, 0xF5, 0xF2, 0xF2, 0xED, 0xE7, 0xE1, 0xDD, 0xD9, 0xD4, 0xCE, 0xCB, 0xCA, +0xC8, 0xCA, 0xCF, 0xD0, 0xD7, 0xE6, 0x7C, 0x5E, 0x53, 0x4F, 0x4C, 0x4A, 0x4A, 0x4A, 0x4C, 0x4E, +0x4F, 0x51, 0x57, 0x5D, 0x62, 0x66, 0x69, 0x6D, 0x6D, 0x67, 0x63, 0x65, 0x6B, 0x70, 0x75, 0xFE, +0xEA, 0xDC, 0xD3, 0xCE, 0xC9, 0xC2, 0xBE, 0xBC, 0xBC, 0xC1, 0xC8, 0xCB, 0xDC, 0x69, 0x50, 0x46, +0x42, 0x3F, 0x3D, 0x3D, 0x3F, 0x43, 0x4B, 0x4F, 0x55, 0x61, 0x6F, 0xFC, 0xF0, 0xFA, 0xFD, 0x7B, +0x63, 0x5A, 0x54, 0x52, 0x54, 0x58, 0x62, 0x7D, 0xDE, 0xCC, 0xC4, 0xBC, 0xB6, 0xB1, 0xAE, 0xB2, +0xBA, 0xC1, 0xDA, 0x5A, 0x45, 0x38, 0x33, 0x32, 0x33, 0x36, 0x3A, 0x3E, 0x4B, 0x66, 0xEA, 0xD6, +0xCE, 0xCF, 0xD3, 0xE0, 0x6E, 0x5C, 0x51, 0x4D, 0x4B, 0x4A, 0x4F, 0x5B, 0x7A, 0xD6, 0xC6, 0xBA, +0xB1, 0xAE, 0xAA, 0xAA, 0xAF, 0xBA, 0xD1, 0x4E, 0x3D, 0x31, 0x2B, 0x2B, 0x2C, 0x30, 0x38, 0x3E, +0x4E, 0xEA, 0xCD, 0xC7, 0xC8, 0xCB, 0xCE, 0xDD, 0x64, 0x4F, 0x48, 0x48, 0x47, 0x46, 0x4D, 0x62, +0xDD, 0xCA, 0xBD, 0xB1, 0xAC, 0xA9, 0xA7, 0xAA, 0xB2, 0xC4, 0x51, 0x3A, 0x2F, 0x29, 0x28, 0x2A, +0x2E, 0x39, 0x45, 0x5A, 0xDB, 0xC8, 0xC2, 0xC5, 0xCD, 0xD5, 0xE8, 0x5B, 0x4C, 0x46, 0x47, 0x4C, +0x4E, 0x59, 0xF3, 0xD3, 0xC6, 0xBE, 0xB5, 0xAD, 0xAA, 0xA6, 0xA8, 0xB2, 0xC5, 0x54, 0x38, 0x2F, +0x28, 0x26, 0x2A, 0x2F, 0x3A, 0x4B, 0x73, 0xCF, 0xC5, 0xC4, 0xC9, 0xD9, 0xF8, 0x5F, 0x4C, 0x47, +0x47, 0x48, 0x4F, 0x5A, 0x74, 0xD9, 0xCE, 0xC7, 0xBF, 0xBA, 0xB0, 0xAC, 0xAA, 0xA9, 0xB0, 0xC5, +0x5C, 0x3B, 0x30, 0x2B, 0x27, 0x29, 0x2E, 0x39, 0x49, 0x6C, 0xD2, 0xC7, 0xC5, 0xCA, 0xDA, 0x7A, +0x5E, 0x4D, 0x48, 0x49, 0x4B, 0x52, 0x5E, 0x70, 0xDC, 0xCE, 0xC9, 0xC4, 0xBE, 0xB5, 0xAE, 0xAC, +0xAA, 0xAD, 0xBB, 0xE1, 0x43, 0x35, 0x2E, 0x29, 0x28, 0x2B, 0x32, 0x3D, 0x4E, 0x7E, 0xD2, 0xC9, +0xC7, 0xCC, 0xDF, 0xFE, 0x61, 0x4F, 0x4C, 0x4B, 0x4C, 0x54, 0x5D, 0x72, 0xDA, 0xCE, 0xC7, 0xC0, +0xB9, 0xB0, 0xAD, 0xAA, 0xAA, 0xB1, 0xC5, 0x5D, 0x3D, 0x33, 0x2C, 0x29, 0x29, 0x2D, 0x35, 0x3F, +0x4F, 0x78, 0xD7, 0xCA, 0xC7, 0xCD, 0xD9, 0xE3, 0x71, 0x5C, 0x54, 0x4D, 0x4E, 0x55, 0x5A, 0x6E, +0xE3, 0xD3, 0xC8, 0xBE, 0xB6, 0xAF, 0xAC, 0xAA, 0xAC, 0xB7, 0xCE, 0x53, 0x3D, 0x34, 0x2D, 0x2A, +0x2A, 0x2E, 0x35, 0x3E, 0x4A, 0x5C, 0xE5, 0xCD, 0xC8, 0xCB, 0xCE, 0xD5, 0xE2, 0xF7, 0x66, 0x57, +0x54, 0x56, 0x5B, 0x6E, 0xE7, 0xD5, 0xC9, 0xBE, 0xB6, 0xB1, 0xAE, 0xAE, 0xB4, 0xBE, 0xD3, 0x5C, +0x46, 0x3C, 0x34, 0x30, 0x2F, 0x32, 0x38, 0x3E, 0x45, 0x4E, 0x69, 0xDC, 0xCF, 0xCD, 0xCF, 0xD3, +0xD9, 0xDF, 0xEF, 0x6F, 0x68, 0x67, 0x6F, 0xF7, 0xE7, 0xDC, 0xD2, 0xC8, 0xC1, 0xBE, 0xBC, 0xBD, +0xC3, 0xCB, 0xD5, 0xEA, 0x6A, 0x58, 0x4D, 0x47, 0x43, 0x43, 0x44, 0x47, 0x4A, 0x4D, 0x52, 0x5D, +0x6D, 0x78, 0x77, 0x78, 0x76, 0x73, 0x75, 0x72, 0x72, 0x78, 0x7E, 0xF6, 0xEB, 0xE3, 0xDC, 0xD6, +0xD2, 0xCF, 0xCE, 0xCE, 0xCD, 0xCF, 0xD2, 0xD4, 0xD7, 0xDA, 0xDD, 0xE3, 0xEF, 0x7A, 0x6C, 0x65, +0x5F, 0x5B, 0x57, 0x53, 0x4F, 0x4E, 0x4D, 0x4C, 0x4A, 0x4A, 0x4A, 0x4B, 0x4D, 0x4F, 0x53, 0x59, +0x5F, 0x6C, 0xFE, 0xEC, 0xE3, 0xDE, 0xDC, 0xDA, 0xD9, 0xD8, 0xD7, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, +0xD8, 0xDA, 0xDC, 0xDE, 0xE3, 0xEC, 0xFB, 0x6F, 0x63, 0x5B, 0x55, 0x4F, 0x4D, 0x4C, 0x4B, 0x4C, +0x4C, 0x4D, 0x4F, 0x53, 0x58, 0x5E, 0x67, 0x73, 0xFB, 0xEE, 0xE8, 0xE3, 0xDF, 0xDC, 0xDA, 0xD9, +0xD8, 0xD8, 0xD8, 0xD9, 0xDB, 0xDD, 0xDF, 0xE1, 0xE5, 0xE9, 0xEE, 0xF6, 0x7C, 0x6E, 0x66, 0x5F, +0x5C, 0x5A, 0x58, 0x56, 0x56, 0x57, 0x58, 0x5A, 0x5D, 0x60, 0x66, 0x6D, 0x74, 0x7C, 0xFB, 0xF4, +0xEE, 0xEA, 0xE7, 0xE5, 0xE4, 0xE4, 0xE4, 0xE7, 0xEA, 0xEC, 0xED, 0xED, 0xEC, 0xED, 0xEC, 0xED, +0xEE, 0xF1, 0xF9, 0x7F, 0x79, 0x75, 0x70, 0x6D, 0x6C, 0x6C, 0x6E, 0x73, 0x79, 0xFE, 0xF9, 0xF3, +0xEE, 0xEB, 0xE9, 0xE8, 0xE6, 0xE5, 0xE6, 0xE8, 0xEA, 0xED, 0xF1, 0xF8, 0xFD, 0x7E, 0x7E, 0xFE, +0xFC, 0xF9, 0xF5, 0xF5, 0xF6, 0xFB, 0x7C, 0x77, 0x71, 0x6E, 0x6C, 0x6A, 0x69, 0x69, 0x6B, 0x6E, +0x70, 0x75, 0x7A, 0xFE, 0xF6, 0xF1, 0xEE, 0xEC, 0xEB, 0xEB, 0xEC, 0xEE, 0xF1, 0xF5, 0xFB, 0x7D, +0x7B, 0x7A, 0x7C, 0xFE, 0xFA, 0xF4, 0xF1, 0xF0, 0xF0, 0xF3, 0xF7, 0xFB, 0xFD, 0xFF, 0xFF, 0x7F, +0x7F, 0xFE, 0xFE, 0xFE, 0xFF, 0x7F, 0xFD, 0xFC, 0xFA, 0xF5, 0xF4, 0xF1, 0xEF, 0xEF, 0xEF, 0xF0, +0xF4, 0xF8, 0xFD, 0x7D, 0x78, 0x77, 0x78, 0x7A, 0x7C, 0x7C, 0x7E, 0x7C, 0x7A, 0x79, 0x76, 0x77, +0x78, 0x77, 0x7B, 0x7E, 0xFF, 0xFD, 0xFD, 0xFE, 0xFF, 0xFF, 0x7F, 0x7F, 0xFF, 0x7E, 0xFE, 0xFB, +0xFA, 0xF9, 0xFA, 0xFB, 0xFE, 0x7F, 0x7C, 0x79, 0x78, 0x77, 0x77, 0x7A, 0x7C, 0x7D, 0x7E, 0x7B, +0x77, 0x74, 0x71, 0x6F, 0x6E, 0x6E, 0x6F, 0x6F, 0x70, 0x71, 0x73, 0x74, 0x76, 0x76, 0x75, 0x76, +0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x77, 0x74, 0x72, 0x70, 0x6F, 0x6E, 0x6E, 0x6E, 0x70, 0x74, +0x78, 0x7B, 0x7D, 0x7D, 0x7C, 0x7C, 0x7A, 0x76, 0x72, 0x71, 0x70, 0x70, 0x70, 0x71, 0x72, 0x72, +0x74, 0x76, 0x78, 0x7B, 0x7F, 0xFE, 0xFC, 0xF9, 0xF6, 0xF5, 0xF7, 0xFC, 0xFF, 0x7C, 0x79, 0x78, +0x77, 0x77, 0x77, 0x7A, 0x7E, 0xFE, 0xFC, 0xFA, 0xF8, 0xF8, 0xFA, 0xFC, 0xFE, 0x7D, 0x7A, 0x76, +0x74, 0x74, 0x73, 0x75, 0x76, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0xFF, 0xFB, 0xF9, 0xF8, 0xF9, 0xFD, +0x7E, 0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x78, 0x7C, 0x7F, 0xFB, 0xF6, 0xF5, 0xF2, 0xF2, 0xF3, +0xF4, 0xF7, 0xF8, 0xFA, 0xFC, 0xFE, 0xFF, 0xFF, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFA, +0xF7, 0xF6, 0xF8, 0xFC, 0x7F, 0x7C, 0x79, 0x72, 0x6F, 0x6E, 0x6E, 0x6E, 0x6F, 0x73, 0x77, 0x77, +0x7B, 0x7D, 0x7E, 0x7F, 0x7E, 0x7C, 0x7A, 0x77, 0x75, 0x71, 0x6F, 0x6E, 0x6D, 0x6E, 0x6F, 0x70, +0x73, 0x74, 0x77, 0x79, 0x7C, 0x7D, 0x7D, 0x7D, 0x7B, 0x7A, 0x79, 0x76, 0x73, 0x70, 0x6F, 0x71, +0x73, 0x78, 0x7B, 0x7F, 0xFB, 0xF8, 0xF5, 0xF3, 0xF5, 0xF6, 0xF6, 0xF8, 0xF9, 0xFC, 0xFE, 0xFF, +0x7F, 0xFF, 0xFD, 0xFC, 0xFC, 0xFB, 0xF7, 0xF6, 0xF6, 0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF7, 0xF9, +0xFA, 0xFC, 0xFF, 0xFF, 0x7F, 0x7F, 0xFE, 0xFD, 0xFE, 0xFD, 0xFB, 0xFB, 0xFC, 0xFE, 0x7E, 0x7C, +0x7A, 0x79, 0x77, 0x76, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7C, 0x7B, 0x7B, 0x7C, 0x7C, 0x7C, 0x7C, +0x7D, 0x7E, 0x7C, 0x7E, 0x7F, 0x7E, 0x7E, 0xFF, 0xFD, 0xFC, 0xFB, 0xF8, 0xF8, 0xFA, 0xFA, 0xFC, +0xFE, 0x7D, 0x78, 0x75, 0x72, 0x70, 0x70, 0x73, 0x75, 0x76, 0x78, 0x79, 0x79, 0x7A, 0x79, 0x78, +0x77, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7D, 0x7F, 0xFC, 0xF8, 0xF6, +0xF4, 0xF2, 0xF3, 0xF7, 0xF9, 0xFB, 0x7E, 0x78, 0x75, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x74, +0x75, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x78, 0x78, 0x78, 0x77, 0x75, 0x75, 0x76, 0x79, 0x7B, +0x7C, 0x7E, 0xFE, 0xFC, 0xF9, 0xF6, 0xF5, 0xF6, 0xF7, 0xF9, 0xFC, 0xFE, 0x7C, 0x79, 0x77, 0x76, +0x75, 0x74, 0x74, 0x75, 0x74, 0x76, 0x77, 0x78, 0x79, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x78, 0x78, +0x77, 0x76, 0x77, 0x77, 0x77, 0x78, 0x7A, 0x7C, 0xFF, 0xFD, 0xFA, 0xF8, 0xF6, 0xF7, 0xF8, 0xF7, +0xF8, 0xF8, 0xF8, 0xF9, 0xFA, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFB, 0xFD, 0xFD, 0xFC, 0xFD, 0xFE, +0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7B, 0x7A, 0x79, 0x79, 0x79, 0x78, 0x79, 0x7B, 0x7C, 0x7C, 0x7C, +0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x71, 0x70, 0x6F, 0x6F, 0x6E, +0x6D, 0x6C, 0x6C, 0x6D, 0x6E, 0x6E, 0x6E, 0x6F, 0x6E, 0x6F, 0x70, 0x70, 0x72, 0x75, 0x78, 0x7B, +0x7B, 0x7C, 0x7D, 0xFF, 0xFD, 0xFC, 0xFA, 0xFA, 0xF9, 0xF7, 0xF7, 0xF7, 0xF7, 0xF8, 0xF8, 0xF9, +0xFA, 0xFA, 0xFA, 0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFB, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFB, +0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFD, 0xFF, 0x7F, 0xFC, 0xFA, 0xFB, 0xFA, 0xFA, 0xF9, +0xFA, 0xFC, 0xFC, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFE, 0xFD, 0xFC, 0xFD, 0xFA, 0xFA, 0xF9, 0xF8, +0xFA, 0xF8, 0xFA, 0xFA, 0xFB, 0xFE, 0xFB, 0xFF, 0xFD, 0xFD, 0xFF, 0xFB, 0xFF, 0xFA, 0xFC, 0x7F, +0xFC, 0x70, 0x6A, 0x64, 0x61, 0x67, 0x6C, 0x73, 0x61, 0x60, 0x5A, 0x47, 0x53, 0xDF, 0x7D, 0x6F, +0x71, 0x69, 0x7F, 0x72, 0x70, 0x68, 0x5A, 0x5B, 0x59, 0x55, 0x5A, 0x5D, 0x4E, 0x46, 0x51, 0x77, +0xF6, 0xEC, 0xE8, 0xE6, 0xE5, 0xE4, 0xE1, 0xE5, 0xFD, 0x60, 0x65, 0xE6, 0xDB, 0xDD, 0xE1, 0xE4, +0xE2, 0xDC, 0xD4, 0xCF, 0xCF, 0xCF, 0xD1, 0xD2, 0xCF, 0xCD, 0xCC, 0xCD, 0xD0, 0xD4, 0xD7, 0xD5, +0xCE, 0xCD, 0xD3, 0xD9, 0xDB, 0xDA, 0xD9, 0xDC, 0xE2, 0xE5, 0xF1, 0xEE, 0xD7, 0xD2, 0xD9, 0xDC, +0xDD, 0xD9, 0xDC, 0xE7, 0xDF, 0xD9, 0xDD, 0x74, 0x62, 0xFC, 0xE9, 0xF2, 0x6C, 0x5D, 0x5D, 0x61, +0x6B, 0x6C, 0x5C, 0x5D, 0x70, 0x64, 0x5B, 0x6D, 0x78, 0x61, 0x57, 0x58, 0x65, 0x62, 0x53, 0x51, +0x69, 0xF5, 0x59, 0x53, 0x69, 0x68, 0x54, 0x4F, 0x67, 0xFA, 0x5B, 0x58, 0x5D, 0x60, 0x5D, 0x4E, +0x53, 0x61, 0x5A, 0x5F, 0x63, 0x61, 0x5E, 0x4C, 0x4C, 0x5A, 0x6B, 0xF7, 0x58, 0x47, 0x51, 0x6D, +0x68, 0x5A, 0x55, 0x53, 0x4A, 0x4E, 0xEE, 0xE1, 0x56, 0x46, 0x4D, 0x74, 0x70, 0x59, 0x53, 0x4C, +0x4E, 0x62, 0xF2, 0xF3, 0x5A, 0x4A, 0x4F, 0x6D, 0x6B, 0x57, 0x63, 0xED, 0x79, 0x69, 0x62, 0x5D, +0x6C, 0x67, 0x5E, 0xF5, 0xEA, 0x69, 0x63, 0x76, 0xFA, 0x6A, 0x59, 0x5D, 0xFB, 0xF6, 0x72, 0xFB, +0x71, 0x5D, 0x65, 0x7D, 0xFC, 0x79, 0xFB, 0xE9, 0xE2, 0xDD, 0xE3, 0xF9, 0xFA, 0xF3, 0xEA, 0xEC, +0x7D, 0xF1, 0xE5, 0xF2, 0x78, 0x6D, 0x6D, 0xED, 0xE4, 0xE5, 0xE4, 0xF6, 0x7A, 0xF8, 0xE5, 0xDC, +0xEA, 0xDF, 0xCF, 0xD3, 0xE7, 0x6C, 0x78, 0xCF, 0xC9, 0xD5, 0x7E, 0x64, 0x7E, 0xD7, 0xCF, 0xD7, +0xE0, 0xE8, 0xDC, 0xCB, 0xC5, 0xCC, 0xE6, 0x73, 0xE9, 0xD2, 0xC9, 0xCA, 0xD3, 0xED, 0x68, 0xF3, +0xD9, 0xDA, 0xDC, 0xDA, 0xD7, 0xDB, 0xE1, 0xE1, 0xEA, 0x69, 0x59, 0x60, 0xEE, 0xE0, 0xDC, 0xDF, +0xE5, 0xDD, 0xE1, 0xED, 0xED, 0xF4, 0xF6, 0xF2, 0xEE, 0xED, 0xFC, 0x6F, 0x64, 0x5C, 0x5C, 0x61, +0x76, 0xEA, 0xE1, 0xEB, 0x73, 0x66, 0x60, 0x69, 0xFB, 0x7E, 0x70, 0x76, 0xFF, 0x78, 0x73, 0xEB, +0xE2, 0x6F, 0x5C, 0x69, 0xE9, 0xDB, 0xDE, 0x73, 0x5E, 0x6D, 0xED, 0xEB, 0xF4, 0xFF, 0x77, 0xFD, +0xE7, 0xE2, 0xFB, 0x66, 0x69, 0xFE, 0xFA, 0x6B, 0x63, 0x69, 0x74, 0x74, 0x66, 0x63, 0x6F, 0xFB, +0xFD, 0x73, 0x71, 0x7B, 0xF4, 0xE8, 0xE2, 0xE8, 0xF2, 0xEB, 0xDE, 0xDC, 0xDE, 0xE2, 0xE7, 0xE5, +0xDF, 0xDF, 0xE8, 0xFC, 0x6D, 0x6A, 0x66, 0x5C, 0x53, 0x51, 0x56, 0x5B, 0x59, 0x56, 0x56, 0x5A, +0x5E, 0x5A, 0x53, 0x53, 0x59, 0x5D, 0x5F, 0x64, 0x74, 0xE9, 0xD6, 0xCB, 0xCB, 0xD3, 0xDC, 0xD4, +0xC8, 0xC1, 0xBF, 0xBE, 0xBE, 0xC5, 0xCE, 0xDC, 0xEA, 0x68, 0x4B, 0x3E, 0x3B, 0x3B, 0x3D, 0x3E, +0x41, 0x45, 0x47, 0x48, 0x4A, 0x4C, 0x4A, 0x47, 0x49, 0x4F, 0x5E, 0x6D, 0xF5, 0xDB, 0xCC, 0xC5, +0xC1, 0xBF, 0xBE, 0xBC, 0xBA, 0xB7, 0xB2, 0xAF, 0xAE, 0xB7, 0xDA, 0x43, 0x38, 0x35, 0x32, 0x31, +0x33, 0x3A, 0x46, 0x54, 0x5D, 0x56, 0x4A, 0x44, 0x43, 0x46, 0x44, 0x3F, 0x40, 0x4B, 0x61, 0x7B, +0xFD, 0xE7, 0xD4, 0xCA, 0xC5, 0xBF, 0xBB, 0xB6, 0xB1, 0xAF, 0xAD, 0xAC, 0xAC, 0xB4, 0xD8, 0x3C, +0x2D, 0x2B, 0x2D, 0x2F, 0x37, 0x41, 0x54, 0x76, 0xFF, 0x68, 0x54, 0x46, 0x40, 0x44, 0x49, 0x4A, +0x49, 0x4C, 0x5C, 0xFB, 0xE9, 0xE5, 0xDB, 0xCE, 0xC8, 0xC3, 0xBE, 0xBA, 0xB6, 0xB1, 0xAE, 0xAB, +0xA9, 0xAB, 0xBD, 0x3F, 0x2A, 0x26, 0x2A, 0x31, 0x3A, 0x43, 0x58, 0xE9, 0xDB, 0xE8, 0x5E, 0x46, +0x3C, 0x3D, 0x48, 0x55, 0x56, 0x55, 0x61, 0xF3, 0xE8, 0xE6, 0xDC, 0xCF, 0xC9, 0xC0, 0xB9, 0xB4, +0xB3, 0xB3, 0xAF, 0xAA, 0xA8, 0xAF, 0x75, 0x2C, 0x22, 0x24, 0x2D, 0x3F, 0x56, 0x72, 0xE0, 0xCF, +0xD4, 0x59, 0x3F, 0x38, 0x38, 0x3E, 0x4C, 0x68, 0xE8, 0xE3, 0xEA, 0x7E, 0x67, 0x64, 0x79, 0xDC, +0xCB, 0xBF, 0xB7, 0xB1, 0xAF, 0xAE, 0xAC, 0xAA, 0xB1, 0x56, 0x29, 0x1F, 0x20, 0x2B, 0x45, 0xD1, +0xC4, 0xC7, 0xCB, 0xD8, 0x4B, 0x33, 0x2E, 0x34, 0x42, 0x6D, 0xCD, 0xC5, 0xC8, 0xD1, 0xE0, 0x69, +0x52, 0x52, 0x6D, 0xD4, 0xC6, 0xBB, 0xB1, 0xAC, 0xAB, 0xAA, 0xA8, 0xAE, 0x68, 0x28, 0x1D, 0x1E, +0x2B, 0x53, 0xBC, 0xB6, 0xBD, 0xCA, 0xE1, 0x4E, 0x33, 0x2A, 0x2C, 0x3B, 0x7D, 0xBF, 0xBB, 0xC1, +0xD5, 0xED, 0x6A, 0x4F, 0x4C, 0x58, 0x7E, 0xD6, 0xC7, 0xBB, 0xB2, 0xAD, 0xA9, 0xA6, 0xA9, 0xC3, +0x2D, 0x1D, 0x1B, 0x23, 0x41, 0xBC, 0xAF, 0xB5, 0xC2, 0xDE, 0x4F, 0x3B, 0x2D, 0x27, 0x2D, 0x49, +0xC3, 0xB5, 0xB9, 0xCB, 0x64, 0x54, 0x56, 0x51, 0x58, 0x75, 0xE3, 0xD1, 0xCA, 0xC1, 0xB6, 0xAB, +0xA6, 0xA3, 0xA6, 0xC0, 0x2B, 0x1C, 0x1A, 0x24, 0x4D, 0xB0, 0xAA, 0xB0, 0xC3, 0xF0, 0x46, 0x38, +0x2D, 0x28, 0x2B, 0x4C, 0xBC, 0xB2, 0xB6, 0xD1, 0x4B, 0x46, 0x4F, 0x58, 0x61, 0xEE, 0xE1, 0xDB, +0xCF, 0xCB, 0xBD, 0xAE, 0xA7, 0xA3, 0xA2, 0xB6, 0x2C, 0x1B, 0x19, 0x21, 0x52, 0xAB, 0xA5, 0xAE, +0xC9, 0x4F, 0x3B, 0x37, 0x32, 0x2A, 0x2A, 0x3D, 0xC9, 0xB2, 0xB1, 0xCA, 0x44, 0x3D, 0x49, 0x5F, +0xDD, 0xD8, 0x77, 0x6E, 0xED, 0xDB, 0xC7, 0xB7, 0xAB, 0xA5, 0xA3, 0xA6, 0xC8, 0x26, 0x1A, 0x1A, +0x27, 0xC5, 0xA4, 0xA3, 0xAF, 0xE9, 0x3B, 0x33, 0x34, 0x31, 0x2D, 0x31, 0x57, 0xBF, 0xB3, 0xBA, +0x78, 0x41, 0x3E, 0x4E, 0xDC, 0xCE, 0xD4, 0x78, 0x5B, 0x67, 0xDC, 0xC4, 0xB7, 0xAC, 0xA6, 0xA5, +0xA5, 0xBA, 0x2C, 0x1B, 0x19, 0x23, 0xD8, 0xA3, 0x9F, 0xAB, 0xDA, 0x34, 0x2D, 0x30, 0x33, 0x33, +0x37, 0x54, 0xC1, 0xB7, 0xBD, 0x6E, 0x41, 0x3D, 0x4A, 0xD8, 0xCA, 0xD0, 0xEB, 0x56, 0x54, 0x74, +0xCE, 0xBD, 0xAF, 0xA8, 0xA6, 0xA5, 0xAE, 0x3B, 0x1E, 0x19, 0x1E, 0x43, 0xA8, 0x9E, 0xA6, 0xC8, +0x33, 0x29, 0x2C, 0x32, 0x39, 0x3D, 0x4E, 0xCD, 0xBE, 0xC2, 0xFF, 0x40, 0x3D, 0x48, 0xE0, 0xC4, +0xCB, 0xEA, 0x4F, 0x48, 0x59, 0xD7, 0xBE, 0xB5, 0xAD, 0xA9, 0xA9, 0xA7, 0xB3, 0x34, 0x1D, 0x19, +0x20, 0xEE, 0xA2, 0x9D, 0xA8, 0x60, 0x2A, 0x26, 0x2D, 0x3B, 0x4A, 0x4E, 0x64, 0xCE, 0xC6, 0xCF, +0x57, 0x42, 0x47, 0x67, 0xCB, 0xC3, 0xD4, 0x5C, 0x48, 0x47, 0x63, 0xCA, 0xBA, 0xB2, 0xAE, 0xAC, +0xAC, 0xAB, 0xB3, 0x3D, 0x1F, 0x1B, 0x20, 0x51, 0xA5, 0x9E, 0xA8, 0x6C, 0x2A, 0x24, 0x2B, 0x3E, +0x5E, 0x6C, 0x66, 0xFA, 0xE0, 0xE4, 0x60, 0x4B, 0x4E, 0x71, 0xD2, 0xC8, 0xD8, 0x5A, 0x4A, 0x49, +0x6E, 0xC6, 0xB9, 0xB3, 0xB1, 0xAF, 0xAE, 0xAC, 0xAF, 0x48, 0x21, 0x1B, 0x1F, 0x4C, 0xA5, 0x9D, +0xA7, 0x64, 0x28, 0x23, 0x2C, 0x4A, 0xD6, 0xD9, 0x61, 0x59, 0x6C, 0xEF, 0xF8, 0x5B, 0x5B, 0xEE, +0xD5, 0xCD, 0xDB, 0x5C, 0x4D, 0x4C, 0x6E, 0xC8, 0xBB, 0xB8, 0xB8, 0xB6, 0xB3, 0xAD, 0xA8, 0xBC, +0x2D, 0x1D, 0x1B, 0x2B, 0xB7, 0x9F, 0xA1, 0xB9, 0x33, 0x23, 0x28, 0x3A, 0xEB, 0xCF, 0x77, 0x50, +0x56, 0x6E, 0xE2, 0x73, 0x55, 0x5D, 0xE6, 0xCF, 0xD9, 0x5B, 0x4B, 0x47, 0x59, 0xD2, 0xC3, 0xBD, +0xBD, 0xBC, 0xB6, 0xAE, 0xA9, 0xAB, 0x68, 0x24, 0x1B, 0x1E, 0x3C, 0xAA, 0x9E, 0xA6, 0xDE, 0x2A, +0x23, 0x2C, 0x47, 0xD1, 0xCE, 0x72, 0x55, 0x58, 0x6A, 0x78, 0x5D, 0x5B, 0xF9, 0xDA, 0xD3, 0xDE, +0x5A, 0x49, 0x4A, 0x5F, 0xD1, 0xC0, 0xBD, 0xBC, 0xB9, 0xB3, 0xAD, 0xA9, 0xB1, 0x3C, 0x1F, 0x1C, +0x24, 0xE1, 0xA4, 0x9F, 0xAD, 0x44, 0x26, 0x25, 0x32, 0x6E, 0xC3, 0xCD, 0x64, 0x4F, 0x4F, 0x62, +0x70, 0x5C, 0x68, 0xE1, 0xD4, 0xD3, 0xF6, 0x4E, 0x45, 0x4B, 0x78, 0xCA, 0xBE, 0xBC, 0xBC, 0xB8, +0xB3, 0xAC, 0xA9, 0xBC, 0x2F, 0x1E, 0x1C, 0x2A, 0xBC, 0xA0, 0xA1, 0xB7, 0x35, 0x24, 0x28, 0x3B, +0xD8, 0xC4, 0xDC, 0x53, 0x4B, 0x4F, 0x67, 0x6D, 0x65, 0xEA, 0xDB, 0xD5, 0xDA, 0x5E, 0x4B, 0x48, +0x53, 0xDB, 0xC4, 0xBD, 0xBD, 0xBB, 0xB5, 0xAE, 0xA8, 0xAB, 0x75, 0x25, 0x1B, 0x1E, 0x3D, 0xAA, +0x9E, 0xA6, 0xDC, 0x2B, 0x25, 0x2E, 0x4E, 0xCC, 0xCC, 0x66, 0x4B, 0x4C, 0x5A, 0x74, 0x6C, 0x6D, +0xDF, 0xD6, 0xD6, 0xED, 0x4D, 0x43, 0x46, 0x59, 0xD0, 0xBF, 0xBC, 0xBB, 0xB8, 0xB3, 0xAD, 0xA9, +0xB3, 0x38, 0x1E, 0x1B, 0x24, 0xCF, 0xA2, 0x9F, 0xAF, 0x3E, 0x26, 0x27, 0x36, 0x6A, 0xCD, 0xE6, +0x4F, 0x4D, 0x55, 0x6E, 0xEF, 0x63, 0x7B, 0xDA, 0xDB, 0xDF, 0x5F, 0x49, 0x47, 0x4F, 0xE8, 0xC5, +0xBD, 0xBC, 0xBB, 0xB6, 0xAF, 0xAA, 0xAA, 0xCD, 0x29, 0x1C, 0x1D, 0x34, 0xAE, 0x9F, 0xA4, 0xC7, +0x2E, 0x25, 0x2C, 0x41, 0xDB, 0xD2, 0x75, 0x5B, 0x59, 0x60, 0x6E, 0x5B, 0x5C, 0xDF, 0xD0, 0xD8, +0x74, 0x4B, 0x43, 0x4B, 0x6E, 0xCC, 0xBF, 0xBD, 0xBE, 0xBC, 0xB5, 0xAD, 0xA8, 0xAD, 0x4B, 0x22, +0x1C, 0x22, 0x65, 0xA6, 0x9F, 0xAC, 0x4C, 0x2A, 0x28, 0x33, 0x50, 0xDE, 0xF2, 0x65, 0x7C, 0xF8, +0x79, 0x5A, 0x4B, 0x5E, 0xD7, 0xCF, 0xD6, 0x5F, 0x45, 0x43, 0x4C, 0xFB, 0xC9, 0xBF, 0xBD, 0xBC, +0xB8, 0xB2, 0xAD, 0xAA, 0xB7, 0x38, 0x20, 0x1D, 0x29, 0xC6, 0xA5, 0xA4, 0xB8, 0x3C, 0x2A, 0x2C, +0x39, 0x53, 0x7F, 0x6F, 0x79, 0xE8, 0xF9, 0x61, 0x4D, 0x4B, 0xFF, 0xD0, 0xD3, 0xEC, 0x4F, 0x45, +0x49, 0x58, 0xDF, 0xC7, 0xBF, 0xBD, 0xBB, 0xB5, 0xAF, 0xA9, 0xAA, 0xCA, 0x2C, 0x1D, 0x1E, 0x36, +0xB1, 0xA2, 0xA8, 0xCE, 0x33, 0x2B, 0x30, 0x3E, 0x4F, 0x5A, 0x6D, 0xD8, 0xD2, 0xF5, 0x4F, 0x46, +0x54, 0xD4, 0xCA, 0xD7, 0x65, 0x48, 0x43, 0x4C, 0x61, 0xD7, 0xC4, 0xBF, 0xBD, 0xB7, 0xB1, 0xAD, +0xAA, 0xB0, 0x48, 0x23, 0x1D, 0x24, 0x5D, 0xAA, 0xA4, 0xB0, 0x56, 0x30, 0x2E, 0x34, 0x3E, 0x47, +0x52, 0xE3, 0xC9, 0xCE, 0x65, 0x45, 0x43, 0x6A, 0xCE, 0xCD, 0xDE, 0x58, 0x49, 0x49, 0x4F, 0x6D, +0xD1, 0xC0, 0xBB, 0xB6, 0xAF, 0xAE, 0xAA, 0xAB, 0xCF, 0x2B, 0x1D, 0x1E, 0x36, 0xB2, 0xA4, 0xA9, +0xC7, 0x3B, 0x2F, 0x31, 0x37, 0x3D, 0x46, 0x6E, 0xC7, 0xC5, 0xE5, 0x4C, 0x41, 0x50, 0xD5, 0xCB, +0xDF, 0x58, 0x4C, 0x49, 0x4E, 0x62, 0xE4, 0xC8, 0xBD, 0xB9, 0xB4, 0xAF, 0xAD, 0xA9, 0xB2, 0x41, +0x22, 0x1D, 0x26, 0xEB, 0xAA, 0xA6, 0xB3, 0x5D, 0x38, 0x32, 0x32, 0x34, 0x39, 0x4D, 0xCC, 0xBD, +0xC7, 0x60, 0x44, 0x47, 0x6D, 0xDC, 0xE2, 0xEF, 0x63, 0x58, 0x50, 0x4D, 0x5A, 0xDB, 0xC3, 0xBA, +0xB3, 0xAF, 0xAD, 0xAC, 0xAE, 0xD3, 0x2D, 0x1F, 0x20, 0x38, 0xB9, 0xA9, 0xAD, 0xC4, 0x4C, 0x39, +0x33, 0x30, 0x32, 0x3C, 0xFA, 0xBE, 0xBE, 0xD5, 0x51, 0x46, 0x4F, 0x6F, 0x7B, 0x65, 0x68, 0x76, +0x60, 0x54, 0x52, 0x61, 0xCF, 0xBF, 0xB7, 0xAE, 0xAC, 0xAB, 0xAB, 0xBC, 0x39, 0x22, 0x1E, 0x2B, +0xE7, 0xAF, 0xAC, 0xB6, 0xD1, 0x4E, 0x3A, 0x2F, 0x2D, 0x31, 0x45, 0xCC, 0xBC, 0xC2, 0xDA, 0x58, +0x4D, 0x55, 0x52, 0x52, 0x68, 0xED, 0xF4, 0x66, 0x55, 0x59, 0xEF, 0xCE, 0xBF, 0xB5, 0xAD, 0xAB, +0xAB, 0xAE, 0xCC, 0x31, 0x22, 0x22, 0x2F, 0xE8, 0xB6, 0xB1, 0xB8, 0xC4, 0x73, 0x3D, 0x2F, 0x2B, +0x2F, 0x3F, 0xDD, 0xC0, 0xBF, 0xC7, 0xD8, 0x73, 0x55, 0x46, 0x44, 0x53, 0x7B, 0xEE, 0xED, 0xF2, +0xED, 0xDC, 0xCF, 0xC1, 0xB3, 0xAC, 0xA9, 0xA9, 0xB2, 0x58, 0x2C, 0x24, 0x28, 0x36, 0x5C, 0xC7, +0xBA, 0xB6, 0xB7, 0xCA, 0x49, 0x34, 0x2D, 0x2E, 0x37, 0x49, 0xE0, 0xC4, 0xBD, 0xBE, 0xC7, 0xEA, +0x4C, 0x42, 0x44, 0x49, 0x50, 0x69, 0xDD, 0xD0, 0xCB, 0xC6, 0xBD, 0xB3, 0xAD, 0xAB, 0xAB, 0xBA, +0x49, 0x2F, 0x2B, 0x2D, 0x32, 0x3A, 0x4B, 0xD6, 0xBC, 0xB6, 0xBD, 0xD7, 0x51, 0x3D, 0x36, 0x34, +0x37, 0x3E, 0x52, 0xDE, 0xC8, 0xC3, 0xC7, 0xCE, 0xDE, 0x72, 0x56, 0x4F, 0x53, 0x5D, 0xFF, 0xDA, +0xC9, 0xBD, 0xB7, 0xB4, 0xB2, 0xB6, 0xC6, 0x62, 0x47, 0x3F, 0x3D, 0x3B, 0x3B, 0x40, 0x4E, 0x6F, +0xEC, 0x7B, 0x67, 0x5E, 0x5B, 0x59, 0x55, 0x5A, 0x65, 0x5E, 0x5A, 0x5F, 0x61, 0x5E, 0x63, 0x65, +0x61, 0x6D, 0xF5, 0xFA, 0x7A, 0x77, 0x77, 0xFF, 0xEB, 0xDC, 0xD5, 0xCE, 0xCA, 0xC8, 0xC8, 0xCD, +0xD9, 0xEC, 0x76, 0x70, 0xFD, 0xED, 0xEA, 0xEE, 0x7D, 0x66, 0x59, 0x53, 0x50, 0x4F, 0x50, 0x54, +0x56, 0x58, 0x59, 0x5A, 0x5A, 0x5C, 0x5F, 0x6B, 0xF6, 0xE5, 0xE1, 0xE7, 0xEF, 0xF5, 0xF2, 0xEA, +0xDF, 0xD8, 0xCE, 0xC8, 0xC4, 0xC5, 0xCB, 0xD4, 0xDF, 0xE9, 0xE7, 0xE1, 0xDD, 0xDB, 0xDE, 0xEE, +0x68, 0x57, 0x4F, 0x4D, 0x4D, 0x4E, 0x4F, 0x4F, 0x52, 0x54, 0x53, 0x51, 0x53, 0x57, 0x5E, 0x6F, +0xFA, 0xEF, 0xEF, 0xF3, 0xF1, 0xF1, 0xEE, 0xE6, 0xDE, 0xD6, 0xCF, 0xCD, 0xCE, 0xD3, 0xDA, 0xDC, +0xDF, 0xDF, 0xD9, 0xD5, 0xD4, 0xD7, 0xE0, 0xFE, 0x6B, 0x5F, 0x5A, 0x58, 0x56, 0x54, 0x54, 0x53, +0x53, 0x50, 0x4E, 0x4F, 0x52, 0x56, 0x5C, 0x64, 0x67, 0x66, 0x69, 0x6C, 0x6D, 0x74, 0xFA, 0xEC, +0xDF, 0xD7, 0xD6, 0xDD, 0xE6, 0xF3, 0x7B, 0xF7, 0xE8, 0xDC, 0xD3, 0xD0, 0xD2, 0xD9, 0xE1, 0xE7, +0xEB, 0xF6, 0x7B, 0x74, 0x6F, 0x6B, 0x66, 0x5E, 0x59, 0x55, 0x53, 0x52, 0x56, 0x59, 0x5C, 0x61, +0x5F, 0x64, 0x65, 0x62, 0x6F, 0x7B, 0xFC, 0xE4, 0xDC, 0xD9, 0xE2, 0x7E, 0xFF, 0xFE, 0xFF, 0xEE, +0xDF, 0xD8, 0xD6, 0xD7, 0xDB, 0xDD, 0xDD, 0xE8, 0xE7, 0xE3, 0xEA, 0xE7, 0xF9, 0x72, 0x7C, 0x5F, +0x56, 0x58, 0x58, 0x56, 0x57, 0x57, 0x53, 0x54, 0x4A, 0x42, 0x4F, 0x67, 0x6A, 0xE0, 0xD4, 0xD8, +0xD7, 0xDD, 0xFD, 0xFE, 0xE4, 0x7B, 0xFF, 0xD9, 0xDF, 0xDE, 0xD7, 0xDA, 0xCF, 0xCF, 0xD3, 0xCD, +0xD3, 0xDF, 0xDD, 0xDE, 0xF3, 0x6D, 0x5F, 0x55, 0x54, 0x55, 0x51, 0x51, 0x50, 0x4F, 0x51, 0x53, +0x57, 0x59, 0x60, 0x73, 0xFB, 0xE7, 0xE1, 0xEA, 0xF4, 0x7D, 0x6D, 0x6F, 0x7F, 0x78, 0xF5, 0xE7, +0xEA, 0xE0, 0xDE, 0xD9, 0xCD, 0xCB, 0xC5, 0xC2, 0xC5, 0xCB, 0xDB, 0xDD, 0xEC, 0x53, 0x4C, 0x45, +0x42, 0x49, 0x48, 0x49, 0x4D, 0x51, 0x5A, 0x55, 0x55, 0x5D, 0x64, 0xF4, 0xED, 0xF5, 0xEB, 0xF2, +0x7A, 0x6D, 0x60, 0x5E, 0x6E, 0xF3, 0xF7, 0xE9, 0xDF, 0xDD, 0xD4, 0xCD, 0xC8, 0xC1, 0xBC, 0xB8, +0xB5, 0xB8, 0xC7, 0xF5, 0x52, 0x3E, 0x38, 0x37, 0x36, 0x3D, 0x4B, 0x59, 0xFD, 0xDF, 0xE3, 0xFE, +0x5C, 0x54, 0x52, 0x4E, 0x50, 0x55, 0x5D, 0xFF, 0xF2, 0xF1, 0xE9, 0xFB, 0xFD, 0x74, 0x5E, 0x62, +0x7A, 0xE3, 0xD2, 0xCA, 0xC0, 0xB9, 0xB1, 0xAD, 0xAE, 0xBC, 0x68, 0x3E, 0x31, 0x2D, 0x2E, 0x30, +0x3B, 0x5B, 0xD4, 0xC7, 0xC9, 0xD6, 0x65, 0x4A, 0x46, 0x3E, 0x3D, 0x47, 0x51, 0x7F, 0xD5, 0xD0, +0xCE, 0xD2, 0xE2, 0x6E, 0x57, 0x55, 0x61, 0xEA, 0xCD, 0xBE, 0xB7, 0xB2, 0xAD, 0xAB, 0xB0, 0xCE, +0x40, 0x2F, 0x2B, 0x2D, 0x2F, 0x38, 0x59, 0xCA, 0xBD, 0xBE, 0xCF, 0x70, 0x45, 0x39, 0x3A, 0x3B, +0x3F, 0x5D, 0xD9, 0xCB, 0xC4, 0xC5, 0xCF, 0xEA, 0x62, 0x52, 0x50, 0x5C, 0x7E, 0xD1, 0xBE, 0xB5, +0xAF, 0xAE, 0xAB, 0xAC, 0xC2, 0x44, 0x2F, 0x2A, 0x2D, 0x32, 0x38, 0x55, 0xC6, 0xBA, 0xBC, 0xCF, +0x5B, 0x40, 0x36, 0x34, 0x38, 0x40, 0x66, 0xCD, 0xC4, 0xC2, 0xC6, 0xD1, 0xFE, 0x52, 0x4C, 0x4F, +0x5C, 0xEF, 0xCF, 0xC1, 0xB7, 0xAF, 0xAD, 0xAB, 0xAD, 0xC9, 0x3C, 0x2D, 0x29, 0x2D, 0x35, 0x3D, +0xF1, 0xBD, 0xB8, 0xBF, 0xE1, 0x4C, 0x3C, 0x32, 0x31, 0x39, 0x47, 0xF7, 0xCB, 0xC4, 0xC2, 0xC7, +0xD9, 0x6F, 0x54, 0x4D, 0x4F, 0x5D, 0xEF, 0xCF, 0xC1, 0xB8, 0xAF, 0xAD, 0xAB, 0xAC, 0xC9, 0x3A, +0x2C, 0x29, 0x2D, 0x37, 0x44, 0xD8, 0xB9, 0xB7, 0xC4, 0x6C, 0x44, 0x38, 0x31, 0x30, 0x39, 0x4E, +0xDF, 0xCA, 0xC5, 0xC6, 0xCD, 0xEF, 0x5E, 0x5A, 0x53, 0x55, 0x64, 0xE8, 0xCD, 0xC4, 0xBB, 0xB0, +0xAC, 0xAA, 0xAC, 0xD0, 0x36, 0x2C, 0x2B, 0x2E, 0x39, 0x4A, 0xCB, 0xB6, 0xB7, 0xC8, 0x5D, 0x43, +0x39, 0x31, 0x32, 0x3D, 0x58, 0xD9, 0xCD, 0xCA, 0xC9, 0xD6, 0x73, 0x62, 0x63, 0x62, 0x68, 0x72, +0xE9, 0xCE, 0xC6, 0xBB, 0xAF, 0xAD, 0xAB, 0xAE, 0xD7, 0x38, 0x2D, 0x2C, 0x2F, 0x3B, 0x4F, 0xCA, +0xB6, 0xB8, 0xCB, 0x5D, 0x41, 0x39, 0x32, 0x33, 0x3E, 0x59, 0xDC, 0xCF, 0xCD, 0xCB, 0xD8, 0x63, +0x53, 0x5A, 0x5E, 0x61, 0x6F, 0xEA, 0xCF, 0xC6, 0xBC, 0xB0, 0xAD, 0xAB, 0xAE, 0xEE, 0x32, 0x2C, +0x2C, 0x30, 0x3B, 0x50, 0xC9, 0xB6, 0xB9, 0xCF, 0x5D, 0x47, 0x3A, 0x32, 0x33, 0x3F, 0x63, 0xDB, +0xD5, 0xCC, 0xC8, 0xD7, 0x66, 0x56, 0x58, 0x5C, 0x5A, 0x5B, 0x7E, 0xCE, 0xC4, 0xBB, 0xB0, 0xAD, +0xAB, 0xAF, 0x7B, 0x2F, 0x2C, 0x2E, 0x31, 0x39, 0x52, 0xC6, 0xB6, 0xBA, 0xD1, 0x5C, 0x4E, 0x3E, +0x32, 0x33, 0x3F, 0x64, 0xEA, 0xFD, 0xCE, 0xC2, 0xD0, 0x6C, 0x57, 0x5D, 0x64, 0x56, 0x53, 0x6C, +0xCE, 0xC4, 0xBD, 0xB1, 0xAD, 0xAA, 0xAE, 0x6F, 0x31, 0x2F, 0x31, 0x31, 0x37, 0x4F, 0xC5, 0xB6, +0xBA, 0xD2, 0xFC, 0x60, 0x45, 0x36, 0x35, 0x41, 0x56, 0xF8, 0xF2, 0xE4, 0xC6, 0xC8, 0x7C, 0x5A, +0x6A, 0x6A, 0x57, 0x52, 0x5B, 0xDA, 0xC6, 0xC0, 0xB6, 0xAE, 0xAA, 0xAC, 0xDD, 0x33, 0x30, 0x35, +0x31, 0x30, 0x42, 0xCE, 0xBA, 0xBA, 0xCC, 0xD9, 0xD9, 0x53, 0x3A, 0x35, 0x3E, 0x4D, 0x52, 0x60, +0xD6, 0xC5, 0xD2, 0x71, 0xE7, 0x7C, 0x55, 0x53, 0x51, 0x5B, 0xEE, 0xDC, 0xD3, 0xBE, 0xB2, 0xB0, +0xAD, 0xAE, 0x67, 0x31, 0x38, 0x39, 0x2F, 0x32, 0x49, 0xD5, 0xBE, 0xBF, 0xCF, 0xCD, 0xCA, 0x5C, +0x3D, 0x3C, 0x41, 0x41, 0x47, 0x5D, 0xE1, 0xC9, 0xC7, 0xD4, 0xDD, 0xFC, 0x51, 0x4C, 0x4C, 0x4C, +0x5F, 0xE2, 0xDC, 0xCA, 0xBF, 0xBD, 0xB9, 0xB5, 0xAF, 0xBA, 0x43, 0x35, 0x44, 0x3E, 0x2F, 0x36, +0x53, 0xD4, 0xC8, 0xCC, 0xD1, 0xC3, 0xC5, 0x5E, 0x41, 0x45, 0x49, 0x42, 0x44, 0x55, 0xE9, 0xD0, +0xD1, 0xDA, 0xD6, 0xD7, 0xFD, 0x5A, 0x58, 0x5B, 0x5A, 0x5B, 0x5F, 0x6C, 0x7A, 0xE1, 0xD3, 0xD2, +0xCC, 0xC8, 0xC4, 0xBF, 0xBD, 0xBA, 0xC2, 0x49, 0x3C, 0x4C, 0x41, 0x35, 0x3B, 0x52, 0xF3, 0xD6, +0xD1, 0xCE, 0xC3, 0xC6, 0x7A, 0x53, 0x57, 0x4E, 0x44, 0x46, 0x4F, 0x64, 0xE8, 0xDF, 0xDD, 0xD4, +0xD6, 0xEC, 0x56, 0x49, 0x54, 0x5E, 0x4F, 0x57, 0xFA, 0xE1, 0xDC, 0xE0, 0xE3, 0xD6, 0xD0, 0xDC, +0xD9, 0xC9, 0xC2, 0xBD, 0xC5, 0x58, 0x50, 0xE6, 0x4B, 0x37, 0x3F, 0x4F, 0x4D, 0x52, 0x6D, 0xDD, +0xC8, 0xC8, 0xDC, 0xDC, 0xD1, 0x72, 0x4A, 0x4A, 0x4E, 0x4B, 0x4E, 0x57, 0x6A, 0xDF, 0xD7, 0xDB, +0xD5, 0xCF, 0xDA, 0xF4, 0x74, 0x67, 0x5E, 0x5E, 0x5E, 0x66, 0xF9, 0xED, 0xEC, 0xE8, 0xE4, 0xE8, +0xF4, 0xF7, 0x60, 0x52, 0x6D, 0xF3, 0x5B, 0x66, 0xE7, 0xE1, 0xE0, 0xDF, 0xDF, 0xD8, 0xD1, 0xEA, +0x6B, 0xD9, 0xD1, 0xE5, 0xE6, 0xCC, 0xC4, 0xE7, 0x52, 0xEA, 0xE4, 0x4C, 0x48, 0x59, 0x5D, 0x59, +0x5D, 0x60, 0xF7, 0xDF, 0x79, 0x6A, 0xDF, 0xDC, 0xFE, 0xF8, 0xE2, 0xF0, 0x6F, 0x6B, 0x61, 0x5F, +0x62, 0x5D, 0x5E, 0x6C, 0x76, 0x70, 0xF7, 0xE5, 0xE6, 0xE7, 0xE5, 0xEA, 0xF4, 0x7D, 0x6C, 0x66, +0x68, 0x66, 0x66, 0x73, 0xF8, 0xF3, 0xF4, 0xF6, 0xFB, 0x7E, 0x7A, 0x7E, 0xF6, 0xEF, 0xED, 0xE9, +0xE9, 0xEF, 0xFB, 0x78, 0x6E, 0x69, 0x64, 0x63, 0x68, 0x6E, 0x74, 0x7B, 0xFA, 0xF6, 0xF3, 0xFF, +0x6B, 0x69, 0x69, 0x60, 0x5F, 0x65, 0x6C, 0x73, 0x7C, 0xF3, 0xEB, 0xE9, 0xEA, 0xE8, 0xE6, 0xE6, +0xE7, 0xE4, 0xDE, 0xDD, 0xE2, 0xE2, 0xE1, 0xE9, 0xF8, 0x77, 0x6B, 0x62, 0x5D, 0x5B, 0x5A, 0x59, +0x5A, 0x5B, 0x5D, 0x5E, 0x5E, 0x5E, 0x5F, 0x62, 0x67, 0x6D, 0x78, 0xFA, 0xF2, 0xEE, 0xEF, 0xF3, +0xF7, 0xF8, 0xFA, 0xFE, 0xFA, 0xF3, 0xF1, 0xEF, 0xEA, 0xE7, 0xE5, 0xE6, 0xE9, 0xEF, 0xFB, 0x79, +0x70, 0x6C, 0x6A, 0x6C, 0x6F, 0x77, 0x7C, 0xFD, 0xFB, 0x7F, 0x73, 0x6C, 0x69, 0x69, 0x6C, 0x73, +0xFA, 0xED, 0xE9, 0xE5, 0xE1, 0xE4, 0xE8, 0xE8, 0xE7, 0xE7, 0xE5, 0xE2, 0xE0, 0xDD, 0xDC, 0xE1, +0xE3, 0xE2, 0xEC, 0x7C, 0x6C, 0x63, 0x5D, 0x5B, 0x59, 0x58, 0x5A, 0x5B, 0x5B, 0x5C, 0x5F, 0x60, +0x5F, 0x60, 0x65, 0x6A, 0x75, 0xF8, 0xEC, 0xE7, 0xE6, 0xE7, 0xEA, 0xF0, 0xF8, 0xF9, 0xF4, 0xED, +0xE9, 0xE5, 0xE3, 0xE3, 0xE6, 0xEB, 0xF6, 0x76, 0x6C, 0x67, 0x62, 0x5F, 0x61, 0x64, 0x69, 0x6F, +0x73, 0x73, 0x75, 0x75, 0x70, 0x6E, 0x6F, 0x6E, 0x6E, 0x75, 0xFE, 0xF3, 0xEC, 0xE9, 0xE9, 0xE9, +0xED, 0xFA, 0x7A, 0x74, 0x71, 0x72, 0x73, 0x74, 0x7A, 0xFF, 0xFF, 0x7B, 0x72, 0x6E, 0x6D, 0x6C, +0x6C, 0x6E, 0x73, 0x7C, 0xFC, 0xF7, 0xF0, 0xEB, 0xE6, 0xE0, 0xDD, 0xDB, 0xDB, 0xDB, 0xDC, 0xDF, +0xE2, 0xE3, 0xE8, 0xF2, 0x7D, 0x6C, 0x61, 0x5D, 0x5A, 0x58, 0x59, 0x5C, 0x5E, 0x61, 0x66, 0x69, +0x6A, 0x6B, 0x6C, 0x6B, 0x6C, 0x75, 0xFE, 0xF7, 0xEE, 0xEA, 0xE8, 0xE6, 0xE9, 0xEC, 0xEB, 0xEB, +0xEE, 0xF0, 0xF2, 0xF6, 0xF9, 0xFD, 0x7F, 0xFF, 0x7C, 0x74, 0x6D, 0x69, 0x66, 0x64, 0x64, 0x68, +0x6B, 0x6D, 0x75, 0x7D, 0xFC, 0xF8, 0xFA, 0xFC, 0xFA, 0xF7, 0xF5, 0xF0, 0xEB, 0xE6, 0xE4, 0xE5, +0xE9, 0xED, 0xF2, 0xFC, 0x7A, 0x74, 0x6E, 0x6C, 0x6A, 0x69, 0x69, 0x6A, 0x6B, 0x6D, 0x6E, 0x6E, +0x6D, 0x6E, 0x70, 0x71, 0x74, 0x79, 0x7B, 0x7E, 0xFB, 0xF6, 0xF8, 0xF9, 0xF6, 0xF5, 0xF8, 0xFA, +0xF7, 0xF0, 0xEE, 0xF0, 0xF7, 0xFC, 0x7D, 0x73, 0x6C, 0x6B, 0x6B, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, +0x6C, 0x6D, 0x6D, 0x6E, 0x71, 0x71, 0x71, 0x72, 0x77, 0x7D, 0xFC, 0xF7, 0xF3, 0xED, 0xEB, 0xEB, +0xEE, 0xF4, 0xF9, 0xFE, 0x79, 0x77, 0x7B, 0x7F, 0x7F, 0xFF, 0xFF, 0x7B, 0x77, 0x76, 0x77, 0x79, +0x79, 0x7A, 0x7C, 0xFF, 0xFC, 0xFC, 0xFA, 0xF8, 0xF9, 0xF9, 0xFA, 0xFD, 0xFE, 0xFD, 0xFA, 0xF8, +0xF5, 0xF2, 0xF1, 0xF2, 0xF4, 0xF8, 0xFE, 0x7C, 0x79, 0x77, 0x77, 0x7A, 0x7E, 0x7F, 0x7C, 0x79, +0x78, 0x75, 0x73, 0x72, 0x71, 0x73, 0x75, 0x76, 0x78, 0x7D, 0xFD, 0xFB, 0xFC, 0xFF, 0x7D, 0x79, +0x76, 0x76, 0x77, 0x7C, 0xFB, 0xF5, 0xF3, 0xF3, 0xF3, 0xF7, 0x7D, 0x74, 0x6F, 0x6D, 0x6B, 0x6C, +0x6E, 0x73, 0x77, 0x77, 0x77, 0x7A, 0x7A, 0x76, 0x73, 0x72, 0x6F, 0x6D, 0x6B, 0x6C, 0x6F, 0x76, +0x7A, 0x7D, 0xFE, 0xFC, 0x7E, 0x7A, 0x75, 0x71, 0x6E, 0x6D, 0x6E, 0x71, 0x77, 0x7D, 0xFE, 0xFC, +0xFC, 0xFE, 0xFF, 0x7D, 0x7B, 0x7C, 0x7E, 0xFF, 0xFF, 0xFC, 0xF9, 0xF7, 0xF6, 0xF8, 0xFA, 0xFC, +0xFF, 0x7E, 0x7F, 0xFF, 0xFD, 0xFB, 0xF7, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFD, 0x7D, 0x7A, 0x7A, +0x7D, 0x7E, 0x7F, 0xFF, 0xFE, 0xFE, 0x7E, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0xFF, 0xFD, 0xFD, 0xFD, +0xFB, 0xF8, 0xF7, 0xF9, 0xFD, 0x7C, 0x79, 0x78, 0x78, 0x77, 0x77, 0x76, 0x75, 0x75, 0x75, 0x75, +0x75, 0x74, 0x76, 0x78, 0x79, 0x7B, 0x7D, 0x7F, 0xFD, 0xFB, 0xFF, 0x7A, 0x79, 0x7D, 0xFE, 0xFD, +0xFD, 0xFC, 0xFD, 0xFA, 0xF5, 0xF2, 0xF3, 0xF6, 0xF8, 0xF9, 0xFC, 0xFF, 0xFD, 0xF8, 0xF5, 0xF5, +0xF6, 0xFA, 0xFC, 0xFD, 0x7F, 0x7A, 0x78, 0x76, 0x75, 0x75, 0x77, 0x7B, 0x7E, 0x7E, 0x7E, 0x7F, +0x7E, 0x7C, 0x79, 0x76, 0x76, 0x78, 0x79, 0x78, 0x7B, 0x7E, 0xFF, 0x7F, 0x7C, 0x7A, 0x78, 0x75, +0x73, 0x73, 0x74, 0x76, 0x77, 0x78, 0x7B, 0x7B, 0x79, 0x79, 0x78, 0x77, 0x78, 0x74, 0x71, 0x72, +0x73, 0x76, 0x77, 0x77, 0x79, 0x79, 0x77, 0x76, 0x75, 0x76, 0x78, 0x7B, 0x7F, 0xFC, 0xFB, 0xFB, +0xFA, 0xFB, 0xFE, 0x7C, 0x79, 0x79, 0x78, 0x78, 0x7B, 0xFF, 0xFD, 0xFD, 0xFC, 0xFC, 0xFE, 0x7E, +0x7A, 0x78, 0x77, 0x75, 0x75, 0x76, 0x78, 0x79, 0x7A, 0x7C, 0x7C, 0x7C, 0x7B, 0x77, 0x78, 0x7A, +0x7A, 0x7A, 0x7C, 0x7E, 0xFF, 0x7F, 0x7F, 0xFF, 0xFF, 0x7F, 0x7E, 0x7F, 0x7E, 0x7C, 0x7A, 0x7A, +0x79, 0x79, 0x7B, 0x7E, 0x7E, 0x7F, 0xFD, 0xFC, 0xFD, 0xFD, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, +0xFF, 0x7E, 0x7D, 0x7F, 0xFF, 0xFF, 0xFE, 0xFC, 0xFB, 0xFC, 0xFC, 0xFE, 0x7E, 0x7F, 0x7E, 0x7B, +0x7C, 0x7D, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7A, 0x7A, 0x79, 0x78, 0x77, 0x76, 0x77, 0x7A, +0x7C, 0x7D, 0x7E, 0x7E, 0x7C, 0x79, 0x76, 0x77, 0x79, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0x7D, +0x7D, 0x7C, 0x7A, 0x78, 0x78, 0x78, 0x79, 0x7A, 0x7C, 0x7E, 0x7D, 0x7E, 0x7E, 0x7C, 0x7B, 0x7C, +0x7C, 0x7C, 0x7D, 0xFF, 0xFC, 0xFB, 0xFB, 0xFC, 0xFF, 0x7C, 0x79, 0x77, 0x74, 0x75, 0x77, 0x7A, +0x7D, 0x7E, 0xFF, 0xFD, 0xFD, 0xFE, 0x7E, 0x7D, 0x7D, 0x7B, 0x7A, 0x7B, 0x7D, 0x7E, 0xFE, 0xFD, +0xFF, 0x7F, 0x7D, 0x7B, 0x7A, 0x78, 0x76, 0x76, 0x79, 0x7C, 0x7E, 0xFF, 0xFF, 0x7F, 0x7E, 0x7C, +0x7A, 0x79, 0x78, 0x78, 0x77, 0x78, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0x7F, 0x7E, 0x7D, 0x7B, 0x7A, +0x7B, 0x7C, 0x7D, 0xFE, 0xFC, 0xFA, 0xF9, 0xFA, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFA, 0xF8, 0xF7, +0xF6, 0xF7, 0xF9, 0xFB, 0xFE, 0x7E, 0x7C, 0x7A, 0x79, 0x79, 0x7B, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, +0x7E, 0x7C, 0x7B, 0x79, 0x7A, 0x7C, 0x7C, 0x7E, 0x7E, 0x7F, 0xFF, 0x7F, 0x7E, 0x7C, 0x7B, 0x79, +0x77, 0x76, 0x76, 0x78, 0x7B, 0x7B, 0x7B, 0x7A, 0x7A, 0x7A, 0x77, 0x77, 0x78, 0x78, 0x79, 0x7B, +0x7D, 0x79, 0x79, 0x7E, 0x79, 0x78, 0x79, 0x78, 0x7A, 0x78, 0x7D, 0x7D, 0x7C, 0xFF, 0x7D, 0x7F, +0x7E, 0x7B, 0x7A, 0x76, 0x79, 0x76, 0x78, 0x7B, 0x79, 0xFF, 0x7D, 0xFB, 0xFD, 0x7D, 0xF9, 0x7B, +0xFF, 0x79, 0x75, 0xFF, 0x6E, 0x7F, 0x71, 0x73, 0x79, 0x51, 0x60, 0xE2, 0x6F, 0x7D, 0xF7, 0xF5, +0x77, 0x72, 0xE4, 0x50, 0x4C, 0xDE, 0xFB, 0x66, 0xE5, 0xF6, 0x6F, 0x6C, 0xE7, 0xDC, 0x6E, 0xFD, +0xE6, 0x7E, 0xE9, 0x77, 0x62, 0xE3, 0xE9, 0x6C, 0x71, 0xEA, 0xDE, 0x75, 0x7E, 0xE8, 0x75, 0xF8, +0x7A, 0xFC, 0xF2, 0x65, 0xF8, 0xEE, 0xF9, 0xF4, 0x73, 0xE4, 0xF9, 0x6E, 0xE5, 0x75, 0x67, 0x5A, +0x50, 0x57, 0x5A, 0x7D, 0x6C, 0x64, 0xDA, 0xED, 0x64, 0xEA, 0xE9, 0xF5, 0x7E, 0x6F, 0xFC, 0xF2, +0x66, 0x63, 0xF7, 0x7D, 0x78, 0xF6, 0xF8, 0xE1, 0xE2, 0xF9, 0xEB, 0xE4, 0xED, 0xFE, 0xFA, 0xF6, +0xF1, 0xF2, 0x6F, 0xFE, 0xE8, 0xFF, 0x7F, 0xEA, 0xE8, 0xED, 0xEE, 0xEA, 0xEB, 0xED, 0xF8, 0xEF, +0xE8, 0x7F, 0xF5, 0xE6, 0xF7, 0xF2, 0xF3, 0xF2, 0xDF, 0xEB, 0xF5, 0xE3, 0xEC, 0xF2, 0xEF, 0xFC, +0xED, 0xEF, 0xFD, 0xED, 0xF0, 0x7C, 0x77, 0xFE, 0xF1, 0x77, 0x79, 0xEF, 0xF6, 0xF8, 0xFB, 0x72, +0x75, 0x79, 0x78, 0x70, 0x6E, 0x7C, 0x7D, 0x74, 0x79, 0x77, 0x6E, 0x71, 0x7D, 0x6F, 0x6B, 0x7D, +0xFE, 0x76, 0x7A, 0x7D, 0x7F, 0x75, 0x6C, 0x7B, 0xF7, 0x7A, 0x7B, 0x7E, 0x74, 0x79, 0x75, 0x70, +0x7C, 0x79, 0xFD, 0xF6, 0x6E, 0x6E, 0x7B, 0x6B, 0x67, 0x6D, 0x6E, 0x6A, 0x67, 0x6A, 0x6D, 0x6F, +0x6B, 0x67, 0x6D, 0x6C, 0x65, 0x63, 0x5E, 0x65, 0x6A, 0x5F, 0x68, 0x6C, 0x60, 0x6C, 0x7D, 0x6D, +0x6E, 0x7D, 0x7C, 0xFF, 0xEE, 0xEB, 0xEC, 0xE3, 0xDE, 0xE1, 0xE4, 0xE0, 0xE8, 0xF8, 0xEE, 0xFA, +0x6D, 0x69, 0x5C, 0x58, 0x59, 0x53, 0x50, 0x4E, 0x4D, 0x4E, 0x4F, 0x4F, 0x53, 0x51, 0x51, 0x54, +0x55, 0x56, 0x5A, 0x5E, 0x69, 0xF7, 0xE2, 0xD8, 0xCF, 0xCE, 0xCD, 0xCC, 0xCE, 0xCE, 0xCA, 0xC7, +0xC2, 0xBF, 0xBE, 0xBD, 0xC2, 0xD5, 0xF1, 0x62, 0x4B, 0x3F, 0x3D, 0x3D, 0x3E, 0x40, 0x42, 0x4A, +0x51, 0x51, 0x51, 0x55, 0x56, 0x4F, 0x4D, 0x4E, 0x50, 0x52, 0x55, 0x5C, 0x69, 0xFA, 0xE4, 0xD9, +0xCD, 0xC9, 0xC5, 0xC1, 0xBF, 0xBB, 0xBB, 0xBD, 0xB9, 0xB7, 0xB5, 0xB7, 0xC8, 0xDD, 0x61, 0x43, +0x3C, 0x38, 0x38, 0x3D, 0x40, 0x46, 0x4E, 0x5A, 0x60, 0x5D, 0x5B, 0x5B, 0x5A, 0x58, 0x56, 0x58, +0x5C, 0x5E, 0x5E, 0x64, 0x6B, 0x6D, 0xF9, 0xED, 0xE5, 0xDC, 0xD9, 0xD2, 0xCC, 0xC5, 0xBF, 0xBF, +0xBA, 0xB7, 0xB6, 0xB1, 0xB7, 0xCB, 0xFA, 0x49, 0x3A, 0x35, 0x31, 0x33, 0x3A, 0x42, 0x4C, 0x5C, +0xFD, 0xE1, 0xEE, 0x68, 0x66, 0x5C, 0x51, 0x4F, 0x4D, 0x55, 0x5F, 0x5D, 0x64, 0x71, 0xFA, 0xED, +0xEF, 0xEB, 0xDE, 0xD3, 0xCC, 0xC3, 0xBE, 0xBD, 0xB9, 0xB8, 0xB5, 0xAF, 0xB7, 0xCC, 0x60, 0x3F, +0x39, 0x34, 0x2F, 0x31, 0x3A, 0x47, 0x56, 0x6F, 0xE3, 0xDA, 0xD5, 0xEC, 0x56, 0x50, 0x4E, 0x4B, +0x4B, 0x4C, 0x57, 0x7B, 0xE7, 0xE0, 0xDC, 0xD5, 0xD6, 0xDE, 0xEC, 0xEF, 0xDE, 0xD7, 0xCC, 0xC1, +0xBD, 0xB8, 0xB5, 0xB4, 0xB0, 0xB6, 0xCB, 0x58, 0x3E, 0x38, 0x34, 0x2F, 0x2F, 0x38, 0x45, 0x59, +0x7D, 0xDD, 0xD1, 0xCE, 0xDD, 0x63, 0x53, 0x4E, 0x4C, 0x4A, 0x4B, 0x53, 0x69, 0xE7, 0xDB, 0xD5, +0xD0, 0xCE, 0xD3, 0xDC, 0xE4, 0xEA, 0xE8, 0xE2, 0xD5, 0xC8, 0xC0, 0xBB, 0xB8, 0xB5, 0xB2, 0xB9, +0xD1, 0x54, 0x40, 0x3A, 0x34, 0x2F, 0x2F, 0x36, 0x3F, 0x4C, 0x5B, 0x7E, 0xDE, 0xD4, 0xDB, 0xFF, +0x62, 0x58, 0x55, 0x53, 0x4F, 0x52, 0x5D, 0x6F, 0xF0, 0xE4, 0xDE, 0xDA, 0xD8, 0xDC, 0xE1, 0xE5, +0xDF, 0xDC, 0xD6, 0xCC, 0xC6, 0xC1, 0xBE, 0xBB, 0xB8, 0xBC, 0xCE, 0x67, 0x4B, 0x44, 0x3E, 0x38, +0x35, 0x37, 0x3D, 0x45, 0x4B, 0x53, 0x61, 0xFA, 0xE4, 0xEC, 0x7F, 0x79, 0x78, 0x79, 0x70, 0x68, +0x68, 0x70, 0x7B, 0x7D, 0x7D, 0xF9, 0xEE, 0xEA, 0xE8, 0xE6, 0xE1, 0xDB, 0xD6, 0xCF, 0xCB, 0xC8, +0xC5, 0xC1, 0xBE, 0xBF, 0xC7, 0xD5, 0xF9, 0x5F, 0x55, 0x4A, 0x42, 0x3E, 0x3E, 0x41, 0x45, 0x48, +0x4C, 0x56, 0x5F, 0x6F, 0xF8, 0xEF, 0xE7, 0xE4, 0xE6, 0xE2, 0xEA, 0xF5, 0xFD, 0x75, 0x75, 0x79, +0x75, 0x76, 0x7E, 0xFA, 0xF1, 0xE9, 0xE0, 0xDA, 0xD3, 0xCF, 0xCC, 0xCA, 0xC7, 0xC4, 0xC5, 0xCC, +0xD3, 0xDF, 0xFF, 0x69, 0x58, 0x4C, 0x47, 0x43, 0x42, 0x42, 0x43, 0x46, 0x4C, 0x55, 0x59, 0x62, +0x77, 0xFD, 0xEE, 0xEC, 0xF3, 0xEC, 0xF2, 0x76, 0x76, 0x6F, 0x6E, 0x79, 0x79, 0x7F, 0xF3, 0xED, +0xE4, 0xDD, 0xDB, 0xD7, 0xD3, 0xD0, 0xCE, 0xCC, 0xCB, 0xCA, 0xCC, 0xD1, 0xD6, 0xDD, 0xEA, 0x7E, +0x62, 0x56, 0x4F, 0x4C, 0x49, 0x48, 0x49, 0x4C, 0x50, 0x56, 0x58, 0x5C, 0x67, 0x6F, 0x6C, 0x6E, +0x6D, 0x6A, 0x6F, 0x6D, 0x6B, 0x6E, 0x6D, 0x76, 0xFA, 0xF9, 0xF1, 0xEB, 0xE7, 0xE1, 0xDE, 0xDD, +0xDB, 0xD8, 0xD7, 0xD4, 0xD2, 0xD0, 0xCF, 0xD4, 0xD8, 0xDA, 0xDF, 0xEA, 0xF8, 0x6D, 0x60, 0x5C, +0x58, 0x56, 0x56, 0x56, 0x57, 0x59, 0x5B, 0x5B, 0x5A, 0x5C, 0x5F, 0x5F, 0x5F, 0x63, 0x66, 0x6B, +0x72, 0x77, 0x7E, 0xF7, 0xF0, 0xEC, 0xE9, 0xE9, 0xE8, 0xE8, 0xE8, 0xE8, 0xEA, 0xE7, 0xE6, 0xE5, +0xE3, 0xE1, 0xDD, 0xDA, 0xD9, 0xD8, 0xDD, 0xDF, 0xDD, 0xE0, 0xE7, 0xEE, 0xFE, 0x74, 0x6C, 0x62, +0x5C, 0x59, 0x56, 0x54, 0x52, 0x51, 0x51, 0x52, 0x55, 0x58, 0x59, 0x5D, 0x62, 0x68, 0x70, 0x75, +0x7D, 0xF9, 0xF8, 0xF4, 0xF5, 0xF9, 0xF5, 0xF3, 0xF3, 0xF2, 0xF2, 0xEF, 0xED, 0xEB, 0xEA, 0xE6, +0xE3, 0xE0, 0xDC, 0xDA, 0xD7, 0xD8, 0xDB, 0xDB, 0xDA, 0xDC, 0xDF, 0xE8, 0xF6, 0x7F, 0x6E, 0x60, +0x5B, 0x58, 0x56, 0x54, 0x53, 0x52, 0x53, 0x59, 0x5C, 0x5E, 0x63, 0x68, 0x6F, 0x7B, 0x7C, 0xFF, +0xFC, 0xFC, 0xFB, 0xFC, 0xFD, 0xF8, 0xF7, 0xF6, 0xF4, 0xF5, 0xF3, 0xF0, 0xEF, 0xED, 0xEB, 0xE8, +0xE4, 0xE0, 0xDD, 0xDA, 0xD9, 0xDA, 0xDC, 0xDC, 0xDC, 0xE0, 0xE9, 0xF8, 0x76, 0x6B, 0x61, 0x5C, +0x5A, 0x5A, 0x5A, 0x5B, 0x5C, 0x5E, 0x62, 0x67, 0x6B, 0x6D, 0x6F, 0x72, 0x75, 0x76, 0x76, 0x75, +0x74, 0x79, 0x7E, 0x7B, 0x78, 0x7D, 0xFB, 0xFB, 0xFD, 0xFA, 0xF6, 0xF5, 0xF2, 0xEF, 0xEC, 0xE9, +0xE4, 0xDF, 0xDE, 0xDE, 0xE0, 0xE7, 0xEA, 0xEC, 0xF2, 0xFE, 0x76, 0x6F, 0x6F, 0x6D, 0x6A, 0x6A, +0x6B, 0x6C, 0x6C, 0x69, 0x67, 0x67, 0x66, 0x65, 0x64, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6B, 0x6E, +0x71, 0x76, 0x7A, 0x7D, 0xFE, 0xFC, 0xFA, 0xF8, 0xF4, 0xF0, 0xEE, 0xEC, 0xEA, 0xE9, 0xE8, 0xE9, +0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xED, 0xEB, 0xE9, 0xEA, 0xE9, 0xE9, 0xEA, 0xEC, 0xF0, 0xF6, 0xFD, +0x78, 0x71, 0x6D, 0x69, 0x67, 0x66, 0x65, 0x65, 0x66, 0x67, 0x66, 0x66, 0x67, 0x67, 0x66, 0x66, +0x69, 0x6B, 0x6D, 0x73, 0x7A, 0xFE, 0xF7, 0xF3, 0xEF, 0xEE, 0xEE, 0xEC, 0xEB, 0xEA, 0xE8, 0xE7, +0xE5, 0xE3, 0xE1, 0xE1, 0xE1, 0xE3, 0xE6, 0xE8, 0xEB, 0xEF, 0xF8, 0x7E, 0x75, 0x6F, 0x6B, 0x69, +0x67, 0x66, 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, +0x70, 0x6F, 0x6F, 0x6F, 0x72, 0x75, 0x79, 0xFF, 0xFB, 0xF6, 0xF2, 0xEF, 0xEE, 0xED, 0xEE, 0xF0, +0xF3, 0xF6, 0xF8, 0xFC, 0xFF, 0x7D, 0x79, 0x76, 0x72, 0x6E, 0x6D, 0x6B, 0x6B, 0x6C, 0x6C, 0x6D, +0x6E, 0x71, 0x76, 0x7B, 0xFF, 0xFE, 0xFD, 0xFC, 0xFD, 0x7F, 0x7B, 0x78, 0x75, 0x74, 0x75, 0x77, +0x7A, 0x7E, 0xFC, 0xF9, 0xF6, 0xF5, 0xF4, 0xF4, 0xF5, 0xF7, 0xF8, 0xF8, 0xF7, 0xF6, 0xF5, 0xF5, +0xF6, 0xF8, 0xFA, 0xFC, 0x7F, 0x7B, 0x79, 0x78, 0x77, 0x77, 0x79, 0x7D, 0xFD, 0xF9, 0xF4, 0xF2, +0xF1, 0xF1, 0xF3, 0xF5, 0xFA, 0xFE, 0x7F, 0xFF, 0xFD, 0xFC, 0xFB, 0xF7, 0xF5, 0xF4, 0xF4, 0xF6, +0xF8, 0xFB, 0xFD, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, 0x7F, 0x7D, 0x7D, 0x7E, 0x7E, 0x7B, 0x79, 0x77, +0x76, 0x75, 0x74, 0x74, 0x75, 0x78, 0x7C, 0x7F, 0xFE, 0xFD, 0xFE, 0xFF, 0x7E, 0x7C, 0x7B, 0x79, +0x79, 0x7A, 0x7D, 0xFE, 0xFC, 0xFA, 0xF8, 0xF8, 0xF9, 0xFB, 0xFD, 0x7F, 0x7B, 0x78, 0x76, 0x75, +0x74, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x7A, 0x7C, +0x7E, 0xFF, 0xFF, 0xFF, 0x7F, 0x7D, 0x7C, 0x7A, 0x78, 0x77, 0x77, 0x76, 0x79, 0x7B, 0x7E, 0xFF, +0xFE, 0xFD, 0xFD, 0xFF, 0x7E, 0x7C, 0x7C, 0x7B, 0x79, 0x78, 0x78, 0x78, 0x77, 0x75, 0x74, 0x74, +0x74, 0x74, 0x75, 0x76, 0x79, 0x7C, 0x7E, 0xFF, 0xFC, 0xF8, 0xF7, 0xF6, 0xF7, 0xF7, 0xF7, 0xF9, +0xFC, 0xFF, 0x7E, 0x7D, 0x7C, 0x7D, 0x7F, 0xFF, 0xFC, 0xFB, 0xFA, 0xF8, 0xF8, 0xF8, 0xF6, 0xF5, +0xF5, 0xF4, 0xF4, 0xF5, 0xF7, 0xF8, 0xFA, 0xFD, 0x7F, 0x7E, 0x7D, 0x7C, 0x7C, 0x7D, 0x7E, 0xFE, +0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF9, 0xF9, 0xFA, 0xFB, 0xFD, 0xFF, 0x7E, 0x7D, 0x79, 0x77, 0x75, +0x72, 0x71, 0x70, 0x70, 0x70, 0x71, 0x71, 0x70, 0x71, 0x72, 0x71, 0x72, 0x71, 0x72, 0x73, 0x72, +0x72, 0x71, 0x71, 0x71, 0x72, 0x74, 0x74, 0x76, 0x77, 0x77, 0x77, 0x78, 0x77, 0x78, 0x78, 0x78, +0x7A, 0x7C, 0x7C, 0x7D, 0x7E, 0x7F, 0xFF, 0xFF, 0xFE, 0xFD, 0xFF, 0x7E, 0x7D, 0x7B, 0x7C, 0x7B, +0x7B, 0x7B, 0x7B, 0x7B, 0x7C, 0x7B, 0x7A, 0x7B, 0x7B, 0x7A, 0x7B, 0x7B, 0x7C, 0x7E, 0xFF, 0xFD, +0xFB, 0xFB, 0xFB, 0xFB, 0xFC, 0xFE, 0xFF, 0x7F, 0x7D, 0x7C, 0x7B, 0x7A, 0x7A, 0x7B, 0x7B, 0x7D, +0x7F, 0x7F, 0xFF, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFC, 0xFC, 0xFC, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF, +0xFF, 0xFF, 0xFF, 0xFD, 0xFA, 0xF9, 0xF7, 0xF5, 0xF4, 0xF3, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, +0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xFA, 0xFC, 0xFE, 0xFE, 0xFF, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, +0x7F, 0x7D, 0x7C, 0x7D, 0x7B, 0x7A, 0x7D, 0x7B, 0x7B, 0x7C, 0x7B, 0x7B, 0x7B, 0x79, 0x78, 0x77, +0x77, 0x76, 0x75, 0x76, 0x75, 0x74, 0x74, 0x75, 0x74, 0x73, 0x74, 0x74, 0x74, 0x74, 0x73, 0x74, +0x72, 0x70, 0x70, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x71, 0x72, 0x73, 0x73, 0x76, +0x79, 0x79, 0x79, 0x7A, 0x7B, 0x7C, 0x7B, 0x7D, 0x7E, 0x7D, 0x7F, 0x7D, 0x7C, 0x7B, 0x7B, 0x7C, +0x7B, 0x7B, 0x7A, 0x78, 0x7A, 0x7B, 0x7D, 0x7E, 0x7E, 0xFF, 0x7D, 0x7E, 0x7C, 0x7C, 0x7B, 0x78, +0x7C, 0x7A, 0x7C, 0x7D, 0x7B, 0x7E, 0x7C, 0x7F, 0x7D, 0x7A, 0x7E, 0x78, 0x7B, 0x6F, 0x68, 0x5C, +0x4C, 0x58, 0xDD, 0xE1, 0xEB, 0xE1, 0xE9, 0xEF, 0xF8, 0xE9, 0xE1, 0xED, 0xF9, 0xFC, 0xF2, 0xED, +0x7D, 0x76, 0xFB, 0xF9, 0x7C, 0x70, 0x6F, 0x78, 0x79, 0x6D, 0x6D, 0x76, 0x7D, 0xFE, 0x7A, 0x79, +0xFC, 0xFD, 0xFD, 0xFA, 0xFB, 0xFA, 0xFC, 0xFF, 0xFC, 0xFC, 0xFE, 0xFD, 0xFE, 0xFB, 0xFB, 0xFE, +0xFE, 0x7E, 0x7D, 0x7C, 0x7C, 0x7B, 0x78, 0x7D, 0x7D, 0xFF, 0xFA, 0xFF, 0xF9, 0xF9, 0xFA, 0xF7, +0x7F, 0xF9, 0x7A, 0x7A, 0x6F, 0x51, 0x52, 0x6E, 0x71, 0xF1, 0xD8, 0xD3, 0xDB, 0xEF, 0xE2, 0xDB, +0xED, 0xF6, 0xF3, 0xEF, 0xF4, 0x6A, 0x74, 0x73, 0x57, 0x5E, 0x77, 0x6E, 0xFD, 0xF4, 0xFC, 0x7D, +0x73, 0xE9, 0xDE, 0xE2, 0xDE, 0xE3, 0xE5, 0xE8, 0xFA, 0xF7, 0xFA, 0x6F, 0x6C, 0x6A, 0x6B, 0x6A, +0x66, 0x6B, 0x75, 0x70, 0x74, 0xFA, 0xFB, 0xFD, 0xF8, 0xF3, 0xF2, 0xFB, 0xF5, 0xED, 0xEF, 0xEB, +0xE5, 0xDF, 0xDA, 0xD7, 0xD5, 0xD3, 0xD3, 0xD4, 0xD8, 0xE0, 0xEE, 0x76, 0x5D, 0x53, 0x4D, 0x49, +0x46, 0x43, 0x42, 0x43, 0x43, 0x45, 0x48, 0x4B, 0x4F, 0x57, 0x60, 0x6D, 0xF7, 0xE3, 0xDB, 0xD4, +0xCF, 0xCB, 0xC8, 0xC4, 0xBE, 0xBC, 0xB9, 0xB4, 0xB6, 0xBC, 0xC1, 0xC3, 0xD5, 0x4F, 0x4B, 0x43, +0x36, 0x33, 0x34, 0x32, 0x31, 0x36, 0x3B, 0x3F, 0x48, 0x60, 0xDF, 0xD2, 0xC9, 0xC1, 0xC3, 0xC6, +0xC7, 0xCF, 0xE3, 0x67, 0x56, 0x47, 0x3E, 0x3F, 0x3C, 0x3B, 0x43, 0x48, 0x4F, 0xFA, 0xD5, 0xC5, +0xBD, 0xB7, 0xB1, 0xAE, 0xAC, 0xAE, 0xB1, 0xB5, 0xC0, 0xD7, 0x5D, 0x40, 0x38, 0x31, 0x2E, 0x2D, +0x2C, 0x2E, 0x32, 0x37, 0x3F, 0x4F, 0xF8, 0xD0, 0xC4, 0xBC, 0xBB, 0xBB, 0xBB, 0xBE, 0xC7, 0xD0, +0xF1, 0x56, 0x48, 0x40, 0x3D, 0x3B, 0x3D, 0x42, 0x49, 0x63, 0xDA, 0xCB, 0xBC, 0xB6, 0xB0, 0xAE, +0xAF, 0xAF, 0xB4, 0xBC, 0xC6, 0xFE, 0x4C, 0x3E, 0x34, 0x30, 0x2E, 0x2D, 0x2E, 0x30, 0x35, 0x3C, +0x46, 0x62, 0xDE, 0xCA, 0xBE, 0xBD, 0xBB, 0xBA, 0xBD, 0xC3, 0xCB, 0xDC, 0x6B, 0x59, 0x51, 0x52, +0x60, 0xFF, 0xD5, 0xC4, 0xBE, 0xBC, 0xBA, 0xB9, 0xBB, 0xC3, 0xC8, 0xD6, 0x5A, 0x4C, 0x42, 0x39, +0x36, 0x35, 0x32, 0x34, 0x36, 0x39, 0x41, 0x47, 0x58, 0xE0, 0xDA, 0xC9, 0xC1, 0xC4, 0xC0, 0xC0, +0xC6, 0xCA, 0xD1, 0xDC, 0xF3, 0x6E, 0xFC, 0xE8, 0xDB, 0xCB, 0xC2, 0xBE, 0xBD, 0xBE, 0xBD, 0xC0, +0xCD, 0xCE, 0xEC, 0x4E, 0x4C, 0x40, 0x38, 0x39, 0x37, 0x35, 0x38, 0x38, 0x3B, 0x43, 0x49, 0x54, +0xEA, 0xDB, 0xCE, 0xC5, 0xC5, 0xC3, 0xC1, 0xC7, 0xCA, 0xCF, 0xDB, 0xE9, 0xFD, 0xF7, 0xE9, 0xDD, +0xCE, 0xC6, 0xC1, 0xBF, 0xC0, 0xBF, 0xC3, 0xCF, 0xD0, 0xE6, 0x51, 0x4E, 0x43, 0x3A, 0x3B, 0x38, +0x35, 0x38, 0x38, 0x3A, 0x3F, 0x44, 0x4E, 0x5F, 0xEF, 0xD7, 0xCD, 0xC8, 0xC6, 0xC4, 0xC6, 0xC9, +0xCC, 0xD4, 0xDD, 0xE6, 0xE1, 0xDA, 0xD7, 0xCA, 0xC3, 0xC4, 0xC4, 0xC1, 0xC3, 0xCC, 0xCF, 0xD8, +0x60, 0x54, 0x4E, 0x3F, 0x3D, 0x3D, 0x39, 0x3A, 0x3C, 0x3B, 0x3D, 0x47, 0x48, 0x51, 0x79, 0xFA, +0xDD, 0xCD, 0xCF, 0xCC, 0xC8, 0xCD, 0xCE, 0xCE, 0xD7, 0xDC, 0xDC, 0xD7, 0xD4, 0xCC, 0xC3, 0xC1, +0xBF, 0xBF, 0xC2, 0xC1, 0xCE, 0xDB, 0xDD, 0x54, 0x4C, 0x49, 0x3C, 0x3B, 0x3B, 0x38, 0x3A, 0x3B, +0x3C, 0x3E, 0x48, 0x4C, 0x55, 0xF3, 0xEC, 0xDB, 0xCD, 0xD1, 0xCD, 0xCB, 0xD2, 0xD1, 0xD6, 0xDD, +0xE1, 0xE2, 0xD7, 0xD3, 0xCA, 0xC0, 0xBE, 0xBE, 0xBF, 0xBE, 0xC1, 0xCF, 0xD0, 0xED, 0x4E, 0x4D, +0x41, 0x3A, 0x3A, 0x37, 0x35, 0x38, 0x3A, 0x3A, 0x3F, 0x49, 0x4A, 0x62, 0xE7, 0xEA, 0xCE, 0xCB, +0xCF, 0xC8, 0xCB, 0xD1, 0xCF, 0xD7, 0xE2, 0xE5, 0xE5, 0xDF, 0xD5, 0xCC, 0xC5, 0xBE, 0xBE, 0xC0, +0xBC, 0xC0, 0xCD, 0xC8, 0xDC, 0x58, 0x5B, 0x47, 0x3C, 0x3D, 0x39, 0x36, 0x39, 0x3A, 0x39, 0x3E, +0x47, 0x49, 0x59, 0xEA, 0xEC, 0xD0, 0xC9, 0xCD, 0xC7, 0xC8, 0xCE, 0xCD, 0xD2, 0xDC, 0xDF, 0xDF, +0xDD, 0xD6, 0xCB, 0xC6, 0xBF, 0xBF, 0xC4, 0xBD, 0xC4, 0xD1, 0xC9, 0xEF, 0x55, 0x5D, 0x43, 0x3D, +0x3E, 0x38, 0x37, 0x3A, 0x3A, 0x3A, 0x3E, 0x45, 0x48, 0x56, 0x74, 0xF8, 0xD8, 0xCF, 0xD0, 0xCB, +0xCB, 0xCE, 0xCE, 0xD2, 0xD9, 0xDC, 0xDC, 0xD7, 0xD3, 0xCA, 0xC4, 0xC0, 0xBF, 0xC2, 0xBE, 0xC3, +0xCF, 0xCB, 0xE5, 0x58, 0x5C, 0x45, 0x3D, 0x3E, 0x39, 0x37, 0x39, 0x3A, 0x3A, 0x3D, 0x45, 0x46, +0x52, 0x79, 0x73, 0xD7, 0xCD, 0xD1, 0xC8, 0xC8, 0xCD, 0xCA, 0xCF, 0xD5, 0xD7, 0xDC, 0xDA, 0xD4, +0xCD, 0xC7, 0xC1, 0xC0, 0xC5, 0xBF, 0xC2, 0xD1, 0xCA, 0xDA, 0x5A, 0x68, 0x4C, 0x3E, 0x42, 0x3B, +0x38, 0x3B, 0x3A, 0x3A, 0x3D, 0x42, 0x44, 0x4B, 0x62, 0x66, 0xF1, 0xD0, 0xD6, 0xCD, 0xC8, 0xCE, +0xCA, 0xCB, 0xD1, 0xCF, 0xD2, 0xD2, 0xCE, 0xC9, 0xC5, 0xC1, 0xBF, 0xC5, 0xC1, 0xC0, 0xD3, 0xCE, +0xD6, 0x57, 0x5F, 0x4E, 0x3E, 0x41, 0x3D, 0x39, 0x3B, 0x3B, 0x3A, 0x3D, 0x42, 0x44, 0x4C, 0x5A, +0x5E, 0xEC, 0xDD, 0xDC, 0xCE, 0xD0, 0xD1, 0xCD, 0xD4, 0xD4, 0xD4, 0xDA, 0xD3, 0xCE, 0xCB, 0xC4, +0xBF, 0xBE, 0xC2, 0xBE, 0xBF, 0xCD, 0xCB, 0xD5, 0x5B, 0x5F, 0x4D, 0x3E, 0x3F, 0x3B, 0x37, 0x39, +0x3A, 0x39, 0x3C, 0x3F, 0x42, 0x4B, 0x59, 0x61, 0xEA, 0xDA, 0xD6, 0xCD, 0xCD, 0xCE, 0xCD, 0xCF, +0xD3, 0xD6, 0xDA, 0xD7, 0xD2, 0xCE, 0xC7, 0xC2, 0xBF, 0xC0, 0xC1, 0xBE, 0xC8, 0xCE, 0xCB, 0x79, +0x5A, 0x5C, 0x41, 0x3F, 0x3E, 0x38, 0x39, 0x3A, 0x39, 0x3A, 0x3E, 0x42, 0x45, 0x51, 0x62, 0x65, +0xDD, 0xD5, 0xD7, 0xCA, 0xCC, 0xCE, 0xCA, 0xCE, 0xD0, 0xCF, 0xCF, 0xCD, 0xC9, 0xC4, 0xBF, 0xBD, +0xBE, 0xBF, 0xBD, 0xC5, 0xCE, 0xCB, 0xF8, 0x58, 0x5B, 0x43, 0x3E, 0x3E, 0x39, 0x38, 0x3A, 0x39, +0x3A, 0x3E, 0x44, 0x45, 0x51, 0x6E, 0x69, 0xDC, 0xCF, 0xD7, 0xCA, 0xCB, 0xD0, 0xCC, 0xD1, 0xD5, +0xD5, 0xD5, 0xCF, 0xCB, 0xC5, 0xBF, 0xBC, 0xBD, 0xBF, 0xBB, 0xC3, 0xCC, 0xC8, 0xEF, 0x5B, 0x5D, +0x41, 0x3D, 0x3D, 0x37, 0x37, 0x38, 0x37, 0x38, 0x3B, 0x41, 0x43, 0x4E, 0x6B, 0x67, 0xDC, 0xCE, +0xD4, 0xC9, 0xC8, 0xCE, 0xCA, 0xCE, 0xD5, 0xD4, 0xD5, 0xD3, 0xCE, 0xC7, 0xC3, 0xBE, 0xBE, 0xC3, +0xBD, 0xC2, 0xD0, 0xC9, 0xDE, 0x57, 0x61, 0x47, 0x3D, 0x3F, 0x39, 0x36, 0x38, 0x38, 0x38, 0x3A, +0x41, 0x40, 0x48, 0x6A, 0x5C, 0xF7, 0xCD, 0xDB, 0xCD, 0xC6, 0xD1, 0xCC, 0xCC, 0xD6, 0xD5, 0xD3, +0xCF, 0xCE, 0xC6, 0xC0, 0xBF, 0xBE, 0xBF, 0xBE, 0xC0, 0xCC, 0xCB, 0xDB, 0x5D, 0x5B, 0x49, 0x3E, +0x3E, 0x39, 0x37, 0x38, 0x38, 0x38, 0x3B, 0x40, 0x42, 0x4A, 0x63, 0x66, 0xE5, 0xCE, 0xD0, 0xCA, +0xC7, 0xCA, 0xCA, 0xCC, 0xCE, 0xD1, 0xCF, 0xCC, 0xCC, 0xC4, 0xBF, 0xBF, 0xBE, 0xC0, 0xBE, 0xC2, +0xCE, 0xCC, 0xE2, 0x58, 0x59, 0x47, 0x3D, 0x3D, 0x39, 0x37, 0x39, 0x39, 0x39, 0x3C, 0x40, 0x44, +0x4D, 0x5D, 0x6F, 0xE0, 0xD4, 0xCF, 0xCC, 0xCB, 0xCB, 0xCD, 0xCF, 0xCF, 0xD4, 0xD1, 0xCC, 0xCA, +0xC4, 0xBF, 0xBD, 0xBF, 0xBF, 0xBE, 0xC7, 0xCF, 0xCE, 0x79, 0x52, 0x51, 0x41, 0x3C, 0x3B, 0x38, +0x36, 0x38, 0x39, 0x38, 0x3D, 0x44, 0x44, 0x55, 0x70, 0x76, 0xD5, 0xCF, 0xCF, 0xC9, 0xCB, 0xCB, +0xCD, 0xD1, 0xD1, 0xD9, 0xD2, 0xCD, 0xCD, 0xC4, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xC9, 0xCF, 0xD0, +0x6A, 0x53, 0x4F, 0x3F, 0x3C, 0x3B, 0x38, 0x37, 0x38, 0x39, 0x3A, 0x3E, 0x44, 0x4A, 0x56, 0x75, +0xE7, 0xD7, 0xCD, 0xCB, 0xCA, 0xC9, 0xCA, 0xCD, 0xCE, 0xCF, 0xD3, 0xCE, 0xCA, 0xC7, 0xC0, 0xBE, +0xBE, 0xBF, 0xBF, 0xC1, 0xCD, 0xD2, 0xDB, 0x5C, 0x4F, 0x4A, 0x3E, 0x3C, 0x3B, 0x39, 0x38, 0x3A, +0x3A, 0x3C, 0x43, 0x46, 0x4E, 0x71, 0x7A, 0xDD, 0xCE, 0xD0, 0xCB, 0xCA, 0xCD, 0xCC, 0xD1, 0xD2, +0xD5, 0xD7, 0xCC, 0xCC, 0xC7, 0xBE, 0xBF, 0xBF, 0xBE, 0xC0, 0xC6, 0xCD, 0xD5, 0xEE, 0x54, 0x4D, +0x45, 0x3C, 0x3B, 0x39, 0x37, 0x38, 0x39, 0x3A, 0x3E, 0x44, 0x4A, 0x59, 0x71, 0xE8, 0xD4, 0xCE, +0xCC, 0xC9, 0xCB, 0xCB, 0xCE, 0xD2, 0xD4, 0xD7, 0xD2, 0xCD, 0xCB, 0xC4, 0xBF, 0xBF, 0xC0, 0xC0, +0xC0, 0xC9, 0xD1, 0xD8, 0x73, 0x52, 0x4B, 0x42, 0x3C, 0x3A, 0x39, 0x38, 0x38, 0x39, 0x3C, 0x3F, +0x41, 0x4E, 0x5D, 0x63, 0xDF, 0xD3, 0xD1, 0xCD, 0xCB, 0xCC, 0xD1, 0xD1, 0xD3, 0xDE, 0xDA, 0xD1, +0xCF, 0xCC, 0xC4, 0xBF, 0xC2, 0xC2, 0xBF, 0xC6, 0xCD, 0xD2, 0xE3, 0x63, 0x4F, 0x4A, 0x42, 0x3C, +0x3B, 0x3A, 0x39, 0x3A, 0x3B, 0x3F, 0x42, 0x47, 0x5A, 0x62, 0x7C, 0xD5, 0xD2, 0xCF, 0xCC, 0xCD, +0xCE, 0xD5, 0xD6, 0xD9, 0xE0, 0xD9, 0xD2, 0xCE, 0xCB, 0xC4, 0xBF, 0xC2, 0xC2, 0xBF, 0xC6, 0xCD, +0xD4, 0xE2, 0x67, 0x4F, 0x4B, 0x43, 0x3D, 0x3C, 0x3B, 0x3B, 0x3B, 0x3C, 0x42, 0x43, 0x49, 0x60, +0x6B, 0xF5, 0xD4, 0xD2, 0xD0, 0xD2, 0xD2, 0xD4, 0xDF, 0xDD, 0xDF, 0xEA, 0xDC, 0xD5, 0xD0, 0xCB, +0xC4, 0xC0, 0xC2, 0xBF, 0xC0, 0xC7, 0xC9, 0xD0, 0xDD, 0x7A, 0x57, 0x52, 0x48, 0x3F, 0x3F, 0x3D, +0x3C, 0x3C, 0x3D, 0x42, 0x44, 0x49, 0x5B, 0x64, 0x7C, 0xDE, 0xDB, 0xDB, 0xDC, 0xDA, 0xDF, 0xEE, +0xE9, 0xE6, 0xE8, 0xE3, 0xDB, 0xD0, 0xCD, 0xCC, 0xC5, 0xC3, 0xC5, 0xC2, 0xC3, 0xC8, 0xCD, 0xD1, +0xDE, 0x6F, 0x5B, 0x50, 0x4A, 0x44, 0x40, 0x3F, 0x3E, 0x3E, 0x42, 0x43, 0x47, 0x4F, 0x58, 0x5F, +0x69, 0xF6, 0xED, 0xF7, 0xEB, 0xE8, 0xEA, 0xEC, 0xE8, 0xE0, 0xE0, 0xDD, 0xD7, 0xD0, 0xCE, 0xCC, +0xC8, 0xC7, 0xC6, 0xC4, 0xC8, 0xC9, 0xCB, 0xD2, 0xDA, 0xEF, 0x71, 0x5B, 0x4F, 0x4E, 0x49, 0x45, +0x46, 0x45, 0x47, 0x47, 0x4A, 0x4E, 0x4E, 0x54, 0x5A, 0x5D, 0x62, 0x65, 0x70, 0x6C, 0x69, 0xFA, +0x7A, 0x7A, 0xE6, 0xE2, 0xDF, 0xDA, 0xD5, 0xCF, 0xD1, 0xD1, 0xCB, 0xCE, 0xD4, 0xCE, 0xD1, 0xD9, +0xDB, 0xE0, 0xE1, 0xEE, 0x6A, 0x74, 0x6C, 0x5A, 0x5B, 0x5A, 0x56, 0x56, 0x53, 0x56, 0x57, 0x53, +0x57, 0x5B, 0x59, 0x59, 0x5D, 0x65, 0x60, 0x61, 0x6F, 0x73, 0xF7, 0xEF, 0xEA, 0xDD, 0xDF, 0xDC, +0xD5, 0xD8, 0xDC, 0xDC, 0xDA, 0xDF, 0xE5, 0xE1, 0xEB, 0xED, 0xED, 0xFC, 0xFF, 0x70, 0x6C, 0x6C, +0x66, 0x5F, 0x5F, 0x62, 0x5F, 0x60, 0x60, 0x5A, 0x5F, 0x64, 0x5C, 0x64, 0x6E, 0x5F, 0x5E, 0x69, +0x6B, 0x7B, 0xF0, 0xFA, 0xE9, 0xE4, 0xE8, 0xDF, 0xE6, 0xEB, 0xE1, 0xE2, 0xE6, 0xE3, 0xE8, 0xED, +0xE5, 0xFC, 0x71, 0xE9, 0xFE, 0x7A, 0xEC, 0x73, 0x7E, 0x77, 0x64, 0xFD, 0x74, 0x66, 0x7B, 0x6C, +0x6B, 0x72, 0x6A, 0x79, 0x73, 0x71, 0xF4, 0x79, 0xFC, 0xEF, 0xF9, 0xEB, 0xF1, 0xF0, 0xE2, 0xE3, +0xE0, 0xE2, 0xE3, 0xEB, 0xFC, 0xEA, 0xF8, 0x6F, 0x70, 0x69, 0x77, 0x6C, 0x6D, 0xEF, 0x6B, 0x67, +0x6D, 0x65, 0x76, 0x75, 0x6F, 0x7E, 0x69, 0x67, 0x6F, 0x68, 0x6F, 0x73, 0x68, 0x69, 0x68, 0x69, +0x75, 0xFA, 0xEE, 0xF2, 0xEA, 0xDE, 0xE2, 0xE7, 0xE5, 0xEB, 0xF3, 0xED, 0xE8, 0xF7, 0x7F, 0xFA, +0x74, 0x6A, 0x68, 0x65, 0x66, 0x68, 0x64, 0x65, 0x68, 0x64, 0x6D, 0x77, 0x72, 0x70, 0x69, 0x77, +0x78, 0x64, 0x6B, 0x68, 0x60, 0x66, 0x67, 0x67, 0x61, 0x6D, 0xEB, 0xE8, 0xEC, 0xEB, 0xEA, 0xF7, +0xF5, 0xE2, 0xE5, 0xF6, 0xF8, 0xF0, 0x7E, 0x77, 0xFA, 0x76, 0x77, 0x7D, 0x6E, 0x74, 0x74, 0x72, +0x72, 0x6D, 0x75, 0x74, 0x74, 0x75, 0x76, 0xEF, 0xEE, 0xEF, 0xEA, 0xFA, 0x7F, 0xF2, 0xF6, 0xF7, +0xEF, 0xEA, 0xE4, 0xE5, 0xE7, 0xE3, 0xE9, 0xEC, 0xE8, 0xEB, 0xF0, 0xF8, 0xFC, 0x7F, 0x73, 0x73, +0x70, 0x72, 0x75, 0x69, 0x67, 0x6A, 0x6C, 0x6D, 0x6C, 0x70, 0x6D, 0x6D, 0x78, 0x72, 0x6F, 0x6E, +0x6C, 0x6F, 0x77, 0xF7, 0xEF, 0xF1, 0xF6, 0xF4, 0xF1, 0xFC, 0xF8, 0xEE, 0xF0, 0xEE, 0xEF, 0xEF, +0xEC, 0xEE, 0xF3, 0xF4, 0x76, 0x69, 0x77, 0xFA, 0x70, 0x75, 0x7E, 0x6E, 0x6D, 0x6E, 0x6E, 0x6E, +0x6D, 0x70, 0x74, 0x79, 0xFE, 0x7D, 0x78, 0x7C, 0x7A, 0x75, 0x78, 0x75, 0x70, 0x7C, 0xFC, 0xFB, +0xF4, 0xF3, 0xED, 0xEC, 0xF6, 0xF1, 0xEF, 0xF6, 0xF0, 0xFB, 0x7A, 0x7C, 0x74, 0x79, 0x74, 0x6C, +0x6C, 0x63, 0x67, 0x6C, 0x62, 0x66, 0x67, 0x68, 0x6C, 0x6A, 0x7D, 0x78, 0x6B, 0x7B, 0x71, 0x75, +0xFB, 0x75, 0x76, 0x7E, 0xEE, 0xED, 0xFA, 0xE8, 0xEE, 0x7F, 0xE4, 0xE9, 0xED, 0xE1, 0xEC, 0xEE, +0xE6, 0xF6, 0xFE, 0xF7, 0xFF, 0x7B, 0x72, 0x7C, 0x7F, 0x6B, 0x6C, 0x66, 0x67, 0x71, 0x66, 0x72, +0xFF, 0x69, 0x7B, 0xF7, 0xFF, 0xF3, 0xF6, 0xF3, 0xF2, 0xEE, 0xED, 0xFF, 0xF6, 0xED, 0xFB, 0xF2, +0xE9, 0xF0, 0xEC, 0xE6, 0xF4, 0xF2, 0xF5, 0x7C, 0xED, 0x7E, 0x6B, 0x7B, 0x77, 0xFE, 0x7A, 0x69, +0x68, 0x60, 0x65, 0x66, 0x61, 0x71, 0x74, 0x74, 0xFC, 0x7E, 0xFA, 0xFF, 0x7E, 0xF9, 0xFB, 0xEE, +0xED, 0xEF, 0xEF, 0xF4, 0xEC, 0xEC, 0xF2, 0xF0, 0xEF, 0xED, 0xF4, 0xF6, 0xEF, 0xFD, 0xF8, 0xF1, +0x77, 0x74, 0x7A, 0x70, 0x6F, 0x79, 0x76, 0x6A, 0x68, 0x6C, 0x6C, 0x69, 0x65, 0x66, 0x6E, 0x73, +0x6C, 0x6D, 0x7A, 0x79, 0x7A, 0xFE, 0x73, 0x72, 0x7A, 0x7E, 0xF3, 0xEF, 0xEE, 0xF1, 0xF6, 0xEC, +0xEF, 0xFA, 0xEE, 0xF5, 0xFE, 0xF2, 0xF0, 0xF7, 0xFA, 0xF9, 0xFB, 0xFB, 0x7A, 0x6B, 0x6B, 0x72, +0x6C, 0x6A, 0x76, 0x74, 0x6E, 0x72, 0x6C, 0x6B, 0x6F, 0x6B, 0x6E, 0x71, 0x6D, 0x75, 0x76, 0x70, +0x78, 0x7C, 0xFA, 0xF2, 0xF7, 0xF1, 0xED, 0xF3, 0xF4, 0xF6, 0xFA, 0xF0, 0xEF, 0xFD, 0x7B, 0xFF, +0x78, 0x6C, 0x70, 0x72, 0x6E, 0x73, 0x6D, 0x6D, 0x6D, 0x65, 0x65, 0x69, 0x6F, 0x70, 0x6B, 0x6E, +0x71, 0x6E, 0x6E, 0x6F, 0x7A, 0x7D, 0x7E, 0xEF, 0xF4, 0x7D, 0xFB, 0xF3, 0xEC, 0xEB, 0xF0, 0xEF, +0xEE, 0xEE, 0xF3, 0xF0, 0xED, 0xFF, 0xFF, 0xF3, 0x7E, 0xFB, 0xFF, 0x73, 0x7E, 0x7A, 0x76, 0x6F, +0x6C, 0x7A, 0x74, 0x6F, 0xFF, 0x7B, 0x79, 0xF7, 0xF5, 0xFB, 0xF6, 0xF3, 0x7E, 0x7D, 0xFA, 0xFD, +0xF6, 0xF5, 0x7D, 0x7B, 0x7A, 0xFF, 0xFB, 0x7D, 0x75, 0x76, 0xF3, 0xF3, 0x78, 0xFE, 0x76, 0x6B, +0x7B, 0xFB, 0x75, 0x69, 0x6C, 0xF6, 0xF6, 0x7B, 0x6F, 0x69, 0x6E, 0x72, 0x70, 0x74, 0x78, 0xFF, +0x7F, 0xFD, 0xF5, 0xF8, 0xF5, 0xF9, 0x75, 0x74, 0x7D, 0xF7, 0xFB, 0x70, 0x7E, 0xE9, 0xEF, 0xF7, +0xE6, 0xED, 0x74, 0x7A, 0xF7, 0x7D, 0x71, 0x77, 0xFE, 0xF6, 0xFC, 0x6B, 0x6B, 0x72, 0x69, 0x6D, +0x7B, 0x7C, 0xFD, 0x6F, 0x6B, 0xFC, 0x7F, 0x73, 0xF9, 0xF5, 0x70, 0x6E, 0xFD, 0xFF, 0x7C, 0xFB, +0x70, 0x76, 0xEF, 0x7D, 0x75, 0xFB, 0xF6, 0xEF, 0xF6, 0x7C, 0x79, 0x76, 0xFE, 0x7D, 0x73, 0xFD, +0xFE, 0x76, 0x6E, 0x66, 0x6B, 0x75, 0x6D, 0x7D, 0xF2, 0x6D, 0x75, 0xED, 0x7A, 0x6D, 0x7E, 0xF9, +0xFE, 0x6E, 0x78, 0xF4, 0x6E, 0x73, 0xF8, 0x74, 0xFF, 0xFE, 0x77, 0xF6, 0x7C, 0x71, 0x7F, 0x7E, +0x7E, 0x79, 0x78, 0x7C, 0x6E, 0x6F, 0x70, 0x6D, 0xFD, 0xFA, 0x6C, 0x6B, 0x71, 0x70, 0x7B, 0x7A, +0x7C, 0xFD, 0x6D, 0x75, 0xED, 0xF8, 0xFC, 0xF7, 0xFB, 0xEE, 0xEF, 0x79, 0xFF, 0xED, 0xF8, 0x7A, +0xF1, 0xED, 0xF7, 0xFC, 0xF8, 0xF1, 0xF8, 0x79, 0x7D, 0xF6, 0x78, 0x6E, 0xFD, 0xFB, 0x72, 0x6E, +0x6C, 0x74, 0xFE, 0xFF, 0xF7, 0xFE, 0x6D, 0x75, 0x79, 0x6F, 0x7C, 0x7D, 0x76, 0x78, 0x7A, 0x7F, +0x75, 0x7D, 0xF7, 0xF8, 0xEF, 0x7B, 0x7A, 0xF3, 0xFF, 0xEF, 0xF8, 0xFE, 0xF6, 0x6E, 0xF4, 0xED, +0x6F, 0x7C, 0x74, 0x75, 0xF2, 0x7C, 0x75, 0xF9, 0xEE, 0xFF, 0x70, 0x7C, 0x7F, 0xFD, 0xFD, 0xFA, +0x79, 0x6C, 0x7C, 0x7F, 0xFD, 0xF3, 0x78, 0xFE, 0xF7, 0xFC, 0xED, 0xF1, 0xFD, 0xFE, 0x7D, 0xF4, +0xED, 0xF4, 0xFA, 0xFD, 0x70, 0x73, 0xF6, 0xFF, 0x7D, 0xF7, 0x79, 0x72, 0x78, 0x7D, 0x77, 0x6E, +0x71, 0x6C, 0x6A, 0x7D, 0x74, 0x71, 0x7D, 0x6E, 0x79, 0xF8, 0x70, 0x7C, 0xFE, 0x6D, 0x6F, 0x6E, +0x6D, 0x7D, 0xFD, 0xFB, 0xFA, 0x7C, 0xFD, 0xFA, 0xF7, 0xF4, 0xFD, 0x7B, 0x76, 0x7D, 0xF3, 0xF5, +0xFD, 0xF5, 0xED, 0x7A, 0x6D, 0xF5, 0x7C, 0x76, 0xEF, 0x77, 0x6E, 0xFF, 0x74, 0x79, 0xFD, 0x76, +0x7A, 0x7E, 0x7B, 0x7D, 0x78, 0x73, 0x7A, 0xFB, 0xFB, 0x7E, 0xFB, 0xF2, 0xF8, 0xF9, 0xFB, 0xFE, +0xF5, 0xF6, 0xF9, 0x7E, 0x73, 0xF7, 0xFA, 0x74, 0xFC, 0x7B, 0x70, 0xFF, 0xFC, 0x7E, 0x7C, 0xFE, +0xFF, 0x7C, 0xFC, 0x7E, 0x79, 0xFF, 0x7C, 0x79, 0x79, 0x79, 0x7A, 0x79, 0x7A, 0x7C, 0x7A, 0x75, +0x72, 0x7C, 0xFC, 0xFE, 0xFE, 0xFC, 0xF9, 0xFE, 0xFE, 0xFA, 0x7C, 0xFD, 0xFC, 0x77, 0x7B, 0x7A, +0x73, 0x78, 0x7A, 0x78, 0x7B, 0x77, 0x6E, 0x70, 0x7B, 0x7A, 0x72, 0x74, 0x76, 0x75, 0x77, 0x75, +0x70, 0x6F, 0x6F, 0x6F, 0x74, 0x77, 0x72, 0x6F, 0x70, 0x79, 0x79, 0x6F, 0x7E, 0xFA, 0x72, 0x79, +0x79, 0x78, 0xFE, 0x78, 0xFF, 0xFE, 0x76, 0xFF, 0xFF, 0x7D, 0xFF, 0x7E, 0x7B, 0x78, 0x7C, 0x75, +0x75, 0xFF, 0xFF, 0xF8, 0xF6, 0xFD, 0xF5, 0xF4, 0xF6, 0xFC, 0x7C, 0x7C, 0x7B, 0xFC, 0xFD, 0xFD, +0xFD, 0x77, 0x7A, 0x7F, 0x7E, 0xFC, 0xFA, 0xF7, 0xFF, 0xFE, 0xF1, 0xFD, 0xFD, 0xF0, 0xF9, 0xF8, +0xEE, 0xEF, 0xF8, 0x7E, 0x79, 0x77, 0x6C, 0x66, 0x6A, 0x68, 0x67, 0x71, 0x74, 0x77, 0x7E, 0x7B, +0xFD, 0xFD, 0x7F, 0xFF, 0x79, 0xFE, 0xFE, 0x75, 0x78, 0x7A, 0x74, 0x70, 0x76, 0x77, 0x75, 0x7C, +0x7C, 0x79, 0x78, 0x77, 0x78, 0x7C, 0x7E, 0x77, 0x74, 0x79, 0x77, 0x78, 0x78, 0x77, 0x7B, 0x78, +0x77, 0x77, 0x7A, 0xFE, 0x7D, 0xFE, 0xF6, 0xFB, 0xFC, 0xF8, 0xF6, 0xFA, 0xFD, 0xFB, 0xFC, 0x7D, +0x7B, 0xFB, 0xF5, 0xFC, 0xFF, 0xFD, 0xFE, 0xFF, 0x7F, 0xFC, 0xF8, 0xFF, 0x7F, 0xF7, 0xF9, 0xFA, +0xF9, 0xFB, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xFD, 0x7E, 0xF8, 0xF7, 0xEF, 0xE0, 0xE1, 0xF7, 0x6E, +0x69, 0x65, 0x5C, 0x5B, 0x61, 0x69, 0x6E, 0x76, 0xF8, 0xEF, 0xF2, 0xEB, 0xE3, 0xDE, 0xE2, 0xFA, +0x66, 0x5B, 0x5F, 0x6E, 0x6E, 0xFB, 0xED, 0xED, 0xEC, 0xF8, 0x7E, 0x7B, 0x6E, 0x67, 0x60, 0x65, +0x6E, 0x6F, 0x78, 0xFB, 0x7E, 0x7D, 0xFA, 0xF9, 0xF7, 0xF5, 0xF7, 0xEE, 0xEB, 0xF3, 0xFA, 0x7A, +0x6D, 0x69, 0x6A, 0x69, 0x64, 0x69, 0x6E, 0x6F, 0x78, 0xFB, 0xFB, 0x7C, 0xF8, 0xF1, 0xFB, 0xF4, +0xF9, 0x7D, 0xF8, 0x7D, 0xFD, 0xF3, 0x7C, 0xFD, 0xF4, 0x7A, 0x6F, 0x79, 0xFF, 0x75, 0x78, 0xFA, +0xFB, 0xFE, 0xFB, 0xF9, 0xFE, 0xFF, 0xFF, 0xFC, 0xF5, 0xFD, 0x79, 0xFF, 0xF9, 0xFA, 0xFF, 0xFF, +0x7B, 0x74, 0x75, 0x6F, 0x71, 0x7D, 0x74, 0x75, 0x79, 0x71, 0x77, 0x7E, 0x7E, 0x7C, 0x74, 0x71, +0x77, 0x7A, 0x7F, 0xFB, 0x72, 0x69, 0x74, 0x74, 0x6A, 0x74, 0x7E, 0x79, 0x76, 0x74, 0x7F, 0x7C, +0x76, 0xF8, 0xF8, 0x7E, 0x7D, 0x7E, 0xFE, 0x77, 0x7B, 0xF8, 0x7B, 0xFF, 0xF3, 0xFC, 0xFF, 0xFE, +0xFD, 0xF5, 0xF6, 0xF3, 0xF2, 0xFA, 0xF8, 0xF3, 0xFB, 0x7E, 0xFA, 0xFA, 0x7E, 0xFE, 0xFC, 0x7C, +0x78, 0x7D, 0xFB, 0xF3, 0xFD, 0x74, 0xF7, 0xF5, 0x76, 0xF9, 0xF8, 0xFF, 0xF5, 0x76, 0x7B, 0xFA, +0x77, 0xFD, 0x7C, 0x77, 0x7D, 0x6F, 0x7D, 0xF7, 0xFC, 0xFD, 0x7B, 0xF5, 0xF8, 0x7A, 0xF4, 0xFB, +0x77, 0x7B, 0x76, 0x74, 0x7D, 0x7E, 0x72, 0x71, 0x70, 0x70, 0x77, 0x70, 0x7B, 0xFD, 0x6F, 0x72, +0x7D, 0x7A, 0x71, 0x79, 0xF8, 0x77, 0x6F, 0x74, 0x7A, 0x78, 0x7C, 0xF6, 0x7C, 0xFB, 0x7D, 0x6C, +0xEF, 0xFC, 0x6D, 0xF1, 0xF5, 0x7D, 0x79, 0xFA, 0xF5, 0x75, 0xF4, 0x7C, 0xFC, 0xE6, 0x68, 0x78, +0xE4, 0x6E, 0x77, 0xF2, 0x7E, 0x70, 0x69, 0xF9, 0xF7, 0x6C, 0x74, 0xF3, 0xEE, 0x79, 0x7E, 0xFB, +0x7A, 0xF6, 0x6C, 0x6B, 0xED, 0x77, 0x6A, 0x70, 0x6F, 0xFC, 0x7E, 0x79, 0xF2, 0xEE, 0xF8, 0x7D, +0xEF, 0xEB, 0x7E, 0x72, 0x7D, 0x74, 0x6B, 0xFA, 0xE5, 0xE7, 0xE4, 0xEB, 0xF8, 0xEC, 0xEA, 0xF3, +0xFB, 0xFA, 0xEF, 0xF6, 0x7C, 0xFF, 0x7B, 0x72, 0x78, 0x7C, 0x6F, 0x6E, 0xFF, 0xFF, 0x75, 0x74, +0x74, 0x76, 0x6C, 0x6A, 0x7F, 0x78, 0x6D, 0x74, 0x6E, 0x76, 0x7B, 0x69, 0x6B, 0x79, 0x72, 0x6A, +0x6D, 0x77, 0xFD, 0xFC, 0x6F, 0x7C, 0xEE, 0x76, 0x69, 0x74, 0xFB, 0x74, 0x6A, 0x7D, 0xF0, 0xFF, +0x6B, 0x71, 0xEF, 0x7D, 0x74, 0xF8, 0x75, 0x74, 0x77, 0x6C, 0x6E, 0x71, 0x73, 0x72, 0x70, 0xF4, +0xF0, 0x79, 0x7B, 0xFB, 0xF3, 0xF4, 0x78, 0x79, 0xF7, 0xFC, 0x6E, 0x70, 0xF8, 0xF8, 0x79, 0x6D, +0x70, 0xFB, 0x6E, 0x64, 0x6B, 0x72, 0x6F, 0x65, 0x71, 0xFC, 0x71, 0x76, 0x6E, 0x71, 0xF9, 0xFF, +0xFD, 0x73, 0x7A, 0xFF, 0x66, 0x70, 0xFE, 0x79, 0xFD, 0x7D, 0xF1, 0xF6, 0x76, 0x79, 0x6D, 0x79, +0xFF, 0x72, 0x7F, 0xF8, 0xF0, 0xFF, 0x71, 0xEC, 0xE9, 0x7C, 0x7C, 0xF1, 0xF7, 0xF7, 0xF5, 0xFA, +0xF1, 0x7C, 0x70, 0x7D, 0xFB, 0xF4, 0x7C, 0x7D, 0xF6, 0xFD, 0xEF, 0xF6, 0xEE, 0xE1, 0xEE, 0xF4, +0xF3, 0xEE, 0xF4, 0x78, 0xEC, 0xF7, 0x72, 0x74, 0x6E, 0xF5, 0x6A, 0x7B, 0xDF, 0x69, 0x7F, 0xE7, +0x6F, 0x7C, 0x68, 0xF4, 0xEE, 0x5C, 0x72, 0x6B, 0x6D, 0xEC, 0x60, 0x7E, 0xEB, 0x66, 0x6D, 0x6A, +0x7A, 0xFF, 0x6A, 0x6E, 0x74, 0xED, 0x79, 0x6B, 0xED, 0xFA, 0xFC, 0x70, 0x65, 0xF6, 0x75, 0x67, +0x78, 0x78, 0xF4, 0xFF, 0x5F, 0xF9, 0xE3, 0x6D, 0xF1, 0x7A, 0x70, 0xDB, 0x6A, 0xF9, 0xDC, 0x66, +0xDB, 0xF8, 0x65, 0xD6, 0x6A, 0xF2, 0xE6, 0x5E, 0xE1, 0x76, 0x6F, 0xF5, 0x65, 0xEF, 0x5F, 0x66, +0x6F, 0x5A, 0xFB, 0x5E, 0x65, 0x7E, 0x60, 0xF2, 0x5D, 0x72, 0xE8, 0x5A, 0xEA, 0x6E, 0x62, 0xE4, +0x5E, 0x6F, 0x74, 0x67, 0xEA, 0x64, 0x77, 0xF3, 0x69, 0xF1, 0x6B, 0x6E, 0xEB, 0x74, 0x74, 0x74, +0xF6, 0xF5, 0x66, 0xF5, 0xF7, 0x6C, 0xE9, 0x70, 0x6F, 0xE4, 0x6E, 0xF0, 0xF4, 0x63, 0xE1, 0xFE, +0x6D, 0xE4, 0x6D, 0xF2, 0xEF, 0x6C, 0xE8, 0x7B, 0xFC, 0xED, 0x74, 0xEB, 0xFE, 0xFC, 0xEF, 0x6D, +0xEC, 0xFB, 0x71, 0xEB, 0x77, 0xF3, 0xF8, 0x6F, 0xE9, 0xFE, 0xFC, 0xEE, 0x6F, 0xEB, 0xED, 0x7A, +0xEE, 0xF4, 0xF3, 0xF8, 0xF1, 0xEE, 0x79, 0xF4, 0xF3, 0xFC, 0xF1, 0x7C, 0xF4, 0xF9, 0x76, 0xE8, +0xF8, 0x75, 0xE3, 0x7B, 0x6E, 0xE0, 0x75, 0x72, 0xE3, 0x6C, 0xF8, 0xE7, 0x6D, 0xEA, 0xF4, 0x75, +0xEA, 0x71, 0xFA, 0xED, 0x6A, 0xF2, 0xFB, 0x70, 0xEB, 0x6F, 0x71, 0xEE, 0x6C, 0xF6, 0xF2, 0x68, +0xF3, 0xF8, 0x6D, 0x7A, 0xFC, 0x75, 0x6D, 0xF3, 0x6E, 0x6E, 0xEE, 0x61, 0x7C, 0xFE, 0x60, 0xF2, +0x6D, 0x78, 0x7C, 0x63, 0xEB, 0x64, 0x71, 0xED, 0x5C, 0xEB, 0x6F, 0x69, 0xEA, 0x5D, 0xE7, 0xF8, +0x5E, 0xE6, 0x6F, 0xFA, 0x79, 0x72, 0xE8, 0x5E, 0xE9, 0xFB, 0x5F, 0xDE, 0x61, 0xFA, 0xE3, 0x5A, +0xE3, 0x79, 0x62, 0xDF, 0x64, 0x76, 0xE4, 0x63, 0xFE, 0xE9, 0x64, 0xF8, 0xE6, 0x5F, 0xF2, 0xE8, +0x62, 0xE7, 0xFB, 0x6A, 0xF0, 0x77, 0xF9, 0x77, 0xE9, 0x78, 0x59, 0xE3, 0x71, 0x6C, 0xDF, 0x5F, +0xEF, 0xE9, 0x5F, 0xFA, 0x76, 0xF9, 0x7D, 0x67, 0xEE, 0x7B, 0x6F, 0x7E, 0x6B, 0x6D, 0xF7, 0x6E, +0x6B, 0xF0, 0x63, 0xF0, 0x75, 0x57, 0xD4, 0x59, 0x57, 0xCD, 0x4F, 0xEE, 0xDE, 0x4B, 0xCF, 0x68, +0x5B, 0xD4, 0x50, 0xE0, 0xEF, 0x54, 0xD5, 0x57, 0x6A, 0xD3, 0x54, 0x79, 0xDF, 0x5E, 0xFC, 0xE6, +0x5F, 0x71, 0xE0, 0x64, 0x76, 0xFC, 0x5D, 0xEA, 0xF8, 0x58, 0x79, 0xDB, 0x68, 0x59, 0xDF, 0x76, +0x64, 0xDA, 0x63, 0x5A, 0xD9, 0xE0, 0x51, 0xF3, 0xD3, 0x4D, 0xDE, 0xCF, 0x3F, 0xDA, 0xC2, 0x41, +0x6A, 0xC3, 0x50, 0x55, 0xC6, 0x5A, 0x4B, 0xC2, 0x7A, 0x46, 0xCA, 0xDF, 0x4C, 0xD8, 0xDE, 0x4F, +0xE2, 0xD2, 0x4D, 0x6B, 0xCD, 0x5C, 0x56, 0xD8, 0xEB, 0x54, 0xEF, 0xDE, 0x4F, 0x78, 0xD7, 0x54, +0x5B, 0xDA, 0xFA, 0x56, 0xF6, 0xE9, 0x58, 0xF1, 0xE5, 0x59, 0x6D, 0xE8, 0x7B, 0x64, 0x7E, 0xE2, +0x60, 0x6D, 0xDC, 0x63, 0x67, 0xDC, 0x6F, 0x62, 0xE2, 0x7B, 0x60, 0xEC, 0xF3, 0x67, 0x77, 0xF1, +0x7C, 0x75, 0xF0, 0x79, 0x6F, 0xED, 0x76, 0x6B, 0xF3, 0xFF, 0x74, 0xF4, 0xF8, 0x6B, 0x6C, 0xE6, +0xE7, 0x61, 0x79, 0xDD, 0x6F, 0x77, 0xDE, 0x6D, 0x6E, 0xE4, 0x7E, 0xF9, 0xE8, 0x6D, 0x6F, 0xDE, +0xEF, 0x5C, 0xEF, 0xDB, 0x64, 0x6B, 0xDF, 0xF9, 0x6C, 0xF4, 0xF5, 0x72, 0xF5, 0xF7, 0x66, 0x7D, +0xE9, 0x7D, 0x70, 0x79, 0xF6, 0x7B, 0x6C, 0xF3, 0xF2, 0x6A, 0x76, 0xED, 0x79, 0x6B, 0xF2, 0xF1, +0x6B, 0x78, 0xEF, 0x73, 0x74, 0xED, 0x73, 0x6A, 0xEB, 0xF2, 0x64, 0x6F, 0xE2, 0xF8, 0x5E, 0xF3, +0xE8, 0x64, 0x74, 0xEF, 0x7B, 0x77, 0x74, 0x78, 0x6D, 0xF8, 0xEA, 0x63, 0x68, 0xEC, 0xEF, 0x6E, +0x63, 0xEE, 0xFA, 0x5F, 0xE5, 0xEB, 0x58, 0x73, 0xD1, 0xF3, 0x4D, 0xF2, 0xCD, 0x5D, 0x54, 0xDF, +0xDD, 0x61, 0x5B, 0xD9, 0xDF, 0x50, 0xF0, 0xD3, 0x5F, 0x5D, 0xE4, 0xDF, 0x6E, 0x5B, 0xED, 0xD8, +0x68, 0x56, 0xEA, 0xD3, 0x70, 0x52, 0xF0, 0xD3, 0x74, 0x57, 0xF5, 0xD8, 0x6A, 0x58, 0xE4, 0xE2, +0x5C, 0x7A, 0xD8, 0x77, 0x5A, 0xE8, 0xD9, 0x73, 0x5F, 0x7D, 0xE3, 0xEC, 0x73, 0x72, 0x6E, 0xF6, +0xDF, 0xF7, 0x62, 0x6B, 0xE5, 0xDD, 0x6B, 0x58, 0xF7, 0xDB, 0x73, 0x5E, 0x75, 0x7E, 0xF6, 0xFE, +0x66, 0x71, 0x7B, 0x7E, 0xEE, 0x6C, 0x6B, 0xEB, 0x76, 0x6C, 0xEA, 0x7A, 0x67, 0xE5, 0xE8, 0x5F, +0x75, 0xDE, 0xEE, 0x6F, 0x7A, 0xED, 0xE8, 0xF8, 0xFB, 0xF4, 0x78, 0xF1, 0xE4, 0x6E, 0x62, 0xEB, +0xEC, 0x65, 0x6D, 0x7E, 0x6B, 0x69, 0x6E, 0x72, 0x78, 0x68, 0x5E, 0x74, 0xE9, 0x6F, 0x5E, 0x72, +0xEB, 0x7A, 0x6F, 0xF1, 0x78, 0x75, 0xE5, 0xE6, 0xE2, 0xDD, 0xE0, 0xD6, 0xD1, 0xD5, 0xD1, 0xD3, +0xDE, 0xE0, 0xDD, 0xE9, 0x68, 0x57, 0x52, 0x50, 0x4B, 0x48, 0x48, 0x48, 0x48, 0x4B, 0x51, 0x56, +0x56, 0x5D, 0x68, 0x6A, 0x69, 0x7C, 0xE8, 0xED, 0xED, 0xDC, 0xD5, 0xCD, 0xCB, 0xCD, 0xC5, 0xBE, +0xBE, 0xBC, 0xBC, 0xBF, 0xC5, 0xD2, 0x72, 0x52, 0x48, 0x40, 0x3C, 0x39, 0x39, 0x3B, 0x3D, 0x40, +0x4A, 0x5A, 0x6C, 0x7E, 0xDF, 0xCF, 0xCE, 0xDB, 0x6F, 0x63, 0x6E, 0x60, 0x4D, 0x47, 0x4D, 0x5E, +0x7C, 0xFA, 0xE4, 0xCD, 0xC1, 0xBD, 0xBC, 0xBA, 0xB7, 0xB4, 0xB5, 0xBE, 0xD7, 0xEB, 0xF2, 0x4E, +0x3B, 0x36, 0x37, 0x39, 0x38, 0x39, 0x3D, 0x48, 0x58, 0xFE, 0xDA, 0xD6, 0xD8, 0xCF, 0xC8, 0xCD, +0xF3, 0x5A, 0x59, 0x58, 0x4E, 0x47, 0x48, 0x4F, 0x5F, 0xF2, 0xD9, 0xCE, 0xC6, 0xBF, 0xBB, 0xBA, +0xB9, 0xB7, 0xB5, 0xB7, 0xC0, 0xD9, 0x68, 0x57, 0x49, 0x3B, 0x35, 0x35, 0x38, 0x3A, 0x3B, 0x3F, +0x4B, 0x60, 0xE7, 0xD8, 0xD5, 0xD4, 0xCE, 0xCA, 0xCF, 0xF9, 0x59, 0x51, 0x4E, 0x4C, 0x48, 0x46, +0x49, 0x56, 0xFB, 0xD9, 0xD1, 0xC9, 0xBE, 0xB8, 0xB6, 0xB5, 0xB3, 0xB2, 0xB6, 0xC3, 0xE5, 0x60, +0x4E, 0x3F, 0x37, 0x32, 0x34, 0x37, 0x39, 0x3C, 0x41, 0x4E, 0x6F, 0xDA, 0xD2, 0xD4, 0xD1, 0xCA, +0xC9, 0xD7, 0x6F, 0x59, 0x4D, 0x47, 0x47, 0x48, 0x45, 0x46, 0x52, 0xEE, 0xD7, 0xD0, 0xC7, 0xBC, +0xB5, 0xB3, 0xB3, 0xB1, 0xB0, 0xB7, 0xCA, 0x68, 0x4F, 0x45, 0x3B, 0x32, 0x2E, 0x30, 0x35, 0x39, +0x3D, 0x44, 0x52, 0xEC, 0xD0, 0xCF, 0xD3, 0xD0, 0xCD, 0xCF, 0xE3, 0x63, 0x5B, 0x59, 0x52, 0x4D, +0x4C, 0x4F, 0x5A, 0x6D, 0xE5, 0xD2, 0xC8, 0xBE, 0xB9, 0xB5, 0xB3, 0xB1, 0xAF, 0xB1, 0xBD, 0xE3, +0x53, 0x4B, 0x40, 0x35, 0x2E, 0x2F, 0x34, 0x39, 0x3B, 0x3F, 0x4C, 0x75, 0xDA, 0xD6, 0xD2, 0xCE, +0xCE, 0xD0, 0xD9, 0xEF, 0x62, 0x56, 0x55, 0x55, 0x4F, 0x4E, 0x52, 0x61, 0xEF, 0xDB, 0xCC, 0xBF, +0xB9, 0xB5, 0xB3, 0xB0, 0xAE, 0xB4, 0xC8, 0x63, 0x4D, 0x45, 0x39, 0x2E, 0x2C, 0x2F, 0x35, 0x38, +0x3B, 0x43, 0x5A, 0xDD, 0xCE, 0xCD, 0xCC, 0xCB, 0xCD, 0xD2, 0xDE, 0x76, 0x5A, 0x52, 0x53, 0x51, +0x4E, 0x4F, 0x5C, 0x7C, 0xE2, 0xD2, 0xC5, 0xBB, 0xB5, 0xB2, 0xB0, 0xAE, 0xAE, 0xB6, 0xCF, 0x55, +0x48, 0x3F, 0x34, 0x2C, 0x2C, 0x2F, 0x36, 0x39, 0x3D, 0x4A, 0x7D, 0xCE, 0xCA, 0xCB, 0xCB, 0xCB, +0xCD, 0xD9, 0x76, 0x59, 0x4F, 0x4C, 0x4C, 0x4A, 0x4C, 0x54, 0x68, 0xEC, 0xD8, 0xCB, 0xBF, 0xB8, +0xB3, 0xB0, 0xAE, 0xAD, 0xAE, 0xBC, 0xF5, 0x4C, 0x43, 0x39, 0x2E, 0x2A, 0x2D, 0x32, 0x37, 0x3B, +0x44, 0x5F, 0xD5, 0xC8, 0xC7, 0xC9, 0xCB, 0xCE, 0xD3, 0xE9, 0x5A, 0x4D, 0x4D, 0x4D, 0x4B, 0x4B, +0x50, 0x63, 0xF6, 0xDE, 0xCE, 0xC5, 0xBD, 0xB7, 0xB2, 0xB0, 0xAF, 0xAD, 0xAF, 0xBF, 0x5E, 0x45, +0x40, 0x37, 0x2C, 0x29, 0x2D, 0x34, 0x38, 0x3C, 0x48, 0x77, 0xCD, 0xC6, 0xC7, 0xCB, 0xCB, 0xCF, +0xE9, 0x59, 0x4E, 0x4B, 0x47, 0x44, 0x47, 0x4D, 0x57, 0x69, 0xEB, 0xD7, 0xCA, 0xBF, 0xB9, 0xB3, +0xAF, 0xAE, 0xAD, 0xAC, 0xB5, 0xDE, 0x49, 0x43, 0x3B, 0x2D, 0x28, 0x2A, 0x30, 0x36, 0x3A, 0x44, +0x6A, 0xCD, 0xC2, 0xC2, 0xC6, 0xCA, 0xCC, 0xD8, 0x60, 0x4C, 0x48, 0x46, 0x44, 0x43, 0x49, 0x54, +0x68, 0xEC, 0xD8, 0xCB, 0xC2, 0xBC, 0xB8, 0xB2, 0xAF, 0xAE, 0xAE, 0xAE, 0xBA, 0x74, 0x45, 0x3F, +0x36, 0x2C, 0x29, 0x2C, 0x32, 0x37, 0x3E, 0x4E, 0xE3, 0xC7, 0xC2, 0xC5, 0xC6, 0xC8, 0xD2, 0x71, +0x52, 0x4D, 0x49, 0x42, 0x41, 0x47, 0x51, 0x60, 0x7B, 0xDE, 0xCE, 0xC6, 0xC0, 0xBE, 0xB9, 0xB2, +0xAF, 0xAF, 0xAE, 0xAE, 0xBC, 0x6B, 0x47, 0x3F, 0x34, 0x2C, 0x2A, 0x2D, 0x32, 0x39, 0x3F, 0x4F, +0xDF, 0xC5, 0xC4, 0xCD, 0xCA, 0xC7, 0xDB, 0x5A, 0x4E, 0x4D, 0x48, 0x42, 0x40, 0x45, 0x54, 0x6F, +0x74, 0xEC, 0xCD, 0xC4, 0xC4, 0xC1, 0xBB, 0xB3, 0xAF, 0xB0, 0xAE, 0xAE, 0xBC, 0x6F, 0x4A, 0x3F, +0x34, 0x2C, 0x2B, 0x2E, 0x34, 0x3B, 0x42, 0x56, 0xCF, 0xBF, 0xC2, 0xCB, 0xC6, 0xC6, 0xE6, 0x52, +0x4B, 0x47, 0x45, 0x3F, 0x3D, 0x45, 0x5E, 0x7A, 0x72, 0xDB, 0xC7, 0xC3, 0xC5, 0xC2, 0xBC, 0xB5, +0xB1, 0xB3, 0xB0, 0xAE, 0xB9, 0xF0, 0x4C, 0x43, 0x38, 0x2E, 0x2B, 0x2D, 0x34, 0x3A, 0x3E, 0x4F, +0xD3, 0xC1, 0xC4, 0xCD, 0xCC, 0xC8, 0xDE, 0x4D, 0x45, 0x47, 0x47, 0x41, 0x3D, 0x47, 0x6D, 0xF7, +0x79, 0xDA, 0xC9, 0xC6, 0xC8, 0xC9, 0xC0, 0xB8, 0xB4, 0xB5, 0xB0, 0xAC, 0xB1, 0xCF, 0x5A, 0x4E, +0x3E, 0x30, 0x2B, 0x2D, 0x33, 0x38, 0x3B, 0x48, 0xDC, 0xC4, 0xC5, 0xCA, 0xCB, 0xCB, 0xD9, 0x50, +0x46, 0x4B, 0x46, 0x3E, 0x3F, 0x48, 0x55, 0x76, 0x7E, 0xE3, 0xCA, 0xC9, 0xCE, 0xCB, 0xC6, 0xBE, +0xBA, 0xB8, 0xB4, 0xAE, 0xAE, 0xBD, 0xEA, 0x62, 0x48, 0x35, 0x2D, 0x2C, 0x30, 0x35, 0x37, 0x3F, +0x6F, 0xCB, 0xCA, 0xC9, 0xC7, 0xCE, 0xD9, 0x60, 0x46, 0x46, 0x48, 0x40, 0x40, 0x49, 0x52, 0x69, +0xED, 0xE1, 0xD1, 0xCC, 0xD0, 0xD2, 0xD0, 0xCD, 0xC2, 0xBD, 0xB9, 0xB2, 0xAE, 0xAF, 0xBA, 0xE0, +0x67, 0x4C, 0x34, 0x2D, 0x2E, 0x30, 0x34, 0x37, 0x41, 0xF4, 0xCD, 0xCC, 0xC7, 0xC5, 0xD0, 0xED, +0x64, 0x4D, 0x45, 0x47, 0x43, 0x44, 0x4F, 0x51, 0x69, 0xD9, 0xDD, 0xD1, 0xCB, 0xD1, 0xCF, 0xD2, +0xCD, 0xBF, 0xBC, 0xB8, 0xB0, 0xAD, 0xAE, 0xBC, 0xD9, 0x76, 0x48, 0x32, 0x2D, 0x2F, 0x30, 0x32, +0x38, 0x49, 0xE6, 0xD1, 0xCB, 0xC1, 0xC1, 0xD2, 0x7A, 0x6F, 0x51, 0x42, 0x43, 0x45, 0x4A, 0x4D, +0x4E, 0x72, 0xD7, 0xDB, 0xD8, 0xCE, 0xCF, 0xD7, 0xDD, 0xD3, 0xC6, 0xBF, 0xBB, 0xB3, 0xAD, 0xAE, +0xB8, 0xCD, 0xE2, 0x52, 0x35, 0x2E, 0x2F, 0x2F, 0x2F, 0x35, 0x43, 0x62, 0xE5, 0xCF, 0xC1, 0xC0, +0xD1, 0xE1, 0xDB, 0x5E, 0x46, 0x49, 0x4D, 0x4A, 0x49, 0x51, 0x6A, 0xEE, 0xEB, 0xDE, 0xD0, 0xD4, +0xDF, 0xDC, 0xD7, 0xCF, 0xC5, 0xBE, 0xB7, 0xB1, 0xAE, 0xAF, 0xBC, 0xD3, 0xE7, 0x48, 0x31, 0x2E, +0x2F, 0x2E, 0x2F, 0x36, 0x44, 0x5B, 0xF4, 0xCC, 0xBF, 0xC2, 0xD4, 0xD1, 0xCF, 0x59, 0x4B, 0x51, +0x4B, 0x46, 0x44, 0x4C, 0x56, 0x51, 0x66, 0xE5, 0xE6, 0xE0, 0xDF, 0xD6, 0xD1, 0xCD, 0xC0, 0xBB, +0xB7, 0xB0, 0xAD, 0xB0, 0xBD, 0xCE, 0xEF, 0x44, 0x31, 0x2F, 0x30, 0x2E, 0x2E, 0x39, 0x46, 0x56, +0xF0, 0xC7, 0xBD, 0xC2, 0xC9, 0xC8, 0xCC, 0x7A, 0x50, 0x57, 0x4F, 0x42, 0x42, 0x4A, 0x4C, 0x4C, +0x5D, 0xEF, 0xEE, 0xE3, 0xD6, 0xD1, 0xD2, 0xCC, 0xC0, 0xBC, 0xBA, 0xB3, 0xAE, 0xAF, 0xBA, 0xC9, +0xD6, 0x4F, 0x36, 0x30, 0x30, 0x2E, 0x2D, 0x34, 0x3E, 0x45, 0x5C, 0xCF, 0xC1, 0xC0, 0xC1, 0xC0, +0xC9, 0xD9, 0xF5, 0x54, 0x4A, 0x47, 0x40, 0x41, 0x42, 0x45, 0x4E, 0x53, 0x68, 0xE8, 0xE1, 0xD1, +0xCD, 0xCC, 0xC5, 0xBF, 0xBB, 0xB8, 0xB4, 0xAF, 0xB0, 0xBD, 0xCB, 0xD8, 0x4B, 0x36, 0x34, 0x33, +0x2E, 0x2E, 0x37, 0x3D, 0x44, 0x62, 0xCD, 0xC4, 0xC0, 0xBE, 0xBF, 0xC5, 0xCF, 0xEE, 0x66, 0x4F, +0x45, 0x43, 0x40, 0x41, 0x44, 0x47, 0x51, 0x5B, 0x71, 0xDD, 0xD8, 0xCE, 0xC8, 0xC6, 0xC1, 0xBD, +0xB9, 0xB7, 0xB2, 0xAF, 0xB9, 0xC3, 0xCC, 0x71, 0x40, 0x37, 0x36, 0x31, 0x2E, 0x33, 0x3A, 0x3D, +0x4A, 0xE6, 0xCF, 0xC7, 0xBE, 0xBF, 0xC2, 0xCA, 0xD3, 0xE6, 0x50, 0x4C, 0x4B, 0x3E, 0x3E, 0x43, +0x41, 0x47, 0x4D, 0x59, 0x75, 0xE4, 0xD0, 0xCC, 0xC9, 0xC3, 0xBF, 0xBD, 0xBB, 0xB8, 0xB3, 0xB3, +0xBB, 0xC3, 0xCD, 0x6A, 0x42, 0x3A, 0x38, 0x31, 0x30, 0x36, 0x39, 0x3C, 0x4B, 0x7A, 0xDB, 0xCB, +0xC1, 0xC3, 0xC5, 0xC5, 0xD5, 0xE5, 0x6B, 0x4E, 0x4C, 0x43, 0x40, 0x45, 0x42, 0x48, 0x50, 0x55, +0x6C, 0xE8, 0xD7, 0xCD, 0xCC, 0xC7, 0xC3, 0xC0, 0xBD, 0xBC, 0xB8, 0xB2, 0xB5, 0xBD, 0xC2, 0xD0, +0x5D, 0x45, 0x3C, 0x38, 0x32, 0x32, 0x37, 0x37, 0x3C, 0x4C, 0x5E, 0xEE, 0xCD, 0xC5, 0xC8, 0xC6, +0xC5, 0xD0, 0xE2, 0xF9, 0x5D, 0x4D, 0x47, 0x46, 0x42, 0x41, 0x47, 0x4B, 0x4E, 0x63, 0xEA, 0xE3, +0xD3, 0xC9, 0xCB, 0xC8, 0xC0, 0xBF, 0xBD, 0xBA, 0xB5, 0xB6, 0xBC, 0xC1, 0xCC, 0x7E, 0x4B, 0x3F, +0x3B, 0x35, 0x32, 0x37, 0x37, 0x3A, 0x45, 0x50, 0x6D, 0xD7, 0xC8, 0xC6, 0xC7, 0xC2, 0xCA, 0xD7, +0xDB, 0x6D, 0x59, 0x4F, 0x48, 0x48, 0x45, 0x46, 0x4D, 0x4F, 0x5E, 0xFF, 0xE6, 0xD7, 0xD4, 0xCF, +0xD1, 0xD5, 0xD0, 0xD3, 0xCD, 0xC6, 0xC1, 0xBA, 0xB8, 0xBB, 0xC2, 0xC4, 0xCD, 0x58, 0x4C, 0x48, +0x39, 0x36, 0x39, 0x36, 0x37, 0x3E, 0x44, 0x4B, 0x66, 0xDC, 0xD4, 0xCA, 0xC3, 0xC5, 0xC7, 0xCA, +0xCE, 0xDD, 0x6D, 0x5F, 0x4F, 0x46, 0x47, 0x46, 0x43, 0x49, 0x4E, 0x53, 0x65, 0xF1, 0xE4, 0xD9, +0xCF, 0xCE, 0xCD, 0xC6, 0xC3, 0xC0, 0xBA, 0xB7, 0xB7, 0xBD, 0xC4, 0xC5, 0xFD, 0x4A, 0x4C, 0x3B, +0x33, 0x37, 0x34, 0x32, 0x3A, 0x3F, 0x47, 0x5B, 0xDC, 0xCB, 0xC8, 0xBE, 0xBC, 0xC3, 0xC0, 0xC7, +0xDE, 0xEC, 0x5B, 0x49, 0x45, 0x3F, 0x3E, 0x3F, 0x40, 0x49, 0x4E, 0x5B, 0xEF, 0xDF, 0xD4, 0xCC, +0xCA, 0xC7, 0xC5, 0xC3, 0xBF, 0xBD, 0xBB, 0xB8, 0xB8, 0xC0, 0xCB, 0xCA, 0x68, 0x45, 0x4D, 0x3D, +0x35, 0x3C, 0x3A, 0x38, 0x41, 0x49, 0x4D, 0x69, 0xDB, 0xD3, 0xCD, 0xC5, 0xC6, 0xCC, 0xCB, 0xD0, +0xEB, 0x76, 0x5C, 0x4C, 0x48, 0x44, 0x41, 0x41, 0x44, 0x49, 0x4E, 0x5D, 0x7E, 0xE6, 0xD4, 0xCE, +0xCE, 0xCB, 0xCB, 0xCE, 0xCE, 0xCA, 0xC9, 0xC6, 0xBE, 0xBC, 0xC6, 0xCA, 0xC4, 0xE0, 0x58, 0x77, +0x4A, 0x3D, 0x45, 0x3D, 0x39, 0x3F, 0x41, 0x40, 0x4C, 0x61, 0x6D, 0xE7, 0xCE, 0xCC, 0xCC, 0xC9, +0xCA, 0xD1, 0xDB, 0xE2, 0x66, 0x54, 0x50, 0x48, 0x45, 0x47, 0x47, 0x4B, 0x51, 0x5A, 0x71, 0xEB, +0xDD, 0xD5, 0xD2, 0xCF, 0xCC, 0xCC, 0xCA, 0xC4, 0xC3, 0xBF, 0xB9, 0xBD, 0xCB, 0xC0, 0xC9, 0x56, +0x71, 0x5A, 0x3A, 0x3F, 0x3E, 0x34, 0x3A, 0x3E, 0x3C, 0x42, 0x54, 0x62, 0x73, 0xCF, 0xC8, 0xCC, +0xC3, 0xC3, 0xCD, 0xCF, 0xDD, 0x6D, 0x56, 0x4D, 0x48, 0x42, 0x42, 0x44, 0x45, 0x4D, 0x57, 0x61, +0xED, 0xDB, 0xD5, 0xCD, 0xCD, 0xCD, 0xCD, 0xD0, 0xCD, 0xD1, 0xCA, 0xC4, 0xC7, 0xBA, 0xC2, 0xD8, +0xBB, 0xD7, 0x53, 0xCC, 0x4E, 0x3E, 0x57, 0x3E, 0x3B, 0x43, 0x3F, 0x3E, 0x46, 0x52, 0x50, 0x60, +0xD6, 0xD9, 0xD4, 0xC6, 0xCA, 0xCF, 0xCB, 0xD5, 0xF4, 0xFB, 0x5D, 0x4E, 0x4C, 0x4B, 0x46, 0x45, +0x4F, 0x4E, 0x4D, 0x7E, 0x6F, 0x71, 0xD4, 0xE1, 0xDF, 0xCE, 0xD9, 0xD5, 0xCD, 0xCD, 0xC5, 0xBF, +0xB9, 0xBA, 0xC6, 0xBA, 0xBF, 0x6C, 0xCE, 0xFB, 0x3D, 0x4D, 0x40, 0x35, 0x3C, 0x3B, 0x38, 0x3E, +0x47, 0x4B, 0x52, 0xDF, 0xD8, 0xD6, 0xC3, 0xC6, 0xCB, 0xC5, 0xCD, 0xDD, 0xE4, 0x6B, 0x50, 0x4D, +0x49, 0x44, 0x45, 0x47, 0x48, 0x49, 0x50, 0x5D, 0x5C, 0xEE, 0xDD, 0xE7, 0xD1, 0xD5, 0xDC, 0xD0, +0xDA, 0xD8, 0xD1, 0xCD, 0xC6, 0xBF, 0xBA, 0xC1, 0xC4, 0xBA, 0xD3, 0xE1, 0xCC, 0x4D, 0x49, 0x4F, +0x3B, 0x3B, 0x3E, 0x3A, 0x3C, 0x42, 0x4A, 0x4C, 0x6D, 0xD9, 0xDC, 0xC9, 0xC4, 0xCA, 0xC6, 0xC8, +0xD0, 0xD8, 0xE5, 0x68, 0x58, 0x53, 0x4C, 0x4A, 0x4B, 0x4C, 0x4E, 0x56, 0x5F, 0x69, 0xEA, 0xE0, +0xE8, 0xD5, 0xDD, 0xE7, 0xD9, 0xFB, 0xF9, 0xF5, 0x64, 0x75, 0x6D, 0xF5, 0xE5, 0xD8, 0xC8, 0xC4, +0xBB, 0xBF, 0xC4, 0xB7, 0xCC, 0xD6, 0xC3, 0x56, 0x52, 0x5F, 0x3D, 0x3E, 0x3E, 0x39, 0x3A, 0x3D, +0x41, 0x41, 0x4F, 0x6B, 0x63, 0xD6, 0xCD, 0xD1, 0xC7, 0xC8, 0xCE, 0xCE, 0xD6, 0xE8, 0x71, 0x67, +0x55, 0x4F, 0x52, 0x4D, 0x4F, 0x56, 0x56, 0x5E, 0x6D, 0x7E, 0xF5, 0xDF, 0xE3, 0xEE, 0xDC, 0x7B, +0x73, 0xED, 0x5C, 0x6A, 0x69, 0x5B, 0x70, 0x6D, 0xEF, 0xE1, 0xCF, 0xC8, 0xC4, 0xB9, 0xC5, 0xC2, +0xB6, 0xDC, 0xCD, 0xC3, 0x49, 0x60, 0x57, 0x3A, 0x40, 0x3D, 0x38, 0x3A, 0x3E, 0x3F, 0x3E, 0x53, +0x58, 0x59, 0xD3, 0xD7, 0xD2, 0xC4, 0xC9, 0xCA, 0xC8, 0xCD, 0xD8, 0xDE, 0xEE, 0x5F, 0x5D, 0x59, +0x4F, 0x54, 0x56, 0x54, 0x5D, 0x69, 0x6D, 0xF2, 0xE1, 0xDF, 0xDB, 0xD8, 0xDD, 0xE8, 0xEB, 0x71, +0x5D, 0x66, 0x56, 0x52, 0x62, 0x52, 0x5C, 0x74, 0x63, 0xE2, 0xDD, 0xD4, 0xCA, 0xC9, 0xBF, 0xC0, +0xBB, 0xBB, 0xCC, 0xBB, 0xC8, 0x68, 0xC8, 0x58, 0x44, 0x5D, 0x3C, 0x3B, 0x40, 0x3A, 0x3B, 0x3F, +0x44, 0x43, 0x52, 0x7A, 0x62, 0xD4, 0xCB, 0xD1, 0xC3, 0xC4, 0xC8, 0xC5, 0xCA, 0xD0, 0xD9, 0xE1, +0x6C, 0x5C, 0x5B, 0x4E, 0x4D, 0x50, 0x4C, 0x50, 0x58, 0x58, 0x63, 0x79, 0xFE, 0xED, 0xE1, 0xE7, +0xEE, 0xE6, 0xFB, 0x6D, 0x7A, 0x61, 0x5D, 0x62, 0x59, 0x5B, 0x5E, 0x5C, 0x64, 0x69, 0x72, 0xFE, +0xF1, 0xE6, 0xE2, 0xDB, 0xD9, 0xD6, 0xD3, 0xD3, 0xD0, 0xD0, 0xCF, 0xCE, 0xCD, 0xCD, 0xDA, 0xDB, +0xDA, 0x64, 0x69, 0x63, 0x4C, 0x4F, 0x4D, 0x46, 0x4A, 0x4A, 0x48, 0x4D, 0x52, 0x52, 0x5C, 0x70, +0x71, 0xEB, 0xDC, 0xDE, 0xD7, 0xD4, 0xD8, 0xD5, 0xD7, 0xDD, 0xDF, 0xE7, 0xFD, 0x71, 0x69, 0x5D, +0x5A, 0x5A, 0x57, 0x56, 0x5B, 0x59, 0x5A, 0x69, 0x62, 0x6A, 0xF5, 0x73, 0xF1, 0xE7, 0xF4, 0xE6, +0xE6, 0xEE, 0xEA, 0xED, 0xF5, 0xF1, 0xED, 0xF0, 0xEA, 0xE4, 0xE7, 0xDF, 0xDE, 0xDF, 0xDC, 0xDE, +0xDF, 0xE1, 0xE8, 0xEE, 0xFD, 0x77, 0x6B, 0x64, 0x61, 0x5C, 0x5C, 0x5C, 0x5B, 0x5F, 0x61, 0x67, +0x6E, 0x78, 0xF8, 0xF1, 0xEA, 0xE5, 0xE4, 0xE2, 0xE1, 0xE5, 0xEA, 0xED, 0xFA, 0x77, 0x6E, 0x66, +0x62, 0x5D, 0x5C, 0x5A, 0x59, 0x59, 0x59, 0x5B, 0x5C, 0x5D, 0x64, 0x64, 0x6C, 0x77, 0x7C, 0xEE, +0xEB, 0xE0, 0xDB, 0xD7, 0xCF, 0xCF, 0xCA, 0xC8, 0xC5, 0xC4, 0xCD, 0xC7, 0xCB, 0xE8, 0xD3, 0xFD, +0x55, 0x67, 0x4B, 0x45, 0x48, 0x40, 0x3F, 0x42, 0x43, 0x43, 0x4A, 0x50, 0x50, 0x6C, 0xF5, 0xF2, +0xD4, 0xD2, 0xD1, 0xCB, 0xCC, 0xCE, 0xCE, 0xD0, 0xDA, 0xDC, 0xDF, 0xFD, 0x7D, 0x6F, 0x5E, 0x5F, +0x5D, 0x59, 0x5A, 0x5C, 0x5C, 0x5F, 0x68, 0x68, 0x6F, 0xFC, 0xFC, 0xEF, 0xE9, 0xEB, 0xE9, 0xE9, +0xED, 0xEF, 0xF2, 0xF8, 0xFE, 0xFE, 0x7C, 0x7C, 0x7E, 0x79, 0xFE, 0xFB, 0xFE, 0xF7, 0xF6, 0xF9, +0xF6, 0xF8, 0xFD, 0xFF, 0x7C, 0x73, 0x70, 0x6E, 0x6A, 0x6A, 0x69, 0x68, 0x6A, 0x6B, 0x6C, 0x6F, +0x74, 0x79, 0xFF, 0xFA, 0xF8, 0xF2, 0xF4, 0xF6, 0xF3, 0xFE, 0xFB, 0x74, 0x62, 0x69, 0x5F, 0x5A, +0x66, 0x5B, 0x5D, 0x70, 0x61, 0x78, 0xED, 0xF4, 0xDB, 0xD7, 0xD0, 0xCB, 0xC9, 0xC3, 0xCC, 0xC9, +0xC4, 0xDD, 0xD2, 0xD7, 0x59, 0x6E, 0x57, 0x45, 0x4B, 0x45, 0x3F, 0x43, 0x44, 0x43, 0x49, 0x51, +0x4F, 0x5E, 0xFB, 0x70, 0xDE, 0xD8, 0xDC, 0xD1, 0xD2, 0xD7, 0xD4, 0xD6, 0xDD, 0xDF, 0xDF, 0xF1, +0xF2, 0xF2, 0x6F, 0x73, 0x72, 0x69, 0x6D, 0x6F, 0x6D, 0x72, 0x7B, 0x79, 0x7E, 0xF8, 0xFD, 0xF9, +0xF6, 0xFD, 0xFA, 0xFA, 0xFD, 0xFB, 0xFB, 0xFD, 0xFA, 0xF6, 0xF8, 0xF3, 0xF2, 0xF4, 0xEF, 0xF1, +0xF4, 0xF2, 0xF6, 0xF9, 0xF8, 0xF9, 0xFD, 0xFD, 0xFC, 0x7F, 0x7E, 0x7D, 0x79, 0x76, 0x74, 0x71, +0x6F, 0x6E, 0x6C, 0x6C, 0x6D, 0x6B, 0x6C, 0x6D, 0x6C, 0x71, 0x71, 0x70, 0x7E, 0x79, 0x7C, 0xFA, +0x7B, 0xFD, 0xFC, 0x7A, 0xFE, 0x7C, 0x78, 0x7B, 0x76, 0x76, 0x78, 0x77, 0x77, 0x7C, 0x7E, 0xFF, +0xF2, 0xF3, 0xED, 0xE7, 0xE8, 0xE0, 0xDE, 0xDC, 0xD7, 0xD8, 0xD3, 0xD4, 0xDA, 0xD7, 0xDF, 0xEB, +0xED, 0x6A, 0x60, 0x5C, 0x52, 0x4F, 0x4D, 0x4B, 0x4A, 0x4B, 0x4B, 0x4D, 0x50, 0x54, 0x5B, 0x66, +0x6F, 0xF1, 0xE6, 0xDF, 0xDA, 0xD8, 0xD8, 0xD7, 0xD9, 0xDB, 0xDE, 0xE2, 0xEA, 0xF1, 0xFA, 0x78, +0x70, 0x6D, 0x69, 0x68, 0x66, 0x65, 0x65, 0x66, 0x66, 0x67, 0x6A, 0x6B, 0x6C, 0x70, 0x74, 0x79, +0xFB, 0xF5, 0xEF, 0xEB, 0xE9, 0xE6, 0xE3, 0xE2, 0xE1, 0xE0, 0xE0, 0xE0, 0xE1, 0xE3, 0xE3, 0xE6, +0xE9, 0xEB, 0xEF, 0xF7, 0x7F, 0x75, 0x6E, 0x6A, 0x66, 0x62, 0x61, 0x5F, 0x5F, 0x60, 0x5F, 0x62, +0x65, 0x66, 0x6C, 0x6E, 0x72, 0x7D, 0xFE, 0xF6, 0xEF, 0xED, 0xE9, 0xE7, 0xE4, 0xE3, 0xE0, 0xDF, +0xE1, 0xDF, 0xE1, 0xE3, 0xE3, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xED, 0xF5, 0xF6, 0xF9, 0x75, +0x76, 0x6E, 0x66, 0x65, 0x5E, 0x5B, 0x5A, 0x56, 0x55, 0x54, 0x53, 0x54, 0x55, 0x57, 0x59, 0x5D, +0x60, 0x66, 0x70, 0x79, 0xFA, 0xF0, 0xEE, 0xEC, 0xED, 0xED, 0xF0, 0xF5, 0xF8, 0xFF, 0xFD, 0x7E, +0x7D, 0xFE, 0x7E, 0xFD, 0xF8, 0xF6, 0xEF, 0xED, 0xEB, 0xE8, 0xE4, 0xE3, 0xE0, 0xDF, 0xDE, 0xDC, +0xDD, 0xDC, 0xDD, 0xE1, 0xE2, 0xE9, 0xF3, 0x7A, 0x65, 0x61, 0x5B, 0x56, 0x56, 0x51, 0x50, 0x52, +0x50, 0x55, 0x58, 0x59, 0x5F, 0x68, 0x70, 0xFC, 0xEE, 0xE9, 0xE4, 0xE1, 0xE4, 0xE4, 0xE7, 0xED, +0xEE, 0xF8, 0xFF, 0x7D, 0x76, 0x76, 0x7A, 0x7B, 0xFF, 0xF9, 0xF7, 0xF1, 0xED, 0xED, 0xEB, 0xE9, +0xEA, 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF2, 0xF3, 0xF0, 0xF1, 0xF3, 0xEF, 0xF3, 0xF5, +0xF4, 0xFB, 0xFC, 0xFC, 0x7C, 0x7A, 0x76, 0x70, 0x6F, 0x6F, 0x6C, 0x6C, 0x6C, 0x68, 0x6B, 0x6B, +0x68, 0x6D, 0x6B, 0x6B, 0x6F, 0x6E, 0x70, 0x77, 0x76, 0x7A, 0x7F, 0xFF, 0xFD, 0xF6, 0xF7, 0xF5, +0xEF, 0xF4, 0xF1, 0xEF, 0xF2, 0xEE, 0xF0, 0xF1, 0xEE, 0xF0, 0xF1, 0xF1, 0xF2, 0xF6, 0xF9, 0xFA, +0x7F, 0x7E, 0x7A, 0x76, 0x7A, 0x73, 0x72, 0x74, 0x6F, 0x73, 0x75, 0x73, 0x75, 0x74, 0x76, 0x73, +0x77, 0x7A, 0x76, 0x7E, 0x78, 0x76, 0x7E, 0x76, 0x7A, 0x79, 0x74, 0x7D, 0x77, 0x77, 0x79, 0x74, +0x76, 0x77, 0x7A, 0x79, 0x7C, 0xFF, 0x79, 0xFC, 0xFA, 0x7E, 0xF4, 0xF9, 0xFC, 0xF6, 0x7E, 0xFF, +0xFF, 0x7B, 0x7C, 0x77, 0x75, 0x76, 0x78, 0x76, 0x74, 0x7B, 0x74, 0x75, 0x7E, 0x74, 0x7B, 0x7C, +0x72, 0x7A, 0x77, 0x74, 0x7A, 0x75, 0x72, 0x73, 0x72, 0x6E, 0x6E, 0x70, 0x6D, 0x70, 0x71, 0x6E, +0x75, 0x73, 0x72, 0x79, 0x78, 0x7B, 0xFF, 0xFF, 0xFD, 0xFA, 0xFC, 0xFB, 0xF7, 0xFD, 0xFE, 0xFA, +0x7D, 0x7D, 0xFE, 0x79, 0x7B, 0x7C, 0x7B, 0x7E, 0x7E, 0xFD, 0xFA, 0xFA, 0xF7, 0xF8, 0xF9, 0xF9, +0xFB, 0xFC, 0xFD, 0xFE, 0xFF, 0x7D, 0x7B, 0x77, 0x78, 0x74, 0x71, 0x74, 0x6F, 0x6F, 0x71, 0x6D, +0x6F, 0x71, 0x6F, 0x73, 0x74, 0x77, 0x7B, 0x7D, 0x7F, 0xFF, 0xFC, 0xFE, 0xFB, 0xF8, 0xFB, 0xF7, +0xF9, 0xFE, 0xFB, 0xFE, 0xFF, 0xFF, 0xFF, 0x7E, 0x7C, 0x7E, 0x7B, 0x7B, 0x7E, 0x7C, 0xFF, 0xFE, +0xFE, 0xFB, 0xFE, 0xFF, 0xFD, 0xFE, 0x7E, 0x7F, 0x7A, 0x78, 0x79, 0x75, 0x74, 0x75, 0x73, 0x75, +0x77, 0x77, 0x79, 0x7B, 0x7D, 0xFF, 0xFA, 0xF8, 0xF4, 0xF2, 0xF5, 0xF1, 0xF2, 0xF6, 0xF3, 0xF6, +0xFA, 0xFB, 0xFC, 0x7D, 0x7A, 0x7A, 0x74, 0x77, 0x78, 0x74, 0x7A, 0x77, 0x7A, 0xFF, 0x7F, 0xF9, +0xFB, 0xFB, 0xF9, 0xFF, 0x7F, 0x7C, 0x7A, 0x77, 0x72, 0x73, 0x6E, 0x6E, 0x6E, 0x6D, 0x70, 0x70, +0x75, 0x7D, 0x7B, 0xFE, 0xFD, 0xFF, 0xFC, 0xFF, 0x7E, 0xFF, 0x7E, 0x7A, 0x79, 0x76, 0x6F, 0x72, +0x72, 0x6F, 0x73, 0x6F, 0x6E, 0x74, 0x72, 0x74, 0x7C, 0x7C, 0xFF, 0xF9, 0xFA, 0xF9, 0xF5, 0xF7, +0xF7, 0xF5, 0xFA, 0xFA, 0xF7, 0xFA, 0xF9, 0xF8, 0xFC, 0xF9, 0xF8, 0xFA, 0xF6, 0xF9, 0xFC, 0xFA, +0xFD, 0xFE, 0xFD, 0xFF, 0x7E, 0x7E, 0x7D, 0x7A, 0x7C, 0x7B, 0x79, 0x7C, 0x7B, 0x7B, 0xFF, 0x7E, +0xFF, 0xFC, 0xFE, 0xFE, 0xFB, 0xFD, 0x7C, 0x7F, 0x7F, 0x76, 0x78, 0x78, 0x74, 0x78, 0x78, 0x78, +0x7B, 0x7A, 0x7A, 0x79, 0x77, 0x78, 0x79, 0x78, 0x7A, 0x7B, 0x7A, 0x7C, 0x7F, 0x7D, 0xFF, 0xFC, +0xFD, 0xFA, 0xFB, 0xFD, 0xFE, 0xFF, 0x7F, 0x7D, 0x7C, 0x7A, 0x7B, 0x7D, 0x7D, 0x7E, 0x7E, 0x7D, +0x7D, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7C, 0x7B, 0x7C, 0x7D, 0x7F, 0xFF, 0xFE, 0xFC, 0xFC, 0xFB, +0xFB, 0xFB, 0xFB, 0xFA, 0xFA, 0xFB, 0xFB, 0xFD, 0xFF, 0x7D, 0x7B, 0x7B, 0x7A, 0x77, 0x77, 0x78, +0x77, 0x77, 0x79, 0x7A, 0x7B, 0x7B, 0x7D, 0x7E, 0xFF, 0xFE, 0xFE, 0xFD, 0xFC, 0xFC, 0xFC, 0xFC, +0xFE, 0xFF, 0xFF, 0x7E, 0x7B, 0x78, 0x77, 0x76, 0x74, 0x73, 0x73, 0x71, 0x72, 0x73, 0x73, 0x74, +0x75, 0x76, 0x77, 0x77, 0x78, 0x78, 0x7A, 0x7D, 0x7D, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x7F, 0x7E, 0x7D, 0x7D, 0x7C, 0x7A, 0x79, 0x79, 0x78, 0x77, 0x78, 0x79, 0x7A, 0x7A, 0x7B, 0x7A, +0x79, 0x7B, 0x7B, 0x7B, 0x7C, 0x7A, 0x7A, 0x79, 0x78, 0x78, 0x76, 0x76, 0x75, 0x73, 0x73, 0x73, +0x74, 0x77, 0x79, 0x7A, 0x7D, 0x7F, 0xFE, 0xFD, 0xFD, 0xFD, 0xFC, 0xFC, 0xFD, 0xFD, 0xFE, 0xFF, +0xFF, 0xFF, 0x7F, 0x7E, 0x7E, 0x7D, 0x7C, 0x7C, 0x7C, 0x7B, 0x7B, 0x7C, 0x7D, 0x7D, 0x7D, 0x7E, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4C, 0x49, 0x53, 0x54, 0x4A, 0x00, +0x00, 0x00, 0x49, 0x4E, 0x46, 0x4F, 0x49, 0x53, 0x46, 0x54, 0x3E, 0x00, 0x00, 0x00, 0x46, 0x69, +0x6C, 0x65, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x47, 0x6F, +0x6C, 0x64, 0x57, 0x61, 0x76, 0x65, 0x2E, 0x20, 0x20, 0x47, 0x6F, 0x6C, 0x64, 0x57, 0x61, 0x76, +0x65, 0x20, 0x63, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x43, 0x29, 0x20, +0x43, 0x68, 0x72, 0x69, 0x73, 0x20, 0x43, 0x72, 0x61, 0x69, 0x67, 0x00 + +}; + +static const uint8_t shaun_png[] PROGMEM = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x57, 0x08, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x0C, 0xE0, + 0x57, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4B, 0x47, 0x44, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xA0, + 0xBD, 0xA7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x42, 0x8A, 0x00, + 0x00, 0x42, 0x8A, 0x01, 0x34, 0xA8, 0x6C, 0x25, 0x00, 0x00, 0x00, 0x09, 0x76, 0x70, 0x41, 0x67, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x57, 0x00, 0x73, 0x4D, 0x3B, 0xD6, 0x00, 0x00, 0x1B, + 0x47, 0x49, 0x44, 0x41, 0x54, 0x78, 0xDA, 0xED, 0x9D, 0x79, 0x74, 0x14, 0x55, 0xBE, 0xC7, 0x3F, + 0x55, 0xDD, 0x49, 0x67, 0x5F, 0x3A, 0x7B, 0x42, 0x82, 0x61, 0x49, 0x02, 0x81, 0xB0, 0x29, 0x32, + 0x22, 0x3C, 0x16, 0x41, 0x45, 0x10, 0x15, 0x7D, 0x13, 0x65, 0xD0, 0x81, 0xE3, 0x7B, 0xCC, 0xD3, + 0x51, 0x04, 0xF5, 0xE1, 0xB8, 0xCE, 0xE6, 0x28, 0x22, 0x23, 0x33, 0x2C, 0x2A, 0x22, 0xEE, 0x02, + 0x22, 0x0C, 0x2A, 0x83, 0x28, 0x7B, 0x20, 0xC8, 0x92, 0x10, 0xD9, 0x97, 0x40, 0x08, 0x81, 0x90, + 0x3D, 0x64, 0x21, 0xE9, 0x4E, 0x3A, 0x5D, 0xF7, 0xFD, 0x71, 0xBB, 0x93, 0x0E, 0x10, 0xD2, 0x59, + 0x3A, 0x01, 0xCF, 0xFB, 0x9E, 0xD3, 0xE7, 0xA4, 0x6E, 0xA5, 0x6E, 0xDD, 0xFA, 0xFD, 0xEA, 0xFE, + 0xEE, 0xEF, 0xFE, 0xB6, 0x52, 0xB8, 0xFE, 0xA1, 0x03, 0x7A, 0x01, 0x43, 0x81, 0x9B, 0x80, 0x6A, + 0x60, 0x3F, 0xA0, 0x02, 0x8F, 0x00, 0xB7, 0x03, 0xA1, 0x80, 0x00, 0x72, 0x81, 0xAD, 0xC0, 0xC7, + 0xC0, 0x6E, 0xA0, 0xAE, 0xB3, 0x07, 0xFF, 0x4B, 0x43, 0x30, 0xF0, 0x67, 0x20, 0x1B, 0xB0, 0x20, + 0x89, 0xAE, 0x01, 0x95, 0x40, 0xB9, 0xED, 0xF8, 0x6A, 0xBF, 0x3C, 0xDB, 0x75, 0x81, 0xCD, 0xF4, + 0xAF, 0x03, 0x8C, 0x40, 0x2C, 0xD0, 0x13, 0x88, 0x00, 0x0C, 0x9D, 0xF9, 0xC0, 0x4A, 0x27, 0xDD, + 0xD7, 0x13, 0x88, 0x07, 0xFA, 0x03, 0x51, 0x36, 0x22, 0xE4, 0x03, 0x87, 0x80, 0x83, 0x48, 0x62, + 0x1B, 0x81, 0x85, 0x40, 0x72, 0x2B, 0xC7, 0xA9, 0x01, 0x5F, 0x00, 0x2F, 0x03, 0x35, 0xB6, 0xE3, + 0x0A, 0xDB, 0xDF, 0xDE, 0xC0, 0x48, 0x60, 0x22, 0x30, 0x08, 0xC9, 0x78, 0x3D, 0x92, 0xD1, 0x99, + 0xC0, 0x0F, 0xC0, 0x5A, 0xE0, 0x5C, 0x47, 0x13, 0xA6, 0xA3, 0x19, 0xA2, 0x07, 0x46, 0x00, 0x4F, + 0x22, 0x45, 0x4D, 0xD0, 0x65, 0x63, 0xB8, 0x84, 0x14, 0x47, 0xEF, 0x03, 0x09, 0xC0, 0x4B, 0x48, + 0xD1, 0x44, 0x40, 0x40, 0x00, 0x31, 0x31, 0x31, 0x54, 0x56, 0x56, 0x92, 0x93, 0x93, 0x83, 0xD5, + 0x6A, 0x25, 0x3A, 0x3A, 0x9A, 0x89, 0x13, 0x27, 0x32, 0x60, 0xC0, 0x00, 0xAC, 0x56, 0x2B, 0xBB, + 0x77, 0xEF, 0x66, 0xDD, 0xBA, 0x75, 0x14, 0x16, 0x16, 0xDA, 0xFB, 0xAB, 0x03, 0x4E, 0x23, 0x67, + 0x4D, 0x9D, 0x8D, 0xC0, 0xFB, 0x90, 0x22, 0xF0, 0x1E, 0xC0, 0xAB, 0x89, 0x71, 0x6A, 0xC0, 0x11, + 0xE0, 0x0D, 0xE0, 0x6B, 0xE4, 0xEC, 0xFC, 0xC5, 0xC1, 0x07, 0x78, 0x0D, 0x28, 0xA5, 0x69, 0x51, + 0x63, 0xFF, 0x99, 0x80, 0x2A, 0xFB, 0xF1, 0xC0, 0x81, 0x03, 0xC5, 0xA6, 0x4D, 0x9B, 0x44, 0x49, + 0x49, 0x89, 0xC8, 0xCA, 0xCA, 0x12, 0xAF, 0xBF, 0xFE, 0xBA, 0x78, 0xFC, 0xF1, 0xC7, 0x45, 0x5A, + 0x5A, 0x9A, 0xD0, 0x34, 0x4D, 0xD8, 0x51, 0x57, 0x57, 0x27, 0xB6, 0x6E, 0xDD, 0x2A, 0xFA, 0xF7, + 0xEF, 0xDF, 0x5C, 0xFF, 0x8D, 0x7E, 0x8A, 0xA2, 0x08, 0x55, 0x55, 0xAF, 0x76, 0xAE, 0x02, 0x78, + 0x0E, 0xF9, 0x22, 0x75, 0x08, 0x3A, 0x6A, 0x86, 0x78, 0x02, 0x7F, 0x05, 0x9E, 0x02, 0xDC, 0x00, + 0x14, 0x45, 0x21, 0x24, 0x24, 0x84, 0xE8, 0xE8, 0x68, 0xDC, 0xDD, 0xDD, 0xB9, 0x70, 0xE1, 0x02, + 0xB9, 0xB9, 0xB9, 0xD4, 0xD5, 0x35, 0x5E, 0x87, 0xBD, 0xBC, 0xBC, 0x58, 0xB1, 0x62, 0x05, 0x13, + 0x26, 0x4C, 0xA8, 0x6F, 0x13, 0x42, 0x20, 0x84, 0x40, 0x55, 0xD5, 0xAB, 0xDE, 0x6C, 0xD7, 0xAE, + 0x5D, 0x3C, 0xFC, 0xF0, 0xC3, 0xE4, 0xE4, 0xE4, 0x34, 0xFD, 0xE0, 0x8A, 0x42, 0x7C, 0x7C, 0x3C, + 0xF7, 0xDC, 0x73, 0x0F, 0xFD, 0xFA, 0xF5, 0xC3, 0xC3, 0xC3, 0x83, 0xEC, 0xEC, 0x6C, 0x36, 0x6C, + 0xD8, 0x40, 0x6A, 0x6A, 0x2A, 0x35, 0x35, 0x35, 0xF6, 0x7F, 0xAD, 0xB4, 0x8D, 0xFB, 0x73, 0xC0, + 0xDA, 0x41, 0xF4, 0x72, 0x39, 0x7E, 0x8F, 0x7C, 0xEB, 0x05, 0x20, 0xC2, 0xC2, 0xC2, 0xC4, 0x2B, + 0xAF, 0xBC, 0x22, 0x0E, 0x1E, 0x3C, 0x28, 0x2E, 0x5E, 0xBC, 0x28, 0x2A, 0x2A, 0x2A, 0x44, 0x56, + 0x56, 0x96, 0x58, 0xBC, 0x78, 0xB1, 0x48, 0x48, 0x48, 0x68, 0xF4, 0x96, 0xC6, 0xC4, 0xC4, 0x88, + 0x33, 0x67, 0xCE, 0x88, 0x96, 0xE2, 0xE3, 0x8F, 0x3F, 0x16, 0x77, 0xDE, 0x79, 0xA7, 0x78, 0xFE, + 0xF9, 0xE7, 0xC5, 0xBB, 0xEF, 0xBE, 0x2B, 0xDE, 0x7E, 0xFB, 0x6D, 0xF1, 0xE0, 0x83, 0x0F, 0x0A, + 0xA3, 0xD1, 0x28, 0xDC, 0xDC, 0xDC, 0xC4, 0xD4, 0xA9, 0x53, 0xC5, 0xE9, 0xD3, 0xA7, 0xAF, 0xB8, + 0xAE, 0xA2, 0xA2, 0x42, 0x2C, 0x58, 0xB0, 0x40, 0x04, 0x07, 0x07, 0x3B, 0x8E, 0xA3, 0x00, 0x58, + 0x0D, 0xFC, 0x0E, 0x08, 0xEF, 0x6C, 0x62, 0xB6, 0x15, 0x7D, 0x80, 0x33, 0xF6, 0x87, 0x8B, 0x8D, + 0x8D, 0x15, 0xEB, 0xD7, 0xAF, 0x6F, 0x24, 0x6A, 0x1C, 0x91, 0x91, 0x91, 0x21, 0x06, 0x0F, 0x1E, + 0x2C, 0x00, 0x61, 0x30, 0x18, 0xC4, 0xAF, 0x7F, 0xFD, 0x6B, 0x51, 0x51, 0x51, 0xD1, 0x62, 0x86, + 0x58, 0xAD, 0x56, 0x61, 0x36, 0x9B, 0x1B, 0xDD, 0xA7, 0xA6, 0xA6, 0x46, 0xEC, 0xD8, 0xB1, 0x43, + 0xAC, 0x5A, 0xB5, 0x4A, 0x94, 0x97, 0x97, 0x37, 0x79, 0xAD, 0xA6, 0x69, 0x62, 0xF1, 0xE2, 0xC5, + 0xC2, 0xC3, 0xC3, 0xE3, 0x72, 0x11, 0x66, 0x05, 0x76, 0x02, 0x43, 0x3A, 0x9B, 0xA8, 0x6D, 0xC1, + 0xDB, 0xF6, 0x07, 0xF2, 0xF5, 0xF5, 0x15, 0x2B, 0x56, 0xAC, 0x68, 0x96, 0x98, 0xC7, 0x8E, 0x1D, + 0x13, 0xF3, 0xE7, 0xCF, 0x17, 0x1B, 0x36, 0x6C, 0x10, 0xA5, 0xA5, 0xA5, 0x2D, 0x66, 0x46, 0x7B, + 0xA0, 0xB2, 0xB2, 0x52, 0x4C, 0x98, 0x30, 0xA1, 0xA9, 0x75, 0xE7, 0x00, 0x52, 0x31, 0x68, 0x77, + 0xB8, 0x7A, 0x0D, 0x89, 0x02, 0x36, 0xDA, 0x07, 0x3F, 0x79, 0xF2, 0x64, 0x3E, 0xFC, 0xF0, 0x43, + 0x0C, 0x86, 0x4E, 0x55, 0xF5, 0x9D, 0xC6, 0xC1, 0x83, 0x07, 0x59, 0xBB, 0x76, 0x2D, 0x16, 0x8B, + 0x85, 0x94, 0x94, 0x14, 0x52, 0x53, 0x53, 0xB1, 0x5A, 0xEB, 0x97, 0x91, 0xF7, 0x91, 0xDA, 0xE2, + 0x0D, 0xB5, 0xAE, 0x8C, 0x07, 0xCC, 0x80, 0xF0, 0xF4, 0xF4, 0x14, 0xEB, 0xD6, 0xAD, 0xEB, 0x94, + 0xB7, 0xBD, 0x3D, 0x50, 0x54, 0x54, 0x24, 0x92, 0x93, 0x93, 0x1D, 0x67, 0x49, 0x36, 0xD0, 0xBD, + 0xB3, 0x09, 0xDC, 0x12, 0x84, 0x00, 0xAB, 0xEC, 0x0F, 0x10, 0x1F, 0x1F, 0x2F, 0x72, 0x73, 0x73, + 0x3B, 0x9B, 0xAE, 0x6D, 0xC2, 0x96, 0x2D, 0x5B, 0x84, 0x8F, 0x8F, 0x8F, 0xA3, 0x6A, 0x7E, 0x47, + 0x7B, 0x13, 0x4D, 0x6D, 0x7B, 0x17, 0x57, 0x85, 0x37, 0x72, 0x53, 0x35, 0xC9, 0xDE, 0xD0, 0xAD, + 0x5B, 0x37, 0x8C, 0x46, 0xA3, 0x8B, 0x6E, 0xD7, 0x31, 0xF0, 0xF5, 0xF5, 0x45, 0xAF, 0xAF, 0xDF, + 0x92, 0xA8, 0x80, 0x47, 0x7B, 0xDF, 0xC3, 0x55, 0x0C, 0xF9, 0x4F, 0xE0, 0x37, 0xD8, 0xD6, 0xA8, + 0x84, 0x84, 0x04, 0x9E, 0x7E, 0xFA, 0x69, 0x3C, 0x3C, 0xDA, 0x7D, 0xFC, 0x1D, 0x8A, 0x5D, 0xBB, + 0x76, 0x51, 0x56, 0x56, 0x66, 0x3F, 0xAC, 0x42, 0xDA, 0xCC, 0xDA, 0x15, 0xAE, 0xD8, 0x81, 0x1A, + 0x81, 0xC7, 0xB1, 0x19, 0xE9, 0x12, 0x12, 0x12, 0xF8, 0xEC, 0xB3, 0xCF, 0xB8, 0xF9, 0xE6, 0x9B, + 0x5D, 0x44, 0xA6, 0x8E, 0x43, 0x5E, 0x5E, 0x23, 0xFA, 0x0B, 0x5C, 0xF7, 0x42, 0xB7, 0x2B, 0x46, + 0x21, 0x6D, 0x52, 0x42, 0xAF, 0xD7, 0x8B, 0x25, 0x4B, 0x96, 0x74, 0xB6, 0xE8, 0x6F, 0x37, 0xEC, + 0xDD, 0xBB, 0x57, 0x84, 0x86, 0x86, 0x3A, 0x2E, 0xEC, 0xB3, 0xDB, 0x9B, 0x78, 0xAE, 0xE0, 0x70, + 0x12, 0x72, 0x0D, 0xA1, 0x6B, 0xD7, 0xAE, 0x8C, 0x1D, 0x3B, 0xD6, 0x85, 0xBC, 0xEF, 0x58, 0x24, + 0x25, 0x25, 0x31, 0x64, 0x48, 0xA3, 0x3D, 0xE1, 0x2D, 0xB4, 0xB3, 0x94, 0x71, 0x05, 0x43, 0x22, + 0xED, 0x7F, 0x44, 0x47, 0x47, 0x13, 0x12, 0x12, 0xE2, 0x1A, 0xEA, 0x74, 0x02, 0x0C, 0x06, 0x03, + 0x89, 0x89, 0x89, 0x8E, 0x4D, 0x51, 0x48, 0x3B, 0x5D, 0xBB, 0xC1, 0x15, 0x0C, 0xA9, 0xEF, 0x53, + 0xA7, 0xD3, 0x35, 0x69, 0x00, 0xBC, 0x51, 0xE1, 0xEB, 0xEB, 0xEB, 0x78, 0x68, 0xE0, 0x06, 0x98, + 0x21, 0xF5, 0xCE, 0x88, 0xBC, 0xBC, 0x3C, 0x47, 0xAD, 0xE4, 0x17, 0x81, 0xD2, 0xD2, 0x52, 0xC7, + 0xC3, 0x6A, 0xDA, 0xD9, 0x57, 0xE2, 0x0A, 0x86, 0x1C, 0x46, 0xEE, 0xCE, 0xC9, 0xCA, 0xCA, 0x22, + 0x35, 0x35, 0xD5, 0x65, 0xC4, 0xE9, 0x68, 0x54, 0x55, 0x55, 0x91, 0x91, 0x91, 0xE1, 0xD8, 0x94, + 0x8D, 0x64, 0xCA, 0x75, 0x8D, 0x70, 0xE0, 0x67, 0x6C, 0x9A, 0xC8, 0xD0, 0xA1, 0x43, 0x45, 0x76, + 0x76, 0x76, 0x67, 0x2B, 0x48, 0xED, 0x82, 0x1F, 0x7E, 0xF8, 0x41, 0xF8, 0xF9, 0xF9, 0x39, 0x6A, + 0x59, 0x4F, 0x74, 0x36, 0xB1, 0x9D, 0xC5, 0x73, 0x48, 0xA3, 0x9B, 0x00, 0xC4, 0xC8, 0x91, 0x23, + 0xC5, 0x8F, 0x3F, 0xFE, 0x28, 0xCC, 0x66, 0xB3, 0xCB, 0x88, 0x65, 0xB5, 0x5A, 0x5D, 0xCA, 0x8C, + 0xAC, 0xAC, 0x2C, 0x31, 0x6C, 0xD8, 0x30, 0x47, 0x66, 0x64, 0x01, 0x71, 0x9D, 0x4D, 0x68, 0x67, + 0x61, 0x04, 0xD6, 0x39, 0x0C, 0x5E, 0xF8, 0xF8, 0xF8, 0x88, 0x05, 0x0B, 0x16, 0xB8, 0x84, 0x58, + 0x99, 0x99, 0x99, 0x62, 0xFA, 0xF4, 0xE9, 0x62, 0xD9, 0xB2, 0x65, 0xC2, 0x64, 0x32, 0x5D, 0x71, + 0x5E, 0xD3, 0x34, 0x71, 0xEE, 0xDC, 0x39, 0x91, 0x97, 0x97, 0xD7, 0xAA, 0xFE, 0x4F, 0x9E, 0x3C, + 0x29, 0x46, 0x8C, 0x18, 0x71, 0xB9, 0x09, 0xFE, 0xF5, 0xCE, 0x26, 0xB2, 0xB3, 0x08, 0x46, 0x5A, + 0x79, 0x1B, 0x31, 0x04, 0x10, 0x49, 0x49, 0x49, 0x22, 0x27, 0x27, 0xA7, 0x5D, 0x99, 0x61, 0xB5, + 0x5A, 0xC5, 0x8C, 0x19, 0x33, 0x04, 0x20, 0xBC, 0xBC, 0xBC, 0xC4, 0xA3, 0x8F, 0x3E, 0x2A, 0x36, + 0x6F, 0xDE, 0x2C, 0xCE, 0x9F, 0x3F, 0x2F, 0x0A, 0x0B, 0x0B, 0xC5, 0xE1, 0xC3, 0x87, 0xC5, 0xDC, + 0xB9, 0x73, 0x45, 0x42, 0x42, 0x82, 0x18, 0x33, 0x66, 0x8C, 0x28, 0x28, 0x28, 0x68, 0xF1, 0x3D, + 0x96, 0x2E, 0x5D, 0x7A, 0x39, 0x33, 0x34, 0x60, 0x3B, 0xF0, 0xDF, 0x40, 0x37, 0x64, 0x38, 0xD1, + 0x75, 0x87, 0x28, 0xA4, 0xEF, 0x79, 0x2F, 0x36, 0x93, 0x3B, 0x57, 0x09, 0x26, 0x98, 0x35, 0x6B, + 0x56, 0xBB, 0x8A, 0xAE, 0x6D, 0xDB, 0xB6, 0x89, 0xD0, 0xD0, 0x50, 0xE1, 0xED, 0x69, 0x10, 0xE1, + 0xC1, 0x01, 0x02, 0x10, 0x9E, 0x5E, 0x5E, 0xA2, 0x5B, 0xB7, 0xEE, 0xA2, 0x57, 0xAF, 0x5E, 0x22, + 0x38, 0x24, 0x44, 0x28, 0x8A, 0x22, 0x00, 0xE1, 0xE6, 0xE6, 0x26, 0x3E, 0xFA, 0xE8, 0xA3, 0x16, + 0xDF, 0x63, 0xCD, 0x9A, 0x35, 0x42, 0xAF, 0xD7, 0x5F, 0xCD, 0x51, 0x55, 0x87, 0x14, 0x5D, 0x0B, + 0x80, 0x5B, 0x01, 0xF7, 0xB6, 0x12, 0xB1, 0x3D, 0x38, 0x1B, 0x0A, 0x4C, 0x07, 0xE6, 0x21, 0x0D, + 0x8A, 0x5D, 0x70, 0xD0, 0xCD, 0x75, 0x3A, 0x1D, 0xE1, 0xE1, 0xE1, 0x24, 0x25, 0x25, 0x61, 0x36, + 0x9B, 0x49, 0x4D, 0x4D, 0xC5, 0x64, 0x32, 0xD1, 0xB7, 0x6F, 0x5F, 0x7C, 0x7C, 0x7C, 0x00, 0x19, + 0xB4, 0x50, 0x50, 0x50, 0x40, 0x59, 0x59, 0x19, 0xBE, 0xBE, 0xBE, 0x28, 0x8A, 0x73, 0x7E, 0xB3, + 0xEC, 0xEC, 0x6C, 0x9E, 0x7E, 0xFA, 0x69, 0x8E, 0x1D, 0x3B, 0xC6, 0x8C, 0x87, 0xC7, 0x31, 0x77, + 0xC6, 0x14, 0x02, 0xFD, 0x7C, 0xA8, 0xAA, 0x36, 0x51, 0x5A, 0x5A, 0x42, 0x65, 0x45, 0x39, 0x81, + 0x3E, 0x06, 0xEE, 0xFC, 0x55, 0x7F, 0x86, 0x0D, 0x48, 0x20, 0xFD, 0xE8, 0x69, 0xB2, 0xB3, 0xCF, + 0x72, 0xC7, 0x1D, 0x77, 0xB4, 0xC8, 0xF2, 0xBC, 0x7E, 0xFD, 0x7A, 0x36, 0x6C, 0xD8, 0x80, 0x9F, + 0x9F, 0x1F, 0x3A, 0x9D, 0x0E, 0x8B, 0xA5, 0x5E, 0xD3, 0x55, 0x91, 0xC1, 0x78, 0x83, 0x81, 0xFB, + 0x81, 0xDE, 0x40, 0x11, 0xD2, 0xE8, 0xA8, 0xB5, 0x03, 0x6D, 0x5B, 0x04, 0x37, 0xA4, 0x68, 0x4A, + 0x45, 0xBE, 0x29, 0x8D, 0x66, 0x42, 0x97, 0x2E, 0x5D, 0xC4, 0x23, 0x8F, 0x3C, 0x22, 0x3E, 0xF9, + 0xE4, 0x13, 0x71, 0xEC, 0xD8, 0x31, 0x51, 0x55, 0x55, 0x25, 0x5E, 0x7A, 0xE9, 0x25, 0x01, 0x08, + 0x9D, 0x4E, 0x27, 0x06, 0x0E, 0x1A, 0x24, 0x66, 0xCF, 0x9E, 0x2D, 0xE6, 0xCF, 0x9F, 0x2F, 0x66, + 0xCE, 0x9C, 0x29, 0xFA, 0xF4, 0xE9, 0x23, 0x7A, 0xF7, 0xEE, 0x2D, 0xD6, 0xAC, 0x59, 0xE3, 0xD4, + 0x02, 0x9D, 0x99, 0x99, 0x29, 0xC6, 0x8D, 0x1B, 0x27, 0x95, 0x86, 0x9B, 0x13, 0x45, 0xEE, 0x86, + 0x25, 0x42, 0x64, 0xAC, 0x16, 0x5A, 0xFA, 0x2A, 0x51, 0xB6, 0xED, 0x63, 0x71, 0xFA, 0x9B, 0x05, + 0xE2, 0xF8, 0xD7, 0xF3, 0xC5, 0x85, 0x0D, 0xEF, 0x0B, 0xCB, 0xDE, 0x15, 0xA2, 0x74, 0xFB, 0x27, + 0x62, 0xFC, 0xB0, 0x41, 0x02, 0x10, 0x77, 0xDF, 0x7D, 0xB7, 0x38, 0x7A, 0xF4, 0x68, 0xA3, 0xFE, + 0x34, 0x4D, 0x13, 0xFB, 0xF7, 0xEF, 0x17, 0x6F, 0xBD, 0xF5, 0x96, 0xD8, 0xBD, 0x7B, 0xB7, 0xB0, + 0x58, 0x2C, 0xC2, 0x62, 0xB1, 0x88, 0x6D, 0xDB, 0xB6, 0x89, 0xB8, 0xB8, 0x38, 0xE1, 0xEE, 0xEE, + 0x2E, 0xDE, 0x7B, 0xEF, 0x3D, 0xB1, 0x69, 0xD3, 0x26, 0xF1, 0xDC, 0x73, 0xCF, 0x89, 0x7E, 0xFD, + 0xFA, 0x09, 0x77, 0x77, 0xF7, 0xAB, 0xCD, 0x9A, 0x12, 0xE0, 0x03, 0x64, 0x10, 0x60, 0x87, 0xC5, + 0xBD, 0x45, 0x20, 0x67, 0x44, 0x99, 0xE3, 0x60, 0x74, 0x3A, 0x9D, 0x48, 0x4A, 0x4A, 0x12, 0x6F, + 0xBE, 0xF9, 0xA6, 0x38, 0x76, 0xEC, 0x98, 0xB0, 0x58, 0x2C, 0xF5, 0x0F, 0x6C, 0x32, 0x99, 0xC4, + 0xA4, 0x49, 0x93, 0x04, 0x20, 0xBA, 0x84, 0x1A, 0x85, 0xA7, 0xC1, 0xAD, 0xD1, 0x83, 0x78, 0xB8, + 0xBB, 0x09, 0x9D, 0xAA, 0x8A, 0xA0, 0xA0, 0x20, 0xF1, 0xDA, 0x6B, 0xAF, 0x89, 0xB3, 0x67, 0xCF, + 0x5E, 0x35, 0x10, 0xA2, 0xA2, 0xA2, 0x42, 0xAC, 0x5C, 0xB9, 0x52, 0x0C, 0x18, 0x30, 0x40, 0x00, + 0xE2, 0x96, 0xDE, 0xDD, 0xC5, 0xC1, 0x95, 0x6F, 0x0B, 0xB1, 0xFF, 0x6B, 0x21, 0xD2, 0xBE, 0x92, + 0xBF, 0xF4, 0x55, 0x42, 0xEC, 0x5F, 0x25, 0xDB, 0xD2, 0x57, 0xC9, 0xB6, 0xFD, 0x5F, 0x8B, 0xC3, + 0x5F, 0xFD, 0x5D, 0x0C, 0x4E, 0xEC, 0x21, 0x00, 0xD1, 0xA3, 0x47, 0x0F, 0xF1, 0x87, 0x3F, 0xFC, + 0x41, 0xAC, 0x5A, 0xB5, 0x4A, 0xAC, 0x5C, 0xB9, 0x52, 0xCC, 0x9A, 0x35, 0x4B, 0xC4, 0xC4, 0xC4, + 0x08, 0x40, 0x84, 0x87, 0x87, 0x8B, 0xFB, 0xEE, 0xBB, 0x4F, 0x4C, 0x9C, 0x38, 0xB1, 0xDE, 0x98, + 0x38, 0x79, 0xF2, 0xE4, 0xFA, 0x60, 0x0B, 0x4D, 0xD3, 0x44, 0x7E, 0x7E, 0xBE, 0x58, 0xBE, 0x7C, + 0xB9, 0x18, 0x3F, 0x7E, 0xBC, 0xF0, 0xF5, 0xF5, 0xBD, 0x1A, 0x63, 0xCE, 0x21, 0x8D, 0x8F, 0xC1, + 0x2D, 0x21, 0x6C, 0x4B, 0x39, 0xA8, 0x20, 0x83, 0x9E, 0x5F, 0x07, 0x86, 0x39, 0x5E, 0x1F, 0x17, + 0x17, 0xC7, 0xF4, 0xE9, 0xD3, 0x49, 0x4E, 0x4E, 0x26, 0x32, 0x32, 0xF2, 0x8A, 0x0B, 0x77, 0xEC, + 0xD8, 0xC1, 0x84, 0x09, 0x13, 0xF0, 0xD4, 0x2B, 0x2C, 0xFF, 0xDB, 0x33, 0x58, 0xEA, 0xEA, 0x48, + 0x3B, 0x7A, 0x9A, 0xD2, 0x8A, 0x4B, 0x04, 0x07, 0xF8, 0x31, 0xA8, 0x57, 0x37, 0x52, 0x7F, 0x3E, + 0xCE, 0x3F, 0x96, 0xAF, 0xA7, 0xEC, 0x52, 0x35, 0x71, 0xF1, 0xF1, 0xDC, 0x39, 0x76, 0x2C, 0x03, + 0x07, 0x0E, 0x24, 0x28, 0x28, 0x88, 0xAA, 0xAA, 0x2A, 0x4E, 0x9C, 0x38, 0xC1, 0x96, 0x2D, 0x5B, + 0xD8, 0xB3, 0x67, 0x0F, 0x96, 0xDA, 0x5A, 0xEE, 0xBA, 0xAD, 0x3F, 0x73, 0x9F, 0x99, 0x42, 0xAF, + 0x6E, 0xD1, 0xA0, 0x39, 0x21, 0x21, 0x54, 0x95, 0x13, 0x67, 0xCE, 0xF3, 0xA7, 0x25, 0xAB, 0xF8, + 0x76, 0x7B, 0x1A, 0x55, 0xE6, 0x1A, 0xDB, 0x23, 0x08, 0x00, 0x02, 0x7C, 0xBD, 0x49, 0xEA, 0x19, + 0xC3, 0xD1, 0xAC, 0xF3, 0x14, 0x97, 0x55, 0xA2, 0x28, 0x0A, 0xE1, 0xE1, 0xE1, 0x24, 0x27, 0x27, + 0x33, 0x7B, 0xF6, 0x6C, 0xC2, 0xC2, 0xC2, 0xAE, 0xE8, 0xB2, 0xBA, 0xBA, 0x9A, 0xD4, 0xD4, 0x54, + 0x96, 0x2E, 0x5D, 0xCA, 0xF7, 0xDF, 0x7F, 0x4F, 0x65, 0x65, 0xA5, 0xE3, 0x69, 0x2B, 0xB0, 0x03, + 0xF8, 0x13, 0x90, 0x82, 0x13, 0x62, 0xAC, 0x25, 0x0C, 0x71, 0x03, 0x26, 0x03, 0x7F, 0x41, 0xAE, + 0x13, 0x80, 0xB4, 0xED, 0x4C, 0x99, 0x32, 0x85, 0x99, 0x33, 0x67, 0xD2, 0xA3, 0x47, 0x8F, 0xAB, + 0x5E, 0x28, 0x84, 0x60, 0xC6, 0x8C, 0x19, 0x2C, 0x58, 0xB0, 0x80, 0xA9, 0xF7, 0x8E, 0x64, 0xC9, + 0x2B, 0xBF, 0xB3, 0x79, 0xDE, 0x84, 0xA4, 0x85, 0x22, 0x87, 0x52, 0x67, 0xA9, 0x63, 0xEB, 0xDE, + 0x43, 0xFC, 0x73, 0xE5, 0xF7, 0x6C, 0x4F, 0x3F, 0x42, 0x65, 0xB5, 0x59, 0x0E, 0x52, 0x51, 0x11, + 0x42, 0x3E, 0x8B, 0x5E, 0xA7, 0x92, 0xD8, 0x2D, 0x9A, 0x69, 0x13, 0x47, 0x32, 0xE5, 0x9E, 0xFF, + 0x20, 0xD0, 0xDF, 0xD7, 0x39, 0x66, 0x38, 0x30, 0xA5, 0xDA, 0x64, 0x66, 0xD7, 0x81, 0x13, 0x6C, + 0xD9, 0x77, 0x98, 0xAC, 0xDC, 0x02, 0x54, 0x45, 0xA1, 0x67, 0x4C, 0x04, 0x63, 0x6E, 0x4D, 0x62, + 0x50, 0xAF, 0x6E, 0x7C, 0xBA, 0x6E, 0x3B, 0x4F, 0xCE, 0x59, 0x4A, 0x78, 0x44, 0x24, 0x5F, 0x7E, + 0xF9, 0x25, 0x43, 0x87, 0x0E, 0x45, 0xA7, 0xBB, 0xF6, 0x72, 0x6B, 0x36, 0x9B, 0xD9, 0xB6, 0x6D, + 0x1B, 0x0B, 0x17, 0x2E, 0x64, 0xD3, 0xA6, 0x4D, 0x8E, 0x81, 0x76, 0x20, 0xE3, 0xBA, 0xDE, 0x04, + 0x96, 0xD0, 0xCC, 0xCE, 0xDE, 0x59, 0x86, 0xF8, 0x02, 0xFF, 0x0B, 0x3C, 0x83, 0x0C, 0x09, 0x05, + 0x20, 0x31, 0x31, 0x91, 0x3F, 0xFE, 0xF1, 0x8F, 0xDC, 0x7B, 0xEF, 0xBD, 0xB8, 0xBB, 0x37, 0xAD, + 0x60, 0x9C, 0x39, 0x73, 0x86, 0x31, 0x63, 0xC7, 0x92, 0x9B, 0x73, 0x96, 0x7F, 0xCD, 0x7B, 0x9E, + 0xBB, 0x6E, 0x1F, 0x08, 0xD6, 0x26, 0x88, 0xA8, 0x53, 0xA9, 0xAA, 0x32, 0xF1, 0xF3, 0xF1, 0x33, + 0xEC, 0x3C, 0x70, 0x82, 0xE3, 0xD9, 0xB9, 0x54, 0x5C, 0xAA, 0xC6, 0xD3, 0xC3, 0x40, 0x4C, 0x78, + 0x30, 0x83, 0x13, 0xBB, 0x33, 0xA4, 0x6F, 0x1C, 0xE1, 0x21, 0x81, 0x36, 0xE1, 0x20, 0x9C, 0x67, + 0x86, 0xE3, 0x53, 0xAB, 0x2A, 0x08, 0xD0, 0xEA, 0xAC, 0xA0, 0x80, 0xAA, 0xD3, 0xD5, 0x4F, 0x96, + 0xBC, 0xA2, 0x52, 0xEE, 0x7A, 0xF2, 0xAF, 0x1C, 0x3D, 0x9B, 0xC7, 0x8A, 0xE5, 0xCB, 0x99, 0x34, + 0x69, 0x92, 0xD3, 0x5D, 0x57, 0x56, 0x56, 0xB2, 0x62, 0xC5, 0x0A, 0xE6, 0xCD, 0x9B, 0xC7, 0x89, + 0x13, 0x27, 0x1C, 0x4F, 0xD5, 0x00, 0xCB, 0x80, 0x57, 0x81, 0xE2, 0xB6, 0x30, 0x24, 0x10, 0x29, + 0xA2, 0xFE, 0x0B, 0x9B, 0xF6, 0xA4, 0xD7, 0xEB, 0x79, 0xE0, 0x81, 0x07, 0xF8, 0xF3, 0x9F, 0xFF, + 0x4C, 0x7C, 0x7C, 0x7C, 0xB3, 0x1D, 0x2C, 0x59, 0xB2, 0x84, 0xE9, 0xD3, 0xA7, 0x33, 0x6C, 0x40, + 0x02, 0xDF, 0xBE, 0xF3, 0x02, 0x01, 0xBE, 0xDE, 0xD7, 0x26, 0xA4, 0x9D, 0x60, 0x80, 0xB0, 0x6A, + 0x68, 0x9A, 0x86, 0xAA, 0x28, 0x28, 0x3A, 0x1D, 0x28, 0x8A, 0x9C, 0x11, 0xAD, 0x61, 0x84, 0xB3, + 0x50, 0x14, 0x5E, 0x5A, 0xB4, 0x9C, 0xBF, 0x2D, 0x5B, 0x43, 0x72, 0x72, 0x32, 0x9F, 0x7E, 0xFA, + 0x29, 0x6E, 0x6E, 0x6E, 0x2D, 0xEA, 0x22, 0x33, 0x33, 0x93, 0xD7, 0x5F, 0x7F, 0x9D, 0x15, 0x2B, + 0x56, 0x38, 0xCE, 0x16, 0x0D, 0x19, 0x01, 0x39, 0x13, 0x99, 0xCB, 0x72, 0x05, 0x9A, 0x33, 0x2E, + 0x86, 0x00, 0xFF, 0x40, 0xAA, 0xB5, 0x7A, 0x90, 0x22, 0xEA, 0x85, 0x17, 0x5E, 0x60, 0xC9, 0x92, + 0x25, 0x4E, 0x31, 0xC3, 0x6C, 0x36, 0xB3, 0x6E, 0xDD, 0x3A, 0x00, 0x26, 0x0C, 0xBF, 0x99, 0x00, + 0x7F, 0x9F, 0xE6, 0x89, 0x29, 0x90, 0x33, 0xC8, 0xAA, 0xA1, 0x00, 0x3A, 0x55, 0x95, 0xAA, 0xB0, + 0xA6, 0x81, 0xD5, 0xEA, 0x5A, 0x66, 0x00, 0xA8, 0x0A, 0x13, 0x86, 0x0F, 0x22, 0xC0, 0xD7, 0x8B, + 0x94, 0x94, 0x14, 0x32, 0x33, 0x33, 0x5B, 0xDC, 0x45, 0xCF, 0x9E, 0x3D, 0x59, 0xBC, 0x78, 0x31, + 0x73, 0xE6, 0xCC, 0x71, 0xF4, 0x09, 0xA9, 0xC0, 0x43, 0xC8, 0x7D, 0x4B, 0xC4, 0x55, 0x6F, 0x7D, + 0x8D, 0x3E, 0x8D, 0x34, 0xEC, 0x2D, 0x54, 0x80, 0x90, 0x90, 0x10, 0xDE, 0x79, 0xE7, 0x1D, 0x5E, + 0x7D, 0xF5, 0x55, 0xFC, 0xFD, 0xFD, 0x9D, 0x1A, 0xD8, 0xF1, 0xE3, 0xC7, 0xF9, 0x69, 0xF7, 0x6E, + 0x42, 0x02, 0xFD, 0x18, 0x3B, 0xA4, 0x9F, 0x7D, 0xFD, 0xBC, 0xBE, 0xA1, 0x69, 0xF4, 0xED, 0x11, + 0xC3, 0x2D, 0x89, 0x3D, 0xB8, 0x70, 0xE1, 0x02, 0x5B, 0xB7, 0x6E, 0x6D, 0x55, 0x37, 0x5E, 0x5E, + 0x5E, 0x3C, 0xF5, 0xD4, 0x53, 0x7C, 0xF0, 0xC1, 0x07, 0xC4, 0xC6, 0xC6, 0x3A, 0x9E, 0xBA, 0x1F, + 0x98, 0x03, 0x5C, 0x41, 0xC4, 0xA6, 0x18, 0xE2, 0x89, 0x4C, 0x1D, 0x98, 0x8C, 0x4D, 0xAC, 0x45, + 0x45, 0x45, 0xB1, 0x68, 0xD1, 0x22, 0xA6, 0x4D, 0x9B, 0xD6, 0xA2, 0xE9, 0xBB, 0x7D, 0xFB, 0x76, + 0x8A, 0x8B, 0x8A, 0xB8, 0xB5, 0x4F, 0x4F, 0xE2, 0xBB, 0x46, 0xB6, 0x6C, 0x01, 0xEE, 0x2C, 0x08, + 0xF0, 0xF6, 0xF6, 0x64, 0xDC, 0xD0, 0x01, 0x00, 0x6C, 0xD8, 0xB0, 0x01, 0xB3, 0xD9, 0xDC, 0xAA, + 0xAE, 0x54, 0x55, 0x65, 0xE2, 0xC4, 0x89, 0x2C, 0x5D, 0xBA, 0xF4, 0x72, 0xA5, 0xE7, 0x11, 0xE4, + 0xBA, 0xDC, 0xC8, 0xC1, 0x75, 0x35, 0x86, 0x28, 0xC0, 0xFF, 0x20, 0xC5, 0x94, 0x0A, 0x92, 0x19, + 0xEF, 0xBE, 0xFB, 0x2E, 0x0F, 0x3D, 0xF4, 0x90, 0xD3, 0xBB, 0x68, 0x90, 0xE2, 0x6A, 0xD3, 0xA6, + 0x4D, 0x00, 0x8C, 0x19, 0x92, 0x84, 0x87, 0xE7, 0x8D, 0x11, 0x42, 0x0A, 0x80, 0x80, 0x11, 0x83, + 0x12, 0x09, 0xF2, 0xF7, 0x21, 0x2D, 0x2D, 0x9D, 0xAC, 0xAC, 0xAC, 0x36, 0x75, 0x37, 0x6A, 0xD4, + 0x28, 0x16, 0x2D, 0x5A, 0x44, 0x54, 0x54, 0x94, 0xBD, 0x49, 0x87, 0xCC, 0x0A, 0xB8, 0xDF, 0xF1, + 0xFF, 0xAE, 0xC6, 0x90, 0x31, 0xC8, 0x0D, 0x8D, 0x01, 0xC0, 0x68, 0x34, 0x32, 0x6F, 0xDE, 0xBC, + 0x46, 0xF9, 0x19, 0xCE, 0x22, 0x3B, 0x3B, 0x9B, 0xF4, 0xFD, 0xFB, 0x09, 0xF2, 0xF7, 0x61, 0xF8, + 0x80, 0x5E, 0x37, 0x86, 0xB8, 0xB2, 0x43, 0x68, 0xC4, 0xC5, 0x44, 0x30, 0x20, 0x3E, 0x96, 0xFC, + 0xFC, 0x3C, 0x76, 0xED, 0xDA, 0xD5, 0xE6, 0x2E, 0xC7, 0x8E, 0x1D, 0xCB, 0x1B, 0x6F, 0xBC, 0xE1, + 0xE8, 0x06, 0xF6, 0x43, 0x66, 0x89, 0xDD, 0x64, 0x6F, 0xB8, 0x9C, 0x21, 0x91, 0x48, 0x51, 0x15, + 0x0A, 0xD2, 0xA9, 0xFF, 0xF2, 0xCB, 0x2F, 0xF3, 0xD0, 0x43, 0x0F, 0xB5, 0x6A, 0x00, 0xE9, 0xE9, + 0xE9, 0xE4, 0x5D, 0xC8, 0xA3, 0x4F, 0xF7, 0x18, 0x7A, 0x44, 0x87, 0x3B, 0xBF, 0x18, 0xAB, 0x2A, + 0xA8, 0xAA, 0x4C, 0xCC, 0x01, 0xD0, 0xA9, 0x52, 0xBB, 0x6A, 0x2D, 0x54, 0x05, 0x54, 0x15, 0xAB, + 0xA6, 0x61, 0xD5, 0xB4, 0xFA, 0xFE, 0xAF, 0x09, 0x01, 0x5E, 0x5E, 0x9E, 0x0C, 0x1F, 0xD8, 0x1B, + 0x80, 0x9D, 0x3B, 0x77, 0xA2, 0xB5, 0x83, 0xB8, 0x4D, 0x4E, 0x4E, 0x66, 0xFA, 0xF4, 0xE9, 0x8E, + 0x4D, 0xFD, 0x90, 0x41, 0xDB, 0x2A, 0x34, 0x96, 0x5F, 0x0A, 0x32, 0x21, 0xE5, 0x36, 0x7B, 0xC3, + 0x23, 0x8F, 0x3C, 0xC2, 0xF4, 0xE9, 0xD3, 0x5B, 0x1D, 0xA8, 0x20, 0xDD, 0xB7, 0x82, 0xDB, 0xFA, + 0xC5, 0xE3, 0xE3, 0xED, 0x09, 0x5A, 0x33, 0x0C, 0x51, 0x14, 0xAC, 0x9A, 0x46, 0xFA, 0xA1, 0x93, + 0xFC, 0x7B, 0xE7, 0x7E, 0x4E, 0x9D, 0xCB, 0xC7, 0xDB, 0xD3, 0xC0, 0x6D, 0x49, 0xF1, 0x8C, 0xBB, + 0x7D, 0x20, 0xA1, 0x41, 0x01, 0x2D, 0x5F, 0x83, 0x14, 0x85, 0x53, 0x39, 0x79, 0xFC, 0x6B, 0xCB, + 0x5E, 0xD2, 0x8F, 0x67, 0x61, 0xB5, 0x6A, 0x24, 0x76, 0x8F, 0xE6, 0xFE, 0x11, 0xB7, 0xD0, 0x37, + 0xEE, 0xA6, 0x6B, 0xAB, 0x99, 0x8A, 0xC2, 0xAD, 0x7D, 0x7B, 0xE2, 0xAE, 0xD7, 0x91, 0x91, 0x91, + 0x41, 0x49, 0x49, 0x49, 0x9B, 0xA3, 0x68, 0xDC, 0xDC, 0xDC, 0x78, 0xE6, 0x99, 0x67, 0x48, 0x49, + 0x49, 0x61, 0xEF, 0xDE, 0xBD, 0xF6, 0xE6, 0xC9, 0xC0, 0x72, 0x60, 0xBF, 0x23, 0x43, 0xFA, 0x03, + 0x53, 0xED, 0x07, 0x7D, 0xFA, 0xF4, 0xE1, 0xC5, 0x17, 0x5F, 0xC4, 0xCB, 0xCB, 0xCB, 0xE9, 0x9B, + 0x39, 0xA2, 0xAC, 0xAC, 0x8C, 0xFD, 0xFB, 0xF7, 0xA3, 0x53, 0x15, 0x86, 0xF4, 0xED, 0x09, 0x8A, + 0x4A, 0x73, 0x91, 0xFB, 0xB5, 0x96, 0x3A, 0x16, 0xAE, 0xFC, 0x9E, 0x39, 0x1F, 0x7F, 0x43, 0xE1, + 0xC5, 0xF2, 0xFA, 0xF6, 0x65, 0xDF, 0x6C, 0x65, 0xD8, 0x80, 0x04, 0xDE, 0x79, 0x76, 0x2A, 0xFD, + 0x13, 0x62, 0x9D, 0x67, 0x8A, 0xA2, 0xF0, 0x7D, 0xEA, 0x7E, 0x9E, 0x7D, 0xE7, 0x53, 0x8E, 0x9D, + 0x69, 0x50, 0xFB, 0xBF, 0xDE, 0xBC, 0x9B, 0x0F, 0xD7, 0x6E, 0xE6, 0x2F, 0xFF, 0x93, 0xCC, 0xA3, + 0xE3, 0xFF, 0x03, 0xB5, 0xA9, 0xD9, 0xA7, 0x69, 0xF4, 0x8E, 0xED, 0x42, 0xD7, 0x88, 0x10, 0x4E, + 0x67, 0x65, 0x71, 0xF2, 0xE4, 0xC9, 0x76, 0x09, 0x6B, 0x8A, 0x8A, 0x8A, 0x62, 0xE6, 0xCC, 0x99, + 0x4C, 0x9D, 0x3A, 0xD5, 0xAE, 0x2C, 0x44, 0x00, 0x8F, 0x02, 0x19, 0xF6, 0x17, 0x44, 0x05, 0xA6, + 0x61, 0x33, 0x89, 0xB8, 0xBB, 0xBB, 0x33, 0x6B, 0xD6, 0xAC, 0x26, 0x4D, 0x21, 0xCE, 0xE0, 0xEC, + 0xD9, 0xB3, 0x64, 0x9E, 0x3A, 0x45, 0x44, 0x70, 0x20, 0xBD, 0xBB, 0x75, 0x01, 0xD1, 0x0C, 0x11, + 0x55, 0x95, 0x55, 0x9B, 0x7E, 0xE2, 0xD5, 0xF7, 0xBE, 0x6A, 0xC4, 0x0C, 0x2F, 0x2F, 0x2F, 0xFC, + 0xFC, 0xFD, 0xD9, 0x96, 0x7E, 0x94, 0x67, 0xDE, 0xFE, 0x88, 0x82, 0xE2, 0x8B, 0xCE, 0x89, 0x2F, + 0x55, 0xE5, 0xF0, 0xE9, 0x1C, 0x66, 0xBC, 0xFD, 0x71, 0x23, 0x66, 0x18, 0x0C, 0x06, 0xC2, 0xC2, + 0xC2, 0x28, 0x2C, 0xAB, 0xE2, 0xF9, 0x7F, 0x7C, 0xCE, 0x96, 0x7D, 0x87, 0x9B, 0x16, 0x5F, 0x42, + 0x10, 0x1E, 0xE4, 0x4F, 0xBF, 0xB8, 0x9B, 0xA8, 0xBA, 0x74, 0x89, 0xB4, 0xB4, 0xB4, 0x36, 0x33, + 0xC3, 0x8E, 0x71, 0xE3, 0xC6, 0x31, 0x7C, 0xF8, 0x70, 0xC7, 0xA6, 0x7B, 0x80, 0x04, 0xFB, 0x48, + 0x12, 0x80, 0xFB, 0xEC, 0x67, 0x46, 0x8C, 0x18, 0xC1, 0x03, 0x0F, 0x3C, 0xD0, 0xA6, 0x1B, 0x1E, + 0x3D, 0x7A, 0x94, 0xD2, 0x92, 0x12, 0xE2, 0xBB, 0x46, 0x12, 0x19, 0x62, 0xBC, 0xB6, 0xB8, 0x52, + 0x14, 0xCA, 0x2B, 0x2E, 0xF1, 0xFE, 0xEA, 0x8D, 0x54, 0x99, 0x1A, 0xD4, 0xCB, 0xBE, 0x7D, 0xFB, + 0xB2, 0x72, 0xE5, 0x4A, 0x36, 0x6F, 0xDE, 0xCC, 0x13, 0x4F, 0x3C, 0xC1, 0x4F, 0x87, 0x32, 0xF9, + 0x2E, 0x25, 0x4D, 0xAE, 0x09, 0xCD, 0x41, 0x08, 0xBE, 0x58, 0xBF, 0x83, 0xCC, 0x9C, 0x86, 0x78, + 0x5C, 0xA3, 0xD1, 0xC8, 0xFC, 0xF9, 0xF3, 0x49, 0x49, 0x49, 0xE1, 0xBD, 0xF7, 0xDE, 0x45, 0xE8, + 0xDD, 0xF9, 0x60, 0xCD, 0x26, 0x2C, 0xB5, 0x4D, 0x47, 0xF2, 0xE8, 0xDD, 0xDD, 0xE4, 0x0C, 0x07, + 0xF6, 0xEC, 0xD9, 0xE3, 0x98, 0xB0, 0xD3, 0x26, 0xF8, 0xF9, 0xF9, 0x31, 0x79, 0xF2, 0x64, 0xC7, + 0x68, 0xFA, 0x58, 0x60, 0xBC, 0x9D, 0x21, 0xE3, 0xB1, 0xCD, 0x0E, 0x83, 0xC1, 0xC0, 0xB4, 0x69, + 0xD3, 0x9C, 0xDE, 0xF8, 0x35, 0x85, 0x43, 0x87, 0x0E, 0x01, 0x90, 0xD8, 0x3D, 0x06, 0x2F, 0x8F, + 0x66, 0xD4, 0x5D, 0x45, 0xE1, 0x6C, 0x5E, 0x11, 0xC7, 0xCE, 0x9C, 0x6F, 0xD4, 0xFC, 0xE4, 0x93, + 0x4F, 0x32, 0x7E, 0xFC, 0x78, 0x06, 0x0C, 0x18, 0xC0, 0x8B, 0x2F, 0xBE, 0x48, 0x74, 0xD7, 0x9B, + 0x48, 0xFD, 0xF9, 0x78, 0xD3, 0x76, 0x30, 0x07, 0x98, 0xCC, 0x35, 0xEC, 0x3D, 0x72, 0xAA, 0x51, + 0xDB, 0x98, 0x31, 0x63, 0x78, 0xFC, 0xF1, 0xC7, 0x89, 0x8B, 0x8B, 0xE3, 0xB1, 0xC7, 0x1E, 0x63, + 0xC2, 0x84, 0x09, 0xA4, 0x1F, 0x3B, 0x4D, 0x71, 0x59, 0xE5, 0x35, 0x66, 0x9D, 0x42, 0xBF, 0xB8, + 0x9B, 0x70, 0xD7, 0xEB, 0x38, 0x7E, 0xFC, 0x38, 0xE5, 0xE5, 0xE5, 0xCD, 0xDE, 0xDB, 0x59, 0x8C, + 0x1A, 0x35, 0x8A, 0xEE, 0xDD, 0xEB, 0x73, 0x7E, 0x74, 0xC0, 0x44, 0x15, 0xB9, 0x5B, 0xBC, 0xDB, + 0xDE, 0xDA, 0xB7, 0x6F, 0x5F, 0x46, 0x8F, 0x1E, 0xDD, 0xA6, 0x1B, 0x59, 0x2C, 0x16, 0x8E, 0x1E, + 0x3D, 0x2A, 0xFB, 0xEB, 0x11, 0xD3, 0xBC, 0x46, 0xA3, 0x80, 0xB9, 0xD6, 0x42, 0xAD, 0xA5, 0xF1, + 0xDB, 0x27, 0x1C, 0xB4, 0x32, 0x21, 0x04, 0x08, 0x41, 0xB5, 0xB9, 0x06, 0xAD, 0x39, 0x6D, 0x4D, + 0x01, 0xAB, 0x55, 0xC3, 0x5C, 0xD3, 0xF8, 0xCD, 0xB7, 0x58, 0x2C, 0xF5, 0x9A, 0x92, 0xA6, 0x69, + 0xD4, 0xD6, 0xD6, 0x52, 0x6B, 0xA9, 0xA3, 0xCE, 0x6A, 0x6D, 0xDA, 0xAA, 0x27, 0x34, 0x62, 0x23, + 0x43, 0x09, 0x35, 0xFA, 0x93, 0x9D, 0x9D, 0x4D, 0x4A, 0x4A, 0x0A, 0x15, 0x15, 0x15, 0x8D, 0xC6, + 0xD6, 0x52, 0x54, 0x57, 0x57, 0x93, 0x9E, 0x9E, 0xCE, 0xFB, 0xEF, 0xBF, 0x7F, 0x79, 0xE0, 0x5D, + 0x92, 0x1E, 0x48, 0x44, 0x66, 0xCA, 0x02, 0x70, 0xF7, 0xDD, 0x77, 0x13, 0x1C, 0xDC, 0x22, 0x9F, + 0xCA, 0x15, 0xA8, 0xA8, 0xA8, 0xE0, 0xCC, 0x99, 0x33, 0x78, 0xB8, 0xEB, 0xE9, 0x19, 0x13, 0x4E, + 0xB3, 0x1B, 0x10, 0x21, 0x08, 0x0F, 0x0A, 0x20, 0xD4, 0xE8, 0x47, 0x45, 0x55, 0x83, 0x75, 0x7A, + 0xD1, 0xA2, 0x45, 0x44, 0x44, 0x44, 0x10, 0x15, 0x15, 0xC5, 0x87, 0x1F, 0x7E, 0xC8, 0xE9, 0xAC, + 0xD3, 0x3C, 0x3C, 0xA2, 0x9F, 0xB4, 0xCC, 0x5E, 0x6B, 0x61, 0x17, 0xE0, 0xE5, 0x61, 0xA0, 0x47, + 0x74, 0x38, 0xBB, 0x0E, 0x36, 0x58, 0x5C, 0x37, 0x6E, 0xDC, 0xC8, 0xDC, 0xB9, 0x73, 0x19, 0x33, + 0x66, 0x0C, 0x3B, 0x76, 0xEC, 0xE0, 0xBB, 0xEF, 0xD6, 0x31, 0xA0, 0x47, 0x24, 0x81, 0xBE, 0xDE, + 0x4D, 0x0F, 0x51, 0x40, 0xA8, 0xD1, 0x9F, 0xAE, 0x11, 0x21, 0xA4, 0x1E, 0x38, 0xC1, 0x6F, 0x7F, + 0xFB, 0x5B, 0x12, 0x12, 0x12, 0x18, 0x32, 0x64, 0x08, 0x83, 0x07, 0x0F, 0xA6, 0x7B, 0xF7, 0xEE, + 0x44, 0x45, 0x45, 0x11, 0x18, 0x18, 0x88, 0xC1, 0x60, 0x40, 0xA7, 0xD3, 0xD5, 0x6F, 0x9E, 0x35, + 0x4D, 0xC3, 0x62, 0xB1, 0x50, 0x5D, 0x5D, 0x4D, 0x71, 0x71, 0x31, 0xA7, 0x4F, 0x9F, 0x26, 0x2D, + 0x2D, 0x8D, 0x94, 0x94, 0x14, 0x32, 0x32, 0x32, 0x28, 0x2E, 0xBE, 0xC2, 0xE8, 0x9B, 0xAB, 0x47, + 0xAA, 0xB9, 0x46, 0x00, 0x6F, 0x6F, 0x6F, 0x46, 0x8C, 0x18, 0xD1, 0x26, 0x66, 0x00, 0x14, 0x14, + 0x14, 0x90, 0x97, 0x9F, 0x4F, 0x90, 0xBF, 0x2F, 0x51, 0xA1, 0xC6, 0xE6, 0x37, 0x84, 0x9A, 0xA0, + 0x4B, 0x58, 0x10, 0xE3, 0x6E, 0x1F, 0xC8, 0x3F, 0x97, 0xAF, 0xAF, 0x6F, 0x3E, 0x7C, 0xF8, 0x30, + 0x0F, 0x3F, 0xFC, 0x30, 0xEE, 0xEE, 0xEE, 0x94, 0x57, 0x54, 0x10, 0x66, 0xF4, 0x63, 0xC2, 0xF0, + 0x41, 0x4E, 0x8D, 0x41, 0x75, 0xD3, 0x31, 0x69, 0xF4, 0xAD, 0xAC, 0xD9, 0xBA, 0x87, 0x4B, 0x36, + 0xBF, 0x4A, 0x65, 0x65, 0x25, 0xAF, 0xBD, 0xF6, 0x1A, 0x73, 0xE6, 0xCC, 0xA1, 0xBA, 0xBA, 0x1A, + 0x10, 0x3C, 0x74, 0xC7, 0x6D, 0xF8, 0xF8, 0x78, 0x35, 0xCD, 0x60, 0x21, 0xF0, 0xF1, 0xF2, 0x20, + 0xBE, 0x6B, 0x24, 0xA9, 0x07, 0x4E, 0x50, 0x5E, 0x5E, 0xCE, 0x9E, 0x3D, 0x7B, 0xD8, 0xB3, 0x67, + 0x0F, 0x3A, 0x9D, 0x0E, 0x1F, 0x1F, 0x1F, 0x42, 0x42, 0x42, 0x88, 0x88, 0x88, 0x20, 0x28, 0x28, + 0x08, 0x5F, 0x5F, 0x5F, 0x0C, 0x06, 0x03, 0x9A, 0xA6, 0x51, 0x5D, 0x5D, 0x4D, 0x59, 0x59, 0x19, + 0x85, 0x85, 0x85, 0x14, 0x14, 0x14, 0x50, 0x52, 0x52, 0xD2, 0x94, 0x09, 0x46, 0x43, 0x66, 0xF6, + 0xBE, 0xAA, 0x47, 0x3A, 0xE8, 0x55, 0x80, 0x2E, 0x5D, 0xBA, 0x90, 0x90, 0x90, 0xD0, 0x6A, 0x46, + 0x98, 0xCD, 0x66, 0x32, 0x32, 0x32, 0x58, 0xB8, 0x70, 0x21, 0x45, 0x85, 0x45, 0x0C, 0x4C, 0x88, + 0x25, 0xC8, 0xDF, 0xD7, 0xA9, 0x0D, 0xA1, 0xAA, 0x53, 0x99, 0xF9, 0xC8, 0x3D, 0x1C, 0x3C, 0x79, + 0x96, 0x6D, 0xE9, 0x47, 0xEA, 0xDB, 0x4D, 0x26, 0x13, 0x26, 0x93, 0x09, 0x1F, 0x4F, 0x0F, 0x66, + 0x3F, 0x76, 0x1F, 0x37, 0xF7, 0xEE, 0xEE, 0x9C, 0xDA, 0x6B, 0xD5, 0xB8, 0xEB, 0xB6, 0xFE, 0x3C, + 0x9D, 0x7C, 0x37, 0xF3, 0x3E, 0x5F, 0x47, 0x8D, 0x6D, 0xE1, 0xB6, 0x5A, 0xAD, 0x54, 0x56, 0x56, + 0xA2, 0x53, 0x55, 0x7E, 0x33, 0x6E, 0x38, 0x53, 0xC6, 0x0D, 0x6B, 0x76, 0x7C, 0x8A, 0x4E, 0x27, + 0x35, 0xC5, 0xCB, 0x6F, 0x61, 0xB5, 0x52, 0x5E, 0x5E, 0x4E, 0x79, 0x79, 0x39, 0xA7, 0x4E, 0x9D, + 0xA2, 0x15, 0xA8, 0x46, 0x46, 0x79, 0xAE, 0x04, 0xD6, 0x00, 0xE7, 0xF5, 0xC8, 0x48, 0x09, 0x40, + 0x9A, 0x8C, 0x5B, 0x2A, 0xAE, 0xEA, 0xEA, 0xEA, 0x38, 0x7F, 0xFE, 0x3C, 0x5B, 0xB7, 0x6E, 0x65, + 0xCD, 0x9A, 0x35, 0xEC, 0xDA, 0xB5, 0xAB, 0x5E, 0x2E, 0x46, 0x04, 0x07, 0xE0, 0xED, 0x61, 0x70, + 0x6E, 0x87, 0xAE, 0x09, 0x6E, 0x8A, 0x0A, 0xE5, 0xE3, 0x3F, 0x3D, 0xC9, 0x3F, 0x97, 0xAF, 0x67, + 0xDD, 0xCE, 0x74, 0x8A, 0x4A, 0x2B, 0x70, 0x73, 0xD3, 0xD3, 0x3B, 0xB6, 0x0B, 0xBF, 0x7B, 0x70, + 0x0C, 0xF7, 0x8F, 0xBA, 0x15, 0x55, 0x55, 0x9C, 0x36, 0xC1, 0xB8, 0xBB, 0xB9, 0xF1, 0xF2, 0xE3, + 0x0F, 0xD2, 0xA3, 0x4B, 0x38, 0x1F, 0x7D, 0xB7, 0x8D, 0x53, 0xE7, 0xF2, 0xD1, 0x34, 0x8D, 0xE8, + 0xB0, 0x20, 0x92, 0xEF, 0x1C, 0xCA, 0xB4, 0x89, 0xA3, 0xF1, 0x6F, 0xCE, 0x37, 0x63, 0x43, 0xD7, + 0x88, 0x10, 0x74, 0x3A, 0x15, 0xAB, 0x54, 0x28, 0x34, 0xA4, 0x93, 0xC9, 0x1B, 0x59, 0xC0, 0xC6, + 0x59, 0x33, 0x42, 0x1D, 0x32, 0x0E, 0xE1, 0x14, 0x32, 0x38, 0xE4, 0x47, 0x64, 0xD8, 0x54, 0x99, + 0xFD, 0x1F, 0xF4, 0x38, 0xD8, 0xE5, 0x2D, 0x16, 0x0B, 0x07, 0x0F, 0x1E, 0x24, 0x2A, 0x2A, 0x0A, + 0x3F, 0x3F, 0x3F, 0x0C, 0x06, 0x03, 0xAA, 0xCD, 0x17, 0xA1, 0x69, 0x1A, 0x75, 0x75, 0x75, 0x98, + 0x4C, 0x26, 0x4A, 0x4B, 0x4B, 0xC9, 0xCE, 0xCE, 0xE6, 0xC0, 0x81, 0x03, 0xEC, 0xDE, 0xBD, 0x9B, + 0x7D, 0xFB, 0xF6, 0x71, 0xEE, 0xDC, 0xB9, 0x2B, 0xEA, 0x94, 0x44, 0x86, 0x04, 0xE2, 0xEE, 0xD6, + 0x82, 0x68, 0x7D, 0x4D, 0xD0, 0x35, 0x32, 0x84, 0xB7, 0x9E, 0x99, 0xC2, 0xAC, 0xC9, 0xE3, 0x29, + 0xBC, 0x58, 0x8E, 0xC1, 0xDD, 0x8D, 0xE8, 0xB0, 0x20, 0x7C, 0x7D, 0xBC, 0x6D, 0x8E, 0x29, 0xE7, + 0xBB, 0x43, 0x08, 0x3C, 0x0D, 0x6E, 0x4C, 0xBD, 0x6F, 0x34, 0x93, 0x46, 0x0F, 0x21, 0xBF, 0xF8, + 0x22, 0x56, 0x4D, 0x10, 0x16, 0xE4, 0x4F, 0xA0, 0xBF, 0xAF, 0xA4, 0xA2, 0x53, 0x8B, 0xB3, 0x5C, + 0xE3, 0x3C, 0xDD, 0xDD, 0xB9, 0x24, 0xD5, 0xF2, 0x3A, 0xE0, 0x0F, 0xC8, 0x52, 0x4E, 0x7D, 0x80, + 0x18, 0x64, 0x5C, 0x5A, 0x28, 0x32, 0x11, 0x54, 0x8F, 0xD4, 0x9A, 0xAA, 0x6D, 0xC4, 0x2E, 0x45, + 0xC6, 0x6F, 0x1D, 0x47, 0x56, 0x27, 0x3A, 0x8B, 0x2C, 0x6C, 0x73, 0xC5, 0xCD, 0xF5, 0x38, 0x64, + 0x92, 0x6E, 0xDC, 0xB8, 0x91, 0x3D, 0x7B, 0xF6, 0x10, 0x1A, 0x1A, 0x4A, 0x68, 0x68, 0x28, 0x46, + 0xA3, 0x11, 0x2F, 0x2F, 0x2F, 0x54, 0x55, 0xA5, 0xB6, 0xB6, 0x96, 0xCA, 0xCA, 0x4A, 0x8A, 0x8B, + 0x8B, 0x29, 0x2C, 0x2C, 0xA4, 0xA4, 0xA4, 0x04, 0x93, 0xC9, 0x74, 0x4D, 0x6D, 0x23, 0x24, 0xD0, + 0x5F, 0x6A, 0x58, 0x2D, 0x31, 0x77, 0x68, 0x02, 0x9D, 0xA2, 0x10, 0x15, 0x16, 0x44, 0x54, 0x78, + 0x70, 0x3D, 0x61, 0x5B, 0x6D, 0xB6, 0x17, 0x80, 0xD0, 0xF0, 0xF3, 0xF1, 0xC2, 0xCF, 0xD7, 0xBB, + 0xA1, 0xBF, 0x96, 0x68, 0x49, 0x02, 0x42, 0x02, 0xFD, 0xF0, 0xF5, 0xF6, 0xB0, 0x33, 0xC4, 0x1D, + 0x39, 0x4B, 0x76, 0xD8, 0x7E, 0x20, 0x67, 0x89, 0x3B, 0x52, 0xFC, 0xEB, 0x6C, 0xC7, 0x16, 0x1B, + 0xF3, 0x9C, 0xAE, 0x6C, 0xA7, 0x07, 0x7E, 0x02, 0x46, 0x83, 0xD4, 0x0A, 0xCA, 0xCA, 0xCA, 0x28, + 0x2B, 0x2B, 0xE3, 0xE4, 0xC9, 0x93, 0x2D, 0x7D, 0xF4, 0x3A, 0xE0, 0x02, 0xD2, 0xCB, 0xE8, 0x09, + 0x10, 0xE4, 0xEF, 0xD3, 0x7A, 0xA3, 0x60, 0x4B, 0x89, 0xE6, 0xCA, 0xFE, 0x84, 0x20, 0xD0, 0xD7, + 0x9B, 0x40, 0x3F, 0x1F, 0xF2, 0x8A, 0xCB, 0xEC, 0xAD, 0x51, 0x97, 0xFF, 0x17, 0xD2, 0x6F, 0xDE, + 0x26, 0xE8, 0x91, 0x36, 0xF9, 0x67, 0x80, 0xE1, 0xC8, 0xCD, 0xA1, 0x0F, 0xCE, 0xC9, 0xC4, 0x3A, + 0xE4, 0xB4, 0xCB, 0x46, 0xCA, 0xC1, 0x6D, 0xC0, 0x79, 0x64, 0x15, 0xB7, 0xAE, 0x00, 0x7E, 0xDE, + 0xED, 0x9A, 0xED, 0xD5, 0x89, 0x10, 0x78, 0x7B, 0x7A, 0x60, 0xF4, 0xF3, 0x71, 0x6C, 0x0C, 0x72, + 0xC5, 0x9D, 0xF4, 0x48, 0xB9, 0xF6, 0x7B, 0x64, 0x40, 0x57, 0x37, 0xE4, 0xBE, 0xA4, 0x2B, 0xD2, + 0x14, 0x1F, 0x8C, 0x9C, 0x86, 0x3A, 0xA4, 0x65, 0xB0, 0x0C, 0x29, 0x0F, 0xCF, 0x03, 0x27, 0x69, + 0x90, 0x87, 0x17, 0x91, 0x6F, 0x48, 0x4F, 0x1C, 0xEA, 0x61, 0x79, 0x7A, 0x18, 0x3A, 0xAF, 0x88, + 0x60, 0x7B, 0x42, 0x80, 0x9B, 0x5E, 0x87, 0xBF, 0x4F, 0x23, 0x43, 0x6B, 0xDB, 0x4C, 0x19, 0x4D, + 0xC0, 0xBE, 0xE2, 0xD6, 0x21, 0x6B, 0x1E, 0xE6, 0x03, 0x76, 0x4F, 0x8C, 0x62, 0x3B, 0xEF, 0xB8, + 0xCD, 0xAE, 0xE3, 0xDA, 0x26, 0x5B, 0x9D, 0xFD, 0xFF, 0x55, 0x45, 0xC1, 0x5D, 0xAF, 0xBB, 0xB1, + 0x9C, 0x52, 0xD7, 0x22, 0x94, 0x5E, 0x87, 0x6F, 0xE3, 0x19, 0xEF, 0x47, 0xC3, 0x8B, 0xDA, 0x7E, + 0xF7, 0xB9, 0xC6, 0x39, 0xC1, 0x2F, 0xB4, 0xD6, 0x60, 0x6B, 0xA0, 0x2A, 0x0A, 0x9E, 0x86, 0x46, + 0xB1, 0x67, 0x9E, 0xB8, 0x80, 0x21, 0xED, 0x9D, 0x63, 0x58, 0x3F, 0x1F, 0x84, 0x10, 0xD2, 0x3B, + 0xF7, 0x4B, 0x10, 0x59, 0x00, 0x8A, 0x82, 0xA1, 0xB1, 0x0A, 0x6F, 0xD7, 0xA4, 0xDA, 0x15, 0xED, + 0xCD, 0x90, 0x1A, 0xA0, 0x16, 0x40, 0x13, 0x82, 0x6A, 0x73, 0xED, 0x2F, 0x46, 0x64, 0x01, 0x97, + 0x07, 0x78, 0xDC, 0x30, 0x0C, 0x31, 0xD9, 0x0F, 0x2A, 0xAA, 0x4C, 0x6D, 0xE8, 0xEA, 0x3A, 0x83, + 0x10, 0xD2, 0x2A, 0xDC, 0x00, 0x0D, 0x17, 0xBC, 0x6E, 0xED, 0xCD, 0x10, 0x13, 0xB2, 0xCE, 0x09, + 0x00, 0x45, 0xA5, 0xE5, 0xAE, 0x8F, 0x32, 0xEC, 0x20, 0x08, 0x21, 0x30, 0x35, 0x36, 0xE7, 0x9B, + 0x70, 0x41, 0x35, 0x39, 0x57, 0x30, 0xA4, 0xBE, 0x70, 0xC0, 0x85, 0xE2, 0x8B, 0x88, 0x1B, 0x21, + 0x30, 0xCE, 0x09, 0x58, 0x35, 0x8D, 0x4B, 0xD5, 0x8D, 0x66, 0xFC, 0x0D, 0xC1, 0x90, 0x1A, 0x1C, + 0x82, 0x88, 0xCF, 0xE6, 0x15, 0x61, 0x32, 0xB7, 0x79, 0xF3, 0xDA, 0xF9, 0x50, 0xA0, 0xA6, 0xB6, + 0x8E, 0x92, 0xF2, 0x4B, 0x8E, 0xAD, 0xC5, 0xDC, 0x00, 0x22, 0x0B, 0xE4, 0x66, 0x11, 0x80, 0x9C, + 0xFC, 0x62, 0xF9, 0x10, 0x6D, 0x89, 0xA9, 0xBA, 0x2E, 0xA0, 0x70, 0xC9, 0x64, 0x96, 0xAE, 0xDE, + 0x06, 0xB4, 0x7B, 0xF1, 0x32, 0x70, 0x5D, 0x69, 0x8D, 0x5A, 0x80, 0xFC, 0x92, 0x32, 0x4E, 0x9D, + 0xCB, 0x77, 0x2E, 0x28, 0xE1, 0x7A, 0x86, 0xA2, 0x50, 0x50, 0x5A, 0x4E, 0x51, 0x43, 0x34, 0x8C, + 0x40, 0x5A, 0x2B, 0xDA, 0x1D, 0xAE, 0x60, 0xC8, 0x49, 0x64, 0x26, 0x2A, 0x55, 0xA6, 0x2B, 0x03, + 0x0D, 0x6E, 0x48, 0x28, 0x0A, 0xA7, 0xCE, 0xE5, 0x53, 0x7E, 0xA9, 0xDE, 0xBD, 0x5C, 0x89, 0xF4, + 0x69, 0xB4, 0x3B, 0x5C, 0xC1, 0x90, 0x73, 0xC8, 0xCF, 0x4E, 0x00, 0xB0, 0x65, 0xDF, 0x61, 0xAA, + 0xAB, 0xCD, 0x9D, 0xBF, 0x41, 0x54, 0x14, 0x19, 0x92, 0xDA, 0x9A, 0x28, 0x4C, 0x21, 0xD8, 0x77, + 0xE4, 0x14, 0x96, 0xBA, 0xFA, 0x35, 0x3C, 0x1F, 0x69, 0xC3, 0x6B, 0x77, 0xB8, 0x82, 0x21, 0x26, + 0xE4, 0x57, 0x6E, 0x00, 0x48, 0x3F, 0x96, 0xC5, 0xE1, 0x53, 0x39, 0xAD, 0x23, 0x84, 0x1D, 0x8A, + 0xD2, 0x40, 0xD0, 0x96, 0xAE, 0x47, 0x0A, 0xA0, 0xD3, 0x51, 0x71, 0xA9, 0x9A, 0x9D, 0xE9, 0x47, + 0x39, 0x9E, 0xD5, 0xC2, 0x4F, 0x82, 0x28, 0x0A, 0xE5, 0x95, 0x97, 0xD8, 0x91, 0x71, 0xDC, 0xB1, + 0xF5, 0x20, 0x36, 0x29, 0xD0, 0xDE, 0x70, 0xD5, 0x67, 0x18, 0x36, 0xDB, 0x06, 0x1C, 0x52, 0x52, + 0x5E, 0xC9, 0xAA, 0x4D, 0xBB, 0x19, 0xDC, 0xA7, 0xF5, 0x51, 0x90, 0xCB, 0x37, 0xEC, 0x64, 0xF7, + 0xA1, 0x4C, 0x46, 0xDF, 0xD2, 0x87, 0x61, 0x03, 0x7B, 0x39, 0x9F, 0xE8, 0xA9, 0x28, 0x54, 0x9B, + 0x6B, 0xF8, 0xF1, 0xA7, 0x03, 0x2C, 0x59, 0xB3, 0x91, 0xED, 0xFB, 0x8F, 0xD2, 0x3B, 0x36, 0x9A, + 0xB5, 0xF3, 0x9E, 0x27, 0x2A, 0x2C, 0xC8, 0xB9, 0x3D, 0x92, 0xAA, 0xB0, 0xF7, 0xC8, 0x69, 0x0E, + 0x64, 0x66, 0xDB, 0x5B, 0x84, 0xED, 0xF9, 0x5C, 0x62, 0xE7, 0x73, 0x55, 0x8D, 0x8E, 0x12, 0x64, + 0xAC, 0x70, 0x1F, 0x80, 0x0B, 0xC5, 0xA5, 0x8C, 0x1D, 0xD2, 0x8F, 0x90, 0xA0, 0x80, 0x96, 0x6D, + 0x14, 0x6D, 0x8B, 0xE9, 0xF4, 0xD7, 0xDF, 0xE7, 0xDB, 0x94, 0x34, 0xD6, 0x6E, 0xDF, 0xC7, 0xB6, + 0xB4, 0x23, 0x18, 0xFD, 0x7C, 0xE8, 0xD9, 0x35, 0xB2, 0xE9, 0x98, 0x5C, 0xDB, 0xB5, 0x85, 0x17, + 0xCB, 0xF9, 0xDF, 0x7F, 0x7C, 0xC6, 0x6B, 0xEF, 0x7F, 0xC5, 0xB1, 0xEC, 0x5C, 0x2C, 0x75, 0x56, + 0xAA, 0xCC, 0x35, 0xDC, 0x3F, 0x62, 0x30, 0x51, 0xE1, 0xCE, 0x31, 0xA4, 0xCE, 0x6A, 0xE5, 0x8D, + 0x65, 0xFF, 0x62, 0xEF, 0xE1, 0xFA, 0x25, 0xE3, 0x3C, 0x32, 0x13, 0xB9, 0xB8, 0xD9, 0x8B, 0x5B, + 0x01, 0x57, 0xD5, 0xDF, 0xAB, 0x45, 0x3A, 0xAA, 0xAA, 0x01, 0xB2, 0x2F, 0x14, 0xB1, 0x74, 0xED, + 0x16, 0xB4, 0x56, 0x84, 0x61, 0x6A, 0x9A, 0x46, 0x8D, 0x45, 0x7A, 0x40, 0xCD, 0x35, 0xB5, 0xA4, + 0x1E, 0x38, 0xC1, 0xB4, 0x3F, 0x2D, 0xE6, 0x93, 0xEF, 0xB6, 0x35, 0xBD, 0x2E, 0xD9, 0x66, 0xC6, + 0x4B, 0x0B, 0x97, 0xB3, 0x74, 0xED, 0x16, 0xCC, 0x0E, 0xA1, 0xA2, 0x31, 0xE1, 0xC1, 0x44, 0x85, + 0x19, 0x9B, 0x8F, 0xC4, 0x07, 0xD0, 0xA9, 0xEC, 0x3B, 0x72, 0x8A, 0x75, 0x3B, 0xD2, 0x1D, 0x5B, + 0xBF, 0x47, 0xFA, 0xD2, 0x5D, 0x02, 0x57, 0x16, 0x44, 0xDC, 0x82, 0x8C, 0xAA, 0x00, 0xE0, 0xCB, + 0xEF, 0x77, 0xB0, 0xEB, 0xE0, 0x49, 0xB9, 0x0E, 0x38, 0x0B, 0x21, 0x08, 0x33, 0x06, 0x70, 0xD7, + 0x6D, 0xFD, 0xEB, 0x9B, 0xBC, 0x3D, 0x0D, 0x18, 0xDC, 0xDD, 0x78, 0xE5, 0xDD, 0x15, 0xEC, 0xFC, + 0xF9, 0xC4, 0xD5, 0xD7, 0x26, 0x45, 0x61, 0xD5, 0xC6, 0x9F, 0xF8, 0xF4, 0xDF, 0xDB, 0x19, 0x79, + 0x73, 0x62, 0xFD, 0xF5, 0xC1, 0x01, 0xBE, 0x3C, 0x3F, 0xE5, 0x5E, 0x19, 0x6B, 0xEC, 0x44, 0xF4, + 0xA3, 0xD9, 0x5C, 0xC3, 0xA2, 0xAF, 0x7E, 0xA0, 0xE8, 0x62, 0x85, 0xBD, 0xB5, 0x14, 0x17, 0x7F, + 0xD8, 0xC5, 0x95, 0x65, 0x85, 0x2C, 0x48, 0xD1, 0x75, 0x2F, 0xE0, 0x51, 0x65, 0xAA, 0xA1, 0xE8, + 0x62, 0x05, 0xF7, 0x0C, 0x1D, 0x88, 0x87, 0xC1, 0xF9, 0xA2, 0x39, 0x8A, 0xAA, 0x92, 0xD4, 0xB3, + 0x2B, 0x25, 0x65, 0x95, 0x14, 0x94, 0x94, 0x73, 0xEF, 0xF0, 0x9B, 0x99, 0xFD, 0xD8, 0x44, 0xBE, + 0x4D, 0x49, 0x27, 0x33, 0x27, 0x8F, 0x7B, 0x87, 0x0D, 0xC2, 0xE0, 0xD8, 0x9F, 0xA2, 0x90, 0x5B, + 0x58, 0xC2, 0xEF, 0xDF, 0x5A, 0x86, 0x5E, 0xA7, 0x63, 0xD9, 0x1F, 0x9F, 0xE0, 0x81, 0x91, 0xB7, + 0x12, 0xDF, 0x35, 0x82, 0x19, 0x0F, 0x8F, 0x63, 0xDC, 0xED, 0x03, 0x9D, 0xDB, 0x16, 0xE9, 0x54, + 0x56, 0x6F, 0xDA, 0xCD, 0x9C, 0x8F, 0xBF, 0x71, 0xD4, 0xAE, 0xBE, 0x44, 0x7E, 0x15, 0xC1, 0x65, + 0xF6, 0x20, 0x57, 0xD7, 0x79, 0x3A, 0x87, 0x0C, 0x33, 0x1A, 0x0C, 0x70, 0xE6, 0x42, 0x21, 0x46, + 0x3F, 0x6F, 0x7E, 0x95, 0x14, 0xD7, 0x22, 0x2D, 0xD8, 0xD7, 0xDB, 0x8B, 0x3B, 0x6F, 0xEB, 0xCF, + 0x83, 0xA3, 0x87, 0x30, 0x69, 0xF4, 0x10, 0x92, 0xE2, 0x63, 0x11, 0x42, 0xE3, 0x93, 0x75, 0xDB, + 0xE9, 0xD3, 0x3D, 0x9A, 0xC4, 0x9E, 0x31, 0x0D, 0x6F, 0xBC, 0x4E, 0xE5, 0x8B, 0x7F, 0xA7, 0xF0, + 0xD9, 0xFA, 0x14, 0x5E, 0xFD, 0xAF, 0x87, 0xB8, 0x7F, 0xD4, 0xAD, 0x04, 0xFA, 0x7B, 0x73, 0x4B, + 0xDF, 0x38, 0x62, 0x22, 0x42, 0x9C, 0x13, 0x09, 0xAA, 0xCA, 0x89, 0xEC, 0x5C, 0x9E, 0x7A, 0x6B, + 0x19, 0xB9, 0x85, 0xF5, 0xB1, 0xB7, 0x39, 0xC0, 0xB3, 0x34, 0x91, 0x5F, 0xDE, 0x5E, 0x70, 0x35, + 0x43, 0x34, 0xE4, 0x46, 0x71, 0x14, 0x10, 0xAA, 0x69, 0x82, 0x03, 0x27, 0xCF, 0xD2, 0x3F, 0x3E, + 0x96, 0xEE, 0x31, 0x11, 0x2D, 0x5A, 0xE0, 0xDD, 0x74, 0x3A, 0x8C, 0x81, 0x7E, 0x78, 0xB8, 0xCB, + 0x0C, 0xE0, 0xB8, 0xAE, 0x91, 0x6C, 0xDE, 0x7B, 0x88, 0xFC, 0x92, 0x8B, 0xDC, 0x3F, 0xE2, 0x96, + 0xFA, 0x05, 0xBE, 0xB6, 0xD6, 0xC2, 0x5F, 0x96, 0x7E, 0x8D, 0x97, 0x87, 0x81, 0x37, 0x9F, 0xFE, + 0x0D, 0x3E, 0x9E, 0x86, 0x86, 0x88, 0x13, 0x67, 0xEE, 0xA7, 0x28, 0x94, 0x55, 0x56, 0xF1, 0xEC, + 0xDF, 0x3F, 0x61, 0x6B, 0x5A, 0x7D, 0x04, 0xA5, 0x15, 0xF9, 0x71, 0x81, 0xD5, 0x2E, 0xA6, 0x57, + 0x87, 0xD4, 0x2E, 0x3F, 0x8D, 0xAC, 0x04, 0x51, 0x09, 0x50, 0x50, 0x2A, 0x35, 0x9F, 0xA3, 0xA7, + 0x72, 0x5A, 0xB6, 0x9E, 0x40, 0x43, 0x05, 0x07, 0x21, 0x08, 0x31, 0xFA, 0xF3, 0xDB, 0x09, 0x23, + 0x38, 0x73, 0xA1, 0x88, 0xCA, 0x2A, 0x53, 0xFD, 0x5E, 0xA5, 0xFC, 0x52, 0x35, 0x39, 0xF9, 0xC5, + 0xFC, 0xE7, 0xD8, 0xDB, 0x08, 0x0B, 0x0E, 0x68, 0xB1, 0x56, 0x67, 0xAE, 0xAD, 0xE5, 0xAF, 0x4B, + 0x57, 0xB3, 0x7A, 0xF3, 0x1E, 0xC7, 0x33, 0xDF, 0x21, 0x4B, 0x2E, 0xB9, 0x1C, 0x1D, 0x55, 0xE5, + 0x78, 0x35, 0xF0, 0x1E, 0x36, 0xEB, 0xE8, 0xCF, 0x27, 0xB2, 0x99, 0x31, 0xF7, 0x23, 0x72, 0x72, + 0x0B, 0x5B, 0xBF, 0x61, 0x14, 0x82, 0x31, 0x43, 0xFA, 0xD1, 0x2D, 0x2A, 0x94, 0x3A, 0x87, 0x7C, + 0x91, 0x3A, 0xAB, 0x95, 0x40, 0x3F, 0x1F, 0xC6, 0xDE, 0x9A, 0xD4, 0x32, 0x5B, 0xAC, 0xA2, 0x60, + 0xAE, 0xB5, 0x30, 0xE7, 0xA3, 0xB5, 0x2C, 0xFC, 0x6A, 0x83, 0x74, 0x3F, 0x4B, 0x1C, 0x01, 0x5E, + 0xC1, 0x21, 0xDC, 0xD3, 0x95, 0xE8, 0xA8, 0x5A, 0x81, 0x1A, 0x90, 0x81, 0x0C, 0x13, 0xEA, 0x05, + 0x90, 0x95, 0x5B, 0xC8, 0xC9, 0xB3, 0x17, 0xB8, 0xBD, 0x7F, 0x82, 0x73, 0xE5, 0x36, 0x2E, 0x87, + 0x00, 0x7F, 0x1F, 0x4F, 0x06, 0x27, 0xF6, 0x20, 0xD4, 0xE8, 0x57, 0x2F, 0xB2, 0xF4, 0x3A, 0x1D, + 0x91, 0x21, 0x46, 0x6E, 0xE9, 0xDD, 0x1D, 0x37, 0x9D, 0x93, 0x8F, 0xA7, 0xAA, 0x54, 0x56, 0x9B, + 0xF9, 0xDB, 0xD2, 0xD5, 0xCC, 0xFD, 0xFC, 0xBB, 0xFA, 0xC0, 0x6C, 0x64, 0xE0, 0xDF, 0x13, 0xC8, + 0x60, 0xC2, 0x0E, 0x41, 0x47, 0x16, 0x6F, 0xAC, 0x46, 0x7E, 0x65, 0x73, 0x10, 0x32, 0x16, 0x96, + 0xCC, 0x73, 0xF9, 0x1C, 0x3A, 0x95, 0xC3, 0xA0, 0x84, 0x6E, 0x84, 0xB6, 0x54, 0xBC, 0x20, 0x23, + 0x41, 0x02, 0xFC, 0x7D, 0x1A, 0x6D, 0x10, 0xDD, 0xF4, 0x3A, 0xE2, 0x6E, 0x8A, 0x72, 0x9E, 0x19, + 0x3A, 0x95, 0xF3, 0xF9, 0xC5, 0xBC, 0xF0, 0xCF, 0xCF, 0x79, 0x77, 0xF5, 0x8F, 0xD4, 0x5A, 0xEA, + 0xA3, 0x3E, 0x8B, 0x90, 0x45, 0x62, 0xBE, 0xE9, 0x40, 0x1A, 0x75, 0x78, 0x35, 0xCD, 0x8B, 0x48, + 0xA6, 0x0C, 0xC4, 0x96, 0x42, 0x77, 0x26, 0xB7, 0x90, 0x9D, 0x3F, 0x1F, 0x27, 0x36, 0x3C, 0x84, + 0xD8, 0xE8, 0xF0, 0x6B, 0xEF, 0xBE, 0xAF, 0x86, 0xAB, 0xF1, 0xD0, 0x49, 0x93, 0x88, 0x26, 0x20, + 0x25, 0xED, 0x08, 0x4F, 0xCE, 0x59, 0xCA, 0xB7, 0xDB, 0xD3, 0xB0, 0x36, 0x6C, 0x16, 0x0B, 0x90, + 0xCC, 0x58, 0xD1, 0xC1, 0xF4, 0xE9, 0x94, 0xF2, 0xA6, 0x85, 0xC8, 0x4F, 0x6B, 0xF7, 0xC5, 0x16, + 0x72, 0x5A, 0x50, 0x5A, 0xCE, 0x86, 0x9F, 0x7E, 0xA6, 0xA6, 0xA6, 0x96, 0x5E, 0x37, 0x45, 0xC9, + 0x04, 0x1A, 0x57, 0xC1, 0x66, 0xA4, 0xCC, 0x2D, 0x28, 0xE1, 0xEF, 0x9F, 0x7F, 0xC7, 0xEC, 0x05, + 0x5F, 0x34, 0xCA, 0xD2, 0x45, 0x2A, 0x21, 0x4F, 0x22, 0xF3, 0x35, 0x3A, 0x3C, 0x20, 0xA0, 0xB3, + 0xEA, 0xCD, 0x16, 0x21, 0x2D, 0xC2, 0x5D, 0x90, 0x5F, 0x8D, 0x56, 0x4D, 0xE6, 0x5A, 0x76, 0x64, + 0x1C, 0x27, 0xF5, 0xE7, 0xE3, 0xF8, 0x79, 0x79, 0x10, 0x13, 0x1E, 0x8C, 0xA1, 0xB9, 0x64, 0xD1, + 0x96, 0x40, 0x95, 0x96, 0xE2, 0xC2, 0xD2, 0x32, 0xBE, 0xF8, 0xF7, 0x0E, 0x9E, 0x7D, 0xE7, 0x13, + 0x56, 0x6D, 0xFC, 0xC9, 0x1E, 0xCD, 0x0E, 0x92, 0xF8, 0xDB, 0x91, 0xC5, 0x13, 0x5A, 0x57, 0xFE, + 0xA7, 0x1D, 0xD0, 0xD9, 0x5E, 0x8A, 0x40, 0xA4, 0x68, 0xF8, 0x3D, 0x0E, 0xDF, 0x3E, 0xF7, 0x34, + 0xB8, 0x33, 0x6C, 0x40, 0x2F, 0xA6, 0x8C, 0x1B, 0xCE, 0xE8, 0xC1, 0x7D, 0x09, 0x0F, 0x09, 0x40, + 0x51, 0x75, 0x36, 0x95, 0xD7, 0xC9, 0x1C, 0x11, 0x45, 0xB1, 0x79, 0x2A, 0x15, 0x6A, 0x6B, 0x6A, + 0x38, 0x95, 0x93, 0xCF, 0xBA, 0x9D, 0xE9, 0x7C, 0xB5, 0xF1, 0x27, 0x0E, 0x9C, 0x3C, 0x7B, 0x79, + 0x48, 0x4F, 0x39, 0xF0, 0x21, 0xF2, 0x23, 0x98, 0x2E, 0x71, 0xCD, 0x3A, 0x8B, 0xCE, 0x66, 0x08, + 0x48, 0x17, 0xC0, 0x9D, 0xC0, 0x8B, 0xC8, 0x62, 0xC4, 0xF5, 0xB3, 0xD6, 0x4D, 0xAF, 0x23, 0xBE, + 0x6B, 0x24, 0x23, 0x6F, 0xEE, 0xC3, 0xA8, 0x5B, 0x12, 0xE9, 0xD3, 0x3D, 0x86, 0x88, 0xE0, 0x40, + 0xBC, 0x3C, 0xDC, 0x1B, 0xAA, 0xCB, 0x35, 0x82, 0x00, 0x4D, 0x50, 0x6B, 0xB1, 0x50, 0x56, 0x51, + 0xC5, 0x99, 0x0B, 0x85, 0xEC, 0x3B, 0x7A, 0x9A, 0x2D, 0xFB, 0x0E, 0xB3, 0xFB, 0x50, 0x26, 0xF9, + 0x25, 0x65, 0x97, 0xE7, 0xB3, 0xD4, 0x21, 0x3F, 0xA5, 0x3A, 0x17, 0xF9, 0x01, 0xCC, 0x4E, 0x0F, + 0x9D, 0xBD, 0x1E, 0x18, 0x62, 0x47, 0x28, 0xB2, 0xB4, 0xC7, 0x34, 0xA4, 0x7A, 0xDC, 0x68, 0x6C, + 0xEE, 0x6E, 0x7A, 0x42, 0x8D, 0xFE, 0x74, 0x8B, 0x0C, 0x25, 0x36, 0x2A, 0x8C, 0x2E, 0xA1, 0x46, + 0x82, 0xFC, 0x7D, 0xF0, 0xF2, 0x34, 0xA0, 0x2A, 0x2A, 0xE6, 0x5A, 0x0B, 0x15, 0x97, 0xAA, 0xB9, + 0x50, 0x7C, 0x91, 0x9C, 0xFC, 0x62, 0xCE, 0xE4, 0x16, 0x72, 0xBE, 0xB0, 0x84, 0x8A, 0xAA, 0xAB, + 0x26, 0x15, 0xD5, 0x21, 0x93, 0x2C, 0x97, 0x02, 0x5F, 0x21, 0x8D, 0x86, 0xFF, 0x8F, 0xAB, 0x40, + 0x41, 0xA6, 0x44, 0xBC, 0x88, 0xDC, 0xB7, 0xD4, 0xD0, 0x82, 0xEF, 0xA2, 0x3B, 0xF1, 0x2B, 0x03, + 0x7E, 0x40, 0x32, 0x3D, 0xB4, 0xB3, 0x1F, 0xB6, 0x29, 0x02, 0x5C, 0xAF, 0x08, 0x45, 0xD6, 0x06, + 0xBE, 0x0B, 0xF8, 0x15, 0x72, 0xEF, 0xE2, 0x6C, 0x32, 0x91, 0x1D, 0x16, 0xA4, 0x23, 0xE9, 0x38, + 0x32, 0xA1, 0x68, 0x13, 0x72, 0x66, 0x54, 0x75, 0xF6, 0xC3, 0x35, 0x85, 0xEB, 0x99, 0x21, 0x76, + 0xE8, 0x90, 0x16, 0xE3, 0x3E, 0x48, 0xAB, 0x71, 0x5F, 0xA0, 0x07, 0x92, 0x61, 0x01, 0x34, 0xE4, + 0xF5, 0xD5, 0x22, 0x09, 0x5D, 0x8C, 0xF4, 0xEA, 0x1D, 0x45, 0xA6, 0x1C, 0x67, 0x20, 0x23, 0x44, + 0x2A, 0x5B, 0x76, 0xDB, 0xCE, 0xC1, 0xFF, 0x01, 0xCE, 0x34, 0xF5, 0xEC, 0x2D, 0xA9, 0x9C, 0xA8, + 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3A, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x00, 0x32, 0x30, 0x31, 0x32, 0x2D, 0x30, 0x31, 0x2D, 0x33, 0x31, 0x54, 0x31, + 0x33, 0x3A, 0x34, 0x31, 0x3A, 0x31, 0x38, 0x2D, 0x30, 0x38, 0x3A, 0x30, 0x30, 0xED, 0x4F, 0xCC, + 0x0D, 0x00, 0x00, 0x00, 0x25, 0x74, 0x45, 0x58, 0x74, 0x64, 0x61, 0x74, 0x65, 0x3A, 0x6D, 0x6F, + 0x64, 0x69, 0x66, 0x79, 0x00, 0x32, 0x30, 0x31, 0x32, 0x2D, 0x30, 0x31, 0x2D, 0x33, 0x31, 0x54, + 0x31, 0x33, 0x3A, 0x34, 0x31, 0x3A, 0x31, 0x38, 0x2D, 0x30, 0x38, 0x3A, 0x30, 0x30, 0x9C, 0x12, + 0x74, 0xB1, 0x00, 0x00, 0x00, 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, + 0x72, 0x65, 0x00, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x6E, 0x6B, 0x73, 0x63, 0x61, 0x70, 0x65, 0x2E, + 0x6F, 0x72, 0x67, 0x9B, 0xEE, 0x3C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; \ No newline at end of file diff --git a/lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino b/lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino rename to lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino index 0f124253c..371515fe1 --- a/lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino +++ b/lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/Send_RFC822_Attachment.ino @@ -1,252 +1,252 @@ - - -/** - * This example will send the Email in plain text version - * with rfc822 message attachment which the rfc822 message - * also contains its attachement. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -/* This is for attachment data */ -#include "image.h" - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - Serial.println("Connect to NTP server and set the device time\r\nPlease wait...\r\n"); - float timeZone = 3;//GMT+3 - float daylightOffset = 0; - - /* Set the device time */ - MailClient.Time.setClock(timeZone, daylightOffset); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending Email with rfc822 attachment"; - message.addRecipient("Someone", "####@#####_dot_com"); - - message.text.content = "This is simple plain text message with rfc822 attachment"; - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.text.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - SMTP_Message rfc822; - rfc822.messageID = "1234@local.machine.example"; - rfc822.from.name = "rob"; - rfc822.from.email = "rob@example.com"; - rfc822.sender.name = "steve"; - rfc822.sender.email = "steve@example.com"; - String dt = MailClient.Time.getDateTimeString(); - rfc822.date = dt.c_str(); - rfc822.subject = "Test rfc822 message"; - rfc822.comment = "This is comment"; - rfc822.addRecipient("joe", "joe@example.com"); - rfc822.response.reply_to = "rob@example.com"; - rfc822.text.charSet = "utf-8"; - rfc822.text.content = "This is rfc822 text message"; - rfc822.text.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - rfc822.html.charSet = "utf-8"; - rfc822.html.content = "This is rfc822 html message"; - rfc822.html.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* The attachment data item */ - SMTP_Attachment att; - - /** Set the attachment info e.g. - * file name, MIME type, BLOB data, BLOB data size, - * and transfer encoding - */ - att.descr.filename = "firebase_logo.png"; - att.descr.mime = "image/png"; - att.blob.data = firebase_png; - att.blob.size = sizeof(firebase_png); - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /* Add the attachment to the rfc822 message */ - rfc822.addAttachment(att); - - /* Prepare other attachment data */ - uint8_t *a = new uint8_t[512]; - int j = 0; - - for (int i = 0; i < 512; i++) - { - a[i] = j; - j++; - if (j > 255) - j = 0; - } - - /** Set the attachment info e.g. - * file name, MIME type, BLOB data, BLOB data size. - * The default transfer encoding is base64. - */ - message.resetAttachItem(att); //Clear the attach item data to reuse - att.descr.filename = "test.dat"; - att.descr.mime = "application/octet-stream"; - att.blob.data = a; - att.blob.size = 512; - - /* Add this attachment to the message */ - message.addAttachment(att); - - /* Add rfc822 message in the message */ - message.addMessage(rfc822); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email in plain text version + * with rfc822 message attachment which the rfc822 message + * also contains its attachement. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +/* This is for attachment data */ +#include "image.h" + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + Serial.println("Connect to NTP server and set the device time\r\nPlease wait...\r\n"); + float timeZone = 3;//GMT+3 + float daylightOffset = 0; + + /* Set the device time */ + MailClient.Time.setClock(timeZone, daylightOffset); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending Email with rfc822 attachment"; + message.addRecipient("Someone", "####@#####_dot_com"); + + message.text.content = "This is simple plain text message with rfc822 attachment"; + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.text.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + SMTP_Message rfc822; + rfc822.messageID = "1234@local.machine.example"; + rfc822.from.name = "rob"; + rfc822.from.email = "rob@example.com"; + rfc822.sender.name = "steve"; + rfc822.sender.email = "steve@example.com"; + String dt = MailClient.Time.getDateTimeString(); + rfc822.date = dt.c_str(); + rfc822.subject = "Test rfc822 message"; + rfc822.comment = "This is comment"; + rfc822.addRecipient("joe", "joe@example.com"); + rfc822.response.reply_to = "rob@example.com"; + rfc822.text.charSet = "utf-8"; + rfc822.text.content = "This is rfc822 text message"; + rfc822.text.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + rfc822.html.charSet = "utf-8"; + rfc822.html.content = "This is rfc822 html message"; + rfc822.html.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* The attachment data item */ + SMTP_Attachment att; + + /** Set the attachment info e.g. + * file name, MIME type, BLOB data, BLOB data size, + * and transfer encoding + */ + att.descr.filename = "firebase_logo.png"; + att.descr.mime = "image/png"; + att.blob.data = firebase_png; + att.blob.size = sizeof(firebase_png); + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /* Add the attachment to the rfc822 message */ + rfc822.addAttachment(att); + + /* Prepare other attachment data */ + uint8_t *a = new uint8_t[512]; + int j = 0; + + for (int i = 0; i < 512; i++) + { + a[i] = j; + j++; + if (j > 255) + j = 0; + } + + /** Set the attachment info e.g. + * file name, MIME type, BLOB data, BLOB data size. + * The default transfer encoding is base64. + */ + message.resetAttachItem(att); //Clear the attach item data to reuse + att.descr.filename = "test.dat"; + att.descr.mime = "application/octet-stream"; + att.blob.data = a; + att.blob.size = 512; + + /* Add this attachment to the message */ + message.addAttachment(att); + + /* Add rfc822 message in the message */ + message.addMessage(rfc822); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/image.h b/lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/image.h old mode 100755 new mode 100644 similarity index 99% rename from lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/image.h rename to lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/image.h index 5ddd6829b..80ce0a0e7 --- a/lib/lib_div/lib_mail/examples/Send_RFC822_Attachment/image.h +++ b/lib/libesp32/lib_mail/examples/Send_RFC822_Attachment/image.h @@ -1,1851 +1,1851 @@ -#include - -static const uint8_t firebase_png[] PROGMEM = { - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, - 0x70, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, - 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, - 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, - 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x55, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5E, 0xED, 0xDD, 0x59, - 0x90, 0x1D, 0xF5, 0x81, 0xE7, 0x7B, 0x3F, 0xF4, 0xC3, 0x84, 0x91, 0x40, 0x6E, 0xEF, 0xC6, 0x8B, - 0x30, 0x92, 0x4A, 0x1B, 0x58, 0x98, 0xCD, 0xA0, 0xA5, 0x4A, 0x12, 0x20, 0x6C, 0x9A, 0xB6, 0xBA, - 0x01, 0x63, 0x4F, 0x3F, 0x8C, 0x1E, 0x26, 0xC2, 0xF7, 0xE5, 0xC6, 0x25, 0xE2, 0xDE, 0x97, 0x3B, - 0x7D, 0x67, 0xAC, 0x6E, 0x63, 0x63, 0x83, 0x8D, 0xBC, 0xB1, 0x99, 0xA5, 0x10, 0x08, 0xED, 0x52, - 0x69, 0xAD, 0x92, 0xA0, 0xBB, 0xAB, 0xA7, 0xED, 0x6E, 0x77, 0xB7, 0x81, 0x42, 0x9B, 0x99, 0x89, - 0x89, 0xDB, 0x9A, 0xB9, 0x0E, 0x2F, 0x2C, 0x76, 0x61, 0x16, 0x49, 0x85, 0xA4, 0xFC, 0xDF, 0xFF, - 0x3F, 0x4F, 0xE6, 0xA9, 0x3C, 0x79, 0x7E, 0xE7, 0x9C, 0xCC, 0x3C, 0x99, 0x75, 0xCE, 0xC9, 0xFA, - 0x7E, 0x22, 0x7E, 0xA1, 0x6E, 0x2F, 0x42, 0xC2, 0x94, 0xE2, 0xFF, 0x8D, 0x7F, 0x65, 0x9E, 0xF7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x28, 0x82, 0x79, 0x6E, 0xCD, 0x80, 0xBF, 0xC3, 0x5F, 0x58, 0x12, 0xFC, - 0x4B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x1F, 0x73, 0x78, 0xCD, 0x3A, 0xEF, 0xB9, 0x35, 0x63, 0xE6, - 0xB9, 0x9B, 0x4D, 0x75, 0xCF, 0xAE, 0xF1, 0xE7, 0x3D, 0x7B, 0xD3, 0xA0, 0x0B, 0x92, 0xE0, 0x3F, - 0x0A, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x98, 0x91, 0x35, 0xB3, 0xEB, 0xC2, 0x23, 0x12, 0x1F, 0xD1, - 0x79, 0x87, 0xD7, 0x6C, 0x08, 0xFE, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x8E, 0xFB, 0x16, 0x2B, - 0xEF, 0xD9, 0x35, 0xE3, 0x95, 0xE0, 0xB8, 0xD1, 0x98, 0x03, 0x57, 0x19, 0xB3, 0x7B, 0xAE, 0x31, - 0x3B, 0x3E, 0x65, 0xCC, 0xB6, 0x8F, 0x1A, 0xB3, 0xF3, 0xD3, 0xC6, 0x0C, 0x2D, 0x34, 0x66, 0x78, - 0xD9, 0x64, 0x84, 0x3C, 0x7B, 0xD3, 0x60, 0xF0, 0x5F, 0x07, 0x00, 0x00, 0x00, 0x80, 0xE4, 0xAA, - 0x37, 0x1F, 0x2E, 0x3C, 0x76, 0x7C, 0xC2, 0x98, 0xED, 0x1F, 0xAB, 0x84, 0x87, 0xDA, 0x2E, 0x1B, - 0x26, 0x87, 0x6F, 0xA8, 0x84, 0xC8, 0xA1, 0x9B, 0xD6, 0x06, 0x3F, 0x05, 0x00, 0x00, 0x00, 0x00, - 0xB4, 0xE6, 0x9E, 0xF9, 0xF0, 0xE3, 0x63, 0xCF, 0xA2, 0x4A, 0x78, 0x34, 0x8B, 0x8F, 0x70, 0xDB, - 0x3F, 0x6E, 0xCC, 0xC8, 0x0A, 0xF7, 0xAD, 0x58, 0xE3, 0x66, 0xB4, 0x7F, 0x56, 0xF0, 0x53, 0x01, - 0x00, 0x00, 0x00, 0x40, 0x73, 0xDE, 0x73, 0x37, 0x9F, 0x34, 0xFB, 0xAF, 0x48, 0x1E, 0x1F, 0xE1, - 0x76, 0x5E, 0x62, 0xCC, 0xE1, 0xD5, 0x76, 0x37, 0xAE, 0x0F, 0x7E, 0x2A, 0x00, 0x00, 0x00, 0x00, - 0x68, 0xCC, 0x3D, 0x78, 0x6E, 0x0E, 0xAF, 0x4A, 0x1F, 0x1F, 0xFE, 0x3E, 0x62, 0xCC, 0xBE, 0xCF, - 0x18, 0xEF, 0xF0, 0x4D, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x68, 0xCD, 0xFF, 0x8C, 0x8F, 0xBD, - 0x97, 0x67, 0x8B, 0x0F, 0xB7, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x20, 0x19, 0x1B, - 0x20, 0xEB, 0xCD, 0xAE, 0x4F, 0xC7, 0xE2, 0xA2, 0xD5, 0x82, 0xF8, 0x08, 0xB7, 0x67, 0xB1, 0x31, - 0xCF, 0xDE, 0x64, 0xDC, 0x6D, 0x4A, 0xF0, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x40, 0x3D, 0xEF, 0xB9, - 0x9B, 0x07, 0x75, 0x64, 0x34, 0x5A, 0x2C, 0x3E, 0xC2, 0x1D, 0x1A, 0xE0, 0xB5, 0xBC, 0x00, 0x00, - 0x00, 0x00, 0x9A, 0xF3, 0x0E, 0x2E, 0x1B, 0xD5, 0xA1, 0xA1, 0x26, 0xC2, 0x23, 0xDC, 0xD0, 0x02, - 0x6E, 0x41, 0x00, 0x00, 0x00, 0x00, 0x34, 0xE7, 0x1D, 0xB8, 0x76, 0x5C, 0xC7, 0x46, 0x7C, 0x22, - 0x3A, 0xE2, 0x1B, 0x59, 0x6E, 0xBC, 0xC3, 0x37, 0x8E, 0x06, 0x3F, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x4C, 0x72, 0x6F, 0xAE, 0x32, 0xFB, 0x96, 0xD8, 0x78, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x0D, 0xB5, - 0x5D, 0x73, 0x2A, 0xB7, 0x20, 0xCF, 0xAD, 0x19, 0x08, 0xFE, 0x12, 0x00, 0x00, 0x00, 0x00, 0x50, - 0xE1, 0xBF, 0x01, 0x6B, 0x77, 0x9F, 0x8D, 0x07, 0x15, 0x1D, 0xE1, 0x44, 0x68, 0x34, 0xDB, 0xF0, - 0xF5, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xE7, 0x7F, 0x02, 0xFA, 0xAE, 0xB9, 0x36, 0x1C, - 0x54, 0x78, 0xB8, 0x89, 0xC0, 0x68, 0xB5, 0x5D, 0x97, 0x1A, 0xF3, 0xEC, 0x8D, 0xC6, 0x3C, 0xB7, - 0x9A, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x93, 0x6C, 0x80, 0xAC, 0xD7, 0xE1, 0xE1, 0x26, 0xE2, - 0xA2, 0xE5, 0x3E, 0x5C, 0xD9, 0x81, 0xAB, 0x8C, 0xF7, 0xEC, 0x0D, 0x27, 0x83, 0xBF, 0x0C, 0x00, - 0x00, 0x00, 0x00, 0xBC, 0xE7, 0x3D, 0xDE, 0xF0, 0x8A, 0x06, 0x6F, 0xC0, 0x52, 0x71, 0xD1, 0x6A, - 0x41, 0x7C, 0xB8, 0xED, 0xF8, 0x44, 0xE5, 0x16, 0xE4, 0xF0, 0x8D, 0xEB, 0x82, 0xBF, 0x14, 0x00, - 0x00, 0x00, 0x80, 0xE9, 0xCE, 0x3B, 0x70, 0xFD, 0xC9, 0xDC, 0xE3, 0x23, 0x1C, 0xB7, 0x20, 0x00, - 0x00, 0x00, 0x00, 0xA2, 0xCC, 0xFE, 0x2B, 0x6D, 0x2C, 0x14, 0x10, 0x1F, 0x6E, 0xEE, 0x16, 0xE4, - 0xF0, 0x2A, 0x77, 0x0B, 0xB2, 0x3E, 0xF8, 0xCB, 0x01, 0x00, 0x00, 0x00, 0x98, 0xAE, 0x6C, 0x18, - 0x2C, 0x31, 0x7B, 0x2E, 0xB3, 0xB1, 0x50, 0x40, 0x7C, 0x84, 0xDB, 0x77, 0xB9, 0x7B, 0x23, 0xD6, - 0xB8, 0x7B, 0xDD, 0x6F, 0xF0, 0x97, 0x05, 0x00, 0x00, 0x00, 0x30, 0x1D, 0x99, 0x43, 0x37, 0xAD, - 0x9D, 0x7C, 0x03, 0x96, 0x8A, 0x8B, 0x56, 0x13, 0xC1, 0x11, 0xDF, 0xF6, 0x8F, 0x71, 0x0B, 0x02, - 0x00, 0x00, 0x00, 0x20, 0x78, 0x03, 0xD6, 0xF6, 0x8F, 0xDB, 0x50, 0x50, 0x71, 0xD1, 0x6A, 0x22, - 0x36, 0x1A, 0x8D, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB3, 0x37, 0x0D, 0xEA, 0xB8, 0x68, - 0x35, 0x11, 0x19, 0xAD, 0x76, 0xA8, 0xDF, 0x3D, 0x90, 0x3E, 0x18, 0xFC, 0xA5, 0x01, 0x00, 0x00, - 0x00, 0x4C, 0x37, 0xDE, 0x81, 0x6B, 0x46, 0x75, 0x60, 0x34, 0x9B, 0x88, 0x8B, 0x56, 0xDB, 0xFA, - 0x21, 0x63, 0x86, 0xE6, 0x57, 0x5E, 0xCB, 0x3B, 0xB2, 0x66, 0x76, 0xF0, 0x97, 0x07, 0x00, 0x00, - 0x00, 0x30, 0x9D, 0x98, 0x03, 0x57, 0xDB, 0x40, 0x50, 0x91, 0xD1, 0x68, 0x22, 0x2E, 0x5A, 0xCD, - 0xC5, 0x47, 0x38, 0x6E, 0x41, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC9, 0xDD, 0x44, 0x98, 0x7D, 0x9F, - 0xB1, 0x91, 0xA0, 0x42, 0x43, 0x4D, 0xC4, 0x45, 0xAB, 0x45, 0xE3, 0xC3, 0x6D, 0xF7, 0xDC, 0xCA, - 0x2D, 0xC8, 0x73, 0xAB, 0x07, 0x82, 0x5F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC0, 0x3C, 0xB7, - 0x66, 0xC0, 0xEC, 0x9E, 0x67, 0x43, 0x41, 0xC5, 0x46, 0x7C, 0x22, 0x2E, 0x5A, 0x2D, 0x1E, 0x1F, - 0xE1, 0x86, 0xAF, 0x73, 0x0F, 0xA4, 0x8F, 0x06, 0xBF, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x81, - 0xF7, 0xEC, 0x4D, 0x77, 0x99, 0x9D, 0x97, 0xD8, 0x58, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x8B, 0x56, - 0x53, 0xE1, 0x11, 0x6E, 0xD7, 0xA7, 0xB9, 0x05, 0x01, 0x00, 0x00, 0x00, 0xA6, 0x1B, 0xEF, 0xF0, - 0x8D, 0x1B, 0x74, 0x70, 0x44, 0x27, 0xE2, 0xA2, 0xD5, 0x54, 0x74, 0xC4, 0xC7, 0x2D, 0x08, 0x00, - 0x00, 0x00, 0x30, 0xBD, 0x78, 0x07, 0xAF, 0x6F, 0xF1, 0x06, 0x2C, 0x11, 0x17, 0xAD, 0xA6, 0x62, - 0x43, 0xCD, 0x7D, 0xF6, 0x88, 0xBB, 0x05, 0x39, 0x7C, 0xE3, 0xBA, 0xE0, 0x97, 0x03, 0x00, 0x00, - 0x00, 0xA0, 0xCC, 0xBC, 0x03, 0xD7, 0x9D, 0xD4, 0xE1, 0xE1, 0x26, 0xE2, 0xA2, 0xD5, 0x54, 0x68, - 0x34, 0xDB, 0xFE, 0x2B, 0xDD, 0x1B, 0xB1, 0x4E, 0x06, 0xBF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x65, - 0xD6, 0xF8, 0x0D, 0x58, 0x22, 0x2E, 0x5A, 0x4D, 0x05, 0x46, 0xAB, 0xB9, 0x5B, 0x90, 0xC3, 0x37, - 0x70, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x94, 0x9D, 0xFF, 0x06, 0xAC, 0xA1, 0x05, 0x36, 0x1E, 0x3A, - 0x14, 0x1F, 0xFE, 0x3E, 0x68, 0x5C, 0x04, 0x79, 0x87, 0x6E, 0x18, 0x37, 0xA3, 0xFD, 0xB3, 0x82, - 0x5F, 0x1A, 0x00, 0x00, 0x00, 0x80, 0xB2, 0x71, 0xB7, 0x0E, 0x66, 0xD7, 0x1C, 0x1B, 0x10, 0x1D, - 0x8C, 0x0F, 0x37, 0xF7, 0xD7, 0x3D, 0xB4, 0xD2, 0x98, 0xC3, 0xAB, 0xD7, 0x07, 0xBF, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x65, 0x63, 0x03, 0x64, 0xBD, 0xD9, 0x7E, 0x71, 0x25, 0x00, 0x3A, 0x15, 0x1F, - 0xE1, 0xF6, 0x5E, 0xC6, 0x2D, 0x08, 0x00, 0x00, 0x00, 0x50, 0x66, 0xDE, 0xC8, 0xCA, 0xA1, 0xAE, - 0x88, 0x0F, 0xB7, 0xE0, 0x16, 0xC4, 0x3B, 0xBC, 0x7A, 0x43, 0xF0, 0xCB, 0x03, 0x00, 0x00, 0x00, - 0x50, 0x26, 0xDE, 0xC1, 0x6B, 0xC7, 0xBA, 0x22, 0x3E, 0xC2, 0xED, 0xEE, 0xAB, 0x3C, 0x90, 0x3E, - 0xB2, 0x66, 0x76, 0xF0, 0x4B, 0x04, 0x00, 0x00, 0x00, 0x50, 0x16, 0x66, 0xFF, 0x67, 0x75, 0x5C, - 0xB4, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0xD1, 0x11, 0xDF, 0xC8, 0x0A, 0xE3, 0x1D, 0x5A, 0x3D, 0x18, - 0xFC, 0x12, 0x01, 0x00, 0x00, 0x00, 0x94, 0x81, 0xBB, 0x65, 0x30, 0xFB, 0x2E, 0xD7, 0x81, 0xD1, - 0x6C, 0x32, 0x2C, 0x92, 0x4C, 0xC4, 0x86, 0x1A, 0xB7, 0x20, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x98, - 0xE7, 0x56, 0x0F, 0x98, 0x5D, 0x97, 0xEA, 0xC8, 0x68, 0x34, 0x19, 0x16, 0x49, 0x26, 0x42, 0xA3, - 0xD9, 0x0E, 0x7E, 0xCE, 0x78, 0x87, 0x6F, 0x18, 0x0D, 0x7E, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x7A, - 0x9D, 0xFF, 0x06, 0xAC, 0x1D, 0x9F, 0xD0, 0xA1, 0xA1, 0x26, 0xC3, 0x22, 0xC9, 0x44, 0x60, 0xB4, - 0xDA, 0xCE, 0x4B, 0x82, 0x5B, 0x90, 0xD5, 0x03, 0xC1, 0x2F, 0x17, 0x00, 0x00, 0x00, 0x40, 0x2F, - 0xF3, 0x9E, 0xBD, 0x61, 0x50, 0x86, 0x86, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0x71, 0x91, 0x74, 0x07, - 0xAF, 0x35, 0xDE, 0xE1, 0x55, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE0, 0x1D, 0xBC, 0x6E, - 0x54, 0xC6, 0x46, 0x7C, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, 0x6D, 0xF9, 0x80, 0x31, 0x3B, - 0x67, 0xBB, 0x0F, 0x26, 0x34, 0xE6, 0xD0, 0xAA, 0xB5, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, 0x40, - 0xAF, 0xF2, 0x0E, 0x5C, 0x33, 0x2E, 0x83, 0x23, 0x3A, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, - 0x5C, 0x7C, 0x84, 0xDB, 0x77, 0x85, 0xFB, 0x5C, 0x90, 0x93, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, - 0x40, 0x2F, 0x72, 0x9F, 0x36, 0xDE, 0xF2, 0x0D, 0x58, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, - 0x45, 0xE3, 0xC3, 0x6D, 0xDB, 0xC7, 0x2A, 0xB7, 0x20, 0x87, 0x57, 0xAD, 0x0B, 0x7E, 0xE9, 0x00, - 0x00, 0x00, 0x00, 0x7A, 0x8D, 0xFF, 0x06, 0xAC, 0xDD, 0x73, 0xEB, 0xA3, 0x23, 0x9C, 0x0C, 0x8B, - 0x24, 0x13, 0x51, 0x91, 0x74, 0xF1, 0xF8, 0x08, 0xC7, 0x2D, 0x08, 0x00, 0x00, 0x00, 0xD0, 0xDB, - 0xCC, 0xE1, 0x1B, 0xD7, 0x35, 0x7C, 0x05, 0xAF, 0x0C, 0x8B, 0x24, 0x13, 0x51, 0x91, 0x74, 0x2A, - 0x3C, 0xC2, 0x6D, 0xB5, 0xBF, 0xA6, 0x43, 0x03, 0xEE, 0x26, 0x64, 0x7D, 0xF0, 0xCB, 0x07, 0x00, - 0x00, 0x00, 0xD0, 0x4B, 0xFC, 0x57, 0xF0, 0xF6, 0x42, 0x7C, 0x84, 0xDB, 0xB3, 0xD8, 0x7D, 0x3A, - 0xFA, 0xB8, 0xFB, 0xD6, 0xB1, 0xE0, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x57, 0x78, 0xC3, 0xCB, - 0xEB, 0xDF, 0x80, 0x25, 0xC3, 0x22, 0xC9, 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x0D, 0x35, 0x6E, 0x41, - 0x00, 0x00, 0x00, 0x80, 0xDE, 0xE5, 0x1D, 0xBC, 0x76, 0xAC, 0x67, 0xE2, 0x23, 0xDC, 0x9E, 0x45, - 0xC6, 0x3B, 0xB4, 0x6A, 0xDC, 0x8C, 0xF4, 0xCF, 0x0E, 0x7E, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x7A, - 0x81, 0xD9, 0x7F, 0x45, 0x6F, 0xC5, 0x87, 0xBF, 0xF7, 0x1B, 0x33, 0xB2, 0xCC, 0x46, 0xC8, 0xCA, - 0xC1, 0xE0, 0xB7, 0x01, 0x00, 0x00, 0x00, 0xA0, 0xDB, 0x99, 0xC3, 0x37, 0x2E, 0x71, 0xB7, 0x09, - 0x3D, 0x17, 0x1F, 0x6E, 0xEE, 0xCD, 0x5D, 0x87, 0x57, 0xD9, 0x10, 0xE9, 0xCD, 0x5B, 0x10, 0xF7, - 0xEB, 0xB6, 0x1B, 0x08, 0xC6, 0x4D, 0x0E, 0x00, 0x00, 0x00, 0xCA, 0xCF, 0x1C, 0xBA, 0x69, 0xAD, - 0xFF, 0x06, 0x2C, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x5A, 0x10, 0x1F, - 0xE1, 0x7A, 0xEC, 0x16, 0xC4, 0xFF, 0xCC, 0x95, 0x91, 0xFE, 0xF5, 0xDE, 0xA1, 0x81, 0x93, 0xFE, - 0x73, 0x2C, 0x91, 0xF9, 0xFF, 0x9A, 0xFD, 0xF7, 0x78, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0xA5, 0x55, - 0x79, 0x03, 0xD6, 0x47, 0x6D, 0x10, 0xA8, 0xB8, 0x68, 0x35, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, - 0xD5, 0x62, 0xF1, 0xE1, 0xB6, 0xF3, 0x53, 0xC1, 0x2D, 0xC8, 0xEA, 0x81, 0xE0, 0xB7, 0xD4, 0xB5, - 0xCC, 0xA1, 0xFE, 0xB5, 0xDE, 0xC8, 0xC0, 0x78, 0x3C, 0x3C, 0xE2, 0xAB, 0xFC, 0x67, 0xFA, 0xD7, - 0x06, 0xFF, 0x35, 0x00, 0x00, 0x00, 0xA0, 0x3C, 0xBC, 0x67, 0x6F, 0x18, 0xD4, 0x71, 0xD1, 0x6A, - 0x22, 0x2A, 0x92, 0x4E, 0xC6, 0x45, 0xAB, 0x89, 0xF8, 0x08, 0x77, 0xF0, 0x1A, 0xE3, 0x1D, 0x5E, - 0x39, 0x1A, 0xFC, 0x96, 0xBA, 0x92, 0x19, 0xE9, 0x5F, 0x57, 0x8D, 0x8C, 0x91, 0xE5, 0xFE, 0x43, - 0xF4, 0x66, 0xC7, 0xC7, 0x6B, 0xE7, 0xFE, 0x35, 0xF7, 0xEF, 0xF9, 0xFF, 0x99, 0x7E, 0x37, 0x3E, - 0xF1, 0x1D, 0x00, 0x00, 0x00, 0xE5, 0xE2, 0x1D, 0xB8, 0x7A, 0x54, 0x07, 0x46, 0xB3, 0x89, 0xA8, - 0x48, 0x3A, 0x19, 0x17, 0xAD, 0x26, 0xA2, 0x23, 0xBA, 0x2E, 0xBF, 0x05, 0xB1, 0x21, 0x31, 0xBB, - 0x7A, 0xF3, 0x31, 0x34, 0xBF, 0xF2, 0x6B, 0xDE, 0x3C, 0x4B, 0xCF, 0xFD, 0x7B, 0x43, 0x7D, 0x7E, - 0x80, 0x78, 0x23, 0x2B, 0x78, 0xCB, 0x17, 0x00, 0x00, 0x00, 0xCA, 0xC5, 0xEC, 0xBF, 0xD2, 0x86, - 0x81, 0x8A, 0x8C, 0x46, 0x13, 0x51, 0x91, 0x74, 0x32, 0x2E, 0x5A, 0x2D, 0x12, 0x1A, 0xCD, 0xE6, - 0x6E, 0x41, 0x0E, 0xAD, 0x1A, 0x0B, 0x7E, 0x5B, 0x5D, 0xC5, 0xC6, 0xC7, 0x90, 0x1F, 0x1F, 0xBB, - 0x3E, 0xAD, 0xA3, 0xA3, 0x66, 0x17, 0x55, 0xB6, 0xF3, 0x92, 0x4A, 0x84, 0x0C, 0xAF, 0xE8, 0xEA, - 0x9B, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x31, 0xFF, 0x81, 0xE8, 0xBD, 0x97, 0xDB, 0x38, 0x50, 0xA1, - 0xA1, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x9A, 0x08, 0x8D, 0x46, 0x73, 0xCF, 0xB1, 0xB8, - 0x5B, 0x90, 0xC3, 0xAB, 0xBA, 0xEA, 0xDB, 0x96, 0x6C, 0x44, 0x0C, 0xF8, 0xF1, 0xE1, 0xDE, 0xD8, - 0x25, 0x83, 0x23, 0xBA, 0x20, 0x3E, 0x62, 0x11, 0xE2, 0x7E, 0x8E, 0xE0, 0xA7, 0x03, 0x00, 0x00, - 0x00, 0x7A, 0x97, 0x79, 0x6E, 0xF5, 0x80, 0x7F, 0x30, 0x96, 0xB1, 0x11, 0x9F, 0x88, 0x8A, 0xA4, - 0x93, 0x71, 0xD1, 0x6A, 0x22, 0x32, 0x5A, 0x6D, 0xDF, 0x12, 0xE3, 0x1D, 0x5E, 0x75, 0x32, 0xF8, - 0xED, 0x75, 0x05, 0xEF, 0xD0, 0xC0, 0xA8, 0x39, 0x70, 0x55, 0x10, 0x18, 0xCD, 0x16, 0x8B, 0x8F, - 0x70, 0xFB, 0xAF, 0xE4, 0x16, 0x04, 0x00, 0x00, 0x00, 0xE5, 0xE0, 0x3D, 0x7B, 0xC3, 0x5D, 0x66, - 0xC7, 0xA7, 0x6C, 0x24, 0xA8, 0xE0, 0x88, 0x4E, 0x44, 0x45, 0xD2, 0xC9, 0xB8, 0x68, 0x35, 0x11, - 0x17, 0x49, 0x16, 0xDC, 0x82, 0x78, 0x87, 0x56, 0xDD, 0x15, 0xFC, 0x16, 0x3B, 0xAA, 0x7A, 0xFB, - 0xE1, 0x1E, 0x30, 0x97, 0xD1, 0x11, 0x4E, 0x84, 0x47, 0xB8, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, - 0x00, 0x80, 0x72, 0xF0, 0x0E, 0xDF, 0xB8, 0x41, 0x07, 0x47, 0x74, 0x22, 0x2A, 0x92, 0x4E, 0xC6, - 0x45, 0xAB, 0x89, 0xB0, 0x48, 0xB3, 0x3D, 0x0B, 0x8D, 0x37, 0xB2, 0x72, 0xBC, 0x1B, 0x3E, 0x4B, - 0x23, 0xD9, 0xED, 0x87, 0x88, 0x8E, 0xF8, 0xB8, 0x05, 0x01, 0x00, 0x00, 0x40, 0x19, 0x78, 0x07, - 0xAF, 0x6B, 0xF1, 0x06, 0x2C, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, 0xD5, 0x44, 0x50, 0xA4, 0xDA, - 0x1F, 0x57, 0xFE, 0xDA, 0x23, 0x2B, 0xEC, 0x56, 0xAE, 0x0F, 0x7E, 0x9B, 0x1D, 0xE1, 0x3E, 0xC7, - 0xC3, 0xBF, 0xFD, 0x70, 0x7F, 0x1F, 0x65, 0x78, 0xB8, 0x89, 0xD8, 0x50, 0xF3, 0x7F, 0x4F, 0xFD, - 0xC6, 0x1C, 0xE4, 0xB3, 0x41, 0x00, 0x00, 0x00, 0xD0, 0xC3, 0xBC, 0x03, 0x9F, 0x3B, 0x59, 0x1F, - 0x1D, 0xE1, 0x22, 0x31, 0x91, 0x76, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xD9, 0xF8, 0x08, - 0xD7, 0x05, 0xB7, 0x20, 0xFE, 0xA7, 0x9A, 0xBB, 0x07, 0xFC, 0x65, 0x78, 0xB8, 0x89, 0xD0, 0x68, - 0xB6, 0xBD, 0x97, 0xB9, 0x5B, 0x90, 0xAE, 0x7A, 0xBE, 0x05, 0x00, 0x00, 0x00, 0x48, 0xA5, 0xF1, - 0x1B, 0xB0, 0x44, 0x54, 0x24, 0x9D, 0x8C, 0x8B, 0x56, 0x53, 0x41, 0x91, 0x66, 0x91, 0xF8, 0x70, - 0x73, 0xBF, 0x8E, 0xE1, 0xA5, 0xEE, 0x13, 0xC5, 0x37, 0x04, 0xBF, 0xD5, 0x29, 0x65, 0xC2, 0x0F, - 0x1D, 0x74, 0x7F, 0x2F, 0xF3, 0x88, 0x0F, 0x37, 0xF7, 0x7B, 0xF2, 0x9F, 0x05, 0x59, 0xCE, 0x87, - 0x13, 0x02, 0x00, 0x00, 0xA0, 0xF7, 0xF8, 0x6F, 0xC0, 0x72, 0x1F, 0x8A, 0x57, 0xB6, 0xF8, 0x08, - 0xB7, 0x6B, 0x8E, 0x31, 0x87, 0x56, 0xBA, 0x43, 0xFB, 0x94, 0x7F, 0x90, 0x5F, 0xF3, 0xDB, 0x0F, - 0x11, 0x17, 0x49, 0xB7, 0x67, 0xB1, 0xF1, 0x86, 0x97, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x80, 0xDE, - 0x63, 0x0E, 0xDD, 0xB4, 0xD6, 0xFF, 0x60, 0xBC, 0x32, 0xC6, 0x47, 0x38, 0x77, 0x0B, 0x72, 0x68, - 0xE5, 0x60, 0xF0, 0x5B, 0x9E, 0x12, 0xCD, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0xDD, 0x33, 0x17, 0xDA, - 0xDF, 0x93, 0xFD, 0x7B, 0x66, 0x7F, 0x4F, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0xE7, 0x98, 0xC3, - 0x37, 0xAE, 0xF7, 0x5F, 0x5B, 0x5B, 0xD6, 0xF8, 0x70, 0x0B, 0x6F, 0x41, 0x0E, 0xF7, 0x2F, 0x09, - 0x7E, 0xDB, 0x85, 0x72, 0xCF, 0x9C, 0xF8, 0xB7, 0x1F, 0xEE, 0x66, 0x29, 0xEF, 0xF8, 0x08, 0xB7, - 0x7B, 0x9E, 0xF1, 0x46, 0x96, 0x8F, 0x9B, 0xA1, 0xCE, 0xBF, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, - 0xCC, 0x1B, 0x59, 0x39, 0x54, 0xEA, 0xF8, 0x08, 0x77, 0xE0, 0x6A, 0x77, 0x0B, 0x32, 0x25, 0xAF, - 0xB0, 0x35, 0x23, 0xFD, 0xEB, 0xCD, 0xC8, 0x72, 0xFB, 0xD7, 0xB5, 0xBF, 0xC6, 0x22, 0xE2, 0xC3, - 0x6D, 0xF3, 0xFB, 0xC2, 0x5B, 0x90, 0x8E, 0xBE, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, 0xC5, 0xDB, - 0x7F, 0xED, 0x58, 0xE9, 0xE3, 0xC3, 0x6D, 0xC7, 0x27, 0xC3, 0x67, 0x41, 0x0A, 0xFD, 0x20, 0x3F, - 0xFF, 0xF6, 0x63, 0x64, 0x60, 0xBC, 0xFE, 0xF6, 0x43, 0x44, 0x45, 0xD2, 0xC5, 0xE3, 0x23, 0x1C, - 0xB7, 0x20, 0x00, 0x00, 0x00, 0xE8, 0x35, 0x66, 0xDF, 0x12, 0x1D, 0x15, 0x49, 0x27, 0xE3, 0xA2, - 0xD5, 0x54, 0x50, 0xA4, 0x99, 0x08, 0x8C, 0x56, 0x73, 0x37, 0x06, 0x53, 0x70, 0x0B, 0xA2, 0x6F, - 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8F, 0x70, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0x97, 0x98, - 0x91, 0x35, 0xB3, 0xDD, 0xE7, 0x4A, 0xC8, 0xB0, 0x48, 0x32, 0x19, 0x17, 0xAD, 0x16, 0x8F, 0x89, - 0xB4, 0x13, 0x71, 0xD1, 0x6A, 0xEE, 0xA0, 0xEE, 0xB6, 0xED, 0x63, 0x95, 0x5B, 0x90, 0x43, 0xC5, - 0x7C, 0x90, 0x9F, 0xBE, 0xFD, 0x10, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0xE2, 0xE3, 0x16, 0x04, 0x00, - 0x00, 0x00, 0xBD, 0xC2, 0x8C, 0xAC, 0x1E, 0x30, 0x3B, 0x2F, 0xD1, 0x71, 0xD1, 0x6A, 0x32, 0x2E, - 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0x30, 0x3E, 0xC2, 0xED, 0xFD, 0x8C, 0xBB, - 0x05, 0x29, 0xE4, 0x15, 0xB6, 0xEE, 0xF3, 0x46, 0xCC, 0xF0, 0xF5, 0xF6, 0xAF, 0x33, 0x45, 0xF1, - 0x11, 0xEE, 0xE0, 0xE7, 0x6C, 0x84, 0xAC, 0xE8, 0xC8, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x89, - 0x99, 0xC3, 0xAB, 0xD7, 0x9B, 0xED, 0x17, 0xEB, 0xC0, 0x68, 0x36, 0x19, 0x17, 0xAD, 0xA6, 0x82, - 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x5A, 0x3C, 0x3E, 0xDC, 0xB6, 0x7E, 0x38, 0x7C, 0x16, 0x24, 0xD7, - 0x57, 0xD8, 0xDA, 0x9F, 0x6F, 0xB6, 0xFF, 0xDA, 0x5D, 0xF7, 0x4A, 0xE3, 0xA9, 0x8C, 0x0F, 0xB7, - 0x9D, 0xB3, 0xED, 0xEF, 0x67, 0x85, 0xFB, 0x3D, 0x4D, 0xF9, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x79, 0x87, 0x57, 0x6F, 0x90, 0x81, 0xD1, 0x6C, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, - 0x89, 0xB8, 0x68, 0x35, 0x15, 0x1F, 0xE1, 0x82, 0x5B, 0x10, 0xF7, 0x2D, 0x53, 0xC1, 0xDF, 0x8A, - 0xB6, 0x79, 0x87, 0x06, 0x06, 0x27, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8C, 0x24, 0x73, - 0xB7, 0x20, 0xC3, 0x2B, 0xA6, 0xF4, 0xB3, 0x4E, 0x00, 0x00, 0x00, 0x80, 0x54, 0xBC, 0x83, 0x9F, - 0x1B, 0x95, 0x91, 0xD1, 0x68, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0x35, - 0x15, 0x1D, 0xD1, 0xB9, 0x5F, 0x97, 0x7F, 0x63, 0xB0, 0x32, 0x97, 0x87, 0xB7, 0x6B, 0x6F, 0x3F, - 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8B, 0xA4, 0xE3, 0x16, 0x04, 0x00, 0x00, 0x00, 0xDD, 0xCE, 0xDB, - 0x7F, 0xD5, 0xB8, 0x0C, 0x0D, 0xB5, 0xBA, 0xB0, 0x48, 0x32, 0x15, 0x14, 0x69, 0x26, 0xE2, 0xA2, - 0xD5, 0x54, 0x70, 0xA8, 0x0D, 0x2D, 0x30, 0xDE, 0xC8, 0xCA, 0xF1, 0x3C, 0x6E, 0x41, 0xFC, 0xDB, - 0x8F, 0x03, 0xD7, 0xDA, 0x9F, 0x57, 0x44, 0x45, 0xD2, 0xA9, 0xA8, 0x48, 0x3B, 0xF7, 0x96, 0xAF, - 0xE1, 0x65, 0xDC, 0x82, 0x34, 0x60, 0xC6, 0xFE, 0xC3, 0x2C, 0xF3, 0xF2, 0xBF, 0x5F, 0x67, 0x4E, - 0x7C, 0x65, 0x7D, 0x64, 0x6B, 0xDD, 0xBF, 0x1E, 0xFC, 0x47, 0x00, 0x00, 0x00, 0x50, 0x14, 0x77, - 0xF0, 0x4E, 0xFC, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, - 0x0A, 0x8D, 0x46, 0x73, 0xBF, 0xC6, 0x1C, 0x6E, 0x41, 0xDC, 0xA7, 0xAB, 0xFB, 0xB7, 0x1F, 0xEE, - 0x59, 0x1A, 0x15, 0x16, 0x49, 0xA6, 0x62, 0x22, 0xCB, 0xB6, 0x7F, 0xD4, 0xFE, 0x7E, 0x96, 0xDB, - 0x2D, 0x2D, 0xF4, 0xB3, 0x4E, 0x7A, 0x8D, 0x0B, 0x0C, 0xEF, 0xC4, 0x97, 0x07, 0xCD, 0x2F, 0xBE, - 0x62, 0xE4, 0x4E, 0x7C, 0xC9, 0xF8, 0xFF, 0x3E, 0x21, 0x02, 0x00, 0x00, 0x50, 0x1C, 0xFF, 0x0D, - 0x58, 0xBB, 0xE6, 0xE8, 0xE0, 0x88, 0xAE, 0x2E, 0x2C, 0x92, 0x4C, 0x05, 0x45, 0x9A, 0x89, 0xB8, - 0x68, 0x35, 0x15, 0x19, 0xAD, 0xE6, 0xBF, 0xC2, 0x76, 0xE5, 0x78, 0x3B, 0xDF, 0xB6, 0xE4, 0x1D, - 0x1A, 0x18, 0x35, 0xFB, 0xAF, 0xB4, 0x3F, 0x9F, 0x08, 0x8B, 0x24, 0x53, 0x21, 0x91, 0x69, 0x33, - 0x2B, 0xDB, 0x7F, 0x85, 0xF1, 0x86, 0x97, 0x4F, 0xC9, 0x27, 0xBE, 0xF7, 0x02, 0x73, 0xFC, 0xDF, - 0x2F, 0xF1, 0x7E, 0xF1, 0xE5, 0xF1, 0xDA, 0xE0, 0xB8, 0xDD, 0x98, 0xE3, 0x5F, 0xF4, 0xC3, 0x23, - 0xFA, 0xAF, 0xFB, 0xFF, 0x39, 0xFB, 0x9F, 0x0F, 0xFE, 0xAB, 0x00, 0x00, 0x00, 0xC8, 0x93, 0x39, - 0x7C, 0xE3, 0xBA, 0x96, 0xAF, 0xE0, 0x95, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, - 0xAB, 0xA9, 0xB8, 0x48, 0xBA, 0xE1, 0xEB, 0x8D, 0xFB, 0x16, 0xAA, 0xE0, 0x6F, 0x4B, 0x2A, 0x36, - 0x5C, 0x06, 0xEC, 0xB2, 0xDF, 0x7E, 0xC8, 0x90, 0xC8, 0xB2, 0x20, 0x3E, 0xDC, 0xB8, 0x05, 0xA9, - 0xAA, 0x89, 0x0F, 0x17, 0x1D, 0x2F, 0xD9, 0xFF, 0xAD, 0x5E, 0xB0, 0xB1, 0xF8, 0xFC, 0x92, 0xC9, - 0xBD, 0x70, 0xB5, 0xFD, 0xD7, 0x57, 0x55, 0xFE, 0x7D, 0x22, 0x04, 0x00, 0x00, 0xA0, 0x38, 0xFE, - 0x2B, 0x78, 0x55, 0x74, 0x84, 0x93, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, - 0xA9, 0xA8, 0x48, 0xBC, 0x59, 0x95, 0x07, 0xC7, 0xDD, 0xB7, 0x50, 0x65, 0xB8, 0x05, 0xF1, 0x86, - 0x57, 0x64, 0xBF, 0xFD, 0x90, 0x21, 0x91, 0x65, 0x91, 0xF8, 0x08, 0xC7, 0x2D, 0x48, 0x6D, 0x7C, - 0x1C, 0xB9, 0xB1, 0x36, 0x3A, 0xD4, 0x5C, 0x98, 0x1C, 0xFD, 0x42, 0x25, 0x42, 0x4E, 0x7C, 0x85, - 0x1B, 0x24, 0x00, 0x00, 0x80, 0xBC, 0x79, 0x07, 0x97, 0x35, 0x7E, 0x03, 0x96, 0x8C, 0x8B, 0x56, - 0x53, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0x85, 0x1F, 0x16, 0x68, 0xE7, - 0x6E, 0x41, 0x46, 0x06, 0x86, 0x82, 0xBF, 0x35, 0x89, 0xB4, 0x75, 0xFB, 0x21, 0x43, 0x22, 0xCB, - 0x44, 0x7C, 0xB8, 0x6D, 0xFD, 0xD0, 0xB4, 0xBE, 0x05, 0xA9, 0x89, 0x0F, 0x77, 0xEB, 0xA1, 0x82, - 0xA3, 0xD1, 0x5C, 0xAC, 0xF8, 0x37, 0x26, 0x5F, 0x29, 0xE4, 0xD3, 0xF2, 0x01, 0x00, 0x00, 0xA6, - 0x2D, 0x6F, 0xFF, 0x35, 0x63, 0xC4, 0x47, 0xB0, 0xED, 0x1F, 0xB7, 0x87, 0x75, 0xFF, 0x16, 0x24, - 0xF1, 0x81, 0xDD, 0x1B, 0x59, 0x31, 0xE6, 0x3F, 0xC4, 0xAF, 0x02, 0xA3, 0xD9, 0x64, 0x48, 0x64, - 0x99, 0x08, 0x8F, 0xE8, 0xF6, 0x2C, 0xB2, 0x51, 0xB5, 0x7C, 0x2C, 0xF8, 0xE5, 0x4E, 0x1B, 0x6D, - 0xC5, 0x47, 0xB8, 0x63, 0x7F, 0xE2, 0x3F, 0x94, 0x1E, 0xFC, 0x94, 0x00, 0x00, 0x00, 0xC8, 0x83, - 0xD9, 0xF7, 0x19, 0xE2, 0xC3, 0xED, 0x99, 0x60, 0xFB, 0xAF, 0x32, 0xDE, 0xF0, 0x40, 0xA2, 0x6F, - 0xBD, 0xB1, 0xA1, 0xB2, 0xCE, 0xBF, 0xFD, 0x70, 0x7F, 0xCF, 0x54, 0x64, 0x34, 0x9A, 0x0C, 0x89, - 0x2C, 0x13, 0xC1, 0x11, 0x9F, 0xFB, 0x7B, 0xEB, 0xDF, 0x82, 0x2C, 0xCF, 0xF5, 0x13, 0xDF, 0xBB, - 0x59, 0x2E, 0xF1, 0xE1, 0xF6, 0xC2, 0xD5, 0xEE, 0xDB, 0xB0, 0x4E, 0x06, 0x3F, 0x2D, 0x00, 0x00, - 0x00, 0xDA, 0x65, 0x0E, 0xDF, 0xB8, 0xC4, 0x0C, 0x2D, 0x9C, 0xBE, 0xF1, 0x11, 0x46, 0x47, 0xF0, - 0x7F, 0x7B, 0x6E, 0xDB, 0x3F, 0xEE, 0xBE, 0x0D, 0x2B, 0xD1, 0x2D, 0x88, 0x37, 0xBC, 0xE2, 0x64, - 0xEA, 0xDB, 0x0F, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, 0xD1, 0xDC, 0x2D, 0xC8, 0xF0, 0xB2, 0x69, - 0x71, 0x90, 0xCE, 0x2D, 0x3E, 0xC2, 0x1D, 0xBB, 0xC5, 0x04, 0x3F, 0x35, 0x00, 0x00, 0x00, 0xDA, - 0x65, 0x0E, 0xAD, 0x5A, 0x5B, 0xF3, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, - 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0xAB, 0x8F, 0x0F, 0x3F, 0x3C, 0xC2, 0x6D, 0xB2, 0xDB, 0x7B, - 0x85, 0x39, 0x3F, 0x3C, 0xD0, 0xF4, 0xC0, 0x9E, 0xE9, 0xF6, 0x43, 0x86, 0x44, 0x96, 0x89, 0xC8, - 0x68, 0x36, 0xF7, 0xF7, 0x79, 0x1A, 0xDC, 0x82, 0xE4, 0x1E, 0x1F, 0xCF, 0x7F, 0xC6, 0x98, 0x23, - 0x37, 0x18, 0xF3, 0xF2, 0x9D, 0x7C, 0x9E, 0x0A, 0x00, 0x00, 0x40, 0x1E, 0xFC, 0x37, 0x60, 0x6D, - 0xFB, 0xC8, 0xF4, 0x8D, 0x8F, 0x68, 0x78, 0xB8, 0xB9, 0xF8, 0x70, 0xDB, 0xFC, 0x21, 0xF7, 0x6D, - 0x58, 0xEE, 0x16, 0xA4, 0xE1, 0x81, 0xDD, 0xBF, 0xFD, 0xD8, 0xB3, 0xD0, 0xFE, 0x7C, 0x22, 0x34, - 0xD4, 0x64, 0x48, 0x64, 0x99, 0x08, 0x8C, 0x24, 0x1B, 0x9A, 0x5F, 0xEA, 0x5B, 0x90, 0x42, 0xE2, - 0xC3, 0x6D, 0xEC, 0x3A, 0x17, 0x20, 0xD3, 0xE6, 0xDB, 0xD7, 0x00, 0x00, 0x00, 0x0A, 0xE5, 0x1D, - 0x5A, 0x3D, 0x38, 0xED, 0xE2, 0x23, 0x12, 0x20, 0xD5, 0xE8, 0x88, 0xC4, 0xC7, 0xF9, 0x70, 0x43, - 0x97, 0x9B, 0x73, 0x07, 0xF5, 0x2D, 0xC8, 0xF9, 0x91, 0xFE, 0xF5, 0x66, 0x78, 0x99, 0xFD, 0xB9, - 0xDC, 0xAF, 0x49, 0xC4, 0x46, 0x7C, 0x32, 0x24, 0xB2, 0x4C, 0x84, 0x45, 0xD2, 0xB9, 0xDF, 0xF7, - 0xF0, 0xF5, 0xEE, 0x16, 0xA4, 0xAD, 0x4F, 0x7C, 0xEF, 0x46, 0x85, 0xC5, 0x87, 0xDB, 0x0B, 0x57, - 0x19, 0x73, 0xE2, 0xCE, 0xD2, 0xFD, 0x3D, 0x03, 0x00, 0x00, 0xE8, 0x08, 0x6F, 0xDF, 0x55, 0xA3, - 0x3A, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xC5, 0xC2, 0x22, 0xC9, 0x64, 0x54, 0x24, 0x5D, 0x24, - 0x3E, 0x82, 0xD5, 0xDC, 0x78, 0x84, 0xD1, 0xF1, 0x74, 0x64, 0x9B, 0x3F, 0x64, 0xCE, 0x1F, 0x5C, - 0x6E, 0xCE, 0x0D, 0xF7, 0xDF, 0x15, 0xFC, 0xED, 0xF2, 0x99, 0xA1, 0xFE, 0x59, 0xE7, 0x47, 0x56, - 0x8C, 0x9B, 0xDD, 0x7D, 0xF6, 0x60, 0x2F, 0x62, 0x23, 0x3E, 0x19, 0x12, 0x59, 0x26, 0xA2, 0x22, - 0xED, 0x76, 0xCF, 0x35, 0xDE, 0xC8, 0xB2, 0x71, 0xF7, 0x7B, 0x08, 0x7E, 0x3B, 0x3D, 0xAF, 0xD0, - 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x9F, 0x07, 0x02, 0x00, 0x00, 0x90, 0x07, 0xB3, 0xEF, 0x0A, - 0x7B, 0xB8, 0x57, 0x81, 0xD1, 0x6C, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, 0x48, - 0xBA, 0x48, 0x78, 0x44, 0x6F, 0x3E, 0xE2, 0xB7, 0x1E, 0x7E, 0x78, 0xBC, 0xCF, 0x9C, 0x7B, 0x6A, - 0x72, 0xE7, 0x77, 0xCE, 0x77, 0xB7, 0x20, 0x35, 0x07, 0xF6, 0xF3, 0x07, 0xFB, 0xD7, 0x7B, 0xC3, - 0xCB, 0x8C, 0xE7, 0x6E, 0x3F, 0x5A, 0x05, 0x88, 0x0C, 0x89, 0x2C, 0x13, 0x31, 0x91, 0x76, 0x9B, - 0x66, 0xD8, 0x1F, 0xED, 0xAF, 0xA9, 0x44, 0xB7, 0x20, 0x53, 0x11, 0x1F, 0x6E, 0xDE, 0xD1, 0x5B, - 0xA6, 0xDD, 0x6B, 0x8C, 0x01, 0x00, 0x00, 0x72, 0x67, 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x8B, 0xED, - 0x01, 0x5F, 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x58, 0x24, 0x99, 0x8C, 0x8A, 0xA4, - 0x8B, 0xC4, 0x47, 0x83, 0x00, 0x09, 0xE3, 0xE3, 0x5C, 0x24, 0x3E, 0xCE, 0x6E, 0x0C, 0xF6, 0xD4, - 0xFB, 0xCD, 0xF9, 0x03, 0xCB, 0x8D, 0x8B, 0x0E, 0xFF, 0xEF, 0x99, 0x0D, 0x91, 0x73, 0xC3, 0x2B, - 0xC6, 0xBD, 0x5D, 0x7D, 0xF6, 0xBF, 0x7F, 0x91, 0x3F, 0xFF, 0x50, 0xAF, 0x42, 0x44, 0x86, 0x44, - 0x96, 0x89, 0x98, 0x48, 0x3B, 0x17, 0x1F, 0xE1, 0x4A, 0x72, 0x0B, 0x32, 0x55, 0xF1, 0xE1, 0xEF, - 0xE8, 0xE7, 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xBB, 0xCC, 0xC8, 0xEA, 0x01, 0xB3, 0xEB, 0x52, - 0x7B, 0xC8, 0x57, 0xA1, 0xA1, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, - 0x4B, 0x1F, 0x1F, 0xD5, 0xF0, 0x78, 0xD2, 0x6D, 0x96, 0xBF, 0x73, 0x3B, 0xE6, 0x9B, 0xB3, 0x07, - 0x2A, 0xB7, 0x20, 0x2E, 0x44, 0xCE, 0x1F, 0x5C, 0x66, 0xFF, 0xFB, 0x7F, 0xDC, 0x3C, 0x40, 0x64, - 0x48, 0x64, 0x99, 0x88, 0x89, 0xB4, 0x8B, 0xC6, 0x87, 0x9B, 0xFB, 0xB5, 0x1E, 0xBC, 0xB6, 0xA7, - 0x6F, 0x41, 0xA6, 0x34, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0xEC, 0x5F, 0x33, 0xF8, 0xCB, 0x03, 0x00, - 0x00, 0x20, 0x0B, 0xEF, 0xD0, 0xAA, 0xBB, 0xCC, 0xF6, 0x4F, 0xD8, 0x83, 0xBE, 0x8A, 0x8D, 0xF8, - 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, 0x74, 0xB1, 0xF8, 0x08, 0x02, 0x24, - 0x49, 0x7C, 0xBC, 0x6B, 0xE3, 0xE3, 0xDD, 0xC1, 0x59, 0x93, 0xDB, 0xF8, 0x7E, 0x73, 0x6E, 0xEF, - 0xF5, 0xE6, 0xDC, 0x81, 0x81, 0x51, 0xF7, 0xED, 0x58, 0xE7, 0xB7, 0x5F, 0x62, 0xFF, 0xBB, 0x36, - 0x3E, 0xDC, 0x54, 0x84, 0xC8, 0x90, 0xC8, 0x32, 0x11, 0x13, 0x69, 0x17, 0x8F, 0x8F, 0x70, 0x3B, - 0x3E, 0xE9, 0x02, 0xC4, 0xBD, 0xE5, 0x6B, 0x76, 0xF0, 0x8F, 0x43, 0xCF, 0x98, 0xF2, 0xF8, 0x70, - 0x1B, 0x5B, 0x66, 0xCC, 0x89, 0xAF, 0xAC, 0x0D, 0x7E, 0x09, 0x00, 0x00, 0x00, 0xC8, 0xC2, 0x3B, - 0xBC, 0x7A, 0x83, 0x8E, 0x8D, 0xF8, 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, - 0x74, 0x3A, 0x3E, 0xEA, 0x02, 0xC4, 0x86, 0x47, 0xF8, 0xCC, 0x47, 0x3C, 0x3E, 0x26, 0xDC, 0x9E, - 0xA8, 0xCC, 0xFD, 0xFF, 0x67, 0xB7, 0x7C, 0xDA, 0x4C, 0x3C, 0xD3, 0x6F, 0xCE, 0x0D, 0xD9, 0x10, - 0x79, 0xCA, 0xFD, 0x77, 0x2F, 0xD2, 0x11, 0xB2, 0x49, 0x85, 0x44, 0x96, 0x89, 0x98, 0x48, 0x3B, - 0x15, 0x1E, 0xD1, 0x1D, 0xBC, 0xD6, 0xBD, 0x96, 0x77, 0x30, 0xF8, 0xC7, 0xA1, 0x27, 0x74, 0x24, - 0x3E, 0xDC, 0x5E, 0xBC, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0xB4, 0xCB, 0x3B, 0x78, 0x4D, 0x82, - 0x37, 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0xE1, 0x11, - 0x2E, 0x1E, 0x1F, 0xE1, 0xED, 0x47, 0xE4, 0x99, 0x0F, 0x17, 0x1F, 0xD1, 0xF0, 0x98, 0x78, 0x3C, - 0x98, 0xFB, 0xBF, 0x1F, 0x9B, 0x65, 0x4E, 0x3F, 0x72, 0x9D, 0x39, 0xF3, 0xF0, 0x25, 0x7E, 0x80, - 0x9C, 0xF3, 0x1F, 0x58, 0x8F, 0x05, 0x88, 0x8B, 0x8F, 0x5C, 0x02, 0x44, 0xC4, 0x44, 0xDA, 0xA9, - 0xE0, 0x88, 0xAF, 0xC7, 0x6E, 0x41, 0x3A, 0x16, 0x1F, 0xC1, 0xBC, 0x5F, 0xDC, 0x39, 0x14, 0xFC, - 0x52, 0x00, 0x00, 0x00, 0x90, 0x85, 0xB7, 0xFF, 0xEA, 0x93, 0x3A, 0x3A, 0xC2, 0xA9, 0xA0, 0x48, - 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x78, 0xB8, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, - 0xDE, 0x7E, 0xD4, 0x7E, 0xEB, 0x55, 0x34, 0x3E, 0xCE, 0xD8, 0xE8, 0x38, 0xF3, 0xF8, 0xFB, 0x82, - 0x55, 0x22, 0xE4, 0xF4, 0xFD, 0x17, 0x99, 0x53, 0xF7, 0x5E, 0x6C, 0x4E, 0xDD, 0x7D, 0x91, 0x39, - 0x6B, 0xFF, 0x33, 0x75, 0xB7, 0x20, 0x36, 0x3C, 0xDC, 0xDA, 0x0F, 0x10, 0x11, 0x13, 0x69, 0xA7, - 0x62, 0xA3, 0xD1, 0x7A, 0xE4, 0x16, 0xA4, 0xD3, 0xF1, 0xE1, 0xE6, 0x1D, 0xFB, 0x53, 0xDE, 0x84, - 0x05, 0x00, 0x00, 0xD0, 0x8E, 0xE6, 0x6F, 0xC0, 0x52, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, - 0x46, 0x45, 0xD2, 0xC5, 0xA2, 0x23, 0xBA, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDC, 0x7E, 0xD4, 0x7C, - 0xDB, 0x55, 0x18, 0x1F, 0x8F, 0xD9, 0xF0, 0xB0, 0x3B, 0x1D, 0xFC, 0x78, 0xE6, 0x91, 0xF7, 0xF9, - 0xE1, 0x11, 0xEE, 0xF4, 0x7D, 0x36, 0x42, 0x36, 0x4E, 0x46, 0x88, 0xF7, 0x74, 0x25, 0x3E, 0xAA, - 0x01, 0x92, 0x39, 0x42, 0x44, 0x4C, 0xA4, 0x9D, 0x8A, 0x8C, 0x66, 0xDB, 0x7E, 0x71, 0xD7, 0xDF, - 0x82, 0x74, 0x43, 0x7C, 0xF8, 0x3B, 0x76, 0x0B, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xC8, 0xCA, 0x7F, - 0x03, 0xD6, 0xEE, 0x79, 0xF6, 0xE0, 0x5F, 0xFE, 0xF8, 0x70, 0x3F, 0x36, 0xBF, 0xFD, 0xB0, 0xF1, - 0xE1, 0x02, 0x24, 0x88, 0x0F, 0x17, 0x1E, 0xA7, 0x1F, 0x9D, 0xDC, 0xA9, 0x7B, 0x67, 0xD5, 0x04, - 0x88, 0xDB, 0xBB, 0x3F, 0x0E, 0x02, 0xE4, 0xA9, 0x0B, 0xED, 0xCF, 0x67, 0xE3, 0x23, 0x1E, 0x21, - 0x32, 0x30, 0x9A, 0x4D, 0xC4, 0x44, 0xDA, 0xA9, 0xC0, 0x48, 0xB2, 0x7D, 0x57, 0x18, 0x6F, 0x78, - 0x79, 0x57, 0x7E, 0xD0, 0x5E, 0xD7, 0xC4, 0x87, 0x9B, 0x7B, 0x13, 0xD6, 0xCB, 0x77, 0xF6, 0xDC, - 0x43, 0xFB, 0x00, 0x00, 0x00, 0x5D, 0xC1, 0x1C, 0x5A, 0xB5, 0xD6, 0xEC, 0x9C, 0x6D, 0x0F, 0xFF, - 0x25, 0x8E, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDD, 0x7E, 0xB8, 0xD7, 0xEC, 0x46, 0x6F, - 0x3F, 0xE2, 0xF1, 0x71, 0xFA, 0x81, 0xDA, 0xDB, 0x8F, 0x70, 0xEE, 0x16, 0xE4, 0xDC, 0x93, 0x76, - 0x1B, 0x2F, 0xF2, 0x23, 0xA4, 0x26, 0x40, 0x5C, 0x50, 0xA4, 0x8A, 0x10, 0x11, 0x13, 0x69, 0xA7, - 0xC2, 0x22, 0xE9, 0xB6, 0x7D, 0x24, 0xB8, 0x05, 0x59, 0x3A, 0x10, 0xFC, 0xE3, 0xD1, 0x15, 0xBA, - 0x2A, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0xB3, 0xAB, 0xFE, 0x1E, 0x01, 0x00, 0x00, 0xF4, - 0x0C, 0x73, 0x78, 0xF5, 0x7A, 0xB3, 0xF5, 0xC3, 0x36, 0x00, 0x4A, 0x1C, 0x1F, 0xC1, 0xD4, 0xED, - 0xC7, 0x59, 0x3F, 0x40, 0x6C, 0x7C, 0xB8, 0xDB, 0x8F, 0xC8, 0xB7, 0x5E, 0x55, 0xC3, 0xE3, 0xC7, - 0x95, 0xA9, 0xDB, 0x0F, 0x7F, 0x5F, 0xBF, 0xD0, 0xBC, 0xFB, 0x48, 0x25, 0x40, 0xCE, 0xB5, 0x75, - 0x0B, 0x22, 0x62, 0x22, 0xED, 0x54, 0x54, 0xA4, 0x9D, 0xBB, 0x05, 0x39, 0xB8, 0xAC, 0x6B, 0x6E, - 0x41, 0xBA, 0x2E, 0x3E, 0xDC, 0x78, 0x13, 0x16, 0x00, 0x00, 0x40, 0x76, 0xDE, 0xC8, 0x8A, 0xA1, - 0xD2, 0xC7, 0x47, 0xC3, 0xDB, 0x8F, 0x59, 0xD5, 0xF8, 0xA8, 0xDE, 0x7E, 0x84, 0xF1, 0x11, 0x84, - 0x87, 0x1F, 0x1F, 0x3F, 0xD2, 0xB7, 0x1F, 0x2E, 0x3E, 0xDC, 0x4E, 0x7F, 0xEB, 0x42, 0x73, 0xB6, - 0xAD, 0x5B, 0x10, 0x11, 0x13, 0x69, 0xA7, 0x62, 0x22, 0xCB, 0xB6, 0xD9, 0x18, 0x1D, 0x5E, 0xD6, - 0x15, 0xB7, 0x20, 0x5D, 0x19, 0x1F, 0xC1, 0xBC, 0x13, 0x5F, 0xEE, 0xA9, 0xD7, 0x16, 0x03, 0x00, - 0x00, 0x74, 0x0D, 0x6F, 0xDF, 0x55, 0x63, 0x65, 0xBF, 0xF9, 0x70, 0xAB, 0xBF, 0xFD, 0x88, 0x04, - 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x70, 0x44, 0x02, 0xE4, 0x94, 0x7B, 0xE8, 0xDC, 0xED, 0xDB, 0xE2, - 0xF6, 0x23, 0x88, 0x8F, 0x70, 0x13, 0x0F, 0x64, 0xBD, 0x05, 0x11, 0x31, 0x91, 0x76, 0x2A, 0x24, - 0x32, 0xED, 0x82, 0xCA, 0xF6, 0x5C, 0xD6, 0xF1, 0x5B, 0x90, 0x6E, 0x8E, 0x0F, 0x37, 0xEF, 0xF8, - 0x9F, 0x75, 0xE5, 0xB3, 0x32, 0x00, 0x00, 0x00, 0x5D, 0xCF, 0xEC, 0xBD, 0xDC, 0x86, 0x40, 0x89, - 0xE3, 0x23, 0xC1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xC1, 0xB7, 0x5E, 0x9D, 0x0A, 0x03, 0xE4, 0x07, - 0xAD, 0xE3, 0xC3, 0xED, 0xF4, 0x3D, 0x2D, 0x6E, 0x41, 0xE4, 0x44, 0x4C, 0xA4, 0x9D, 0x0C, 0x89, - 0x2C, 0x0B, 0xE2, 0xC3, 0xCD, 0xFD, 0xFD, 0xF5, 0x6F, 0x41, 0x96, 0xAF, 0x0B, 0xFE, 0x31, 0x99, - 0x52, 0xDD, 0x1E, 0x1F, 0xFE, 0x8E, 0xFF, 0x29, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x48, 0xCB, 0xBD, - 0x72, 0xD5, 0xEC, 0x59, 0x64, 0x63, 0x40, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0xBA, 0xA0, - 0x48, 0x33, 0x11, 0x1A, 0x8D, 0x16, 0x0F, 0x90, 0xE0, 0xF6, 0xE3, 0x6C, 0x92, 0xDB, 0x8F, 0x87, - 0xED, 0xEE, 0x69, 0x1D, 0x1F, 0xE1, 0xC2, 0x5B, 0x90, 0x64, 0x6F, 0xC4, 0x12, 0x31, 0x91, 0x76, - 0x32, 0x24, 0xB2, 0x2C, 0x12, 0x1F, 0xE1, 0x86, 0x16, 0x1A, 0xEF, 0xE0, 0xD2, 0x93, 0xC1, 0x3F, - 0x2A, 0x53, 0xA6, 0x27, 0xE2, 0xC3, 0xED, 0xC8, 0x6A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0x90, 0x96, - 0xFF, 0x0A, 0xDE, 0x9D, 0x9F, 0xB2, 0x41, 0xA0, 0xA2, 0x22, 0xE9, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, - 0x8A, 0xA4, 0x13, 0x91, 0xA1, 0x66, 0x83, 0xA3, 0x1A, 0x1F, 0x36, 0x3C, 0x12, 0xDF, 0x7E, 0x44, - 0xBF, 0xF5, 0xEA, 0xFB, 0xB1, 0xDB, 0x0F, 0x11, 0x1D, 0xD1, 0x25, 0xBF, 0x05, 0x11, 0x31, 0x91, - 0x76, 0x32, 0x24, 0xB2, 0x4C, 0xC4, 0x87, 0x9B, 0xFB, 0x7B, 0x3D, 0xC5, 0xB7, 0x20, 0x3D, 0x13, - 0x1F, 0x3F, 0xBF, 0xDC, 0x98, 0xB1, 0x15, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x20, 0x2D, 0x73, 0x78, - 0xE5, 0x7A, 0xB3, 0xED, 0xA3, 0x36, 0x0A, 0x54, 0x58, 0x24, 0x99, 0x88, 0x8B, 0x56, 0xAB, 0x0B, - 0x8A, 0x34, 0x8B, 0x45, 0x46, 0xB3, 0x45, 0x03, 0xC4, 0xAE, 0x12, 0x1F, 0x36, 0x0E, 0xC2, 0x00, - 0x69, 0xF4, 0xDA, 0xDD, 0x46, 0xB7, 0x1F, 0x22, 0x38, 0xD4, 0x26, 0x7E, 0x50, 0x09, 0x90, 0x9A, - 0x67, 0x41, 0x82, 0x10, 0xA9, 0xDC, 0x82, 0x88, 0x98, 0x48, 0x3B, 0x19, 0x12, 0x59, 0x26, 0xC2, - 0x23, 0xBA, 0x29, 0xBC, 0x05, 0xE9, 0xA9, 0xF8, 0x70, 0x7B, 0xF1, 0x3A, 0x63, 0x7F, 0xBD, 0x77, - 0x05, 0xBF, 0x7C, 0x00, 0x00, 0x00, 0x24, 0xE1, 0x1D, 0x5E, 0xB9, 0x41, 0x87, 0x45, 0x92, 0x89, - 0xB8, 0x68, 0x35, 0x19, 0x15, 0x49, 0x17, 0x0B, 0x8C, 0x66, 0x8B, 0xC5, 0x47, 0xE5, 0xF6, 0x23, - 0x12, 0x1F, 0xCD, 0x6E, 0x3F, 0xC2, 0x00, 0x89, 0xDE, 0x7E, 0x88, 0xD0, 0x68, 0xB8, 0x6F, 0x5C, - 0x68, 0xCE, 0x3E, 0xD1, 0xE0, 0x16, 0x64, 0x93, 0x88, 0x89, 0xB4, 0x93, 0x21, 0x91, 0x65, 0x22, - 0x38, 0xE2, 0x73, 0x7F, 0xDF, 0x0F, 0x5E, 0x57, 0xF8, 0x2D, 0x48, 0xCF, 0xC5, 0x47, 0x30, 0xEF, - 0xC4, 0x9D, 0x1B, 0x82, 0xDF, 0x02, 0x00, 0x00, 0x00, 0x92, 0xF0, 0x0E, 0x5E, 0x3D, 0xAA, 0xE3, - 0xA2, 0xD5, 0x44, 0x5C, 0xB4, 0x5A, 0x5D, 0x50, 0xA4, 0x59, 0x2C, 0x30, 0x9A, 0xAD, 0x41, 0x7C, - 0xF8, 0xB7, 0x1F, 0x2E, 0x3E, 0xD4, 0xED, 0x47, 0x10, 0x1F, 0xD5, 0xDB, 0x8F, 0x87, 0xEC, 0xBE, - 0x91, 0x21, 0x3E, 0x82, 0x85, 0xB7, 0x20, 0xE7, 0x9F, 0x72, 0x7F, 0xDD, 0x20, 0x42, 0x9E, 0x9E, - 0x69, 0x7F, 0x2D, 0x2E, 0x20, 0x82, 0xA9, 0xB8, 0x68, 0x35, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, - 0xD1, 0x76, 0xCD, 0x31, 0xDE, 0xF0, 0xD2, 0x71, 0x33, 0xD4, 0x3F, 0x2B, 0xF8, 0xC7, 0x26, 0x57, - 0xBD, 0x1A, 0x1F, 0x7E, 0x80, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x74, 0xBC, 0xFD, 0x9F, 0x1D, - 0xD7, 0x81, 0xD1, 0x6C, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xC5, 0x02, 0xA3, 0xD5, 0xEA, - 0x02, 0x24, 0x88, 0x8F, 0xF8, 0xED, 0x47, 0x10, 0x1F, 0xF2, 0xD9, 0x8F, 0xFB, 0x83, 0xDB, 0x0F, - 0x11, 0x17, 0x89, 0x16, 0xBB, 0x05, 0x39, 0x1F, 0xC4, 0x47, 0x5B, 0x01, 0x22, 0x43, 0x22, 0xCB, - 0x44, 0x64, 0x34, 0x9B, 0x7B, 0x66, 0xC5, 0xDD, 0x82, 0x1C, 0x5C, 0x9A, 0xFB, 0x07, 0xEF, 0xF5, - 0x72, 0x7C, 0xB8, 0x79, 0xC7, 0xFE, 0x64, 0x3C, 0xF8, 0xAD, 0x00, 0x00, 0x00, 0xA0, 0x15, 0x33, - 0xDA, 0x3F, 0x2B, 0xFD, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x8B, 0xC5, 0x45, - 0x92, 0xC5, 0xE3, 0x23, 0x7A, 0xFB, 0x11, 0xC4, 0x47, 0xCD, 0x9B, 0xAF, 0x1A, 0xDD, 0x7E, 0xA8, - 0xB0, 0x48, 0xB1, 0x33, 0xDF, 0xBB, 0x70, 0xF2, 0x59, 0x90, 0xA7, 0x6C, 0x7C, 0xB4, 0x13, 0x21, - 0x32, 0x24, 0xB2, 0x4C, 0x04, 0x46, 0x92, 0x15, 0x70, 0x0B, 0xD2, 0xEB, 0xF1, 0xE1, 0xEF, 0xC8, - 0x4D, 0xC6, 0x8C, 0xFD, 0x87, 0x42, 0x6E, 0x86, 0x00, 0x00, 0x00, 0x4A, 0xC7, 0x7F, 0x03, 0xD6, - 0xAE, 0x4F, 0xDB, 0x48, 0x50, 0xA1, 0xA1, 0x26, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, - 0x8B, 0x56, 0xB3, 0xD1, 0x31, 0x19, 0x20, 0x17, 0x55, 0x02, 0x24, 0xB8, 0xFD, 0xA8, 0xBE, 0x76, - 0xB7, 0x7A, 0xFB, 0xF1, 0xBE, 0xC6, 0xB7, 0x1F, 0x22, 0x28, 0xB2, 0xEC, 0xEC, 0x63, 0x2E, 0x42, - 0x2A, 0x01, 0xE2, 0xDF, 0x82, 0x64, 0x89, 0x10, 0x19, 0x12, 0x59, 0x26, 0xC2, 0x22, 0xE9, 0x72, - 0xBE, 0x05, 0x29, 0x45, 0x7C, 0xB8, 0xBD, 0x34, 0xC0, 0x9B, 0xB0, 0x00, 0x00, 0x00, 0x92, 0x32, - 0x87, 0x57, 0xAD, 0x4B, 0xFE, 0x0A, 0x5E, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x61, - 0x91, 0x74, 0xF1, 0xF8, 0xC8, 0x74, 0xFB, 0xA1, 0x63, 0x22, 0xFD, 0x66, 0x9A, 0x33, 0xF7, 0xCF, - 0x34, 0x67, 0x9F, 0x9C, 0x8C, 0x90, 0xD4, 0x01, 0x22, 0x43, 0x22, 0xCB, 0x44, 0x54, 0xA4, 0xDD, - 0xCE, 0xD9, 0xC6, 0x1B, 0xBE, 0xBE, 0xED, 0x5B, 0x90, 0xD2, 0xC4, 0x87, 0x1B, 0x6F, 0xC2, 0x02, - 0x00, 0x00, 0x48, 0xCE, 0xB8, 0x57, 0xF0, 0xCA, 0xD8, 0x88, 0x4F, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, - 0x48, 0x3A, 0x11, 0x16, 0x49, 0xE6, 0x6E, 0x3F, 0xEC, 0x8F, 0x7E, 0x7C, 0xC4, 0x6E, 0x3F, 0xEA, - 0x9F, 0xFD, 0x68, 0x70, 0xFB, 0x71, 0x5F, 0xFB, 0xDF, 0x7A, 0x55, 0xD9, 0xCC, 0xEA, 0xDE, 0x0D, - 0x6E, 0x41, 0xCE, 0x35, 0xBA, 0x05, 0x51, 0xE1, 0xE1, 0x26, 0x43, 0x22, 0xCB, 0x44, 0x4C, 0x64, - 0xDA, 0x7B, 0x8D, 0x39, 0x70, 0xB5, 0x7B, 0x2D, 0xEF, 0x60, 0xF0, 0x8F, 0x50, 0x6A, 0xA5, 0x8A, - 0x0F, 0xB7, 0xE7, 0xAF, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x49, 0x79, 0x07, 0xAF, 0x4B, 0xF0, - 0x06, 0x2C, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x51, 0x91, 0x66, 0xE1, 0xCD, 0x47, - 0x83, 0xDB, 0x8F, 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0x78, 0x30, 0xAF, 0x6F, 0xBD, 0x9A, 0x8C, 0x0F, - 0xB7, 0x4C, 0xB7, 0x20, 0x32, 0x24, 0xB2, 0x4C, 0x85, 0x44, 0x96, 0xD9, 0xF8, 0x70, 0xDB, 0xFE, - 0x71, 0xF7, 0x6D, 0x58, 0xC6, 0x8C, 0x5C, 0x9B, 0xFA, 0x13, 0xC0, 0x4B, 0x17, 0x1F, 0xC1, 0xBC, - 0x13, 0x77, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x24, 0xBC, 0x7D, 0x57, 0x8E, 0xE9, 0xE8, 0x08, - 0x27, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, 0x8A, 0xA4, 0xF3, 0x6F, 0x3F, 0x82, 0xF8, - 0x88, 0xDD, 0x7E, 0xA8, 0x67, 0x3F, 0xEA, 0x3F, 0x74, 0xD0, 0xC6, 0x47, 0x2E, 0xB7, 0x1F, 0xB5, - 0xF1, 0x11, 0xEE, 0xDD, 0x47, 0x5A, 0x3C, 0x0B, 0xD2, 0x0B, 0xF1, 0xF1, 0x74, 0xB0, 0xFD, 0xE9, - 0x6F, 0x41, 0xCA, 0x1A, 0x1F, 0x6E, 0xDE, 0xB1, 0x5B, 0xA6, 0xE4, 0x83, 0x1A, 0x01, 0x00, 0x00, - 0x7A, 0x9E, 0xD9, 0x7B, 0x99, 0x8D, 0x06, 0x15, 0x1E, 0x6E, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, - 0xD2, 0xC5, 0x82, 0x22, 0xED, 0xC2, 0xF0, 0x68, 0x70, 0xFB, 0x51, 0xF7, 0xEC, 0x47, 0x10, 0x20, - 0x95, 0xDB, 0x0F, 0x1B, 0x1F, 0xB9, 0xDC, 0x7E, 0xE8, 0xF8, 0x70, 0x3B, 0x7D, 0xEF, 0x4C, 0x73, - 0xAE, 0xD1, 0x2D, 0x88, 0x1F, 0x1D, 0x3D, 0x12, 0x1F, 0xF6, 0xFF, 0xF6, 0xB6, 0x7F, 0xDC, 0x05, - 0x48, 0xE2, 0x5B, 0x90, 0x32, 0xC7, 0x87, 0xBF, 0x23, 0x37, 0x99, 0xE0, 0xB7, 0x0A, 0x00, 0x00, - 0x80, 0x46, 0xCC, 0xE1, 0xFE, 0x25, 0x66, 0x68, 0xBE, 0x0D, 0x87, 0x72, 0xC4, 0x47, 0xCB, 0xDB, - 0x0F, 0x11, 0x1F, 0x95, 0xDB, 0x0F, 0x1B, 0x1E, 0xB9, 0xDC, 0x7E, 0xE8, 0xF0, 0x88, 0x2E, 0xBC, - 0x05, 0x69, 0xF8, 0x2C, 0x88, 0x0C, 0x89, 0x2C, 0x53, 0x21, 0x91, 0x65, 0x22, 0x3E, 0xEC, 0x8F, - 0xFE, 0xF6, 0x7C, 0xC6, 0x9C, 0x3F, 0x70, 0xFD, 0x50, 0xF0, 0x8F, 0x53, 0x43, 0xA5, 0x8F, 0x0F, - 0x37, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x9A, 0xFF, 0x0A, 0x5E, 0xF9, 0x06, 0x2C, 0x11, 0x17, - 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x50, 0xA4, 0x9A, 0x8D, 0x8F, 0x30, 0x3C, 0xC2, 0xF8, 0x48, - 0x7A, 0xFB, 0xF1, 0x63, 0x1B, 0x1E, 0x2E, 0x40, 0x7E, 0xD8, 0xEE, 0xED, 0x87, 0x0E, 0x8E, 0xF8, - 0xDC, 0x2D, 0x48, 0xE3, 0x67, 0x41, 0x54, 0x48, 0x64, 0x99, 0x0A, 0x89, 0x2C, 0x6B, 0x12, 0x1F, - 0x4F, 0xBD, 0xD7, 0x9C, 0xDF, 0xF2, 0x21, 0x73, 0x7E, 0xFF, 0x32, 0x73, 0x76, 0xFF, 0xD2, 0x86, - 0x07, 0xEF, 0x69, 0x11, 0x1F, 0x6E, 0x2F, 0x5E, 0xE7, 0x02, 0x64, 0x5D, 0xF0, 0xDB, 0x06, 0x00, - 0x00, 0x80, 0xE2, 0xBF, 0x01, 0x6B, 0xEB, 0x87, 0x6C, 0x40, 0xF4, 0x7E, 0x7C, 0x64, 0xB9, 0xFD, - 0xA8, 0xC6, 0x87, 0xBB, 0xFD, 0xF8, 0x56, 0x3B, 0xB7, 0x1F, 0x3A, 0x36, 0x1A, 0x6D, 0xE2, 0x21, - 0x75, 0x0B, 0x32, 0xC3, 0x9F, 0x0E, 0x8A, 0x34, 0x53, 0x21, 0x91, 0x65, 0x2D, 0xE2, 0xC3, 0x6D, - 0xE3, 0x05, 0xE6, 0xFC, 0xEE, 0x25, 0xE6, 0xDC, 0xDE, 0x65, 0xF2, 0x01, 0xEC, 0x69, 0x13, 0x1F, - 0x6E, 0xCF, 0x5F, 0x69, 0xCC, 0x89, 0x3B, 0x73, 0xFF, 0x94, 0x78, 0x00, 0x00, 0x80, 0x52, 0xF1, - 0x0E, 0xAD, 0x1C, 0xEC, 0xF9, 0xF8, 0xC8, 0x78, 0xFB, 0x71, 0xEA, 0x51, 0x1B, 0x1D, 0xB9, 0xDC, - 0x7E, 0xE8, 0xC8, 0x68, 0xB6, 0xD3, 0xF7, 0xC4, 0x6F, 0x41, 0x2A, 0xF1, 0xD1, 0x7E, 0x80, 0xA8, - 0x90, 0xC8, 0xB2, 0x64, 0xF1, 0x71, 0xEE, 0x49, 0xBB, 0x4D, 0x1F, 0x76, 0x01, 0x62, 0xCE, 0x0E, - 0xD5, 0xDE, 0x82, 0x4C, 0xAB, 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x6F, 0xC2, 0x02, 0x00, 0x00, - 0x68, 0xC6, 0xDB, 0x77, 0x45, 0xE4, 0x15, 0xBC, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xA9, - 0xA0, 0x48, 0xB3, 0x20, 0x3E, 0x82, 0xA5, 0xBB, 0xFD, 0x88, 0xC4, 0x47, 0x5B, 0xB7, 0x1F, 0x3A, - 0x30, 0x92, 0x6C, 0xE2, 0x81, 0xE8, 0x2D, 0x88, 0x8D, 0x8F, 0xB6, 0x23, 0x44, 0x85, 0x44, 0x96, - 0x25, 0x8F, 0x8F, 0xB3, 0x83, 0x76, 0x4F, 0x5C, 0x60, 0xDE, 0xDD, 0xB1, 0xC4, 0x4C, 0xEC, 0x5E, - 0x5E, 0x3D, 0x7C, 0x4F, 0xC7, 0xF8, 0x70, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x2D, 0x98, 0x7D, - 0xF6, 0x70, 0xD7, 0xEB, 0xF1, 0x91, 0xE6, 0xF6, 0x23, 0x8C, 0x8F, 0x5C, 0x6E, 0x3F, 0x74, 0x58, - 0x24, 0x5D, 0xE5, 0x16, 0x64, 0xA6, 0x8D, 0x10, 0x1B, 0x20, 0x4F, 0xB5, 0x1B, 0x20, 0x2A, 0x24, - 0xB2, 0x2C, 0x43, 0x7C, 0x3C, 0x7E, 0x81, 0x39, 0x33, 0xF8, 0x41, 0x17, 0x20, 0xE6, 0xF4, 0x8E, - 0xA5, 0x03, 0xD3, 0x35, 0x3E, 0xFC, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x00, 0x34, 0x62, - 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x0B, 0x75, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, - 0x66, 0x91, 0xF8, 0x08, 0xA6, 0x6E, 0x3F, 0xAA, 0x01, 0x52, 0x73, 0xFB, 0x31, 0xAB, 0xFE, 0xF6, - 0xE3, 0x9B, 0x2A, 0x2E, 0x5A, 0x4D, 0x47, 0x45, 0x9A, 0xBD, 0xF3, 0xD7, 0x33, 0xCC, 0x99, 0x1F, - 0x4E, 0x06, 0x48, 0xF6, 0x5B, 0x10, 0x15, 0x12, 0x59, 0x96, 0x31, 0x3E, 0x1E, 0x9D, 0x61, 0x4E, - 0xFF, 0xD8, 0xEE, 0x99, 0x45, 0x66, 0xE2, 0xC0, 0xAA, 0x93, 0xD3, 0x36, 0x3E, 0xDC, 0x8E, 0xAC, - 0x32, 0xE6, 0xF8, 0x1D, 0x4B, 0x82, 0x2F, 0x31, 0x00, 0x00, 0x00, 0x44, 0x99, 0x91, 0xFE, 0x01, - 0xB3, 0xF3, 0x12, 0x1D, 0x18, 0xCD, 0x26, 0xA3, 0x22, 0xE9, 0x54, 0x50, 0xA4, 0x59, 0x2C, 0x3E, - 0x12, 0xDC, 0x7E, 0x4C, 0xB8, 0x00, 0x79, 0xDC, 0xDD, 0x7E, 0xC4, 0xE2, 0xC3, 0xED, 0x7B, 0x59, - 0xBE, 0xF5, 0x4A, 0x07, 0x45, 0x9A, 0xB9, 0xF8, 0xF0, 0x77, 0xF7, 0x0C, 0x7B, 0x90, 0x6F, 0xE7, - 0x16, 0x44, 0x85, 0x44, 0x96, 0xB5, 0x17, 0x1F, 0xA7, 0x1E, 0x76, 0x01, 0x32, 0xDB, 0x98, 0x63, - 0x77, 0x9A, 0x69, 0x1B, 0x1F, 0x3F, 0xBF, 0xCC, 0x98, 0x17, 0x3F, 0x67, 0xCC, 0x89, 0x2F, 0xAD, - 0x0D, 0xBE, 0xC4, 0x00, 0x00, 0x00, 0x10, 0xE5, 0x1D, 0x5A, 0x75, 0x97, 0xD9, 0x7E, 0xB1, 0x8E, - 0x8C, 0x46, 0x93, 0x51, 0x91, 0x74, 0x2A, 0x28, 0xD2, 0x2C, 0x16, 0x1F, 0x2A, 0x40, 0x1A, 0x3E, - 0xFB, 0x61, 0xE3, 0x23, 0x97, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0x56, 0x8D, 0x8F, 0x60, 0x67, 0xBE, - 0x5F, 0x09, 0x90, 0x73, 0xD1, 0x5B, 0x90, 0x20, 0x44, 0x74, 0x74, 0x84, 0x53, 0x21, 0x91, 0x65, - 0x39, 0xC4, 0xC7, 0xD3, 0x1F, 0x33, 0xDE, 0xD1, 0xDB, 0xA7, 0x77, 0x7C, 0xB8, 0x3D, 0xFF, 0x59, - 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x88, 0x19, 0x59, 0xB9, 0x5E, 0x46, 0x46, 0xA3, 0xC9, 0xA8, - 0x48, 0x3A, 0x15, 0x14, 0x69, 0x56, 0x1F, 0x1E, 0xD5, 0xF8, 0xB0, 0xE1, 0x31, 0x79, 0xFB, 0x11, - 0xF9, 0xD6, 0xAB, 0xEA, 0xED, 0xC7, 0x45, 0x95, 0xDB, 0x0F, 0xBB, 0xF6, 0x6E, 0x3F, 0x74, 0x50, - 0xA4, 0x59, 0x3C, 0x3E, 0xFC, 0x65, 0xBA, 0x05, 0x51, 0x21, 0x91, 0x65, 0xC4, 0x47, 0xFB, 0x0B, - 0xE2, 0x23, 0x98, 0xF7, 0x8B, 0x3B, 0x5B, 0x7E, 0x30, 0x23, 0x00, 0x00, 0xC0, 0xB4, 0xE4, 0x1D, - 0xB8, 0x7A, 0x54, 0x86, 0x86, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, 0x66, 0xB1, 0xF8, 0x88, - 0x07, 0x88, 0x5D, 0xF5, 0x5B, 0xAF, 0xC2, 0x00, 0xA9, 0x3E, 0x78, 0x6E, 0xE3, 0xC3, 0x06, 0x48, - 0xFB, 0xB7, 0x1F, 0x3A, 0x28, 0xD2, 0x4C, 0xC6, 0x47, 0xB0, 0xF0, 0x16, 0xC4, 0xBD, 0x92, 0xB7, - 0xF5, 0x2D, 0x88, 0x0A, 0x89, 0x2C, 0x23, 0x3E, 0xDA, 0x5F, 0x6D, 0x7C, 0xF8, 0x01, 0x72, 0xF4, - 0xD6, 0xB1, 0xE0, 0x4B, 0x0C, 0x00, 0x00, 0x00, 0x51, 0xDE, 0xFE, 0x2B, 0x4F, 0xCA, 0xD8, 0x88, - 0x4F, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x78, 0x88, 0xF8, 0x08, 0x6F, 0x3F, 0x6A, - 0x1E, 0x3C, 0xF7, 0x6F, 0x3F, 0x2E, 0x9A, 0xBC, 0xFD, 0x08, 0xE3, 0x23, 0x0C, 0x90, 0xFB, 0xD3, - 0xDC, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x1D, 0x35, 0x73, 0xB7, 0x20, 0x8F, 0x4D, 0xDE, 0x82, - 0xF8, 0x11, 0x62, 0x63, 0xA3, 0x3E, 0x40, 0x54, 0x48, 0x64, 0x19, 0xF1, 0xD1, 0xFE, 0xEA, 0xE3, - 0xC3, 0xDF, 0xB1, 0x2F, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x00, 0x25, 0xD1, 0x1B, 0xB0, 0x64, 0x54, - 0x24, 0x9D, 0x0A, 0x8A, 0x34, 0x8B, 0x85, 0x47, 0x93, 0xF8, 0xA8, 0x3E, 0x78, 0x5E, 0xBD, 0xFD, - 0xB0, 0xF1, 0x11, 0xDE, 0x7E, 0xC4, 0xE3, 0xE3, 0x21, 0xBB, 0xBB, 0x55, 0x68, 0xA8, 0xE9, 0xA0, - 0x48, 0x33, 0x19, 0x1C, 0x62, 0xA7, 0xBF, 0x3B, 0x23, 0x78, 0x16, 0xA4, 0xC1, 0xB7, 0x62, 0x3D, - 0xAD, 0x42, 0x22, 0xCB, 0x88, 0x8F, 0xF6, 0x27, 0xC2, 0x23, 0x1C, 0x6F, 0xC2, 0x02, 0x00, 0x00, - 0xA8, 0xE7, 0xBF, 0x01, 0x6B, 0xD7, 0x1C, 0x1D, 0x1D, 0xE1, 0x64, 0x54, 0x24, 0x9D, 0x0A, 0x8A, - 0x34, 0x8B, 0x85, 0x47, 0x38, 0x15, 0x20, 0xF1, 0xD7, 0xEE, 0x3E, 0x39, 0x19, 0x1F, 0xF2, 0xCD, - 0x57, 0x89, 0x6F, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0A, 0x8D, 0x66, 0x7B, 0x37, 0x7E, 0x0B, 0x12, - 0x46, 0x88, 0x8B, 0x8F, 0x70, 0x32, 0x2A, 0x92, 0x8E, 0xF8, 0x68, 0x7F, 0x22, 0x3A, 0xA2, 0x7B, - 0xF1, 0x7A, 0x63, 0x5E, 0xBE, 0xB3, 0xE6, 0x53, 0xE1, 0x01, 0x00, 0x00, 0xA6, 0x3D, 0x73, 0xA8, - 0x7F, 0xAD, 0xD9, 0xF1, 0x49, 0x1D, 0x1E, 0x6E, 0x32, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, 0xC5, - 0xA2, 0x23, 0xBA, 0x78, 0x7C, 0x44, 0x6F, 0x3F, 0x82, 0xF8, 0x98, 0xBC, 0xFD, 0x10, 0xDF, 0x7A, - 0x95, 0xF8, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xA9, 0xC0, 0x68, 0x35, 0x77, 0x0B, 0x52, 0xF7, 0xE1, - 0x84, 0x36, 0x3A, 0xF2, 0x09, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xE0, 0x88, 0xEF, 0x85, 0xAB, 0x79, - 0x13, 0x16, 0x00, 0x00, 0x40, 0x9C, 0xFF, 0x06, 0xAC, 0xAD, 0x1F, 0xEC, 0xB9, 0xF8, 0x68, 0x74, - 0xFB, 0xE1, 0xE2, 0xE3, 0x6C, 0x2C, 0x3E, 0xB2, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x17, - 0x49, 0xF7, 0xEE, 0x8F, 0x2B, 0x01, 0xE2, 0xBF, 0x96, 0xF7, 0x29, 0x1B, 0x1F, 0x6E, 0xD1, 0x08, - 0x91, 0x71, 0xD1, 0x6A, 0xC4, 0x47, 0xFB, 0x13, 0xB1, 0xD1, 0x60, 0xDE, 0x89, 0x2F, 0x0D, 0x06, - 0x5F, 0x6A, 0x00, 0x00, 0x00, 0x70, 0xBC, 0xE1, 0xE5, 0x43, 0x3D, 0x15, 0x1F, 0x6E, 0xF1, 0xF8, - 0x88, 0xDE, 0x7E, 0xB8, 0xF8, 0x68, 0x75, 0xFB, 0xF1, 0xA0, 0x9D, 0x0C, 0x8E, 0xE8, 0x74, 0x50, - 0xA4, 0x99, 0x8A, 0x8A, 0x34, 0x3B, 0xFD, 0xED, 0xE0, 0x16, 0xE4, 0x49, 0x1B, 0x20, 0x36, 0x0A, - 0xEA, 0x02, 0x24, 0x75, 0x84, 0x10, 0x1F, 0xED, 0x4F, 0x87, 0x46, 0xA3, 0x79, 0x47, 0x6F, 0x1D, - 0x0D, 0xBE, 0xD4, 0x00, 0x00, 0x00, 0xE0, 0x78, 0x7B, 0xAF, 0xA8, 0x7F, 0x05, 0xAF, 0x8C, 0x8A, - 0xA4, 0x53, 0x41, 0x91, 0x66, 0x22, 0x38, 0xA2, 0xB3, 0xD1, 0xE1, 0x7E, 0xAC, 0xBB, 0xFD, 0xB0, - 0xF1, 0x71, 0x6E, 0xE3, 0x45, 0xC9, 0x6E, 0x3F, 0xEE, 0x6B, 0x75, 0xFB, 0xA1, 0x83, 0x22, 0xCD, - 0x54, 0x50, 0x64, 0xD9, 0xBB, 0xF6, 0xD0, 0x1F, 0x06, 0x48, 0x7B, 0xB7, 0x20, 0xC4, 0x47, 0xFB, - 0xD3, 0x91, 0xD1, 0x74, 0xC7, 0x6E, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x51, 0x66, 0xEF, 0xE2, - 0xDE, 0x89, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x08, 0x03, 0x24, 0x88, 0x8F, 0x78, 0x80, 0xD4, - 0xC5, 0x47, 0xCB, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0xA6, 0x42, 0x22, 0xDB, 0x2E, 0x30, 0xA7, 0xBF, - 0x6D, 0xA3, 0x60, 0x70, 0x32, 0x42, 0x6A, 0x02, 0xC4, 0x85, 0x45, 0xA2, 0x08, 0x21, 0x3E, 0xDA, - 0x9F, 0x88, 0x8B, 0x24, 0x73, 0x6F, 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x1B, 0x00, 0x00, - 0xC0, 0xF4, 0x66, 0x46, 0xFA, 0x67, 0xD7, 0xBC, 0x82, 0x57, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, - 0x33, 0x11, 0x1B, 0xF1, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, 0xDF, 0x7E, 0xD8, 0xF0, 0x10, 0xB7, 0x1F, - 0xF2, 0x43, 0x07, 0x9B, 0xDE, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x76, 0x41, 0x75, - 0xE1, 0x2D, 0xC8, 0xB9, 0x4C, 0xB7, 0x20, 0xC4, 0x47, 0xFB, 0x13, 0x61, 0x91, 0x74, 0x63, 0xCB, - 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xC8, 0x7F, 0x05, 0x6F, 0xF8, 0x06, 0x2C, 0x19, 0x15, 0x49, - 0xA7, 0x82, 0x22, 0xCD, 0x62, 0xA1, 0xD1, 0x68, 0x61, 0x78, 0x84, 0xF1, 0xE1, 0xF6, 0xD4, 0x85, - 0x35, 0xDF, 0x7A, 0x15, 0x0F, 0x90, 0x53, 0x2E, 0x40, 0xC2, 0xDB, 0x8F, 0x1F, 0xD9, 0xC9, 0xF0, - 0x70, 0xD3, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x6C, 0x32, 0x3E, 0xDC, 0x4E, 0x7D, 0xD3, 0xC5, - 0x41, 0x96, 0x5B, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xA8, 0x48, 0xB3, 0x17, 0x3F, 0xC7, 0x9B, 0xB0, - 0x00, 0x00, 0x00, 0x42, 0xFE, 0x1B, 0xB0, 0xB6, 0x7D, 0xC4, 0x1E, 0xEE, 0x55, 0x54, 0x24, 0x9D, - 0x0A, 0x8A, 0x34, 0x13, 0xA1, 0xA1, 0x66, 0xA3, 0xC3, 0xFD, 0x58, 0x7B, 0xFB, 0x61, 0xE3, 0x23, - 0xCD, 0xED, 0xC7, 0xB7, 0x1A, 0xDD, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x56, 0x1B, - 0x1F, 0xE1, 0x26, 0x7E, 0x94, 0xF6, 0x16, 0x84, 0xF8, 0x68, 0x7F, 0x22, 0x28, 0x32, 0x8C, 0x37, - 0x61, 0x01, 0x00, 0x00, 0x04, 0xBC, 0x91, 0x81, 0x0D, 0x3A, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, - 0xC5, 0x22, 0xA3, 0xD9, 0xC2, 0xF0, 0x88, 0xC4, 0x47, 0xA2, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x68, - 0xD7, 0xA3, 0xF1, 0xE1, 0xD6, 0xF2, 0x16, 0x84, 0xF8, 0xC8, 0x79, 0x3A, 0x26, 0xB2, 0xCC, 0x3B, - 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0x38, 0xDE, 0x81, 0xAB, 0x47, 0x75, 0x58, 0x24, 0x99, - 0x0A, 0x8A, 0x34, 0x13, 0x91, 0xD1, 0x62, 0xD5, 0x00, 0x09, 0xE3, 0xA3, 0xED, 0xDB, 0x0F, 0x1D, - 0x14, 0x69, 0xA6, 0x43, 0x22, 0xCB, 0x74, 0x78, 0x44, 0x17, 0xDE, 0x82, 0xB8, 0x78, 0x68, 0x7C, - 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x12, 0x59, 0xE7, 0x1D, 0xBB, 0x65, 0x3C, 0xF8, 0x92, 0x03, - 0x00, 0x00, 0x98, 0xDE, 0xBC, 0x7D, 0x57, 0x8C, 0xEB, 0xB8, 0x68, 0x35, 0x15, 0x14, 0x69, 0xA6, - 0x03, 0xA3, 0xE1, 0x6A, 0x6E, 0x3F, 0x6C, 0x7C, 0x34, 0xBB, 0xFD, 0x08, 0xE3, 0xA3, 0xE5, 0xED, - 0x87, 0x0E, 0x8A, 0x34, 0xD3, 0x21, 0x91, 0x65, 0x3A, 0x38, 0xE2, 0xF3, 0x6F, 0x41, 0x1E, 0x6F, - 0x76, 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x11, 0x6D, 0xCD, 0xBD, 0x09, 0x6B, 0xEC, 0x8B, 0xB3, - 0x82, 0x2F, 0x3B, 0x00, 0x00, 0x80, 0xE9, 0xCB, 0x0C, 0x2D, 0xB0, 0x07, 0x7C, 0x15, 0x18, 0xCD, - 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x60, 0x24, 0x58, 0x18, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xB9, 0x1F, - 0xC9, 0x6E, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0E, 0x89, 0x2C, 0xD3, 0xB1, 0xD1, 0x68, 0x67, 0xBE, - 0x2F, 0x9E, 0x05, 0xF1, 0x43, 0x24, 0x12, 0x1E, 0x6E, 0xC4, 0x47, 0x86, 0x89, 0x78, 0xC8, 0x63, - 0x63, 0xCB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x80, 0xFF, 0x06, 0xAC, 0x9D, 0xB3, 0xED, 0xE1, 0x5E, - 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0x33, 0x1D, 0x17, 0x4D, 0x17, 0xB9, 0xF9, 0xF0, 0xE3, 0x43, - 0xDC, 0x7E, 0x4C, 0xB8, 0x3D, 0x1E, 0xB9, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xE5, 0x89, 0x0F, - 0x7F, 0x77, 0xAB, 0x5B, 0x90, 0x4A, 0x68, 0x18, 0x1B, 0x19, 0xC4, 0x47, 0xD6, 0x89, 0x70, 0xC8, - 0x65, 0x8B, 0x8D, 0x79, 0xF1, 0x5A, 0xE3, 0xFD, 0xE2, 0x4B, 0x77, 0x05, 0x5F, 0x7A, 0x00, 0x00, - 0x00, 0xD3, 0x93, 0x0D, 0x90, 0x75, 0x66, 0xC7, 0x27, 0xEC, 0x21, 0x5F, 0x85, 0x86, 0x9A, 0x0A, - 0x8A, 0x34, 0x13, 0x71, 0x91, 0x64, 0x61, 0x7C, 0xC4, 0x6E, 0x3F, 0x12, 0x3F, 0xFB, 0xF1, 0xCD, - 0x12, 0xC5, 0x47, 0xB0, 0x33, 0xDF, 0x8B, 0x3E, 0x0B, 0x52, 0x89, 0x8C, 0x70, 0x2E, 0x40, 0xC2, - 0xF0, 0x20, 0x3E, 0x92, 0x4E, 0x85, 0x43, 0x1E, 0xB3, 0xF1, 0xE1, 0x66, 0x7F, 0xED, 0xDE, 0x89, - 0x3B, 0x36, 0x04, 0x5F, 0x7A, 0x00, 0x00, 0x00, 0xD3, 0x93, 0xFF, 0x0A, 0x5E, 0x19, 0x1A, 0x6A, - 0x2A, 0x28, 0xD2, 0x4C, 0x84, 0x45, 0xAB, 0xD9, 0xF0, 0xA8, 0xC6, 0x87, 0x0D, 0x8F, 0xC9, 0xDB, - 0x8F, 0xD8, 0xB7, 0x5E, 0x45, 0x6F, 0x3F, 0xEC, 0x1A, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, - 0x12, 0x59, 0xA6, 0xC3, 0x22, 0xF1, 0xAA, 0xB7, 0x20, 0x36, 0x40, 0x36, 0xBA, 0xC0, 0x08, 0x82, - 0x23, 0x12, 0x1E, 0xC4, 0x47, 0xD2, 0xA9, 0x70, 0xC8, 0x63, 0x41, 0x7C, 0x04, 0xF3, 0x4E, 0xFC, - 0x39, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x9B, 0x77, 0xF0, 0xBA, 0x84, 0x6F, 0xC0, 0x52, 0x41, - 0x91, 0x66, 0xB1, 0xB0, 0x48, 0x3A, 0x17, 0x20, 0xE1, 0xCD, 0x87, 0x5D, 0xF5, 0x5B, 0xAF, 0xE4, - 0xB3, 0x1F, 0x17, 0xB5, 0xB8, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x04, 0x45, - 0x86, 0x9D, 0xFE, 0xCE, 0x7B, 0xFD, 0xB0, 0x08, 0x23, 0xA4, 0x1A, 0x1C, 0x2E, 0x3E, 0x82, 0x28, - 0x21, 0x3E, 0x5A, 0x4D, 0x85, 0x43, 0x1E, 0xAB, 0x8D, 0x0F, 0x3F, 0x40, 0x8E, 0x7D, 0x81, 0x37, - 0x61, 0x01, 0x00, 0x80, 0xE9, 0xCD, 0xDB, 0xFB, 0xD9, 0x31, 0x1D, 0x1C, 0xD1, 0xA9, 0xA0, 0x48, - 0xB3, 0x58, 0x54, 0x24, 0x5D, 0x2C, 0x3E, 0xC2, 0xDB, 0x8F, 0xBA, 0xF8, 0x88, 0xDE, 0x7E, 0x84, - 0xF1, 0x11, 0x06, 0xC8, 0x86, 0xF0, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xE9, 0x90, 0xC8, 0x32, 0x1D, - 0x13, 0xA9, 0xF7, 0x57, 0xEF, 0xF5, 0xF7, 0xEE, 0xA3, 0x93, 0x01, 0x12, 0x9F, 0x1F, 0x27, 0x2E, - 0x3C, 0x88, 0x8F, 0x06, 0x53, 0xE1, 0x90, 0xC7, 0xEA, 0xE3, 0xC3, 0xDF, 0x91, 0x1B, 0x4C, 0xF0, - 0xA5, 0x07, 0x00, 0x00, 0x30, 0x3D, 0x99, 0x3D, 0xF6, 0x50, 0x24, 0xA3, 0x23, 0x9C, 0x0A, 0x8A, - 0x34, 0x8B, 0x45, 0x45, 0xD2, 0x35, 0x88, 0x8F, 0xEA, 0x83, 0xE7, 0xEA, 0xF6, 0x23, 0x1E, 0x1F, - 0x0F, 0xD9, 0xDD, 0x5D, 0xEE, 0xF8, 0x70, 0x73, 0xB7, 0x20, 0x7E, 0x60, 0xD8, 0x9D, 0x7B, 0xD2, - 0x86, 0x87, 0xDD, 0xB9, 0x68, 0x7C, 0xD8, 0xF0, 0x38, 0x6B, 0xC3, 0x83, 0xF8, 0x88, 0x4F, 0x85, - 0x43, 0x1E, 0x8B, 0x45, 0x47, 0x74, 0x2F, 0xAD, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xFA, 0x32, - 0x87, 0xFB, 0x97, 0x98, 0xDD, 0xF3, 0xEC, 0x81, 0x5F, 0x85, 0x87, 0x9B, 0x0A, 0x8A, 0x34, 0x8B, - 0x45, 0x45, 0x9A, 0xD9, 0xE8, 0xA8, 0x0B, 0x90, 0x26, 0xDF, 0x7A, 0x25, 0xDF, 0x7C, 0x75, 0xBF, - 0xBB, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x45, 0xE2, 0x23, - 0x9C, 0xBB, 0x05, 0xF1, 0x43, 0x63, 0xD0, 0xC6, 0x88, 0x0D, 0x90, 0xB3, 0xB1, 0x6F, 0xB9, 0x7A, - 0xF7, 0x31, 0xE2, 0xA3, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x88, 0x8E, 0xE8, 0x5E, 0xBC, 0xD6, 0x06, - 0xC8, 0x1D, 0xEB, 0x82, 0x2F, 0x41, 0x00, 0x00, 0x80, 0xE9, 0xC5, 0x7F, 0x05, 0x6F, 0xC3, 0x37, - 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x15, 0x49, 0xF7, 0x8C, 0x88, 0x8F, 0xE8, 0xED, 0x87, 0x8B, - 0x8F, 0x9A, 0x00, 0x11, 0xDF, 0x7A, 0xE5, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, - 0x26, 0x42, 0x22, 0xCB, 0x44, 0x7C, 0xBC, 0xBD, 0xFE, 0xBD, 0xE6, 0xD4, 0x3D, 0x36, 0x42, 0x5C, - 0x68, 0xB8, 0xE0, 0x70, 0x21, 0x12, 0xFC, 0xE8, 0xFE, 0xB5, 0x09, 0x17, 0x1F, 0x36, 0x3C, 0x4E, - 0x3F, 0x42, 0x7C, 0x54, 0xA6, 0xC2, 0x21, 0x8F, 0x89, 0xE0, 0x88, 0xCF, 0xFD, 0xBE, 0x4E, 0xDC, - 0xB1, 0x3E, 0xF8, 0x12, 0x04, 0x00, 0x00, 0x98, 0x5E, 0xFC, 0x37, 0x60, 0x6D, 0xF9, 0x80, 0x3D, - 0xF4, 0x77, 0x5F, 0x7C, 0x34, 0xBA, 0xFD, 0x50, 0xAF, 0xDD, 0xD5, 0xB7, 0x1F, 0xED, 0x7F, 0xEB, - 0x95, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0x0D, 0xE2, 0x23, 0xDC, 0x99, 0x07, 0x6D, 0x6C, - 0x3C, 0x3E, 0xA3, 0x12, 0x22, 0x41, 0x78, 0x4C, 0x3C, 0x7A, 0x81, 0x7F, 0xEB, 0x41, 0x7C, 0x84, - 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x6C, 0x34, 0x98, 0x77, 0xE2, 0x0E, 0xDE, 0x84, 0x05, 0x00, 0x00, - 0xA6, 0x27, 0xEF, 0xD0, 0xCA, 0xC1, 0xAE, 0x8B, 0x8F, 0x5C, 0x6E, 0x3F, 0x2E, 0x6A, 0xFB, 0xF6, - 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0xD6, 0xD7, 0xEC, 0xBF, 0xF6, - 0x4D, 0x17, 0x1D, 0x33, 0xFC, 0x9D, 0xB1, 0xE1, 0xE1, 0xE6, 0x7F, 0xCB, 0x55, 0x18, 0x1F, 0x4F, - 0x11, 0x1F, 0xC5, 0x4C, 0x87, 0x46, 0xA3, 0x79, 0xC7, 0xBE, 0x70, 0x32, 0xF8, 0x12, 0x04, 0x00, - 0x00, 0x98, 0x5E, 0xBC, 0xBD, 0x57, 0xC4, 0x5E, 0xC1, 0xAB, 0x82, 0x22, 0xCD, 0x44, 0x54, 0x24, - 0x5D, 0x24, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0xA3, 0xFE, 0xD9, 0x0F, 0x75, 0xFB, 0x61, 0xE3, 0xE3, - 0xBE, 0xF6, 0x6E, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x4B, 0x10, 0x1F, 0x6F, 0x7D, - 0xED, 0x02, 0xF3, 0xD6, 0x7F, 0xB9, 0xC0, 0x9C, 0xFA, 0xA1, 0x0D, 0x0D, 0x77, 0xE3, 0xF1, 0x63, - 0x1B, 0x1F, 0x41, 0x78, 0xBC, 0xF3, 0x90, 0xFD, 0x91, 0xF8, 0x28, 0x68, 0x3A, 0x32, 0x9A, 0xEE, - 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x4C, 0x4F, 0x66, 0xAF, 0x3D, 0xF0, 0x75, 0x53, 0x7C, - 0x04, 0x01, 0xD2, 0xEC, 0xF6, 0x23, 0x1E, 0x20, 0x75, 0xF1, 0xF1, 0x60, 0x7B, 0x0F, 0x9E, 0xEB, - 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x96, 0x22, 0x3E, 0xDE, 0xFC, 0xCF, 0xF6, 0xC7, 0xBB, 0x6D, - 0x84, 0x04, 0xD1, 0xE1, 0xEF, 0x41, 0xE2, 0x43, 0x87, 0x43, 0x1E, 0x13, 0x71, 0x91, 0x64, 0x47, - 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xFD, 0x98, 0xD1, 0xFE, 0x59, 0x66, 0x68, 0x81, 0x0D, - 0x80, 0xEE, 0x8A, 0x0F, 0xF7, 0xA3, 0xBA, 0xFD, 0x50, 0xCF, 0x7E, 0xD4, 0x7E, 0xE8, 0xA0, 0x0D, - 0x8F, 0x87, 0xDB, 0xBB, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x2C, 0x65, 0x7C, 0xB8, - 0xFD, 0xE1, 0xFF, 0xB1, 0xFF, 0xFF, 0x77, 0x66, 0x98, 0xB7, 0x1F, 0x98, 0x69, 0x67, 0xE3, 0x63, - 0x23, 0xF1, 0x51, 0xCC, 0x44, 0x58, 0x24, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x2F, 0xAD, 0x0D, 0xBE, - 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0xFF, 0x0D, 0x58, 0x3B, 0x67, 0xDB, 0x08, 0x50, 0x41, 0x91, - 0x66, 0x22, 0x2A, 0x92, 0x2E, 0x1A, 0x1F, 0x41, 0x80, 0x34, 0xBB, 0xFD, 0x70, 0xF1, 0x11, 0x0F, - 0x90, 0x6A, 0x7C, 0xB4, 0x79, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, 0x64, 0x59, 0xC6, 0xF8, - 0x78, 0xE3, 0x2F, 0x67, 0x98, 0xF1, 0xFF, 0x3C, 0xC3, 0xBC, 0xF5, 0x23, 0xFB, 0x7B, 0x7A, 0x92, - 0xF8, 0x28, 0x66, 0x22, 0x2A, 0xD2, 0xEC, 0xF9, 0x2B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x7E, - 0xBC, 0x43, 0xAB, 0xEE, 0x32, 0xDB, 0x3E, 0x6A, 0x43, 0x40, 0x45, 0x45, 0xD2, 0x89, 0xA8, 0x48, - 0x3A, 0x11, 0x1F, 0xEE, 0xC7, 0xD4, 0xB7, 0x1F, 0x61, 0x7C, 0xB8, 0xDB, 0x8F, 0x7B, 0xB3, 0xDD, - 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x4E, 0x7C, 0xFC, 0xA7, 0x19, 0xE6, 0xF7, - 0xFF, 0xF7, 0x0C, 0xF3, 0xE6, 0x03, 0xC4, 0x47, 0x31, 0x13, 0x41, 0x91, 0x61, 0xDE, 0x2F, 0xEE, - 0x18, 0x0A, 0xBE, 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0x33, 0xD2, 0xBF, 0x5E, 0x47, 0x45, 0xD2, - 0x89, 0xA8, 0x48, 0xBA, 0x78, 0x7C, 0xB8, 0x85, 0xE1, 0x11, 0xC6, 0x47, 0xAB, 0xDB, 0x0F, 0xFF, - 0x5B, 0xAF, 0x82, 0xF8, 0x70, 0xFB, 0xA1, 0x9D, 0x88, 0x8B, 0x56, 0xD3, 0x21, 0x91, 0x65, 0x22, - 0x24, 0xB2, 0x2C, 0x87, 0xF8, 0x78, 0xE3, 0xDB, 0x36, 0x3E, 0x5E, 0x22, 0x3E, 0xF2, 0x9F, 0x8E, - 0x89, 0x2C, 0xF3, 0x8E, 0xDE, 0x3C, 0x16, 0x7C, 0x29, 0x02, 0x00, 0x00, 0x4C, 0x0F, 0xDE, 0x81, - 0xAB, 0x46, 0x75, 0x58, 0x24, 0x99, 0x88, 0x8A, 0xA4, 0x6B, 0x10, 0x1F, 0xEE, 0xC7, 0x54, 0xB7, - 0x1F, 0xD1, 0xF8, 0x70, 0xB7, 0x1F, 0xDF, 0x4A, 0x7F, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, - 0x64, 0x19, 0xF1, 0x91, 0xC3, 0x54, 0x38, 0xE4, 0x31, 0x1D, 0x12, 0x99, 0xC7, 0x9B, 0xB0, 0x00, - 0x00, 0xC0, 0x74, 0xE3, 0xED, 0xFF, 0xEC, 0x49, 0x1D, 0x17, 0xAD, 0x26, 0xA2, 0x22, 0xE9, 0xE2, - 0xE1, 0x11, 0x2E, 0x0C, 0x8F, 0x30, 0x3E, 0x9A, 0xDD, 0x7E, 0x3C, 0x76, 0x91, 0x39, 0xFD, 0xA8, - 0x0D, 0x8E, 0x36, 0x6F, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, - 0x0A, 0x87, 0x3C, 0x26, 0x02, 0xA2, 0xDD, 0xB9, 0x37, 0x61, 0x1D, 0xBF, 0x63, 0x49, 0xF0, 0xE5, - 0x08, 0x00, 0x00, 0x50, 0x7E, 0x66, 0x68, 0xBE, 0x8D, 0x02, 0x15, 0x18, 0xCD, 0x26, 0xA2, 0x22, - 0xE9, 0x54, 0x78, 0x44, 0x96, 0xE8, 0xF6, 0x23, 0x88, 0x8F, 0x76, 0x6F, 0x3F, 0x74, 0x48, 0x64, - 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0x26, 0xE2, 0x21, 0x8F, 0xBD, - 0x78, 0x1D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x87, 0xFF, 0x06, 0xAC, 0x5D, 0x9F, 0xB6, 0x61, - 0xA0, 0x22, 0xA3, 0xD1, 0x44, 0x54, 0x24, 0x9D, 0x08, 0x8E, 0xEA, 0x92, 0xDE, 0x7E, 0x3C, 0x6E, - 0x03, 0x24, 0x8C, 0x8F, 0x68, 0x80, 0xFC, 0x80, 0xF8, 0x20, 0x3E, 0x54, 0x38, 0xE4, 0x31, 0x11, - 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x3E, 0xCC, 0xA1, 0xFE, 0xB5, - 0x66, 0xC7, 0xC7, 0x6D, 0x1C, 0xA8, 0xD0, 0x50, 0x13, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0x62, 0x53, - 0xB7, 0x1F, 0xD1, 0x00, 0xF1, 0xE3, 0xA3, 0xD1, 0xED, 0xC7, 0x37, 0x75, 0x68, 0xA8, 0xE9, 0x90, - 0xC8, 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x44, 0x43, 0xCE, - 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xDA, 0xF0, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0x36, 0x10, 0x54, - 0x6C, 0xC4, 0x27, 0xA2, 0x22, 0xE9, 0x44, 0x6C, 0xD4, 0x2C, 0xC1, 0xED, 0x87, 0xFB, 0xD6, 0xAB, - 0x89, 0xC7, 0x2E, 0x9C, 0xBC, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xC9, 0x6F, 0x3F, 0x74, 0x48, - 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0xA6, 0x83, 0x21, 0xEF, - 0x79, 0x47, 0xFF, 0x64, 0x34, 0xF8, 0x92, 0x04, 0x00, 0x00, 0x28, 0x37, 0x6F, 0x78, 0xE9, 0x90, - 0x8E, 0x8D, 0xF8, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8E, 0xF8, 0xE2, 0x01, 0xA2, 0x9E, 0xFD, 0x78, - 0xDC, 0xC6, 0x47, 0x9B, 0xB7, 0x1F, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x11, 0x1F, 0x39, - 0x4C, 0x85, 0x43, 0x1E, 0xD3, 0xB1, 0x50, 0xC8, 0x8E, 0x7D, 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, - 0xE9, 0xC1, 0xDB, 0xBB, 0x24, 0xC1, 0x2B, 0x78, 0x45, 0x54, 0x24, 0x9D, 0x8A, 0x8D, 0xE8, 0x6C, - 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, 0x47, 0xEC, 0x5B, 0xAF, 0x9E, 0xB8, 0x70, 0xF2, - 0xF6, 0xC3, 0x2E, 0xCB, 0xED, 0x87, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, - 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x24, 0x14, 0xB6, 0x45, 0x95, 0x37, 0x61, 0xBD, 0x7C, 0xE7, 0xEC, - 0xE0, 0xCB, 0x12, 0x00, 0x00, 0xA0, 0xBC, 0xCC, 0x1E, 0x7B, 0xF8, 0x91, 0xD1, 0x11, 0x4E, 0x44, - 0x45, 0xD2, 0xA9, 0xE0, 0x88, 0x2F, 0x1A, 0x20, 0x76, 0xD5, 0x6F, 0xBD, 0x8A, 0x06, 0x88, 0x8B, - 0x0F, 0xFF, 0xF6, 0xE3, 0xC2, 0xFA, 0xDB, 0x8F, 0x87, 0xEC, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, - 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xB3, - 0x5F, 0x7F, 0x6E, 0x2F, 0x7E, 0xCE, 0x05, 0xC8, 0x40, 0xF0, 0x65, 0x09, 0x00, 0x00, 0x50, 0x4E, - 0x66, 0xA4, 0x7F, 0x76, 0xF3, 0x57, 0xF0, 0x8A, 0xA8, 0x48, 0x3A, 0x15, 0x1B, 0xF1, 0xC5, 0xE2, - 0x23, 0xBC, 0xFD, 0xA8, 0x7D, 0xF0, 0xDC, 0xC6, 0x47, 0xF4, 0xF6, 0x23, 0xFE, 0xAD, 0x57, 0xF7, - 0xB7, 0xBE, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, - 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x16, 0xC4, 0x87, 0x1F, 0x20, 0xD7, 0xF0, 0x26, 0x2C, 0x00, 0x00, - 0x50, 0x7E, 0xFE, 0x2B, 0x78, 0x1B, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, 0xA9, 0xD8, 0x88, 0xAF, - 0x41, 0x7C, 0x54, 0x1F, 0x3C, 0x77, 0xB3, 0xF1, 0x51, 0x73, 0xFB, 0x11, 0x7F, 0xF0, 0xDC, 0xDD, - 0x7E, 0xDC, 0xAD, 0xA3, 0x23, 0x9C, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, - 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0xB5, 0x48, 0x7C, 0x04, 0xF3, 0x4E, 0x7C, 0x69, 0x30, - 0xF8, 0xD2, 0x04, 0x00, 0x00, 0x28, 0x27, 0xFF, 0x0D, 0x58, 0x5B, 0x3F, 0x64, 0xA3, 0x61, 0x8A, - 0xE3, 0xC3, 0x4D, 0x05, 0x48, 0xCD, 0xB7, 0x5E, 0xD9, 0xF8, 0x70, 0xB7, 0x1F, 0x41, 0x7C, 0x64, - 0xB9, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x56, 0x1F, 0x1F, 0x6E, 0xDE, 0xF1, 0x3F, 0xE5, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0xDC, 0xBC, 0x91, 0x81, 0x0D, 0x1D, 0x89, 0x0F, 0xB7, 0x78, 0x7C, 0x54, 0x6F, 0x3F, 0x2E, - 0xAC, 0xC6, 0xC7, 0xE4, 0xED, 0x47, 0x2C, 0x3E, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, - 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xF1, 0xE1, - 0xEF, 0xD8, 0x2D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x94, 0x9B, 0x77, 0xE0, 0xAA, 0xD8, 0x1B, 0xB0, - 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x8C, 0x46, 0xB3, 0xD1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0x44, 0x02, - 0x24, 0x8C, 0x0F, 0xF9, 0xB9, 0x1F, 0xDF, 0x69, 0x7C, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, - 0x64, 0x59, 0x34, 0x3A, 0x88, 0x8F, 0x8C, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x11, - 0x1D, 0xD1, 0xB9, 0x37, 0x61, 0x8D, 0x7D, 0x71, 0x56, 0xF0, 0xE5, 0x09, 0x00, 0x00, 0x50, 0x3E, - 0xDE, 0xBE, 0x25, 0xE3, 0x53, 0x1E, 0x1F, 0x6E, 0xF1, 0xF8, 0x48, 0x7B, 0xFB, 0xF1, 0xA0, 0x9D, - 0x08, 0x0F, 0x37, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x52, 0xC4, 0x47, 0x18, 0x20, 0x2E, - 0x3E, 0xFE, 0x40, 0x7C, 0x44, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x38, 0xE2, - 0x1B, 0x5B, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB9, 0x4D, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, - 0xA9, 0xC0, 0x68, 0x36, 0x1B, 0x1D, 0xEE, 0x47, 0x75, 0xFB, 0x11, 0x0D, 0x90, 0xA6, 0xB7, 0x1F, - 0xF7, 0xE9, 0xDB, 0x0F, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x22, 0xF1, 0x11, 0xAE, 0x3E, - 0x3E, 0xEC, 0x5C, 0x78, 0x84, 0xB7, 0x1F, 0xC4, 0x47, 0x6C, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, - 0xA2, 0x26, 0x62, 0x43, 0xED, 0xC5, 0x6B, 0x8C, 0xF7, 0x8B, 0x2F, 0xDD, 0x15, 0x7C, 0x79, 0x02, - 0x00, 0x00, 0x94, 0x8B, 0xFF, 0x06, 0xAC, 0x9D, 0x9F, 0xD2, 0x51, 0x91, 0x74, 0xF1, 0xB8, 0x48, - 0xB2, 0x30, 0x3C, 0xC2, 0xF8, 0xF0, 0x03, 0x64, 0x66, 0x35, 0x3E, 0x6A, 0xDF, 0x7C, 0x95, 0xFC, - 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0x76, 0x24, 0x3E, 0xE2, - 0x37, 0x1F, 0x6F, 0x04, 0xF1, 0xF1, 0x07, 0xE2, 0xA3, 0xA0, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x34, - 0x9A, 0xCC, 0x3B, 0x71, 0xC7, 0x86, 0xE0, 0x4B, 0x14, 0x00, 0x00, 0xA0, 0x5C, 0x6C, 0x80, 0xAC, - 0x33, 0xDB, 0x2F, 0xD6, 0x61, 0x91, 0x64, 0x2A, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0xF6, - 0xF6, 0xC3, 0xC6, 0x47, 0x83, 0x67, 0x3F, 0xEA, 0x3E, 0x74, 0xD0, 0xBD, 0xF9, 0x4A, 0xDC, 0x7E, - 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x22, 0x3E, 0xA2, 0x37, 0x1F, 0x61, 0x7C, 0x84, - 0x37, 0x1F, 0xC4, 0x47, 0x38, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x91, 0xD1, 0x6C, - 0xDE, 0x89, 0x3F, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x9C, 0xFC, 0x57, 0xF0, 0xAA, 0xB0, 0x48, - 0xB2, 0x78, 0x58, 0x24, 0x5D, 0x18, 0x1E, 0x61, 0x7C, 0x3C, 0x3D, 0xB3, 0xE6, 0xF6, 0xA3, 0xEE, - 0xD9, 0x0F, 0x1B, 0x20, 0xA7, 0xA2, 0x01, 0xF2, 0x23, 0xBB, 0x1E, 0x8C, 0x0F, 0xB7, 0xBA, 0x00, - 0x69, 0x10, 0x1F, 0xE3, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0xC0, - 0x68, 0x35, 0xEF, 0xD8, 0x17, 0xC6, 0x83, 0x2F, 0x51, 0x00, 0x00, 0x80, 0x72, 0xF1, 0x0E, 0x5C, - 0x33, 0x2A, 0xE3, 0xA2, 0xD5, 0x54, 0x58, 0x24, 0x99, 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, - 0xF1, 0x11, 0xBD, 0xFD, 0x88, 0xC5, 0x47, 0xDD, 0xED, 0xC7, 0xB7, 0x6A, 0x6F, 0x3F, 0x74, 0x48, - 0x64, 0x99, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x56, 0x17, 0x1F, 0xEA, 0x8D, 0x57, 0xC4, 0x47, - 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x71, 0x91, 0x68, 0x47, 0x56, 0xF3, 0x26, - 0x2C, 0x00, 0x00, 0x50, 0x4E, 0xDE, 0xDE, 0x25, 0x63, 0x32, 0x30, 0x9A, 0x2D, 0x1E, 0x15, 0x69, - 0x16, 0x86, 0x47, 0x18, 0x1F, 0x69, 0x6F, 0x3F, 0x7E, 0x68, 0x57, 0xB6, 0xF8, 0x70, 0x37, 0x1F, - 0x2E, 0x3E, 0xDC, 0x43, 0xE7, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, - 0xA8, 0x48, 0xB3, 0x97, 0x56, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x4E, 0x66, 0xCF, 0x42, 0x1D, - 0x19, 0x8D, 0xA6, 0xA2, 0x22, 0xE5, 0x2A, 0x01, 0x32, 0xB3, 0xED, 0xDB, 0x0F, 0x1D, 0x12, 0x59, - 0x26, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0xB5, 0x8C, 0x8F, 0xBF, 0x9C, 0xFC, 0xAC, 0x8F, 0x3F, - 0xDC, 0x4B, 0x7C, 0x14, 0x33, 0x15, 0x09, 0x45, 0x4D, 0x04, 0x45, 0xDA, 0xBD, 0x78, 0x8D, 0x0D, - 0x90, 0x3B, 0xD6, 0x05, 0x5F, 0xA6, 0x00, 0x00, 0x00, 0xE5, 0x60, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, - 0xE6, 0xE8, 0xD0, 0x50, 0x13, 0x31, 0x91, 0x6A, 0xD5, 0xDB, 0x8F, 0x20, 0x3E, 0x9A, 0xDD, 0x7E, - 0x84, 0xF1, 0xD1, 0xE0, 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, - 0x0D, 0xE3, 0x23, 0x08, 0x90, 0xE8, 0x07, 0x0D, 0x12, 0x1F, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, - 0xA2, 0x26, 0x62, 0x22, 0xCB, 0x9E, 0xB7, 0x7F, 0x3F, 0x4E, 0xDC, 0xB1, 0x3E, 0xF8, 0x52, 0x05, - 0x00, 0x00, 0x28, 0x07, 0xFF, 0x15, 0xBC, 0x49, 0xDF, 0x80, 0xA5, 0x82, 0x22, 0xC3, 0xFC, 0xF8, - 0x88, 0xDD, 0x7E, 0x54, 0x03, 0xA4, 0xD5, 0xED, 0xC7, 0x3D, 0xBD, 0x1B, 0x1F, 0x95, 0xD7, 0xED, - 0x12, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x24, 0xDA, 0x18, 0x6F, - 0xC2, 0x02, 0x00, 0x00, 0xA5, 0xE3, 0xBF, 0x01, 0x6B, 0xEB, 0x07, 0x75, 0x70, 0x44, 0x27, 0x42, - 0x22, 0xF5, 0xC2, 0x9B, 0x8F, 0x26, 0xB7, 0x1F, 0x13, 0x2E, 0x40, 0x1E, 0x8B, 0xDC, 0x7E, 0x84, - 0xF1, 0xE1, 0xF6, 0xBD, 0xCA, 0xB7, 0x5E, 0xE9, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x26, 0xC2, - 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x11, 0xD1, 0xCE, - 0xBC, 0x63, 0x5F, 0x38, 0x19, 0x7C, 0xA9, 0x02, 0x00, 0x00, 0x94, 0x83, 0x37, 0xBC, 0x62, 0x50, - 0x06, 0x47, 0x74, 0x2A, 0x26, 0xB2, 0x2C, 0x8C, 0x8F, 0xD8, 0xED, 0x47, 0xE2, 0x67, 0x3F, 0xBE, - 0xC9, 0xB7, 0x5D, 0x25, 0x9B, 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, - 0x2A, 0x12, 0x8A, 0x9A, 0x0E, 0x88, 0xB6, 0x77, 0x64, 0xB5, 0x09, 0xBE, 0x54, 0x01, 0x00, 0x00, - 0xCA, 0xC1, 0xDB, 0x7B, 0x79, 0xF3, 0x57, 0xF0, 0xAA, 0x90, 0x48, 0xBB, 0x4D, 0x6E, 0x93, 0xE1, - 0x31, 0x79, 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xFC, 0xF6, 0x43, 0x87, 0x44, - 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0x0D, 0xE3, 0xC3, 0x3D, 0x70, 0x1E, 0x8B, 0x8F, - 0xF1, 0xFF, 0x32, 0xCB, 0x9C, 0xFF, 0xD7, 0x5B, 0x83, 0xF8, 0x58, 0x65, 0x0F, 0xFA, 0x2A, 0x28, - 0xD2, 0x4C, 0x84, 0x43, 0x1E, 0x93, 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x84, 0x43, 0x5E, 0x3B, 0xB2, 0x92, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF1, 0xF6, 0x2C, - 0x1A, 0x97, 0xE1, 0xE1, 0xA6, 0x62, 0x22, 0xCB, 0x6C, 0x78, 0x44, 0x6F, 0x3F, 0xAA, 0xDF, 0x7A, - 0x15, 0x06, 0x48, 0xCD, 0x6B, 0x77, 0x2F, 0x94, 0xB7, 0x1F, 0xEF, 0x7C, 0x23, 0xAF, 0xDB, 0x0F, - 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x2D, 0x6D, 0x7C, 0x9C, 0xFB, 0xE9, 0x17, 0x2A, 0xF1, 0x71, - 0xD4, 0xFE, 0x28, 0x83, 0x22, 0xCD, 0x44, 0x38, 0xE4, 0x31, 0x19, 0x0E, 0x79, 0x4C, 0x85, 0x43, - 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x44, 0x34, 0xE4, 0x39, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xCA, 0xC4, - 0x0C, 0xF5, 0xCF, 0x32, 0x43, 0x7D, 0x53, 0x1A, 0x1F, 0xE1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xCD, - 0x9E, 0xFD, 0x70, 0xF1, 0x71, 0x7F, 0x5E, 0xB7, 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, - 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x82, 0x21, 0xEF, 0xB9, 0xFF, - 0x6D, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0x0B, 0xFF, 0x0D, 0x58, 0x3B, 0x3E, 0x39, 0xE5, 0xF1, - 0x51, 0x7D, 0xF0, 0x5C, 0xDD, 0x7E, 0xC4, 0x1E, 0x3C, 0x7F, 0xE7, 0x41, 0x1B, 0x1F, 0x5F, 0x57, - 0x31, 0x91, 0x76, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x62, 0xA1, 0xA0, 0x79, 0x27, 0xEE, 0xE0, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0x1C, 0xBC, 0xE1, 0xFE, 0xBB, 0xCC, 0xB6, 0x8F, 0x14, 0x13, 0x1F, 0xCF, 0x54, 0xE2, 0xA3, - 0x2E, 0x40, 0xE2, 0xAF, 0xDD, 0x75, 0xB7, 0x1F, 0x41, 0x7C, 0xC4, 0x6F, 0x3F, 0xDE, 0x79, 0xD8, - 0xC6, 0xC7, 0x77, 0xF3, 0xF8, 0xD6, 0x2B, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, - 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xA1, 0x50, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, - 0x82, 0x2F, 0x59, 0x00, 0x00, 0x80, 0xDE, 0xE6, 0xBF, 0x82, 0xB7, 0xA8, 0xF8, 0x08, 0x02, 0xA4, - 0xE1, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0x8A, 0xC7, 0x47, 0x2E, 0xB7, 0x1F, 0x22, 0x24, - 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0xA6, - 0x23, 0xA1, 0xD0, 0x1D, 0xB9, 0x91, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF0, 0x0E, 0x5C, 0x33, - 0xF9, 0x06, 0x2C, 0x19, 0x12, 0x59, 0x36, 0x19, 0x1F, 0x8D, 0x6E, 0x3F, 0xAA, 0xAF, 0xDD, 0xAD, - 0xDE, 0x7E, 0x5C, 0x14, 0xBB, 0xFD, 0xB0, 0xE1, 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, - 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x38, 0x98, - 0x8A, 0xBD, 0x34, 0x60, 0xCC, 0xF1, 0x3B, 0x96, 0x04, 0x5F, 0xB6, 0x00, 0x00, 0x00, 0xBD, 0xCB, - 0xDB, 0x77, 0xC5, 0xC9, 0x42, 0xE2, 0xA3, 0xAD, 0xDB, 0x8F, 0x20, 0x3E, 0x1E, 0x68, 0xF7, 0xF6, - 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, - 0x50, 0xD4, 0x44, 0x18, 0x4C, 0xD5, 0xDC, 0x9B, 0xB0, 0x4E, 0x7C, 0x69, 0x6D, 0xF0, 0x65, 0x0B, - 0x00, 0x00, 0xD0, 0xBB, 0xCC, 0xEE, 0xBE, 0x48, 0x3C, 0xB4, 0xBB, 0xDA, 0xF8, 0x70, 0x3F, 0xAA, - 0xDB, 0x8F, 0xFA, 0x67, 0x3F, 0xA2, 0xB7, 0x1F, 0x41, 0x7C, 0xB8, 0xDD, 0xDB, 0xCE, 0xED, 0x87, - 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, - 0xA8, 0x89, 0x28, 0x98, 0xCA, 0xBD, 0x70, 0x25, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xCF, 0x1C, - 0xE8, 0x5F, 0x62, 0x76, 0xCE, 0x0E, 0xE2, 0xA1, 0xDD, 0x45, 0xE2, 0x23, 0x08, 0x90, 0x66, 0xB7, - 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xE1, 0x6E, 0x3F, 0x64, 0x58, 0x24, 0x99, 0x08, 0x89, 0x2C, - 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x20, - 0xE8, 0xC0, 0xBC, 0xE3, 0xB7, 0x0F, 0x05, 0x5F, 0xBA, 0x00, 0x00, 0x00, 0xBD, 0xC9, 0x1C, 0x5C, - 0xB6, 0xD6, 0x6C, 0xFF, 0x58, 0x10, 0x10, 0xED, 0xAC, 0x3E, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0x43, - 0x3D, 0xFB, 0x51, 0xF9, 0xD0, 0xC1, 0x0B, 0x27, 0x03, 0xE4, 0x21, 0xBB, 0xCC, 0xB7, 0x1F, 0x22, - 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0xA6, 0x63, 0xA0, 0x13, 0xF3, 0x8E, 0xDE, 0xC2, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x33, 0x23, - 0xCB, 0xD7, 0x9B, 0xCD, 0xEF, 0xB3, 0xB1, 0xA0, 0xA2, 0x22, 0xE9, 0x62, 0xF1, 0xE1, 0x16, 0x86, - 0x47, 0x18, 0x1F, 0xB1, 0xDB, 0x8F, 0xBA, 0x67, 0x3F, 0x1E, 0xB5, 0xF1, 0xE1, 0x02, 0xA4, 0xED, - 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, - 0x45, 0x42, 0x51, 0xD3, 0x21, 0xD0, 0xB1, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, - 0xE6, 0x1D, 0xB8, 0x7E, 0xA8, 0x36, 0x26, 0xD2, 0x4E, 0xC7, 0x87, 0xFB, 0xB1, 0xE9, 0xED, 0x47, - 0x2C, 0x3E, 0xEA, 0x6E, 0x3F, 0xEE, 0xC9, 0x72, 0xFB, 0x21, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, - 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x00, 0x3A, 0x3D, 0xF7, - 0x26, 0xAC, 0x97, 0xEF, 0x9C, 0x1D, 0x7C, 0xF9, 0x02, 0x00, 0x00, 0xF4, 0x1E, 0x6F, 0xEF, 0x65, - 0xA3, 0xF5, 0x51, 0x91, 0x74, 0xB1, 0xF0, 0x08, 0x17, 0x86, 0x47, 0x18, 0x1F, 0x4D, 0x6F, 0x3F, - 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x4E, 0x06, 0x46, 0xB3, 0x89, 0x90, 0xC8, - 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xFC, 0x77, 0xC3, 0x5E, 0xFC, 0x9C, 0x31, 0xC7, 0x6E, 0x1B, 0x08, 0xBE, 0x7C, 0x01, 0x00, 0x00, - 0x7A, 0x8F, 0xD9, 0x63, 0x0F, 0x5B, 0x32, 0x2E, 0x5A, 0x4D, 0x84, 0x47, 0x64, 0xC9, 0x6E, 0x3F, - 0x2A, 0xF1, 0xD1, 0xFE, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, - 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xFE, 0xDD, 0x32, 0xDE, 0x84, 0x05, 0x00, - 0x00, 0x7A, 0x99, 0x19, 0xE9, 0x9F, 0x6D, 0x76, 0xCF, 0xB3, 0xC1, 0xA0, 0x02, 0xA3, 0xD9, 0x74, - 0x74, 0xF8, 0x4B, 0x7C, 0xFB, 0x31, 0xD3, 0x9C, 0x09, 0xE3, 0xA3, 0xAD, 0xDB, 0x0F, 0x11, 0x12, - 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xFE, 0x2E, 0x9B, 0x77, 0xE2, 0xF6, 0xC1, 0xE0, 0x4B, 0x18, 0x00, 0x00, 0xA0, 0xB7, 0xD8, - 0x00, 0x19, 0x48, 0xFF, 0x06, 0x2C, 0x11, 0x1D, 0xB1, 0xA9, 0xDB, 0x8F, 0x6A, 0x80, 0x84, 0xF1, - 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, - 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7F, 0xB7, 0xCD, 0x3B, 0xFA, 0x85, 0xD1, 0xE0, 0x4B, - 0x18, 0x00, 0x00, 0xA0, 0xB7, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0x3E, 0x60, 0xA3, 0x41, 0x85, - 0x86, 0x9A, 0x0E, 0x8E, 0xEA, 0x12, 0xDC, 0x7E, 0x4C, 0x3C, 0x31, 0xD3, 0x4C, 0x3C, 0x16, 0xB9, - 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0xFB, 0xC4, 0x47, 0x65, 0x22, 0x1C, 0xF2, 0x98, 0x0C, 0x87, 0x3C, - 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0xB0, 0xDF, 0x95, 0x3B, 0x76, 0x0B, 0x6F, - 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xC9, 0x1B, 0x59, 0xB1, 0xA1, 0x3E, 0x32, 0x1A, 0x4D, 0x04, 0x47, - 0x7C, 0xF1, 0x00, 0xA9, 0x7B, 0xF6, 0xC3, 0xC6, 0x47, 0xB3, 0xDB, 0x8F, 0x6F, 0xA8, 0xD0, 0x50, - 0x13, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, - 0x14, 0x35, 0x71, 0xC8, 0xEF, 0xE6, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xAB, 0xBC, 0x7D, 0x57, - 0x24, 0x7C, 0x03, 0x96, 0x88, 0x8D, 0xE8, 0x6C, 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, - 0x47, 0xE4, 0x5B, 0xAF, 0x06, 0x6D, 0x7C, 0x44, 0x6F, 0x3F, 0xEC, 0x6A, 0x6E, 0x3F, 0x36, 0x24, - 0xBD, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, - 0x54, 0x24, 0x14, 0x35, 0x71, 0xC0, 0xEF, 0xF6, 0x8D, 0x2D, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0xA0, - 0x37, 0x79, 0x7B, 0x2F, 0x1B, 0xAF, 0x8F, 0x8D, 0xF8, 0x62, 0xB1, 0xA1, 0x16, 0x0D, 0x10, 0xBB, - 0xEA, 0xB7, 0x5E, 0x85, 0x01, 0x62, 0xE3, 0x63, 0xF2, 0xF6, 0x63, 0x66, 0x1B, 0xB7, 0x1F, 0x22, - 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0x26, 0x0E, 0xF7, 0xBD, 0xB0, 0x17, 0xAF, 0x31, 0xDE, 0x2F, 0x6E, 0xBF, 0x2B, 0xF8, 0x32, 0x06, - 0x00, 0x00, 0xE8, 0x1D, 0xAD, 0xDF, 0x80, 0x15, 0x0B, 0x0D, 0xB5, 0x58, 0x7C, 0x84, 0xB7, 0x1F, - 0x93, 0x0F, 0x9E, 0xDB, 0xF8, 0x88, 0xDF, 0x7E, 0x84, 0xF1, 0x11, 0x06, 0x48, 0xA2, 0xDB, 0x0F, - 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, - 0x51, 0x13, 0x07, 0xFB, 0x1E, 0x9A, 0x77, 0xFC, 0x8E, 0x0D, 0xC1, 0x97, 0x31, 0x00, 0x00, 0x40, - 0x6F, 0xF0, 0xDF, 0x80, 0xB5, 0x73, 0xB6, 0x8D, 0x08, 0x15, 0x1E, 0x6E, 0xB1, 0xD0, 0x50, 0x6B, - 0x10, 0x1F, 0xD5, 0x07, 0xCF, 0x37, 0xCE, 0xAC, 0xBF, 0xFD, 0x08, 0xE2, 0xA3, 0x7A, 0xFB, 0xF1, - 0xA0, 0xDD, 0xD7, 0x55, 0x70, 0x44, 0x27, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, - 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0x50, 0xDF, 0x4B, 0xF3, 0x8E, 0xDE, 0xCA, - 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x31, 0x23, 0xCB, 0xD7, 0x35, 0x7E, 0x05, 0x6F, 0x2C, 0x34, - 0x1A, 0x4D, 0x05, 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x41, 0x7C, 0xC8, 0x37, - 0x5F, 0x7D, 0xB7, 0xD5, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, - 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7D, 0xAF, 0xCD, 0x3B, 0xFA, 0xF9, 0xF1, - 0xE0, 0x4B, 0x19, 0x00, 0x00, 0xA0, 0x37, 0xD8, 0x00, 0x59, 0x5F, 0x1F, 0x1E, 0x29, 0xE2, 0xC3, - 0x2D, 0x1E, 0x1F, 0xD5, 0xDB, 0x8F, 0xC9, 0xF8, 0xA8, 0x79, 0xF3, 0x55, 0xEA, 0xDB, 0x0F, 0x11, - 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, - 0xD3, 0x87, 0xF9, 0x9E, 0x9C, 0x7B, 0x13, 0xD6, 0xD8, 0x17, 0x67, 0x05, 0x5F, 0xCE, 0x00, 0x00, - 0x00, 0xDD, 0xCF, 0x3B, 0x70, 0xD5, 0x50, 0xBB, 0xF1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0xD4, 0xDD, - 0x7E, 0x5C, 0x98, 0xE1, 0xF6, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, - 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBE, 0x97, 0x37, 0xB6, 0x9C, 0x37, 0x61, - 0x01, 0x00, 0x80, 0xDE, 0xE2, 0xED, 0xBD, 0x7C, 0x2C, 0x73, 0x7C, 0xB8, 0xC5, 0xE3, 0xC3, 0x0F, - 0x90, 0x19, 0x39, 0xDD, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xC0, 0xF7, 0xFA, 0x78, 0x13, 0x16, 0x00, 0x00, - 0xE8, 0x35, 0x66, 0x68, 0x81, 0x0D, 0x89, 0xEC, 0xF1, 0xE1, 0x7E, 0xAC, 0xBD, 0xFD, 0xB0, 0xF1, - 0x11, 0xFB, 0xF6, 0xAB, 0xA6, 0xB7, 0x1F, 0xF7, 0x35, 0xBA, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, - 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x78, 0x2F, - 0xC3, 0x9E, 0xBF, 0x9C, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, 0x61, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, - 0x2E, 0xB5, 0x11, 0x91, 0x21, 0x3E, 0xDC, 0xC2, 0xF0, 0x08, 0xE3, 0xE3, 0xE9, 0x19, 0x35, 0xB7, - 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xF1, 0x80, 0x1D, 0xF1, 0x91, 0xCF, 0x64, 0x38, 0xE4, 0x31, - 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x07, 0xF7, 0x12, 0xCD, 0x3B, 0xFE, 0x67, 0xBC, - 0x09, 0x0B, 0x00, 0x00, 0xF4, 0x06, 0xFF, 0x15, 0xBC, 0xFE, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, - 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, 0xF1, 0xD1, 0xE0, 0xD9, 0x0F, 0xF9, 0xA1, 0x83, 0xF7, - 0xAA, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, - 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x87, 0xF6, 0x32, 0xCD, 0x3B, 0xFA, 0xF9, 0x93, 0xC1, 0x97, 0x34, - 0x00, 0x00, 0x40, 0x77, 0xF3, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0xF5, 0x71, 0x91, 0x64, 0x61, 0x78, - 0x84, 0xF1, 0x11, 0xBB, 0xFD, 0xA8, 0x7B, 0xF6, 0xC3, 0x06, 0xC8, 0x29, 0x17, 0x20, 0x4D, 0x6F, - 0x3F, 0x44, 0x48, 0x64, 0x99, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1F, 0xD8, 0x4B, 0xB7, 0x23, 0xAB, 0x4C, 0xF0, 0x25, 0x0D, 0x00, 0x00, 0xD0, - 0xDD, 0xBC, 0xE1, 0x65, 0x83, 0x32, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0x12, 0x20, 0x33, - 0xF4, 0xED, 0x47, 0x2C, 0x3E, 0x5A, 0xDF, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, - 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA8, 0x97, 0x75, 0x63, 0xCB, - 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0D, 0xDE, 0xDE, 0xCB, 0x46, 0xEB, 0xE2, 0x22, 0xC9, 0xAA, - 0xB7, 0x1F, 0x41, 0x7C, 0x24, 0xBD, 0xFD, 0x08, 0x03, 0xE4, 0x07, 0x76, 0xC4, 0x47, 0x7B, 0x93, - 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0xBA, 0x7F, - 0x5D, 0x68, 0xCC, 0x0B, 0x57, 0x1B, 0x73, 0xFC, 0x8E, 0x75, 0xC1, 0x97, 0x35, 0x00, 0x00, 0x40, - 0xF7, 0xF2, 0xF6, 0x2C, 0x18, 0x97, 0x81, 0xD1, 0x6C, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0x59, 0x6F, - 0x3F, 0xEE, 0x89, 0xDE, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2E, 0x3E, 0xDC, 0x7E, 0x6E, - 0xFF, 0x99, 0x38, 0x71, 0xDB, 0xFA, 0xE0, 0xCB, 0x1A, 0x00, 0x00, 0xA0, 0x3B, 0x99, 0xA1, 0xFE, - 0x59, 0x66, 0xF7, 0xDC, 0xFA, 0xC0, 0x68, 0xB5, 0xF0, 0xE6, 0x23, 0x97, 0xDB, 0x0F, 0x11, 0x12, - 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x2E, 0x8C, 0x8F, 0x60, 0xDE, 0x89, 0xDB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, - 0x6E, 0x66, 0x64, 0xE9, 0x80, 0xD9, 0xF1, 0x71, 0x1D, 0x19, 0xCD, 0x16, 0xC6, 0x47, 0xDB, 0xB7, - 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, - 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x5D, 0x2C, 0x3E, 0xFC, 0x00, 0x39, 0x7A, 0x33, 0x6F, 0xC2, - 0x02, 0x00, 0x00, 0xDD, 0xCD, 0x1B, 0x5E, 0x7E, 0x97, 0xD9, 0xFA, 0x21, 0x1D, 0x19, 0x8D, 0x16, - 0x8F, 0x8F, 0x66, 0xB7, 0x1F, 0x61, 0x7C, 0xC8, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, - 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, - 0xC4, 0x87, 0xBF, 0x23, 0x37, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xDD, 0xFC, 0x57, 0xF0, 0xAA, - 0xC8, 0x68, 0x34, 0x1B, 0x1D, 0x8D, 0x6E, 0x3F, 0xAA, 0x01, 0xD2, 0xEA, 0xF6, 0xE3, 0x1B, 0xC4, - 0x47, 0xA6, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, - 0x65, 0x9D, 0x0A, 0x8F, 0x70, 0x2F, 0xF5, 0xBB, 0x07, 0xD1, 0x97, 0x04, 0x5F, 0xDE, 0x00, 0x00, - 0x00, 0xDD, 0xC7, 0x3B, 0x70, 0x75, 0xF2, 0x37, 0x60, 0x45, 0xE3, 0xC3, 0x86, 0x47, 0xA3, 0xDB, - 0x8F, 0x09, 0x17, 0x20, 0x8F, 0x45, 0x6E, 0x3F, 0xC2, 0xF8, 0x70, 0xDB, 0xE0, 0xBE, 0xF5, 0x4A, - 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xD1, 0x11, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x3F, 0x5F, 0x1B, - 0x7C, 0x79, 0x03, 0x00, 0x00, 0x74, 0x1F, 0x6F, 0xDF, 0xE5, 0x27, 0x65, 0x6C, 0xC4, 0x17, 0xC4, - 0x47, 0xA3, 0xDB, 0x8F, 0xC4, 0xCF, 0x7E, 0x7C, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, - 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xC1, - 0x11, 0x1F, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xDD, 0x2E, 0xD1, 0x1B, 0xB0, 0xE2, 0xF1, 0x51, 0x73, - 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xED, 0xED, 0x47, 0x4E, 0xDF, 0x7A, 0x25, - 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, - 0xB2, 0x4E, 0xC5, 0x46, 0x83, 0x79, 0xC7, 0x6F, 0x1F, 0x0A, 0xBE, 0xBC, 0x01, 0x00, 0x00, 0xBA, - 0x8B, 0x39, 0x70, 0xFD, 0x12, 0xB3, 0xF3, 0x53, 0x3A, 0x3A, 0xC2, 0x45, 0xE2, 0xA3, 0x1A, 0x20, - 0x76, 0xD5, 0x6F, 0xBD, 0x0A, 0x03, 0xA4, 0xE6, 0xB5, 0xBB, 0x33, 0xC5, 0xED, 0xC7, 0xCC, 0x7C, - 0x6E, 0x3F, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, - 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0xC8, 0x68, 0x36, 0xEF, 0xC8, 0x9A, 0xB1, 0xE0, 0x4B, 0x1C, 0x00, - 0x00, 0xA0, 0xBB, 0x98, 0x83, 0xCB, 0xD6, 0x9A, 0xED, 0x1F, 0xD5, 0xE1, 0xE1, 0x16, 0x09, 0x8F, - 0x68, 0x7C, 0x84, 0xB7, 0x1F, 0x35, 0x0F, 0x9E, 0x37, 0x7A, 0xF6, 0xC3, 0x0F, 0x10, 0x1B, 0x1F, - 0xDF, 0xCD, 0xE1, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, - 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x08, 0x8C, 0x96, 0x3B, 0xBA, 0x86, 0x37, 0x61, 0x01, - 0x00, 0x80, 0xEE, 0xE4, 0xBF, 0x01, 0x6B, 0xF3, 0xAC, 0x4C, 0xF1, 0x51, 0x7D, 0xF0, 0x5C, 0xDD, - 0x7E, 0x04, 0xF1, 0x51, 0xB9, 0xFD, 0xB0, 0xF1, 0xF1, 0xA0, 0xDD, 0xD7, 0x45, 0x50, 0xA4, 0x99, - 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, - 0xCB, 0x3A, 0x15, 0x17, 0x49, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB7, 0xF2, 0x0E, 0x7C, 0x6E, - 0x28, 0x49, 0x7C, 0xD4, 0x05, 0x48, 0xFC, 0xB5, 0xBB, 0xEE, 0xF6, 0x23, 0x88, 0x8F, 0xDA, 0x37, - 0x5F, 0xD9, 0xF0, 0xC8, 0xE3, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x0A, 0x8B, 0xA4, 0x7B, 0xE1, 0x5A, 0x63, - 0x8E, 0xDD, 0x36, 0x10, 0x7C, 0x99, 0x03, 0x00, 0x00, 0x74, 0x0F, 0x6F, 0xEF, 0x65, 0xF5, 0xAF, - 0xE0, 0x8D, 0x44, 0x87, 0x8C, 0x8F, 0xE8, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0xAA, 0xDE, - 0x7E, 0x04, 0xF1, 0xD1, 0xEE, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, - 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x15, 0x69, 0xF6, 0xFC, 0x67, 0x79, - 0x13, 0x16, 0x00, 0x00, 0xE8, 0x4E, 0x66, 0x8F, 0x3D, 0xEC, 0xB4, 0x88, 0x0F, 0x37, 0x75, 0xFB, - 0x51, 0x7D, 0xED, 0x6E, 0xF5, 0xF6, 0xE3, 0xC2, 0xC8, 0xED, 0x47, 0x10, 0x1F, 0xED, 0xDE, 0x7E, - 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x62, 0x07, - 0xF4, 0x32, 0x4F, 0x05, 0x45, 0x86, 0x79, 0x27, 0x6E, 0x1B, 0x0C, 0xBE, 0xCC, 0x01, 0x00, 0x00, - 0xBA, 0x83, 0x19, 0xE9, 0x9F, 0x5D, 0xF3, 0x0A, 0xDE, 0x48, 0x70, 0x44, 0x97, 0xFE, 0xF6, 0x63, - 0xE6, 0x64, 0x80, 0x3C, 0x60, 0xA7, 0xC2, 0x22, 0xC9, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, - 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0x90, 0xC8, 0x3A, 0xEF, - 0xC8, 0xE7, 0x47, 0x83, 0x2F, 0x75, 0x00, 0x00, 0x80, 0xEE, 0x60, 0x46, 0x96, 0x0E, 0x54, 0xDF, - 0x80, 0x15, 0x09, 0x8E, 0xF8, 0xD4, 0xED, 0x47, 0xFD, 0xB3, 0x1F, 0xE1, 0xED, 0x47, 0x24, 0x3E, - 0xDC, 0xEE, 0xCD, 0x78, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, - 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x44, 0x44, 0x5B, 0xE3, 0x4D, 0x58, 0x00, 0x00, - 0xA0, 0xDB, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0xDE, 0x2F, 0xA3, 0x23, 0x5C, 0xB3, 0xDB, 0x8F, - 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0xC8, 0x7A, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, - 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x05, 0x44, 0xBB, 0x73, 0x6F, - 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x3B, 0x00, 0x00, 0x40, 0xE7, 0x79, 0x23, 0xCB, 0x36, - 0xA8, 0xE8, 0x88, 0x4E, 0xDD, 0x7E, 0xD4, 0x3F, 0xFB, 0xE1, 0x3E, 0x74, 0xD0, 0xBD, 0x7A, 0x37, - 0x12, 0x20, 0xEE, 0x43, 0x07, 0xB3, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, - 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xF1, 0x90, 0xC7, 0x5E, 0xBC, - 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, 0xEE, 0xE2, 0xED, 0xBD, 0x62, 0x54, 0x45, 0x47, 0xB8, 0x66, - 0xB7, 0x1F, 0xD5, 0x67, 0x3F, 0xFC, 0xD7, 0xEE, 0x06, 0xF1, 0xE1, 0xD6, 0xCE, 0xED, 0x87, 0x08, - 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, - 0x3A, 0x15, 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x2E, 0xDE, 0xDE, - 0xC5, 0xE3, 0x2A, 0x3C, 0xC2, 0x35, 0xBD, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC5, 0x87, 0xBA, 0xFD, - 0xB8, 0x27, 0xE5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x0D, 0x39, 0xCF, 0x3B, 0xB6, 0x96, 0x37, 0x61, - 0x01, 0x00, 0x80, 0xEE, 0xE1, 0xBF, 0x01, 0x2B, 0x12, 0x1C, 0xD1, 0xB5, 0xBC, 0xFD, 0xB0, 0xF1, - 0x31, 0xF1, 0xD8, 0x8C, 0xDA, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x60, 0xA7, 0x22, 0xA3, 0xD1, 0x44, - 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0x89, 0x58, 0x28, 0x62, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0E, 0xFE, - 0x1B, 0xB0, 0x76, 0x7C, 0x52, 0xC6, 0x87, 0x5B, 0xCB, 0xDB, 0x8F, 0x30, 0x3E, 0xDA, 0xBD, 0xFD, - 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, - 0x7A, 0x59, 0x27, 0x42, 0xA1, 0xA8, 0x79, 0x47, 0x6F, 0x1E, 0x0F, 0xBE, 0xE4, 0x01, 0x00, 0x00, - 0x3A, 0xCB, 0x8C, 0x2C, 0x5F, 0x67, 0xB6, 0x7D, 0xA4, 0x75, 0x7C, 0xC8, 0xDB, 0x8F, 0x19, 0xF9, - 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, - 0xC4, 0x21, 0xBD, 0xAC, 0x13, 0x91, 0x50, 0xE8, 0xDC, 0x9B, 0xB0, 0xC6, 0xBE, 0x38, 0x2B, 0xF8, - 0xB2, 0x07, 0x00, 0x00, 0xE8, 0x1C, 0x1B, 0x20, 0xEB, 0x55, 0x7C, 0xB8, 0xA9, 0xDB, 0x0F, 0x3F, - 0x40, 0x44, 0x7C, 0x64, 0xBE, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, - 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA1, 0xE8, 0xF1, 0x26, 0x2C, 0x00, - 0x00, 0xD0, 0x2D, 0xBC, 0x03, 0x57, 0x0D, 0x35, 0x8C, 0x0F, 0x1B, 0x1E, 0xF2, 0xF6, 0xE3, 0x09, - 0x1B, 0x1F, 0x2E, 0x40, 0x1E, 0x8D, 0xDC, 0x7E, 0x84, 0xF1, 0xE1, 0xB6, 0x81, 0xF8, 0xA8, 0x9B, - 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xA9, - 0x38, 0x98, 0x8A, 0xBD, 0x70, 0xB5, 0xF1, 0x7E, 0x71, 0xFB, 0x5D, 0xC1, 0x97, 0x3D, 0x00, 0x00, - 0x40, 0xE7, 0x78, 0x7B, 0x2F, 0x1B, 0x6B, 0x18, 0x20, 0x6E, 0x91, 0xDB, 0x0F, 0x17, 0x1F, 0x67, - 0x07, 0x67, 0xD4, 0xDE, 0x7E, 0xA8, 0x67, 0x3F, 0xBE, 0x21, 0x62, 0x23, 0x3E, 0x11, 0x1E, 0x6E, - 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2A, - 0x0C, 0xA6, 0x70, 0xDE, 0xF1, 0xDB, 0x36, 0x04, 0x5F, 0xF6, 0x00, 0x00, 0x00, 0x9D, 0x63, 0x86, - 0xE6, 0xEB, 0xF8, 0xA8, 0xB9, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC6, 0x47, 0x1E, 0xB7, 0x1F, 0x22, - 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, - 0xEB, 0x44, 0x10, 0x4C, 0xF5, 0xBC, 0xE3, 0x7F, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x67, 0x99, - 0x91, 0xFE, 0xD9, 0x66, 0xE7, 0x25, 0x3A, 0x40, 0xEC, 0xA2, 0xDF, 0x7A, 0x75, 0xEE, 0xC9, 0x19, - 0xE6, 0xAC, 0x8D, 0x8F, 0xC9, 0xDB, 0x8F, 0x19, 0xFA, 0xD9, 0x8F, 0x56, 0xB7, 0x1F, 0x22, 0x3C, - 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, - 0x44, 0x0C, 0x74, 0x62, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x74, 0x9C, 0xFF, 0x0A, 0xDE, 0xD8, 0x1B, - 0xB0, 0x6A, 0xBE, 0xF5, 0xCA, 0xEE, 0x9C, 0x0B, 0x10, 0x17, 0x1F, 0xF1, 0xDB, 0x8F, 0x68, 0x7C, - 0x84, 0x01, 0x72, 0x7F, 0x8B, 0xDB, 0x0F, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, - 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x22, 0x04, 0x3A, 0xB6, 0x97, 0x56, 0x99, - 0xE0, 0x4B, 0x1F, 0x00, 0x00, 0xA0, 0x33, 0xFC, 0x37, 0x60, 0x6D, 0xFE, 0xE3, 0x86, 0xF1, 0x51, - 0xB9, 0xFD, 0xB0, 0xF1, 0xD1, 0xE0, 0xF6, 0xC3, 0xC5, 0x47, 0xF5, 0xF6, 0xE3, 0x41, 0xBB, 0xAF, - 0x8B, 0xE8, 0x08, 0x27, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, - 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x45, 0x40, 0x27, 0x37, 0xB6, 0x8C, 0x37, 0x61, 0x01, 0x00, - 0x80, 0xCE, 0xF2, 0x86, 0x97, 0x0D, 0x36, 0xBC, 0xFD, 0x08, 0xE2, 0xA3, 0xE6, 0xF6, 0x23, 0x88, - 0x8F, 0x9A, 0xDB, 0x8F, 0xF0, 0xD9, 0x8F, 0xEF, 0x36, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, - 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA0, - 0xD3, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0xF2, 0x07, 0x00, 0x00, 0x98, 0x7A, - 0xDE, 0xDE, 0xCB, 0x46, 0x1B, 0xDE, 0x7E, 0x6C, 0xBC, 0xA0, 0x26, 0x3E, 0xEA, 0x3E, 0x74, 0x30, - 0xE9, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x75, 0xF8, 0xEF, 0xF8, 0x16, 0xD8, 0x5F, 0x9B, 0xFD, 0xDF, 0xE1, - 0xC4, 0x6D, 0xEB, 0x83, 0x2F, 0x7F, 0x00, 0x00, 0x80, 0xA9, 0xE7, 0xED, 0x9E, 0x3F, 0xAE, 0x6F, - 0x3F, 0x6C, 0x7C, 0x84, 0xDF, 0x7A, 0x55, 0x73, 0xFB, 0x31, 0x33, 0xDD, 0xED, 0x87, 0x08, 0x0F, - 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0xB1, 0x78, 0x20, 0x14, 0x39, 0x71, 0x48, 0x2F, 0xEB, - 0xE4, 0xE1, 0xBF, 0xD3, 0xB3, 0xF1, 0x11, 0xCC, 0x3B, 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x80, - 0xCE, 0x30, 0x43, 0xFD, 0xB3, 0xCC, 0xEE, 0xB9, 0x32, 0x3E, 0x72, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, - 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, - 0x0F, 0xFF, 0x9D, 0xDE, 0x64, 0x7C, 0xF8, 0x01, 0x72, 0xF4, 0xE6, 0x93, 0xC1, 0x1F, 0x01, 0x00, - 0x00, 0x00, 0x53, 0xCB, 0x7F, 0x03, 0xD6, 0xF6, 0x8B, 0x6B, 0x03, 0x24, 0x8C, 0x0F, 0xF9, 0xEC, - 0x47, 0x83, 0xDB, 0x8F, 0x7B, 0xC5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, - 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x6A, 0xE3, - 0xC3, 0xDF, 0x91, 0x1B, 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0C, 0x33, 0xB2, 0x7C, 0x9D, 0xD9, - 0xFA, 0xC1, 0xC8, 0xED, 0x87, 0x8D, 0x8F, 0xD8, 0xED, 0x47, 0xED, 0x9B, 0xAF, 0x44, 0x7C, 0x3C, - 0x60, 0x47, 0x7C, 0x14, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x11, 0x1F, 0x6E, 0x2F, 0xF5, 0xF3, 0x26, 0x2C, 0x00, 0x00, 0xD0, - 0x19, 0xE6, 0xE0, 0xF2, 0xF5, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xEC, 0xC7, - 0xE9, 0x78, 0x80, 0xB8, 0x0F, 0x1D, 0x8C, 0xDF, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, - 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, - 0x84, 0x47, 0x38, 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x00, 0x00, 0x00, 0x60, - 0xEA, 0x78, 0xFB, 0xAE, 0x1E, 0xAD, 0x89, 0x8F, 0x24, 0xCF, 0x7E, 0xB8, 0x35, 0xBA, 0xFD, 0x10, - 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, - 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x88, 0x8E, 0xE8, 0xDC, 0x3F, 0x67, 0xBC, 0x09, 0x0B, 0x00, - 0x00, 0x74, 0x82, 0xB7, 0xF7, 0xF2, 0x93, 0x7E, 0x7C, 0x34, 0xBA, 0xFD, 0x88, 0xC6, 0x87, 0xBA, - 0xFD, 0xF8, 0x76, 0xE4, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, - 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x38, 0xC4, 0xBC, - 0xE3, 0xB7, 0x0F, 0x05, 0x7F, 0x0C, 0x00, 0x00, 0x00, 0x4C, 0x1D, 0x6F, 0xE7, 0x9C, 0xF4, 0xB7, - 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x8E, 0xF8, 0xC8, 0x71, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, - 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x36, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, - 0x82, 0x3F, 0x06, 0x00, 0x00, 0x00, 0xA6, 0x86, 0x39, 0x70, 0xFD, 0x12, 0x6F, 0xFB, 0x27, 0xB2, - 0xDF, 0x7E, 0xDC, 0x13, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, - 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x87, 0x46, 0xC3, - 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xD5, 0xCC, 0xC1, 0x65, 0x6B, 0xBD, 0xAD, 0x1F, 0x6E, 0xEF, - 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, - 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x30, 0x5A, 0xCD, 0xBD, 0x09, 0xEB, 0xF8, - 0x1D, 0x4B, 0x82, 0x3F, 0x0E, 0x00, 0x00, 0x00, 0x8A, 0x77, 0xFE, 0xE0, 0xD2, 0xF5, 0xDE, 0xA6, - 0x0B, 0x33, 0xDE, 0x7E, 0x10, 0x1F, 0xF9, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, - 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC4, 0x45, 0x92, 0xBD, 0x70, 0x0D, 0x6F, 0xC2, - 0x02, 0x00, 0x00, 0x53, 0xEB, 0xFC, 0xFE, 0x6B, 0x06, 0xB3, 0xDD, 0x7E, 0xCC, 0x90, 0xE1, 0xE1, - 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, - 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x8B, 0xA4, 0x7B, 0xFE, 0x0A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xA6, - 0xD6, 0xF9, 0xDD, 0x8B, 0x47, 0xA3, 0xB7, 0x1F, 0xD5, 0x00, 0x89, 0xC5, 0x47, 0xDD, 0xED, 0xC7, - 0xDD, 0xC4, 0x47, 0xFB, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, - 0xE4, 0xE1, 0xBF, 0xD3, 0x13, 0x51, 0x91, 0x72, 0xDE, 0xB1, 0xB5, 0xBC, 0x09, 0x0B, 0x00, 0x00, - 0x4C, 0x1D, 0x6F, 0xF7, 0x42, 0x79, 0xFB, 0x31, 0xE1, 0x02, 0xE4, 0xD1, 0xC8, 0xED, 0x47, 0x18, - 0x1F, 0x6E, 0xF7, 0xEB, 0x6F, 0xBD, 0x22, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x07, 0x45, 0xDA, 0x79, 0x47, 0x3E, - 0x3F, 0x1A, 0xFC, 0x71, 0x00, 0x00, 0x00, 0x50, 0x2C, 0x33, 0xD2, 0x3F, 0xDB, 0xDB, 0x31, 0x27, - 0xDD, 0xB3, 0x1F, 0x0F, 0xCE, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, - 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x98, 0xC8, 0xB4, 0xA3, - 0x6B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x1A, 0x66, 0xFF, 0xD2, 0x81, 0xF3, 0x5B, 0x3E, 0x6C, - 0x03, 0x24, 0xF2, 0xAD, 0x57, 0xCD, 0x6E, 0x3F, 0x1E, 0xB2, 0xF1, 0x21, 0x6E, 0x3F, 0x88, 0x8F, - 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, - 0xD3, 0x13, 0x11, 0xD1, 0xCE, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0x73, 0x76, 0xF0, 0xC7, 0x02, 0x00, - 0x00, 0x40, 0x71, 0xCE, 0xED, 0x5F, 0x7A, 0xD7, 0xF9, 0x4D, 0xEF, 0x9B, 0x7C, 0xF6, 0xA3, 0xE6, - 0xC1, 0xF3, 0x19, 0xB5, 0xCF, 0x7E, 0xB8, 0xF8, 0x78, 0xA0, 0xFE, 0xF6, 0x83, 0xF8, 0x48, 0x33, - 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, - 0x11, 0x10, 0xED, 0xCE, 0xBD, 0x09, 0xEB, 0xD8, 0x6D, 0x03, 0xC1, 0x1F, 0x0B, 0x00, 0x00, 0x00, - 0xC5, 0x39, 0x7F, 0x60, 0xD9, 0x86, 0x9A, 0x07, 0xCF, 0xA3, 0xB7, 0x1F, 0xD1, 0xF8, 0x78, 0xD8, - 0x86, 0x87, 0x0B, 0x90, 0xEF, 0xD4, 0xDE, 0x7E, 0x10, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x21, 0x8F, 0xF1, - 0x26, 0x2C, 0x00, 0x00, 0x30, 0x55, 0xCE, 0xEF, 0x5E, 0x32, 0xEA, 0xC7, 0x47, 0x83, 0xDB, 0x0F, - 0x17, 0x1F, 0xA7, 0xC2, 0xF8, 0x70, 0xB7, 0x1F, 0x7F, 0x4D, 0x7C, 0x64, 0x9B, 0x0A, 0x87, 0x3C, - 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x87, 0x1C, - 0xE7, 0x9D, 0xB8, 0x6D, 0x30, 0xF8, 0x63, 0x01, 0x00, 0x00, 0xA0, 0x38, 0xE7, 0x76, 0x2E, 0x3A, - 0x59, 0x73, 0xFB, 0x11, 0xC4, 0x47, 0xF5, 0xD9, 0x8F, 0x30, 0x3E, 0x62, 0xB7, 0x1F, 0xC4, 0x47, - 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, - 0xE9, 0xE9, 0x68, 0xC8, 0x73, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0xDE, 0xF9, - 0xED, 0x73, 0xAA, 0xF1, 0x51, 0xF7, 0xA1, 0x83, 0x8F, 0xCC, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, - 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, - 0xE9, 0x60, 0xC8, 0x7D, 0x47, 0x3F, 0xCF, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB1, 0xCE, 0xEE, 0x5F, - 0x3A, 0x70, 0x6E, 0xCB, 0x27, 0x63, 0xB7, 0x1F, 0x33, 0x2B, 0xCF, 0x7E, 0x44, 0xE3, 0xC3, 0xED, - 0xDE, 0xCA, 0xED, 0x07, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, - 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x14, 0x8A, 0x9A, 0x7B, 0x13, 0xD6, 0xD8, 0x17, - 0x67, 0x05, 0x7F, 0x3C, 0x00, 0x00, 0x00, 0xE4, 0xEF, 0xDC, 0xDE, 0xE5, 0xEB, 0xCE, 0x6D, 0xFA, - 0x48, 0xEC, 0xF6, 0x63, 0x46, 0x35, 0x3E, 0xAA, 0x01, 0xF2, 0x23, 0x3B, 0xE2, 0x23, 0xE5, 0x54, - 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x44, - 0x24, 0x14, 0xB9, 0x17, 0xAF, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x58, 0xE7, 0xF7, 0x2E, 0x5F, - 0x5F, 0xF7, 0xEC, 0xC7, 0x8F, 0x27, 0x03, 0xA4, 0x7A, 0xFB, 0xF1, 0xED, 0x0B, 0x88, 0x8F, 0x54, - 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, - 0x13, 0x81, 0x50, 0xF4, 0x5E, 0xB8, 0xCA, 0x78, 0xBF, 0xB8, 0xFD, 0xAE, 0xE0, 0x8F, 0x07, 0x00, - 0x00, 0x80, 0xFC, 0x9D, 0xDB, 0x7D, 0xD5, 0x50, 0xCD, 0x9B, 0xAF, 0xC2, 0xF8, 0x88, 0x06, 0xC8, - 0x8F, 0x66, 0x10, 0x1F, 0xA9, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x60, 0x8A, 0xE6, 0x1D, 0xBF, 0x6D, 0x43, 0xF0, 0xC7, - 0x03, 0x00, 0x00, 0x40, 0xFE, 0xCE, 0xED, 0xB8, 0x6C, 0x6C, 0xF2, 0xF6, 0xE3, 0x02, 0x79, 0xFB, - 0xF1, 0x76, 0xE4, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x06, 0x53, 0x35, 0xDE, 0x84, 0x05, 0x00, - 0x00, 0x0A, 0x75, 0x76, 0xDB, 0xFC, 0xE0, 0xF6, 0xC3, 0xC6, 0x87, 0x78, 0xF6, 0xE3, 0xED, 0x1F, - 0x4C, 0xDE, 0x7E, 0x10, 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, - 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0xA3, 0x60, 0x2A, 0xE7, 0x1D, 0xBD, 0x79, 0x3C, 0xF8, - 0xE3, 0x01, 0x00, 0x00, 0x20, 0x5F, 0x66, 0xA8, 0x7F, 0xF6, 0xD9, 0xCD, 0x97, 0x04, 0xF1, 0x51, - 0x7B, 0xFB, 0x71, 0x2A, 0xBC, 0xFD, 0xF8, 0x66, 0xE5, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, - 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x04, - 0x53, 0xBE, 0x97, 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x8C, 0xB3, 0x43, 0x4B, 0x07, 0xDE, - 0xDD, 0xF8, 0xE1, 0x6A, 0x80, 0x9C, 0xB6, 0x01, 0x52, 0xF3, 0xEC, 0xC7, 0xF7, 0x2B, 0xB7, 0x1F, - 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, - 0xF0, 0xDF, 0xE9, 0x89, 0x10, 0xE8, 0xD4, 0xC6, 0x96, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x0C, - 0x1B, 0x20, 0xEB, 0x27, 0x06, 0xDF, 0x57, 0x73, 0xFB, 0x11, 0x7D, 0xFE, 0xC3, 0xDD, 0x7E, 0x10, - 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, - 0x7F, 0xA7, 0x27, 0x22, 0xA0, 0x93, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0x63, - 0x02, 0x00, 0x00, 0x20, 0x3F, 0xEF, 0xEE, 0x59, 0x3A, 0xE8, 0xDF, 0x7E, 0x88, 0xF8, 0x70, 0xB7, - 0x1F, 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, - 0xF2, 0xF0, 0xDF, 0xE9, 0x89, 0x00, 0xE8, 0xF4, 0xDC, 0x3F, 0x17, 0x27, 0x6E, 0x5B, 0x1F, 0xFC, - 0x31, 0x01, 0x00, 0x00, 0x90, 0x9F, 0x89, 0x6D, 0x8B, 0x47, 0xEB, 0xDE, 0x7C, 0x15, 0xDE, 0x7E, - 0xDC, 0x4D, 0x7C, 0x34, 0x9F, 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xE9, 0x65, - 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xE2, 0xF0, 0xDF, 0x25, 0xF3, 0x8E, 0xAF, 0xE5, 0x4D, 0x58, 0x00, - 0x00, 0x20, 0x7F, 0x13, 0x5B, 0xFA, 0xC6, 0xE5, 0xC3, 0xE7, 0xF7, 0xBB, 0x6F, 0xBD, 0x22, 0x3E, - 0x1A, 0x4F, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, - 0x4E, 0x4F, 0x1F, 0xFC, 0xBB, 0x65, 0xDE, 0xD1, 0x9B, 0x4F, 0x06, 0x7F, 0x4C, 0x00, 0x00, 0x00, - 0xE4, 0xC3, 0x0C, 0xF5, 0xCF, 0x7A, 0xF7, 0x99, 0x39, 0x95, 0xF8, 0xB0, 0x8B, 0x3E, 0x7C, 0xFE, - 0xD6, 0x5F, 0x11, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, - 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFD, 0x5D, 0xB5, 0x97, 0x56, 0x9A, 0xE0, 0x8F, 0x0A, - 0x00, 0x00, 0x80, 0x7C, 0xB8, 0x37, 0x60, 0x4D, 0x6C, 0xFC, 0x98, 0x0D, 0x90, 0x0B, 0xCC, 0xE9, - 0x47, 0xEC, 0xDC, 0xED, 0x87, 0x0B, 0x90, 0xFB, 0x6D, 0x80, 0x10, 0x1F, 0x0D, 0xA6, 0xC2, 0x21, - 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xFB, - 0xDD, 0x38, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x7C, 0xDD, 0xC4, 0x13, 0x1F, - 0x30, 0xA7, 0xFD, 0x00, 0x09, 0xBE, 0xFD, 0xEA, 0xE1, 0x99, 0xFE, 0xED, 0x07, 0xF1, 0xA1, 0xA6, - 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, - 0x0E, 0xFA, 0xDD, 0x3A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x74, 0x7D, 0x78, - 0xFB, 0x71, 0xCA, 0xCD, 0xDD, 0x7E, 0x7C, 0x67, 0x06, 0xF1, 0x21, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF9, 0xDD, 0x3C, - 0xF7, 0xCF, 0x3D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x79, 0x9A, 0xD8, 0x79, 0xD5, 0x68, 0xF5, 0xDB, - 0xAF, 0xDC, 0x0D, 0x88, 0x7B, 0xF6, 0xE3, 0x6B, 0xC4, 0x47, 0xFD, 0x54, 0x38, 0xE4, 0x31, 0x15, - 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, 0x01, 0xBF, 0x07, 0xC6, - 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xAE, 0x26, 0xB6, 0x5D, 0x76, 0xB2, 0x7A, 0x03, 0xF2, 0xF0, 0x0C, - 0xF3, 0xF6, 0x7D, 0xC1, 0xED, 0x87, 0x8D, 0x8F, 0x3F, 0x10, 0x1F, 0xC1, 0x54, 0x38, 0xE4, 0x31, - 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0xE1, 0xBE, 0x17, - 0xE6, 0x1D, 0x59, 0x33, 0x16, 0xFC, 0x71, 0x01, 0x00, 0x00, 0xD0, 0xBE, 0x09, 0xFF, 0x0D, 0x58, - 0x93, 0xB7, 0x1F, 0x6F, 0x7E, 0x6D, 0xF2, 0xE6, 0xE3, 0x0D, 0xE2, 0xC3, 0x4E, 0x85, 0x43, 0x1E, - 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xEC, 0x7B, - 0x66, 0x47, 0x6E, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x20, 0x1F, 0x13, 0x43, 0xD7, 0x2F, 0x99, 0x78, - 0xFA, 0x13, 0xE6, 0xCC, 0xA3, 0x95, 0xCF, 0xFF, 0x70, 0xB7, 0x1F, 0x95, 0xF8, 0xB8, 0xC0, 0xBF, - 0xF9, 0x78, 0x83, 0xF8, 0x28, 0x68, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, - 0xDF, 0xE9, 0x89, 0x03, 0x7D, 0xAF, 0xED, 0xA5, 0x7E, 0xF7, 0x20, 0xFA, 0x92, 0xE0, 0x8F, 0x0D, - 0x00, 0x00, 0x80, 0xEC, 0xCE, 0x0C, 0x2D, 0x5B, 0x3B, 0xF1, 0xE4, 0x87, 0x2B, 0x9F, 0x01, 0xF2, - 0x60, 0xE5, 0xB5, 0xBB, 0xD5, 0xDB, 0x8F, 0xAF, 0xD9, 0xF8, 0xF8, 0x47, 0xE2, 0x23, 0xFF, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF3, 0xBD, 0x38, - 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x03, 0x00, 0x00, 0x20, 0x3B, 0xF7, 0x06, - 0xAC, 0x89, 0xC7, 0x2F, 0xF2, 0x03, 0xE4, 0xED, 0x6F, 0x55, 0x9E, 0xFB, 0x20, 0x3E, 0xDC, 0x54, - 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, - 0x41, 0xBE, 0x57, 0xE7, 0xBE, 0x26, 0x78, 0x13, 0x16, 0x00, 0x00, 0xC8, 0xC3, 0xD9, 0x5D, 0xD7, - 0x0C, 0x4E, 0x3C, 0x36, 0xC3, 0x9C, 0x71, 0xCF, 0x7E, 0x04, 0x6F, 0xBD, 0xFA, 0xC3, 0x7A, 0xE2, - 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, - 0xF1, 0x3D, 0xBB, 0xF9, 0xFE, 0xBC, 0xE3, 0xB7, 0x0D, 0x05, 0x7F, 0x6C, 0x00, 0x00, 0x00, 0x64, - 0xF7, 0xEE, 0xB6, 0xC5, 0xA3, 0x13, 0x8F, 0xCF, 0x30, 0xEF, 0x7C, 0xBB, 0x72, 0xFB, 0xF1, 0xE6, - 0x5F, 0x5D, 0x44, 0x7C, 0x14, 0x32, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, - 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x57, 0x89, 0x0F, 0x3F, 0x40, 0x8E, 0xDC, 0xCC, 0x9B, 0xB0, - 0x00, 0x00, 0x40, 0xFB, 0xCE, 0x6D, 0x5F, 0x60, 0x26, 0x1E, 0xB9, 0xC0, 0xBC, 0xE5, 0x5E, 0xBB, - 0xFB, 0xD7, 0xC4, 0x47, 0x31, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, - 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0xE1, 0xEF, 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, - 0x00, 0xB4, 0xC7, 0x0C, 0x5D, 0x3B, 0xFB, 0xDC, 0xB6, 0x4B, 0xCD, 0x3B, 0xDF, 0xB4, 0x01, 0xF2, - 0x75, 0xE2, 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, - 0xA7, 0x0E, 0xF1, 0xBD, 0xBA, 0x58, 0x7C, 0xB8, 0xBD, 0xB4, 0xC2, 0x98, 0x97, 0xBF, 0x38, 0x3B, - 0xF8, 0xE3, 0x03, 0x00, 0x00, 0x20, 0xBD, 0xB3, 0xFB, 0x97, 0x0E, 0xBC, 0xFB, 0xD8, 0x87, 0xCC, - 0xDB, 0x77, 0x5F, 0x64, 0xCE, 0x13, 0x1F, 0x05, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x89, 0xF8, 0x70, 0x7B, 0xE1, 0x6A, 0x63, - 0x8E, 0xDD, 0x36, 0x10, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDE, 0x99, 0x5D, 0x9F, 0xBB, 0xEB, - 0xD4, 0x86, 0x0F, 0x98, 0xF3, 0xFF, 0x44, 0x7C, 0xE4, 0x3F, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, - 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x27, 0xC2, 0x23, 0x98, 0xF7, 0xAF, - 0x4B, 0xCC, 0xE9, 0x9F, 0xDD, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x76, 0xAF, 0x3D, 0x70, 0xED, - 0x86, 0xF1, 0xDD, 0xAB, 0xCD, 0xD9, 0x17, 0xBF, 0x44, 0x7C, 0xE4, 0x3A, 0x15, 0x09, 0x45, 0x4D, - 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0xA7, 0xC3, 0xC3, 0xED, - 0xD4, 0xB3, 0xF3, 0xCC, 0xEF, 0x77, 0xCC, 0x31, 0xBF, 0xDB, 0x77, 0xD3, 0x60, 0xF0, 0xC7, 0x07, - 0x00, 0x00, 0x40, 0x7A, 0xAF, 0xEF, 0xB9, 0x75, 0xF8, 0xF7, 0x07, 0x6F, 0x35, 0xBF, 0xDF, 0xB7, - 0xD2, 0xBC, 0x79, 0xE0, 0x32, 0x73, 0xFE, 0x9F, 0x55, 0x54, 0x24, 0x9D, 0x08, 0x87, 0x3C, 0x26, - 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, - 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0x4E, 0x87, 0xC7, 0x99, 0xBF, 0xEB, 0x33, 0xE3, 0xBB, - 0xE6, 0x9A, 0xDF, 0x6D, 0xB3, 0xF1, 0xE1, 0xB6, 0x67, 0x39, 0xAF, 0xE2, 0x05, 0x00, 0x00, 0xD9, - 0xFD, 0xF3, 0xFF, 0xFE, 0x17, 0x17, 0xFE, 0x76, 0xFB, 0xEA, 0xFB, 0x7E, 0xB7, 0x6D, 0xF1, 0x5B, - 0xBF, 0xDF, 0xB9, 0xD8, 0xB8, 0xBD, 0x7D, 0xE8, 0x72, 0xFF, 0x5B, 0x2D, 0x74, 0x64, 0x34, 0x9A, - 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0xAE, 0x3E, 0x3C, 0xCE, 0xFE, - 0x43, 0x9F, 0x79, 0x73, 0xEF, 0x64, 0x78, 0xBC, 0xBE, 0x75, 0xCE, 0xF8, 0xEF, 0xB6, 0x5D, 0xBA, - 0x7E, 0xB4, 0xFF, 0x3D, 0x7F, 0x14, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDD, 0x7F, 0xBF, 0xFB, - 0x33, 0x17, 0xBF, 0xB6, 0x79, 0xF1, 0x96, 0x30, 0x42, 0xC6, 0x77, 0x2F, 0x36, 0xA7, 0xFF, 0xC6, - 0x1E, 0xD4, 0x65, 0x6C, 0xC4, 0x27, 0xC2, 0x21, 0x8F, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, - 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, 0xDF, - 0xAB, 0xAB, 0x0D, 0x8F, 0xF3, 0xFF, 0x34, 0xDF, 0xBC, 0xB5, 0x3F, 0x72, 0xE3, 0x61, 0xF7, 0xEA, - 0x33, 0x73, 0x7E, 0x34, 0x3E, 0xF4, 0xA9, 0x59, 0xC1, 0x1F, 0x17, 0x00, 0x00, 0x00, 0xF9, 0xF9, - 0xEF, 0xDF, 0x5D, 0xF2, 0x99, 0xD7, 0xB6, 0x2C, 0xFA, 0x59, 0x18, 0x22, 0x6F, 0xEC, 0xB9, 0xCC, - 0x4C, 0xFC, 0xBD, 0x0B, 0x02, 0x15, 0x1E, 0x6E, 0xB1, 0x68, 0xC8, 0x6B, 0x32, 0x1C, 0xF2, 0x98, - 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, - 0x3A, 0xC4, 0xF7, 0xEA, 0x26, 0xC3, 0xC3, 0xFB, 0xE7, 0xF9, 0xE6, 0x9D, 0x91, 0x79, 0x35, 0xE1, - 0xF1, 0xDA, 0xE6, 0x4B, 0xF7, 0x8F, 0x6F, 0xE9, 0xE3, 0xB5, 0xBB, 0x00, 0x00, 0xA0, 0x78, 0xFF, - 0xEB, 0xA1, 0xCB, 0xBE, 0xF8, 0xBB, 0x6D, 0x8B, 0x7E, 0x19, 0x86, 0x88, 0x7B, 0x3E, 0xE4, 0xEC, - 0x4F, 0x5D, 0x1C, 0x10, 0x1F, 0xF5, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, - 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0x51, 0x7D, 0xC0, 0x3C, 0x08, 0x8F, 0xD7, - 0xB7, 0x5E, 0x3A, 0xF6, 0xDA, 0x96, 0x4B, 0x79, 0xDD, 0x2E, 0x00, 0x00, 0x98, 0x5A, 0xA3, 0xFD, - 0xFD, 0x7F, 0xF4, 0xAB, 0x27, 0x2E, 0xFB, 0x3F, 0xA2, 0xCF, 0x87, 0xBC, 0x35, 0xEC, 0x1E, 0x54, - 0x17, 0xD1, 0x90, 0xD7, 0x64, 0x38, 0xE4, 0x31, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, - 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x55, 0xC2, 0x23, 0xFE, - 0x80, 0xB9, 0x8D, 0x8E, 0x5F, 0xBE, 0xBE, 0x7D, 0xEE, 0xBA, 0xE0, 0x8F, 0x00, 0x00, 0x00, 0x80, - 0xCE, 0xF8, 0xD5, 0x23, 0x57, 0x7E, 0xE0, 0x37, 0x4F, 0x2E, 0xDE, 0x30, 0x19, 0x22, 0x8B, 0xCC, - 0xE9, 0xBF, 0xB9, 0xCC, 0x78, 0xFF, 0x2A, 0x02, 0xA2, 0x9D, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, - 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, - 0xDF, 0xAB, 0x6B, 0xFC, 0x80, 0x39, 0xCF, 0x79, 0x00, 0x00, 0x80, 0xAE, 0x72, 0xF2, 0x91, 0xC5, - 0xF3, 0x5F, 0x7D, 0x66, 0xF1, 0x0E, 0x17, 0x20, 0x6E, 0xE3, 0xBB, 0x17, 0x99, 0x33, 0x7F, 0x67, - 0x0F, 0xE2, 0x2A, 0x26, 0xD2, 0x4E, 0x86, 0x43, 0x1E, 0x8B, 0x47, 0x43, 0x5E, 0x53, 0x91, 0x50, - 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x73, 0xEA, 0x01, - 0xF3, 0xDF, 0x6D, 0xBD, 0x74, 0xF0, 0xBF, 0x7D, 0x67, 0xDE, 0x07, 0x82, 0x2F, 0x73, 0x00, 0x00, - 0x80, 0xEE, 0xF3, 0xAB, 0x8D, 0x8B, 0xD6, 0xBC, 0xFA, 0xCC, 0xC2, 0x9F, 0x87, 0x21, 0xF2, 0xC6, - 0x9E, 0xC5, 0xE6, 0xDD, 0x7F, 0xB0, 0x87, 0x7D, 0x15, 0x16, 0x49, 0x26, 0xC3, 0x21, 0x8F, 0xA9, - 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, - 0x83, 0x7C, 0xAF, 0xCD, 0xFB, 0xE7, 0x05, 0x75, 0x0F, 0x98, 0xDB, 0x8D, 0xFE, 0x7F, 0x8F, 0x5E, - 0x3A, 0x27, 0xF8, 0xB2, 0x06, 0x00, 0x00, 0xE8, 0x6E, 0xFE, 0xF3, 0x21, 0x83, 0x8B, 0xFF, 0xE2, - 0x77, 0xDB, 0x16, 0xFE, 0x32, 0x0C, 0x91, 0x37, 0x0F, 0x2C, 0x36, 0x67, 0x7F, 0x6A, 0x0F, 0xFE, - 0x2A, 0x32, 0x1A, 0x4D, 0x86, 0x43, 0x1E, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, - 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x87, 0xF9, 0x5E, 0xDB, 0xE9, 0xBF, 0xE9, 0xAB, - 0x79, 0xC0, 0xFC, 0xB5, 0x67, 0xE6, 0x1C, 0xE3, 0x01, 0x73, 0x00, 0x00, 0xD0, 0xB3, 0x6C, 0x88, - 0xFC, 0xBB, 0x57, 0x37, 0x2F, 0xBA, 0xE7, 0x77, 0xDB, 0x16, 0xBD, 0x15, 0x86, 0xC8, 0xDB, 0x87, - 0x12, 0x3E, 0xA8, 0x2E, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, - 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xC3, 0x7C, 0x2F, 0x6D, 0xE2, 0xEF, 0xE7, 0xF3, - 0x80, 0x39, 0x00, 0x00, 0x28, 0xAF, 0x93, 0xF7, 0x2E, 0xFC, 0xC8, 0x2B, 0x9B, 0x16, 0x3F, 0x1A, - 0x46, 0x88, 0x7B, 0x3E, 0xA4, 0xE9, 0x83, 0xEA, 0x32, 0x1C, 0xF2, 0x98, 0x0A, 0x87, 0x3C, 0xA6, - 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x3E, 0xD0, 0xF7, 0xCA, - 0xCE, 0xFE, 0xC3, 0x7C, 0xF3, 0xE6, 0x5E, 0xF7, 0xED, 0x56, 0x95, 0xF8, 0xE0, 0x01, 0x73, 0x00, - 0x00, 0x50, 0x6A, 0xFF, 0xE3, 0xFE, 0xCF, 0x2C, 0x7A, 0x75, 0xF3, 0xA2, 0xE7, 0xA2, 0x21, 0x32, - 0xF1, 0xF7, 0x36, 0x0C, 0x88, 0x8F, 0x84, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, - 0x7D, 0xA8, 0xEF, 0x85, 0x9D, 0xFF, 0xA7, 0x05, 0xE6, 0xED, 0x83, 0x7D, 0x41, 0x78, 0x04, 0x37, - 0x1F, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0xA6, 0x8B, 0x5F, 0x3E, 0xB6, 0xF8, 0x96, 0xD7, 0xB7, 0x2E, - 0x7C, 0x39, 0x0C, 0x11, 0xF7, 0xA0, 0xBA, 0xFF, 0x7C, 0x88, 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, - 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xF6, - 0xDD, 0x3E, 0xF7, 0x80, 0xF9, 0xA9, 0x67, 0xA3, 0xE1, 0xE1, 0xC7, 0x07, 0x0F, 0x98, 0x03, 0x00, - 0x80, 0xE9, 0xC7, 0x3D, 0xA8, 0xFE, 0xDB, 0xA7, 0x17, 0x7F, 0xF5, 0xF5, 0xAD, 0x8B, 0x7E, 0xE3, - 0x22, 0xE4, 0x77, 0x3B, 0x16, 0x9A, 0x37, 0xF7, 0x2F, 0x32, 0xE7, 0xFE, 0xD1, 0x1D, 0xEC, 0x55, - 0x44, 0x64, 0x5D, 0x3C, 0x1A, 0xF2, 0x9A, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, - 0xFC, 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xED, 0xAB, 0x3C, 0x60, 0x3E, 0x19, 0x1E, 0xAF, 0x3D, 0x33, - 0x97, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x7E, 0xF5, 0xC8, 0xBC, 0x0F, 0xFC, 0xE6, 0xE9, 0x85, 0xF7, - 0xBD, 0xBE, 0x75, 0xC1, 0x5B, 0x2E, 0x42, 0xDC, 0xDE, 0x1E, 0x59, 0x6C, 0xBC, 0x7F, 0x51, 0x31, - 0x91, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, - 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xCD, 0xAB, 0x3C, 0x60, 0x1E, 0x3E, 0xE7, 0x61, 0xC3, 0x63, 0xCB, - 0x5C, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x88, 0x1B, 0xFB, 0x4F, 0x9F, 0xB9, 0xF8, 0x95, 0xCD, 0x0B, - 0xB7, 0x84, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCD, 0xA9, 0xE7, 0xDA, 0x09, 0x11, 0x15, 0x0E, 0x79, - 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC0, 0xEF, - 0xD6, 0xD5, 0x3E, 0x60, 0x3E, 0xD7, 0xBC, 0xBE, 0x75, 0xEE, 0xB8, 0xFD, 0x91, 0x07, 0xCC, 0x01, - 0x00, 0x00, 0x9A, 0xF9, 0x9F, 0x0F, 0x2D, 0x5A, 0xFA, 0xEA, 0xE6, 0x85, 0x3F, 0x0B, 0x43, 0x64, - 0x7C, 0x68, 0x91, 0x39, 0x33, 0xEA, 0x0E, 0xFE, 0x2A, 0x32, 0x1A, 0x2D, 0x1E, 0x0D, 0x79, 0x4D, - 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC8, 0xEF, 0xC6, - 0xD5, 0x3F, 0x60, 0x6E, 0xB7, 0x75, 0x0E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA4, 0xF1, 0xBF, 0x1E, - 0x5A, 0xF0, 0xC5, 0xD7, 0xB6, 0x2E, 0xF8, 0x65, 0x18, 0x22, 0x6F, 0xEC, 0x5D, 0x64, 0xCE, 0xFE, - 0xC4, 0x45, 0x80, 0x0A, 0x8E, 0xE8, 0xE2, 0xD1, 0x90, 0xD7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, - 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x07, 0xFD, 0x6E, 0x9B, 0x7E, 0xC0, 0x7C, 0x2E, 0x0F, - 0x98, 0x03, 0x00, 0x00, 0x64, 0xE5, 0x1E, 0x54, 0xFF, 0xF5, 0xC6, 0x45, 0x7F, 0x19, 0x7D, 0x3E, - 0xC4, 0x3D, 0xA8, 0x7E, 0xFE, 0x67, 0x2A, 0x3C, 0xDC, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, - 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0x61, 0xBF, 0xDB, 0xC6, 0x03, 0xE6, - 0x00, 0x00, 0x00, 0x05, 0x72, 0x0F, 0xAA, 0xFF, 0xFA, 0xC9, 0x85, 0x1B, 0xC2, 0x08, 0x71, 0x7B, - 0xE7, 0x70, 0xFC, 0xF9, 0x10, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, - 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xD8, 0xEF, 0xA6, 0xF1, 0x80, 0x39, 0x00, 0x00, 0xC0, 0x14, - 0x3A, 0xF9, 0xC8, 0xE2, 0xF9, 0xBF, 0xDD, 0xB4, 0x60, 0x47, 0x18, 0x21, 0xE1, 0x83, 0xEA, 0x3A, - 0x1C, 0xF2, 0x58, 0x3C, 0x10, 0x8A, 0x9C, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, - 0x03, 0x7F, 0xB7, 0x8C, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x3A, 0xE8, 0x57, 0x1B, 0x17, 0xAD, 0x79, - 0x65, 0xD3, 0x82, 0x9F, 0x57, 0x42, 0x64, 0x81, 0x19, 0x1F, 0x5A, 0x68, 0xCE, 0x8C, 0xBA, 0x43, - 0xBC, 0x8A, 0x88, 0xAC, 0x8B, 0x07, 0x42, 0x91, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, - 0x3D, 0x7D, 0xE8, 0xEF, 0x86, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x40, 0x97, 0xA8, 0x7C, 0x90, 0xE1, - 0xC2, 0xAF, 0xBE, 0xB6, 0x75, 0xFE, 0x2F, 0x5D, 0x84, 0xB8, 0xBD, 0xB9, 0x7F, 0xA1, 0x39, 0xFB, - 0x13, 0x77, 0xA0, 0x57, 0x41, 0x91, 0x66, 0xF1, 0x40, 0x28, 0x72, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, - 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFE, 0x9D, 0x5E, 0xA3, 0x07, 0xCC, 0x4F, 0x7E, 0xE7, 0x53, 0xF3, - 0x83, 0x2F, 0x01, 0x00, 0x00, 0x00, 0x74, 0xC2, 0x60, 0xFF, 0xA7, 0xFE, 0xDD, 0xAB, 0x9B, 0x16, - 0xDC, 0xF3, 0xFA, 0xD6, 0xF9, 0x6F, 0x85, 0x21, 0xF2, 0xF6, 0x88, 0x7B, 0x50, 0x5D, 0x85, 0x45, - 0x92, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFF, - 0x9D, 0x9E, 0x7A, 0xC0, 0xFC, 0xB7, 0x1B, 0xE7, 0xDE, 0x10, 0xFC, 0x23, 0x0F, 0x00, 0x00, 0x80, - 0x6E, 0xF0, 0xFF, 0x7E, 0x63, 0xF1, 0x87, 0x7F, 0xF3, 0xD4, 0x82, 0x47, 0xC3, 0x08, 0x71, 0x3B, - 0xF5, 0xDC, 0x22, 0xE3, 0xFD, 0x8B, 0x8A, 0x8C, 0x46, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, - 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xFE, 0x3B, 0xB9, 0xF8, 0x03, 0xE6, 0xEE, 0x39, 0x8F, - 0x57, 0x9F, 0xBE, 0xF4, 0x3F, 0x06, 0xFF, 0x88, 0x03, 0x00, 0x00, 0xA0, 0x1B, 0xFD, 0x8F, 0xFB, - 0x17, 0x2C, 0x7A, 0x75, 0xF3, 0x82, 0x9F, 0x85, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCC, 0xE9, 0xBF, - 0x75, 0x07, 0x61, 0x15, 0x1C, 0xD1, 0xA9, 0x48, 0x28, 0x6A, 0xB1, 0x03, 0x7A, 0x99, 0x27, 0x0F, - 0xFF, 0x9D, 0x9E, 0x0E, 0x80, 0x4E, 0xAD, 0xEE, 0x01, 0xF3, 0xCD, 0x73, 0xDE, 0xB2, 0x3F, 0xF2, - 0x80, 0x39, 0x00, 0x00, 0x40, 0x2F, 0xF9, 0xE5, 0xC3, 0xF3, 0x6F, 0x79, 0x75, 0xCB, 0x82, 0x97, - 0xC3, 0x10, 0x71, 0x0F, 0xAA, 0x4F, 0xFC, 0x57, 0x77, 0xF8, 0x27, 0x3E, 0xA6, 0x6C, 0xF2, 0xF0, - 0xDF, 0xE9, 0xE9, 0x08, 0xE8, 0xC4, 0x1A, 0x3D, 0x60, 0x3E, 0xBE, 0xA5, 0x6F, 0x76, 0xF0, 0x8F, - 0x31, 0x00, 0x00, 0x00, 0x7A, 0x49, 0xF8, 0xA0, 0x7A, 0xF4, 0xF9, 0x10, 0xF7, 0xA0, 0xFA, 0xB9, - 0x7F, 0x74, 0x21, 0x40, 0x7C, 0x14, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x74, 0x08, 0x4C, 0xF5, 0x1A, - 0x3D, 0x60, 0xFE, 0xEB, 0xC7, 0x3E, 0x7D, 0x55, 0xF0, 0x8F, 0x2E, 0x00, 0x00, 0x00, 0x7A, 0x99, - 0xFB, 0x20, 0xC3, 0xDF, 0x6C, 0x9C, 0x7F, 0x5F, 0xED, 0x83, 0xEA, 0x0B, 0xCD, 0xF9, 0x9F, 0xA9, - 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x63, 0x60, 0xAA, 0x17, - 0x7F, 0xC0, 0xFC, 0xF5, 0x6D, 0x73, 0x4E, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x40, 0x49, 0xFD, 0xDB, - 0x83, 0x7D, 0x7D, 0xAF, 0x6C, 0x5E, 0xB0, 0xA5, 0x12, 0x21, 0xF3, 0xCD, 0xEF, 0x77, 0xCD, 0x37, - 0xA7, 0x9E, 0x5B, 0x68, 0xBC, 0x7F, 0x51, 0xC1, 0x90, 0xE7, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, - 0xFF, 0x4E, 0x4F, 0xC7, 0xC0, 0x54, 0x8E, 0x07, 0xCC, 0x01, 0x00, 0x00, 0xA6, 0xB1, 0xFF, 0xF9, - 0xD0, 0xBC, 0xA5, 0xAF, 0x6E, 0xEE, 0xFB, 0x99, 0x8B, 0x90, 0x30, 0x44, 0xCE, 0x8C, 0xBA, 0x03, - 0xB4, 0x8A, 0x87, 0x76, 0x17, 0x3B, 0xA0, 0x97, 0x79, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x20, 0x98, - 0xAA, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xAA, 0x7E, 0xF5, 0x58, 0xDF, 0x5F, 0x54, 0x3E, 0xC8, - 0xB0, 0x12, 0x22, 0xE3, 0x43, 0xF6, 0xC0, 0xF8, 0x13, 0x77, 0x98, 0x56, 0x21, 0x91, 0x65, 0xB1, - 0x03, 0x7A, 0x99, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x82, 0xA9, 0x18, 0x0F, 0x98, 0x03, 0x00, - 0x00, 0x40, 0x72, 0x0F, 0xAA, 0xFF, 0x7A, 0x63, 0xDF, 0x5F, 0xBE, 0xBE, 0xB5, 0xEF, 0xAD, 0x30, - 0x44, 0xDE, 0xDC, 0x6F, 0x0F, 0x90, 0x6D, 0x3F, 0x1F, 0x22, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, - 0x77, 0x7A, 0x3A, 0x0C, 0x8A, 0x1E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x20, 0x11, 0xFF, 0x41, 0xF5, - 0xA7, 0xFA, 0x1E, 0x0D, 0x23, 0xC4, 0xCD, 0x3D, 0xA8, 0x9E, 0xED, 0xF9, 0x10, 0x71, 0x48, 0x2F, - 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x71, 0x50, 0xF4, 0xCE, 0xFC, 0xDD, 0x7C, 0x1E, 0x30, 0x07, - 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x41, 0xF5, 0x57, 0x9F, 0x99, 0x3F, 0x12, 0x46, 0x48, 0xFA, 0x07, - 0xD5, 0xC5, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x41, 0x91, 0x73, 0xCF, 0x79, - 0xF0, 0x80, 0x39, 0x00, 0x00, 0x00, 0xDA, 0xF2, 0xAB, 0x27, 0xFA, 0xD6, 0x44, 0x1F, 0x54, 0x77, - 0xCF, 0x87, 0xB4, 0x7E, 0x50, 0x3D, 0x76, 0x40, 0x2F, 0xF3, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x81, - 0x50, 0xD4, 0xCE, 0xFD, 0x94, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x90, 0xA3, 0xCA, 0x07, 0x19, 0xF6, - 0x7D, 0x35, 0xFA, 0xA0, 0xBA, 0x7B, 0x3E, 0x44, 0x3F, 0xA8, 0x1E, 0x3B, 0xA0, 0x97, 0x79, 0xF2, - 0xF0, 0xDF, 0xE9, 0xE9, 0x48, 0x28, 0x62, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x42, 0xB9, 0xE7, - 0x43, 0x5E, 0xDD, 0xD4, 0x77, 0x4F, 0xF4, 0x41, 0xF5, 0xB7, 0x0E, 0x46, 0x1F, 0x54, 0x17, 0x87, - 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0A, 0x79, 0x2F, 0x7C, 0xC0, 0x3C, 0xFA, 0x9C, - 0x87, 0x1D, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA0, 0x18, 0x47, 0xBF, 0xB1, 0xF8, 0xC3, 0xAF, 0x6C, - 0xEE, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0x3B, 0x87, 0xEC, 0xC1, 0xF4, 0x5F, 0xC4, 0x41, 0xBD, 0x8C, - 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x42, 0xDE, 0x53, 0x0F, 0x98, 0xFF, 0xFA, 0xE1, 0x4B, 0x6F, - 0x0E, 0xFE, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xE3, 0x3F, 0xA8, 0xEE, 0x9E, 0x0F, 0xD9, 0xDE, - 0x67, 0xDC, 0x7E, 0xBF, 0xB3, 0xCF, 0x9C, 0x7A, 0xCE, 0x1E, 0x54, 0xD5, 0xA1, 0xBD, 0x2C, 0x93, - 0x87, 0xFF, 0x4E, 0xAF, 0x3E, 0x14, 0xF2, 0x9E, 0x7A, 0xC0, 0xFC, 0xD7, 0x83, 0x73, 0xFE, 0xB7, - 0xE0, 0x1F, 0x05, 0x00, 0x00, 0x00, 0x60, 0xEA, 0xFC, 0xF2, 0xE1, 0xB9, 0xB7, 0xBC, 0xB6, 0xB5, - 0xEF, 0x97, 0x61, 0x88, 0x8C, 0xEF, 0x9E, 0x6F, 0x26, 0xFE, 0xAB, 0x3D, 0x18, 0xAB, 0x03, 0x7C, - 0x2F, 0x4F, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0C, 0x79, 0x4D, 0x3D, 0x60, 0xFE, 0xDA, 0xA6, 0x39, - 0x77, 0xF3, 0x80, 0x39, 0x00, 0x00, 0x00, 0x3A, 0xCA, 0x7F, 0x50, 0xFD, 0xC9, 0xBE, 0xAF, 0xBE, - 0xBE, 0x65, 0xDE, 0x5B, 0x61, 0x88, 0xBC, 0xB9, 0x6F, 0xBE, 0x39, 0xFB, 0x13, 0x7B, 0x48, 0x56, - 0x87, 0xF9, 0x5E, 0x9B, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x1A, 0xF2, 0x98, 0x7A, 0xC0, 0xFC, 0xD5, - 0xCD, 0x73, 0x37, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xBA, 0x8A, 0x7B, 0x50, 0xFD, 0xB7, 0x9B, - 0xFA, 0xEE, 0x09, 0x23, 0xC4, 0xED, 0xED, 0x61, 0xF7, 0xA0, 0xBA, 0x38, 0xD4, 0xF7, 0xCA, 0xE4, - 0xE1, 0xBF, 0xD3, 0xD3, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0xC9, 0x3D, - 0x1F, 0xF2, 0xCA, 0xA6, 0xBE, 0x2D, 0xD1, 0x10, 0x71, 0xCF, 0x87, 0xF4, 0xDC, 0x83, 0xEA, 0xF2, - 0xF0, 0xDF, 0xE9, 0xE9, 0x78, 0x68, 0x77, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x9E, 0x57, 0xF9, - 0x20, 0xC3, 0x79, 0x35, 0x0F, 0xAA, 0x9F, 0xFE, 0x5B, 0x7B, 0xE0, 0x55, 0x87, 0xFD, 0x6E, 0x9B, - 0x3C, 0xFC, 0x77, 0x7A, 0xF5, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xA5, 0xF3, - 0xAB, 0xC7, 0xFA, 0xFE, 0x22, 0xFE, 0xA0, 0x7A, 0x57, 0x3F, 0x1F, 0x22, 0x0F, 0xFF, 0x9D, 0x9E, - 0x0E, 0x88, 0xAC, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0x94, 0x9A, 0x7B, 0x50, 0xFD, 0xB5, 0x2D, - 0xF3, 0xBE, 0x1E, 0x7F, 0x50, 0xFD, 0xDC, 0x3F, 0xDA, 0xC3, 0xB5, 0x8A, 0x80, 0x4E, 0x4D, 0x1E, - 0xFE, 0x3B, 0x3D, 0x1D, 0x11, 0x59, 0xC6, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x98, 0x56, 0xDC, 0x83, - 0xEA, 0xBF, 0x79, 0xAA, 0xEF, 0xC7, 0x61, 0x84, 0xB8, 0x75, 0xCD, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, - 0xE9, 0xE9, 0x90, 0x48, 0xBB, 0x46, 0x0F, 0x98, 0xBF, 0xB6, 0xE5, 0xD2, 0x81, 0xE0, 0x7F, 0x1A, - 0x00, 0x00, 0x00, 0xA0, 0xBC, 0xFC, 0x0F, 0x32, 0xDC, 0xD4, 0x37, 0x12, 0x46, 0x48, 0xF8, 0x41, - 0x86, 0x1D, 0x7B, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x13, 0x69, 0xA7, 0x1E, 0x30, 0x7F, - 0xE5, 0xC9, 0x39, 0xB7, 0x07, 0xFF, 0x53, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x87, 0x7B, 0x50, 0xFD, - 0xB5, 0x2D, 0xF3, 0x8E, 0x85, 0x21, 0xE2, 0x9E, 0x0F, 0x39, 0x33, 0x6A, 0x0F, 0xDF, 0x2A, 0x12, - 0x8A, 0x9A, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x26, 0xD2, 0x4C, 0x3D, 0x60, 0xFE, 0xCA, 0xA6, 0xB9, - 0xFF, 0x57, 0xF0, 0xB7, 0x1E, 0x00, 0x00, 0x00, 0x98, 0x9E, 0xC2, 0x0F, 0x32, 0x8C, 0x3E, 0xA8, - 0xFE, 0xC6, 0x9E, 0x29, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x14, 0x49, 0x17, 0x7F, - 0xC0, 0xDC, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x98, 0xF0, 0x83, 0x0C, 0xE3, 0x0F, 0xAA, - 0x17, 0xF6, 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2A, 0x92, 0x8C, 0x07, 0xCC, 0x01, 0x00, - 0x00, 0x80, 0x0C, 0x5E, 0x79, 0x72, 0xCE, 0xA5, 0xF1, 0x0F, 0x32, 0x74, 0x0F, 0xAA, 0xE7, 0xFA, - 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2C, 0x5A, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, - 0x1C, 0xFC, 0x7A, 0x63, 0xDF, 0x55, 0xAF, 0x6E, 0x9E, 0xF7, 0x4F, 0x61, 0x84, 0x84, 0x0F, 0xAA, - 0xCB, 0xA0, 0x48, 0x33, 0x79, 0xF8, 0xEF, 0xF4, 0xEA, 0xC3, 0x22, 0xC9, 0x78, 0xC0, 0x1C, 0x00, - 0x00, 0x00, 0xC8, 0xD9, 0x2F, 0x1F, 0x9E, 0x7B, 0x4B, 0xFC, 0x83, 0x0C, 0x33, 0x3F, 0xA8, 0x2E, - 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x8B, 0x66, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, 0x40, 0xFE, - 0x07, 0x19, 0x6E, 0xEE, 0xFB, 0x3F, 0xE3, 0xCF, 0x87, 0xA4, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, - 0x3D, 0x1D, 0x18, 0x8D, 0xA6, 0x1E, 0x30, 0xFF, 0xED, 0xC6, 0x4B, 0xBF, 0xC5, 0x03, 0xE6, 0x00, - 0x00, 0x00, 0x40, 0x01, 0xC2, 0x07, 0xD5, 0xC3, 0x08, 0x71, 0x7B, 0xEB, 0x40, 0x82, 0x07, 0xD5, - 0xE5, 0xE1, 0xBF, 0xD3, 0xD3, 0x91, 0xA1, 0xE6, 0x9E, 0xF3, 0x88, 0x3F, 0x60, 0xFE, 0xCA, 0x53, - 0x73, 0xB6, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x00, 0x30, 0x05, 0xDC, 0x07, 0x19, 0xBE, 0xBE, 0x75, - 0xDE, 0xFE, 0x68, 0x88, 0xBC, 0x73, 0xA8, 0xC1, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xD0, - 0x88, 0x4F, 0x3D, 0x60, 0xFE, 0xDA, 0xE6, 0x39, 0x3F, 0xE1, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, - 0x03, 0xDC, 0x07, 0x19, 0xC6, 0x1F, 0x54, 0x3F, 0xFD, 0xB7, 0xF6, 0xF0, 0x5E, 0x82, 0xF8, 0x70, - 0x0F, 0x98, 0xD7, 0x3C, 0xE7, 0xC1, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x40, 0x77, 0x88, 0x7F, 0x90, - 0xE1, 0xF8, 0xEE, 0x3E, 0x33, 0xF1, 0xF7, 0xEE, 0x20, 0xAF, 0x02, 0xA0, 0x93, 0xAB, 0x0F, 0x8D, - 0xF8, 0xDC, 0x03, 0xE6, 0x6F, 0x0C, 0xD5, 0x3F, 0x60, 0x3E, 0xDA, 0xFF, 0x9E, 0x3F, 0x0A, 0x7E, - 0xBB, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xCD, 0x7F, 0x50, 0x7D, 0xCB, 0xBC, 0xAF, 0x57, 0x1E, 0x54, - 0xB7, 0x07, 0x78, 0xBB, 0x37, 0xF7, 0xF5, 0xD9, 0x03, 0xBD, 0x3B, 0xD8, 0xAB, 0x18, 0x98, 0xEA, - 0xD5, 0xC7, 0x46, 0x74, 0xEE, 0x83, 0x04, 0x79, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xE8, 0x31, 0xEE, - 0x41, 0xF5, 0x57, 0x36, 0xCD, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0xDB, 0xC3, 0xF3, 0xED, 0x01, 0x5F, - 0x45, 0xC1, 0x54, 0x4D, 0x47, 0x87, 0x1B, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x00, 0x25, 0xF0, 0x6F, - 0x1B, 0xFA, 0xFA, 0x5E, 0xDB, 0x32, 0xF7, 0x27, 0x61, 0x84, 0xFC, 0x7E, 0xE7, 0x3C, 0x73, 0xEA, - 0xD9, 0xF9, 0xF6, 0xC0, 0xAF, 0x02, 0xA1, 0xC8, 0x35, 0x0E, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, - 0x00, 0x4A, 0xE6, 0x57, 0x4F, 0x5C, 0xBA, 0xC6, 0x86, 0xC8, 0xB1, 0x68, 0x88, 0x9C, 0xF9, 0x3B, - 0x17, 0x01, 0x2A, 0x16, 0xF2, 0x5E, 0x7D, 0x78, 0xB8, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0x50, - 0x62, 0xEE, 0x01, 0xEE, 0xDF, 0x3E, 0x39, 0xF7, 0xAB, 0xAF, 0x6F, 0x9B, 0x37, 0x1E, 0x86, 0x88, - 0x7B, 0x50, 0xBD, 0xD8, 0xE7, 0x43, 0xEA, 0xC3, 0x83, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x69, - 0xA4, 0xF2, 0x41, 0x86, 0x73, 0xEF, 0x79, 0x7D, 0xCB, 0xDC, 0x9A, 0x07, 0xD5, 0xCF, 0xFD, 0x34, - 0xEF, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, 0xA6, 0xB1, 0x57, 0x9E, 0x9C, - 0x73, 0xA9, 0x7A, 0x50, 0x3D, 0x9F, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, - 0xE0, 0x7B, 0x6D, 0x47, 0xDF, 0x40, 0xE5, 0x83, 0x0C, 0x27, 0x43, 0xE4, 0x8D, 0x3D, 0x7D, 0xFE, - 0x33, 0x22, 0xD9, 0xDE, 0x9A, 0x55, 0x79, 0xB8, 0xDC, 0x3D, 0xE3, 0x11, 0xBF, 0xF1, 0xE0, 0x01, - 0x73, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x57, 0x9E, 0x99, 0x73, 0xDB, 0xEB, 0xDB, 0xE7, 0x9E, 0x8C, - 0x86, 0x48, 0x18, 0x23, 0xEF, 0x1C, 0x9A, 0x6F, 0x4E, 0xFF, 0xCD, 0x7C, 0xFF, 0x79, 0x91, 0xF8, - 0x0D, 0x89, 0xFB, 0xFF, 0xDD, 0xBF, 0xEE, 0xFE, 0xFD, 0x77, 0x0E, 0xF5, 0xD5, 0x3C, 0xDF, 0x11, - 0xCE, 0x3D, 0x60, 0x6E, 0x7F, 0xEE, 0x75, 0xC1, 0x5F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xDC, - 0x1B, 0xB3, 0x5E, 0x79, 0x66, 0xEE, 0x70, 0x3C, 0x44, 0x5A, 0x2E, 0x16, 0x1D, 0x95, 0xCD, 0x19, - 0xFA, 0xF5, 0xA3, 0x97, 0xDE, 0x1C, 0xFC, 0xD4, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xB9, 0x87, 0xD5, - 0x7F, 0xB3, 0x71, 0xDE, 0x9D, 0xAF, 0x3E, 0x3D, 0xEF, 0xA1, 0x57, 0x37, 0xCF, 0x7D, 0x59, 0x46, - 0x47, 0xB8, 0x20, 0x38, 0x5E, 0xDB, 0x3C, 0xF7, 0xD8, 0xAB, 0xCF, 0xCC, 0x7D, 0xDA, 0xDD, 0x76, - 0xFC, 0xB7, 0xEF, 0xCC, 0xFB, 0x40, 0xF0, 0x53, 0x01, 0x00, 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x35, - 0xB9, 0xFE, 0x07, 0x1B, 0xEE, 0xE8, 0x1B, 0x88, 0xEF, 0xDF, 0x36, 0xCC, 0xEE, 0x0B, 0xFE, 0x63, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xD0, 0x15, 0xDE, 0xF3, 0x9E, 0xFF, 0x1F, 0x5B, 0xE2, 0x94, 0x1D, 0x86, - 0x86, 0x23, 0x09, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; - -static const uint8_t tree_gif[] PROGMEM = { - - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x28, 0x00, 0x3C, 0x00, 0x83, 0x0B, 0x00, 0x00, 0xFF, 0xFF, - 0x00, 0x21, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x94, 0x00, 0x00, 0xB5, 0x00, 0x00, - 0xDE, 0x00, 0x00, 0xFF, 0x00, 0x6B, 0x00, 0x00, 0xFF, 0x33, 0x33, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, - 0xFF, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x00, 0xCC, 0x00, 0x00, 0x21, 0xFF, 0x0B, - 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0xA0, 0x0F, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, - 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, - 0x8C, 0xCA, 0xE8, 0x85, 0xE8, 0xA5, 0xAC, 0x67, 0xEA, 0x8A, 0xE5, 0x2B, 0x2F, 0xE3, 0x31, 0xCE, - 0xAF, 0x41, 0x1C, 0xB8, 0x1B, 0x24, 0x80, 0x04, 0xAF, 0x87, 0x02, 0x26, 0x88, 0xA1, 0x07, 0x60, - 0xB9, 0x44, 0x82, 0x04, 0x02, 0x80, 0x61, 0x30, 0x70, 0x6E, 0x0C, 0x86, 0x03, 0x60, 0x20, 0xB0, - 0x6A, 0xA8, 0xD3, 0x03, 0x21, 0xEC, 0xAD, 0x18, 0x12, 0xE8, 0xF4, 0xB1, 0x4C, 0x51, 0xA0, 0x07, - 0xEA, 0x07, 0x5B, 0xF2, 0x70, 0x13, 0x76, 0xD4, 0xA8, 0xBC, 0xBC, 0xEA, 0x1F, 0x06, 0x77, 0x51, - 0x73, 0x50, 0x0A, 0x80, 0x7F, 0x04, 0x4C, 0x4D, 0x65, 0x06, 0x0A, 0x3B, 0x3B, 0x62, 0x5A, 0x53, - 0x65, 0x05, 0x02, 0x04, 0x68, 0x0C, 0x6A, 0x00, 0x5D, 0x5E, 0x0F, 0x0F, 0x6A, 0x98, 0x6A, 0x09, - 0x7B, 0x56, 0x9D, 0x09, 0x0C, 0xA7, 0xA7, 0x7F, 0x06, 0x9D, 0xA3, 0x44, 0x02, 0x01, 0x03, 0x90, - 0x0C, 0x87, 0x03, 0x06, 0x02, 0x05, 0x56, 0x06, 0x01, 0x02, 0x53, 0xB1, 0xB3, 0x04, 0x79, 0xAF, - 0x55, 0x48, 0x51, 0xB0, 0x87, 0xC0, 0x7F, 0x07, 0x95, 0x05, 0x85, 0x44, 0xB6, 0x00, 0x05, 0x53, - 0x96, 0x6A, 0x60, 0x02, 0x0A, 0x48, 0x89, 0x00, 0x07, 0xA1, 0x69, 0x7D, 0x2B, 0x3D, 0x9D, 0x41, - 0xA1, 0x04, 0xA0, 0xDD, 0x9D, 0x38, 0x02, 0xE1, 0x6A, 0x07, 0x62, 0x0C, 0x77, 0xE6, 0xAD, 0x2F, - 0x05, 0x01, 0xB6, 0x90, 0x07, 0xA8, 0xA7, 0x54, 0xB5, 0xBC, 0x06, 0x33, 0x50, 0x53, 0x3A, 0x00, - 0x39, 0x62, 0x10, 0x6B, 0xDF, 0xBE, 0x19, 0x03, 0x18, 0x40, 0xA9, 0x15, 0x4B, 0xCC, 0x0E, 0x77, - 0x3B, 0x04, 0xC4, 0x02, 0x38, 0x43, 0x21, 0x96, 0x2C, 0xD3, 0xD6, 0x81, 0x01, 0x26, 0x2A, 0xFB, - 0xDE, 0x07, 0x7C, 0x0A, 0xB7, 0x71, 0x1B, 0x79, 0xEE, 0xC5, 0x83, 0x72, 0xDD, 0x84, 0xBC, 0x4B, - 0x13, 0xA4, 0x13, 0x15, 0x17, 0xAB, 0x18, 0x78, 0x4A, 0x43, 0x40, 0x81, 0x43, 0x76, 0x8E, 0xB6, - 0x04, 0x7C, 0x19, 0x02, 0x98, 0xC4, 0x86, 0x35, 0xBD, 0xE1, 0x1C, 0x90, 0x4D, 0x62, 0xBF, 0x14, - 0xFC, 0x7E, 0x02, 0x1A, 0x60, 0xD3, 0xD1, 0x9D, 0x3B, 0x3A, 0x09, 0xFC, 0x44, 0xB1, 0xD0, 0x96, - 0x8E, 0x43, 0x8D, 0x9E, 0xDA, 0xDB, 0x22, 0x11, 0x8A, 0x42, 0x10, 0x5C, 0x00, 0x1A, 0x2D, 0x38, - 0xF2, 0x0E, 0xA0, 0x4A, 0x03, 0x4E, 0xA2, 0xF0, 0xF4, 0x80, 0x17, 0x95, 0x8C, 0x23, 0xD1, 0xD8, - 0x12, 0x00, 0x92, 0x03, 0xAB, 0x99, 0x23, 0x71, 0x3E, 0x8D, 0xEB, 0x6E, 0x18, 0x07, 0x66, 0x23, - 0x05, 0x3E, 0x72, 0xE4, 0x06, 0x0D, 0x81, 0x02, 0xE4, 0x36, 0x5D, 0xD9, 0xA5, 0xC0, 0xAD, 0x59, - 0xA2, 0x86, 0x6E, 0xD6, 0xC4, 0x73, 0xEB, 0x15, 0xA5, 0xC5, 0xDE, 0x14, 0xF4, 0x62, 0x07, 0xC0, - 0xE9, 0x9D, 0xCC, 0x58, 0x02, 0x14, 0xA0, 0x84, 0x45, 0x83, 0x68, 0x7A, 0x8D, 0x81, 0x66, 0xC3, - 0xE9, 0xD0, 0xA6, 0x3E, 0x03, 0xF3, 0x80, 0x7D, 0x90, 0xD8, 0x58, 0xA2, 0x59, 0x2D, 0x90, 0x68, - 0xD6, 0x64, 0x17, 0x76, 0x5A, 0x23, 0xBF, 0x13, 0x58, 0x89, 0x7A, 0xC3, 0xF0, 0x8F, 0xB6, 0xB8, - 0x4B, 0xD3, 0x74, 0x52, 0x50, 0x92, 0xC2, 0x03, 0xD1, 0x78, 0xD7, 0x89, 0x4C, 0x23, 0x26, 0x6E, - 0x82, 0xCC, 0x15, 0x5E, 0x05, 0x08, 0x30, 0x06, 0x8A, 0x75, 0x64, 0xFA, 0x42, 0xF1, 0x12, 0xAD, - 0x60, 0xDE, 0x51, 0x0A, 0x94, 0xBA, 0x5E, 0x2C, 0x8C, 0x86, 0xDD, 0x3D, 0xDE, 0x8F, 0x0A, 0x70, - 0x51, 0x5F, 0xFE, 0xF2, 0x84, 0xD1, 0xD0, 0xA6, 0x68, 0xB7, 0x96, 0x62, 0x14, 0x0C, 0xC1, 0xB7, - 0x90, 0x68, 0x72, 0xD1, 0x7C, 0xD9, 0x3D, 0xA3, 0x94, 0x35, 0xDE, 0x54, 0x06, 0x92, 0x51, 0x50, - 0x68, 0xC2, 0xC5, 0x44, 0xB7, 0x30, 0x70, 0xDE, 0x6A, 0x49, 0x69, 0x86, 0xC5, 0x2D, 0xA3, 0x29, - 0x04, 0x05, 0x2F, 0xD9, 0xC4, 0x02, 0x0C, 0x6C, 0x0C, 0x48, 0xB0, 0x94, 0x01, 0xDA, 0x3C, 0xF2, - 0x56, 0x21, 0xFA, 0xE8, 0xA3, 0xA1, 0x55, 0x12, 0xE9, 0xF4, 0x56, 0x42, 0xF8, 0x50, 0x00, 0x40, - 0x08, 0x08, 0xD4, 0x88, 0x80, 0x05, 0x33, 0x66, 0x10, 0xA2, 0x0C, 0xDC, 0xA0, 0xB0, 0xA3, 0x0B, - 0x71, 0xCD, 0xB1, 0x80, 0x75, 0x69, 0x20, 0x41, 0xE4, 0x91, 0x6B, 0x64, 0x10, 0x01, 0x00, 0x21, - 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, 0x00, - 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, 0x8C, - 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0x31, 0x1C, 0x34, - 0x1B, 0x24, 0x4C, 0x82, 0xE7, 0x28, 0x5E, 0x02, 0x78, 0x7A, 0x30, 0x8E, 0x47, 0x62, 0x48, 0x20, - 0x60, 0xD8, 0x06, 0x4A, 0x8E, 0xC1, 0x70, 0x60, 0x0C, 0x04, 0xD1, 0xCD, 0x60, 0x60, 0x3B, 0x10, - 0xBA, 0x59, 0x8B, 0x21, 0x41, 0x2E, 0x0F, 0xC3, 0x16, 0xF2, 0xC0, 0xAC, 0x40, 0x4F, 0x1E, 0x80, - 0x04, 0x81, 0x70, 0xD8, 0x0A, 0x14, 0x0F, 0xF7, 0x42, 0x00, 0xA0, 0xD6, 0x09, 0x0A, 0x81, 0x81, - 0x68, 0x00, 0x85, 0x85, 0x7F, 0x07, 0x07, 0x0A, 0x06, 0x7A, 0x06, 0x00, 0x74, 0x74, 0x5E, 0x8A, - 0x36, 0x68, 0x05, 0x7C, 0x66, 0x3D, 0x64, 0x58, 0x61, 0x0F, 0x0F, 0x66, 0x64, 0x99, 0x64, 0x79, - 0x9C, 0x9E, 0xA0, 0x48, 0x4E, 0x9D, 0xA3, 0x4A, 0x02, 0x01, 0x37, 0x90, 0x55, 0x04, 0x5C, 0x02, - 0x05, 0x59, 0x06, 0x01, 0x02, 0x4F, 0x89, 0x55, 0x76, 0xAC, 0x50, 0xAB, 0x0A, 0xAD, 0x7F, 0xB1, - 0x75, 0x07, 0x02, 0x04, 0x05, 0x00, 0xBE, 0x39, 0x06, 0x77, 0x05, 0x36, 0x04, 0x9F, 0x5B, 0xCC, - 0x00, 0x4A, 0x82, 0x81, 0x07, 0x9F, 0x65, 0x86, 0x85, 0x40, 0x9D, 0x0A, 0x9F, 0x0C, 0xD0, 0x9F, - 0x00, 0x9D, 0x39, 0x02, 0xDE, 0x66, 0xB0, 0x73, 0xE2, 0x09, 0xE4, 0xAA, 0x2F, 0x05, 0x01, 0xCC, - 0x5E, 0xA7, 0x56, 0x75, 0xD2, 0xB8, 0x8C, 0x2F, 0x4C, 0x36, 0x06, 0xB1, 0x73, 0xE1, 0x88, 0xC9, - 0x92, 0x45, 0x43, 0x96, 0x00, 0x2E, 0x37, 0xE8, 0x79, 0x99, 0x53, 0xEC, 0x86, 0x0D, 0x06, 0x05, - 0xF3, 0x51, 0x61, 0x57, 0xE6, 0x9E, 0xBF, 0x35, 0x0C, 0xDE, 0x81, 0xE8, 0x64, 0x10, 0xFB, 0x5B, - 0xB6, 0x6C, 0xF5, 0x56, 0x94, 0x22, 0x13, 0xA7, 0xE2, 0x9C, 0x04, 0xDF, 0xCA, 0x74, 0xB2, 0xB2, - 0xC2, 0x40, 0x2A, 0x92, 0x72, 0x20, 0x45, 0x52, 0xE0, 0x45, 0x1A, 0x01, 0x2B, 0xCA, 0x3E, 0xC4, - 0x3A, 0x38, 0x60, 0xDB, 0x23, 0x86, 0xD6, 0x14, 0x0C, 0x20, 0xC0, 0x84, 0xCB, 0x8A, 0x7C, 0x3C, - 0x1F, 0x25, 0x94, 0xA9, 0x80, 0x21, 0x17, 0xA2, 0x5B, 0x4E, 0x34, 0x91, 0xE5, 0x2F, 0x99, 0x4C, - 0xA0, 0x89, 0xEC, 0x5C, 0xC1, 0xB5, 0x89, 0xC3, 0x15, 0x2B, 0xCC, 0x10, 0xD6, 0xF9, 0x38, 0x67, - 0xA8, 0xB1, 0x01, 0xE5, 0x40, 0xD4, 0x7B, 0x42, 0xF1, 0x63, 0x02, 0x66, 0x02, 0x12, 0xA4, 0xFA, - 0x90, 0x2A, 0x54, 0xB6, 0x44, 0xEB, 0xDA, 0x96, 0x19, 0x9A, 0x53, 0x43, 0x01, 0xBB, 0x64, 0x84, - 0xAE, 0xC3, 0xBB, 0xB0, 0xCC, 0x31, 0x40, 0x5D, 0x35, 0xD8, 0x62, 0x82, 0x6B, 0x68, 0xAC, 0xA6, - 0x43, 0xF1, 0xD2, 0x01, 0x20, 0xE9, 0x8A, 0x25, 0x05, 0x96, 0x36, 0xD8, 0x8A, 0xD7, 0xD8, 0x46, - 0xD0, 0x85, 0xEB, 0x7E, 0x72, 0x31, 0x10, 0x54, 0x9F, 0x85, 0x00, 0xF1, 0xA6, 0x1C, 0x94, 0x2C, - 0x34, 0xB2, 0x17, 0x9F, 0x5B, 0xB8, 0x14, 0x80, 0xFC, 0x41, 0x28, 0x63, 0xC7, 0x03, 0x9A, 0x4A, - 0x32, 0x9C, 0x2C, 0x2B, 0x2E, 0x40, 0x72, 0x63, 0x59, 0x48, 0x85, 0x47, 0x8D, 0xD8, 0xC8, 0xD9, - 0xE2, 0xF0, 0x55, 0x99, 0x4A, 0xE3, 0x82, 0x07, 0x41, 0x3F, 0x25, 0x92, 0xAE, 0xD7, 0x4C, 0x3C, - 0xE7, 0xAC, 0x02, 0x04, 0x58, 0xC4, 0xC4, 0xAD, 0x9C, 0x1B, 0xB1, 0x3F, 0xE1, 0x42, 0x3D, 0x4B, - 0x9E, 0x85, 0xCB, 0x57, 0xA6, 0x30, 0x4B, 0x97, 0x88, 0x26, 0xA2, 0x03, 0x7F, 0x8B, 0x72, 0x2D, - 0x90, 0x79, 0x02, 0xFD, 0x59, 0x36, 0xB2, 0xF3, 0x41, 0x2A, 0x65, 0xAD, 0x75, 0x64, 0x3A, 0x0C, - 0x48, 0xB4, 0x15, 0x35, 0x13, 0xE0, 0xC2, 0xC0, 0x2D, 0xB1, 0x15, 0x95, 0x0C, 0x13, 0x97, 0x59, - 0x73, 0x90, 0x01, 0xF5, 0xAC, 0x36, 0x8D, 0x79, 0x12, 0x40, 0xB8, 0x45, 0x22, 0xB8, 0xF8, 0xC4, - 0x0C, 0x7D, 0xB3, 0x31, 0x96, 0xCF, 0x00, 0x01, 0x76, 0x41, 0x80, 0x4F, 0x0B, 0x80, 0x78, 0x4A, - 0x24, 0x5B, 0x8C, 0x18, 0xDB, 0x8A, 0x0A, 0xF0, 0xC3, 0x15, 0x88, 0x08, 0xF1, 0xC5, 0xC7, 0x3C, - 0x13, 0x40, 0x94, 0x01, 0x81, 0x13, 0x20, 0xA0, 0x23, 0x02, 0x27, 0xE0, 0x58, 0x63, 0x08, 0xD9, - 0xE8, 0x41, 0x81, 0x5B, 0x42, 0x7A, 0x57, 0x86, 0x12, 0x46, 0x26, 0x79, 0x46, 0x06, 0x11, 0x00, - 0x00, 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, - 0x3C, 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, - 0xE4, 0x8C, 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0xA1, - 0x1C, 0x34, 0x1B, 0x24, 0x4A, 0x82, 0xE7, 0x27, 0x85, 0x50, 0x08, 0x0C, 0x3D, 0x12, 0xBC, 0xC4, - 0xA3, 0x18, 0x12, 0x08, 0x14, 0x86, 0xC1, 0x80, 0xC9, 0x31, 0x18, 0x0E, 0x84, 0x81, 0x80, 0xBA, - 0x91, 0x46, 0xB1, 0x51, 0x06, 0xD7, 0x62, 0x40, 0x9A, 0x91, 0xE2, 0x71, 0xA5, 0x37, 0x40, 0x33, - 0xDE, 0xE9, 0xF1, 0xE3, 0xA1, 0x20, 0x10, 0x0E, 0x52, 0x01, 0x63, 0xA9, 0x5E, 0x0C, 0x85, 0x03, - 0x76, 0x7A, 0x7D, 0x0B, 0x4E, 0x03, 0x75, 0x78, 0x77, 0x78, 0x06, 0x84, 0x51, 0x37, 0x8A, 0x77, - 0x02, 0x51, 0x6A, 0x05, 0x02, 0x04, 0x67, 0x67, 0x5B, 0x72, 0x47, 0x98, 0x98, 0x7C, 0x5C, 0x73, - 0x66, 0x58, 0x07, 0x8B, 0x0F, 0x00, 0x9F, 0x45, 0x02, 0x01, 0x03, 0xA3, 0x0C, 0x89, 0x03, 0x06, - 0x02, 0x00, 0x5C, 0x06, 0x01, 0x92, 0x52, 0x07, 0xAE, 0x59, 0x03, 0x00, 0xBD, 0xBD, 0x4C, 0x02, - 0x05, 0xAB, 0xB9, 0x70, 0x0C, 0xAC, 0x96, 0x00, 0x52, 0x45, 0x36, 0xC1, 0x51, 0x97, 0x0C, 0x66, - 0x5E, 0x00, 0x8C, 0x4C, 0x0A, 0x05, 0x5F, 0x68, 0x98, 0x4E, 0x56, 0x40, 0x7F, 0x3D, 0x67, 0x97, - 0x9D, 0x73, 0x39, 0x02, 0x74, 0x9C, 0x48, 0xA4, 0x76, 0x76, 0x66, 0x73, 0xA8, 0x2C, 0x05, 0x50, - 0x02, 0xA3, 0xA3, 0x59, 0x78, 0x5E, 0x92, 0xD4, 0x2C, 0xDB, 0xB0, 0x59, 0x76, 0x07, 0x00, 0x78, - 0x16, 0x09, 0x80, 0xF5, 0xE4, 0x05, 0xC1, 0x81, 0x5F, 0xB0, 0x10, 0x00, 0xA0, 0xEE, 0xC0, 0xC0, - 0x03, 0x8E, 0x68, 0x68, 0xB1, 0x72, 0xE5, 0x92, 0xAF, 0x5E, 0xF6, 0x0C, 0x10, 0xF0, 0x86, 0x62, - 0xFB, 0xCE, 0xC1, 0x03, 0x98, 0x00, 0x74, 0x42, 0x42, 0xE7, 0xC5, 0xB9, 0x4E, 0xFF, 0xC2, 0x9D, - 0x99, 0xA3, 0x60, 0x0A, 0x0A, 0x03, 0xED, 0xC0, 0xDD, 0xF1, 0x47, 0xCF, 0xCB, 0x2E, 0x97, 0x20, - 0x92, 0x0D, 0x64, 0x85, 0xA5, 0x65, 0x43, 0x2C, 0x3C, 0x19, 0x10, 0x30, 0x94, 0x2F, 0x27, 0xBE, - 0x9D, 0x87, 0x78, 0xCE, 0x54, 0x47, 0xC0, 0x98, 0xC6, 0x9D, 0x39, 0x2F, 0xC6, 0xB2, 0xF1, 0x87, - 0x29, 0x96, 0x62, 0xC6, 0xB6, 0x69, 0xE2, 0xC0, 0x2B, 0x4A, 0x2C, 0x58, 0x49, 0x47, 0xDA, 0x11, - 0x6A, 0x69, 0xC0, 0xB8, 0x10, 0x22, 0x1F, 0xDC, 0x0A, 0xF4, 0x6D, 0x64, 0x02, 0x06, 0xB1, 0x04, - 0x28, 0x39, 0xBB, 0xA1, 0xDD, 0x49, 0x4C, 0xE9, 0xD4, 0xB9, 0x4D, 0x0A, 0xA2, 0x40, 0x81, 0x91, - 0x81, 0x68, 0x2E, 0x35, 0x43, 0xA0, 0xC0, 0x50, 0x05, 0x1F, 0x6A, 0x39, 0xB9, 0x65, 0x47, 0x4A, - 0xBF, 0xBC, 0xF4, 0xF0, 0x04, 0xE3, 0x98, 0xA1, 0x96, 0x02, 0x5B, 0x5E, 0x71, 0xA5, 0x53, 0xA8, - 0x4E, 0x68, 0x16, 0x2B, 0x01, 0xFC, 0x42, 0x29, 0x4A, 0x21, 0xF4, 0xE8, 0x87, 0x0A, 0xA5, 0x04, - 0xDA, 0xDC, 0x94, 0x95, 0x6A, 0x03, 0xC2, 0x5A, 0x6E, 0xF0, 0x66, 0xC8, 0x0E, 0xC0, 0x99, 0x20, - 0xDF, 0x16, 0x93, 0xAC, 0x91, 0x64, 0x16, 0x0B, 0xED, 0x14, 0x70, 0xF2, 0xC2, 0x6B, 0x75, 0x27, - 0x68, 0x81, 0xDA, 0x90, 0xB4, 0x0B, 0xDC, 0x74, 0xC8, 0x8B, 0x67, 0xB0, 0xBC, 0x75, 0x2B, 0xCC, - 0x5D, 0xA1, 0x00, 0xD8, 0x09, 0xC4, 0x3D, 0x23, 0x92, 0xB0, 0x6B, 0x29, 0xDA, 0x6A, 0x55, 0x12, - 0x46, 0x7A, 0x41, 0xA5, 0x81, 0xF8, 0x62, 0x25, 0xE8, 0x4E, 0x2A, 0x60, 0x22, 0x52, 0x05, 0xB4, - 0xB4, 0x94, 0x14, 0xAC, 0x12, 0x05, 0xBF, 0xCD, 0xB4, 0x60, 0xA7, 0x6F, 0x66, 0x65, 0xE7, 0x57, - 0xC7, 0xA3, 0xC8, 0x67, 0x85, 0x16, 0x5B, 0x49, 0xF0, 0x84, 0x2A, 0x48, 0x19, 0x92, 0x47, 0x30, - 0x4E, 0x00, 0x60, 0xC9, 0x57, 0x71, 0x79, 0x03, 0x51, 0x30, 0xD3, 0x4C, 0xA0, 0xC0, 0x4E, 0xA4, - 0x1C, 0x45, 0x51, 0x2C, 0x7E, 0x39, 0xB8, 0x18, 0x3E, 0x04, 0xB5, 0x04, 0x14, 0x6C, 0xB3, 0xF8, - 0xF1, 0xC7, 0x3C, 0x00, 0xAA, 0xC6, 0xCB, 0x45, 0xFC, 0x21, 0xD5, 0x52, 0x72, 0x17, 0x95, 0x88, - 0xD8, 0x09, 0x08, 0x00, 0x80, 0x00, 0x02, 0x28, 0x94, 0x28, 0xC1, 0x8C, 0x20, 0x20, 0xD1, 0x5D, - 0x02, 0x27, 0xE8, 0x88, 0x82, 0x5B, 0x84, 0x2C, 0xE0, 0xD6, 0x19, 0x4C, 0x1C, 0xA9, 0xA4, 0x19, - 0x1B, 0x44, 0x00, 0x00, 0x21, 0xFE, 0x19, 0x42, 0x6F, 0x79, 0x27, 0x73, 0x20, 0x48, 0x61, 0x70, - 0x70, 0x79, 0x20, 0x48, 0x6F, 0x6C, 0x69, 0x64, 0x61, 0x79, 0x73, 0x20, 0x50, 0x61, 0x67, 0x65, - 0x00, 0x3B - -}; - -static const uint8_t bird_gif[] PROGMEM = { - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x74, 0x00, 0x4E, 0x00, 0xCC, 0x13, 0x00, 0xFF, 0xFF, 0xFF, - 0xEF, 0xFF, 0xFF, 0xBD, 0xE7, 0xE7, 0xD6, 0xEF, 0xEF, 0x63, 0x63, 0x63, 0xAD, 0xAD, 0xAD, 0xCE, - 0xCE, 0xCE, 0xD6, 0xDE, 0xDE, 0x4A, 0x4A, 0x4A, 0x7B, 0x84, 0x84, 0x94, 0x9C, 0x9C, 0xE7, 0xE7, - 0xE7, 0x84, 0x94, 0x94, 0xB5, 0xC6, 0xC6, 0x73, 0x7B, 0x7B, 0x31, 0x31, 0x31, 0xF7, 0xF7, 0xF7, - 0x18, 0x10, 0x10, 0xCE, 0x63, 0x39, 0x6B, 0x39, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xFF, 0x0B, - 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x4B, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0xAE, 0xEC, 0x68, 0x2C, - 0x6D, 0x2C, 0xCF, 0x74, 0x6D, 0x03, 0x46, 0x84, 0x40, 0x77, 0xEF, 0xFF, 0x40, 0x51, 0x42, 0x41, - 0x0C, 0x1A, 0x8F, 0x48, 0x13, 0xC1, 0xD0, 0x20, 0x00, 0x0E, 0x8E, 0x42, 0x72, 0x4A, 0xAD, 0x21, - 0x0E, 0xCD, 0x05, 0xE1, 0x80, 0xA8, 0x7A, 0xBF, 0x29, 0xC2, 0xA0, 0x91, 0x28, 0x34, 0x02, 0x5D, - 0xB0, 0x5A, 0xFD, 0x08, 0x34, 0x14, 0x84, 0x00, 0x7A, 0x4D, 0xF7, 0x22, 0xDC, 0x89, 0xB8, 0xC1, - 0xB9, 0x80, 0xD5, 0xFF, 0x40, 0x0B, 0x77, 0x0C, 0x0F, 0x05, 0x01, 0x0C, 0x0A, 0x0B, 0x0F, 0x08, - 0x06, 0x80, 0x8E, 0x37, 0x7B, 0x01, 0x09, 0x11, 0x03, 0x68, 0x5C, 0x4C, 0x0A, 0x8F, 0x9A, 0x33, - 0x06, 0x09, 0x01, 0x0E, 0x71, 0x68, 0x88, 0x92, 0x07, 0x9B, 0xA6, 0x2C, 0x0A, 0x86, 0x07, 0x95, - 0x05, 0x04, 0xA1, 0x5D, 0x8D, 0xA7, 0xB2, 0x26, 0x09, 0x06, 0x72, 0x72, 0x0E, 0x94, 0x01, 0xA9, - 0x04, 0x08, 0x52, 0xB3, 0xC0, 0x00, 0x09, 0x95, 0xB7, 0x0D, 0x86, 0x01, 0x04, 0x0A, 0xC3, 0x09, - 0x34, 0x43, 0xC1, 0x54, 0x0E, 0xB7, 0xD2, 0x87, 0xAE, 0xBB, 0x99, 0x32, 0x7B, 0x0E, 0xCF, 0x49, - 0x0B, 0xD1, 0xD3, 0x72, 0x0C, 0xDE, 0x57, 0x33, 0xBE, 0xCC, 0xDB, 0x47, 0x05, 0x0C, 0xDF, 0xD3, - 0x05, 0xD7, 0x31, 0x0C, 0xE1, 0xA5, 0xE7, 0x46, 0xBE, 0xEB, 0xD2, 0x04, 0x05, 0xBF, 0x2C, 0x5C, - 0x4D, 0xF3, 0x46, 0x0B, 0x11, 0x56, 0xD9, 0x3B, 0x14, 0x01, 0x14, 0x0F, 0x16, 0x04, 0x1A, 0x30, - 0x28, 0x00, 0x81, 0xA1, 0x3F, 0x1F, 0xAD, 0x02, 0x10, 0x5B, 0x87, 0x6F, 0x0E, 0x8B, 0x02, 0x0E, - 0x04, 0x5C, 0x41, 0x10, 0xF0, 0x61, 0x8F, 0x25, 0x03, 0x26, 0xDA, 0x43, 0xC4, 0x42, 0x8B, 0x00, - 0x05, 0x0E, 0x7C, 0xFF, 0xED, 0xF0, 0x68, 0x83, 0x8B, 0x44, 0x91, 0xDF, 0x06, 0xAC, 0x54, 0x01, - 0x21, 0xA1, 0x80, 0x07, 0x0F, 0x1A, 0x38, 0x70, 0xC7, 0x72, 0x46, 0xC2, 0x97, 0x03, 0x3F, 0xC5, - 0x52, 0xE1, 0x80, 0x81, 0x00, 0x01, 0x19, 0x1B, 0x3C, 0x38, 0xD8, 0x53, 0xC6, 0x81, 0x38, 0x21, - 0x61, 0x4A, 0xEB, 0xB7, 0x42, 0x01, 0x82, 0xA3, 0x58, 0x7D, 0xC9, 0x6B, 0x1A, 0x63, 0x0B, 0xD0, - 0x81, 0x11, 0x08, 0x74, 0x44, 0x61, 0xE0, 0x01, 0xD6, 0xA3, 0x05, 0x16, 0x2D, 0xE5, 0xDA, 0xC2, - 0x80, 0xB7, 0x90, 0x41, 0xCB, 0xA4, 0x39, 0xC1, 0xEF, 0x2C, 0x56, 0xAA, 0x6C, 0x57, 0x38, 0xA8, - 0x14, 0x35, 0xA8, 0x35, 0x14, 0x10, 0x10, 0x34, 0xB0, 0x9B, 0xD5, 0x4F, 0xDE, 0x14, 0x06, 0x8E, - 0xF5, 0x1D, 0xC8, 0x25, 0x85, 0x83, 0xC1, 0x84, 0x05, 0x20, 0x32, 0x30, 0xF4, 0x30, 0x2D, 0x39, - 0x8B, 0x07, 0x32, 0x42, 0xA1, 0x00, 0x72, 0xE4, 0x45, 0x08, 0x7C, 0x59, 0x36, 0x91, 0x18, 0x73, - 0xE6, 0x6F, 0x79, 0x38, 0x2B, 0x88, 0x7C, 0x54, 0xE7, 0xE0, 0x2D, 0xA3, 0x4B, 0x78, 0x7A, 0x79, - 0x7A, 0x1A, 0xCE, 0x04, 0x4C, 0x85, 0xAC, 0x66, 0x7D, 0x96, 0x80, 0xB9, 0xD8, 0x22, 0x4A, 0x4B, - 0x14, 0x00, 0x97, 0xF1, 0xD3, 0x12, 0x3B, 0x79, 0x9F, 0x45, 0xC0, 0x13, 0xB8, 0xB7, 0xE1, 0xC5, - 0x19, 0x33, 0xEB, 0x53, 0xD3, 0xA8, 0x72, 0x01, 0x0D, 0x44, 0x97, 0x5C, 0x90, 0x7B, 0x9B, 0x01, - 0x05, 0xA6, 0x89, 0xFB, 0x3D, 0x54, 0x20, 0x81, 0xDA, 0x8C, 0xD7, 0x91, 0x16, 0x22, 0x91, 0xF8, - 0x80, 0x9F, 0x86, 0x08, 0x1E, 0xF4, 0xF2, 0xD5, 0x7D, 0x96, 0x18, 0xCC, 0x47, 0xA3, 0xDB, 0x7B, - 0xCA, 0x60, 0x40, 0xCA, 0xF4, 0x58, 0x81, 0xE2, 0x42, 0x04, 0x11, 0x4C, 0x80, 0x53, 0x7C, 0x3F, - 0xC9, 0xB1, 0xC7, 0xFF, 0x7A, 0xC1, 0x18, 0xA0, 0x0E, 0x7E, 0xC4, 0x49, 0xA5, 0x99, 0x67, 0xD7, - 0xA5, 0xB4, 0x15, 0x04, 0x93, 0x48, 0xA0, 0xA1, 0x81, 0x7C, 0x49, 0xB3, 0xC7, 0x38, 0xF6, 0x11, - 0x13, 0x52, 0x7E, 0x12, 0xAE, 0x63, 0xC0, 0x55, 0x00, 0x82, 0x62, 0xD8, 0x08, 0x0B, 0x24, 0xA0, - 0xA1, 0x86, 0x57, 0x3C, 0xF0, 0xE0, 0x2D, 0x05, 0x68, 0x67, 0x4A, 0x27, 0xB7, 0x0C, 0x80, 0x95, - 0x7E, 0x41, 0x11, 0x80, 0x5E, 0x85, 0xBF, 0x9D, 0xD0, 0xC0, 0x04, 0x2F, 0x2E, 0x62, 0xCB, 0x34, - 0x03, 0xE4, 0xB1, 0xA2, 0x23, 0x5E, 0x41, 0xC8, 0x63, 0x50, 0x0E, 0x9A, 0x75, 0x5D, 0x68, 0x5B, - 0x9D, 0xA0, 0x45, 0x04, 0x38, 0x2D, 0x72, 0xCC, 0x34, 0x7B, 0x24, 0xF2, 0x88, 0x5B, 0x39, 0xEA, - 0x18, 0xE1, 0x78, 0xD2, 0x58, 0x05, 0x20, 0x6C, 0x2B, 0xD4, 0x78, 0x64, 0x89, 0x6E, 0xB8, 0xF2, - 0xC2, 0x1F, 0x4D, 0xBE, 0x44, 0x22, 0x99, 0x72, 0xE4, 0x01, 0x60, 0x19, 0x31, 0x68, 0xB1, 0xC4, - 0x78, 0x50, 0xF4, 0xE2, 0x9B, 0x43, 0x5E, 0x80, 0x99, 0xE3, 0x8E, 0x6C, 0x4E, 0xE3, 0x63, 0x7A, - 0xD9, 0x55, 0xD6, 0x82, 0x4E, 0x04, 0x30, 0xD0, 0xC0, 0x01, 0x7C, 0x2A, 0xE4, 0xA3, 0xA2, 0x47, - 0xDC, 0x37, 0xE8, 0x9C, 0xE3, 0xC9, 0x64, 0x9D, 0x72, 0x70, 0x54, 0x29, 0x03, 0x04, 0xDF, 0xE5, - 0xE1, 0x4A, 0x19, 0x06, 0xB0, 0xD9, 0xC4, 0x66, 0x48, 0x7C, 0x27, 0xCD, 0x88, 0x98, 0x92, 0x89, - 0xE2, 0x75, 0x69, 0x2D, 0x69, 0xC3, 0x02, 0x07, 0xA4, 0x03, 0x8A, 0x2B, 0x8E, 0xC2, 0x04, 0x05, - 0x03, 0x48, 0x3C, 0x17, 0x5E, 0xAB, 0x7E, 0x61, 0x04, 0x20, 0x03, 0x33, 0x21, 0x01, 0xC1, 0x02, - 0xDF, 0x81, 0x52, 0x8B, 0x34, 0xAD, 0xC8, 0x6A, 0x43, 0x3E, 0x48, 0x9E, 0xF5, 0x24, 0x45, 0xAF, - 0xF2, 0x96, 0x9D, 0xFF, 0x03, 0xF5, 0x51, 0xD1, 0xA2, 0x03, 0x90, 0xCA, 0xF1, 0x94, 0x3E, 0x37, - 0x68, 0x81, 0xA4, 0x98, 0xC0, 0x9A, 0xF8, 0xC0, 0x8F, 0xCA, 0x39, 0xA0, 0xCD, 0x1F, 0x0B, 0x10, - 0x21, 0x0D, 0x03, 0xB8, 0x7D, 0x74, 0x64, 0x98, 0xD2, 0x16, 0x8A, 0x0C, 0xBA, 0xCA, 0x29, 0xF5, - 0x48, 0x8B, 0x53, 0xA1, 0x19, 0x03, 0xA8, 0x18, 0x85, 0xB2, 0x2A, 0xB9, 0x63, 0x06, 0x45, 0x06, - 0x80, 0x48, 0x05, 0xC9, 0x6E, 0x9C, 0x4F, 0xE1, 0xA3, 0xC2, 0x77, 0xF0, 0x16, 0x95, 0x4B, 0xB7, - 0xF4, 0x12, 0xEA, 0x57, 0x59, 0x14, 0xB2, 0x46, 0x08, 0xB8, 0x8E, 0x08, 0x72, 0x06, 0xB3, 0x3E, - 0xA6, 0xF2, 0x28, 0x46, 0x8B, 0x2C, 0xEB, 0xED, 0x03, 0xF3, 0x56, 0x5C, 0xF0, 0x40, 0xE6, 0xED, - 0xA6, 0x9C, 0x7C, 0xD9, 0x02, 0xA2, 0x48, 0xCA, 0x98, 0x25, 0xD6, 0xCE, 0x4E, 0x14, 0x2B, 0x18, - 0xE7, 0xC0, 0xE5, 0xAE, 0x93, 0x00, 0xC2, 0x45, 0x01, 0xB3, 0xC0, 0x8C, 0x74, 0xFA, 0xF7, 0x80, - 0x84, 0xAC, 0xAE, 0x6C, 0xCF, 0x00, 0x04, 0xA4, 0x47, 0x2C, 0x02, 0xCE, 0x6A, 0xF2, 0x06, 0x9D, - 0x98, 0xE5, 0x3C, 0xB0, 0x98, 0xB5, 0x4D, 0xD3, 0x40, 0x04, 0x05, 0x28, 0x57, 0x40, 0x04, 0x0A, - 0x50, 0x2A, 0x4B, 0x02, 0x1F, 0x53, 0xBD, 0x74, 0x54, 0x59, 0xBF, 0x1B, 0x5A, 0x41, 0xAC, 0x65, - 0xA7, 0xF0, 0x33, 0x0C, 0x58, 0x6D, 0xB6, 0x69, 0x68, 0x4F, 0x2B, 0x87, 0x55, 0xB7, 0x38, 0x50, - 0xC8, 0x5D, 0x7E, 0x8A, 0x1D, 0x0C, 0x04, 0x8F, 0xCD, 0x1D, 0x94, 0xDD, 0x4F, 0xA1, 0xEC, 0xA1, - 0x7C, 0xAD, 0x8D, 0xC5, 0x52, 0x4D, 0xBE, 0x0A, 0x3E, 0x9E, 0x55, 0x32, 0xDA, 0xA3, 0x37, 0x03, - 0xBD, 0x44, 0x3D, 0x8F, 0x55, 0x34, 0x3B, 0x6E, 0x4F, 0x03, 0x99, 0x73, 0x89, 0x93, 0xE5, 0xFE, - 0x74, 0x13, 0xB7, 0xCB, 0xE6, 0xA4, 0x4B, 0x63, 0x90, 0x65, 0xED, 0x16, 0x25, 0x77, 0xE9, 0x54, - 0x67, 0x11, 0xDB, 0x89, 0x44, 0xB3, 0x6E, 0xF6, 0x89, 0x7E, 0x37, 0x05, 0x47, 0xE7, 0xB2, 0xFB, - 0xF5, 0x2D, 0x70, 0xDD, 0x80, 0x37, 0xDE, 0x0A, 0x8E, 0xFB, 0x06, 0x1C, 0x00, 0x4D, 0xE0, 0x1E, - 0x80, 0x0C, 0x73, 0xFB, 0x47, 0x00, 0xE8, 0x2C, 0x89, 0x6E, 0xCF, 0x0C, 0x66, 0x87, 0x64, 0x55, - 0x73, 0x87, 0xB5, 0xD2, 0x39, 0xF2, 0x54, 0xA3, 0xAD, 0x24, 0xEF, 0x09, 0xC4, 0x2E, 0x47, 0x0C, - 0x83, 0x4F, 0x44, 0x6E, 0x25, 0xA9, 0x0C, 0x8F, 0x43, 0xE0, 0xD3, 0xB4, 0x30, 0x50, 0x54, 0xF9, - 0x41, 0x57, 0xC9, 0x63, 0xE6, 0x03, 0x00, 0xC1, 0xED, 0xE9, 0xAB, 0x60, 0x4F, 0x62, 0x94, 0x41, - 0x2A, 0xFE, 0x01, 0x56, 0x11, 0x10, 0xB3, 0x65, 0x4F, 0x11, 0xCC, 0x37, 0x4A, 0x10, 0x2C, 0x8E, - 0x3C, 0x80, 0x40, 0x3A, 0x30, 0x20, 0x02, 0x96, 0x10, 0x3F, 0x13, 0x64, 0xA7, 0x1E, 0x17, 0x53, - 0x90, 0x02, 0x28, 0x87, 0x00, 0xB2, 0xC1, 0xE0, 0x29, 0x08, 0x24, 0x50, 0xC9, 0xFE, 0x37, 0x3C, - 0x28, 0x30, 0x67, 0x75, 0x01, 0x9C, 0x20, 0x4E, 0x0A, 0xE0, 0xAC, 0xAF, 0x21, 0x90, 0x3E, 0x0D, - 0x4C, 0x41, 0xBB, 0x42, 0x13, 0x05, 0x63, 0xA4, 0x64, 0x81, 0x43, 0x28, 0x40, 0xED, 0x44, 0x00, - 0x01, 0x2C, 0x78, 0x29, 0x85, 0x2C, 0x38, 0x56, 0x62, 0x1A, 0xC0, 0x1D, 0x35, 0x84, 0x00, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, - 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, - 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, - 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, - 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, - 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, - 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, - 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, - 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, - 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, - 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, - 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, - 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, - 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, - 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, - 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, - 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, - 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, - 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, - 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, - 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, - 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, - 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, - 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, - 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, - 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, - 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, - 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, - 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, - 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, - 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, - 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, - 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, - 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, - 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, - 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, - 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, - 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, - 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, - 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, - 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, - 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, - 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, - 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, - 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, - 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, - 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, - 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, - 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, - 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, - 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, - 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, - 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, - 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, - 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, - 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, - 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, - 0x0D, 0x21, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, - 0x00, 0x6A, 0x00, 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, - 0xAE, 0x6C, 0xEB, 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, - 0x0A, 0xC7, 0x03, 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, - 0xA4, 0x10, 0xA8, 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, - 0x9E, 0x18, 0x02, 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, - 0xD4, 0x81, 0x01, 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, - 0x0D, 0x08, 0x05, 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, - 0x04, 0x06, 0x10, 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, - 0x93, 0x09, 0x0C, 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, - 0x0C, 0x39, 0x9A, 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, - 0xAB, 0x5B, 0x4A, 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, - 0x0D, 0x0D, 0x07, 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, - 0x0E, 0x7B, 0x04, 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, - 0x92, 0x0C, 0x0E, 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, - 0x5B, 0x03, 0x0E, 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, - 0x98, 0xBE, 0x73, 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, - 0xB0, 0x6A, 0xA3, 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, - 0xBC, 0xA5, 0x0C, 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, - 0x04, 0x32, 0x53, 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, - 0x67, 0x8E, 0xA2, 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, - 0x33, 0xA0, 0x2B, 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, - 0x0E, 0x10, 0xC0, 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, - 0x0A, 0x08, 0xF8, 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, - 0x1D, 0x8A, 0x47, 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, - 0x22, 0x22, 0x82, 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, - 0x6B, 0x50, 0xE0, 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, - 0xA3, 0x2D, 0x60, 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, - 0x94, 0x14, 0x7F, 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, - 0x05, 0x30, 0x4A, 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, - 0x53, 0x20, 0xC1, 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, - 0x10, 0x03, 0x80, 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, - 0x26, 0x0F, 0x28, 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, - 0xC6, 0x0A, 0x02, 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, - 0xA0, 0xC0, 0x28, 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, - 0x42, 0x01, 0x5A, 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, - 0x88, 0x33, 0x14, 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, - 0x6A, 0xC4, 0x80, 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, - 0x28, 0xC2, 0xE2, 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, - 0x21, 0xA4, 0x0C, 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, - 0x4A, 0x93, 0x29, 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, - 0x94, 0x0C, 0x38, 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, - 0x56, 0x74, 0x54, 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, - 0x99, 0x83, 0x8A, 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, - 0x10, 0x02, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, - 0x6A, 0x00, 0x2E, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x00, 0x40, 0x62, 0xB9, 0x28, 0x08, 0x41, 0x28, - 0x4B, 0xE9, 0xBE, 0x70, 0x2C, 0xCF, 0x74, 0x6D, 0xDF, 0xE5, 0xF1, 0x3C, 0x69, 0x52, 0x24, 0x0F, - 0x82, 0xE1, 0x60, 0x00, 0x26, 0x5A, 0xB8, 0xA4, 0x72, 0xC9, 0x94, 0xE9, 0x22, 0x12, 0xC9, 0x64, - 0xF2, 0x18, 0x04, 0xAE, 0xD7, 0x01, 0xB0, 0xD0, 0xEC, 0x7A, 0xBF, 0x31, 0x48, 0x61, 0x12, 0x95, - 0x32, 0x02, 0x8E, 0x06, 0x36, 0x70, 0x50, 0x21, 0xC1, 0xF0, 0x78, 0x72, 0x91, 0x20, 0x4B, 0x55, - 0x85, 0xF5, 0xB5, 0x81, 0xE0, 0xCA, 0xFF, 0x80, 0x32, 0x0D, 0x0F, 0x53, 0x11, 0x11, 0x04, 0x07, - 0x7A, 0x57, 0x0E, 0x04, 0x6F, 0x81, 0x8F, 0x7F, 0x0B, 0x2A, 0x06, 0x94, 0x6A, 0x8A, 0x01, 0x06, - 0x04, 0x05, 0x07, 0x0B, 0x0B, 0x44, 0x94, 0xA0, 0x43, 0x24, 0x90, 0xA4, 0x37, 0x05, 0x04, 0x96, - 0x97, 0x58, 0x45, 0x09, 0x0A, 0x0C, 0x0C, 0x0A, 0x05, 0x0D, 0xB3, 0x05, 0x0A, 0x09, 0x0C, 0x05, - 0x06, 0xA3, 0x4D, 0x9F, 0xA0, 0x0B, 0xBB, 0xA5, 0x33, 0x6D, 0x08, 0x0C, 0x89, 0xAA, 0xC7, 0x8A, - 0x07, 0xAE, 0x3E, 0xC0, 0x32, 0x10, 0x07, 0x3F, 0x2A, 0x0E, 0x8C, 0x2A, 0x08, 0xD6, 0x29, 0x0E, - 0xB9, 0xCD, 0xC1, 0x22, 0x10, 0x45, 0xD2, 0xB8, 0x07, 0x56, 0x97, 0x22, 0xC8, 0x01, 0x0D, 0x0C, - 0x3E, 0x9C, 0x25, 0xCF, 0xA7, 0x04, 0xD9, 0xE2, 0xC8, 0x03, 0xD0, 0x09, 0x04, 0xD6, 0x09, 0xBA, - 0xDC, 0x30, 0x10, 0x0B, 0x06, 0xB6, 0x2A, 0x04, 0x12, 0xDC, 0x52, 0x40, 0x90, 0x60, 0x81, 0x5C, - 0x07, 0x20, 0xA8, 0x1A, 0xD0, 0x20, 0x01, 0x35, 0x07, 0x3C, 0x1C, 0x18, 0x30, 0x87, 0xCC, 0x00, - 0x03, 0x02, 0x0F, 0x12, 0x34, 0xD8, 0xA6, 0xCF, 0x44, 0xA7, 0x8F, 0x9D, 0x0E, 0x88, 0xAC, 0x75, - 0x6B, 0x22, 0xC5, 0x4B, 0x44, 0x4E, 0xFF, 0x26, 0x63, 0x80, 0x20, 0xE3, 0x81, 0x8E, 0x5D, 0x0E, - 0xA4, 0x33, 0xA6, 0x92, 0x8D, 0x03, 0x04, 0x35, 0x2F, 0x35, 0xC0, 0x98, 0xE0, 0x25, 0x4C, 0x26, - 0x07, 0x7C, 0xE4, 0xBC, 0x82, 0x40, 0xE1, 0x50, 0x3D, 0xCA, 0x52, 0x38, 0xFA, 0x89, 0xE3, 0x47, - 0xCE, 0x1F, 0x00, 0x8E, 0xAA, 0x02, 0xC2, 0x80, 0xE9, 0x92, 0x86, 0x2A, 0x0D, 0x3C, 0xA0, 0x29, - 0x55, 0xD1, 0x00, 0x8C, 0x06, 0xAC, 0x26, 0x31, 0x80, 0xC0, 0x24, 0x32, 0x04, 0x09, 0xBA, 0x9A, - 0x1B, 0x94, 0x80, 0xA3, 0x58, 0x18, 0x5A, 0xCF, 0x1C, 0x13, 0x78, 0x69, 0x9C, 0xDA, 0x2C, 0x04, - 0x22, 0x34, 0x78, 0x6B, 0x03, 0x82, 0x02, 0x44, 0x97, 0x5E, 0xD5, 0x1D, 0x60, 0xF7, 0xEE, 0xB9, - 0x07, 0x61, 0xF9, 0xD6, 0x58, 0xC0, 0x20, 0xDB, 0x1A, 0x82, 0x0B, 0x09, 0x17, 0xBE, 0xFB, 0x55, - 0x81, 0xE2, 0x1B, 0x99, 0x10, 0x39, 0xF6, 0x2A, 0x99, 0xB0, 0x61, 0x3D, 0x0E, 0x2F, 0xDF, 0x18, - 0x94, 0x87, 0xF3, 0x00, 0x01, 0x02, 0x24, 0x7F, 0x7E, 0x8C, 0x60, 0xA9, 0x68, 0x17, 0x6D, 0xCC, - 0x62, 0x91, 0x8C, 0x1A, 0x75, 0xE7, 0x2B, 0x30, 0xBA, 0xF2, 0xF1, 0xF9, 0xDA, 0x45, 0x26, 0xAE, - 0x59, 0x4E, 0xD7, 0x4E, 0xED, 0x19, 0xF7, 0x8C, 0xA3, 0x5A, 0xF7, 0xF6, 0x16, 0xF1, 0xC3, 0x68, - 0x70, 0xE1, 0xC3, 0x8B, 0xAF, 0xA1, 0x31, 0xF4, 0xB4, 0xD2, 0xD7, 0x10, 0x60, 0xE9, 0x21, 0x3C, - 0x3C, 0xFA, 0x64, 0xE3, 0x32, 0x6A, 0xD2, 0x56, 0xF0, 0xC0, 0x0F, 0x5F, 0x3A, 0xA9, 0x02, 0x70, - 0xEF, 0x4E, 0x5C, 0x3A, 0x68, 0xD7, 0xE5, 0x54, 0x72, 0xB7, 0xF2, 0xD5, 0xBC, 0xD5, 0xA0, 0x34, - 0xD7, 0xDB, 0x6E, 0xAF, 0x5A, 0x95, 0x03, 0xDE, 0x2F, 0x98, 0x73, 0xDB, 0x70, 0x01, 0xC4, 0xF2, - 0x96, 0x45, 0xB3, 0x41, 0x67, 0x5B, 0xFF, 0x7F, 0xE6, 0x00, 0x20, 0x51, 0x0C, 0xF2, 0xD0, 0x47, - 0x9C, 0x7A, 0xEA, 0x91, 0x07, 0x20, 0x4C, 0x05, 0xC8, 0xA5, 0x9E, 0x70, 0xA7, 0x31, 0x78, 0x92, - 0x24, 0x89, 0xB9, 0x70, 0x4C, 0x26, 0x41, 0x04, 0xA4, 0x40, 0x03, 0x19, 0x32, 0x72, 0xC8, 0x85, - 0x1D, 0xB9, 0x92, 0x20, 0x7F, 0xEE, 0x9D, 0x94, 0xE1, 0x49, 0x0C, 0xDD, 0x84, 0x16, 0x79, 0x11, - 0xF4, 0x11, 0x0B, 0x42, 0x62, 0x41, 0x70, 0xCB, 0x73, 0x1E, 0x0E, 0xE5, 0x20, 0x70, 0x7B, 0x10, - 0x40, 0x58, 0x5E, 0xC4, 0xE4, 0x23, 0x42, 0x02, 0x86, 0x34, 0xA2, 0x98, 0x24, 0x0A, 0x00, 0x19, - 0x63, 0x4E, 0xFD, 0x10, 0x43, 0x93, 0x32, 0x79, 0xF1, 0x90, 0x42, 0x88, 0x2E, 0x2C, 0xF0, 0x80, - 0x21, 0x0F, 0x30, 0x00, 0x5F, 0x30, 0x62, 0xF4, 0xB1, 0x61, 0x90, 0x47, 0x39, 0x18, 0xCB, 0x3F, - 0xEF, 0xF8, 0x60, 0xC0, 0x2F, 0x36, 0x3C, 0xB1, 0x22, 0x53, 0x99, 0x20, 0x90, 0x08, 0x9A, 0x5D, - 0x79, 0xD9, 0x80, 0x00, 0x29, 0xB4, 0xD6, 0x04, 0x59, 0x2C, 0xFC, 0x24, 0x13, 0x2A, 0x78, 0x76, - 0xF5, 0x95, 0x03, 0x0C, 0xA0, 0xA6, 0xA3, 0x17, 0x6E, 0x3D, 0xB2, 0xC0, 0x0F, 0x0A, 0x10, 0xB9, - 0xDA, 0x39, 0x08, 0x08, 0x90, 0x86, 0x00, 0x4E, 0x2E, 0x17, 0x86, 0x3B, 0xA5, 0x4D, 0xAA, 0x87, - 0x42, 0xAF, 0x0C, 0xE2, 0x00, 0xA6, 0x63, 0x8A, 0xF6, 0xA8, 0x0A, 0x91, 0x06, 0xF0, 0x4B, 0x9E, - 0xCE, 0x29, 0xF2, 0x17, 0x4B, 0x96, 0x16, 0x50, 0xA9, 0x9F, 0x9A, 0x02, 0x50, 0x00, 0x23, 0x81, - 0x9E, 0x1A, 0x41, 0x94, 0x47, 0x31, 0x10, 0xE6, 0x01, 0xD3, 0x98, 0xA5, 0x10, 0x2A, 0x2D, 0xF1, - 0x21, 0x46, 0x40, 0xB5, 0xDA, 0x8A, 0xD8, 0x02, 0x0D, 0x21, 0x9A, 0x80, 0x99, 0x98, 0xE0, 0x63, - 0xCE, 0x10, 0x01, 0xC8, 0x7A, 0xC2, 0x79, 0x11, 0x50, 0x65, 0x26, 0x2B, 0x9F, 0x64, 0xF9, 0xD1, - 0xE8, 0x93, 0x29, 0x32, 0x30, 0x84, 0x03, 0x0A, 0x28, 0x04, 0x2C, 0x46, 0x0B, 0x5C, 0x91, 0xAE, - 0x22, 0x64, 0x21, 0xB0, 0x13, 0x30, 0x99, 0xDC, 0xB4, 0xD3, 0xB3, 0xE9, 0x00, 0x50, 0x6A, 0xB2, - 0x00, 0xB4, 0xD1, 0x42, 0x50, 0x42, 0x00, 0xF6, 0x55, 0x51, 0xAA, 0x6A, 0x82, 0x09, 0x22, 0x08, - 0x38, 0xB0, 0x4D, 0xB7, 0x88, 0x2C, 0x60, 0x0D, 0x8B, 0xF8, 0xBE, 0x90, 0x89, 0x02, 0x0E, 0xE0, - 0x03, 0x81, 0xC0, 0x99, 0xDC, 0xAA, 0x2A, 0x44, 0xB3, 0xF8, 0xE9, 0x16, 0x04, 0x08, 0xD8, 0xC2, - 0x1C, 0x97, 0x0D, 0xCB, 0xE0, 0xC9, 0x28, 0x3A, 0x28, 0xF3, 0x5F, 0x6B, 0xBF, 0xD9, 0x72, 0xC4, - 0x62, 0x1D, 0x7F, 0x1B, 0x72, 0x0D, 0xCA, 0xBC, 0xD4, 0x86, 0x2D, 0x3E, 0xB9, 0xBC, 0x5C, 0x08, - 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, 0x6A, 0x00, - 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, 0xAE, 0x6C, 0xEB, - 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, 0x0A, 0xC7, 0x03, - 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, 0xA4, 0x10, 0xA8, - 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, 0x9E, 0x18, 0x02, - 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, 0xD4, 0x81, 0x01, - 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, 0x0D, 0x08, 0x05, - 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, 0x04, 0x06, 0x10, - 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, 0x93, 0x09, 0x0C, - 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, 0x0C, 0x39, 0x9A, - 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, 0xAB, 0x5B, 0x4A, - 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, 0x0D, 0x0D, 0x07, - 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, 0x0E, 0x7B, 0x04, - 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, 0x92, 0x0C, 0x0E, - 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, 0x5B, 0x03, 0x0E, - 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, 0x98, 0xBE, 0x73, - 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, 0xB0, 0x6A, 0xA3, - 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, 0xBC, 0xA5, 0x0C, - 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, 0x04, 0x32, 0x53, - 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, 0x67, 0x8E, 0xA2, - 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, 0x33, 0xA0, 0x2B, - 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, 0x0E, 0x10, 0xC0, - 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, 0x0A, 0x08, 0xF8, - 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, 0x1D, 0x8A, 0x47, - 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, 0x22, 0x22, 0x82, - 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, 0x6B, 0x50, 0xE0, - 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, 0xA3, 0x2D, 0x60, - 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, 0x94, 0x14, 0x7F, - 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, 0x05, 0x30, 0x4A, - 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, 0x53, 0x20, 0xC1, - 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, 0x10, 0x03, 0x80, - 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, 0x26, 0x0F, 0x28, - 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, 0xC6, 0x0A, 0x02, - 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, 0xA0, 0xC0, 0x28, - 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, 0x42, 0x01, 0x5A, - 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, 0x88, 0x33, 0x14, - 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, 0x6A, 0xC4, 0x80, - 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, 0x28, 0xC2, 0xE2, - 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, 0x21, 0xA4, 0x0C, - 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, 0x4A, 0x93, 0x29, - 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, 0x94, 0x0C, 0x38, - 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, 0x56, 0x74, 0x54, - 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, 0x99, 0x83, 0x8A, - 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, 0x10, 0x02, 0x00, - 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, - 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, - 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, - 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, - 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, - 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, - 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, - 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, - 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, - 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, - 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, - 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, - 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, - 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, - 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, - 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, - 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, - 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, - 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, - 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, - 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, - 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, - 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, - 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, - 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, - 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, - 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, - 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, - 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, - 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, - 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, - 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, - 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, - 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, - 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, - 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, - 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, - 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, - 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, - 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, - 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, - 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, - 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, - 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, - 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, - 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, - 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, - 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, - 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, - 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, - 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, - 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, - 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, - 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, - 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, - 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, - 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, - 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, - 0x0D, 0x21, 0x00, 0x00, 0x3B}; +#include + +static const uint8_t firebase_png[] PROGMEM = { + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x02, 0x58, 0x08, 0x06, 0x00, 0x00, 0x00, 0x9A, 0x76, 0x82, + 0x70, 0x00, 0x00, 0x00, 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xAE, 0xCE, 0x1C, 0xE9, 0x00, 0x00, + 0x00, 0x04, 0x67, 0x41, 0x4D, 0x41, 0x00, 0x00, 0xB1, 0x8F, 0x0B, 0xFC, 0x61, 0x05, 0x00, 0x00, + 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, 0xC7, + 0x6F, 0xA8, 0x64, 0x00, 0x00, 0x55, 0x14, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5E, 0xED, 0xDD, 0x59, + 0x90, 0x1D, 0xF5, 0x81, 0xE7, 0x7B, 0x3F, 0xF4, 0xC3, 0x84, 0x91, 0x40, 0x6E, 0xEF, 0xC6, 0x8B, + 0x30, 0x92, 0x4A, 0x1B, 0x58, 0x98, 0xCD, 0xA0, 0xA5, 0x4A, 0x12, 0x20, 0x6C, 0x9A, 0xB6, 0xBA, + 0x01, 0x63, 0x4F, 0x3F, 0x8C, 0x1E, 0x26, 0xC2, 0xF7, 0xE5, 0xC6, 0x25, 0xE2, 0xDE, 0x97, 0x3B, + 0x7D, 0x67, 0xAC, 0x6E, 0x63, 0x63, 0x83, 0x8D, 0xBC, 0xB1, 0x99, 0xA5, 0x10, 0x08, 0xED, 0x52, + 0x69, 0xAD, 0x92, 0xA0, 0xBB, 0xAB, 0xA7, 0xED, 0x6E, 0x77, 0xB7, 0x81, 0x42, 0x9B, 0x99, 0x89, + 0x89, 0xDB, 0x9A, 0xB9, 0x0E, 0x2F, 0x2C, 0x76, 0x61, 0x16, 0x49, 0x85, 0xA4, 0xFC, 0xDF, 0xFF, + 0x3F, 0x4F, 0xE6, 0xA9, 0x3C, 0x79, 0x7E, 0xE7, 0x9C, 0xCC, 0x3C, 0x99, 0x75, 0xCE, 0xC9, 0xFA, + 0x7E, 0x22, 0x7E, 0xA1, 0x6E, 0x2F, 0x42, 0xC2, 0x94, 0xE2, 0xFF, 0x8D, 0x7F, 0x65, 0x9E, 0xF7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x82, 0x79, 0x6E, 0xCD, 0x80, 0xBF, 0xC3, 0x5F, 0x58, 0x12, 0xFC, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x1F, 0x73, 0x78, 0xCD, 0x3A, 0xEF, 0xB9, 0x35, 0x63, 0xE6, + 0xB9, 0x9B, 0x4D, 0x75, 0xCF, 0xAE, 0xF1, 0xE7, 0x3D, 0x7B, 0xD3, 0xA0, 0x0B, 0x92, 0xE0, 0x3F, + 0x0A, 0x00, 0x00, 0x00, 0x00, 0xD9, 0x98, 0x91, 0x35, 0xB3, 0xEB, 0xC2, 0x23, 0x12, 0x1F, 0xD1, + 0x79, 0x87, 0xD7, 0x6C, 0x08, 0xFE, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x90, 0x8E, 0xFB, 0x16, 0x2B, + 0xEF, 0xD9, 0x35, 0xE3, 0x95, 0xE0, 0xB8, 0xD1, 0x98, 0x03, 0x57, 0x19, 0xB3, 0x7B, 0xAE, 0x31, + 0x3B, 0x3E, 0x65, 0xCC, 0xB6, 0x8F, 0x1A, 0xB3, 0xF3, 0xD3, 0xC6, 0x0C, 0x2D, 0x34, 0x66, 0x78, + 0xD9, 0x64, 0x84, 0x3C, 0x7B, 0xD3, 0x60, 0xF0, 0x5F, 0x07, 0x00, 0x00, 0x00, 0x80, 0xE4, 0xAA, + 0x37, 0x1F, 0x2E, 0x3C, 0x76, 0x7C, 0xC2, 0x98, 0xED, 0x1F, 0xAB, 0x84, 0x87, 0xDA, 0x2E, 0x1B, + 0x26, 0x87, 0x6F, 0xA8, 0x84, 0xC8, 0xA1, 0x9B, 0xD6, 0x06, 0x3F, 0x05, 0x00, 0x00, 0x00, 0x00, + 0xB4, 0xE6, 0x9E, 0xF9, 0xF0, 0xE3, 0x63, 0xCF, 0xA2, 0x4A, 0x78, 0x34, 0x8B, 0x8F, 0x70, 0xDB, + 0x3F, 0x6E, 0xCC, 0xC8, 0x0A, 0xF7, 0xAD, 0x58, 0xE3, 0x66, 0xB4, 0x7F, 0x56, 0xF0, 0x53, 0x01, + 0x00, 0x00, 0x00, 0x40, 0x73, 0xDE, 0x73, 0x37, 0x9F, 0x34, 0xFB, 0xAF, 0x48, 0x1E, 0x1F, 0xE1, + 0x76, 0x5E, 0x62, 0xCC, 0xE1, 0xD5, 0x76, 0x37, 0xAE, 0x0F, 0x7E, 0x2A, 0x00, 0x00, 0x00, 0x00, + 0x68, 0xCC, 0x3D, 0x78, 0x6E, 0x0E, 0xAF, 0x4A, 0x1F, 0x1F, 0xFE, 0x3E, 0x62, 0xCC, 0xBE, 0xCF, + 0x18, 0xEF, 0xF0, 0x4D, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x68, 0xCD, 0xFF, 0x8C, 0x8F, 0xBD, + 0x97, 0x67, 0x8B, 0x0F, 0xB7, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x20, 0x19, 0x1B, + 0x20, 0xEB, 0xCD, 0xAE, 0x4F, 0xC7, 0xE2, 0xA2, 0xD5, 0x82, 0xF8, 0x08, 0xB7, 0x67, 0xB1, 0x31, + 0xCF, 0xDE, 0x64, 0xDC, 0x6D, 0x4A, 0xF0, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x40, 0x3D, 0xEF, 0xB9, + 0x9B, 0x07, 0x75, 0x64, 0x34, 0x5A, 0x2C, 0x3E, 0xC2, 0x1D, 0x1A, 0xE0, 0xB5, 0xBC, 0x00, 0x00, + 0x00, 0x00, 0x9A, 0xF3, 0x0E, 0x2E, 0x1B, 0xD5, 0xA1, 0xA1, 0x26, 0xC2, 0x23, 0xDC, 0xD0, 0x02, + 0x6E, 0x41, 0x00, 0x00, 0x00, 0x00, 0x34, 0xE7, 0x1D, 0xB8, 0x76, 0x5C, 0xC7, 0x46, 0x7C, 0x22, + 0x3A, 0xE2, 0x1B, 0x59, 0x6E, 0xBC, 0xC3, 0x37, 0x8E, 0x06, 0x3F, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x4C, 0x72, 0x6F, 0xAE, 0x32, 0xFB, 0x96, 0xD8, 0x78, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x0D, 0xB5, + 0x5D, 0x73, 0x2A, 0xB7, 0x20, 0xCF, 0xAD, 0x19, 0x08, 0xFE, 0x12, 0x00, 0x00, 0x00, 0x00, 0x50, + 0xE1, 0xBF, 0x01, 0x6B, 0x77, 0x9F, 0x8D, 0x07, 0x15, 0x1D, 0xE1, 0x44, 0x68, 0x34, 0xDB, 0xF0, + 0xF5, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0xA8, 0xE7, 0x7F, 0x02, 0xFA, 0xAE, 0xB9, 0x36, 0x1C, + 0x54, 0x78, 0xB8, 0x89, 0xC0, 0x68, 0xB5, 0x5D, 0x97, 0x1A, 0xF3, 0xEC, 0x8D, 0xC6, 0x3C, 0xB7, + 0x9A, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x93, 0x6C, 0x80, 0xAC, 0xD7, 0xE1, 0xE1, 0x26, 0xE2, + 0xA2, 0xE5, 0x3E, 0x5C, 0xD9, 0x81, 0xAB, 0x8C, 0xF7, 0xEC, 0x0D, 0x27, 0x83, 0xBF, 0x0C, 0x00, + 0x00, 0x00, 0x00, 0xBC, 0xE7, 0x3D, 0xDE, 0xF0, 0x8A, 0x06, 0x6F, 0xC0, 0x52, 0x71, 0xD1, 0x6A, + 0x41, 0x7C, 0xB8, 0xED, 0xF8, 0x44, 0xE5, 0x16, 0xE4, 0xF0, 0x8D, 0xEB, 0x82, 0xBF, 0x14, 0x00, + 0x00, 0x00, 0x80, 0xE9, 0xCE, 0x3B, 0x70, 0xFD, 0xC9, 0xDC, 0xE3, 0x23, 0x1C, 0xB7, 0x20, 0x00, + 0x00, 0x00, 0x00, 0xA2, 0xCC, 0xFE, 0x2B, 0x6D, 0x2C, 0x14, 0x10, 0x1F, 0x6E, 0xEE, 0x16, 0xE4, + 0xF0, 0x2A, 0x77, 0x0B, 0xB2, 0x3E, 0xF8, 0xCB, 0x01, 0x00, 0x00, 0x00, 0x98, 0xAE, 0x6C, 0x18, + 0x2C, 0x31, 0x7B, 0x2E, 0xB3, 0xB1, 0x50, 0x40, 0x7C, 0x84, 0xDB, 0x77, 0xB9, 0x7B, 0x23, 0xD6, + 0xB8, 0x7B, 0xDD, 0x6F, 0xF0, 0x97, 0x05, 0x00, 0x00, 0x00, 0x30, 0x1D, 0x99, 0x43, 0x37, 0xAD, + 0x9D, 0x7C, 0x03, 0x96, 0x8A, 0x8B, 0x56, 0x13, 0xC1, 0x11, 0xDF, 0xF6, 0x8F, 0x71, 0x0B, 0x02, + 0x00, 0x00, 0x00, 0x20, 0x78, 0x03, 0xD6, 0xF6, 0x8F, 0xDB, 0x50, 0x50, 0x71, 0xD1, 0x6A, 0x22, + 0x36, 0x1A, 0x8D, 0x5B, 0x10, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xB3, 0x37, 0x0D, 0xEA, 0xB8, 0x68, + 0x35, 0x11, 0x19, 0xAD, 0x76, 0xA8, 0xDF, 0x3D, 0x90, 0x3E, 0x18, 0xFC, 0xA5, 0x01, 0x00, 0x00, + 0x00, 0x4C, 0x37, 0xDE, 0x81, 0x6B, 0x46, 0x75, 0x60, 0x34, 0x9B, 0x88, 0x8B, 0x56, 0xDB, 0xFA, + 0x21, 0x63, 0x86, 0xE6, 0x57, 0x5E, 0xCB, 0x3B, 0xB2, 0x66, 0x76, 0xF0, 0x97, 0x07, 0x00, 0x00, + 0x00, 0x30, 0x9D, 0x98, 0x03, 0x57, 0xDB, 0x40, 0x50, 0x91, 0xD1, 0x68, 0x22, 0x2E, 0x5A, 0xCD, + 0xC5, 0x47, 0x38, 0x6E, 0x41, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC9, 0xDD, 0x44, 0x98, 0x7D, 0x9F, + 0xB1, 0x91, 0xA0, 0x42, 0x43, 0x4D, 0xC4, 0x45, 0xAB, 0x45, 0xE3, 0xC3, 0x6D, 0xF7, 0xDC, 0xCA, + 0x2D, 0xC8, 0x73, 0xAB, 0x07, 0x82, 0x5F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xE9, 0xC0, 0x3C, 0xB7, + 0x66, 0xC0, 0xEC, 0x9E, 0x67, 0x43, 0x41, 0xC5, 0x46, 0x7C, 0x22, 0x2E, 0x5A, 0x2D, 0x1E, 0x1F, + 0xE1, 0x86, 0xAF, 0x73, 0x0F, 0xA4, 0x8F, 0x06, 0xBF, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x81, + 0xF7, 0xEC, 0x4D, 0x77, 0x99, 0x9D, 0x97, 0xD8, 0x58, 0x50, 0xC1, 0x11, 0x9D, 0x88, 0x8B, 0x56, + 0x53, 0xE1, 0x11, 0x6E, 0xD7, 0xA7, 0xB9, 0x05, 0x01, 0x00, 0x00, 0x00, 0xA6, 0x1B, 0xEF, 0xF0, + 0x8D, 0x1B, 0x74, 0x70, 0x44, 0x27, 0xE2, 0xA2, 0xD5, 0x54, 0x74, 0xC4, 0xC7, 0x2D, 0x08, 0x00, + 0x00, 0x00, 0x30, 0xBD, 0x78, 0x07, 0xAF, 0x6F, 0xF1, 0x06, 0x2C, 0x11, 0x17, 0xAD, 0xA6, 0x62, + 0x43, 0xCD, 0x7D, 0xF6, 0x88, 0xBB, 0x05, 0x39, 0x7C, 0xE3, 0xBA, 0xE0, 0x97, 0x03, 0x00, 0x00, + 0x00, 0xA0, 0xCC, 0xBC, 0x03, 0xD7, 0x9D, 0xD4, 0xE1, 0xE1, 0x26, 0xE2, 0xA2, 0xD5, 0x54, 0x68, + 0x34, 0xDB, 0xFE, 0x2B, 0xDD, 0x1B, 0xB1, 0x4E, 0x06, 0xBF, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xD6, 0xF8, 0x0D, 0x58, 0x22, 0x2E, 0x5A, 0x4D, 0x05, 0x46, 0xAB, 0xB9, 0x5B, 0x90, 0xC3, 0x37, + 0x70, 0x0B, 0x02, 0x00, 0x00, 0x00, 0x94, 0x9D, 0xFF, 0x06, 0xAC, 0xA1, 0x05, 0x36, 0x1E, 0x3A, + 0x14, 0x1F, 0xFE, 0x3E, 0x68, 0x5C, 0x04, 0x79, 0x87, 0x6E, 0x18, 0x37, 0xA3, 0xFD, 0xB3, 0x82, + 0x5F, 0x1A, 0x00, 0x00, 0x00, 0x80, 0xB2, 0x71, 0xB7, 0x0E, 0x66, 0xD7, 0x1C, 0x1B, 0x10, 0x1D, + 0x8C, 0x0F, 0x37, 0xF7, 0xD7, 0x3D, 0xB4, 0xD2, 0x98, 0xC3, 0xAB, 0xD7, 0x07, 0xBF, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x65, 0x63, 0x03, 0x64, 0xBD, 0xD9, 0x7E, 0x71, 0x25, 0x00, 0x3A, 0x15, 0x1F, + 0xE1, 0xF6, 0x5E, 0xC6, 0x2D, 0x08, 0x00, 0x00, 0x00, 0x50, 0x66, 0xDE, 0xC8, 0xCA, 0xA1, 0xAE, + 0x88, 0x0F, 0xB7, 0xE0, 0x16, 0xC4, 0x3B, 0xBC, 0x7A, 0x43, 0xF0, 0xCB, 0x03, 0x00, 0x00, 0x00, + 0x50, 0x26, 0xDE, 0xC1, 0x6B, 0xC7, 0xBA, 0x22, 0x3E, 0xC2, 0xED, 0xEE, 0xAB, 0x3C, 0x90, 0x3E, + 0xB2, 0x66, 0x76, 0xF0, 0x4B, 0x04, 0x00, 0x00, 0x00, 0x50, 0x16, 0x66, 0xFF, 0x67, 0x75, 0x5C, + 0xB4, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0xD1, 0x11, 0xDF, 0xC8, 0x0A, 0xE3, 0x1D, 0x5A, 0x3D, 0x18, + 0xFC, 0x12, 0x01, 0x00, 0x00, 0x00, 0x94, 0x81, 0xBB, 0x65, 0x30, 0xFB, 0x2E, 0xD7, 0x81, 0xD1, + 0x6C, 0x32, 0x2C, 0x92, 0x4C, 0xC4, 0x86, 0x1A, 0xB7, 0x20, 0x00, 0x00, 0x00, 0x40, 0xF9, 0x98, + 0xE7, 0x56, 0x0F, 0x98, 0x5D, 0x97, 0xEA, 0xC8, 0x68, 0x34, 0x19, 0x16, 0x49, 0x26, 0x42, 0xA3, + 0xD9, 0x0E, 0x7E, 0xCE, 0x78, 0x87, 0x6F, 0x18, 0x0D, 0x7E, 0xA9, 0x00, 0x00, 0x00, 0x00, 0x7A, + 0x9D, 0xFF, 0x06, 0xAC, 0x1D, 0x9F, 0xD0, 0xA1, 0xA1, 0x26, 0xC3, 0x22, 0xC9, 0x44, 0x60, 0xB4, + 0xDA, 0xCE, 0x4B, 0x82, 0x5B, 0x90, 0xD5, 0x03, 0xC1, 0x2F, 0x17, 0x00, 0x00, 0x00, 0x40, 0x2F, + 0xF3, 0x9E, 0xBD, 0x61, 0x50, 0x86, 0x86, 0x9A, 0x0C, 0x8B, 0x24, 0x13, 0x71, 0x91, 0x74, 0x07, + 0xAF, 0x35, 0xDE, 0xE1, 0x55, 0xDC, 0x82, 0x00, 0x00, 0x00, 0x00, 0x65, 0xE0, 0x1D, 0xBC, 0x6E, + 0x54, 0xC6, 0x46, 0x7C, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, 0x6D, 0xF9, 0x80, 0x31, 0x3B, + 0x67, 0xBB, 0x0F, 0x26, 0x34, 0xE6, 0xD0, 0xAA, 0xB5, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, 0x40, + 0xAF, 0xF2, 0x0E, 0x5C, 0x33, 0x2E, 0x83, 0x23, 0x3A, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, + 0x5C, 0x7C, 0x84, 0xDB, 0x77, 0x85, 0xFB, 0x5C, 0x90, 0x93, 0xC1, 0x2F, 0x19, 0x00, 0x00, 0x00, + 0x40, 0x2F, 0x72, 0x9F, 0x36, 0xDE, 0xF2, 0x0D, 0x58, 0x32, 0x2C, 0x92, 0x4C, 0x44, 0x45, 0xD2, + 0x45, 0xE3, 0xC3, 0x6D, 0xDB, 0xC7, 0x2A, 0xB7, 0x20, 0x87, 0x57, 0xAD, 0x0B, 0x7E, 0xE9, 0x00, + 0x00, 0x00, 0x00, 0x7A, 0x8D, 0xFF, 0x06, 0xAC, 0xDD, 0x73, 0xEB, 0xA3, 0x23, 0x9C, 0x0C, 0x8B, + 0x24, 0x13, 0x51, 0x91, 0x74, 0xF1, 0xF8, 0x08, 0xC7, 0x2D, 0x08, 0x00, 0x00, 0x00, 0xD0, 0xDB, + 0xCC, 0xE1, 0x1B, 0xD7, 0x35, 0x7C, 0x05, 0xAF, 0x0C, 0x8B, 0x24, 0x13, 0x51, 0x91, 0x74, 0x2A, + 0x3C, 0xC2, 0x6D, 0xB5, 0xBF, 0xA6, 0x43, 0x03, 0xEE, 0x26, 0x64, 0x7D, 0xF0, 0xCB, 0x07, 0x00, + 0x00, 0x00, 0xD0, 0x4B, 0xFC, 0x57, 0xF0, 0xF6, 0x42, 0x7C, 0x84, 0xDB, 0xB3, 0xD8, 0x7D, 0x3A, + 0xFA, 0xB8, 0xFB, 0xD6, 0xB1, 0xE0, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x57, 0x78, 0xC3, 0xCB, + 0xEB, 0xDF, 0x80, 0x25, 0xC3, 0x22, 0xC9, 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x0D, 0x35, 0x6E, 0x41, + 0x00, 0x00, 0x00, 0x80, 0xDE, 0xE5, 0x1D, 0xBC, 0x76, 0xAC, 0x67, 0xE2, 0x23, 0xDC, 0x9E, 0x45, + 0xC6, 0x3B, 0xB4, 0x6A, 0xDC, 0x8C, 0xF4, 0xCF, 0x0E, 0x7E, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x7A, + 0x81, 0xD9, 0x7F, 0x45, 0x6F, 0xC5, 0x87, 0xBF, 0xF7, 0x1B, 0x33, 0xB2, 0xCC, 0x46, 0xC8, 0xCA, + 0xC1, 0xE0, 0xB7, 0x01, 0x00, 0x00, 0x00, 0xA0, 0xDB, 0x99, 0xC3, 0x37, 0x2E, 0x71, 0xB7, 0x09, + 0x3D, 0x17, 0x1F, 0x6E, 0xEE, 0xCD, 0x5D, 0x87, 0x57, 0xD9, 0x10, 0xE9, 0xCD, 0x5B, 0x10, 0xF7, + 0xEB, 0xB6, 0x1B, 0x08, 0xC6, 0x4D, 0x0E, 0x00, 0x00, 0x00, 0xCA, 0xCF, 0x1C, 0xBA, 0x69, 0xAD, + 0xFF, 0x06, 0x2C, 0x19, 0x16, 0x49, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x5A, 0x10, 0x1F, + 0xE1, 0x7A, 0xEC, 0x16, 0xC4, 0xFF, 0xCC, 0x95, 0x91, 0xFE, 0xF5, 0xDE, 0xA1, 0x81, 0x93, 0xFE, + 0x73, 0x2C, 0x91, 0xF9, 0xFF, 0x9A, 0xFD, 0xF7, 0x78, 0xB8, 0x1E, 0x00, 0x00, 0x00, 0xA5, 0x55, + 0x79, 0x03, 0xD6, 0x47, 0x6D, 0x10, 0xA8, 0xB8, 0x68, 0x35, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, + 0xD5, 0x62, 0xF1, 0xE1, 0xB6, 0xF3, 0x53, 0xC1, 0x2D, 0xC8, 0xEA, 0x81, 0xE0, 0xB7, 0xD4, 0xB5, + 0xCC, 0xA1, 0xFE, 0xB5, 0xDE, 0xC8, 0xC0, 0x78, 0x3C, 0x3C, 0xE2, 0xAB, 0xFC, 0x67, 0xFA, 0xD7, + 0x06, 0xFF, 0x35, 0x00, 0x00, 0x00, 0xA0, 0x3C, 0xBC, 0x67, 0x6F, 0x18, 0xD4, 0x71, 0xD1, 0x6A, + 0x22, 0x2A, 0x92, 0x4E, 0xC6, 0x45, 0xAB, 0x89, 0xF8, 0x08, 0x77, 0xF0, 0x1A, 0xE3, 0x1D, 0x5E, + 0x39, 0x1A, 0xFC, 0x96, 0xBA, 0x92, 0x19, 0xE9, 0x5F, 0x57, 0x8D, 0x8C, 0x91, 0xE5, 0xFE, 0x43, + 0xF4, 0x66, 0xC7, 0xC7, 0x6B, 0xE7, 0xFE, 0x35, 0xF7, 0xEF, 0xF9, 0xFF, 0x99, 0x7E, 0x37, 0x3E, + 0xF1, 0x1D, 0x00, 0x00, 0x00, 0xE5, 0xE2, 0x1D, 0xB8, 0x7A, 0x54, 0x07, 0x46, 0xB3, 0x89, 0xA8, + 0x48, 0x3A, 0x19, 0x17, 0xAD, 0x26, 0xA2, 0x23, 0xBA, 0x2E, 0xBF, 0x05, 0xB1, 0x21, 0x31, 0xBB, + 0x7A, 0xF3, 0x31, 0x34, 0xBF, 0xF2, 0x6B, 0xDE, 0x3C, 0x4B, 0xCF, 0xFD, 0x7B, 0x43, 0x7D, 0x7E, + 0x80, 0x78, 0x23, 0x2B, 0x78, 0xCB, 0x17, 0x00, 0x00, 0x00, 0xCA, 0xC5, 0xEC, 0xBF, 0xD2, 0x86, + 0x81, 0x8A, 0x8C, 0x46, 0x13, 0x51, 0x91, 0x74, 0x32, 0x2E, 0x5A, 0x2D, 0x12, 0x1A, 0xCD, 0xE6, + 0x6E, 0x41, 0x0E, 0xAD, 0x1A, 0x0B, 0x7E, 0x5B, 0x5D, 0xC5, 0xC6, 0xC7, 0x90, 0x1F, 0x1F, 0xBB, + 0x3E, 0xAD, 0xA3, 0xA3, 0x66, 0x17, 0x55, 0xB6, 0xF3, 0x92, 0x4A, 0x84, 0x0C, 0xAF, 0xE8, 0xEA, + 0x9B, 0x1D, 0x00, 0x00, 0x00, 0x20, 0x31, 0xFF, 0x81, 0xE8, 0xBD, 0x97, 0xDB, 0x38, 0x50, 0xA1, + 0xA1, 0x26, 0xA2, 0x22, 0xE9, 0x64, 0x5C, 0xB4, 0x9A, 0x08, 0x8D, 0x46, 0x73, 0xCF, 0xB1, 0xB8, + 0x5B, 0x90, 0xC3, 0xAB, 0xBA, 0xEA, 0xDB, 0x96, 0x6C, 0x44, 0x0C, 0xF8, 0xF1, 0xE1, 0xDE, 0xD8, + 0x25, 0x83, 0x23, 0xBA, 0x20, 0x3E, 0x62, 0x11, 0xE2, 0x7E, 0x8E, 0xE0, 0xA7, 0x03, 0x00, 0x00, + 0x00, 0x7A, 0x97, 0x79, 0x6E, 0xF5, 0x80, 0x7F, 0x30, 0x96, 0xB1, 0x11, 0x9F, 0x88, 0x8A, 0xA4, + 0x93, 0x71, 0xD1, 0x6A, 0x22, 0x32, 0x5A, 0x6D, 0xDF, 0x12, 0xE3, 0x1D, 0x5E, 0x75, 0x32, 0xF8, + 0xED, 0x75, 0x05, 0xEF, 0xD0, 0xC0, 0xA8, 0x39, 0x70, 0x55, 0x10, 0x18, 0xCD, 0x16, 0x8B, 0x8F, + 0x70, 0xFB, 0xAF, 0xE4, 0x16, 0x04, 0x00, 0x00, 0x00, 0xE5, 0xE0, 0x3D, 0x7B, 0xC3, 0x5D, 0x66, + 0xC7, 0xA7, 0x6C, 0x24, 0xA8, 0xE0, 0x88, 0x4E, 0x44, 0x45, 0xD2, 0xC9, 0xB8, 0x68, 0x35, 0x11, + 0x17, 0x49, 0x16, 0xDC, 0x82, 0x78, 0x87, 0x56, 0xDD, 0x15, 0xFC, 0x16, 0x3B, 0xAA, 0x7A, 0xFB, + 0xE1, 0x1E, 0x30, 0x97, 0xD1, 0x11, 0x4E, 0x84, 0x47, 0xB8, 0xED, 0x17, 0x73, 0x0B, 0x02, 0x00, + 0x00, 0x80, 0x72, 0xF0, 0x0E, 0xDF, 0xB8, 0x41, 0x07, 0x47, 0x74, 0x22, 0x2A, 0x92, 0x4E, 0xC6, + 0x45, 0xAB, 0x89, 0xB0, 0x48, 0xB3, 0x3D, 0x0B, 0x8D, 0x37, 0xB2, 0x72, 0xBC, 0x1B, 0x3E, 0x4B, + 0x23, 0xD9, 0xED, 0x87, 0x88, 0x8E, 0xF8, 0xB8, 0x05, 0x01, 0x00, 0x00, 0x40, 0x19, 0x78, 0x07, + 0xAF, 0x6B, 0xF1, 0x06, 0x2C, 0x11, 0x15, 0x49, 0x27, 0xE3, 0xA2, 0xD5, 0x44, 0x50, 0xA4, 0xDA, + 0x1F, 0x57, 0xFE, 0xDA, 0x23, 0x2B, 0xEC, 0x56, 0xAE, 0x0F, 0x7E, 0x9B, 0x1D, 0xE1, 0x3E, 0xC7, + 0xC3, 0xBF, 0xFD, 0x70, 0x7F, 0x1F, 0x65, 0x78, 0xB8, 0x89, 0xD8, 0x50, 0xF3, 0x7F, 0x4F, 0xFD, + 0xC6, 0x1C, 0xE4, 0xB3, 0x41, 0x00, 0x00, 0x00, 0xD0, 0xC3, 0xBC, 0x03, 0x9F, 0x3B, 0x59, 0x1F, + 0x1D, 0xE1, 0x22, 0x31, 0x91, 0x76, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xD9, 0xF8, 0x08, + 0xD7, 0x05, 0xB7, 0x20, 0xFE, 0xA7, 0x9A, 0xBB, 0x07, 0xFC, 0x65, 0x78, 0xB8, 0x89, 0xD0, 0x68, + 0xB6, 0xBD, 0x97, 0xB9, 0x5B, 0x90, 0xAE, 0x7A, 0xBE, 0x05, 0x00, 0x00, 0x00, 0x48, 0xA5, 0xF1, + 0x1B, 0xB0, 0x44, 0x54, 0x24, 0x9D, 0x8C, 0x8B, 0x56, 0x53, 0x41, 0x91, 0x66, 0x91, 0xF8, 0x70, + 0x73, 0xBF, 0x8E, 0xE1, 0xA5, 0xEE, 0x13, 0xC5, 0x37, 0x04, 0xBF, 0xD5, 0x29, 0x65, 0xC2, 0x0F, + 0x1D, 0x74, 0x7F, 0x2F, 0xF3, 0x88, 0x0F, 0x37, 0xF7, 0x7B, 0xF2, 0x9F, 0x05, 0x59, 0xCE, 0x87, + 0x13, 0x02, 0x00, 0x00, 0xA0, 0xF7, 0xF8, 0x6F, 0xC0, 0x72, 0x1F, 0x8A, 0x57, 0xB6, 0xF8, 0x08, + 0xB7, 0x6B, 0x8E, 0x31, 0x87, 0x56, 0xBA, 0x43, 0xFB, 0x94, 0x7F, 0x90, 0x5F, 0xF3, 0xDB, 0x0F, + 0x11, 0x17, 0x49, 0xB7, 0x67, 0xB1, 0xF1, 0x86, 0x97, 0x73, 0x0B, 0x02, 0x00, 0x00, 0x80, 0xDE, + 0x63, 0x0E, 0xDD, 0xB4, 0xD6, 0xFF, 0x60, 0xBC, 0x32, 0xC6, 0x47, 0x38, 0x77, 0x0B, 0x72, 0x68, + 0xE5, 0x60, 0xF0, 0x5B, 0x9E, 0x12, 0xCD, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0xDD, 0x33, 0x17, 0xDA, + 0xDF, 0x93, 0xFD, 0x7B, 0x66, 0x7F, 0x4F, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0xE7, 0x98, 0xC3, + 0x37, 0xAE, 0xF7, 0x5F, 0x5B, 0x5B, 0xD6, 0xF8, 0x70, 0x0B, 0x6F, 0x41, 0x0E, 0xF7, 0x2F, 0x09, + 0x7E, 0xDB, 0x85, 0x72, 0xCF, 0x9C, 0xF8, 0xB7, 0x1F, 0xEE, 0x66, 0x29, 0xEF, 0xF8, 0x08, 0xB7, + 0x7B, 0x9E, 0xF1, 0x46, 0x96, 0x8F, 0x9B, 0xA1, 0xCE, 0xBF, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, + 0xCC, 0x1B, 0x59, 0x39, 0x54, 0xEA, 0xF8, 0x08, 0x77, 0xE0, 0x6A, 0x77, 0x0B, 0x32, 0x25, 0xAF, + 0xB0, 0x35, 0x23, 0xFD, 0xEB, 0xCD, 0xC8, 0x72, 0xFB, 0xD7, 0xB5, 0xBF, 0xC6, 0x22, 0xE2, 0xC3, + 0x6D, 0xF3, 0xFB, 0xC2, 0x5B, 0x90, 0x8E, 0xBE, 0xE5, 0x0B, 0x00, 0x00, 0x00, 0x48, 0xC5, 0xDB, + 0x7F, 0xED, 0x58, 0xE9, 0xE3, 0xC3, 0x6D, 0xC7, 0x27, 0xC3, 0x67, 0x41, 0x0A, 0xFD, 0x20, 0x3F, + 0xFF, 0xF6, 0x63, 0x64, 0x60, 0xBC, 0xFE, 0xF6, 0x43, 0x44, 0x45, 0xD2, 0xC5, 0xE3, 0x23, 0x1C, + 0xB7, 0x20, 0x00, 0x00, 0x00, 0xE8, 0x35, 0x66, 0xDF, 0x12, 0x1D, 0x15, 0x49, 0x27, 0xE3, 0xA2, + 0xD5, 0x54, 0x50, 0xA4, 0x99, 0x08, 0x8C, 0x56, 0x73, 0x37, 0x06, 0x53, 0x70, 0x0B, 0xA2, 0x6F, + 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8F, 0x70, 0xDC, 0x82, 0x00, 0x00, 0x00, 0xA0, 0x97, 0x98, + 0x91, 0x35, 0xB3, 0xDD, 0xE7, 0x4A, 0xC8, 0xB0, 0x48, 0x32, 0x19, 0x17, 0xAD, 0x16, 0x8F, 0x89, + 0xB4, 0x13, 0x71, 0xD1, 0x6A, 0xEE, 0xA0, 0xEE, 0xB6, 0xED, 0x63, 0x95, 0x5B, 0x90, 0x43, 0xC5, + 0x7C, 0x90, 0x9F, 0xBE, 0xFD, 0x10, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0xE2, 0xE3, 0x16, 0x04, 0x00, + 0x00, 0x00, 0xBD, 0xC2, 0x8C, 0xAC, 0x1E, 0x30, 0x3B, 0x2F, 0xD1, 0x71, 0xD1, 0x6A, 0x32, 0x2E, + 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0x30, 0x3E, 0xC2, 0xED, 0xFD, 0x8C, 0xBB, + 0x05, 0x29, 0xE4, 0x15, 0xB6, 0xEE, 0xF3, 0x46, 0xCC, 0xF0, 0xF5, 0xF6, 0xAF, 0x33, 0x45, 0xF1, + 0x11, 0xEE, 0xE0, 0xE7, 0x6C, 0x84, 0xAC, 0xE8, 0xC8, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x89, + 0x99, 0xC3, 0xAB, 0xD7, 0x9B, 0xED, 0x17, 0xEB, 0xC0, 0x68, 0x36, 0x19, 0x17, 0xAD, 0xA6, 0x82, + 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x5A, 0x3C, 0x3E, 0xDC, 0xB6, 0x7E, 0x38, 0x7C, 0x16, 0x24, 0xD7, + 0x57, 0xD8, 0xDA, 0x9F, 0x6F, 0xB6, 0xFF, 0xDA, 0x5D, 0xF7, 0x4A, 0xE3, 0xA9, 0x8C, 0x0F, 0xB7, + 0x9D, 0xB3, 0xED, 0xEF, 0x67, 0x85, 0xFB, 0x3D, 0x4D, 0xF9, 0x67, 0x9D, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x79, 0x87, 0x57, 0x6F, 0x90, 0x81, 0xD1, 0x6C, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, + 0x89, 0xB8, 0x68, 0x35, 0x15, 0x1F, 0xE1, 0x82, 0x5B, 0x10, 0xF7, 0x2D, 0x53, 0xC1, 0xDF, 0x8A, + 0xB6, 0x79, 0x87, 0x06, 0x06, 0x27, 0x6F, 0x3F, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8C, 0x24, 0x73, + 0xB7, 0x20, 0xC3, 0x2B, 0xA6, 0xF4, 0xB3, 0x4E, 0x00, 0x00, 0x00, 0x80, 0x54, 0xBC, 0x83, 0x9F, + 0x1B, 0x95, 0x91, 0xD1, 0x68, 0x32, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0x35, + 0x15, 0x1D, 0xD1, 0xB9, 0x5F, 0x97, 0x7F, 0x63, 0xB0, 0x32, 0x97, 0x87, 0xB7, 0x6B, 0x6F, 0x3F, + 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8B, 0xA4, 0xE3, 0x16, 0x04, 0x00, 0x00, 0x00, 0xDD, 0xCE, 0xDB, + 0x7F, 0xD5, 0xB8, 0x0C, 0x0D, 0xB5, 0xBA, 0xB0, 0x48, 0x32, 0x15, 0x14, 0x69, 0x26, 0xE2, 0xA2, + 0xD5, 0x54, 0x70, 0xA8, 0x0D, 0x2D, 0x30, 0xDE, 0xC8, 0xCA, 0xF1, 0x3C, 0x6E, 0x41, 0xFC, 0xDB, + 0x8F, 0x03, 0xD7, 0xDA, 0x9F, 0x57, 0x44, 0x45, 0xD2, 0xA9, 0xA8, 0x48, 0x3B, 0xF7, 0x96, 0xAF, + 0xE1, 0x65, 0xDC, 0x82, 0x34, 0x60, 0xC6, 0xFE, 0xC3, 0x2C, 0xF3, 0xF2, 0xBF, 0x5F, 0x67, 0x4E, + 0x7C, 0x65, 0x7D, 0x64, 0x6B, 0xDD, 0xBF, 0x1E, 0xFC, 0x47, 0x00, 0x00, 0x00, 0x50, 0x14, 0x77, + 0xF0, 0x4E, 0xFC, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, + 0x0A, 0x8D, 0x46, 0x73, 0xBF, 0xC6, 0x1C, 0x6E, 0x41, 0xDC, 0xA7, 0xAB, 0xFB, 0xB7, 0x1F, 0xEE, + 0x59, 0x1A, 0x15, 0x16, 0x49, 0xA6, 0x62, 0x22, 0xCB, 0xB6, 0x7F, 0xD4, 0xFE, 0x7E, 0x96, 0xDB, + 0x2D, 0x2D, 0xF4, 0xB3, 0x4E, 0x7A, 0x8D, 0x0B, 0x0C, 0xEF, 0xC4, 0x97, 0x07, 0xCD, 0x2F, 0xBE, + 0x62, 0xE4, 0x4E, 0x7C, 0xC9, 0xF8, 0xFF, 0x3E, 0x21, 0x02, 0x00, 0x00, 0x50, 0x1C, 0xFF, 0x0D, + 0x58, 0xBB, 0xE6, 0xE8, 0xE0, 0x88, 0xAE, 0x2E, 0x2C, 0x92, 0x4C, 0x05, 0x45, 0x9A, 0x89, 0xB8, + 0x68, 0x35, 0x15, 0x19, 0xAD, 0xE6, 0xBF, 0xC2, 0x76, 0xE5, 0x78, 0x3B, 0xDF, 0xB6, 0xE4, 0x1D, + 0x1A, 0x18, 0x35, 0xFB, 0xAF, 0xB4, 0x3F, 0x9F, 0x08, 0x8B, 0x24, 0x53, 0x21, 0x91, 0x69, 0x33, + 0x2B, 0xDB, 0x7F, 0x85, 0xF1, 0x86, 0x97, 0x4F, 0xC9, 0x27, 0xBE, 0xF7, 0x02, 0x73, 0xFC, 0xDF, + 0x2F, 0xF1, 0x7E, 0xF1, 0xE5, 0xF1, 0xDA, 0xE0, 0xB8, 0xDD, 0x98, 0xE3, 0x5F, 0xF4, 0xC3, 0x23, + 0xFA, 0xAF, 0xFB, 0xFF, 0x39, 0xFB, 0x9F, 0x0F, 0xFE, 0xAB, 0x00, 0x00, 0x00, 0xC8, 0x93, 0x39, + 0x7C, 0xE3, 0xBA, 0x96, 0xAF, 0xE0, 0x95, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, + 0xAB, 0xA9, 0xB8, 0x48, 0xBA, 0xE1, 0xEB, 0x8D, 0xFB, 0x16, 0xAA, 0xE0, 0x6F, 0x4B, 0x2A, 0x36, + 0x5C, 0x06, 0xEC, 0xB2, 0xDF, 0x7E, 0xC8, 0x90, 0xC8, 0xB2, 0x20, 0x3E, 0xDC, 0xB8, 0x05, 0xA9, + 0xAA, 0x89, 0x0F, 0x17, 0x1D, 0x2F, 0xD9, 0xFF, 0xAD, 0x5E, 0xB0, 0xB1, 0xF8, 0xFC, 0x92, 0xC9, + 0xBD, 0x70, 0xB5, 0xFD, 0xD7, 0x57, 0x55, 0xFE, 0x7D, 0x22, 0x04, 0x00, 0x00, 0xA0, 0x38, 0xFE, + 0x2B, 0x78, 0x55, 0x74, 0x84, 0x93, 0x71, 0xD1, 0x6A, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, + 0xA9, 0xA8, 0x48, 0xBC, 0x59, 0x95, 0x07, 0xC7, 0xDD, 0xB7, 0x50, 0x65, 0xB8, 0x05, 0xF1, 0x86, + 0x57, 0x64, 0xBF, 0xFD, 0x90, 0x21, 0x91, 0x65, 0x91, 0xF8, 0x08, 0xC7, 0x2D, 0x48, 0x6D, 0x7C, + 0x1C, 0xB9, 0xB1, 0x36, 0x3A, 0xD4, 0x5C, 0x98, 0x1C, 0xFD, 0x42, 0x25, 0x42, 0x4E, 0x7C, 0x85, + 0x1B, 0x24, 0x00, 0x00, 0x80, 0xBC, 0x79, 0x07, 0x97, 0x35, 0x7E, 0x03, 0x96, 0x8C, 0x8B, 0x56, + 0x53, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0x85, 0x1F, 0x16, 0x68, 0xE7, + 0x6E, 0x41, 0x46, 0x06, 0x86, 0x82, 0xBF, 0x35, 0x89, 0xB4, 0x75, 0xFB, 0x21, 0x43, 0x22, 0xCB, + 0x44, 0x7C, 0xB8, 0x6D, 0xFD, 0xD0, 0xB4, 0xBE, 0x05, 0xA9, 0x89, 0x0F, 0x77, 0xEB, 0xA1, 0x82, + 0xA3, 0xD1, 0x5C, 0xAC, 0xF8, 0x37, 0x26, 0x5F, 0x29, 0xE4, 0xD3, 0xF2, 0x01, 0x00, 0x00, 0xA6, + 0x2D, 0x6F, 0xFF, 0x35, 0x63, 0xC4, 0x47, 0xB0, 0xED, 0x1F, 0xB7, 0x87, 0x75, 0xFF, 0x16, 0x24, + 0xF1, 0x81, 0xDD, 0x1B, 0x59, 0x31, 0xE6, 0x3F, 0xC4, 0xAF, 0x02, 0xA3, 0xD9, 0x64, 0x48, 0x64, + 0x99, 0x08, 0x8F, 0xE8, 0xF6, 0x2C, 0xB2, 0x51, 0xB5, 0x7C, 0x2C, 0xF8, 0xE5, 0x4E, 0x1B, 0x6D, + 0xC5, 0x47, 0xB8, 0x63, 0x7F, 0xE2, 0x3F, 0x94, 0x1E, 0xFC, 0x94, 0x00, 0x00, 0x00, 0xC8, 0x83, + 0xD9, 0xF7, 0x19, 0xE2, 0xC3, 0xED, 0x99, 0x60, 0xFB, 0xAF, 0x32, 0xDE, 0xF0, 0x40, 0xA2, 0x6F, + 0xBD, 0xB1, 0xA1, 0xB2, 0xCE, 0xBF, 0xFD, 0x70, 0x7F, 0xCF, 0x54, 0x64, 0x34, 0x9A, 0x0C, 0x89, + 0x2C, 0x13, 0xC1, 0x11, 0x9F, 0xFB, 0x7B, 0xEB, 0xDF, 0x82, 0x2C, 0xCF, 0xF5, 0x13, 0xDF, 0xBB, + 0x59, 0x2E, 0xF1, 0xE1, 0xF6, 0xC2, 0xD5, 0xEE, 0xDB, 0xB0, 0x4E, 0x06, 0x3F, 0x2D, 0x00, 0x00, + 0x00, 0xDA, 0x65, 0x0E, 0xDF, 0xB8, 0xC4, 0x0C, 0x2D, 0x9C, 0xBE, 0xF1, 0x11, 0x46, 0x47, 0xF0, + 0x7F, 0x7B, 0x6E, 0xDB, 0x3F, 0xEE, 0xBE, 0x0D, 0x2B, 0xD1, 0x2D, 0x88, 0x37, 0xBC, 0xE2, 0x64, + 0xEA, 0xDB, 0x0F, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, 0xD1, 0xDC, 0x2D, 0xC8, 0xF0, 0xB2, 0x69, + 0x71, 0x90, 0xCE, 0x2D, 0x3E, 0xC2, 0x1D, 0xBB, 0xC5, 0x04, 0x3F, 0x35, 0x00, 0x00, 0x00, 0xDA, + 0x65, 0x0E, 0xAD, 0x5A, 0x5B, 0xF3, 0x06, 0x2C, 0x19, 0x17, 0xAD, 0xA6, 0x82, 0x22, 0xCD, 0x44, + 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0xAB, 0x8F, 0x0F, 0x3F, 0x3C, 0xC2, 0x6D, 0xB2, 0xDB, 0x7B, + 0x85, 0x39, 0x3F, 0x3C, 0xD0, 0xF4, 0xC0, 0x9E, 0xE9, 0xF6, 0x43, 0x86, 0x44, 0x96, 0x89, 0xC8, + 0x68, 0x36, 0xF7, 0xF7, 0x79, 0x1A, 0xDC, 0x82, 0xE4, 0x1E, 0x1F, 0xCF, 0x7F, 0xC6, 0x98, 0x23, + 0x37, 0x18, 0xF3, 0xF2, 0x9D, 0x7C, 0x9E, 0x0A, 0x00, 0x00, 0x40, 0x1E, 0xFC, 0x37, 0x60, 0x6D, + 0xFB, 0xC8, 0xF4, 0x8D, 0x8F, 0x68, 0x78, 0xB8, 0xB9, 0xF8, 0x70, 0xDB, 0xFC, 0x21, 0xF7, 0x6D, + 0x58, 0xEE, 0x16, 0xA4, 0xE1, 0x81, 0xDD, 0xBF, 0xFD, 0xD8, 0xB3, 0xD0, 0xFE, 0x7C, 0x22, 0x34, + 0xD4, 0x64, 0x48, 0x64, 0x99, 0x08, 0x8C, 0x24, 0x1B, 0x9A, 0x5F, 0xEA, 0x5B, 0x90, 0x42, 0xE2, + 0xC3, 0x6D, 0xEC, 0x3A, 0x17, 0x20, 0xD3, 0xE6, 0xDB, 0xD7, 0x00, 0x00, 0x00, 0x0A, 0xE5, 0x1D, + 0x5A, 0x3D, 0x38, 0xED, 0xE2, 0x23, 0x12, 0x20, 0xD5, 0xE8, 0x88, 0xC4, 0xC7, 0xF9, 0x70, 0x43, + 0x97, 0x9B, 0x73, 0x07, 0xF5, 0x2D, 0xC8, 0xF9, 0x91, 0xFE, 0xF5, 0x66, 0x78, 0x99, 0xFD, 0xB9, + 0xDC, 0xAF, 0x49, 0xC4, 0x46, 0x7C, 0x32, 0x24, 0xB2, 0x4C, 0x84, 0x45, 0xD2, 0xB9, 0xDF, 0xF7, + 0xF0, 0xF5, 0xEE, 0x16, 0xA4, 0xAD, 0x4F, 0x7C, 0xEF, 0x46, 0x85, 0xC5, 0x87, 0xDB, 0x0B, 0x57, + 0x19, 0x73, 0xE2, 0xCE, 0xD2, 0xFD, 0x3D, 0x03, 0x00, 0x00, 0xE8, 0x08, 0x6F, 0xDF, 0x55, 0xA3, + 0x3A, 0x2E, 0x5A, 0x4D, 0x05, 0x45, 0x9A, 0xC5, 0xC2, 0x22, 0xC9, 0x64, 0x54, 0x24, 0x5D, 0x24, + 0x3E, 0x82, 0xD5, 0xDC, 0x78, 0x84, 0xD1, 0xF1, 0x74, 0x64, 0x9B, 0x3F, 0x64, 0xCE, 0x1F, 0x5C, + 0x6E, 0xCE, 0x0D, 0xF7, 0xDF, 0x15, 0xFC, 0xED, 0xF2, 0x99, 0xA1, 0xFE, 0x59, 0xE7, 0x47, 0x56, + 0x8C, 0x9B, 0xDD, 0x7D, 0xF6, 0x60, 0x2F, 0x62, 0x23, 0x3E, 0x19, 0x12, 0x59, 0x26, 0xA2, 0x22, + 0xED, 0x76, 0xCF, 0x35, 0xDE, 0xC8, 0xB2, 0x71, 0xF7, 0x7B, 0x08, 0x7E, 0x3B, 0x3D, 0xAF, 0xD0, + 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x9F, 0x07, 0x02, 0x00, 0x00, 0x90, 0x07, 0xB3, 0xEF, 0x0A, + 0x7B, 0xB8, 0x57, 0x81, 0xD1, 0x6C, 0x2A, 0x28, 0xD2, 0x4C, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, 0x48, + 0xBA, 0x48, 0x78, 0x44, 0x6F, 0x3E, 0xE2, 0xB7, 0x1E, 0x7E, 0x78, 0xBC, 0xCF, 0x9C, 0x7B, 0x6A, + 0x72, 0xE7, 0x77, 0xCE, 0x77, 0xB7, 0x20, 0x35, 0x07, 0xF6, 0xF3, 0x07, 0xFB, 0xD7, 0x7B, 0xC3, + 0xCB, 0x8C, 0xE7, 0x6E, 0x3F, 0x5A, 0x05, 0x88, 0x0C, 0x89, 0x2C, 0x13, 0x31, 0x91, 0x76, 0x9B, + 0x66, 0xD8, 0x1F, 0xED, 0xAF, 0xA9, 0x44, 0xB7, 0x20, 0x53, 0x11, 0x1F, 0x6E, 0xDE, 0xD1, 0x5B, + 0xA6, 0xDD, 0x6B, 0x8C, 0x01, 0x00, 0x00, 0x72, 0x67, 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x8B, 0xED, + 0x01, 0x5F, 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x58, 0x24, 0x99, 0x8C, 0x8A, 0xA4, + 0x8B, 0xC4, 0x47, 0x83, 0x00, 0x09, 0xE3, 0xE3, 0x5C, 0x24, 0x3E, 0xCE, 0x6E, 0x0C, 0xF6, 0xD4, + 0xFB, 0xCD, 0xF9, 0x03, 0xCB, 0x8D, 0x8B, 0x0E, 0xFF, 0xEF, 0x99, 0x0D, 0x91, 0x73, 0xC3, 0x2B, + 0xC6, 0xBD, 0x5D, 0x7D, 0xF6, 0xBF, 0x7F, 0x91, 0x3F, 0xFF, 0x50, 0xAF, 0x42, 0x44, 0x86, 0x44, + 0x96, 0x89, 0x98, 0x48, 0x3B, 0x17, 0x1F, 0xE1, 0x4A, 0x72, 0x0B, 0x32, 0x55, 0xF1, 0xE1, 0xEF, + 0xE8, 0xE7, 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xBB, 0xCC, 0xC8, 0xEA, 0x01, 0xB3, 0xEB, 0x52, + 0x7B, 0xC8, 0x57, 0xA1, 0xA1, 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, + 0x4B, 0x1F, 0x1F, 0xD5, 0xF0, 0x78, 0xD2, 0x6D, 0x96, 0xBF, 0x73, 0x3B, 0xE6, 0x9B, 0xB3, 0x07, + 0x2A, 0xB7, 0x20, 0x2E, 0x44, 0xCE, 0x1F, 0x5C, 0x66, 0xFF, 0xFB, 0x7F, 0xDC, 0x3C, 0x40, 0x64, + 0x48, 0x64, 0x99, 0x88, 0x89, 0xB4, 0x8B, 0xC6, 0x87, 0x9B, 0xFB, 0xB5, 0x1E, 0xBC, 0xB6, 0xA7, + 0x6F, 0x41, 0xA6, 0x34, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0xEC, 0x5F, 0x33, 0xF8, 0xCB, 0x03, 0x00, + 0x00, 0x20, 0x0B, 0xEF, 0xD0, 0xAA, 0xBB, 0xCC, 0xF6, 0x4F, 0xD8, 0x83, 0xBE, 0x8A, 0x8D, 0xF8, + 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, 0x74, 0xB1, 0xF8, 0x08, 0x02, 0x24, + 0x49, 0x7C, 0xBC, 0x6B, 0xE3, 0xE3, 0xDD, 0xC1, 0x59, 0x93, 0xDB, 0xF8, 0x7E, 0x73, 0x6E, 0xEF, + 0xF5, 0xE6, 0xDC, 0x81, 0x81, 0x51, 0xF7, 0xED, 0x58, 0xE7, 0xB7, 0x5F, 0x62, 0xFF, 0xBB, 0x36, + 0x3E, 0xDC, 0x54, 0x84, 0xC8, 0x90, 0xC8, 0x32, 0x11, 0x13, 0x69, 0x17, 0x8F, 0x8F, 0x70, 0x3B, + 0x3E, 0xE9, 0x02, 0xC4, 0xBD, 0xE5, 0x6B, 0x76, 0xF0, 0x8F, 0x43, 0xCF, 0x98, 0xF2, 0xF8, 0x70, + 0x1B, 0x5B, 0x66, 0xCC, 0x89, 0xAF, 0xAC, 0x0D, 0x7E, 0x09, 0x00, 0x00, 0x00, 0xC8, 0xC2, 0x3B, + 0xBC, 0x7A, 0x83, 0x8E, 0x8D, 0xF8, 0x54, 0x50, 0xA4, 0x99, 0x88, 0x8B, 0x56, 0x93, 0x51, 0x91, + 0x74, 0x3A, 0x3E, 0xEA, 0x02, 0xC4, 0x86, 0x47, 0xF8, 0xCC, 0x47, 0x3C, 0x3E, 0x26, 0xDC, 0x9E, + 0xA8, 0xCC, 0xFD, 0xFF, 0x67, 0xB7, 0x7C, 0xDA, 0x4C, 0x3C, 0xD3, 0x6F, 0xCE, 0x0D, 0xD9, 0x10, + 0x79, 0xCA, 0xFD, 0x77, 0x2F, 0xD2, 0x11, 0xB2, 0x49, 0x85, 0x44, 0x96, 0x89, 0x98, 0x48, 0x3B, + 0x15, 0x1E, 0xD1, 0x1D, 0xBC, 0xD6, 0xBD, 0x96, 0x77, 0x30, 0xF8, 0xC7, 0xA1, 0x27, 0x74, 0x24, + 0x3E, 0xDC, 0x5E, 0xBC, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0xB4, 0xCB, 0x3B, 0x78, 0x4D, 0x82, + 0x37, 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0xE1, 0x11, + 0x2E, 0x1E, 0x1F, 0xE1, 0xED, 0x47, 0xE4, 0x99, 0x0F, 0x17, 0x1F, 0xD1, 0xF0, 0x98, 0x78, 0x3C, + 0x98, 0xFB, 0xBF, 0x1F, 0x9B, 0x65, 0x4E, 0x3F, 0x72, 0x9D, 0x39, 0xF3, 0xF0, 0x25, 0x7E, 0x80, + 0x9C, 0xF3, 0x1F, 0x58, 0x8F, 0x05, 0x88, 0x8B, 0x8F, 0x5C, 0x02, 0x44, 0xC4, 0x44, 0xDA, 0xA9, + 0xE0, 0x88, 0xAF, 0xC7, 0x6E, 0x41, 0x3A, 0x16, 0x1F, 0xC1, 0xBC, 0x5F, 0xDC, 0x39, 0x14, 0xFC, + 0x52, 0x00, 0x00, 0x00, 0x90, 0x85, 0xB7, 0xFF, 0xEA, 0x93, 0x3A, 0x3A, 0xC2, 0xA9, 0xA0, 0x48, + 0x33, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x78, 0xB8, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, + 0xDE, 0x7E, 0xD4, 0x7E, 0xEB, 0x55, 0x34, 0x3E, 0xCE, 0xD8, 0xE8, 0x38, 0xF3, 0xF8, 0xFB, 0x82, + 0x55, 0x22, 0xE4, 0xF4, 0xFD, 0x17, 0x99, 0x53, 0xF7, 0x5E, 0x6C, 0x4E, 0xDD, 0x7D, 0x91, 0x39, + 0x6B, 0xFF, 0x33, 0x75, 0xB7, 0x20, 0x36, 0x3C, 0xDC, 0xDA, 0x0F, 0x10, 0x11, 0x13, 0x69, 0xA7, + 0x62, 0xA3, 0xD1, 0x7A, 0xE4, 0x16, 0xA4, 0xD3, 0xF1, 0xE1, 0xE6, 0x1D, 0xFB, 0x53, 0xDE, 0x84, + 0x05, 0x00, 0x00, 0xD0, 0x8E, 0xE6, 0x6F, 0xC0, 0x52, 0x41, 0x91, 0x66, 0x22, 0x2E, 0x5A, 0x4D, + 0x46, 0x45, 0xD2, 0xC5, 0xA2, 0x23, 0xBA, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDC, 0x7E, 0xD4, 0x7C, + 0xDB, 0x55, 0x18, 0x1F, 0x8F, 0xD9, 0xF0, 0xB0, 0x3B, 0x1D, 0xFC, 0x78, 0xE6, 0x91, 0xF7, 0xF9, + 0xE1, 0x11, 0xEE, 0xF4, 0x7D, 0x36, 0x42, 0x36, 0x4E, 0x46, 0x88, 0xF7, 0x74, 0x25, 0x3E, 0xAA, + 0x01, 0x92, 0x39, 0x42, 0x44, 0x4C, 0xA4, 0x9D, 0x8A, 0x8C, 0x66, 0xDB, 0x7E, 0x71, 0xD7, 0xDF, + 0x82, 0x74, 0x43, 0x7C, 0xF8, 0x3B, 0x76, 0x0B, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xC8, 0xCA, 0x7F, + 0x03, 0xD6, 0xEE, 0x79, 0xF6, 0xE0, 0x5F, 0xFE, 0xF8, 0x70, 0x3F, 0x36, 0xBF, 0xFD, 0xB0, 0xF1, + 0xE1, 0x02, 0x24, 0x88, 0x0F, 0x17, 0x1E, 0xA7, 0x1F, 0x9D, 0xDC, 0xA9, 0x7B, 0x67, 0xD5, 0x04, + 0x88, 0xDB, 0xBB, 0x3F, 0x0E, 0x02, 0xE4, 0xA9, 0x0B, 0xED, 0xCF, 0x67, 0xE3, 0x23, 0x1E, 0x21, + 0x32, 0x30, 0x9A, 0x4D, 0xC4, 0x44, 0xDA, 0xA9, 0xC0, 0x48, 0xB2, 0x7D, 0x57, 0x18, 0x6F, 0x78, + 0x79, 0x57, 0x7E, 0xD0, 0x5E, 0xD7, 0xC4, 0x87, 0x9B, 0x7B, 0x13, 0xD6, 0xCB, 0x77, 0xF6, 0xDC, + 0x43, 0xFB, 0x00, 0x00, 0x00, 0x5D, 0xC1, 0x1C, 0x5A, 0xB5, 0xD6, 0xEC, 0x9C, 0x6D, 0x0F, 0xFF, + 0x25, 0x8E, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x88, 0xDD, 0x7E, 0xB8, 0xD7, 0xEC, 0x46, 0x6F, + 0x3F, 0xE2, 0xF1, 0x71, 0xFA, 0x81, 0xDA, 0xDB, 0x8F, 0x70, 0xEE, 0x16, 0xE4, 0xDC, 0x93, 0x76, + 0x1B, 0x2F, 0xF2, 0x23, 0xA4, 0x26, 0x40, 0x5C, 0x50, 0xA4, 0x8A, 0x10, 0x11, 0x13, 0x69, 0xA7, + 0xC2, 0x22, 0xE9, 0xB6, 0x7D, 0x24, 0xB8, 0x05, 0x59, 0x3A, 0x10, 0xFC, 0xE3, 0xD1, 0x15, 0xBA, + 0x2A, 0x3E, 0xDC, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0xB3, 0xAB, 0xFE, 0x1E, 0x01, 0x00, 0x00, 0xF4, + 0x0C, 0x73, 0x78, 0xF5, 0x7A, 0xB3, 0xF5, 0xC3, 0x36, 0x00, 0x4A, 0x1C, 0x1F, 0xC1, 0xD4, 0xED, + 0xC7, 0x59, 0x3F, 0x40, 0x6C, 0x7C, 0xB8, 0xDB, 0x8F, 0xC8, 0xB7, 0x5E, 0x55, 0xC3, 0xE3, 0xC7, + 0x95, 0xA9, 0xDB, 0x0F, 0x7F, 0x5F, 0xBF, 0xD0, 0xBC, 0xFB, 0x48, 0x25, 0x40, 0xCE, 0xB5, 0x75, + 0x0B, 0x22, 0x62, 0x22, 0xED, 0x54, 0x54, 0xA4, 0x9D, 0xBB, 0x05, 0x39, 0xB8, 0xAC, 0x6B, 0x6E, + 0x41, 0xBA, 0x2E, 0x3E, 0xDC, 0x78, 0x13, 0x16, 0x00, 0x00, 0x40, 0x76, 0xDE, 0xC8, 0x8A, 0xA1, + 0xD2, 0xC7, 0x47, 0xC3, 0xDB, 0x8F, 0x59, 0xD5, 0xF8, 0xA8, 0xDE, 0x7E, 0x84, 0xF1, 0x11, 0x84, + 0x87, 0x1F, 0x1F, 0x3F, 0xD2, 0xB7, 0x1F, 0x2E, 0x3E, 0xDC, 0x4E, 0x7F, 0xEB, 0x42, 0x73, 0xB6, + 0xAD, 0x5B, 0x10, 0x11, 0x13, 0x69, 0xA7, 0x62, 0x22, 0xCB, 0xB6, 0xD9, 0x18, 0x1D, 0x5E, 0xD6, + 0x15, 0xB7, 0x20, 0x5D, 0x19, 0x1F, 0xC1, 0xBC, 0x13, 0x5F, 0xEE, 0xA9, 0xD7, 0x16, 0x03, 0x00, + 0x00, 0x74, 0x0D, 0x6F, 0xDF, 0x55, 0x63, 0x65, 0xBF, 0xF9, 0x70, 0xAB, 0xBF, 0xFD, 0x88, 0x04, + 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x70, 0x44, 0x02, 0xE4, 0x94, 0x7B, 0xE8, 0xDC, 0xED, 0xDB, 0xE2, + 0xF6, 0x23, 0x88, 0x8F, 0x70, 0x13, 0x0F, 0x64, 0xBD, 0x05, 0x11, 0x31, 0x91, 0x76, 0x2A, 0x24, + 0x32, 0xED, 0x82, 0xCA, 0xF6, 0x5C, 0xD6, 0xF1, 0x5B, 0x90, 0x6E, 0x8E, 0x0F, 0x37, 0xEF, 0xF8, + 0x9F, 0x75, 0xE5, 0xB3, 0x32, 0x00, 0x00, 0x00, 0x5D, 0xCF, 0xEC, 0xBD, 0xDC, 0x86, 0x40, 0x89, + 0xE3, 0x23, 0xC1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xC1, 0xB7, 0x5E, 0x9D, 0x0A, 0x03, 0xE4, 0x07, + 0xAD, 0xE3, 0xC3, 0xED, 0xF4, 0x3D, 0x2D, 0x6E, 0x41, 0xE4, 0x44, 0x4C, 0xA4, 0x9D, 0x0C, 0x89, + 0x2C, 0x0B, 0xE2, 0xC3, 0xCD, 0xFD, 0xFD, 0xF5, 0x6F, 0x41, 0x96, 0xAF, 0x0B, 0xFE, 0x31, 0x99, + 0x52, 0xDD, 0x1E, 0x1F, 0xFE, 0x8E, 0xFF, 0x29, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x48, 0xCB, 0xBD, + 0x72, 0xD5, 0xEC, 0x59, 0x64, 0x63, 0x40, 0x05, 0x45, 0x9A, 0x89, 0xB8, 0x68, 0xB5, 0xBA, 0xA0, + 0x48, 0x33, 0x11, 0x1A, 0x8D, 0x16, 0x0F, 0x90, 0xE0, 0xF6, 0xE3, 0x6C, 0x92, 0xDB, 0x8F, 0x87, + 0xED, 0xEE, 0x69, 0x1D, 0x1F, 0xE1, 0xC2, 0x5B, 0x90, 0x64, 0x6F, 0xC4, 0x12, 0x31, 0x91, 0x76, + 0x32, 0x24, 0xB2, 0x2C, 0x12, 0x1F, 0xE1, 0x86, 0x16, 0x1A, 0xEF, 0xE0, 0xD2, 0x93, 0xC1, 0x3F, + 0x2A, 0x53, 0xA6, 0x27, 0xE2, 0xC3, 0xED, 0xC8, 0x6A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0x90, 0x96, + 0xFF, 0x0A, 0xDE, 0x9D, 0x9F, 0xB2, 0x41, 0xA0, 0xA2, 0x22, 0xE9, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, + 0x8A, 0xA4, 0x13, 0x91, 0xA1, 0x66, 0x83, 0xA3, 0x1A, 0x1F, 0x36, 0x3C, 0x12, 0xDF, 0x7E, 0x44, + 0xBF, 0xF5, 0xEA, 0xFB, 0xB1, 0xDB, 0x0F, 0x11, 0x1D, 0xD1, 0x25, 0xBF, 0x05, 0x11, 0x31, 0x91, + 0x76, 0x32, 0x24, 0xB2, 0x4C, 0xC4, 0x87, 0x9B, 0xFB, 0x7B, 0x3D, 0xC5, 0xB7, 0x20, 0x3D, 0x13, + 0x1F, 0x3F, 0xBF, 0xDC, 0x98, 0xB1, 0x15, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x20, 0x2D, 0x73, 0x78, + 0xE5, 0x7A, 0xB3, 0xED, 0xA3, 0x36, 0x0A, 0x54, 0x58, 0x24, 0x99, 0x88, 0x8B, 0x56, 0xAB, 0x0B, + 0x8A, 0x34, 0x8B, 0x45, 0x46, 0xB3, 0x45, 0x03, 0xC4, 0xAE, 0x12, 0x1F, 0x36, 0x0E, 0xC2, 0x00, + 0x69, 0xF4, 0xDA, 0xDD, 0x46, 0xB7, 0x1F, 0x22, 0x38, 0xD4, 0x26, 0x7E, 0x50, 0x09, 0x90, 0x9A, + 0x67, 0x41, 0x82, 0x10, 0xA9, 0xDC, 0x82, 0x88, 0x98, 0x48, 0x3B, 0x19, 0x12, 0x59, 0x26, 0xC2, + 0x23, 0xBA, 0x29, 0xBC, 0x05, 0xE9, 0xA9, 0xF8, 0x70, 0x7B, 0xF1, 0x3A, 0x63, 0x7F, 0xBD, 0x77, + 0x05, 0xBF, 0x7C, 0x00, 0x00, 0x00, 0x24, 0xE1, 0x1D, 0x5E, 0xB9, 0x41, 0x87, 0x45, 0x92, 0x89, + 0xB8, 0x68, 0x35, 0x19, 0x15, 0x49, 0x17, 0x0B, 0x8C, 0x66, 0x8B, 0xC5, 0x47, 0xE5, 0xF6, 0x23, + 0x12, 0x1F, 0xCD, 0x6E, 0x3F, 0xC2, 0x00, 0x89, 0xDE, 0x7E, 0x88, 0xD0, 0x68, 0xB8, 0x6F, 0x5C, + 0x68, 0xCE, 0x3E, 0xD1, 0xE0, 0x16, 0x64, 0x93, 0x88, 0x89, 0xB4, 0x93, 0x21, 0x91, 0x65, 0x22, + 0x38, 0xE2, 0x73, 0x7F, 0xDF, 0x0F, 0x5E, 0x57, 0xF8, 0x2D, 0x48, 0xCF, 0xC5, 0x47, 0x30, 0xEF, + 0xC4, 0x9D, 0x1B, 0x82, 0xDF, 0x02, 0x00, 0x00, 0x00, 0x92, 0xF0, 0x0E, 0x5E, 0x3D, 0xAA, 0xE3, + 0xA2, 0xD5, 0x44, 0x5C, 0xB4, 0x5A, 0x5D, 0x50, 0xA4, 0x59, 0x2C, 0x30, 0x9A, 0xAD, 0x41, 0x7C, + 0xF8, 0xB7, 0x1F, 0x2E, 0x3E, 0xD4, 0xED, 0x47, 0x10, 0x1F, 0xD5, 0xDB, 0x8F, 0x87, 0xEC, 0xBE, + 0x91, 0x21, 0x3E, 0x82, 0x85, 0xB7, 0x20, 0xE7, 0x9F, 0x72, 0x7F, 0xDD, 0x20, 0x42, 0x9E, 0x9E, + 0x69, 0x7F, 0x2D, 0x2E, 0x20, 0x82, 0xA9, 0xB8, 0x68, 0x35, 0x19, 0x12, 0x59, 0x26, 0x62, 0xA3, + 0xD1, 0x76, 0xCD, 0x31, 0xDE, 0xF0, 0xD2, 0x71, 0x33, 0xD4, 0x3F, 0x2B, 0xF8, 0xC7, 0x26, 0x57, + 0xBD, 0x1A, 0x1F, 0x7E, 0x80, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x74, 0xBC, 0xFD, 0x9F, 0x1D, + 0xD7, 0x81, 0xD1, 0x6C, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xC5, 0x02, 0xA3, 0xD5, 0xEA, + 0x02, 0x24, 0x88, 0x8F, 0xF8, 0xED, 0x47, 0x10, 0x1F, 0xF2, 0xD9, 0x8F, 0xFB, 0x83, 0xDB, 0x0F, + 0x11, 0x17, 0x89, 0x16, 0xBB, 0x05, 0x39, 0x1F, 0xC4, 0x47, 0x5B, 0x01, 0x22, 0x43, 0x22, 0xCB, + 0x44, 0x64, 0x34, 0x9B, 0x7B, 0x66, 0xC5, 0xDD, 0x82, 0x1C, 0x5C, 0x9A, 0xFB, 0x07, 0xEF, 0xF5, + 0x72, 0x7C, 0xB8, 0x79, 0xC7, 0xFE, 0x64, 0x3C, 0xF8, 0xAD, 0x00, 0x00, 0x00, 0xA0, 0x15, 0x33, + 0xDA, 0x3F, 0x2B, 0xFD, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x8B, 0xC5, 0x45, + 0x92, 0xC5, 0xE3, 0x23, 0x7A, 0xFB, 0x11, 0xC4, 0x47, 0xCD, 0x9B, 0xAF, 0x1A, 0xDD, 0x7E, 0xA8, + 0xB0, 0x48, 0xB1, 0x33, 0xDF, 0xBB, 0x70, 0xF2, 0x59, 0x90, 0xA7, 0x6C, 0x7C, 0xB4, 0x13, 0x21, + 0x32, 0x24, 0xB2, 0x4C, 0x04, 0x46, 0x92, 0x15, 0x70, 0x0B, 0xD2, 0xEB, 0xF1, 0xE1, 0xEF, 0xC8, + 0x4D, 0xC6, 0x8C, 0xFD, 0x87, 0x42, 0x6E, 0x86, 0x00, 0x00, 0x00, 0x4A, 0xC7, 0x7F, 0x03, 0xD6, + 0xAE, 0x4F, 0xDB, 0x48, 0x50, 0xA1, 0xA1, 0x26, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, + 0x8B, 0x56, 0xB3, 0xD1, 0x31, 0x19, 0x20, 0x17, 0x55, 0x02, 0x24, 0xB8, 0xFD, 0xA8, 0xBE, 0x76, + 0xB7, 0x7A, 0xFB, 0xF1, 0xBE, 0xC6, 0xB7, 0x1F, 0x22, 0x28, 0xB2, 0xEC, 0xEC, 0x63, 0x2E, 0x42, + 0x2A, 0x01, 0xE2, 0xDF, 0x82, 0x64, 0x89, 0x10, 0x19, 0x12, 0x59, 0x26, 0xC2, 0x22, 0xE9, 0x72, + 0xBE, 0x05, 0x29, 0x45, 0x7C, 0xB8, 0xBD, 0x34, 0xC0, 0x9B, 0xB0, 0x00, 0x00, 0x00, 0x92, 0x32, + 0x87, 0x57, 0xAD, 0x4B, 0xFE, 0x0A, 0x5E, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x61, + 0x91, 0x74, 0xF1, 0xF8, 0xC8, 0x74, 0xFB, 0xA1, 0x63, 0x22, 0xFD, 0x66, 0x9A, 0x33, 0xF7, 0xCF, + 0x34, 0x67, 0x9F, 0x9C, 0x8C, 0x90, 0xD4, 0x01, 0x22, 0x43, 0x22, 0xCB, 0x44, 0x54, 0xA4, 0xDD, + 0xCE, 0xD9, 0xC6, 0x1B, 0xBE, 0xBE, 0xED, 0x5B, 0x90, 0xD2, 0xC4, 0x87, 0x1B, 0x6F, 0xC2, 0x02, + 0x00, 0x00, 0x48, 0xCE, 0xB8, 0x57, 0xF0, 0xCA, 0xD8, 0x88, 0x4F, 0xC4, 0x45, 0xAB, 0xC9, 0xA8, + 0x48, 0x3A, 0x11, 0x16, 0x49, 0xE6, 0x6E, 0x3F, 0xEC, 0x8F, 0x7E, 0x7C, 0xC4, 0x6E, 0x3F, 0xEA, + 0x9F, 0xFD, 0x68, 0x70, 0xFB, 0x71, 0x5F, 0xFB, 0xDF, 0x7A, 0x55, 0xD9, 0xCC, 0xEA, 0xDE, 0x0D, + 0x6E, 0x41, 0xCE, 0x35, 0xBA, 0x05, 0x51, 0xE1, 0xE1, 0x26, 0x43, 0x22, 0xCB, 0x44, 0x4C, 0x64, + 0xDA, 0x7B, 0x8D, 0x39, 0x70, 0xB5, 0x7B, 0x2D, 0xEF, 0x60, 0xF0, 0x8F, 0x50, 0x6A, 0xA5, 0x8A, + 0x0F, 0xB7, 0xE7, 0xAF, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x49, 0x79, 0x07, 0xAF, 0x4B, 0xF0, + 0x06, 0x2C, 0x11, 0x17, 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x62, 0x51, 0x91, 0x66, 0xE1, 0xCD, 0x47, + 0x83, 0xDB, 0x8F, 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0x78, 0x30, 0xAF, 0x6F, 0xBD, 0x9A, 0x8C, 0x0F, + 0xB7, 0x4C, 0xB7, 0x20, 0x32, 0x24, 0xB2, 0x4C, 0x85, 0x44, 0x96, 0xD9, 0xF8, 0x70, 0xDB, 0xFE, + 0x71, 0xF7, 0x6D, 0x58, 0xC6, 0x8C, 0x5C, 0x9B, 0xFA, 0x13, 0xC0, 0x4B, 0x17, 0x1F, 0xC1, 0xBC, + 0x13, 0x77, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x80, 0x24, 0xBC, 0x7D, 0x57, 0x8E, 0xE9, 0xE8, 0x08, + 0x27, 0xE2, 0xA2, 0xD5, 0x64, 0x54, 0x24, 0x9D, 0x88, 0x8A, 0xA4, 0xF3, 0x6F, 0x3F, 0x82, 0xF8, + 0x88, 0xDD, 0x7E, 0xA8, 0x67, 0x3F, 0xEA, 0x3F, 0x74, 0xD0, 0xC6, 0x47, 0x2E, 0xB7, 0x1F, 0xB5, + 0xF1, 0x11, 0xEE, 0xDD, 0x47, 0x5A, 0x3C, 0x0B, 0xD2, 0x0B, 0xF1, 0xF1, 0x74, 0xB0, 0xFD, 0xE9, + 0x6F, 0x41, 0xCA, 0x1A, 0x1F, 0x6E, 0xDE, 0xB1, 0x5B, 0xA6, 0xE4, 0x83, 0x1A, 0x01, 0x00, 0x00, + 0x7A, 0x9E, 0xD9, 0x7B, 0x99, 0x8D, 0x06, 0x15, 0x1E, 0x6E, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, + 0xD2, 0xC5, 0x82, 0x22, 0xED, 0xC2, 0xF0, 0x68, 0x70, 0xFB, 0x51, 0xF7, 0xEC, 0x47, 0x10, 0x20, + 0x95, 0xDB, 0x0F, 0x1B, 0x1F, 0xB9, 0xDC, 0x7E, 0xE8, 0xF8, 0x70, 0x3B, 0x7D, 0xEF, 0x4C, 0x73, + 0xAE, 0xD1, 0x2D, 0x88, 0x1F, 0x1D, 0x3D, 0x12, 0x1F, 0xF6, 0xFF, 0xF6, 0xB6, 0x7F, 0xDC, 0x05, + 0x48, 0xE2, 0x5B, 0x90, 0x32, 0xC7, 0x87, 0xBF, 0x23, 0x37, 0x99, 0xE0, 0xB7, 0x0A, 0x00, 0x00, + 0x80, 0x46, 0xCC, 0xE1, 0xFE, 0x25, 0x66, 0x68, 0xBE, 0x0D, 0x87, 0x72, 0xC4, 0x47, 0xCB, 0xDB, + 0x0F, 0x11, 0x1F, 0x95, 0xDB, 0x0F, 0x1B, 0x1E, 0xB9, 0xDC, 0x7E, 0xE8, 0xF0, 0x88, 0x2E, 0xBC, + 0x05, 0x69, 0xF8, 0x2C, 0x88, 0x0C, 0x89, 0x2C, 0x53, 0x21, 0x91, 0x65, 0x22, 0x3E, 0xEC, 0x8F, + 0xFE, 0xF6, 0x7C, 0xC6, 0x9C, 0x3F, 0x70, 0xFD, 0x50, 0xF0, 0x8F, 0x53, 0x43, 0xA5, 0x8F, 0x0F, + 0x37, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x9A, 0xFF, 0x0A, 0x5E, 0xF9, 0x06, 0x2C, 0x11, 0x17, + 0xAD, 0x26, 0xA3, 0x22, 0xE9, 0x44, 0x50, 0xA4, 0x9A, 0x8D, 0x8F, 0x30, 0x3C, 0xC2, 0xF8, 0x48, + 0x7A, 0xFB, 0xF1, 0x63, 0x1B, 0x1E, 0x2E, 0x40, 0x7E, 0xD8, 0xEE, 0xED, 0x87, 0x0E, 0x8E, 0xF8, + 0xDC, 0x2D, 0x48, 0xE3, 0x67, 0x41, 0x54, 0x48, 0x64, 0x99, 0x0A, 0x89, 0x2C, 0x6B, 0x12, 0x1F, + 0x4F, 0xBD, 0xD7, 0x9C, 0xDF, 0xF2, 0x21, 0x73, 0x7E, 0xFF, 0x32, 0x73, 0x76, 0xFF, 0xD2, 0x86, + 0x07, 0xEF, 0x69, 0x11, 0x1F, 0x6E, 0x2F, 0x5E, 0xE7, 0x02, 0x64, 0x5D, 0xF0, 0xDB, 0x06, 0x00, + 0x00, 0x80, 0xE2, 0xBF, 0x01, 0x6B, 0xEB, 0x87, 0x6C, 0x40, 0xF4, 0x7E, 0x7C, 0x64, 0xB9, 0xFD, + 0xA8, 0xC6, 0x87, 0xBB, 0xFD, 0xF8, 0x56, 0x3B, 0xB7, 0x1F, 0x3A, 0x36, 0x1A, 0x6D, 0xE2, 0x21, + 0x75, 0x0B, 0x32, 0xC3, 0x9F, 0x0E, 0x8A, 0x34, 0x53, 0x21, 0x91, 0x65, 0x2D, 0xE2, 0xC3, 0x6D, + 0xE3, 0x05, 0xE6, 0xFC, 0xEE, 0x25, 0xE6, 0xDC, 0xDE, 0x65, 0xF2, 0x01, 0xEC, 0x69, 0x13, 0x1F, + 0x6E, 0xCF, 0x5F, 0x69, 0xCC, 0x89, 0x3B, 0x73, 0xFF, 0x94, 0x78, 0x00, 0x00, 0x80, 0x52, 0xF1, + 0x0E, 0xAD, 0x1C, 0xEC, 0xF9, 0xF8, 0xC8, 0x78, 0xFB, 0x71, 0xEA, 0x51, 0x1B, 0x1D, 0xB9, 0xDC, + 0x7E, 0xE8, 0xC8, 0x68, 0xB6, 0xD3, 0xF7, 0xC4, 0x6F, 0x41, 0x2A, 0xF1, 0xD1, 0x7E, 0x80, 0xA8, + 0x90, 0xC8, 0xB2, 0x64, 0xF1, 0x71, 0xEE, 0x49, 0xBB, 0x4D, 0x1F, 0x76, 0x01, 0x62, 0xCE, 0x0E, + 0xD5, 0xDE, 0x82, 0x4C, 0xAB, 0xF8, 0x08, 0xE6, 0x9D, 0xF8, 0x32, 0x6F, 0xC2, 0x02, 0x00, 0x00, + 0x68, 0xC6, 0xDB, 0x77, 0x45, 0xE4, 0x15, 0xBC, 0x22, 0x2E, 0x5A, 0x4D, 0x46, 0x45, 0xD2, 0xA9, + 0xA0, 0x48, 0xB3, 0x20, 0x3E, 0x82, 0xA5, 0xBB, 0xFD, 0x88, 0xC4, 0x47, 0x5B, 0xB7, 0x1F, 0x3A, + 0x30, 0x92, 0x6C, 0xE2, 0x81, 0xE8, 0x2D, 0x88, 0x8D, 0x8F, 0xB6, 0x23, 0x44, 0x85, 0x44, 0x96, + 0x25, 0x8F, 0x8F, 0xB3, 0x83, 0x76, 0x4F, 0x5C, 0x60, 0xDE, 0xDD, 0xB1, 0xC4, 0x4C, 0xEC, 0x5E, + 0x5E, 0x3D, 0x7C, 0x4F, 0xC7, 0xF8, 0x70, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x2D, 0x98, 0x7D, + 0xF6, 0x70, 0xD7, 0xEB, 0xF1, 0x91, 0xE6, 0xF6, 0x23, 0x8C, 0x8F, 0x5C, 0x6E, 0x3F, 0x74, 0x58, + 0x24, 0x5D, 0xE5, 0x16, 0x64, 0xA6, 0x8D, 0x10, 0x1B, 0x20, 0x4F, 0xB5, 0x1B, 0x20, 0x2A, 0x24, + 0xB2, 0x2C, 0x43, 0x7C, 0x3C, 0x7E, 0x81, 0x39, 0x33, 0xF8, 0x41, 0x17, 0x20, 0xE6, 0xF4, 0x8E, + 0xA5, 0x03, 0xD3, 0x35, 0x3E, 0xFC, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x00, 0x34, 0x62, + 0x46, 0xFB, 0x67, 0x99, 0x3D, 0x0B, 0x75, 0x5C, 0xB4, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, + 0x66, 0x91, 0xF8, 0x08, 0xA6, 0x6E, 0x3F, 0xAA, 0x01, 0x52, 0x73, 0xFB, 0x31, 0xAB, 0xFE, 0xF6, + 0xE3, 0x9B, 0x2A, 0x2E, 0x5A, 0x4D, 0x47, 0x45, 0x9A, 0xBD, 0xF3, 0xD7, 0x33, 0xCC, 0x99, 0x1F, + 0x4E, 0x06, 0x48, 0xF6, 0x5B, 0x10, 0x15, 0x12, 0x59, 0x96, 0x31, 0x3E, 0x1E, 0x9D, 0x61, 0x4E, + 0xFF, 0xD8, 0xEE, 0x99, 0x45, 0x66, 0xE2, 0xC0, 0xAA, 0x93, 0xD3, 0x36, 0x3E, 0xDC, 0x8E, 0xAC, + 0x32, 0xE6, 0xF8, 0x1D, 0x4B, 0x82, 0x2F, 0x31, 0x00, 0x00, 0x00, 0x44, 0x99, 0x91, 0xFE, 0x01, + 0xB3, 0xF3, 0x12, 0x1D, 0x18, 0xCD, 0x26, 0xA3, 0x22, 0xE9, 0x54, 0x50, 0xA4, 0x59, 0x2C, 0x3E, + 0x12, 0xDC, 0x7E, 0x4C, 0xB8, 0x00, 0x79, 0xDC, 0xDD, 0x7E, 0xC4, 0xE2, 0xC3, 0xED, 0x7B, 0x59, + 0xBE, 0xF5, 0x4A, 0x07, 0x45, 0x9A, 0xB9, 0xF8, 0xF0, 0x77, 0xF7, 0x0C, 0x7B, 0x90, 0x6F, 0xE7, + 0x16, 0x44, 0x85, 0x44, 0x96, 0xB5, 0x17, 0x1F, 0xA7, 0x1E, 0x76, 0x01, 0x32, 0xDB, 0x98, 0x63, + 0x77, 0x9A, 0x69, 0x1B, 0x1F, 0x3F, 0xBF, 0xCC, 0x98, 0x17, 0x3F, 0x67, 0xCC, 0x89, 0x2F, 0xAD, + 0x0D, 0xBE, 0xC4, 0x00, 0x00, 0x00, 0x10, 0xE5, 0x1D, 0x5A, 0x75, 0x97, 0xD9, 0x7E, 0xB1, 0x8E, + 0x8C, 0x46, 0x93, 0x51, 0x91, 0x74, 0x2A, 0x28, 0xD2, 0x2C, 0x16, 0x1F, 0x2A, 0x40, 0x1A, 0x3E, + 0xFB, 0x61, 0xE3, 0x23, 0x97, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0x56, 0x8D, 0x8F, 0x60, 0x67, 0xBE, + 0x5F, 0x09, 0x90, 0x73, 0xD1, 0x5B, 0x90, 0x20, 0x44, 0x74, 0x74, 0x84, 0x53, 0x21, 0x91, 0x65, + 0x39, 0xC4, 0xC7, 0xD3, 0x1F, 0x33, 0xDE, 0xD1, 0xDB, 0xA7, 0x77, 0x7C, 0xB8, 0x3D, 0xFF, 0x59, + 0xDE, 0x84, 0x05, 0x00, 0x00, 0xD0, 0x88, 0x19, 0x59, 0xB9, 0x5E, 0x46, 0x46, 0xA3, 0xC9, 0xA8, + 0x48, 0x3A, 0x15, 0x14, 0x69, 0x56, 0x1F, 0x1E, 0xD5, 0xF8, 0xB0, 0xE1, 0x31, 0x79, 0xFB, 0x11, + 0xF9, 0xD6, 0xAB, 0xEA, 0xED, 0xC7, 0x45, 0x95, 0xDB, 0x0F, 0xBB, 0xF6, 0x6E, 0x3F, 0x74, 0x50, + 0xA4, 0x59, 0x3C, 0x3E, 0xFC, 0x65, 0xBA, 0x05, 0x51, 0x21, 0x91, 0x65, 0xC4, 0x47, 0xFB, 0x0B, + 0xE2, 0x23, 0x98, 0xF7, 0x8B, 0x3B, 0x5B, 0x7E, 0x30, 0x23, 0x00, 0x00, 0xC0, 0xB4, 0xE4, 0x1D, + 0xB8, 0x7A, 0x54, 0x86, 0x86, 0x9A, 0x8C, 0x8A, 0xA4, 0x53, 0x41, 0x91, 0x66, 0xB1, 0xF8, 0x88, + 0x07, 0x88, 0x5D, 0xF5, 0x5B, 0xAF, 0xC2, 0x00, 0xA9, 0x3E, 0x78, 0x6E, 0xE3, 0xC3, 0x06, 0x48, + 0xFB, 0xB7, 0x1F, 0x3A, 0x28, 0xD2, 0x4C, 0xC6, 0x47, 0xB0, 0xF0, 0x16, 0xC4, 0xBD, 0x92, 0xB7, + 0xF5, 0x2D, 0x88, 0x0A, 0x89, 0x2C, 0x23, 0x3E, 0xDA, 0x5F, 0x6D, 0x7C, 0xF8, 0x01, 0x72, 0xF4, + 0xD6, 0xB1, 0xE0, 0x4B, 0x0C, 0x00, 0x00, 0x00, 0x51, 0xDE, 0xFE, 0x2B, 0x4F, 0xCA, 0xD8, 0x88, + 0x4F, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, 0xB3, 0x58, 0x78, 0x88, 0xF8, 0x08, 0x6F, 0x3F, 0x6A, + 0x1E, 0x3C, 0xF7, 0x6F, 0x3F, 0x2E, 0x9A, 0xBC, 0xFD, 0x08, 0xE3, 0x23, 0x0C, 0x90, 0xFB, 0xD3, + 0xDC, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x1D, 0x35, 0x73, 0xB7, 0x20, 0x8F, 0x4D, 0xDE, 0x82, + 0xF8, 0x11, 0x62, 0x63, 0xA3, 0x3E, 0x40, 0x54, 0x48, 0x64, 0x19, 0xF1, 0xD1, 0xFE, 0xEA, 0xE3, + 0xC3, 0xDF, 0xB1, 0x2F, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x00, 0x25, 0xD1, 0x1B, 0xB0, 0x64, 0x54, + 0x24, 0x9D, 0x0A, 0x8A, 0x34, 0x8B, 0x85, 0x47, 0x93, 0xF8, 0xA8, 0x3E, 0x78, 0x5E, 0xBD, 0xFD, + 0xB0, 0xF1, 0x11, 0xDE, 0x7E, 0xC4, 0xE3, 0xE3, 0x21, 0xBB, 0xBB, 0x55, 0x68, 0xA8, 0xE9, 0xA0, + 0x48, 0x33, 0x19, 0x1C, 0x62, 0xA7, 0xBF, 0x3B, 0x23, 0x78, 0x16, 0xA4, 0xC1, 0xB7, 0x62, 0x3D, + 0xAD, 0x42, 0x22, 0xCB, 0x88, 0x8F, 0xF6, 0x27, 0xC2, 0x23, 0x1C, 0x6F, 0xC2, 0x02, 0x00, 0x00, + 0xA8, 0xE7, 0xBF, 0x01, 0x6B, 0xD7, 0x1C, 0x1D, 0x1D, 0xE1, 0x64, 0x54, 0x24, 0x9D, 0x0A, 0x8A, + 0x34, 0x8B, 0x85, 0x47, 0x38, 0x15, 0x20, 0xF1, 0xD7, 0xEE, 0x3E, 0x39, 0x19, 0x1F, 0xF2, 0xCD, + 0x57, 0x89, 0x6F, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0A, 0x8D, 0x66, 0x7B, 0x37, 0x7E, 0x0B, 0x12, + 0x46, 0x88, 0x8B, 0x8F, 0x70, 0x32, 0x2A, 0x92, 0x8E, 0xF8, 0x68, 0x7F, 0x22, 0x3A, 0xA2, 0x7B, + 0xF1, 0x7A, 0x63, 0x5E, 0xBE, 0xB3, 0xE6, 0x53, 0xE1, 0x01, 0x00, 0x00, 0xA6, 0x3D, 0x73, 0xA8, + 0x7F, 0xAD, 0xD9, 0xF1, 0x49, 0x1D, 0x1E, 0x6E, 0x32, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, 0xC5, + 0xA2, 0x23, 0xBA, 0x78, 0x7C, 0x44, 0x6F, 0x3F, 0x82, 0xF8, 0x98, 0xBC, 0xFD, 0x10, 0xDF, 0x7A, + 0x95, 0xF8, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xA9, 0xC0, 0x68, 0x35, 0x77, 0x0B, 0x52, 0xF7, 0xE1, + 0x84, 0x36, 0x3A, 0xF2, 0x09, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xE0, 0x88, 0xEF, 0x85, 0xAB, 0x79, + 0x13, 0x16, 0x00, 0x00, 0x40, 0x9C, 0xFF, 0x06, 0xAC, 0xAD, 0x1F, 0xEC, 0xB9, 0xF8, 0x68, 0x74, + 0xFB, 0xE1, 0xE2, 0xE3, 0x6C, 0x2C, 0x3E, 0xB2, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x15, 0x17, + 0x49, 0xF7, 0xEE, 0x8F, 0x2B, 0x01, 0xE2, 0xBF, 0x96, 0xF7, 0x29, 0x1B, 0x1F, 0x6E, 0xD1, 0x08, + 0x91, 0x71, 0xD1, 0x6A, 0xC4, 0x47, 0xFB, 0x13, 0xB1, 0xD1, 0x60, 0xDE, 0x89, 0x2F, 0x0D, 0x06, + 0x5F, 0x6A, 0x00, 0x00, 0x00, 0x70, 0xBC, 0xE1, 0xE5, 0x43, 0x3D, 0x15, 0x1F, 0x6E, 0xF1, 0xF8, + 0x88, 0xDE, 0x7E, 0xB8, 0xF8, 0x68, 0x75, 0xFB, 0xF1, 0xA0, 0x9D, 0x0C, 0x8E, 0xE8, 0x74, 0x50, + 0xA4, 0x99, 0x8A, 0x8A, 0x34, 0x3B, 0xFD, 0xED, 0xE0, 0x16, 0xE4, 0x49, 0x1B, 0x20, 0x36, 0x0A, + 0xEA, 0x02, 0x24, 0x75, 0x84, 0x10, 0x1F, 0xED, 0x4F, 0x87, 0x46, 0xA3, 0x79, 0x47, 0x6F, 0x1D, + 0x0D, 0xBE, 0xD4, 0x00, 0x00, 0x00, 0xE0, 0x78, 0x7B, 0xAF, 0xA8, 0x7F, 0x05, 0xAF, 0x8C, 0x8A, + 0xA4, 0x53, 0x41, 0x91, 0x66, 0x22, 0x38, 0xA2, 0xB3, 0xD1, 0xE1, 0x7E, 0xAC, 0xBB, 0xFD, 0xB0, + 0xF1, 0x71, 0x6E, 0xE3, 0x45, 0xC9, 0x6E, 0x3F, 0xEE, 0x6B, 0x75, 0xFB, 0xA1, 0x83, 0x22, 0xCD, + 0x54, 0x50, 0x64, 0xD9, 0xBB, 0xF6, 0xD0, 0x1F, 0x06, 0x48, 0x7B, 0xB7, 0x20, 0xC4, 0x47, 0xFB, + 0xD3, 0x91, 0xD1, 0x74, 0xC7, 0x6E, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0x00, 0x51, 0x66, 0xEF, 0xE2, + 0xDE, 0x89, 0x0F, 0xB7, 0x30, 0x3C, 0xC2, 0xF8, 0x08, 0x03, 0x24, 0x88, 0x8F, 0x78, 0x80, 0xD4, + 0xC5, 0x47, 0xCB, 0xDB, 0x0F, 0x1D, 0x14, 0x69, 0xA6, 0x42, 0x22, 0xDB, 0x2E, 0x30, 0xA7, 0xBF, + 0x6D, 0xA3, 0x60, 0x70, 0x32, 0x42, 0x6A, 0x02, 0xC4, 0x85, 0x45, 0xA2, 0x08, 0x21, 0x3E, 0xDA, + 0x9F, 0x88, 0x8B, 0x24, 0x73, 0x6F, 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x1B, 0x00, 0x00, + 0xC0, 0xF4, 0x66, 0x46, 0xFA, 0x67, 0xD7, 0xBC, 0x82, 0x57, 0x46, 0x45, 0xD2, 0xA9, 0xA0, 0x48, + 0x33, 0x11, 0x1B, 0xF1, 0xD9, 0xE8, 0x70, 0x3F, 0xD6, 0xDF, 0x7E, 0xD8, 0xF0, 0x10, 0xB7, 0x1F, + 0xF2, 0x43, 0x07, 0x9B, 0xDE, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x76, 0x41, 0x75, + 0xE1, 0x2D, 0xC8, 0xB9, 0x4C, 0xB7, 0x20, 0xC4, 0x47, 0xFB, 0x13, 0x61, 0x91, 0x74, 0x63, 0xCB, + 0x79, 0x13, 0x16, 0x00, 0x00, 0x40, 0xC8, 0x7F, 0x05, 0x6F, 0xF8, 0x06, 0x2C, 0x19, 0x15, 0x49, + 0xA7, 0x82, 0x22, 0xCD, 0x62, 0xA1, 0xD1, 0x68, 0x61, 0x78, 0x84, 0xF1, 0xE1, 0xF6, 0xD4, 0x85, + 0x35, 0xDF, 0x7A, 0x15, 0x0F, 0x90, 0x53, 0x2E, 0x40, 0xC2, 0xDB, 0x8F, 0x1F, 0xD9, 0xC9, 0xF0, + 0x70, 0xD3, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x6C, 0x32, 0x3E, 0xDC, 0x4E, 0x7D, 0xD3, 0xC5, + 0x41, 0x96, 0x5B, 0x10, 0xE2, 0xA3, 0xFD, 0x89, 0xA8, 0x48, 0xB3, 0x17, 0x3F, 0xC7, 0x9B, 0xB0, + 0x00, 0x00, 0x00, 0x42, 0xFE, 0x1B, 0xB0, 0xB6, 0x7D, 0xC4, 0x1E, 0xEE, 0x55, 0x54, 0x24, 0x9D, + 0x0A, 0x8A, 0x34, 0x13, 0xA1, 0xA1, 0x66, 0xA3, 0xC3, 0xFD, 0x58, 0x7B, 0xFB, 0x61, 0xE3, 0x23, + 0xCD, 0xED, 0xC7, 0xB7, 0x1A, 0xDD, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, 0x56, 0x1B, + 0x1F, 0xE1, 0x26, 0x7E, 0x94, 0xF6, 0x16, 0x84, 0xF8, 0x68, 0x7F, 0x22, 0x28, 0x32, 0x8C, 0x37, + 0x61, 0x01, 0x00, 0x00, 0x04, 0xBC, 0x91, 0x81, 0x0D, 0x3A, 0x2A, 0x92, 0x4E, 0x05, 0x45, 0x9A, + 0xC5, 0x22, 0xA3, 0xD9, 0xC2, 0xF0, 0x88, 0xC4, 0x47, 0xA2, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x68, + 0xD7, 0xA3, 0xF1, 0xE1, 0xD6, 0xF2, 0x16, 0x84, 0xF8, 0xC8, 0x79, 0x3A, 0x26, 0xB2, 0xCC, 0x3B, + 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x00, 0x38, 0xDE, 0x81, 0xAB, 0x47, 0x75, 0x58, 0x24, 0x99, + 0x0A, 0x8A, 0x34, 0x13, 0x91, 0xD1, 0x62, 0xD5, 0x00, 0x09, 0xE3, 0xA3, 0xED, 0xDB, 0x0F, 0x1D, + 0x14, 0x69, 0xA6, 0x43, 0x22, 0xCB, 0x74, 0x78, 0x44, 0x17, 0xDE, 0x82, 0xB8, 0x78, 0x68, 0x7C, + 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x12, 0x59, 0xE7, 0x1D, 0xBB, 0x65, 0x3C, 0xF8, 0x92, 0x03, + 0x00, 0x00, 0x98, 0xDE, 0xBC, 0x7D, 0x57, 0x8C, 0xEB, 0xB8, 0x68, 0x35, 0x15, 0x14, 0x69, 0xA6, + 0x03, 0xA3, 0xE1, 0x6A, 0x6E, 0x3F, 0x6C, 0x7C, 0x34, 0xBB, 0xFD, 0x08, 0xE3, 0xA3, 0xE5, 0xED, + 0x87, 0x0E, 0x8A, 0x34, 0xD3, 0x21, 0x91, 0x65, 0x3A, 0x38, 0xE2, 0xF3, 0x6F, 0x41, 0x1E, 0x6F, + 0x76, 0x0B, 0x42, 0x7C, 0xB4, 0x3F, 0x1D, 0x11, 0x6D, 0xCD, 0xBD, 0x09, 0x6B, 0xEC, 0x8B, 0xB3, + 0x82, 0x2F, 0x3B, 0x00, 0x00, 0x80, 0xE9, 0xCB, 0x0C, 0x2D, 0xB0, 0x07, 0x7C, 0x15, 0x18, 0xCD, + 0xA6, 0x82, 0x22, 0xCD, 0x44, 0x60, 0x24, 0x58, 0x18, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xB9, 0x1F, + 0xC9, 0x6E, 0x3F, 0x74, 0x50, 0xA4, 0x99, 0x0E, 0x89, 0x2C, 0xD3, 0xB1, 0xD1, 0x68, 0x67, 0xBE, + 0x2F, 0x9E, 0x05, 0xF1, 0x43, 0x24, 0x12, 0x1E, 0x6E, 0xC4, 0x47, 0x86, 0x89, 0x78, 0xC8, 0x63, + 0x63, 0xCB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x80, 0xFF, 0x06, 0xAC, 0x9D, 0xB3, 0xED, 0xE1, 0x5E, + 0x45, 0x46, 0xA3, 0xA9, 0xA0, 0x48, 0x33, 0x1D, 0x17, 0x4D, 0x17, 0xB9, 0xF9, 0xF0, 0xE3, 0x43, + 0xDC, 0x7E, 0x4C, 0xB8, 0x3D, 0x1E, 0xB9, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xE5, 0x89, 0x0F, + 0x7F, 0x77, 0xAB, 0x5B, 0x90, 0x4A, 0x68, 0x18, 0x1B, 0x19, 0xC4, 0x47, 0xD6, 0x89, 0x70, 0xC8, + 0x65, 0x8B, 0x8D, 0x79, 0xF1, 0x5A, 0xE3, 0xFD, 0xE2, 0x4B, 0x77, 0x05, 0x5F, 0x7A, 0x00, 0x00, + 0x00, 0xD3, 0x93, 0x0D, 0x90, 0x75, 0x66, 0xC7, 0x27, 0xEC, 0x21, 0x5F, 0x85, 0x86, 0x9A, 0x0A, + 0x8A, 0x34, 0x13, 0x71, 0x91, 0x64, 0x61, 0x7C, 0xC4, 0x6E, 0x3F, 0x12, 0x3F, 0xFB, 0xF1, 0xCD, + 0x12, 0xC5, 0x47, 0xB0, 0x33, 0xDF, 0x8B, 0x3E, 0x0B, 0x52, 0x89, 0x8C, 0x70, 0x2E, 0x40, 0xC2, + 0xF0, 0x20, 0x3E, 0x92, 0x4E, 0x85, 0x43, 0x1E, 0xB3, 0xF1, 0xE1, 0x66, 0x7F, 0xED, 0xDE, 0x89, + 0x3B, 0x36, 0x04, 0x5F, 0x7A, 0x00, 0x00, 0x00, 0xD3, 0x93, 0xFF, 0x0A, 0x5E, 0x19, 0x1A, 0x6A, + 0x2A, 0x28, 0xD2, 0x4C, 0x84, 0x45, 0xAB, 0xD9, 0xF0, 0xA8, 0xC6, 0x87, 0x0D, 0x8F, 0xC9, 0xDB, + 0x8F, 0xD8, 0xB7, 0x5E, 0x45, 0x6F, 0x3F, 0xEC, 0x1A, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, + 0x12, 0x59, 0xA6, 0xC3, 0x22, 0xF1, 0xAA, 0xB7, 0x20, 0x36, 0x40, 0x36, 0xBA, 0xC0, 0x08, 0x82, + 0x23, 0x12, 0x1E, 0xC4, 0x47, 0xD2, 0xA9, 0x70, 0xC8, 0x63, 0x41, 0x7C, 0x04, 0xF3, 0x4E, 0xFC, + 0x39, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x9B, 0x77, 0xF0, 0xBA, 0x84, 0x6F, 0xC0, 0x52, 0x41, + 0x91, 0x66, 0xB1, 0xB0, 0x48, 0x3A, 0x17, 0x20, 0xE1, 0xCD, 0x87, 0x5D, 0xF5, 0x5B, 0xAF, 0xE4, + 0xB3, 0x1F, 0x17, 0xB5, 0xB8, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x04, 0x45, + 0x86, 0x9D, 0xFE, 0xCE, 0x7B, 0xFD, 0xB0, 0x08, 0x23, 0xA4, 0x1A, 0x1C, 0x2E, 0x3E, 0x82, 0x28, + 0x21, 0x3E, 0x5A, 0x4D, 0x85, 0x43, 0x1E, 0xAB, 0x8D, 0x0F, 0x3F, 0x40, 0x8E, 0x7D, 0x81, 0x37, + 0x61, 0x01, 0x00, 0x80, 0xE9, 0xCD, 0xDB, 0xFB, 0xD9, 0x31, 0x1D, 0x1C, 0xD1, 0xA9, 0xA0, 0x48, + 0xB3, 0x58, 0x54, 0x24, 0x5D, 0x2C, 0x3E, 0xC2, 0xDB, 0x8F, 0xBA, 0xF8, 0x88, 0xDE, 0x7E, 0x84, + 0xF1, 0x11, 0x06, 0xC8, 0x86, 0xF0, 0xF6, 0x43, 0x07, 0x45, 0x9A, 0xE9, 0x90, 0xC8, 0x32, 0x1D, + 0x13, 0xA9, 0xF7, 0x57, 0xEF, 0xF5, 0xF7, 0xEE, 0xA3, 0x93, 0x01, 0x12, 0x9F, 0x1F, 0x27, 0x2E, + 0x3C, 0x88, 0x8F, 0x06, 0x53, 0xE1, 0x90, 0xC7, 0xEA, 0xE3, 0xC3, 0xDF, 0x91, 0x1B, 0x4C, 0xF0, + 0xA5, 0x07, 0x00, 0x00, 0x30, 0x3D, 0x99, 0x3D, 0xF6, 0x50, 0x24, 0xA3, 0x23, 0x9C, 0x0A, 0x8A, + 0x34, 0x8B, 0x45, 0x45, 0xD2, 0x35, 0x88, 0x8F, 0xEA, 0x83, 0xE7, 0xEA, 0xF6, 0x23, 0x1E, 0x1F, + 0x0F, 0xD9, 0xDD, 0x5D, 0xEE, 0xF8, 0x70, 0x73, 0xB7, 0x20, 0x7E, 0x60, 0xD8, 0x9D, 0x7B, 0xD2, + 0x86, 0x87, 0xDD, 0xB9, 0x68, 0x7C, 0xD8, 0xF0, 0x38, 0x6B, 0xC3, 0x83, 0xF8, 0x88, 0x4F, 0x85, + 0x43, 0x1E, 0x8B, 0x45, 0x47, 0x74, 0x2F, 0xAD, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xFA, 0x32, + 0x87, 0xFB, 0x97, 0x98, 0xDD, 0xF3, 0xEC, 0x81, 0x5F, 0x85, 0x87, 0x9B, 0x0A, 0x8A, 0x34, 0x8B, + 0x45, 0x45, 0x9A, 0xD9, 0xE8, 0xA8, 0x0B, 0x90, 0x26, 0xDF, 0x7A, 0x25, 0xDF, 0x7C, 0x75, 0xBF, + 0xBB, 0xFD, 0xD0, 0x41, 0x91, 0x66, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x45, 0xE2, 0x23, + 0x9C, 0xBB, 0x05, 0xF1, 0x43, 0x63, 0xD0, 0xC6, 0x88, 0x0D, 0x90, 0xB3, 0xB1, 0x6F, 0xB9, 0x7A, + 0xF7, 0x31, 0xE2, 0xA3, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x88, 0x8E, 0xE8, 0x5E, 0xBC, 0xD6, 0x06, + 0xC8, 0x1D, 0xEB, 0x82, 0x2F, 0x41, 0x00, 0x00, 0x80, 0xE9, 0xC5, 0x7F, 0x05, 0x6F, 0xC3, 0x37, + 0x60, 0xA9, 0xA0, 0x48, 0x33, 0x11, 0x15, 0x49, 0xF7, 0x8C, 0x88, 0x8F, 0xE8, 0xED, 0x87, 0x8B, + 0x8F, 0x9A, 0x00, 0x11, 0xDF, 0x7A, 0xE5, 0xDF, 0x7E, 0xE8, 0xA0, 0x48, 0x33, 0x1D, 0x12, 0x59, + 0x26, 0x42, 0x22, 0xCB, 0x44, 0x7C, 0xBC, 0xBD, 0xFE, 0xBD, 0xE6, 0xD4, 0x3D, 0x36, 0x42, 0x5C, + 0x68, 0xB8, 0xE0, 0x70, 0x21, 0x12, 0xFC, 0xE8, 0xFE, 0xB5, 0x09, 0x17, 0x1F, 0x36, 0x3C, 0x4E, + 0x3F, 0x42, 0x7C, 0x54, 0xA6, 0xC2, 0x21, 0x8F, 0x89, 0xE0, 0x88, 0xCF, 0xFD, 0xBE, 0x4E, 0xDC, + 0xB1, 0x3E, 0xF8, 0x12, 0x04, 0x00, 0x00, 0x98, 0x5E, 0xFC, 0x37, 0x60, 0x6D, 0xF9, 0x80, 0x3D, + 0xF4, 0x77, 0x5F, 0x7C, 0x34, 0xBA, 0xFD, 0x50, 0xAF, 0xDD, 0xD5, 0xB7, 0x1F, 0xED, 0x7F, 0xEB, + 0x95, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0x0D, 0xE2, 0x23, 0xDC, 0x99, 0x07, 0x6D, 0x6C, + 0x3C, 0x3E, 0xA3, 0x12, 0x22, 0x41, 0x78, 0x4C, 0x3C, 0x7A, 0x81, 0x7F, 0xEB, 0x41, 0x7C, 0x84, + 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x6C, 0x34, 0x98, 0x77, 0xE2, 0x0E, 0xDE, 0x84, 0x05, 0x00, 0x00, + 0xA6, 0x27, 0xEF, 0xD0, 0xCA, 0xC1, 0xAE, 0x8B, 0x8F, 0x5C, 0x6E, 0x3F, 0x2E, 0x6A, 0xFB, 0xF6, + 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0xD6, 0xD7, 0xEC, 0xBF, 0xF6, + 0x4D, 0x17, 0x1D, 0x33, 0xFC, 0x9D, 0xB1, 0xE1, 0xE1, 0xE6, 0x7F, 0xCB, 0x55, 0x18, 0x1F, 0x4F, + 0x11, 0x1F, 0xC5, 0x4C, 0x87, 0x46, 0xA3, 0x79, 0xC7, 0xBE, 0x70, 0x32, 0xF8, 0x12, 0x04, 0x00, + 0x00, 0x98, 0x5E, 0xBC, 0xBD, 0x57, 0xC4, 0x5E, 0xC1, 0xAB, 0x82, 0x22, 0xCD, 0x44, 0x54, 0x24, + 0x5D, 0x24, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0xA3, 0xFE, 0xD9, 0x0F, 0x75, 0xFB, 0x61, 0xE3, 0xE3, + 0xBE, 0xF6, 0x6E, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x4B, 0x10, 0x1F, 0x6F, 0x7D, + 0xED, 0x02, 0xF3, 0xD6, 0x7F, 0xB9, 0xC0, 0x9C, 0xFA, 0xA1, 0x0D, 0x0D, 0x77, 0xE3, 0xF1, 0x63, + 0x1B, 0x1F, 0x41, 0x78, 0xBC, 0xF3, 0x90, 0xFD, 0x91, 0xF8, 0x28, 0x68, 0x3A, 0x32, 0x9A, 0xEE, + 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x4C, 0x4F, 0x66, 0xAF, 0x3D, 0xF0, 0x75, 0x53, 0x7C, + 0x04, 0x01, 0xD2, 0xEC, 0xF6, 0x23, 0x1E, 0x20, 0x75, 0xF1, 0xF1, 0x60, 0x7B, 0x0F, 0x9E, 0xEB, + 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x96, 0x22, 0x3E, 0xDE, 0xFC, 0xCF, 0xF6, 0xC7, 0xBB, 0x6D, + 0x84, 0x04, 0xD1, 0xE1, 0xEF, 0x41, 0xE2, 0x43, 0x87, 0x43, 0x1E, 0x13, 0x71, 0x91, 0x64, 0x47, + 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xFD, 0x98, 0xD1, 0xFE, 0x59, 0x66, 0x68, 0x81, 0x0D, + 0x80, 0xEE, 0x8A, 0x0F, 0xF7, 0xA3, 0xBA, 0xFD, 0x50, 0xCF, 0x7E, 0xD4, 0x7E, 0xE8, 0xA0, 0x0D, + 0x8F, 0x87, 0xDB, 0xBB, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x2C, 0x65, 0x7C, 0xB8, + 0xFD, 0xE1, 0xFF, 0xB1, 0xFF, 0xFF, 0x77, 0x66, 0x98, 0xB7, 0x1F, 0x98, 0x69, 0x67, 0xE3, 0x63, + 0x23, 0xF1, 0x51, 0xCC, 0x44, 0x58, 0x24, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x2F, 0xAD, 0x0D, 0xBE, + 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0xFF, 0x0D, 0x58, 0x3B, 0x67, 0xDB, 0x08, 0x50, 0x41, 0x91, + 0x66, 0x22, 0x2A, 0x92, 0x2E, 0x1A, 0x1F, 0x41, 0x80, 0x34, 0xBB, 0xFD, 0x70, 0xF1, 0x11, 0x0F, + 0x90, 0x6A, 0x7C, 0xB4, 0x79, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, 0x64, 0x59, 0xC6, 0xF8, + 0x78, 0xE3, 0x2F, 0x67, 0x98, 0xF1, 0xFF, 0x3C, 0xC3, 0xBC, 0xF5, 0x23, 0xFB, 0x7B, 0x7A, 0x92, + 0xF8, 0x28, 0x66, 0x22, 0x2A, 0xD2, 0xEC, 0xF9, 0x2B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x7E, + 0xBC, 0x43, 0xAB, 0xEE, 0x32, 0xDB, 0x3E, 0x6A, 0x43, 0x40, 0x45, 0x45, 0xD2, 0x89, 0xA8, 0x48, + 0x3A, 0x11, 0x1F, 0xEE, 0xC7, 0xD4, 0xB7, 0x1F, 0x61, 0x7C, 0xB8, 0xDB, 0x8F, 0x7B, 0xB3, 0xDD, + 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x4E, 0x7C, 0xFC, 0xA7, 0x19, 0xE6, 0xF7, + 0xFF, 0xF7, 0x0C, 0xF3, 0xE6, 0x03, 0xC4, 0x47, 0x31, 0x13, 0x41, 0x91, 0x61, 0xDE, 0x2F, 0xEE, + 0x18, 0x0A, 0xBE, 0x14, 0x01, 0x00, 0x00, 0xA6, 0x07, 0x33, 0xD2, 0xBF, 0x5E, 0x47, 0x45, 0xD2, + 0x89, 0xA8, 0x48, 0xBA, 0x78, 0x7C, 0xB8, 0x85, 0xE1, 0x11, 0xC6, 0x47, 0xAB, 0xDB, 0x0F, 0xFF, + 0x5B, 0xAF, 0x82, 0xF8, 0x70, 0xFB, 0xA1, 0x9D, 0x88, 0x8B, 0x56, 0xD3, 0x21, 0x91, 0x65, 0x22, + 0x24, 0xB2, 0x2C, 0x87, 0xF8, 0x78, 0xE3, 0xDB, 0x36, 0x3E, 0x5E, 0x22, 0x3E, 0xF2, 0x9F, 0x8E, + 0x89, 0x2C, 0xF3, 0x8E, 0xDE, 0x3C, 0x16, 0x7C, 0x29, 0x02, 0x00, 0x00, 0x4C, 0x0F, 0xDE, 0x81, + 0xAB, 0x46, 0x75, 0x58, 0x24, 0x99, 0x88, 0x8A, 0xA4, 0x6B, 0x10, 0x1F, 0xEE, 0xC7, 0x54, 0xB7, + 0x1F, 0xD1, 0xF8, 0x70, 0xB7, 0x1F, 0xDF, 0x4A, 0x7F, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, + 0x64, 0x19, 0xF1, 0x91, 0xC3, 0x54, 0x38, 0xE4, 0x31, 0x1D, 0x12, 0x99, 0xC7, 0x9B, 0xB0, 0x00, + 0x00, 0xC0, 0x74, 0xE3, 0xED, 0xFF, 0xEC, 0x49, 0x1D, 0x17, 0xAD, 0x26, 0xA2, 0x22, 0xE9, 0xE2, + 0xE1, 0x11, 0x2E, 0x0C, 0x8F, 0x30, 0x3E, 0x9A, 0xDD, 0x7E, 0x3C, 0x76, 0x91, 0x39, 0xFD, 0xA8, + 0x0D, 0x8E, 0x36, 0x6F, 0x3F, 0x74, 0x48, 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, + 0x0A, 0x87, 0x3C, 0x26, 0x02, 0xA2, 0xDD, 0xB9, 0x37, 0x61, 0x1D, 0xBF, 0x63, 0x49, 0xF0, 0xE5, + 0x08, 0x00, 0x00, 0x50, 0x7E, 0x66, 0x68, 0xBE, 0x8D, 0x02, 0x15, 0x18, 0xCD, 0x26, 0xA2, 0x22, + 0xE9, 0x54, 0x78, 0x44, 0x96, 0xE8, 0xF6, 0x23, 0x88, 0x8F, 0x76, 0x6F, 0x3F, 0x74, 0x48, 0x64, + 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0x26, 0xE2, 0x21, 0x8F, 0xBD, + 0x78, 0x1D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xD3, 0x87, 0xFF, 0x06, 0xAC, 0x5D, 0x9F, 0xB6, 0x61, + 0xA0, 0x22, 0xA3, 0xD1, 0x44, 0x54, 0x24, 0x9D, 0x08, 0x8E, 0xEA, 0x92, 0xDE, 0x7E, 0x3C, 0x6E, + 0x03, 0x24, 0x8C, 0x8F, 0x68, 0x80, 0xFC, 0x80, 0xF8, 0x20, 0x3E, 0x54, 0x38, 0xE4, 0x31, 0x11, + 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x3E, 0xCC, 0xA1, 0xFE, 0xB5, + 0x66, 0xC7, 0xC7, 0x6D, 0x1C, 0xA8, 0xD0, 0x50, 0x13, 0x51, 0x91, 0x74, 0x2A, 0x3A, 0x62, 0x53, + 0xB7, 0x1F, 0xD1, 0x00, 0xF1, 0xE3, 0xA3, 0xD1, 0xED, 0xC7, 0x37, 0x75, 0x68, 0xA8, 0xE9, 0x90, + 0xC8, 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x44, 0x43, 0xCE, + 0xE3, 0x4D, 0x58, 0x00, 0x00, 0x60, 0xDA, 0xF0, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0x36, 0x10, 0x54, + 0x6C, 0xC4, 0x27, 0xA2, 0x22, 0xE9, 0x44, 0x6C, 0xD4, 0x2C, 0xC1, 0xED, 0x87, 0xFB, 0xD6, 0xAB, + 0x89, 0xC7, 0x2E, 0x9C, 0xBC, 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0x7B, 0xC9, 0x6F, 0x3F, 0x74, 0x48, + 0x64, 0x99, 0x08, 0x89, 0x2C, 0x23, 0x3E, 0x72, 0x98, 0x0A, 0x87, 0x3C, 0xA6, 0x83, 0x21, 0xEF, + 0x79, 0x47, 0xFF, 0x64, 0x34, 0xF8, 0x92, 0x04, 0x00, 0x00, 0x28, 0x37, 0x6F, 0x78, 0xE9, 0x90, + 0x8E, 0x8D, 0xF8, 0x44, 0x54, 0x24, 0x9D, 0x0A, 0x8E, 0xF8, 0xE2, 0x01, 0xA2, 0x9E, 0xFD, 0x78, + 0xDC, 0xC6, 0x47, 0x9B, 0xB7, 0x1F, 0x3A, 0x24, 0xB2, 0x4C, 0x84, 0x44, 0x96, 0x11, 0x1F, 0x39, + 0x4C, 0x85, 0x43, 0x1E, 0xD3, 0xB1, 0x50, 0xC8, 0x8E, 0x7D, 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, + 0xE9, 0xC1, 0xDB, 0xBB, 0x24, 0xC1, 0x2B, 0x78, 0x45, 0x54, 0x24, 0x9D, 0x8A, 0x8D, 0xE8, 0x6C, + 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, 0x47, 0xEC, 0x5B, 0xAF, 0x9E, 0xB8, 0x70, 0xF2, + 0xF6, 0xC3, 0x2E, 0xCB, 0xED, 0x87, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, + 0x53, 0xE1, 0x90, 0xC7, 0x44, 0x24, 0x14, 0xB6, 0x45, 0x95, 0x37, 0x61, 0xBD, 0x7C, 0xE7, 0xEC, + 0xE0, 0xCB, 0x12, 0x00, 0x00, 0xA0, 0xBC, 0xCC, 0x1E, 0x7B, 0xF8, 0x91, 0xD1, 0x11, 0x4E, 0x44, + 0x45, 0xD2, 0xA9, 0xE0, 0x88, 0x2F, 0x1A, 0x20, 0x76, 0xD5, 0x6F, 0xBD, 0x8A, 0x06, 0x88, 0x8B, + 0x0F, 0xFF, 0xF6, 0xE3, 0xC2, 0xFA, 0xDB, 0x8F, 0x87, 0xEC, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, + 0x32, 0x11, 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xB3, + 0x5F, 0x7F, 0x6E, 0x2F, 0x7E, 0xCE, 0x05, 0xC8, 0x40, 0xF0, 0x65, 0x09, 0x00, 0x00, 0x50, 0x4E, + 0x66, 0xA4, 0x7F, 0x76, 0xF3, 0x57, 0xF0, 0x8A, 0xA8, 0x48, 0x3A, 0x15, 0x1B, 0xF1, 0xC5, 0xE2, + 0x23, 0xBC, 0xFD, 0xA8, 0x7D, 0xF0, 0xDC, 0xC6, 0x47, 0xF4, 0xF6, 0x23, 0xFE, 0xAD, 0x57, 0xF7, + 0xB7, 0xBE, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, + 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x16, 0xC4, 0x87, 0x1F, 0x20, 0xD7, 0xF0, 0x26, 0x2C, 0x00, 0x00, + 0x50, 0x7E, 0xFE, 0x2B, 0x78, 0x1B, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, 0xA9, 0xD8, 0x88, 0xAF, + 0x41, 0x7C, 0x54, 0x1F, 0x3C, 0x77, 0xB3, 0xF1, 0x51, 0x73, 0xFB, 0x11, 0x7F, 0xF0, 0xDC, 0xDD, + 0x7E, 0xDC, 0xAD, 0xA3, 0x23, 0x9C, 0x0E, 0x89, 0x2C, 0x13, 0x21, 0x91, 0x65, 0xC4, 0x47, 0x0E, + 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0xB5, 0x48, 0x7C, 0x04, 0xF3, 0x4E, 0x7C, 0x69, 0x30, + 0xF8, 0xD2, 0x04, 0x00, 0x00, 0x28, 0x27, 0xFF, 0x0D, 0x58, 0x5B, 0x3F, 0x64, 0xA3, 0x61, 0x8A, + 0xE3, 0xC3, 0x4D, 0x05, 0x48, 0xCD, 0xB7, 0x5E, 0xD9, 0xF8, 0x70, 0xB7, 0x1F, 0x41, 0x7C, 0x64, + 0xB9, 0xFD, 0xD0, 0x21, 0x91, 0x65, 0x22, 0x24, 0xB2, 0x8C, 0xF8, 0xC8, 0x61, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x56, 0x1F, 0x1F, 0x6E, 0xDE, 0xF1, 0x3F, 0xE5, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0xDC, 0xBC, 0x91, 0x81, 0x0D, 0x1D, 0x89, 0x0F, 0xB7, 0x78, 0x7C, 0x54, 0x6F, 0x3F, 0x2E, + 0xAC, 0xC6, 0xC7, 0xE4, 0xED, 0x47, 0x2C, 0x3E, 0x12, 0xDC, 0x7E, 0xE8, 0x90, 0xC8, 0x32, 0x11, + 0x12, 0x59, 0x46, 0x7C, 0xE4, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xF1, 0xE1, + 0xEF, 0xD8, 0x2D, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x94, 0x9B, 0x77, 0xE0, 0xAA, 0xD8, 0x1B, 0xB0, + 0x44, 0x54, 0x24, 0x9D, 0x8A, 0x8C, 0x46, 0xB3, 0xD1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0x44, 0x02, + 0x24, 0x8C, 0x0F, 0xF9, 0xB9, 0x1F, 0xDF, 0x69, 0x7C, 0xFB, 0xA1, 0x43, 0x22, 0xCB, 0x44, 0x48, + 0x64, 0x59, 0x34, 0x3A, 0x88, 0x8F, 0x8C, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x11, + 0x1D, 0xD1, 0xB9, 0x37, 0x61, 0x8D, 0x7D, 0x71, 0x56, 0xF0, 0xE5, 0x09, 0x00, 0x00, 0x50, 0x3E, + 0xDE, 0xBE, 0x25, 0xE3, 0x53, 0x1E, 0x1F, 0x6E, 0xF1, 0xF8, 0x48, 0x7B, 0xFB, 0xF1, 0xA0, 0x9D, + 0x08, 0x0F, 0x37, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x52, 0xC4, 0x47, 0x18, 0x20, 0x2E, + 0x3E, 0xFE, 0x40, 0x7C, 0x44, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x38, 0xE2, + 0x1B, 0x5B, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB9, 0x4D, 0xBE, 0x01, 0x4B, 0x44, 0x45, 0xD2, + 0xA9, 0xC0, 0x68, 0x36, 0x1B, 0x1D, 0xEE, 0x47, 0x75, 0xFB, 0x11, 0x0D, 0x90, 0xA6, 0xB7, 0x1F, + 0xF7, 0xE9, 0xDB, 0x0F, 0x1D, 0x12, 0x59, 0x26, 0x42, 0x22, 0xCB, 0x22, 0xF1, 0x11, 0xAE, 0x3E, + 0x3E, 0xEC, 0x5C, 0x78, 0x84, 0xB7, 0x1F, 0xC4, 0x47, 0x6C, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, + 0xA2, 0x26, 0x62, 0x43, 0xED, 0xC5, 0x6B, 0x8C, 0xF7, 0x8B, 0x2F, 0xDD, 0x15, 0x7C, 0x79, 0x02, + 0x00, 0x00, 0x94, 0x8B, 0xFF, 0x06, 0xAC, 0x9D, 0x9F, 0xD2, 0x51, 0x91, 0x74, 0xF1, 0xB8, 0x48, + 0xB2, 0x30, 0x3C, 0xC2, 0xF8, 0xF0, 0x03, 0x64, 0x66, 0x35, 0x3E, 0x6A, 0xDF, 0x7C, 0x95, 0xFC, + 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0xB2, 0x16, 0xF1, 0xF1, 0x76, 0x24, 0x3E, 0xE2, + 0x37, 0x1F, 0x6F, 0x04, 0xF1, 0xF1, 0x07, 0xE2, 0xA3, 0xA0, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x34, + 0x9A, 0xCC, 0x3B, 0x71, 0xC7, 0x86, 0xE0, 0x4B, 0x14, 0x00, 0x00, 0xA0, 0x5C, 0x6C, 0x80, 0xAC, + 0x33, 0xDB, 0x2F, 0xD6, 0x61, 0x91, 0x64, 0x2A, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0xF6, + 0xF6, 0xC3, 0xC6, 0x47, 0x83, 0x67, 0x3F, 0xEA, 0x3E, 0x74, 0xD0, 0xBD, 0xF9, 0x4A, 0xDC, 0x7E, + 0xE8, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0xD6, 0x22, 0x3E, 0xA2, 0x37, 0x1F, 0x61, 0x7C, 0x84, + 0x37, 0x1F, 0xC4, 0x47, 0x38, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x91, 0xD1, 0x6C, + 0xDE, 0x89, 0x3F, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x9C, 0xFC, 0x57, 0xF0, 0xAA, 0xB0, 0x48, + 0xB2, 0x78, 0x58, 0x24, 0x5D, 0x18, 0x1E, 0x61, 0x7C, 0x3C, 0x3D, 0xB3, 0xE6, 0xF6, 0xA3, 0xEE, + 0xD9, 0x0F, 0x1B, 0x20, 0xA7, 0xA2, 0x01, 0xF2, 0x23, 0xBB, 0x1E, 0x8C, 0x0F, 0xB7, 0xBA, 0x00, + 0x69, 0x10, 0x1F, 0xE3, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0xC0, + 0x68, 0x35, 0xEF, 0xD8, 0x17, 0xC6, 0x83, 0x2F, 0x51, 0x00, 0x00, 0x80, 0x72, 0xF1, 0x0E, 0x5C, + 0x33, 0x2A, 0xE3, 0xA2, 0xD5, 0x54, 0x58, 0x24, 0x99, 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, + 0xF1, 0x11, 0xBD, 0xFD, 0x88, 0xC5, 0x47, 0xDD, 0xED, 0xC7, 0xB7, 0x6A, 0x6F, 0x3F, 0x74, 0x48, + 0x64, 0x99, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x56, 0x17, 0x1F, 0xEA, 0x8D, 0x57, 0xC4, 0x47, + 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x71, 0x91, 0x68, 0x47, 0x56, 0xF3, 0x26, + 0x2C, 0x00, 0x00, 0x50, 0x4E, 0xDE, 0xDE, 0x25, 0x63, 0x32, 0x30, 0x9A, 0x2D, 0x1E, 0x15, 0x69, + 0x16, 0x86, 0x47, 0x18, 0x1F, 0x69, 0x6F, 0x3F, 0x7E, 0x68, 0x57, 0xB6, 0xF8, 0x70, 0x37, 0x1F, + 0x2E, 0x3E, 0xDC, 0x43, 0xE7, 0xC4, 0x87, 0x9D, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, + 0xA8, 0x48, 0xB3, 0x97, 0x56, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x4E, 0x66, 0xCF, 0x42, 0x1D, + 0x19, 0x8D, 0xA6, 0xA2, 0x22, 0xE5, 0x2A, 0x01, 0x32, 0xB3, 0xED, 0xDB, 0x0F, 0x1D, 0x12, 0x59, + 0x26, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0xB5, 0x8C, 0x8F, 0xBF, 0x9C, 0xFC, 0xAC, 0x8F, 0x3F, + 0xDC, 0x4B, 0x7C, 0x14, 0x33, 0x15, 0x09, 0x45, 0x4D, 0x04, 0x45, 0xDA, 0xBD, 0x78, 0x8D, 0x0D, + 0x90, 0x3B, 0xD6, 0x05, 0x5F, 0xA6, 0x00, 0x00, 0x00, 0xE5, 0x60, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, + 0xE6, 0xE8, 0xD0, 0x50, 0x13, 0x31, 0x91, 0x6A, 0xD5, 0xDB, 0x8F, 0x20, 0x3E, 0x9A, 0xDD, 0x7E, + 0x84, 0xF1, 0xD1, 0xE0, 0xF6, 0x43, 0x87, 0x44, 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, + 0x0D, 0xE3, 0x23, 0x08, 0x90, 0xE8, 0x07, 0x0D, 0x12, 0x1F, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, + 0xA2, 0x26, 0x62, 0x22, 0xCB, 0x9E, 0xB7, 0x7F, 0x3F, 0x4E, 0xDC, 0xB1, 0x3E, 0xF8, 0x52, 0x05, + 0x00, 0x00, 0x28, 0x07, 0xFF, 0x15, 0xBC, 0x49, 0xDF, 0x80, 0xA5, 0x82, 0x22, 0xC3, 0xFC, 0xF8, + 0x88, 0xDD, 0x7E, 0x54, 0x03, 0xA4, 0xD5, 0xED, 0xC7, 0x3D, 0xBD, 0x1B, 0x1F, 0x95, 0xD7, 0xED, + 0x12, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x24, 0xDA, 0x18, 0x6F, + 0xC2, 0x02, 0x00, 0x00, 0xA5, 0xE3, 0xBF, 0x01, 0x6B, 0xEB, 0x07, 0x75, 0x70, 0x44, 0x27, 0x42, + 0x22, 0xF5, 0xC2, 0x9B, 0x8F, 0x26, 0xB7, 0x1F, 0x13, 0x2E, 0x40, 0x1E, 0x8B, 0xDC, 0x7E, 0x84, + 0xF1, 0xE1, 0xF6, 0xBD, 0xCA, 0xB7, 0x5E, 0xE9, 0x90, 0xC8, 0x32, 0x11, 0x12, 0x59, 0x26, 0xC2, + 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x11, 0xD1, 0xCE, + 0xBC, 0x63, 0x5F, 0x38, 0x19, 0x7C, 0xA9, 0x02, 0x00, 0x00, 0x94, 0x83, 0x37, 0xBC, 0x62, 0x50, + 0x06, 0x47, 0x74, 0x2A, 0x26, 0xB2, 0x2C, 0x8C, 0x8F, 0xD8, 0xED, 0x47, 0xE2, 0x67, 0x3F, 0xBE, + 0xC9, 0xB7, 0x5D, 0x25, 0x9B, 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, + 0x2A, 0x12, 0x8A, 0x9A, 0x0E, 0x88, 0xB6, 0x77, 0x64, 0xB5, 0x09, 0xBE, 0x54, 0x01, 0x00, 0x00, + 0xCA, 0xC1, 0xDB, 0x7B, 0x79, 0xF3, 0x57, 0xF0, 0xAA, 0x90, 0x48, 0xBB, 0x4D, 0x6E, 0x93, 0xE1, + 0x31, 0x79, 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xFC, 0xF6, 0x43, 0x87, 0x44, + 0x96, 0x89, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0x0D, 0xE3, 0xC3, 0x3D, 0x70, 0x1E, 0x8B, 0x8F, + 0xF1, 0xFF, 0x32, 0xCB, 0x9C, 0xFF, 0xD7, 0x5B, 0x83, 0xF8, 0x58, 0x65, 0x0F, 0xFA, 0x2A, 0x28, + 0xD2, 0x4C, 0x84, 0x43, 0x1E, 0x93, 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x84, 0x43, 0x5E, 0x3B, 0xB2, 0x92, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF1, 0xF6, 0x2C, + 0x1A, 0x97, 0xE1, 0xE1, 0xA6, 0x62, 0x22, 0xCB, 0x6C, 0x78, 0x44, 0x6F, 0x3F, 0xAA, 0xDF, 0x7A, + 0x15, 0x06, 0x48, 0xCD, 0x6B, 0x77, 0x2F, 0x94, 0xB7, 0x1F, 0xEF, 0x7C, 0x23, 0xAF, 0xDB, 0x0F, + 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x2D, 0x6D, 0x7C, 0x9C, 0xFB, 0xE9, 0x17, 0x2A, 0xF1, 0x71, + 0xD4, 0xFE, 0x28, 0x83, 0x22, 0xCD, 0x44, 0x38, 0xE4, 0x31, 0x19, 0x0E, 0x79, 0x4C, 0x85, 0x43, + 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x44, 0x34, 0xE4, 0x39, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xCA, 0xC4, + 0x0C, 0xF5, 0xCF, 0x32, 0x43, 0x7D, 0x53, 0x1A, 0x1F, 0xE1, 0xED, 0x47, 0xCD, 0x83, 0xE7, 0xCD, + 0x9E, 0xFD, 0x70, 0xF1, 0x71, 0x7F, 0x5E, 0xB7, 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, + 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x82, 0x21, 0xEF, 0xB9, 0xFF, + 0x6D, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0x0B, 0xFF, 0x0D, 0x58, 0x3B, 0x3E, 0x39, 0xE5, 0xF1, + 0x51, 0x7D, 0xF0, 0x5C, 0xDD, 0x7E, 0xC4, 0x1E, 0x3C, 0x7F, 0xE7, 0x41, 0x1B, 0x1F, 0x5F, 0x57, + 0x31, 0x91, 0x76, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x62, 0xA1, 0xA0, 0x79, 0x27, 0xEE, 0xE0, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0x1C, 0xBC, 0xE1, 0xFE, 0xBB, 0xCC, 0xB6, 0x8F, 0x14, 0x13, 0x1F, 0xCF, 0x54, 0xE2, 0xA3, + 0x2E, 0x40, 0xE2, 0xAF, 0xDD, 0x75, 0xB7, 0x1F, 0x41, 0x7C, 0xC4, 0x6F, 0x3F, 0xDE, 0x79, 0xD8, + 0xC6, 0xC7, 0x77, 0xF3, 0xF8, 0xD6, 0x2B, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, + 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0xD3, 0xA1, 0x50, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, + 0x82, 0x2F, 0x59, 0x00, 0x00, 0x80, 0xDE, 0xE6, 0xBF, 0x82, 0xB7, 0xA8, 0xF8, 0x08, 0x02, 0xA4, + 0xE1, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0x8A, 0xC7, 0x47, 0x2E, 0xB7, 0x1F, 0x22, 0x24, + 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0xA6, + 0x23, 0xA1, 0xD0, 0x1D, 0xB9, 0x91, 0x37, 0x61, 0x01, 0x00, 0x80, 0x72, 0xF0, 0x0E, 0x5C, 0x33, + 0xF9, 0x06, 0x2C, 0x19, 0x12, 0x59, 0x36, 0x19, 0x1F, 0x8D, 0x6E, 0x3F, 0xAA, 0xAF, 0xDD, 0xAD, + 0xDE, 0x7E, 0x5C, 0x14, 0xBB, 0xFD, 0xB0, 0xE1, 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, + 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x38, 0x98, + 0x8A, 0xBD, 0x34, 0x60, 0xCC, 0xF1, 0x3B, 0x96, 0x04, 0x5F, 0xB6, 0x00, 0x00, 0x00, 0xBD, 0xCB, + 0xDB, 0x77, 0xC5, 0xC9, 0x42, 0xE2, 0xA3, 0xAD, 0xDB, 0x8F, 0x20, 0x3E, 0x1E, 0x68, 0xF7, 0xF6, + 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, + 0x50, 0xD4, 0x44, 0x18, 0x4C, 0xD5, 0xDC, 0x9B, 0xB0, 0x4E, 0x7C, 0x69, 0x6D, 0xF0, 0x65, 0x0B, + 0x00, 0x00, 0xD0, 0xBB, 0xCC, 0xEE, 0xBE, 0x48, 0x3C, 0xB4, 0xBB, 0xDA, 0xF8, 0x70, 0x3F, 0xAA, + 0xDB, 0x8F, 0xFA, 0x67, 0x3F, 0xA2, 0xB7, 0x1F, 0x41, 0x7C, 0xB8, 0xDD, 0xDB, 0xCE, 0xED, 0x87, + 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, + 0xA8, 0x89, 0x28, 0x98, 0xCA, 0xBD, 0x70, 0x25, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xCF, 0x1C, + 0xE8, 0x5F, 0x62, 0x76, 0xCE, 0x0E, 0xE2, 0xA1, 0xDD, 0x45, 0xE2, 0x23, 0x08, 0x90, 0x66, 0xB7, + 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xE1, 0x6E, 0x3F, 0x64, 0x58, 0x24, 0x99, 0x08, 0x89, 0x2C, + 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x20, + 0xE8, 0xC0, 0xBC, 0xE3, 0xB7, 0x0F, 0x05, 0x5F, 0xBA, 0x00, 0x00, 0x00, 0xBD, 0xC9, 0x1C, 0x5C, + 0xB6, 0xD6, 0x6C, 0xFF, 0x58, 0x10, 0x10, 0xED, 0xAC, 0x3E, 0x3E, 0xDC, 0x8F, 0xEA, 0xF6, 0x43, + 0x3D, 0xFB, 0x51, 0xF9, 0xD0, 0xC1, 0x0B, 0x27, 0x03, 0xE4, 0x21, 0xBB, 0xCC, 0xB7, 0x1F, 0x22, + 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0xA6, 0x63, 0xA0, 0x13, 0xF3, 0x8E, 0xDE, 0xC2, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x33, 0x23, + 0xCB, 0xD7, 0x9B, 0xCD, 0xEF, 0xB3, 0xB1, 0xA0, 0xA2, 0x22, 0xE9, 0x62, 0xF1, 0xE1, 0x16, 0x86, + 0x47, 0x18, 0x1F, 0xB1, 0xDB, 0x8F, 0xBA, 0x67, 0x3F, 0x1E, 0xB5, 0xF1, 0xE1, 0x02, 0xA4, 0xED, + 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, + 0x45, 0x42, 0x51, 0xD3, 0x21, 0xD0, 0xB1, 0x1D, 0xBD, 0x99, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, + 0xE6, 0x1D, 0xB8, 0x7E, 0xA8, 0x36, 0x26, 0xD2, 0x4E, 0xC7, 0x87, 0xFB, 0xB1, 0xE9, 0xED, 0x47, + 0x2C, 0x3E, 0xEA, 0x6E, 0x3F, 0xEE, 0xC9, 0x72, 0xFB, 0x21, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, + 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0x22, 0x00, 0x3A, 0x3D, 0xF7, + 0x26, 0xAC, 0x97, 0xEF, 0x9C, 0x1D, 0x7C, 0xF9, 0x02, 0x00, 0x00, 0xF4, 0x1E, 0x6F, 0xEF, 0x65, + 0xA3, 0xF5, 0x51, 0x91, 0x74, 0xB1, 0xF0, 0x08, 0x17, 0x86, 0x47, 0x18, 0x1F, 0x4D, 0x6F, 0x3F, + 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x4E, 0x06, 0x46, 0xB3, 0x89, 0x90, 0xC8, + 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xFC, 0x77, 0xC3, 0x5E, 0xFC, 0x9C, 0x31, 0xC7, 0x6E, 0x1B, 0x08, 0xBE, 0x7C, 0x01, 0x00, 0x00, + 0x7A, 0x8F, 0xD9, 0x63, 0x0F, 0x5B, 0x32, 0x2E, 0x5A, 0x4D, 0x84, 0x47, 0x64, 0xC9, 0x6E, 0x3F, + 0x2A, 0xF1, 0xD1, 0xFE, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, + 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xFE, 0xDD, 0x32, 0xDE, 0x84, 0x05, 0x00, + 0x00, 0x7A, 0x99, 0x19, 0xE9, 0x9F, 0x6D, 0x76, 0xCF, 0xB3, 0xC1, 0xA0, 0x02, 0xA3, 0xD9, 0x74, + 0x74, 0xF8, 0x4B, 0x7C, 0xFB, 0x31, 0xD3, 0x9C, 0x09, 0xE3, 0xA3, 0xAD, 0xDB, 0x0F, 0x11, 0x12, + 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xFE, 0x2E, 0x9B, 0x77, 0xE2, 0xF6, 0xC1, 0xE0, 0x4B, 0x18, 0x00, 0x00, 0xA0, 0xB7, 0xD8, + 0x00, 0x19, 0x48, 0xFF, 0x06, 0x2C, 0x11, 0x1D, 0xB1, 0xA9, 0xDB, 0x8F, 0x6A, 0x80, 0x84, 0xF1, + 0x91, 0xCB, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, + 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7F, 0xB7, 0xCD, 0x3B, 0xFA, 0x85, 0xD1, 0xE0, 0x4B, + 0x18, 0x00, 0x00, 0xA0, 0xB7, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0x3E, 0x60, 0xA3, 0x41, 0x85, + 0x86, 0x9A, 0x0E, 0x8E, 0xEA, 0x12, 0xDC, 0x7E, 0x4C, 0x3C, 0x31, 0xD3, 0x4C, 0x3C, 0x16, 0xB9, + 0xFD, 0x08, 0xE3, 0xC3, 0xED, 0xFB, 0xC4, 0x47, 0x65, 0x22, 0x1C, 0xF2, 0x98, 0x0C, 0x87, 0x3C, + 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0xB0, 0xDF, 0x95, 0x3B, 0x76, 0x0B, 0x6F, + 0xC2, 0x02, 0x00, 0x00, 0xBD, 0xC9, 0x1B, 0x59, 0xB1, 0xA1, 0x3E, 0x32, 0x1A, 0x4D, 0x04, 0x47, + 0x7C, 0xF1, 0x00, 0xA9, 0x7B, 0xF6, 0xC3, 0xC6, 0x47, 0xB3, 0xDB, 0x8F, 0x6F, 0xA8, 0xD0, 0x50, + 0x13, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, + 0x14, 0x35, 0x71, 0xC8, 0xEF, 0xE6, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xAB, 0xBC, 0x7D, 0x57, + 0x24, 0x7C, 0x03, 0x96, 0x88, 0x8D, 0xE8, 0x6C, 0x70, 0x54, 0xE3, 0xC3, 0x86, 0xC7, 0xE4, 0xED, + 0x47, 0xE4, 0x5B, 0xAF, 0x06, 0x6D, 0x7C, 0x44, 0x6F, 0x3F, 0xEC, 0x6A, 0x6E, 0x3F, 0x36, 0x24, + 0xBD, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, + 0x54, 0x24, 0x14, 0x35, 0x71, 0xC0, 0xEF, 0xF6, 0x8D, 0x2D, 0xE5, 0x4D, 0x58, 0x00, 0x00, 0xA0, + 0x37, 0x79, 0x7B, 0x2F, 0x1B, 0xAF, 0x8F, 0x8D, 0xF8, 0x62, 0xB1, 0xA1, 0x16, 0x0D, 0x10, 0xBB, + 0xEA, 0xB7, 0x5E, 0x85, 0x01, 0x62, 0xE3, 0x63, 0xF2, 0xF6, 0x63, 0x66, 0x1B, 0xB7, 0x1F, 0x22, + 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0x26, 0x0E, 0xF7, 0xBD, 0xB0, 0x17, 0xAF, 0x31, 0xDE, 0x2F, 0x6E, 0xBF, 0x2B, 0xF8, 0x32, 0x06, + 0x00, 0x00, 0xE8, 0x1D, 0xAD, 0xDF, 0x80, 0x15, 0x0B, 0x0D, 0xB5, 0x58, 0x7C, 0x84, 0xB7, 0x1F, + 0x93, 0x0F, 0x9E, 0xDB, 0xF8, 0x88, 0xDF, 0x7E, 0x84, 0xF1, 0x11, 0x06, 0x48, 0xA2, 0xDB, 0x0F, + 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, + 0x51, 0x13, 0x07, 0xFB, 0x1E, 0x9A, 0x77, 0xFC, 0x8E, 0x0D, 0xC1, 0x97, 0x31, 0x00, 0x00, 0x40, + 0x6F, 0xF0, 0xDF, 0x80, 0xB5, 0x73, 0xB6, 0x8D, 0x08, 0x15, 0x1E, 0x6E, 0xB1, 0xD0, 0x50, 0x6B, + 0x10, 0x1F, 0xD5, 0x07, 0xCF, 0x37, 0xCE, 0xAC, 0xBF, 0xFD, 0x08, 0xE2, 0xA3, 0x7A, 0xFB, 0xF1, + 0xA0, 0xDD, 0xD7, 0x55, 0x70, 0x44, 0x27, 0x42, 0x22, 0xCB, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, + 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xFA, 0x50, 0xDF, 0x4B, 0xF3, 0x8E, 0xDE, 0xCA, + 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x6F, 0x31, 0x23, 0xCB, 0xD7, 0x35, 0x7E, 0x05, 0x6F, 0x2C, 0x34, + 0x1A, 0x4D, 0x05, 0x48, 0xF5, 0x5B, 0xAF, 0x6C, 0x7C, 0x84, 0xB7, 0x1F, 0x41, 0x7C, 0xC8, 0x37, + 0x5F, 0x7D, 0xB7, 0xD5, 0xED, 0x87, 0x08, 0x89, 0x2C, 0x13, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, + 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0xE9, 0x03, 0x7D, 0xAF, 0xCD, 0x3B, 0xFA, 0xF9, 0xF1, + 0xE0, 0x4B, 0x19, 0x00, 0x00, 0xA0, 0x37, 0xD8, 0x00, 0x59, 0x5F, 0x1F, 0x1E, 0x29, 0xE2, 0xC3, + 0x2D, 0x1E, 0x1F, 0xD5, 0xDB, 0x8F, 0xC9, 0xF8, 0xA8, 0x79, 0xF3, 0x55, 0xEA, 0xDB, 0x0F, 0x11, + 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, + 0xD3, 0x87, 0xF9, 0x9E, 0x9C, 0x7B, 0x13, 0xD6, 0xD8, 0x17, 0x67, 0x05, 0x5F, 0xCE, 0x00, 0x00, + 0x00, 0xDD, 0xCF, 0x3B, 0x70, 0xD5, 0x50, 0xBB, 0xF1, 0xA1, 0x6F, 0x3F, 0x6C, 0x7C, 0xD4, 0xDD, + 0x7E, 0x5C, 0x98, 0xE1, 0xF6, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, + 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBE, 0x97, 0x37, 0xB6, 0x9C, 0x37, 0x61, + 0x01, 0x00, 0x80, 0xDE, 0xE2, 0xED, 0xBD, 0x7C, 0x2C, 0x73, 0x7C, 0xB8, 0xC5, 0xE3, 0xC3, 0x0F, + 0x90, 0x19, 0x39, 0xDD, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xC0, 0xF7, 0xFA, 0x78, 0x13, 0x16, 0x00, 0x00, + 0xE8, 0x35, 0x66, 0x68, 0x81, 0x0D, 0x89, 0xEC, 0xF1, 0xE1, 0x7E, 0xAC, 0xBD, 0xFD, 0xB0, 0xF1, + 0x11, 0xFB, 0xF6, 0xAB, 0xA6, 0xB7, 0x1F, 0xF7, 0x35, 0xBA, 0xFD, 0x10, 0x21, 0x91, 0x65, 0x22, + 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x78, 0x2F, + 0xC3, 0x9E, 0xBF, 0x9C, 0x37, 0x61, 0x01, 0x00, 0x80, 0xDE, 0x61, 0x0E, 0xF4, 0x2F, 0x31, 0xBB, + 0x2E, 0xB5, 0x11, 0x91, 0x21, 0x3E, 0xDC, 0xC2, 0xF0, 0x08, 0xE3, 0xE3, 0xE9, 0x19, 0x35, 0xB7, + 0x1F, 0xB5, 0x6F, 0xBE, 0x12, 0xF1, 0xF1, 0x80, 0x1D, 0xF1, 0x91, 0xCF, 0x64, 0x38, 0xE4, 0x31, + 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x07, 0xF7, 0x12, 0xCD, 0x3B, 0xFE, 0x67, 0xBC, + 0x09, 0x0B, 0x00, 0x00, 0xF4, 0x06, 0xFF, 0x15, 0xBC, 0xFE, 0x1B, 0xB0, 0x44, 0x5C, 0xB4, 0x9A, + 0x8D, 0x0E, 0xF7, 0xE3, 0x64, 0x80, 0x04, 0xF1, 0xD1, 0xE0, 0xD9, 0x0F, 0xF9, 0xA1, 0x83, 0xF7, + 0xAA, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, + 0x4C, 0x45, 0x42, 0x51, 0xD3, 0x87, 0xF6, 0x32, 0xCD, 0x3B, 0xFA, 0xF9, 0x93, 0xC1, 0x97, 0x34, + 0x00, 0x00, 0x40, 0x77, 0xF3, 0xDF, 0x80, 0xB5, 0xE5, 0xFD, 0xF5, 0x71, 0x91, 0x64, 0x61, 0x78, + 0x84, 0xF1, 0x11, 0xBB, 0xFD, 0xA8, 0x7B, 0xF6, 0xC3, 0x06, 0xC8, 0x29, 0x17, 0x20, 0x4D, 0x6F, + 0x3F, 0x44, 0x48, 0x64, 0x99, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1F, 0xD8, 0x4B, 0xB7, 0x23, 0xAB, 0x4C, 0xF0, 0x25, 0x0D, 0x00, 0x00, 0xD0, + 0xDD, 0xBC, 0xE1, 0x65, 0x83, 0x32, 0x2E, 0x5A, 0xCD, 0x46, 0x87, 0xFB, 0xB1, 0x12, 0x20, 0x33, + 0xF4, 0xED, 0x47, 0x2C, 0x3E, 0x5A, 0xDF, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, + 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA8, 0x97, 0x75, 0x63, 0xCB, + 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0D, 0xDE, 0xDE, 0xCB, 0x46, 0xEB, 0xE2, 0x22, 0xC9, 0xAA, + 0xB7, 0x1F, 0x41, 0x7C, 0x24, 0xBD, 0xFD, 0x08, 0x03, 0xE4, 0x07, 0x76, 0xC4, 0x47, 0x7B, 0x93, + 0xE1, 0x90, 0xC7, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0xBA, 0x7F, + 0x5D, 0x68, 0xCC, 0x0B, 0x57, 0x1B, 0x73, 0xFC, 0x8E, 0x75, 0xC1, 0x97, 0x35, 0x00, 0x00, 0x40, + 0xF7, 0xF2, 0xF6, 0x2C, 0x18, 0x97, 0x81, 0xD1, 0x6C, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0x59, 0x6F, + 0x3F, 0xEE, 0x89, 0xDE, 0x7E, 0x88, 0x90, 0xC8, 0x32, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2E, 0x3E, 0xDC, 0x7E, 0x6E, + 0xFF, 0x99, 0x38, 0x71, 0xDB, 0xFA, 0xE0, 0xCB, 0x1A, 0x00, 0x00, 0xA0, 0x3B, 0x99, 0xA1, 0xFE, + 0x59, 0x66, 0xF7, 0xDC, 0xFA, 0xC0, 0x68, 0xB5, 0xF0, 0xE6, 0x23, 0x97, 0xDB, 0x0F, 0x11, 0x12, + 0x59, 0x26, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x2E, 0x8C, 0x8F, 0x60, 0xDE, 0x89, 0xDB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, + 0x6E, 0x66, 0x64, 0xE9, 0x80, 0xD9, 0xF1, 0x71, 0x1D, 0x19, 0xCD, 0x16, 0xC6, 0x47, 0xDB, 0xB7, + 0x1F, 0x22, 0x24, 0xB2, 0x4C, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, + 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x5D, 0x2C, 0x3E, 0xFC, 0x00, 0x39, 0x7A, 0x33, 0x6F, 0xC2, + 0x02, 0x00, 0x00, 0xDD, 0xCD, 0x1B, 0x5E, 0x7E, 0x97, 0xD9, 0xFA, 0x21, 0x1D, 0x19, 0x8D, 0x16, + 0x8F, 0x8F, 0x66, 0xB7, 0x1F, 0x61, 0x7C, 0xC8, 0xDB, 0x0F, 0x11, 0x12, 0x59, 0x26, 0xC2, 0xC3, + 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, + 0xC4, 0x87, 0xBF, 0x23, 0x37, 0xF0, 0x26, 0x2C, 0x00, 0x00, 0xD0, 0xDD, 0xFC, 0x57, 0xF0, 0xAA, + 0xC8, 0x68, 0x34, 0x1B, 0x1D, 0x8D, 0x6E, 0x3F, 0xAA, 0x01, 0xD2, 0xEA, 0xF6, 0xE3, 0x1B, 0xC4, + 0x47, 0xA6, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, + 0x65, 0x9D, 0x0A, 0x8F, 0x70, 0x2F, 0xF5, 0xBB, 0x07, 0xD1, 0x97, 0x04, 0x5F, 0xDE, 0x00, 0x00, + 0x00, 0xDD, 0xC7, 0x3B, 0x70, 0x75, 0xF2, 0x37, 0x60, 0x45, 0xE3, 0xC3, 0x86, 0x47, 0xA3, 0xDB, + 0x8F, 0x09, 0x17, 0x20, 0x8F, 0x45, 0x6E, 0x3F, 0xC2, 0xF8, 0x70, 0xDB, 0xE0, 0xBE, 0xF5, 0x4A, + 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xD1, 0x11, 0x9D, 0x7B, 0x13, 0xD6, 0x89, 0x3F, 0x5F, 0x1B, + 0x7C, 0x79, 0x03, 0x00, 0x00, 0x74, 0x1F, 0x6F, 0xDF, 0xE5, 0x27, 0x65, 0x6C, 0xC4, 0x17, 0xC4, + 0x47, 0xA3, 0xDB, 0x8F, 0xC4, 0xCF, 0x7E, 0x7C, 0x43, 0x84, 0x44, 0x96, 0x89, 0xF0, 0x70, 0x23, + 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xC1, + 0x11, 0x1F, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0xDD, 0x2E, 0xD1, 0x1B, 0xB0, 0xE2, 0xF1, 0x51, 0x73, + 0xFB, 0x11, 0xF9, 0xD6, 0xAB, 0xF8, 0xED, 0x87, 0x5D, 0xED, 0xED, 0x47, 0x4E, 0xDF, 0x7A, 0x25, + 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, + 0xB2, 0x4E, 0xC5, 0x46, 0x83, 0x79, 0xC7, 0x6F, 0x1F, 0x0A, 0xBE, 0xBC, 0x01, 0x00, 0x00, 0xBA, + 0x8B, 0x39, 0x70, 0xFD, 0x12, 0xB3, 0xF3, 0x53, 0x3A, 0x3A, 0xC2, 0x45, 0xE2, 0xA3, 0x1A, 0x20, + 0x76, 0xD5, 0x6F, 0xBD, 0x0A, 0x03, 0xA4, 0xE6, 0xB5, 0xBB, 0x33, 0xC5, 0xED, 0xC7, 0xCC, 0x7C, + 0x6E, 0x3F, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, + 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0xC8, 0x68, 0x36, 0xEF, 0xC8, 0x9A, 0xB1, 0xE0, 0x4B, 0x1C, 0x00, + 0x00, 0xA0, 0xBB, 0x98, 0x83, 0xCB, 0xD6, 0x9A, 0xED, 0x1F, 0xD5, 0xE1, 0xE1, 0x16, 0x09, 0x8F, + 0x68, 0x7C, 0x84, 0xB7, 0x1F, 0x35, 0x0F, 0x9E, 0x37, 0x7A, 0xF6, 0xC3, 0x0F, 0x10, 0x1B, 0x1F, + 0xDF, 0xCD, 0xE1, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, + 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x08, 0x8C, 0x96, 0x3B, 0xBA, 0x86, 0x37, 0x61, 0x01, + 0x00, 0x80, 0xEE, 0xE4, 0xBF, 0x01, 0x6B, 0xF3, 0xAC, 0x4C, 0xF1, 0x51, 0x7D, 0xF0, 0x5C, 0xDD, + 0x7E, 0x04, 0xF1, 0x51, 0xB9, 0xFD, 0xB0, 0xF1, 0xF1, 0xA0, 0xDD, 0xD7, 0x45, 0x50, 0xA4, 0x99, + 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, + 0xCB, 0x3A, 0x15, 0x17, 0x49, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB7, 0xF2, 0x0E, 0x7C, 0x6E, + 0x28, 0x49, 0x7C, 0xD4, 0x05, 0x48, 0xFC, 0xB5, 0xBB, 0xEE, 0xF6, 0x23, 0x88, 0x8F, 0xDA, 0x37, + 0x5F, 0xD9, 0xF0, 0xC8, 0xE3, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x0A, 0x8B, 0xA4, 0x7B, 0xE1, 0x5A, 0x63, + 0x8E, 0xDD, 0x36, 0x10, 0x7C, 0x99, 0x03, 0x00, 0x00, 0x74, 0x0F, 0x6F, 0xEF, 0x65, 0xF5, 0xAF, + 0xE0, 0x8D, 0x44, 0x87, 0x8C, 0x8F, 0xE8, 0xED, 0x47, 0x10, 0x1F, 0x35, 0x6F, 0xBE, 0xAA, 0xDE, + 0x7E, 0x04, 0xF1, 0xD1, 0xEE, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, + 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x15, 0x69, 0xF6, 0xFC, 0x67, 0x79, + 0x13, 0x16, 0x00, 0x00, 0xE8, 0x4E, 0x66, 0x8F, 0x3D, 0xEC, 0xB4, 0x88, 0x0F, 0x37, 0x75, 0xFB, + 0x51, 0x7D, 0xED, 0x6E, 0xF5, 0xF6, 0xE3, 0xC2, 0xC8, 0xED, 0x47, 0x10, 0x1F, 0xED, 0xDE, 0x7E, + 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0x62, 0x07, + 0xF4, 0x32, 0x4F, 0x05, 0x45, 0x86, 0x79, 0x27, 0x6E, 0x1B, 0x0C, 0xBE, 0xCC, 0x01, 0x00, 0x00, + 0xBA, 0x83, 0x19, 0xE9, 0x9F, 0x5D, 0xF3, 0x0A, 0xDE, 0x48, 0x70, 0x44, 0x97, 0xFE, 0xF6, 0x63, + 0xE6, 0x64, 0x80, 0x3C, 0x60, 0xA7, 0xC2, 0x22, 0xC9, 0x44, 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, + 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0x89, 0x90, 0xC8, 0x3A, 0xEF, + 0xC8, 0xE7, 0x47, 0x83, 0x2F, 0x75, 0x00, 0x00, 0x80, 0xEE, 0x60, 0x46, 0x96, 0x0E, 0x54, 0xDF, + 0x80, 0x15, 0x09, 0x8E, 0xF8, 0xD4, 0xED, 0x47, 0xFD, 0xB3, 0x1F, 0xE1, 0xED, 0x47, 0x24, 0x3E, + 0xDC, 0xEE, 0xCD, 0x78, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, + 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x44, 0x44, 0x5B, 0xE3, 0x4D, 0x58, 0x00, 0x00, + 0xA0, 0xDB, 0x78, 0xC3, 0xCB, 0xEF, 0x32, 0x5B, 0xDE, 0x2F, 0xA3, 0x23, 0x5C, 0xB3, 0xDB, 0x8F, + 0xDA, 0x37, 0x5F, 0x89, 0xF8, 0xC8, 0x7A, 0xFB, 0x21, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, + 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x05, 0x44, 0xBB, 0x73, 0x6F, + 0xC2, 0x7A, 0xF9, 0xCE, 0xD9, 0xC1, 0x97, 0x3B, 0x00, 0x00, 0x40, 0xE7, 0x79, 0x23, 0xCB, 0x36, + 0xA8, 0xE8, 0x88, 0x4E, 0xDD, 0x7E, 0xD4, 0x3F, 0xFB, 0xE1, 0x3E, 0x74, 0xD0, 0xBD, 0x7A, 0x37, + 0x12, 0x20, 0xEE, 0x43, 0x07, 0xB3, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, + 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x53, 0xF1, 0x90, 0xC7, 0x5E, 0xBC, + 0x9E, 0x37, 0x61, 0x01, 0x00, 0x80, 0xEE, 0xE2, 0xED, 0xBD, 0x62, 0x54, 0x45, 0x47, 0xB8, 0x66, + 0xB7, 0x1F, 0xD5, 0x67, 0x3F, 0xFC, 0xD7, 0xEE, 0x06, 0xF1, 0xE1, 0xD6, 0xCE, 0xED, 0x87, 0x08, + 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, + 0x3A, 0x15, 0x0E, 0x79, 0xED, 0x85, 0xAB, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x2E, 0xDE, 0xDE, + 0xC5, 0xE3, 0x2A, 0x3C, 0xC2, 0x35, 0xBD, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC5, 0x87, 0xBA, 0xFD, + 0xB8, 0x27, 0xE5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x15, 0x0D, 0x39, 0xCF, 0x3B, 0xB6, 0x96, 0x37, 0x61, + 0x01, 0x00, 0x80, 0xEE, 0xE1, 0xBF, 0x01, 0x2B, 0x12, 0x1C, 0xD1, 0xB5, 0xBC, 0xFD, 0xB0, 0xF1, + 0x31, 0xF1, 0xD8, 0x8C, 0xDA, 0xDB, 0x8F, 0x30, 0x40, 0x7E, 0x60, 0xA7, 0x22, 0xA3, 0xD1, 0x44, + 0x78, 0xB8, 0x11, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0x89, 0x58, 0x28, 0x62, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0E, 0xFE, + 0x1B, 0xB0, 0x76, 0x7C, 0x52, 0xC6, 0x87, 0x5B, 0xCB, 0xDB, 0x8F, 0x30, 0x3E, 0xDA, 0xBD, 0xFD, + 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, + 0x7A, 0x59, 0x27, 0x42, 0xA1, 0xA8, 0x79, 0x47, 0x6F, 0x1E, 0x0F, 0xBE, 0xE4, 0x01, 0x00, 0x00, + 0x3A, 0xCB, 0x8C, 0x2C, 0x5F, 0x67, 0xB6, 0x7D, 0xA4, 0x75, 0x7C, 0xC8, 0xDB, 0x8F, 0x19, 0xF9, + 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, + 0xC4, 0x21, 0xBD, 0xAC, 0x13, 0x91, 0x50, 0xE8, 0xDC, 0x9B, 0xB0, 0xC6, 0xBE, 0x38, 0x2B, 0xF8, + 0xB2, 0x07, 0x00, 0x00, 0xE8, 0x1C, 0x1B, 0x20, 0xEB, 0x55, 0x7C, 0xB8, 0xA9, 0xDB, 0x0F, 0x3F, + 0x40, 0x44, 0x7C, 0x64, 0xBE, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, + 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA1, 0xE8, 0xF1, 0x26, 0x2C, 0x00, + 0x00, 0xD0, 0x2D, 0xBC, 0x03, 0x57, 0x0D, 0x35, 0x8C, 0x0F, 0x1B, 0x1E, 0xF2, 0xF6, 0xE3, 0x09, + 0x1B, 0x1F, 0x2E, 0x40, 0x1E, 0x8D, 0xDC, 0x7E, 0x84, 0xF1, 0xE1, 0xB6, 0x81, 0xF8, 0xA8, 0x9B, + 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xA9, + 0x38, 0x98, 0x8A, 0xBD, 0x70, 0xB5, 0xF1, 0x7E, 0x71, 0xFB, 0x5D, 0xC1, 0x97, 0x3D, 0x00, 0x00, + 0x40, 0xE7, 0x78, 0x7B, 0x2F, 0x1B, 0x6B, 0x18, 0x20, 0x6E, 0x91, 0xDB, 0x0F, 0x17, 0x1F, 0x67, + 0x07, 0x67, 0xD4, 0xDE, 0x7E, 0xA8, 0x67, 0x3F, 0xBE, 0x21, 0x62, 0x23, 0x3E, 0x11, 0x1E, 0x6E, + 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x2A, + 0x0C, 0xA6, 0x70, 0xDE, 0xF1, 0xDB, 0x36, 0x04, 0x5F, 0xF6, 0x00, 0x00, 0x00, 0x9D, 0x63, 0x86, + 0xE6, 0xEB, 0xF8, 0xA8, 0xB9, 0xFD, 0x08, 0xBF, 0xF5, 0xCA, 0xC6, 0x47, 0x1E, 0xB7, 0x1F, 0x22, + 0x3C, 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, + 0xEB, 0x44, 0x10, 0x4C, 0xF5, 0xBC, 0xE3, 0x7F, 0xC6, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x67, 0x99, + 0x91, 0xFE, 0xD9, 0x66, 0xE7, 0x25, 0x3A, 0x40, 0xEC, 0xA2, 0xDF, 0x7A, 0x75, 0xEE, 0xC9, 0x19, + 0xE6, 0xAC, 0x8D, 0x8F, 0xC9, 0xDB, 0x8F, 0x19, 0xFA, 0xD9, 0x8F, 0x56, 0xB7, 0x1F, 0x22, 0x3C, + 0xDC, 0x88, 0x8F, 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, + 0x44, 0x0C, 0x74, 0x62, 0xBC, 0x09, 0x0B, 0x00, 0x00, 0x74, 0x9C, 0xFF, 0x0A, 0xDE, 0xD8, 0x1B, + 0xB0, 0x6A, 0xBE, 0xF5, 0xCA, 0xEE, 0x9C, 0x0B, 0x10, 0x17, 0x1F, 0xF1, 0xDB, 0x8F, 0x68, 0x7C, + 0x84, 0x01, 0x72, 0x7F, 0x8B, 0xDB, 0x0F, 0x11, 0x1E, 0x6E, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, + 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0x22, 0x04, 0x3A, 0xB6, 0x97, 0x56, 0x99, + 0xE0, 0x4B, 0x1F, 0x00, 0x00, 0xA0, 0x33, 0xFC, 0x37, 0x60, 0x6D, 0xFE, 0xE3, 0x86, 0xF1, 0x51, + 0xB9, 0xFD, 0xB0, 0xF1, 0xD1, 0xE0, 0xF6, 0xC3, 0xC5, 0x47, 0xF5, 0xF6, 0xE3, 0x41, 0xBB, 0xAF, + 0x8B, 0xE8, 0x08, 0x27, 0xC2, 0xC3, 0x8D, 0xF8, 0x48, 0x33, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, + 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x45, 0x40, 0x27, 0x37, 0xB6, 0x8C, 0x37, 0x61, 0x01, 0x00, + 0x80, 0xCE, 0xF2, 0x86, 0x97, 0x0D, 0x36, 0xBC, 0xFD, 0x08, 0xE2, 0xA3, 0xE6, 0xF6, 0x23, 0x88, + 0x8F, 0x9A, 0xDB, 0x8F, 0xF0, 0xD9, 0x8F, 0xEF, 0x36, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, 0x46, 0x7C, + 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0xA7, 0x02, 0xA0, + 0xD3, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0xF2, 0x07, 0x00, 0x00, 0x98, 0x7A, + 0xDE, 0xDE, 0xCB, 0x46, 0x1B, 0xDE, 0x7E, 0x6C, 0xBC, 0xA0, 0x26, 0x3E, 0xEA, 0x3E, 0x74, 0x30, + 0xE9, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x75, 0xF8, 0xEF, 0xF8, 0x16, 0xD8, 0x5F, 0x9B, 0xFD, 0xDF, 0xE1, + 0xC4, 0x6D, 0xEB, 0x83, 0x2F, 0x7F, 0x00, 0x00, 0x80, 0xA9, 0xE7, 0xED, 0x9E, 0x3F, 0xAE, 0x6F, + 0x3F, 0x6C, 0x7C, 0x84, 0xDF, 0x7A, 0x55, 0x73, 0xFB, 0x31, 0x33, 0xDD, 0xED, 0x87, 0x08, 0x0F, + 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, 0xE4, 0xB1, 0x78, 0x20, 0x14, 0x39, 0x71, 0x48, 0x2F, 0xEB, + 0xE4, 0xE1, 0xBF, 0xD3, 0xB3, 0xF1, 0x11, 0xCC, 0x3B, 0xBE, 0x96, 0x37, 0x61, 0x01, 0x00, 0x80, + 0xCE, 0x30, 0x43, 0xFD, 0xB3, 0xCC, 0xEE, 0xB9, 0x32, 0x3E, 0x72, 0xB9, 0xFD, 0x10, 0xE1, 0xE1, + 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, + 0x0F, 0xFF, 0x9D, 0xDE, 0x64, 0x7C, 0xF8, 0x01, 0x72, 0xF4, 0xE6, 0x93, 0xC1, 0x1F, 0x01, 0x00, + 0x00, 0x00, 0x53, 0xCB, 0x7F, 0x03, 0xD6, 0xF6, 0x8B, 0x6B, 0x03, 0x24, 0x8C, 0x0F, 0xF9, 0xEC, + 0x47, 0x83, 0xDB, 0x8F, 0x7B, 0xC5, 0xED, 0x87, 0x08, 0x0F, 0x37, 0xE2, 0x23, 0xCD, 0x54, 0x38, + 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x6A, 0xE3, + 0xC3, 0xDF, 0x91, 0x1B, 0x78, 0x13, 0x16, 0x00, 0x00, 0xE8, 0x0C, 0x33, 0xB2, 0x7C, 0x9D, 0xD9, + 0xFA, 0xC1, 0xC8, 0xED, 0x87, 0x8D, 0x8F, 0xD8, 0xED, 0x47, 0xED, 0x9B, 0xAF, 0x44, 0x7C, 0x3C, + 0x60, 0x47, 0x7C, 0x14, 0x30, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x11, 0x1F, 0x6E, 0x2F, 0xF5, 0xF3, 0x26, 0x2C, 0x00, 0x00, 0xD0, + 0x19, 0xE6, 0xE0, 0xF2, 0xF5, 0xD5, 0xDB, 0x0F, 0x1B, 0x1F, 0xD1, 0xDB, 0x0F, 0xF5, 0xEC, 0xC7, + 0xE9, 0x78, 0x80, 0xB8, 0x0F, 0x1D, 0x8C, 0xDF, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, + 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, + 0x84, 0x47, 0x38, 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x00, 0x00, 0x00, 0x60, + 0xEA, 0x78, 0xFB, 0xAE, 0x1E, 0xAD, 0x89, 0x8F, 0x24, 0xCF, 0x7E, 0xB8, 0x35, 0xBA, 0xFD, 0x10, + 0xE1, 0xE1, 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, + 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x88, 0x8E, 0xE8, 0xDC, 0x3F, 0x67, 0xBC, 0x09, 0x0B, 0x00, + 0x00, 0x74, 0x82, 0xB7, 0xF7, 0xF2, 0x93, 0x7E, 0x7C, 0x34, 0xBA, 0xFD, 0x88, 0xC6, 0x87, 0xBA, + 0xFD, 0xF8, 0x76, 0xE4, 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, + 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x38, 0xC4, 0xBC, + 0xE3, 0xB7, 0x0F, 0x05, 0x7F, 0x0C, 0x00, 0x00, 0x00, 0x4C, 0x1D, 0x6F, 0xE7, 0x9C, 0xF4, 0xB7, + 0x1F, 0x61, 0x80, 0xFC, 0xC0, 0x8E, 0xF8, 0xC8, 0x71, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, + 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x36, 0xD4, 0xBC, 0x23, 0x6B, 0xC6, + 0x82, 0x3F, 0x06, 0x00, 0x00, 0x00, 0xA6, 0x86, 0x39, 0x70, 0xFD, 0x12, 0x6F, 0xFB, 0x27, 0xB2, + 0xDF, 0x7E, 0xDC, 0x13, 0xDC, 0x7E, 0x88, 0xF0, 0x70, 0x23, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, + 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x87, 0x46, 0xC3, + 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x30, 0xD5, 0xCC, 0xC1, 0x65, 0x6B, 0xBD, 0xAD, 0x1F, 0x6E, 0xEF, + 0xF6, 0x43, 0x84, 0x87, 0x1B, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, + 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x30, 0x5A, 0xCD, 0xBD, 0x09, 0xEB, 0xF8, + 0x1D, 0x4B, 0x82, 0x3F, 0x0E, 0x00, 0x00, 0x00, 0x8A, 0x77, 0xFE, 0xE0, 0xD2, 0xF5, 0xDE, 0xA6, + 0x0B, 0x33, 0xDE, 0x7E, 0x10, 0x1F, 0xF9, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, + 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC4, 0x45, 0x92, 0xBD, 0x70, 0x0D, 0x6F, 0xC2, + 0x02, 0x00, 0x00, 0x53, 0xEB, 0xFC, 0xFE, 0x6B, 0x06, 0xB3, 0xDD, 0x7E, 0xCC, 0x90, 0xE1, 0xE1, + 0x46, 0x7C, 0xA4, 0x99, 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, + 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x8B, 0xA4, 0x7B, 0xFE, 0x0A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xA6, + 0xD6, 0xF9, 0xDD, 0x8B, 0x47, 0xA3, 0xB7, 0x1F, 0xD5, 0x00, 0x89, 0xC5, 0x47, 0xDD, 0xED, 0xC7, + 0xDD, 0xC4, 0x47, 0xFB, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, + 0xE4, 0xE1, 0xBF, 0xD3, 0x13, 0x51, 0x91, 0x72, 0xDE, 0xB1, 0xB5, 0xBC, 0x09, 0x0B, 0x00, 0x00, + 0x4C, 0x1D, 0x6F, 0xF7, 0x42, 0x79, 0xFB, 0x31, 0xE1, 0x02, 0xE4, 0xD1, 0xC8, 0xED, 0x47, 0x18, + 0x1F, 0x6E, 0xF7, 0xEB, 0x6F, 0xBD, 0x22, 0x3E, 0xD2, 0x4C, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x07, 0x45, 0xDA, 0x79, 0x47, 0x3E, + 0x3F, 0x1A, 0xFC, 0x71, 0x00, 0x00, 0x00, 0x50, 0x2C, 0x33, 0xD2, 0x3F, 0xDB, 0xDB, 0x31, 0x27, + 0xDD, 0xB3, 0x1F, 0x0F, 0xCE, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, + 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x98, 0xC8, 0xB4, 0xA3, + 0x6B, 0x78, 0x13, 0x16, 0x00, 0x00, 0x98, 0x1A, 0x66, 0xFF, 0xD2, 0x81, 0xF3, 0x5B, 0x3E, 0x6C, + 0x03, 0x24, 0xF2, 0xAD, 0x57, 0xCD, 0x6E, 0x3F, 0x1E, 0xB2, 0xF1, 0x21, 0x6E, 0x3F, 0x88, 0x8F, + 0x34, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, + 0xD3, 0x13, 0x11, 0xD1, 0xCE, 0xDC, 0x9B, 0xB0, 0x5E, 0xBE, 0x73, 0x76, 0xF0, 0xC7, 0x02, 0x00, + 0x00, 0x40, 0x71, 0xCE, 0xED, 0x5F, 0x7A, 0xD7, 0xF9, 0x4D, 0xEF, 0x9B, 0x7C, 0xF6, 0xA3, 0xE6, + 0xC1, 0xF3, 0x19, 0xB5, 0xCF, 0x7E, 0xB8, 0xF8, 0x78, 0xA0, 0xFE, 0xF6, 0x83, 0xF8, 0x48, 0x33, + 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, + 0x11, 0x10, 0xED, 0xCE, 0xBD, 0x09, 0xEB, 0xD8, 0x6D, 0x03, 0xC1, 0x1F, 0x0B, 0x00, 0x00, 0x00, + 0xC5, 0x39, 0x7F, 0x60, 0xD9, 0x86, 0x9A, 0x07, 0xCF, 0xA3, 0xB7, 0x1F, 0xD1, 0xF8, 0x78, 0xD8, + 0x86, 0x87, 0x0B, 0x90, 0xEF, 0xD4, 0xDE, 0x7E, 0x10, 0x1F, 0x69, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x21, 0x8F, 0xF1, + 0x26, 0x2C, 0x00, 0x00, 0x30, 0x55, 0xCE, 0xEF, 0x5E, 0x32, 0xEA, 0xC7, 0x47, 0x83, 0xDB, 0x0F, + 0x17, 0x1F, 0xA7, 0xC2, 0xF8, 0x70, 0xB7, 0x1F, 0x7F, 0x4D, 0x7C, 0x64, 0x9B, 0x0A, 0x87, 0x3C, + 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x08, 0x87, 0x1C, + 0xE7, 0x9D, 0xB8, 0x6D, 0x30, 0xF8, 0x63, 0x01, 0x00, 0x00, 0xA0, 0x38, 0xE7, 0x76, 0x2E, 0x3A, + 0x59, 0x73, 0xFB, 0x11, 0xC4, 0x47, 0xF5, 0xD9, 0x8F, 0x30, 0x3E, 0x62, 0xB7, 0x1F, 0xC4, 0x47, + 0x9A, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, + 0xE9, 0xE9, 0x68, 0xC8, 0x73, 0xDE, 0xD1, 0x5B, 0x79, 0x13, 0x16, 0x00, 0x00, 0x28, 0xDE, 0xF9, + 0xED, 0x73, 0xAA, 0xF1, 0x51, 0xF7, 0xA1, 0x83, 0x8F, 0xCC, 0x90, 0xB7, 0x1F, 0xC4, 0x47, 0x9A, + 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, + 0xE9, 0x60, 0xC8, 0x7D, 0x47, 0x3F, 0xCF, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xB1, 0xCE, 0xEE, 0x5F, + 0x3A, 0x70, 0x6E, 0xCB, 0x27, 0x63, 0xB7, 0x1F, 0x33, 0x2B, 0xCF, 0x7E, 0x44, 0xE3, 0xC3, 0xED, + 0xDE, 0xCA, 0xED, 0x07, 0xF1, 0x91, 0x66, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, + 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0x22, 0x14, 0x8A, 0x9A, 0x7B, 0x13, 0xD6, 0xD8, 0x17, + 0x67, 0x05, 0x7F, 0x3C, 0x00, 0x00, 0x00, 0xE4, 0xEF, 0xDC, 0xDE, 0xE5, 0xEB, 0xCE, 0x6D, 0xFA, + 0x48, 0xEC, 0xF6, 0x63, 0x46, 0x35, 0x3E, 0xAA, 0x01, 0xF2, 0x23, 0x3B, 0xE2, 0x23, 0xE5, 0x54, + 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x44, + 0x24, 0x14, 0xB9, 0x17, 0xAF, 0xE3, 0x4D, 0x58, 0x00, 0x00, 0xA0, 0x58, 0xE7, 0xF7, 0x2E, 0x5F, + 0x5F, 0xF7, 0xEC, 0xC7, 0x8F, 0x27, 0x03, 0xA4, 0x7A, 0xFB, 0xF1, 0xED, 0x0B, 0x88, 0x8F, 0x54, + 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, + 0x13, 0x81, 0x50, 0xF4, 0x5E, 0xB8, 0xCA, 0x78, 0xBF, 0xB8, 0xFD, 0xAE, 0xE0, 0x8F, 0x07, 0x00, + 0x00, 0x80, 0xFC, 0x9D, 0xDB, 0x7D, 0xD5, 0x50, 0xCD, 0x9B, 0xAF, 0xC2, 0xF8, 0x88, 0x06, 0xC8, + 0x8F, 0x66, 0x10, 0x1F, 0xA9, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0xE2, 0x60, 0x8A, 0xE6, 0x1D, 0xBF, 0x6D, 0x43, 0xF0, 0xC7, + 0x03, 0x00, 0x00, 0x40, 0xFE, 0xCE, 0xED, 0xB8, 0x6C, 0x6C, 0xF2, 0xF6, 0xE3, 0x02, 0x79, 0xFB, + 0xF1, 0x76, 0xE4, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x06, 0x53, 0x35, 0xDE, 0x84, 0x05, 0x00, + 0x00, 0x0A, 0x75, 0x76, 0xDB, 0xFC, 0xE0, 0xF6, 0xC3, 0xC6, 0x87, 0x78, 0xF6, 0xE3, 0xED, 0x1F, + 0x4C, 0xDE, 0x7E, 0x10, 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, + 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0xA3, 0x60, 0x2A, 0xE7, 0x1D, 0xBD, 0x79, 0x3C, 0xF8, + 0xE3, 0x01, 0x00, 0x00, 0x20, 0x5F, 0x66, 0xA8, 0x7F, 0xF6, 0xD9, 0xCD, 0x97, 0x04, 0xF1, 0x51, + 0x7B, 0xFB, 0x71, 0x2A, 0xBC, 0xFD, 0xF8, 0x66, 0xE5, 0xF6, 0x83, 0xF8, 0x68, 0x35, 0x15, 0x0E, + 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x04, + 0x53, 0xBE, 0x97, 0x56, 0xF2, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x8C, 0xB3, 0x43, 0x4B, 0x07, 0xDE, + 0xDD, 0xF8, 0xE1, 0x6A, 0x80, 0x9C, 0xB6, 0x01, 0x52, 0xF3, 0xEC, 0xC7, 0xF7, 0x2B, 0xB7, 0x1F, + 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, + 0xF0, 0xDF, 0xE9, 0x89, 0x10, 0xE8, 0xD4, 0xC6, 0x96, 0xF1, 0x26, 0x2C, 0x00, 0x00, 0x50, 0x0C, + 0x1B, 0x20, 0xEB, 0x27, 0x06, 0xDF, 0x57, 0x73, 0xFB, 0x11, 0x7D, 0xFE, 0xC3, 0xDD, 0x7E, 0x10, + 0x1F, 0xAD, 0xA6, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, + 0x7F, 0xA7, 0x27, 0x22, 0xA0, 0x93, 0x7B, 0xE1, 0x2A, 0x63, 0x8E, 0xDF, 0xB1, 0x2E, 0xF8, 0x63, + 0x02, 0x00, 0x00, 0x20, 0x3F, 0xEF, 0xEE, 0x59, 0x3A, 0xE8, 0xDF, 0x7E, 0x88, 0xF8, 0x70, 0xB7, + 0x1F, 0xC4, 0x47, 0xAB, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, + 0xF2, 0xF0, 0xDF, 0xE9, 0x89, 0x00, 0xE8, 0xF4, 0xDC, 0x3F, 0x17, 0x27, 0x6E, 0x5B, 0x1F, 0xFC, + 0x31, 0x01, 0x00, 0x00, 0x90, 0x9F, 0x89, 0x6D, 0x8B, 0x47, 0xEB, 0xDE, 0x7C, 0x15, 0xDE, 0x7E, + 0xDC, 0x4D, 0x7C, 0x34, 0x9F, 0x0A, 0x87, 0x3C, 0x16, 0x0F, 0x84, 0x22, 0x27, 0x0E, 0xE9, 0x65, + 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xE2, 0xF0, 0xDF, 0x25, 0xF3, 0x8E, 0xAF, 0xE5, 0x4D, 0x58, 0x00, + 0x00, 0x20, 0x7F, 0x13, 0x5B, 0xFA, 0xC6, 0xE5, 0xC3, 0xE7, 0xF7, 0xBB, 0x6F, 0xBD, 0x22, 0x3E, + 0x1A, 0x4F, 0x85, 0x43, 0x1E, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, + 0x4E, 0x4F, 0x1F, 0xFC, 0xBB, 0x65, 0xDE, 0xD1, 0x9B, 0x4F, 0x06, 0x7F, 0x4C, 0x00, 0x00, 0x00, + 0xE4, 0xC3, 0x0C, 0xF5, 0xCF, 0x7A, 0xF7, 0x99, 0x39, 0x95, 0xF8, 0xB0, 0x8B, 0x3E, 0x7C, 0xFE, + 0xD6, 0x5F, 0x11, 0x1F, 0x8D, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, + 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFD, 0x5D, 0xB5, 0x97, 0x56, 0x9A, 0xE0, 0x8F, 0x0A, + 0x00, 0x00, 0x80, 0x7C, 0xB8, 0x37, 0x60, 0x4D, 0x6C, 0xFC, 0x98, 0x0D, 0x90, 0x0B, 0xCC, 0xE9, + 0x47, 0xEC, 0xDC, 0xED, 0x87, 0x0B, 0x90, 0xFB, 0x6D, 0x80, 0x10, 0x1F, 0x0D, 0xA6, 0xC2, 0x21, + 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xFB, + 0xDD, 0x38, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x7C, 0xDD, 0xC4, 0x13, 0x1F, + 0x30, 0xA7, 0xFD, 0x00, 0x09, 0xBE, 0xFD, 0xEA, 0xE1, 0x99, 0xFE, 0xED, 0x07, 0xF1, 0xA1, 0xA6, + 0xC2, 0x21, 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, + 0x0E, 0xFA, 0xDD, 0x3A, 0xDE, 0x84, 0x05, 0x00, 0x00, 0xF2, 0x36, 0x31, 0xB4, 0x74, 0x7D, 0x78, + 0xFB, 0x71, 0xCA, 0xCD, 0xDD, 0x7E, 0x7C, 0x67, 0x06, 0xF1, 0x21, 0xA7, 0xC2, 0x21, 0x8F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF9, 0xDD, 0x3C, + 0xF7, 0xCF, 0x3D, 0x6F, 0xC2, 0x02, 0x00, 0x00, 0x79, 0x9A, 0xD8, 0x79, 0xD5, 0x68, 0xF5, 0xDB, + 0xAF, 0xDC, 0x0D, 0x88, 0x7B, 0xF6, 0xE3, 0x6B, 0xC4, 0x47, 0xFD, 0x54, 0x38, 0xE4, 0x31, 0x15, + 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, 0x01, 0xBF, 0x07, 0xC6, + 0x9B, 0xB0, 0x00, 0x00, 0x40, 0xAE, 0x26, 0xB6, 0x5D, 0x76, 0xB2, 0x7A, 0x03, 0xF2, 0xF0, 0x0C, + 0xF3, 0xF6, 0x7D, 0xC1, 0xED, 0x87, 0x8D, 0x8F, 0x3F, 0x10, 0x1F, 0xC1, 0x54, 0x38, 0xE4, 0x31, + 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0xE1, 0xBE, 0x17, + 0xE6, 0x1D, 0x59, 0x33, 0x16, 0xFC, 0x71, 0x01, 0x00, 0x00, 0xD0, 0xBE, 0x09, 0xFF, 0x0D, 0x58, + 0x93, 0xB7, 0x1F, 0x6F, 0x7E, 0x6D, 0xF2, 0xE6, 0xE3, 0x0D, 0xE2, 0xC3, 0x4E, 0x85, 0x43, 0x1E, + 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xEC, 0x7B, + 0x66, 0x47, 0x6E, 0xE0, 0x4D, 0x58, 0x00, 0x00, 0x20, 0x1F, 0x13, 0x43, 0xD7, 0x2F, 0x99, 0x78, + 0xFA, 0x13, 0xE6, 0xCC, 0xA3, 0x95, 0xCF, 0xFF, 0x70, 0xB7, 0x1F, 0x95, 0xF8, 0xB8, 0xC0, 0xBF, + 0xF9, 0x78, 0x83, 0xF8, 0x28, 0x68, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, + 0xDF, 0xE9, 0x89, 0x03, 0x7D, 0xAF, 0xED, 0xA5, 0x7E, 0xF7, 0x20, 0xFA, 0x92, 0xE0, 0x8F, 0x0D, + 0x00, 0x00, 0x80, 0xEC, 0xCE, 0x0C, 0x2D, 0x5B, 0x3B, 0xF1, 0xE4, 0x87, 0x2B, 0x9F, 0x01, 0xF2, + 0x60, 0xE5, 0xB5, 0xBB, 0xD5, 0xDB, 0x8F, 0xAF, 0xD9, 0xF8, 0xF8, 0x47, 0xE2, 0x23, 0xFF, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, 0xF3, 0xBD, 0x38, + 0xF7, 0x26, 0xAC, 0x13, 0x7F, 0xBE, 0x36, 0xF8, 0x63, 0x03, 0x00, 0x00, 0x20, 0x3B, 0xF7, 0x06, + 0xAC, 0x89, 0xC7, 0x2F, 0xF2, 0x03, 0xE4, 0xED, 0x6F, 0x55, 0x9E, 0xFB, 0x20, 0x3E, 0xDC, 0x54, + 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xC4, + 0x41, 0xBE, 0x57, 0xE7, 0xBE, 0x26, 0x78, 0x13, 0x16, 0x00, 0x00, 0xC8, 0xC3, 0xD9, 0x5D, 0xD7, + 0x0C, 0x4E, 0x3C, 0x36, 0xC3, 0x9C, 0x71, 0xCF, 0x7E, 0x04, 0x6F, 0xBD, 0xFA, 0xC3, 0x7A, 0xE2, + 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0x27, 0x0E, + 0xF1, 0x3D, 0xBB, 0xF9, 0xFE, 0xBC, 0xE3, 0xB7, 0x0D, 0x05, 0x7F, 0x6C, 0x00, 0x00, 0x00, 0x64, + 0xF7, 0xEE, 0xB6, 0xC5, 0xA3, 0x13, 0x8F, 0xCF, 0x30, 0xEF, 0x7C, 0xBB, 0x72, 0xFB, 0xF1, 0xE6, + 0x5F, 0x5D, 0x44, 0x7C, 0x14, 0x32, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, + 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x57, 0x89, 0x0F, 0x3F, 0x40, 0x8E, 0xDC, 0xCC, 0x9B, 0xB0, + 0x00, 0x00, 0x40, 0xFB, 0xCE, 0x6D, 0x5F, 0x60, 0x26, 0x1E, 0xB9, 0xC0, 0xBC, 0xE5, 0x5E, 0xBB, + 0xFB, 0xD7, 0xC4, 0x47, 0x31, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, + 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0xE1, 0xEF, 0xE8, 0x4D, 0xBC, 0x09, 0x0B, 0x00, + 0x00, 0xB4, 0xC7, 0x0C, 0x5D, 0x3B, 0xFB, 0xDC, 0xB6, 0x4B, 0xCD, 0x3B, 0xDF, 0xB4, 0x01, 0xF2, + 0x75, 0xE2, 0xA3, 0x98, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, + 0xA7, 0x0E, 0xF1, 0xBD, 0xBA, 0x58, 0x7C, 0xB8, 0xBD, 0xB4, 0xC2, 0x98, 0x97, 0xBF, 0x38, 0x3B, + 0xF8, 0xE3, 0x03, 0x00, 0x00, 0x20, 0xBD, 0xB3, 0xFB, 0x97, 0x0E, 0xBC, 0xFB, 0xD8, 0x87, 0xCC, + 0xDB, 0x77, 0x5F, 0x64, 0xCE, 0x13, 0x1F, 0x05, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x89, 0xF8, 0x70, 0x7B, 0xE1, 0x6A, 0x63, + 0x8E, 0xDD, 0x36, 0x10, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDE, 0x99, 0x5D, 0x9F, 0xBB, 0xEB, + 0xD4, 0x86, 0x0F, 0x98, 0xF3, 0xFF, 0x44, 0x7C, 0xE4, 0x3F, 0x15, 0x09, 0x45, 0x4D, 0x1C, 0xD2, + 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0x27, 0xC2, 0x23, 0x98, 0xF7, 0xAF, + 0x4B, 0xCC, 0xE9, 0x9F, 0xDD, 0xCA, 0x9B, 0xB0, 0x00, 0x00, 0x40, 0x76, 0xAF, 0x3D, 0x70, 0xED, + 0x86, 0xF1, 0xDD, 0xAB, 0xCD, 0xD9, 0x17, 0xBF, 0x44, 0x7C, 0xE4, 0x3A, 0x15, 0x09, 0x45, 0x4D, + 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xD4, 0x21, 0xBE, 0x57, 0xA7, 0xC3, 0xC3, 0xED, + 0xD4, 0xB3, 0xF3, 0xCC, 0xEF, 0x77, 0xCC, 0x31, 0xBF, 0xDB, 0x77, 0xD3, 0x60, 0xF0, 0xC7, 0x07, + 0x00, 0x00, 0x40, 0x7A, 0xAF, 0xEF, 0xB9, 0x75, 0xF8, 0xF7, 0x07, 0x6F, 0x35, 0xBF, 0xDF, 0xB7, + 0xD2, 0xBC, 0x79, 0xE0, 0x32, 0x73, 0xFE, 0x9F, 0x55, 0x54, 0x24, 0x9D, 0x08, 0x87, 0x3C, 0x26, + 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, + 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0x4E, 0x87, 0xC7, 0x99, 0xBF, 0xEB, 0x33, 0xE3, 0xBB, + 0xE6, 0x9A, 0xDF, 0x6D, 0xB3, 0xF1, 0xE1, 0xB6, 0x67, 0x39, 0xAF, 0xE2, 0x05, 0x00, 0x00, 0xD9, + 0xFD, 0xF3, 0xFF, 0xFE, 0x17, 0x17, 0xFE, 0x76, 0xFB, 0xEA, 0xFB, 0x7E, 0xB7, 0x6D, 0xF1, 0x5B, + 0xBF, 0xDF, 0xB9, 0xD8, 0xB8, 0xBD, 0x7D, 0xE8, 0x72, 0xFF, 0x5B, 0x2D, 0x74, 0x64, 0x34, 0x9A, + 0x08, 0x87, 0x3C, 0x26, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xA9, 0x43, 0x7C, 0xAF, 0xAE, 0x3E, 0x3C, 0xCE, 0xFE, + 0x43, 0x9F, 0x79, 0x73, 0xEF, 0x64, 0x78, 0xBC, 0xBE, 0x75, 0xCE, 0xF8, 0xEF, 0xB6, 0x5D, 0xBA, + 0x7E, 0xB4, 0xFF, 0x3D, 0x7F, 0x14, 0xFC, 0xF1, 0x01, 0x00, 0x00, 0x90, 0xDD, 0x7F, 0xBF, 0xFB, + 0x33, 0x17, 0xBF, 0xB6, 0x79, 0xF1, 0x96, 0x30, 0x42, 0xC6, 0x77, 0x2F, 0x36, 0xA7, 0xFF, 0xC6, + 0x1E, 0xD4, 0x65, 0x6C, 0xC4, 0x27, 0xC2, 0x21, 0x8F, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, 0xF2, + 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, 0xDF, + 0xAB, 0xAB, 0x0D, 0x8F, 0xF3, 0xFF, 0x34, 0xDF, 0xBC, 0xB5, 0x3F, 0x72, 0xE3, 0x61, 0xF7, 0xEA, + 0x33, 0x73, 0x7E, 0x34, 0x3E, 0xF4, 0xA9, 0x59, 0xC1, 0x1F, 0x17, 0x00, 0x00, 0x00, 0xF9, 0xF9, + 0xEF, 0xDF, 0x5D, 0xF2, 0x99, 0xD7, 0xB6, 0x2C, 0xFA, 0x59, 0x18, 0x22, 0x6F, 0xEC, 0xB9, 0xCC, + 0x4C, 0xFC, 0xBD, 0x0B, 0x02, 0x15, 0x1E, 0x6E, 0xB1, 0x68, 0xC8, 0x6B, 0x32, 0x1C, 0xF2, 0x98, + 0x0A, 0x87, 0x3C, 0xA6, 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, + 0x3A, 0xC4, 0xF7, 0xEA, 0x26, 0xC3, 0xC3, 0xFB, 0xE7, 0xF9, 0xE6, 0x9D, 0x91, 0x79, 0x35, 0xE1, + 0xF1, 0xDA, 0xE6, 0x4B, 0xF7, 0x8F, 0x6F, 0xE9, 0xE3, 0xB5, 0xBB, 0x00, 0x00, 0xA0, 0x78, 0xFF, + 0xEB, 0xA1, 0xCB, 0xBE, 0xF8, 0xBB, 0x6D, 0x8B, 0x7E, 0x19, 0x86, 0x88, 0x7B, 0x3E, 0xE4, 0xEC, + 0x4F, 0x5D, 0x1C, 0x10, 0x1F, 0xF5, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, + 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x75, 0x93, 0xF1, 0x51, 0x7D, 0xC0, 0x3C, 0x08, 0x8F, 0xD7, + 0xB7, 0x5E, 0x3A, 0xF6, 0xDA, 0x96, 0x4B, 0x79, 0xDD, 0x2E, 0x00, 0x00, 0x98, 0x5A, 0xA3, 0xFD, + 0xFD, 0x7F, 0xF4, 0xAB, 0x27, 0x2E, 0xFB, 0x3F, 0xA2, 0xCF, 0x87, 0xBC, 0x35, 0xEC, 0x1E, 0x54, + 0x17, 0xD1, 0x90, 0xD7, 0x64, 0x38, 0xE4, 0x31, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, + 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x75, 0x88, 0xEF, 0xD5, 0x55, 0xC2, 0x23, 0xFE, + 0x80, 0xB9, 0x8D, 0x8E, 0x5F, 0xBE, 0xBE, 0x7D, 0xEE, 0xBA, 0xE0, 0x8F, 0x00, 0x00, 0x00, 0x80, + 0xCE, 0xF8, 0xD5, 0x23, 0x57, 0x7E, 0xE0, 0x37, 0x4F, 0x2E, 0xDE, 0x30, 0x19, 0x22, 0x8B, 0xCC, + 0xE9, 0xBF, 0xB9, 0xCC, 0x78, 0xFF, 0x2A, 0x02, 0xA2, 0x9D, 0xC9, 0x70, 0xC8, 0x63, 0x2A, 0x1C, + 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, 0x77, 0x7A, 0xEA, 0x10, + 0xDF, 0xAB, 0x6B, 0xFC, 0x80, 0x39, 0xCF, 0x79, 0x00, 0x00, 0x80, 0xAE, 0x72, 0xF2, 0x91, 0xC5, + 0xF3, 0x5F, 0x7D, 0x66, 0xF1, 0x0E, 0x17, 0x20, 0x6E, 0xE3, 0xBB, 0x17, 0x99, 0x33, 0x7F, 0x67, + 0x0F, 0xE2, 0x2A, 0x26, 0xD2, 0x4E, 0x86, 0x43, 0x1E, 0x8B, 0x47, 0x43, 0x5E, 0x53, 0x91, 0x50, + 0xD4, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1D, 0xE2, 0x7B, 0x73, 0xEA, 0x01, + 0xF3, 0xDF, 0x6D, 0xBD, 0x74, 0xF0, 0xBF, 0x7D, 0x67, 0xDE, 0x07, 0x82, 0x2F, 0x73, 0x00, 0x00, + 0x80, 0xEE, 0xF3, 0xAB, 0x8D, 0x8B, 0xD6, 0xBC, 0xFA, 0xCC, 0xC2, 0x9F, 0x87, 0x21, 0xF2, 0xC6, + 0x9E, 0xC5, 0xE6, 0xDD, 0x7F, 0xB0, 0x87, 0x7D, 0x15, 0x16, 0x49, 0x26, 0xC3, 0x21, 0x8F, 0xA9, + 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, + 0x83, 0x7C, 0xAF, 0xCD, 0xFB, 0xE7, 0x05, 0x75, 0x0F, 0x98, 0xDB, 0x8D, 0xFE, 0x7F, 0x8F, 0x5E, + 0x3A, 0x27, 0xF8, 0xB2, 0x06, 0x00, 0x00, 0xE8, 0x6E, 0xFE, 0xF3, 0x21, 0x83, 0x8B, 0xFF, 0xE2, + 0x77, 0xDB, 0x16, 0xFE, 0x32, 0x0C, 0x91, 0x37, 0x0F, 0x2C, 0x36, 0x67, 0x7F, 0x6A, 0x0F, 0xFE, + 0x2A, 0x32, 0x1A, 0x4D, 0x86, 0x43, 0x1E, 0x53, 0xE1, 0x90, 0xC7, 0x54, 0x24, 0x14, 0x35, 0x71, + 0x48, 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x87, 0xF9, 0x5E, 0xDB, 0xE9, 0xBF, 0xE9, 0xAB, + 0x79, 0xC0, 0xFC, 0xB5, 0x67, 0xE6, 0x1C, 0xE3, 0x01, 0x73, 0x00, 0x00, 0xD0, 0xB3, 0x6C, 0x88, + 0xFC, 0xBB, 0x57, 0x37, 0x2F, 0xBA, 0xE7, 0x77, 0xDB, 0x16, 0xBD, 0x15, 0x86, 0xC8, 0xDB, 0x87, + 0x12, 0x3E, 0xA8, 0x2E, 0xC3, 0x21, 0x8F, 0xA9, 0x70, 0xC8, 0x63, 0x2A, 0x12, 0x8A, 0x9A, 0x38, + 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xC3, 0x7C, 0x2F, 0x6D, 0xE2, 0xEF, 0xE7, 0xF3, + 0x80, 0x39, 0x00, 0x00, 0x28, 0xAF, 0x93, 0xF7, 0x2E, 0xFC, 0xC8, 0x2B, 0x9B, 0x16, 0x3F, 0x1A, + 0x46, 0x88, 0x7B, 0x3E, 0xA4, 0xE9, 0x83, 0xEA, 0x32, 0x1C, 0xF2, 0x98, 0x0A, 0x87, 0x3C, 0xA6, + 0x22, 0xA1, 0xA8, 0x89, 0x43, 0x7A, 0x59, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x3E, 0xD0, 0xF7, 0xCA, + 0xCE, 0xFE, 0xC3, 0x7C, 0xF3, 0xE6, 0x5E, 0xF7, 0xED, 0x56, 0x95, 0xF8, 0xE0, 0x01, 0x73, 0x00, + 0x00, 0x50, 0x6A, 0xFF, 0xE3, 0xFE, 0xCF, 0x2C, 0x7A, 0x75, 0xF3, 0xA2, 0xE7, 0xA2, 0x21, 0x32, + 0xF1, 0xF7, 0x36, 0x0C, 0x88, 0x8F, 0x84, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, + 0x7D, 0xA8, 0xEF, 0x85, 0x9D, 0xFF, 0xA7, 0x05, 0xE6, 0xED, 0x83, 0x7D, 0x41, 0x78, 0x04, 0x37, + 0x1F, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0xA6, 0x8B, 0x5F, 0x3E, 0xB6, 0xF8, 0x96, 0xD7, 0xB7, 0x2E, + 0x7C, 0x39, 0x0C, 0x11, 0xF7, 0xA0, 0xBA, 0xFF, 0x7C, 0x88, 0x0C, 0x87, 0x3C, 0xA6, 0xC2, 0x21, + 0x8F, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xF6, + 0xDD, 0x3E, 0xF7, 0x80, 0xF9, 0xA9, 0x67, 0xA3, 0xE1, 0xE1, 0xC7, 0x07, 0x0F, 0x98, 0x03, 0x00, + 0x80, 0xE9, 0xC7, 0x3D, 0xA8, 0xFE, 0xDB, 0xA7, 0x17, 0x7F, 0xF5, 0xF5, 0xAD, 0x8B, 0x7E, 0xE3, + 0x22, 0xE4, 0x77, 0x3B, 0x16, 0x9A, 0x37, 0xF7, 0x2F, 0x32, 0xE7, 0xFE, 0xD1, 0x1D, 0xEC, 0x55, + 0x44, 0x64, 0x5D, 0x3C, 0x1A, 0xF2, 0x9A, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, + 0xFC, 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xED, 0xAB, 0x3C, 0x60, 0x3E, 0x19, 0x1E, 0xAF, 0x3D, 0x33, + 0x97, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x7E, 0xF5, 0xC8, 0xBC, 0x0F, 0xFC, 0xE6, 0xE9, 0x85, 0xF7, + 0xBD, 0xBE, 0x75, 0xC1, 0x5B, 0x2E, 0x42, 0xDC, 0xDE, 0x1E, 0x59, 0x6C, 0xBC, 0x7F, 0x51, 0x31, + 0x91, 0x76, 0x2A, 0x1C, 0xF2, 0x98, 0x8A, 0x84, 0xA2, 0x26, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, + 0x77, 0x7A, 0xFA, 0x70, 0xDF, 0xCD, 0xAB, 0x3C, 0x60, 0x1E, 0x3E, 0xE7, 0x61, 0xC3, 0x63, 0xCB, + 0x5C, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x88, 0x1B, 0xFB, 0x4F, 0x9F, 0xB9, 0xF8, 0x95, 0xCD, 0x0B, + 0xB7, 0x84, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCD, 0xA9, 0xE7, 0xDA, 0x09, 0x11, 0x15, 0x0E, 0x79, + 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC0, 0xEF, + 0xD6, 0xD5, 0x3E, 0x60, 0x3E, 0xD7, 0xBC, 0xBE, 0x75, 0xEE, 0xB8, 0xFD, 0x91, 0x07, 0xCC, 0x01, + 0x00, 0x00, 0x9A, 0xF9, 0x9F, 0x0F, 0x2D, 0x5A, 0xFA, 0xEA, 0xE6, 0x85, 0x3F, 0x0B, 0x43, 0x64, + 0x7C, 0x68, 0x91, 0x39, 0x33, 0xEA, 0x0E, 0xFE, 0x2A, 0x32, 0x1A, 0x2D, 0x1E, 0x0D, 0x79, 0x4D, + 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xC8, 0xEF, 0xC6, + 0xD5, 0x3F, 0x60, 0x6E, 0xB7, 0x75, 0x0E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA4, 0xF1, 0xBF, 0x1E, + 0x5A, 0xF0, 0xC5, 0xD7, 0xB6, 0x2E, 0xF8, 0x65, 0x18, 0x22, 0x6F, 0xEC, 0x5D, 0x64, 0xCE, 0xFE, + 0xC4, 0x45, 0x80, 0x0A, 0x8E, 0xE8, 0xE2, 0xD1, 0x90, 0xD7, 0x54, 0x24, 0x14, 0x35, 0x71, 0x48, + 0x2F, 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x07, 0xFD, 0x6E, 0x9B, 0x7E, 0xC0, 0x7C, 0x2E, 0x0F, + 0x98, 0x03, 0x00, 0x00, 0x64, 0xE5, 0x1E, 0x54, 0xFF, 0xF5, 0xC6, 0x45, 0x7F, 0x19, 0x7D, 0x3E, + 0xC4, 0x3D, 0xA8, 0x7E, 0xFE, 0x67, 0x2A, 0x3C, 0xDC, 0x54, 0x38, 0xE4, 0x31, 0x15, 0x09, 0x45, + 0x4D, 0x1C, 0xD2, 0xCB, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0xF4, 0x61, 0xBF, 0xDB, 0xC6, 0x03, 0xE6, + 0x00, 0x00, 0x00, 0x05, 0x72, 0x0F, 0xAA, 0xFF, 0xFA, 0xC9, 0x85, 0x1B, 0xC2, 0x08, 0x71, 0x7B, + 0xE7, 0x70, 0xFC, 0xF9, 0x10, 0x15, 0x0E, 0x79, 0x4C, 0x45, 0x42, 0x51, 0x13, 0x87, 0xF4, 0xB2, + 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x7D, 0xD8, 0xEF, 0xA6, 0xF1, 0x80, 0x39, 0x00, 0x00, 0xC0, 0x14, + 0x3A, 0xF9, 0xC8, 0xE2, 0xF9, 0xBF, 0xDD, 0xB4, 0x60, 0x47, 0x18, 0x21, 0xE1, 0x83, 0xEA, 0x3A, + 0x1C, 0xF2, 0x58, 0x3C, 0x10, 0x8A, 0x9C, 0x38, 0xA4, 0x97, 0x75, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, + 0x03, 0x7F, 0xB7, 0x8C, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x3A, 0xE8, 0x57, 0x1B, 0x17, 0xAD, 0x79, + 0x65, 0xD3, 0x82, 0x9F, 0x57, 0x42, 0x64, 0x81, 0x19, 0x1F, 0x5A, 0x68, 0xCE, 0x8C, 0xBA, 0x43, + 0xBC, 0x8A, 0x88, 0xAC, 0x8B, 0x07, 0x42, 0x91, 0x13, 0x87, 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, + 0x3D, 0x7D, 0xE8, 0xEF, 0x86, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x40, 0x97, 0xA8, 0x7C, 0x90, 0xE1, + 0xC2, 0xAF, 0xBE, 0xB6, 0x75, 0xFE, 0x2F, 0x5D, 0x84, 0xB8, 0xBD, 0xB9, 0x7F, 0xA1, 0x39, 0xFB, + 0x13, 0x77, 0xA0, 0x57, 0x41, 0x91, 0x66, 0xF1, 0x40, 0x28, 0x72, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, + 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFE, 0x9D, 0x5E, 0xA3, 0x07, 0xCC, 0x4F, 0x7E, 0xE7, 0x53, 0xF3, + 0x83, 0x2F, 0x01, 0x00, 0x00, 0x00, 0x74, 0xC2, 0x60, 0xFF, 0xA7, 0xFE, 0xDD, 0xAB, 0x9B, 0x16, + 0xDC, 0xF3, 0xFA, 0xD6, 0xF9, 0x6F, 0x85, 0x21, 0xF2, 0xF6, 0x88, 0x7B, 0x50, 0x5D, 0x85, 0x45, + 0x92, 0xA9, 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x0F, 0xFF, + 0x9D, 0x9E, 0x7A, 0xC0, 0xFC, 0xB7, 0x1B, 0xE7, 0xDE, 0x10, 0xFC, 0x23, 0x0F, 0x00, 0x00, 0x80, + 0x6E, 0xF0, 0xFF, 0x7E, 0x63, 0xF1, 0x87, 0x7F, 0xF3, 0xD4, 0x82, 0x47, 0xC3, 0x08, 0x71, 0x3B, + 0xF5, 0xDC, 0x22, 0xE3, 0xFD, 0x8B, 0x8A, 0x8C, 0x46, 0x53, 0x91, 0x50, 0xD4, 0xC4, 0x21, 0xBD, + 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0x1F, 0xFE, 0x3B, 0xB9, 0xF8, 0x03, 0xE6, 0xEE, 0x39, 0x8F, + 0x57, 0x9F, 0xBE, 0xF4, 0x3F, 0x06, 0xFF, 0x88, 0x03, 0x00, 0x00, 0xA0, 0x1B, 0xFD, 0x8F, 0xFB, + 0x17, 0x2C, 0x7A, 0x75, 0xF3, 0x82, 0x9F, 0x85, 0x11, 0xF2, 0xFB, 0x5D, 0x0B, 0xCC, 0xE9, 0xBF, + 0x75, 0x07, 0x61, 0x15, 0x1C, 0xD1, 0xA9, 0x48, 0x28, 0x6A, 0xB1, 0x03, 0x7A, 0x99, 0x27, 0x0F, + 0xFF, 0x9D, 0x9E, 0x0E, 0x80, 0x4E, 0xAD, 0xEE, 0x01, 0xF3, 0xCD, 0x73, 0xDE, 0xB2, 0x3F, 0xF2, + 0x80, 0x39, 0x00, 0x00, 0x40, 0x2F, 0xF9, 0xE5, 0xC3, 0xF3, 0x6F, 0x79, 0x75, 0xCB, 0x82, 0x97, + 0xC3, 0x10, 0x71, 0x0F, 0xAA, 0x4F, 0xFC, 0x57, 0x77, 0xF8, 0x27, 0x3E, 0xA6, 0x6C, 0xF2, 0xF0, + 0xDF, 0xE9, 0xE9, 0x08, 0xE8, 0xC4, 0x1A, 0x3D, 0x60, 0x3E, 0xBE, 0xA5, 0x6F, 0x76, 0xF0, 0x8F, + 0x31, 0x00, 0x00, 0x00, 0x7A, 0x49, 0xF8, 0xA0, 0x7A, 0xF4, 0xF9, 0x10, 0xF7, 0xA0, 0xFA, 0xB9, + 0x7F, 0x74, 0x21, 0x40, 0x7C, 0x14, 0x3A, 0x79, 0xF8, 0xEF, 0xF4, 0x74, 0x08, 0x4C, 0xF5, 0x1A, + 0x3D, 0x60, 0xFE, 0xEB, 0xC7, 0x3E, 0x7D, 0x55, 0xF0, 0x8F, 0x2E, 0x00, 0x00, 0x00, 0x7A, 0x99, + 0xFB, 0x20, 0xC3, 0xDF, 0x6C, 0x9C, 0x7F, 0x5F, 0xED, 0x83, 0xEA, 0x0B, 0xCD, 0xF9, 0x9F, 0xA9, + 0x48, 0x28, 0x6A, 0xE2, 0x90, 0x5E, 0xD6, 0xC9, 0xC3, 0x7F, 0xA7, 0xA7, 0x63, 0x60, 0xAA, 0x17, + 0x7F, 0xC0, 0xFC, 0xF5, 0x6D, 0x73, 0x4E, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x40, 0x49, 0xFD, 0xDB, + 0x83, 0x7D, 0x7D, 0xAF, 0x6C, 0x5E, 0xB0, 0xA5, 0x12, 0x21, 0xF3, 0xCD, 0xEF, 0x77, 0xCD, 0x37, + 0xA7, 0x9E, 0x5B, 0x68, 0xBC, 0x7F, 0x51, 0xC1, 0x90, 0xE7, 0xC4, 0x21, 0xBD, 0xAC, 0x93, 0x87, + 0xFF, 0x4E, 0x4F, 0xC7, 0xC0, 0x54, 0x8E, 0x07, 0xCC, 0x01, 0x00, 0x00, 0xA6, 0xB1, 0xFF, 0xF9, + 0xD0, 0xBC, 0xA5, 0xAF, 0x6E, 0xEE, 0xFB, 0x99, 0x8B, 0x90, 0x30, 0x44, 0xCE, 0x8C, 0xBA, 0x03, + 0xB4, 0x8A, 0x87, 0x76, 0x17, 0x3B, 0xA0, 0x97, 0x79, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0x20, 0x98, + 0xAA, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xAA, 0x7E, 0xF5, 0x58, 0xDF, 0x5F, 0x54, 0x3E, 0xC8, + 0xB0, 0x12, 0x22, 0xE3, 0x43, 0xF6, 0xC0, 0xF8, 0x13, 0x77, 0x98, 0x56, 0x21, 0x91, 0x65, 0xB1, + 0x03, 0x7A, 0x99, 0x27, 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x82, 0xA9, 0x18, 0x0F, 0x98, 0x03, 0x00, + 0x00, 0x40, 0x72, 0x0F, 0xAA, 0xFF, 0x7A, 0x63, 0xDF, 0x5F, 0xBE, 0xBE, 0xB5, 0xEF, 0xAD, 0x30, + 0x44, 0xDE, 0xDC, 0x6F, 0x0F, 0x90, 0x6D, 0x3F, 0x1F, 0x22, 0x0E, 0xE9, 0x65, 0x9D, 0x3C, 0xFC, + 0x77, 0x7A, 0x3A, 0x0C, 0x8A, 0x1E, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x20, 0x11, 0xFF, 0x41, 0xF5, + 0xA7, 0xFA, 0x1E, 0x0D, 0x23, 0xC4, 0xCD, 0x3D, 0xA8, 0x9E, 0xED, 0xF9, 0x10, 0x71, 0x48, 0x2F, + 0xEB, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x71, 0x50, 0xF4, 0xCE, 0xFC, 0xDD, 0x7C, 0x1E, 0x30, 0x07, + 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x41, 0xF5, 0x57, 0x9F, 0x99, 0x3F, 0x12, 0x46, 0x48, 0xFA, 0x07, + 0xD5, 0xC5, 0x21, 0xBD, 0xAC, 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x41, 0x91, 0x73, 0xCF, 0x79, + 0xF0, 0x80, 0x39, 0x00, 0x00, 0x00, 0xDA, 0xF2, 0xAB, 0x27, 0xFA, 0xD6, 0x44, 0x1F, 0x54, 0x77, + 0xCF, 0x87, 0xB4, 0x7E, 0x50, 0x3D, 0x76, 0x40, 0x2F, 0xF3, 0xE4, 0xE1, 0xBF, 0xD3, 0xD3, 0x81, + 0x50, 0xD4, 0xCE, 0xFD, 0x94, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x90, 0xA3, 0xCA, 0x07, 0x19, 0xF6, + 0x7D, 0x35, 0xFA, 0xA0, 0xBA, 0x7B, 0x3E, 0x44, 0x3F, 0xA8, 0x1E, 0x3B, 0xA0, 0x97, 0x79, 0xF2, + 0xF0, 0xDF, 0xE9, 0xE9, 0x48, 0x28, 0x62, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x42, 0xB9, 0xE7, + 0x43, 0x5E, 0xDD, 0xD4, 0x77, 0x4F, 0xF4, 0x41, 0xF5, 0xB7, 0x0E, 0x46, 0x1F, 0x54, 0x17, 0x87, + 0xF4, 0xB2, 0x4E, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0A, 0x79, 0x2F, 0x7C, 0xC0, 0x3C, 0xFA, 0x9C, + 0x87, 0x1D, 0x0F, 0x98, 0x03, 0x00, 0x00, 0xA0, 0x18, 0x47, 0xBF, 0xB1, 0xF8, 0xC3, 0xAF, 0x6C, + 0xEE, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0x3B, 0x87, 0xEC, 0xC1, 0xF4, 0x5F, 0xC4, 0x41, 0xBD, 0x8C, + 0x93, 0x87, 0xFF, 0x4E, 0x4F, 0xC7, 0x42, 0xDE, 0x53, 0x0F, 0x98, 0xFF, 0xFA, 0xE1, 0x4B, 0x6F, + 0x0E, 0xFE, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x8A, 0xE3, 0x3F, 0xA8, 0xEE, 0x9E, 0x0F, 0xD9, 0xDE, + 0x67, 0xDC, 0x7E, 0xBF, 0xB3, 0xCF, 0x9C, 0x7A, 0xCE, 0x1E, 0x54, 0xD5, 0xA1, 0xBD, 0x2C, 0x93, + 0x87, 0xFF, 0x4E, 0xAF, 0x3E, 0x14, 0xF2, 0x9E, 0x7A, 0xC0, 0xFC, 0xD7, 0x83, 0x73, 0xFE, 0xB7, + 0xE0, 0x1F, 0x05, 0x00, 0x00, 0x00, 0x60, 0xEA, 0xFC, 0xF2, 0xE1, 0xB9, 0xB7, 0xBC, 0xB6, 0xB5, + 0xEF, 0x97, 0x61, 0x88, 0x8C, 0xEF, 0x9E, 0x6F, 0x26, 0xFE, 0xAB, 0x3D, 0x18, 0xAB, 0x03, 0x7C, + 0x2F, 0x4F, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x0C, 0x79, 0x4D, 0x3D, 0x60, 0xFE, 0xDA, 0xA6, 0x39, + 0x77, 0xF3, 0x80, 0x39, 0x00, 0x00, 0x00, 0x3A, 0xCA, 0x7F, 0x50, 0xFD, 0xC9, 0xBE, 0xAF, 0xBE, + 0xBE, 0x65, 0xDE, 0x5B, 0x61, 0x88, 0xBC, 0xB9, 0x6F, 0xBE, 0x39, 0xFB, 0x13, 0x7B, 0x48, 0x56, + 0x87, 0xF9, 0x5E, 0x9B, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x1A, 0xF2, 0x98, 0x7A, 0xC0, 0xFC, 0xD5, + 0xCD, 0x73, 0x37, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0xBA, 0x8A, 0x7B, 0x50, 0xFD, 0xB7, 0x9B, + 0xFA, 0xEE, 0x09, 0x23, 0xC4, 0xED, 0xED, 0x61, 0xF7, 0xA0, 0xBA, 0x38, 0xD4, 0xF7, 0xCA, 0xE4, + 0xE1, 0xBF, 0xD3, 0xD3, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0x3D, 0xC9, 0x3D, + 0x1F, 0xF2, 0xCA, 0xA6, 0xBE, 0x2D, 0xD1, 0x10, 0x71, 0xCF, 0x87, 0xF4, 0xDC, 0x83, 0xEA, 0xF2, + 0xF0, 0xDF, 0xE9, 0xE9, 0x78, 0x68, 0x77, 0x3C, 0x60, 0x0E, 0x00, 0x00, 0x80, 0x9E, 0x57, 0xF9, + 0x20, 0xC3, 0x79, 0x35, 0x0F, 0xAA, 0x9F, 0xFE, 0x5B, 0x7B, 0xE0, 0x55, 0x87, 0xFD, 0x6E, 0x9B, + 0x3C, 0xFC, 0x77, 0x7A, 0xF5, 0xE1, 0xD0, 0xEE, 0x78, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xA5, 0xF3, + 0xAB, 0xC7, 0xFA, 0xFE, 0x22, 0xFE, 0xA0, 0x7A, 0x57, 0x3F, 0x1F, 0x22, 0x0F, 0xFF, 0x9D, 0x9E, + 0x0E, 0x88, 0xAC, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0x94, 0x9A, 0x7B, 0x50, 0xFD, 0xB5, 0x2D, + 0xF3, 0xBE, 0x1E, 0x7F, 0x50, 0xFD, 0xDC, 0x3F, 0xDA, 0xC3, 0xB5, 0x8A, 0x80, 0x4E, 0x4D, 0x1E, + 0xFE, 0x3B, 0x3D, 0x1D, 0x11, 0x59, 0xC6, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x98, 0x56, 0xDC, 0x83, + 0xEA, 0xBF, 0x79, 0xAA, 0xEF, 0xC7, 0x61, 0x84, 0xB8, 0x75, 0xCD, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, + 0xE9, 0xE9, 0x90, 0x48, 0xBB, 0x46, 0x0F, 0x98, 0xBF, 0xB6, 0xE5, 0xD2, 0x81, 0xE0, 0x7F, 0x1A, + 0x00, 0x00, 0x00, 0xA0, 0xBC, 0xFC, 0x0F, 0x32, 0xDC, 0xD4, 0x37, 0x12, 0x46, 0x48, 0xF8, 0x41, + 0x86, 0x1D, 0x7B, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x13, 0x69, 0xA7, 0x1E, 0x30, 0x7F, + 0xE5, 0xC9, 0x39, 0xB7, 0x07, 0xFF, 0x53, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x87, 0x7B, 0x50, 0xFD, + 0xB5, 0x2D, 0xF3, 0x8E, 0x85, 0x21, 0xE2, 0x9E, 0x0F, 0x39, 0x33, 0x6A, 0x0F, 0xDF, 0x2A, 0x12, + 0x8A, 0x9A, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x26, 0xD2, 0x4C, 0x3D, 0x60, 0xFE, 0xCA, 0xA6, 0xB9, + 0xFF, 0x57, 0xF0, 0xB7, 0x1E, 0x00, 0x00, 0x00, 0x98, 0x9E, 0xC2, 0x0F, 0x32, 0x8C, 0x3E, 0xA8, + 0xFE, 0xC6, 0x9E, 0x29, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, 0x3D, 0x1D, 0x14, 0x49, 0x17, 0x7F, + 0xC0, 0xDC, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x98, 0xF0, 0x83, 0x0C, 0xE3, 0x0F, 0xAA, + 0x17, 0xF6, 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2A, 0x92, 0x8C, 0x07, 0xCC, 0x01, 0x00, + 0x00, 0x80, 0x0C, 0x5E, 0x79, 0x72, 0xCE, 0xA5, 0xF1, 0x0F, 0x32, 0x74, 0x0F, 0xAA, 0xE7, 0xFA, + 0x7C, 0x88, 0x3C, 0xFC, 0x77, 0x7A, 0x3A, 0x2C, 0x5A, 0x8D, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, + 0x1C, 0xFC, 0x7A, 0x63, 0xDF, 0x55, 0xAF, 0x6E, 0x9E, 0xF7, 0x4F, 0x61, 0x84, 0x84, 0x0F, 0xAA, + 0xCB, 0xA0, 0x48, 0x33, 0x79, 0xF8, 0xEF, 0xF4, 0xEA, 0xC3, 0x22, 0xC9, 0x78, 0xC0, 0x1C, 0x00, + 0x00, 0x00, 0xC8, 0xD9, 0x2F, 0x1F, 0x9E, 0x7B, 0x4B, 0xFC, 0x83, 0x0C, 0x33, 0x3F, 0xA8, 0x2E, + 0x0F, 0xFF, 0x9D, 0x9E, 0x8E, 0x8B, 0x66, 0xE3, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, 0x40, 0xFE, + 0x07, 0x19, 0x6E, 0xEE, 0xFB, 0x3F, 0xE3, 0xCF, 0x87, 0xA4, 0x7A, 0x50, 0x5D, 0x1E, 0xFE, 0x3B, + 0x3D, 0x1D, 0x18, 0x8D, 0xA6, 0x1E, 0x30, 0xFF, 0xED, 0xC6, 0x4B, 0xBF, 0xC5, 0x03, 0xE6, 0x00, + 0x00, 0x00, 0x40, 0x01, 0xC2, 0x07, 0xD5, 0xC3, 0x08, 0x71, 0x7B, 0xEB, 0x40, 0x82, 0x07, 0xD5, + 0xE5, 0xE1, 0xBF, 0xD3, 0xD3, 0x91, 0xA1, 0xE6, 0x9E, 0xF3, 0x88, 0x3F, 0x60, 0xFE, 0xCA, 0x53, + 0x73, 0xB6, 0xF2, 0x80, 0x39, 0x00, 0x00, 0x00, 0x30, 0x05, 0xDC, 0x07, 0x19, 0xBE, 0xBE, 0x75, + 0xDE, 0xFE, 0x68, 0x88, 0xBC, 0x73, 0xA8, 0xC1, 0x83, 0xEA, 0xF2, 0xF0, 0xDF, 0xE9, 0xE9, 0xD0, + 0x88, 0x4F, 0x3D, 0x60, 0xFE, 0xDA, 0xE6, 0x39, 0x3F, 0xE1, 0x01, 0x73, 0x00, 0x00, 0x00, 0xA0, + 0x03, 0xDC, 0x07, 0x19, 0xC6, 0x1F, 0x54, 0x3F, 0xFD, 0xB7, 0xF6, 0xF0, 0x5E, 0x82, 0xF8, 0x70, + 0x0F, 0x98, 0xD7, 0x3C, 0xE7, 0xC1, 0x03, 0xE6, 0x00, 0x00, 0x00, 0x40, 0x77, 0x88, 0x7F, 0x90, + 0xE1, 0xF8, 0xEE, 0x3E, 0x33, 0xF1, 0xF7, 0xEE, 0x20, 0xAF, 0x02, 0xA0, 0x93, 0xAB, 0x0F, 0x8D, + 0xF8, 0xDC, 0x03, 0xE6, 0x6F, 0x0C, 0xD5, 0x3F, 0x60, 0x3E, 0xDA, 0xFF, 0x9E, 0x3F, 0x0A, 0x7E, + 0xBB, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xCD, 0x7F, 0x50, 0x7D, 0xCB, 0xBC, 0xAF, 0x57, 0x1E, 0x54, + 0xB7, 0x07, 0x78, 0xBB, 0x37, 0xF7, 0xF5, 0xD9, 0x03, 0xBD, 0x3B, 0xD8, 0xAB, 0x18, 0x98, 0xEA, + 0xD5, 0xC7, 0x46, 0x74, 0xEE, 0x83, 0x04, 0x79, 0xC0, 0x1C, 0x00, 0x00, 0x00, 0xE8, 0x31, 0xEE, + 0x41, 0xF5, 0x57, 0x36, 0xCD, 0xDB, 0x12, 0x46, 0x88, 0xDB, 0xDB, 0xC3, 0xF3, 0xED, 0x01, 0x5F, + 0x45, 0xC1, 0x54, 0x4D, 0x47, 0x87, 0x1B, 0x0F, 0x98, 0x03, 0x00, 0x00, 0x00, 0x25, 0xF0, 0x6F, + 0x1B, 0xFA, 0xFA, 0x5E, 0xDB, 0x32, 0xF7, 0x27, 0x61, 0x84, 0xFC, 0x7E, 0xE7, 0x3C, 0x73, 0xEA, + 0xD9, 0xF9, 0xF6, 0xC0, 0xAF, 0x02, 0xA1, 0xC8, 0x35, 0x0E, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, + 0x00, 0x4A, 0xE6, 0x57, 0x4F, 0x5C, 0xBA, 0xC6, 0x86, 0xC8, 0xB1, 0x68, 0x88, 0x9C, 0xF9, 0x3B, + 0x17, 0x01, 0x2A, 0x16, 0xF2, 0x5E, 0x7D, 0x78, 0xB8, 0xF1, 0x80, 0x39, 0x00, 0x00, 0x00, 0x50, + 0x62, 0xEE, 0x01, 0xEE, 0xDF, 0x3E, 0x39, 0xF7, 0xAB, 0xAF, 0x6F, 0x9B, 0x37, 0x1E, 0x86, 0x88, + 0x7B, 0x50, 0xBD, 0xD8, 0xE7, 0x43, 0xEA, 0xC3, 0x83, 0x07, 0xCC, 0x01, 0x00, 0x00, 0x80, 0x69, + 0xA4, 0xF2, 0x41, 0x86, 0x73, 0xEF, 0x79, 0x7D, 0xCB, 0xDC, 0x9A, 0x07, 0xD5, 0xCF, 0xFD, 0x34, + 0xEF, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, 0xA6, 0xB1, 0x57, 0x9E, 0x9C, + 0x73, 0xA9, 0x7A, 0x50, 0x3D, 0x9F, 0x10, 0xA9, 0x0D, 0x0F, 0x1E, 0x30, 0x07, 0x00, 0x00, 0x00, + 0xE0, 0x7B, 0x6D, 0x47, 0xDF, 0x40, 0xE5, 0x83, 0x0C, 0x27, 0x43, 0xE4, 0x8D, 0x3D, 0x7D, 0xFE, + 0x33, 0x22, 0xD9, 0xDE, 0x9A, 0x55, 0x79, 0xB8, 0xDC, 0x3D, 0xE3, 0x11, 0xBF, 0xF1, 0xE0, 0x01, + 0x73, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x57, 0x9E, 0x99, 0x73, 0xDB, 0xEB, 0xDB, 0xE7, 0x9E, 0x8C, + 0x86, 0x48, 0x18, 0x23, 0xEF, 0x1C, 0x9A, 0x6F, 0x4E, 0xFF, 0xCD, 0x7C, 0xFF, 0x79, 0x91, 0xF8, + 0x0D, 0x89, 0xFB, 0xFF, 0xDD, 0xBF, 0xEE, 0xFE, 0xFD, 0x77, 0x0E, 0xF5, 0xD5, 0x3C, 0xDF, 0x11, + 0xCE, 0x3D, 0x60, 0x6E, 0x7F, 0xEE, 0x75, 0xC1, 0x5F, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xDC, + 0x1B, 0xB3, 0x5E, 0x79, 0x66, 0xEE, 0x70, 0x3C, 0x44, 0x5A, 0x2E, 0x16, 0x1D, 0x95, 0xCD, 0x19, + 0xFA, 0xF5, 0xA3, 0x97, 0xDE, 0x1C, 0xFC, 0xD4, 0x00, 0x00, 0x00, 0x00, 0xA0, 0xB9, 0x87, 0xD5, + 0x7F, 0xB3, 0x71, 0xDE, 0x9D, 0xAF, 0x3E, 0x3D, 0xEF, 0xA1, 0x57, 0x37, 0xCF, 0x7D, 0x59, 0x46, + 0x47, 0xB8, 0x20, 0x38, 0x5E, 0xDB, 0x3C, 0xF7, 0xD8, 0xAB, 0xCF, 0xCC, 0x7D, 0xDA, 0xDD, 0x76, + 0xFC, 0xB7, 0xEF, 0xCC, 0xFB, 0x40, 0xF0, 0x53, 0x01, 0x00, 0x00, 0x00, 0x40, 0x3A, 0xEE, 0x35, + 0xB9, 0xFE, 0x07, 0x1B, 0xEE, 0xE8, 0x1B, 0x88, 0xEF, 0xDF, 0x36, 0xCC, 0xEE, 0x0B, 0xFE, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xD0, 0x15, 0xDE, 0xF3, 0x9E, 0xFF, 0x1F, 0x5B, 0xE2, 0x94, 0x1D, 0x86, + 0x86, 0x23, 0x09, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; + +static const uint8_t tree_gif[] PROGMEM = { + + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x28, 0x00, 0x3C, 0x00, 0x83, 0x0B, 0x00, 0x00, 0xFF, 0xFF, + 0x00, 0x21, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x94, 0x00, 0x00, 0xB5, 0x00, 0x00, + 0xDE, 0x00, 0x00, 0xFF, 0x00, 0x6B, 0x00, 0x00, 0xFF, 0x33, 0x33, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x00, 0xCC, 0x00, 0x00, 0x21, 0xFF, 0x0B, + 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0xA0, 0x0F, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, + 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, + 0x8C, 0xCA, 0xE8, 0x85, 0xE8, 0xA5, 0xAC, 0x67, 0xEA, 0x8A, 0xE5, 0x2B, 0x2F, 0xE3, 0x31, 0xCE, + 0xAF, 0x41, 0x1C, 0xB8, 0x1B, 0x24, 0x80, 0x04, 0xAF, 0x87, 0x02, 0x26, 0x88, 0xA1, 0x07, 0x60, + 0xB9, 0x44, 0x82, 0x04, 0x02, 0x80, 0x61, 0x30, 0x70, 0x6E, 0x0C, 0x86, 0x03, 0x60, 0x20, 0xB0, + 0x6A, 0xA8, 0xD3, 0x03, 0x21, 0xEC, 0xAD, 0x18, 0x12, 0xE8, 0xF4, 0xB1, 0x4C, 0x51, 0xA0, 0x07, + 0xEA, 0x07, 0x5B, 0xF2, 0x70, 0x13, 0x76, 0xD4, 0xA8, 0xBC, 0xBC, 0xEA, 0x1F, 0x06, 0x77, 0x51, + 0x73, 0x50, 0x0A, 0x80, 0x7F, 0x04, 0x4C, 0x4D, 0x65, 0x06, 0x0A, 0x3B, 0x3B, 0x62, 0x5A, 0x53, + 0x65, 0x05, 0x02, 0x04, 0x68, 0x0C, 0x6A, 0x00, 0x5D, 0x5E, 0x0F, 0x0F, 0x6A, 0x98, 0x6A, 0x09, + 0x7B, 0x56, 0x9D, 0x09, 0x0C, 0xA7, 0xA7, 0x7F, 0x06, 0x9D, 0xA3, 0x44, 0x02, 0x01, 0x03, 0x90, + 0x0C, 0x87, 0x03, 0x06, 0x02, 0x05, 0x56, 0x06, 0x01, 0x02, 0x53, 0xB1, 0xB3, 0x04, 0x79, 0xAF, + 0x55, 0x48, 0x51, 0xB0, 0x87, 0xC0, 0x7F, 0x07, 0x95, 0x05, 0x85, 0x44, 0xB6, 0x00, 0x05, 0x53, + 0x96, 0x6A, 0x60, 0x02, 0x0A, 0x48, 0x89, 0x00, 0x07, 0xA1, 0x69, 0x7D, 0x2B, 0x3D, 0x9D, 0x41, + 0xA1, 0x04, 0xA0, 0xDD, 0x9D, 0x38, 0x02, 0xE1, 0x6A, 0x07, 0x62, 0x0C, 0x77, 0xE6, 0xAD, 0x2F, + 0x05, 0x01, 0xB6, 0x90, 0x07, 0xA8, 0xA7, 0x54, 0xB5, 0xBC, 0x06, 0x33, 0x50, 0x53, 0x3A, 0x00, + 0x39, 0x62, 0x10, 0x6B, 0xDF, 0xBE, 0x19, 0x03, 0x18, 0x40, 0xA9, 0x15, 0x4B, 0xCC, 0x0E, 0x77, + 0x3B, 0x04, 0xC4, 0x02, 0x38, 0x43, 0x21, 0x96, 0x2C, 0xD3, 0xD6, 0x81, 0x01, 0x26, 0x2A, 0xFB, + 0xDE, 0x07, 0x7C, 0x0A, 0xB7, 0x71, 0x1B, 0x79, 0xEE, 0xC5, 0x83, 0x72, 0xDD, 0x84, 0xBC, 0x4B, + 0x13, 0xA4, 0x13, 0x15, 0x17, 0xAB, 0x18, 0x78, 0x4A, 0x43, 0x40, 0x81, 0x43, 0x76, 0x8E, 0xB6, + 0x04, 0x7C, 0x19, 0x02, 0x98, 0xC4, 0x86, 0x35, 0xBD, 0xE1, 0x1C, 0x90, 0x4D, 0x62, 0xBF, 0x14, + 0xFC, 0x7E, 0x02, 0x1A, 0x60, 0xD3, 0xD1, 0x9D, 0x3B, 0x3A, 0x09, 0xFC, 0x44, 0xB1, 0xD0, 0x96, + 0x8E, 0x43, 0x8D, 0x9E, 0xDA, 0xDB, 0x22, 0x11, 0x8A, 0x42, 0x10, 0x5C, 0x00, 0x1A, 0x2D, 0x38, + 0xF2, 0x0E, 0xA0, 0x4A, 0x03, 0x4E, 0xA2, 0xF0, 0xF4, 0x80, 0x17, 0x95, 0x8C, 0x23, 0xD1, 0xD8, + 0x12, 0x00, 0x92, 0x03, 0xAB, 0x99, 0x23, 0x71, 0x3E, 0x8D, 0xEB, 0x6E, 0x18, 0x07, 0x66, 0x23, + 0x05, 0x3E, 0x72, 0xE4, 0x06, 0x0D, 0x81, 0x02, 0xE4, 0x36, 0x5D, 0xD9, 0xA5, 0xC0, 0xAD, 0x59, + 0xA2, 0x86, 0x6E, 0xD6, 0xC4, 0x73, 0xEB, 0x15, 0xA5, 0xC5, 0xDE, 0x14, 0xF4, 0x62, 0x07, 0xC0, + 0xE9, 0x9D, 0xCC, 0x58, 0x02, 0x14, 0xA0, 0x84, 0x45, 0x83, 0x68, 0x7A, 0x8D, 0x81, 0x66, 0xC3, + 0xE9, 0xD0, 0xA6, 0x3E, 0x03, 0xF3, 0x80, 0x7D, 0x90, 0xD8, 0x58, 0xA2, 0x59, 0x2D, 0x90, 0x68, + 0xD6, 0x64, 0x17, 0x76, 0x5A, 0x23, 0xBF, 0x13, 0x58, 0x89, 0x7A, 0xC3, 0xF0, 0x8F, 0xB6, 0xB8, + 0x4B, 0xD3, 0x74, 0x52, 0x50, 0x92, 0xC2, 0x03, 0xD1, 0x78, 0xD7, 0x89, 0x4C, 0x23, 0x26, 0x6E, + 0x82, 0xCC, 0x15, 0x5E, 0x05, 0x08, 0x30, 0x06, 0x8A, 0x75, 0x64, 0xFA, 0x42, 0xF1, 0x12, 0xAD, + 0x60, 0xDE, 0x51, 0x0A, 0x94, 0xBA, 0x5E, 0x2C, 0x8C, 0x86, 0xDD, 0x3D, 0xDE, 0x8F, 0x0A, 0x70, + 0x51, 0x5F, 0xFE, 0xF2, 0x84, 0xD1, 0xD0, 0xA6, 0x68, 0xB7, 0x96, 0x62, 0x14, 0x0C, 0xC1, 0xB7, + 0x90, 0x68, 0x72, 0xD1, 0x7C, 0xD9, 0x3D, 0xA3, 0x94, 0x35, 0xDE, 0x54, 0x06, 0x92, 0x51, 0x50, + 0x68, 0xC2, 0xC5, 0x44, 0xB7, 0x30, 0x70, 0xDE, 0x6A, 0x49, 0x69, 0x86, 0xC5, 0x2D, 0xA3, 0x29, + 0x04, 0x05, 0x2F, 0xD9, 0xC4, 0x02, 0x0C, 0x6C, 0x0C, 0x48, 0xB0, 0x94, 0x01, 0xDA, 0x3C, 0xF2, + 0x56, 0x21, 0xFA, 0xE8, 0xA3, 0xA1, 0x55, 0x12, 0xE9, 0xF4, 0x56, 0x42, 0xF8, 0x50, 0x00, 0x40, + 0x08, 0x08, 0xD4, 0x88, 0x80, 0x05, 0x33, 0x66, 0x10, 0xA2, 0x0C, 0xDC, 0xA0, 0xB0, 0xA3, 0x0B, + 0x71, 0xCD, 0xB1, 0x80, 0x75, 0x69, 0x20, 0x41, 0xE4, 0x91, 0x6B, 0x64, 0x10, 0x01, 0x00, 0x21, + 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x3C, 0x00, + 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, 0xE4, 0x8C, + 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0x31, 0x1C, 0x34, + 0x1B, 0x24, 0x4C, 0x82, 0xE7, 0x28, 0x5E, 0x02, 0x78, 0x7A, 0x30, 0x8E, 0x47, 0x62, 0x48, 0x20, + 0x60, 0xD8, 0x06, 0x4A, 0x8E, 0xC1, 0x70, 0x60, 0x0C, 0x04, 0xD1, 0xCD, 0x60, 0x60, 0x3B, 0x10, + 0xBA, 0x59, 0x8B, 0x21, 0x41, 0x2E, 0x0F, 0xC3, 0x16, 0xF2, 0xC0, 0xAC, 0x40, 0x4F, 0x1E, 0x80, + 0x04, 0x81, 0x70, 0xD8, 0x0A, 0x14, 0x0F, 0xF7, 0x42, 0x00, 0xA0, 0xD6, 0x09, 0x0A, 0x81, 0x81, + 0x68, 0x00, 0x85, 0x85, 0x7F, 0x07, 0x07, 0x0A, 0x06, 0x7A, 0x06, 0x00, 0x74, 0x74, 0x5E, 0x8A, + 0x36, 0x68, 0x05, 0x7C, 0x66, 0x3D, 0x64, 0x58, 0x61, 0x0F, 0x0F, 0x66, 0x64, 0x99, 0x64, 0x79, + 0x9C, 0x9E, 0xA0, 0x48, 0x4E, 0x9D, 0xA3, 0x4A, 0x02, 0x01, 0x37, 0x90, 0x55, 0x04, 0x5C, 0x02, + 0x05, 0x59, 0x06, 0x01, 0x02, 0x4F, 0x89, 0x55, 0x76, 0xAC, 0x50, 0xAB, 0x0A, 0xAD, 0x7F, 0xB1, + 0x75, 0x07, 0x02, 0x04, 0x05, 0x00, 0xBE, 0x39, 0x06, 0x77, 0x05, 0x36, 0x04, 0x9F, 0x5B, 0xCC, + 0x00, 0x4A, 0x82, 0x81, 0x07, 0x9F, 0x65, 0x86, 0x85, 0x40, 0x9D, 0x0A, 0x9F, 0x0C, 0xD0, 0x9F, + 0x00, 0x9D, 0x39, 0x02, 0xDE, 0x66, 0xB0, 0x73, 0xE2, 0x09, 0xE4, 0xAA, 0x2F, 0x05, 0x01, 0xCC, + 0x5E, 0xA7, 0x56, 0x75, 0xD2, 0xB8, 0x8C, 0x2F, 0x4C, 0x36, 0x06, 0xB1, 0x73, 0xE1, 0x88, 0xC9, + 0x92, 0x45, 0x43, 0x96, 0x00, 0x2E, 0x37, 0xE8, 0x79, 0x99, 0x53, 0xEC, 0x86, 0x0D, 0x06, 0x05, + 0xF3, 0x51, 0x61, 0x57, 0xE6, 0x9E, 0xBF, 0x35, 0x0C, 0xDE, 0x81, 0xE8, 0x64, 0x10, 0xFB, 0x5B, + 0xB6, 0x6C, 0xF5, 0x56, 0x94, 0x22, 0x13, 0xA7, 0xE2, 0x9C, 0x04, 0xDF, 0xCA, 0x74, 0xB2, 0xB2, + 0xC2, 0x40, 0x2A, 0x92, 0x72, 0x20, 0x45, 0x52, 0xE0, 0x45, 0x1A, 0x01, 0x2B, 0xCA, 0x3E, 0xC4, + 0x3A, 0x38, 0x60, 0xDB, 0x23, 0x86, 0xD6, 0x14, 0x0C, 0x20, 0xC0, 0x84, 0xCB, 0x8A, 0x7C, 0x3C, + 0x1F, 0x25, 0x94, 0xA9, 0x80, 0x21, 0x17, 0xA2, 0x5B, 0x4E, 0x34, 0x91, 0xE5, 0x2F, 0x99, 0x4C, + 0xA0, 0x89, 0xEC, 0x5C, 0xC1, 0xB5, 0x89, 0xC3, 0x15, 0x2B, 0xCC, 0x10, 0xD6, 0xF9, 0x38, 0x67, + 0xA8, 0xB1, 0x01, 0xE5, 0x40, 0xD4, 0x7B, 0x42, 0xF1, 0x63, 0x02, 0x66, 0x02, 0x12, 0xA4, 0xFA, + 0x90, 0x2A, 0x54, 0xB6, 0x44, 0xEB, 0xDA, 0x96, 0x19, 0x9A, 0x53, 0x43, 0x01, 0xBB, 0x64, 0x84, + 0xAE, 0xC3, 0xBB, 0xB0, 0xCC, 0x31, 0x40, 0x5D, 0x35, 0xD8, 0x62, 0x82, 0x6B, 0x68, 0xAC, 0xA6, + 0x43, 0xF1, 0xD2, 0x01, 0x20, 0xE9, 0x8A, 0x25, 0x05, 0x96, 0x36, 0xD8, 0x8A, 0xD7, 0xD8, 0x46, + 0xD0, 0x85, 0xEB, 0x7E, 0x72, 0x31, 0x10, 0x54, 0x9F, 0x85, 0x00, 0xF1, 0xA6, 0x1C, 0x94, 0x2C, + 0x34, 0xB2, 0x17, 0x9F, 0x5B, 0xB8, 0x14, 0x80, 0xFC, 0x41, 0x28, 0x63, 0xC7, 0x03, 0x9A, 0x4A, + 0x32, 0x9C, 0x2C, 0x2B, 0x2E, 0x40, 0x72, 0x63, 0x59, 0x48, 0x85, 0x47, 0x8D, 0xD8, 0xC8, 0xD9, + 0xE2, 0xF0, 0x55, 0x99, 0x4A, 0xE3, 0x82, 0x07, 0x41, 0x3F, 0x25, 0x92, 0xAE, 0xD7, 0x4C, 0x3C, + 0xE7, 0xAC, 0x02, 0x04, 0x58, 0xC4, 0xC4, 0xAD, 0x9C, 0x1B, 0xB1, 0x3F, 0xE1, 0x42, 0x3D, 0x4B, + 0x9E, 0x85, 0xCB, 0x57, 0xA6, 0x30, 0x4B, 0x97, 0x88, 0x26, 0xA2, 0x03, 0x7F, 0x8B, 0x72, 0x2D, + 0x90, 0x79, 0x02, 0xFD, 0x59, 0x36, 0xB2, 0xF3, 0x41, 0x2A, 0x65, 0xAD, 0x75, 0x64, 0x3A, 0x0C, + 0x48, 0xB4, 0x15, 0x35, 0x13, 0xE0, 0xC2, 0xC0, 0x2D, 0xB1, 0x15, 0x95, 0x0C, 0x13, 0x97, 0x59, + 0x73, 0x90, 0x01, 0xF5, 0xAC, 0x36, 0x8D, 0x79, 0x12, 0x40, 0xB8, 0x45, 0x22, 0xB8, 0xF8, 0xC4, + 0x0C, 0x7D, 0xB3, 0x31, 0x96, 0xCF, 0x00, 0x01, 0x76, 0x41, 0x80, 0x4F, 0x0B, 0x80, 0x78, 0x4A, + 0x24, 0x5B, 0x8C, 0x18, 0xDB, 0x8A, 0x0A, 0xF0, 0xC3, 0x15, 0x88, 0x08, 0xF1, 0xC5, 0xC7, 0x3C, + 0x13, 0x40, 0x94, 0x01, 0x81, 0x13, 0x20, 0xA0, 0x23, 0x02, 0x27, 0xE0, 0x58, 0x63, 0x08, 0xD9, + 0xE8, 0x41, 0x81, 0x5B, 0x42, 0x7A, 0x57, 0x86, 0x12, 0x46, 0x26, 0x79, 0x46, 0x06, 0x11, 0x00, + 0x00, 0x21, 0xF9, 0x04, 0x09, 0x14, 0x00, 0x0B, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, + 0x3C, 0x00, 0x03, 0x04, 0xFB, 0x70, 0xC9, 0x49, 0x6B, 0x75, 0x36, 0xEB, 0xBD, 0x31, 0xFF, 0xA0, + 0xE4, 0x8C, 0xA4, 0x17, 0x9E, 0x57, 0x89, 0xAE, 0xA9, 0xC9, 0xAE, 0xE3, 0x31, 0xBE, 0xAF, 0xA1, + 0x1C, 0x34, 0x1B, 0x24, 0x4A, 0x82, 0xE7, 0x27, 0x85, 0x50, 0x08, 0x0C, 0x3D, 0x12, 0xBC, 0xC4, + 0xA3, 0x18, 0x12, 0x08, 0x14, 0x86, 0xC1, 0x80, 0xC9, 0x31, 0x18, 0x0E, 0x84, 0x81, 0x80, 0xBA, + 0x91, 0x46, 0xB1, 0x51, 0x06, 0xD7, 0x62, 0x40, 0x9A, 0x91, 0xE2, 0x71, 0xA5, 0x37, 0x40, 0x33, + 0xDE, 0xE9, 0xF1, 0xE3, 0xA1, 0x20, 0x10, 0x0E, 0x52, 0x01, 0x63, 0xA9, 0x5E, 0x0C, 0x85, 0x03, + 0x76, 0x7A, 0x7D, 0x0B, 0x4E, 0x03, 0x75, 0x78, 0x77, 0x78, 0x06, 0x84, 0x51, 0x37, 0x8A, 0x77, + 0x02, 0x51, 0x6A, 0x05, 0x02, 0x04, 0x67, 0x67, 0x5B, 0x72, 0x47, 0x98, 0x98, 0x7C, 0x5C, 0x73, + 0x66, 0x58, 0x07, 0x8B, 0x0F, 0x00, 0x9F, 0x45, 0x02, 0x01, 0x03, 0xA3, 0x0C, 0x89, 0x03, 0x06, + 0x02, 0x00, 0x5C, 0x06, 0x01, 0x92, 0x52, 0x07, 0xAE, 0x59, 0x03, 0x00, 0xBD, 0xBD, 0x4C, 0x02, + 0x05, 0xAB, 0xB9, 0x70, 0x0C, 0xAC, 0x96, 0x00, 0x52, 0x45, 0x36, 0xC1, 0x51, 0x97, 0x0C, 0x66, + 0x5E, 0x00, 0x8C, 0x4C, 0x0A, 0x05, 0x5F, 0x68, 0x98, 0x4E, 0x56, 0x40, 0x7F, 0x3D, 0x67, 0x97, + 0x9D, 0x73, 0x39, 0x02, 0x74, 0x9C, 0x48, 0xA4, 0x76, 0x76, 0x66, 0x73, 0xA8, 0x2C, 0x05, 0x50, + 0x02, 0xA3, 0xA3, 0x59, 0x78, 0x5E, 0x92, 0xD4, 0x2C, 0xDB, 0xB0, 0x59, 0x76, 0x07, 0x00, 0x78, + 0x16, 0x09, 0x80, 0xF5, 0xE4, 0x05, 0xC1, 0x81, 0x5F, 0xB0, 0x10, 0x00, 0xA0, 0xEE, 0xC0, 0xC0, + 0x03, 0x8E, 0x68, 0x68, 0xB1, 0x72, 0xE5, 0x92, 0xAF, 0x5E, 0xF6, 0x0C, 0x10, 0xF0, 0x86, 0x62, + 0xFB, 0xCE, 0xC1, 0x03, 0x98, 0x00, 0x74, 0x42, 0x42, 0xE7, 0xC5, 0xB9, 0x4E, 0xFF, 0xC2, 0x9D, + 0x99, 0xA3, 0x60, 0x0A, 0x0A, 0x03, 0xED, 0xC0, 0xDD, 0xF1, 0x47, 0xCF, 0xCB, 0x2E, 0x97, 0x20, + 0x92, 0x0D, 0x64, 0x85, 0xA5, 0x65, 0x43, 0x2C, 0x3C, 0x19, 0x10, 0x30, 0x94, 0x2F, 0x27, 0xBE, + 0x9D, 0x87, 0x78, 0xCE, 0x54, 0x47, 0xC0, 0x98, 0xC6, 0x9D, 0x39, 0x2F, 0xC6, 0xB2, 0xF1, 0x87, + 0x29, 0x96, 0x62, 0xC6, 0xB6, 0x69, 0xE2, 0xC0, 0x2B, 0x4A, 0x2C, 0x58, 0x49, 0x47, 0xDA, 0x11, + 0x6A, 0x69, 0xC0, 0xB8, 0x10, 0x22, 0x1F, 0xDC, 0x0A, 0xF4, 0x6D, 0x64, 0x02, 0x06, 0xB1, 0x04, + 0x28, 0x39, 0xBB, 0xA1, 0xDD, 0x49, 0x4C, 0xE9, 0xD4, 0xB9, 0x4D, 0x0A, 0xA2, 0x40, 0x81, 0x91, + 0x81, 0x68, 0x2E, 0x35, 0x43, 0xA0, 0xC0, 0x50, 0x05, 0x1F, 0x6A, 0x39, 0xB9, 0x65, 0x47, 0x4A, + 0xBF, 0xBC, 0xF4, 0xF0, 0x04, 0xE3, 0x98, 0xA1, 0x96, 0x02, 0x5B, 0x5E, 0x71, 0xA5, 0x53, 0xA8, + 0x4E, 0x68, 0x16, 0x2B, 0x01, 0xFC, 0x42, 0x29, 0x4A, 0x21, 0xF4, 0xE8, 0x87, 0x0A, 0xA5, 0x04, + 0xDA, 0xDC, 0x94, 0x95, 0x6A, 0x03, 0xC2, 0x5A, 0x6E, 0xF0, 0x66, 0xC8, 0x0E, 0xC0, 0x99, 0x20, + 0xDF, 0x16, 0x93, 0xAC, 0x91, 0x64, 0x16, 0x0B, 0xED, 0x14, 0x70, 0xF2, 0xC2, 0x6B, 0x75, 0x27, + 0x68, 0x81, 0xDA, 0x90, 0xB4, 0x0B, 0xDC, 0x74, 0xC8, 0x8B, 0x67, 0xB0, 0xBC, 0x75, 0x2B, 0xCC, + 0x5D, 0xA1, 0x00, 0xD8, 0x09, 0xC4, 0x3D, 0x23, 0x92, 0xB0, 0x6B, 0x29, 0xDA, 0x6A, 0x55, 0x12, + 0x46, 0x7A, 0x41, 0xA5, 0x81, 0xF8, 0x62, 0x25, 0xE8, 0x4E, 0x2A, 0x60, 0x22, 0x52, 0x05, 0xB4, + 0xB4, 0x94, 0x14, 0xAC, 0x12, 0x05, 0xBF, 0xCD, 0xB4, 0x60, 0xA7, 0x6F, 0x66, 0x65, 0xE7, 0x57, + 0xC7, 0xA3, 0xC8, 0x67, 0x85, 0x16, 0x5B, 0x49, 0xF0, 0x84, 0x2A, 0x48, 0x19, 0x92, 0x47, 0x30, + 0x4E, 0x00, 0x60, 0xC9, 0x57, 0x71, 0x79, 0x03, 0x51, 0x30, 0xD3, 0x4C, 0xA0, 0xC0, 0x4E, 0xA4, + 0x1C, 0x45, 0x51, 0x2C, 0x7E, 0x39, 0xB8, 0x18, 0x3E, 0x04, 0xB5, 0x04, 0x14, 0x6C, 0xB3, 0xF8, + 0xF1, 0xC7, 0x3C, 0x00, 0xAA, 0xC6, 0xCB, 0x45, 0xFC, 0x21, 0xD5, 0x52, 0x72, 0x17, 0x95, 0x88, + 0xD8, 0x09, 0x08, 0x00, 0x80, 0x00, 0x02, 0x28, 0x94, 0x28, 0xC1, 0x8C, 0x20, 0x20, 0xD1, 0x5D, + 0x02, 0x27, 0xE8, 0x88, 0x82, 0x5B, 0x84, 0x2C, 0xE0, 0xD6, 0x19, 0x4C, 0x1C, 0xA9, 0xA4, 0x19, + 0x1B, 0x44, 0x00, 0x00, 0x21, 0xFE, 0x19, 0x42, 0x6F, 0x79, 0x27, 0x73, 0x20, 0x48, 0x61, 0x70, + 0x70, 0x79, 0x20, 0x48, 0x6F, 0x6C, 0x69, 0x64, 0x61, 0x79, 0x73, 0x20, 0x50, 0x61, 0x67, 0x65, + 0x00, 0x3B + +}; + +static const uint8_t bird_gif[] PROGMEM = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x74, 0x00, 0x4E, 0x00, 0xCC, 0x13, 0x00, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xBD, 0xE7, 0xE7, 0xD6, 0xEF, 0xEF, 0x63, 0x63, 0x63, 0xAD, 0xAD, 0xAD, 0xCE, + 0xCE, 0xCE, 0xD6, 0xDE, 0xDE, 0x4A, 0x4A, 0x4A, 0x7B, 0x84, 0x84, 0x94, 0x9C, 0x9C, 0xE7, 0xE7, + 0xE7, 0x84, 0x94, 0x94, 0xB5, 0xC6, 0xC6, 0x73, 0x7B, 0x7B, 0x31, 0x31, 0x31, 0xF7, 0xF7, 0xF7, + 0x18, 0x10, 0x10, 0xCE, 0x63, 0x39, 0x6B, 0x39, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xFF, 0x0B, + 0x4E, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2E, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x4B, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0xAE, 0xEC, 0x68, 0x2C, + 0x6D, 0x2C, 0xCF, 0x74, 0x6D, 0x03, 0x46, 0x84, 0x40, 0x77, 0xEF, 0xFF, 0x40, 0x51, 0x42, 0x41, + 0x0C, 0x1A, 0x8F, 0x48, 0x13, 0xC1, 0xD0, 0x20, 0x00, 0x0E, 0x8E, 0x42, 0x72, 0x4A, 0xAD, 0x21, + 0x0E, 0xCD, 0x05, 0xE1, 0x80, 0xA8, 0x7A, 0xBF, 0x29, 0xC2, 0xA0, 0x91, 0x28, 0x34, 0x02, 0x5D, + 0xB0, 0x5A, 0xFD, 0x08, 0x34, 0x14, 0x84, 0x00, 0x7A, 0x4D, 0xF7, 0x22, 0xDC, 0x89, 0xB8, 0xC1, + 0xB9, 0x80, 0xD5, 0xFF, 0x40, 0x0B, 0x77, 0x0C, 0x0F, 0x05, 0x01, 0x0C, 0x0A, 0x0B, 0x0F, 0x08, + 0x06, 0x80, 0x8E, 0x37, 0x7B, 0x01, 0x09, 0x11, 0x03, 0x68, 0x5C, 0x4C, 0x0A, 0x8F, 0x9A, 0x33, + 0x06, 0x09, 0x01, 0x0E, 0x71, 0x68, 0x88, 0x92, 0x07, 0x9B, 0xA6, 0x2C, 0x0A, 0x86, 0x07, 0x95, + 0x05, 0x04, 0xA1, 0x5D, 0x8D, 0xA7, 0xB2, 0x26, 0x09, 0x06, 0x72, 0x72, 0x0E, 0x94, 0x01, 0xA9, + 0x04, 0x08, 0x52, 0xB3, 0xC0, 0x00, 0x09, 0x95, 0xB7, 0x0D, 0x86, 0x01, 0x04, 0x0A, 0xC3, 0x09, + 0x34, 0x43, 0xC1, 0x54, 0x0E, 0xB7, 0xD2, 0x87, 0xAE, 0xBB, 0x99, 0x32, 0x7B, 0x0E, 0xCF, 0x49, + 0x0B, 0xD1, 0xD3, 0x72, 0x0C, 0xDE, 0x57, 0x33, 0xBE, 0xCC, 0xDB, 0x47, 0x05, 0x0C, 0xDF, 0xD3, + 0x05, 0xD7, 0x31, 0x0C, 0xE1, 0xA5, 0xE7, 0x46, 0xBE, 0xEB, 0xD2, 0x04, 0x05, 0xBF, 0x2C, 0x5C, + 0x4D, 0xF3, 0x46, 0x0B, 0x11, 0x56, 0xD9, 0x3B, 0x14, 0x01, 0x14, 0x0F, 0x16, 0x04, 0x1A, 0x30, + 0x28, 0x00, 0x81, 0xA1, 0x3F, 0x1F, 0xAD, 0x02, 0x10, 0x5B, 0x87, 0x6F, 0x0E, 0x8B, 0x02, 0x0E, + 0x04, 0x5C, 0x41, 0x10, 0xF0, 0x61, 0x8F, 0x25, 0x03, 0x26, 0xDA, 0x43, 0xC4, 0x42, 0x8B, 0x00, + 0x05, 0x0E, 0x7C, 0xFF, 0xED, 0xF0, 0x68, 0x83, 0x8B, 0x44, 0x91, 0xDF, 0x06, 0xAC, 0x54, 0x01, + 0x21, 0xA1, 0x80, 0x07, 0x0F, 0x1A, 0x38, 0x70, 0xC7, 0x72, 0x46, 0xC2, 0x97, 0x03, 0x3F, 0xC5, + 0x52, 0xE1, 0x80, 0x81, 0x00, 0x01, 0x19, 0x1B, 0x3C, 0x38, 0xD8, 0x53, 0xC6, 0x81, 0x38, 0x21, + 0x61, 0x4A, 0xEB, 0xB7, 0x42, 0x01, 0x82, 0xA3, 0x58, 0x7D, 0xC9, 0x6B, 0x1A, 0x63, 0x0B, 0xD0, + 0x81, 0x11, 0x08, 0x74, 0x44, 0x61, 0xE0, 0x01, 0xD6, 0xA3, 0x05, 0x16, 0x2D, 0xE5, 0xDA, 0xC2, + 0x80, 0xB7, 0x90, 0x41, 0xCB, 0xA4, 0x39, 0xC1, 0xEF, 0x2C, 0x56, 0xAA, 0x6C, 0x57, 0x38, 0xA8, + 0x14, 0x35, 0xA8, 0x35, 0x14, 0x10, 0x10, 0x34, 0xB0, 0x9B, 0xD5, 0x4F, 0xDE, 0x14, 0x06, 0x8E, + 0xF5, 0x1D, 0xC8, 0x25, 0x85, 0x83, 0xC1, 0x84, 0x05, 0x20, 0x32, 0x30, 0xF4, 0x30, 0x2D, 0x39, + 0x8B, 0x07, 0x32, 0x42, 0xA1, 0x00, 0x72, 0xE4, 0x45, 0x08, 0x7C, 0x59, 0x36, 0x91, 0x18, 0x73, + 0xE6, 0x6F, 0x79, 0x38, 0x2B, 0x88, 0x7C, 0x54, 0xE7, 0xE0, 0x2D, 0xA3, 0x4B, 0x78, 0x7A, 0x79, + 0x7A, 0x1A, 0xCE, 0x04, 0x4C, 0x85, 0xAC, 0x66, 0x7D, 0x96, 0x80, 0xB9, 0xD8, 0x22, 0x4A, 0x4B, + 0x14, 0x00, 0x97, 0xF1, 0xD3, 0x12, 0x3B, 0x79, 0x9F, 0x45, 0xC0, 0x13, 0xB8, 0xB7, 0xE1, 0xC5, + 0x19, 0x33, 0xEB, 0x53, 0xD3, 0xA8, 0x72, 0x01, 0x0D, 0x44, 0x97, 0x5C, 0x90, 0x7B, 0x9B, 0x01, + 0x05, 0xA6, 0x89, 0xFB, 0x3D, 0x54, 0x20, 0x81, 0xDA, 0x8C, 0xD7, 0x91, 0x16, 0x22, 0x91, 0xF8, + 0x80, 0x9F, 0x86, 0x08, 0x1E, 0xF4, 0xF2, 0xD5, 0x7D, 0x96, 0x18, 0xCC, 0x47, 0xA3, 0xDB, 0x7B, + 0xCA, 0x60, 0x40, 0xCA, 0xF4, 0x58, 0x81, 0xE2, 0x42, 0x04, 0x11, 0x4C, 0x80, 0x53, 0x7C, 0x3F, + 0xC9, 0xB1, 0xC7, 0xFF, 0x7A, 0xC1, 0x18, 0xA0, 0x0E, 0x7E, 0xC4, 0x49, 0xA5, 0x99, 0x67, 0xD7, + 0xA5, 0xB4, 0x15, 0x04, 0x93, 0x48, 0xA0, 0xA1, 0x81, 0x7C, 0x49, 0xB3, 0xC7, 0x38, 0xF6, 0x11, + 0x13, 0x52, 0x7E, 0x12, 0xAE, 0x63, 0xC0, 0x55, 0x00, 0x82, 0x62, 0xD8, 0x08, 0x0B, 0x24, 0xA0, + 0xA1, 0x86, 0x57, 0x3C, 0xF0, 0xE0, 0x2D, 0x05, 0x68, 0x67, 0x4A, 0x27, 0xB7, 0x0C, 0x80, 0x95, + 0x7E, 0x41, 0x11, 0x80, 0x5E, 0x85, 0xBF, 0x9D, 0xD0, 0xC0, 0x04, 0x2F, 0x2E, 0x62, 0xCB, 0x34, + 0x03, 0xE4, 0xB1, 0xA2, 0x23, 0x5E, 0x41, 0xC8, 0x63, 0x50, 0x0E, 0x9A, 0x75, 0x5D, 0x68, 0x5B, + 0x9D, 0xA0, 0x45, 0x04, 0x38, 0x2D, 0x72, 0xCC, 0x34, 0x7B, 0x24, 0xF2, 0x88, 0x5B, 0x39, 0xEA, + 0x18, 0xE1, 0x78, 0xD2, 0x58, 0x05, 0x20, 0x6C, 0x2B, 0xD4, 0x78, 0x64, 0x89, 0x6E, 0xB8, 0xF2, + 0xC2, 0x1F, 0x4D, 0xBE, 0x44, 0x22, 0x99, 0x72, 0xE4, 0x01, 0x60, 0x19, 0x31, 0x68, 0xB1, 0xC4, + 0x78, 0x50, 0xF4, 0xE2, 0x9B, 0x43, 0x5E, 0x80, 0x99, 0xE3, 0x8E, 0x6C, 0x4E, 0xE3, 0x63, 0x7A, + 0xD9, 0x55, 0xD6, 0x82, 0x4E, 0x04, 0x30, 0xD0, 0xC0, 0x01, 0x7C, 0x2A, 0xE4, 0xA3, 0xA2, 0x47, + 0xDC, 0x37, 0xE8, 0x9C, 0xE3, 0xC9, 0x64, 0x9D, 0x72, 0x70, 0x54, 0x29, 0x03, 0x04, 0xDF, 0xE5, + 0xE1, 0x4A, 0x19, 0x06, 0xB0, 0xD9, 0xC4, 0x66, 0x48, 0x7C, 0x27, 0xCD, 0x88, 0x98, 0x92, 0x89, + 0xE2, 0x75, 0x69, 0x2D, 0x69, 0xC3, 0x02, 0x07, 0xA4, 0x03, 0x8A, 0x2B, 0x8E, 0xC2, 0x04, 0x05, + 0x03, 0x48, 0x3C, 0x17, 0x5E, 0xAB, 0x7E, 0x61, 0x04, 0x20, 0x03, 0x33, 0x21, 0x01, 0xC1, 0x02, + 0xDF, 0x81, 0x52, 0x8B, 0x34, 0xAD, 0xC8, 0x6A, 0x43, 0x3E, 0x48, 0x9E, 0xF5, 0x24, 0x45, 0xAF, + 0xF2, 0x96, 0x9D, 0xFF, 0x03, 0xF5, 0x51, 0xD1, 0xA2, 0x03, 0x90, 0xCA, 0xF1, 0x94, 0x3E, 0x37, + 0x68, 0x81, 0xA4, 0x98, 0xC0, 0x9A, 0xF8, 0xC0, 0x8F, 0xCA, 0x39, 0xA0, 0xCD, 0x1F, 0x0B, 0x10, + 0x21, 0x0D, 0x03, 0xB8, 0x7D, 0x74, 0x64, 0x98, 0xD2, 0x16, 0x8A, 0x0C, 0xBA, 0xCA, 0x29, 0xF5, + 0x48, 0x8B, 0x53, 0xA1, 0x19, 0x03, 0xA8, 0x18, 0x85, 0xB2, 0x2A, 0xB9, 0x63, 0x06, 0x45, 0x06, + 0x80, 0x48, 0x05, 0xC9, 0x6E, 0x9C, 0x4F, 0xE1, 0xA3, 0xC2, 0x77, 0xF0, 0x16, 0x95, 0x4B, 0xB7, + 0xF4, 0x12, 0xEA, 0x57, 0x59, 0x14, 0xB2, 0x46, 0x08, 0xB8, 0x8E, 0x08, 0x72, 0x06, 0xB3, 0x3E, + 0xA6, 0xF2, 0x28, 0x46, 0x8B, 0x2C, 0xEB, 0xED, 0x03, 0xF3, 0x56, 0x5C, 0xF0, 0x40, 0xE6, 0xED, + 0xA6, 0x9C, 0x7C, 0xD9, 0x02, 0xA2, 0x48, 0xCA, 0x98, 0x25, 0xD6, 0xCE, 0x4E, 0x14, 0x2B, 0x18, + 0xE7, 0xC0, 0xE5, 0xAE, 0x93, 0x00, 0xC2, 0x45, 0x01, 0xB3, 0xC0, 0x8C, 0x74, 0xFA, 0xF7, 0x80, + 0x84, 0xAC, 0xAE, 0x6C, 0xCF, 0x00, 0x04, 0xA4, 0x47, 0x2C, 0x02, 0xCE, 0x6A, 0xF2, 0x06, 0x9D, + 0x98, 0xE5, 0x3C, 0xB0, 0x98, 0xB5, 0x4D, 0xD3, 0x40, 0x04, 0x05, 0x28, 0x57, 0x40, 0x04, 0x0A, + 0x50, 0x2A, 0x4B, 0x02, 0x1F, 0x53, 0xBD, 0x74, 0x54, 0x59, 0xBF, 0x1B, 0x5A, 0x41, 0xAC, 0x65, + 0xA7, 0xF0, 0x33, 0x0C, 0x58, 0x6D, 0xB6, 0x69, 0x68, 0x4F, 0x2B, 0x87, 0x55, 0xB7, 0x38, 0x50, + 0xC8, 0x5D, 0x7E, 0x8A, 0x1D, 0x0C, 0x04, 0x8F, 0xCD, 0x1D, 0x94, 0xDD, 0x4F, 0xA1, 0xEC, 0xA1, + 0x7C, 0xAD, 0x8D, 0xC5, 0x52, 0x4D, 0xBE, 0x0A, 0x3E, 0x9E, 0x55, 0x32, 0xDA, 0xA3, 0x37, 0x03, + 0xBD, 0x44, 0x3D, 0x8F, 0x55, 0x34, 0x3B, 0x6E, 0x4F, 0x03, 0x99, 0x73, 0x89, 0x93, 0xE5, 0xFE, + 0x74, 0x13, 0xB7, 0xCB, 0xE6, 0xA4, 0x4B, 0x63, 0x90, 0x65, 0xED, 0x16, 0x25, 0x77, 0xE9, 0x54, + 0x67, 0x11, 0xDB, 0x89, 0x44, 0xB3, 0x6E, 0xF6, 0x89, 0x7E, 0x37, 0x05, 0x47, 0xE7, 0xB2, 0xFB, + 0xF5, 0x2D, 0x70, 0xDD, 0x80, 0x37, 0xDE, 0x0A, 0x8E, 0xFB, 0x06, 0x1C, 0x00, 0x4D, 0xE0, 0x1E, + 0x80, 0x0C, 0x73, 0xFB, 0x47, 0x00, 0xE8, 0x2C, 0x89, 0x6E, 0xCF, 0x0C, 0x66, 0x87, 0x64, 0x55, + 0x73, 0x87, 0xB5, 0xD2, 0x39, 0xF2, 0x54, 0xA3, 0xAD, 0x24, 0xEF, 0x09, 0xC4, 0x2E, 0x47, 0x0C, + 0x83, 0x4F, 0x44, 0x6E, 0x25, 0xA9, 0x0C, 0x8F, 0x43, 0xE0, 0xD3, 0xB4, 0x30, 0x50, 0x54, 0xF9, + 0x41, 0x57, 0xC9, 0x63, 0xE6, 0x03, 0x00, 0xC1, 0xED, 0xE9, 0xAB, 0x60, 0x4F, 0x62, 0x94, 0x41, + 0x2A, 0xFE, 0x01, 0x56, 0x11, 0x10, 0xB3, 0x65, 0x4F, 0x11, 0xCC, 0x37, 0x4A, 0x10, 0x2C, 0x8E, + 0x3C, 0x80, 0x40, 0x3A, 0x30, 0x20, 0x02, 0x96, 0x10, 0x3F, 0x13, 0x64, 0xA7, 0x1E, 0x17, 0x53, + 0x90, 0x02, 0x28, 0x87, 0x00, 0xB2, 0xC1, 0xE0, 0x29, 0x08, 0x24, 0x50, 0xC9, 0xFE, 0x37, 0x3C, + 0x28, 0x30, 0x67, 0x75, 0x01, 0x9C, 0x20, 0x4E, 0x0A, 0xE0, 0xAC, 0xAF, 0x21, 0x90, 0x3E, 0x0D, + 0x4C, 0x41, 0xBB, 0x42, 0x13, 0x05, 0x63, 0xA4, 0x64, 0x81, 0x43, 0x28, 0x40, 0xED, 0x44, 0x00, + 0x01, 0x2C, 0x78, 0x29, 0x85, 0x2C, 0x38, 0x56, 0x62, 0x1A, 0xC0, 0x1D, 0x35, 0x84, 0x00, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, + 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, + 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, + 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, + 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, + 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, + 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, + 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, + 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, + 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, + 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, + 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, + 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, + 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, + 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, + 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, + 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, + 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, + 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, + 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, + 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, + 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, + 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, + 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, + 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, + 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, + 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, + 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, + 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, + 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, + 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, + 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, + 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, + 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, + 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, + 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, + 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, + 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, + 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, + 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, + 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, + 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, + 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, + 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, + 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, + 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, + 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, + 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, + 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, + 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, + 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, + 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, + 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, + 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, + 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, + 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, + 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, + 0x0D, 0x21, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, + 0x00, 0x6A, 0x00, 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, + 0xAE, 0x6C, 0xEB, 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, + 0x0A, 0xC7, 0x03, 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, + 0xA4, 0x10, 0xA8, 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, + 0x9E, 0x18, 0x02, 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, + 0xD4, 0x81, 0x01, 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, + 0x0D, 0x08, 0x05, 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, + 0x04, 0x06, 0x10, 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, + 0x93, 0x09, 0x0C, 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, + 0x0C, 0x39, 0x9A, 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, + 0xAB, 0x5B, 0x4A, 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, + 0x0D, 0x0D, 0x07, 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, + 0x0E, 0x7B, 0x04, 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, + 0x92, 0x0C, 0x0E, 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, + 0x5B, 0x03, 0x0E, 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, + 0x98, 0xBE, 0x73, 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, + 0xB0, 0x6A, 0xA3, 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, + 0xBC, 0xA5, 0x0C, 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, + 0x04, 0x32, 0x53, 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, + 0x67, 0x8E, 0xA2, 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, + 0x33, 0xA0, 0x2B, 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, + 0x0E, 0x10, 0xC0, 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, + 0x0A, 0x08, 0xF8, 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, + 0x1D, 0x8A, 0x47, 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, + 0x22, 0x22, 0x82, 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, + 0x6B, 0x50, 0xE0, 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, + 0xA3, 0x2D, 0x60, 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, + 0x94, 0x14, 0x7F, 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, + 0x05, 0x30, 0x4A, 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, + 0x53, 0x20, 0xC1, 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, + 0x10, 0x03, 0x80, 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, + 0x26, 0x0F, 0x28, 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, + 0xC6, 0x0A, 0x02, 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, + 0xA0, 0xC0, 0x28, 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, + 0x42, 0x01, 0x5A, 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, + 0x88, 0x33, 0x14, 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, + 0x6A, 0xC4, 0x80, 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, + 0x28, 0xC2, 0xE2, 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, + 0x21, 0xA4, 0x0C, 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, + 0x4A, 0x93, 0x29, 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, + 0x94, 0x0C, 0x38, 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, + 0x56, 0x74, 0x54, 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, + 0x99, 0x83, 0x8A, 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, + 0x10, 0x02, 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, + 0x6A, 0x00, 0x2E, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x00, 0x40, 0x62, 0xB9, 0x28, 0x08, 0x41, 0x28, + 0x4B, 0xE9, 0xBE, 0x70, 0x2C, 0xCF, 0x74, 0x6D, 0xDF, 0xE5, 0xF1, 0x3C, 0x69, 0x52, 0x24, 0x0F, + 0x82, 0xE1, 0x60, 0x00, 0x26, 0x5A, 0xB8, 0xA4, 0x72, 0xC9, 0x94, 0xE9, 0x22, 0x12, 0xC9, 0x64, + 0xF2, 0x18, 0x04, 0xAE, 0xD7, 0x01, 0xB0, 0xD0, 0xEC, 0x7A, 0xBF, 0x31, 0x48, 0x61, 0x12, 0x95, + 0x32, 0x02, 0x8E, 0x06, 0x36, 0x70, 0x50, 0x21, 0xC1, 0xF0, 0x78, 0x72, 0x91, 0x20, 0x4B, 0x55, + 0x85, 0xF5, 0xB5, 0x81, 0xE0, 0xCA, 0xFF, 0x80, 0x32, 0x0D, 0x0F, 0x53, 0x11, 0x11, 0x04, 0x07, + 0x7A, 0x57, 0x0E, 0x04, 0x6F, 0x81, 0x8F, 0x7F, 0x0B, 0x2A, 0x06, 0x94, 0x6A, 0x8A, 0x01, 0x06, + 0x04, 0x05, 0x07, 0x0B, 0x0B, 0x44, 0x94, 0xA0, 0x43, 0x24, 0x90, 0xA4, 0x37, 0x05, 0x04, 0x96, + 0x97, 0x58, 0x45, 0x09, 0x0A, 0x0C, 0x0C, 0x0A, 0x05, 0x0D, 0xB3, 0x05, 0x0A, 0x09, 0x0C, 0x05, + 0x06, 0xA3, 0x4D, 0x9F, 0xA0, 0x0B, 0xBB, 0xA5, 0x33, 0x6D, 0x08, 0x0C, 0x89, 0xAA, 0xC7, 0x8A, + 0x07, 0xAE, 0x3E, 0xC0, 0x32, 0x10, 0x07, 0x3F, 0x2A, 0x0E, 0x8C, 0x2A, 0x08, 0xD6, 0x29, 0x0E, + 0xB9, 0xCD, 0xC1, 0x22, 0x10, 0x45, 0xD2, 0xB8, 0x07, 0x56, 0x97, 0x22, 0xC8, 0x01, 0x0D, 0x0C, + 0x3E, 0x9C, 0x25, 0xCF, 0xA7, 0x04, 0xD9, 0xE2, 0xC8, 0x03, 0xD0, 0x09, 0x04, 0xD6, 0x09, 0xBA, + 0xDC, 0x30, 0x10, 0x0B, 0x06, 0xB6, 0x2A, 0x04, 0x12, 0xDC, 0x52, 0x40, 0x90, 0x60, 0x81, 0x5C, + 0x07, 0x20, 0xA8, 0x1A, 0xD0, 0x20, 0x01, 0x35, 0x07, 0x3C, 0x1C, 0x18, 0x30, 0x87, 0xCC, 0x00, + 0x03, 0x02, 0x0F, 0x12, 0x34, 0xD8, 0xA6, 0xCF, 0x44, 0xA7, 0x8F, 0x9D, 0x0E, 0x88, 0xAC, 0x75, + 0x6B, 0x22, 0xC5, 0x4B, 0x44, 0x4E, 0xFF, 0x26, 0x63, 0x80, 0x20, 0xE3, 0x81, 0x8E, 0x5D, 0x0E, + 0xA4, 0x33, 0xA6, 0x92, 0x8D, 0x03, 0x04, 0x35, 0x2F, 0x35, 0xC0, 0x98, 0xE0, 0x25, 0x4C, 0x26, + 0x07, 0x7C, 0xE4, 0xBC, 0x82, 0x40, 0xE1, 0x50, 0x3D, 0xCA, 0x52, 0x38, 0xFA, 0x89, 0xE3, 0x47, + 0xCE, 0x1F, 0x00, 0x8E, 0xAA, 0x02, 0xC2, 0x80, 0xE9, 0x92, 0x86, 0x2A, 0x0D, 0x3C, 0xA0, 0x29, + 0x55, 0xD1, 0x00, 0x8C, 0x06, 0xAC, 0x26, 0x31, 0x80, 0xC0, 0x24, 0x32, 0x04, 0x09, 0xBA, 0x9A, + 0x1B, 0x94, 0x80, 0xA3, 0x58, 0x18, 0x5A, 0xCF, 0x1C, 0x13, 0x78, 0x69, 0x9C, 0xDA, 0x2C, 0x04, + 0x22, 0x34, 0x78, 0x6B, 0x03, 0x82, 0x02, 0x44, 0x97, 0x5E, 0xD5, 0x1D, 0x60, 0xF7, 0xEE, 0xB9, + 0x07, 0x61, 0xF9, 0xD6, 0x58, 0xC0, 0x20, 0xDB, 0x1A, 0x82, 0x0B, 0x09, 0x17, 0xBE, 0xFB, 0x55, + 0x81, 0xE2, 0x1B, 0x99, 0x10, 0x39, 0xF6, 0x2A, 0x99, 0xB0, 0x61, 0x3D, 0x0E, 0x2F, 0xDF, 0x18, + 0x94, 0x87, 0xF3, 0x00, 0x01, 0x02, 0x24, 0x7F, 0x7E, 0x8C, 0x60, 0xA9, 0x68, 0x17, 0x6D, 0xCC, + 0x62, 0x91, 0x8C, 0x1A, 0x75, 0xE7, 0x2B, 0x30, 0xBA, 0xF2, 0xF1, 0xF9, 0xDA, 0x45, 0x26, 0xAE, + 0x59, 0x4E, 0xD7, 0x4E, 0xED, 0x19, 0xF7, 0x8C, 0xA3, 0x5A, 0xF7, 0xF6, 0x16, 0xF1, 0xC3, 0x68, + 0x70, 0xE1, 0xC3, 0x8B, 0xAF, 0xA1, 0x31, 0xF4, 0xB4, 0xD2, 0xD7, 0x10, 0x60, 0xE9, 0x21, 0x3C, + 0x3C, 0xFA, 0x64, 0xE3, 0x32, 0x6A, 0xD2, 0x56, 0xF0, 0xC0, 0x0F, 0x5F, 0x3A, 0xA9, 0x02, 0x70, + 0xEF, 0x4E, 0x5C, 0x3A, 0x68, 0xD7, 0xE5, 0x54, 0x72, 0xB7, 0xF2, 0xD5, 0xBC, 0xD5, 0xA0, 0x34, + 0xD7, 0xDB, 0x6E, 0xAF, 0x5A, 0x95, 0x03, 0xDE, 0x2F, 0x98, 0x73, 0xDB, 0x70, 0x01, 0xC4, 0xF2, + 0x96, 0x45, 0xB3, 0x41, 0x67, 0x5B, 0xFF, 0x7F, 0xE6, 0x00, 0x20, 0x51, 0x0C, 0xF2, 0xD0, 0x47, + 0x9C, 0x7A, 0xEA, 0x91, 0x07, 0x20, 0x4C, 0x05, 0xC8, 0xA5, 0x9E, 0x70, 0xA7, 0x31, 0x78, 0x92, + 0x24, 0x89, 0xB9, 0x70, 0x4C, 0x26, 0x41, 0x04, 0xA4, 0x40, 0x03, 0x19, 0x32, 0x72, 0xC8, 0x85, + 0x1D, 0xB9, 0x92, 0x20, 0x7F, 0xEE, 0x9D, 0x94, 0xE1, 0x49, 0x0C, 0xDD, 0x84, 0x16, 0x79, 0x11, + 0xF4, 0x11, 0x0B, 0x42, 0x62, 0x41, 0x70, 0xCB, 0x73, 0x1E, 0x0E, 0xE5, 0x20, 0x70, 0x7B, 0x10, + 0x40, 0x58, 0x5E, 0xC4, 0xE4, 0x23, 0x42, 0x02, 0x86, 0x34, 0xA2, 0x98, 0x24, 0x0A, 0x00, 0x19, + 0x63, 0x4E, 0xFD, 0x10, 0x43, 0x93, 0x32, 0x79, 0xF1, 0x90, 0x42, 0x88, 0x2E, 0x2C, 0xF0, 0x80, + 0x21, 0x0F, 0x30, 0x00, 0x5F, 0x30, 0x62, 0xF4, 0xB1, 0x61, 0x90, 0x47, 0x39, 0x18, 0xCB, 0x3F, + 0xEF, 0xF8, 0x60, 0xC0, 0x2F, 0x36, 0x3C, 0xB1, 0x22, 0x53, 0x99, 0x20, 0x90, 0x08, 0x9A, 0x5D, + 0x79, 0xD9, 0x80, 0x00, 0x29, 0xB4, 0xD6, 0x04, 0x59, 0x2C, 0xFC, 0x24, 0x13, 0x2A, 0x78, 0x76, + 0xF5, 0x95, 0x03, 0x0C, 0xA0, 0xA6, 0xA3, 0x17, 0x6E, 0x3D, 0xB2, 0xC0, 0x0F, 0x0A, 0x10, 0xB9, + 0xDA, 0x39, 0x08, 0x08, 0x90, 0x86, 0x00, 0x4E, 0x2E, 0x17, 0x86, 0x3B, 0xA5, 0x4D, 0xAA, 0x87, + 0x42, 0xAF, 0x0C, 0xE2, 0x00, 0xA6, 0x63, 0x8A, 0xF6, 0xA8, 0x0A, 0x91, 0x06, 0xF0, 0x4B, 0x9E, + 0xCE, 0x29, 0xF2, 0x17, 0x4B, 0x96, 0x16, 0x50, 0xA9, 0x9F, 0x9A, 0x02, 0x50, 0x00, 0x23, 0x81, + 0x9E, 0x1A, 0x41, 0x94, 0x47, 0x31, 0x10, 0xE6, 0x01, 0xD3, 0x98, 0xA5, 0x10, 0x2A, 0x2D, 0xF1, + 0x21, 0x46, 0x40, 0xB5, 0xDA, 0x8A, 0xD8, 0x02, 0x0D, 0x21, 0x9A, 0x80, 0x99, 0x98, 0xE0, 0x63, + 0xCE, 0x10, 0x01, 0xC8, 0x7A, 0xC2, 0x79, 0x11, 0x50, 0x65, 0x26, 0x2B, 0x9F, 0x64, 0xF9, 0xD1, + 0xE8, 0x93, 0x29, 0x32, 0x30, 0x84, 0x03, 0x0A, 0x28, 0x04, 0x2C, 0x46, 0x0B, 0x5C, 0x91, 0xAE, + 0x22, 0x64, 0x21, 0xB0, 0x13, 0x30, 0x99, 0xDC, 0xB4, 0xD3, 0xB3, 0xE9, 0x00, 0x50, 0x6A, 0xB2, + 0x00, 0xB4, 0xD1, 0x42, 0x50, 0x42, 0x00, 0xF6, 0x55, 0x51, 0xAA, 0x6A, 0x82, 0x09, 0x22, 0x08, + 0x38, 0xB0, 0x4D, 0xB7, 0x88, 0x2C, 0x60, 0x0D, 0x8B, 0xF8, 0xBE, 0x90, 0x89, 0x02, 0x0E, 0xE0, + 0x03, 0x81, 0xC0, 0x99, 0xDC, 0xAA, 0x2A, 0x44, 0xB3, 0xF8, 0xE9, 0x16, 0x04, 0x08, 0xD8, 0xC2, + 0x1C, 0x97, 0x0D, 0xCB, 0xE0, 0xC9, 0x28, 0x3A, 0x28, 0xF3, 0x5F, 0x6B, 0xBF, 0xD9, 0x72, 0xC4, + 0x62, 0x1D, 0x7F, 0x1B, 0x72, 0x0D, 0xCA, 0xBC, 0xD4, 0x86, 0x2D, 0x3E, 0xB9, 0xBC, 0x5C, 0x08, + 0x00, 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x20, 0x00, 0x6A, 0x00, + 0x25, 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0xE3, 0xA2, 0x14, 0xC6, 0x42, 0xAE, 0x6C, 0xEB, + 0xBE, 0x70, 0x2C, 0xCF, 0x2D, 0x94, 0x10, 0x4C, 0x71, 0x2C, 0x06, 0xF1, 0x24, 0x0A, 0xC7, 0x03, + 0x61, 0xA0, 0x19, 0x8F, 0xC8, 0x24, 0xCC, 0x10, 0x89, 0x48, 0x26, 0x93, 0x47, 0xA4, 0x10, 0xA8, + 0x56, 0x0B, 0x0F, 0xC2, 0x41, 0xC9, 0xED, 0x7A, 0x5B, 0x06, 0xC4, 0x44, 0x42, 0x9E, 0x18, 0x02, + 0x3A, 0x2B, 0xFA, 0xA1, 0xF8, 0xBA, 0xDF, 0xC9, 0xC6, 0xB8, 0xAC, 0x40, 0x30, 0xD4, 0x81, 0x01, + 0x81, 0xA0, 0x82, 0xFB, 0xFF, 0x2D, 0x0B, 0x0E, 0x73, 0x11, 0x6C, 0x78, 0x55, 0x0D, 0x08, 0x05, + 0x80, 0x8C, 0x8D, 0x05, 0x11, 0x08, 0x09, 0x09, 0x0E, 0x54, 0x87, 0x01, 0x0C, 0x04, 0x06, 0x10, + 0x8D, 0x9C, 0x6F, 0x0B, 0x38, 0x96, 0x78, 0x07, 0x05, 0x0A, 0x0C, 0x93, 0x0E, 0x93, 0x09, 0x0C, + 0x0A, 0x0D, 0x7D, 0x9D, 0xAF, 0x30, 0x75, 0x0E, 0x67, 0xA1, 0xB5, 0x79, 0xA3, 0x0C, 0x39, 0x9A, + 0xB0, 0xBC, 0x2B, 0x10, 0x0D, 0x37, 0x0E, 0xAC, 0x07, 0xB6, 0xB5, 0x03, 0x0D, 0xAB, 0x5B, 0x4A, + 0x07, 0x06, 0xCD, 0xCD, 0x3B, 0xBD, 0x32, 0x10, 0x3C, 0x0A, 0x37, 0x7B, 0x40, 0x0D, 0x0D, 0x07, + 0xDB, 0xDB, 0x03, 0xB5, 0x07, 0xA5, 0xCA, 0x30, 0x10, 0x06, 0xD5, 0x7B, 0x0E, 0x0E, 0x7B, 0x04, + 0x08, 0xEC, 0x08, 0x04, 0xC2, 0xBB, 0xD1, 0x31, 0x0B, 0xA3, 0xA4, 0xA6, 0x92, 0x92, 0x0C, 0x0E, + 0x0C, 0x0D, 0x10, 0x87, 0x03, 0xD5, 0x74, 0xB8, 0x82, 0x30, 0xEA, 0x5A, 0x1A, 0x5B, 0x03, 0x0E, + 0x00, 0x5B, 0x17, 0x29, 0x9E, 0xBC, 0x24, 0x0B, 0x0A, 0xEC, 0x23, 0x86, 0xE7, 0x98, 0xBE, 0x73, + 0x43, 0x66, 0x15, 0xB3, 0x55, 0xCE, 0x47, 0x82, 0x02, 0xAE, 0x1E, 0x1E, 0x39, 0xB0, 0x6A, 0xA3, + 0x25, 0x66, 0x26, 0x0F, 0xFF, 0x91, 0x44, 0xF0, 0x43, 0x9C, 0x48, 0x23, 0xC0, 0xBC, 0xA5, 0x0C, + 0x70, 0x80, 0x40, 0x83, 0x99, 0x96, 0x1A, 0x78, 0x74, 0xF9, 0x52, 0xC6, 0x82, 0x04, 0x32, 0x53, + 0x1A, 0x48, 0x80, 0x33, 0xD4, 0x80, 0x02, 0xEC, 0x42, 0xF6, 0x84, 0x21, 0x08, 0x67, 0x8E, 0xA2, + 0xB6, 0x12, 0x3C, 0x60, 0xB0, 0x74, 0xC6, 0x01, 0xA2, 0x1B, 0x75, 0xD2, 0x52, 0x33, 0xA0, 0x2B, + 0x54, 0x2B, 0x3E, 0x8A, 0x54, 0x5D, 0x42, 0xE4, 0x5B, 0x0E, 0x00, 0x56, 0xBA, 0x0E, 0x10, 0xC0, + 0x96, 0xAD, 0x57, 0xA8, 0x0D, 0x7E, 0x6C, 0x1A, 0xEB, 0xC2, 0xC0, 0x54, 0x4B, 0x0A, 0x08, 0xF8, + 0xCB, 0xB3, 0xB6, 0xAD, 0x5A, 0xB5, 0x5F, 0xC1, 0x46, 0x68, 0x40, 0xF7, 0x45, 0x1D, 0x8A, 0x47, + 0x23, 0xA1, 0xFD, 0xFB, 0x76, 0xA3, 0x42, 0x8A, 0x59, 0x1F, 0x10, 0x2E, 0xCC, 0x22, 0x22, 0x82, + 0x7D, 0x1F, 0xBD, 0x01, 0xFE, 0x0A, 0x20, 0xDB, 0x4C, 0x02, 0x6D, 0x96, 0xD2, 0x6B, 0x50, 0xE0, + 0x5E, 0x2E, 0x05, 0x29, 0x36, 0x05, 0x0E, 0x65, 0xE3, 0x66, 0x4A, 0x7D, 0x73, 0xA3, 0x2D, 0x60, + 0x90, 0xE5, 0x44, 0x0A, 0x66, 0xA4, 0xAB, 0x3D, 0x78, 0xB0, 0xC8, 0xCA, 0xDE, 0x94, 0x14, 0x7F, + 0x07, 0x28, 0x07, 0x79, 0x23, 0x52, 0xA5, 0xB0, 0xE8, 0x35, 0x03, 0x06, 0x04, 0x05, 0x30, 0x4A, + 0x2A, 0x68, 0x4E, 0xE2, 0x5D, 0x0B, 0x2D, 0x9A, 0x66, 0x0F, 0xEA, 0x35, 0x43, 0x53, 0x20, 0xC1, + 0x56, 0x93, 0x61, 0x78, 0xF6, 0xAC, 0xD9, 0xCF, 0x40, 0xE9, 0x3D, 0x20, 0x23, 0x10, 0x03, 0x80, + 0x54, 0x23, 0x00, 0x4C, 0x2A, 0x14, 0xE5, 0x25, 0xC1, 0xCC, 0x01, 0xBD, 0xE2, 0x26, 0x0F, 0x28, + 0xA2, 0xCC, 0x0C, 0x45, 0xEC, 0x30, 0x0D, 0x98, 0x97, 0xDD, 0x24, 0x04, 0x49, 0xC6, 0x0A, 0x02, + 0xC8, 0xB1, 0x50, 0xD4, 0xAA, 0x5A, 0xEE, 0x24, 0x48, 0x19, 0x00, 0xE4, 0xF4, 0xA0, 0xC0, 0x28, + 0x0A, 0xB0, 0xE2, 0x1B, 0x0D, 0x33, 0xA9, 0x25, 0x00, 0x52, 0x8B, 0x3C, 0xF8, 0x42, 0x01, 0x5A, + 0x1C, 0x62, 0x44, 0x86, 0x6B, 0x69, 0x46, 0x40, 0x87, 0x1E, 0x06, 0x02, 0x84, 0x88, 0x33, 0x14, + 0xF3, 0x57, 0x00, 0x6D, 0x09, 0x10, 0xC0, 0x09, 0x29, 0x2E, 0xE1, 0x80, 0x6B, 0x6A, 0xC4, 0x80, + 0x90, 0x66, 0x80, 0x69, 0xA6, 0xC0, 0x03, 0x0E, 0xD6, 0x28, 0x02, 0x04, 0x0C, 0x28, 0xC2, 0xE2, + 0x0A, 0x1C, 0xF9, 0xE0, 0xCE, 0x3E, 0x05, 0x94, 0xE6, 0x00, 0x02, 0x11, 0x68, 0x21, 0xA4, 0x0C, + 0x35, 0xB1, 0x11, 0x94, 0x49, 0x09, 0x35, 0xF0, 0x24, 0x0E, 0x48, 0x35, 0x61, 0x4A, 0x93, 0x29, + 0x4C, 0x69, 0xC4, 0x01, 0x42, 0x30, 0x70, 0x25, 0x1E, 0x20, 0x26, 0x44, 0x40, 0x94, 0x0C, 0x38, + 0xC4, 0x44, 0x13, 0x99, 0x1C, 0x10, 0x9B, 0x98, 0x34, 0x98, 0xC0, 0x4E, 0x9B, 0x56, 0x74, 0x54, + 0x08, 0x3B, 0x99, 0xB8, 0xF0, 0x48, 0x13, 0x4D, 0x88, 0x45, 0x27, 0x12, 0x0A, 0x99, 0x83, 0x8A, + 0x40, 0x73, 0xBE, 0x60, 0x57, 0x13, 0xA1, 0x0D, 0xFA, 0xD2, 0x6C, 0x93, 0x91, 0x10, 0x02, 0x00, + 0x21, 0xF9, 0x04, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x17, 0x00, 0x6A, 0x00, 0x32, + 0x00, 0x00, 0x05, 0xFF, 0x20, 0x20, 0x8E, 0x64, 0x69, 0x9E, 0x68, 0xAA, 0x8A, 0x85, 0x02, 0xAD, + 0x70, 0x2C, 0xCF, 0x74, 0x5D, 0x26, 0x0C, 0x63, 0xD8, 0x7C, 0xEF, 0xFF, 0xA2, 0x05, 0x21, 0x80, + 0x58, 0x2C, 0x70, 0x0B, 0xA0, 0x72, 0xC9, 0x64, 0x34, 0x0C, 0x89, 0x86, 0x42, 0xC8, 0xAC, 0x5A, + 0x6B, 0x54, 0x46, 0x81, 0x11, 0x70, 0x5E, 0xBF, 0x60, 0x94, 0xA2, 0xB0, 0x40, 0x10, 0x00, 0x86, + 0x73, 0x23, 0x71, 0x08, 0xBB, 0xAF, 0x90, 0x73, 0xE2, 0x11, 0x28, 0x97, 0x1B, 0xBA, 0xB7, 0x7E, + 0xA9, 0x68, 0x04, 0x0A, 0x01, 0x01, 0x04, 0x39, 0x01, 0x50, 0x7B, 0x87, 0x3E, 0x0B, 0x5C, 0x81, + 0x5D, 0x0F, 0x03, 0x01, 0x78, 0x69, 0x05, 0x88, 0x94, 0x33, 0x0C, 0x8F, 0x8C, 0x0B, 0x81, 0x5A, + 0x08, 0x06, 0x2D, 0x3F, 0x0A, 0x0A, 0x33, 0x0B, 0x63, 0x0D, 0x49, 0x95, 0x26, 0x2D, 0x8C, 0xAB, + 0x05, 0x04, 0x7E, 0x0D, 0x67, 0x3C, 0x10, 0x09, 0x0A, 0x0C, 0x26, 0x10, 0x0E, 0x83, 0x05, 0x07, + 0x0B, 0x69, 0x0F, 0xB4, 0x0E, 0x0F, 0x9D, 0xA8, 0x24, 0x06, 0x08, 0xAB, 0xC8, 0x81, 0xA1, 0x3C, + 0x07, 0x0E, 0x6B, 0x6D, 0x25, 0x06, 0x11, 0x11, 0x12, 0x13, 0x13, 0x0F, 0x11, 0x80, 0xAC, 0xC2, + 0xD0, 0xA8, 0x65, 0x01, 0x98, 0xC9, 0x10, 0x66, 0x0E, 0x93, 0x32, 0x07, 0x38, 0x02, 0x0C, 0xB4, + 0xA2, 0x25, 0x07, 0x08, 0x13, 0x12, 0xF2, 0x13, 0x06, 0x7F, 0x07, 0xDB, 0xED, 0x95, 0xDF, 0xE1, + 0xC9, 0x63, 0x71, 0xDD, 0x29, 0x8E, 0xB8, 0x12, 0xA0, 0x2E, 0x94, 0x03, 0x14, 0xB0, 0xE4, 0x55, + 0x53, 0x80, 0x40, 0x01, 0xB2, 0x01, 0xB9, 0x4E, 0x21, 0x3A, 0x30, 0x64, 0x00, 0xBF, 0x64, 0x0B, + 0x1E, 0x48, 0x3C, 0x41, 0x2A, 0x0A, 0xC1, 0x8F, 0x05, 0x8A, 0x04, 0x74, 0x10, 0x4F, 0x42, 0x84, + 0x07, 0x0E, 0x93, 0xFF, 0x35, 0x40, 0x60, 0xEE, 0x90, 0x14, 0x70, 0x17, 0x91, 0x29, 0x48, 0x80, + 0x62, 0xC1, 0x16, 0x05, 0x1F, 0x73, 0x12, 0x9C, 0xB9, 0xA2, 0x40, 0x04, 0x04, 0x09, 0x82, 0x6A, + 0x4B, 0xC6, 0x80, 0x80, 0x81, 0x17, 0x7A, 0x0A, 0xD4, 0xB3, 0x18, 0x33, 0x10, 0x84, 0x5F, 0x6C, + 0x48, 0xB4, 0x1A, 0xA8, 0x53, 0x67, 0x82, 0x1D, 0x2B, 0x84, 0x2C, 0x4A, 0xB6, 0xEA, 0xC0, 0x4C, + 0x1C, 0xA6, 0x20, 0x20, 0xB5, 0x32, 0x05, 0x66, 0xD3, 0x40, 0x07, 0xA0, 0xD8, 0x12, 0xD1, 0xE0, + 0x41, 0x83, 0xAA, 0x55, 0x61, 0x31, 0xC8, 0xA7, 0x82, 0xA1, 0x83, 0x7A, 0x5C, 0x19, 0x41, 0x48, + 0xDB, 0x22, 0x47, 0xD0, 0xA3, 0x4C, 0x52, 0x32, 0x3D, 0x0B, 0x0E, 0x01, 0x34, 0x03, 0x6E, 0xE1, + 0xC2, 0xC5, 0x43, 0x57, 0x05, 0x04, 0x28, 0x04, 0x1C, 0x38, 0xB9, 0x97, 0x17, 0xD9, 0x81, 0x02, + 0x7F, 0xC7, 0xF6, 0x48, 0x10, 0x68, 0x80, 0x00, 0x8B, 0x95, 0x73, 0x8D, 0x38, 0xB0, 0xEE, 0xAD, + 0xE2, 0x8F, 0x0A, 0x08, 0x00, 0x94, 0x01, 0xA1, 0x17, 0x66, 0x02, 0xB0, 0x71, 0x84, 0x52, 0x9A, + 0x97, 0xB4, 0xEA, 0x1F, 0x07, 0xC1, 0x11, 0x24, 0xDC, 0xC0, 0xC1, 0x18, 0x00, 0xA4, 0x10, 0x30, + 0x38, 0x8D, 0xDA, 0xF0, 0x92, 0x05, 0x69, 0x0F, 0xA4, 0x55, 0xE0, 0x40, 0x32, 0xDE, 0xAE, 0xE5, + 0x7A, 0xE0, 0x02, 0xD0, 0x79, 0x77, 0x65, 0x70, 0xB0, 0x09, 0x14, 0x20, 0xFE, 0x51, 0xFB, 0xC6, + 0x2F, 0x8F, 0x85, 0x42, 0x40, 0x86, 0xF9, 0x7B, 0x0C, 0x5C, 0x8C, 0x3C, 0x7F, 0x26, 0xDC, 0x85, + 0xC0, 0x83, 0x41, 0xA6, 0x4F, 0x6B, 0x41, 0x04, 0xA1, 0xD5, 0x73, 0xB4, 0xDA, 0x35, 0xAB, 0xB8, + 0x8C, 0x23, 0xBD, 0xF5, 0xEB, 0x30, 0x41, 0xD0, 0xD6, 0x69, 0x6B, 0x34, 0x80, 0x0A, 0x04, 0xEB, + 0xA8, 0x14, 0x85, 0xFF, 0x7E, 0x41, 0x60, 0xE6, 0x40, 0x02, 0x05, 0x48, 0xE1, 0x47, 0x75, 0xBB, + 0xB1, 0xC7, 0xC8, 0x4A, 0xC4, 0xCD, 0x47, 0x0C, 0x2C, 0x31, 0x85, 0x64, 0x46, 0x64, 0x09, 0x20, + 0xF0, 0xC0, 0x2F, 0xF7, 0x39, 0xA0, 0x89, 0x7F, 0xEB, 0x01, 0x18, 0x88, 0x5A, 0xF1, 0xE5, 0xC4, + 0x40, 0x74, 0xC4, 0x00, 0xD0, 0x8C, 0x85, 0x03, 0x18, 0xD0, 0x21, 0x02, 0xD4, 0xA1, 0x98, 0xA2, + 0x8A, 0x10, 0x28, 0xF0, 0x00, 0x5C, 0x08, 0x30, 0xA8, 0x8F, 0x33, 0x2A, 0x5A, 0x46, 0x40, 0x02, + 0x27, 0xEA, 0xF8, 0x99, 0x8A, 0x88, 0x39, 0xA3, 0x13, 0x66, 0x34, 0xC5, 0x48, 0xC2, 0x2C, 0x29, + 0x15, 0x79, 0x9D, 0x7A, 0x15, 0xAA, 0xB8, 0x80, 0x6F, 0x39, 0xA1, 0x64, 0x9E, 0x94, 0x00, 0xF4, + 0x67, 0x65, 0x5E, 0x4C, 0x65, 0x79, 0x9D, 0x4D, 0x0E, 0x20, 0x20, 0xE2, 0x16, 0xCD, 0x09, 0x09, + 0x26, 0x00, 0x84, 0x8C, 0xC9, 0xD5, 0x60, 0xA0, 0x9D, 0x89, 0xC0, 0x84, 0x88, 0xDD, 0xF6, 0x66, + 0x0A, 0xCC, 0xC9, 0x79, 0x25, 0x7B, 0xE8, 0x34, 0xB4, 0x4A, 0x30, 0x06, 0xEE, 0x29, 0xC6, 0x03, + 0xF7, 0xF9, 0x69, 0xE5, 0x63, 0x04, 0xC4, 0x84, 0x98, 0x48, 0x86, 0x9A, 0x40, 0xD1, 0x56, 0x8A, + 0x56, 0x66, 0x40, 0x03, 0x52, 0xB0, 0x94, 0xE8, 0xA0, 0x1A, 0x45, 0x7A, 0x42, 0x01, 0xBE, 0x51, + 0x56, 0x29, 0x57, 0x97, 0x1A, 0x20, 0x6A, 0x65, 0x2B, 0x61, 0xE5, 0xA9, 0x3B, 0x09, 0x10, 0x50, + 0xE5, 0xA8, 0xA3, 0x0E, 0xA0, 0xDD, 0xAA, 0x27, 0x20, 0xA8, 0x1A, 0xAC, 0xB8, 0x0E, 0x42, 0xEB, + 0x09, 0x14, 0xBD, 0x3A, 0xA6, 0x08, 0xB8, 0x16, 0xE5, 0xE6, 0xAA, 0xAD, 0x9C, 0xCA, 0x55, 0x0C, + 0x8A, 0x32, 0xF4, 0x25, 0xAD, 0x47, 0xF8, 0xBA, 0xCA, 0x0C, 0x7E, 0x0E, 0xD0, 0x56, 0x4B, 0xBB, + 0x4A, 0x75, 0x6B, 0x8F, 0x32, 0x34, 0x44, 0x2B, 0x00, 0x6C, 0xAB, 0x55, 0xAB, 0xD5, 0xB1, 0x32, + 0xC8, 0xC9, 0x14, 0x24, 0xC3, 0x54, 0x5B, 0x02, 0x2C, 0x9B, 0x86, 0xFB, 0x27, 0x26, 0x1F, 0x3D, + 0x52, 0x00, 0xB5, 0xE6, 0x8A, 0xD0, 0xE3, 0x25, 0xC8, 0x20, 0x4B, 0xE6, 0x23, 0x59, 0xAE, 0xF7, + 0x8E, 0xAA, 0xF1, 0x92, 0x30, 0xA9, 0xB1, 0x29, 0xA0, 0x9A, 0x80, 0x6F, 0x7D, 0x98, 0x9A, 0x56, + 0xA6, 0x0F, 0xC0, 0xDB, 0x2F, 0x09, 0x3E, 0x3A, 0x60, 0x6C, 0x00, 0x23, 0x5C, 0xD9, 0x9B, 0x9A, + 0x0E, 0x4C, 0x33, 0x8D, 0x30, 0x47, 0x16, 0x30, 0xEC, 0xC2, 0xC0, 0xF9, 0x68, 0xD4, 0x75, 0xA4, + 0x6D, 0xA2, 0xE6, 0x91, 0x06, 0x9C, 0xF2, 0x8E, 0xC5, 0x51, 0x2C, 0xCB, 0x71, 0x09, 0x8C, 0x0E, + 0x92, 0xA8, 0x57, 0x27, 0xA9, 0xF9, 0xCB, 0x97, 0x19, 0xC5, 0xCC, 0xD2, 0xCA, 0x33, 0x78, 0x35, + 0xF0, 0x8B, 0xB9, 0xE8, 0x52, 0xF2, 0x02, 0x1B, 0x93, 0xA2, 0x32, 0xCE, 0x30, 0xB4, 0x36, 0xB4, + 0x0D, 0x21, 0x00, 0x00, 0x3B}; diff --git a/lib/lib_div/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino b/lib/libesp32/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino rename to lib/libesp32/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino index b47496535..45bd373f0 --- a/lib/lib_div/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino +++ b/lib/libesp32/lib_mail/examples/Send_Reuse_Session/Send_Reuse_Session.ino @@ -1,245 +1,245 @@ - - -/** - * This example will send multiple messages which - * the session was keep open during sending. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT esp_mail_smtp_port_587 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "First Email with session reusage"; - message.addRecipient("Admin1", "####@#####_dot_com"); - message.addRecipient("Admin2", "####@#####_dot_com"); - message.addCc("####@#####_dot_com"); - message.addBcc("####@#####_dot_com"); - - message.html.content = "

This is the first message.

"; - - /** The HTML text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.html.charSet = "utf-8"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - /** The option to add soft line break to to the message for - * the long text message > 78 characters (rfc 3676) - * Some Servers may not compliant with the standard. - */ - message.text.flowed = true; - - message.text.content = "This is the first message"; - message.text.charSet = "us-ascii"; - - message.html.transfer_encoding = Content_Transfer_Encoding::enc_base64; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - Serial.println(); - Serial.println("Sending first Email..."); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /** Start sending the first Email and keep open the session - * The third parameter is for close the session. - */ - if (!MailClient.sendMail(&smtp, &message, false)) - Serial.println("Error sending Email, " + smtp.errorReason()); - - - /* To clear all message data */ - //message.clear(); - - /** Clear primary recipients, Cc recipients, Bcc recipients, custom headers - * attachments and inline images - */ - message.clearRecipients(); - message.clearCc(); - message.clearBcc(); - //message.clearAttachments(); - //message.clearInlineimages(); - - message.subject = "Second Email with session reusage"; - - message.addRecipient("Admin3", "####@#####_dot_com"); - message.addRecipient("Admin4", "####@#####_dot_com"); - message.addCc("####@#####_dot_com"); - message.addBcc("####@#####_dot_com"); - - message.html.content = "

This is the second message.

"; - message.html.charSet = "us-ascii"; - - message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - message.text.content = "This is the second message"; - message.text.charSet = "UTF-8"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_qp; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - Serial.println(); - Serial.println("Sending second Email..."); - - /* Start sending the second mail and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send multiple messages which + * the session was keep open during sending. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT esp_mail_smtp_port_587 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "First Email with session reusage"; + message.addRecipient("Admin1", "####@#####_dot_com"); + message.addRecipient("Admin2", "####@#####_dot_com"); + message.addCc("####@#####_dot_com"); + message.addBcc("####@#####_dot_com"); + + message.html.content = "

This is the first message.

"; + + /** The HTML text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.html.charSet = "utf-8"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.html.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + /** The option to add soft line break to to the message for + * the long text message > 78 characters (rfc 3676) + * Some Servers may not compliant with the standard. + */ + message.text.flowed = true; + + message.text.content = "This is the first message"; + message.text.charSet = "us-ascii"; + + message.html.transfer_encoding = Content_Transfer_Encoding::enc_base64; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + Serial.println(); + Serial.println("Sending first Email..."); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /** Start sending the first Email and keep open the session + * The third parameter is for close the session. + */ + if (!MailClient.sendMail(&smtp, &message, false)) + Serial.println("Error sending Email, " + smtp.errorReason()); + + + /* To clear all message data */ + //message.clear(); + + /** Clear primary recipients, Cc recipients, Bcc recipients, custom headers + * attachments and inline images + */ + message.clearRecipients(); + message.clearCc(); + message.clearBcc(); + //message.clearAttachments(); + //message.clearInlineimages(); + + message.subject = "Second Email with session reusage"; + + message.addRecipient("Admin3", "####@#####_dot_com"); + message.addRecipient("Admin4", "####@#####_dot_com"); + message.addCc("####@#####_dot_com"); + message.addBcc("####@#####_dot_com"); + + message.html.content = "

This is the second message.

"; + message.html.charSet = "us-ascii"; + + message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + message.text.content = "This is the second message"; + message.text.charSet = "UTF-8"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_qp; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + Serial.println(); + Serial.println("Sending second Email..."); + + /* Start sending the second mail and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Text/Send_Text.ino b/lib/libesp32/lib_mail/examples/Send_Text/Send_Text.ino old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/examples/Send_Text/Send_Text.ino rename to lib/libesp32/lib_mail/examples/Send_Text/Send_Text.ino index 85aa42f03..8c7cfc418 --- a/lib/lib_div/lib_mail/examples/Send_Text/Send_Text.ino +++ b/lib/libesp32/lib_mail/examples/Send_Text/Send_Text.ino @@ -1,219 +1,219 @@ - - -/** - * This example will send the Email in plain text version. - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The sign in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** ######################################################## - * Some properties of SMTPSession data and parameters pass to - * SMTP_Message class accept the pointer to constant char - * i.e. const char*. - * - * You may assign a string literal to that properties or function - * like below example. - * - * session.login.user_domain = "mydomain.net"; - * session.login.user_domain = String("mydomain.net").c_str(); - * - * or - * - * String doman = "mydomain.net"; - * session.login.user_domain = domain.c_str(); - * - * And - * - * String name = "Jack " + String("dawson"); - * String email = "jack_dawson" + String(123) + "@mail.com"; - * - * message.addRecipient(name.c_str(), email.c_str()); - * - * message.addHeader(String("Message-ID: ").c_str()); - * - * or - * - * String header = "Message-ID: "; - * message.addHeader(header.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending plain text Email"; - message.addRecipient("Someone", "####@#####_dot_com"); - - String textMsg = "This is simple plain text message"; - message.text.content = textMsg.c_str(); - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.text.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email in plain text version. + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The sign in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** ######################################################## + * Some properties of SMTPSession data and parameters pass to + * SMTP_Message class accept the pointer to constant char + * i.e. const char*. + * + * You may assign a string literal to that properties or function + * like below example. + * + * session.login.user_domain = "mydomain.net"; + * session.login.user_domain = String("mydomain.net").c_str(); + * + * or + * + * String doman = "mydomain.net"; + * session.login.user_domain = domain.c_str(); + * + * And + * + * String name = "Jack " + String("dawson"); + * String email = "jack_dawson" + String(123) + "@mail.com"; + * + * message.addRecipient(name.c_str(), email.c_str()); + * + * message.addHeader(String("Message-ID: ").c_str()); + * + * or + * + * String header = "Message-ID: "; + * message.addHeader(header.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending plain text Email"; + message.addRecipient("Someone", "####@#####_dot_com"); + + String textMsg = "This is simple plain text message"; + message.text.content = textMsg.c_str(); + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.text.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino b/lib/libesp32/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino rename to lib/libesp32/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino index 83eaa88a4..a1ea2f4fe --- a/lib/lib_div/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino +++ b/lib/libesp32/lib_mail/examples/Send_Text_Flowed/Send_Text_Flowed.ino @@ -1,229 +1,229 @@ - - -/** - * This example will send the Email in plain text version - * with the quoted text and long line text. - * - * - * Created by K. Suwatchai (Mobizt) - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com - * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to - * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc - * and use the app password as password with your yahoo mail account to login. - * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en -*/ -#define SMTP_HOST "################" - -/** The smtp port e.g. - * 25 or esp_mail_smtp_port_25 - * 465 or esp_mail_smtp_port_465 - * 587 or esp_mail_smtp_port_587 -*/ -#define SMTP_PORT 25 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* The SMTP Session object used for Email sending */ -SMTPSession smtp; - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status); - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - smtp.debug(1); - - /* Set the callback function to get the sending results */ - smtp.callback(smtpCallback); - - /* Declare the session config data */ - ESP_Mail_Session session; - - /** ######################################################## - * Some properties of SMTPSession data and parameters pass to - * SMTP_Message class accept the pointer to constant char - * i.e. const char*. - * - * You may assign a string literal to that properties or function - * like below example. - * - * session.login.user_domain = "mydomain.net"; - * session.login.user_domain = String("mydomain.net").c_str(); - * - * or - * - * String doman = "mydomain.net"; - * session.login.user_domain = domain.c_str(); - * - * And - * - * String name = "Jack " + String("dawson"); - * String email = "jack_dawson" + String(123) + "@mail.com"; - * - * message.addRecipient(name.c_str(), email.c_str()); - * - * message.addHeader(String("Message-ID: ").c_str()); - * - * or - * - * String header = "Message-ID: "; - * message.addHeader(header.c_str()); - * - * ########################################################### - */ - - /* Set the session config */ - session.server.host_name = SMTP_HOST; - session.server.port = SMTP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - session.login.user_domain = "mydomain.net"; - - /* Declare the message class */ - SMTP_Message message; - - /* Set the message headers */ - message.sender.name = "ESP Mail"; - message.sender.email = AUTHOR_EMAIL; - message.subject = "Test sending flowed plain text Email"; - message.addRecipient("Someone", "####@#####_dot_com"); - - /** The option to add soft line break to to the message for - * the long text message > 78 characters (rfc 3676) - * Some Servers may not compliant with the standard. - */ - message.text.flowed = true; - - /** if the option message.text.flowed is true, - * the following plain text message will be wrapped. - */ - message.text.content = "The text below is the long quoted text which breaks into several lines.\r\n\r\n>> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\r\n\r\nThis is the normal short text.\r\n\r\nAnother long text, abcdefg hijklmnop qrstuv wxyz abcdefg hijklmnop qrstuv wxyz abcdefg hijklmnop qrstuv wxyz."; - - /** The Plain text message character set e.g. - * us-ascii - * utf-8 - * utf-7 - * The default value is utf-8 - */ - message.text.charSet = "us-ascii"; - - /** The content transfer encoding e.g. - * enc_7bit or "7bit" (not encoded) - * enc_qp or "quoted-printable" (encoded) - * enc_base64 or "base64" (encoded) - * enc_binary or "binary" (not encoded) - * enc_8bit or "8bit" (not encoded) - * The default value is "7bit" - */ - message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - - /** The message priority - * esp_mail_smtp_priority_high or 1 - * esp_mail_smtp_priority_normal or 3 - * esp_mail_smtp_priority_low or 5 - * The default value is esp_mail_smtp_priority_low - */ - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; - - /** The Delivery Status Notifications e.g. - * esp_mail_smtp_notify_never - * esp_mail_smtp_notify_success - * esp_mail_smtp_notify_failure - * esp_mail_smtp_notify_delay - * The default value is esp_mail_smtp_notify_never - */ - message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - - /* Set the custom message header */ - message.addHeader("Message-ID: "); - - /* Connect to server with the session config */ - if (!smtp.connect(&session)) - return; - - /* Start sending Email and close the session */ - if (!MailClient.sendMail(&smtp, &message)) - Serial.println("Error sending Email, " + smtp.errorReason()); -} - -void loop() -{ -} - -/* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Print the sending result */ - if (status.success()) - { - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; - - for (size_t i = 0; i < smtp.sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp.sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); - - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); - } - Serial.println("----------------\n"); - } -} + + +/** + * This example will send the Email in plain text version + * with the quoted text and long line text. + * + * + * Created by K. Suwatchai (Mobizt) + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +//To use send Email for Gmail to port 465 (SSL), less secure app option should be enabled. https://myaccount.google.com/lesssecureapps?pli=1 + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/** The smtp host name e.g. smtp.gmail.com for GMail or smtp.office365.com for Outlook or smtp.mail.yahoo.com + * For yahoo mail, log in to your yahoo mail in web browser and generate app password by go to + * https://login.yahoo.com/account/security/app-passwords/add/confirm?src=noSrc + * and use the app password as password with your yahoo mail account to login. + * The google app password signin is also available https://support.google.com/mail/answer/185833?hl=en +*/ +#define SMTP_HOST "################" + +/** The smtp port e.g. + * 25 or esp_mail_smtp_port_25 + * 465 or esp_mail_smtp_port_465 + * 587 or esp_mail_smtp_port_587 +*/ +#define SMTP_PORT 25 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* The SMTP Session object used for Email sending */ +SMTPSession smtp; + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status); + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + smtp.debug(1); + + /* Set the callback function to get the sending results */ + smtp.callback(smtpCallback); + + /* Declare the session config data */ + ESP_Mail_Session session; + + /** ######################################################## + * Some properties of SMTPSession data and parameters pass to + * SMTP_Message class accept the pointer to constant char + * i.e. const char*. + * + * You may assign a string literal to that properties or function + * like below example. + * + * session.login.user_domain = "mydomain.net"; + * session.login.user_domain = String("mydomain.net").c_str(); + * + * or + * + * String doman = "mydomain.net"; + * session.login.user_domain = domain.c_str(); + * + * And + * + * String name = "Jack " + String("dawson"); + * String email = "jack_dawson" + String(123) + "@mail.com"; + * + * message.addRecipient(name.c_str(), email.c_str()); + * + * message.addHeader(String("Message-ID: ").c_str()); + * + * or + * + * String header = "Message-ID: "; + * message.addHeader(header.c_str()); + * + * ########################################################### + */ + + /* Set the session config */ + session.server.host_name = SMTP_HOST; + session.server.port = SMTP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + session.login.user_domain = "mydomain.net"; + + /* Declare the message class */ + SMTP_Message message; + + /* Set the message headers */ + message.sender.name = "ESP Mail"; + message.sender.email = AUTHOR_EMAIL; + message.subject = "Test sending flowed plain text Email"; + message.addRecipient("Someone", "####@#####_dot_com"); + + /** The option to add soft line break to to the message for + * the long text message > 78 characters (rfc 3676) + * Some Servers may not compliant with the standard. + */ + message.text.flowed = true; + + /** if the option message.text.flowed is true, + * the following plain text message will be wrapped. + */ + message.text.content = "The text below is the long quoted text which breaks into several lines.\r\n\r\n>> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\r\n\r\nThis is the normal short text.\r\n\r\nAnother long text, abcdefg hijklmnop qrstuv wxyz abcdefg hijklmnop qrstuv wxyz abcdefg hijklmnop qrstuv wxyz."; + + /** The Plain text message character set e.g. + * us-ascii + * utf-8 + * utf-7 + * The default value is utf-8 + */ + message.text.charSet = "us-ascii"; + + /** The content transfer encoding e.g. + * enc_7bit or "7bit" (not encoded) + * enc_qp or "quoted-printable" (encoded) + * enc_base64 or "base64" (encoded) + * enc_binary or "binary" (not encoded) + * enc_8bit or "8bit" (not encoded) + * The default value is "7bit" + */ + message.text.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + + /** The message priority + * esp_mail_smtp_priority_high or 1 + * esp_mail_smtp_priority_normal or 3 + * esp_mail_smtp_priority_low or 5 + * The default value is esp_mail_smtp_priority_low + */ + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_low; + + /** The Delivery Status Notifications e.g. + * esp_mail_smtp_notify_never + * esp_mail_smtp_notify_success + * esp_mail_smtp_notify_failure + * esp_mail_smtp_notify_delay + * The default value is esp_mail_smtp_notify_never + */ + message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + + /* Set the custom message header */ + message.addHeader("Message-ID: "); + + /* Connect to server with the session config */ + if (!smtp.connect(&session)) + return; + + /* Start sending Email and close the session */ + if (!MailClient.sendMail(&smtp, &message)) + Serial.println("Error sending Email, " + smtp.errorReason()); +} + +void loop() +{ +} + +/* Callback function to get the Email sending status */ +void smtpCallback(SMTP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Print the sending result */ + if (status.success()) + { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; + + for (size_t i = 0; i < smtp.sendingResult.size(); i++) + { + /* Get the result item */ + SMTP_Result result = smtp.sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); + + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); + } +} diff --git a/lib/lib_div/lib_mail/examples/Set_Flags/Set_Flags.ino b/lib/libesp32/lib_mail/examples/Set_Flags/Set_Flags.ino old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/examples/Set_Flags/Set_Flags.ino rename to lib/libesp32/lib_mail/examples/Set_Flags/Set_Flags.ino index 04ce2b3fe..238a17ddf --- a/lib/lib_div/lib_mail/examples/Set_Flags/Set_Flags.ino +++ b/lib/libesp32/lib_mail/examples/Set_Flags/Set_Flags.ino @@ -1,363 +1,363 @@ -/** - * This example will set the argument to the flags and read the message. - * - * Email: suwatchai@outlook.com - * - * Github: https://github.com/mobizt/ESP-Mail-Client - * - * Copyright (c) 2021 mobizt - * -*/ - -/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en - * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha - * -*/ - -/** For ESP8266, with BearSSL WiFi Client - * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which - * may cause your device out of memory reset in case the memory - * allocation error. -*/ - -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#endif -#include - -#define WIFI_SSID "################" -#define WIFI_PASSWORD "################" - -/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ -#define IMAP_HOST "################" - -/** The imap port e.g. - * 143 or esp_mail_imap_port_143 - * 993 or esp_mail_imap_port_993 -*/ -#define IMAP_PORT 993 - -/* The log in credentials */ -#define AUTHOR_EMAIL "################" -#define AUTHOR_PASSWORD "################" - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status); - -/* Print the list of mailbox folders */ -void printAllMailboxesInfo(IMAPSession &imap); - -/* Print the selected folder info */ -void printSelectedMailboxInfo(IMAPSession &imap); - -/* Print all messages from the message list */ -void printMessages(IMAPSession &imap); - -/* Print all rfc822 messages included in the message */ -void printRFC822Messages(IMAP_MSG_Item &msg); - -/* Print all attachments info from the message */ -void printAttacements(IMAP_MSG_Item &msg); - -/* The IMAP Session object used for Email reading */ -IMAPSession imap; - - -void setup() -{ - - Serial.begin(115200); - Serial.println(); - - Serial.print("Connecting to AP"); - - WiFi.begin(WIFI_SSID, WIFI_PASSWORD); - while (WiFi.status() != WL_CONNECTED) - { - Serial.print("."); - delay(200); - } - - Serial.println(""); - Serial.println("WiFi connected."); - Serial.println("IP address: "); - Serial.println(WiFi.localIP()); - Serial.println(); - - /** Enable the debug via Serial port - * none debug or 0 - * basic debug or 1 - */ - imap.debug(1); - - /* Set the callback function to get the reading results */ - imap.callback(imapCallback); - - /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from - * MailClient.sdBegin function which may be different for ESP32 and ESP8266 - * For ESP32, assign all of SPI pins - * MailClient.sdBegin(14,2,15,13) - * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 - * And for ESP8266, assign the CS pins of SPI port - * MailClient.sdBegin(15) - * Which pin 15 is the CS pin of SD card adapter - */ - - /* Declare the session config data */ - ESP_Mail_Session session; - - /* Set the session config */ - session.server.host_name = IMAP_HOST; - session.server.port = IMAP_PORT; - session.login.email = AUTHOR_EMAIL; - session.login.password = AUTHOR_PASSWORD; - - - /* Setup the configuration for searching or fetching operation and its result */ - IMAP_Config config; - - /* Message UID to fetch or read e.g. 100 */ - config.fetch.uid = "100"; - - /* Set seen flag */ - //config.fetch.set_seen = true; - - /* Search criteria */ - config.search.criteria = ""; - - /* Also search the unseen message */ - config.search.unseen_msg = true; - - /* Set the storage to save the downloaded files and attachments */ - config.storage.saved_path = "/email_data"; - - /** The file storage type e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - config.storage.type = esp_mail_file_storage_type_flash; - - /** Set to download heades, text and html messaeges, - * attachments and inline images respectively. - */ - config.download.header = true; - config.download.text = true; - config.download.html = true; - config.download.attachment = true; - config.download.inlineImg = true; - - /** Set to enable the results i.e. html and text messaeges - * which the content stored in the IMAPSession object is limited - * by the option config.limit.msg_size. - * The whole message can be download through config.download.text - * or config.download.html which not depends on these enable options. - */ - config.enable.html = true; - config.enable.text = true; - - /* Set to enable the sort the result by message UID in the ascending order */ - config.enable.recent_sort = true; - - /* Set to report the download progress via the default serial port */ - config.enable.download_status = true; - - /* Set the limit of number of messages in the search results */ - config.limit.search = 5; - - /** Set the maximum size of message stored in - * IMAPSession object in byte - */ - config.limit.msg_size = 512; - - /** Set the maximum attachments and inline images files size - * that can be downloaded in byte. - * The file which its size is largger than this limit may be saved - * as truncated file. - */ - config.limit.attachment_size = 1024 * 1024 * 5; - - - - /* Connect to server with the session and config */ - if (!imap.connect(&session, &config)) - return; - - /* {Optional] */ - printAllMailboxesInfo(imap); - - /* Open or select the mailbox folder to read or search the message */ - if (!imap.selectFolder("INBOX")) - return; - - /* {Optional] */ - printSelectedMailboxInfo(imap); - - /** Set \Seen and \Answered to flags for message with UID 100 - * The seesion will keep open. - */ - if (MailClient.setFlag(&imap, 100, "\\Seen \\Answered", false)) - Serial.println("Setting FLAG success"); - else - Serial.println("Error, setting FLAG"); - - /* Add \Seen and \Answered to flags for message with UID 100 */ - //MailClient.addFlag(imap, 100, "\\Seen \\Answered", false); - - /* Remove \Seen and \Answered from flags for message with UID 100 */ - //MailClient.removeFlag(imap, 100, "\\Seen \\Answered", false); - - /* Read or search the Email and close the session */ - MailClient.readMail(&imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); -} - -void loop() -{ - -} - -/* Callback function to get the Email reading status */ -void imapCallback(IMAP_Status status) -{ - /* Print the current status */ - Serial.println(status.info()); - - /* Show the result when reading finished */ - if (status.success()) - { - /* Print the result */ - printMessages(imap); - - /* Clear all stored data in IMAPSession object */ - imap.empty(); - Serial.printf("Free Heap: %d", ESP.getFreeHeap()); - } -} - -void printAllMailboxesInfo(IMAPSession &imap) -{ - /* Declare the folder collection class to get the list of mailbox folders */ - FoldersCollection folders; - - /* Get the mailbox folders */ - if (imap.getFolders(folders)) - { - for (size_t i = 0; i < folders.size(); i++) - { - /* Iterate each folder info using the folder info item data */ - FolderInfo folderInfo = folders.info(i); - Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); - } - } -} - -void printSelectedMailboxInfo(IMAPSession &imap) -{ - /* Declare the selected folder info class to get the info of selected mailbox folder */ - SelectedFolderInfo sFolder = imap.selectedFolder(); - - /* Show the mailbox info */ - Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); - Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); - for (size_t i = 0; i < sFolder.flagCount(); i++) - Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); -} - -void printRFC822Messages(IMAP_MSG_Item &msg) -{ - Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); - for (size_t j = 0; j < msg.rfc822.size(); j++) - { - IMAP_MSG_Item rfc822 = msg.rfc822[j]; - Serial.printf("%d. \n", j + 1); - Serial.printf("Messsage ID: %s\n", rfc822.messageID); - Serial.printf("From: %s\n", rfc822.from); - Serial.printf("Sender: %s\n", rfc822.sender); - Serial.printf("To: %s\n", rfc822.to); - Serial.printf("CC: %s\n", rfc822.cc); - Serial.printf("Subject: %s\n", rfc822.subject); - Serial.printf("Date: %s\n", rfc822.date); - Serial.printf("Reply-To: %s\n", rfc822.reply_to); - Serial.printf("Return-Path: %s\n", rfc822.return_path); - Serial.printf("Comment: %s\n", rfc822.comment); - Serial.printf("Keyword: %s\n", rfc822.keyword); - Serial.printf("Text Message: %s\n", rfc822.text.content); - Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", rfc822.html.content); - Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); - - if (rfc822.attachments.size() > 0) - printAttacements(rfc822); - } -} - -void printAttacements(IMAP_MSG_Item &msg) -{ - Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); - for (size_t j = 0; j < msg.attachments.size(); j++) - { - IMAP_Attach_Item att = msg.attachments[j]; - /** att.type can be - * esp_mail_att_type_none or 0 - * esp_mail_att_type_attachment or 1 - * esp_mail_att_type_inline or 2 - */ - Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); - } - Serial.println(); -} - -void printMessages(IMAPSession &imap) -{ - /* Get the message list from the message list data */ - IMAP_MSG_List msgList = imap.data(); - - for (size_t i = 0; i < msgList.msgItems.size(); i++) - { - /* Iterate to get each message data through the message item data */ - IMAP_MSG_Item msg = msgList.msgItems[i]; - - Serial.println("################################"); - Serial.printf("Messsage Number: %s\n", msg.msgNo); - Serial.printf("Messsage UID: %s\n", msg.UID); - Serial.printf("Messsage ID: %s\n", msg.ID); - Serial.printf("Accept Language: %s\n", msg.acceptLang); - Serial.printf("Content Language: %s\n", msg.contentLang); - Serial.printf("From: %s\n", msg.from); - Serial.printf("From Charset: %s\n", msg.fromCharset); - Serial.printf("To: %s\n", msg.to); - Serial.printf("To Charset: %s\n", msg.toCharset); - Serial.printf("CC: %s\n", msg.cc); - Serial.printf("CC Charset: %s\n", msg.ccCharset); - Serial.printf("Date: %s\n", msg.date); - Serial.printf("Subject: %s\n", msg.subject); - Serial.printf("Subject Charset: %s\n", msg.subjectCharset); - - /* If the result contains the message info (Fetch mode) */ - if (!imap.headerOnly()) - { - Serial.printf("Text Message: %s\n", msg.text.content); - Serial.printf("Text Message Charset: %s\n", msg.text.charSet); - Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); - Serial.printf("HTML Message: %s\n", msg.html.content); - Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); - Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); - - if (msg.attachments.size() > 0) - printAttacements(msg); - - if (msg.rfc822.size() > 0) - printRFC822Messages(msg); - } - - Serial.println(); - } -} +/** + * This example will set the argument to the flags and read the message. + * + * Email: suwatchai@outlook.com + * + * Github: https://github.com/mobizt/ESP-Mail-Client + * + * Copyright (c) 2021 mobizt + * +*/ + +/** To receive Email using Gmail, IMAP option should be enabled. https://support.google.com/mail/answer/7126229?hl=en + * and also https://accounts.google.com/b/0/DisplayUnlockCaptcha + * +*/ + +/** For ESP8266, with BearSSL WiFi Client + * The memory reserved for completed valid SSL response from IMAP is 16 kbytes which + * may cause your device out of memory reset in case the memory + * allocation error. +*/ + +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#endif +#include + +#define WIFI_SSID "################" +#define WIFI_PASSWORD "################" + +/* The imap host name e.g. imap.gmail.com for GMail or outlook.office365.com for Outlook */ +#define IMAP_HOST "################" + +/** The imap port e.g. + * 143 or esp_mail_imap_port_143 + * 993 or esp_mail_imap_port_993 +*/ +#define IMAP_PORT 993 + +/* The log in credentials */ +#define AUTHOR_EMAIL "################" +#define AUTHOR_PASSWORD "################" + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status); + +/* Print the list of mailbox folders */ +void printAllMailboxesInfo(IMAPSession &imap); + +/* Print the selected folder info */ +void printSelectedMailboxInfo(IMAPSession &imap); + +/* Print all messages from the message list */ +void printMessages(IMAPSession &imap); + +/* Print all rfc822 messages included in the message */ +void printRFC822Messages(IMAP_MSG_Item &msg); + +/* Print all attachments info from the message */ +void printAttacements(IMAP_MSG_Item &msg); + +/* The IMAP Session object used for Email reading */ +IMAPSession imap; + + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + + Serial.print("Connecting to AP"); + + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + while (WiFi.status() != WL_CONNECTED) + { + Serial.print("."); + delay(200); + } + + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + Serial.println(); + + /** Enable the debug via Serial port + * none debug or 0 + * basic debug or 1 + */ + imap.debug(1); + + /* Set the callback function to get the reading results */ + imap.callback(imapCallback); + + /** In case the SD card/adapter was used for the file storagge, the SPI pins can be configure from + * MailClient.sdBegin function which may be different for ESP32 and ESP8266 + * For ESP32, assign all of SPI pins + * MailClient.sdBegin(14,2,15,13) + * Which SCK = 14, MISO = 2, MOSI = 15 and SS = 13 + * And for ESP8266, assign the CS pins of SPI port + * MailClient.sdBegin(15) + * Which pin 15 is the CS pin of SD card adapter + */ + + /* Declare the session config data */ + ESP_Mail_Session session; + + /* Set the session config */ + session.server.host_name = IMAP_HOST; + session.server.port = IMAP_PORT; + session.login.email = AUTHOR_EMAIL; + session.login.password = AUTHOR_PASSWORD; + + + /* Setup the configuration for searching or fetching operation and its result */ + IMAP_Config config; + + /* Message UID to fetch or read e.g. 100 */ + config.fetch.uid = "100"; + + /* Set seen flag */ + //config.fetch.set_seen = true; + + /* Search criteria */ + config.search.criteria = ""; + + /* Also search the unseen message */ + config.search.unseen_msg = true; + + /* Set the storage to save the downloaded files and attachments */ + config.storage.saved_path = "/email_data"; + + /** The file storage type e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + config.storage.type = esp_mail_file_storage_type_flash; + + /** Set to download heades, text and html messaeges, + * attachments and inline images respectively. + */ + config.download.header = true; + config.download.text = true; + config.download.html = true; + config.download.attachment = true; + config.download.inlineImg = true; + + /** Set to enable the results i.e. html and text messaeges + * which the content stored in the IMAPSession object is limited + * by the option config.limit.msg_size. + * The whole message can be download through config.download.text + * or config.download.html which not depends on these enable options. + */ + config.enable.html = true; + config.enable.text = true; + + /* Set to enable the sort the result by message UID in the ascending order */ + config.enable.recent_sort = true; + + /* Set to report the download progress via the default serial port */ + config.enable.download_status = true; + + /* Set the limit of number of messages in the search results */ + config.limit.search = 5; + + /** Set the maximum size of message stored in + * IMAPSession object in byte + */ + config.limit.msg_size = 512; + + /** Set the maximum attachments and inline images files size + * that can be downloaded in byte. + * The file which its size is largger than this limit may be saved + * as truncated file. + */ + config.limit.attachment_size = 1024 * 1024 * 5; + + + + /* Connect to server with the session and config */ + if (!imap.connect(&session, &config)) + return; + + /* {Optional] */ + printAllMailboxesInfo(imap); + + /* Open or select the mailbox folder to read or search the message */ + if (!imap.selectFolder("INBOX")) + return; + + /* {Optional] */ + printSelectedMailboxInfo(imap); + + /** Set \Seen and \Answered to flags for message with UID 100 + * The seesion will keep open. + */ + if (MailClient.setFlag(&imap, 100, "\\Seen \\Answered", false)) + Serial.println("Setting FLAG success"); + else + Serial.println("Error, setting FLAG"); + + /* Add \Seen and \Answered to flags for message with UID 100 */ + //MailClient.addFlag(imap, 100, "\\Seen \\Answered", false); + + /* Remove \Seen and \Answered from flags for message with UID 100 */ + //MailClient.removeFlag(imap, 100, "\\Seen \\Answered", false); + + /* Read or search the Email and close the session */ + MailClient.readMail(&imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); +} + +void loop() +{ + +} + +/* Callback function to get the Email reading status */ +void imapCallback(IMAP_Status status) +{ + /* Print the current status */ + Serial.println(status.info()); + + /* Show the result when reading finished */ + if (status.success()) + { + /* Print the result */ + printMessages(imap); + + /* Clear all stored data in IMAPSession object */ + imap.empty(); + Serial.printf("Free Heap: %d", ESP.getFreeHeap()); + } +} + +void printAllMailboxesInfo(IMAPSession &imap) +{ + /* Declare the folder collection class to get the list of mailbox folders */ + FoldersCollection folders; + + /* Get the mailbox folders */ + if (imap.getFolders(folders)) + { + for (size_t i = 0; i < folders.size(); i++) + { + /* Iterate each folder info using the folder info item data */ + FolderInfo folderInfo = folders.info(i); + Serial.printf("%s%s%s", i == 0 ? "\nAvailable folders: " : ", ", folderInfo.name, i == folders.size() - 1 ? "\n" : ""); + } + } +} + +void printSelectedMailboxInfo(IMAPSession &imap) +{ + /* Declare the selected folder info class to get the info of selected mailbox folder */ + SelectedFolderInfo sFolder = imap.selectedFolder(); + + /* Show the mailbox info */ + Serial.printf("\nInfo of the selected folder\nTotal Messages: %d\n", sFolder.msgCount()); + Serial.printf("Predicted next UID: %d\n", sFolder.nextUID()); + for (size_t i = 0; i < sFolder.flagCount(); i++) + Serial.printf("%s%s%s", i == 0 ? "Flags: " : ", ", sFolder.flag(i).c_str(), i == sFolder.flagCount() - 1 ? "\n" : ""); +} + +void printRFC822Messages(IMAP_MSG_Item &msg) +{ + Serial.printf("RFC822 Messages: %d message(s)\n****************************\n", msg.rfc822.size()); + for (size_t j = 0; j < msg.rfc822.size(); j++) + { + IMAP_MSG_Item rfc822 = msg.rfc822[j]; + Serial.printf("%d. \n", j + 1); + Serial.printf("Messsage ID: %s\n", rfc822.messageID); + Serial.printf("From: %s\n", rfc822.from); + Serial.printf("Sender: %s\n", rfc822.sender); + Serial.printf("To: %s\n", rfc822.to); + Serial.printf("CC: %s\n", rfc822.cc); + Serial.printf("Subject: %s\n", rfc822.subject); + Serial.printf("Date: %s\n", rfc822.date); + Serial.printf("Reply-To: %s\n", rfc822.reply_to); + Serial.printf("Return-Path: %s\n", rfc822.return_path); + Serial.printf("Comment: %s\n", rfc822.comment); + Serial.printf("Keyword: %s\n", rfc822.keyword); + Serial.printf("Text Message: %s\n", rfc822.text.content); + Serial.printf("Text Message Charset: %s\n", rfc822.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", rfc822.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", rfc822.html.content); + Serial.printf("HTML Message Charset: %s\n", rfc822.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", rfc822.html.transfer_encoding); + + if (rfc822.attachments.size() > 0) + printAttacements(rfc822); + } +} + +void printAttacements(IMAP_MSG_Item &msg) +{ + Serial.printf("Attachment: %d file(s)\n****************************\n", msg.attachments.size()); + for (size_t j = 0; j < msg.attachments.size(); j++) + { + IMAP_Attach_Item att = msg.attachments[j]; + /** att.type can be + * esp_mail_att_type_none or 0 + * esp_mail_att_type_attachment or 1 + * esp_mail_att_type_inline or 2 + */ + Serial.printf("%d. Filename: %s, Name: %s, Size: %d, MIME: %s, Type: %s, Creation Date: %s\n", j + 1, att.filename, att.name, att.size, att.mime, att.type == esp_mail_att_type_attachment ? "attachment" : "inline", att.creationDate); + } + Serial.println(); +} + +void printMessages(IMAPSession &imap) +{ + /* Get the message list from the message list data */ + IMAP_MSG_List msgList = imap.data(); + + for (size_t i = 0; i < msgList.msgItems.size(); i++) + { + /* Iterate to get each message data through the message item data */ + IMAP_MSG_Item msg = msgList.msgItems[i]; + + Serial.println("################################"); + Serial.printf("Messsage Number: %s\n", msg.msgNo); + Serial.printf("Messsage UID: %s\n", msg.UID); + Serial.printf("Messsage ID: %s\n", msg.ID); + Serial.printf("Accept Language: %s\n", msg.acceptLang); + Serial.printf("Content Language: %s\n", msg.contentLang); + Serial.printf("From: %s\n", msg.from); + Serial.printf("From Charset: %s\n", msg.fromCharset); + Serial.printf("To: %s\n", msg.to); + Serial.printf("To Charset: %s\n", msg.toCharset); + Serial.printf("CC: %s\n", msg.cc); + Serial.printf("CC Charset: %s\n", msg.ccCharset); + Serial.printf("Date: %s\n", msg.date); + Serial.printf("Subject: %s\n", msg.subject); + Serial.printf("Subject Charset: %s\n", msg.subjectCharset); + + /* If the result contains the message info (Fetch mode) */ + if (!imap.headerOnly()) + { + Serial.printf("Text Message: %s\n", msg.text.content); + Serial.printf("Text Message Charset: %s\n", msg.text.charSet); + Serial.printf("Text Message Transfer Encoding: %s\n", msg.text.transfer_encoding); + Serial.printf("HTML Message: %s\n", msg.html.content); + Serial.printf("HTML Message Charset: %s\n", msg.html.charSet); + Serial.printf("HTML Message Transfer Encoding: %s\n\n", msg.html.transfer_encoding); + + if (msg.attachments.size() > 0) + printAttacements(msg); + + if (msg.rfc822.size() > 0) + printRFC822Messages(msg); + } + + Serial.println(); + } +} diff --git a/lib/lib_div/lib_mail/keywords.txt b/lib/libesp32/lib_mail/keywords.txt old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/keywords.txt rename to lib/libesp32/lib_mail/keywords.txt index eccf383ae..8e669d92c --- a/lib/lib_div/lib_mail/keywords.txt +++ b/lib/libesp32/lib_mail/keywords.txt @@ -1,127 +1,127 @@ -###################################### -# Syntax Coloring Map ESP Mail Client -###################################### - -####################################### -# Classes and Structured Type (KEYWORD1) -####################################### - -MailClient KEYWORD1 -IMAPSession KEYWORD1 -SMTPSession KEYWORD1 -SMTP_Message KEYWORD1 -IMAP_Config KEYWORD1 -FoldersCollection KEYWORD1 -imapStatusCallback KEYWORD1 -IMAP_MSG_List KEYWORD1 -SelectedFolderInfo KEYWORD1 -ESP_Mail_Session KEYWORD1 -smtpStatusCallback KEYWORD1 -SMTP_Attachment KEYWORD1 -SMTP_Result KEYWORD1 -IMAP_MSG_Item KEYWORD1 -Content_Transfer_Encoding KEYWORD1 -MessageList KEYWORD1 - -############################################### -# Methods and Functions (KEYWORD2) -############################################### - -sendMail KEYWORD2 -readMail KEYWORD2 -setFlag KEYWORD2 -addFlag KEYWORD2 -removeFlag KEYWORD2 -sdBegin KEYWORD2 -sdMMCBegin KEYWORD2 -connect KEYWORD2 -closeSession KEYWORD2 -debug KEYWORD2 -getFolders KEYWORD2 -selectFolder KEYWORD2 -openFolder KEYWORD2 -closeFolder KEYWORD2 -callback KEYWORD2 -headerOnly KEYWORD2 -data KEYWORD2 -selectedFolder KEYWORD2 -errorReason KEYWORD2 -empty KEYWORD2 -resetAttachItem KEYWORD2 -clear KEYWORD2 -clearInlineimages KEYWORD2 -clearAttachments KEYWORD2 -clearRFC822Messages KEYWORD2 -clearRecipients KEYWORD2 -clearCc KEYWORD2 -clearBcc KEYWORD2 -clearHeader KEYWORD2 -addAttachment KEYWORD2 -addParallelAttachment KEYWORD2 -addInlineImage KEYWORD2 -addMessage KEYWORD2 -addRecipient KEYWORD2 -addCc KEYWORD2 -addBcc KEYWORD2 -addHeader KEYWORD2 -info KEYWORD2 -successs KEYWORD2 -completedCount KEYWORD2 -failedCount KEYWORD2 -getItem KEYWORD2 -size KEYWORD2 -flagCount KEYWORD2 -msgCount KEYWORD2 -nextUID KEYWORD2 -searchCount KEYWORD2 -availableMessages KEYWORD2 -flag KEYWORD2 -setClock KEYWORD2 -getUnixTime KEYWORD2 -getTimestamp KEYWORD2 -getYear KEYWORD2 -getMonth KEYWORD2 -getDay KEYWORD2 -getDayOfWeek KEYWORD2 -getDayOfWeekString KEYWORD2 -getHour KEYWORD2 -getMin KEYWORD2 -getSecond KEYWORD2 -getNumberOfDayThisYear KEYWORD2 -getTotalDays KEYWORD2 -dayofWeek KEYWORD2 -getCurrentSecond KEYWORD2 -getCurrentTimestamp KEYWORD2 -getTimeFromSec KEYWORD2 -getDateTimeString KEYWORD2 -copyMessages KEYWORD2 -deleteMessages KEYWORD2 -createFolder KEYWORD2 -deleteFolder KEYWORD2 - - -####################################### -# Struct (KEYWORD3) -####################################### - -esp_mail_email_info_t KEYWORD3 -esp_mail_plain_body_t KEYWORD3 -esp_mail_html_body_t KEYWORD3 -esp_mail_smtp_msg_response_t KEYWORD3 -esp_mail_smtp_enable_option_t KEYWORD3 -esp_mail_email_info_t KEYWORD3 -esp_mail_folder_info_item_t KEYWORD3 -esp_mail_sesson_sever_config_t KEYWORD3 -esp_mail_sesson_login_config_t KEYWORD3 -esp_mail_sesson_secure_config_t KEYWORD3 -esp_mail_sesson_cert_config_t KEYWORD3 -esp_mail_imap_fetch_config_t KEYWORD3 -esp_mail_imap_search_config_t KEYWORD3 -esp_mail_imap_limit_config_t KEYWORD3 -esp_mail_imap_enable_config_t KEYWORD3 -esp_mail_imap_download_config_t KEYWORD3 -esp_mail_imap_storage_config_t KEYWORD3 - -esp_mail_file_storage_type_none KEYWORD3 -esp_mail_file_storage_type_flash KEYWORD3 +###################################### +# Syntax Coloring Map ESP Mail Client +###################################### + +####################################### +# Classes and Structured Type (KEYWORD1) +####################################### + +MailClient KEYWORD1 +IMAPSession KEYWORD1 +SMTPSession KEYWORD1 +SMTP_Message KEYWORD1 +IMAP_Config KEYWORD1 +FoldersCollection KEYWORD1 +imapStatusCallback KEYWORD1 +IMAP_MSG_List KEYWORD1 +SelectedFolderInfo KEYWORD1 +ESP_Mail_Session KEYWORD1 +smtpStatusCallback KEYWORD1 +SMTP_Attachment KEYWORD1 +SMTP_Result KEYWORD1 +IMAP_MSG_Item KEYWORD1 +Content_Transfer_Encoding KEYWORD1 +MessageList KEYWORD1 + +############################################### +# Methods and Functions (KEYWORD2) +############################################### + +sendMail KEYWORD2 +readMail KEYWORD2 +setFlag KEYWORD2 +addFlag KEYWORD2 +removeFlag KEYWORD2 +sdBegin KEYWORD2 +sdMMCBegin KEYWORD2 +connect KEYWORD2 +closeSession KEYWORD2 +debug KEYWORD2 +getFolders KEYWORD2 +selectFolder KEYWORD2 +openFolder KEYWORD2 +closeFolder KEYWORD2 +callback KEYWORD2 +headerOnly KEYWORD2 +data KEYWORD2 +selectedFolder KEYWORD2 +errorReason KEYWORD2 +empty KEYWORD2 +resetAttachItem KEYWORD2 +clear KEYWORD2 +clearInlineimages KEYWORD2 +clearAttachments KEYWORD2 +clearRFC822Messages KEYWORD2 +clearRecipients KEYWORD2 +clearCc KEYWORD2 +clearBcc KEYWORD2 +clearHeader KEYWORD2 +addAttachment KEYWORD2 +addParallelAttachment KEYWORD2 +addInlineImage KEYWORD2 +addMessage KEYWORD2 +addRecipient KEYWORD2 +addCc KEYWORD2 +addBcc KEYWORD2 +addHeader KEYWORD2 +info KEYWORD2 +successs KEYWORD2 +completedCount KEYWORD2 +failedCount KEYWORD2 +getItem KEYWORD2 +size KEYWORD2 +flagCount KEYWORD2 +msgCount KEYWORD2 +nextUID KEYWORD2 +searchCount KEYWORD2 +availableMessages KEYWORD2 +flag KEYWORD2 +setClock KEYWORD2 +getUnixTime KEYWORD2 +getTimestamp KEYWORD2 +getYear KEYWORD2 +getMonth KEYWORD2 +getDay KEYWORD2 +getDayOfWeek KEYWORD2 +getDayOfWeekString KEYWORD2 +getHour KEYWORD2 +getMin KEYWORD2 +getSecond KEYWORD2 +getNumberOfDayThisYear KEYWORD2 +getTotalDays KEYWORD2 +dayofWeek KEYWORD2 +getCurrentSecond KEYWORD2 +getCurrentTimestamp KEYWORD2 +getTimeFromSec KEYWORD2 +getDateTimeString KEYWORD2 +copyMessages KEYWORD2 +deleteMessages KEYWORD2 +createFolder KEYWORD2 +deleteFolder KEYWORD2 + + +####################################### +# Struct (KEYWORD3) +####################################### + +esp_mail_email_info_t KEYWORD3 +esp_mail_plain_body_t KEYWORD3 +esp_mail_html_body_t KEYWORD3 +esp_mail_smtp_msg_response_t KEYWORD3 +esp_mail_smtp_enable_option_t KEYWORD3 +esp_mail_email_info_t KEYWORD3 +esp_mail_folder_info_item_t KEYWORD3 +esp_mail_sesson_sever_config_t KEYWORD3 +esp_mail_sesson_login_config_t KEYWORD3 +esp_mail_sesson_secure_config_t KEYWORD3 +esp_mail_sesson_cert_config_t KEYWORD3 +esp_mail_imap_fetch_config_t KEYWORD3 +esp_mail_imap_search_config_t KEYWORD3 +esp_mail_imap_limit_config_t KEYWORD3 +esp_mail_imap_enable_config_t KEYWORD3 +esp_mail_imap_download_config_t KEYWORD3 +esp_mail_imap_storage_config_t KEYWORD3 + +esp_mail_file_storage_type_none KEYWORD3 +esp_mail_file_storage_type_flash KEYWORD3 esp_mail_file_storage_type_sd KEYWORD3 \ No newline at end of file diff --git a/lib/lib_div/lib_mail/library.json b/lib/libesp32/lib_mail/library.json old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/library.json rename to lib/libesp32/lib_mail/library.json index 870d4f993..30dac17bb --- a/lib/lib_div/lib_mail/library.json +++ b/lib/libesp32/lib_mail/library.json @@ -1,16 +1,16 @@ -{ - "name": "ESP Mail Client", - "version": "1.2.0", - "keywords": "communication, email, imap, smtp, esp32, esp8266, arduino", - "description": "Mail Client Arduino Library for Espressif ESP32 and ESP8266 devices. This library allows the ESP32 and ESP8266 devices to send and read Email with the many options and features e.g. the attachments, inline images, embeded rfc822 messages are supported for upload when sending and download when reading the Emails.", - "repository": { - "type": "git", - "url": "https://github.com/mobizt/ESP-Mail-Client.git" - }, - "authors": [{ - "name": "Mobizt", - "email": "suwatchai@outlook.com" - }], - "frameworks": "arduino", - "platforms": "espressif32, espressif8266" -} +{ + "name": "ESP Mail Client", + "version": "1.2.0", + "keywords": "communication, email, imap, smtp, esp32, esp8266, arduino", + "description": "Mail Client Arduino Library for Espressif ESP32 and ESP8266 devices. This library allows the ESP32 and ESP8266 devices to send and read Email with the many options and features e.g. the attachments, inline images, embeded rfc822 messages are supported for upload when sending and download when reading the Emails.", + "repository": { + "type": "git", + "url": "https://github.com/mobizt/ESP-Mail-Client.git" + }, + "authors": [{ + "name": "Mobizt", + "email": "suwatchai@outlook.com" + }], + "frameworks": "arduino", + "platforms": "espressif32, espressif8266" +} diff --git a/lib/lib_div/lib_mail/library.properties b/lib/libesp32/lib_mail/library.properties old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/library.properties rename to lib/libesp32/lib_mail/library.properties index 572b4998a..a574456bd --- a/lib/lib_div/lib_mail/library.properties +++ b/lib/libesp32/lib_mail/library.properties @@ -1,17 +1,17 @@ -name=ESP Mail Client - -version=1.2.0 - -author=Mobizt - -maintainer=Mobizt - -sentence=Mail Client Arduino Library for Espressif ESP32 and ESP8266 devices. - -paragraph=This library allows the ESP32 and ESP8266 devices to send and read Email with the many options and features e.g. the attachments, inline images, embeded rfc822 messages are supported for upload when sending and download when reading the Emails. - -category=Communication - -url=https://github.com/mobizt/ESP-Mail-Client - -architectures=esp32,esp8266 +name=ESP Mail Client + +version=1.2.0 + +author=Mobizt + +maintainer=Mobizt + +sentence=Mail Client Arduino Library for Espressif ESP32 and ESP8266 devices. + +paragraph=This library allows the ESP32 and ESP8266 devices to send and read Email with the many options and features e.g. the attachments, inline images, embeded rfc822 messages are supported for upload when sending and download when reading the Emails. + +category=Communication + +url=https://github.com/mobizt/ESP-Mail-Client + +architectures=esp32,esp8266 diff --git a/lib/lib_div/lib_mail/media/images/ArduinoIDE.png b/lib/libesp32/lib_mail/media/images/ArduinoIDE.png similarity index 100% rename from lib/lib_div/lib_mail/media/images/ArduinoIDE.png rename to lib/libesp32/lib_mail/media/images/ArduinoIDE.png diff --git a/lib/lib_div/lib_mail/media/images/esp-mail-client.png b/lib/libesp32/lib_mail/media/images/esp-mail-client.png old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/media/images/esp-mail-client.png rename to lib/libesp32/lib_mail/media/images/esp-mail-client.png diff --git a/lib/lib_div/lib_mail/media/images/esp-mail-client.svg b/lib/libesp32/lib_mail/media/images/esp-mail-client.svg old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/media/images/esp-mail-client.svg rename to lib/libesp32/lib_mail/media/images/esp-mail-client.svg diff --git a/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp b/lib/libesp32/lib_mail/src/ESP_Mail_Client.cpp old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp rename to lib/libesp32/lib_mail/src/ESP_Mail_Client.cpp index f36788385..897d832b8 --- a/lib/lib_div/lib_mail/src/ESP_Mail_Client.cpp +++ b/lib/libesp32/lib_mail/src/ESP_Mail_Client.cpp @@ -1,8548 +1,8548 @@ -/** - * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 - * - * Version: 1.2.0 - * Released: May 17, 2021 - * - * Updates: - * - Add support ESP8266 Core SDK v3.x.x. - * - * - * This library allows Espressif's ESP32 and ESP8266 devices to send and read Email - * through the SMTP and IMAP servers. - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef ESP_Mail_Client_CPP -#define ESP_Mail_Client_CPP - -#include "ESP_Mail_Client.h" - -#if defined(ESP32) -extern "C" -{ -#include -#include -} -#endif - -bool ESP_Mail_Client::sendIMAPCommand(IMAPSession *imap, int msgIndex, int cmdCase) -{ - - std::string cmd; - if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) - appendP(cmd, esp_mail_str_142, true); - else - appendP(cmd, esp_mail_str_143, true); - - char *tmp = intStr(imap->_msgNum[msgIndex]); - cmd += tmp; - delS(tmp); - appendP(cmd, esp_mail_str_147, false); - if (!imap->_config->fetch.set_seen) - { - appendP(cmd, esp_mail_str_152, false); - appendP(cmd, esp_mail_str_214, false); - } - appendP(cmd, esp_mail_str_218, false); - - switch (cmdCase) - { - case 1: - - appendP(cmd, esp_mail_str_269, false); - break; - - case 2: - - if (cPart(imap)->partNumFetchStr.length() > 0) - cmd += cPart(imap)->partNumFetchStr; - else - appendP(cmd, esp_mail_str_215, false); - appendP(cmd, esp_mail_str_156, false); - break; - - case 3: - - cmd += cPart(imap)->partNumFetchStr; - appendP(cmd, esp_mail_str_156, false); - break; - - default: - break; - } - - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - return true; -} - -bool ESP_Mail_Client::readMail(IMAPSession *imap, bool closeSession) -{ - - imap->checkUID(); - imap->checkPath(); - - if (!imap->_tcpConnected) - imap->_mailboxOpened = false; - - std::string buf; - std::string command; - std::string _uid; - appendP(command, esp_mail_str_27, true); - char *tmp = nullptr; - size_t readCount = 0; - imap->_multipart_levels.clear(); - - if (!reconnect(imap)) - return false; - - int cmem = ESP.getFreeHeap(); - - if (cmem < ESP_MAIL_MIN_MEM) - { - if (imap->_debug) - { - esp_mail_debug(""); - errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY); - } - goto out; - } - - //new session - if (!imap->_tcpConnected) - { - //authenticate new - if (!imapAuth(imap)) - { - closeTCP(imap); - return false; - } - } - else - { - //reuse session - for (size_t i = 0; i < imap->_headers.size(); i++) - imap->_headers[i].part_headers.clear(); - imap->_headers.clear(); - - if (strlen(imap->_config->fetch.uid) > 0) - imap->_headerOnly = false; - else - imap->_headerOnly = true; - } - imap->_rfc822_part_count = 0; - imap->_mbif._availableItems = 0; - imap->_msgNum.clear(); - imap->_uidSearch = false; - imap->_mbif._searchCount = 0; - - if (imap->_currentFolder.length() == 0) - return handleIMAPError(imap, IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED, false); - - if (!imap->_mailboxOpened || (imap->_config->fetch.set_seen && !imap->_headerOnly && imap->_readOnlyMode)) - { - if (!imap->openFolder(imap->_currentFolder.c_str(), imap->_readOnlyMode && !imap->_config->fetch.set_seen)) - return handleIMAPError(imap, IMAP_STATUS_OPEN_MAILBOX_FAILED, false); - } - - if (imap->_headerOnly) - { - if (strlen(imap->_config->search.criteria) > 0) - { - - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_66, false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_232); - - if (strposP(imap->_config->search.criteria, esp_mail_str_137, 0) != -1) - { - imap->_uidSearch = true; - appendP(command, esp_mail_str_138, false); - } - - appendP(command, esp_mail_str_139, false); - - for (size_t i = 0; i < strlen(imap->_config->search.criteria); i++) - { - if (imap->_config->search.criteria[i] != ' ' && imap->_config->search.criteria[i] != '\r' && imap->_config->search.criteria[i] != '\n' && imap->_config->search.criteria[i] != '$') - buf.append(1, imap->_config->search.criteria[i]); - - if (imap->_config->search.criteria[i] == ' ') - { - tmp = strP(esp_mail_str_140); - char *tmp2 = strP(esp_mail_str_224); - - if ((imap->_uidSearch && strcmp(buf.c_str(), tmp) == 0) || (imap->_unseen && buf.find(tmp2) != std::string::npos)) - buf.clear(); - delS(tmp); - delS(tmp2); - - tmp = strP(esp_mail_str_141); - if (strcmp(buf.c_str(), tmp) != 0 && buf.length() > 0) - { - appendP(command, esp_mail_str_131, false); - command += buf; - } - delS(tmp); - buf.clear(); - } - } - - tmp = strP(esp_mail_str_223); - if (imap->_unseen && strpos(imap->_config->search.criteria, tmp, 0) == -1) - appendP(command, esp_mail_str_223, false); - delS(tmp); - - if (buf.length() > 0) - { - appendP(command, esp_mail_str_131, false); - command += buf; - } - - if (imapSend(imap, command.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - std::string().swap(command); - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_search; - - if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession)) - return false; - - if (imap->_readCallback) - { - std::string s; - appendP(s, esp_mail_str_34, true); - appendP(s, esp_mail_str_68, false); - char *tmp = intStr(imap->_config->limit.search); - s += tmp; - delS(tmp); - imapCB(imap, s.c_str(), false); - - if (imap->_msgNum.size() > 0) - { - - appendP(s, esp_mail_str_69, true); - tmp = intStr(imap->_mbif._searchCount); - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_70, false); - imapCB(imap, s.c_str(), false); - - appendP(s, esp_mail_str_71, true); - tmp = intStr(imap->_msgNum.size()); - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_70, false); - imapCB(imap, s.c_str(), false); - } - else - imapCBP(imap, esp_mail_str_72, false); - } - } - else - { - imap->_mbif._availableItems++; - imap->_msgNum.push_back(imap->_mbif._nextUID - 1); - imap->_headerOnly = false; - char *tmp = intStr(imap->_mbif._nextUID - 1); - _uid = tmp; - delS(tmp); - imap->_config->fetch.uid = _uid.c_str(); - - if (imap->_readCallback) - imapCBP(imap, esp_mail_str_73, false); - } - } - else - { - imap->_mbif._availableItems++; - imap->_msgNum.push_back(atoi(imap->_config->fetch.uid)); - } - - for (size_t i = 0; i < imap->_msgNum.size(); i++) - { - - imap->_cMsgIdx = i; - imap->_totalRead++; - - if (ESP.getFreeHeap() - (imap->_config->limit.msg_size * (i + 1)) < ESP_MAIL_MIN_MEM) - { - if (imap->_debug) - errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY); - goto out; - } - - if (imap->_readCallback) - { - readCount++; - - std::string s; - appendP(s, esp_mail_str_74, true); - char *tmp = intStr(imap->_totalRead); - s += tmp; - delS(tmp); - - if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) - appendP(s, esp_mail_str_75, false); - else - appendP(s, esp_mail_str_76, false); - - tmp = intStr(imap->_msgNum[i]); - s += tmp; - delS(tmp); - imapCB(imap, "", false); - imapCB(imap, s.c_str(), false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_233); - - std::string cmd; - if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) - appendP(cmd, esp_mail_str_142, true); - else - appendP(cmd, esp_mail_str_143, true); - - if (imap->_debug) - debugInfoP(esp_mail_str_77); - - char *tmp = intStr(imap->_msgNum[i]); - cmd += tmp; - delS(tmp); - - appendP(cmd, esp_mail_str_147, false); - if (!imap->_config->fetch.set_seen) - { - appendP(cmd, esp_mail_str_152, false); - appendP(cmd, esp_mail_str_214, false); - } - appendP(cmd, esp_mail_str_218, false); - - appendP(cmd, esp_mail_str_144, false); - appendP(cmd, esp_mail_str_156, false); - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_header; - - int err = IMAP_STATUS_BAD_COMMAND; - if (imap->_headerOnly) - err = IMAP_STATUS_IMAP_RESPONSE_FAILED; - - if (!handleIMAPResponse(imap, err, closeSession)) - return false; - - if (!imap->_headerOnly) - { - imap->_cPartIdx = 0; - - //multipart - if (cHeader(imap)->multipart) - { - struct esp_mail_imap_multipart_level_t mlevel; - mlevel.level = 1; - mlevel.fetch_rfc822_header = false; - mlevel.append_body_text = false; - imap->_multipart_levels.push_back(mlevel); - - if (!fetchMultipartBodyHeader(imap, i)) - return false; - } - else - { - //singlepart - if (imap->_debug) - { - std::string s; - appendP(s, esp_mail_str_81, true); - s += '1'; - esp_mail_debug(s.c_str()); - } - - cHeader(imap)->partNumStr.clear(); - if (!sendIMAPCommand(imap, i, 1)) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_mime; - if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession)) - return false; - } - - if (imap->_config->download.text || imap->_config->download.html || imap->_config->download.attachment || imap->_config->download.inlineImg) - { - if (!_sdOk && imap->_storageType == esp_mail_file_storage_type_sd) - { - _sdOk = sdTest(); - if (_sdOk) - if (!ESP_MAIL_SD_FS.exists(imap->_config->storage.saved_path)) - createDirs(imap->_config->storage.saved_path); - } - else if (!_flashOk && imap->_storageType == esp_mail_file_storage_type_flash) -#if defined(ESP32) - _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - _flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - } - - if (cHeader(imap)->part_headers.size() > 0) - { - if (cHeader(imap)->attachment_count > 0 && imap->_readCallback) - { - std::string s; - appendP(s, esp_mail_str_34, true); - appendP(s, esp_mail_str_78, false); - char *tmp = intStr(cHeader(imap)->attachment_count); - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_79, false); - imapCB(imap, s.c_str(), false); - - for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) - { - imap->_cPartIdx = j; - if (!cPart(imap)->rfc822_part && cPart(imap)->attach_type != esp_mail_att_type_none) - imapCB(imap, cPart(imap)->filename.c_str(), false); - } - } - - std::string s1, s2; - int _idx1 = 0; - for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) - { - imap->_cPartIdx = j; - if (cPart(imap)->rfc822_part) - { - s1 = cPart(imap)->partNumStr; - _idx1 = cPart(imap)->rfc822_msg_Idx; - } - else if (s1.length() > 0) - { - if (multipartMember(s1, cPart(imap)->partNumStr)) - { - cPart(imap)->message_sub_type = esp_mail_imap_message_sub_type_rfc822; - cPart(imap)->rfc822_msg_Idx = _idx1; - } - } - - if (cPart(imap)->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel) - s2 = cPart(imap)->partNumStr; - else if (s2.length() > 0) - { - if (multipartMember(s2, cPart(imap)->partNumStr)) - { - cPart(imap)->attach_type = esp_mail_att_type_attachment; - if (cPart(imap)->filename.length() == 0) - { - if (cPart(imap)->name.length() > 0) - cPart(imap)->filename = cPart(imap)->name; - else - { - char *tmp = getUID(); - cPart(imap)->filename = tmp; - appendP(cPart(imap)->filename, esp_mail_str_40, false); - delS(tmp); - } - } - } - } - } - - int acnt = 0; - int ccnt = 0; - - for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) - { - imap->_cPartIdx = j; - - if (cPart(imap)->rfc822_part || cPart(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_none) - continue; - - bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - - if (cPart(imap)->attach_type == esp_mail_att_type_none || cPart(imap)->msg_type == esp_mail_msg_type_html || cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) - { - - bool ret = ((imap->_config->enable.rfc822 || imap->_config->download.rfc822) && rfc822_body_subtype) || (!rfc822_body_subtype && ((imap->_config->enable.text && (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)) || (imap->_config->enable.html && cPart(imap)->msg_type == esp_mail_msg_type_html) || (cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))); - if (!ret) - continue; - - if ((imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))) - { - - if (ccnt == 0) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_57, false); - } - - if (imap->_debug) - { - if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) - debugInfoP(esp_mail_str_59); - else if (cPart(imap)->msg_type == esp_mail_msg_type_html) - debugInfoP(esp_mail_str_60); - } - } - else - { - if (ccnt == 0) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_307, false); - } - - if (imap->_debug) - { - if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) - debugInfoP(esp_mail_str_308); - else if (cPart(imap)->msg_type == esp_mail_msg_type_html) - debugInfoP(esp_mail_str_309); - } - } - - ccnt++; - - if (!sendIMAPCommand(imap, i, 2)) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_text; - if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession)) - return false; - } - else if (cPart(imap)->attach_type != esp_mail_att_type_none && (_sdOk || _flashOk)) - { - - if (imap->_config->download.attachment || imap->_config->download.inlineImg) - { - if (acnt == 0) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_80, false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_55); - - acnt++; - if (cPart(imap)->octetLen <= (int)imap->_config->limit.attachment_size) - { - - if (_sdOk || _flashOk) - { - - if ((int)j < (int)cHeader(imap)->part_headers.size() - 1) - if (cHeader(imap)->part_headers[j + 1].octetLen > (int)imap->_config->limit.attachment_size) - cHeader(imap)->downloaded_bytes += cHeader(imap)->part_headers[j + 1].octetLen; - - if (!sendIMAPCommand(imap, i, 3)) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_attachment; - if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession)) - return false; - delay(0); - } - } - else - { - if ((int)j == (int)cHeader(imap)->part_headers.size() - 1) - cHeader(imap)->downloaded_bytes += cPart(imap)->octetLen; - } - } - } - } - } - - if (imap->_config->download.header && !imap->_headerSaved) - { - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_124, false); - } - saveHeader(imap); - } - - if (closeSession) - { - if (imap->_storageType == esp_mail_file_storage_type_sd) - { - if (_sdOk) - ESP_MAIL_SD_FS.end(); - _sdOk = false; - } - else if (imap->_storageType == esp_mail_file_storage_type_flash) - { - if (_flashOk) - ESP_MAIL_FLASH_FS.end(); - - _flashOk = false; - } - } - - imap->_cMsgIdx++; - } - - if (imap->_debug) - { - std::string s; - appendP(s, esp_mail_str_261, true); - appendP(s, esp_mail_str_84, false); - char *tmp = intStr(ESP.getFreeHeap()); - s += tmp; - delS(tmp); - esp_mail_debug(s.c_str()); - } - } - -out: - - if (readCount < imap->_msgNum.size()) - { - imap->_mbif._availableItems = readCount; - imap->_msgNum.erase(imap->_msgNum.begin() + readCount, imap->_msgNum.end()); - } - - if (closeSession) - { - if (!imap->closeSession()) - return false; - } - else - { - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_87, false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_88); - } - - if (imap->_readCallback) - imapCB(imap, "", true); - - return true; -} -bool ESP_Mail_Client::getMultipartFechCmd(IMAPSession *imap, int msgIdx, std::string &partText) -{ - if (imap->_multipart_levels.size() == 0) - return false; - - int cLevel = imap->_multipart_levels.size() - 1; - - cHeader(imap)->partNumStr.clear(); - - if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) - appendP(partText, esp_mail_str_142, true); - else - appendP(partText, esp_mail_str_143, true); - - char *tmp = intStr(imap->_msgNum[msgIdx]); - partText += tmp; - delS(tmp); - - appendP(partText, esp_mail_str_147, false); - if (!imap->_config->fetch.set_seen) - { - appendP(partText, esp_mail_str_152, false); - appendP(partText, esp_mail_str_214, false); - } - appendP(partText, esp_mail_str_218, false); - - for (size_t i = 0; i < imap->_multipart_levels.size(); i++) - { - if (i > 0) - { - appendP(partText, esp_mail_str_152, false); - appendP(cHeader(imap)->partNumStr, esp_mail_str_152, false); - } - - tmp = intStr(imap->_multipart_levels[i].level); - partText += tmp; - cHeader(imap)->partNumStr += tmp; - delS(tmp); - } - - if (imap->_multipart_levels[cLevel].fetch_rfc822_header) - { - appendP(partText, esp_mail_str_51, false); - imap->_multipart_levels[cLevel].append_body_text = true; - } - else - appendP(partText, esp_mail_str_148, false); - - imap->_multipart_levels[cLevel].fetch_rfc822_header = false; - - return true; -} - -bool ESP_Mail_Client::multipartMember(const std::string &part, const std::string &check) -{ - if (part.length() > check.length()) - return false; - - for (size_t i = 0; i < part.length(); i++) - if (part[i] != check[i]) - return false; - - return true; -} - -bool ESP_Mail_Client::fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx) -{ - bool ret = true; - - if (!connected(imap)) - { - closeTCP(imap); - return false; - } - int cLevel = 0; - - do - { - - struct esp_mail_message_part_info_t *_cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1]; - bool rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - - std::string cmd; - - if (!getMultipartFechCmd(imap, msgIdx, cmd)) - return true; - - if (imap->_debug) - { - std::string s; - if (imap->_multipart_levels.size() > 1) - appendP(s, esp_mail_str_86, true); - else - appendP(s, esp_mail_str_81, true); - s += cHeader(imap)->partNumStr; - esp_mail_debug(s.c_str()); - } - - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_mime; - - ret = handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, false); - - _cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1]; - rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - cLevel = imap->_multipart_levels.size() - 1; - - if (ret) - { - - if (_cpart->multipart) - { - if (_cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_alternative || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_related || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_mixed) - { - struct esp_mail_imap_multipart_level_t mlevel; - mlevel.level = 1; - mlevel.fetch_rfc822_header = false; - mlevel.append_body_text = false; - imap->_multipart_levels.push_back(mlevel); - fetchMultipartBodyHeader(imap, msgIdx); - } - else - imap->_multipart_levels[cLevel].level++; - } - else - { - if (rfc822_body_subtype) - { - //to get additional rfc822 message header - imap->_multipart_levels[cLevel].fetch_rfc822_header = true; - fetchMultipartBodyHeader(imap, msgIdx); - } - else - { - if (imap->_multipart_levels[cLevel].append_body_text) - { - //single part rfc822 message body, append TEXT to the body fetch command - appendP(_cpart->partNumFetchStr, esp_mail_str_152, false); - appendP(_cpart->partNumFetchStr, esp_mail_str_215, false); - imap->_multipart_levels[cLevel].append_body_text = false; - } - imap->_multipart_levels[cLevel].level++; - } - } - } - - } while (ret); - - imap->_multipart_levels.pop_back(); - - if (imap->_multipart_levels.size() > 0) - { - cLevel = imap->_multipart_levels.size() - 1; - imap->_multipart_levels[cLevel].level++; - } - - return true; -} - -bool ESP_Mail_Client::connected(IMAPSession *imap) -{ - if (!imap->_secure) - { - if (!imap->httpClient._stream()) - return false; - return imap->httpClient._stream()->_ns_connected(); - } - else - { - if (!imap->httpClient.stream()) - return false; - return imap->httpClient.stream()->connected(); - } -} - -bool ESP_Mail_Client::imapAuth(IMAPSession *imap) -{ - - bool ssl = false; - std::string buf; -#if defined(ESP32) - imap->httpClient.setDebugCallback(NULL); -#elif defined(ESP8266) - -#endif - - if (imap->_config != nullptr) - { - if (strlen(imap->_config->fetch.uid) > 0) - imap->_headerOnly = false; - else - imap->_headerOnly = true; - } - - imap->_totalRead = 0; - imap->_secure = true; - bool secureMode = true; - -#if defined(ESP32) - if (imap->_debug) - imap->httpClient.setDebugCallback(esp_mail_debug); -#elif defined(ESP8266) - imap->httpClient.txBufDivider = 16; //minimum, tx buffer size for ssl data and request command data - imap->httpClient.rxBufDivider = 1; - if (imap->_config != nullptr) - { - if (!imap->_headerOnly && !imap->_config->enable.html && !imap->_config->enable.text && !imap->_config->download.attachment && !imap->_config->download.inlineImg && !imap->_config->download.html && !imap->_config->download.text) - imap->httpClient.rxBufDivider = 16; // minimum rx buffer size for only message header - } -#endif - - if (imap->_sesson_cfg->server.port == esp_mail_imap_port_143) - { - imap->_secure = false; - secureMode = false; - } - else - secureMode = !imap->_sesson_cfg->secure.startTLS; - - setSecure(imap->httpClient, imap->_sesson_cfg, imap->_caCert); - - if (imap->_readCallback) - imapCBP(imap, esp_mail_str_50, false); - - if (imap->_debug) - { - std::string s; - appendP(s, esp_mail_str_314, true); - s += ESP_MAIL_VERSION; - esp_mail_debug(s.c_str()); - - debugInfoP(esp_mail_str_225); - appendP(s, esp_mail_str_261, true); - appendP(s, esp_mail_str_211, false); - s += imap->_sesson_cfg->server.host_name; - esp_mail_debug(s.c_str()); - char *tmp = intStr(imap->_sesson_cfg->server.port); - appendP(s, esp_mail_str_261, true); - appendP(s, esp_mail_str_201, false); - s += tmp; - delS(tmp); - esp_mail_debug(s.c_str()); - } - - imap->httpClient.begin(imap->_sesson_cfg->server.host_name, imap->_sesson_cfg->server.port); - - if (!imap->httpClient.connect(secureMode)) - return handleIMAPError(imap, IMAP_STATUS_SERVER_CONNECT_FAILED, false); - - imap->_tcpConnected = true; - WiFiClient *stream = imap->httpClient.stream(); -#if defined(ESP32) - stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC); -#elif defined(ESP8266) - stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000); -#endif - imap->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000; - - if (imap->_readCallback) - imapCBP(imap, esp_mail_str_54, false); - - if (imap->_debug) - debugInfoP(esp_mail_str_228); - -init: - - if (!imap->checkCapability()) - return false; - - //start TLS when needed or the server issue - if ((imap->_auth_capability.start_tls || imap->_sesson_cfg->secure.startTLS) && !ssl) - { - std::string s; - if (imap->_readCallback) - { - appendP(s, esp_mail_str_34, true); - appendP(s, esp_mail_str_209, false); - esp_mail_debug(s.c_str()); - } - - if (imap->_debug) - { - appendP(s, esp_mail_str_196, true); - esp_mail_debug(s.c_str()); - } - - imapSendP(imap, esp_mail_str_311, false); - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_starttls; - if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false)) - return false; - - if (imap->_debug) - { - debugInfoP(esp_mail_str_310); - } - - //connect in secure mode - //do ssl handshaking - if (!imap->httpClient._stream()->_ns_connect_ssl()) - return handleIMAPError(imap, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP, false); - - //set the secure mode - imap->_sesson_cfg->secure.startTLS = false; - ssl = true; - imap->_secure = true; - - //check the capabilitiy again - goto init; - } - - imap->clearMessageData(); - imap->_mailboxOpened = false; - - bool creds = strlen(imap->_sesson_cfg->login.email) > 0 && strlen(imap->_sesson_cfg->login.password) > 0; - bool xoauth_auth = strlen(imap->_sesson_cfg->login.accessToken) > 0 && imap->_auth_capability.xoauth2; - bool login_auth = creds; - bool plain_auth = imap->_auth_capability.plain && creds; - - bool supported_auth = xoauth_auth || login_auth || plain_auth; - - if (!supported_auth) - return handleIMAPError(imap, IMAP_STATUS_NO_SUPPORTED_AUTH, false); - - //rfc4959 - if (supported_auth) - { - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_56, false); - } - } - - if (xoauth_auth) - { - if (!imap->_auth_capability.xoauth2) - return handleIMAPError(imap, IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false); - - if (imap->_debug) - debugInfoP(esp_mail_str_291); - - std::string cmd; - appendP(cmd, esp_mail_str_292, true); - cmd += getEncodedToken(imap); - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_auth; - if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, false)) - return false; - } - else if (login_auth) - { - - if (imap->_debug) - debugInfoP(esp_mail_str_229); - - std::string cmd; - - appendP(cmd, esp_mail_str_130, true); - cmd += imap->_sesson_cfg->login.email; - appendP(cmd, esp_mail_str_131, false); - cmd += imap->_sesson_cfg->login.password; - - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login; - if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true)) - return false; - } - else if (plain_auth) - { - if (imap->_debug) - debugInfoP(esp_mail_str_290); - - const char *usr = imap->_sesson_cfg->login.email; - const char *psw = imap->_sesson_cfg->login.password; - int len = strlen(usr) + strlen(psw) + 2; - uint8_t *tmp = new uint8_t[len]; - memset(tmp, 0, len); - int p = 1; - memcpy(tmp + p, usr, strlen(usr)); - p += strlen(usr) + 1; - memcpy(tmp + p, psw, strlen(psw)); - p += strlen(psw); - - std::string s; - appendP(s, esp_mail_str_41, true); - s += encodeBase64Str(tmp, p); - delete[] tmp; - - if (imapSend(imap, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login; - if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true)) - return false; - } - - return true; -} - -std::string ESP_Mail_Client::getEncodedToken(IMAPSession *imap) -{ - std::string raw; - appendP(raw, esp_mail_str_285, true); - raw += imap->_sesson_cfg->login.email; - appendP(raw, esp_mail_str_286, false); - raw += imap->_sesson_cfg->login.accessToken; - appendP(raw, esp_mail_str_287, false); - std::string s = encodeBase64Str((const unsigned char *)raw.c_str(), raw.length()); - return s; -} - -bool ESP_Mail_Client::imapLogout(IMAPSession *imap) -{ - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_85, false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_234); - - if (imapSendP(imap, esp_mail_str_146, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_cmd_logout; - if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false)) - return false; - - if (imap->_readCallback) - { - imapCB(imap, "", false); - imapCBP(imap, esp_mail_str_187, false); - } - - if (imap->_debug) - debugInfoP(esp_mail_str_235); - - return true; -} - -void ESP_Mail_Client::errorStatusCB(IMAPSession *imap, int error) -{ - imap->_imapStatus.statusCode = error; - std::string s; - if (imap->_readCallback) - { - appendP(s, esp_mail_str_53, true); - s += imap->errorReason().c_str(); - imapCB(imap, s.c_str(), false); - } - - if (imap->_debug) - { - appendP(s, esp_mail_str_185, true); - s += imap->errorReason().c_str(); - esp_mail_debug(s.c_str()); - } -} - -size_t ESP_Mail_Client::imapSendP(IMAPSession *imap, PGM_P v, bool newline) -{ - if (!reconnect(imap)) - { - closeTCP(imap); - return 0; - } - - if (!connected(imap)) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!imap->_tcpConnected) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - char *tmp = strP(v); - size_t len = 0; - - if (newline) - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(tmp); - if (!imap->_secure) - len = imap->httpClient._ns_println(tmp); - else - len = imap->httpClient.stream()->println(tmp); - } - else - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(tmp, false); - if (!imap->_secure) - len = imap->httpClient._ns_print(tmp); - else - len = imap->httpClient.stream()->print(tmp); - } - - if (len != strlen(tmp) && len != strlen(tmp) + 2) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - delS(tmp); - - return len; -} - -size_t ESP_Mail_Client::imapSend(IMAPSession *imap, const char *data, bool newline) -{ - if (!reconnect(imap)) - { - closeTCP(imap); - return 0; - } - - if (!connected(imap)) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!imap->_tcpConnected) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - size_t len = 0; - - if (newline) - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(data); - if (!imap->_secure) - len = imap->httpClient._ns_println(data); - else - len = imap->httpClient.stream()->println(data); - } - else - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(data, false); - if (!imap->_secure) - len = imap->httpClient._ns_print(data); - else - len = imap->httpClient.stream()->print(data); - } - - if (len != strlen(data) && len != strlen(data) + 2) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - return len; -} - -size_t ESP_Mail_Client::imapSend(IMAPSession *imap, int data, bool newline) -{ - if (!reconnect(imap)) - { - closeTCP(imap); - return 0; - } - - if (!connected(imap)) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!imap->_tcpConnected) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - char *tmp = intStr(data); - size_t len = 0; - - if (newline) - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(tmp); - if (!imap->_secure) - len = imap->httpClient._ns_println(tmp); - else - len = imap->httpClient.stream()->println(tmp); - } - else - { - if (imap->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(tmp, false); - if (!imap->_secure) - len = imap->httpClient._ns_print(tmp); - else - len = imap->httpClient.stream()->print(tmp); - } - - if (len != strlen(tmp) && len != strlen(tmp) + 2) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - delS(tmp); - - return len; -} - -bool ESP_Mail_Client::setFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) -{ - return _setFlag(imap, msgUID, flag, 0, closeSession); -} - -bool ESP_Mail_Client::addFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) -{ - return _setFlag(imap, msgUID, flag, 1, closeSession); -} - -bool ESP_Mail_Client::removeFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) -{ - return _setFlag(imap, msgUID, flag, 2, closeSession); -} - -bool ESP_Mail_Client::_setFlag(IMAPSession *imap, int msgUID, const char *flag, uint8_t action, bool closeSession) -{ - if (!reconnect(imap)) - return false; - - if (!imap->_tcpConnected) - { - imap->_mailboxOpened = false; - return false; - } - - if (imap->_currentFolder.length() == 0) - { - if (imap->_readCallback) - debugInfoP(esp_mail_str_153); - - if (imap->_debug) - { - std::string e; - appendP(e, esp_mail_str_185, true); - appendP(e, esp_mail_str_151, false); - esp_mail_debug(e.c_str()); - } - } - else - { - if (imap->_readOnlyMode || !imap->_mailboxOpened) - { - if (!imap->selectFolder(imap->_currentFolder.c_str(), false)) - return false; - } - } - - if (imap->_readCallback) - { - imapCB(imap, "", false); - if (action == 0) - debugInfoP(esp_mail_str_157); - else if (action == 1) - debugInfoP(esp_mail_str_155); - else - debugInfoP(esp_mail_str_154); - } - - if (imap->_debug) - { - if (action == 0) - debugInfoP(esp_mail_str_253); - else if (action == 1) - debugInfoP(esp_mail_str_254); - else - debugInfoP(esp_mail_str_255); - } - - std::string cmd; - appendP(cmd, esp_mail_str_249, true); - char *tmp = intStr(msgUID); - cmd += tmp; - delS(tmp); - if (action == 0) - appendP(cmd, esp_mail_str_250, false); - else if (action == 1) - appendP(cmd, esp_mail_str_251, false); - else - appendP(cmd, esp_mail_str_252, false); - cmd += flag; - appendP(cmd, esp_mail_str_192, false); - - if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - imap->_imap_cmd = esp_mail_imap_cmd_store; - - if (!handleIMAPResponse(imap, IMAP_STATUS_PARSE_FLAG_FAILED, false)) - return false; - - if (closeSession) - imap->closeSession(); - - return true; -} - -void ESP_Mail_Client::createDirs(std::string dirs) -{ - std::string dir = ""; - int count = 0; - for (size_t i = 0; i < dirs.length(); i++) - { - dir.append(1, dirs[i]); - count++; - if (dirs[i] == '/') - { - if (dir.length() > 0) - ESP_MAIL_SD_FS.mkdir(dir.substr(0, dir.length() - 1).c_str()); - count = 0; - } - } - if (count > 0) - ESP_MAIL_SD_FS.mkdir(dir.c_str()); - std::string().swap(dir); -} - -bool ESP_Mail_Client::sdTest() -{ -#if defined(CARD_TYPE_SD) - if (_sdConfigSet) - sdBegin(_sck, _miso, _mosi, _ss); - else - sdBegin(); -#endif - -#if defined(ESP32) -#if defined(CARD_TYPE_SD_MMC) - if (_sdConfigSet) - sdMMCBegin(sd_mmc_mountpoint, sd_mmc_mode1bit, sd_mmc_format_if_mount_failed); -#endif -#endif - - file = ESP_MAIL_SD_FS.open(esp_mail_str_204, FILE_WRITE); - if (!file) - return false; - - if (!file.write(32)) - return false; - file.close(); - - file = ESP_MAIL_SD_FS.open(esp_mail_str_204); - if (!file) - return false; - - while (file.available()) - { - if (file.read() != 32) - return false; - } - file.close(); - - ESP_MAIL_SD_FS.remove(esp_mail_str_204); - - return true; -} - -std::string ESP_Mail_Client::getEncodedToken(SMTPSession *smtp) -{ - std::string raw; - appendP(raw, esp_mail_str_285, true); - raw += smtp->_sesson_cfg->login.email; - appendP(raw, esp_mail_str_286, false); - raw += smtp->_sesson_cfg->login.accessToken; - appendP(raw, esp_mail_str_287, false); - return encodeBase64Str((const unsigned char *)raw.c_str(), raw.length()); -} - -bool ESP_Mail_Client::smtpAuth(SMTPSession *smtp) -{ - - if (!reconnect(smtp)) - return false; - - bool ssl = false; - smtp->_secure = true; - bool secureMode = true; - - std::string s; - -#if defined(ESP32) - smtp->httpClient.setDebugCallback(NULL); -#elif defined(ESP8266) - smtp->httpClient.rxBufDivider = 16; // minimum rx buffer for smtp status response - smtp->httpClient.txBufDivider = 8; // medium tx buffer for faster attachment/inline data transfer -#endif - - if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_25) - { - smtp->_secure = false; - secureMode = false; - } - else - { - if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_587) - smtp->_sesson_cfg->secure.startTLS = true; - - secureMode = !smtp->_sesson_cfg->secure.startTLS; - - //to prevent to send the connection upgrade command when some server promotes - //the starttls capability even the current connection was already secured. - if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_465) - ssl = true; - } - - setSecure(smtp->httpClient, smtp->_sesson_cfg, smtp->_caCert); - - //Server connection attempt: no status code - if (smtp->_sendCallback) - smtpCBP(smtp, esp_mail_str_120); - - if (smtp->_debug) - { - appendP(s, esp_mail_str_314, true); - s += ESP_MAIL_VERSION; - esp_mail_debug(s.c_str()); - - debugInfoP(esp_mail_str_236); - appendP(s, esp_mail_str_261, true); - appendP(s, esp_mail_str_211, false); - s += smtp->_sesson_cfg->server.host_name; - esp_mail_debug(s.c_str()); - char *tmp = intStr(smtp->_sesson_cfg->server.port); - appendP(s, esp_mail_str_261, true); - appendP(s, esp_mail_str_201, false); - s += tmp; - delS(tmp); - esp_mail_debug(s.c_str()); - } -#if defined(ESP32) - if (smtp->_debug) - smtp->httpClient.setDebugCallback(esp_mail_debug); -#endif - - smtp->httpClient.begin(smtp->_sesson_cfg->server.host_name, smtp->_sesson_cfg->server.port); - - if (!smtp->httpClient.connect(secureMode)) - return handleSMTPError(smtp, SMTP_STATUS_SERVER_CONNECT_FAILED); - - //server connected - smtp->_tcpConnected = true; - - if (smtp->_debug) - debugInfoP(esp_mail_str_238); - - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_121); - } - -#if defined(ESP32) - smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC); -#elif defined(ESP8266) - smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000); -#endif - smtp->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state; - - //expected status code 220 for ready to service - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_220, SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED)) - return false; - -init: - - //Sending greeting hello response - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_122); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_239); - - appendP(s, esp_mail_str_6, true); - if (strlen(smtp->_sesson_cfg->login.user_domain) > 0) - s += smtp->_sesson_cfg->login.user_domain; - else - appendP(s, esp_mail_str_44, false); - - if (smtpSendP(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_greeting; - - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, 0)) - { - appendP(s, esp_mail_str_5, true); - if (strlen(smtp->_sesson_cfg->login.user_domain) > 0) - s += smtp->_sesson_cfg->login.user_domain; - else - appendP(s, esp_mail_str_44, false); - - if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED)) - return false; - smtp->_send_capability.esmtp = false; - smtp->_auth_capability.login = true; - } - else - smtp->_send_capability.esmtp = true; - - //start TLS when needed - if ((smtp->_auth_capability.start_tls || smtp->_sesson_cfg->secure.startTLS) && !ssl) - { - //send starttls command - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_209); - } - - if (smtp->_debug) - { - appendP(s, esp_mail_str_196, true); - esp_mail_debug(s.c_str()); - } - - //expected status code 250 for complete the request - //some server returns 220 to restart to initial state - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls; - smtpSendP(smtp, esp_mail_str_311, false); - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED)) - return false; - - if (smtp->_debug) - { - debugInfoP(esp_mail_str_310); - } - - //connect using secure mode - //do ssl handshaking - if (!smtp->httpClient._stream()->_ns_connect_ssl()) - return handleSMTPError(smtp, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP); - - //set the secure mode - smtp->_sesson_cfg->secure.startTLS = false; - ssl = true; - smtp->_secure = true; - - //return to initial state if the response status is 220. - if (smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_220) - goto init; - } - - bool creds = strlen(smtp->_sesson_cfg->login.email) > 0 && strlen(smtp->_sesson_cfg->login.password) > 0; - bool xoauth_auth = strlen(smtp->_sesson_cfg->login.accessToken) > 0 && smtp->_auth_capability.xoauth2; - bool login_auth = smtp->_auth_capability.login && creds; - bool plain_auth = smtp->_auth_capability.plain && creds; - - if (xoauth_auth || login_auth || plain_auth) - { - if (smtp->_sendCallback) - { - smtpCB(smtp, "", false); - smtpCBP(smtp, esp_mail_str_56, false); - } - - //log in - if (xoauth_auth) - { - if (smtp->_debug) - debugInfoP(esp_mail_str_288); - - if (!smtp->_auth_capability.xoauth2) - return handleSMTPError(smtp, SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false); - - if (smtpSendP(smtp, esp_mail_str_289, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (smtpSend(smtp, getEncodedToken(smtp).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_AUTHEN_FAILED)) - return false; - - return true; - } - else if (plain_auth) - { - - if (smtp->_debug) - debugInfoP(esp_mail_str_241); - - if (smtp->_debug) - { - appendP(s, esp_mail_str_261, true); - s += smtp->_sesson_cfg->login.email; - esp_mail_debug(s.c_str()); - - appendP(s, esp_mail_str_131, false); - for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++) - appendP(s, esp_mail_str_183, false); - esp_mail_debug(s.c_str()); - } - - //rfc4616 - const char *usr = smtp->_sesson_cfg->login.email; - const char *psw = smtp->_sesson_cfg->login.password; - int len = strlen(usr) + strlen(psw) + 2; - uint8_t *tmp = new uint8_t[len]; - memset(tmp, 0, len); - int p = 1; - memcpy(tmp + p, usr, strlen(usr)); - p += strlen(usr) + 1; - memcpy(tmp + p, psw, strlen(psw)); - p += strlen(psw); - - std::string s; - appendP(s, esp_mail_str_45, true); - appendP(s, esp_mail_str_131, false); - s += encodeBase64Str(tmp, p); - delete[] tmp; - - if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth_plain; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_USER_LOGIN_FAILED)) - return false; - - return true; - } - else if (login_auth) - { - if (smtp->_debug) - debugInfoP(esp_mail_str_240); - - if (smtpSendP(smtp, esp_mail_str_4, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_AUTHEN_FAILED)) - return false; - - if (smtp->_debug) - { - appendP(s, esp_mail_str_261, true); - s += smtp->_sesson_cfg->login.email; - esp_mail_debug(s.c_str()); - } - - if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.email, strlen(smtp->_sesson_cfg->login.email)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_user; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_USER_LOGIN_FAILED)) - return false; - - if (smtp->_debug) - { - appendP(s, esp_mail_str_261, true); - for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++) - appendP(s, esp_mail_str_183, false); - esp_mail_debug(s.c_str()); - } - - if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.password, strlen(smtp->_sesson_cfg->login.password)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_psw; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_PASSWORD_LOGIN_FAILED)) - return false; - - return true; - } - } - - return true; -} - -void ESP_Mail_Client::mimeFromFile(const char *name, std::string &mime) -{ - std::string ext = name; - size_t p = ext.find_last_of("."); - if (p != std::string::npos) - { - ext = ext.substr(p, ext.length() - p); - if (ext.length() > 0) - getMIME(ext.c_str(), mime); - } -} - -bool ESP_Mail_Client::connected(SMTPSession *smtp) -{ - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - { - if (!smtp->httpClient._stream()) - return false; - return smtp->httpClient._stream()->_ns_connected(); - } - else - { - if (!smtp->httpClient.stream()) - return false; - return smtp->httpClient.stream()->connected(); - } -} - -bool ESP_Mail_Client::setSendingResult(SMTPSession *smtp, SMTP_Message *msg, bool result) -{ - if (result) - smtp->_sentSuccessCount++; - else - smtp->_sentFailedCount++; - - if (smtp->_sendCallback) - { - struct esp_mail_smtp_send_status_t status; - status.completed = result; - status.timesstamp = time(nullptr); - status.subject = msg->subject; - status.recipients = msg->_rcp[0].email; - - smtp->sendingResult.add(status); - - smtp->_cbData._sentSuccess = smtp->_sentSuccessCount; - smtp->_cbData._sentFailed = smtp->_sentFailedCount; - } - - return result; -} - -bool ESP_Mail_Client::sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession) -{ - - if (strlen(msg->html.content) > 0 || msg->html.blob.size > 0 || strlen(msg->html.file.name) > 0) - msg->type |= esp_mail_msg_type_html; - - if (strlen(msg->text.content) > 0 || msg->text.blob.size > 0 || strlen(msg->text.file.name) > 0) - msg->type |= esp_mail_msg_type_plain; - - for (size_t i = 0; i < msg->_rfc822.size(); i++) - { - if (strlen(msg->_rfc822[i].html.content) > 0) - msg->_rfc822[i].type |= esp_mail_msg_type_html; - - if (strlen(msg->_rfc822[i].text.content) > 0) - msg->_rfc822[i].type |= esp_mail_msg_type_plain; - } - - return _sendMail(smtp, msg, closeSession); -} - -void ESP_Mail_Client::getMIME(const char *ext, std::string &mime) -{ - mime = ""; - for (int i = 0; i < esp_mail_file_extension_maxType; i++) - { - if (strcmp_P(ext, mimeinfo[i].endsWith) == 0) - { - char *tmp = strP(mimeinfo[i].mimeType); - mime = tmp; - delS(tmp); - break; - } - } -} - -size_t ESP_Mail_Client::numAtt(SMTPSession *smtp, esp_mail_attach_type type, SMTP_Message *msg) -{ - size_t count = 0; - for (size_t i = 0; i < msg->_att.size(); i++) - { - if (msg->_att[i]._int.att_type == type) - count++; - } - return count; -} - -bool ESP_Mail_Client::validEmail(const char *s) -{ - std::string str(s); - auto at = std::find(str.begin(), str.end(), '@'); - auto dot = std::find(at, str.end(), '.'); - return (at != str.end()) && (dot != str.end()); -} -bool ESP_Mail_Client::checkEmail(SMTPSession *smtp, SMTP_Message *msg) -{ - bool validRecipient = false; - - if (!validEmail(msg->sender.email)) - { - errorStatusCB(smtp, SMTP_STATUS_NO_VALID_SENDER_EXISTED); - return setSendingResult(smtp, msg, false); - } - - for (uint8_t i = 0; i < msg->_rcp.size(); i++) - { - if (validEmail(msg->_rcp[i].email)) - validRecipient = true; - } - - if (!validRecipient) - { - errorStatusCB(smtp, SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED); - return setSendingResult(smtp, msg, false); - } - - return true; -} - -bool ESP_Mail_Client::_sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession) -{ - - smtp->_smtpStatus.statusCode = 0; - smtp->_smtpStatus.respCode = 0; - smtp->_smtpStatus.text.clear(); - bool rfc822MSG = false; - - if (!checkEmail(smtp, msg)) - return false; - - smtp->_chunkedEnable = false; - smtp->_chunkCount = 0; - - //new session - if (!smtp->_tcpConnected) - { - if (!smtpAuth(smtp)) - { - closeTCP(smtp); - return setSendingResult(smtp, msg, false); - } - smtp->_sentSuccessCount = 0; - smtp->_sentFailedCount = 0; - smtp->sendingResult.clear(); - } - else - { - //reuse session - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - if (smtp->_sentSuccessCount || smtp->_sentFailedCount) - smtpCBP(smtp, esp_mail_str_267); - else - smtpCBP(smtp, esp_mail_str_208); - } - - if (smtp->_debug) - { - if (smtp->_sentSuccessCount || smtp->_sentFailedCount) - debugInfoP(esp_mail_str_268); - else - debugInfoP(esp_mail_str_207); - } - } - - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_125); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_242); - - std::string buf; - std::string buf2; - checkBinaryData(smtp, msg); - - if (msg->priority >= esp_mail_smtp_priority_high && msg->priority <= esp_mail_smtp_priority_low) - { - char *tmp = intStr(msg->priority); - appendP(buf2, esp_mail_str_17, true); - buf2 += tmp; - delS(tmp); - appendP(buf2, esp_mail_str_34, false); - - if (msg->priority == esp_mail_smtp_priority_high) - { - appendP(buf2, esp_mail_str_18, false); - appendP(buf2, esp_mail_str_21, false); - } - else if (msg->priority == esp_mail_smtp_priority_normal) - { - appendP(buf2, esp_mail_str_19, false); - appendP(buf2, esp_mail_str_22, false); - } - else if (msg->priority == esp_mail_smtp_priority_low) - { - appendP(buf2, esp_mail_str_20, false); - appendP(buf2, esp_mail_str_23, false); - } - } - - appendP(buf2, esp_mail_str_10, false); - - if (strlen(msg->sender.name) > 0) - buf2 += msg->sender.name; - - appendP(buf2, esp_mail_str_14, false); - buf2 += msg->sender.email; - appendP(buf2, esp_mail_str_15, false); - appendP(buf2, esp_mail_str_34, false); - - appendP(buf, esp_mail_str_8, true); - appendP(buf, esp_mail_str_14, false); - buf += msg->sender.email; - appendP(buf, esp_mail_str_15, false); - - if (smtp->_send_capability.binaryMIME && smtp->_send_capability.chunking && msg->enable.chunking && (msg->text._int.binary || msg->html._int.binary)) - appendP(buf, esp_mail_str_104, false); - - if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_sender; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_SENDER_FAILED)) - return setSendingResult(smtp, msg, false); - - for (uint8_t i = 0; i < msg->_rcp.size(); i++) - { - if (i == 0) - { - appendP(buf2, esp_mail_str_11, false); - if (strlen(msg->_rcp[i].name) > 0) - buf2 += msg->_rcp[i].name; - - appendP(buf2, esp_mail_str_14, false); - buf2 += msg->_rcp[i].email; - appendP(buf2, esp_mail_str_15, false); - } - else - { - if (strlen(msg->_rcp[i].name) > 0) - { - appendP(buf2, esp_mail_str_263, false); - buf2 += msg->_rcp[i].name; - appendP(buf2, esp_mail_str_14, false); - } - else - appendP(buf2, esp_mail_str_13, false); - buf2 += msg->_rcp[i].email; - appendP(buf2, esp_mail_str_15, false); - } - - if (i == msg->_rcp.size() - 1) - appendP(buf2, esp_mail_str_34, false); - - buf.clear(); - //only address - appendP(buf, esp_mail_str_9, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->_rcp[i].email; - appendP(buf, esp_mail_str_15, false); - - //rfc3461, rfc3464 - if (smtp->_send_capability.dsn) - { - if (msg->response.notify != esp_mail_smtp_notify::esp_mail_smtp_notify_never) - { - appendP(buf, esp_mail_str_262, false); - int opcnt = 0; - if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_success) - { - if (opcnt > 0) - appendP(buf, esp_mail_str_263, false); - appendP(buf, esp_mail_str_264, false); - opcnt++; - } - if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_failure) - { - if (opcnt > 0) - appendP(buf, esp_mail_str_263, false); - appendP(buf, esp_mail_str_265, false); - opcnt++; - } - if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_delay) - { - if (opcnt > 0) - appendP(buf, esp_mail_str_263, false); - appendP(buf, esp_mail_str_266, false); - opcnt++; - } - } - } - - if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) - return setSendingResult(smtp, msg, false); - } - - for (uint8_t i = 0; i < msg->_cc.size(); i++) - { - if (i == 0) - { - appendP(buf2, esp_mail_str_12, false); - appendP(buf2, esp_mail_str_14, false); - buf2 += msg->_cc[i].email; - appendP(buf2, esp_mail_str_15, false); - } - else - { - appendP(buf2, esp_mail_str_13, false); - buf2 += msg->_cc[i].email; - appendP(buf2, esp_mail_str_15, false); - } - - if (i == msg->_cc.size() - 1) - appendP(buf2, esp_mail_str_34, false); - - buf.clear(); - - appendP(buf, esp_mail_str_9, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->_cc[i].email; - appendP(buf, esp_mail_str_15, false); - - if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) - return setSendingResult(smtp, msg, false); - } - - for (uint8_t i = 0; i < msg->_bcc.size(); i++) - { - appendP(buf, esp_mail_str_9, true); - appendP(buf, esp_mail_str_14, false); - buf += msg->_bcc[i].email; - appendP(buf, esp_mail_str_15, false); - if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) - return setSendingResult(smtp, msg, false); - } - - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_126); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_243); - - if (smtp->_send_capability.chunking && msg->enable.chunking) - { - smtp->_chunkedEnable = true; - if (!bdat(smtp, msg, buf2.length(), false)) - return false; - } - else - { - if (smtpSendP(smtp, esp_mail_str_16, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_354, SMTP_STATUS_SEND_BODY_FAILED)) - return setSendingResult(smtp, msg, false); - } - - if (smtpSend(smtp, buf2.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - std::string s; - appendP(s, esp_mail_str_24, true); - s += msg->subject; - appendP(s, esp_mail_str_34, false); - - if (msg->_hdr.size() > 0) - { - for (uint8_t k = 0; k < msg->_hdr.size(); k++) - { - s += msg->_hdr[k]; - appendP(s, esp_mail_str_34, false); - } - } - - if (strlen(msg->response.reply_to) > 0) - { - appendP(s, esp_mail_str_184, false); - appendP(s, esp_mail_str_14, false); - s += msg->response.reply_to; - appendP(s, esp_mail_str_15, false); - appendP(s, esp_mail_str_34, false); - } - - if (strlen(msg->response.return_path) > 0) - { - appendP(s, esp_mail_str_46, false); - appendP(s, esp_mail_str_14, false); - s += msg->response.return_path; - appendP(s, esp_mail_str_15, false); - appendP(s, esp_mail_str_34, false); - } - - appendP(s, esp_mail_str_3, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - return sendMSGData(smtp, msg, closeSession, rfc822MSG); -} - -bool ESP_Mail_Client::sendMSGData(SMTPSession *smtp, SMTP_Message *msg, bool closeSession, bool rfc822MSG) -{ - std::string s; - std::string mixed = getBoundary(15); - std::string alt = getBoundary(15); - - if (numAtt(smtp, esp_mail_att_type_attachment, msg) == 0 && msg->_parallel.size() == 0 && msg->_rfc822.size() == 0) - { - if (msg->type == (esp_mail_msg_type_plain | esp_mail_msg_type_html | esp_mail_msg_type_enriched) || numAtt(smtp, esp_mail_att_type_inline, msg) > 0) - { - if (!sendMSG(smtp, msg, alt)) - return setSendingResult(smtp, msg, false); - } - else if (msg->type != esp_mail_msg_type_none) - { - if (!sendPartText(smtp, msg, msg->type, "")) - return setSendingResult(smtp, msg, false); - } - } - else - { - appendP(s, esp_mail_str_1, true); - s += mixed; - appendP(s, esp_mail_str_35, false); - - appendP(s, esp_mail_str_33, false); - s += mixed; - appendP(s, esp_mail_str_34, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - if (!sendMSG(smtp, msg, alt)) - return setSendingResult(smtp, msg, false); - - if (!bdat(smtp, msg, 2, false)) - return false; - - if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_127); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_244); - - if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_attachment, msg) > 0) - esp_mail_debug(""); - - if (!sendAttachments(smtp, msg, mixed)) - return setSendingResult(smtp, msg, false); - - if (!sendParallelAttachments(smtp, msg, mixed)) - return setSendingResult(smtp, msg, false); - - if (!sendRFC822Msg(smtp, msg, mixed, closeSession, msg->_rfc822.size() > 0)) - return setSendingResult(smtp, msg, false); - - appendP(s, esp_mail_str_33, true); - s += mixed; - appendP(s, esp_mail_str_33, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - } - - if (!rfc822MSG) - { - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_303); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_304); - - if (smtp->_chunkedEnable) - { - - if (!bdat(smtp, msg, 0, true)) - return false; - - smtp->_smtp_cmd = esp_mail_smtp_cmd_chunk_termination; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) - return false; - } - else - { - if (smtpSendP(smtp, esp_mail_str_37, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) - return setSendingResult(smtp, msg, false); - } - - setSendingResult(smtp, msg, true); - - if (closeSession) - if (!smtp->closeSession()) - return false; - } - - return true; -} - -bool ESP_Mail_Client::sendRFC822Msg(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool closeSession, bool rfc822MSG) -{ - if (msg->_rfc822.size() == 0) - return true; - std::string buf; - for (uint8_t i = 0; i < msg->_rfc822.size(); i++) - { - buf.clear(); - getRFC822PartHeader(smtp, buf, boundary); - - getRFC822MsgEnvelope(smtp, &msg->_rfc822[i], buf); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!sendMSGData(smtp, &msg->_rfc822[i], closeSession, rfc822MSG)) - return false; - } - - return true; -} - -void ESP_Mail_Client::getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg, std::string &buf) -{ - if (strlen(msg->date) > 0) - { - appendP(buf, esp_mail_str_99, false); - buf += msg->date; - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->from.email) > 0) - { - appendP(buf, esp_mail_str_10, false); - - if (strlen(msg->from.name) > 0) - buf += msg->from.name; - - appendP(buf, esp_mail_str_14, false); - buf += msg->from.email; - appendP(buf, esp_mail_str_15, false); - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->sender.email) > 0) - { - appendP(buf, esp_mail_str_150, false); - - if (strlen(msg->sender.name) > 0) - buf += msg->sender.name; - - appendP(buf, esp_mail_str_14, false); - buf += msg->sender.email; - appendP(buf, esp_mail_str_15, false); - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->response.reply_to) > 0) - { - appendP(buf, esp_mail_str_184, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->response.reply_to; - appendP(buf, esp_mail_str_15, false); - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->response.return_path) > 0) - { - appendP(buf, esp_mail_str_46, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->response.return_path; - appendP(buf, esp_mail_str_15, false); - appendP(buf, esp_mail_str_34, false); - } - - for (uint8_t i = 0; i < msg->_rcp.size(); i++) - { - if (i == 0) - { - appendP(buf, esp_mail_str_11, false); - if (strlen(msg->_rcp[i].name) > 0) - buf += msg->_rcp[i].name; - - appendP(buf, esp_mail_str_14, false); - buf += msg->_rcp[i].email; - appendP(buf, esp_mail_str_15, false); - } - else - { - if (strlen(msg->_rcp[i].name) > 0) - { - appendP(buf, esp_mail_str_263, false); - buf += msg->_rcp[i].name; - appendP(buf, esp_mail_str_14, false); - } - else - appendP(buf, esp_mail_str_13, false); - buf += msg->_rcp[i].email; - appendP(buf, esp_mail_str_15, false); - } - - if (i == msg->_rcp.size() - 1) - appendP(buf, esp_mail_str_34, false); - } - - for (uint8_t i = 0; i < msg->_cc.size(); i++) - { - if (i == 0) - { - appendP(buf, esp_mail_str_12, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->_cc[i].email; - appendP(buf, esp_mail_str_15, false); - } - else - { - appendP(buf, esp_mail_str_13, false); - buf += msg->_cc[i].email; - appendP(buf, esp_mail_str_15, false); - } - - if (i == msg->_cc.size() - 1) - appendP(buf, esp_mail_str_34, false); - } - - for (uint8_t i = 0; i < msg->_bcc.size(); i++) - { - if (i == 0) - { - appendP(buf, esp_mail_str_149, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->_bcc[i].email; - appendP(buf, esp_mail_str_15, false); - } - else - { - appendP(buf, esp_mail_str_13, false); - buf += msg->_bcc[i].email; - appendP(buf, esp_mail_str_15, false); - } - - if (i == msg->_bcc.size() - 1) - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->subject) > 0) - { - appendP(buf, esp_mail_str_279, false); - buf += msg->subject; - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->keyword) > 0) - { - appendP(buf, esp_mail_str_145, false); - buf += msg->keyword; - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->comment) > 0) - { - appendP(buf, esp_mail_str_134, false); - buf += msg->comment; - appendP(buf, esp_mail_str_34, false); - } - - if (strlen(msg->messageID) > 0) - { - appendP(buf, esp_mail_str_274, false); - appendP(buf, esp_mail_str_14, false); - buf += msg->messageID; - appendP(buf, esp_mail_str_15, false); - appendP(buf, esp_mail_str_34, false); - } -} - -bool ESP_Mail_Client::bdat(SMTPSession *smtp, SMTP_Message *msg, int len, bool last) -{ - if (!smtp->_chunkedEnable || !msg->enable.chunking) - return true; - - smtp->_chunkCount++; - - std::string bdat; - appendP(bdat, esp_mail_str_106, true); - char *tmp = intStr(len); - bdat += tmp; - if (last) - appendP(bdat, esp_mail_str_173, false); - delS(tmp); - if (smtpSend(smtp, bdat.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - if (!smtp->_send_capability.pipelining) - { - smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; - if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) - return setSendingResult(smtp, msg, false); - smtp->_chunkCount = 0; - } - return true; -} - -void ESP_Mail_Client::checkBinaryData(SMTPSession *smtp, SMTP_Message *msg) -{ - if (msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type & esp_mail_msg_type_html) - { - if ((msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched) > 0) - { - if (strlen(msg->text.transfer_encoding) > 0) - { - if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0) - { - msg->text._int.binary = true; - } - } - } - - if ((msg->type & esp_mail_msg_type_html) > 0) - { - if (strlen(msg->html.transfer_encoding) > 0) - { - if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0) - { - msg->html._int.binary = true; - } - } - } - } - - for (size_t i = 0; i < msg->_att.size(); i++) - { - if (strcmpP(msg->_att[i].descr.transfer_encoding, 0, esp_mail_str_166)) - { - msg->_att[i]._int.binary = true; - } - } -} - -bool ESP_Mail_Client::sendBlob(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att) -{ - if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0) - { - if (!sendBase64(smtp, msg, (const unsigned char *)att->blob.data, att->blob.size, att->_int.flash_blob, att->descr.filename, smtp->_sendCallback != NULL)) - return false; - return true; - } - else - { - if (att->blob.size > 0) - { - size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; - size_t writeLen = 0; - uint8_t *buf = new uint8_t[chunkSize]; - int pg = 0, _pg = 0; - while (writeLen < att->blob.size) - { - if (writeLen > att->blob.size - chunkSize) - chunkSize = att->blob.size - writeLen; - - if (!bdat(smtp, msg, chunkSize, false)) - break; - memcpy_P(buf, att->blob.data, chunkSize); - if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - break; - - if (smtp->_sendCallback) - { - pg = (float)(100.0f * writeLen / att->blob.size); - if (pg != _pg) - uploadReport(att->descr.filename, pg); - _pg = pg; - } - writeLen += chunkSize; - } - delete[] buf; - if (smtp->_sendCallback && _pg < 100) - uploadReport(att->descr.filename, 100); - - return writeLen >= att->blob.size; - } - } - return false; -} - -bool ESP_Mail_Client::sendFile(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file) -{ - if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0) - { - if (!sendBase64Stream(smtp, msg, file, att->descr.filename, smtp->_sendCallback != NULL)) - return false; - return true; - } - else - { - if (file.size() > 0) - { - size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; - size_t writeLen = 0; - if (file.size() < chunkSize) - chunkSize = file.size(); - uint8_t *buf = new uint8_t[chunkSize]; - int pg = 0, _pg = 0; - while (writeLen < file.size() && file.available()) - { - if (writeLen > file.size() - chunkSize) - chunkSize = file.size() - writeLen; - size_t readLen = file.read(buf, chunkSize); - if (readLen != chunkSize) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - break; - } - - if (!bdat(smtp, msg, chunkSize, false)) - break; - - if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - break; - - if (smtp->_sendCallback) - { - pg = (float)(100.0f * writeLen / file.size()); - if (pg != _pg) - uploadReport(att->descr.filename, pg); - _pg = pg; - } - writeLen += chunkSize; - } - delete[] buf; - if (smtp->_sendCallback && _pg < 100) - uploadReport(att->descr.filename, 100); - return writeLen == file.size(); - } - return false; - } - return false; -} - -bool ESP_Mail_Client::sendParallelAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary) -{ - if (msg->_parallel.size() == 0) - return true; - - std::string buf; - std::string parallel = getBoundary(15); - appendP(buf, esp_mail_str_33, true); - buf += boundary; - appendP(buf, esp_mail_str_34, false); - - appendP(buf, esp_mail_str_28, false); - buf += parallel; - appendP(buf, esp_mail_str_35, false); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - if (!sendAttachments(smtp, msg, parallel, true)) - return setSendingResult(smtp, msg, false); - - appendP(buf, esp_mail_str_33, true); - buf += parallel; - appendP(buf, esp_mail_str_33, false); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return setSendingResult(smtp, msg, false); - - return true; -} - -bool ESP_Mail_Client::sendAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool parallel) -{ - std::string s; - std::string buf; - int cnt = 0; - - SMTP_Attachment *att = nullptr; - - size_t sz = msg->_att.size(); - if (parallel) - sz = msg->_parallel.size(); - - for (size_t i = 0; i < sz; i++) - { - if (parallel) - att = &msg->_parallel[i]; - else - att = &msg->_att[i]; - - if (att->_int.att_type == esp_mail_att_type_attachment) - { - appendP(s, esp_mail_str_261, true); - s += att->descr.filename; - - if (smtp->_sendCallback) - { - if (cnt > 0) - smtpCB(smtp, ""); - smtpCB(smtp, att->descr.filename); - } - - if (smtp->_debug) - esp_mail_debug(s.c_str()); - - cnt++; - - if (att->file.storage_type == esp_mail_file_storage_type_none) - { - if (!att->blob.data) - continue; - - if (smtp->_sendCallback) - smtpCB(smtp, att->descr.filename); - - if (smtp->_debug) - esp_mail_debug(s.c_str()); - - buf.clear(); - getAttachHeader(buf, boundary, att, att->blob.size); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!sendBlob(smtp, msg, att)) - return false; - - if (!bdat(smtp, msg, 2, false)) - return false; - - if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - } - else - { - - if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) - _sdOk = sdTest(); - - if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash) -#if defined(ESP32) - _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - _flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - - if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)) - { - - if (smtp->_sendCallback) - debugInfoP(esp_mail_str_158); - - if (smtp->_debug) - { - std::string e; - appendP(e, esp_mail_str_185, true); - appendP(e, esp_mail_str_158, false); - esp_mail_debug(e.c_str()); - } - - continue; - } - - if (openFileRead(smtp, msg, att, file, s, buf, boundary, false)) - { - if (file) - { - - if (!sendFile(smtp, msg, att, file)) - return false; - - if (!bdat(smtp, msg, 2, false)) - return false; - - if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - } - } - } - } - } - return true; -} - -bool ESP_Mail_Client::openFileRead(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file, std::string &s, std::string &buf, const std::string &boundary, bool inlined) -{ - bool file_existed = false; - std::string filepath; - - if (strlen(att->file.path) > 0) - { - if (att->file.path[0] != '/') - appendP(filepath, esp_mail_str_202, true); - filepath += att->file.path; - } - - if (att->file.storage_type == esp_mail_file_storage_type_sd) { - file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); - } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { - file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); - } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { - file_existed = ufsp->exists(filepath.c_str()); - } - - if (!file_existed) - { - - if (strlen(att->descr.filename) > 0) - { - filepath.clear(); - if (att->descr.filename[0] != '/') - appendP(filepath, esp_mail_str_202, true); - filepath += att->descr.filename; - } - - if (att->file.storage_type == esp_mail_file_storage_type_sd) { - file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); - } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { - file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); - } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { - file_existed = ufsp->exists(filepath.c_str()); - } - } - - if (!file_existed) - { - if (smtp->_sendCallback) - debugInfoP(esp_mail_str_158); - - if (smtp->_debug) - { - std::string e; - appendP(e, esp_mail_str_185, true); - appendP(e, esp_mail_str_158, false); - esp_mail_debug(e.c_str()); - } - } - - if (file_existed) - { - - buf.clear(); - - if (att->file.storage_type == esp_mail_file_storage_type_sd) { - file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ); - } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { -#if defined(ESP32) - file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ); -#elif defined(ESP8266) - file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r"); -#endif - } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { - file = ufsp->open(filepath.c_str(), "r"); - } - - if (!file) - return false; - - if (inlined) - getInlineHeader(buf, boundary, att, file.size()); - else - getAttachHeader(buf, boundary, att, file.size()); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - return true; - } - - return false; -} - -bool ESP_Mail_Client::openFileRead2(SMTPSession *smtp, SMTP_Message *msg, File &file, const char *path, esp_mail_file_storage_type storageType) -{ - bool file_existed = false; - std::string filepath; - - if (strlen(path) > 0) - { - if (path[0] != '/') - appendP(filepath, esp_mail_str_202, true); - filepath += path; - } - - if (storageType == esp_mail_file_storage_type_sd) { - file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); - } else if (storageType == esp_mail_file_storage_type_flash) { - file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); - } else if (storageType == esp_mail_file_storage_type_univ) { - file_existed = ufsp->exists(filepath.c_str()); - } - - if (!file_existed) - { - if (smtp->_sendCallback) - debugInfoP(esp_mail_str_158); - - if (smtp->_debug) - { - std::string e; - appendP(e, esp_mail_str_185, true); - appendP(e, esp_mail_str_158, false); - esp_mail_debug(e.c_str()); - } - } - - if (file_existed) - { - if (storageType == esp_mail_file_storage_type_sd) - file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ); - else if (storageType == esp_mail_file_storage_type_flash) -#if defined(ESP32) - file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ); -#elif defined(ESP8266) - file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r"); -#endif - return true; - } - - return false; -} - -bool ESP_Mail_Client::sendInline(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, byte type) -{ - size_t num = numAtt(smtp, esp_mail_att_type_inline, msg) > 0; - - if (num > 0) - { - if (smtp->_sendCallback) - { - smtpCB(smtp, ""); - smtpCBP(smtp, esp_mail_str_167); - } - - if (smtp->_debug) - debugInfoP(esp_mail_str_271); - } - - std::string s; - std::string buf; - std::string related = getBoundary(15); - int cnt = 0; - SMTP_Attachment *att = nullptr; - - appendP(s, esp_mail_str_33, true); - s += boundary; - appendP(s, esp_mail_str_34, false); - - appendP(s, esp_mail_str_298, false); - s += related; - appendP(s, esp_mail_str_35, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!sendPartText(smtp, msg, type, related.c_str())) - return false; - - if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_inline, msg) > 0) - esp_mail_debug(""); - - if (num > 0) - { - for (uint8_t i = 0; i < msg->_att.size(); i++) - { - att = &msg->_att[i]; - if (att->_int.att_type == esp_mail_att_type_inline) - { - appendP(s, esp_mail_str_261, true); - s += att->descr.filename; - - if (smtp->_sendCallback) - { - if (cnt > 0) - smtpCB(smtp, ""); - smtpCB(smtp, att->descr.filename); - } - - if (smtp->_debug) - esp_mail_debug(s.c_str()); - - cnt++; - - if (att->file.storage_type == esp_mail_file_storage_type_none) - { - if (!att->blob.data) - continue; - - if (smtp->_sendCallback) - smtpCB(smtp, att->descr.filename); - - if (smtp->_debug) - esp_mail_debug(s.c_str()); - - buf.clear(); - getInlineHeader(buf, related, att, att->blob.size); - - if (!bdat(smtp, msg, buf.length(), false)) - return false; - - if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!sendBlob(smtp, msg, att)) - return false; - - if (!bdat(smtp, msg, 2, false)) - return false; - - if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - } - else - { - if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) - _sdOk = sdTest(); - - if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash) -#if defined(ESP32) - _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - _flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - - if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)) - { - - if (smtp->_sendCallback) - debugInfoP(esp_mail_str_158); - - if (smtp->_debug) - { - std::string e; - appendP(e, esp_mail_str_185, true); - appendP(e, esp_mail_str_158, false); - esp_mail_debug(e.c_str()); - } - - continue; - } - - if (openFileRead(smtp, msg, att, file, s, buf, related, true)) - { - if (!file) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - return false; - } - - if (!sendFile(smtp, msg, att, file)) - return false; - - if (!bdat(smtp, msg, 2, false)) - return false; - - if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - } - } - } - } - } - - appendP(s, esp_mail_str_34, true); - appendP(s, esp_mail_str_33, false); - s += related; - appendP(s, esp_mail_str_33, false); - appendP(s, esp_mail_str_34, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - return true; -} - -void ESP_Mail_Client::errorStatusCB(SMTPSession *smtp, int error) -{ - smtp->_smtpStatus.statusCode = error; - std::string s; - - if (smtp->_sendCallback) - { - appendP(s, esp_mail_str_53, true); - s += smtp->errorReason().c_str(); - smtpCB(smtp, s.c_str(), false); - } - - if (smtp->_debug) - { - appendP(s, esp_mail_str_185, true); - s += smtp->errorReason().c_str(); - esp_mail_debug(s.c_str()); - } -} - -size_t ESP_Mail_Client::smtpSendP(SMTPSession *smtp, PGM_P v, bool newline) -{ - if (!reconnect(smtp)) - { - closeTCP(smtp); - return 0; - } - - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!smtp->_tcpConnected) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - char *tmp = strP(v); - size_t len = 0; - - if (newline) - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(tmp); - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_println(tmp); - else - len = smtp->httpClient.stream()->println(tmp); - } - else - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(tmp, false); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_print(tmp); - else - len = smtp->httpClient.stream()->print(tmp); - } - - if (len != strlen(tmp) && len != strlen(tmp) + 2) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - delS(tmp); - - return len; -} - -size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, const char *data, bool newline) -{ - if (!reconnect(smtp)) - { - closeTCP(smtp); - return 0; - } - - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!smtp->_tcpConnected) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - size_t len = 0; - - if (newline) - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(data); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_println(data); - else - len = smtp->httpClient.stream()->println(data); - } - else - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(data, false); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_print(data); - else - len = smtp->httpClient.stream()->print(data); - } - - if (len != strlen(data) && len != strlen(data) + 2) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - return len; -} - -size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, int data, bool newline) -{ - if (!reconnect(smtp)) - { - closeTCP(smtp); - return 0; - } - - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!smtp->_tcpConnected) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - char *tmp = intStr(data); - size_t len = 0; - - if (newline) - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug(tmp); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_println(tmp); - else - len = smtp->httpClient.stream()->println(tmp); - } - else - { - if (smtp->_debugLevel > esp_mail_debug_level_2) - esp_mail_debug_line(tmp, false); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._ns_print(tmp); - else - len = smtp->httpClient.stream()->print(tmp); - } - - if (len != strlen(tmp) && len != strlen(tmp) + 2) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - delS(tmp); - - return len; -} - -size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, uint8_t *data, size_t size) -{ - if (!reconnect(smtp)) - { - closeTCP(smtp); - return 0; - } - - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return 0; - } - - if (!smtp->_tcpConnected) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - return 0; - } - - size_t len = 0; - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - len = smtp->httpClient._stream()->write(data, size); - else - len = smtp->httpClient.stream()->write(data, size); - - if (len != size) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); - len = 0; - } - - return len; -} - -bool ESP_Mail_Client::handleSMTPError(SMTPSession *smtp, int err, bool ret) -{ - if (err < 0) - errorStatusCB(smtp, err); - - if (smtp->_tcpConnected) - closeTCP(smtp); - - return ret; -} - -void ESP_Mail_Client::debugInfoP(PGM_P info) -{ - char *tmp = strP(info); - esp_mail_debug(tmp); - delS(tmp); -} - -bool ESP_Mail_Client::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss) -{ - _sck = sck; - _miso = miso; - _mosi = mosi; - _ss = ss; - _sdConfigSet = true; -#if defined(ESP32) -#if defined(CARD_TYPE_SD) - SPI.begin(_sck, _miso, _mosi, _ss); - return ESP_MAIL_SD_FS.begin(_ss, SPI); -#endif -#elif defined(ESP8266) - return ESP_MAIL_SD_FS.begin(_ss); -#endif - return false; -} - -bool ESP_Mail_Client::sdBegin(void) -{ - _sdConfigSet = false; -#if defined(ESP32) - return ESP_MAIL_SD_FS.begin(); -#elif defined(ESP8266) - return ESP_MAIL_SD_FS.begin(SD_CS_PIN); -#endif - return false; -} - -bool ESP_Mail_Client::sdMMCBegin(const char *mountpoint, bool mode1bit, bool format_if_mount_failed) -{ -#if defined(ESP32) -#if defined(CARD_TYPE_SD_MMC) - _sdConfigSet = true; - sd_mmc_mountpoint = mountpoint; - sd_mmc_mode1bit = mode1bit; - sd_mmc_format_if_mount_failed = format_if_mount_failed; - return ESP_MAIL_SD_FS.begin(mountpoint, mode1bit, format_if_mount_failed); -#endif -#endif - return false; -} - -bool ESP_Mail_Client::sendPartText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, const char *boundary) -{ - std::string header; - - if (strlen(boundary) > 0) - { - appendP(header, esp_mail_str_33, false); - header += boundary; - appendP(header, esp_mail_str_34, false); - } - - if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) - { - if (strlen(msg->text.content_type) > 0) - { - appendP(header, esp_mail_str_25, false); - header += msg->text.content_type; - - if (strlen(msg->text.charSet) > 0) - { - appendP(header, esp_mail_str_97, false); - appendP(header, esp_mail_str_131, false); - appendP(header, esp_mail_str_168, false); - header += msg->text.charSet; - appendP(header, esp_mail_str_136, false); - } - - if (msg->text.flowed) - { - appendP(header, esp_mail_str_97, false); - appendP(header, esp_mail_str_131, false); - appendP(header, esp_mail_str_270, false); - - appendP(header, esp_mail_str_97, false); - appendP(header, esp_mail_str_131, false); - appendP(header, esp_mail_str_110, false); - } - - if (msg->text.embed.enable) - { - appendP(header, esp_mail_str_26, false); - appendP(header, esp_mail_str_164, false); - appendP(header, esp_mail_str_136, false); - char *tmp = getUID(); - msg->text._int.cid = tmp; - delS(tmp); - } - - appendP(header, esp_mail_str_34, false); - } - - if (strlen(msg->text.transfer_encoding) > 0) - { - appendP(header, esp_mail_str_272, false); - header += msg->text.transfer_encoding; - appendP(header, esp_mail_str_34, false); - } - } - else if (type == esp_mail_msg_type_html) - { - if (strlen(msg->text.content_type) > 0) - { - appendP(header, esp_mail_str_25, false); - header += msg->html.content_type; - - if (strlen(msg->html.charSet) > 0) - { - appendP(header, esp_mail_str_97, false); - appendP(header, esp_mail_str_131, false); - appendP(header, esp_mail_str_168, false); - header += msg->html.charSet; - appendP(header, esp_mail_str_136, false); - } - if (msg->html.embed.enable) - { - appendP(header, esp_mail_str_26, false); - appendP(header, esp_mail_str_159, false); - appendP(header, esp_mail_str_136, false); - char *tmp = getUID(); - msg->html._int.cid = tmp; - delS(tmp); - } - appendP(header, esp_mail_str_34, false); - } - - if (strlen(msg->html.transfer_encoding) > 0) - { - appendP(header, esp_mail_str_272, false); - header += msg->html.transfer_encoding; - appendP(header, esp_mail_str_34, false); - } - } - - if ((type == esp_mail_msg_type_html && msg->html.embed.enable) || ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable)) - { - - if ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable) - { - if (msg->text.embed.type == esp_mail_smtp_embed_message_type_attachment) - appendP(header, esp_mail_str_30, false); - else if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline) - appendP(header, esp_mail_str_299, false); - - if (strlen(msg->text.embed.filename) > 0) - header += msg->text.embed.filename; - else - appendP(header, esp_mail_str_164, false); - appendP(header, esp_mail_str_36, false); - - if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline) - { - appendP(header, esp_mail_str_300, false); - if (strlen(msg->text.embed.filename) > 0) - header += msg->text.embed.filename; - else - appendP(header, esp_mail_str_159, false); - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_301, false); - header += msg->text._int.cid; - appendP(header, esp_mail_str_15, false); - appendP(header, esp_mail_str_34, false); - } - } - else if (type == esp_mail_msg_type_html && msg->html.embed.enable) - { - if (msg->html.embed.type == esp_mail_smtp_embed_message_type_attachment) - appendP(header, esp_mail_str_30, false); - else if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline) - appendP(header, esp_mail_str_299, false); - - if (strlen(msg->html.embed.filename) > 0) - header += msg->html.embed.filename; - else - appendP(header, esp_mail_str_159, false); - appendP(header, esp_mail_str_36, false); - - if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline) - { - appendP(header, esp_mail_str_300, false); - if (strlen(msg->html.embed.filename) > 0) - header += msg->html.embed.filename; - else - appendP(header, esp_mail_str_159, false); - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_301, false); - header += msg->html._int.cid; - appendP(header, esp_mail_str_15, false); - appendP(header, esp_mail_str_34, false); - } - } - } - - appendP(header, esp_mail_str_34, false); - - if ((msg->text.blob.size > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (msg->html.blob.size > 0 && type == esp_mail_msg_type_html)) - { - if (!bdat(smtp, msg, header.length(), false)) - return false; - - if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - header.clear(); - - if (!sendBlobBody(smtp, msg, type)) - return false; - } - else if ((strlen(msg->text.file.name) > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (strlen(msg->html.file.name) > 0 && type == esp_mail_msg_type_html)) - { - if (!bdat(smtp, msg, header.length(), false)) - return false; - - if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - header.clear(); - - if (!sendFileBody(smtp, msg, type)) - return false; - } - else - encodingText(smtp, msg, type, header); - - appendP(header, esp_mail_str_34, false); - - if (strlen(boundary) > 0) - appendP(header, esp_mail_str_34, false); - - if (!bdat(smtp, msg, header.length(), false)) - return false; - - if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - return true; -} - -char *ESP_Mail_Client::getUID() -{ - char *tmp = new char[36]; - memset(tmp, 0, 36); - itoa(random(10000000, 20000000), tmp, 10); - return tmp; -} - -bool ESP_Mail_Client::sendBlobBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type) -{ - - if (msg->text.blob.size == 0 && msg->html.blob.size == 0) - return true; - - bool ret = true; - int bufLen = 512; - size_t pos = 0; - int pg = 0, _pg = 0; - - if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) - { - char *tmp = strP(esp_mail_str_325); - - if (strlen(msg->text.transfer_encoding) > 0) - { - if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - { - ret = sendBase64(smtp, msg, (const unsigned char *)msg->text.blob.data, msg->text.blob.size, true, tmp, smtp->_sendCallback != NULL); - delS(tmp); - return ret; - } - } - - int len = msg->text.blob.size; - int available = len; - uint8_t *buf = new uint8_t[bufLen + 1]; - while (available) - { - if (available > bufLen) - available = bufLen; - - memcpy_P(buf, msg->text.blob.data + pos, available); - - if (!bdat(smtp, msg, available, false)) - break; - if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - break; - pos += available; - len -= available; - available = len; - if (smtp->_sendCallback) - { - pg = (float)(100.0f * pos / msg->text.blob.size); - if (pg != _pg) - uploadReport(tmp, pg); - _pg = pg; - } - } - delete[] buf; - delS(tmp); - } - else if (type == esp_mail_message_type::esp_mail_msg_type_html) - { - char *tmp = strP(esp_mail_str_325); - - if (strlen(msg->html.transfer_encoding) > 0) - { - if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - { - ret = sendBase64(smtp, msg, (const unsigned char *)msg->html.blob.data, msg->html.blob.size, true, tmp, smtp->_sendCallback != NULL); - delS(tmp); - return ret; - } - } - int len = msg->html.blob.size; - int available = len; - uint8_t *buf = new uint8_t[bufLen + 1]; - while (available) - { - - if (available > bufLen) - available = bufLen; - - memcpy_P(buf, msg->html.blob.data + pos, available); - - if (!bdat(smtp, msg, available, false)) - { - ret = false; - break; - } - - if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - { - ret = false; - break; - } - pos += available; - len -= available; - available = len; - if (smtp->_sendCallback) - { - pg = (float)(100.0f * pos / msg->html.blob.size); - if (pg != _pg) - uploadReport(tmp, pg); - _pg = pg; - } - } - delete[] buf; - delS(tmp); - } - return ret; -} - -bool ESP_Mail_Client::sendFileBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type) -{ - - if (strlen(msg->text.file.name) == 0 && strlen(msg->html.file.name) == 0) - return true; - - bool ret = true; - size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; - size_t writeLen = 0; - int pg = 0, _pg = 0; - - if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) - { - - if (!openFileRead2(smtp, msg, file, msg->text.file.name, msg->text.file.type)) - return false; - - char *tmp = strP(esp_mail_str_326); - - if (strlen(msg->text.transfer_encoding) > 0) - { - if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - { - ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL); - delS(tmp); - return ret; - } - } - - if (file.size() > 0) - { - - if (file.size() < chunkSize) - chunkSize = file.size(); - uint8_t *buf = new uint8_t[chunkSize]; - while (writeLen < file.size() && file.available()) - { - if (writeLen > file.size() - chunkSize) - chunkSize = file.size() - writeLen; - size_t readLen = file.read(buf, chunkSize); - - if (readLen != chunkSize) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - break; - } - - if (!bdat(smtp, msg, chunkSize, false)) - { - ret = false; - break; - } - - if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - { - ret = false; - break; - } - - if (smtp->_sendCallback) - { - pg = (float)(100.0f * writeLen / file.size()); - if (pg != _pg) - uploadReport(tmp, pg); - _pg = pg; - } - writeLen += chunkSize; - } - delete[] buf; - if (smtp->_sendCallback && _pg < 100) - uploadReport(tmp, 100); - - delS(tmp); - return ret && writeLen == file.size(); - } - } - else if (type == esp_mail_message_type::esp_mail_msg_type_html) - { - - if (!openFileRead2(smtp, msg, file, msg->html.file.name, msg->html.file.type)) - return false; - - char *tmp = strP(esp_mail_str_326); - - if (strlen(msg->html.transfer_encoding) > 0) - { - if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - { - ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL); - delS(tmp); - return ret; - } - } - - if (file.size() > 0) - { - - if (file.size() < chunkSize) - chunkSize = file.size(); - uint8_t *buf = new uint8_t[chunkSize]; - while (writeLen < file.size() && file.available()) - { - if (writeLen > file.size() - chunkSize) - chunkSize = file.size() - writeLen; - size_t readLen = file.read(buf, chunkSize); - - if (readLen != chunkSize) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - break; - } - - if (!bdat(smtp, msg, chunkSize, false)) - { - ret = false; - break; - } - - if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - { - ret = false; - break; - } - - if (smtp->_sendCallback) - { - pg = (float)(100.0f * writeLen / file.size()); - if (pg != _pg) - uploadReport(tmp, pg); - _pg = pg; - } - writeLen += chunkSize; - } - delete[] buf; - if (smtp->_sendCallback && _pg < 100) - uploadReport(tmp, 100); - - delS(tmp); - return ret && writeLen == file.size(); - } - } - - return false; -} - -void ESP_Mail_Client::encodingText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, std::string &content) -{ - if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) - { - std::string s = msg->text.content; - - if (msg->text.flowed) - formatFlowedText(s); - - if (strlen(msg->text.transfer_encoding) > 0) - { - if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - content += encodeBase64Str((const unsigned char *)s.c_str(), s.length()); - else if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0) - { - char *out = newS(s.length() * 3 + 1); - encodeQP(s.c_str(), out); - content += out; - delS(out); - } - else - content += s; - } - else - content += s; - } - else if (type == esp_mail_message_type::esp_mail_msg_type_html) - { - char *tmp = nullptr; - std::string s = msg->html.content; - std::string fnd, rep; - SMTP_Attachment *att = nullptr; - for (uint8_t i = 0; i < msg->_att.size(); i++) - { - att = &msg->_att[i]; - if (att->_int.att_type == esp_mail_att_type_inline) - { - std::string filename(att->descr.filename); - - size_t found = filename.find_last_of("/\\"); - if (found != std::string::npos) - filename = filename.substr(found + 1); - - appendP(fnd, esp_mail_str_136, true); - fnd += filename; - appendP(fnd, esp_mail_str_136, false); - - appendP(rep, esp_mail_str_136, true); - appendP(rep, esp_mail_str_302, false); - if (strlen(att->descr.content_id) > 0) - rep += att->descr.content_id; - else - rep += att->_int.cid; - appendP(rep, esp_mail_str_136, false); - - tmp = strReplace((char *)s.c_str(), (char *)fnd.c_str(), (char *)rep.c_str()); - s = tmp; - delS(tmp); - } - } - - if (strlen(msg->html.transfer_encoding) > 0) - { - if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) - content += encodeBase64Str((const unsigned char *)s.c_str(), s.length()); - else if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0) - { - char *out = newS(strlen(msg->html.content) * 3 + 1); - encodeQP(msg->html.content, out); - content += out; - delS(out); - } - else - content += s; - } - else - content += s; - std::string().swap(s); - } -} - -/* Safe string splitter to avoid strsep bugs*/ -void ESP_Mail_Client::splitTk(std::string &str, std::vector &tk, const char *delim) -{ - std::size_t current, previous = 0; - current = str.find(delim, previous); - std::string s; - while (current != std::string::npos) - { - s = str.substr(previous, current - previous); - tk.push_back(s); - previous = current + strlen(delim); - current = str.find(delim, previous); - } - s = str.substr(previous, current - previous); - tk.push_back(s); - std::string().swap(s); -} - -/** Add the soft line break to the long text line (rfc 3676) - * and add Format=flowed parameter in the plain text content-type header. - * We use the existing white space as a part of this soft line break - * and set delSp="no" parameter to the header. - * - * Some servers are not rfc 3676 compliant. - * This causes the text lines are wrapped instead of joined. - * - * Some mail clients trim the space before the line break - * which makes the soft line break cannot be seen. -*/ -void ESP_Mail_Client::formatFlowedText(std::string &content) -{ - int count = 0; - std::string qms; - int j = 0; - std::vector tokens = std::vector(); - char *stk = strP(esp_mail_str_34); - char *qm = strP(esp_mail_str_15); - splitTk(content, tokens, stk); - content.clear(); - for (size_t i = 0; i < tokens.size(); i++) - { - if (tokens[i].length() > 0) - { - j = 0; - qms.clear(); - while (tokens[i][j] == qm[0]) - { - qms += qm; - j++; - } - softBreak(tokens[i], qms.c_str()); - if (count > 0) - content += stk; - content += tokens[i]; - } - else if (count > 0) - content += stk; - count++; - } - - delS(stk); - delS(qm); - tokens.clear(); -} - -void ESP_Mail_Client::softBreak(std::string &content, const char *quoteMarks) -{ - size_t len = 0; - char *stk = strP(esp_mail_str_131); - std::vector tokens = std::vector(); - splitTk(content, tokens, stk); - content.clear(); - for (size_t i = 0; i < tokens.size(); i++) - { - if (tokens[i].length() > 0) - { - if (len + tokens[i].length() + 3 > FLOWED_TEXT_LEN) - { - /* insert soft crlf */ - content += stk; - appendP(content, esp_mail_str_34, false); - - /* insert quote marks */ - if (strlen(quoteMarks) > 0) - content += quoteMarks; - content += tokens[i]; - len = tokens[i].length(); - } - else - { - if (len > 0) - { - content += stk; - len += strlen(stk); - } - content += tokens[i]; - len += tokens[i].length(); - } - } - } - delS(stk); - tokens.clear(); -} - -bool ESP_Mail_Client::sendMSG(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary) -{ - std::string alt = getBoundary(15); - std::string s; - - if (numAtt(smtp, esp_mail_att_type_inline, msg) > 0) - { - appendP(s, esp_mail_str_297, true); - s += alt; - appendP(s, esp_mail_str_35, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html) - { - if (!sendInline(smtp, msg, alt, msg->type)) - return false; - } - else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain)) - { - if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str())) - return false; - if (!sendInline(smtp, msg, alt, esp_mail_msg_type_html)) - return false; - } - - appendP(s, esp_mail_str_33, true); - s += alt; - appendP(s, esp_mail_str_33, false); - appendP(s, esp_mail_str_34, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - } - else - { - if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html) - { - if (!sendPartText(smtp, msg, msg->type, "")) - return false; - } - else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain)) - { - appendP(s, esp_mail_str_33, true); - s += boundary; - appendP(s, esp_mail_str_34, false); - appendP(s, esp_mail_str_297, false); - s += alt; - appendP(s, esp_mail_str_35, false); - - if (!bdat(smtp, msg, s.length(), false)) - return false; - - if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str())) - return false; - - if (!sendPartText(smtp, msg, esp_mail_msg_type_html, alt.c_str())) - return false; - } - } - return true; -} - -void ESP_Mail_Client::getInlineHeader(std::string &header, const std::string &boundary, SMTP_Attachment *inlineAttach, size_t size) -{ - appendP(header, esp_mail_str_33, false); - header += boundary; - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_25, false); - - if (strlen(inlineAttach->descr.mime) == 0) - { - std::string mime; - mimeFromFile(inlineAttach->descr.filename, mime); - if (mime.length() > 0) - header += mime; - else - appendP(header, esp_mail_str_32, false); - } - else - header += inlineAttach->descr.mime; - - appendP(header, esp_mail_str_26, false); - - std::string filename = inlineAttach->descr.filename; - - size_t found = filename.find_last_of("/\\"); - - if (found != std::string::npos) - filename = filename.substr(found + 1); - - header += filename; - appendP(header, esp_mail_str_36, false); - - appendP(header, esp_mail_str_299, false); - header += filename; - appendP(header, esp_mail_str_327, false); - char *tmp = intStr(size); - header += tmp; - delS(tmp); - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_300, false); - header += filename; - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_301, false); - if (strlen(inlineAttach->descr.content_id) > 0) - header += inlineAttach->descr.content_id; - else - header += inlineAttach->_int.cid; - - appendP(header, esp_mail_str_15, false); - - appendP(header, esp_mail_str_34, false); - - if (strlen(inlineAttach->descr.transfer_encoding) > 0) - { - appendP(header, esp_mail_str_272, false); - header += inlineAttach->descr.transfer_encoding; - appendP(header, esp_mail_str_34, false); - } - appendP(header, esp_mail_str_34, false); - - std::string().swap(filename); -} - -void ESP_Mail_Client::getAttachHeader(std::string &header, const std::string &boundary, SMTP_Attachment *attach, size_t size) -{ - appendP(header, esp_mail_str_33, false); - header += boundary; - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_25, false); - - if (strlen(attach->descr.mime) == 0) - { - std::string mime; - mimeFromFile(attach->descr.filename, mime); - if (mime.length() > 0) - header += mime; - else - appendP(header, esp_mail_str_32, false); - } - else - header += attach->descr.mime; - - appendP(header, esp_mail_str_26, false); - - std::string filename = attach->descr.filename; - - size_t found = filename.find_last_of("/\\"); - if (found != std::string::npos) - filename = filename.substr(found + 1); - - header += filename; - appendP(header, esp_mail_str_36, false); - - if (!attach->_int.parallel) - { - appendP(header, esp_mail_str_30, false); - header += filename; - appendP(header, esp_mail_str_327, false); - char *tmp = intStr(size); - header += tmp; - delS(tmp); - appendP(header, esp_mail_str_34, false); - } - - if (strlen(attach->descr.transfer_encoding) > 0) - { - appendP(header, esp_mail_str_272, false); - header += attach->descr.transfer_encoding; - appendP(header, esp_mail_str_34, false); - } - - appendP(header, esp_mail_str_34, false); - - std::string().swap(filename); -} - -void ESP_Mail_Client::getRFC822PartHeader(SMTPSession *smtp, std::string &header, const std::string &boundary) -{ - appendP(header, esp_mail_str_33, false); - header += boundary; - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_25, false); - - appendP(header, esp_mail_str_123, false); - appendP(header, esp_mail_str_34, false); - - appendP(header, esp_mail_str_98, false); - - appendP(header, esp_mail_str_34, false); -} - -void ESP_Mail_Client::smtpCBP(SMTPSession *smtp, PGM_P info, bool success) -{ - std::string s; - appendP(s, info, true); - smtp->_cbData._info = s; - smtp->_cbData._success = success; - smtp->_sendCallback(smtp->_cbData); - std::string().swap(s); -} - -void ESP_Mail_Client::smtpCB(SMTPSession *smtp, const char *info, bool success) -{ - smtp->_cbData._info = info; - smtp->_cbData._success = success; - smtp->_sendCallback(smtp->_cbData); -} - -void ESP_Mail_Client::imapCBP(IMAPSession *imap, PGM_P info, bool success) -{ - char *tmp = strP(info); - imap->_cbData._info = tmp; - imap->_cbData._success = success; - imap->_readCallback(imap->_cbData); - delS(tmp); -} - -void ESP_Mail_Client::imapCB(IMAPSession *imap, const char *info, bool success) -{ - imap->_cbData._info = info; - imap->_cbData._success = success; - imap->_readCallback(imap->_cbData); -} - -void ESP_Mail_Client::strcat_c(char *str, char c) -{ - for (; *str; str++) - ; - *str++ = c; - *str++ = 0; -} -int ESP_Mail_Client::strpos(const char *haystack, const char *needle, int offset) -{ - size_t len = strlen(haystack); - size_t len2 = strlen(needle); - if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len || offset < 0) - return -1; - char *_haystack = newS(len - offset + 1); - _haystack[len - offset] = 0; - strncpy(_haystack, haystack + offset, len - offset); - char *p = stristr(_haystack, needle); - int r = -1; - if (p) - r = p - _haystack + offset; - delS(_haystack); - return r; -} - -char *ESP_Mail_Client::stristr(const char *str1, const char *str2) -{ - const char *p1 = str1; - const char *p2 = str2; - const char *r = *p2 == 0 ? str1 : 0; - - while (*p1 != 0 && *p2 != 0) - { - if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) - { - if (r == 0) - r = p1; - p2++; - } - else - { - p2 = str2; - if (r != 0) - p1 = r + 1; - - if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) - { - r = p1; - p2++; - } - else - r = 0; - } - - p1++; - } - - return *p2 == 0 ? (char *)r : 0; -} - -char *ESP_Mail_Client::rstrstr(const char *haystack, const char *needle) -{ - size_t needle_length = strlen(needle); - const char *haystack_end = haystack + strlen(haystack) - needle_length; - const char *p; - size_t i; - for (p = haystack_end; p >= haystack; --p) - { - for (i = 0; i < needle_length; ++i) - { - if (p[i] != needle[i]) - goto next; - } - return (char *)p; - next:; - } - return 0; -} - -int ESP_Mail_Client::rstrpos(const char *haystack, const char *needle, int offset) -{ - size_t len = strlen(haystack); - size_t len2 = strlen(needle); - if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len) - return -1; - char *_haystack = newS(len - offset + 1); - _haystack[len - offset] = 0; - strncpy(_haystack, haystack + offset, len - offset); - char *p = rstrstr(_haystack, needle); - int r = -1; - if (p) - r = p - _haystack + offset; - delS(_haystack); - return r; -} - -int ESP_Mail_Client::readLine(WiFiClient *stream, char *buf, int bufLen, bool crlf, int &count) -{ - int ret = -1; - char c = 0; - char _c = 0; - int idx = 0; - if (!stream) - return idx; - while (stream->available() && idx < bufLen) - { - ret = stream->read(); - if (ret > -1) - { - if (idx >= bufLen - 1) - return idx; - - c = (char)ret; - strcat_c(buf, c); - idx++; - count++; - if (_c == '\r' && c == '\n') - { - if (!crlf) - { - buf[idx - 2] = 0; - idx -= 2; - } - return idx; - } - _c = c; - } - if (!stream) - return idx; - } - return idx; -} - -#if defined(ESP32) -int ESP_Mail_Client::_readLine(ESP_Mail_WCS32 *stream, char *buf, int bufLen, bool crlf, int &count) -#elif defined(ESP8266) -int ESP_Mail_Client::_readLine(ESP_Mail::ESP_Mail_WCS *stream, char *buf, int bufLen, bool crlf, int &count) -#endif -{ - int ret = -1; - char c = 0; - char _c = 0; - int idx = 0; - if (!stream) - return idx; - while (stream->_ns_available() && idx < bufLen) - { - ret = stream->_ns_read(); - if (ret > -1) - { - if (idx >= bufLen - 1) - return idx; - - c = (char)ret; - strcat_c(buf, c); - idx++; - count++; - if (_c == '\r' && c == '\n') - { - if (!crlf) - { - buf[idx - 2] = 0; - idx -= 2; - } - return idx; - } - _c = c; - } - if (!stream) - return idx; - } - return idx; -} - -int ESP_Mail_Client::getMSGNUM(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, bool &endSearch, int &nump, const char *key, const char *pc) -{ - int ret = -1; - char c = 0; - int idx = 0; - int num = 0; - while (available(imap) > 0 && idx < bufLen) - { - delay(0); - if (!imap->_secure) - ret = imap->httpClient._stream()->read(); - else - ret = imap->httpClient.stream()->read(); - - if (ret > -1) - { - - if (idx >= bufLen - 1) - return idx; - - c = (char)ret; - - if (c == '\n') - c = ' '; - - strcat_c(buf, c); - idx++; - - if (chunkIdx == 0) - { - if (strcmp(buf, key) == 0) - { - chunkIdx++; - return 0; - } - - if (strposP(buf, esp_mail_imap_response_1, 0) > -1) - goto end_search; - } - else - { - if (c == ' ') - { - imap->_mbif._searchCount++; - if (imap->_config->enable.recent_sort) - { - imap->_msgNum.push_back(atoi(buf)); - if (imap->_msgNum.size() > imap->_config->limit.search) - imap->_msgNum.erase(imap->_msgNum.begin()); - } - else - { - if (imap->_msgNum.size() < imap->_config->limit.search) - imap->_msgNum.push_back(atoi(buf)); - } - - if (imap->_debug) - { - num = (float)(100.0f * imap->_mbif._searchCount / imap->_mbif._msgCount); - if (nump != num) - { - nump = num; - searchReport(num, pc); - } - } - - chunkIdx++; - return idx; - } - else if (c == '$') - { - if (imap->_config->enable.recent_sort) - std::sort(imap->_msgNum.begin(), imap->_msgNum.end(), compFunc); - - goto end_search; - } - } - } - } - - return idx; - -end_search: - - endSearch = true; - int read = available(imap); - - if (!imap->_secure) - idx = imap->httpClient._stream()->readBytes(buf + idx, read); - else - idx = imap->httpClient.stream()->readBytes(buf + idx, read); - - return idx; -} - -struct esp_mail_message_part_info_t *ESP_Mail_Client::cPart(IMAPSession *imap) -{ - return &cHeader(imap)->part_headers[imap->_cPartIdx]; -} - -struct esp_mail_message_header_t *ESP_Mail_Client::cHeader(IMAPSession *imap) -{ - return &imap->_headers[cIdx(imap)]; -} - -void ESP_Mail_Client::handleHeader(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, struct esp_mail_message_header_t &header, int &headerState, int &octetCount) -{ - - char *tmp = nullptr; - if (chunkIdx == 0) - { - if (strposP(buf, esp_mail_str_324, 0) != -1 && buf[0] == '*') - chunkIdx++; - - tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); - if (tmp) - { - octetCount = 2; - header.header_data_len = atoi(tmp); - delS(tmp); - } - } - else - { - if (octetCount > header.header_data_len + 2) - return; - - if (strcmpP(buf, 0, esp_mail_str_10)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_from; - tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1); - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - else if (strcmpP(buf, 0, esp_mail_str_11)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_to; - tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1); - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - else if (strcmpP(buf, 0, esp_mail_str_276)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_cc; - tmp = subStr(buf, esp_mail_str_276, NULL, 0, -1); - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - else if (strcmpP(buf, 0, esp_mail_str_279)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_subject; - tmp = subStr(buf, esp_mail_str_279, NULL, 0, -1); - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - else if (strcmpP(buf, 0, esp_mail_str_25)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type; - tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); - if (tmp) - { - setHeader(imap, buf, header, headerState); - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_172)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding; - tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1); - if (tmp) - { - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_190)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_accept_language; - tmp = subStr(buf, esp_mail_str_190, NULL, 0, -1); - if (tmp) - { - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_191)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_language; - tmp = subStr(buf, esp_mail_str_191, NULL, 0, -1); - if (tmp) - { - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_273)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_date; - tmp = subStr(buf, esp_mail_str_273, NULL, 0, -1); - if (tmp) - { - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_274)) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_msg_id; - tmp = subStr(buf, esp_mail_str_274, NULL, 0, -1); - if (tmp) - { - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - chunkIdx++; - } -} - -void ESP_Mail_Client::setHeader(IMAPSession *imap, char *buf, struct esp_mail_message_header_t &header, int state) -{ - switch (state) - { - case esp_mail_imap_header_state::esp_mail_imap_state_from: - header.from += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_to: - header.to += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_cc: - header.cc += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_subject: - header.subject += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_content_type: - header.content_type += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding: - header.content_transfer_encoding += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_accept_language: - header.accept_language += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_content_language: - header.content_language += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_date: - header.date += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_msg_id: - header.message_id += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_char_set: - header.char_set += buf; - break; - case esp_mail_imap_header_state::esp_mail_imap_state_boundary: - header.boundary += buf; - break; - default: - break; - } -} - -void ESP_Mail_Client::handlePartHeader(IMAPSession *imap, char *buf, int &chunkIdx, struct esp_mail_message_part_info_t &part) -{ - char *tmp = nullptr; - if (chunkIdx == 0) - { - tmp = subStr(buf, esp_mail_imap_response_7, NULL, 0, -1); - if (tmp) - { - delS(tmp); - tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); - if (tmp) - { - chunkIdx++; - part.octetLen = atoi(tmp); - delS(tmp); - } - } - } - else - { - if (strcmpP(buf, 0, esp_mail_str_25)) - { - tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); - bool con_type = false; - if (tmp) - { - con_type = true; - part.content_type = tmp; - delS(tmp); - int p1 = strposP(part.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0); - if (p1 != -1) - { - p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1; - part.multipart = true; - //inline or embedded images - if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_related; - //multiple text formats e.g. plain, html, enriched - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative; - //medias - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel; - //rfc822 encapsulated - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest; - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_report; - //others can be attachments - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1) - part.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed; - } - - p1 = strposP(part.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0); - if (p1 != -1) - { - p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1; - if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1) - part.message_sub_type = esp_mail_imap_message_sub_type_rfc822; - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1) - part.message_sub_type = esp_mail_imap_message_sub_type_partial; - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1) - part.message_sub_type = esp_mail_imap_message_sub_type_external_body; - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1) - part.message_sub_type = esp_mail_imap_message_sub_type_delivery_status; - } - - p1 = strpos(part.content_type.c_str(), esp_mail_imap_descrete_media_type_t::text, 0); - if (p1 != -1) - { - p1 += strlen(esp_mail_imap_descrete_media_type_t::text) + 1; - if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::plain, p1) != -1) - part.msg_type = esp_mail_msg_type_plain; - else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::enriched, p1) != -1) - part.msg_type = esp_mail_msg_type_enriched; - else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::html, p1) != -1) - part.msg_type = esp_mail_msg_type_html; - else - part.msg_type = esp_mail_msg_type_plain; - } - } - - if (con_type) - { - if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched) - { - tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0); - if (tmp) - { - part.charset = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); - if (tmp) - { - part.charset = tmp; - delS(tmp); - } - } - - if (strposP(buf, esp_mail_str_275, 0) > -1 || strposP(buf, esp_mail_str_270, 0) > -1) - part.plain_flowed = true; - if (strposP(buf, esp_mail_str_259, 0) > -1 || strposP(buf, esp_mail_str_257, 0) > -1) - part.plain_delsp = true; - } - - if (part.charset.length() == 0) - { - tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0); - if (tmp) - { - part.charset = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); - if (tmp) - { - part.charset = tmp; - delS(tmp); - } - } - } - - tmp = subStr(buf, esp_mail_str_170, esp_mail_str_136, 0); - if (tmp) - { - part.name = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_171, NULL, 0, -1); - if (tmp) - { - part.name = tmp; - delS(tmp); - } - } - } - } - else if (strcmpP(buf, 0, esp_mail_str_172)) - { - tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1); - if (tmp) - { - part.content_transfer_encoding = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_174)) - { - tmp = subStr(buf, esp_mail_str_174, NULL, 0, -1); - if (tmp) - { - part.descr = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_175)) - { - tmp = subStr(buf, esp_mail_str_175, esp_mail_str_97, 0); - if (tmp) - { - //don't count altenative part text and html as embedded contents - if (cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative) - { - part.content_disposition = tmp; - if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::attachment) == 0) - part.attach_type = esp_mail_att_type_attachment; - else if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::inline_) == 0) - part.attach_type = esp_mail_att_type_inline; - } - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_150)) - { - tmp = subStr(buf, esp_mail_str_150, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.sender = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_10)) - { - tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.from = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_11)) - { - tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.to = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_12)) - { - tmp = subStr(buf, esp_mail_str_12, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.cc = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_184)) - { - tmp = subStr(buf, esp_mail_str_184, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.reply_to = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_134)) - { - tmp = subStr(buf, esp_mail_str_134, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.comment = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_24)) - { - tmp = subStr(buf, esp_mail_str_24, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.subject = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_189)) - { - tmp = subStr(buf, esp_mail_str_189, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.messageID = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_46)) - { - tmp = subStr(buf, esp_mail_str_46, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.return_path = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_99)) - { - tmp = subStr(buf, esp_mail_str_99, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.date = tmp; - delS(tmp); - } - } - else if (strcmpP(buf, 0, esp_mail_str_145)) - { - tmp = subStr(buf, esp_mail_str_145, NULL, 0, -1); - if (tmp) - { - part.rfc822_header.keyword = tmp; - delS(tmp); - } - } - - if (part.content_disposition.length() > 0) - { - tmp = subStr(buf, esp_mail_str_176, esp_mail_str_136, 0); - if (tmp) - { - part.filename = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_177, NULL, 0, -1); - if (tmp) - { - part.filename = tmp; - delS(tmp); - } - } - - tmp = subStr(buf, esp_mail_str_178, esp_mail_str_97, 0); - if (tmp) - { - part.attach_data_size = atoi(tmp); - delS(tmp); - cHeader(imap)->total_attach_data_size += part.attach_data_size; - part.sizeProp = true; - } - else - { - tmp = subStr(buf, esp_mail_str_178, NULL, 0, -1); - if (tmp) - { - part.attach_data_size = atoi(tmp); - delS(tmp); - cHeader(imap)->total_attach_data_size += part.attach_data_size; - part.sizeProp = true; - } - } - - tmp = subStr(buf, esp_mail_str_179, esp_mail_str_136, 0); - if (tmp) - { - part.creation_date = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_180, NULL, 0, -1); - if (tmp) - { - part.creation_date = tmp; - delS(tmp); - } - } - - tmp = subStr(buf, esp_mail_str_181, esp_mail_str_136, 0); - if (tmp) - { - part.modification_date = tmp; - delS(tmp); - } - else - { - tmp = subStr(buf, esp_mail_str_182, NULL, 0, -1); - if (tmp) - { - part.modification_date = tmp; - delS(tmp); - } - } - } - - chunkIdx++; - } -} - -char *ESP_Mail_Client::subStr(const char *buf, PGM_P beginH, PGM_P endH, int beginPos, int endPos) -{ - - char *tmp = nullptr; - int p1 = strposP(buf, beginH, beginPos); - if (p1 != -1) - { - int p2 = -1; - if (endPos == 0) - p2 = strposP(buf, endH, p1 + strlen_P(beginH)); - - if (p2 == -1) - p2 = strlen(buf); - - int len = p2 - p1 - strlen_P(beginH); - tmp = newS(len + 1); - memcpy(tmp, &buf[p1 + strlen_P(beginH)], len); - return tmp; - } - - return nullptr; -} - -void ESP_Mail_Client::handleAuth(SMTPSession *smtp, char *buf) -{ - if (strposP(buf, esp_mail_smtp_response_1, 0) > -1) - { - if (strposP(buf, esp_mail_smtp_response_2, 0) > -1) - smtp->_auth_capability.login = true; - if (strposP(buf, esp_mail_smtp_response_3, 0) > -1) - smtp->_auth_capability.plain = true; - if (strposP(buf, esp_mail_smtp_response_4, 0) > -1) - smtp->_auth_capability.xoauth2 = true; - if (strposP(buf, esp_mail_smtp_response_11, 0) > -1) - smtp->_auth_capability.cram_md5 = true; - if (strposP(buf, esp_mail_smtp_response_12, 0) > -1) - smtp->_auth_capability.digest_md5 = true; - } - else if (strposP(buf, esp_mail_smtp_response_5, 0) > -1) - smtp->_auth_capability.start_tls = true; - else if (strposP(buf, esp_mail_smtp_response_6, 0) > -1) - smtp->_send_capability._8bitMIME = true; - else if (strposP(buf, esp_mail_smtp_response_7, 0) > -1) - smtp->_send_capability.binaryMIME = true; - else if (strposP(buf, esp_mail_smtp_response_8, 0) > -1) - smtp->_send_capability.chunking = true; - else if (strposP(buf, esp_mail_smtp_response_9, 0) > -1) - smtp->_send_capability.utf8 = true; - else if (strposP(buf, esp_mail_smtp_response_10, 0) > -1) - smtp->_send_capability.pipelining = true; - else if (strposP(buf, esp_mail_smtp_response_13, 0) > -1) - smtp->_send_capability.dsn = true; -} - -int ESP_Mail_Client::available(SMTPSession *smtp) -{ - int sz = 0; - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - { - if (smtp->httpClient._stream()) - sz = smtp->httpClient._stream()->_ns_available(); - } - else - { - if (smtp->httpClient.stream()) - sz = smtp->httpClient.stream()->available(); - } - - return sz; -} - -bool ESP_Mail_Client::handleSMTPResponse(SMTPSession *smtp, esp_mail_smtp_status_code respCode, int errCode) -{ - if (!reconnect(smtp)) - return false; - - bool ret = false; - char *response = nullptr; - int readLen = 0; - long dataTime = millis(); - int chunkBufSize = 0; - std::string s, r; - int chunkIndex = 0; - int count = 0; - bool completedResponse = false; - smtp->_smtpStatus.statusCode = 0; - smtp->_smtpStatus.respCode = 0; - smtp->_smtpStatus.text.clear(); - uint8_t minResLen = 5; - struct esp_mail_smtp_response_status_t status; - - chunkBufSize = available(smtp); - - while (smtp->_tcpConnected && chunkBufSize <= 0) - { - if (!reconnect(smtp, dataTime)) - return false; - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return false; - } - chunkBufSize = available(smtp); - delay(0); - } - - dataTime = millis(); - - if (chunkBufSize > 1) - { - while (!completedResponse) - { - delay(0); - - if (!reconnect(smtp, dataTime)) - return false; - - if (!connected(smtp)) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return false; - } - - chunkBufSize = available(smtp); - - if (chunkBufSize <= 0) - break; - - if (chunkBufSize > 0) - { - chunkBufSize = 512; - response = newS(chunkBufSize + 1); - - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - readLen = _readLine(smtp->httpClient._stream(), response, chunkBufSize, false, count); - else - readLen = readLine(smtp->httpClient.stream(), response, chunkBufSize, false, count); - - if (readLen) - { - if (smtp->_smtp_cmd != esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state) - { - //sometimes server sent multiple lines response - //sometimes rx buffer may not ready for a while - if (strlen(response) < minResLen) - { - r += response; - chunkBufSize = 0; - while (chunkBufSize == 0) - { - delay(0); - if (!reconnect(smtp, dataTime)) - return false; - chunkBufSize = available(smtp); - } - } - else - { - if (r.length() > 0) - { - r += response; - memset(response, 0, chunkBufSize); - strcpy(response, r.c_str()); - } - - if (smtp->_debugLevel > esp_mail_debug_level_1) - esp_mail_debug((const char *)response); - } - - if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_greeting) - handleAuth(smtp, response); - } - - getResponseStatus(response, respCode, 0, status); - - //get the status code again for unexpected return code - if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls || status.respCode == 0) - getResponseStatus(response, esp_mail_smtp_status_code_0, 0, status); - - ret = respCode == status.respCode; - smtp->_smtpStatus = status; - - if (status.respCode > 0 && (status.respCode < 400 || status.respCode == respCode)) - ret = true; - - if (smtp->_debug && strlen(response) >= minResLen) - { - appendP(s, esp_mail_str_260, true); - if (smtp->_smtpStatus.respCode != esp_mail_smtp_status_code_334) - s += response; - else - { - //base64 response - size_t olen; - char *decoded = (char *)decodeBase64((const unsigned char *)status.text.c_str(), status.text.length(), &olen); - if (decoded && olen > 0) - { - olen += s.length(); - s += decoded; - s[olen] = 0; - delete[] decoded; - } - } - esp_mail_debug(s.c_str()); - r.clear(); - } - - completedResponse = smtp->_smtpStatus.respCode > 0 && status.text.length() > minResLen; - - if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_auth && smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_334) - { - if (authFailed(response, readLen, chunkIndex, 4)) - { - smtp->_smtpStatus.statusCode = -1; - ret = false; - } - } - - chunkIndex++; - - if (smtp->_chunkedEnable && smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_chunk_termination) - completedResponse = smtp->_chunkCount == chunkIndex; - } - delS(response); - } - } - - if (!ret) - handleSMTPError(smtp, errCode, false); - } - - return ret; -} - -void ESP_Mail_Client::getResponseStatus(const char *buf, esp_mail_smtp_status_code respCode, int beginPos, struct esp_mail_smtp_response_status_t &status) -{ - std::string s; - char *tmp = nullptr; - int p1 = 0; - if (respCode > esp_mail_smtp_status_code_0) - { - tmp = intStr((int)respCode); - s = tmp; - appendP(s, esp_mail_str_131, false); - delS(tmp); - p1 = strpos(buf, (const char *)s.c_str(), beginPos); - } - - if (p1 != -1) - { - int ofs = s.length() - 2; - if (ofs < 0) - ofs = 1; - - int p2 = strposP(buf, esp_mail_str_131, p1 + ofs); - - if (p2 < 4 && p2 > -1) - { - tmp = newS(p2 + 1); - memcpy(tmp, &buf[p1], p2); - status.respCode = atoi(tmp); - delS(tmp); - - p1 = p2 + 1; - p2 = strlen(buf); - if (p2 > p1) - { - tmp = newS(p2 + 1); - memcpy(tmp, &buf[p1], p2 - p1); - status.text = tmp; - delS(tmp); - } - } - } -} - -void ESP_Mail_Client::closeTCP(SMTPSession *smtp) -{ - - if (smtp->_tcpConnected) - { - if (smtp->httpClient.stream()) - { - if (connected(smtp)) - { - if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) - smtp->httpClient._stream()->stop(); - else - smtp->httpClient.stream()->stop(); - } - } - _lastReconnectMillis = millis(); - } - smtp->_tcpConnected = false; -} - -void ESP_Mail_Client::closeTCP(IMAPSession *imap) -{ - - if (imap->_tcpConnected) - { - if (imap->httpClient.stream()) - { - if (connected(imap)) - { - if (!imap->_secure) - imap->httpClient._stream()->stop(); - else - imap->httpClient.stream()->stop(); - } - } - _lastReconnectMillis = millis(); - } - imap->_tcpConnected = false; -} - -#if defined(ESP32) -void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient32 &httpClient, ESP_Mail_Session *session, std::shared_ptr caCert) -#elif defined(ESP8266) -void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient &httpClient, ESP_Mail_Session *session, std::shared_ptr caCert) -#endif -{ - -#if defined(ESP32) - if (httpClient._certType == -1) - { - if (strlen(session->certificate.cert_file) == 0) - { - if (caCert != nullptr) - httpClient.setCACert(caCert.get()); - else - httpClient.setCACert(nullptr); - } - else - { - httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type); - } - } -#elif defined(ESP8266) - - if (httpClient._certType == -1) - { - -#ifndef USING_AXTLS - if (!MailClient._clockReady && (strlen(session->certificate.cert_file) > 0 || caCert != nullptr)) - { - MailClient.setClock(MailClient._gmtOffset); - httpClient._clockReady = MailClient._clockReady; - } -#endif - if (strlen(session->certificate.cert_file) == 0) - { - if (caCert != nullptr) - httpClient.setCACert(caCert.get()); - else - httpClient.setCACert(nullptr); - } - else - { - httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type, MailClient._sdPin); - } - } -#endif -} - -bool ESP_Mail_Client::ethLinkUp() -{ - bool ret = false; -#if defined(ESP32) - char *ip = strP(esp_mail_str_328); - if (strcmp(ETH.localIP().toString().c_str(), ip) != 0) -// ret = ETH.linkUp(); - ret = true; - delS(ip); -#endif - return ret; -} - -bool ESP_Mail_Client::reconnect(SMTPSession *smtp, unsigned long dataTime) -{ - - bool status = WiFi.status() == WL_CONNECTED || ethLinkUp(); - - if (dataTime > 0) - { - if (millis() - dataTime > smtp->httpClient.tcpTimeout) - { - closeTCP(smtp); - errorStatusCB(smtp, MAIL_CLIENT_ERROR_READ_TIMEOUT); - return false; - } - } - - if (!status) - { - if (smtp->_tcpConnected) - closeTCP(smtp); - - errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); - - if (millis() - _lastReconnectMillis > _reconnectTimeout && !smtp->_tcpConnected) - { -#if defined(ESP32) - esp_wifi_connect(); -#elif defined(ESP8266) - WiFi.reconnect(); -#endif - _lastReconnectMillis = millis(); - } - - status = WiFi.status() == WL_CONNECTED || ethLinkUp(); - } - - return status; -} - -bool ESP_Mail_Client::reconnect(IMAPSession *imap, unsigned long dataTime, bool downloadRequest) -{ - - bool status = WiFi.status() == WL_CONNECTED || ethLinkUp(); - - if (dataTime > 0) - { - if (millis() - dataTime > imap->httpClient.tcpTimeout) - { - - closeTCP(imap); - - if (imap->_headers.size() > 0) - { - if (downloadRequest) - { - errorStatusCB(imap, IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT); - if (cHeader(imap)->part_headers.size() > 0) - cPart(imap)->download_error = imap->errorReason().c_str(); - } - else - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_READ_TIMEOUT); - cHeader(imap)->error_msg = imap->errorReason().c_str(); - } - } - return false; - } - } - - if (!status) - { - - if (imap->_tcpConnected) - closeTCP(imap); - - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - - if (imap->_headers.size() > 0) - { - if (downloadRequest) - cPart(imap)->download_error = imap->errorReason().c_str(); - else - cHeader(imap)->error_msg = imap->errorReason().c_str(); - } - - if (millis() - _lastReconnectMillis > _reconnectTimeout && !imap->_tcpConnected) - { -#if defined(ESP32) - esp_wifi_connect(); -#elif defined(ESP8266) - WiFi.reconnect(); -#endif - _lastReconnectMillis = millis(); - } - - status = WiFi.status() == WL_CONNECTED || ethLinkUp(); - } - - return status; -} - -void ESP_Mail_Client::delS(char *p) -{ - if (p != nullptr) - delete[] p; -} - -char *ESP_Mail_Client::newS(size_t len) -{ - char *p = new char[len]; - memset(p, 0, len); - return p; -} - -char *ESP_Mail_Client::newS(char *p, size_t len) -{ - delS(p); - p = newS(len); - return p; -} - -char *ESP_Mail_Client::newS(char *p, size_t len, char *d) -{ - delS(p); - p = newS(len); - strcpy(p, d); - return p; -} - -bool ESP_Mail_Client::strcmpP(const char *buf, int ofs, PGM_P beginH) -{ - char *tmp = nullptr; - if (ofs < 0) - { - int p = strposP(buf, beginH, 0); - if (p == -1) - return false; - ofs = p; - } - tmp = strP(beginH); - char *tmp2 = newS(strlen_P(beginH) + 1); - memcpy(tmp2, &buf[ofs], strlen_P(beginH)); - tmp2[strlen_P(beginH)] = 0; - bool ret = (strcasecmp(tmp, tmp2) == 0); - delS(tmp); - delS(tmp2); - return ret; -} - -int ESP_Mail_Client::strposP(const char *buf, PGM_P beginH, int ofs) -{ - char *tmp = strP(beginH); - int p = strpos(buf, tmp, ofs); - delS(tmp); - return p; -} - -char *ESP_Mail_Client::strP(PGM_P pgm) -{ - size_t len = strlen_P(pgm) + 1; - char *buf = newS(len); - strcpy_P(buf, pgm); - buf[len - 1] = 0; - return buf; -} - -void ESP_Mail_Client::appendP(std::string &buf, PGM_P p, bool empty) -{ - if (empty) - buf.clear(); - char *b = strP(p); - buf += b; - delS(b); -} - -char *ESP_Mail_Client::intStr(int value) -{ - char *buf = newS(36); - memset(buf, 0, 36); - itoa(value, buf, 10); - return buf; -} - -int ESP_Mail_Client::available(IMAPSession *imap) -{ - int sz = 0; - if (!imap->_secure) - { - if (imap->httpClient._stream()) - sz = imap->httpClient._stream()->_ns_available(); - } - else - { - if (imap->httpClient.stream()) - sz = imap->httpClient.stream()->available(); - } - return sz; -} - -bool ESP_Mail_Client::handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession) -{ - - if (!reconnect(imap)) - return false; - - esp_mail_imap_response_status imapResp = esp_mail_imap_response_status::esp_mail_imap_resp_unknown; - char *response = nullptr; - int readLen = 0; - long dataTime = millis(); - int chunkBufSize = available(imap); - int chunkIdx = 0; - std::string s; - bool completedResponse = false; - bool endSearch = false; - struct esp_mail_message_header_t header; - struct esp_mail_message_part_info_t part; - - std::string filePath = ""; - bool downloadRequest = false; - int reportState = 0; - int octetCount = 0; - int octetLength = 0; - int oCount = 0; - bool tmo = false; - int headerState = 0; - int scnt = 0; - int dcnt = -1; - char *skey = nullptr; - char *spc = nullptr; - char *lastBuf = nullptr; - char *tmp = nullptr; - bool crLF = imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text && strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31); - - while (imap->_tcpConnected && chunkBufSize <= 0) - { - if (!reconnect(imap, dataTime)) - return false; - if (!connected(imap)) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return false; - } - chunkBufSize = available(imap); - delay(0); - } - - dataTime = millis(); - - if (chunkBufSize > 1) - { - if (imap->_imap_cmd == esp_mail_imap_cmd_examine) - { - imap->_mbif.clear(); - imap->_mbif._msgCount = 0; - imap->_nextUID = ""; - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_search) - { - imap->_mbif._searchCount = 0; - imap->_msgNum.clear(); - } - - chunkBufSize = 512; - response = newS(chunkBufSize + 1); - - if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) - { - skey = strP(esp_mail_imap_response_6); - spc = strP(esp_mail_str_92); - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) - lastBuf = newS(BASE64_CHUNKED_LEN + 1); - - while (!completedResponse) - { - delay(0); - if (!reconnect(imap, dataTime) || !connected(imap)) - { - if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) - { - delS(skey); - delS(spc); - } - - if (!connected(imap)) - { - errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); - return false; - } - return false; - } - chunkBufSize = available(imap); - - if (chunkBufSize > 0) - { - chunkBufSize = 512; - - if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) - { - readLen = getMSGNUM(imap, response, chunkBufSize, chunkIdx, endSearch, scnt, skey, spc); - imap->_mbif._availableItems = imap->_msgNum.size(); - } - else - { - if (!imap->_secure) - readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, crLF, octetCount); - else - readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, crLF, octetCount); - } - - if (readLen) - { - - if (imap->_debugLevel > esp_mail_debug_level_1) - { - if (imap->_imap_cmd != esp_mail_imap_cmd_search && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_text && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_attachment && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_inline) - esp_mail_debug((const char *)response); - } - - if (imap->_imap_cmd != esp_mail_imap_cmd_search || (imap->_imap_cmd == esp_mail_imap_cmd_search && endSearch)) - imapResp = imapResponseStatus(imap, response); - - if (imapResp != esp_mail_imap_response_status::esp_mail_imap_resp_unknown) - { - if (imap->_debugLevel > esp_mail_debug_level_1) - { - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) - esp_mail_debug((const char *)response); - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_close) - completedResponse = true; - else - { - //some IMAP servers advertise CAPABILITY in their responses - //try to read the next available response - memset(response, 0, chunkBufSize); - if (!imap->_secure) - readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, true, octetCount); - else - readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, true, octetCount); - if (readLen) - { - completedResponse = false; - imapResp = imapResponseStatus(imap, response); - if (imapResp > esp_mail_imap_response_status::esp_mail_imap_resp_unknown) - completedResponse = true; - } - else - completedResponse = true; - } - } - else - { - if (imap->_imap_cmd == esp_mail_imap_cmd_auth) - { - if (authFailed(response, readLen, chunkIdx, 2)) - completedResponse = true; - } - else if (imap->_imap_cmd == esp_mail_imap_cmd_capability) - handleCapability(imap, response, chunkIdx); - else if (imap->_imap_cmd == esp_mail_imap_cmd_list) - handleFolders(imap, response); - else if (imap->_imap_cmd == esp_mail_imap_cmd_select || imap->_imap_cmd == esp_mail_imap_cmd_examine) - handleExamine(imap, response); - else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header) - { - char *tmp = intStr(cMSG(imap)); - header.message_uid = tmp; - delS(tmp); - - tmp = intStr(imap->_totalRead); - header.message_no = tmp; - delS(tmp); - - int _st = headerState; - handleHeader(imap, response, readLen, chunkIdx, header, headerState, octetCount); - if (_st == headerState && headerState > 0 && octetCount <= header.header_data_len) - setHeader(imap, response, header, headerState); - } - else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime) - handlePartHeader(imap, response, chunkIdx, part); - else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text) - decodeText(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetLength, octetCount, dcnt); - else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) - { - - if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) - { - //multi-line chunked base64 string attachment handle - if (octetCount < octetLength && readLen < BASE64_CHUNKED_LEN) - { - if (strlen(lastBuf) > 0) - { - tmp = newS(readLen + strlen(lastBuf) + 2); - strcpy(tmp, lastBuf); - strcat(tmp, response); - readLen = strlen(tmp); - tmo = handleAttachment(imap, tmp, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); - delS(tmp); - memset(lastBuf, 0, BASE64_CHUNKED_LEN + 1); - if (!tmo) - break; - } - else if (readLen < BASE64_CHUNKED_LEN + 1) - strcpy(lastBuf, response); - } - else - { - tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); - if (!tmo) - break; - } - } - else - tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); - } - dataTime = millis(); - } - } - memset(response, 0, chunkBufSize); - } - } - delS(response); - if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) - { - if (imap->_debug && scnt > 0 && scnt < 100) - searchReport(100, spc); - delS(skey); - delS(spc); - } - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment) - delS(lastBuf); - } - - if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header && header.header_data_len == 0) || imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no) - { - if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no) - imap->_imapStatus.statusCode = IMAP_STATUS_IMAP_RESPONSE_FAILED; - else - imap->_imapStatus.statusCode = IMAP_STATUS_NO_MESSAGE; - - if (imap->_readCallback) - { - std::string s; - appendP(s, esp_mail_str_53, true); - s += imap->errorReason().c_str(); - imapCB(imap, s.c_str(), false); - } - - if (imap->_debug) - { - std::string s; - appendP(s, esp_mail_str_185, true); - s += imap->errorReason().c_str(); - esp_mail_debug_line(s.c_str(), true); - } - - return false; - } - - if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_ok) - { - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header) - { - char *buf = newS(header.content_type.length() + 1); - strcpy(buf, header.content_type.c_str()); - header.content_type.clear(); - - tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); - if (tmp) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type; - setHeader(imap, tmp, header, headerState); - delS(tmp); - - int p1 = strposP(header.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0); - if (p1 != -1) - { - p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1; - header.multipart = true; - //inline or embedded images - if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_related; - //multiple text formats e.g. plain, html, enriched - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative; - //medias - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel; - //rfc822 encapsulated - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest; - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_report; - //others can be attachments - else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1) - header.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed; - } - - p1 = strposP(header.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0); - if (p1 != -1) - { - p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1; - if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1) - { - header.rfc822_part = true; - header.message_sub_type = esp_mail_imap_message_sub_type_rfc822; - } - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1) - header.message_sub_type = esp_mail_imap_message_sub_type_partial; - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1) - header.message_sub_type = esp_mail_imap_message_sub_type_external_body; - else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1) - header.message_sub_type = esp_mail_imap_message_sub_type_delivery_status; - } - - tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); - if (tmp) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_char_set; - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - - if (header.multipart) - { - if (strcmpP(buf, 0, esp_mail_str_277)) - { - tmp = subStr(buf, esp_mail_str_277, esp_mail_str_136, 0); - if (tmp) - { - headerState = esp_mail_imap_header_state::esp_mail_imap_state_boundary; - setHeader(imap, tmp, header, headerState); - delS(tmp); - } - } - } - } - - delS(buf); - - decodeHeader(header.from, header.from_charset); - decodeHeader(header.to, header.to_charset); - decodeHeader(header.cc, header.cc_charset); - decodeHeader(header.subject, header.subject_charset); - - imap->_headers.push_back(header); - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime) - { - //expect the octet length in the response for the existent part - if (part.octetLen > 0) - { - - part.partNumStr = cHeader(imap)->partNumStr; - part.partNumFetchStr = cHeader(imap)->partNumStr; - if (cHeader(imap)->part_headers.size() > 0) - { - - struct esp_mail_message_part_info_t *_part = &cHeader(imap)->part_headers[cHeader(imap)->part_headers.size() - 1]; - bool rfc822_body_subtype = _part->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - - if (rfc822_body_subtype) - { - if (!_part->rfc822_part) - { - //additional rfc822 message header, store it to the rfc822 part header - _part->rfc822_part = true; - _part->rfc822_header = part.rfc822_header; - imap->_rfc822_part_count++; - _part->rfc822_msg_Idx = imap->_rfc822_part_count; - } - } - } - - cHeader(imap)->part_headers.push_back(part); - cHeader(imap)->message_data_count = cHeader(imap)->part_headers.size(); - - if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched || part.msg_type == esp_mail_msg_type_html || part.attach_type == esp_mail_att_type_none || (part.attach_type == esp_mail_att_type_attachment && imap->_config->download.attachment) || (part.attach_type == esp_mail_att_type_inline && imap->_config->download.inlineImg)) - { - if (part.message_sub_type != esp_mail_imap_message_sub_type_rfc822) - { - if (part.attach_type != esp_mail_att_type_none && cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative) - cHeader(imap)->attachment_count++; - } - } - } - else - { - //nonexistent part - //return false to exit the loop without closing the connection - if (closeSession) - imap->closeSession(); - return false; - } - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) - { - if (cPart(imap)->file_open_write) - file.close(); - } - - if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text) - cPart(imap)->text[cPart(imap)->textLen] = 0; - } - else - { - //some server responses NO and should exit (false) from MIME feching loop without - //closing the session - if (imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_mime) - return handleIMAPError(imap, errCode, false); - - if (closeSession) - imap->closeSession(); - return false; - } - - return true; -} - -void ESP_Mail_Client::saveHeader(IMAPSession *imap) -{ - - std::string headerFilePath; - prepareFilePath(imap, headerFilePath, true); - if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk) - _sdOk = sdTest(); - else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk) -#if defined(ESP32) - _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - _flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - - if (_sdOk || _flashOk) - { - if (file) - file.close(); - - if (imap->_storageType == esp_mail_file_storage_type_sd) { - file = ESP_MAIL_SD_FS.open(headerFilePath.c_str(), FILE_WRITE); - } else if (imap->_storageType == esp_mail_file_storage_type_flash) { -#if defined(ESP32) - file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), FILE_WRITE); -#elif defined(ESP8266) - file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), "w"); -#endif - } else if (imap->_storageType == esp_mail_file_storage_type_univ) { - file = ufsp->open(headerFilePath.c_str(), "w"); - } - - if (file) - { - std::string s; - appendP(s, esp_mail_str_99, true); - file.print(s.c_str()); - file.println(cHeader(imap)->date.c_str()); - - appendP(s, esp_mail_str_100, true); - file.print(s.c_str()); - if (imap->_uidSearch) - file.println(cMSG(imap)); - else - file.println(); - - appendP(s, esp_mail_str_101, true); - file.print(s.c_str()); - file.println(cMSG(imap)); - - appendP(s, esp_mail_str_102, true); - file.print(s.c_str()); - file.println(cHeader(imap)->accept_language.c_str()); - - appendP(s, esp_mail_str_103, true); - file.print(s.c_str()); - file.println(cHeader(imap)->content_language.c_str()); - - appendP(s, esp_mail_str_10, true); - file.print(s.c_str()); - file.println(cHeader(imap)->from.c_str()); - - appendP(s, esp_mail_str_105, true); - file.print(s.c_str()); - file.println(cHeader(imap)->from_charset.c_str()); - - appendP(s, esp_mail_str_11, true); - file.print(s.c_str()); - file.println(cHeader(imap)->to.c_str()); - - appendP(s, esp_mail_str_107, true); - file.print(s.c_str()); - file.println(cHeader(imap)->to_charset.c_str()); - - appendP(s, esp_mail_str_108, true); - file.print(s.c_str()); - file.println(cHeader(imap)->cc.c_str()); - - appendP(s, esp_mail_str_109, true); - file.print(s.c_str()); - file.println(cHeader(imap)->cc_charset.c_str()); - - appendP(s, esp_mail_str_24, true); - file.print(s.c_str()); - file.println(cHeader(imap)->subject.c_str()); - - appendP(s, esp_mail_str_111, true); - file.print(s.c_str()); - file.println(cHeader(imap)->subject_charset.c_str()); - - appendP(s, esp_mail_str_112, true); - file.print(s.c_str()); - file.println(cPart(imap)->charset.c_str()); - - if (cHeader(imap)->attachment_count > 0) - { - - appendP(s, esp_mail_str_113, true); - file.print(s.c_str()); - file.println(cHeader(imap)->attachment_count); - - for (int j = 0; j < cHeader(imap)->attachment_count; j++) - { - if (imap->_headers[cIdx(imap)].part_headers[j].attach_type == esp_mail_att_type_none || imap->_headers[cIdx(imap)].part_headers[j].rfc822_part) - continue; - struct esp_mail_attacment_info_t att; - att.filename = imap->_headers[cIdx(imap)].part_headers[j].filename.c_str(); - att.mime = imap->_headers[cIdx(imap)].part_headers[j].content_type.c_str(); - att.name = imap->_headers[cIdx(imap)].part_headers[j].name.c_str(); - att.size = imap->_headers[cIdx(imap)].part_headers[j].attach_data_size; - att.creationDate = imap->_headers[cIdx(imap)].part_headers[j].creation_date.c_str(); - att.type = imap->_headers[cIdx(imap)].part_headers[j].attach_type; - - appendP(s, esp_mail_str_114, true); - file.print(s.c_str()); - file.println(j + 1); - - appendP(s, esp_mail_str_115, true); - file.print(s.c_str()); - file.println(att.filename); - - appendP(s, esp_mail_str_116, true); - file.print(s.c_str()); - file.println(att.name); - - appendP(s, esp_mail_str_117, true); - file.print(s.c_str()); - file.println(att.size); - - appendP(s, esp_mail_str_118, true); - file.print(s.c_str()); - file.println(att.mime); - - appendP(s, esp_mail_str_119, true); - file.print(s.c_str()); - file.println(att.creationDate); - } - } - - file.close(); - } - imap->_headerSaved = true; - } -} - -esp_mail_imap_response_status ESP_Mail_Client::imapResponseStatus(IMAPSession *imap, char *response) -{ - imap->_imapStatus.text.clear(); - if (strposP(response, esp_mail_imap_response_1, 0) > -1) - return esp_mail_imap_response_status::esp_mail_imap_resp_ok; - else if (strposP(response, esp_mail_imap_response_2, 0) > -1) - { - imap->_imapStatus.text = response; - imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_2)); - return esp_mail_imap_response_status::esp_mail_imap_resp_no; - } - else if (strposP(response, esp_mail_imap_response_3, 0) > -1) - { - imap->_imapStatus.text = response; - imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_3)); - return esp_mail_imap_response_status::esp_mail_imap_resp_bad; - } - return esp_mail_imap_response_status::esp_mail_imap_resp_unknown; -} - -void ESP_Mail_Client::decodeHeader(std::string &headerField, std::string &headerEnc) -{ - - size_t p1 = 0, p2 = 0; - - while (headerField[p1] == ' ' && p1 < headerField.length() - 1) - p1++; - - if (headerField[p1] == '=' && headerField[p1 + 1] == '?') - { - p2 = headerField.find("?", p1 + 2); - if (p2 != std::string::npos) - headerEnc = headerField.substr(p1 + 2, p2 - p1 - 2); - } - - int bufSize = 512; - char *buf = newS(bufSize); - - RFC2047Decoder.rfc2047Decode(buf, headerField.c_str(), bufSize); - - if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_iso8859_1) - { - int len = strlen(buf); - int olen = (len + 1) * 2; - unsigned char *out = (unsigned char *)newS(olen); - decodeLatin1_UTF8(out, &olen, (unsigned char *)buf, &len); - delS(buf); - buf = (char *)out; - } - else if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_tis620) - { - size_t len2 = strlen(buf); - char *tmp = newS((len2 + 1) * 3); - decodeTIS620_UTF8(tmp, buf, len2); - delS(buf); - buf = tmp; - } - - headerField = buf; - delS(buf); -} - -esp_mail_char_decoding_scheme ESP_Mail_Client::getEncodingFromCharset(const char *enc) -{ - esp_mail_char_decoding_scheme scheme = esp_mail_char_decoding_scheme_default; - - if (strposP(enc, esp_mail_str_237, 0) > -1 || strposP(enc, esp_mail_str_231, 0) > -1 || strposP(enc, esp_mail_str_226, 0) > -1) - scheme = esp_mail_char_decoding_scheme_tis620; - else if (strposP(enc, esp_mail_str_227, 0) > -1) - scheme = esp_mail_char_decoding_scheme_iso8859_1; - - return scheme; -} - -bool ESP_Mail_Client::handleAttachment(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetCount, int &octetLength, int &oCount, int &reportState, int &downloadCount) -{ - if (chunkIdx == 0) - { - char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); - if (tmp) - { - octetCount = 2; //CRLF counted from first line - octetLength = atoi(tmp); - delS(tmp); - chunkIdx++; - cHeader(imap)->total_download_size += octetLength; - } - return true; - } - - if (octetLength == 0) - return true; - - chunkIdx++; - - delay(0); - - if (!cPart(imap)->file_open_write) - { - - cPart(imap)->file_open_write = true; - - if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk) - _sdOk = sdTest(); - else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk) -#if defined(ESP32) - _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - _flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - - if (_sdOk || _flashOk) - { - - downloadRequest = true; - - filePath.clear(); - filePath += imap->_config->storage.saved_path; - appendP(filePath, esp_mail_str_202, false); - - char *tmp = intStr(cMSG(imap)); - filePath += tmp; - delS(tmp); - - if (imap->_storageType == esp_mail_file_storage_type_sd) - if (!ESP_MAIL_SD_FS.exists(filePath.c_str())) - createDirs(filePath); - - appendP(filePath, esp_mail_str_202, false); - - filePath += cPart(imap)->filename; - - if (imap->_storageType == esp_mail_file_storage_type_sd) - file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE); - else if (imap->_storageType == esp_mail_file_storage_type_flash) -#if defined(ESP32) - file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE); -#elif defined(ESP8266) - file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w"); -#endif - } - } - - if (_sdOk || _flashOk) - { - int nOctet = oCount + bufLen + 2; - if (nOctet > octetLength) - { - if (imap->_readCallback) - downloadReport(imap, 100); - - if (oCount < octetLength) - { - int dLen = nOctet - 2 - octetLength; - bufLen -= dLen; - buf[bufLen] = 0; - } - else - return true; - } - - oCount += bufLen + 2; - - if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) - { - - size_t olen = 0; - unsigned char *decoded = decodeBase64((const unsigned char *)buf, bufLen, &olen); - - if (decoded) - { - - if (!cPart(imap)->sizeProp) - { - cPart(imap)->attach_data_size += olen; - cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size; - } - - file.write((const uint8_t *)decoded, olen); - delay(0); - delete[] decoded; - - if (imap->_config->enable.download_status) - { - int p = 0; - if (cHeader(imap)->total_download_size > 0) - p = 100 * octetCount / cHeader(imap)->total_download_size; - - if ((p != downloadCount) && (p <= 100)) - { - downloadCount = p; - if (imap->_readCallback && reportState != -1) - downloadReport(imap, p); - reportState = -1; - } - else - reportState = 0; - } - } - - if (!reconnect(imap)) - return false; - } - else - { - //binary content - if (!cPart(imap)->sizeProp) - { - cPart(imap)->attach_data_size += bufLen; - cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size; - } - - file.write((const uint8_t *)buf, bufLen); - delay(0); - - if (imap->_config->enable.download_status) - { - int p = 0; - if (cHeader(imap)->total_download_size > 0) - p = 100 * octetCount / cHeader(imap)->total_download_size; - - if ((p != downloadCount) && (p <= 100)) - { - downloadCount = p; - if (imap->_readCallback && reportState != -1) - downloadReport(imap, p); - reportState = -1; - } - else - reportState = 0; - } - - if (!reconnect(imap)) - return false; - } - } - return true; -} - -void ESP_Mail_Client::downloadReport(IMAPSession *imap, int progress) -{ - if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) - { - std::string s; - char *tmp = intStr(progress); - appendP(s, esp_mail_str_90, true); - appendP(s, esp_mail_str_131, false); - s += cPart(imap)->filename; - appendP(s, esp_mail_str_91, false); - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_92, false); - appendP(s, esp_mail_str_34, false); - esp_mail_debug_line(s.c_str(), false); - std::string().swap(s); - } -} - -void ESP_Mail_Client::fetchReport(IMAPSession *imap, int progress, bool download) -{ - if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) - { - std::string s; - char *tmp = intStr(progress); - if (download) - appendP(s, esp_mail_str_90, true); - else - appendP(s, esp_mail_str_83, true); - appendP(s, esp_mail_str_131, false); - if (cPart(imap)->filename.length() > 0) - { - s += cPart(imap)->filename; - appendP(s, esp_mail_str_91, false); - } - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_92, false); - appendP(s, esp_mail_str_34, false); - esp_mail_debug_line(s.c_str(), false); - std::string().swap(s); - } -} - -void ESP_Mail_Client::searchReport(int progress, const char *percent) -{ - if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) - { - char *tmp = intStr(progress); - std::string s; - appendP(s, esp_mail_str_261, true); - s += tmp; - s += percent; - appendP(s, esp_mail_str_34, false); - esp_mail_debug_line(s.c_str(), false); - delS(tmp); - } -} - -void ESP_Mail_Client::uploadReport(const char *filename, int progress) -{ - if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) - { - std::string s; - char *tmp = intStr(progress); - appendP(s, esp_mail_str_160, true); - s += filename; - appendP(s, esp_mail_str_91, false); - s += tmp; - delS(tmp); - appendP(s, esp_mail_str_92, false); - appendP(s, esp_mail_str_34, false); - esp_mail_debug_line(s.c_str(), false); - std::string().swap(s); - } -} - -int ESP_Mail_Client::cMSG(IMAPSession *imap) -{ - return imap->_msgNum[cIdx(imap)]; -} - -int ESP_Mail_Client::cIdx(IMAPSession *imap) -{ - return imap->_cMsgIdx; -} - -void ESP_Mail_Client::decodeTIS620_UTF8(char *out, const char *in, size_t len) -{ - //output is the 3-byte value UTF-8 - int j = 0; - for (size_t i = 0; i < len; i++) - { - if (in[i] < 0x80) - out[j++] = in[i]; - else if ((in[i] >= 0xa0 && in[i] < 0xdb) || (in[i] > 0xde && in[i] < 0xfc)) - { - int unicode = 0x0e00 + in[i] - 0xa0; - out[j++] = 0xe0 | ((unicode >> 12) & 0xf); - out[j++] = 0x80 | ((unicode >> 6) & 0x3f); - out[j++] = 0x80 | (unicode & 0x3f); - } - } -} - -int ESP_Mail_Client::decodeLatin1_UTF8(unsigned char *out, int *outlen, const unsigned char *in, int *inlen) -{ - unsigned char *outstart = out; - const unsigned char *base = in; - const unsigned char *processed = in; - unsigned char *outend = out + *outlen; - const unsigned char *inend; - unsigned int c; - int bits; - - inend = in + (*inlen); - while ((in < inend) && (out - outstart + 5 < *outlen)) - { - c = *in++; - - /* assertion: c is a single UTF-4 value */ - if (out >= outend) - break; - if (c < 0x80) - { - *out++ = c; - bits = -6; - } - else - { - *out++ = ((c >> 6) & 0x1F) | 0xC0; - bits = 0; - } - - for (; bits >= 0; bits -= 6) - { - if (out >= outend) - break; - *out++ = ((c >> bits) & 0x3F) | 0x80; - } - processed = (const unsigned char *)in; - } - *outlen = out - outstart; - *inlen = processed - base; - return (0); -} - -void ESP_Mail_Client::decodeText(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetLength, int &octetCount, int &readCount) -{ - bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - if (chunkIdx == 0) - { - char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); - if (tmp) - { - octetCount = 2; - octetLength = atoi(tmp); - delS(tmp); - chunkIdx++; - cPart(imap)->octetLen = octetLength; - - if ((rfc822_body_subtype && imap->_config->download.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))) - prepareFilePath(imap, filePath, false); - - if (filePath.length() == 0) - { - if (!rfc822_body_subtype) - appendP(filePath, esp_mail_str_67, false); - else - { - appendP(filePath, esp_mail_str_82, false); - appendP(filePath, esp_mail_str_131, false); - appendP(filePath, esp_mail_str_67, false); - } - } - cPart(imap)->filename = filePath; - - return; - } - else - { - if (imap->_debug) - { - char *tmp = strP(esp_mail_str_280); - esp_mail_debug_line(tmp, false); - delS(tmp); - } - } - } - - delay(0); - - if (octetLength == 0) - return; - - if (imap->_config->download.rfc822 || imap->_config->download.html || imap->_config->download.text || (rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text)))) - { - if (imap->_readCallback && octetCount > octetLength + 2 && readCount < 100) - fetchReport(imap, 100, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))); - - if (octetCount <= octetLength + 2) - { - size_t olen = 0; - char *decoded = nullptr; - bool newC = true; - if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) - { - decoded = (char *)decodeBase64((const unsigned char *)buf, bufLen, &olen); - } - else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_278)) - { - decoded = newS(bufLen + 10); - decodeQP(buf, decoded); - olen = strlen(decoded); - } - else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_29)) - { - decoded = decode7Bit(buf); - olen = strlen(decoded); - } - else - { - //8bit and binary - newC = false; - decoded = buf; - olen = bufLen; - } - - if (decoded) - { - - if ((rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text)))) - { - - if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_iso8859_1) - { - int ilen = olen; - int olen2 = (ilen + 1) * 2; - unsigned char *tmp = (unsigned char *)newS(olen2); - decodeLatin1_UTF8(tmp, &olen2, (unsigned char *)decoded, &ilen); - delS(decoded); - olen = olen2; - decoded = (char *)tmp; - } - else if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_tis620) - { - char *out = newS((olen + 1) * 3); - delS(decoded); - decodeTIS620_UTF8(out, decoded, olen); - olen = strlen(out); - decoded = out; - } - - int p = 0; - - if (octetLength > 0) - p = 100 * octetCount / octetLength; - - if ((p != readCount) && (p <= 100)) - { - readCount = p; - if (imap->_readCallback) - fetchReport(imap, p, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))); - } - - if (cPart(imap)->text.length() < imap->_config->limit.msg_size) - { - - if (cPart(imap)->text.length() + olen < imap->_config->limit.msg_size) - { - cPart(imap)->textLen += olen; - cPart(imap)->text.append(decoded, olen); - } - else - { - int d = imap->_config->limit.msg_size - cPart(imap)->text.length(); - cPart(imap)->textLen += d; - if (d > 0) - cPart(imap)->text.append(decoded, d); - } - } - } - - if (filePath.length() > 0) - { - if (!cPart(imap)->file_open_write) - { - cPart(imap)->file_open_write = true; - - if (_sdOk || _flashOk) - { - downloadRequest = true; - - if (imap->_storageType == esp_mail_file_storage_type_sd) - file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE); - else if (imap->_storageType == esp_mail_file_storage_type_flash) -#if defined(ESP32) - file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE); -#elif defined(ESP8266) - file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w"); -#endif - } - } - - if (_sdOk || _flashOk) - file.write((const uint8_t *)decoded, olen); - } - - if (newC) - delS(decoded); - } - } - } -} -void ESP_Mail_Client::prepareFilePath(IMAPSession *imap, std::string &filePath, bool header) -{ - bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; - std::string fpath = imap->_config->storage.saved_path; - appendP(fpath, esp_mail_str_202, false); - char *tmp = intStr(cMSG(imap)); - fpath += tmp; - delS(tmp); - - if (imap->_storageType == esp_mail_file_storage_type_sd) - if (!ESP_MAIL_SD_FS.exists(fpath.c_str())) - createDirs(fpath); - - if (header) - { - appendP(fpath, esp_mail_str_203, false); - } - else - { - if (!rfc822_body_subtype) - { - appendP(fpath, esp_mail_str_161, false); - if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) - appendP(fpath, esp_mail_str_95, false); - else if (cPart(imap)->msg_type == esp_mail_msg_type_html) - appendP(fpath, esp_mail_str_94, false); - } - else - { - appendP(fpath, esp_mail_str_163, false); - - if (cPart(imap)->rfc822_msg_Idx > 0) - { - char *tmp = intStr(cPart(imap)->rfc822_msg_Idx); - fpath += tmp; - delS(tmp); - } - - if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) - appendP(fpath, esp_mail_str_95, false); - else if (cPart(imap)->msg_type == esp_mail_msg_type_html) - appendP(fpath, esp_mail_str_94, false); - else - //possible rfc822 encapsulated message which cannot fetch its header - appendP(fpath, esp_mail_str_40, false); - } - } - - filePath = fpath; -} - -char *ESP_Mail_Client::strReplace(char *orig, char *rep, char *with) -{ - char *result = nullptr; - char *ins = nullptr; - char *tmp = nullptr; - int len_rep; - int len_with; - int len_front; - int count; - - len_with = strlen(with); - len_rep = strlen(rep); - - ins = orig; - for (count = 0; (tmp = strstr(ins, rep)); ++count) - ins = tmp + len_rep; - - tmp = result = newS(strlen(orig) + (len_with - len_rep) * count + 1); - while (count--) - { - ins = strstr(orig, rep); - len_front = ins - orig; - tmp = strncpy(tmp, orig, len_front) + len_front; - tmp = strcpy(tmp, with) + len_with; - orig += len_front + len_rep; - } - strcpy(tmp, orig); - return result; -} - -int ESP_Mail_Client::decodeChar(const char *s) -{ - assert(s); - assert(*s == '='); - return 16 * hexval(*(s + 1)) + hexval(*(s + 2)); -} - -void ESP_Mail_Client::decodeQP(const char *buf, char *out) -{ - char *tmp = strP(esp_mail_str_295); - while (*buf) - { - if (*buf != '=') - strcat_c(out, *buf++); - else if (*(buf + 1) == '\r' && *(buf + 2) == '\n') - buf += 3; - else if (*(buf + 1) == '\n') - buf += 2; - else if (!strchr(tmp, *(buf + 1))) - strcat_c(out, *buf++); - else if (!strchr(tmp, *(buf + 2))) - strcat_c(out, *buf++); - else - { - strcat_c(out, decodeChar(buf)); - buf += 3; - } - } - delS(tmp); -} - -std::string ESP_Mail_Client::getBoundary(size_t len) -{ - char *tmp = strP(boundary_table); - char *buf = newS(len); - if (len) - { - --len; - buf[0] = tmp[0]; - buf[1] = tmp[1]; - for (size_t n = 2; n < len; n++) - { - int key = rand() % (int)(strlen(tmp) - 1); - buf[n] = tmp[key]; - } - buf[len] = '\0'; - } - std::string s = buf; - delS(buf); - delS(tmp); - return s; -} - -char *ESP_Mail_Client::decode7Bit(char *buf) -{ - char *out = strReplaceP(buf, imap_7bit_key1, imap_7bit_val1); - char *tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key2, imap_7bit_val2); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key3, imap_7bit_val3); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key4, imap_7bit_val4); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key5, imap_7bit_val5); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key6, imap_7bit_val6); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key7, imap_7bit_val7); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key8, imap_7bit_val8); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key9, imap_7bit_val9); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key10, imap_7bit_val10); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key11, imap_7bit_val11); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key12, imap_7bit_val12); - - delS(tmp); - tmp = newS(strlen(out) + 10); - strcpy(tmp, out); - delS(out); - out = strReplaceP(tmp, imap_7bit_key13, imap_7bit_val13); - delS(tmp); - return out; -} - -char *ESP_Mail_Client::strReplaceP(char *buf, PGM_P name, PGM_P value) -{ - char *n = strP(name); - char *v = strP(value); - char *out = strReplace(buf, n, v); - delS(n); - delS(v); - return out; -} - -void ESP_Mail_Client::handleCapability(IMAPSession *imap, char *buf, int &chunkIdx) -{ - if (chunkIdx == 0) - { - if (strposP(buf, esp_mail_imap_response_10, 0) > -1) - { - if (strposP(buf, esp_mail_imap_response_11, 0) > -1) - imap->_auth_capability.login = true; - if (strposP(buf, esp_mail_imap_response_12, 0) > -1) - imap->_auth_capability.plain = true; - if (strposP(buf, esp_mail_imap_response_13, 0) > -1) - imap->_auth_capability.xoauth2 = true; - if (strposP(buf, esp_mail_imap_response_14, 0) > -1) - imap->_auth_capability.start_tls = true; - if (strposP(buf, esp_mail_imap_response_15, 0) > -1) - imap->_auth_capability.cram_md5 = true; - if (strposP(buf, esp_mail_imap_response_16, 0) > -1) - imap->_auth_capability.digest_md5 = true; - } - } -} - -bool ESP_Mail_Client::authFailed(char *buf, int bufLen, int &chunkIdx, int ofs) -{ - bool ret = false; - if (chunkIdx == 0) - { - size_t olen; - unsigned char *decoded = decodeBase64((const unsigned char *)(buf + ofs), bufLen - ofs, &olen); - if (decoded) - { - ret = strposP((char *)decoded, esp_mail_str_294, 0) > -1; - delete[] decoded; - } - chunkIdx++; - } - return ret; -} - -void ESP_Mail_Client::handleFolders(IMAPSession *imap, char *buf) -{ - struct esp_mail_folder_info_t fd; - char *tmp = nullptr; - int p1 = strposP(buf, esp_mail_imap_response_4, 0); - int p2 = 0; - if (p1 != -1) - { - p1 = strposP(buf, esp_mail_str_198, 0); - if (p1 != -1) - { - p2 = strposP(buf, esp_mail_str_192, p1 + 1); - if (p2 != -1) - { - tmp = newS(p2 - p1); - strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); - if (tmp[p2 - p1 - 2] == '\r') - tmp[p2 - p1 - 2] = 0; - fd.attributes = tmp; - delS(tmp); - } - } - - p1 = strposP(buf, esp_mail_str_136, 0); - if (p1 != -1) - { - p2 = strposP(buf, esp_mail_str_136, p1 + 1); - if (p2 != -1) - { - tmp = newS(p2 - p1); - strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); - if (tmp[p2 - p1 - 2] == '\r') - tmp[p2 - p1 - 2] = 0; - fd.delimiter = tmp; - delS(tmp); - } - } - - p1 = strposP(buf, esp_mail_str_131, p2); - if (p1 != -1) - { - p2 = strlen(buf); - tmp = newS(p2 - p1); - if (buf[p1 + 1] == '"') - p1++; - strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); - if (tmp[p2 - p1 - 2] == '\r') - tmp[p2 - p1 - 2] = 0; - if (tmp[strlen(tmp) - 1] == '"') - tmp[strlen(tmp) - 1] = 0; - fd.name = tmp; - delS(tmp); - } - imap->_folders.add(fd); - } -} - -void ESP_Mail_Client::handleExamine(IMAPSession *imap, char *buf) -{ - char *tmp = nullptr; - int p1, p2; - - if (imap->_mbif._msgCount == 0) - { - p1 = strposP(buf, esp_mail_str_199, 0); - if (p1 != -1) - { - tmp = newS(p1); - strncpy(tmp, buf + 2, p1 - 1); - imap->_mbif._msgCount = atoi(tmp); - delS(tmp); - return; - } - } - - if (imap->_mbif._flags.size() == 0) - { - p1 = strposP(buf, esp_mail_imap_response_5, 0); - if (p1 != -1) - { - p1 = strposP(buf, esp_mail_str_198, 0); - if (p1 != -1) - { - p2 = strposP(buf, esp_mail_str_192, p1 + 1); - if (p2 != -1) - { - tmp = newS(p2 - p1); - strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); - char *stk = strP(esp_mail_str_131); - char *end_token; - char *token = strtok_r(tmp, stk, &end_token); - while (token != NULL) - { - imap->_mbif.addFlag(token); - token = strtok_r(NULL, stk, &end_token); - } - if (token) - delS(token); - delS(tmp); - delS(stk); - } - } - return; - } - } - - if (imap->_nextUID.length() == 0) - { - p1 = strposP(buf, esp_mail_str_200, 0); - if (p1 != -1) - { - p2 = strposP(buf, esp_mail_str_156, p1 + strlen_P(esp_mail_str_200)); - if (p2 != -1) - { - tmp = newS(p2 - p1 - strlen_P(esp_mail_str_200) + 1); - strncpy(tmp, buf + p1 + strlen_P(esp_mail_str_200), p2 - p1 - strlen_P(esp_mail_str_200)); - imap->_nextUID = tmp; - imap->_mbif._nextUID = atoi(tmp); - delS(tmp); - } - return; - } - } -} - -bool ESP_Mail_Client::handleIMAPError(IMAPSession *imap, int err, bool ret) -{ - if (err < 0) - { - errorStatusCB(imap, err); - - if (imap->_headers.size() > 0) - { - if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) && (imap->_config->download.attachment || imap->_config->download.inlineImg)) - { - if (cHeader(imap)->part_headers.size() > 0) - cPart(imap)->download_error = imap->errorReason().c_str(); - } - else - cHeader(imap)->error_msg = imap->errorReason().c_str(); - - cHeader(imap)->error = true; - } - } - - if (imap->_tcpConnected) - closeTCP(imap); - - imap->_cbData.empty(); - - return ret; -} - -unsigned char *ESP_Mail_Client::decodeBase64(const unsigned char *src, size_t len, size_t *out_len) -{ - - unsigned char *out, *pos, block[4], tmp; - size_t i, count, olen; - int pad = 0; - size_t extra_pad; - - unsigned char *dtable = new unsigned char[256]; - - memset(dtable, 0x80, 256); - - for (i = 0; i < sizeof(b64_index_table) - 1; i++) - dtable[b64_index_table[i]] = (unsigned char)i; - dtable['='] = 0; - - count = 0; - for (i = 0; i < len; i++) - { - if (dtable[src[i]] != 0x80) - count++; - } - - if (count == 0) - goto exit; - extra_pad = (4 - count % 4) % 4; - - olen = (count + extra_pad) / 4 * 3; - pos = out = (unsigned char *)malloc(olen); - if (out == NULL) - goto exit; - - count = 0; - for (i = 0; i < len + extra_pad; i++) - { - unsigned char val; - - if (i >= len) - val = '='; - else - val = src[i]; - tmp = dtable[val]; - if (tmp == 0x80) - continue; - - if (val == '=') - pad++; - block[count] = tmp; - count++; - if (count == 4) - { - *pos++ = (block[0] << 2) | (block[1] >> 4); - *pos++ = (block[1] << 4) | (block[2] >> 2); - *pos++ = (block[2] << 6) | block[3]; - count = 0; - if (pad) - { - if (pad == 1) - pos--; - else if (pad == 2) - pos -= 2; - else - { - free(out); - goto exit; - } - break; - } - } - } - - *out_len = pos - out; - delete[] dtable; - return out; -exit: - delete[] dtable; - return nullptr; -} - -std::string ESP_Mail_Client::encodeBase64Str(const unsigned char *src, size_t len) -{ - return encodeBase64Str((uint8_t *)src, len); -} - -std::string ESP_Mail_Client::encodeBase64Str(uint8_t *src, size_t len) -{ - std::string outStr; - unsigned char *out, *pos; - const unsigned char *end, *in; - size_t olen = 4 * ((len + 2) / 3); - if (olen < len) - return outStr; - - outStr.resize(olen); - out = (unsigned char *)&outStr[0]; - - end = src + len; - in = src; - pos = out; - - while (end - in >= 3) - { - *pos++ = b64_index_table[in[0] >> 2]; - *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; - *pos++ = b64_index_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; - *pos++ = b64_index_table[in[2] & 0x3f]; - in += 3; - } - - if (end - in) - { - *pos++ = b64_index_table[in[0] >> 2]; - if (end - in == 1) - { - *pos++ = b64_index_table[(in[0] & 0x03) << 4]; - *pos++ = '='; - } - else - { - *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; - *pos++ = b64_index_table[(in[1] & 0x0f) << 2]; - } - *pos++ = '='; - } - - return outStr; -} - -void ESP_Mail_Client::encodeQP(const char *buf, char *out) -{ - int n = 0, p = 0, pos = 0; - assert(buf); - for (n = 0; *buf; buf++) - { - if (n >= 73 && *buf != 10 && *buf != 13) - { - p = sprintf(out + pos, "=\r\n"); - pos += p; - n = 0; - } - - if (*buf == 10 || *buf == 13) - { - strcat_c(out, *buf); - pos++; - n = 0; - } - else if (*buf < 32 || *buf == 61 || *buf > 126) - { - p = sprintf(out + pos, "=%02X", (unsigned char)*buf); - n += p; - pos += p; - } - else if (*buf != 32 || (*(buf + 1) != 10 && *(buf + 1) != 13)) - { - strcat_c(out, *buf); - n++; - pos++; - } - else - { - p = sprintf(out + pos, "=20"); - n += p; - pos += p; - } - } -} - -bool ESP_Mail_Client::sendBase64(SMTPSession *smtp, SMTP_Message *msg, const unsigned char *data, size_t len, bool flashMem, const char *filename, bool report) -{ - bool ret = false; - const unsigned char *end, *in; - - size_t olen = 4 * ((len + 2) / 3); - - if (olen < len) - return false; - - end = data + len; - in = data; - - size_t chunkSize = 936; - size_t byteAdded = 0; - size_t byteSent = 0; - - int dByte = 0; - unsigned char *buf = new unsigned char[chunkSize]; - memset(buf, 0, chunkSize); - - unsigned char *tmp = new unsigned char[3]; - int bcnt = 0; - int pg = 0, _pg = 0; - - if (report) - uploadReport(filename, bcnt); - - while (end - in >= 3) - { - - memset(tmp, 0, 3); - if (flashMem) - memcpy_P(tmp, in, 3); - else - memcpy(tmp, in, 3); - bcnt += 3; - - buf[byteAdded++] = b64_index_table[tmp[0] >> 2]; - buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)]; - buf[byteAdded++] = b64_index_table[((tmp[1] & 0x0f) << 2) | (tmp[2] >> 6)]; - buf[byteAdded++] = b64_index_table[tmp[2] & 0x3f]; - dByte += 4; - if (dByte == BASE64_CHUNKED_LEN) - { - if (byteAdded + 1 < chunkSize) - { - buf[byteAdded++] = 0x0d; - buf[byteAdded++] = 0x0a; - } - dByte = 0; - } - if (byteAdded >= chunkSize - 4) - { - byteSent += byteAdded; - - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - memset(buf, 0, chunkSize); - byteAdded = 0; - } - in += 3; - - if (report) - { - pg = (float)(100.0f * bcnt / len); - if (pg != _pg) - uploadReport(filename, pg); - _pg = pg; - } - } - - if (byteAdded > 0) - { - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - } - - if (end - in) - { - memset(buf, 0, chunkSize); - byteAdded = 0; - memset(tmp, 0, 3); - if (flashMem) - { - if (end - in == 1) - memcpy_P(tmp, in, 1); - else - memcpy_P(tmp, in, 2); - } - else - { - if (end - in == 1) - memcpy(tmp, in, 1); - else - memcpy(tmp, in, 2); - } - - buf[byteAdded++] = b64_index_table[tmp[0] >> 2]; - if (end - in == 1) - { - buf[byteAdded++] = b64_index_table[(tmp[0] & 0x03) << 4]; - buf[byteAdded++] = '='; - } - else - { - buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)]; - buf[byteAdded++] = b64_index_table[(tmp[1] & 0x0f) << 2]; - } - buf[byteAdded++] = '='; - - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - memset(buf, 0, chunkSize); - } - - if (report && _pg < 100) - uploadReport(filename, 100); - - ret = true; -ex: - if (report) - esp_mail_debug(""); - delete[] tmp; - delete[] buf; - return ret; -} - -bool ESP_Mail_Client::sendBase64Stream(SMTPSession *smtp, SMTP_Message *msg, File file, const char *filename, bool report) -{ - bool ret = false; - - if (!file) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - return false; - } - - size_t chunkSize = 936; - size_t byteAdded = 0; - size_t byteSent = 0; - - unsigned char *buf = new unsigned char[chunkSize]; - memset(buf, 0, chunkSize); - - size_t len = file.size(); - size_t fbufIndex = 0; - unsigned char *fbuf = new unsigned char[3]; - - int dByte = 0; - - int bcnt = 0; - int pg = 0, _pg = 0; - - if (report) - uploadReport(filename, bcnt); - - while (file.available()) - { - memset(fbuf, 0, 3); - if (len - fbufIndex >= 3) - { - bcnt += 3; - size_t readLen = file.read(fbuf, 3); - if (readLen != 3) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - break; - } - - buf[byteAdded++] = b64_index_table[fbuf[0] >> 2]; - buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; - buf[byteAdded++] = b64_index_table[((fbuf[1] & 0x0f) << 2) | (fbuf[2] >> 6)]; - buf[byteAdded++] = b64_index_table[fbuf[2] & 0x3f]; - dByte += 4; - if (dByte == BASE64_CHUNKED_LEN) - { - if (byteAdded + 1 < chunkSize) - { - buf[byteAdded++] = 0x0d; - buf[byteAdded++] = 0x0a; - } - dByte = 0; - } - if (byteAdded >= chunkSize - 4) - { - byteSent += byteAdded; - - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - - memset(buf, 0, chunkSize); - byteAdded = 0; - } - fbufIndex += 3; - - if (report) - { - pg = (float)(100.0f * bcnt / len); - if (pg != _pg) - uploadReport(filename, pg); - _pg = pg; - } - } - else - { - size_t readLen = file.read(fbuf, len - fbufIndex); - if (readLen != len - fbufIndex) - { - errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); - break; - } - } - } - - file.close(); - if (byteAdded > 0) - { - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - } - - if (len - fbufIndex > 0) - { - memset(buf, 0, chunkSize); - byteAdded = 0; - buf[byteAdded++] = b64_index_table[fbuf[0] >> 2]; - if (len - fbufIndex == 1) - { - buf[byteAdded++] = b64_index_table[(fbuf[0] & 0x03) << 4]; - buf[byteAdded++] = '='; - } - else - { - buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; - buf[byteAdded++] = b64_index_table[(fbuf[1] & 0x0f) << 2]; - } - buf[byteAdded++] = '='; - - if (!bdat(smtp, msg, byteAdded, false)) - goto ex; - - if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - goto ex; - } - ret = true; - - if (report && _pg < 100) - uploadReport(filename, 100); - -ex: - delete[] buf; - delete[] fbuf; - file.close(); - return ret; -} - -IMAPSession::IMAPSession() {} -IMAPSession::~IMAPSession() -{ - empty(); - _caCert.reset(); - _caCert = nullptr; -} - -bool IMAPSession::closeSession() -{ - if (!_tcpConnected) - return false; -#if defined(ESP32) - /** - * The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure - * The client disposed without memory released after the server close - * the connection due to LOGOUT command, which caused the memory leaks. - */ - if (!MailClient.imapLogout(this)) - return false; -#endif - return MailClient.handleIMAPError(this, 0, true); -} - -bool IMAPSession::connect(ESP_Mail_Session *sesssion, IMAP_Config *config) -{ - if (_tcpConnected) - MailClient.closeTCP(this); - - _sesson_cfg = sesssion; - _config = config; - _caCert = nullptr; - - if (strlen(_sesson_cfg->certificate.cert_data) > 0) - _caCert = std::shared_ptr(_sesson_cfg->certificate.cert_data); - - if (strlen(_sesson_cfg->certificate.cert_file) > 0) - { - if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk) - MailClient._sdOk = MailClient.sdTest(); - if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk) -#if defined(ESP32) - MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - } - - return MailClient.imapAuth(this); -} - -void IMAPSession::debug(int level) -{ - if (level > esp_mail_debug_level_0) - { - if (level > esp_mail_debug_level_3) - level = esp_mail_debug_level_1; - _debugLevel = level; - _debug = true; - } - else - { - _debugLevel = esp_mail_debug_level_0; - _debug = false; - } -} - -String IMAPSession::errorReason() -{ - std::string ret; - - if (_imapStatus.text.length() > 0) - return _imapStatus.text.c_str(); - - switch (_imapStatus.statusCode) - { - case IMAP_STATUS_SERVER_CONNECT_FAILED: - MailClient.appendP(ret, esp_mail_str_38, true); - break; - case MAIL_CLIENT_ERROR_CONNECTION_LOST: - MailClient.appendP(ret, esp_mail_str_221, true); - break; - case MAIL_CLIENT_ERROR_READ_TIMEOUT: - MailClient.appendP(ret, esp_mail_str_258, true); - break; - case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED: - MailClient.appendP(ret, esp_mail_str_305, true); - break; - case IMAP_STATUS_NO_MESSAGE: - MailClient.appendP(ret, esp_mail_str_306, true); - break; - case IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT: - MailClient.appendP(ret, esp_mail_str_93, true); - break; - case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP: - MailClient.appendP(ret, esp_mail_str_132, true); - break; - case IMAP_STATUS_CLOSE_MAILBOX_FAILED: - MailClient.appendP(ret, esp_mail_str_188, true); - break; - case IMAP_STATUS_OPEN_MAILBOX_FAILED: - MailClient.appendP(ret, esp_mail_str_281, true); - break; - case IMAP_STATUS_LIST_MAILBOXS_FAILED: - MailClient.appendP(ret, esp_mail_str_62, true); - break; - case IMAP_STATUS_NO_SUPPORTED_AUTH: - MailClient.appendP(ret, esp_mail_str_42, true); - break; - case IMAP_STATUS_CHECK_CAPABILITIES_FAILED: - MailClient.appendP(ret, esp_mail_str_63, true); - break; - case MAIL_CLIENT_ERROR_OUT_OF_MEMORY: - MailClient.appendP(ret, esp_mail_str_186, true); - break; - case IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED: - MailClient.appendP(ret, esp_mail_str_153, true); - break; - - default: - break; - } - return ret.c_str(); -} - -bool IMAPSession::selectFolder(const char *folderName, bool readOnly) -{ - if (_tcpConnected) - { - if (!openFolder(folderName, readOnly)) - return false; - } - else - { - _currentFolder = folderName; - } - return true; -} - -bool IMAPSession::openFolder(const char *folderName, bool readOnly) -{ - if (!_tcpConnected) - return false; - if (readOnly) - return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_examine, true); - else - return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_select, true); -} - -bool IMAPSession::getFolders(FoldersCollection &folders) -{ - if (!_tcpConnected) - return false; - return getMailboxes(folders); -} - -bool IMAPSession::closeFolder(const char *folderName) -{ - if (!_tcpConnected) - return false; - return closeMailbox(); -} - -void IMAPSession::checkUID() -{ - if (MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_140) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_212) || - MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_213) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_214) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_215) || - MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_216) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_217) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_218) || - MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_219) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_220)) - _config->fetch.uid = "*"; -} - -void IMAPSession::checkPath() -{ - std::string path = _config->storage.saved_path; - if (path[0] != '/') - { - path = "/"; - path += _config->storage.saved_path; - path = path.c_str(); - } -} - -bool IMAPSession::headerOnly() -{ - return _headerOnly; -} - -struct esp_mail_imap_msg_list_t IMAPSession::data() -{ - struct esp_mail_imap_msg_list_t ret; - - for (size_t i = 0; i < _headers.size(); i++) - { - if (ESP.getFreeHeap() < ESP_MAIL_MIN_MEM) - continue; - - struct esp_mail_imap_msg_item_t itm; - - itm.UID = _headers[i].message_uid.c_str(); - itm.ID = _headers[i].message_id.c_str(); - itm.msgNo = _headers[i].message_no.c_str(); - itm.from = _headers[i].from.c_str(); - itm.fromCharset = _headers[i].from_charset.c_str(); - itm.to = _headers[i].to.c_str(); - itm.toCharset = _headers[i].to_charset.c_str(); - itm.cc = _headers[i].cc.c_str(); - itm.ccCharset = _headers[i].cc_charset.c_str(); - itm.subject = _headers[i].subject.c_str(); - itm.subjectCharset = _headers[i].subject_charset.c_str(); - itm.date = _headers[i].date.c_str(); - itm.fetchError = _headers[i].error_msg.c_str(); - - getMessages(i, itm); - - getRFC822Messages(i, itm); - - ret.msgItems.push_back(itm); - } - - return ret; -} - -SelectedFolderInfo IMAPSession::selectedFolder() -{ - return _mbif; -} - -void IMAPSession::callback(imapStatusCallback imapCallback) -{ - _readCallback = std::move(imapCallback); -} - -void IMAPSession::getMessages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg) -{ - msg.text.content = ""; - msg.text.charSet = ""; - msg.text.content_type = ""; - msg.text.transfer_encoding = ""; - msg.html.content = ""; - msg.html.charSet = ""; - msg.html.content_type = ""; - msg.html.transfer_encoding = ""; - - if (messageIndex < _headers.size()) - { - int sz = _headers[messageIndex].part_headers.size(); - if (sz > 0) - { - for (int i = 0; i < sz; i++) - { - if (!_headers[messageIndex].part_headers[i].rfc822_part && _headers[messageIndex].part_headers[i].message_sub_type != esp_mail_imap_message_sub_type_rfc822) - { - if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none) - { - if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched) - { - msg.text.content = _headers[messageIndex].part_headers[i].text.c_str(); - msg.text.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); - msg.text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); - msg.text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); - } - - if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html) - { - msg.html.content = _headers[messageIndex].part_headers[i].text.c_str(); - msg.html.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); - msg.html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); - msg.html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); - } - } - else - { - struct esp_mail_attacment_info_t att; - att.filename = _headers[messageIndex].part_headers[i].filename.c_str(); - att.mime = _headers[messageIndex].part_headers[i].content_type.c_str(); - att.name = _headers[messageIndex].part_headers[i].name.c_str(); - att.size = _headers[messageIndex].part_headers[i].attach_data_size; - att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str(); - att.type = _headers[messageIndex].part_headers[i].attach_type; - msg.attachments.push_back(att); - } - } - } - } - } -} - -void IMAPSession::getRFC822Messages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg) -{ - if (messageIndex < _headers.size()) - { - int sz = _headers[messageIndex].part_headers.size(); - int partIdx = 0; - int cIdx = 0; - IMAP_MSG_Item *_rfc822 = nullptr; - if (sz > 0) - { - for (int i = 0; i < sz; i++) - { - if (_headers[messageIndex].part_headers[i].message_sub_type == esp_mail_imap_message_sub_type_rfc822) - { - if (_headers[messageIndex].part_headers[i].rfc822_part) - { - if (partIdx > 0) - msg.rfc822.push_back(*_rfc822); - cIdx = i; - partIdx++; - _rfc822 = new IMAP_MSG_Item(); - - _rfc822->from = _headers[messageIndex].part_headers[i].rfc822_header.from.c_str(); - _rfc822->sender = _headers[messageIndex].part_headers[i].rfc822_header.sender.c_str(); - _rfc822->to = _headers[messageIndex].part_headers[i].rfc822_header.to.c_str(); - _rfc822->cc = _headers[messageIndex].part_headers[i].rfc822_header.cc.c_str(); - _rfc822->bcc = _headers[messageIndex].part_headers[i].rfc822_header.bcc.c_str(); - _rfc822->return_path = _headers[messageIndex].part_headers[i].rfc822_header.return_path.c_str(); - _rfc822->reply_to = _headers[messageIndex].part_headers[i].rfc822_header.reply_to.c_str(); - _rfc822->subject = _headers[messageIndex].part_headers[i].rfc822_header.subject.c_str(); - _rfc822->comment = _headers[messageIndex].part_headers[i].rfc822_header.comment.c_str(); - _rfc822->keyword = _headers[messageIndex].part_headers[i].rfc822_header.keyword.c_str(); - _rfc822->date = _headers[messageIndex].part_headers[i].rfc822_header.date.c_str(); - _rfc822->messageID = _headers[messageIndex].part_headers[i].rfc822_header.messageID.c_str(); - _rfc822->text.charSet = ""; - _rfc822->text.content_type = ""; - _rfc822->text.transfer_encoding = ""; - _rfc822->html.charSet = ""; - _rfc822->html.content_type = ""; - _rfc822->html.transfer_encoding = ""; - } - else - { - if (MailClient.multipartMember(_headers[messageIndex].part_headers[cIdx].partNumStr, _headers[messageIndex].part_headers[i].partNumStr)) - { - if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none) - { - if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched) - { - _rfc822->text.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); - _rfc822->text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); - _rfc822->text.content = _headers[messageIndex].part_headers[i].text.c_str(); - _rfc822->text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); - } - if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html) - { - _rfc822->html.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); - _rfc822->html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); - _rfc822->html.content = _headers[messageIndex].part_headers[i].text.c_str(); - _rfc822->html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); - } - } - else - { - struct esp_mail_attacment_info_t att; - att.filename = _headers[messageIndex].part_headers[i].filename.c_str(); - att.mime = _headers[messageIndex].part_headers[i].content_type.c_str(); - att.name = _headers[messageIndex].part_headers[i].name.c_str(); - att.size = _headers[messageIndex].part_headers[i].attach_data_size; - att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str(); - att.type = _headers[messageIndex].part_headers[i].attach_type; - _rfc822->attachments.push_back(att); - } - } - } - } - } - - if ((int)msg.rfc822.size() < partIdx && _rfc822 != nullptr) - msg.rfc822.push_back(*_rfc822); - } - } -} - -bool IMAPSession::closeMailbox() -{ - - if (!MailClient.reconnect(this)) - return false; - - std::string s; - - if (_readCallback) - { - MailClient.appendP(s, esp_mail_str_210, true); - s += _currentFolder; - MailClient.appendP(s, esp_mail_str_96, false); - MailClient.imapCB(this, "", false); - MailClient.imapCB(this, s.c_str(), false); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_197); - - if (MailClient.imapSendP(this, esp_mail_str_195, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - _imap_cmd = esp_mail_imap_cmd_close; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CLOSE_MAILBOX_FAILED, false)) - return false; - - _currentFolder.clear(); - _mailboxOpened = false; - - return true; -} - -bool IMAPSession::openMailbox(const char *folder, esp_mail_imap_auth_mode mode, bool waitResponse) -{ - - if (!MailClient.reconnect(this)) - return false; - - if (_currentFolder.length() > 0) - { - if (strcmp(_currentFolder.c_str(), folder) != 0) - { - if (!closeMailbox()) - return false; - } - } - - _currentFolder = folder; - std::string s; - if (_readCallback) - { - MailClient.appendP(s, esp_mail_str_61, true); - s += _currentFolder; - MailClient.appendP(s, esp_mail_str_96, false); - MailClient.imapCB(this, "", false); - MailClient.imapCB(this, s.c_str(), false); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_248); - - if (mode == esp_mail_imap_mode_examine) - { - MailClient.appendP(s, esp_mail_str_135, true); - _imap_cmd = esp_mail_imap_cmd_examine; - } - else if (mode == esp_mail_imap_mode_select) - { - MailClient.appendP(s, esp_mail_str_247, true); - _imap_cmd = esp_mail_imap_cmd_select; - } - s += _currentFolder; - MailClient.appendP(s, esp_mail_str_136, false); - if (MailClient.imapSend(this, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - if (waitResponse) - { - - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_OPEN_MAILBOX_FAILED, false)) - return false; - } - - if (mode == esp_mail_imap_mode_examine) - _readOnlyMode = true; - else if (mode == esp_mail_imap_mode_select) - _readOnlyMode = false; - - _mailboxOpened = true; - - return true; -} - -bool IMAPSession::getMailboxes(FoldersCollection &folders) -{ - _folders.clear(); - - if (_readCallback) - { - MailClient.imapCB(this, "", false); - MailClient.imapCBP(this, esp_mail_str_58, false); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_230); - - if (MailClient.imapSendP(this, esp_mail_str_133, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_list; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false)) - return false; - - folders = _folders; - return true; -} - -bool IMAPSession::checkCapability() -{ - if (_readCallback) - { - MailClient.imapCB(this, "", false); - MailClient.imapCBP(this, esp_mail_str_64, false); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_65); - - if (MailClient.imapSendP(this, esp_mail_str_2, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_capability; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CHECK_CAPABILITIES_FAILED, false)) - return false; - - return true; -} - -bool IMAPSession::createFolder(const char *folderName) -{ - if (_debug) - { - MailClient.imapCB(this, "", false); - MailClient.debugInfoP(esp_mail_str_320); - } - - std::string cmd; - MailClient.appendP(cmd, esp_mail_str_322, true); - cmd += folderName; - - if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_create; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) - return false; - - return true; -} - -bool IMAPSession::deleteFolder(const char *folderName) -{ - if (_debug) - { - MailClient.imapCB(this, "", false); - MailClient.debugInfoP(esp_mail_str_321); - } - - std::string cmd; - MailClient.appendP(cmd, esp_mail_str_323, true); - cmd += folderName; - - if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_delete; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) - return false; - - return true; -} - -bool IMAPSession::deleteMessages(MessageList *toDelete, bool expunge) -{ - if (toDelete->_list.size() > 0) - { - - if (!selectFolder(_currentFolder.c_str(), false)) - return false; - - if (_debug) - { - MailClient.imapCB(this, "", false); - MailClient.debugInfoP(esp_mail_str_316); - } - - std::string cmd; - char *tmp = nullptr; - MailClient.appendP(cmd, esp_mail_str_249, true); - for (size_t i = 0; i < toDelete->_list.size(); i++) - { - if (i > 0) - MailClient.appendP(cmd, esp_mail_str_263, false); - tmp = MailClient.intStr(toDelete->_list[i]); - cmd += tmp; - MailClient.delS(tmp); - } - MailClient.appendP(cmd, esp_mail_str_315, false); - - if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) - return false; - - if (expunge) - { - if (MailClient.imapSendP(this, esp_mail_str_317, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_expunge; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) - return false; - } - } - - return true; -} - -bool IMAPSession::copyMessages(MessageList *toCopy, const char *dest) -{ - if (toCopy->_list.size() > 0) - { - - if (!selectFolder(_currentFolder.c_str(), false)) - return false; - - if (_debug) - { - MailClient.imapCB(this, "", false); - std::string s; - MailClient.appendP(s, esp_mail_str_318, true); - s += dest; - esp_mail_debug(s.c_str()); - } - - std::string cmd; - char *tmp = nullptr; - MailClient.appendP(cmd, esp_mail_str_319, true); - for (size_t i = 0; i < toCopy->_list.size(); i++) - { - if (i > 0) - MailClient.appendP(cmd, esp_mail_str_263, false); - tmp = MailClient.intStr(toCopy->_list[i]); - cmd += tmp; - MailClient.delS(tmp); - } - MailClient.appendP(cmd, esp_mail_str_131, false); - cmd += dest; - - if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) - return false; - - _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store; - if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) - return false; - } - - return true; -} - -#if defined(ESP8266) -void ESP_Mail_Client::setClock(float offset) -{ - if (WiFi.status() != WL_CONNECTED) - WiFi.reconnect(); - - time_t now = time(nullptr); - - _clockReady = now > ESP_MAIL_CLIENT_VALID_TS; - - if (!_clockReady) - { - char *server1 = strP(esp_mail_str_283); - char *server2 = strP(esp_mail_str_296); - - configTime(offset * 3600, 0, server1, server2); - - now = time(nullptr); - uint8_t attempts = 0; - while (now < ESP_MAIL_CLIENT_VALID_TS) - { - now = time(nullptr); - attempts++; - if (attempts > 200 || now > ESP_MAIL_CLIENT_VALID_TS) - break; - delay(100); - } - - delS(server1); - delS(server2); - } - - _clockReady = now > ESP_MAIL_CLIENT_VALID_TS; -} -#endif - -void IMAPSession::empty() -{ - std::string().swap(_nextUID); - clearMessageData(); -} - -void IMAPSession::clearMessageData() -{ - for (size_t i = 0; i < _headers.size(); i++) - { - _headers[i].part_headers.clear(); - std::vector().swap(_headers[i].part_headers); - } - std::vector().swap(_headers); - std::vector().swap(_msgNum); - _folders.clear(); - _mbif._flags.clear(); - _mbif._searchCount = 0; -} - -SMTPSession::SMTPSession() -{ -} - -SMTPSession::~SMTPSession() -{ - closeSession(); - _caCert.reset(); - _caCert = nullptr; -} - -bool SMTPSession::connect(ESP_Mail_Session *config) -{ - if (_tcpConnected) - MailClient.closeTCP(this); - - _sesson_cfg = config; - _caCert = nullptr; - - if (strlen(_sesson_cfg->certificate.cert_data) > 0) - _caCert = std::shared_ptr(_sesson_cfg->certificate.cert_data); - - if (strlen(_sesson_cfg->certificate.cert_file) > 0) - { - if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk) - MailClient._sdOk = MailClient.sdTest(); - if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk) -#if defined(ESP32) - MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); -#elif defined(ESP8266) - MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(); -#endif - } - return MailClient.smtpAuth(this); -} - -void SMTPSession::debug(int level) -{ - if (level > esp_mail_debug_level_0) - { - if (level > esp_mail_debug_level_3) - level = esp_mail_debug_level_1; - _debugLevel = level; - _debug = true; - } - else - { - _debugLevel = esp_mail_debug_level_0; - _debug = false; - } -} - -String SMTPSession::errorReason() -{ - std::string ret; - - switch (_smtpStatus.statusCode) - { - case SMTP_STATUS_SERVER_CONNECT_FAILED: - MailClient.appendP(ret, esp_mail_str_38, true); - break; - case SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED: - MailClient.appendP(ret, esp_mail_str_39, true); - break; - case SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED: - MailClient.appendP(ret, esp_mail_str_39, true); - break; - case SMTP_STATUS_AUTHEN_NOT_SUPPORT: - MailClient.appendP(ret, esp_mail_str_42, true); - break; - case SMTP_STATUS_AUTHEN_FAILED: - MailClient.appendP(ret, esp_mail_str_43, true); - break; - case SMTP_STATUS_USER_LOGIN_FAILED: - MailClient.appendP(ret, esp_mail_str_43, true); - break; - case SMTP_STATUS_PASSWORD_LOGIN_FAILED: - MailClient.appendP(ret, esp_mail_str_47, true); - break; - case SMTP_STATUS_SEND_HEADER_SENDER_FAILED: - MailClient.appendP(ret, esp_mail_str_48, true); - break; - case SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED: - MailClient.appendP(ret, esp_mail_str_222, true); - break; - case SMTP_STATUS_SEND_BODY_FAILED: - MailClient.appendP(ret, esp_mail_str_49, true); - break; - case MAIL_CLIENT_ERROR_CONNECTION_LOST: - MailClient.appendP(ret, esp_mail_str_221, true); - break; - case MAIL_CLIENT_ERROR_READ_TIMEOUT: - MailClient.appendP(ret, esp_mail_str_258, true); - break; - case MAIL_CLIENT_ERROR_FILE_IO_ERROR: - MailClient.appendP(ret, esp_mail_str_282, true); - break; - case SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED: - MailClient.appendP(ret, esp_mail_str_293, true); - break; - case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED: - MailClient.appendP(ret, esp_mail_str_305, true); - break; - case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP: - MailClient.appendP(ret, esp_mail_str_132, true); - break; - case SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED: - MailClient.appendP(ret, esp_mail_str_206, true); - break; - case SMTP_STATUS_NO_VALID_SENDER_EXISTED: - MailClient.appendP(ret, esp_mail_str_205, true); - break; - case MAIL_CLIENT_ERROR_OUT_OF_MEMORY: - MailClient.appendP(ret, esp_mail_str_186, true); - break; - case SMTP_STATUS_NO_SUPPORTED_AUTH: - MailClient.appendP(ret, esp_mail_str_42, true); - break; - - default: - break; - } - - if (_smtpStatus.text.length() > 0 && ret.length() == 0) - { - MailClient.appendP(ret, esp_mail_str_312, true); - char *code = MailClient.intStr(_smtpStatus.respCode); - ret += code; - MailClient.delS(code); - MailClient.appendP(ret, esp_mail_str_313, false); - ret += _smtpStatus.text; - return ret.c_str(); - } - return ret.c_str(); -} - -bool SMTPSession::closeSession() -{ - if (!_tcpConnected) - return false; - - if (_sendCallback) - { - _cbData._sentSuccess = _sentSuccessCount; - _cbData._sentFailed = _sentFailedCount; - MailClient.smtpCB(this, ""); - MailClient.smtpCBP(this, esp_mail_str_128); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_245); - - bool ret = true; - -/* Sign out */ -#if defined(ESP32) - /** - * The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure - * The client disposed without memory released after the server close - * the connection due to QUIT command, which caused the memory leaks. - */ - MailClient.smtpSendP(this, esp_mail_str_7, true); - _smtp_cmd = esp_mail_smtp_cmd_logout; - ret = MailClient.handleSMTPResponse(this, esp_mail_smtp_status_code_221, SMTP_STATUS_SEND_BODY_FAILED); -#endif - - if (ret) - { - - if (_sendCallback) - { - MailClient.smtpCB(this, ""); - MailClient.smtpCBP(this, esp_mail_str_129, false); - } - - if (_debug) - MailClient.debugInfoP(esp_mail_str_246); - - if (_sendCallback) - MailClient.smtpCB(this, "", true); - } - - return MailClient.handleSMTPError(this, 0, ret); -} - -void SMTPSession::callback(smtpStatusCallback smtpCallback) -{ - _sendCallback = std::move(smtpCallback); -} - -IMAP_Status::IMAP_Status() -{ -} -IMAP_Status::~IMAP_Status() -{ - empty(); -} - -const char *IMAP_Status::info() -{ - return _info.c_str(); -} - -bool IMAP_Status::success() -{ - return _success; -} - -void IMAP_Status::empty() -{ - std::string().swap(_info); -} - -SMTP_Status::SMTP_Status() -{ -} - -SMTP_Status::~SMTP_Status() -{ - empty(); -} - -const char *SMTP_Status::info() -{ - return _info.c_str(); -} - -bool SMTP_Status::success() -{ - return _success; -} - -size_t SMTP_Status::completedCount() -{ - return _sentSuccess; -} - -size_t SMTP_Status::failedCount() -{ - return _sentFailed; -} - -void SMTP_Status::empty() -{ - std::string().swap(_info); -} - -ESP_Mail_Client MailClient = ESP_Mail_Client(); - -#endif /* ESP_Mail_Client_CPP */ +/** + * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 + * + * Version: 1.2.0 + * Released: May 17, 2021 + * + * Updates: + * - Add support ESP8266 Core SDK v3.x.x. + * + * + * This library allows Espressif's ESP32 and ESP8266 devices to send and read Email + * through the SMTP and IMAP servers. + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP_Mail_Client_CPP +#define ESP_Mail_Client_CPP + +#include "ESP_Mail_Client.h" + +#if defined(ESP32) +extern "C" +{ +#include +#include +} +#endif + +bool ESP_Mail_Client::sendIMAPCommand(IMAPSession *imap, int msgIndex, int cmdCase) +{ + + std::string cmd; + if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) + appendP(cmd, esp_mail_str_142, true); + else + appendP(cmd, esp_mail_str_143, true); + + char *tmp = intStr(imap->_msgNum[msgIndex]); + cmd += tmp; + delS(tmp); + appendP(cmd, esp_mail_str_147, false); + if (!imap->_config->fetch.set_seen) + { + appendP(cmd, esp_mail_str_152, false); + appendP(cmd, esp_mail_str_214, false); + } + appendP(cmd, esp_mail_str_218, false); + + switch (cmdCase) + { + case 1: + + appendP(cmd, esp_mail_str_269, false); + break; + + case 2: + + if (cPart(imap)->partNumFetchStr.length() > 0) + cmd += cPart(imap)->partNumFetchStr; + else + appendP(cmd, esp_mail_str_215, false); + appendP(cmd, esp_mail_str_156, false); + break; + + case 3: + + cmd += cPart(imap)->partNumFetchStr; + appendP(cmd, esp_mail_str_156, false); + break; + + default: + break; + } + + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + return true; +} + +bool ESP_Mail_Client::readMail(IMAPSession *imap, bool closeSession) +{ + + imap->checkUID(); + imap->checkPath(); + + if (!imap->_tcpConnected) + imap->_mailboxOpened = false; + + std::string buf; + std::string command; + std::string _uid; + appendP(command, esp_mail_str_27, true); + char *tmp = nullptr; + size_t readCount = 0; + imap->_multipart_levels.clear(); + + if (!reconnect(imap)) + return false; + + int cmem = ESP.getFreeHeap(); + + if (cmem < ESP_MAIL_MIN_MEM) + { + if (imap->_debug) + { + esp_mail_debug(""); + errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY); + } + goto out; + } + + //new session + if (!imap->_tcpConnected) + { + //authenticate new + if (!imapAuth(imap)) + { + closeTCP(imap); + return false; + } + } + else + { + //reuse session + for (size_t i = 0; i < imap->_headers.size(); i++) + imap->_headers[i].part_headers.clear(); + imap->_headers.clear(); + + if (strlen(imap->_config->fetch.uid) > 0) + imap->_headerOnly = false; + else + imap->_headerOnly = true; + } + imap->_rfc822_part_count = 0; + imap->_mbif._availableItems = 0; + imap->_msgNum.clear(); + imap->_uidSearch = false; + imap->_mbif._searchCount = 0; + + if (imap->_currentFolder.length() == 0) + return handleIMAPError(imap, IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED, false); + + if (!imap->_mailboxOpened || (imap->_config->fetch.set_seen && !imap->_headerOnly && imap->_readOnlyMode)) + { + if (!imap->openFolder(imap->_currentFolder.c_str(), imap->_readOnlyMode && !imap->_config->fetch.set_seen)) + return handleIMAPError(imap, IMAP_STATUS_OPEN_MAILBOX_FAILED, false); + } + + if (imap->_headerOnly) + { + if (strlen(imap->_config->search.criteria) > 0) + { + + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_66, false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_232); + + if (strposP(imap->_config->search.criteria, esp_mail_str_137, 0) != -1) + { + imap->_uidSearch = true; + appendP(command, esp_mail_str_138, false); + } + + appendP(command, esp_mail_str_139, false); + + for (size_t i = 0; i < strlen(imap->_config->search.criteria); i++) + { + if (imap->_config->search.criteria[i] != ' ' && imap->_config->search.criteria[i] != '\r' && imap->_config->search.criteria[i] != '\n' && imap->_config->search.criteria[i] != '$') + buf.append(1, imap->_config->search.criteria[i]); + + if (imap->_config->search.criteria[i] == ' ') + { + tmp = strP(esp_mail_str_140); + char *tmp2 = strP(esp_mail_str_224); + + if ((imap->_uidSearch && strcmp(buf.c_str(), tmp) == 0) || (imap->_unseen && buf.find(tmp2) != std::string::npos)) + buf.clear(); + delS(tmp); + delS(tmp2); + + tmp = strP(esp_mail_str_141); + if (strcmp(buf.c_str(), tmp) != 0 && buf.length() > 0) + { + appendP(command, esp_mail_str_131, false); + command += buf; + } + delS(tmp); + buf.clear(); + } + } + + tmp = strP(esp_mail_str_223); + if (imap->_unseen && strpos(imap->_config->search.criteria, tmp, 0) == -1) + appendP(command, esp_mail_str_223, false); + delS(tmp); + + if (buf.length() > 0) + { + appendP(command, esp_mail_str_131, false); + command += buf; + } + + if (imapSend(imap, command.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + std::string().swap(command); + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_search; + + if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession)) + return false; + + if (imap->_readCallback) + { + std::string s; + appendP(s, esp_mail_str_34, true); + appendP(s, esp_mail_str_68, false); + char *tmp = intStr(imap->_config->limit.search); + s += tmp; + delS(tmp); + imapCB(imap, s.c_str(), false); + + if (imap->_msgNum.size() > 0) + { + + appendP(s, esp_mail_str_69, true); + tmp = intStr(imap->_mbif._searchCount); + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_70, false); + imapCB(imap, s.c_str(), false); + + appendP(s, esp_mail_str_71, true); + tmp = intStr(imap->_msgNum.size()); + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_70, false); + imapCB(imap, s.c_str(), false); + } + else + imapCBP(imap, esp_mail_str_72, false); + } + } + else + { + imap->_mbif._availableItems++; + imap->_msgNum.push_back(imap->_mbif._nextUID - 1); + imap->_headerOnly = false; + char *tmp = intStr(imap->_mbif._nextUID - 1); + _uid = tmp; + delS(tmp); + imap->_config->fetch.uid = _uid.c_str(); + + if (imap->_readCallback) + imapCBP(imap, esp_mail_str_73, false); + } + } + else + { + imap->_mbif._availableItems++; + imap->_msgNum.push_back(atoi(imap->_config->fetch.uid)); + } + + for (size_t i = 0; i < imap->_msgNum.size(); i++) + { + + imap->_cMsgIdx = i; + imap->_totalRead++; + + if (ESP.getFreeHeap() - (imap->_config->limit.msg_size * (i + 1)) < ESP_MAIL_MIN_MEM) + { + if (imap->_debug) + errorStatusCB(imap, MAIL_CLIENT_ERROR_OUT_OF_MEMORY); + goto out; + } + + if (imap->_readCallback) + { + readCount++; + + std::string s; + appendP(s, esp_mail_str_74, true); + char *tmp = intStr(imap->_totalRead); + s += tmp; + delS(tmp); + + if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) + appendP(s, esp_mail_str_75, false); + else + appendP(s, esp_mail_str_76, false); + + tmp = intStr(imap->_msgNum[i]); + s += tmp; + delS(tmp); + imapCB(imap, "", false); + imapCB(imap, s.c_str(), false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_233); + + std::string cmd; + if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) + appendP(cmd, esp_mail_str_142, true); + else + appendP(cmd, esp_mail_str_143, true); + + if (imap->_debug) + debugInfoP(esp_mail_str_77); + + char *tmp = intStr(imap->_msgNum[i]); + cmd += tmp; + delS(tmp); + + appendP(cmd, esp_mail_str_147, false); + if (!imap->_config->fetch.set_seen) + { + appendP(cmd, esp_mail_str_152, false); + appendP(cmd, esp_mail_str_214, false); + } + appendP(cmd, esp_mail_str_218, false); + + appendP(cmd, esp_mail_str_144, false); + appendP(cmd, esp_mail_str_156, false); + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_header; + + int err = IMAP_STATUS_BAD_COMMAND; + if (imap->_headerOnly) + err = IMAP_STATUS_IMAP_RESPONSE_FAILED; + + if (!handleIMAPResponse(imap, err, closeSession)) + return false; + + if (!imap->_headerOnly) + { + imap->_cPartIdx = 0; + + //multipart + if (cHeader(imap)->multipart) + { + struct esp_mail_imap_multipart_level_t mlevel; + mlevel.level = 1; + mlevel.fetch_rfc822_header = false; + mlevel.append_body_text = false; + imap->_multipart_levels.push_back(mlevel); + + if (!fetchMultipartBodyHeader(imap, i)) + return false; + } + else + { + //singlepart + if (imap->_debug) + { + std::string s; + appendP(s, esp_mail_str_81, true); + s += '1'; + esp_mail_debug(s.c_str()); + } + + cHeader(imap)->partNumStr.clear(); + if (!sendIMAPCommand(imap, i, 1)) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_mime; + if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, closeSession)) + return false; + } + + if (imap->_config->download.text || imap->_config->download.html || imap->_config->download.attachment || imap->_config->download.inlineImg) + { + if (!_sdOk && imap->_storageType == esp_mail_file_storage_type_sd) + { + _sdOk = sdTest(); + if (_sdOk) + if (!ESP_MAIL_SD_FS.exists(imap->_config->storage.saved_path)) + createDirs(imap->_config->storage.saved_path); + } + else if (!_flashOk && imap->_storageType == esp_mail_file_storage_type_flash) +#if defined(ESP32) + _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + _flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + } + + if (cHeader(imap)->part_headers.size() > 0) + { + if (cHeader(imap)->attachment_count > 0 && imap->_readCallback) + { + std::string s; + appendP(s, esp_mail_str_34, true); + appendP(s, esp_mail_str_78, false); + char *tmp = intStr(cHeader(imap)->attachment_count); + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_79, false); + imapCB(imap, s.c_str(), false); + + for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) + { + imap->_cPartIdx = j; + if (!cPart(imap)->rfc822_part && cPart(imap)->attach_type != esp_mail_att_type_none) + imapCB(imap, cPart(imap)->filename.c_str(), false); + } + } + + std::string s1, s2; + int _idx1 = 0; + for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) + { + imap->_cPartIdx = j; + if (cPart(imap)->rfc822_part) + { + s1 = cPart(imap)->partNumStr; + _idx1 = cPart(imap)->rfc822_msg_Idx; + } + else if (s1.length() > 0) + { + if (multipartMember(s1, cPart(imap)->partNumStr)) + { + cPart(imap)->message_sub_type = esp_mail_imap_message_sub_type_rfc822; + cPart(imap)->rfc822_msg_Idx = _idx1; + } + } + + if (cPart(imap)->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel) + s2 = cPart(imap)->partNumStr; + else if (s2.length() > 0) + { + if (multipartMember(s2, cPart(imap)->partNumStr)) + { + cPart(imap)->attach_type = esp_mail_att_type_attachment; + if (cPart(imap)->filename.length() == 0) + { + if (cPart(imap)->name.length() > 0) + cPart(imap)->filename = cPart(imap)->name; + else + { + char *tmp = getUID(); + cPart(imap)->filename = tmp; + appendP(cPart(imap)->filename, esp_mail_str_40, false); + delS(tmp); + } + } + } + } + } + + int acnt = 0; + int ccnt = 0; + + for (size_t j = 0; j < cHeader(imap)->part_headers.size(); j++) + { + imap->_cPartIdx = j; + + if (cPart(imap)->rfc822_part || cPart(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_none) + continue; + + bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + + if (cPart(imap)->attach_type == esp_mail_att_type_none || cPart(imap)->msg_type == esp_mail_msg_type_html || cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) + { + + bool ret = ((imap->_config->enable.rfc822 || imap->_config->download.rfc822) && rfc822_body_subtype) || (!rfc822_body_subtype && ((imap->_config->enable.text && (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched)) || (imap->_config->enable.html && cPart(imap)->msg_type == esp_mail_msg_type_html) || (cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text))); + if (!ret) + continue; + + if ((imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))) + { + + if (ccnt == 0) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_57, false); + } + + if (imap->_debug) + { + if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) + debugInfoP(esp_mail_str_59); + else if (cPart(imap)->msg_type == esp_mail_msg_type_html) + debugInfoP(esp_mail_str_60); + } + } + else + { + if (ccnt == 0) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_307, false); + } + + if (imap->_debug) + { + if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) + debugInfoP(esp_mail_str_308); + else if (cPart(imap)->msg_type == esp_mail_msg_type_html) + debugInfoP(esp_mail_str_309); + } + } + + ccnt++; + + if (!sendIMAPCommand(imap, i, 2)) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_text; + if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession)) + return false; + } + else if (cPart(imap)->attach_type != esp_mail_att_type_none && (_sdOk || _flashOk)) + { + + if (imap->_config->download.attachment || imap->_config->download.inlineImg) + { + if (acnt == 0) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_80, false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_55); + + acnt++; + if (cPart(imap)->octetLen <= (int)imap->_config->limit.attachment_size) + { + + if (_sdOk || _flashOk) + { + + if ((int)j < (int)cHeader(imap)->part_headers.size() - 1) + if (cHeader(imap)->part_headers[j + 1].octetLen > (int)imap->_config->limit.attachment_size) + cHeader(imap)->downloaded_bytes += cHeader(imap)->part_headers[j + 1].octetLen; + + if (!sendIMAPCommand(imap, i, 3)) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_fetch_body_attachment; + if (!handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, closeSession)) + return false; + delay(0); + } + } + else + { + if ((int)j == (int)cHeader(imap)->part_headers.size() - 1) + cHeader(imap)->downloaded_bytes += cPart(imap)->octetLen; + } + } + } + } + } + + if (imap->_config->download.header && !imap->_headerSaved) + { + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_124, false); + } + saveHeader(imap); + } + + if (closeSession) + { + if (imap->_storageType == esp_mail_file_storage_type_sd) + { + if (_sdOk) + ESP_MAIL_SD_FS.end(); + _sdOk = false; + } + else if (imap->_storageType == esp_mail_file_storage_type_flash) + { + if (_flashOk) + ESP_MAIL_FLASH_FS.end(); + + _flashOk = false; + } + } + + imap->_cMsgIdx++; + } + + if (imap->_debug) + { + std::string s; + appendP(s, esp_mail_str_261, true); + appendP(s, esp_mail_str_84, false); + char *tmp = intStr(ESP.getFreeHeap()); + s += tmp; + delS(tmp); + esp_mail_debug(s.c_str()); + } + } + +out: + + if (readCount < imap->_msgNum.size()) + { + imap->_mbif._availableItems = readCount; + imap->_msgNum.erase(imap->_msgNum.begin() + readCount, imap->_msgNum.end()); + } + + if (closeSession) + { + if (!imap->closeSession()) + return false; + } + else + { + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_87, false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_88); + } + + if (imap->_readCallback) + imapCB(imap, "", true); + + return true; +} +bool ESP_Mail_Client::getMultipartFechCmd(IMAPSession *imap, int msgIdx, std::string &partText) +{ + if (imap->_multipart_levels.size() == 0) + return false; + + int cLevel = imap->_multipart_levels.size() - 1; + + cHeader(imap)->partNumStr.clear(); + + if (imap->_uidSearch || strlen(imap->_config->fetch.uid) > 0) + appendP(partText, esp_mail_str_142, true); + else + appendP(partText, esp_mail_str_143, true); + + char *tmp = intStr(imap->_msgNum[msgIdx]); + partText += tmp; + delS(tmp); + + appendP(partText, esp_mail_str_147, false); + if (!imap->_config->fetch.set_seen) + { + appendP(partText, esp_mail_str_152, false); + appendP(partText, esp_mail_str_214, false); + } + appendP(partText, esp_mail_str_218, false); + + for (size_t i = 0; i < imap->_multipart_levels.size(); i++) + { + if (i > 0) + { + appendP(partText, esp_mail_str_152, false); + appendP(cHeader(imap)->partNumStr, esp_mail_str_152, false); + } + + tmp = intStr(imap->_multipart_levels[i].level); + partText += tmp; + cHeader(imap)->partNumStr += tmp; + delS(tmp); + } + + if (imap->_multipart_levels[cLevel].fetch_rfc822_header) + { + appendP(partText, esp_mail_str_51, false); + imap->_multipart_levels[cLevel].append_body_text = true; + } + else + appendP(partText, esp_mail_str_148, false); + + imap->_multipart_levels[cLevel].fetch_rfc822_header = false; + + return true; +} + +bool ESP_Mail_Client::multipartMember(const std::string &part, const std::string &check) +{ + if (part.length() > check.length()) + return false; + + for (size_t i = 0; i < part.length(); i++) + if (part[i] != check[i]) + return false; + + return true; +} + +bool ESP_Mail_Client::fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx) +{ + bool ret = true; + + if (!connected(imap)) + { + closeTCP(imap); + return false; + } + int cLevel = 0; + + do + { + + struct esp_mail_message_part_info_t *_cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1]; + bool rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + + std::string cmd; + + if (!getMultipartFechCmd(imap, msgIdx, cmd)) + return true; + + if (imap->_debug) + { + std::string s; + if (imap->_multipart_levels.size() > 1) + appendP(s, esp_mail_str_86, true); + else + appendP(s, esp_mail_str_81, true); + s += cHeader(imap)->partNumStr; + esp_mail_debug(s.c_str()); + } + + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_cmd_fetch_body_mime; + + ret = handleIMAPResponse(imap, IMAP_STATUS_IMAP_RESPONSE_FAILED, false); + + _cpart = &cHeader(imap)->part_headers[cHeader(imap)->message_data_count - 1]; + rfc822_body_subtype = _cpart->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + cLevel = imap->_multipart_levels.size() - 1; + + if (ret) + { + + if (_cpart->multipart) + { + if (_cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_parallel || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_alternative || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_related || _cpart->multipart_sub_type == esp_mail_imap_multipart_sub_type_mixed) + { + struct esp_mail_imap_multipart_level_t mlevel; + mlevel.level = 1; + mlevel.fetch_rfc822_header = false; + mlevel.append_body_text = false; + imap->_multipart_levels.push_back(mlevel); + fetchMultipartBodyHeader(imap, msgIdx); + } + else + imap->_multipart_levels[cLevel].level++; + } + else + { + if (rfc822_body_subtype) + { + //to get additional rfc822 message header + imap->_multipart_levels[cLevel].fetch_rfc822_header = true; + fetchMultipartBodyHeader(imap, msgIdx); + } + else + { + if (imap->_multipart_levels[cLevel].append_body_text) + { + //single part rfc822 message body, append TEXT to the body fetch command + appendP(_cpart->partNumFetchStr, esp_mail_str_152, false); + appendP(_cpart->partNumFetchStr, esp_mail_str_215, false); + imap->_multipart_levels[cLevel].append_body_text = false; + } + imap->_multipart_levels[cLevel].level++; + } + } + } + + } while (ret); + + imap->_multipart_levels.pop_back(); + + if (imap->_multipart_levels.size() > 0) + { + cLevel = imap->_multipart_levels.size() - 1; + imap->_multipart_levels[cLevel].level++; + } + + return true; +} + +bool ESP_Mail_Client::connected(IMAPSession *imap) +{ + if (!imap->_secure) + { + if (!imap->httpClient._stream()) + return false; + return imap->httpClient._stream()->_ns_connected(); + } + else + { + if (!imap->httpClient.stream()) + return false; + return imap->httpClient.stream()->connected(); + } +} + +bool ESP_Mail_Client::imapAuth(IMAPSession *imap) +{ + + bool ssl = false; + std::string buf; +#if defined(ESP32) + imap->httpClient.setDebugCallback(NULL); +#elif defined(ESP8266) + +#endif + + if (imap->_config != nullptr) + { + if (strlen(imap->_config->fetch.uid) > 0) + imap->_headerOnly = false; + else + imap->_headerOnly = true; + } + + imap->_totalRead = 0; + imap->_secure = true; + bool secureMode = true; + +#if defined(ESP32) + if (imap->_debug) + imap->httpClient.setDebugCallback(esp_mail_debug); +#elif defined(ESP8266) + imap->httpClient.txBufDivider = 16; //minimum, tx buffer size for ssl data and request command data + imap->httpClient.rxBufDivider = 1; + if (imap->_config != nullptr) + { + if (!imap->_headerOnly && !imap->_config->enable.html && !imap->_config->enable.text && !imap->_config->download.attachment && !imap->_config->download.inlineImg && !imap->_config->download.html && !imap->_config->download.text) + imap->httpClient.rxBufDivider = 16; // minimum rx buffer size for only message header + } +#endif + + if (imap->_sesson_cfg->server.port == esp_mail_imap_port_143) + { + imap->_secure = false; + secureMode = false; + } + else + secureMode = !imap->_sesson_cfg->secure.startTLS; + + setSecure(imap->httpClient, imap->_sesson_cfg, imap->_caCert); + + if (imap->_readCallback) + imapCBP(imap, esp_mail_str_50, false); + + if (imap->_debug) + { + std::string s; + appendP(s, esp_mail_str_314, true); + s += ESP_MAIL_VERSION; + esp_mail_debug(s.c_str()); + + debugInfoP(esp_mail_str_225); + appendP(s, esp_mail_str_261, true); + appendP(s, esp_mail_str_211, false); + s += imap->_sesson_cfg->server.host_name; + esp_mail_debug(s.c_str()); + char *tmp = intStr(imap->_sesson_cfg->server.port); + appendP(s, esp_mail_str_261, true); + appendP(s, esp_mail_str_201, false); + s += tmp; + delS(tmp); + esp_mail_debug(s.c_str()); + } + + imap->httpClient.begin(imap->_sesson_cfg->server.host_name, imap->_sesson_cfg->server.port); + + if (!imap->httpClient.connect(secureMode)) + return handleIMAPError(imap, IMAP_STATUS_SERVER_CONNECT_FAILED, false); + + imap->_tcpConnected = true; + WiFiClient *stream = imap->httpClient.stream(); +#if defined(ESP32) + stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC); +#elif defined(ESP8266) + stream->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000); +#endif + imap->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000; + + if (imap->_readCallback) + imapCBP(imap, esp_mail_str_54, false); + + if (imap->_debug) + debugInfoP(esp_mail_str_228); + +init: + + if (!imap->checkCapability()) + return false; + + //start TLS when needed or the server issue + if ((imap->_auth_capability.start_tls || imap->_sesson_cfg->secure.startTLS) && !ssl) + { + std::string s; + if (imap->_readCallback) + { + appendP(s, esp_mail_str_34, true); + appendP(s, esp_mail_str_209, false); + esp_mail_debug(s.c_str()); + } + + if (imap->_debug) + { + appendP(s, esp_mail_str_196, true); + esp_mail_debug(s.c_str()); + } + + imapSendP(imap, esp_mail_str_311, false); + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_starttls; + if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false)) + return false; + + if (imap->_debug) + { + debugInfoP(esp_mail_str_310); + } + + //connect in secure mode + //do ssl handshaking + if (!imap->httpClient._stream()->_ns_connect_ssl()) + return handleIMAPError(imap, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP, false); + + //set the secure mode + imap->_sesson_cfg->secure.startTLS = false; + ssl = true; + imap->_secure = true; + + //check the capabilitiy again + goto init; + } + + imap->clearMessageData(); + imap->_mailboxOpened = false; + + bool creds = strlen(imap->_sesson_cfg->login.email) > 0 && strlen(imap->_sesson_cfg->login.password) > 0; + bool xoauth_auth = strlen(imap->_sesson_cfg->login.accessToken) > 0 && imap->_auth_capability.xoauth2; + bool login_auth = creds; + bool plain_auth = imap->_auth_capability.plain && creds; + + bool supported_auth = xoauth_auth || login_auth || plain_auth; + + if (!supported_auth) + return handleIMAPError(imap, IMAP_STATUS_NO_SUPPORTED_AUTH, false); + + //rfc4959 + if (supported_auth) + { + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_56, false); + } + } + + if (xoauth_auth) + { + if (!imap->_auth_capability.xoauth2) + return handleIMAPError(imap, IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false); + + if (imap->_debug) + debugInfoP(esp_mail_str_291); + + std::string cmd; + appendP(cmd, esp_mail_str_292, true); + cmd += getEncodedToken(imap); + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_auth; + if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, false)) + return false; + } + else if (login_auth) + { + + if (imap->_debug) + debugInfoP(esp_mail_str_229); + + std::string cmd; + + appendP(cmd, esp_mail_str_130, true); + cmd += imap->_sesson_cfg->login.email; + appendP(cmd, esp_mail_str_131, false); + cmd += imap->_sesson_cfg->login.password; + + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login; + if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true)) + return false; + } + else if (plain_auth) + { + if (imap->_debug) + debugInfoP(esp_mail_str_290); + + const char *usr = imap->_sesson_cfg->login.email; + const char *psw = imap->_sesson_cfg->login.password; + int len = strlen(usr) + strlen(psw) + 2; + uint8_t *tmp = new uint8_t[len]; + memset(tmp, 0, len); + int p = 1; + memcpy(tmp + p, usr, strlen(usr)); + p += strlen(usr) + 1; + memcpy(tmp + p, psw, strlen(psw)); + p += strlen(psw); + + std::string s; + appendP(s, esp_mail_str_41, true); + s += encodeBase64Str(tmp, p); + delete[] tmp; + + if (imapSend(imap, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_login; + if (!handleIMAPResponse(imap, IMAP_STATUS_LOGIN_FAILED, true)) + return false; + } + + return true; +} + +std::string ESP_Mail_Client::getEncodedToken(IMAPSession *imap) +{ + std::string raw; + appendP(raw, esp_mail_str_285, true); + raw += imap->_sesson_cfg->login.email; + appendP(raw, esp_mail_str_286, false); + raw += imap->_sesson_cfg->login.accessToken; + appendP(raw, esp_mail_str_287, false); + std::string s = encodeBase64Str((const unsigned char *)raw.c_str(), raw.length()); + return s; +} + +bool ESP_Mail_Client::imapLogout(IMAPSession *imap) +{ + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_85, false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_234); + + if (imapSendP(imap, esp_mail_str_146, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_cmd_logout; + if (!handleIMAPResponse(imap, IMAP_STATUS_BAD_COMMAND, false)) + return false; + + if (imap->_readCallback) + { + imapCB(imap, "", false); + imapCBP(imap, esp_mail_str_187, false); + } + + if (imap->_debug) + debugInfoP(esp_mail_str_235); + + return true; +} + +void ESP_Mail_Client::errorStatusCB(IMAPSession *imap, int error) +{ + imap->_imapStatus.statusCode = error; + std::string s; + if (imap->_readCallback) + { + appendP(s, esp_mail_str_53, true); + s += imap->errorReason().c_str(); + imapCB(imap, s.c_str(), false); + } + + if (imap->_debug) + { + appendP(s, esp_mail_str_185, true); + s += imap->errorReason().c_str(); + esp_mail_debug(s.c_str()); + } +} + +size_t ESP_Mail_Client::imapSendP(IMAPSession *imap, PGM_P v, bool newline) +{ + if (!reconnect(imap)) + { + closeTCP(imap); + return 0; + } + + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!imap->_tcpConnected) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + char *tmp = strP(v); + size_t len = 0; + + if (newline) + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(tmp); + if (!imap->_secure) + len = imap->httpClient._ns_println(tmp); + else + len = imap->httpClient.stream()->println(tmp); + } + else + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(tmp, false); + if (!imap->_secure) + len = imap->httpClient._ns_print(tmp); + else + len = imap->httpClient.stream()->print(tmp); + } + + if (len != strlen(tmp) && len != strlen(tmp) + 2) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + delS(tmp); + + return len; +} + +size_t ESP_Mail_Client::imapSend(IMAPSession *imap, const char *data, bool newline) +{ + if (!reconnect(imap)) + { + closeTCP(imap); + return 0; + } + + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!imap->_tcpConnected) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + size_t len = 0; + + if (newline) + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(data); + if (!imap->_secure) + len = imap->httpClient._ns_println(data); + else + len = imap->httpClient.stream()->println(data); + } + else + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(data, false); + if (!imap->_secure) + len = imap->httpClient._ns_print(data); + else + len = imap->httpClient.stream()->print(data); + } + + if (len != strlen(data) && len != strlen(data) + 2) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + return len; +} + +size_t ESP_Mail_Client::imapSend(IMAPSession *imap, int data, bool newline) +{ + if (!reconnect(imap)) + { + closeTCP(imap); + return 0; + } + + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!imap->_tcpConnected) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + char *tmp = intStr(data); + size_t len = 0; + + if (newline) + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(tmp); + if (!imap->_secure) + len = imap->httpClient._ns_println(tmp); + else + len = imap->httpClient.stream()->println(tmp); + } + else + { + if (imap->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(tmp, false); + if (!imap->_secure) + len = imap->httpClient._ns_print(tmp); + else + len = imap->httpClient.stream()->print(tmp); + } + + if (len != strlen(tmp) && len != strlen(tmp) + 2) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + delS(tmp); + + return len; +} + +bool ESP_Mail_Client::setFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) +{ + return _setFlag(imap, msgUID, flag, 0, closeSession); +} + +bool ESP_Mail_Client::addFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) +{ + return _setFlag(imap, msgUID, flag, 1, closeSession); +} + +bool ESP_Mail_Client::removeFlag(IMAPSession *imap, int msgUID, const char *flag, bool closeSession) +{ + return _setFlag(imap, msgUID, flag, 2, closeSession); +} + +bool ESP_Mail_Client::_setFlag(IMAPSession *imap, int msgUID, const char *flag, uint8_t action, bool closeSession) +{ + if (!reconnect(imap)) + return false; + + if (!imap->_tcpConnected) + { + imap->_mailboxOpened = false; + return false; + } + + if (imap->_currentFolder.length() == 0) + { + if (imap->_readCallback) + debugInfoP(esp_mail_str_153); + + if (imap->_debug) + { + std::string e; + appendP(e, esp_mail_str_185, true); + appendP(e, esp_mail_str_151, false); + esp_mail_debug(e.c_str()); + } + } + else + { + if (imap->_readOnlyMode || !imap->_mailboxOpened) + { + if (!imap->selectFolder(imap->_currentFolder.c_str(), false)) + return false; + } + } + + if (imap->_readCallback) + { + imapCB(imap, "", false); + if (action == 0) + debugInfoP(esp_mail_str_157); + else if (action == 1) + debugInfoP(esp_mail_str_155); + else + debugInfoP(esp_mail_str_154); + } + + if (imap->_debug) + { + if (action == 0) + debugInfoP(esp_mail_str_253); + else if (action == 1) + debugInfoP(esp_mail_str_254); + else + debugInfoP(esp_mail_str_255); + } + + std::string cmd; + appendP(cmd, esp_mail_str_249, true); + char *tmp = intStr(msgUID); + cmd += tmp; + delS(tmp); + if (action == 0) + appendP(cmd, esp_mail_str_250, false); + else if (action == 1) + appendP(cmd, esp_mail_str_251, false); + else + appendP(cmd, esp_mail_str_252, false); + cmd += flag; + appendP(cmd, esp_mail_str_192, false); + + if (imapSend(imap, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + imap->_imap_cmd = esp_mail_imap_cmd_store; + + if (!handleIMAPResponse(imap, IMAP_STATUS_PARSE_FLAG_FAILED, false)) + return false; + + if (closeSession) + imap->closeSession(); + + return true; +} + +void ESP_Mail_Client::createDirs(std::string dirs) +{ + std::string dir = ""; + int count = 0; + for (size_t i = 0; i < dirs.length(); i++) + { + dir.append(1, dirs[i]); + count++; + if (dirs[i] == '/') + { + if (dir.length() > 0) + ESP_MAIL_SD_FS.mkdir(dir.substr(0, dir.length() - 1).c_str()); + count = 0; + } + } + if (count > 0) + ESP_MAIL_SD_FS.mkdir(dir.c_str()); + std::string().swap(dir); +} + +bool ESP_Mail_Client::sdTest() +{ +#if defined(CARD_TYPE_SD) + if (_sdConfigSet) + sdBegin(_sck, _miso, _mosi, _ss); + else + sdBegin(); +#endif + +#if defined(ESP32) +#if defined(CARD_TYPE_SD_MMC) + if (_sdConfigSet) + sdMMCBegin(sd_mmc_mountpoint, sd_mmc_mode1bit, sd_mmc_format_if_mount_failed); +#endif +#endif + + file = ESP_MAIL_SD_FS.open(esp_mail_str_204, FILE_WRITE); + if (!file) + return false; + + if (!file.write(32)) + return false; + file.close(); + + file = ESP_MAIL_SD_FS.open(esp_mail_str_204); + if (!file) + return false; + + while (file.available()) + { + if (file.read() != 32) + return false; + } + file.close(); + + ESP_MAIL_SD_FS.remove(esp_mail_str_204); + + return true; +} + +std::string ESP_Mail_Client::getEncodedToken(SMTPSession *smtp) +{ + std::string raw; + appendP(raw, esp_mail_str_285, true); + raw += smtp->_sesson_cfg->login.email; + appendP(raw, esp_mail_str_286, false); + raw += smtp->_sesson_cfg->login.accessToken; + appendP(raw, esp_mail_str_287, false); + return encodeBase64Str((const unsigned char *)raw.c_str(), raw.length()); +} + +bool ESP_Mail_Client::smtpAuth(SMTPSession *smtp) +{ + + if (!reconnect(smtp)) + return false; + + bool ssl = false; + smtp->_secure = true; + bool secureMode = true; + + std::string s; + +#if defined(ESP32) + smtp->httpClient.setDebugCallback(NULL); +#elif defined(ESP8266) + smtp->httpClient.rxBufDivider = 16; // minimum rx buffer for smtp status response + smtp->httpClient.txBufDivider = 8; // medium tx buffer for faster attachment/inline data transfer +#endif + + if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_25) + { + smtp->_secure = false; + secureMode = false; + } + else + { + if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_587) + smtp->_sesson_cfg->secure.startTLS = true; + + secureMode = !smtp->_sesson_cfg->secure.startTLS; + + //to prevent to send the connection upgrade command when some server promotes + //the starttls capability even the current connection was already secured. + if (smtp->_sesson_cfg->server.port == esp_mail_smtp_port_465) + ssl = true; + } + + setSecure(smtp->httpClient, smtp->_sesson_cfg, smtp->_caCert); + + //Server connection attempt: no status code + if (smtp->_sendCallback) + smtpCBP(smtp, esp_mail_str_120); + + if (smtp->_debug) + { + appendP(s, esp_mail_str_314, true); + s += ESP_MAIL_VERSION; + esp_mail_debug(s.c_str()); + + debugInfoP(esp_mail_str_236); + appendP(s, esp_mail_str_261, true); + appendP(s, esp_mail_str_211, false); + s += smtp->_sesson_cfg->server.host_name; + esp_mail_debug(s.c_str()); + char *tmp = intStr(smtp->_sesson_cfg->server.port); + appendP(s, esp_mail_str_261, true); + appendP(s, esp_mail_str_201, false); + s += tmp; + delS(tmp); + esp_mail_debug(s.c_str()); + } +#if defined(ESP32) + if (smtp->_debug) + smtp->httpClient.setDebugCallback(esp_mail_debug); +#endif + + smtp->httpClient.begin(smtp->_sesson_cfg->server.host_name, smtp->_sesson_cfg->server.port); + + if (!smtp->httpClient.connect(secureMode)) + return handleSMTPError(smtp, SMTP_STATUS_SERVER_CONNECT_FAILED); + + //server connected + smtp->_tcpConnected = true; + + if (smtp->_debug) + debugInfoP(esp_mail_str_238); + + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_121); + } + +#if defined(ESP32) + smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC); +#elif defined(ESP8266) + smtp->httpClient.stream()->setTimeout(ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000); +#endif + smtp->httpClient.tcpTimeout = ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC * 1000; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state; + + //expected status code 220 for ready to service + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_220, SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED)) + return false; + +init: + + //Sending greeting hello response + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_122); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_239); + + appendP(s, esp_mail_str_6, true); + if (strlen(smtp->_sesson_cfg->login.user_domain) > 0) + s += smtp->_sesson_cfg->login.user_domain; + else + appendP(s, esp_mail_str_44, false); + + if (smtpSendP(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_greeting; + + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, 0)) + { + appendP(s, esp_mail_str_5, true); + if (strlen(smtp->_sesson_cfg->login.user_domain) > 0) + s += smtp->_sesson_cfg->login.user_domain; + else + appendP(s, esp_mail_str_44, false); + + if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED)) + return false; + smtp->_send_capability.esmtp = false; + smtp->_auth_capability.login = true; + } + else + smtp->_send_capability.esmtp = true; + + //start TLS when needed + if ((smtp->_auth_capability.start_tls || smtp->_sesson_cfg->secure.startTLS) && !ssl) + { + //send starttls command + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_209); + } + + if (smtp->_debug) + { + appendP(s, esp_mail_str_196, true); + esp_mail_debug(s.c_str()); + } + + //expected status code 250 for complete the request + //some server returns 220 to restart to initial state + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls; + smtpSendP(smtp, esp_mail_str_311, false); + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED)) + return false; + + if (smtp->_debug) + { + debugInfoP(esp_mail_str_310); + } + + //connect using secure mode + //do ssl handshaking + if (!smtp->httpClient._stream()->_ns_connect_ssl()) + return handleSMTPError(smtp, MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP); + + //set the secure mode + smtp->_sesson_cfg->secure.startTLS = false; + ssl = true; + smtp->_secure = true; + + //return to initial state if the response status is 220. + if (smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_220) + goto init; + } + + bool creds = strlen(smtp->_sesson_cfg->login.email) > 0 && strlen(smtp->_sesson_cfg->login.password) > 0; + bool xoauth_auth = strlen(smtp->_sesson_cfg->login.accessToken) > 0 && smtp->_auth_capability.xoauth2; + bool login_auth = smtp->_auth_capability.login && creds; + bool plain_auth = smtp->_auth_capability.plain && creds; + + if (xoauth_auth || login_auth || plain_auth) + { + if (smtp->_sendCallback) + { + smtpCB(smtp, "", false); + smtpCBP(smtp, esp_mail_str_56, false); + } + + //log in + if (xoauth_auth) + { + if (smtp->_debug) + debugInfoP(esp_mail_str_288); + + if (!smtp->_auth_capability.xoauth2) + return handleSMTPError(smtp, SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED, false); + + if (smtpSendP(smtp, esp_mail_str_289, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (smtpSend(smtp, getEncodedToken(smtp).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_AUTHEN_FAILED)) + return false; + + return true; + } + else if (plain_auth) + { + + if (smtp->_debug) + debugInfoP(esp_mail_str_241); + + if (smtp->_debug) + { + appendP(s, esp_mail_str_261, true); + s += smtp->_sesson_cfg->login.email; + esp_mail_debug(s.c_str()); + + appendP(s, esp_mail_str_131, false); + for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++) + appendP(s, esp_mail_str_183, false); + esp_mail_debug(s.c_str()); + } + + //rfc4616 + const char *usr = smtp->_sesson_cfg->login.email; + const char *psw = smtp->_sesson_cfg->login.password; + int len = strlen(usr) + strlen(psw) + 2; + uint8_t *tmp = new uint8_t[len]; + memset(tmp, 0, len); + int p = 1; + memcpy(tmp + p, usr, strlen(usr)); + p += strlen(usr) + 1; + memcpy(tmp + p, psw, strlen(psw)); + p += strlen(psw); + + std::string s; + appendP(s, esp_mail_str_45, true); + appendP(s, esp_mail_str_131, false); + s += encodeBase64Str(tmp, p); + delete[] tmp; + + if (smtpSend(smtp, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_auth_plain; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_USER_LOGIN_FAILED)) + return false; + + return true; + } + else if (login_auth) + { + if (smtp->_debug) + debugInfoP(esp_mail_str_240); + + if (smtpSendP(smtp, esp_mail_str_4, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_AUTHEN_FAILED)) + return false; + + if (smtp->_debug) + { + appendP(s, esp_mail_str_261, true); + s += smtp->_sesson_cfg->login.email; + esp_mail_debug(s.c_str()); + } + + if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.email, strlen(smtp->_sesson_cfg->login.email)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_user; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_334, SMTP_STATUS_USER_LOGIN_FAILED)) + return false; + + if (smtp->_debug) + { + appendP(s, esp_mail_str_261, true); + for (size_t i = 0; i < strlen(smtp->_sesson_cfg->login.password); i++) + appendP(s, esp_mail_str_183, false); + esp_mail_debug(s.c_str()); + } + + if (smtpSend(smtp, encodeBase64Str((const unsigned char *)smtp->_sesson_cfg->login.password, strlen(smtp->_sesson_cfg->login.password)).c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_login_psw; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_235, SMTP_STATUS_PASSWORD_LOGIN_FAILED)) + return false; + + return true; + } + } + + return true; +} + +void ESP_Mail_Client::mimeFromFile(const char *name, std::string &mime) +{ + std::string ext = name; + size_t p = ext.find_last_of("."); + if (p != std::string::npos) + { + ext = ext.substr(p, ext.length() - p); + if (ext.length() > 0) + getMIME(ext.c_str(), mime); + } +} + +bool ESP_Mail_Client::connected(SMTPSession *smtp) +{ + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + { + if (!smtp->httpClient._stream()) + return false; + return smtp->httpClient._stream()->_ns_connected(); + } + else + { + if (!smtp->httpClient.stream()) + return false; + return smtp->httpClient.stream()->connected(); + } +} + +bool ESP_Mail_Client::setSendingResult(SMTPSession *smtp, SMTP_Message *msg, bool result) +{ + if (result) + smtp->_sentSuccessCount++; + else + smtp->_sentFailedCount++; + + if (smtp->_sendCallback) + { + struct esp_mail_smtp_send_status_t status; + status.completed = result; + status.timesstamp = time(nullptr); + status.subject = msg->subject; + status.recipients = msg->_rcp[0].email; + + smtp->sendingResult.add(status); + + smtp->_cbData._sentSuccess = smtp->_sentSuccessCount; + smtp->_cbData._sentFailed = smtp->_sentFailedCount; + } + + return result; +} + +bool ESP_Mail_Client::sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession) +{ + + if (strlen(msg->html.content) > 0 || msg->html.blob.size > 0 || strlen(msg->html.file.name) > 0) + msg->type |= esp_mail_msg_type_html; + + if (strlen(msg->text.content) > 0 || msg->text.blob.size > 0 || strlen(msg->text.file.name) > 0) + msg->type |= esp_mail_msg_type_plain; + + for (size_t i = 0; i < msg->_rfc822.size(); i++) + { + if (strlen(msg->_rfc822[i].html.content) > 0) + msg->_rfc822[i].type |= esp_mail_msg_type_html; + + if (strlen(msg->_rfc822[i].text.content) > 0) + msg->_rfc822[i].type |= esp_mail_msg_type_plain; + } + + return _sendMail(smtp, msg, closeSession); +} + +void ESP_Mail_Client::getMIME(const char *ext, std::string &mime) +{ + mime = ""; + for (int i = 0; i < esp_mail_file_extension_maxType; i++) + { + if (strcmp_P(ext, mimeinfo[i].endsWith) == 0) + { + char *tmp = strP(mimeinfo[i].mimeType); + mime = tmp; + delS(tmp); + break; + } + } +} + +size_t ESP_Mail_Client::numAtt(SMTPSession *smtp, esp_mail_attach_type type, SMTP_Message *msg) +{ + size_t count = 0; + for (size_t i = 0; i < msg->_att.size(); i++) + { + if (msg->_att[i]._int.att_type == type) + count++; + } + return count; +} + +bool ESP_Mail_Client::validEmail(const char *s) +{ + std::string str(s); + auto at = std::find(str.begin(), str.end(), '@'); + auto dot = std::find(at, str.end(), '.'); + return (at != str.end()) && (dot != str.end()); +} +bool ESP_Mail_Client::checkEmail(SMTPSession *smtp, SMTP_Message *msg) +{ + bool validRecipient = false; + + if (!validEmail(msg->sender.email)) + { + errorStatusCB(smtp, SMTP_STATUS_NO_VALID_SENDER_EXISTED); + return setSendingResult(smtp, msg, false); + } + + for (uint8_t i = 0; i < msg->_rcp.size(); i++) + { + if (validEmail(msg->_rcp[i].email)) + validRecipient = true; + } + + if (!validRecipient) + { + errorStatusCB(smtp, SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED); + return setSendingResult(smtp, msg, false); + } + + return true; +} + +bool ESP_Mail_Client::_sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession) +{ + + smtp->_smtpStatus.statusCode = 0; + smtp->_smtpStatus.respCode = 0; + smtp->_smtpStatus.text.clear(); + bool rfc822MSG = false; + + if (!checkEmail(smtp, msg)) + return false; + + smtp->_chunkedEnable = false; + smtp->_chunkCount = 0; + + //new session + if (!smtp->_tcpConnected) + { + if (!smtpAuth(smtp)) + { + closeTCP(smtp); + return setSendingResult(smtp, msg, false); + } + smtp->_sentSuccessCount = 0; + smtp->_sentFailedCount = 0; + smtp->sendingResult.clear(); + } + else + { + //reuse session + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + if (smtp->_sentSuccessCount || smtp->_sentFailedCount) + smtpCBP(smtp, esp_mail_str_267); + else + smtpCBP(smtp, esp_mail_str_208); + } + + if (smtp->_debug) + { + if (smtp->_sentSuccessCount || smtp->_sentFailedCount) + debugInfoP(esp_mail_str_268); + else + debugInfoP(esp_mail_str_207); + } + } + + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_125); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_242); + + std::string buf; + std::string buf2; + checkBinaryData(smtp, msg); + + if (msg->priority >= esp_mail_smtp_priority_high && msg->priority <= esp_mail_smtp_priority_low) + { + char *tmp = intStr(msg->priority); + appendP(buf2, esp_mail_str_17, true); + buf2 += tmp; + delS(tmp); + appendP(buf2, esp_mail_str_34, false); + + if (msg->priority == esp_mail_smtp_priority_high) + { + appendP(buf2, esp_mail_str_18, false); + appendP(buf2, esp_mail_str_21, false); + } + else if (msg->priority == esp_mail_smtp_priority_normal) + { + appendP(buf2, esp_mail_str_19, false); + appendP(buf2, esp_mail_str_22, false); + } + else if (msg->priority == esp_mail_smtp_priority_low) + { + appendP(buf2, esp_mail_str_20, false); + appendP(buf2, esp_mail_str_23, false); + } + } + + appendP(buf2, esp_mail_str_10, false); + + if (strlen(msg->sender.name) > 0) + buf2 += msg->sender.name; + + appendP(buf2, esp_mail_str_14, false); + buf2 += msg->sender.email; + appendP(buf2, esp_mail_str_15, false); + appendP(buf2, esp_mail_str_34, false); + + appendP(buf, esp_mail_str_8, true); + appendP(buf, esp_mail_str_14, false); + buf += msg->sender.email; + appendP(buf, esp_mail_str_15, false); + + if (smtp->_send_capability.binaryMIME && smtp->_send_capability.chunking && msg->enable.chunking && (msg->text._int.binary || msg->html._int.binary)) + appendP(buf, esp_mail_str_104, false); + + if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_sender; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_SENDER_FAILED)) + return setSendingResult(smtp, msg, false); + + for (uint8_t i = 0; i < msg->_rcp.size(); i++) + { + if (i == 0) + { + appendP(buf2, esp_mail_str_11, false); + if (strlen(msg->_rcp[i].name) > 0) + buf2 += msg->_rcp[i].name; + + appendP(buf2, esp_mail_str_14, false); + buf2 += msg->_rcp[i].email; + appendP(buf2, esp_mail_str_15, false); + } + else + { + if (strlen(msg->_rcp[i].name) > 0) + { + appendP(buf2, esp_mail_str_263, false); + buf2 += msg->_rcp[i].name; + appendP(buf2, esp_mail_str_14, false); + } + else + appendP(buf2, esp_mail_str_13, false); + buf2 += msg->_rcp[i].email; + appendP(buf2, esp_mail_str_15, false); + } + + if (i == msg->_rcp.size() - 1) + appendP(buf2, esp_mail_str_34, false); + + buf.clear(); + //only address + appendP(buf, esp_mail_str_9, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->_rcp[i].email; + appendP(buf, esp_mail_str_15, false); + + //rfc3461, rfc3464 + if (smtp->_send_capability.dsn) + { + if (msg->response.notify != esp_mail_smtp_notify::esp_mail_smtp_notify_never) + { + appendP(buf, esp_mail_str_262, false); + int opcnt = 0; + if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_success) + { + if (opcnt > 0) + appendP(buf, esp_mail_str_263, false); + appendP(buf, esp_mail_str_264, false); + opcnt++; + } + if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_failure) + { + if (opcnt > 0) + appendP(buf, esp_mail_str_263, false); + appendP(buf, esp_mail_str_265, false); + opcnt++; + } + if (msg->response.notify && esp_mail_smtp_notify::esp_mail_smtp_notify_delay) + { + if (opcnt > 0) + appendP(buf, esp_mail_str_263, false); + appendP(buf, esp_mail_str_266, false); + opcnt++; + } + } + } + + if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) + return setSendingResult(smtp, msg, false); + } + + for (uint8_t i = 0; i < msg->_cc.size(); i++) + { + if (i == 0) + { + appendP(buf2, esp_mail_str_12, false); + appendP(buf2, esp_mail_str_14, false); + buf2 += msg->_cc[i].email; + appendP(buf2, esp_mail_str_15, false); + } + else + { + appendP(buf2, esp_mail_str_13, false); + buf2 += msg->_cc[i].email; + appendP(buf2, esp_mail_str_15, false); + } + + if (i == msg->_cc.size() - 1) + appendP(buf2, esp_mail_str_34, false); + + buf.clear(); + + appendP(buf, esp_mail_str_9, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->_cc[i].email; + appendP(buf, esp_mail_str_15, false); + + if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) + return setSendingResult(smtp, msg, false); + } + + for (uint8_t i = 0; i < msg->_bcc.size(); i++) + { + appendP(buf, esp_mail_str_9, true); + appendP(buf, esp_mail_str_14, false); + buf += msg->_bcc[i].email; + appendP(buf, esp_mail_str_15, false); + if (smtpSend(smtp, buf.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_header_recipient; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED)) + return setSendingResult(smtp, msg, false); + } + + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_126); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_243); + + if (smtp->_send_capability.chunking && msg->enable.chunking) + { + smtp->_chunkedEnable = true; + if (!bdat(smtp, msg, buf2.length(), false)) + return false; + } + else + { + if (smtpSendP(smtp, esp_mail_str_16, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_354, SMTP_STATUS_SEND_BODY_FAILED)) + return setSendingResult(smtp, msg, false); + } + + if (smtpSend(smtp, buf2.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + std::string s; + appendP(s, esp_mail_str_24, true); + s += msg->subject; + appendP(s, esp_mail_str_34, false); + + if (msg->_hdr.size() > 0) + { + for (uint8_t k = 0; k < msg->_hdr.size(); k++) + { + s += msg->_hdr[k]; + appendP(s, esp_mail_str_34, false); + } + } + + if (strlen(msg->response.reply_to) > 0) + { + appendP(s, esp_mail_str_184, false); + appendP(s, esp_mail_str_14, false); + s += msg->response.reply_to; + appendP(s, esp_mail_str_15, false); + appendP(s, esp_mail_str_34, false); + } + + if (strlen(msg->response.return_path) > 0) + { + appendP(s, esp_mail_str_46, false); + appendP(s, esp_mail_str_14, false); + s += msg->response.return_path; + appendP(s, esp_mail_str_15, false); + appendP(s, esp_mail_str_34, false); + } + + appendP(s, esp_mail_str_3, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + return sendMSGData(smtp, msg, closeSession, rfc822MSG); +} + +bool ESP_Mail_Client::sendMSGData(SMTPSession *smtp, SMTP_Message *msg, bool closeSession, bool rfc822MSG) +{ + std::string s; + std::string mixed = getBoundary(15); + std::string alt = getBoundary(15); + + if (numAtt(smtp, esp_mail_att_type_attachment, msg) == 0 && msg->_parallel.size() == 0 && msg->_rfc822.size() == 0) + { + if (msg->type == (esp_mail_msg_type_plain | esp_mail_msg_type_html | esp_mail_msg_type_enriched) || numAtt(smtp, esp_mail_att_type_inline, msg) > 0) + { + if (!sendMSG(smtp, msg, alt)) + return setSendingResult(smtp, msg, false); + } + else if (msg->type != esp_mail_msg_type_none) + { + if (!sendPartText(smtp, msg, msg->type, "")) + return setSendingResult(smtp, msg, false); + } + } + else + { + appendP(s, esp_mail_str_1, true); + s += mixed; + appendP(s, esp_mail_str_35, false); + + appendP(s, esp_mail_str_33, false); + s += mixed; + appendP(s, esp_mail_str_34, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + if (!sendMSG(smtp, msg, alt)) + return setSendingResult(smtp, msg, false); + + if (!bdat(smtp, msg, 2, false)) + return false; + + if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_127); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_244); + + if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_attachment, msg) > 0) + esp_mail_debug(""); + + if (!sendAttachments(smtp, msg, mixed)) + return setSendingResult(smtp, msg, false); + + if (!sendParallelAttachments(smtp, msg, mixed)) + return setSendingResult(smtp, msg, false); + + if (!sendRFC822Msg(smtp, msg, mixed, closeSession, msg->_rfc822.size() > 0)) + return setSendingResult(smtp, msg, false); + + appendP(s, esp_mail_str_33, true); + s += mixed; + appendP(s, esp_mail_str_33, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + } + + if (!rfc822MSG) + { + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_303); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_304); + + if (smtp->_chunkedEnable) + { + + if (!bdat(smtp, msg, 0, true)) + return false; + + smtp->_smtp_cmd = esp_mail_smtp_cmd_chunk_termination; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) + return false; + } + else + { + if (smtpSendP(smtp, esp_mail_str_37, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) + return setSendingResult(smtp, msg, false); + } + + setSendingResult(smtp, msg, true); + + if (closeSession) + if (!smtp->closeSession()) + return false; + } + + return true; +} + +bool ESP_Mail_Client::sendRFC822Msg(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool closeSession, bool rfc822MSG) +{ + if (msg->_rfc822.size() == 0) + return true; + std::string buf; + for (uint8_t i = 0; i < msg->_rfc822.size(); i++) + { + buf.clear(); + getRFC822PartHeader(smtp, buf, boundary); + + getRFC822MsgEnvelope(smtp, &msg->_rfc822[i], buf); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!sendMSGData(smtp, &msg->_rfc822[i], closeSession, rfc822MSG)) + return false; + } + + return true; +} + +void ESP_Mail_Client::getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg, std::string &buf) +{ + if (strlen(msg->date) > 0) + { + appendP(buf, esp_mail_str_99, false); + buf += msg->date; + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->from.email) > 0) + { + appendP(buf, esp_mail_str_10, false); + + if (strlen(msg->from.name) > 0) + buf += msg->from.name; + + appendP(buf, esp_mail_str_14, false); + buf += msg->from.email; + appendP(buf, esp_mail_str_15, false); + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->sender.email) > 0) + { + appendP(buf, esp_mail_str_150, false); + + if (strlen(msg->sender.name) > 0) + buf += msg->sender.name; + + appendP(buf, esp_mail_str_14, false); + buf += msg->sender.email; + appendP(buf, esp_mail_str_15, false); + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->response.reply_to) > 0) + { + appendP(buf, esp_mail_str_184, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->response.reply_to; + appendP(buf, esp_mail_str_15, false); + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->response.return_path) > 0) + { + appendP(buf, esp_mail_str_46, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->response.return_path; + appendP(buf, esp_mail_str_15, false); + appendP(buf, esp_mail_str_34, false); + } + + for (uint8_t i = 0; i < msg->_rcp.size(); i++) + { + if (i == 0) + { + appendP(buf, esp_mail_str_11, false); + if (strlen(msg->_rcp[i].name) > 0) + buf += msg->_rcp[i].name; + + appendP(buf, esp_mail_str_14, false); + buf += msg->_rcp[i].email; + appendP(buf, esp_mail_str_15, false); + } + else + { + if (strlen(msg->_rcp[i].name) > 0) + { + appendP(buf, esp_mail_str_263, false); + buf += msg->_rcp[i].name; + appendP(buf, esp_mail_str_14, false); + } + else + appendP(buf, esp_mail_str_13, false); + buf += msg->_rcp[i].email; + appendP(buf, esp_mail_str_15, false); + } + + if (i == msg->_rcp.size() - 1) + appendP(buf, esp_mail_str_34, false); + } + + for (uint8_t i = 0; i < msg->_cc.size(); i++) + { + if (i == 0) + { + appendP(buf, esp_mail_str_12, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->_cc[i].email; + appendP(buf, esp_mail_str_15, false); + } + else + { + appendP(buf, esp_mail_str_13, false); + buf += msg->_cc[i].email; + appendP(buf, esp_mail_str_15, false); + } + + if (i == msg->_cc.size() - 1) + appendP(buf, esp_mail_str_34, false); + } + + for (uint8_t i = 0; i < msg->_bcc.size(); i++) + { + if (i == 0) + { + appendP(buf, esp_mail_str_149, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->_bcc[i].email; + appendP(buf, esp_mail_str_15, false); + } + else + { + appendP(buf, esp_mail_str_13, false); + buf += msg->_bcc[i].email; + appendP(buf, esp_mail_str_15, false); + } + + if (i == msg->_bcc.size() - 1) + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->subject) > 0) + { + appendP(buf, esp_mail_str_279, false); + buf += msg->subject; + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->keyword) > 0) + { + appendP(buf, esp_mail_str_145, false); + buf += msg->keyword; + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->comment) > 0) + { + appendP(buf, esp_mail_str_134, false); + buf += msg->comment; + appendP(buf, esp_mail_str_34, false); + } + + if (strlen(msg->messageID) > 0) + { + appendP(buf, esp_mail_str_274, false); + appendP(buf, esp_mail_str_14, false); + buf += msg->messageID; + appendP(buf, esp_mail_str_15, false); + appendP(buf, esp_mail_str_34, false); + } +} + +bool ESP_Mail_Client::bdat(SMTPSession *smtp, SMTP_Message *msg, int len, bool last) +{ + if (!smtp->_chunkedEnable || !msg->enable.chunking) + return true; + + smtp->_chunkCount++; + + std::string bdat; + appendP(bdat, esp_mail_str_106, true); + char *tmp = intStr(len); + bdat += tmp; + if (last) + appendP(bdat, esp_mail_str_173, false); + delS(tmp); + if (smtpSend(smtp, bdat.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + if (!smtp->_send_capability.pipelining) + { + smtp->_smtp_cmd = esp_mail_smtp_command::esp_mail_smtp_cmd_send_body; + if (!handleSMTPResponse(smtp, esp_mail_smtp_status_code_250, SMTP_STATUS_SEND_BODY_FAILED)) + return setSendingResult(smtp, msg, false); + smtp->_chunkCount = 0; + } + return true; +} + +void ESP_Mail_Client::checkBinaryData(SMTPSession *smtp, SMTP_Message *msg) +{ + if (msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type & esp_mail_msg_type_html) + { + if ((msg->type & esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched) > 0) + { + if (strlen(msg->text.transfer_encoding) > 0) + { + if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0) + { + msg->text._int.binary = true; + } + } + } + + if ((msg->type & esp_mail_msg_type_html) > 0) + { + if (strlen(msg->html.transfer_encoding) > 0) + { + if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_binary) == 0) + { + msg->html._int.binary = true; + } + } + } + } + + for (size_t i = 0; i < msg->_att.size(); i++) + { + if (strcmpP(msg->_att[i].descr.transfer_encoding, 0, esp_mail_str_166)) + { + msg->_att[i]._int.binary = true; + } + } +} + +bool ESP_Mail_Client::sendBlob(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att) +{ + if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0) + { + if (!sendBase64(smtp, msg, (const unsigned char *)att->blob.data, att->blob.size, att->_int.flash_blob, att->descr.filename, smtp->_sendCallback != NULL)) + return false; + return true; + } + else + { + if (att->blob.size > 0) + { + size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; + size_t writeLen = 0; + uint8_t *buf = new uint8_t[chunkSize]; + int pg = 0, _pg = 0; + while (writeLen < att->blob.size) + { + if (writeLen > att->blob.size - chunkSize) + chunkSize = att->blob.size - writeLen; + + if (!bdat(smtp, msg, chunkSize, false)) + break; + memcpy_P(buf, att->blob.data, chunkSize); + if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + break; + + if (smtp->_sendCallback) + { + pg = (float)(100.0f * writeLen / att->blob.size); + if (pg != _pg) + uploadReport(att->descr.filename, pg); + _pg = pg; + } + writeLen += chunkSize; + } + delete[] buf; + if (smtp->_sendCallback && _pg < 100) + uploadReport(att->descr.filename, 100); + + return writeLen >= att->blob.size; + } + } + return false; +} + +bool ESP_Mail_Client::sendFile(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file) +{ + if (strcmp(att->descr.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0 && strcmp(att->descr.transfer_encoding, att->descr.content_encoding) != 0) + { + if (!sendBase64Stream(smtp, msg, file, att->descr.filename, smtp->_sendCallback != NULL)) + return false; + return true; + } + else + { + if (file.size() > 0) + { + size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; + size_t writeLen = 0; + if (file.size() < chunkSize) + chunkSize = file.size(); + uint8_t *buf = new uint8_t[chunkSize]; + int pg = 0, _pg = 0; + while (writeLen < file.size() && file.available()) + { + if (writeLen > file.size() - chunkSize) + chunkSize = file.size() - writeLen; + size_t readLen = file.read(buf, chunkSize); + if (readLen != chunkSize) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + break; + } + + if (!bdat(smtp, msg, chunkSize, false)) + break; + + if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + break; + + if (smtp->_sendCallback) + { + pg = (float)(100.0f * writeLen / file.size()); + if (pg != _pg) + uploadReport(att->descr.filename, pg); + _pg = pg; + } + writeLen += chunkSize; + } + delete[] buf; + if (smtp->_sendCallback && _pg < 100) + uploadReport(att->descr.filename, 100); + return writeLen == file.size(); + } + return false; + } + return false; +} + +bool ESP_Mail_Client::sendParallelAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary) +{ + if (msg->_parallel.size() == 0) + return true; + + std::string buf; + std::string parallel = getBoundary(15); + appendP(buf, esp_mail_str_33, true); + buf += boundary; + appendP(buf, esp_mail_str_34, false); + + appendP(buf, esp_mail_str_28, false); + buf += parallel; + appendP(buf, esp_mail_str_35, false); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + if (!sendAttachments(smtp, msg, parallel, true)) + return setSendingResult(smtp, msg, false); + + appendP(buf, esp_mail_str_33, true); + buf += parallel; + appendP(buf, esp_mail_str_33, false); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return setSendingResult(smtp, msg, false); + + return true; +} + +bool ESP_Mail_Client::sendAttachments(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, bool parallel) +{ + std::string s; + std::string buf; + int cnt = 0; + + SMTP_Attachment *att = nullptr; + + size_t sz = msg->_att.size(); + if (parallel) + sz = msg->_parallel.size(); + + for (size_t i = 0; i < sz; i++) + { + if (parallel) + att = &msg->_parallel[i]; + else + att = &msg->_att[i]; + + if (att->_int.att_type == esp_mail_att_type_attachment) + { + appendP(s, esp_mail_str_261, true); + s += att->descr.filename; + + if (smtp->_sendCallback) + { + if (cnt > 0) + smtpCB(smtp, ""); + smtpCB(smtp, att->descr.filename); + } + + if (smtp->_debug) + esp_mail_debug(s.c_str()); + + cnt++; + + if (att->file.storage_type == esp_mail_file_storage_type_none) + { + if (!att->blob.data) + continue; + + if (smtp->_sendCallback) + smtpCB(smtp, att->descr.filename); + + if (smtp->_debug) + esp_mail_debug(s.c_str()); + + buf.clear(); + getAttachHeader(buf, boundary, att, att->blob.size); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!sendBlob(smtp, msg, att)) + return false; + + if (!bdat(smtp, msg, 2, false)) + return false; + + if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + } + else + { + + if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) + _sdOk = sdTest(); + + if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash) +#if defined(ESP32) + _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + _flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + + if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)) + { + + if (smtp->_sendCallback) + debugInfoP(esp_mail_str_158); + + if (smtp->_debug) + { + std::string e; + appendP(e, esp_mail_str_185, true); + appendP(e, esp_mail_str_158, false); + esp_mail_debug(e.c_str()); + } + + continue; + } + + if (openFileRead(smtp, msg, att, file, s, buf, boundary, false)) + { + if (file) + { + + if (!sendFile(smtp, msg, att, file)) + return false; + + if (!bdat(smtp, msg, 2, false)) + return false; + + if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + } + } + } + } + } + return true; +} + +bool ESP_Mail_Client::openFileRead(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, File &file, std::string &s, std::string &buf, const std::string &boundary, bool inlined) +{ + bool file_existed = false; + std::string filepath; + + if (strlen(att->file.path) > 0) + { + if (att->file.path[0] != '/') + appendP(filepath, esp_mail_str_202, true); + filepath += att->file.path; + } + + if (att->file.storage_type == esp_mail_file_storage_type_sd) { + file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); + } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { + file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); + } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { + file_existed = ufsp->exists(filepath.c_str()); + } + + if (!file_existed) + { + + if (strlen(att->descr.filename) > 0) + { + filepath.clear(); + if (att->descr.filename[0] != '/') + appendP(filepath, esp_mail_str_202, true); + filepath += att->descr.filename; + } + + if (att->file.storage_type == esp_mail_file_storage_type_sd) { + file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); + } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { + file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); + } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { + file_existed = ufsp->exists(filepath.c_str()); + } + } + + if (!file_existed) + { + if (smtp->_sendCallback) + debugInfoP(esp_mail_str_158); + + if (smtp->_debug) + { + std::string e; + appendP(e, esp_mail_str_185, true); + appendP(e, esp_mail_str_158, false); + esp_mail_debug(e.c_str()); + } + } + + if (file_existed) + { + + buf.clear(); + + if (att->file.storage_type == esp_mail_file_storage_type_sd) { + file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ); + } else if (att->file.storage_type == esp_mail_file_storage_type_flash) { +#if defined(ESP32) + file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ); +#elif defined(ESP8266) + file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r"); +#endif + } else if (att->file.storage_type == esp_mail_file_storage_type_univ) { + file = ufsp->open(filepath.c_str(), "r"); + } + + if (!file) + return false; + + if (inlined) + getInlineHeader(buf, boundary, att, file.size()); + else + getAttachHeader(buf, boundary, att, file.size()); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + return true; + } + + return false; +} + +bool ESP_Mail_Client::openFileRead2(SMTPSession *smtp, SMTP_Message *msg, File &file, const char *path, esp_mail_file_storage_type storageType) +{ + bool file_existed = false; + std::string filepath; + + if (strlen(path) > 0) + { + if (path[0] != '/') + appendP(filepath, esp_mail_str_202, true); + filepath += path; + } + + if (storageType == esp_mail_file_storage_type_sd) { + file_existed = ESP_MAIL_SD_FS.exists(filepath.c_str()); + } else if (storageType == esp_mail_file_storage_type_flash) { + file_existed = ESP_MAIL_FLASH_FS.exists(filepath.c_str()); + } else if (storageType == esp_mail_file_storage_type_univ) { + file_existed = ufsp->exists(filepath.c_str()); + } + + if (!file_existed) + { + if (smtp->_sendCallback) + debugInfoP(esp_mail_str_158); + + if (smtp->_debug) + { + std::string e; + appendP(e, esp_mail_str_185, true); + appendP(e, esp_mail_str_158, false); + esp_mail_debug(e.c_str()); + } + } + + if (file_existed) + { + if (storageType == esp_mail_file_storage_type_sd) + file = ESP_MAIL_SD_FS.open(filepath.c_str(), FILE_READ); + else if (storageType == esp_mail_file_storage_type_flash) +#if defined(ESP32) + file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), FILE_READ); +#elif defined(ESP8266) + file = ESP_MAIL_FLASH_FS.open(filepath.c_str(), "r"); +#endif + return true; + } + + return false; +} + +bool ESP_Mail_Client::sendInline(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary, byte type) +{ + size_t num = numAtt(smtp, esp_mail_att_type_inline, msg) > 0; + + if (num > 0) + { + if (smtp->_sendCallback) + { + smtpCB(smtp, ""); + smtpCBP(smtp, esp_mail_str_167); + } + + if (smtp->_debug) + debugInfoP(esp_mail_str_271); + } + + std::string s; + std::string buf; + std::string related = getBoundary(15); + int cnt = 0; + SMTP_Attachment *att = nullptr; + + appendP(s, esp_mail_str_33, true); + s += boundary; + appendP(s, esp_mail_str_34, false); + + appendP(s, esp_mail_str_298, false); + s += related; + appendP(s, esp_mail_str_35, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!sendPartText(smtp, msg, type, related.c_str())) + return false; + + if (smtp->_sendCallback && numAtt(smtp, esp_mail_att_type_inline, msg) > 0) + esp_mail_debug(""); + + if (num > 0) + { + for (uint8_t i = 0; i < msg->_att.size(); i++) + { + att = &msg->_att[i]; + if (att->_int.att_type == esp_mail_att_type_inline) + { + appendP(s, esp_mail_str_261, true); + s += att->descr.filename; + + if (smtp->_sendCallback) + { + if (cnt > 0) + smtpCB(smtp, ""); + smtpCB(smtp, att->descr.filename); + } + + if (smtp->_debug) + esp_mail_debug(s.c_str()); + + cnt++; + + if (att->file.storage_type == esp_mail_file_storage_type_none) + { + if (!att->blob.data) + continue; + + if (smtp->_sendCallback) + smtpCB(smtp, att->descr.filename); + + if (smtp->_debug) + esp_mail_debug(s.c_str()); + + buf.clear(); + getInlineHeader(buf, related, att, att->blob.size); + + if (!bdat(smtp, msg, buf.length(), false)) + return false; + + if (smtpSend(smtp, buf.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!sendBlob(smtp, msg, att)) + return false; + + if (!bdat(smtp, msg, 2, false)) + return false; + + if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + } + else + { + if (!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) + _sdOk = sdTest(); + + if (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash) +#if defined(ESP32) + _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + _flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + + if ((!_sdOk && att->file.storage_type == esp_mail_file_storage_type_sd) || (!_flashOk && att->file.storage_type == esp_mail_file_storage_type_flash)) + { + + if (smtp->_sendCallback) + debugInfoP(esp_mail_str_158); + + if (smtp->_debug) + { + std::string e; + appendP(e, esp_mail_str_185, true); + appendP(e, esp_mail_str_158, false); + esp_mail_debug(e.c_str()); + } + + continue; + } + + if (openFileRead(smtp, msg, att, file, s, buf, related, true)) + { + if (!file) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + return false; + } + + if (!sendFile(smtp, msg, att, file)) + return false; + + if (!bdat(smtp, msg, 2, false)) + return false; + + if (smtpSendP(smtp, esp_mail_str_34, false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + } + } + } + } + } + + appendP(s, esp_mail_str_34, true); + appendP(s, esp_mail_str_33, false); + s += related; + appendP(s, esp_mail_str_33, false); + appendP(s, esp_mail_str_34, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + return true; +} + +void ESP_Mail_Client::errorStatusCB(SMTPSession *smtp, int error) +{ + smtp->_smtpStatus.statusCode = error; + std::string s; + + if (smtp->_sendCallback) + { + appendP(s, esp_mail_str_53, true); + s += smtp->errorReason().c_str(); + smtpCB(smtp, s.c_str(), false); + } + + if (smtp->_debug) + { + appendP(s, esp_mail_str_185, true); + s += smtp->errorReason().c_str(); + esp_mail_debug(s.c_str()); + } +} + +size_t ESP_Mail_Client::smtpSendP(SMTPSession *smtp, PGM_P v, bool newline) +{ + if (!reconnect(smtp)) + { + closeTCP(smtp); + return 0; + } + + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!smtp->_tcpConnected) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + char *tmp = strP(v); + size_t len = 0; + + if (newline) + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(tmp); + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_println(tmp); + else + len = smtp->httpClient.stream()->println(tmp); + } + else + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(tmp, false); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_print(tmp); + else + len = smtp->httpClient.stream()->print(tmp); + } + + if (len != strlen(tmp) && len != strlen(tmp) + 2) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + delS(tmp); + + return len; +} + +size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, const char *data, bool newline) +{ + if (!reconnect(smtp)) + { + closeTCP(smtp); + return 0; + } + + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!smtp->_tcpConnected) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + size_t len = 0; + + if (newline) + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(data); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_println(data); + else + len = smtp->httpClient.stream()->println(data); + } + else + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(data, false); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_print(data); + else + len = smtp->httpClient.stream()->print(data); + } + + if (len != strlen(data) && len != strlen(data) + 2) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + return len; +} + +size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, int data, bool newline) +{ + if (!reconnect(smtp)) + { + closeTCP(smtp); + return 0; + } + + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!smtp->_tcpConnected) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + char *tmp = intStr(data); + size_t len = 0; + + if (newline) + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug(tmp); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_println(tmp); + else + len = smtp->httpClient.stream()->println(tmp); + } + else + { + if (smtp->_debugLevel > esp_mail_debug_level_2) + esp_mail_debug_line(tmp, false); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._ns_print(tmp); + else + len = smtp->httpClient.stream()->print(tmp); + } + + if (len != strlen(tmp) && len != strlen(tmp) + 2) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + delS(tmp); + + return len; +} + +size_t ESP_Mail_Client::smtpSend(SMTPSession *smtp, uint8_t *data, size_t size) +{ + if (!reconnect(smtp)) + { + closeTCP(smtp); + return 0; + } + + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return 0; + } + + if (!smtp->_tcpConnected) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + return 0; + } + + size_t len = 0; + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + len = smtp->httpClient._stream()->write(data, size); + else + len = smtp->httpClient.stream()->write(data, size); + + if (len != size) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED); + len = 0; + } + + return len; +} + +bool ESP_Mail_Client::handleSMTPError(SMTPSession *smtp, int err, bool ret) +{ + if (err < 0) + errorStatusCB(smtp, err); + + if (smtp->_tcpConnected) + closeTCP(smtp); + + return ret; +} + +void ESP_Mail_Client::debugInfoP(PGM_P info) +{ + char *tmp = strP(info); + esp_mail_debug(tmp); + delS(tmp); +} + +bool ESP_Mail_Client::sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss) +{ + _sck = sck; + _miso = miso; + _mosi = mosi; + _ss = ss; + _sdConfigSet = true; +#if defined(ESP32) +#if defined(CARD_TYPE_SD) + SPI.begin(_sck, _miso, _mosi, _ss); + return ESP_MAIL_SD_FS.begin(_ss, SPI); +#endif +#elif defined(ESP8266) + return ESP_MAIL_SD_FS.begin(_ss); +#endif + return false; +} + +bool ESP_Mail_Client::sdBegin(void) +{ + _sdConfigSet = false; +#if defined(ESP32) + return ESP_MAIL_SD_FS.begin(); +#elif defined(ESP8266) + return ESP_MAIL_SD_FS.begin(SD_CS_PIN); +#endif + return false; +} + +bool ESP_Mail_Client::sdMMCBegin(const char *mountpoint, bool mode1bit, bool format_if_mount_failed) +{ +#if defined(ESP32) +#if defined(CARD_TYPE_SD_MMC) + _sdConfigSet = true; + sd_mmc_mountpoint = mountpoint; + sd_mmc_mode1bit = mode1bit; + sd_mmc_format_if_mount_failed = format_if_mount_failed; + return ESP_MAIL_SD_FS.begin(mountpoint, mode1bit, format_if_mount_failed); +#endif +#endif + return false; +} + +bool ESP_Mail_Client::sendPartText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, const char *boundary) +{ + std::string header; + + if (strlen(boundary) > 0) + { + appendP(header, esp_mail_str_33, false); + header += boundary; + appendP(header, esp_mail_str_34, false); + } + + if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) + { + if (strlen(msg->text.content_type) > 0) + { + appendP(header, esp_mail_str_25, false); + header += msg->text.content_type; + + if (strlen(msg->text.charSet) > 0) + { + appendP(header, esp_mail_str_97, false); + appendP(header, esp_mail_str_131, false); + appendP(header, esp_mail_str_168, false); + header += msg->text.charSet; + appendP(header, esp_mail_str_136, false); + } + + if (msg->text.flowed) + { + appendP(header, esp_mail_str_97, false); + appendP(header, esp_mail_str_131, false); + appendP(header, esp_mail_str_270, false); + + appendP(header, esp_mail_str_97, false); + appendP(header, esp_mail_str_131, false); + appendP(header, esp_mail_str_110, false); + } + + if (msg->text.embed.enable) + { + appendP(header, esp_mail_str_26, false); + appendP(header, esp_mail_str_164, false); + appendP(header, esp_mail_str_136, false); + char *tmp = getUID(); + msg->text._int.cid = tmp; + delS(tmp); + } + + appendP(header, esp_mail_str_34, false); + } + + if (strlen(msg->text.transfer_encoding) > 0) + { + appendP(header, esp_mail_str_272, false); + header += msg->text.transfer_encoding; + appendP(header, esp_mail_str_34, false); + } + } + else if (type == esp_mail_msg_type_html) + { + if (strlen(msg->text.content_type) > 0) + { + appendP(header, esp_mail_str_25, false); + header += msg->html.content_type; + + if (strlen(msg->html.charSet) > 0) + { + appendP(header, esp_mail_str_97, false); + appendP(header, esp_mail_str_131, false); + appendP(header, esp_mail_str_168, false); + header += msg->html.charSet; + appendP(header, esp_mail_str_136, false); + } + if (msg->html.embed.enable) + { + appendP(header, esp_mail_str_26, false); + appendP(header, esp_mail_str_159, false); + appendP(header, esp_mail_str_136, false); + char *tmp = getUID(); + msg->html._int.cid = tmp; + delS(tmp); + } + appendP(header, esp_mail_str_34, false); + } + + if (strlen(msg->html.transfer_encoding) > 0) + { + appendP(header, esp_mail_str_272, false); + header += msg->html.transfer_encoding; + appendP(header, esp_mail_str_34, false); + } + } + + if ((type == esp_mail_msg_type_html && msg->html.embed.enable) || ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable)) + { + + if ((type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) && msg->text.embed.enable) + { + if (msg->text.embed.type == esp_mail_smtp_embed_message_type_attachment) + appendP(header, esp_mail_str_30, false); + else if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline) + appendP(header, esp_mail_str_299, false); + + if (strlen(msg->text.embed.filename) > 0) + header += msg->text.embed.filename; + else + appendP(header, esp_mail_str_164, false); + appendP(header, esp_mail_str_36, false); + + if (msg->text.embed.type == esp_mail_smtp_embed_message_type_inline) + { + appendP(header, esp_mail_str_300, false); + if (strlen(msg->text.embed.filename) > 0) + header += msg->text.embed.filename; + else + appendP(header, esp_mail_str_159, false); + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_301, false); + header += msg->text._int.cid; + appendP(header, esp_mail_str_15, false); + appendP(header, esp_mail_str_34, false); + } + } + else if (type == esp_mail_msg_type_html && msg->html.embed.enable) + { + if (msg->html.embed.type == esp_mail_smtp_embed_message_type_attachment) + appendP(header, esp_mail_str_30, false); + else if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline) + appendP(header, esp_mail_str_299, false); + + if (strlen(msg->html.embed.filename) > 0) + header += msg->html.embed.filename; + else + appendP(header, esp_mail_str_159, false); + appendP(header, esp_mail_str_36, false); + + if (msg->html.embed.type == esp_mail_smtp_embed_message_type_inline) + { + appendP(header, esp_mail_str_300, false); + if (strlen(msg->html.embed.filename) > 0) + header += msg->html.embed.filename; + else + appendP(header, esp_mail_str_159, false); + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_301, false); + header += msg->html._int.cid; + appendP(header, esp_mail_str_15, false); + appendP(header, esp_mail_str_34, false); + } + } + } + + appendP(header, esp_mail_str_34, false); + + if ((msg->text.blob.size > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (msg->html.blob.size > 0 && type == esp_mail_msg_type_html)) + { + if (!bdat(smtp, msg, header.length(), false)) + return false; + + if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + header.clear(); + + if (!sendBlobBody(smtp, msg, type)) + return false; + } + else if ((strlen(msg->text.file.name) > 0 && (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched)) || (strlen(msg->html.file.name) > 0 && type == esp_mail_msg_type_html)) + { + if (!bdat(smtp, msg, header.length(), false)) + return false; + + if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + header.clear(); + + if (!sendFileBody(smtp, msg, type)) + return false; + } + else + encodingText(smtp, msg, type, header); + + appendP(header, esp_mail_str_34, false); + + if (strlen(boundary) > 0) + appendP(header, esp_mail_str_34, false); + + if (!bdat(smtp, msg, header.length(), false)) + return false; + + if (smtpSend(smtp, header.c_str()) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + return true; +} + +char *ESP_Mail_Client::getUID() +{ + char *tmp = new char[36]; + memset(tmp, 0, 36); + itoa(random(10000000, 20000000), tmp, 10); + return tmp; +} + +bool ESP_Mail_Client::sendBlobBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type) +{ + + if (msg->text.blob.size == 0 && msg->html.blob.size == 0) + return true; + + bool ret = true; + int bufLen = 512; + size_t pos = 0; + int pg = 0, _pg = 0; + + if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) + { + char *tmp = strP(esp_mail_str_325); + + if (strlen(msg->text.transfer_encoding) > 0) + { + if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + { + ret = sendBase64(smtp, msg, (const unsigned char *)msg->text.blob.data, msg->text.blob.size, true, tmp, smtp->_sendCallback != NULL); + delS(tmp); + return ret; + } + } + + int len = msg->text.blob.size; + int available = len; + uint8_t *buf = new uint8_t[bufLen + 1]; + while (available) + { + if (available > bufLen) + available = bufLen; + + memcpy_P(buf, msg->text.blob.data + pos, available); + + if (!bdat(smtp, msg, available, false)) + break; + if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + break; + pos += available; + len -= available; + available = len; + if (smtp->_sendCallback) + { + pg = (float)(100.0f * pos / msg->text.blob.size); + if (pg != _pg) + uploadReport(tmp, pg); + _pg = pg; + } + } + delete[] buf; + delS(tmp); + } + else if (type == esp_mail_message_type::esp_mail_msg_type_html) + { + char *tmp = strP(esp_mail_str_325); + + if (strlen(msg->html.transfer_encoding) > 0) + { + if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + { + ret = sendBase64(smtp, msg, (const unsigned char *)msg->html.blob.data, msg->html.blob.size, true, tmp, smtp->_sendCallback != NULL); + delS(tmp); + return ret; + } + } + int len = msg->html.blob.size; + int available = len; + uint8_t *buf = new uint8_t[bufLen + 1]; + while (available) + { + + if (available > bufLen) + available = bufLen; + + memcpy_P(buf, msg->html.blob.data + pos, available); + + if (!bdat(smtp, msg, available, false)) + { + ret = false; + break; + } + + if (smtpSend(smtp, buf, available) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + { + ret = false; + break; + } + pos += available; + len -= available; + available = len; + if (smtp->_sendCallback) + { + pg = (float)(100.0f * pos / msg->html.blob.size); + if (pg != _pg) + uploadReport(tmp, pg); + _pg = pg; + } + } + delete[] buf; + delS(tmp); + } + return ret; +} + +bool ESP_Mail_Client::sendFileBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type) +{ + + if (strlen(msg->text.file.name) == 0 && strlen(msg->html.file.name) == 0) + return true; + + bool ret = true; + size_t chunkSize = ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE; + size_t writeLen = 0; + int pg = 0, _pg = 0; + + if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) + { + + if (!openFileRead2(smtp, msg, file, msg->text.file.name, msg->text.file.type)) + return false; + + char *tmp = strP(esp_mail_str_326); + + if (strlen(msg->text.transfer_encoding) > 0) + { + if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + { + ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL); + delS(tmp); + return ret; + } + } + + if (file.size() > 0) + { + + if (file.size() < chunkSize) + chunkSize = file.size(); + uint8_t *buf = new uint8_t[chunkSize]; + while (writeLen < file.size() && file.available()) + { + if (writeLen > file.size() - chunkSize) + chunkSize = file.size() - writeLen; + size_t readLen = file.read(buf, chunkSize); + + if (readLen != chunkSize) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + break; + } + + if (!bdat(smtp, msg, chunkSize, false)) + { + ret = false; + break; + } + + if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + { + ret = false; + break; + } + + if (smtp->_sendCallback) + { + pg = (float)(100.0f * writeLen / file.size()); + if (pg != _pg) + uploadReport(tmp, pg); + _pg = pg; + } + writeLen += chunkSize; + } + delete[] buf; + if (smtp->_sendCallback && _pg < 100) + uploadReport(tmp, 100); + + delS(tmp); + return ret && writeLen == file.size(); + } + } + else if (type == esp_mail_message_type::esp_mail_msg_type_html) + { + + if (!openFileRead2(smtp, msg, file, msg->html.file.name, msg->html.file.type)) + return false; + + char *tmp = strP(esp_mail_str_326); + + if (strlen(msg->html.transfer_encoding) > 0) + { + if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + { + ret = sendBase64Stream(smtp, msg, file, tmp, smtp->_sendCallback != NULL); + delS(tmp); + return ret; + } + } + + if (file.size() > 0) + { + + if (file.size() < chunkSize) + chunkSize = file.size(); + uint8_t *buf = new uint8_t[chunkSize]; + while (writeLen < file.size() && file.available()) + { + if (writeLen > file.size() - chunkSize) + chunkSize = file.size() - writeLen; + size_t readLen = file.read(buf, chunkSize); + + if (readLen != chunkSize) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + break; + } + + if (!bdat(smtp, msg, chunkSize, false)) + { + ret = false; + break; + } + + if (smtpSend(smtp, buf, chunkSize) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + { + ret = false; + break; + } + + if (smtp->_sendCallback) + { + pg = (float)(100.0f * writeLen / file.size()); + if (pg != _pg) + uploadReport(tmp, pg); + _pg = pg; + } + writeLen += chunkSize; + } + delete[] buf; + if (smtp->_sendCallback && _pg < 100) + uploadReport(tmp, 100); + + delS(tmp); + return ret && writeLen == file.size(); + } + } + + return false; +} + +void ESP_Mail_Client::encodingText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, std::string &content) +{ + if (type == esp_mail_msg_type_plain || type == esp_mail_msg_type_enriched) + { + std::string s = msg->text.content; + + if (msg->text.flowed) + formatFlowedText(s); + + if (strlen(msg->text.transfer_encoding) > 0) + { + if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + content += encodeBase64Str((const unsigned char *)s.c_str(), s.length()); + else if (strcmp(msg->text.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0) + { + char *out = newS(s.length() * 3 + 1); + encodeQP(s.c_str(), out); + content += out; + delS(out); + } + else + content += s; + } + else + content += s; + } + else if (type == esp_mail_message_type::esp_mail_msg_type_html) + { + char *tmp = nullptr; + std::string s = msg->html.content; + std::string fnd, rep; + SMTP_Attachment *att = nullptr; + for (uint8_t i = 0; i < msg->_att.size(); i++) + { + att = &msg->_att[i]; + if (att->_int.att_type == esp_mail_att_type_inline) + { + std::string filename(att->descr.filename); + + size_t found = filename.find_last_of("/\\"); + if (found != std::string::npos) + filename = filename.substr(found + 1); + + appendP(fnd, esp_mail_str_136, true); + fnd += filename; + appendP(fnd, esp_mail_str_136, false); + + appendP(rep, esp_mail_str_136, true); + appendP(rep, esp_mail_str_302, false); + if (strlen(att->descr.content_id) > 0) + rep += att->descr.content_id; + else + rep += att->_int.cid; + appendP(rep, esp_mail_str_136, false); + + tmp = strReplace((char *)s.c_str(), (char *)fnd.c_str(), (char *)rep.c_str()); + s = tmp; + delS(tmp); + } + } + + if (strlen(msg->html.transfer_encoding) > 0) + { + if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_base64) == 0) + content += encodeBase64Str((const unsigned char *)s.c_str(), s.length()); + else if (strcmp(msg->html.transfer_encoding, Content_Transfer_Encoding::enc_qp) == 0) + { + char *out = newS(strlen(msg->html.content) * 3 + 1); + encodeQP(msg->html.content, out); + content += out; + delS(out); + } + else + content += s; + } + else + content += s; + std::string().swap(s); + } +} + +/* Safe string splitter to avoid strsep bugs*/ +void ESP_Mail_Client::splitTk(std::string &str, std::vector &tk, const char *delim) +{ + std::size_t current, previous = 0; + current = str.find(delim, previous); + std::string s; + while (current != std::string::npos) + { + s = str.substr(previous, current - previous); + tk.push_back(s); + previous = current + strlen(delim); + current = str.find(delim, previous); + } + s = str.substr(previous, current - previous); + tk.push_back(s); + std::string().swap(s); +} + +/** Add the soft line break to the long text line (rfc 3676) + * and add Format=flowed parameter in the plain text content-type header. + * We use the existing white space as a part of this soft line break + * and set delSp="no" parameter to the header. + * + * Some servers are not rfc 3676 compliant. + * This causes the text lines are wrapped instead of joined. + * + * Some mail clients trim the space before the line break + * which makes the soft line break cannot be seen. +*/ +void ESP_Mail_Client::formatFlowedText(std::string &content) +{ + int count = 0; + std::string qms; + int j = 0; + std::vector tokens = std::vector(); + char *stk = strP(esp_mail_str_34); + char *qm = strP(esp_mail_str_15); + splitTk(content, tokens, stk); + content.clear(); + for (size_t i = 0; i < tokens.size(); i++) + { + if (tokens[i].length() > 0) + { + j = 0; + qms.clear(); + while (tokens[i][j] == qm[0]) + { + qms += qm; + j++; + } + softBreak(tokens[i], qms.c_str()); + if (count > 0) + content += stk; + content += tokens[i]; + } + else if (count > 0) + content += stk; + count++; + } + + delS(stk); + delS(qm); + tokens.clear(); +} + +void ESP_Mail_Client::softBreak(std::string &content, const char *quoteMarks) +{ + size_t len = 0; + char *stk = strP(esp_mail_str_131); + std::vector tokens = std::vector(); + splitTk(content, tokens, stk); + content.clear(); + for (size_t i = 0; i < tokens.size(); i++) + { + if (tokens[i].length() > 0) + { + if (len + tokens[i].length() + 3 > FLOWED_TEXT_LEN) + { + /* insert soft crlf */ + content += stk; + appendP(content, esp_mail_str_34, false); + + /* insert quote marks */ + if (strlen(quoteMarks) > 0) + content += quoteMarks; + content += tokens[i]; + len = tokens[i].length(); + } + else + { + if (len > 0) + { + content += stk; + len += strlen(stk); + } + content += tokens[i]; + len += tokens[i].length(); + } + } + } + delS(stk); + tokens.clear(); +} + +bool ESP_Mail_Client::sendMSG(SMTPSession *smtp, SMTP_Message *msg, const std::string &boundary) +{ + std::string alt = getBoundary(15); + std::string s; + + if (numAtt(smtp, esp_mail_att_type_inline, msg) > 0) + { + appendP(s, esp_mail_str_297, true); + s += alt; + appendP(s, esp_mail_str_35, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html) + { + if (!sendInline(smtp, msg, alt, msg->type)) + return false; + } + else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain)) + { + if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str())) + return false; + if (!sendInline(smtp, msg, alt, esp_mail_msg_type_html)) + return false; + } + + appendP(s, esp_mail_str_33, true); + s += alt; + appendP(s, esp_mail_str_33, false); + appendP(s, esp_mail_str_34, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + } + else + { + if (msg->type == esp_mail_msg_type_plain || msg->type == esp_mail_msg_type_enriched || msg->type == esp_mail_msg_type_html) + { + if (!sendPartText(smtp, msg, msg->type, "")) + return false; + } + else if (msg->type == (esp_mail_msg_type_html | esp_mail_msg_type_enriched | esp_mail_msg_type_plain)) + { + appendP(s, esp_mail_str_33, true); + s += boundary; + appendP(s, esp_mail_str_34, false); + appendP(s, esp_mail_str_297, false); + s += alt; + appendP(s, esp_mail_str_35, false); + + if (!bdat(smtp, msg, s.length(), false)) + return false; + + if (smtpSend(smtp, s.c_str(), false) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + if (!sendPartText(smtp, msg, esp_mail_msg_type_plain, alt.c_str())) + return false; + + if (!sendPartText(smtp, msg, esp_mail_msg_type_html, alt.c_str())) + return false; + } + } + return true; +} + +void ESP_Mail_Client::getInlineHeader(std::string &header, const std::string &boundary, SMTP_Attachment *inlineAttach, size_t size) +{ + appendP(header, esp_mail_str_33, false); + header += boundary; + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_25, false); + + if (strlen(inlineAttach->descr.mime) == 0) + { + std::string mime; + mimeFromFile(inlineAttach->descr.filename, mime); + if (mime.length() > 0) + header += mime; + else + appendP(header, esp_mail_str_32, false); + } + else + header += inlineAttach->descr.mime; + + appendP(header, esp_mail_str_26, false); + + std::string filename = inlineAttach->descr.filename; + + size_t found = filename.find_last_of("/\\"); + + if (found != std::string::npos) + filename = filename.substr(found + 1); + + header += filename; + appendP(header, esp_mail_str_36, false); + + appendP(header, esp_mail_str_299, false); + header += filename; + appendP(header, esp_mail_str_327, false); + char *tmp = intStr(size); + header += tmp; + delS(tmp); + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_300, false); + header += filename; + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_301, false); + if (strlen(inlineAttach->descr.content_id) > 0) + header += inlineAttach->descr.content_id; + else + header += inlineAttach->_int.cid; + + appendP(header, esp_mail_str_15, false); + + appendP(header, esp_mail_str_34, false); + + if (strlen(inlineAttach->descr.transfer_encoding) > 0) + { + appendP(header, esp_mail_str_272, false); + header += inlineAttach->descr.transfer_encoding; + appendP(header, esp_mail_str_34, false); + } + appendP(header, esp_mail_str_34, false); + + std::string().swap(filename); +} + +void ESP_Mail_Client::getAttachHeader(std::string &header, const std::string &boundary, SMTP_Attachment *attach, size_t size) +{ + appendP(header, esp_mail_str_33, false); + header += boundary; + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_25, false); + + if (strlen(attach->descr.mime) == 0) + { + std::string mime; + mimeFromFile(attach->descr.filename, mime); + if (mime.length() > 0) + header += mime; + else + appendP(header, esp_mail_str_32, false); + } + else + header += attach->descr.mime; + + appendP(header, esp_mail_str_26, false); + + std::string filename = attach->descr.filename; + + size_t found = filename.find_last_of("/\\"); + if (found != std::string::npos) + filename = filename.substr(found + 1); + + header += filename; + appendP(header, esp_mail_str_36, false); + + if (!attach->_int.parallel) + { + appendP(header, esp_mail_str_30, false); + header += filename; + appendP(header, esp_mail_str_327, false); + char *tmp = intStr(size); + header += tmp; + delS(tmp); + appendP(header, esp_mail_str_34, false); + } + + if (strlen(attach->descr.transfer_encoding) > 0) + { + appendP(header, esp_mail_str_272, false); + header += attach->descr.transfer_encoding; + appendP(header, esp_mail_str_34, false); + } + + appendP(header, esp_mail_str_34, false); + + std::string().swap(filename); +} + +void ESP_Mail_Client::getRFC822PartHeader(SMTPSession *smtp, std::string &header, const std::string &boundary) +{ + appendP(header, esp_mail_str_33, false); + header += boundary; + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_25, false); + + appendP(header, esp_mail_str_123, false); + appendP(header, esp_mail_str_34, false); + + appendP(header, esp_mail_str_98, false); + + appendP(header, esp_mail_str_34, false); +} + +void ESP_Mail_Client::smtpCBP(SMTPSession *smtp, PGM_P info, bool success) +{ + std::string s; + appendP(s, info, true); + smtp->_cbData._info = s; + smtp->_cbData._success = success; + smtp->_sendCallback(smtp->_cbData); + std::string().swap(s); +} + +void ESP_Mail_Client::smtpCB(SMTPSession *smtp, const char *info, bool success) +{ + smtp->_cbData._info = info; + smtp->_cbData._success = success; + smtp->_sendCallback(smtp->_cbData); +} + +void ESP_Mail_Client::imapCBP(IMAPSession *imap, PGM_P info, bool success) +{ + char *tmp = strP(info); + imap->_cbData._info = tmp; + imap->_cbData._success = success; + imap->_readCallback(imap->_cbData); + delS(tmp); +} + +void ESP_Mail_Client::imapCB(IMAPSession *imap, const char *info, bool success) +{ + imap->_cbData._info = info; + imap->_cbData._success = success; + imap->_readCallback(imap->_cbData); +} + +void ESP_Mail_Client::strcat_c(char *str, char c) +{ + for (; *str; str++) + ; + *str++ = c; + *str++ = 0; +} +int ESP_Mail_Client::strpos(const char *haystack, const char *needle, int offset) +{ + size_t len = strlen(haystack); + size_t len2 = strlen(needle); + if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len || offset < 0) + return -1; + char *_haystack = newS(len - offset + 1); + _haystack[len - offset] = 0; + strncpy(_haystack, haystack + offset, len - offset); + char *p = stristr(_haystack, needle); + int r = -1; + if (p) + r = p - _haystack + offset; + delS(_haystack); + return r; +} + +char *ESP_Mail_Client::stristr(const char *str1, const char *str2) +{ + const char *p1 = str1; + const char *p2 = str2; + const char *r = *p2 == 0 ? str1 : 0; + + while (*p1 != 0 && *p2 != 0) + { + if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) + { + if (r == 0) + r = p1; + p2++; + } + else + { + p2 = str2; + if (r != 0) + p1 = r + 1; + + if (tolower((unsigned char)*p1) == tolower((unsigned char)*p2)) + { + r = p1; + p2++; + } + else + r = 0; + } + + p1++; + } + + return *p2 == 0 ? (char *)r : 0; +} + +char *ESP_Mail_Client::rstrstr(const char *haystack, const char *needle) +{ + size_t needle_length = strlen(needle); + const char *haystack_end = haystack + strlen(haystack) - needle_length; + const char *p; + size_t i; + for (p = haystack_end; p >= haystack; --p) + { + for (i = 0; i < needle_length; ++i) + { + if (p[i] != needle[i]) + goto next; + } + return (char *)p; + next:; + } + return 0; +} + +int ESP_Mail_Client::rstrpos(const char *haystack, const char *needle, int offset) +{ + size_t len = strlen(haystack); + size_t len2 = strlen(needle); + if (len == 0 || len < len2 || len2 == 0 || offset >= (int)len) + return -1; + char *_haystack = newS(len - offset + 1); + _haystack[len - offset] = 0; + strncpy(_haystack, haystack + offset, len - offset); + char *p = rstrstr(_haystack, needle); + int r = -1; + if (p) + r = p - _haystack + offset; + delS(_haystack); + return r; +} + +int ESP_Mail_Client::readLine(WiFiClient *stream, char *buf, int bufLen, bool crlf, int &count) +{ + int ret = -1; + char c = 0; + char _c = 0; + int idx = 0; + if (!stream) + return idx; + while (stream->available() && idx < bufLen) + { + ret = stream->read(); + if (ret > -1) + { + if (idx >= bufLen - 1) + return idx; + + c = (char)ret; + strcat_c(buf, c); + idx++; + count++; + if (_c == '\r' && c == '\n') + { + if (!crlf) + { + buf[idx - 2] = 0; + idx -= 2; + } + return idx; + } + _c = c; + } + if (!stream) + return idx; + } + return idx; +} + +#if defined(ESP32) +int ESP_Mail_Client::_readLine(ESP_Mail_WCS32 *stream, char *buf, int bufLen, bool crlf, int &count) +#elif defined(ESP8266) +int ESP_Mail_Client::_readLine(ESP_Mail::ESP_Mail_WCS *stream, char *buf, int bufLen, bool crlf, int &count) +#endif +{ + int ret = -1; + char c = 0; + char _c = 0; + int idx = 0; + if (!stream) + return idx; + while (stream->_ns_available() && idx < bufLen) + { + ret = stream->_ns_read(); + if (ret > -1) + { + if (idx >= bufLen - 1) + return idx; + + c = (char)ret; + strcat_c(buf, c); + idx++; + count++; + if (_c == '\r' && c == '\n') + { + if (!crlf) + { + buf[idx - 2] = 0; + idx -= 2; + } + return idx; + } + _c = c; + } + if (!stream) + return idx; + } + return idx; +} + +int ESP_Mail_Client::getMSGNUM(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, bool &endSearch, int &nump, const char *key, const char *pc) +{ + int ret = -1; + char c = 0; + int idx = 0; + int num = 0; + while (available(imap) > 0 && idx < bufLen) + { + delay(0); + if (!imap->_secure) + ret = imap->httpClient._stream()->read(); + else + ret = imap->httpClient.stream()->read(); + + if (ret > -1) + { + + if (idx >= bufLen - 1) + return idx; + + c = (char)ret; + + if (c == '\n') + c = ' '; + + strcat_c(buf, c); + idx++; + + if (chunkIdx == 0) + { + if (strcmp(buf, key) == 0) + { + chunkIdx++; + return 0; + } + + if (strposP(buf, esp_mail_imap_response_1, 0) > -1) + goto end_search; + } + else + { + if (c == ' ') + { + imap->_mbif._searchCount++; + if (imap->_config->enable.recent_sort) + { + imap->_msgNum.push_back(atoi(buf)); + if (imap->_msgNum.size() > imap->_config->limit.search) + imap->_msgNum.erase(imap->_msgNum.begin()); + } + else + { + if (imap->_msgNum.size() < imap->_config->limit.search) + imap->_msgNum.push_back(atoi(buf)); + } + + if (imap->_debug) + { + num = (float)(100.0f * imap->_mbif._searchCount / imap->_mbif._msgCount); + if (nump != num) + { + nump = num; + searchReport(num, pc); + } + } + + chunkIdx++; + return idx; + } + else if (c == '$') + { + if (imap->_config->enable.recent_sort) + std::sort(imap->_msgNum.begin(), imap->_msgNum.end(), compFunc); + + goto end_search; + } + } + } + } + + return idx; + +end_search: + + endSearch = true; + int read = available(imap); + + if (!imap->_secure) + idx = imap->httpClient._stream()->readBytes(buf + idx, read); + else + idx = imap->httpClient.stream()->readBytes(buf + idx, read); + + return idx; +} + +struct esp_mail_message_part_info_t *ESP_Mail_Client::cPart(IMAPSession *imap) +{ + return &cHeader(imap)->part_headers[imap->_cPartIdx]; +} + +struct esp_mail_message_header_t *ESP_Mail_Client::cHeader(IMAPSession *imap) +{ + return &imap->_headers[cIdx(imap)]; +} + +void ESP_Mail_Client::handleHeader(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, struct esp_mail_message_header_t &header, int &headerState, int &octetCount) +{ + + char *tmp = nullptr; + if (chunkIdx == 0) + { + if (strposP(buf, esp_mail_str_324, 0) != -1 && buf[0] == '*') + chunkIdx++; + + tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); + if (tmp) + { + octetCount = 2; + header.header_data_len = atoi(tmp); + delS(tmp); + } + } + else + { + if (octetCount > header.header_data_len + 2) + return; + + if (strcmpP(buf, 0, esp_mail_str_10)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_from; + tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1); + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + else if (strcmpP(buf, 0, esp_mail_str_11)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_to; + tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1); + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + else if (strcmpP(buf, 0, esp_mail_str_276)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_cc; + tmp = subStr(buf, esp_mail_str_276, NULL, 0, -1); + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + else if (strcmpP(buf, 0, esp_mail_str_279)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_subject; + tmp = subStr(buf, esp_mail_str_279, NULL, 0, -1); + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + else if (strcmpP(buf, 0, esp_mail_str_25)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type; + tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); + if (tmp) + { + setHeader(imap, buf, header, headerState); + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_172)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding; + tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1); + if (tmp) + { + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_190)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_accept_language; + tmp = subStr(buf, esp_mail_str_190, NULL, 0, -1); + if (tmp) + { + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_191)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_language; + tmp = subStr(buf, esp_mail_str_191, NULL, 0, -1); + if (tmp) + { + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_273)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_date; + tmp = subStr(buf, esp_mail_str_273, NULL, 0, -1); + if (tmp) + { + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_274)) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_msg_id; + tmp = subStr(buf, esp_mail_str_274, NULL, 0, -1); + if (tmp) + { + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + chunkIdx++; + } +} + +void ESP_Mail_Client::setHeader(IMAPSession *imap, char *buf, struct esp_mail_message_header_t &header, int state) +{ + switch (state) + { + case esp_mail_imap_header_state::esp_mail_imap_state_from: + header.from += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_to: + header.to += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_cc: + header.cc += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_subject: + header.subject += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_content_type: + header.content_type += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_content_transfer_encoding: + header.content_transfer_encoding += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_accept_language: + header.accept_language += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_content_language: + header.content_language += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_date: + header.date += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_msg_id: + header.message_id += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_char_set: + header.char_set += buf; + break; + case esp_mail_imap_header_state::esp_mail_imap_state_boundary: + header.boundary += buf; + break; + default: + break; + } +} + +void ESP_Mail_Client::handlePartHeader(IMAPSession *imap, char *buf, int &chunkIdx, struct esp_mail_message_part_info_t &part) +{ + char *tmp = nullptr; + if (chunkIdx == 0) + { + tmp = subStr(buf, esp_mail_imap_response_7, NULL, 0, -1); + if (tmp) + { + delS(tmp); + tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); + if (tmp) + { + chunkIdx++; + part.octetLen = atoi(tmp); + delS(tmp); + } + } + } + else + { + if (strcmpP(buf, 0, esp_mail_str_25)) + { + tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); + bool con_type = false; + if (tmp) + { + con_type = true; + part.content_type = tmp; + delS(tmp); + int p1 = strposP(part.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0); + if (p1 != -1) + { + p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1; + part.multipart = true; + //inline or embedded images + if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_related; + //multiple text formats e.g. plain, html, enriched + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative; + //medias + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel; + //rfc822 encapsulated + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest; + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_report; + //others can be attachments + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1) + part.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed; + } + + p1 = strposP(part.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0); + if (p1 != -1) + { + p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1; + if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1) + part.message_sub_type = esp_mail_imap_message_sub_type_rfc822; + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1) + part.message_sub_type = esp_mail_imap_message_sub_type_partial; + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1) + part.message_sub_type = esp_mail_imap_message_sub_type_external_body; + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1) + part.message_sub_type = esp_mail_imap_message_sub_type_delivery_status; + } + + p1 = strpos(part.content_type.c_str(), esp_mail_imap_descrete_media_type_t::text, 0); + if (p1 != -1) + { + p1 += strlen(esp_mail_imap_descrete_media_type_t::text) + 1; + if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::plain, p1) != -1) + part.msg_type = esp_mail_msg_type_plain; + else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::enriched, p1) != -1) + part.msg_type = esp_mail_msg_type_enriched; + else if (strpos(part.content_type.c_str(), esp_mail_imap_media_text_sub_type_t::html, p1) != -1) + part.msg_type = esp_mail_msg_type_html; + else + part.msg_type = esp_mail_msg_type_plain; + } + } + + if (con_type) + { + if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched) + { + tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0); + if (tmp) + { + part.charset = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); + if (tmp) + { + part.charset = tmp; + delS(tmp); + } + } + + if (strposP(buf, esp_mail_str_275, 0) > -1 || strposP(buf, esp_mail_str_270, 0) > -1) + part.plain_flowed = true; + if (strposP(buf, esp_mail_str_259, 0) > -1 || strposP(buf, esp_mail_str_257, 0) > -1) + part.plain_delsp = true; + } + + if (part.charset.length() == 0) + { + tmp = subStr(buf, esp_mail_str_168, esp_mail_str_136, 0); + if (tmp) + { + part.charset = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); + if (tmp) + { + part.charset = tmp; + delS(tmp); + } + } + } + + tmp = subStr(buf, esp_mail_str_170, esp_mail_str_136, 0); + if (tmp) + { + part.name = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_171, NULL, 0, -1); + if (tmp) + { + part.name = tmp; + delS(tmp); + } + } + } + } + else if (strcmpP(buf, 0, esp_mail_str_172)) + { + tmp = subStr(buf, esp_mail_str_172, NULL, 0, -1); + if (tmp) + { + part.content_transfer_encoding = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_174)) + { + tmp = subStr(buf, esp_mail_str_174, NULL, 0, -1); + if (tmp) + { + part.descr = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_175)) + { + tmp = subStr(buf, esp_mail_str_175, esp_mail_str_97, 0); + if (tmp) + { + //don't count altenative part text and html as embedded contents + if (cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative) + { + part.content_disposition = tmp; + if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::attachment) == 0) + part.attach_type = esp_mail_att_type_attachment; + else if (strcmp(tmp, esp_mail_imap_content_disposition_type_t::inline_) == 0) + part.attach_type = esp_mail_att_type_inline; + } + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_150)) + { + tmp = subStr(buf, esp_mail_str_150, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.sender = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_10)) + { + tmp = subStr(buf, esp_mail_str_10, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.from = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_11)) + { + tmp = subStr(buf, esp_mail_str_11, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.to = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_12)) + { + tmp = subStr(buf, esp_mail_str_12, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.cc = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_184)) + { + tmp = subStr(buf, esp_mail_str_184, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.reply_to = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_134)) + { + tmp = subStr(buf, esp_mail_str_134, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.comment = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_24)) + { + tmp = subStr(buf, esp_mail_str_24, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.subject = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_189)) + { + tmp = subStr(buf, esp_mail_str_189, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.messageID = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_46)) + { + tmp = subStr(buf, esp_mail_str_46, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.return_path = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_99)) + { + tmp = subStr(buf, esp_mail_str_99, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.date = tmp; + delS(tmp); + } + } + else if (strcmpP(buf, 0, esp_mail_str_145)) + { + tmp = subStr(buf, esp_mail_str_145, NULL, 0, -1); + if (tmp) + { + part.rfc822_header.keyword = tmp; + delS(tmp); + } + } + + if (part.content_disposition.length() > 0) + { + tmp = subStr(buf, esp_mail_str_176, esp_mail_str_136, 0); + if (tmp) + { + part.filename = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_177, NULL, 0, -1); + if (tmp) + { + part.filename = tmp; + delS(tmp); + } + } + + tmp = subStr(buf, esp_mail_str_178, esp_mail_str_97, 0); + if (tmp) + { + part.attach_data_size = atoi(tmp); + delS(tmp); + cHeader(imap)->total_attach_data_size += part.attach_data_size; + part.sizeProp = true; + } + else + { + tmp = subStr(buf, esp_mail_str_178, NULL, 0, -1); + if (tmp) + { + part.attach_data_size = atoi(tmp); + delS(tmp); + cHeader(imap)->total_attach_data_size += part.attach_data_size; + part.sizeProp = true; + } + } + + tmp = subStr(buf, esp_mail_str_179, esp_mail_str_136, 0); + if (tmp) + { + part.creation_date = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_180, NULL, 0, -1); + if (tmp) + { + part.creation_date = tmp; + delS(tmp); + } + } + + tmp = subStr(buf, esp_mail_str_181, esp_mail_str_136, 0); + if (tmp) + { + part.modification_date = tmp; + delS(tmp); + } + else + { + tmp = subStr(buf, esp_mail_str_182, NULL, 0, -1); + if (tmp) + { + part.modification_date = tmp; + delS(tmp); + } + } + } + + chunkIdx++; + } +} + +char *ESP_Mail_Client::subStr(const char *buf, PGM_P beginH, PGM_P endH, int beginPos, int endPos) +{ + + char *tmp = nullptr; + int p1 = strposP(buf, beginH, beginPos); + if (p1 != -1) + { + int p2 = -1; + if (endPos == 0) + p2 = strposP(buf, endH, p1 + strlen_P(beginH)); + + if (p2 == -1) + p2 = strlen(buf); + + int len = p2 - p1 - strlen_P(beginH); + tmp = newS(len + 1); + memcpy(tmp, &buf[p1 + strlen_P(beginH)], len); + return tmp; + } + + return nullptr; +} + +void ESP_Mail_Client::handleAuth(SMTPSession *smtp, char *buf) +{ + if (strposP(buf, esp_mail_smtp_response_1, 0) > -1) + { + if (strposP(buf, esp_mail_smtp_response_2, 0) > -1) + smtp->_auth_capability.login = true; + if (strposP(buf, esp_mail_smtp_response_3, 0) > -1) + smtp->_auth_capability.plain = true; + if (strposP(buf, esp_mail_smtp_response_4, 0) > -1) + smtp->_auth_capability.xoauth2 = true; + if (strposP(buf, esp_mail_smtp_response_11, 0) > -1) + smtp->_auth_capability.cram_md5 = true; + if (strposP(buf, esp_mail_smtp_response_12, 0) > -1) + smtp->_auth_capability.digest_md5 = true; + } + else if (strposP(buf, esp_mail_smtp_response_5, 0) > -1) + smtp->_auth_capability.start_tls = true; + else if (strposP(buf, esp_mail_smtp_response_6, 0) > -1) + smtp->_send_capability._8bitMIME = true; + else if (strposP(buf, esp_mail_smtp_response_7, 0) > -1) + smtp->_send_capability.binaryMIME = true; + else if (strposP(buf, esp_mail_smtp_response_8, 0) > -1) + smtp->_send_capability.chunking = true; + else if (strposP(buf, esp_mail_smtp_response_9, 0) > -1) + smtp->_send_capability.utf8 = true; + else if (strposP(buf, esp_mail_smtp_response_10, 0) > -1) + smtp->_send_capability.pipelining = true; + else if (strposP(buf, esp_mail_smtp_response_13, 0) > -1) + smtp->_send_capability.dsn = true; +} + +int ESP_Mail_Client::available(SMTPSession *smtp) +{ + int sz = 0; + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + { + if (smtp->httpClient._stream()) + sz = smtp->httpClient._stream()->_ns_available(); + } + else + { + if (smtp->httpClient.stream()) + sz = smtp->httpClient.stream()->available(); + } + + return sz; +} + +bool ESP_Mail_Client::handleSMTPResponse(SMTPSession *smtp, esp_mail_smtp_status_code respCode, int errCode) +{ + if (!reconnect(smtp)) + return false; + + bool ret = false; + char *response = nullptr; + int readLen = 0; + long dataTime = millis(); + int chunkBufSize = 0; + std::string s, r; + int chunkIndex = 0; + int count = 0; + bool completedResponse = false; + smtp->_smtpStatus.statusCode = 0; + smtp->_smtpStatus.respCode = 0; + smtp->_smtpStatus.text.clear(); + uint8_t minResLen = 5; + struct esp_mail_smtp_response_status_t status; + + chunkBufSize = available(smtp); + + while (smtp->_tcpConnected && chunkBufSize <= 0) + { + if (!reconnect(smtp, dataTime)) + return false; + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return false; + } + chunkBufSize = available(smtp); + delay(0); + } + + dataTime = millis(); + + if (chunkBufSize > 1) + { + while (!completedResponse) + { + delay(0); + + if (!reconnect(smtp, dataTime)) + return false; + + if (!connected(smtp)) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return false; + } + + chunkBufSize = available(smtp); + + if (chunkBufSize <= 0) + break; + + if (chunkBufSize > 0) + { + chunkBufSize = 512; + response = newS(chunkBufSize + 1); + + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + readLen = _readLine(smtp->httpClient._stream(), response, chunkBufSize, false, count); + else + readLen = readLine(smtp->httpClient.stream(), response, chunkBufSize, false, count); + + if (readLen) + { + if (smtp->_smtp_cmd != esp_mail_smtp_command::esp_mail_smtp_cmd_initial_state) + { + //sometimes server sent multiple lines response + //sometimes rx buffer may not ready for a while + if (strlen(response) < minResLen) + { + r += response; + chunkBufSize = 0; + while (chunkBufSize == 0) + { + delay(0); + if (!reconnect(smtp, dataTime)) + return false; + chunkBufSize = available(smtp); + } + } + else + { + if (r.length() > 0) + { + r += response; + memset(response, 0, chunkBufSize); + strcpy(response, r.c_str()); + } + + if (smtp->_debugLevel > esp_mail_debug_level_1) + esp_mail_debug((const char *)response); + } + + if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_greeting) + handleAuth(smtp, response); + } + + getResponseStatus(response, respCode, 0, status); + + //get the status code again for unexpected return code + if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_start_tls || status.respCode == 0) + getResponseStatus(response, esp_mail_smtp_status_code_0, 0, status); + + ret = respCode == status.respCode; + smtp->_smtpStatus = status; + + if (status.respCode > 0 && (status.respCode < 400 || status.respCode == respCode)) + ret = true; + + if (smtp->_debug && strlen(response) >= minResLen) + { + appendP(s, esp_mail_str_260, true); + if (smtp->_smtpStatus.respCode != esp_mail_smtp_status_code_334) + s += response; + else + { + //base64 response + size_t olen; + char *decoded = (char *)decodeBase64((const unsigned char *)status.text.c_str(), status.text.length(), &olen); + if (decoded && olen > 0) + { + olen += s.length(); + s += decoded; + s[olen] = 0; + delete[] decoded; + } + } + esp_mail_debug(s.c_str()); + r.clear(); + } + + completedResponse = smtp->_smtpStatus.respCode > 0 && status.text.length() > minResLen; + + if (smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_auth && smtp->_smtpStatus.respCode == esp_mail_smtp_status_code_334) + { + if (authFailed(response, readLen, chunkIndex, 4)) + { + smtp->_smtpStatus.statusCode = -1; + ret = false; + } + } + + chunkIndex++; + + if (smtp->_chunkedEnable && smtp->_smtp_cmd == esp_mail_smtp_command::esp_mail_smtp_cmd_chunk_termination) + completedResponse = smtp->_chunkCount == chunkIndex; + } + delS(response); + } + } + + if (!ret) + handleSMTPError(smtp, errCode, false); + } + + return ret; +} + +void ESP_Mail_Client::getResponseStatus(const char *buf, esp_mail_smtp_status_code respCode, int beginPos, struct esp_mail_smtp_response_status_t &status) +{ + std::string s; + char *tmp = nullptr; + int p1 = 0; + if (respCode > esp_mail_smtp_status_code_0) + { + tmp = intStr((int)respCode); + s = tmp; + appendP(s, esp_mail_str_131, false); + delS(tmp); + p1 = strpos(buf, (const char *)s.c_str(), beginPos); + } + + if (p1 != -1) + { + int ofs = s.length() - 2; + if (ofs < 0) + ofs = 1; + + int p2 = strposP(buf, esp_mail_str_131, p1 + ofs); + + if (p2 < 4 && p2 > -1) + { + tmp = newS(p2 + 1); + memcpy(tmp, &buf[p1], p2); + status.respCode = atoi(tmp); + delS(tmp); + + p1 = p2 + 1; + p2 = strlen(buf); + if (p2 > p1) + { + tmp = newS(p2 + 1); + memcpy(tmp, &buf[p1], p2 - p1); + status.text = tmp; + delS(tmp); + } + } + } +} + +void ESP_Mail_Client::closeTCP(SMTPSession *smtp) +{ + + if (smtp->_tcpConnected) + { + if (smtp->httpClient.stream()) + { + if (connected(smtp)) + { + if (smtp->_sesson_cfg->secure.startTLS || !smtp->_secure) + smtp->httpClient._stream()->stop(); + else + smtp->httpClient.stream()->stop(); + } + } + _lastReconnectMillis = millis(); + } + smtp->_tcpConnected = false; +} + +void ESP_Mail_Client::closeTCP(IMAPSession *imap) +{ + + if (imap->_tcpConnected) + { + if (imap->httpClient.stream()) + { + if (connected(imap)) + { + if (!imap->_secure) + imap->httpClient._stream()->stop(); + else + imap->httpClient.stream()->stop(); + } + } + _lastReconnectMillis = millis(); + } + imap->_tcpConnected = false; +} + +#if defined(ESP32) +void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient32 &httpClient, ESP_Mail_Session *session, std::shared_ptr caCert) +#elif defined(ESP8266) +void ESP_Mail_Client::setSecure(ESP_Mail_HTTPClient &httpClient, ESP_Mail_Session *session, std::shared_ptr caCert) +#endif +{ + +#if defined(ESP32) + if (httpClient._certType == -1) + { + if (strlen(session->certificate.cert_file) == 0) + { + if (caCert != nullptr) + httpClient.setCACert(caCert.get()); + else + httpClient.setCACert(nullptr); + } + else + { + httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type); + } + } +#elif defined(ESP8266) + + if (httpClient._certType == -1) + { + +#ifndef USING_AXTLS + if (!MailClient._clockReady && (strlen(session->certificate.cert_file) > 0 || caCert != nullptr)) + { + MailClient.setClock(MailClient._gmtOffset); + httpClient._clockReady = MailClient._clockReady; + } +#endif + if (strlen(session->certificate.cert_file) == 0) + { + if (caCert != nullptr) + httpClient.setCACert(caCert.get()); + else + httpClient.setCACert(nullptr); + } + else + { + httpClient.setCertFile(session->certificate.cert_file, session->certificate.cert_file_storage_type, MailClient._sdPin); + } + } +#endif +} + +bool ESP_Mail_Client::ethLinkUp() +{ + bool ret = false; +#if defined(ESP32) + char *ip = strP(esp_mail_str_328); + if (strcmp(ETH.localIP().toString().c_str(), ip) != 0) +// ret = ETH.linkUp(); + ret = true; + delS(ip); +#endif + return ret; +} + +bool ESP_Mail_Client::reconnect(SMTPSession *smtp, unsigned long dataTime) +{ + + bool status = WiFi.status() == WL_CONNECTED || ethLinkUp(); + + if (dataTime > 0) + { + if (millis() - dataTime > smtp->httpClient.tcpTimeout) + { + closeTCP(smtp); + errorStatusCB(smtp, MAIL_CLIENT_ERROR_READ_TIMEOUT); + return false; + } + } + + if (!status) + { + if (smtp->_tcpConnected) + closeTCP(smtp); + + errorStatusCB(smtp, MAIL_CLIENT_ERROR_CONNECTION_LOST); + + if (millis() - _lastReconnectMillis > _reconnectTimeout && !smtp->_tcpConnected) + { +#if defined(ESP32) + esp_wifi_connect(); +#elif defined(ESP8266) + WiFi.reconnect(); +#endif + _lastReconnectMillis = millis(); + } + + status = WiFi.status() == WL_CONNECTED || ethLinkUp(); + } + + return status; +} + +bool ESP_Mail_Client::reconnect(IMAPSession *imap, unsigned long dataTime, bool downloadRequest) +{ + + bool status = WiFi.status() == WL_CONNECTED || ethLinkUp(); + + if (dataTime > 0) + { + if (millis() - dataTime > imap->httpClient.tcpTimeout) + { + + closeTCP(imap); + + if (imap->_headers.size() > 0) + { + if (downloadRequest) + { + errorStatusCB(imap, IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT); + if (cHeader(imap)->part_headers.size() > 0) + cPart(imap)->download_error = imap->errorReason().c_str(); + } + else + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_READ_TIMEOUT); + cHeader(imap)->error_msg = imap->errorReason().c_str(); + } + } + return false; + } + } + + if (!status) + { + + if (imap->_tcpConnected) + closeTCP(imap); + + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + + if (imap->_headers.size() > 0) + { + if (downloadRequest) + cPart(imap)->download_error = imap->errorReason().c_str(); + else + cHeader(imap)->error_msg = imap->errorReason().c_str(); + } + + if (millis() - _lastReconnectMillis > _reconnectTimeout && !imap->_tcpConnected) + { +#if defined(ESP32) + esp_wifi_connect(); +#elif defined(ESP8266) + WiFi.reconnect(); +#endif + _lastReconnectMillis = millis(); + } + + status = WiFi.status() == WL_CONNECTED || ethLinkUp(); + } + + return status; +} + +void ESP_Mail_Client::delS(char *p) +{ + if (p != nullptr) + delete[] p; +} + +char *ESP_Mail_Client::newS(size_t len) +{ + char *p = new char[len]; + memset(p, 0, len); + return p; +} + +char *ESP_Mail_Client::newS(char *p, size_t len) +{ + delS(p); + p = newS(len); + return p; +} + +char *ESP_Mail_Client::newS(char *p, size_t len, char *d) +{ + delS(p); + p = newS(len); + strcpy(p, d); + return p; +} + +bool ESP_Mail_Client::strcmpP(const char *buf, int ofs, PGM_P beginH) +{ + char *tmp = nullptr; + if (ofs < 0) + { + int p = strposP(buf, beginH, 0); + if (p == -1) + return false; + ofs = p; + } + tmp = strP(beginH); + char *tmp2 = newS(strlen_P(beginH) + 1); + memcpy(tmp2, &buf[ofs], strlen_P(beginH)); + tmp2[strlen_P(beginH)] = 0; + bool ret = (strcasecmp(tmp, tmp2) == 0); + delS(tmp); + delS(tmp2); + return ret; +} + +int ESP_Mail_Client::strposP(const char *buf, PGM_P beginH, int ofs) +{ + char *tmp = strP(beginH); + int p = strpos(buf, tmp, ofs); + delS(tmp); + return p; +} + +char *ESP_Mail_Client::strP(PGM_P pgm) +{ + size_t len = strlen_P(pgm) + 1; + char *buf = newS(len); + strcpy_P(buf, pgm); + buf[len - 1] = 0; + return buf; +} + +void ESP_Mail_Client::appendP(std::string &buf, PGM_P p, bool empty) +{ + if (empty) + buf.clear(); + char *b = strP(p); + buf += b; + delS(b); +} + +char *ESP_Mail_Client::intStr(int value) +{ + char *buf = newS(36); + memset(buf, 0, 36); + itoa(value, buf, 10); + return buf; +} + +int ESP_Mail_Client::available(IMAPSession *imap) +{ + int sz = 0; + if (!imap->_secure) + { + if (imap->httpClient._stream()) + sz = imap->httpClient._stream()->_ns_available(); + } + else + { + if (imap->httpClient.stream()) + sz = imap->httpClient.stream()->available(); + } + return sz; +} + +bool ESP_Mail_Client::handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession) +{ + + if (!reconnect(imap)) + return false; + + esp_mail_imap_response_status imapResp = esp_mail_imap_response_status::esp_mail_imap_resp_unknown; + char *response = nullptr; + int readLen = 0; + long dataTime = millis(); + int chunkBufSize = available(imap); + int chunkIdx = 0; + std::string s; + bool completedResponse = false; + bool endSearch = false; + struct esp_mail_message_header_t header; + struct esp_mail_message_part_info_t part; + + std::string filePath = ""; + bool downloadRequest = false; + int reportState = 0; + int octetCount = 0; + int octetLength = 0; + int oCount = 0; + bool tmo = false; + int headerState = 0; + int scnt = 0; + int dcnt = -1; + char *skey = nullptr; + char *spc = nullptr; + char *lastBuf = nullptr; + char *tmp = nullptr; + bool crLF = imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text && strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31); + + while (imap->_tcpConnected && chunkBufSize <= 0) + { + if (!reconnect(imap, dataTime)) + return false; + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return false; + } + chunkBufSize = available(imap); + delay(0); + } + + dataTime = millis(); + + if (chunkBufSize > 1) + { + if (imap->_imap_cmd == esp_mail_imap_cmd_examine) + { + imap->_mbif.clear(); + imap->_mbif._msgCount = 0; + imap->_nextUID = ""; + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_search) + { + imap->_mbif._searchCount = 0; + imap->_msgNum.clear(); + } + + chunkBufSize = 512; + response = newS(chunkBufSize + 1); + + if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) + { + skey = strP(esp_mail_imap_response_6); + spc = strP(esp_mail_str_92); + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) + lastBuf = newS(BASE64_CHUNKED_LEN + 1); + + while (!completedResponse) + { + delay(0); + if (!reconnect(imap, dataTime) || !connected(imap)) + { + if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) + { + delS(skey); + delS(spc); + } + + if (!connected(imap)) + { + errorStatusCB(imap, MAIL_CLIENT_ERROR_CONNECTION_LOST); + return false; + } + return false; + } + chunkBufSize = available(imap); + + if (chunkBufSize > 0) + { + chunkBufSize = 512; + + if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) + { + readLen = getMSGNUM(imap, response, chunkBufSize, chunkIdx, endSearch, scnt, skey, spc); + imap->_mbif._availableItems = imap->_msgNum.size(); + } + else + { + if (!imap->_secure) + readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, crLF, octetCount); + else + readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, crLF, octetCount); + } + + if (readLen) + { + + if (imap->_debugLevel > esp_mail_debug_level_1) + { + if (imap->_imap_cmd != esp_mail_imap_cmd_search && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_text && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_attachment && imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_inline) + esp_mail_debug((const char *)response); + } + + if (imap->_imap_cmd != esp_mail_imap_cmd_search || (imap->_imap_cmd == esp_mail_imap_cmd_search && endSearch)) + imapResp = imapResponseStatus(imap, response); + + if (imapResp != esp_mail_imap_response_status::esp_mail_imap_resp_unknown) + { + if (imap->_debugLevel > esp_mail_debug_level_1) + { + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) + esp_mail_debug((const char *)response); + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_close) + completedResponse = true; + else + { + //some IMAP servers advertise CAPABILITY in their responses + //try to read the next available response + memset(response, 0, chunkBufSize); + if (!imap->_secure) + readLen = _readLine(imap->httpClient._stream(), response, chunkBufSize, true, octetCount); + else + readLen = readLine(imap->httpClient.stream(), response, chunkBufSize, true, octetCount); + if (readLen) + { + completedResponse = false; + imapResp = imapResponseStatus(imap, response); + if (imapResp > esp_mail_imap_response_status::esp_mail_imap_resp_unknown) + completedResponse = true; + } + else + completedResponse = true; + } + } + else + { + if (imap->_imap_cmd == esp_mail_imap_cmd_auth) + { + if (authFailed(response, readLen, chunkIdx, 2)) + completedResponse = true; + } + else if (imap->_imap_cmd == esp_mail_imap_cmd_capability) + handleCapability(imap, response, chunkIdx); + else if (imap->_imap_cmd == esp_mail_imap_cmd_list) + handleFolders(imap, response); + else if (imap->_imap_cmd == esp_mail_imap_cmd_select || imap->_imap_cmd == esp_mail_imap_cmd_examine) + handleExamine(imap, response); + else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header) + { + char *tmp = intStr(cMSG(imap)); + header.message_uid = tmp; + delS(tmp); + + tmp = intStr(imap->_totalRead); + header.message_no = tmp; + delS(tmp); + + int _st = headerState; + handleHeader(imap, response, readLen, chunkIdx, header, headerState, octetCount); + if (_st == headerState && headerState > 0 && octetCount <= header.header_data_len) + setHeader(imap, response, header, headerState); + } + else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime) + handlePartHeader(imap, response, chunkIdx, part); + else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text) + decodeText(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetLength, octetCount, dcnt); + else if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) + { + + if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) + { + //multi-line chunked base64 string attachment handle + if (octetCount < octetLength && readLen < BASE64_CHUNKED_LEN) + { + if (strlen(lastBuf) > 0) + { + tmp = newS(readLen + strlen(lastBuf) + 2); + strcpy(tmp, lastBuf); + strcat(tmp, response); + readLen = strlen(tmp); + tmo = handleAttachment(imap, tmp, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); + delS(tmp); + memset(lastBuf, 0, BASE64_CHUNKED_LEN + 1); + if (!tmo) + break; + } + else if (readLen < BASE64_CHUNKED_LEN + 1) + strcpy(lastBuf, response); + } + else + { + tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); + if (!tmo) + break; + } + } + else + tmo = handleAttachment(imap, response, readLen, chunkIdx, file, filePath, downloadRequest, octetCount, octetLength, oCount, reportState, dcnt); + } + dataTime = millis(); + } + } + memset(response, 0, chunkBufSize); + } + } + delS(response); + if (imap->_imap_cmd == esp_mail_imap_command::esp_mail_imap_cmd_search) + { + if (imap->_debug && scnt > 0 && scnt < 100) + searchReport(100, spc); + delS(skey); + delS(spc); + } + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment) + delS(lastBuf); + } + + if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header && header.header_data_len == 0) || imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no) + { + if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_no) + imap->_imapStatus.statusCode = IMAP_STATUS_IMAP_RESPONSE_FAILED; + else + imap->_imapStatus.statusCode = IMAP_STATUS_NO_MESSAGE; + + if (imap->_readCallback) + { + std::string s; + appendP(s, esp_mail_str_53, true); + s += imap->errorReason().c_str(); + imapCB(imap, s.c_str(), false); + } + + if (imap->_debug) + { + std::string s; + appendP(s, esp_mail_str_185, true); + s += imap->errorReason().c_str(); + esp_mail_debug_line(s.c_str(), true); + } + + return false; + } + + if (imapResp == esp_mail_imap_response_status::esp_mail_imap_resp_ok) + { + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_header) + { + char *buf = newS(header.content_type.length() + 1); + strcpy(buf, header.content_type.c_str()); + header.content_type.clear(); + + tmp = subStr(buf, esp_mail_str_25, esp_mail_str_97, 0); + if (tmp) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_content_type; + setHeader(imap, tmp, header, headerState); + delS(tmp); + + int p1 = strposP(header.content_type.c_str(), esp_mail_imap_composite_media_type_t::multipart, 0); + if (p1 != -1) + { + p1 += strlen(esp_mail_imap_composite_media_type_t::multipart) + 1; + header.multipart = true; + //inline or embedded images + if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::related, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_related; + //multiple text formats e.g. plain, html, enriched + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::alternative, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_alternative; + //medias + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::parallel, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_parallel; + //rfc822 encapsulated + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::digest, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_digest; + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::report, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_report; + //others can be attachments + else if (strpos(part.content_type.c_str(), esp_mail_imap_multipart_sub_type_t::mixed, p1) != -1) + header.multipart_sub_type = esp_mail_imap_multipart_sub_type_mixed; + } + + p1 = strposP(header.content_type.c_str(), esp_mail_imap_composite_media_type_t::message, 0); + if (p1 != -1) + { + p1 += strlen(esp_mail_imap_composite_media_type_t::message) + 1; + if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::rfc822, p1) != -1) + { + header.rfc822_part = true; + header.message_sub_type = esp_mail_imap_message_sub_type_rfc822; + } + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::Partial, p1) != -1) + header.message_sub_type = esp_mail_imap_message_sub_type_partial; + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::External_Body, p1) != -1) + header.message_sub_type = esp_mail_imap_message_sub_type_external_body; + else if (strpos(part.content_type.c_str(), esp_mail_imap_message_sub_type_t::delivery_status, p1) != -1) + header.message_sub_type = esp_mail_imap_message_sub_type_delivery_status; + } + + tmp = subStr(buf, esp_mail_str_169, NULL, 0, -1); + if (tmp) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_char_set; + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + + if (header.multipart) + { + if (strcmpP(buf, 0, esp_mail_str_277)) + { + tmp = subStr(buf, esp_mail_str_277, esp_mail_str_136, 0); + if (tmp) + { + headerState = esp_mail_imap_header_state::esp_mail_imap_state_boundary; + setHeader(imap, tmp, header, headerState); + delS(tmp); + } + } + } + } + + delS(buf); + + decodeHeader(header.from, header.from_charset); + decodeHeader(header.to, header.to_charset); + decodeHeader(header.cc, header.cc_charset); + decodeHeader(header.subject, header.subject_charset); + + imap->_headers.push_back(header); + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_mime) + { + //expect the octet length in the response for the existent part + if (part.octetLen > 0) + { + + part.partNumStr = cHeader(imap)->partNumStr; + part.partNumFetchStr = cHeader(imap)->partNumStr; + if (cHeader(imap)->part_headers.size() > 0) + { + + struct esp_mail_message_part_info_t *_part = &cHeader(imap)->part_headers[cHeader(imap)->part_headers.size() - 1]; + bool rfc822_body_subtype = _part->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + + if (rfc822_body_subtype) + { + if (!_part->rfc822_part) + { + //additional rfc822 message header, store it to the rfc822 part header + _part->rfc822_part = true; + _part->rfc822_header = part.rfc822_header; + imap->_rfc822_part_count++; + _part->rfc822_msg_Idx = imap->_rfc822_part_count; + } + } + } + + cHeader(imap)->part_headers.push_back(part); + cHeader(imap)->message_data_count = cHeader(imap)->part_headers.size(); + + if (part.msg_type == esp_mail_msg_type_plain || part.msg_type == esp_mail_msg_type_enriched || part.msg_type == esp_mail_msg_type_html || part.attach_type == esp_mail_att_type_none || (part.attach_type == esp_mail_att_type_attachment && imap->_config->download.attachment) || (part.attach_type == esp_mail_att_type_inline && imap->_config->download.inlineImg)) + { + if (part.message_sub_type != esp_mail_imap_message_sub_type_rfc822) + { + if (part.attach_type != esp_mail_att_type_none && cHeader(imap)->multipart_sub_type != esp_mail_imap_multipart_sub_type_alternative) + cHeader(imap)->attachment_count++; + } + } + } + else + { + //nonexistent part + //return false to exit the loop without closing the connection + if (closeSession) + imap->closeSession(); + return false; + } + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) + { + if (cPart(imap)->file_open_write) + file.close(); + } + + if (imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_text) + cPart(imap)->text[cPart(imap)->textLen] = 0; + } + else + { + //some server responses NO and should exit (false) from MIME feching loop without + //closing the session + if (imap->_imap_cmd != esp_mail_imap_cmd_fetch_body_mime) + return handleIMAPError(imap, errCode, false); + + if (closeSession) + imap->closeSession(); + return false; + } + + return true; +} + +void ESP_Mail_Client::saveHeader(IMAPSession *imap) +{ + + std::string headerFilePath; + prepareFilePath(imap, headerFilePath, true); + if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk) + _sdOk = sdTest(); + else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk) +#if defined(ESP32) + _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + _flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + + if (_sdOk || _flashOk) + { + if (file) + file.close(); + + if (imap->_storageType == esp_mail_file_storage_type_sd) { + file = ESP_MAIL_SD_FS.open(headerFilePath.c_str(), FILE_WRITE); + } else if (imap->_storageType == esp_mail_file_storage_type_flash) { +#if defined(ESP32) + file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), FILE_WRITE); +#elif defined(ESP8266) + file = ESP_MAIL_FLASH_FS.open(headerFilePath.c_str(), "w"); +#endif + } else if (imap->_storageType == esp_mail_file_storage_type_univ) { + file = ufsp->open(headerFilePath.c_str(), "w"); + } + + if (file) + { + std::string s; + appendP(s, esp_mail_str_99, true); + file.print(s.c_str()); + file.println(cHeader(imap)->date.c_str()); + + appendP(s, esp_mail_str_100, true); + file.print(s.c_str()); + if (imap->_uidSearch) + file.println(cMSG(imap)); + else + file.println(); + + appendP(s, esp_mail_str_101, true); + file.print(s.c_str()); + file.println(cMSG(imap)); + + appendP(s, esp_mail_str_102, true); + file.print(s.c_str()); + file.println(cHeader(imap)->accept_language.c_str()); + + appendP(s, esp_mail_str_103, true); + file.print(s.c_str()); + file.println(cHeader(imap)->content_language.c_str()); + + appendP(s, esp_mail_str_10, true); + file.print(s.c_str()); + file.println(cHeader(imap)->from.c_str()); + + appendP(s, esp_mail_str_105, true); + file.print(s.c_str()); + file.println(cHeader(imap)->from_charset.c_str()); + + appendP(s, esp_mail_str_11, true); + file.print(s.c_str()); + file.println(cHeader(imap)->to.c_str()); + + appendP(s, esp_mail_str_107, true); + file.print(s.c_str()); + file.println(cHeader(imap)->to_charset.c_str()); + + appendP(s, esp_mail_str_108, true); + file.print(s.c_str()); + file.println(cHeader(imap)->cc.c_str()); + + appendP(s, esp_mail_str_109, true); + file.print(s.c_str()); + file.println(cHeader(imap)->cc_charset.c_str()); + + appendP(s, esp_mail_str_24, true); + file.print(s.c_str()); + file.println(cHeader(imap)->subject.c_str()); + + appendP(s, esp_mail_str_111, true); + file.print(s.c_str()); + file.println(cHeader(imap)->subject_charset.c_str()); + + appendP(s, esp_mail_str_112, true); + file.print(s.c_str()); + file.println(cPart(imap)->charset.c_str()); + + if (cHeader(imap)->attachment_count > 0) + { + + appendP(s, esp_mail_str_113, true); + file.print(s.c_str()); + file.println(cHeader(imap)->attachment_count); + + for (int j = 0; j < cHeader(imap)->attachment_count; j++) + { + if (imap->_headers[cIdx(imap)].part_headers[j].attach_type == esp_mail_att_type_none || imap->_headers[cIdx(imap)].part_headers[j].rfc822_part) + continue; + struct esp_mail_attacment_info_t att; + att.filename = imap->_headers[cIdx(imap)].part_headers[j].filename.c_str(); + att.mime = imap->_headers[cIdx(imap)].part_headers[j].content_type.c_str(); + att.name = imap->_headers[cIdx(imap)].part_headers[j].name.c_str(); + att.size = imap->_headers[cIdx(imap)].part_headers[j].attach_data_size; + att.creationDate = imap->_headers[cIdx(imap)].part_headers[j].creation_date.c_str(); + att.type = imap->_headers[cIdx(imap)].part_headers[j].attach_type; + + appendP(s, esp_mail_str_114, true); + file.print(s.c_str()); + file.println(j + 1); + + appendP(s, esp_mail_str_115, true); + file.print(s.c_str()); + file.println(att.filename); + + appendP(s, esp_mail_str_116, true); + file.print(s.c_str()); + file.println(att.name); + + appendP(s, esp_mail_str_117, true); + file.print(s.c_str()); + file.println(att.size); + + appendP(s, esp_mail_str_118, true); + file.print(s.c_str()); + file.println(att.mime); + + appendP(s, esp_mail_str_119, true); + file.print(s.c_str()); + file.println(att.creationDate); + } + } + + file.close(); + } + imap->_headerSaved = true; + } +} + +esp_mail_imap_response_status ESP_Mail_Client::imapResponseStatus(IMAPSession *imap, char *response) +{ + imap->_imapStatus.text.clear(); + if (strposP(response, esp_mail_imap_response_1, 0) > -1) + return esp_mail_imap_response_status::esp_mail_imap_resp_ok; + else if (strposP(response, esp_mail_imap_response_2, 0) > -1) + { + imap->_imapStatus.text = response; + imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_2)); + return esp_mail_imap_response_status::esp_mail_imap_resp_no; + } + else if (strposP(response, esp_mail_imap_response_3, 0) > -1) + { + imap->_imapStatus.text = response; + imap->_imapStatus.text = imap->_imapStatus.text.substr(strlen_P(esp_mail_imap_response_3)); + return esp_mail_imap_response_status::esp_mail_imap_resp_bad; + } + return esp_mail_imap_response_status::esp_mail_imap_resp_unknown; +} + +void ESP_Mail_Client::decodeHeader(std::string &headerField, std::string &headerEnc) +{ + + size_t p1 = 0, p2 = 0; + + while (headerField[p1] == ' ' && p1 < headerField.length() - 1) + p1++; + + if (headerField[p1] == '=' && headerField[p1 + 1] == '?') + { + p2 = headerField.find("?", p1 + 2); + if (p2 != std::string::npos) + headerEnc = headerField.substr(p1 + 2, p2 - p1 - 2); + } + + int bufSize = 512; + char *buf = newS(bufSize); + + RFC2047Decoder.rfc2047Decode(buf, headerField.c_str(), bufSize); + + if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_iso8859_1) + { + int len = strlen(buf); + int olen = (len + 1) * 2; + unsigned char *out = (unsigned char *)newS(olen); + decodeLatin1_UTF8(out, &olen, (unsigned char *)buf, &len); + delS(buf); + buf = (char *)out; + } + else if (getEncodingFromCharset(headerEnc.c_str()) == esp_mail_char_decoding_scheme_tis620) + { + size_t len2 = strlen(buf); + char *tmp = newS((len2 + 1) * 3); + decodeTIS620_UTF8(tmp, buf, len2); + delS(buf); + buf = tmp; + } + + headerField = buf; + delS(buf); +} + +esp_mail_char_decoding_scheme ESP_Mail_Client::getEncodingFromCharset(const char *enc) +{ + esp_mail_char_decoding_scheme scheme = esp_mail_char_decoding_scheme_default; + + if (strposP(enc, esp_mail_str_237, 0) > -1 || strposP(enc, esp_mail_str_231, 0) > -1 || strposP(enc, esp_mail_str_226, 0) > -1) + scheme = esp_mail_char_decoding_scheme_tis620; + else if (strposP(enc, esp_mail_str_227, 0) > -1) + scheme = esp_mail_char_decoding_scheme_iso8859_1; + + return scheme; +} + +bool ESP_Mail_Client::handleAttachment(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetCount, int &octetLength, int &oCount, int &reportState, int &downloadCount) +{ + if (chunkIdx == 0) + { + char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); + if (tmp) + { + octetCount = 2; //CRLF counted from first line + octetLength = atoi(tmp); + delS(tmp); + chunkIdx++; + cHeader(imap)->total_download_size += octetLength; + } + return true; + } + + if (octetLength == 0) + return true; + + chunkIdx++; + + delay(0); + + if (!cPart(imap)->file_open_write) + { + + cPart(imap)->file_open_write = true; + + if (imap->_storageType == esp_mail_file_storage_type_sd && !_sdOk) + _sdOk = sdTest(); + else if (imap->_storageType == esp_mail_file_storage_type_flash && !_flashOk) +#if defined(ESP32) + _flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + _flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + + if (_sdOk || _flashOk) + { + + downloadRequest = true; + + filePath.clear(); + filePath += imap->_config->storage.saved_path; + appendP(filePath, esp_mail_str_202, false); + + char *tmp = intStr(cMSG(imap)); + filePath += tmp; + delS(tmp); + + if (imap->_storageType == esp_mail_file_storage_type_sd) + if (!ESP_MAIL_SD_FS.exists(filePath.c_str())) + createDirs(filePath); + + appendP(filePath, esp_mail_str_202, false); + + filePath += cPart(imap)->filename; + + if (imap->_storageType == esp_mail_file_storage_type_sd) + file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE); + else if (imap->_storageType == esp_mail_file_storage_type_flash) +#if defined(ESP32) + file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE); +#elif defined(ESP8266) + file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w"); +#endif + } + } + + if (_sdOk || _flashOk) + { + int nOctet = oCount + bufLen + 2; + if (nOctet > octetLength) + { + if (imap->_readCallback) + downloadReport(imap, 100); + + if (oCount < octetLength) + { + int dLen = nOctet - 2 - octetLength; + bufLen -= dLen; + buf[bufLen] = 0; + } + else + return true; + } + + oCount += bufLen + 2; + + if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) + { + + size_t olen = 0; + unsigned char *decoded = decodeBase64((const unsigned char *)buf, bufLen, &olen); + + if (decoded) + { + + if (!cPart(imap)->sizeProp) + { + cPart(imap)->attach_data_size += olen; + cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size; + } + + file.write((const uint8_t *)decoded, olen); + delay(0); + delete[] decoded; + + if (imap->_config->enable.download_status) + { + int p = 0; + if (cHeader(imap)->total_download_size > 0) + p = 100 * octetCount / cHeader(imap)->total_download_size; + + if ((p != downloadCount) && (p <= 100)) + { + downloadCount = p; + if (imap->_readCallback && reportState != -1) + downloadReport(imap, p); + reportState = -1; + } + else + reportState = 0; + } + } + + if (!reconnect(imap)) + return false; + } + else + { + //binary content + if (!cPart(imap)->sizeProp) + { + cPart(imap)->attach_data_size += bufLen; + cHeader(imap)->total_attach_data_size += cPart(imap)->attach_data_size; + } + + file.write((const uint8_t *)buf, bufLen); + delay(0); + + if (imap->_config->enable.download_status) + { + int p = 0; + if (cHeader(imap)->total_download_size > 0) + p = 100 * octetCount / cHeader(imap)->total_download_size; + + if ((p != downloadCount) && (p <= 100)) + { + downloadCount = p; + if (imap->_readCallback && reportState != -1) + downloadReport(imap, p); + reportState = -1; + } + else + reportState = 0; + } + + if (!reconnect(imap)) + return false; + } + } + return true; +} + +void ESP_Mail_Client::downloadReport(IMAPSession *imap, int progress) +{ + if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) + { + std::string s; + char *tmp = intStr(progress); + appendP(s, esp_mail_str_90, true); + appendP(s, esp_mail_str_131, false); + s += cPart(imap)->filename; + appendP(s, esp_mail_str_91, false); + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_92, false); + appendP(s, esp_mail_str_34, false); + esp_mail_debug_line(s.c_str(), false); + std::string().swap(s); + } +} + +void ESP_Mail_Client::fetchReport(IMAPSession *imap, int progress, bool download) +{ + if (imap->_readCallback && progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) + { + std::string s; + char *tmp = intStr(progress); + if (download) + appendP(s, esp_mail_str_90, true); + else + appendP(s, esp_mail_str_83, true); + appendP(s, esp_mail_str_131, false); + if (cPart(imap)->filename.length() > 0) + { + s += cPart(imap)->filename; + appendP(s, esp_mail_str_91, false); + } + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_92, false); + appendP(s, esp_mail_str_34, false); + esp_mail_debug_line(s.c_str(), false); + std::string().swap(s); + } +} + +void ESP_Mail_Client::searchReport(int progress, const char *percent) +{ + if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) + { + char *tmp = intStr(progress); + std::string s; + appendP(s, esp_mail_str_261, true); + s += tmp; + s += percent; + appendP(s, esp_mail_str_34, false); + esp_mail_debug_line(s.c_str(), false); + delS(tmp); + } +} + +void ESP_Mail_Client::uploadReport(const char *filename, int progress) +{ + if (progress % ESP_MAIL_PROGRESS_REPORT_STEP == 0) + { + std::string s; + char *tmp = intStr(progress); + appendP(s, esp_mail_str_160, true); + s += filename; + appendP(s, esp_mail_str_91, false); + s += tmp; + delS(tmp); + appendP(s, esp_mail_str_92, false); + appendP(s, esp_mail_str_34, false); + esp_mail_debug_line(s.c_str(), false); + std::string().swap(s); + } +} + +int ESP_Mail_Client::cMSG(IMAPSession *imap) +{ + return imap->_msgNum[cIdx(imap)]; +} + +int ESP_Mail_Client::cIdx(IMAPSession *imap) +{ + return imap->_cMsgIdx; +} + +void ESP_Mail_Client::decodeTIS620_UTF8(char *out, const char *in, size_t len) +{ + //output is the 3-byte value UTF-8 + int j = 0; + for (size_t i = 0; i < len; i++) + { + if (in[i] < 0x80) + out[j++] = in[i]; + else if ((in[i] >= 0xa0 && in[i] < 0xdb) || (in[i] > 0xde && in[i] < 0xfc)) + { + int unicode = 0x0e00 + in[i] - 0xa0; + out[j++] = 0xe0 | ((unicode >> 12) & 0xf); + out[j++] = 0x80 | ((unicode >> 6) & 0x3f); + out[j++] = 0x80 | (unicode & 0x3f); + } + } +} + +int ESP_Mail_Client::decodeLatin1_UTF8(unsigned char *out, int *outlen, const unsigned char *in, int *inlen) +{ + unsigned char *outstart = out; + const unsigned char *base = in; + const unsigned char *processed = in; + unsigned char *outend = out + *outlen; + const unsigned char *inend; + unsigned int c; + int bits; + + inend = in + (*inlen); + while ((in < inend) && (out - outstart + 5 < *outlen)) + { + c = *in++; + + /* assertion: c is a single UTF-4 value */ + if (out >= outend) + break; + if (c < 0x80) + { + *out++ = c; + bits = -6; + } + else + { + *out++ = ((c >> 6) & 0x1F) | 0xC0; + bits = 0; + } + + for (; bits >= 0; bits -= 6) + { + if (out >= outend) + break; + *out++ = ((c >> bits) & 0x3F) | 0x80; + } + processed = (const unsigned char *)in; + } + *outlen = out - outstart; + *inlen = processed - base; + return (0); +} + +void ESP_Mail_Client::decodeText(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, File &file, std::string &filePath, bool &downloadRequest, int &octetLength, int &octetCount, int &readCount) +{ + bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + if (chunkIdx == 0) + { + char *tmp = subStr(buf, esp_mail_str_193, esp_mail_str_194, 0); + if (tmp) + { + octetCount = 2; + octetLength = atoi(tmp); + delS(tmp); + chunkIdx++; + cPart(imap)->octetLen = octetLength; + + if ((rfc822_body_subtype && imap->_config->download.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))) + prepareFilePath(imap, filePath, false); + + if (filePath.length() == 0) + { + if (!rfc822_body_subtype) + appendP(filePath, esp_mail_str_67, false); + else + { + appendP(filePath, esp_mail_str_82, false); + appendP(filePath, esp_mail_str_131, false); + appendP(filePath, esp_mail_str_67, false); + } + } + cPart(imap)->filename = filePath; + + return; + } + else + { + if (imap->_debug) + { + char *tmp = strP(esp_mail_str_280); + esp_mail_debug_line(tmp, false); + delS(tmp); + } + } + } + + delay(0); + + if (octetLength == 0) + return; + + if (imap->_config->download.rfc822 || imap->_config->download.html || imap->_config->download.text || (rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text)))) + { + if (imap->_readCallback && octetCount > octetLength + 2 && readCount < 100) + fetchReport(imap, 100, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))); + + if (octetCount <= octetLength + 2) + { + size_t olen = 0; + char *decoded = nullptr; + bool newC = true; + if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_31)) + { + decoded = (char *)decodeBase64((const unsigned char *)buf, bufLen, &olen); + } + else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_278)) + { + decoded = newS(bufLen + 10); + decodeQP(buf, decoded); + olen = strlen(decoded); + } + else if (strcmpP(cPart(imap)->content_transfer_encoding.c_str(), 0, esp_mail_str_29)) + { + decoded = decode7Bit(buf); + olen = strlen(decoded); + } + else + { + //8bit and binary + newC = false; + decoded = buf; + olen = bufLen; + } + + if (decoded) + { + + if ((rfc822_body_subtype && imap->_config->enable.rfc822) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->enable.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->enable.text)))) + { + + if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_iso8859_1) + { + int ilen = olen; + int olen2 = (ilen + 1) * 2; + unsigned char *tmp = (unsigned char *)newS(olen2); + decodeLatin1_UTF8(tmp, &olen2, (unsigned char *)decoded, &ilen); + delS(decoded); + olen = olen2; + decoded = (char *)tmp; + } + else if (getEncodingFromCharset(cPart(imap)->charset.c_str()) == esp_mail_char_decoding_scheme_tis620) + { + char *out = newS((olen + 1) * 3); + delS(decoded); + decodeTIS620_UTF8(out, decoded, olen); + olen = strlen(out); + decoded = out; + } + + int p = 0; + + if (octetLength > 0) + p = 100 * octetCount / octetLength; + + if ((p != readCount) && (p <= 100)) + { + readCount = p; + if (imap->_readCallback) + fetchReport(imap, p, (imap->_config->download.rfc822 && rfc822_body_subtype) || (!rfc822_body_subtype && ((cPart(imap)->msg_type == esp_mail_msg_type_html && imap->_config->download.html) || ((cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) && imap->_config->download.text)))); + } + + if (cPart(imap)->text.length() < imap->_config->limit.msg_size) + { + + if (cPart(imap)->text.length() + olen < imap->_config->limit.msg_size) + { + cPart(imap)->textLen += olen; + cPart(imap)->text.append(decoded, olen); + } + else + { + int d = imap->_config->limit.msg_size - cPart(imap)->text.length(); + cPart(imap)->textLen += d; + if (d > 0) + cPart(imap)->text.append(decoded, d); + } + } + } + + if (filePath.length() > 0) + { + if (!cPart(imap)->file_open_write) + { + cPart(imap)->file_open_write = true; + + if (_sdOk || _flashOk) + { + downloadRequest = true; + + if (imap->_storageType == esp_mail_file_storage_type_sd) + file = ESP_MAIL_SD_FS.open(filePath.c_str(), FILE_WRITE); + else if (imap->_storageType == esp_mail_file_storage_type_flash) +#if defined(ESP32) + file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), FILE_WRITE); +#elif defined(ESP8266) + file = ESP_MAIL_FLASH_FS.open(filePath.c_str(), "w"); +#endif + } + } + + if (_sdOk || _flashOk) + file.write((const uint8_t *)decoded, olen); + } + + if (newC) + delS(decoded); + } + } + } +} +void ESP_Mail_Client::prepareFilePath(IMAPSession *imap, std::string &filePath, bool header) +{ + bool rfc822_body_subtype = cPart(imap)->message_sub_type == esp_mail_imap_message_sub_type_rfc822; + std::string fpath = imap->_config->storage.saved_path; + appendP(fpath, esp_mail_str_202, false); + char *tmp = intStr(cMSG(imap)); + fpath += tmp; + delS(tmp); + + if (imap->_storageType == esp_mail_file_storage_type_sd) + if (!ESP_MAIL_SD_FS.exists(fpath.c_str())) + createDirs(fpath); + + if (header) + { + appendP(fpath, esp_mail_str_203, false); + } + else + { + if (!rfc822_body_subtype) + { + appendP(fpath, esp_mail_str_161, false); + if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) + appendP(fpath, esp_mail_str_95, false); + else if (cPart(imap)->msg_type == esp_mail_msg_type_html) + appendP(fpath, esp_mail_str_94, false); + } + else + { + appendP(fpath, esp_mail_str_163, false); + + if (cPart(imap)->rfc822_msg_Idx > 0) + { + char *tmp = intStr(cPart(imap)->rfc822_msg_Idx); + fpath += tmp; + delS(tmp); + } + + if (cPart(imap)->msg_type == esp_mail_msg_type_plain || cPart(imap)->msg_type == esp_mail_msg_type_enriched) + appendP(fpath, esp_mail_str_95, false); + else if (cPart(imap)->msg_type == esp_mail_msg_type_html) + appendP(fpath, esp_mail_str_94, false); + else + //possible rfc822 encapsulated message which cannot fetch its header + appendP(fpath, esp_mail_str_40, false); + } + } + + filePath = fpath; +} + +char *ESP_Mail_Client::strReplace(char *orig, char *rep, char *with) +{ + char *result = nullptr; + char *ins = nullptr; + char *tmp = nullptr; + int len_rep; + int len_with; + int len_front; + int count; + + len_with = strlen(with); + len_rep = strlen(rep); + + ins = orig; + for (count = 0; (tmp = strstr(ins, rep)); ++count) + ins = tmp + len_rep; + + tmp = result = newS(strlen(orig) + (len_with - len_rep) * count + 1); + while (count--) + { + ins = strstr(orig, rep); + len_front = ins - orig; + tmp = strncpy(tmp, orig, len_front) + len_front; + tmp = strcpy(tmp, with) + len_with; + orig += len_front + len_rep; + } + strcpy(tmp, orig); + return result; +} + +int ESP_Mail_Client::decodeChar(const char *s) +{ + assert(s); + assert(*s == '='); + return 16 * hexval(*(s + 1)) + hexval(*(s + 2)); +} + +void ESP_Mail_Client::decodeQP(const char *buf, char *out) +{ + char *tmp = strP(esp_mail_str_295); + while (*buf) + { + if (*buf != '=') + strcat_c(out, *buf++); + else if (*(buf + 1) == '\r' && *(buf + 2) == '\n') + buf += 3; + else if (*(buf + 1) == '\n') + buf += 2; + else if (!strchr(tmp, *(buf + 1))) + strcat_c(out, *buf++); + else if (!strchr(tmp, *(buf + 2))) + strcat_c(out, *buf++); + else + { + strcat_c(out, decodeChar(buf)); + buf += 3; + } + } + delS(tmp); +} + +std::string ESP_Mail_Client::getBoundary(size_t len) +{ + char *tmp = strP(boundary_table); + char *buf = newS(len); + if (len) + { + --len; + buf[0] = tmp[0]; + buf[1] = tmp[1]; + for (size_t n = 2; n < len; n++) + { + int key = rand() % (int)(strlen(tmp) - 1); + buf[n] = tmp[key]; + } + buf[len] = '\0'; + } + std::string s = buf; + delS(buf); + delS(tmp); + return s; +} + +char *ESP_Mail_Client::decode7Bit(char *buf) +{ + char *out = strReplaceP(buf, imap_7bit_key1, imap_7bit_val1); + char *tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key2, imap_7bit_val2); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key3, imap_7bit_val3); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key4, imap_7bit_val4); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key5, imap_7bit_val5); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key6, imap_7bit_val6); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key7, imap_7bit_val7); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key8, imap_7bit_val8); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key9, imap_7bit_val9); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key10, imap_7bit_val10); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key11, imap_7bit_val11); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key12, imap_7bit_val12); + + delS(tmp); + tmp = newS(strlen(out) + 10); + strcpy(tmp, out); + delS(out); + out = strReplaceP(tmp, imap_7bit_key13, imap_7bit_val13); + delS(tmp); + return out; +} + +char *ESP_Mail_Client::strReplaceP(char *buf, PGM_P name, PGM_P value) +{ + char *n = strP(name); + char *v = strP(value); + char *out = strReplace(buf, n, v); + delS(n); + delS(v); + return out; +} + +void ESP_Mail_Client::handleCapability(IMAPSession *imap, char *buf, int &chunkIdx) +{ + if (chunkIdx == 0) + { + if (strposP(buf, esp_mail_imap_response_10, 0) > -1) + { + if (strposP(buf, esp_mail_imap_response_11, 0) > -1) + imap->_auth_capability.login = true; + if (strposP(buf, esp_mail_imap_response_12, 0) > -1) + imap->_auth_capability.plain = true; + if (strposP(buf, esp_mail_imap_response_13, 0) > -1) + imap->_auth_capability.xoauth2 = true; + if (strposP(buf, esp_mail_imap_response_14, 0) > -1) + imap->_auth_capability.start_tls = true; + if (strposP(buf, esp_mail_imap_response_15, 0) > -1) + imap->_auth_capability.cram_md5 = true; + if (strposP(buf, esp_mail_imap_response_16, 0) > -1) + imap->_auth_capability.digest_md5 = true; + } + } +} + +bool ESP_Mail_Client::authFailed(char *buf, int bufLen, int &chunkIdx, int ofs) +{ + bool ret = false; + if (chunkIdx == 0) + { + size_t olen; + unsigned char *decoded = decodeBase64((const unsigned char *)(buf + ofs), bufLen - ofs, &olen); + if (decoded) + { + ret = strposP((char *)decoded, esp_mail_str_294, 0) > -1; + delete[] decoded; + } + chunkIdx++; + } + return ret; +} + +void ESP_Mail_Client::handleFolders(IMAPSession *imap, char *buf) +{ + struct esp_mail_folder_info_t fd; + char *tmp = nullptr; + int p1 = strposP(buf, esp_mail_imap_response_4, 0); + int p2 = 0; + if (p1 != -1) + { + p1 = strposP(buf, esp_mail_str_198, 0); + if (p1 != -1) + { + p2 = strposP(buf, esp_mail_str_192, p1 + 1); + if (p2 != -1) + { + tmp = newS(p2 - p1); + strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); + if (tmp[p2 - p1 - 2] == '\r') + tmp[p2 - p1 - 2] = 0; + fd.attributes = tmp; + delS(tmp); + } + } + + p1 = strposP(buf, esp_mail_str_136, 0); + if (p1 != -1) + { + p2 = strposP(buf, esp_mail_str_136, p1 + 1); + if (p2 != -1) + { + tmp = newS(p2 - p1); + strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); + if (tmp[p2 - p1 - 2] == '\r') + tmp[p2 - p1 - 2] = 0; + fd.delimiter = tmp; + delS(tmp); + } + } + + p1 = strposP(buf, esp_mail_str_131, p2); + if (p1 != -1) + { + p2 = strlen(buf); + tmp = newS(p2 - p1); + if (buf[p1 + 1] == '"') + p1++; + strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); + if (tmp[p2 - p1 - 2] == '\r') + tmp[p2 - p1 - 2] = 0; + if (tmp[strlen(tmp) - 1] == '"') + tmp[strlen(tmp) - 1] = 0; + fd.name = tmp; + delS(tmp); + } + imap->_folders.add(fd); + } +} + +void ESP_Mail_Client::handleExamine(IMAPSession *imap, char *buf) +{ + char *tmp = nullptr; + int p1, p2; + + if (imap->_mbif._msgCount == 0) + { + p1 = strposP(buf, esp_mail_str_199, 0); + if (p1 != -1) + { + tmp = newS(p1); + strncpy(tmp, buf + 2, p1 - 1); + imap->_mbif._msgCount = atoi(tmp); + delS(tmp); + return; + } + } + + if (imap->_mbif._flags.size() == 0) + { + p1 = strposP(buf, esp_mail_imap_response_5, 0); + if (p1 != -1) + { + p1 = strposP(buf, esp_mail_str_198, 0); + if (p1 != -1) + { + p2 = strposP(buf, esp_mail_str_192, p1 + 1); + if (p2 != -1) + { + tmp = newS(p2 - p1); + strncpy(tmp, buf + p1 + 1, p2 - p1 - 1); + char *stk = strP(esp_mail_str_131); + char *end_token; + char *token = strtok_r(tmp, stk, &end_token); + while (token != NULL) + { + imap->_mbif.addFlag(token); + token = strtok_r(NULL, stk, &end_token); + } + if (token) + delS(token); + delS(tmp); + delS(stk); + } + } + return; + } + } + + if (imap->_nextUID.length() == 0) + { + p1 = strposP(buf, esp_mail_str_200, 0); + if (p1 != -1) + { + p2 = strposP(buf, esp_mail_str_156, p1 + strlen_P(esp_mail_str_200)); + if (p2 != -1) + { + tmp = newS(p2 - p1 - strlen_P(esp_mail_str_200) + 1); + strncpy(tmp, buf + p1 + strlen_P(esp_mail_str_200), p2 - p1 - strlen_P(esp_mail_str_200)); + imap->_nextUID = tmp; + imap->_mbif._nextUID = atoi(tmp); + delS(tmp); + } + return; + } + } +} + +bool ESP_Mail_Client::handleIMAPError(IMAPSession *imap, int err, bool ret) +{ + if (err < 0) + { + errorStatusCB(imap, err); + + if (imap->_headers.size() > 0) + { + if ((imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_attachment || imap->_imap_cmd == esp_mail_imap_cmd_fetch_body_inline) && (imap->_config->download.attachment || imap->_config->download.inlineImg)) + { + if (cHeader(imap)->part_headers.size() > 0) + cPart(imap)->download_error = imap->errorReason().c_str(); + } + else + cHeader(imap)->error_msg = imap->errorReason().c_str(); + + cHeader(imap)->error = true; + } + } + + if (imap->_tcpConnected) + closeTCP(imap); + + imap->_cbData.empty(); + + return ret; +} + +unsigned char *ESP_Mail_Client::decodeBase64(const unsigned char *src, size_t len, size_t *out_len) +{ + + unsigned char *out, *pos, block[4], tmp; + size_t i, count, olen; + int pad = 0; + size_t extra_pad; + + unsigned char *dtable = new unsigned char[256]; + + memset(dtable, 0x80, 256); + + for (i = 0; i < sizeof(b64_index_table) - 1; i++) + dtable[b64_index_table[i]] = (unsigned char)i; + dtable['='] = 0; + + count = 0; + for (i = 0; i < len; i++) + { + if (dtable[src[i]] != 0x80) + count++; + } + + if (count == 0) + goto exit; + extra_pad = (4 - count % 4) % 4; + + olen = (count + extra_pad) / 4 * 3; + pos = out = (unsigned char *)malloc(olen); + if (out == NULL) + goto exit; + + count = 0; + for (i = 0; i < len + extra_pad; i++) + { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; + if (tmp == 0x80) + continue; + + if (val == '=') + pad++; + block[count] = tmp; + count++; + if (count == 4) + { + *pos++ = (block[0] << 2) | (block[1] >> 4); + *pos++ = (block[1] << 4) | (block[2] >> 2); + *pos++ = (block[2] << 6) | block[3]; + count = 0; + if (pad) + { + if (pad == 1) + pos--; + else if (pad == 2) + pos -= 2; + else + { + free(out); + goto exit; + } + break; + } + } + } + + *out_len = pos - out; + delete[] dtable; + return out; +exit: + delete[] dtable; + return nullptr; +} + +std::string ESP_Mail_Client::encodeBase64Str(const unsigned char *src, size_t len) +{ + return encodeBase64Str((uint8_t *)src, len); +} + +std::string ESP_Mail_Client::encodeBase64Str(uint8_t *src, size_t len) +{ + std::string outStr; + unsigned char *out, *pos; + const unsigned char *end, *in; + size_t olen = 4 * ((len + 2) / 3); + if (olen < len) + return outStr; + + outStr.resize(olen); + out = (unsigned char *)&outStr[0]; + + end = src + len; + in = src; + pos = out; + + while (end - in >= 3) + { + *pos++ = b64_index_table[in[0] >> 2]; + *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = b64_index_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = b64_index_table[in[2] & 0x3f]; + in += 3; + } + + if (end - in) + { + *pos++ = b64_index_table[in[0] >> 2]; + if (end - in == 1) + { + *pos++ = b64_index_table[(in[0] & 0x03) << 4]; + *pos++ = '='; + } + else + { + *pos++ = b64_index_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *pos++ = b64_index_table[(in[1] & 0x0f) << 2]; + } + *pos++ = '='; + } + + return outStr; +} + +void ESP_Mail_Client::encodeQP(const char *buf, char *out) +{ + int n = 0, p = 0, pos = 0; + assert(buf); + for (n = 0; *buf; buf++) + { + if (n >= 73 && *buf != 10 && *buf != 13) + { + p = sprintf(out + pos, "=\r\n"); + pos += p; + n = 0; + } + + if (*buf == 10 || *buf == 13) + { + strcat_c(out, *buf); + pos++; + n = 0; + } + else if (*buf < 32 || *buf == 61 || *buf > 126) + { + p = sprintf(out + pos, "=%02X", (unsigned char)*buf); + n += p; + pos += p; + } + else if (*buf != 32 || (*(buf + 1) != 10 && *(buf + 1) != 13)) + { + strcat_c(out, *buf); + n++; + pos++; + } + else + { + p = sprintf(out + pos, "=20"); + n += p; + pos += p; + } + } +} + +bool ESP_Mail_Client::sendBase64(SMTPSession *smtp, SMTP_Message *msg, const unsigned char *data, size_t len, bool flashMem, const char *filename, bool report) +{ + bool ret = false; + const unsigned char *end, *in; + + size_t olen = 4 * ((len + 2) / 3); + + if (olen < len) + return false; + + end = data + len; + in = data; + + size_t chunkSize = 936; + size_t byteAdded = 0; + size_t byteSent = 0; + + int dByte = 0; + unsigned char *buf = new unsigned char[chunkSize]; + memset(buf, 0, chunkSize); + + unsigned char *tmp = new unsigned char[3]; + int bcnt = 0; + int pg = 0, _pg = 0; + + if (report) + uploadReport(filename, bcnt); + + while (end - in >= 3) + { + + memset(tmp, 0, 3); + if (flashMem) + memcpy_P(tmp, in, 3); + else + memcpy(tmp, in, 3); + bcnt += 3; + + buf[byteAdded++] = b64_index_table[tmp[0] >> 2]; + buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)]; + buf[byteAdded++] = b64_index_table[((tmp[1] & 0x0f) << 2) | (tmp[2] >> 6)]; + buf[byteAdded++] = b64_index_table[tmp[2] & 0x3f]; + dByte += 4; + if (dByte == BASE64_CHUNKED_LEN) + { + if (byteAdded + 1 < chunkSize) + { + buf[byteAdded++] = 0x0d; + buf[byteAdded++] = 0x0a; + } + dByte = 0; + } + if (byteAdded >= chunkSize - 4) + { + byteSent += byteAdded; + + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + memset(buf, 0, chunkSize); + byteAdded = 0; + } + in += 3; + + if (report) + { + pg = (float)(100.0f * bcnt / len); + if (pg != _pg) + uploadReport(filename, pg); + _pg = pg; + } + } + + if (byteAdded > 0) + { + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + } + + if (end - in) + { + memset(buf, 0, chunkSize); + byteAdded = 0; + memset(tmp, 0, 3); + if (flashMem) + { + if (end - in == 1) + memcpy_P(tmp, in, 1); + else + memcpy_P(tmp, in, 2); + } + else + { + if (end - in == 1) + memcpy(tmp, in, 1); + else + memcpy(tmp, in, 2); + } + + buf[byteAdded++] = b64_index_table[tmp[0] >> 2]; + if (end - in == 1) + { + buf[byteAdded++] = b64_index_table[(tmp[0] & 0x03) << 4]; + buf[byteAdded++] = '='; + } + else + { + buf[byteAdded++] = b64_index_table[((tmp[0] & 0x03) << 4) | (tmp[1] >> 4)]; + buf[byteAdded++] = b64_index_table[(tmp[1] & 0x0f) << 2]; + } + buf[byteAdded++] = '='; + + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + memset(buf, 0, chunkSize); + } + + if (report && _pg < 100) + uploadReport(filename, 100); + + ret = true; +ex: + if (report) + esp_mail_debug(""); + delete[] tmp; + delete[] buf; + return ret; +} + +bool ESP_Mail_Client::sendBase64Stream(SMTPSession *smtp, SMTP_Message *msg, File file, const char *filename, bool report) +{ + bool ret = false; + + if (!file) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + return false; + } + + size_t chunkSize = 936; + size_t byteAdded = 0; + size_t byteSent = 0; + + unsigned char *buf = new unsigned char[chunkSize]; + memset(buf, 0, chunkSize); + + size_t len = file.size(); + size_t fbufIndex = 0; + unsigned char *fbuf = new unsigned char[3]; + + int dByte = 0; + + int bcnt = 0; + int pg = 0, _pg = 0; + + if (report) + uploadReport(filename, bcnt); + + while (file.available()) + { + memset(fbuf, 0, 3); + if (len - fbufIndex >= 3) + { + bcnt += 3; + size_t readLen = file.read(fbuf, 3); + if (readLen != 3) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + break; + } + + buf[byteAdded++] = b64_index_table[fbuf[0] >> 2]; + buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdded++] = b64_index_table[((fbuf[1] & 0x0f) << 2) | (fbuf[2] >> 6)]; + buf[byteAdded++] = b64_index_table[fbuf[2] & 0x3f]; + dByte += 4; + if (dByte == BASE64_CHUNKED_LEN) + { + if (byteAdded + 1 < chunkSize) + { + buf[byteAdded++] = 0x0d; + buf[byteAdded++] = 0x0a; + } + dByte = 0; + } + if (byteAdded >= chunkSize - 4) + { + byteSent += byteAdded; + + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + + memset(buf, 0, chunkSize); + byteAdded = 0; + } + fbufIndex += 3; + + if (report) + { + pg = (float)(100.0f * bcnt / len); + if (pg != _pg) + uploadReport(filename, pg); + _pg = pg; + } + } + else + { + size_t readLen = file.read(fbuf, len - fbufIndex); + if (readLen != len - fbufIndex) + { + errorStatusCB(smtp, MAIL_CLIENT_ERROR_FILE_IO_ERROR); + break; + } + } + } + + file.close(); + if (byteAdded > 0) + { + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + } + + if (len - fbufIndex > 0) + { + memset(buf, 0, chunkSize); + byteAdded = 0; + buf[byteAdded++] = b64_index_table[fbuf[0] >> 2]; + if (len - fbufIndex == 1) + { + buf[byteAdded++] = b64_index_table[(fbuf[0] & 0x03) << 4]; + buf[byteAdded++] = '='; + } + else + { + buf[byteAdded++] = b64_index_table[((fbuf[0] & 0x03) << 4) | (fbuf[1] >> 4)]; + buf[byteAdded++] = b64_index_table[(fbuf[1] & 0x0f) << 2]; + } + buf[byteAdded++] = '='; + + if (!bdat(smtp, msg, byteAdded, false)) + goto ex; + + if (smtpSend(smtp, buf, byteAdded) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + goto ex; + } + ret = true; + + if (report && _pg < 100) + uploadReport(filename, 100); + +ex: + delete[] buf; + delete[] fbuf; + file.close(); + return ret; +} + +IMAPSession::IMAPSession() {} +IMAPSession::~IMAPSession() +{ + empty(); + _caCert.reset(); + _caCert = nullptr; +} + +bool IMAPSession::closeSession() +{ + if (!_tcpConnected) + return false; +#if defined(ESP32) + /** + * The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure + * The client disposed without memory released after the server close + * the connection due to LOGOUT command, which caused the memory leaks. + */ + if (!MailClient.imapLogout(this)) + return false; +#endif + return MailClient.handleIMAPError(this, 0, true); +} + +bool IMAPSession::connect(ESP_Mail_Session *sesssion, IMAP_Config *config) +{ + if (_tcpConnected) + MailClient.closeTCP(this); + + _sesson_cfg = sesssion; + _config = config; + _caCert = nullptr; + + if (strlen(_sesson_cfg->certificate.cert_data) > 0) + _caCert = std::shared_ptr(_sesson_cfg->certificate.cert_data); + + if (strlen(_sesson_cfg->certificate.cert_file) > 0) + { + if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk) + MailClient._sdOk = MailClient.sdTest(); + if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk) +#if defined(ESP32) + MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + } + + return MailClient.imapAuth(this); +} + +void IMAPSession::debug(int level) +{ + if (level > esp_mail_debug_level_0) + { + if (level > esp_mail_debug_level_3) + level = esp_mail_debug_level_1; + _debugLevel = level; + _debug = true; + } + else + { + _debugLevel = esp_mail_debug_level_0; + _debug = false; + } +} + +String IMAPSession::errorReason() +{ + std::string ret; + + if (_imapStatus.text.length() > 0) + return _imapStatus.text.c_str(); + + switch (_imapStatus.statusCode) + { + case IMAP_STATUS_SERVER_CONNECT_FAILED: + MailClient.appendP(ret, esp_mail_str_38, true); + break; + case MAIL_CLIENT_ERROR_CONNECTION_LOST: + MailClient.appendP(ret, esp_mail_str_221, true); + break; + case MAIL_CLIENT_ERROR_READ_TIMEOUT: + MailClient.appendP(ret, esp_mail_str_258, true); + break; + case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED: + MailClient.appendP(ret, esp_mail_str_305, true); + break; + case IMAP_STATUS_NO_MESSAGE: + MailClient.appendP(ret, esp_mail_str_306, true); + break; + case IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT: + MailClient.appendP(ret, esp_mail_str_93, true); + break; + case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP: + MailClient.appendP(ret, esp_mail_str_132, true); + break; + case IMAP_STATUS_CLOSE_MAILBOX_FAILED: + MailClient.appendP(ret, esp_mail_str_188, true); + break; + case IMAP_STATUS_OPEN_MAILBOX_FAILED: + MailClient.appendP(ret, esp_mail_str_281, true); + break; + case IMAP_STATUS_LIST_MAILBOXS_FAILED: + MailClient.appendP(ret, esp_mail_str_62, true); + break; + case IMAP_STATUS_NO_SUPPORTED_AUTH: + MailClient.appendP(ret, esp_mail_str_42, true); + break; + case IMAP_STATUS_CHECK_CAPABILITIES_FAILED: + MailClient.appendP(ret, esp_mail_str_63, true); + break; + case MAIL_CLIENT_ERROR_OUT_OF_MEMORY: + MailClient.appendP(ret, esp_mail_str_186, true); + break; + case IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED: + MailClient.appendP(ret, esp_mail_str_153, true); + break; + + default: + break; + } + return ret.c_str(); +} + +bool IMAPSession::selectFolder(const char *folderName, bool readOnly) +{ + if (_tcpConnected) + { + if (!openFolder(folderName, readOnly)) + return false; + } + else + { + _currentFolder = folderName; + } + return true; +} + +bool IMAPSession::openFolder(const char *folderName, bool readOnly) +{ + if (!_tcpConnected) + return false; + if (readOnly) + return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_examine, true); + else + return openMailbox(folderName, esp_mail_imap_auth_mode::esp_mail_imap_mode_select, true); +} + +bool IMAPSession::getFolders(FoldersCollection &folders) +{ + if (!_tcpConnected) + return false; + return getMailboxes(folders); +} + +bool IMAPSession::closeFolder(const char *folderName) +{ + if (!_tcpConnected) + return false; + return closeMailbox(); +} + +void IMAPSession::checkUID() +{ + if (MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_140) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_212) || + MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_213) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_214) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_215) || + MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_216) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_217) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_218) || + MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_219) || MailClient.strcmpP(_config->fetch.uid, 0, esp_mail_str_220)) + _config->fetch.uid = "*"; +} + +void IMAPSession::checkPath() +{ + std::string path = _config->storage.saved_path; + if (path[0] != '/') + { + path = "/"; + path += _config->storage.saved_path; + path = path.c_str(); + } +} + +bool IMAPSession::headerOnly() +{ + return _headerOnly; +} + +struct esp_mail_imap_msg_list_t IMAPSession::data() +{ + struct esp_mail_imap_msg_list_t ret; + + for (size_t i = 0; i < _headers.size(); i++) + { + if (ESP.getFreeHeap() < ESP_MAIL_MIN_MEM) + continue; + + struct esp_mail_imap_msg_item_t itm; + + itm.UID = _headers[i].message_uid.c_str(); + itm.ID = _headers[i].message_id.c_str(); + itm.msgNo = _headers[i].message_no.c_str(); + itm.from = _headers[i].from.c_str(); + itm.fromCharset = _headers[i].from_charset.c_str(); + itm.to = _headers[i].to.c_str(); + itm.toCharset = _headers[i].to_charset.c_str(); + itm.cc = _headers[i].cc.c_str(); + itm.ccCharset = _headers[i].cc_charset.c_str(); + itm.subject = _headers[i].subject.c_str(); + itm.subjectCharset = _headers[i].subject_charset.c_str(); + itm.date = _headers[i].date.c_str(); + itm.fetchError = _headers[i].error_msg.c_str(); + + getMessages(i, itm); + + getRFC822Messages(i, itm); + + ret.msgItems.push_back(itm); + } + + return ret; +} + +SelectedFolderInfo IMAPSession::selectedFolder() +{ + return _mbif; +} + +void IMAPSession::callback(imapStatusCallback imapCallback) +{ + _readCallback = std::move(imapCallback); +} + +void IMAPSession::getMessages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg) +{ + msg.text.content = ""; + msg.text.charSet = ""; + msg.text.content_type = ""; + msg.text.transfer_encoding = ""; + msg.html.content = ""; + msg.html.charSet = ""; + msg.html.content_type = ""; + msg.html.transfer_encoding = ""; + + if (messageIndex < _headers.size()) + { + int sz = _headers[messageIndex].part_headers.size(); + if (sz > 0) + { + for (int i = 0; i < sz; i++) + { + if (!_headers[messageIndex].part_headers[i].rfc822_part && _headers[messageIndex].part_headers[i].message_sub_type != esp_mail_imap_message_sub_type_rfc822) + { + if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none) + { + if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched) + { + msg.text.content = _headers[messageIndex].part_headers[i].text.c_str(); + msg.text.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); + msg.text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); + msg.text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); + } + + if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html) + { + msg.html.content = _headers[messageIndex].part_headers[i].text.c_str(); + msg.html.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); + msg.html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); + msg.html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); + } + } + else + { + struct esp_mail_attacment_info_t att; + att.filename = _headers[messageIndex].part_headers[i].filename.c_str(); + att.mime = _headers[messageIndex].part_headers[i].content_type.c_str(); + att.name = _headers[messageIndex].part_headers[i].name.c_str(); + att.size = _headers[messageIndex].part_headers[i].attach_data_size; + att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str(); + att.type = _headers[messageIndex].part_headers[i].attach_type; + msg.attachments.push_back(att); + } + } + } + } + } +} + +void IMAPSession::getRFC822Messages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg) +{ + if (messageIndex < _headers.size()) + { + int sz = _headers[messageIndex].part_headers.size(); + int partIdx = 0; + int cIdx = 0; + IMAP_MSG_Item *_rfc822 = nullptr; + if (sz > 0) + { + for (int i = 0; i < sz; i++) + { + if (_headers[messageIndex].part_headers[i].message_sub_type == esp_mail_imap_message_sub_type_rfc822) + { + if (_headers[messageIndex].part_headers[i].rfc822_part) + { + if (partIdx > 0) + msg.rfc822.push_back(*_rfc822); + cIdx = i; + partIdx++; + _rfc822 = new IMAP_MSG_Item(); + + _rfc822->from = _headers[messageIndex].part_headers[i].rfc822_header.from.c_str(); + _rfc822->sender = _headers[messageIndex].part_headers[i].rfc822_header.sender.c_str(); + _rfc822->to = _headers[messageIndex].part_headers[i].rfc822_header.to.c_str(); + _rfc822->cc = _headers[messageIndex].part_headers[i].rfc822_header.cc.c_str(); + _rfc822->bcc = _headers[messageIndex].part_headers[i].rfc822_header.bcc.c_str(); + _rfc822->return_path = _headers[messageIndex].part_headers[i].rfc822_header.return_path.c_str(); + _rfc822->reply_to = _headers[messageIndex].part_headers[i].rfc822_header.reply_to.c_str(); + _rfc822->subject = _headers[messageIndex].part_headers[i].rfc822_header.subject.c_str(); + _rfc822->comment = _headers[messageIndex].part_headers[i].rfc822_header.comment.c_str(); + _rfc822->keyword = _headers[messageIndex].part_headers[i].rfc822_header.keyword.c_str(); + _rfc822->date = _headers[messageIndex].part_headers[i].rfc822_header.date.c_str(); + _rfc822->messageID = _headers[messageIndex].part_headers[i].rfc822_header.messageID.c_str(); + _rfc822->text.charSet = ""; + _rfc822->text.content_type = ""; + _rfc822->text.transfer_encoding = ""; + _rfc822->html.charSet = ""; + _rfc822->html.content_type = ""; + _rfc822->html.transfer_encoding = ""; + } + else + { + if (MailClient.multipartMember(_headers[messageIndex].part_headers[cIdx].partNumStr, _headers[messageIndex].part_headers[i].partNumStr)) + { + if (_headers[messageIndex].part_headers[i].attach_type == esp_mail_att_type_none) + { + if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_plain || _headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_enriched) + { + _rfc822->text.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); + _rfc822->text.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); + _rfc822->text.content = _headers[messageIndex].part_headers[i].text.c_str(); + _rfc822->text.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); + } + if (_headers[messageIndex].part_headers[i].msg_type == esp_mail_msg_type_html) + { + _rfc822->html.charSet = _headers[messageIndex].part_headers[i].charset.c_str(); + _rfc822->html.content_type = _headers[messageIndex].part_headers[i].content_type.c_str(); + _rfc822->html.content = _headers[messageIndex].part_headers[i].text.c_str(); + _rfc822->html.transfer_encoding = _headers[messageIndex].part_headers[i].content_transfer_encoding.c_str(); + } + } + else + { + struct esp_mail_attacment_info_t att; + att.filename = _headers[messageIndex].part_headers[i].filename.c_str(); + att.mime = _headers[messageIndex].part_headers[i].content_type.c_str(); + att.name = _headers[messageIndex].part_headers[i].name.c_str(); + att.size = _headers[messageIndex].part_headers[i].attach_data_size; + att.creationDate = _headers[messageIndex].part_headers[i].creation_date.c_str(); + att.type = _headers[messageIndex].part_headers[i].attach_type; + _rfc822->attachments.push_back(att); + } + } + } + } + } + + if ((int)msg.rfc822.size() < partIdx && _rfc822 != nullptr) + msg.rfc822.push_back(*_rfc822); + } + } +} + +bool IMAPSession::closeMailbox() +{ + + if (!MailClient.reconnect(this)) + return false; + + std::string s; + + if (_readCallback) + { + MailClient.appendP(s, esp_mail_str_210, true); + s += _currentFolder; + MailClient.appendP(s, esp_mail_str_96, false); + MailClient.imapCB(this, "", false); + MailClient.imapCB(this, s.c_str(), false); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_197); + + if (MailClient.imapSendP(this, esp_mail_str_195, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + _imap_cmd = esp_mail_imap_cmd_close; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CLOSE_MAILBOX_FAILED, false)) + return false; + + _currentFolder.clear(); + _mailboxOpened = false; + + return true; +} + +bool IMAPSession::openMailbox(const char *folder, esp_mail_imap_auth_mode mode, bool waitResponse) +{ + + if (!MailClient.reconnect(this)) + return false; + + if (_currentFolder.length() > 0) + { + if (strcmp(_currentFolder.c_str(), folder) != 0) + { + if (!closeMailbox()) + return false; + } + } + + _currentFolder = folder; + std::string s; + if (_readCallback) + { + MailClient.appendP(s, esp_mail_str_61, true); + s += _currentFolder; + MailClient.appendP(s, esp_mail_str_96, false); + MailClient.imapCB(this, "", false); + MailClient.imapCB(this, s.c_str(), false); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_248); + + if (mode == esp_mail_imap_mode_examine) + { + MailClient.appendP(s, esp_mail_str_135, true); + _imap_cmd = esp_mail_imap_cmd_examine; + } + else if (mode == esp_mail_imap_mode_select) + { + MailClient.appendP(s, esp_mail_str_247, true); + _imap_cmd = esp_mail_imap_cmd_select; + } + s += _currentFolder; + MailClient.appendP(s, esp_mail_str_136, false); + if (MailClient.imapSend(this, s.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + if (waitResponse) + { + + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_OPEN_MAILBOX_FAILED, false)) + return false; + } + + if (mode == esp_mail_imap_mode_examine) + _readOnlyMode = true; + else if (mode == esp_mail_imap_mode_select) + _readOnlyMode = false; + + _mailboxOpened = true; + + return true; +} + +bool IMAPSession::getMailboxes(FoldersCollection &folders) +{ + _folders.clear(); + + if (_readCallback) + { + MailClient.imapCB(this, "", false); + MailClient.imapCBP(this, esp_mail_str_58, false); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_230); + + if (MailClient.imapSendP(this, esp_mail_str_133, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_list; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_LIST_MAILBOXS_FAILED, false)) + return false; + + folders = _folders; + return true; +} + +bool IMAPSession::checkCapability() +{ + if (_readCallback) + { + MailClient.imapCB(this, "", false); + MailClient.imapCBP(this, esp_mail_str_64, false); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_65); + + if (MailClient.imapSendP(this, esp_mail_str_2, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_capability; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_CHECK_CAPABILITIES_FAILED, false)) + return false; + + return true; +} + +bool IMAPSession::createFolder(const char *folderName) +{ + if (_debug) + { + MailClient.imapCB(this, "", false); + MailClient.debugInfoP(esp_mail_str_320); + } + + std::string cmd; + MailClient.appendP(cmd, esp_mail_str_322, true); + cmd += folderName; + + if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_create; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + + return true; +} + +bool IMAPSession::deleteFolder(const char *folderName) +{ + if (_debug) + { + MailClient.imapCB(this, "", false); + MailClient.debugInfoP(esp_mail_str_321); + } + + std::string cmd; + MailClient.appendP(cmd, esp_mail_str_323, true); + cmd += folderName; + + if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_delete; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + + return true; +} + +bool IMAPSession::deleteMessages(MessageList *toDelete, bool expunge) +{ + if (toDelete->_list.size() > 0) + { + + if (!selectFolder(_currentFolder.c_str(), false)) + return false; + + if (_debug) + { + MailClient.imapCB(this, "", false); + MailClient.debugInfoP(esp_mail_str_316); + } + + std::string cmd; + char *tmp = nullptr; + MailClient.appendP(cmd, esp_mail_str_249, true); + for (size_t i = 0; i < toDelete->_list.size(); i++) + { + if (i > 0) + MailClient.appendP(cmd, esp_mail_str_263, false); + tmp = MailClient.intStr(toDelete->_list[i]); + cmd += tmp; + MailClient.delS(tmp); + } + MailClient.appendP(cmd, esp_mail_str_315, false); + + if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + + if (expunge) + { + if (MailClient.imapSendP(this, esp_mail_str_317, true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_expunge; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + } + } + + return true; +} + +bool IMAPSession::copyMessages(MessageList *toCopy, const char *dest) +{ + if (toCopy->_list.size() > 0) + { + + if (!selectFolder(_currentFolder.c_str(), false)) + return false; + + if (_debug) + { + MailClient.imapCB(this, "", false); + std::string s; + MailClient.appendP(s, esp_mail_str_318, true); + s += dest; + esp_mail_debug(s.c_str()); + } + + std::string cmd; + char *tmp = nullptr; + MailClient.appendP(cmd, esp_mail_str_319, true); + for (size_t i = 0; i < toCopy->_list.size(); i++) + { + if (i > 0) + MailClient.appendP(cmd, esp_mail_str_263, false); + tmp = MailClient.intStr(toCopy->_list[i]); + cmd += tmp; + MailClient.delS(tmp); + } + MailClient.appendP(cmd, esp_mail_str_131, false); + cmd += dest; + + if (MailClient.imapSend(this, cmd.c_str(), true) == ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED) + return false; + + _imap_cmd = esp_mail_imap_command::esp_mail_imap_cmd_store; + if (!MailClient.handleIMAPResponse(this, IMAP_STATUS_BAD_COMMAND, false)) + return false; + } + + return true; +} + +#if defined(ESP8266) +void ESP_Mail_Client::setClock(float offset) +{ + if (WiFi.status() != WL_CONNECTED) + WiFi.reconnect(); + + time_t now = time(nullptr); + + _clockReady = now > ESP_MAIL_CLIENT_VALID_TS; + + if (!_clockReady) + { + char *server1 = strP(esp_mail_str_283); + char *server2 = strP(esp_mail_str_296); + + configTime(offset * 3600, 0, server1, server2); + + now = time(nullptr); + uint8_t attempts = 0; + while (now < ESP_MAIL_CLIENT_VALID_TS) + { + now = time(nullptr); + attempts++; + if (attempts > 200 || now > ESP_MAIL_CLIENT_VALID_TS) + break; + delay(100); + } + + delS(server1); + delS(server2); + } + + _clockReady = now > ESP_MAIL_CLIENT_VALID_TS; +} +#endif + +void IMAPSession::empty() +{ + std::string().swap(_nextUID); + clearMessageData(); +} + +void IMAPSession::clearMessageData() +{ + for (size_t i = 0; i < _headers.size(); i++) + { + _headers[i].part_headers.clear(); + std::vector().swap(_headers[i].part_headers); + } + std::vector().swap(_headers); + std::vector().swap(_msgNum); + _folders.clear(); + _mbif._flags.clear(); + _mbif._searchCount = 0; +} + +SMTPSession::SMTPSession() +{ +} + +SMTPSession::~SMTPSession() +{ + closeSession(); + _caCert.reset(); + _caCert = nullptr; +} + +bool SMTPSession::connect(ESP_Mail_Session *config) +{ + if (_tcpConnected) + MailClient.closeTCP(this); + + _sesson_cfg = config; + _caCert = nullptr; + + if (strlen(_sesson_cfg->certificate.cert_data) > 0) + _caCert = std::shared_ptr(_sesson_cfg->certificate.cert_data); + + if (strlen(_sesson_cfg->certificate.cert_file) > 0) + { + if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_sd && !MailClient._sdOk) + MailClient._sdOk = MailClient.sdTest(); + if (_sesson_cfg->certificate.cert_file_storage_type == esp_mail_file_storage_type::esp_mail_file_storage_type_flash && !MailClient._flashOk) +#if defined(ESP32) + MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(FORMAT_FLASH); +#elif defined(ESP8266) + MailClient._flashOk = ESP_MAIL_FLASH_FS.begin(); +#endif + } + return MailClient.smtpAuth(this); +} + +void SMTPSession::debug(int level) +{ + if (level > esp_mail_debug_level_0) + { + if (level > esp_mail_debug_level_3) + level = esp_mail_debug_level_1; + _debugLevel = level; + _debug = true; + } + else + { + _debugLevel = esp_mail_debug_level_0; + _debug = false; + } +} + +String SMTPSession::errorReason() +{ + std::string ret; + + switch (_smtpStatus.statusCode) + { + case SMTP_STATUS_SERVER_CONNECT_FAILED: + MailClient.appendP(ret, esp_mail_str_38, true); + break; + case SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED: + MailClient.appendP(ret, esp_mail_str_39, true); + break; + case SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED: + MailClient.appendP(ret, esp_mail_str_39, true); + break; + case SMTP_STATUS_AUTHEN_NOT_SUPPORT: + MailClient.appendP(ret, esp_mail_str_42, true); + break; + case SMTP_STATUS_AUTHEN_FAILED: + MailClient.appendP(ret, esp_mail_str_43, true); + break; + case SMTP_STATUS_USER_LOGIN_FAILED: + MailClient.appendP(ret, esp_mail_str_43, true); + break; + case SMTP_STATUS_PASSWORD_LOGIN_FAILED: + MailClient.appendP(ret, esp_mail_str_47, true); + break; + case SMTP_STATUS_SEND_HEADER_SENDER_FAILED: + MailClient.appendP(ret, esp_mail_str_48, true); + break; + case SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED: + MailClient.appendP(ret, esp_mail_str_222, true); + break; + case SMTP_STATUS_SEND_BODY_FAILED: + MailClient.appendP(ret, esp_mail_str_49, true); + break; + case MAIL_CLIENT_ERROR_CONNECTION_LOST: + MailClient.appendP(ret, esp_mail_str_221, true); + break; + case MAIL_CLIENT_ERROR_READ_TIMEOUT: + MailClient.appendP(ret, esp_mail_str_258, true); + break; + case MAIL_CLIENT_ERROR_FILE_IO_ERROR: + MailClient.appendP(ret, esp_mail_str_282, true); + break; + case SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED: + MailClient.appendP(ret, esp_mail_str_293, true); + break; + case MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED: + MailClient.appendP(ret, esp_mail_str_305, true); + break; + case MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP: + MailClient.appendP(ret, esp_mail_str_132, true); + break; + case SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED: + MailClient.appendP(ret, esp_mail_str_206, true); + break; + case SMTP_STATUS_NO_VALID_SENDER_EXISTED: + MailClient.appendP(ret, esp_mail_str_205, true); + break; + case MAIL_CLIENT_ERROR_OUT_OF_MEMORY: + MailClient.appendP(ret, esp_mail_str_186, true); + break; + case SMTP_STATUS_NO_SUPPORTED_AUTH: + MailClient.appendP(ret, esp_mail_str_42, true); + break; + + default: + break; + } + + if (_smtpStatus.text.length() > 0 && ret.length() == 0) + { + MailClient.appendP(ret, esp_mail_str_312, true); + char *code = MailClient.intStr(_smtpStatus.respCode); + ret += code; + MailClient.delS(code); + MailClient.appendP(ret, esp_mail_str_313, false); + ret += _smtpStatus.text; + return ret.c_str(); + } + return ret.c_str(); +} + +bool SMTPSession::closeSession() +{ + if (!_tcpConnected) + return false; + + if (_sendCallback) + { + _cbData._sentSuccess = _sentSuccessCount; + _cbData._sentFailed = _sentFailedCount; + MailClient.smtpCB(this, ""); + MailClient.smtpCBP(this, esp_mail_str_128); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_245); + + bool ret = true; + +/* Sign out */ +#if defined(ESP32) + /** + * The strange behavior in ESP8266 SSL client, BearSSLWiFiClientSecure + * The client disposed without memory released after the server close + * the connection due to QUIT command, which caused the memory leaks. + */ + MailClient.smtpSendP(this, esp_mail_str_7, true); + _smtp_cmd = esp_mail_smtp_cmd_logout; + ret = MailClient.handleSMTPResponse(this, esp_mail_smtp_status_code_221, SMTP_STATUS_SEND_BODY_FAILED); +#endif + + if (ret) + { + + if (_sendCallback) + { + MailClient.smtpCB(this, ""); + MailClient.smtpCBP(this, esp_mail_str_129, false); + } + + if (_debug) + MailClient.debugInfoP(esp_mail_str_246); + + if (_sendCallback) + MailClient.smtpCB(this, "", true); + } + + return MailClient.handleSMTPError(this, 0, ret); +} + +void SMTPSession::callback(smtpStatusCallback smtpCallback) +{ + _sendCallback = std::move(smtpCallback); +} + +IMAP_Status::IMAP_Status() +{ +} +IMAP_Status::~IMAP_Status() +{ + empty(); +} + +const char *IMAP_Status::info() +{ + return _info.c_str(); +} + +bool IMAP_Status::success() +{ + return _success; +} + +void IMAP_Status::empty() +{ + std::string().swap(_info); +} + +SMTP_Status::SMTP_Status() +{ +} + +SMTP_Status::~SMTP_Status() +{ + empty(); +} + +const char *SMTP_Status::info() +{ + return _info.c_str(); +} + +bool SMTP_Status::success() +{ + return _success; +} + +size_t SMTP_Status::completedCount() +{ + return _sentSuccess; +} + +size_t SMTP_Status::failedCount() +{ + return _sentFailed; +} + +void SMTP_Status::empty() +{ + std::string().swap(_info); +} + +ESP_Mail_Client MailClient = ESP_Mail_Client(); + +#endif /* ESP_Mail_Client_CPP */ diff --git a/lib/lib_div/lib_mail/src/ESP_Mail_Client.h b/lib/libesp32/lib_mail/src/ESP_Mail_Client.h old mode 100755 new mode 100644 similarity index 97% rename from lib/lib_div/lib_mail/src/ESP_Mail_Client.h rename to lib/libesp32/lib_mail/src/ESP_Mail_Client.h index 9d3c97d5b..88785170a --- a/lib/lib_div/lib_mail/src/ESP_Mail_Client.h +++ b/lib/libesp32/lib_mail/src/ESP_Mail_Client.h @@ -1,2820 +1,2820 @@ -#ifndef ESP_Mail_Client_H -#define ESP_Mail_Client_H - -#define ESP_MAIL_VERSION "1.2.0" - -/** - * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 - * - * Version: 1.2.0 - * Released: May 17, 2021 - * - * Updates: - * - Add support ESP8266 Core SDK v3.x.x. - * - * - * This library allows Espressif's ESP32 and ESP8266 devices to send and read - * Email - * through the SMTP and IMAP servers. - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of - * the Software, and to permit persons to whom the Software is furnished to do - * so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS - * OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include "extras/RFC2047.h" -#include "extras/ESPTimeHelper.h" - -#if defined(ESP32) -#define FORMAT_FLASH FORMAT_FLASH_IF_MOUNT_FAILED -#include -#include -//#include -#include -#include "wcs/esp32/ESP_Mail_HTTPClient32.h" -#elif defined(ESP8266) -#include -#define FS_NO_GLOBALS -#include -#include "wcs/esp8266/ESP_Mail_HTTPClient.h" -#endif - -#include "extras/MIMEInfo.h" - -#include -#include -#include - -#if defined(ESP8266) -#define SD_CS_PIN 15 -#endif - -#define SMTP_STATUS_SERVER_CONNECT_FAILED -100 -#define SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED -101 -#define SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED -102 -#define SMTP_STATUS_AUTHEN_NOT_SUPPORT -103 -#define SMTP_STATUS_AUTHEN_FAILED -104 -#define SMTP_STATUS_USER_LOGIN_FAILED -105 -#define SMTP_STATUS_PASSWORD_LOGIN_FAILED -106 -#define SMTP_STATUS_SEND_HEADER_SENDER_FAILED -107 -#define SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED -108 -#define SMTP_STATUS_SEND_BODY_FAILED -109 -#define SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED -110 -#define SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED -111 -#define SMTP_STATUS_NO_VALID_SENDER_EXISTED -112 -#define SMTP_STATUS_NO_SUPPORTED_AUTH -113 - -#define IMAP_STATUS_SERVER_CONNECT_FAILED -200 -#define IMAP_STATUS_IMAP_RESPONSE_FAILED -201 -#define IMAP_STATUS_LOGIN_FAILED -202 -#define IMAP_STATUS_BAD_COMMAND -203 -#define IMAP_STATUS_PARSE_FLAG_FAILED -204 -#define IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED -205 -#define IMAP_STATUS_NO_MESSAGE -206 -#define IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT -207 -#define IMAP_STATUS_CLOSE_MAILBOX_FAILED -208 -#define IMAP_STATUS_OPEN_MAILBOX_FAILED -209 -#define IMAP_STATUS_LIST_MAILBOXS_FAILED -210 -#define IMAP_STATUS_CHECK_CAPABILITIES_FAILED -211 -#define IMAP_STATUS_NO_SUPPORTED_AUTH -212 -#define IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED -213 - -#define MAIL_CLIENT_ERROR_CONNECTION_LOST -28 -#define MAIL_CLIENT_ERROR_READ_TIMEOUT -29 -#define MAIL_CLIENT_ERROR_FILE_IO_ERROR -30 -#define MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED -31 -#define MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP -32 -#define MAIL_CLIENT_ERROR_OUT_OF_MEMORY -33 - -#define MAX_EMAIL_SEARCH_LIMIT 1000 -#define BASE64_CHUNKED_LEN 76 -#define FLOWED_TEXT_LEN 78 -#define ESP_MAIL_WIFI_RECONNECT_TIMEOUT 10000 -#define ESP_MAIL_PROGRESS_REPORT_STEP 20 -#define ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED 0 -#define ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE 256 -#define ESP_MAIL_CLIENT_VALID_TS 1577836800 - -#if defined(ESP32) -#define ESP_MAIL_MIN_MEM 70000 -#elif defined(ESP8266) -#define ESP_MAIL_MIN_MEM 4000 -#endif - -class IMAPSession; -class SMTPSession; -class SMTP_Status; -class DownloadProgress; -class MessageData; - -enum esp_mail_smtp_status_code -{ - esp_mail_smtp_status_code_0, // default - - /* Positive Completion */ - esp_mail_smtp_status_code_211 = 221, // System status, or system help reply - esp_mail_smtp_status_code_214 = - 214, // Help message(A response to the HELP command) - esp_mail_smtp_status_code_220 = 220, // Service ready - esp_mail_smtp_status_code_221 = - 221, // Service closing transmission channel [RFC 2034] - esp_mail_smtp_status_code_235 = - 235, // 2.7.0 Authentication succeeded[RFC 4954] - esp_mail_smtp_status_code_250 = 250, // Requested mail action okay, completed - esp_mail_smtp_status_code_251 = 251, // User not local; will forward - esp_mail_smtp_status_code_252 = 252, // Cannot verify the user, but it will - // try to deliver the message anyway - - /* Positive Intermediate */ - esp_mail_smtp_status_code_334 = 334, //(Server challenge - the text part - //contains the Base64 - encoded - //challenge)[RFC 4954] - esp_mail_smtp_status_code_354 = 354, // Start mail input - - /* Transient Negative Completion */ - /* "Transient Negative" means the error condition is temporary, and the action - may be requested again.*/ - esp_mail_smtp_status_code_421 = - 421, // Service is unavailable because the server is shutting down. - esp_mail_smtp_status_code_432 = - 432, // 4.7.12 A password transition is needed [RFC 4954] - esp_mail_smtp_status_code_450 = - 450, // Requested mail action not taken: mailbox unavailable (e.g., - // mailbox busy or temporarily blocked for policy reasons) - esp_mail_smtp_status_code_451 = - 451, // Requested action aborted : local error in processing - // e.g.IMAP server unavailable[RFC 4468] - esp_mail_smtp_status_code_452 = - 452, // Requested action not taken : insufficient system storage - esp_mail_smtp_status_code_454 = - 454, // 4.7.0 Temporary authentication failure[RFC 4954] - esp_mail_smtp_status_code_455 = - 455, // Server unable to accommodate parameters - - /* Permanent Negative Completion */ - esp_mail_smtp_status_code_500 = 500, // Syntax error, command unrecognized - // (This may include errors such as - // command line too long) - // e.g. Authentication Exchange line is too long [RFC 4954] - esp_mail_smtp_status_code_501 = - 501, // Syntax error in parameters or arguments - // e.g. 5.5.2 Cannot Base64-decode Client responses [RFC 4954] - // 5.7.0 Client initiated Authentication Exchange (only when the SASL - // mechanism specified that client does not begin the authentication exchange) - // [RFC 4954] - esp_mail_smtp_status_code_502 = 502, // Command not implemented - esp_mail_smtp_status_code_503 = 503, // Bad sequence of commands - esp_mail_smtp_status_code_504 = 504, // Command parameter is not implemented - // e.g. 5.5.4 Unrecognized authentication type [RFC 4954] - esp_mail_smtp_status_code_521 = 521, // Server does not accept mail [RFC 7504] - esp_mail_smtp_status_code_523 = 523, // Encryption Needed [RFC 5248] - esp_mail_smtp_status_code_530 = - 530, // 5.7.0 Authentication required [RFC 4954] - esp_mail_smtp_status_code_534 = - 534, // 5.7.9 Authentication mechanism is too weak [RFC 4954] - esp_mail_smtp_status_code_535 = - 535, // 5.7.8 Authentication credentials invalid [RFC 4954] - esp_mail_smtp_status_code_538 = 538, // 5.7.11 Encryption required for - // requested authentication mechanism[RFC - // 4954] - esp_mail_smtp_status_code_550 = - 550, // Requested action not taken: mailbox unavailable (e.g., mailbox not - // found, no access, or command rejected for policy reasons) - esp_mail_smtp_status_code_551 = - 551, // User not local; please try - esp_mail_smtp_status_code_552 = - 552, // Requested mail action aborted: exceeded storage allocation - esp_mail_smtp_status_code_553 = - 553, // Requested action not taken: mailbox name not allowed - esp_mail_smtp_status_code_554 = 554, // Transaction has failed (Or, in the - // case of a connection-opening response, - // "No SMTP service here") - // e.g. 5.3.4 Message too big for system [RFC 4468] - esp_mail_smtp_status_code_556 = 556, // Domain does not accept mail[RFC 7504] -}; - -enum esp_mail_smtp_port -{ - esp_mail_smtp_port_25 = 25, // PLAIN/TLS with STARTTLS - esp_mail_smtp_port_465 = 465, // SSL - esp_mail_smtp_port_587 = 587 // TLS with STARTTLS -}; - -enum esp_mail_imap_port -{ - esp_mail_imap_port_143 = 143, // PLAIN/TLS with STARTTLS - esp_mail_imap_port_993 = 993, // SSL -}; - -enum esp_mail_smtp_notify -{ - esp_mail_smtp_notify_never = 0, - esp_mail_smtp_notify_success = 1, - esp_mail_smtp_notify_failure = 2, - esp_mail_smtp_notify_delay = 4 -}; - -enum esp_mail_attach_type -{ - esp_mail_att_type_none, - esp_mail_att_type_attachment, - esp_mail_att_type_inline -}; - -enum esp_mail_message_type -{ - esp_mail_msg_type_none = 0, - esp_mail_msg_type_plain = 1, - esp_mail_msg_type_html = 2, - esp_mail_msg_type_enriched = 1 -}; - -enum esp_mail_auth_type -{ - esp_mail_auth_type_psw, - esp_mail_auth_type_oath2, - esp_mail_auth_type_token -}; - -enum esp_mail_imap_auth_mode -{ - esp_mail_imap_mode_examine, - esp_mail_imap_mode_select -}; - -enum esp_mail_imap_response_status -{ - esp_mail_imap_resp_unknown, - esp_mail_imap_resp_ok, - esp_mail_imap_resp_no, - esp_mail_imap_resp_bad -}; - -enum esp_mail_imap_header_state -{ - esp_mail_imap_state_from = 1, - esp_mail_imap_state_to, - esp_mail_imap_state_cc, - esp_mail_imap_state_subject, - esp_mail_imap_state_content_type, - esp_mail_imap_state_content_transfer_encoding, - esp_mail_imap_state_accept_language, - esp_mail_imap_state_content_language, - esp_mail_imap_state_date, - esp_mail_imap_state_msg_id, - esp_mail_imap_state_char_set, - esp_mail_imap_state_boundary -}; - -enum esp_mail_imap_command -{ - esp_mail_imap_cmd_capability, - esp_mail_imap_cmd_starttls, - esp_mail_imap_cmd_login, - esp_mail_imap_cmd_plain, - esp_mail_imap_cmd_auth, - esp_mail_imap_cmd_list, - esp_mail_imap_cmd_select, - esp_mail_imap_cmd_examine, - esp_mail_imap_cmd_close, - esp_mail_imap_cmd_status, - esp_mail_imap_cmd_search, - esp_mail_imap_cmd_fetch_body_header, - esp_mail_imap_cmd_fetch_body_mime, - esp_mail_imap_cmd_fetch_body_text, - esp_mail_imap_cmd_fetch_body_attachment, - esp_mail_imap_cmd_fetch_body_inline, - esp_mail_imap_cmd_logout, - esp_mail_imap_cmd_store, - esp_mail_imap_cmd_expunge, - esp_mail_imap_cmd_create, - esp_mail_imap_cmd_delete -}; - -enum esp_mail_imap_mime_fetch_type -{ - esp_mail_imap_mime_fetch_type_part, - esp_mail_imap_mime_fetch_type_sub_part1, - esp_mail_imap_mime_fetch_type_sub_part2 -}; - -enum esp_mail_smtp_command -{ - esp_mail_smtp_cmd_initial_state, - esp_mail_smtp_cmd_greeting, - esp_mail_smtp_cmd_start_tls, - esp_mail_smtp_cmd_login_user, - esp_mail_smtp_cmd_auth_plain, - esp_mail_smtp_cmd_auth, - esp_mail_smtp_cmd_login_psw, - esp_mail_smtp_cmd_send_header_sender, - esp_mail_smtp_cmd_send_header_recipient, - esp_mail_smtp_cmd_send_body, - esp_mail_smtp_cmd_chunk_termination, - esp_mail_smtp_cmd_logout -}; - -enum esp_mail_imap_header_type -{ - esp_mail_imap_header_from, - esp_mail_imap_header_to, - esp_mail_imap_header_cc, - esp_mail_imap_header_subject, - esp_mail_imap_header_date, - esp_mail_imap_header_msg_id, - esp_mail_imap_header_cont_lang, - esp_mail_imap_header_accept_lang -}; - -enum esp_mail_char_decoding_scheme -{ - esp_mail_char_decoding_scheme_default, - esp_mail_char_decoding_scheme_iso8859_1, - esp_mail_char_decoding_scheme_tis620 -}; - -enum esp_mail_smtp_priority -{ - esp_mail_smtp_priority_high = 1, - esp_mail_smtp_priority_normal = 3, - esp_mail_smtp_priority_low = 5, -}; - -enum esp_mail_smtp_embed_message_type -{ - esp_mail_smtp_embed_message_type_attachment = 0, - esp_mail_smtp_embed_message_type_inline -}; - -enum esp_mail_debug_level -{ - esp_mail_debug_level_0 = 0, - esp_mail_debug_level_1, - esp_mail_debug_level_2 = 222, - esp_mail_debug_level_3 = 333 -}; - -enum esp_mail_imap_multipart_sub_type -{ - esp_mail_imap_multipart_sub_type_none = 0, - esp_mail_imap_multipart_sub_type_mixed, - esp_mail_imap_multipart_sub_type_alternative, - esp_mail_imap_multipart_sub_type_parallel, - esp_mail_imap_multipart_sub_type_digest, - esp_mail_imap_multipart_sub_type_related, - esp_mail_imap_multipart_sub_type_report, -}; - -enum esp_mail_imap_message_sub_type -{ - esp_mail_imap_message_sub_type_none = 0, - esp_mail_imap_message_sub_type_rfc822, - esp_mail_imap_message_sub_type_delivery_status, - esp_mail_imap_message_sub_type_partial, - esp_mail_imap_message_sub_type_external_body, -}; - -/* descrete media types (rfc 2046) */ -struct esp_mail_imap_descrete_media_type_t -{ - /** textual information with subtypes - * "plain" - * "enriched" (rfc 1896 revised from richtext in rfc 1341) - * - * unrecognized subtypes and charset should be interpreted as - * application/octet-stream - * - * parameters: - * "charset" (rfc 2045) default is us-ascii - * for character set includes 8-bit characters - * and such characters are used in the body, Content-Transfer-Encoding - * header field and a corresponding encoding on the data are required - * - * ISO-8859-X where "X" is to be replaced, as - * necessary, for the parts of ISO-8859 [ISO-8859]. - */ - static constexpr const char *text = "text"; - - /** image data with subtypes (rfc 2048) - * "jpeg" - * "gif" - * - * unrecognized subtypes should be interpreted as application/octet-stream - */ - static constexpr const char *image = "image"; - - /** audio data with initial subtype - * "baic" -- for single channel audio encoded using 8bit ISDN mu-law [PCM] - * at a sample rate of 8000 Hz. - * - * Unrecognized subtypes of "audio" should at a miniumum be treated as - * "application/octet-stream" - */ - static constexpr const char *audio = "audio"; - - /** video data with initial subtype - * "mpeg" - * - * Unrecognized subtypes of "video" should at a minumum be treated as - * "application/octet-stream" - */ - static constexpr const char *video = "video"; - - /** some other kind of data, typically either - * uninterpreted binary data or information to be - * processed by an application with subtypes - * - * "octet-stream" -- uninterpreted binary data - * "PostScript" -- for the transport of PostScript material - * - * Other expected uses include spreadsheets, data for mail-based - * scheduling systems, and languages for "active" (computational) - * messaging, and word processing formats that are not directly readable. - * - * The octet-stream subtype parameters: - * TYPE, PADDING, NAME - */ - static constexpr const char *application = "application"; -}; - -/** composite media types (rfc 2046) - * - * As stated in the definition of the Content-Transfer-Encoding field - * [RFC 2045], no encoding other than "7bit", "8bit", or "binary" is - * permitted for entities of type "multipart". The "multipart" boundary - * delimiters and header fields are always represented as 7bit US-ASCII - * in any case (though the header fields may encode non-US-ASCII header - * text as per RFC 2047) and data within the body parts can be encoded - * on a part-by-part basis, with Content-Transfer-Encoding fields for - * each appropriate body part. -*/ -struct esp_mail_imap_composite_media_type_t -{ - /** data consisting of multiple entities of independent data types - * The Content-Type field for multipart entities requires one parameter, - * "boundary". - * The boundary delimiter line is then defined as a line - * consisting entirely of two hyphen characters ("-", decimal value 45) - * followed by the boundary parameter value from the Content-Type header - * field, optional linear whitespace, and a terminating CRLF. - * - * NOTE: The CRLF preceding the boundary delimiter line is conceptually - * attached to the boundary so that it is possible to have a part that - * does not end with a CRLF (line break). Body parts that must be - * considered to end with line breaks, therefore, must have two CRLFs - * preceding the boundary delimiter line, the first of which is part of - * the preceding body part, and the second of which is part of the - * encapsulation boundary. - * - * Boundary delimiters must not appear within the encapsulated material, - * and must be no longer than 70 characters, not counting the two - * leading hyphens. - * - * The boundary delimiter line following the last body part is a - * distinguished delimiter that indicates that no further body parts - * will follow. Such a delimiter line is identical to the previous - * delimiter lines, with the addition of two more hyphens after the - * boundary parameter value. - * - * See rfc2049 Appendix A for a Complex Multipart Example - */ - static constexpr const char *multipart = "multipart"; - - /* an encapsulated message */ - static constexpr const char *message = "message"; -}; - -struct esp_mail_imap_media_text_sub_type_t -{ - static constexpr const char *plain = "plain"; - static constexpr const char *enriched = "enriched"; - static constexpr const char *html = "html"; -}; - -/* multipart sub types */ -struct esp_mail_imap_multipart_sub_type_t -{ - /* a generic mixed set of parts */ - static constexpr const char *mixed = "mixed"; - - /* the same data in multiple formats */ - static constexpr const char *alternative = "alternative"; - - /* parts intended to be viewed simultaneously */ - static constexpr const char *parallel = "parallel"; - - /* multipart entities in which each part has a default type of - * "message/rfc822" */ - static constexpr const char *digest = "digest"; - - /* for compound objects consisting of several inter-related body parts (rfc - * 2387) */ - static constexpr const char *related = "related"; - - /* rfc 3462 */ - static constexpr const char *report = "report"; -}; - -/* message body sub types */ -struct esp_mail_imap_message_sub_type_t -{ - /* body contains an encapsulated message, with the syntax of an RFC 822 - * message. */ - static constexpr const char *rfc822 = "rfc822"; - - /* to allow large objects to be delivered as several separate pieces of mail - */ - static constexpr const char *Partial = "Partial"; - - /* the actual body data are not included, but merely referenced */ - static constexpr const char *External_Body = "External-Body"; - - static constexpr const char *delivery_status = "delivery-status"; -}; - -/** content disposition rfc 2183 - * - * Parameters: - * "filename", "creation-date","modification-date", - * "read-date", * "size" -*/ -struct esp_mail_imap_content_disposition_type_t -{ - /** if it is intended to be displayed automatically - * upon display of the message. - */ - static constexpr const char *inline_ = "inline"; - - /** to indicate that they are separate from the main body - * of the mail message, and that their display should not - * be automatic, but contingent upon some further action of the user. - */ - static constexpr const char *attachment = "attachment"; -}; - -struct esp_mail_internal_use_t -{ - bool binary = false; - std::string cid = ""; -}; - -struct esp_mail_content_transfer_encoding_t -{ - /* The default 7-bit transfer encoding for US-ACII characters*/ - static constexpr const char *enc_7bit = "7bit"; - - /* The quoted printable transfer encoding for non-US-ASCII characters*/ - static constexpr const char *enc_qp = "quoted-printable"; - - /* The base64 encoded transfer encoding */ - static constexpr const char *enc_base64 = "base64"; - - /* The 8-bit transfer encoding for extended-US-ASCII characters*/ - static constexpr const char *enc_8bit = "8bit"; - - /* The binary transfer encoding for extended-US-ASCII characters with no line - * length limit*/ - static constexpr const char *enc_binary = "binary"; -}; - -struct esp_mail_smtp_response_status_t -{ - int respCode = 0; - int statusCode = 0; - std::string text = ""; -}; - -struct esp_mail_imap_response_status_t -{ - int statusCode = 0; - std::string text = ""; -}; - -/* The option to embed this message content as a file */ -struct esp_mail_smtp_embed_message_body_t -{ - /* Enable to send this message body as file */ - bool enable = false; - - /* The name of embedded file */ - const char *filename = ""; - - /** The embedded type - * esp_mail_smtp_embed_message_type_attachment or 0 - * esp_mail_smtp_embed_message_type_inline or 1 - */ - esp_mail_smtp_embed_message_type type = - esp_mail_smtp_embed_message_type_attachment; -}; - -struct esp_mail_file_message_content_t -{ - /* The file path include its name */ - const char *name = ""; - - /** The type of file storages e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - esp_mail_file_storage_type type = esp_mail_file_storage_type_flash; -}; - -struct esp_mail_blob_message_content_t -{ - /* The array of content in flash memory */ - const uint8_t *data = nullptr; - - /* The array size in bytes */ - size_t size = 0; -}; - -/* The PLAIN text body details of the message */ -struct esp_mail_plain_body_t -{ - /* The option to embed this message content as a file */ - struct esp_mail_smtp_embed_message_body_t embed; - - /* The PLAIN text content of the message */ - const char *content = ""; - - /* The blob that contins PLAIN text content of the message */ - struct esp_mail_blob_message_content_t blob; - - /* The file that contins PLAIN text content of the message */ - struct esp_mail_file_message_content_t file; - - /* The charset of the PLAIN text content of the message */ - const char *charSet = "UTF-8"; - - /* The content type of message */ - const char *content_type = "text/plain"; - - /* The option to encode the content for data transfer */ - const char *transfer_encoding = "7bit"; - - /* The option to send the PLAIN text with wrapping */ - bool flowed = false; - - /* The internal usage data */ - struct esp_mail_internal_use_t _int; -}; - -struct esp_mail_html_body_t -{ - /* The option to embedded the content as a file */ - struct esp_mail_smtp_embed_message_body_t embed; - - /* The HTML content of the message */ - const char *content = ""; - - /* The blob that contins HTML content of the message */ - struct esp_mail_blob_message_content_t blob; - - /* The file that contins HTML content of the message */ - struct esp_mail_file_message_content_t file; - - /* The charset of the HTML content of the message */ - const char *charSet = "UTF-8"; - - /* The content type of message */ - const char *content_type = "text/html"; - - /* The option to encode the content for data transfer */ - const char *transfer_encoding = "7bit"; - - /* The internal usage data */ - struct esp_mail_internal_use_t _int; -}; - -struct esp_mail_link_internal_t -{ - std::string cid = ""; -}; - -struct esp_mail_email_info_t -{ - /* The name of Email author*/ - const char *name = ""; - - /* The Email address */ - const char *email = ""; -}; -struct esp_mail_smtp_msg_response_t -{ - /* The author Email address to reply */ - const char *reply_to = ""; - - /* The sender Email address to return the message */ - const char *return_path = ""; - - /** The Delivery Status Notifications e.g. esp_mail_smtp_notify_never, - * esp_mail_smtp_notify_success, - * esp_mail_smtp_notify_failure, and - * esp_mail_smtp_notify_delay - */ - int notify = esp_mail_smtp_notify::esp_mail_smtp_notify_never; -}; - -struct esp_mail_smtp_enable_option_t -{ - /* Enable chunk data sending for large message */ - bool chunking = false; -}; - -struct esp_mail_attach_blob_t -{ - /* BLOB data (flash or ram) */ - const uint8_t *data = nullptr; - - /* BLOB data size in byte */ - size_t size = 0; -}; - -struct esp_mail_attach_file_t -{ - const char *path = ""; - /** The file storage type e.g. esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - esp_mail_file_storage_type storage_type = esp_mail_file_storage_type_none; -}; - -struct esp_mail_attach_descr_t -{ - /* The name of attachment */ - const char *name = ""; - - /* The attachment file name */ - const char *filename = ""; - - /* The MIME type of attachment */ - const char *mime = ""; - - /* The transfer encoding of attachment e.g. base64 */ - const char *transfer_encoding = "base64"; - - /* The content encoding of attachment e.g. base64 */ - const char *content_encoding = ""; - - /* The content id of attachment file */ - const char *content_id = ""; -}; - -struct esp_mail_attach_internal_t -{ - esp_mail_attach_type att_type = esp_mail_att_type_attachment; - int index = 0; - int msg_uid = 0; - bool flash_blob = false; - bool binary = false; - bool parallel = false; - std::string cid = ""; -}; - -struct esp_mail_attachment_t -{ - /* The attachment description */ - struct esp_mail_attach_descr_t descr; - - /* The BLOB data config */ - struct esp_mail_attach_blob_t blob; - - /* The file data config */ - struct esp_mail_attach_file_t file; - - /* reserved for internal usage */ - struct esp_mail_attach_internal_t _int; -}; - -struct esp_mail_smtp_recipient_t -{ - /* The recipient's name */ - const char *name = ""; - - /* The recipient's Email address */ - const char *email = ""; -}; - -struct esp_mail_smtp_recipient_address_t -{ - /* The recipient's Email address */ - const char *email = ""; -}; - -struct esp_mail_smtp_send_status_t -{ - /* The status of the message */ - bool completed = false; - - /* The primary recipient mailbox of the message */ - const char *recipients = ""; - - /* The topic of the message */ - const char *subject = ""; - - /* The timestamp of the message */ - time_t timesstamp = 0; -}; - -struct esp_mail_attacment_info_t -{ - const char *filename = ""; - const char *name = ""; - const char *creationDate = ""; - const char *mime = ""; - esp_mail_attach_type type = esp_mail_att_type_none; - size_t size; -}; - -struct esp_mail_auth_capability_t -{ - bool plain = false; - bool xoauth2 = false; - bool cram_md5 = false; - bool digest_md5 = false; - bool login = false; - bool start_tls = false; -}; - -struct esp_mail_smtp_capability_t -{ - bool esmtp = false; - bool binaryMIME = false; - bool _8bitMIME = false; - bool chunking = false; - bool utf8 = false; - bool pipelining = false; - bool dsn = false; -}; - -struct esp_mail_imap_rfc822_msg_header_item_t -{ - std::string sender = ""; - std::string from; - std::string subject = ""; - std::string messageID = ""; - std::string keyword = ""; - std::string comment = ""; - std::string date = ""; - std::string return_path = ""; - std::string reply_to; - std::string to = ""; - std::string cc = ""; - std::string bcc = ""; -}; - -struct esp_mail_message_part_info_t -{ - int octetLen = 0; - int attach_data_size = 0; - int textLen = 0; - bool sizeProp = false; - int nestedLevel = 0; - std::string partNumStr = ""; - std::string partNumFetchStr = ""; - std::string text = ""; - std::string filename = ""; - std::string type = ""; - std::string save_path = ""; - std::string name = ""; - std::string content_disposition = ""; - std::string content_type = ""; - std::string descr = ""; - std::string content_transfer_encoding = ""; - std::string creation_date = ""; - std::string modification_date = ""; - std::string charset = ""; - std::string download_error = ""; - esp_mail_attach_type attach_type = esp_mail_att_type_none; - esp_mail_message_type msg_type = esp_mail_msg_type_none; - bool file_open_write = false; - bool multipart = false; - esp_mail_imap_multipart_sub_type multipart_sub_type = - esp_mail_imap_multipart_sub_type_none; - esp_mail_imap_message_sub_type message_sub_type = - esp_mail_imap_message_sub_type_none; - bool rfc822_part = false; - int rfc822_msg_Idx = 0; - struct esp_mail_imap_rfc822_msg_header_item_t rfc822_header; - bool error = false; - bool plain_flowed = false; - bool plain_delsp = false; -}; - -struct esp_mail_message_header_t -{ - int header_data_len = 0; - std::string from = ""; - std::string to = ""; - std::string cc = ""; - std::string subject = ""; - std::string content_type = ""; - std::string content_transfer_encoding = ""; - std::string date = ""; - std::string message_id = ""; - std::string message_uid = ""; - std::string message_no = ""; - std::string boundary = ""; - std::string accept_language = ""; - std::string content_language = ""; - std::string char_set = ""; - bool multipart = false; - bool rfc822_part = false; - int rfc822Idx = 0; - std::string partNumStr = ""; - - esp_mail_imap_multipart_sub_type multipart_sub_type = - esp_mail_imap_multipart_sub_type_none; - esp_mail_imap_message_sub_type message_sub_type = - esp_mail_imap_message_sub_type_none; - std::string from_charset = ""; - std::string to_charset = ""; - std::string cc_charset = ""; - std::string subject_charset = ""; - std::string msgID = ""; - std::string error_msg = ""; - bool error = false; - std::vector part_headers = - std::vector(); - int attachment_count = 0; - int total_download_size = 0; - int downloaded_size = 0; - int total_attach_data_size = 0; - int downloaded_bytes = 0; - int message_data_count = 0; -}; - -/* Internal use */ -struct esp_mail_folder_info_t -{ - std::string name = ""; - std::string attributes = ""; - std::string delimiter = ""; -}; - -struct esp_mail_folder_info_item_t -{ - /* The name of folder */ - const char *name = ""; - - /* The attributes of folder */ - const char *attributes = ""; - - /* The delimeter of folder */ - const char *delimiter = ""; -}; - -struct esp_mail_sesson_cert_config_t -{ - /* The certificate data (base64 data) */ - const char *cert_data = ""; - - /* The certificate file (DER format) */ - const char *cert_file = ""; - - /* The storage type */ - esp_mail_file_storage_type cert_file_storage_type; -}; - -struct esp_mail_sesson_sever_config_t -{ - /* The hostName of the server */ - const char *host_name = ""; - /* The port on the server to connect to */ - uint16_t port = 0; -}; - -/* The log in credentials */ -struct esp_mail_sesson_login_config_t -{ - /* The user Email address to log in */ - const char *email = ""; - - /* The user password to log in */ - const char *password = ""; - - /* The OAuth2.0 access token to log in */ - const char *accessToken = ""; - - /* The user domain or ip of client */ - const char *user_domain = ""; -}; - -struct esp_mail_sesson_secure_config_t -{ - /* The option to send the command to start the TLS connection */ - bool startTLS = false; -}; - -struct esp_mail_session_config_t -{ - /* The server config */ - struct esp_mail_sesson_sever_config_t server; - - /* The log in config */ - struct esp_mail_sesson_login_config_t login; - - /* The secure config */ - struct esp_mail_sesson_secure_config_t secure; - - /* The certificate config */ - struct esp_mail_sesson_cert_config_t certificate; -}; - -struct esp_mail_imap_download_config_t -{ - /* To download the PLAIN text content of the message */ - bool text = false; - - /* To download the HTML content of the message */ - bool html = false; - - /* To download the attachments of the message */ - bool attachment = false; - - /* To download the inline image of the message */ - bool inlineImg = false; - - /* To download the rfc822 mesages in the message */ - bool rfc822 = false; - - /* To download the message header */ - bool header = false; -}; - -struct esp_mail_imap_enable_config_t -{ - /* To store the PLAIN text of the message in the IMAPSession */ - bool text = false; - - /* To store the HTML of the message in the IMAPSession */ - bool html = false; - - /* To store the rfc822 messages in the IMAPSession */ - bool rfc822 = false; - - /* To enable the download status via the serial port */ - bool download_status = false; - - /* To sort the message UID of the search result in descending order */ - bool recent_sort = false; -}; - -struct esp_mail_imap_limit_config_t -{ - /* The maximum messages from the search result */ - size_t search = 10; - - /** The maximum size of the memory buffer to store the message content. - * This is only limit for data to be stored in the IMAPSession. - */ - size_t msg_size = 1024; - - /* The maximum size of each attachment to download */ - size_t attachment_size = 1024 * 1024 * 5; -}; - -struct esp_mail_imap_storage_config_t -{ - /* The path to save the downloaded file */ - const char *saved_path = "/"; - - /** The type of file storages e.g. - * esp_mail_file_storage_type_none, - * esp_mail_file_storage_type_flash, and - * esp_mail_file_storage_type_sd - */ - esp_mail_file_storage_type type = esp_mail_file_storage_type_flash; -}; - -struct esp_mail_imap_search_config_t -{ - /* The search criteria */ - const char *criteria = ""; - - /* The option to search the unseen message */ - bool unseen_msg = false; -}; - -struct esp_mail_imap_fetch_config_t -{ - /* The UID of message to fetch */ - const char *uid = ""; - - /* Set the message flag as seen */ - bool set_seen = false; -}; - -struct esp_mail_imap_read_config_t -{ - /* The config for fetching */ - struct esp_mail_imap_fetch_config_t fetch; - - /* The config for search */ - struct esp_mail_imap_search_config_t search; - - /* The config about the limits */ - struct esp_mail_imap_limit_config_t limit; - - /* The config to enable the features */ - struct esp_mail_imap_enable_config_t enable; - - /* The config about downloads */ - struct esp_mail_imap_download_config_t download; - - /* The config about the storage and path to save the downloaded file */ - struct esp_mail_imap_storage_config_t storage; -}; - -/* Mail and MIME Header Fields */ -struct esp_mail_imap_msg_item_t -{ - /* The message number */ - const char *msgNo = ""; - - /* The message UID */ - const char *UID = ""; - - /* The message identifier (RFC 4021) */ - const char *ID = ""; - - /* The language(s) for auto-responses (RFC 4021) */ - const char *acceptLang = ""; - - /* The language of message content (RFC 4021) */ - const char *contentLang = ""; - - /* The mailbox of message author (RFC 4021) */ - const char *from = ""; - - /* The charset of the mailbox of message author */ - const char *fromCharset = ""; - - /* The primary recipient mailbox (RFC 4021) */ - const char *to = ""; - - /* The charset of the primary recipient mailbox */ - const char *toCharset = ""; - - /* The Carbon-copy recipient mailboxes (RFC 4021) */ - const char *cc = ""; - - /* The charset of the Carbon-copy recipient mailbox header */ - const char *ccCharset = ""; - - /* The message date and time (RFC 4021) */ - const char *date = ""; - - /* The topic of message (RFC 4021) */ - const char *subject = ""; - - /* The topic of message charset */ - const char *subjectCharset = ""; - - /* The PLAIN text content of the message */ - struct esp_mail_plain_body_t text; - - /* The HTML content of the message */ - struct esp_mail_html_body_t html; - - /* rfc822 related */ - - /* The sender Email */ - const char *sender; - - /* The message identifier */ - const char *messageID = ""; - - /* The keywords or phrases, separated by commas */ - const char *keyword = ""; - - /* The comment about message */ - const char *comment = ""; - - /* The return recipient of the message */ - const char *return_path = ""; - - /* The Email address to reply */ - const char *reply_to; - - /* The Blind carbon-copy recipients */ - const char *bcc = ""; - - /* The error description from fetching the message */ - const char *fetchError = ""; - - /* The info about the attachments in the message */ - std::vector attachments = - std::vector(); - - /* The info about the rfc822 messages included in the message */ - std::vector rfc822 = - std::vector(); -}; - -struct esp_mail_imap_msg_list_t -{ - /* The info of a message */ - std::vector msgItems = - std::vector(); -}; - -struct esp_mail_smtp_msg_type_t -{ - bool rfc822 = false; - int rfc822Idx = 0; -}; -struct esp_mail_imap_multipart_level_t -{ - uint8_t level = 0; - bool fetch_rfc822_header = false; - bool append_body_text = false; -}; - -/** The content transfer encoding - * enc_7bit or "7bit" - * enc_qp or "quoted-printable" - * enc_base64 or "base64" - * enc_binary or "binary" - * enc_8bit or "8bit" -*/ -typedef struct esp_mail_content_transfer_encoding_t Content_Transfer_Encoding; - -/* The result from sending the Email */ -typedef struct esp_mail_smtp_send_status_t SMTP_Result; - -/* The attachment item details for a message which returned from fetching the - * Email */ -typedef struct esp_mail_attacment_info_t IMAP_Attach_Item; - -/* The attachment details for sending the Email */ -typedef struct esp_mail_attachment_t SMTP_Attachment; - -/* The info of the selected or open mailbox folder e.g. name, attributes and - * delimiter */ -typedef struct esp_mail_folder_info_item_t FolderInfo; - -/* The session configuations */ -typedef struct esp_mail_session_config_t ESP_Mail_Session; - -/** The IMAP operation configuations */ -typedef struct esp_mail_imap_read_config_t IMAP_Config; - -/* The message item data of the IMAP_MSG_List which contains header, body and - * attachments info for eacch message*/ -typedef struct esp_mail_imap_msg_item_t IMAP_MSG_Item; - -/* The list that contains the message items from searching or fetching the Email - */ -typedef struct esp_mail_imap_msg_list_t IMAP_MSG_List; - -static const char esp_mail_str_1[] PROGMEM = - "Content-Type: multipart/mixed; boundary=\""; -static const char esp_mail_str_2[] PROGMEM = "$ CAPABILITY"; -static const char esp_mail_str_3[] PROGMEM = "Mime-Version: 1.0\r\n"; -static const char esp_mail_str_4[] PROGMEM = "AUTH LOGIN"; -static const char esp_mail_str_5[] PROGMEM = "HELO "; -static const char esp_mail_str_6[] PROGMEM = "EHLO "; -static const char esp_mail_str_7[] PROGMEM = "QUIT"; -static const char esp_mail_str_8[] PROGMEM = "MAIL FROM:"; -static const char esp_mail_str_9[] PROGMEM = "RCPT TO:"; -static const char esp_mail_str_10[] PROGMEM = "From: "; -static const char esp_mail_str_11[] PROGMEM = "To: "; -static const char esp_mail_str_12[] PROGMEM = "Cc: "; -static const char esp_mail_str_13[] PROGMEM = ",<"; -static const char esp_mail_str_14[] PROGMEM = "<"; -static const char esp_mail_str_15[] PROGMEM = ">"; -static const char esp_mail_str_16[] PROGMEM = "DATA"; -static const char esp_mail_str_17[] PROGMEM = "X-Priority: "; -static const char esp_mail_str_18[] PROGMEM = "X-MSMail-Priority: High\r\n"; -static const char esp_mail_str_19[] PROGMEM = "X-MSMail-Priority: Normal\r\n"; -static const char esp_mail_str_20[] PROGMEM = "X-MSMail-Priority: Low\r\n"; -static const char esp_mail_str_21[] PROGMEM = "Importance: High\r\n"; -static const char esp_mail_str_22[] PROGMEM = "Importance: Normal\r\n"; -static const char esp_mail_str_23[] PROGMEM = "Importance: Low\r\n"; -static const char esp_mail_str_24[] PROGMEM = "Subject: "; -static const char esp_mail_str_25[] PROGMEM = "Content-Type: "; -static const char esp_mail_str_26[] PROGMEM = "; Name=\""; -static const char esp_mail_str_27[] PROGMEM = "$"; -static const char esp_mail_str_28[] PROGMEM = - "Content-Type: multipart/parallel; boundary=\""; -static const char esp_mail_str_29[] PROGMEM = "7bit"; -static const char esp_mail_str_30[] PROGMEM = - "Content-Disposition: attachment; filename=\""; -static const char esp_mail_str_31[] PROGMEM = "base64"; -static const char esp_mail_str_32[] PROGMEM = "application/octet-stream"; -static const char esp_mail_str_33[] PROGMEM = "--"; -static const char esp_mail_str_34[] PROGMEM = "\r\n"; -static const char esp_mail_str_35[] PROGMEM = "\"\r\n\r\n"; -static const char esp_mail_str_36[] PROGMEM = "\"\r\n"; -static const char esp_mail_str_37[] PROGMEM = "\r\n.\r\n"; -static const char esp_mail_str_38[] PROGMEM = "unable to connect to server"; -static const char esp_mail_str_39[] PROGMEM = "SMTP server greeting failed"; -static const char esp_mail_str_40[] PROGMEM = ".dat"; -static const char esp_mail_str_41[] PROGMEM = "$ AUTHENTICATE PLAIN "; -static const char esp_mail_str_42[] PROGMEM = - "the provided SASL authentication mechanism is not support"; -static const char esp_mail_str_43[] PROGMEM = "authentication failed"; -static const char esp_mail_str_44[] PROGMEM = "mydomain.com"; -static const char esp_mail_str_45[] PROGMEM = "AUTH PLAIN"; -static const char esp_mail_str_46[] PROGMEM = "Return-Path: "; -static const char esp_mail_str_47[] PROGMEM = "login password is not valid"; -static const char esp_mail_str_48[] PROGMEM = "send header failed"; -static const char esp_mail_str_49[] PROGMEM = "send body failed"; -static const char esp_mail_str_50[] PROGMEM = "Connecting to IMAP server..."; -static const char esp_mail_str_51[] PROGMEM = - ".HEADER.FIELDS (SUBJECT FROM SENDER RETURN-PATH TO REPLY-TO DATE CC " - "Message-ID COMMENT KEYWORD content-type Content-transfer-encoding)]"; -static const char esp_mail_str_52[] PROGMEM = "failed"; -static const char esp_mail_str_53[] PROGMEM = "Error, "; -static const char esp_mail_str_54[] PROGMEM = "IMAP server connected"; -static const char esp_mail_str_55[] PROGMEM = "> C: download attachment"; -static const char esp_mail_str_56[] PROGMEM = "Logging in..."; -static const char esp_mail_str_57[] PROGMEM = "Downloading messages..."; -static const char esp_mail_str_58[] PROGMEM = - "Reading the list of mailboxes..."; -static const char esp_mail_str_59[] PROGMEM = - "> C: download plain TEXT message"; -static const char esp_mail_str_60[] PROGMEM = "> C: download HTML message"; -static const char esp_mail_str_61[] PROGMEM = "Selecting the "; -static const char esp_mail_str_62[] PROGMEM = "fail to list the mailboxes"; -static const char esp_mail_str_63[] PROGMEM = "fail to check the capabilities"; -static const char esp_mail_str_64[] PROGMEM = "Check the capability..."; -static const char esp_mail_str_65[] PROGMEM = "> C: check the capability"; -static const char esp_mail_str_66[] PROGMEM = "Searching messages..."; -static const char esp_mail_str_67[] PROGMEM = "message"; -static const char esp_mail_str_68[] PROGMEM = "Search limit:"; -static const char esp_mail_str_69[] PROGMEM = "Found "; -static const char esp_mail_str_70[] PROGMEM = " messages"; -static const char esp_mail_str_71[] PROGMEM = "Show "; -static const char esp_mail_str_72[] PROGMEM = - "No message found for search criteria"; -static const char esp_mail_str_73[] PROGMEM = - "Search criteria does not set, fetch the recent message"; -static const char esp_mail_str_74[] PROGMEM = "Fetching message "; -static const char esp_mail_str_75[] PROGMEM = ", UID: "; -static const char esp_mail_str_76[] PROGMEM = ", Number: "; -static const char esp_mail_str_77[] PROGMEM = "> C: fetch message header"; -static const char esp_mail_str_78[] PROGMEM = "Attachments ("; -static const char esp_mail_str_79[] PROGMEM = ")"; -static const char esp_mail_str_80[] PROGMEM = "Downloading attachments..."; -static const char esp_mail_str_81[] PROGMEM = "> C: fetch body part header, "; -static const char esp_mail_str_82[] PROGMEM = "rfc822"; -static const char esp_mail_str_83[] PROGMEM = "reading"; -static const char esp_mail_str_84[] PROGMEM = "Free Heap: "; -static const char esp_mail_str_85[] PROGMEM = "Logging out..."; -static const char esp_mail_str_86[] PROGMEM = - "> C: fetch body sub part header, "; -static const char esp_mail_str_87[] PROGMEM = "Finished reading Email"; -static const char esp_mail_str_88[] PROGMEM = "> C: finished reading Email"; -static const char esp_mail_str_89[] PROGMEM = "SD card mount failed"; -static const char esp_mail_str_90[] PROGMEM = "download"; -static const char esp_mail_str_91[] PROGMEM = ", "; -static const char esp_mail_str_92[] PROGMEM = "%"; -static const char esp_mail_str_93[] PROGMEM = "connection timeout"; -static const char esp_mail_str_94[] PROGMEM = ".html"; -static const char esp_mail_str_95[] PROGMEM = ".txt"; -static const char esp_mail_str_96[] PROGMEM = " folder..."; -static const char esp_mail_str_97[] PROGMEM = ";"; -static const char esp_mail_str_98[] PROGMEM = - "Content-Disposition: attachment\r\n"; -static const char esp_mail_str_99[] PROGMEM = "Date: "; -static const char esp_mail_str_100[] PROGMEM = "Messsage UID: "; -static const char esp_mail_str_101[] PROGMEM = "Messsage ID: "; -static const char esp_mail_str_102[] PROGMEM = "Accept Language: "; -static const char esp_mail_str_103[] PROGMEM = "Content Language: "; -static const char esp_mail_str_104[] PROGMEM = " BODY=BINARYMIME"; -static const char esp_mail_str_105[] PROGMEM = "From Charset: "; -static const char esp_mail_str_106[] PROGMEM = "BDAT "; -static const char esp_mail_str_107[] PROGMEM = "To Charset: "; -static const char esp_mail_str_108[] PROGMEM = "CC: "; -static const char esp_mail_str_109[] PROGMEM = "CC Charset: "; -static const char esp_mail_str_110[] PROGMEM = "delsp=\"no\""; -static const char esp_mail_str_111[] PROGMEM = "Subject Charset: "; -static const char esp_mail_str_112[] PROGMEM = "Message Charset: "; -static const char esp_mail_str_113[] PROGMEM = "Attachment: "; -static const char esp_mail_str_114[] PROGMEM = "File Index: "; -static const char esp_mail_str_115[] PROGMEM = "Filename: "; -static const char esp_mail_str_116[] PROGMEM = "Name: "; -static const char esp_mail_str_117[] PROGMEM = "Size: "; -static const char esp_mail_str_118[] PROGMEM = "Type: "; -static const char esp_mail_str_119[] PROGMEM = "Creation Date: "; -static const char esp_mail_str_120[] PROGMEM = "Connecting to SMTP server..."; -static const char esp_mail_str_121[] PROGMEM = - "SMTP server connected, wait for greeting..."; -static const char esp_mail_str_122[] PROGMEM = "Sending greeting response..."; -static const char esp_mail_str_123[] PROGMEM = "message/rfc822"; -static const char esp_mail_str_124[] PROGMEM = - "Saving message header to file..."; -static const char esp_mail_str_125[] PROGMEM = "Sending message header..."; -static const char esp_mail_str_126[] PROGMEM = "Sending message body..."; -static const char esp_mail_str_127[] PROGMEM = "Sending attachments..."; -static const char esp_mail_str_128[] PROGMEM = "Closing the session..."; -static const char esp_mail_str_129[] PROGMEM = "Message sent successfully"; -static const char esp_mail_str_130[] PROGMEM = "$ LOGIN "; -static const char esp_mail_str_131[] PROGMEM = " "; -static const char esp_mail_str_132[] PROGMEM = - "fail to set up the SSL/TLS structure"; -static const char esp_mail_str_133[] PROGMEM = "$ LIST \"\" *"; -static const char esp_mail_str_134[] PROGMEM = "Comment: "; -static const char esp_mail_str_135[] PROGMEM = "$ EXAMINE \""; -static const char esp_mail_str_136[] PROGMEM = "\""; -static const char esp_mail_str_137[] PROGMEM = "UID "; -static const char esp_mail_str_138[] PROGMEM = " UID"; -static const char esp_mail_str_139[] PROGMEM = " SEARCH"; -static const char esp_mail_str_140[] PROGMEM = "UID"; -static const char esp_mail_str_141[] PROGMEM = "SEARCH"; -static const char esp_mail_str_142[] PROGMEM = "$ UID FETCH "; -static const char esp_mail_str_143[] PROGMEM = "$ FETCH "; -static const char esp_mail_str_144[] PROGMEM = - "HEADER.FIELDS (SUBJECT FROM TO DATE CC Message-ID Accept-Language " - "content-type Content-transfer-encoding Content-Language)"; -static const char esp_mail_str_145[] PROGMEM = "Keyword: "; -static const char esp_mail_str_146[] PROGMEM = "$ LOGOUT"; -static const char esp_mail_str_147[] PROGMEM = " BODY"; -static const char esp_mail_str_148[] PROGMEM = ".MIME]"; -static const char esp_mail_str_149[] PROGMEM = "Bcc: "; -static const char esp_mail_str_150[] PROGMEM = "Sender: "; -static const char esp_mail_str_151[] PROGMEM = "no mailbox opened"; -static const char esp_mail_str_152[] PROGMEM = "."; -static const char esp_mail_str_153[] PROGMEM = "No mailbox opened"; -static const char esp_mail_str_154[] PROGMEM = "Remove FLAG"; -static const char esp_mail_str_155[] PROGMEM = "Add FLAG"; -static const char esp_mail_str_156[] PROGMEM = "]"; -static const char esp_mail_str_157[] PROGMEM = "Set FLAG"; -static const char esp_mail_str_158[] PROGMEM = - "file does not exist or can't access"; -static const char esp_mail_str_159[] PROGMEM = "msg.html"; -static const char esp_mail_str_160[] PROGMEM = "upload "; -static const char esp_mail_str_161[] PROGMEM = "/msg"; -static const char esp_mail_str_163[] PROGMEM = "/rfc822_msg"; -static const char esp_mail_str_164[] PROGMEM = "msg.txt"; -static const char esp_mail_str_165[] PROGMEM = "Content-Length: "; -static const char esp_mail_str_166[] PROGMEM = "binary"; -static const char esp_mail_str_167[] PROGMEM = "Sending inline data..."; -static const char esp_mail_str_168[] PROGMEM = "charset=\""; -static const char esp_mail_str_169[] PROGMEM = "charset="; -static const char esp_mail_str_170[] PROGMEM = "name=\""; -static const char esp_mail_str_171[] PROGMEM = "name="; -static const char esp_mail_str_172[] PROGMEM = "content-transfer-encoding: "; -static const char esp_mail_str_173[] PROGMEM = " LAST"; -static const char esp_mail_str_174[] PROGMEM = "content-description: "; -static const char esp_mail_str_175[] PROGMEM = "content-disposition: "; -static const char esp_mail_str_176[] PROGMEM = "filename=\""; -static const char esp_mail_str_177[] PROGMEM = "filename="; -static const char esp_mail_str_178[] PROGMEM = "size="; -static const char esp_mail_str_179[] PROGMEM = "creation-date=\""; -static const char esp_mail_str_180[] PROGMEM = "creation-date="; -static const char esp_mail_str_181[] PROGMEM = "modification-date=\""; -static const char esp_mail_str_182[] PROGMEM = "modification-date="; -static const char esp_mail_str_183[] PROGMEM = "*"; -static const char esp_mail_str_184[] PROGMEM = "Reply-To: "; -static const char esp_mail_str_185[] PROGMEM = "> E: "; -static const char esp_mail_str_186[] PROGMEM = "out of memory"; -static const char esp_mail_str_187[] PROGMEM = "Message fetch cmpleted"; -static const char esp_mail_str_188[] PROGMEM = "fail to close the mailbox"; -static const char esp_mail_str_189[] PROGMEM = "message-id: "; -static const char esp_mail_str_190[] PROGMEM = "accept-language: "; -static const char esp_mail_str_191[] PROGMEM = "content-language: "; -static const char esp_mail_str_192[] PROGMEM = ")"; -static const char esp_mail_str_193[] PROGMEM = "{"; -static const char esp_mail_str_194[] PROGMEM = "}"; -static const char esp_mail_str_195[] PROGMEM = "$ CLOSE\r\n"; -static const char esp_mail_str_196[] PROGMEM = "> C: send STARTTLS command"; -static const char esp_mail_str_197[] PROGMEM = "> C: close the mailbox folder"; -static const char esp_mail_str_198[] PROGMEM = "("; -static const char esp_mail_str_199[] PROGMEM = " EXISTS"; -static const char esp_mail_str_200[] PROGMEM = " [UIDNEXT "; -static const char esp_mail_str_201[] PROGMEM = "port > "; -static const char esp_mail_str_202[] PROGMEM = "/"; -static const char esp_mail_str_203[] PROGMEM = "/header.txt"; -static const char esp_mail_str_204[] PROGMEM = "/esp.32"; -static const char esp_mail_str_205[] PROGMEM = - "sender Email address is not valid"; -static const char esp_mail_str_206[] PROGMEM = - "some of the recipient Email address is not valid"; -static const char esp_mail_str_207[] PROGMEM = "> C: send Email"; -static const char esp_mail_str_208[] PROGMEM = "Sending Email..."; -static const char esp_mail_str_209[] PROGMEM = "Send command, STARTTLS"; -static const char esp_mail_str_210[] PROGMEM = "Closing the "; -static const char esp_mail_str_211[] PROGMEM = "host > "; -static const char esp_mail_str_212[] PROGMEM = "FLAGS"; -static const char esp_mail_str_213[] PROGMEM = "BODY"; -static const char esp_mail_str_214[] PROGMEM = "PEEK"; -static const char esp_mail_str_215[] PROGMEM = "TEXT"; -static const char esp_mail_str_216[] PROGMEM = "HEADER"; -static const char esp_mail_str_217[] PROGMEM = "FIELDS"; -static const char esp_mail_str_218[] PROGMEM = "["; -static const char esp_mail_str_219[] PROGMEM = "]"; -static const char esp_mail_str_220[] PROGMEM = "MIME"; -static const char esp_mail_str_221[] PROGMEM = "connection lost"; -static const char esp_mail_str_222[] PROGMEM = "set recipient failed"; -static const char esp_mail_str_223[] PROGMEM = " NEW"; -static const char esp_mail_str_224[] PROGMEM = "ALL"; -static const char esp_mail_str_225[] PROGMEM = "> C: connect to IMAP server"; -static const char esp_mail_str_226[] PROGMEM = "windows-874"; -static const char esp_mail_str_227[] PROGMEM = "iso-8859-1"; -static const char esp_mail_str_228[] PROGMEM = "> C: server connected"; -static const char esp_mail_str_229[] PROGMEM = "> C: send imap command, LOGIN"; -static const char esp_mail_str_230[] PROGMEM = "> C: send imap command, LIST"; -static const char esp_mail_str_231[] PROGMEM = "iso-8859-11"; -static const char esp_mail_str_232[] PROGMEM = "> C: search messages"; -static const char esp_mail_str_233[] PROGMEM = "> C: send imap command, FETCH"; -static const char esp_mail_str_234[] PROGMEM = "> C: send imap command, LOGOUT"; -static const char esp_mail_str_235[] PROGMEM = "> C: message fetch completed"; -static const char esp_mail_str_236[] PROGMEM = "> C: connect to SMTP server"; -static const char esp_mail_str_237[] PROGMEM = "tis-620"; -static const char esp_mail_str_238[] PROGMEM = "> C: smtp server connected"; -static const char esp_mail_str_239[] PROGMEM = "> C: send smtp command, HELO"; -static const char esp_mail_str_240[] PROGMEM = - "> C: send smtp command, AUTH LOGIN"; -static const char esp_mail_str_241[] PROGMEM = - "> C: send smtp command, AUTH PLAIN"; -static const char esp_mail_str_242[] PROGMEM = "> C: send message header"; -static const char esp_mail_str_243[] PROGMEM = "> C: send message body"; -static const char esp_mail_str_244[] PROGMEM = "> C: send attachments"; -static const char esp_mail_str_245[] PROGMEM = - "> C: terminate the SMTP session"; -static const char esp_mail_str_246[] PROGMEM = "> C: Message sent successfully"; -static const char esp_mail_str_247[] PROGMEM = "$ SELECT \""; -static const char esp_mail_str_248[] PROGMEM = "> C: open the mailbox folder"; -static const char esp_mail_str_249[] PROGMEM = "$ UID STORE "; -static const char esp_mail_str_250[] PROGMEM = " FLAGS ("; -static const char esp_mail_str_251[] PROGMEM = " +FLAGS ("; -static const char esp_mail_str_252[] PROGMEM = " -FLAGS ("; -static const char esp_mail_str_253[] PROGMEM = "> C: set FLAG"; -static const char esp_mail_str_254[] PROGMEM = "> C: add FLAG"; -static const char esp_mail_str_255[] PROGMEM = "> C: remove FLAG"; -static const char esp_mail_str_256[] PROGMEM = "could not parse flag"; -static const char esp_mail_str_257[] PROGMEM = "delsp=\"yes\""; -static const char esp_mail_str_258[] PROGMEM = "session timed out"; -static const char esp_mail_str_259[] PROGMEM = "delsp=yes"; -static const char esp_mail_str_260[] PROGMEM = "< S: "; -static const char esp_mail_str_261[] PROGMEM = "> C: "; -static const char esp_mail_str_262[] PROGMEM = " NOTIFY="; -static const char esp_mail_str_263[] PROGMEM = ","; -static const char esp_mail_str_264[] PROGMEM = "SUCCESS"; -static const char esp_mail_str_265[] PROGMEM = "FAILURE"; -static const char esp_mail_str_266[] PROGMEM = "DELAY"; -static const char esp_mail_str_267[] PROGMEM = "Sending next Email..."; -static const char esp_mail_str_268[] PROGMEM = "> C: send next Email"; -static const char esp_mail_str_269[] PROGMEM = - "header.fields (content-type Content-transfer-encoding)]"; -static const char esp_mail_str_270[] PROGMEM = "format=\"flowed\""; -static const char esp_mail_str_271[] PROGMEM = "> C: send inline data"; -static const char esp_mail_str_272[] PROGMEM = "Content-transfer-encoding: "; -static const char esp_mail_str_273[] PROGMEM = "Date: "; -static const char esp_mail_str_274[] PROGMEM = "Message-ID:"; -static const char esp_mail_str_275[] PROGMEM = "format=flowed"; -static const char esp_mail_str_276[] PROGMEM = "CC: "; -static const char esp_mail_str_277[] PROGMEM = "boundary=\""; -static const char esp_mail_str_278[] PROGMEM = "quoted-printable"; -static const char esp_mail_str_279[] PROGMEM = "Subject:"; -static const char esp_mail_str_280[] PROGMEM = "> C: no content"; -static const char esp_mail_str_281[] PROGMEM = "fail to open the mailbox"; -static const char esp_mail_str_282[] PROGMEM = "file I/O error"; -static const char esp_mail_str_283[] PROGMEM = "time.nist.gov"; -static const char esp_mail_str_284[] PROGMEM = - "log in was disabled for this server"; -static const char esp_mail_str_285[] PROGMEM = "user="; -static const char esp_mail_str_286[] PROGMEM = "\1auth=Bearer "; -static const char esp_mail_str_287[] PROGMEM = "\1\1"; -static const char esp_mail_str_288[] PROGMEM = - "> C: send smtp command, AUTH XOAUTH2"; -static const char esp_mail_str_289[] PROGMEM = "AUTH XOAUTH2 "; -static const char esp_mail_str_290[] PROGMEM = - "> C: send imap command, AUTHENTICATE PLAIN"; -static const char esp_mail_str_291[] PROGMEM = - "> C: send imap command, AUTH XOAUTH2"; -static const char esp_mail_str_292[] PROGMEM = "$ AUTHENTICATE XOAUTH2 "; -static const char esp_mail_str_293[] PROGMEM = - "OAuth2.0 log in was disabled for this server"; -static const char esp_mail_str_294[] PROGMEM = "{\"status\":"; -static const char esp_mail_str_295[] PROGMEM = "0123456789ABCDEF"; -static const char esp_mail_str_296[] PROGMEM = "pool.ntp.org"; -static const char esp_mail_str_297[] PROGMEM = - "Content-Type: multipart/alternative; boundary=\""; -static const char esp_mail_str_298[] PROGMEM = - "Content-Type: multipart/related; boundary=\""; -static const char esp_mail_str_299[] PROGMEM = - "Content-Disposition: inline; filename=\""; -static const char esp_mail_str_300[] PROGMEM = "Content-Location: "; -static const char esp_mail_str_301[] PROGMEM = "Content-ID: <"; -static const char esp_mail_str_302[] PROGMEM = "cid:"; -static const char esp_mail_str_303[] PROGMEM = - "Finishing the message sending..."; -static const char esp_mail_str_304[] PROGMEM = - "> C: Finish the message sending"; -static const char esp_mail_str_305[] PROGMEM = "connection failed"; -static const char esp_mail_str_306[] PROGMEM = - "some of the requested messages no longer exist"; -static const char esp_mail_str_307[] PROGMEM = "Reading messages..."; -static const char esp_mail_str_308[] PROGMEM = - "> C: reading plain TEXT message"; -static const char esp_mail_str_309[] PROGMEM = "> C: reading HTML message"; -static const char esp_mail_str_310[] PROGMEM = - "> C: performing the SSL/TLS handshake"; -static const char esp_mail_str_311[] PROGMEM = "STARTTLS\r\n"; -static const char esp_mail_str_312[] PROGMEM = "code: "; -static const char esp_mail_str_313[] PROGMEM = ", text: "; -static const char esp_mail_str_314[] PROGMEM = "> C: ESP Mail Client v"; -static const char esp_mail_str_315[] PROGMEM = " +FLAGS.SILENT (\\Deleted)"; -static const char esp_mail_str_316[] PROGMEM = "> C: delete message(s)"; -static const char esp_mail_str_317[] PROGMEM = "$ EXPUNGE"; -static const char esp_mail_str_318[] PROGMEM = "> C: copy message(s) to "; -static const char esp_mail_str_319[] PROGMEM = "$ UID COPY "; -static const char esp_mail_str_320[] PROGMEM = "> C: create folder"; -static const char esp_mail_str_321[] PROGMEM = "> C: delete folder"; -static const char esp_mail_str_322[] PROGMEM = "$ CREATE "; -static const char esp_mail_str_323[] PROGMEM = "$ DELETE "; -static const char esp_mail_str_324[] PROGMEM = "HEADER.FIELDS"; -static const char esp_mail_str_325[] PROGMEM = "flash content message"; -static const char esp_mail_str_326[] PROGMEM = "file content message"; -static const char esp_mail_str_327[] PROGMEM = "\"; size="; -static const char esp_mail_str_328[] PROGMEM = "0.0.0.0"; - -static const char esp_mail_smtp_response_1[] PROGMEM = "AUTH "; -static const char esp_mail_smtp_response_2[] PROGMEM = " LOGIN"; -static const char esp_mail_smtp_response_3[] PROGMEM = " PLAIN"; -static const char esp_mail_smtp_response_4[] PROGMEM = " XOAUTH2"; -static const char esp_mail_smtp_response_5[] PROGMEM = "STARTTLS"; -static const char esp_mail_smtp_response_6[] PROGMEM = "8BITMIME"; -static const char esp_mail_smtp_response_7[] PROGMEM = "BINARYMIME"; -static const char esp_mail_smtp_response_8[] PROGMEM = "CHUNKING"; -static const char esp_mail_smtp_response_9[] PROGMEM = "SMTPUTF8"; -static const char esp_mail_smtp_response_10[] PROGMEM = "PIPELINING"; -static const char esp_mail_smtp_response_11[] PROGMEM = " CRAM-MD5"; -static const char esp_mail_smtp_response_12[] PROGMEM = " DIGEST-MD5"; -static const char esp_mail_smtp_response_13[] PROGMEM = "DSN"; -// Tagged -static const char esp_mail_imap_response_1[] PROGMEM = "$ OK "; -static const char esp_mail_imap_response_2[] PROGMEM = "$ NO "; -static const char esp_mail_imap_response_3[] PROGMEM = "$ BAD "; -// Untagged -static const char esp_mail_imap_response_4[] PROGMEM = "* LIST "; -static const char esp_mail_imap_response_5[] PROGMEM = "* FLAGS "; -static const char esp_mail_imap_response_6[] PROGMEM = "* SEARCH "; -static const char esp_mail_imap_response_7[] PROGMEM = " FETCH "; -static const char esp_mail_imap_response_8[] PROGMEM = " NIL "; -static const char esp_mail_imap_response_9[] PROGMEM = " UID "; -static const char esp_mail_imap_response_10[] PROGMEM = "* CAPABILITY "; -static const char esp_mail_imap_response_11[] PROGMEM = "LOGINDISABLED"; -static const char esp_mail_imap_response_12[] PROGMEM = "AUTH=PLAIN"; -static const char esp_mail_imap_response_13[] PROGMEM = "AUTH=XOAUTH2"; -static const char esp_mail_imap_response_14[] PROGMEM = "STARTTLS"; -static const char esp_mail_imap_response_15[] PROGMEM = "CRAM-MD5"; -static const char esp_mail_imap_response_16[] PROGMEM = "DIGEST-MD5"; - -static const char imap_7bit_key1[] PROGMEM = "=20"; -static const char imap_7bit_val1[] PROGMEM = " "; -static const char imap_7bit_key2[] PROGMEM = "=2C"; -static const char imap_7bit_val2[] PROGMEM = ","; -static const char imap_7bit_key3[] PROGMEM = "=E2=80=99"; -static const char imap_7bit_val3[] PROGMEM = "'"; -static const char imap_7bit_key4[] PROGMEM = "=0A"; -static const char imap_7bit_val4[] PROGMEM = "\r\n"; -static const char imap_7bit_key5[] PROGMEM = "=0D"; -static const char imap_7bit_val5[] PROGMEM = "\r\n"; -static const char imap_7bit_key6[] PROGMEM = "=A0"; -static const char imap_7bit_val6[] PROGMEM = " "; -static const char imap_7bit_key7[] PROGMEM = "=B9"; -static const char imap_7bit_val7[] PROGMEM = "$sup1"; -static const char imap_7bit_key8[] PROGMEM = "=C2=A0"; -static const char imap_7bit_val8[] PROGMEM = " "; -static const char imap_7bit_key9[] PROGMEM = "=\r\n"; -static const char imap_7bit_val9[] PROGMEM = ""; -static const char imap_7bit_key10[] PROGMEM = "=E2=80=A6"; -static const char imap_7bit_val10[] PROGMEM = "…"; -static const char imap_7bit_key11[] PROGMEM = "=E2=80=A2"; -static const char imap_7bit_val11[] PROGMEM = "•"; -static const char imap_7bit_key12[] PROGMEM = "=E2=80=93"; -static const char imap_7bit_val12[] PROGMEM = "–"; -static const char imap_7bit_key13[] PROGMEM = "=E2=80=94"; -static const char imap_7bit_val13[] PROGMEM = "—"; - -static const unsigned char b64_index_table[65] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char boundary_table[] PROGMEM = - "=_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -__attribute__((used)) static bool compFunc(uint32_t i, uint32_t j) -{ - return (i > j); -} - -class MessageList -{ -public: - friend class IMAPSession; - MessageList(){}; - ~MessageList() { clear(); }; - void add(int uid) - { - if (uid > 0) - _list.push_back(uid); - } - - void clear() { _list.clear(); } - -private: - std::vector _list = std::vector(); -}; - -/* The class that provides the info of selected or opened mailbox folder */ -class SelectedFolderInfo -{ -public: - friend class ESP_Mail_Client; - friend class IMAPSession; - SelectedFolderInfo(){}; - ~SelectedFolderInfo() { clear(); }; - - /* Get the flags count for this mailbox */ - size_t flagCount() { return _flags.size(); }; - - /* Get the numbers of messages in this mailbox */ - size_t msgCount() { return _msgCount; }; - - /* Get the predict next message UID */ - size_t nextUID() { return _nextUID; }; - - /* Get the numbers of messages from search result based on the search criteria - */ - size_t searchCount() { return _searchCount; }; - - /* Get the numbers of messages to be stored in the ressult */ - size_t availableMessages() { return _availableItems; }; - - /* Get the flag argument at the specified index */ - String flag(size_t index) - { - if (index < _flags.size()) - return _flags[index].c_str(); - return ""; - } - -private: - void addFlag(const char *flag) { _flags.push_back(flag); }; - void clear() - { - for (size_t i = 0; i < _flags.size(); i++) - std::string().swap(_flags[i]); - _flags.clear(); - } - size_t _msgCount = 0; - size_t _nextUID = 0; - size_t _searchCount = 0; - size_t _availableItems = 0; - std::vector _flags = std::vector(); -}; - -/* The class that provides the list of FolderInfo e.g. name, attributes and - * delimiter */ -class FoldersCollection -{ -public: - friend class ESP_Mail_Client; - friend class IMAPSession; - FoldersCollection(){}; - ~FoldersCollection() { clear(); }; - size_t size() { return _folders.size(); }; - - struct esp_mail_folder_info_item_t info(size_t index) - { - struct esp_mail_folder_info_item_t fd; - if (index < _folders.size()) - { - fd.name = _folders[index].name.c_str(); - fd.attributes = _folders[index].attributes.c_str(); - fd.delimiter = _folders[index].delimiter.c_str(); - } - return fd; - } - -private: - void add(struct esp_mail_folder_info_t &fd) { _folders.push_back(fd); }; - void clear() - { - for (size_t i = 0; i < _folders.size(); i++) - { - if (_folders[i].name.length() > 0) - std::string().swap(_folders[i].name); - if (_folders[i].attributes.length() > 0) - std::string().swap(_folders[i].attributes); - if (_folders[i].delimiter.length() > 0) - std::string().swap(_folders[i].delimiter); - } - _folders.clear(); - } - std::vector _folders = - std::vector(); -}; - -/* The class that provides the status of message feching and searching */ -class IMAP_Status -{ -public: - IMAP_Status(); - ~IMAP_Status(); - const char *info(); - bool success(); - void empty(); - friend class IMAPSession; - - std::string _info = ""; - bool _success = false; -}; - -/* The SMTP message class */ -class SMTP_Message -{ -public: - SMTP_Message(){}; - ~SMTP_Message() { clear(); }; - - void resetAttachItem(SMTP_Attachment &att) - { - att.blob.size = 0; - att.blob.data = nullptr; - att.file.path = ""; - att.file.storage_type = esp_mail_file_storage_type_none; - att.descr.name = ""; - att.descr.filename = ""; - att.descr.transfer_encoding = ""; - att.descr.content_encoding = ""; - att.descr.mime = ""; - att.descr.content_id = ""; - att._int.att_type = esp_mail_att_type_none; - att._int.index = 0; - att._int.msg_uid = 0; - att._int.flash_blob = false; - att._int.binary = false; - att._int.parallel = false; - att._int.cid = ""; - } - - void clear() - { - sender.name = ""; - sender.email = ""; - subject = ""; - text.charSet = ""; - text.content = ""; - text.content_type = ""; - text.embed.enable = false; - html.charSet = ""; - html.content = ""; - html.content_type = ""; - html.embed.enable = false; - response.reply_to = ""; - response.notify = esp_mail_smtp_notify::esp_mail_smtp_notify_never; - priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - for (size_t i = 0; i < _rcp.size(); i++) - { - _rcp[i].name = ""; - _rcp[i].email = ""; - } - - for (size_t i = 0; i < _cc.size(); i++) - _cc[i].email = ""; - - for (size_t i = 0; i < _bcc.size(); i++) - _bcc[i].email = ""; - - for (size_t i = 0; i < _hdr.size(); i++) - _hdr[i] = ""; - - for (size_t i = 0; i < _att.size(); i++) - { - _att[i].descr.filename = ""; - _att[i].blob.data = nullptr; - _att[i].descr.mime = ""; - _att[i].descr.name = ""; - _att[i].blob.size = 0; - _att[i].descr.transfer_encoding = ""; - _att[i].file.path = ""; - _att[i].file.storage_type = esp_mail_file_storage_type_none; - } - - for (size_t i = 0; i < _parallel.size(); i++) - { - _parallel[i].descr.filename = ""; - _parallel[i].blob.data = nullptr; - _parallel[i].descr.mime = ""; - _parallel[i].descr.name = ""; - _parallel[i].blob.size = 0; - _parallel[i].descr.transfer_encoding = ""; - _parallel[i].file.path = ""; - _parallel[i].file.storage_type = esp_mail_file_storage_type_none; - } - _rcp.clear(); - _cc.clear(); - _bcc.clear(); - _hdr.clear(); - _att.clear(); - _parallel.clear(); - } - - /** Clear all the inline images - */ - void clearInlineimages() - { - for (int i = (int)_att.size() - 1; i >= 0; i--) - { - if (_att[i]._int.att_type == esp_mail_att_type_inline) - _att.erase(_att.begin() + i); - } - }; - - /* Clear all the attachments */ - void clearAttachments() - { - for (int i = (int)_att.size() - 1; i >= 0; i--) - { - if (_att[i]._int.att_type == esp_mail_att_type_attachment) - _att.erase(_att.begin() + i); - } - - for (int i = (int)_parallel.size() - 1; i >= 0; i--) - _parallel.erase(_parallel.begin() + i); - }; - - /** Clear all rfc822 message attachment - */ - void clearRFC822Messages() - { - for (int i = (int)_rfc822.size() - 1; i >= 0; i--) - { - _rfc822[i].clear(); - _rfc822.erase(_rfc822.begin() + i); - } - }; - - /** Clear the primary recipient mailboxes - */ - void clearRecipients() { _rcp.clear(); }; - - /** Clear the Carbon-copy recipient mailboxes - */ - void clearCc() { _cc.clear(); }; - - /** Clear the Blind-carbon-copy recipient mailboxes - */ - void clearBcc() { _bcc.clear(); }; - - /** Clear the custom message headers - */ - void clearHeader() { _hdr.clear(); }; - - /** Add attachment to the message - * - * @param att The SMTP_Attachment data item - */ - void addAttachment(SMTP_Attachment &att) - { - att._int.att_type = esp_mail_att_type_attachment; - att._int.parallel = false; - att._int.flash_blob = true; - _att.push_back(att); - }; - - /** Add parallel attachment to the message - * - * @param att The SMTP_Attachment data item - */ - void addParallelAttachment(SMTP_Attachment &att) - { - att._int.att_type = esp_mail_att_type_attachment; - att._int.parallel = true; - att._int.flash_blob = true; - _parallel.push_back(att); - }; - - /** Add inline image to the message - * - * @param att The SMTP_Attachment data item - */ - void addInlineImage(SMTP_Attachment &att) - { - att._int.flash_blob = true; - att._int.parallel = false; - att._int.att_type = esp_mail_att_type_inline; - char *tmp = new char[36]; - memset(tmp, 0, 36); - itoa(random(10000000, 20000000), tmp, 10); - att._int.cid = tmp; - delete[] tmp; - _att.push_back(att); - }; - - /** Add rfc822 message to the message - * - * @param msg The RFC822_Message class object - */ - void addMessage(SMTP_Message &msg) { _rfc822.push_back(msg); } - - /** Add the primary recipient mailbox to the message - * - * @param name The name of primary recipient - * @param email The Email address of primary recipient - */ - void addRecipient(const char *name, const char *email) - { - struct esp_mail_smtp_recipient_t rcp; - rcp.name = name; - rcp.email = email; - _rcp.push_back(rcp); - }; - - /** Add Carbon-copy recipient mailbox - * - * @param email The Email address of the secondary recipient - */ - void addCc(const char *email) - { - struct esp_mail_smtp_recipient_address_t cc; - cc.email = email; - _cc.push_back(cc); - }; - - /** Add Blind-carbon-copy recipient mailbox - * - * @param email The Email address of the tertiary recipient - */ - void addBcc(const char *email) - { - struct esp_mail_smtp_recipient_address_t bcc; - bcc.email = email; - _bcc.push_back(bcc); - }; - - /** Add the custom header to the message - * - * @param hdr The header name and value - */ - void addHeader(const char *hdr) { _hdr.push_back(hdr); }; - - /* The message author config */ - struct esp_mail_email_info_t sender; - - /* The topic of message */ - const char *subject = ""; - - /* The message type */ - byte type = esp_mail_msg_type_none; - - /* The PLAIN text message */ - struct esp_mail_plain_body_t text; - - /* The HTML text message */ - struct esp_mail_html_body_t html; - - /* The response config */ - struct esp_mail_smtp_msg_response_t response; - - /* The priority of the message */ - esp_mail_smtp_priority priority = - esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - - /* The enable options */ - struct esp_mail_smtp_enable_option_t enable; - - /* The message from config */ - struct esp_mail_email_info_t from; - - /* The message identifier */ - const char *messageID = ""; - - /* The keywords or phrases, separated by commas */ - const char *keyword = ""; - - /* The comment about message */ - const char *comment = ""; - - /* The date of message */ - const char *date = ""; - - /* The return recipient of the message */ - const char *return_path = ""; - -private: - friend class ESP_Mail_Client; - std::vector _rcp = - std::vector(); - std::vector _cc = - std::vector(); - std::vector _bcc = - std::vector(); - std::vector _hdr = std::vector(); - std::vector _att = std::vector(); - std::vector _parallel = std::vector(); - std::vector _rfc822 = std::vector(); -}; - -class SMTP_Status -{ -public: - friend class SMTPSession; - friend class ESP_Mail_Client; - - SMTP_Status(); - ~SMTP_Status(); - const char *info(); - bool success(); - void empty(); - size_t completedCount(); - size_t failedCount(); - -private: - std::string _info = ""; - bool _success = false; - size_t _sentSuccess = 0; - size_t _sentFailed = 0; -}; - -typedef void (*imapStatusCallback)(IMAP_Status); -typedef void (*smtpStatusCallback)(SMTP_Status); - -class ESP_Mail_Client -{ - -public: - /** Sending Email through the SMTP server - * - * @param smtp The pointer to SMTP session object which holds the data and the - * TCP client. - * @param msg The pointer to SMTP_Message class which contains the header, - * body, and attachments. - * @param closeSession The option to Close the SMTP session after sent. - * @return The boolean value indicates the success of operation. - */ - bool sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession = true); - - /** Reading Email through IMAP server. - * - * @param imap The pointer to IMAP sesssion object which holds the data and - the TCP client. - - * @param closeSession The option to close the IMAP session after fetching or - searching the Email. - * @return The boolean value indicates the success of operation. - */ - bool readMail(IMAPSession *imap, bool closeSession = true); - - /** Set the argument to the Flags for the specified message. - * - * @param imap The pointer to IMAP session object which holds the data and the - * TCP client. - * @param msgUID The UID of the message. - * @param flags The flag list to set. - * @param closeSession The option to close the IMAP session after set flag. - * @return The boolean value indicates the success of operation. - */ - bool setFlag(IMAPSession *imap, int msgUID, const char *flags, - bool closeSession); - - /** Add the argument to the Flags for the specified message. - * - * @param imap The pointer to IMAP session object which holds the data and the - * TCP client. - * @param msgUID The UID of the message. - * @param flags The flag list to set. - * @param closeSession The option to close the IMAP session after add flag. - * @return The boolean value indicates the success of operation. - */ - bool addFlag(IMAPSession *imap, int msgUID, const char *flags, - bool closeSession); - - /** Remove the argument from the Flags for the specified message. - * - * @param imap The pointer to IMAP session object which holds the data and the - * TCP client. - * @param msgUID The UID of the message that flags to be removed. - * @param flags The flag list to remove. - * @param closeSession The option to close the IMAP session after remove flag. - * @return The boolean value indicates the success of operation. - */ - bool removeFlag(IMAPSession *imap, int msgUID, const char *flags, - bool closeSession); - - /** Initialize the SD card with the SPI port. - * - * @param sck The SPI Clock pin (ESP32 only). - * @param miso The SPI MISO pin (ESSP32 only). - * @param mosi The SPI MOSI pin (ESP32 only). - * @param ss The SPI Chip/Slave Select pin (ESP32 and ESP8266). - * @return The boolean value indicates the success of operation. - */ - bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); - - /** Initialize the SD card with the default SPI port. - * - * @return The boolean value which indicates the success of operation. - */ - bool sdBegin(); - - /** Initialize the SD_MMC card (ESSP32 only). - * - * @param mountpoint The mounting point. - * @param mode1bit Allow 1 bit data line. - * @param format_if_mount_failed Format SD_MMC card if mount failed. - * @return The boolean value indicates the success of operation. - */ - bool sdMMCBegin(const char *mountpoint = "/sdcard", bool mode1bit = false, bool format_if_mount_failed = false); - - - ESPTimeHelper Time; - -private: - friend class SMTPSession; - friend class IMAPSession; -#if defined(ESP8266) - void setClock(float offset); -#endif - - RFC2047_Decoder RFC2047Decoder; - File file; - - bool _sdOk = false; - bool _flashOk = false; - bool _sdConfigSet = false; - uint8_t _sck, _miso, _mosi, _ss; - const char *sd_mmc_mountpoint = ""; - bool sd_mmc_mode1bit = false; - bool sd_mmc_format_if_mount_failed = false; - -#if defined(ESP8266) - bool _clockReady = false; - uint8_t _sdPin = SD_CS_PIN; - float _gmtOffset = 0.0; -#endif - - unsigned long _lastReconnectMillis = 0; - uint16_t _reconnectTimeout = ESP_MAIL_WIFI_RECONNECT_TIMEOUT; - - bool _sendMail(SMTPSession *smtp, SMTP_Message *msg, - bool closeSession = true); - bool ethLinkUp(); - bool reconnect(SMTPSession *smtp, unsigned long dataTime = 0); - bool reconnect(IMAPSession *imap, unsigned long dataTime = 0, - bool downloadRequestuest = false); - void closeTCP(SMTPSession *smtp); - void closeTCP(IMAPSession *imap); - void getMIME(const char *ext, std::string &mime); - void mimeFromFile(const char *name, std::string &mime); -#if defined(ESP32) - void setSecure(ESP_Mail_HTTPClient32 &httpClient, ESP_Mail_Session *session, - std::shared_ptr caCert); -#elif defined(ESP8266) - void setSecure(ESP_Mail_HTTPClient &httpClient, ESP_Mail_Session *session, - std::shared_ptr caCert); -#endif - void delS(char *p); - char *newS(size_t len); - char *newS(char *p, size_t len); - char *newS(char *p, size_t len, char *d); - bool strcmpP(const char *buf, int ofs, PGM_P beginH); - int strposP(const char *buf, PGM_P beginH, int ofs); - char *strP(PGM_P pgm); - void appendP(std::string &buf, PGM_P p, bool empty); - char *intStr(int value); - void errorStatusCB(SMTPSession *smtp, int error); - size_t smtpSendP(SMTPSession *smtp, PGM_P v, bool newline = false); - size_t smtpSend(SMTPSession *smtp, const char *data, bool newline = false); - size_t smtpSend(SMTPSession *smtp, int data, bool newline = false); - size_t smtpSend(SMTPSession *smtp, uint8_t *data, size_t size); - bool getMultipartFechCmd(IMAPSession *imap, int msgIdx, - std::string &partText); - bool multipartMember(const std::string &part, const std::string &check); - bool fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx); - bool connected(IMAPSession *imap); - bool imapAuth(IMAPSession *imap); - bool sendIMAPCommand(IMAPSession *imap, int msgIndex, int cmdCase); - bool handleSMTPError(SMTPSession *smtp, int err, bool ret = false); - void errorStatusCB(IMAPSession *imap, int error); - size_t imapSendP(IMAPSession *imap, PGM_P v, bool newline = false); - size_t imapSend(IMAPSession *imap, const char *data, bool nwline = false); - size_t imapSend(IMAPSession *imap, int data, bool newline = false); - std::string getBoundary(size_t len); - std::string getEncodedToken(IMAPSession *imap); - bool imapLogout(IMAPSession *imap); - bool sendParallelAttachments(SMTPSession *smtp, SMTP_Message *msg, - const std::string &boundary); - bool sendAttachments(SMTPSession *smtp, SMTP_Message *msg, - const std::string &boundary, bool parallel = false); - - bool sendMSGData(SMTPSession *smtp, SMTP_Message *msg, bool closeSession, - bool rfc822MSG); - bool sendRFC822Msg(SMTPSession *smtp, SMTP_Message *msg, - const std::string &boundary, bool closeSession, - bool rfc822MSG); - void getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg, - std::string &buf); - bool bdat(SMTPSession *smtp, SMTP_Message *msg, int len, bool last); - void checkBinaryData(SMTPSession *smtp, SMTP_Message *msg); - bool sendBlob(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att); - bool sendFile(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, - File &file); - bool openFileRead(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, - File &file, std::string &s, std::string &buf, - const std::string &boundary, bool inlined); - bool openFileRead2(SMTPSession *smtp, SMTP_Message *msg, File &file, - const char *path, esp_mail_file_storage_type storageType); - bool sendInline(SMTPSession *smtp, SMTP_Message *msg, - const std::string &boundary, byte type); - void debugInfoP(PGM_P info); - size_t numAtt(SMTPSession *smtp, esp_mail_attach_type type, - SMTP_Message *msg); - bool validEmail(const char *s); - bool checkEmail(SMTPSession *smtp, SMTP_Message *msg); - bool sendPartText(SMTPSession *smtp, SMTP_Message *msg, byte type, - const char *boundary); - char *getUID(); - bool sendBlobBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type); - bool sendFileBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type); - void encodingText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, - std::string &content); - void splitTk(std::string &str, std::vector &tk, - const char *delim); - void formatFlowedText(std::string &content); - void softBreak(std::string &content, const char *quoteMarks); - bool sendMSG(SMTPSession *smtp, SMTP_Message *msg, - const std::string &boundary); - void getAttachHeader(std::string &header, const std::string &boundary, - SMTP_Attachment *attach, size_t size); - void getRFC822PartHeader(SMTPSession *smtp, std::string &header, - const std::string &boundary); - void getInlineHeader(std::string &header, const std::string &boundary, - SMTP_Attachment *inlineAttach, size_t size); - unsigned char *decodeBase64(const unsigned char *src, size_t len, - size_t *out_len); - std::string encodeBase64Str(const unsigned char *src, size_t len); - std::string encodeBase64Str(uint8_t *src, size_t len); - void encodeQP(const char *buf, char *out); - bool sendBase64(SMTPSession *smtp, SMTP_Message *msg, - const unsigned char *data, size_t len, bool flashMem, - const char *filename, bool report); - bool sendBase64Stream(SMTPSession *smtp, SMTP_Message *msg, File file, - const char *filename, bool report); - void smtpCBP(SMTPSession *smtp, PGM_P info, bool success = false); - void smtpCB(SMTPSession *smtp, const char *info, bool success = false); - void imapCBP(IMAPSession *imap, PGM_P info, bool success); - void imapCB(IMAPSession *imap, const char *info, bool success); - int readLine(WiFiClient *stream, char *buf, int bufLen, bool crlf, - int &count); -#if defined(ESP32) - int _readLine(ESP_Mail_WCS32 *stream, char *buf, int bufLen, bool crlf, - int &count); -#elif defined(ESP8266) - int _readLine(ESP_Mail::ESP_Mail_WCS *stream, char *buf, int bufLen, - bool crlf, int &count); -#endif - int getMSGNUM(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, - bool &endSearch, int &nump, const char *key, const char *pc); - void handleHeader(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, - struct esp_mail_message_header_t &header, int &headerState, - int &octetCount); - void setHeader(IMAPSession *imap, char *buf, - struct esp_mail_message_header_t &header, int state); - void handlePartHeader(IMAPSession *imap, char *buf, int &chunkIdx, - struct esp_mail_message_part_info_t &part); - char *subStr(const char *buf, PGM_P beginH, PGM_P endH, int beginPos, - int endPos = 0); - struct esp_mail_message_part_info_t *cPart(IMAPSession *imap); - struct esp_mail_message_header_t *cHeader(IMAPSession *imap); - void strcat_c(char *str, char c); - int strpos(const char *haystack, const char *needle, int offset); - char *stristr(const char *str1, const char *str2); - char *rstrstr(const char *haystack, const char *needle); - int rstrpos(const char *haystack, const char *needle, int offset); - void getResponseStatus(const char *buf, esp_mail_smtp_status_code respCode, - int beginPos, - struct esp_mail_smtp_response_status_t &status); - void handleAuth(SMTPSession *smtp, char *buf); - std::string getEncodedToken(SMTPSession *smtp); - bool connected(SMTPSession *smtp); - bool setSendingResult(SMTPSession *smtp, SMTP_Message *msg, bool result); - bool smtpAuth(SMTPSession *smtp); - int available(SMTPSession *smtp); - bool handleSMTPResponse(SMTPSession *smtp, esp_mail_smtp_status_code respCode, - int errCode); - int available(IMAPSession *imap); - bool handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession); - void downloadReport(IMAPSession *imap, int progress); - void fetchReport(IMAPSession *imap, int progress, bool html); - void searchReport(int progress, const char *percent); - void uploadReport(const char *filename, int progress); - int cMSG(IMAPSession *imap); - int cIdx(IMAPSession *imap); - esp_mail_imap_response_status imapResponseStatus(IMAPSession *imap, - char *response); - void saveHeader(IMAPSession *imap); - esp_mail_char_decoding_scheme getEncodingFromCharset(const char *enc); - void decodeHeader(std::string &headerField, std::string &headerEnc); - bool handleAttachment(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, - File &file, std::string &filePath, - bool &downloadRequest, int &octetCount, - int &octetLength, int &oCount, int &reportState, - int &downloadCount); - int decodeLatin1_UTF8(unsigned char *out, int *outlen, - const unsigned char *in, int *inlen); - void decodeTIS620_UTF8(char *out, const char *in, size_t len); - void decodeText(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, - File &file, std::string &filePath, bool &downloadRequest, - int &octetLength, int &readDataLen, int &readCount); - void prepareFilePath(IMAPSession *imap, std::string &filePath, bool header); - int decodeChar(const char *s); - void decodeQP(const char *buf, char *out); - char *decode7Bit(char *buf); - char *strReplace(char *orig, char *rep, char *with); - char *strReplaceP(char *buf, PGM_P key, PGM_P value); - bool authFailed(char *buf, int bufLen, int &chunkIdx, int ofs); - void handleFolders(IMAPSession *imap, char *buf); - void handleCapability(IMAPSession *imap, char *buf, int &chunkIdx); - void handleExamine(IMAPSession *imap, char *buf); - bool handleIMAPError(IMAPSession *imap, int err, bool ret); - bool _setFlag(IMAPSession *imap, int msgUID, const char *flags, - uint8_t action, bool closeSession); - void createDirs(std::string dirs); - bool sdTest(); -}; - -class IMAPSession -{ -public: - IMAPSession(); - ~IMAPSession(); - - /** Begin the IMAP server connection. - * - * @param session The pointer to ESP_Mail_Session structured data that keeps - * the server and log in details. - * @param config The pointer to IMAP_Config structured data that keeps the - * operation options. - * @return The boolean value which indicates the success of operation. - */ - bool connect(ESP_Mail_Session *session, IMAP_Config *config); - - /** Close the IMAP session. - * - * @return The boolean value which indicates the success of operation. - */ - bool closeSession(); - - /** Set to enable the debug. - * - * @param level The level to enable the debug message - * level = 0, no debug - * level = 1, basic debug - * level = 2, full debug 1 - * level = 333, full debug 2 - */ - void debug(int level); - - /** Get the list of all the mailbox folders since the TCP session was opened - * and user was authenticated. - * - * @param folders The FoldersCollection class that contains the collection of - * the - * FolderInfo structured data. - * @return The boolean value which indicates the success of operation. - */ - bool getFolders(FoldersCollection &folders); - - /** Select or open the mailbox folder to search or fetch the message inside. - * - * @param folderName The known mailbox folder name. The default name is INBOX. - * @param readOnly The option to open the mailbox for read only. Set this - * option to false when you wish - * to modify the Flags using the setFlag, addFlag and removeFlag functions. - * @return The boolean value which indicates the success of operation. - */ - bool selectFolder(const char *folderName, bool readOnly = true); - - /** Open the mailbox folder to read or search the mesages. - * - * @param folderName The name of known mailbox folder to be opened. - * @param readOnly The option to open the mailbox for reading only. Set this - * option to false when you wish - * to modify the flags using the setFlag, addFlag and removeFlag functions. - * @return The boolean value which indicates the success of operation. - */ - bool openFolder(const char *folderName, bool readOnly = true); - - /** Close the mailbox folder that was opened. - * - * @param folderName The known mailbox folder name. - * @return The boolean value which indicates the success of operation. - */ - bool closeFolder(const char *folderName); - - /** Create folder. - * - * @param folderName The name of folder to create. - * @return The boolean value which indicates the success of operation. - */ - bool createFolder(const char *folderName); - - /** Delete folder. - * - * @param folderName The name of folder to delete. - * @return The boolean value which indicates the success of operation. - */ - bool deleteFolder(const char *folderName); - - /** Copy the messages to the defined mailbox folder. - * - * @param toCopy The pointer to the MessageListList class that contains the - * list of messages to copy. - * @param dest The destination folder that the messages to copy to. - * @return The boolean value which indicates the success of operation. - */ - bool copyMessages(MessageList *toCopy, const char *dest); - - /** Delete the messages in the opened mailbox folder. - * - * @param toDelete The pointer to the MessageListList class that contains the - * list of messages to delete. - * @param expunge The boolean option to expunge all messages. - * @return The boolean value which indicates the success of operation. - */ - bool deleteMessages(MessageList *toDelete, bool expunge = false); - - /** Assign the callback function that returns the operating status when - * fetching or reading the Email. - * - * @param imapCallback The function that accepts the imapStatusCallback as - * parameter. - */ - void callback(imapStatusCallback imapCallback); - - /** Determine if no message body contained in the search result and only the - * message header is available. - */ - bool headerOnly(); - - /** Get the message list from search or fetch the Emails - * - * @return The IMAP_MSG_List structured data which contains text and html - * contents, - * attachments, inline images, embedded rfc822 messages details for each - * message. - */ - IMAP_MSG_List data(); - - /** Get the details of the selected or opned mailbox folder - * - * @return The SelectedFolderInfo class which contains the info about flags, - * total messages, next UID, - * search count and the available messages count. - */ - SelectedFolderInfo selectedFolder(); - - /** Get the error details when readingg the Emails - * - * @return The string of error details. - */ - String errorReason(); - - /** Clear all the cache data stored in the IMAP session object. - */ - void empty(); - - friend class ESP_Mail_Client; - friend class foldderList; - -private: - void clearMessageData(); - void checkUID(); - void checkPath(); - void getMessages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg); - void getRFC822Messages(uint16_t messageIndex, - struct esp_mail_imap_msg_item_t &msg); - bool closeMailbox(); - bool openMailbox(const char *folder, esp_mail_imap_auth_mode mode, - bool waitResponse); - bool getMailboxes(FoldersCollection &flders); - bool checkCapability(); - - bool _tcpConnected = false; - struct esp_mail_imap_response_status_t _imapStatus; - int _cMsgIdx = 0; - int _cPartIdx = 0; - int _totalRead = 0; - std::vector _headers = - std::vector(); - - esp_mail_imap_command _imap_cmd = - esp_mail_imap_command::esp_mail_imap_cmd_login; - // std::string _partNumStr = ""; - std::vector _multipart_levels = - std::vector(); - int _rfc822_part_count = 0; - esp_mail_file_storage_type _storageType = - esp_mail_file_storage_type::esp_mail_file_storage_type_flash; - bool _unseen = false; - bool _readOnlyMode = true; - struct esp_mail_auth_capability_t _auth_capability; - ESP_Mail_Session *_sesson_cfg; - std::string _currentFolder = ""; - bool _mailboxOpened = false; - std::string _nextUID = ""; - - struct esp_mail_imap_read_config_t *_config = nullptr; - - bool _headerOnly = true; - bool _uidSearch = false; - bool _headerSaved = false; - bool _debug = false; - int _debugLevel = 0; - bool _secure = false; - imapStatusCallback _readCallback = NULL; - - std::vector _msgNum = std::vector(); - FoldersCollection _folders; - SelectedFolderInfo _mbif; - - int _certType = -1; - std::shared_ptr _caCert = nullptr; - -#if defined(ESP32) - ESP_Mail_HTTPClient32 httpClient; -#elif defined(ESP8266) - ESP_Mail_HTTPClient httpClient; -#endif - - IMAP_Status _cbData; -}; - -class SendingResult -{ -private: - std::vector _result = - std::vector(); - void add(struct esp_mail_smtp_send_status_t r) - { - struct esp_mail_smtp_send_status_t _r = r; - _result.push_back(_r); - } - void clear() - { - for (size_t i = 0; i < _result.size(); i++) - { - _result[i].recipients = ""; - _result[i].subject = ""; - _result[i].timesstamp = 0; - _result[i].completed = false; - } - _result.clear(); - } - -public: - friend class SMTPSession; - friend class ESP_Mail_Client; - SendingResult(){}; - ~SendingResult() { clear(); }; - SMTP_Result getItem(size_t index) - { - struct esp_mail_smtp_send_status_t r; - if (index < _result.size()) - return _result[index]; - return r; - } - size_t size() { return _result.size(); }; -}; - -class SMTPSession -{ -public: - SMTPSession(); - ~SMTPSession(); - - /** Begin the SMTP server connection. - * - * @param session The pointer to ESP_Mail_Session structured data that keeps - * the server and log in details. - * @return The boolean value indicates the success of operation. - */ - bool connect(ESP_Mail_Session *session); - - /** Close the SMTP session. - * - */ - bool closeSession(); - - /** Set to enable the debug. - * - * @param level The level to enable the debug message - * level = 0, no debug - * level = 1, basic debug - * level = 2, full debug 1 - * level = 333, full debug 2 - */ - void debug(int level); - - /** Get the error details when sending the Email - * - * @return The string of error details. - */ - String errorReason(); - - /** Set the Email sending status callback function. - * - * @param smtpCallback The callback function that accept the - * smtpStatusCallback param. - */ - void callback(smtpStatusCallback smtpCallback); - - SendingResult sendingResult; - - friend class ESP_Mail_Client; - -private: - bool _tcpConnected = false; - struct esp_mail_smtp_response_status_t _smtpStatus; - int _sentSuccessCount = 0; - int _sentFailedCount = 0; - bool _chunkedEnable = false; - int _chunkCount = 0; - - esp_mail_smtp_command _smtp_cmd = - esp_mail_smtp_command::esp_mail_smtp_cmd_greeting; - struct esp_mail_auth_capability_t _auth_capability; - struct esp_mail_smtp_capability_t _send_capability; - ESP_Mail_Session *_sesson_cfg = NULL; - - bool _debug = false; - int _debugLevel = 0; - bool _secure = false; - smtpStatusCallback _sendCallback = NULL; - - SMTP_Status _cbData; - struct esp_mail_smtp_msg_type_t _msgType; - - int _certType = -1; - std::shared_ptr _caCert = nullptr; - -#if defined(ESP32) - ESP_Mail_HTTPClient32 httpClient; -#elif defined(ESP8266) - ESP_Mail_HTTPClient httpClient; -#endif -}; - - -//#define EMAIL_DEBUG_PRINTLN Serial.println -//#define EMAIL_DEBUG_PRINT Serial.print -#define EMAIL_DEBUG_PRINTLN Tasmota_print -#define EMAIL_DEBUG_PRINT Tasmota_print - -extern void Tasmota_print(const char *); - -static void __attribute__((used)) esp_mail_debug(const char *msg) -{ - delay(0); - EMAIL_DEBUG_PRINTLN(msg); -} - -static void __attribute__((used)) -esp_mail_debug_line(const char *msg, bool newline) -{ - delay(0); - if (newline) - EMAIL_DEBUG_PRINTLN(msg); - else - EMAIL_DEBUG_PRINT(msg); -} - -extern ESP_Mail_Client MailClient; - -extern FS *ufsp; - -#endif // ESP_Mail_Client_H +#ifndef ESP_Mail_Client_H +#define ESP_Mail_Client_H + +#define ESP_MAIL_VERSION "1.2.0" + +/** + * Mail Client Arduino Library for Espressif's ESP32 and ESP8266 + * + * Version: 1.2.0 + * Released: May 17, 2021 + * + * Updates: + * - Add support ESP8266 Core SDK v3.x.x. + * + * + * This library allows Espressif's ESP32 and ESP8266 devices to send and read + * Email + * through the SMTP and IMAP servers. + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of + * the Software, and to permit persons to whom the Software is furnished to do + * so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include "extras/RFC2047.h" +#include "extras/ESPTimeHelper.h" + +#if defined(ESP32) +#define FORMAT_FLASH FORMAT_FLASH_IF_MOUNT_FAILED +#include +#include +//#include +#include +#include "wcs/esp32/ESP_Mail_HTTPClient32.h" +#elif defined(ESP8266) +#include +#define FS_NO_GLOBALS +#include +#include "wcs/esp8266/ESP_Mail_HTTPClient.h" +#endif + +#include "extras/MIMEInfo.h" + +#include +#include +#include + +#if defined(ESP8266) +#define SD_CS_PIN 15 +#endif + +#define SMTP_STATUS_SERVER_CONNECT_FAILED -100 +#define SMTP_STATUS_SMTP_GREETING_GET_RESPONSE_FAILED -101 +#define SMTP_STATUS_SMTP_GREETING_SEND_ACK_FAILED -102 +#define SMTP_STATUS_AUTHEN_NOT_SUPPORT -103 +#define SMTP_STATUS_AUTHEN_FAILED -104 +#define SMTP_STATUS_USER_LOGIN_FAILED -105 +#define SMTP_STATUS_PASSWORD_LOGIN_FAILED -106 +#define SMTP_STATUS_SEND_HEADER_SENDER_FAILED -107 +#define SMTP_STATUS_SEND_HEADER_RECIPIENT_FAILED -108 +#define SMTP_STATUS_SEND_BODY_FAILED -109 +#define SMTP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED -110 +#define SMTP_STATUS_NO_VALID_RECIPIENTS_EXISTED -111 +#define SMTP_STATUS_NO_VALID_SENDER_EXISTED -112 +#define SMTP_STATUS_NO_SUPPORTED_AUTH -113 + +#define IMAP_STATUS_SERVER_CONNECT_FAILED -200 +#define IMAP_STATUS_IMAP_RESPONSE_FAILED -201 +#define IMAP_STATUS_LOGIN_FAILED -202 +#define IMAP_STATUS_BAD_COMMAND -203 +#define IMAP_STATUS_PARSE_FLAG_FAILED -204 +#define IMAP_STATUS_SERVER_OAUTH2_LOGIN_DISABLED -205 +#define IMAP_STATUS_NO_MESSAGE -206 +#define IMAP_STATUS_ERROR_DOWNLAD_TIMEOUT -207 +#define IMAP_STATUS_CLOSE_MAILBOX_FAILED -208 +#define IMAP_STATUS_OPEN_MAILBOX_FAILED -209 +#define IMAP_STATUS_LIST_MAILBOXS_FAILED -210 +#define IMAP_STATUS_CHECK_CAPABILITIES_FAILED -211 +#define IMAP_STATUS_NO_SUPPORTED_AUTH -212 +#define IMAP_STATUS_NO_MAILBOX_FOLDER_OPENED -213 + +#define MAIL_CLIENT_ERROR_CONNECTION_LOST -28 +#define MAIL_CLIENT_ERROR_READ_TIMEOUT -29 +#define MAIL_CLIENT_ERROR_FILE_IO_ERROR -30 +#define MAIL_CLIENT_ERROR_SERVER_CONNECTION_FAILED -31 +#define MAIL_CLIENT_ERROR_SSL_TLS_STRUCTURE_SETUP -32 +#define MAIL_CLIENT_ERROR_OUT_OF_MEMORY -33 + +#define MAX_EMAIL_SEARCH_LIMIT 1000 +#define BASE64_CHUNKED_LEN 76 +#define FLOWED_TEXT_LEN 78 +#define ESP_MAIL_WIFI_RECONNECT_TIMEOUT 10000 +#define ESP_MAIL_PROGRESS_REPORT_STEP 20 +#define ESP_MAIL_CLIENT_TRANSFER_DATA_FAILED 0 +#define ESP_MAIL_CLIENT_STREAM_CHUNK_SIZE 256 +#define ESP_MAIL_CLIENT_VALID_TS 1577836800 + +#if defined(ESP32) +#define ESP_MAIL_MIN_MEM 70000 +#elif defined(ESP8266) +#define ESP_MAIL_MIN_MEM 4000 +#endif + +class IMAPSession; +class SMTPSession; +class SMTP_Status; +class DownloadProgress; +class MessageData; + +enum esp_mail_smtp_status_code +{ + esp_mail_smtp_status_code_0, // default + + /* Positive Completion */ + esp_mail_smtp_status_code_211 = 221, // System status, or system help reply + esp_mail_smtp_status_code_214 = + 214, // Help message(A response to the HELP command) + esp_mail_smtp_status_code_220 = 220, // Service ready + esp_mail_smtp_status_code_221 = + 221, // Service closing transmission channel [RFC 2034] + esp_mail_smtp_status_code_235 = + 235, // 2.7.0 Authentication succeeded[RFC 4954] + esp_mail_smtp_status_code_250 = 250, // Requested mail action okay, completed + esp_mail_smtp_status_code_251 = 251, // User not local; will forward + esp_mail_smtp_status_code_252 = 252, // Cannot verify the user, but it will + // try to deliver the message anyway + + /* Positive Intermediate */ + esp_mail_smtp_status_code_334 = 334, //(Server challenge - the text part + //contains the Base64 - encoded + //challenge)[RFC 4954] + esp_mail_smtp_status_code_354 = 354, // Start mail input + + /* Transient Negative Completion */ + /* "Transient Negative" means the error condition is temporary, and the action + may be requested again.*/ + esp_mail_smtp_status_code_421 = + 421, // Service is unavailable because the server is shutting down. + esp_mail_smtp_status_code_432 = + 432, // 4.7.12 A password transition is needed [RFC 4954] + esp_mail_smtp_status_code_450 = + 450, // Requested mail action not taken: mailbox unavailable (e.g., + // mailbox busy or temporarily blocked for policy reasons) + esp_mail_smtp_status_code_451 = + 451, // Requested action aborted : local error in processing + // e.g.IMAP server unavailable[RFC 4468] + esp_mail_smtp_status_code_452 = + 452, // Requested action not taken : insufficient system storage + esp_mail_smtp_status_code_454 = + 454, // 4.7.0 Temporary authentication failure[RFC 4954] + esp_mail_smtp_status_code_455 = + 455, // Server unable to accommodate parameters + + /* Permanent Negative Completion */ + esp_mail_smtp_status_code_500 = 500, // Syntax error, command unrecognized + // (This may include errors such as + // command line too long) + // e.g. Authentication Exchange line is too long [RFC 4954] + esp_mail_smtp_status_code_501 = + 501, // Syntax error in parameters or arguments + // e.g. 5.5.2 Cannot Base64-decode Client responses [RFC 4954] + // 5.7.0 Client initiated Authentication Exchange (only when the SASL + // mechanism specified that client does not begin the authentication exchange) + // [RFC 4954] + esp_mail_smtp_status_code_502 = 502, // Command not implemented + esp_mail_smtp_status_code_503 = 503, // Bad sequence of commands + esp_mail_smtp_status_code_504 = 504, // Command parameter is not implemented + // e.g. 5.5.4 Unrecognized authentication type [RFC 4954] + esp_mail_smtp_status_code_521 = 521, // Server does not accept mail [RFC 7504] + esp_mail_smtp_status_code_523 = 523, // Encryption Needed [RFC 5248] + esp_mail_smtp_status_code_530 = + 530, // 5.7.0 Authentication required [RFC 4954] + esp_mail_smtp_status_code_534 = + 534, // 5.7.9 Authentication mechanism is too weak [RFC 4954] + esp_mail_smtp_status_code_535 = + 535, // 5.7.8 Authentication credentials invalid [RFC 4954] + esp_mail_smtp_status_code_538 = 538, // 5.7.11 Encryption required for + // requested authentication mechanism[RFC + // 4954] + esp_mail_smtp_status_code_550 = + 550, // Requested action not taken: mailbox unavailable (e.g., mailbox not + // found, no access, or command rejected for policy reasons) + esp_mail_smtp_status_code_551 = + 551, // User not local; please try + esp_mail_smtp_status_code_552 = + 552, // Requested mail action aborted: exceeded storage allocation + esp_mail_smtp_status_code_553 = + 553, // Requested action not taken: mailbox name not allowed + esp_mail_smtp_status_code_554 = 554, // Transaction has failed (Or, in the + // case of a connection-opening response, + // "No SMTP service here") + // e.g. 5.3.4 Message too big for system [RFC 4468] + esp_mail_smtp_status_code_556 = 556, // Domain does not accept mail[RFC 7504] +}; + +enum esp_mail_smtp_port +{ + esp_mail_smtp_port_25 = 25, // PLAIN/TLS with STARTTLS + esp_mail_smtp_port_465 = 465, // SSL + esp_mail_smtp_port_587 = 587 // TLS with STARTTLS +}; + +enum esp_mail_imap_port +{ + esp_mail_imap_port_143 = 143, // PLAIN/TLS with STARTTLS + esp_mail_imap_port_993 = 993, // SSL +}; + +enum esp_mail_smtp_notify +{ + esp_mail_smtp_notify_never = 0, + esp_mail_smtp_notify_success = 1, + esp_mail_smtp_notify_failure = 2, + esp_mail_smtp_notify_delay = 4 +}; + +enum esp_mail_attach_type +{ + esp_mail_att_type_none, + esp_mail_att_type_attachment, + esp_mail_att_type_inline +}; + +enum esp_mail_message_type +{ + esp_mail_msg_type_none = 0, + esp_mail_msg_type_plain = 1, + esp_mail_msg_type_html = 2, + esp_mail_msg_type_enriched = 1 +}; + +enum esp_mail_auth_type +{ + esp_mail_auth_type_psw, + esp_mail_auth_type_oath2, + esp_mail_auth_type_token +}; + +enum esp_mail_imap_auth_mode +{ + esp_mail_imap_mode_examine, + esp_mail_imap_mode_select +}; + +enum esp_mail_imap_response_status +{ + esp_mail_imap_resp_unknown, + esp_mail_imap_resp_ok, + esp_mail_imap_resp_no, + esp_mail_imap_resp_bad +}; + +enum esp_mail_imap_header_state +{ + esp_mail_imap_state_from = 1, + esp_mail_imap_state_to, + esp_mail_imap_state_cc, + esp_mail_imap_state_subject, + esp_mail_imap_state_content_type, + esp_mail_imap_state_content_transfer_encoding, + esp_mail_imap_state_accept_language, + esp_mail_imap_state_content_language, + esp_mail_imap_state_date, + esp_mail_imap_state_msg_id, + esp_mail_imap_state_char_set, + esp_mail_imap_state_boundary +}; + +enum esp_mail_imap_command +{ + esp_mail_imap_cmd_capability, + esp_mail_imap_cmd_starttls, + esp_mail_imap_cmd_login, + esp_mail_imap_cmd_plain, + esp_mail_imap_cmd_auth, + esp_mail_imap_cmd_list, + esp_mail_imap_cmd_select, + esp_mail_imap_cmd_examine, + esp_mail_imap_cmd_close, + esp_mail_imap_cmd_status, + esp_mail_imap_cmd_search, + esp_mail_imap_cmd_fetch_body_header, + esp_mail_imap_cmd_fetch_body_mime, + esp_mail_imap_cmd_fetch_body_text, + esp_mail_imap_cmd_fetch_body_attachment, + esp_mail_imap_cmd_fetch_body_inline, + esp_mail_imap_cmd_logout, + esp_mail_imap_cmd_store, + esp_mail_imap_cmd_expunge, + esp_mail_imap_cmd_create, + esp_mail_imap_cmd_delete +}; + +enum esp_mail_imap_mime_fetch_type +{ + esp_mail_imap_mime_fetch_type_part, + esp_mail_imap_mime_fetch_type_sub_part1, + esp_mail_imap_mime_fetch_type_sub_part2 +}; + +enum esp_mail_smtp_command +{ + esp_mail_smtp_cmd_initial_state, + esp_mail_smtp_cmd_greeting, + esp_mail_smtp_cmd_start_tls, + esp_mail_smtp_cmd_login_user, + esp_mail_smtp_cmd_auth_plain, + esp_mail_smtp_cmd_auth, + esp_mail_smtp_cmd_login_psw, + esp_mail_smtp_cmd_send_header_sender, + esp_mail_smtp_cmd_send_header_recipient, + esp_mail_smtp_cmd_send_body, + esp_mail_smtp_cmd_chunk_termination, + esp_mail_smtp_cmd_logout +}; + +enum esp_mail_imap_header_type +{ + esp_mail_imap_header_from, + esp_mail_imap_header_to, + esp_mail_imap_header_cc, + esp_mail_imap_header_subject, + esp_mail_imap_header_date, + esp_mail_imap_header_msg_id, + esp_mail_imap_header_cont_lang, + esp_mail_imap_header_accept_lang +}; + +enum esp_mail_char_decoding_scheme +{ + esp_mail_char_decoding_scheme_default, + esp_mail_char_decoding_scheme_iso8859_1, + esp_mail_char_decoding_scheme_tis620 +}; + +enum esp_mail_smtp_priority +{ + esp_mail_smtp_priority_high = 1, + esp_mail_smtp_priority_normal = 3, + esp_mail_smtp_priority_low = 5, +}; + +enum esp_mail_smtp_embed_message_type +{ + esp_mail_smtp_embed_message_type_attachment = 0, + esp_mail_smtp_embed_message_type_inline +}; + +enum esp_mail_debug_level +{ + esp_mail_debug_level_0 = 0, + esp_mail_debug_level_1, + esp_mail_debug_level_2 = 222, + esp_mail_debug_level_3 = 333 +}; + +enum esp_mail_imap_multipart_sub_type +{ + esp_mail_imap_multipart_sub_type_none = 0, + esp_mail_imap_multipart_sub_type_mixed, + esp_mail_imap_multipart_sub_type_alternative, + esp_mail_imap_multipart_sub_type_parallel, + esp_mail_imap_multipart_sub_type_digest, + esp_mail_imap_multipart_sub_type_related, + esp_mail_imap_multipart_sub_type_report, +}; + +enum esp_mail_imap_message_sub_type +{ + esp_mail_imap_message_sub_type_none = 0, + esp_mail_imap_message_sub_type_rfc822, + esp_mail_imap_message_sub_type_delivery_status, + esp_mail_imap_message_sub_type_partial, + esp_mail_imap_message_sub_type_external_body, +}; + +/* descrete media types (rfc 2046) */ +struct esp_mail_imap_descrete_media_type_t +{ + /** textual information with subtypes + * "plain" + * "enriched" (rfc 1896 revised from richtext in rfc 1341) + * + * unrecognized subtypes and charset should be interpreted as + * application/octet-stream + * + * parameters: + * "charset" (rfc 2045) default is us-ascii + * for character set includes 8-bit characters + * and such characters are used in the body, Content-Transfer-Encoding + * header field and a corresponding encoding on the data are required + * + * ISO-8859-X where "X" is to be replaced, as + * necessary, for the parts of ISO-8859 [ISO-8859]. + */ + static constexpr const char *text = "text"; + + /** image data with subtypes (rfc 2048) + * "jpeg" + * "gif" + * + * unrecognized subtypes should be interpreted as application/octet-stream + */ + static constexpr const char *image = "image"; + + /** audio data with initial subtype + * "baic" -- for single channel audio encoded using 8bit ISDN mu-law [PCM] + * at a sample rate of 8000 Hz. + * + * Unrecognized subtypes of "audio" should at a miniumum be treated as + * "application/octet-stream" + */ + static constexpr const char *audio = "audio"; + + /** video data with initial subtype + * "mpeg" + * + * Unrecognized subtypes of "video" should at a minumum be treated as + * "application/octet-stream" + */ + static constexpr const char *video = "video"; + + /** some other kind of data, typically either + * uninterpreted binary data or information to be + * processed by an application with subtypes + * + * "octet-stream" -- uninterpreted binary data + * "PostScript" -- for the transport of PostScript material + * + * Other expected uses include spreadsheets, data for mail-based + * scheduling systems, and languages for "active" (computational) + * messaging, and word processing formats that are not directly readable. + * + * The octet-stream subtype parameters: + * TYPE, PADDING, NAME + */ + static constexpr const char *application = "application"; +}; + +/** composite media types (rfc 2046) + * + * As stated in the definition of the Content-Transfer-Encoding field + * [RFC 2045], no encoding other than "7bit", "8bit", or "binary" is + * permitted for entities of type "multipart". The "multipart" boundary + * delimiters and header fields are always represented as 7bit US-ASCII + * in any case (though the header fields may encode non-US-ASCII header + * text as per RFC 2047) and data within the body parts can be encoded + * on a part-by-part basis, with Content-Transfer-Encoding fields for + * each appropriate body part. +*/ +struct esp_mail_imap_composite_media_type_t +{ + /** data consisting of multiple entities of independent data types + * The Content-Type field for multipart entities requires one parameter, + * "boundary". + * The boundary delimiter line is then defined as a line + * consisting entirely of two hyphen characters ("-", decimal value 45) + * followed by the boundary parameter value from the Content-Type header + * field, optional linear whitespace, and a terminating CRLF. + * + * NOTE: The CRLF preceding the boundary delimiter line is conceptually + * attached to the boundary so that it is possible to have a part that + * does not end with a CRLF (line break). Body parts that must be + * considered to end with line breaks, therefore, must have two CRLFs + * preceding the boundary delimiter line, the first of which is part of + * the preceding body part, and the second of which is part of the + * encapsulation boundary. + * + * Boundary delimiters must not appear within the encapsulated material, + * and must be no longer than 70 characters, not counting the two + * leading hyphens. + * + * The boundary delimiter line following the last body part is a + * distinguished delimiter that indicates that no further body parts + * will follow. Such a delimiter line is identical to the previous + * delimiter lines, with the addition of two more hyphens after the + * boundary parameter value. + * + * See rfc2049 Appendix A for a Complex Multipart Example + */ + static constexpr const char *multipart = "multipart"; + + /* an encapsulated message */ + static constexpr const char *message = "message"; +}; + +struct esp_mail_imap_media_text_sub_type_t +{ + static constexpr const char *plain = "plain"; + static constexpr const char *enriched = "enriched"; + static constexpr const char *html = "html"; +}; + +/* multipart sub types */ +struct esp_mail_imap_multipart_sub_type_t +{ + /* a generic mixed set of parts */ + static constexpr const char *mixed = "mixed"; + + /* the same data in multiple formats */ + static constexpr const char *alternative = "alternative"; + + /* parts intended to be viewed simultaneously */ + static constexpr const char *parallel = "parallel"; + + /* multipart entities in which each part has a default type of + * "message/rfc822" */ + static constexpr const char *digest = "digest"; + + /* for compound objects consisting of several inter-related body parts (rfc + * 2387) */ + static constexpr const char *related = "related"; + + /* rfc 3462 */ + static constexpr const char *report = "report"; +}; + +/* message body sub types */ +struct esp_mail_imap_message_sub_type_t +{ + /* body contains an encapsulated message, with the syntax of an RFC 822 + * message. */ + static constexpr const char *rfc822 = "rfc822"; + + /* to allow large objects to be delivered as several separate pieces of mail + */ + static constexpr const char *Partial = "Partial"; + + /* the actual body data are not included, but merely referenced */ + static constexpr const char *External_Body = "External-Body"; + + static constexpr const char *delivery_status = "delivery-status"; +}; + +/** content disposition rfc 2183 + * + * Parameters: + * "filename", "creation-date","modification-date", + * "read-date", * "size" +*/ +struct esp_mail_imap_content_disposition_type_t +{ + /** if it is intended to be displayed automatically + * upon display of the message. + */ + static constexpr const char *inline_ = "inline"; + + /** to indicate that they are separate from the main body + * of the mail message, and that their display should not + * be automatic, but contingent upon some further action of the user. + */ + static constexpr const char *attachment = "attachment"; +}; + +struct esp_mail_internal_use_t +{ + bool binary = false; + std::string cid = ""; +}; + +struct esp_mail_content_transfer_encoding_t +{ + /* The default 7-bit transfer encoding for US-ACII characters*/ + static constexpr const char *enc_7bit = "7bit"; + + /* The quoted printable transfer encoding for non-US-ASCII characters*/ + static constexpr const char *enc_qp = "quoted-printable"; + + /* The base64 encoded transfer encoding */ + static constexpr const char *enc_base64 = "base64"; + + /* The 8-bit transfer encoding for extended-US-ASCII characters*/ + static constexpr const char *enc_8bit = "8bit"; + + /* The binary transfer encoding for extended-US-ASCII characters with no line + * length limit*/ + static constexpr const char *enc_binary = "binary"; +}; + +struct esp_mail_smtp_response_status_t +{ + int respCode = 0; + int statusCode = 0; + std::string text = ""; +}; + +struct esp_mail_imap_response_status_t +{ + int statusCode = 0; + std::string text = ""; +}; + +/* The option to embed this message content as a file */ +struct esp_mail_smtp_embed_message_body_t +{ + /* Enable to send this message body as file */ + bool enable = false; + + /* The name of embedded file */ + const char *filename = ""; + + /** The embedded type + * esp_mail_smtp_embed_message_type_attachment or 0 + * esp_mail_smtp_embed_message_type_inline or 1 + */ + esp_mail_smtp_embed_message_type type = + esp_mail_smtp_embed_message_type_attachment; +}; + +struct esp_mail_file_message_content_t +{ + /* The file path include its name */ + const char *name = ""; + + /** The type of file storages e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + esp_mail_file_storage_type type = esp_mail_file_storage_type_flash; +}; + +struct esp_mail_blob_message_content_t +{ + /* The array of content in flash memory */ + const uint8_t *data = nullptr; + + /* The array size in bytes */ + size_t size = 0; +}; + +/* The PLAIN text body details of the message */ +struct esp_mail_plain_body_t +{ + /* The option to embed this message content as a file */ + struct esp_mail_smtp_embed_message_body_t embed; + + /* The PLAIN text content of the message */ + const char *content = ""; + + /* The blob that contins PLAIN text content of the message */ + struct esp_mail_blob_message_content_t blob; + + /* The file that contins PLAIN text content of the message */ + struct esp_mail_file_message_content_t file; + + /* The charset of the PLAIN text content of the message */ + const char *charSet = "UTF-8"; + + /* The content type of message */ + const char *content_type = "text/plain"; + + /* The option to encode the content for data transfer */ + const char *transfer_encoding = "7bit"; + + /* The option to send the PLAIN text with wrapping */ + bool flowed = false; + + /* The internal usage data */ + struct esp_mail_internal_use_t _int; +}; + +struct esp_mail_html_body_t +{ + /* The option to embedded the content as a file */ + struct esp_mail_smtp_embed_message_body_t embed; + + /* The HTML content of the message */ + const char *content = ""; + + /* The blob that contins HTML content of the message */ + struct esp_mail_blob_message_content_t blob; + + /* The file that contins HTML content of the message */ + struct esp_mail_file_message_content_t file; + + /* The charset of the HTML content of the message */ + const char *charSet = "UTF-8"; + + /* The content type of message */ + const char *content_type = "text/html"; + + /* The option to encode the content for data transfer */ + const char *transfer_encoding = "7bit"; + + /* The internal usage data */ + struct esp_mail_internal_use_t _int; +}; + +struct esp_mail_link_internal_t +{ + std::string cid = ""; +}; + +struct esp_mail_email_info_t +{ + /* The name of Email author*/ + const char *name = ""; + + /* The Email address */ + const char *email = ""; +}; +struct esp_mail_smtp_msg_response_t +{ + /* The author Email address to reply */ + const char *reply_to = ""; + + /* The sender Email address to return the message */ + const char *return_path = ""; + + /** The Delivery Status Notifications e.g. esp_mail_smtp_notify_never, + * esp_mail_smtp_notify_success, + * esp_mail_smtp_notify_failure, and + * esp_mail_smtp_notify_delay + */ + int notify = esp_mail_smtp_notify::esp_mail_smtp_notify_never; +}; + +struct esp_mail_smtp_enable_option_t +{ + /* Enable chunk data sending for large message */ + bool chunking = false; +}; + +struct esp_mail_attach_blob_t +{ + /* BLOB data (flash or ram) */ + const uint8_t *data = nullptr; + + /* BLOB data size in byte */ + size_t size = 0; +}; + +struct esp_mail_attach_file_t +{ + const char *path = ""; + /** The file storage type e.g. esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + esp_mail_file_storage_type storage_type = esp_mail_file_storage_type_none; +}; + +struct esp_mail_attach_descr_t +{ + /* The name of attachment */ + const char *name = ""; + + /* The attachment file name */ + const char *filename = ""; + + /* The MIME type of attachment */ + const char *mime = ""; + + /* The transfer encoding of attachment e.g. base64 */ + const char *transfer_encoding = "base64"; + + /* The content encoding of attachment e.g. base64 */ + const char *content_encoding = ""; + + /* The content id of attachment file */ + const char *content_id = ""; +}; + +struct esp_mail_attach_internal_t +{ + esp_mail_attach_type att_type = esp_mail_att_type_attachment; + int index = 0; + int msg_uid = 0; + bool flash_blob = false; + bool binary = false; + bool parallel = false; + std::string cid = ""; +}; + +struct esp_mail_attachment_t +{ + /* The attachment description */ + struct esp_mail_attach_descr_t descr; + + /* The BLOB data config */ + struct esp_mail_attach_blob_t blob; + + /* The file data config */ + struct esp_mail_attach_file_t file; + + /* reserved for internal usage */ + struct esp_mail_attach_internal_t _int; +}; + +struct esp_mail_smtp_recipient_t +{ + /* The recipient's name */ + const char *name = ""; + + /* The recipient's Email address */ + const char *email = ""; +}; + +struct esp_mail_smtp_recipient_address_t +{ + /* The recipient's Email address */ + const char *email = ""; +}; + +struct esp_mail_smtp_send_status_t +{ + /* The status of the message */ + bool completed = false; + + /* The primary recipient mailbox of the message */ + const char *recipients = ""; + + /* The topic of the message */ + const char *subject = ""; + + /* The timestamp of the message */ + time_t timesstamp = 0; +}; + +struct esp_mail_attacment_info_t +{ + const char *filename = ""; + const char *name = ""; + const char *creationDate = ""; + const char *mime = ""; + esp_mail_attach_type type = esp_mail_att_type_none; + size_t size; +}; + +struct esp_mail_auth_capability_t +{ + bool plain = false; + bool xoauth2 = false; + bool cram_md5 = false; + bool digest_md5 = false; + bool login = false; + bool start_tls = false; +}; + +struct esp_mail_smtp_capability_t +{ + bool esmtp = false; + bool binaryMIME = false; + bool _8bitMIME = false; + bool chunking = false; + bool utf8 = false; + bool pipelining = false; + bool dsn = false; +}; + +struct esp_mail_imap_rfc822_msg_header_item_t +{ + std::string sender = ""; + std::string from; + std::string subject = ""; + std::string messageID = ""; + std::string keyword = ""; + std::string comment = ""; + std::string date = ""; + std::string return_path = ""; + std::string reply_to; + std::string to = ""; + std::string cc = ""; + std::string bcc = ""; +}; + +struct esp_mail_message_part_info_t +{ + int octetLen = 0; + int attach_data_size = 0; + int textLen = 0; + bool sizeProp = false; + int nestedLevel = 0; + std::string partNumStr = ""; + std::string partNumFetchStr = ""; + std::string text = ""; + std::string filename = ""; + std::string type = ""; + std::string save_path = ""; + std::string name = ""; + std::string content_disposition = ""; + std::string content_type = ""; + std::string descr = ""; + std::string content_transfer_encoding = ""; + std::string creation_date = ""; + std::string modification_date = ""; + std::string charset = ""; + std::string download_error = ""; + esp_mail_attach_type attach_type = esp_mail_att_type_none; + esp_mail_message_type msg_type = esp_mail_msg_type_none; + bool file_open_write = false; + bool multipart = false; + esp_mail_imap_multipart_sub_type multipart_sub_type = + esp_mail_imap_multipart_sub_type_none; + esp_mail_imap_message_sub_type message_sub_type = + esp_mail_imap_message_sub_type_none; + bool rfc822_part = false; + int rfc822_msg_Idx = 0; + struct esp_mail_imap_rfc822_msg_header_item_t rfc822_header; + bool error = false; + bool plain_flowed = false; + bool plain_delsp = false; +}; + +struct esp_mail_message_header_t +{ + int header_data_len = 0; + std::string from = ""; + std::string to = ""; + std::string cc = ""; + std::string subject = ""; + std::string content_type = ""; + std::string content_transfer_encoding = ""; + std::string date = ""; + std::string message_id = ""; + std::string message_uid = ""; + std::string message_no = ""; + std::string boundary = ""; + std::string accept_language = ""; + std::string content_language = ""; + std::string char_set = ""; + bool multipart = false; + bool rfc822_part = false; + int rfc822Idx = 0; + std::string partNumStr = ""; + + esp_mail_imap_multipart_sub_type multipart_sub_type = + esp_mail_imap_multipart_sub_type_none; + esp_mail_imap_message_sub_type message_sub_type = + esp_mail_imap_message_sub_type_none; + std::string from_charset = ""; + std::string to_charset = ""; + std::string cc_charset = ""; + std::string subject_charset = ""; + std::string msgID = ""; + std::string error_msg = ""; + bool error = false; + std::vector part_headers = + std::vector(); + int attachment_count = 0; + int total_download_size = 0; + int downloaded_size = 0; + int total_attach_data_size = 0; + int downloaded_bytes = 0; + int message_data_count = 0; +}; + +/* Internal use */ +struct esp_mail_folder_info_t +{ + std::string name = ""; + std::string attributes = ""; + std::string delimiter = ""; +}; + +struct esp_mail_folder_info_item_t +{ + /* The name of folder */ + const char *name = ""; + + /* The attributes of folder */ + const char *attributes = ""; + + /* The delimeter of folder */ + const char *delimiter = ""; +}; + +struct esp_mail_sesson_cert_config_t +{ + /* The certificate data (base64 data) */ + const char *cert_data = ""; + + /* The certificate file (DER format) */ + const char *cert_file = ""; + + /* The storage type */ + esp_mail_file_storage_type cert_file_storage_type; +}; + +struct esp_mail_sesson_sever_config_t +{ + /* The hostName of the server */ + const char *host_name = ""; + /* The port on the server to connect to */ + uint16_t port = 0; +}; + +/* The log in credentials */ +struct esp_mail_sesson_login_config_t +{ + /* The user Email address to log in */ + const char *email = ""; + + /* The user password to log in */ + const char *password = ""; + + /* The OAuth2.0 access token to log in */ + const char *accessToken = ""; + + /* The user domain or ip of client */ + const char *user_domain = ""; +}; + +struct esp_mail_sesson_secure_config_t +{ + /* The option to send the command to start the TLS connection */ + bool startTLS = false; +}; + +struct esp_mail_session_config_t +{ + /* The server config */ + struct esp_mail_sesson_sever_config_t server; + + /* The log in config */ + struct esp_mail_sesson_login_config_t login; + + /* The secure config */ + struct esp_mail_sesson_secure_config_t secure; + + /* The certificate config */ + struct esp_mail_sesson_cert_config_t certificate; +}; + +struct esp_mail_imap_download_config_t +{ + /* To download the PLAIN text content of the message */ + bool text = false; + + /* To download the HTML content of the message */ + bool html = false; + + /* To download the attachments of the message */ + bool attachment = false; + + /* To download the inline image of the message */ + bool inlineImg = false; + + /* To download the rfc822 mesages in the message */ + bool rfc822 = false; + + /* To download the message header */ + bool header = false; +}; + +struct esp_mail_imap_enable_config_t +{ + /* To store the PLAIN text of the message in the IMAPSession */ + bool text = false; + + /* To store the HTML of the message in the IMAPSession */ + bool html = false; + + /* To store the rfc822 messages in the IMAPSession */ + bool rfc822 = false; + + /* To enable the download status via the serial port */ + bool download_status = false; + + /* To sort the message UID of the search result in descending order */ + bool recent_sort = false; +}; + +struct esp_mail_imap_limit_config_t +{ + /* The maximum messages from the search result */ + size_t search = 10; + + /** The maximum size of the memory buffer to store the message content. + * This is only limit for data to be stored in the IMAPSession. + */ + size_t msg_size = 1024; + + /* The maximum size of each attachment to download */ + size_t attachment_size = 1024 * 1024 * 5; +}; + +struct esp_mail_imap_storage_config_t +{ + /* The path to save the downloaded file */ + const char *saved_path = "/"; + + /** The type of file storages e.g. + * esp_mail_file_storage_type_none, + * esp_mail_file_storage_type_flash, and + * esp_mail_file_storage_type_sd + */ + esp_mail_file_storage_type type = esp_mail_file_storage_type_flash; +}; + +struct esp_mail_imap_search_config_t +{ + /* The search criteria */ + const char *criteria = ""; + + /* The option to search the unseen message */ + bool unseen_msg = false; +}; + +struct esp_mail_imap_fetch_config_t +{ + /* The UID of message to fetch */ + const char *uid = ""; + + /* Set the message flag as seen */ + bool set_seen = false; +}; + +struct esp_mail_imap_read_config_t +{ + /* The config for fetching */ + struct esp_mail_imap_fetch_config_t fetch; + + /* The config for search */ + struct esp_mail_imap_search_config_t search; + + /* The config about the limits */ + struct esp_mail_imap_limit_config_t limit; + + /* The config to enable the features */ + struct esp_mail_imap_enable_config_t enable; + + /* The config about downloads */ + struct esp_mail_imap_download_config_t download; + + /* The config about the storage and path to save the downloaded file */ + struct esp_mail_imap_storage_config_t storage; +}; + +/* Mail and MIME Header Fields */ +struct esp_mail_imap_msg_item_t +{ + /* The message number */ + const char *msgNo = ""; + + /* The message UID */ + const char *UID = ""; + + /* The message identifier (RFC 4021) */ + const char *ID = ""; + + /* The language(s) for auto-responses (RFC 4021) */ + const char *acceptLang = ""; + + /* The language of message content (RFC 4021) */ + const char *contentLang = ""; + + /* The mailbox of message author (RFC 4021) */ + const char *from = ""; + + /* The charset of the mailbox of message author */ + const char *fromCharset = ""; + + /* The primary recipient mailbox (RFC 4021) */ + const char *to = ""; + + /* The charset of the primary recipient mailbox */ + const char *toCharset = ""; + + /* The Carbon-copy recipient mailboxes (RFC 4021) */ + const char *cc = ""; + + /* The charset of the Carbon-copy recipient mailbox header */ + const char *ccCharset = ""; + + /* The message date and time (RFC 4021) */ + const char *date = ""; + + /* The topic of message (RFC 4021) */ + const char *subject = ""; + + /* The topic of message charset */ + const char *subjectCharset = ""; + + /* The PLAIN text content of the message */ + struct esp_mail_plain_body_t text; + + /* The HTML content of the message */ + struct esp_mail_html_body_t html; + + /* rfc822 related */ + + /* The sender Email */ + const char *sender; + + /* The message identifier */ + const char *messageID = ""; + + /* The keywords or phrases, separated by commas */ + const char *keyword = ""; + + /* The comment about message */ + const char *comment = ""; + + /* The return recipient of the message */ + const char *return_path = ""; + + /* The Email address to reply */ + const char *reply_to; + + /* The Blind carbon-copy recipients */ + const char *bcc = ""; + + /* The error description from fetching the message */ + const char *fetchError = ""; + + /* The info about the attachments in the message */ + std::vector attachments = + std::vector(); + + /* The info about the rfc822 messages included in the message */ + std::vector rfc822 = + std::vector(); +}; + +struct esp_mail_imap_msg_list_t +{ + /* The info of a message */ + std::vector msgItems = + std::vector(); +}; + +struct esp_mail_smtp_msg_type_t +{ + bool rfc822 = false; + int rfc822Idx = 0; +}; +struct esp_mail_imap_multipart_level_t +{ + uint8_t level = 0; + bool fetch_rfc822_header = false; + bool append_body_text = false; +}; + +/** The content transfer encoding + * enc_7bit or "7bit" + * enc_qp or "quoted-printable" + * enc_base64 or "base64" + * enc_binary or "binary" + * enc_8bit or "8bit" +*/ +typedef struct esp_mail_content_transfer_encoding_t Content_Transfer_Encoding; + +/* The result from sending the Email */ +typedef struct esp_mail_smtp_send_status_t SMTP_Result; + +/* The attachment item details for a message which returned from fetching the + * Email */ +typedef struct esp_mail_attacment_info_t IMAP_Attach_Item; + +/* The attachment details for sending the Email */ +typedef struct esp_mail_attachment_t SMTP_Attachment; + +/* The info of the selected or open mailbox folder e.g. name, attributes and + * delimiter */ +typedef struct esp_mail_folder_info_item_t FolderInfo; + +/* The session configuations */ +typedef struct esp_mail_session_config_t ESP_Mail_Session; + +/** The IMAP operation configuations */ +typedef struct esp_mail_imap_read_config_t IMAP_Config; + +/* The message item data of the IMAP_MSG_List which contains header, body and + * attachments info for eacch message*/ +typedef struct esp_mail_imap_msg_item_t IMAP_MSG_Item; + +/* The list that contains the message items from searching or fetching the Email + */ +typedef struct esp_mail_imap_msg_list_t IMAP_MSG_List; + +static const char esp_mail_str_1[] PROGMEM = + "Content-Type: multipart/mixed; boundary=\""; +static const char esp_mail_str_2[] PROGMEM = "$ CAPABILITY"; +static const char esp_mail_str_3[] PROGMEM = "Mime-Version: 1.0\r\n"; +static const char esp_mail_str_4[] PROGMEM = "AUTH LOGIN"; +static const char esp_mail_str_5[] PROGMEM = "HELO "; +static const char esp_mail_str_6[] PROGMEM = "EHLO "; +static const char esp_mail_str_7[] PROGMEM = "QUIT"; +static const char esp_mail_str_8[] PROGMEM = "MAIL FROM:"; +static const char esp_mail_str_9[] PROGMEM = "RCPT TO:"; +static const char esp_mail_str_10[] PROGMEM = "From: "; +static const char esp_mail_str_11[] PROGMEM = "To: "; +static const char esp_mail_str_12[] PROGMEM = "Cc: "; +static const char esp_mail_str_13[] PROGMEM = ",<"; +static const char esp_mail_str_14[] PROGMEM = "<"; +static const char esp_mail_str_15[] PROGMEM = ">"; +static const char esp_mail_str_16[] PROGMEM = "DATA"; +static const char esp_mail_str_17[] PROGMEM = "X-Priority: "; +static const char esp_mail_str_18[] PROGMEM = "X-MSMail-Priority: High\r\n"; +static const char esp_mail_str_19[] PROGMEM = "X-MSMail-Priority: Normal\r\n"; +static const char esp_mail_str_20[] PROGMEM = "X-MSMail-Priority: Low\r\n"; +static const char esp_mail_str_21[] PROGMEM = "Importance: High\r\n"; +static const char esp_mail_str_22[] PROGMEM = "Importance: Normal\r\n"; +static const char esp_mail_str_23[] PROGMEM = "Importance: Low\r\n"; +static const char esp_mail_str_24[] PROGMEM = "Subject: "; +static const char esp_mail_str_25[] PROGMEM = "Content-Type: "; +static const char esp_mail_str_26[] PROGMEM = "; Name=\""; +static const char esp_mail_str_27[] PROGMEM = "$"; +static const char esp_mail_str_28[] PROGMEM = + "Content-Type: multipart/parallel; boundary=\""; +static const char esp_mail_str_29[] PROGMEM = "7bit"; +static const char esp_mail_str_30[] PROGMEM = + "Content-Disposition: attachment; filename=\""; +static const char esp_mail_str_31[] PROGMEM = "base64"; +static const char esp_mail_str_32[] PROGMEM = "application/octet-stream"; +static const char esp_mail_str_33[] PROGMEM = "--"; +static const char esp_mail_str_34[] PROGMEM = "\r\n"; +static const char esp_mail_str_35[] PROGMEM = "\"\r\n\r\n"; +static const char esp_mail_str_36[] PROGMEM = "\"\r\n"; +static const char esp_mail_str_37[] PROGMEM = "\r\n.\r\n"; +static const char esp_mail_str_38[] PROGMEM = "unable to connect to server"; +static const char esp_mail_str_39[] PROGMEM = "SMTP server greeting failed"; +static const char esp_mail_str_40[] PROGMEM = ".dat"; +static const char esp_mail_str_41[] PROGMEM = "$ AUTHENTICATE PLAIN "; +static const char esp_mail_str_42[] PROGMEM = + "the provided SASL authentication mechanism is not support"; +static const char esp_mail_str_43[] PROGMEM = "authentication failed"; +static const char esp_mail_str_44[] PROGMEM = "mydomain.com"; +static const char esp_mail_str_45[] PROGMEM = "AUTH PLAIN"; +static const char esp_mail_str_46[] PROGMEM = "Return-Path: "; +static const char esp_mail_str_47[] PROGMEM = "login password is not valid"; +static const char esp_mail_str_48[] PROGMEM = "send header failed"; +static const char esp_mail_str_49[] PROGMEM = "send body failed"; +static const char esp_mail_str_50[] PROGMEM = "Connecting to IMAP server..."; +static const char esp_mail_str_51[] PROGMEM = + ".HEADER.FIELDS (SUBJECT FROM SENDER RETURN-PATH TO REPLY-TO DATE CC " + "Message-ID COMMENT KEYWORD content-type Content-transfer-encoding)]"; +static const char esp_mail_str_52[] PROGMEM = "failed"; +static const char esp_mail_str_53[] PROGMEM = "Error, "; +static const char esp_mail_str_54[] PROGMEM = "IMAP server connected"; +static const char esp_mail_str_55[] PROGMEM = "> C: download attachment"; +static const char esp_mail_str_56[] PROGMEM = "Logging in..."; +static const char esp_mail_str_57[] PROGMEM = "Downloading messages..."; +static const char esp_mail_str_58[] PROGMEM = + "Reading the list of mailboxes..."; +static const char esp_mail_str_59[] PROGMEM = + "> C: download plain TEXT message"; +static const char esp_mail_str_60[] PROGMEM = "> C: download HTML message"; +static const char esp_mail_str_61[] PROGMEM = "Selecting the "; +static const char esp_mail_str_62[] PROGMEM = "fail to list the mailboxes"; +static const char esp_mail_str_63[] PROGMEM = "fail to check the capabilities"; +static const char esp_mail_str_64[] PROGMEM = "Check the capability..."; +static const char esp_mail_str_65[] PROGMEM = "> C: check the capability"; +static const char esp_mail_str_66[] PROGMEM = "Searching messages..."; +static const char esp_mail_str_67[] PROGMEM = "message"; +static const char esp_mail_str_68[] PROGMEM = "Search limit:"; +static const char esp_mail_str_69[] PROGMEM = "Found "; +static const char esp_mail_str_70[] PROGMEM = " messages"; +static const char esp_mail_str_71[] PROGMEM = "Show "; +static const char esp_mail_str_72[] PROGMEM = + "No message found for search criteria"; +static const char esp_mail_str_73[] PROGMEM = + "Search criteria does not set, fetch the recent message"; +static const char esp_mail_str_74[] PROGMEM = "Fetching message "; +static const char esp_mail_str_75[] PROGMEM = ", UID: "; +static const char esp_mail_str_76[] PROGMEM = ", Number: "; +static const char esp_mail_str_77[] PROGMEM = "> C: fetch message header"; +static const char esp_mail_str_78[] PROGMEM = "Attachments ("; +static const char esp_mail_str_79[] PROGMEM = ")"; +static const char esp_mail_str_80[] PROGMEM = "Downloading attachments..."; +static const char esp_mail_str_81[] PROGMEM = "> C: fetch body part header, "; +static const char esp_mail_str_82[] PROGMEM = "rfc822"; +static const char esp_mail_str_83[] PROGMEM = "reading"; +static const char esp_mail_str_84[] PROGMEM = "Free Heap: "; +static const char esp_mail_str_85[] PROGMEM = "Logging out..."; +static const char esp_mail_str_86[] PROGMEM = + "> C: fetch body sub part header, "; +static const char esp_mail_str_87[] PROGMEM = "Finished reading Email"; +static const char esp_mail_str_88[] PROGMEM = "> C: finished reading Email"; +static const char esp_mail_str_89[] PROGMEM = "SD card mount failed"; +static const char esp_mail_str_90[] PROGMEM = "download"; +static const char esp_mail_str_91[] PROGMEM = ", "; +static const char esp_mail_str_92[] PROGMEM = "%"; +static const char esp_mail_str_93[] PROGMEM = "connection timeout"; +static const char esp_mail_str_94[] PROGMEM = ".html"; +static const char esp_mail_str_95[] PROGMEM = ".txt"; +static const char esp_mail_str_96[] PROGMEM = " folder..."; +static const char esp_mail_str_97[] PROGMEM = ";"; +static const char esp_mail_str_98[] PROGMEM = + "Content-Disposition: attachment\r\n"; +static const char esp_mail_str_99[] PROGMEM = "Date: "; +static const char esp_mail_str_100[] PROGMEM = "Messsage UID: "; +static const char esp_mail_str_101[] PROGMEM = "Messsage ID: "; +static const char esp_mail_str_102[] PROGMEM = "Accept Language: "; +static const char esp_mail_str_103[] PROGMEM = "Content Language: "; +static const char esp_mail_str_104[] PROGMEM = " BODY=BINARYMIME"; +static const char esp_mail_str_105[] PROGMEM = "From Charset: "; +static const char esp_mail_str_106[] PROGMEM = "BDAT "; +static const char esp_mail_str_107[] PROGMEM = "To Charset: "; +static const char esp_mail_str_108[] PROGMEM = "CC: "; +static const char esp_mail_str_109[] PROGMEM = "CC Charset: "; +static const char esp_mail_str_110[] PROGMEM = "delsp=\"no\""; +static const char esp_mail_str_111[] PROGMEM = "Subject Charset: "; +static const char esp_mail_str_112[] PROGMEM = "Message Charset: "; +static const char esp_mail_str_113[] PROGMEM = "Attachment: "; +static const char esp_mail_str_114[] PROGMEM = "File Index: "; +static const char esp_mail_str_115[] PROGMEM = "Filename: "; +static const char esp_mail_str_116[] PROGMEM = "Name: "; +static const char esp_mail_str_117[] PROGMEM = "Size: "; +static const char esp_mail_str_118[] PROGMEM = "Type: "; +static const char esp_mail_str_119[] PROGMEM = "Creation Date: "; +static const char esp_mail_str_120[] PROGMEM = "Connecting to SMTP server..."; +static const char esp_mail_str_121[] PROGMEM = + "SMTP server connected, wait for greeting..."; +static const char esp_mail_str_122[] PROGMEM = "Sending greeting response..."; +static const char esp_mail_str_123[] PROGMEM = "message/rfc822"; +static const char esp_mail_str_124[] PROGMEM = + "Saving message header to file..."; +static const char esp_mail_str_125[] PROGMEM = "Sending message header..."; +static const char esp_mail_str_126[] PROGMEM = "Sending message body..."; +static const char esp_mail_str_127[] PROGMEM = "Sending attachments..."; +static const char esp_mail_str_128[] PROGMEM = "Closing the session..."; +static const char esp_mail_str_129[] PROGMEM = "Message sent successfully"; +static const char esp_mail_str_130[] PROGMEM = "$ LOGIN "; +static const char esp_mail_str_131[] PROGMEM = " "; +static const char esp_mail_str_132[] PROGMEM = + "fail to set up the SSL/TLS structure"; +static const char esp_mail_str_133[] PROGMEM = "$ LIST \"\" *"; +static const char esp_mail_str_134[] PROGMEM = "Comment: "; +static const char esp_mail_str_135[] PROGMEM = "$ EXAMINE \""; +static const char esp_mail_str_136[] PROGMEM = "\""; +static const char esp_mail_str_137[] PROGMEM = "UID "; +static const char esp_mail_str_138[] PROGMEM = " UID"; +static const char esp_mail_str_139[] PROGMEM = " SEARCH"; +static const char esp_mail_str_140[] PROGMEM = "UID"; +static const char esp_mail_str_141[] PROGMEM = "SEARCH"; +static const char esp_mail_str_142[] PROGMEM = "$ UID FETCH "; +static const char esp_mail_str_143[] PROGMEM = "$ FETCH "; +static const char esp_mail_str_144[] PROGMEM = + "HEADER.FIELDS (SUBJECT FROM TO DATE CC Message-ID Accept-Language " + "content-type Content-transfer-encoding Content-Language)"; +static const char esp_mail_str_145[] PROGMEM = "Keyword: "; +static const char esp_mail_str_146[] PROGMEM = "$ LOGOUT"; +static const char esp_mail_str_147[] PROGMEM = " BODY"; +static const char esp_mail_str_148[] PROGMEM = ".MIME]"; +static const char esp_mail_str_149[] PROGMEM = "Bcc: "; +static const char esp_mail_str_150[] PROGMEM = "Sender: "; +static const char esp_mail_str_151[] PROGMEM = "no mailbox opened"; +static const char esp_mail_str_152[] PROGMEM = "."; +static const char esp_mail_str_153[] PROGMEM = "No mailbox opened"; +static const char esp_mail_str_154[] PROGMEM = "Remove FLAG"; +static const char esp_mail_str_155[] PROGMEM = "Add FLAG"; +static const char esp_mail_str_156[] PROGMEM = "]"; +static const char esp_mail_str_157[] PROGMEM = "Set FLAG"; +static const char esp_mail_str_158[] PROGMEM = + "file does not exist or can't access"; +static const char esp_mail_str_159[] PROGMEM = "msg.html"; +static const char esp_mail_str_160[] PROGMEM = "upload "; +static const char esp_mail_str_161[] PROGMEM = "/msg"; +static const char esp_mail_str_163[] PROGMEM = "/rfc822_msg"; +static const char esp_mail_str_164[] PROGMEM = "msg.txt"; +static const char esp_mail_str_165[] PROGMEM = "Content-Length: "; +static const char esp_mail_str_166[] PROGMEM = "binary"; +static const char esp_mail_str_167[] PROGMEM = "Sending inline data..."; +static const char esp_mail_str_168[] PROGMEM = "charset=\""; +static const char esp_mail_str_169[] PROGMEM = "charset="; +static const char esp_mail_str_170[] PROGMEM = "name=\""; +static const char esp_mail_str_171[] PROGMEM = "name="; +static const char esp_mail_str_172[] PROGMEM = "content-transfer-encoding: "; +static const char esp_mail_str_173[] PROGMEM = " LAST"; +static const char esp_mail_str_174[] PROGMEM = "content-description: "; +static const char esp_mail_str_175[] PROGMEM = "content-disposition: "; +static const char esp_mail_str_176[] PROGMEM = "filename=\""; +static const char esp_mail_str_177[] PROGMEM = "filename="; +static const char esp_mail_str_178[] PROGMEM = "size="; +static const char esp_mail_str_179[] PROGMEM = "creation-date=\""; +static const char esp_mail_str_180[] PROGMEM = "creation-date="; +static const char esp_mail_str_181[] PROGMEM = "modification-date=\""; +static const char esp_mail_str_182[] PROGMEM = "modification-date="; +static const char esp_mail_str_183[] PROGMEM = "*"; +static const char esp_mail_str_184[] PROGMEM = "Reply-To: "; +static const char esp_mail_str_185[] PROGMEM = "> E: "; +static const char esp_mail_str_186[] PROGMEM = "out of memory"; +static const char esp_mail_str_187[] PROGMEM = "Message fetch cmpleted"; +static const char esp_mail_str_188[] PROGMEM = "fail to close the mailbox"; +static const char esp_mail_str_189[] PROGMEM = "message-id: "; +static const char esp_mail_str_190[] PROGMEM = "accept-language: "; +static const char esp_mail_str_191[] PROGMEM = "content-language: "; +static const char esp_mail_str_192[] PROGMEM = ")"; +static const char esp_mail_str_193[] PROGMEM = "{"; +static const char esp_mail_str_194[] PROGMEM = "}"; +static const char esp_mail_str_195[] PROGMEM = "$ CLOSE\r\n"; +static const char esp_mail_str_196[] PROGMEM = "> C: send STARTTLS command"; +static const char esp_mail_str_197[] PROGMEM = "> C: close the mailbox folder"; +static const char esp_mail_str_198[] PROGMEM = "("; +static const char esp_mail_str_199[] PROGMEM = " EXISTS"; +static const char esp_mail_str_200[] PROGMEM = " [UIDNEXT "; +static const char esp_mail_str_201[] PROGMEM = "port > "; +static const char esp_mail_str_202[] PROGMEM = "/"; +static const char esp_mail_str_203[] PROGMEM = "/header.txt"; +static const char esp_mail_str_204[] PROGMEM = "/esp.32"; +static const char esp_mail_str_205[] PROGMEM = + "sender Email address is not valid"; +static const char esp_mail_str_206[] PROGMEM = + "some of the recipient Email address is not valid"; +static const char esp_mail_str_207[] PROGMEM = "> C: send Email"; +static const char esp_mail_str_208[] PROGMEM = "Sending Email..."; +static const char esp_mail_str_209[] PROGMEM = "Send command, STARTTLS"; +static const char esp_mail_str_210[] PROGMEM = "Closing the "; +static const char esp_mail_str_211[] PROGMEM = "host > "; +static const char esp_mail_str_212[] PROGMEM = "FLAGS"; +static const char esp_mail_str_213[] PROGMEM = "BODY"; +static const char esp_mail_str_214[] PROGMEM = "PEEK"; +static const char esp_mail_str_215[] PROGMEM = "TEXT"; +static const char esp_mail_str_216[] PROGMEM = "HEADER"; +static const char esp_mail_str_217[] PROGMEM = "FIELDS"; +static const char esp_mail_str_218[] PROGMEM = "["; +static const char esp_mail_str_219[] PROGMEM = "]"; +static const char esp_mail_str_220[] PROGMEM = "MIME"; +static const char esp_mail_str_221[] PROGMEM = "connection lost"; +static const char esp_mail_str_222[] PROGMEM = "set recipient failed"; +static const char esp_mail_str_223[] PROGMEM = " NEW"; +static const char esp_mail_str_224[] PROGMEM = "ALL"; +static const char esp_mail_str_225[] PROGMEM = "> C: connect to IMAP server"; +static const char esp_mail_str_226[] PROGMEM = "windows-874"; +static const char esp_mail_str_227[] PROGMEM = "iso-8859-1"; +static const char esp_mail_str_228[] PROGMEM = "> C: server connected"; +static const char esp_mail_str_229[] PROGMEM = "> C: send imap command, LOGIN"; +static const char esp_mail_str_230[] PROGMEM = "> C: send imap command, LIST"; +static const char esp_mail_str_231[] PROGMEM = "iso-8859-11"; +static const char esp_mail_str_232[] PROGMEM = "> C: search messages"; +static const char esp_mail_str_233[] PROGMEM = "> C: send imap command, FETCH"; +static const char esp_mail_str_234[] PROGMEM = "> C: send imap command, LOGOUT"; +static const char esp_mail_str_235[] PROGMEM = "> C: message fetch completed"; +static const char esp_mail_str_236[] PROGMEM = "> C: connect to SMTP server"; +static const char esp_mail_str_237[] PROGMEM = "tis-620"; +static const char esp_mail_str_238[] PROGMEM = "> C: smtp server connected"; +static const char esp_mail_str_239[] PROGMEM = "> C: send smtp command, HELO"; +static const char esp_mail_str_240[] PROGMEM = + "> C: send smtp command, AUTH LOGIN"; +static const char esp_mail_str_241[] PROGMEM = + "> C: send smtp command, AUTH PLAIN"; +static const char esp_mail_str_242[] PROGMEM = "> C: send message header"; +static const char esp_mail_str_243[] PROGMEM = "> C: send message body"; +static const char esp_mail_str_244[] PROGMEM = "> C: send attachments"; +static const char esp_mail_str_245[] PROGMEM = + "> C: terminate the SMTP session"; +static const char esp_mail_str_246[] PROGMEM = "> C: Message sent successfully"; +static const char esp_mail_str_247[] PROGMEM = "$ SELECT \""; +static const char esp_mail_str_248[] PROGMEM = "> C: open the mailbox folder"; +static const char esp_mail_str_249[] PROGMEM = "$ UID STORE "; +static const char esp_mail_str_250[] PROGMEM = " FLAGS ("; +static const char esp_mail_str_251[] PROGMEM = " +FLAGS ("; +static const char esp_mail_str_252[] PROGMEM = " -FLAGS ("; +static const char esp_mail_str_253[] PROGMEM = "> C: set FLAG"; +static const char esp_mail_str_254[] PROGMEM = "> C: add FLAG"; +static const char esp_mail_str_255[] PROGMEM = "> C: remove FLAG"; +static const char esp_mail_str_256[] PROGMEM = "could not parse flag"; +static const char esp_mail_str_257[] PROGMEM = "delsp=\"yes\""; +static const char esp_mail_str_258[] PROGMEM = "session timed out"; +static const char esp_mail_str_259[] PROGMEM = "delsp=yes"; +static const char esp_mail_str_260[] PROGMEM = "< S: "; +static const char esp_mail_str_261[] PROGMEM = "> C: "; +static const char esp_mail_str_262[] PROGMEM = " NOTIFY="; +static const char esp_mail_str_263[] PROGMEM = ","; +static const char esp_mail_str_264[] PROGMEM = "SUCCESS"; +static const char esp_mail_str_265[] PROGMEM = "FAILURE"; +static const char esp_mail_str_266[] PROGMEM = "DELAY"; +static const char esp_mail_str_267[] PROGMEM = "Sending next Email..."; +static const char esp_mail_str_268[] PROGMEM = "> C: send next Email"; +static const char esp_mail_str_269[] PROGMEM = + "header.fields (content-type Content-transfer-encoding)]"; +static const char esp_mail_str_270[] PROGMEM = "format=\"flowed\""; +static const char esp_mail_str_271[] PROGMEM = "> C: send inline data"; +static const char esp_mail_str_272[] PROGMEM = "Content-transfer-encoding: "; +static const char esp_mail_str_273[] PROGMEM = "Date: "; +static const char esp_mail_str_274[] PROGMEM = "Message-ID:"; +static const char esp_mail_str_275[] PROGMEM = "format=flowed"; +static const char esp_mail_str_276[] PROGMEM = "CC: "; +static const char esp_mail_str_277[] PROGMEM = "boundary=\""; +static const char esp_mail_str_278[] PROGMEM = "quoted-printable"; +static const char esp_mail_str_279[] PROGMEM = "Subject:"; +static const char esp_mail_str_280[] PROGMEM = "> C: no content"; +static const char esp_mail_str_281[] PROGMEM = "fail to open the mailbox"; +static const char esp_mail_str_282[] PROGMEM = "file I/O error"; +static const char esp_mail_str_283[] PROGMEM = "time.nist.gov"; +static const char esp_mail_str_284[] PROGMEM = + "log in was disabled for this server"; +static const char esp_mail_str_285[] PROGMEM = "user="; +static const char esp_mail_str_286[] PROGMEM = "\1auth=Bearer "; +static const char esp_mail_str_287[] PROGMEM = "\1\1"; +static const char esp_mail_str_288[] PROGMEM = + "> C: send smtp command, AUTH XOAUTH2"; +static const char esp_mail_str_289[] PROGMEM = "AUTH XOAUTH2 "; +static const char esp_mail_str_290[] PROGMEM = + "> C: send imap command, AUTHENTICATE PLAIN"; +static const char esp_mail_str_291[] PROGMEM = + "> C: send imap command, AUTH XOAUTH2"; +static const char esp_mail_str_292[] PROGMEM = "$ AUTHENTICATE XOAUTH2 "; +static const char esp_mail_str_293[] PROGMEM = + "OAuth2.0 log in was disabled for this server"; +static const char esp_mail_str_294[] PROGMEM = "{\"status\":"; +static const char esp_mail_str_295[] PROGMEM = "0123456789ABCDEF"; +static const char esp_mail_str_296[] PROGMEM = "pool.ntp.org"; +static const char esp_mail_str_297[] PROGMEM = + "Content-Type: multipart/alternative; boundary=\""; +static const char esp_mail_str_298[] PROGMEM = + "Content-Type: multipart/related; boundary=\""; +static const char esp_mail_str_299[] PROGMEM = + "Content-Disposition: inline; filename=\""; +static const char esp_mail_str_300[] PROGMEM = "Content-Location: "; +static const char esp_mail_str_301[] PROGMEM = "Content-ID: <"; +static const char esp_mail_str_302[] PROGMEM = "cid:"; +static const char esp_mail_str_303[] PROGMEM = + "Finishing the message sending..."; +static const char esp_mail_str_304[] PROGMEM = + "> C: Finish the message sending"; +static const char esp_mail_str_305[] PROGMEM = "connection failed"; +static const char esp_mail_str_306[] PROGMEM = + "some of the requested messages no longer exist"; +static const char esp_mail_str_307[] PROGMEM = "Reading messages..."; +static const char esp_mail_str_308[] PROGMEM = + "> C: reading plain TEXT message"; +static const char esp_mail_str_309[] PROGMEM = "> C: reading HTML message"; +static const char esp_mail_str_310[] PROGMEM = + "> C: performing the SSL/TLS handshake"; +static const char esp_mail_str_311[] PROGMEM = "STARTTLS\r\n"; +static const char esp_mail_str_312[] PROGMEM = "code: "; +static const char esp_mail_str_313[] PROGMEM = ", text: "; +static const char esp_mail_str_314[] PROGMEM = "> C: ESP Mail Client v"; +static const char esp_mail_str_315[] PROGMEM = " +FLAGS.SILENT (\\Deleted)"; +static const char esp_mail_str_316[] PROGMEM = "> C: delete message(s)"; +static const char esp_mail_str_317[] PROGMEM = "$ EXPUNGE"; +static const char esp_mail_str_318[] PROGMEM = "> C: copy message(s) to "; +static const char esp_mail_str_319[] PROGMEM = "$ UID COPY "; +static const char esp_mail_str_320[] PROGMEM = "> C: create folder"; +static const char esp_mail_str_321[] PROGMEM = "> C: delete folder"; +static const char esp_mail_str_322[] PROGMEM = "$ CREATE "; +static const char esp_mail_str_323[] PROGMEM = "$ DELETE "; +static const char esp_mail_str_324[] PROGMEM = "HEADER.FIELDS"; +static const char esp_mail_str_325[] PROGMEM = "flash content message"; +static const char esp_mail_str_326[] PROGMEM = "file content message"; +static const char esp_mail_str_327[] PROGMEM = "\"; size="; +static const char esp_mail_str_328[] PROGMEM = "0.0.0.0"; + +static const char esp_mail_smtp_response_1[] PROGMEM = "AUTH "; +static const char esp_mail_smtp_response_2[] PROGMEM = " LOGIN"; +static const char esp_mail_smtp_response_3[] PROGMEM = " PLAIN"; +static const char esp_mail_smtp_response_4[] PROGMEM = " XOAUTH2"; +static const char esp_mail_smtp_response_5[] PROGMEM = "STARTTLS"; +static const char esp_mail_smtp_response_6[] PROGMEM = "8BITMIME"; +static const char esp_mail_smtp_response_7[] PROGMEM = "BINARYMIME"; +static const char esp_mail_smtp_response_8[] PROGMEM = "CHUNKING"; +static const char esp_mail_smtp_response_9[] PROGMEM = "SMTPUTF8"; +static const char esp_mail_smtp_response_10[] PROGMEM = "PIPELINING"; +static const char esp_mail_smtp_response_11[] PROGMEM = " CRAM-MD5"; +static const char esp_mail_smtp_response_12[] PROGMEM = " DIGEST-MD5"; +static const char esp_mail_smtp_response_13[] PROGMEM = "DSN"; +// Tagged +static const char esp_mail_imap_response_1[] PROGMEM = "$ OK "; +static const char esp_mail_imap_response_2[] PROGMEM = "$ NO "; +static const char esp_mail_imap_response_3[] PROGMEM = "$ BAD "; +// Untagged +static const char esp_mail_imap_response_4[] PROGMEM = "* LIST "; +static const char esp_mail_imap_response_5[] PROGMEM = "* FLAGS "; +static const char esp_mail_imap_response_6[] PROGMEM = "* SEARCH "; +static const char esp_mail_imap_response_7[] PROGMEM = " FETCH "; +static const char esp_mail_imap_response_8[] PROGMEM = " NIL "; +static const char esp_mail_imap_response_9[] PROGMEM = " UID "; +static const char esp_mail_imap_response_10[] PROGMEM = "* CAPABILITY "; +static const char esp_mail_imap_response_11[] PROGMEM = "LOGINDISABLED"; +static const char esp_mail_imap_response_12[] PROGMEM = "AUTH=PLAIN"; +static const char esp_mail_imap_response_13[] PROGMEM = "AUTH=XOAUTH2"; +static const char esp_mail_imap_response_14[] PROGMEM = "STARTTLS"; +static const char esp_mail_imap_response_15[] PROGMEM = "CRAM-MD5"; +static const char esp_mail_imap_response_16[] PROGMEM = "DIGEST-MD5"; + +static const char imap_7bit_key1[] PROGMEM = "=20"; +static const char imap_7bit_val1[] PROGMEM = " "; +static const char imap_7bit_key2[] PROGMEM = "=2C"; +static const char imap_7bit_val2[] PROGMEM = ","; +static const char imap_7bit_key3[] PROGMEM = "=E2=80=99"; +static const char imap_7bit_val3[] PROGMEM = "'"; +static const char imap_7bit_key4[] PROGMEM = "=0A"; +static const char imap_7bit_val4[] PROGMEM = "\r\n"; +static const char imap_7bit_key5[] PROGMEM = "=0D"; +static const char imap_7bit_val5[] PROGMEM = "\r\n"; +static const char imap_7bit_key6[] PROGMEM = "=A0"; +static const char imap_7bit_val6[] PROGMEM = " "; +static const char imap_7bit_key7[] PROGMEM = "=B9"; +static const char imap_7bit_val7[] PROGMEM = "$sup1"; +static const char imap_7bit_key8[] PROGMEM = "=C2=A0"; +static const char imap_7bit_val8[] PROGMEM = " "; +static const char imap_7bit_key9[] PROGMEM = "=\r\n"; +static const char imap_7bit_val9[] PROGMEM = ""; +static const char imap_7bit_key10[] PROGMEM = "=E2=80=A6"; +static const char imap_7bit_val10[] PROGMEM = "…"; +static const char imap_7bit_key11[] PROGMEM = "=E2=80=A2"; +static const char imap_7bit_val11[] PROGMEM = "•"; +static const char imap_7bit_key12[] PROGMEM = "=E2=80=93"; +static const char imap_7bit_val12[] PROGMEM = "–"; +static const char imap_7bit_key13[] PROGMEM = "=E2=80=94"; +static const char imap_7bit_val13[] PROGMEM = "—"; + +static const unsigned char b64_index_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char boundary_table[] PROGMEM = + "=_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +__attribute__((used)) static bool compFunc(uint32_t i, uint32_t j) +{ + return (i > j); +} + +class MessageList +{ +public: + friend class IMAPSession; + MessageList(){}; + ~MessageList() { clear(); }; + void add(int uid) + { + if (uid > 0) + _list.push_back(uid); + } + + void clear() { _list.clear(); } + +private: + std::vector _list = std::vector(); +}; + +/* The class that provides the info of selected or opened mailbox folder */ +class SelectedFolderInfo +{ +public: + friend class ESP_Mail_Client; + friend class IMAPSession; + SelectedFolderInfo(){}; + ~SelectedFolderInfo() { clear(); }; + + /* Get the flags count for this mailbox */ + size_t flagCount() { return _flags.size(); }; + + /* Get the numbers of messages in this mailbox */ + size_t msgCount() { return _msgCount; }; + + /* Get the predict next message UID */ + size_t nextUID() { return _nextUID; }; + + /* Get the numbers of messages from search result based on the search criteria + */ + size_t searchCount() { return _searchCount; }; + + /* Get the numbers of messages to be stored in the ressult */ + size_t availableMessages() { return _availableItems; }; + + /* Get the flag argument at the specified index */ + String flag(size_t index) + { + if (index < _flags.size()) + return _flags[index].c_str(); + return ""; + } + +private: + void addFlag(const char *flag) { _flags.push_back(flag); }; + void clear() + { + for (size_t i = 0; i < _flags.size(); i++) + std::string().swap(_flags[i]); + _flags.clear(); + } + size_t _msgCount = 0; + size_t _nextUID = 0; + size_t _searchCount = 0; + size_t _availableItems = 0; + std::vector _flags = std::vector(); +}; + +/* The class that provides the list of FolderInfo e.g. name, attributes and + * delimiter */ +class FoldersCollection +{ +public: + friend class ESP_Mail_Client; + friend class IMAPSession; + FoldersCollection(){}; + ~FoldersCollection() { clear(); }; + size_t size() { return _folders.size(); }; + + struct esp_mail_folder_info_item_t info(size_t index) + { + struct esp_mail_folder_info_item_t fd; + if (index < _folders.size()) + { + fd.name = _folders[index].name.c_str(); + fd.attributes = _folders[index].attributes.c_str(); + fd.delimiter = _folders[index].delimiter.c_str(); + } + return fd; + } + +private: + void add(struct esp_mail_folder_info_t &fd) { _folders.push_back(fd); }; + void clear() + { + for (size_t i = 0; i < _folders.size(); i++) + { + if (_folders[i].name.length() > 0) + std::string().swap(_folders[i].name); + if (_folders[i].attributes.length() > 0) + std::string().swap(_folders[i].attributes); + if (_folders[i].delimiter.length() > 0) + std::string().swap(_folders[i].delimiter); + } + _folders.clear(); + } + std::vector _folders = + std::vector(); +}; + +/* The class that provides the status of message feching and searching */ +class IMAP_Status +{ +public: + IMAP_Status(); + ~IMAP_Status(); + const char *info(); + bool success(); + void empty(); + friend class IMAPSession; + + std::string _info = ""; + bool _success = false; +}; + +/* The SMTP message class */ +class SMTP_Message +{ +public: + SMTP_Message(){}; + ~SMTP_Message() { clear(); }; + + void resetAttachItem(SMTP_Attachment &att) + { + att.blob.size = 0; + att.blob.data = nullptr; + att.file.path = ""; + att.file.storage_type = esp_mail_file_storage_type_none; + att.descr.name = ""; + att.descr.filename = ""; + att.descr.transfer_encoding = ""; + att.descr.content_encoding = ""; + att.descr.mime = ""; + att.descr.content_id = ""; + att._int.att_type = esp_mail_att_type_none; + att._int.index = 0; + att._int.msg_uid = 0; + att._int.flash_blob = false; + att._int.binary = false; + att._int.parallel = false; + att._int.cid = ""; + } + + void clear() + { + sender.name = ""; + sender.email = ""; + subject = ""; + text.charSet = ""; + text.content = ""; + text.content_type = ""; + text.embed.enable = false; + html.charSet = ""; + html.content = ""; + html.content_type = ""; + html.embed.enable = false; + response.reply_to = ""; + response.notify = esp_mail_smtp_notify::esp_mail_smtp_notify_never; + priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + for (size_t i = 0; i < _rcp.size(); i++) + { + _rcp[i].name = ""; + _rcp[i].email = ""; + } + + for (size_t i = 0; i < _cc.size(); i++) + _cc[i].email = ""; + + for (size_t i = 0; i < _bcc.size(); i++) + _bcc[i].email = ""; + + for (size_t i = 0; i < _hdr.size(); i++) + _hdr[i] = ""; + + for (size_t i = 0; i < _att.size(); i++) + { + _att[i].descr.filename = ""; + _att[i].blob.data = nullptr; + _att[i].descr.mime = ""; + _att[i].descr.name = ""; + _att[i].blob.size = 0; + _att[i].descr.transfer_encoding = ""; + _att[i].file.path = ""; + _att[i].file.storage_type = esp_mail_file_storage_type_none; + } + + for (size_t i = 0; i < _parallel.size(); i++) + { + _parallel[i].descr.filename = ""; + _parallel[i].blob.data = nullptr; + _parallel[i].descr.mime = ""; + _parallel[i].descr.name = ""; + _parallel[i].blob.size = 0; + _parallel[i].descr.transfer_encoding = ""; + _parallel[i].file.path = ""; + _parallel[i].file.storage_type = esp_mail_file_storage_type_none; + } + _rcp.clear(); + _cc.clear(); + _bcc.clear(); + _hdr.clear(); + _att.clear(); + _parallel.clear(); + } + + /** Clear all the inline images + */ + void clearInlineimages() + { + for (int i = (int)_att.size() - 1; i >= 0; i--) + { + if (_att[i]._int.att_type == esp_mail_att_type_inline) + _att.erase(_att.begin() + i); + } + }; + + /* Clear all the attachments */ + void clearAttachments() + { + for (int i = (int)_att.size() - 1; i >= 0; i--) + { + if (_att[i]._int.att_type == esp_mail_att_type_attachment) + _att.erase(_att.begin() + i); + } + + for (int i = (int)_parallel.size() - 1; i >= 0; i--) + _parallel.erase(_parallel.begin() + i); + }; + + /** Clear all rfc822 message attachment + */ + void clearRFC822Messages() + { + for (int i = (int)_rfc822.size() - 1; i >= 0; i--) + { + _rfc822[i].clear(); + _rfc822.erase(_rfc822.begin() + i); + } + }; + + /** Clear the primary recipient mailboxes + */ + void clearRecipients() { _rcp.clear(); }; + + /** Clear the Carbon-copy recipient mailboxes + */ + void clearCc() { _cc.clear(); }; + + /** Clear the Blind-carbon-copy recipient mailboxes + */ + void clearBcc() { _bcc.clear(); }; + + /** Clear the custom message headers + */ + void clearHeader() { _hdr.clear(); }; + + /** Add attachment to the message + * + * @param att The SMTP_Attachment data item + */ + void addAttachment(SMTP_Attachment &att) + { + att._int.att_type = esp_mail_att_type_attachment; + att._int.parallel = false; + att._int.flash_blob = true; + _att.push_back(att); + }; + + /** Add parallel attachment to the message + * + * @param att The SMTP_Attachment data item + */ + void addParallelAttachment(SMTP_Attachment &att) + { + att._int.att_type = esp_mail_att_type_attachment; + att._int.parallel = true; + att._int.flash_blob = true; + _parallel.push_back(att); + }; + + /** Add inline image to the message + * + * @param att The SMTP_Attachment data item + */ + void addInlineImage(SMTP_Attachment &att) + { + att._int.flash_blob = true; + att._int.parallel = false; + att._int.att_type = esp_mail_att_type_inline; + char *tmp = new char[36]; + memset(tmp, 0, 36); + itoa(random(10000000, 20000000), tmp, 10); + att._int.cid = tmp; + delete[] tmp; + _att.push_back(att); + }; + + /** Add rfc822 message to the message + * + * @param msg The RFC822_Message class object + */ + void addMessage(SMTP_Message &msg) { _rfc822.push_back(msg); } + + /** Add the primary recipient mailbox to the message + * + * @param name The name of primary recipient + * @param email The Email address of primary recipient + */ + void addRecipient(const char *name, const char *email) + { + struct esp_mail_smtp_recipient_t rcp; + rcp.name = name; + rcp.email = email; + _rcp.push_back(rcp); + }; + + /** Add Carbon-copy recipient mailbox + * + * @param email The Email address of the secondary recipient + */ + void addCc(const char *email) + { + struct esp_mail_smtp_recipient_address_t cc; + cc.email = email; + _cc.push_back(cc); + }; + + /** Add Blind-carbon-copy recipient mailbox + * + * @param email The Email address of the tertiary recipient + */ + void addBcc(const char *email) + { + struct esp_mail_smtp_recipient_address_t bcc; + bcc.email = email; + _bcc.push_back(bcc); + }; + + /** Add the custom header to the message + * + * @param hdr The header name and value + */ + void addHeader(const char *hdr) { _hdr.push_back(hdr); }; + + /* The message author config */ + struct esp_mail_email_info_t sender; + + /* The topic of message */ + const char *subject = ""; + + /* The message type */ + byte type = esp_mail_msg_type_none; + + /* The PLAIN text message */ + struct esp_mail_plain_body_t text; + + /* The HTML text message */ + struct esp_mail_html_body_t html; + + /* The response config */ + struct esp_mail_smtp_msg_response_t response; + + /* The priority of the message */ + esp_mail_smtp_priority priority = + esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + + /* The enable options */ + struct esp_mail_smtp_enable_option_t enable; + + /* The message from config */ + struct esp_mail_email_info_t from; + + /* The message identifier */ + const char *messageID = ""; + + /* The keywords or phrases, separated by commas */ + const char *keyword = ""; + + /* The comment about message */ + const char *comment = ""; + + /* The date of message */ + const char *date = ""; + + /* The return recipient of the message */ + const char *return_path = ""; + +private: + friend class ESP_Mail_Client; + std::vector _rcp = + std::vector(); + std::vector _cc = + std::vector(); + std::vector _bcc = + std::vector(); + std::vector _hdr = std::vector(); + std::vector _att = std::vector(); + std::vector _parallel = std::vector(); + std::vector _rfc822 = std::vector(); +}; + +class SMTP_Status +{ +public: + friend class SMTPSession; + friend class ESP_Mail_Client; + + SMTP_Status(); + ~SMTP_Status(); + const char *info(); + bool success(); + void empty(); + size_t completedCount(); + size_t failedCount(); + +private: + std::string _info = ""; + bool _success = false; + size_t _sentSuccess = 0; + size_t _sentFailed = 0; +}; + +typedef void (*imapStatusCallback)(IMAP_Status); +typedef void (*smtpStatusCallback)(SMTP_Status); + +class ESP_Mail_Client +{ + +public: + /** Sending Email through the SMTP server + * + * @param smtp The pointer to SMTP session object which holds the data and the + * TCP client. + * @param msg The pointer to SMTP_Message class which contains the header, + * body, and attachments. + * @param closeSession The option to Close the SMTP session after sent. + * @return The boolean value indicates the success of operation. + */ + bool sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession = true); + + /** Reading Email through IMAP server. + * + * @param imap The pointer to IMAP sesssion object which holds the data and + the TCP client. + + * @param closeSession The option to close the IMAP session after fetching or + searching the Email. + * @return The boolean value indicates the success of operation. + */ + bool readMail(IMAPSession *imap, bool closeSession = true); + + /** Set the argument to the Flags for the specified message. + * + * @param imap The pointer to IMAP session object which holds the data and the + * TCP client. + * @param msgUID The UID of the message. + * @param flags The flag list to set. + * @param closeSession The option to close the IMAP session after set flag. + * @return The boolean value indicates the success of operation. + */ + bool setFlag(IMAPSession *imap, int msgUID, const char *flags, + bool closeSession); + + /** Add the argument to the Flags for the specified message. + * + * @param imap The pointer to IMAP session object which holds the data and the + * TCP client. + * @param msgUID The UID of the message. + * @param flags The flag list to set. + * @param closeSession The option to close the IMAP session after add flag. + * @return The boolean value indicates the success of operation. + */ + bool addFlag(IMAPSession *imap, int msgUID, const char *flags, + bool closeSession); + + /** Remove the argument from the Flags for the specified message. + * + * @param imap The pointer to IMAP session object which holds the data and the + * TCP client. + * @param msgUID The UID of the message that flags to be removed. + * @param flags The flag list to remove. + * @param closeSession The option to close the IMAP session after remove flag. + * @return The boolean value indicates the success of operation. + */ + bool removeFlag(IMAPSession *imap, int msgUID, const char *flags, + bool closeSession); + + /** Initialize the SD card with the SPI port. + * + * @param sck The SPI Clock pin (ESP32 only). + * @param miso The SPI MISO pin (ESSP32 only). + * @param mosi The SPI MOSI pin (ESP32 only). + * @param ss The SPI Chip/Slave Select pin (ESP32 and ESP8266). + * @return The boolean value indicates the success of operation. + */ + bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); + + /** Initialize the SD card with the default SPI port. + * + * @return The boolean value which indicates the success of operation. + */ + bool sdBegin(); + + /** Initialize the SD_MMC card (ESSP32 only). + * + * @param mountpoint The mounting point. + * @param mode1bit Allow 1 bit data line. + * @param format_if_mount_failed Format SD_MMC card if mount failed. + * @return The boolean value indicates the success of operation. + */ + bool sdMMCBegin(const char *mountpoint = "/sdcard", bool mode1bit = false, bool format_if_mount_failed = false); + + + ESPTimeHelper Time; + +private: + friend class SMTPSession; + friend class IMAPSession; +#if defined(ESP8266) + void setClock(float offset); +#endif + + RFC2047_Decoder RFC2047Decoder; + File file; + + bool _sdOk = false; + bool _flashOk = false; + bool _sdConfigSet = false; + uint8_t _sck, _miso, _mosi, _ss; + const char *sd_mmc_mountpoint = ""; + bool sd_mmc_mode1bit = false; + bool sd_mmc_format_if_mount_failed = false; + +#if defined(ESP8266) + bool _clockReady = false; + uint8_t _sdPin = SD_CS_PIN; + float _gmtOffset = 0.0; +#endif + + unsigned long _lastReconnectMillis = 0; + uint16_t _reconnectTimeout = ESP_MAIL_WIFI_RECONNECT_TIMEOUT; + + bool _sendMail(SMTPSession *smtp, SMTP_Message *msg, + bool closeSession = true); + bool ethLinkUp(); + bool reconnect(SMTPSession *smtp, unsigned long dataTime = 0); + bool reconnect(IMAPSession *imap, unsigned long dataTime = 0, + bool downloadRequestuest = false); + void closeTCP(SMTPSession *smtp); + void closeTCP(IMAPSession *imap); + void getMIME(const char *ext, std::string &mime); + void mimeFromFile(const char *name, std::string &mime); +#if defined(ESP32) + void setSecure(ESP_Mail_HTTPClient32 &httpClient, ESP_Mail_Session *session, + std::shared_ptr caCert); +#elif defined(ESP8266) + void setSecure(ESP_Mail_HTTPClient &httpClient, ESP_Mail_Session *session, + std::shared_ptr caCert); +#endif + void delS(char *p); + char *newS(size_t len); + char *newS(char *p, size_t len); + char *newS(char *p, size_t len, char *d); + bool strcmpP(const char *buf, int ofs, PGM_P beginH); + int strposP(const char *buf, PGM_P beginH, int ofs); + char *strP(PGM_P pgm); + void appendP(std::string &buf, PGM_P p, bool empty); + char *intStr(int value); + void errorStatusCB(SMTPSession *smtp, int error); + size_t smtpSendP(SMTPSession *smtp, PGM_P v, bool newline = false); + size_t smtpSend(SMTPSession *smtp, const char *data, bool newline = false); + size_t smtpSend(SMTPSession *smtp, int data, bool newline = false); + size_t smtpSend(SMTPSession *smtp, uint8_t *data, size_t size); + bool getMultipartFechCmd(IMAPSession *imap, int msgIdx, + std::string &partText); + bool multipartMember(const std::string &part, const std::string &check); + bool fetchMultipartBodyHeader(IMAPSession *imap, int msgIdx); + bool connected(IMAPSession *imap); + bool imapAuth(IMAPSession *imap); + bool sendIMAPCommand(IMAPSession *imap, int msgIndex, int cmdCase); + bool handleSMTPError(SMTPSession *smtp, int err, bool ret = false); + void errorStatusCB(IMAPSession *imap, int error); + size_t imapSendP(IMAPSession *imap, PGM_P v, bool newline = false); + size_t imapSend(IMAPSession *imap, const char *data, bool nwline = false); + size_t imapSend(IMAPSession *imap, int data, bool newline = false); + std::string getBoundary(size_t len); + std::string getEncodedToken(IMAPSession *imap); + bool imapLogout(IMAPSession *imap); + bool sendParallelAttachments(SMTPSession *smtp, SMTP_Message *msg, + const std::string &boundary); + bool sendAttachments(SMTPSession *smtp, SMTP_Message *msg, + const std::string &boundary, bool parallel = false); + + bool sendMSGData(SMTPSession *smtp, SMTP_Message *msg, bool closeSession, + bool rfc822MSG); + bool sendRFC822Msg(SMTPSession *smtp, SMTP_Message *msg, + const std::string &boundary, bool closeSession, + bool rfc822MSG); + void getRFC822MsgEnvelope(SMTPSession *smtp, SMTP_Message *msg, + std::string &buf); + bool bdat(SMTPSession *smtp, SMTP_Message *msg, int len, bool last); + void checkBinaryData(SMTPSession *smtp, SMTP_Message *msg); + bool sendBlob(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att); + bool sendFile(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, + File &file); + bool openFileRead(SMTPSession *smtp, SMTP_Message *msg, SMTP_Attachment *att, + File &file, std::string &s, std::string &buf, + const std::string &boundary, bool inlined); + bool openFileRead2(SMTPSession *smtp, SMTP_Message *msg, File &file, + const char *path, esp_mail_file_storage_type storageType); + bool sendInline(SMTPSession *smtp, SMTP_Message *msg, + const std::string &boundary, byte type); + void debugInfoP(PGM_P info); + size_t numAtt(SMTPSession *smtp, esp_mail_attach_type type, + SMTP_Message *msg); + bool validEmail(const char *s); + bool checkEmail(SMTPSession *smtp, SMTP_Message *msg); + bool sendPartText(SMTPSession *smtp, SMTP_Message *msg, byte type, + const char *boundary); + char *getUID(); + bool sendBlobBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type); + bool sendFileBody(SMTPSession *smtp, SMTP_Message *msg, uint8_t type); + void encodingText(SMTPSession *smtp, SMTP_Message *msg, uint8_t type, + std::string &content); + void splitTk(std::string &str, std::vector &tk, + const char *delim); + void formatFlowedText(std::string &content); + void softBreak(std::string &content, const char *quoteMarks); + bool sendMSG(SMTPSession *smtp, SMTP_Message *msg, + const std::string &boundary); + void getAttachHeader(std::string &header, const std::string &boundary, + SMTP_Attachment *attach, size_t size); + void getRFC822PartHeader(SMTPSession *smtp, std::string &header, + const std::string &boundary); + void getInlineHeader(std::string &header, const std::string &boundary, + SMTP_Attachment *inlineAttach, size_t size); + unsigned char *decodeBase64(const unsigned char *src, size_t len, + size_t *out_len); + std::string encodeBase64Str(const unsigned char *src, size_t len); + std::string encodeBase64Str(uint8_t *src, size_t len); + void encodeQP(const char *buf, char *out); + bool sendBase64(SMTPSession *smtp, SMTP_Message *msg, + const unsigned char *data, size_t len, bool flashMem, + const char *filename, bool report); + bool sendBase64Stream(SMTPSession *smtp, SMTP_Message *msg, File file, + const char *filename, bool report); + void smtpCBP(SMTPSession *smtp, PGM_P info, bool success = false); + void smtpCB(SMTPSession *smtp, const char *info, bool success = false); + void imapCBP(IMAPSession *imap, PGM_P info, bool success); + void imapCB(IMAPSession *imap, const char *info, bool success); + int readLine(WiFiClient *stream, char *buf, int bufLen, bool crlf, + int &count); +#if defined(ESP32) + int _readLine(ESP_Mail_WCS32 *stream, char *buf, int bufLen, bool crlf, + int &count); +#elif defined(ESP8266) + int _readLine(ESP_Mail::ESP_Mail_WCS *stream, char *buf, int bufLen, + bool crlf, int &count); +#endif + int getMSGNUM(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, + bool &endSearch, int &nump, const char *key, const char *pc); + void handleHeader(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, + struct esp_mail_message_header_t &header, int &headerState, + int &octetCount); + void setHeader(IMAPSession *imap, char *buf, + struct esp_mail_message_header_t &header, int state); + void handlePartHeader(IMAPSession *imap, char *buf, int &chunkIdx, + struct esp_mail_message_part_info_t &part); + char *subStr(const char *buf, PGM_P beginH, PGM_P endH, int beginPos, + int endPos = 0); + struct esp_mail_message_part_info_t *cPart(IMAPSession *imap); + struct esp_mail_message_header_t *cHeader(IMAPSession *imap); + void strcat_c(char *str, char c); + int strpos(const char *haystack, const char *needle, int offset); + char *stristr(const char *str1, const char *str2); + char *rstrstr(const char *haystack, const char *needle); + int rstrpos(const char *haystack, const char *needle, int offset); + void getResponseStatus(const char *buf, esp_mail_smtp_status_code respCode, + int beginPos, + struct esp_mail_smtp_response_status_t &status); + void handleAuth(SMTPSession *smtp, char *buf); + std::string getEncodedToken(SMTPSession *smtp); + bool connected(SMTPSession *smtp); + bool setSendingResult(SMTPSession *smtp, SMTP_Message *msg, bool result); + bool smtpAuth(SMTPSession *smtp); + int available(SMTPSession *smtp); + bool handleSMTPResponse(SMTPSession *smtp, esp_mail_smtp_status_code respCode, + int errCode); + int available(IMAPSession *imap); + bool handleIMAPResponse(IMAPSession *imap, int errCode, bool closeSession); + void downloadReport(IMAPSession *imap, int progress); + void fetchReport(IMAPSession *imap, int progress, bool html); + void searchReport(int progress, const char *percent); + void uploadReport(const char *filename, int progress); + int cMSG(IMAPSession *imap); + int cIdx(IMAPSession *imap); + esp_mail_imap_response_status imapResponseStatus(IMAPSession *imap, + char *response); + void saveHeader(IMAPSession *imap); + esp_mail_char_decoding_scheme getEncodingFromCharset(const char *enc); + void decodeHeader(std::string &headerField, std::string &headerEnc); + bool handleAttachment(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, + File &file, std::string &filePath, + bool &downloadRequest, int &octetCount, + int &octetLength, int &oCount, int &reportState, + int &downloadCount); + int decodeLatin1_UTF8(unsigned char *out, int *outlen, + const unsigned char *in, int *inlen); + void decodeTIS620_UTF8(char *out, const char *in, size_t len); + void decodeText(IMAPSession *imap, char *buf, int bufLen, int &chunkIdx, + File &file, std::string &filePath, bool &downloadRequest, + int &octetLength, int &readDataLen, int &readCount); + void prepareFilePath(IMAPSession *imap, std::string &filePath, bool header); + int decodeChar(const char *s); + void decodeQP(const char *buf, char *out); + char *decode7Bit(char *buf); + char *strReplace(char *orig, char *rep, char *with); + char *strReplaceP(char *buf, PGM_P key, PGM_P value); + bool authFailed(char *buf, int bufLen, int &chunkIdx, int ofs); + void handleFolders(IMAPSession *imap, char *buf); + void handleCapability(IMAPSession *imap, char *buf, int &chunkIdx); + void handleExamine(IMAPSession *imap, char *buf); + bool handleIMAPError(IMAPSession *imap, int err, bool ret); + bool _setFlag(IMAPSession *imap, int msgUID, const char *flags, + uint8_t action, bool closeSession); + void createDirs(std::string dirs); + bool sdTest(); +}; + +class IMAPSession +{ +public: + IMAPSession(); + ~IMAPSession(); + + /** Begin the IMAP server connection. + * + * @param session The pointer to ESP_Mail_Session structured data that keeps + * the server and log in details. + * @param config The pointer to IMAP_Config structured data that keeps the + * operation options. + * @return The boolean value which indicates the success of operation. + */ + bool connect(ESP_Mail_Session *session, IMAP_Config *config); + + /** Close the IMAP session. + * + * @return The boolean value which indicates the success of operation. + */ + bool closeSession(); + + /** Set to enable the debug. + * + * @param level The level to enable the debug message + * level = 0, no debug + * level = 1, basic debug + * level = 2, full debug 1 + * level = 333, full debug 2 + */ + void debug(int level); + + /** Get the list of all the mailbox folders since the TCP session was opened + * and user was authenticated. + * + * @param folders The FoldersCollection class that contains the collection of + * the + * FolderInfo structured data. + * @return The boolean value which indicates the success of operation. + */ + bool getFolders(FoldersCollection &folders); + + /** Select or open the mailbox folder to search or fetch the message inside. + * + * @param folderName The known mailbox folder name. The default name is INBOX. + * @param readOnly The option to open the mailbox for read only. Set this + * option to false when you wish + * to modify the Flags using the setFlag, addFlag and removeFlag functions. + * @return The boolean value which indicates the success of operation. + */ + bool selectFolder(const char *folderName, bool readOnly = true); + + /** Open the mailbox folder to read or search the mesages. + * + * @param folderName The name of known mailbox folder to be opened. + * @param readOnly The option to open the mailbox for reading only. Set this + * option to false when you wish + * to modify the flags using the setFlag, addFlag and removeFlag functions. + * @return The boolean value which indicates the success of operation. + */ + bool openFolder(const char *folderName, bool readOnly = true); + + /** Close the mailbox folder that was opened. + * + * @param folderName The known mailbox folder name. + * @return The boolean value which indicates the success of operation. + */ + bool closeFolder(const char *folderName); + + /** Create folder. + * + * @param folderName The name of folder to create. + * @return The boolean value which indicates the success of operation. + */ + bool createFolder(const char *folderName); + + /** Delete folder. + * + * @param folderName The name of folder to delete. + * @return The boolean value which indicates the success of operation. + */ + bool deleteFolder(const char *folderName); + + /** Copy the messages to the defined mailbox folder. + * + * @param toCopy The pointer to the MessageListList class that contains the + * list of messages to copy. + * @param dest The destination folder that the messages to copy to. + * @return The boolean value which indicates the success of operation. + */ + bool copyMessages(MessageList *toCopy, const char *dest); + + /** Delete the messages in the opened mailbox folder. + * + * @param toDelete The pointer to the MessageListList class that contains the + * list of messages to delete. + * @param expunge The boolean option to expunge all messages. + * @return The boolean value which indicates the success of operation. + */ + bool deleteMessages(MessageList *toDelete, bool expunge = false); + + /** Assign the callback function that returns the operating status when + * fetching or reading the Email. + * + * @param imapCallback The function that accepts the imapStatusCallback as + * parameter. + */ + void callback(imapStatusCallback imapCallback); + + /** Determine if no message body contained in the search result and only the + * message header is available. + */ + bool headerOnly(); + + /** Get the message list from search or fetch the Emails + * + * @return The IMAP_MSG_List structured data which contains text and html + * contents, + * attachments, inline images, embedded rfc822 messages details for each + * message. + */ + IMAP_MSG_List data(); + + /** Get the details of the selected or opned mailbox folder + * + * @return The SelectedFolderInfo class which contains the info about flags, + * total messages, next UID, + * search count and the available messages count. + */ + SelectedFolderInfo selectedFolder(); + + /** Get the error details when readingg the Emails + * + * @return The string of error details. + */ + String errorReason(); + + /** Clear all the cache data stored in the IMAP session object. + */ + void empty(); + + friend class ESP_Mail_Client; + friend class foldderList; + +private: + void clearMessageData(); + void checkUID(); + void checkPath(); + void getMessages(uint16_t messageIndex, struct esp_mail_imap_msg_item_t &msg); + void getRFC822Messages(uint16_t messageIndex, + struct esp_mail_imap_msg_item_t &msg); + bool closeMailbox(); + bool openMailbox(const char *folder, esp_mail_imap_auth_mode mode, + bool waitResponse); + bool getMailboxes(FoldersCollection &flders); + bool checkCapability(); + + bool _tcpConnected = false; + struct esp_mail_imap_response_status_t _imapStatus; + int _cMsgIdx = 0; + int _cPartIdx = 0; + int _totalRead = 0; + std::vector _headers = + std::vector(); + + esp_mail_imap_command _imap_cmd = + esp_mail_imap_command::esp_mail_imap_cmd_login; + // std::string _partNumStr = ""; + std::vector _multipart_levels = + std::vector(); + int _rfc822_part_count = 0; + esp_mail_file_storage_type _storageType = + esp_mail_file_storage_type::esp_mail_file_storage_type_flash; + bool _unseen = false; + bool _readOnlyMode = true; + struct esp_mail_auth_capability_t _auth_capability; + ESP_Mail_Session *_sesson_cfg; + std::string _currentFolder = ""; + bool _mailboxOpened = false; + std::string _nextUID = ""; + + struct esp_mail_imap_read_config_t *_config = nullptr; + + bool _headerOnly = true; + bool _uidSearch = false; + bool _headerSaved = false; + bool _debug = false; + int _debugLevel = 0; + bool _secure = false; + imapStatusCallback _readCallback = NULL; + + std::vector _msgNum = std::vector(); + FoldersCollection _folders; + SelectedFolderInfo _mbif; + + int _certType = -1; + std::shared_ptr _caCert = nullptr; + +#if defined(ESP32) + ESP_Mail_HTTPClient32 httpClient; +#elif defined(ESP8266) + ESP_Mail_HTTPClient httpClient; +#endif + + IMAP_Status _cbData; +}; + +class SendingResult +{ +private: + std::vector _result = + std::vector(); + void add(struct esp_mail_smtp_send_status_t r) + { + struct esp_mail_smtp_send_status_t _r = r; + _result.push_back(_r); + } + void clear() + { + for (size_t i = 0; i < _result.size(); i++) + { + _result[i].recipients = ""; + _result[i].subject = ""; + _result[i].timesstamp = 0; + _result[i].completed = false; + } + _result.clear(); + } + +public: + friend class SMTPSession; + friend class ESP_Mail_Client; + SendingResult(){}; + ~SendingResult() { clear(); }; + SMTP_Result getItem(size_t index) + { + struct esp_mail_smtp_send_status_t r; + if (index < _result.size()) + return _result[index]; + return r; + } + size_t size() { return _result.size(); }; +}; + +class SMTPSession +{ +public: + SMTPSession(); + ~SMTPSession(); + + /** Begin the SMTP server connection. + * + * @param session The pointer to ESP_Mail_Session structured data that keeps + * the server and log in details. + * @return The boolean value indicates the success of operation. + */ + bool connect(ESP_Mail_Session *session); + + /** Close the SMTP session. + * + */ + bool closeSession(); + + /** Set to enable the debug. + * + * @param level The level to enable the debug message + * level = 0, no debug + * level = 1, basic debug + * level = 2, full debug 1 + * level = 333, full debug 2 + */ + void debug(int level); + + /** Get the error details when sending the Email + * + * @return The string of error details. + */ + String errorReason(); + + /** Set the Email sending status callback function. + * + * @param smtpCallback The callback function that accept the + * smtpStatusCallback param. + */ + void callback(smtpStatusCallback smtpCallback); + + SendingResult sendingResult; + + friend class ESP_Mail_Client; + +private: + bool _tcpConnected = false; + struct esp_mail_smtp_response_status_t _smtpStatus; + int _sentSuccessCount = 0; + int _sentFailedCount = 0; + bool _chunkedEnable = false; + int _chunkCount = 0; + + esp_mail_smtp_command _smtp_cmd = + esp_mail_smtp_command::esp_mail_smtp_cmd_greeting; + struct esp_mail_auth_capability_t _auth_capability; + struct esp_mail_smtp_capability_t _send_capability; + ESP_Mail_Session *_sesson_cfg = NULL; + + bool _debug = false; + int _debugLevel = 0; + bool _secure = false; + smtpStatusCallback _sendCallback = NULL; + + SMTP_Status _cbData; + struct esp_mail_smtp_msg_type_t _msgType; + + int _certType = -1; + std::shared_ptr _caCert = nullptr; + +#if defined(ESP32) + ESP_Mail_HTTPClient32 httpClient; +#elif defined(ESP8266) + ESP_Mail_HTTPClient httpClient; +#endif +}; + + +//#define EMAIL_DEBUG_PRINTLN Serial.println +//#define EMAIL_DEBUG_PRINT Serial.print +#define EMAIL_DEBUG_PRINTLN Tasmota_print +#define EMAIL_DEBUG_PRINT Tasmota_print + +extern void Tasmota_print(const char *); + +static void __attribute__((used)) esp_mail_debug(const char *msg) +{ + delay(0); + EMAIL_DEBUG_PRINTLN(msg); +} + +static void __attribute__((used)) +esp_mail_debug_line(const char *msg, bool newline) +{ + delay(0); + if (newline) + EMAIL_DEBUG_PRINTLN(msg); + else + EMAIL_DEBUG_PRINT(msg); +} + +extern ESP_Mail_Client MailClient; + +extern FS *ufsp; + +#endif // ESP_Mail_Client_H diff --git a/lib/lib_div/lib_mail/src/ESP_Mail_FS.h b/lib/libesp32/lib_mail/src/ESP_Mail_FS.h similarity index 100% rename from lib/lib_div/lib_mail/src/ESP_Mail_FS.h rename to lib/libesp32/lib_mail/src/ESP_Mail_FS.h diff --git a/lib/lib_div/lib_mail/src/README.md b/lib/libesp32/lib_mail/src/README.md old mode 100755 new mode 100644 similarity index 94% rename from lib/lib_div/lib_mail/src/README.md rename to lib/libesp32/lib_mail/src/README.md index d83236eca..0e9c5094e --- a/lib/lib_div/lib_mail/src/README.md +++ b/lib/libesp32/lib_mail/src/README.md @@ -1,2031 +1,2031 @@ -# ESP Mail Client Arduino Library for ESP32 and ESP8266 - - -The detail and usage of the available functions in the latest version (1.2.0) are showed below. - - -## Global functions - - -#### Sending Email through the SMTP server. - -param **`smtp`** The pointer to SMTP session object which holds the data and the TCP client. - -param **`msg`** The pointer to SMTP_Message class which contains the header, body, and attachments. - -param **`closeSession`** The option to Close the SMTP session after sent. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession = true); -``` - - - - - -#### Reading Email through IMAP server. - -param **`imap`** The pointer to IMAP sesssion object which holds the data and the TCP client. - -param **`closeSession`** The option to close the IMAP session after fetching or searching the Email. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool readMail(IMAPSession *imap, bool closeSession = true); -``` - - - - - -#### Set the argument to the Flags for the specified message. - -param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. - -param **`msgUID`** The UID of the message. - -param **`flags`** The flag list to set. - -param **`closeSession`** The option to close the IMAP session after set flag. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool setFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); -``` - - - - - -#### Add the argument to the Flags for the specified message. - -param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. - -param **`msgUID`** The UID of the message. - -param **`flags`** The flag list to set. - -param **`closeSession`** The option to close the IMAP session after add flag. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool addFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); -``` - - - - - - -#### Remove the argument from the Flags for the specified message. - -param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. - -param **`msgUID`** The UID of the message that flags to be removed. - -param **`flags`** The flag list to remove. - -param **`closeSession`** The option to close the IMAP session after remove flag. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool removeFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); -``` - - - - - - -#### Initialize the SD card with the SPI port. - -param **`sck`** The SPI Clock pin (ESP32 only). - -param **`miso`** The SPI MISO pin (ESSP32 only). - -param **`mosi`** The SPI MOSI pin (ESP32 only). - -param **`ss`** The SPI Chip/Slave Select pin (ESP32 and ESP8266). - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); -``` - - - - - - -#### Initialize the SD_MMC card (ESP32 only). - -param **`mountpoint`** The mounting point. - -param **`mode1bit`** Allow 1 bit data line (SPI mode). - -param **`format_if_mount_failed`** Format SD_MMC card if mount failed. - -return **`Boolean`** type status indicates the success of the operation. - -```C++ -bool sdMMCBegin(const char *mountpoint = "/sdcard", bool mode1bit = false, bool format_if_mount_failed = false); -``` - - - - - - -#### Initialize the SD card with the default SPI port. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool sdBegin(void); -``` - - - - - - -## IMAPSession class functions - - -The following functions are available from the IMAP Session class. - -This class used for controlling IMAP transports and retrieving the data from the IMAP server. - - - - - -#### Begin the IMAP server connection. - -param **`session`** The pointer to ESP_Mail_Session structured data that keeps the server and log in details. - -param **`config`** The pointer to IMAP_Config structured data that keeps the operation options. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool connect(ESP_Mail_Session *session, IMAP_Config *config); -``` - - -#### Close the IMAP session. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool closeSession(); -``` - - - - - - -#### Set to enable the debug. - -param **`level`** The level to enable the debug message - -level = 0, no debug - -level = 1, basic debug - -level = 2, full debug 1 - -level = 333, full debug 2 - -```C++ -void debug(int level); -``` - - - - - -#### Get the list of all the mailbox folders since the TCP session was opened and user was authenticated. - -param **`folders`** The FoldersCollection class that contains the collection of the -FolderInfo structured data. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool getFolders(FoldersCollection &folders); -``` - - - - - -#### Select or open the mailbox folder to search or fetch the message inside. - -param **`folderName`** The known mailbox folder name. The default name is INBOX. - -param **`readOnly`** The option to open the mailbox for read only. Set this option to false when you wish -to modify the Flags using the setFlag, addFlag and removeFlag functions. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool selectFolder(const char *folderName, bool readOnly = true); -``` - - - - - -#### Open the mailbox folder to read or search the mesages. - -param **`folderName`** The name of known mailbox folder to be opened. - -param **`readOnly`** The option to open the mailbox for reading only. Set this option to false when you wish -to modify the flags using the setFlag, addFlag and removeFlag functions. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool openFolder(const char *folderName, bool readOnly = true); -``` - - - - - -#### Close the mailbox folder that was opened. - -param **`folderName`** The mailbox folder name. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool closeFolder(const char *folderName); -``` - - - - - - -#### Create folder. - -param **`folderName`** The name of folder to create. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool createFolder(const char *folderName); -``` - - - - - - -#### Delete folder. - -param **`folderName`** The name of folder to delete.. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool deleteFolder(const char *folderName); -``` - - - - - - -#### Copy the messages to the defined mailbox folder. - -param **`toCopy`** The pointer to the MessageList class that contains the list of messages to copy. - -param **`dest`** The destination folder that the messages to copy to. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool copyMessages(MessageList *toCopy, const char *dest); -``` - - - - - -#### Delete the messages in the opened mailbox folder. - -param **`toDelete`** The pointer to the MessageList class that contains the list of messages to delete. - -param **`expunge`** The boolean option to expunge all messages. - -return **`boolean`** The boolean value which indicates the success of operation. - -```C++ -bool deleteMessages(MessageList *toDelete, bool expunge = false); -``` - - - - - - - -#### Assign the callback function that returns the operating status when fetching or reading the Email. - -param **`imapCallback`** The function that accepts the imapStatusCallback as parameter. - -```C++ -void callback(imapStatusCallback imapCallback); -``` - - - - - -#### Determine if no message body contained in the search result and only the message header is available. - -```C++ -bool headerOnly(); -``` - - - - - -#### Get the message list from search or fetch the Emails - -return **`The IMAP_MSG_List structured`** data which contains the text and html contents, -attachments, inline images, embedded rfc822 messages details for each message. - -```C++ -IMAP_MSG_List data(); -``` - - - - - -#### Get the details of the selected or opned mailbox folder - -return **`The SelectedFolderInfo class`** instance which contains the info about flags, total messages, next UID, -earch count and the available messages count. - -```C++ -SelectedFolderInfo selectedFolder(); -``` - - - - - -#### Get the error details when readingg the Emails - -return **`String`** The string of error details. - -```C++ -String errorReason(); -``` - - - - - -#### Clear all the cache data stored in the IMAP session object. - -```C++ -void empty(); -``` - - - - - -## IMAPSession class functions - - -The following functions are available from the SMTP Session class. - -This class is similar to the IMAP session class, used for controlling SMTP transports -and retrieving the data from the SMTP server. - - - - - - -#### Begin the SMTP server connection. - -param **`session`** The pointer to ESP_Mail_Session structured data that keeps the server and log in details. - -return **`boolean`** The boolean value indicates the success of operation. - -```C++ -bool connect(ESP_Mail_Session *session); -``` - - - - - -### Close the SMTP session. - -```C++ -bool closeSession(); -``` - - - - - -#### Set to enable the debug. - -param **`level`** The level to enable the debug message - -level = 0, no debug - -level = 1, basic debug - -level = 2, full debug 1 - -level = 333, full debug 2 - -```C++ -void debug(int level); -``` - - - - - -#### Get the error details when sending the Email - -return **`String`** The string of error details. - -```C++ -String errorReason(); -``` - - - - - -#### Set the Email sending status callback function. - -param **`smtpCallback`** The callback function that accept the smtpStatusCallback param. - -```C++ -void callback(smtpStatusCallback smtpCallback); -``` - - - - - -## SMTP_Message class functions - - -The following functions are available from the SMTP Message class. - -This class is used for storing the message data including headers, body and attachments -which will be processed with the SMTP session class. - - - - -#### To reset the SMTP_Attachment item data - -param **`att`** The SMTP_Attachment class that stores the info about attachment - -This function was used for clear the internal data of attachment item to be ready for reuse. - -```C++ -void resetAttachItem(SMTP_Attachment &att); -``` - - - -#### To clear all data in SMTP_Message class included headers, bodies and attachments - -```C++ -void clear(); -``` - - - - -#### To clear all the inline images in SMTP_Message class. - -```C++ -void clearInlineimages(); -``` - - - - - -#### To clear all the attachments. - -```C++ -void clearAttachments(); -``` - - - - - -#### To clear all rfc822 message attachment. - -```C++ -void clearRFC822Messages(); -``` - - - - - -#### To clear the primary recipient mailboxes. - -```C++ -void clearRecipients(); -``` - - - - - -#### To clear the Carbon-copy recipient mailboxes. - -```C++ -void clearCc(); -``` - - - - - -#### To clear the Blind-carbon-copy recipient mailboxes. - -```C++ -void clearBcc(); -``` - - -#### To clear the custom message headers. - -```C++ -void clearHeader(); -``` - - - - -#### To add attachment to the message. - -param **`att`** The SMTP_Attachment data item - -```C++ -void addAttachment(SMTP_Attachment &att); -``` - - - - - -#### To add parallel attachment to the message. - -param **`att`** The SMTP_Attachment data item - -```C++ -void addParallelAttachment(SMTP_Attachment &att); -``` - - - - - -#### To add inline image to the message. - -param **`att`** The SMTP_Attachment data item - -```C++ -void addInlineImage(SMTP_Attachment &att); -``` - - - - - -#### To add rfc822 message to the message. - -param **`msg`** The RFC822_Message class object - -```C++ -void addMessage(SMTP_Message &msg); -``` - - - - - -#### To add the primary recipient mailbox to the message. - -param **`name`** The name of primary recipient - -param **`email`** The Email address of primary recipient - -```C++ -void addRecipient(const char *name, const char *email); -``` - - - - - -#### To add Carbon-copy recipient mailbox. - -param **`email`** The Email address of secondary recipient - -```C++ -void addCc(const char *email); -``` - - - - - -#### To add Blind-carbon-copy recipient mailbox. - -param **`email`** The Email address of the tertiary recipient - -```C++ -void addBcc(const char *email); -``` - - - - - -#### To add the custom header to the message. - -param **`hdr`** The header name and value - -```C++ -void addHeader(const char *hdr); -``` - - - - -##### [properties] The message author config - -This property has the sub properties - -###### [const char*] name - The sender name. - -###### [const char*] email - The sender Email address. - -```C++ -esp_mail_email_info_t sender; -``` - - -##### [properties] The topic of message - -```C++ -const char *subject; -``` - - -##### [properties] The message type - -```C++ -byte type; -``` - - -##### [properties] The PLAIN text message - -This property has the sub properties - -###### [esp_mail_smtp_embed_message_body_t] embed - The option to embed this message content as a file. - -###### [const char*] content - The PLAIN text content of the message. - -###### [esp_mail_blob_message_content_t] blob - The blob that contins PLAIN text content of the message. - -###### [esp_mail_file_message_content_t] file - The file that contins PLAIN text content of the message. - -###### [const char*] charSet - The character transcoding of the PLAIN text content of the message. - -###### [const char*] content_type - The content type of message. - -###### [const char*] transfer_encoding - The option to encode the content for data transfer. - -###### [boolean] flowed - The option to send the PLAIN text with wrapping. - -```C++ -esp_mail_plain_body_t text; -``` - - -##### [properties] The HTML text message - -This propery has the sub properties - -###### [const char*] content - The HTML content of the message. - -###### [esp_mail_blob_message_content_t] blob - The blob that contins HTML content of the message. - -###### [esp_mail_file_message_content_t] file - The file that contins HTML content of the message. - -###### [const char*] charSet - The character transcoding of the HTML content of the message. - -###### [const char*] content_type - The content type of message. - -###### [const char*] transfer_encoding - The option to encode the content for data transfer. - -```C++ -esp_mail_html_body_t html; -``` - - -##### [properties] The response config - -This propery has the sub properties - -###### [const char*] reply_to - The author Email address to reply. - -###### [const char*] return_path - The sender Email address to return the message. - -###### [int] notify - The Delivery Status Notifications enumeration e.g. - -esp_mail_smtp_notify_never = 0, - -esp_mail_smtp_notify_success = 1, - -esp_mail_smtp_notify_failure = 2, and - -esp_mail_smtp_notify_delay = 4 - -```C++ -esp_mail_smtp_msg_response_t response; -``` - - -##### [properties] The priority of the message - -This property has the enumeration values - -esp_mail_smtp_priority_high = 1, - -esp_mail_smtp_priority_normal = 3, - -esp_mail_smtp_priority_low = 5 - -```C++ -esp_mail_smtp_priority priority; -``` - - -##### [properties] The enable options - -This propery has the sub property - -###### [boolean] chunking - enable chunk data sending for large message. - -```C++ -esp_mail_smtp_enable_option_t enable; -``` - - -##### [properties] The message from config - -This property has the sub properties - -###### [const char*] name - The messsage author name. - -###### [const char*] email - The message author Email address. - -```C++ -esp_mail_email_info_t from; -``` - - -##### [properties] The message identifier - -```C++ -const char *messageID; -``` - -##### [properties] The keywords or phrases, separated by commas - -```C++ -const char *keyword; -``` - - -##### [properties] The comment about message - -```C++ -const char *comment; -``` - - -##### [properties] The date of message - -```C++ -const char *date; -``` - - -##### [properties] The return recipient of the message - -```C++ -const char *return_path; -``` - - - - - - -## IMAP_Status class functions - - -The following functions are available from the IMAP Status class. - -This class is used as the callback parameter for retrieving the status while reading the Email. - - - - -#### Provide the information of each process in the reading operation. - -return **`string`** The info for each process - -```C++ -const char *info(); -``` - - - - -#### Provide the status of completion. - -return **`boolean`** The bool value indicates that all reading processes are finished - -```C++ -bool success(); -``` - - - - - -#### To clear all data store in this class. - -```C++ -void empty(); -``` - - - - - - -## SMTP_Status class functions - - -The following functions are available from the SMTP Status class. - -This class is used as the callback parameter for retrieving the status while sending the Email. - - - - -#### Provide the information of each process in the sending operation. - -return **`string`** The info for each process - -```C++ -const char *info(); -``` - - - - -#### Provide the status of completion. - -return **`boolean`** The bool value indicates that all sending processes are finished - -```C++ -bool success(); -``` - - - - - -#### To clear all data store in this class. - -```C++ -void empty(); -``` - - - - -#### Provide the number of complete sending message. - -return **`number`** The number of message that was sent - -```C++ -size_t completedCount(); -``` - - - - - -#### Provide the number of failed sending message. - -return **`number`** The number of message that was not sent - -```C++ -size_t failedCount(); -``` - - - - -## SendingResult class functions - - -The following functions are available from the SendingResult class. - -This class is used for retrieving the info about the result of sending the messages. - - - - -#### Provide the information of a message sending status. - -param **`index`** The index number of a message sending status - -return **`SMTP_Result`** The SMTP_Result type data that provides these properties - -##### [bool] completed - The status of the message - -#### [const char *] recipients - The primary recipient mailbox of the message - -#### [const char *] subject - The topic of the message - -#### [time_t] timesstamp - The timestamp of the message - -```C++ -SMTP_Result getItem(size_t index); -``` - - - - - -#### Provide the amount of the result data. - -return **`number`** The number of result item - -```C++ -size_t size(); -``` - - - - -## FoldersCollection class functions - - -The following functions are available from the FoldersCollection class. - -This class is used for retrieving the info about the mailbox folders which available to read or serach -in the user Email mailbox. - - - - -#### Provide the information of a folder in a folder collection. - -param **`index`** The index number of folders - -return **`esp_mail_folder_info_item_t`** The esp_mail_folder_info_item_t structured data that provides these properties - -#### [const char *] name - The name of folder - -#### [const char *] attributes - The attributes of folder - -#### [const char *] delimiter - The delimeter of folder - -```C++ -esp_mail_folder_info_item_t info(size_t index); -``` - - - - - -#### Provide the number of folders in the collection. - -return **`number`** The number of folder in the collection - -```C++ -size_t size(); -``` - - - - - - -## SelectedFolderInfo class functions - - -The following functions are available from the SelectedFolderInfo class. - -This class is used for retrieving the info about the sselected or opened mailbox folder. - - - - -#### Provide the numbers of flags in the user Email mailbox. - -return **`number`** The numbers of flags - -```C++ -size_t flagCount(); -``` - - - - - -#### Provide the numbers of messages in this mailbox. - -return **`number`** The numbers of messages in the selected mailbox folder - -```C++ -size_t msgCount(); -``` - - - - - -#### Provide the predicted next message UID in the sselected folder. - -return **`number`** The number represents the next message UID number - -```C++ -size_t nextUID(); -``` - - - - - -#### Provide the numbers of messages from search result based on the search criteria. - -return **`number`** The total number of messsages from search - -```C++ -size_t searchCount(); -``` - - - - - -#### Provide the numbers of messages to be stored in the ressult. - -return **`number`** The number of messsage stored from search - -```C++ -size_t availableMessages(); -``` - - - - - -#### Provide the flag argument at the specified index. - -return **`index`** The index of flag in the flags list - -return **`String`** The argument of selected flag - -```C++ -String flag(size_t index); -``` - - - - -## ESP_Mail_Session type data - - -The following properties are available from the ESP_Mail_Session data type. - -This data type is used for storing the session info about the server and login credentials. - - -#### [Properties] The server config - -This property has the sub properties - -##### [const char *] host_name - The hostName of the server. - -##### [uint16_t] port - The port on the server to connect to. - -```C++ -esp_mail_sesson_sever_config_t server; -``` - - -#### [Properties] The log in config - -This property has the sub properties - -##### [const char *] email - The user Email address to log in. - -##### [consst char *] password - The user password to log in. - -##### [consst char *] accessToken - The OAuth2.0 access token to log in. - -##### [consst char *] user_domain - The user domain or ip of client. - -```C++ -esp_mail_sesson_login_config_t login; -``` - - -#### [Properties] The secure config - -This property has the sub properties - -##### [bool] startTLS - The option to send the command to start the TLS connection. - -```C++ -esp_mail_sesson_secure_config_t secure; -``` - - - -#### [Properties] The certificate config - -##### [const char *] cert_data - The certificate data (base64 data). - -##### [consst char *] cert_file - The certificate file (DER format). - -##### [esp_mail_file_storage_type] cert_file_storage_type - The storage type. - -```C++ -esp_mail_sesson_cert_config_t certificate; -``` - - - - -## IMAP_Config type data - - -The following properties are available from the IMAP_Config data type. - -This data type is used for storing the IMAP transport and operating options to -control and store the operation result e.g. the messahe contents from search and fetch. - - - - -#### [Properties] The config for fetching - -This property has the sub property - -##### [const char *] uid - The UID of message to fetch. - -```C++ -esp_mail_imap_fetch_config_t fetch; -``` - - -#### [Properties] The config for search - -This property has the sub properties - -##### [const char *] criteria - The search criteria. - -##### [boolean] unseen_msg - The option to search the unseen message. - -```C++ -esp_mail_imap_search_config_t search; -``` - - -#### [Properties] The config about the limits - -This property has the sub properties - -##### [size_t] search - The maximum messages from the search result. - -##### [size_t] msg_size - The maximum size of the memory buffer to store the message content. - -This is only limit for data to be stored in the IMAPSession. - -##### [size_t] attachment_size - The maximum size of each attachment to download. - -```C++ -esp_mail_imap_limit_config_t limit; -``` - - - -#### [Properties] The config to enable the features - -This property has the sub properties - -##### [boolean] text - To store the PLAIN text of the message in the IMAPSession. - -##### [boolean] html - To store the HTML of the message in the IMAPSession. - -##### [boolean] rfc822 - To store the rfc822 messages in the IMAPSession. - -##### [boolean] download_status - To enable the download status via the serial port. - -##### [boolean] recent_sort - To sort the message UID of the search result in descending order. - -```C++ -esp_mail_imap_enable_config_t enable; -``` - - - -#### [Properties] The config about downloads - -This property has the sub properties - -##### [boolean] text - To download the PLAIN text content of the message. - -##### [boolean] html - To download the HTML content of the message. - -##### [boolean] attachment - To download the attachments of the message. - -##### [boolean] inlineImg - To download the inline image of the message. - -##### [boolean] rfc822 - To download the rfc822 mesages in the message. - -##### [boolean] header - To download the message header. - -```C++ -esp_mail_imap_download_config_t download; -``` - - - -#### [Properties] The config about the storage and path to save the downloaded file. - -This property has the sub properties - -##### [const char*] saved_path - The path to save the downloaded file. - -##### [esp_mail_file_storage_type] type - The type of file storages enumeration e.g. - -esp_mail_file_storage_type_none = 0, - -esp_mail_file_storage_type_flash = 1, and - -esp_mail_file_storage_type_sd = 2 - -```C++ -esp_mail_imap_storage_config_t storage; -``` - - - - - -## esp_mail_smtp_embed_message_body_t structured data - - -The following properties are available from the IMAP_Config data type. - -This data type is used for storing the IMAP transport and operating options to -control and store the operation result e.g. the messahe contents from search and fetch. - - - - -##### [Properties] Enable to send this message body as file - -```C++ -bool enable; -``` - - -##### [Properties] The name of embedded file - -```C++ -const char* enable; -``` - - -##### [Properties] The embedded type enumeration - -esp_mail_smtp_embed_message_type_attachment = 0 - -sp_mail_smtp_embed_message_type_inline = 1 - -```C++ -esp_mail_smtp_embed_message_type type; -``` - - - - - - -## esp_mail_blob_message_content_t structured data - - -The following properties are available from the esp_mail_blob_message_content_t data type. - -This data type is used for storing the blob info of message body. - - - - -##### [Properties] The array of content in flash memory. - -```C++ -const uint8_t * data; -``` - - -##### [Properties] The array size in bytes. - -```C++ -size_t size; -``` - - - - - -## esp_mail_file_message_content_t structured data - - -The following properties are available from the esp_mail_file_message_content_t data type. - -This data type is used for storing the file info of message body. - - - - -##### [Properties] The file path include its name. - -```C++ -const char *name; -``` - - -##### [Properties] The type of file storages. - -```C++ -esp_mail_file_storage_type type; -``` - - - - - - -## IMAP_MSG_Item type data - - -The following properties are available from the IMAP_MSG_Item data type. - -This data type is used for message item info and its contents from search and fetch. - - - - -#### [Properties] The message number - -```C++ -const char *msgNo; -``` - - -#### [Properties] The message UID - -```C++ -const char *UID; -``` - - -#### [Properties] The message identifier - -```C++ -const char *ID; -``` - - - -#### [Properties] The language(s) for auto-responses - -```C++ -const char *acceptLang; -``` - - - -#### [Properties] The language of message content - -```C++ -const char *contentLang; -``` - - - -#### [Properties] The mailbox of message author - -```C++ -const char *from; -``` - - -#### [Properties] The charset of the mailbox of message author - -```C++ -const char *fromCharset; -``` - - -#### [Properties] The primary recipient mailbox - -```C++ -const char *to; -``` - - -#### [Properties] The charset of the primary recipient mailbox - -```C++ -const char *toCharset; -``` - - -#### [Properties] The Carbon-copy recipient mailboxes - -```C++ -const char *cc; -``` - - -#### [Properties] The charset of the Carbon-copy recipient mailbox header - -```C++ -const char *ccCharset; -``` - -#### [Properties] The message date and time - -```C++ -const char *date; -``` - -#### [Properties] The topic of message - -```C++ -const char *subject; -``` - -#### [Properties] The topic of message charset - -```C++ -const char *subjectCharset; -``` - -#### [Properties] The PLAIN text content of the message - -```C++ -esp_mail_plain_body_t text; -``` - -#### [Properties] The HTML content of the message - -```C++ -esp_mail_html_body_t html; -``` - -#### [Properties] The sender Email - -```C++ -const char *sender; -``` - -#### [Properties] The message identifier - -```C++ -const char *messageID; -``` - -#### [Properties] The keywords or phrases, separated by commas - -```C++ -const char *keyword; -``` - -#### [Properties] The comment about message - -```C++ -const char *comment; -``` - - -#### [Properties] The return recipient of the message - -```C++ -const char *return_path; -``` - - -#### [Properties] The Email address to reply - -```C++ -const char *reply_to; -``` - - -#### [Properties] The Blind carbon-copy recipients - -```C++ -const char *bcc; -``` - - -#### [Properties] The error description from fetching the message - -```C++ -const char *fetchError; -``` - - -#### [Properties] The info about the attachments in the message - -```C++ -std::vector attachments; -``` - -#### [Properties] The info about the rfc822 messages included in the message - -```C++ -std::vector rfc822; -``` - - - - - - -## Search Criteria - -Search crieria is used for searching the mailbox for messages that match -the given searching criteria. - -Searching criteria consist of one or more search keys. When multiple keys are -specified, the result is the intersection (AND function) of all the messages -that match those keys. - -Example: - - **`DELETED FROM "SMITH" SINCE 1-Feb-1994`** refers -to all deleted messages from Smith that were placed in the mailbox since -February 1, 1994. - -A search key can also be a parenthesized list of one or more search keys -(e.g., for use with the OR and NOT keys). - -**`SINCE 10-Feb-2019`** will search all messages that received since 10 Feb 2019 - -**`UID SEARCH ALL`** will seach all message which will return the message UID -that can be use later for fetch one or more messages. - - -The following keywords can be used for the search criteria. - - -**ALL** - All messages in the mailbox; the default initial key for ANDing. - -**ANSWERED** - Messages with the \Answered flag set. - -**BCC** - Messages that contain the specified string in the envelope structure's BCC field. - -**BEFORE** - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. - -**BODY** - Messages that contain the specified string in the body of the message. - -**CC** - Messages that contain the specified string in the envelope structure's CC field. - -**DELETED** - Messages with the \Deleted flag set. - -**DRAFT** - Messages with the \Draft flag set. - -**FLAGGED** - Messages with the \Flagged flag set. - -**FROM** - Messages that contain the specified string in the envelope structure's FROM field. - -**HEADER** - Messages that have a header with the specified field-name (as defined in [RFC-2822]) - -and that contains the specified string in the text of the header (what comes after the colon). - -If the string to search is zero-length, this matches all messages that have a header line with - -the specified field-name regardless of the contents. - -**KEYWORD** - Messages with the specified keyword flag set. - -**LARGER** - Messages with an (RFC-2822) size larger than the specified number of octets. - -**NEW** - Messages that have the \Recent flag set but not the \Seen flag. - -This is functionally equivalent to **"(RECENT UNSEEN)"**. - -**NOT** - Messages that do not match the specified search key. - -**OLD** - Messages that do not have the \Recent flag set. This is functionally equivalent to - -**"NOT RECENT"** (as opposed to **"NOT NEW"**). - -**ON** - Messages whose internal date (disregarding time and timezone) is within the specified date. - -**OR** - Messages that match either search key. - -**RECENT** - Messages that have the \Recent flag set. - -**SEEN** - Messages that have the \Seen flag set. - -**SENTBEFORE** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is earlier than the specified date. - -**SENTON** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within the specified date. - -**SENTSINCE** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within or later than the specified date. - -**SINCE** - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. - -**SMALLER** - Messages with an (RFC-2822) size smaller than the specified number of octets. - -**SUBJECT** - Messages that contain the specified string in the envelope structure's SUBJECT field. - -**TEXT** - Messages that contain the specified string in the header or body of the message. - -**TO** - Messages that contain the specified string in the envelope structure's TO field. - -**UID** - Messages with unique identifiers corresponding to the specified unique identifier set. - -Sequence set ranges are permitted. - -**UNANSWERED** - Messages that do not have the \Answered flag set. - -**UNDELETED** - Messages that do not have the \Deleted flag set. - -**UNDRAFT** - Messages that do not have the \Draft flag set. - -**UNFLAGGED** - Messages that do not have the \Flagged flag set. - -**UNKEYWORD** - Messages that do not have the specified keyword flag set. - -**UNSEEN** - Messages that do not have the \Seen flag set. - - - - - - - - - -## MailClient.Time functions - - -The helper function to set and get the system time. - - - - - -#### Set the system time from the NTP server - -param **`gmtOffset`** The GMT time offset in hour. - -param **`daylightOffset`** The Daylight time offset in hour. - -return **`boolean`** The status indicates the success of operation. - -This requires internet connection - -```C++ -bool setClock(float gmtOffset, float daylightOffset); -``` - - - - - - -#### Provide the Unix time - -return **`uint32_t`** The value of current Unix time. - -```C++ -uint32_t getUnixTime(); -``` - - - - - - -#### Provide the timestamp from the year, month, date, hour, minute, and second provided - -param **`year`** The year. - -param **`mon`** The months from 1 to 12. - -param **`date`** The dates. - -param **`hour`** The hours. - -param **`mins`** The minutes. - -param **`sec`** The seconds. - -return **`time_t`** The value of timestamp. - -```C++ -time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); -``` - - - - - - -#### Provide the current year. - -return **`int`** The value of current year. - -```C++ -int getYear(); -``` - - - - - - -#### Provide the current month. - -return **`int`** The value of current month. - -```C++ -int getMonth(); -``` - - - - - -#### Provide the current date. - -return **`int`** The value of current date. - -```C++ -int getDay(); -``` - - - - - - -#### Provide the current day of week. - -return **`int`** The value of day of week. - -1 for sunday and 7 for saturday - -```C++ -int getDayOfWeek(); -``` - - - - - -#### Provide the current day of week in String. - -return **`String`** The value of day of week. - -Returns sunday, monday, tuesday, wednesday, thurseday, friday and saturday. - -```C++ -String getDayOfWeekString(); -``` - - - - - - -#### Provide the current hour. - -return **`int`** The value of current hour (0 to 23). - -```C++ -int getHour(); -``` - - - - - - -#### Provide the current minute. - -return **`int`** The value of current minute (0 to 59). - -```C++ -int getMin(); -``` - - - - - - -#### Provide the current second. - -return **`int`** The value of current second (0 to 59). - -```C++ -int getSecond(); -``` - - - - - - - -#### Provide the total days of current year. - -return **`int`** The value of total days of current year. - -```C++ -int getNumberOfDayThisYear(); -``` - - - - - - -#### Provide the total days of from January 1, 1970 to specific date. - -param **`year`** The years from 1970. - -param **`mon`** The months from 1 to 12. - -param **`date`** The dates. - -return **`int`** The value of total days. - -```C++ -int getTotalDays(int year, int month, int day); -``` - - - - - -#### Provide the day of week from specific date. - -param **`year`** The years. - -param **`month`** The months from 1 to 12. - -param **`day`** The dates. - -return **`int`** The value of day of week. - -1 for sunday and 7 for saturday - -```C++ -int dayofWeek(int year, int month, int day); -``` - - - - - - -#### Provide the second of current hour. - -return **`int`** The value of current second. - -```C++ -int getCurrentSecond(); -``` - - - - - -#### Provide the current timestamp. - -return **`uint64_t`** The value of current timestamp. - -```C++ -uint64_t getCurrentTimestamp(); -``` - - - - - - -#### Provide the date and time from second counted from January 1, 1970. - -param **`sec`** The seconds from January 1, 1970 00.00. - -return **`tm`** The tm structured data. - -The returned structured data tm has the members e.g. - -tm_year (from 1900), tm_mon (from 0 to 11), tm_mday, tm_hour, tm_min and tm_sec. - -```C++ -struct tm getTimeFromSec(int secCount); -``` - - - - - - -#### Provide the current date time string that valid for Email - -return **`String`** The current date time string. - -```C++ -String getDateTimeString(); -``` - - - - - - - -## License - -The MIT License (MIT) - -Copyright (c) 2021 K. Suwatchai (Mobizt) - - -Permission is hereby granted, free of charge, to any person returning a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - +# ESP Mail Client Arduino Library for ESP32 and ESP8266 + + +The detail and usage of the available functions in the latest version (1.2.0) are showed below. + + +## Global functions + + +#### Sending Email through the SMTP server. + +param **`smtp`** The pointer to SMTP session object which holds the data and the TCP client. + +param **`msg`** The pointer to SMTP_Message class which contains the header, body, and attachments. + +param **`closeSession`** The option to Close the SMTP session after sent. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool sendMail(SMTPSession *smtp, SMTP_Message *msg, bool closeSession = true); +``` + + + + + +#### Reading Email through IMAP server. + +param **`imap`** The pointer to IMAP sesssion object which holds the data and the TCP client. + +param **`closeSession`** The option to close the IMAP session after fetching or searching the Email. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool readMail(IMAPSession *imap, bool closeSession = true); +``` + + + + + +#### Set the argument to the Flags for the specified message. + +param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. + +param **`msgUID`** The UID of the message. + +param **`flags`** The flag list to set. + +param **`closeSession`** The option to close the IMAP session after set flag. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool setFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); +``` + + + + + +#### Add the argument to the Flags for the specified message. + +param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. + +param **`msgUID`** The UID of the message. + +param **`flags`** The flag list to set. + +param **`closeSession`** The option to close the IMAP session after add flag. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool addFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); +``` + + + + + + +#### Remove the argument from the Flags for the specified message. + +param **`imap`** The pointer to IMAP session object which holds the data and the TCP client. + +param **`msgUID`** The UID of the message that flags to be removed. + +param **`flags`** The flag list to remove. + +param **`closeSession`** The option to close the IMAP session after remove flag. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool removeFlag(IMAPSession *imap, int msgUID, const char *flags, bool closeSession); +``` + + + + + + +#### Initialize the SD card with the SPI port. + +param **`sck`** The SPI Clock pin (ESP32 only). + +param **`miso`** The SPI MISO pin (ESSP32 only). + +param **`mosi`** The SPI MOSI pin (ESP32 only). + +param **`ss`** The SPI Chip/Slave Select pin (ESP32 and ESP8266). + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool sdBegin(uint8_t sck, uint8_t miso, uint8_t mosi, uint8_t ss); +``` + + + + + + +#### Initialize the SD_MMC card (ESP32 only). + +param **`mountpoint`** The mounting point. + +param **`mode1bit`** Allow 1 bit data line (SPI mode). + +param **`format_if_mount_failed`** Format SD_MMC card if mount failed. + +return **`Boolean`** type status indicates the success of the operation. + +```C++ +bool sdMMCBegin(const char *mountpoint = "/sdcard", bool mode1bit = false, bool format_if_mount_failed = false); +``` + + + + + + +#### Initialize the SD card with the default SPI port. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool sdBegin(void); +``` + + + + + + +## IMAPSession class functions + + +The following functions are available from the IMAP Session class. + +This class used for controlling IMAP transports and retrieving the data from the IMAP server. + + + + + +#### Begin the IMAP server connection. + +param **`session`** The pointer to ESP_Mail_Session structured data that keeps the server and log in details. + +param **`config`** The pointer to IMAP_Config structured data that keeps the operation options. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool connect(ESP_Mail_Session *session, IMAP_Config *config); +``` + + +#### Close the IMAP session. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool closeSession(); +``` + + + + + + +#### Set to enable the debug. + +param **`level`** The level to enable the debug message + +level = 0, no debug + +level = 1, basic debug + +level = 2, full debug 1 + +level = 333, full debug 2 + +```C++ +void debug(int level); +``` + + + + + +#### Get the list of all the mailbox folders since the TCP session was opened and user was authenticated. + +param **`folders`** The FoldersCollection class that contains the collection of the +FolderInfo structured data. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool getFolders(FoldersCollection &folders); +``` + + + + + +#### Select or open the mailbox folder to search or fetch the message inside. + +param **`folderName`** The known mailbox folder name. The default name is INBOX. + +param **`readOnly`** The option to open the mailbox for read only. Set this option to false when you wish +to modify the Flags using the setFlag, addFlag and removeFlag functions. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool selectFolder(const char *folderName, bool readOnly = true); +``` + + + + + +#### Open the mailbox folder to read or search the mesages. + +param **`folderName`** The name of known mailbox folder to be opened. + +param **`readOnly`** The option to open the mailbox for reading only. Set this option to false when you wish +to modify the flags using the setFlag, addFlag and removeFlag functions. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool openFolder(const char *folderName, bool readOnly = true); +``` + + + + + +#### Close the mailbox folder that was opened. + +param **`folderName`** The mailbox folder name. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool closeFolder(const char *folderName); +``` + + + + + + +#### Create folder. + +param **`folderName`** The name of folder to create. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool createFolder(const char *folderName); +``` + + + + + + +#### Delete folder. + +param **`folderName`** The name of folder to delete.. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool deleteFolder(const char *folderName); +``` + + + + + + +#### Copy the messages to the defined mailbox folder. + +param **`toCopy`** The pointer to the MessageList class that contains the list of messages to copy. + +param **`dest`** The destination folder that the messages to copy to. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool copyMessages(MessageList *toCopy, const char *dest); +``` + + + + + +#### Delete the messages in the opened mailbox folder. + +param **`toDelete`** The pointer to the MessageList class that contains the list of messages to delete. + +param **`expunge`** The boolean option to expunge all messages. + +return **`boolean`** The boolean value which indicates the success of operation. + +```C++ +bool deleteMessages(MessageList *toDelete, bool expunge = false); +``` + + + + + + + +#### Assign the callback function that returns the operating status when fetching or reading the Email. + +param **`imapCallback`** The function that accepts the imapStatusCallback as parameter. + +```C++ +void callback(imapStatusCallback imapCallback); +``` + + + + + +#### Determine if no message body contained in the search result and only the message header is available. + +```C++ +bool headerOnly(); +``` + + + + + +#### Get the message list from search or fetch the Emails + +return **`The IMAP_MSG_List structured`** data which contains the text and html contents, +attachments, inline images, embedded rfc822 messages details for each message. + +```C++ +IMAP_MSG_List data(); +``` + + + + + +#### Get the details of the selected or opned mailbox folder + +return **`The SelectedFolderInfo class`** instance which contains the info about flags, total messages, next UID, +earch count and the available messages count. + +```C++ +SelectedFolderInfo selectedFolder(); +``` + + + + + +#### Get the error details when readingg the Emails + +return **`String`** The string of error details. + +```C++ +String errorReason(); +``` + + + + + +#### Clear all the cache data stored in the IMAP session object. + +```C++ +void empty(); +``` + + + + + +## IMAPSession class functions + + +The following functions are available from the SMTP Session class. + +This class is similar to the IMAP session class, used for controlling SMTP transports +and retrieving the data from the SMTP server. + + + + + + +#### Begin the SMTP server connection. + +param **`session`** The pointer to ESP_Mail_Session structured data that keeps the server and log in details. + +return **`boolean`** The boolean value indicates the success of operation. + +```C++ +bool connect(ESP_Mail_Session *session); +``` + + + + + +### Close the SMTP session. + +```C++ +bool closeSession(); +``` + + + + + +#### Set to enable the debug. + +param **`level`** The level to enable the debug message + +level = 0, no debug + +level = 1, basic debug + +level = 2, full debug 1 + +level = 333, full debug 2 + +```C++ +void debug(int level); +``` + + + + + +#### Get the error details when sending the Email + +return **`String`** The string of error details. + +```C++ +String errorReason(); +``` + + + + + +#### Set the Email sending status callback function. + +param **`smtpCallback`** The callback function that accept the smtpStatusCallback param. + +```C++ +void callback(smtpStatusCallback smtpCallback); +``` + + + + + +## SMTP_Message class functions + + +The following functions are available from the SMTP Message class. + +This class is used for storing the message data including headers, body and attachments +which will be processed with the SMTP session class. + + + + +#### To reset the SMTP_Attachment item data + +param **`att`** The SMTP_Attachment class that stores the info about attachment + +This function was used for clear the internal data of attachment item to be ready for reuse. + +```C++ +void resetAttachItem(SMTP_Attachment &att); +``` + + + +#### To clear all data in SMTP_Message class included headers, bodies and attachments + +```C++ +void clear(); +``` + + + + +#### To clear all the inline images in SMTP_Message class. + +```C++ +void clearInlineimages(); +``` + + + + + +#### To clear all the attachments. + +```C++ +void clearAttachments(); +``` + + + + + +#### To clear all rfc822 message attachment. + +```C++ +void clearRFC822Messages(); +``` + + + + + +#### To clear the primary recipient mailboxes. + +```C++ +void clearRecipients(); +``` + + + + + +#### To clear the Carbon-copy recipient mailboxes. + +```C++ +void clearCc(); +``` + + + + + +#### To clear the Blind-carbon-copy recipient mailboxes. + +```C++ +void clearBcc(); +``` + + +#### To clear the custom message headers. + +```C++ +void clearHeader(); +``` + + + + +#### To add attachment to the message. + +param **`att`** The SMTP_Attachment data item + +```C++ +void addAttachment(SMTP_Attachment &att); +``` + + + + + +#### To add parallel attachment to the message. + +param **`att`** The SMTP_Attachment data item + +```C++ +void addParallelAttachment(SMTP_Attachment &att); +``` + + + + + +#### To add inline image to the message. + +param **`att`** The SMTP_Attachment data item + +```C++ +void addInlineImage(SMTP_Attachment &att); +``` + + + + + +#### To add rfc822 message to the message. + +param **`msg`** The RFC822_Message class object + +```C++ +void addMessage(SMTP_Message &msg); +``` + + + + + +#### To add the primary recipient mailbox to the message. + +param **`name`** The name of primary recipient + +param **`email`** The Email address of primary recipient + +```C++ +void addRecipient(const char *name, const char *email); +``` + + + + + +#### To add Carbon-copy recipient mailbox. + +param **`email`** The Email address of secondary recipient + +```C++ +void addCc(const char *email); +``` + + + + + +#### To add Blind-carbon-copy recipient mailbox. + +param **`email`** The Email address of the tertiary recipient + +```C++ +void addBcc(const char *email); +``` + + + + + +#### To add the custom header to the message. + +param **`hdr`** The header name and value + +```C++ +void addHeader(const char *hdr); +``` + + + + +##### [properties] The message author config + +This property has the sub properties + +###### [const char*] name - The sender name. + +###### [const char*] email - The sender Email address. + +```C++ +esp_mail_email_info_t sender; +``` + + +##### [properties] The topic of message + +```C++ +const char *subject; +``` + + +##### [properties] The message type + +```C++ +byte type; +``` + + +##### [properties] The PLAIN text message + +This property has the sub properties + +###### [esp_mail_smtp_embed_message_body_t] embed - The option to embed this message content as a file. + +###### [const char*] content - The PLAIN text content of the message. + +###### [esp_mail_blob_message_content_t] blob - The blob that contins PLAIN text content of the message. + +###### [esp_mail_file_message_content_t] file - The file that contins PLAIN text content of the message. + +###### [const char*] charSet - The character transcoding of the PLAIN text content of the message. + +###### [const char*] content_type - The content type of message. + +###### [const char*] transfer_encoding - The option to encode the content for data transfer. + +###### [boolean] flowed - The option to send the PLAIN text with wrapping. + +```C++ +esp_mail_plain_body_t text; +``` + + +##### [properties] The HTML text message + +This propery has the sub properties + +###### [const char*] content - The HTML content of the message. + +###### [esp_mail_blob_message_content_t] blob - The blob that contins HTML content of the message. + +###### [esp_mail_file_message_content_t] file - The file that contins HTML content of the message. + +###### [const char*] charSet - The character transcoding of the HTML content of the message. + +###### [const char*] content_type - The content type of message. + +###### [const char*] transfer_encoding - The option to encode the content for data transfer. + +```C++ +esp_mail_html_body_t html; +``` + + +##### [properties] The response config + +This propery has the sub properties + +###### [const char*] reply_to - The author Email address to reply. + +###### [const char*] return_path - The sender Email address to return the message. + +###### [int] notify - The Delivery Status Notifications enumeration e.g. + +esp_mail_smtp_notify_never = 0, + +esp_mail_smtp_notify_success = 1, + +esp_mail_smtp_notify_failure = 2, and + +esp_mail_smtp_notify_delay = 4 + +```C++ +esp_mail_smtp_msg_response_t response; +``` + + +##### [properties] The priority of the message + +This property has the enumeration values + +esp_mail_smtp_priority_high = 1, + +esp_mail_smtp_priority_normal = 3, + +esp_mail_smtp_priority_low = 5 + +```C++ +esp_mail_smtp_priority priority; +``` + + +##### [properties] The enable options + +This propery has the sub property + +###### [boolean] chunking - enable chunk data sending for large message. + +```C++ +esp_mail_smtp_enable_option_t enable; +``` + + +##### [properties] The message from config + +This property has the sub properties + +###### [const char*] name - The messsage author name. + +###### [const char*] email - The message author Email address. + +```C++ +esp_mail_email_info_t from; +``` + + +##### [properties] The message identifier + +```C++ +const char *messageID; +``` + +##### [properties] The keywords or phrases, separated by commas + +```C++ +const char *keyword; +``` + + +##### [properties] The comment about message + +```C++ +const char *comment; +``` + + +##### [properties] The date of message + +```C++ +const char *date; +``` + + +##### [properties] The return recipient of the message + +```C++ +const char *return_path; +``` + + + + + + +## IMAP_Status class functions + + +The following functions are available from the IMAP Status class. + +This class is used as the callback parameter for retrieving the status while reading the Email. + + + + +#### Provide the information of each process in the reading operation. + +return **`string`** The info for each process + +```C++ +const char *info(); +``` + + + + +#### Provide the status of completion. + +return **`boolean`** The bool value indicates that all reading processes are finished + +```C++ +bool success(); +``` + + + + + +#### To clear all data store in this class. + +```C++ +void empty(); +``` + + + + + + +## SMTP_Status class functions + + +The following functions are available from the SMTP Status class. + +This class is used as the callback parameter for retrieving the status while sending the Email. + + + + +#### Provide the information of each process in the sending operation. + +return **`string`** The info for each process + +```C++ +const char *info(); +``` + + + + +#### Provide the status of completion. + +return **`boolean`** The bool value indicates that all sending processes are finished + +```C++ +bool success(); +``` + + + + + +#### To clear all data store in this class. + +```C++ +void empty(); +``` + + + + +#### Provide the number of complete sending message. + +return **`number`** The number of message that was sent + +```C++ +size_t completedCount(); +``` + + + + + +#### Provide the number of failed sending message. + +return **`number`** The number of message that was not sent + +```C++ +size_t failedCount(); +``` + + + + +## SendingResult class functions + + +The following functions are available from the SendingResult class. + +This class is used for retrieving the info about the result of sending the messages. + + + + +#### Provide the information of a message sending status. + +param **`index`** The index number of a message sending status + +return **`SMTP_Result`** The SMTP_Result type data that provides these properties + +##### [bool] completed - The status of the message + +#### [const char *] recipients - The primary recipient mailbox of the message + +#### [const char *] subject - The topic of the message + +#### [time_t] timesstamp - The timestamp of the message + +```C++ +SMTP_Result getItem(size_t index); +``` + + + + + +#### Provide the amount of the result data. + +return **`number`** The number of result item + +```C++ +size_t size(); +``` + + + + +## FoldersCollection class functions + + +The following functions are available from the FoldersCollection class. + +This class is used for retrieving the info about the mailbox folders which available to read or serach +in the user Email mailbox. + + + + +#### Provide the information of a folder in a folder collection. + +param **`index`** The index number of folders + +return **`esp_mail_folder_info_item_t`** The esp_mail_folder_info_item_t structured data that provides these properties + +#### [const char *] name - The name of folder + +#### [const char *] attributes - The attributes of folder + +#### [const char *] delimiter - The delimeter of folder + +```C++ +esp_mail_folder_info_item_t info(size_t index); +``` + + + + + +#### Provide the number of folders in the collection. + +return **`number`** The number of folder in the collection + +```C++ +size_t size(); +``` + + + + + + +## SelectedFolderInfo class functions + + +The following functions are available from the SelectedFolderInfo class. + +This class is used for retrieving the info about the sselected or opened mailbox folder. + + + + +#### Provide the numbers of flags in the user Email mailbox. + +return **`number`** The numbers of flags + +```C++ +size_t flagCount(); +``` + + + + + +#### Provide the numbers of messages in this mailbox. + +return **`number`** The numbers of messages in the selected mailbox folder + +```C++ +size_t msgCount(); +``` + + + + + +#### Provide the predicted next message UID in the sselected folder. + +return **`number`** The number represents the next message UID number + +```C++ +size_t nextUID(); +``` + + + + + +#### Provide the numbers of messages from search result based on the search criteria. + +return **`number`** The total number of messsages from search + +```C++ +size_t searchCount(); +``` + + + + + +#### Provide the numbers of messages to be stored in the ressult. + +return **`number`** The number of messsage stored from search + +```C++ +size_t availableMessages(); +``` + + + + + +#### Provide the flag argument at the specified index. + +return **`index`** The index of flag in the flags list + +return **`String`** The argument of selected flag + +```C++ +String flag(size_t index); +``` + + + + +## ESP_Mail_Session type data + + +The following properties are available from the ESP_Mail_Session data type. + +This data type is used for storing the session info about the server and login credentials. + + +#### [Properties] The server config + +This property has the sub properties + +##### [const char *] host_name - The hostName of the server. + +##### [uint16_t] port - The port on the server to connect to. + +```C++ +esp_mail_sesson_sever_config_t server; +``` + + +#### [Properties] The log in config + +This property has the sub properties + +##### [const char *] email - The user Email address to log in. + +##### [consst char *] password - The user password to log in. + +##### [consst char *] accessToken - The OAuth2.0 access token to log in. + +##### [consst char *] user_domain - The user domain or ip of client. + +```C++ +esp_mail_sesson_login_config_t login; +``` + + +#### [Properties] The secure config + +This property has the sub properties + +##### [bool] startTLS - The option to send the command to start the TLS connection. + +```C++ +esp_mail_sesson_secure_config_t secure; +``` + + + +#### [Properties] The certificate config + +##### [const char *] cert_data - The certificate data (base64 data). + +##### [consst char *] cert_file - The certificate file (DER format). + +##### [esp_mail_file_storage_type] cert_file_storage_type - The storage type. + +```C++ +esp_mail_sesson_cert_config_t certificate; +``` + + + + +## IMAP_Config type data + + +The following properties are available from the IMAP_Config data type. + +This data type is used for storing the IMAP transport and operating options to +control and store the operation result e.g. the messahe contents from search and fetch. + + + + +#### [Properties] The config for fetching + +This property has the sub property + +##### [const char *] uid - The UID of message to fetch. + +```C++ +esp_mail_imap_fetch_config_t fetch; +``` + + +#### [Properties] The config for search + +This property has the sub properties + +##### [const char *] criteria - The search criteria. + +##### [boolean] unseen_msg - The option to search the unseen message. + +```C++ +esp_mail_imap_search_config_t search; +``` + + +#### [Properties] The config about the limits + +This property has the sub properties + +##### [size_t] search - The maximum messages from the search result. + +##### [size_t] msg_size - The maximum size of the memory buffer to store the message content. + +This is only limit for data to be stored in the IMAPSession. + +##### [size_t] attachment_size - The maximum size of each attachment to download. + +```C++ +esp_mail_imap_limit_config_t limit; +``` + + + +#### [Properties] The config to enable the features + +This property has the sub properties + +##### [boolean] text - To store the PLAIN text of the message in the IMAPSession. + +##### [boolean] html - To store the HTML of the message in the IMAPSession. + +##### [boolean] rfc822 - To store the rfc822 messages in the IMAPSession. + +##### [boolean] download_status - To enable the download status via the serial port. + +##### [boolean] recent_sort - To sort the message UID of the search result in descending order. + +```C++ +esp_mail_imap_enable_config_t enable; +``` + + + +#### [Properties] The config about downloads + +This property has the sub properties + +##### [boolean] text - To download the PLAIN text content of the message. + +##### [boolean] html - To download the HTML content of the message. + +##### [boolean] attachment - To download the attachments of the message. + +##### [boolean] inlineImg - To download the inline image of the message. + +##### [boolean] rfc822 - To download the rfc822 mesages in the message. + +##### [boolean] header - To download the message header. + +```C++ +esp_mail_imap_download_config_t download; +``` + + + +#### [Properties] The config about the storage and path to save the downloaded file. + +This property has the sub properties + +##### [const char*] saved_path - The path to save the downloaded file. + +##### [esp_mail_file_storage_type] type - The type of file storages enumeration e.g. + +esp_mail_file_storage_type_none = 0, + +esp_mail_file_storage_type_flash = 1, and + +esp_mail_file_storage_type_sd = 2 + +```C++ +esp_mail_imap_storage_config_t storage; +``` + + + + + +## esp_mail_smtp_embed_message_body_t structured data + + +The following properties are available from the IMAP_Config data type. + +This data type is used for storing the IMAP transport and operating options to +control and store the operation result e.g. the messahe contents from search and fetch. + + + + +##### [Properties] Enable to send this message body as file + +```C++ +bool enable; +``` + + +##### [Properties] The name of embedded file + +```C++ +const char* enable; +``` + + +##### [Properties] The embedded type enumeration + +esp_mail_smtp_embed_message_type_attachment = 0 + +sp_mail_smtp_embed_message_type_inline = 1 + +```C++ +esp_mail_smtp_embed_message_type type; +``` + + + + + + +## esp_mail_blob_message_content_t structured data + + +The following properties are available from the esp_mail_blob_message_content_t data type. + +This data type is used for storing the blob info of message body. + + + + +##### [Properties] The array of content in flash memory. + +```C++ +const uint8_t * data; +``` + + +##### [Properties] The array size in bytes. + +```C++ +size_t size; +``` + + + + + +## esp_mail_file_message_content_t structured data + + +The following properties are available from the esp_mail_file_message_content_t data type. + +This data type is used for storing the file info of message body. + + + + +##### [Properties] The file path include its name. + +```C++ +const char *name; +``` + + +##### [Properties] The type of file storages. + +```C++ +esp_mail_file_storage_type type; +``` + + + + + + +## IMAP_MSG_Item type data + + +The following properties are available from the IMAP_MSG_Item data type. + +This data type is used for message item info and its contents from search and fetch. + + + + +#### [Properties] The message number + +```C++ +const char *msgNo; +``` + + +#### [Properties] The message UID + +```C++ +const char *UID; +``` + + +#### [Properties] The message identifier + +```C++ +const char *ID; +``` + + + +#### [Properties] The language(s) for auto-responses + +```C++ +const char *acceptLang; +``` + + + +#### [Properties] The language of message content + +```C++ +const char *contentLang; +``` + + + +#### [Properties] The mailbox of message author + +```C++ +const char *from; +``` + + +#### [Properties] The charset of the mailbox of message author + +```C++ +const char *fromCharset; +``` + + +#### [Properties] The primary recipient mailbox + +```C++ +const char *to; +``` + + +#### [Properties] The charset of the primary recipient mailbox + +```C++ +const char *toCharset; +``` + + +#### [Properties] The Carbon-copy recipient mailboxes + +```C++ +const char *cc; +``` + + +#### [Properties] The charset of the Carbon-copy recipient mailbox header + +```C++ +const char *ccCharset; +``` + +#### [Properties] The message date and time + +```C++ +const char *date; +``` + +#### [Properties] The topic of message + +```C++ +const char *subject; +``` + +#### [Properties] The topic of message charset + +```C++ +const char *subjectCharset; +``` + +#### [Properties] The PLAIN text content of the message + +```C++ +esp_mail_plain_body_t text; +``` + +#### [Properties] The HTML content of the message + +```C++ +esp_mail_html_body_t html; +``` + +#### [Properties] The sender Email + +```C++ +const char *sender; +``` + +#### [Properties] The message identifier + +```C++ +const char *messageID; +``` + +#### [Properties] The keywords or phrases, separated by commas + +```C++ +const char *keyword; +``` + +#### [Properties] The comment about message + +```C++ +const char *comment; +``` + + +#### [Properties] The return recipient of the message + +```C++ +const char *return_path; +``` + + +#### [Properties] The Email address to reply + +```C++ +const char *reply_to; +``` + + +#### [Properties] The Blind carbon-copy recipients + +```C++ +const char *bcc; +``` + + +#### [Properties] The error description from fetching the message + +```C++ +const char *fetchError; +``` + + +#### [Properties] The info about the attachments in the message + +```C++ +std::vector attachments; +``` + +#### [Properties] The info about the rfc822 messages included in the message + +```C++ +std::vector rfc822; +``` + + + + + + +## Search Criteria + +Search crieria is used for searching the mailbox for messages that match +the given searching criteria. + +Searching criteria consist of one or more search keys. When multiple keys are +specified, the result is the intersection (AND function) of all the messages +that match those keys. + +Example: + + **`DELETED FROM "SMITH" SINCE 1-Feb-1994`** refers +to all deleted messages from Smith that were placed in the mailbox since +February 1, 1994. + +A search key can also be a parenthesized list of one or more search keys +(e.g., for use with the OR and NOT keys). + +**`SINCE 10-Feb-2019`** will search all messages that received since 10 Feb 2019 + +**`UID SEARCH ALL`** will seach all message which will return the message UID +that can be use later for fetch one or more messages. + + +The following keywords can be used for the search criteria. + + +**ALL** - All messages in the mailbox; the default initial key for ANDing. + +**ANSWERED** - Messages with the \Answered flag set. + +**BCC** - Messages that contain the specified string in the envelope structure's BCC field. + +**BEFORE** - Messages whose internal date (disregarding time and timezone) is earlier than the specified date. + +**BODY** - Messages that contain the specified string in the body of the message. + +**CC** - Messages that contain the specified string in the envelope structure's CC field. + +**DELETED** - Messages with the \Deleted flag set. + +**DRAFT** - Messages with the \Draft flag set. + +**FLAGGED** - Messages with the \Flagged flag set. + +**FROM** - Messages that contain the specified string in the envelope structure's FROM field. + +**HEADER** - Messages that have a header with the specified field-name (as defined in [RFC-2822]) + +and that contains the specified string in the text of the header (what comes after the colon). + +If the string to search is zero-length, this matches all messages that have a header line with + +the specified field-name regardless of the contents. + +**KEYWORD** - Messages with the specified keyword flag set. + +**LARGER** - Messages with an (RFC-2822) size larger than the specified number of octets. + +**NEW** - Messages that have the \Recent flag set but not the \Seen flag. + +This is functionally equivalent to **"(RECENT UNSEEN)"**. + +**NOT** - Messages that do not match the specified search key. + +**OLD** - Messages that do not have the \Recent flag set. This is functionally equivalent to + +**"NOT RECENT"** (as opposed to **"NOT NEW"**). + +**ON** - Messages whose internal date (disregarding time and timezone) is within the specified date. + +**OR** - Messages that match either search key. + +**RECENT** - Messages that have the \Recent flag set. + +**SEEN** - Messages that have the \Seen flag set. + +**SENTBEFORE** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is earlier than the specified date. + +**SENTON** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within the specified date. + +**SENTSINCE** - Messages whose (RFC-2822) Date: header (disregarding time and timezone) is within or later than the specified date. + +**SINCE** - Messages whose internal date (disregarding time and timezone) is within or later than the specified date. + +**SMALLER** - Messages with an (RFC-2822) size smaller than the specified number of octets. + +**SUBJECT** - Messages that contain the specified string in the envelope structure's SUBJECT field. + +**TEXT** - Messages that contain the specified string in the header or body of the message. + +**TO** - Messages that contain the specified string in the envelope structure's TO field. + +**UID** - Messages with unique identifiers corresponding to the specified unique identifier set. + +Sequence set ranges are permitted. + +**UNANSWERED** - Messages that do not have the \Answered flag set. + +**UNDELETED** - Messages that do not have the \Deleted flag set. + +**UNDRAFT** - Messages that do not have the \Draft flag set. + +**UNFLAGGED** - Messages that do not have the \Flagged flag set. + +**UNKEYWORD** - Messages that do not have the specified keyword flag set. + +**UNSEEN** - Messages that do not have the \Seen flag set. + + + + + + + + + +## MailClient.Time functions + + +The helper function to set and get the system time. + + + + + +#### Set the system time from the NTP server + +param **`gmtOffset`** The GMT time offset in hour. + +param **`daylightOffset`** The Daylight time offset in hour. + +return **`boolean`** The status indicates the success of operation. + +This requires internet connection + +```C++ +bool setClock(float gmtOffset, float daylightOffset); +``` + + + + + + +#### Provide the Unix time + +return **`uint32_t`** The value of current Unix time. + +```C++ +uint32_t getUnixTime(); +``` + + + + + + +#### Provide the timestamp from the year, month, date, hour, minute, and second provided + +param **`year`** The year. + +param **`mon`** The months from 1 to 12. + +param **`date`** The dates. + +param **`hour`** The hours. + +param **`mins`** The minutes. + +param **`sec`** The seconds. + +return **`time_t`** The value of timestamp. + +```C++ +time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); +``` + + + + + + +#### Provide the current year. + +return **`int`** The value of current year. + +```C++ +int getYear(); +``` + + + + + + +#### Provide the current month. + +return **`int`** The value of current month. + +```C++ +int getMonth(); +``` + + + + + +#### Provide the current date. + +return **`int`** The value of current date. + +```C++ +int getDay(); +``` + + + + + + +#### Provide the current day of week. + +return **`int`** The value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int getDayOfWeek(); +``` + + + + + +#### Provide the current day of week in String. + +return **`String`** The value of day of week. + +Returns sunday, monday, tuesday, wednesday, thurseday, friday and saturday. + +```C++ +String getDayOfWeekString(); +``` + + + + + + +#### Provide the current hour. + +return **`int`** The value of current hour (0 to 23). + +```C++ +int getHour(); +``` + + + + + + +#### Provide the current minute. + +return **`int`** The value of current minute (0 to 59). + +```C++ +int getMin(); +``` + + + + + + +#### Provide the current second. + +return **`int`** The value of current second (0 to 59). + +```C++ +int getSecond(); +``` + + + + + + + +#### Provide the total days of current year. + +return **`int`** The value of total days of current year. + +```C++ +int getNumberOfDayThisYear(); +``` + + + + + + +#### Provide the total days of from January 1, 1970 to specific date. + +param **`year`** The years from 1970. + +param **`mon`** The months from 1 to 12. + +param **`date`** The dates. + +return **`int`** The value of total days. + +```C++ +int getTotalDays(int year, int month, int day); +``` + + + + + +#### Provide the day of week from specific date. + +param **`year`** The years. + +param **`month`** The months from 1 to 12. + +param **`day`** The dates. + +return **`int`** The value of day of week. + +1 for sunday and 7 for saturday + +```C++ +int dayofWeek(int year, int month, int day); +``` + + + + + + +#### Provide the second of current hour. + +return **`int`** The value of current second. + +```C++ +int getCurrentSecond(); +``` + + + + + +#### Provide the current timestamp. + +return **`uint64_t`** The value of current timestamp. + +```C++ +uint64_t getCurrentTimestamp(); +``` + + + + + + +#### Provide the date and time from second counted from January 1, 1970. + +param **`sec`** The seconds from January 1, 1970 00.00. + +return **`tm`** The tm structured data. + +The returned structured data tm has the members e.g. + +tm_year (from 1900), tm_mon (from 0 to 11), tm_mday, tm_hour, tm_min and tm_sec. + +```C++ +struct tm getTimeFromSec(int secCount); +``` + + + + + + +#### Provide the current date time string that valid for Email + +return **`String`** The current date time string. + +```C++ +String getDateTimeString(); +``` + + + + + + + +## License + +The MIT License (MIT) + +Copyright (c) 2021 K. Suwatchai (Mobizt) + + +Permission is hereby granted, free of charge, to any person returning a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/lib/lib_div/lib_mail/src/SDK_Version_Common.h b/lib/libesp32/lib_mail/src/SDK_Version_Common.h similarity index 100% rename from lib/lib_div/lib_mail/src/SDK_Version_Common.h rename to lib/libesp32/lib_mail/src/SDK_Version_Common.h diff --git a/lib/lib_div/lib_mail/src/extras/ESPTimeHelper.cpp b/lib/libesp32/lib_mail/src/extras/ESPTimeHelper.cpp old mode 100755 new mode 100644 similarity index 95% rename from lib/lib_div/lib_mail/src/extras/ESPTimeHelper.cpp rename to lib/libesp32/lib_mail/src/extras/ESPTimeHelper.cpp index 227f8e59d..b88b1c753 --- a/lib/lib_div/lib_mail/src/extras/ESPTimeHelper.cpp +++ b/lib/libesp32/lib_mail/src/extras/ESPTimeHelper.cpp @@ -1,286 +1,286 @@ -/* - * ESP8266/ESP32 Internet Time Helper Arduino Library v 1.0.2 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person returning a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef ESPTimeHelper_CPP -#define ESPTimeHelper_CPP - -#include "ESPTimeHelper.h" - -ESPTimeHelper::ESPTimeHelper() -{ -} - -uint32_t ESPTimeHelper::getUnixTime() -{ - uint32_t utime = (msec_time_diff + millis()) / 1000; - return utime; -} - -int ESPTimeHelper::setTimestamp(time_t ts) -{ - struct timeval tm = {ts, 0};//sec, us - return settimeofday((const timeval *)&tm, 0); -} - -time_t ESPTimeHelper::getTimestamp(int year, int mon, int date, int hour, int mins, int sec) -{ - struct tm timeinfo; - timeinfo.tm_year = year - 1900; - timeinfo.tm_mon = mon - 1; - timeinfo.tm_mday = date; - timeinfo.tm_hour = hour; - timeinfo.tm_min = mins; - timeinfo.tm_sec = sec; - time_t ts = mktime(&timeinfo); - return ts; -} - -bool ESPTimeHelper::setClock(float gmtOffset, float daylightOffset) -{ - TZ = gmtOffset; - DST_MN = daylightOffset; - configTime((TZ)*3600, (DST_MN)*60, "pool.ntp.org", "time.nist.gov"); - - now = time(nullptr); - uint8_t attempts = 0; - while (now < 1577836800) - { - now = time(nullptr); - attempts++; - if (attempts > 200 || now > 1577836800) - break; - delay(100); - } - - uint64_t tmp = now; - tmp = tmp * 1000; - msec_time_diff = tmp - millis(); - -#if defined(ESP32) - getLocalTime(&timeinfo); -#elif defined(ESP8266) - gmtime_r(&now, &timeinfo); -#endif - - clockReady = now > 8 * 3600 * 2; - return clockReady; -} - -int ESPTimeHelper::getYear() -{ - setSysTime(); - return timeinfo.tm_year + 1900; -} -int ESPTimeHelper::getMonth() -{ - setSysTime(); - return timeinfo.tm_mon + 1; -} -int ESPTimeHelper::getDay() -{ - setSysTime(); - return timeinfo.tm_mday; -} - -int ESPTimeHelper::getDayOfWeek() -{ - setSysTime(); - return timeinfo.tm_wday; -} -String ESPTimeHelper::getDayOfWeekString() -{ - setSysTime(); - return dow[timeinfo.tm_wday]; -} - -int ESPTimeHelper::getHour() -{ - setSysTime(); - return timeinfo.tm_hour; -} - -int ESPTimeHelper::getMin() -{ - setSysTime(); - return timeinfo.tm_min; -} -int ESPTimeHelper::getSec() -{ - setSysTime(); - return timeinfo.tm_sec; -} -int ESPTimeHelper::getNumberOfDayThisYear() -{ - setSysTime(); - return timeinfo.tm_yday + 1; -} - -int ESPTimeHelper::totalDays(int y, int m, int d) -{ - static char daytab[2][13] = - { - {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; - int daystotal = d; - for (int year = 1; year <= y; year++) - { - int max_month = (year < y ? 12 : m - 1); - int leap = (year % 4 == 0); - if (year % 100 == 0 && year % 400 != 0) - leap = 0; - for (int month = 1; month <= max_month; month++) - { - daystotal += daytab[leap][month]; - } - } - return daystotal; -} -int ESPTimeHelper::getTotalDays(int year, int month, int day) -{ - return totalDays(year, month, day) - totalDays(1970, 1, 1); -} - -int ESPTimeHelper::dayofWeek(int year, int month, int day) /* 1 <= m <= 12, y > 1752 (in the U.K.) */ -{ - static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; - year -= month < 3; - return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7; -} - -int ESPTimeHelper::getCurrentSecond() -{ - return (timeinfo.tm_hour * 3600) + (timeinfo.tm_min * 60) + timeinfo.tm_sec; -} -uint64_t ESPTimeHelper::getCurrentTimestamp() -{ - return now; -} -struct tm ESPTimeHelper::getTimeFromSec(int seconds) -{ - struct tm timeinfo; - int _yrs = seconds / (365 * 24 * 3600); - seconds = seconds - _yrs * (365 * 24 * 3600); - timeinfo.tm_year = _yrs - 1900; - int _months = seconds / (30 * 24 * 3600); - seconds = seconds - _months * (30 * 24 * 3600); - timeinfo.tm_mon = _months - 1; - int _days = seconds / (24 * 3600); - seconds = seconds - _days * (24 * 3600); - timeinfo.tm_mday = _days; - int _hr = seconds / 3600; - seconds = seconds - _hr * 3600; - timeinfo.tm_hour = _hr; - int _min = seconds / 60; - seconds = seconds - _min * 60; - timeinfo.tm_min = _min; - timeinfo.tm_sec = seconds; - return timeinfo; -} - -char *ESPTimeHelper::intStr(int value) -{ - char *buf = new char[36]; - memset(buf, 0, 36); - itoa(value, buf, 10); - return buf; -} - -String ESPTimeHelper::getDateTimeString() -{ - setSysTime(); - std::string s; - - s = sdow[timeinfo.tm_wday]; - - s += ", "; - char *tmp = intStr(timeinfo.tm_mday); - s += tmp; - delete[] tmp; - - s += " "; - s += months[timeinfo.tm_mon]; - - s += " "; - tmp = intStr(timeinfo.tm_year + 1900); - s += tmp; - delete[] tmp; - - s += " "; - if (timeinfo.tm_hour < 10) - s += "0"; - tmp = intStr(timeinfo.tm_hour); - s += tmp; - delete[] tmp; - - s += ":"; - if (timeinfo.tm_min < 10) - s += "0"; - tmp = intStr(timeinfo.tm_min); - s += tmp; - delete[] tmp; - - s += ":"; - if (timeinfo.tm_sec < 10) - s += "0"; - tmp = intStr(timeinfo.tm_sec); - s += tmp; - delete[] tmp; - - int p = 1; - if (TZ < 0) - p = -1; - int tz = TZ; - float dif = (p * (TZ - tz)) * 60.0; - if (TZ < 0) - s += " -"; - else - s += " +"; - - if (tz < 10) - s += "0"; - tmp = intStr(tz); - s += tmp; - delete[] tmp; - - if (dif < 10) - s += "0"; - tmp = intStr((int)dif); - s += tmp; - delete[] tmp; - - return s.c_str(); -} - -void ESPTimeHelper::setSysTime() -{ -#if defined(ESP32) - getLocalTime(&timeinfo); -#elif defined(ESP8266) - now = time(nullptr); - localtime_r(&now, &timeinfo); -#endif -} - +/* + * ESP8266/ESP32 Internet Time Helper Arduino Library v 1.0.2 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESPTimeHelper_CPP +#define ESPTimeHelper_CPP + +#include "ESPTimeHelper.h" + +ESPTimeHelper::ESPTimeHelper() +{ +} + +uint32_t ESPTimeHelper::getUnixTime() +{ + uint32_t utime = (msec_time_diff + millis()) / 1000; + return utime; +} + +int ESPTimeHelper::setTimestamp(time_t ts) +{ + struct timeval tm = {ts, 0};//sec, us + return settimeofday((const timeval *)&tm, 0); +} + +time_t ESPTimeHelper::getTimestamp(int year, int mon, int date, int hour, int mins, int sec) +{ + struct tm timeinfo; + timeinfo.tm_year = year - 1900; + timeinfo.tm_mon = mon - 1; + timeinfo.tm_mday = date; + timeinfo.tm_hour = hour; + timeinfo.tm_min = mins; + timeinfo.tm_sec = sec; + time_t ts = mktime(&timeinfo); + return ts; +} + +bool ESPTimeHelper::setClock(float gmtOffset, float daylightOffset) +{ + TZ = gmtOffset; + DST_MN = daylightOffset; + configTime((TZ)*3600, (DST_MN)*60, "pool.ntp.org", "time.nist.gov"); + + now = time(nullptr); + uint8_t attempts = 0; + while (now < 1577836800) + { + now = time(nullptr); + attempts++; + if (attempts > 200 || now > 1577836800) + break; + delay(100); + } + + uint64_t tmp = now; + tmp = tmp * 1000; + msec_time_diff = tmp - millis(); + +#if defined(ESP32) + getLocalTime(&timeinfo); +#elif defined(ESP8266) + gmtime_r(&now, &timeinfo); +#endif + + clockReady = now > 8 * 3600 * 2; + return clockReady; +} + +int ESPTimeHelper::getYear() +{ + setSysTime(); + return timeinfo.tm_year + 1900; +} +int ESPTimeHelper::getMonth() +{ + setSysTime(); + return timeinfo.tm_mon + 1; +} +int ESPTimeHelper::getDay() +{ + setSysTime(); + return timeinfo.tm_mday; +} + +int ESPTimeHelper::getDayOfWeek() +{ + setSysTime(); + return timeinfo.tm_wday; +} +String ESPTimeHelper::getDayOfWeekString() +{ + setSysTime(); + return dow[timeinfo.tm_wday]; +} + +int ESPTimeHelper::getHour() +{ + setSysTime(); + return timeinfo.tm_hour; +} + +int ESPTimeHelper::getMin() +{ + setSysTime(); + return timeinfo.tm_min; +} +int ESPTimeHelper::getSec() +{ + setSysTime(); + return timeinfo.tm_sec; +} +int ESPTimeHelper::getNumberOfDayThisYear() +{ + setSysTime(); + return timeinfo.tm_yday + 1; +} + +int ESPTimeHelper::totalDays(int y, int m, int d) +{ + static char daytab[2][13] = + { + {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; + int daystotal = d; + for (int year = 1; year <= y; year++) + { + int max_month = (year < y ? 12 : m - 1); + int leap = (year % 4 == 0); + if (year % 100 == 0 && year % 400 != 0) + leap = 0; + for (int month = 1; month <= max_month; month++) + { + daystotal += daytab[leap][month]; + } + } + return daystotal; +} +int ESPTimeHelper::getTotalDays(int year, int month, int day) +{ + return totalDays(year, month, day) - totalDays(1970, 1, 1); +} + +int ESPTimeHelper::dayofWeek(int year, int month, int day) /* 1 <= m <= 12, y > 1752 (in the U.K.) */ +{ + static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + year -= month < 3; + return (year + year / 4 - year / 100 + year / 400 + t[month - 1] + day) % 7; +} + +int ESPTimeHelper::getCurrentSecond() +{ + return (timeinfo.tm_hour * 3600) + (timeinfo.tm_min * 60) + timeinfo.tm_sec; +} +uint64_t ESPTimeHelper::getCurrentTimestamp() +{ + return now; +} +struct tm ESPTimeHelper::getTimeFromSec(int seconds) +{ + struct tm timeinfo; + int _yrs = seconds / (365 * 24 * 3600); + seconds = seconds - _yrs * (365 * 24 * 3600); + timeinfo.tm_year = _yrs - 1900; + int _months = seconds / (30 * 24 * 3600); + seconds = seconds - _months * (30 * 24 * 3600); + timeinfo.tm_mon = _months - 1; + int _days = seconds / (24 * 3600); + seconds = seconds - _days * (24 * 3600); + timeinfo.tm_mday = _days; + int _hr = seconds / 3600; + seconds = seconds - _hr * 3600; + timeinfo.tm_hour = _hr; + int _min = seconds / 60; + seconds = seconds - _min * 60; + timeinfo.tm_min = _min; + timeinfo.tm_sec = seconds; + return timeinfo; +} + +char *ESPTimeHelper::intStr(int value) +{ + char *buf = new char[36]; + memset(buf, 0, 36); + itoa(value, buf, 10); + return buf; +} + +String ESPTimeHelper::getDateTimeString() +{ + setSysTime(); + std::string s; + + s = sdow[timeinfo.tm_wday]; + + s += ", "; + char *tmp = intStr(timeinfo.tm_mday); + s += tmp; + delete[] tmp; + + s += " "; + s += months[timeinfo.tm_mon]; + + s += " "; + tmp = intStr(timeinfo.tm_year + 1900); + s += tmp; + delete[] tmp; + + s += " "; + if (timeinfo.tm_hour < 10) + s += "0"; + tmp = intStr(timeinfo.tm_hour); + s += tmp; + delete[] tmp; + + s += ":"; + if (timeinfo.tm_min < 10) + s += "0"; + tmp = intStr(timeinfo.tm_min); + s += tmp; + delete[] tmp; + + s += ":"; + if (timeinfo.tm_sec < 10) + s += "0"; + tmp = intStr(timeinfo.tm_sec); + s += tmp; + delete[] tmp; + + int p = 1; + if (TZ < 0) + p = -1; + int tz = TZ; + float dif = (p * (TZ - tz)) * 60.0; + if (TZ < 0) + s += " -"; + else + s += " +"; + + if (tz < 10) + s += "0"; + tmp = intStr(tz); + s += tmp; + delete[] tmp; + + if (dif < 10) + s += "0"; + tmp = intStr((int)dif); + s += tmp; + delete[] tmp; + + return s.c_str(); +} + +void ESPTimeHelper::setSysTime() +{ +#if defined(ESP32) + getLocalTime(&timeinfo); +#elif defined(ESP8266) + now = time(nullptr); + localtime_r(&now, &timeinfo); +#endif +} + #endif //ESPTimeHelper_CPP \ No newline at end of file diff --git a/lib/lib_div/lib_mail/src/extras/ESPTimeHelper.h b/lib/libesp32/lib_mail/src/extras/ESPTimeHelper.h old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/src/extras/ESPTimeHelper.h rename to lib/libesp32/lib_mail/src/extras/ESPTimeHelper.h index 1e3111951..2cc5fcddf --- a/lib/lib_div/lib_mail/src/extras/ESPTimeHelper.h +++ b/lib/libesp32/lib_mail/src/extras/ESPTimeHelper.h @@ -1,199 +1,199 @@ -/* - * ESP8266/ESP32 Internet Time Helper Arduino Library v 1.0.2 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person returning a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef ESPTimeHelper_H -#define ESPTimeHelper_H - -#include -#include -#if defined(ESP32) -#include -#elif defined(ESP8266) -#include -#include "SDK_Version_Common.h" -#endif - -class ESPTimeHelper -{ -public: - ESPTimeHelper(); - - /** Set the system time from the NTP server - * - * @param gmtOffset The GMT time offset in hour. - * @param daylightOffset The Daylight time offset in hour. - * @return boolean The status indicates the success of operation. - * - * @note This requires internet connection - */ - bool setClock(float gmtOffset, float daylightOffset); - - /** Set system time with provided timestamp - * - * @param ts timestamp in seconds from midnight Jan 1, 1970. - * @return error number, 0 for success. - */ - int setTimestamp(time_t ts); - - /** Provide the Unix time - * - * @return uint32_t The value of current Unix time. - */ - uint32_t getUnixTime(); - - /** Provide the timestamp from the year, month, date, hour, minute, - * and second provided. - * - * @param year The year. - * @param mon The month from 1 to 12. - * @param date The dates. - * @param hour The hours. - * @param mins The minutes. - * @param sec The seconds. - * @return time_t The value of timestamp. - */ - time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); - - /** Provide the current year. - * - * @return int The value of current year. - */ - int getYear(); - - /** Provide the current month. - * - * @return int The value of current month. - */ - int getMonth(); - - /** Provide the current date. - * - * @return int The value of current date. - */ - int getDay(); - - /** Provide the current day of week. - * - * @return int The value of current day of week. - * - * @note 1 for sunday and 7 for saturday. - */ - int getDayOfWeek(); - - /** Provide the current day of week in String. - * - * @return String The value of day of week. - */ - String getDayOfWeekString(); - - /** Provide the current hour. - * - * @return int The value of current hour (0 to 23). - */ - int getHour(); - - /** Provide the current minute. - * - * @return int The value of current minute. - */ - int getMin(); - - /** Provide the current second. - * - * @return int The value of current second. - */ - int getSec(); - - /** Provide the total days of current year. - * - * @return int The value of total days of current year. - */ - int getNumberOfDayThisYear(); - - /** Provide the total days of from January 1, 1970 to specific date. - * - * @param year The year from 1970. - * @param mon The month from 1 to 12. - * @param day The dates. - * @return int The value of total days. - */ - int getTotalDays(int year, int month, int day); - - /** Provide the day of week from specific date. - * - * @param year The year from 1970. - * @param mon The month from 1 to 12. - * @param day The dates. - * @return int the value of day of week. - * @note 1 for sunday and 7 for saturday - */ - int dayofWeek(int year, int month, int day); - - /** Provide the second of current hour. - * - * @return int The value of current second. - */ - int getCurrentSecond(); - - /** Provide the current timestamp. - * - * @return uint64_t The value of current timestamp. - */ - uint64_t getCurrentTimestamp(); - - /** Provide the date and time from second counted from January 1, 1970. - * - * @param sec The seconds from January 1, 1970 00.00. - * @return tm The tm structured data. - * - * @note The returned structured data tm has the members e.g. - * tm_year (from 1900), tm_mon (from 0 to 11), tm_mday, tm_hour, - * tm_min and tm_sec. - */ - struct tm getTimeFromSec(int seconds); - - /** Provide the current date time string that valid for Email. - * - * @return String The current date time string. - */ - String getDateTimeString(); - - time_t now; - uint64_t msec_time_diff = 0; - struct tm timeinfo; - float TZ = 0.0; - uint8_t DST_MN = 0; - bool clockReady = false; - -private: - int totalDays(int y, int m, int d); - char *intStr(int value); - void setSysTime(); - const char *dow[7] = {"sunday", "monday", "tuesday", "wednesday", "thurseday", "friday", "saturday"}; - const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - const char *sdow[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -}; - -#endif //ESPTimeHelper_H +/* + * ESP8266/ESP32 Internet Time Helper Arduino Library v 1.0.2 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESPTimeHelper_H +#define ESPTimeHelper_H + +#include +#include +#if defined(ESP32) +#include +#elif defined(ESP8266) +#include +#include "SDK_Version_Common.h" +#endif + +class ESPTimeHelper +{ +public: + ESPTimeHelper(); + + /** Set the system time from the NTP server + * + * @param gmtOffset The GMT time offset in hour. + * @param daylightOffset The Daylight time offset in hour. + * @return boolean The status indicates the success of operation. + * + * @note This requires internet connection + */ + bool setClock(float gmtOffset, float daylightOffset); + + /** Set system time with provided timestamp + * + * @param ts timestamp in seconds from midnight Jan 1, 1970. + * @return error number, 0 for success. + */ + int setTimestamp(time_t ts); + + /** Provide the Unix time + * + * @return uint32_t The value of current Unix time. + */ + uint32_t getUnixTime(); + + /** Provide the timestamp from the year, month, date, hour, minute, + * and second provided. + * + * @param year The year. + * @param mon The month from 1 to 12. + * @param date The dates. + * @param hour The hours. + * @param mins The minutes. + * @param sec The seconds. + * @return time_t The value of timestamp. + */ + time_t getTimestamp(int year, int mon, int date, int hour, int mins, int sec); + + /** Provide the current year. + * + * @return int The value of current year. + */ + int getYear(); + + /** Provide the current month. + * + * @return int The value of current month. + */ + int getMonth(); + + /** Provide the current date. + * + * @return int The value of current date. + */ + int getDay(); + + /** Provide the current day of week. + * + * @return int The value of current day of week. + * + * @note 1 for sunday and 7 for saturday. + */ + int getDayOfWeek(); + + /** Provide the current day of week in String. + * + * @return String The value of day of week. + */ + String getDayOfWeekString(); + + /** Provide the current hour. + * + * @return int The value of current hour (0 to 23). + */ + int getHour(); + + /** Provide the current minute. + * + * @return int The value of current minute. + */ + int getMin(); + + /** Provide the current second. + * + * @return int The value of current second. + */ + int getSec(); + + /** Provide the total days of current year. + * + * @return int The value of total days of current year. + */ + int getNumberOfDayThisYear(); + + /** Provide the total days of from January 1, 1970 to specific date. + * + * @param year The year from 1970. + * @param mon The month from 1 to 12. + * @param day The dates. + * @return int The value of total days. + */ + int getTotalDays(int year, int month, int day); + + /** Provide the day of week from specific date. + * + * @param year The year from 1970. + * @param mon The month from 1 to 12. + * @param day The dates. + * @return int the value of day of week. + * @note 1 for sunday and 7 for saturday + */ + int dayofWeek(int year, int month, int day); + + /** Provide the second of current hour. + * + * @return int The value of current second. + */ + int getCurrentSecond(); + + /** Provide the current timestamp. + * + * @return uint64_t The value of current timestamp. + */ + uint64_t getCurrentTimestamp(); + + /** Provide the date and time from second counted from January 1, 1970. + * + * @param sec The seconds from January 1, 1970 00.00. + * @return tm The tm structured data. + * + * @note The returned structured data tm has the members e.g. + * tm_year (from 1900), tm_mon (from 0 to 11), tm_mday, tm_hour, + * tm_min and tm_sec. + */ + struct tm getTimeFromSec(int seconds); + + /** Provide the current date time string that valid for Email. + * + * @return String The current date time string. + */ + String getDateTimeString(); + + time_t now; + uint64_t msec_time_diff = 0; + struct tm timeinfo; + float TZ = 0.0; + uint8_t DST_MN = 0; + bool clockReady = false; + +private: + int totalDays(int y, int m, int d); + char *intStr(int value); + void setSysTime(); + const char *dow[7] = {"sunday", "monday", "tuesday", "wednesday", "thurseday", "friday", "saturday"}; + const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + const char *sdow[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +}; + +#endif //ESPTimeHelper_H diff --git a/lib/lib_div/lib_mail/src/extras/MIMEInfo.h b/lib/libesp32/lib_mail/src/extras/MIMEInfo.h old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/src/extras/MIMEInfo.h rename to lib/libesp32/lib_mail/src/extras/MIMEInfo.h index 0c432a88d..38269eb6b --- a/lib/lib_div/lib_mail/src/extras/MIMEInfo.h +++ b/lib/libesp32/lib_mail/src/extras/MIMEInfo.h @@ -1,65 +1,65 @@ -#ifndef MIMEInfo_H -#define MIMEInfo_H -#include - -enum esp_mail_file_extension -{ - esp_mail_file_extension_html, - esp_mail_file_extension_htm, - esp_mail_file_extension_css, - esp_mail_file_extension_txt, - esp_mail_file_extension_js, - esp_mail_file_extension_json, - esp_mail_file_extension_png, - esp_mail_file_extension_gif, - esp_mail_file_extension_jpg, - esp_mail_file_extension_ico, - esp_mail_file_extension_svg, - esp_mail_file_extension_ttf, - esp_mail_file_extension_otf, - esp_mail_file_extension_woff, - esp_mail_file_extension_woff2, - esp_mail_file_extension_eot, - esp_mail_file_extension_sfnt, - esp_mail_file_extension_xml, - esp_mail_file_extension_pdf, - esp_mail_file_extension_zip, - esp_mail_file_extension_gz, - esp_mail_file_extension_appcache, - esp_mail_file_extension_none, - esp_mail_file_extension_maxType -}; - -struct esp_mail_mime_prop_t -{ - char endsWith[10]; - char mimeType[50]; -}; - -const struct esp_mail_mime_prop_t mimeinfo[esp_mail_file_extension_maxType] PROGMEM = - { - {".html", "text/html"}, - {".htm", "text/html"}, - {".css", "text/css"}, - {".txt", "text/plain"}, - {".js", "application/javascript"}, - {".json", "application/json"}, - {".png", "image/png"}, - {".gif", "image/gif"}, - {".jpg", "image/jpeg"}, - {".ico", "image/x-icon"}, - {".svg", "image/svg+xml"}, - {".ttf", "application/x-font-ttf"}, - {".otf", "application/x-font-opentype"}, - {".woff", "application/font-woff"}, - {".woff2", "application/font-woff2"}, - {".eot", "application/vnd.ms-fontobject"}, - {".sfnt", "application/font-sfnt"}, - {".xml", "text/xml"}, - {".pdf", "application/pdf"}, - {".zip", "application/zip"}, - {".gz", "application/x-gzip"}, - {".appcache", "text/cache-manifest"}, - {"", "application/octet-stream"}}; - -#endif +#ifndef MIMEInfo_H +#define MIMEInfo_H +#include + +enum esp_mail_file_extension +{ + esp_mail_file_extension_html, + esp_mail_file_extension_htm, + esp_mail_file_extension_css, + esp_mail_file_extension_txt, + esp_mail_file_extension_js, + esp_mail_file_extension_json, + esp_mail_file_extension_png, + esp_mail_file_extension_gif, + esp_mail_file_extension_jpg, + esp_mail_file_extension_ico, + esp_mail_file_extension_svg, + esp_mail_file_extension_ttf, + esp_mail_file_extension_otf, + esp_mail_file_extension_woff, + esp_mail_file_extension_woff2, + esp_mail_file_extension_eot, + esp_mail_file_extension_sfnt, + esp_mail_file_extension_xml, + esp_mail_file_extension_pdf, + esp_mail_file_extension_zip, + esp_mail_file_extension_gz, + esp_mail_file_extension_appcache, + esp_mail_file_extension_none, + esp_mail_file_extension_maxType +}; + +struct esp_mail_mime_prop_t +{ + char endsWith[10]; + char mimeType[50]; +}; + +const struct esp_mail_mime_prop_t mimeinfo[esp_mail_file_extension_maxType] PROGMEM = + { + {".html", "text/html"}, + {".htm", "text/html"}, + {".css", "text/css"}, + {".txt", "text/plain"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + {".png", "image/png"}, + {".gif", "image/gif"}, + {".jpg", "image/jpeg"}, + {".ico", "image/x-icon"}, + {".svg", "image/svg+xml"}, + {".ttf", "application/x-font-ttf"}, + {".otf", "application/x-font-opentype"}, + {".woff", "application/font-woff"}, + {".woff2", "application/font-woff2"}, + {".eot", "application/vnd.ms-fontobject"}, + {".sfnt", "application/font-sfnt"}, + {".xml", "text/xml"}, + {".pdf", "application/pdf"}, + {".zip", "application/zip"}, + {".gz", "application/x-gzip"}, + {".appcache", "text/cache-manifest"}, + {"", "application/octet-stream"}}; + +#endif diff --git a/lib/lib_div/lib_mail/src/extras/RFC2047.cpp b/lib/libesp32/lib_mail/src/extras/RFC2047.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/extras/RFC2047.cpp rename to lib/libesp32/lib_mail/src/extras/RFC2047.cpp diff --git a/lib/lib_div/lib_mail/src/extras/RFC2047.h b/lib/libesp32/lib_mail/src/extras/RFC2047.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/extras/RFC2047.h rename to lib/libesp32/lib_mail/src/extras/RFC2047.h diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp old mode 100755 new mode 100644 similarity index 95% rename from lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp rename to lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp index 86bfbcb69..a1d386ea6 --- a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp +++ b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.cpp @@ -1,233 +1,233 @@ -/* - * Customized version of ESP32 HTTPClient Library. - * - * v 1.1.5 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * HTTPClient Arduino library for ESP32 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the HTTPClient for Arduino. - * Port to ESP32 by Evandro Luis Copercini (2017), - * changed fingerprints to CA verification. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * -*/ - -#ifndef ESP_Mail_HTTPClient32_CPP -#define ESP_Mail_HTTPClient32_CPP - -#ifdef ESP32 - -#include "ESP_Mail_HTTPClient32.h" - -ESP_Mail_HTTPClient32::ESP_Mail_HTTPClient32() -{ -} - -ESP_Mail_HTTPClient32::~ESP_Mail_HTTPClient32() -{ - if (_wcs) - { - _wcs->stop(); - _wcs.reset(nullptr); - _wcs.release(); - } - std::string().swap(_host); - std::string().swap(_caCertFile); -} - -bool ESP_Mail_HTTPClient32::begin(const char *host, uint16_t port) -{ - _host = host; - _port = port; - return true; -} - -bool ESP_Mail_HTTPClient32::connected() -{ - if (_wcs) - { - if (_secured) - return _wcs->connected(); - else - return _wcs->_ns_connected(); - } - return false; -} - -bool ESP_Mail_HTTPClient32::send(const char *header) -{ - if (!connected()) - return false; - if (_secured) - return (_wcs->write(header, strlen(header)) == strlen(header)); - else - return (_wcs->_ns_write(header, strlen(header)) == strlen(header)); -} - -int ESP_Mail_HTTPClient32::send(const char *header, const char *payload) -{ - size_t size = strlen(payload); - if (strlen(header) > 0) - { - if (!connect(_secured)) - { - return ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED; - } - - if (!send(header)) - { - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED; - } - } - - if (size > 0) - { - if (_secured) - { - if (_wcs->write(&payload[0], size) != size) - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; - } - else - { - if (_wcs->_ns_write(&payload[0], size) != size) - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; - } - } - - return 0; -} - -WiFiClient *ESP_Mail_HTTPClient32::stream(void) -{ - if (connected()) - return _wcs.get(); - return nullptr; -} -ESP_Mail_WCS32 *ESP_Mail_HTTPClient32::_stream(void) -{ - if (connected()) - return _wcs.get(); - return nullptr; -} - -size_t ESP_Mail_HTTPClient32::_ns_print(const char *buf) -{ - size_t size = strlen(buf); - return _wcs->_ns_write(&buf[0], size); -} - -size_t ESP_Mail_HTTPClient32::_ns_println(const char *buf) -{ - size_t size = strlen(buf); - size_t wr = _wcs->_ns_write((const char *)&buf[0], size); - std::string s = "\r\n"; - wr += _wcs->_ns_write(s.c_str(), s.length()); - return wr; -} - -bool ESP_Mail_HTTPClient32::connect(void) -{ - return connect(false); -} - -bool ESP_Mail_HTTPClient32::connect(bool secured) -{ - _secured = secured; - - if (connected()) - { - if (_secured) - { - while (_wcs->available() > 0) - _wcs->read(); - } - else - { - while (_wcs->_ns_available() > 0) - _wcs->_ns_read(); - } - return true; - } - - if (_debugCallback) - _wcs->setDebugCB(&_debugCallback); - _wcs->setSTARTTLS(!secured); - - if (!_wcs->connect(_host.c_str(), _port)) - return false; - return connected(); -} - -void ESP_Mail_HTTPClient32::setDebugCallback(DebugMsgCallback cb) -{ - _debugCallback = std::move(cb); -} - -void ESP_Mail_HTTPClient32::setCACert(const char *caCert) -{ - _wcs->setCACert(caCert); - if (caCert) - _certType = 1; - else - { - setInsecure(); - _certType = 0; - } - //_wcs->setNoDelay(true); -} - -void ESP_Mail_HTTPClient32::setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType) -{ - - if (strlen(caCertFile) > 0) - { - File f; - if (storageType == esp_mail_file_storage_type_flash) - { - ESP_MAIL_FLASH_FS.begin(); - if (ESP_MAIL_FLASH_FS.exists(caCertFile)) - f = ESP_MAIL_FLASH_FS.open(caCertFile, FILE_READ); - } - else if (storageType == esp_mail_file_storage_type_sd) - { - ESP_MAIL_SD_FS.begin(); - if (ESP_MAIL_SD_FS.exists(caCertFile)) - f = ESP_MAIL_SD_FS.open(caCertFile, FILE_READ); - } - - if (f) - { - size_t len = f.size(); - _wcs->loadCACert(f, len); - f.close(); - } - _certType = 2; - } - //_wcs->setNoDelay(true); -} - -void ESP_Mail_HTTPClient32::setInsecure() -{ - _wcs->setInsecure(); -} - -#endif //ESP32 - -#endif //ESP_Mail_HTTPClient32_CPP +/* + * Customized version of ESP32 HTTPClient Library. + * + * v 1.1.5 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP_Mail_HTTPClient32_CPP +#define ESP_Mail_HTTPClient32_CPP + +#ifdef ESP32 + +#include "ESP_Mail_HTTPClient32.h" + +ESP_Mail_HTTPClient32::ESP_Mail_HTTPClient32() +{ +} + +ESP_Mail_HTTPClient32::~ESP_Mail_HTTPClient32() +{ + if (_wcs) + { + _wcs->stop(); + _wcs.reset(nullptr); + _wcs.release(); + } + std::string().swap(_host); + std::string().swap(_caCertFile); +} + +bool ESP_Mail_HTTPClient32::begin(const char *host, uint16_t port) +{ + _host = host; + _port = port; + return true; +} + +bool ESP_Mail_HTTPClient32::connected() +{ + if (_wcs) + { + if (_secured) + return _wcs->connected(); + else + return _wcs->_ns_connected(); + } + return false; +} + +bool ESP_Mail_HTTPClient32::send(const char *header) +{ + if (!connected()) + return false; + if (_secured) + return (_wcs->write(header, strlen(header)) == strlen(header)); + else + return (_wcs->_ns_write(header, strlen(header)) == strlen(header)); +} + +int ESP_Mail_HTTPClient32::send(const char *header, const char *payload) +{ + size_t size = strlen(payload); + if (strlen(header) > 0) + { + if (!connect(_secured)) + { + return ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED; + } + + if (!send(header)) + { + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED; + } + } + + if (size > 0) + { + if (_secured) + { + if (_wcs->write(&payload[0], size) != size) + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; + } + else + { + if (_wcs->_ns_write(&payload[0], size) != size) + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; + } + } + + return 0; +} + +WiFiClient *ESP_Mail_HTTPClient32::stream(void) +{ + if (connected()) + return _wcs.get(); + return nullptr; +} +ESP_Mail_WCS32 *ESP_Mail_HTTPClient32::_stream(void) +{ + if (connected()) + return _wcs.get(); + return nullptr; +} + +size_t ESP_Mail_HTTPClient32::_ns_print(const char *buf) +{ + size_t size = strlen(buf); + return _wcs->_ns_write(&buf[0], size); +} + +size_t ESP_Mail_HTTPClient32::_ns_println(const char *buf) +{ + size_t size = strlen(buf); + size_t wr = _wcs->_ns_write((const char *)&buf[0], size); + std::string s = "\r\n"; + wr += _wcs->_ns_write(s.c_str(), s.length()); + return wr; +} + +bool ESP_Mail_HTTPClient32::connect(void) +{ + return connect(false); +} + +bool ESP_Mail_HTTPClient32::connect(bool secured) +{ + _secured = secured; + + if (connected()) + { + if (_secured) + { + while (_wcs->available() > 0) + _wcs->read(); + } + else + { + while (_wcs->_ns_available() > 0) + _wcs->_ns_read(); + } + return true; + } + + if (_debugCallback) + _wcs->setDebugCB(&_debugCallback); + _wcs->setSTARTTLS(!secured); + + if (!_wcs->connect(_host.c_str(), _port)) + return false; + return connected(); +} + +void ESP_Mail_HTTPClient32::setDebugCallback(DebugMsgCallback cb) +{ + _debugCallback = std::move(cb); +} + +void ESP_Mail_HTTPClient32::setCACert(const char *caCert) +{ + _wcs->setCACert(caCert); + if (caCert) + _certType = 1; + else + { + setInsecure(); + _certType = 0; + } + //_wcs->setNoDelay(true); +} + +void ESP_Mail_HTTPClient32::setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType) +{ + + if (strlen(caCertFile) > 0) + { + File f; + if (storageType == esp_mail_file_storage_type_flash) + { + ESP_MAIL_FLASH_FS.begin(); + if (ESP_MAIL_FLASH_FS.exists(caCertFile)) + f = ESP_MAIL_FLASH_FS.open(caCertFile, FILE_READ); + } + else if (storageType == esp_mail_file_storage_type_sd) + { + ESP_MAIL_SD_FS.begin(); + if (ESP_MAIL_SD_FS.exists(caCertFile)) + f = ESP_MAIL_SD_FS.open(caCertFile, FILE_READ); + } + + if (f) + { + size_t len = f.size(); + _wcs->loadCACert(f, len); + f.close(); + } + _certType = 2; + } + //_wcs->setNoDelay(true); +} + +void ESP_Mail_HTTPClient32::setInsecure() +{ + _wcs->setInsecure(); +} + +#endif //ESP32 + +#endif //ESP_Mail_HTTPClient32_CPP diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h rename to lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h index d0399d820..189e25ad1 --- a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h +++ b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_HTTPClient32.h @@ -1,142 +1,142 @@ -/* - * Customized version of ESP32 HTTPClient Library. - * - * v 1.1.5 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * HTTPClient Arduino library for ESP32 - * - * Copyright (c) 2015 Markus Sattler. All rights reserved. - * This file is part of the HTTPClient for Arduino. - * Port to ESP32 by Evandro Luis Copercini (2017), - * changed fingerprints to CA verification. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * -*/ - -#ifndef ESP_Mail_HTTPClient32_H -#define ESP_Mail_HTTPClient32_H - -#ifdef ESP32 - -#include -#include -#include -//#include -#include -#include "ESP_Mail_FS.h" -#include "ESP_Mail_WCS32.h" - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#define ESP_MAIL_FLASH_FS ESP_Mail_DEFAULT_FLASH_FS -#define ESP_MAIL_SD_FS ESP_Mail_DEFAULT_SD_FS - -#if __has_include() || __has_include() -#error WiFi UART bridge was not supported. -#endif - -#define ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED (-1) -#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED (-2) -#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3) -#define ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC 30 - -enum esp_mail_file_storage_type -{ - esp_mail_file_storage_type_none, - esp_mail_file_storage_type_flash, - esp_mail_file_storage_type_sd, - esp_mail_file_storage_type_univ -}; - -class ESP_Mail_HTTPClient32 -{ -public: - ESP_Mail_HTTPClient32(); - ~ESP_Mail_HTTPClient32(); - - /** - * Initialization of new http connection. - * \param host - Host name without protocols. - * \param port - Server's port. - * \return True as default. - * If no certificate string provided, use (const char*)NULL to CAcert param - */ - bool begin(const char *host, uint16_t port); - - /** - * Check the http connection status. - * \return True if connected. - */ - bool connected(); - - /** - * Establish http connection if header provided and send it, send payload if provided. - * \param header - The header string (constant chars array). - * \param payload - The payload string (constant chars array), optional. - * \return http status code, Return zero if new http connection and header and/or payload sent - * with no error or no header and payload provided. If obly payload provided, no new http connection was established. - */ - int send(const char *header, const char *payload); - - /** - * Send extra header without making new http connection (if send has been called) - * \param header - The header string (constant chars array). - * \return True if header sending success. - * Need to call send with header first. - */ - bool send(const char *header); - - /** - * Get the WiFi client pointer. - * \return WiFi client pointer. - */ - WiFiClient *stream(void); - - /** - * Set insecure mode - */ - void setInsecure(); - - ESP_Mail_WCS32 *_stream(void); - size_t _ns_print(const char *buf); - size_t _ns_println(const char *buf); - - int tcpTimeout = 40000; - bool connect(void); - bool connect(bool secured); - void setCACert(const char *caCert); - void setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType); - void setDebugCallback(DebugMsgCallback cb); - bool _secured = true; - - int _certType = -1; - std::string _caCertFile = ""; - esp_mail_file_storage_type _caCertFileStoreageType = esp_mail_file_storage_type::esp_mail_file_storage_type_none; - -protected: - DebugMsgCallback _debugCallback = NULL; - std::unique_ptr _wcs = std::unique_ptr(new ESP_Mail_WCS32()); - - std::string _host = ""; - uint16_t _port = 0; -}; - -#endif //ESP32 - -#endif //ESP_Mail_HTTPClient32_H +/* + * Customized version of ESP32 HTTPClient Library. + * + * v 1.1.5 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * HTTPClient Arduino library for ESP32 + * + * Copyright (c) 2015 Markus Sattler. All rights reserved. + * This file is part of the HTTPClient for Arduino. + * Port to ESP32 by Evandro Luis Copercini (2017), + * changed fingerprints to CA verification. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * +*/ + +#ifndef ESP_Mail_HTTPClient32_H +#define ESP_Mail_HTTPClient32_H + +#ifdef ESP32 + +#include +#include +#include +//#include +#include +#include "ESP_Mail_FS.h" +#include "ESP_Mail_WCS32.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#define ESP_MAIL_FLASH_FS ESP_Mail_DEFAULT_FLASH_FS +#define ESP_MAIL_SD_FS ESP_Mail_DEFAULT_SD_FS + +#if __has_include() || __has_include() +#error WiFi UART bridge was not supported. +#endif + +#define ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED (-1) +#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED (-2) +#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3) +#define ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC 30 + +enum esp_mail_file_storage_type +{ + esp_mail_file_storage_type_none, + esp_mail_file_storage_type_flash, + esp_mail_file_storage_type_sd, + esp_mail_file_storage_type_univ +}; + +class ESP_Mail_HTTPClient32 +{ +public: + ESP_Mail_HTTPClient32(); + ~ESP_Mail_HTTPClient32(); + + /** + * Initialization of new http connection. + * \param host - Host name without protocols. + * \param port - Server's port. + * \return True as default. + * If no certificate string provided, use (const char*)NULL to CAcert param + */ + bool begin(const char *host, uint16_t port); + + /** + * Check the http connection status. + * \return True if connected. + */ + bool connected(); + + /** + * Establish http connection if header provided and send it, send payload if provided. + * \param header - The header string (constant chars array). + * \param payload - The payload string (constant chars array), optional. + * \return http status code, Return zero if new http connection and header and/or payload sent + * with no error or no header and payload provided. If obly payload provided, no new http connection was established. + */ + int send(const char *header, const char *payload); + + /** + * Send extra header without making new http connection (if send has been called) + * \param header - The header string (constant chars array). + * \return True if header sending success. + * Need to call send with header first. + */ + bool send(const char *header); + + /** + * Get the WiFi client pointer. + * \return WiFi client pointer. + */ + WiFiClient *stream(void); + + /** + * Set insecure mode + */ + void setInsecure(); + + ESP_Mail_WCS32 *_stream(void); + size_t _ns_print(const char *buf); + size_t _ns_println(const char *buf); + + int tcpTimeout = 40000; + bool connect(void); + bool connect(bool secured); + void setCACert(const char *caCert); + void setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType); + void setDebugCallback(DebugMsgCallback cb); + bool _secured = true; + + int _certType = -1; + std::string _caCertFile = ""; + esp_mail_file_storage_type _caCertFileStoreageType = esp_mail_file_storage_type::esp_mail_file_storage_type_none; + +protected: + DebugMsgCallback _debugCallback = NULL; + std::unique_ptr _wcs = std::unique_ptr(new ESP_Mail_WCS32()); + + std::string _host = ""; + uint16_t _port = 0; +}; + +#endif //ESP32 + +#endif //ESP_Mail_HTTPClient32_H diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp rename to lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.cpp diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h b/lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h rename to lib/libesp32/lib_mail/src/wcs/esp32/ESP_Mail_WCS32.h diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.cpp b/lib/libesp32/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.cpp rename to lib/libesp32/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.cpp diff --git a/lib/lib_div/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.h b/lib/libesp32/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.h rename to lib/libesp32/lib_mail/src/wcs/esp32/esp_mail_ssl_client32.h diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.cpp diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_BearSSLHelpers.h diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.cpp diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.h b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.h rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_CertStoreBearSSL.h diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp old mode 100755 new mode 100644 similarity index 95% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp index 06a282e07..b1fdbff5f --- a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp +++ b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.cpp @@ -1,261 +1,261 @@ -/* - * HTTP Client for ESP8266 wrapper v1.0.3 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person returning a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef ESP_Mail_HTTPClient_CPP -#define ESP_Mail_HTTPClient_CPP - -#ifdef ESP8266 - -#include "ESP_Mail_HTTPClient.h" - -ESP_Mail_HTTPClient::ESP_Mail_HTTPClient() -{ -} - -ESP_Mail_HTTPClient::~ESP_Mail_HTTPClient() -{ - if (_wcs) - { - _wcs->stop(); - _wcs.reset(nullptr); - _wcs.release(); - } - - std::string().swap(_host); - std::string().swap(_caCertFile); - _cacert.reset(new char); - _cacert = nullptr; -} - -bool ESP_Mail_HTTPClient::begin(const char *host, uint16_t port) -{ - if (strcmp(_host.c_str(), host) != 0) - mflnChecked = false; - - _host = host; - _port = port; - - //probe for fragmentation support at the specified size - if (!mflnChecked) - { - fragmentable = _wcs->probeMaxFragmentLength(_host.c_str(), _port, chunkSize); - if (fragmentable) - { - _bsslRxSize = chunkSize; - _bsslTxSize = chunkSize; - _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); - } - mflnChecked = true; - } - - if (!fragmentable) - _wcs->setBufferSizes(maxRXBufSize / rxBufDivider, maxTXBufSize / txBufDivider); - - return true; -} - -bool ESP_Mail_HTTPClient::connected() -{ - if (_wcs) - { - if (_secured) - return _wcs->connected(); - else - return _wcs->_ns_connected(); - } - - return false; -} - -bool ESP_Mail_HTTPClient::send(const char *header) -{ - if (!connected()) - return false; - if (_secured) - return (_wcs->write((uint8_t *)header, strlen(header)) == strlen(header)); - else - return (_wcs->_ns_write((uint8_t *)header, strlen(header)) == strlen(header)); -} - -int ESP_Mail_HTTPClient::send(const char *header, const char *payload) -{ - size_t size = strlen(payload); - if (strlen(header) > 0) - { - if (!connect(_secured)) - { - return ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED; - } - - if (!send(header)) - { - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED; - } - } - - if (size > 0) - { - if (_secured) - { - if (_wcs->write((uint8_t *)&payload[0], size) != size) - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; - } - else - { - if (_wcs->_ns_write((uint8_t *)&payload[0], size) != size) - return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; - } - } - - return 0; -} - -WiFiClient *ESP_Mail_HTTPClient::stream(void) -{ - if (connected()) - return _wcs.get(); - return nullptr; -} - -ESP_Mail::ESP_Mail_WCS *ESP_Mail_HTTPClient::_stream(void) -{ - if (connected()) - return _wcs.get(); - return nullptr; -} - -size_t ESP_Mail_HTTPClient::_ns_print(const char *buf) -{ - size_t size = strlen(buf); - return _wcs->_ns_write((uint8_t *)&buf[0], size); -} - -size_t ESP_Mail_HTTPClient::_ns_println(const char *buf) -{ - size_t size = strlen(buf); - size_t wr = _wcs->_ns_write((uint8_t *)&buf[0], size); - std::string s = "\r\n"; - wr += _wcs->_ns_write((uint8_t *)s.c_str(), s.length()); - return wr; -} - -bool ESP_Mail_HTTPClient::connect(bool secured) -{ - _secured = secured; - - if (connected()) - { - if (_secured) - { - while (_wcs->available() > 0) - _wcs->read(); - } - else - { - while (_wcs->_ns_available() > 0) - _wcs->_ns_read(); - } - return true; - } - - _wcs->setStartTLS(!_secured); - if (!_wcs->connect(_host.c_str(), _port)) - return false; - - return connected(); -} - - -void ESP_Mail_HTTPClient::setCACert(const char *caCert) -{ - -#ifndef USING_AXTLS - _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); -#endif - - if (caCert) - { -#ifndef USING_AXTLS - _wcs->setTrustAnchors(new ESP_Mail::ESP_Mail_X509List(caCert)); -#else - _wcs->setCACert_P(caCert, strlen_P(caCert)); -#endif - _certType = 1; - } - else - { -#ifndef USING_AXTLS - _wcs->setInsecure(); -#endif - _certType = 0; - } - - _wcs->setNoDelay(true); -} - -void ESP_Mail_HTTPClient::setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType, uint8_t sdPin) -{ - -#ifndef USING_AXTLS - _sdPin = sdPin; - _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); - - if (_clockReady && strlen(caCertFile) > 0) - { - - fs::File f; - if (storageType == esp_mail_file_storage_type_flash) - { - ESP_MAIL_FLASH_FS.begin(); - if (ESP_MAIL_FLASH_FS.exists(caCertFile)) - f = ESP_MAIL_FLASH_FS.open(caCertFile, "r"); - } - else if (storageType == esp_mail_file_storage_type_sd) - { - ESP_MAIL_SD_FS.begin(_sdPin); - if (ESP_MAIL_SD_FS.exists(caCertFile)) - f = ESP_MAIL_SD_FS.open(caCertFile, FILE_READ); - } - - if (f) - { - size_t len = f.size(); - uint8_t *der = new uint8_t[len]; - if (f.available()) - f.read(der, len); - f.close(); - _wcs->setTrustAnchors(new ESP_Mail::ESP_Mail_X509List(der, len)); - delete[] der; - } - _certType = 2; - } -#endif - - _wcs->setNoDelay(true); -} - -#endif /* ESP8266 */ - +/* + * HTTP Client for ESP8266 wrapper v1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP_Mail_HTTPClient_CPP +#define ESP_Mail_HTTPClient_CPP + +#ifdef ESP8266 + +#include "ESP_Mail_HTTPClient.h" + +ESP_Mail_HTTPClient::ESP_Mail_HTTPClient() +{ +} + +ESP_Mail_HTTPClient::~ESP_Mail_HTTPClient() +{ + if (_wcs) + { + _wcs->stop(); + _wcs.reset(nullptr); + _wcs.release(); + } + + std::string().swap(_host); + std::string().swap(_caCertFile); + _cacert.reset(new char); + _cacert = nullptr; +} + +bool ESP_Mail_HTTPClient::begin(const char *host, uint16_t port) +{ + if (strcmp(_host.c_str(), host) != 0) + mflnChecked = false; + + _host = host; + _port = port; + + //probe for fragmentation support at the specified size + if (!mflnChecked) + { + fragmentable = _wcs->probeMaxFragmentLength(_host.c_str(), _port, chunkSize); + if (fragmentable) + { + _bsslRxSize = chunkSize; + _bsslTxSize = chunkSize; + _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); + } + mflnChecked = true; + } + + if (!fragmentable) + _wcs->setBufferSizes(maxRXBufSize / rxBufDivider, maxTXBufSize / txBufDivider); + + return true; +} + +bool ESP_Mail_HTTPClient::connected() +{ + if (_wcs) + { + if (_secured) + return _wcs->connected(); + else + return _wcs->_ns_connected(); + } + + return false; +} + +bool ESP_Mail_HTTPClient::send(const char *header) +{ + if (!connected()) + return false; + if (_secured) + return (_wcs->write((uint8_t *)header, strlen(header)) == strlen(header)); + else + return (_wcs->_ns_write((uint8_t *)header, strlen(header)) == strlen(header)); +} + +int ESP_Mail_HTTPClient::send(const char *header, const char *payload) +{ + size_t size = strlen(payload); + if (strlen(header) > 0) + { + if (!connect(_secured)) + { + return ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED; + } + + if (!send(header)) + { + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED; + } + } + + if (size > 0) + { + if (_secured) + { + if (_wcs->write((uint8_t *)&payload[0], size) != size) + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; + } + else + { + if (_wcs->_ns_write((uint8_t *)&payload[0], size) != size) + return ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED; + } + } + + return 0; +} + +WiFiClient *ESP_Mail_HTTPClient::stream(void) +{ + if (connected()) + return _wcs.get(); + return nullptr; +} + +ESP_Mail::ESP_Mail_WCS *ESP_Mail_HTTPClient::_stream(void) +{ + if (connected()) + return _wcs.get(); + return nullptr; +} + +size_t ESP_Mail_HTTPClient::_ns_print(const char *buf) +{ + size_t size = strlen(buf); + return _wcs->_ns_write((uint8_t *)&buf[0], size); +} + +size_t ESP_Mail_HTTPClient::_ns_println(const char *buf) +{ + size_t size = strlen(buf); + size_t wr = _wcs->_ns_write((uint8_t *)&buf[0], size); + std::string s = "\r\n"; + wr += _wcs->_ns_write((uint8_t *)s.c_str(), s.length()); + return wr; +} + +bool ESP_Mail_HTTPClient::connect(bool secured) +{ + _secured = secured; + + if (connected()) + { + if (_secured) + { + while (_wcs->available() > 0) + _wcs->read(); + } + else + { + while (_wcs->_ns_available() > 0) + _wcs->_ns_read(); + } + return true; + } + + _wcs->setStartTLS(!_secured); + if (!_wcs->connect(_host.c_str(), _port)) + return false; + + return connected(); +} + + +void ESP_Mail_HTTPClient::setCACert(const char *caCert) +{ + +#ifndef USING_AXTLS + _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); +#endif + + if (caCert) + { +#ifndef USING_AXTLS + _wcs->setTrustAnchors(new ESP_Mail::ESP_Mail_X509List(caCert)); +#else + _wcs->setCACert_P(caCert, strlen_P(caCert)); +#endif + _certType = 1; + } + else + { +#ifndef USING_AXTLS + _wcs->setInsecure(); +#endif + _certType = 0; + } + + _wcs->setNoDelay(true); +} + +void ESP_Mail_HTTPClient::setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType, uint8_t sdPin) +{ + +#ifndef USING_AXTLS + _sdPin = sdPin; + _wcs->setBufferSizes(_bsslRxSize, _bsslTxSize); + + if (_clockReady && strlen(caCertFile) > 0) + { + + fs::File f; + if (storageType == esp_mail_file_storage_type_flash) + { + ESP_MAIL_FLASH_FS.begin(); + if (ESP_MAIL_FLASH_FS.exists(caCertFile)) + f = ESP_MAIL_FLASH_FS.open(caCertFile, "r"); + } + else if (storageType == esp_mail_file_storage_type_sd) + { + ESP_MAIL_SD_FS.begin(_sdPin); + if (ESP_MAIL_SD_FS.exists(caCertFile)) + f = ESP_MAIL_SD_FS.open(caCertFile, FILE_READ); + } + + if (f) + { + size_t len = f.size(); + uint8_t *der = new uint8_t[len]; + if (f.available()) + f.read(der, len); + f.close(); + _wcs->setTrustAnchors(new ESP_Mail::ESP_Mail_X509List(der, len)); + delete[] der; + } + _certType = 2; + } +#endif + + _wcs->setNoDelay(true); +} + +#endif /* ESP8266 */ + #endif /* ESP_Mail_HTTPClient_CPP */ \ No newline at end of file diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h old mode 100755 new mode 100644 similarity index 96% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h index 19028e682..883158a8d --- a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h +++ b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_HTTPClient.h @@ -1,149 +1,149 @@ -/* - * HTTP Client for ESP8266 wrapper v1.0.3 - * - * The MIT License (MIT) - * Copyright (c) 2021 K. Suwatchai (Mobizt) - * - * - * Permission is hereby granted, free of charge, to any person returning a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef ESP_Mail_HTTPClient_H -#define ESP_Mail_HTTPClient_H - -#ifdef ESP8266 - -//ARDUINO_ESP8266_GIT_VER -//2.6.2 0xbc204a9b -//2.6.1 0x482516e3 -//2.6.0 0x643ec203 -//2.5.2 0x8b899c12 -//2.5.1 0xac02aff5 -//2.5.0 0x951aeffa -//2.5.0-beta3 0x21db8fc9 -//2.5.0-beta2 0x0fd86a07 -//2.5.0-beta1 0x9c1e03a1 -//2.4.2 0xbb28d4a3 -//2.4.1 0x614f7c32 -//2.4.0 0x4ceabea9 -//2.4.0-rc2 0x0c897c37 -//2.4.0-rc1 0xf6d232f1 - -#include -#include -#include -#include - -#include "SDK_Version_Common.h" - -#ifndef ARDUINO_ESP8266_GIT_VER -#error Your ESP8266 Arduino Core SDK is outdated, please update. From Arduino IDE go to Boards Manager and search 'esp8266' then select the latest version. -#endif - -#include - -#if ARDUINO_ESP8266_GIT_VER != 0xf6d232f1 && ARDUINO_ESP8266_GIT_VER != 0x0c897c37 && ARDUINO_ESP8266_GIT_VER != 0x4ceabea9 && ARDUINO_ESP8266_GIT_VER != 0x614f7c32 && ARDUINO_ESP8266_GIT_VER != 0xbb28d4a3 -#include "ESP_Mail_WCS.h" -#include "ESP_Mail_BearSSLHelpers.h" -#define ESP_MAIL_SSL_CLIENT ESP_Mail::ESP_Mail_WCS -#else -#error Please update the ESP8266 Arduino Core SDK to latest version. -#endif - -#define FS_NO_GLOBALS -#include -#include -#include "ESP_Mail_FS.h" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#define ESP_MAIL_FLASH_FS ESP_Mail_DEFAULT_FLASH_FS -#define ESP_MAIL_SD_FS ESP_Mail_DEFAULT_SD_FS - -#if __has_include() || __has_include() -#error WiFi UART bridge was not supported. -#endif - -#define ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED (-1) -#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED (-2) -#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3) -#define ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC 30 - -enum esp_mail_file_storage_type -{ - esp_mail_file_storage_type_none, - esp_mail_file_storage_type_flash, - esp_mail_file_storage_type_sd, - esp_mail_file_storage_type_univ -}; - -class ESP_Mail_HTTPClient -{ - -public: - ESP_Mail_HTTPClient(); - ~ESP_Mail_HTTPClient(); - - bool begin(const char *host, uint16_t port); - - bool connected(void); - - int send(const char *header, const char *payload); - - bool send(const char *header); - - WiFiClient *stream(void); - - ESP_Mail::ESP_Mail_WCS *_stream(void); - - size_t _ns_print(const char *buf); - - size_t _ns_println(const char *buf); - - void setCACert(const char *caCert); - void setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType, uint8_t sdPin); - bool connect(bool secured); - - int _certType = -1; - std::string _caCertFile = ""; - esp_mail_file_storage_type _caCertFileStoreageType = esp_mail_file_storage_type::esp_mail_file_storage_type_none; - uint16_t tcpTimeout = 40000; - - uint8_t _sdPin = 15; - bool _clockReady = false; - uint16_t _bsslRxSize = 1024; - uint16_t _bsslTxSize = 1024; - bool fragmentable = false; - int chunkSize = 1024; - int maxRXBufSize = 16384; //SSL full supported 16 kB - int maxTXBufSize = 16384; - bool mflnChecked = false; - int rxBufDivider = maxRXBufSize / chunkSize; - int txBufDivider = maxRXBufSize / chunkSize; - ; - -private: - std::unique_ptr _wcs = std::unique_ptr(new ESP_MAIL_SSL_CLIENT()); - std::unique_ptr _cacert; - std::string _host = ""; - uint16_t _port = 0; - bool _secured = true; -}; - -#endif /* ESP8266 */ - +/* + * HTTP Client for ESP8266 wrapper v1.0.3 + * + * The MIT License (MIT) + * Copyright (c) 2021 K. Suwatchai (Mobizt) + * + * + * Permission is hereby granted, free of charge, to any person returning a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef ESP_Mail_HTTPClient_H +#define ESP_Mail_HTTPClient_H + +#ifdef ESP8266 + +//ARDUINO_ESP8266_GIT_VER +//2.6.2 0xbc204a9b +//2.6.1 0x482516e3 +//2.6.0 0x643ec203 +//2.5.2 0x8b899c12 +//2.5.1 0xac02aff5 +//2.5.0 0x951aeffa +//2.5.0-beta3 0x21db8fc9 +//2.5.0-beta2 0x0fd86a07 +//2.5.0-beta1 0x9c1e03a1 +//2.4.2 0xbb28d4a3 +//2.4.1 0x614f7c32 +//2.4.0 0x4ceabea9 +//2.4.0-rc2 0x0c897c37 +//2.4.0-rc1 0xf6d232f1 + +#include +#include +#include +#include + +#include "SDK_Version_Common.h" + +#ifndef ARDUINO_ESP8266_GIT_VER +#error Your ESP8266 Arduino Core SDK is outdated, please update. From Arduino IDE go to Boards Manager and search 'esp8266' then select the latest version. +#endif + +#include + +#if ARDUINO_ESP8266_GIT_VER != 0xf6d232f1 && ARDUINO_ESP8266_GIT_VER != 0x0c897c37 && ARDUINO_ESP8266_GIT_VER != 0x4ceabea9 && ARDUINO_ESP8266_GIT_VER != 0x614f7c32 && ARDUINO_ESP8266_GIT_VER != 0xbb28d4a3 +#include "ESP_Mail_WCS.h" +#include "ESP_Mail_BearSSLHelpers.h" +#define ESP_MAIL_SSL_CLIENT ESP_Mail::ESP_Mail_WCS +#else +#error Please update the ESP8266 Arduino Core SDK to latest version. +#endif + +#define FS_NO_GLOBALS +#include +#include +#include "ESP_Mail_FS.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#define ESP_MAIL_FLASH_FS ESP_Mail_DEFAULT_FLASH_FS +#define ESP_MAIL_SD_FS ESP_Mail_DEFAULT_SD_FS + +#if __has_include() || __has_include() +#error WiFi UART bridge was not supported. +#endif + +#define ESP_MAIL_ERROR_HTTPC_ERROR_CONNECTION_REFUSED (-1) +#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_HEADER_FAILED (-2) +#define ESP_MAIL_ERROR_HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3) +#define ESP_MAIL_DEFAULT_TCP_TIMEOUT_SEC 30 + +enum esp_mail_file_storage_type +{ + esp_mail_file_storage_type_none, + esp_mail_file_storage_type_flash, + esp_mail_file_storage_type_sd, + esp_mail_file_storage_type_univ +}; + +class ESP_Mail_HTTPClient +{ + +public: + ESP_Mail_HTTPClient(); + ~ESP_Mail_HTTPClient(); + + bool begin(const char *host, uint16_t port); + + bool connected(void); + + int send(const char *header, const char *payload); + + bool send(const char *header); + + WiFiClient *stream(void); + + ESP_Mail::ESP_Mail_WCS *_stream(void); + + size_t _ns_print(const char *buf); + + size_t _ns_println(const char *buf); + + void setCACert(const char *caCert); + void setCertFile(const char *caCertFile, esp_mail_file_storage_type storageType, uint8_t sdPin); + bool connect(bool secured); + + int _certType = -1; + std::string _caCertFile = ""; + esp_mail_file_storage_type _caCertFileStoreageType = esp_mail_file_storage_type::esp_mail_file_storage_type_none; + uint16_t tcpTimeout = 40000; + + uint8_t _sdPin = 15; + bool _clockReady = false; + uint16_t _bsslRxSize = 1024; + uint16_t _bsslTxSize = 1024; + bool fragmentable = false; + int chunkSize = 1024; + int maxRXBufSize = 16384; //SSL full supported 16 kB + int maxTXBufSize = 16384; + bool mflnChecked = false; + int rxBufDivider = maxRXBufSize / chunkSize; + int txBufDivider = maxRXBufSize / chunkSize; + ; + +private: + std::unique_ptr _wcs = std::unique_ptr(new ESP_MAIL_SSL_CLIENT()); + std::unique_ptr _cacert; + std::string _host = ""; + uint16_t _port = 0; + bool _secured = true; +}; + +#endif /* ESP8266 */ + #endif /* ESP_Mail_HTTPClient_H */ \ No newline at end of file diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.cpp diff --git a/lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h b/lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h old mode 100755 new mode 100644 similarity index 100% rename from lib/lib_div/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h rename to lib/libesp32/lib_mail/src/wcs/esp8266/ESP_Mail_WCS.h diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 8f240ac5a..48a10c264 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -77,7 +77,7 @@ board = esp32-odroid [env:tasmota32-core2] extends = env:tasmota32-lvgl board = esp32-m5core2 -build_flags = ${env:tasmota32-lvgl.build_flags} -DUSE_I2S_SAY_TIME -DUSE_I2S_WEBRADIO -DUSE_SENDMAIL -DUSE_ESP32MAIL +build_flags = ${env:tasmota32-lvgl.build_flags} -DUSE_I2S_SAY_TIME -DUSE_I2S_WEBRADIO -DUSE_SENDMAIL lib_extra_dirs = lib/libesp32, lib/libesp32_lvgl, lib/lib_basic, lib/lib_i2c, lib/lib_rf, lib/lib_div, lib/lib_ssl, lib/lib_display, lib/lib_audio [env:tasmota32-bluetooth] diff --git a/tasmota/include/sendemail_ESP8266.h b/tasmota/include/sendemail_ESP8266.h deleted file mode 100644 index a56f6916f..000000000 --- a/tasmota/include/sendemail_ESP8266.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __SENDEMAIL_H -#define __SENDEMAIL_H - -//#define DEBUG_EMAIL_PORT - -#include -#include "WiFiClientSecureLightBearSSL.h" - -class SendEmail -{ - private: - const String host; - const int port; - const String user; - const String passwd; - const int timeout; - const bool ssl; - const int auth_used; - // use bear ssl - BearSSL::WiFiClientSecure_light *client; - - String readClient(); - void a3_to_a4(unsigned char * a4, unsigned char * a3); - int base64_encode(char *output, const char *input, int inputLen); - public: - SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); - bool send(const String& from, const String& to, const String& subject, const char *msg); - void send_message_txt(char *msg); - ~SendEmail() {client->stop(); delete client;} -}; - -#endif diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 1ce6bf230..9412afbf5 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1129,7 +1129,15 @@ \*********************************************************************************************/ #ifdef USE_CONFIG_OVERRIDE - #include "user_config_override.h" // Configuration overrides for my_user_config.h + #include "user_config_override.h" // Configuration overrides for my_user_config.h +#endif + +/*********************************************************************************************\ + * Post-process obsoletes +\*********************************************************************************************/ + +#ifdef USE_ESP32MAIL +#define USE_SENDMAIL // USE_ESP32MAIL is replaced by USE_SENDMAIL #endif /*********************************************************************************************\ @@ -1168,8 +1176,14 @@ * Post-process compile options for TLS \*********************************************************************************************/ -#if defined(USE_MQTT_TLS) || defined(USE_SENDMAIL) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS) || defined(USE_ALEXA_AVS) - #define USE_TLS // flag indicates we need to include TLS code +#ifdef ESP8266 +#ifdef USE_SENDMAIL + #define USE_TLS // flag indicates we need to include TLS code +#endif +#endif + +#if defined(USE_MQTT_TLS) || defined(USE_TELEGRAM) || defined(USE_WEBCLIENT_HTTPS) || defined(USE_ALEXA_AVS) + #define USE_TLS // flag indicates we need to include TLS code #endif /*********************************************************************************************\ diff --git a/tasmota/tasmota_support/sendemail_ESP8266.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino similarity index 82% rename from tasmota/tasmota_support/sendemail_ESP8266.ino rename to tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino index 508ca8131..da9314850 100644 --- a/tasmota/tasmota_support/sendemail_ESP8266.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino @@ -1,26 +1,62 @@ +/* + xdrv_01_1_webserver_mail.ino - Mail client + + SPDX-FileCopyrightText: 2019 Gerhard Mutz and Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + #ifdef ESP8266 +#ifdef USE_WEBSERVER #ifdef USE_SENDMAIL -#include "include/sendemail_ESP8266.h" +/*********************************************************************************************\ + * ESP8266 SendMail works only with server port 465 SSL and doesnt support STARTTLS (not supported in Arduino) + * only a couple of mailservers support this (e.g. gmail,gmx,yahoo,freenetmail) + * + * sendmail [server:port:user:passwd:::subject] body + * + * sendmail [*:*:*:*:*:to:subject] data uses defines from user_config_overwrite + * #define EMAIL_USER "user" + * #define EMAIL_PASSWORD "passwd" + * #define EMAIL_FROM "" + * #define EMAIL_SERVER "smtp.gmail.com" + * #define EMAIL_PORT 465 + * + * if email body consist of a single * and scripter is present + * and a section >m is found, the lines in this section (until #) are sent as email body + * + * sendmail works with pre2.6 using Light BearSSL + * HW Watchdog 8.44 sec. + * SW Watchdog 3.2 sec. +\*********************************************************************************************/ -// enable serial debugging -//#define DEBUG_EMAIL_PORT +//#define DEBUG_EMAIL_PORT // Enable serial debugging -// sendmail works only with server port 465 SSL and doesnt support STARTTLS (not supported in Arduino) -// only a couple of mailservers support this (e.g. gmail,gmx,yahoo,freenetmail) -// sendmail [server:port:user:passwd:from:to:subject] body -// sendmail [*:*:*:*:*:to:subject] data uses defines from user_config_overwrite -// #define EMAIL_USER "user" -// #define EMAIL_PASSWORD "passwd" -// #define EMAIL_FROM "" -// #define EMAIL_SERVER "smtp.gmail.com" -// #define EMAIL_PORT 465 -// if email body consist of a single * and scripter is present -// and a section >m is found, the lines in this section (until #) are sent -// as email body +#include +#include "WiFiClientSecureLightBearSSL.h" -// sendmail works with pre2.6 using Light BearSSL -//HW Watchdog 8.44 sec. -//SW Watchdog 3.2 sec. +class SendEmail +{ + private: + const String host; + const int port; + const String user; + const String passwd; + const int timeout; + const bool ssl; + const int auth_used; + // use bear ssl + BearSSL::WiFiClientSecure_light *client; + + String readClient(); + void a3_to_a4(unsigned char * a4, unsigned char * a3); + int base64_encode(char *output, const char *input, int inputLen); + public: + SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); + bool send(const String& from, const String& to, const String& subject, const char *msg); + void send_message_txt(char *msg); + ~SendEmail() {client->stop(); delete client;} +}; #ifndef SEND_MAIL_MINRAM #define SEND_MAIL_MINRAM 12*1024 @@ -142,7 +178,7 @@ uint16_t SendMail(char *buffer) { if (*mserv=='*') { mserv=xPSTR(EMAIL_SERVER); } -#endif //USE_SENDMAIL +#endif #ifdef DEBUG_EMAIL_PORT @@ -352,7 +388,7 @@ String buffer; g_client=client; script_send_email_body(xsend_message_txt); } else { -#endif +#endif // USE_SCRIPT buffer = F("\r\n"); client->print(buffer); client->println(msg); @@ -396,7 +432,7 @@ void xsend_message_txt(char *msg) { g_client->println(msg); g_client->print(F("\r\n--frontier\r\n")); } -#else +#else // No USE_UFILESYS if (*msg=='&') { msg++; attach_Array(msg); @@ -406,7 +442,7 @@ void xsend_message_txt(char *msg) { g_client->println(msg); g_client->print(F("\r\n--frontier\r\n")); } -#endif +#endif // USE_UFILESYS } float *get_array_by_name(char *name, uint16_t *alen); @@ -447,7 +483,7 @@ void attach_Array(char *aname) { } g_client->print(F("\r\n--frontier\r\n")); } -#endif +#endif // USE_SCRIPT #ifdef USE_UFILESYS @@ -481,9 +517,8 @@ void attach_File(char *path) { g_client->print(F("\r\n--frontier\r\n")); } -#endif // USE_UFILESYS +#endif // USE_UFILESYS - -#endif // USE_SENDMAIL - -#endif // ESP8266 +#endif // USE_SENDMAIL +#endif // USE_WEBSERVER +#endif // ESP8266 \ No newline at end of file diff --git a/tasmota/tasmota_support/sendemail_ESP32.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino similarity index 89% rename from tasmota/tasmota_support/sendemail_ESP32.ino rename to tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino index d7f8a2b4a..8dbee213c 100644 --- a/tasmota/tasmota_support/sendemail_ESP32.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino @@ -1,14 +1,39 @@ -#ifdef USE_ESP32MAIL +/* + xdrv_01_2_webserver_esp32_mail.ino - Mail client + + SPDX-FileCopyrightText: 2020 Gerhard Mutz and Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef ESP32 +#ifdef USE_WEBSERVER +#ifdef USE_SENDMAIL +/*********************************************************************************************\ + * ESP32 SendMail works with server port 465 SSL and 587 STARTTLS + * + * sendmail [server:port:user:passwd:from:to:subject] body + * + * sendmail [*:*:*:*:*:to:subject] data uses defines from user_config_overwrite + * #define EMAIL_USER "user" + * #define EMAIL_PASSWORD "passwd" + * #define EMAIL_FROM "mr.x@gmail.com" + * #define EMAIL_SERVER "smtp.gmail.com" + * #define EMAIL_PORT 465 + * + * if email body consist of a single * and scripter is present + * and a section >m is found, the lines in this section (until #) are sent as email body +\*********************************************************************************************/ #include +//#define DEBUG_EMAIL_PORT // Enable serial debugging #ifndef SEND_MAIL32_MINRAM #undef SEND_MAIL32_MINRAM #define SEND_MAIL32_MINRAM 70*1024 #endif - #define xPSTR(a) a #define MAX_ATTCHMENTS 8 char *attachments[MAX_ATTCHMENTS]; @@ -20,8 +45,6 @@ SMTPSession *smtp; //SMTPSession smtp; void smtpCallback(SMTP_Status status); -//#define DEBUG_EMAIL_PORT - uint16_t SendMail(char *buffer) { char *params,*oparams = 0; const char *mserv; @@ -379,8 +402,6 @@ void Tasmota_print(const char *txt) { #endif } - - - - -#endif // USE_ESP32MAIL +#endif // USE_SENDMAIL +#endif // USE_WEBSERVER +#endif // ESP32 \ No newline at end of file diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino similarity index 99% rename from tasmota/tasmota_xdrv_driver/xdrv_01_webserver.ino rename to tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index 47a5aa592..72fce484d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -3484,7 +3484,7 @@ const char kWebCommands[] PROGMEM = "|" // No prefix #ifdef USE_EMULATION D_CMND_EMULATION "|" #endif -#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) +#ifdef USE_SENDMAIL D_CMND_SENDMAIL "|" #endif D_CMND_WEBSERVER "|" D_CMND_WEBPASSWORD "|" D_CMND_WEBREFRESH "|" D_CMND_WEBSEND "|" D_CMND_WEBQUERY "|" @@ -3505,7 +3505,7 @@ void (* const WebCommand[])(void) PROGMEM = { #ifdef USE_EMULATION &CmndEmulation, #endif -#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) +#ifdef USE_SENDMAIL &CmndSendmail, #endif &CmndWebServer, &CmndWebPassword, &CmndWebRefresh, &CmndWebSend, &CmndWebQuery, @@ -3564,9 +3564,8 @@ void CmndEmulation(void) } #endif // USE_EMULATION -#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) -void CmndSendmail(void) -{ +#ifdef USE_SENDMAIL +void CmndSendmail(void) { if (XdrvMailbox.data_len > 0) { uint8_t result = SendMail(XdrvMailbox.data); char stemp1[20]; @@ -3575,7 +3574,6 @@ void CmndSendmail(void) } #endif // USE_SENDMAIL - void CmndWebServer(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 2)) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino index d365beb21..d39265ea5 100755 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino @@ -6139,7 +6139,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } -#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) +#ifdef USE_SENDMAIL else if (!strncmp(lp, "mail", 4)) { lp+=5; //char tmp[256]; @@ -6151,7 +6151,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } goto next_line; } -#endif +#endif // USE_SENDMAIL else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { // execute cmd uint8_t sflag = 0,pflg = 0,svmqtt,swll; @@ -9720,7 +9720,7 @@ exgc: #endif //USE_SCRIPT_WEB_DISPLAY -#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) +#ifdef USE_SENDMAIL void script_send_email_body(void(*func)(char *)) { uint8_t msect = Run_Scripter1(">m", -2, 0); @@ -9753,7 +9753,7 @@ uint8_t msect = Run_Scripter1(">m", -2, 0); func((char*)"*"); } } -#endif //USE_SENDMAIL +#endif // USE_SENDMAIL #ifdef USE_SCRIPT_JSON_EXPORT void ScriptJsonAppend(void) { From 6cd19cbdf933e9ac38cbeb5c566a51ef48072e8a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 21 Jun 2022 23:03:34 +0200 Subject: [PATCH 007/219] Refactor sendmail --- .../xdrv_01_1_webserver_mail.ino | 404 ++++++++---------- .../xdrv_01_2_webserver_esp32_mail.ino | 337 +++++++-------- 2 files changed, 324 insertions(+), 417 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino index da9314850..b7c77924d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino @@ -30,11 +30,37 @@ * SW Watchdog 3.2 sec. \*********************************************************************************************/ -//#define DEBUG_EMAIL_PORT // Enable serial debugging +//#define DEBUG_EMAIL_PORT // Enable debugging + +#ifndef MAIL_TIMEOUT +#define MAIL_TIMEOUT 2000 +#endif + +#ifndef SEND_MAIL_MINRAM +#define SEND_MAIL_MINRAM 12*1024 +#endif + +#define xPSTR(a) a #include #include "WiFiClientSecureLightBearSSL.h" +void script_send_email_body(void(*func)(char *)); + +/*********************************************************************************************/ + +#ifdef DEBUG_EMAIL_PORT +void MailWriteAddLogBuffer(String *buffer) { + AddLog(LOG_LEVEL_INFO, PSTR("MAI: > C: %s"), buffer->c_str()); +} + +void MailReadAddLogBuffer(String *buffer) { + AddLog(LOG_LEVEL_INFO, PSTR("MAI: < S: %s"), buffer->c_str()); +} +#endif + +/*********************************************************************************************/ + class SendEmail { private: @@ -49,173 +75,24 @@ class SendEmail BearSSL::WiFiClientSecure_light *client; String readClient(); - void a3_to_a4(unsigned char * a4, unsigned char * a3); - int base64_encode(char *output, const char *input, int inputLen); public: SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used); bool send(const String& from, const String& to, const String& subject, const char *msg); - void send_message_txt(char *msg); +// void send_message_txt(char *msg); ~SendEmail() {client->stop(); delete client;} }; -#ifndef SEND_MAIL_MINRAM -#define SEND_MAIL_MINRAM 12*1024 -#endif - -void script_send_email_body(void(*func)(char *)); - -#define xPSTR(a) a - -uint16_t SendMail(char *buffer) { - char *params,*oparams; - const char *mserv; - uint16_t port; - const char *user; - const char *pstr; - const char *passwd; - const char *from; - const char *to; - const char *subject; - const char *cmd; - char auth=0; - uint16_t status=1; - SendEmail *mail=0; - uint16_t blen; - char *endcmd; - - -// return if not enough memory - uint16_t mem=ESP.getFreeHeap(); - if (memsend(from,to,subject,cmd); - delete mail; - if (result==true) status=0; - } - -exit: - if (oparams) free(oparams); - return status; -} - - WiFiClient *g_client; + SendEmail::SendEmail(const String& host, const int port, const String& user, const String& passwd, const int timeout, const int auth_used) : - host(host), port(port), user(user), passwd(passwd), timeout(timeout), ssl(ssl), auth_used(auth_used), client(new BearSSL::WiFiClientSecure_light(1024,1024)) { + host(host), + port(port), + user(user), + passwd(passwd), + timeout(timeout), + ssl(ssl), + auth_used(auth_used), + client(new BearSSL::WiFiClientSecure_light(1024,1024)) { } String SendEmail::readClient() { @@ -231,91 +108,75 @@ String SendEmail::readClient() { } bool SendEmail::send(const String& from, const String& to, const String& subject, const char *msg) { -bool status=false; -String buffer; - - if (!host.length()) { - return status; - } + if (!host.length()) { return false; } client->setTimeout(timeout); client->setInsecure(); // smtp connect #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("Connecting: %s on port %d"),host.c_str(),port); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: > C: connecting host %s on port %d"), host.c_str(), port); #endif if (!client->connect(host.c_str(), port)) { #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("Connection failed")); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: ! E: connection failed")); #endif - goto exit; + return false; } - buffer = readClient(); + String buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("220"))) { - goto exit; - } + if (!buffer.startsWith(F("220"))) { return false; } buffer = F("EHLO "); buffer += client->localIP().toString(); - client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } - if (user.length()>0 && passwd.length()>0 ) { + if (!buffer.startsWith(F("250"))) { return false; } + if ((user.length() > 0) && (passwd.length() > 0)) { buffer = F("AUTH LOGIN"); client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("334"))) - { - goto exit; - } + if (!buffer.startsWith(F("334"))) { return false; } + base64 b; buffer = b.encode(user); - client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("334"))) { - goto exit; - } + if (!buffer.startsWith(F("334"))) { return false; } + buffer = b.encode(passwd); client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("235"))) { - goto exit; - } + if (!buffer.startsWith(F("235"))) { return false; } } // smtp send mail @@ -323,69 +184,65 @@ String buffer; buffer += from; client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } + if (!buffer.startsWith(F("250"))) { return false; } + buffer = F("RCPT TO:"); buffer += to; client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("250"))) { - goto exit; - } + if (!buffer.startsWith(F("250"))) { return false; } buffer = F("DATA"); client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = readClient(); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailReadAddLogBuffer(&buffer); #endif - if (!buffer.startsWith(F("354"))) { - goto exit; - } + if (!buffer.startsWith(F("354"))) { return false; } buffer = F("From: "); buffer += from; client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = F("To: "); buffer += to; client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = F("Subject: "); buffer += subject; client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif +/*-------------------------------------------------------------------------------------------*/ + #ifdef USE_SCRIPT - if (*msg=='*' && *(msg+1)==0) { + if (*msg == '*' && *(msg +1) == 0) { buffer = F("MIME-Version: 1.0\r\n"); client->print(buffer); buffer = F("Content-Type: Multipart/mixed; boundary=frontier\r\n\r\n"); client->print(buffer); - - g_client=client; + g_client = client; script_send_email_body(xsend_message_txt); } else { #endif // USE_SCRIPT @@ -394,29 +251,121 @@ String buffer; client->println(msg); #ifdef USE_SCRIPT } -#endif +#endif // USE_SCRIPT + +/*-------------------------------------------------------------------------------------------*/ + client->println('.'); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif buffer = F("QUIT"); client->println(buffer); #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),buffer.c_str()); + MailWriteAddLogBuffer(&buffer); #endif - status=true; -exit: + return true; +} +/*********************************************************************************************/ + +uint16_t SendMail(char *buffer) { + // return if not enough memory + uint16_t mem = ESP_getFreeHeap(); + if (mem < SEND_MAIL_MINRAM) { return 4; } + + while (*buffer == ' ') { buffer++; } + if (*buffer != '[') { return 1; } + + buffer++; + char *endcmd = strchr(buffer, ']'); + if (!endcmd) { return 1; } + + // copy params + uint16_t blen = (uint32_t)endcmd - (uint32_t)buffer; + char *oparams = (char*)calloc(blen +2, 1); + if (!oparams) { return 4; } + + uint16_t status = 1; + char *params = oparams; + strncpy(oparams, buffer, blen +2); + oparams[blen] = 0; + const char *cmd = endcmd +1; + +#ifdef DEBUG_EMAIL_PORT + AddLog(LOG_LEVEL_INFO, PSTR("MAI: Size: %d"), blen); +#endif + + const char *mserv = strtok(params, ":"); + if (mserv) { + // port + const char *pstr = strtok(NULL, ":"); + if (pstr) { + uint16_t port; +#ifdef EMAIL_PORT + port = (*pstr == '*') ? EMAIL_PORT : atoi(pstr); +#else + port = atoi(pstr); +#endif + const char *user = strtok(NULL, ":"); + if (user) { + const char *passwd = strtok(NULL, ":"); + if (passwd) { + const char *from = strtok(NULL, ":"); + if (from) { + const char *to = strtok(NULL,":"); + if (to) { + const char *subject = strtok(NULL, "]"); + if (subject) { +#ifdef EMAIL_USER + if (*user == '*') { user = xPSTR(EMAIL_USER); } +#endif +#ifdef EMAIL_PASSWORD + if (*passwd == '*') { passwd = xPSTR(EMAIL_PASSWORD); } +#endif +#ifdef EMAIL_SERVER + if (*mserv == '*') { mserv = xPSTR(EMAIL_SERVER); } +#endif + +#ifdef DEBUG_EMAIL_PORT + AddLog(LOG_LEVEL_INFO, PSTR("MAI: %s, %d, %s, %s"), mserv, port, user, passwd); +#endif + +#ifdef EMAIL_FROM + if (*from == '*') { from = xPSTR(EMAIL_FROM); } +#endif + +#ifdef DEBUG_EMAIL_PORT + AddLog(LOG_LEVEL_INFO, PSTR("MAI: %s, %s, %s, %s"), from, to, subject, cmd); +#endif + + char auth = 0; + SendEmail *mail = new SendEmail(mserv, port, user, passwd, MAIL_TIMEOUT, auth); + if (mail) { + bool result = mail->send(from, to, subject, cmd); + delete mail; + if (result == true) { status = 0; } + } + } + } + } + } + } + } + } + if (oparams) { free(oparams); } return status; } +/*********************************************************************************************/ + #ifdef USE_SCRIPT void xsend_message_txt(char *msg) { #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s"),msg); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: '%s'"), msg); #endif #ifdef USE_UFILESYS @@ -456,7 +405,7 @@ void attach_Array(char *aname) { g_client->print(F("Content-Type: text/plain\r\n")); if (array && alen) { #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("array found %d"),alen); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: Array found %d"), alen); #endif char buff[64]; sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); @@ -485,6 +434,7 @@ void attach_Array(char *aname) { } #endif // USE_SCRIPT +/*********************************************************************************************/ #ifdef USE_UFILESYS @@ -519,6 +469,8 @@ void attach_File(char *path) { #endif // USE_UFILESYS +/*********************************************************************************************/ + #endif // USE_SENDMAIL #endif // USE_WEBSERVER #endif // ESP8266 \ No newline at end of file diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino index 8dbee213c..9522c4e78 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino @@ -27,7 +27,7 @@ #include -//#define DEBUG_EMAIL_PORT // Enable serial debugging +// #define DEBUG_EMAIL_PORT // Enable debugging #ifndef SEND_MAIL32_MINRAM #undef SEND_MAIL32_MINRAM @@ -35,224 +35,177 @@ #endif #define xPSTR(a) a + #define MAX_ATTCHMENTS 8 + +void script_send_email_body(void(*func)(char *)); + +/*********************************************************************************************/ + char *attachments[MAX_ATTCHMENTS]; uint8_t num_attachments; -void script_send_email_body(void(*func)(char *)); String html_content = ""; SMTP_Message *email_mptr; SMTPSession *smtp; + //SMTPSession smtp; void smtpCallback(SMTP_Status status); uint16_t SendMail(char *buffer) { - char *params,*oparams = 0; - const char *mserv; - uint16_t port; - const char *user; - const char *pstr; - const char *passwd; - const char *from; - const char *to; - const char *subject; - const char *cmd; - uint16_t status = 1; - uint16_t blen; - char *endcmd; - ESP_Mail_Session session; - SMTP_Message message; - email_mptr = &message; - smtp = new SMTPSession(); - if (!smtp) { - return 4; - } // return if not enough memory - uint32_t mem = ESP.getFreeHeap(); - //AddLog(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); - if (mem < SEND_MAIL32_MINRAM) { - return 4; - } - #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("heap: %d"),mem); - #endif + uint32_t mem = ESP_getFreeHeap(); + if (mem < SEND_MAIL32_MINRAM) { return 4; } while (*buffer==' ') buffer++; - - if (*buffer != '[') { - goto exit; - } + if (*buffer != '[') { return 1; } buffer++; - - endcmd = strchr(buffer, ']'); - if (!endcmd) { - goto exit; - } + char *endcmd = strchr(buffer, ']'); + if (!endcmd) { return 1; } // copy params - blen = (uint32_t)endcmd - (uint32_t)buffer; - oparams = (char*)calloc(blen+2, 1); - if (!oparams) return 4; - params = oparams; + uint16_t blen = (uint32_t)endcmd - (uint32_t)buffer; + char *oparams = (char*)calloc(blen+2, 1); + if (!oparams) { return 4; } + + uint16_t status = 1; + char *params = oparams; strncpy(oparams, buffer, blen+2); oparams[blen] = 0; + const char *cmd = endcmd + 1; - cmd = endcmd + 1; - +#ifdef DEBUG_EMAIL_PORT + AddLog(LOG_LEVEL_INFO, PSTR("MAI: Size %d"), blen); +#endif for (uint32_t cnt = 0; cnt < MAX_ATTCHMENTS; cnt++) { attachments[cnt] = 0; } num_attachments = 0; -#ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("mailsize: %d"),blen); -#endif - - mserv = strtok(params, ":"); - if (!mserv) { - goto exit; - } - - // port - pstr = strtok(NULL, ":"); - if (!pstr) { - goto exit; - } - -#ifdef EMAIL_PORT - if (*pstr == '*') { - port = EMAIL_PORT; - } else { - port = atoi(pstr); - } -#else - port = atoi(pstr); -#endif - - user = strtok(NULL, ":"); - if (!user) { - goto exit; - } - - passwd = strtok(NULL, ":"); - if (!passwd) { - goto exit; - } - - from = strtok(NULL, ":"); - if (!from) { - goto exit; - } - - to = strtok(NULL, ":"); - if (!to) { - goto exit; - } - - subject = strtok(NULL, "]"); - if (!subject) { - goto exit; + smtp = new SMTPSession(); + if (!smtp) { + if (oparams) { free(oparams); } + return 4; } + const char *mserv = strtok(params, ":"); + if (mserv) { + // port + const char *pstr = strtok(NULL, ":"); + if (pstr) { + uint16_t port; + #ifdef EMAIL_PORT + port = (*pstr == '*') ? EMAIL_PORT : port = atoi(pstr); + #else + port = atoi(pstr); + #endif + const char *user = strtok(NULL, ":"); + if (user) { + const char *passwd = strtok(NULL, ":"); + if (passwd) { + const char *from = strtok(NULL, ":"); + if (from) { + const char *to = strtok(NULL, ":"); + if (to) { + const char *subject = strtok(NULL, "]"); + if (subject) { #ifdef EMAIL_USER - if (*user == '*') { - user = xPSTR(EMAIL_USER); - } + if (*user == '*') { user = xPSTR(EMAIL_USER); } #endif - #ifdef EMAIL_PASSWORD - if (*passwd == '*') { - passwd = xPSTR(EMAIL_PASSWORD); - } + if (*passwd == '*') { passwd = xPSTR(EMAIL_PASSWORD); } #endif - #ifdef EMAIL_SERVER - if (*mserv == '*') { - mserv = xPSTR(EMAIL_SERVER); - } + if (*mserv == '*') { mserv = xPSTR(EMAIL_SERVER); } #endif #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s - %d - %s - %s"), mserv, port, user, passwd); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: %s, %d, %s, %s"), mserv, port, user, passwd); #endif #ifdef EMAIL_FROM - if (*from == '*') { - from = xPSTR(EMAIL_FROM); - } + if (*from == '*') { from = xPSTR(EMAIL_FROM); } #endif #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("%s - %s - %s - %s"), from, to, subject, cmd); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: %s, %s, %s, %s"), from, to, subject, cmd); #endif - #ifdef DEBUG_EMAIL_PORT - smtp->debug(true); + smtp->debug(true); #else - smtp->debug(false); + smtp->debug(false); #endif -// smtp->callback(smtpCallback); + // smtp->callback(smtpCallback); - message.clearRecipients(); - message.clearCc(); - message.clearBcc(); + SMTP_Message message; + email_mptr = &message; + message.clearRecipients(); + message.clearCc(); + message.clearBcc(); - session.server.host_name = mserv; - session.server.port = port; - session.login.email = user; - session.login.password = passwd; - session.login.user_domain = "googlemail.com"; + ESP_Mail_Session session; + session.server.host_name = mserv; + session.server.port = port; + session.login.email = user; + session.login.password = passwd; + session.login.user_domain = "googlemail.com"; + message.enable.chunking = true; + char sname[32]; + strlcpy(sname, SettingsText(SET_FRIENDLYNAME1), sizeof(sname)); + message.sender.name = sname; + message.sender.email = from; + message.subject = subject; + message.addRecipient("user1", to); + message.html.charSet = "utf-8"; + message.text.charSet = "utf-8"; + message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; + message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; + //message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; + message.response.notify = esp_mail_smtp_notify_failure; + message.html.charSet = "us-ascii"; + message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; + message.addHeader("Message-ID: "); - message.enable.chunking = true; - char sname[32]; - strlcpy(sname, SettingsText(SET_FRIENDLYNAME1), sizeof(sname)); - message.sender.name = sname; - message.sender.email = from; - message.subject = subject; - message.addRecipient("user1", to); - message.html.charSet = "utf-8"; - message.text.charSet = "utf-8"; - message.text.transfer_encoding = Content_Transfer_Encoding::enc_base64; - message.priority = esp_mail_smtp_priority::esp_mail_smtp_priority_normal; - //message.response.notify = esp_mail_smtp_notify_success | esp_mail_smtp_notify_failure | esp_mail_smtp_notify_delay; - message.response.notify = esp_mail_smtp_notify_failure; - message.html.charSet = "us-ascii"; - message.html.transfer_encoding = Content_Transfer_Encoding::enc_7bit; - message.addHeader("Message-ID: "); +/*-------------------------------------------------------------------------------------------*/ - #ifdef USE_SCRIPT - if (*cmd == '*' && *(cmd + 1) == 0) { - script_send_email_body(send_message_txt); - } else { - html_content += cmd; - message.html.content = html_content.c_str(); +#ifdef USE_SCRIPT + if (*cmd == '*' && *(cmd + 1) == 0) { + script_send_email_body(send_message_txt); + } else { + html_content += cmd; + message.html.content = html_content.c_str(); + } +#else + html_content += cmd; + message.html.content = html_content.c_str(); +#endif // USE_SCRIPT + +/*-------------------------------------------------------------------------------------------*/ + + /* Connect to server with the session config */ + delay(0); + if (smtp->connect(&session)) { + /* Start sending the Email and close the session */ + delay(0); + if (!MailClient.sendMail(smtp, &message, true)) { + Serial.println("Error sending Email, " + smtp->errorReason()); + } else { + status = 0; + } + } + } + } + } + } + } } - #else - html_content += cmd; - message.html.content = html_content.c_str(); - #endif - - /* Connect to server with the session config */ - delay(0); - if (!smtp->connect(&session)) { - goto exit; } - /* Start sending the Email and close the session */ - delay(0); - - if (!MailClient.sendMail(smtp, &message, true)) { - Serial.println("Error sending Email, " + smtp->errorReason()); - } else { - status = 0; - } - -exit: - if (smtp) delete smtp; + if (smtp) { delete smtp; } for (uint32_t cnt = 0; cnt < MAX_ATTCHMENTS; cnt++) { if (attachments[cnt]) { free(attachments[cnt]); @@ -260,10 +213,13 @@ exit: } } html_content = ""; - if (oparams) free(oparams); + if (oparams) { free(oparams); } return status; } +/*********************************************************************************************/ + +#ifdef USE_SCRIPT void send_message_txt(char *txt) { if (*txt == '@') { @@ -363,42 +319,41 @@ void attach_Data(char *name, uint8_t *buff, uint32_t len) { email_mptr->resetAttachItem(att); } +#endif // USE_SCRIPT + +/*********************************************************************************************/ + /* Callback function to get the Email sending status */ -void smtpCallback(SMTP_Status status) -{ -/* Print the current status */ -Serial.println(status.info()); +void smtpCallback(SMTP_Status status) { + /* Print the current status */ + Serial.println(status.info()); -/* Print the sending result */ -if (status.success()) -{ - Serial.println("----------------"); - Serial.printf("Message sent success: %d\n", status.completedCount()); - Serial.printf("Message sent failled: %d\n", status.failedCount()); - Serial.println("----------------\n"); - struct tm dt; + /* Print the sending result */ + if (status.success()) { + Serial.println("----------------"); + Serial.printf("Message sent success: %d\n", status.completedCount()); + Serial.printf("Message sent failled: %d\n", status.failedCount()); + Serial.println("----------------\n"); + struct tm dt; - for (size_t i = 0; i < smtp->sendingResult.size(); i++) - { - /* Get the result item */ - SMTP_Result result = smtp->sendingResult.getItem(i); - localtime_r(&result.timesstamp, &dt); + for (size_t i = 0; i < smtp->sendingResult.size(); i++) { + /* Get the result item */ + SMTP_Result result = smtp->sendingResult.getItem(i); + localtime_r(&result.timesstamp, &dt); - Serial.printf("Message No: %d\n", i + 1); - Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); - Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); - Serial.printf("Recipient: %s\n", result.recipients); - Serial.printf("Subject: %s\n", result.subject); + Serial.printf("Message No: %d\n", i + 1); + Serial.printf("Status: %s\n", result.completed ? "success" : "failed"); + Serial.printf("Date/Time: %d/%d/%d %d:%d:%d\n", dt.tm_year + 1900, dt.tm_mon + 1, dt.tm_mday, dt.tm_hour, dt.tm_min, dt.tm_sec); + Serial.printf("Recipient: %s\n", result.recipients); + Serial.printf("Subject: %s\n", result.subject); + } + Serial.println("----------------\n"); } - Serial.println("----------------\n"); } -} - - void Tasmota_print(const char *txt) { #ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("ESP32mail: %s"),txt); + AddLog(LOG_LEVEL_INFO, PSTR("MAI: %s"),txt); #endif } From 5ec8aa542079be89b47f9b2bf6e271a0422106bd Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 19:01:16 -0500 Subject: [PATCH 008/219] Remove unknown comment of code size added. --- tasmota/my_user_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 397da4737..ea88e9bef 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -556,7 +556,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb (+0k# code) +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) From 9774cc84782c07ee471bf8888ea0af0f62c25814 Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 19:01:32 -0500 Subject: [PATCH 009/219] Add release note for feature added. --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 1a0505579..92888e5b0 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -109,6 +109,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo ## Changelog v12.0.2.1 ### Added +- Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs ### Breaking Changed From d763bf24ebfa313c359c29253df9d69dee0e4944 Mon Sep 17 00:00:00 2001 From: Cossid Date: Tue, 21 Jun 2022 22:44:50 -0500 Subject: [PATCH 010/219] Remove unknown code size comments. --- tasmota/include/tasmota_configurations.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index eb8af48f5..1eb21552f 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -69,7 +69,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -404,7 +404,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -554,7 +554,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -700,7 +700,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb (+0k# code) +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller From 1201d56473f3538ed7aff7d3454eee054caed9f9 Mon Sep 17 00:00:00 2001 From: jenniferdewan <45907677+jenniferdewan@users.noreply.github.com> Date: Tue, 21 Jun 2022 22:09:17 -0700 Subject: [PATCH 011/219] Update to ReadMe Redundant to mention Tasmota version because the whole sentence is talking about one situation and one device. This change made the sentence into a more reasonable length and made it easier to read --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a5ad50aa..6c8df87ca 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Firmware binaries can be downloaded from http://ota.tasmota.com/tasmota/release/ See [CHANGELOG.md](CHANGELOG.md) for detailed change information. -Unless your Tasmota powered device exhibits a problem or you need to make use of a feature that is not available in the Tasmota version currently installed on your device, leave your device alone - it works so don't make unnecessary changes! If the release version (i.e., the master branch) exhibits unexpected behaviour for your device and configuration, you should upgrade to the latest development version instead to see if your problem is resolved as some bugs in previous releases or development builds may already have been resolved. +Unless your Tasmota powered device exhibits a problem or lacks a feature that you need, leave your device alone - it works so don’t make unnecessary changes!If the release version (i.e., the master branch) exhibits unexpected behaviour for your device and configuration, you should upgrade to the latest development version instead to see if your problem is resolved as some bugs in previous releases or development builds may already have been resolved. Every commit made to the development branch, which is compiling successfuly, will post new binary files at http://ota.tasmota.com/tasmota/ (this web address can be used for OTA updates too). It is important to note that these binaries are based on the current development codebase. These commits are tested as much as is possible and are typically quite stable. However, it is infeasible to test on the hundreds of different types of devices with all the available configuration options permitted. From d4ab49d3dc63f6106646ac51b9acbbe1808203e9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 22 Jun 2022 12:01:38 +0200 Subject: [PATCH 012/219] Refactor sendmail and solve buffer overflows --- .../xdrv_01_1_webserver_mail.ino | 144 ++++++++---------- .../xdrv_01_2_webserver_esp32_mail.ino | 82 +++++----- 2 files changed, 102 insertions(+), 124 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino index b7c77924d..13d0a99de 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino @@ -243,7 +243,7 @@ bool SendEmail::send(const String& from, const String& to, const String& subject buffer = F("Content-Type: Multipart/mixed; boundary=frontier\r\n\r\n"); client->print(buffer); g_client = client; - script_send_email_body(xsend_message_txt); + script_send_email_body(send_message_txt); } else { #endif // USE_SCRIPT buffer = F("\r\n"); @@ -362,113 +362,91 @@ uint16_t SendMail(char *buffer) { /*********************************************************************************************/ #ifdef USE_SCRIPT -void xsend_message_txt(char *msg) { - -#ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("MAI: '%s'"), msg); -#endif - -#ifdef USE_UFILESYS - if (*msg=='@') { - msg++; - attach_File(msg); - } else if (*msg=='&') { - msg++; - attach_Array(msg); - } else { - g_client->print(F("--frontier\r\n")); - g_client->print(F("Content-Type: text/plain\r\n\r\n")); - g_client->println(msg); - g_client->print(F("\r\n--frontier\r\n")); - } -#else // No USE_UFILESYS - if (*msg=='&') { - msg++; - attach_Array(msg); - } else { - g_client->print(F("--frontier\r\n")); - g_client->print(F("Content-Type: text/plain\r\n\r\n")); - g_client->println(msg); - g_client->print(F("\r\n--frontier\r\n")); - } -#endif // USE_UFILESYS -} - -float *get_array_by_name(char *name, uint16_t *alen); -void flt2char(float num, char *nbuff); - -void attach_Array(char *aname) { - float *array = 0; - uint16_t alen; - array = get_array_by_name(aname, &alen); - g_client->print(F("--frontier\r\n")); - g_client->print(F("Content-Type: text/plain\r\n")); - if (array && alen) { -#ifdef DEBUG_EMAIL_PORT - AddLog(LOG_LEVEL_INFO, PSTR("MAI: Array found %d"), alen); -#endif - char buff[64]; - sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); - g_client->write(buff); - // send timestamp - strcpy(buff, GetDateAndTime(DT_LOCAL).c_str()); - strcat(buff,"\t"); - g_client->write(buff); - - float *fp=array; - for (uint32_t cnt = 0; cntwrite(nbuff, strlen(nbuff)); - } - } else { - g_client->print(F("\r\n\r\narray not found!\r\n")); - } - g_client->print(F("\r\n--frontier\r\n")); -} -#endif // USE_SCRIPT - -/*********************************************************************************************/ - #ifdef USE_UFILESYS #include extern FS *ufsp; void attach_File(char *path) { - g_client->print(F("--frontier\r\n")); - g_client->print(F("Content-Type: text/plain\r\n")); - char buff[64]; char *cp = path; - while (*cp=='/') cp++; + while (*cp == '/') { cp++; } File file = ufsp->open(path, "r"); if (file) { - sprintf_P(buff,PSTR("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n"), cp); + char buff[64]; + snprintf_P(buff, sizeof(buff), PSTR("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n"), cp); g_client->write(buff); uint16_t flen = file.size(); uint8_t fbuff[64]; uint16_t blen = sizeof(fbuff); - while (flen>0) { + while (flen > 0) { file.read(fbuff, blen); flen -= blen; g_client->write(fbuff, blen); - if (flenprint(F("\r\n\r\nfile not found!\r\n")); } - g_client->print(F("\r\n--frontier\r\n")); } #endif // USE_UFILESYS +float *get_array_by_name(char *name, uint16_t *alen); +void flt2char(float num, char *nbuff); + +void attach_Array(char *aname) { + uint16_t alen; + float *array = get_array_by_name(aname, &alen); + if (array && alen) { +#ifdef DEBUG_EMAIL_PORT + AddLog(LOG_LEVEL_INFO, PSTR("MAI: Array found %d"), alen); +#endif + char buff[64]; + snprintf_P(buff, sizeof(buff), PSTR("Content-Disposition: attachment; filename=\"%s.txt\"\r\n\r\n"), aname); + g_client->write(buff); + // send timestamp + strcpy(buff, GetDateAndTime(DT_LOCAL).c_str()); + strcat(buff, "\t"); + g_client->write(buff); + + float *fp=array; + for (uint32_t cnt = 0; cnt < alen; cnt++) { + // export array as tab gelimited text + char nbuff[16]; + flt2char(*fp++, nbuff); + if (cnt < (alen - 1)) { + strcat(nbuff, "\t"); + } else { + strcat(nbuff, "\n"); + } + g_client->write(nbuff, strlen(nbuff)); + } + } else { + g_client->print(F("\r\n\r\narray not found!\r\n")); + } +} + +void send_message_txt(char *txt) { + g_client->print(F("--frontier\r\n")); + g_client->print(F("Content-Type: text/plain\r\n")); + if (*txt == '&') { + txt++; + attach_Array(txt); +#ifdef USE_UFILESYS + } else if (*txt == '@') { + txt++; + attach_File(txt); +#endif // USE_UFILESYS + } else { + g_client->print(F("\r\n")); + g_client->println(txt); + } + g_client->print(F("\r\n--frontier\r\n")); +} + +#endif // USE_SCRIPT + /*********************************************************************************************/ #endif // USE_SENDMAIL diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino index 9522c4e78..8a99bc5f0 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_2_webserver_esp32_mail.ino @@ -221,38 +221,6 @@ uint16_t SendMail(char *buffer) { #ifdef USE_SCRIPT -void send_message_txt(char *txt) { - if (*txt == '@') { - txt++; - attach_File(txt); - } else if (*txt == '&') { - txt++; - attach_Array(txt); - } else if (*txt == '$') { - txt++; -#if defined(ESP32) && defined(USE_WEBCAM) - if (num_attachments < MAX_ATTCHMENTS) { - attachments[num_attachments] = (char*)malloc(32); - uint32_t cnt; - uint8_t *buff; - uint32_t len, picmax; - picmax = WcGetPicstore(-1, 0); - cnt = *txt&7; - if (cnt < 1 || cnt > picmax) cnt = 1; - len = WcGetPicstore(cnt - 1, &buff); - if (len) { - sprintf(attachments[num_attachments], "img_%1d.jpg", cnt); - attach_Data(attachments[num_attachments], buff, len); - } - num_attachments++; - } -#endif - } else { - html_content += txt; - email_mptr->html.content = html_content.c_str(); - } -} - void attach_File(char *path) { SMTP_Attachment att; if (num_attachments < MAX_ATTCHMENTS) { @@ -274,6 +242,17 @@ void attach_File(char *path) { } } +void attach_Data(char *name, uint8_t *buff, uint32_t len) { + SMTP_Attachment att; + att.descr.filename = name; + att.descr.mime = "application/octet-stream"; + att.blob.data = buff; + att.blob.size = len; + att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; + email_mptr->addAttachment(att); + email_mptr->resetAttachItem(att); +} + float *get_array_by_name(char *name, uint16_t *alen); void flt2char(float num, char *nbuff); @@ -308,15 +287,36 @@ void attach_Array(char *aname) { } } -void attach_Data(char *name, uint8_t *buff, uint32_t len) { - SMTP_Attachment att; - att.descr.filename = name; - att.descr.mime = "application/octet-stream"; - att.blob.data = buff; - att.blob.size = len; - att.descr.transfer_encoding = Content_Transfer_Encoding::enc_base64; - email_mptr->addAttachment(att); - email_mptr->resetAttachItem(att); +void send_message_txt(char *txt) { + if (*txt == '&') { + txt++; + attach_Array(txt); + } else if (*txt == '@') { + txt++; + attach_File(txt); + } else if (*txt == '$') { + txt++; +#ifdef USE_WEBCAM + if (num_attachments < MAX_ATTCHMENTS) { + attachments[num_attachments] = (char*)malloc(32); + uint32_t cnt; + uint8_t *buff; + uint32_t len, picmax; + picmax = WcGetPicstore(-1, 0); + cnt = *txt &7; + if (cnt < 1 || cnt > picmax) cnt = 1; + len = WcGetPicstore(cnt - 1, &buff); + if (len) { + sprintf(attachments[num_attachments], "img_%1d.jpg", cnt); + attach_Data(attachments[num_attachments], buff, len); + } + num_attachments++; + } +#endif // USE_WEBCAM + } else { + html_content += txt; + email_mptr->html.content = html_content.c_str(); + } } #endif // USE_SCRIPT From 958c337f8bb0c7010cde64df642b5fda951d2afc Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 22 Jun 2022 14:04:35 +0200 Subject: [PATCH 013/219] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c8df87ca..cb330d44b 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Firmware binaries can be downloaded from http://ota.tasmota.com/tasmota/release/ See [CHANGELOG.md](CHANGELOG.md) for detailed change information. -Unless your Tasmota powered device exhibits a problem or lacks a feature that you need, leave your device alone - it works so don’t make unnecessary changes!If the release version (i.e., the master branch) exhibits unexpected behaviour for your device and configuration, you should upgrade to the latest development version instead to see if your problem is resolved as some bugs in previous releases or development builds may already have been resolved. +Unless your Tasmota powered device exhibits a problem or lacks a feature that you need, leave your device alone - it works so don’t make unnecessary changes! If the release version (i.e., the master branch) exhibits unexpected behaviour for your device and configuration, you should upgrade to the latest development version instead to see if your problem is resolved as some bugs in previous releases or development builds may already have been resolved. Every commit made to the development branch, which is compiling successfuly, will post new binary files at http://ota.tasmota.com/tasmota/ (this web address can be used for OTA updates too). It is important to note that these binaries are based on the current development codebase. These commits are tested as much as is possible and are typically quite stable. However, it is infeasible to test on the hundreds of different types of devices with all the available configuration options permitted. From 8ffc7b9f0260c5d86af663db36116791a172eee6 Mon Sep 17 00:00:00 2001 From: Cossid Date: Wed, 22 Jun 2022 10:00:34 -0500 Subject: [PATCH 014/219] SM2335 - Reduce code size by replacing redundant calls with for loops. Remove defines that are unused or can be replaced with array locations. --- tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino | 133 +++++------------- 1 file changed, 33 insertions(+), 100 deletions(-) diff --git a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino index f8ecd89a0..e020b0a5e 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino @@ -31,39 +31,34 @@ // 11 = identification | 0 = reserved | 00 = Standby | 000 = start at OUT1/5 #define SM2335_ADDR_STANDBY 0xC0 // 11000000 0xC0 // 11 = identification | 0 = reserved | 01 = 3 channels (RGB) | 000 = start at OUT1/5 -#define SM2335_ADDR_START_3CH 0xC8 // 11001000 0xC8 +//#define SM2335_ADDR_START_3CH 0xC8 // 11001000 0xC8 // 11 = identification | 0 = reserved | 10 = 2 channels (CW) | 000 = start at OUT1/5 -#define SM2335_ADDR_START_2CH 0xD0 // 11010000 0xD0 +//#define SM2335_ADDR_START_2CH 0xD0 // 11010000 0xD0 // 11 = identification | 0 = reserved | 11 = 5 channels (RGB+CW) | 000 = start at OUT1/5 #define SM2335_ADDR_START_5CH 0xD8 // 11011000 0xD8 // Current values -#define SM2335_RGB_10MA_CW_5MA 0x0 // 0000 RGB 10mA | CW 5mA -#define SM2335_RGB_20MA_CW_10MA 0x1 // 0001 RGB 20mA | CW 10mA -#define SM2335_RGB_30MA_CW_15MA 0x2 // 0010 RGB 30mA | CW 15mA -#define SM2335_RGB_40MA_CW_20MA 0x3 // 0011 RGB 40mA | CW 20mA -#define SM2335_RGB_50MA_CW_25MA 0x4 // 0100 RGB 50mA | CW 25mA -#define SM2335_RGB_60MA_CW_30MA 0x5 // 0101 RGB 60mA | CW 30mA -#define SM2335_RGB_70MA_CW_35MA 0x6 // 0110 RGB 70mA | CW 35mA -#define SM2335_RGB_80MA_CW_40MA 0x7 // 0111 RGB 80mA | CW 40mA -#define SM2335_RGB_90MA_CW_45MA 0x8 // 1000 RGB 90mA | CW 45mA -#define SM2335_RGB_100MA_CW_50MA 0x9 // 1001 RGB 100mA | CW 50mA -#define SM2335_RGB_110MA_CW_55MA 0xA // 1010 RGB 110mA | CW 55mA -#define SM2335_RGB_120MA_CW_60MA 0xB // 1011 RGB 120mA | CW 60mA -#define SM2335_RGB_130MA_CW_65MA 0xC // 1100 RGB 130mA | CW 65mA -#define SM2335_RGB_140MA_CW_70MA 0xD // 1101 RGB 140mA | CW 70mA -#define SM2335_RGB_150MA_CW_75MA 0xE // 1110 RGB 150mA | CW 75mA -#define SM2335_RGB_160MA_CW_80MA 0xF // 1111 RGB 160mA | CW 80mA - -enum Sm2335Current { SM2335_10_5, SM2335_20_10, SM2335_30_15, SM2335_40_20, SM2335_50_25, - SM2335_60_30, SM2335_70_35, SM2335_80_40, SM2335_90_45, SM2335_100_50, SM2335_110_55, - SM2335_120_60, SM2335_130_65, SM2335_140_70, SM2335_150_75, SM2335_160_80 }; +// 0x0 // 0000 RGB 10mA | CW 5mA +// 0x1 // 0001 RGB 20mA | CW 10mA +// 0x2 // 0010 RGB 30mA | CW 15mA +// 0x3 // 0011 RGB 40mA | CW 20mA +// 0x4 // 0100 RGB 50mA | CW 25mA +// 0x5 // 0101 RGB 60mA | CW 30mA +// 0x6 // 0110 RGB 70mA | CW 35mA +// 0x7 // 0111 RGB 80mA | CW 40mA +// 0x8 // 1000 RGB 90mA | CW 45mA +// 0x9 // 1001 RGB 100mA | CW 50mA +// 0xA // 1010 RGB 110mA | CW 55mA +// 0xB // 1011 RGB 120mA | CW 60mA +// 0xC // 1100 RGB 130mA | CW 65mA +// 0xD // 1101 RGB 140mA | CW 70mA +// 0xE // 1110 RGB 150mA | CW 75mA +// 0xF // 1111 RGB 160mA | CW 80mA struct SM2335 { uint8_t clk = 0; uint8_t data = 0; - uint8_t currentValue; - uint8_t currentEnum = SM2335_50_25; + uint8_t current; } Sm2335; /*********************************************************************************************\ @@ -119,17 +114,11 @@ bool SM2335SetChannels(void) { // If we receive 0 for all channels, we'll assume that the lightbulb is off, and activate SM2335's standby mode. if (cur_col_10[0] == 0 && cur_col_10[1] == 0 && cur_col_10[2] == 0 && cur_col_10[3] == 0 && cur_col_10[4] == 0) { SM2335Start(SM2335_ADDR_STANDBY); - SM2335Write(0); // Current - SM2335Write(0); // Red 1/2 - SM2335Write(0); // Red 2/2 - SM2335Write(0); // Green 1/2 - SM2335Write(0); // Green 2/2 - SM2335Write(0); // Blue 1/2 - SM2335Write(0); // Blue 2/2 - SM2335Write(0); // Cold White 1/2 - SM2335Write(0); // Cold White 2/2 - SM2335Write(0); // Warm White 1/2 - SM2335Write(0); // Warm White 2/2 + // Clear all remaining data. This clears out Current, Red, Green, Blue, Cold White, Warm White. + for (int i = 0; i < 11; i++) + { + SM2335Write(0); + } SM2335Stop(); return true; } @@ -137,19 +126,13 @@ bool SM2335SetChannels(void) { // Write the header activating all 5 channels SM2335Start(SM2335_ADDR_START_5CH); // Set the current defined in ModuleSelected. - SM2335Write(Sm2335.currentValue); + SM2335Write(Sm2335.current); // Set RGB and CW grayscale. - SM2335Write((uint8_t)(cur_col_10[0] >> 8)); // Red 1/2 - SM2335Write((uint8_t)(cur_col_10[0] & 0xFF)); // Red 2/2 - SM2335Write((uint8_t)(cur_col_10[1] >> 8)); // Green 1/2 - SM2335Write((uint8_t)(cur_col_10[1] & 0xFF)); // Green 2/2 - SM2335Write((uint8_t)(cur_col_10[2] >> 8)); // Blue 1/2 - SM2335Write((uint8_t)(cur_col_10[2] & 0xFF)); // Blue 2/2 - SM2335Write((uint8_t)(cur_col_10[3] >> 8)); // Cold White 1/2 - SM2335Write((uint8_t)(cur_col_10[3] & 0xFF)); // Cold White 2/2 - SM2335Write((uint8_t)(cur_col_10[4] >> 8)); // Warm White 1/2 - SM2335Write((uint8_t)(cur_col_10[4] & 0xFF)); // Warm White 2/2 - + for (int i = 0; i < 5; i++) + { + SM2335Write((uint8_t)(cur_col_10[i] >> 8)); + SM2335Write((uint8_t)(cur_col_10[i] & 0xFF)); + } SM2335Stop(); return true; } @@ -159,60 +142,10 @@ void SM2335ModuleSelected(void) if (PinUsed(GPIO_SM2335_CLK) && PinUsed(GPIO_SM2335_DAT, GPIO_ANY)) { Sm2335.clk = Pin(GPIO_SM2335_CLK); Sm2335.data = Pin(GPIO_SM2335_DAT, GPIO_ANY); - // See #define MAX_SM2335_DAT 16 in tasmota_template.h - Sm2335.currentEnum = GetPin(Sm2335.data) - AGPIO(GPIO_SM2335_DAT); // 0 .. 15 - - switch (Sm2335.currentEnum) { - case SM2335_10_5: - Sm2335.currentValue = (SM2335_RGB_10MA_CW_5MA << 4) | SM2335_RGB_10MA_CW_5MA; - break; - case SM2335_20_10: - Sm2335.currentValue = (SM2335_RGB_20MA_CW_10MA << 4) | SM2335_RGB_20MA_CW_10MA; - break; - case SM2335_30_15: - Sm2335.currentValue = (SM2335_RGB_30MA_CW_15MA << 4) | SM2335_RGB_30MA_CW_15MA; - break; - case SM2335_40_20: - Sm2335.currentValue = (SM2335_RGB_30MA_CW_15MA << 4) | SM2335_RGB_30MA_CW_15MA; - break; - case SM2335_50_25: - Sm2335.currentValue = (SM2335_RGB_50MA_CW_25MA << 4) | SM2335_RGB_50MA_CW_25MA; - break; - case SM2335_60_30: - Sm2335.currentValue = (SM2335_RGB_60MA_CW_30MA << 4) | SM2335_RGB_60MA_CW_30MA; - break; - case SM2335_70_35: - Sm2335.currentValue = (SM2335_RGB_70MA_CW_35MA << 4) | SM2335_RGB_70MA_CW_35MA; - break; - case SM2335_80_40: - Sm2335.currentValue = (SM2335_RGB_80MA_CW_40MA << 4) | SM2335_RGB_80MA_CW_40MA; - break; - case SM2335_90_45: - Sm2335.currentValue = (SM2335_RGB_90MA_CW_45MA << 4) | SM2335_RGB_90MA_CW_45MA; - break; - case SM2335_100_50: - Sm2335.currentValue = (SM2335_RGB_100MA_CW_50MA << 4) | SM2335_RGB_100MA_CW_50MA; - break; - case SM2335_110_55: - Sm2335.currentValue = (SM2335_RGB_110MA_CW_55MA << 4) | SM2335_RGB_110MA_CW_55MA; - break; - case SM2335_120_60: - Sm2335.currentValue = (SM2335_RGB_120MA_CW_60MA << 4) | SM2335_RGB_120MA_CW_60MA; - break; - case SM2335_130_65: - Sm2335.currentValue = (SM2335_RGB_130MA_CW_65MA << 4) | SM2335_RGB_130MA_CW_65MA; - break; - case SM2335_140_70: - Sm2335.currentValue = (SM2335_RGB_140MA_CW_70MA << 4) | SM2335_RGB_140MA_CW_70MA; - break; - case SM2335_150_75: - Sm2335.currentValue = (SM2335_RGB_150MA_CW_75MA << 4) | SM2335_RGB_150MA_CW_75MA; - break; - case SM2335_160_80: - Sm2335.currentValue = (SM2335_RGB_160MA_CW_80MA << 4) | SM2335_RGB_160MA_CW_80MA; - break; - } + int currentDat = GetPin(Sm2335.data) - AGPIO(GPIO_SM2335_DAT); // 0 .. 15 + // Split RGB and CW current. + Sm2335.current = (currentDat << 4) | currentDat; SM2335Init(); From df9c3a733e3e3b7e5520dcdc158671807c5889aa Mon Sep 17 00:00:00 2001 From: Cossid Date: Wed, 22 Jun 2022 10:02:20 -0500 Subject: [PATCH 015/219] SM2335 - Disable by default on ESP8266, enable by default on ESP32. --- BUILDS.md | 2 +- tasmota/include/tasmota_configurations.h | 8 ++++---- tasmota/include/tasmota_configurations_ESP32.h | 2 +- tasmota/my_user_config.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BUILDS.md b/BUILDS.md index 576c8be6f..4d7611a29 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -65,7 +65,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_MY92X1 | - | x / - | x | x | - | x | | USE_SM16716 | - | x / - | x | x | - | x | | USE_SM2135 | - | x / - | x | x | - | x | -| USE_SM2335 | - | x / - | x | x | - | x | +| USE_SM2335 | - | - / x | - | - | - | - | | USE_BP5758D | - | x / - | x | x | - | x | | USE_SONOFF_L1 | - | x / - | x | x | - | x | | USE_ELECTRIQ_MOODL | - | x / - | x | x | - | x | diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index 1eb21552f..5fe9514ea 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -69,7 +69,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb +//#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -404,7 +404,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -554,7 +554,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -700,7 +700,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h index cf2e21cc3..6b0b8c3b0 100644 --- a/tasmota/include/tasmota_configurations_ESP32.h +++ b/tasmota/include/tasmota_configurations_ESP32.h @@ -696,7 +696,7 @@ #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_THERMOSTAT // Add support for Thermostat - +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb #define USE_ETHERNET // Add support for ethernet (+20k code) #ifndef USE_KNX diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index ea88e9bef..868a16bd0 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -556,7 +556,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb +//#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) From 5741c06acdd1a99890fdb6662848e9317f9f0d10 Mon Sep 17 00:00:00 2001 From: Cossid Date: Wed, 22 Jun 2022 10:10:58 -0500 Subject: [PATCH 016/219] SM2335 - Be more consistent with code formatting. --- tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino index e020b0a5e..eb4c8f035 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_09_sm2335.ino @@ -115,8 +115,7 @@ bool SM2335SetChannels(void) { if (cur_col_10[0] == 0 && cur_col_10[1] == 0 && cur_col_10[2] == 0 && cur_col_10[3] == 0 && cur_col_10[4] == 0) { SM2335Start(SM2335_ADDR_STANDBY); // Clear all remaining data. This clears out Current, Red, Green, Blue, Cold White, Warm White. - for (int i = 0; i < 11; i++) - { + for (int i = 0; i < 11; i++) { SM2335Write(0); } SM2335Stop(); @@ -128,8 +127,7 @@ bool SM2335SetChannels(void) { // Set the current defined in ModuleSelected. SM2335Write(Sm2335.current); // Set RGB and CW grayscale. - for (int i = 0; i < 5; i++) - { + for (int i = 0; i < 5; i++) { SM2335Write((uint8_t)(cur_col_10[i] >> 8)); SM2335Write((uint8_t)(cur_col_10[i] & 0xFF)); } From 7b8a8a39e6ec34ad5a95442543b0ec56fc40660a Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 22 Jun 2022 17:28:13 +0200 Subject: [PATCH 017/219] Update Italian language --- tasmota/language/it_IT.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index e98b1a790..f0b987fc1 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -771,8 +771,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 - RX" #define D_SENSOR_SM2135_CLK "SM2135 - CLK" #define D_SENSOR_SM2135_DAT "SM2135 - DATI" -#define D_SENSOR_SM2335_CLK "SM2335 CLK" -#define D_SENSOR_SM2335_DAT "SM2335 DATI" +#define D_SENSOR_SM2335_CLK "SM2335 - CLK" +#define D_SENSOR_SM2335_DAT "SM2335 - DATI" #define D_SENSOR_BP5758D_CLK "BP5758D - CLK" #define D_SENSOR_BP5758D_DAT "BP5758D - DATI" #define D_SENSOR_DEEPSLEEP "Sleep profondo" From 07d2862c7ff8bd539431baf604190aff75200711 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 22 Jun 2022 17:28:54 +0200 Subject: [PATCH 018/219] Add support for 5-channel light dimmer driver SM2335 Add support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs (#15839) --- BUILDS.md | 2 +- CHANGELOG.md | 1 + RELEASENOTES.md | 2 +- tasmota/include/tasmota_configurations.h | 8 ++++---- tasmota/include/tasmota_configurations_ESP32.h | 1 - tasmota/my_user_config.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BUILDS.md b/BUILDS.md index 4d7611a29..576c8be6f 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -65,7 +65,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_MY92X1 | - | x / - | x | x | - | x | | USE_SM16716 | - | x / - | x | x | - | x | | USE_SM2135 | - | x / - | x | x | - | x | -| USE_SM2335 | - | - / x | - | - | - | - | +| USE_SM2335 | - | x / - | x | x | - | x | | USE_BP5758D | - | x / - | x | x | - | x | | USE_SONOFF_L1 | - | x / - | x | x | - | x | | USE_ELECTRIQ_MOODL | - | x / - | x | x | - | x | diff --git a/CHANGELOG.md b/CHANGELOG.md index 06cb69b50..4dfab2aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [12.0.2.1] ### Added +- Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs (#15839) ### Changed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 92888e5b0..a1e32ea8b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -109,7 +109,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo ## Changelog v12.0.2.1 ### Added -- Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs +- Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) ### Breaking Changed diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index 5fe9514ea..1eb21552f 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -69,7 +69,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -//#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -404,7 +404,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -554,7 +554,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller @@ -700,7 +700,7 @@ #undef USE_MY92X1 // Disable support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #undef USE_SM16716 // Disable support for SM16716 RGB LED controller (+0k7 code) #undef USE_SM2135 // Disable support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -//#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb +#undef USE_SM2335 // Disable support for SM2335 RGBCW led control as used in Switchbot Bulb #undef USE_BP5758D // Disable support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #undef USE_SONOFF_L1 // Disable support for Sonoff L1 led control #undef USE_ELECTRIQ_MOODL // Disable support for ElectriQ iQ-wifiMOODL RGBW LED controller diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h index 6b0b8c3b0..7df7efbf6 100644 --- a/tasmota/include/tasmota_configurations_ESP32.h +++ b/tasmota/include/tasmota_configurations_ESP32.h @@ -696,7 +696,6 @@ #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_THERMOSTAT // Add support for Thermostat -#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in Switchbot Bulb #define USE_ETHERNET // Add support for ethernet (+20k code) #ifndef USE_KNX diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 868a16bd0..28d77ed07 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -556,7 +556,7 @@ #define USE_MY92X1 // Add support for MY92X1 RGBCW led controller as used in Sonoff B1, Ailight and Lohas #define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) #define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) -//#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb +#define USE_SM2335 // Add support for SM2335 RGBCW led control as used in SwitchBot Color Bulb (+0k7 code) #define USE_BP5758D // Add support for BP5758D RGBCW led control as used in some Tuya lightbulbs (+0k8 code) #define USE_SONOFF_L1 // Add support for Sonoff L1 led control #define USE_ELECTRIQ_MOODL // Add support for ElectriQ iQ-wifiMOODL RGBW LED controller (+0k3 code) From 90bd5622485132981d89965ae0627e85d8e9648d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 22 Jun 2022 18:50:02 +0200 Subject: [PATCH 019/219] safeboot firmware from OTA source --- pio-tools/post_esp32.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index 276b0c3ec..686fe2e4f 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -74,7 +74,7 @@ def esp32_build_filesystem(fs_size): return True def esp32_fetch_safeboot_bin(tasmota_platform): - safeboot_fw_url = "https://github.com/arendst/Tasmota-firmware/raw/main/firmware/tasmota32/" + tasmota_platform + "-safeboot.bin" + safeboot_fw_url = "http://ota.tasmota.com/tasmota32/release/" + tasmota_platform + "-safeboot.bin" safeboot_fw_name = join(variants_dir, tasmota_platform + "-safeboot.bin") if(exists(safeboot_fw_name)): print("safeboot binary already in place.") From 8aaf620cd02b7a281e371c4319cfaf8d70e1e8ce Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 22 Jun 2022 19:05:53 +0200 Subject: [PATCH 020/219] Berry bytes.ismapped() --- lib/libesp32/berry/generate/be_const_strtab.h | 1 + .../berry/generate/be_const_strtab_def.h | 2120 +++++++++-------- .../berry/generate/be_fixed_be_class_bytes.h | 57 +- lib/libesp32/berry/src/be_byteslib.c | 16 + 4 files changed, 1107 insertions(+), 1087 deletions(-) diff --git a/lib/libesp32/berry/generate/be_const_strtab.h b/lib/libesp32/berry/generate/be_const_strtab.h index 8c3adf264..1a1785a13 100644 --- a/lib/libesp32/berry/generate/be_const_strtab.h +++ b/lib/libesp32/berry/generate/be_const_strtab.h @@ -519,6 +519,7 @@ extern const bcstring be_const_str_is_ota; extern const bcstring be_const_str_is_running; extern const bcstring be_const_str_is_spiffs; extern const bcstring be_const_str_isinstance; +extern const bcstring be_const_str_ismapped; extern const bcstring be_const_str_ismethod; extern const bcstring be_const_str_isnan; extern const bcstring be_const_str_issubclass; diff --git a/lib/libesp32/berry/generate/be_const_strtab_def.h b/lib/libesp32/berry/generate/be_const_strtab_def.h index 697fa3b5c..1f3cf3960 100644 --- a/lib/libesp32/berry/generate/be_const_strtab_def.h +++ b/lib/libesp32/berry/generate/be_const_strtab_def.h @@ -1,761 +1,762 @@ -be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_before_del); -be_define_const_str(_X0A, "\n", 252472541u, 0, 1, &be_const_str__X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E); -be_define_const_str(_X0A_X29_X3E, "\n)>", 804061574u, 0, 3, NULL); -be_define_const_str(_X20, " ", 621580159u, 0, 1, &be_const_str_delay); -be_define_const_str(_X20_X20, " ", 2982523533u, 0, 2, &be_const_str_exists); -be_define_const_str(_X20_X28, " (", 2848302581u, 0, 2, &be_const_str__def); -be_define_const_str(_X21_X3D, "!=", 2428715011u, 0, 2, &be_const_str__X3Cselect_X20name_X3D_X27zip_X27_X3E); -be_define_const_str(_X21_X3D_X3D, "!==", 559817114u, 0, 3, &be_const_str_mqtt_data); -be_define_const_str(_X22, "\"", 655135397u, 0, 1, &be_const_str_name); -be_define_const_str(_X22_X3A, "\":", 399167565u, 0, 2, &be_const_str_destructor_cb); -be_define_const_str(_X23, "#", 638357778u, 0, 1, &be_const_str_get_switches); -be_define_const_str(_X23autoexec_X2Ebat, "#autoexec.bat", 3382890497u, 0, 13, &be_const_str_constructor_cb); -be_define_const_str(_X23autoexec_X2Ebe, "#autoexec.be", 1181757091u, 0, 12, &be_const_str_h); -be_define_const_str(_X23display_X2Eini, "#display.ini", 182218220u, 0, 12, NULL); -be_define_const_str(_X23init_X2Ebat, "#init.bat", 3297595077u, 0, 9, NULL); -be_define_const_str(_X23preinit_X2Ebe, "#preinit.be", 687035716u, 0, 11, &be_const_str_setmember); -be_define_const_str(_X25, "%", 537692064u, 0, 1, &be_const_str_Unknown); -be_define_const_str(_X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, "%04d-%02d-%02dT%02d:%02d:%02d", 3425528601u, 0, 29, &be_const_str_call); -be_define_const_str(_X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, "%08x-%04x-%04x-%04x-%04x%08x", 1670063141u, 0, 28, &be_const_str_0x_X2502X); -be_define_const_str(_X25s_X2Eautoconf, "%s.autoconf", 3560383524u, 0, 11, &be_const_str_clear); -be_define_const_str(_X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, "<Error: apply new or remove>", 2855507949u, 0, 34, NULL); -be_define_const_str(_X26lt_X3BNone_X26gt_X3B, "<None>", 2602165498u, 0, 12, &be_const_str_addr); -be_define_const_str(_X27_X20_X2D_X20, "' - ", 3420378487u, 0, 4, &be_const_str_hue_status); -be_define_const_str(_X28_X29, "()", 685372826u, 0, 2, &be_const_str_RGBCT); -be_define_const_str(_X29, ")", 739023492u, 0, 1, &be_const_str_SERIAL_8O1); -be_define_const_str(_X2A, "*", 789356349u, 0, 1, &be_const_str_exec_cmd); -be_define_const_str(_X2B, "+", 772578730u, 0, 1, &be_const_str__X2E_X2E); -be_define_const_str(_X2C, ",", 688690635u, 0, 1, &be_const_str_connection_error); -be_define_const_str(_X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, ",\"AXP192\":{\"VBusVoltage\":%.3f,\"VBusCurrent\":%.1f,\"BattVoltage\":%.3f,\"BattCurrent\":%.1f,\"Temperature\":%.1f}", 2598755376u, 0, 106, &be_const_str_assert); -be_define_const_str(_X2D, "-", 671913016u, 0, 1, &be_const_str__X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E); -be_define_const_str(_X2D_X2A, "-*", 499980374u, 0, 2, &be_const_str_type_error); -be_define_const_str(_X2E, ".", 722245873u, 0, 1, &be_const_str_SERIAL_8O2); -be_define_const_str(_X2E_X2E, "..", 2748622605u, 0, 2, &be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E); -be_define_const_str(_X2Eautoconf, ".autoconf", 2524679088u, 0, 9, &be_const_str_CFG_X3A_X20loaded_X20_X20); -be_define_const_str(_X2Ebe, ".be", 1325797348u, 0, 3, NULL); -be_define_const_str(_X2Ebec, ".bec", 3985273221u, 0, 4, &be_const_str_add_cb_event_closure); -be_define_const_str(_X2Elen, ".len", 850842136u, 0, 4, NULL); -be_define_const_str(_X2Ep, ".p", 1171526419u, 0, 2, &be_const_str_ccronexpr); -be_define_const_str(_X2Esize, ".size", 1965188224u, 0, 5, NULL); -be_define_const_str(_X2Etapp, ".tapp", 1363391594u, 0, 5, &be_const_str_BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27); -be_define_const_str(_X2Ew, ".w", 1255414514u, 0, 2, &be_const_str_remove); -be_define_const_str(_X2F, "/", 705468254u, 0, 1, NULL); -be_define_const_str(_X2F_X2Eautoconf, "/.autoconf", 2212074393u, 0, 10, NULL); -be_define_const_str(_X2F_X3Frst_X3D, "/?rst=", 580074707u, 0, 6, &be_const_str_SK6812_GRBW); -be_define_const_str(_X2Fac, "/ac", 3904651978u, 0, 3, &be_const_str_create_custom_widget); -be_define_const_str(_X2Flights_X2F, "/lights/", 2370247908u, 0, 8, &be_const_str___upper__); -be_define_const_str(_X2Fstate_X2F, "/state/", 4226179876u, 0, 7, &be_const_str_EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF); -be_define_const_str(00, "00", 569209421u, 0, 2, &be_const_str_CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29); -be_define_const_str(0x_X2502X, "0x%02X", 2626549866u, 0, 6, &be_const_str_ota_); -be_define_const_str(_X3A, ":", 1057798253u, 0, 1, &be_const_str_EVENT_DELETE); -be_define_const_str(_X3C, "<", 957132539u, 0, 1, &be_const_str_BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s); -be_define_const_str(_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 3546571739u, 0, 11, &be_const_str_MI32); -be_define_const_str(_X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 1863865923u, 0, 16, &be_const_str_lv_extra); -be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str_active_otadata); -be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_True); -be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str__archive); -be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_engine); -be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_widget_event_impl); -be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_light_to_id); -be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_global); -be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str_path); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, &be_const_str_lv); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str__timers); -be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_event); -be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_leds); -be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, &be_const_str_cb_obj); -be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, &be_const_str_pc); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_c); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_map); -be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str__X3E_X3D); -be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "

", 1863865923u, 0, 16, &be_const_str__change_buffer); +be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, NULL); +be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_SERIAL_6O1); +be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_factory); +be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_assign_rmt); +be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_push_path); +be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_MD5); +be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_MI32); +be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str_finish); +be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, &be_const_str_pc); +be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29); +be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_yield); +be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker); +be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, &be_const_str_BUTTON_CONFIGURATION); +be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, &be_const_str_animate); +be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_set_reachable); +be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_def); +be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str_write_flash); +be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "

", 1863865923u, 0, 16, &be_const_str__change_buffer); -be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, NULL); -be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_SERIAL_6O1); -be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_factory); -be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_assign_rmt); -be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_push_path); -be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_MD5); -be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_MI32); -be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str_finish); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, &be_const_str_pc); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29); -be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_yield); -be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker); -be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, &be_const_str_BUTTON_CONFIGURATION); -be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, &be_const_str_animate); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_set_reachable); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_def); -be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str_write_flash); -be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "

", 1863865923u, 0, 16, &be_const_str_active_otadata); +be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str__filename); +be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str__change_buffer); +be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_publish); +be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_publish_result); +be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, NULL); +be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_delay); +be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_get_warning_level); +be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, NULL); +be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, NULL); +be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_add_cb_event_closure); +be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_flush); +be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_SERIAL_5N1); +be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, NULL); +be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, NULL); +be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_nan); +be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_atan); +be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str_concat); +be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "

", 1863865923u, 0, 16, &be_const_str_active_otadata); -be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str__filename); -be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str__change_buffer); -be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_publish); -be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_publish_result); -be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, NULL); -be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_delay); -be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_get_warning_level); -be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, NULL); +be_define_const_str(_X2F, "/", 705468254u, 0, 1, &be_const_str__archive); +be_define_const_str(_X2F_X2Eautoconf, "/.autoconf", 2212074393u, 0, 10, &be_const_str_add_rule); +be_define_const_str(_X2F_X3Frst_X3D, "/?rst=", 580074707u, 0, 6, &be_const_str__X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E); +be_define_const_str(_X2Fac, "/ac", 3904651978u, 0, 3, &be_const_str_RELAY); +be_define_const_str(_X2Flights_X2F, "/lights/", 2370247908u, 0, 8, &be_const_str_battery_present); +be_define_const_str(_X2Fstate_X2F, "/state/", 4226179876u, 0, 7, &be_const_str_coredump); +be_define_const_str(00, "00", 569209421u, 0, 2, &be_const_str_reverse); +be_define_const_str(0x_X2502X, "0x%02X", 2626549866u, 0, 6, &be_const_str_Partition_otadata); +be_define_const_str(_X3A, ":", 1057798253u, 0, 1, &be_const_str_EVENT_DELETE); +be_define_const_str(_X3C, "<", 957132539u, 0, 1, &be_const_str_set_reachable); +be_define_const_str(_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 3546571739u, 0, 11, &be_const_str_SERIAL_6O2); +be_define_const_str(_X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 1863865923u, 0, 16, &be_const_str_exists); +be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str_rtc); +be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_CT); +be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, NULL); +be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_wire2); +be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found); +be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, NULL); +be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, NULL); +be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str__X3F); be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, NULL); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_add_cb_event_closure); -be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_flush); -be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_SERIAL_5N1); -be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, NULL); +be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_get_warning_level); +be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s); +be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_pc_abs); +be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, &be_const_str_back_forth); be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, NULL); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_nan); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_atan); -be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str_concat); -be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "", 4247924536u, 0, 19, &be_const_str__X5B); +be_define_const_str(_X3D, "=", 940354920u, 0, 1, NULL); +be_define_const_str(_X3D_X3C_X3E_X21, "=<>!", 2664470277u, 0, 4, &be_const_str_open); be_define_const_str(_X3D_X3D, "==", 2431966415u, 0, 2, NULL); -be_define_const_str(_X3E, ">", 990687777u, 0, 1, &be_const_str__def); -be_define_const_str(_X3E_X3D, ">=", 284975636u, 0, 2, &be_const_str_c); -be_define_const_str(_X3F, "?", 973910158u, 0, 1, &be_const_str_now); -be_define_const_str(AA50, "AA50", 2265997666u, 0, 4, &be_const_str_SERIAL_5O2); -be_define_const_str(AXP192, "AXP192", 757230128u, 0, 6, &be_const_str_get_bat_power); -be_define_const_str(Animate_X20pc_X20is_X20out_X20of_X20range, "Animate pc is out of range", 1854929421u, 0, 26, &be_const_str_debug); -be_define_const_str(Auto_X2Dconfiguration, "Auto-configuration", 1665006109u, 0, 18, &be_const_str_event); -be_define_const_str(BECDFE, "BECDFE", 608341218u, 0, 6, &be_const_str_crc32); -be_define_const_str(BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, "BRY: ERROR, bad json: ", 2715135809u, 0, 22, &be_const_str_CFG_X3A_X20removed_X20file_X20_X27_X25s_X27); -be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27, "BRY: Exception> '", 3883673906u, 0, 17, &be_const_str_OPTION_A); -be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: Exception> '%s' - %s", 2246990964u, 0, 25, &be_const_str_MI32); -be_define_const_str(BRY_X3A_X20argument_X20must_X20be_X20a_X20function, "BRY: argument must be a function", 3917068408u, 0, 32, &be_const_str_CFG_X3A_X20loading_X20); -be_define_const_str(BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29, "BRY: bytecode has wrong version '%s' (%i)", 2140321415u, 0, 41, &be_const_str_break); -be_define_const_str(BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, "BRY: corrupt bytecode '%s'", 4009923544u, 0, 26, &be_const_str_closure); -be_define_const_str(BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, "BRY: could not save compiled file %s (%s)", 736659787u, 0, 41, &be_const_str_ceil); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, "BRY: failed to load '%s' (%s - %s)", 1047433014u, 0, 34, &be_const_str___iterator__); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, "BRY: failed to load _persist.json", 2991913445u, 0, 33, &be_const_str_chars_in_string); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, "BRY: failed to load compiled '%s' (%s)", 3488122666u, 0, 38, &be_const_str_format); -be_define_const_str(BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: failed to run compiled code '%s' - %s", 380265962u, 0, 42, &be_const_str_widget_group_def); -be_define_const_str(BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, "BRY: invalid hue payload: ", 203709367u, 0, 26, &be_const_str_maxota); -be_define_const_str(BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, "BRY: method not allowed, use a closure like '/ args -> obj.func(args)'", 177121572u, 0, 70, &be_const_str_CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem); -be_define_const_str(BUTTON_CONFIGURATION, "BUTTON_CONFIGURATION", 70820856u, 0, 20, &be_const_str_Too_X20many_X20partiition_X20slots); -be_define_const_str(CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, "CFG: 'init.bat' done, restarting", 1569670677u, 0, 32, &be_const_str_save_before_restart); -be_define_const_str(CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "CFG: Exception> '%s' - %s", 1228874553u, 0, 25, NULL); -be_define_const_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found, "CFG: No '*.autoconf' file found", 755798501u, 0, 31, NULL); -be_define_const_str(CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, "CFG: could not run %s (%s - %s)", 1428829580u, 0, 31, &be_const_str_connect); -be_define_const_str(CFG_X3A_X20downloading_X20_X27_X25s_X27, "CFG: downloading '%s'", 589480701u, 0, 21, &be_const_str_flash); -be_define_const_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, "CFG: exception '%s' - '%s'", 4095407913u, 0, 26, &be_const_str_widget_instance_size); -be_define_const_str(CFG_X3A_X20loaded_X20_X20, "CFG: loaded ", 3710273538u, 0, 13, &be_const_str_get_bri); -be_define_const_str(CFG_X3A_X20loaded_X20_X27_X25s_X27, "CFG: loaded '%s'", 1699028828u, 0, 16, &be_const_str_add_light); -be_define_const_str(CFG_X3A_X20loading_X20, "CFG: loading ", 4010361503u, 0, 13, &be_const_str_SERIAL_6O2); -be_define_const_str(CFG_X3A_X20loading_X20_X27_X25s_X27, "CFG: loading '%s'", 2285306097u, 0, 17, &be_const_str_crc8); -be_define_const_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, "CFG: multiple autoconf files found, aborting ('%s' + '%s')", 197663371u, 0, 58, &be_const_str_set_useragent); -be_define_const_str(CFG_X3A_X20ran_X20_X20, "CFG: ran ", 3579570472u, 0, 10, &be_const_str_get); -be_define_const_str(CFG_X3A_X20removed_X20file_X20_X27_X25s_X27, "CFG: removed file '%s'", 2048602473u, 0, 22, &be_const_str_abs); -be_define_const_str(CFG_X3A_X20removing_X20autoconf_X20files, "CFG: removing autoconf files", 4014704970u, 0, 28, &be_const_str__anonymous_); -be_define_const_str(CFG_X3A_X20removing_X20first_X20time_X20marker, "CFG: removing first time marker", 2125556683u, 0, 31, &be_const_str_CFG_X3A_X20return_code_X3D_X25i); -be_define_const_str(CFG_X3A_X20return_code_X3D_X25i, "CFG: return_code=%i", 2059897320u, 0, 19, &be_const_str_base_class); -be_define_const_str(CFG_X3A_X20running_X20, "CFG: running ", 2478334534u, 0, 13, &be_const_str_set_auth); -be_define_const_str(CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem, "CFG: skipping 'display.ini' because already present in file-system", 3965549264u, 0, 66, &be_const_str_hue_ntv); -be_define_const_str(CT, "CT", 1792671826u, 0, 2, &be_const_str_Trigger); -be_define_const_str(DIMMER, "DIMMER", 4049308363u, 0, 6, &be_const_str__begin_transmission); -be_define_const_str(EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3217293201u, 0, 32, &be_const_str_create_matrix); -be_define_const_str(EVENT_DELETE, "EVENT_DELETE", 282828603u, 0, 12, NULL); -be_define_const_str(FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2684107141u, 0, 48, &be_const_str__buffer); -be_define_const_str(False, "False", 2541049336u, 0, 5, &be_const_str_subtype); -be_define_const_str(GET, "GET", 2531704439u, 0, 3, &be_const_str_cmd_res); -be_define_const_str(HTTP_GET, "HTTP_GET", 1722467738u, 0, 8, &be_const_str_get_log); -be_define_const_str(HTTP_POST, "HTTP_POST", 1999554144u, 0, 9, &be_const_str_log); -be_define_const_str(I2C_X3A, "I2C:", 813483371u, 0, 4, &be_const_str_invalid_X20magic_X20number_X20_X2502X); -be_define_const_str(I2C_Driver, "I2C_Driver", 1714501658u, 0, 10, &be_const_str_deregister_obj); -be_define_const_str(Invalid_X20ota_X20partition_X20number, "Invalid ota partition number", 1611602265u, 0, 28, &be_const_str_load); -be_define_const_str(LVG_X3A_X20call_X20to_X20unsupported_X20callback, "LVG: call to unsupported callback", 504176819u, 0, 33, NULL); -be_define_const_str(Leds, "Leds", 2709245275u, 0, 4, &be_const_str__debug_present); -be_define_const_str(MAX_RMT, "MAX_RMT", 1615574873u, 0, 7, &be_const_str_is_ota); -be_define_const_str(MD5, "MD5", 1935726387u, 0, 3, &be_const_str_classof); -be_define_const_str(MI32, "MI32", 4074273414u, 0, 4, &be_const_str_add_rule); -be_define_const_str(No_X20SPIFFS_X20partition_X20found, "No SPIFFS partition found", 4165718279u, 0, 25, &be_const_str_couldn_X27t_X20not_X20initialize_X20noepixelbus); -be_define_const_str(None, "None", 810547195u, 0, 4, &be_const_str_bytes); -be_define_const_str(OPTION_A, "OPTION_A", 1133299440u, 0, 8, &be_const_str_call); -be_define_const_str(POST, "POST", 1929554311u, 0, 4, &be_const_str_app); -be_define_const_str(Parameter_X20error, "Parameter error", 3840042038u, 0, 15, &be_const_str_raise); -be_define_const_str(Partition, "Partition", 3077057705u, 0, 9, &be_const_str_string); -be_define_const_str(Partition_info, "Partition_info", 3970922042u, 0, 14, &be_const_str_leds); -be_define_const_str(Partition_otadata, "Partition_otadata", 2666256496u, 0, 17, &be_const_str_day); -be_define_const_str(RELAY, "RELAY", 2163786658u, 0, 5, &be_const_str_before_del); -be_define_const_str(RGB, "RGB", 3386082140u, 0, 3, &be_const_str_resize); -be_define_const_str(RGBCT, "RGBCT", 8076251u, 0, 5, &be_const_str_SERIAL_5N2); -be_define_const_str(RGBW, "RGBW", 3270986321u, 0, 4, &be_const_str_copy); -be_define_const_str(Restart_X201, "Restart 1", 3504455855u, 0, 9, &be_const_str_get_image_size); -be_define_const_str(SERIAL_5E1, "SERIAL_5E1", 1163775235u, 0, 10, &be_const_str_content_send); -be_define_const_str(SERIAL_5E2, "SERIAL_5E2", 1180552854u, 0, 10, &be_const_str_ins_time); -be_define_const_str(SERIAL_5N1, "SERIAL_5N1", 3313031680u, 0, 10, &be_const_str_SERIAL_7E2); -be_define_const_str(SERIAL_5N2, "SERIAL_5N2", 3363364537u, 0, 10, &be_const_str_SERIAL_7E1); -be_define_const_str(SERIAL_5O1, "SERIAL_5O1", 3782657917u, 0, 10, &be_const_str_millis); -be_define_const_str(SERIAL_5O2, "SERIAL_5O2", 3732325060u, 0, 10, NULL); -be_define_const_str(SERIAL_6E1, "SERIAL_6E1", 334249486u, 0, 10, &be_const_str_def); -be_define_const_str(SERIAL_6E2, "SERIAL_6E2", 317471867u, 0, 10, NULL); -be_define_const_str(SERIAL_6N1, "SERIAL_6N1", 198895701u, 0, 10, &be_const_str_keys); -be_define_const_str(SERIAL_6N2, "SERIAL_6N2", 148562844u, 0, 10, &be_const_str_seq0); -be_define_const_str(SERIAL_6O1, "SERIAL_6O1", 266153272u, 0, 10, &be_const_str_light); +be_define_const_str(_X3E, ">", 990687777u, 0, 1, NULL); +be_define_const_str(_X3E_X3D, ">=", 284975636u, 0, 2, NULL); +be_define_const_str(_X3F, "?", 973910158u, 0, 1, &be_const_str_Tasmota); +be_define_const_str(AA50, "AA50", 2265997666u, 0, 4, &be_const_str_light_X20must_X20be_X20of_X20class_X20_X27light_state_X27); +be_define_const_str(AXP192, "AXP192", 757230128u, 0, 6, &be_const_str_every_50ms); +be_define_const_str(Animate_X20pc_X20is_X20out_X20of_X20range, "Animate pc is out of range", 1854929421u, 0, 26, &be_const_str_CFG_X3A_X20downloading_X20_X27_X25s_X27); +be_define_const_str(Auto_X2Dconfiguration, "Auto-configuration", 1665006109u, 0, 18, &be_const_str_LVG_X3A_X20call_X20to_X20unsupported_X20callback); +be_define_const_str(BECDFE, "BECDFE", 608341218u, 0, 6, &be_const_str_c); +be_define_const_str(BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, "BRY: ERROR, bad json: ", 2715135809u, 0, 22, &be_const_str_cmd); +be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27, "BRY: Exception> '", 3883673906u, 0, 17, NULL); +be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: Exception> '%s' - %s", 2246990964u, 0, 25, &be_const_str_class_init_obj); +be_define_const_str(BRY_X3A_X20argument_X20must_X20be_X20a_X20function, "BRY: argument must be a function", 3917068408u, 0, 32, &be_const_str_escape); +be_define_const_str(BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29, "BRY: bytecode has wrong version '%s' (%i)", 2140321415u, 0, 41, NULL); +be_define_const_str(BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, "BRY: corrupt bytecode '%s'", 4009923544u, 0, 26, &be_const_str_CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem); +be_define_const_str(BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, "BRY: could not save compiled file %s (%s)", 736659787u, 0, 41, NULL); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, "BRY: failed to load '%s' (%s - %s)", 1047433014u, 0, 34, NULL); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, "BRY: failed to load _persist.json", 2991913445u, 0, 33, &be_const_str_Invalid_X20ota_X20partition_X20number); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, "BRY: failed to load compiled '%s' (%s)", 3488122666u, 0, 38, &be_const_str_fast_loop_enabled); +be_define_const_str(BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: failed to run compiled code '%s' - %s", 380265962u, 0, 42, &be_const_str_rotate); +be_define_const_str(BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, "BRY: invalid hue payload: ", 203709367u, 0, 26, &be_const_str_get_factory_slot); +be_define_const_str(BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, "BRY: method not allowed, use a closure like '/ args -> obj.func(args)'", 177121572u, 0, 70, &be_const_str_lvgl_event_dispatch); +be_define_const_str(BUTTON_CONFIGURATION, "BUTTON_CONFIGURATION", 70820856u, 0, 20, &be_const_str_super); +be_define_const_str(CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, "CFG: 'init.bat' done, restarting", 1569670677u, 0, 32, &be_const_str_tasmota_log_reader); +be_define_const_str(CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "CFG: Exception> '%s' - %s", 1228874553u, 0, 25, &be_const_str_CFG_X3A_X20removed_X20file_X20_X27_X25s_X27); +be_define_const_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found, "CFG: No '*.autoconf' file found", 755798501u, 0, 31, &be_const_str_RGBW); +be_define_const_str(CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, "CFG: could not run %s (%s - %s)", 1428829580u, 0, 31, &be_const_str_animate); +be_define_const_str(CFG_X3A_X20downloading_X20_X27_X25s_X27, "CFG: downloading '%s'", 589480701u, 0, 21, NULL); +be_define_const_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, "CFG: exception '%s' - '%s'", 4095407913u, 0, 26, &be_const_str_read13); +be_define_const_str(CFG_X3A_X20loaded_X20_X20, "CFG: loaded ", 3710273538u, 0, 13, &be_const_str_asstring); +be_define_const_str(CFG_X3A_X20loaded_X20_X27_X25s_X27, "CFG: loaded '%s'", 1699028828u, 0, 16, &be_const_str_addr); +be_define_const_str(CFG_X3A_X20loading_X20, "CFG: loading ", 4010361503u, 0, 13, &be_const_str__unsubscribe); +be_define_const_str(CFG_X3A_X20loading_X20_X27_X25s_X27, "CFG: loading '%s'", 2285306097u, 0, 17, NULL); +be_define_const_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, "CFG: multiple autoconf files found, aborting ('%s' + '%s')", 197663371u, 0, 58, &be_const_str_map); +be_define_const_str(CFG_X3A_X20ran_X20_X20, "CFG: ran ", 3579570472u, 0, 10, &be_const_str_cb_obj); +be_define_const_str(CFG_X3A_X20removed_X20file_X20_X27_X25s_X27, "CFG: removed file '%s'", 2048602473u, 0, 22, NULL); +be_define_const_str(CFG_X3A_X20removing_X20autoconf_X20files, "CFG: removing autoconf files", 4014704970u, 0, 28, &be_const_str__end_transmission); +be_define_const_str(CFG_X3A_X20removing_X20first_X20time_X20marker, "CFG: removing first time marker", 2125556683u, 0, 31, &be_const_str_pixel_count); +be_define_const_str(CFG_X3A_X20return_code_X3D_X25i, "CFG: return_code=%i", 2059897320u, 0, 19, &be_const_str_uuid4); +be_define_const_str(CFG_X3A_X20running_X20, "CFG: running ", 2478334534u, 0, 13, &be_const_str_available); +be_define_const_str(CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem, "CFG: skipping 'display.ini' because already present in file-system", 3965549264u, 0, 66, &be_const_str_fromhex); +be_define_const_str(CT, "CT", 1792671826u, 0, 2, &be_const_str_closure); +be_define_const_str(DIMMER, "DIMMER", 4049308363u, 0, 6, &be_const_str_atan2); +be_define_const_str(EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3217293201u, 0, 32, NULL); +be_define_const_str(EVENT_DELETE, "EVENT_DELETE", 282828603u, 0, 12, &be_const_str_dirty); +be_define_const_str(FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2684107141u, 0, 48, &be_const_str_add_cb_event_closure); +be_define_const_str(False, "False", 2541049336u, 0, 5, &be_const_str_add_fast_loop); +be_define_const_str(GET, "GET", 2531704439u, 0, 3, &be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf); +be_define_const_str(HTTP_GET, "HTTP_GET", 1722467738u, 0, 8, &be_const_str_spiffs); +be_define_const_str(HTTP_POST, "HTTP_POST", 1999554144u, 0, 9, &be_const_str_attrdump); +be_define_const_str(I2C_X3A, "I2C:", 813483371u, 0, 4, &be_const_str_log); +be_define_const_str(I2C_Driver, "I2C_Driver", 1714501658u, 0, 10, &be_const_str_group_def); +be_define_const_str(Invalid_X20ota_X20partition_X20number, "Invalid ota partition number", 1611602265u, 0, 28, NULL); +be_define_const_str(LVG_X3A_X20call_X20to_X20unsupported_X20callback, "LVG: call to unsupported callback", 504176819u, 0, 33, &be_const_str_add_cmd); +be_define_const_str(Leds, "Leds", 2709245275u, 0, 4, &be_const_str_widget_group_def); +be_define_const_str(MAX_RMT, "MAX_RMT", 1615574873u, 0, 7, &be_const_str_duration); +be_define_const_str(MD5, "MD5", 1935726387u, 0, 3, &be_const_str_compress); +be_define_const_str(MI32, "MI32", 4074273414u, 0, 4, NULL); +be_define_const_str(No_X20SPIFFS_X20partition_X20found, "No SPIFFS partition found", 4165718279u, 0, 25, &be_const_str_ccronexpr); +be_define_const_str(None, "None", 810547195u, 0, 4, &be_const_str_subscribe); +be_define_const_str(OPTION_A, "OPTION_A", 1133299440u, 0, 8, &be_const_str_asin); +be_define_const_str(POST, "POST", 1929554311u, 0, 4, &be_const_str_add_driver); +be_define_const_str(Parameter_X20error, "Parameter error", 3840042038u, 0, 15, &be_const_str_WS2812); +be_define_const_str(Partition, "Partition", 3077057705u, 0, 9, &be_const_str_lv); +be_define_const_str(Partition_info, "Partition_info", 3970922042u, 0, 14, &be_const_str_cb_do_nothing); +be_define_const_str(Partition_otadata, "Partition_otadata", 2666256496u, 0, 17, NULL); +be_define_const_str(RELAY, "RELAY", 2163786658u, 0, 5, &be_const_str_create_custom_widget); +be_define_const_str(RGB, "RGB", 3386082140u, 0, 3, &be_const_str_connection_error); +be_define_const_str(RGBCT, "RGBCT", 8076251u, 0, 5, &be_const_str_strftime); +be_define_const_str(RGBW, "RGBW", 3270986321u, 0, 4, &be_const_str_exec_tele); +be_define_const_str(Restart_X201, "Restart 1", 3504455855u, 0, 9, &be_const_str_widget_cb); +be_define_const_str(SERIAL_5E1, "SERIAL_5E1", 1163775235u, 0, 10, &be_const_str__rmt); +be_define_const_str(SERIAL_5E2, "SERIAL_5E2", 1180552854u, 0, 10, &be_const_str_cos); +be_define_const_str(SERIAL_5N1, "SERIAL_5N1", 3313031680u, 0, 10, NULL); +be_define_const_str(SERIAL_5N2, "SERIAL_5N2", 3363364537u, 0, 10, &be_const_str_crc8); +be_define_const_str(SERIAL_5O1, "SERIAL_5O1", 3782657917u, 0, 10, &be_const_str_from_to); +be_define_const_str(SERIAL_5O2, "SERIAL_5O2", 3732325060u, 0, 10, &be_const_str__class); +be_define_const_str(SERIAL_6E1, "SERIAL_6E1", 334249486u, 0, 10, &be_const_str__anonymous_); +be_define_const_str(SERIAL_6E2, "SERIAL_6E2", 317471867u, 0, 10, &be_const_str_next_cron); +be_define_const_str(SERIAL_6N1, "SERIAL_6N1", 198895701u, 0, 10, &be_const_str_arg); +be_define_const_str(SERIAL_6N2, "SERIAL_6N2", 148562844u, 0, 10, NULL); +be_define_const_str(SERIAL_6O1, "SERIAL_6O1", 266153272u, 0, 10, &be_const_str_remove_cmd); be_define_const_str(SERIAL_6O2, "SERIAL_6O2", 316486129u, 0, 10, NULL); -be_define_const_str(SERIAL_7E1, "SERIAL_7E1", 147718061u, 0, 10, &be_const_str_code); -be_define_const_str(SERIAL_7E2, "SERIAL_7E2", 97385204u, 0, 10, &be_const_str_get_active); -be_define_const_str(SERIAL_7N1, "SERIAL_7N1", 1891060246u, 0, 10, &be_const_str_classname); -be_define_const_str(SERIAL_7N2, "SERIAL_7N2", 1874282627u, 0, 10, &be_const_str_toint); -be_define_const_str(SERIAL_7O1, "SERIAL_7O1", 1823802675u, 0, 10, &be_const_str_digital_write); -be_define_const_str(SERIAL_7O2, "SERIAL_7O2", 1840580294u, 0, 10, &be_const_str_pin); -be_define_const_str(SERIAL_8E1, "SERIAL_8E1", 2371121616u, 0, 10, &be_const_str_find); -be_define_const_str(SERIAL_8E2, "SERIAL_8E2", 2421454473u, 0, 10, &be_const_str_phy); +be_define_const_str(SERIAL_7E1, "SERIAL_7E1", 147718061u, 0, 10, &be_const_str_data); +be_define_const_str(SERIAL_7E2, "SERIAL_7E2", 97385204u, 0, 10, &be_const_str_discover); +be_define_const_str(SERIAL_7N1, "SERIAL_7N1", 1891060246u, 0, 10, &be_const_str_id); +be_define_const_str(SERIAL_7N2, "SERIAL_7N2", 1874282627u, 0, 10, &be_const_str__settings_def); +be_define_const_str(SERIAL_7O1, "SERIAL_7O1", 1823802675u, 0, 10, &be_const_str_SERIAL_8N1); +be_define_const_str(SERIAL_7O2, "SERIAL_7O2", 1840580294u, 0, 10, &be_const_str_SERIAL_8N2); +be_define_const_str(SERIAL_8E1, "SERIAL_8E1", 2371121616u, 0, 10, &be_const_str__ptr); +be_define_const_str(SERIAL_8E2, "SERIAL_8E2", 2421454473u, 0, 10, &be_const_str___iterator__); be_define_const_str(SERIAL_8N1, "SERIAL_8N1", 2369297235u, 0, 10, NULL); -be_define_const_str(SERIAL_8N2, "SERIAL_8N2", 2386074854u, 0, 10, &be_const_str_int); -be_define_const_str(SERIAL_8O1, "SERIAL_8O1", 289122742u, 0, 10, &be_const_str_erase); -be_define_const_str(SERIAL_8O2, "SERIAL_8O2", 272345123u, 0, 10, &be_const_str_cb); -be_define_const_str(SK6812_GRBW, "SK6812_GRBW", 81157857u, 0, 11, &be_const_str_otadata); -be_define_const_str(TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, "TAP: Loaded Tasmota App '%s'", 926477145u, 0, 28, &be_const_str_light_to_id); -be_define_const_str(Tasmota, "Tasmota", 4047617668u, 0, 7, &be_const_str_web_add_management_button); -be_define_const_str(Tele, "Tele", 1329980653u, 0, 4, &be_const_str_content_stop); -be_define_const_str(Too_X20many_X20partiition_X20slots, "Too many partiition slots", 3190277896u, 0, 25, &be_const_str_arg); -be_define_const_str(Trigger, "Trigger", 2783579555u, 0, 7, &be_const_str_register_obj); -be_define_const_str(True, "True", 3453902341u, 0, 4, &be_const_str_next_cron); -be_define_const_str(Unknown, "Unknown", 3424652889u, 0, 7, &be_const_str_lower); -be_define_const_str(Unknown_X20command, "Unknown command", 1830905432u, 0, 15, NULL); +be_define_const_str(SERIAL_8N2, "SERIAL_8N2", 2386074854u, 0, 10, &be_const_str_get_alternate); +be_define_const_str(SERIAL_8O1, "SERIAL_8O1", 289122742u, 0, 10, &be_const_str_crc32); +be_define_const_str(SERIAL_8O2, "SERIAL_8O2", 272345123u, 0, 10, &be_const_str_engine); +be_define_const_str(SK6812_GRBW, "SK6812_GRBW", 81157857u, 0, 11, &be_const_str_can_show); +be_define_const_str(TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, "TAP: Loaded Tasmota App '%s'", 926477145u, 0, 28, &be_const_str_valuer_error); +be_define_const_str(Tasmota, "Tasmota", 4047617668u, 0, 7, &be_const_str_false); +be_define_const_str(Tele, "Tele", 1329980653u, 0, 4, NULL); +be_define_const_str(Too_X20many_X20partiition_X20slots, "Too many partiition slots", 3190277896u, 0, 25, &be_const_str_io_error); +be_define_const_str(Trigger, "Trigger", 2783579555u, 0, 7, &be_const_str_has); +be_define_const_str(True, "True", 3453902341u, 0, 4, &be_const_str_display_X2Eini); +be_define_const_str(Unknown, "Unknown", 3424652889u, 0, 7, NULL); +be_define_const_str(Unknown_X20command, "Unknown command", 1830905432u, 0, 15, &be_const_str_set_light); be_define_const_str(WS2812, "WS2812", 3539741218u, 0, 6, NULL); -be_define_const_str(WS2812_GRB, "WS2812_GRB", 1736405692u, 0, 10, &be_const_str_get_event_cb); -be_define_const_str(Wire, "Wire", 1938276536u, 0, 4, &be_const_str__end_transmission); -be_define_const_str(_X5B, "[", 3725336506u, 0, 1, &be_const_str_has); -be_define_const_str(_X5D, "]", 3624670792u, 0, 1, &be_const_str_height_def); -be_define_const_str(_X5D_X2C_X0A_X20_X20, "],\n ", 2456223650u, 0, 5, &be_const_str_content_button); -be_define_const_str(_, "_", 3658226030u, 0, 1, &be_const_str_ptr); -be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_flags); -be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_sat); -be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, &be_const_str__write); -be_define_const_str(_anonymous_, "_anonymous_", 1957281476u, 0, 11, &be_const_str_lv_point_arr); -be_define_const_str(_archive, "_archive", 4004559404u, 0, 8, &be_const_str_compile); +be_define_const_str(WS2812_GRB, "WS2812_GRB", 1736405692u, 0, 10, NULL); +be_define_const_str(Wire, "Wire", 1938276536u, 0, 4, &be_const_str_set_pixel_color); +be_define_const_str(_X5B, "[", 3725336506u, 0, 1, NULL); +be_define_const_str(_X5D, "]", 3624670792u, 0, 1, &be_const_str_enabled); +be_define_const_str(_X5D_X2C_X0A_X20_X20, "],\n ", 2456223650u, 0, 5, NULL); +be_define_const_str(_, "_", 3658226030u, 0, 1, &be_const_str_geti); +be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_lvgl_timer_dispatch); +be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_pin); +be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, NULL); +be_define_const_str(_anonymous_, "_anonymous_", 1957281476u, 0, 11, NULL); +be_define_const_str(_archive, "_archive", 4004559404u, 0, 8, &be_const_str_write_gpio); be_define_const_str(_available, "_available", 1306196581u, 0, 10, NULL); -be_define_const_str(_begin_transmission, "_begin_transmission", 2779461176u, 0, 19, &be_const_str__energy); -be_define_const_str(_buffer, "_buffer", 2044888568u, 0, 7, NULL); -be_define_const_str(_ccmd, "_ccmd", 2163421413u, 0, 5, &be_const_str_ota_); -be_define_const_str(_change_buffer, "_change_buffer", 2101848693u, 0, 14, &be_const_str_animate); -be_define_const_str(_class, "_class", 2732146350u, 0, 6, &be_const_str_allocated); -be_define_const_str(_cmd, "_cmd", 3419822142u, 0, 4, &be_const_str_pc); -be_define_const_str(_crons, "_crons", 1000733579u, 0, 6, &be_const_str_lv_point); -be_define_const_str(_debug_present, "_debug_present", 4063411725u, 0, 14, &be_const_str_factory); -be_define_const_str(_def, "_def", 1985022181u, 0, 4, &be_const_str_lv_event); -be_define_const_str(_dirty, "_dirty", 283846766u, 0, 6, NULL); -be_define_const_str(_drivers, "_drivers", 3260328985u, 0, 8, &be_const_str_byte); -be_define_const_str(_end_transmission, "_end_transmission", 3237480400u, 0, 17, &be_const_str__timers); -be_define_const_str(_energy, "_energy", 535372070u, 0, 7, &be_const_str_argument_X20must_X20be_X20a_X20function); -be_define_const_str(_error, "_error", 1132109656u, 0, 6, &be_const_str_else); -be_define_const_str(_filename, "_filename", 1430813195u, 0, 9, &be_const_str_create_segment); -be_define_const_str(_fl, "_fl", 4042564892u, 0, 3, &be_const_str_readline); -be_define_const_str(_global_addr, "_global_addr", 533766721u, 0, 12, &be_const_str_set_ct); -be_define_const_str(_global_def, "_global_def", 646007001u, 0, 11, &be_const_str_try); -be_define_const_str(_p, "_p", 1594591802u, 0, 2, &be_const_str_lv_solidified); -be_define_const_str(_persist_X2Ejson, "_persist.json", 2008425138u, 0, 13, &be_const_str_rule); -be_define_const_str(_ptr, "_ptr", 306235816u, 0, 4, &be_const_str_file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27); -be_define_const_str(_read, "_read", 346717030u, 0, 5, &be_const_str_get_size); -be_define_const_str(_request_from, "_request_from", 3965148604u, 0, 13, &be_const_str_back_forth); -be_define_const_str(_rmt, "_rmt", 1094422685u, 0, 4, &be_const_str_asstring); -be_define_const_str(_rules, "_rules", 4266217105u, 0, 6, &be_const_str__subscribe); -be_define_const_str(_settings_def, "_settings_def", 3775560307u, 0, 13, &be_const_str_getfloat); -be_define_const_str(_settings_ptr, "_settings_ptr", 1825772182u, 0, 13, &be_const_str_every_second); -be_define_const_str(_subscribe, "_subscribe", 2888456770u, 0, 10, NULL); -be_define_const_str(_t, "_t", 1527481326u, 0, 2, &be_const_str_o); -be_define_const_str(_timers, "_timers", 2600100916u, 0, 7, &be_const_str_persist); -be_define_const_str(_unsubscribe, "_unsubscribe", 2110993883u, 0, 12, &be_const_str_length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032); -be_define_const_str(_validate, "_validate", 1742604448u, 0, 9, &be_const_str_get_name); -be_define_const_str(_write, "_write", 2215462825u, 0, 6, &be_const_str_deg); -be_define_const_str(a, "a", 3826002220u, 0, 1, &be_const_str_set_ldo_enable); -be_define_const_str(abs, "abs", 709362235u, 0, 3, &be_const_str_data); -be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_detect); -be_define_const_str(active_otadata, "active_otadata", 3055353486u, 0, 14, &be_const_str_null_cb); -be_define_const_str(add, "add", 993596020u, 0, 3, &be_const_str_cb_do_nothing); -be_define_const_str(add_anim, "add_anim", 3980662668u, 0, 8, &be_const_str_invalidate_spiffs); -be_define_const_str(add_cb_event_closure, "add_cb_event_closure", 1775958321u, 0, 20, &be_const_str_devices); -be_define_const_str(add_cmd, "add_cmd", 3361630879u, 0, 7, &be_const_str_clear); -be_define_const_str(add_cron, "add_cron", 2475327477u, 0, 8, &be_const_str_value_error); -be_define_const_str(add_driver, "add_driver", 1654458371u, 0, 10, &be_const_str_json_fdump_list); -be_define_const_str(add_fast_loop, "add_fast_loop", 3025842946u, 0, 13, &be_const_str_get_object_from_ptr); -be_define_const_str(add_handler, "add_handler", 2055124119u, 0, 11, NULL); -be_define_const_str(add_header, "add_header", 927130612u, 0, 10, &be_const_str_argument_X20must_X20be_X20a_X20list); +be_define_const_str(_begin_transmission, "_begin_transmission", 2779461176u, 0, 19, &be_const_str_codedump); +be_define_const_str(_buffer, "_buffer", 2044888568u, 0, 7, &be_const_str_get_bat_voltage); +be_define_const_str(_ccmd, "_ccmd", 2163421413u, 0, 5, NULL); +be_define_const_str(_change_buffer, "_change_buffer", 2101848693u, 0, 14, NULL); +be_define_const_str(_class, "_class", 2732146350u, 0, 6, &be_const_str_detected_X20on_X20bus); +be_define_const_str(_cmd, "_cmd", 3419822142u, 0, 4, &be_const_str_connected); +be_define_const_str(_crons, "_crons", 1000733579u, 0, 6, &be_const_str_instance_X20required); +be_define_const_str(_debug_present, "_debug_present", 4063411725u, 0, 14, &be_const_str_content_flush); +be_define_const_str(_def, "_def", 1985022181u, 0, 4, NULL); +be_define_const_str(_dirty, "_dirty", 283846766u, 0, 6, &be_const_str_webclient); +be_define_const_str(_drivers, "_drivers", 3260328985u, 0, 8, &be_const_str_factory); +be_define_const_str(_end_transmission, "_end_transmission", 3237480400u, 0, 17, &be_const_str_for); +be_define_const_str(_energy, "_energy", 535372070u, 0, 7, &be_const_str__subscribe); +be_define_const_str(_error, "_error", 1132109656u, 0, 6, &be_const_str__t); +be_define_const_str(_filename, "_filename", 1430813195u, 0, 9, &be_const_str_raise); +be_define_const_str(_fl, "_fl", 4042564892u, 0, 3, &be_const_str_add_header); +be_define_const_str(_global_addr, "_global_addr", 533766721u, 0, 12, &be_const_str_member); +be_define_const_str(_global_def, "_global_def", 646007001u, 0, 11, &be_const_str_f); +be_define_const_str(_p, "_p", 1594591802u, 0, 2, &be_const_str_adc_config); +be_define_const_str(_persist_X2Ejson, "_persist.json", 2008425138u, 0, 13, &be_const_str_argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize); +be_define_const_str(_ptr, "_ptr", 306235816u, 0, 4, &be_const_str__timers); +be_define_const_str(_read, "_read", 346717030u, 0, 5, &be_const_str_destructor_cb); +be_define_const_str(_request_from, "_request_from", 3965148604u, 0, 13, NULL); +be_define_const_str(_rmt, "_rmt", 1094422685u, 0, 4, NULL); +be_define_const_str(_rules, "_rules", 4266217105u, 0, 6, &be_const_str__write); +be_define_const_str(_settings_def, "_settings_def", 3775560307u, 0, 13, &be_const_str_alternate); +be_define_const_str(_settings_ptr, "_settings_ptr", 1825772182u, 0, 13, &be_const_str_classof); +be_define_const_str(_subscribe, "_subscribe", 2888456770u, 0, 10, &be_const_str_detect); +be_define_const_str(_t, "_t", 1527481326u, 0, 2, &be_const_str_find_key_i); +be_define_const_str(_timers, "_timers", 2600100916u, 0, 7, &be_const_str_toupper); +be_define_const_str(_unsubscribe, "_unsubscribe", 2110993883u, 0, 12, &be_const_str_sat); +be_define_const_str(_validate, "_validate", 1742604448u, 0, 9, &be_const_str_ct); +be_define_const_str(_write, "_write", 2215462825u, 0, 6, &be_const_str_acos); +be_define_const_str(a, "a", 3826002220u, 0, 1, NULL); +be_define_const_str(abs, "abs", 709362235u, 0, 3, NULL); +be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_maxota); +be_define_const_str(active_otadata, "active_otadata", 3055353486u, 0, 14, &be_const_str_set_chg_current); +be_define_const_str(adc_config, "adc_config", 2769927572u, 0, 10, &be_const_str_int); +be_define_const_str(add, "add", 993596020u, 0, 3, &be_const_str_set_first_time); +be_define_const_str(add_anim, "add_anim", 3980662668u, 0, 8, &be_const_str_delete_all_configs); +be_define_const_str(add_cb_event_closure, "add_cb_event_closure", 1775958321u, 0, 20, &be_const_str_list_handlers); +be_define_const_str(add_cmd, "add_cmd", 3361630879u, 0, 7, &be_const_str_tolower); +be_define_const_str(add_cron, "add_cron", 2475327477u, 0, 8, &be_const_str_return); +be_define_const_str(add_driver, "add_driver", 1654458371u, 0, 10, &be_const_str_autoexec); +be_define_const_str(add_fast_loop, "add_fast_loop", 3025842946u, 0, 13, &be_const_str_item); +be_define_const_str(add_handler, "add_handler", 2055124119u, 0, 11, &be_const_str_arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj); +be_define_const_str(add_header, "add_header", 927130612u, 0, 10, &be_const_str_app); be_define_const_str(add_light, "add_light", 3169328603u, 0, 9, NULL); -be_define_const_str(add_rule, "add_rule", 596540743u, 0, 8, NULL); -be_define_const_str(addr, "addr", 1087856498u, 0, 4, &be_const_str_list_handlers); -be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_lv_obj); -be_define_const_str(alternate, "alternate", 1140253277u, 0, 9, &be_const_str_esphttpd); -be_define_const_str(animate, "animate", 3885786800u, 0, 7, &be_const_str_tr); -be_define_const_str(animators, "animators", 279858213u, 0, 9, &be_const_str_fromb64); -be_define_const_str(app, "app", 527074092u, 0, 3, &be_const_str_state); -be_define_const_str(arch, "arch", 2952804297u, 0, 4, &be_const_str_save); -be_define_const_str(arg, "arg", 1047474471u, 0, 3, NULL); -be_define_const_str(arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj, "arg must be a subclass of lv_obj", 1641882079u, 0, 32, &be_const_str_fast_loop); -be_define_const_str(arg_name, "arg_name", 1345046155u, 0, 8, &be_const_str_energy_struct); -be_define_const_str(arg_size, "arg_size", 3310243257u, 0, 8, NULL); -be_define_const_str(argument_X20must_X20be_X20a_X20function, "argument must be a function", 527172389u, 0, 27, &be_const_str_false); -be_define_const_str(argument_X20must_X20be_X20a_X20list, "argument must be a list", 3056915661u, 0, 23, &be_const_str_bus); -be_define_const_str(argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, "argument must be a list or a pointer+size", 241605448u, 0, 41, &be_const_str_exec_rules); -be_define_const_str(as, "as", 1579491469u, 67, 2, &be_const_str_fat); -be_define_const_str(asin, "asin", 4272848550u, 0, 4, NULL); +be_define_const_str(add_rule, "add_rule", 596540743u, 0, 8, &be_const_str_animators); +be_define_const_str(addr, "addr", 1087856498u, 0, 4, NULL); +be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_cb); +be_define_const_str(alternate, "alternate", 1140253277u, 0, 9, &be_const_str_autorun); +be_define_const_str(animate, "animate", 3885786800u, 0, 7, &be_const_str_code); +be_define_const_str(animators, "animators", 279858213u, 0, 9, &be_const_str_ctypes_bytes); +be_define_const_str(app, "app", 527074092u, 0, 3, &be_const_str_def); +be_define_const_str(arch, "arch", 2952804297u, 0, 4, &be_const_str_b); +be_define_const_str(arg, "arg", 1047474471u, 0, 3, &be_const_str_response_append); +be_define_const_str(arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj, "arg must be a subclass of lv_obj", 1641882079u, 0, 32, NULL); +be_define_const_str(arg_name, "arg_name", 1345046155u, 0, 8, &be_const_str_every_second); +be_define_const_str(arg_size, "arg_size", 3310243257u, 0, 8, &be_const_str_flush); +be_define_const_str(argument_X20must_X20be_X20a_X20function, "argument must be a function", 527172389u, 0, 27, NULL); +be_define_const_str(argument_X20must_X20be_X20a_X20list, "argument must be a list", 3056915661u, 0, 23, &be_const_str_subtype); +be_define_const_str(argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, "argument must be a list or a pointer+size", 241605448u, 0, 41, &be_const_str_value); +be_define_const_str(as, "as", 1579491469u, 67, 2, &be_const_str_get_string); +be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_decompress); be_define_const_str(assert, "assert", 2774883451u, 0, 6, NULL); -be_define_const_str(assign_rmt, "assign_rmt", 1047642576u, 0, 10, &be_const_str_fromstring); -be_define_const_str(asstring, "asstring", 1298225088u, 0, 8, &be_const_str_connected); -be_define_const_str(atan, "atan", 108579519u, 0, 4, &be_const_str_success); -be_define_const_str(atan2, "atan2", 3173440503u, 0, 5, &be_const_str_exec_tele); -be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, &be_const_str_scan); -be_define_const_str(autoexec, "autoexec", 3676861891u, 0, 8, NULL); -be_define_const_str(autorun, "autorun", 1447527407u, 0, 7, NULL); -be_define_const_str(available, "available", 1727918744u, 0, 9, &be_const_str_can_show); -be_define_const_str(b, "b", 3876335077u, 0, 1, &be_const_str_connection_error); +be_define_const_str(assign_rmt, "assign_rmt", 1047642576u, 0, 10, &be_const_str_try); +be_define_const_str(asstring, "asstring", 1298225088u, 0, 8, &be_const_str_run_bat); +be_define_const_str(atan, "atan", 108579519u, 0, 4, NULL); +be_define_const_str(atan2, "atan2", 3173440503u, 0, 5, NULL); +be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, NULL); +be_define_const_str(autoexec, "autoexec", 3676861891u, 0, 8, &be_const_str_subtype_to_string); +be_define_const_str(autorun, "autorun", 1447527407u, 0, 7, &be_const_str_concat); +be_define_const_str(available, "available", 1727918744u, 0, 9, NULL); +be_define_const_str(b, "b", 3876335077u, 0, 1, &be_const_str_month); be_define_const_str(back_forth, "back_forth", 2665042062u, 0, 10, &be_const_str_pin_used); -be_define_const_str(base_class, "base_class", 1107737279u, 0, 10, &be_const_str_hue_status); -be_define_const_str(battery_present, "battery_present", 3588397058u, 0, 15, &be_const_str_cmd); -be_define_const_str(before_del, "before_del", 815924436u, 0, 10, &be_const_str_counters); -be_define_const_str(begin, "begin", 1748273790u, 0, 5, &be_const_str_set_ldo_voltage); -be_define_const_str(begin_multicast, "begin_multicast", 57647915u, 0, 15, &be_const_str_out_X20of_X20range); -be_define_const_str(bool, "bool", 3365180733u, 0, 4, &be_const_str_content_flush); -be_define_const_str(break, "break", 3378807160u, 58, 5, &be_const_str_url_encode); -be_define_const_str(bri, "bri", 2112284244u, 0, 3, &be_const_str__X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D); -be_define_const_str(bus, "bus", 1607822841u, 0, 3, &be_const_str_clear_to); -be_define_const_str(button_pressed, "button_pressed", 1694209616u, 0, 14, &be_const_str_get_bat_charge_current); -be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_is_factory); -be_define_const_str(bytes, "bytes", 1706151940u, 0, 5, &be_const_str_crc); -be_define_const_str(c, "c", 3859557458u, 0, 1, &be_const_str_cb_event_closure); -be_define_const_str(call, "call", 3018949801u, 0, 4, &be_const_str_widget_dtor_impl); -be_define_const_str(call_native, "call_native", 1389147405u, 0, 11, &be_const_str_geti); -be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, NULL); -be_define_const_str(can_show, "can_show", 960091187u, 0, 8, &be_const_str_page_autoconf_ctl); -be_define_const_str(cb, "cb", 1428787088u, 0, 2, &be_const_str_mqtt_data); -be_define_const_str(cb_do_nothing, "cb_do_nothing", 1488730702u, 0, 13, &be_const_str_get_light); -be_define_const_str(cb_event_closure, "cb_event_closure", 3828267325u, 0, 16, &be_const_str_group_def); -be_define_const_str(cb_obj, "cb_obj", 1195696482u, 0, 6, &be_const_str_set_dc_voltage); -be_define_const_str(ccronexpr, "ccronexpr", 258146169u, 0, 9, &be_const_str_cosh); -be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, &be_const_str_widget_editable); -be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_full_status); -be_define_const_str(chars_in_string, "chars_in_string", 3148785132u, 0, 15, &be_const_str_has_arg); -be_define_const_str(check_not_method, "check_not_method", 2597324607u, 0, 16, &be_const_str_static); -be_define_const_str(check_privileged_access, "check_privileged_access", 3692933968u, 0, 23, &be_const_str_io_error); -be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_end); -be_define_const_str(class_init_obj, "class_init_obj", 178410604u, 0, 14, &be_const_str_path); -be_define_const_str(classname, "classname", 1998589948u, 0, 9, &be_const_str_find_key_i); -be_define_const_str(classof, "classof", 1796577762u, 0, 7, &be_const_str_map); -be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_get_string); -be_define_const_str(clear_first_time, "clear_first_time", 632769909u, 0, 16, &be_const_str_set_alternate); -be_define_const_str(clear_to, "clear_to", 3528002130u, 0, 8, NULL); +be_define_const_str(base_class, "base_class", 1107737279u, 0, 10, &be_const_str_type_to_string); +be_define_const_str(battery_present, "battery_present", 3588397058u, 0, 15, &be_const_str_get_event_cb); +be_define_const_str(before_del, "before_del", 815924436u, 0, 10, &be_const_str_nvs); +be_define_const_str(begin, "begin", 1748273790u, 0, 5, &be_const_str_end); +be_define_const_str(begin_multicast, "begin_multicast", 57647915u, 0, 15, NULL); +be_define_const_str(bool, "bool", 3365180733u, 0, 4, NULL); +be_define_const_str(break, "break", 3378807160u, 58, 5, &be_const_str_nil); +be_define_const_str(bri, "bri", 2112284244u, 0, 3, NULL); +be_define_const_str(bus, "bus", 1607822841u, 0, 3, &be_const_str_set_ldo_enable); +be_define_const_str(button_pressed, "button_pressed", 1694209616u, 0, 14, &be_const_str_page_autoconf_ctl); +be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_pow); +be_define_const_str(bytes, "bytes", 1706151940u, 0, 5, &be_const_str_lv_solidified); +be_define_const_str(c, "c", 3859557458u, 0, 1, &be_const_str_get_current_module_path); +be_define_const_str(call, "call", 3018949801u, 0, 4, &be_const_str_get_vbus_voltage); +be_define_const_str(call_native, "call_native", 1389147405u, 0, 11, &be_const_str_serial); +be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, &be_const_str_persist_X2E_p_X20is_X20not_X20a_X20map); +be_define_const_str(can_show, "can_show", 960091187u, 0, 8, &be_const_str_get_input_power_status); +be_define_const_str(cb, "cb", 1428787088u, 0, 2, NULL); +be_define_const_str(cb_do_nothing, "cb_do_nothing", 1488730702u, 0, 13, &be_const_str_get_cb_list); +be_define_const_str(cb_event_closure, "cb_event_closure", 3828267325u, 0, 16, &be_const_str_lv_module_init); +be_define_const_str(cb_obj, "cb_obj", 1195696482u, 0, 6, &be_const_str_efuse_em); +be_define_const_str(ccronexpr, "ccronexpr", 258146169u, 0, 9, &be_const_str_clear_first_time); +be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, NULL); +be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_hue); +be_define_const_str(chars_in_string, "chars_in_string", 3148785132u, 0, 15, NULL); +be_define_const_str(check_not_method, "check_not_method", 2597324607u, 0, 16, &be_const_str_content_send_style); +be_define_const_str(check_privileged_access, "check_privileged_access", 3692933968u, 0, 23, &be_const_str_coord_arr); +be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_hs2rgb); +be_define_const_str(class_init_obj, "class_init_obj", 178410604u, 0, 14, &be_const_str_members); +be_define_const_str(classname, "classname", 1998589948u, 0, 9, NULL); +be_define_const_str(classof, "classof", 1796577762u, 0, 7, NULL); +be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_remove_light); +be_define_const_str(clear_first_time, "clear_first_time", 632769909u, 0, 16, NULL); +be_define_const_str(clear_to, "clear_to", 3528002130u, 0, 8, &be_const_str_lower); be_define_const_str(close, "close", 667630371u, 0, 5, NULL); -be_define_const_str(closure, "closure", 1548407746u, 0, 7, NULL); -be_define_const_str(cmd, "cmd", 4136785899u, 0, 3, &be_const_str_detected_X20on_X20bus); -be_define_const_str(cmd_res, "cmd_res", 921166762u, 0, 7, NULL); -be_define_const_str(code, "code", 4180765940u, 0, 4, &be_const_str_get_battery_chargin_status); -be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_yield); -be_define_const_str(collect, "collect", 2399039025u, 0, 7, &be_const_str_id_X20must_X20be_X20of_X20type_X20_X27int_X27); -be_define_const_str(compile, "compile", 1000265118u, 0, 7, &be_const_str_coord_arr); -be_define_const_str(compress, "compress", 2818084237u, 0, 8, NULL); -be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_memory); -be_define_const_str(connect, "connect", 2866859257u, 0, 7, &be_const_str_pixel_size); -be_define_const_str(connected, "connected", 1424938192u, 0, 9, &be_const_str_lv_module_init); -be_define_const_str(connection_error, "connection_error", 1358926260u, 0, 16, &be_const_str_remove_trailing_zeroes); -be_define_const_str(constructor_cb, "constructor_cb", 2489105297u, 0, 14, &be_const_str_wire_scan); -be_define_const_str(contains, "contains", 1825239352u, 0, 8, NULL); -be_define_const_str(content_button, "content_button", 1956476087u, 0, 14, &be_const_str_run_cron); -be_define_const_str(content_flush, "content_flush", 214922475u, 0, 13, NULL); -be_define_const_str(content_response, "content_response", 3881475860u, 0, 16, NULL); -be_define_const_str(content_send, "content_send", 1673733649u, 0, 12, NULL); -be_define_const_str(content_send_style, "content_send_style", 1087907647u, 0, 18, &be_const_str_pop_path); -be_define_const_str(content_start, "content_start", 2937509069u, 0, 13, &be_const_str_test); -be_define_const_str(content_stop, "content_stop", 658554751u, 0, 12, &be_const_str_get_current_module_path); -be_define_const_str(continue, "continue", 2977070660u, 59, 8, &be_const_str_pc_abs); -be_define_const_str(coord_arr, "coord_arr", 4189963658u, 0, 9, &be_const_str_listdir); -be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_lv_event_cb); -be_define_const_str(coredump, "coredump", 2141225116u, 0, 8, &be_const_str_elements_X20must_X20be_X20a_X20lv_point); -be_define_const_str(cos, "cos", 4220379804u, 0, 3, &be_const_str_gamma10); -be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, NULL); -be_define_const_str(couldn_X27t_X20not_X20initialize_X20noepixelbus, "couldn't not initialize noepixelbus", 2536490812u, 0, 35, NULL); -be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_srand); -be_define_const_str(counters, "counters", 4095866864u, 0, 8, &be_const_str_mqtt); -be_define_const_str(crc, "crc", 3812935353u, 0, 3, NULL); -be_define_const_str(crc16, "crc16", 3504496746u, 0, 5, NULL); +be_define_const_str(closure, "closure", 1548407746u, 0, 7, &be_const_str_scan); +be_define_const_str(cmd, "cmd", 4136785899u, 0, 3, &be_const_str_event); +be_define_const_str(cmd_res, "cmd_res", 921166762u, 0, 7, &be_const_str_exec_rules); +be_define_const_str(code, "code", 4180765940u, 0, 4, &be_const_str_exp); +be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_missing_X20name); +be_define_const_str(collect, "collect", 2399039025u, 0, 7, &be_const_str_init); +be_define_const_str(compile, "compile", 1000265118u, 0, 7, &be_const_str_devices); +be_define_const_str(compress, "compress", 2818084237u, 0, 8, &be_const_str_flash); +be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_light_to_id); +be_define_const_str(connect, "connect", 2866859257u, 0, 7, &be_const_str_get_image_size); +be_define_const_str(connected, "connected", 1424938192u, 0, 9, &be_const_str_deinit); +be_define_const_str(connection_error, "connection_error", 1358926260u, 0, 16, &be_const_str_read32); +be_define_const_str(constructor_cb, "constructor_cb", 2489105297u, 0, 14, &be_const_str_crc32_ota_seq); +be_define_const_str(contains, "contains", 1825239352u, 0, 8, &be_const_str_do); +be_define_const_str(content_button, "content_button", 1956476087u, 0, 14, &be_const_str_upper); +be_define_const_str(content_flush, "content_flush", 214922475u, 0, 13, &be_const_str_run_cron); +be_define_const_str(content_response, "content_response", 3881475860u, 0, 16, &be_const_str_set_auth); +be_define_const_str(content_send, "content_send", 1673733649u, 0, 12, &be_const_str_widget_instance_size); +be_define_const_str(content_send_style, "content_send_style", 1087907647u, 0, 18, &be_const_str_solidified); +be_define_const_str(content_start, "content_start", 2937509069u, 0, 13, NULL); +be_define_const_str(content_stop, "content_stop", 658554751u, 0, 12, &be_const_str_partition_core); +be_define_const_str(continue, "continue", 2977070660u, 59, 8, &be_const_str_lv_point); +be_define_const_str(coord_arr, "coord_arr", 4189963658u, 0, 9, &be_const_str_pop_path); +be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_lv_obj_class); +be_define_const_str(coredump, "coredump", 2141225116u, 0, 8, NULL); +be_define_const_str(cos, "cos", 4220379804u, 0, 3, NULL); +be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, &be_const_str_elements_X20must_X20be_X20a_X20lv_point); +be_define_const_str(couldn_X27t_X20not_X20initialize_X20noepixelbus, "couldn't not initialize noepixelbus", 2536490812u, 0, 35, &be_const_str_get_active); +be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_isnan); +be_define_const_str(counters, "counters", 4095866864u, 0, 8, &be_const_str_fromstring); +be_define_const_str(crc, "crc", 3812935353u, 0, 3, &be_const_str_strip); +be_define_const_str(crc16, "crc16", 3504496746u, 0, 5, &be_const_str_files); be_define_const_str(crc32, "crc32", 3571901412u, 0, 5, NULL); -be_define_const_str(crc32_ota_seq, "crc32_ota_seq", 172417u, 0, 13, &be_const_str_tanh); -be_define_const_str(crc8, "crc8", 1178893587u, 0, 4, &be_const_str_rtc); -be_define_const_str(create_custom_widget, "create_custom_widget", 1140594778u, 0, 20, &be_const_str_pop); +be_define_const_str(crc32_ota_seq, "crc32_ota_seq", 172417u, 0, 13, NULL); +be_define_const_str(crc8, "crc8", 1178893587u, 0, 4, &be_const_str_display); +be_define_const_str(create_custom_widget, "create_custom_widget", 1140594778u, 0, 20, NULL); be_define_const_str(create_matrix, "create_matrix", 3528185923u, 0, 13, NULL); -be_define_const_str(create_segment, "create_segment", 3863522719u, 0, 14, NULL); +be_define_const_str(create_segment, "create_segment", 3863522719u, 0, 14, &be_const_str_erase); be_define_const_str(ct, "ct", 1261010898u, 0, 2, NULL); -be_define_const_str(ctor, "ctor", 375399343u, 0, 4, &be_const_str_efuse_em); -be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, NULL); -be_define_const_str(ctypes_bytes_dyn, "ctypes_bytes_dyn", 915205307u, 0, 16, &be_const_str_exists); -be_define_const_str(dac_voltage, "dac_voltage", 1552257222u, 0, 11, &be_const_str_discover); -be_define_const_str(data, "data", 3631407781u, 0, 4, &be_const_str_remove_driver); -be_define_const_str(day, "day", 3830391293u, 0, 3, &be_const_str_list); -be_define_const_str(debug, "debug", 1483009432u, 0, 5, &be_const_str_rotate); -be_define_const_str(decompress, "decompress", 2887031650u, 0, 10, &be_const_str_get_input_power_status); -be_define_const_str(def, "def", 3310976652u, 55, 3, &be_const_str_web_add_button); -be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_every_50ms); -be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, NULL); -be_define_const_str(delay, "delay", 1322381784u, 0, 5, &be_const_str_internal_error); -be_define_const_str(delete_all_configs, "delete_all_configs", 2382067578u, 0, 18, &be_const_str__X7B); -be_define_const_str(deregister_obj, "deregister_obj", 3909966993u, 0, 14, NULL); -be_define_const_str(destructor_cb, "destructor_cb", 1930283190u, 0, 13, &be_const_str_publish_rule); -be_define_const_str(detect, "detect", 8884370u, 0, 6, NULL); -be_define_const_str(detected_X20on_X20bus, "detected on bus", 1432002650u, 0, 15, &be_const_str_get_bat_voltage); -be_define_const_str(devices, "devices", 2701822848u, 0, 7, &be_const_str_stop_iteration); -be_define_const_str(digital_read, "digital_read", 3585496928u, 0, 12, &be_const_str_function); -be_define_const_str(digital_write, "digital_write", 3435877979u, 0, 13, &be_const_str_global); -be_define_const_str(dirty, "dirty", 2667581083u, 0, 5, &be_const_str_lights); -be_define_const_str(discover, "discover", 1383599054u, 0, 8, &be_const_str_setmember); -be_define_const_str(display, "display", 1164572437u, 0, 7, NULL); -be_define_const_str(display_X2Eini, "display.ini", 2646174001u, 0, 11, &be_const_str_lv_); -be_define_const_str(do, "do", 1646057492u, 65, 2, &be_const_str_serial); -be_define_const_str(dump, "dump", 3663001223u, 0, 4, &be_const_str_model); -be_define_const_str(duration, "duration", 799079693u, 0, 8, &be_const_str_hs2rgb); -be_define_const_str(editable, "editable", 60532369u, 0, 8, NULL); -be_define_const_str(efuse_em, "efuse_em", 1643301972u, 0, 8, NULL); -be_define_const_str(elements_X20must_X20be_X20a_X20lv_point, "elements must be a lv_point", 1415796524u, 0, 27, &be_const_str_gamma); -be_define_const_str(elif, "elif", 3232090307u, 51, 4, &be_const_str_fromhex); -be_define_const_str(else, "else", 3183434736u, 52, 4, &be_const_str_number); -be_define_const_str(enabled, "enabled", 49525662u, 0, 7, NULL); -be_define_const_str(end, "end", 1787721130u, 56, 3, &be_const_str_nvskeys); -be_define_const_str(energy_struct, "energy_struct", 1655792843u, 0, 13, &be_const_str_tasmota); -be_define_const_str(engine, "engine", 3993360443u, 0, 6, &be_const_str_ota_max); -be_define_const_str(erase, "erase", 1010949589u, 0, 5, &be_const_str_strftime); -be_define_const_str(escape, "escape", 2652972038u, 0, 6, NULL); -be_define_const_str(esphttpd, "esphttpd", 2255925709u, 0, 8, &be_const_str_lv_obj_class); +be_define_const_str(ctor, "ctor", 375399343u, 0, 4, &be_const_str_day); +be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, &be_const_str_get_option); +be_define_const_str(ctypes_bytes_dyn, "ctypes_bytes_dyn", 915205307u, 0, 16, &be_const_str_get_power); +be_define_const_str(dac_voltage, "dac_voltage", 1552257222u, 0, 11, &be_const_str_hue_ntv); +be_define_const_str(data, "data", 3631407781u, 0, 4, &be_const_str_return_X20code_X3D_X25i); +be_define_const_str(day, "day", 3830391293u, 0, 3, NULL); +be_define_const_str(debug, "debug", 1483009432u, 0, 5, &be_const_str_set_active); +be_define_const_str(decompress, "decompress", 2887031650u, 0, 10, &be_const_str_ismethod); +be_define_const_str(def, "def", 3310976652u, 55, 3, NULL); +be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_start); +be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_get_vbus_current); +be_define_const_str(delay, "delay", 1322381784u, 0, 5, NULL); +be_define_const_str(delete_all_configs, "delete_all_configs", 2382067578u, 0, 18, &be_const_str_no_X20GPIO_X20specified_X20for_X20neopixelbus); +be_define_const_str(deregister_obj, "deregister_obj", 3909966993u, 0, 14, &be_const_str_dump); +be_define_const_str(destructor_cb, "destructor_cb", 1930283190u, 0, 13, &be_const_str_signal_change); +be_define_const_str(detect, "detect", 8884370u, 0, 6, &be_const_str_running); +be_define_const_str(detected_X20on_X20bus, "detected on bus", 1432002650u, 0, 15, &be_const_str_full_state); +be_define_const_str(devices, "devices", 2701822848u, 0, 7, &be_const_str_digital_read); +be_define_const_str(digital_read, "digital_read", 3585496928u, 0, 12, &be_const_str_finish); +be_define_const_str(digital_write, "digital_write", 3435877979u, 0, 13, &be_const_str_trig); +be_define_const_str(dirty, "dirty", 2667581083u, 0, 5, &be_const_str_ins_goto); +be_define_const_str(discover, "discover", 1383599054u, 0, 8, &be_const_str_every_100ms); +be_define_const_str(display, "display", 1164572437u, 0, 7, &be_const_str_mqtt); +be_define_const_str(display_X2Eini, "display.ini", 2646174001u, 0, 11, &be_const_str_get_object_from_ptr); +be_define_const_str(do, "do", 1646057492u, 65, 2, NULL); +be_define_const_str(dump, "dump", 3663001223u, 0, 4, NULL); +be_define_const_str(duration, "duration", 799079693u, 0, 8, NULL); +be_define_const_str(editable, "editable", 60532369u, 0, 8, &be_const_str_lv_style_prop_arr); +be_define_const_str(efuse_em, "efuse_em", 1643301972u, 0, 8, &be_const_str_tanh); +be_define_const_str(elements_X20must_X20be_X20a_X20lv_point, "elements must be a lv_point", 1415796524u, 0, 27, NULL); +be_define_const_str(elif, "elif", 3232090307u, 51, 4, &be_const_str_setfloat); +be_define_const_str(else, "else", 3183434736u, 52, 4, &be_const_str_set_dc_voltage); +be_define_const_str(enabled, "enabled", 49525662u, 0, 7, &be_const_str_save); +be_define_const_str(end, "end", 1787721130u, 56, 3, &be_const_str_listdir); +be_define_const_str(energy_struct, "energy_struct", 1655792843u, 0, 13, &be_const_str_millis); +be_define_const_str(engine, "engine", 3993360443u, 0, 6, NULL); +be_define_const_str(erase, "erase", 1010949589u, 0, 5, &be_const_str_lv_obj); +be_define_const_str(escape, "escape", 2652972038u, 0, 6, &be_const_str_web_add_button); +be_define_const_str(esphttpd, "esphttpd", 2255925709u, 0, 8, NULL); be_define_const_str(eth, "eth", 2191266556u, 0, 3, NULL); -be_define_const_str(event, "event", 4264611999u, 0, 5, &be_const_str_xy); -be_define_const_str(event_cb, "event_cb", 3128698017u, 0, 8, &be_const_str_month); -be_define_const_str(every_100ms, "every_100ms", 1546407804u, 0, 11, &be_const_str_point_arr); -be_define_const_str(every_250ms, "every_250ms", 2579240000u, 0, 11, NULL); -be_define_const_str(every_50ms, "every_50ms", 2383884008u, 0, 10, NULL); -be_define_const_str(every_second, "every_second", 2075451465u, 0, 12, &be_const_str_offset); +be_define_const_str(event, "event", 4264611999u, 0, 5, &be_const_str_fromb64); +be_define_const_str(event_cb, "event_cb", 3128698017u, 0, 8, NULL); +be_define_const_str(every_100ms, "every_100ms", 1546407804u, 0, 11, &be_const_str_widget_event_cb); +be_define_const_str(every_250ms, "every_250ms", 2579240000u, 0, 11, &be_const_str_json_fdump); +be_define_const_str(every_50ms, "every_50ms", 2383884008u, 0, 10, &be_const_str_invalidate_spiffs); +be_define_const_str(every_second, "every_second", 2075451465u, 0, 12, &be_const_str_set_mode_rgb); be_define_const_str(except, "except", 950914032u, 69, 6, NULL); -be_define_const_str(exec_cmd, "exec_cmd", 493567399u, 0, 8, &be_const_str_strip); -be_define_const_str(exec_rules, "exec_rules", 1445221092u, 0, 10, NULL); -be_define_const_str(exec_tele, "exec_tele", 1020751601u, 0, 9, &be_const_str_is_running); -be_define_const_str(exists, "exists", 1002329533u, 0, 6, NULL); -be_define_const_str(exp, "exp", 1923516200u, 0, 3, NULL); -be_define_const_str(f, "f", 3809224601u, 0, 1, NULL); +be_define_const_str(exec_cmd, "exec_cmd", 493567399u, 0, 8, &be_const_str_persist); +be_define_const_str(exec_rules, "exec_rules", 1445221092u, 0, 10, &be_const_str_fat); +be_define_const_str(exec_tele, "exec_tele", 1020751601u, 0, 9, &be_const_str__X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D); +be_define_const_str(exists, "exists", 1002329533u, 0, 6, &be_const_str_type); +be_define_const_str(exp, "exp", 1923516200u, 0, 3, &be_const_str_lights); +be_define_const_str(f, "f", 3809224601u, 0, 1, &be_const_str_sin); be_define_const_str(factory, "factory", 2510088205u, 0, 7, NULL); -be_define_const_str(false, "false", 184981848u, 62, 5, NULL); -be_define_const_str(fast_loop, "fast_loop", 3414422702u, 0, 9, &be_const_str_fast_loop_enabled); -be_define_const_str(fast_loop_enabled, "fast_loop_enabled", 2567964376u, 0, 17, &be_const_str_set_chg_current); -be_define_const_str(fat, "fat", 3203931412u, 0, 3, NULL); -be_define_const_str(file, "file", 2867484483u, 0, 4, &be_const_str_try_rule); -be_define_const_str(file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27, "file extension is not '.be' or '.bec'", 3095719639u, 0, 37, &be_const_str_sin); -be_define_const_str(files, "files", 1055342736u, 0, 5, &be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson); -be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_for); +be_define_const_str(false, "false", 184981848u, 62, 5, &be_const_str_length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032); +be_define_const_str(fast_loop, "fast_loop", 3414422702u, 0, 9, NULL); +be_define_const_str(fast_loop_enabled, "fast_loop_enabled", 2567964376u, 0, 17, &be_const_str_print); +be_define_const_str(fat, "fat", 3203931412u, 0, 3, &be_const_str_lv_); +be_define_const_str(file, "file", 2867484483u, 0, 4, NULL); +be_define_const_str(file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27, "file extension is not '.be' or '.bec'", 3095719639u, 0, 37, NULL); +be_define_const_str(files, "files", 1055342736u, 0, 5, &be_const_str_pc_rel); +be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_mqtt_listener); be_define_const_str(find_key_i, "find_key_i", 850136726u, 0, 10, NULL); -be_define_const_str(find_op, "find_op", 3766713376u, 0, 7, &be_const_str_get_vbus_voltage); -be_define_const_str(finish, "finish", 1494643858u, 0, 6, &be_const_str_get_vbus_current); -be_define_const_str(flags, "flags", 2624027180u, 0, 5, &be_const_str_instance); -be_define_const_str(flash, "flash", 2944773417u, 0, 5, NULL); -be_define_const_str(floor, "floor", 3102149661u, 0, 5, NULL); -be_define_const_str(flush, "flush", 3002334877u, 0, 5, NULL); +be_define_const_str(find_op, "find_op", 3766713376u, 0, 7, &be_const_str_try_remove_file); +be_define_const_str(finish, "finish", 1494643858u, 0, 6, &be_const_str_lv_extra); +be_define_const_str(flags, "flags", 2624027180u, 0, 5, &be_const_str_hex); +be_define_const_str(flash, "flash", 2944773417u, 0, 5, &be_const_str_remote_port); +be_define_const_str(floor, "floor", 3102149661u, 0, 5, &be_const_str_setmember); +be_define_const_str(flush, "flush", 3002334877u, 0, 5, &be_const_str_stop); be_define_const_str(for, "for", 2901640080u, 54, 3, NULL); -be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_write); +be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_widget_struct_by_class); be_define_const_str(from_to, "from_to", 21625507u, 0, 7, NULL); be_define_const_str(fromb64, "fromb64", 2717019639u, 0, 7, NULL); be_define_const_str(frombytes, "frombytes", 3771700788u, 0, 9, NULL); be_define_const_str(fromhex, "fromhex", 1847150394u, 0, 7, NULL); -be_define_const_str(fromptr, "fromptr", 666189689u, 0, 7, &be_const_str_topic); -be_define_const_str(fromstring, "fromstring", 610302344u, 0, 10, &be_const_str_show); -be_define_const_str(full_state, "full_state", 255687770u, 0, 10, NULL); +be_define_const_str(fromptr, "fromptr", 666189689u, 0, 7, &be_const_str_input); +be_define_const_str(fromstring, "fromstring", 610302344u, 0, 10, &be_const_str_mqtt_data); +be_define_const_str(full_state, "full_state", 255687770u, 0, 10, &be_const_str_ins_ramp); be_define_const_str(full_status, "full_status", 648242459u, 0, 11, NULL); -be_define_const_str(fulltopic, "fulltopic", 3636750803u, 0, 9, &be_const_str_set_timer); -be_define_const_str(function, "function", 2664841801u, 0, 8, &be_const_str_remove_light); -be_define_const_str(gamma, "gamma", 3492353034u, 0, 5, &be_const_str_push); -be_define_const_str(gamma10, "gamma10", 3472052483u, 0, 7, &be_const_str_set_light); +be_define_const_str(fulltopic, "fulltopic", 3636750803u, 0, 9, &be_const_str_load_templates); +be_define_const_str(function, "function", 2664841801u, 0, 8, NULL); +be_define_const_str(gamma, "gamma", 3492353034u, 0, 5, &be_const_str_size); +be_define_const_str(gamma10, "gamma10", 3472052483u, 0, 7, &be_const_str_widget_struct_default); be_define_const_str(gamma8, "gamma8", 3802843830u, 0, 6, NULL); -be_define_const_str(gc, "gc", 1042313471u, 0, 2, NULL); -be_define_const_str(gen_cb, "gen_cb", 3245227551u, 0, 6, &be_const_str_run_bat); -be_define_const_str(get, "get", 1410115415u, 0, 3, &be_const_str_power_off); -be_define_const_str(get_MAC, "get_MAC", 2091521771u, 0, 7, &be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E); -be_define_const_str(get_active, "get_active", 3504842642u, 0, 10, &be_const_str_instance_size); -be_define_const_str(get_alternate, "get_alternate", 1450148894u, 0, 13, &be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E); -be_define_const_str(get_aps_voltage, "get_aps_voltage", 2293036435u, 0, 15, &be_const_str_is_first_time); -be_define_const_str(get_bat_charge_current, "get_bat_charge_current", 1385293050u, 0, 22, &be_const_str_str); -be_define_const_str(get_bat_current, "get_bat_current", 1912106073u, 0, 15, NULL); +be_define_const_str(gc, "gc", 1042313471u, 0, 2, &be_const_str_gen_cb); +be_define_const_str(gen_cb, "gen_cb", 3245227551u, 0, 6, &be_const_str_lv_coord_arr); +be_define_const_str(get, "get", 1410115415u, 0, 3, &be_const_str_module); +be_define_const_str(get_MAC, "get_MAC", 2091521771u, 0, 7, &be_const_str_is_spiffs); +be_define_const_str(get_active, "get_active", 3504842642u, 0, 10, &be_const_str_widget_width_def); +be_define_const_str(get_alternate, "get_alternate", 1450148894u, 0, 13, NULL); +be_define_const_str(get_aps_voltage, "get_aps_voltage", 2293036435u, 0, 15, &be_const_str_min); +be_define_const_str(get_bat_charge_current, "get_bat_charge_current", 1385293050u, 0, 22, &be_const_str_introspect); +be_define_const_str(get_bat_current, "get_bat_current", 1912106073u, 0, 15, &be_const_str_label); be_define_const_str(get_bat_power, "get_bat_power", 3067374853u, 0, 13, NULL); -be_define_const_str(get_bat_voltage, "get_bat_voltage", 706676538u, 0, 15, &be_const_str_h); -be_define_const_str(get_battery_chargin_status, "get_battery_chargin_status", 2233241571u, 0, 26, NULL); -be_define_const_str(get_bri, "get_bri", 2041809895u, 0, 7, NULL); -be_define_const_str(get_cb_list, "get_cb_list", 1605319182u, 0, 11, &be_const_str_set_mode_ct); -be_define_const_str(get_current_module_name, "get_current_module_name", 2379270740u, 0, 23, &be_const_str_iter); -be_define_const_str(get_current_module_path, "get_current_module_path", 3206673408u, 0, 23, NULL); +be_define_const_str(get_bat_voltage, "get_bat_voltage", 706676538u, 0, 15, &be_const_str_wd); +be_define_const_str(get_battery_chargin_status, "get_battery_chargin_status", 2233241571u, 0, 26, &be_const_str_h); +be_define_const_str(get_bri, "get_bri", 2041809895u, 0, 7, &be_const_str__X7Bs_X7DVBus_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); +be_define_const_str(get_cb_list, "get_cb_list", 1605319182u, 0, 11, &be_const_str_power_off); +be_define_const_str(get_current_module_name, "get_current_module_name", 2379270740u, 0, 23, &be_const_str_read12); +be_define_const_str(get_current_module_path, "get_current_module_path", 3206673408u, 0, 23, &be_const_str_invalid_X20GPIO_X20number); be_define_const_str(get_event_cb, "get_event_cb", 375876088u, 0, 12, NULL); -be_define_const_str(get_factory_slot, "get_factory_slot", 3086140407u, 0, 16, &be_const_str_ismethod); -be_define_const_str(get_free_heap, "get_free_heap", 625069757u, 0, 13, &be_const_str_is_dirty); -be_define_const_str(get_image_size, "get_image_size", 4009859887u, 0, 14, &be_const_str_reset); -be_define_const_str(get_input_power_status, "get_input_power_status", 4102829177u, 0, 22, &be_const_str_nil); -be_define_const_str(get_light, "get_light", 381930476u, 0, 9, NULL); +be_define_const_str(get_factory_slot, "get_factory_slot", 3086140407u, 0, 16, NULL); +be_define_const_str(get_free_heap, "get_free_heap", 625069757u, 0, 13, NULL); +be_define_const_str(get_image_size, "get_image_size", 4009859887u, 0, 14, &be_const_str_out_X20of_X20range); +be_define_const_str(get_input_power_status, "get_input_power_status", 4102829177u, 0, 22, &be_const_str_gpio_init); +be_define_const_str(get_light, "get_light", 381930476u, 0, 9, &be_const_str_setitem); be_define_const_str(get_log, "get_log", 3524441898u, 0, 7, &be_const_str_get_switches); +be_define_const_str(get_mem, "get_mem", 1482912361u, 0, 7, &be_const_str_read_sensors); be_define_const_str(get_name, "get_name", 1616902907u, 0, 8, NULL); -be_define_const_str(get_object_from_ptr, "get_object_from_ptr", 2345019201u, 0, 19, NULL); -be_define_const_str(get_option, "get_option", 2123730033u, 0, 10, &be_const_str_init); -be_define_const_str(get_ota_slot, "get_ota_slot", 2686180151u, 0, 12, &be_const_str_get_temp); -be_define_const_str(get_pixel_color, "get_pixel_color", 337490048u, 0, 15, &be_const_str_is_spiffs); -be_define_const_str(get_power, "get_power", 3009799377u, 0, 9, &be_const_str_imax); -be_define_const_str(get_size, "get_size", 2803644713u, 0, 8, &be_const_str_name); -be_define_const_str(get_string, "get_string", 4195847969u, 0, 10, &be_const_str_open); -be_define_const_str(get_switch, "get_switch", 164821028u, 0, 10, &be_const_str_no_X20more_X20RMT_X20channel_X20available); +be_define_const_str(get_object_from_ptr, "get_object_from_ptr", 2345019201u, 0, 19, &be_const_str_tele); +be_define_const_str(get_option, "get_option", 2123730033u, 0, 10, NULL); +be_define_const_str(get_ota_slot, "get_ota_slot", 2686180151u, 0, 12, &be_const_str_read24); +be_define_const_str(get_pixel_color, "get_pixel_color", 337490048u, 0, 15, &be_const_str_offset); +be_define_const_str(get_power, "get_power", 3009799377u, 0, 9, NULL); +be_define_const_str(get_size, "get_size", 2803644713u, 0, 8, &be_const_str_ptr); +be_define_const_str(get_string, "get_string", 4195847969u, 0, 10, &be_const_str_remove); +be_define_const_str(get_switch, "get_switch", 164821028u, 0, 10, NULL); be_define_const_str(get_switches, "get_switches", 4116216928u, 0, 12, NULL); -be_define_const_str(get_temp, "get_temp", 3370919486u, 0, 8, &be_const_str_widget_event_cb); -be_define_const_str(get_vbus_current, "get_vbus_current", 1205347942u, 0, 16, NULL); -be_define_const_str(get_vbus_voltage, "get_vbus_voltage", 2398210401u, 0, 16, NULL); -be_define_const_str(get_warning_level, "get_warning_level", 1737834441u, 0, 17, &be_const_str_nvs); -be_define_const_str(getbits, "getbits", 3094168979u, 0, 7, &be_const_str_if); -be_define_const_str(getfloat, "getfloat", 2820979603u, 0, 8, &be_const_str_persist_X2E_p_X20is_X20not_X20a_X20map); -be_define_const_str(geti, "geti", 2381006490u, 0, 4, NULL); -be_define_const_str(global, "global", 503252654u, 0, 6, NULL); -be_define_const_str(gpio, "gpio", 2638155258u, 0, 4, NULL); -be_define_const_str(group_def, "group_def", 1524213328u, 0, 9, &be_const_str_push_path); +be_define_const_str(get_temp, "get_temp", 3370919486u, 0, 8, &be_const_str_tomap); +be_define_const_str(get_vbus_current, "get_vbus_current", 1205347942u, 0, 16, &be_const_str_tasmota); +be_define_const_str(get_vbus_voltage, "get_vbus_voltage", 2398210401u, 0, 16, &be_const_str_real); +be_define_const_str(get_warning_level, "get_warning_level", 1737834441u, 0, 17, &be_const_str_redirect); +be_define_const_str(getbits, "getbits", 3094168979u, 0, 7, &be_const_str_the_X20second_X20argument_X20is_X20not_X20a_X20function); +be_define_const_str(getfloat, "getfloat", 2820979603u, 0, 8, NULL); +be_define_const_str(geti, "geti", 2381006490u, 0, 4, &be_const_str_ismapped); +be_define_const_str(global, "global", 503252654u, 0, 6, &be_const_str_matrix); +be_define_const_str(gpio, "gpio", 2638155258u, 0, 4, &be_const_str_lv_point_arr); +be_define_const_str(gpio_init, "gpio_init", 4229187257u, 0, 9, NULL); +be_define_const_str(group_def, "group_def", 1524213328u, 0, 9, &be_const_str_instance_size); be_define_const_str(groups, "groups", 2943077229u, 0, 6, NULL); be_define_const_str(h, "h", 3977000791u, 0, 1, NULL); -be_define_const_str(has, "has", 3988721635u, 0, 3, &be_const_str_read); -be_define_const_str(has_arg, "has_arg", 424878688u, 0, 7, NULL); +be_define_const_str(has, "has", 3988721635u, 0, 3, &be_const_str_light); +be_define_const_str(has_arg, "has_arg", 424878688u, 0, 7, &be_const_str_instance); be_define_const_str(has_factory, "has_factory", 2702829042u, 0, 11, NULL); -be_define_const_str(height_def, "height_def", 2348238838u, 0, 10, &be_const_str_hour); -be_define_const_str(hex, "hex", 4273249610u, 0, 3, &be_const_str_json_fdump); -be_define_const_str(hour, "hour", 3053661199u, 0, 4, NULL); -be_define_const_str(hs2rgb, "hs2rgb", 1040816349u, 0, 6, &be_const_str_pin_mode); -be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s/%s.autoconf", 2743526309u, 0, 70, &be_const_str_remove_timer); -be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s_manifest.json", 3657552045u, 0, 72, &be_const_str_settings); -be_define_const_str(hue, "hue", 3817694041u, 0, 3, NULL); -be_define_const_str(hue_ntv, "hue_ntv", 705068642u, 0, 7, NULL); -be_define_const_str(hue_status, "hue_status", 437978812u, 0, 10, &be_const_str_math); +be_define_const_str(height_def, "height_def", 2348238838u, 0, 10, NULL); +be_define_const_str(hex, "hex", 4273249610u, 0, 3, NULL); +be_define_const_str(hour, "hour", 3053661199u, 0, 4, &be_const_str_set_hue16sat); +be_define_const_str(hs2rgb, "hs2rgb", 1040816349u, 0, 6, NULL); +be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s/%s.autoconf", 2743526309u, 0, 70, &be_const_str_issubclass); +be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s_manifest.json", 3657552045u, 0, 72, &be_const_str_resolvecmnd); +be_define_const_str(hue, "hue", 3817694041u, 0, 3, &be_const_str_r); +be_define_const_str(hue_ntv, "hue_ntv", 705068642u, 0, 7, &be_const_str_set_matrix_pixel_color); +be_define_const_str(hue_status, "hue_status", 437978812u, 0, 10, &be_const_str_obj_class_create_obj); be_define_const_str(i2c_enabled, "i2c_enabled", 218388101u, 0, 11, NULL); -be_define_const_str(id, "id", 926444256u, 0, 2, NULL); -be_define_const_str(id_X20must_X20be_X20of_X20type_X20_X27int_X27, "id must be of type 'int'", 2097653458u, 0, 24, NULL); +be_define_const_str(id, "id", 926444256u, 0, 2, &be_const_str_readbytes); +be_define_const_str(id_X20must_X20be_X20of_X20type_X20_X27int_X27, "id must be of type 'int'", 2097653458u, 0, 24, &be_const_str_webserver); be_define_const_str(if, "if", 959999494u, 50, 2, NULL); be_define_const_str(imax, "imax", 3084515410u, 0, 4, NULL); -be_define_const_str(imin, "imin", 2714127864u, 0, 4, NULL); -be_define_const_str(import, "import", 288002260u, 66, 6, &be_const_str_zip); +be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_yield); +be_define_const_str(import, "import", 288002260u, 66, 6, NULL); be_define_const_str(init, "init", 380752755u, 0, 4, NULL); be_define_const_str(input, "input", 4191711099u, 0, 5, NULL); -be_define_const_str(ins_goto, "ins_goto", 1342843963u, 0, 8, &be_const_str_send_multicast); +be_define_const_str(ins_goto, "ins_goto", 1342843963u, 0, 8, NULL); be_define_const_str(ins_ramp, "ins_ramp", 1068049360u, 0, 8, NULL); -be_define_const_str(ins_time, "ins_time", 2980245553u, 0, 8, &be_const_str_missing_X20name); -be_define_const_str(insert, "insert", 3332609576u, 0, 6, NULL); -be_define_const_str(instance, "instance", 193386898u, 0, 8, &be_const_str_set_matrix_pixel_color); -be_define_const_str(instance_X20required, "instance required", 381192159u, 0, 17, &be_const_str_set_ota_max); -be_define_const_str(instance_size, "instance_size", 4280269518u, 0, 13, &be_const_str_lv_timer_cb); -be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_on); -be_define_const_str(int64, "int64", 64103268u, 0, 5, &be_const_str_trig); -be_define_const_str(internal_error, "internal_error", 2519158169u, 0, 14, &be_const_str_set_dcdc_enable); -be_define_const_str(introspect, "introspect", 164638290u, 0, 10, &be_const_str_value); -be_define_const_str(invalid_X20GPIO_X20number, "invalid GPIO number", 4135793328u, 0, 19, &be_const_str_r); -be_define_const_str(invalid_X20magic_X20number_X20_X2502X, "invalid magic number %02X", 2836756259u, 0, 25, NULL); -be_define_const_str(invalidate_spiffs, "invalidate_spiffs", 1470453498u, 0, 17, &be_const_str_tcpclient); -be_define_const_str(io_error, "io_error", 1970281036u, 0, 8, &be_const_str_range); -be_define_const_str(is_dirty, "is_dirty", 418034110u, 0, 8, NULL); -be_define_const_str(is_factory, "is_factory", 1312753376u, 0, 10, NULL); -be_define_const_str(is_first_time, "is_first_time", 275242384u, 0, 13, &be_const_str_obj_class_create_obj); -be_define_const_str(is_ota, "is_ota", 2892315548u, 0, 6, NULL); -be_define_const_str(is_running, "is_running", 2226847261u, 0, 10, NULL); -be_define_const_str(is_spiffs, "is_spiffs", 3196570601u, 0, 9, &be_const_str_setitem); -be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, &be_const_str_send); -be_define_const_str(ismapped, "ismapped", 2725004770u, 0, 8, NULL); -be_define_const_str(ismethod, "ismethod", 3513438880u, 0, 8, NULL); +be_define_const_str(ins_time, "ins_time", 2980245553u, 0, 8, &be_const_str_json_fdump_map); +be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_is_factory); +be_define_const_str(instance, "instance", 193386898u, 0, 8, &be_const_str_set_exten); +be_define_const_str(instance_X20required, "instance required", 381192159u, 0, 17, &be_const_str_set_mem); +be_define_const_str(instance_size, "instance_size", 4280269518u, 0, 13, &be_const_str_top); +be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_range); +be_define_const_str(int64, "int64", 64103268u, 0, 5, &be_const_str_path); +be_define_const_str(internal_error, "internal_error", 2519158169u, 0, 14, &be_const_str_write_file); +be_define_const_str(introspect, "introspect", 164638290u, 0, 10, &be_const_str_widget_dtor_impl); +be_define_const_str(invalid_X20GPIO_X20number, "invalid GPIO number", 4135793328u, 0, 19, NULL); +be_define_const_str(invalid_X20magic_X20number_X20_X2502X, "invalid magic number %02X", 2836756259u, 0, 25, &be_const_str_resize); +be_define_const_str(invalidate_spiffs, "invalidate_spiffs", 1470453498u, 0, 17, NULL); +be_define_const_str(io_error, "io_error", 1970281036u, 0, 8, &be_const_str_ota_); +be_define_const_str(is_dirty, "is_dirty", 418034110u, 0, 8, &be_const_str_send_multicast); +be_define_const_str(is_factory, "is_factory", 1312753376u, 0, 10, &be_const_str_web_add_management_button); +be_define_const_str(is_first_time, "is_first_time", 275242384u, 0, 13, NULL); +be_define_const_str(is_ota, "is_ota", 2892315548u, 0, 6, &be_const_str_string); +be_define_const_str(is_running, "is_running", 2226847261u, 0, 10, &be_const_str_strptime); +be_define_const_str(is_spiffs, "is_spiffs", 3196570601u, 0, 9, &be_const_str_split); +be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, NULL); +be_define_const_str(ismapped, "ismapped", 2725004770u, 0, 8, &be_const_str_log10); +be_define_const_str(ismethod, "ismethod", 3513438880u, 0, 8, &be_const_str_rand); be_define_const_str(isnan, "isnan", 2981347434u, 0, 5, NULL); -be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_no_X20GPIO_X20specified_X20for_X20neopixelbus); +be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_read); be_define_const_str(item, "item", 2671260646u, 0, 4, NULL); -be_define_const_str(iter, "iter", 3124256359u, 0, 4, &be_const_str_run); -be_define_const_str(json, "json", 916562499u, 0, 4, &be_const_str_module); -be_define_const_str(json_append, "json_append", 3002019284u, 0, 11, &be_const_str_tasmota_log_reader); +be_define_const_str(iter, "iter", 3124256359u, 0, 4, NULL); +be_define_const_str(json, "json", 916562499u, 0, 4, &be_const_str_sqrt); +be_define_const_str(json_append, "json_append", 3002019284u, 0, 11, NULL); be_define_const_str(json_fdump, "json_fdump", 1694216580u, 0, 10, NULL); -be_define_const_str(json_fdump_any, "json_fdump_any", 3348629385u, 0, 14, &be_const_str_lv_extra); -be_define_const_str(json_fdump_list, "json_fdump_list", 3903879853u, 0, 15, &be_const_str_size); -be_define_const_str(json_fdump_map, "json_fdump_map", 4091954653u, 0, 14, NULL); +be_define_const_str(json_fdump_any, "json_fdump_any", 3348629385u, 0, 14, NULL); +be_define_const_str(json_fdump_list, "json_fdump_list", 3903879853u, 0, 15, &be_const_str_tcpclient); +be_define_const_str(json_fdump_map, "json_fdump_map", 4091954653u, 0, 14, &be_const_str_remote_ip); be_define_const_str(keys, "keys", 4182378701u, 0, 4, NULL); -be_define_const_str(label, "label", 4137097213u, 0, 5, NULL); -be_define_const_str(last_modified, "last_modified", 772177145u, 0, 13, NULL); -be_define_const_str(leds, "leds", 558858555u, 0, 4, NULL); -be_define_const_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032, "length in bits must be between 0 and 32", 2584509128u, 0, 39, &be_const_str_read12); +be_define_const_str(label, "label", 4137097213u, 0, 5, &be_const_str_set_timer); +be_define_const_str(last_modified, "last_modified", 772177145u, 0, 13, &be_const_str_list); +be_define_const_str(leds, "leds", 558858555u, 0, 4, &be_const_str_light_state); +be_define_const_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032, "length in bits must be between 0 and 32", 2584509128u, 0, 39, &be_const_str_lv_event); be_define_const_str(light, "light", 3801947695u, 0, 5, NULL); -be_define_const_str(light_X20must_X20be_X20of_X20class_X20_X27light_state_X27, "light must be of class 'light_state'", 3669350396u, 0, 36, &be_const_str_widget_struct_by_class); -be_define_const_str(light_state, "light_state", 905783845u, 0, 11, NULL); -be_define_const_str(light_to_id, "light_to_id", 1117015647u, 0, 11, &be_const_str_redirect); -be_define_const_str(lights, "lights", 425118420u, 0, 6, &be_const_str_widget_destructor); -be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_pc_rel); -be_define_const_str(list_handlers, "list_handlers", 593774371u, 0, 13, &be_const_str_set_rgb); -be_define_const_str(listdir, "listdir", 2005220720u, 0, 7, &be_const_str_member); -be_define_const_str(load, "load", 3859241449u, 0, 4, &be_const_str_read24); -be_define_const_str(load_otadata, "load_otadata", 1955073712u, 0, 12, NULL); -be_define_const_str(load_templates, "load_templates", 3513870133u, 0, 14, &be_const_str_remote_port); -be_define_const_str(log, "log", 1062293841u, 0, 3, &be_const_str_sec); -be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_running); -be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_page_autoconf_mgr); -be_define_const_str(lv, "lv", 1529997255u, 0, 2, NULL); -be_define_const_str(lv_, "lv_", 663721032u, 0, 3, &be_const_str_return_X20code_X3D_X25i); -be_define_const_str(lv_coord_arr, "lv_coord_arr", 1197238601u, 0, 12, &be_const_str_setrange); -be_define_const_str(lv_event, "lv_event", 2434089968u, 0, 8, &be_const_str_partition_core); +be_define_const_str(light_X20must_X20be_X20of_X20class_X20_X27light_state_X27, "light must be of class 'light_state'", 3669350396u, 0, 36, NULL); +be_define_const_str(light_state, "light_state", 905783845u, 0, 11, &be_const_str_math); +be_define_const_str(light_to_id, "light_to_id", 1117015647u, 0, 11, NULL); +be_define_const_str(lights, "lights", 425118420u, 0, 6, &be_const_str_pin_mode); +be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_resp_cmnd_done); +be_define_const_str(list_handlers, "list_handlers", 593774371u, 0, 13, NULL); +be_define_const_str(listdir, "listdir", 2005220720u, 0, 7, &be_const_str_nan); +be_define_const_str(load, "load", 3859241449u, 0, 4, NULL); +be_define_const_str(load_otadata, "load_otadata", 1955073712u, 0, 12, &be_const_str_reset); +be_define_const_str(load_templates, "load_templates", 3513870133u, 0, 14, NULL); +be_define_const_str(log, "log", 1062293841u, 0, 3, NULL); +be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_memory); +be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_manuf); +be_define_const_str(lv, "lv", 1529997255u, 0, 2, &be_const_str_make_cb); +be_define_const_str(lv_, "lv_", 663721032u, 0, 3, NULL); +be_define_const_str(lv_coord_arr, "lv_coord_arr", 1197238601u, 0, 12, NULL); +be_define_const_str(lv_event, "lv_event", 2434089968u, 0, 8, &be_const_str_rad); be_define_const_str(lv_event_cb, "lv_event_cb", 2480731016u, 0, 11, &be_const_str_set_hum); -be_define_const_str(lv_extra, "lv_extra", 399561998u, 0, 8, &be_const_str_to_gamma); -be_define_const_str(lv_module_init, "lv_module_init", 1133027755u, 0, 14, &be_const_str_time_dump); -be_define_const_str(lv_obj, "lv_obj", 4257833149u, 0, 6, &be_const_str_set_mode_rgb); +be_define_const_str(lv_extra, "lv_extra", 399561998u, 0, 8, &be_const_str_sec); +be_define_const_str(lv_module_init, "lv_module_init", 1133027755u, 0, 14, NULL); +be_define_const_str(lv_obj, "lv_obj", 4257833149u, 0, 6, NULL); be_define_const_str(lv_obj_class, "lv_obj_class", 4039656294u, 0, 12, NULL); -be_define_const_str(lv_point, "lv_point", 4120221790u, 0, 8, &be_const_str_matrix); -be_define_const_str(lv_point_arr, "lv_point_arr", 3959768858u, 0, 12, &be_const_str_rad); -be_define_const_str(lv_solidified, "lv_solidified", 2274121310u, 0, 13, NULL); -be_define_const_str(lv_style_prop_arr, "lv_style_prop_arr", 2504347499u, 0, 17, NULL); -be_define_const_str(lv_timer_cb, "lv_timer_cb", 1383473763u, 0, 11, &be_const_str_sum); -be_define_const_str(lvgl_event_dispatch, "lvgl_event_dispatch", 2104396622u, 0, 19, NULL); -be_define_const_str(lvgl_timer_dispatch, "lvgl_timer_dispatch", 975257833u, 0, 19, &be_const_str_run_deferred); +be_define_const_str(lv_point, "lv_point", 4120221790u, 0, 8, NULL); +be_define_const_str(lv_point_arr, "lv_point_arr", 3959768858u, 0, 12, NULL); +be_define_const_str(lv_solidified, "lv_solidified", 2274121310u, 0, 13, &be_const_str_pop); +be_define_const_str(lv_style_prop_arr, "lv_style_prop_arr", 2504347499u, 0, 17, &be_const_str_toint); +be_define_const_str(lv_timer_cb, "lv_timer_cb", 1383473763u, 0, 11, NULL); +be_define_const_str(lvgl_event_dispatch, "lvgl_event_dispatch", 2104396622u, 0, 19, &be_const_str_no_X20more_X20RMT_X20channel_X20available); +be_define_const_str(lvgl_timer_dispatch, "lvgl_timer_dispatch", 975257833u, 0, 19, &be_const_str_srand); be_define_const_str(make_cb, "make_cb", 71252785u, 0, 7, NULL); -be_define_const_str(manuf, "manuf", 4120929560u, 0, 5, &be_const_str_y); -be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_pi); -be_define_const_str(math, "math", 4001929615u, 0, 4, NULL); +be_define_const_str(manuf, "manuf", 4120929560u, 0, 5, &be_const_str_set_temp); +be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_send); +be_define_const_str(math, "math", 4001929615u, 0, 4, &be_const_str_widget_destructor); be_define_const_str(matrix, "matrix", 365099244u, 0, 6, NULL); -be_define_const_str(maxota, "maxota", 2594898265u, 0, 6, &be_const_str_sqrt); +be_define_const_str(maxota, "maxota", 2594898265u, 0, 6, NULL); be_define_const_str(md5, "md5", 2393554675u, 0, 3, NULL); -be_define_const_str(member, "member", 719708611u, 0, 6, NULL); -be_define_const_str(members, "members", 937576464u, 0, 7, &be_const_str_subscribe); -be_define_const_str(memory, "memory", 2229924270u, 0, 6, NULL); -be_define_const_str(millis, "millis", 1214679063u, 0, 6, &be_const_str_valuer_error); +be_define_const_str(member, "member", 719708611u, 0, 6, &be_const_str_value_error); +be_define_const_str(members, "members", 937576464u, 0, 7, NULL); +be_define_const_str(memory, "memory", 2229924270u, 0, 6, &be_const_str_set_ldo_voltage); +be_define_const_str(millis, "millis", 1214679063u, 0, 6, &be_const_str_publish_rule); be_define_const_str(min, "min", 3381609815u, 0, 3, NULL); -be_define_const_str(missing_X20name, "missing name", 3635024006u, 0, 12, &be_const_str_resp_cmnd_done); +be_define_const_str(missing_X20name, "missing name", 3635024006u, 0, 12, &be_const_str_switch_factory); be_define_const_str(model, "model", 2961925722u, 0, 5, NULL); -be_define_const_str(module, "module", 3617558685u, 0, 6, &be_const_str_rand); -be_define_const_str(month, "month", 3598321157u, 0, 5, NULL); -be_define_const_str(mqtt, "mqtt", 353147387u, 0, 4, NULL); -be_define_const_str(mqtt_data, "mqtt_data", 2538213364u, 0, 9, NULL); -be_define_const_str(mqtt_listener, "mqtt_listener", 2764719532u, 0, 13, NULL); -be_define_const_str(name, "name", 2369371622u, 0, 4, &be_const_str_write_gpio); +be_define_const_str(module, "module", 3617558685u, 0, 6, NULL); +be_define_const_str(month, "month", 3598321157u, 0, 5, &be_const_str_remove_trailing_zeroes); +be_define_const_str(mqtt, "mqtt", 353147387u, 0, 4, &be_const_str_sinh); +be_define_const_str(mqtt_data, "mqtt_data", 2538213364u, 0, 9, &be_const_str_scale_uint); +be_define_const_str(mqtt_listener, "mqtt_listener", 2764719532u, 0, 13, &be_const_str_set_alternate); +be_define_const_str(name, "name", 2369371622u, 0, 4, &be_const_str_parse); be_define_const_str(nan, "nan", 797905850u, 0, 3, NULL); -be_define_const_str(next, "next", 1555467752u, 0, 4, &be_const_str_resp_cmnd_str); -be_define_const_str(next_cron, "next_cron", 3260705337u, 0, 9, &be_const_str_seti); -be_define_const_str(nil, "nil", 228849900u, 63, 3, &be_const_str_uuid4); +be_define_const_str(next, "next", 1555467752u, 0, 4, &be_const_str_time_str); +be_define_const_str(next_cron, "next_cron", 3260705337u, 0, 9, &be_const_str_static); +be_define_const_str(nil, "nil", 228849900u, 63, 3, &be_const_str_preinit); be_define_const_str(no_X20GPIO_X20specified_X20for_X20neopixelbus, "no GPIO specified for neopixelbus", 42078528u, 0, 33, NULL); -be_define_const_str(no_X20more_X20RMT_X20channel_X20available, "no more RMT channel available", 305838632u, 0, 29, &be_const_str_web_add_main_button); -be_define_const_str(now, "now", 682728183u, 0, 3, &be_const_str_web_add_handler); -be_define_const_str(null_cb, "null_cb", 2333536460u, 0, 7, NULL); -be_define_const_str(number, "number", 467038368u, 0, 6, NULL); -be_define_const_str(nvs, "nvs", 477704066u, 0, 3, &be_const_str_split); -be_define_const_str(nvskeys, "nvskeys", 1760042990u, 0, 7, NULL); -be_define_const_str(o, "o", 3926667934u, 0, 1, NULL); -be_define_const_str(obj_class_create_obj, "obj_class_create_obj", 3304390632u, 0, 20, &be_const_str_offseta); -be_define_const_str(offset, "offset", 348705738u, 0, 6, &be_const_str_stop); -be_define_const_str(offseta, "offseta", 1663383089u, 0, 7, &be_const_str_set_huesat); -be_define_const_str(on, "on", 1630810064u, 0, 2, &be_const_str_resp_cmnd_failed); -be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will cause a restart.\");'>", 232646018u, 0, 57, &be_const_str_var); +be_define_const_str(no_X20more_X20RMT_X20channel_X20available, "no more RMT channel available", 305838632u, 0, 29, &be_const_str_run); +be_define_const_str(now, "now", 682728183u, 0, 3, NULL); +be_define_const_str(null_cb, "null_cb", 2333536460u, 0, 7, &be_const_str_resp_cmnd_str); +be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_remove_driver); +be_define_const_str(nvs, "nvs", 477704066u, 0, 3, &be_const_str__X7Bs_X7DBatt_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); +be_define_const_str(nvskeys, "nvskeys", 1760042990u, 0, 7, &be_const_str_ota_max); +be_define_const_str(o, "o", 3926667934u, 0, 1, &be_const_str_write); +be_define_const_str(obj_class_create_obj, "obj_class_create_obj", 3304390632u, 0, 20, NULL); +be_define_const_str(offset, "offset", 348705738u, 0, 6, NULL); +be_define_const_str(offseta, "offseta", 1663383089u, 0, 7, &be_const_str_replace); +be_define_const_str(on, "on", 1630810064u, 0, 2, &be_const_str_set_useragent); +be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will cause a restart.\");'>", 232646018u, 0, 57, NULL); be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will change the current configuration and cause a restart.\");'>", 3792412559u, 0, 94, NULL); -be_define_const_str(open, "open", 3546203337u, 0, 4, &be_const_str_set_active); -be_define_const_str(ota_, "ota_", 773595766u, 0, 4, NULL); +be_define_const_str(open, "open", 3546203337u, 0, 4, &be_const_str_set_ota_max); +be_define_const_str(ota_, "ota_", 773595766u, 0, 4, &be_const_str_phy); be_define_const_str(ota_max, "ota_max", 2940511240u, 0, 7, NULL); be_define_const_str(otadata, "otadata", 1962391757u, 0, 7, NULL); -be_define_const_str(out_X20of_X20range, "out of range", 2236631477u, 0, 12, &be_const_str_type_error); -be_define_const_str(page_autoconf_ctl, "page_autoconf_ctl", 2453381496u, 0, 17, NULL); -be_define_const_str(page_autoconf_mgr, "page_autoconf_mgr", 3643937031u, 0, 17, NULL); -be_define_const_str(parse, "parse", 1111180012u, 0, 5, NULL); -be_define_const_str(partition_core, "partition_core", 2913046901u, 0, 14, NULL); -be_define_const_str(path, "path", 2223459638u, 0, 4, &be_const_str_unknown_X20instruction); -be_define_const_str(pc, "pc", 1313756516u, 0, 2, NULL); -be_define_const_str(pc_abs, "pc_abs", 920256495u, 0, 6, NULL); +be_define_const_str(out_X20of_X20range, "out of range", 2236631477u, 0, 12, NULL); +be_define_const_str(page_autoconf_ctl, "page_autoconf_ctl", 2453381496u, 0, 17, &be_const_str_seq0); +be_define_const_str(page_autoconf_mgr, "page_autoconf_mgr", 3643937031u, 0, 17, &be_const_str_write_flash); +be_define_const_str(parse, "parse", 1111180012u, 0, 5, &be_const_str_web_sensor); +be_define_const_str(partition_core, "partition_core", 2913046901u, 0, 14, &be_const_str_set_bat); +be_define_const_str(path, "path", 2223459638u, 0, 4, NULL); +be_define_const_str(pc, "pc", 1313756516u, 0, 2, &be_const_str_run_deferred); +be_define_const_str(pc_abs, "pc_abs", 920256495u, 0, 6, &be_const_str_wake_period); be_define_const_str(pc_rel, "pc_rel", 991921176u, 0, 6, NULL); -be_define_const_str(persist, "persist", 3917083779u, 0, 7, &be_const_str_strptime); -be_define_const_str(persist_X2E_p_X20is_X20not_X20a_X20map, "persist._p is not a map", 1176528732u, 0, 23, NULL); -be_define_const_str(phy, "phy", 1648673716u, 0, 3, &be_const_str_set_hue16sat); -be_define_const_str(pi, "pi", 1213090802u, 0, 2, &be_const_str_reverse_gamma10); +be_define_const_str(persist, "persist", 3917083779u, 0, 7, &be_const_str_wire1); +be_define_const_str(persist_X2E_p_X20is_X20not_X20a_X20map, "persist._p is not a map", 1176528732u, 0, 23, &be_const_str_set_pwm); +be_define_const_str(phy, "phy", 1648673716u, 0, 3, &be_const_str_resp_cmnd); +be_define_const_str(pi, "pi", 1213090802u, 0, 2, &be_const_str_write8); be_define_const_str(pin, "pin", 1866532500u, 0, 3, NULL); -be_define_const_str(pin_mode, "pin_mode", 3258314030u, 0, 8, &be_const_str_read13); -be_define_const_str(pin_used, "pin_used", 4033854612u, 0, 8, &be_const_str_read32); -be_define_const_str(pixel_count, "pixel_count", 2439130743u, 0, 11, NULL); -be_define_const_str(pixel_size, "pixel_size", 2209135785u, 0, 10, NULL); -be_define_const_str(pixels_buffer, "pixels_buffer", 1229555807u, 0, 13, &be_const_str_web_send); -be_define_const_str(point_arr, "point_arr", 1140859857u, 0, 9, &be_const_str_tolower); -be_define_const_str(pop, "pop", 1362321360u, 0, 3, &be_const_str_webserver); -be_define_const_str(pop_path, "pop_path", 2403243998u, 0, 8, &be_const_str_pow); -be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str__X7Bs_X7DVBus_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D); -be_define_const_str(power_off, "power_off", 3568741752u, 0, 9, &be_const_str_replace); +be_define_const_str(pin_mode, "pin_mode", 3258314030u, 0, 8, &be_const_str_wire_scan); +be_define_const_str(pin_used, "pin_used", 4033854612u, 0, 8, &be_const_str_topic); +be_define_const_str(pixel_count, "pixel_count", 2439130743u, 0, 11, &be_const_str_try_get_bec_version); +be_define_const_str(pixel_size, "pixel_size", 2209135785u, 0, 10, &be_const_str_seq1); +be_define_const_str(pixels_buffer, "pixels_buffer", 1229555807u, 0, 13, NULL); +be_define_const_str(point_arr, "point_arr", 1140859857u, 0, 9, NULL); +be_define_const_str(pop, "pop", 1362321360u, 0, 3, NULL); +be_define_const_str(pop_path, "pop_path", 2403243998u, 0, 8, NULL); +be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str_set_power); +be_define_const_str(power_off, "power_off", 3568741752u, 0, 9, NULL); be_define_const_str(preinit, "preinit", 2722007100u, 0, 7, NULL); -be_define_const_str(print, "print", 372738696u, 0, 5, &be_const_str_raw); +be_define_const_str(print, "print", 372738696u, 0, 5, &be_const_str_sleep); be_define_const_str(ptr, "ptr", 1433816073u, 0, 3, NULL); -be_define_const_str(publish, "publish", 264247304u, 0, 7, &be_const_str_remote_ip); +be_define_const_str(publish, "publish", 264247304u, 0, 7, &be_const_str_wire); be_define_const_str(publish_result, "publish_result", 2013351252u, 0, 14, NULL); -be_define_const_str(publish_rule, "publish_rule", 1829459523u, 0, 12, &be_const_str_tostring); +be_define_const_str(publish_rule, "publish_rule", 1829459523u, 0, 12, NULL); be_define_const_str(push, "push", 2272264157u, 0, 4, NULL); -be_define_const_str(push_path, "push_path", 1155254157u, 0, 9, &be_const_str_real); +be_define_const_str(push_path, "push_path", 1155254157u, 0, 9, NULL); be_define_const_str(r, "r", 4144776981u, 0, 1, NULL); be_define_const_str(rad, "rad", 1358899048u, 0, 3, NULL); -be_define_const_str(raise, "raise", 1593437475u, 70, 5, NULL); -be_define_const_str(rand, "rand", 2711325910u, 0, 4, &be_const_str_try_get_bec_version); -be_define_const_str(range, "range", 4208725202u, 0, 5, &be_const_str_tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29); +be_define_const_str(raise, "raise", 1593437475u, 70, 5, &be_const_str_set_rgb); +be_define_const_str(rand, "rand", 2711325910u, 0, 4, NULL); +be_define_const_str(range, "range", 4208725202u, 0, 5, NULL); be_define_const_str(raw, "raw", 1140790001u, 0, 3, NULL); -be_define_const_str(read, "read", 3470762949u, 0, 4, NULL); +be_define_const_str(read, "read", 3470762949u, 0, 4, &be_const_str_set_bri); be_define_const_str(read12, "read12", 4291076970u, 0, 6, NULL); be_define_const_str(read13, "read13", 12887293u, 0, 6, NULL); -be_define_const_str(read24, "read24", 1808533811u, 0, 6, &be_const_str_subtype_to_string); -be_define_const_str(read32, "read32", 1741276240u, 0, 6, NULL); -be_define_const_str(read8, "read8", 2802788167u, 0, 5, &be_const_str__X7Bs_X7DVBus_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); -be_define_const_str(read_bytes, "read_bytes", 3576733173u, 0, 10, &be_const_str_spiffs); -be_define_const_str(read_sensors, "read_sensors", 892689201u, 0, 12, &be_const_str_set_bat); +be_define_const_str(read24, "read24", 1808533811u, 0, 6, &be_const_str_widget_event_impl); +be_define_const_str(read32, "read32", 1741276240u, 0, 6, &be_const_str_try_compile); +be_define_const_str(read8, "read8", 2802788167u, 0, 5, NULL); +be_define_const_str(read_bytes, "read_bytes", 3576733173u, 0, 10, NULL); +be_define_const_str(read_sensors, "read_sensors", 892689201u, 0, 12, NULL); be_define_const_str(readbytes, "readbytes", 2716426756u, 0, 9, NULL); be_define_const_str(readline, "readline", 1212709927u, 0, 8, NULL); -be_define_const_str(real, "real", 3604983901u, 0, 4, &be_const_str_remove); -be_define_const_str(reapply, "reapply", 3778939332u, 0, 7, NULL); -be_define_const_str(redirect, "redirect", 389758641u, 0, 8, NULL); +be_define_const_str(real, "real", 3604983901u, 0, 4, NULL); +be_define_const_str(reapply, "reapply", 3778939332u, 0, 7, &be_const_str_slots); +be_define_const_str(redirect, "redirect", 389758641u, 0, 8, &be_const_str_stop_iteration); be_define_const_str(register_obj, "register_obj", 3982614770u, 0, 12, NULL); -be_define_const_str(remote_ip, "remote_ip", 2953154693u, 0, 9, &be_const_str_set_exten); -be_define_const_str(remote_port, "remote_port", 2163585967u, 0, 11, NULL); -be_define_const_str(remove, "remove", 3683784189u, 0, 6, &be_const_str_web_send_decimal); +be_define_const_str(remote_ip, "remote_ip", 2953154693u, 0, 9, &be_const_str_tohex); +be_define_const_str(remote_port, "remote_port", 2163585967u, 0, 11, &be_const_str_time_dump); +be_define_const_str(remove, "remove", 3683784189u, 0, 6, NULL); be_define_const_str(remove_cmd, "remove_cmd", 3832315702u, 0, 10, NULL); -be_define_const_str(remove_cron, "remove_cron", 2914538962u, 0, 11, &be_const_str_slots); -be_define_const_str(remove_driver, "remove_driver", 1030243768u, 0, 13, &be_const_str_super); -be_define_const_str(remove_light, "remove_light", 1783624394u, 0, 12, NULL); +be_define_const_str(remove_cron, "remove_cron", 2914538962u, 0, 11, NULL); +be_define_const_str(remove_driver, "remove_driver", 1030243768u, 0, 13, NULL); +be_define_const_str(remove_light, "remove_light", 1783624394u, 0, 12, &be_const_str_seti); be_define_const_str(remove_rule, "remove_rule", 3456211328u, 0, 11, NULL); be_define_const_str(remove_timer, "remove_timer", 4141472215u, 0, 12, NULL); -be_define_const_str(remove_trailing_zeroes, "remove_trailing_zeroes", 2688378377u, 0, 22, &be_const_str_set_bri); -be_define_const_str(replace, "replace", 2704835779u, 0, 7, &be_const_str__X7Bs_X7DBatt_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); -be_define_const_str(reset, "reset", 1695364032u, 0, 5, NULL); +be_define_const_str(remove_trailing_zeroes, "remove_trailing_zeroes", 2688378377u, 0, 22, &be_const_str_set_ct); +be_define_const_str(replace, "replace", 2704835779u, 0, 7, NULL); +be_define_const_str(reset, "reset", 1695364032u, 0, 5, &be_const_str_str); be_define_const_str(resize, "resize", 3514612129u, 0, 6, NULL); -be_define_const_str(resolvecmnd, "resolvecmnd", 993361485u, 0, 11, &be_const_str_switch_factory); +be_define_const_str(resolvecmnd, "resolvecmnd", 993361485u, 0, 11, NULL); be_define_const_str(resp_cmnd, "resp_cmnd", 2869459626u, 0, 9, NULL); -be_define_const_str(resp_cmnd_done, "resp_cmnd_done", 2601874875u, 0, 14, NULL); -be_define_const_str(resp_cmnd_error, "resp_cmnd_error", 2404088863u, 0, 15, NULL); -be_define_const_str(resp_cmnd_failed, "resp_cmnd_failed", 2136281562u, 0, 16, &be_const_str_signal_change); +be_define_const_str(resp_cmnd_done, "resp_cmnd_done", 2601874875u, 0, 14, &be_const_str_zero); +be_define_const_str(resp_cmnd_error, "resp_cmnd_error", 2404088863u, 0, 15, &be_const_str_rule); +be_define_const_str(resp_cmnd_failed, "resp_cmnd_failed", 2136281562u, 0, 16, &be_const_str_web_add_handler); be_define_const_str(resp_cmnd_str, "resp_cmnd_str", 737845590u, 0, 13, NULL); be_define_const_str(response_append, "response_append", 450346371u, 0, 15, NULL); be_define_const_str(return, "return", 2246981567u, 60, 6, NULL); be_define_const_str(return_X20code_X3D_X25i, "return code=%i", 2127454401u, 0, 14, NULL); be_define_const_str(reverse, "reverse", 558918661u, 0, 7, NULL); be_define_const_str(reverse_gamma10, "reverse_gamma10", 739112262u, 0, 15, NULL); -be_define_const_str(rotate, "rotate", 2784296202u, 0, 6, &be_const_str_traceback); -be_define_const_str(rtc, "rtc", 1070575216u, 0, 3, NULL); -be_define_const_str(rule, "rule", 4230889683u, 0, 4, &be_const_str_set_power); -be_define_const_str(run, "run", 718098122u, 0, 3, &be_const_str_write8); -be_define_const_str(run_bat, "run_bat", 2536903298u, 0, 7, &be_const_str_the_X20second_X20argument_X20is_X20not_X20a_X20function); -be_define_const_str(run_cron, "run_cron", 1929098555u, 0, 8, NULL); -be_define_const_str(run_deferred, "run_deferred", 371594696u, 0, 12, &be_const_str_sinh); -be_define_const_str(running, "running", 343848780u, 0, 7, &be_const_str_widget_cb); -be_define_const_str(sat, "sat", 3592196823u, 0, 3, &be_const_str_set_xy); +be_define_const_str(rotate, "rotate", 2784296202u, 0, 6, NULL); +be_define_const_str(rtc, "rtc", 1070575216u, 0, 3, &be_const_str_show); +be_define_const_str(rule, "rule", 4230889683u, 0, 4, NULL); +be_define_const_str(run, "run", 718098122u, 0, 3, &be_const_str_style_prop_arr); +be_define_const_str(run_bat, "run_bat", 2536903298u, 0, 7, NULL); +be_define_const_str(run_cron, "run_cron", 1929098555u, 0, 8, &be_const_str_web_add_config_button); +be_define_const_str(run_deferred, "run_deferred", 371594696u, 0, 12, &be_const_str_zip); +be_define_const_str(running, "running", 343848780u, 0, 7, &be_const_str_tob64); +be_define_const_str(sat, "sat", 3592196823u, 0, 3, NULL); be_define_const_str(save, "save", 3439296072u, 0, 4, NULL); be_define_const_str(save_before_restart, "save_before_restart", 1253239338u, 0, 19, NULL); -be_define_const_str(scale_uint, "scale_uint", 3090811094u, 0, 10, NULL); -be_define_const_str(scan, "scan", 3974641896u, 0, 4, &be_const_str_widget_ctor_impl); -be_define_const_str(sec, "sec", 3139892658u, 0, 3, &be_const_str_widget_event); +be_define_const_str(scale_uint, "scale_uint", 3090811094u, 0, 10, &be_const_str_update); +be_define_const_str(scan, "scan", 3974641896u, 0, 4, NULL); +be_define_const_str(sec, "sec", 3139892658u, 0, 3, NULL); be_define_const_str(send, "send", 1919010991u, 0, 4, NULL); be_define_const_str(send_multicast, "send_multicast", 812185870u, 0, 14, NULL); -be_define_const_str(seq0, "seq0", 880225636u, 0, 4, &be_const_str_web_add_console_button); -be_define_const_str(seq1, "seq1", 897003255u, 0, 4, NULL); -be_define_const_str(serial, "serial", 3687697785u, 0, 6, &be_const_str_set_timeouts); -be_define_const_str(set, "set", 3324446467u, 0, 3, &be_const_str_widget_event_impl); +be_define_const_str(seq0, "seq0", 880225636u, 0, 4, &be_const_str_widget_editable); +be_define_const_str(seq1, "seq1", 897003255u, 0, 4, &be_const_str_set_mode_ct); +be_define_const_str(serial, "serial", 3687697785u, 0, 6, NULL); +be_define_const_str(set, "set", 3324446467u, 0, 3, NULL); be_define_const_str(set_active, "set_active", 3683994102u, 0, 10, NULL); -be_define_const_str(set_alternate, "set_alternate", 1709680562u, 0, 13, NULL); -be_define_const_str(set_auth, "set_auth", 1057170930u, 0, 8, &be_const_str_time_str); -be_define_const_str(set_bat, "set_bat", 2736667351u, 0, 7, &be_const_str_set_first_time); +be_define_const_str(set_alternate, "set_alternate", 1709680562u, 0, 13, &be_const_str_tan); +be_define_const_str(set_auth, "set_auth", 1057170930u, 0, 8, NULL); +be_define_const_str(set_bat, "set_bat", 2736667351u, 0, 7, &be_const_str_true); be_define_const_str(set_bri, "set_bri", 2789118779u, 0, 7, NULL); -be_define_const_str(set_chg_current, "set_chg_current", 336304386u, 0, 15, NULL); +be_define_const_str(set_chg_current, "set_chg_current", 336304386u, 0, 15, &be_const_str_set_dcdc_enable); be_define_const_str(set_ct, "set_ct", 972363187u, 0, 6, NULL); -be_define_const_str(set_dc_voltage, "set_dc_voltage", 2181981936u, 0, 14, &be_const_str_widget_ctor_cb); +be_define_const_str(set_dc_voltage, "set_dc_voltage", 2181981936u, 0, 14, NULL); be_define_const_str(set_dcdc_enable, "set_dcdc_enable", 1594690786u, 0, 15, NULL); be_define_const_str(set_exten, "set_exten", 1721782768u, 0, 9, NULL); be_define_const_str(set_first_time, "set_first_time", 3111247550u, 0, 14, NULL); be_define_const_str(set_hue16sat, "set_hue16sat", 1858983599u, 0, 12, NULL); -be_define_const_str(set_huesat, "set_huesat", 626496854u, 0, 10, &be_const_str_x); +be_define_const_str(set_huesat, "set_huesat", 626496854u, 0, 10, NULL); be_define_const_str(set_hum, "set_hum", 964296026u, 0, 7, NULL); be_define_const_str(set_ldo_enable, "set_ldo_enable", 2916502041u, 0, 14, NULL); be_define_const_str(set_ldo_voltage, "set_ldo_voltage", 4090501160u, 0, 15, NULL); be_define_const_str(set_light, "set_light", 3176076152u, 0, 9, NULL); be_define_const_str(set_matrix_pixel_color, "set_matrix_pixel_color", 1197149462u, 0, 22, NULL); +be_define_const_str(set_mem, "set_mem", 4097333429u, 0, 7, NULL); be_define_const_str(set_mode_ct, "set_mode_ct", 665073295u, 0, 11, NULL); -be_define_const_str(set_mode_rgb, "set_mode_rgb", 852310875u, 0, 12, &be_const_str_tobytes); -be_define_const_str(set_ota_max, "set_ota_max", 4093779527u, 0, 11, &be_const_str_top); -be_define_const_str(set_pixel_color, "set_pixel_color", 1275248356u, 0, 15, &be_const_str_widget_constructor); +be_define_const_str(set_mode_rgb, "set_mode_rgb", 852310875u, 0, 12, NULL); +be_define_const_str(set_ota_max, "set_ota_max", 4093779527u, 0, 11, NULL); +be_define_const_str(set_pixel_color, "set_pixel_color", 1275248356u, 0, 15, &be_const_str_write_bit); be_define_const_str(set_power, "set_power", 549820893u, 0, 9, NULL); be_define_const_str(set_pwm, "set_pwm", 3781811012u, 0, 7, NULL); be_define_const_str(set_reachable, "set_reachable", 3280367499u, 0, 13, NULL); -be_define_const_str(set_rgb, "set_rgb", 3380244855u, 0, 7, NULL); +be_define_const_str(set_rgb, "set_rgb", 3380244855u, 0, 7, &be_const_str_x); be_define_const_str(set_temp, "set_temp", 1952131250u, 0, 8, NULL); be_define_const_str(set_timeouts, "set_timeouts", 3732850900u, 0, 12, NULL); be_define_const_str(set_timer, "set_timer", 2135414533u, 0, 9, NULL); -be_define_const_str(set_useragent, "set_useragent", 612237244u, 0, 13, &be_const_str_setfloat); +be_define_const_str(set_useragent, "set_useragent", 612237244u, 0, 13, &be_const_str_wifi); be_define_const_str(set_xy, "set_xy", 1155092615u, 0, 6, NULL); -be_define_const_str(setbits, "setbits", 2762408167u, 0, 7, &be_const_str_upper); -be_define_const_str(setfloat, "setfloat", 2799488807u, 0, 8, NULL); +be_define_const_str(setbits, "setbits", 2762408167u, 0, 7, NULL); +be_define_const_str(setfloat, "setfloat", 2799488807u, 0, 8, &be_const_str_tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29); be_define_const_str(seti, "seti", 1500556254u, 0, 4, NULL); be_define_const_str(setitem, "setitem", 1554834596u, 0, 7, NULL); be_define_const_str(setmember, "setmember", 1432909441u, 0, 9, NULL); be_define_const_str(setrange, "setrange", 3794019032u, 0, 8, NULL); be_define_const_str(settings, "settings", 1745255176u, 0, 8, NULL); -be_define_const_str(show, "show", 2840060476u, 0, 4, &be_const_str_year); +be_define_const_str(show, "show", 2840060476u, 0, 4, NULL); be_define_const_str(signal_change, "signal_change", 3262299350u, 0, 13, NULL); be_define_const_str(sin, "sin", 3761252941u, 0, 3, NULL); be_define_const_str(sinh, "sinh", 282220607u, 0, 4, NULL); -be_define_const_str(size, "size", 597743964u, 0, 4, NULL); -be_define_const_str(slots, "slots", 1023330342u, 0, 5, NULL); +be_define_const_str(size, "size", 597743964u, 0, 4, &be_const_str_widget_constructor); +be_define_const_str(sleep, "sleep", 2313861896u, 0, 5, NULL); +be_define_const_str(slots, "slots", 1023330342u, 0, 5, &be_const_str_write_bytes); be_define_const_str(solidified, "solidified", 3257553487u, 0, 10, NULL); be_define_const_str(spiffs, "spiffs", 994943858u, 0, 6, NULL); be_define_const_str(split, "split", 2276994531u, 0, 5, NULL); be_define_const_str(sqrt, "sqrt", 2112764879u, 0, 4, NULL); -be_define_const_str(srand, "srand", 465518633u, 0, 5, NULL); +be_define_const_str(srand, "srand", 465518633u, 0, 5, &be_const_str_time_reached); be_define_const_str(start, "start", 1697318111u, 0, 5, NULL); be_define_const_str(state, "state", 2016490230u, 0, 5, NULL); be_define_const_str(static, "static", 3532702267u, 71, 6, NULL); -be_define_const_str(stop, "stop", 3411225317u, 0, 4, &be_const_str_try_compile); +be_define_const_str(stop, "stop", 3411225317u, 0, 4, NULL); be_define_const_str(stop_iteration, "stop_iteration", 4173793901u, 0, 14, NULL); be_define_const_str(str, "str", 3259748752u, 0, 3, NULL); -be_define_const_str(strftime, "strftime", 187738851u, 0, 8, NULL); +be_define_const_str(strftime, "strftime", 187738851u, 0, 8, &be_const_str_tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29); be_define_const_str(string, "string", 398550328u, 0, 6, NULL); be_define_const_str(strip, "strip", 4246411473u, 0, 5, NULL); be_define_const_str(strptime, "strptime", 1277910361u, 0, 8, NULL); be_define_const_str(style_prop_arr, "style_prop_arr", 3019174322u, 0, 14, NULL); be_define_const_str(subscribe, "subscribe", 2946386435u, 0, 9, NULL); be_define_const_str(subtype, "subtype", 2023873341u, 0, 7, NULL); -be_define_const_str(subtype_to_string, "subtype_to_string", 2996733901u, 0, 17, NULL); -be_define_const_str(success, "success", 979353360u, 0, 7, NULL); -be_define_const_str(sum, "sum", 3712891560u, 0, 3, &be_const_str_udp); +be_define_const_str(subtype_to_string, "subtype_to_string", 2996733901u, 0, 17, &be_const_str__X7B_X7D); +be_define_const_str(success, "success", 979353360u, 0, 7, &be_const_str__X7D); +be_define_const_str(sum, "sum", 3712891560u, 0, 3, NULL); be_define_const_str(super, "super", 4152230356u, 0, 5, NULL); be_define_const_str(switch_factory, "switch_factory", 4206217516u, 0, 14, NULL); be_define_const_str(sys, "sys", 3277365014u, 0, 3, NULL); be_define_const_str(tan, "tan", 2633446552u, 0, 3, NULL); -be_define_const_str(tanh, "tanh", 153638352u, 0, 4, NULL); +be_define_const_str(tanh, "tanh", 153638352u, 0, 4, &be_const_str_while); be_define_const_str(target, "target", 845187144u, 0, 6, NULL); -be_define_const_str(tasmota, "tasmota", 424643812u, 0, 7, NULL); -be_define_const_str(tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29, "tasmota.get_light() is deprecated, use light.get()", 3525753647u, 0, 50, &be_const_str_widget_width_def); +be_define_const_str(tasmota, "tasmota", 424643812u, 0, 7, &be_const_str_to_gamma); +be_define_const_str(tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29, "tasmota.get_light() is deprecated, use light.get()", 3525753647u, 0, 50, NULL); be_define_const_str(tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29, "tasmota.set_light() is deprecated, use light.set()", 2124937871u, 0, 50, NULL); be_define_const_str(tasmota_log_reader, "tasmota_log_reader", 3555069257u, 0, 18, NULL); -be_define_const_str(tcpclient, "tcpclient", 3828797983u, 0, 9, NULL); -be_define_const_str(tele, "tele", 3474458061u, 0, 4, NULL); +be_define_const_str(tcpclient, "tcpclient", 3828797983u, 0, 9, &be_const_str_tr); +be_define_const_str(tele, "tele", 3474458061u, 0, 4, &be_const_str_unknown_X20instruction); be_define_const_str(test, "test", 2949673445u, 0, 4, NULL); be_define_const_str(the_X20second_X20argument_X20is_X20not_X20a_X20function, "the second argument is not a function", 3954574469u, 0, 37, NULL); be_define_const_str(time_dump, "time_dump", 3330410747u, 0, 9, NULL); be_define_const_str(time_reached, "time_reached", 2075136773u, 0, 12, NULL); be_define_const_str(time_str, "time_str", 2613827612u, 0, 8, NULL); -be_define_const_str(timer_cb, "timer_cb", 79918026u, 0, 8, &be_const_str_wifi); +be_define_const_str(timer_cb, "timer_cb", 79918026u, 0, 8, NULL); be_define_const_str(to_gamma, "to_gamma", 1597139862u, 0, 8, NULL); be_define_const_str(tob64, "tob64", 373777640u, 0, 5, NULL); -be_define_const_str(tobytes, "tobytes", 595962279u, 0, 7, &be_const_str_width_def); +be_define_const_str(tobytes, "tobytes", 595962279u, 0, 7, NULL); be_define_const_str(tohex, "tohex", 1583935793u, 0, 5, NULL); -be_define_const_str(toint, "toint", 3613182909u, 0, 5, NULL); +be_define_const_str(toint, "toint", 3613182909u, 0, 5, &be_const_str_widget_ctor_impl); be_define_const_str(tolower, "tolower", 1042520049u, 0, 7, NULL); -be_define_const_str(tomap, "tomap", 612167626u, 0, 5, NULL); +be_define_const_str(tomap, "tomap", 612167626u, 0, 5, &be_const_str_w); be_define_const_str(top, "top", 2802900028u, 0, 3, NULL); -be_define_const_str(topic, "topic", 3264522692u, 0, 5, NULL); -be_define_const_str(toptr, "toptr", 3379847454u, 0, 5, NULL); +be_define_const_str(topic, "topic", 3264522692u, 0, 5, &be_const_str_web_add_console_button); +be_define_const_str(toptr, "toptr", 3379847454u, 0, 5, &be_const_str_web_add_main_button); be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, NULL); be_define_const_str(toupper, "toupper", 3691983576u, 0, 7, NULL); be_define_const_str(tr, "tr", 1195724803u, 0, 2, NULL); be_define_const_str(traceback, "traceback", 3385188109u, 0, 9, NULL); -be_define_const_str(trig, "trig", 2073314619u, 0, 4, &be_const_str_wire1); +be_define_const_str(trig, "trig", 2073314619u, 0, 4, &be_const_str__X7Bs_X7DBatt_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D); be_define_const_str(true, "true", 1303515621u, 61, 4, NULL); be_define_const_str(try, "try", 2887626766u, 68, 3, NULL); -be_define_const_str(try_compile, "try_compile", 4263879840u, 0, 11, &be_const_str_write_file); +be_define_const_str(try_compile, "try_compile", 4263879840u, 0, 11, NULL); be_define_const_str(try_get_bec_version, "try_get_bec_version", 3143116423u, 0, 19, NULL); be_define_const_str(try_remove_file, "try_remove_file", 3025429926u, 0, 15, NULL); -be_define_const_str(try_rule, "try_rule", 1986449405u, 0, 8, &be_const_str_web_add_config_button); +be_define_const_str(try_rule, "try_rule", 1986449405u, 0, 8, NULL); be_define_const_str(try_run_compiled, "try_run_compiled", 2339741218u, 0, 16, NULL); be_define_const_str(type, "type", 1361572173u, 0, 4, NULL); -be_define_const_str(type_error, "type_error", 3789613824u, 0, 10, NULL); +be_define_const_str(type_error, "type_error", 3789613824u, 0, 10, &be_const_str_udp); be_define_const_str(type_to_string, "type_to_string", 1072804509u, 0, 14, NULL); be_define_const_str(udp, "udp", 1253872004u, 0, 3, NULL); be_define_const_str(unknown_X20instruction, "unknown instruction", 1093911841u, 0, 19, NULL); @@ -812,20 +817,21 @@ be_define_const_str(unsubscribe, "unsubscribe", 4190043798u, 0, 11, NULL); be_define_const_str(update, "update", 672109684u, 0, 6, NULL); be_define_const_str(upper, "upper", 176974407u, 0, 5, NULL); be_define_const_str(url_encode, "url_encode", 528392145u, 0, 10, NULL); -be_define_const_str(uuid4, "uuid4", 1153582450u, 0, 5, NULL); +be_define_const_str(uuid4, "uuid4", 1153582450u, 0, 5, &be_const_str_widget_ctor_cb); be_define_const_str(value, "value", 1113510858u, 0, 5, NULL); be_define_const_str(value_error, "value_error", 773297791u, 0, 11, NULL); -be_define_const_str(valuer_error, "valuer_error", 2567947105u, 0, 12, &be_const_str_wire); +be_define_const_str(valuer_error, "valuer_error", 2567947105u, 0, 12, NULL); be_define_const_str(var, "var", 2317739966u, 64, 3, NULL); be_define_const_str(w, "w", 4060888886u, 0, 1, NULL); -be_define_const_str(wd, "wd", 1531424278u, 0, 2, &be_const_str_write_bit); +be_define_const_str(wake_period, "wake_period", 1520770415u, 0, 11, &be_const_str_widget_dtor_cb); +be_define_const_str(wd, "wd", 1531424278u, 0, 2, NULL); be_define_const_str(web_add_button, "web_add_button", 3537875058u, 0, 14, NULL); be_define_const_str(web_add_config_button, "web_add_config_button", 639674325u, 0, 21, NULL); be_define_const_str(web_add_console_button, "web_add_console_button", 3481436192u, 0, 22, NULL); be_define_const_str(web_add_handler, "web_add_handler", 3990174962u, 0, 15, NULL); be_define_const_str(web_add_main_button, "web_add_main_button", 3960367664u, 0, 19, NULL); -be_define_const_str(web_add_management_button, "web_add_management_button", 2738877186u, 0, 25, NULL); -be_define_const_str(web_send, "web_send", 2989941448u, 0, 8, &be_const_str_webclient); +be_define_const_str(web_add_management_button, "web_add_management_button", 2738877186u, 0, 25, &be_const_str_y); +be_define_const_str(web_send, "web_send", 2989941448u, 0, 8, NULL); be_define_const_str(web_send_decimal, "web_send_decimal", 1407210204u, 0, 16, NULL); be_define_const_str(web_sensor, "web_sensor", 2900096972u, 0, 10, NULL); be_define_const_str(webclient, "webclient", 4076389146u, 0, 9, NULL); @@ -846,7 +852,7 @@ be_define_const_str(widget_group_def, "widget_group_def", 1246968785u, 0, 16, NU be_define_const_str(widget_height_def, "widget_height_def", 3131667813u, 0, 17, NULL); be_define_const_str(widget_instance_size, "widget_instance_size", 2055354779u, 0, 20, NULL); be_define_const_str(widget_struct_by_class, "widget_struct_by_class", 3806373842u, 0, 22, NULL); -be_define_const_str(widget_struct_default, "widget_struct_default", 781673633u, 0, 21, &be_const_str_write_flash); +be_define_const_str(widget_struct_default, "widget_struct_default", 781673633u, 0, 21, NULL); be_define_const_str(widget_width_def, "widget_width_def", 3986078862u, 0, 16, NULL); be_define_const_str(width_def, "width_def", 1143717879u, 0, 9, NULL); be_define_const_str(wifi, "wifi", 120087624u, 0, 4, NULL); @@ -855,8 +861,8 @@ be_define_const_str(wire1, "wire1", 3212721419u, 0, 5, NULL); be_define_const_str(wire2, "wire2", 3229499038u, 0, 5, NULL); be_define_const_str(wire_scan, "wire_scan", 2671275880u, 0, 9, NULL); be_define_const_str(write, "write", 3190202204u, 0, 5, NULL); -be_define_const_str(write8, "write8", 3133991532u, 0, 6, NULL); -be_define_const_str(write_bit, "write_bit", 2660990436u, 0, 9, NULL); +be_define_const_str(write8, "write8", 3133991532u, 0, 6, &be_const_str_xy); +be_define_const_str(write_bit, "write_bit", 2660990436u, 0, 9, &be_const_str_year); be_define_const_str(write_bytes, "write_bytes", 1227543792u, 0, 11, NULL); be_define_const_str(write_file, "write_file", 3177658879u, 0, 10, NULL); be_define_const_str(write_flash, "write_flash", 3003629621u, 0, 11, NULL); @@ -865,13 +871,13 @@ be_define_const_str(x, "x", 4245442695u, 0, 1, NULL); be_define_const_str(xy, "xy", 1482915802u, 0, 2, NULL); be_define_const_str(y, "y", 4228665076u, 0, 1, NULL); be_define_const_str(year, "year", 2927578396u, 0, 4, NULL); -be_define_const_str(yield, "yield", 1821831854u, 0, 5, NULL); +be_define_const_str(yield, "yield", 1821831854u, 0, 5, &be_const_str__X7B); be_define_const_str(zero, "zero", 2339366755u, 0, 4, NULL); be_define_const_str(zip, "zip", 2877453236u, 0, 3, NULL); be_define_const_str(_X7B, "{", 4262220314u, 0, 1, NULL); be_define_const_str(_X7Bs_X7DBatt_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D, "{s}Batt Current{m}%.1f mA{e}", 866537156u, 0, 28, NULL); be_define_const_str(_X7Bs_X7DBatt_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D, "{s}Batt Voltage{m}%.3f V{e}", 3184308199u, 0, 27, NULL); -be_define_const_str(_X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D, "{s}Temp AXP{m}%.1f °C{e}", 2622904081u, 0, 28, &be_const_str__X7D); +be_define_const_str(_X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D, "{s}Temp AXP{m}%.1f °C{e}", 2622904081u, 0, 28, NULL); be_define_const_str(_X7Bs_X7DVBus_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D, "{s}VBus Current{m}%.1f mA{e}", 1032721155u, 0, 28, NULL); be_define_const_str(_X7Bs_X7DVBus_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D, "{s}VBus Voltage{m}%.3f V{e}", 165651270u, 0, 27, NULL); be_define_const_str(_X7B_X7D, "{}", 1415952421u, 0, 2, NULL); @@ -1451,437 +1457,440 @@ be_define_const_str(_X7B_X22hasp_X22_X3A_X7B_X22p_X25i_X22_X3A_X22out_X22_X7D_X7 be_define_const_str(_X7B_X22hasp_X22_X3A_X7B_X22p_X25ib_X25i_X22_X3A_X7B_X22event_X22_X3A_X22_X25s_X22_X25s_X7D_X7D_X7D, "{\"hasp\":{\"p%ib%i\":{\"event\":\"%s\"%s}}}", 0u, 0, 36, NULL); static const bstring* const m_string_table[] = { - (const bstring *)&be_const_str__X25, - (const bstring *)&be_const_str_mqtt_listener, - (const bstring *)&be_const_str_Animate_X20pc_X20is_X20out_X20of_X20range, - (const bstring *)&be_const_str_add_anim, - (const bstring *)&be_const_str__X23autoexec_X2Ebe, - NULL, - (const bstring *)&be_const_str_duration, + (const bstring *)&be_const_str_begin, + (const bstring *)&be_const_str_raw, (const bstring *)&be_const_str_reapply, - (const bstring *)&be_const_str_button_pressed, - (const bstring *)&be_const_str__X3Cselect_X20name_X3D_X27zip_X27_X3E, - (const bstring *)&be_const_str__X2Fac, - (const bstring *)&be_const_str_get_ota_slot, - (const bstring *)&be_const_str_read_sensors, - (const bstring *)&be_const_str__p, - NULL, - NULL, - NULL, - NULL, - (const bstring *)&be_const_str__X3F, - NULL, - (const bstring *)&be_const_str_add_handler, - (const bstring *)&be_const_str_BRY_X3A_X20Exception_X3E_X20_X27, - NULL, - NULL, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, - (const bstring *)&be_const_str_load_templates, - (const bstring *)&be_const_str_RELAY, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_info_X28_X25d_X25s_X2C_X25d_X25s_X2C0x_X2508X_X2C0x_X2508X_X2C_X27_X25s_X27_X2C0x_X25X_X29_X3E, - (const bstring *)&be_const_str_MD5, - (const bstring *)&be_const_str__X27_X20_X2D_X20, - (const bstring *)&be_const_str_add_header, - (const bstring *)&be_const_str__settings_ptr, - (const bstring *)&be_const_str__, - (const bstring *)&be_const_str_asin, - (const bstring *)&be_const_str_WS2812, - (const bstring *)&be_const_str__X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_content_send_style, - (const bstring *)&be_const_str__X23, - (const bstring *)&be_const_str_time_reached, - (const bstring *)&be_const_str__X2F_X2Eautoconf, - (const bstring *)&be_const_str_arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj, - (const bstring *)&be_const_str_b, - (const bstring *)&be_const_str__X23init_X2Ebat, - NULL, + (const bstring *)&be_const_str_fulltopic, + (const bstring *)&be_const_str_global, + (const bstring *)&be_const_str_TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, (const bstring *)&be_const_str_AA50, - (const bstring *)&be_const_str__X21_X3D_X3D, - (const bstring *)&be_const_str_set_pixel_color, - (const bstring *)&be_const_str_parse, - (const bstring *)&be_const_str_available, - (const bstring *)&be_const_str_ct, - (const bstring *)&be_const_str_BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, + (const bstring *)&be_const_str_read8, + (const bstring *)&be_const_str_BECDFE, + (const bstring *)&be_const_str_width_def, + (const bstring *)&be_const_str__X5D_X2C_X0A_X20_X20, + (const bstring *)&be_const_str_True, + (const bstring *)&be_const_str__p, + (const bstring *)&be_const_str_energy_struct, NULL, + (const bstring *)&be_const_str_SERIAL_5E1, + (const bstring *)&be_const_str__X3E_X3D, + (const bstring *)&be_const_str_setbits, + (const bstring *)&be_const_str_add_anim, + (const bstring *)&be_const_str__X2Ep, + (const bstring *)&be_const_str_content_response, + (const bstring *)&be_const_str_get_battery_chargin_status, + (const bstring *)&be_const_str_load_otadata, + (const bstring *)&be_const_str__X2F_X2Eautoconf, + (const bstring *)&be_const_str__X2D_X2A, + (const bstring *)&be_const_str_leds, + (const bstring *)&be_const_str_False, + (const bstring *)&be_const_str__X3E, + NULL, + (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, + (const bstring *)&be_const_str_break, + NULL, + (const bstring *)&be_const_str_setrange, + (const bstring *)&be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker, + NULL, + (const bstring *)&be_const_str_pixel_size, + (const bstring *)&be_const_str_assign_rmt, + (const bstring *)&be_const_str_elif, + (const bstring *)&be_const_str_save_before_restart, + (const bstring *)&be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, + (const bstring *)&be_const_str_OPTION_A, + (const bstring *)&be_const_str_keys, + (const bstring *)&be_const_str_SERIAL_6O1, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_sys, + (const bstring *)&be_const_str_get_bri, + NULL, + (const bstring *)&be_const_str_pixels_buffer, + (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X20, + (const bstring *)&be_const_str_load, + (const bstring *)&be_const_str_clear_to, + (const bstring *)&be_const_str_get_MAC, + (const bstring *)&be_const_str_chars_in_string, + (const bstring *)&be_const_str_read_bytes, + (const bstring *)&be_const_str__X2Etapp, + (const bstring *)&be_const_str__X2C, NULL, - (const bstring *)&be_const_str_tele, - (const bstring *)&be_const_str_json_append, (const bstring *)&be_const_str_arg_size, + (const bstring *)&be_const_str__persist_X2Ejson, + (const bstring *)&be_const_str_class, + (const bstring *)&be_const_str_continue, + (const bstring *)&be_const_str_SERIAL_6N1, + (const bstring *)&be_const_str_debug, + NULL, NULL, (const bstring *)&be_const_str_Partition, - (const bstring *)&be_const_str_tan, - (const bstring *)&be_const_str_codedump, - (const bstring *)&be_const_str_00, - (const bstring *)&be_const_str_elif, - (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, - NULL, - (const bstring *)&be_const_str_create_custom_widget, - (const bstring *)&be_const_str__cmd, - (const bstring *)&be_const_str_gc, - (const bstring *)&be_const_str_enabled, - (const bstring *)&be_const_str__global_addr, - (const bstring *)&be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, (const bstring *)&be_const_str__X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, - (const bstring *)&be_const_str_readbytes, - (const bstring *)&be_const_str__X21_X3D, - (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, + NULL, + (const bstring *)&be_const_str_I2C_Driver, + (const bstring *)&be_const_str__X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, + (const bstring *)&be_const_str_state, + (const bstring *)&be_const_str_page_autoconf_mgr, + NULL, + NULL, + (const bstring *)&be_const_str__X2Esize, + (const bstring *)&be_const_str_get, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, + NULL, + (const bstring *)&be_const_str_frombytes, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20removing_X20autoconf_X20files, + (const bstring *)&be_const_str_, + (const bstring *)&be_const_str_format, + (const bstring *)&be_const_str_DIMMER, + (const bstring *)&be_const_str_json_append, + (const bstring *)&be_const_str_json_fdump_any, + (const bstring *)&be_const_str__begin_transmission, + (const bstring *)&be_const_str__X23autoexec_X2Ebat, + (const bstring *)&be_const_str_web_send, + (const bstring *)&be_const_str_base_class, + (const bstring *)&be_const_str_bytes, + (const bstring *)&be_const_str_EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + (const bstring *)&be_const_str_publish_result, + (const bstring *)&be_const_str__change_buffer, + (const bstring *)&be_const_str_SERIAL_7E2, + (const bstring *)&be_const_str_md5, + (const bstring *)&be_const_str_active_otadata, + NULL, + (const bstring *)&be_const_str_is_ota, + (const bstring *)&be_const_str_Auto_X2Dconfiguration, + (const bstring *)&be_const_str_nvskeys, + (const bstring *)&be_const_str_char, + (const bstring *)&be_const_str__X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, + (const bstring *)&be_const_str__X20_X20, + (const bstring *)&be_const_str_gamma, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, + (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, + (const bstring *)&be_const_str_push, + (const bstring *)&be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, + (const bstring *)&be_const_str__X20, + (const bstring *)&be_const_str___lower__, + (const bstring *)&be_const_str__X2Ebec, + NULL, + (const bstring *)&be_const_str_Tele, + (const bstring *)&be_const_str__X21_X3D_X3D, + (const bstring *)&be_const_str_cb_event_closure, + (const bstring *)&be_const_str__X3Cselect_X20name_X3D_X27zip_X27_X3E, + (const bstring *)&be_const_str_MD5, + NULL, + NULL, + NULL, + NULL, (const bstring *)&be_const_str__X29, NULL, - (const bstring *)&be_const_str_remove_cron, - (const bstring *)&be_const_str__X23autoexec_X2Ebat, - (const bstring *)&be_const_str__X0A_X29_X3E, - (const bstring *)&be_const_str_engine, - (const bstring *)&be_const_str_web_sensor, - (const bstring *)&be_const_str_lvgl_timer_dispatch, - (const bstring *)&be_const_str_ismapped, - (const bstring *)&be_const_str__X3C, - (const bstring *)&be_const_str_dump, - (const bstring *)&be_const_str_seq1, - (const bstring *)&be_const_str_FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, - (const bstring *)&be_const_str__class, - (const bstring *)&be_const_str_fromptr, - (const bstring *)&be_const_str_GET, - (const bstring *)&be_const_str__X5D_X2C_X0A_X20_X20, - (const bstring *)&be_const_str_False, - (const bstring *)&be_const_str_from_to, - NULL, - (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, - (const bstring *)&be_const_str_char, - (const bstring *)&be_const_str__X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, - (const bstring *)&be_const_str_contains, - (const bstring *)&be_const_str__X3D_X3C_X3E_X21, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, - (const bstring *)&be_const_str_Partition_info, - (const bstring *)&be_const_str__X20, - (const bstring *)&be_const_str_get_alternate, - (const bstring *)&be_const_str_decompress, - (const bstring *)&be_const_str__X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, - (const bstring *)&be_const_str__X3E_X3D, - (const bstring *)&be_const_str__X7Bs_X7DBatt_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D, - (const bstring *)&be_const_str_as, - (const bstring *)&be_const_str_label, - NULL, - (const bstring *)&be_const_str__archive, - (const bstring *)&be_const_str_set_pwm, - (const bstring *)&be_const_str_True, - (const bstring *)&be_const_str_insert, - (const bstring *)&be_const_str_escape, - NULL, - (const bstring *)&be_const_str_while, - (const bstring *)&be_const_str_ctypes_bytes, - (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, - (const bstring *)&be_const_str__X2E_X2E, - NULL, - (const bstring *)&be_const_str_Tasmota, - (const bstring *)&be_const_str__X3Clambda_X3E, - (const bstring *)&be_const_str_HTTP_GET, - (const bstring *)&be_const_str_read_bytes, - (const bstring *)&be_const_str_tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29, - (const bstring *)&be_const_str_addr, - (const bstring *)&be_const_str_isnan, - (const bstring *)&be_const_str_check_privileged_access, - (const bstring *)&be_const_str__request_from, - NULL, - (const bstring *)&be_const_str_digital_read, - (const bstring *)&be_const_str_Wire, - (const bstring *)&be_const_str_BRY_X3A_X20argument_X20must_X20be_X20a_X20function, - (const bstring *)&be_const_str_SERIAL_8N2, - (const bstring *)&be_const_str__X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, - NULL, - (const bstring *)&be_const_str_bool, - (const bstring *)&be_const_str__X2E, - (const bstring *)&be_const_str_hex, - (const bstring *)&be_const_str_BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29, - (const bstring *)&be_const_str_crc16, - (const bstring *)&be_const_str_file, - NULL, - (const bstring *)&be_const_str_content_start, - (const bstring *)&be_const_str__X2Esize, - (const bstring *)&be_const_str_imin, - (const bstring *)&be_const_str__X3D, - (const bstring *)&be_const_str_make_cb, - (const bstring *)&be_const_str_SERIAL_6N2, - (const bstring *)&be_const_str_CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, - NULL, - (const bstring *)&be_const_str__crons, - (const bstring *)&be_const_str_import, - (const bstring *)&be_const_str_update, - (const bstring *)&be_const_str_I2C_Driver, - (const bstring *)&be_const_str_CFG_X3A_X20removing_X20autoconf_X20files, - (const bstring *)&be_const_str_ctor, - (const bstring *)&be_const_str_collect, - (const bstring *)&be_const_str_getbits, - (const bstring *)&be_const_str_Unknown, - (const bstring *)&be_const_str__X5D, - (const bstring *)&be_const_str_alternate, - (const bstring *)&be_const_str_json_fdump_any, - (const bstring *)&be_const_str__available, - (const bstring *)&be_const_str_last_modified, - (const bstring *)&be_const_str_json, - (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, - (const bstring *)&be_const_str__X2Eautoconf, - (const bstring *)&be_const_str__X26lt_X3BNone_X26gt_X3B, - (const bstring *)&be_const_str_content_response, - (const bstring *)&be_const_str_has_factory, - (const bstring *)&be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, - (const bstring *)&be_const_str_widget_struct_default, - (const bstring *)&be_const_str_constructor_cb, - (const bstring *)&be_const_str_Partition_otadata, - NULL, - (const bstring *)&be_const_str_check_not_method, - NULL, - (const bstring *)&be_const_str_w, - (const bstring *)&be_const_str_members, - (const bstring *)&be_const_str_widget_dtor_cb, - (const bstring *)&be_const_str_class_init_obj, - NULL, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, - (const bstring *)&be_const_str_continue, - NULL, - (const bstring *)&be_const_str_SERIAL_8O1, - NULL, - NULL, - (const bstring *)&be_const_str_gamma8, - (const bstring *)&be_const_str_get_MAC, - (const bstring *)&be_const_str_cos, - (const bstring *)&be_const_str_light_X20must_X20be_X20of_X20class_X20_X27light_state_X27, - NULL, - (const bstring *)&be_const_str_hue, - (const bstring *)&be_const_str_md5, - (const bstring *)&be_const_str_log10, - (const bstring *)&be_const_str__X5B, - (const bstring *)&be_const_str_SERIAL_8N1, - (const bstring *)&be_const_str_tob64, - (const bstring *)&be_const_str_lv_coord_arr, - (const bstring *)&be_const_str_scale_uint, - (const bstring *)&be_const_str__X2F, - NULL, - (const bstring *)&be_const_str_set_reachable, - (const bstring *)&be_const_str_get_power, - (const bstring *)&be_const_str_acos, - (const bstring *)&be_const_str_dirty, (const bstring *)&be_const_str_SERIAL_7O2, - (const bstring *)&be_const_str_assign_rmt, - (const bstring *)&be_const_str_floor, - (const bstring *)&be_const_str_EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, - (const bstring *)&be_const_str_resolvecmnd, - (const bstring *)&be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker, - (const bstring *)&be_const_str_tohex, - (const bstring *)&be_const_str_bri, - (const bstring *)&be_const_str_RGBW, - (const bstring *)&be_const_str_No_X20SPIFFS_X20partition_X20found, - NULL, - NULL, - (const bstring *)&be_const_str_SERIAL_7N1, - (const bstring *)&be_const_str_gen_cb, - (const bstring *)&be_const_str__dirty, - (const bstring *)&be_const_str_groups, - (const bstring *)&be_const_str___upper__, - (const bstring *)&be_const_str_destructor_cb, - (const bstring *)&be_const_str__X2Elen, - NULL, - (const bstring *)&be_const_str_class, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, - (const bstring *)&be_const_str_i2c_enabled, - (const bstring *)&be_const_str_arch, - (const bstring *)&be_const_str_exec_cmd, - (const bstring *)&be_const_str_gpio, - (const bstring *)&be_const_str__X2Etapp, - (const bstring *)&be_const_str__X2Ebec, - (const bstring *)&be_const_str_ins_ramp, - (const bstring *)&be_const_str_toupper, - (const bstring *)&be_const_str__X23display_X2Eini, - (const bstring *)&be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, - (const bstring *)&be_const_str_SERIAL_8E2, - (const bstring *)&be_const_str_Restart_X201, - (const bstring *)&be_const_str_count, - (const bstring *)&be_const_str__X3C_X3D, - (const bstring *)&be_const_str_calldepth, - (const bstring *)&be_const_str_do, - (const bstring *)&be_const_str__X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, NULL, (const bstring *)&be_const_str__X28_X29, - (const bstring *)&be_const_str__X0A, - (const bstring *)&be_const_str__X2Ep, - (const bstring *)&be_const_str_SERIAL_8O2, - (const bstring *)&be_const_str_delete_all_configs, - (const bstring *)&be_const_str_wire2, - (const bstring *)&be_const_str_get_free_heap, - NULL, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, - NULL, - (const bstring *)&be_const_str__settings_def, - (const bstring *)&be_const_str_call_native, - (const bstring *)&be_const_str_type, - (const bstring *)&be_const_str__X3C_X2Fform_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, - (const bstring *)&be_const_str_next, - (const bstring *)&be_const_str_get_current_module_name, - NULL, - (const bstring *)&be_const_str_input, - (const bstring *)&be_const_str__X20_X20, - (const bstring *)&be_const_str_event_cb, - (const bstring *)&be_const_str_get_option, - (const bstring *)&be_const_str_ins_goto, - (const bstring *)&be_const_str__t, - (const bstring *)&be_const_str_light_state, - (const bstring *)&be_const_str_, - (const bstring *)&be_const_str_0x_X2502X, - (const bstring *)&be_const_str_BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, - (const bstring *)&be_const_str_animators, - (const bstring *)&be_const_str__X22_X3A, - (const bstring *)&be_const_str_files, - (const bstring *)&be_const_str_widget_height_def, - (const bstring *)&be_const_str_invalid_X20GPIO_X20number, - NULL, + (const bstring *)&be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, (const bstring *)&be_const_str_AXP192, - (const bstring *)&be_const_str_zero, - (const bstring *)&be_const_str__error, - (const bstring *)&be_const_str_SERIAL_7N2, - (const bstring *)&be_const_str_CT, - (const bstring *)&be_const_str__read, - (const bstring *)&be_const_str_id, - (const bstring *)&be_const_str_SERIAL_6E1, - (const bstring *)&be_const_str_instance_X20required, - (const bstring *)&be_const_str__ccmd, - (const bstring *)&be_const_str_Parameter_X20error, - (const bstring *)&be_const_str_resp_cmnd_error, - (const bstring *)&be_const_str_BECDFE, - NULL, - (const bstring *)&be_const_str__X3D_X3D, - (const bstring *)&be_const_str_get_cb_list, - (const bstring *)&be_const_str_TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, - (const bstring *)&be_const_str_f, - (const bstring *)&be_const_str_every_250ms, - (const bstring *)&be_const_str_Leds, - (const bstring *)&be_const_str_MAX_RMT, - (const bstring *)&be_const_str_read8, - (const bstring *)&be_const_str__X25s_X2Eautoconf, - (const bstring *)&be_const_str_wd, - (const bstring *)&be_const_str_argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, - (const bstring *)&be_const_str_get_switch, - (const bstring *)&be_const_str_coredump, - (const bstring *)&be_const_str_remove_rule, - (const bstring *)&be_const_str_Auto_X2Dconfiguration, - (const bstring *)&be_const_str_item, - (const bstring *)&be_const_str_Unknown_X20command, - (const bstring *)&be_const_str_type_to_string, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_X28_X5B_X0A, - (const bstring *)&be_const_str_clear_first_time, - (const bstring *)&be_const_str_lv, - (const bstring *)&be_const_str_get_aps_voltage, - (const bstring *)&be_const_str_int64, - (const bstring *)&be_const_str__persist_X2Ejson, - (const bstring *)&be_const_str_RGB, - NULL, - (const bstring *)&be_const_str__X2Fstate_X2F, - (const bstring *)&be_const_str__rules, - (const bstring *)&be_const_str_add, - (const bstring *)&be_const_str_introspect, - (const bstring *)&be_const_str_add_driver, - (const bstring *)&be_const_str_SERIAL_6O1, - (const bstring *)&be_const_str_ccronexpr, - (const bstring *)&be_const_str__X2F_X3Frst_X3D, - (const bstring *)&be_const_str_autoexec, - (const bstring *)&be_const_str_exp, - (const bstring *)&be_const_str__X2D, - (const bstring *)&be_const_str__rmt, - (const bstring *)&be_const_str_crc32_ota_seq, - (const bstring *)&be_const_str_Tele, - (const bstring *)&be_const_str_true, - (const bstring *)&be_const_str_eth, - (const bstring *)&be_const_str_pixels_buffer, - (const bstring *)&be_const_str_deinit, - (const bstring *)&be_const_str_find_op, - (const bstring *)&be_const_str_fulltopic, - (const bstring *)&be_const_str_BUTTON_CONFIGURATION, - (const bstring *)&be_const_str_display_X2Eini, - (const bstring *)&be_const_str__X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, - NULL, - (const bstring *)&be_const_str_dac_voltage, - (const bstring *)&be_const_str_write_bytes, - (const bstring *)&be_const_str_get_pixel_color, - (const bstring *)&be_const_str__X2Ebe, - (const bstring *)&be_const_str__X7B_X7D, - (const bstring *)&be_const_str_begin_multicast, - (const bstring *)&be_const_str__X2Ew, - NULL, - (const bstring *)&be_const_str_SERIAL_6E2, - (const bstring *)&be_const_str_tomap, - (const bstring *)&be_const_str_attrdump, - NULL, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, - NULL, - (const bstring *)&be_const_str__X20_X28, - NULL, - (const bstring *)&be_const_str_add_cron, - NULL, - NULL, - (const bstring *)&be_const_str_style_prop_arr, - (const bstring *)&be_const_str__validate, - (const bstring *)&be_const_str_CFG_X3A_X20downloading_X20_X27_X25s_X27, - (const bstring *)&be_const_str_start, - (const bstring *)&be_const_str__X2B, - (const bstring *)&be_const_str_BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, - (const bstring *)&be_const_str__drivers, - NULL, - (const bstring *)&be_const_str_solidified, + (const bstring *)&be_const_str_argument_X20must_X20be_X20a_X20function, + (const bstring *)&be_const_str__X23display_X2Eini, + (const bstring *)&be_const_str__X23autoexec_X2Ebe, + (const bstring *)&be_const_str_find, + (const bstring *)&be_const_str_get_bat_current, (const bstring *)&be_const_str__X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, - (const bstring *)&be_const_str_preinit, - (const bstring *)&be_const_str__X3E, - (const bstring *)&be_const_str_compress, - (const bstring *)&be_const_str_SERIAL_6N1, - (const bstring *)&be_const_str__X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, - (const bstring *)&be_const_str_manuf, - (const bstring *)&be_const_str_sys, - (const bstring *)&be_const_str_finish, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found, - (const bstring *)&be_const_str_CFG_X3A_X20loading_X20_X27_X25s_X27, - (const bstring *)&be_const_str_return, - (const bstring *)&be_const_str_set, - (const bstring *)&be_const_str_display, - (const bstring *)&be_const_str_BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, - (const bstring *)&be_const_str_frombytes, - (const bstring *)&be_const_str_min, - (const bstring *)&be_const_str_autorun, - (const bstring *)&be_const_str___lower__, - NULL, - (const bstring *)&be_const_str__X22, - (const bstring *)&be_const_str_RGBCT, - (const bstring *)&be_const_str_isinstance, - (const bstring *)&be_const_str__X23preinit_X2Ebe, - (const bstring *)&be_const_str_every_100ms, - (const bstring *)&be_const_str_except, - (const bstring *)&be_const_str_set_temp, - NULL, - (const bstring *)&be_const_str_cb_obj, - (const bstring *)&be_const_str_ctypes_bytes_dyn, - (const bstring *)&be_const_str__global_def, - (const bstring *)&be_const_str_load_otadata, - (const bstring *)&be_const_str_resp_cmnd, - (const bstring *)&be_const_str_LVG_X3A_X20call_X20to_X20unsupported_X20callback, - (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_setbits, - (const bstring *)&be_const_str_atan2, - (const bstring *)&be_const_str_json_fdump_map, - (const bstring *)&be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, - NULL, - NULL, - (const bstring *)&be_const_str_pixel_count, - (const bstring *)&be_const_str_try_remove_file, - (const bstring *)&be_const_str_editable, - (const bstring *)&be_const_str_None, - (const bstring *)&be_const_str_target, (const bstring *)&be_const_str_arg_name, - NULL + (const bstring *)&be_const_str_SERIAL_8E1, + (const bstring *)&be_const_str_get_name, + (const bstring *)&be_const_str_Parameter_X20error, + (const bstring *)&be_const_str_BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, + (const bstring *)&be_const_str__energy, + (const bstring *)&be_const_str_assert, + (const bstring *)&be_const_str_calldepth, + (const bstring *)&be_const_str_resp_cmnd_error, + NULL, + (const bstring *)&be_const_str_Restart_X201, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20loading_X20_X27_X25s_X27, + (const bstring *)&be_const_str__X3Clambda_X3E, + (const bstring *)&be_const_str_fromptr, + (const bstring *)&be_const_str_is_dirty, + (const bstring *)&be_const_str_Animate_X20pc_X20is_X20out_X20of_X20range, + (const bstring *)&be_const_str_model, + (const bstring *)&be_const_str_json_fdump_list, + (const bstring *)&be_const_str_toptr, + (const bstring *)&be_const_str_call_native, + (const bstring *)&be_const_str_button_pressed, + NULL, + (const bstring *)&be_const_str_int64, + (const bstring *)&be_const_str_internal_error, + NULL, + (const bstring *)&be_const_str_SERIAL_7E1, + (const bstring *)&be_const_str_hue_status, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, + (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, + (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, + (const bstring *)&be_const_str_SERIAL_6E1, + (const bstring *)&be_const_str_content_button, + NULL, + (const bstring *)&be_const_str__crons, + (const bstring *)&be_const_str__, + (const bstring *)&be_const_str_get_mem, + (const bstring *)&be_const_str_pi, + (const bstring *)&be_const_str_widget_height_def, + (const bstring *)&be_const_str_type_error, + (const bstring *)&be_const_str_Leds, + (const bstring *)&be_const_str__X2D, + NULL, + (const bstring *)&be_const_str_BRY_X3A_X20argument_X20must_X20be_X20a_X20function, + (const bstring *)&be_const_str_add_handler, + (const bstring *)&be_const_str_add, + (const bstring *)&be_const_str_deg, + (const bstring *)&be_const_str_Partition_info, + NULL, + (const bstring *)&be_const_str_set_huesat, + (const bstring *)&be_const_str__drivers, + (const bstring *)&be_const_str_crc16, + (const bstring *)&be_const_str_ctypes_bytes_dyn, + (const bstring *)&be_const_str__X2Flights_X2F, + (const bstring *)&be_const_str__X2A, + (const bstring *)&be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, + (const bstring *)&be_const_str__X0A, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, + (const bstring *)&be_const_str_CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, + (const bstring *)&be_const_str_MI32, + NULL, + (const bstring *)&be_const_str__X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_point_arr, + (const bstring *)&be_const_str_isinstance, + (const bstring *)&be_const_str_iter, + (const bstring *)&be_const_str_ceil, + (const bstring *)&be_const_str__X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_SERIAL_8O1, + (const bstring *)&be_const_str_SERIAL_8E2, + (const bstring *)&be_const_str_delay, + (const bstring *)&be_const_str_None, + (const bstring *)&be_const_str__X23preinit_X2Ebe, + NULL, + (const bstring *)&be_const_str__validate, + (const bstring *)&be_const_str_traceback, + (const bstring *)&be_const_str_CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, + (const bstring *)&be_const_str__X20_X28, + (const bstring *)&be_const_str_CFG_X3A_X20ran_X20_X20, + (const bstring *)&be_const_str_deregister_obj, + (const bstring *)&be_const_str_target, + NULL, + (const bstring *)&be_const_str_0x_X2502X, + (const bstring *)&be_const_str__X3D_X3C_X3E_X21, + (const bstring *)&be_const_str_get_log, + (const bstring *)&be_const_str_atan, + (const bstring *)&be_const_str__X7Bs_X7DVBus_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D, + (const bstring *)&be_const_str_function, + (const bstring *)&be_const_str_dac_voltage, + (const bstring *)&be_const_str_crc, + (const bstring *)&be_const_str_copy, + (const bstring *)&be_const_str_collect, + (const bstring *)&be_const_str_SERIAL_7N1, + (const bstring *)&be_const_str_add_cron, + (const bstring *)&be_const_str_check_privileged_access, + NULL, + (const bstring *)&be_const_str_sum, + (const bstring *)&be_const_str_floor, + NULL, + (const bstring *)&be_const_str_create_matrix, + (const bstring *)&be_const_str_on, + (const bstring *)&be_const_str__filename, + (const bstring *)&be_const_str__X3Cp_X3E_X3Csmall_X3E_X26nbsp_X3B_X28This_X20feature_X20requires_X20an_X20internet_X20connection_X29_X3C_X2Fsmall_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_connect, + (const bstring *)&be_const_str_height_def, + (const bstring *)&be_const_str_create_segment, + (const bstring *)&be_const_str_every_250ms, + (const bstring *)&be_const_str_is_running, + (const bstring *)&be_const_str_name, + (const bstring *)&be_const_str__X2E, + (const bstring *)&be_const_str_is_first_time, + (const bstring *)&be_const_str_SERIAL_7O1, + (const bstring *)&be_const_str_widget_event, + (const bstring *)&be_const_str__X27_X20_X2D_X20, + (const bstring *)&be_const_str_get_pixel_color, + (const bstring *)&be_const_str_invalid_X20magic_X20number_X20_X2502X, + (const bstring *)&be_const_str_a, + (const bstring *)&be_const_str__X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, + (const bstring *)&be_const_str_remove_cron, + (const bstring *)&be_const_str__ccmd, + (const bstring *)&be_const_str_BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, + (const bstring *)&be_const_str__X2E_X2E, + (const bstring *)&be_const_str_else, + (const bstring *)&be_const_str_get_free_heap, + (const bstring *)&be_const_str_classname, + (const bstring *)&be_const_str_full_status, + (const bstring *)&be_const_str_set_timeouts, + (const bstring *)&be_const_str_POST, + (const bstring *)&be_const_str_Unknown_X20command, + (const bstring *)&be_const_str_ins_time, + (const bstring *)&be_const_str_publish, + (const bstring *)&be_const_str_abs, + NULL, + NULL, + NULL, + (const bstring *)&be_const_str__X3C, + (const bstring *)&be_const_str_RGB, + (const bstring *)&be_const_str_FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + (const bstring *)&be_const_str_BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, + (const bstring *)&be_const_str_add_light, + (const bstring *)&be_const_str__X0A_X29_X3E, + (const bstring *)&be_const_str_tostring, + (const bstring *)&be_const_str__X2Elen, + (const bstring *)&be_const_str_readline, + (const bstring *)&be_const_str__X2Ebe, + (const bstring *)&be_const_str_groups, + (const bstring *)&be_const_str_imax, + (const bstring *)&be_const_str__def, + (const bstring *)&be_const_str_resp_cmnd_failed, + (const bstring *)&be_const_str_gamma10, + (const bstring *)&be_const_str__X25, + (const bstring *)&be_const_str__X22_X3A, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dzip_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, + (const bstring *)&be_const_str_SERIAL_6E2, + (const bstring *)&be_const_str__X26lt_X3BNone_X26gt_X3B, + (const bstring *)&be_const_str_hour, + (const bstring *)&be_const_str__read, + (const bstring *)&be_const_str_content_stop, + (const bstring *)&be_const_str_contains, + (const bstring *)&be_const_str_getfloat, + (const bstring *)&be_const_str__X25s_X2Eautoconf, + (const bstring *)&be_const_str_test, + (const bstring *)&be_const_str_before_del, + (const bstring *)&be_const_str__X22, + NULL, + (const bstring *)&be_const_str_json, + (const bstring *)&be_const_str_import, + NULL, + (const bstring *)&be_const_str__fl, + (const bstring *)&be_const_str_bool, + (const bstring *)&be_const_str_clear, + (const bstring *)&be_const_str__debug_present, + (const bstring *)&be_const_str_pc, + (const bstring *)&be_const_str_BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, + (const bstring *)&be_const_str__X2Eautoconf, + NULL, + (const bstring *)&be_const_str__X2B, + NULL, + (const bstring *)&be_const_str_reverse_gamma10, + (const bstring *)&be_const_str_ctor, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, + (const bstring *)&be_const_str_remove_timer, + (const bstring *)&be_const_str_eth, + (const bstring *)&be_const_str_arch, + (const bstring *)&be_const_str_number, + (const bstring *)&be_const_str_GET, + (const bstring *)&be_const_str_get_bat_charge_current, + NULL, + (const bstring *)&be_const_str__cmd, + (const bstring *)&be_const_str_SERIAL_8O2, + (const bstring *)&be_const_str_SERIAL_5E2, + NULL, + (const bstring *)&be_const_str__dirty, + (const bstring *)&be_const_str__X23init_X2Ebat, + (const bstring *)&be_const_str_remove_rule, + (const bstring *)&be_const_str_offseta, + (const bstring *)&be_const_str_get_current_module_name, + (const bstring *)&be_const_str__X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, + (const bstring *)&be_const_str_WS2812_GRB, + NULL, + (const bstring *)&be_const_str_counters, + (const bstring *)&be_const_str_try_rule, + (const bstring *)&be_const_str_BRY_X3A_X20Exception_X3E_X20_X27, + (const bstring *)&be_const_str_check_not_method, + (const bstring *)&be_const_str_gpio, + (const bstring *)&be_const_str_tobytes, + (const bstring *)&be_const_str_flags, + (const bstring *)&be_const_str_I2C_X3A, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, + NULL, + (const bstring *)&be_const_str_last_modified, + (const bstring *)&be_const_str__X3C_X3D, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_X28_X5B_X0A, + (const bstring *)&be_const_str__buffer, + (const bstring *)&be_const_str_getbits, + (const bstring *)&be_const_str_register_obj, + (const bstring *)&be_const_str_gc, + (const bstring *)&be_const_str__settings_ptr, + (const bstring *)&be_const_str___upper__, + (const bstring *)&be_const_str__request_from, + (const bstring *)&be_const_str_Trigger, + (const bstring *)&be_const_str_insert, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X27_X25s_X27, + (const bstring *)&be_const_str_content_send, + (const bstring *)&be_const_str_SERIAL_5N1, + (const bstring *)&be_const_str_00, + (const bstring *)&be_const_str_couldn_X27t_X20not_X20initialize_X20noepixelbus, + (const bstring *)&be_const_str_lv_timer_cb, + (const bstring *)&be_const_str__X2F, + (const bstring *)&be_const_str__X3D_X3D, + (const bstring *)&be_const_str_Wire, + (const bstring *)&be_const_str_set, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, + (const bstring *)&be_const_str_esphttpd, + (const bstring *)&be_const_str_gamma8, + (const bstring *)&be_const_str__global_def, + (const bstring *)&be_const_str_next, + (const bstring *)&be_const_str_MAX_RMT, + (const bstring *)&be_const_str_web_send_decimal, + (const bstring *)&be_const_str_get_aps_voltage, + (const bstring *)&be_const_str_timer_cb, + (const bstring *)&be_const_str_SERIAL_5O1, + (const bstring *)&be_const_str__X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20return_code_X3D_X25i, + (const bstring *)&be_const_str_bus, + NULL, + (const bstring *)&be_const_str_get_bat_power, + (const bstring *)&be_const_str_o, + (const bstring *)&be_const_str_set_xy, + (const bstring *)&be_const_str_lv_event_cb, + (const bstring *)&be_const_str_event_cb, + (const bstring *)&be_const_str_unsubscribe, + (const bstring *)&be_const_str_digital_write, + (const bstring *)&be_const_str__X3D, + NULL, + (const bstring *)&be_const_str_fast_loop, + (const bstring *)&be_const_str__X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_HTTP_POST, + (const bstring *)&be_const_str_begin_multicast, + (const bstring *)&be_const_str_Too_X20many_X20partiition_X20slots, + (const bstring *)&be_const_str_CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, + (const bstring *)&be_const_str_HTTP_GET, + (const bstring *)&be_const_str_No_X20SPIFFS_X20partition_X20found, + NULL, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_info_X28_X25d_X25s_X2C_X25d_X25s_X2C0x_X2508X_X2C0x_X2508X_X2C_X27_X25s_X27_X2C0x_X25X_X29_X3E, + (const bstring *)&be_const_str_has_factory, + (const bstring *)&be_const_str__X3A, + (const bstring *)&be_const_str_bri, + (const bstring *)&be_const_str_url_encode, + (const bstring *)&be_const_str_var, + (const bstring *)&be_const_str__X2F_X3Frst_X3D, + (const bstring *)&be_const_str_compile, + (const bstring *)&be_const_str_editable, + (const bstring *)&be_const_str_success, + (const bstring *)&be_const_str__global_addr, + (const bstring *)&be_const_str__X5D, + (const bstring *)&be_const_str_now, + (const bstring *)&be_const_str_if, + NULL, + (const bstring *)&be_const_str_settings, + (const bstring *)&be_const_str_constructor_cb, + (const bstring *)&be_const_str__X23, + (const bstring *)&be_const_str_as, + (const bstring *)&be_const_str_null_cb, + (const bstring *)&be_const_str_RGBCT, + (const bstring *)&be_const_str_except, + (const bstring *)&be_const_str_get_size, + (const bstring *)&be_const_str_SERIAL_6N2, + (const bstring *)&be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, + NULL, + (const bstring *)&be_const_str_SERIAL_5N2, + (const bstring *)&be_const_str__X2Fac, + (const bstring *)&be_const_str_file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27 }; static const struct bconststrtab m_const_string_table = { - .size = 427, - .count = 878, + .size = 430, + .count = 884, .table = m_string_table }; diff --git a/lib/libesp32/berry/generate/be_fixed_ULP.h b/lib/libesp32/berry/generate/be_fixed_ULP.h new file mode 100644 index 000000000..cbc74d5dc --- /dev/null +++ b/lib/libesp32/berry/generate/be_fixed_ULP.h @@ -0,0 +1,24 @@ +#include "be_constobj.h" + +static be_define_const_map_slots(m_libULP_map) { + { be_const_key(sleep, -1), be_const_ctype_func(be_ULP_sleep) }, + { be_const_key(get_mem, 6), be_const_ctype_func(be_ULP_get_mem) }, + { be_const_key(run, -1), be_const_ctype_func(be_ULP_run) }, + { be_const_key(load, -1), be_const_ctype_func(be_ULP_load) }, + { be_const_key(adc_config, -1), be_const_ctype_func(be_ULP_adc_config) }, + { be_const_key(set_mem, -1), be_const_ctype_func(be_ULP_set_mem) }, + { be_const_key(gpio_init, 3), be_const_ctype_func(be_ULP_gpio_init) }, + { be_const_key(wake_period, -1), be_const_ctype_func(be_ULP_wake_up_period) }, +}; + +static be_define_const_map( + m_libULP_map, + 8 +); + +static be_define_const_module( + m_libULP, + "ULP" +); + +BE_EXPORT_VARIABLE be_define_const_native_module(ULP); diff --git a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c new file mode 100644 index 000000000..9c6ec5078 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c @@ -0,0 +1,55 @@ +/******************************************************************** + * Tasmota lib + * + * To use: import ULP` + *******************************************************************/ +#include "be_constobj.h" +#include "be_mapping.h" + +#if defined(USE_BERRY_ULP) && defined(CONFIG_IDF_TARGET_ESP32) + +#include "esp32/ulp.h" +#include "driver/rtc_io.h" +#include "driver/gpio.h" +#include "driver/adc.h" + +extern void be_ULP_run(int32_t entry); +BE_FUNC_CTYPE_DECLARE(be_ULP_run, "", "[i]"); + +extern void be_ULP_wake_up_period(int32_t period_index, int32_t period_us); +BE_FUNC_CTYPE_DECLARE(be_ULP_wake_up_period, "", "ii"); + +extern int32_t be_ULP_set_mem(int32_t pos, int32_t value); +BE_FUNC_CTYPE_DECLARE(be_ULP_set_mem, "i", "ii"); + +extern int32_t be_ULP_get_mem(int32_t pos); +BE_FUNC_CTYPE_DECLARE(be_ULP_get_mem, "i", "i"); + +extern int32_t be_ULP_gpio_init(gpio_num_t pin, rtc_gpio_mode_t mode); +BE_FUNC_CTYPE_DECLARE(be_ULP_gpio_init, "i", "ii"); + +extern int32_t be_ULP_adc_config(adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width); +BE_FUNC_CTYPE_DECLARE(be_ULP_adc_config, "i", "iii"); + +extern void be_ULP_sleep(int32_t wake_up_s); +BE_FUNC_CTYPE_DECLARE(be_ULP_sleep, "", "[i]"); // optional int arg + +extern void be_ULP_load(struct bvm *vm, const uint8_t *buf, size_t size); +BE_FUNC_CTYPE_DECLARE(be_ULP_load, "", "@(bytes)~"); // pass: 1/ vm, 2/ bytes point, 3/ bytes size + +#include "be_fixed_ULP.h" + +/* @const_object_info_begin +module ULP (scope: global) { + run, ctype_func(be_ULP_run) + load, ctype_func(be_ULP_load) + set_mem, ctype_func(be_ULP_set_mem) + get_mem, ctype_func(be_ULP_get_mem) + gpio_init, ctype_func(be_ULP_gpio_init) + wake_period, ctype_func(be_ULP_wake_up_period) + sleep, ctype_func(be_ULP_sleep) + adc_config, ctype_func(be_ULP_adc_config) +} +@const_object_info_end */ + +#endif // USE_BERRY_ULP \ No newline at end of file diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino new file mode 100644 index 000000000..993aabceb --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino @@ -0,0 +1,117 @@ +/* + xdrv_52_3_berry_ulp.ino - Berry scripting language, ULP support for ESP32 + + Copyright (C) 2021 Stephan Hadinger & Christian Baars, Berry language by Guan Wenliang https://github.com/Skiars/berry + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifdef USE_BERRY +#ifdef USE_BERRY_ULP +#include + +#if defined(USE_BERRY_ULP) && defined(CONFIG_IDF_TARGET_ESP32) + +#include "esp32/ulp.h" +#include "driver/rtc_io.h" +#include "driver/gpio.h" +#include "driver/adc.h" + +#include "sdkconfig.h" + +extern "C" { + // start the ULP program - always from offset 0 (optional: pass entry) + // The first 32 bits must be a JMP instruction to the start of the program + // + // `ULP.run() -> nil` + void be_ULP_run(int32_t entry) { + ulp_run(entry); // entry point should be at the beginning of program + } + + // `ULP.wake_period(period_index:int, period_us:int) -> nil` + void be_ULP_wake_up_period(int32_t period_index, int32_t period_us) { + ulp_set_wakeup_period(period_index, period_us); + } + + // `ULP.set_mem(position:int, value:int) -> value:int` + int32_t be_ULP_set_mem(int32_t pos, int32_t value) { + RTC_SLOW_MEM[pos]=value; + return value; + } + + // `ULP.get_mem(position:int) -> int` + int32_t be_ULP_get_mem(int32_t pos) { + return RTC_SLOW_MEM[pos] & 0xFFFF; // only low 16 bits are used + } + + // `ULP.gpio_init(pin:int, mode:int) -> rtc_pin:int` + // returns -1 if pin is invalid + int32_t be_ULP_gpio_init(gpio_num_t pin, rtc_gpio_mode_t mode) { + if (rtc_gpio_is_valid_gpio(pin)){ + rtc_gpio_init(pin); + rtc_gpio_set_direction(pin, mode); + return rtc_io_number_get(pin); + } else { + return -1; + } + } + + // `ULP.adc_config(channel:int, attenuation:int, width:int) -> err:int` + // returns combined error like xxx000yyy, xxx for adc1_config_channel_atten and yyy for adc1_config_width + // enums: channel 0-7, attenuation 0-3, width 0-3 + int32_t be_ULP_adc_config(adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width) { + esp_err_t err = adc1_config_channel_atten(channel, attenuation) * 1000000; //shift error left + err += adc1_config_width(width); + if(err==ESP_OK){ + adc1_ulp_enable(); + } + return err; + } + + /** + * @brief Load a Berry byte buffer containing a ULP program as raw byte data + * + * @param vm as `ULP.load(code:bytes) -> nil` + * @return int + */ + void be_ULP_load(struct bvm *vm, const uint8_t *buf, size_t size) { + // AddLog(LOG_LEVEL_INFO, "ULP: load addr=%p size=%i %*_H", buf, size/4, size, buf); + esp_err_t err = ulp_load_binary(0, buf, size / 4); + if (err != ESP_OK) { + be_raisef(vm, "ulp_load_error", "ULP: invalid code err=%i", err); + } + } + + // `ULP.sleep([wake time in seconds:int]) -> nil` + void be_ULP_sleep(int32_t wake_up_s) { + AddLog(LOG_LEVEL_INFO, "ULP: Enter sleep mode."); + WifiShutdown(); + RtcSettingsSave(); + RtcRebootReset(); + + if (wake_up_s) { + AddLog(LOG_LEVEL_INFO, PSTR("ULP: will wake up in %u seconds."), wake_up_s); + esp_sleep_enable_timer_wakeup(wake_up_s * 1000000); + } + esp_sleep_enable_ulp_wakeup(); + esp_deep_sleep_start(); + } + + +} //extern "C" + +#endif //CONFIG_IDF_TARGET_ESP32 + +#endif // USE_BERRY_ULP +#endif // USE_BERRY \ No newline at end of file From 73dd995c3bbb1da9c1c013075445a195ca16727c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 2 Jul 2022 21:46:02 +0200 Subject: [PATCH 056/219] Berry solidify to file --- lib/libesp32/berry/src/be_debug.c | 19 ++- lib/libesp32/berry/src/be_debug.h | 2 +- lib/libesp32/berry/src/be_solidifylib.c | 158 +++++++++--------------- 3 files changed, 73 insertions(+), 106 deletions(-) diff --git a/lib/libesp32/berry/src/be_debug.c b/lib/libesp32/berry/src/be_debug.c index 6184a32ef..2432abaf0 100644 --- a/lib/libesp32/berry/src/be_debug.c +++ b/lib/libesp32/berry/src/be_debug.c @@ -15,6 +15,7 @@ #include "be_strlib.h" #include "be_exec.h" #include "be_mem.h" +#include "be_sys.h" #include #include @@ -46,13 +47,17 @@ static const char* opc2str(bopcode op) return op < array_count(opc_tab) ? opc_tab[op] : "ERROP"; } -void be_print_inst(binstruction ins, int pc) +void be_print_inst(binstruction ins, int pc, void* fout) { char __lbuf[INST_BUF_SIZE]; bopcode op = IGET_OP(ins); logbuf(" %.4X ", pc); - be_writestring(__lbuf); + if (fout) { + be_fwrite(fout, __lbuf, strlen(__lbuf)); + } else { + be_writestring(__lbuf); + } switch (op) { case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: case OP_LT: case OP_LE: case OP_EQ: @@ -132,8 +137,12 @@ void be_print_inst(binstruction ins, int pc) logbuf("%s", opc2str(op)); break; } - be_writestring(__lbuf); - be_writenewline(); + logbuf("%s\n", __lbuf); + if (fout) { + be_fwrite(fout, __lbuf, strlen(__lbuf)); + } else { + be_writestring(__lbuf); + } } #endif @@ -159,7 +168,7 @@ void be_dumpclosure(bclosure *cl) logfmt("; line %d\n", (++lineinfo)->linenumber); } #endif - be_print_inst(*code++, pc); + be_print_inst(*code++, pc, NULL); } } #endif diff --git a/lib/libesp32/berry/src/be_debug.h b/lib/libesp32/berry/src/be_debug.h index 0956e8f8e..b8e2edf4b 100644 --- a/lib/libesp32/berry/src/be_debug.h +++ b/lib/libesp32/berry/src/be_debug.h @@ -22,7 +22,7 @@ bbool be_debug_varname(bvm *vm, int level, int index); bbool be_debug_upvname(bvm *vm, int level, int index); #if BE_USE_DEBUG_MODULE -void be_print_inst(binstruction ins, int pc); +void be_print_inst(binstruction ins, int pc, void* fout); #endif #endif diff --git a/lib/libesp32/berry/src/be_solidifylib.c b/lib/libesp32/berry/src/be_solidifylib.c index 9a545643f..4bf31ed80 100644 --- a/lib/libesp32/berry/src/be_solidifylib.c +++ b/lib/libesp32/berry/src/be_solidifylib.c @@ -15,6 +15,7 @@ #include "be_map.h" #include "be_vm.h" #include "be_decoder.h" +#include "be_sys.h" #include #include #include @@ -25,17 +26,22 @@ extern const bclass be_class_map; #if BE_USE_SOLIDIFY_MODULE #include +#define be_builtin_count(vm) \ + be_vector_count(&(vm)->gbldesc.builtin.vlist) + #ifndef INST_BUF_SIZE #define INST_BUF_SIZE 288 #endif -#define logbuf(...) snprintf(__lbuf, sizeof(__lbuf), __VA_ARGS__) - -#define logfmt(...) \ - do { \ - char __lbuf[INST_BUF_SIZE]; \ - logbuf(__VA_ARGS__); \ - be_writestring(__lbuf); \ +#define logfmt(...) \ + do { \ + char __lbuf[INST_BUF_SIZE]; \ + snprintf(__lbuf, sizeof(__lbuf), __VA_ARGS__); \ + if (fout) { \ + be_fwrite(fout, __lbuf, strlen(__lbuf)); \ + } else { \ + be_writestring(__lbuf); \ + } \ } while (0) /********************************************************************\ @@ -94,63 +100,9 @@ static void toidentifier(char *to, const char *p) *to = 0; // final NULL } +static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const char *classname, const char *key, void* fout); -/********************************************************************\ - * Encode string as literals with simple rules - * - * Encode specifically \\, \n, \" - * All other characters outside of 0x20-0x7F is escaped with \x.. -\********************************************************************/ -static unsigned toliteral_length(const char *s) -{ - unsigned len = 1; - const char * p = s; - while (*p) { - if (*p == '\n' || *p == '\\' || *p == '"') { - len += 2; - } else if (*p >= 0x20 && (*p & 0x80) == 0) { - len++; - } else { - len += 4; /* encode as \x.. */ - } - p++; - } - return len; -} - -static void toliteral(char *to, const char *p) -{ - while (*p) { - if (*p == '\n') { - to[0] = '\\'; - to[1] = 'n'; - to += 2; - } else if (*p == '\\') { - to[0] = '\\'; - to[1] = '\\'; - to += 2; - } else if (*p == '"') { - to[0] = '\\'; - to[1] = '"'; - to += 2; - } else if (*p >= 0x20 && (*p & 0x80) == 0) { - *to = *p; - to++; - } else { - to[0] = '\\'; - to[1] = 'x'; - to[2] = hexdigit((*p & 0xF0) >> 4); - to[3] = hexdigit(*p & 0x0F); - to += 4; - } - p++; - } - *to = 0; // final NULL -} - -static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const char *classname, const char *key); - -static void m_solidify_map(bvm *vm, bbool str_literal, bmap * map, const char *class_name) +static void m_solidify_map(bvm *vm, bbool str_literal, bmap * map, const char *class_name, void* fout) { // compact first be_map_compact(vm, map); @@ -178,10 +130,10 @@ static void m_solidify_map(bvm *vm, bbool str_literal, bmap * map, const char *c } else { logfmt(" { be_const_key_weak(%s, %i), ", id_buf, key_next); } - m_solidify_bvalue(vm, str_literal, &node->value, class_name, str(node->key.v.s)); + m_solidify_bvalue(vm, str_literal, &node->value, class_name, str(node->key.v.s), fout); } else if (node->key.type == BE_INT) { - logfmt(" { be_const_key_int(%i, %i), ", node->key.v.i, key_next); - m_solidify_bvalue(vm, str_literal, &node->value, class_name, NULL); + logfmt(" { be_const_key_int(%lli, %i), ", node->key.v.i, key_next); + m_solidify_bvalue(vm, str_literal, &node->value, class_name, NULL, fout); } else { char error[64]; snprintf(error, sizeof(error), "Unsupported type in key: %i", node->key.type); @@ -194,21 +146,21 @@ static void m_solidify_map(bvm *vm, bbool str_literal, bmap * map, const char *c } -static void m_solidify_list(bvm *vm, bbool str_literal, blist * list, const char *class_name) +static void m_solidify_list(bvm *vm, bbool str_literal, blist * list, const char *class_name, void* fout) { logfmt(" be_nested_list(%i,\n", list->count); logfmt(" ( (struct bvalue*) &(const bvalue[]) {\n"); for (int i = 0; i < list->count; i++) { logfmt(" "); - m_solidify_bvalue(vm, str_literal, &list->data[i], class_name, ""); + m_solidify_bvalue(vm, str_literal, &list->data[i], class_name, "", fout); logfmt(",\n"); } logfmt(" }))"); // TODO need terminal comma? } // pass key name in case of class, or NULL if none -static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const char *classname, const char *key) +static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const char *classname, const char *key, void* fout) { int type = var_primetype(value); switch (type) { @@ -297,16 +249,16 @@ static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const } else { logfmt(" be_const_list( * "); } - m_solidify_bvalue(vm, str_literal, &ins->members[0], classname, key); + m_solidify_bvalue(vm, str_literal, &ins->members[0], classname, key, fout); logfmt(" ) } ))"); } } break; case BE_MAP: - m_solidify_map(vm, str_literal, (bmap *) var_toobj(value), classname); + m_solidify_map(vm, str_literal, (bmap *) var_toobj(value), classname, fout); break; case BE_LIST: - m_solidify_list(vm, str_literal, (blist *) var_toobj(value), classname); + m_solidify_list(vm, str_literal, (blist *) var_toobj(value), classname, fout); break; default: { @@ -317,24 +269,24 @@ static void m_solidify_bvalue(bvm *vm, bbool str_literal, bvalue * value, const } } -static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, int builtins); +static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, void* fout); /* solidify any inner class */ -static void m_solidify_proto_inner_class(bvm *vm, bbool str_literal, bproto *pr, int builtins) +static void m_solidify_proto_inner_class(bvm *vm, bbool str_literal, bproto *pr, void* fout) { // parse any class in constants to output it first if (pr->nconst > 0) { for (int k = 0; k < pr->nconst; k++) { if (var_type(&pr->ktab[k]) == BE_CLASS) { // output the class - m_solidify_subclass(vm, str_literal, (bclass*) var_toobj(&pr->ktab[k]), builtins); + m_solidify_subclass(vm, str_literal, (bclass*) var_toobj(&pr->ktab[k]), fout); } } } } -static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char * func_name, int builtins, int indent) +static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char * func_name, int indent, void* fout) { // const char * func_name = str(pr->name); // const char * func_source = str(pr->source); @@ -364,7 +316,7 @@ static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char size_t sub_len = strlen(func_name) + 10; char sub_name[sub_len]; snprintf(sub_name, sizeof(sub_name), "%s_%d", func_name, i); - m_solidify_proto(vm, str_literal, pr->ptab[i], sub_name, builtins, indent+2); + m_solidify_proto(vm, str_literal, pr->ptab[i], sub_name, indent+2, fout); logfmt(",\n"); } logfmt("%*s}),\n", indent, ""); @@ -377,7 +329,7 @@ static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char logfmt("%*s( &(const bvalue[%2d]) { /* constants */\n", indent, "", pr->nconst); for (int k = 0; k < pr->nconst; k++) { logfmt("%*s/* K%-3d */ ", indent, "", k); - m_solidify_bvalue(vm, str_literal, &pr->ktab[k], NULL, NULL); + m_solidify_bvalue(vm, str_literal, &pr->ktab[k], NULL, NULL, fout); logfmt(",\n"); } logfmt("%*s}),\n", indent, ""); @@ -402,12 +354,12 @@ static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char for (int pc = 0; pc < pr->codesize; pc++) { uint32_t ins = pr->code[pc]; logfmt("%*s 0x%08X, //", indent, "", ins); - be_print_inst(ins, pc); + be_print_inst(ins, pc, fout); bopcode op = IGET_OP(ins); if (op == OP_GETGBL || op == OP_SETGBL) { // check if the global is in built-ins int glb = IGET_Bx(ins); - if (glb > builtins) { + if (glb > be_builtin_count(vm)) { // not supported logfmt("\n===== unsupported global G%d\n", glb); be_raise(vm, "internal_error", "Unsupported access to non-builtin global"); @@ -420,7 +372,7 @@ static void m_solidify_proto(bvm *vm, bbool str_literal, bproto *pr, const char } -static void m_solidify_closure(bvm *vm, bbool str_literal, bclosure *cl, const char * classname, int builtins) +static void m_solidify_closure(bvm *vm, bbool str_literal, bclosure *cl, const char * classname, void* fout) { bproto *pr = cl->proto; const char * func_name = str(pr->name); @@ -432,7 +384,7 @@ static void m_solidify_closure(bvm *vm, bbool str_literal, bclosure *cl, const c int indent = 2; - m_solidify_proto_inner_class(vm, str_literal, pr, builtins); + m_solidify_proto_inner_class(vm, str_literal, pr, fout); logfmt("\n"); logfmt("/********************************************************************\n"); @@ -448,7 +400,7 @@ static void m_solidify_closure(bvm *vm, bbool str_literal, bclosure *cl, const c func_name_id); } - m_solidify_proto(vm, str_literal, pr, func_name, builtins, indent); + m_solidify_proto(vm, str_literal, pr, func_name, indent, fout); logfmt("\n"); // closure @@ -456,7 +408,7 @@ static void m_solidify_closure(bvm *vm, bbool str_literal, bclosure *cl, const c logfmt("/*******************************************************************/\n\n"); } -static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, int builtins) +static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, void* fout) { const char * class_name = str(cl->name); @@ -467,7 +419,7 @@ static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, int buil while ((node = be_map_next(cl->members, &iter)) != NULL) { if (var_isstr(&node->key) && var_isclosure(&node->value)) { bclosure *f = var_toobj(&node->value); - m_solidify_closure(vm, str_literal, f, class_name, builtins); + m_solidify_closure(vm, str_literal, f, class_name, fout); } } } @@ -491,7 +443,7 @@ static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, int buil } if (cl->members) { - m_solidify_map(vm, str_literal, cl->members, class_name); + m_solidify_map(vm, str_literal, cl->members, class_name, fout); logfmt(",\n"); } else { logfmt(" NULL,\n"); @@ -509,10 +461,10 @@ static void m_solidify_subclass(bvm *vm, bbool str_literal, bclass *cl, int buil } -static void m_solidify_class(bvm *vm, bbool str_literal, bclass *cl, int builtins) +static void m_solidify_class(bvm *vm, bbool str_literal, bclass *cl, void* fout) { const char * class_name = str(cl->name); - m_solidify_subclass(vm, str_literal, cl, builtins); + m_solidify_subclass(vm, str_literal, cl, fout); logfmt("/*******************************************************************/\n\n"); logfmt("void be_load_%s_class(bvm *vm) {\n", class_name); @@ -522,7 +474,7 @@ static void m_solidify_class(bvm *vm, bbool str_literal, bclass *cl, int builtin logfmt("}\n"); } -static void m_solidify_module(bvm *vm, bbool str_literal, bmodule *ml, int builtins) +static void m_solidify_module(bvm *vm, bbool str_literal, bmodule *ml, void* fout) { const char * module_name = be_module_name(ml); if (!module_name) { module_name = ""; } @@ -534,11 +486,11 @@ static void m_solidify_module(bvm *vm, bbool str_literal, bmodule *ml, int built while ((node = be_map_next(ml->table, &iter)) != NULL) { if (var_isstr(&node->key) && var_isclosure(&node->value)) { bclosure *f = var_toobj(&node->value); - m_solidify_closure(vm, str_literal, f, module_name, builtins); + m_solidify_closure(vm, str_literal, f, module_name, fout); } if (var_isstr(&node->key) && var_isclass(&node->value)) { bclass *cl = var_toobj(&node->value); - m_solidify_subclass(vm, str_literal, cl, builtins); + m_solidify_subclass(vm, str_literal, cl, fout); } } } @@ -553,7 +505,7 @@ static void m_solidify_module(bvm *vm, bbool str_literal, bmodule *ml, int built logfmt(" \"%s\",\n", module_name); if (ml->table) { - m_solidify_map(vm, str_literal, ml->table, module_name); + m_solidify_map(vm, str_literal, ml->table, module_name, fout); logfmt("\n"); } else { logfmt(" NULL,\n"); @@ -564,23 +516,29 @@ static void m_solidify_module(bvm *vm, bbool str_literal, bmodule *ml, int built } -#define be_builtin_count(vm) \ - be_vector_count(&(vm)->gbldesc.builtin.vlist) - static int m_dump(bvm *vm) { - if (be_top(vm) >= 1) { + int top = be_top(vm); + if (top >= 1) { bvalue *v = be_indexof(vm, 1); bbool str_literal = bfalse; - if (be_top(vm) >= 2) { + if (top >= 2) { str_literal = be_tobool(vm, 2); } + void* fout = NULL; /* output file */ + if (top >= 3 && be_isinstance(vm, 3)) { + be_getmember(vm, 3, ".p"); + if (be_iscomptr(vm, -1)) { + fout = be_tocomptr(vm, -1); + } + be_pop(vm, 1); + } if (var_isclosure(v)) { - m_solidify_closure(vm, str_literal, var_toobj(v), NULL, be_builtin_count(vm)); + m_solidify_closure(vm, str_literal, var_toobj(v), NULL, fout); } else if (var_isclass(v)) { - m_solidify_class(vm, str_literal, var_toobj(v), be_builtin_count(vm)); + m_solidify_class(vm, str_literal, var_toobj(v), fout); } else if (var_ismodule(v)) { - m_solidify_module(vm, str_literal, var_toobj(v), be_builtin_count(vm)); + m_solidify_module(vm, str_literal, var_toobj(v), fout); } else { be_raise(vm, "value_error", "unsupported type"); } From e730f43d268733a19787f45887957f916b091288 Mon Sep 17 00:00:00 2001 From: Christian Baars Date: Sat, 2 Jul 2022 22:33:31 +0200 Subject: [PATCH 057/219] change error reporting for adc_config --- lib/libesp32/berry_tasmota/src/be_ULP_lib.c | 4 ++-- .../xdrv_52_3_berry_ulp.ino | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c index 9c6ec5078..4ba202619 100644 --- a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c @@ -28,8 +28,8 @@ BE_FUNC_CTYPE_DECLARE(be_ULP_get_mem, "i", "i"); extern int32_t be_ULP_gpio_init(gpio_num_t pin, rtc_gpio_mode_t mode); BE_FUNC_CTYPE_DECLARE(be_ULP_gpio_init, "i", "ii"); -extern int32_t be_ULP_adc_config(adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width); -BE_FUNC_CTYPE_DECLARE(be_ULP_adc_config, "i", "iii"); +extern void be_ULP_adc_config(struct bvm *vm, adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width); +BE_FUNC_CTYPE_DECLARE(be_ULP_adc_config, "", "iii"); extern void be_ULP_sleep(int32_t wake_up_s); BE_FUNC_CTYPE_DECLARE(be_ULP_sleep, "", "[i]"); // optional int arg diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino index 993aabceb..4dbc24b3c 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_ulp.ino @@ -67,23 +67,24 @@ extern "C" { } } - // `ULP.adc_config(channel:int, attenuation:int, width:int) -> err:int` - // returns combined error like xxx000yyy, xxx for adc1_config_channel_atten and yyy for adc1_config_width + // `ULP.adc_config(channel:int, attenuation:int, width:int) -> nil` + // // enums: channel 0-7, attenuation 0-3, width 0-3 - int32_t be_ULP_adc_config(adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width) { - esp_err_t err = adc1_config_channel_atten(channel, attenuation) * 1000000; //shift error left + void be_ULP_adc_config(struct bvm *vm, adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width) { + esp_err_t err = adc1_config_channel_atten(channel, attenuation); err += adc1_config_width(width); - if(err==ESP_OK){ - adc1_ulp_enable(); - } - return err; + if (err != ESP_OK) { + be_raisef(vm, "ulp_adc_config_error", "ULP: invalid code err=%i", err); + } else { + adc1_ulp_enable(); + } } /** * @brief Load a Berry byte buffer containing a ULP program as raw byte data * * @param vm as `ULP.load(code:bytes) -> nil` - * @return int + * @return void */ void be_ULP_load(struct bvm *vm, const uint8_t *buf, size_t size) { // AddLog(LOG_LEVEL_INFO, "ULP: load addr=%p size=%i %*_H", buf, size/4, size, buf); From ea90138181ecabd48a7b36f3faca41058644034a Mon Sep 17 00:00:00 2001 From: Christian Baars Date: Sat, 2 Jul 2022 22:59:10 +0200 Subject: [PATCH 058/219] add the missing '@' --- lib/libesp32/berry_tasmota/src/be_ULP_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c index 4ba202619..7edf43206 100644 --- a/lib/libesp32/berry_tasmota/src/be_ULP_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_ULP_lib.c @@ -29,7 +29,7 @@ extern int32_t be_ULP_gpio_init(gpio_num_t pin, rtc_gpio_mode_t mode); BE_FUNC_CTYPE_DECLARE(be_ULP_gpio_init, "i", "ii"); extern void be_ULP_adc_config(struct bvm *vm, adc1_channel_t channel, adc_atten_t attenuation, adc_bits_width_t width); -BE_FUNC_CTYPE_DECLARE(be_ULP_adc_config, "", "iii"); +BE_FUNC_CTYPE_DECLARE(be_ULP_adc_config, "", "@iii"); extern void be_ULP_sleep(int32_t wake_up_s); BE_FUNC_CTYPE_DECLARE(be_ULP_sleep, "", "[i]"); // optional int arg From c78c461216d3630150e011e826f40ba5d8b785fc Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sun, 3 Jul 2022 15:56:16 +0200 Subject: [PATCH 059/219] fix number of esp32 uarts --- tasmota/tasmota_xsns_sensor/xsns_53_sml.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino index 29ff1b044..4b5109194 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino @@ -2886,7 +2886,7 @@ init10: RtcSettings.pulse_counter[i] = Settings->pulse_counter[i]; sml_counters[i].sml_cnt_last_ts = millis(); } - uint32_t uart_index = 2; + uint32_t uart_index = SOC_UART_NUM - 1; sml_counter_pinstate = 0; for (uint8_t meters = 0; meters < meters_used; meters++) { if (meter_desc_p[meters].type == 'c') { From 53d2c76cec611fe1f68eae169af146359f6adf42 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 4 Jul 2022 10:46:35 +0200 Subject: [PATCH 060/219] Prep TM1621 support --- tasmota/include/tasmota_template.h | 10 ++++++++++ tasmota/language/af_AF.h | 4 ++++ tasmota/language/bg_BG.h | 4 ++++ tasmota/language/cs_CZ.h | 4 ++++ tasmota/language/de_DE.h | 4 ++++ tasmota/language/el_GR.h | 4 ++++ tasmota/language/en_GB.h | 4 ++++ tasmota/language/es_ES.h | 4 ++++ tasmota/language/fr_FR.h | 4 ++++ tasmota/language/fy_NL.h | 4 ++++ tasmota/language/he_HE.h | 4 ++++ tasmota/language/hu_HU.h | 4 ++++ tasmota/language/it_IT.h | 4 ++++ tasmota/language/ko_KO.h | 4 ++++ tasmota/language/nl_NL.h | 4 ++++ tasmota/language/pl_PL.h | 4 ++++ tasmota/language/pt_BR.h | 4 ++++ tasmota/language/pt_PT.h | 4 ++++ tasmota/language/ro_RO.h | 4 ++++ tasmota/language/ru_RU.h | 4 ++++ tasmota/language/sk_SK.h | 4 ++++ tasmota/language/sv_SE.h | 4 ++++ tasmota/language/tr_TR.h | 4 ++++ tasmota/language/uk_UA.h | 4 ++++ tasmota/language/vi_VN.h | 4 ++++ tasmota/language/zh_CN.h | 4 ++++ tasmota/language/zh_TW.h | 4 ++++ 27 files changed, 114 insertions(+) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 156eebc40..b833a8b0d 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -191,6 +191,7 @@ enum UserSelectablePins { GPIO_BP5758D_CLK, GPIO_BP5758D_DAT, // BP5758D PWM controller GPIO_SM2335_CLK, GPIO_SM2335_DAT, // SM2335 PWM controller GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag + GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display GPIO_SENSOR_END }; enum ProgramSelectablePins { @@ -426,6 +427,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_BP5758D_CLK "|" D_SENSOR_BP5758D_DAT "|" D_SENSOR_SM2335_CLK "|" D_SENSOR_SM2335_DAT "|" D_SENSOR_DFR562_BUSY "|" + D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -612,6 +614,14 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif #endif // USE_DISPLAY +#ifdef USE_DISPLAY_TM1621_SONOFF +// Initial support outside display driver + AGPIO(GPIO_TM1621_CS), + AGPIO(GPIO_TM1621_WR), + AGPIO(GPIO_TM1621_RD), + AGPIO(GPIO_TM1621_DAT), +#endif // USE_DISPLAY_TM1621_SONOFF + #ifdef USE_MAX31865 AGPIO(GPIO_SSPI_MAX31865_CS1) + MAX_MAX31865S, #endif diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 5e2963823..61e9e5c99 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index a95260b9a..091ef3fe5 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 64d7bc2fd..5eb89c9a4 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 36b211fb5..7e574e0c8 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index ac0d2982f..9bb00cc63 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 50f46c05f..958819a01 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 6bb749d32..3ff22557d 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index c71223a16..7abc864ba 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 RX" #define D_SENSOR_WE517_TX "WE517 TX" #define D_SENSOR_WE517_RX "WE517 RX" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index ccbda1e68..c155bffa9 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index f041b1c06..96cf98eb5 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 63845956c..0d5ddbca8 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index c43a76402..6a4ecb8c8 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 - RX" #define D_SENSOR_WE517_TX "WE517 - TX" #define D_SENSOR_WE517_RX "WE517 - RX" +#define D_GPIO_TM1621_CS "TM1621 - CS" +#define D_GPIO_TM1621_WR "TM1621 - WR" +#define D_GPIO_TM1621_RD "TM1621 - RD" +#define D_GPIO_TM1621_DAT "TM1621 - DAT" #define D_SENSOR_TM1637_CLK "TM1637 - CLK" #define D_SENSOR_TM1637_DIO "TM1637 - DIO" #define D_SENSOR_TM1638_CLK "TM1638 - CLK" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index bb3c9c247..091c530e5 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 82f84da06..bd5dcceb8 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 80a976873..59c3059c8 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index a0bbc2fe1..11ebd3d13 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index b0a2c7023..1da1f2e27 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index e820aa9a2..b1921be4b 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index ddc6934a3..d042e83f5 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 4a1122825..10e5aaacf 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 6b7d1889d..1b7a985ee 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 7890f0ae7..1127a96c4 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 951798278..7d9fc0bc5 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 814d637ac..1116e810b 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 508400581..751959218 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index bf239ec28..5945993e8 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -684,6 +684,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" From 2c0f00389898f29f2dcb40840dce4b680a5509b7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:09:03 +0200 Subject: [PATCH 061/219] Add initial Sonoff POWR3xxD display support Add initial Sonoff POWR3xxD display support (#15856) --- .../xdrv_87_tm1621_sonoff.ino | 265 ++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino new file mode 100644 index 000000000..eb9c3228e --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -0,0 +1,265 @@ +/* + xdrv_87_tm1621_sonoff.ino - Sonoff POWR3xxD and THR3xxD display support for Tasmota + + SPDX-FileCopyrightText: 2022 Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef USE_DISPLAY_TM1621_SONOFF +/*********************************************************************************************\ + * Sonoff POWR3xxD and THR3xxD LCD support + * + * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff POWR320D","GPIO":[32,0,224,0,225,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +\*********************************************************************************************/ + +#define XDRV_87 87 + +#define TM1621_PULSE_WIDTH 10 // microseconds (Sonoff = 100) + +#define TM1621_SYS_EN 0x01 // 0b00000001 +#define TM1621_LCD_ON 0x03 // 0b00000011 +#define TM1621_TIMER_DIS 0x04 // 0b00000100 +#define TM1621_WDT_DIS 0x05 // 0b00000101 +#define TM1621_TONE_OFF 0x08 // 0b00001000 +#define TM1621_BIAS 0x29 // 0b00101001 = LCD 1/3 bias 4 commons option +#define TM1621_IRQ_DIS 0x80 // 0b100x0xxx + +const uint8_t tm1621_commands[] = { TM1621_SYS_EN, TM1621_LCD_ON, TM1621_BIAS, TM1621_TIMER_DIS, TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS }; + +const char tm1621_kchar[] PROGMEM = { "0|1|2|3|4|5|6|7|8|9|-| " }; +// 0 1 2 3 4 5 6 7 8 9 - off +const uint8_t tm1621_digit_row[2][12] = {{ 0x5F, 0x50, 0x3D, 0x79, 0x72, 0x6B, 0x6F, 0x51, 0x7F, 0x7B, 0x20, 0x00 }, + { 0xF5, 0x05, 0xB6, 0x97, 0x47, 0xD3, 0xF3, 0x85, 0xF7, 0xD7, 0x02, 0x00 }}; + +struct Tm1621 { + uint8_t buffer[8]; + char row[2][12]; + uint8_t pin_da; + uint8_t pin_cs; + uint8_t pin_rd; + uint8_t pin_wr; + uint8_t state; + bool celsius; + bool fahrenheit; + bool humidity; + bool voltage; + bool kwh; + bool present; +} Tm1621; + +void TM1621StopSequence(void) { + digitalWrite(Tm1621.pin_cs, 1); // Stop command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + digitalWrite(Tm1621.pin_da, 1); // Reset data +} + +void TM1621SendCmnd(uint16_t command) { + uint16_t full_command = (0x0400 | command) << 5; // 0b100cccccccc00000 + digitalWrite(Tm1621.pin_cs, 0); // Start command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + for (uint32_t i = 0; i < 12; i++) { + digitalWrite(Tm1621.pin_wr, 0); // Start write sequence + if (full_command & 0x8000) { + digitalWrite(Tm1621.pin_da, 1); // Set data + } else { + digitalWrite(Tm1621.pin_da, 0); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + digitalWrite(Tm1621.pin_wr, 1); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + full_command <<= 1; + } + TM1621StopSequence(); +} + +void TM1621SendAddress(uint16_t address) { + uint16_t full_address = (address | 0x0140) << 7; // 0b101aaaaaa0000000 + digitalWrite(Tm1621.pin_cs, 0); // Start command sequence + delayMicroseconds(TM1621_PULSE_WIDTH / 2); + for (uint32_t i = 0; i < 9; i++) { + digitalWrite(Tm1621.pin_wr, 0); // Start write sequence + if (full_address & 0x8000) { + digitalWrite(Tm1621.pin_da, 1); // Set data + } else { + digitalWrite(Tm1621.pin_da, 0); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + digitalWrite(Tm1621.pin_wr, 1); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + full_address <<= 1; + } +} + +void TM1621SendCommon(uint8_t common) { + for (uint32_t i = 0; i < 8; i++) { + digitalWrite(Tm1621.pin_wr, 0); // Start write sequence + if (common & 1) { + digitalWrite(Tm1621.pin_da, 1); // Set data + } else { + digitalWrite(Tm1621.pin_da, 0); // Set data + } + delayMicroseconds(TM1621_PULSE_WIDTH); + digitalWrite(Tm1621.pin_wr, 1); // Read data + delayMicroseconds(TM1621_PULSE_WIDTH); + common >>= 1; + } +} + +void TM1621SendRows(void) { +// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Row1 '%s', Row2 '%s'"), Tm1621.row[0], Tm1621.row[1]); + + uint8_t buffer[8] = { 0 }; // TM1621 16-segment 4-bit common buffer + char row[4]; + for (uint32_t j = 0; j < 2; j++) { + // 0.4V => " 04", 0.0A => " ", 1234.5V => "1234" + uint32_t len = strlen(Tm1621.row[j]); + char *dp = nullptr; + int row_idx = len -3; + if (len <= 5) { + dp = strchr(Tm1621.row[j], '.'); + row_idx = len -1; + } + row[3] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; + if ((row_idx >= 0) && dp) { row_idx--; } + row[2] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; + row[1] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; + row[0] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; + +// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump%d %4_H"), j +1, row); + + char command[10]; + char needle[2] = { 0 }; + for (uint32_t i = 0; i < 4; i++) { + needle[0] = row[i]; + int index = GetCommandCode(command, sizeof(command), (const char*)needle, tm1621_kchar); + if (-1 == index) { index = 11; } + uint32_t bidx = (0 == j) ? i : 7 -i; + buffer[bidx] = tm1621_digit_row[j][index]; + } + if (dp) { + if (0 == j) { + buffer[2] |= 0x80; // Row 1 decimal point + } else { + buffer[5] |= 0x08; // Row 2 decimal point + } + } + } + + if (Tm1621.fahrenheit) { buffer[1] |= 0x80; } + if (Tm1621.celsius) { buffer[3] |= 0x80; } + if (Tm1621.kwh) { buffer[4] |= 0x08; } + if (Tm1621.humidity) { buffer[6] |= 0x08; } + if (Tm1621.voltage) { buffer[7] |= 0x08; } + +// AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Dump3 %8_H"), buffer); + + TM1621SendAddress(0x10); // Sonoff only uses the upper 16 Segments + for (uint32_t i = 0; i < 8; i++) { + TM1621SendCommon(buffer[i]); + } + TM1621StopSequence(); +} + +void TM1621PreInit(void) { + if (!PinUsed(GPIO_TM1621_CS) || !PinUsed(GPIO_TM1621_WR) || !PinUsed(GPIO_TM1621_RD) || !PinUsed(GPIO_TM1621_DAT)) { return; } + + Tm1621.present = true; + Tm1621.pin_da = Pin(GPIO_TM1621_DAT); + Tm1621.pin_cs = Pin(GPIO_TM1621_CS); + Tm1621.pin_rd = Pin(GPIO_TM1621_RD); + Tm1621.pin_wr = Pin(GPIO_TM1621_WR); + pinMode(Tm1621.pin_da, OUTPUT); + digitalWrite(Tm1621.pin_da, 1); + pinMode(Tm1621.pin_cs, OUTPUT); + digitalWrite(Tm1621.pin_cs, 1); + pinMode(Tm1621.pin_rd, OUTPUT); + digitalWrite(Tm1621.pin_rd, 1); + pinMode(Tm1621.pin_wr, OUTPUT); + digitalWrite(Tm1621.pin_wr, 1); + + Tm1621.state = 100; + + AddLog(LOG_LEVEL_INFO, PSTR("DSP: TM1621")); +} + +void TM1621Init(void) { + digitalWrite(Tm1621.pin_cs, 0); + delayMicroseconds(80); + digitalWrite(Tm1621.pin_rd, 0); + delayMicroseconds(15); + digitalWrite(Tm1621.pin_wr, 0); + delayMicroseconds(25); + digitalWrite(Tm1621.pin_da, 0); + delayMicroseconds(TM1621_PULSE_WIDTH); + digitalWrite(Tm1621.pin_da, 1); + + for (uint32_t command = 0; command < sizeof(tm1621_commands); command++) { + TM1621SendCmnd(tm1621_commands[command]); + } + + TM1621SendAddress(0x00); + for (uint32_t segment = 0; segment < 16; segment++) { + TM1621SendCommon(0); + } + TM1621StopSequence(); + + snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("----")); + snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("----")); + TM1621SendRows(); +} + +void TM1621Show(void) { + static uint32_t display = 0; + + if (0 == display) { + Tm1621.kwh = false; + ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.voltage[0]); + ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.current[0]); + Tm1621.voltage = true; + display = 1; + } else { + Tm1621.voltage = false; + ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.total[0]); + ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.active_power[0]); + Tm1621.kwh = true; + display = 0; + } + + TM1621SendRows(); +} + +void TM1621EverySecond(void) { + Tm1621.state++; + if (5 == Tm1621.state) { + TM1621Show(); + Tm1621.state = 0; + } + if (102 == Tm1621.state) { + TM1621Init(); + Tm1621.state = 0; + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv87(uint8_t function) { + bool result = false; + + if (FUNC_INIT == function) { + TM1621PreInit(); + } + else if (Tm1621.present) { + switch (function) { + case FUNC_EVERY_SECOND: + TM1621EverySecond(); + break; + } + } + return result; +} + +#endif // USE_DISPLAY_TM1621_SONOFF From 720942e8184322a7859b4f916ea947eeb3f51127 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 5 Jul 2022 17:26:04 +0200 Subject: [PATCH 062/219] Add THR316D display support - Add THR316D display support - Provide supported THR316D template --- .../xdrv_87_tm1621_sonoff.ino | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index eb9c3228e..bc2cba534 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -12,6 +12,7 @@ * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff POWR320D","GPIO":[32,0,224,0,225,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ #define XDRV_87 87 @@ -26,6 +27,8 @@ #define TM1621_BIAS 0x29 // 0b00101001 = LCD 1/3 bias 4 commons option #define TM1621_IRQ_DIS 0x80 // 0b100x0xxx +enum Tm1621Device { TM1621_USER, TM1621_POWR316D, TM1621_THR316D }; + const uint8_t tm1621_commands[] = { TM1621_SYS_EN, TM1621_LCD_ON, TM1621_BIAS, TM1621_TIMER_DIS, TM1621_WDT_DIS, TM1621_TONE_OFF, TM1621_IRQ_DIS }; const char tm1621_kchar[] PROGMEM = { "0|1|2|3|4|5|6|7|8|9|-| " }; @@ -41,6 +44,7 @@ struct Tm1621 { uint8_t pin_rd; uint8_t pin_wr; uint8_t state; + uint8_t device; bool celsius; bool fahrenheit; bool humidity; @@ -165,6 +169,7 @@ void TM1621SendRows(void) { void TM1621PreInit(void) { if (!PinUsed(GPIO_TM1621_CS) || !PinUsed(GPIO_TM1621_WR) || !PinUsed(GPIO_TM1621_RD) || !PinUsed(GPIO_TM1621_DAT)) { return; } + Tm1621.device = (14 == Pin(GPIO_TM1621_DAT)) ? TM1621_POWR316D : (5 == Pin(GPIO_TM1621_DAT)) ? TM1621_THR316D : TM1621_USER; Tm1621.present = true; Tm1621.pin_da = Pin(GPIO_TM1621_DAT); Tm1621.pin_cs = Pin(GPIO_TM1621_CS); @@ -211,23 +216,45 @@ void TM1621Init(void) { } void TM1621Show(void) { - static uint32_t display = 0; - - if (0 == display) { - Tm1621.kwh = false; - ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.voltage[0]); - ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.current[0]); - Tm1621.voltage = true; - display = 1; - } else { - Tm1621.voltage = false; - ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.total[0]); - ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.active_power[0]); - Tm1621.kwh = true; - display = 0; + if (TM1621_POWR316D == Tm1621.device) { + static uint32_t display = 0; + if (0 == display) { + Tm1621.kwh = false; + ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.voltage[0]); + ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.current[0]); + Tm1621.voltage = true; + display = 1; + } else { + Tm1621.voltage = false; + ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &Energy.total[0]); + ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &Energy.active_power[0]); + Tm1621.kwh = true; + display = 0; + } + TM1621SendRows(); } - TM1621SendRows(); + if (TM1621_THR316D == Tm1621.device) { + Tm1621.celsius = false; + Tm1621.fahrenheit = false; + Tm1621.humidity = false; + snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR(" ")); + snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR(" ")); + if (!isnan(TasmotaGlobal.temperature_celsius)) { + float temperature = ConvertTempToFahrenheit(TasmotaGlobal.temperature_celsius); + ext_snprintf_P(Tm1621.row[0], sizeof(Tm1621.row[0]), PSTR("%1_f"), &temperature); + if (Settings->flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit + Tm1621.fahrenheit = true; + } else { + Tm1621.celsius = true; + } + } + if (TasmotaGlobal.humidity > 0.0f) { + Tm1621.humidity = true; + ext_snprintf_P(Tm1621.row[1], sizeof(Tm1621.row[1]), PSTR("%1_f"), &TasmotaGlobal.humidity); + } + TM1621SendRows(); + } } void TM1621EverySecond(void) { From 69de76338c8b9a637fef97ee7c5f58ad29047d05 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 5 Jul 2022 21:14:50 +0200 Subject: [PATCH 063/219] Berry allow setmember() to fail with `false` or `undefined` --- lib/libesp32/berry/src/be_class.c | 18 ++++++++++++++++-- lib/libesp32/berry/src/be_module.c | 15 ++++++++++++++- lib/libesp32/berry/tests/virtual_methods2.be | 11 +++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/libesp32/berry/src/be_class.c b/lib/libesp32/berry/src/be_class.c index 225974aff..90c7b5599 100644 --- a/lib/libesp32/berry/src/be_class.c +++ b/lib/libesp32/berry/src/be_class.c @@ -299,8 +299,8 @@ int be_instance_member(bvm *vm, binstance *instance, bstring *name, bvalue *dst) return BE_NONE; /* if the return value is module `undefined`, consider it is an error */ } } - var_clearstatic(dst); - return type; + var_clearstatic(dst); + return type; } } } @@ -339,6 +339,20 @@ bbool be_instance_setmember(bvm *vm, binstance *o, bstring *name, bvalue *src) vm->top += 4; /* prevent collection results */ be_dofunc(vm, top, 3); /* call method 'member' */ vm->top -= 4; + /* if return value is `false` or `undefined` signal an unknown attribute */ + int type = var_type(vm->top); + if (type == BE_BOOL) { + bbool ret = var_tobool(vm->top); + if (!ret) { + return bfalse; + } + } else if (type == BE_MODULE) { + /* check if the module is named `undefined` */ + bmodule *mod = var_toobj(vm->top); + if (strcmp(be_module_name(mod), "undefined") == 0) { + return bfalse; /* if the return value is module `undefined`, consider it is an error */ + } + } return btrue; } } diff --git a/lib/libesp32/berry/src/be_module.c b/lib/libesp32/berry/src/be_module.c index 935b89bb3..bf7c7720f 100644 --- a/lib/libesp32/berry/src/be_module.c +++ b/lib/libesp32/berry/src/be_module.c @@ -339,7 +339,7 @@ int be_module_attr(bvm *vm, bmodule *module, bstring *attr, bvalue *dst) bmodule *mod = var_toobj(dst); if (strcmp(be_module_name(mod), "undefined") == 0) { return BE_NONE; /* if the return value is module `undefined`, consider it is an error */ - } + } } return type; } @@ -373,6 +373,19 @@ bbool be_module_setmember(bvm *vm, bmodule *module, bstring *attr, bvalue *src) vm->top += 3; /* prevent collection results */ be_dofunc(vm, top, 2); /* call method 'setmember' */ vm->top -= 3; + int type = var_type(vm->top); + if (type == BE_BOOL) { + bbool ret = var_tobool(vm->top); + if (!ret) { + return bfalse; + } + } else if (type == BE_MODULE) { + /* check if the module is named `undefined` */ + bmodule *mod = var_toobj(vm->top); + if (strcmp(be_module_name(mod), "undefined") == 0) { + return bfalse; /* if the return value is module `undefined`, consider it is an error */ + } + } return btrue; } } diff --git a/lib/libesp32/berry/tests/virtual_methods2.be b/lib/libesp32/berry/tests/virtual_methods2.be index 9bb38a9c5..10fee905d 100644 --- a/lib/libesp32/berry/tests/virtual_methods2.be +++ b/lib/libesp32/berry/tests/virtual_methods2.be @@ -1,4 +1,14 @@ #- virtual attributes -# + +def assert_attribute_error(f) + try + f() + assert(false, 'unexpected execution flow') + except .. as e, m + assert(e == 'attribute_error') + end +end + class Ta var a, b, virtual_c def init() @@ -26,3 +36,4 @@ assert(ta.c == 3) ta.c = 30 assert(ta.c == 30) assert(ta.virtual_c == 30) +assert_attribute_error(def() ta.d = 0 end) From e6b4a77abd6621dd49fac12ccfa88cc7f1742983 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 6 Jul 2022 16:58:27 +0200 Subject: [PATCH 064/219] Add ESP32 Sonoff sensor support - Bump version to v12.0.2.3 - Add ESP32 SI7021, THS01 and MS01 support - Fix THRxxD template --- BUILDS.md | 163 ++++---- CHANGELOG.md | 17 +- RELEASENOTES.md | 4 +- .../include/tasmota_configurations_ESP32.h | 1 + tasmota/include/tasmota_version.h | 2 +- tasmota/my_user_config.h | 1 + tasmota/tasmota_support/support_features.ino | 4 +- .../xdrv_87_tm1621_sonoff.ino | 2 +- .../tasmota_xnrg_energy/xnrg_02_cse7766.ino | 3 + .../{xsns_06_dht.ino => xsns_06_dht_v5.ino} | 15 +- .../tasmota_xsns_sensor/xsns_06_dht_v6.ino | 364 ++++++++++++++++++ tools/decode-status.py | 4 +- 12 files changed, 484 insertions(+), 96 deletions(-) rename tasmota/tasmota_xsns_sensor/{xsns_06_dht.ino => xsns_06_dht_v5.ino} (97%) create mode 100644 tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino diff --git a/BUILDS.md b/BUILDS.md index 576c8be6f..65d96802d 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -167,84 +167,85 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_TOF10120 | - | - / - | - | - | - | - | | USE_BM8563 | - | - / - | - | - | - | - | | USE_AM2320 | - | - / - | - | - | - | - | -| USE_T67XX | - | - / - | - | - | - | - | -| USE_HM330X | - | - / - | - | - | - | - | -| USE_HDC2010 | - | - / - | - | - | - | - | -| USE_PCF85363 | - | - / - | - | - | - | - | -| USE_DS3502 | - | - / - | - | - | - | - | -| USE_HYT | - | - / - | - | - | - | - | -| | | | | | | | -| Feature or Sensor | l | t | k | s | i | d | Remarks -| USE_SPI | - | - / - | - | - | - | x | -| USE_RC522 | - | - / - | - | - | - | - | -| USE_MHZ19 | - | - / x | - | x | - | - | -| USE_SENSEAIR | - | - / x | - | x | - | - | -| USE_PMS5003 | - | - / x | - | x | - | - | -| USE_NOVA_SDS | - | - / x | - | x | - | - | -| USE_HPMA | - | - / x | - | x | - | - | -| USE_SERIAL_BRIDGE | - | x / x | x | x | - | x | -| USE_MP3_PLAYER | - | - / x | - | x | - | - | -| USE_AZ7798 | - | - / - | - | - | - | - | -| USE_PN532_HSU | - | - / x | - | x | - | - | -| USE_RDM6300 | - | - / x | - | x | - | - | -| USE_IBEACON | - | - / x | - | x | - | - | -| USE_GPS | - | - / - | - | - | - | - | -| USE_HM10 | - | - / - | - | x | - | - | -| USE_HRXL | - | - / x | - | x | - | - | -| USE_TASMOTA_CLIENT | - | - / - | - | - | - | - | -| USE_OPENTHERM | - | - / - | - | - | - | - | -| USE_MIEL_HVAC | - | - / - | - | - | - | - | -| USE_PROJECTOR_CTRL | - | - / - | - | - | - | - | -| USE_AS608 | - | - / - | - | - | - | - | -| USE_TCP_BRIDGE | - | - / - | - | - | - | - | zbbridge -| | | | | | | | -| USE_NRF24 | - | - / - | - | - | - | - | -| USE_MIBLE | - | - / - | - | - | - | - | -| USE_ZIGBEE | - | - / - | - | - | - | - | -| USE_ZIGBEE_ZNP | - | - / - | - | - | - | - | -| USE_ZIGBEE_EZSP | - | - / - | - | - | - | - | Sonoff ZbBridge -| | | | | | | | -| USE_IR_REMOTE | - | x / - | x | x | x | x | -| USE_IR_RECEIVE | - | x / - | x | x | x | x | -| USE_IR_REMOTE_FULL | - | - / - | - | - | x | - | Enable ALL protocols -| | | | | | | | -| USE_SR04 | - | - / - | - | x | - | - | -| USE_DYP | - | - / - | - | - | - | - | -| USE_TM1638 | - | - / x | - | x | - | - | -| USE_HX711 | - | - / x | - | x | - | - | -| USE_TX2x_WIND_SENSOR | - | - / - | - | - | - | - | -| USE_WINDMETER | - | - / - | - | - | - | - | -| USE_RC_SWITCH | - | - / x | - | x | - | - | -| USE_RF_SENSOR | - | - / x | - | x | - | - | AlectoV2 only -| USE_HRE | - | - / x | - | x | - | - | -| USE_A4988_STEPPER | - | - / - | - | - | - | - | -| USE_NEOPOOL | - | - / - | - | - | - | - | -| USE_FLOWRATEMETER | - | - / - | - | - | - | - | -| | | | | | | | -| Feature or Sensor | l | t | k | s | i | d | Remarks -| USE_DISPLAY | - | - / - | - | - | - | x | -| USE_DISPLAY_LCD | - | - / - | - | - | - | x | -| USE_DISPLAY_SSD1306 | - | - / - | - | - | - | x | -| USE_DISPLAY_MATRIX | - | - / - | - | - | - | x | -| USE_DISPLAY_SH1106 | - | - / - | - | - | - | x | -| USE_DISPLAY_ILI9341 | - | - / - | - | - | - | x | -| USE_DISPLAY_EPAPER_29 | - | - / - | - | - | - | x | -| USE_DISPLAY_EPAPER_42 | - | - / - | - | - | - | x | -| USE_DISPLAY_SSD1351 | - | - / - | - | - | - | x | -| USE_DISPLAY_RA8876 | - | - / - | - | - | - | x | -| USE_DISPLAY_ST7789 | - | - / - | - | - | - | x | -| USE_DISPLAY_TM1637 | - | - / - | - | - | - | x | -| | | | | | | | -| USE_FT5206 | - | - / - | - | - | - | - | -| USE_FTC532 | - | - / - | - | - | - | - | -| USE_BS814A2 | - | - / - | - | - | - | - | -| | | | | | | | -| ESP32 Feature | l | t | k | s | i | d | Remarks -| USE_HALLEFFECT | | / x | | | | | -| USE_MI_ESP32 | | / x | | | | | See SetOption115 -| USE_IBEACON_ESP32 | | / - | | | | | -| USE_WEBCAM | | / - | | | | | -| USE_ETHERNET | | / x | | | | | -| USE_I2S_AUDIO | | / - | | | | | -| USE_TTGO_WATCH | | / - | | | | | -| USE_SONOFF_SPM | | / x | | | | | +| USE_T67XX | - | - / - | - | - | - | - | +| USE_HM330X | - | - / - | - | - | - | - | +| USE_HDC2010 | - | - / - | - | - | - | - | +| USE_PCF85363 | - | - / - | - | - | - | - | +| USE_DS3502 | - | - / - | - | - | - | - | +| USE_HYT | - | - / - | - | - | - | - | +| | | | | | | | +| Feature or Sensor | l | t | k | s | i | d | Remarks +| USE_SPI | - | - / - | - | - | - | x | +| USE_RC522 | - | - / - | - | - | - | - | +| USE_MHZ19 | - | - / x | - | x | - | - | +| USE_SENSEAIR | - | - / x | - | x | - | - | +| USE_PMS5003 | - | - / x | - | x | - | - | +| USE_NOVA_SDS | - | - / x | - | x | - | - | +| USE_HPMA | - | - / x | - | x | - | - | +| USE_SERIAL_BRIDGE | - | x / x | x | x | - | x | +| USE_MP3_PLAYER | - | - / x | - | x | - | - | +| USE_AZ7798 | - | - / - | - | - | - | - | +| USE_PN532_HSU | - | - / x | - | x | - | - | +| USE_RDM6300 | - | - / x | - | x | - | - | +| USE_IBEACON | - | - / x | - | x | - | - | +| USE_GPS | - | - / - | - | - | - | - | +| USE_HM10 | - | - / - | - | x | - | - | +| USE_HRXL | - | - / x | - | x | - | - | +| USE_TASMOTA_CLIENT | - | - / - | - | - | - | - | +| USE_OPENTHERM | - | - / - | - | - | - | - | +| USE_MIEL_HVAC | - | - / - | - | - | - | - | +| USE_PROJECTOR_CTRL | - | - / - | - | - | - | - | +| USE_AS608 | - | - / - | - | - | - | - | +| USE_TCP_BRIDGE | - | - / - | - | - | - | - | zbbridge +| | | | | | | | +| USE_NRF24 | - | - / - | - | - | - | - | +| USE_MIBLE | - | - / - | - | - | - | - | +| USE_ZIGBEE | - | - / - | - | - | - | - | +| USE_ZIGBEE_ZNP | - | - / - | - | - | - | - | +| USE_ZIGBEE_EZSP | - | - / - | - | - | - | - | Sonoff ZbBridge +| | | | | | | | +| USE_IR_REMOTE | - | x / - | x | x | x | x | +| USE_IR_RECEIVE | - | x / - | x | x | x | x | +| USE_IR_REMOTE_FULL | - | - / - | - | - | x | - | Enable ALL protocols +| | | | | | | | +| USE_SR04 | - | - / - | - | x | - | - | +| USE_DYP | - | - / - | - | - | - | - | +| USE_TM1638 | - | - / x | - | x | - | - | +| USE_HX711 | - | - / x | - | x | - | - | +| USE_TX2x_WIND_SENSOR | - | - / - | - | - | - | - | +| USE_WINDMETER | - | - / - | - | - | - | - | +| USE_RC_SWITCH | - | - / x | - | x | - | - | +| USE_RF_SENSOR | - | - / x | - | x | - | - | AlectoV2 only +| USE_HRE | - | - / x | - | x | - | - | +| USE_A4988_STEPPER | - | - / - | - | - | - | - | +| USE_NEOPOOL | - | - / - | - | - | - | - | +| USE_FLOWRATEMETER | - | - / - | - | - | - | - | +| | | | | | | | +| Feature or Sensor | l | t | k | s | i | d | Remarks +| USE_DISPLAY | - | - / - | - | - | - | x | +| USE_DISPLAY_LCD | - | - / - | - | - | - | x | +| USE_DISPLAY_SSD1306 | - | - / - | - | - | - | x | +| USE_DISPLAY_MATRIX | - | - / - | - | - | - | x | +| USE_DISPLAY_SH1106 | - | - / - | - | - | - | x | +| USE_DISPLAY_ILI9341 | - | - / - | - | - | - | x | +| USE_DISPLAY_EPAPER_29 | - | - / - | - | - | - | x | +| USE_DISPLAY_EPAPER_42 | - | - / - | - | - | - | x | +| USE_DISPLAY_SSD1351 | - | - / - | - | - | - | x | +| USE_DISPLAY_RA8876 | - | - / - | - | - | - | x | +| USE_DISPLAY_ST7789 | - | - / - | - | - | - | x | +| USE_DISPLAY_TM1637 | - | - / - | - | - | - | x | +| USE_DISPLAY_TM1621_SONOFF | - | - / x | - | - | - | - | +| | | | | | | | +| USE_FT5206 | - | - / - | - | - | - | - | +| USE_FTC532 | - | - / - | - | - | - | - | +| USE_BS814A2 | - | - / - | - | - | - | - | +| | | | | | | | +| ESP32 Feature | l | t | k | s | i | d | Remarks +| USE_HALLEFFECT | | / x | | | | | +| USE_MI_ESP32 | | / x | | | | | See SetOption115 +| USE_IBEACON_ESP32 | | / - | | | | | +| USE_WEBCAM | | / - | | | | | +| USE_ETHERNET | | / x | | | | | +| USE_I2S_AUDIO | | / - | | | | | +| USE_TTGO_WATCH | | / - | | | | | +| USE_SONOFF_SPM | | / x | | | | | diff --git a/CHANGELOG.md b/CHANGELOG.md index eb048a005..9b0a5f013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,18 +3,23 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development +## [12.0.2.3] +### Added +- Support for Sonoff POWR3xxD and THR3xxD (#15856) + +### Changed +- Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) + +### Fixed + +### Removed + ## [12.0.2.2] ### Added - Command ``GlobalTemp2 1..250`` to select Global Temperature source indexed from teleperiod occurance data (#15834) - Command ``GlobalHum2 1..250`` to select Global Humidity source indexed from teleperiod occurance data (#15834) - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data (#15834) -### Changed - -### Fixed - -### Removed - ## [12.0.2.1] 20220622 ### Added - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs (#15839) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b235a6031..da2b1b939 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -107,16 +107,18 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v12.0.2.2 +## Changelog v12.0.2.3 ### Added - Command ``GlobalTemp2 1..250`` to select Global Temperature source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalHum2 1..250`` to select Global Humidity source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) +- Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Breaking Changed ### Changed +- Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Fixed - ESP32 SendMail not working over ethernet [#15794](https://github.com/arendst/Tasmota/issues/15794) diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h index 7df7efbf6..7d5d0e4b3 100644 --- a/tasmota/include/tasmota_configurations_ESP32.h +++ b/tasmota/include/tasmota_configurations_ESP32.h @@ -697,6 +697,7 @@ //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_THERMOSTAT // Add support for Thermostat #define USE_ETHERNET // Add support for ethernet (+20k code) +#define USE_DISPLAY_TM1621_SONOFF // Add support for TM1621 display driver used by Sonoff POWR3xxD and THR3xxD #ifndef USE_KNX #define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem) diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index 4f5773c7b..2a21c8947 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0C000202; // 12.0.2.2 +const uint32_t VERSION = 0x0C000203; // 12.0.2.3 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index ceb300bf3..2e25186a1 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1007,6 +1007,7 @@ #define USE_ESP32_SENSORS // Add support for ESP32 temperature and optional hall effect sensor //#define USE_SONOFF_SPM // Add support for ESP32 based Sonoff Smart Stackable Power Meter (+11k code) +//#define USE_DISPLAY_TM1621_SONOFF // Add support for TM1621 dsiplay driver used by Sonoff POWR3xxD and THR3xxD //#define USE_ETHERNET // Add support for ethernet (+20k code) // #define USE_WT32_ETH01 // Add support for Wireless-Tag WT32-ETH01 diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index 2e6203aa8..ee301ba9f 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -818,7 +818,9 @@ void ResponseAppendFeatures(void) #if defined(USE_LIGHT) && defined(USE_SM2335) feature8 |= 0x40000000; // xlgt_09_sm2335.ino #endif -// feature8 |= 0x80000000; +#ifdef USE_DISPLAY_TM1621_SONOFF + feature8 |= 0x80000000; // xdrv_87_tm1621_sonoff.ino +#endif } static uint32_t feature9 = 0x00000000; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index bc2cba534..969fb0382 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -12,7 +12,7 @@ * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff POWR320D","GPIO":[32,0,224,0,225,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ #define XDRV_87 87 diff --git a/tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino b/tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino index 552e2b209..296bd6520 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_02_cse7766.ino @@ -64,6 +64,9 @@ void CseReceived(void) { // F2 5A 02 F7 60 00 03 61 00 40 10 05 72 40 51 A6 58 63 10 1B E1 7F 4D 4E - F2 = Power cycle exceeds range - takes too long - No load // 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - 55 = Ok, 61 = Power not valid (load below 5W) // 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36 - 55 = Ok, 71 = Ok + + // 55 5A 02 DB 40 00 03 25 00 3D 18 03 8E CD 4F 0A 60 2A 56 85 61 01 02 1A - OK voltage + // 55 5A 02 DB 40 07 17 1D 00 3D 18 03 8E CD 4F 0A 60 2B EF EA 61 01 02 2C - Wrong voltage // Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck uint8_t header = Cse.rx_buffer[0]; diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v5.ino similarity index 97% rename from tasmota/tasmota_xsns_sensor/xsns_06_dht.ino rename to tasmota/tasmota_xsns_sensor/xsns_06_dht_v5.ino index 1f750a6fa..bfcda401e 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_06_dht.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v5.ino @@ -18,7 +18,7 @@ */ #ifdef ESP8266 -#ifdef USE_DHT +#ifdef USE_DHT_V5 /*********************************************************************************************\ * DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), SI7021 - Temperature and Humidity * @@ -50,7 +50,7 @@ struct DHTSTRUCT { int16_t raw; char stype[12]; int8_t pin; - uint8_t type; + uint16_t type; uint8_t lastresult; } Dht[DHT_MAX_SENSORS]; @@ -111,7 +111,12 @@ bool DhtRead(uint32_t sensor) { } uint32_t i = 0; +#ifdef ESP32 + { portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&mux); +#else noInterrupts(); +#endif if (DhtWaitState(sensor, 0) && DhtWaitState(sensor, 1) && DhtWaitState(sensor, 0)) { for (i = 0; i < 40; i++) { if (!DhtWaitState(sensor, 1)) { break; } @@ -122,7 +127,11 @@ bool DhtRead(uint32_t sensor) { if (!DhtWaitState(sensor, 0)) { break; } } } +#ifdef ESP32 + portEXIT_CRITICAL(&mux); } +#else interrupts(); +#endif AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Read %5_H"), dht_data); @@ -312,5 +321,5 @@ bool Xsns06(uint8_t function) { return result; } -#endif // USE_DHT +#endif // USE_DHT_V5 #endif // ESP8266 diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino new file mode 100644 index 000000000..bd2353560 --- /dev/null +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino @@ -0,0 +1,364 @@ +/* + xsns_06_dht.ino - DHTxx, AM23xx and SI7021 temperature and humidity sensor support for Tasmota + + SPDX-FileCopyrightText: 2022 Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef USE_DHT +/*********************************************************************************************\ + * DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), SI7021, THS01, MS01 - Temperature and Humidity + * + * Reading temperature or humidity takes about 250 milliseconds! + * Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + * + * Changelog + * 20220706 - v6 + * - Consolidate Adafruit DHT library + * - Fix ESP32 interrupt control to solve intermittent results + * 20211229 - Change poll time from to 2 to 4 seconds for better results + * 20211226 - https://github.com/arendst/Tasmota/pull/14173 + * 20210524 - https://github.com/arendst/Tasmota/issues/12180 + * 20200621 - https://github.com/arendst/Tasmota/pull/7468#issuecomment-647067015 + * 20200313 - https://github.com/arendst/Tasmota/issues/7717#issuecomment-585833243 +\*********************************************************************************************/ + +#define XSNS_06 6 + +#define DHT_MAX_SENSORS 4 +#define DHT_MAX_RETRY 8 + +uint32_t dht_maxcycles; +uint8_t dht_data[5]; +uint8_t dht_sensors = 0; +uint8_t dht_pin; +uint8_t dht_pin_out = 0; // Shelly GPIO00 output only +bool dht_active = true; // DHT configured +bool dht_dual_mode = false; // Single pin mode + +struct DHTSTRUCT { + float t = NAN; + float h = NAN; + int16_t raw; + char stype[12]; + int8_t pin; + uint16_t type; + uint8_t lastresult; +} Dht[DHT_MAX_SENSORS]; + +// Expect the signal line to be at the specified level for a period of time and +// return a count of loop cycles spent at that level (this cycle count can be +// used to compare the relative time of two pulses). If more than a millisecond +// ellapses without the level changing then the call fails with a 0 response. +// This is adapted from Arduino's pulseInLong function +uint32_t DhtExpectPulse(bool level) { + uint32_t count = 0; + while (digitalRead(dht_pin) == level) { + if (count++ >= dht_maxcycles) { + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), + (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); + return UINT32_MAX; // Exceeded timeout, fail. + } + } + return count; +} + +bool DhtRead(uint32_t sensor) { + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; + + dht_pin = Dht[sensor].pin; + if (!dht_dual_mode) { + // Go into high impedence state to let pull-up raise data line level and + // start the reading process. + pinMode(dht_pin, INPUT_PULLUP); + delay(1); + + // First set data line low for a period according to sensor type + pinMode(dht_pin, OUTPUT); + digitalWrite(dht_pin, LOW); + } else { + digitalWrite(dht_pin_out, LOW); + } + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + delay(19); // minimum 18ms + break; + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 +// delay(2); // minimum 1ms + delayMicroseconds(2000); // 20200621: See https://github.com/arendst/Tasmota/pull/7468#issuecomment-647067015 + break; + case GPIO_SI7021: // iTead SI7021 +// delayMicroseconds(500); + delayMicroseconds(400); // Higher results in Timeout waiting for start signal high pulse + break; + case GPIO_MS01: // Sonoff MS01 + delayMicroseconds(450); + break; + } + + uint32_t cycles[80]; + uint32_t i = 0; + + // End the start signal by setting data line high for 40 microseconds. + if (!dht_dual_mode) { + pinMode(dht_pin, INPUT_PULLUP); + } else { + digitalWrite(dht_pin_out, HIGH); + } + + // Delay a moment to let sensor pull data line low. + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 + delayMicroseconds(50); + break; + case GPIO_SI7021: // iTead SI7021 + case GPIO_MS01: // Sonoff MS01 + delayMicroseconds(30); // See: https://github.com/letscontrolit/ESPEasy/issues/1798 and 20210524: https://github.com/arendst/Tasmota/issues/12180 + break; + } + + // Now start reading the data line to get the value from the DHT sensor. + + // Turn off interrupts temporarily because the next sections + // are timing critical and we don't want any interruptions. +#ifdef ESP32 + {portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + portENTER_CRITICAL(&mux); +#else + noInterrupts(); +#endif + + // First expect a low signal for ~80 microseconds followed by a high signal + // for ~80 microseconds again. + if ((DhtExpectPulse(LOW) != UINT32_MAX) && (DhtExpectPulse(HIGH) != UINT32_MAX)) { + // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 + // microsecond low pulse followed by a variable length high pulse. If the + // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds + // then it's a 1. We measure the cycle count of the initial 50us low pulse + // and use that to compare to the cycle count of the high pulse to determine + // if the bit is a 0 (high state cycle count < low state cycle count), or a + // 1 (high state cycle count > low state cycle count). Note that for speed + // all the pulses are read into a array and then examined in a later step. + for (i = 0; i < 80; i += 2) { + cycles[i] = DhtExpectPulse(LOW); + if (cycles[i] == UINT32_MAX) { break; } + cycles[i + 1] = DhtExpectPulse(HIGH); + if (cycles[1 + i] == UINT32_MAX) { break; } + } + } +#ifdef ESP32 + portEXIT_CRITICAL(&mux);} +#else + interrupts(); +#endif + + if (i < 80) { return false; } + + // Inspect pulses and determine which ones are 0 (high state cycle count < low + // state cycle count), or 1 (high state cycle count > low state cycle count). + for (int i = 0; i < 40; ++i) { + uint32_t lowCycles = cycles[2 * i]; + uint32_t highCycles = cycles[2 * i + 1]; + dht_data[i / 8] <<= 1; + // Now compare the low and high cycle times to see if the bit is a 0 or 1. + if (highCycles > lowCycles) { + // High cycles are greater than 50us low cycle count, must be a 1. + dht_data[i / 8] |= 1; + } + // Else high cycles are less than (or equal to, a weird case) the 50us low + // cycle count so this must be a zero. Nothing needs to be changed in the + // stored data. + } + + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Read %5_H"), dht_data); + + uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; + if (dht_data[4] != checksum) { + char hex_char[15]; + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), + ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + return false; + } + + float temperature = NAN; + float humidity = NAN; + switch (Dht[sensor].type) { + case GPIO_DHT11: // DHT11 + humidity = dht_data[0]; + // 20200313: DHT11 (Adafruit): + temperature = dht_data[2]; + if (dht_data[3] & 0x80) { + temperature = -1 - temperature; + } + temperature += (dht_data[3] & 0x0f) * 0.1f; +/* + // DHT12 (Adafruit): + temperature = dht_data[2]; + temperature += (dht_data[3] & 0x0f) * 0.1f; + if (dht_data[2] & 0x80) { + temperature *= -1; + } +*/ + break; + case GPIO_DHT22: // DHT21, DHT22, AM2301, AM2302, AM2321 + case GPIO_SI7021: { // iTead SI7021 + humidity = ((dht_data[0] << 8) | dht_data[1]) * 0.1f; + // DHT21/22 (Adafruit): + int16_t temp16 = dht_data[2] << 8 | dht_data[3]; // case 1 : signed 16 bits + if ((dht_data[2] & 0xF0) == 0x80) { // case 2 : negative when high nibble = 0x80 + temp16 = -(0xFFF & temp16); + } + temperature = 0.1f * temp16; + break; + } + case GPIO_MS01: { // Sonoff MS01 + int16_t voltage = ((dht_data[0] << 8) | dht_data[1]); + +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: MS01 %d"), voltage); + + // Rough approximate of soil moisture % (based on values observed in the eWeLink app) + // Observed values are available here: https://gist.github.com/minovap/654cdcd8bc37bb0d2ff338f8d144a509 + + float x; + if (voltage < 15037) { + x = voltage - 15200; + humidity = - FastPrecisePowf(0.0024f * x, 3) - 0.0004f * x + 20.1f; + } + else if (voltage < 22300) { + humidity = - 0.00069f * voltage + 30.6f; + } + else { + x = voltage - 22800; + humidity = - FastPrecisePowf(0.00046f * x, 3) - 0.0004f * x + 15; + } + temperature = 0; + Dht[sensor].raw = voltage; + break; + } + } + if (isnan(temperature) || isnan(humidity)) { + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid reading")); + return false; + } + + if (humidity > 100) { humidity = 100.0f; } + if (humidity < 0) { humidity = 0.1f; } + Dht[sensor].h = ConvertHumidity(humidity); + Dht[sensor].t = ConvertTemp(temperature); + Dht[sensor].lastresult = 0; + + return true; +} + +/********************************************************************************************/ + +bool DhtPinState() { + if (((XdrvMailbox.index >= AGPIO(GPIO_DHT11)) && (XdrvMailbox.index <= AGPIO(GPIO_SI7021))) || + (XdrvMailbox.index == AGPIO(GPIO_MS01))) { + if (dht_sensors < DHT_MAX_SENSORS) { + Dht[dht_sensors].pin = XdrvMailbox.payload; + Dht[dht_sensors].type = BGPIO(XdrvMailbox.index); + dht_sensors++; + XdrvMailbox.index = AGPIO(GPIO_DHT11); + } else { + XdrvMailbox.index = 0; + } + return true; + } + return false; +} + +void DhtInit(void) { + if (dht_sensors) { + if (PinUsed(GPIO_DHT11_OUT)) { + dht_pin_out = Pin(GPIO_DHT11_OUT); + dht_dual_mode = true; // Dual pins mode as used by Shelly + dht_sensors = 1; // We only support one sensor in pseudo mode + pinMode(dht_pin_out, OUTPUT); + } + + for (uint32_t i = 0; i < dht_sensors; i++) { + pinMode(Dht[i].pin, INPUT_PULLUP); + Dht[i].lastresult = DHT_MAX_RETRY; // Start with NAN + GetTextIndexed(Dht[i].stype, sizeof(Dht[i].stype), Dht[i].type, kSensorNames); + if (dht_sensors > 1) { + snprintf_P(Dht[i].stype, sizeof(Dht[i].stype), PSTR("%s%c%02d"), Dht[i].stype, IndexSeparator(), Dht[i].pin); + } + } + + dht_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. + + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v6) " D_SENSORS_FOUND " %d"), dht_sensors); + } else { + dht_active = false; + } +} + +void DhtEverySecond(void) { + if (!(TasmotaGlobal.uptime %4)) { // Every 4 seconds + for (uint32_t sensor = 0; sensor < dht_sensors; sensor++) { + // DHT11 and AM2301 25mS per sensor, SI7021 5mS per sensor + if (!DhtRead(sensor)) { + Dht[sensor].lastresult++; + if (Dht[sensor].lastresult > DHT_MAX_RETRY) { // Reset after 8 misses + Dht[sensor].t = NAN; + Dht[sensor].h = NAN; + } + } + } + } +} + +void DhtShow(bool json) { + for (uint32_t i = 0; i < dht_sensors; i++) { + if (GPIO_MS01 == Dht[i].type) { + if (json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_HUMIDITY "\":%*_f,\"Raw\":%d}"), + Dht[i].stype, Settings->flag2.humidity_resolution, &Dht[i].h, Dht[i].raw); +#ifdef USE_WEBSERVER + } else { + char parameter[FLOATSZ]; + dtostrfd(Dht[i].h, Settings->flag2.humidity_resolution, parameter); + WSContentSend_PD(HTTP_SNS_HUM, Dht[i].stype, parameter); +#endif // USE_WEBSERVER + } + } else { + TempHumDewShow(json, ((0 == TasmotaGlobal.tele_period) && (0 == i)), Dht[i].stype, Dht[i].t, Dht[i].h); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns06(uint8_t function) { + bool result = false; + + if (dht_active) { + switch (function) { + case FUNC_EVERY_SECOND: + DhtEverySecond(); + break; + case FUNC_JSON_APPEND: + DhtShow(1); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + DhtShow(0); + break; +#endif // USE_WEBSERVER + case FUNC_INIT: + DhtInit(); + break; + case FUNC_PIN_STATE: + result = DhtPinState(); + break; + } + } + return result; +} + +#endif // USE_DHT diff --git a/tools/decode-status.py b/tools/decode-status.py index 5a7bba7d6..802852f64 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -280,7 +280,7 @@ a_features = [[ "USE_HDC2010","USE_LSC_MCSL","USE_SONOFF_SPM","USE_SHIFT595", "USE_SDM230","USE_CM110x","USE_BL6523","USE_ADE7880", "USE_PCF85363","USE_DS3502","USE_IMPROV","USE_FLOWRATEMETER", - "USE_BP5758D","USE_HYT","USE_SM2335","" + "USE_BP5758D","USE_HYT","USE_SM2335","USE_DISPLAY_TM1621_SONOFF" ],[ "","","","", "","","","", @@ -317,7 +317,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v11.1.0.3 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v12.0.2.3 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) From f4851dbf5531811216e7c14547a3167c326d799e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 6 Jul 2022 17:01:51 +0200 Subject: [PATCH 065/219] Fix compilation --- tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino b/tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino index 3399bebc4..66ee0f782 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_06_esp32_dht.ino @@ -18,7 +18,7 @@ */ #ifdef ESP32 -#ifdef USE_DHT +#ifdef USE_DHT_V5 /*********************************************************************************************\ * DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), SI7021 - Temperature and Humidity * @@ -155,5 +155,5 @@ bool Xsns06(uint8_t function) { return result; } -#endif // USE_DHT +#endif // USE_DHT_V5 #endif // ESP32 From 37163a2d38b44449aa58fc8bcd8bc9711a258d3c Mon Sep 17 00:00:00 2001 From: Moleus Date: Wed, 6 Jul 2022 21:08:16 +0300 Subject: [PATCH 066/219] Fix build environment name check for esp32. --- pio-tools/post_esp32.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index 686fe2e4f..2a4be8569 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -41,8 +41,9 @@ variants_dir = join(FRAMEWORK_DIR, "variants", "tasmota") def esp32_create_chip_string(chip): tasmota_platform = env.subst("$BUILD_DIR").split(os.path.sep)[-1] tasmota_platform = tasmota_platform.split('-')[0] - if 'tasmota' and chip[3:] not in tasmota_platform: # quick check for a valid name like 'tasmota' + '32c3' + if 'tasmota' + chip[3:] not in tasmota_platform: # quick check for a valid name like 'tasmota' + '32c3' print('Unexpected naming conventions in this build environment -> Undefined behavior for further build process!!') + print("Expected build environment name like 'tasmota32-whatever-you-want'") return tasmota_platform def esp32_build_filesystem(fs_size): From 55b2018fedc42dbfeaa89c5ef8f26238b3beb1a3 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 6 Jul 2022 21:48:00 +0200 Subject: [PATCH 067/219] fix esp8266 compile --- tasmota/tasmota_xsns_sensor/xsns_53_sml.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino index 4b5109194..319c2b4b6 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino @@ -2886,7 +2886,9 @@ init10: RtcSettings.pulse_counter[i] = Settings->pulse_counter[i]; sml_counters[i].sml_cnt_last_ts = millis(); } + #ifdef ESP32 uint32_t uart_index = SOC_UART_NUM - 1; + #endif sml_counter_pinstate = 0; for (uint8_t meters = 0; meters < meters_used; meters++) { if (meter_desc_p[meters].type == 'c') { From 389543d36f06c457539c1bf9ccc045d806d5a626 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 6 Jul 2022 23:13:35 +0200 Subject: [PATCH 068/219] Berry fix regression --- lib/libesp32/berry/src/be_globallib.c | 1 - tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/libesp32/berry/src/be_globallib.c b/lib/libesp32/berry/src/be_globallib.c index 6ff96b204..6597fe78b 100644 --- a/lib/libesp32/berry/src/be_globallib.c +++ b/lib/libesp32/berry/src/be_globallib.c @@ -72,7 +72,6 @@ static int m_setglobal(bvm *vm) if (top >= 2 && be_isstring(vm, 1)) { const char * name = be_tostring(vm, 1); be_setglobal(vm, name); - be_return(vm); } be_return_nil(vm); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino index e571e5a88..08fdb1e88 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_7_berry_embedded.ino @@ -31,8 +31,8 @@ const char berry_prog[] = #ifdef USE_BERRY_PYTHON_COMPAT // enable python syntax compatibility mode "import python_compat " - "import cb " #endif + "import cb " #ifdef USE_ENERGY_SENSOR "import energy " From b28543b479131e16ebe3ab9bc7b45f592692439d Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 7 Jul 2022 11:39:42 +0200 Subject: [PATCH 069/219] Tasmota Core 2.0.4 --- platformio_tasmota32.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index d88649ef2..4d62ccf92 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -38,20 +38,20 @@ extra_scripts = pre:pio-tools/add_c_flags.py ${esp_defaults.extra_scripts} [core32] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-v.2.0.3.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-2.0.4.zip platform_packages = build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} [core32solo1] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-solo1-v.2.0.3.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-solo1-2.0.4.zip platform_packages = build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} [core32itead] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-solo1-v.2.0.3.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/tasmota/esp32-arduino-lib-builder/releases/download/v2.0.3/framework-arduinoespressif32-itead.tar.gz +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-ITEAD-2.0.4.zip +platform_packages = build_unflags = ${esp32_defaults.build_unflags} -build_flags = ${esp32_defaults.build_flags} \ No newline at end of file +build_flags = ${esp32_defaults.build_flags} From 9f8fffd6ba1f9181224b0a8e1184ddd48f915b85 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 7 Jul 2022 13:24:03 +0200 Subject: [PATCH 070/219] Berry fix potential crash when parsing unfinished strings --- lib/libesp32/berry/src/be_lexer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/libesp32/berry/src/be_lexer.c b/lib/libesp32/berry/src/be_lexer.c index ca11141c0..7b3f05726 100644 --- a/lib/libesp32/berry/src/be_lexer.c +++ b/lib/libesp32/berry/src/be_lexer.c @@ -419,6 +419,9 @@ static btokentype scan_string(blexer *lexer) save(lexer); /* skip '\\.' */ } } + if (c == EOS) { + be_lexerror(lexer, "unfinished string"); + } c = next(lexer); /* skip '"' or '\'' */ /* check if there's an additional string literal right after */ skip_delimiter(lexer); From c3856a687308f44e70dd20bc6114197d1bf5da92 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:27:48 +0200 Subject: [PATCH 071/219] sbox support --- .../ESP8266Audio/src/AudioOutputI2S.cpp | 32 +++++++++++++++---- .../ESP8266Audio/src/AudioOutputI2S.h | 20 ++++++++---- 2 files changed, 40 insertions(+), 12 deletions(-) mode change 100644 => 100755 lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp mode change 100644 => 100755 lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h diff --git a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp old mode 100644 new mode 100755 index 0eb3c0da7..a01a7b999 --- a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp +++ b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp @@ -1,7 +1,7 @@ /* AudioOutputI2S Base class for I2S interface port - + Copyright (C) 2017 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify @@ -59,10 +59,13 @@ bool AudioOutputI2S::SetPinout() return false; // Not allowed i2s_pin_config_t pins = { + .mck_io_num = mclkPin, .bck_io_num = bclkPin, .ws_io_num = wclkPin, .data_out_num = doutPin, - .data_in_num = I2S_PIN_NO_CHANGE}; + .data_in_num = dinPin + }; + //.data_in_num = I2S_PIN_NO_CHANGE}; i2s_set_pin((i2s_port_t)portNo, &pins); return true; #else @@ -73,11 +76,15 @@ bool AudioOutputI2S::SetPinout() #endif } -bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout) +bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout, int mclk, int din) { bclkPin = bclk; wclkPin = wclk; doutPin = dout; + mclkPin = mclk; + dinPin = din; + + if (i2sOn) return SetPinout(); @@ -178,7 +185,7 @@ bool AudioOutputI2S::begin(bool txDAC) #if CONFIG_IDF_TARGET_ESP32 mode = (i2s_mode_t)(mode | I2S_MODE_DAC_BUILT_IN); #else - return false; + return false; #endif } else if (output_mode == INTERNAL_PDM) @@ -186,7 +193,7 @@ bool AudioOutputI2S::begin(bool txDAC) #if CONFIG_IDF_TARGET_ESP32 mode = (i2s_mode_t)(mode | I2S_MODE_PDM); #else - return false; + return false; #endif } @@ -212,6 +219,14 @@ bool AudioOutputI2S::begin(bool txDAC) #endif } + if (mclkPin != I2S_PIN_NO_CHANGE) { + use_apll = false; + } + + if (dinPin != I2S_PIN_NO_CHANGE) { + mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX); + } + i2s_config_t i2s_config_dac = { .mode = mode, .sample_rate = 44100, @@ -221,7 +236,12 @@ bool AudioOutputI2S::begin(bool txDAC) .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // lowest interrupt priority .dma_buf_count = dma_buf_count, .dma_buf_len = 128, - .use_apll = use_apll // Use audio PLL + .use_apll = use_apll, // Use audio PLL + .tx_desc_auto_clear = true, + .fixed_mclk = 0, + //.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, + .mclk_multiple = I2S_MCLK_MULTIPLE_128, + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT, }; audioLogger->printf("+%d %p\n", portNo, &i2s_config_dac); if (i2s_driver_install((i2s_port_t)portNo, &i2s_config_dac, 0, NULL) != ESP_OK) diff --git a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h old mode 100644 new mode 100755 index b3f621d28..272959bc0 --- a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h +++ b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h @@ -1,7 +1,7 @@ /* AudioOutputI2S Base class for an I2S output port - + Copyright (C) 2017 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify @@ -19,15 +19,21 @@ */ #pragma once - +#ifdef ESP32 +#include +#endif #include "AudioOutput.h" +#ifndef I2S_PIN_NO_CHANGE +#define I2S_PIN_NO_CHANGE -1 +#endif + class AudioOutputI2S : public AudioOutput { public: #if defined(ESP32) || defined(ESP8266) AudioOutputI2S(int port=0, int output_mode=EXTERNAL_I2S, int dma_buf_count = 8, int use_apll=APLL_DISABLE); - bool SetPinout(int bclkPin, int wclkPin, int doutPin); + bool SetPinout(int bclkPin, int wclkPin, int doutPin, int mclk = I2S_PIN_NO_CHANGE, int din = I2S_PIN_NO_CHANGE); enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; #elif defined(ARDUINO_ARCH_RP2040) @@ -41,10 +47,10 @@ class AudioOutputI2S : public AudioOutput virtual bool ConsumeSample(int16_t sample[2]) override; virtual void flush() override; virtual bool stop() override; - + bool begin(bool txDAC); bool SetOutputModeMono(bool mono); // Force mono output no matter the input - bool SetLsbJustified(bool lsbJustified); // Allow supporting non-I2S chips, e.g. PT8211 + bool SetLsbJustified(bool lsbJustified); // Allow supporting non-I2S chips, e.g. PT8211 protected: bool SetPinout(); @@ -59,8 +65,10 @@ class AudioOutputI2S : public AudioOutput // We can restore the old values and free up these pins when in NoDAC mode uint32_t orig_bck; uint32_t orig_ws; - + uint8_t bclkPin; uint8_t wclkPin; uint8_t doutPin; + uint8_t dinPin; + uint8_t mclkPin; }; From 4f84d0f5e3b53f9475731d174cb8bcf13e037bf6 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:28:45 +0200 Subject: [PATCH 072/219] add sbox codecs --- lib/lib_audio/es7243e/library.json | 7 + lib/lib_audio/es7243e/library.properties | 9 ++ lib/lib_audio/es7243e/src/es7243e.cpp | 98 ++++++++++++ lib/lib_audio/es7243e/src/es7243e.h | 80 ++++++++++ lib/lib_audio/es8156/library.json | 7 + lib/lib_audio/es8156/library.properties | 9 ++ lib/lib_audio/es8156/src/audio_hal.h | 136 ++++++++++++++++ lib/lib_audio/es8156/src/es8156.cpp | 160 +++++++++++++++++++ lib/lib_audio/es8156/src/es8156.h | 180 ++++++++++++++++++++++ lib/lib_audio/es8156/src/esxxx_common.h | 188 +++++++++++++++++++++++ 10 files changed, 874 insertions(+) create mode 100644 lib/lib_audio/es7243e/library.json create mode 100644 lib/lib_audio/es7243e/library.properties create mode 100644 lib/lib_audio/es7243e/src/es7243e.cpp create mode 100644 lib/lib_audio/es7243e/src/es7243e.h create mode 100644 lib/lib_audio/es8156/library.json create mode 100644 lib/lib_audio/es8156/library.properties create mode 100755 lib/lib_audio/es8156/src/audio_hal.h create mode 100644 lib/lib_audio/es8156/src/es8156.cpp create mode 100644 lib/lib_audio/es8156/src/es8156.h create mode 100644 lib/lib_audio/es8156/src/esxxx_common.h diff --git a/lib/lib_audio/es7243e/library.json b/lib/lib_audio/es7243e/library.json new file mode 100644 index 000000000..1ce32c11e --- /dev/null +++ b/lib/lib_audio/es7243e/library.json @@ -0,0 +1,7 @@ +{ + "name": "ES7243e", + "description": "Audio codec", + "keywords": "ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/es7243e/library.properties b/lib/lib_audio/es7243e/library.properties new file mode 100644 index 000000000..61b7ded09 --- /dev/null +++ b/lib/lib_audio/es7243e/library.properties @@ -0,0 +1,9 @@ +name=ES7243e +version=1.0 +author= +maintainer= +sentence=Audio fcodec for ESP32 +paragraph= +category=Signal Output +url= +architectures=esp32 diff --git a/lib/lib_audio/es7243e/src/es7243e.cpp b/lib/lib_audio/es7243e/src/es7243e.cpp new file mode 100644 index 000000000..536c237ec --- /dev/null +++ b/lib/lib_audio/es7243e/src/es7243e.cpp @@ -0,0 +1,98 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifdef ESP32 + + #include + #include + #include "string.h" + #include "esp_log.h" +#include "es7243e.h" + + +static const char *TAG = "DRV7243E"; +static TwoWire *es7243e_wire; + +static esp_err_t es7243e_write_reg(uint8_t reg_addr, uint8_t data) +{ + //return i2c_bus_write_byte(i2c_handle, reg_addr, data); + es7243e_wire->beginTransmission(ES7243_ADDR); + es7243e_wire->write(reg_addr); + es7243e_wire->write(data); + es7243e_wire->endTransmission(); + return 0; +} + +esp_err_t es7243e_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg) +{ + esp_err_t ret = ESP_OK; + + es7243e_wire = tw; + + ret |= es7243e_write_reg(0x01, 0x3A); + ret |= es7243e_write_reg(0x00, 0x80); + ret |= es7243e_write_reg(0xF9, 0x00); + ret |= es7243e_write_reg(0x04, 0x02); + ret |= es7243e_write_reg(0x04, 0x01); + ret |= es7243e_write_reg(0xF9, 0x01); + ret |= es7243e_write_reg(0x00, 0x1E); + ret |= es7243e_write_reg(0x01, 0x00); + + ret |= es7243e_write_reg(0x02, 0x00); + ret |= es7243e_write_reg(0x03, 0x20); + ret |= es7243e_write_reg(0x04, 0x01); + ret |= es7243e_write_reg(0x0D, 0x00); + ret |= es7243e_write_reg(0x05, 0x00); + ret |= es7243e_write_reg(0x06, 0x03); // SCLK=MCLK/4 + ret |= es7243e_write_reg(0x07, 0x00); // LRCK=MCLK/256 + ret |= es7243e_write_reg(0x08, 0xFF); // LRCK=MCLK/256 + + ret |= es7243e_write_reg(0x09, 0xCA); + ret |= es7243e_write_reg(0x0A, 0x85); + ret |= es7243e_write_reg(0x0B, 0x00); + ret |= es7243e_write_reg(0x0E, 0xBF); + ret |= es7243e_write_reg(0x0F, 0x80); + ret |= es7243e_write_reg(0x14, 0x0C); + ret |= es7243e_write_reg(0x15, 0x0C); + ret |= es7243e_write_reg(0x17, 0x02); + ret |= es7243e_write_reg(0x18, 0x26); + ret |= es7243e_write_reg(0x19, 0x77); + ret |= es7243e_write_reg(0x1A, 0xF4); + ret |= es7243e_write_reg(0x1B, 0x66); + ret |= es7243e_write_reg(0x1C, 0x44); + ret |= es7243e_write_reg(0x1E, 0x00); + ret |= es7243e_write_reg(0x1F, 0x0C); + ret |= es7243e_write_reg(0x20, 0x1E); //PGA gain +30dB + ret |= es7243e_write_reg(0x21, 0x1E); //PGA gain +30dB + + ret |= es7243e_write_reg(0x00, 0x80); //Slave Mode + ret |= es7243e_write_reg(0x01, 0x3A); + ret |= es7243e_write_reg(0x16, 0x3F); + ret |= es7243e_write_reg(0x16, 0x00); + if (ret) { + ESP_LOGE(TAG, "Es7243e initialize failed!"); + return ESP_FAIL; + } + return ret; +} +#endif diff --git a/lib/lib_audio/es7243e/src/es7243e.h b/lib/lib_audio/es7243e/src/es7243e.h new file mode 100644 index 000000000..bef4b3ba6 --- /dev/null +++ b/lib/lib_audio/es7243e/src/es7243e.h @@ -0,0 +1,80 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ES7243E_H_ +#define _ES7243E_H_ + +#include "audio_hal.h" +#include "esp_types.h" +#include "esxxx_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ES7243_ADDR 0x10 + +/** + * @brief Initialize ES7243E adc chip + * + * @param codec_cfg configuration of ES7243E + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7243e_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); + +/** + * @brief Set gain of given mask + * + * @param[in] mic_mask Mask of MIC channel + * + * @param[in] gain: gain + * + * gain : value + * GAIN_0DB : 1 + * GAIN_3DB : 2 + * GAIN_6DB : 3 + * · + * · + * · + * GAIN_30DB : 10 + * GAIN_33DB : 11 + * GAIN_34_5DB : 12 + * GAIN_36DB : 13 + * GAIN_37_5DB : 14 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +// esp_err_t es7243_adc_set_gain(es7243_input_mics_t mic_mask, es7243_gain_value_t gain); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_audio/es8156/library.json b/lib/lib_audio/es8156/library.json new file mode 100644 index 000000000..8a00f5423 --- /dev/null +++ b/lib/lib_audio/es8156/library.json @@ -0,0 +1,7 @@ +{ + "name": "ES8156", + "description": "Audio codec", + "keywords": "ESP8266, ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/es8156/library.properties b/lib/lib_audio/es8156/library.properties new file mode 100644 index 000000000..bd6db1442 --- /dev/null +++ b/lib/lib_audio/es8156/library.properties @@ -0,0 +1,9 @@ +name=ES8156 +version=1.0 +author= +maintainer= +sentence=Audio fcodec for ESP8266, ESP32 +paragraph= +category=Signal Output +url= +architectures=* diff --git a/lib/lib_audio/es8156/src/audio_hal.h b/lib/lib_audio/es8156/src/audio_hal.h new file mode 100755 index 000000000..ef43c0f67 --- /dev/null +++ b/lib/lib_audio/es8156/src/audio_hal.h @@ -0,0 +1,136 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2018 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AUDIO_HAL_H_ +#define _AUDIO_HAL_H_ + +#define AUDIO_HAL_VOL_DEFAULT 60 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Select media hal codec mode + */ +typedef enum { + AUDIO_HAL_CODEC_MODE_ENCODE = 1, /*!< select adc */ + AUDIO_HAL_CODEC_MODE_DECODE, /*!< select dac */ + AUDIO_HAL_CODEC_MODE_BOTH, /*!< select both adc and dac */ + AUDIO_HAL_CODEC_MODE_LINE_IN, /*!< set adc channel */ +} audio_hal_codec_mode_t; + +/** + * @brief Select adc channel for input mic signal + */ +typedef enum { + AUDIO_HAL_ADC_INPUT_LINE1 = 0x00, /*!< mic input to adc channel 1 */ + AUDIO_HAL_ADC_INPUT_LINE2, /*!< mic input to adc channel 2 */ + AUDIO_HAL_ADC_INPUT_ALL, /*!< mic input to both channels of adc */ + AUDIO_HAL_ADC_INPUT_DIFFERENCE, /*!< mic input to adc difference channel */ +} audio_hal_adc_input_t; + +/** + * @brief Select channel for dac output + */ +typedef enum { + AUDIO_HAL_DAC_OUTPUT_LINE1 = 0x00, /*!< dac output signal to channel 1 */ + AUDIO_HAL_DAC_OUTPUT_LINE2, /*!< dac output signal to channel 2 */ + AUDIO_HAL_DAC_OUTPUT_ALL, /*!< dac output signal to both channels */ +} audio_hal_dac_output_t; + +/** + * @brief Select operating mode i.e. start or stop for audio codec chip + */ +typedef enum { + AUDIO_HAL_CTRL_STOP = 0x00, /*!< set stop mode */ + AUDIO_HAL_CTRL_START = 0x01, /*!< set start mode */ +} audio_hal_ctrl_t; + +/** + * @brief Select I2S interface operating mode i.e. master or slave for audio codec chip + */ +typedef enum { + AUDIO_HAL_MODE_SLAVE = 0x00, /*!< set slave mode */ + AUDIO_HAL_MODE_MASTER = 0x01, /*!< set master mode */ +} audio_hal_iface_mode_t; + +/** + * @brief Select I2S interface samples per second + */ +typedef enum { + AUDIO_HAL_08K_SAMPLES, /*!< set to 8k samples per second */ + AUDIO_HAL_11K_SAMPLES, /*!< set to 11.025k samples per second */ + AUDIO_HAL_16K_SAMPLES, /*!< set to 16k samples in per second */ + AUDIO_HAL_22K_SAMPLES, /*!< set to 22.050k samples per second */ + AUDIO_HAL_24K_SAMPLES, /*!< set to 24k samples in per second */ + AUDIO_HAL_32K_SAMPLES, /*!< set to 32k samples in per second */ + AUDIO_HAL_44K_SAMPLES, /*!< set to 44.1k samples per second */ + AUDIO_HAL_48K_SAMPLES, /*!< set to 48k samples per second */ +} audio_hal_iface_samples_t; + +/** + * @brief Select I2S interface number of bits per sample + */ +typedef enum { + AUDIO_HAL_BIT_LENGTH_16BITS = 1, /*!< set 16 bits per sample */ + AUDIO_HAL_BIT_LENGTH_24BITS, /*!< set 24 bits per sample */ + AUDIO_HAL_BIT_LENGTH_32BITS, /*!< set 32 bits per sample */ +} audio_hal_iface_bits_t; + +/** + * @brief Select I2S interface format for audio codec chip + */ +typedef enum { + AUDIO_HAL_I2S_NORMAL = 0, /*!< set normal I2S format */ + AUDIO_HAL_I2S_LEFT, /*!< set all left format */ + AUDIO_HAL_I2S_RIGHT, /*!< set all right format */ + AUDIO_HAL_I2S_DSP, /*!< set dsp/pcm format */ +} audio_hal_iface_format_t; + +/** + * @brief I2s interface configuration for audio codec chip + */ +typedef struct { + audio_hal_iface_mode_t mode; /*!< audio codec chip mode */ + audio_hal_iface_format_t fmt; /*!< I2S interface format */ + audio_hal_iface_samples_t samples; /*!< I2S interface samples per second */ + audio_hal_iface_bits_t bits; /*!< i2s interface number of bits per sample */ +} audio_hal_codec_i2s_iface_t; + +/** + * @brief Configure media hal for initialization of audio codec chip + */ +typedef struct { + audio_hal_adc_input_t adc_input; /*!< set adc channel */ + audio_hal_dac_output_t dac_output; /*!< set dac channel */ + audio_hal_codec_mode_t codec_mode; /*!< select codec mode: adc, dac or both */ + audio_hal_codec_i2s_iface_t i2s_iface; /*!< set I2S interface configuration */ +} audio_hal_codec_config_t; + +#ifdef __cplusplus +} +#endif + +#endif //__AUDIO_HAL_H__ diff --git a/lib/lib_audio/es8156/src/es8156.cpp b/lib/lib_audio/es8156/src/es8156.cpp new file mode 100644 index 000000000..1de1ed716 --- /dev/null +++ b/lib/lib_audio/es8156/src/es8156.cpp @@ -0,0 +1,160 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifdef ESP32 + +#include +#include +#include "string.h" +#include "esp_log.h" +#include "es8156.h" + +/* +typedef struct { + int mck_io_num; //!< MCK in out pin + int bck_io_num; //!< BCK in out pin + int ws_io_num; //!< WS in out pin + int data_out_num; //!< DATA out pin + int data_in_num; //!< DATA in pin +} i2s_pin_config_t; +*/ + +static const char *TAG = "es8156"; + +static TwoWire *es8156_wire; + + +static esp_err_t es8156_write_reg(uint8_t reg_addr, uint8_t data) +{ + es8156_wire->beginTransmission(ES8156_ADDR); + es8156_wire->write(reg_addr); + es8156_wire->write(data); + es8156_wire->endTransmission(); + return 0; +} + +static uint8_t es8156_read_reg(uint8_t reg_addr) +{ + uint8_t data; + es8156_wire->beginTransmission(ES8156_ADDR); + es8156_wire->write(reg_addr); + es8156_wire->endTransmission(false); + es8156_wire->requestFrom(ES8156_ADDR, (size_t)1); + data = es8156_wire->read(); + //i2c_bus_read_byte(i2c_handle, reg_addr, &data); + return data; +} + +esp_err_t es8156_codec_init(TwoWire *tw, audio_hal_codec_config_t *cfg) +{ + + es8156_wire = tw; + esp_err_t ret_val = ESP_OK; + //ret_val |= bsp_i2c_add_device(&i2c_handle, ES8156_ADDR); + + uint8_t misc_ctrl_reg_val = 0x00; + uint8_t dac_iface_reg_val = 0x00; + + if (AUDIO_HAL_MODE_MASTER == cfg->i2s_iface.mode) { + misc_ctrl_reg_val |= 0b00100000; + } else { + misc_ctrl_reg_val |= 0b00000000; + } + + switch (cfg->i2s_iface.bits) { + case AUDIO_HAL_BIT_LENGTH_16BITS: + dac_iface_reg_val |= 0b00110000; + break; + case AUDIO_HAL_BIT_LENGTH_24BITS: + dac_iface_reg_val |= 0b00000000; + break; + case AUDIO_HAL_BIT_LENGTH_32BITS: + dac_iface_reg_val |= 0b01000000; + break; + default: /* Use 32 bit as default */ + dac_iface_reg_val |= 0b01000000; + break; + } + + ret_val |= es8156_write_reg(0x09, misc_ctrl_reg_val); // MCLK from pad, Slave mode, power down DLL, enable pin pull up + ret_val |= es8156_write_reg(0x11, dac_iface_reg_val); // DAC Interface Config + ret_val |= es8156_write_reg(0x14, ES8156_VOL_MIN_3dB); // Set default volume to 0dB + + return ret_val; +} + +esp_err_t es8156_codec_deinit(void) +{ + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t es8156_codec_set_voice_mute(bool enable) +{ + int regv = es8156_read_reg(ES8156_DAC_MUTE_REG13); + if (enable) { + regv = regv | BIT(1) | BIT(2); + } else { + regv = regv & (~(BIT(1) | BIT(2))) ; + } + es8156_write_reg(ES8156_DAC_MUTE_REG13, regv); + return ESP_OK; +} + +esp_err_t es8156_codec_set_voice_volume(uint8_t volume) +{ + if (volume > 100) { + volume = 100; + } + uint8_t d = 0xBF - 60 + 6 * volume / 10; + if (0 == volume) { + d = 0; + } + return es8156_write_reg(ES8156_VOLUME_CONTROL_REG14, d); +} + +esp_err_t es8156_codec_get_voice_volume(uint8_t *volume) +{ + *volume = es8156_read_reg(ES8156_VOLUME_CONTROL_REG14); + + return ESP_OK; +} + +esp_err_t es8156_config_fmt(es_i2s_fmt_t fmt) +{ + ESP_LOGW(TAG, "Not support config format for es8156 now"); + return ESP_ERR_NOT_SUPPORTED; +} + +esp_err_t es8156_set_channel(uint8_t is_right) +{ + uint8_t reg = es8156_read_reg(ES8156_DAC_SDP_REG11); + if (is_right) { + reg |= 0b00000100; + } else { + reg &= 0b11111011; + } + return es8156_write_reg(ES8156_DAC_SDP_REG11, reg); +} + +#endif diff --git a/lib/lib_audio/es8156/src/es8156.h b/lib/lib_audio/es8156/src/es8156.h new file mode 100644 index 000000000..6ceba46d2 --- /dev/null +++ b/lib/lib_audio/es8156/src/es8156.h @@ -0,0 +1,180 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ES8156_H +#define _ES8156_H + +#include "audio_hal.h" +#include "esp_types.h" +#include "esxxx_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ES8156_ADDR (0x08) + +/* ES8156 register space */ +/* +* RESET Control +*/ +#define ES8156_RESET_REG00 0x00 +/* +* Clock Managerment +*/ +#define ES8156_MAINCLOCK_CTL_REG01 0x01 +#define ES8156_SCLK_MODE_REG02 0x02 +#define ES8156_LRCLK_DIV_H_REG03 0x03 +#define ES8156_LRCLK_DIV_L_REG04 0x04 +#define ES8156_SCLK_DIV_REG05 0x05 +#define ES8156_NFS_CONFIG_REG06 0x06 +#define ES8156_MISC_CONTROL1_REG07 0x07 +#define ES8156_CLOCK_ON_OFF_REG08 0x08 +#define ES8156_MISC_CONTROL2_REG09 0x09 +#define ES8156_TIME_CONTROL1_REG0A 0x0a +#define ES8156_TIME_CONTROL2_REG0B 0x0b +/* +* System Control +*/ +#define ES8156_CHIP_STATUS_REG0C 0x0c +#define ES8156_P2S_CONTROL_REG0D 0x0d +#define ES8156_DAC_OSR_COUNTER_REG10 0x10 +/* +* SDP Control +*/ +#define ES8156_DAC_SDP_REG11 0x11 +#define ES8156_AUTOMUTE_SET_REG12 0x12 +#define ES8156_DAC_MUTE_REG13 0x13 +#define ES8156_VOLUME_CONTROL_REG14 0x14 + +/* +* ALC Control +*/ +#define ES8156_ALC_CONFIG1_REG15 0x15 +#define ES8156_ALC_CONFIG2_REG16 0x16 +#define ES8156_ALC_CONFIG3_REG17 0x17 +#define ES8156_MISC_CONTROL3_REG18 0x18 +#define ES8156_EQ_CONTROL1_REG19 0x19 +#define ES8156_EQ_CONTROL2_REG1A 0x1a +/* +* Analog System Control +*/ +#define ES8156_ANALOG_SYS1_REG20 0x20 +#define ES8156_ANALOG_SYS2_REG21 0x21 +#define ES8156_ANALOG_SYS3_REG22 0x22 +#define ES8156_ANALOG_SYS4_REG23 0x23 +#define ES8156_ANALOG_LP_REG24 0x24 +#define ES8156_ANALOG_SYS5_REG25 0x25 +/* +* Chip Information +*/ +#define ES8156_I2C_PAGESEL_REGFC 0xFC +#define ES8156_CHIPID1_REGFD 0xFD +#define ES8156_CHIPID0_REGFE 0xFE +#define ES8156_CHIP_VERSION_REGFF 0xFF + +typedef enum { + ES8156_VOL_MIN = 0x00, /*!< -95.5dB */ + ES8156_VOL_MIN_10dB = 0xAB, /*!< -10dB */ + ES8156_VOL_MIN_9dB = 0xAD, /*!< -9dB */ + ES8156_VOL_MIN_6dB = 0xB3, /*!< -6dB */ + ES8156_VOL_MIN_3dB = 0xB9, /*!< -3dB */ + ES8156_VOL_0dB = 0xBF, /*!< 0dB */ + ES8156_VOL_3dB = 0xC5, /*!< +3dB */ + ES8156_VOL_10dB = 0xD3, /*!< +10dB */ + ES8156_VOL_MAX = 0xFF, /*!< +32dB */ +} es8156_volume_t; + +/* + * @brief Initialize ES8156 codec chip + * + * @param codec_cfg configuration of ES8156 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8156_codec_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); + +/** + * @brief Deinitialize ES8156 codec chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8156_codec_deinit(void); + + +esp_err_t es8156_config_fmt(es_i2s_fmt_t fmt); + +/** + * @brief Configure ES8156 DAC mute or not. Basically you can use this function to mute the output or unmute + * + * @param enable enable(1) or disable(0) + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es8156_codec_set_voice_mute(bool enable); + +/** + * @brief Set voice volume + * + * @param volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8156_codec_set_voice_volume(uint8_t volume); + +/** + * @brief Get voice volume + * + * @param[out] *volume: voice volume (0~255) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8156_codec_get_voice_volume(uint8_t *volume); + +/** + * @brief + * + * @param is_right + * @return esp_err_t + */ +esp_err_t es8156_set_channel(uint8_t is_right); + + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/lib_audio/es8156/src/esxxx_common.h b/lib/lib_audio/es8156/src/esxxx_common.h new file mode 100644 index 000000000..34c042ed6 --- /dev/null +++ b/lib/lib_audio/es8156/src/esxxx_common.h @@ -0,0 +1,188 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2019 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ESXXX_COMMON_H_ +#define _ESXXX_COMMON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BIT_LENGTH_MIN = -1, + BIT_LENGTH_16BITS = 0x03, + BIT_LENGTH_18BITS = 0x02, + BIT_LENGTH_20BITS = 0x01, + BIT_LENGTH_24BITS = 0x00, + BIT_LENGTH_32BITS = 0x04, + BIT_LENGTH_MAX, +} es_bits_length_t; + +typedef enum { + MCLK_DIV_MIN = -1, + MCLK_DIV_1 = 1, + MCLK_DIV_2 = 2, + MCLK_DIV_3 = 3, + MCLK_DIV_4 = 4, + MCLK_DIV_6 = 5, + MCLK_DIV_8 = 6, + MCLK_DIV_9 = 7, + MCLK_DIV_11 = 8, + MCLK_DIV_12 = 9, + MCLK_DIV_16 = 10, + MCLK_DIV_18 = 11, + MCLK_DIV_22 = 12, + MCLK_DIV_24 = 13, + MCLK_DIV_33 = 14, + MCLK_DIV_36 = 15, + MCLK_DIV_44 = 16, + MCLK_DIV_48 = 17, + MCLK_DIV_66 = 18, + MCLK_DIV_72 = 19, + MCLK_DIV_5 = 20, + MCLK_DIV_10 = 21, + MCLK_DIV_15 = 22, + MCLK_DIV_17 = 23, + MCLK_DIV_20 = 24, + MCLK_DIV_25 = 25, + MCLK_DIV_30 = 26, + MCLK_DIV_32 = 27, + MCLK_DIV_34 = 28, + MCLK_DIV_7 = 29, + MCLK_DIV_13 = 30, + MCLK_DIV_14 = 31, + MCLK_DIV_MAX, +} es_sclk_div_t; + +typedef enum { + LCLK_DIV_MIN = -1, + LCLK_DIV_128 = 0, + LCLK_DIV_192 = 1, + LCLK_DIV_256 = 2, + LCLK_DIV_384 = 3, + LCLK_DIV_512 = 4, + LCLK_DIV_576 = 5, + LCLK_DIV_768 = 6, + LCLK_DIV_1024 = 7, + LCLK_DIV_1152 = 8, + LCLK_DIV_1408 = 9, + LCLK_DIV_1536 = 10, + LCLK_DIV_2112 = 11, + LCLK_DIV_2304 = 12, + + LCLK_DIV_125 = 16, + LCLK_DIV_136 = 17, + LCLK_DIV_250 = 18, + LCLK_DIV_272 = 19, + LCLK_DIV_375 = 20, + LCLK_DIV_500 = 21, + LCLK_DIV_544 = 22, + LCLK_DIV_750 = 23, + LCLK_DIV_1000 = 24, + LCLK_DIV_1088 = 25, + LCLK_DIV_1496 = 26, + LCLK_DIV_1500 = 27, + LCLK_DIV_MAX, +} es_lclk_div_t; + +typedef enum { + D2SE_PGA_GAIN_MIN = -1, + D2SE_PGA_GAIN_DIS = 0, + D2SE_PGA_GAIN_EN = 1, + D2SE_PGA_GAIN_MAX = 2, +} es_d2se_pga_t; + +typedef enum { + ADC_INPUT_MIN = -1, + ADC_INPUT_LINPUT1_RINPUT1 = 0x00, + ADC_INPUT_MIC1 = 0x05, + ADC_INPUT_MIC2 = 0x06, + ADC_INPUT_LINPUT2_RINPUT2 = 0x50, + ADC_INPUT_DIFFERENCE = 0xf0, + ADC_INPUT_MAX, +} es_adc_input_t; + +typedef enum { + DAC_OUTPUT_MIN = -1, + DAC_OUTPUT_LOUT1 = 0x04, + DAC_OUTPUT_LOUT2 = 0x08, + DAC_OUTPUT_SPK = 0x09, + DAC_OUTPUT_ROUT1 = 0x10, + DAC_OUTPUT_ROUT2 = 0x20, + DAC_OUTPUT_ALL = 0x3c, + DAC_OUTPUT_MAX, +} es_dac_output_t; + +typedef enum { + MIC_GAIN_MIN = -1, + MIC_GAIN_0DB = 0, + MIC_GAIN_3DB = 3, + MIC_GAIN_6DB = 6, + MIC_GAIN_9DB = 9, + MIC_GAIN_12DB = 12, + MIC_GAIN_15DB = 15, + MIC_GAIN_18DB = 18, + MIC_GAIN_21DB = 21, + MIC_GAIN_24DB = 24, + MIC_GAIN_MAX, +} es_mic_gain_t; + +typedef enum { + ES_MODULE_MIN = -1, + ES_MODULE_ADC = 0x01, + ES_MODULE_DAC = 0x02, + ES_MODULE_ADC_DAC = 0x03, + ES_MODULE_LINE = 0x04, + ES_MODULE_MAX +} es_module_t; + +typedef enum { + ES_MODE_MIN = -1, + ES_MODE_SLAVE = 0x00, + ES_MODE_MASTER = 0x01, + ES_MODE_MAX, +} es_mode_t; + +typedef enum { + ES_I2S_MIN = -1, + ES_I2S_NORMAL = 0, + ES_I2S_LEFT = 1, + ES_I2S_RIGHT = 2, + ES_I2S_DSP = 3, + ES_I2S_MAX +} es_i2s_fmt_t; + +/** + * @brief Configure ES8388 clock + */ +typedef struct { + es_sclk_div_t sclk_div; /*!< bits clock divide */ + es_lclk_div_t lclk_div; /*!< WS clock divide */ +} es_i2s_clock_t; + +#ifdef __cplusplus +} +#endif + +#endif From 612a0f3fcc9aff03de3359be3fd03e48f3146b57 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:29:36 +0200 Subject: [PATCH 073/219] sbox audio support --- .../xdrv_42_1_i2s_audio.ino | 56 +++++++++++++++++++ .../tasmota_xdrv_driver/xdrv_42_i2s_audio.ino | 36 +++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino new file mode 100644 index 000000000..c48c342c6 --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino @@ -0,0 +1,56 @@ + +#ifdef ESP32 +#ifdef ESP32S3_BOX +#include +#include +#include + +void S3boxAudioPower(uint8_t pwr) { + pinMode(46 , OUTPUT); + digitalWrite(46, pwr); +} + +uint32_t ES8156_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES8156_ADDR, 1)) { + I2cSetActiveFound(ES8156_ADDR, "ES8156-I2C", 1); + audio_hal_codec_config_t cfg = { + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + } + }; + ret_val |= es8156_codec_init(&Wire1, &cfg); + ret_val |= es8156_codec_set_voice_volume(75); + } + return ret_val; +} + +uint32_t es7243e_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES7243_ADDR, 1)) { + I2cSetActiveFound(ES7243_ADDR, "ES7243e-I2C", 1); + } + + audio_hal_codec_config_t cfg = { + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + } + }; + + ret_val |= es8156_codec_init(&Wire1, &cfg); + + return ret_val; +} + +void S3boxInit() { + if (TasmotaGlobal.i2c_enabled_2) { + ES8156_init(); + es7243e_init(); + } +} +#endif +#endif diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino index 6849859de..31cfb64ca 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino @@ -54,6 +54,10 @@ #include "AudioFileSourceBuffer.h" #include "AudioGeneratorAAC.h" +#ifdef ESP32 +#include +#endif + #undef AUDIO_PWR_ON #undef AUDIO_PWR_OFF #define AUDIO_PWR_ON @@ -79,6 +83,24 @@ #define DAC_IIS_DOUT 2 #endif // USE_M5STACK_CORE2 + +#ifdef ESP32S3_BOX +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON S3boxAudioPower(true); +#define AUDIO_PWR_OFF S3boxAudioPower(false); + +#undef DAC_IIS_BCK +#undef DAC_IIS_WS +#undef DAC_IIS_DOUT +#define DAC_IIS_BCK 17 +#define DAC_IIS_WS 47 +#define DAC_IIS_DOUT 15 +#define DAC_IIS_DIN 16 +#define DAC_IIS_MCLK 2 + +#endif // ESP32S3_BOX + AudioGeneratorMP3 *mp3 = nullptr; AudioFileSourceFS *file; #ifdef USE_I2S_NO_DAC @@ -264,8 +286,14 @@ void I2S_Init(void) { out = new AudioOutputI2S(); #endif // USE_I2S_NO_DAC #ifdef ESP32 +#ifdef ESP32S3_BOX + S3boxInit(); + out->SetPinout(DAC_IIS_BCK, DAC_IIS_WS, DAC_IIS_DOUT, DAC_IIS_MCLK, DAC_IIS_DIN); +#else out->SetPinout(DAC_IIS_BCK, DAC_IIS_WS, DAC_IIS_DOUT); +#endif #endif // ESP32 + #else #ifdef USE_I2S_NO_DAC out = new AudioOutputI2SNoDAC(); @@ -307,8 +335,6 @@ void I2S_Init(void) { //#define MICSRATE 44100 #define MICSRATE 16000 -#include - uint32_t SpeakerMic(uint8_t spkr) { esp_err_t err = ESP_OK; @@ -469,6 +495,7 @@ void mp3_task(void *arg) { if (!mp3->loop()) { mp3->stop(); mp3_delete(); + out->stop(); if (mp3_task_h) { vTaskDelete(mp3_task_h); mp3_task_h = 0; @@ -610,6 +637,10 @@ void Play_mp3(const char *path) { if (decoder || mp3) return; if (!out) return; + if (!ufsp->exists(path)) { + return; + } + bool I2S_Task; AUDIO_PWR_ON @@ -641,6 +672,7 @@ void Play_mp3(const char *path) { } OsWatchLoop(); } + out->stop(); mp3_delete(); } From 53bd637568e370b3baa0b1ab9e12a98bc6c51bcf Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:30:30 +0200 Subject: [PATCH 074/219] add sbox display descriptor --- tasmota/displaydesc/esp32_s3_sbox_display.ini | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tasmota/displaydesc/esp32_s3_sbox_display.ini diff --git a/tasmota/displaydesc/esp32_s3_sbox_display.ini b/tasmota/displaydesc/esp32_s3_sbox_display.ini new file mode 100644 index 000000000..9d9f49264 --- /dev/null +++ b/tasmota/displaydesc/esp32_s3_sbox_display.ini @@ -0,0 +1,37 @@ +:H,ILI9342,320,240,16,SPI,2,*,*,*,*,*,*,*,80 +:S,2,1,3,0,100,100 +:I +EF,3,03,80,02 +CF,3,00,C1,30 +ED,4,64,03,12,81 +E8,3,85,00,78 +CB,5,39,2C,00,34,02 +F7,1,20 +EA,2,00,00 +C0,1,23 +C1,1,10 +C5,2,3e,28 +C7,1,86 +36,1,48 +37,1,00 +3A,1,55 +B1,2,00,18 +B6,3,08,82,27 +F2,1,00 +26,1,01 +E0,0F,0F,31,2B,0C,0E,08,4E,F1,37,07,10,03,0E,09,00 +E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F +21,80 +11,80 +29,80 +:o,28 +:O,29 +:A,2A,2B,2C,16 +:R,36 +:0,48,00,00,00 +:1,28,00,00,01 +:2,88,00,00,02 +:3,E8,00,00,03 +:i,21,20 +:b,01 +# From 1f70dc8f97fd9e110283e82ccd64acd8afb432ec Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:31:52 +0200 Subject: [PATCH 075/219] inverted backpanel --- lib/lib_display/UDisplay/uDisplay.cpp | 39 +++++++++++++++++++++------ lib/lib_display/UDisplay/uDisplay.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/lib_display/UDisplay/uDisplay.cpp b/lib/lib_display/UDisplay/uDisplay.cpp index 71025069b..34b79e838 100755 --- a/lib/lib_display/UDisplay/uDisplay.cpp +++ b/lib/lib_display/UDisplay/uDisplay.cpp @@ -62,6 +62,7 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { sa_mode = 16; saw_3 = 0xff; dim_op = 0xff; + bpmode = 0; dsp_off = 0xff; dsp_on = 0xff; lutpsize = 0; @@ -305,6 +306,9 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) { rotmap_ymin = next_val(&lp1); rotmap_ymax = next_val(&lp1); break; + case 'b': + bpmode = next_val(&lp1); + break; } } } @@ -1344,7 +1348,7 @@ void uDisplay::DisplayOnff(int8_t on) { pwr_cbp(on); } -// udisp_bpwr(on); +#define AW_PWMRES 1024 if (interface == _UDSP_I2C) { if (on) { @@ -1357,10 +1361,17 @@ void uDisplay::DisplayOnff(int8_t on) { if (dsp_on != 0xff) spi_command_one(dsp_on); if (bpanel >= 0) { #ifdef ESP32 - analogWrite(bpanel, dimmer10_gamma); - // ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma); + if (!bpmode) { + analogWrite(bpanel, dimmer10_gamma); + } else { + analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); + } #else - digitalWrite(bpanel, HIGH); + if (!bpmode) { + digitalWrite(bpanel, HIGH); + } else { + digitalWrite(bpanel, LOW); + } #endif } @@ -1368,10 +1379,17 @@ void uDisplay::DisplayOnff(int8_t on) { if (dsp_off != 0xff) spi_command_one(dsp_off); if (bpanel >= 0) { #ifdef ESP32 - analogWrite(bpanel, 0); - // ledcWrite(ESP32_PWM_CHANNEL, 0); + if (!bpmode) { + analogWrite(bpanel, 0); + } else { + analogWrite(bpanel, AW_PWMRES - 1); + } #else - digitalWrite(bpanel, LOW); + if (!bpmode) { + digitalWrite(bpanel, LOW); + } else { + digitalWrite(bpanel, HIGH); + } #endif } } @@ -1417,7 +1435,12 @@ void uDisplay::dim10(uint8_t dim, uint16_t dim_gamma) { // dimmer with #ifdef ESP32 // TODO should we also add a ESP8266 version for bpanel? if (bpanel >= 0) { // is the BaclPanel GPIO configured - analogWrite(bpanel, dimmer10_gamma); + if (!bpmode) { + analogWrite(bpanel, dimmer10_gamma); + } else { + analogWrite(bpanel, AW_PWMRES - dimmer10_gamma); + } + // ledcWrite(ESP32_PWM_CHANNEL, dimmer8_gamma); } else if (dim_cbp) { dim_cbp(dim); diff --git a/lib/lib_display/UDisplay/uDisplay.h b/lib/lib_display/UDisplay/uDisplay.h index 9a233fcff..93ee851e6 100755 --- a/lib/lib_display/UDisplay/uDisplay.h +++ b/lib/lib_display/UDisplay/uDisplay.h @@ -176,6 +176,7 @@ class uDisplay : public Renderer { uint16_t bg_col; uint16_t gxs; uint16_t gys; + int8_t bpmode; int8_t spi_cs; int8_t spi_clk; int8_t spi_mosi; From 9cd3ad3f6b714f31f4a1923954bc919675842946 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 13:45:40 +0200 Subject: [PATCH 076/219] Update xdrv_42_i2s_audio.ino --- tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino index 31cfb64ca..bd155f9b1 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino @@ -17,7 +17,7 @@ along with this program. If not, see . */ -#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2)) +#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) /*********************************************************************************************\ * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor * From f3b1c4d543bbbb06d990f50dca859392be2fe6d0 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 7 Jul 2022 14:24:53 +0200 Subject: [PATCH 077/219] Add POWR3xxD and THR3xxD overflow display --- CHANGELOG.md | 1 + RELEASENOTES.md | 3 ++- .../tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino | 13 ++++++++++--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0a5f013..0f1a260d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) +- Tasmota ESP32 Arduino core from v2.0.3 to v2.0.4 (#15940) ### Fixed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index da2b1b939..833ce1892 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -77,7 +77,7 @@ Historical binaries can be downloaded from The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` ### ESP32, ESP32-C3, ESP32-S2 and ESP32-S3 based -The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.3**. +The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.4**. - **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY** - **tasmota32xy.bin** = The Tasmota version with most drivers including additional sensors and KNX for ESP32-C3/S2/S3 and 4M+ flash. @@ -118,6 +118,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo ### Breaking Changed ### Changed +- Tasmota ESP32 Arduino core from v2.0.3 to v2.0.4 [#15940](https://github.com/arendst/Tasmota/issues/15940) - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Fixed diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index 969fb0382..17a1d486b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -112,6 +112,9 @@ void TM1621SendCommon(uint8_t common) { } void TM1621SendRows(void) { + // Tm1621.row[x] = "text", "----", " " or a number with one decimal like "0.4", "237.5", "123456.7" + // "123456.7" will be shown as "9999" being a four digit overflow + // AddLog(LOG_LEVEL_DEBUG, PSTR("TM1: Row1 '%s', Row2 '%s'"), Tm1621.row[0], Tm1621.row[1]); uint8_t buffer[8] = { 0 }; // TM1621 16-segment 4-bit common buffer @@ -119,12 +122,16 @@ void TM1621SendRows(void) { for (uint32_t j = 0; j < 2; j++) { // 0.4V => " 04", 0.0A => " ", 1234.5V => "1234" uint32_t len = strlen(Tm1621.row[j]); - char *dp = nullptr; - int row_idx = len -3; - if (len <= 5) { + char *dp = nullptr; // Expect number larger than "123" + int row_idx = len -3; // "1234.5" + if (len <= 5) { // "----", " ", "0.4", "237.5" dp = strchr(Tm1621.row[j], '.'); row_idx = len -1; } + else if (len > 6) { // "12345.6" + snprintf_P(Tm1621.row[j], sizeof(Tm1621.row[j]), PSTR("9999")); + row_idx = 3; + } row[3] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; if ((row_idx >= 0) && dp) { row_idx--; } row[2] = (row_idx >= 0) ? Tm1621.row[j][row_idx--] : ' '; From 2ce5a895ac6a97d496f40acfb6a3ddb2e4e77abc Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 7 Jul 2022 15:26:09 +0200 Subject: [PATCH 078/219] Create esp32s3boxusb.json --- boards/esp32s3boxusb.json | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 boards/esp32s3boxusb.json diff --git a/boards/esp32s3boxusb.json b/boards/esp32s3boxusb.json new file mode 100644 index 000000000..a7e539fe1 --- /dev/null +++ b/boards/esp32s3boxusb.json @@ -0,0 +1,38 @@ +{ + "build": { + "arduino":{ + "ldscript": "esp32s3_out.ld", + "memory_type": "qspi_opi" + }, + "core": "esp32", + "extra_flags": "-DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DUSE_USB_SERIAL_CONSOLE -DESP32_16M -DESP32S3", + "f_cpu": "240000000L", + "f_flash": "80000000L", + "flash_mode": "dio", + "mcu": "esp32s3", + "variant": "esp32s3", + "partitions": "partitions/esp32_partition_app2944k_fs10M.csv" + }, + "connectivity": [ + "wifi", + "bluetooth", + "ethernet" + ], + "debug": { + "openocd_target": "esp32s3.cfg" + }, + "frameworks": [ + "espidf", + "arduino" + ], + "name": "Espressif Generic ESP32-S3 16M Flash, Tasmota 2880k Code/OTA, 10M FS", + "upload": { + "flash_size": "16MB", + "maximum_ram_size": 327680, + "maximum_size": 16777216, + "require_upload_port": true, + "speed": 460800 + }, + "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", + "vendor": "Espressif" +} From 7a684be3d38c891c0e69fe64e7f7fd2e711f78da Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:49:21 +0200 Subject: [PATCH 079/219] Tasmota ESP32 core 2.0.4 --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f799af042..10ada3243 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR and the code change compiles without warnings - [ ] The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9 - - [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.3 + - [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.4 - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). _NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_ From 5e758db898dc1c5f0bd9d9801e95be51cc08b390 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 7 Jul 2022 19:28:17 +0200 Subject: [PATCH 080/219] Berry tcpserver --- lib/libesp32/berry/default/be_modtab.c | 4 + lib/libesp32/berry/generate/be_const_strtab.h | 3 + .../berry/generate/be_const_strtab_def.h | 2153 +++++++++-------- .../generate/be_fixed_be_class_tcpclient.h | 12 +- .../generate/be_fixed_be_class_tcpserver.h | 22 + .../berry_tasmota/src/be_tcpclient_lib.c | 2 +- .../berry_tasmota/src/be_tcpserver_class.c | 32 + .../xdrv_52_3_berry_tcpserver.ino | 81 + .../xdrv_52_3_berry_webclient.ino | 36 +- 9 files changed, 1253 insertions(+), 1092 deletions(-) create mode 100644 lib/libesp32/berry/generate/be_fixed_be_class_tcpserver.h create mode 100644 lib/libesp32/berry_tasmota/src/be_tcpserver_class.c create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino diff --git a/lib/libesp32/berry/default/be_modtab.c b/lib/libesp32/berry/default/be_modtab.c index 68a06f05b..346ba9adf 100644 --- a/lib/libesp32/berry/default/be_modtab.c +++ b/lib/libesp32/berry/default/be_modtab.c @@ -199,6 +199,7 @@ be_extern_native_class(md5); be_extern_native_class(udp); be_extern_native_class(webclient); be_extern_native_class(tcpclient); +be_extern_native_class(tcpserver); be_extern_native_class(energy_struct); // BLE be_extern_native_class(MI32); @@ -246,6 +247,9 @@ BERRY_LOCAL bclass_array be_class_table = { &be_native_class(webclient), &be_native_class(tcpclient), #endif // USE_WEBCLIENT +#ifdef USE_BERRY_TCPSERVER + &be_native_class(tcpserver), +#endif // USE_BERRY_TCPSERVER #ifdef USE_WS2812 &be_native_class(Leds_ntv), &be_native_class(Leds), diff --git a/lib/libesp32/berry/generate/be_const_strtab.h b/lib/libesp32/berry/generate/be_const_strtab.h index d7b7959dd..27596f14b 100644 --- a/lib/libesp32/berry/generate/be_const_strtab.h +++ b/lib/libesp32/berry/generate/be_const_strtab.h @@ -235,6 +235,7 @@ extern const bcstring be_const_str__validate; extern const bcstring be_const_str__write; extern const bcstring be_const_str_a; extern const bcstring be_const_str_abs; +extern const bcstring be_const_str_accept; extern const bcstring be_const_str_acos; extern const bcstring be_const_str_active_otadata; extern const bcstring be_const_str_adc_config; @@ -484,6 +485,7 @@ extern const bcstring be_const_str_h; extern const bcstring be_const_str_has; extern const bcstring be_const_str_has_arg; extern const bcstring be_const_str_has_factory; +extern const bcstring be_const_str_hasclient; extern const bcstring be_const_str_height_def; extern const bcstring be_const_str_hex; extern const bcstring be_const_str_hour; @@ -787,6 +789,7 @@ extern const bcstring be_const_str_tasmota_X2Eget_light_X28_X29_X20is_X20depreca extern const bcstring be_const_str_tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29; extern const bcstring be_const_str_tasmota_log_reader; extern const bcstring be_const_str_tcpclient; +extern const bcstring be_const_str_tcpserver; extern const bcstring be_const_str_tele; extern const bcstring be_const_str_test; extern const bcstring be_const_str_the_X20second_X20argument_X20is_X20not_X20a_X20function; diff --git a/lib/libesp32/berry/generate/be_const_strtab_def.h b/lib/libesp32/berry/generate/be_const_strtab_def.h index b9f20a7d8..1122a3681 100644 --- a/lib/libesp32/berry/generate/be_const_strtab_def.h +++ b/lib/libesp32/berry/generate/be_const_strtab_def.h @@ -1,844 +1,847 @@ -be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_argument_X20must_X20be_X20a_X20list); -be_define_const_str(_X0A, "\n", 252472541u, 0, 1, &be_const_str__X21_X3D); -be_define_const_str(_X0A_X29_X3E, "\n)>", 804061574u, 0, 3, &be_const_str__X2Ew); -be_define_const_str(_X20, " ", 621580159u, 0, 1, &be_const_str__X3C_X2Fform_X3E_X3C_X2Fp_X3E); -be_define_const_str(_X20_X20, " ", 2982523533u, 0, 2, &be_const_str_byte); -be_define_const_str(_X20_X28, " (", 2848302581u, 0, 2, &be_const_str_call); -be_define_const_str(_X21_X3D, "!=", 2428715011u, 0, 2, &be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E); -be_define_const_str(_X21_X3D_X3D, "!==", 559817114u, 0, 3, &be_const_str_imin); -be_define_const_str(_X22, "\"", 655135397u, 0, 1, NULL); -be_define_const_str(_X22_X3A, "\":", 399167565u, 0, 2, &be_const_str_BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29); -be_define_const_str(_X23, "#", 638357778u, 0, 1, &be_const_str_has_arg); -be_define_const_str(_X23autoexec_X2Ebat, "#autoexec.bat", 3382890497u, 0, 13, &be_const_str_SK6812_GRBW); -be_define_const_str(_X23autoexec_X2Ebe, "#autoexec.be", 1181757091u, 0, 12, &be_const_str_i2c_enabled); -be_define_const_str(_X23display_X2Eini, "#display.ini", 182218220u, 0, 12, NULL); -be_define_const_str(_X23init_X2Ebat, "#init.bat", 3297595077u, 0, 9, &be_const_str_otadata); -be_define_const_str(_X23preinit_X2Ebe, "#preinit.be", 687035716u, 0, 11, NULL); -be_define_const_str(_X25, "%", 537692064u, 0, 1, NULL); -be_define_const_str(_X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, "%04d-%02d-%02dT%02d:%02d:%02d", 3425528601u, 0, 29, &be_const_str_get_ota_slot); -be_define_const_str(_X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, "%08x-%04x-%04x-%04x-%04x%08x", 1670063141u, 0, 28, &be_const_str_close); -be_define_const_str(_X25s_X2Eautoconf, "%s.autoconf", 3560383524u, 0, 11, &be_const_str_CFG_X3A_X20running_X20); -be_define_const_str(_X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, "<Error: apply new or remove>", 2855507949u, 0, 34, &be_const_str_exec_cmd); -be_define_const_str(_X26lt_X3BNone_X26gt_X3B, "<None>", 2602165498u, 0, 12, &be_const_str_allocated); -be_define_const_str(_X27_X20_X2D_X20, "' - ", 3420378487u, 0, 4, &be_const_str_push_path); -be_define_const_str(_X28_X29, "()", 685372826u, 0, 2, &be_const_str__X2Fstate_X2F); -be_define_const_str(_X29, ")", 739023492u, 0, 1, &be_const_str_cmd_res); -be_define_const_str(_X2A, "*", 789356349u, 0, 1, &be_const_str_content_start); -be_define_const_str(_X2B, "+", 772578730u, 0, 1, &be_const_str_SERIAL_5O2); -be_define_const_str(_X2C, ",", 688690635u, 0, 1, &be_const_str__rules); -be_define_const_str(_X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, ",\"AXP192\":{\"VBusVoltage\":%.3f,\"VBusCurrent\":%.1f,\"BattVoltage\":%.3f,\"BattCurrent\":%.1f,\"Temperature\":%.1f}", 2598755376u, 0, 106, &be_const_str__error); -be_define_const_str(_X2D, "-", 671913016u, 0, 1, &be_const_str_get_light); -be_define_const_str(_X2D_X2A, "-*", 499980374u, 0, 2, &be_const_str_cosh); -be_define_const_str(_X2E, ".", 722245873u, 0, 1, &be_const_str_file); -be_define_const_str(_X2E_X2E, "..", 2748622605u, 0, 2, NULL); -be_define_const_str(_X2Eautoconf, ".autoconf", 2524679088u, 0, 9, &be_const_str_get_switch); -be_define_const_str(_X2Ebe, ".be", 1325797348u, 0, 3, &be_const_str_try_run_compiled); -be_define_const_str(_X2Ebec, ".bec", 3985273221u, 0, 4, &be_const_str_CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29); -be_define_const_str(_X2Elen, ".len", 850842136u, 0, 4, &be_const_str_get_temp); -be_define_const_str(_X2Ep, ".p", 1171526419u, 0, 2, &be_const_str_Unknown); -be_define_const_str(_X2Esize, ".size", 1965188224u, 0, 5, NULL); -be_define_const_str(_X2Etapp, ".tapp", 1363391594u, 0, 5, &be_const_str_count); -be_define_const_str(_X2Ew, ".w", 1255414514u, 0, 2, NULL); -be_define_const_str(_X2F, "/", 705468254u, 0, 1, &be_const_str__archive); -be_define_const_str(_X2F_X2Eautoconf, "/.autoconf", 2212074393u, 0, 10, &be_const_str_add_rule); -be_define_const_str(_X2F_X3Frst_X3D, "/?rst=", 580074707u, 0, 6, &be_const_str__X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E); -be_define_const_str(_X2Fac, "/ac", 3904651978u, 0, 3, &be_const_str_RELAY); -be_define_const_str(_X2Flights_X2F, "/lights/", 2370247908u, 0, 8, &be_const_str_battery_present); -be_define_const_str(_X2Fstate_X2F, "/state/", 4226179876u, 0, 7, &be_const_str_coredump); -be_define_const_str(00, "00", 569209421u, 0, 2, &be_const_str_reverse); -be_define_const_str(0x_X2502X, "0x%02X", 2626549866u, 0, 6, &be_const_str_Partition_otadata); -be_define_const_str(_X3A, ":", 1057798253u, 0, 1, &be_const_str_EVENT_DELETE); -be_define_const_str(_X3C, "<", 957132539u, 0, 1, &be_const_str_set_reachable); -be_define_const_str(_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 3546571739u, 0, 11, &be_const_str_SERIAL_6O2); -be_define_const_str(_X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 1863865923u, 0, 16, &be_const_str_exists); -be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str_rtc); -be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_CT); -be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, NULL); -be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str_wire2); -be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found); -be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, NULL); -be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, NULL); -be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str__X3F); +be_define_const_str(, "", 2166136261u, 0, 0, &be_const_str_True); +be_define_const_str(_X0A, "\n", 252472541u, 0, 1, &be_const_str_alternate); +be_define_const_str(_X0A_X29_X3E, "\n)>", 804061574u, 0, 3, NULL); +be_define_const_str(_X20, " ", 621580159u, 0, 1, &be_const_str_add_cmd); +be_define_const_str(_X20_X20, " ", 2982523533u, 0, 2, &be_const_str_CFG_X3A_X20downloading_X20_X27_X25s_X27); +be_define_const_str(_X20_X28, " (", 2848302581u, 0, 2, &be_const_str_i2c_enabled); +be_define_const_str(_X21_X3D, "!=", 2428715011u, 0, 2, &be_const_str_tr); +be_define_const_str(_X21_X3D_X3D, "!==", 559817114u, 0, 3, &be_const_str_coord_arr); +be_define_const_str(_X22, "\"", 655135397u, 0, 1, &be_const_str_month); +be_define_const_str(_X22_X3A, "\":", 399167565u, 0, 2, &be_const_str_call_native); +be_define_const_str(_X23, "#", 638357778u, 0, 1, &be_const_str_sec); +be_define_const_str(_X23autoexec_X2Ebat, "#autoexec.bat", 3382890497u, 0, 13, &be_const_str_get_string); +be_define_const_str(_X23autoexec_X2Ebe, "#autoexec.be", 1181757091u, 0, 12, &be_const_str_md5); +be_define_const_str(_X23display_X2Eini, "#display.ini", 182218220u, 0, 12, &be_const_str__archive); +be_define_const_str(_X23init_X2Ebat, "#init.bat", 3297595077u, 0, 9, &be_const_str_CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting); +be_define_const_str(_X23preinit_X2Ebe, "#preinit.be", 687035716u, 0, 11, &be_const_str_update); +be_define_const_str(_X25, "%", 537692064u, 0, 1, &be_const_str__X2Eautoconf); +be_define_const_str(_X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, "%04d-%02d-%02dT%02d:%02d:%02d", 3425528601u, 0, 29, &be_const_str_CFG_X3A_X20removed_X20file_X20_X27_X25s_X27); +be_define_const_str(_X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, "%08x-%04x-%04x-%04x-%04x%08x", 1670063141u, 0, 28, &be_const_str_stop); +be_define_const_str(_X25s_X2Eautoconf, "%s.autoconf", 3560383524u, 0, 11, &be_const_str__X29); +be_define_const_str(_X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, "<Error: apply new or remove>", 2855507949u, 0, 34, &be_const_str_Animate_X20pc_X20is_X20out_X20of_X20range); +be_define_const_str(_X26lt_X3BNone_X26gt_X3B, "<None>", 2602165498u, 0, 12, &be_const_str__X2Fac); +be_define_const_str(_X27_X20_X2D_X20, "' - ", 3420378487u, 0, 4, &be_const_str_get_bri); +be_define_const_str(_X28_X29, "()", 685372826u, 0, 2, &be_const_str_Partition_info); +be_define_const_str(_X29, ")", 739023492u, 0, 1, &be_const_str_count); +be_define_const_str(_X2A, "*", 789356349u, 0, 1, &be_const_str_groups); +be_define_const_str(_X2B, "+", 772578730u, 0, 1, &be_const_str_create_custom_widget); +be_define_const_str(_X2C, ",", 688690635u, 0, 1, &be_const_str_cmd); +be_define_const_str(_X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, ",\"AXP192\":{\"VBusVoltage\":%.3f,\"VBusCurrent\":%.1f,\"BattVoltage\":%.3f,\"BattCurrent\":%.1f,\"Temperature\":%.1f}", 2598755376u, 0, 106, &be_const_str_SERIAL_8E1); +be_define_const_str(_X2D, "-", 671913016u, 0, 1, &be_const_str__error); +be_define_const_str(_X2D_X2A, "-*", 499980374u, 0, 2, &be_const_str_escape); +be_define_const_str(_X2E, ".", 722245873u, 0, 1, &be_const_str_content_send); +be_define_const_str(_X2E_X2E, "..", 2748622605u, 0, 2, &be_const_str_toint); +be_define_const_str(_X2Eautoconf, ".autoconf", 2524679088u, 0, 9, &be_const_str_HTTP_POST); +be_define_const_str(_X2Ebe, ".be", 1325797348u, 0, 3, &be_const_str_SERIAL_5O2); +be_define_const_str(_X2Ebec, ".bec", 3985273221u, 0, 4, &be_const_str__change_buffer); +be_define_const_str(_X2Elen, ".len", 850842136u, 0, 4, &be_const_str_BRY_X3A_X20argument_X20must_X20be_X20a_X20function); +be_define_const_str(_X2Ep, ".p", 1171526419u, 0, 2, &be_const_str_get_aps_voltage); +be_define_const_str(_X2Esize, ".size", 1965188224u, 0, 5, &be_const_str_SERIAL_5N1); +be_define_const_str(_X2Etapp, ".tapp", 1363391594u, 0, 5, NULL); +be_define_const_str(_X2Ew, ".w", 1255414514u, 0, 2, &be_const_str_web_add_handler); +be_define_const_str(_X2F, "/", 705468254u, 0, 1, &be_const_str_get_temp); +be_define_const_str(_X2F_X2Eautoconf, "/.autoconf", 2212074393u, 0, 10, &be_const_str__global_def); +be_define_const_str(_X2F_X3Frst_X3D, "/?rst=", 580074707u, 0, 6, &be_const_str_SERIAL_8O2); +be_define_const_str(_X2Fac, "/ac", 3904651978u, 0, 3, NULL); +be_define_const_str(_X2Flights_X2F, "/lights/", 2370247908u, 0, 8, &be_const_str_BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s); +be_define_const_str(_X2Fstate_X2F, "/state/", 4226179876u, 0, 7, &be_const_str_add); +be_define_const_str(00, "00", 569209421u, 0, 2, NULL); +be_define_const_str(0x_X2502X, "0x%02X", 2626549866u, 0, 6, &be_const_str_CFG_X3A_X20removing_X20autoconf_X20files); +be_define_const_str(_X3A, ":", 1057798253u, 0, 1, NULL); +be_define_const_str(_X3C, "<", 957132539u, 0, 1, &be_const_str_SERIAL_6E2); +be_define_const_str(_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 3546571739u, 0, 11, &be_const_str_add_light); +be_define_const_str(_X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 1863865923u, 0, 16, &be_const_str_SERIAL_7O1); +be_define_const_str(_X3C_X3D, "<=", 2499223986u, 0, 2, &be_const_str_ct); +be_define_const_str(_X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, "", 3147934216u, 0, 82, &be_const_str_CFG_X3A_X20ran_X20_X20); +be_define_const_str(_X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, "", 1205771629u, 0, 72, &be_const_str_is_running); +be_define_const_str(_X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, "
", 842307168u, 0, 77, &be_const_str__X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E); +be_define_const_str(_X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, "", 2342198361u, 0, 61, &be_const_str_get_bat_current); +be_define_const_str(_X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, "", 179503047u, 0, 73, &be_const_str_fromb64); +be_define_const_str(_X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, "
", 1336654704u, 0, 49, &be_const_str_get_current_module_path); +be_define_const_str(_X3Clambda_X3E, "", 607256038u, 0, 8, &be_const_str_SERIAL_7N1); be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Current auto-configuration", 4212500780u, 0, 82, NULL); -be_define_const_str(_X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, " Select new auto-configuration", 1926223891u, 0, 80, &be_const_str_get_warning_level); -be_define_const_str(_X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, "", 510303524u, 0, 30, &be_const_str_BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s); -be_define_const_str(_X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, "", 3994619755u, 0, 54, &be_const_str_pc_abs); -be_define_const_str(_X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, "

Exception:
'%s'
%s

", 4252565082u, 0, 59, &be_const_str_back_forth); -be_define_const_str(_X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, "

", 2052843416u, 0, 25, NULL); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, "

", 2058443583u, 0, 110, &be_const_str_CFG_X3A_X20loading_X20); -be_define_const_str(_X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, "

 (This feature requires an internet connection)

", 2719266486u, 0, 74, &be_const_str_find_op); -be_define_const_str(_X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, "

Current configuration:

%s

", 4115655761u, 0, 46, &be_const_str__available); -be_define_const_str(_X3Cselect_X20name_X3D_X27zip_X27_X3E, "", 4247924536u, 0, 19, &be_const_str_False); +be_define_const_str(_X3D, "=", 940354920u, 0, 1, &be_const_str_save); +be_define_const_str(_X3D_X3C_X3E_X21, "=<>!", 2664470277u, 0, 4, &be_const_str_true); +be_define_const_str(_X3D_X3D, "==", 2431966415u, 0, 2, &be_const_str_autorun); +be_define_const_str(_X3E, ">", 990687777u, 0, 1, &be_const_str_get_option); +be_define_const_str(_X3E_X3D, ">=", 284975636u, 0, 2, &be_const_str_content_response); +be_define_const_str(_X3F, "?", 973910158u, 0, 1, &be_const_str_SERIAL_6E1); +be_define_const_str(AA50, "AA50", 2265997666u, 0, 4, &be_const_str_imax); +be_define_const_str(AXP192, "AXP192", 757230128u, 0, 6, &be_const_str_button_pressed); +be_define_const_str(Animate_X20pc_X20is_X20out_X20of_X20range, "Animate pc is out of range", 1854929421u, 0, 26, &be_const_str_hs2rgb); +be_define_const_str(Auto_X2Dconfiguration, "Auto-configuration", 1665006109u, 0, 18, NULL); +be_define_const_str(BECDFE, "BECDFE", 608341218u, 0, 6, &be_const_str__persist_X2Ejson); +be_define_const_str(BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, "BRY: ERROR, bad json: ", 2715135809u, 0, 22, &be_const_str_RGBW); +be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27, "BRY: Exception> '", 3883673906u, 0, 17, &be_const_str__X5D_X2C_X0A_X20_X20); +be_define_const_str(BRY_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: Exception> '%s' - %s", 2246990964u, 0, 25, &be_const_str_before_del); +be_define_const_str(BRY_X3A_X20argument_X20must_X20be_X20a_X20function, "BRY: argument must be a function", 3917068408u, 0, 32, &be_const_str_fast_loop_enabled); be_define_const_str(BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29, "BRY: bytecode has wrong version '%s' (%i)", 2140321415u, 0, 41, NULL); -be_define_const_str(BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, "BRY: corrupt bytecode '%s'", 4009923544u, 0, 26, &be_const_str_CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem); -be_define_const_str(BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, "BRY: could not save compiled file %s (%s)", 736659787u, 0, 41, NULL); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, "BRY: failed to load '%s' (%s - %s)", 1047433014u, 0, 34, NULL); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, "BRY: failed to load _persist.json", 2991913445u, 0, 33, &be_const_str_Invalid_X20ota_X20partition_X20number); -be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, "BRY: failed to load compiled '%s' (%s)", 3488122666u, 0, 38, &be_const_str_fast_loop_enabled); -be_define_const_str(BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: failed to run compiled code '%s' - %s", 380265962u, 0, 42, &be_const_str_rotate); -be_define_const_str(BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, "BRY: invalid hue payload: ", 203709367u, 0, 26, &be_const_str_get_factory_slot); -be_define_const_str(BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, "BRY: method not allowed, use a closure like '/ args -> obj.func(args)'", 177121572u, 0, 70, &be_const_str_lvgl_event_dispatch); -be_define_const_str(BUTTON_CONFIGURATION, "BUTTON_CONFIGURATION", 70820856u, 0, 20, &be_const_str_super); -be_define_const_str(CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, "CFG: 'init.bat' done, restarting", 1569670677u, 0, 32, &be_const_str_tasmota_log_reader); -be_define_const_str(CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "CFG: Exception> '%s' - %s", 1228874553u, 0, 25, &be_const_str_CFG_X3A_X20removed_X20file_X20_X27_X25s_X27); -be_define_const_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found, "CFG: No '*.autoconf' file found", 755798501u, 0, 31, &be_const_str_RGBW); -be_define_const_str(CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, "CFG: could not run %s (%s - %s)", 1428829580u, 0, 31, &be_const_str_animate); -be_define_const_str(CFG_X3A_X20downloading_X20_X27_X25s_X27, "CFG: downloading '%s'", 589480701u, 0, 21, NULL); -be_define_const_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, "CFG: exception '%s' - '%s'", 4095407913u, 0, 26, &be_const_str_read13); -be_define_const_str(CFG_X3A_X20loaded_X20_X20, "CFG: loaded ", 3710273538u, 0, 13, &be_const_str_asstring); -be_define_const_str(CFG_X3A_X20loaded_X20_X27_X25s_X27, "CFG: loaded '%s'", 1699028828u, 0, 16, &be_const_str_addr); -be_define_const_str(CFG_X3A_X20loading_X20, "CFG: loading ", 4010361503u, 0, 13, &be_const_str__unsubscribe); -be_define_const_str(CFG_X3A_X20loading_X20_X27_X25s_X27, "CFG: loading '%s'", 2285306097u, 0, 17, NULL); -be_define_const_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, "CFG: multiple autoconf files found, aborting ('%s' + '%s')", 197663371u, 0, 58, &be_const_str_map); -be_define_const_str(CFG_X3A_X20ran_X20_X20, "CFG: ran ", 3579570472u, 0, 10, &be_const_str_cb_obj); +be_define_const_str(BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, "BRY: corrupt bytecode '%s'", 4009923544u, 0, 26, NULL); +be_define_const_str(BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, "BRY: could not save compiled file %s (%s)", 736659787u, 0, 41, &be_const_str_DIMMER); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, "BRY: failed to load '%s' (%s - %s)", 1047433014u, 0, 34, &be_const_str_MI32); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, "BRY: failed to load _persist.json", 2991913445u, 0, 33, &be_const_str_CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found); +be_define_const_str(BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, "BRY: failed to load compiled '%s' (%s)", 3488122666u, 0, 38, &be_const_str_lower); +be_define_const_str(BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, "BRY: failed to run compiled code '%s' - %s", 380265962u, 0, 42, &be_const_str_nan); +be_define_const_str(BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, "BRY: invalid hue payload: ", 203709367u, 0, 26, &be_const_str_try_get_bec_version); +be_define_const_str(BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, "BRY: method not allowed, use a closure like '/ args -> obj.func(args)'", 177121572u, 0, 70, &be_const_str_connection_error); +be_define_const_str(BUTTON_CONFIGURATION, "BUTTON_CONFIGURATION", 70820856u, 0, 20, NULL); +be_define_const_str(CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, "CFG: 'init.bat' done, restarting", 1569670677u, 0, 32, &be_const_str__ccmd); +be_define_const_str(CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, "CFG: Exception> '%s' - %s", 1228874553u, 0, 25, &be_const_str_TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27); +be_define_const_str(CFG_X3A_X20No_X20_X27_X2A_X2Eautoconf_X27_X20file_X20found, "CFG: No '*.autoconf' file found", 755798501u, 0, 31, &be_const_str_the_X20second_X20argument_X20is_X20not_X20a_X20function); +be_define_const_str(CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, "CFG: could not run %s (%s - %s)", 1428829580u, 0, 31, &be_const_str_is_ota); +be_define_const_str(CFG_X3A_X20downloading_X20_X27_X25s_X27, "CFG: downloading '%s'", 589480701u, 0, 21, &be_const_str__debug_present); +be_define_const_str(CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, "CFG: exception '%s' - '%s'", 4095407913u, 0, 26, &be_const_str_get_warning_level); +be_define_const_str(CFG_X3A_X20loaded_X20_X20, "CFG: loaded ", 3710273538u, 0, 13, &be_const_str_introspect); +be_define_const_str(CFG_X3A_X20loaded_X20_X27_X25s_X27, "CFG: loaded '%s'", 1699028828u, 0, 16, &be_const_str_RGB); +be_define_const_str(CFG_X3A_X20loading_X20, "CFG: loading ", 4010361503u, 0, 13, &be_const_str_return); +be_define_const_str(CFG_X3A_X20loading_X20_X27_X25s_X27, "CFG: loading '%s'", 2285306097u, 0, 17, &be_const_str_SK6812_GRBW); +be_define_const_str(CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, "CFG: multiple autoconf files found, aborting ('%s' + '%s')", 197663371u, 0, 58, &be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker); +be_define_const_str(CFG_X3A_X20ran_X20_X20, "CFG: ran ", 3579570472u, 0, 10, &be_const_str_web_send); be_define_const_str(CFG_X3A_X20removed_X20file_X20_X27_X25s_X27, "CFG: removed file '%s'", 2048602473u, 0, 22, NULL); -be_define_const_str(CFG_X3A_X20removing_X20autoconf_X20files, "CFG: removing autoconf files", 4014704970u, 0, 28, &be_const_str__end_transmission); -be_define_const_str(CFG_X3A_X20removing_X20first_X20time_X20marker, "CFG: removing first time marker", 2125556683u, 0, 31, &be_const_str_pixel_count); -be_define_const_str(CFG_X3A_X20return_code_X3D_X25i, "CFG: return_code=%i", 2059897320u, 0, 19, &be_const_str_uuid4); -be_define_const_str(CFG_X3A_X20running_X20, "CFG: running ", 2478334534u, 0, 13, &be_const_str_available); -be_define_const_str(CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem, "CFG: skipping 'display.ini' because already present in file-system", 3965549264u, 0, 66, &be_const_str_fromhex); -be_define_const_str(CT, "CT", 1792671826u, 0, 2, &be_const_str_closure); -be_define_const_str(DIMMER, "DIMMER", 4049308363u, 0, 6, &be_const_str_atan2); -be_define_const_str(EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3217293201u, 0, 32, NULL); -be_define_const_str(EVENT_DELETE, "EVENT_DELETE", 282828603u, 0, 12, &be_const_str_dirty); -be_define_const_str(FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2684107141u, 0, 48, &be_const_str_add_cb_event_closure); -be_define_const_str(False, "False", 2541049336u, 0, 5, &be_const_str_add_fast_loop); -be_define_const_str(GET, "GET", 2531704439u, 0, 3, &be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf); -be_define_const_str(HTTP_GET, "HTTP_GET", 1722467738u, 0, 8, &be_const_str_spiffs); -be_define_const_str(HTTP_POST, "HTTP_POST", 1999554144u, 0, 9, &be_const_str_attrdump); -be_define_const_str(I2C_X3A, "I2C:", 813483371u, 0, 4, &be_const_str_log); -be_define_const_str(I2C_Driver, "I2C_Driver", 1714501658u, 0, 10, &be_const_str_group_def); -be_define_const_str(Invalid_X20ota_X20partition_X20number, "Invalid ota partition number", 1611602265u, 0, 28, NULL); -be_define_const_str(LVG_X3A_X20call_X20to_X20unsupported_X20callback, "LVG: call to unsupported callback", 504176819u, 0, 33, &be_const_str_add_cmd); -be_define_const_str(Leds, "Leds", 2709245275u, 0, 4, &be_const_str_widget_group_def); -be_define_const_str(MAX_RMT, "MAX_RMT", 1615574873u, 0, 7, &be_const_str_duration); -be_define_const_str(MD5, "MD5", 1935726387u, 0, 3, &be_const_str_compress); -be_define_const_str(MI32, "MI32", 4074273414u, 0, 4, NULL); -be_define_const_str(No_X20SPIFFS_X20partition_X20found, "No SPIFFS partition found", 4165718279u, 0, 25, &be_const_str_ccronexpr); -be_define_const_str(None, "None", 810547195u, 0, 4, &be_const_str_subscribe); -be_define_const_str(OPTION_A, "OPTION_A", 1133299440u, 0, 8, &be_const_str_asin); -be_define_const_str(POST, "POST", 1929554311u, 0, 4, &be_const_str_add_driver); -be_define_const_str(Parameter_X20error, "Parameter error", 3840042038u, 0, 15, &be_const_str_WS2812); -be_define_const_str(Partition, "Partition", 3077057705u, 0, 9, &be_const_str_lv); -be_define_const_str(Partition_info, "Partition_info", 3970922042u, 0, 14, &be_const_str_cb_do_nothing); -be_define_const_str(Partition_otadata, "Partition_otadata", 2666256496u, 0, 17, NULL); -be_define_const_str(RELAY, "RELAY", 2163786658u, 0, 5, &be_const_str_create_custom_widget); -be_define_const_str(RGB, "RGB", 3386082140u, 0, 3, &be_const_str_connection_error); -be_define_const_str(RGBCT, "RGBCT", 8076251u, 0, 5, &be_const_str_strftime); -be_define_const_str(RGBW, "RGBW", 3270986321u, 0, 4, &be_const_str_exec_tele); -be_define_const_str(Restart_X201, "Restart 1", 3504455855u, 0, 9, &be_const_str_widget_cb); -be_define_const_str(SERIAL_5E1, "SERIAL_5E1", 1163775235u, 0, 10, &be_const_str__rmt); -be_define_const_str(SERIAL_5E2, "SERIAL_5E2", 1180552854u, 0, 10, &be_const_str_cos); +be_define_const_str(CFG_X3A_X20removing_X20autoconf_X20files, "CFG: removing autoconf files", 4014704970u, 0, 28, &be_const_str_gamma); +be_define_const_str(CFG_X3A_X20removing_X20first_X20time_X20marker, "CFG: removing first time marker", 2125556683u, 0, 31, &be_const_str_Leds); +be_define_const_str(CFG_X3A_X20return_code_X3D_X25i, "CFG: return_code=%i", 2059897320u, 0, 19, &be_const_str_page_autoconf_ctl); +be_define_const_str(CFG_X3A_X20running_X20, "CFG: running ", 2478334534u, 0, 13, &be_const_str__settings_ptr); +be_define_const_str(CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem, "CFG: skipping 'display.ini' because already present in file-system", 3965549264u, 0, 66, &be_const_str_remove_rule); +be_define_const_str(CT, "CT", 1792671826u, 0, 2, &be_const_str_id_X20must_X20be_X20of_X20type_X20_X27int_X27); +be_define_const_str(DIMMER, "DIMMER", 4049308363u, 0, 6, NULL); +be_define_const_str(EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 3217293201u, 0, 32, &be_const_str_get_vbus_voltage); +be_define_const_str(EVENT_DELETE, "EVENT_DELETE", 282828603u, 0, 12, &be_const_str_set_mode_rgb); +be_define_const_str(FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 2684107141u, 0, 48, NULL); +be_define_const_str(False, "False", 2541049336u, 0, 5, &be_const_str__X5D); +be_define_const_str(GET, "GET", 2531704439u, 0, 3, NULL); +be_define_const_str(HTTP_GET, "HTTP_GET", 1722467738u, 0, 8, &be_const_str_I2C_Driver); +be_define_const_str(HTTP_POST, "HTTP_POST", 1999554144u, 0, 9, &be_const_str_assign_rmt); +be_define_const_str(I2C_X3A, "I2C:", 813483371u, 0, 4, NULL); +be_define_const_str(I2C_Driver, "I2C_Driver", 1714501658u, 0, 10, &be_const_str_full_state); +be_define_const_str(Invalid_X20ota_X20partition_X20number, "Invalid ota partition number", 1611602265u, 0, 28, &be_const_str_every_second); +be_define_const_str(LVG_X3A_X20call_X20to_X20unsupported_X20callback, "LVG: call to unsupported callback", 504176819u, 0, 33, &be_const_str_subscribe); +be_define_const_str(Leds, "Leds", 2709245275u, 0, 4, &be_const_str_ins_goto); +be_define_const_str(MAX_RMT, "MAX_RMT", 1615574873u, 0, 7, &be_const_str_internal_error); +be_define_const_str(MD5, "MD5", 1935726387u, 0, 3, &be_const_str_init); +be_define_const_str(MI32, "MI32", 4074273414u, 0, 4, &be_const_str_state); +be_define_const_str(No_X20SPIFFS_X20partition_X20found, "No SPIFFS partition found", 4165718279u, 0, 25, NULL); +be_define_const_str(None, "None", 810547195u, 0, 4, NULL); +be_define_const_str(OPTION_A, "OPTION_A", 1133299440u, 0, 8, &be_const_str_number); +be_define_const_str(POST, "POST", 1929554311u, 0, 4, &be_const_str_iter); +be_define_const_str(Parameter_X20error, "Parameter error", 3840042038u, 0, 15, &be_const_str__energy); +be_define_const_str(Partition, "Partition", 3077057705u, 0, 9, &be_const_str_fromptr); +be_define_const_str(Partition_info, "Partition_info", 3970922042u, 0, 14, &be_const_str_remove_light); +be_define_const_str(Partition_otadata, "Partition_otadata", 2666256496u, 0, 17, &be_const_str_connected); +be_define_const_str(RELAY, "RELAY", 2163786658u, 0, 5, &be_const_str_WS2812); +be_define_const_str(RGB, "RGB", 3386082140u, 0, 3, &be_const_str__fl); +be_define_const_str(RGBCT, "RGBCT", 8076251u, 0, 5, NULL); +be_define_const_str(RGBW, "RGBW", 3270986321u, 0, 4, &be_const_str_raw); +be_define_const_str(Restart_X201, "Restart 1", 3504455855u, 0, 9, NULL); +be_define_const_str(SERIAL_5E1, "SERIAL_5E1", 1163775235u, 0, 10, &be_const_str_member); +be_define_const_str(SERIAL_5E2, "SERIAL_5E2", 1180552854u, 0, 10, &be_const_str_gamma8); be_define_const_str(SERIAL_5N1, "SERIAL_5N1", 3313031680u, 0, 10, NULL); -be_define_const_str(SERIAL_5N2, "SERIAL_5N2", 3363364537u, 0, 10, &be_const_str_crc8); -be_define_const_str(SERIAL_5O1, "SERIAL_5O1", 3782657917u, 0, 10, &be_const_str_from_to); -be_define_const_str(SERIAL_5O2, "SERIAL_5O2", 3732325060u, 0, 10, &be_const_str__class); -be_define_const_str(SERIAL_6E1, "SERIAL_6E1", 334249486u, 0, 10, &be_const_str__anonymous_); -be_define_const_str(SERIAL_6E2, "SERIAL_6E2", 317471867u, 0, 10, &be_const_str_next_cron); -be_define_const_str(SERIAL_6N1, "SERIAL_6N1", 198895701u, 0, 10, &be_const_str_arg); -be_define_const_str(SERIAL_6N2, "SERIAL_6N2", 148562844u, 0, 10, NULL); -be_define_const_str(SERIAL_6O1, "SERIAL_6O1", 266153272u, 0, 10, &be_const_str_remove_cmd); -be_define_const_str(SERIAL_6O2, "SERIAL_6O2", 316486129u, 0, 10, NULL); -be_define_const_str(SERIAL_7E1, "SERIAL_7E1", 147718061u, 0, 10, &be_const_str_data); -be_define_const_str(SERIAL_7E2, "SERIAL_7E2", 97385204u, 0, 10, &be_const_str_discover); -be_define_const_str(SERIAL_7N1, "SERIAL_7N1", 1891060246u, 0, 10, &be_const_str_id); -be_define_const_str(SERIAL_7N2, "SERIAL_7N2", 1874282627u, 0, 10, &be_const_str__settings_def); -be_define_const_str(SERIAL_7O1, "SERIAL_7O1", 1823802675u, 0, 10, &be_const_str_SERIAL_8N1); -be_define_const_str(SERIAL_7O2, "SERIAL_7O2", 1840580294u, 0, 10, &be_const_str_SERIAL_8N2); -be_define_const_str(SERIAL_8E1, "SERIAL_8E1", 2371121616u, 0, 10, &be_const_str__ptr); -be_define_const_str(SERIAL_8E2, "SERIAL_8E2", 2421454473u, 0, 10, &be_const_str___iterator__); -be_define_const_str(SERIAL_8N1, "SERIAL_8N1", 2369297235u, 0, 10, NULL); -be_define_const_str(SERIAL_8N2, "SERIAL_8N2", 2386074854u, 0, 10, &be_const_str_get_alternate); -be_define_const_str(SERIAL_8O1, "SERIAL_8O1", 289122742u, 0, 10, &be_const_str_crc32); -be_define_const_str(SERIAL_8O2, "SERIAL_8O2", 272345123u, 0, 10, &be_const_str_engine); -be_define_const_str(SK6812_GRBW, "SK6812_GRBW", 81157857u, 0, 11, &be_const_str_can_show); -be_define_const_str(TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, "TAP: Loaded Tasmota App '%s'", 926477145u, 0, 28, &be_const_str_valuer_error); -be_define_const_str(Tasmota, "Tasmota", 4047617668u, 0, 7, &be_const_str_false); -be_define_const_str(Tele, "Tele", 1329980653u, 0, 4, NULL); -be_define_const_str(Too_X20many_X20partiition_X20slots, "Too many partiition slots", 3190277896u, 0, 25, &be_const_str_io_error); -be_define_const_str(Trigger, "Trigger", 2783579555u, 0, 7, &be_const_str_has); -be_define_const_str(True, "True", 3453902341u, 0, 4, &be_const_str_display_X2Eini); -be_define_const_str(Unknown, "Unknown", 3424652889u, 0, 7, NULL); -be_define_const_str(Unknown_X20command, "Unknown command", 1830905432u, 0, 15, &be_const_str_set_light); -be_define_const_str(WS2812, "WS2812", 3539741218u, 0, 6, NULL); -be_define_const_str(WS2812_GRB, "WS2812_GRB", 1736405692u, 0, 10, NULL); -be_define_const_str(Wire, "Wire", 1938276536u, 0, 4, &be_const_str_set_pixel_color); +be_define_const_str(SERIAL_5N2, "SERIAL_5N2", 3363364537u, 0, 10, NULL); +be_define_const_str(SERIAL_5O1, "SERIAL_5O1", 3782657917u, 0, 10, &be_const_str_traceback); +be_define_const_str(SERIAL_5O2, "SERIAL_5O2", 3732325060u, 0, 10, &be_const_str_Tasmota); +be_define_const_str(SERIAL_6E1, "SERIAL_6E1", 334249486u, 0, 10, NULL); +be_define_const_str(SERIAL_6E2, "SERIAL_6E2", 317471867u, 0, 10, &be_const_str__filename); +be_define_const_str(SERIAL_6N1, "SERIAL_6N1", 198895701u, 0, 10, &be_const_str_add_cron); +be_define_const_str(SERIAL_6N2, "SERIAL_6N2", 148562844u, 0, 10, &be_const_str_chars_in_string); +be_define_const_str(SERIAL_6O1, "SERIAL_6O1", 266153272u, 0, 10, &be_const_str_Too_X20many_X20partiition_X20slots); +be_define_const_str(SERIAL_6O2, "SERIAL_6O2", 316486129u, 0, 10, &be_const_str_display_X2Eini); +be_define_const_str(SERIAL_7E1, "SERIAL_7E1", 147718061u, 0, 10, &be_const_str_stop_iteration); +be_define_const_str(SERIAL_7E2, "SERIAL_7E2", 97385204u, 0, 10, &be_const_str_copy); +be_define_const_str(SERIAL_7N1, "SERIAL_7N1", 1891060246u, 0, 10, &be_const_str_get_vbus_current); +be_define_const_str(SERIAL_7N2, "SERIAL_7N2", 1874282627u, 0, 10, &be_const_str_Trigger); +be_define_const_str(SERIAL_7O1, "SERIAL_7O1", 1823802675u, 0, 10, &be_const_str_json); +be_define_const_str(SERIAL_7O2, "SERIAL_7O2", 1840580294u, 0, 10, &be_const_str_name); +be_define_const_str(SERIAL_8E1, "SERIAL_8E1", 2371121616u, 0, 10, &be_const_str_for); +be_define_const_str(SERIAL_8E2, "SERIAL_8E2", 2421454473u, 0, 10, &be_const_str_ptr); +be_define_const_str(SERIAL_8N1, "SERIAL_8N1", 2369297235u, 0, 10, &be_const_str_crc8); +be_define_const_str(SERIAL_8N2, "SERIAL_8N2", 2386074854u, 0, 10, &be_const_str_missing_X20name); +be_define_const_str(SERIAL_8O1, "SERIAL_8O1", 289122742u, 0, 10, &be_const_str_wd); +be_define_const_str(SERIAL_8O2, "SERIAL_8O2", 272345123u, 0, 10, &be_const_str_elif); +be_define_const_str(SK6812_GRBW, "SK6812_GRBW", 81157857u, 0, 11, NULL); +be_define_const_str(TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, "TAP: Loaded Tasmota App '%s'", 926477145u, 0, 28, &be_const_str_next_cron); +be_define_const_str(Tasmota, "Tasmota", 4047617668u, 0, 7, &be_const_str_phy); +be_define_const_str(Tele, "Tele", 1329980653u, 0, 4, &be_const_str_cb_event_closure); +be_define_const_str(Too_X20many_X20partiition_X20slots, "Too many partiition slots", 3190277896u, 0, 25, &be_const_str_get_event_cb); +be_define_const_str(Trigger, "Trigger", 2783579555u, 0, 7, &be_const_str_getbits); +be_define_const_str(True, "True", 3453902341u, 0, 4, NULL); +be_define_const_str(Unknown, "Unknown", 3424652889u, 0, 7, &be_const_str_crc); +be_define_const_str(Unknown_X20command, "Unknown command", 1830905432u, 0, 15, &be_const_str_every_50ms); +be_define_const_str(WS2812, "WS2812", 3539741218u, 0, 6, &be_const_str_ismapped); +be_define_const_str(WS2812_GRB, "WS2812_GRB", 1736405692u, 0, 10, &be_const_str_a); +be_define_const_str(Wire, "Wire", 1938276536u, 0, 4, &be_const_str__X7D); be_define_const_str(_X5B, "[", 3725336506u, 0, 1, NULL); -be_define_const_str(_X5D, "]", 3624670792u, 0, 1, &be_const_str_enabled); -be_define_const_str(_X5D_X2C_X0A_X20_X20, "],\n ", 2456223650u, 0, 5, NULL); -be_define_const_str(_, "_", 3658226030u, 0, 1, &be_const_str_geti); -be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_lvgl_timer_dispatch); -be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_pin); -be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, NULL); -be_define_const_str(_anonymous_, "_anonymous_", 1957281476u, 0, 11, NULL); -be_define_const_str(_archive, "_archive", 4004559404u, 0, 8, &be_const_str_write_gpio); -be_define_const_str(_available, "_available", 1306196581u, 0, 10, NULL); -be_define_const_str(_begin_transmission, "_begin_transmission", 2779461176u, 0, 19, &be_const_str_codedump); -be_define_const_str(_buffer, "_buffer", 2044888568u, 0, 7, &be_const_str_get_bat_voltage); -be_define_const_str(_ccmd, "_ccmd", 2163421413u, 0, 5, NULL); -be_define_const_str(_change_buffer, "_change_buffer", 2101848693u, 0, 14, NULL); -be_define_const_str(_class, "_class", 2732146350u, 0, 6, &be_const_str_detected_X20on_X20bus); -be_define_const_str(_cmd, "_cmd", 3419822142u, 0, 4, &be_const_str_connected); -be_define_const_str(_crons, "_crons", 1000733579u, 0, 6, &be_const_str_instance_X20required); -be_define_const_str(_debug_present, "_debug_present", 4063411725u, 0, 14, &be_const_str_content_flush); -be_define_const_str(_def, "_def", 1985022181u, 0, 4, NULL); -be_define_const_str(_dirty, "_dirty", 283846766u, 0, 6, &be_const_str_webclient); -be_define_const_str(_drivers, "_drivers", 3260328985u, 0, 8, &be_const_str_factory); -be_define_const_str(_end_transmission, "_end_transmission", 3237480400u, 0, 17, &be_const_str_for); -be_define_const_str(_energy, "_energy", 535372070u, 0, 7, &be_const_str__subscribe); -be_define_const_str(_error, "_error", 1132109656u, 0, 6, &be_const_str__t); -be_define_const_str(_filename, "_filename", 1430813195u, 0, 9, &be_const_str_raise); -be_define_const_str(_fl, "_fl", 4042564892u, 0, 3, &be_const_str_add_header); -be_define_const_str(_global_addr, "_global_addr", 533766721u, 0, 12, &be_const_str_member); -be_define_const_str(_global_def, "_global_def", 646007001u, 0, 11, &be_const_str_f); -be_define_const_str(_p, "_p", 1594591802u, 0, 2, &be_const_str_adc_config); -be_define_const_str(_persist_X2Ejson, "_persist.json", 2008425138u, 0, 13, &be_const_str_argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize); -be_define_const_str(_ptr, "_ptr", 306235816u, 0, 4, &be_const_str__timers); -be_define_const_str(_read, "_read", 346717030u, 0, 5, &be_const_str_destructor_cb); -be_define_const_str(_request_from, "_request_from", 3965148604u, 0, 13, NULL); +be_define_const_str(_X5D, "]", 3624670792u, 0, 1, &be_const_str_rad); +be_define_const_str(_X5D_X2C_X0A_X20_X20, "],\n ", 2456223650u, 0, 5, &be_const_str_format); +be_define_const_str(_, "_", 3658226030u, 0, 1, &be_const_str_pop_path); +be_define_const_str(__iterator__, "__iterator__", 3884039703u, 0, 12, &be_const_str_x); +be_define_const_str(__lower__, "__lower__", 123855590u, 0, 9, &be_const_str_w); +be_define_const_str(__upper__, "__upper__", 3612202883u, 0, 9, &be_const_str_add_driver); +be_define_const_str(_anonymous_, "_anonymous_", 1957281476u, 0, 11, &be_const_str_get_current_module_name); +be_define_const_str(_archive, "_archive", 4004559404u, 0, 8, &be_const_str_cosh); +be_define_const_str(_available, "_available", 1306196581u, 0, 10, &be_const_str_reverse); +be_define_const_str(_begin_transmission, "_begin_transmission", 2779461176u, 0, 19, &be_const_str_available); +be_define_const_str(_buffer, "_buffer", 2044888568u, 0, 7, NULL); +be_define_const_str(_ccmd, "_ccmd", 2163421413u, 0, 5, &be_const_str_animators); +be_define_const_str(_change_buffer, "_change_buffer", 2101848693u, 0, 14, &be_const_str_read); +be_define_const_str(_class, "_class", 2732146350u, 0, 6, &be_const_str_back_forth); +be_define_const_str(_cmd, "_cmd", 3419822142u, 0, 4, &be_const_str_toptr); +be_define_const_str(_crons, "_crons", 1000733579u, 0, 6, &be_const_str__unsubscribe); +be_define_const_str(_debug_present, "_debug_present", 4063411725u, 0, 14, &be_const_str_argument_X20must_X20be_X20a_X20list); +be_define_const_str(_def, "_def", 1985022181u, 0, 4, &be_const_str_get_bat_power); +be_define_const_str(_dirty, "_dirty", 283846766u, 0, 6, &be_const_str_global); +be_define_const_str(_drivers, "_drivers", 3260328985u, 0, 8, &be_const_str_strptime); +be_define_const_str(_end_transmission, "_end_transmission", 3237480400u, 0, 17, &be_const_str__validate); +be_define_const_str(_energy, "_energy", 535372070u, 0, 7, &be_const_str_set_huesat); +be_define_const_str(_error, "_error", 1132109656u, 0, 6, &be_const_str__ptr); +be_define_const_str(_filename, "_filename", 1430813195u, 0, 9, &be_const_str_get_MAC); +be_define_const_str(_fl, "_fl", 4042564892u, 0, 3, NULL); +be_define_const_str(_global_addr, "_global_addr", 533766721u, 0, 12, &be_const_str_crc32_ota_seq); +be_define_const_str(_global_def, "_global_def", 646007001u, 0, 11, &be_const_str_ccronexpr); +be_define_const_str(_p, "_p", 1594591802u, 0, 2, &be_const_str_set_hum); +be_define_const_str(_persist_X2Ejson, "_persist.json", 2008425138u, 0, 13, NULL); +be_define_const_str(_ptr, "_ptr", 306235816u, 0, 4, &be_const_str_debug); +be_define_const_str(_read, "_read", 346717030u, 0, 5, &be_const_str_item); +be_define_const_str(_request_from, "_request_from", 3965148604u, 0, 13, &be_const_str_io_error); be_define_const_str(_rmt, "_rmt", 1094422685u, 0, 4, NULL); -be_define_const_str(_rules, "_rules", 4266217105u, 0, 6, &be_const_str__write); -be_define_const_str(_settings_def, "_settings_def", 3775560307u, 0, 13, &be_const_str_alternate); -be_define_const_str(_settings_ptr, "_settings_ptr", 1825772182u, 0, 13, &be_const_str_classof); -be_define_const_str(_subscribe, "_subscribe", 2888456770u, 0, 10, &be_const_str_detect); -be_define_const_str(_t, "_t", 1527481326u, 0, 2, &be_const_str_find_key_i); -be_define_const_str(_timers, "_timers", 2600100916u, 0, 7, &be_const_str_toupper); -be_define_const_str(_unsubscribe, "_unsubscribe", 2110993883u, 0, 12, &be_const_str_sat); -be_define_const_str(_validate, "_validate", 1742604448u, 0, 9, &be_const_str_ct); -be_define_const_str(_write, "_write", 2215462825u, 0, 6, &be_const_str_acos); -be_define_const_str(a, "a", 3826002220u, 0, 1, NULL); -be_define_const_str(abs, "abs", 709362235u, 0, 3, NULL); -be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_maxota); -be_define_const_str(active_otadata, "active_otadata", 3055353486u, 0, 14, &be_const_str_set_chg_current); -be_define_const_str(adc_config, "adc_config", 2769927572u, 0, 10, &be_const_str_int); -be_define_const_str(add, "add", 993596020u, 0, 3, &be_const_str_set_first_time); -be_define_const_str(add_anim, "add_anim", 3980662668u, 0, 8, &be_const_str_delete_all_configs); -be_define_const_str(add_cb_event_closure, "add_cb_event_closure", 1775958321u, 0, 20, &be_const_str_list_handlers); -be_define_const_str(add_cmd, "add_cmd", 3361630879u, 0, 7, &be_const_str_tolower); -be_define_const_str(add_cron, "add_cron", 2475327477u, 0, 8, &be_const_str_return); -be_define_const_str(add_driver, "add_driver", 1654458371u, 0, 10, &be_const_str_autoexec); -be_define_const_str(add_fast_loop, "add_fast_loop", 3025842946u, 0, 13, &be_const_str_item); -be_define_const_str(add_handler, "add_handler", 2055124119u, 0, 11, &be_const_str_arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj); -be_define_const_str(add_header, "add_header", 927130612u, 0, 10, &be_const_str_app); -be_define_const_str(add_light, "add_light", 3169328603u, 0, 9, NULL); -be_define_const_str(add_rule, "add_rule", 596540743u, 0, 8, &be_const_str_animators); -be_define_const_str(addr, "addr", 1087856498u, 0, 4, NULL); -be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_cb); -be_define_const_str(alternate, "alternate", 1140253277u, 0, 9, &be_const_str_autorun); -be_define_const_str(animate, "animate", 3885786800u, 0, 7, &be_const_str_code); -be_define_const_str(animators, "animators", 279858213u, 0, 9, &be_const_str_ctypes_bytes); -be_define_const_str(app, "app", 527074092u, 0, 3, &be_const_str_def); -be_define_const_str(arch, "arch", 2952804297u, 0, 4, &be_const_str_b); -be_define_const_str(arg, "arg", 1047474471u, 0, 3, &be_const_str_response_append); -be_define_const_str(arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj, "arg must be a subclass of lv_obj", 1641882079u, 0, 32, NULL); -be_define_const_str(arg_name, "arg_name", 1345046155u, 0, 8, &be_const_str_every_second); -be_define_const_str(arg_size, "arg_size", 3310243257u, 0, 8, &be_const_str_flush); +be_define_const_str(_rules, "_rules", 4266217105u, 0, 6, &be_const_str_ins_time); +be_define_const_str(_settings_def, "_settings_def", 3775560307u, 0, 13, &be_const_str_has); +be_define_const_str(_settings_ptr, "_settings_ptr", 1825772182u, 0, 13, NULL); +be_define_const_str(_subscribe, "_subscribe", 2888456770u, 0, 10, NULL); +be_define_const_str(_t, "_t", 1527481326u, 0, 2, &be_const_str_widget_width_def); +be_define_const_str(_timers, "_timers", 2600100916u, 0, 7, NULL); +be_define_const_str(_unsubscribe, "_unsubscribe", 2110993883u, 0, 12, &be_const_str_engine); +be_define_const_str(_validate, "_validate", 1742604448u, 0, 9, &be_const_str_group_def); +be_define_const_str(_write, "_write", 2215462825u, 0, 6, NULL); +be_define_const_str(a, "a", 3826002220u, 0, 1, &be_const_str_classname); +be_define_const_str(abs, "abs", 709362235u, 0, 3, &be_const_str_dirty); +be_define_const_str(accept, "accept", 136609321u, 0, 6, &be_const_str_connect); +be_define_const_str(acos, "acos", 1006755615u, 0, 4, &be_const_str_gen_cb); +be_define_const_str(active_otadata, "active_otadata", 3055353486u, 0, 14, &be_const_str_compile); +be_define_const_str(adc_config, "adc_config", 2769927572u, 0, 10, &be_const_str_zip); +be_define_const_str(add, "add", 993596020u, 0, 3, &be_const_str_bytes); +be_define_const_str(add_anim, "add_anim", 3980662668u, 0, 8, &be_const_str_app); +be_define_const_str(add_cb_event_closure, "add_cb_event_closure", 1775958321u, 0, 20, NULL); +be_define_const_str(add_cmd, "add_cmd", 3361630879u, 0, 7, &be_const_str_arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj); +be_define_const_str(add_cron, "add_cron", 2475327477u, 0, 8, NULL); +be_define_const_str(add_driver, "add_driver", 1654458371u, 0, 10, NULL); +be_define_const_str(add_fast_loop, "add_fast_loop", 3025842946u, 0, 13, &be_const_str_isinstance); +be_define_const_str(add_handler, "add_handler", 2055124119u, 0, 11, NULL); +be_define_const_str(add_header, "add_header", 927130612u, 0, 10, NULL); +be_define_const_str(add_light, "add_light", 3169328603u, 0, 9, &be_const_str_ctypes_bytes_dyn); +be_define_const_str(add_rule, "add_rule", 596540743u, 0, 8, &be_const_str_h); +be_define_const_str(addr, "addr", 1087856498u, 0, 4, &be_const_str_hue_ntv); +be_define_const_str(allocated, "allocated", 429986098u, 0, 9, &be_const_str_closure); +be_define_const_str(alternate, "alternate", 1140253277u, 0, 9, &be_const_str_keys); +be_define_const_str(animate, "animate", 3885786800u, 0, 7, NULL); +be_define_const_str(animators, "animators", 279858213u, 0, 9, &be_const_str_read_bytes); +be_define_const_str(app, "app", 527074092u, 0, 3, NULL); +be_define_const_str(arch, "arch", 2952804297u, 0, 4, &be_const_str_arg_size); +be_define_const_str(arg, "arg", 1047474471u, 0, 3, &be_const_str_page_autoconf_mgr); +be_define_const_str(arg_X20must_X20be_X20a_X20subclass_X20of_X20lv_obj, "arg must be a subclass of lv_obj", 1641882079u, 0, 32, &be_const_str_ctor); +be_define_const_str(arg_name, "arg_name", 1345046155u, 0, 8, NULL); +be_define_const_str(arg_size, "arg_size", 3310243257u, 0, 8, &be_const_str_bus); be_define_const_str(argument_X20must_X20be_X20a_X20function, "argument must be a function", 527172389u, 0, 27, NULL); -be_define_const_str(argument_X20must_X20be_X20a_X20list, "argument must be a list", 3056915661u, 0, 23, &be_const_str_subtype); -be_define_const_str(argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, "argument must be a list or a pointer+size", 241605448u, 0, 41, &be_const_str_value); -be_define_const_str(as, "as", 1579491469u, 67, 2, &be_const_str_get_string); -be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_decompress); +be_define_const_str(argument_X20must_X20be_X20a_X20list, "argument must be a list", 3056915661u, 0, 23, &be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson); +be_define_const_str(argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, "argument must be a list or a pointer+size", 241605448u, 0, 41, &be_const_str_break); +be_define_const_str(as, "as", 1579491469u, 67, 2, &be_const_str_esphttpd); +be_define_const_str(asin, "asin", 4272848550u, 0, 4, &be_const_str_destructor_cb); be_define_const_str(assert, "assert", 2774883451u, 0, 6, NULL); -be_define_const_str(assign_rmt, "assign_rmt", 1047642576u, 0, 10, &be_const_str_try); -be_define_const_str(asstring, "asstring", 1298225088u, 0, 8, &be_const_str_run_bat); -be_define_const_str(atan, "atan", 108579519u, 0, 4, NULL); -be_define_const_str(atan2, "atan2", 3173440503u, 0, 5, NULL); -be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, NULL); -be_define_const_str(autoexec, "autoexec", 3676861891u, 0, 8, &be_const_str_subtype_to_string); -be_define_const_str(autorun, "autorun", 1447527407u, 0, 7, &be_const_str_concat); -be_define_const_str(available, "available", 1727918744u, 0, 9, NULL); -be_define_const_str(b, "b", 3876335077u, 0, 1, &be_const_str_month); -be_define_const_str(back_forth, "back_forth", 2665042062u, 0, 10, &be_const_str_pin_used); -be_define_const_str(base_class, "base_class", 1107737279u, 0, 10, &be_const_str_type_to_string); -be_define_const_str(battery_present, "battery_present", 3588397058u, 0, 15, &be_const_str_get_event_cb); -be_define_const_str(before_del, "before_del", 815924436u, 0, 10, &be_const_str_nvs); -be_define_const_str(begin, "begin", 1748273790u, 0, 5, &be_const_str_end); -be_define_const_str(begin_multicast, "begin_multicast", 57647915u, 0, 15, NULL); -be_define_const_str(bool, "bool", 3365180733u, 0, 4, NULL); -be_define_const_str(break, "break", 3378807160u, 58, 5, &be_const_str_nil); -be_define_const_str(bri, "bri", 2112284244u, 0, 3, NULL); -be_define_const_str(bus, "bus", 1607822841u, 0, 3, &be_const_str_set_ldo_enable); -be_define_const_str(button_pressed, "button_pressed", 1694209616u, 0, 14, &be_const_str_page_autoconf_ctl); -be_define_const_str(byte, "byte", 1683620383u, 0, 4, &be_const_str_pow); -be_define_const_str(bytes, "bytes", 1706151940u, 0, 5, &be_const_str_lv_solidified); -be_define_const_str(c, "c", 3859557458u, 0, 1, &be_const_str_get_current_module_path); -be_define_const_str(call, "call", 3018949801u, 0, 4, &be_const_str_get_vbus_voltage); -be_define_const_str(call_native, "call_native", 1389147405u, 0, 11, &be_const_str_serial); -be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, &be_const_str_persist_X2E_p_X20is_X20not_X20a_X20map); -be_define_const_str(can_show, "can_show", 960091187u, 0, 8, &be_const_str_get_input_power_status); -be_define_const_str(cb, "cb", 1428787088u, 0, 2, NULL); -be_define_const_str(cb_do_nothing, "cb_do_nothing", 1488730702u, 0, 13, &be_const_str_get_cb_list); -be_define_const_str(cb_event_closure, "cb_event_closure", 3828267325u, 0, 16, &be_const_str_lv_module_init); -be_define_const_str(cb_obj, "cb_obj", 1195696482u, 0, 6, &be_const_str_efuse_em); -be_define_const_str(ccronexpr, "ccronexpr", 258146169u, 0, 9, &be_const_str_clear_first_time); -be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, NULL); -be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_hue); +be_define_const_str(assign_rmt, "assign_rmt", 1047642576u, 0, 10, &be_const_str_devices); +be_define_const_str(asstring, "asstring", 1298225088u, 0, 8, &be_const_str_invalid_X20GPIO_X20number); +be_define_const_str(atan, "atan", 108579519u, 0, 4, &be_const_str_check_not_method); +be_define_const_str(atan2, "atan2", 3173440503u, 0, 5, &be_const_str_millis); +be_define_const_str(attrdump, "attrdump", 1521571304u, 0, 8, &be_const_str_exp); +be_define_const_str(autoexec, "autoexec", 3676861891u, 0, 8, &be_const_str_getfloat); +be_define_const_str(autorun, "autorun", 1447527407u, 0, 7, &be_const_str_deg); +be_define_const_str(available, "available", 1727918744u, 0, 9, &be_const_str_ceil); +be_define_const_str(b, "b", 3876335077u, 0, 1, &be_const_str_light_state); +be_define_const_str(back_forth, "back_forth", 2665042062u, 0, 10, &be_const_str_seti); +be_define_const_str(base_class, "base_class", 1107737279u, 0, 10, &be_const_str_gc); +be_define_const_str(battery_present, "battery_present", 3588397058u, 0, 15, &be_const_str_detect); +be_define_const_str(before_del, "before_del", 815924436u, 0, 10, NULL); +be_define_const_str(begin, "begin", 1748273790u, 0, 5, NULL); +be_define_const_str(begin_multicast, "begin_multicast", 57647915u, 0, 15, &be_const_str_digital_write); +be_define_const_str(bool, "bool", 3365180733u, 0, 4, &be_const_str_char); +be_define_const_str(break, "break", 3378807160u, 58, 5, NULL); +be_define_const_str(bri, "bri", 2112284244u, 0, 3, &be_const_str_frombytes); +be_define_const_str(bus, "bus", 1607822841u, 0, 3, &be_const_str_flash); +be_define_const_str(button_pressed, "button_pressed", 1694209616u, 0, 14, &be_const_str_cb); +be_define_const_str(byte, "byte", 1683620383u, 0, 4, NULL); +be_define_const_str(bytes, "bytes", 1706151940u, 0, 5, &be_const_str_hasclient); +be_define_const_str(c, "c", 3859557458u, 0, 1, &be_const_str_clear); +be_define_const_str(call, "call", 3018949801u, 0, 4, &be_const_str_hue); +be_define_const_str(call_native, "call_native", 1389147405u, 0, 11, &be_const_str_floor); +be_define_const_str(calldepth, "calldepth", 3122364302u, 0, 9, &be_const_str_get_alternate); +be_define_const_str(can_show, "can_show", 960091187u, 0, 8, NULL); +be_define_const_str(cb, "cb", 1428787088u, 0, 2, &be_const_str_lv_event); +be_define_const_str(cb_do_nothing, "cb_do_nothing", 1488730702u, 0, 13, &be_const_str_send_multicast); +be_define_const_str(cb_event_closure, "cb_event_closure", 3828267325u, 0, 16, &be_const_str_json_fdump_list); +be_define_const_str(cb_obj, "cb_obj", 1195696482u, 0, 6, &be_const_str_set_auth); +be_define_const_str(ccronexpr, "ccronexpr", 258146169u, 0, 9, NULL); +be_define_const_str(ceil, "ceil", 1659167240u, 0, 4, &be_const_str_fromstring); +be_define_const_str(char, "char", 2823553821u, 0, 4, &be_const_str_concat); be_define_const_str(chars_in_string, "chars_in_string", 3148785132u, 0, 15, NULL); -be_define_const_str(check_not_method, "check_not_method", 2597324607u, 0, 16, &be_const_str_content_send_style); -be_define_const_str(check_privileged_access, "check_privileged_access", 3692933968u, 0, 23, &be_const_str_coord_arr); -be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_hs2rgb); -be_define_const_str(class_init_obj, "class_init_obj", 178410604u, 0, 14, &be_const_str_members); -be_define_const_str(classname, "classname", 1998589948u, 0, 9, NULL); +be_define_const_str(check_not_method, "check_not_method", 2597324607u, 0, 16, NULL); +be_define_const_str(check_privileged_access, "check_privileged_access", 3692933968u, 0, 23, &be_const_str_set_exten); +be_define_const_str(class, "class", 2872970239u, 57, 5, &be_const_str_get_image_size); +be_define_const_str(class_init_obj, "class_init_obj", 178410604u, 0, 14, &be_const_str_couldn_X27t_X20not_X20initialize_X20noepixelbus); +be_define_const_str(classname, "classname", 1998589948u, 0, 9, &be_const_str_coredump); be_define_const_str(classof, "classof", 1796577762u, 0, 7, NULL); -be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_remove_light); -be_define_const_str(clear_first_time, "clear_first_time", 632769909u, 0, 16, NULL); -be_define_const_str(clear_to, "clear_to", 3528002130u, 0, 8, &be_const_str_lower); -be_define_const_str(close, "close", 667630371u, 0, 5, NULL); -be_define_const_str(closure, "closure", 1548407746u, 0, 7, &be_const_str_scan); -be_define_const_str(cmd, "cmd", 4136785899u, 0, 3, &be_const_str_event); -be_define_const_str(cmd_res, "cmd_res", 921166762u, 0, 7, &be_const_str_exec_rules); -be_define_const_str(code, "code", 4180765940u, 0, 4, &be_const_str_exp); -be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_missing_X20name); -be_define_const_str(collect, "collect", 2399039025u, 0, 7, &be_const_str_init); -be_define_const_str(compile, "compile", 1000265118u, 0, 7, &be_const_str_devices); -be_define_const_str(compress, "compress", 2818084237u, 0, 8, &be_const_str_flash); -be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_light_to_id); -be_define_const_str(connect, "connect", 2866859257u, 0, 7, &be_const_str_get_image_size); -be_define_const_str(connected, "connected", 1424938192u, 0, 9, &be_const_str_deinit); -be_define_const_str(connection_error, "connection_error", 1358926260u, 0, 16, &be_const_str_read32); -be_define_const_str(constructor_cb, "constructor_cb", 2489105297u, 0, 14, &be_const_str_crc32_ota_seq); -be_define_const_str(contains, "contains", 1825239352u, 0, 8, &be_const_str_do); -be_define_const_str(content_button, "content_button", 1956476087u, 0, 14, &be_const_str_upper); -be_define_const_str(content_flush, "content_flush", 214922475u, 0, 13, &be_const_str_run_cron); -be_define_const_str(content_response, "content_response", 3881475860u, 0, 16, &be_const_str_set_auth); -be_define_const_str(content_send, "content_send", 1673733649u, 0, 12, &be_const_str_widget_instance_size); -be_define_const_str(content_send_style, "content_send_style", 1087907647u, 0, 18, &be_const_str_solidified); -be_define_const_str(content_start, "content_start", 2937509069u, 0, 13, NULL); -be_define_const_str(content_stop, "content_stop", 658554751u, 0, 12, &be_const_str_partition_core); -be_define_const_str(continue, "continue", 2977070660u, 59, 8, &be_const_str_lv_point); -be_define_const_str(coord_arr, "coord_arr", 4189963658u, 0, 9, &be_const_str_pop_path); -be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_lv_obj_class); -be_define_const_str(coredump, "coredump", 2141225116u, 0, 8, NULL); -be_define_const_str(cos, "cos", 4220379804u, 0, 3, NULL); -be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, &be_const_str_elements_X20must_X20be_X20a_X20lv_point); -be_define_const_str(couldn_X27t_X20not_X20initialize_X20noepixelbus, "couldn't not initialize noepixelbus", 2536490812u, 0, 35, &be_const_str_get_active); -be_define_const_str(count, "count", 967958004u, 0, 5, &be_const_str_isnan); -be_define_const_str(counters, "counters", 4095866864u, 0, 8, &be_const_str_fromstring); -be_define_const_str(crc, "crc", 3812935353u, 0, 3, &be_const_str_strip); -be_define_const_str(crc16, "crc16", 3504496746u, 0, 5, &be_const_str_files); -be_define_const_str(crc32, "crc32", 3571901412u, 0, 5, NULL); +be_define_const_str(clear, "clear", 1550717474u, 0, 5, &be_const_str_spiffs); +be_define_const_str(clear_first_time, "clear_first_time", 632769909u, 0, 16, &be_const_str_r); +be_define_const_str(clear_to, "clear_to", 3528002130u, 0, 8, &be_const_str_web_add_button); +be_define_const_str(close, "close", 667630371u, 0, 5, &be_const_str_get_battery_chargin_status); +be_define_const_str(closure, "closure", 1548407746u, 0, 7, &be_const_str_widget_event_cb); +be_define_const_str(cmd, "cmd", 4136785899u, 0, 3, &be_const_str_set_reachable); +be_define_const_str(cmd_res, "cmd_res", 921166762u, 0, 7, &be_const_str_xy); +be_define_const_str(code, "code", 4180765940u, 0, 4, NULL); +be_define_const_str(codedump, "codedump", 1786337906u, 0, 8, &be_const_str_nvs); +be_define_const_str(collect, "collect", 2399039025u, 0, 7, &be_const_str_map); +be_define_const_str(compile, "compile", 1000265118u, 0, 7, NULL); +be_define_const_str(compress, "compress", 2818084237u, 0, 8, &be_const_str_real); +be_define_const_str(concat, "concat", 4124019837u, 0, 6, &be_const_str_remove); +be_define_const_str(connect, "connect", 2866859257u, 0, 7, &be_const_str_function); +be_define_const_str(connected, "connected", 1424938192u, 0, 9, &be_const_str_read32); +be_define_const_str(connection_error, "connection_error", 1358926260u, 0, 16, &be_const_str_json_fdump); +be_define_const_str(constructor_cb, "constructor_cb", 2489105297u, 0, 14, &be_const_str_offseta); +be_define_const_str(contains, "contains", 1825239352u, 0, 8, &be_const_str_deinit); +be_define_const_str(content_button, "content_button", 1956476087u, 0, 14, NULL); +be_define_const_str(content_flush, "content_flush", 214922475u, 0, 13, NULL); +be_define_const_str(content_response, "content_response", 3881475860u, 0, 16, NULL); +be_define_const_str(content_send, "content_send", 1673733649u, 0, 12, NULL); +be_define_const_str(content_send_style, "content_send_style", 1087907647u, 0, 18, NULL); +be_define_const_str(content_start, "content_start", 2937509069u, 0, 13, &be_const_str_day); +be_define_const_str(content_stop, "content_stop", 658554751u, 0, 12, &be_const_str_tcpclient); +be_define_const_str(continue, "continue", 2977070660u, 59, 8, &be_const_str_do); +be_define_const_str(coord_arr, "coord_arr", 4189963658u, 0, 9, &be_const_str_detected_X20on_X20bus); +be_define_const_str(copy, "copy", 3848464964u, 0, 4, &be_const_str_json_append); +be_define_const_str(coredump, "coredump", 2141225116u, 0, 8, &be_const_str_switch_factory); +be_define_const_str(cos, "cos", 4220379804u, 0, 3, &be_const_str_every_100ms); +be_define_const_str(cosh, "cosh", 4099687964u, 0, 4, &be_const_str_web_sensor); +be_define_const_str(couldn_X27t_X20not_X20initialize_X20noepixelbus, "couldn't not initialize noepixelbus", 2536490812u, 0, 35, &be_const_str_top); +be_define_const_str(count, "count", 967958004u, 0, 5, NULL); +be_define_const_str(counters, "counters", 4095866864u, 0, 8, &be_const_str_on); +be_define_const_str(crc, "crc", 3812935353u, 0, 3, &be_const_str_pixel_size); +be_define_const_str(crc16, "crc16", 3504496746u, 0, 5, &be_const_str_save_before_restart); +be_define_const_str(crc32, "crc32", 3571901412u, 0, 5, &be_const_str_publish_result); be_define_const_str(crc32_ota_seq, "crc32_ota_seq", 172417u, 0, 13, NULL); -be_define_const_str(crc8, "crc8", 1178893587u, 0, 4, &be_const_str_display); -be_define_const_str(create_custom_widget, "create_custom_widget", 1140594778u, 0, 20, NULL); -be_define_const_str(create_matrix, "create_matrix", 3528185923u, 0, 13, NULL); -be_define_const_str(create_segment, "create_segment", 3863522719u, 0, 14, &be_const_str_erase); -be_define_const_str(ct, "ct", 1261010898u, 0, 2, NULL); -be_define_const_str(ctor, "ctor", 375399343u, 0, 4, &be_const_str_day); -be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, &be_const_str_get_option); -be_define_const_str(ctypes_bytes_dyn, "ctypes_bytes_dyn", 915205307u, 0, 16, &be_const_str_get_power); -be_define_const_str(dac_voltage, "dac_voltage", 1552257222u, 0, 11, &be_const_str_hue_ntv); -be_define_const_str(data, "data", 3631407781u, 0, 4, &be_const_str_return_X20code_X3D_X25i); -be_define_const_str(day, "day", 3830391293u, 0, 3, NULL); -be_define_const_str(debug, "debug", 1483009432u, 0, 5, &be_const_str_set_active); -be_define_const_str(decompress, "decompress", 2887031650u, 0, 10, &be_const_str_ismethod); +be_define_const_str(crc8, "crc8", 1178893587u, 0, 4, NULL); +be_define_const_str(create_custom_widget, "create_custom_widget", 1140594778u, 0, 20, &be_const_str_tomap); +be_define_const_str(create_matrix, "create_matrix", 3528185923u, 0, 13, &be_const_str_from_to); +be_define_const_str(create_segment, "create_segment", 3863522719u, 0, 14, NULL); +be_define_const_str(ct, "ct", 1261010898u, 0, 2, &be_const_str_has_factory); +be_define_const_str(ctor, "ctor", 375399343u, 0, 4, &be_const_str_math); +be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, NULL); +be_define_const_str(ctypes_bytes_dyn, "ctypes_bytes_dyn", 915205307u, 0, 16, &be_const_str_energy_struct); +be_define_const_str(dac_voltage, "dac_voltage", 1552257222u, 0, 11, &be_const_str_reverse_gamma10); +be_define_const_str(data, "data", 3631407781u, 0, 4, &be_const_str_display); +be_define_const_str(day, "day", 3830391293u, 0, 3, &be_const_str_duration); +be_define_const_str(debug, "debug", 1483009432u, 0, 5, &be_const_str_string); +be_define_const_str(decompress, "decompress", 2887031650u, 0, 10, &be_const_str_try_run_compiled); be_define_const_str(def, "def", 3310976652u, 55, 3, NULL); -be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_start); -be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_get_vbus_current); -be_define_const_str(delay, "delay", 1322381784u, 0, 5, NULL); -be_define_const_str(delete_all_configs, "delete_all_configs", 2382067578u, 0, 18, &be_const_str_no_X20GPIO_X20specified_X20for_X20neopixelbus); -be_define_const_str(deregister_obj, "deregister_obj", 3909966993u, 0, 14, &be_const_str_dump); -be_define_const_str(destructor_cb, "destructor_cb", 1930283190u, 0, 13, &be_const_str_signal_change); -be_define_const_str(detect, "detect", 8884370u, 0, 6, &be_const_str_running); -be_define_const_str(detected_X20on_X20bus, "detected on bus", 1432002650u, 0, 15, &be_const_str_full_state); -be_define_const_str(devices, "devices", 2701822848u, 0, 7, &be_const_str_digital_read); -be_define_const_str(digital_read, "digital_read", 3585496928u, 0, 12, &be_const_str_finish); -be_define_const_str(digital_write, "digital_write", 3435877979u, 0, 13, &be_const_str_trig); -be_define_const_str(dirty, "dirty", 2667581083u, 0, 5, &be_const_str_ins_goto); -be_define_const_str(discover, "discover", 1383599054u, 0, 8, &be_const_str_every_100ms); -be_define_const_str(display, "display", 1164572437u, 0, 7, &be_const_str_mqtt); -be_define_const_str(display_X2Eini, "display.ini", 2646174001u, 0, 11, &be_const_str_get_object_from_ptr); +be_define_const_str(deg, "deg", 3327754271u, 0, 3, &be_const_str_wake_period); +be_define_const_str(deinit, "deinit", 2345559592u, 0, 6, &be_const_str_tan); +be_define_const_str(delay, "delay", 1322381784u, 0, 5, &be_const_str_scan); +be_define_const_str(delete_all_configs, "delete_all_configs", 2382067578u, 0, 18, &be_const_str_get_bat_voltage); +be_define_const_str(deregister_obj, "deregister_obj", 3909966993u, 0, 14, &be_const_str_event_cb); +be_define_const_str(destructor_cb, "destructor_cb", 1930283190u, 0, 13, &be_const_str_set_active); +be_define_const_str(detect, "detect", 8884370u, 0, 6, &be_const_str_get_active); +be_define_const_str(detected_X20on_X20bus, "detected on bus", 1432002650u, 0, 15, NULL); +be_define_const_str(devices, "devices", 2701822848u, 0, 7, &be_const_str_write_bytes); +be_define_const_str(digital_read, "digital_read", 3585496928u, 0, 12, NULL); +be_define_const_str(digital_write, "digital_write", 3435877979u, 0, 13, &be_const_str_full_status); +be_define_const_str(dirty, "dirty", 2667581083u, 0, 5, NULL); +be_define_const_str(discover, "discover", 1383599054u, 0, 8, &be_const_str_nvskeys); +be_define_const_str(display, "display", 1164572437u, 0, 7, NULL); +be_define_const_str(display_X2Eini, "display.ini", 2646174001u, 0, 11, NULL); be_define_const_str(do, "do", 1646057492u, 65, 2, NULL); -be_define_const_str(dump, "dump", 3663001223u, 0, 4, NULL); -be_define_const_str(duration, "duration", 799079693u, 0, 8, NULL); -be_define_const_str(editable, "editable", 60532369u, 0, 8, &be_const_str_lv_style_prop_arr); -be_define_const_str(efuse_em, "efuse_em", 1643301972u, 0, 8, &be_const_str_tanh); -be_define_const_str(elements_X20must_X20be_X20a_X20lv_point, "elements must be a lv_point", 1415796524u, 0, 27, NULL); -be_define_const_str(elif, "elif", 3232090307u, 51, 4, &be_const_str_setfloat); -be_define_const_str(else, "else", 3183434736u, 52, 4, &be_const_str_set_dc_voltage); -be_define_const_str(enabled, "enabled", 49525662u, 0, 7, &be_const_str_save); -be_define_const_str(end, "end", 1787721130u, 56, 3, &be_const_str_listdir); -be_define_const_str(energy_struct, "energy_struct", 1655792843u, 0, 13, &be_const_str_millis); -be_define_const_str(engine, "engine", 3993360443u, 0, 6, NULL); -be_define_const_str(erase, "erase", 1010949589u, 0, 5, &be_const_str_lv_obj); -be_define_const_str(escape, "escape", 2652972038u, 0, 6, &be_const_str_web_add_button); -be_define_const_str(esphttpd, "esphttpd", 2255925709u, 0, 8, NULL); +be_define_const_str(dump, "dump", 3663001223u, 0, 4, &be_const_str_min); +be_define_const_str(duration, "duration", 799079693u, 0, 8, &be_const_str_otadata); +be_define_const_str(editable, "editable", 60532369u, 0, 8, &be_const_str_resize); +be_define_const_str(efuse_em, "efuse_em", 1643301972u, 0, 8, &be_const_str_reapply); +be_define_const_str(elements_X20must_X20be_X20a_X20lv_point, "elements must be a lv_point", 1415796524u, 0, 27, &be_const_str_null_cb); +be_define_const_str(elif, "elif", 3232090307u, 51, 4, &be_const_str_gamma10); +be_define_const_str(else, "else", 3183434736u, 52, 4, &be_const_str_except); +be_define_const_str(enabled, "enabled", 49525662u, 0, 7, &be_const_str_memory); +be_define_const_str(end, "end", 1787721130u, 56, 3, NULL); +be_define_const_str(energy_struct, "energy_struct", 1655792843u, 0, 13, &be_const_str_get_name); +be_define_const_str(engine, "engine", 3993360443u, 0, 6, &be_const_str_run_cron); +be_define_const_str(erase, "erase", 1010949589u, 0, 5, NULL); +be_define_const_str(escape, "escape", 2652972038u, 0, 6, NULL); +be_define_const_str(esphttpd, "esphttpd", 2255925709u, 0, 8, &be_const_str_exists); be_define_const_str(eth, "eth", 2191266556u, 0, 3, NULL); -be_define_const_str(event, "event", 4264611999u, 0, 5, &be_const_str_fromb64); -be_define_const_str(event_cb, "event_cb", 3128698017u, 0, 8, NULL); -be_define_const_str(every_100ms, "every_100ms", 1546407804u, 0, 11, &be_const_str_widget_event_cb); -be_define_const_str(every_250ms, "every_250ms", 2579240000u, 0, 11, &be_const_str_json_fdump); -be_define_const_str(every_50ms, "every_50ms", 2383884008u, 0, 10, &be_const_str_invalidate_spiffs); -be_define_const_str(every_second, "every_second", 2075451465u, 0, 12, &be_const_str_set_mode_rgb); -be_define_const_str(except, "except", 950914032u, 69, 6, NULL); -be_define_const_str(exec_cmd, "exec_cmd", 493567399u, 0, 8, &be_const_str_persist); -be_define_const_str(exec_rules, "exec_rules", 1445221092u, 0, 10, &be_const_str_fat); -be_define_const_str(exec_tele, "exec_tele", 1020751601u, 0, 9, &be_const_str__X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D); -be_define_const_str(exists, "exists", 1002329533u, 0, 6, &be_const_str_type); -be_define_const_str(exp, "exp", 1923516200u, 0, 3, &be_const_str_lights); -be_define_const_str(f, "f", 3809224601u, 0, 1, &be_const_str_sin); +be_define_const_str(event, "event", 4264611999u, 0, 5, NULL); +be_define_const_str(event_cb, "event_cb", 3128698017u, 0, 8, &be_const_str_log); +be_define_const_str(every_100ms, "every_100ms", 1546407804u, 0, 11, NULL); +be_define_const_str(every_250ms, "every_250ms", 2579240000u, 0, 11, &be_const_str_get_pixel_color); +be_define_const_str(every_50ms, "every_50ms", 2383884008u, 0, 10, &be_const_str_insert); +be_define_const_str(every_second, "every_second", 2075451465u, 0, 12, NULL); +be_define_const_str(except, "except", 950914032u, 69, 6, &be_const_str_id); +be_define_const_str(exec_cmd, "exec_cmd", 493567399u, 0, 8, &be_const_str_set_bat); +be_define_const_str(exec_rules, "exec_rules", 1445221092u, 0, 10, &be_const_str_int64); +be_define_const_str(exec_tele, "exec_tele", 1020751601u, 0, 9, NULL); +be_define_const_str(exists, "exists", 1002329533u, 0, 6, &be_const_str_flush); +be_define_const_str(exp, "exp", 1923516200u, 0, 3, &be_const_str_manuf); +be_define_const_str(f, "f", 3809224601u, 0, 1, NULL); be_define_const_str(factory, "factory", 2510088205u, 0, 7, NULL); -be_define_const_str(false, "false", 184981848u, 62, 5, &be_const_str_length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032); +be_define_const_str(false, "false", 184981848u, 62, 5, &be_const_str_target); be_define_const_str(fast_loop, "fast_loop", 3414422702u, 0, 9, NULL); -be_define_const_str(fast_loop_enabled, "fast_loop_enabled", 2567964376u, 0, 17, &be_const_str_print); -be_define_const_str(fat, "fat", 3203931412u, 0, 3, &be_const_str_lv_); -be_define_const_str(file, "file", 2867484483u, 0, 4, NULL); +be_define_const_str(fast_loop_enabled, "fast_loop_enabled", 2567964376u, 0, 17, &be_const_str_ota_max); +be_define_const_str(fat, "fat", 3203931412u, 0, 3, &be_const_str_import); +be_define_const_str(file, "file", 2867484483u, 0, 4, &be_const_str_lv_timer_cb); be_define_const_str(file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27, "file extension is not '.be' or '.bec'", 3095719639u, 0, 37, NULL); -be_define_const_str(files, "files", 1055342736u, 0, 5, &be_const_str_pc_rel); -be_define_const_str(find, "find", 3186656602u, 0, 4, &be_const_str_mqtt_listener); -be_define_const_str(find_key_i, "find_key_i", 850136726u, 0, 10, NULL); -be_define_const_str(find_op, "find_op", 3766713376u, 0, 7, &be_const_str_try_remove_file); -be_define_const_str(finish, "finish", 1494643858u, 0, 6, &be_const_str_lv_extra); -be_define_const_str(flags, "flags", 2624027180u, 0, 5, &be_const_str_hex); -be_define_const_str(flash, "flash", 2944773417u, 0, 5, &be_const_str_remote_port); -be_define_const_str(floor, "floor", 3102149661u, 0, 5, &be_const_str_setmember); -be_define_const_str(flush, "flush", 3002334877u, 0, 5, &be_const_str_stop); +be_define_const_str(files, "files", 1055342736u, 0, 5, &be_const_str_no_X20GPIO_X20specified_X20for_X20neopixelbus); +be_define_const_str(find, "find", 3186656602u, 0, 4, NULL); +be_define_const_str(find_key_i, "find_key_i", 850136726u, 0, 10, &be_const_str_sys); +be_define_const_str(find_op, "find_op", 3766713376u, 0, 7, &be_const_str_get_switches); +be_define_const_str(finish, "finish", 1494643858u, 0, 6, &be_const_str_uuid4); +be_define_const_str(flags, "flags", 2624027180u, 0, 5, NULL); +be_define_const_str(flash, "flash", 2944773417u, 0, 5, &be_const_str_set_ldo_enable); +be_define_const_str(floor, "floor", 3102149661u, 0, 5, &be_const_str_subtype); +be_define_const_str(flush, "flush", 3002334877u, 0, 5, &be_const_str_json_fdump_map); be_define_const_str(for, "for", 2901640080u, 54, 3, NULL); -be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_widget_struct_by_class); +be_define_const_str(format, "format", 3114108242u, 0, 6, &be_const_str_set_alternate); be_define_const_str(from_to, "from_to", 21625507u, 0, 7, NULL); -be_define_const_str(fromb64, "fromb64", 2717019639u, 0, 7, NULL); +be_define_const_str(fromb64, "fromb64", 2717019639u, 0, 7, &be_const_str_lv); be_define_const_str(frombytes, "frombytes", 3771700788u, 0, 9, NULL); -be_define_const_str(fromhex, "fromhex", 1847150394u, 0, 7, NULL); -be_define_const_str(fromptr, "fromptr", 666189689u, 0, 7, &be_const_str_input); -be_define_const_str(fromstring, "fromstring", 610302344u, 0, 10, &be_const_str_mqtt_data); -be_define_const_str(full_state, "full_state", 255687770u, 0, 10, &be_const_str_ins_ramp); +be_define_const_str(fromhex, "fromhex", 1847150394u, 0, 7, &be_const_str_geti); +be_define_const_str(fromptr, "fromptr", 666189689u, 0, 7, &be_const_str_get_input_power_status); +be_define_const_str(fromstring, "fromstring", 610302344u, 0, 10, &be_const_str_lv_event_cb); +be_define_const_str(full_state, "full_state", 255687770u, 0, 10, NULL); be_define_const_str(full_status, "full_status", 648242459u, 0, 11, NULL); -be_define_const_str(fulltopic, "fulltopic", 3636750803u, 0, 9, &be_const_str_load_templates); -be_define_const_str(function, "function", 2664841801u, 0, 8, NULL); -be_define_const_str(gamma, "gamma", 3492353034u, 0, 5, &be_const_str_size); -be_define_const_str(gamma10, "gamma10", 3472052483u, 0, 7, &be_const_str_widget_struct_default); -be_define_const_str(gamma8, "gamma8", 3802843830u, 0, 6, NULL); -be_define_const_str(gc, "gc", 1042313471u, 0, 2, &be_const_str_gen_cb); -be_define_const_str(gen_cb, "gen_cb", 3245227551u, 0, 6, &be_const_str_lv_coord_arr); -be_define_const_str(get, "get", 1410115415u, 0, 3, &be_const_str_module); -be_define_const_str(get_MAC, "get_MAC", 2091521771u, 0, 7, &be_const_str_is_spiffs); -be_define_const_str(get_active, "get_active", 3504842642u, 0, 10, &be_const_str_widget_width_def); +be_define_const_str(fulltopic, "fulltopic", 3636750803u, 0, 9, NULL); +be_define_const_str(function, "function", 2664841801u, 0, 8, &be_const_str_load); +be_define_const_str(gamma, "gamma", 3492353034u, 0, 5, &be_const_str_invalidate_spiffs); +be_define_const_str(gamma10, "gamma10", 3472052483u, 0, 7, NULL); +be_define_const_str(gamma8, "gamma8", 3802843830u, 0, 6, &be_const_str_lv_obj_class); +be_define_const_str(gc, "gc", 1042313471u, 0, 2, &be_const_str_tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29); +be_define_const_str(gen_cb, "gen_cb", 3245227551u, 0, 6, NULL); +be_define_const_str(get, "get", 1410115415u, 0, 3, NULL); +be_define_const_str(get_MAC, "get_MAC", 2091521771u, 0, 7, NULL); +be_define_const_str(get_active, "get_active", 3504842642u, 0, 10, &be_const_str_register_obj); be_define_const_str(get_alternate, "get_alternate", 1450148894u, 0, 13, NULL); -be_define_const_str(get_aps_voltage, "get_aps_voltage", 2293036435u, 0, 15, &be_const_str_min); -be_define_const_str(get_bat_charge_current, "get_bat_charge_current", 1385293050u, 0, 22, &be_const_str_introspect); -be_define_const_str(get_bat_current, "get_bat_current", 1912106073u, 0, 15, &be_const_str_label); +be_define_const_str(get_aps_voltage, "get_aps_voltage", 2293036435u, 0, 15, &be_const_str_list_handlers); +be_define_const_str(get_bat_charge_current, "get_bat_charge_current", 1385293050u, 0, 22, &be_const_str_gpio); +be_define_const_str(get_bat_current, "get_bat_current", 1912106073u, 0, 15, &be_const_str_serial); be_define_const_str(get_bat_power, "get_bat_power", 3067374853u, 0, 13, NULL); -be_define_const_str(get_bat_voltage, "get_bat_voltage", 706676538u, 0, 15, &be_const_str_wd); -be_define_const_str(get_battery_chargin_status, "get_battery_chargin_status", 2233241571u, 0, 26, &be_const_str_h); -be_define_const_str(get_bri, "get_bri", 2041809895u, 0, 7, &be_const_str__X7Bs_X7DVBus_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); -be_define_const_str(get_cb_list, "get_cb_list", 1605319182u, 0, 11, &be_const_str_power_off); -be_define_const_str(get_current_module_name, "get_current_module_name", 2379270740u, 0, 23, &be_const_str_read12); -be_define_const_str(get_current_module_path, "get_current_module_path", 3206673408u, 0, 23, &be_const_str_invalid_X20GPIO_X20number); -be_define_const_str(get_event_cb, "get_event_cb", 375876088u, 0, 12, NULL); +be_define_const_str(get_bat_voltage, "get_bat_voltage", 706676538u, 0, 15, &be_const_str_get_log); +be_define_const_str(get_battery_chargin_status, "get_battery_chargin_status", 2233241571u, 0, 26, &be_const_str_raise); +be_define_const_str(get_bri, "get_bri", 2041809895u, 0, 7, &be_const_str__X7Bs_X7DBatt_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D); +be_define_const_str(get_cb_list, "get_cb_list", 1605319182u, 0, 11, NULL); +be_define_const_str(get_current_module_name, "get_current_module_name", 2379270740u, 0, 23, &be_const_str_get_switch); +be_define_const_str(get_current_module_path, "get_current_module_path", 3206673408u, 0, 23, &be_const_str_set_dc_voltage); +be_define_const_str(get_event_cb, "get_event_cb", 375876088u, 0, 12, &be_const_str_remove_driver); be_define_const_str(get_factory_slot, "get_factory_slot", 3086140407u, 0, 16, NULL); -be_define_const_str(get_free_heap, "get_free_heap", 625069757u, 0, 13, NULL); -be_define_const_str(get_image_size, "get_image_size", 4009859887u, 0, 14, &be_const_str_out_X20of_X20range); -be_define_const_str(get_input_power_status, "get_input_power_status", 4102829177u, 0, 22, &be_const_str_gpio_init); -be_define_const_str(get_light, "get_light", 381930476u, 0, 9, &be_const_str_setitem); -be_define_const_str(get_log, "get_log", 3524441898u, 0, 7, &be_const_str_get_switches); -be_define_const_str(get_mem, "get_mem", 1482912361u, 0, 7, &be_const_str_read_sensors); -be_define_const_str(get_name, "get_name", 1616902907u, 0, 8, NULL); -be_define_const_str(get_object_from_ptr, "get_object_from_ptr", 2345019201u, 0, 19, &be_const_str_tele); +be_define_const_str(get_free_heap, "get_free_heap", 625069757u, 0, 13, &be_const_str_push); +be_define_const_str(get_image_size, "get_image_size", 4009859887u, 0, 14, &be_const_str_send); +be_define_const_str(get_input_power_status, "get_input_power_status", 4102829177u, 0, 22, NULL); +be_define_const_str(get_light, "get_light", 381930476u, 0, 9, &be_const_str_write); +be_define_const_str(get_log, "get_log", 3524441898u, 0, 7, NULL); +be_define_const_str(get_mem, "get_mem", 1482912361u, 0, 7, NULL); +be_define_const_str(get_name, "get_name", 1616902907u, 0, 8, &be_const_str_mqtt); +be_define_const_str(get_object_from_ptr, "get_object_from_ptr", 2345019201u, 0, 19, &be_const_str_get_power); be_define_const_str(get_option, "get_option", 2123730033u, 0, 10, NULL); -be_define_const_str(get_ota_slot, "get_ota_slot", 2686180151u, 0, 12, &be_const_str_read24); -be_define_const_str(get_pixel_color, "get_pixel_color", 337490048u, 0, 15, &be_const_str_offset); -be_define_const_str(get_power, "get_power", 3009799377u, 0, 9, NULL); -be_define_const_str(get_size, "get_size", 2803644713u, 0, 8, &be_const_str_ptr); -be_define_const_str(get_string, "get_string", 4195847969u, 0, 10, &be_const_str_remove); -be_define_const_str(get_switch, "get_switch", 164821028u, 0, 10, NULL); +be_define_const_str(get_ota_slot, "get_ota_slot", 2686180151u, 0, 12, NULL); +be_define_const_str(get_pixel_color, "get_pixel_color", 337490048u, 0, 15, &be_const_str_is_factory); +be_define_const_str(get_power, "get_power", 3009799377u, 0, 9, &be_const_str_read_sensors); +be_define_const_str(get_size, "get_size", 2803644713u, 0, 8, &be_const_str_gpio_init); +be_define_const_str(get_string, "get_string", 4195847969u, 0, 10, &be_const_str_tohex); +be_define_const_str(get_switch, "get_switch", 164821028u, 0, 10, &be_const_str_set_pwm); be_define_const_str(get_switches, "get_switches", 4116216928u, 0, 12, NULL); -be_define_const_str(get_temp, "get_temp", 3370919486u, 0, 8, &be_const_str_tomap); -be_define_const_str(get_vbus_current, "get_vbus_current", 1205347942u, 0, 16, &be_const_str_tasmota); -be_define_const_str(get_vbus_voltage, "get_vbus_voltage", 2398210401u, 0, 16, &be_const_str_real); -be_define_const_str(get_warning_level, "get_warning_level", 1737834441u, 0, 17, &be_const_str_redirect); -be_define_const_str(getbits, "getbits", 3094168979u, 0, 7, &be_const_str_the_X20second_X20argument_X20is_X20not_X20a_X20function); +be_define_const_str(get_temp, "get_temp", 3370919486u, 0, 8, &be_const_str_widget_constructor); +be_define_const_str(get_vbus_current, "get_vbus_current", 1205347942u, 0, 16, NULL); +be_define_const_str(get_vbus_voltage, "get_vbus_voltage", 2398210401u, 0, 16, &be_const_str_return_X20code_X3D_X25i); +be_define_const_str(get_warning_level, "get_warning_level", 1737834441u, 0, 17, &be_const_str_json_fdump_any); +be_define_const_str(getbits, "getbits", 3094168979u, 0, 7, NULL); be_define_const_str(getfloat, "getfloat", 2820979603u, 0, 8, NULL); -be_define_const_str(geti, "geti", 2381006490u, 0, 4, &be_const_str_ismapped); -be_define_const_str(global, "global", 503252654u, 0, 6, &be_const_str_matrix); -be_define_const_str(gpio, "gpio", 2638155258u, 0, 4, &be_const_str_lv_point_arr); +be_define_const_str(geti, "geti", 2381006490u, 0, 4, &be_const_str_rotate); +be_define_const_str(global, "global", 503252654u, 0, 6, &be_const_str_lv_solidified); +be_define_const_str(gpio, "gpio", 2638155258u, 0, 4, NULL); be_define_const_str(gpio_init, "gpio_init", 4229187257u, 0, 9, NULL); -be_define_const_str(group_def, "group_def", 1524213328u, 0, 9, &be_const_str_instance_size); -be_define_const_str(groups, "groups", 2943077229u, 0, 6, NULL); -be_define_const_str(h, "h", 3977000791u, 0, 1, NULL); -be_define_const_str(has, "has", 3988721635u, 0, 3, &be_const_str_light); -be_define_const_str(has_arg, "has_arg", 424878688u, 0, 7, &be_const_str_instance); +be_define_const_str(group_def, "group_def", 1524213328u, 0, 9, &be_const_str_load_otadata); +be_define_const_str(groups, "groups", 2943077229u, 0, 6, &be_const_str_type_to_string); +be_define_const_str(h, "h", 3977000791u, 0, 1, &be_const_str_setbits); +be_define_const_str(has, "has", 3988721635u, 0, 3, &be_const_str_replace); +be_define_const_str(has_arg, "has_arg", 424878688u, 0, 7, &be_const_str_ins_ramp); be_define_const_str(has_factory, "has_factory", 2702829042u, 0, 11, NULL); -be_define_const_str(height_def, "height_def", 2348238838u, 0, 10, NULL); +be_define_const_str(hasclient, "hasclient", 3357240820u, 0, 9, &be_const_str_set_pixel_color); +be_define_const_str(height_def, "height_def", 2348238838u, 0, 10, &be_const_str_if); be_define_const_str(hex, "hex", 4273249610u, 0, 3, NULL); -be_define_const_str(hour, "hour", 3053661199u, 0, 4, &be_const_str_set_hue16sat); +be_define_const_str(hour, "hour", 3053661199u, 0, 4, &be_const_str_value_error); be_define_const_str(hs2rgb, "hs2rgb", 1040816349u, 0, 6, NULL); -be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s/%s.autoconf", 2743526309u, 0, 70, &be_const_str_issubclass); -be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s_manifest.json", 3657552045u, 0, 72, &be_const_str_resolvecmnd); -be_define_const_str(hue, "hue", 3817694041u, 0, 3, &be_const_str_r); -be_define_const_str(hue_ntv, "hue_ntv", 705068642u, 0, 7, &be_const_str_set_matrix_pixel_color); -be_define_const_str(hue_status, "hue_status", 437978812u, 0, 10, &be_const_str_obj_class_create_obj); -be_define_const_str(i2c_enabled, "i2c_enabled", 218388101u, 0, 11, NULL); -be_define_const_str(id, "id", 926444256u, 0, 2, &be_const_str_readbytes); -be_define_const_str(id_X20must_X20be_X20of_X20type_X20_X27int_X27, "id must be of type 'int'", 2097653458u, 0, 24, &be_const_str_webserver); +be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s/%s.autoconf", 2743526309u, 0, 70, &be_const_str_time_reached); +be_define_const_str(https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, "https://raw.githubusercontent.com/tasmota/autoconf/main/%s_manifest.json", 3657552045u, 0, 72, NULL); +be_define_const_str(hue, "hue", 3817694041u, 0, 3, NULL); +be_define_const_str(hue_ntv, "hue_ntv", 705068642u, 0, 7, &be_const_str_run_bat); +be_define_const_str(hue_status, "hue_status", 437978812u, 0, 10, &be_const_str_set_useragent); +be_define_const_str(i2c_enabled, "i2c_enabled", 218388101u, 0, 11, &be_const_str_widget_dtor_cb); +be_define_const_str(id, "id", 926444256u, 0, 2, &be_const_str_log10); +be_define_const_str(id_X20must_X20be_X20of_X20type_X20_X27int_X27, "id must be of type 'int'", 2097653458u, 0, 24, NULL); be_define_const_str(if, "if", 959999494u, 50, 2, NULL); -be_define_const_str(imax, "imax", 3084515410u, 0, 4, NULL); -be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_yield); -be_define_const_str(import, "import", 288002260u, 66, 6, NULL); -be_define_const_str(init, "init", 380752755u, 0, 4, NULL); +be_define_const_str(imax, "imax", 3084515410u, 0, 4, &be_const_str_remove_cron); +be_define_const_str(imin, "imin", 2714127864u, 0, 4, &be_const_str_obj_class_create_obj); +be_define_const_str(import, "import", 288002260u, 66, 6, &be_const_str_mqtt_data); +be_define_const_str(init, "init", 380752755u, 0, 4, &be_const_str_rule); be_define_const_str(input, "input", 4191711099u, 0, 5, NULL); be_define_const_str(ins_goto, "ins_goto", 1342843963u, 0, 8, NULL); be_define_const_str(ins_ramp, "ins_ramp", 1068049360u, 0, 8, NULL); -be_define_const_str(ins_time, "ins_time", 2980245553u, 0, 8, &be_const_str_json_fdump_map); -be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_is_factory); -be_define_const_str(instance, "instance", 193386898u, 0, 8, &be_const_str_set_exten); -be_define_const_str(instance_X20required, "instance required", 381192159u, 0, 17, &be_const_str_set_mem); -be_define_const_str(instance_size, "instance_size", 4280269518u, 0, 13, &be_const_str_top); -be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_range); -be_define_const_str(int64, "int64", 64103268u, 0, 5, &be_const_str_path); -be_define_const_str(internal_error, "internal_error", 2519158169u, 0, 14, &be_const_str_write_file); -be_define_const_str(introspect, "introspect", 164638290u, 0, 10, &be_const_str_widget_dtor_impl); -be_define_const_str(invalid_X20GPIO_X20number, "invalid GPIO number", 4135793328u, 0, 19, NULL); -be_define_const_str(invalid_X20magic_X20number_X20_X2502X, "invalid magic number %02X", 2836756259u, 0, 25, &be_const_str_resize); -be_define_const_str(invalidate_spiffs, "invalidate_spiffs", 1470453498u, 0, 17, NULL); -be_define_const_str(io_error, "io_error", 1970281036u, 0, 8, &be_const_str_ota_); -be_define_const_str(is_dirty, "is_dirty", 418034110u, 0, 8, &be_const_str_send_multicast); -be_define_const_str(is_factory, "is_factory", 1312753376u, 0, 10, &be_const_str_web_add_management_button); +be_define_const_str(ins_time, "ins_time", 2980245553u, 0, 8, NULL); +be_define_const_str(insert, "insert", 3332609576u, 0, 6, &be_const_str_length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032); +be_define_const_str(instance, "instance", 193386898u, 0, 8, &be_const_str_set_dcdc_enable); +be_define_const_str(instance_X20required, "instance required", 381192159u, 0, 17, NULL); +be_define_const_str(instance_size, "instance_size", 4280269518u, 0, 13, NULL); +be_define_const_str(int, "int", 2515107422u, 0, 3, &be_const_str_set_first_time); +be_define_const_str(int64, "int64", 64103268u, 0, 5, NULL); +be_define_const_str(internal_error, "internal_error", 2519158169u, 0, 14, &be_const_str_lv_coord_arr); +be_define_const_str(introspect, "introspect", 164638290u, 0, 10, NULL); +be_define_const_str(invalid_X20GPIO_X20number, "invalid GPIO number", 4135793328u, 0, 19, &be_const_str_pop); +be_define_const_str(invalid_X20magic_X20number_X20_X2502X, "invalid magic number %02X", 2836756259u, 0, 25, NULL); +be_define_const_str(invalidate_spiffs, "invalidate_spiffs", 1470453498u, 0, 17, &be_const_str_model); +be_define_const_str(io_error, "io_error", 1970281036u, 0, 8, &be_const_str_mqtt_listener); +be_define_const_str(is_dirty, "is_dirty", 418034110u, 0, 8, NULL); +be_define_const_str(is_factory, "is_factory", 1312753376u, 0, 10, NULL); be_define_const_str(is_first_time, "is_first_time", 275242384u, 0, 13, NULL); -be_define_const_str(is_ota, "is_ota", 2892315548u, 0, 6, &be_const_str_string); -be_define_const_str(is_running, "is_running", 2226847261u, 0, 10, &be_const_str_strptime); -be_define_const_str(is_spiffs, "is_spiffs", 3196570601u, 0, 9, &be_const_str_split); +be_define_const_str(is_ota, "is_ota", 2892315548u, 0, 6, &be_const_str_time_str); +be_define_const_str(is_running, "is_running", 2226847261u, 0, 10, &be_const_str_sin); +be_define_const_str(is_spiffs, "is_spiffs", 3196570601u, 0, 9, &be_const_str_tasmota_log_reader); be_define_const_str(isinstance, "isinstance", 3669352738u, 0, 10, NULL); -be_define_const_str(ismapped, "ismapped", 2725004770u, 0, 8, &be_const_str_log10); -be_define_const_str(ismethod, "ismethod", 3513438880u, 0, 8, &be_const_str_rand); -be_define_const_str(isnan, "isnan", 2981347434u, 0, 5, NULL); -be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_read); -be_define_const_str(item, "item", 2671260646u, 0, 4, NULL); -be_define_const_str(iter, "iter", 3124256359u, 0, 4, NULL); -be_define_const_str(json, "json", 916562499u, 0, 4, &be_const_str_sqrt); +be_define_const_str(ismapped, "ismapped", 2725004770u, 0, 8, NULL); +be_define_const_str(ismethod, "ismethod", 3513438880u, 0, 8, &be_const_str_rtc); +be_define_const_str(isnan, "isnan", 2981347434u, 0, 5, &be_const_str_offset); +be_define_const_str(issubclass, "issubclass", 4078395519u, 0, 10, &be_const_str_light_to_id); +be_define_const_str(item, "item", 2671260646u, 0, 4, &be_const_str_widget_editable); +be_define_const_str(iter, "iter", 3124256359u, 0, 4, &be_const_str_readline); +be_define_const_str(json, "json", 916562499u, 0, 4, &be_const_str_response_append); be_define_const_str(json_append, "json_append", 3002019284u, 0, 11, NULL); be_define_const_str(json_fdump, "json_fdump", 1694216580u, 0, 10, NULL); be_define_const_str(json_fdump_any, "json_fdump_any", 3348629385u, 0, 14, NULL); -be_define_const_str(json_fdump_list, "json_fdump_list", 3903879853u, 0, 15, &be_const_str_tcpclient); -be_define_const_str(json_fdump_map, "json_fdump_map", 4091954653u, 0, 14, &be_const_str_remote_ip); +be_define_const_str(json_fdump_list, "json_fdump_list", 3903879853u, 0, 15, &be_const_str_label); +be_define_const_str(json_fdump_map, "json_fdump_map", 4091954653u, 0, 14, &be_const_str_lv_obj); be_define_const_str(keys, "keys", 4182378701u, 0, 4, NULL); -be_define_const_str(label, "label", 4137097213u, 0, 5, &be_const_str_set_timer); -be_define_const_str(last_modified, "last_modified", 772177145u, 0, 13, &be_const_str_list); -be_define_const_str(leds, "leds", 558858555u, 0, 4, &be_const_str_light_state); -be_define_const_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032, "length in bits must be between 0 and 32", 2584509128u, 0, 39, &be_const_str_lv_event); -be_define_const_str(light, "light", 3801947695u, 0, 5, NULL); +be_define_const_str(label, "label", 4137097213u, 0, 5, NULL); +be_define_const_str(last_modified, "last_modified", 772177145u, 0, 13, &be_const_str_remove_trailing_zeroes); +be_define_const_str(leds, "leds", 558858555u, 0, 4, NULL); +be_define_const_str(length_X20in_X20bits_X20must_X20be_X20between_X200_X20and_X2032, "length in bits must be between 0 and 32", 2584509128u, 0, 39, &be_const_str_tob64); +be_define_const_str(light, "light", 3801947695u, 0, 5, &be_const_str_remote_port); be_define_const_str(light_X20must_X20be_X20of_X20class_X20_X27light_state_X27, "light must be of class 'light_state'", 3669350396u, 0, 36, NULL); -be_define_const_str(light_state, "light_state", 905783845u, 0, 11, &be_const_str_math); -be_define_const_str(light_to_id, "light_to_id", 1117015647u, 0, 11, NULL); -be_define_const_str(lights, "lights", 425118420u, 0, 6, &be_const_str_pin_mode); -be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_resp_cmnd_done); -be_define_const_str(list_handlers, "list_handlers", 593774371u, 0, 13, NULL); -be_define_const_str(listdir, "listdir", 2005220720u, 0, 7, &be_const_str_nan); -be_define_const_str(load, "load", 3859241449u, 0, 4, NULL); -be_define_const_str(load_otadata, "load_otadata", 1955073712u, 0, 12, &be_const_str_reset); -be_define_const_str(load_templates, "load_templates", 3513870133u, 0, 14, NULL); +be_define_const_str(light_state, "light_state", 905783845u, 0, 11, NULL); +be_define_const_str(light_to_id, "light_to_id", 1117015647u, 0, 11, &be_const_str_pc_abs); +be_define_const_str(lights, "lights", 425118420u, 0, 6, &be_const_str_pin_used); +be_define_const_str(list, "list", 217798785u, 0, 4, &be_const_str_tolower); +be_define_const_str(list_handlers, "list_handlers", 593774371u, 0, 13, &be_const_str_set_ct); +be_define_const_str(listdir, "listdir", 2005220720u, 0, 7, NULL); +be_define_const_str(load, "load", 3859241449u, 0, 4, &be_const_str_lvgl_timer_dispatch); +be_define_const_str(load_otadata, "load_otadata", 1955073712u, 0, 12, NULL); +be_define_const_str(load_templates, "load_templates", 3513870133u, 0, 14, &be_const_str_tostring); be_define_const_str(log, "log", 1062293841u, 0, 3, NULL); -be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_memory); -be_define_const_str(lower, "lower", 3038577850u, 0, 5, &be_const_str_manuf); -be_define_const_str(lv, "lv", 1529997255u, 0, 2, &be_const_str_make_cb); -be_define_const_str(lv_, "lv_", 663721032u, 0, 3, NULL); +be_define_const_str(log10, "log10", 2346846000u, 0, 5, &be_const_str_members); +be_define_const_str(lower, "lower", 3038577850u, 0, 5, NULL); +be_define_const_str(lv, "lv", 1529997255u, 0, 2, &be_const_str_now); +be_define_const_str(lv_, "lv_", 663721032u, 0, 3, &be_const_str_power_off); be_define_const_str(lv_coord_arr, "lv_coord_arr", 1197238601u, 0, 12, NULL); -be_define_const_str(lv_event, "lv_event", 2434089968u, 0, 8, &be_const_str_rad); -be_define_const_str(lv_event_cb, "lv_event_cb", 2480731016u, 0, 11, &be_const_str_set_hum); -be_define_const_str(lv_extra, "lv_extra", 399561998u, 0, 8, &be_const_str_sec); +be_define_const_str(lv_event, "lv_event", 2434089968u, 0, 8, NULL); +be_define_const_str(lv_event_cb, "lv_event_cb", 2480731016u, 0, 11, &be_const_str_run_deferred); +be_define_const_str(lv_extra, "lv_extra", 399561998u, 0, 8, NULL); be_define_const_str(lv_module_init, "lv_module_init", 1133027755u, 0, 14, NULL); be_define_const_str(lv_obj, "lv_obj", 4257833149u, 0, 6, NULL); -be_define_const_str(lv_obj_class, "lv_obj_class", 4039656294u, 0, 12, NULL); -be_define_const_str(lv_point, "lv_point", 4120221790u, 0, 8, NULL); +be_define_const_str(lv_obj_class, "lv_obj_class", 4039656294u, 0, 12, &be_const_str_slots); +be_define_const_str(lv_point, "lv_point", 4120221790u, 0, 8, &be_const_str_while); be_define_const_str(lv_point_arr, "lv_point_arr", 3959768858u, 0, 12, NULL); -be_define_const_str(lv_solidified, "lv_solidified", 2274121310u, 0, 13, &be_const_str_pop); -be_define_const_str(lv_style_prop_arr, "lv_style_prop_arr", 2504347499u, 0, 17, &be_const_str_toint); -be_define_const_str(lv_timer_cb, "lv_timer_cb", 1383473763u, 0, 11, NULL); -be_define_const_str(lvgl_event_dispatch, "lvgl_event_dispatch", 2104396622u, 0, 19, &be_const_str_no_X20more_X20RMT_X20channel_X20available); -be_define_const_str(lvgl_timer_dispatch, "lvgl_timer_dispatch", 975257833u, 0, 19, &be_const_str_srand); -be_define_const_str(make_cb, "make_cb", 71252785u, 0, 7, NULL); -be_define_const_str(manuf, "manuf", 4120929560u, 0, 5, &be_const_str_set_temp); -be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_send); -be_define_const_str(math, "math", 4001929615u, 0, 4, &be_const_str_widget_destructor); -be_define_const_str(matrix, "matrix", 365099244u, 0, 6, NULL); +be_define_const_str(lv_solidified, "lv_solidified", 2274121310u, 0, 13, NULL); +be_define_const_str(lv_style_prop_arr, "lv_style_prop_arr", 2504347499u, 0, 17, &be_const_str_widget_instance_size); +be_define_const_str(lv_timer_cb, "lv_timer_cb", 1383473763u, 0, 11, &be_const_str_persist); +be_define_const_str(lvgl_event_dispatch, "lvgl_event_dispatch", 2104396622u, 0, 19, NULL); +be_define_const_str(lvgl_timer_dispatch, "lvgl_timer_dispatch", 975257833u, 0, 19, NULL); +be_define_const_str(make_cb, "make_cb", 71252785u, 0, 7, &be_const_str_setmember); +be_define_const_str(manuf, "manuf", 4120929560u, 0, 5, &be_const_str_next); +be_define_const_str(map, "map", 3751997361u, 0, 3, &be_const_str_redirect); +be_define_const_str(math, "math", 4001929615u, 0, 4, NULL); +be_define_const_str(matrix, "matrix", 365099244u, 0, 6, &be_const_str_nil); be_define_const_str(maxota, "maxota", 2594898265u, 0, 6, NULL); -be_define_const_str(md5, "md5", 2393554675u, 0, 3, NULL); -be_define_const_str(member, "member", 719708611u, 0, 6, &be_const_str_value_error); +be_define_const_str(md5, "md5", 2393554675u, 0, 3, &be_const_str_zero); +be_define_const_str(member, "member", 719708611u, 0, 6, NULL); be_define_const_str(members, "members", 937576464u, 0, 7, NULL); -be_define_const_str(memory, "memory", 2229924270u, 0, 6, &be_const_str_set_ldo_voltage); -be_define_const_str(millis, "millis", 1214679063u, 0, 6, &be_const_str_publish_rule); -be_define_const_str(min, "min", 3381609815u, 0, 3, NULL); -be_define_const_str(missing_X20name, "missing name", 3635024006u, 0, 12, &be_const_str_switch_factory); -be_define_const_str(model, "model", 2961925722u, 0, 5, NULL); -be_define_const_str(module, "module", 3617558685u, 0, 6, NULL); -be_define_const_str(month, "month", 3598321157u, 0, 5, &be_const_str_remove_trailing_zeroes); -be_define_const_str(mqtt, "mqtt", 353147387u, 0, 4, &be_const_str_sinh); -be_define_const_str(mqtt_data, "mqtt_data", 2538213364u, 0, 9, &be_const_str_scale_uint); -be_define_const_str(mqtt_listener, "mqtt_listener", 2764719532u, 0, 13, &be_const_str_set_alternate); -be_define_const_str(name, "name", 2369371622u, 0, 4, &be_const_str_parse); +be_define_const_str(memory, "memory", 2229924270u, 0, 6, NULL); +be_define_const_str(millis, "millis", 1214679063u, 0, 6, &be_const_str_width_def); +be_define_const_str(min, "min", 3381609815u, 0, 3, &be_const_str_set_ota_max); +be_define_const_str(missing_X20name, "missing name", 3635024006u, 0, 12, NULL); +be_define_const_str(model, "model", 2961925722u, 0, 5, &be_const_str_read12); +be_define_const_str(module, "module", 3617558685u, 0, 6, &be_const_str_push_path); +be_define_const_str(month, "month", 3598321157u, 0, 5, &be_const_str_partition_core); +be_define_const_str(mqtt, "mqtt", 353147387u, 0, 4, NULL); +be_define_const_str(mqtt_data, "mqtt_data", 2538213364u, 0, 9, &be_const_str_readbytes); +be_define_const_str(mqtt_listener, "mqtt_listener", 2764719532u, 0, 13, NULL); +be_define_const_str(name, "name", 2369371622u, 0, 4, NULL); be_define_const_str(nan, "nan", 797905850u, 0, 3, NULL); -be_define_const_str(next, "next", 1555467752u, 0, 4, &be_const_str_time_str); -be_define_const_str(next_cron, "next_cron", 3260705337u, 0, 9, &be_const_str_static); -be_define_const_str(nil, "nil", 228849900u, 63, 3, &be_const_str_preinit); +be_define_const_str(next, "next", 1555467752u, 0, 4, &be_const_str_no_X20more_X20RMT_X20channel_X20available); +be_define_const_str(next_cron, "next_cron", 3260705337u, 0, 9, &be_const_str_open); +be_define_const_str(nil, "nil", 228849900u, 63, 3, &be_const_str_persist_X2E_p_X20is_X20not_X20a_X20map); be_define_const_str(no_X20GPIO_X20specified_X20for_X20neopixelbus, "no GPIO specified for neopixelbus", 42078528u, 0, 33, NULL); -be_define_const_str(no_X20more_X20RMT_X20channel_X20available, "no more RMT channel available", 305838632u, 0, 29, &be_const_str_run); -be_define_const_str(now, "now", 682728183u, 0, 3, NULL); -be_define_const_str(null_cb, "null_cb", 2333536460u, 0, 7, &be_const_str_resp_cmnd_str); -be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_remove_driver); -be_define_const_str(nvs, "nvs", 477704066u, 0, 3, &be_const_str__X7Bs_X7DBatt_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); -be_define_const_str(nvskeys, "nvskeys", 1760042990u, 0, 7, &be_const_str_ota_max); -be_define_const_str(o, "o", 3926667934u, 0, 1, &be_const_str_write); -be_define_const_str(obj_class_create_obj, "obj_class_create_obj", 3304390632u, 0, 20, NULL); -be_define_const_str(offset, "offset", 348705738u, 0, 6, NULL); -be_define_const_str(offseta, "offseta", 1663383089u, 0, 7, &be_const_str_replace); -be_define_const_str(on, "on", 1630810064u, 0, 2, &be_const_str_set_useragent); -be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will cause a restart.\");'>", 232646018u, 0, 57, NULL); -be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will change the current configuration and cause a restart.\");'>", 3792412559u, 0, 94, NULL); -be_define_const_str(open, "open", 3546203337u, 0, 4, &be_const_str_set_ota_max); -be_define_const_str(ota_, "ota_", 773595766u, 0, 4, &be_const_str_phy); -be_define_const_str(ota_max, "ota_max", 2940511240u, 0, 7, NULL); -be_define_const_str(otadata, "otadata", 1962391757u, 0, 7, NULL); +be_define_const_str(no_X20more_X20RMT_X20channel_X20available, "no more RMT channel available", 305838632u, 0, 29, &be_const_str_sleep); +be_define_const_str(now, "now", 682728183u, 0, 3, &be_const_str_tobytes); +be_define_const_str(null_cb, "null_cb", 2333536460u, 0, 7, NULL); +be_define_const_str(number, "number", 467038368u, 0, 6, &be_const_str_tanh); +be_define_const_str(nvs, "nvs", 477704066u, 0, 3, &be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E); +be_define_const_str(nvskeys, "nvskeys", 1760042990u, 0, 7, &be_const_str_pin_mode); +be_define_const_str(o, "o", 3926667934u, 0, 1, NULL); +be_define_const_str(obj_class_create_obj, "obj_class_create_obj", 3304390632u, 0, 20, &be_const_str_sum); +be_define_const_str(offset, "offset", 348705738u, 0, 6, &be_const_str_resp_cmnd_failed); +be_define_const_str(offseta, "offseta", 1663383089u, 0, 7, &be_const_str_widget_group_def); +be_define_const_str(on, "on", 1630810064u, 0, 2, NULL); +be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will cause a restart.\");'>", 232646018u, 0, 57, &be_const_str_range); +be_define_const_str(onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, "onsubmit='return confirm(\"This will change the current configuration and cause a restart.\");'>", 3792412559u, 0, 94, &be_const_str_set_hue16sat); +be_define_const_str(open, "open", 3546203337u, 0, 4, NULL); +be_define_const_str(ota_, "ota_", 773595766u, 0, 4, NULL); +be_define_const_str(ota_max, "ota_max", 2940511240u, 0, 7, &be_const_str_wire_scan); +be_define_const_str(otadata, "otadata", 1962391757u, 0, 7, &be_const_str_try_rule); be_define_const_str(out_X20of_X20range, "out of range", 2236631477u, 0, 12, NULL); -be_define_const_str(page_autoconf_ctl, "page_autoconf_ctl", 2453381496u, 0, 17, &be_const_str_seq0); -be_define_const_str(page_autoconf_mgr, "page_autoconf_mgr", 3643937031u, 0, 17, &be_const_str_write_flash); -be_define_const_str(parse, "parse", 1111180012u, 0, 5, &be_const_str_web_sensor); -be_define_const_str(partition_core, "partition_core", 2913046901u, 0, 14, &be_const_str_set_bat); -be_define_const_str(path, "path", 2223459638u, 0, 4, NULL); -be_define_const_str(pc, "pc", 1313756516u, 0, 2, &be_const_str_run_deferred); -be_define_const_str(pc_abs, "pc_abs", 920256495u, 0, 6, &be_const_str_wake_period); +be_define_const_str(page_autoconf_ctl, "page_autoconf_ctl", 2453381496u, 0, 17, &be_const_str_pc_rel); +be_define_const_str(page_autoconf_mgr, "page_autoconf_mgr", 3643937031u, 0, 17, &be_const_str_sat); +be_define_const_str(parse, "parse", 1111180012u, 0, 5, NULL); +be_define_const_str(partition_core, "partition_core", 2913046901u, 0, 14, &be_const_str_set_mem); +be_define_const_str(path, "path", 2223459638u, 0, 4, &be_const_str_scale_uint); +be_define_const_str(pc, "pc", 1313756516u, 0, 2, &be_const_str__X7Bs_X7DBatt_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D); +be_define_const_str(pc_abs, "pc_abs", 920256495u, 0, 6, NULL); be_define_const_str(pc_rel, "pc_rel", 991921176u, 0, 6, NULL); -be_define_const_str(persist, "persist", 3917083779u, 0, 7, &be_const_str_wire1); -be_define_const_str(persist_X2E_p_X20is_X20not_X20a_X20map, "persist._p is not a map", 1176528732u, 0, 23, &be_const_str_set_pwm); -be_define_const_str(phy, "phy", 1648673716u, 0, 3, &be_const_str_resp_cmnd); -be_define_const_str(pi, "pi", 1213090802u, 0, 2, &be_const_str_write8); -be_define_const_str(pin, "pin", 1866532500u, 0, 3, NULL); -be_define_const_str(pin_mode, "pin_mode", 3258314030u, 0, 8, &be_const_str_wire_scan); -be_define_const_str(pin_used, "pin_used", 4033854612u, 0, 8, &be_const_str_topic); -be_define_const_str(pixel_count, "pixel_count", 2439130743u, 0, 11, &be_const_str_try_get_bec_version); -be_define_const_str(pixel_size, "pixel_size", 2209135785u, 0, 10, &be_const_str_seq1); -be_define_const_str(pixels_buffer, "pixels_buffer", 1229555807u, 0, 13, NULL); +be_define_const_str(persist, "persist", 3917083779u, 0, 7, NULL); +be_define_const_str(persist_X2E_p_X20is_X20not_X20a_X20map, "persist._p is not a map", 1176528732u, 0, 23, NULL); +be_define_const_str(phy, "phy", 1648673716u, 0, 3, &be_const_str_seq0); +be_define_const_str(pi, "pi", 1213090802u, 0, 2, &be_const_str_widget_struct_by_class); +be_define_const_str(pin, "pin", 1866532500u, 0, 3, &be_const_str_write_bit); +be_define_const_str(pin_mode, "pin_mode", 3258314030u, 0, 8, NULL); +be_define_const_str(pin_used, "pin_used", 4033854612u, 0, 8, NULL); +be_define_const_str(pixel_count, "pixel_count", 2439130743u, 0, 11, &be_const_str_upper); +be_define_const_str(pixel_size, "pixel_size", 2209135785u, 0, 10, NULL); +be_define_const_str(pixels_buffer, "pixels_buffer", 1229555807u, 0, 13, &be_const_str_widget_cb); be_define_const_str(point_arr, "point_arr", 1140859857u, 0, 9, NULL); -be_define_const_str(pop, "pop", 1362321360u, 0, 3, NULL); -be_define_const_str(pop_path, "pop_path", 2403243998u, 0, 8, NULL); -be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str_set_power); -be_define_const_str(power_off, "power_off", 3568741752u, 0, 9, NULL); +be_define_const_str(pop, "pop", 1362321360u, 0, 3, &be_const_str_success); +be_define_const_str(pop_path, "pop_path", 2403243998u, 0, 8, &be_const_str_var); +be_define_const_str(pow, "pow", 1479764693u, 0, 3, &be_const_str_remote_ip); +be_define_const_str(power_off, "power_off", 3568741752u, 0, 9, &be_const_str_toupper); be_define_const_str(preinit, "preinit", 2722007100u, 0, 7, NULL); -be_define_const_str(print, "print", 372738696u, 0, 5, &be_const_str_sleep); +be_define_const_str(print, "print", 372738696u, 0, 5, NULL); be_define_const_str(ptr, "ptr", 1433816073u, 0, 3, NULL); -be_define_const_str(publish, "publish", 264247304u, 0, 7, &be_const_str_wire); +be_define_const_str(publish, "publish", 264247304u, 0, 7, NULL); be_define_const_str(publish_result, "publish_result", 2013351252u, 0, 14, NULL); be_define_const_str(publish_rule, "publish_rule", 1829459523u, 0, 12, NULL); be_define_const_str(push, "push", 2272264157u, 0, 4, NULL); -be_define_const_str(push_path, "push_path", 1155254157u, 0, 9, NULL); +be_define_const_str(push_path, "push_path", 1155254157u, 0, 9, &be_const_str_type); be_define_const_str(r, "r", 4144776981u, 0, 1, NULL); be_define_const_str(rad, "rad", 1358899048u, 0, 3, NULL); -be_define_const_str(raise, "raise", 1593437475u, 70, 5, &be_const_str_set_rgb); +be_define_const_str(raise, "raise", 1593437475u, 70, 5, &be_const_str_split); be_define_const_str(rand, "rand", 2711325910u, 0, 4, NULL); -be_define_const_str(range, "range", 4208725202u, 0, 5, NULL); +be_define_const_str(range, "range", 4208725202u, 0, 5, &be_const_str_style_prop_arr); be_define_const_str(raw, "raw", 1140790001u, 0, 3, NULL); -be_define_const_str(read, "read", 3470762949u, 0, 4, &be_const_str_set_bri); +be_define_const_str(read, "read", 3470762949u, 0, 4, NULL); be_define_const_str(read12, "read12", 4291076970u, 0, 6, NULL); be_define_const_str(read13, "read13", 12887293u, 0, 6, NULL); -be_define_const_str(read24, "read24", 1808533811u, 0, 6, &be_const_str_widget_event_impl); -be_define_const_str(read32, "read32", 1741276240u, 0, 6, &be_const_str_try_compile); +be_define_const_str(read24, "read24", 1808533811u, 0, 6, NULL); +be_define_const_str(read32, "read32", 1741276240u, 0, 6, NULL); be_define_const_str(read8, "read8", 2802788167u, 0, 5, NULL); be_define_const_str(read_bytes, "read_bytes", 3576733173u, 0, 10, NULL); -be_define_const_str(read_sensors, "read_sensors", 892689201u, 0, 12, NULL); +be_define_const_str(read_sensors, "read_sensors", 892689201u, 0, 12, &be_const_str_strip); be_define_const_str(readbytes, "readbytes", 2716426756u, 0, 9, NULL); be_define_const_str(readline, "readline", 1212709927u, 0, 8, NULL); be_define_const_str(real, "real", 3604983901u, 0, 4, NULL); -be_define_const_str(reapply, "reapply", 3778939332u, 0, 7, &be_const_str_slots); -be_define_const_str(redirect, "redirect", 389758641u, 0, 8, &be_const_str_stop_iteration); +be_define_const_str(reapply, "reapply", 3778939332u, 0, 7, NULL); +be_define_const_str(redirect, "redirect", 389758641u, 0, 8, NULL); be_define_const_str(register_obj, "register_obj", 3982614770u, 0, 12, NULL); -be_define_const_str(remote_ip, "remote_ip", 2953154693u, 0, 9, &be_const_str_tohex); -be_define_const_str(remote_port, "remote_port", 2163585967u, 0, 11, &be_const_str_time_dump); +be_define_const_str(remote_ip, "remote_ip", 2953154693u, 0, 9, NULL); +be_define_const_str(remote_port, "remote_port", 2163585967u, 0, 11, &be_const_str_write_file); be_define_const_str(remove, "remove", 3683784189u, 0, 6, NULL); be_define_const_str(remove_cmd, "remove_cmd", 3832315702u, 0, 10, NULL); be_define_const_str(remove_cron, "remove_cron", 2914538962u, 0, 11, NULL); -be_define_const_str(remove_driver, "remove_driver", 1030243768u, 0, 13, NULL); -be_define_const_str(remove_light, "remove_light", 1783624394u, 0, 12, &be_const_str_seti); +be_define_const_str(remove_driver, "remove_driver", 1030243768u, 0, 13, &be_const_str_settings); +be_define_const_str(remove_light, "remove_light", 1783624394u, 0, 12, &be_const_str_run); be_define_const_str(remove_rule, "remove_rule", 3456211328u, 0, 11, NULL); be_define_const_str(remove_timer, "remove_timer", 4141472215u, 0, 12, NULL); -be_define_const_str(remove_trailing_zeroes, "remove_trailing_zeroes", 2688378377u, 0, 22, &be_const_str_set_ct); -be_define_const_str(replace, "replace", 2704835779u, 0, 7, NULL); -be_define_const_str(reset, "reset", 1695364032u, 0, 5, &be_const_str_str); -be_define_const_str(resize, "resize", 3514612129u, 0, 6, NULL); +be_define_const_str(remove_trailing_zeroes, "remove_trailing_zeroes", 2688378377u, 0, 22, NULL); +be_define_const_str(replace, "replace", 2704835779u, 0, 7, &be_const_str_set); +be_define_const_str(reset, "reset", 1695364032u, 0, 5, NULL); +be_define_const_str(resize, "resize", 3514612129u, 0, 6, &be_const_str__X7Bs_X7DTemp_X20AXP_X7Bm_X7D_X25_X2E1f_X20_X26deg_X3BC_X7Be_X7D); be_define_const_str(resolvecmnd, "resolvecmnd", 993361485u, 0, 11, NULL); be_define_const_str(resp_cmnd, "resp_cmnd", 2869459626u, 0, 9, NULL); -be_define_const_str(resp_cmnd_done, "resp_cmnd_done", 2601874875u, 0, 14, &be_const_str_zero); -be_define_const_str(resp_cmnd_error, "resp_cmnd_error", 2404088863u, 0, 15, &be_const_str_rule); -be_define_const_str(resp_cmnd_failed, "resp_cmnd_failed", 2136281562u, 0, 16, &be_const_str_web_add_handler); -be_define_const_str(resp_cmnd_str, "resp_cmnd_str", 737845590u, 0, 13, NULL); +be_define_const_str(resp_cmnd_done, "resp_cmnd_done", 2601874875u, 0, 14, &be_const_str_tcpserver); +be_define_const_str(resp_cmnd_error, "resp_cmnd_error", 2404088863u, 0, 15, &be_const_str_set_mode_ct); +be_define_const_str(resp_cmnd_failed, "resp_cmnd_failed", 2136281562u, 0, 16, NULL); +be_define_const_str(resp_cmnd_str, "resp_cmnd_str", 737845590u, 0, 13, &be_const_str_webserver); be_define_const_str(response_append, "response_append", 450346371u, 0, 15, NULL); be_define_const_str(return, "return", 2246981567u, 60, 6, NULL); -be_define_const_str(return_X20code_X3D_X25i, "return code=%i", 2127454401u, 0, 14, NULL); -be_define_const_str(reverse, "reverse", 558918661u, 0, 7, NULL); -be_define_const_str(reverse_gamma10, "reverse_gamma10", 739112262u, 0, 15, NULL); +be_define_const_str(return_X20code_X3D_X25i, "return code=%i", 2127454401u, 0, 14, &be_const_str_url_encode); +be_define_const_str(reverse, "reverse", 558918661u, 0, 7, &be_const_str_set_timer); +be_define_const_str(reverse_gamma10, "reverse_gamma10", 739112262u, 0, 15, &be_const_str_to_gamma); be_define_const_str(rotate, "rotate", 2784296202u, 0, 6, NULL); -be_define_const_str(rtc, "rtc", 1070575216u, 0, 3, &be_const_str_show); +be_define_const_str(rtc, "rtc", 1070575216u, 0, 3, NULL); be_define_const_str(rule, "rule", 4230889683u, 0, 4, NULL); -be_define_const_str(run, "run", 718098122u, 0, 3, &be_const_str_style_prop_arr); -be_define_const_str(run_bat, "run_bat", 2536903298u, 0, 7, NULL); -be_define_const_str(run_cron, "run_cron", 1929098555u, 0, 8, &be_const_str_web_add_config_button); -be_define_const_str(run_deferred, "run_deferred", 371594696u, 0, 12, &be_const_str_zip); -be_define_const_str(running, "running", 343848780u, 0, 7, &be_const_str_tob64); -be_define_const_str(sat, "sat", 3592196823u, 0, 3, NULL); +be_define_const_str(run, "run", 718098122u, 0, 3, NULL); +be_define_const_str(run_bat, "run_bat", 2536903298u, 0, 7, &be_const_str_set_temp); +be_define_const_str(run_cron, "run_cron", 1929098555u, 0, 8, NULL); +be_define_const_str(run_deferred, "run_deferred", 371594696u, 0, 12, NULL); +be_define_const_str(running, "running", 343848780u, 0, 7, NULL); +be_define_const_str(sat, "sat", 3592196823u, 0, 3, &be_const_str_seq1); be_define_const_str(save, "save", 3439296072u, 0, 4, NULL); -be_define_const_str(save_before_restart, "save_before_restart", 1253239338u, 0, 19, NULL); -be_define_const_str(scale_uint, "scale_uint", 3090811094u, 0, 10, &be_const_str_update); +be_define_const_str(save_before_restart, "save_before_restart", 1253239338u, 0, 19, &be_const_str_webclient); +be_define_const_str(scale_uint, "scale_uint", 3090811094u, 0, 10, &be_const_str_set_matrix_pixel_color); be_define_const_str(scan, "scan", 3974641896u, 0, 4, NULL); be_define_const_str(sec, "sec", 3139892658u, 0, 3, NULL); -be_define_const_str(send, "send", 1919010991u, 0, 4, NULL); -be_define_const_str(send_multicast, "send_multicast", 812185870u, 0, 14, NULL); -be_define_const_str(seq0, "seq0", 880225636u, 0, 4, &be_const_str_widget_editable); -be_define_const_str(seq1, "seq1", 897003255u, 0, 4, &be_const_str_set_mode_ct); +be_define_const_str(send, "send", 1919010991u, 0, 4, &be_const_str_solidified); +be_define_const_str(send_multicast, "send_multicast", 812185870u, 0, 14, &be_const_str_write_gpio); +be_define_const_str(seq0, "seq0", 880225636u, 0, 4, &be_const_str_super); +be_define_const_str(seq1, "seq1", 897003255u, 0, 4, NULL); be_define_const_str(serial, "serial", 3687697785u, 0, 6, NULL); be_define_const_str(set, "set", 3324446467u, 0, 3, NULL); be_define_const_str(set_active, "set_active", 3683994102u, 0, 10, NULL); -be_define_const_str(set_alternate, "set_alternate", 1709680562u, 0, 13, &be_const_str_tan); +be_define_const_str(set_alternate, "set_alternate", 1709680562u, 0, 13, NULL); be_define_const_str(set_auth, "set_auth", 1057170930u, 0, 8, NULL); -be_define_const_str(set_bat, "set_bat", 2736667351u, 0, 7, &be_const_str_true); +be_define_const_str(set_bat, "set_bat", 2736667351u, 0, 7, NULL); be_define_const_str(set_bri, "set_bri", 2789118779u, 0, 7, NULL); -be_define_const_str(set_chg_current, "set_chg_current", 336304386u, 0, 15, &be_const_str_set_dcdc_enable); +be_define_const_str(set_chg_current, "set_chg_current", 336304386u, 0, 15, NULL); be_define_const_str(set_ct, "set_ct", 972363187u, 0, 6, NULL); be_define_const_str(set_dc_voltage, "set_dc_voltage", 2181981936u, 0, 14, NULL); be_define_const_str(set_dcdc_enable, "set_dcdc_enable", 1594690786u, 0, 15, NULL); be_define_const_str(set_exten, "set_exten", 1721782768u, 0, 9, NULL); be_define_const_str(set_first_time, "set_first_time", 3111247550u, 0, 14, NULL); -be_define_const_str(set_hue16sat, "set_hue16sat", 1858983599u, 0, 12, NULL); +be_define_const_str(set_hue16sat, "set_hue16sat", 1858983599u, 0, 12, &be_const_str_tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29); be_define_const_str(set_huesat, "set_huesat", 626496854u, 0, 10, NULL); -be_define_const_str(set_hum, "set_hum", 964296026u, 0, 7, NULL); +be_define_const_str(set_hum, "set_hum", 964296026u, 0, 7, &be_const_str__X7B); be_define_const_str(set_ldo_enable, "set_ldo_enable", 2916502041u, 0, 14, NULL); be_define_const_str(set_ldo_voltage, "set_ldo_voltage", 4090501160u, 0, 15, NULL); be_define_const_str(set_light, "set_light", 3176076152u, 0, 9, NULL); be_define_const_str(set_matrix_pixel_color, "set_matrix_pixel_color", 1197149462u, 0, 22, NULL); be_define_const_str(set_mem, "set_mem", 4097333429u, 0, 7, NULL); -be_define_const_str(set_mode_ct, "set_mode_ct", 665073295u, 0, 11, NULL); +be_define_const_str(set_mode_ct, "set_mode_ct", 665073295u, 0, 11, &be_const_str_widget_ctor_impl); be_define_const_str(set_mode_rgb, "set_mode_rgb", 852310875u, 0, 12, NULL); -be_define_const_str(set_ota_max, "set_ota_max", 4093779527u, 0, 11, NULL); -be_define_const_str(set_pixel_color, "set_pixel_color", 1275248356u, 0, 15, &be_const_str_write_bit); +be_define_const_str(set_ota_max, "set_ota_max", 4093779527u, 0, 11, &be_const_str_set_xy); +be_define_const_str(set_pixel_color, "set_pixel_color", 1275248356u, 0, 15, &be_const_str_tasmota); be_define_const_str(set_power, "set_power", 549820893u, 0, 9, NULL); -be_define_const_str(set_pwm, "set_pwm", 3781811012u, 0, 7, NULL); +be_define_const_str(set_pwm, "set_pwm", 3781811012u, 0, 7, &be_const_str_setitem); be_define_const_str(set_reachable, "set_reachable", 3280367499u, 0, 13, NULL); -be_define_const_str(set_rgb, "set_rgb", 3380244855u, 0, 7, &be_const_str_x); +be_define_const_str(set_rgb, "set_rgb", 3380244855u, 0, 7, NULL); be_define_const_str(set_temp, "set_temp", 1952131250u, 0, 8, NULL); -be_define_const_str(set_timeouts, "set_timeouts", 3732850900u, 0, 12, NULL); +be_define_const_str(set_timeouts, "set_timeouts", 3732850900u, 0, 12, &be_const_str_y); be_define_const_str(set_timer, "set_timer", 2135414533u, 0, 9, NULL); -be_define_const_str(set_useragent, "set_useragent", 612237244u, 0, 13, &be_const_str_wifi); +be_define_const_str(set_useragent, "set_useragent", 612237244u, 0, 13, &be_const_str_year); be_define_const_str(set_xy, "set_xy", 1155092615u, 0, 6, NULL); be_define_const_str(setbits, "setbits", 2762408167u, 0, 7, NULL); -be_define_const_str(setfloat, "setfloat", 2799488807u, 0, 8, &be_const_str_tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29); +be_define_const_str(setfloat, "setfloat", 2799488807u, 0, 8, NULL); be_define_const_str(seti, "seti", 1500556254u, 0, 4, NULL); be_define_const_str(setitem, "setitem", 1554834596u, 0, 7, NULL); -be_define_const_str(setmember, "setmember", 1432909441u, 0, 9, NULL); +be_define_const_str(setmember, "setmember", 1432909441u, 0, 9, &be_const_str_valuer_error); be_define_const_str(setrange, "setrange", 3794019032u, 0, 8, NULL); be_define_const_str(settings, "settings", 1745255176u, 0, 8, NULL); be_define_const_str(show, "show", 2840060476u, 0, 4, NULL); be_define_const_str(signal_change, "signal_change", 3262299350u, 0, 13, NULL); -be_define_const_str(sin, "sin", 3761252941u, 0, 3, NULL); -be_define_const_str(sinh, "sinh", 282220607u, 0, 4, NULL); -be_define_const_str(size, "size", 597743964u, 0, 4, &be_const_str_widget_constructor); +be_define_const_str(sin, "sin", 3761252941u, 0, 3, &be_const_str_subtype_to_string); +be_define_const_str(sinh, "sinh", 282220607u, 0, 4, &be_const_str_sqrt); +be_define_const_str(size, "size", 597743964u, 0, 4, NULL); be_define_const_str(sleep, "sleep", 2313861896u, 0, 5, NULL); -be_define_const_str(slots, "slots", 1023330342u, 0, 5, &be_const_str_write_bytes); +be_define_const_str(slots, "slots", 1023330342u, 0, 5, NULL); be_define_const_str(solidified, "solidified", 3257553487u, 0, 10, NULL); be_define_const_str(spiffs, "spiffs", 994943858u, 0, 6, NULL); -be_define_const_str(split, "split", 2276994531u, 0, 5, NULL); +be_define_const_str(split, "split", 2276994531u, 0, 5, &be_const_str_strftime); be_define_const_str(sqrt, "sqrt", 2112764879u, 0, 4, NULL); -be_define_const_str(srand, "srand", 465518633u, 0, 5, &be_const_str_time_reached); +be_define_const_str(srand, "srand", 465518633u, 0, 5, NULL); be_define_const_str(start, "start", 1697318111u, 0, 5, NULL); -be_define_const_str(state, "state", 2016490230u, 0, 5, NULL); +be_define_const_str(state, "state", 2016490230u, 0, 5, &be_const_str_unsubscribe); be_define_const_str(static, "static", 3532702267u, 71, 6, NULL); be_define_const_str(stop, "stop", 3411225317u, 0, 4, NULL); be_define_const_str(stop_iteration, "stop_iteration", 4173793901u, 0, 14, NULL); be_define_const_str(str, "str", 3259748752u, 0, 3, NULL); -be_define_const_str(strftime, "strftime", 187738851u, 0, 8, &be_const_str_tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29); +be_define_const_str(strftime, "strftime", 187738851u, 0, 8, NULL); be_define_const_str(string, "string", 398550328u, 0, 6, NULL); be_define_const_str(strip, "strip", 4246411473u, 0, 5, NULL); be_define_const_str(strptime, "strptime", 1277910361u, 0, 8, NULL); be_define_const_str(style_prop_arr, "style_prop_arr", 3019174322u, 0, 14, NULL); be_define_const_str(subscribe, "subscribe", 2946386435u, 0, 9, NULL); -be_define_const_str(subtype, "subtype", 2023873341u, 0, 7, NULL); -be_define_const_str(subtype_to_string, "subtype_to_string", 2996733901u, 0, 17, &be_const_str__X7B_X7D); -be_define_const_str(success, "success", 979353360u, 0, 7, &be_const_str__X7D); +be_define_const_str(subtype, "subtype", 2023873341u, 0, 7, &be_const_str_tele); +be_define_const_str(subtype_to_string, "subtype_to_string", 2996733901u, 0, 17, NULL); +be_define_const_str(success, "success", 979353360u, 0, 7, NULL); be_define_const_str(sum, "sum", 3712891560u, 0, 3, NULL); be_define_const_str(super, "super", 4152230356u, 0, 5, NULL); be_define_const_str(switch_factory, "switch_factory", 4206217516u, 0, 14, NULL); be_define_const_str(sys, "sys", 3277365014u, 0, 3, NULL); be_define_const_str(tan, "tan", 2633446552u, 0, 3, NULL); -be_define_const_str(tanh, "tanh", 153638352u, 0, 4, &be_const_str_while); +be_define_const_str(tanh, "tanh", 153638352u, 0, 4, NULL); be_define_const_str(target, "target", 845187144u, 0, 6, NULL); -be_define_const_str(tasmota, "tasmota", 424643812u, 0, 7, &be_const_str_to_gamma); +be_define_const_str(tasmota, "tasmota", 424643812u, 0, 7, NULL); be_define_const_str(tasmota_X2Eget_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eget_X28_X29, "tasmota.get_light() is deprecated, use light.get()", 3525753647u, 0, 50, NULL); be_define_const_str(tasmota_X2Eset_light_X28_X29_X20is_X20deprecated_X2C_X20use_X20light_X2Eset_X28_X29, "tasmota.set_light() is deprecated, use light.set()", 2124937871u, 0, 50, NULL); be_define_const_str(tasmota_log_reader, "tasmota_log_reader", 3555069257u, 0, 18, NULL); -be_define_const_str(tcpclient, "tcpclient", 3828797983u, 0, 9, &be_const_str_tr); -be_define_const_str(tele, "tele", 3474458061u, 0, 4, &be_const_str_unknown_X20instruction); +be_define_const_str(tcpclient, "tcpclient", 3828797983u, 0, 9, NULL); +be_define_const_str(tcpserver, "tcpserver", 240809979u, 0, 9, NULL); +be_define_const_str(tele, "tele", 3474458061u, 0, 4, NULL); be_define_const_str(test, "test", 2949673445u, 0, 4, NULL); be_define_const_str(the_X20second_X20argument_X20is_X20not_X20a_X20function, "the second argument is not a function", 3954574469u, 0, 37, NULL); -be_define_const_str(time_dump, "time_dump", 3330410747u, 0, 9, NULL); -be_define_const_str(time_reached, "time_reached", 2075136773u, 0, 12, NULL); +be_define_const_str(time_dump, "time_dump", 3330410747u, 0, 9, &be_const_str_wire1); +be_define_const_str(time_reached, "time_reached", 2075136773u, 0, 12, &be_const_str_write_flash); be_define_const_str(time_str, "time_str", 2613827612u, 0, 8, NULL); -be_define_const_str(timer_cb, "timer_cb", 79918026u, 0, 8, NULL); -be_define_const_str(to_gamma, "to_gamma", 1597139862u, 0, 8, NULL); +be_define_const_str(timer_cb, "timer_cb", 79918026u, 0, 8, &be_const_str_value); +be_define_const_str(to_gamma, "to_gamma", 1597139862u, 0, 8, &be_const_str_try_remove_file); be_define_const_str(tob64, "tob64", 373777640u, 0, 5, NULL); be_define_const_str(tobytes, "tobytes", 595962279u, 0, 7, NULL); be_define_const_str(tohex, "tohex", 1583935793u, 0, 5, NULL); -be_define_const_str(toint, "toint", 3613182909u, 0, 5, &be_const_str_widget_ctor_impl); +be_define_const_str(toint, "toint", 3613182909u, 0, 5, NULL); be_define_const_str(tolower, "tolower", 1042520049u, 0, 7, NULL); -be_define_const_str(tomap, "tomap", 612167626u, 0, 5, &be_const_str_w); +be_define_const_str(tomap, "tomap", 612167626u, 0, 5, NULL); be_define_const_str(top, "top", 2802900028u, 0, 3, NULL); -be_define_const_str(topic, "topic", 3264522692u, 0, 5, &be_const_str_web_add_console_button); -be_define_const_str(toptr, "toptr", 3379847454u, 0, 5, &be_const_str_web_add_main_button); -be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, NULL); -be_define_const_str(toupper, "toupper", 3691983576u, 0, 7, NULL); +be_define_const_str(topic, "topic", 3264522692u, 0, 5, &be_const_str_udp); +be_define_const_str(toptr, "toptr", 3379847454u, 0, 5, NULL); +be_define_const_str(tostring, "tostring", 2299708645u, 0, 8, &be_const_str__X7B_X7D); +be_define_const_str(toupper, "toupper", 3691983576u, 0, 7, &be_const_str_widget_ctor_cb); be_define_const_str(tr, "tr", 1195724803u, 0, 2, NULL); be_define_const_str(traceback, "traceback", 3385188109u, 0, 9, NULL); -be_define_const_str(trig, "trig", 2073314619u, 0, 4, &be_const_str__X7Bs_X7DBatt_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D); +be_define_const_str(trig, "trig", 2073314619u, 0, 4, NULL); be_define_const_str(true, "true", 1303515621u, 61, 4, NULL); -be_define_const_str(try, "try", 2887626766u, 68, 3, NULL); -be_define_const_str(try_compile, "try_compile", 4263879840u, 0, 11, NULL); +be_define_const_str(try, "try", 2887626766u, 68, 3, &be_const_str_wire2); +be_define_const_str(try_compile, "try_compile", 4263879840u, 0, 11, &be_const_str_type_error); be_define_const_str(try_get_bec_version, "try_get_bec_version", 3143116423u, 0, 19, NULL); -be_define_const_str(try_remove_file, "try_remove_file", 3025429926u, 0, 15, NULL); +be_define_const_str(try_remove_file, "try_remove_file", 3025429926u, 0, 15, &be_const_str__X7Bs_X7DVBus_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D); be_define_const_str(try_rule, "try_rule", 1986449405u, 0, 8, NULL); be_define_const_str(try_run_compiled, "try_run_compiled", 2339741218u, 0, 16, NULL); be_define_const_str(type, "type", 1361572173u, 0, 4, NULL); -be_define_const_str(type_error, "type_error", 3789613824u, 0, 10, &be_const_str_udp); +be_define_const_str(type_error, "type_error", 3789613824u, 0, 10, NULL); be_define_const_str(type_to_string, "type_to_string", 1072804509u, 0, 14, NULL); be_define_const_str(udp, "udp", 1253872004u, 0, 3, NULL); -be_define_const_str(unknown_X20instruction, "unknown instruction", 1093911841u, 0, 19, NULL); +be_define_const_str(unknown_X20instruction, "unknown instruction", 1093911841u, 0, 19, &be_const_str_widget_event_impl); be_define_const_str(unsubscribe, "unsubscribe", 4190043798u, 0, 11, NULL); be_define_const_str(update, "update", 672109684u, 0, 6, NULL); be_define_const_str(upper, "upper", 176974407u, 0, 5, NULL); be_define_const_str(url_encode, "url_encode", 528392145u, 0, 10, NULL); -be_define_const_str(uuid4, "uuid4", 1153582450u, 0, 5, &be_const_str_widget_ctor_cb); +be_define_const_str(uuid4, "uuid4", 1153582450u, 0, 5, NULL); be_define_const_str(value, "value", 1113510858u, 0, 5, NULL); be_define_const_str(value_error, "value_error", 773297791u, 0, 11, NULL); be_define_const_str(valuer_error, "valuer_error", 2567947105u, 0, 12, NULL); be_define_const_str(var, "var", 2317739966u, 64, 3, NULL); be_define_const_str(w, "w", 4060888886u, 0, 1, NULL); -be_define_const_str(wake_period, "wake_period", 1520770415u, 0, 11, &be_const_str_widget_dtor_cb); +be_define_const_str(wake_period, "wake_period", 1520770415u, 0, 11, NULL); be_define_const_str(wd, "wd", 1531424278u, 0, 2, NULL); be_define_const_str(web_add_button, "web_add_button", 3537875058u, 0, 14, NULL); be_define_const_str(web_add_config_button, "web_add_config_button", 639674325u, 0, 21, NULL); be_define_const_str(web_add_console_button, "web_add_console_button", 3481436192u, 0, 22, NULL); be_define_const_str(web_add_handler, "web_add_handler", 3990174962u, 0, 15, NULL); be_define_const_str(web_add_main_button, "web_add_main_button", 3960367664u, 0, 19, NULL); -be_define_const_str(web_add_management_button, "web_add_management_button", 2738877186u, 0, 25, &be_const_str_y); +be_define_const_str(web_add_management_button, "web_add_management_button", 2738877186u, 0, 25, &be_const_str_widget_dtor_impl); be_define_const_str(web_send, "web_send", 2989941448u, 0, 8, NULL); -be_define_const_str(web_send_decimal, "web_send_decimal", 1407210204u, 0, 16, NULL); +be_define_const_str(web_send_decimal, "web_send_decimal", 1407210204u, 0, 16, &be_const_str_write8); be_define_const_str(web_sensor, "web_sensor", 2900096972u, 0, 10, NULL); be_define_const_str(webclient, "webclient", 4076389146u, 0, 9, NULL); be_define_const_str(webserver, "webserver", 1572454038u, 0, 9, NULL); be_define_const_str(while, "while", 231090382u, 53, 5, NULL); be_define_const_str(widget_cb, "widget_cb", 2763583055u, 0, 9, NULL); -be_define_const_str(widget_constructor, "widget_constructor", 2543785934u, 0, 18, NULL); +be_define_const_str(widget_constructor, "widget_constructor", 2543785934u, 0, 18, &be_const_str_yield); be_define_const_str(widget_ctor_cb, "widget_ctor_cb", 876007560u, 0, 14, NULL); be_define_const_str(widget_ctor_impl, "widget_ctor_impl", 194252479u, 0, 16, NULL); be_define_const_str(widget_destructor, "widget_destructor", 4207388345u, 0, 17, NULL); @@ -861,8 +864,8 @@ be_define_const_str(wire1, "wire1", 3212721419u, 0, 5, NULL); be_define_const_str(wire2, "wire2", 3229499038u, 0, 5, NULL); be_define_const_str(wire_scan, "wire_scan", 2671275880u, 0, 9, NULL); be_define_const_str(write, "write", 3190202204u, 0, 5, NULL); -be_define_const_str(write8, "write8", 3133991532u, 0, 6, &be_const_str_xy); -be_define_const_str(write_bit, "write_bit", 2660990436u, 0, 9, &be_const_str_year); +be_define_const_str(write8, "write8", 3133991532u, 0, 6, NULL); +be_define_const_str(write_bit, "write_bit", 2660990436u, 0, 9, NULL); be_define_const_str(write_bytes, "write_bytes", 1227543792u, 0, 11, NULL); be_define_const_str(write_file, "write_file", 3177658879u, 0, 10, NULL); be_define_const_str(write_flash, "write_flash", 3003629621u, 0, 11, NULL); @@ -871,7 +874,7 @@ be_define_const_str(x, "x", 4245442695u, 0, 1, NULL); be_define_const_str(xy, "xy", 1482915802u, 0, 2, NULL); be_define_const_str(y, "y", 4228665076u, 0, 1, NULL); be_define_const_str(year, "year", 2927578396u, 0, 4, NULL); -be_define_const_str(yield, "yield", 1821831854u, 0, 5, &be_const_str__X7B); +be_define_const_str(yield, "yield", 1821831854u, 0, 5, NULL); be_define_const_str(zero, "zero", 2339366755u, 0, 4, NULL); be_define_const_str(zip, "zip", 2877453236u, 0, 3, NULL); be_define_const_str(_X7B, "{", 4262220314u, 0, 1, NULL); @@ -1457,440 +1460,442 @@ be_define_const_str(_X7B_X22hasp_X22_X3A_X7B_X22p_X25i_X22_X3A_X22out_X22_X7D_X7 be_define_const_str(_X7B_X22hasp_X22_X3A_X7B_X22p_X25ib_X25i_X22_X3A_X7B_X22event_X22_X3A_X22_X25s_X22_X25s_X7D_X7D_X7D, "{\"hasp\":{\"p%ib%i\":{\"event\":\"%s\"%s}}}", 0u, 0, 36, NULL); static const bstring* const m_string_table[] = { - (const bstring *)&be_const_str_begin, - (const bstring *)&be_const_str_raw, - (const bstring *)&be_const_str_reapply, - (const bstring *)&be_const_str_fulltopic, - (const bstring *)&be_const_str_global, - (const bstring *)&be_const_str_TAP_X3A_X20Loaded_X20Tasmota_X20App_X20_X27_X25s_X27, - (const bstring *)&be_const_str_AA50, - (const bstring *)&be_const_str_read8, - (const bstring *)&be_const_str_BECDFE, - (const bstring *)&be_const_str_width_def, - (const bstring *)&be_const_str__X5D_X2C_X0A_X20_X20, - (const bstring *)&be_const_str_True, - (const bstring *)&be_const_str__p, - (const bstring *)&be_const_str_energy_struct, + (const bstring *)&be_const_str_files, + (const bstring *)&be_const_str_make_cb, NULL, - (const bstring *)&be_const_str_SERIAL_5E1, - (const bstring *)&be_const_str__X3E_X3D, - (const bstring *)&be_const_str_setbits, - (const bstring *)&be_const_str_add_anim, - (const bstring *)&be_const_str__X2Ep, - (const bstring *)&be_const_str_content_response, - (const bstring *)&be_const_str_get_battery_chargin_status, - (const bstring *)&be_const_str_load_otadata, - (const bstring *)&be_const_str__X2F_X2Eautoconf, - (const bstring *)&be_const_str__X2D_X2A, - (const bstring *)&be_const_str_leds, - (const bstring *)&be_const_str_False, - (const bstring *)&be_const_str__X3E, + (const bstring *)&be_const_str_publish_rule, NULL, - (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, - (const bstring *)&be_const_str_break, + (const bstring *)&be_const_str__X20_X28, + (const bstring *)&be_const_str_dac_voltage, NULL, - (const bstring *)&be_const_str_setrange, - (const bstring *)&be_const_str_CFG_X3A_X20removing_X20first_X20time_X20marker, - NULL, - (const bstring *)&be_const_str_pixel_size, - (const bstring *)&be_const_str_assign_rmt, - (const bstring *)&be_const_str_elif, - (const bstring *)&be_const_str_save_before_restart, - (const bstring *)&be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, - (const bstring *)&be_const_str_OPTION_A, - (const bstring *)&be_const_str_keys, - (const bstring *)&be_const_str_SERIAL_6O1, - (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_sys, - (const bstring *)&be_const_str_get_bri, - NULL, - (const bstring *)&be_const_str_pixels_buffer, - (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X20, - (const bstring *)&be_const_str_load, - (const bstring *)&be_const_str_clear_to, - (const bstring *)&be_const_str_get_MAC, - (const bstring *)&be_const_str_chars_in_string, - (const bstring *)&be_const_str_read_bytes, - (const bstring *)&be_const_str__X2Etapp, - (const bstring *)&be_const_str__X2C, - NULL, - (const bstring *)&be_const_str_arg_size, - (const bstring *)&be_const_str__persist_X2Ejson, - (const bstring *)&be_const_str_class, - (const bstring *)&be_const_str_continue, - (const bstring *)&be_const_str_SERIAL_6N1, - (const bstring *)&be_const_str_debug, - NULL, - NULL, - (const bstring *)&be_const_str_Partition, - (const bstring *)&be_const_str__X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, - NULL, - (const bstring *)&be_const_str_I2C_Driver, - (const bstring *)&be_const_str__X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, - (const bstring *)&be_const_str_state, - (const bstring *)&be_const_str_page_autoconf_mgr, - NULL, - NULL, - (const bstring *)&be_const_str__X2Esize, - (const bstring *)&be_const_str_get, + (const bstring *)&be_const_str_Unknown_X20command, + (const bstring *)&be_const_str_CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20compiled_X20_X27_X25s_X27_X20_X28_X25s_X29, - NULL, - (const bstring *)&be_const_str_frombytes, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20removing_X20autoconf_X20files, - (const bstring *)&be_const_str_, - (const bstring *)&be_const_str_format, - (const bstring *)&be_const_str_DIMMER, - (const bstring *)&be_const_str_json_append, - (const bstring *)&be_const_str_json_fdump_any, - (const bstring *)&be_const_str__begin_transmission, + (const bstring *)&be_const_str_RGBCT, + (const bstring *)&be_const_str_web_send_decimal, + (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27zipapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3EApply_X20configuration_X3C_X2Fbutton_X3E, + (const bstring *)&be_const_str_lv_extra, + (const bstring *)&be_const_str_event, + (const bstring *)&be_const_str_Partition_otadata, (const bstring *)&be_const_str__X23autoexec_X2Ebat, - (const bstring *)&be_const_str_web_send, - (const bstring *)&be_const_str_base_class, - (const bstring *)&be_const_str_bytes, - (const bstring *)&be_const_str_EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, - (const bstring *)&be_const_str_publish_result, - (const bstring *)&be_const_str__change_buffer, - (const bstring *)&be_const_str_SERIAL_7E2, - (const bstring *)&be_const_str_md5, - (const bstring *)&be_const_str_active_otadata, - NULL, - (const bstring *)&be_const_str_is_ota, - (const bstring *)&be_const_str_Auto_X2Dconfiguration, - (const bstring *)&be_const_str_nvskeys, - (const bstring *)&be_const_str_char, - (const bstring *)&be_const_str__X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, - (const bstring *)&be_const_str__X20_X20, - (const bstring *)&be_const_str_gamma, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, - (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, - (const bstring *)&be_const_str_push, - (const bstring *)&be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, - (const bstring *)&be_const_str__X20, - (const bstring *)&be_const_str___lower__, - (const bstring *)&be_const_str__X2Ebec, - NULL, - (const bstring *)&be_const_str_Tele, - (const bstring *)&be_const_str__X21_X3D_X3D, - (const bstring *)&be_const_str_cb_event_closure, - (const bstring *)&be_const_str__X3Cselect_X20name_X3D_X27zip_X27_X3E, - (const bstring *)&be_const_str_MD5, - NULL, - NULL, - NULL, - NULL, - (const bstring *)&be_const_str__X29, - NULL, - (const bstring *)&be_const_str_SERIAL_7O2, - NULL, - (const bstring *)&be_const_str__X28_X29, - (const bstring *)&be_const_str_BRY_X3A_X20could_X20not_X20save_X20compiled_X20file_X20_X25s_X20_X28_X25s_X29, - (const bstring *)&be_const_str_AXP192, - (const bstring *)&be_const_str_argument_X20must_X20be_X20a_X20function, - (const bstring *)&be_const_str__X23display_X2Eini, - (const bstring *)&be_const_str__X23autoexec_X2Ebe, - (const bstring *)&be_const_str_find, - (const bstring *)&be_const_str_get_bat_current, - (const bstring *)&be_const_str__X3Clabel_X3EChoose_X20a_X20device_X20configuration_X3A_X3C_X2Flabel_X3E_X3Cbr_X3E, - (const bstring *)&be_const_str_arg_name, - (const bstring *)&be_const_str_SERIAL_8E1, - (const bstring *)&be_const_str_get_name, - (const bstring *)&be_const_str_Parameter_X20error, - (const bstring *)&be_const_str_BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, - (const bstring *)&be_const_str__energy, - (const bstring *)&be_const_str_assert, - (const bstring *)&be_const_str_calldepth, - (const bstring *)&be_const_str_resp_cmnd_error, - NULL, - (const bstring *)&be_const_str_Restart_X201, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20loading_X20_X27_X25s_X27, - (const bstring *)&be_const_str__X3Clambda_X3E, - (const bstring *)&be_const_str_fromptr, - (const bstring *)&be_const_str_is_dirty, - (const bstring *)&be_const_str_Animate_X20pc_X20is_X20out_X20of_X20range, - (const bstring *)&be_const_str_model, - (const bstring *)&be_const_str_json_fdump_list, - (const bstring *)&be_const_str_toptr, - (const bstring *)&be_const_str_call_native, - (const bstring *)&be_const_str_button_pressed, - NULL, - (const bstring *)&be_const_str_int64, - (const bstring *)&be_const_str_internal_error, - NULL, - (const bstring *)&be_const_str_SERIAL_7E1, - (const bstring *)&be_const_str_hue_status, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, - (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, - (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, - (const bstring *)&be_const_str_SERIAL_6E1, - (const bstring *)&be_const_str_content_button, - NULL, - (const bstring *)&be_const_str__crons, - (const bstring *)&be_const_str__, - (const bstring *)&be_const_str_get_mem, - (const bstring *)&be_const_str_pi, + (const bstring *)&be_const_str__X23, + (const bstring *)&be_const_str_create_matrix, + (const bstring *)&be_const_str_pc, (const bstring *)&be_const_str_widget_height_def, - (const bstring *)&be_const_str_type_error, - (const bstring *)&be_const_str_Leds, + (const bstring *)&be_const_str_height_def, + (const bstring *)&be_const_str_get, + (const bstring *)&be_const_str_print, + (const bstring *)&be_const_str_SERIAL_5N2, + (const bstring *)&be_const_str__X2Etapp, + (const bstring *)&be_const_str_leds, + (const bstring *)&be_const_str_show, + (const bstring *)&be_const_str__X3A, + (const bstring *)&be_const_str_begin, + (const bstring *)&be_const_str_class, + (const bstring *)&be_const_str_web_add_console_button, + (const bstring *)&be_const_str_collect, + (const bstring *)&be_const_str_add_fast_loop, + NULL, + (const bstring *)&be_const_str_pin, + (const bstring *)&be_const_str_erase, + (const bstring *)&be_const_str_find_key_i, + (const bstring *)&be_const_str_atan2, + (const bstring *)&be_const_str_contains, + (const bstring *)&be_const_str_last_modified, + (const bstring *)&be_const_str_fromhex, + (const bstring *)&be_const_str_CFG_X3A_X20multiple_X20autoconf_X20files_X20found_X2C_X20aborting_X20_X28_X27_X25s_X27_X20_X2B_X20_X27_X25s_X27_X29, + (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X27_X25s_X27, + (const bstring *)&be_const_str__X26lt_X3BError_X3A_X20apply_X20new_X20or_X20remove_X26gt_X3B, + (const bstring *)&be_const_str_o, + (const bstring *)&be_const_str_start, + (const bstring *)&be_const_str__X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, + (const bstring *)&be_const_str__global_addr, + (const bstring *)&be_const_str__X2Ew, + (const bstring *)&be_const_str_MD5, + (const bstring *)&be_const_str__timers, + (const bstring *)&be_const_str__X22, + (const bstring *)&be_const_str_asin, + (const bstring *)&be_const_str_exec_cmd, + (const bstring *)&be_const_str_set_light, + (const bstring *)&be_const_str_arch, + (const bstring *)&be_const_str__X5B, + (const bstring *)&be_const_str_time_dump, + (const bstring *)&be_const_str_matrix, + (const bstring *)&be_const_str_compress, + (const bstring *)&be_const_str_calldepth, + NULL, + (const bstring *)&be_const_str_is_first_time, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X20, + (const bstring *)&be_const_str_SERIAL_5E1, + (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27_X25s_X27_X3E_X25s_X3C_X2Foption_X3E, + (const bstring *)&be_const_str_clear_first_time, + (const bstring *)&be_const_str_remove_cmd, + (const bstring *)&be_const_str_setfloat, + (const bstring *)&be_const_str_BUTTON_CONFIGURATION, + (const bstring *)&be_const_str__write, + (const bstring *)&be_const_str__X28_X29, + NULL, + NULL, + (const bstring *)&be_const_str__X0A, + NULL, + (const bstring *)&be_const_str_hour, + (const bstring *)&be_const_str_CFG_X3A_X20skipping_X20_X27display_X2Eini_X27_X20because_X20already_X20present_X20in_X20file_X2Dsystem, + (const bstring *)&be_const_str_get_object_from_ptr, + (const bstring *)&be_const_str_allocated, + (const bstring *)&be_const_str_SERIAL_7N2, + (const bstring *)&be_const_str_exec_rules, + (const bstring *)&be_const_str__available, + (const bstring *)&be_const_str_Parameter_X20error, + NULL, (const bstring *)&be_const_str__X2D, NULL, - (const bstring *)&be_const_str_BRY_X3A_X20argument_X20must_X20be_X20a_X20function, - (const bstring *)&be_const_str_add_handler, - (const bstring *)&be_const_str_add, - (const bstring *)&be_const_str_deg, - (const bstring *)&be_const_str_Partition_info, - NULL, - (const bstring *)&be_const_str_set_huesat, - (const bstring *)&be_const_str__drivers, - (const bstring *)&be_const_str_crc16, - (const bstring *)&be_const_str_ctypes_bytes_dyn, - (const bstring *)&be_const_str__X2Flights_X2F, - (const bstring *)&be_const_str__X2A, + (const bstring *)&be_const_str_isnan, + (const bstring *)&be_const_str_abs, (const bstring *)&be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27Autoconfiguration_X27_X3E_X26nbsp_X3BCurrent_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, - (const bstring *)&be_const_str__X0A, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, - (const bstring *)&be_const_str_CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, - (const bstring *)&be_const_str_MI32, + (const bstring *)&be_const_str_bool, + (const bstring *)&be_const_str_try, + (const bstring *)&be_const_str_CFG_X3A_X20loading_X20, + (const bstring *)&be_const_str_try_compile, + (const bstring *)&be_const_str_editable, NULL, - (const bstring *)&be_const_str__X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_point_arr, - (const bstring *)&be_const_str_isinstance, - (const bstring *)&be_const_str_iter, - (const bstring *)&be_const_str_ceil, - (const bstring *)&be_const_str__X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_SERIAL_8O1, - (const bstring *)&be_const_str_SERIAL_8E2, - (const bstring *)&be_const_str_delay, - (const bstring *)&be_const_str_None, - (const bstring *)&be_const_str__X23preinit_X2Ebe, + (const bstring *)&be_const_str__X7Bs_X7DVBus_X20Current_X7Bm_X7D_X25_X2E1f_X20mA_X7Be_X7D, NULL, - (const bstring *)&be_const_str__validate, - (const bstring *)&be_const_str_traceback, - (const bstring *)&be_const_str_CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, - (const bstring *)&be_const_str__X20_X28, - (const bstring *)&be_const_str_CFG_X3A_X20ran_X20_X20, - (const bstring *)&be_const_str_deregister_obj, - (const bstring *)&be_const_str_target, - NULL, - (const bstring *)&be_const_str_0x_X2502X, - (const bstring *)&be_const_str__X3D_X3C_X3E_X21, - (const bstring *)&be_const_str_get_log, - (const bstring *)&be_const_str_atan, - (const bstring *)&be_const_str__X7Bs_X7DVBus_X20Voltage_X7Bm_X7D_X25_X2E3f_X20V_X7Be_X7D, - (const bstring *)&be_const_str_function, - (const bstring *)&be_const_str_dac_voltage, - (const bstring *)&be_const_str_crc, - (const bstring *)&be_const_str_copy, - (const bstring *)&be_const_str_collect, - (const bstring *)&be_const_str_SERIAL_7N1, - (const bstring *)&be_const_str_add_cron, + (const bstring *)&be_const_str_pow, + (const bstring *)&be_const_str_SERIAL_5E2, + (const bstring *)&be_const_str_remove_timer, + (const bstring *)&be_const_str_Wire, + (const bstring *)&be_const_str_Unknown, + (const bstring *)&be_const_str__X26lt_X3BNone_X26gt_X3B, + (const bstring *)&be_const_str_begin_multicast, + (const bstring *)&be_const_str_running, + (const bstring *)&be_const_str_as, + (const bstring *)&be_const_str_fast_loop, + (const bstring *)&be_const_str_instance_X20required, (const bstring *)&be_const_str_check_privileged_access, NULL, - (const bstring *)&be_const_str_sum, - (const bstring *)&be_const_str_floor, + (const bstring *)&be_const_str_BECDFE, NULL, - (const bstring *)&be_const_str_create_matrix, - (const bstring *)&be_const_str_on, - (const bstring *)&be_const_str__filename, - (const bstring *)&be_const_str__X3Cp_X3E_X3Csmall_X3E_X26nbsp_X3B_X28This_X20feature_X20requires_X20an_X20internet_X20connection_X29_X3C_X2Fsmall_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_connect, - (const bstring *)&be_const_str_height_def, - (const bstring *)&be_const_str_create_segment, - (const bstring *)&be_const_str_every_250ms, - (const bstring *)&be_const_str_is_running, - (const bstring *)&be_const_str_name, - (const bstring *)&be_const_str__X2E, - (const bstring *)&be_const_str_is_first_time, - (const bstring *)&be_const_str_SERIAL_7O1, - (const bstring *)&be_const_str_widget_event, - (const bstring *)&be_const_str__X27_X20_X2D_X20, - (const bstring *)&be_const_str_get_pixel_color, - (const bstring *)&be_const_str_invalid_X20magic_X20number_X20_X2502X, - (const bstring *)&be_const_str_a, - (const bstring *)&be_const_str__X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, - (const bstring *)&be_const_str_remove_cron, - (const bstring *)&be_const_str__ccmd, - (const bstring *)&be_const_str_BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, - (const bstring *)&be_const_str__X2E_X2E, - (const bstring *)&be_const_str_else, - (const bstring *)&be_const_str_get_free_heap, - (const bstring *)&be_const_str_classname, - (const bstring *)&be_const_str_full_status, - (const bstring *)&be_const_str_set_timeouts, - (const bstring *)&be_const_str_POST, - (const bstring *)&be_const_str_Unknown_X20command, - (const bstring *)&be_const_str_ins_time, - (const bstring *)&be_const_str_publish, - (const bstring *)&be_const_str_abs, - NULL, - NULL, - NULL, - (const bstring *)&be_const_str__X3C, - (const bstring *)&be_const_str_RGB, - (const bstring *)&be_const_str_FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, - (const bstring *)&be_const_str_BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, - (const bstring *)&be_const_str_add_light, - (const bstring *)&be_const_str__X0A_X29_X3E, - (const bstring *)&be_const_str_tostring, - (const bstring *)&be_const_str__X2Elen, - (const bstring *)&be_const_str_readline, - (const bstring *)&be_const_str__X2Ebe, - (const bstring *)&be_const_str_groups, - (const bstring *)&be_const_str_imax, - (const bstring *)&be_const_str__def, - (const bstring *)&be_const_str_resp_cmnd_failed, - (const bstring *)&be_const_str_gamma10, - (const bstring *)&be_const_str__X25, - (const bstring *)&be_const_str__X22_X3A, - (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dzip_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, - (const bstring *)&be_const_str_SERIAL_6E2, - (const bstring *)&be_const_str__X26lt_X3BNone_X26gt_X3B, - (const bstring *)&be_const_str_hour, - (const bstring *)&be_const_str__read, - (const bstring *)&be_const_str_content_stop, - (const bstring *)&be_const_str_contains, - (const bstring *)&be_const_str_getfloat, - (const bstring *)&be_const_str__X25s_X2Eautoconf, - (const bstring *)&be_const_str_test, - (const bstring *)&be_const_str_before_del, - (const bstring *)&be_const_str__X22, - NULL, - (const bstring *)&be_const_str_json, - (const bstring *)&be_const_str_import, - NULL, - (const bstring *)&be_const_str__fl, - (const bstring *)&be_const_str_bool, - (const bstring *)&be_const_str_clear, - (const bstring *)&be_const_str__debug_present, - (const bstring *)&be_const_str_pc, - (const bstring *)&be_const_str_BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, - (const bstring *)&be_const_str__X2Eautoconf, - NULL, - (const bstring *)&be_const_str__X2B, - NULL, - (const bstring *)&be_const_str_reverse_gamma10, - (const bstring *)&be_const_str_ctor, - (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, - (const bstring *)&be_const_str_remove_timer, + (const bstring *)&be_const_str_continue, + (const bstring *)&be_const_str__X3D_X3C_X3E_X21, + (const bstring *)&be_const_str__X3Clambda_X3E, + (const bstring *)&be_const_str_ctypes_bytes, + (const bstring *)&be_const_str_lv_, + (const bstring *)&be_const_str_accept, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20run_X20compiled_X20code_X20_X27_X25s_X27_X20_X2D_X20_X25s, + (const bstring *)&be_const_str__X2C, (const bstring *)&be_const_str_eth, - (const bstring *)&be_const_str_arch, - (const bstring *)&be_const_str_number, - (const bstring *)&be_const_str_GET, - (const bstring *)&be_const_str_get_bat_charge_current, NULL, - (const bstring *)&be_const_str__cmd, - (const bstring *)&be_const_str_SERIAL_8O2, - (const bstring *)&be_const_str_SERIAL_5E2, + (const bstring *)&be_const_str_instance_size, + (const bstring *)&be_const_str_content_stop, + (const bstring *)&be_const_str_counters, + (const bstring *)&be_const_str_point_arr, + (const bstring *)&be_const_str_CT, + (const bstring *)&be_const_str_can_show, + (const bstring *)&be_const_str_crc32, + (const bstring *)&be_const_str_, + (const bstring *)&be_const_str_path, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_otadata_X28ota_active_X3A_X25s_X2C_X20ota_seq_X3D_X5B_X25d_X2C_X25d_X5D_X2C_X20ota_max_X3D_X25d_X29_X3E, + (const bstring *)&be_const_str_wire, + (const bstring *)&be_const_str_get_size, + (const bstring *)&be_const_str_delete_all_configs, + (const bstring *)&be_const_str_assert, + (const bstring *)&be_const_str_get_light, + (const bstring *)&be_const_str__X2E_X2E, + NULL, + (const bstring *)&be_const_str_Restart_X201, + (const bstring *)&be_const_str_OPTION_A, + (const bstring *)&be_const_str_unknown_X20instruction, + (const bstring *)&be_const_str_addr, + (const bstring *)&be_const_str_SERIAL_8N1, + (const bstring *)&be_const_str__X2Ebe, + (const bstring *)&be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_X2F_X25s_X2Eautoconf, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dzip_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, + (const bstring *)&be_const_str_add_rule, + (const bstring *)&be_const_str__X3Cp_X3E_X3C_X2Fp_X3E_X3C_X2Ffieldset_X3E_X3Cp_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str__X2504d_X2D_X2502d_X2D_X2502dT_X2502d_X3A_X2502d_X3A_X2502d, + (const bstring *)&be_const_str_end, + (const bstring *)&be_const_str_I2C_X3A, + (const bstring *)&be_const_str_preinit, + (const bstring *)&be_const_str_factory, + (const bstring *)&be_const_str__X2F, + (const bstring *)&be_const_str_issubclass, + (const bstring *)&be_const_str_ismethod, + NULL, + (const bstring *)&be_const_str_set_chg_current, + (const bstring *)&be_const_str_autoexec, + (const bstring *)&be_const_str_adc_config, + NULL, + (const bstring *)&be_const_str_rand, + (const bstring *)&be_const_str_No_X20SPIFFS_X20partition_X20found, + (const bstring *)&be_const_str_imin, + NULL, + NULL, + (const bstring *)&be_const_str_trig, + NULL, + (const bstring *)&be_const_str_00, + (const bstring *)&be_const_str__t, + (const bstring *)&be_const_str_byte, + (const bstring *)&be_const_str_digital_read, + (const bstring *)&be_const_str_CFG_X3A_X20loading_X20_X27_X25s_X27, + (const bstring *)&be_const_str_RELAY, + (const bstring *)&be_const_str_invalid_X20magic_X20number_X20_X2502X, + (const bstring *)&be_const_str_lights, + NULL, + (const bstring *)&be_const_str_SERIAL_8N2, + (const bstring *)&be_const_str_arg, + (const bstring *)&be_const_str_BRY_X3A_X20corrupt_X20bytecode_X20_X27_X25s_X27, + (const bstring *)&be_const_str_srand, + (const bstring *)&be_const_str_timer_cb, + (const bstring *)&be_const_str_lv_module_init, + (const bstring *)&be_const_str__X23display_X2Eini, + (const bstring *)&be_const_str_module, + (const bstring *)&be_const_str_lv_point, + (const bstring *)&be_const_str_sinh, + (const bstring *)&be_const_str__X2C_X22AXP192_X22_X3A_X7B_X22VBusVoltage_X22_X3A_X25_X2E3f_X2C_X22VBusCurrent_X22_X3A_X25_X2E1f_X2C_X22BattVoltage_X22_X3A_X25_X2E3f_X2C_X22BattCurrent_X22_X3A_X25_X2E1f_X2C_X22Temperature_X22_X3A_X25_X2E1f_X7D, + (const bstring *)&be_const_str__X2E, + (const bstring *)&be_const_str_codedump, + (const bstring *)&be_const_str__X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str__X23preinit_X2Ebe, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20_X25s_X28_X25s_X2C_X20_X25s_X2C_X20_X25s_X29, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_X27_X25s_X27_X20_X28_X25s_X20_X2D_X20_X25s_X29, + (const bstring *)&be_const_str_BRY_X3A_X20invalid_X20hue_X20payload_X3A_X20, + (const bstring *)&be_const_str_setrange, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_info_X28_X25d_X25s_X2C_X25d_X25s_X2C0x_X2508X_X2C0x_X2508X_X2C_X27_X25s_X27_X2C0x_X25X_X29_X3E, + (const bstring *)&be_const_str_cmd_res, + (const bstring *)&be_const_str_set_bri, + (const bstring *)&be_const_str_SERIAL_6N2, + NULL, + (const bstring *)&be_const_str_lvgl_event_dispatch, + (const bstring *)&be_const_str_atan, + (const bstring *)&be_const_str_has_arg, + NULL, + (const bstring *)&be_const_str_web_add_management_button, + (const bstring *)&be_const_str__X21_X3D, + (const bstring *)&be_const_str_topic, + (const bstring *)&be_const_str__X2Ebec, + (const bstring *)&be_const_str_CFG_X3A_X20running_X20, + (const bstring *)&be_const_str_dump, + (const bstring *)&be_const_str__X3D, + (const bstring *)&be_const_str_get_mem, + (const bstring *)&be_const_str_hex, + (const bstring *)&be_const_str_resp_cmnd_done, + (const bstring *)&be_const_str_class_init_obj, + (const bstring *)&be_const_str_content_start, + (const bstring *)&be_const_str_active_otadata, + (const bstring *)&be_const_str_resp_cmnd_error, + (const bstring *)&be_const_str_AXP192, + (const bstring *)&be_const_str_deregister_obj, + (const bstring *)&be_const_str__subscribe, + (const bstring *)&be_const_str__X2F_X3Frst_X3D, + (const bstring *)&be_const_str__X25s_X2Eautoconf, + (const bstring *)&be_const_str_data, + (const bstring *)&be_const_str_SERIAL_7O2, + (const bstring *)&be_const_str_get_factory_slot, + (const bstring *)&be_const_str_SERIAL_6O1, + (const bstring *)&be_const_str_f, + (const bstring *)&be_const_str_0x_X2502X, + (const bstring *)&be_const_str_static, + NULL, + (const bstring *)&be_const_str_set_power, + (const bstring *)&be_const_str_is_dirty, + NULL, + (const bstring *)&be_const_str__X25, + (const bstring *)&be_const_str__rules, + (const bstring *)&be_const_str_pi, + (const bstring *)&be_const_str_file, + (const bstring *)&be_const_str_set_timeouts, + (const bstring *)&be_const_str_BRY_X3A_X20failed_X20to_X20load_X20_persist_X2Ejson, + NULL, + (const bstring *)&be_const_str_read8, + (const bstring *)&be_const_str_publish, + (const bstring *)&be_const_str__X2F_X2Eautoconf, + (const bstring *)&be_const_str_widget_event, + (const bstring *)&be_const_str__X3C, + (const bstring *)&be_const_str_size, + (const bstring *)&be_const_str_SERIAL_5O1, + (const bstring *)&be_const_str_discover, + NULL, + (const bstring *)&be_const_str__end_transmission, + (const bstring *)&be_const_str_BRY_X3A_X20ERROR_X2C_X20bad_X20json_X3A_X20, + (const bstring *)&be_const_str_clear_to, + (const bstring *)&be_const_str__X3Clegend_X3E_X3Cb_X20title_X3D_X27New_X20autoconf_X27_X3E_X26nbsp_X3BSelect_X20new_X20auto_X2Dconfiguration_X3C_X2Fb_X3E_X3C_X2Flegend_X3E, + NULL, + (const bstring *)&be_const_str_web_add_config_button, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, + (const bstring *)&be_const_str_get_ota_slot, + (const bstring *)&be_const_str_wifi, + NULL, + (const bstring *)&be_const_str_lv_point_arr, NULL, - (const bstring *)&be_const_str__dirty, - (const bstring *)&be_const_str__X23init_X2Ebat, - (const bstring *)&be_const_str_remove_rule, - (const bstring *)&be_const_str_offseta, - (const bstring *)&be_const_str_get_current_module_name, - (const bstring *)&be_const_str__X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, (const bstring *)&be_const_str_WS2812_GRB, NULL, - (const bstring *)&be_const_str_counters, - (const bstring *)&be_const_str_try_rule, - (const bstring *)&be_const_str_BRY_X3A_X20Exception_X3E_X20_X27, - (const bstring *)&be_const_str_check_not_method, - (const bstring *)&be_const_str_gpio, - (const bstring *)&be_const_str_tobytes, - (const bstring *)&be_const_str_flags, - (const bstring *)&be_const_str_I2C_X3A, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20Exception_X3E_X20_X27_X25s_X27_X20_X2D_X20_X25s, - NULL, - (const bstring *)&be_const_str_last_modified, - (const bstring *)&be_const_str__X3C_X3D, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_X28_X5B_X0A, - (const bstring *)&be_const_str__buffer, - (const bstring *)&be_const_str_getbits, - (const bstring *)&be_const_str_register_obj, - (const bstring *)&be_const_str_gc, - (const bstring *)&be_const_str__settings_ptr, - (const bstring *)&be_const_str___upper__, - (const bstring *)&be_const_str__request_from, - (const bstring *)&be_const_str_Trigger, - (const bstring *)&be_const_str_insert, - NULL, - (const bstring *)&be_const_str_CFG_X3A_X20loaded_X20_X27_X25s_X27, - (const bstring *)&be_const_str_content_send, - (const bstring *)&be_const_str_SERIAL_5N1, - (const bstring *)&be_const_str_00, - (const bstring *)&be_const_str_couldn_X27t_X20not_X20initialize_X20noepixelbus, - (const bstring *)&be_const_str_lv_timer_cb, - (const bstring *)&be_const_str__X2F, - (const bstring *)&be_const_str__X3D_X3D, - (const bstring *)&be_const_str_Wire, - (const bstring *)&be_const_str_set, - (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dreapply_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20action_X3D_X27_X2Fac_X27_X20method_X3D_X27post_X27_X20, - (const bstring *)&be_const_str_esphttpd, - (const bstring *)&be_const_str_gamma8, - (const bstring *)&be_const_str__global_def, - (const bstring *)&be_const_str_next, + (const bstring *)&be_const_str_get_cb_list, + (const bstring *)&be_const_str_create_segment, + (const bstring *)&be_const_str_listdir, + (const bstring *)&be_const_str_add_cb_event_closure, + (const bstring *)&be_const_str_decompress, + (const bstring *)&be_const_str_fulltopic, + (const bstring *)&be_const_str_BRY_X3A_X20method_X20not_X20allowed_X2C_X20use_X20a_X20closure_X20like_X20_X27_X2F_X20args_X20_X2D_X3E_X20obj_X2Efunc_X28args_X29_X27, + (const bstring *)&be_const_str_load_templates, + (const bstring *)&be_const_str__X0A_X29_X3E, + (const bstring *)&be_const_str___iterator__, + (const bstring *)&be_const_str__X3Cselect_X20name_X3D_X27zip_X27_X3E, (const bstring *)&be_const_str_MAX_RMT, - (const bstring *)&be_const_str_web_send_decimal, - (const bstring *)&be_const_str_get_aps_voltage, - (const bstring *)&be_const_str_timer_cb, - (const bstring *)&be_const_str_SERIAL_5O1, - (const bstring *)&be_const_str__X3Cfieldset_X3E_X3Cstyle_X3E_X2Ebdis_X7Bbackground_X3A_X23888_X3B_X7D_X2Ebdis_X3Ahover_X7Bbackground_X3A_X23888_X3B_X7D_X3C_X2Fstyle_X3E, + (const bstring *)&be_const_str__X3Cp_X20style_X3D_X27width_X3A340px_X3B_X27_X3E_X3Cb_X3EException_X3A_X3C_X2Fb_X3E_X3Cbr_X3E_X27_X25s_X27_X3Cbr_X3E_X25s_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_None, + (const bstring *)&be_const_str_light_X20must_X20be_X20of_X20class_X20_X27light_state_X27, + (const bstring *)&be_const_str__X20_X20, + (const bstring *)&be_const_str__X3F, + (const bstring *)&be_const_str_base_class, + (const bstring *)&be_const_str_asstring, NULL, - (const bstring *)&be_const_str_CFG_X3A_X20return_code_X3D_X25i, - (const bstring *)&be_const_str_bus, + (const bstring *)&be_const_str_battery_present, + (const bstring *)&be_const_str_close, + (const bstring *)&be_const_str_add_header, + (const bstring *)&be_const_str_out_X20of_X20range, NULL, - (const bstring *)&be_const_str_get_bat_power, - (const bstring *)&be_const_str_o, - (const bstring *)&be_const_str_set_xy, - (const bstring *)&be_const_str_lv_event_cb, - (const bstring *)&be_const_str_event_cb, - (const bstring *)&be_const_str_unsubscribe, - (const bstring *)&be_const_str_digital_write, - (const bstring *)&be_const_str__X3D, + (const bstring *)&be_const_str_file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27, + (const bstring *)&be_const_str_set_ldo_voltage, + (const bstring *)&be_const_str_SERIAL_8E2, + (const bstring *)&be_const_str__X2B, + (const bstring *)&be_const_str_lv_style_prop_arr, + (const bstring *)&be_const_str_cos, + (const bstring *)&be_const_str_read13, + (const bstring *)&be_const_str__dirty, + (const bstring *)&be_const_str__X3Cp_X3E_X3Cform_X20id_X3Dac_X20action_X3D_X27ac_X27_X20style_X3D_X27display_X3A_X20block_X3B_X27_X20method_X3D_X27get_X27_X3E_X3Cbutton_X3EAuto_X2Dconfiguration_X3C_X2Fbutton_X3E_X3C_X2Fform_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_str, + (const bstring *)&be_const_str_widget_struct_default, + (const bstring *)&be_const_str__X3C_X3D, + (const bstring *)&be_const_str__X2Ep, + (const bstring *)&be_const_str_SERIAL_7E2, + (const bstring *)&be_const_str_SERIAL_6N1, + (const bstring *)&be_const_str__read, + (const bstring *)&be_const_str_BRY_X3A_X20bytecode_X20has_X20wrong_X20version_X20_X27_X25s_X27_X20_X28_X25i_X29, + (const bstring *)&be_const_str_false, + (const bstring *)&be_const_str_call, + (const bstring *)&be_const_str__p, + (const bstring *)&be_const_str_content_flush, + (const bstring *)&be_const_str__request_from, + (const bstring *)&be_const_str_get_free_heap, + (const bstring *)&be_const_str_enabled, + (const bstring *)&be_const_str_content_send_style, + (const bstring *)&be_const_str_web_add_main_button, + (const bstring *)&be_const_str__X3E, + (const bstring *)&be_const_str_finish, + (const bstring *)&be_const_str___upper__, + (const bstring *)&be_const_str_efuse_em, + (const bstring *)&be_const_str__def, + (const bstring *)&be_const_str___lower__, + (const bstring *)&be_const_str_add_handler, + (const bstring *)&be_const_str__X3Cbutton_X20name_X3D_X27reapply_X27_X20class_X3D_X27button_X20bgrn_X27_X3ERe_X2Dapply_X20current_X20configuration_X3C_X2Fbutton_X3E, + (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_X28_X5B_X0A, NULL, - (const bstring *)&be_const_str_fast_loop, - (const bstring *)&be_const_str__X3C_X2Fselect_X3E_X3Cp_X3E_X3C_X2Fp_X3E, - (const bstring *)&be_const_str_HTTP_POST, - (const bstring *)&be_const_str_begin_multicast, - (const bstring *)&be_const_str_Too_X20many_X20partiition_X20slots, - (const bstring *)&be_const_str_CFG_X3A_X20_X27init_X2Ebat_X27_X20done_X2C_X20restarting, - (const bstring *)&be_const_str_HTTP_GET, - (const bstring *)&be_const_str_No_X20SPIFFS_X20partition_X20found, + (const bstring *)&be_const_str_arg_name, + (const bstring *)&be_const_str_elements_X20must_X20be_X20a_X20lv_point, + (const bstring *)&be_const_str__X2A, NULL, - (const bstring *)&be_const_str__X3Cinstance_X3A_X20Partition_info_X28_X25d_X25s_X2C_X25d_X25s_X2C0x_X2508X_X2C0x_X2508X_X2C_X27_X25s_X27_X2C0x_X25X_X29_X3E, - (const bstring *)&be_const_str_has_factory, - (const bstring *)&be_const_str__X3A, - (const bstring *)&be_const_str_bri, - (const bstring *)&be_const_str_url_encode, - (const bstring *)&be_const_str_var, - (const bstring *)&be_const_str__X2F_X3Frst_X3D, - (const bstring *)&be_const_str_compile, - (const bstring *)&be_const_str_editable, - (const bstring *)&be_const_str_success, - (const bstring *)&be_const_str__global_addr, - (const bstring *)&be_const_str__X5D, - (const bstring *)&be_const_str_now, - (const bstring *)&be_const_str_if, + (const bstring *)&be_const_str_onsubmit_X3D_X27return_X20confirm_X28_X22This_X20will_X20change_X20the_X20current_X20configuration_X20and_X20cause_X20a_X20restart_X2E_X22_X29_X3B_X27_X3E, + (const bstring *)&be_const_str_reset, + (const bstring *)&be_const_str_SERIAL_6O2, + (const bstring *)&be_const_str_c, NULL, - (const bstring *)&be_const_str_settings, + (const bstring *)&be_const_str__X2Fstate_X2F, + (const bstring *)&be_const_str_test, + NULL, + (const bstring *)&be_const_str__X27_X20_X2D_X20, + (const bstring *)&be_const_str_attrdump, + (const bstring *)&be_const_str_Invalid_X20ota_X20partition_X20number, + NULL, + (const bstring *)&be_const_str__X3C_X2Fform_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_def, + (const bstring *)&be_const_str_Auto_X2Dconfiguration, + (const bstring *)&be_const_str__, + (const bstring *)&be_const_str_acos, + (const bstring *)&be_const_str__X2Esize, (const bstring *)&be_const_str_constructor_cb, - (const bstring *)&be_const_str__X23, - (const bstring *)&be_const_str_as, - (const bstring *)&be_const_str_null_cb, - (const bstring *)&be_const_str_RGBCT, - (const bstring *)&be_const_str_except, - (const bstring *)&be_const_str_get_size, - (const bstring *)&be_const_str_SERIAL_6N2, - (const bstring *)&be_const_str_https_X3A_X2F_X2Fraw_X2Egithubusercontent_X2Ecom_X2Ftasmota_X2Fautoconf_X2Fmain_X2F_X25s_manifest_X2Ejson, + (const bstring *)&be_const_str_classof, + (const bstring *)&be_const_str__X23autoexec_X2Ebe, + (const bstring *)&be_const_str__X3E_X3D, NULL, - (const bstring *)&be_const_str_SERIAL_5N2, - (const bstring *)&be_const_str__X2Fac, - (const bstring *)&be_const_str_file_X20extension_X20is_X20not_X20_X27_X2Ebe_X27_X20or_X20_X27_X2Ebec_X27 + NULL, + (const bstring *)&be_const_str_content_button, + (const bstring *)&be_const_str_CFG_X3A_X20return_code_X3D_X25i, + (const bstring *)&be_const_str__drivers, + (const bstring *)&be_const_str_HTTP_GET, + (const bstring *)&be_const_str_EVENT_DELETE, + (const bstring *)&be_const_str_hue_status, + (const bstring *)&be_const_str__rmt, + (const bstring *)&be_const_str__class, + (const bstring *)&be_const_str_light, + (const bstring *)&be_const_str_animate, + (const bstring *)&be_const_str_list, + (const bstring *)&be_const_str_instance, + (const bstring *)&be_const_str_read24, + (const bstring *)&be_const_str_bri, + (const bstring *)&be_const_str_b, + (const bstring *)&be_const_str_signal_change, + (const bstring *)&be_const_str_set_rgb, + (const bstring *)&be_const_str_argument_X20must_X20be_X20a_X20list_X20or_X20a_X20pointer_X2Bsize, + (const bstring *)&be_const_str_widget_destructor, + (const bstring *)&be_const_str_get_bat_charge_current, + NULL, + (const bstring *)&be_const_str_CFG_X3A_X20could_X20not_X20run_X20_X25s_X20_X28_X25s_X20_X2D_X20_X25s_X29, + (const bstring *)&be_const_str_resolvecmnd, + (const bstring *)&be_const_str_cb_do_nothing, + (const bstring *)&be_const_str__X3D_X3D, + (const bstring *)&be_const_str_else, + NULL, + (const bstring *)&be_const_str_BRY_X3A_X20Exception_X3E_X20_X27, + NULL, + (const bstring *)&be_const_str_fat, + (const bstring *)&be_const_str__X2508x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2D_X2504x_X2508x, + (const bstring *)&be_const_str_resp_cmnd_str, + (const bstring *)&be_const_str_POST, + (const bstring *)&be_const_str__begin_transmission, + (const bstring *)&be_const_str_CFG_X3A_X20exception_X20_X27_X25s_X27_X20_X2D_X20_X27_X25s_X27, + (const bstring *)&be_const_str_find, + (const bstring *)&be_const_str__crons, + (const bstring *)&be_const_str_add_anim, + (const bstring *)&be_const_str_Tele, + (const bstring *)&be_const_str_int, + NULL, + (const bstring *)&be_const_str_find_op, + (const bstring *)&be_const_str__X3Cp_X3ECurrent_X20configuration_X3A_X20_X3C_X2Fp_X3E_X3Cp_X3E_X3Cb_X3E_X25s_X3C_X2Fb_X3E_X3C_X2Fp_X3E, + (const bstring *)&be_const_str_cb_obj, + (const bstring *)&be_const_str__settings_def, + (const bstring *)&be_const_str_code, + (const bstring *)&be_const_str__X23init_X2Ebat, + (const bstring *)&be_const_str_ota_, + (const bstring *)&be_const_str_GET, + (const bstring *)&be_const_str_delay, + (const bstring *)&be_const_str_maxota, + (const bstring *)&be_const_str__X21_X3D_X3D, + (const bstring *)&be_const_str_input, + (const bstring *)&be_const_str_parse, + (const bstring *)&be_const_str_SERIAL_7E1, + (const bstring *)&be_const_str__cmd, + (const bstring *)&be_const_str__X20, + (const bstring *)&be_const_str_every_250ms, + (const bstring *)&be_const_str_EBEBFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + (const bstring *)&be_const_str_AA50, + (const bstring *)&be_const_str_LVG_X3A_X20call_X20to_X20unsupported_X20callback, + (const bstring *)&be_const_str__X2Flights_X2F, + (const bstring *)&be_const_str_FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + (const bstring *)&be_const_str__X2D_X2A, + (const bstring *)&be_const_str_pixel_count, + (const bstring *)&be_const_str__X2Elen, + (const bstring *)&be_const_str_is_spiffs, + (const bstring *)&be_const_str_crc16, + (const bstring *)&be_const_str__X3Coption_X20value_X3D_X27reset_X27_X3E_X26lt_X3BRemove_X20autoconf_X26gt_X3B_X3C_X2Foption_X3E, + (const bstring *)&be_const_str_flags, + (const bstring *)&be_const_str__X22_X3A, + NULL, + (const bstring *)&be_const_str_pixels_buffer }; static const struct bconststrtab m_const_string_table = { - .size = 430, - .count = 884, + .size = 432, + .count = 887, .table = m_string_table }; diff --git a/lib/libesp32/berry/generate/be_fixed_be_class_tcpclient.h b/lib/libesp32/berry/generate/be_fixed_be_class_tcpclient.h index 72bdbffb4..b689a5a3b 100644 --- a/lib/libesp32/berry/generate/be_fixed_be_class_tcpclient.h +++ b/lib/libesp32/berry/generate/be_fixed_be_class_tcpclient.h @@ -1,16 +1,16 @@ #include "be_constobj.h" static be_define_const_map_slots(be_class_tcpclient_map) { - { be_const_key(write, 8), be_const_func(wc_tcp_write) }, - { be_const_key(close, -1), be_const_func(wc_tcp_close) }, - { be_const_key(connected, 3), be_const_func(wc_tcp_connected) }, { be_const_key(deinit, -1), be_const_func(wc_tcp_deinit) }, - { be_const_key(_X2Ew, 0), be_const_var(0) }, + { be_const_key(close, -1), be_const_func(wc_tcp_close) }, + { be_const_key(connected, 0), be_const_func(wc_tcp_connected) }, + { be_const_key(_X2Ep, -1), be_const_var(0) }, + { be_const_key(available, 8), be_const_func(wc_tcp_available) }, { be_const_key(init, -1), be_const_func(wc_tcp_init) }, { be_const_key(readbytes, -1), be_const_func(wc_tcp_readbytes) }, { be_const_key(connect, -1), be_const_func(wc_tcp_connect) }, - { be_const_key(available, -1), be_const_func(wc_tcp_available) }, - { be_const_key(read, -1), be_const_func(wc_tcp_read) }, + { be_const_key(write, -1), be_const_func(wc_tcp_write) }, + { be_const_key(read, 3), be_const_func(wc_tcp_read) }, }; static be_define_const_map( diff --git a/lib/libesp32/berry/generate/be_fixed_be_class_tcpserver.h b/lib/libesp32/berry/generate/be_fixed_be_class_tcpserver.h new file mode 100644 index 000000000..74e46cc35 --- /dev/null +++ b/lib/libesp32/berry/generate/be_fixed_be_class_tcpserver.h @@ -0,0 +1,22 @@ +#include "be_constobj.h" + +static be_define_const_map_slots(be_class_tcpserver_map) { + { be_const_key(init, -1), be_const_ctype_func(tcpserver_init) }, + { be_const_key(accept, -1), be_const_ctype_func(tcpserver_accept) }, + { be_const_key(_p, -1), be_const_var(0) }, + { be_const_key(close, 0), be_const_ctype_func(tcpserver_close) }, + { be_const_key(deinit, 5), be_const_ctype_func(tcpserver_deinit) }, + { be_const_key(hasclient, -1), be_const_ctype_func(tcpserver_hasclient) }, +}; + +static be_define_const_map( + be_class_tcpserver_map, + 6 +); + +BE_EXPORT_VARIABLE be_define_const_class( + be_class_tcpserver, + 1, + NULL, + tcpserver +); diff --git a/lib/libesp32/berry_tasmota/src/be_tcpclient_lib.c b/lib/libesp32/berry_tasmota/src/be_tcpclient_lib.c index 246436058..5fe1c7e78 100644 --- a/lib/libesp32/berry_tasmota/src/be_tcpclient_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_tcpclient_lib.c @@ -30,7 +30,7 @@ void be_load_tcpclient_lib(bvm *vm) { /* @const_object_info_begin class be_class_tcpclient (scope: global, name: tcpclient) { - .w, var + .p, var init, func(wc_tcp_init) deinit, func(wc_tcp_deinit) diff --git a/lib/libesp32/berry_tasmota/src/be_tcpserver_class.c b/lib/libesp32/berry_tasmota/src/be_tcpserver_class.c new file mode 100644 index 000000000..c8aa216d4 --- /dev/null +++ b/lib/libesp32/berry_tasmota/src/be_tcpserver_class.c @@ -0,0 +1,32 @@ +/******************************************************************** + * Tcp socket + *******************************************************************/ +#include "be_constobj.h" +#include "be_mapping.h" +#include "be_ctypes.h" + +#ifdef USE_BERRY_TCPSERVER + +extern const void * tcpserver_init(struct bvm *vm, int32_t port); BE_FUNC_CTYPE_DECLARE(tcpserver_init, "+_p", "@i") +extern void tcpserver_deinit(void *server_tcp); BE_FUNC_CTYPE_DECLARE(tcpserver_deinit, "", ".") + +extern void tcpserver_close(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_close, "", ".") +extern bbool tcpserver_hasclient(void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_hasclient, "b", ".") +extern void * tcpserver_accept(struct bvm *vm, void *server); BE_FUNC_CTYPE_DECLARE(tcpserver_accept, "tcpclient", "@.") + + +#include "be_fixed_be_class_tcpserver.h" + +/* @const_object_info_begin +class be_class_tcpserver (scope: global, name: tcpserver) { + _p, var + init, ctype_func(tcpserver_init) + deinit, ctype_func(tcpserver_deinit) + + close, ctype_func(tcpserver_close) + hasclient, ctype_func(tcpserver_hasclient) + accept, ctype_func(tcpserver_accept) +} +@const_object_info_end */ + +#endif // USE_BERRY_TCPSERVER diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino new file mode 100644 index 000000000..e8458d17f --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino @@ -0,0 +1,81 @@ +/* + xdrv_52_3_berry_tcpserver.ino - Berry scripting language, tcp socket and server class + + Copyright (C) 2022 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// also includes tcp_client + +#ifdef USE_BERRY +#ifdef USE_BERRY_TCPSERVER + +#include + +/*********************************************************************************************\ + * Native functions mapped to Berry functions + * +\*********************************************************************************************/ +extern "C" { + const void* tcpserver_init(struct bvm *vm, int32_t tcp_port) { + if (tcp_port > 0 && tcp_port < 65535) { + WiFiServer *server_tcp = new WiFiServer(tcp_port); + server_tcp->begin(); // start TCP server + server_tcp->setNoDelay(true); + return server_tcp; + } else { + be_raisef(vm, "value_error","Invalid port %d", tcp_port); + } + return NULL; + } + + void tcpserver_deinit(void *server) { + WiFiServer *server_tcp = (WiFiServer*) server; + if (server_tcp) { + server_tcp->stop(); + delete server_tcp; + } + } + + void tcpserver_close(void *server) { + WiFiServer *server_tcp = (WiFiServer*) server; + if (server_tcp) { + server_tcp->stop(); + } + } + + bbool tcpserver_hasclient(void *server) { + WiFiServer *server_tcp = (WiFiServer*) server; + return server_tcp->hasClient(); + } + + void * tcpserver_accept(struct bvm *vm, void *server) { + WiFiServer *server_tcp = (WiFiServer*) server; + WiFiClient * client_ptr = nullptr; + if (server_tcp->hasClient()) { + WiFiClient new_client = server_tcp->available(); + AddLog(LOG_LEVEL_INFO, "BRY: Got connection from %s", new_client.remoteIP().toString().c_str()); + client_ptr = new WiFiClient(); + *client_ptr = new_client; + } else { + be_raise(vm, "internal_error", "tcpserver has no client connected"); + } + return client_ptr; + } + +} + +#endif // USE_BERRY_TCPSERVER +#endif // USE_BERRY diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino index ee42ff22c..1b9773bb5 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_webclient.ino @@ -98,9 +98,15 @@ extern "C" { int32_t wc_tcp_init(struct bvm *vm); int32_t wc_tcp_init(struct bvm *vm) { - WiFiClient * wcl = new WiFiClient(); + int32_t argc = be_top(vm); + WiFiClient * wcl; + if (argc >= 2 && be_iscomptr(vm, 2)) { + wcl = (WiFiClient*) be_tocomptr(vm, 2); + } else { + wcl = new WiFiClient(); + } be_pushcomptr(vm, (void*) wcl); - be_setmember(vm, 1, ".w"); + be_setmember(vm, 1, ".p"); be_return_nil(vm); } @@ -118,6 +124,14 @@ extern "C" { return (WiFiClient*) p; } + // same but using `.p` argument + WiFiClient * wc_getwificlient_p(struct bvm *vm) { + be_getmember(vm, 1, ".p"); + void *p = be_tocomptr(vm, -1); + be_pop(vm, 1); + return (WiFiClient*) p; + } + int32_t wc_deinit(struct bvm *vm); int32_t wc_deinit(struct bvm *vm) { // int32_t argc = be_top(vm); // Get the number of arguments @@ -133,9 +147,9 @@ extern "C" { int32_t wc_tcp_deinit(struct bvm *vm); int32_t wc_tcp_deinit(struct bvm *vm) { - WiFiClient * wcl = wc_getwificlient(vm); + WiFiClient * wcl = wc_getwificlient_p(vm); if (wcl != nullptr) { delete wcl; } - be_setmember(vm, 1, ".w"); + be_setmember(vm, 1, ".p"); be_return_nil(vm); } @@ -172,7 +186,7 @@ extern "C" { int32_t wc_tcp_connect(struct bvm *vm) { int32_t argc = be_top(vm); if (argc >= 3 && be_isstring(vm, 2) && be_isint(vm, 3)) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); const char * address = be_tostring(vm, 2); int32_t port = be_toint(vm, 3); int32_t timeout = USE_BERRY_WEBCLIENT_TIMEOUT; // default timeout of 2 seconds @@ -198,7 +212,7 @@ extern "C" { // tcp.close(void) -> nil int32_t wc_tcp_close(struct bvm *vm); int32_t wc_tcp_close(struct bvm *vm) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); tcp->stop(); be_return_nil(vm); } @@ -206,7 +220,7 @@ extern "C" { // tcp.available(void) -> int int32_t wc_tcp_available(struct bvm *vm); int32_t wc_tcp_available(struct bvm *vm) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); int32_t available = tcp->available(); be_pushint(vm, available); be_return(vm); @@ -295,7 +309,7 @@ extern "C" { // tcp.connected(void) -> bool int32_t wc_tcp_connected(struct bvm *vm); int32_t wc_tcp_connected(struct bvm *vm) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); be_pushbool(vm, tcp->connected()); be_return(vm); /* return code */ } @@ -305,7 +319,7 @@ extern "C" { int32_t wc_tcp_write(struct bvm *vm) { int32_t argc = be_top(vm); if (argc >= 2 && (be_isstring(vm, 2) || be_isbytes(vm, 2))) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); const char * buf = nullptr; size_t buf_len = 0; if (be_isstring(vm, 2)) { // string @@ -324,7 +338,7 @@ extern "C" { // tcp.read() -> string int32_t wc_tcp_read(struct bvm *vm); int32_t wc_tcp_read(struct bvm *vm) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); int32_t max_read = -1; // by default read as much as we can if (be_top(vm) >= 2 && be_isint(vm, 2)) { max_read = be_toint(vm, 2); @@ -346,7 +360,7 @@ extern "C" { // tcp.readbytes() -> bytes int32_t wc_tcp_readbytes(struct bvm *vm); int32_t wc_tcp_readbytes(struct bvm *vm) { - WiFiClient * tcp = wc_getwificlient(vm); + WiFiClient * tcp = wc_getwificlient_p(vm); int32_t max_read = -1; // by default read as much as we can if (be_top(vm) >= 2 && be_isint(vm, 2)) { max_read = be_toint(vm, 2); From d27a87f86f8372871fad8a51189aca632bff2663 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 7 Jul 2022 19:56:08 +0200 Subject: [PATCH 081/219] Report error of socket failed to open --- tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino index e8458d17f..1040ff255 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tcpserver.ino @@ -33,6 +33,9 @@ extern "C" { if (tcp_port > 0 && tcp_port < 65535) { WiFiServer *server_tcp = new WiFiServer(tcp_port); server_tcp->begin(); // start TCP server + if (!*server_tcp) { + be_raise(vm, "network_error", "Failed to open socket"); + } server_tcp->setNoDelay(true); return server_tcp; } else { From 5aadcc7b36b6798cc9099ed825394e4cbaf256f9 Mon Sep 17 00:00:00 2001 From: CCTweaker <19865794+cctweaker@users.noreply.github.com> Date: Fri, 8 Jul 2022 11:42:55 +0300 Subject: [PATCH 082/219] Update LedMatrix.cpp I have a few MAX7219 modules that use a different column assignment and the 2 already implemented orientations did not work for me. After these changes DisplayRotate 1 and DisplayRotate 3 commands do what they are supposed to do. --- lib/lib_display/LedControl/src/LedMatrix.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/lib_display/LedControl/src/LedMatrix.cpp b/lib/lib_display/LedControl/src/LedMatrix.cpp index fb23fbc26..12ce04e1d 100644 --- a/lib/lib_display/LedControl/src/LedMatrix.cpp +++ b/lib/lib_display/LedControl/src/LedMatrix.cpp @@ -330,7 +330,22 @@ void LedMatrix::refresh() } else // ORIENTATION_TURN_RIGHT || ORIENTATION_TURN_LEFT { - // not implemented yet + col = addr % modulesPerRow; + pixelRow = (addr / modulesPerRow) * 8 + ledRow; + bufPos = pixelRow * modulesPerRow + col; + + if (moduleOrientation == ORIENTATION_TURN_RIGHT) + { + // ORIENTATION_TURN_RIGHT + deviceDataBuff[addr] = buffer[bufPos]; + deviceRow = ledRow; + } + else + { + // ORIENTATION_TURN_LEFT + deviceDataBuff[maxDevices - 1 - addr] = revereBitorder(buffer[bufPos]); + deviceRow = 7 - ledRow; // upside down + } } } setRow_allDevices(deviceRow, deviceDataBuff); From 65434b208186cc96486a7f7c6f86d85065accf88 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 8 Jul 2022 12:21:27 +0200 Subject: [PATCH 083/219] Fix compile with core 2.0.4 --- boards/esp32s3boxusb.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/esp32s3boxusb.json b/boards/esp32s3boxusb.json index a7e539fe1..92385aeb5 100644 --- a/boards/esp32s3boxusb.json +++ b/boards/esp32s3boxusb.json @@ -2,7 +2,7 @@ "build": { "arduino":{ "ldscript": "esp32s3_out.ld", - "memory_type": "qspi_opi" + "memory_type": "qio_opi" }, "core": "esp32", "extra_flags": "-DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DUSE_USB_SERIAL_CONSOLE -DESP32_16M -DESP32S3", @@ -36,3 +36,4 @@ "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", "vendor": "Espressif" } + From 554a49e520dc0823a4d0f50691b797ba116c48ab Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 8 Jul 2022 19:47:05 +0200 Subject: [PATCH 084/219] rename manifest and use cdc console for s3box --- boards/{esp32s3boxusb.json => esp32s3cdc-box.json} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename boards/{esp32s3boxusb.json => esp32s3cdc-box.json} (93%) diff --git a/boards/esp32s3boxusb.json b/boards/esp32s3cdc-box.json similarity index 93% rename from boards/esp32s3boxusb.json rename to boards/esp32s3cdc-box.json index 92385aeb5..d422133d9 100644 --- a/boards/esp32s3boxusb.json +++ b/boards/esp32s3cdc-box.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DUSE_USB_SERIAL_CONSOLE -DESP32_16M -DESP32S3", + "extra_flags": "-DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -DARDUINO_USB_MSC_ON_BOOT=0 -DARDUINO_USB_DFU_ON_BOOT=0 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "dio", From 6031e5df98936e84e5547d4fb69421dde616ffcd Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 9 Jul 2022 12:27:59 +0200 Subject: [PATCH 085/219] Add better support for bistable (latching) relays Add support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/include/tasmota_template.h | 11 ++++ tasmota/my_user_config.h | 3 + tasmota/tasmota.ino | 3 + tasmota/tasmota_support/support_tasmota.ino | 73 +++++++++++++++++++-- 6 files changed, 87 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1a260d6..08c835494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## [12.0.2.3] ### Added - Support for Sonoff POWR3xxD and THR3xxD (#15856) +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 833ce1892..b0b8156cf 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -114,6 +114,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Breaking Changed diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index b833a8b0d..5920db14a 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -192,8 +192,12 @@ enum UserSelectablePins { GPIO_SM2335_CLK, GPIO_SM2335_DAT, // SM2335 PWM controller GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display + GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable GPIO_SENSOR_END }; +// Error as warning to rethink GPIO usage +static_assert(GPIO_SENSOR_END < 2000, "Too many UserSelectablePins"); + enum ProgramSelectablePins { GPIO_FIX_START = 2046, GPIO_USER, // User configurable needs to be 2047 @@ -428,6 +432,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SM2335_CLK "|" D_SENSOR_SM2335_DAT "|" D_SENSOR_DFR562_BUSY "|" D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" + D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" ; const char kSensorNamesFixed[] PROGMEM = @@ -471,6 +476,12 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays AGPIO(GPIO_REL1_INV) + MAX_RELAYS, + +#ifdef USE_BISTABLE_RELAY_SUPPORT + AGPIO(GPIO_REL1_BI) + MAX_RELAYS, // Bistable (Latching) two coil relays + AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS, +#endif + AGPIO(GPIO_LED1) + MAX_LEDS, // Leds AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2e25186a1..9e0dde67a 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -397,6 +397,9 @@ //#define MY_LANGUAGE zh_CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh_TW // Chinese (Traditional) in Taiwan +// -- Basic features ------------------------------ +#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi + // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wi-Fi Manager web GUI #define USE_IMPROV // Add support for IMPROV serial protocol as used by esp-web-tools (+2k code) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index fafd1d936..79e8aa697 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -221,6 +221,9 @@ struct TasmotaGlobal_t { power_t power; // Current copy of Settings->power power_t rel_inverted; // Relay inverted flag (1 = (0 = On, 1 = Off)) +#ifdef USE_BISTABLE_RELAY_SUPPORT + power_t rel_bistable; // Relay bistable bitmap +#endif // USE_BISTABLE_RELAY_SUPPORT power_t last_power; // Last power set state power_t blink_power; // Blink power state power_t blink_powersave; // Blink start power save state diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 3b7bce95c..d5325e46e 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -221,8 +221,7 @@ void ZeroCrossInit(uint32_t offset) { /********************************************************************************************/ -void SetLatchingRelay(power_t lpower, uint32_t state) -{ +void SetLatchingRelay(power_t lpower, uint32_t state) { // TasmotaGlobal.power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off // TasmotaGlobal.power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off // TasmotaGlobal.power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On @@ -240,8 +239,17 @@ void SetLatchingRelay(power_t lpower, uint32_t state) } } -void SetDevicePower(power_t rpower, uint32_t source) -{ +#ifdef USE_BISTABLE_RELAY_SUPPORT +void ResetBistableRelays(void) { + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (bitRead(TasmotaGlobal.rel_bistable, i)) { + DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); + } + } +} +#endif // USE_BISTABLE_RELAY_SUPPORT + +void SetDevicePower(power_t rpower, uint32_t source) { ShowSource(source); TasmotaGlobal.last_source = source; @@ -298,6 +306,26 @@ void SetDevicePower(power_t rpower, uint32_t source) { ZeroCrossMomentStart(); +#ifdef USE_BISTABLE_RELAY_SUPPORT + uint32_t port = 0; + uint32_t port_next; + for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { + power_t state = rpower &1; + + port_next = 1; // Select next relay + if (bitRead(TasmotaGlobal.rel_bistable, port)) { + if (!state) { port_next = 2; } // Skip highest relay + TasmotaGlobal.latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) + port += state; // Relay = Off, Relay = On + state = 1; // Set pulse + } + if (i < MAX_RELAYS) { + DigitalWrite(GPIO_REL1, port, bitRead(TasmotaGlobal.rel_inverted, port) ? !state : state); + } + port += port_next; // Select next relay + rpower >>= 1; + } +#else for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { @@ -305,6 +333,7 @@ void SetDevicePower(power_t rpower, uint32_t source) } rpower >>= 1; } +#endif // USE_BISTABLE_RELAY_SUPPORT ZeroCrossMomentEnd(); } @@ -1125,7 +1154,16 @@ void Every100mSeconds(void) if (TasmotaGlobal.latching_relay_pulse) { TasmotaGlobal.latching_relay_pulse--; - if (!TasmotaGlobal.latching_relay_pulse) SetLatchingRelay(0, 0); + if (!TasmotaGlobal.latching_relay_pulse) { +#ifdef ESP8266 + if (EXS_RELAY == TasmotaGlobal.module_type) { + SetLatchingRelay(0, 0); + } +#endif // ESP8266 +#ifdef USE_BISTABLE_RELAY_SUPPORT + ResetBistableRelays(); +#endif // USE_BISTABLE_RELAY_SUPPORT + } } if (TasmotaGlobal.skip_sleep) { @@ -1969,6 +2007,19 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } + +#ifdef USE_BISTABLE_RELAY_SUPPORT + else if ((mpin >= AGPIO(GPIO_REL1_BI)) && (mpin < (AGPIO(GPIO_REL1_BI) + MAX_RELAYS))) { + bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI)); + mpin -= (AGPIO(GPIO_REL1_BI) - AGPIO(GPIO_REL1)); + } + else if ((mpin >= AGPIO(GPIO_REL1_BI_INV)) && (mpin < (AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS))) { + bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI_INV)); + bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_BI_INV)); + mpin -= (AGPIO(GPIO_REL1_BI_INV) - AGPIO(GPIO_REL1)); + } +#endif // USE_BISTABLE_RELAY_SUPPORT + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { bitSet(TasmotaGlobal.led_inverted, mpin - AGPIO(GPIO_LED1_INV)); mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); @@ -2163,6 +2214,10 @@ void GpioInit(void) GpioInitPwm(); +#ifdef USE_BISTABLE_RELAY_SUPPORT + uint32_t bi_device = 0; +#endif // USE_BISTABLE_RELAY_SUPPORT + for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (PinUsed(GPIO_REL1, i)) { TasmotaGlobal.devices_present++; @@ -2171,6 +2226,14 @@ void GpioInit(void) if (i &1) { TasmotaGlobal.devices_present--; } } #endif // ESP8266 + +#ifdef USE_BISTABLE_RELAY_SUPPORT + if (bitRead(TasmotaGlobal.rel_bistable, i)) { + if (bi_device &1) { TasmotaGlobal.devices_present--; } + bi_device++; + } +#endif // USE_BISTABLE_RELAY_SUPPORT + } } From bc6d65c725cbcf00af7a2da3d5f3a05f262e45e5 Mon Sep 17 00:00:00 2001 From: David Gwynne Date: Sat, 9 Jul 2022 20:57:06 +1000 Subject: [PATCH 086/219] add `Speed2 !` to cancel pending one-shot speed setting --- tasmota/tasmota_xdrv_driver/xdrv_04_light.ino | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino index dc4b027b5..94268aa99 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light.ino @@ -3130,6 +3130,14 @@ void CmndFade(void) void CmndSpeed(void) { if (2 == XdrvMailbox.index) { + // Speed2 ! cancels use of Speed2 in the future + if ((1 == XdrvMailbox.data_len) && ('!' == XdrvMailbox.data[0])) { + Light.fade_once_enabled = false; + Light.speed_once_enabled = false; + ResponseCmndDone(); + return; + } + // Speed2 setting will be used only once, then revert to fade/speed if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 40)) { Light.fade_once_enabled = true; From c6867ddd3de2d23d7927652d5fd3661c021d3aff Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 9 Jul 2022 14:47:36 +0200 Subject: [PATCH 087/219] Revert "Add better support for bistable (latching) relays" This reverts commit 6031e5df98936e84e5547d4fb69421dde616ffcd. --- CHANGELOG.md | 1 - RELEASENOTES.md | 1 - tasmota/include/tasmota_template.h | 11 ---- tasmota/my_user_config.h | 3 - tasmota/tasmota.ino | 3 - tasmota/tasmota_support/support_tasmota.ino | 73 ++------------------- 6 files changed, 5 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c835494..0f1a260d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ All notable changes to this project will be documented in this file. ## [12.0.2.3] ### Added - Support for Sonoff POWR3xxD and THR3xxD (#15856) -- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b0b8156cf..833ce1892 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -114,7 +114,6 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) -- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Breaking Changed diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 5920db14a..b833a8b0d 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -192,12 +192,8 @@ enum UserSelectablePins { GPIO_SM2335_CLK, GPIO_SM2335_DAT, // SM2335 PWM controller GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display - GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable GPIO_SENSOR_END }; -// Error as warning to rethink GPIO usage -static_assert(GPIO_SENSOR_END < 2000, "Too many UserSelectablePins"); - enum ProgramSelectablePins { GPIO_FIX_START = 2046, GPIO_USER, // User configurable needs to be 2047 @@ -432,7 +428,6 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SM2335_CLK "|" D_SENSOR_SM2335_DAT "|" D_SENSOR_DFR562_BUSY "|" D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" - D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" ; const char kSensorNamesFixed[] PROGMEM = @@ -476,12 +471,6 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays AGPIO(GPIO_REL1_INV) + MAX_RELAYS, - -#ifdef USE_BISTABLE_RELAY_SUPPORT - AGPIO(GPIO_REL1_BI) + MAX_RELAYS, // Bistable (Latching) two coil relays - AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS, -#endif - AGPIO(GPIO_LED1) + MAX_LEDS, // Leds AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9e0dde67a..2e25186a1 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -397,9 +397,6 @@ //#define MY_LANGUAGE zh_CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh_TW // Chinese (Traditional) in Taiwan -// -- Basic features ------------------------------ -#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi - // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wi-Fi Manager web GUI #define USE_IMPROV // Add support for IMPROV serial protocol as used by esp-web-tools (+2k code) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 79e8aa697..fafd1d936 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -221,9 +221,6 @@ struct TasmotaGlobal_t { power_t power; // Current copy of Settings->power power_t rel_inverted; // Relay inverted flag (1 = (0 = On, 1 = Off)) -#ifdef USE_BISTABLE_RELAY_SUPPORT - power_t rel_bistable; // Relay bistable bitmap -#endif // USE_BISTABLE_RELAY_SUPPORT power_t last_power; // Last power set state power_t blink_power; // Blink power state power_t blink_powersave; // Blink start power save state diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index d5325e46e..3b7bce95c 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -221,7 +221,8 @@ void ZeroCrossInit(uint32_t offset) { /********************************************************************************************/ -void SetLatchingRelay(power_t lpower, uint32_t state) { +void SetLatchingRelay(power_t lpower, uint32_t state) +{ // TasmotaGlobal.power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off // TasmotaGlobal.power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off // TasmotaGlobal.power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On @@ -239,17 +240,8 @@ void SetLatchingRelay(power_t lpower, uint32_t state) { } } -#ifdef USE_BISTABLE_RELAY_SUPPORT -void ResetBistableRelays(void) { - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (bitRead(TasmotaGlobal.rel_bistable, i)) { - DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); - } - } -} -#endif // USE_BISTABLE_RELAY_SUPPORT - -void SetDevicePower(power_t rpower, uint32_t source) { +void SetDevicePower(power_t rpower, uint32_t source) +{ ShowSource(source); TasmotaGlobal.last_source = source; @@ -306,26 +298,6 @@ void SetDevicePower(power_t rpower, uint32_t source) { { ZeroCrossMomentStart(); -#ifdef USE_BISTABLE_RELAY_SUPPORT - uint32_t port = 0; - uint32_t port_next; - for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { - power_t state = rpower &1; - - port_next = 1; // Select next relay - if (bitRead(TasmotaGlobal.rel_bistable, port)) { - if (!state) { port_next = 2; } // Skip highest relay - TasmotaGlobal.latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) - port += state; // Relay = Off, Relay = On - state = 1; // Set pulse - } - if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1, port, bitRead(TasmotaGlobal.rel_inverted, port) ? !state : state); - } - port += port_next; // Select next relay - rpower >>= 1; - } -#else for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { @@ -333,7 +305,6 @@ void SetDevicePower(power_t rpower, uint32_t source) { } rpower >>= 1; } -#endif // USE_BISTABLE_RELAY_SUPPORT ZeroCrossMomentEnd(); } @@ -1154,16 +1125,7 @@ void Every100mSeconds(void) if (TasmotaGlobal.latching_relay_pulse) { TasmotaGlobal.latching_relay_pulse--; - if (!TasmotaGlobal.latching_relay_pulse) { -#ifdef ESP8266 - if (EXS_RELAY == TasmotaGlobal.module_type) { - SetLatchingRelay(0, 0); - } -#endif // ESP8266 -#ifdef USE_BISTABLE_RELAY_SUPPORT - ResetBistableRelays(); -#endif // USE_BISTABLE_RELAY_SUPPORT - } + if (!TasmotaGlobal.latching_relay_pulse) SetLatchingRelay(0, 0); } if (TasmotaGlobal.skip_sleep) { @@ -2007,19 +1969,6 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } - -#ifdef USE_BISTABLE_RELAY_SUPPORT - else if ((mpin >= AGPIO(GPIO_REL1_BI)) && (mpin < (AGPIO(GPIO_REL1_BI) + MAX_RELAYS))) { - bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI)); - mpin -= (AGPIO(GPIO_REL1_BI) - AGPIO(GPIO_REL1)); - } - else if ((mpin >= AGPIO(GPIO_REL1_BI_INV)) && (mpin < (AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS))) { - bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI_INV)); - bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_BI_INV)); - mpin -= (AGPIO(GPIO_REL1_BI_INV) - AGPIO(GPIO_REL1)); - } -#endif // USE_BISTABLE_RELAY_SUPPORT - else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { bitSet(TasmotaGlobal.led_inverted, mpin - AGPIO(GPIO_LED1_INV)); mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); @@ -2214,10 +2163,6 @@ void GpioInit(void) GpioInitPwm(); -#ifdef USE_BISTABLE_RELAY_SUPPORT - uint32_t bi_device = 0; -#endif // USE_BISTABLE_RELAY_SUPPORT - for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (PinUsed(GPIO_REL1, i)) { TasmotaGlobal.devices_present++; @@ -2226,14 +2171,6 @@ void GpioInit(void) if (i &1) { TasmotaGlobal.devices_present--; } } #endif // ESP8266 - -#ifdef USE_BISTABLE_RELAY_SUPPORT - if (bitRead(TasmotaGlobal.rel_bistable, i)) { - if (bi_device &1) { TasmotaGlobal.devices_present--; } - bi_device++; - } -#endif // USE_BISTABLE_RELAY_SUPPORT - } } From cd0f9c73608db3a51eb6d08810c28c5074e3e273 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 9 Jul 2022 15:55:27 +0200 Subject: [PATCH 088/219] Revert "Revert "Add better support for bistable (latching) relays"" This reverts commit c6867ddd3de2d23d7927652d5fd3661c021d3aff. --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/include/tasmota_template.h | 11 ++++ tasmota/my_user_config.h | 3 + tasmota/tasmota.ino | 3 + tasmota/tasmota_support/support_tasmota.ino | 73 +++++++++++++++++++-- 6 files changed, 87 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f1a260d6..08c835494 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## [12.0.2.3] ### Added - Support for Sonoff POWR3xxD and THR3xxD (#15856) +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 833ce1892..b0b8156cf 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -114,6 +114,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi ### Breaking Changed diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index b833a8b0d..5920db14a 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -192,8 +192,12 @@ enum UserSelectablePins { GPIO_SM2335_CLK, GPIO_SM2335_DAT, // SM2335 PWM controller GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display + GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable GPIO_SENSOR_END }; +// Error as warning to rethink GPIO usage +static_assert(GPIO_SENSOR_END < 2000, "Too many UserSelectablePins"); + enum ProgramSelectablePins { GPIO_FIX_START = 2046, GPIO_USER, // User configurable needs to be 2047 @@ -428,6 +432,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_SM2335_CLK "|" D_SENSOR_SM2335_DAT "|" D_SENSOR_DFR562_BUSY "|" D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" + D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" ; const char kSensorNamesFixed[] PROGMEM = @@ -471,6 +476,12 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays AGPIO(GPIO_REL1_INV) + MAX_RELAYS, + +#ifdef USE_BISTABLE_RELAY_SUPPORT + AGPIO(GPIO_REL1_BI) + MAX_RELAYS, // Bistable (Latching) two coil relays + AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS, +#endif + AGPIO(GPIO_LED1) + MAX_LEDS, // Leds AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2e25186a1..9e0dde67a 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -397,6 +397,9 @@ //#define MY_LANGUAGE zh_CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh_TW // Chinese (Traditional) in Taiwan +// -- Basic features ------------------------------ +#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi + // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wi-Fi Manager web GUI #define USE_IMPROV // Add support for IMPROV serial protocol as used by esp-web-tools (+2k code) diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index fafd1d936..79e8aa697 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -221,6 +221,9 @@ struct TasmotaGlobal_t { power_t power; // Current copy of Settings->power power_t rel_inverted; // Relay inverted flag (1 = (0 = On, 1 = Off)) +#ifdef USE_BISTABLE_RELAY_SUPPORT + power_t rel_bistable; // Relay bistable bitmap +#endif // USE_BISTABLE_RELAY_SUPPORT power_t last_power; // Last power set state power_t blink_power; // Blink power state power_t blink_powersave; // Blink start power save state diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 3b7bce95c..d5325e46e 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -221,8 +221,7 @@ void ZeroCrossInit(uint32_t offset) { /********************************************************************************************/ -void SetLatchingRelay(power_t lpower, uint32_t state) -{ +void SetLatchingRelay(power_t lpower, uint32_t state) { // TasmotaGlobal.power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off // TasmotaGlobal.power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off // TasmotaGlobal.power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On @@ -240,8 +239,17 @@ void SetLatchingRelay(power_t lpower, uint32_t state) } } -void SetDevicePower(power_t rpower, uint32_t source) -{ +#ifdef USE_BISTABLE_RELAY_SUPPORT +void ResetBistableRelays(void) { + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (bitRead(TasmotaGlobal.rel_bistable, i)) { + DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); + } + } +} +#endif // USE_BISTABLE_RELAY_SUPPORT + +void SetDevicePower(power_t rpower, uint32_t source) { ShowSource(source); TasmotaGlobal.last_source = source; @@ -298,6 +306,26 @@ void SetDevicePower(power_t rpower, uint32_t source) { ZeroCrossMomentStart(); +#ifdef USE_BISTABLE_RELAY_SUPPORT + uint32_t port = 0; + uint32_t port_next; + for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { + power_t state = rpower &1; + + port_next = 1; // Select next relay + if (bitRead(TasmotaGlobal.rel_bistable, port)) { + if (!state) { port_next = 2; } // Skip highest relay + TasmotaGlobal.latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) + port += state; // Relay = Off, Relay = On + state = 1; // Set pulse + } + if (i < MAX_RELAYS) { + DigitalWrite(GPIO_REL1, port, bitRead(TasmotaGlobal.rel_inverted, port) ? !state : state); + } + port += port_next; // Select next relay + rpower >>= 1; + } +#else for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { power_t state = rpower &1; if (i < MAX_RELAYS) { @@ -305,6 +333,7 @@ void SetDevicePower(power_t rpower, uint32_t source) } rpower >>= 1; } +#endif // USE_BISTABLE_RELAY_SUPPORT ZeroCrossMomentEnd(); } @@ -1125,7 +1154,16 @@ void Every100mSeconds(void) if (TasmotaGlobal.latching_relay_pulse) { TasmotaGlobal.latching_relay_pulse--; - if (!TasmotaGlobal.latching_relay_pulse) SetLatchingRelay(0, 0); + if (!TasmotaGlobal.latching_relay_pulse) { +#ifdef ESP8266 + if (EXS_RELAY == TasmotaGlobal.module_type) { + SetLatchingRelay(0, 0); + } +#endif // ESP8266 +#ifdef USE_BISTABLE_RELAY_SUPPORT + ResetBistableRelays(); +#endif // USE_BISTABLE_RELAY_SUPPORT + } } if (TasmotaGlobal.skip_sleep) { @@ -1969,6 +2007,19 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } + +#ifdef USE_BISTABLE_RELAY_SUPPORT + else if ((mpin >= AGPIO(GPIO_REL1_BI)) && (mpin < (AGPIO(GPIO_REL1_BI) + MAX_RELAYS))) { + bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI)); + mpin -= (AGPIO(GPIO_REL1_BI) - AGPIO(GPIO_REL1)); + } + else if ((mpin >= AGPIO(GPIO_REL1_BI_INV)) && (mpin < (AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS))) { + bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI_INV)); + bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_BI_INV)); + mpin -= (AGPIO(GPIO_REL1_BI_INV) - AGPIO(GPIO_REL1)); + } +#endif // USE_BISTABLE_RELAY_SUPPORT + else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { bitSet(TasmotaGlobal.led_inverted, mpin - AGPIO(GPIO_LED1_INV)); mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); @@ -2163,6 +2214,10 @@ void GpioInit(void) GpioInitPwm(); +#ifdef USE_BISTABLE_RELAY_SUPPORT + uint32_t bi_device = 0; +#endif // USE_BISTABLE_RELAY_SUPPORT + for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (PinUsed(GPIO_REL1, i)) { TasmotaGlobal.devices_present++; @@ -2171,6 +2226,14 @@ void GpioInit(void) if (i &1) { TasmotaGlobal.devices_present--; } } #endif // ESP8266 + +#ifdef USE_BISTABLE_RELAY_SUPPORT + if (bitRead(TasmotaGlobal.rel_bistable, i)) { + if (bi_device &1) { TasmotaGlobal.devices_present--; } + bi_device++; + } +#endif // USE_BISTABLE_RELAY_SUPPORT + } } From c9d289822ca15d9cd2bc6f8f075d6a258d74ef16 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 9 Jul 2022 16:26:13 +0200 Subject: [PATCH 089/219] Fix bistable relays and SO63 --- tasmota/include/tasmota_template.h | 2 -- tasmota/tasmota_support/support_tasmota.ino | 27 +++++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 5920db14a..c739c4b7e 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -476,12 +476,10 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays AGPIO(GPIO_REL1_INV) + MAX_RELAYS, - #ifdef USE_BISTABLE_RELAY_SUPPORT AGPIO(GPIO_REL1_BI) + MAX_RELAYS, // Bistable (Latching) two coil relays AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS, #endif - AGPIO(GPIO_LED1) + MAX_LEDS, // Leds AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index d5325e46e..7db38c451 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -428,6 +428,26 @@ void SetPowerOnState(void) } // Issue #526 and #909 +#ifdef USE_BISTABLE_RELAY_SUPPORT + uint32_t port = 0; + for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { +#ifdef ESP8266 + if (!Settings->flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 + if ((port < MAX_RELAYS) && PinUsed(GPIO_REL1, port)) { + if (bitRead(TasmotaGlobal.rel_bistable, port)) { + port++; // Skip both bistable relays as always 0 + } else { + bitWrite(TasmotaGlobal.power, i, digitalRead(Pin(GPIO_REL1, port)) ^ bitRead(TasmotaGlobal.rel_inverted, port)); + } + } + port++; + } +#endif // ESP8266 + if (bitRead(TasmotaGlobal.power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings->poweronstate)) { + SetPulseTimer(i % MAX_PULSETIMERS, Settings->pulse_timer[i % MAX_PULSETIMERS]); + } + } +#else // No USE_BISTABLE_RELAY_SUPPORT for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { #ifdef ESP8266 if (!Settings->flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 @@ -440,6 +460,8 @@ void SetPowerOnState(void) SetPulseTimer(i % MAX_PULSETIMERS, Settings->pulse_timer[i % MAX_PULSETIMERS]); } } +#endif // USE_BISTABLE_RELAY_SUPPORT + TasmotaGlobal.blink_powersave = TasmotaGlobal.power; #ifdef USE_RULES RulesEvery50ms(); @@ -2007,7 +2029,6 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } - #ifdef USE_BISTABLE_RELAY_SUPPORT else if ((mpin >= AGPIO(GPIO_REL1_BI)) && (mpin < (AGPIO(GPIO_REL1_BI) + MAX_RELAYS))) { bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI)); @@ -2019,7 +2040,6 @@ void GpioInit(void) mpin -= (AGPIO(GPIO_REL1_BI_INV) - AGPIO(GPIO_REL1)); } #endif // USE_BISTABLE_RELAY_SUPPORT - else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { bitSet(TasmotaGlobal.led_inverted, mpin - AGPIO(GPIO_LED1_INV)); mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); @@ -2217,7 +2237,6 @@ void GpioInit(void) #ifdef USE_BISTABLE_RELAY_SUPPORT uint32_t bi_device = 0; #endif // USE_BISTABLE_RELAY_SUPPORT - for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (PinUsed(GPIO_REL1, i)) { TasmotaGlobal.devices_present++; @@ -2226,14 +2245,12 @@ void GpioInit(void) if (i &1) { TasmotaGlobal.devices_present--; } } #endif // ESP8266 - #ifdef USE_BISTABLE_RELAY_SUPPORT if (bitRead(TasmotaGlobal.rel_bistable, i)) { if (bi_device &1) { TasmotaGlobal.devices_present--; } bi_device++; } #endif // USE_BISTABLE_RELAY_SUPPORT - } } From 485328b94d85c069e166123a84adc9b4399b9a1e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 10 Jul 2022 11:38:06 +0200 Subject: [PATCH 090/219] Update sonoff POWR320D template Update sonoff POWR320D template with bistable relay --- tasmota/include/tasmota_configurations.h | 35 ++++++++++--------- tasmota/my_user_config.h | 3 -- .../xdrv_87_tm1621_sonoff.ino | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index 1eb21552f..49908f033 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -919,11 +919,11 @@ #undef CODE_IMAGE_STR #define CODE_IMAGE_STR "mini-custom" -#undef FIRMWARE_LITE // Disable tasmota-lite with no sensors -#undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled -#undef FIRMWARE_KNX_NO_EMULATION // Disable tasmota-knx with KNX but without Emulation -#undef FIRMWARE_DISPLAYS // Disable tasmota-display with display drivers enabled -#undef FIRMWARE_IR // Disable tasmota-ir with IR full protocols activated +#undef FIRMWARE_LITE // Disable tasmota-lite with no sensors +#undef FIRMWARE_SENSORS // Disable tasmota-sensors with useful sensors enabled +#undef FIRMWARE_KNX_NO_EMULATION // Disable tasmota-knx with KNX but without Emulation +#undef FIRMWARE_DISPLAYS // Disable tasmota-display with display drivers enabled +#undef FIRMWARE_IR // Disable tasmota-ir with IR full protocols activated #undef FIRMWARE_WEBCAM #undef FIRMWARE_ODROID_GO #undef FIRMWARE_M5STACK_CORE2 @@ -953,12 +953,12 @@ // IR_SEND has two mode: minimal or full (USE_IR_REMOTE_FULL) #ifdef USE_IR_REMOTE_FULL // Enable all protocols except PRONTO - #ifndef _IR_ENABLE_DEFAULT_ // it is possible to define this value previously in config - #define _IR_ENABLE_DEFAULT_ true // Enable all protocols except exluded below + #ifndef _IR_ENABLE_DEFAULT_ // it is possible to define this value previously in config + #define _IR_ENABLE_DEFAULT_ true // Enable all protocols except exluded below #endif // PRONTO protocol cannot be supported because it requires specific APIs which are not supported in Tasmota - #define DECODE_PRONTO false // Exclude PRONTO protocol - #define SEND_PRONTO false // Exclude PRONTO protocol + #define DECODE_PRONTO false // Exclude PRONTO protocol + #define SEND_PRONTO false // Exclude PRONTO protocol #else #define _IR_ENABLE_DEFAULT_ false // disable all protocols by default // below are the default IR protocols @@ -982,7 +982,7 @@ \*********************************************************************************************/ #ifndef ESP8266_1M - #ifndef FIRMWARE_MINIMAL // there might be a ESP32-minimal + #ifndef FIRMWARE_MINIMAL // There might be a ESP32-minimal #define USE_UFILESYS #define GUI_TRASH_FILE #define GUI_EDIT_FILE @@ -1035,17 +1035,20 @@ #endif #ifdef USE_SCRIPT -#define USE_UNISHOX_COMPRESSION // Add support for string compression +#define USE_UNISHOX_COMPRESSION // Add support for string compression #endif #ifdef USE_ZIGBEE -#define USE_UNISHOX_COMPRESSION // Add support for string compression +#define USE_UNISHOX_COMPRESSION // Add support for string compression #endif #ifdef USE_EMULATION_HUE -#define USE_UNISHOX_COMPRESSION // Add support for string compression +#define USE_UNISHOX_COMPRESSION // Add support for string compression #endif -#if defined(USE_MQTT_TLS) // Enable TLS if required: - #define USE_TLS // flag indicates we need to include TLS code -#endif // USE_MQTT_TLS +#if defined(USE_MQTT_TLS) // Enable TLS if required: + #define USE_TLS // flag indicates we need to include TLS code +#endif // USE_MQTT_TLS + +// -- Basic features ------------------------------ +#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi #endif // _TASMOTA_CONFIGURATIONS_H_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9e0dde67a..2e25186a1 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -397,9 +397,6 @@ //#define MY_LANGUAGE zh_CN // Chinese (Simplified) in China //#define MY_LANGUAGE zh_TW // Chinese (Traditional) in Taiwan -// -- Basic features ------------------------------ -#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi - // -- Wifi Config tools --------------------------- #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 13 as used by Wi-Fi Manager web GUI #define USE_IMPROV // Add support for IMPROV serial protocol as used by esp-web-tools (+2k code) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index 17a1d486b..c4a809afa 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -11,7 +11,7 @@ * Sonoff POWR3xxD and THR3xxD LCD support * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff POWR320D","GPIO":[32,0,224,0,225,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff POWR320D","GPIO":[32,0,9312,0,9313,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ From 8283cee8230606d2e9374184ce91c582c39e6a2f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:07:58 +0200 Subject: [PATCH 091/219] Fix bistable relay support --- tasmota/include/tasmota_configurations.h | 3 -- tasmota/include/tasmota_template.h | 6 ++-- tasmota/tasmota.ino | 2 -- tasmota/tasmota_support/support_tasmota.ino | 35 --------------------- 4 files changed, 2 insertions(+), 44 deletions(-) diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index 49908f033..cf99d8aa7 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -1048,7 +1048,4 @@ #define USE_TLS // flag indicates we need to include TLS code #endif // USE_MQTT_TLS -// -- Basic features ------------------------------ -#define USE_BISTABLE_RELAY_SUPPORT // Add support for bistable (latching) relays using GPIO Relay_b or Relay_bi - #endif // _TASMOTA_CONFIGURATIONS_H_ diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index c739c4b7e..58954bd07 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -476,18 +476,16 @@ const uint16_t kGpioNiceList[] PROGMEM = { #endif AGPIO(GPIO_REL1) + MAX_RELAYS, // Relays AGPIO(GPIO_REL1_INV) + MAX_RELAYS, -#ifdef USE_BISTABLE_RELAY_SUPPORT AGPIO(GPIO_REL1_BI) + MAX_RELAYS, // Bistable (Latching) two coil relays AGPIO(GPIO_REL1_BI_INV) + MAX_RELAYS, -#endif AGPIO(GPIO_LED1) + MAX_LEDS, // Leds AGPIO(GPIO_LED1_INV) + MAX_LEDS, #ifdef USE_COUNTER AGPIO(GPIO_CNTR1) + MAX_COUNTERS, // Counters AGPIO(GPIO_CNTR1_NP) + MAX_COUNTERS, #endif - AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White - AGPIO(GPIO_PWM1_INV) + MAX_PWMS, // or extended PWM for ESP32 + AGPIO(GPIO_PWM1) + MAX_PWMS, // RGB Red or C Cold White + AGPIO(GPIO_PWM1_INV) + MAX_PWMS, // or extended PWM for ESP32 #ifdef USE_BUZZER AGPIO(GPIO_BUZZER), // Buzzer AGPIO(GPIO_BUZZER_INV), // Inverted buzzer diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 79e8aa697..4e72a5b0c 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -221,9 +221,7 @@ struct TasmotaGlobal_t { power_t power; // Current copy of Settings->power power_t rel_inverted; // Relay inverted flag (1 = (0 = On, 1 = Off)) -#ifdef USE_BISTABLE_RELAY_SUPPORT power_t rel_bistable; // Relay bistable bitmap -#endif // USE_BISTABLE_RELAY_SUPPORT power_t last_power; // Last power set state power_t blink_power; // Blink power state power_t blink_powersave; // Blink start power save state diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 7db38c451..0e735a11f 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -239,7 +239,6 @@ void SetLatchingRelay(power_t lpower, uint32_t state) { } } -#ifdef USE_BISTABLE_RELAY_SUPPORT void ResetBistableRelays(void) { for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (bitRead(TasmotaGlobal.rel_bistable, i)) { @@ -247,7 +246,6 @@ void ResetBistableRelays(void) { } } } -#endif // USE_BISTABLE_RELAY_SUPPORT void SetDevicePower(power_t rpower, uint32_t source) { ShowSource(source); @@ -306,7 +304,6 @@ void SetDevicePower(power_t rpower, uint32_t source) { { ZeroCrossMomentStart(); -#ifdef USE_BISTABLE_RELAY_SUPPORT uint32_t port = 0; uint32_t port_next; for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { @@ -325,15 +322,6 @@ void SetDevicePower(power_t rpower, uint32_t source) { port += port_next; // Select next relay rpower >>= 1; } -#else - for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { - power_t state = rpower &1; - if (i < MAX_RELAYS) { - DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? !state : state); - } - rpower >>= 1; - } -#endif // USE_BISTABLE_RELAY_SUPPORT ZeroCrossMomentEnd(); } @@ -428,7 +416,6 @@ void SetPowerOnState(void) } // Issue #526 and #909 -#ifdef USE_BISTABLE_RELAY_SUPPORT uint32_t port = 0; for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { #ifdef ESP8266 @@ -447,20 +434,6 @@ void SetPowerOnState(void) SetPulseTimer(i % MAX_PULSETIMERS, Settings->pulse_timer[i % MAX_PULSETIMERS]); } } -#else // No USE_BISTABLE_RELAY_SUPPORT - for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { -#ifdef ESP8266 - if (!Settings->flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663 - if ((i < MAX_RELAYS) && PinUsed(GPIO_REL1, i)) { - bitWrite(TasmotaGlobal.power, i, digitalRead(Pin(GPIO_REL1, i)) ^ bitRead(TasmotaGlobal.rel_inverted, i)); - } - } -#endif // ESP8266 - if (bitRead(TasmotaGlobal.power, i) || (POWER_ALL_OFF_PULSETIME_ON == Settings->poweronstate)) { - SetPulseTimer(i % MAX_PULSETIMERS, Settings->pulse_timer[i % MAX_PULSETIMERS]); - } - } -#endif // USE_BISTABLE_RELAY_SUPPORT TasmotaGlobal.blink_powersave = TasmotaGlobal.power; #ifdef USE_RULES @@ -1182,9 +1155,7 @@ void Every100mSeconds(void) SetLatchingRelay(0, 0); } #endif // ESP8266 -#ifdef USE_BISTABLE_RELAY_SUPPORT ResetBistableRelays(); -#endif // USE_BISTABLE_RELAY_SUPPORT } } @@ -2029,7 +2000,6 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_INV)); mpin -= (AGPIO(GPIO_REL1_INV) - AGPIO(GPIO_REL1)); } -#ifdef USE_BISTABLE_RELAY_SUPPORT else if ((mpin >= AGPIO(GPIO_REL1_BI)) && (mpin < (AGPIO(GPIO_REL1_BI) + MAX_RELAYS))) { bitSet(TasmotaGlobal.rel_bistable, mpin - AGPIO(GPIO_REL1_BI)); mpin -= (AGPIO(GPIO_REL1_BI) - AGPIO(GPIO_REL1)); @@ -2039,7 +2009,6 @@ void GpioInit(void) bitSet(TasmotaGlobal.rel_inverted, mpin - AGPIO(GPIO_REL1_BI_INV)); mpin -= (AGPIO(GPIO_REL1_BI_INV) - AGPIO(GPIO_REL1)); } -#endif // USE_BISTABLE_RELAY_SUPPORT else if ((mpin >= AGPIO(GPIO_LED1_INV)) && (mpin < (AGPIO(GPIO_LED1_INV) + MAX_LEDS))) { bitSet(TasmotaGlobal.led_inverted, mpin - AGPIO(GPIO_LED1_INV)); mpin -= (AGPIO(GPIO_LED1_INV) - AGPIO(GPIO_LED1)); @@ -2234,9 +2203,7 @@ void GpioInit(void) GpioInitPwm(); -#ifdef USE_BISTABLE_RELAY_SUPPORT uint32_t bi_device = 0; -#endif // USE_BISTABLE_RELAY_SUPPORT for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (PinUsed(GPIO_REL1, i)) { TasmotaGlobal.devices_present++; @@ -2245,12 +2212,10 @@ void GpioInit(void) if (i &1) { TasmotaGlobal.devices_present--; } } #endif // ESP8266 -#ifdef USE_BISTABLE_RELAY_SUPPORT if (bitRead(TasmotaGlobal.rel_bistable, i)) { if (bi_device &1) { TasmotaGlobal.devices_present--; } bi_device++; } -#endif // USE_BISTABLE_RELAY_SUPPORT } } From 4048aef96b65a82880d5759ec647e9708891cb60 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 10 Jul 2022 12:36:34 +0200 Subject: [PATCH 092/219] Add Sonoff THR320D template and update THR316D template --- tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index c4a809afa..3231ad74e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -12,7 +12,8 @@ * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff POWR320D","GPIO":[32,0,9312,0,9313,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR320D","GPIO":[32,0,0,0,226,9280,0,0,0,321,0,576,320,9184,9216,0,0,0,0,9248,0,9312,9313,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ #define XDRV_87 87 From 2fe1d139e3507b4f756928a68b44f9c057a69dae Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 10 Jul 2022 14:24:35 +0200 Subject: [PATCH 093/219] Fix Sonoff THR3xx templates --- tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index 3231ad74e..bfb093d1e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -12,8 +12,8 @@ * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff POWR320D","GPIO":[32,0,9312,0,9313,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,0,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff THR320D","GPIO":[32,0,0,0,226,9280,0,0,0,321,0,576,320,9184,9216,0,0,0,0,9248,0,9312,9313,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff THR320D","GPIO":[32,0,0,0,226,9280,0,0,0,321,0,576,320,9184,9216,9312,0,0,9313,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ #define XDRV_87 87 From d5ed1abf070a497e8b2e32415bdf21beb3723874 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sun, 10 Jul 2022 19:34:04 +0200 Subject: [PATCH 094/219] Berry add tasmota.remove_fast_loop() --- lib/libesp32/berry/generate/be_const_strtab.h | 1 + .../berry/generate/be_const_strtab_def.h | 5 +- .../generate/be_fixed_be_class_tasmota.h | 169 +++++++++--------- .../berry_tasmota/src/be_tasmota_lib.c | 42 +++++ .../berry_tasmota/src/embedded/Tasmota.be | 8 + 5 files changed, 139 insertions(+), 86 deletions(-) diff --git a/lib/libesp32/berry/generate/be_const_strtab.h b/lib/libesp32/berry/generate/be_const_strtab.h index 27596f14b..ebb7ae44e 100644 --- a/lib/libesp32/berry/generate/be_const_strtab.h +++ b/lib/libesp32/berry/generate/be_const_strtab.h @@ -673,6 +673,7 @@ extern const bcstring be_const_str_remove; extern const bcstring be_const_str_remove_cmd; extern const bcstring be_const_str_remove_cron; extern const bcstring be_const_str_remove_driver; +extern const bcstring be_const_str_remove_fast_loop; extern const bcstring be_const_str_remove_light; extern const bcstring be_const_str_remove_rule; extern const bcstring be_const_str_remove_timer; diff --git a/lib/libesp32/berry/generate/be_const_strtab_def.h b/lib/libesp32/berry/generate/be_const_strtab_def.h index 1122a3681..1ac8d0010 100644 --- a/lib/libesp32/berry/generate/be_const_strtab_def.h +++ b/lib/libesp32/berry/generate/be_const_strtab_def.h @@ -344,7 +344,7 @@ be_define_const_str(create_matrix, "create_matrix", 3528185923u, 0, 13, &be_cons be_define_const_str(create_segment, "create_segment", 3863522719u, 0, 14, NULL); be_define_const_str(ct, "ct", 1261010898u, 0, 2, &be_const_str_has_factory); be_define_const_str(ctor, "ctor", 375399343u, 0, 4, &be_const_str_math); -be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, NULL); +be_define_const_str(ctypes_bytes, "ctypes_bytes", 3879019703u, 0, 12, &be_const_str_remove_fast_loop); be_define_const_str(ctypes_bytes_dyn, "ctypes_bytes_dyn", 915205307u, 0, 16, &be_const_str_energy_struct); be_define_const_str(dac_voltage, "dac_voltage", 1552257222u, 0, 11, &be_const_str_reverse_gamma10); be_define_const_str(data, "data", 3631407781u, 0, 4, &be_const_str_display); @@ -665,6 +665,7 @@ be_define_const_str(remove, "remove", 3683784189u, 0, 6, NULL); be_define_const_str(remove_cmd, "remove_cmd", 3832315702u, 0, 10, NULL); be_define_const_str(remove_cron, "remove_cron", 2914538962u, 0, 11, NULL); be_define_const_str(remove_driver, "remove_driver", 1030243768u, 0, 13, &be_const_str_settings); +be_define_const_str(remove_fast_loop, "remove_fast_loop", 3811152503u, 0, 16, NULL); be_define_const_str(remove_light, "remove_light", 1783624394u, 0, 12, &be_const_str_run); be_define_const_str(remove_rule, "remove_rule", 3456211328u, 0, 11, NULL); be_define_const_str(remove_timer, "remove_timer", 4141472215u, 0, 12, NULL); @@ -1896,6 +1897,6 @@ static const bstring* const m_string_table[] = { static const struct bconststrtab m_const_string_table = { .size = 432, - .count = 887, + .count = 888, .table = m_string_table }; diff --git a/lib/libesp32/berry/generate/be_fixed_be_class_tasmota.h b/lib/libesp32/berry/generate/be_fixed_be_class_tasmota.h index e3f76732d..bd54c6149 100644 --- a/lib/libesp32/berry/generate/be_fixed_be_class_tasmota.h +++ b/lib/libesp32/berry/generate/be_fixed_be_class_tasmota.h @@ -1,98 +1,99 @@ #include "be_constobj.h" static be_define_const_map_slots(be_class_tasmota_map) { + { be_const_key(run_deferred, -1), be_const_closure(Tasmota_run_deferred_closure) }, + { be_const_key(_drivers, 42), be_const_var(0) }, { be_const_key(gc, -1), be_const_closure(Tasmota_gc_closure) }, - { be_const_key(gen_cb, -1), be_const_closure(Tasmota_gen_cb_closure) }, - { be_const_key(yield, 20), be_const_func(l_yield) }, - { be_const_key(log, 21), be_const_func(l_logInfo) }, - { be_const_key(find_op, -1), be_const_closure(Tasmota_find_op_closure) }, - { be_const_key(i2c_enabled, -1), be_const_func(l_i2cenabled) }, - { be_const_key(response_append, -1), be_const_func(l_respAppend) }, - { be_const_key(web_send, -1), be_const_func(l_webSend) }, - { be_const_key(hs2rgb, -1), be_const_closure(Tasmota_hs2rgb_closure) }, - { be_const_key(resolvecmnd, 1), be_const_func(l_resolveCmnd) }, - { be_const_key(load, -1), be_const_closure(Tasmota_load_closure) }, - { be_const_key(_fl, -1), be_const_var(0) }, - { be_const_key(get_power, -1), be_const_func(l_getpower) }, - { be_const_key(add_rule, 32), be_const_closure(Tasmota_add_rule_closure) }, - { be_const_key(eth, 30), be_const_func(l_eth) }, - { be_const_key(time_dump, -1), be_const_func(l_time_dump) }, - { be_const_key(exec_rules, -1), be_const_closure(Tasmota_exec_rules_closure) }, - { be_const_key(chars_in_string, -1), be_const_closure(Tasmota_chars_in_string_closure) }, - { be_const_key(cmd, -1), be_const_closure(Tasmota_cmd_closure) }, - { be_const_key(_rules, -1), be_const_var(1) }, - { be_const_key(exec_tele, -1), be_const_closure(Tasmota_exec_tele_closure) }, - { be_const_key(get_option, -1), be_const_func(l_getoption) }, - { be_const_key(get_switches, 46), be_const_func(l_getswitch) }, - { be_const_key(global, -1), be_const_var(2) }, - { be_const_key(arch, -1), be_const_func(l_arch) }, - { be_const_key(wire1, -1), be_const_var(3) }, - { be_const_key(scale_uint, 15), be_const_func(l_scaleuint) }, - { be_const_key(millis, 8), be_const_func(l_millis) }, - { be_const_key(resp_cmnd_error, 64), be_const_func(l_respCmndError) }, - { be_const_key(publish, -1), be_const_func(l_publish) }, - { be_const_key(_settings_def, 75), be_const_comptr(&be_tasmota_settings_struct) }, - { be_const_key(init, -1), be_const_closure(Tasmota_init_closure) }, - { be_const_key(_settings_ptr, -1), be_const_comptr(&Settings) }, - { be_const_key(publish_rule, 57), be_const_func(l_publish_rule) }, - { be_const_key(remove_driver, 49), be_const_closure(Tasmota_remove_driver_closure) }, - { be_const_key(run_deferred, 25), be_const_closure(Tasmota_run_deferred_closure) }, - { be_const_key(read_sensors, 82), be_const_func(l_read_sensors) }, - { be_const_key(_timers, -1), be_const_var(4) }, - { be_const_key(set_light, -1), be_const_closure(Tasmota_set_light_closure) }, - { be_const_key(memory, 31), be_const_func(l_memory) }, - { be_const_key(wire_scan, -1), be_const_closure(Tasmota_wire_scan_closure) }, - { be_const_key(get_light, -1), be_const_closure(Tasmota_get_light_closure) }, - { be_const_key(_cmd, 76), be_const_func(l_cmd) }, - { be_const_key(exec_cmd, -1), be_const_closure(Tasmota_exec_cmd_closure) }, - { be_const_key(find_key_i, -1), be_const_closure(Tasmota_find_key_i_closure) }, - { be_const_key(wifi, -1), be_const_func(l_wifi) }, - { be_const_key(rtc, -1), be_const_func(l_rtc) }, - { be_const_key(time_str, -1), be_const_closure(Tasmota_time_str_closure) }, - { be_const_key(resp_cmnd, 17), be_const_func(l_respCmnd) }, - { be_const_key(add_cmd, -1), be_const_closure(Tasmota_add_cmd_closure) }, - { be_const_key(get_switch, -1), be_const_func(l_getswitch) }, - { be_const_key(save, -1), be_const_func(l_save) }, - { be_const_key(wire2, -1), be_const_var(5) }, - { be_const_key(run_cron, 0), be_const_closure(Tasmota_run_cron_closure) }, - { be_const_key(delay, -1), be_const_func(l_delay) }, - { be_const_key(set_timer, -1), be_const_closure(Tasmota_set_timer_closure) }, - { be_const_key(time_reached, -1), be_const_func(l_timereached) }, - { be_const_key(set_power, -1), be_const_func(l_setpower) }, - { be_const_key(_global_addr, -1), be_const_comptr(&TasmotaGlobal) }, - { be_const_key(_ccmd, -1), be_const_var(6) }, - { be_const_key(next_cron, 16), be_const_closure(Tasmota_next_cron_closure) }, - { be_const_key(cmd_res, -1), be_const_var(7) }, - { be_const_key(strftime, 80), be_const_func(l_strftime) }, - { be_const_key(event, -1), be_const_closure(Tasmota_event_closure) }, - { be_const_key(settings, -1), be_const_var(8) }, - { be_const_key(strptime, -1), be_const_func(l_strptime) }, - { be_const_key(resp_cmnd_done, 51), be_const_func(l_respCmndDone) }, + { be_const_key(strftime, -1), be_const_func(l_strftime) }, + { be_const_key(web_send_decimal, 49), be_const_func(l_webSendDecimal) }, + { be_const_key(yield, -1), be_const_func(l_yield) }, { be_const_key(remove_cmd, -1), be_const_closure(Tasmota_remove_cmd_closure) }, - { be_const_key(try_rule, -1), be_const_closure(Tasmota_try_rule_closure) }, - { be_const_key(resp_cmnd_str, 59), be_const_func(l_respCmndStr) }, - { be_const_key(_drivers, -1), be_const_var(9) }, - { be_const_key(_crons, 56), be_const_var(10) }, - { be_const_key(check_not_method, 62), be_const_closure(Tasmota_check_not_method_closure) }, - { be_const_key(remove_cron, 65), be_const_closure(Tasmota_remove_cron_closure) }, - { be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) }, - { be_const_key(remove_timer, -1), be_const_closure(Tasmota_remove_timer_closure) }, - { be_const_key(resp_cmnd_failed, -1), be_const_func(l_respCmndFailed) }, - { be_const_key(add_driver, -1), be_const_closure(Tasmota_add_driver_closure) }, - { be_const_key(_debug_present, -1), be_const_var(11) }, - { be_const_key(wd, 14), be_const_var(12) }, - { be_const_key(_global_def, -1), be_const_comptr(&be_tasmota_global_struct) }, - { be_const_key(web_send_decimal, -1), be_const_func(l_webSendDecimal) }, - { be_const_key(publish_result, -1), be_const_func(l_publish_result) }, + { be_const_key(add_rule, -1), be_const_closure(Tasmota_add_rule_closure) }, + { be_const_key(find_op, 50), be_const_closure(Tasmota_find_op_closure) }, + { be_const_key(try_rule, 68), be_const_closure(Tasmota_try_rule_closure) }, + { be_const_key(cmd_res, -1), be_const_var(1) }, + { be_const_key(eth, -1), be_const_func(l_eth) }, + { be_const_key(time_dump, -1), be_const_func(l_time_dump) }, + { be_const_key(chars_in_string, -1), be_const_closure(Tasmota_chars_in_string_closure) }, + { be_const_key(global, 38), be_const_var(2) }, + { be_const_key(millis, 19), be_const_func(l_millis) }, + { be_const_key(settings, -1), be_const_var(3) }, { be_const_key(fast_loop, -1), be_const_closure(Tasmota_fast_loop_closure) }, + { be_const_key(set_light, -1), be_const_closure(Tasmota_set_light_closure) }, + { be_const_key(remove_fast_loop, -1), be_const_closure(Tasmota_remove_fast_loop_closure) }, + { be_const_key(get_switch, 13), be_const_func(l_getswitch) }, + { be_const_key(_debug_present, 58), be_const_var(4) }, + { be_const_key(_cmd, -1), be_const_func(l_cmd) }, + { be_const_key(remove_timer, 2), be_const_closure(Tasmota_remove_timer_closure) }, + { be_const_key(publish, -1), be_const_func(l_publish) }, + { be_const_key(arch, -1), be_const_func(l_arch) }, + { be_const_key(_settings_def, -1), be_const_comptr(&be_tasmota_settings_struct) }, + { be_const_key(add_driver, -1), be_const_closure(Tasmota_add_driver_closure) }, + { be_const_key(_timers, 11), be_const_var(5) }, { be_const_key(add_cron, -1), be_const_closure(Tasmota_add_cron_closure) }, - { be_const_key(add_fast_loop, -1), be_const_closure(Tasmota_add_fast_loop_closure) }, - { be_const_key(remove_rule, -1), be_const_closure(Tasmota_remove_rule_closure) }, + { be_const_key(scale_uint, -1), be_const_func(l_scaleuint) }, + { be_const_key(check_not_method, -1), be_const_closure(Tasmota_check_not_method_closure) }, + { be_const_key(web_send, 63), be_const_func(l_webSend) }, + { be_const_key(get_option, -1), be_const_func(l_getoption) }, + { be_const_key(time_str, -1), be_const_closure(Tasmota_time_str_closure) }, + { be_const_key(response_append, 26), be_const_func(l_respAppend) }, + { be_const_key(_fl, -1), be_const_var(6) }, + { be_const_key(get_free_heap, -1), be_const_func(l_getFreeHeap) }, + { be_const_key(_settings_ptr, -1), be_const_comptr(&Settings) }, + { be_const_key(exec_cmd, -1), be_const_closure(Tasmota_exec_cmd_closure) }, + { be_const_key(rtc, 18), be_const_func(l_rtc) }, + { be_const_key(read_sensors, 52), be_const_func(l_read_sensors) }, + { be_const_key(exec_tele, -1), be_const_closure(Tasmota_exec_tele_closure) }, + { be_const_key(_crons, 51), be_const_var(7) }, + { be_const_key(publish_result, 34), be_const_func(l_publish_result) }, + { be_const_key(hs2rgb, -1), be_const_closure(Tasmota_hs2rgb_closure) }, + { be_const_key(memory, -1), be_const_func(l_memory) }, + { be_const_key(wire1, -1), be_const_var(8) }, + { be_const_key(remove_driver, -1), be_const_closure(Tasmota_remove_driver_closure) }, + { be_const_key(get_light, -1), be_const_closure(Tasmota_get_light_closure) }, + { be_const_key(wifi, -1), be_const_func(l_wifi) }, + { be_const_key(run_cron, -1), be_const_closure(Tasmota_run_cron_closure) }, + { be_const_key(load, -1), be_const_closure(Tasmota_load_closure) }, + { be_const_key(wire_scan, 72), be_const_closure(Tasmota_wire_scan_closure) }, + { be_const_key(find_key_i, 76), be_const_closure(Tasmota_find_key_i_closure) }, + { be_const_key(resp_cmnd_error, -1), be_const_func(l_respCmndError) }, + { be_const_key(get_power, 69), be_const_func(l_getpower) }, + { be_const_key(_global_def, 56), be_const_comptr(&be_tasmota_global_struct) }, + { be_const_key(_ccmd, 60), be_const_var(9) }, + { be_const_key(cmd, 12), be_const_closure(Tasmota_cmd_closure) }, + { be_const_key(set_timer, -1), be_const_closure(Tasmota_set_timer_closure) }, + { be_const_key(resolvecmnd, 9), be_const_func(l_resolveCmnd) }, + { be_const_key(wire2, 5), be_const_var(10) }, + { be_const_key(get_switches, -1), be_const_func(l_getswitch) }, + { be_const_key(remove_rule, 53), be_const_closure(Tasmota_remove_rule_closure) }, + { be_const_key(next_cron, -1), be_const_closure(Tasmota_next_cron_closure) }, + { be_const_key(resp_cmnd, -1), be_const_func(l_respCmnd) }, + { be_const_key(publish_rule, -1), be_const_func(l_publish_rule) }, + { be_const_key(set_power, 70), be_const_func(l_setpower) }, + { be_const_key(strptime, -1), be_const_func(l_strptime) }, + { be_const_key(time_reached, -1), be_const_func(l_timereached) }, + { be_const_key(gen_cb, -1), be_const_closure(Tasmota_gen_cb_closure) }, + { be_const_key(delay, -1), be_const_func(l_delay) }, + { be_const_key(_rules, 33), be_const_var(11) }, + { be_const_key(resp_cmnd_failed, -1), be_const_func(l_respCmndFailed) }, + { be_const_key(init, -1), be_const_closure(Tasmota_init_closure) }, + { be_const_key(wd, -1), be_const_var(12) }, + { be_const_key(remove_cron, -1), be_const_closure(Tasmota_remove_cron_closure) }, + { be_const_key(log, -1), be_const_func(l_logInfo) }, + { be_const_key(add_cmd, -1), be_const_closure(Tasmota_add_cmd_closure) }, + { be_const_key(save, -1), be_const_func(l_save) }, + { be_const_key(_global_addr, 78), be_const_comptr(&TasmotaGlobal) }, + { be_const_key(add_fast_loop, 77), be_const_closure(Tasmota_add_fast_loop_closure) }, + { be_const_key(resp_cmnd_done, 47), be_const_func(l_respCmndDone) }, + { be_const_key(exec_rules, -1), be_const_closure(Tasmota_exec_rules_closure) }, + { be_const_key(i2c_enabled, -1), be_const_func(l_i2cenabled) }, + { be_const_key(resp_cmnd_str, 17), be_const_func(l_respCmndStr) }, + { be_const_key(event, -1), be_const_closure(Tasmota_event_closure) }, }; static be_define_const_map( be_class_tasmota_map, - 87 + 88 ); BE_EXPORT_VARIABLE be_define_const_class( diff --git a/lib/libesp32/berry_tasmota/src/be_tasmota_lib.c b/lib/libesp32/berry_tasmota/src/be_tasmota_lib.c index ea32d3c36..9a2091515 100644 --- a/lib/libesp32/berry_tasmota/src/be_tasmota_lib.c +++ b/lib/libesp32/berry_tasmota/src/be_tasmota_lib.c @@ -735,6 +735,47 @@ be_local_closure(Tasmota_add_fast_loop, /* name */ ); /*******************************************************************/ +/******************************************************************** +** Solidified function: remove_fast_loop +********************************************************************/ +be_local_closure(Tasmota_remove_fast_loop, /* name */ + be_nested_proto( + 6, /* nstack */ + 2, /* argc */ + 2, /* varg */ + 0, /* has upvals */ + NULL, /* no upvals */ + 0, /* has sup protos */ + NULL, /* no sub protos */ + 1, /* has constants */ + ( &(const bvalue[ 3]) { /* constants */ + /* K0 */ be_nested_str(_fl), + /* K1 */ be_nested_str(find), + /* K2 */ be_nested_str(remove), + }), + &be_const_str_remove_fast_loop, + &be_const_str_solidified, + ( &(const binstruction[15]) { /* code */ + 0x88080100, // 0000 GETMBR R2 R0 K0 + 0x740A0000, // 0001 JMPT R2 #0003 + 0x80000400, // 0002 RET 0 + 0x88080100, // 0003 GETMBR R2 R0 K0 + 0x8C080501, // 0004 GETMET R2 R2 K1 + 0x5C100200, // 0005 MOVE R4 R1 + 0x7C080400, // 0006 CALL R2 2 + 0x4C0C0000, // 0007 LDNIL R3 + 0x200C0403, // 0008 NE R3 R2 R3 + 0x780E0003, // 0009 JMPF R3 #000E + 0x880C0100, // 000A GETMBR R3 R0 K0 + 0x8C0C0702, // 000B GETMET R3 R3 K2 + 0x5C140400, // 000C MOVE R5 R2 + 0x7C0C0400, // 000D CALL R3 2 + 0x80000000, // 000E RET 0 + }) + ) +); +/*******************************************************************/ + /******************************************************************** ** Solidified function: event @@ -2621,6 +2662,7 @@ class be_class_tasmota (scope: global, name: Tasmota) { fast_loop, closure(Tasmota_fast_loop_closure) add_fast_loop, closure(Tasmota_add_fast_loop_closure) + remove_fast_loop, closure(Tasmota_remove_fast_loop_closure) cmd, closure(Tasmota_cmd_closure) chars_in_string, closure(Tasmota_chars_in_string_closure) find_key_i, closure(Tasmota_find_key_i_closure) diff --git a/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be b/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be index 119302e0f..9e825cbf6 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be +++ b/lib/libesp32/berry_tasmota/src/embedded/Tasmota.be @@ -628,6 +628,14 @@ class Tasmota self._fl.push(cl) end + def remove_fast_loop(cl) + if !self._fl return end + var idx = self._fl.find(cl) + if idx != nil + self._fl.remove(idx) + end + end + def event(event_type, cmd, idx, payload, raw) import introspect import string From d80ba93b68ed3f12b108147851137bd88a350173 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 Jul 2022 09:08:06 +0200 Subject: [PATCH 095/219] audio i2s refactor --- .../xdrv_42_1_i2s_audio.ino | 69 ++- .../tasmota_xdrv_driver/xdrv_42_i2s_audio.ino | 518 ++++++++++-------- 2 files changed, 347 insertions(+), 240 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino index c48c342c6..b772fbc4d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino @@ -3,13 +3,16 @@ #ifdef ESP32S3_BOX #include #include +#include #include +#include void S3boxAudioPower(uint8_t pwr) { pinMode(46 , OUTPUT); digitalWrite(46, pwr); } +// box lite dac init uint32_t ES8156_init() { uint32_t ret_val = ESP_OK; @@ -27,29 +30,87 @@ uint32_t ES8156_init() { return ret_val; } +// box lite adc init uint32_t es7243e_init() { uint32_t ret_val = ESP_OK; if (I2cSetDevice(ES7243_ADDR, 1)) { I2cSetActiveFound(ES7243_ADDR, "ES7243e-I2C", 1); - } - audio_hal_codec_config_t cfg = { + audio_hal_codec_config_t cfg = { .i2s_iface = { .mode = AUDIO_HAL_MODE_SLAVE, .bits = AUDIO_HAL_BIT_LENGTH_16BITS, } - }; + }; - ret_val |= es8156_codec_init(&Wire1, &cfg); + ret_val |= es7243e_adc_init(&Wire1, &cfg); + } return ret_val; } +// box adc init +uint32_t es7210_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES7210_ADDR, 1)) { + I2cSetActiveFound(ES7210_ADDR, "ES7210-I2C", 1); + audio_hal_codec_config_t cfg = { + .adc_input = AUDIO_HAL_ADC_INPUT_ALL, + .codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE, + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .fmt = AUDIO_HAL_I2S_NORMAL, + .samples = AUDIO_HAL_16K_SAMPLES, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + }, + }; + + ret_val |= es7210_adc_init(&Wire1, &cfg); + ret_val |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface); + ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2), (es7210_gain_value_t) GAIN_37_5DB); + ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4), (es7210_gain_value_t) GAIN_0DB); + ret_val |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + } + return ret_val; +} + +// box dac init +uint32_t ES8311_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES8311_ADDR, 1)) { + I2cSetActiveFound(ES8311_ADDR, "ES8311-I2C", 1); + audio_hal_codec_config_t cfg = { + .dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1, + .codec_mode = AUDIO_HAL_CODEC_MODE_DECODE, + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .fmt = AUDIO_HAL_I2S_NORMAL, + .samples = AUDIO_HAL_16K_SAMPLES, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + }, + }; + + ret_val |= es8311_codec_init(&Wire1, &cfg); + ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); + ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); + ret_val |= es8311_codec_set_voice_volume(75); + ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + + } + return ret_val; +} + void S3boxInit() { if (TasmotaGlobal.i2c_enabled_2) { + // box lite ES8156_init(); es7243e_init(); + // box full + ES8311_init(); + es7210_init(); } } #endif diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino index bd155f9b1..370b68187 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino @@ -63,6 +63,50 @@ #define AUDIO_PWR_ON #define AUDIO_PWR_OFF +#ifdef ESP8266 +#define i2s_port_t uint8_t +#endif + +struct AUDIO_I2S { + uint8_t is2_volume; // should be in settings + i2s_port_t i2s_port; + int8_t mclk = -1; + int8_t bclk = -1; + int8_t ws = -1; + int8_t dout = -1; + int8_t din = -1; + AudioGeneratorMP3 *mp3 = nullptr; + AudioFileSourceFS *file; +#ifdef USE_I2S_NO_DAC + AudioOutputI2SNoDAC *out; +#else + AudioOutputI2S *out; +#endif // USE_I2S_NO_DAC + AudioFileSourceID3 *id3; + AudioGeneratorMP3 *decoder = NULL; + void *mp3ram = NULL; +#ifdef USE_I2S_WEBRADIO + AudioFileSourceICYStream *ifile = NULL; + AudioFileSourceBuffer *buff = NULL; + char wr_title[64]; + void *preallocateBuffer = NULL; + void *preallocateCodec = NULL; + uint32_t retryms = 0; +#endif // USE_I2S_WEBRADIO + +#ifdef ESP32 + TaskHandle_t mp3_task_h; + TaskHandle_t mic_task_h; + uint32_t mic_size; + uint8_t *mic_buff; + char mic_path[32]; +#endif + +} audio_i2s; + + +#define MIC_CHANNELS 1 + #ifdef USE_TTGO_WATCH #undef AUDIO_PWR_ON #undef AUDIO_PWR_OFF @@ -71,6 +115,7 @@ #endif // USE_TTGO_WATCH #ifdef USE_M5STACK_CORE2 +// leave this predefined currently #undef AUDIO_PWR_ON #undef AUDIO_PWR_OFF #define AUDIO_PWR_ON Core2AudioPower(true); @@ -89,29 +134,10 @@ #undef AUDIO_PWR_OFF #define AUDIO_PWR_ON S3boxAudioPower(true); #define AUDIO_PWR_OFF S3boxAudioPower(false); - -#undef DAC_IIS_BCK -#undef DAC_IIS_WS -#undef DAC_IIS_DOUT -#define DAC_IIS_BCK 17 -#define DAC_IIS_WS 47 -#define DAC_IIS_DOUT 15 -#define DAC_IIS_DIN 16 -#define DAC_IIS_MCLK 2 - +#undef MIC_CHANNELS +#define MIC_CHANNELS 2 #endif // ESP32S3_BOX -AudioGeneratorMP3 *mp3 = nullptr; -AudioFileSourceFS *file; -#ifdef USE_I2S_NO_DAC - AudioOutputI2SNoDAC *out; -#else - AudioOutputI2S *out; -#endif // USE_I2S_NO_DAC -AudioFileSourceID3 *id3; -AudioGeneratorMP3 *decoder = NULL; -void *mp3ram = NULL; - extern FS *ufsp; #ifdef ESP8266 @@ -124,54 +150,6 @@ const int preallocateCodecSize = 29192; // MP3 codec max mem needed //const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed #endif // ESP32 -#ifdef USE_I2S_WEBRADIO -AudioFileSourceICYStream *ifile = NULL; -AudioFileSourceBuffer *buff = NULL; -char wr_title[64]; -//char status[64]; - -void *preallocateBuffer = NULL; -void *preallocateCodec = NULL; -uint32_t retryms = 0; -#endif // USE_I2S_WEBRADIO - -#ifdef USE_I2S_SAY_TIME -AudioGeneratorTalkie *talkie = nullptr; -#endif // USE_I2S_SAY_TIME - -//! MAX98357A + INMP441 DOUBLE I2S BOARD -#ifdef ESP8266 -#undef DAC_IIS_BCK -#undef DAC_IIS_WS -#undef DAC_IIS_DOUT -#define DAC_IIS_BCK 15 -#define DAC_IIS_WS 2 -#define DAC_IIS_DOUT 3 -#endif // ESP8266 - -// defaults to TTGO WATCH -#ifdef ESP32 -#ifndef DAC_IIS_BCK -#undef DAC_IIS_BCK -#define DAC_IIS_BCK 26 -#endif // DAC_IIS_BCK - -#ifndef DAC_IIS_WS -#undef DAC_IIS_WS -#define DAC_IIS_WS 25 -#endif // DAC_IIS_WS - -#ifndef DAC_IIS_DOUT -#undef DAC_IIS_DOUT -#define DAC_IIS_DOUT 33 -#endif // DAC_IIS_DOUT - -#ifndef DAC_IIS_DIN -#undef DAC_IIS_DIN -#define DAC_IIS_DIN 34 -#endif // DAC_IIS_DIN - -#endif // ESP32 #ifdef USE_I2S_SAY_TIME long timezone = 2; @@ -213,15 +191,16 @@ uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; -void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) ; +void sayTime(int hour, int minutes) ; -void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) { +void sayTime(int hour, int minutes) { +AudioGeneratorTalkie *talkie = nullptr; - if (!out) return; + if (!audio_i2s.out) return; AUDIO_PWR_ON talkie = new AudioGeneratorTalkie(); - talkie->begin(nullptr, out); + talkie->begin(nullptr, audio_i2s.out); bool pm = (hour >= 12); uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, @@ -269,58 +248,113 @@ void sayTime(int hour, int minutes, AudioGeneratorTalkie *talkie) { talkie->say(spA_M_, sizeof(spA_M_)); } delete talkie; - out->stop(); + audio_i2s.out->stop(); AUDIO_PWR_OFF } #endif // USE_I2S_SAY_TIME -// should be in settings -uint8_t is2_volume; + +int32_t I2S_Init_0(void) { + + audio_i2s.i2s_port = (i2s_port_t)0; + +#if USE_I2S_EXTERNAL_DAC + // use i2s +#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT)) + audio_i2s.i2s_port = (i2s_port_t)0; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(); +#endif + audio_i2s.bclk = DAC_IIS_BCK; + audio_i2s.ws = DAC_IIS_WS; + audio_i2s.dout = DAC_IIS_DOUT; +#else +#ifdef USE_I2S_NO_DAC + if (PinUsed(GPIO_I2S_DOUT)) { +#else + if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) { +#endif // USE_I2S_NO_DAC + audio_i2s.i2s_port = (i2s_port_t)0; + #ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); + #else + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + #endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK); + audio_i2s.ws = Pin(GPIO_I2S_WS); + audio_i2s.dout = Pin(GPIO_I2S_DOUT); + audio_i2s.din = Pin(GPIO_I2S_DIN); + } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT), 1) { + audio_i2s.i2s_port = (i2s_port_t)1; + #ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); + #else + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + #endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); + audio_i2s.ws = Pin(GPIO_I2S_WS, 1); + audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); + audio_i2s.din = Pin(GPIO_I2S_DIN, 1); + } else { + return -1; + } +#ifdef ESP8266 + // esp8266 have fixed pins + if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout!= 3)) { + return -2; + } +#endif // ESP8266 +#endif // defined(DAC_IIS_BCK) + + audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + + AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + +#if defined(ESP32) && defined(ESP32S3_BOX) + S3boxInit(); +#endif + +#else + +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0 +#endif // USE_I2S_NO_DAC +#endif // USE_I2S_EXTERNAL_DAC + + return 0; +} void I2S_Init(void) { -#if USE_I2S_EXTERNAL_DAC - #ifdef USE_I2S_NO_DAC - out = new AudioOutputI2SNoDAC(); - #else - out = new AudioOutputI2S(); - #endif // USE_I2S_NO_DAC -#ifdef ESP32 -#ifdef ESP32S3_BOX - S3boxInit(); - out->SetPinout(DAC_IIS_BCK, DAC_IIS_WS, DAC_IIS_DOUT, DAC_IIS_MCLK, DAC_IIS_DIN); -#else - out->SetPinout(DAC_IIS_BCK, DAC_IIS_WS, DAC_IIS_DOUT); -#endif -#endif // ESP32 + if (I2S_Init_0()) { + return; + } -#else - #ifdef USE_I2S_NO_DAC - out = new AudioOutputI2SNoDAC(); - #else - out = new AudioOutputI2S(0, 1); // Internal DAC port 0 - #endif // USE_I2S_NO_DAC -#endif // USE_I2S_EXTERNAL_DAC - - is2_volume=10; - out->SetGain(((float)is2_volume/100.0)*4.0); - out->stop(); - mp3ram = nullptr; + audio_i2s.is2_volume=10; + audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); + audio_i2s.out->stop(); + audio_i2s.mp3ram = nullptr; #ifdef ESP32 if (UsePSRAM()) { - mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } #ifdef USE_I2S_WEBRADIO if (UsePSRAM()) { - preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } else { - preallocateBuffer = malloc(preallocateBufferSize); - preallocateCodec = malloc(preallocateCodecSize); + audio_i2s.preallocateBuffer = malloc(preallocateBufferSize); + audio_i2s.preallocateCodec = malloc(preallocateCodecSize); } - if (!preallocateBuffer || !preallocateCodec) { + if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) { //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); } #endif // USE_I2S_WEBRADIO @@ -331,29 +365,24 @@ void I2S_Init(void) { #ifdef ESP32 #define MODE_MIC 0 #define MODE_SPK 1 -#define Speak_I2S_NUMBER I2S_NUM_0 //#define MICSRATE 44100 #define MICSRATE 16000 uint32_t SpeakerMic(uint8_t spkr) { esp_err_t err = ESP_OK; - if (out) { - out->stop(); - delete out; - out = nullptr; + if (audio_i2s.out) { + audio_i2s.out->stop(); + delete audio_i2s.out; + audio_i2s.out = nullptr; } - i2s_driver_uninstall(Speak_I2S_NUMBER); - if (spkr==MODE_SPK) { - #ifdef USE_I2S_NO_DAC - out = new AudioOutputI2SNoDAC(); - #else - out = new AudioOutputI2S(0, 1); - #endif - out->SetPinout(DAC_IIS_BCK, DAC_IIS_WS, DAC_IIS_DOUT); - out->SetGain(((float)is2_volume/100.0)*4.0); - out->stop(); + i2s_driver_uninstall(audio_i2s.i2s_port); + + if (spkr == MODE_SPK) { + I2S_Init_0(); + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + audio_i2s.out->stop(); } else { // config mic i2s_config_t i2s_config = { @@ -366,48 +395,65 @@ uint32_t SpeakerMic(uint8_t spkr) { .dma_buf_count = 2, //.dma_buf_len = 128, .dma_buf_len = 1024, + .use_apll = 0, // Use audio PLL + .tx_desc_auto_clear = true, + .fixed_mclk = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT }; + +#ifdef ESP32S3_BOX + i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; +#else i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); - err += i2s_driver_install(Speak_I2S_NUMBER, &i2s_config, 0, NULL); +#endif + + err += i2s_driver_install(audio_i2s.i2s_port, &i2s_config, 0, NULL); i2s_pin_config_t tx_pin_config; - tx_pin_config.bck_io_num = DAC_IIS_BCK; - tx_pin_config.ws_io_num = DAC_IIS_WS; - tx_pin_config.data_out_num = DAC_IIS_DOUT; - tx_pin_config.data_in_num = DAC_IIS_DIN; - err += i2s_set_pin(Speak_I2S_NUMBER, &tx_pin_config); +#ifdef ESP32S3_BOX + tx_pin_config.mck_io_num = audio_i2s.mclk; +#else + tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE; +#endif + tx_pin_config.bck_io_num = audio_i2s.bclk; + tx_pin_config.ws_io_num = audio_i2s.ws; + tx_pin_config.data_out_num = audio_i2s.dout; + tx_pin_config.data_in_num = audio_i2s.din; - err += i2s_set_clk(Speak_I2S_NUMBER, MICSRATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); + err += i2s_set_pin(audio_i2s.i2s_port, &tx_pin_config); +#ifdef ESP32S3_BOX + err += i2s_set_clk(audio_i2s.i2s_port, MICSRATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); +#else + err += i2s_set_clk(audio_i2s.i2s_port, MICSRATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); +#endif } return err; } #define DATA_SIZE 1024 -TaskHandle_t mic_task_h; -uint32_t mic_size; -uint8_t *mic_buff; -char mic_path[32]; - void mic_task(void *arg){ uint32_t data_offset = 0; while (1) { uint32_t bytes_read; - i2s_read(Speak_I2S_NUMBER, (char *)(mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); + i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); if (bytes_read != DATA_SIZE) break; data_offset += DATA_SIZE; - if (data_offset >= mic_size-DATA_SIZE) break; + if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; } SpeakerMic(MODE_SPK); - SaveWav(mic_path, mic_buff, mic_size); - free(mic_buff); - vTaskDelete(mic_task_h); + SaveWav(audio_i2s.mic_path, audio_i2s.mic_buff, audio_i2s.mic_size); + free(audio_i2s.mic_buff); + vTaskDelete(audio_i2s.mic_task_h); } uint32_t i2s_record(char *path, uint32_t secs) { esp_err_t err = ESP_OK; - if (decoder || mp3) return 0; + if (audio_i2s.decoder || audio_i2s.mp3) return 0; err = SpeakerMic(MODE_MIC); if (err) { @@ -415,15 +461,15 @@ uint32_t i2s_record(char *path, uint32_t secs) { return err; } - mic_size = secs * MICSRATE * 2; + audio_i2s.mic_size = secs * MICSRATE * 2 * MIC_CHANNELS; - mic_buff = (uint8_t*)heap_caps_malloc(mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (!mic_buff) return 2; + audio_i2s.mic_buff = (uint8_t*)heap_caps_malloc(audio_i2s.mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!audio_i2s.mic_buff) return 2; if (*path=='+') { path++; - strlcpy(mic_path, path , sizeof(mic_path)); - xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &mic_task_h, 1); + strlcpy(audio_i2s.mic_path, path , sizeof(audio_i2s.mic_path)); + xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); return 0; } @@ -431,17 +477,17 @@ uint32_t i2s_record(char *path, uint32_t secs) { uint32_t stime=millis(); while (1) { uint32_t bytes_read; - i2s_read(Speak_I2S_NUMBER, (char *)(mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); + i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); if (bytes_read != DATA_SIZE) break; data_offset += DATA_SIZE; - if (data_offset >= mic_size-DATA_SIZE) break; + if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; delay(0); } //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime); SpeakerMic(MODE_SPK); // save to path - SaveWav(path, mic_buff, mic_size); - free(mic_buff); + SaveWav(path, audio_i2s.mic_buff, audio_i2s.mic_size); + free(audio_i2s.mic_buff); return 0; } @@ -456,7 +502,7 @@ bool SaveWav(char *path, uint8_t *buff, uint32_t size) { uint8_t wavHeader[sizeof(wavHTemplate)]; memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate)); - uint8_t channels = 1; + uint8_t channels = MIC_CHANNELS; uint32_t hertz = MICSRATE; uint8_t bps = 16; @@ -487,18 +533,17 @@ bool SaveWav(char *path, uint8_t *buff, uint32_t size) { #endif // ESP32 #ifdef ESP32 -TaskHandle_t mp3_task_h; void mp3_task(void *arg) { while (1) { - while (mp3->isRunning()) { - if (!mp3->loop()) { - mp3->stop(); + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); mp3_delete(); - out->stop(); - if (mp3_task_h) { - vTaskDelete(mp3_task_h); - mp3_task_h = 0; + audio_i2s.out->stop(); + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = 0; } //mp3_task_h=nullptr; } @@ -514,8 +559,8 @@ void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) (void) isUnicode; // Punt this ball for now (void) ptr; if (strstr_P(type, PSTR("Title"))) { - strncpy(wr_title, str, sizeof(wr_title)); - wr_title[sizeof(wr_title)-1] = 0; + strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title)); + audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0; //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title); } else { // Who knows what to do? Not me! @@ -531,30 +576,30 @@ void StatusCallback(void *cbData, int code, const char *string) { } void Webradio(const char *url) { - if (decoder || mp3) return; - if (!out) return; + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; AUDIO_PWR_ON - ifile = new AudioFileSourceICYStream(url); - ifile->RegisterMetadataCB(MDCallback, NULL); - buff = new AudioFileSourceBuffer(ifile, preallocateBuffer, preallocateBufferSize); - buff->RegisterStatusCB(StatusCallback, NULL); - decoder = new AudioGeneratorMP3(preallocateCodec, preallocateCodecSize); - decoder->RegisterStatusCB(StatusCallback, NULL); - decoder->begin(buff, out); - if (!decoder->isRunning()) { + audio_i2s.ifile = new AudioFileSourceICYStream(url); + audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL); + audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize); + audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize); + audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out); + if (!audio_i2s.decoder->isRunning()) { // Serial.printf_P(PSTR("Can't connect to URL")); StopPlaying(); // strcpy_P(status, PSTR("Unable to connect to URL")); - retryms = millis() + 2000; + audio_i2s.retryms = millis() + 2000; } - xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &mp3_task_h, 1); + xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); } void mp3_task2(void *arg){ while (1) { - if (decoder && decoder->isRunning()) { - if (!decoder->loop()) { + if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { + if (!audio_i2s.decoder->loop()) { StopPlaying(); //retryms = millis() + 2000; } @@ -565,31 +610,33 @@ void mp3_task2(void *arg){ void StopPlaying() { - if (mp3_task_h) { - vTaskDelete(mp3_task_h); - mp3_task_h = nullptr; + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = nullptr; } - if (decoder) { - decoder->stop(); - delete decoder; - decoder = NULL; + if (audio_i2s.decoder) { + audio_i2s.decoder->stop(); + delete audio_i2s.decoder; + audio_i2s.decoder = NULL; } - if (buff) { - buff->close(); - delete buff; - buff = NULL; + + if (audio_i2s.buff) { + audio_i2s.buff->close(); + delete audio_i2s.buff; + audio_i2s.buff = NULL; } - if (ifile) { - ifile->close(); - delete ifile; - ifile = NULL; + + if (audio_i2s.ifile) { + audio_i2s.ifile->close(); + delete audio_i2s.ifile; + audio_i2s.ifile = NULL; } AUDIO_PWR_OFF } void Cmd_WebRadio(void) { - if (decoder) { + if (audio_i2s.decoder) { StopPlaying(); } if (XdrvMailbox.data_len > 0) { @@ -598,10 +645,22 @@ void Cmd_WebRadio(void) { } else { ResponseCmndChar_P(PSTR("Stopped")); } - } -#ifdef USE_M5STACK_CORE2 +#ifdef USE_WEBSERVER +const char HTTP_WEBRADIO[] PROGMEM = + "{s}" "I2S_WR-Title" "{m}%s{e}"; + +void I2S_WR_Show(void) { + if (audio_i2s.decoder) { + WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); + } +} +#endif // USE_WEBSERVER + +#endif // USE_I2S_WEBRADIO + +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) void Cmd_MicRec(void) { if (XdrvMailbox.data_len > 0) { uint16 time = 10; @@ -618,24 +677,11 @@ void Cmd_MicRec(void) { } #endif // USE_M5STACK_CORE2 -#ifdef USE_WEBSERVER -const char HTTP_WEBRADIO[] PROGMEM = - "{s}" "I2S_WR-Title" "{m}%s{e}"; - -void I2S_WR_Show(void) { - if (decoder) { - WSContentSend_PD(HTTP_WEBRADIO,wr_title); - } -} -#endif // USE_WEBSERVER - -#endif // USE_I2S_WEBRADIO - #ifdef ESP32 void Play_mp3(const char *path) { -#if (defined(USE_SCRIPT_FATFS) && defined(USE_SCRIPT)) || defined(USE_UFILESYS) - if (decoder || mp3) return; - if (!out) return; +#ifdef USE_UFILESYS + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; if (!ufsp->exists(path)) { return; @@ -651,54 +697,54 @@ void Play_mp3(const char *path) { I2S_Task = false; } - file = new AudioFileSourceFS(*ufsp, path); + audio_i2s.file = new AudioFileSourceFS(*ufsp, path); - id3 = new AudioFileSourceID3(file); + audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); - if (mp3ram) { - mp3 = new AudioGeneratorMP3(mp3ram, preallocateCodecSize); + if (audio_i2s.mp3ram) { + audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); } else { - mp3 = new AudioGeneratorMP3(); + audio_i2s.mp3 = new AudioGeneratorMP3(); } - mp3->begin(id3, out); + audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); if (I2S_Task) { - xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &mp3_task_h, 1); + xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); } else { - while (mp3->isRunning()) { - if (!mp3->loop()) { - mp3->stop(); + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); break; } OsWatchLoop(); } - out->stop(); + audio_i2s.out->stop(); mp3_delete(); } -#endif // USE_SCRIPT +#endif // USE_UFILESYS } void mp3_delete(void) { - delete file; - delete id3; - delete mp3; - mp3=nullptr; + delete audio_i2s.file; + delete audio_i2s.id3; + delete audio_i2s.mp3; + audio_i2s.mp3=nullptr; AUDIO_PWR_OFF } #endif // ESP32 void Say(char *text) { - if (!out) return; + if (!audio_i2s.out) return; AUDIO_PWR_ON - out->begin(); + audio_i2s.out->begin(); ESP8266SAM *sam = new ESP8266SAM; - sam->Say(out, text); + sam->Say(audio_i2s.out, text); delete sam; - out->stop(); + audio_i2s.out->stop(); AUDIO_PWR_OFF } @@ -711,7 +757,7 @@ const char kI2SAudio_Commands[] PROGMEM = "I2S|" #ifdef USE_I2S_WEBRADIO "|WR" #endif // USE_I2S_WEBRADIO -#ifdef USE_M5STACK_CORE2 +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) "|REC" #endif // USE_M5STACK_CORE2 #endif // ESP32 @@ -724,7 +770,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { #ifdef USE_I2S_WEBRADIO ,&Cmd_WebRadio #endif // USE_I2S_WEBRADIO -#ifdef USE_M5STACK_CORE2 +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ,&Cmd_MicRec #endif // USE_M5STACK_CORE2 #endif // ESP32 @@ -741,12 +787,12 @@ void Cmd_Play(void) { void Cmd_Gain(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - if (out) { - is2_volume=XdrvMailbox.payload; - out->SetGain(((float)(is2_volume-2)/100.0)*4.0); + if (audio_i2s.out) { + audio_i2s.is2_volume=XdrvMailbox.payload; + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); } } - ResponseCmndNumber(is2_volume); + ResponseCmndNumber(audio_i2s.is2_volume); } void Cmd_Say(void) { @@ -758,7 +804,7 @@ void Cmd_Say(void) { void Cmd_Time(void) { #ifdef USE_I2S_SAY_TIME - sayTime(RtcTime.hour, RtcTime.minute, talkie); + sayTime(RtcTime.hour, RtcTime.minute); #endif // USE_I2S_SAY_TIME ResponseCmndDone(); } From 82d668144edd1a2f1ca9a31d7d0c0929396b6008 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 Jul 2022 09:10:10 +0200 Subject: [PATCH 096/219] update sbox codecs --- lib/lib_audio/es7210/library.json | 7 + lib/lib_audio/es7210/library.properties | 9 + lib/lib_audio/es7210/src/es7210.cpp | 549 +++++++++++++++++++ lib/lib_audio/es7210/src/es7210.h | 260 +++++++++ lib/lib_audio/es8156/library.properties | 4 +- lib/lib_audio/es8311/library.json | 7 + lib/lib_audio/es8311/library.properties | 9 + lib/lib_audio/es8311/src/es8311.cpp | 695 ++++++++++++++++++++++++ lib/lib_audio/es8311/src/es8311.h | 285 ++++++++++ 9 files changed, 1823 insertions(+), 2 deletions(-) create mode 100644 lib/lib_audio/es7210/library.json create mode 100644 lib/lib_audio/es7210/library.properties create mode 100644 lib/lib_audio/es7210/src/es7210.cpp create mode 100644 lib/lib_audio/es7210/src/es7210.h create mode 100644 lib/lib_audio/es8311/library.json create mode 100644 lib/lib_audio/es8311/library.properties create mode 100644 lib/lib_audio/es8311/src/es8311.cpp create mode 100644 lib/lib_audio/es8311/src/es8311.h diff --git a/lib/lib_audio/es7210/library.json b/lib/lib_audio/es7210/library.json new file mode 100644 index 000000000..27e99c11a --- /dev/null +++ b/lib/lib_audio/es7210/library.json @@ -0,0 +1,7 @@ +{ + "name": "ES7210", + "description": "Audio codec", + "keywords": "ESP8266, ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/es7210/library.properties b/lib/lib_audio/es7210/library.properties new file mode 100644 index 000000000..ffa00ca45 --- /dev/null +++ b/lib/lib_audio/es7210/library.properties @@ -0,0 +1,9 @@ +name=ES7210 +version=1.0 +author= +maintainer= +sentence=Audio fcodec for ESP32 +paragraph= +category=Signal Output +url= +architectures=esp32 diff --git a/lib/lib_audio/es7210/src/es7210.cpp b/lib/lib_audio/es7210/src/es7210.cpp new file mode 100644 index 000000000..3bf680308 --- /dev/null +++ b/lib/lib_audio/es7210/src/es7210.cpp @@ -0,0 +1,549 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifdef ESP32 + +#include +#include +#include "esp_log.h" +#include "es7210.h" + + +#define I2S_DSP_MODE_A 0 +#define MCLK_DIV_FRE 256 + + +#define ES7210_MCLK_SOURCE FROM_CLOCK_DOUBLE_PIN /* In master mode, 0 : MCLK from pad 1 : MCLK from clock doubler */ +#define FROM_PAD_PIN 0 +#define FROM_CLOCK_DOUBLE_PIN 1 + + +static TwoWire *es7210wire; +static es7210_gain_value_t gain; + +/* + * Clock coefficient structer + */ +struct _coeff_div { + uint32_t mclk; /* mclk frequency */ + uint32_t lrck; /* lrck */ + uint8_t ss_ds; + uint8_t adc_div; /* adcclk divider */ + uint8_t dll; /* dll_bypass */ + uint8_t doubler; /* doubler enable */ + uint8_t osr; /* adc osr */ + uint8_t mclk_src; /* select mclk source */ + uint32_t lrck_h; /* The high 4 bits of lrck */ + uint32_t lrck_l; /* The low 8 bits of lrck */ +}; + +static const char *TAG = "ES7210"; + +static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4); + +/* Codec hifi mclk clock divider coefficients + * MEMBER REG + * mclk: 0x03 + * lrck: standard + * ss_ds: -- + * adc_div: 0x02 + * dll: 0x06 + * doubler: 0x02 + * osr: 0x07 + * mclk_src: 0x03 + * lrckh: 0x04 + * lrckl: 0x05 +*/ +static const struct _coeff_div coeff_div[] = { + //mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl + /* 8k */ + {12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, + {16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00}, + {19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60}, + {4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 11.025k */ + {11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + + /* 12k */ + {12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40}, + + /* 16k */ + {4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80}, + {16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, + {12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00}, + + /* 22.05k */ + {11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + + /* 24k */ + {12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20}, + + /* 32k */ + {12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80}, + {16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, + {19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58}, + + /* 44.1k */ + {11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + + /* 48k */ + {12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, + {19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90}, + + /* 64k */ + {16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, + {19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + + /* 96k */ + {12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, + {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, +}; + +static esp_err_t es7210_write_reg(uint8_t reg_addr, uint8_t data) +{ + + es7210wire->beginTransmission(ES7210_ADDR); + es7210wire->write(reg_addr); + es7210wire->write(data); + return es7210wire->endTransmission(); + +} + +static esp_err_t es7210_update_reg_bit(uint8_t reg_addr, uint8_t update_bits, uint8_t data) +{ + uint8_t regv; + regv = es7210_read_reg(reg_addr); + regv = (regv & (~update_bits)) | (update_bits & data); + return es7210_write_reg(reg_addr, regv); +} + +static int get_coeff(uint32_t mclk, uint32_t lrck) +{ + for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { + if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk) + return i; + } + return -1; +} + +int8_t get_es7210_mclk_src(void) +{ + return ES7210_MCLK_SOURCE; +} + +int es7210_read_reg(uint8_t reg_addr) +{ + uint8_t data; + es7210wire->beginTransmission(ES7210_ADDR); + es7210wire->write(reg_addr); + es7210wire->endTransmission(false); + es7210wire->requestFrom(ES7210_ADDR, (size_t)1); + data = es7210wire->read(); + return (int)data; +} + +esp_err_t es7210_config_sample(audio_hal_iface_samples_t sample) +{ + uint8_t regv; + int coeff; + int sample_fre = 0; + int mclk_fre = 0; + esp_err_t ret = ESP_OK; + switch (sample) { + case AUDIO_HAL_08K_SAMPLES: + sample_fre = 8000; + break; + case AUDIO_HAL_11K_SAMPLES: + sample_fre = 11025; + break; + case AUDIO_HAL_16K_SAMPLES: + sample_fre = 16000; + break; + case AUDIO_HAL_22K_SAMPLES: + sample_fre = 22050; + break; + case AUDIO_HAL_24K_SAMPLES: + sample_fre = 24000; + break; + case AUDIO_HAL_32K_SAMPLES: + sample_fre = 32000; + break; + case AUDIO_HAL_44K_SAMPLES: + sample_fre = 44100; + break; + case AUDIO_HAL_48K_SAMPLES: + sample_fre = 48000; + break; + default: + ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre); + break; + } + mclk_fre = sample_fre * MCLK_DIV_FRE; + coeff = get_coeff(mclk_fre, sample_fre); + if (coeff < 0) { + ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre); + return ESP_FAIL; + } + /* Set clock parammeters */ + if (coeff >= 0) { + /* Set adc_div & doubler & dll */ + regv = es7210_read_reg(ES7210_MAINCLK_REG02) & 0x00; + regv |= coeff_div[coeff].adc_div; + regv |= coeff_div[coeff].doubler << 6; + regv |= coeff_div[coeff].dll << 7; + ret |= es7210_write_reg(ES7210_MAINCLK_REG02, regv); + /* Set osr */ + regv = coeff_div[coeff].osr; + ret |= es7210_write_reg(ES7210_OSR_REG07, regv); + /* Set lrck */ + regv = coeff_div[coeff].lrck_h; + ret |= es7210_write_reg(ES7210_LRCK_DIVH_REG04, regv); + regv = coeff_div[coeff].lrck_l; + ret |= es7210_write_reg(ES7210_LRCK_DIVL_REG05, regv); + } + return ret; +} + +esp_err_t es7210_mic_select(es7210_input_mics_t mic) +{ + esp_err_t ret = ESP_OK; + mic_select = mic; + if (mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) { + for (int i = 0; i < 4; i++) { + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00); + } + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0xff); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); + if (mic_select & ES7210_INPUT_MIC1) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC2) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC3) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x10, 0x10); + } + if (mic_select & ES7210_INPUT_MIC4) { + ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4"); + ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); + ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x10, 0x10); + } + } else { + ESP_LOGE(TAG, "Microphone selection error"); + return ESP_FAIL; + } + return ret; +} + +esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg) +{ + esp_err_t ret = ESP_OK; + + es7210wire = tw; + + ret |= es7210_write_reg(ES7210_RESET_REG00, 0xff); + ret |= es7210_write_reg(ES7210_RESET_REG00, 0x41); + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x1f); + ret |= es7210_write_reg(ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */ + ret |= es7210_write_reg(ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */ + // ret |= es7210_write_reg(ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */ + // ret |= es7210_write_reg(ES7210_ADC12_HPF1_REG22, 0x0a); + // ret |= es7210_write_reg(ES7210_ADC34_HPF2_REG20, 0x0a); + // ret |= es7210_write_reg(ES7210_ADC34_HPF1_REG21, 0x2a); + /* Set master/slave audio interface */ + audio_hal_codec_i2s_iface_t *i2s_cfg = & (codec_cfg->i2s_iface); + switch (i2s_cfg->mode) { + case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */ + ESP_LOGI(TAG, "ES7210 in Master mode"); + // ret |= es7210_update_reg_bit(ES7210_MODE_CONFIG_REG08, 0x01, 0x01); + ret |= es7210_write_reg(ES7210_MODE_CONFIG_REG08, 0x20); + /* Select clock source for internal mclk */ + switch (get_es7210_mclk_src()) { + case FROM_PAD_PIN: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); + break; + case FROM_CLOCK_DOUBLE_PIN: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x80); + break; + default: + ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); + break; + } + break; + case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */ + ESP_LOGI(TAG, "ES7210 in Slave mode"); + break; + default: + break; + } + ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xC3); /* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */ + ret |= es7210_write_reg(ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */ + ret |= es7210_write_reg(ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */ + ret |= es7210_write_reg(ES7210_OSR_REG07, 0x20); + ret |= es7210_write_reg(ES7210_MAINCLK_REG02, 0xc1); /* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */ + ret |= es7210_config_sample(i2s_cfg->samples); + ret |= es7210_mic_select(mic_select); + ret |= es7210_adc_set_gain_all(GAIN_0DB); + return ESP_OK; +} + +esp_err_t es7210_adc_deinit() +{ + return ESP_OK; +} + +esp_err_t es7210_config_fmt(audio_hal_iface_format_t fmt) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0; + adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); + adc_iface &= 0xfc; + switch (fmt) { + case AUDIO_HAL_I2S_NORMAL: + ESP_LOGD(TAG, "ES7210 in I2S Format"); + adc_iface |= 0x00; + break; + case AUDIO_HAL_I2S_LEFT: + case AUDIO_HAL_I2S_RIGHT: + ESP_LOGD(TAG, "ES7210 in LJ Format"); + adc_iface |= 0x01; + break; + case AUDIO_HAL_I2S_DSP: + if (I2S_DSP_MODE_A) { + ESP_LOGD(TAG, "ES7210 in DSP-A Format"); + adc_iface |= 0x03; + } else { + ESP_LOGD(TAG, "ES7210 in DSP-B Format"); + adc_iface |= 0x13; + } + break; + default: + adc_iface &= 0xfc; + break; + } + ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); + /* Force ADC1/2 output to SDOUT1 and ADC3/4 output to SDOUT2 */ + ret |= es7210_write_reg(ES7210_SDP_INTERFACE2_REG12, 0x00); + return ret; +} + +esp_err_t es7210_set_bits(audio_hal_iface_bits_t bits) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0; + adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); + adc_iface &= 0x1f; + switch (bits) { + case AUDIO_HAL_BIT_LENGTH_16BITS: + adc_iface |= 0x60; + break; + case AUDIO_HAL_BIT_LENGTH_24BITS: + adc_iface |= 0x00; + break; + case AUDIO_HAL_BIT_LENGTH_32BITS: + adc_iface |= 0x80; + break; + default: + adc_iface |= 0x60; + break; + } + ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); + return ret; +} + +esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_set_bits(iface->bits); + ret |= es7210_config_fmt(iface->fmt); + ret |= es7210_config_sample(iface->samples); + return ret; +} + +esp_err_t es7210_start(uint8_t clock_reg_value) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, clock_reg_value); + ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x00); + // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0x40); + ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0x00); + ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0x00); + ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0x00); + ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0x00); + ret |= es7210_mic_select(mic_select); + return ret; +} + +esp_err_t es7210_stop(void) +{ + esp_err_t ret = ESP_OK; + ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0xff); + ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0xff); + ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0xff); + ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0xff); + ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B,0xff); + ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); + // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xc0); + ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x7f); + ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x07); + return ret; +} + +esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) +{ + static uint8_t regv; + esp_err_t ret = ESP_OK; + // ESP_LOGW(TAG, "ES7210 only supports ADC mode"); + ret = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + if ((ret != 0x7f) && (ret != 0xff)) { + regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + } + if (ctrl_state == AUDIO_HAL_CTRL_START) { + ESP_LOGI(TAG, "The ES7210_CLOCK_OFF_REG01 value before stop is %x",regv); + ret |= es7210_start(regv); + } else { + ESP_LOGW(TAG, "The codec is about to stop"); + regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); + ret |= es7210_stop(); + } + return ESP_OK; +} + +esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain) +{ + esp_err_t ret_val = ESP_OK; + + if (gain < GAIN_0DB) { + gain = GAIN_0DB; + } + + if (gain > GAIN_37_5DB) { + gain = GAIN_37_5DB; + } + + if (mic_mask & ES7210_INPUT_MIC1) { + ret_val |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC2) { + ret_val |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC3) { + ret_val |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); + } + if (mic_mask & ES7210_INPUT_MIC4) { + ret_val |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); + } + + return ret_val; +} + +esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain) +{ + esp_err_t ret = ESP_OK; + uint32_t max_gain_vaule = 14; + if (gain < 0) { + gain = (es7210_gain_value_t) 0; + } else if (gain > max_gain_vaule) { + gain = (es7210_gain_value_t) max_gain_vaule; + } + ESP_LOGD(TAG, "SET: gain:%d", gain); + if (mic_select & ES7210_INPUT_MIC1) { + ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC2) { + ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC3) { + ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); + } + if (mic_select & ES7210_INPUT_MIC4) { + ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); + } + return ret; +} + +esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain) +{ + int regv = 0; + uint8_t gain_value; + if (mic_mask & ES7210_INPUT_MIC1) { + regv = es7210_read_reg(ES7210_MIC1_GAIN_REG43); + } else if (mic_mask & ES7210_INPUT_MIC2) { + regv = es7210_read_reg(ES7210_MIC2_GAIN_REG44); + } else if (mic_mask & ES7210_INPUT_MIC3) { + regv = es7210_read_reg(ES7210_MIC3_GAIN_REG45); + } else if (mic_mask & ES7210_INPUT_MIC4) { + regv = es7210_read_reg(ES7210_MIC4_GAIN_REG46); + } else { + ESP_LOGE(TAG, "No MIC selected"); + return ESP_FAIL; + } + if (regv == ESP_FAIL) { + return regv; + } + gain_value = (regv & 0x0f); /* Retain the last four bits for gain */ + *gain = (es7210_gain_value_t) gain_value; + ESP_LOGI(TAG, "GET: gain_value:%d", gain_value); + return ESP_OK; +} + +esp_err_t es7210_adc_set_volume(int volume) +{ + esp_err_t ret = ESP_OK; + ESP_LOGD(TAG, "ADC can adjust gain"); + return ret; +} + +esp_err_t es7210_set_mute(bool enable) +{ + ESP_LOGD(TAG, "ES7210 SetMute :%d", enable); + return ESP_OK; +} + +void es7210_read_all(void) +{ + for (int i = 0; i <= 0x4E; i++) { + uint8_t reg = es7210_read_reg(i); + ets_printf("REG:%02x, %02x\n", reg, i); + } +} + +#endif diff --git a/lib/lib_audio/es7210/src/es7210.h b/lib/lib_audio/es7210/src/es7210.h new file mode 100644 index 000000000..07f7e6311 --- /dev/null +++ b/lib/lib_audio/es7210/src/es7210.h @@ -0,0 +1,260 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2021 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ES7210_H +#define _ES7210_H + +#include "audio_hal.h" + + +typedef enum { + ES7210_AD1_AD0_00 = 0x40, + ES7210_AD1_AD0_01 = 0x41, + ES7210_AD1_AD0_10 = 0x42, + ES7210_AD1_AD0_11 = 0x43, +} es7210_address_t; + +/* ES7210 address*/ +#define ES7210_ADDR ES7210_AD1_AD0_00 + +#ifdef __cplusplus +extern "C" { +#endif + +#define ES7210_RESET_REG00 0x00 /* Reset control */ +#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */ +#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */ +#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */ +#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */ +#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */ +#define ES7210_POWER_DOWN_REG06 0x06 /* power down */ +#define ES7210_OSR_REG07 0x07 +#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */ +#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/ +#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */ +#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */ +#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */ +#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */ +#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */ +#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */ +#define ES7210_ADC34_HPF1_REG21 0x21 +#define ES7210_ADC12_HPF1_REG22 0x22 +#define ES7210_ADC12_HPF2_REG23 0x23 +#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */ +#define ES7210_MIC12_BIAS_REG41 0x41 +#define ES7210_MIC34_BIAS_REG42 0x42 +#define ES7210_MIC1_GAIN_REG43 0x43 +#define ES7210_MIC2_GAIN_REG44 0x44 +#define ES7210_MIC3_GAIN_REG45 0x45 +#define ES7210_MIC4_GAIN_REG46 0x46 +#define ES7210_MIC1_POWER_REG47 0x47 +#define ES7210_MIC2_POWER_REG48 0x48 +#define ES7210_MIC3_POWER_REG49 0x49 +#define ES7210_MIC4_POWER_REG4A 0x4A +#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */ +#define ES7210_MIC34_POWER_REG4C 0x4C + + + +typedef enum { + ES7210_INPUT_MIC1 = 0x01, + ES7210_INPUT_MIC2 = 0x02, + ES7210_INPUT_MIC3 = 0x04, + ES7210_INPUT_MIC4 = 0x08 +} es7210_input_mics_t; + +typedef enum gain_value{ + GAIN_0DB = 0, + GAIN_3DB, + GAIN_6DB, + GAIN_9DB, + GAIN_12DB, + GAIN_15DB, + GAIN_18DB, + GAIN_21DB, + GAIN_24DB, + GAIN_27DB, + GAIN_30DB, + GAIN_33DB, + GAIN_34_5DB, + GAIN_36DB, + GAIN_37_5DB, +} es7210_gain_value_t; + +/* + * @brief Initialize ES7210 ADC chip + * + * @param[in] codec_cfg: configuration of ES7210 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); + +/** + * @brief Deinitialize ES7210 ADC chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_deinit(); + +/** + * @brief Configure ES7210 ADC mode and I2S interface + * + * @param[in] mode: codec mode + * @param[in] iface: I2S config + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); + +/** + * @brief Control ES7210 ADC chip + * + * @param[in] mode: codec mode + * @param[in] ctrl_state: start or stop progress + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); + +/** + * @brief Set gain of given mask + * + * @param[in] mic_mask Mask of MIC channel + * + * @param[in] gain: gain + * + * gain : value + * GAIN_0DB : 1 + * GAIN_3DB : 2 + * GAIN_6DB : 3 + * · + * · + * · + * GAIN_30DB : 10 + * GAIN_33DB : 11 + * GAIN_34_5DB : 12 + * GAIN_36DB : 13 + * GAIN_37_5DB : 14 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain); + +/** + * @brief Set gain (Note: the enabled microphone sets the same gain) + * + * @param[in] gain: gain + * + * gain : value + * GAIN_0DB : 1 + * GAIN_3DB : 2 + * GAIN_6DB : 3 + * · + * · + * · + * GAIN_30DB : 10 + * GAIN_33DB : 11 + * GAIN_34_5DB : 12 + * GAIN_36DB : 13 + * GAIN_37_5DB : 14 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain); + +/** + * @brief Get MIC gain + * + * @param mic_mask Selected MIC + * @param gain Pointer to `es7210_gain_value_t` + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain); + +/** + * @brief Set volume + * + * @param[in] volume: volume + * + * @return + * - ESP_OK + */ +esp_err_t es7210_adc_set_volume(int volume); + +/** + * @brief Set ES7210 ADC mute status + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +esp_err_t es7210_set_mute(bool enable); + +/** + * @brief Select ES7210 mic + * + * @param[in] mic: mics + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +esp_err_t es7210_mic_select(es7210_input_mics_t mic); + +/** + * @brief Read regs of ES7210 + * + * @param[in] reg_addr: reg_addr + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +int es7210_read_reg(uint8_t reg_addr); + +/** + * @brief Read all regs of ES7210 + */ +void es7210_read_all(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _ES7210_H_ */ diff --git a/lib/lib_audio/es8156/library.properties b/lib/lib_audio/es8156/library.properties index bd6db1442..7ff0a31b6 100644 --- a/lib/lib_audio/es8156/library.properties +++ b/lib/lib_audio/es8156/library.properties @@ -2,8 +2,8 @@ name=ES8156 version=1.0 author= maintainer= -sentence=Audio fcodec for ESP8266, ESP32 +sentence=Audio fcodec for ESP32 paragraph= category=Signal Output url= -architectures=* +architectures=esp32 diff --git a/lib/lib_audio/es8311/library.json b/lib/lib_audio/es8311/library.json new file mode 100644 index 000000000..bf1b4701b --- /dev/null +++ b/lib/lib_audio/es8311/library.json @@ -0,0 +1,7 @@ +{ + "name": "ES8311", + "description": "Audio codec", + "keywords": "ESP8266, ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/es8311/library.properties b/lib/lib_audio/es8311/library.properties new file mode 100644 index 000000000..fdbc224d2 --- /dev/null +++ b/lib/lib_audio/es8311/library.properties @@ -0,0 +1,9 @@ +name=ES8311 +version=1.0 +author= +maintainer= +sentence=Audio fcodec for ESP32 +paragraph= +category=Signal Output +url= +architectures=esp32 diff --git a/lib/lib_audio/es8311/src/es8311.cpp b/lib/lib_audio/es8311/src/es8311.cpp new file mode 100644 index 000000000..6cdc41f75 --- /dev/null +++ b/lib/lib_audio/es8311/src/es8311.cpp @@ -0,0 +1,695 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2019 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifdef ESP32 + +#include +#include +#include "esp_log.h" +#include "es8311.h" + + + +/* + * to define the clock soure of MCLK + */ +#define FROM_MCLK_PIN 0 +#define FROM_SCLK_PIN 1 +#define ES8311_MCLK_SOURCE FROM_MCLK_PIN + +/* + * to define whether to reverse the clock + */ +#define INVERT_MCLK 0 // do not invert +#define INVERT_SCLK 0 + +#define IS_DMIC 0 // Is it a digital microphone + +#define MCLK_DIV_FRE 256 + +static TwoWire *es8311_wire; + + +/* + * Clock coefficient structer + */ +struct _coeff_div { + uint32_t mclk; /* mclk frequency */ + uint32_t rate; /* sample rate */ + uint8_t pre_div; /* the pre divider with range from 1 to 8 */ + uint8_t pre_multi; /* the pre multiplier with x1, x2, x4 and x8 selection */ + uint8_t adc_div; /* adcclk divider */ + uint8_t dac_div; /* dacclk divider */ + uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */ + uint8_t lrck_h; /* adclrck divider and daclrck divider */ + uint8_t lrck_l; + uint8_t bclk_div; /* sclk divider */ + uint8_t adc_osr; /* adc osr */ + uint8_t dac_osr; /* dac osr */ +}; + +/* codec hifi mclk clock divider coefficients */ +static const struct _coeff_div coeff_div[] = { + //mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr + /* 8k */ + {12288000, 8000 , 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 8000 , 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10}, + {16384000, 8000 , 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 8000 , 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 8000 , 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 8000 , 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 8000 , 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1024000 , 8000 , 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 11.025k */ + {11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 12k */ + {12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 16k */ + {12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, + {16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1024000 , 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 22.05k */ + {11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 24k */ + {12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 32k */ + {12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, + {16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {4096000 , 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2048000 , 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + {1024000 , 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 44.1k */ + {11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 48k */ + {12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + + /* 64k */ + {12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {4096000 , 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, + {2048000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18}, + {1024000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + /* 88.2k */ + {11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {2822400 , 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1411200 , 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, + + /* 96k */ + {12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {6144000 , 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {3072000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, + {1536000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, +}; + +static const char *TAG = "DRV8311"; + +#define ES_ASSERT(a, format, b, ...) \ + if ((a) != 0) { \ + ESP_LOGE(TAG, format, ##__VA_ARGS__); \ + return b;\ + } + +static esp_err_t es8311_write_reg(uint8_t reg_addr, uint8_t data) +{ + //return i2c_bus_write_byte(i2c_handle, reg_addr, data); + es8311_wire->beginTransmission(ES8311_ADDR); + es8311_wire->write(reg_addr); + es8311_wire->write(data); + return es8311_wire->endTransmission(); + +} + +static int es8311_read_reg(uint8_t reg_addr) +{ + uint8_t data; + //i2c_bus_read_byte(i2c_handle, reg_addr, &data); + es8311_wire->beginTransmission(ES8311_ADDR); + es8311_wire->write(reg_addr); + es8311_wire->endTransmission(false); + es8311_wire->requestFrom(ES8311_ADDR, (size_t)1); + data = es8311_wire->read(); + return (int)data; +} + +/* +* look for the coefficient in coeff_div[] table +*/ +static int get_coeff(uint32_t mclk, uint32_t rate) +{ + for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { + if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) + return i; + } + return -1; +} + +/* +* set es8311 dac mute or not +* if mute = 0, dac un-mute +* if mute = 1, dac mute +*/ +static void es8311_mute(int mute) +{ + uint8_t regv; + regv = es8311_read_reg(ES8311_DAC_REG31) & 0x9f; + if (mute) { + es8311_write_reg(ES8311_SYSTEM_REG12, 0x02); + es8311_write_reg(ES8311_DAC_REG31, regv | 0x60); + es8311_write_reg(ES8311_DAC_REG32, 0x00); + es8311_write_reg(ES8311_DAC_REG37, 0x08); + } else { + es8311_write_reg(ES8311_DAC_REG31, regv); + es8311_write_reg(ES8311_SYSTEM_REG12, 0x00); + } +} + +/* +* set es8311 into suspend mode +*/ +static void es8311_suspend(void) +{ + ESP_LOGI(TAG, "Enter into es8311_suspend()"); + es8311_write_reg(ES8311_DAC_REG32, 0x00); + es8311_write_reg(ES8311_ADC_REG17, 0x00); + es8311_write_reg(ES8311_SYSTEM_REG0E, 0xFF); + es8311_write_reg(ES8311_SYSTEM_REG12, 0x02); + es8311_write_reg(ES8311_SYSTEM_REG14, 0x00); + es8311_write_reg(ES8311_SYSTEM_REG0D, 0xFA); + es8311_write_reg(ES8311_ADC_REG15, 0x00); + es8311_write_reg(ES8311_DAC_REG37, 0x08); + es8311_write_reg(ES8311_GP_REG45, 0x01); +} + +esp_err_t es8311_codec_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg) +{ + + es8311_wire = tw; + + uint8_t datmp, regv; + int coeff; + esp_err_t ret = ESP_OK; + + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, 0x30); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x00); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG03, 0x10); + ret |= es8311_write_reg(ES8311_ADC_REG16, 0x24); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG04, 0x10); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG05, 0x00); + ret |= es8311_write_reg(ES8311_SYSTEM_REG0B, 0x00); + ret |= es8311_write_reg(ES8311_SYSTEM_REG0C, 0x00); + ret |= es8311_write_reg(ES8311_SYSTEM_REG10, 0x1F); + ret |= es8311_write_reg(ES8311_SYSTEM_REG11, 0x7F); + ret |= es8311_write_reg(ES8311_RESET_REG00, 0x80); + /* + * Set Codec into Master or Slave mode + */ + regv = es8311_read_reg(ES8311_RESET_REG00); + /* + * Set master/slave audio interface + */ + audio_hal_codec_i2s_iface_t *i2s_cfg = &(codec_cfg->i2s_iface); + switch (i2s_cfg->mode) { + case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */ + ESP_LOGI(TAG, "ES8311 in Master mode"); + regv |= 0x40; + break; + case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */ + ESP_LOGI(TAG, "ES8311 in Slave mode"); + regv &= 0xBF; + break; + default: + regv &= 0xBF; + } + ret |= es8311_write_reg(ES8311_RESET_REG00, regv); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, 0x3F); + /* + * Select clock source for internal mclk + */ + + int es8311_mclk_src = ES8311_MCLK_SOURCE; + switch (es8311_mclk_src) { + case FROM_MCLK_PIN: + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); + regv &= 0x7F; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); + break; + case FROM_SCLK_PIN: + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); + regv |= 0x80; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); + break; + default: + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); + regv &= 0x7F; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); + break; + } + int sample_fre = 0; + int mclk_fre = 0; + switch (i2s_cfg->samples) { + case AUDIO_HAL_08K_SAMPLES: + sample_fre = 8000; + break; + case AUDIO_HAL_11K_SAMPLES: + sample_fre = 11025; + break; + case AUDIO_HAL_16K_SAMPLES: + sample_fre = 16000; + break; + case AUDIO_HAL_22K_SAMPLES: + sample_fre = 22050; + break; + case AUDIO_HAL_24K_SAMPLES: + sample_fre = 24000; + break; + case AUDIO_HAL_32K_SAMPLES: + sample_fre = 32000; + break; + case AUDIO_HAL_44K_SAMPLES: + sample_fre = 44100; + break; + case AUDIO_HAL_48K_SAMPLES: + sample_fre = 48000; + break; + default: + ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre); + break; + } + mclk_fre = sample_fre * MCLK_DIV_FRE; + coeff = get_coeff(mclk_fre, sample_fre); + if (coeff < 0) { + ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre); + return ESP_FAIL; + } + /* + * Set clock parammeters + */ + if (coeff >= 0) { + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG02) & 0x07; + regv |= (coeff_div[coeff].pre_div - 1) << 5; + datmp = 0; + switch (coeff_div[coeff].pre_multi) { + case 1: + datmp = 0; + break; + case 2: + datmp = 1; + break; + case 4: + datmp = 2; + break; + case 8: + datmp = 3; + break; + default: + break; + } + + if (ES8311_MCLK_SOURCE == FROM_SCLK_PIN) { + datmp = 3; /* DIG_MCLK = LRCK * 256 = BCLK * 8 */ + } + regv |= (datmp) << 3; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG05) & 0x00; + regv |= (coeff_div[coeff].adc_div - 1) << 4; + regv |= (coeff_div[coeff].dac_div - 1) << 0; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG05, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG03) & 0x80; + regv |= coeff_div[coeff].fs_mode << 6; + regv |= coeff_div[coeff].adc_osr << 0; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG03, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG04) & 0x80; + regv |= coeff_div[coeff].dac_osr << 0; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG04, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG07) & 0xC0; + regv |= coeff_div[coeff].lrck_h << 0; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG07, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG08) & 0x00; + regv |= coeff_div[coeff].lrck_l << 0; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG08, regv); + + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06) & 0xE0; + if (coeff_div[coeff].bclk_div < 19) { + regv |= (coeff_div[coeff].bclk_div - 1) << 0; + } else { + regv |= (coeff_div[coeff].bclk_div) << 0; + } + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); + } + + /* + * mclk inverted or not + */ + if (INVERT_MCLK) { + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); + regv |= 0x40; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); + } else { + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); + regv &= ~(0x40); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); + } + /* + * sclk inverted or not + */ + if (INVERT_SCLK) { + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06); + regv |= 0x20; + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); + } else { + regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06); + regv &= ~(0x20); + ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); + } + + ret |= es8311_write_reg(ES8311_SYSTEM_REG13, 0x10); + ret |= es8311_write_reg(ES8311_ADC_REG1B, 0x0A); + ret |= es8311_write_reg(ES8311_ADC_REG1C, 0x6A); + + return ESP_OK; +} + +esp_err_t es8311_codec_deinit() +{ + // i2c_bus_delete(i2c_handle); + return ESP_OK; +} + +esp_err_t es8311_config_fmt(es_i2s_fmt_t fmt) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0, dac_iface = 0; + dac_iface = es8311_read_reg(ES8311_SDPIN_REG09); + adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A); + switch (fmt) { + case AUDIO_HAL_I2S_NORMAL: + ESP_LOGD(TAG, "ES8311 in I2S Format"); + dac_iface &= 0xFC; + adc_iface &= 0xFC; + break; + case AUDIO_HAL_I2S_LEFT: + case AUDIO_HAL_I2S_RIGHT: + ESP_LOGD(TAG, "ES8311 in LJ Format"); + adc_iface &= 0xFC; + dac_iface &= 0xFC; + adc_iface |= 0x01; + dac_iface |= 0x01; + break; + case AUDIO_HAL_I2S_DSP: + ESP_LOGD(TAG, "ES8311 in DSP-B Format"); + adc_iface &= 0xDC; + dac_iface &= 0xDC; + adc_iface |= 0x23; + dac_iface |= 0x23; + break; + default: + dac_iface &= 0xFC; + adc_iface &= 0xFC; + break; + } + ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); + ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); + + return ret; +} + +esp_err_t es8311_set_bits_per_sample(audio_hal_iface_bits_t bits) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0, dac_iface = 0; + dac_iface = es8311_read_reg(ES8311_SDPIN_REG09); + adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A); + switch (bits) { + case AUDIO_HAL_BIT_LENGTH_16BITS: + dac_iface |= 0x0c; + adc_iface |= 0x0c; + break; + case AUDIO_HAL_BIT_LENGTH_24BITS: + break; + case AUDIO_HAL_BIT_LENGTH_32BITS: + dac_iface |= 0x10; + adc_iface |= 0x10; + break; + default: + dac_iface |= 0x0c; + adc_iface |= 0x0c; + break; + + } + ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); + ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); + + return ret; +} + +esp_err_t es8311_codec_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) +{ + int ret = ESP_OK; + ret |= es8311_set_bits_per_sample(iface->bits); + ret |= es8311_config_fmt((es_i2s_fmt_t)iface->fmt); + return ret; +} + +esp_err_t es8311_codec_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) +{ + esp_err_t ret = ESP_OK; + es_module_t es_mode = ES_MODULE_MIN; + + switch (mode) { + case AUDIO_HAL_CODEC_MODE_ENCODE: + es_mode = ES_MODULE_ADC; + break; + case AUDIO_HAL_CODEC_MODE_LINE_IN: + es_mode = ES_MODULE_LINE; + break; + case AUDIO_HAL_CODEC_MODE_DECODE: + es_mode = ES_MODULE_DAC; + break; + case AUDIO_HAL_CODEC_MODE_BOTH: + es_mode = ES_MODULE_ADC_DAC; + break; + default: + es_mode = ES_MODULE_DAC; + ESP_LOGW(TAG, "Codec mode not support, default is decode mode"); + break; + } + + if (ctrl_state == AUDIO_HAL_CTRL_START) { + ret |= es8311_start(es_mode); + } else { + ESP_LOGW(TAG, "The codec is about to stop"); + ret |= es8311_stop(es_mode); + } + + return ret; +} + +esp_err_t es8311_start(es_module_t mode) +{ + esp_err_t ret = ESP_OK; + uint8_t adc_iface = 0, dac_iface = 0; + + dac_iface = es8311_read_reg(ES8311_SDPIN_REG09) & 0xBF; + adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A) & 0xBF; + adc_iface |= BIT(6); + dac_iface |= BIT(6); + + if (mode == ES_MODULE_LINE) { + ESP_LOGE(TAG, "The codec es8311 doesn't support ES_MODULE_LINE mode"); + return ESP_FAIL; + } + if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { + adc_iface &= ~(BIT(6)); + } + if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { + dac_iface &= ~(BIT(6)); + } + + ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); + ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); + + ret |= es8311_write_reg(ES8311_ADC_REG17, 0xBF); + ret |= es8311_write_reg(ES8311_SYSTEM_REG0E, 0x02); + ret |= es8311_write_reg(ES8311_SYSTEM_REG12, 0x00); + ret |= es8311_write_reg(ES8311_SYSTEM_REG14, 0x1A); + + /* + * pdm dmic enable or disable + */ + int regv = 0; + if (IS_DMIC) { + regv = es8311_read_reg(ES8311_SYSTEM_REG14); + regv |= 0x40; + ret |= es8311_write_reg(ES8311_SYSTEM_REG14, regv); + } else { + regv = es8311_read_reg(ES8311_SYSTEM_REG14); + regv &= ~(0x40); + ret |= es8311_write_reg(ES8311_SYSTEM_REG14, regv); + } + + ret |= es8311_write_reg(ES8311_SYSTEM_REG0D, 0x01); + ret |= es8311_write_reg(ES8311_ADC_REG15, 0x40); + ret |= es8311_write_reg(ES8311_DAC_REG37, 0x48); + ret |= es8311_write_reg(ES8311_GP_REG45, 0x00); + + return ret; +} + +esp_err_t es8311_stop(es_module_t mode) +{ + esp_err_t ret = ESP_OK; + es8311_suspend(); + return ret; +} + +esp_err_t es8311_codec_set_voice_volume(int volume) +{ + esp_err_t res = ESP_OK; + if (volume < 0) { + volume = 0; + } else if (volume > 100) { + volume = 100; + } + int vol = (volume) * 2550 / 1000; + ESP_LOGD(TAG, "SET: volume:%d", vol); + es8311_write_reg(ES8311_DAC_REG32, vol); + return res; +} + +esp_err_t es8311_codec_get_voice_volume(int *volume) +{ + esp_err_t res = ESP_OK; + int regv = 0; + regv = es8311_read_reg(ES8311_DAC_REG32); + if (regv == ESP_FAIL) { + *volume = 0; + res = ESP_FAIL; + } else { + *volume = regv * 100 / 256; + } + ESP_LOGD(TAG, "GET: res:%d, volume:%d", regv, *volume); + return res; +} + +esp_err_t es8311_set_voice_mute(bool enable) +{ + ESP_LOGD(TAG, "Es8311SetVoiceMute volume:%d", enable); + es8311_mute(enable); + return ESP_OK; +} + +esp_err_t es8311_get_voice_mute(int *mute) +{ + esp_err_t res = ESP_OK; + uint8_t reg = 0; + res = es8311_read_reg(ES8311_DAC_REG31); + if (res != ESP_FAIL) { + reg = (res & 0x20) >> 5; + } + *mute = reg; + return res; +} + +esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db) +{ + esp_err_t res = ESP_OK; + res = es8311_write_reg(ES8311_ADC_REG16, gain_db); // MIC gain scale + return res; +} + +void es8311_read_all() +{ + for (int i = 0; i < 0x4A; i++) { + uint8_t reg = es8311_read_reg(i); + ets_printf("REG:%02x, %02x\n", reg, i); + } +} + +esp_err_t es8311_set_channel(int is_right) +{ + uint8_t reg_val = es8311_read_reg(ES8311_SDPIN_REG09); + if (is_right) { + reg_val |= 0b10000000; + } else { + reg_val &= 0b01111111; + } + return es8311_write_reg(ES8311_SDPIN_REG09, reg_val); +} + +#endif diff --git a/lib/lib_audio/es8311/src/es8311.h b/lib/lib_audio/es8311/src/es8311.h new file mode 100644 index 000000000..609357987 --- /dev/null +++ b/lib/lib_audio/es8311/src/es8311.h @@ -0,0 +1,285 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2019 + * + * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _ES8311_H +#define _ES8311_H + +#include "audio_hal.h" +#include "esp_types.h" +#include "esxxx_common.h" + + +/* ES8311 address + * 0x32:CE=1;0x30:CE=0 + */ +#define ES8311_ADDR 0x18 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ES8311_REGISTER NAME_REG_REGISTER ADDRESS + */ +#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/ + +/* + * Clock Scheme Register definition + */ +#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */ +#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */ +#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */ +#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */ +#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */ +#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */ +#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */ +#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */ +/* + * SDP + */ +#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */ +#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */ +/* + * SYSTEM + */ +#define ES8311_SYSTEM_REG0B 0x0B /* system */ +#define ES8311_SYSTEM_REG0C 0x0C /* system */ +#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */ +#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */ +#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */ +#define ES8311_SYSTEM_REG10 0x10 /* system */ +#define ES8311_SYSTEM_REG11 0x11 /* system */ +#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */ +#define ES8311_SYSTEM_REG13 0x13 /* system */ +#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */ +/* + * ADC + */ +#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */ +#define ES8311_ADC_REG16 0x16 /* ADC */ +#define ES8311_ADC_REG17 0x17 /* ADC, volume */ +#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */ +#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */ +#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */ +#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */ +#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */ +/* + * DAC + */ +#define ES8311_DAC_REG31 0x31 /* DAC, mute */ +#define ES8311_DAC_REG32 0x32 /* DAC, volume */ +#define ES8311_DAC_REG33 0x33 /* DAC, offset */ +#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */ +#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */ +#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */ +/* + *GPIO + */ +#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */ +#define ES8311_GP_REG45 0x45 /* GP CONTROL */ +/* + * CHIP + */ +#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ +#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */ +#define ES8311_CHVER_REGFF 0xFF /* VERSION */ +#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ + +#define ES8311_MAX_REGISTER 0xFF + +typedef enum { + ES8311_MIC_GAIN_MIN = -1, + ES8311_MIC_GAIN_0DB, + ES8311_MIC_GAIN_6DB, + ES8311_MIC_GAIN_12DB, + ES8311_MIC_GAIN_18DB, + ES8311_MIC_GAIN_24DB, + ES8311_MIC_GAIN_30DB, + ES8311_MIC_GAIN_36DB, + ES8311_MIC_GAIN_42DB, + ES8311_MIC_GAIN_MAX +} es8311_mic_gain_t; + +/* + * @brief Initialize ES8311 codec chip + * + * @param codec_cfg configuration of ES8311 + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_codec_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); + +/** + * @brief Deinitialize ES8311 codec chip + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_codec_deinit(void); + +/** + * @brief Control ES8311 codec chip + * + * @param mode codec mode + * @param ctrl_state start or stop decode or encode progress + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es8311_codec_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); + +/** + * @brief Configure ES8311 codec mode and I2S interface + * + * @param mode codec mode + * @param iface I2S config + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es8311_codec_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); + +/** + * @brief Configure ES8311 DAC mute or not. Basically you can use this function to mute the output or unmute + * + * @param enable enable(1) or disable(0) + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es8311_set_voice_mute(bool enable); + +/** + * @brief Set voice volume + * + * @param volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_codec_set_voice_volume(int volume); + +/** + * @brief Get voice volume + * + * @param[out] *volume: voice volume (0~100) + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_codec_get_voice_volume(int *volume); + +/** + * @brief Configure ES8311 I2S format + * + * @param cfg: ES8388 I2S format + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_config_fmt(es_i2s_fmt_t fmt); + +/** + * @brief Configure ES8311 data sample bits + * + * @param bit_per_sample: bit number of per sample + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_set_bits_per_sample(audio_hal_iface_bits_t bits); + +/** + * @brief Start ES8311 codec chip + * + * @param mode: set ADC or DAC or both + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_start(es_module_t mode); + +/** + * @brief Stop ES8311 codec chip + * + * @param mode: set ADC or DAC or both + * + * @return + * - ESP_OK + * - ESP_FAIL + */ +esp_err_t es8311_stop(es_module_t mode); + +/** + * @brief Get ES8311 DAC mute status + * + * @return + * - ESP_FAIL + * - ESP_OK + */ +esp_err_t es8311_get_voice_mute(int *mute); + +/** + * @brief Set ES8311 mic gain + * + * @param gain db of mic gain + * + * @return + * - ESP_FAIL Parameter error + * - ESP_OK Success + */ +esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db); + +/** + * @brief Print all ES8311 registers + * + * @return + * - void + */ +void es8311_read_all(); + +/** + * @brief + * + * @param is_right + * @return esp_err_t + */ +esp_err_t es8311_set_channel(int is_right); + +#ifdef __cplusplus +} +#endif + +#endif From 7d9c51fb796e9951a197902f9162e761b1920c85 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 Jul 2022 09:13:49 +0200 Subject: [PATCH 097/219] audio i2s pins --- tasmota/language/af_AF.h | 9 ++++++++- tasmota/language/bg_BG.h | 8 +++++++- tasmota/language/cs_CZ.h | 8 +++++++- tasmota/language/de_DE.h | 9 ++++++++- tasmota/language/el_GR.h | 9 ++++++++- tasmota/language/en_GB.h | 6 ++++++ tasmota/language/es_ES.h | 8 +++++++- tasmota/language/fr_FR.h | 8 +++++++- tasmota/language/fy_NL.h | 8 +++++++- tasmota/language/he_HE.h | 8 +++++++- tasmota/language/hu_HU.h | 6 ++++++ tasmota/language/it_IT.h | 6 ++++++ tasmota/language/ko_KO.h | 8 +++++++- tasmota/language/nl_NL.h | 8 +++++++- tasmota/language/pl_PL.h | 8 +++++++- tasmota/language/pt_BR.h | 8 +++++++- tasmota/language/pt_PT.h | 8 +++++++- tasmota/language/ro_RO.h | 8 +++++++- tasmota/language/ru_RU.h | 8 +++++++- tasmota/language/sk_SK.h | 8 +++++++- tasmota/language/sv_SE.h | 8 +++++++- tasmota/language/tr_TR.h | 8 +++++++- tasmota/language/uk_UA.h | 8 +++++++- tasmota/language/vi_VN.h | 8 +++++++- tasmota/language/zh_CN.h | 8 +++++++- tasmota/language/zh_TW.h | 8 +++++++- 26 files changed, 182 insertions(+), 23 deletions(-) diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 61e9e5c99..6ca9d320f 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -878,10 +878,17 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" + +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 091ef3fe5..a8efc271b 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 5eb89c9a4..c8ef289b2 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 7e574e0c8..70b3334df 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -878,10 +878,17 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" + +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 9bb00cc63..e72267baf 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -878,10 +878,17 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" + +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 958819a01..7994e2137 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -882,6 +882,12 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 3ff22557d..db311000e 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 7abc864ba..83c8cca0c 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index c155bffa9..3d0ff7549 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 96cf98eb5..6e9b40ffe 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 0d5ddbca8..73d0fed27 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -882,6 +882,12 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 6a4ecb8c8..6eb0c85df 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -882,6 +882,12 @@ #define D_SENSOR_CM11_RX "CM110x - RX" #define D_SENSOR_FLOWRATEMETER "Portata" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 091c530e5..e4301665f 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index bd5dcceb8..058cf32a4 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 59c3059c8..080b3d14f 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 11ebd3d13..ef3b038bb 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 1da1f2e27..70fd6b7d3 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index b1921be4b..8adb8cfa0 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index d042e83f5..b141139de 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 10e5aaacf..632b6d939 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 1b7a985ee..6e98dee29 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 1127a96c4..3c081de22 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 7d9fc0bc5..6a28205fe 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 1116e810b..55d5362ab 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 751959218..7ad9dd77d 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 5945993e8..d6bd3ba8f 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -878,10 +878,16 @@ #define D_GPIO_SHIFT595_RCLK "74x595 RCLK" #define D_GPIO_SHIFT595_OE "74x595 OE" #define D_GPIO_SHIFT595_SER "74x595 SER" -#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_TX "CM110x TX" #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" +#define D_SENSOR_I2S_MCLK "I2S_MCLK" +#define D_SENSOR_I2S_BCLK "I2S_BCLK" +#define D_SENSOR_I2S_WS "I2S_WS" +#define D_SENSOR_I2S_DIN "I2S_DIN" +#define D_SENSOR_I2S_DOUT "I2S_DOUT" + // Units #define D_UNIT_AMPERE "安培" #define D_UNIT_CELSIUS "C" From 11c72a985f2b91a0545e6916bcc0c706d01fe370 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 Jul 2022 09:18:27 +0200 Subject: [PATCH 098/219] i2s audio pins --- tasmota/include/tasmota_template.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 58954bd07..6919a1a72 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -193,6 +193,7 @@ enum UserSelectablePins { GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable + GPIO_I2S_MCLK, GPIO_I2S_BCLK, GPIO_I2S_WS, GPIO_I2S_DIN, GPIO_I2S_DOUT, GPIO_SENSOR_END }; // Error as warning to rethink GPIO usage @@ -433,6 +434,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_DFR562_BUSY "|" D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" + D_SENSOR_I2S_MCLK "|" D_SENSOR_I2S_BCLK "|" D_SENSOR_I2S_WS "|" D_SENSOR_I2S_DIN "|" D_SENSOR_I2S_DOUT "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -527,6 +529,14 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_I2S_IN_SLCT) + MAX_I2S, // I2C In Word Select #endif +#ifdef USE_I2S_AUDIO + AGPIO(GPIO_I2S_MCLK) + MAX_I2S, // I2S master clock + AGPIO(GPIO_I2S_BCLK) + MAX_I2S, // I2S bit clock + AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select + AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data + AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data +#endif + #ifdef USE_SPI AGPIO(GPIO_SPI_MISO) + MAX_SPI, // SPI MISO AGPIO(GPIO_SPI_MOSI) + MAX_SPI, // SPI MOSI From 3834075990a4f0b65fc1006260d42d1fd5052ab2 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 11 Jul 2022 09:59:27 +0200 Subject: [PATCH 099/219] ULP Support --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c835494..dd741cba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Support for Sonoff POWR3xxD and THR3xxD (#15856) - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi +- Support for ULP (ESP32) via Berry ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) From 26f55f752b65d56da0d8f0f62d114069262a7068 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 11 Jul 2022 10:09:02 +0200 Subject: [PATCH 100/219] Update changelogs with ULP --- CHANGELOG.md | 4 ++-- CODE_OWNERS.md | 2 +- RELEASENOTES.md | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd741cba4..fa7933a83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,8 @@ All notable changes to this project will be documented in this file. ## [12.0.2.3] ### Added - Support for Sonoff POWR3xxD and THR3xxD (#15856) -- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi -- Support for ULP (ESP32) via Berry +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D +- ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars (#15916) ### Changed - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) diff --git a/CODE_OWNERS.md b/CODE_OWNERS.md index bff3b2d2a..49a268bb7 100644 --- a/CODE_OWNERS.md +++ b/CODE_OWNERS.md @@ -60,7 +60,7 @@ In addition to @arendst the following code is mainly owned by: | xdrv_49_pid | Colin Law, Thomas Herrmann | xdrv_50_filesystem | @gemu, @barbudor | xdrv_51_bs814a2 | Peter Franck -| xdrv_52_berry | @s-hadinger +| xdrv_52_berry | @s-hadinger, @staars (ULP) | xdrv_53_projector_ctrl | Jan Bubík | xdrv_54_lvgl | @s-hadinger | xdrv_55_touch | @gemu, @s-hadinger diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b0b8156cf..0b41824aa 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -114,7 +114,8 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) -- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D +- ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars [#15916](https://github.com/arendst/Tasmota/issues/15916) ### Breaking Changed From 2825f8e50a9f9850b1b3daf6f46a74eb88b2c896 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 11 Jul 2022 14:05:02 +0200 Subject: [PATCH 101/219] sbox has blue, red inversion --- tasmota/displaydesc/esp32_s3_sbox_display.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/displaydesc/esp32_s3_sbox_display.ini b/tasmota/displaydesc/esp32_s3_sbox_display.ini index 9d9f49264..dcf0252cc 100644 --- a/tasmota/displaydesc/esp32_s3_sbox_display.ini +++ b/tasmota/displaydesc/esp32_s3_sbox_display.ini @@ -28,10 +28,10 @@ E1,0F,00,0E,14,03,11,07,31,C1,48,08,0F,0C,31,36,0F :O,29 :A,2A,2B,2C,16 :R,36 -:0,48,00,00,00 -:1,28,00,00,01 -:2,88,00,00,02 -:3,E8,00,00,03 +:0,40,00,00,00 +:1,20,00,00,01 +:2,80,00,00,02 +:3,E0,00,00,03 :i,21,20 :b,01 # From cc9c6299ca4682b6c42e63c9b551d040b70f782a Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 11 Jul 2022 15:49:57 +0200 Subject: [PATCH 102/219] Consolidate I2S GPIOs --- tasmota/berry/include/be_gpio_defines.h | 12 ++++----- tasmota/include/tasmota_template.h | 35 ++++++++++--------------- tasmota/language/af_AF.h | 20 +++++--------- tasmota/language/bg_BG.h | 19 +++++--------- tasmota/language/cs_CZ.h | 19 +++++--------- tasmota/language/de_DE.h | 20 +++++--------- tasmota/language/el_GR.h | 20 +++++--------- tasmota/language/en_GB.h | 19 +++++--------- tasmota/language/es_ES.h | 19 +++++--------- tasmota/language/fr_FR.h | 19 +++++--------- tasmota/language/fy_NL.h | 19 +++++--------- tasmota/language/he_HE.h | 19 +++++--------- tasmota/language/hu_HU.h | 19 +++++--------- tasmota/language/it_IT.h | 19 +++++--------- tasmota/language/ko_KO.h | 19 +++++--------- tasmota/language/nl_NL.h | 18 +++++-------- tasmota/language/pl_PL.h | 19 +++++--------- tasmota/language/pt_BR.h | 19 +++++--------- tasmota/language/pt_PT.h | 19 +++++--------- tasmota/language/ro_RO.h | 19 +++++--------- tasmota/language/ru_RU.h | 19 +++++--------- tasmota/language/sk_SK.h | 19 +++++--------- tasmota/language/sv_SE.h | 19 +++++--------- tasmota/language/tr_TR.h | 19 +++++--------- tasmota/language/uk_UA.h | 19 +++++--------- tasmota/language/vi_VN.h | 19 +++++--------- tasmota/language/zh_CN.h | 19 +++++--------- tasmota/language/zh_TW.h | 19 +++++--------- tools/lv_gpio/lv_gpio_enum.h | 12 ++++----- 29 files changed, 207 insertions(+), 348 deletions(-) diff --git a/tasmota/berry/include/be_gpio_defines.h b/tasmota/berry/include/be_gpio_defines.h index 359ced5ab..292445d32 100644 --- a/tasmota/berry/include/be_gpio_defines.h +++ b/tasmota/berry/include/be_gpio_defines.h @@ -96,12 +96,12 @@ const be_const_member_t lv_gpio_constants[] = { { "HX711_SCK", (int32_t) GPIO_HX711_SCK }, { "I2C_SCL", (int32_t) GPIO_I2C_SCL }, { "I2C_SDA", (int32_t) GPIO_I2C_SDA }, - { "I2S_IN_CLK", (int32_t) GPIO_I2S_IN_CLK }, - { "I2S_IN_DATA", (int32_t) GPIO_I2S_IN_DATA }, - { "I2S_IN_SLCT", (int32_t) GPIO_I2S_IN_SLCT }, - { "I2S_OUT_CLK", (int32_t) GPIO_I2S_OUT_CLK }, - { "I2S_OUT_DATA", (int32_t) GPIO_I2S_OUT_DATA }, - { "I2S_OUT_SLCT", (int32_t) GPIO_I2S_OUT_SLCT }, + { "I2S_IN_CLK", (int32_t) GPIO_I2S_BCLK_IN }, + { "I2S_IN_DATA", (int32_t) GPIO_I2S_DIN }, + { "I2S_IN_SLCT", (int32_t) GPIO_I2S_WS_IN }, + { "I2S_OUT_CLK", (int32_t) GPIO_I2S_BCLK }, + { "I2S_OUT_DATA", (int32_t) GPIO_I2S_DOUT }, + { "I2S_OUT_SLCT", (int32_t) GPIO_I2S_WS }, { "IBEACON_RX", (int32_t) GPIO_IBEACON_RX }, { "IBEACON_TX", (int32_t) GPIO_IBEACON_TX }, { "IEM3000_RX", (int32_t) GPIO_IEM3000_RX }, diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 6919a1a72..8fe2cb640 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -166,8 +166,8 @@ enum UserSelectablePins { #ifdef ESP32 GPIO_KEY1_PD, GPIO_KEY1_INV_PD, GPIO_SWT1_PD, #endif - GPIO_I2S_OUT_DATA, GPIO_I2S_OUT_CLK, GPIO_I2S_OUT_SLCT, - GPIO_I2S_IN_DATA, GPIO_I2S_IN_CLK, GPIO_I2S_IN_SLCT, + GPIO_I2S_DOUT, GPIO_I2S_BCLK, GPIO_I2S_WS, + GPIO_I2S_DIN, GPIO_I2S_BCLK_IN, GPIO_I2S_WS_IN, GPIO_INTERRUPT, GPIO_MCP2515_CS, // MCP2515 Chip Select GPIO_HRG15_TX, GPIO_HRG15_RX, // Hydreon RG-15 rain sensor serial interface @@ -193,7 +193,7 @@ enum UserSelectablePins { GPIO_MP3_DFR562_BUSY, // RB-DFR-562, DFPlayer Mini MP3 Player busy flag GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable - GPIO_I2S_MCLK, GPIO_I2S_BCLK, GPIO_I2S_WS, GPIO_I2S_DIN, GPIO_I2S_DOUT, + GPIO_I2S_MCLK, GPIO_SENSOR_END }; // Error as warning to rethink GPIO usage @@ -402,8 +402,8 @@ const char kSensorNames[] PROGMEM = #ifdef ESP32 D_SENSOR_BUTTON "_d|" D_SENSOR_BUTTON "_id|" D_SENSOR_SWITCH "_d|" #endif - D_SENSOR_I2S_OUT_DATA "|" D_SENSOR_I2S_OUT_CLK "|" D_SENSOR_I2S_OUT_SLCT "|" - D_SENSOR_I2S_IN_DATA "|" D_SENSOR_I2S_IN_CLK "|" D_SENSOR_I2S_IN_SLCT "|" + D_SENSOR_I2S_DOUT "|" D_SENSOR_I2S_BCLK "|" D_SENSOR_I2S_WS "|" + D_SENSOR_I2S_DIN "|" D_SENSOR_I2S_BCLK_IN "|" D_SENSOR_I2S_WS_IN "|" D_SENSOR_INTERRUPT "|" D_SENSOR_MCP2515_CS "|" D_SENSOR_HRG15_TX "|" D_SENSOR_HRG15_RX "|" @@ -434,7 +434,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_DFR562_BUSY "|" D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" - D_SENSOR_I2S_MCLK "|" D_SENSOR_I2S_BCLK "|" D_SENSOR_I2S_WS "|" D_SENSOR_I2S_DIN "|" D_SENSOR_I2S_DOUT "|" + D_SENSOR_I2S_MCLK "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -520,21 +520,14 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_I2C_SDA) + MAX_I2C, // I2C SDA #endif -#ifdef USE_I2S - AGPIO(GPIO_I2S_OUT_DATA) + MAX_I2S, // I2S Out Data - AGPIO(GPIO_I2S_OUT_CLK) + MAX_I2S, // I2C Out Clock - AGPIO(GPIO_I2S_OUT_SLCT) + MAX_I2S, // I2C Out Word Select - AGPIO(GPIO_I2S_IN_DATA) + MAX_I2S, // I2S In Data - AGPIO(GPIO_I2S_IN_CLK) + MAX_I2S, // I2C In Clock - AGPIO(GPIO_I2S_IN_SLCT) + MAX_I2S, // I2C In Word Select -#endif - -#ifdef USE_I2S_AUDIO - AGPIO(GPIO_I2S_MCLK) + MAX_I2S, // I2S master clock - AGPIO(GPIO_I2S_BCLK) + MAX_I2S, // I2S bit clock - AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select - AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data - AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data +#if defined(USE_I2S_AUDIO) || defined (USE_I2S) + AGPIO(GPIO_I2S_MCLK) + MAX_I2S, // I2S master clock + AGPIO(GPIO_I2S_BCLK) + MAX_I2S, // I2S bit clock + AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select + AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data + AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data + AGPIO(GPIO_I2S_BCLK_IN) + MAX_I2S, // I2S bit clock in + AGPIO(GPIO_I2S_WS_IN) + MAX_I2S, // I2S word select in #endif #ifdef USE_SPI diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 6ca9d320f..b0d8a4dbd 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562_BUSY "MP3 Bezet" @@ -882,13 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" - -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index a8efc271b..9085b9f6c 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index c8ef289b2..648ae398e 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 70b3334df..4a91c56c6 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,13 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" - -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index e72267baf..9c57f5342 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,13 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" - -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 7994e2137..f9db613df 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index db311000e..402756730 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 83c8cca0c..46e552dbf 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index 3d0ff7549..d73b512f1 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562_BUSY "MP3 Bezet" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 6e9b40ffe..e76545d7d 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "נגן מוזיקה" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 73d0fed27..e95b4f095 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 lejátszó" #define D_SENSOR_DFR562_BUSY "MP3 elfoglalt" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 6eb0c85df..922d98a80 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S - Dati out" -#define D_SENSOR_I2S_OUT_CLK "I2S - Clk out" -#define D_SENSOR_I2S_OUT_SLCT "I2S - Sel. out" -#define D_SENSOR_I2S_IN_DATA "I2S - Dati in" -#define D_SENSOR_I2S_IN_CLK "I2S - Clk in" -#define D_SENSOR_I2S_IN_SLCT "I2S - Sel. in" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "Riproduttore MP3" #define D_SENSOR_DFR562_BUSY "MP3 occupato" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x - RX" #define D_SENSOR_FLOWRATEMETER "Portata" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index e4301665f..62f4a01e4 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 058cf32a4..22314beed 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -624,12 +624,12 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S uit Data" -#define D_SENSOR_I2S_OUT_CLK "I2S uit Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S uit Slct" -#define D_SENSOR_I2S_IN_DATA "I2S in Data" -#define D_SENSOR_I2S_IN_CLK "I2S in Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S in Slct" +#define D_SENSOR_I2S_DOUT "I2S uit Data" +#define D_SENSOR_I2S_BCLK "I2S uit Clk" +#define D_SENSOR_I2S_WS "I2S uit Slct" +#define D_SENSOR_I2S_DIN "I2S in Data" +#define D_SENSOR_I2S_BCLK_IN "I2S in Clk" +#define D_SENSOR_I2S_WS_IN "I2S in Slct" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562_BUSY "MP3 Bezet" @@ -882,12 +882,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 080b3d14f..dde14e14c 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "Odtwarzacz MP3" #define D_SENSOR_DFR562_BUSY "MP3 zajęty" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index ef3b038bb..b8cfb91e7 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 70fd6b7d3..907a60fc1 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 8adb8cfa0..0cf495c2b 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index b141139de..9e1d17fce 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 632b6d939..af6d5ac01 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index 6e98dee29..c7098ce86 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 spelare" #define D_SENSOR_DFR562_BUSY "MP3 upptaget" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 3c081de22..63bd50a6f 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 6a28205fe..36bb725a3 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "А" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 55d5362ab..3ef76ce6a 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 7ad9dd77d..656b89efa 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "A" #define D_UNIT_CELSIUS "C" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index d6bd3ba8f..90ff0e879 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Player" #define D_SENSOR_DFR562_BUSY "MP3 Busy" @@ -882,12 +883,6 @@ #define D_SENSOR_CM11_RX "CM110x RX" #define D_SENSOR_FLOWRATEMETER "Flowrate" -#define D_SENSOR_I2S_MCLK "I2S_MCLK" -#define D_SENSOR_I2S_BCLK "I2S_BCLK" -#define D_SENSOR_I2S_WS "I2S_WS" -#define D_SENSOR_I2S_DIN "I2S_DIN" -#define D_SENSOR_I2S_DOUT "I2S_DOUT" - // Units #define D_UNIT_AMPERE "安培" #define D_UNIT_CELSIUS "C" diff --git a/tools/lv_gpio/lv_gpio_enum.h b/tools/lv_gpio/lv_gpio_enum.h index 0d2132307..11535f97a 100644 --- a/tools/lv_gpio/lv_gpio_enum.h +++ b/tools/lv_gpio/lv_gpio_enum.h @@ -265,12 +265,12 @@ INPUT = GPIO_INPUT KEY1_PD = GPIO_KEY1_PD KEY1_INV_PD = GPIO_KEY1_INV_PD SWT1_PD = GPIO_SWT1_PD -I2S_OUT_DATA = GPIO_I2S_OUT_DATA -I2S_OUT_CLK = GPIO_I2S_OUT_CLK -I2S_OUT_SLCT = GPIO_I2S_OUT_SLCT -I2S_IN_DATA = GPIO_I2S_IN_DATA -I2S_IN_CLK = GPIO_I2S_IN_CLK -I2S_IN_SLCT = GPIO_I2S_IN_SLCT +I2S_OUT_DATA = GPIO_I2S_DOUT +I2S_OUT_CLK = GPIO_I2S_BCLK +I2S_OUT_SLCT = GPIO_I2S_WS +I2S_IN_DATA = GPIO_I2S_DIN +I2S_IN_CLK = GPIO_I2S_BCLK_IN +I2S_IN_SLCT = GPIO_I2S_WS_IN INTERRUPT = GPIO_INTERRUPT MCP2515_CS = GPIO_MCP2515_CS From 43371ce1967345cf7758823ef4e1da3d84599cc3 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 11 Jul 2022 15:54:47 +0200 Subject: [PATCH 103/219] Extract USE_I2S from not used USE_I2S_AUDIO gpios --- tasmota/include/tasmota_template.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 8fe2cb640..f47ebace5 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -526,6 +526,8 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_I2S_WS) + MAX_I2S, // I2S word select AGPIO(GPIO_I2S_DIN) + MAX_I2S, // I2S IN Data AGPIO(GPIO_I2S_DOUT) + MAX_I2S, // I2S Out Data +#endif +#ifdef USE_I2S AGPIO(GPIO_I2S_BCLK_IN) + MAX_I2S, // I2S bit clock in AGPIO(GPIO_I2S_WS_IN) + MAX_I2S, // I2S word select in #endif From 74bd5a14258e0e4aeb9c9dec5e2271efc5f0721f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 11 Jul 2022 16:17:09 +0200 Subject: [PATCH 104/219] Fix NL compilation --- tasmota/language/nl_NL.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 22314beed..ebad56dc5 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -624,12 +624,13 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_DOUT "I2S uit Data" -#define D_SENSOR_I2S_BCLK "I2S uit Clk" -#define D_SENSOR_I2S_WS "I2S uit Slct" -#define D_SENSOR_I2S_DIN "I2S in Data" -#define D_SENSOR_I2S_BCLK_IN "I2S in Clk" -#define D_SENSOR_I2S_WS_IN "I2S in Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "MP3 Speler" #define D_SENSOR_DFR562_BUSY "MP3 Bezet" From c040844d3b04ae0f134220ae51a2154e037045b2 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Mon, 11 Jul 2022 17:44:30 +0200 Subject: [PATCH 105/219] Update italian language Please check and merge --- tasmota/language/it_IT.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 922d98a80..9842ab470 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.4.0.1 - Last update 30.05.2022 + * Updated until v9.4.0.1 - Last update 11.07.2022 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -622,15 +622,15 @@ #define D_SENSOR_SI7021 "SI7021" #define D_SENSOR_MS01 "MS01" #define D_SENSOR_DS18X20 "DS18x20" -#define D_SENSOR_I2C_SCL "I2C SCL" -#define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_MCLK "I2S MCLK" -#define D_SENSOR_I2S_BCLK "I2S BCLK" -#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" -#define D_SENSOR_I2S_WS "I2S WS" -#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" -#define D_SENSOR_I2S_DIN "I2S DIN" -#define D_SENSOR_I2S_DOUT "I2S DOUT" +#define D_SENSOR_I2C_SCL "I2C - SCL" +#define D_SENSOR_I2C_SDA "I2C - SDA" +#define D_SENSOR_I2S_MCLK "I2S - MCLK" +#define D_SENSOR_I2S_BCLK "I2S - BCLK" +#define D_SENSOR_I2S_WS_IN "I2S - BCLK IN" +#define D_SENSOR_I2S_WS "I2S - WS" +#define D_SENSOR_I2S_BCLK_IN "I2S - WS IN" +#define D_SENSOR_I2S_DIN "I2S - DIN" +#define D_SENSOR_I2S_DOUT "I2S - DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "Riproduttore MP3" #define D_SENSOR_DFR562_BUSY "MP3 occupato" From f9e0431b884b2ddf2d8f241a0f5cdc27208b2757 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:27:17 +0200 Subject: [PATCH 106/219] set used flash mode in factory.bin --- pio-tools/post_esp32.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index 2a4be8569..7c7b8c1ea 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -132,12 +132,18 @@ def esp32_create_combined_bin(source, target, env): else: esp32_fetch_safeboot_bin(tasmota_platform) flash_size = env.BoardConfig().get("upload.flash_size", "4MB") + flash_freq = "keep" + flash_mode = env.BoardConfig().get("build.flash_mode", "dout") cmd = [ "--chip", chip, "merge_bin", "-o", new_file_name, + "--flash_mode", + flash_mode, + "--flash_freq", + flash_freq, "--flash_size", flash_size, ] From 3e8428e857016395945e8eb170d7954ceb60baf1 Mon Sep 17 00:00:00 2001 From: Marius Bezuidenhout Date: Wed, 13 Jul 2022 11:23:03 +0200 Subject: [PATCH 107/219] iem3xxx series energy sensor fixes --- .../tasmota_xnrg_energy/xnrg_16_iem3000.ino | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino b/tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino index 3ee7f47da..10e3a5de1 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_16_iem3000.ino @@ -1,7 +1,7 @@ /* xnrg_16_iem3000.ino - Schneider Electric iEM3000 series Modbus energy meter support for Tasmota - Copyright (C) 2021 Marius Bezuidenhout + Copyright (C) 2022 Marius Bezuidenhout This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,22 +47,17 @@ const uint16_t Iem3000_start_addresses[] { 0x0bd3, // 3 . IEM3000_L1_VOLTAGE (2/Float32) [V] Voltage L1–N 0x0bd5, // 4 . IEM3000_L2_VOLTAGE (2/Float32) [V] Voltage L2–N 0x0bd7, // 5 . IEM3000_L3_VOLTAGE (2/Float32) [V] Voltage L3–N - 0x0bed, // 6 . IEM3000_P1_POWER (2/Float32) [KW] Active Power Phase 1 - 0x0bef, // 7 . IEM3000_P2_POWER (2/Float32) [KW] Active Power Phase 2 - 0x0bf1, // 8 . IEM3000_P3_POWER (2/Float32) [KW] Active Power Phase 3 + 0x0bed, // 6 . IEM3000_P1_POWER (2/Float32) [kW] Active Power Phase 1 + 0x0bef, // 7 . IEM3000_P2_POWER (2/Float32) [kW] Active Power Phase 2 + 0x0bf1, // 8 . IEM3000_P3_POWER (2/Float32) [kW] Active Power Phase 3 0x0c25, // 9 . IEM3000_FREQUENCY (2/Float32) [Hz] Frequency -#ifdef IEM3000_IEM3155 - 0xb02b, // 10 . IEM3000_TOTAL_ACTIVE (2/Float32) [Wh] Total Active Energy Import -#else - 0xb02b, // 10 . IEM3000_TOTAL_ACTIVE (4/Int64) [Wh] Total Active Energy Import -#endif + 0x0dbd, // 10 . IEM3000_L1_IMPORT (4/Int64) [Wh] Active Energy Import Phase 1 + 0x0dc1, // 11 . IEM3000_L1_IMPORT (4/Int64) [Wh] Active Energy Import Phase 1 + 0x0dc5, // 12 . IEM3000_L1_IMPORT (4/Int64) [Wh] Active Energy Import Phase 1 + 0x0c83, // 13 . IEM3000_IMPORT (4/Int64) [Wh] Total Active Energy Import }; -#ifdef IEM3000_IEM3155 - #define FLOAT_ParamLimit 11 -#else - #define FLOAT_ParamLimit 10 -#endif +#define FLOAT_ParamLimit 10 struct IEM3000 { uint8_t read_state = 0; @@ -137,27 +132,15 @@ void IEM3000Every250ms(void) break; case 6: -#ifdef IEM3000_IEM3155 Energy.active_power[0] = value*1000; -#else - Energy.active_power[0] = value; -#endif break; case 7: -#ifdef IEM3000_IEM3155 Energy.active_power[1] = value*1000; -#else - Energy.active_power[1] = value; -#endif break; case 8: -#ifdef IEM3000_IEM3155 Energy.active_power[2] = value*1000; -#else - Energy.active_power[2] = value; -#endif break; case 9: @@ -165,11 +148,18 @@ void IEM3000Every250ms(void) break; case 10: -#ifdef IEM3000_IEM3155 Energy.import_active[0] = value; -#else - Energy.import_active[0] = value64 * 0.001f; // 1125 => 1.125 -#endif + break; + + case 11: + Energy.import_active[1] = value; + break; + + case 12: + Energy.import_active[2] = value; + break; + + case 13: EnergyUpdateTotal(); break; } From 861518679ac2868429f4296d11e8d43391080723 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:33:13 +0200 Subject: [PATCH 108/219] Optimize factory firmware generation to use all settings from boards.json manifest. Set boot flash modes correctly for all flash modes --- pio-tools/post_esp32.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index 7c7b8c1ea..fde5f30e7 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -132,8 +132,14 @@ def esp32_create_combined_bin(source, target, env): else: esp32_fetch_safeboot_bin(tasmota_platform) flash_size = env.BoardConfig().get("upload.flash_size", "4MB") - flash_freq = "keep" + flash_freq = env.BoardConfig().get("build.f_flash", "40000000L") + flash_freq = str(flash_freq).replace("L", "") + flash_freq = str(int(int(flash_freq) / 1000000)) + "m" flash_mode = env.BoardConfig().get("build.flash_mode", "dout") + if flash_mode == "qio": + flash_mode = "dio" + elif flash_mode == "qout": + flash_mode = "dout" cmd = [ "--chip", chip, From ef89614b743d5f9ef99c19680038218a17bf1a4f Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 15 Jul 2022 14:46:23 +0200 Subject: [PATCH 109/219] ad fixed is2 mclk --- lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp | 13 ++++++++----- lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h | 12 +++++++++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp index a01a7b999..b18f9ae39 100755 --- a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp +++ b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.cpp @@ -29,7 +29,7 @@ #include "AudioOutputI2S.h" #if defined(ESP32) || defined(ESP8266) -AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int use_apll) +AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int use_apll, uint8_t mult, uint32_t freq) { this->portNo = port; this->i2sOn = false; @@ -49,6 +49,8 @@ AudioOutputI2S::AudioOutputI2S(int port, int output_mode, int dma_buf_count, int bclkPin = 26; wclkPin = 25; doutPin = 22; + mcmult = mult; + mclk_freq = freq; SetGain(1.0); } @@ -84,7 +86,6 @@ bool AudioOutputI2S::SetPinout(int bclk, int wclk, int dout, int mclk, int din) mclkPin = mclk; dinPin = din; - if (i2sOn) return SetPinout(); @@ -238,10 +239,12 @@ bool AudioOutputI2S::begin(bool txDAC) .dma_buf_len = 128, .use_apll = use_apll, // Use audio PLL .tx_desc_auto_clear = true, - .fixed_mclk = 0, +#ifdef ESP32 + .fixed_mclk = (int)mclk_freq, //.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, - .mclk_multiple = I2S_MCLK_MULTIPLE_128, - .bits_per_chan = I2S_BITS_PER_CHAN_16BIT, + .mclk_multiple = (i2s_mclk_multiple_t)mcmult, +#endif + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT }; audioLogger->printf("+%d %p\n", portNo, &i2s_config_dac); if (i2s_driver_install((i2s_port_t)portNo, &i2s_config_dac, 0, NULL) != ESP_OK) diff --git a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h index 272959bc0..f086cb11a 100755 --- a/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h +++ b/lib/lib_audio/ESP8266Audio/src/AudioOutputI2S.h @@ -28,11 +28,15 @@ #define I2S_PIN_NO_CHANGE -1 #endif +#ifdef ESP8266 +#define I2S_MCLK_MULTIPLE_DEFAULT 0 +#endif + class AudioOutputI2S : public AudioOutput { public: #if defined(ESP32) || defined(ESP8266) - AudioOutputI2S(int port=0, int output_mode=EXTERNAL_I2S, int dma_buf_count = 8, int use_apll=APLL_DISABLE); + AudioOutputI2S(int port=0, int output_mode=EXTERNAL_I2S, int dma_buf_count = 8, int use_apll=APLL_DISABLE, uint8_t mult=I2S_MCLK_MULTIPLE_DEFAULT, uint32_t freq=0); bool SetPinout(int bclkPin, int wclkPin, int doutPin, int mclk = I2S_PIN_NO_CHANGE, int din = I2S_PIN_NO_CHANGE); enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; @@ -69,6 +73,8 @@ class AudioOutputI2S : public AudioOutput uint8_t bclkPin; uint8_t wclkPin; uint8_t doutPin; - uint8_t dinPin; - uint8_t mclkPin; + int8_t dinPin; + int8_t mclkPin; + uint8_t mcmult; + uint32_t mclk_freq; }; From f3e6ef1558d4e1fe1e44336c2185cf51d030dfae Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 15 Jul 2022 14:47:34 +0200 Subject: [PATCH 110/219] fixes for s3 box --- .../tasmota_xdrv_driver/xdrv_42_i2s_audio.ino | 87 ++++++++++++++----- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino index 370b68187..5adc20597 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino @@ -105,6 +105,16 @@ struct AUDIO_I2S { } audio_i2s; +// because S3 box mclk severly disturbs WLAN +// we must slow down after each sound +#ifdef ESP32S3_BOX +#undef DOWNRATE +#define DOWNRATE audio_i2s.out->SetRate(1000); +#else +#undef DOWNRATE +#define DOWNRATE +#endif + #define MIC_CHANNELS 1 #ifdef USE_TTGO_WATCH @@ -249,11 +259,19 @@ AudioGeneratorTalkie *talkie = nullptr; } delete talkie; audio_i2s.out->stop(); + DOWNRATE AUDIO_PWR_OFF } #endif // USE_I2S_SAY_TIME +enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; +enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; + +#ifdef ESP8266 +#define I2S_MCLK_MULTIPLE_128 0 +#endif + int32_t I2S_Init_0(void) { audio_i2s.i2s_port = (i2s_port_t)0; @@ -280,7 +298,8 @@ int32_t I2S_Init_0(void) { #ifdef USE_I2S_NO_DAC audio_i2s.out = new AudioOutputI2SNoDAC(); #else - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); #endif // USE_I2S_NO_DAC audio_i2s.mclk = Pin(GPIO_I2S_MCLK); audio_i2s.bclk = Pin(GPIO_I2S_BCLK); @@ -292,7 +311,8 @@ int32_t I2S_Init_0(void) { #ifdef USE_I2S_NO_DAC audio_i2s.out = new AudioOutputI2SNoDAC(); #else - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); #endif // USE_I2S_NO_DAC audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); @@ -304,7 +324,7 @@ int32_t I2S_Init_0(void) { } #ifdef ESP8266 // esp8266 have fixed pins - if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout!= 3)) { + if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) { return -2; } #endif // ESP8266 @@ -312,11 +332,8 @@ int32_t I2S_Init_0(void) { audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); - AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); -#if defined(ESP32) && defined(ESP32S3_BOX) - S3boxInit(); -#endif #else @@ -330,12 +347,19 @@ int32_t I2S_Init_0(void) { return 0; } + + void I2S_Init(void) { + #if defined(ESP32) && defined(ESP32S3_BOX) + S3boxInit(); + #endif + if (I2S_Init_0()) { return; } + DOWNRATE audio_i2s.is2_volume=10; audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); audio_i2s.out->stop(); @@ -383,6 +407,7 @@ uint32_t SpeakerMic(uint8_t spkr) { I2S_Init_0(); audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); audio_i2s.out->stop(); + DOWNRATE } else { // config mic i2s_config_t i2s_config = { @@ -398,7 +423,7 @@ uint32_t SpeakerMic(uint8_t spkr) { .use_apll = 0, // Use audio PLL .tx_desc_auto_clear = true, .fixed_mclk = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 .bits_per_chan = I2S_BITS_PER_CHAN_16BIT }; @@ -406,7 +431,16 @@ uint32_t SpeakerMic(uint8_t spkr) { i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; -#else +#endif + +#ifdef USE_I2S_MIC + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); + // mic select to GND + i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; +#endif + +#ifdef USE_M5STACK_CORE2 i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); #endif @@ -632,6 +666,7 @@ void StopPlaying() { delete audio_i2s.ifile; audio_i2s.ifile = NULL; } + DOWNRATE AUDIO_PWR_OFF } @@ -651,16 +686,20 @@ void Cmd_WebRadio(void) { const char HTTP_WEBRADIO[] PROGMEM = "{s}" "I2S_WR-Title" "{m}%s{e}"; -void I2S_WR_Show(void) { +void I2S_WR_Show(bool json) { if (audio_i2s.decoder) { - WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); + if (json) { + ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title); + } else { + WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); + } } } #endif // USE_WEBSERVER #endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) void Cmd_MicRec(void) { if (XdrvMailbox.data_len > 0) { uint16 time = 10; @@ -683,13 +722,8 @@ void Play_mp3(const char *path) { if (audio_i2s.decoder || audio_i2s.mp3) return; if (!audio_i2s.out) return; - if (!ufsp->exists(path)) { - return; - } - bool I2S_Task; - AUDIO_PWR_ON if (*path=='+') { I2S_Task = true; path++; @@ -697,6 +731,12 @@ void Play_mp3(const char *path) { I2S_Task = false; } + if (!ufsp->exists(path)) { + return; + } + + AUDIO_PWR_ON + audio_i2s.file = new AudioFileSourceFS(*ufsp, path); audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); @@ -730,6 +770,7 @@ void mp3_delete(void) { delete audio_i2s.id3; delete audio_i2s.mp3; audio_i2s.mp3=nullptr; + DOWNRATE AUDIO_PWR_OFF } #endif // ESP32 @@ -746,6 +787,7 @@ void Say(char *text) { delete sam; audio_i2s.out->stop(); + DOWNRATE AUDIO_PWR_OFF } @@ -757,7 +799,7 @@ const char kI2SAudio_Commands[] PROGMEM = "I2S|" #ifdef USE_I2S_WEBRADIO "|WR" #endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) "|REC" #endif // USE_M5STACK_CORE2 #endif // ESP32 @@ -770,7 +812,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { #ifdef USE_I2S_WEBRADIO ,&Cmd_WebRadio #endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) ,&Cmd_MicRec #endif // USE_M5STACK_CORE2 #endif // ESP32 @@ -826,10 +868,15 @@ bool Xdrv42(uint8_t function) { #ifdef USE_WEBSERVER #ifdef USE_I2S_WEBRADIO case FUNC_WEB_SENSOR: - I2S_WR_Show(); + I2S_WR_Show(false); break; #endif // USE_I2S_WEBRADIO #endif // USE_WEBSERVER +#ifdef USE_I2S_WEBRADIO + case FUNC_JSON_APPEND: + I2S_WR_Show(true); + break; +#endif // USE_I2S_WEBRADIO } return result; } From 0ded72252f8a481f395ade9265ec5bb8a53a307b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 15 Jul 2022 15:02:59 +0200 Subject: [PATCH 111/219] Fix POWR320D bistable relay connection --- tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino index bfb093d1e..844033f59 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_87_tm1621_sonoff.ino @@ -11,7 +11,7 @@ * Sonoff POWR3xxD and THR3xxD LCD support * * {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} - * {"NAME":"Sonoff POWR320D","GPIO":[32,0,9312,0,9313,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} + * {"NAME":"Sonoff POWR320D","GPIO":[32,0,9313,0,9312,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} * {"NAME":"Sonoff THR320D","GPIO":[32,0,0,0,226,9280,0,0,0,321,0,576,320,9184,9216,9312,0,0,9313,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} \*********************************************************************************************/ From 6e0f643c641bb5b355dd03944adc000f59ac8750 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 15 Jul 2022 17:41:39 +0200 Subject: [PATCH 112/219] fixes and fast pin multiplexer --- .../tasmota_xdrv_driver/xdrv_10_scripter.ino | 211 +++++++++++++++++- 1 file changed, 203 insertions(+), 8 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino index 00bcd5110..3fd2f8eb8 100755 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino @@ -3595,6 +3595,48 @@ chknext: goto strexit; } #endif //USE_MORITZ +#ifdef ESP32_FAST_MUX + if (!strncmp(lp, "mux(", 4)) { + lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); + if (fvar == 0) { + // start + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + uint16_t alen; + float *fa; + lp = get_array_by_name(lp, &fa, &alen, 0); + if (!fa) { + fvar = -1; + goto nfuncexit; + } + float falen; + lp = GetNumericArgument(lp, OPER_EQU, &falen, gv); + if (falen > alen) { + falen = alen; + } + fvar = fast_mux(0, fvar, fa, falen); + } else if (fvar == 1) { + // stop + fvar = fast_mux(1, 0, 0, 0); + } else if (fvar == 2) { + // set array + uint16_t alen; + float *fa; + lp = get_array_by_name(lp, &fa, &alen, 0); + if (!fa) { + fvar = -1; + goto nfuncexit; + } + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + if (fvar > alen) { + fvar = alen; + } + fvar = fast_mux(2, 0, fa, fvar); + } else { + fvar = fast_mux(3, 0, 0, 0); + } + goto nfuncexit; + } +#endif break; case 'n': @@ -3722,6 +3764,36 @@ chknext: len++; goto exit; } + if (!strncmp(lp, "rma(", 4)) { + uint16_t alen; + float *array; + lp = get_array_by_name(lp + 4, &array, &alen, 0); + char str[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp, OPER_EQU, str, 0); + uint32_t raddr = strtol(str, NULL, 16); + raddr &= 0xfffffffc; + const uint32_t *addr = (const uint32_t*)raddr; + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + uint32_t mlen = fvar; + mlen &= 0xfffffffc; + uint32_t *lp = (uint32_t*)special_malloc(mlen); + if (lp) { + for (uint32_t cnt = 0; cnt < mlen/4; cnt++) { + lp[cnt] = addr[cnt]; + } + //esp_flash_read(); + + uint8_t *ucp = (uint8_t*)lp; + for (uint32_t cnt = 0; cnt < mlen; cnt++) { + array[cnt] = ucp[cnt]; + } + free(lp); + fvar = 0; + } else { + fvar = -1; + } + goto nfuncexit; + } #if defined(ESP32) && (defined(USE_M5STACK_CORE2)) if (!strncmp(lp, "rec(", 4)) { char str[SCRIPT_MAXSSIZE]; @@ -6115,13 +6187,17 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { lp += 4; // skip one space after cmd web_send_line(0, lp); - WSContentFlush(); + //WSContentFlush(); goto next_line; } else if (!strncmp(lp, "wfs", 3)) { lp += 4; // skip one space after cmd web_send_file(0, lp); + //WSContentFlush(); + goto next_line; + } + else if (!strncmp(lp, "wcf", 3)) { WSContentFlush(); goto next_line; } @@ -6139,7 +6215,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } -#ifdef USE_SENDMAIL +#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) else if (!strncmp(lp, "mail", 4)) { lp+=5; //char tmp[256]; @@ -6151,7 +6227,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } goto next_line; } -#endif // USE_SENDMAIL +#endif else if (!strncmp(lp,"=>",2) || !strncmp(lp,"->",2) || !strncmp(lp,"+>",2) || !strncmp(lp,"print",5)) { // execute cmd uint8_t sflag = 0,pflg = 0,svmqtt,swll; @@ -7755,8 +7831,8 @@ bool ScriptCommand(void) { // is array Response_P(PSTR("{\"script\":{\"%s\":["), lp); for (uint16_t cnt = 0; cnt < alend; cnt++) { - ext_snprintf_P(str, sizeof(str), PSTR("%*_f"), -glob_script_mem.script_dprec, fpd); - fpd++; + float tvar = *fpd++; + ext_snprintf_P(str, sizeof(str), PSTR("%*_f"), -glob_script_mem.script_dprec, &tvar); if (cnt) { ResponseAppend_P(PSTR(",%s"), str); } else { @@ -9720,7 +9796,7 @@ exgc: #endif //USE_SCRIPT_WEB_DISPLAY -#ifdef USE_SENDMAIL +#if defined(USE_SENDMAIL) || defined(USE_ESP32MAIL) void script_send_email_body(void(*func)(char *)) { uint8_t msect = Run_Scripter1(">m", -2, 0); @@ -9753,7 +9829,7 @@ uint8_t msect = Run_Scripter1(">m", -2, 0); func((char*)"*"); } } -#endif // USE_SENDMAIL +#endif //USE_SENDMAIL #ifdef USE_SCRIPT_JSON_EXPORT void ScriptJsonAppend(void) { @@ -9799,6 +9875,122 @@ bool RulesProcessEvent(const char *json_event) { return true; } +#ifdef ESP32 +#ifdef ESP32_FAST_MUX + +#define MUX_SIZE 128 +struct FAST_PIN_MUX { + volatile uint8_t scan_cnt; + uint8_t scan_buff[MUX_SIZE]; + uint8_t scan_buff_size; + uint8_t time; + uint32_t low_pins; + uint32_t high_pins; + hw_timer_t * scan_timer = NULL; + portMUX_TYPE scan_timerMux = portMUX_INITIALIZER_UNLOCKED; +} fast_pin_mux; + + +void IRAM_ATTR fast_mux_irq() { + portENTER_CRITICAL_ISR(&fast_pin_mux.scan_timerMux); + // this could be optimized for multiple pins + if (fast_pin_mux.scan_buff_size) { + while (fast_pin_mux.scan_cnt < fast_pin_mux.scan_buff_size) { + uint8_t iob = fast_pin_mux.scan_buff[fast_pin_mux.scan_cnt]; + if (iob & 0x20) { + if (iob & 1) { + GPIO.out_w1tc = fast_pin_mux.low_pins; + } + if (iob & 2) { + GPIO.out_w1ts = fast_pin_mux.high_pins; + } + if (iob & 4) { + // modify mux timer + uint8_t fac = (iob & 0x1f) >> 2; + timerAlarmWrite(fast_pin_mux.scan_timer, fast_pin_mux.time * fac, true); + } + } else { + uint8_t mode = (iob >> 6) & 1; + digitalWrite(iob & 0x1f, mode); + } + fast_pin_mux.scan_cnt++; + if (iob & 0x80) { + break; + } + } + if (fast_pin_mux.scan_cnt >= fast_pin_mux.scan_buff_size) { + fast_pin_mux.scan_cnt = 0; + } + } + portEXIT_CRITICAL_ISR(&fast_pin_mux.scan_timerMux); +} + +/* uint8_t pin nr, 0x40 = value, 0x80 = next +*/ + +int32_t fast_mux(uint32_t flag, uint32_t time, float *buf, uint32_t len) { +int32_t retval; + if (!flag) { + if (len > MUX_SIZE) { + len = MUX_SIZE; + } + fast_pin_mux.high_pins = 0; + fast_pin_mux.low_pins = 0; + for (uint32_t cnt = 0; cnt < len; cnt++) { + uint8_t iob = *buf++; + fast_pin_mux.scan_buff[cnt] = iob; + uint8_t pin = iob & 0x1f; + pinMode(pin, OUTPUT); + if (iob & 0x40) { + digitalWrite(pin, 1); + fast_pin_mux.high_pins |= (1 << pin); + } else { + digitalWrite(pin, 0); + fast_pin_mux.low_pins |= (1 << pin); + } + } + fast_pin_mux.scan_buff_size = 0; + + if (fast_pin_mux.scan_timer) { + timerStop(fast_pin_mux.scan_timer); + timerDetachInterrupt(fast_pin_mux.scan_timer); + timerEnd(fast_pin_mux.scan_timer); + } + fast_pin_mux.scan_timer = timerBegin(3, 1000, true); + if (!fast_pin_mux.scan_timer) { + return -1; + } + fast_pin_mux.time = time; + timerAttachInterrupt(fast_pin_mux.scan_timer, &fast_mux_irq, true); + timerSetAutoReload(fast_pin_mux.scan_timer, true); + timerAlarmWrite(fast_pin_mux.scan_timer, fast_pin_mux.time, true); + timerAlarmEnable(fast_pin_mux.scan_timer); + timerStart(fast_pin_mux.scan_timer); + } else if (flag == 1) { + if (fast_pin_mux.scan_timer) { + timerStop(fast_pin_mux.scan_timer); + timerDetachInterrupt(fast_pin_mux.scan_timer); + timerEnd(fast_pin_mux.scan_timer); + fast_pin_mux.scan_timer = 0; + } + } else if (flag == 2) { + portENTER_CRITICAL(&fast_pin_mux.scan_timerMux); + for (uint32_t cnt = 0; cnt < len; cnt++) { + fast_pin_mux.scan_buff[cnt] = *buf++; + } + fast_pin_mux.scan_buff_size = len; + portEXIT_CRITICAL(&fast_pin_mux.scan_timerMux); + } else { + portENTER_CRITICAL(&fast_pin_mux.scan_timerMux); + retval = fast_pin_mux.scan_cnt; + portEXIT_CRITICAL(&fast_pin_mux.scan_timerMux); + return retval; + } + return 0; +} +#endif // ESP32_FAST_MUX +#endif // ESP32 + #ifdef ESP32 #ifdef USE_SCRIPT_TASK @@ -9912,7 +10104,7 @@ int32_t url2file(uint8_t fref, char *url) { uint32_t read = stream->readBytes(buff, size); glob_script_mem.files[fref].write(buff, read); len -= read; - AddLog(LOG_LEVEL_INFO,PSTR("HTTP read %d"), len); + AddLog(LOG_LEVEL_DEBUG,PSTR("HTTP read %d"), len); } delayMicroseconds(1); } @@ -10610,6 +10802,9 @@ bool Xdrv10(uint8_t function) switch (function) { //case FUNC_PRE_INIT: case FUNC_INIT: + + //bitWrite(Settings->rule_enabled, 0, 0); // >>>>>>>>>>> + // set defaults to rules memory //bitWrite(Settings->rule_enabled,0,0); glob_script_mem.script_ram = Settings->rules[0]; From 571a2afce50f3bff82361f2117ccb87a13daf3cb Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 16 Jul 2022 12:35:22 +0200 Subject: [PATCH 113/219] Fix bistable relay interlock - Fix bistable relay interlock - Reset bistable relay more consistent and faster (40ms instead of max 200ms) --- tasmota/tasmota_support/support_tasmota.ino | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 0e735a11f..42b4e1733 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -239,14 +239,6 @@ void SetLatchingRelay(power_t lpower, uint32_t state) { } } -void ResetBistableRelays(void) { - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (bitRead(TasmotaGlobal.rel_bistable, i)) { - DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); - } - } -} - void SetDevicePower(power_t rpower, uint32_t source) { ShowSource(source); TasmotaGlobal.last_source = source; @@ -300,19 +292,18 @@ void SetDevicePower(power_t rpower, uint32_t source) { SetLatchingRelay(rpower, 1); } #endif // ESP8266 - else - { - ZeroCrossMomentStart(); - + else { uint32_t port = 0; uint32_t port_next; + + ZeroCrossMomentStart(); + for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) { power_t state = rpower &1; port_next = 1; // Select next relay if (bitRead(TasmotaGlobal.rel_bistable, port)) { if (!state) { port_next = 2; } // Skip highest relay - TasmotaGlobal.latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) port += state; // Relay = Off, Relay = On state = 1; // Set pulse } @@ -320,10 +311,20 @@ void SetDevicePower(power_t rpower, uint32_t source) { DigitalWrite(GPIO_REL1, port, bitRead(TasmotaGlobal.rel_inverted, port) ? !state : state); } port += port_next; // Select next relay - rpower >>= 1; + rpower >>= 1; // Select next power } ZeroCrossMomentEnd(); + + // Reset bistable relay here to fix non-interlock situations due to fast switching + if (TasmotaGlobal.rel_bistable) { // If bistable relays in the mix reset them after 40ms + delay(40); // About 5 x operation time + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (bitRead(TasmotaGlobal.rel_bistable, i)) { + DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); + } + } + } } } @@ -1155,7 +1156,6 @@ void Every100mSeconds(void) SetLatchingRelay(0, 0); } #endif // ESP8266 - ResetBistableRelays(); } } From 7bc67f8d182e59ec8680e498ab03b1e45f9e66a9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 16 Jul 2022 12:49:33 +0200 Subject: [PATCH 114/219] Reset bistable relays up to detected amount of ports --- tasmota/tasmota_support/support_tasmota.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index 42b4e1733..a601168a8 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -318,8 +318,8 @@ void SetDevicePower(power_t rpower, uint32_t source) { // Reset bistable relay here to fix non-interlock situations due to fast switching if (TasmotaGlobal.rel_bistable) { // If bistable relays in the mix reset them after 40ms - delay(40); // About 5 x operation time - for (uint32_t i = 0; i < MAX_RELAYS; i++) { + delay(40); // Keep energized for about 5 x operation time + for (uint32_t i = 0; i < port; i++) { // Reset up to detected amount of ports if (bitRead(TasmotaGlobal.rel_bistable, i)) { DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); } From 846b2174279df6e4cd7569b2b4c07954770366e1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 16 Jul 2022 14:32:18 +0200 Subject: [PATCH 115/219] Bump version to v12.0.2.4 - Bump version to v12.0.2.4 - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds (#15856) --- CHANGELOG.md | 19 +++++++++++++------ RELEASENOTES.md | 4 +++- tasmota/include/tasmota.h | 2 +- tasmota/include/tasmota_version.h | 2 +- tasmota/my_user_config.h | 1 + tasmota/tasmota_support/settings.ino | 4 ++++ tasmota/tasmota_support/support_command.ino | 1 + tasmota/tasmota_support/support_tasmota.ino | 2 +- tools/decode-status.py | 4 ++-- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7933a83..7b506a39a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,20 +3,27 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - Development -## [12.0.2.3] +## [12.0.2.4] ### Added -- Support for Sonoff POWR3xxD and THR3xxD (#15856) -- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D -- ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars (#15916) +- Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds ### Changed -- Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) -- Tasmota ESP32 Arduino core from v2.0.3 to v2.0.4 (#15940) ### Fixed ### Removed +## [12.0.2.3] 20220716 +### Added +- Support for Sonoff POWR3xxD and THR3xxD (#15856) +- Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D +- ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars (#15916) +- Command ``Sleep2 !`` to cancel pending one-shot speed setting (#15954) + +### Changed +- Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 (#15856) +- Tasmota ESP32 Arduino core from v2.0.3 to v2.0.4 (#15940) + ## [12.0.2.2] ### Added - Command ``GlobalTemp2 1..250`` to select Global Temperature source indexed from teleperiod occurance data (#15834) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0b41824aa..b0e0b4f2b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -107,11 +107,13 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo [Complete list](BUILDS.md) of available feature and sensors. -## Changelog v12.0.2.3 +## Changelog v12.0.2.4 ### Added +- Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds - Command ``GlobalTemp2 1..250`` to select Global Temperature source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalHum2 1..250`` to select Global Humidity source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) +- Command ``Sleep2 !`` to cancel pending one-shot speed setting [#15954](https://github.com/arendst/Tasmota/issues/15954) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index 580afd812..b210e0932 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -351,7 +351,7 @@ enum SO32_49Index { P_HOLD_TIME, // SetOption32 - (Button/Switch) K P_OVER_TEMP, // SetOption42 - (Energy) Turn all power off at or above this temperature (default 90C) P_ROTARY_MAX_STEP, // SetOption43 - (Rotary) Rotary step boundary (default 10) P_IR_TOLERANCE, // SetOption44 - (IR) Base tolerance percentage for matching incoming IR messages (default 25, max 100) - P_SO45_FREE, // SetOption45 + P_BISTABLE_PULSE, // SetOption45 - (Bistable) Pulse time for two coil bistable latching relays (default 40) P_SO46_FREE, // SetOption46 P_SO47_FREE, // SetOption47 P_SO48_FREE, // SetOption48 diff --git a/tasmota/include/tasmota_version.h b/tasmota/include/tasmota_version.h index 2a21c8947..939ce3041 100644 --- a/tasmota/include/tasmota_version.h +++ b/tasmota/include/tasmota_version.h @@ -20,6 +20,6 @@ #ifndef _TASMOTA_VERSION_H_ #define _TASMOTA_VERSION_H_ -const uint32_t VERSION = 0x0C000203; // 12.0.2.3 +const uint32_t VERSION = 0x0C000204; // 12.0.2.4 #endif // _TASMOTA_VERSION_H_ diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2e25186a1..e95e337e2 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -283,6 +283,7 @@ // (POWER_ALL_OFF, POWER_ALL_ON, POWER_ALL_SAVED_TOGGLE, POWER_ALL_SAVED, POWER_ALL_ALWAYS_ON, POWER_ALL_OFF_PULSETIME_ON) #define APP_BLINKTIME 10 // [BlinkTime] Time in 0.1 Sec to blink/toggle power for relay 1 #define APP_BLINKCOUNT 10 // [BlinkCount] Number of blinks (0 = 32000) +#define APP_BISTABLE_PULSE 40 // [SetOption45] Pulse time in ms for two coil bistable latching relays #define APP_NORMAL_SLEEP false // [SetOption60] Enable normal sleep instead of dynamic sleep #define APP_SLEEP 0 // [Sleep] Sleep time to lower energy consumption (0 = Off, 1 - 250 mSec), diff --git a/tasmota/tasmota_support/settings.ino b/tasmota/tasmota_support/settings.ino index c25b2c75e..90cd2f7bd 100644 --- a/tasmota/tasmota_support/settings.ino +++ b/tasmota/tasmota_support/settings.ino @@ -905,6 +905,7 @@ void SettingsDefaultSet2(void) { // Settings->ledpwm_mask = 0; Settings->pulse_timer[0] = APP_PULSETIME; // for (uint32_t i = 1; i < MAX_PULSETIMERS; i++) { Settings->pulse_timer[i] = 0; } + Settings->param[P_BISTABLE_PULSE] = APP_BISTABLE_PULSE; // Serial Settings->serial_config = TS_SERIAL_8N1; @@ -1549,6 +1550,9 @@ void SettingsDelta(void) { Settings->global_sensor_index[i] = 0; } } + if (Settings->version < 0x0C000204) { // 12.0.2.4 + Settings->param[P_BISTABLE_PULSE] = APP_BISTABLE_PULSE; + } Settings->version = VERSION; SettingsSave(1); diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 7580028f6..a41560e12 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -1138,6 +1138,7 @@ void CmndSetoptionBase(bool indexed) { switch (pindex) { case P_HOLD_TIME: case P_MAX_POWER_RETRY: + case P_BISTABLE_PULSE: param_low = 1; param_high = 250; break; diff --git a/tasmota/tasmota_support/support_tasmota.ino b/tasmota/tasmota_support/support_tasmota.ino index a601168a8..ef88dc674 100644 --- a/tasmota/tasmota_support/support_tasmota.ino +++ b/tasmota/tasmota_support/support_tasmota.ino @@ -318,7 +318,7 @@ void SetDevicePower(power_t rpower, uint32_t source) { // Reset bistable relay here to fix non-interlock situations due to fast switching if (TasmotaGlobal.rel_bistable) { // If bistable relays in the mix reset them after 40ms - delay(40); // Keep energized for about 5 x operation time + delay(Settings->param[P_BISTABLE_PULSE]); // SetOption45 - Keep energized for about 5 x operation time for (uint32_t i = 0; i < port; i++) { // Reset up to detected amount of ports if (bitRead(TasmotaGlobal.rel_bistable, i)) { DigitalWrite(GPIO_REL1, i, bitRead(TasmotaGlobal.rel_inverted, i) ? 1 : 0); diff --git a/tools/decode-status.py b/tools/decode-status.py index 802852f64..abbd334dc 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -95,7 +95,7 @@ a_setoption = [[ "(Wifi) Gratuitous ARP repeat time", "(Temperature) Over temperature threshold (celsius)", "(Rotary) Max allowed steps", - "(not used) Tuya MCU voltage Id", + "(Bistable) Pulse time for two coil bistable latching relays (default 40)", "(not used) Tuya MCU current Id", "(not used) Tuya MCU power Id", "(not used) Energy Tariff1 start hour", @@ -317,7 +317,7 @@ else: obj = json.load(fp) def StartDecode(): - print ("\n*** decode-status.py v12.0.2.3 by Theo Arends and Jacek Ziolkowski ***") + print ("\n*** decode-status.py v12.0.2.4 by Theo Arends and Jacek Ziolkowski ***") # print("Decoding\n{}".format(obj)) From cc22151ca9869e06b62aa4ccfe709d42d21b54ff Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 16 Jul 2022 18:28:24 +0200 Subject: [PATCH 116/219] Fix MS01 timings on ESP32 Fix MS01 timings on ESP32 (#16001) --- .../tasmota_xsns_sensor/xsns_06_dht_v6.ino | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino index bd2353560..77ded0e4e 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino @@ -56,8 +56,8 @@ uint32_t DhtExpectPulse(bool level) { uint32_t count = 0; while (digitalRead(dht_pin) == level) { if (count++ >= dht_maxcycles) { - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_TIMEOUT_WAITING_FOR " %s " D_PULSE), - (level) ? D_START_SIGNAL_HIGH : D_START_SIGNAL_LOW); + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d timeout waiting for %s pulse"), + dht_pin, (level) ? "high" : "low"); return UINT32_MAX; // Exceeded timeout, fail. } } @@ -89,11 +89,18 @@ bool DhtRead(uint32_t sensor) { delayMicroseconds(2000); // 20200621: See https://github.com/arendst/Tasmota/pull/7468#issuecomment-647067015 break; case GPIO_SI7021: // iTead SI7021 -// delayMicroseconds(500); - delayMicroseconds(400); // Higher results in Timeout waiting for start signal high pulse +#ifdef ESP8266 + delayMicroseconds(500); +#else + delayMicroseconds(400); // Higher (or lower) results in Timeout waiting for high pulse on ESP32 +#endif break; case GPIO_MS01: // Sonoff MS01 +#ifdef ESP8266 delayMicroseconds(450); +#else + delayMicroseconds(400); // Higher (or lower) results in Timeout waiting for high pulse on ESP32 +#endif break; } @@ -172,13 +179,12 @@ bool DhtRead(uint32_t sensor) { // stored data. } - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Read %5_H"), dht_data); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d read %5_H"), dht_pin, dht_data); uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { - char hex_char[15]; - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT D_CHECKSUM_FAILURE " %s =? %02X"), - ToHex_P(dht_data, 5, hex_char, sizeof(hex_char), ' '), checksum); + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d checksum failure %5_H =? %02X"), + dht_pin, dht_data, checksum); return false; } @@ -216,7 +222,7 @@ bool DhtRead(uint32_t sensor) { case GPIO_MS01: { // Sonoff MS01 int16_t voltage = ((dht_data[0] << 8) | dht_data[1]); -// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: MS01 %d"), voltage); +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d MS01 %d"), dht_pin, voltage); // Rough approximate of soil moisture % (based on values observed in the eWeLink app) // Observed values are available here: https://gist.github.com/minovap/654cdcd8bc37bb0d2ff338f8d144a509 @@ -239,7 +245,7 @@ bool DhtRead(uint32_t sensor) { } } if (isnan(temperature) || isnan(humidity)) { - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "Invalid reading")); + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d invalid reading"), dht_pin); return false; } @@ -290,7 +296,7 @@ void DhtInit(void) { dht_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for reading pulses from DHT sensor. - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_DHT "(v6) " D_SENSORS_FOUND " %d"), dht_sensors); + AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: (v6) " D_SENSORS_FOUND " %d"), dht_sensors); } else { dht_active = false; } From 543831257dd646cca270b73a167bdaf2aad55fb1 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 18 Jul 2022 11:17:17 +0200 Subject: [PATCH 117/219] Add ESP32-S3 comments to DHT driver --- .../tasmota_xsns_sensor/xsns_06_dht_v6.ino | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino index 77ded0e4e..45f3c361a 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_06_dht_v6.ino @@ -65,8 +65,6 @@ uint32_t DhtExpectPulse(bool level) { } bool DhtRead(uint32_t sensor) { - dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; - dht_pin = Dht[sensor].pin; if (!dht_dual_mode) { // Go into high impedence state to let pull-up raise data line level and @@ -163,6 +161,37 @@ bool DhtRead(uint32_t sensor) { if (i < 80) { return false; } +// AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d cycles %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u %u .."), +// dht_pin, cycles[0], cycles[1], cycles[2], cycles[3], cycles[4], cycles[5], cycles[6], cycles[7], cycles[8], cycles[9], cycles[10], cycles[11], cycles[12], cycles[13], cycles[14], cycles[15]); + // DHT11 on ESP8266 - 80MHz + // 10:49:06.532 DHT: Pin14 cycles 81 35 74 34 81 106 81 35 81 34 81 34 81 106 81 35 .. + // 10:49:06.533 DHT: Pin14 read 22001A003C + // DHT11 on ESP32 - 80MHz + // 10:55:51.868 DHT: Pin25 cycles 94 33 86 41 94 124 94 40 95 40 94 41 94 124 94 40 .. + // 10:55:51.872 DHT: Pin25 read 22001A003C + // DHT11 on ESP32-S3 - 240MHz + // 11:13:44.712 DHT: Pin21 cycles 264 116 264 117 267 350 258 117 267 117 267 117 267 349 268 116 .. + // 11:13:44.713 DHT: Pin21 read 22001A003C + // AM2301 on ESP8266 - 80MHz + // 11:00:06.423 DHT: Pin14 cycles 92 38 83 38 89 38 89 38 90 38 89 38 89 38 89 114 .. + // 11:00:06.425 DHT: Pin14 read 01F900FCF6 + // AM2301 on ESP32 - 80MHz + // 14:54:15.930 DHT: Pin25 cycles 99 45 96 45 104 45 103 45 104 45 103 46 103 132 104 45 .. + // 14:54:15.932 DHT: Pin25 read 020B010513 + // AM2301 on ESP32-S3 - 240MHz + // 11:07:29.700 DHT: Pin21 cycles 301 129 290 129 294 127 293 129 294 129 294 129 293 129 294 374 .. + // 11:07:29.701 DHT: Pin21 read 01E300FFE3 + // Sonoff MS01 on ESP8266 - 80MHz + // 10:54:38.409 DHT: Pin14 cycles 80 39 72 105 79 105 79 39 78 106 78 106 79 105 79 39 .. + // 10:54:38.412 DHT: Pin14 read 6E620FA07F + // Sonoff MS01 on ESP32 - 80MHz + // 14:34:34.811 DHT: Pin25 cycles 84 47 83 123 91 123 91 46 91 123 91 123 91 123 91 47 .. + // 14:34:34.816 DHT: Pin25 read 6EE30FA000 + // Sonoff THS01 on ESP32 - 80MHz + // 14:36:43.787 DHT: Pin25 cycles 67 42 66 41 75 42 74 42 75 42 75 41 75 131 74 52 .. + // 14:36:43.789 DHT: Pin25 read 020B00FC09 + + dht_data[0] = dht_data[1] = dht_data[2] = dht_data[3] = dht_data[4] = 0; // Inspect pulses and determine which ones are 0 (high state cycle count < low // state cycle count), or 1 (high state cycle count > low state cycle count). for (int i = 0; i < 40; ++i) { @@ -179,8 +208,6 @@ bool DhtRead(uint32_t sensor) { // stored data. } - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d read %5_H"), dht_pin, dht_data); - uint8_t checksum = (dht_data[0] + dht_data[1] + dht_data[2] + dht_data[3]) & 0xFF; if (dht_data[4] != checksum) { AddLog(LOG_LEVEL_DEBUG, PSTR("DHT: Pin%d checksum failure %5_H =? %02X"), @@ -188,6 +215,8 @@ bool DhtRead(uint32_t sensor) { return false; } + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DHT: Pin%d read %5_H"), dht_pin, dht_data); + float temperature = NAN; float humidity = NAN; switch (Dht[sensor].type) { From 59e2b3ea1d40ad63423d9f7a841a7c1bde59104e Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 18 Jul 2022 11:26:05 +0200 Subject: [PATCH 118/219] Berry add tcpserver by default --- tasmota/my_user_config.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index e95e337e2..44f0d6953 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1049,6 +1049,8 @@ // Note that only one cipher is enabled: ECDHE_RSA_WITH_AES_128_GCM_SHA256 which is very commonly used and highly secure #define USE_BERRY_WEBCLIENT_USERAGENT "TasmotaClient" // default user-agent used, can be changed with `wc.set_useragent()` #define USE_BERRY_WEBCLIENT_TIMEOUT 2000 // Default timeout in milliseconds + #define USE_BERRY_TCPSERVER // Enable TCP socket server (+0.6k) + // #define USE_BERRY_ULP // Enable ULP (Ultra Low Power) support (+4.9k) #define USE_CSE7761 // Add support for CSE7761 Energy monitor as used in Sonoff Dual R3 // -- LVGL Graphics Library --------------------------------- From 1f2e69272f5072fb6e4a9bc7c9a007b09100697f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 18 Jul 2022 15:39:26 +0200 Subject: [PATCH 119/219] Fix shutter reset savedata Fix shutter reset savedata (#15895) --- tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index 9c3080c3c..8cdea103a 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -55,7 +55,6 @@ int32_t current_stop_way = 0; int32_t next_possible_stop_position = 0; int32_t current_real_position = 0; int32_t current_pwm_velocity = 0; -int8_t savedata_original = 0; const uint8_t MAX_MODES = 7; enum Shutterposition_mode {SHT_UNDEF, SHT_TIME, SHT_TIME_UP_DOWN, SHT_TIME_GARAGE, SHT_COUNTER, SHT_PWM_VALUE, SHT_PWM_TIME,}; @@ -536,8 +535,9 @@ void ShutterPowerOff(uint8_t i) #endif break; } - Settings->save_data = savedata_original; - TasmotaGlobal.save_data_counter = Settings->save_data; + if (Settings->save_data) { + TasmotaGlobal.save_data_counter = Settings->save_data; + } delay(MOTOR_STOP_TIME); } @@ -649,11 +649,6 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) ShutterAllowPreStartProcedure(i); Shutter[i].time = Shutter[i].last_reported_time = 0; - // avoid file system writes during move to minimize missing steps - savedata_original = Settings->save_data; - Settings->save_data = 0; // will be restored after movement - - TasmotaGlobal.save_data_counter = Settings->save_data; ShutterGlobal.skip_relay_change = 0; TasmotaGlobal.rules_flag.shutter_moved = 0; ShutterGlobal.start_reported = 0; @@ -665,6 +660,12 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: VenetianDelay: %d, Pos: %d, Dir: %d, Delta: %d, Dur: %d, StartP: %d, TgtP: %d"), Shutter[i].venetian_delay, Shutter[i].tilt_real_pos,direction,(Shutter[i].tilt_config[1]-Shutter[i].tilt_config[0]), Shutter[i].tilt_config[2],Shutter[i].tilt_start_pos,Shutter[i].tilt_target_pos); } + + // avoid file system writes during move to minimize missing steps + if (Settings->save_data) { + uint32_t move_duration = (direction > 0) ? Shutter[i].open_time : Shutter[i].close_time; + TasmotaGlobal.save_data_counter = Settings->save_data + (move_duration / 10) +1; + } } //AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: Start shtr%d from %d to %d in dir: %d"), i, Shutter[i].start_position, Shutter[i].target_position, direction); Shutter[i].direction = direction; // Last action. This causes RTC to start. From 1cc091e4fa5131c069f2a99050f37934e1812047 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Tue, 19 Jul 2022 10:37:03 +0200 Subject: [PATCH 120/219] Added ModbusBridge --- BUILDS.md | 1 + tasmota/berry/include/be_gpio_defines.h | 2 + tasmota/include/tasmota_configurations.h | 1 + tasmota/include/tasmota_template.h | 6 + tasmota/language/af_AF.h | 2 + tasmota/language/bg_BG.h | 2 + tasmota/language/cs_CZ.h | 2 + tasmota/language/de_DE.h | 2 + tasmota/language/el_GR.h | 2 + tasmota/language/en_GB.h | 2 + tasmota/language/es_ES.h | 2 + tasmota/language/fr_FR.h | 2 + tasmota/language/fy_NL.h | 2 + tasmota/language/he_HE.h | 2 + tasmota/language/hu_HU.h | 2 + tasmota/language/it_IT.h | 2 + tasmota/language/ko_KO.h | 2 + tasmota/language/nl_NL.h | 2 + tasmota/language/pl_PL.h | 2 + tasmota/language/pt_BR.h | 2 + tasmota/language/pt_PT.h | 2 + tasmota/language/ro_RO.h | 2 + tasmota/language/ru_RU.h | 2 + tasmota/language/sk_SK.h | 2 + tasmota/language/sv_SE.h | 2 + tasmota/language/tr_TR.h | 2 + tasmota/language/uk_UA.h | 2 + tasmota/language/vi_VN.h | 2 + tasmota/language/zh_CN.h | 2 + tasmota/language/zh_TW.h | 2 + tasmota/my_user_config.h | 1 + .../xdrv_100_modbus_bridge.ino | 438 ++++++++++++++++++ 32 files changed, 501 insertions(+) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino diff --git a/BUILDS.md b/BUILDS.md index 65d96802d..0ffe39764 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -183,6 +183,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_NOVA_SDS | - | - / x | - | x | - | - | | USE_HPMA | - | - / x | - | x | - | - | | USE_SERIAL_BRIDGE | - | x / x | x | x | - | x | +| USE_MODBUS_BRIDGE | - | - / - | - | - | - | - | | USE_MP3_PLAYER | - | - / x | - | x | - | - | | USE_AZ7798 | - | - / - | - | - | - | - | | USE_PN532_HSU | - | - / x | - | x | - | - | diff --git a/tasmota/berry/include/be_gpio_defines.h b/tasmota/berry/include/be_gpio_defines.h index 292445d32..7e97defe5 100644 --- a/tasmota/berry/include/be_gpio_defines.h +++ b/tasmota/berry/include/be_gpio_defines.h @@ -300,6 +300,8 @@ const be_const_member_t lv_gpio_constants[] = { { "ZIGBEE_RST", (int32_t) GPIO_ZIGBEE_RST }, { "ZIGBEE_RX", (int32_t) GPIO_ZIGBEE_RX }, { "ZIGBEE_TX", (int32_t) GPIO_ZIGBEE_TX }, + { "MBR_RX", (int32_t) GPIO_MBR_RX }, + { "MBR_TX", (int32_t) GPIO_MBR_TX }, }; diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index cf99d8aa7..75b1d6367 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -235,6 +235,7 @@ #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_THERMOSTAT // Add support for Thermostat +//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_SENSORS diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index f47ebace5..06bbfb0a6 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -194,6 +194,7 @@ enum UserSelectablePins { GPIO_TM1621_CS, GPIO_TM1621_WR, GPIO_TM1621_RD, GPIO_TM1621_DAT, // Sonoff POWR3xxD and THR3xxD LCD display GPIO_REL1_BI, GPIO_REL1_BI_INV, // 8 x Relays bistable GPIO_I2S_MCLK, + GPIO_MBR_TX, GPIO_MBR_RX, // Modbus Bridge Serial interface GPIO_SENSOR_END }; // Error as warning to rethink GPIO usage @@ -435,6 +436,7 @@ const char kSensorNames[] PROGMEM = D_GPIO_TM1621_CS "|" D_GPIO_TM1621_WR "|" D_GPIO_TM1621_RD "|" D_GPIO_TM1621_DAT "|" D_SENSOR_RELAY "_b|" D_SENSOR_RELAY "_bi|" D_SENSOR_I2S_MCLK "|" + D_SENSOR_MBR_TX "|" D_SENSOR_MBR_RX "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -853,6 +855,10 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_SBR_TX), // Serial Bridge Serial interface AGPIO(GPIO_SBR_RX), // Serial Bridge Serial interface #endif +#ifdef USE_MODBUS_BRIDGE + AGPIO(GPIO_MBR_TX), // Modbus Bridge Serial interface + AGPIO(GPIO_MBR_RX), // Modbus Bridge Serial interface +#endif #ifdef USE_TCP_BRIDGE AGPIO(GPIO_TCP_TX), // TCP Serial bridge AGPIO(GPIO_TCP_RX), // TCP Serial bridge diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index b0d8a4dbd..a02170f37 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 9085b9f6c..581d0b6fc 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 648ae398e..8cc9a3255 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index 4a91c56c6..44c63ba50 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 9c57f5342..ab3479641 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index f9db613df..25d605ab3 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 402756730..c0209fe37 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 46e552dbf..21dcf9060 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA TX" #define D_SENSOR_SBR_RX "SerBr RX" #define D_SENSOR_SBR_TX "SerBr TX" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 TX" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index d73b512f1..0212d2f2c 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index e76545d7d..9f0a2df84 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index e95b4f095..b748b0dbc 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 9842ab470..1b2dc800b 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA - TX" #define D_SENSOR_SBR_RX "SerBr - RX" #define D_SENSOR_SBR_TX "SerBr - TX" +#define D_SENSOR_MBR_TX "ModBr - Tx" +#define D_SENSOR_MBR_RX "ModBr - Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri - TX" #define D_SENSOR_SR04_ECHO "SR04 Ech - RX" #define D_SENSOR_SDM72_TX "SDM72 - TX" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index 62f4a01e4..40ebaec62 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index ebad56dc5..2b69901e2 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index dde14e14c..992419ee1 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index b8cfb91e7..763efdea3 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index 907a60fc1..2d216a4a6 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 0cf495c2b..8d2d409e2 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 9e1d17fce..e5f0a0e81 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index af6d5ac01..c24ab53cb 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index c7098ce86..c36612aed 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 63bd50a6f..319791d99 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 36bb725a3..ea736ffea 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 3ef76ce6a..26de9f0a7 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index 656b89efa..e74e51008 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 90ff0e879..2fe003a1c 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -673,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 44f0d6953..a5a9af276 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -782,6 +782,7 @@ //#define USE_VINDRIKTNING // Add support for IKEA VINDRIKTNING particle concentration sensor (+0k6 code) // #define VINDRIKTNING_SHOW_PM1 // Display undocumented/supposed PM1.0 values // #define VINDRIKTNING_SHOW_PM10 // Display undocumented/supposed PM10 values +#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge // -- Power monitoring sensors -------------------- #define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino new file mode 100644 index 000000000..7a71cc3df --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino @@ -0,0 +1,438 @@ +/* + xdrv_100_modbus_bridge.ino - modbus bridge support for Tasmota + + Copyright (C) 2021 Theo Arends and Dániel Zoltán Tolnai + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + Example Command: modbussend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint8", "count":4} +*/ + +#ifdef USE_MODBUS_BRIDGE +/*********************************************************************************************\ + * Modbus Bridge using Modbus library (TasmotaModbus) +\*********************************************************************************************/ + +#define XDRV_100 100 +#define HARDWARE_FALLBACK 2 +#define MBR_MAX_VALUE_LENGTH 30 +#define MBR_SPEED TM_MODBUS_BAUDRATE +#define MBR_MAX_REGISTERS 64 + +#define D_CMND_MODBUS_SEND "ModbusSend" +#define D_CMND_MODBUS_SETBAUDRATE "ModbusSetBaudrate" +#define D_CMND_MODBUS_SETSERIALCONFIG "ModbusSetSerialConfig" + +#define D_JSON_MODBUS_RECEIVED "ModbusReceived" +#define D_JSON_MODBUS_DEVICE_ADDRESS "DeviceAddress" +#define D_JSON_MODBUS_FUNCTION_CODE "FunctionCode" +#define D_JSON_MODBUS_START_ADDRESS "StartAddress" +#define D_JSON_MODBUS_COUNT "Count" +#define D_JSON_MODBUS_ENDIAN "Endian" +#define D_JSON_MODBUS_TYPE "Type" // allready defined +#define D_JSON_MODBUS_VALUES "Values" +#define D_JSON_MODBUS_LENGTH "Length" + +const char kModbusBridgeCommands[] PROGMEM = "|" // No prefix + D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; + +void (*const ModbusBridgeCommand[])(void) PROGMEM = { + &CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig}; + +#include +TasmotaModbus *tasmotaModbus = nullptr; + +enum class ModbusBridgeError +{ + noerror = 0, + nodataexpected = 1, + wrongdeviceaddress = 2, + wrongfunctioncode = 3, + wrongstartaddress = 4, + wrongtype = 5, + wrongregistercount = 6, + wrongcount = 7 +}; + +enum class ModbusBridgeFunctionCode +{ + mb_undefined = 0, + mb_readCoilstartregister = 1, + mb_readContactstartregister = 2, + mb_readHoldingstartregister = 3, + mb_readInputstartregister = 4, + mb_writeSingleCoil = 5, + mb_writeSinglestartregister = 6 +}; + +enum class ModbusBridgeType +{ + mb_undefined, + mb_uint8, + mb_uint16, + mb_uint32, + mb_int8, + mb_int16, + mb_int32, + mb_float, + mb_raw, + mb_bit8 +}; + +enum class ModbusBridgeEndian +{ + mb_undefined, + mb_msb, + mb_lsb +}; + +struct ModbusBridge +{ + unsigned long polling_window = 0; + int in_byte_counter = 0; + bool raw = false; + + ModbusBridgeFunctionCode functionCode = ModbusBridgeFunctionCode::mb_undefined; + ModbusBridgeType type = ModbusBridgeType::mb_undefined; + + uint8_t count = 0; + uint16_t registerCount = 0; + uint8_t deviceAddress = 0; + uint8_t startAddress = 0; +}; + +ModbusBridge modbusBridge; + +/********************************************************************************************/ + +bool SetModbusBridgeBegin(void) +{ + return tasmotaModbus->begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate +} + +void SetModbusBridgeConfig(uint32_t serial_config) +{ + if (serial_config > TS_SERIAL_8O2) + { + serial_config = TS_SERIAL_8N1; + } + if (serial_config != Settings->sserial_config) + { + Settings->sserial_config = serial_config; + SetModbusBridgeBegin(); + } +} + +/********************************************************************************************/ + +void ModbusBridgeHandle(void) +{ + bool data_ready = tasmotaModbus->ReceiveReady(); + if (data_ready) + { + uint8_t *buffer; + buffer = (uint8_t *)malloc(5 + modbusBridge.registerCount); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2) + uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, modbusBridge.registerCount); + + ModbusBridgeError errorcode = ModbusBridgeError::noerror; + if (error) + { + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Driver error %d"), error); + } + else if (modbusBridge.deviceAddress == 0) + errorcode = ModbusBridgeError::nodataexpected; + else if (modbusBridge.deviceAddress != (uint8_t)buffer[0]) + errorcode = ModbusBridgeError::wrongdeviceaddress; + else if ((uint8_t)modbusBridge.functionCode != (uint8_t)buffer[1]) + errorcode = ModbusBridgeError::wrongfunctioncode; + else if ((uint8_t)modbusBridge.registerCount != (uint8_t)buffer[2]) + errorcode = ModbusBridgeError::wrongregistercount; + else + { + if (modbusBridge.type == ModbusBridgeType::mb_raw) + { + Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":[")); + for (uint8_t i = 0; i < tasmotaModbus->ReceiveCount(); i++) + { + ResponseAppend_P(PSTR("%d"), buffer[i]); + if (i < tasmotaModbus->ReceiveCount() - 1) + ResponseAppend_P(PSTR(",")); + } + ResponseAppend_P(PSTR("]}")); + ResponseJsonEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); + } + else if ((buffer[1] > 0) && (buffer[0] < 5)) // Read Registers, writing is not supported at this moment + { + Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{")); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_FUNCTION_CODE "\":%d,"), buffer[1]); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_START_ADDRESS "\":%d,"), modbusBridge.startAddress); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_LENGTH "\":%d,"), tasmotaModbus->ReceiveCount()); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_COUNT "\":%d,"), modbusBridge.count); + ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_VALUES "\":[")); + + for (uint8_t count = 0; count < modbusBridge.count; count++) + { + char svalue[MBR_MAX_VALUE_LENGTH + 1] = ""; + if (modbusBridge.type == ModbusBridgeType::mb_float) + { + float value = 0; + ((uint8_t *)&value)[3] = buffer[3 + (count * 4)]; // Get float values + ((uint8_t *)&value)[2] = buffer[4 + (count * 4)]; + ((uint8_t *)&value)[1] = buffer[5 + (count * 4)]; + ((uint8_t *)&value)[0] = buffer[6 + (count * 4)]; + ext_snprintf_P(svalue, sizeof(svalue), "%*_f", 10, &value); + } + else + { + if ((modbusBridge.type == ModbusBridgeType::mb_int32) || + (modbusBridge.type == ModbusBridgeType::mb_uint32)) + { + uint32_t value = 0; + ((uint8_t *)&value)[3] = buffer[3 + (count * 4)]; // Get int values + ((uint8_t *)&value)[2] = buffer[4 + (count * 4)]; + ((uint8_t *)&value)[1] = buffer[5 + (count * 4)]; + ((uint8_t *)&value)[0] = buffer[6 + (count * 4)]; + if (modbusBridge.type == ModbusBridgeType::mb_int32) + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d", value); + else + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value); + } + else if ((modbusBridge.type == ModbusBridgeType::mb_int16) || + (modbusBridge.type == ModbusBridgeType::mb_uint16)) + { + uint16_t value = 0; + ((uint8_t *)&value)[1] = buffer[3 + (count * 2)]; + ((uint8_t *)&value)[0] = buffer[4 + (count * 2)]; + if (modbusBridge.type == ModbusBridgeType::mb_int16) + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d", value); + else + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value); + } + else if ((modbusBridge.type == ModbusBridgeType::mb_int8) || + (modbusBridge.type == ModbusBridgeType::mb_uint8)) + { + if (modbusBridge.type == ModbusBridgeType::mb_int8) + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d", (int8_t)(buffer[3 + count])); + else + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", (uint8_t)(buffer[3 + count])); + } + else if (modbusBridge.type == ModbusBridgeType::mb_bit8) + { + uint8_t value = (uint8_t)(buffer[3 + count]); + snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d%d%d%d%d%d%d%d", ((value >> 7) & 1), ((value >> 6) & 1), ((value >> 5) & 1), ((value >> 4) & 1), ((value >> 3) & 1), ((value >> 2) & 1), ((value >> 1) & 1), (value & 1)); + } + } + ResponseAppend_P(PSTR("%s"), svalue); + if (count < modbusBridge.count - 1) + ResponseAppend_P(PSTR(",")); + } + + ResponseAppend_P(PSTR("]}")); + ResponseJsonEnd(); + + if (errorcode == ModbusBridgeError::noerror) + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); + } + } + if (errorcode != ModbusBridgeError::noerror) + { + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Recv Error %d"), (uint8_t)errorcode); + } + modbusBridge.deviceAddress = 0; + free(buffer); + } +} + +/********************************************************************************************/ + +void ModbusBridgeInit(void) +{ + if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) + { + tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); + uint8_t result = tasmotaModbus->Begin(MBR_SPEED); + if (result) + { + if (2 == result) + { + Serial.begin(MBR_SPEED, SERIAL_8E1); + ClaimSerial(); + } + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), MBR_SPEED); + } + } +} + +/*********************************************************************************************\ + * Commands +\*********************************************************************************************/ + +void CmndModbusBridgeSend(void) +{ + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); + if (!root) + return; + + modbusBridge.deviceAddress = root.getUInt(PSTR(D_JSON_MODBUS_DEVICE_ADDRESS), 0); + uint8_t functionCode = root.getUInt(PSTR(D_JSON_MODBUS_FUNCTION_CODE), 0); + modbusBridge.startAddress = root.getULong(PSTR(D_JSON_MODBUS_START_ADDRESS), 0); + const char *stype = root.getStr(PSTR(D_JSON_MODBUS_TYPE), "uint8"); + modbusBridge.count = root.getUInt(PSTR(D_JSON_MODBUS_COUNT), 1); + ModbusBridgeError errorcode = ModbusBridgeError::noerror; + + if (modbusBridge.deviceAddress == 0) + errorcode = ModbusBridgeError::wrongdeviceaddress; + else if (modbusBridge.startAddress == 0) + ; + else if (functionCode > 4) + errorcode = ModbusBridgeError::wrongfunctioncode; // Writing is not supported + else + { + modbusBridge.functionCode = static_cast(functionCode); + if (modbusBridge.functionCode == ModbusBridgeFunctionCode::mb_undefined) + errorcode = ModbusBridgeError::wrongfunctioncode; + } + + modbusBridge.type = ModbusBridgeType::mb_undefined; + if (strcmp(stype, "int8") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_int8; + modbusBridge.registerCount = 1 * modbusBridge.count; + } + else if (strcmp(stype, "int16") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_int16; + modbusBridge.registerCount = 2 * modbusBridge.count; + } + else if (strcmp(stype, "int32") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_int32; + modbusBridge.registerCount = 4 * modbusBridge.count; + } + else if (strcmp(stype, "uint8") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_uint8; + modbusBridge.registerCount = 1 * modbusBridge.count; + } + else if (strcmp(stype, "uint16") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_uint16; + modbusBridge.registerCount = 2 * modbusBridge.count; + } + else if (strcmp(stype, "uint32") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_uint32; + modbusBridge.registerCount = 4 * modbusBridge.count; + } + else if (strcmp(stype, "float") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_float; + modbusBridge.registerCount = 4 * modbusBridge.count; + } + else if (strcmp(stype, "raw") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_raw; + modbusBridge.registerCount = modbusBridge.count; + } + else if (strcmp(stype, "bit8") == 0) + { + modbusBridge.type = ModbusBridgeType::mb_bit8; + modbusBridge.registerCount = modbusBridge.count; + } + else + errorcode = ModbusBridgeError::wrongtype; + + if (modbusBridge.registerCount > MBR_MAX_REGISTERS) errorcode = ModbusBridgeError::wrongcount; + + if (errorcode != ModbusBridgeError::noerror) + { + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Send Error %d"), (uint8_t)errorcode); + return; + } + + tasmotaModbus->Send(modbusBridge.deviceAddress, (uint8_t)modbusBridge.functionCode, modbusBridge.startAddress, modbusBridge.registerCount); + ResponseCmndDone(); +} + +void CmndModbusBridgeSetBaudrate(void) +{ + if (XdrvMailbox.payload >= 300) + { + XdrvMailbox.payload /= 300; // Make it a valid baudrate + Settings->sbaudrate = XdrvMailbox.payload; + SetModbusBridgeBegin(); + } + ResponseCmndNumber(Settings->sbaudrate * 300); +} + +void CmndModbusBridgeSetConfig(void) +{ + // See TasmotaModusConfig for possible options + // ModbusConfig 0..23 where 3 equals 8N1 + // ModbusConfig 8N1 + + if (XdrvMailbox.data_len > 0) + { + if (XdrvMailbox.data_len < 3) + { // Use 0..23 as serial config option + if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) + { + SetModbusBridgeConfig(XdrvMailbox.payload); + } + } + else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) + { + int8_t serial_config = ParseSerialConfig(XdrvMailbox.data); + if (serial_config >= 0) + { + SetModbusBridgeConfig(serial_config); + } + } + } + ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv100(uint8_t function) +{ + bool result = false; + + if (FUNC_PRE_INIT == function) + { + ModbusBridgeInit(); + } + else if (tasmotaModbus) + { + switch (function) + { + case FUNC_EVERY_250_MSECOND: + ModbusBridgeHandle(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kModbusBridgeCommands, ModbusBridgeCommand); + break; + } + } + return result; +} + +#endif // USE_MODBUS_BRIDGE From 750bbcbd8bf2a367a7a2f7e0dda71597acc0b4a3 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Tue, 19 Jul 2022 16:10:20 +0200 Subject: [PATCH 121/219] Disabled Modbus Bridge in my_user_config.h --- tasmota/my_user_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index a5a9af276..2a3921def 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -782,7 +782,7 @@ //#define USE_VINDRIKTNING // Add support for IKEA VINDRIKTNING particle concentration sensor (+0k6 code) // #define VINDRIKTNING_SHOW_PM1 // Display undocumented/supposed PM1.0 values // #define VINDRIKTNING_SHOW_PM10 // Display undocumented/supposed PM10 values -#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge +//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge // -- Power monitoring sensors -------------------- #define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code) From e9dce04fc9848975f7a4fd9b9fd4c3e202c47576 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:36:39 +0200 Subject: [PATCH 122/219] Update italian language Please check and merge. Thanks. --- tasmota/language/it_IT.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index 1b2dc800b..560cb299f 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -28,7 +28,7 @@ * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. * Use online command Prefix to translate cmnd, stat and tele. * - * Updated until v9.4.0.1 - Last update 11.07.2022 + * Updated until v9.4.0.1 - Last update 19.07.2022 \*********************************************************************/ #define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) @@ -673,8 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA - TX" #define D_SENSOR_SBR_RX "SerBr - RX" #define D_SENSOR_SBR_TX "SerBr - TX" -#define D_SENSOR_MBR_TX "ModBr - Tx" -#define D_SENSOR_MBR_RX "ModBr - Rx" +#define D_SENSOR_MBR_TX "ModBr - TX" +#define D_SENSOR_MBR_RX "ModBr - RX" #define D_SENSOR_SR04_TRIG "SR04 Tri - TX" #define D_SENSOR_SR04_ECHO "SR04 Ech - RX" #define D_SENSOR_SDM72_TX "SDM72 - TX" From d3837a8b3a3a53ff6b7e3eb12634801523de5098 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 20 Jul 2022 09:36:22 +0200 Subject: [PATCH 123/219] ESP32 LVGL library from v8.2.0 to v8.3.0 --- CHANGELOG.md | 1 + .../generate/be_lv_c_mapping.h | 45 +- .../generate/be_lvgl_module.c | 50 +- .../generate/be_lvgl_widgets_lib.c | 45 +- .../lv_binding_berry/mapping/lv_enum.h | 58 +- .../lv_binding_berry/mapping/lv_funcs.h | 93 +- .../src/be_lvgl_ctypes_definitions.c | 119 +- .../src/embedded/lvgl_ctypes.py | 202 +- .../lv_binding_berry/tools/convert.py | 5 +- .../lv_binding_berry/tools/gen.sh | 2 +- lib/libesp32_lvgl/lvgl/Kconfig | 1031 ------ lib/libesp32_lvgl/lvgl/README.md | 25 +- lib/libesp32_lvgl/lvgl/README_pt_BR.md | 206 ++ lib/libesp32_lvgl/lvgl/README_zh.md | 193 + lib/libesp32_lvgl/lvgl/library.json | 2 +- lib/libesp32_lvgl/lvgl/library.properties | 2 +- lib/libesp32_lvgl/lvgl/lv_conf_template.h | 137 +- lib/libesp32_lvgl/lvgl/lvgl.h | 7 +- lib/libesp32_lvgl/lvgl/lvgl.mk | 10 - lib/libesp32_lvgl/lvgl/src/core/lv_disp.c | 122 +- lib/libesp32_lvgl/lvgl/src/core/lv_disp.h | 24 +- lib/libesp32_lvgl/lvgl/src/core/lv_event.c | 6 +- lib/libesp32_lvgl/lvgl/src/core/lv_event.h | 2 +- lib/libesp32_lvgl/lvgl/src/core/lv_group.c | 50 +- lib/libesp32_lvgl/lvgl/src/core/lv_group.h | 20 + lib/libesp32_lvgl/lvgl/src/core/lv_indev.c | 124 +- lib/libesp32_lvgl/lvgl/src/core/lv_indev.h | 6 +- .../lvgl/src/core/lv_indev_scroll.c | 3 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj.c | 36 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj.h | 5 +- .../lvgl/src/core/lv_obj_class.c | 1 + lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.c | 124 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.h | 81 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.c | 84 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.h | 18 + .../lvgl/src/core/lv_obj_scroll.c | 58 +- .../lvgl/src/core/lv_obj_scroll.h | 13 + .../lvgl/src/core/lv_obj_style.c | 174 +- .../lvgl/src/core/lv_obj_style.h | 12 +- .../lvgl/src/core/lv_obj_style_gen.c | 110 +- .../lvgl/src/core/lv_obj_style_gen.h | 60 +- lib/libesp32_lvgl/lvgl/src/core/lv_obj_tree.c | 1 + lib/libesp32_lvgl/lvgl/src/core/lv_refr.c | 385 +- lib/libesp32_lvgl/lvgl/src/core/lv_refr.h | 2 +- .../lvgl/src/draw/arm2d/lv_draw_arm2d.mk | 6 + .../lvgl/src/draw/arm2d/lv_gpu_arm2d.c | 1376 ++++++++ .../lvgl/src/draw/arm2d/lv_gpu_arm2d.h | 51 + lib/libesp32_lvgl/lvgl/src/draw/lv_draw.c | 12 +- lib/libesp32_lvgl/lvgl/src/draw/lv_draw.h | 99 +- lib/libesp32_lvgl/lvgl/src/draw/lv_draw.mk | 7 + lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.c | 11 +- lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.h | 2 +- .../lvgl/src/draw/lv_draw_label.c | 2 +- .../lvgl/src/draw/lv_draw_layer.c | 93 + .../lvgl/src/draw/lv_draw_layer.h | 83 + .../lvgl/src/draw/lv_draw_mask.c | 5 +- .../lvgl/src/draw/lv_draw_mask.h | 2 +- .../lvgl/src/draw/lv_draw_rect.c | 2 - .../lvgl/src/draw/lv_draw_rect.h | 10 +- .../lvgl/src/draw/lv_draw_transform.c | 54 + .../lvgl/src/draw/lv_draw_transform.h | 44 + lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.c | 341 +- lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.h | 83 +- .../lvgl/src/draw/lv_img_decoder.c | 13 +- .../lvgl/src/draw/lv_img_decoder.h | 4 +- .../lvgl/src/draw/nxp/lv_draw_nxp.mk | 9 + .../lvgl/src/draw/nxp/lv_gpu_nxp.c | 418 +++ .../lvgl/src/draw/nxp/lv_gpu_nxp.h | 71 + .../lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk | 8 + .../lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c | 632 ++++ .../lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h | 143 + .../lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c | 116 + .../lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h | 153 + .../{nxp_pxp => nxp/pxp}/lv_gpu_nxp_pxp_osa.c | 58 +- .../{nxp_pxp => nxp/pxp}/lv_gpu_nxp_pxp_osa.h | 45 +- .../src/draw/nxp/vglite/lv_draw_nxp_vglite.mk | 9 + .../src/draw/nxp/vglite/lv_draw_vglite_arc.c | 699 ++++ .../src/draw/nxp/vglite/lv_draw_vglite_arc.h | 79 + .../draw/nxp/vglite/lv_draw_vglite_blend.c | 618 ++++ .../vglite/lv_draw_vglite_blend.h} | 74 +- .../src/draw/nxp/vglite/lv_draw_vglite_rect.c | 244 ++ .../src/draw/nxp/vglite/lv_draw_vglite_rect.h | 77 + .../src/draw/nxp/vglite/lv_gpu_nxp_vglite.c | 153 + .../src/draw/nxp/vglite/lv_gpu_nxp_vglite.h | 185 + .../lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c | 446 --- .../lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.h | 193 - .../src/draw/nxp_vglite/lv_gpu_nxp_vglite.c | 770 ---- .../lvgl/src/draw/sdl/lv_draw_sdl.c | 5 + .../lvgl/src/draw/sdl/lv_draw_sdl.mk | 1 + .../lvgl/src/draw/sdl/lv_draw_sdl_composite.c | 15 +- .../lvgl/src/draw/sdl/lv_draw_sdl_composite.h | 1 + .../lvgl/src/draw/sdl/lv_draw_sdl_img.c | 9 +- .../lvgl/src/draw/sdl/lv_draw_sdl_label.c | 22 +- .../lvgl/src/draw/sdl/lv_draw_sdl_layer.c | 132 + .../lvgl/src/draw/sdl/lv_draw_sdl_layer.h | 55 + .../lvgl/src/draw/sdl/lv_draw_sdl_priv.h | 2 + .../lvgl/src/draw/sdl/lv_draw_sdl_rect.c | 8 +- .../src/draw/sdl/lv_draw_sdl_texture_cache.c | 2 +- .../draw/stm32_dma2d/lv_draw_stm32_dma2d.mk | 6 + .../src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c | 13 +- .../src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h | 4 + .../lvgl/src/draw/sw/lv_draw_sw.c | 34 + .../lvgl/src/draw/sw/lv_draw_sw.h | 26 + .../lvgl/src/draw/sw/lv_draw_sw.mk | 8 +- .../lvgl/src/draw/sw/lv_draw_sw_arc.c | 42 +- .../lvgl/src/draw/sw/lv_draw_sw_blend.c | 478 ++- .../lvgl/src/draw/sw/lv_draw_sw_gradient.c | 19 +- .../lvgl/src/draw/sw/lv_draw_sw_img.c | 446 ++- .../lvgl/src/draw/sw/lv_draw_sw_layer.c | 150 + .../lvgl/src/draw/sw/lv_draw_sw_letter.c | 43 +- .../lvgl/src/draw/sw/lv_draw_sw_rect.c | 73 +- .../lvgl/src/draw/sw/lv_draw_sw_transform.c | 496 +++ .../draw/swm341_dma2d/lv_draw_swm341_dma2d.mk | 6 + .../draw/swm341_dma2d/lv_gpu_swm341_dma2d.c | 241 ++ .../draw/swm341_dma2d/lv_gpu_swm341_dma2d.h | 64 + lib/libesp32_lvgl/lvgl/src/extra/README.md | 8 +- .../lvgl/src/extra/layouts/flex/lv_flex.c | 13 +- .../lvgl/src/extra/layouts/flex/lv_flex.h | 2 +- .../lvgl/src/extra/layouts/grid/lv_grid.c | 26 +- .../lvgl/src/extra/layouts/grid/lv_grid.h | 2 +- .../lvgl/src/extra/libs/bmp/lv_bmp.c | 258 ++ .../lvgl/src/extra/libs/bmp/lv_bmp.h | 42 + .../lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c | 875 +++++ .../lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.h | 104 + .../lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c | 4 +- .../lvgl/src/extra/libs/fsdrv/lv_fs_posix.c | 19 +- .../lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c | 83 +- .../lvgl/src/extra/libs/fsdrv/lv_fs_win32.c | 78 +- .../lvgl/src/extra/libs/gif/gifdec.c | 659 ++++ .../lvgl/src/extra/libs/gif/gifdec.h | 60 + .../lvgl/src/extra/libs/gif/lv_gif.c | 155 + .../lvgl/src/extra/libs/gif/lv_gif.h | 58 + .../lvgl/src/extra/libs/lv_libs.h | 12 +- .../lvgl/src/extra/libs/png/lodepng.c | 8 +- .../lvgl/src/extra/libs/png/lv_png.c | 23 +- .../lvgl/src/extra/libs/qrcode/qrcodegen.c | 6 +- .../lvgl/src/extra/libs/qrcode/qrcodegen.h | 48 +- .../lvgl/src/extra/libs/rlottie/lv_rlottie.c | 284 ++ .../lvgl/src/extra/libs/rlottie/lv_rlottie.h | 75 + .../lvgl/src/extra/libs/sjpg/lv_sjpg.c | 917 +++++ .../lvgl/src/extra/libs/sjpg/lv_sjpg.h | 43 + .../lvgl/src/extra/libs/sjpg/tjpgd.c | 1155 ++++++ .../lvgl/src/extra/libs/sjpg/tjpgd.h | 93 + .../lvgl/src/extra/libs/sjpg/tjpgdcnf.h | 33 + lib/libesp32_lvgl/lvgl/src/extra/lv_extra.c | 13 +- lib/libesp32_lvgl/lvgl/src/extra/lv_extra.h | 6 + .../lvgl/src/extra/{extra.mk => lv_extra.mk} | 0 .../lvgl/src/extra/others/fragment/README.md | 0 .../src/extra/others/fragment/lv_fragment.c | 144 + .../src/extra/others/fragment/lv_fragment.h | 339 ++ .../others/fragment/lv_fragment_manager.c | 280 ++ .../src/extra/others/gridnav/lv_gridnav.c | 25 +- .../src/extra/others/gridnav/lv_gridnav.h | 8 + .../lvgl/src/extra/others/ime/lv_ime_pinyin.c | 1198 +++++++ .../lvgl/src/extra/others/ime/lv_ime_pinyin.h | 145 + .../src/extra/others/imgfont/lv_imgfont.c | 126 + .../src/extra/others/imgfont/lv_imgfont.h | 60 + .../lvgl/src/extra/others/lv_others.h | 5 + .../lvgl/src/extra/others/msg/lv_msg.c | 172 + .../lvgl/src/extra/others/msg/lv_msg.h | 124 + .../src/extra/others/snapshot/lv_snapshot.c | 14 +- .../src/extra/themes/basic/lv_theme_basic.c | 210 +- .../src/extra/themes/basic/lv_theme_basic.h | 6 + .../extra/themes/default/lv_theme_default.c | 163 +- .../src/extra/themes/mono/lv_theme_mono.c | 18 +- .../src/extra/themes/mono/lv_theme_mono.h | 8 +- .../src/extra/widgets/calendar/lv_calendar.c | 4 +- .../lvgl/src/extra/widgets/chart/lv_chart.c | 52 +- .../lvgl/src/extra/widgets/chart/lv_chart.h | 10 +- .../src/extra/widgets/keyboard/lv_keyboard.c | 6 +- .../lvgl/src/extra/widgets/list/lv_list.c | 2 +- .../lvgl/src/extra/widgets/list/lv_list.h | 2 +- .../lvgl/src/extra/widgets/menu/lv_menu.c | 19 +- .../lvgl/src/extra/widgets/meter/lv_meter.c | 40 +- .../lvgl/src/extra/widgets/msgbox/lv_msgbox.c | 2 +- .../lvgl/src/extra/widgets/span/lv_span.c | 87 +- .../lvgl/src/extra/widgets/span/lv_span.h | 15 + .../src/extra/widgets/spinbox/lv_spinbox.c | 5 +- .../src/extra/widgets/spinbox/lv_spinbox.h | 6 +- .../src/extra/widgets/tabview/lv_tabview.c | 63 +- .../src/extra/widgets/tabview/lv_tabview.h | 2 + .../src/extra/widgets/tileview/lv_tileview.c | 6 + lib/libesp32_lvgl/lvgl/src/font/lv_font.c | 72 +- lib/libesp32_lvgl/lvgl/src/font/lv_font.h | 13 +- .../lvgl/src/font/lv_font_fmt_txt.c | 4 +- .../lvgl/src/font/lv_font_fmt_txt.h | 6 +- lib/libesp32_lvgl/lvgl/src/gpu/lv_gpu.mk | 10 - lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.c | 84 +- lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.h | 7 + lib/libesp32_lvgl/lvgl/src/hal/lv_hal_indev.c | 3 +- lib/libesp32_lvgl/lvgl/src/lv_conf_internal.h | 3136 ++++++++++------- lib/libesp32_lvgl/lvgl/src/lv_conf_kconfig.h | 44 + lib/libesp32_lvgl/lvgl/src/misc/lv_anim.c | 7 + lib/libesp32_lvgl/lvgl/src/misc/lv_anim.h | 21 + lib/libesp32_lvgl/lvgl/src/misc/lv_area.c | 53 + lib/libesp32_lvgl/lvgl/src/misc/lv_area.h | 2 + lib/libesp32_lvgl/lvgl/src/misc/lv_async.c | 27 + lib/libesp32_lvgl/lvgl/src/misc/lv_async.h | 7 + lib/libesp32_lvgl/lvgl/src/misc/lv_bidi.c | 8 +- lib/libesp32_lvgl/lvgl/src/misc/lv_color.h | 10 +- lib/libesp32_lvgl/lvgl/src/misc/lv_fs.c | 27 +- lib/libesp32_lvgl/lvgl/src/misc/lv_fs.h | 4 +- lib/libesp32_lvgl/lvgl/src/misc/lv_gc.h | 3 +- lib/libesp32_lvgl/lvgl/src/misc/lv_ll.c | 2 +- lib/libesp32_lvgl/lvgl/src/misc/lv_log.c | 23 +- lib/libesp32_lvgl/lvgl/src/misc/lv_log.h | 15 +- lib/libesp32_lvgl/lvgl/src/misc/lv_lru.c | 80 +- lib/libesp32_lvgl/lvgl/src/misc/lv_lru.h | 6 + lib/libesp32_lvgl/lvgl/src/misc/lv_math.c | 7 +- lib/libesp32_lvgl/lvgl/src/misc/lv_mem.c | 26 +- lib/libesp32_lvgl/lvgl/src/misc/lv_style.c | 341 +- lib/libesp32_lvgl/lvgl/src/misc/lv_style.h | 365 +- .../lvgl/src/misc/lv_style_gen.c | 104 +- .../lvgl/src/misc/lv_style_gen.h | 78 +- lib/libesp32_lvgl/lvgl/src/misc/lv_timer.h | 19 + lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.c | 10 +- lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.h | 2 +- lib/libesp32_lvgl/lvgl/src/misc/lv_txt.c | 16 +- lib/libesp32_lvgl/lvgl/src/misc/lv_txt.h | 8 +- lib/libesp32_lvgl/lvgl/src/misc/lv_types.h | 2 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.c | 84 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.h | 80 +- .../lvgl/src/widgets/lv_btnmatrix.c | 7 +- .../lvgl/src/widgets/lv_btnmatrix.h | 2 +- .../lvgl/src/widgets/lv_canvas.c | 109 +- .../lvgl/src/widgets/lv_dropdown.c | 22 +- .../lvgl/src/widgets/lv_dropdown.h | 8 + lib/libesp32_lvgl/lvgl/src/widgets/lv_img.c | 114 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_img.h | 9 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_label.c | 23 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_label.h | 2 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_line.c | 34 +- .../lvgl/src/widgets/lv_roller.c | 23 +- .../lvgl/src/widgets/lv_roller.h | 5 + .../lvgl/src/widgets/lv_slider.c | 143 +- lib/libesp32_lvgl/lvgl/src/widgets/lv_table.c | 366 +- .../lvgl/src/widgets/lv_textarea.c | 347 +- .../lvgl/src/widgets/lv_textarea.h | 15 + tasmota/lvgl_berry/tasmota_lv_conf.h | 135 +- 239 files changed, 21280 insertions(+), 7083 deletions(-) delete mode 100644 lib/libesp32_lvgl/lvgl/Kconfig create mode 100644 lib/libesp32_lvgl/lvgl/README_pt_BR.md create mode 100644 lib/libesp32_lvgl/lvgl/README_zh.md delete mode 100644 lib/libesp32_lvgl/lvgl/lvgl.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_draw_arm2d.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_draw_nxp.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h rename lib/libesp32_lvgl/lvgl/src/draw/{nxp_pxp => nxp/pxp}/lv_gpu_nxp_pxp_osa.c (89%) rename lib/libesp32_lvgl/lvgl/src/draw/{nxp_pxp => nxp/pxp}/lv_gpu_nxp_pxp_osa.h (62%) create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c rename lib/libesp32_lvgl/lvgl/src/draw/{nxp_vglite/lv_gpu_nxp_vglite.h => nxp/vglite/lv_draw_vglite_blend.h} (74%) create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h delete mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c delete mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.h delete mode 100644 lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.h create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_layer.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_transform.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c create mode 100644 lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgdcnf.h rename lib/libesp32_lvgl/lvgl/src/extra/{extra.mk => lv_extra.mk} (100%) create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/fragment/README.md create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment_manager.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.h create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.c create mode 100644 lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.h mode change 100644 => 100755 lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.c delete mode 100644 lib/libesp32_lvgl/lvgl/src/gpu/lv_gpu.mk diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b506a39a..050611aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. ## [12.0.2.4] ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds +- ESP32 LVGL library from v8.2.0 to v8.3.0 ### Changed diff --git a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lv_c_mapping.h b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lv_c_mapping.h index aac9f4a35..7c99171d3 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lv_c_mapping.h +++ b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lv_c_mapping.h @@ -18,26 +18,23 @@ const be_ntv_func_def_t lv_style_func[] = { { "remove_prop", { (const void*) &lv_style_remove_prop, "b", "(lv.lv_style)i" } }, { "reset", { (const void*) &lv_style_reset, "", "(lv.lv_style)" } }, { "set_align", { (const void*) &lv_style_set_align, "", "(lv.lv_style)i" } }, + { "set_anim", { (const void*) &lv_style_set_anim, "", "(lv.lv_style)(lv.lv_anim)" } }, { "set_anim_speed", { (const void*) &lv_style_set_anim_speed, "", "(lv.lv_style)i" } }, { "set_anim_time", { (const void*) &lv_style_set_anim_time, "", "(lv.lv_style)i" } }, { "set_arc_color", { (const void*) &lv_style_set_arc_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_arc_color_filtered", { (const void*) &lv_style_set_arc_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_arc_img_src", { (const void*) &lv_style_set_arc_img_src, "", "(lv.lv_style)." } }, { "set_arc_opa", { (const void*) &lv_style_set_arc_opa, "", "(lv.lv_style)i" } }, { "set_arc_rounded", { (const void*) &lv_style_set_arc_rounded, "", "(lv.lv_style)b" } }, { "set_arc_width", { (const void*) &lv_style_set_arc_width, "", "(lv.lv_style)i" } }, { "set_base_dir", { (const void*) &lv_style_set_base_dir, "", "(lv.lv_style)i" } }, { "set_bg_color", { (const void*) &lv_style_set_bg_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_bg_color_filtered", { (const void*) &lv_style_set_bg_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_bg_dither_mode", { (const void*) &lv_style_set_bg_dither_mode, "", "(lv.lv_style)i" } }, { "set_bg_grad", { (const void*) &lv_style_set_bg_grad, "", "(lv.lv_style)(lv.lv_grad_dsc)" } }, { "set_bg_grad_color", { (const void*) &lv_style_set_bg_grad_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_bg_grad_color_filtered", { (const void*) &lv_style_set_bg_grad_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_bg_grad_dir", { (const void*) &lv_style_set_bg_grad_dir, "", "(lv.lv_style)i" } }, { "set_bg_grad_stop", { (const void*) &lv_style_set_bg_grad_stop, "", "(lv.lv_style)i" } }, { "set_bg_img_opa", { (const void*) &lv_style_set_bg_img_opa, "", "(lv.lv_style)i" } }, { "set_bg_img_recolor", { (const void*) &lv_style_set_bg_img_recolor, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_bg_img_recolor_filtered", { (const void*) &lv_style_set_bg_img_recolor_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_bg_img_recolor_opa", { (const void*) &lv_style_set_bg_img_recolor_opa, "", "(lv.lv_style)i" } }, { "set_bg_img_src", { (const void*) &lv_style_set_bg_img_src, "", "(lv.lv_style)." } }, { "set_bg_img_tiled", { (const void*) &lv_style_set_bg_img_tiled, "", "(lv.lv_style)b" } }, @@ -45,7 +42,6 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_bg_opa", { (const void*) &lv_style_set_bg_opa, "", "(lv.lv_style)i" } }, { "set_blend_mode", { (const void*) &lv_style_set_blend_mode, "", "(lv.lv_style)i" } }, { "set_border_color", { (const void*) &lv_style_set_border_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_border_color_filtered", { (const void*) &lv_style_set_border_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_border_opa", { (const void*) &lv_style_set_border_opa, "", "(lv.lv_style)i" } }, { "set_border_post", { (const void*) &lv_style_set_border_post, "", "(lv.lv_style)b" } }, { "set_border_side", { (const void*) &lv_style_set_border_side, "", "(lv.lv_style)i" } }, @@ -71,11 +67,9 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_height", { (const void*) &lv_style_set_height, "", "(lv.lv_style)i" } }, { "set_img_opa", { (const void*) &lv_style_set_img_opa, "", "(lv.lv_style)i" } }, { "set_img_recolor", { (const void*) &lv_style_set_img_recolor, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_img_recolor_filtered", { (const void*) &lv_style_set_img_recolor_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_img_recolor_opa", { (const void*) &lv_style_set_img_recolor_opa, "", "(lv.lv_style)i" } }, { "set_layout", { (const void*) &lv_style_set_layout, "", "(lv.lv_style)i" } }, { "set_line_color", { (const void*) &lv_style_set_line_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_line_color_filtered", { (const void*) &lv_style_set_line_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_line_dash_gap", { (const void*) &lv_style_set_line_dash_gap, "", "(lv.lv_style)i" } }, { "set_line_dash_width", { (const void*) &lv_style_set_line_dash_width, "", "(lv.lv_style)i" } }, { "set_line_opa", { (const void*) &lv_style_set_line_opa, "", "(lv.lv_style)i" } }, @@ -87,7 +81,6 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_min_width", { (const void*) &lv_style_set_min_width, "", "(lv.lv_style)i" } }, { "set_opa", { (const void*) &lv_style_set_opa, "", "(lv.lv_style)i" } }, { "set_outline_color", { (const void*) &lv_style_set_outline_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_outline_color_filtered", { (const void*) &lv_style_set_outline_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_outline_opa", { (const void*) &lv_style_set_outline_opa, "", "(lv.lv_style)i" } }, { "set_outline_pad", { (const void*) &lv_style_set_outline_pad, "", "(lv.lv_style)i" } }, { "set_outline_width", { (const void*) &lv_style_set_outline_width, "", "(lv.lv_style)i" } }, @@ -102,9 +95,9 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_pad_top", { (const void*) &lv_style_set_pad_top, "", "(lv.lv_style)i" } }, { "set_pad_ver", { (const void*) &lv_style_set_pad_ver, "", "(lv.lv_style)i" } }, { "set_prop", { (const void*) &lv_style_set_prop, "", "(lv.lv_style)ii" } }, + { "set_prop_meta", { (const void*) &lv_style_set_prop_meta, "", "(lv.lv_style)ii" } }, { "set_radius", { (const void*) &lv_style_set_radius, "", "(lv.lv_style)i" } }, { "set_shadow_color", { (const void*) &lv_style_set_shadow_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_shadow_color_filtered", { (const void*) &lv_style_set_shadow_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_shadow_ofs_x", { (const void*) &lv_style_set_shadow_ofs_x, "", "(lv.lv_style)i" } }, { "set_shadow_ofs_y", { (const void*) &lv_style_set_shadow_ofs_y, "", "(lv.lv_style)i" } }, { "set_shadow_opa", { (const void*) &lv_style_set_shadow_opa, "", "(lv.lv_style)i" } }, @@ -113,7 +106,6 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_size", { (const void*) &lv_style_set_size, "", "(lv.lv_style)i" } }, { "set_text_align", { (const void*) &lv_style_set_text_align, "", "(lv.lv_style)i" } }, { "set_text_color", { (const void*) &lv_style_set_text_color, "", "(lv.lv_style)(lv.lv_color)" } }, - { "set_text_color_filtered", { (const void*) &lv_style_set_text_color_filtered, "", "(lv.lv_style)(lv.lv_color)" } }, { "set_text_decor", { (const void*) &lv_style_set_text_decor, "", "(lv.lv_style)i" } }, { "set_text_font", { (const void*) &lv_style_set_text_font, "", "(lv.lv_style)(lv.lv_font)" } }, { "set_text_letter_space", { (const void*) &lv_style_set_text_letter_space, "", "(lv.lv_style)i" } }, @@ -121,6 +113,8 @@ const be_ntv_func_def_t lv_style_func[] = { { "set_text_opa", { (const void*) &lv_style_set_text_opa, "", "(lv.lv_style)i" } }, { "set_transform_angle", { (const void*) &lv_style_set_transform_angle, "", "(lv.lv_style)i" } }, { "set_transform_height", { (const void*) &lv_style_set_transform_height, "", "(lv.lv_style)i" } }, + { "set_transform_pivot_x", { (const void*) &lv_style_set_transform_pivot_x, "", "(lv.lv_style)i" } }, + { "set_transform_pivot_y", { (const void*) &lv_style_set_transform_pivot_y, "", "(lv.lv_style)i" } }, { "set_transform_width", { (const void*) &lv_style_set_transform_width, "", "(lv.lv_style)i" } }, { "set_transform_zoom", { (const void*) &lv_style_set_transform_zoom, "", "(lv.lv_style)i" } }, { "set_transition", { (const void*) &lv_style_set_transition, "", "(lv.lv_style)(lv.lv_style_transition_dsc)" } }, @@ -170,12 +164,14 @@ const be_ntv_func_def_t lv_img_func[] = { const be_ntv_func_def_t lv_disp_func[] = { { "clean_dcache", { (const void*) &lv_disp_clean_dcache, "", "(lv.lv_disp)" } }, { "dpx", { (const void*) &lv_disp_dpx, "i", "(lv.lv_disp)i" } }, + { "enable_invalidation", { (const void*) &lv_disp_enable_invalidation, "", "(lv.lv_disp)b" } }, { "get_inactive_time", { (const void*) &lv_disp_get_inactive_time, "i", "(lv.lv_disp)" } }, { "get_layer_sys", { (const void*) &lv_disp_get_layer_sys, "lv.lv_obj", "(lv.lv_disp)" } }, { "get_layer_top", { (const void*) &lv_disp_get_layer_top, "lv.lv_obj", "(lv.lv_disp)" } }, { "get_scr_act", { (const void*) &lv_disp_get_scr_act, "lv.lv_obj", "(lv.lv_disp)" } }, { "get_scr_prev", { (const void*) &lv_disp_get_scr_prev, "lv.lv_obj", "(lv.lv_disp)" } }, { "get_theme", { (const void*) &lv_disp_get_theme, "lv.lv_theme", "(lv.lv_disp)" } }, + { "is_invalidation_enabled", { (const void*) &lv_disp_is_invalidation_enabled, "b", "(lv.lv_disp)" } }, { "load_scr", { (const void*) &lv_disp_load_scr, "", "(lv.lv_obj)" } }, { "set_bg_color", { (const void*) &lv_disp_set_bg_color, "", "(lv.lv_disp)(lv.lv_color)" } }, { "set_bg_image", { (const void*) &lv_disp_set_bg_image, "", "(lv.lv_disp)." } }, @@ -241,6 +237,7 @@ const be_ntv_func_def_t lv_obj_func[] = { { "get_self_width", { (const void*) &lv_obj_get_self_width, "i", "(lv.lv_obj)" } }, { "get_state", { (const void*) &lv_obj_get_state, "i", "(lv.lv_obj)" } }, { "get_style_align", { (const void*) &lv_obj_get_style_align, "i", "(lv.lv_obj)i" } }, + { "get_style_anim", { (const void*) &lv_obj_get_style_anim, "lv.lv_anim", "(lv.lv_obj)i" } }, { "get_style_anim_speed", { (const void*) &lv_obj_get_style_anim_speed, "i", "(lv.lv_obj)i" } }, { "get_style_anim_time", { (const void*) &lv_obj_get_style_anim_time, "i", "(lv.lv_obj)i" } }, { "get_style_arc_color", { (const void*) &lv_obj_get_style_arc_color, "lv.lv_color", "(lv.lv_obj)i" } }, @@ -339,6 +336,8 @@ const be_ntv_func_def_t lv_obj_func[] = { { "get_style_text_opa", { (const void*) &lv_obj_get_style_text_opa, "i", "(lv.lv_obj)i" } }, { "get_style_transform_angle", { (const void*) &lv_obj_get_style_transform_angle, "i", "(lv.lv_obj)i" } }, { "get_style_transform_height", { (const void*) &lv_obj_get_style_transform_height, "i", "(lv.lv_obj)i" } }, + { "get_style_transform_pivot_x", { (const void*) &lv_obj_get_style_transform_pivot_x, "i", "(lv.lv_obj)i" } }, + { "get_style_transform_pivot_y", { (const void*) &lv_obj_get_style_transform_pivot_y, "i", "(lv.lv_obj)i" } }, { "get_style_transform_width", { (const void*) &lv_obj_get_style_transform_width, "i", "(lv.lv_obj)i" } }, { "get_style_transform_zoom", { (const void*) &lv_obj_get_style_transform_zoom, "i", "(lv.lv_obj)i" } }, { "get_style_transition", { (const void*) &lv_obj_get_style_transition, "lv.lv_style_transition_dsc", "(lv.lv_obj)i" } }, @@ -347,6 +346,7 @@ const be_ntv_func_def_t lv_obj_func[] = { { "get_style_width", { (const void*) &lv_obj_get_style_width, "i", "(lv.lv_obj)i" } }, { "get_style_x", { (const void*) &lv_obj_get_style_x, "i", "(lv.lv_obj)i" } }, { "get_style_y", { (const void*) &lv_obj_get_style_y, "i", "(lv.lv_obj)i" } }, + { "get_transformed_area", { (const void*) &lv_obj_get_transformed_area, "", "(lv.lv_obj)(lv.lv_area)bb" } }, { "get_user_data", { (const void*) &lv_obj_get_user_data, ".", "(lv.lv_obj)" } }, { "get_width", { (const void*) &lv_obj_get_width, "i", "(lv.lv_obj)" } }, { "get_x", { (const void*) &lv_obj_get_x, "i", "(lv.lv_obj)" } }, @@ -412,6 +412,7 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_height", { (const void*) &lv_obj_set_height, "", "(lv.lv_obj)i" } }, { "set_layout", { (const void*) &lv_obj_set_layout, "", "(lv.lv_obj)i" } }, { "set_local_style_prop", { (const void*) &lv_obj_set_local_style_prop, "", "(lv.lv_obj)iii" } }, + { "set_local_style_prop_meta", { (const void*) &lv_obj_set_local_style_prop_meta, "", "(lv.lv_obj)iii" } }, { "set_parent", { (const void*) &lv_obj_set_parent, "", "(lv.lv_obj)(lv.lv_obj)" } }, { "set_pos", { (const void*) &lv_obj_set_pos, "", "(lv.lv_obj)ii" } }, { "set_scroll_dir", { (const void*) &lv_obj_set_scroll_dir, "", "(lv.lv_obj)i" } }, @@ -420,26 +421,23 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_scrollbar_mode", { (const void*) &lv_obj_set_scrollbar_mode, "", "(lv.lv_obj)i" } }, { "set_size", { (const void*) &lv_obj_set_size, "", "(lv.lv_obj)ii" } }, { "set_style_align", { (const void*) &lv_obj_set_style_align, "", "(lv.lv_obj)ii" } }, + { "set_style_anim", { (const void*) &lv_obj_set_style_anim, "", "(lv.lv_obj)(lv.lv_anim)i" } }, { "set_style_anim_speed", { (const void*) &lv_obj_set_style_anim_speed, "", "(lv.lv_obj)ii" } }, { "set_style_anim_time", { (const void*) &lv_obj_set_style_anim_time, "", "(lv.lv_obj)ii" } }, { "set_style_arc_color", { (const void*) &lv_obj_set_style_arc_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_arc_color_filtered", { (const void*) &lv_obj_set_style_arc_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_arc_img_src", { (const void*) &lv_obj_set_style_arc_img_src, "", "(lv.lv_obj).i" } }, { "set_style_arc_opa", { (const void*) &lv_obj_set_style_arc_opa, "", "(lv.lv_obj)ii" } }, { "set_style_arc_rounded", { (const void*) &lv_obj_set_style_arc_rounded, "", "(lv.lv_obj)bi" } }, { "set_style_arc_width", { (const void*) &lv_obj_set_style_arc_width, "", "(lv.lv_obj)ii" } }, { "set_style_base_dir", { (const void*) &lv_obj_set_style_base_dir, "", "(lv.lv_obj)ii" } }, { "set_style_bg_color", { (const void*) &lv_obj_set_style_bg_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_bg_color_filtered", { (const void*) &lv_obj_set_style_bg_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_bg_dither_mode", { (const void*) &lv_obj_set_style_bg_dither_mode, "", "(lv.lv_obj)ii" } }, { "set_style_bg_grad", { (const void*) &lv_obj_set_style_bg_grad, "", "(lv.lv_obj)(lv.lv_grad_dsc)i" } }, { "set_style_bg_grad_color", { (const void*) &lv_obj_set_style_bg_grad_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_bg_grad_color_filtered", { (const void*) &lv_obj_set_style_bg_grad_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_bg_grad_dir", { (const void*) &lv_obj_set_style_bg_grad_dir, "", "(lv.lv_obj)ii" } }, { "set_style_bg_grad_stop", { (const void*) &lv_obj_set_style_bg_grad_stop, "", "(lv.lv_obj)ii" } }, { "set_style_bg_img_opa", { (const void*) &lv_obj_set_style_bg_img_opa, "", "(lv.lv_obj)ii" } }, { "set_style_bg_img_recolor", { (const void*) &lv_obj_set_style_bg_img_recolor, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_bg_img_recolor_filtered", { (const void*) &lv_obj_set_style_bg_img_recolor_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_bg_img_recolor_opa", { (const void*) &lv_obj_set_style_bg_img_recolor_opa, "", "(lv.lv_obj)ii" } }, { "set_style_bg_img_src", { (const void*) &lv_obj_set_style_bg_img_src, "", "(lv.lv_obj).i" } }, { "set_style_bg_img_tiled", { (const void*) &lv_obj_set_style_bg_img_tiled, "", "(lv.lv_obj)bi" } }, @@ -447,7 +445,6 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_bg_opa", { (const void*) &lv_obj_set_style_bg_opa, "", "(lv.lv_obj)ii" } }, { "set_style_blend_mode", { (const void*) &lv_obj_set_style_blend_mode, "", "(lv.lv_obj)ii" } }, { "set_style_border_color", { (const void*) &lv_obj_set_style_border_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_border_color_filtered", { (const void*) &lv_obj_set_style_border_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_border_opa", { (const void*) &lv_obj_set_style_border_opa, "", "(lv.lv_obj)ii" } }, { "set_style_border_post", { (const void*) &lv_obj_set_style_border_post, "", "(lv.lv_obj)bi" } }, { "set_style_border_side", { (const void*) &lv_obj_set_style_border_side, "", "(lv.lv_obj)ii" } }, @@ -473,11 +470,9 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_height", { (const void*) &lv_obj_set_style_height, "", "(lv.lv_obj)ii" } }, { "set_style_img_opa", { (const void*) &lv_obj_set_style_img_opa, "", "(lv.lv_obj)ii" } }, { "set_style_img_recolor", { (const void*) &lv_obj_set_style_img_recolor, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_img_recolor_filtered", { (const void*) &lv_obj_set_style_img_recolor_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_img_recolor_opa", { (const void*) &lv_obj_set_style_img_recolor_opa, "", "(lv.lv_obj)ii" } }, { "set_style_layout", { (const void*) &lv_obj_set_style_layout, "", "(lv.lv_obj)ii" } }, { "set_style_line_color", { (const void*) &lv_obj_set_style_line_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_line_color_filtered", { (const void*) &lv_obj_set_style_line_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_line_dash_gap", { (const void*) &lv_obj_set_style_line_dash_gap, "", "(lv.lv_obj)ii" } }, { "set_style_line_dash_width", { (const void*) &lv_obj_set_style_line_dash_width, "", "(lv.lv_obj)ii" } }, { "set_style_line_opa", { (const void*) &lv_obj_set_style_line_opa, "", "(lv.lv_obj)ii" } }, @@ -489,7 +484,6 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_min_width", { (const void*) &lv_obj_set_style_min_width, "", "(lv.lv_obj)ii" } }, { "set_style_opa", { (const void*) &lv_obj_set_style_opa, "", "(lv.lv_obj)ii" } }, { "set_style_outline_color", { (const void*) &lv_obj_set_style_outline_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_outline_color_filtered", { (const void*) &lv_obj_set_style_outline_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_outline_opa", { (const void*) &lv_obj_set_style_outline_opa, "", "(lv.lv_obj)ii" } }, { "set_style_outline_pad", { (const void*) &lv_obj_set_style_outline_pad, "", "(lv.lv_obj)ii" } }, { "set_style_outline_width", { (const void*) &lv_obj_set_style_outline_width, "", "(lv.lv_obj)ii" } }, @@ -505,7 +499,6 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_pad_ver", { (const void*) &lv_obj_set_style_pad_ver, "", "(lv.lv_obj)ii" } }, { "set_style_radius", { (const void*) &lv_obj_set_style_radius, "", "(lv.lv_obj)ii" } }, { "set_style_shadow_color", { (const void*) &lv_obj_set_style_shadow_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_shadow_color_filtered", { (const void*) &lv_obj_set_style_shadow_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_shadow_ofs_x", { (const void*) &lv_obj_set_style_shadow_ofs_x, "", "(lv.lv_obj)ii" } }, { "set_style_shadow_ofs_y", { (const void*) &lv_obj_set_style_shadow_ofs_y, "", "(lv.lv_obj)ii" } }, { "set_style_shadow_opa", { (const void*) &lv_obj_set_style_shadow_opa, "", "(lv.lv_obj)ii" } }, @@ -514,7 +507,6 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_size", { (const void*) &lv_obj_set_style_size, "", "(lv.lv_obj)ii" } }, { "set_style_text_align", { (const void*) &lv_obj_set_style_text_align, "", "(lv.lv_obj)ii" } }, { "set_style_text_color", { (const void*) &lv_obj_set_style_text_color, "", "(lv.lv_obj)(lv.lv_color)i" } }, - { "set_style_text_color_filtered", { (const void*) &lv_obj_set_style_text_color_filtered, "", "(lv.lv_obj)(lv.lv_color)i" } }, { "set_style_text_decor", { (const void*) &lv_obj_set_style_text_decor, "", "(lv.lv_obj)ii" } }, { "set_style_text_font", { (const void*) &lv_obj_set_style_text_font, "", "(lv.lv_obj)(lv.lv_font)i" } }, { "set_style_text_letter_space", { (const void*) &lv_obj_set_style_text_letter_space, "", "(lv.lv_obj)ii" } }, @@ -522,6 +514,8 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_style_text_opa", { (const void*) &lv_obj_set_style_text_opa, "", "(lv.lv_obj)ii" } }, { "set_style_transform_angle", { (const void*) &lv_obj_set_style_transform_angle, "", "(lv.lv_obj)ii" } }, { "set_style_transform_height", { (const void*) &lv_obj_set_style_transform_height, "", "(lv.lv_obj)ii" } }, + { "set_style_transform_pivot_x", { (const void*) &lv_obj_set_style_transform_pivot_x, "", "(lv.lv_obj)ii" } }, + { "set_style_transform_pivot_y", { (const void*) &lv_obj_set_style_transform_pivot_y, "", "(lv.lv_obj)ii" } }, { "set_style_transform_width", { (const void*) &lv_obj_set_style_transform_width, "", "(lv.lv_obj)ii" } }, { "set_style_transform_zoom", { (const void*) &lv_obj_set_style_transform_zoom, "", "(lv.lv_obj)ii" } }, { "set_style_transition", { (const void*) &lv_obj_set_style_transition, "", "(lv.lv_obj)(lv.lv_style_transition_dsc)i" } }, @@ -535,6 +529,7 @@ const be_ntv_func_def_t lv_obj_func[] = { { "set_x", { (const void*) &lv_obj_set_x, "", "(lv.lv_obj)i" } }, { "set_y", { (const void*) &lv_obj_set_y, "", "(lv.lv_obj)i" } }, { "swap", { (const void*) &lv_obj_swap, "", "(lv.lv_obj)(lv.lv_obj)" } }, + { "transform_point", { (const void*) &lv_obj_transform_point, "", "(lv.lv_obj)(lv.lv_point)bb" } }, { "tree_walk", { (const void*) &lv_obj_tree_walk, "", "(lv.lv_obj)^lv_obj_tree_walk_cb^." } }, { "update_layout", { (const void*) &lv_obj_update_layout, "", "(lv.lv_obj)" } }, { "update_snap", { (const void*) &lv_obj_update_snap, "", "(lv.lv_obj)i" } }, @@ -548,6 +543,7 @@ const be_ntv_func_def_t lv_group_func[] = { { "focus_next", { (const void*) &lv_group_focus_next, "", "(lv.lv_group)" } }, { "focus_obj", { (const void*) &lv_group_focus_obj, "", "(lv.lv_obj)" } }, { "focus_prev", { (const void*) &lv_group_focus_prev, "", "(lv.lv_group)" } }, + { "get_edge_cb", { (const void*) &lv_group_get_edge_cb, "C", "(lv.lv_group)" } }, { "get_editing", { (const void*) &lv_group_get_editing, "b", "(lv.lv_group)" } }, { "get_focus_cb", { (const void*) &lv_group_get_focus_cb, "lv.lv_group_focus_cb", "(lv.lv_group)" } }, { "get_focused", { (const void*) &lv_group_get_focused, "lv.lv_obj", "(lv.lv_group)" } }, @@ -557,6 +553,7 @@ const be_ntv_func_def_t lv_group_func[] = { { "remove_obj", { (const void*) &lv_group_remove_obj, "", "(lv.lv_obj)" } }, { "send_data", { (const void*) &lv_group_send_data, "i", "(lv.lv_group)i" } }, { "set_default", { (const void*) &lv_group_set_default, "", "(lv.lv_group)" } }, + { "set_edge_cb", { (const void*) &lv_group_set_edge_cb, "", "(lv.lv_group)^lv_group_edge_cb^" } }, { "set_editing", { (const void*) &lv_group_set_editing, "", "(lv.lv_group)b" } }, { "set_focus_cb", { (const void*) &lv_group_set_focus_cb, "", "(lv.lv_group)^lv_group_focus_cb^" } }, { "set_refocus_policy", { (const void*) &lv_group_set_refocus_policy, "", "(lv.lv_group)(lv.lv_group_refocus_policy)" } }, @@ -706,9 +703,9 @@ const be_ntv_func_def_t lv_spinbox_func[] = { { "get_step", { (const void*) &lv_spinbox_get_step, "i", "(lv.lv_obj)" } }, { "get_value", { (const void*) &lv_spinbox_get_value, "i", "(lv.lv_obj)" } }, { "increment", { (const void*) &lv_spinbox_increment, "", "(lv.lv_obj)" } }, + { "set_cursor_pos", { (const void*) &lv_spinbox_set_cursor_pos, "", "(lv.lv_obj)i" } }, { "set_digit_format", { (const void*) &lv_spinbox_set_digit_format, "", "(lv.lv_obj)ii" } }, { "set_digit_step_direction", { (const void*) &lv_spinbox_set_digit_step_direction, "", "(lv.lv_obj)i" } }, - { "set_pos", { (const void*) &lv_spinbox_set_pos, "", "(lv.lv_obj)i" } }, { "set_range", { (const void*) &lv_spinbox_set_range, "", "(lv.lv_obj)ii" } }, { "set_rollover", { (const void*) &lv_spinbox_set_rollover, "", "(lv.lv_obj)b" } }, { "set_step", { (const void*) &lv_spinbox_set_step, "", "(lv.lv_obj)i" } }, @@ -734,6 +731,7 @@ const be_ntv_func_def_t lv_anim_func[] = { { "init", { (const void*) &lv_anim_init, "", "(lv.lv_anim)" } }, { "set_custom_exec_cb", { (const void*) &lv_anim_set_custom_exec_cb, "", "(lv.lv_anim)c" } }, { "set_delay", { (const void*) &lv_anim_set_delay, "", "(lv.lv_anim)i" } }, + { "set_deleted_cb", { (const void*) &lv_anim_set_deleted_cb, "", "(lv.lv_anim)^lv_anim_deleted_cb^" } }, { "set_early_apply", { (const void*) &lv_anim_set_early_apply, "", "(lv.lv_anim)b" } }, { "set_exec_cb", { (const void*) &lv_anim_set_exec_cb, "", "(lv.lv_anim)c" } }, { "set_get_value_cb", { (const void*) &lv_anim_set_get_value_cb, "", "(lv.lv_anim)c" } }, @@ -767,6 +765,7 @@ const be_ntv_func_def_t lv_timer_func[] = { /* `lv_arc` methods */ #ifdef BE_LV_WIDGET_ARC const be_ntv_func_def_t lv_arc_func[] = { + { "align_obj_to_angle", { (const void*) &lv_arc_align_obj_to_angle, "", "(lv.lv_obj)(lv.lv_obj)i" } }, { "get_angle_end", { (const void*) &lv_arc_get_angle_end, "i", "(lv.lv_obj)" } }, { "get_angle_start", { (const void*) &lv_arc_get_angle_start, "i", "(lv.lv_obj)" } }, { "get_bg_angle_end", { (const void*) &lv_arc_get_bg_angle_end, "i", "(lv.lv_obj)" } }, @@ -775,6 +774,7 @@ const be_ntv_func_def_t lv_arc_func[] = { { "get_min_value", { (const void*) &lv_arc_get_min_value, "i", "(lv.lv_obj)" } }, { "get_mode", { (const void*) &lv_arc_get_mode, "i", "(lv.lv_obj)" } }, { "get_value", { (const void*) &lv_arc_get_value, "i", "(lv.lv_obj)" } }, + { "rotate_obj_to_angle", { (const void*) &lv_arc_rotate_obj_to_angle, "", "(lv.lv_obj)(lv.lv_obj)i" } }, { "set_angles", { (const void*) &lv_arc_set_angles, "", "(lv.lv_obj)ii" } }, { "set_bg_angles", { (const void*) &lv_arc_set_bg_angles, "", "(lv.lv_obj)ii" } }, { "set_bg_end_angle", { (const void*) &lv_arc_set_bg_end_angle, "", "(lv.lv_obj)i" } }, @@ -872,6 +872,7 @@ const be_ntv_func_def_t lv_dropdown_func[] = { { "get_dir", { (const void*) &lv_dropdown_get_dir, "i", "(lv.lv_obj)" } }, { "get_list", { (const void*) &lv_dropdown_get_list, "lv.lv_obj", "(lv.lv_obj)" } }, { "get_option_cnt", { (const void*) &lv_dropdown_get_option_cnt, "i", "(lv.lv_obj)" } }, + { "get_option_index", { (const void*) &lv_dropdown_get_option_index, "i", "(lv.lv_obj)s" } }, { "get_options", { (const void*) &lv_dropdown_get_options, "s", "(lv.lv_obj)" } }, { "get_selected", { (const void*) &lv_dropdown_get_selected, "i", "(lv.lv_obj)" } }, { "get_selected_highlight", { (const void*) &lv_dropdown_get_selected_highlight, "b", "(lv.lv_obj)" } }, @@ -994,6 +995,7 @@ const be_ntv_func_def_t lv_textarea_func[] = { { "get_label", { (const void*) &lv_textarea_get_label, "lv.lv_obj", "(lv.lv_obj)" } }, { "get_max_length", { (const void*) &lv_textarea_get_max_length, "i", "(lv.lv_obj)" } }, { "get_one_line", { (const void*) &lv_textarea_get_one_line, "b", "(lv.lv_obj)" } }, + { "get_password_bullet", { (const void*) &lv_textarea_get_password_bullet, "s", "(lv.lv_obj)" } }, { "get_password_mode", { (const void*) &lv_textarea_get_password_mode, "b", "(lv.lv_obj)" } }, { "get_password_show_time", { (const void*) &lv_textarea_get_password_show_time, "i", "(lv.lv_obj)" } }, { "get_placeholder_text", { (const void*) &lv_textarea_get_placeholder_text, "s", "(lv.lv_obj)" } }, @@ -1006,6 +1008,7 @@ const be_ntv_func_def_t lv_textarea_func[] = { { "set_insert_replace", { (const void*) &lv_textarea_set_insert_replace, "", "(lv.lv_obj)s" } }, { "set_max_length", { (const void*) &lv_textarea_set_max_length, "", "(lv.lv_obj)i" } }, { "set_one_line", { (const void*) &lv_textarea_set_one_line, "", "(lv.lv_obj)b" } }, + { "set_password_bullet", { (const void*) &lv_textarea_set_password_bullet, "", "(lv.lv_obj)s" } }, { "set_password_mode", { (const void*) &lv_textarea_set_password_mode, "", "(lv.lv_obj)b" } }, { "set_password_show_time", { (const void*) &lv_textarea_set_password_show_time, "", "(lv.lv_obj)i" } }, { "set_placeholder_text", { (const void*) &lv_textarea_set_placeholder_text, "", "(lv.lv_obj)s" } }, diff --git a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c index 9f333f36b..794f07c5e 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c +++ b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_module.c @@ -31,6 +31,7 @@ const be_ntv_func_def_t lv_func[] = { { "anim_del", { (const void*) &lv_anim_del, "b", ".c" } }, { "anim_del_all", { (const void*) &lv_anim_del_all, "", "" } }, { "anim_get", { (const void*) &lv_anim_get, "lv.lv_anim", ".c" } }, + { "anim_get_timer", { (const void*) &lv_anim_get_timer, "lv.lv_timer", "" } }, { "anim_refr_now", { (const void*) &lv_anim_refr_now, "", "" } }, { "anim_speed_to_time", { (const void*) &lv_anim_speed_to_time, "i", "iii" } }, { "area_align", { (const void*) &lv_area_align, "", "(lv.lv_area)(lv.lv_area)iii" } }, @@ -80,6 +81,10 @@ const be_ntv_func_def_t lv_func[] = { { "draw_init", { (const void*) &lv_draw_init, "", "" } }, { "draw_label", { (const void*) &lv_draw_label, "", "(lv.lv_draw_ctx)(lv.lv_draw_label_dsc)(lv.lv_area)s(lv.lv_draw_label_hint)" } }, { "draw_label_dsc_init", { (const void*) &lv_draw_label_dsc_init, "", "(lv.lv_draw_label_dsc)" } }, + { "draw_layer_adjust", { (const void*) &lv_draw_layer_adjust, "", "(lv.lv_draw_ctx)(lv.lv_draw_layer_ctx)(lv.lv_draw_layer_flags)" } }, + { "draw_layer_blend", { (const void*) &lv_draw_layer_blend, "", "(lv.lv_draw_ctx)(lv.lv_draw_layer_ctx)(lv.lv_draw_img_dsc)" } }, + { "draw_layer_create", { (const void*) &lv_draw_layer_create, "lv.lv_draw_layer_ctx", "(lv.lv_draw_ctx)(lv.lv_area)(lv.lv_draw_layer_flags)" } }, + { "draw_layer_destroy", { (const void*) &lv_draw_layer_destroy, "", "(lv.lv_draw_ctx)(lv.lv_draw_layer_ctx)" } }, { "draw_letter", { (const void*) &lv_draw_letter, "", "(lv.lv_draw_ctx)(lv.lv_draw_label_dsc)(lv.lv_point)i" } }, { "draw_line", { (const void*) &lv_draw_line, "", "(lv.lv_draw_ctx)(lv.lv_draw_line_dsc)(lv.lv_point)(lv.lv_point)" } }, { "draw_line_dsc_init", { (const void*) &lv_draw_line_dsc_init, "", "(lv.lv_draw_line_dsc)" } }, @@ -101,7 +106,9 @@ const be_ntv_func_def_t lv_func[] = { { "draw_polygon", { (const void*) &lv_draw_polygon, "", "(lv.lv_draw_ctx)(lv.lv_draw_rect_dsc)(lv.lv_point_arr)i" } }, { "draw_rect", { (const void*) &lv_draw_rect, "", "(lv.lv_draw_ctx)(lv.lv_draw_rect_dsc)(lv.lv_area)" } }, { "draw_rect_dsc_init", { (const void*) &lv_draw_rect_dsc_init, "", "(lv.lv_draw_rect_dsc)" } }, + { "draw_transform", { (const void*) &lv_draw_transform, "", "(lv.lv_draw_ctx)(lv.lv_area).iii(lv.lv_draw_img_dsc)i(lv.lv_color)(lv.lv_opa)" } }, { "draw_triangle", { (const void*) &lv_draw_triangle, "", "(lv.lv_draw_ctx)(lv.lv_draw_rect_dsc)(lv.lv_point_arr)" } }, + { "draw_wait_for_finish", { (const void*) &lv_draw_wait_for_finish, "", "(lv.lv_draw_ctx)" } }, { "event_register_id", { (const void*) &lv_event_register_id, "i", "" } }, { "event_send", { (const void*) &lv_event_send, "i", "(lv.lv_obj)i." } }, { "event_set_cover_res", { (const void*) &lv_event_set_cover_res, "", "(lv.lv_event)(lv.lv_cover_res)" } }, @@ -135,6 +142,7 @@ const be_ntv_func_def_t lv_func[] = { { "obj_draw_part_check_type", { (const void*) &lv_obj_draw_part_check_type, "b", "(lv.lv_obj_draw_part_dsc)(lv._lv_obj_class)i" } }, { "obj_enable_style_refresh", { (const void*) &lv_obj_enable_style_refresh, "", "b" } }, { "obj_event_base", { (const void*) &lv_obj_event_base, "i", "(lv.lv_obj_class)(lv.lv_event)" } }, + { "obj_redraw", { (const void*) &lv_obj_redraw, "", "(lv.lv_draw_ctx)(lv.lv_obj)" } }, { "obj_report_style_change", { (const void*) &lv_obj_report_style_change, "", "(lv.lv_style)" } }, { "obj_style_get_selector_part", { (const void*) &lv_obj_style_get_selector_part, "i", "i" } }, { "obj_style_get_selector_state", { (const void*) &lv_obj_style_get_selector_state, "i", "i" } }, @@ -142,15 +150,17 @@ const be_ntv_func_def_t lv_func[] = { { "palette_lighten", { (const void*) &lv_palette_lighten, "lv.lv_color", "ii" } }, { "palette_main", { (const void*) &lv_palette_main, "lv.lv_color", "i" } }, { "pct", { (const void*) &lv_pct, "i", "i" } }, + { "point_transform", { (const void*) &lv_point_transform, "", "(lv.lv_point)ii(lv.lv_point)" } }, { "rand", { (const void*) &lv_rand, "i", "ii" } }, { "refr_now", { (const void*) &lv_refr_now, "", "(lv.lv_disp)" } }, - { "refr_obj", { (const void*) &lv_refr_obj, "", "(lv.lv_draw_ctx)(lv.lv_obj)" } }, { "scr_act", { (const void*) &lv_scr_act, "lv.lv_obj", "" } }, { "scr_load", { (const void*) &lv_scr_load, "", "(lv.lv_obj)" } }, { "scr_load_anim", { (const void*) &lv_scr_load_anim, "", "(lv.lv_obj)iiib" } }, { "sqrt", { (const void*) &lv_sqrt, "", "i(lv.lv_sqrt_res)i" } }, + { "style_get_num_custom_props", { (const void*) &lv_style_get_num_custom_props, "i", "" } }, { "style_prop_get_default", { (const void*) &lv_style_prop_get_default, "i", "i" } }, - { "style_register_prop", { (const void*) &lv_style_register_prop, "i", "" } }, + { "style_prop_has_flag", { (const void*) &lv_style_prop_has_flag, "b", "ii" } }, + { "style_register_prop", { (const void*) &lv_style_register_prop, "i", "i" } }, { "style_transition_dsc_init", { (const void*) &lv_style_transition_dsc_init, "", "(lv.lv_style_transition_dsc)(lv.lv_style_prop)cii." } }, { "task_handler", { (const void*) &lv_task_handler, "i", "" } }, { "theme_apply", { (const void*) &lv_theme_apply, "", "(lv.lv_obj)" } }, @@ -166,6 +176,7 @@ const be_ntv_func_def_t lv_func[] = { { "theme_haspmota_init", { (const void*) &lv_theme_haspmota_init, "lv.lv_theme", "(lv.lv_disp)(lv.lv_color)(lv.lv_color)b(lv.lv_font)" } }, { "theme_haspmota_is_inited", { (const void*) &lv_theme_haspmota_is_inited, "b", "" } }, { "theme_mono_init", { (const void*) &lv_theme_mono_init, "lv.lv_theme", "(lv.lv_disp)b(lv.lv_font)" } }, + { "theme_mono_is_inited", { (const void*) &lv_theme_mono_is_inited, "b", "" } }, { "theme_set_apply_cb", { (const void*) &lv_theme_set_apply_cb, "", "(lv.lv_theme)^lv_theme_apply_cb^" } }, { "theme_set_parent", { (const void*) &lv_theme_set_parent, "", "(lv.lv_theme)(lv.lv_theme)" } }, { "timer_create", { (const void*) &lv_timer_create, "lv.lv_timer", "^lv_timer_cb^i." } }, @@ -173,6 +184,7 @@ const be_ntv_func_def_t lv_func[] = { { "timer_enable", { (const void*) &lv_timer_enable, "", "b" } }, { "timer_get_idle", { (const void*) &lv_timer_get_idle, "i", "" } }, { "timer_handler", { (const void*) &lv_timer_handler, "i", "" } }, + { "timer_handler_run_in_period", { (const void*) &lv_timer_handler_run_in_period, "i", "i" } }, { "trigo_cos", { (const void*) &lv_trigo_cos, "i", "i" } }, { "trigo_sin", { (const void*) &lv_trigo_sin, "i", "i" } }, @@ -311,6 +323,9 @@ const be_const_member_t lv0_constants[] = { { "DITHER_ERR_DIFF", be_cconst_int(LV_DITHER_ERR_DIFF) }, { "DITHER_NONE", be_cconst_int(LV_DITHER_NONE) }, { "DITHER_ORDERED", be_cconst_int(LV_DITHER_ORDERED) }, + { "DRAW_LAYER_FLAG_CAN_SUBDIVIDE", be_cconst_int(LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) }, + { "DRAW_LAYER_FLAG_HAS_ALPHA", be_cconst_int(LV_DRAW_LAYER_FLAG_HAS_ALPHA) }, + { "DRAW_LAYER_FLAG_NONE", be_cconst_int(LV_DRAW_LAYER_FLAG_NONE) }, { "DRAW_MASK_LINE_SIDE_BOTTOM", be_cconst_int(LV_DRAW_MASK_LINE_SIDE_BOTTOM) }, { "DRAW_MASK_LINE_SIDE_LEFT", be_cconst_int(LV_DRAW_MASK_LINE_SIDE_LEFT) }, { "DRAW_MASK_LINE_SIDE_RIGHT", be_cconst_int(LV_DRAW_MASK_LINE_SIDE_RIGHT) }, @@ -439,6 +454,12 @@ const be_const_member_t lv0_constants[] = { { "IMG_CF_RAW", be_cconst_int(LV_IMG_CF_RAW) }, { "IMG_CF_RAW_ALPHA", be_cconst_int(LV_IMG_CF_RAW_ALPHA) }, { "IMG_CF_RAW_CHROMA_KEYED", be_cconst_int(LV_IMG_CF_RAW_CHROMA_KEYED) }, + { "IMG_CF_RGB565", be_cconst_int(LV_IMG_CF_RGB565) }, + { "IMG_CF_RGB565A8", be_cconst_int(LV_IMG_CF_RGB565A8) }, + { "IMG_CF_RGB888", be_cconst_int(LV_IMG_CF_RGB888) }, + { "IMG_CF_RGBA5658", be_cconst_int(LV_IMG_CF_RGBA5658) }, + { "IMG_CF_RGBA8888", be_cconst_int(LV_IMG_CF_RGBA8888) }, + { "IMG_CF_RGBX8888", be_cconst_int(LV_IMG_CF_RGBX8888) }, { "IMG_CF_TRUE_COLOR", be_cconst_int(LV_IMG_CF_TRUE_COLOR) }, { "IMG_CF_TRUE_COLOR_ALPHA", be_cconst_int(LV_IMG_CF_TRUE_COLOR_ALPHA) }, { "IMG_CF_TRUE_COLOR_CHROMA_KEYED", be_cconst_int(LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) }, @@ -477,6 +498,9 @@ const be_const_member_t lv0_constants[] = { { "LABEL_LONG_WRAP", be_cconst_int(LV_LABEL_LONG_WRAP) }, { "LABEL_POS_LAST", be_cconst_int(LV_LABEL_POS_LAST) }, { "LABEL_TEXT_SELECTION_OFF", be_cconst_int(LV_LABEL_TEXT_SELECTION_OFF) }, + { "LAYER_TYPE_NONE", be_cconst_int(LV_LAYER_TYPE_NONE) }, + { "LAYER_TYPE_SIMPLE", be_cconst_int(LV_LAYER_TYPE_SIMPLE) }, + { "LAYER_TYPE_TRANSFORM", be_cconst_int(LV_LAYER_TYPE_TRANSFORM) }, { "LED_DRAW_PART_RECTANGLE", be_cconst_int(LV_LED_DRAW_PART_RECTANGLE) }, { "LOG_LEVEL_ERROR", be_cconst_int(LV_LOG_LEVEL_ERROR) }, { "LOG_LEVEL_INFO", be_cconst_int(LV_LOG_LEVEL_INFO) }, @@ -596,12 +620,18 @@ const be_const_member_t lv0_constants[] = { { "SCROLL_SNAP_END", be_cconst_int(LV_SCROLL_SNAP_END) }, { "SCROLL_SNAP_NONE", be_cconst_int(LV_SCROLL_SNAP_NONE) }, { "SCROLL_SNAP_START", be_cconst_int(LV_SCROLL_SNAP_START) }, + { "SCR_LOAD_ANIM_FADE_IN", be_cconst_int(LV_SCR_LOAD_ANIM_FADE_IN) }, { "SCR_LOAD_ANIM_FADE_ON", be_cconst_int(LV_SCR_LOAD_ANIM_FADE_ON) }, + { "SCR_LOAD_ANIM_FADE_OUT", be_cconst_int(LV_SCR_LOAD_ANIM_FADE_OUT) }, { "SCR_LOAD_ANIM_MOVE_BOTTOM", be_cconst_int(LV_SCR_LOAD_ANIM_MOVE_BOTTOM) }, { "SCR_LOAD_ANIM_MOVE_LEFT", be_cconst_int(LV_SCR_LOAD_ANIM_MOVE_LEFT) }, { "SCR_LOAD_ANIM_MOVE_RIGHT", be_cconst_int(LV_SCR_LOAD_ANIM_MOVE_RIGHT) }, { "SCR_LOAD_ANIM_MOVE_TOP", be_cconst_int(LV_SCR_LOAD_ANIM_MOVE_TOP) }, { "SCR_LOAD_ANIM_NONE", be_cconst_int(LV_SCR_LOAD_ANIM_NONE) }, + { "SCR_LOAD_ANIM_OUT_BOTTOM", be_cconst_int(LV_SCR_LOAD_ANIM_OUT_BOTTOM) }, + { "SCR_LOAD_ANIM_OUT_LEFT", be_cconst_int(LV_SCR_LOAD_ANIM_OUT_LEFT) }, + { "SCR_LOAD_ANIM_OUT_RIGHT", be_cconst_int(LV_SCR_LOAD_ANIM_OUT_RIGHT) }, + { "SCR_LOAD_ANIM_OUT_TOP", be_cconst_int(LV_SCR_LOAD_ANIM_OUT_TOP) }, { "SCR_LOAD_ANIM_OVER_BOTTOM", be_cconst_int(LV_SCR_LOAD_ANIM_OVER_BOTTOM) }, { "SCR_LOAD_ANIM_OVER_LEFT", be_cconst_int(LV_SCR_LOAD_ANIM_OVER_LEFT) }, { "SCR_LOAD_ANIM_OVER_RIGHT", be_cconst_int(LV_SCR_LOAD_ANIM_OVER_RIGHT) }, @@ -632,26 +662,23 @@ const be_const_member_t lv0_constants[] = { { "STATE_USER_3", be_cconst_int(LV_STATE_USER_3) }, { "STATE_USER_4", be_cconst_int(LV_STATE_USER_4) }, { "STYLE_ALIGN", be_cconst_int(LV_STYLE_ALIGN) }, + { "STYLE_ANIM", be_cconst_int(LV_STYLE_ANIM) }, { "STYLE_ANIM_SPEED", be_cconst_int(LV_STYLE_ANIM_SPEED) }, { "STYLE_ANIM_TIME", be_cconst_int(LV_STYLE_ANIM_TIME) }, { "STYLE_ARC_COLOR", be_cconst_int(LV_STYLE_ARC_COLOR) }, - { "STYLE_ARC_COLOR_FILTERED", be_cconst_int(LV_STYLE_ARC_COLOR_FILTERED) }, { "STYLE_ARC_IMG_SRC", be_cconst_int(LV_STYLE_ARC_IMG_SRC) }, { "STYLE_ARC_OPA", be_cconst_int(LV_STYLE_ARC_OPA) }, { "STYLE_ARC_ROUNDED", be_cconst_int(LV_STYLE_ARC_ROUNDED) }, { "STYLE_ARC_WIDTH", be_cconst_int(LV_STYLE_ARC_WIDTH) }, { "STYLE_BASE_DIR", be_cconst_int(LV_STYLE_BASE_DIR) }, { "STYLE_BG_COLOR", be_cconst_int(LV_STYLE_BG_COLOR) }, - { "STYLE_BG_COLOR_FILTERED", be_cconst_int(LV_STYLE_BG_COLOR_FILTERED) }, { "STYLE_BG_DITHER_MODE", be_cconst_int(LV_STYLE_BG_DITHER_MODE) }, { "STYLE_BG_GRAD", be_cconst_int(LV_STYLE_BG_GRAD) }, { "STYLE_BG_GRAD_COLOR", be_cconst_int(LV_STYLE_BG_GRAD_COLOR) }, - { "STYLE_BG_GRAD_COLOR_FILTERED", be_cconst_int(LV_STYLE_BG_GRAD_COLOR_FILTERED) }, { "STYLE_BG_GRAD_DIR", be_cconst_int(LV_STYLE_BG_GRAD_DIR) }, { "STYLE_BG_GRAD_STOP", be_cconst_int(LV_STYLE_BG_GRAD_STOP) }, { "STYLE_BG_IMG_OPA", be_cconst_int(LV_STYLE_BG_IMG_OPA) }, { "STYLE_BG_IMG_RECOLOR", be_cconst_int(LV_STYLE_BG_IMG_RECOLOR) }, - { "STYLE_BG_IMG_RECOLOR_FILTERED", be_cconst_int(LV_STYLE_BG_IMG_RECOLOR_FILTERED) }, { "STYLE_BG_IMG_RECOLOR_OPA", be_cconst_int(LV_STYLE_BG_IMG_RECOLOR_OPA) }, { "STYLE_BG_IMG_SRC", be_cconst_int(LV_STYLE_BG_IMG_SRC) }, { "STYLE_BG_IMG_TILED", be_cconst_int(LV_STYLE_BG_IMG_TILED) }, @@ -659,7 +686,6 @@ const be_const_member_t lv0_constants[] = { { "STYLE_BG_OPA", be_cconst_int(LV_STYLE_BG_OPA) }, { "STYLE_BLEND_MODE", be_cconst_int(LV_STYLE_BLEND_MODE) }, { "STYLE_BORDER_COLOR", be_cconst_int(LV_STYLE_BORDER_COLOR) }, - { "STYLE_BORDER_COLOR_FILTERED", be_cconst_int(LV_STYLE_BORDER_COLOR_FILTERED) }, { "STYLE_BORDER_OPA", be_cconst_int(LV_STYLE_BORDER_OPA) }, { "STYLE_BORDER_POST", be_cconst_int(LV_STYLE_BORDER_POST) }, { "STYLE_BORDER_SIDE", be_cconst_int(LV_STYLE_BORDER_SIDE) }, @@ -670,11 +696,9 @@ const be_const_member_t lv0_constants[] = { { "STYLE_HEIGHT", be_cconst_int(LV_STYLE_HEIGHT) }, { "STYLE_IMG_OPA", be_cconst_int(LV_STYLE_IMG_OPA) }, { "STYLE_IMG_RECOLOR", be_cconst_int(LV_STYLE_IMG_RECOLOR) }, - { "STYLE_IMG_RECOLOR_FILTERED", be_cconst_int(LV_STYLE_IMG_RECOLOR_FILTERED) }, { "STYLE_IMG_RECOLOR_OPA", be_cconst_int(LV_STYLE_IMG_RECOLOR_OPA) }, { "STYLE_LAYOUT", be_cconst_int(LV_STYLE_LAYOUT) }, { "STYLE_LINE_COLOR", be_cconst_int(LV_STYLE_LINE_COLOR) }, - { "STYLE_LINE_COLOR_FILTERED", be_cconst_int(LV_STYLE_LINE_COLOR_FILTERED) }, { "STYLE_LINE_DASH_GAP", be_cconst_int(LV_STYLE_LINE_DASH_GAP) }, { "STYLE_LINE_DASH_WIDTH", be_cconst_int(LV_STYLE_LINE_DASH_WIDTH) }, { "STYLE_LINE_OPA", be_cconst_int(LV_STYLE_LINE_OPA) }, @@ -686,7 +710,6 @@ const be_const_member_t lv0_constants[] = { { "STYLE_MIN_WIDTH", be_cconst_int(LV_STYLE_MIN_WIDTH) }, { "STYLE_OPA", be_cconst_int(LV_STYLE_OPA) }, { "STYLE_OUTLINE_COLOR", be_cconst_int(LV_STYLE_OUTLINE_COLOR) }, - { "STYLE_OUTLINE_COLOR_FILTERED", be_cconst_int(LV_STYLE_OUTLINE_COLOR_FILTERED) }, { "STYLE_OUTLINE_OPA", be_cconst_int(LV_STYLE_OUTLINE_OPA) }, { "STYLE_OUTLINE_PAD", be_cconst_int(LV_STYLE_OUTLINE_PAD) }, { "STYLE_OUTLINE_WIDTH", be_cconst_int(LV_STYLE_OUTLINE_WIDTH) }, @@ -699,8 +722,10 @@ const be_const_member_t lv0_constants[] = { { "STYLE_PROP_ANY", be_cconst_int(LV_STYLE_PROP_ANY) }, { "STYLE_PROP_INV", be_cconst_int(LV_STYLE_PROP_INV) }, { "STYLE_RADIUS", be_cconst_int(LV_STYLE_RADIUS) }, + { "STYLE_RES_FOUND", be_cconst_int(LV_STYLE_RES_FOUND) }, + { "STYLE_RES_INHERIT", be_cconst_int(LV_STYLE_RES_INHERIT) }, + { "STYLE_RES_NOT_FOUND", be_cconst_int(LV_STYLE_RES_NOT_FOUND) }, { "STYLE_SHADOW_COLOR", be_cconst_int(LV_STYLE_SHADOW_COLOR) }, - { "STYLE_SHADOW_COLOR_FILTERED", be_cconst_int(LV_STYLE_SHADOW_COLOR_FILTERED) }, { "STYLE_SHADOW_OFS_X", be_cconst_int(LV_STYLE_SHADOW_OFS_X) }, { "STYLE_SHADOW_OFS_Y", be_cconst_int(LV_STYLE_SHADOW_OFS_Y) }, { "STYLE_SHADOW_OPA", be_cconst_int(LV_STYLE_SHADOW_OPA) }, @@ -708,7 +733,6 @@ const be_const_member_t lv0_constants[] = { { "STYLE_SHADOW_WIDTH", be_cconst_int(LV_STYLE_SHADOW_WIDTH) }, { "STYLE_TEXT_ALIGN", be_cconst_int(LV_STYLE_TEXT_ALIGN) }, { "STYLE_TEXT_COLOR", be_cconst_int(LV_STYLE_TEXT_COLOR) }, - { "STYLE_TEXT_COLOR_FILTERED", be_cconst_int(LV_STYLE_TEXT_COLOR_FILTERED) }, { "STYLE_TEXT_DECOR", be_cconst_int(LV_STYLE_TEXT_DECOR) }, { "STYLE_TEXT_FONT", be_cconst_int(LV_STYLE_TEXT_FONT) }, { "STYLE_TEXT_LETTER_SPACE", be_cconst_int(LV_STYLE_TEXT_LETTER_SPACE) }, @@ -716,6 +740,8 @@ const be_const_member_t lv0_constants[] = { { "STYLE_TEXT_OPA", be_cconst_int(LV_STYLE_TEXT_OPA) }, { "STYLE_TRANSFORM_ANGLE", be_cconst_int(LV_STYLE_TRANSFORM_ANGLE) }, { "STYLE_TRANSFORM_HEIGHT", be_cconst_int(LV_STYLE_TRANSFORM_HEIGHT) }, + { "STYLE_TRANSFORM_PIVOT_X", be_cconst_int(LV_STYLE_TRANSFORM_PIVOT_X) }, + { "STYLE_TRANSFORM_PIVOT_Y", be_cconst_int(LV_STYLE_TRANSFORM_PIVOT_Y) }, { "STYLE_TRANSFORM_WIDTH", be_cconst_int(LV_STYLE_TRANSFORM_WIDTH) }, { "STYLE_TRANSFORM_ZOOM", be_cconst_int(LV_STYLE_TRANSFORM_ZOOM) }, { "STYLE_TRANSITION", be_cconst_int(LV_STYLE_TRANSITION) }, diff --git a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_widgets_lib.c b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_widgets_lib.c index b5438ab72..581fce9a4 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_widgets_lib.c +++ b/lib/libesp32_lvgl/lv_binding_berry/generate/be_lvgl_widgets_lib.c @@ -48,6 +48,7 @@ extern int lvbe_style_set_grid_cell_y_align(bvm *vm); extern int lvbe_style_reset(bvm *vm); extern int lvbe_style_remove_prop(bvm *vm); extern int lvbe_style_set_prop(bvm *vm); +extern int lvbe_style_set_prop_meta(bvm *vm); extern int lvbe_style_get_prop(bvm *vm); extern int lvbe_style_get_prop_inlined(bvm *vm); extern int lvbe_style_is_empty(bvm *vm); @@ -71,6 +72,8 @@ extern int lvbe_style_set_translate_x(bvm *vm); extern int lvbe_style_set_translate_y(bvm *vm); extern int lvbe_style_set_transform_zoom(bvm *vm); extern int lvbe_style_set_transform_angle(bvm *vm); +extern int lvbe_style_set_transform_pivot_x(bvm *vm); +extern int lvbe_style_set_transform_pivot_y(bvm *vm); extern int lvbe_style_set_pad_top(bvm *vm); extern int lvbe_style_set_pad_bottom(bvm *vm); extern int lvbe_style_set_pad_left(bvm *vm); @@ -78,10 +81,8 @@ extern int lvbe_style_set_pad_right(bvm *vm); extern int lvbe_style_set_pad_row(bvm *vm); extern int lvbe_style_set_pad_column(bvm *vm); extern int lvbe_style_set_bg_color(bvm *vm); -extern int lvbe_style_set_bg_color_filtered(bvm *vm); extern int lvbe_style_set_bg_opa(bvm *vm); extern int lvbe_style_set_bg_grad_color(bvm *vm); -extern int lvbe_style_set_bg_grad_color_filtered(bvm *vm); extern int lvbe_style_set_bg_grad_dir(bvm *vm); extern int lvbe_style_set_bg_main_stop(bvm *vm); extern int lvbe_style_set_bg_grad_stop(bvm *vm); @@ -90,18 +91,15 @@ extern int lvbe_style_set_bg_dither_mode(bvm *vm); extern int lvbe_style_set_bg_img_src(bvm *vm); extern int lvbe_style_set_bg_img_opa(bvm *vm); extern int lvbe_style_set_bg_img_recolor(bvm *vm); -extern int lvbe_style_set_bg_img_recolor_filtered(bvm *vm); extern int lvbe_style_set_bg_img_recolor_opa(bvm *vm); extern int lvbe_style_set_bg_img_tiled(bvm *vm); extern int lvbe_style_set_border_color(bvm *vm); -extern int lvbe_style_set_border_color_filtered(bvm *vm); extern int lvbe_style_set_border_opa(bvm *vm); extern int lvbe_style_set_border_width(bvm *vm); extern int lvbe_style_set_border_side(bvm *vm); extern int lvbe_style_set_border_post(bvm *vm); extern int lvbe_style_set_outline_width(bvm *vm); extern int lvbe_style_set_outline_color(bvm *vm); -extern int lvbe_style_set_outline_color_filtered(bvm *vm); extern int lvbe_style_set_outline_opa(bvm *vm); extern int lvbe_style_set_outline_pad(bvm *vm); extern int lvbe_style_set_shadow_width(bvm *vm); @@ -109,27 +107,22 @@ extern int lvbe_style_set_shadow_ofs_x(bvm *vm); extern int lvbe_style_set_shadow_ofs_y(bvm *vm); extern int lvbe_style_set_shadow_spread(bvm *vm); extern int lvbe_style_set_shadow_color(bvm *vm); -extern int lvbe_style_set_shadow_color_filtered(bvm *vm); extern int lvbe_style_set_shadow_opa(bvm *vm); extern int lvbe_style_set_img_opa(bvm *vm); extern int lvbe_style_set_img_recolor(bvm *vm); -extern int lvbe_style_set_img_recolor_filtered(bvm *vm); extern int lvbe_style_set_img_recolor_opa(bvm *vm); extern int lvbe_style_set_line_width(bvm *vm); extern int lvbe_style_set_line_dash_width(bvm *vm); extern int lvbe_style_set_line_dash_gap(bvm *vm); extern int lvbe_style_set_line_rounded(bvm *vm); extern int lvbe_style_set_line_color(bvm *vm); -extern int lvbe_style_set_line_color_filtered(bvm *vm); extern int lvbe_style_set_line_opa(bvm *vm); extern int lvbe_style_set_arc_width(bvm *vm); extern int lvbe_style_set_arc_rounded(bvm *vm); extern int lvbe_style_set_arc_color(bvm *vm); -extern int lvbe_style_set_arc_color_filtered(bvm *vm); extern int lvbe_style_set_arc_opa(bvm *vm); extern int lvbe_style_set_arc_img_src(bvm *vm); extern int lvbe_style_set_text_color(bvm *vm); -extern int lvbe_style_set_text_color_filtered(bvm *vm); extern int lvbe_style_set_text_opa(bvm *vm); extern int lvbe_style_set_text_font(bvm *vm); extern int lvbe_style_set_text_letter_space(bvm *vm); @@ -141,6 +134,7 @@ extern int lvbe_style_set_clip_corner(bvm *vm); extern int lvbe_style_set_opa(bvm *vm); extern int lvbe_style_set_color_filter_dsc(bvm *vm); extern int lvbe_style_set_color_filter_opa(bvm *vm); +extern int lvbe_style_set_anim(bvm *vm); extern int lvbe_style_set_anim_time(bvm *vm); extern int lvbe_style_set_anim_speed(bvm *vm); extern int lvbe_style_set_transition(bvm *vm); @@ -188,6 +182,8 @@ extern int lvbe_disp_set_bg_opa(bvm *vm); extern int lvbe_disp_get_inactive_time(bvm *vm); extern int lvbe_disp_trig_activity(bvm *vm); extern int lvbe_disp_clean_dcache(bvm *vm); +extern int lvbe_disp_enable_invalidation(bvm *vm); +extern int lvbe_disp_is_invalidation_enabled(bvm *vm); extern int lvbe_disp_dpx(bvm *vm); /* `lv_obj` external functions definitions */ @@ -259,6 +255,8 @@ extern int lvbe_obj_refresh_self_size(bvm *vm); extern int lvbe_obj_refr_pos(bvm *vm); extern int lvbe_obj_move_to(bvm *vm); extern int lvbe_obj_move_children_by(bvm *vm); +extern int lvbe_obj_transform_point(bvm *vm); +extern int lvbe_obj_get_transformed_area(bvm *vm); extern int lvbe_obj_invalidate_area(bvm *vm); extern int lvbe_obj_invalidate(bvm *vm); extern int lvbe_obj_area_is_visible(bvm *vm); @@ -299,6 +297,7 @@ extern int lvbe_obj_remove_style_all(bvm *vm); extern int lvbe_obj_refresh_style(bvm *vm); extern int lvbe_obj_get_style_prop(bvm *vm); extern int lvbe_obj_set_local_style_prop(bvm *vm); +extern int lvbe_obj_set_local_style_prop_meta(bvm *vm); extern int lvbe_obj_get_local_style_prop(bvm *vm); extern int lvbe_obj_remove_local_style_prop(bvm *vm); extern int lvbe_obj_fade_in(bvm *vm); @@ -324,6 +323,8 @@ extern int lvbe_obj_get_style_translate_x(bvm *vm); extern int lvbe_obj_get_style_translate_y(bvm *vm); extern int lvbe_obj_get_style_transform_zoom(bvm *vm); extern int lvbe_obj_get_style_transform_angle(bvm *vm); +extern int lvbe_obj_get_style_transform_pivot_x(bvm *vm); +extern int lvbe_obj_get_style_transform_pivot_y(bvm *vm); extern int lvbe_obj_get_style_pad_top(bvm *vm); extern int lvbe_obj_get_style_pad_bottom(bvm *vm); extern int lvbe_obj_get_style_pad_left(bvm *vm); @@ -394,6 +395,7 @@ extern int lvbe_obj_get_style_clip_corner(bvm *vm); extern int lvbe_obj_get_style_opa(bvm *vm); extern int lvbe_obj_get_style_color_filter_dsc(bvm *vm); extern int lvbe_obj_get_style_color_filter_opa(bvm *vm); +extern int lvbe_obj_get_style_anim(bvm *vm); extern int lvbe_obj_get_style_anim_time(bvm *vm); extern int lvbe_obj_get_style_anim_speed(bvm *vm); extern int lvbe_obj_get_style_transition(bvm *vm); @@ -415,6 +417,8 @@ extern int lvbe_obj_set_style_translate_x(bvm *vm); extern int lvbe_obj_set_style_translate_y(bvm *vm); extern int lvbe_obj_set_style_transform_zoom(bvm *vm); extern int lvbe_obj_set_style_transform_angle(bvm *vm); +extern int lvbe_obj_set_style_transform_pivot_x(bvm *vm); +extern int lvbe_obj_set_style_transform_pivot_y(bvm *vm); extern int lvbe_obj_set_style_pad_top(bvm *vm); extern int lvbe_obj_set_style_pad_bottom(bvm *vm); extern int lvbe_obj_set_style_pad_left(bvm *vm); @@ -422,10 +426,8 @@ extern int lvbe_obj_set_style_pad_right(bvm *vm); extern int lvbe_obj_set_style_pad_row(bvm *vm); extern int lvbe_obj_set_style_pad_column(bvm *vm); extern int lvbe_obj_set_style_bg_color(bvm *vm); -extern int lvbe_obj_set_style_bg_color_filtered(bvm *vm); extern int lvbe_obj_set_style_bg_opa(bvm *vm); extern int lvbe_obj_set_style_bg_grad_color(bvm *vm); -extern int lvbe_obj_set_style_bg_grad_color_filtered(bvm *vm); extern int lvbe_obj_set_style_bg_grad_dir(bvm *vm); extern int lvbe_obj_set_style_bg_main_stop(bvm *vm); extern int lvbe_obj_set_style_bg_grad_stop(bvm *vm); @@ -434,18 +436,15 @@ extern int lvbe_obj_set_style_bg_dither_mode(bvm *vm); extern int lvbe_obj_set_style_bg_img_src(bvm *vm); extern int lvbe_obj_set_style_bg_img_opa(bvm *vm); extern int lvbe_obj_set_style_bg_img_recolor(bvm *vm); -extern int lvbe_obj_set_style_bg_img_recolor_filtered(bvm *vm); extern int lvbe_obj_set_style_bg_img_recolor_opa(bvm *vm); extern int lvbe_obj_set_style_bg_img_tiled(bvm *vm); extern int lvbe_obj_set_style_border_color(bvm *vm); -extern int lvbe_obj_set_style_border_color_filtered(bvm *vm); extern int lvbe_obj_set_style_border_opa(bvm *vm); extern int lvbe_obj_set_style_border_width(bvm *vm); extern int lvbe_obj_set_style_border_side(bvm *vm); extern int lvbe_obj_set_style_border_post(bvm *vm); extern int lvbe_obj_set_style_outline_width(bvm *vm); extern int lvbe_obj_set_style_outline_color(bvm *vm); -extern int lvbe_obj_set_style_outline_color_filtered(bvm *vm); extern int lvbe_obj_set_style_outline_opa(bvm *vm); extern int lvbe_obj_set_style_outline_pad(bvm *vm); extern int lvbe_obj_set_style_shadow_width(bvm *vm); @@ -453,27 +452,22 @@ extern int lvbe_obj_set_style_shadow_ofs_x(bvm *vm); extern int lvbe_obj_set_style_shadow_ofs_y(bvm *vm); extern int lvbe_obj_set_style_shadow_spread(bvm *vm); extern int lvbe_obj_set_style_shadow_color(bvm *vm); -extern int lvbe_obj_set_style_shadow_color_filtered(bvm *vm); extern int lvbe_obj_set_style_shadow_opa(bvm *vm); extern int lvbe_obj_set_style_img_opa(bvm *vm); extern int lvbe_obj_set_style_img_recolor(bvm *vm); -extern int lvbe_obj_set_style_img_recolor_filtered(bvm *vm); extern int lvbe_obj_set_style_img_recolor_opa(bvm *vm); extern int lvbe_obj_set_style_line_width(bvm *vm); extern int lvbe_obj_set_style_line_dash_width(bvm *vm); extern int lvbe_obj_set_style_line_dash_gap(bvm *vm); extern int lvbe_obj_set_style_line_rounded(bvm *vm); extern int lvbe_obj_set_style_line_color(bvm *vm); -extern int lvbe_obj_set_style_line_color_filtered(bvm *vm); extern int lvbe_obj_set_style_line_opa(bvm *vm); extern int lvbe_obj_set_style_arc_width(bvm *vm); extern int lvbe_obj_set_style_arc_rounded(bvm *vm); extern int lvbe_obj_set_style_arc_color(bvm *vm); -extern int lvbe_obj_set_style_arc_color_filtered(bvm *vm); extern int lvbe_obj_set_style_arc_opa(bvm *vm); extern int lvbe_obj_set_style_arc_img_src(bvm *vm); extern int lvbe_obj_set_style_text_color(bvm *vm); -extern int lvbe_obj_set_style_text_color_filtered(bvm *vm); extern int lvbe_obj_set_style_text_opa(bvm *vm); extern int lvbe_obj_set_style_text_font(bvm *vm); extern int lvbe_obj_set_style_text_letter_space(bvm *vm); @@ -485,6 +479,7 @@ extern int lvbe_obj_set_style_clip_corner(bvm *vm); extern int lvbe_obj_set_style_opa(bvm *vm); extern int lvbe_obj_set_style_color_filter_dsc(bvm *vm); extern int lvbe_obj_set_style_color_filter_opa(bvm *vm); +extern int lvbe_obj_set_style_anim(bvm *vm); extern int lvbe_obj_set_style_anim_time(bvm *vm); extern int lvbe_obj_set_style_anim_speed(bvm *vm); extern int lvbe_obj_set_style_transition(bvm *vm); @@ -559,11 +554,13 @@ extern int lvbe_group_focus_prev(bvm *vm); extern int lvbe_group_focus_freeze(bvm *vm); extern int lvbe_group_send_data(bvm *vm); extern int lvbe_group_set_focus_cb(bvm *vm); +extern int lvbe_group_set_edge_cb(bvm *vm); extern int lvbe_group_set_refocus_policy(bvm *vm); extern int lvbe_group_set_editing(bvm *vm); extern int lvbe_group_set_wrap(bvm *vm); extern int lvbe_group_get_focused(bvm *vm); extern int lvbe_group_get_focus_cb(bvm *vm); +extern int lvbe_group_get_edge_cb(bvm *vm); extern int lvbe_group_get_editing(bvm *vm); extern int lvbe_group_get_wrap(bvm *vm); extern int lvbe_group_get_obj_count(bvm *vm); @@ -686,7 +683,7 @@ extern int lvbe_spinbox_set_rollover(bvm *vm); extern int lvbe_spinbox_set_digit_format(bvm *vm); extern int lvbe_spinbox_set_step(bvm *vm); extern int lvbe_spinbox_set_range(bvm *vm); -extern int lvbe_spinbox_set_pos(bvm *vm); +extern int lvbe_spinbox_set_cursor_pos(bvm *vm); extern int lvbe_spinbox_set_digit_step_direction(bvm *vm); extern int lvbe_spinbox_get_rollover(bvm *vm); extern int lvbe_spinbox_get_value(bvm *vm); @@ -711,6 +708,7 @@ extern int lvbe_anim_set_path_cb(bvm *vm); extern int lvbe_anim_set_start_cb(bvm *vm); extern int lvbe_anim_set_get_value_cb(bvm *vm); extern int lvbe_anim_set_ready_cb(bvm *vm); +extern int lvbe_anim_set_deleted_cb(bvm *vm); extern int lvbe_anim_set_playback_time(bvm *vm); extern int lvbe_anim_set_playback_delay(bvm *vm); extern int lvbe_anim_set_repeat_count(bvm *vm); @@ -756,6 +754,8 @@ extern int lvbe_arc_get_value(bvm *vm); extern int lvbe_arc_get_min_value(bvm *vm); extern int lvbe_arc_get_max_value(bvm *vm); extern int lvbe_arc_get_mode(bvm *vm); +extern int lvbe_arc_align_obj_to_angle(bvm *vm); +extern int lvbe_arc_rotate_obj_to_angle(bvm *vm); /* `lv_bar` external functions definitions */ extern int lvbe_bar_create(bvm *vm); @@ -833,6 +833,7 @@ extern int lvbe_dropdown_get_options(bvm *vm); extern int lvbe_dropdown_get_selected(bvm *vm); extern int lvbe_dropdown_get_option_cnt(bvm *vm); extern int lvbe_dropdown_get_selected_str(bvm *vm); +extern int lvbe_dropdown_get_option_index(bvm *vm); extern int lvbe_dropdown_get_symbol(bvm *vm); extern int lvbe_dropdown_get_selected_highlight(bvm *vm); extern int lvbe_dropdown_get_dir(bvm *vm); @@ -919,6 +920,7 @@ extern int lvbe_textarea_set_placeholder_text(bvm *vm); extern int lvbe_textarea_set_cursor_pos(bvm *vm); extern int lvbe_textarea_set_cursor_click_pos(bvm *vm); extern int lvbe_textarea_set_password_mode(bvm *vm); +extern int lvbe_textarea_set_password_bullet(bvm *vm); extern int lvbe_textarea_set_one_line(bvm *vm); extern int lvbe_textarea_set_accepted_chars(bvm *vm); extern int lvbe_textarea_set_max_length(bvm *vm); @@ -932,6 +934,7 @@ extern int lvbe_textarea_get_label(bvm *vm); extern int lvbe_textarea_get_cursor_pos(bvm *vm); extern int lvbe_textarea_get_cursor_click_pos(bvm *vm); extern int lvbe_textarea_get_password_mode(bvm *vm); +extern int lvbe_textarea_get_password_bullet(bvm *vm); extern int lvbe_textarea_get_one_line(bvm *vm); extern int lvbe_textarea_get_accepted_chars(bvm *vm); extern int lvbe_textarea_get_max_length(bvm *vm); diff --git a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h index 8e216a122..c5d2bbb5f 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h +++ b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_enum.h @@ -124,7 +124,13 @@ LV_SCR_LOAD_ANIM_MOVE_LEFT LV_SCR_LOAD_ANIM_MOVE_RIGHT LV_SCR_LOAD_ANIM_MOVE_TOP LV_SCR_LOAD_ANIM_MOVE_BOTTOM +LV_SCR_LOAD_ANIM_FADE_IN LV_SCR_LOAD_ANIM_FADE_ON +LV_SCR_LOAD_ANIM_FADE_OUT +LV_SCR_LOAD_ANIM_OUT_LEFT +LV_SCR_LOAD_ANIM_OUT_RIGHT +LV_SCR_LOAD_ANIM_OUT_TOP +LV_SCR_LOAD_ANIM_OUT_BOTTOM // File: ../../lvgl/src/core/lv_event.h LV_EVENT_ALL @@ -267,6 +273,10 @@ LV_COVER_RES_COVER LV_COVER_RES_NOT_COVER LV_COVER_RES_MASKED +LV_LAYER_TYPE_NONE +LV_LAYER_TYPE_SIMPLE +LV_LAYER_TYPE_TRANSFORM + // File: ../../lvgl/src/core/lv_obj_pos.h // File: ../../lvgl/src/core/lv_obj_scroll.h LV_SCROLLBAR_MODE_OFF @@ -292,6 +302,11 @@ LV_OBJ_TREE_WALK_END // File: ../../lvgl/src/draw/lv_draw_arc.h // File: ../../lvgl/src/draw/lv_draw_img.h // File: ../../lvgl/src/draw/lv_draw_label.h +// File: ../../lvgl/src/draw/lv_draw_layer.h +LV_DRAW_LAYER_FLAG_NONE +LV_DRAW_LAYER_FLAG_HAS_ALPHA +LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE + // File: ../../lvgl/src/draw/lv_draw_line.h // File: ../../lvgl/src/draw/lv_draw_mask.h LV_DRAW_MASK_RES_TRANSP @@ -312,6 +327,7 @@ LV_DRAW_MASK_LINE_SIDE_BOTTOM // File: ../../lvgl/src/draw/lv_draw_rect.h LV_RADIUS_CIRCLE +// File: ../../lvgl/src/draw/lv_draw_transform.h // File: ../../lvgl/src/draw/lv_draw_triangle.h // File: ../../lvgl/src/draw/lv_img_buf.h LV_IMG_CF_UNKNOWN @@ -329,6 +345,12 @@ LV_IMG_CF_ALPHA_1BIT LV_IMG_CF_ALPHA_2BIT LV_IMG_CF_ALPHA_4BIT LV_IMG_CF_ALPHA_8BIT +LV_IMG_CF_RGB888 +LV_IMG_CF_RGBA8888 +LV_IMG_CF_RGBX8888 +LV_IMG_CF_RGB565 +LV_IMG_CF_RGBA5658 +LV_IMG_CF_RGB565A8 // File: ../../lvgl/src/draw/lv_img_cache.h // File: ../../lvgl/src/draw/lv_img_decoder.h @@ -623,23 +645,19 @@ LV_STYLE_MAX_HEIGHT LV_STYLE_X LV_STYLE_Y LV_STYLE_ALIGN -LV_STYLE_TRANSFORM_WIDTH -LV_STYLE_TRANSFORM_HEIGHT -LV_STYLE_TRANSLATE_X -LV_STYLE_TRANSLATE_Y -LV_STYLE_TRANSFORM_ZOOM -LV_STYLE_TRANSFORM_ANGLE +LV_STYLE_LAYOUT +LV_STYLE_RADIUS LV_STYLE_PAD_TOP LV_STYLE_PAD_BOTTOM LV_STYLE_PAD_LEFT LV_STYLE_PAD_RIGHT LV_STYLE_PAD_ROW LV_STYLE_PAD_COLUMN +LV_STYLE_BASE_DIR +LV_STYLE_CLIP_CORNER LV_STYLE_BG_COLOR -LV_STYLE_BG_COLOR_FILTERED LV_STYLE_BG_OPA LV_STYLE_BG_GRAD_COLOR -LV_STYLE_BG_GRAD_COLOR_FILTERED LV_STYLE_BG_GRAD_DIR LV_STYLE_BG_MAIN_STOP LV_STYLE_BG_GRAD_STOP @@ -648,18 +666,15 @@ LV_STYLE_BG_DITHER_MODE LV_STYLE_BG_IMG_SRC LV_STYLE_BG_IMG_OPA LV_STYLE_BG_IMG_RECOLOR -LV_STYLE_BG_IMG_RECOLOR_FILTERED LV_STYLE_BG_IMG_RECOLOR_OPA LV_STYLE_BG_IMG_TILED LV_STYLE_BORDER_COLOR -LV_STYLE_BORDER_COLOR_FILTERED LV_STYLE_BORDER_OPA LV_STYLE_BORDER_WIDTH LV_STYLE_BORDER_SIDE LV_STYLE_BORDER_POST LV_STYLE_OUTLINE_WIDTH LV_STYLE_OUTLINE_COLOR -LV_STYLE_OUTLINE_COLOR_FILTERED LV_STYLE_OUTLINE_OPA LV_STYLE_OUTLINE_PAD LV_STYLE_SHADOW_WIDTH @@ -667,45 +682,48 @@ LV_STYLE_SHADOW_OFS_X LV_STYLE_SHADOW_OFS_Y LV_STYLE_SHADOW_SPREAD LV_STYLE_SHADOW_COLOR -LV_STYLE_SHADOW_COLOR_FILTERED LV_STYLE_SHADOW_OPA LV_STYLE_IMG_OPA LV_STYLE_IMG_RECOLOR -LV_STYLE_IMG_RECOLOR_FILTERED LV_STYLE_IMG_RECOLOR_OPA LV_STYLE_LINE_WIDTH LV_STYLE_LINE_DASH_WIDTH LV_STYLE_LINE_DASH_GAP LV_STYLE_LINE_ROUNDED LV_STYLE_LINE_COLOR -LV_STYLE_LINE_COLOR_FILTERED LV_STYLE_LINE_OPA LV_STYLE_ARC_WIDTH LV_STYLE_ARC_ROUNDED LV_STYLE_ARC_COLOR -LV_STYLE_ARC_COLOR_FILTERED LV_STYLE_ARC_OPA LV_STYLE_ARC_IMG_SRC LV_STYLE_TEXT_COLOR -LV_STYLE_TEXT_COLOR_FILTERED LV_STYLE_TEXT_OPA LV_STYLE_TEXT_FONT LV_STYLE_TEXT_LETTER_SPACE LV_STYLE_TEXT_LINE_SPACE LV_STYLE_TEXT_DECOR LV_STYLE_TEXT_ALIGN -LV_STYLE_RADIUS -LV_STYLE_CLIP_CORNER LV_STYLE_OPA LV_STYLE_COLOR_FILTER_DSC LV_STYLE_COLOR_FILTER_OPA +LV_STYLE_ANIM LV_STYLE_ANIM_TIME LV_STYLE_ANIM_SPEED LV_STYLE_TRANSITION LV_STYLE_BLEND_MODE -LV_STYLE_LAYOUT -LV_STYLE_BASE_DIR +LV_STYLE_TRANSFORM_WIDTH +LV_STYLE_TRANSFORM_HEIGHT +LV_STYLE_TRANSLATE_X +LV_STYLE_TRANSLATE_Y +LV_STYLE_TRANSFORM_ZOOM +LV_STYLE_TRANSFORM_ANGLE +LV_STYLE_TRANSFORM_PIVOT_X +LV_STYLE_TRANSFORM_PIVOT_Y LV_STYLE_PROP_ANY +LV_STYLE_RES_NOT_FOUND +LV_STYLE_RES_FOUND +LV_STYLE_RES_INHERIT LV_IMG_ZOOM_NONE // File: ../../lvgl/src/misc/lv_style_gen.h // File: ../../lvgl/src/misc/lv_templ.h diff --git a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_funcs.h b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_funcs.h index 46c91aa8f..125384800 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_funcs.h +++ b/lib/libesp32_lvgl/lv_binding_berry/mapping/lv_funcs.h @@ -44,6 +44,8 @@ void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t tim uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp) void lv_disp_trig_activity(lv_disp_t * disp) void lv_disp_clean_dcache(lv_disp_t * disp) +void lv_disp_enable_invalidation(lv_disp_t * disp, bool en) +bool lv_disp_is_invalidation_enabled(lv_disp_t * disp) static inline lv_obj_t * lv_scr_act(void) static inline lv_obj_t * lv_layer_top(void) static inline lv_obj_t * lv_layer_sys(void) @@ -80,11 +82,13 @@ void lv_group_focus_prev(lv_group_t * group) void lv_group_focus_freeze(lv_group_t * group, bool en) lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c) void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb) +void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb) void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy) void lv_group_set_editing(lv_group_t * group, bool edit) void lv_group_set_wrap(lv_group_t * group, bool en) struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group) lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group) +lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group) bool lv_group_get_editing(const lv_group_t * group) bool lv_group_get_wrap(lv_group_t * group) uint32_t lv_group_get_obj_count(lv_group_t * group) @@ -187,6 +191,8 @@ bool lv_obj_refresh_self_size(struct _lv_obj_t * obj) void lv_obj_refr_pos(struct _lv_obj_t * obj) void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y) void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating) +void lv_obj_transform_point(const struct _lv_obj_t * obj, lv_point_t * p, bool recursive, bool inv) +void lv_obj_get_transformed_area(const struct _lv_obj_t * obj, lv_area_t * area, bool recursive, bool inv) void lv_obj_invalidate_area(const struct _lv_obj_t * obj, const lv_area_t * area) void lv_obj_invalidate(const struct _lv_obj_t * obj) bool lv_obj_area_is_visible(const struct _lv_obj_t * obj, lv_area_t * area) @@ -235,7 +241,8 @@ void lv_obj_refresh_style(struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_ void lv_obj_enable_style_refresh(bool en) lv_style_value_t lv_obj_get_style_prop(const struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop) void lv_obj_set_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value, lv_style_selector_t selector) -lv_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, lv_style_selector_t selector) +void lv_obj_set_local_style_prop_meta(struct _lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta, lv_style_selector_t selector) +lv_style_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, lv_style_selector_t selector) bool lv_obj_remove_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector) void lv_obj_fade_in(struct _lv_obj_t * obj, uint32_t time, uint32_t delay) void lv_obj_fade_out(struct _lv_obj_t * obj, uint32_t time, uint32_t delay) @@ -264,6 +271,8 @@ static inline lv_coord_t lv_obj_get_style_translate_x(const struct _lv_obj_t * o static inline lv_coord_t lv_obj_get_style_translate_y(const struct _lv_obj_t * obj, uint32_t part) static inline lv_coord_t lv_obj_get_style_transform_zoom(const struct _lv_obj_t * obj, uint32_t part) static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t * obj, uint32_t part) +static inline lv_coord_t lv_obj_get_style_transform_pivot_x(const struct _lv_obj_t * obj, uint32_t part) +static inline lv_coord_t lv_obj_get_style_transform_pivot_y(const struct _lv_obj_t * obj, uint32_t part) static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part) static inline lv_coord_t lv_obj_get_style_pad_bottom(const struct _lv_obj_t * obj, uint32_t part) static inline lv_coord_t lv_obj_get_style_pad_left(const struct _lv_obj_t * obj, uint32_t part) @@ -334,6 +343,7 @@ static inline bool lv_obj_get_style_clip_corner(const struct _lv_obj_t * obj, ui static inline lv_opa_t lv_obj_get_style_opa(const struct _lv_obj_t * obj, uint32_t part) static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, uint32_t part) static inline lv_opa_t lv_obj_get_style_color_filter_opa(const struct _lv_obj_t * obj, uint32_t part) +static inline const lv_anim_t * lv_obj_get_style_anim(const struct _lv_obj_t * obj, uint32_t part) static inline uint32_t lv_obj_get_style_anim_time(const struct _lv_obj_t * obj, uint32_t part) static inline uint32_t lv_obj_get_style_anim_speed(const struct _lv_obj_t * obj, uint32_t part) static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const struct _lv_obj_t * obj, uint32_t part) @@ -355,6 +365,8 @@ void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_s void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) +void lv_obj_set_style_transform_pivot_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) +void lv_obj_set_style_transform_pivot_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) @@ -362,10 +374,8 @@ void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_sty void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) @@ -374,18 +384,15 @@ void lv_obj_set_style_bg_dither_mode(struct _lv_obj_t * obj, lv_dither_mode_t va void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector) void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector) void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector) void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector) void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) @@ -393,27 +400,22 @@ void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_ void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector) void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector) void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector) void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector) void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) @@ -425,6 +427,7 @@ void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_s void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector) void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) +void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector) void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector) void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector) void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector) @@ -451,7 +454,7 @@ void lv_obj_tree_walk(struct _lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, vo // ../../lvgl/src/core/lv_refr.h void lv_refr_now(lv_disp_t * disp) -void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) +void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) // ../../lvgl/src/core/lv_theme.h lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj) @@ -466,6 +469,7 @@ lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj) // ../../lvgl/src/draw/lv_draw.h void lv_draw_init(void) +void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx) // ../../lvgl/src/draw/lv_draw_arc.h void lv_draw_arc_dsc_init(lv_draw_arc_dsc_t * dsc) @@ -483,6 +487,12 @@ void lv_draw_label_dsc_init(lv_draw_label_dsc_t * dsc) void lv_draw_label(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_area_t * coords, const char * txt, lv_draw_label_hint_t * hint) void lv_draw_letter(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p, uint32_t letter) +// ../../lvgl/src/draw/lv_draw_layer.h +struct _lv_draw_layer_ctx_t * lv_draw_layer_create(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area, lv_draw_layer_flags_t flags) +void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, lv_draw_layer_flags_t flags) +void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, lv_draw_img_dsc_t * draw_dsc) +void lv_draw_layer_destroy(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx) + // ../../lvgl/src/draw/lv_draw_line.h void lv_draw_line_dsc_init(lv_draw_line_dsc_t * dsc) void lv_draw_line(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1, const lv_point_t * point2) @@ -510,6 +520,9 @@ void lv_draw_mask_polygon_init(lv_draw_mask_polygon_param_t * param, const lv_po void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc) void lv_draw_rect(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +// ../../lvgl/src/draw/lv_draw_transform.h +void lv_draw_transform(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) + // ../../lvgl/src/draw/lv_draw_triangle.h void lv_draw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[], uint16_t point_cnt) void lv_draw_triangle(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t points[]) @@ -592,6 +605,7 @@ bool lv_theme_default_is_inited(void) // ../../lvgl/src/extra/themes/mono/lv_theme_mono.h lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font) +bool lv_theme_mono_is_inited(void) // ../../lvgl/src/extra/widgets/chart/lv_chart.h lv_obj_t * lv_chart_create(lv_obj_t * parent) @@ -689,7 +703,7 @@ void lv_spinbox_set_rollover(lv_obj_t * obj, bool b) void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t separator_position) void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step) void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max) -void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos) +void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos) void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction) bool lv_spinbox_get_rollover(lv_obj_t * obj) int32_t lv_spinbox_get_value(lv_obj_t * obj) @@ -726,6 +740,7 @@ static inline void lv_anim_set_path_cb(lv_anim_t * a, lv_anim_path_cb_t path_cb) static inline void lv_anim_set_start_cb(lv_anim_t * a, lv_anim_start_cb_t start_cb) static inline void lv_anim_set_get_value_cb(lv_anim_t * a, lv_anim_get_value_cb_t get_value_cb) static inline void lv_anim_set_ready_cb(lv_anim_t * a, lv_anim_ready_cb_t ready_cb) +static inline void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t deleted_cb) static inline void lv_anim_set_playback_time(lv_anim_t * a, uint32_t time) static inline void lv_anim_set_playback_delay(lv_anim_t * a, uint32_t delay) static inline void lv_anim_set_repeat_count(lv_anim_t * a, uint16_t cnt) @@ -739,6 +754,7 @@ static inline void * lv_anim_get_user_data(lv_anim_t * a) bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) void lv_anim_del_all(void) lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb) +struct _lv_timer_t * lv_anim_get_timer(void) static inline bool lv_anim_custom_del(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb) static inline lv_anim_t * lv_anim_custom_get(lv_anim_t * a, lv_anim_custom_exec_cb_t exec_cb) uint16_t lv_anim_count_running(void) @@ -756,6 +772,7 @@ uint32_t lv_area_get_size(const lv_area_t * area_p) void lv_area_increase(lv_area_t * area, lv_coord_t w_extra, lv_coord_t h_extra) void lv_area_move(lv_area_t * area, lv_coord_t x_ofs, lv_coord_t y_ofs) void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y) +void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot) static inline lv_coord_t lv_pct(lv_coord_t x) // ../../lvgl/src/misc/lv_color.h @@ -798,19 +815,22 @@ uint32_t lv_rand(uint32_t min, uint32_t max) // ../../lvgl/src/misc/lv_style.h void lv_style_init(lv_style_t * style) void lv_style_reset(lv_style_t * style) -lv_style_prop_t lv_style_register_prop(void) +lv_style_prop_t lv_style_register_prop(uint8_t flag) +lv_style_prop_t lv_style_get_num_custom_props(void) bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value) -lv_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) -static inline lv_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) +void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta) +lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style_prop_t props[], lv_anim_path_cb_t path_cb, uint32_t time, uint32_t delay, void * user_data) lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop) +static inline lv_style_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) bool lv_style_is_empty(const lv_style_t * style) static inline void lv_style_set_size(lv_style_t * style, lv_coord_t value) static inline void lv_style_set_pad_all(lv_style_t * style, lv_coord_t value) static inline void lv_style_set_pad_hor(lv_style_t * style, lv_coord_t value) static inline void lv_style_set_pad_ver(lv_style_t * style, lv_coord_t value) static inline void lv_style_set_pad_gap(lv_style_t * style, lv_coord_t value) +static inline bool lv_style_prop_has_flag(lv_style_prop_t prop, uint8_t flag) // ../../lvgl/src/misc/lv_style_gen.h void lv_style_set_width(lv_style_t * style, lv_coord_t value) @@ -828,6 +848,8 @@ void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value) void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value) void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value) void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value) +void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value) +void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value) void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value) void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value) void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value) @@ -835,10 +857,8 @@ void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value) void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value) void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value) void lv_style_set_bg_color(lv_style_t * style, lv_color_t value) -void lv_style_set_bg_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value) -void lv_style_set_bg_grad_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value) void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value) void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value) @@ -847,18 +867,15 @@ void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value) void lv_style_set_bg_img_src(lv_style_t * style, const void * value) void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value) -void lv_style_set_bg_img_recolor_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_bg_img_tiled(lv_style_t * style, bool value) void lv_style_set_border_color(lv_style_t * style, lv_color_t value) -void lv_style_set_border_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_border_width(lv_style_t * style, lv_coord_t value) void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value) void lv_style_set_border_post(lv_style_t * style, bool value) void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value) void lv_style_set_outline_color(lv_style_t * style, lv_color_t value) -void lv_style_set_outline_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value) void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value) @@ -866,27 +883,22 @@ void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value) void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value) void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value) void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value) -void lv_style_set_shadow_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value) -void lv_style_set_img_recolor_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_line_width(lv_style_t * style, lv_coord_t value) void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value) void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value) void lv_style_set_line_rounded(lv_style_t * style, bool value) void lv_style_set_line_color(lv_style_t * style, lv_color_t value) -void lv_style_set_line_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value) void lv_style_set_arc_rounded(lv_style_t * style, bool value) void lv_style_set_arc_color(lv_style_t * style, lv_color_t value) -void lv_style_set_arc_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_arc_img_src(lv_style_t * style, const void * value) void lv_style_set_text_color(lv_style_t * style, lv_color_t value) -void lv_style_set_text_color_filtered(lv_style_t * style, lv_color_t value) void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value) void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value) @@ -898,6 +910,7 @@ void lv_style_set_clip_corner(lv_style_t * style, bool value) void lv_style_set_opa(lv_style_t * style, lv_opa_t value) void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value) void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value) +void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value) void lv_style_set_anim_time(lv_style_t * style, uint32_t value) void lv_style_set_anim_speed(lv_style_t * style, uint32_t value) void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value) @@ -907,6 +920,7 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value) // ../../lvgl/src/misc/lv_timer.h uint32_t lv_timer_handler(void) +static inline uint32_t lv_timer_handler_run_in_period(uint32_t ms) lv_timer_t * lv_timer_create_basic(void) lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data) void lv_timer_del(lv_timer_t * timer) @@ -923,17 +937,17 @@ lv_timer_t * lv_timer_get_next(lv_timer_t * timer) // ../../lvgl/src/widgets/lv_arc.h lv_obj_t * lv_arc_create(lv_obj_t * parent) -void lv_arc_set_start_angle(lv_obj_t * arc, uint16_t start) -void lv_arc_set_end_angle(lv_obj_t * arc, uint16_t end) -void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end) -void lv_arc_set_bg_start_angle(lv_obj_t * arc, uint16_t start) -void lv_arc_set_bg_end_angle(lv_obj_t * arc, uint16_t end) -void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end) -void lv_arc_set_rotation(lv_obj_t * arc, uint16_t rotation) -void lv_arc_set_mode(lv_obj_t * arc, lv_arc_mode_t type) -void lv_arc_set_value(lv_obj_t * arc, int16_t value) -void lv_arc_set_range(lv_obj_t * arc, int16_t min, int16_t max) -void lv_arc_set_change_rate(lv_obj_t * arc, uint16_t rate) +void lv_arc_set_start_angle(lv_obj_t * obj, uint16_t start) +void lv_arc_set_end_angle(lv_obj_t * obj, uint16_t end) +void lv_arc_set_angles(lv_obj_t * obj, uint16_t start, uint16_t end) +void lv_arc_set_bg_start_angle(lv_obj_t * obj, uint16_t start) +void lv_arc_set_bg_end_angle(lv_obj_t * obj, uint16_t end) +void lv_arc_set_bg_angles(lv_obj_t * obj, uint16_t start, uint16_t end) +void lv_arc_set_rotation(lv_obj_t * obj, uint16_t rotation) +void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type) +void lv_arc_set_value(lv_obj_t * obj, int16_t value) +void lv_arc_set_range(lv_obj_t * obj, int16_t min, int16_t max) +void lv_arc_set_change_rate(lv_obj_t * obj, uint16_t rate) uint16_t lv_arc_get_angle_start(lv_obj_t * obj) uint16_t lv_arc_get_angle_end(lv_obj_t * obj) uint16_t lv_arc_get_bg_angle_start(lv_obj_t * obj) @@ -942,6 +956,8 @@ int16_t lv_arc_get_value(const lv_obj_t * obj) int16_t lv_arc_get_min_value(const lv_obj_t * obj) int16_t lv_arc_get_max_value(const lv_obj_t * obj) lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj) +void lv_arc_align_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_align, lv_coord_t r_offset) +void lv_arc_rotate_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_rotate, lv_coord_t r_offset) // ../../lvgl/src/widgets/lv_bar.h lv_obj_t * lv_bar_create(lv_obj_t * parent) @@ -1019,6 +1035,7 @@ const char * lv_dropdown_get_options(const lv_obj_t * obj) uint16_t lv_dropdown_get_selected(const lv_obj_t * obj) uint16_t lv_dropdown_get_option_cnt(const lv_obj_t * obj) void lv_dropdown_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size) +int32_t lv_dropdown_get_option_index(lv_obj_t * obj, const char * option) const char * lv_dropdown_get_symbol(lv_obj_t * obj) bool lv_dropdown_get_selected_highlight(lv_obj_t * obj) lv_dir_t lv_dropdown_get_dir(const lv_obj_t * obj) @@ -1126,6 +1143,7 @@ void lv_textarea_set_placeholder_text(lv_obj_t * obj, const char * txt) void lv_textarea_set_cursor_pos(lv_obj_t * obj, int32_t pos) void lv_textarea_set_cursor_click_pos(lv_obj_t * obj, bool en) void lv_textarea_set_password_mode(lv_obj_t * obj, bool en) +void lv_textarea_set_password_bullet(lv_obj_t * obj, const char * bullet) void lv_textarea_set_one_line(lv_obj_t * obj, bool en) void lv_textarea_set_accepted_chars(lv_obj_t * obj, const char * list) void lv_textarea_set_max_length(lv_obj_t * obj, uint32_t num) @@ -1139,6 +1157,7 @@ lv_obj_t * lv_textarea_get_label(const lv_obj_t * obj) uint32_t lv_textarea_get_cursor_pos(const lv_obj_t * obj) bool lv_textarea_get_cursor_click_pos(lv_obj_t * obj) bool lv_textarea_get_password_mode(const lv_obj_t * obj) +const char * lv_textarea_get_password_bullet(lv_obj_t * obj) bool lv_textarea_get_one_line(const lv_obj_t * obj) const char * lv_textarea_get_accepted_chars(lv_obj_t * obj) uint32_t lv_textarea_get_max_length(lv_obj_t * obj) diff --git a/lib/libesp32_lvgl/lv_binding_berry/src/be_lvgl_ctypes_definitions.c b/lib/libesp32_lvgl/lv_binding_berry/src/be_lvgl_ctypes_definitions.c index 62b092dd8..33004e1e0 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/src/be_lvgl_ctypes_definitions.c +++ b/lib/libesp32_lvgl/lv_binding_berry/src/be_lvgl_ctypes_definitions.c @@ -138,7 +138,7 @@ const be_ctypes_structure_t be_lv_draw_img_dsc = { 10, /* number of elements */ be_ctypes_instance_mappings, (const be_ctypes_structure_item_t[10]) { - { "angle", 0, 0, 0, ctypes_u16, 0 }, + { "angle", 0, 0, 0, ctypes_i16, 0 }, { "antialias", 20, 0, 1, ctypes_bf, 0 }, { "blend_mode", 12, 0, 4, ctypes_bf, 0 }, { "frame_id", 16, 0, 0, ctypes_i32, 0 }, @@ -175,6 +175,27 @@ const be_ctypes_structure_t be_lv_obj_draw_part_dsc = { { "value", 64, 0, 0, ctypes_i32, 0 }, }}; +const be_ctypes_structure_t be_lv_draw_layer_ctx = { + 40, /* size in bytes */ + 14, /* number of elements */ + be_ctypes_instance_mappings, + (const be_ctypes_structure_item_t[14]) { + { "area_act_x1", 8, 0, 0, ctypes_i16, 0 }, + { "area_act_x2", 12, 0, 0, ctypes_i16, 0 }, + { "area_act_y1", 10, 0, 0, ctypes_i16, 0 }, + { "area_act_y2", 14, 0, 0, ctypes_i16, 0 }, + { "area_full_x1", 0, 0, 0, ctypes_i16, 0 }, + { "area_full_x2", 4, 0, 0, ctypes_i16, 0 }, + { "area_full_y1", 2, 0, 0, ctypes_i16, 0 }, + { "area_full_y2", 6, 0, 0, ctypes_i16, 0 }, + { "buf", 32, 0, 0, ctypes_ptr32, 0 }, + { "buf_area", 28, 0, 0, ctypes_ptr32, 0 }, + { "clip_area", 24, 0, 0, ctypes_ptr32, 0 }, + { "max_row_with_alpha", 16, 0, 0, ctypes_i16, 0 }, + { "max_row_with_no_alpha", 18, 0, 0, ctypes_i16, 0 }, + { "screen_transp", 36, 0, 0, ctypes_u8, 0 }, +}}; + const be_ctypes_structure_t be_lv_draw_mask_common_dsc = { 8, /* size in bytes */ 2, /* number of elements */ @@ -446,68 +467,6 @@ const be_ctypes_structure_t be_lv_meter_indicator = { { "type", 4, 0, 0, ctypes_u8, 0 }, }}; -const be_ctypes_structure_t be_lv_meter_indicator_needle_img = { - 24, /* size in bytes */ - 8, /* number of elements */ - be_ctypes_instance_mappings, - (const be_ctypes_structure_item_t[8]) { - { "end_value", 12, 0, 0, ctypes_i32, 0 }, - { "opa", 5, 0, 0, ctypes_u8, 0 }, - { "pivot_x", 20, 0, 0, ctypes_i16, 0 }, - { "pivot_y", 22, 0, 0, ctypes_i16, 0 }, - { "scale", 0, 0, 0, ctypes_ptr32, 0 }, - { "src", 16, 0, 0, ctypes_ptr32, 0 }, - { "start_value", 8, 0, 0, ctypes_i32, 0 }, - { "type", 4, 0, 0, ctypes_u8, 0 }, -}}; - -const be_ctypes_structure_t be_lv_meter_indicator_needle_line = { - 24, /* size in bytes */ - 8, /* number of elements */ - be_ctypes_instance_mappings, - (const be_ctypes_structure_item_t[8]) { - { "color", 20, 0, 0, ctypes_u16, 1 }, - { "end_value", 12, 0, 0, ctypes_i32, 0 }, - { "opa", 5, 0, 0, ctypes_u8, 0 }, - { "r_mod", 18, 0, 0, ctypes_i16, 0 }, - { "scale", 0, 0, 0, ctypes_ptr32, 0 }, - { "start_value", 8, 0, 0, ctypes_i32, 0 }, - { "type", 4, 0, 0, ctypes_u8, 0 }, - { "width", 16, 0, 0, ctypes_u16, 0 }, -}}; - -const be_ctypes_structure_t be_lv_meter_indicator_arc = { - 28, /* size in bytes */ - 9, /* number of elements */ - be_ctypes_instance_mappings, - (const be_ctypes_structure_item_t[9]) { - { "color", 24, 0, 0, ctypes_u16, 1 }, - { "end_value", 12, 0, 0, ctypes_i32, 0 }, - { "opa", 5, 0, 0, ctypes_u8, 0 }, - { "r_mod", 26, 0, 0, ctypes_i16, 0 }, - { "scale", 0, 0, 0, ctypes_ptr32, 0 }, - { "src", 20, 0, 0, ctypes_ptr32, 0 }, - { "start_value", 8, 0, 0, ctypes_i32, 0 }, - { "type", 4, 0, 0, ctypes_u8, 0 }, - { "width", 16, 0, 0, ctypes_u16, 0 }, -}}; - -const be_ctypes_structure_t be_lv_meter_indicator_scale_lines = { - 24, /* size in bytes */ - 9, /* number of elements */ - be_ctypes_instance_mappings, - (const be_ctypes_structure_item_t[9]) { - { "color_end", 20, 0, 0, ctypes_u16, 1 }, - { "color_start", 18, 0, 0, ctypes_u16, 1 }, - { "end_value", 12, 0, 0, ctypes_i32, 0 }, - { "local_grad", 22, 0, 1, ctypes_bf, 0 }, - { "opa", 5, 0, 0, ctypes_u8, 0 }, - { "scale", 0, 0, 0, ctypes_ptr32, 0 }, - { "start_value", 8, 0, 0, ctypes_i32, 0 }, - { "type", 4, 0, 0, ctypes_u8, 0 }, - { "width_mod", 16, 0, 0, ctypes_i16, 0 }, -}}; - const be_ctypes_structure_t be_lv_chart_series = { 16, /* size in bytes */ 9, /* number of elements */ @@ -654,29 +613,36 @@ const be_ctypes_structure_t be_lv_timer = { }}; const be_ctypes_structure_t be_lv_draw_ctx = { - 60, /* size in bytes */ - 19, /* number of elements */ + 88, /* size in bytes */ + 26, /* number of elements */ be_ctypes_instance_mappings, - (const be_ctypes_structure_item_t[19]) { + (const be_ctypes_structure_item_t[26]) { { "buf", 0, 0, 0, ctypes_ptr32, 0 }, { "buf_area_x1", 4, 0, 0, ctypes_i16, 0 }, { "buf_area_x2", 8, 0, 0, ctypes_i16, 0 }, { "buf_area_y1", 6, 0, 0, ctypes_i16, 0 }, { "buf_area_y2", 10, 0, 0, ctypes_i16, 0 }, + { "buffer_copy", 60, 0, 0, ctypes_ptr32, 0 }, { "clip_area_x1", 12, 0, 0, ctypes_i16, 0 }, { "clip_area_x2", 16, 0, 0, ctypes_i16, 0 }, { "clip_area_y1", 14, 0, 0, ctypes_i16, 0 }, { "clip_area_y2", 18, 0, 0, ctypes_i16, 0 }, { "draw_arc", 24, 0, 0, ctypes_ptr32, 0 }, - { "draw_bg", 48, 0, 0, ctypes_ptr32, 0 }, + { "draw_bg", 52, 0, 0, ctypes_ptr32, 0 }, { "draw_img", 32, 0, 0, ctypes_ptr32, 0 }, { "draw_img_decoded", 28, 0, 0, ctypes_ptr32, 0 }, { "draw_letter", 36, 0, 0, ctypes_ptr32, 0 }, { "draw_line", 40, 0, 0, ctypes_ptr32, 0 }, { "draw_polygon", 44, 0, 0, ctypes_ptr32, 0 }, { "draw_rect", 20, 0, 0, ctypes_ptr32, 0 }, - { "user_data", 56, 0, 0, ctypes_ptr32, 0 }, - { "wait_for_finish", 52, 0, 0, ctypes_ptr32, 0 }, + { "draw_transform", 48, 0, 0, ctypes_ptr32, 0 }, + { "layer_adjust", 68, 0, 0, ctypes_ptr32, 0 }, + { "layer_blend", 72, 0, 0, ctypes_ptr32, 0 }, + { "layer_destroy", 76, 0, 0, ctypes_ptr32, 0 }, + { "layer_init", 64, 0, 0, ctypes_ptr32, 0 }, + { "layer_instance_size", 80, 0, 0, ctypes_u32, 0 }, + { "user_data", 84, 0, 0, ctypes_ptr32, 0 }, + { "wait_for_finish", 56, 0, 0, ctypes_ptr32, 0 }, }}; const be_ctypes_structure_t be_lv_ts_calibration = { @@ -705,6 +671,7 @@ static be_define_ctypes_class(lv_coord, &be_lv_coord, &be_class_ctypes_bytes, "l static be_define_ctypes_class(lv_draw_arc_dsc, &be_lv_draw_arc_dsc, &be_class_ctypes_bytes, "lv_draw_arc_dsc"); static be_define_ctypes_class(lv_draw_ctx, &be_lv_draw_ctx, &be_class_ctypes_bytes, "lv_draw_ctx"); static be_define_ctypes_class(lv_draw_img_dsc, &be_lv_draw_img_dsc, &be_class_ctypes_bytes, "lv_draw_img_dsc"); +static be_define_ctypes_class(lv_draw_layer_ctx, &be_lv_draw_layer_ctx, &be_class_ctypes_bytes, "lv_draw_layer_ctx"); static be_define_ctypes_class(lv_draw_line_dsc, &be_lv_draw_line_dsc, &be_class_ctypes_bytes, "lv_draw_line_dsc"); static be_define_ctypes_class(lv_draw_mask_angle_param, &be_lv_draw_mask_angle_param, &be_class_ctypes_bytes, "lv_draw_mask_angle_param"); static be_define_ctypes_class(lv_draw_mask_angle_param_cfg, &be_lv_draw_mask_angle_param_cfg, &be_class_ctypes_bytes, "lv_draw_mask_angle_param_cfg"); @@ -728,10 +695,6 @@ static be_define_ctypes_class(lv_gradient_stop, &be_lv_gradient_stop, &be_class_ static be_define_ctypes_class(lv_img_dsc, &be_lv_img_dsc, &be_class_ctypes_bytes, "lv_img_dsc"); static be_define_ctypes_class(lv_img_header, &be_lv_img_header, &be_class_ctypes_bytes, "lv_img_header"); static be_define_ctypes_class(lv_meter_indicator, &be_lv_meter_indicator, &be_class_ctypes_bytes, "lv_meter_indicator"); -static be_define_ctypes_class(lv_meter_indicator_arc, &be_lv_meter_indicator_arc, &be_class_ctypes_bytes, "lv_meter_indicator_arc"); -static be_define_ctypes_class(lv_meter_indicator_needle_img, &be_lv_meter_indicator_needle_img, &be_class_ctypes_bytes, "lv_meter_indicator_needle_img"); -static be_define_ctypes_class(lv_meter_indicator_needle_line, &be_lv_meter_indicator_needle_line, &be_class_ctypes_bytes, "lv_meter_indicator_needle_line"); -static be_define_ctypes_class(lv_meter_indicator_scale_lines, &be_lv_meter_indicator_scale_lines, &be_class_ctypes_bytes, "lv_meter_indicator_scale_lines"); static be_define_ctypes_class(lv_meter_scale, &be_lv_meter_scale, &be_class_ctypes_bytes, "lv_meter_scale"); static be_define_ctypes_class(lv_obj_class, &be_lv_obj_class, &be_class_ctypes_bytes, "lv_obj_class"); static be_define_ctypes_class(lv_obj_draw_part_dsc, &be_lv_obj_draw_part_dsc, &be_class_ctypes_bytes, "lv_obj_draw_part_dsc"); @@ -751,6 +714,7 @@ void be_load_ctypes_lvgl_definitions_lib(bvm *vm) { ctypes_register_class(vm, &be_class_lv_draw_arc_dsc); ctypes_register_class(vm, &be_class_lv_draw_ctx); ctypes_register_class(vm, &be_class_lv_draw_img_dsc); + ctypes_register_class(vm, &be_class_lv_draw_layer_ctx); ctypes_register_class(vm, &be_class_lv_draw_line_dsc); ctypes_register_class(vm, &be_class_lv_draw_mask_angle_param); ctypes_register_class(vm, &be_class_lv_draw_mask_angle_param_cfg); @@ -774,10 +738,6 @@ void be_load_ctypes_lvgl_definitions_lib(bvm *vm) { ctypes_register_class(vm, &be_class_lv_img_dsc); ctypes_register_class(vm, &be_class_lv_img_header); ctypes_register_class(vm, &be_class_lv_meter_indicator); - ctypes_register_class(vm, &be_class_lv_meter_indicator_arc); - ctypes_register_class(vm, &be_class_lv_meter_indicator_needle_img); - ctypes_register_class(vm, &be_class_lv_meter_indicator_needle_line); - ctypes_register_class(vm, &be_class_lv_meter_indicator_scale_lines); ctypes_register_class(vm, &be_class_lv_meter_scale); ctypes_register_class(vm, &be_class_lv_obj_class); ctypes_register_class(vm, &be_class_lv_obj_draw_part_dsc); @@ -798,6 +758,7 @@ be_ctypes_class_by_name_t be_ctypes_lvgl_classes[] = { { "lv_draw_arc_dsc", &be_class_lv_draw_arc_dsc }, { "lv_draw_ctx", &be_class_lv_draw_ctx }, { "lv_draw_img_dsc", &be_class_lv_draw_img_dsc }, + { "lv_draw_layer_ctx", &be_class_lv_draw_layer_ctx }, { "lv_draw_line_dsc", &be_class_lv_draw_line_dsc }, { "lv_draw_mask_angle_param", &be_class_lv_draw_mask_angle_param }, { "lv_draw_mask_angle_param_cfg", &be_class_lv_draw_mask_angle_param_cfg }, @@ -821,10 +782,6 @@ be_ctypes_class_by_name_t be_ctypes_lvgl_classes[] = { { "lv_img_dsc", &be_class_lv_img_dsc }, { "lv_img_header", &be_class_lv_img_header }, { "lv_meter_indicator", &be_class_lv_meter_indicator }, - { "lv_meter_indicator_arc", &be_class_lv_meter_indicator_arc }, - { "lv_meter_indicator_needle_img", &be_class_lv_meter_indicator_needle_img }, - { "lv_meter_indicator_needle_line", &be_class_lv_meter_indicator_needle_line }, - { "lv_meter_indicator_scale_lines", &be_class_lv_meter_indicator_scale_lines }, { "lv_meter_scale", &be_class_lv_meter_scale }, { "lv_obj_class", &be_class_lv_obj_class }, { "lv_obj_draw_part_dsc", &be_class_lv_obj_draw_part_dsc }, diff --git a/lib/libesp32_lvgl/lv_binding_berry/src/embedded/lvgl_ctypes.py b/lib/libesp32_lvgl/lv_binding_berry/src/embedded/lvgl_ctypes.py index e193ece08..14cd55e11 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/src/embedded/lvgl_ctypes.py +++ b/lib/libesp32_lvgl/lv_binding_berry/src/embedded/lvgl_ctypes.py @@ -46,18 +46,18 @@ uint32_t = ct.u32 int32_t = ct.i32 ptr = ct.ptr32 -lv_coord = [ # valid LVGL8 +lv_coord = [ # valid LVGL8.3 [lv_coord_t, "v"], ] lv_coord = ct.structure(lv_coord, "lv_coord") -lv_point = [ # valid LVGL8 +lv_point = [ # valid LVGL8.3 [lv_coord_t, "x"], [lv_coord_t, "y"], ] lv_point = ct.structure(lv_point, "lv_point") -lv_area = [ # valid LVGL8 +lv_area = [ # valid LVGL8.3 [lv_coord_t, "x1"], [lv_coord_t, "y1"], [lv_coord_t, "x2"], @@ -67,13 +67,13 @@ lv_area = ct.structure(lv_area, "lv_area") ####################################################################### # lv_grad_dsc -lv_gradient_stop = [ +lv_gradient_stop = [ # valid LVGL8.3 [lv_color, "color"], [uint8_t, "frac"], ] lv_gradient_stop = ct.structure(lv_gradient_stop, "lv_gradient_stop") -lv_grad_dsc = [ +lv_grad_dsc = [ # valid LVGL8.3 [lv_gradient_stop, "stops_0"], [lv_gradient_stop, "stops_1"], [uint8_t, "stops_count"], @@ -82,7 +82,7 @@ lv_grad_dsc = [ ] lv_grad_dsc = ct.structure(lv_grad_dsc, "lv_grad_dsc") -lv_draw_rect_dsc = [ # valid LVGL8.2 +lv_draw_rect_dsc = [ # valid LVGL8.3 [lv_coord_t, "radius"], [lv_blend_mode, "blend_mode"], @@ -122,7 +122,7 @@ lv_draw_rect_dsc = [ # valid LVGL8.2 ] lv_draw_rect_dsc = ct.structure(lv_draw_rect_dsc, "lv_draw_rect_dsc") -lv_draw_line_dsc = [ # valid LVGL8.2 +lv_draw_line_dsc = [ # valid LVGL8.3 [lv_color, "color"], [lv_coord_t, "width"], [lv_coord_t, "dash_width"], @@ -135,7 +135,7 @@ lv_draw_line_dsc = [ # valid LVGL8.2 ] lv_draw_line_dsc = ct.structure(lv_draw_line_dsc, "lv_draw_line_dsc") -lv_draw_arc_dsc = [ # valid LVGL8.2 +lv_draw_arc_dsc = [ # valid LVGL8.3 [lv_color, "color"], [lv_coord_t, "width"], [uint16_t, "start_angle"], @@ -147,8 +147,8 @@ lv_draw_arc_dsc = [ # valid LVGL8.2 ] lv_draw_arc_dsc = ct.structure(lv_draw_arc_dsc, "lv_draw_arc_dsc") -lv_draw_img_dsc = [ # valid LVGL8.2 - [uint16_t, "angle"], +lv_draw_img_dsc = [ # valid LVGL8.3 + [int16_t, "angle"], [uint16_t, "zoom"], [lv_point, "pivot"], @@ -163,7 +163,7 @@ lv_draw_img_dsc = [ # valid LVGL8.2 ] lv_draw_img_dsc = ct.structure(lv_draw_img_dsc, "lv_draw_img_dsc") -lv_obj_draw_part_dsc = [ # valid LVGL8.2 +lv_obj_draw_part_dsc = [ # valid LVGL8.3 [ptr, "draw_ctx"], [ptr, "class_p"], [uint32_t, "type"], @@ -185,18 +185,31 @@ lv_obj_draw_part_dsc = [ # valid LVGL8.2 ] lv_obj_draw_part_dsc = ct.structure(lv_obj_draw_part_dsc, "lv_obj_draw_part_dsc") +lv_draw_layer_ctx = [ # valid LVGL8.3 + [lv_area, "area_full"], + [lv_area, "area_act"], + [lv_coord_t, "max_row_with_alpha"], + [lv_coord_t, "max_row_with_no_alpha"], + [ptr, "buf"], + [ptr, "clip_area"], + [ptr, "buf_area"], + [ptr, "buf"], + [uint8_t, "screen_transp"], +] +lv_draw_layer_ctx = ct.structure(lv_draw_layer_ctx, "lv_draw_layer_ctx") + #- --------- lv_mask --------- -# lv_draw_mask_xcb = ptr # callback lv_draw_mask_type = ct.u8 lv_draw_mask_line_side = ct.u8 -lv_draw_mask_common_dsc = [ # valid LVGL8.2 +lv_draw_mask_common_dsc = [ # valid LVGL8.3 [lv_draw_mask_xcb, "cb"], [lv_draw_mask_type, "type"], ] lv_draw_mask_common_dsc = ct.structure(lv_draw_mask_common_dsc, "lv_draw_mask_common_dsc") -lv_draw_mask_line_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_line_param_cfg = [ # valid LVGL8.3 #/*First point */ [lv_point, "p1"], @@ -208,7 +221,7 @@ lv_draw_mask_line_param_cfg = [ # valid LVGL8.2 ] lv_draw_mask_line_param_cfg = ct.structure(lv_draw_mask_line_param_cfg, "lv_draw_mask_line_param_cfg") -lv_draw_mask_line_param = [ # valid LVGL8.2 +lv_draw_mask_line_param = [ # valid LVGL8.3 #/*The first element must be the common descriptor*/ [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_line_param_cfg, "cfg"], @@ -230,14 +243,14 @@ lv_draw_mask_line_param = [ # valid LVGL8.2 ] lv_draw_mask_line_param = ct.structure(lv_draw_mask_line_param, "lv_draw_mask_line_param") -lv_draw_mask_angle_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_angle_param_cfg = [ # valid LVGL8.3 [lv_point, "vertex_p"], [lv_coord_t, "start_angle"], [lv_coord_t, "end_angle"], ] lv_draw_mask_angle_param_cfg = ct.structure(lv_draw_mask_angle_param_cfg, "lv_draw_mask_angle_param_cfg") -lv_draw_mask_angle_param = [ # valid LVGL8.2 +lv_draw_mask_angle_param = [ # valid LVGL8.3 #/*The first element must be the common descriptor*/ [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_angle_param_cfg, "cfg"], @@ -249,14 +262,14 @@ lv_draw_mask_angle_param = [ # valid LVGL8.2 lv_draw_mask_angle_param = ct.structure(lv_draw_mask_angle_param, "lv_draw_mask_angle_param") -lv_draw_mask_radius_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_radius_param_cfg = [ # valid LVGL8.3 [lv_area, "rect"], [lv_coord_t, "radius"], [uint8_t_1, "outer"], ] lv_draw_mask_radius_param_cfg = ct.structure(lv_draw_mask_radius_param_cfg, "lv_draw_mask_radius_param_cfg") -lv_draw_mask_radius_circle_dsc = [ # valid LVGL8.2 +lv_draw_mask_radius_circle_dsc = [ # valid LVGL8.3 [ptr, "buf"], [ptr, "cir_opa"], [ptr, "x_start_on_y"], @@ -267,7 +280,7 @@ lv_draw_mask_radius_circle_dsc = [ # valid LVGL8.2 ] lv_draw_mask_radius_circle_dsc = ct.structure(lv_draw_mask_radius_circle_dsc, "lv_draw_mask_radius_circle_dsc") -lv_draw_mask_radius_param = [ # valid LVGL8.2 +lv_draw_mask_radius_param = [ # valid LVGL8.3 #/*The first element must be the common descriptor*/ [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_radius_param_cfg, "cfg"], @@ -276,7 +289,7 @@ lv_draw_mask_radius_param = [ # valid LVGL8.2 lv_draw_mask_radius_param = ct.structure(lv_draw_mask_radius_param, "lv_draw_mask_radius_param") -lv_draw_mask_fade_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_fade_param_cfg = [ # valid LVGL8.3 [lv_area, "coords"], [lv_coord_t, "y_top"], [lv_coord_t, "y_bottom"], @@ -285,7 +298,7 @@ lv_draw_mask_fade_param_cfg = [ # valid LVGL8.2 ] lv_draw_mask_fade_param_cfg = ct.structure(lv_draw_mask_fade_param_cfg, "lv_draw_mask_fade_param_cfg") -lv_draw_mask_fade_param = [ # valid LVGL8.2 +lv_draw_mask_fade_param = [ # valid LVGL8.3 # /*The first element must be the common descriptor*/ [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_fade_param_cfg, "cfg"], @@ -293,33 +306,33 @@ lv_draw_mask_fade_param = [ # valid LVGL8.2 lv_draw_mask_fade_param = ct.structure(lv_draw_mask_fade_param, "lv_draw_mask_fade_param") -lv_draw_mask_map_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_map_param_cfg = [ # valid LVGL8.3 [lv_area, "coords"], [ptr, "map"], ] lv_draw_mask_map_param_cfg = ct.structure(lv_draw_mask_map_param_cfg, "lv_draw_mask_map_param_cfg") -lv_draw_mask_map_param = [ # valid LVGL8.2 +lv_draw_mask_map_param = [ # valid LVGL8.3 [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_map_param_cfg, "cfg"], ] lv_draw_mask_map_param = ct.structure(lv_draw_mask_map_param, "lv_draw_mask_map_param") -lv_draw_mask_polygon_param_cfg = [ # valid LVGL8.2 +lv_draw_mask_polygon_param_cfg = [ # valid LVGL8.3 [ptr, "points"], [uint16_t, "point_cnt"], ] lv_draw_mask_polygon_param_cfg = ct.structure(lv_draw_mask_polygon_param_cfg, "lv_draw_mask_polygon_param_cfg") -lv_draw_mask_polygon_param = [ # valid LVGL8.2 +lv_draw_mask_polygon_param = [ # valid LVGL8.3 [lv_draw_mask_common_dsc, "dsc"], [lv_draw_mask_polygon_param_cfg, "cfg"], ] lv_draw_mask_polygon_param = ct.structure(lv_draw_mask_polygon_param, "lv_draw_mask_polygon_param") -lv_draw_mask_saved = [ # valid LVGL8.2 +lv_draw_mask_saved = [ # valid LVGL8.3 [ptr, "param"], [ptr, "custom_id"], ] @@ -328,7 +341,7 @@ lv_draw_mask_saved = ct.structure(lv_draw_mask_saved, "lv_draw_mask_saved") # lv_meter # lv_meter_scale_t -lv_meter_scale = [ # valid LVGL8.2 +lv_meter_scale = [ # valid LVGL8.3 [lv_color, "tick_color"], [uint16_t, "tick_cnt"], [uint16_t, "tick_length"], @@ -351,7 +364,7 @@ lv_meter_scale = [ # valid LVGL8.2 lv_meter_scale = ct.structure(lv_meter_scale, "lv_meter_scale") # lv_meter_indicator_t -lv_meter_indicator = [ # valid LVGL8.2 +lv_meter_indicator = [ # valid LVGL8.3 [ptr, "scale"], [lv_meter_indicator_type_t, "type"], [lv_opa, "opa"], @@ -364,61 +377,61 @@ lv_meter_indicator = [ # valid LVGL8.2 ] lv_meter_indicator = ct.structure(lv_meter_indicator, "lv_meter_indicator") -# variants -lv_meter_indicator_needle_img = [ # valid LVGL8.2 - [ptr, "scale"], - [lv_meter_indicator_type_t, "type"], - [lv_opa, "opa"], - [int32_t, "start_value"], - [int32_t, "end_value"], - # specifc portion - [ptr, "src"], - [lv_point, "pivot"], -] -lv_meter_indicator_needle_img = ct.structure(lv_meter_indicator_needle_img, "lv_meter_indicator_needle_img") +# # variants +# lv_meter_indicator_needle_img = [ # valid LVGL8.2 +# [ptr, "scale"], +# [lv_meter_indicator_type_t, "type"], +# [lv_opa, "opa"], +# [int32_t, "start_value"], +# [int32_t, "end_value"], +# # specifc portion +# [ptr, "src"], +# [lv_point, "pivot"], +# ] +# lv_meter_indicator_needle_img = ct.structure(lv_meter_indicator_needle_img, "lv_meter_indicator_needle_img") -lv_meter_indicator_needle_line = [ # valid LVGL8.2 - [ptr, "scale"], - [lv_meter_indicator_type_t, "type"], - [lv_opa, "opa"], - [int32_t, "start_value"], - [int32_t, "end_value"], - # specifc portion - [uint16_t, "width"], - [int16_t, "r_mod"], - [lv_color, "color"], -] -lv_meter_indicator_needle_line = ct.structure(lv_meter_indicator_needle_line, "lv_meter_indicator_needle_line") +# lv_meter_indicator_needle_line = [ # valid LVGL8.2 +# [ptr, "scale"], +# [lv_meter_indicator_type_t, "type"], +# [lv_opa, "opa"], +# [int32_t, "start_value"], +# [int32_t, "end_value"], +# # specifc portion +# [uint16_t, "width"], +# [int16_t, "r_mod"], +# [lv_color, "color"], +# ] +# lv_meter_indicator_needle_line = ct.structure(lv_meter_indicator_needle_line, "lv_meter_indicator_needle_line") -lv_meter_indicator_arc = [ # valid LVGL8.2 - [ptr, "scale"], - [lv_meter_indicator_type_t, "type"], - [lv_opa, "opa"], - [int32_t, "start_value"], - [int32_t, "end_value"], - # specifc portion - [uint16_t, "width"], - [ptr, "src"], - [lv_color, "color"], - [int16_t, "r_mod"], -] -lv_meter_indicator_arc = ct.structure(lv_meter_indicator_arc, "lv_meter_indicator_arc") +# lv_meter_indicator_arc = [ # valid LVGL8.2 +# [ptr, "scale"], +# [lv_meter_indicator_type_t, "type"], +# [lv_opa, "opa"], +# [int32_t, "start_value"], +# [int32_t, "end_value"], +# # specifc portion +# [uint16_t, "width"], +# [ptr, "src"], +# [lv_color, "color"], +# [int16_t, "r_mod"], +# ] +# lv_meter_indicator_arc = ct.structure(lv_meter_indicator_arc, "lv_meter_indicator_arc") -lv_meter_indicator_scale_lines = [ # valid LVGL8.2 - [ptr, "scale"], - [lv_meter_indicator_type_t, "type"], - [lv_opa, "opa"], - [int32_t, "start_value"], - [int32_t, "end_value"], - # specifc portion - [int16_t, "width_mod"], - [lv_color, "color_start"], - [lv_color, "color_end"], - [uint8_t_1, "local_grad"], -] -lv_meter_indicator_scale_lines = ct.structure(lv_meter_indicator_scale_lines, "lv_meter_indicator_scale_lines") +# lv_meter_indicator_scale_lines = [ # valid LVGL8.2 +# [ptr, "scale"], +# [lv_meter_indicator_type_t, "type"], +# [lv_opa, "opa"], +# [int32_t, "start_value"], +# [int32_t, "end_value"], +# # specifc portion +# [int16_t, "width_mod"], +# [lv_color, "color_start"], +# [lv_color, "color_end"], +# [uint8_t_1, "local_grad"], +# ] +# lv_meter_indicator_scale_lines = ct.structure(lv_meter_indicator_scale_lines, "lv_meter_indicator_scale_lines") -lv_chart_series = [ # valid LVGL8.2 +lv_chart_series = [ # valid LVGL8.3 [ptr, "x_points"], [ptr, "y_points"], [lv_color, "color"], @@ -431,7 +444,7 @@ lv_chart_series = [ # valid LVGL8.2 ] lv_chart_series = ct.structure(lv_chart_series, "lv_chart_series") -lv_chart_cursor = [ # valid LVGL8.2 +lv_chart_cursor = [ # valid LVGL8.3 [lv_point, "pos"], [uint16_t, "point_id"], [lv_color, "color"], @@ -441,7 +454,7 @@ lv_chart_cursor = [ # valid LVGL8.2 ] lv_chart_cursor = ct.structure(lv_chart_cursor, "lv_chart_cursor") -lv_chart_tick_dsc = [ # valid LVGL8.2 +lv_chart_tick_dsc = [ # valid LVGL8.3 [lv_coord_t, "major_len"], [lv_coord_t, "minor_len"], [lv_coord_t, "draw_size"], @@ -461,7 +474,7 @@ destructor_cb = ptr # callback event_cb = ptr # callback lv_event_code = ct.i32 -lv_obj_class = [ # valid LVGL8.2 +lv_obj_class = [ # valid LVGL8.3 [lv_obj_class_ptr, "base_class"], [constructor_cb, "constructor_cb"], [destructor_cb, "destructor_cb"], @@ -475,7 +488,7 @@ lv_obj_class = [ # valid LVGL8.2 ] lv_obj_class = ct.structure(lv_obj_class, "lv_obj_class") -lv_event = [ # valid LVGL8.2 +lv_event = [ # valid LVGL8.3 [lv_obj_ptr, "target"], [lv_obj_ptr, "current_target"], [lv_event_code, "code"], @@ -488,7 +501,7 @@ lv_event = [ # valid LVGL8.2 ] lv_event = ct.structure(lv_event, "lv_event") -lv_sqrt_res = [ # valid LVGL8.2 +lv_sqrt_res = [ # valid LVGL8.3 [uint16_t, "i"], [uint16_t, "f"], ] @@ -496,7 +509,7 @@ lv_sqrt_res = ct.structure(lv_sqrt_res, "lv_sqrt_res") ####################################################################### # lv_img structures -lv_img_header = [ # valid LVGL8.2 +lv_img_header = [ # valid LVGL8.3 [uint8_t_5, "cf"], [uint8_t_3, "always_zero"], [uint8_t_2, "reserved"], @@ -505,7 +518,7 @@ lv_img_header = [ # valid LVGL8.2 ] lv_img_header = ct.structure(lv_img_header, "lv_img_header") -lv_img_dsc = [ # valid LVGL8.2 +lv_img_dsc = [ # valid LVGL8.3 [lv_img_header, "header"], [uint32_t, "data_size"], [ptr, "data"], @@ -514,7 +527,7 @@ lv_img_dsc = ct.structure(lv_img_dsc, "lv_img_dsc") ####################################################################### # lv_style -lv_style_transition_dsc = [ # valid LVGL8.2 +lv_style_transition_dsc = [ # valid LVGL8.3 [ptr, "props"], [ptr, "user_data"], [ptr, "path_xcb"], @@ -532,7 +545,7 @@ lv_style_transition_dsc = ct.structure(lv_style_transition_dsc, "lv_style_transi # ] # lv_color_hsv = ct.structure(lv_color_hsv, "lv_color_hsv") -lv_color_filter_dsc = [ # valid LVGL8.2 +lv_color_filter_dsc = [ # valid LVGL8.3 [ptr, "filter_cb"], [ptr, "user_data"], ] @@ -540,7 +553,7 @@ lv_color_filter_dsc = ct.structure(lv_color_filter_dsc, "lv_color_filter_dsc") ####################################################################### # lv_timer -lv_timer = [ # valid LVGL8.2 +lv_timer = [ # valid LVGL8.3 [uint32_t, "period"], [uint32_t, "last_run"], [ptr, "timer_cb"], @@ -582,7 +595,7 @@ lv_timer = ct.structure(lv_timer, "lv_timer") ####################################################################### # lv_draw_ctx -lv_draw_ctx = [ # valid LVGL8.2 +lv_draw_ctx = [ # valid LVGL8.3 [ptr, "buf"], [lv_area, "buf_area"], [lv_area, "clip_area"], @@ -593,8 +606,15 @@ lv_draw_ctx = [ # valid LVGL8.2 [ptr, "draw_letter"], [ptr, "draw_line"], [ptr, "draw_polygon"], + [ptr, "draw_transform"], [ptr, "draw_bg"], [ptr, "wait_for_finish"], + [ptr, "buffer_copy"], + [ptr, "layer_init"], + [ptr, "layer_adjust"], + [ptr, "layer_blend"], + [ptr, "layer_destroy"], + [uint32_t, "layer_instance_size"], [ptr, "user_data"], ] lv_draw_ctx = ct.structure(lv_draw_ctx, "lv_draw_ctx") @@ -602,7 +622,7 @@ lv_draw_ctx = ct.structure(lv_draw_ctx, "lv_draw_ctx") ####################################################################### # Special structure used to calibrate resistive touchscreens ####################################################################### -lv_ts_calibration = [ # valid LVGL8 +lv_ts_calibration = [ # valid LVGL8.3 [lv_coord_t, "raw_x"], [lv_coord_t, "raw_y"], [lv_coord_t, "x"], diff --git a/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py b/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py index 8e77aec11..de147382b 100644 --- a/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py +++ b/lib/libesp32_lvgl/lv_binding_berry/tools/convert.py @@ -73,7 +73,7 @@ return_types = { "lv_textarea_style_t": "i", "lv_slider_type_t": "i", "lv_spinner_type_t": "i", - "lv_spinner_dir_t": "i", + "lv_spinner_dir_t": "i", "lv_blend_mode_t": "i", "lv_grad_dir_t": "i", "lv_border_side_t": "i", @@ -107,6 +107,7 @@ return_types = { "lv_style_prop_t": "i", "lv_dither_mode_t": "i", "lv_chart_update_mode_t": "i", + "lv_style_res_t": "i", # layouts "lv_flex_align_t": "i", "lv_flex_flow_t": "i", @@ -157,11 +158,13 @@ return_types = { "lv_style_transition_dsc_t *": "lv_style_transition_dsc", "lv_draw_ctx_t *": "lv_draw_ctx", "_lv_draw_ctx_t *": "lv_draw_ctx", + "_lv_draw_layer_ctx_t *": "lv_draw_layer_ctx", "lv_grad_dsc_t *": "lv_grad_dsc", "lv_sqrt_res_t *": "lv_sqrt_res", # "lv_color_hsv_t *": "lv_color_hsv", "lv_color_filter_dsc_t *": "lv_color_filter_dsc", "lv_timer_t *": "lv_timer", + "_lv_timer_t *": "lv_timer", "lv_coord_t *": "c", # treat as a simple pointer, decoding needs to be done at Berry level "char **": "c", # treat as a simple pointer, decoding needs to be done at Berry level "constchar **": "c", # treat as a simple pointer, decoding needs to be done at Berry level diff --git a/lib/libesp32_lvgl/lv_binding_berry/tools/gen.sh b/lib/libesp32_lvgl/lv_binding_berry/tools/gen.sh index 5bd081698..40dcad0c2 100755 --- a/lib/libesp32_lvgl/lv_binding_berry/tools/gen.sh +++ b/lib/libesp32_lvgl/lv_binding_berry/tools/gen.sh @@ -3,4 +3,4 @@ python3 preprocessor.py python3 convert.py cd ../src/embedded -python3 berry_ctypes.py \ No newline at end of file +python3 lvgl_ctypes.py \ No newline at end of file diff --git a/lib/libesp32_lvgl/lvgl/Kconfig b/lib/libesp32_lvgl/lvgl/Kconfig deleted file mode 100644 index 8fe14ec1d..000000000 --- a/lib/libesp32_lvgl/lvgl/Kconfig +++ /dev/null @@ -1,1031 +0,0 @@ -# Kconfig file for LVGL v8.0 - -menu "LVGL configuration" - - # Define CONFIG_LV_CONF_SKIP so we can use LVGL - # without lv_conf.h file, the lv_conf_internal.h and - # lv_conf_kconfig.h files are used instead. - config LV_CONF_SKIP - bool - default y - - config LV_CONF_MINIMAL - bool "LVGL minimal configuration." - - menu "Color settings" - choice LV_COLOR_DEPTH - prompt "Color depth." - default LV_COLOR_DEPTH_16 - help - Color depth to be used. - - config LV_COLOR_DEPTH_32 - bool "32: ARGB8888" - config LV_COLOR_DEPTH_16 - bool "16: RGB565" - config LV_COLOR_DEPTH_8 - bool "8: RGB232" - config LV_COLOR_DEPTH_1 - bool "1: 1 byte per pixel" - endchoice - - config LV_COLOR_DEPTH - int - default 1 if LV_COLOR_DEPTH_1 - default 8 if LV_COLOR_DEPTH_8 - default 16 if LV_COLOR_DEPTH_16 - default 32 if LV_COLOR_DEPTH_32 - - config LV_COLOR_16_SWAP - bool "Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)." - depends on LV_COLOR_DEPTH_16 - - config LV_COLOR_SCREEN_TRANSP - bool "Enable more complex drawing routines to manage screens transparency." - depends on LV_COLOR_DEPTH_32 - help - Can be used if the UI is above another layer, e.g. an OSD menu or video player. - Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to - non LV_OPA_COVER value - - config LV_COLOR_MIX_ROUND_OFS - int "Adjust color mix functions rounding" - default 128 if !LV_COLOR_DEPTH_32 - default 0 if LV_COLOR_DEPTH_32 - range 0 254 - help - 0: no adjustment, get the integer part of the result (round down) - 64: round up from x.75 - 128: round up from half - 192: round up from x.25 - 254: round up - - config LV_COLOR_CHROMA_KEY_HEX - hex "Images pixels with this color will not be drawn (if they are chroma keyed)." - range 0x000000 0xFFFFFF - default 0x00FF00 - help - See misc/lv_color.h for some color values examples. - endmenu - - menu "Memory settings" - config LV_MEM_CUSTOM - bool "If true use custom malloc/free, otherwise use the built-in `lv_mem_alloc()` and `lv_mem_free()`" - - config LV_MEM_SIZE_KILOBYTES - int "Size of the memory used by `lv_mem_alloc` in kilobytes (>= 2kB)" - range 2 128 - default 32 - depends on !LV_MEM_CUSTOM - - config LV_MEM_ADDR - hex "Address for the memory pool instead of allocating it as a normal array" - default 0x0 - depends on !LV_MEM_CUSTOM - - config LV_MEM_CUSTOM_INCLUDE - string "Header to include for the custom memory function" - default "stdlib.h" - depends on LV_MEM_CUSTOM - - config LV_MEM_BUF_MAX_NUM - int "Number of the memory buffer" - default 16 - help - Number of the intermediate memory buffer used during rendering and other - internal processing mechanisms. You will see an error log message if - there wasn't enough buffers. - - config LV_MEMCPY_MEMSET_STD - bool "Use the standard memcpy and memset instead of LVGL's own functions" - endmenu - - menu "HAL Settings" - config LV_DISP_DEF_REFR_PERIOD - int "Default display refresh period (ms)." - default 30 - help - Can be changed in the display driver (`lv_disp_drv_t`). - - config LV_INDEV_DEF_READ_PERIOD - int "Input device read period [ms]." - default 30 - - config LV_TICK_CUSTOM - bool "Use a custom tick source" - - config LV_TICK_CUSTOM_INCLUDE - string "Header for the system time function" - default "Arduino.h" - depends on LV_TICK_CUSTOM - - config LV_DPI_DEF - int "Default Dots Per Inch (in px)." - default 130 - help - Used to initialize default sizes such as widgets sized, style paddings. - (Not so important, you can adjust it to modify default sizes and spaces) - endmenu - - menu "Feature configuration" - - menu "Drawing" - config LV_DRAW_COMPLEX - bool "Enable complex draw engine" - default y - help - Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, - image transformations or any masks. - - config LV_SHADOW_CACHE_SIZE - int "Allow buffering some shadow calculation" - depends on LV_DRAW_COMPLEX - default 0 - help - LV_SHADOW_CACHE_SIZE is the max shadow size to buffer, where - shadow size is `shadow_width + radius`. - Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost. - - config LV_CIRCLE_CACHE_SIZE - int "Set number of maximally cached circle data" - depends on LV_DRAW_COMPLEX - default 4 - help - The circumference of 1/4 circle are saved for anti-aliasing - radius * 4 bytes are used per circle (the most often used - radiuses are saved). - Set to 0 to disable caching. - - config LV_IMG_CACHE_DEF_SIZE - int "Default image cache size. 0 to disable caching." - default 0 - help - If only the built-in image formats are used there is no real advantage of caching. - (I.e. no new image decoder is added). - - With complex image decoders (e.g. PNG or JPG) caching can - save the continuous open/decode of images. - However the opened images might consume additional RAM. - - config LV_GRADIENT_MAX_STOPS - int "Number of stops allowed per gradient." - default 2 - help - Increase this to allow more stops. - This adds (sizeof(lv_color_t) + 1) bytes per additional stop - - config LV_GRAD_CACHE_DEF_SIZE - int "Default gradient buffer size." - default 0 - help - When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. - LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. - If the cache is too small the map will be allocated only while it's required for the drawing. - 0 mean no caching. - - config LV_DITHER_GRADIENT - bool "Allow dithering the gradients" - help - Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) - LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface - The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion - - config LV_DITHER_ERROR_DIFFUSION - bool "Add support for error diffusion dithering" - depends on LV_DITHER_GRADIENT - help - Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. - The increase in memory consumption is (24 bits * object's width) - - config LV_DISP_ROT_MAX_BUF - int "Maximum buffer size to allocate for rotation" - default 10240 - help - Only used if software rotation is enabled in the display driver. - endmenu - - menu "GPU" - config LV_USE_EXTERNAL_RENDERER - bool - - config LV_USE_GPU_STM32_DMA2D - bool "Enable STM32 DMA2D (aka Chrom Art) GPU." - config LV_GPU_DMA2D_CMSIS_INCLUDE - string "include path of CMSIS header of target processor" - depends on LV_USE_GPU_STM32_DMA2D - default "" - help - Must be defined to include path of CMSIS header of target processor - e.g. "stm32f769xx.h" or "stm32f429xx.h" - - config LV_USE_GPU_NXP_PXP - bool "Use NXP's PXP GPU iMX RTxxx platforms." - config LV_USE_GPU_NXP_PXP_AUTO_INIT - bool "Call lv_gpu_nxp_pxp_init() automatically or manually." - depends on LV_USE_GPU_NXP_PXP - help - 1: Add default bare metal and FreeRTOS interrupt handling - routines for PXP (lv_gpu_nxp_pxp_osa.c) and call - lv_gpu_nxp_pxp_init() automatically during lv_init(). - Note that symbol SDK_OS_FREE_RTOS has to be defined in order - to use FreeRTOS OSA, otherwise bare-metal implementation is - selected. - 0: lv_gpu_nxp_pxp_init() has to be called manually before - lv_init(). - - config LV_USE_GPU_NXP_VG_LITE - bool "Use NXP's VG-Lite GPU iMX RTxxx platforms." - - config LV_USE_GPU_SDL - bool "Use SDL renderer API" - select LV_USE_EXTERNAL_RENDERER - default n - config LV_GPU_SDL_INCLUDE_PATH - string "include path of SDL header" - depends on LV_USE_GPU_SDL - default "SDL2/SDL.h" - endmenu - - menu "Logging" - config LV_USE_LOG - bool "Enable the log module" - - choice - bool "Default log verbosity" if LV_USE_LOG - default LV_LOG_LEVEL_WARN - help - Specify how important log should be added. - - config LV_LOG_LEVEL_TRACE - bool "A lot of logs to give detailed information" - config LV_LOG_LEVEL_INFO - bool "Log important events" - config LV_LOG_LEVEL_WARN - bool "Log if something unwanted happened but didn't cause a problem" - config LV_LOG_LEVEL_ERROR - bool "Only critical issues, when the system may fail" - config LV_LOG_LEVEL_USER - bool "Only logs added by the user" - config LV_LOG_LEVEL_NONE - bool "Do not log anything" - endchoice - - config LV_LOG_LEVEL - int - default 0 if LV_LOG_LEVEL_TRACE - default 1 if LV_LOG_LEVEL_INFO - default 2 if LV_LOG_LEVEL_WARN - default 3 if LV_LOG_LEVEL_ERROR - default 4 if LV_LOG_LEVEL_USER - default 5 if LV_LOG_LEVEL_NONE - - config LV_LOG_PRINTF - bool "Print the log with 'printf'" if LV_USE_LOG - help - Use printf for log output. - If not set the user needs to register a callback with `lv_log_register_print_cb`. - - config LV_LOG_TRACE_MEM - bool "Enable/Disable LV_LOG_TRACE in mem module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_TIMER - bool "Enable/Disable LV_LOG_TRACE in timer module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_INDEV - bool "Enable/Disable LV_LOG_TRACE in indev module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_DISP_REFR - bool "Enable/Disable LV_LOG_TRACE in disp refr module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_EVENT - bool "Enable/Disable LV_LOG_TRACE in event module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_OBJ_CREATE - bool "Enable/Disable LV_LOG_TRACE in obj create module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_LAYOUT - bool "Enable/Disable LV_LOG_TRACE in layout module" - default y - depends on LV_USE_LOG - - config LV_LOG_TRACE_ANIM - bool "Enable/Disable LV_LOG_TRACE in anim module" - default y - depends on LV_USE_LOG - endmenu - - menu "Asserts" - config LV_USE_ASSERT_NULL - bool "Check if the parameter is NULL. (Very fast, recommended)" - default y if !LV_CONF_MINIMAL - - config LV_USE_ASSERT_MALLOC - bool "Checks if the memory is successfully allocated or no. (Very fast, recommended)" - default y if !LV_CONF_MINIMAL - - config LV_USE_ASSERT_STYLE - bool "Check if the styles are properly initialized. (Very fast, recommended)" - - config LV_USE_ASSERT_MEM_INTEGRITY - bool "Check the integrity of `lv_mem` after critical operations. (Slow)" - - config LV_USE_ASSERT_OBJ - bool "Check NULL, the object's type and existence (e.g. not deleted). (Slow)." - - config LV_ASSERT_HANDLER_INCLUDE - string "Header to include for the custom assert function" - default "assert.h" - endmenu - - menu "Others" - config LV_USE_PERF_MONITOR - bool "Show CPU usage and FPS count." - - choice - prompt "Performance monitor position." - depends on LV_USE_PERF_MONITOR - default LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT - - config LV_PERF_MONITOR_ALIGN_TOP_LEFT - bool "Top left" - config LV_PERF_MONITOR_ALIGN_TOP_MID - bool "Top middle" - config LV_PERF_MONITOR_ALIGN_TOP_RIGHT - bool "Top right" - config LV_PERF_MONITOR_ALIGN_BOTTOM_LEFT - bool "Bottom left" - config LV_PERF_MONITOR_ALIGN_BOTTOM_MID - bool "Bottom middle" - config LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT - bool "Bottom right" - config LV_PERF_MONITOR_ALIGN_LEFT_MID - bool "Left middle" - config LV_PERF_MONITOR_ALIGN_RIGHT_MID - bool "Right middle" - config LV_PERF_MONITOR_ALIGN_BOTTOM_CENTER - bool "Center" - endchoice - - config LV_USE_MEM_MONITOR - bool "Show the used memory and the memory fragmentation." - depends on !LV_MEM_CUSTOM - - choice - prompt "Memory monitor position." - depends on LV_USE_MEM_MONITOR - default LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT - - config LV_MEM_MONITOR_ALIGN_TOP_LEFT - bool "Top left" - config LV_MEM_MONITOR_ALIGN_TOP_MID - bool "Top middle" - config LV_MEM_MONITOR_ALIGN_TOP_RIGHT - bool "Top right" - config LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT - bool "Bottom left" - config LV_MEM_MONITOR_ALIGN_BOTTOM_MID - bool "Bottom middle" - config LV_MEM_MONITOR_ALIGN_BOTTOM_RIGHT - bool "Bottom right" - config LV_MEM_MONITOR_ALIGN_LEFT_MID - bool "Left middle" - config LV_MEM_MONITOR_ALIGN_RIGHT_MID - bool "Right middle" - config LV_MEM_MONITOR_ALIGN_BOTTOM_CENTER - bool "Center" - endchoice - - config LV_USE_REFR_DEBUG - bool "Draw random colored rectangles over the redrawn areas." - - config LV_SPRINTF_CUSTOM - bool "Change the built-in (v)snprintf functions" - - config LV_SPRINTF_INCLUDE - string "Header to include for the custom sprintf function" - depends on LV_SPRINTF_CUSTOM - default "stdio.h" - - config LV_SPRINTF_USE_FLOAT - bool "Enable float in built-in (v)snprintf functions" - depends on !LV_SPRINTF_CUSTOM - - config LV_USE_USER_DATA - bool "Add a 'user_data' to drivers and objects." - default y - - config LV_ENABLE_GC - bool "Enable garbage collector" - - config LV_GC_INCLUDE - string "Header to include for the garbage collector related things" - depends on LV_ENABLE_GC - default "gc.h" - endmenu - - menu "Compiler settings" - config LV_BIG_ENDIAN_SYSTEM - bool "For big endian systems set to 1" - - config LV_ATTRIBUTE_MEM_ALIGN_SIZE - int "Required alignment size for buffers" - default 1 - - config LV_ATTRIBUTE_FAST_MEM_USE_IRAM - bool "Set IRAM as LV_ATTRIBUTE_FAST_MEM" - help - Set this option to configure IRAM as LV_ATTRIBUTE_FAST_MEM - - config LV_USE_LARGE_COORD - bool "Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t" - endmenu - endmenu - - menu "Font usage" - menu "Enable built-in fonts" - config LV_FONT_MONTSERRAT_8 - bool "Enable Montserrat 8" - config LV_FONT_MONTSERRAT_10 - bool "Enable Montserrat 10" - config LV_FONT_MONTSERRAT_12 - bool "Enable Montserrat 12" - config LV_FONT_MONTSERRAT_14 - bool "Enable Montserrat 14" - default y if !LV_CONF_MINIMAL - config LV_FONT_MONTSERRAT_16 - bool "Enable Montserrat 16" - config LV_FONT_MONTSERRAT_18 - bool "Enable Montserrat 18" - config LV_FONT_MONTSERRAT_20 - bool "Enable Montserrat 20" - config LV_FONT_MONTSERRAT_22 - bool "Enable Montserrat 22" - config LV_FONT_MONTSERRAT_24 - bool "Enable Montserrat 24" - config LV_FONT_MONTSERRAT_26 - bool "Enable Montserrat 26" - config LV_FONT_MONTSERRAT_28 - bool "Enable Montserrat 28" - config LV_FONT_MONTSERRAT_30 - bool "Enable Montserrat 30" - config LV_FONT_MONTSERRAT_32 - bool "Enable Montserrat 32" - config LV_FONT_MONTSERRAT_34 - bool "Enable Montserrat 34" - config LV_FONT_MONTSERRAT_36 - bool "Enable Montserrat 36" - config LV_FONT_MONTSERRAT_38 - bool "Enable Montserrat 38" - config LV_FONT_MONTSERRAT_40 - bool "Enable Montserrat 40" - config LV_FONT_MONTSERRAT_42 - bool "Enable Montserrat 42" - config LV_FONT_MONTSERRAT_44 - bool "Enable Montserrat 44" - config LV_FONT_MONTSERRAT_46 - bool "Enable Montserrat 46" - config LV_FONT_MONTSERRAT_48 - bool "Enable Montserrat 48" - - config LV_FONT_MONTSERRAT_12_SUBPX - bool "Enable Montserrat 12 sub-pixel" - config LV_FONT_MONTSERRAT_28_COMPRESSED - bool "Enable Montserrat 28 compressed" - config LV_FONT_DEJAVU_16_PERSIAN_HEBREW - bool "Enable Dejavu 16 Persian, Hebrew, Arabic letters" - config LV_FONT_SIMSUN_16_CJK - bool "Enable Simsun 16 CJK" - - config LV_FONT_UNSCII_8 - bool "Enable UNSCII 8 (Perfect monospace font)" - default y if LV_CONF_MINIMAL - config LV_FONT_UNSCII_16 - bool "Enable UNSCII 16 (Perfect monospace font)" - - config LV_FONT_CUSTOM - bool "Enable the custom font" - config LV_FONT_CUSTOM_DECLARE - string "Header to include for the custom font" - depends on LV_FONT_CUSTOM - endmenu - - choice LV_FONT_DEFAULT - prompt "Select theme default title font" - default LV_FONT_DEFAULT_MONTSERRAT_14 if !LV_CONF_MINIMAL - default LV_FONT_DEFAULT_UNSCII_8 if LV_CONF_MINIMAL - help - Select theme default title font - - config LV_FONT_DEFAULT_MONTSERRAT_8 - bool "Montserrat 8" - select LV_FONT_MONTSERRAT_8 - config LV_FONT_DEFAULT_MONTSERRAT_12 - bool "Montserrat 12" - select LV_FONT_MONTSERRAT_12 - config LV_FONT_DEFAULT_MONTSERRAT_14 - bool "Montserrat 14" - select LV_FONT_MONTSERRAT_14 - config LV_FONT_DEFAULT_MONTSERRAT_16 - bool "Montserrat 16" - select LV_FONT_MONTSERRAT_16 - config LV_FONT_DEFAULT_MONTSERRAT_18 - bool "Montserrat 18" - select LV_FONT_MONTSERRAT_18 - config LV_FONT_DEFAULT_MONTSERRAT_20 - bool "Montserrat 20" - select LV_FONT_MONTSERRAT_20 - config LV_FONT_DEFAULT_MONTSERRAT_22 - bool "Montserrat 22" - select LV_FONT_MONTSERRAT_22 - config LV_FONT_DEFAULT_MONTSERRAT_24 - bool "Montserrat 24" - select LV_FONT_MONTSERRAT_24 - config LV_FONT_DEFAULT_MONTSERRAT_26 - bool "Montserrat 26" - select LV_FONT_MONTSERRAT_26 - config LV_FONT_DEFAULT_MONTSERRAT_28 - bool "Montserrat 28" - select LV_FONT_MONTSERRAT_28 - config LV_FONT_DEFAULT_MONTSERRAT_30 - bool "Montserrat 30" - select LV_FONT_MONTSERRAT_30 - config LV_FONT_DEFAULT_MONTSERRAT_32 - bool "Montserrat 32" - select LV_FONT_MONTSERRAT_32 - config LV_FONT_DEFAULT_MONTSERRAT_34 - bool "Montserrat 34" - select LV_FONT_MONTSERRAT_34 - config LV_FONT_DEFAULT_MONTSERRAT_36 - bool "Montserrat 36" - select LV_FONT_MONTSERRAT_36 - config LV_FONT_DEFAULT_MONTSERRAT_38 - bool "Montserrat 38" - select LV_FONT_MONTSERRAT_38 - config LV_FONT_DEFAULT_MONTSERRAT_40 - bool "Montserrat 40" - select LV_FONT_MONTSERRAT_40 - config LV_FONT_DEFAULT_MONTSERRAT_42 - bool "Montserrat 42" - select LV_FONT_MONTSERRAT_42 - config LV_FONT_DEFAULT_MONTSERRAT_44 - bool "Montserrat 44" - select LV_FONT_MONTSERRAT_44 - config LV_FONT_DEFAULT_MONTSERRAT_46 - bool "Montserrat 46" - select LV_FONT_MONTSERRAT_46 - config LV_FONT_DEFAULT_MONTSERRAT_48 - bool "Montserrat 48" - select LV_FONT_MONTSERRAT_48 - config LV_FONT_DEFAULT_MONTSERRAT_12_SUBPX - bool "Montserrat 12 sub-pixel" - select LV_FONT_MONTSERRAT_12_SUBPX - config LV_FONT_DEFAULT_MONTSERRAT_28_COMPRESSED - bool "Montserrat 28 compressed" - select LV_FONT_MONTSERRAT_28_COMPRESSED - config LV_FONT_DEFAULT_DEJAVU_16_PERSIAN_HEBREW - bool "Dejavu 16 Persian, Hebrew, Arabic letters" - select LV_FONT_DEJAVU_16_PERSIAN_HEBREW - config LV_FONT_DEFAULT_SIMSUN_16_CJK - bool "Simsun 16 CJK" - select LV_FONT_SIMSUN_16_CJK - config LV_FONT_DEFAULT_UNSCII_8 - bool "UNSCII 8 (Perfect monospace font)" - select LV_FONT_UNSCII_8 - config LV_FONT_DEFAULT_UNSCII_16 - bool "UNSCII 16 (Perfect monospace font)" - select LV_FONT_UNSCII_16 - endchoice - - config LV_FONT_FMT_TXT_LARGE - bool "Enable it if you have fonts with a lot of characters." - help - The limit depends on the font size, font face and bpp - but with > 10,000 characters if you see issues probably you - need to enable it. - - config LV_USE_FONT_COMPRESSED - bool "Sets support for compressed fonts." - - config LV_USE_FONT_SUBPX - bool "Enable subpixel rendering." - - config LV_FONT_SUBPX_BGR - bool "Use BGR instead RGB for sub-pixel rendering." - depends on LV_USE_FONT_SUBPX - help - Set the pixel order of the display. - Important only if "subpx fonts" are used. - With "normal" font it doesn't matter. - endmenu - - menu "Text Settings" - choice LV_TXT_ENC - prompt "Select a character encoding for strings" - help - Select a character encoding for strings. Your IDE or editor should have the same character encoding. - default LV_TXT_ENC_UTF8 if !LV_CONF_MINIMAL - default LV_TXT_ENC_ASCII if LV_CONF_MINIMAL - - config LV_TXT_ENC_UTF8 - bool "UTF8" - config LV_TXT_ENC_ASCII - bool "ASCII" - endchoice - - config LV_TXT_BREAK_CHARS - string "Can break (wrap) texts on these chars" - default " ,.;:-_" - - config LV_TXT_LINE_BREAK_LONG_LEN - int "Line break long length" - default 0 - help - If a word is at least this long, will break wherever 'prettiest'. - To disable, set to a value <= 0. - - config LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN - int "Min num chars before break" - default 3 - depends on LV_TXT_LINE_BREAK_LONG_LEN > 0 - help - Minimum number of characters in a long word to put on a line before a break. - - config LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN - int "Min num chars after break" - default 3 - depends on LV_TXT_LINE_BREAK_LONG_LEN > 0 - help - Minimum number of characters in a long word to put on a line after a break. - - config LV_TXT_COLOR_CMD - string "The control character to use for signalling text recoloring" - default "#" - - config LV_USE_BIDI - bool "Support bidirectional texts" - help - Allows mixing Left-to-Right and Right-to-Left texts. - The direction will be processed according to the Unicode Bidirectional Algorithm: - https://www.w3.org/International/articles/inline-bidi-markup/uba-basics - - choice - prompt "Set the default BIDI direction" - default LV_BIDI_DIR_AUTO - depends on LV_USE_BIDI - - config LV_BIDI_DIR_LTR - bool "Left-to-Right" - config LV_BIDI_DIR_RTL - bool "Right-to-Left" - config LV_BIDI_DIR_AUTO - bool "Detect texts base direction" - endchoice - - config LV_USE_ARABIC_PERSIAN_CHARS - bool "Enable Arabic/Persian processing" - help - In these languages characters should be replaced with - an other form based on their position in the text. - endmenu - - menu "Widget usage" - config LV_USE_ARC - bool "Arc." - default y if !LV_CONF_MINIMAL - config LV_USE_BAR - bool "Bar." - default y if !LV_CONF_MINIMAL - config LV_USE_BTN - bool "Button." - default y if !LV_CONF_MINIMAL - config LV_USE_BTNMATRIX - bool "Button matrix." - default y if !LV_CONF_MINIMAL - config LV_USE_CANVAS - bool "Canvas. Dependencies: lv_img." - default y if !LV_CONF_MINIMAL - config LV_USE_CHECKBOX - bool "Check Box" - default y if !LV_CONF_MINIMAL - config LV_USE_DROPDOWN - bool "Drop down list. Requires: lv_label." - select LV_USE_LABEL - default y if !LV_CONF_MINIMAL - config LV_USE_IMG - bool "Image. Requires: lv_label." - select LV_USE_LABEL - default y if !LV_CONF_MINIMAL - config LV_USE_LABEL - bool "Label." - default y if !LV_CONF_MINIMAL - config LV_LABEL_TEXT_SELECTION - bool "Enable selecting text of the label." - depends on LV_USE_LABEL - default y - config LV_LABEL_LONG_TXT_HINT - bool "Store extra some info in labels (12 bytes) to speed up drawing of very long texts." - depends on LV_USE_LABEL - default y - config LV_USE_LINE - bool "Line." - default y if !LV_CONF_MINIMAL - config LV_USE_ROLLER - bool "Roller. Requires: lv_label." - select LV_USE_LABEL - default y if !LV_CONF_MINIMAL - config LV_ROLLER_INF_PAGES - int "Number of extra 'pages' when the controller is infinite." - default 7 - depends on LV_USE_ROLLER - config LV_USE_SLIDER - bool "Slider. Requires: lv_bar." - select LV_USE_BAR - default y if !LV_CONF_MINIMAL - config LV_USE_SWITCH - bool "Switch." - default y if !LV_CONF_MINIMAL - config LV_USE_TEXTAREA - bool "Text area. Requires: lv_label." - select LV_USE_LABEL - default y if !LV_CONF_MINIMAL - config LV_TEXTAREA_DEF_PWD_SHOW_TIME - int "Text area def. pwd show time [ms]." - default 1500 - depends on LV_USE_TEXTAREA - config LV_USE_TABLE - bool "Table." - default y if !LV_CONF_MINIMAL - endmenu - - menu "Extra Widgets" - config LV_USE_ANIMIMG - bool "Anim image." - default y if !LV_CONF_MINIMAL - config LV_USE_CALENDAR - bool "Calendar." - default y if !LV_CONF_MINIMAL - config LV_CALENDAR_WEEK_STARTS_MONDAY - bool "Calendar week starts monday." - depends on LV_USE_CALENDAR - config LV_USE_CALENDAR_HEADER_ARROW - bool "Use calendar header arrow" - depends on LV_USE_CALENDAR - default y - config LV_USE_CALENDAR_HEADER_DROPDOWN - bool "Use calendar header dropdown" - depends on LV_USE_CALENDAR - default y - config LV_USE_CHART - bool "Chart." - default y if !LV_CONF_MINIMAL - config LV_USE_COLORWHEEL - bool "Colorwheel." - default y if !LV_CONF_MINIMAL - config LV_USE_IMGBTN - bool "Imgbtn." - default y if !LV_CONF_MINIMAL - config LV_USE_KEYBOARD - bool "Keyboard." - default y if !LV_CONF_MINIMAL - config LV_USE_LED - bool "LED." - default y if !LV_CONF_MINIMAL - config LV_USE_LIST - bool "List." - default y if !LV_CONF_MINIMAL - config LV_USE_MENU - bool "Menu." - default y if !LV_CONF_MINIMAL - config LV_USE_METER - bool "Meter." - default y if !LV_CONF_MINIMAL - config LV_USE_MSGBOX - bool "Msgbox." - default y if !LV_CONF_MINIMAL - config LV_USE_SPINBOX - bool "Spinbox." - default y if !LV_CONF_MINIMAL - config LV_USE_SPINNER - bool "Spinner." - default y if !LV_CONF_MINIMAL - config LV_USE_TABVIEW - bool "Tabview." - default y if !LV_CONF_MINIMAL - config LV_USE_TILEVIEW - bool "Tileview" - default y if !LV_CONF_MINIMAL - config LV_USE_WIN - bool "Win" - default y if !LV_CONF_MINIMAL - config LV_USE_SPAN - bool "span" - default y if !LV_CONF_MINIMAL - config LV_SPAN_SNIPPET_STACK_SIZE - int "Maximum number of span descriptor" - default 64 - depends on LV_USE_SPAN - endmenu - - menu "Themes" - config LV_USE_THEME_DEFAULT - bool "A simple, impressive and very complete theme" - default y if !LV_CONF_MINIMAL - config LV_THEME_DEFAULT_DARK - bool "Yes to set dark mode, No to set light mode" - depends on LV_USE_THEME_DEFAULT - config LV_THEME_DEFAULT_GROW - bool "Enable grow on press" - default y - depends on LV_USE_THEME_DEFAULT - config LV_THEME_DEFAULT_TRANSITION_TIME - int "Default transition time in [ms]" - default 80 - depends on LV_USE_THEME_DEFAULT - config LV_USE_THEME_BASIC - bool "A very simple theme that is a good starting point for a custom theme" - default y if !LV_CONF_MINIMAL - endmenu - - menu "Layouts" - config LV_USE_FLEX - bool "A layout similar to Flexbox in CSS." - default y if !LV_CONF_MINIMAL - config LV_USE_GRID - bool "A layout similar to Grid in CSS." - default y if !LV_CONF_MINIMAL - endmenu - - menu "3rd Party Libraries" - config LV_USE_FS_STDIO - bool "File system on top of stdio API" - config LV_FS_STDIO_LETTER - string "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65 )" - depends on LV_USE_FS_STDIO != 0 - config LV_FS_STDIO_PATH - string "Set the working directory" - depends on LV_USE_FS_STDIO != 0 - config LV_FS_STDIO_CACHE_SIZE - string ">0 to cache this number of bytes in lv_fs_read()" - depends on LV_USE_FS_STDIO != 0 - - config LV_USE_FS_POSIX - bool "File system on top of posix API" - config LV_FS_POSIX_LETTER - int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" - default 0 - depends on LV_USE_FS_POSIX != 0 - config LV_FS_POSIX_PATH - string "Set the working directory" - depends on LV_USE_FS_POSIX != 0 - config LV_FS_POSIX_CACHE_SIZE - int ">0 to cache this number of bytes in lv_fs_read()" - default 0 - depends on LV_USE_FS_POSIX != 0 - - config LV_USE_FS_WIN32 - bool "File system on top of Win32 API" - config LV_FS_WIN32_LETTER - int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" - default 0 - depends on LV_USE_FS_WIN32 != 0 - config LV_FS_WIN32_PATH - string "Set the working directory" - depends on LV_USE_FS_WIN32 != 0 - config LV_FS_WIN32_CACHE_SIZE - int ">0 to cache this number of bytes in lv_fs_read()" - default 0 - depends on LV_USE_FS_WIN32 != 0 - - config LV_USE_FS_FATFS - bool "File system on top of FatFS" - config LV_FS_FATFS_LETTER - int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" - default 0 - depends on LV_USE_FS_FATFS != 0 - config LV_FS_FATFS_CACHE_SIZE - int ">0 to cache this number of bytes in lv_fs_read()" - default 0 - depends on LV_USE_FS_FATFS != 0 - - config LV_USE_PNG - bool "PNG decoder library" - - config LV_USE_BMP - bool "BMP decoder library" - - config LV_USE_SJPG - bool "JPG + split JPG decoder library" - - config LV_USE_GIF - bool "GIF decoder library" - - config LV_USE_QRCODE - bool "QR code library" - - config LV_USE_FREETYPE - bool "FreeType library" - if LV_USE_FREETYPE - menu "FreeType cache config" - config LV_FREETYPE_CACHE_SIZE - int "Memory used by FreeType to cache characters [bytes] (-1: no caching)" - default 16384 - if LV_FREETYPE_CACHE_SIZE >= 0 - config LV_FREETYPE_SBIT_CACHE - bool "enable sbit cache" - default n - config LV_FREETYPE_CACHE_FT_FACES - int "The maximum number of FT_Face(0: use defaults)" - default 0 - config LV_FREETYPE_CACHE_FT_SIZES - int "The maximum number of FT_Size(0: use defaults)" - default 0 - endif - endmenu - endif - - config LV_USE_RLOTTIE - bool "Lottie library" - - config LV_USE_FFMPEG - bool "FFmpeg library" - config LV_FFMPEG_AV_DUMP_FORMAT - bool "Dump av format" - depends on LV_USE_FFMPEG - default n - endmenu - - menu "Others" - config LV_USE_SNAPSHOT - bool "Enable API to take snapshot" - default y if !LV_CONF_MINIMAL - - config LV_USE_MONKEY - bool "Enable Monkey test" - default n - endmenu - - menu "Examples" - config LV_BUILD_EXAMPLES - bool "Enable the examples to be built" - default y if !LV_CONF_MINIMAL - endmenu - - menu "Demos" - config LV_USE_DEMO_WIDGETS - bool "Show some widget" - default n - config LV_DEMO_WIDGETS_SLIDESHOW - bool "Enable slide show" - depends on LV_USE_DEMO_WIDGETS - default n - - config LV_USE_DEMO_KEYPAD_AND_ENCODER - bool "Demonstrate the usage of encoder and keyboard" - default n - - config LV_USE_DEMO_BENCHMARK - bool "Benchmark your system" - default n - - config LV_USE_DEMO_STRESS - bool "Stress test for LVGL" - default n - - config LV_USE_DEMO_MUSIC - bool "Music player demo" - default n - config LV_DEMO_MUSIC_SQUARE - bool "Enable Square" - depends on LV_USE_DEMO_MUSIC - default n - config LV_DEMO_MUSIC_LANDSCAPE - bool "Enable Landscape" - depends on LV_USE_DEMO_MUSIC - default n - config LV_DEMO_MUSIC_ROUND - bool "Enable Round" - depends on LV_USE_DEMO_MUSIC - default n - config LV_DEMO_MUSIC_LARGE - bool "Enable Large" - depends on LV_USE_DEMO_MUSIC - default n - config LV_DEMO_MUSIC_AUTO_PLAY - bool "Enable Auto play" - depends on LV_USE_DEMO_MUSIC - default n - endmenu - -endmenu diff --git a/lib/libesp32_lvgl/lvgl/README.md b/lib/libesp32_lvgl/lvgl/README.md index e8a452ee1..aea07a841 100644 --- a/lib/libesp32_lvgl/lvgl/README.md +++ b/lib/libesp32_lvgl/lvgl/README.md @@ -17,7 +17,7 @@ LVGL provides everything you need to create an embedded GUI with easy-to-use gra -**English** | [中文](./README_zh.md) | +**English** | [中文](./README_zh.md) | [Português do Brasil](./README_pt_BR.md) --- @@ -40,11 +40,11 @@ LVGL provides everything you need to create an embedded GUI with easy-to-use gra * Multi-language support with UTF-8 handling, CJK, Bidirectional and Arabic script support * Fully customizable graphical elements via [CSS-like styles](https://docs.lvgl.io/master/overview/style.html) * Powerful layouts inspired by CSS: [Flexbox](https://docs.lvgl.io/master/layouts/flex.html) and [Grid](https://docs.lvgl.io/master/layouts/grid.html) -* OS, External memory and GPU are supported but not required. (built in support for STM32 DMA2D, and NXP PXP and VGLite) +* OS, External memory and GPU are supported but not required. (built in support for STM32 DMA2D, SWM341 DMA2D, and NXP PXP and VGLite) * Smooth rendering even with a [single frame buffer](https://docs.lvgl.io/master/porting/display.html) * Written in C and compatible with C++ * Micropython Binding exposes [LVGL API in Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings) -* [Simulator](https://docs.lvgl.io/master/get-started/pc-simulator.html) to develop on PC without embedded hardware +* [Simulator](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html) to develop on PC without embedded hardware * 100+ simple [Examples](https://github.com/lvgl/lvgl/tree/master/examples) * [Documentation](http://docs.lvgl.io/) and API references online and in PDF @@ -93,26 +93,27 @@ Basically, every modern controller (which is able to drive a display) is suitabl *Note that the memory usage might vary depending on the architecture, compiler and build options.* ### Supported platforms -LVGL is completely platform independent and can be used with any MCU that fulfills the requirements. +LVGL is completely platform independent and can be used with any MCU that fulfills the requirements. Just to mention some platforms: - NXP: Kinetis, LPC, iMX, iMX RT - STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7 - Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ - [Linux frame buffer](https://blog.lvgl.io/2018-01-03/linux_fb) (/dev/fb) -- [Raspberry Pi](http://www.vk3erw.com/index.php/16-software/63-raspberry-pi-official-7-touchscreen-and-littlevgl) +- [Raspberry Pi](https://github.com/lvgl/lv_port_linux_frame_buffer) - [Espressif ESP32](https://github.com/lvgl/lv_port_esp32) - [Infineon Aurix](https://github.com/lvgl/lv_port_aurix) - Nordic NRF52 Bluetooth modules - Quectel modems +- [SYNWIT SWM341](http://www.synwit.cn/) LVGL is also available as: -- [Arduino library](https://docs.lvgl.io/master/get-started/arduino.html) -- [PlatformIO package](https://platformio.org/lib/show/12440/lvgl) +- [Arduino library](https://docs.lvgl.io/master/get-started/platforms/arduino.html) +- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl) - [Zephyr library](https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_LVGL.html) -- [ESP32 component](https://docs.lvgl.io/master/get-started/espressif.html) +- [ESP32 component](https://docs.lvgl.io/master/get-started/platforms/espressif.html) - [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY) -- [NuttX library](https://docs.lvgl.io/master/get-started/nuttx.html) -- [RT-Thread RTOS](https://docs.lvgl.io/master/get-started/rt-thread.html) +- [NuttX library](https://docs.lvgl.io/master/get-started/os/nuttx.html) +- [RT-Thread RTOS](https://docs.lvgl.io/master/get-started/os/rt-thread.html) ## Get started @@ -120,7 +121,7 @@ This list shows the recommended way of learning the library: 1. Check the [Online demos](https://lvgl.io/demos) to see LVGL in action (3 minutes) 2. Read the [Introduction](https://docs.lvgl.io/master/intro/index.html) page of the documentation (5 minutes) 3. Get familiar with the basics on the [Quick overview](https://docs.lvgl.io/master/get-started/quick-overview.html) page (15 minutes) -4. Set up a [Simulator](https://docs.lvgl.io/master/get-started/pc-simulator.html) (10 minutes) +4. Set up a [Simulator](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html) (10 minutes) 5. Try out some [Examples](https://github.com/lvgl/lvgl/tree/master/examples) 6. Port LVGL to a board. See the [Porting](https://docs.lvgl.io/master/porting/index.html) guide or check the ready to use [Projects](https://github.com/lvgl?q=lv_port_) 7. Read the [Overview](https://docs.lvgl.io/master/overview/index.html) page to get a better understanding of the library (2-3 hours) @@ -152,7 +153,7 @@ void btn_event_cb(lv_event_t * e) } ``` ### Micropython -Learn more about [Micropython](https://docs.lvgl.io/master/get-started/micropython.html). +Learn more about [Micropython](https://docs.lvgl.io/master/get-started/bindings/micropython.html). ```python def btn_event_cb(e): print("Clicked") diff --git a/lib/libesp32_lvgl/lvgl/README_pt_BR.md b/lib/libesp32_lvgl/lvgl/README_pt_BR.md new file mode 100644 index 000000000..f62f0a0f8 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/README_pt_BR.md @@ -0,0 +1,206 @@ +

LVGL - Biblioteca gráfica leve e versátil

+

+ +

+

+ O LVGL fornece tudo o que você precisa para criar uma GUI incorporada com elementos gráficos fáceis de usar, belos efeitos visuais e um baixo consumo de memória. +

+

+ Site · + Documentação · + Fórum · + Serviços · + Exemplos interativos +

+ +[English](./README.md) | [中文](./README_zh.md) | **Português do Brasil** + +--- + +### Tabela de conteúdo + +- [Visão Geral](#visão-geral) +- [Iniciando](#iniciando) +- [Exemplos](#exemplos) +- [Serviços](#serviços) +- [Contribuindo](#contribuindo) + +## Visão Geral + +### Recursos +* Poderosos [widgets](https://docs.lvgl.io/master/widgets/index.html): botões, gráficos, listas, controles deslizantes (sliders), imagens, etc. +* Mecanismo gráfico avançado: animações, anti-aliasing, opacidade, rolagem suave, modos de mesclagem (blending modes), etc. +* Suporte à [vários dispositivos de entrada](https://docs.lvgl.io/master/overview/indev.html): tela sensível ao toque, mouse, teclado, codificador, botões, etc. +* Suporte à [vários monitores](https://docs.lvgl.io/master/overview/display.html) +* Pode ser usado com qualquer microcontrolador e display, independente do hardware +* Escalável para operar com pouca memória (64 kB Flash, 16 kB RAM) +* Suporte multilíngue com manipulação UTF-8, suporte ao alfabeto bidirecional, árabe e CJK (Chinês, Japonês e Coreano) +* Elementos gráficos totalmente personalizáveis por meio de [CSS](https://docs.lvgl.io/master/overview/style.html) +* Layouts poderosos inspirados em CSS: [Flexbox](https://docs.lvgl.io/master/layouts/flex.html) e [Grid](https://docs.lvgl.io/master/layouts/grid.html) +* SO, memória externa e GPU são suportados, mas não obrigatórios. (suporte integrado para STM32 DMA2D, SWM341 DMA2D e NXP PXP e VGLite) +* Renderização suave mesmo com um [buffer de quadro único](https://docs.lvgl.io/master/porting/display.html) (single frame buffer) +* Escrito em C e compatível com C++ +* Uso do LittlevGL com Micropython simplificado com [LVGL API in Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings) +* [Simulador](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html) para desenvolver no PC sem hardware embutido +* Mais de 100 [exemplos simples](https://github.com/lvgl/lvgl/tree/master/examples) +* [Documentação](http://docs.lvgl.io/) e referências de API online e em PDF + +### Requerimentos +Basicamente, todo controlador moderno (que é capaz de acionar um display) é adequado para executar LVGL. Os requisitos mínimos são: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Nome + + Minímo + + Recomendado +
+ Arquitetura + Microcontrolador ou processador de 16, 32 ou 64 bits
+ Clock + > 16 MHz> 48 MHz
+ Flash/ROM + > 64 kB> 180 kB
+ RAM estática + > 16 kB> 48 kB
+ Draw buffer + > 1 × hor. res. pixels> tamanho da tela de 1/10
+ Compilador + Padrão C99 ou mais recente
+ +*Observe que o uso de memória pode variar dependendo da arquitetura, do compilador e das opções de compilação.* + +### Plataformas suportadas +O LVGL é completamente independente de plataforma e pode ser usado com qualquer MCU que atenda aos requisitos. +Apenas para citar algumas plataformas: + +- NXP: Kinetis, LPC, iMX, iMX RT +- STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7 +- Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ +- [Linux frame buffer](https://blog.lvgl.io/2018-01-03/linux_fb) (/dev/fb) +- [Raspberry Pi](http://www.vk3erw.com/index.php/16-software/63-raspberry-pi-official-7-touchscreen-and-littlevgl) +- [Espressif ESP32](https://github.com/lvgl/lv_port_esp32) +- [Infineon Aurix](https://github.com/lvgl/lv_port_aurix) +- Nordic NRF52 Bluetooth modules +- Quectel modems +- [SYNWIT SWM341](https://www.synwit.cn/) + +LVGL também está disponível para: +- [Arduino library](https://docs.lvgl.io/master/get-started/platforms/arduino.html) +- [PlatformIO package](https://registry.platformio.org/libraries/lvgl/lvgl) +- [Zephyr library](https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_LVGL.html) +- [ESP32 component](https://docs.lvgl.io/master/get-started/platforms/espressif.html) +- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY) +- [NuttX library](https://docs.lvgl.io/master/get-started/os/nuttx.html) +- [RT-Thread RTOS](https://docs.lvgl.io/master/get-started/os/rt-thread.html) + +## Iniciando +Esta lista mostra a maneira recomendada de aprender sobre a biblioteca: + +1. Confira as [demos on-line](https://lvgl.io/demos) para ver o LVGL em ação (3 minutos) +2. Leia a [introdução](https://docs.lvgl.io/master/intro/index.html) da documentação (5 minutos) +3. Familiarize-se com o básico da [Visão geral rápida](https://docs.lvgl.io/master/get-started/quick-overview.html) (15 minutos) +4. Configure um [simulador](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html) (10 minutos) +5. Experimente alguns [Exemplos](https://github.com/lvgl/lvgl/tree/master/examples) +6. Placa para porta LVGL. Veja o guia [porting](https://docs.lvgl.io/master/porting/index.html) ou verifique o pronto para usar [Projects](https://github.com/lvgl?q=lv_port_) +7. Leia a [visão geral](https://docs.lvgl.io/master/overview/index.html) para entender melhor a biblioteca (2-3 horas) +8. Verifique a documentação dos [widgets](https://docs.lvgl.io/master/widgets/index.html) para ver seus recursos e como utilizá-los +9. Se você tiver dúvidas, acesse o [fórum](http://forum.lvgl.io/) +10. Leia o guia de [contribuição](https://docs.lvgl.io/master/CONTRIBUTING.html) para ver como você pode ajudar a melhorar o LVGL (15 minutos) + +## Exemplos +Para mais exemplos, veja a pasta [examples](https://github.com/lvgl/lvgl/tree/master/examples). + +![Exemplo de botão LVGL com rótulo (label)](https://github.com/lvgl/lvgl/raw/master/docs/misc/btn_example.png) + +### C + +```c +lv_obj_t * button = lv_btn_create(lv_scr_act()); /* Adiciona um botão à tela atual */ +lv_obj_set_pos(button, 10, 10); /* Define uma posição ao botão na tela */ +lv_obj_set_size(button, 100, 50); /* Define o tamanho */ +lv_obj_add_event_cb(button, button_event_callback, LV_EVENT_CLICKED, NULL); /* Atribui um retorno de chamada (callback) */ + +lv_obj_t * label = lv_label_create(button); /* Adiciona um rótulo (label) */ +lv_label_set_text(label, "Clique aqui"); /* Define o texto do rótulo (label) */ +lv_obj_center(label); /* Alinha o texto ao centro */ +... + +void button_event_callback(lv_event_t * e) +{ + printf("Clicado\n"); +} +``` + +### Micropython +Saiba mais em [Micropython](https://docs.lvgl.io/master/get-started/bindings/micropython.html) + +```python +def button_event_callback(event): + print("Clicado") + +# Cria um botão e um rótulo (label) +button = lv.btn(lv.scr_act()) +button.set_pos(10, 10) +button.set_size(100, 50) +button.add_event_cb(button_event_callback, lv.EVENT.CLICKED, None) + +label = lv.label(button) +label.set_text("Cliquq aqui") +label.center() +``` + +## Serviços +O LVGL Kft foi estabelecido para fornecer uma base sólida para a biblioteca LVGL. Oferecemos vários tipos de serviços +para ajudá-lo no desenvolvimento da interface do usuário: + +- Design gráfico +- Implementação de IU +- Consultoria/Suporte + +Para mais informações, consulte [LVGL Serviços](https://lvgl.io/services). Sinta-se à vontade para entrar em contato +conosco se tiver alguma dúvida. + +## Contribuindo +O LVGL é um projeto aberto e sua contribuição é muito bem-vinda. Há muitas maneiras de contribuir, desde simplesmente +falando sobre seu projeto, escrevendo exemplos, melhorando a documentação, corrigindo bugs até hospedar seu próprio +projeto sob a organização LVGL. + +Para obter uma descrição detalhada das oportunidades de contribuição, visite a seção de [contribuição](https://docs.lvgl.io/master/CONTRIBUTING.html) da documentação. diff --git a/lib/libesp32_lvgl/lvgl/README_zh.md b/lib/libesp32_lvgl/lvgl/README_zh.md new file mode 100644 index 000000000..cac080d4e --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/README_zh.md @@ -0,0 +1,193 @@ +

LVGL - Light and Versatile Graphics Library

+

LVGL - 轻量级通用型图形库

+ + + +

+ +

+

+LVGL是一个高度可裁剪、低资源占用、界面美观且易用的嵌入式系统图形库 +

+ + +

+官网 · +文档 · +论坛 · +服务 · +例程 +

+ + +[English](./README.md) | **中文** | [Português do Brasil](./README_pt_BR.md) + + +--- + +#### 目录 +- [概况与总览](#概况与总览) +- [如何入门](#如何入门) +- [例程](#例程) +- [服务](#服务) +- [如何向社区贡献](#如何向社区贡献) + +## 概况与总览 +### 特性 +* 丰富且强大的模块化[图形组件](https://docs.lvgl.io/master/widgets/index.html):按钮 (buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片 (images) 等 +* 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果 +* 支持多种[输入设备](https://docs.lvgl.io/master/overview/indev.html):触摸屏、 键盘、编码器、按键等 +* 支持[多显示设备](https://docs.lvgl.io/master/overview/display.html) +* 不依赖特定的硬件平台,可以在任何显示屏上运行 +* 配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM) +* 基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等 +* 可以通过[类CSS](https://docs.lvgl.io/master/overview/style.html)的方式来设计、布局图形界面(例如:[Flexbox](https://docs.lvgl.io/master/layouts/flex.html)、[Grid](https://docs.lvgl.io/master/layouts/grid.html)) +* 支持操作系统、外置内存、以及硬件加速(LVGL已内建支持STM32 DMA2D、SWM341 DMA2D、NXP PXP和VGLite) +* 即便仅有[单缓冲区(frame buffer)](https://docs.lvgl.io/master/porting/display.html)的情况下,也可保证渲染如丝般顺滑 +* 全部由C编写完成,并支持C++调用 +* 支持Micropython编程,参见:[LVGL API in Micropython](https://blog.lvgl.io/2019-02-20/micropython-bindings) +* 支持[模拟器](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html)仿真,可以无硬件依托进行开发 +* 丰富详实的[例程](https://github.com/lvgl/lvgl/tree/master/examples) +* 详尽的[文档](http://docs.lvgl.io/)以及API参考手册,可线上查阅或可下载为PDF格式 + +### 硬件要求 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
要求 最低要求建议要求
架构16、32、64位微控制器或微处理器
时钟 > 16 MHz > 48 MHz
Flash/ROM > 64 kB > 180 kB
Static RAM > 16 kB > 48 kB
Draw buffer > 1 × hor. res. pixels > 1/10屏幕大小
编译器 C99或更新
+ +*注意:资源占用情况与具体硬件平台、编译器等因素有关,上表中仅给出参考值* + +### 已经支持的平台 +LVGL本身并不依赖特定的硬件平台,任何满足LVGL硬件配置要求的微控制器均可运行LVGL。 +如下仅列举其中一部分: + +- NXP: Kinetis, LPC, iMX, iMX RT +- STM32F1, STM32F3, STM32F4, STM32F7, STM32L4, STM32L5, STM32H7 +- Microchip dsPIC33, PIC24, PIC32MX, PIC32MZ +- [Linux frame buffer](https://blog.lvgl.io/2018-01-03/linux_fb) (/dev/fb) +- [Raspberry Pi](http://www.vk3erw.com/index.php/16-software/63-raspberry-pi-official-7-touchscreen-and-littlevgl) +- [Espressif ESP32](https://github.com/lvgl/lv_port_esp32) +- [Infineon Aurix](https://github.com/lvgl/lv_port_aurix) +- Nordic NRF52 Bluetooth modules +- Quectel modems +- [SYNWIT SWM341](https://www.synwit.cn/) + +LVGL也支持: +- [Arduino library](https://docs.lvgl.io/master/get-started/platforms/arduino.html) +- [PlatformIO package](https://platformio.org/lib/show/12440/lvgl) +- [Zephyr library](https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_LVGL.html) +- [ESP32 component](https://docs.lvgl.io/master/get-started/platforms/espressif.html) +- [NXP MCUXpresso component](https://www.nxp.com/design/software/embedded-software/lvgl-open-source-graphics-library:LITTLEVGL-OPEN-SOURCE-GRAPHICS-LIBRARY) +- [NuttX library](https://docs.lvgl.io/master/get-started/os/nuttx.html) +- [RT-Thread RTOS](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/packages-manual/lvgl-docs/introduction) + + +## 如何入门 +请按照如下顺序来学习LVGL: +1. 使用[网页在线例程](https://lvgl.io/demos)来体验LVGL(3分钟) +2. 阅读文档[简介](https://docs.lvgl.io/master/intro/index.html)章节来初步了解LVGL(5分钟) +3. 再来阅读一下文档快速[快速概览](https://docs.lvgl.io/master/get-started/quick-overview.html)章节来了解LVGL的基本知识(15分钟) +4. 学习如何使用[模拟器](https://docs.lvgl.io/master/get-started/platforms/pc-simulator.html)来在电脑上仿真LVGL(10分钟) +5. 试着动手实践一些[例程](https://github.com/lvgl/lvgl/tree/master/examples) +6. 参考[移植指南](https://docs.lvgl.io/master/porting/index.html)尝试将LVGL移植到一块开发板上,LVGL也已经提供了一些移植好的[工程](https://github.com/lvgl?q=lv_port_) +7. 仔细阅读文档[总览](https://docs.lvgl.io/master/overview/index.html)章节来更加深入的了解和熟悉LVGL(2-3小时) +8. 浏览文档[组件(Widgets)](https://docs.lvgl.io/master/widgets/index.html)章节来了解如何使用它们 +9. 如果你有问题可以到LVGL[论坛](http://forum.lvgl.io/)提问 +10. 阅读文档[如何向社区贡献](https://docs.lvgl.io/master/CONTRIBUTING.html)章节来看看你能帮LVGL社区做些什么,以促进LVGL软件质量的不断提高(15分钟) + +## 例程 + +更多例程请参见 [examples](https://github.com/lvgl/lvgl/tree/master/examples) 文件夹。 + +![LVGL button with label example](https://github.com/lvgl/lvgl/raw/master/docs/misc/btn_example.png) + +### C +```c +lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button to the current screen*/ +lv_obj_set_pos(btn, 10, 10); /*Set its position*/ +lv_obj_set_size(btn, 100, 50); /*Set its size*/ +lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_CLICKED, NULL); /*Assign a callback to the button*/ + +lv_obj_t * label = lv_label_create(btn); /*Add a label to the button*/ +lv_label_set_text(label, "Button"); /*Set the labels text*/ +lv_obj_center(label); /*Align the label to the center*/ +... + +void btn_event_cb(lv_event_t * e) +{ + printf("Clicked\n"); +} +``` +### Micropython +更多信息请到 [Micropython官网](https://docs.lvgl.io/master/get-started/bindings/micropython.html) 查询. +```python +def btn_event_cb(e): + print("Clicked") + +# Create a Button and a Label +btn = lv.btn(lv.scr_act()) +btn.set_pos(10, 10) +btn.set_size(100, 50) +btn.add_event_cb(btn_event_cb, lv.EVENT.CLICKED, None) + +label = lv.label(btn) +label.set_text("Button") +label.center() +``` + +## 服务 +LVGL 责任有限公司成立的目的是为了给用户使用LVGL图形库提供额外的技术支持,我们致力于提供以下服务: + +- 图形设计 +- UI设计 +- 技术咨询以及技术支持 + +更多信息请参见 https://lvgl.io/services ,如果有任何问题请随时联系我们。 + + +## 如何向社区贡献 +LVGL是一个开源项目,非常欢迎您参与到社区贡献当中。您有很多种方式来为提高LVGL贡献您的一份力量,包括但不限于: + +- 介绍你基于LVGL设计的作品或项目 +- 写一些例程 +- 修改以及完善文档 +- 修复bug + +请参见文档[如何向社区贡献](https://docs.lvgl.io/master/CONTRIBUTING.html)章节来获取更多信息。 diff --git a/lib/libesp32_lvgl/lvgl/library.json b/lib/libesp32_lvgl/lvgl/library.json index 79cf2c0e1..7a475b1ce 100644 --- a/lib/libesp32_lvgl/lvgl/library.json +++ b/lib/libesp32_lvgl/lvgl/library.json @@ -1,6 +1,6 @@ { "name": "lvgl", - "version": "8.2.0", + "version": "8.3.0", "keywords": "graphics, gui, embedded, tft, lvgl", "description": "Graphics library to create embedded GUI with easy-to-use graphical elements, beautiful visual effects and low memory footprint. It offers anti-aliasing, opacity, and animations using only one frame buffer.", "repository": { diff --git a/lib/libesp32_lvgl/lvgl/library.properties b/lib/libesp32_lvgl/lvgl/library.properties index 99a97ed6e..4b27e7d96 100644 --- a/lib/libesp32_lvgl/lvgl/library.properties +++ b/lib/libesp32_lvgl/lvgl/library.properties @@ -1,5 +1,5 @@ name=lvgl -version=8.2.0 +version=8.3.0 author=kisvegabor maintainer=kisvegabor,embeddedt,pete-pjb sentence=Full-featured Graphics Library for Embedded Systems diff --git a/lib/libesp32_lvgl/lvgl/lv_conf_template.h b/lib/libesp32_lvgl/lvgl/lv_conf_template.h index c268d269b..03a4ec353 100644 --- a/lib/libesp32_lvgl/lvgl/lv_conf_template.h +++ b/lib/libesp32_lvgl/lvgl/lv_conf_template.h @@ -1,6 +1,6 @@ /** * @file lv_conf.h - * Configuration file for v8.2.0 + * Configuration file for v8.3.0 */ /* @@ -29,14 +29,14 @@ /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ #define LV_COLOR_16_SWAP 0 -/*Enable more complex drawing routines to manage screens transparency. - *Can be used if the UI is above another layer, e.g. an OSD menu or video player. - *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/ +/*Enable features to draw on transparent background. + *It's required if opa, and transform_* style properties are used. + *Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/ #define LV_COLOR_SCREEN_TRANSP 0 /* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ -#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128) +#define LV_COLOR_MIX_ROUND_OFS 0 /*Images pixels with this color will not be drawn if they are chroma keyed)*/ #define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ @@ -55,8 +55,8 @@ #define LV_MEM_ADR 0 /*0: unused*/ /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ #if LV_MEM_ADR == 0 - //#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/ - //#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/ + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC #endif #else /*LV_MEM_CUSTOM*/ @@ -120,33 +120,49 @@ #define LV_CIRCLE_CACHE_SIZE 4 #endif /*LV_DRAW_COMPLEX*/ +/** + * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer + * and blend it as an image with the given opacity. + * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) + * The widget can be buffered in smaller chunks to avoid using large buffers. + * + * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it + * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. + * + * Both buffer sizes are in bytes. + * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers + * and can't be drawn in chunks. So these settings affects only widgets with opacity. + */ +#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) +#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) + /*Default image cache size. Image caching keeps the images opened. *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. *However the opened images might consume additional RAM. *0: to disable caching*/ -#define LV_IMG_CACHE_DEF_SIZE 0 +#define LV_IMG_CACHE_DEF_SIZE 0 /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ -#define LV_GRADIENT_MAX_STOPS 2 +#define LV_GRADIENT_MAX_STOPS 2 /*Default gradient buffer size. *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. *If the cache is too small the map will be allocated only while it's required for the drawing. *0 mean no caching.*/ -#define LV_GRAD_CACHE_DEF_SIZE 0 +#define LV_GRAD_CACHE_DEF_SIZE 0 /*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ -#define LV_DITHER_GRADIENT 0 +#define LV_DITHER_GRADIENT 0 #if LV_DITHER_GRADIENT /*Add support for error diffusion dithering. *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. *The increase in memory consumption is (24 bits * object's width)*/ - #define LV_DITHER_ERROR_DIFFUSION 0 + #define LV_DITHER_ERROR_DIFFUSION 0 #endif /*Maximum buffer size to allocate for rotation. @@ -157,6 +173,9 @@ * GPU *-----------*/ +/*Use Arm's 2D acceleration library Arm-2D */ +#define LV_USE_GPU_ARM2D 0 + /*Use STM32's DMA2D (aka Chrom Art) GPU*/ #define LV_USE_GPU_STM32_DMA2D 0 #if LV_USE_GPU_STM32_DMA2D @@ -165,6 +184,12 @@ #define LV_GPU_DMA2D_CMSIS_INCLUDE #endif +/*Use SWM341's DMA2D GPU*/ +#define LV_USE_GPU_SWM341_DMA2D 0 +#if LV_USE_GPU_SWM341_DMA2D + #define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h" +#endif + /*Use NXP's PXP GPU iMX RTxxx platforms*/ #define LV_USE_GPU_NXP_PXP 0 #if LV_USE_GPU_NXP_PXP @@ -380,6 +405,9 @@ #define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ #endif +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + /*================= * TEXT SETTINGS *=================*/ @@ -434,8 +462,6 @@ #define LV_USE_ARC 1 -#define LV_USE_ANIMIMG 1 - #define LV_USE_BAR 1 #define LV_USE_BTN 1 @@ -481,6 +507,8 @@ /*----------- * Widgets *----------*/ +#define LV_USE_ANIMIMG 1 + #define LV_USE_CALENDAR 1 #if LV_USE_CALENDAR #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 @@ -513,6 +541,12 @@ #define LV_USE_MSGBOX 1 +#define LV_USE_SPAN 1 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + #define LV_USE_SPINBOX 1 #define LV_USE_SPINNER 1 @@ -523,12 +557,6 @@ #define LV_USE_WIN 1 -#define LV_USE_SPAN 1 -#if LV_USE_SPAN - /*A line text can contain maximum num of span descriptor */ - #define LV_SPAN_SNIPPET_STACK_SIZE 64 -#endif - /*----------- * Themes *----------*/ @@ -574,7 +602,7 @@ #if LV_USE_FS_STDIO #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for open, read, etc*/ @@ -582,19 +610,19 @@ #if LV_USE_FS_POSIX #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for CreateFile, ReadFile, etc*/ #define LV_USE_FS_WIN32 0 #if LV_USE_FS_WIN32 - #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ -#define LV_USE_FS_FATFS 0 +#define LV_USE_FS_FATFS 0 #if LV_USE_FS_FATFS #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ @@ -638,10 +666,10 @@ /*FFmpeg library for image decoding and playing videos *Supports all major image formats so do not enable other image decoder with it*/ -#define LV_USE_FFMPEG 0 +#define LV_USE_FFMPEG 0 #if LV_USE_FFMPEG /*Dump input information to stderr*/ - #define LV_FFMPEG_AV_DUMP_FORMAT 0 + #define LV_FFMPEG_DUMP_FORMAT 0 #endif /*----------- @@ -652,10 +680,37 @@ #define LV_USE_SNAPSHOT 0 /*1: Enable Monkey test*/ -#define LV_USE_MONKEY 0 +#define LV_USE_MONKEY 0 /*1: Enable grid navigation*/ -#define LV_USE_GRIDNAV 0 +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable a published subscriber based messaging system */ +#define LV_USE_MSG 0 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif // LV_IME_PINYIN_USE_K9_MODE +#endif /*================== * EXAMPLES @@ -669,28 +724,32 @@ ====================*/ /*Show some widget. It might be required to increase `LV_MEM_SIZE` */ -#define LV_USE_DEMO_WIDGETS 0 +#define LV_USE_DEMO_WIDGETS 0 #if LV_USE_DEMO_WIDGETS -#define LV_DEMO_WIDGETS_SLIDESHOW 0 +#define LV_DEMO_WIDGETS_SLIDESHOW 0 #endif /*Demonstrate the usage of encoder and keyboard*/ -#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 /*Benchmark your system*/ -#define LV_USE_DEMO_BENCHMARK 0 +#define LV_USE_DEMO_BENCHMARK 0 +#if LV_USE_DEMO_BENCHMARK +/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ +#define LV_DEMO_BENCHMARK_RGB565A8 0 +#endif /*Stress test for LVGL*/ -#define LV_USE_DEMO_STRESS 0 +#define LV_USE_DEMO_STRESS 0 /*Music player demo*/ -#define LV_USE_DEMO_MUSIC 0 +#define LV_USE_DEMO_MUSIC 0 #if LV_USE_DEMO_MUSIC -# define LV_DEMO_MUSIC_SQUARE 0 -# define LV_DEMO_MUSIC_LANDSCAPE 0 -# define LV_DEMO_MUSIC_ROUND 0 -# define LV_DEMO_MUSIC_LARGE 0 -# define LV_DEMO_MUSIC_AUTO_PLAY 0 + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 #endif /*--END OF LV_CONF_H--*/ diff --git a/lib/libesp32_lvgl/lvgl/lvgl.h b/lib/libesp32_lvgl/lvgl/lvgl.h index df96d213d..315764416 100644 --- a/lib/libesp32_lvgl/lvgl/lvgl.h +++ b/lib/libesp32_lvgl/lvgl/lvgl.h @@ -14,7 +14,7 @@ extern "C" { * CURRENT VERSION OF LVGL ***************************/ #define LVGL_VERSION_MAJOR 8 -#define LVGL_VERSION_MINOR 2 +#define LVGL_VERSION_MINOR 3 #define LVGL_VERSION_PATCH 0 #define LVGL_VERSION_INFO "" @@ -67,11 +67,6 @@ extern "C" { * EXTRAS *----------------*/ #include "src/extra/lv_extra.h" -#include "src/extra/widgets/lv_widgets.h" -#include "src/extra/layouts/lv_layouts.h" -#include "src/extra/themes/lv_themes.h" -#include "src/extra/others/lv_others.h" -#include "src/extra/libs/lv_libs.h" /********************* * DEFINES diff --git a/lib/libesp32_lvgl/lvgl/lvgl.mk b/lib/libesp32_lvgl/lvgl/lvgl.mk deleted file mode 100644 index a57c1d2bb..000000000 --- a/lib/libesp32_lvgl/lvgl/lvgl.mk +++ /dev/null @@ -1,10 +0,0 @@ -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/demos/lv_demos.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/examples/examples.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/extra/extra.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core/lv_core.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/lv_draw.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/font/lv_font.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/hal/lv_hal.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/misc/lv_misc.mk -include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/widgets/lv_widgets.mk diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_disp.c b/lib/libesp32_lvgl/lvgl/src/core/lv_disp.c index 2806c4e7b..a1022b56c 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_disp.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_disp.c @@ -21,12 +21,13 @@ /********************** * STATIC PROTOTYPES **********************/ - +static void scr_load_internal(lv_obj_t * scr); static void scr_load_anim_start(lv_anim_t * a); static void opa_scale_anim(void * obj, int32_t v); static void set_x_anim(void * obj, int32_t v); static void set_y_anim(void * obj, int32_t v); static void scr_anim_ready(lv_anim_t * a); +static bool is_out_anim(lv_scr_load_anim_t a); /********************** * STATIC VARIABLES @@ -80,20 +81,7 @@ lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp) */ void lv_disp_load_scr(lv_obj_t * scr) { - lv_disp_t * d = lv_obj_get_disp(scr); - if(!d) return; /*Shouldn't happen, just to be sure*/ - - lv_obj_t * old_scr = d->act_scr; - - if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL); - if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL); - - d->act_scr = scr; - - if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL); - if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL); - - lv_obj_invalidate(scr); + lv_scr_load_anim(scr, LV_SCR_LOAD_ANIM_NONE, 0, 0, false); } /** @@ -135,7 +123,12 @@ lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp) */ void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th) { - if(disp == NULL) disp = lv_disp_get_default(); + if(!disp) disp = lv_disp_get_default(); + if(!disp) { + LV_LOG_WARN("no display registered"); + return; + } + disp->theme = th; if(disp->screen_cnt == 3 && @@ -220,7 +213,7 @@ void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa) /** * Switch screen with animation * @param scr pointer to the new screen to load - * @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT` + * @param anim_type type of the animation from `lv_scr_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT` * @param time time of the animation * @param delay delay before the transition * @param auto_del true: automatically delete the old screen @@ -234,7 +227,7 @@ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t /*If an other screen load animation is in progress *make target screen loaded immediately. */ if(d->scr_to_load && act_scr != d->scr_to_load) { - lv_disp_load_scr(d->scr_to_load); + scr_load_internal(d->scr_to_load); lv_anim_del(d->scr_to_load, NULL); lv_obj_set_pos(d->scr_to_load, 0, 0); lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0); @@ -252,6 +245,7 @@ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t d->prev_scr = NULL; } + d->draw_prev_over_act = is_out_anim(anim_type); d->del_prev = auto_del; /*Be sure there is no other animation on the screens*/ @@ -264,6 +258,13 @@ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0); lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0); + + /*Shortcut for immediate load*/ + if(time == 0 && delay == 0) { + scr_load_internal(new_scr); + return; + } + lv_anim_t a_new; lv_anim_init(&a_new); lv_anim_set_var(&a_new, new_scr); @@ -328,18 +329,36 @@ void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t lv_anim_set_exec_cb(&a_old, set_y_anim); lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d)); break; - - case LV_SCR_LOAD_ANIM_FADE_ON: + case LV_SCR_LOAD_ANIM_FADE_IN: lv_anim_set_exec_cb(&a_new, opa_scale_anim); lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER); break; + case LV_SCR_LOAD_ANIM_FADE_OUT: + lv_anim_set_exec_cb(&a_old, opa_scale_anim); + lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP); + break; + case LV_SCR_LOAD_ANIM_OUT_LEFT: + lv_anim_set_exec_cb(&a_old, set_x_anim); + lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d)); + break; + case LV_SCR_LOAD_ANIM_OUT_RIGHT: + lv_anim_set_exec_cb(&a_old, set_x_anim); + lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d)); + break; + case LV_SCR_LOAD_ANIM_OUT_TOP: + lv_anim_set_exec_cb(&a_old, set_y_anim); + lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d)); + break; + case LV_SCR_LOAD_ANIM_OUT_BOTTOM: + lv_anim_set_exec_cb(&a_old, set_y_anim); + lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d)); + break; } lv_event_send(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL); lv_anim_start(&a_new); lv_anim_start(&a_old); - } /** @@ -394,6 +413,38 @@ void lv_disp_clean_dcache(lv_disp_t * disp) disp->driver->clean_dcache_cb(disp->driver); } +/** + * Temporarily enable and disable the invalidation of the display. + * @param disp pointer to a display (NULL to use the default display) + * @param en true: enable invalidation; false: invalidation + */ +void lv_disp_enable_invalidation(lv_disp_t * disp, bool en) +{ + if(!disp) disp = lv_disp_get_default(); + if(!disp) { + LV_LOG_WARN("no display registered"); + return; + } + + disp->inv_en_cnt += en ? 1 : -1; +} + +/** + * Get display invalidation is enabled. + * @param disp pointer to a display (NULL to use the default display) + * @return return true if invalidation is enabled + */ +bool lv_disp_is_invalidation_enabled(lv_disp_t * disp) +{ + if(!disp) disp = lv_disp_get_default(); + if(!disp) { + LV_LOG_WARN("no display registered"); + return false; + } + + return (disp->inv_en_cnt > 0); +} + /** * Get a pointer to the screen refresher timer to * modify its parameters with `lv_timer_...` functions. @@ -415,6 +466,24 @@ lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp) * STATIC FUNCTIONS **********************/ +static void scr_load_internal(lv_obj_t * scr) +{ + lv_disp_t * d = lv_obj_get_disp(scr); + if(!d) return; /*Shouldn't happen, just to be sure*/ + + lv_obj_t * old_scr = d->act_scr; + + if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL); + if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL); + + d->act_scr = scr; + + if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL); + if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL); + + lv_obj_invalidate(scr); +} + static void scr_load_anim_start(lv_anim_t * a) { lv_disp_t * d = lv_obj_get_disp(a->var); @@ -449,6 +518,17 @@ static void scr_anim_ready(lv_anim_t * a) if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr); d->prev_scr = NULL; + d->draw_prev_over_act = false; d->scr_to_load = NULL; lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0); + lv_obj_invalidate(d->act_scr); +} + +static bool is_out_anim(lv_scr_load_anim_t anim_type) +{ + return anim_type == LV_SCR_LOAD_ANIM_FADE_OUT || + anim_type == LV_SCR_LOAD_ANIM_OUT_LEFT || + anim_type == LV_SCR_LOAD_ANIM_OUT_RIGHT || + anim_type == LV_SCR_LOAD_ANIM_OUT_TOP || + anim_type == LV_SCR_LOAD_ANIM_OUT_BOTTOM; } diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_disp.h b/lib/libesp32_lvgl/lvgl/src/core/lv_disp.h index 01766b345..7854cb7f2 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_disp.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_disp.h @@ -35,7 +35,13 @@ typedef enum { LV_SCR_LOAD_ANIM_MOVE_RIGHT, LV_SCR_LOAD_ANIM_MOVE_TOP, LV_SCR_LOAD_ANIM_MOVE_BOTTOM, - LV_SCR_LOAD_ANIM_FADE_ON, + LV_SCR_LOAD_ANIM_FADE_IN, + LV_SCR_LOAD_ANIM_FADE_ON = LV_SCR_LOAD_ANIM_FADE_IN, /*For backward compatibility*/ + LV_SCR_LOAD_ANIM_FADE_OUT, + LV_SCR_LOAD_ANIM_OUT_LEFT, + LV_SCR_LOAD_ANIM_OUT_RIGHT, + LV_SCR_LOAD_ANIM_OUT_TOP, + LV_SCR_LOAD_ANIM_OUT_BOTTOM, } lv_scr_load_anim_t; /********************** @@ -116,7 +122,7 @@ void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa); /** * Switch screen with animation * @param scr pointer to the new screen to load - * @param anim_type type of the animation from `lv_scr_load_anim_t`. E.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT` + * @param anim_type type of the animation from `lv_scr_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT` * @param time time of the animation * @param delay delay before the transition * @param auto_del true: automatically delete the old screen @@ -142,6 +148,20 @@ void lv_disp_trig_activity(lv_disp_t * disp); */ void lv_disp_clean_dcache(lv_disp_t * disp); +/** + * Temporarily enable and disable the invalidation of the display. + * @param disp pointer to a display (NULL to use the default display) + * @param en true: enable invalidation; false: invalidation + */ +void lv_disp_enable_invalidation(lv_disp_t * disp, bool en); + +/** + * Get display invalidation is enabled. + * @param disp pointer to a display (NULL to use the default display) + * @return return true if invalidation is enabled + */ +bool lv_disp_is_invalidation_enabled(lv_disp_t * disp); + /** * Get a pointer to the screen refresher timer to * modify its parameters with `lv_timer_...` functions. diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_event.c b/lib/libesp32_lvgl/lvgl/src/core/lv_event.c index 143ff34b8..f53ceac1d 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_event.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_event.c @@ -208,7 +208,7 @@ bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_c int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { - if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb) && + if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) && obj->spec_attr->event_dsc[i].user_data == user_data) { /*Shift the remaining event handlers forward*/ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) { @@ -254,7 +254,7 @@ bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc) void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb) { LV_ASSERT_OBJ(obj, MY_CLASS); - if(obj->spec_attr == NULL) return false; + if(obj->spec_attr == NULL) return NULL; int32_t i = 0; for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) { @@ -436,7 +436,7 @@ static lv_res_t event_send_core(lv_event_t * e) } lv_res_t res = LV_RES_OK; - lv_event_dsc_t * event_dsc = res == LV_RES_INV ? NULL : lv_obj_get_event_dsc(e->current_target, 0); + lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0); uint32_t i = 0; while(event_dsc && res == LV_RES_OK) { diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_event.h b/lib/libesp32_lvgl/lvgl/src/core/lv_event.h index 038570b72..d5a9eb6ba 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_event.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_event.h @@ -41,7 +41,7 @@ typedef enum { LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/ LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/ LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/ - LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins*/ + LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified*/ LV_EVENT_SCROLL_END, /**< Scrolling ends*/ LV_EVENT_SCROLL, /**< Scrolling*/ LV_EVENT_GESTURE, /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */ diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_group.c b/lib/libesp32_lvgl/lvgl/src/core/lv_group.c index 3e13b0b79..242c9d1c4 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_group.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_group.c @@ -24,7 +24,7 @@ /********************** * STATIC PROTOTYPES **********************/ -static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), +static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), void * (*move)(const lv_ll_t *, const void *)); static void lv_group_refocus(lv_group_t * g); static lv_indev_t * get_indev(const lv_group_t * g); @@ -57,6 +57,7 @@ lv_group_t * lv_group_create(void) group->obj_focus = NULL; group->frozen = 0; group->focus_cb = NULL; + group->edge_cb = NULL; group->editing = 0; group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV; group->wrap = 1; @@ -261,12 +262,20 @@ void lv_group_focus_obj(lv_obj_t * obj) void lv_group_focus_next(lv_group_t * group) { - focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next); + bool focus_changed = focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next); + if(group->edge_cb) { + if(!focus_changed) + group->edge_cb(group, true); + } } void lv_group_focus_prev(lv_group_t * group) { - focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev); + bool focus_changed = focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev); + if(group->edge_cb) { + if(!focus_changed) + group->edge_cb(group, false); + } } void lv_group_focus_freeze(lv_group_t * group, bool en) @@ -279,6 +288,9 @@ lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c) { lv_obj_t * act = lv_group_get_focused(group); if(act == NULL) return LV_RES_OK; + + if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RES_OK; + return lv_event_send(act, LV_EVENT_KEY, &c); } @@ -287,6 +299,11 @@ void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb) group->focus_cb = focus_cb; } +void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb) +{ + group->edge_cb = edge_cb; +} + void lv_group_set_editing(lv_group_t * group, bool edit) { if(group == NULL) return; @@ -329,6 +346,12 @@ lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group) return group->focus_cb; } +lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group) +{ + if(!group) return NULL; + return group->edge_cb; +} + bool lv_group_get_editing(const lv_group_t * group) { if(!group) return false; @@ -363,10 +386,11 @@ static void lv_group_refocus(lv_group_t * g) g->wrap = temp_wrap; } -static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), +static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *), void * (*move)(const lv_ll_t *, const void *)) { - if(group->frozen) return; + bool focus_changed = false; + if(group->frozen) return focus_changed; lv_obj_t ** obj_next = group->obj_focus; lv_obj_t ** obj_sentinel = NULL; @@ -376,27 +400,27 @@ static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *) for(;;) { if(obj_next == NULL) { if(group->wrap || obj_sentinel == NULL) { - if(!can_begin) return; + if(!can_begin) return focus_changed; obj_next = begin(&group->obj_ll); can_move = false; can_begin = false; } else { /*Currently focused object is the last/first in the group, keep it that way*/ - return; + return focus_changed; } } if(obj_sentinel == NULL) { obj_sentinel = obj_next; - if(obj_sentinel == NULL) return; /*Group is empty*/ + if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/ } if(can_move) { obj_next = move(&group->obj_ll, obj_next); /*Give up if we walked the entire list and haven't found another visible object*/ - if(obj_next == obj_sentinel) return; + if(obj_next == obj_sentinel) return focus_changed; } can_move = true; @@ -418,22 +442,24 @@ static void focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *) break; } - if(obj_next == group->obj_focus) return; /*There's only one visible object and it's already focused*/ + if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/ if(group->obj_focus) { lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group)); - if(res != LV_RES_OK) return; + if(res != LV_RES_OK) return focus_changed; lv_obj_invalidate(*group->obj_focus); } group->obj_focus = obj_next; lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group)); - if(res != LV_RES_OK) return; + if(res != LV_RES_OK) return focus_changed; lv_obj_invalidate(*group->obj_focus); if(group->focus_cb) group->focus_cb(group); + focus_changed = true; + return focus_changed; } /** diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_group.h b/lib/libesp32_lvgl/lvgl/src/core/lv_group.h index ecf88df6b..091059730 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_group.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_group.h @@ -50,6 +50,7 @@ struct _lv_obj_t; struct _lv_group_t; typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *); +typedef void (*lv_group_edge_cb_t)(struct _lv_group_t *, bool); /** * Groups can be used to logically hold objects so that they can be individually focused. @@ -60,6 +61,10 @@ typedef struct _lv_group_t { struct _lv_obj_t ** obj_focus; /**< The object in focus*/ lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/ + lv_group_edge_cb_t edge_cb; /**< A function to call when an edge is reached, no more focus + targets are available in this direction (to allow edge feedback + like a sound or a scroll bounce) */ + #if LV_USE_USER_DATA void * user_data; #endif @@ -178,6 +183,14 @@ lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c); */ void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb); +/** + * Set a function for a group which will be called when a focus edge is reached + * @param group pointer to a group + * @param edge_cb the call back function or NULL if unused + */ +void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb); + + /** * Set whether the next or previous item in a group is focused if the currently focused obj is * deleted. @@ -214,6 +227,13 @@ struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group); */ lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group); +/** + * Get the edge callback function of a group + * @param group pointer to a group + * @return the call back function or NULL if not set + */ +lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group); + /** * Get the current mode (edit or navigate). * @param group pointer to group diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_indev.c b/lib/libesp32_lvgl/lvgl/src/core/lv_indev.c index 079aa943c..ab1265ec8 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_indev.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_indev.c @@ -75,14 +75,15 @@ void lv_indev_read_timer_cb(lv_timer_t * timer) /*Handle reset query before processing the point*/ indev_proc_reset_query_handler(indev_act); - if(indev_act->proc.disabled) return; + if(indev_act->proc.disabled || + indev_act->driver->disp->prev_scr != NULL) return; /*Input disabled or screen animation active*/ bool continue_reading; do { /*Read the data*/ _lv_indev_read(indev_act, &data); continue_reading = data.continue_reading; - /*The active object might deleted even in the read function*/ + /*The active object might be deleted even in the read function*/ indev_proc_reset_query_handler(indev_act); indev_obj_act = NULL; @@ -121,9 +122,18 @@ void lv_indev_read_timer_cb(lv_timer_t * timer) void lv_indev_enable(lv_indev_t * indev, bool en) { - if(!indev) return; + uint8_t enable = en ? 0 : 1; - indev->proc.disabled = en ? 0 : 1; + if(indev) { + indev->proc.disabled = enable; + } + else { + lv_indev_t * i = lv_indev_get_next(NULL); + while(i) { + i->proc.disabled = enable; + i = lv_indev_get_next(i); + } + } } lv_indev_t * lv_indev_get_act(void) @@ -286,6 +296,7 @@ lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev) return indev->refr_timer; } + lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point) { lv_obj_t * found_p = NULL; @@ -293,16 +304,20 @@ lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point) /*If this obj is hidden the children are hidden too so return immediately*/ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL; - bool hit_test_ok = lv_obj_hit_test(obj, point); + lv_point_t p_trans = *point; + lv_obj_transform_point(obj, &p_trans, false, true); + + bool hit_test_ok = lv_obj_hit_test(obj, &p_trans); /*If the point is on this object or has overflow visible check its children too*/ - if(_lv_area_is_point_on(&obj->coords, point, 0) || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { + if(_lv_area_is_point_on(&obj->coords, &p_trans, 0) || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { int32_t i; uint32_t child_cnt = lv_obj_get_child_cnt(obj); + /*If a child matches use it*/ for(i = child_cnt - 1; i >= 0; i--) { lv_obj_t * child = obj->spec_attr->children[i]; - found_p = lv_indev_search_obj(child, point); + found_p = lv_indev_search_obj(child, &p_trans); if(found_p) return found_p; } } @@ -395,6 +410,8 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data) indev_obj_act = lv_group_get_focused(g); if(indev_obj_act == NULL) return; + bool dis = lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED); + /*Save the last key to compare it with the current latter on RELEASE*/ uint32_t prev_key = i->proc.types.keypad.last_key; @@ -410,26 +427,11 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data) /*Key press happened*/ if(data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_RELEASED) { - LV_LOG_INFO("%d key is pressed", data->key); + LV_LOG_INFO("%" LV_PRIu32 " key is pressed", data->key); i->proc.pr_timestamp = lv_tick_get(); - /*Simulate a press on the object if ENTER was pressed*/ - if(data->key == LV_KEY_ENTER) { - /*Send the ENTER as a normal KEY*/ - lv_group_send_data(g, LV_KEY_ENTER); - - lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act); - if(indev_reset_check(&i->proc)) return; - } - else if(data->key == LV_KEY_ESC) { - /*Send the ESC as a normal KEY*/ - lv_group_send_data(g, LV_KEY_ESC); - - lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act); - if(indev_reset_check(&i->proc)) return; - } /*Move the focus on NEXT*/ - else if(data->key == LV_KEY_NEXT) { + if(data->key == LV_KEY_NEXT) { lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/ lv_group_focus_next(g); if(indev_reset_check(&i->proc)) return; @@ -440,13 +442,33 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data) lv_group_focus_prev(g); if(indev_reset_check(&i->proc)) return; } - /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/ - else { - lv_group_send_data(g, data->key); + else if(!dis) { + /*Simulate a press on the object if ENTER was pressed*/ + if(data->key == LV_KEY_ENTER) { + /*Send the ENTER as a normal KEY*/ + lv_group_send_data(g, LV_KEY_ENTER); + if(indev_reset_check(&i->proc)) return; + + if(!dis) lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act); + if(indev_reset_check(&i->proc)) return; + } + else if(data->key == LV_KEY_ESC) { + /*Send the ESC as a normal KEY*/ + lv_group_send_data(g, LV_KEY_ESC); + if(indev_reset_check(&i->proc)) return; + + lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act); + if(indev_reset_check(&i->proc)) return; + } + /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/ + else { + lv_group_send_data(g, data->key); + if(indev_reset_check(&i->proc)) return; + } } } /*Pressing*/ - else if(data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_PRESSED) { + else if(!dis && data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_PRESSED) { if(data->key == LV_KEY_ENTER) { lv_event_send(indev_obj_act, LV_EVENT_PRESSING, indev_act); @@ -493,8 +515,8 @@ static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data) } } /*Release happened*/ - else if(data->state == LV_INDEV_STATE_RELEASED && prev_state == LV_INDEV_STATE_PRESSED) { - LV_LOG_INFO("%d key is released", data->key); + else if(!dis && data->state == LV_INDEV_STATE_RELEASED && prev_state == LV_INDEV_STATE_PRESSED) { + LV_LOG_INFO("%" LV_PRIu32 " key is released", data->key); /*The user might clear the key when it was released. Always release the pressed key*/ data->key = prev_key; if(data->key == LV_KEY_ENTER) { @@ -579,6 +601,7 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data) else if(data->key == LV_KEY_ESC) { /*Send the ESC as a normal KEY*/ lv_group_send_data(g, LV_KEY_ESC); + if(indev_reset_check(&i->proc)) return; lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act); if(indev_reset_check(&i->proc)) return; @@ -586,6 +609,7 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data) /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/ else { lv_group_send_data(g, data->key); + if(indev_reset_check(&i->proc)) return; } } /*Pressing*/ @@ -676,8 +700,8 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data) lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act); if(indev_reset_check(&i->proc)) return; - lv_group_send_data(g, LV_KEY_ENTER); + if(indev_reset_check(&i->proc)) return; } else { lv_obj_clear_state(indev_obj_act, LV_STATE_PRESSED); /*Remove the pressed state manually*/ @@ -703,10 +727,16 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data) LV_LOG_INFO("rotated by %+d (edit)", data->enc_diff); int32_t s; if(data->enc_diff < 0) { - for(s = 0; s < -data->enc_diff; s++) lv_group_send_data(g, LV_KEY_LEFT); + for(s = 0; s < -data->enc_diff; s++) { + lv_group_send_data(g, LV_KEY_LEFT); + if(indev_reset_check(&i->proc)) return; + } } else if(data->enc_diff > 0) { - for(s = 0; s < data->enc_diff; s++) lv_group_send_data(g, LV_KEY_RIGHT); + for(s = 0; s < data->enc_diff; s++) { + lv_group_send_data(g, LV_KEY_RIGHT); + if(indev_reset_check(&i->proc)) return; + } } } /*In navigate mode focus on the next/prev objects*/ @@ -714,10 +744,16 @@ static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data) LV_LOG_INFO("rotated by %+d (nav)", data->enc_diff); int32_t s; if(data->enc_diff < 0) { - for(s = 0; s < -data->enc_diff; s++) lv_group_focus_prev(g); + for(s = 0; s < -data->enc_diff; s++) { + lv_group_focus_prev(g); + if(indev_reset_check(&i->proc)) return; + } } else if(data->enc_diff > 0) { - for(s = 0; s < data->enc_diff; s++) lv_group_focus_next(g); + for(s = 0; s < data->enc_diff; s++) { + lv_group_focus_next(g); + if(indev_reset_check(&i->proc)) return; + } } } } @@ -743,10 +779,10 @@ static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data) static lv_indev_state_t prev_state = LV_INDEV_STATE_RELEASED; if(prev_state != data->state) { if(data->state == LV_INDEV_STATE_PRESSED) { - LV_LOG_INFO("button %d is pressed (x:%d y:%d)", data->btn_id, x, y); + LV_LOG_INFO("button %" LV_PRIu32 " is pressed (x:%d y:%d)", data->btn_id, x, y); } else { - LV_LOG_INFO("button %d is released (x:%d y:%d)", data->btn_id, x, y); + LV_LOG_INFO("button %" LV_PRIu32 " is released (x:%d y:%d)", data->btn_id, x, y); } } @@ -816,7 +852,9 @@ static void indev_proc_press(_lv_indev_proc_t * proc) if(indev_reset_check(proc)) return; } - /*If a new object was found reset some variables and send a pressed Call the ancestor's event handler*/ + lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, true, true); + + /*If a new object was found reset some variables and send a pressed event handler*/ if(indev_obj_act != proc->types.pointer.act_obj) { proc->types.pointer.last_point.x = proc->types.pointer.act_point.x; proc->types.pointer.last_point.y = proc->types.pointer.act_point.y; @@ -864,11 +902,8 @@ static void indev_proc_press(_lv_indev_proc_t * proc) proc->types.pointer.vect.x = proc->types.pointer.act_point.x - proc->types.pointer.last_point.x; proc->types.pointer.vect.y = proc->types.pointer.act_point.y - proc->types.pointer.last_point.y; - proc->types.pointer.scroll_throw_vect.x = (proc->types.pointer.scroll_throw_vect.x * 4) >> 3; - proc->types.pointer.scroll_throw_vect.y = (proc->types.pointer.scroll_throw_vect.y * 4) >> 3; - - proc->types.pointer.scroll_throw_vect.x += (proc->types.pointer.vect.x * 4) >> 3; - proc->types.pointer.scroll_throw_vect.y += (proc->types.pointer.vect.y * 4) >> 3; + proc->types.pointer.scroll_throw_vect.x = (proc->types.pointer.scroll_throw_vect.x + proc->types.pointer.vect.x) / 2; + proc->types.pointer.scroll_throw_vect.y = (proc->types.pointer.scroll_throw_vect.y + proc->types.pointer.vect.y) / 2; proc->types.pointer.scroll_throw_vect_ori = proc->types.pointer.scroll_throw_vect; @@ -917,6 +952,9 @@ static void indev_proc_press(_lv_indev_proc_t * proc) static void indev_proc_release(_lv_indev_proc_t * proc) { if(proc->wait_until_release != 0) { + lv_event_send(proc->types.pointer.act_obj, LV_EVENT_PRESS_LOST, indev_act); + if(indev_reset_check(proc)) return; + proc->types.pointer.act_obj = NULL; proc->types.pointer.last_obj = NULL; proc->pr_timestamp = 0; diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_indev.h b/lib/libesp32_lvgl/lvgl/src/core/lv_indev.h index 80c793977..a98df86ef 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_indev.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_indev.h @@ -35,7 +35,11 @@ extern "C" { */ void lv_indev_read_timer_cb(lv_timer_t * timer); - +/** + * Enable or disable one or all input devices (default enabled) + * @param indev pointer to an input device or NULL to enable/disable all of them + * @param en true to enable, false to disable + */ void lv_indev_enable(lv_indev_t * indev, bool en); /** diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_indev_scroll.c b/lib/libesp32_lvgl/lvgl/src/core/lv_indev_scroll.c index 96fa2f66d..c05e3459f 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_indev_scroll.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_indev_scroll.c @@ -85,7 +85,8 @@ void _lv_indev_scroll_handler(_lv_indev_proc_t * proc) /*Respect the scroll limit area*/ scroll_limit_diff(proc, &diff_x, &diff_y); - lv_obj_scroll_by(scroll_obj, diff_x, diff_y, LV_ANIM_OFF); + _lv_obj_scroll_by_raw(scroll_obj, diff_x, diff_y); + if(proc->reset_query) return; proc->types.pointer.scroll_sum.x += diff_x; proc->types.pointer.scroll_sum.y += diff_y; } diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj.c index 0b5f27a85..8890fcbe0 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj.c @@ -30,9 +30,12 @@ #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h" #endif +#if LV_USE_GPU_SWM341_DMA2D + #include "../draw/swm341_dma2d/lv_gpu_swm341_dma2d.h" +#endif + #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT - #include "../gpu/lv_gpu_nxp_pxp.h" - #include "../gpu/lv_gpu_nxp_pxp_osa.h" + #include "../draw/nxp/pxp/lv_gpu_nxp_pxp.h" #endif /********************* @@ -41,7 +44,6 @@ #define MY_CLASS &lv_obj_class #define LV_OBJ_DEF_WIDTH (LV_DPX(100)) #define LV_OBJ_DEF_HEIGHT (LV_DPX(50)) -#define GRID_DEBUG 0 /*Draw rectangles on grid cells*/ #define STYLE_TRANSITION_MAX 32 /********************** @@ -117,11 +119,13 @@ void lv_init(void) lv_draw_stm32_dma2d_init(); #endif +#if LV_USE_GPU_SWM341_DMA2D + /*Initialize DMA2D GPU*/ + lv_draw_swm341_dma2d_init(); +#endif + #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT - if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) { - LV_LOG_ERROR("PXP init error. STOP.\n"); - for(; ;) ; - } + PXP_COND_STOP(!lv_gpu_nxp_pxp_init(), "PXP init failed."); #endif _lv_obj_style_init(); @@ -265,6 +269,7 @@ void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f) if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) { lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj)); } + } void lv_obj_add_state(lv_obj_t * obj, lv_state_t state) @@ -489,17 +494,6 @@ static void lv_obj_draw(lv_event_t * e) return; } -#if LV_DRAW_COMPLEX - if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) { - info->res = LV_COVER_RES_NOT_COVER; - return; - } -#endif - if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) { - info->res = LV_COVER_RES_NOT_COVER; - return; - } - info->res = LV_COVER_RES_COVER; } @@ -531,7 +525,6 @@ static void lv_obj_draw(lv_event_t * e) part_dsc.part = LV_PART_MAIN; lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc); - #if LV_DRAW_COMPLEX /*With clip corner enabled draw the bg img separately to make it clipped*/ bool clip_corner = (lv_obj_get_style_clip_corner(obj, LV_PART_MAIN) && draw_dsc.radius != 0) ? true : false; @@ -846,9 +839,8 @@ static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e) } } else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { - lv_coord_t * s = lv_event_get_param(e); lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN); - *s = LV_MAX(*s, d); + lv_event_set_ext_draw_size(e, d); } else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) { lv_obj_draw(e); @@ -886,7 +878,7 @@ static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state) if(obj_style->is_trans) continue; lv_style_value_t v; - if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) == false) continue; + if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) != LV_STYLE_RES_FOUND) continue; const lv_style_transition_dsc_t * tr = v.ptr; /*Add the props to the set if not added yet or added but with smaller weight*/ diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj.h index 7bd89c8a4..c89e46099 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj.h @@ -63,7 +63,7 @@ typedef uint16_t lv_state_t; * The possible parts of widgets. * The parts can be considered as the internal building block of the widgets. * E.g. slider = background + indicator + knob - * Note every part is used by every widget + * Not all parts are used by every widget */ enum { LV_PART_MAIN = 0x000000, /**< A background like rectangle*/ @@ -167,7 +167,8 @@ typedef struct { lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally*/ lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/ lv_dir_t scroll_dir : 4; /**< The allowed scroll direction(s)*/ - uint8_t event_dsc_cnt; /**< Number of event callbacks stored in `event_dsc` array*/ + uint8_t event_dsc_cnt : 6; /**< Number of event callbacks stored in `event_dsc` array*/ + uint8_t layer_type : 2; /**< Cache the layer type here. Element of @lv_intermediate_layer_type_t */ } _lv_obj_spec_attr_t; typedef struct _lv_obj_t { diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_class.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_class.c index 174453e98..fe2044824 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_class.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_class.c @@ -56,6 +56,7 @@ lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * pa lv_disp_t * disp = lv_disp_get_default(); if(!disp) { LV_LOG_WARN("No display created yet. No place to assign the new screen"); + lv_mem_free(obj); return NULL; } diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.c index db7c6ea54..f2428cddf 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.c @@ -38,21 +38,23 @@ void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc) { - -#if LV_DRAW_COMPLEX - draw_dsc->radius = lv_obj_get_style_radius(obj, part); - - lv_opa_t main_opa = part != LV_PART_MAIN ? lv_obj_get_style_opa(obj, part) : LV_OPA_COVER; - lv_opa_t opa = lv_obj_get_style_opa(obj, part); - if(opa <= LV_OPA_MIN || main_opa <= LV_OPA_MIN) { - draw_dsc->bg_opa = LV_OPA_TRANSP; - draw_dsc->border_opa = LV_OPA_TRANSP; - draw_dsc->shadow_opa = LV_OPA_TRANSP; - draw_dsc->outline_opa = LV_OPA_TRANSP; - return; + lv_opa_t opa = LV_OPA_COVER; + if(part != LV_PART_MAIN) { + opa = lv_obj_get_style_opa(obj, part); + if(opa <= LV_OPA_MIN) { + draw_dsc->bg_opa = LV_OPA_TRANSP; + draw_dsc->bg_img_opa = LV_OPA_TRANSP; + draw_dsc->border_opa = LV_OPA_TRANSP; + draw_dsc->outline_opa = LV_OPA_TRANSP; + draw_dsc->shadow_opa = LV_OPA_TRANSP; + return; + } } - draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); +#if LV_DRAW_COMPLEX + if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); + + draw_dsc->radius = lv_obj_get_style_radius(obj, part); if(draw_dsc->bg_opa != LV_OPA_TRANSP) { draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part); @@ -130,17 +132,6 @@ void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t } } - if(main_opa < LV_OPA_MAX) { - opa = (uint16_t)((uint16_t) main_opa * opa) >> 8; - } - - if(opa < LV_OPA_MAX) { - draw_dsc->bg_opa = (uint16_t)((uint16_t)draw_dsc->bg_opa * opa) >> 8; - draw_dsc->bg_img_opa = (uint16_t)((uint16_t)draw_dsc->bg_img_opa * opa) >> 8; - draw_dsc->border_opa = (uint16_t)((uint16_t)draw_dsc->border_opa * opa) >> 8; - draw_dsc->shadow_opa = (uint16_t)((uint16_t)draw_dsc->shadow_opa * opa) >> 8; - draw_dsc->outline_opa = (uint16_t)((uint16_t)draw_dsc->outline_opa * opa) >> 8; - } #else /*LV_DRAW_COMPLEX*/ if(draw_dsc->bg_opa != LV_OPA_TRANSP) { draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part); @@ -189,6 +180,16 @@ void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t } } #endif + + if(part != LV_PART_MAIN) { + if(opa < LV_OPA_MAX) { + draw_dsc->bg_opa = (opa * draw_dsc->shadow_opa) >> 8; + draw_dsc->bg_img_opa = (opa * draw_dsc->shadow_opa) >> 8; + draw_dsc->border_opa = (opa * draw_dsc->shadow_opa) >> 8; + draw_dsc->outline_opa = (opa * draw_dsc->shadow_opa) >> 8; + draw_dsc->shadow_opa = (opa * draw_dsc->shadow_opa) >> 8; + } + } } void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc) @@ -196,18 +197,23 @@ void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc draw_dsc->opa = lv_obj_get_style_text_opa(obj, part); if(draw_dsc->opa <= LV_OPA_MIN) return; - lv_opa_t opa = lv_obj_get_style_opa(obj, part); - if(opa < LV_OPA_MAX) { - draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8; + if(part != LV_PART_MAIN) { + lv_opa_t opa = lv_obj_get_style_opa(obj, part); + if(opa <= LV_OPA_MIN) { + draw_dsc->opa = LV_OPA_TRANSP; + return; + } + if(opa < LV_OPA_MAX) { + draw_dsc->opa = (opa * draw_dsc->opa) >> 8; + } } - if(draw_dsc->opa <= LV_OPA_MIN) return; draw_dsc->color = lv_obj_get_style_text_color_filtered(obj, part); draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part); draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part); draw_dsc->decor = lv_obj_get_style_text_decor(obj, part); #if LV_DRAW_COMPLEX - draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); + if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); #endif draw_dsc->font = lv_obj_get_style_text_font(obj, part); @@ -222,13 +228,18 @@ void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc) { draw_dsc->opa = lv_obj_get_style_img_opa(obj, part); - if(draw_dsc->opa <= LV_OPA_MIN) return; + if(draw_dsc->opa <= LV_OPA_MIN) return; - lv_opa_t opa_scale = lv_obj_get_style_opa(obj, part); - if(opa_scale < LV_OPA_MAX) { - draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa_scale) >> 8; + if(part != LV_PART_MAIN) { + lv_opa_t opa = lv_obj_get_style_opa(obj, part); + if(opa <= LV_OPA_MIN) { + draw_dsc->opa = LV_OPA_TRANSP; + return; + } + if(opa < LV_OPA_MAX) { + draw_dsc->opa = (opa * draw_dsc->opa) >> 8; + } } - if(draw_dsc->opa <= LV_OPA_MIN) return; draw_dsc->angle = 0; draw_dsc->zoom = LV_IMG_ZOOM_NONE; @@ -240,23 +251,28 @@ void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc->recolor = lv_obj_get_style_img_recolor_filtered(obj, part); } #if LV_DRAW_COMPLEX - draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); + if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); #endif } void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc) { - draw_dsc->width = lv_obj_get_style_line_width(obj, part); - if(draw_dsc->width == 0) return; - draw_dsc->opa = lv_obj_get_style_line_opa(obj, part); if(draw_dsc->opa <= LV_OPA_MIN) return; - lv_opa_t opa = lv_obj_get_style_opa(obj, part); - if(opa < LV_OPA_MAX) { - draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8; + if(part != LV_PART_MAIN) { + lv_opa_t opa = lv_obj_get_style_opa(obj, part); + if(opa <= LV_OPA_MIN) { + draw_dsc->opa = LV_OPA_TRANSP; + return; + } + if(opa < LV_OPA_MAX) { + draw_dsc->opa = (opa * draw_dsc->opa) >> 8; + } } - if(draw_dsc->opa <= LV_OPA_MIN) return; + + draw_dsc->width = lv_obj_get_style_line_width(obj, part); + if(draw_dsc->width == 0) return; draw_dsc->color = lv_obj_get_style_line_color_filtered(obj, part); @@ -269,7 +285,7 @@ void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t draw_dsc->round_end = draw_dsc->round_start; #if LV_DRAW_COMPLEX - draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); + if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); #endif } @@ -281,11 +297,16 @@ void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part); if(draw_dsc->opa <= LV_OPA_MIN) return; - lv_opa_t opa = lv_obj_get_style_opa(obj, part); - if(opa < LV_OPA_MAX) { - draw_dsc->opa = (uint16_t)((uint16_t)draw_dsc->opa * opa) >> 8; + if(part != LV_PART_MAIN) { + lv_opa_t opa = lv_obj_get_style_opa(obj, part); + if(opa <= LV_OPA_MIN) { + draw_dsc->opa = LV_OPA_TRANSP; + return; + } + if(opa < LV_OPA_MAX) { + draw_dsc->opa = (opa * draw_dsc->opa) >> 8; + } } - if(draw_dsc->opa <= LV_OPA_MIN) return; draw_dsc->color = lv_obj_get_style_arc_color_filtered(obj, part); draw_dsc->img_src = lv_obj_get_style_arc_img_src(obj, part); @@ -293,7 +314,7 @@ void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part); #if LV_DRAW_COMPLEX - draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); + if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part); #endif } @@ -373,6 +394,13 @@ lv_coord_t _lv_obj_get_ext_draw_size(const lv_obj_t * obj) else return 0; } +lv_layer_type_t _lv_obj_get_layer_type(const lv_obj_t * obj) +{ + + if(obj->spec_attr) return obj->spec_attr->layer_type; + else return LV_LAYER_TYPE_NONE; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.h index 063216540..3f9d0f30f 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_draw.h @@ -33,6 +33,12 @@ typedef enum { LV_COVER_RES_MASKED = 2, } lv_cover_res_t; +typedef enum { + LV_LAYER_TYPE_NONE, + LV_LAYER_TYPE_SIMPLE, + LV_LAYER_TYPE_TRANSFORM, +} lv_layer_type_t; + typedef struct { lv_draw_ctx_t * draw_ctx; /**< Draw context*/ const struct _lv_obj_class_t * class_p; /**< The class that sent the event */ @@ -50,14 +56,14 @@ typedef struct { arc_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for arc-like parts*/ const lv_point_t * p1; /**< A point calculated during drawing. E.g. a point of chart or the center of an arc.*/ - const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/ + const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/ char * text; /**< A text calculated during drawing. Can be modified. E.g. tick labels on a chart axis.*/ - uint32_t text_length; /**< Size of the text buffer containing null-terminated text string calculated during drawing.*/ - uint32_t part; /**< The current part for which the event is sent*/ - uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/ - lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/ - int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/ - const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */ + uint32_t text_length; /**< Size of the text buffer containing null-terminated text string calculated during drawing.*/ + uint32_t part; /**< The current part for which the event is sent*/ + uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/ + lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/ + int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/ + const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */ } lv_obj_draw_part_dsc_t; /********************** @@ -66,11 +72,11 @@ typedef struct { /** * Initialize a rectangle draw descriptor from an object's styles in its current state - * @param obj pointer to an object - * @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc - * @param draw_dsc the descriptor to initialize. - * If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized. - * Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`. + * @param obj pointer to an object + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized. + * Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`. * @note Only the relevant fields will be set. * E.g. if `border width == 0` the other border properties won't be evaluated. */ @@ -78,20 +84,20 @@ void lv_obj_init_draw_rect_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_re /** * Initialize a label draw descriptor from an object's styles in its current state - * @param obj pointer to an object - * @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc - * @param draw_dsc the descriptor to initialize. - * If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized. - * Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`. + * @param obj pointer to an object + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized. + * Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`. */ void lv_obj_init_draw_label_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc); /** * Initialize an image draw descriptor from an object's styles in its current state - * @param obj pointer to an object - * @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc - * @param draw_dsc the descriptor to initialize. - * Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`. + * @param obj pointer to an object + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`. */ void lv_obj_init_draw_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc); @@ -99,33 +105,33 @@ void lv_obj_init_draw_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img /** * Initialize a line draw descriptor from an object's styles in its current state * @param obj pointer to an object - * @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc - * @param draw_dsc the descriptor to initialize. - * Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`. + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`. */ void lv_obj_init_draw_line_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc); /** * Initialize an arc draw descriptor from an object's styles in its current state - * @param obj pointer to an object - * @param part part of the object. E.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc - * @param draw_dsc the descriptor to initialize. - * Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`. + * @param obj pointer to an object + * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc + * @param draw_dsc the descriptor to initialize. + * Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`. */ void lv_obj_init_draw_arc_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc); /** * Get the required extra size (around the object's part) to draw shadow, outline, value etc. - * @param obj pointer to an object - * @param part part of the object - * @return the extra size required around the object + * @param obj pointer to an object + * @param part part of the object + * @return the extra size required around the object */ lv_coord_t lv_obj_calculate_ext_draw_size(struct _lv_obj_t * obj, uint32_t part); /** * Initialize a draw descriptor used in events. - * @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EEVNT_DRAW_PART_BEGIN/END` event. - * @param draw the current draw context. (usually returned by `lv_event_get_draw_ctx(e)`) + * @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EVENT_DRAW_PART_BEGIN/END` event. + * @param draw the current draw context. (usually returned by `lv_event_get_draw_ctx(e)`) */ void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, lv_draw_ctx_t * draw_ctx); @@ -141,17 +147,20 @@ bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const struct _lv_ /** * Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size. * The result will be saved in `obj`. - * @param obj pointer to an object + * @param obj pointer to an object */ void lv_obj_refresh_ext_draw_size(struct _lv_obj_t * obj); /** * Get the extended draw area of an object. - * @param obj pointer to an object - * @return the size extended draw area around the real coordinates + * @param obj pointer to an object + * @return the size extended draw area around the real coordinates */ lv_coord_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj); + +lv_layer_type_t _lv_obj_get_layer_type(const struct _lv_obj_t * obj); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.c index 92d4bc4d9..a31c11db8 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.c @@ -26,6 +26,7 @@ static lv_coord_t calc_content_width(lv_obj_t * obj); static lv_coord_t calc_content_height(lv_obj_t * obj); static void layout_update_core(lv_obj_t * obj); +static void transform_point(const lv_obj_t * obj, lv_point_t * p, bool inv); /********************** * STATIC VARIABLES @@ -206,6 +207,8 @@ bool lv_obj_refr_size(lv_obj_t * obj) bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0); if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent); + lv_obj_refresh_ext_draw_size(obj); + return true; } @@ -627,6 +630,7 @@ void lv_obj_refr_pos(lv_obj_t * obj) { if(lv_obj_is_layout_positioned(obj)) return; + lv_obj_t * parent = lv_obj_get_parent(obj); lv_coord_t x = lv_obj_get_style_x(obj, LV_PART_MAIN); lv_coord_t y = lv_obj_get_style_y(obj, LV_PART_MAIN); @@ -791,28 +795,63 @@ void lv_obj_move_children_by(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_dif } } +void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, bool recursive, bool inv) +{ + if(obj) { + lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj); + bool do_tranf = layer_type == LV_LAYER_TYPE_TRANSFORM; + if(inv) { + if(recursive) lv_obj_transform_point(lv_obj_get_parent(obj), p, recursive, inv); + if(do_tranf) transform_point(obj, p, inv); + } + else { + if(do_tranf) transform_point(obj, p, inv); + if(recursive) lv_obj_transform_point(lv_obj_get_parent(obj), p, recursive, inv); + } + } +} + +void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, bool recursive, + bool inv) +{ + lv_point_t p[4] = { + {area->x1, area->y1}, + {area->x1, area->y2}, + {area->x2, area->y1}, + {area->x2, area->y2}, + }; + + lv_obj_transform_point(obj, &p[0], recursive, inv); + lv_obj_transform_point(obj, &p[1], recursive, inv); + lv_obj_transform_point(obj, &p[2], recursive, inv); + lv_obj_transform_point(obj, &p[3], recursive, inv); + + area->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x); + area->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x); + area->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y); + area->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y); + lv_area_increase(area, 5, 5); +} + void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area) { LV_ASSERT_OBJ(obj, MY_CLASS); + lv_disp_t * disp = lv_obj_get_disp(obj); + if(!lv_disp_is_invalidation_enabled(disp)) return; + lv_area_t area_tmp; lv_area_copy(&area_tmp, area); - bool visible = lv_obj_area_is_visible(obj, &area_tmp); + if(!lv_obj_area_is_visible(obj, &area_tmp)) return; - if(visible) _lv_inv_area(lv_obj_get_disp(obj), &area_tmp); + _lv_inv_area(lv_obj_get_disp(obj), &area_tmp); } void lv_obj_invalidate(const lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); - /*If the object has overflow visible it can be drawn anywhere on its parent - *It needs to be checked recursively*/ - while(lv_obj_get_parent(obj) && lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { - obj = lv_obj_get_parent(obj); - } - /*Truncate the area to the object*/ lv_area_t obj_coords; lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj); @@ -841,7 +880,7 @@ bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area) } /*Truncate the area to the object*/ - if(!lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { + if(!lv_obj_has_flag_any(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { lv_area_t obj_coords; lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj); lv_area_copy(&obj_coords, &obj->coords); @@ -854,6 +893,9 @@ bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area) if(!_lv_area_intersect(area, area, &obj_coords)) return false; } + lv_obj_get_transformed_area(obj, area, true, false); + + /*Truncate recursively to the parents*/ lv_obj_t * par = lv_obj_get_parent(obj); while(par != NULL) { @@ -861,8 +903,10 @@ bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area) if(lv_obj_has_flag(par, LV_OBJ_FLAG_HIDDEN)) return false; /*Truncate to the parent and if no common parts break*/ - if(!lv_obj_has_flag(par, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { - if(!_lv_area_intersect(area, area, &par->coords)) return false; + if(!lv_obj_has_flag_any(par, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { + lv_area_t par_area = par->coords; + lv_obj_get_transformed_area(par, &par_area, true, false); + if(!_lv_area_intersect(area, area, &par_area)) return false; } par = lv_obj_get_parent(par); @@ -1108,3 +1152,21 @@ static void layout_update_core(lv_obj_t * obj) } } } + +static void transform_point(const lv_obj_t * obj, lv_point_t * p, bool inv) +{ + int16_t angle = lv_obj_get_style_transform_angle(obj, 0); + int16_t zoom = lv_obj_get_style_transform_zoom(obj, 0); + + if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) return; + + lv_point_t pivot; + pivot.x = obj->coords.x1 + lv_obj_get_style_transform_pivot_x(obj, 0); + pivot.y = obj->coords.y1 + lv_obj_get_style_transform_pivot_y(obj, 0); + if(inv) { + angle = -angle; + zoom = (256 * 256) / zoom; + } + + lv_point_transform(p, angle, zoom, &pivot); +} diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.h index 60130398e..d20ee9656 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_pos.h @@ -348,6 +348,24 @@ void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y); void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating); +/** + * Transform a point using the angle and zoom style properties of an object + * @param obj pointer to an object whose style properties should be used + * @param p a point to transform, the result will be written back here too + * @param recursive consider the transformation properties of the parents too + * @param inv do the inverse of the transformation (-angle and 1/zoom) + */ +void lv_obj_transform_point(const struct _lv_obj_t * obj, lv_point_t * p, bool recursive, bool inv); + +/** + * Transform an area using the angle and zoom style properties of an object + * @param obj pointer to an object whose style properties should be used + * @param area an area to transform, the result will be written back here too + * @param recursive consider the transformation properties of the parents too + * @param inv do the inverse of the transformation (-angle and 1/zoom) + */ +void lv_obj_get_transformed_area(const struct _lv_obj_t * obj, lv_area_t * area, bool recursive, bool inv); + /** * Mark an area of an object as invalid. * The area will be truncated to the object's area and marked for redraw. diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.c index 6927acace..02b982614 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.c @@ -31,7 +31,6 @@ /********************** * STATIC PROTOTYPES **********************/ -static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y); static void scroll_x_anim(void * obj, int32_t v); static void scroll_y_anim(void * obj, int32_t v); static void scroll_anim_ready_cb(lv_anim_t * a); @@ -345,14 +344,18 @@ void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enab } else { /*Remove pending animations*/ - bool y_del = lv_anim_del(obj, scroll_y_anim); - bool x_del = lv_anim_del(obj, scroll_x_anim); - scroll_by_raw(obj, dx, dy); - if(y_del || x_del) { - lv_res_t res; - res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL); - if(res != LV_RES_OK) return; - } + lv_anim_del(obj, scroll_y_anim); + lv_anim_del(obj, scroll_x_anim); + + lv_res_t res; + res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL); + if(res != LV_RES_OK) return; + + res = _lv_obj_scroll_by_raw(obj, dx, dy); + if(res != LV_RES_OK) return; + + res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL); + if(res != LV_RES_OK) return; } } @@ -406,6 +409,23 @@ void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en) } } +lv_res_t _lv_obj_scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) +{ + if(x == 0 && y == 0) return LV_RES_OK; + + lv_obj_allocate_spec_attr(obj); + + obj->spec_attr->scroll.x += x; + obj->spec_attr->scroll.y += y; + + lv_obj_move_children_by(obj, x, y, true); + lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL); + if(res != LV_RES_OK) return res; + lv_obj_invalidate(obj); + return LV_RES_OK; +} + + bool lv_obj_is_scrolling(const lv_obj_t * obj) { lv_indev_t * indev = lv_indev_get_next(NULL); @@ -648,29 +668,15 @@ void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en) * STATIC FUNCTIONS **********************/ -static void scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) -{ - if(x == 0 && y == 0) return; - - lv_obj_allocate_spec_attr(obj); - - obj->spec_attr->scroll.x += x; - obj->spec_attr->scroll.y += y; - - lv_obj_move_children_by(obj, x, y, true); - lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL); - if(res != LV_RES_OK) return; - lv_obj_invalidate(obj); -} static void scroll_x_anim(void * obj, int32_t v) { - scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0); + _lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0); } static void scroll_y_anim(void * obj, int32_t v) { - scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj)); + _lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj)); } static void scroll_anim_ready_cb(lv_anim_t * a) @@ -746,7 +752,7 @@ static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_p x_scroll = left_diff; /*Do not let scrolling in*/ lv_coord_t sl = lv_obj_get_scroll_left(parent); - if(sl + x_scroll > 0) x_scroll = 0; + if(sl - x_scroll < 0) x_scroll = 0; } else if(right_diff > 0) { x_scroll = -right_diff; diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.h index c9fdd720d..e1da245b7 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_scroll.h @@ -15,6 +15,7 @@ extern "C" { *********************/ #include "../misc/lv_area.h" #include "../misc/lv_anim.h" +#include "../misc/lv_types.h" /********************* * DEFINES @@ -248,6 +249,18 @@ void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en); */ void lv_obj_scroll_to_view_recursive(struct _lv_obj_t * obj, lv_anim_enable_t anim_en); + +/** + * Low level function to scroll by given x and y coordinates. + * `LV_EVENT_SCROLL` is sent. + * @param obj pointer to an object to scroll + * @param x pixels to scroll horizontally + * @param y pixels to scroll vertically + * @return `LV_RES_INV`: to object was deleted in `LV_EVENT_SCROLL`; + * `LV_RES_OK`: if the object is still valid + */ +lv_res_t _lv_obj_scroll_by_raw(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y); + /** * Tell whether an object is being scrolled or not at this moment * @param obj pointer to an object diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.c index 82e1ebba2..86016c415 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.c @@ -44,14 +44,14 @@ typedef enum { **********************/ static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector); static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part); -static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v); -static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v); +static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v); static void report_style_change_core(void * style, lv_obj_t * obj); static void refresh_children_style(lv_obj_t * obj); static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit); static void trans_anim_cb(void * _tr, int32_t v); static void trans_anim_start_cb(lv_anim_t * a); static void trans_anim_ready_cb(lv_anim_t * a); +static lv_layer_type_t calculate_layer_type(lv_obj_t * obj); static void fade_anim_cb(void * obj, int32_t v); static void fade_in_anim_ready(lv_anim_t * a); @@ -174,7 +174,12 @@ void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style lv_part_t part = lv_obj_style_get_selector_part(selector); - if(prop & LV_STYLE_PROP_LAYOUT_REFR) { + bool is_layout_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYOUT_REFR); + bool is_ext_draw = lv_style_prop_has_flag(prop, LV_STYLE_PROP_EXT_DRAW); + bool is_inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT); + bool is_layer_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYER_REFR); + + if(is_layout_refr) { if(part == LV_PART_ANY || part == LV_PART_MAIN || lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT || @@ -183,19 +188,27 @@ void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style lv_obj_mark_layout_as_dirty(obj); } } - if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || - (prop & LV_STYLE_PROP_PARENT_LAYOUT_REFR))) { + if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || is_layout_refr)) { lv_obj_t * parent = lv_obj_get_parent(obj); if(parent) lv_obj_mark_layout_as_dirty(parent); } - if(prop == LV_STYLE_PROP_ANY || (prop & LV_STYLE_PROP_EXT_DRAW)) { + /*Cache the layer type*/ + if((part == LV_PART_ANY || part == LV_PART_MAIN) && is_layer_refr) { + lv_layer_type_t layer_type = calculate_layer_type(obj); + if(obj->spec_attr) obj->spec_attr->layer_type = layer_type; + else if(layer_type != LV_LAYER_TYPE_NONE) { + lv_obj_allocate_spec_attr(obj); + obj->spec_attr->layer_type = layer_type; + } + } + + if(prop == LV_STYLE_PROP_ANY || is_ext_draw) { lv_obj_refresh_ext_draw_size(obj); } lv_obj_invalidate(obj); - if(prop == LV_STYLE_PROP_ANY || - ((prop & LV_STYLE_PROP_INHERIT) && ((prop & LV_STYLE_PROP_EXT_DRAW) || (prop & LV_STYLE_PROP_LAYOUT_REFR)))) { + if(prop == LV_STYLE_PROP_ANY || (is_inheritable && (is_ext_draw || is_layout_refr))) { if(part != LV_PART_SCROLLBAR) { refresh_children_style(obj); } @@ -210,19 +223,15 @@ void lv_obj_enable_style_refresh(bool en) lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop) { lv_style_value_t value_act; - bool inherit = prop & LV_STYLE_PROP_INHERIT ? true : false; - bool filter = prop & LV_STYLE_PROP_FILTER ? true : false; - if(filter) { - prop &= ~LV_STYLE_PROP_FILTER; - } - bool found = false; + bool inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT); + lv_style_res_t found = LV_STYLE_RES_NOT_FOUND; while(obj) { found = get_prop_core(obj, part, prop, &value_act); - if(found) break; - if(!inherit) break; + if(found == LV_STYLE_RES_FOUND) break; + if(!inheritable) break; /*If not found, check the `MAIN` style first*/ - if(part != LV_PART_MAIN) { + if(found != LV_STYLE_RES_INHERIT && part != LV_PART_MAIN) { part = LV_PART_MAIN; continue; } @@ -231,7 +240,7 @@ lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_ obj = lv_obj_get_parent(obj); } - if(!found) { + if(found != LV_STYLE_RES_FOUND) { if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) { const lv_obj_class_t * cls = obj->class_p; while(cls) { @@ -244,13 +253,17 @@ lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_ cls = cls->base_class; } - value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def; + if(cls) { + value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def; + } + else { + value_act.num = 0; + } } else { value_act = lv_style_prop_get_default(prop); } } - if(filter) value_act = apply_color_filter(obj, part, value_act); return value_act; } @@ -262,9 +275,17 @@ void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_ lv_obj_refresh_style(obj, selector, prop); } +void lv_obj_set_local_style_prop_meta(lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta, + lv_style_selector_t selector) +{ + lv_style_t * style = get_local_style(obj, selector); + lv_style_set_prop_meta(style, prop, meta); + lv_obj_refresh_style(obj, selector, prop); +} -lv_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, - lv_style_selector_t selector) + +lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, + lv_style_selector_t selector) { uint32_t i; for(i = 0; i < obj->style_cnt; i++) { @@ -274,7 +295,7 @@ lv_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_st } } - return LV_RES_INV; + return LV_STYLE_RES_NOT_FOUND; } bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector) @@ -320,7 +341,7 @@ void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t if(tr_dsc->prop == LV_STYLE_RADIUS) { if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) { lv_coord_t whalf = lv_obj_get_width(obj) / 2; - lv_coord_t hhalf = lv_obj_get_width(obj) / 2; + lv_coord_t hhalf = lv_obj_get_height(obj) / 2; if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1); if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1); } @@ -331,28 +352,37 @@ void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t if(tr == NULL) return; tr->start_value = v1; tr->end_value = v2; + tr->obj = obj; + tr->prop = tr_dsc->prop; + tr->selector = part; - if(tr) { - tr->obj = obj; - tr->prop = tr_dsc->prop; - tr->selector = part; - - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, tr); - lv_anim_set_exec_cb(&a, trans_anim_cb); - lv_anim_set_start_cb(&a, trans_anim_start_cb); - lv_anim_set_ready_cb(&a, trans_anim_ready_cb); - lv_anim_set_values(&a, 0x00, 0xFF); - lv_anim_set_time(&a, tr_dsc->time); - lv_anim_set_delay(&a, tr_dsc->delay); - lv_anim_set_path_cb(&a, tr_dsc->path_cb); - lv_anim_set_early_apply(&a, false); + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, tr); + lv_anim_set_exec_cb(&a, trans_anim_cb); + lv_anim_set_start_cb(&a, trans_anim_start_cb); + lv_anim_set_ready_cb(&a, trans_anim_ready_cb); + lv_anim_set_values(&a, 0x00, 0xFF); + lv_anim_set_time(&a, tr_dsc->time); + lv_anim_set_delay(&a, tr_dsc->delay); + lv_anim_set_path_cb(&a, tr_dsc->path_cb); + lv_anim_set_early_apply(&a, false); #if LV_USE_USER_DATA - a.user_data = tr_dsc->user_data; + a.user_data = tr_dsc->user_data; #endif - lv_anim_start(&a); + lv_anim_start(&a); +} + + +lv_style_value_t _lv_obj_style_apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v) +{ + if(obj == NULL) return v; + const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part); + if(f && f->filter_cb) { + lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part); + if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa); } + return v; } _lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2) @@ -534,7 +564,7 @@ static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t se } -static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v) +static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v) { uint8_t group = 1 << _lv_style_get_prop_group(prop); int32_t weight = -1; @@ -543,7 +573,7 @@ static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t lv_style_value_t value_tmp; bool skip_trans = obj->skip_trans; uint32_t i; - bool found; + lv_style_res_t found; for(i = 0; i < obj->style_cnt; i++) { _lv_obj_style_t * obj_style = &obj->styles[i]; if(obj_style->is_trans == false) break; @@ -554,20 +584,22 @@ static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t if(part_act != part) continue; if((obj_style->style->has_group & group) == 0) continue; found = lv_style_get_prop(obj_style->style, prop, &value_tmp); - if(found) { + if(found == LV_STYLE_RES_FOUND) { *v = value_tmp; - return true; + return LV_STYLE_RES_FOUND; + } + else if(found == LV_STYLE_RES_INHERIT) { + return LV_STYLE_RES_INHERIT; } } for(; i < obj->style_cnt; i++) { + if((obj->styles[i].style->has_group & group) == 0) continue; _lv_obj_style_t * obj_style = &obj->styles[i]; lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector); lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector); if(part_act != part) continue; - if((obj_style->style->has_group & group) == 0) continue; - /*Be sure the style not specifies other state than the requested. *E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/ if((state_act & state_inv)) continue; @@ -577,34 +609,26 @@ static bool get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t found = lv_style_get_prop(obj_style->style, prop, &value_tmp); - if(found) { + if(found == LV_STYLE_RES_FOUND) { if(state_act == state) { *v = value_tmp; - return true; + return LV_STYLE_RES_FOUND; } if(weight < state_act) { weight = state_act; *v = value_tmp; } } + else if(found == LV_STYLE_RES_INHERIT) { + return LV_STYLE_RES_INHERIT; + } } if(weight >= 0) { *v = value_tmp; - return true; + return LV_STYLE_RES_FOUND; } - else return false; -} - -static lv_style_value_t apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v) -{ - if(obj == NULL) return v; - const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part); - if(f && f->filter_cb) { - lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part); - if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa); - } - return v; + else return LV_STYLE_RES_NOT_FOUND; } /** @@ -669,19 +693,21 @@ static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, tran tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr); if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) { - /*Remove the transitioned property from trans. style + /*Remove any transitioned properties from the trans. style *to allow changing it by normal styles*/ uint32_t i; for(i = 0; i < obj->style_cnt; i++) { if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) { lv_style_remove_prop(obj->styles[i].style, tr->prop); - lv_anim_del(tr, NULL); - _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr); - lv_mem_free(tr); - removed = true; } } + /*Free the transition descriptor too*/ + lv_anim_del(tr, NULL); + _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr); + lv_mem_free(tr); + removed = true; + } tr = tr_prev; } @@ -810,6 +836,18 @@ static void trans_anim_ready_cb(lv_anim_t * a) } } +static lv_layer_type_t calculate_layer_type(lv_obj_t * obj) +{ + if(lv_obj_get_style_transform_angle(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM; + if(lv_obj_get_style_transform_zoom(obj, 0) != 256) return LV_LAYER_TYPE_TRANSFORM; + if(lv_obj_get_style_opa(obj, 0) != LV_OPA_COVER) return LV_LAYER_TYPE_SIMPLE; + +#if LV_DRAW_COMPLEX + if(lv_obj_get_style_blend_mode(obj, 0) != LV_BLEND_MODE_NORMAL) return LV_LAYER_TYPE_SIMPLE; +#endif + return LV_LAYER_TYPE_NONE; +} + static void fade_anim_cb(void * obj, int32_t v) { lv_obj_set_style_opa(obj, v, 0); diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.h index 7fb2723c8..5d122ca62 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style.h @@ -140,8 +140,11 @@ lv_style_value_t lv_obj_get_style_prop(const struct _lv_obj_t * obj, lv_part_t p void lv_obj_set_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value, lv_style_selector_t selector); -lv_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, - lv_style_selector_t selector); +void lv_obj_set_local_style_prop_meta(struct _lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta, + lv_style_selector_t selector); + +lv_style_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value, + lv_style_selector_t selector); /** * Remove a local style property from a part of an object with a given state. @@ -152,6 +155,11 @@ lv_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t pro */ bool lv_obj_remove_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector); +/** + * Used internally for color filtering + */ +lv_style_value_t _lv_obj_style_apply_color_filter(const struct _lv_obj_t * obj, uint32_t part, lv_style_value_t v); + /** * Used internally to create a style transition * @param obj diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.c index 8eb7ebe6d..64a0bb28c 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.c @@ -120,6 +120,22 @@ void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ANGLE, v, selector); } +void lv_obj_set_style_transform_pivot_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_X, v, selector); +} + +void lv_obj_set_style_transform_pivot_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_Y, v, selector); +} + void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -176,14 +192,6 @@ void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_styl lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector); } -void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -200,14 +208,6 @@ void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector); } -void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -272,14 +272,6 @@ void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, l lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR, v, selector); } -void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_FILTERED, v, selector); -} - void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -304,14 +296,6 @@ void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector); } -void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -360,14 +344,6 @@ void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector); } -void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -424,14 +400,6 @@ void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector); } -void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -456,14 +424,6 @@ void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_s lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR, v, selector); } -void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_FILTERED, v, selector); -} - void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -512,14 +472,6 @@ void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_st lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector); } -void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -552,14 +504,6 @@ void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_sty lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector); } -void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -584,14 +528,6 @@ void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_st lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector); } -void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector) -{ - lv_style_value_t v = { - .color = value - }; - lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR_FILTERED, v, selector); -} - void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -664,8 +600,7 @@ void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selec lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector); } -void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, - lv_style_selector_t selector) +void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector) { lv_style_value_t v = { .ptr = value @@ -681,6 +616,14 @@ void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, l lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector); } +void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM, v, selector); +} + void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector) { lv_style_value_t v = { @@ -697,8 +640,7 @@ void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_styl lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_SPEED, v, selector); } -void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, - lv_style_selector_t selector) +void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector) { lv_style_value_t v = { .ptr = value diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.h b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.h index e4383d5ce..576927d9a 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_style_gen.h @@ -88,6 +88,18 @@ static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t return (lv_coord_t)v.num; } +static inline lv_coord_t lv_obj_get_style_transform_pivot_x(const struct _lv_obj_t * obj, uint32_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_X); + return (lv_coord_t)v.num; +} + +static inline lv_coord_t lv_obj_get_style_transform_pivot_y(const struct _lv_obj_t * obj, uint32_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_Y); + return (lv_coord_t)v.num; +} + static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part) { lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP); @@ -132,7 +144,7 @@ static inline lv_color_t lv_obj_get_style_bg_color(const struct _lv_obj_t * obj, static inline lv_color_t lv_obj_get_style_bg_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR)); return v.color; } @@ -150,7 +162,7 @@ static inline lv_color_t lv_obj_get_style_bg_grad_color(const struct _lv_obj_t * static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR)); return v.color; } @@ -204,7 +216,7 @@ static inline lv_color_t lv_obj_get_style_bg_img_recolor(const struct _lv_obj_t static inline lv_color_t lv_obj_get_style_bg_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR)); return v.color; } @@ -228,7 +240,7 @@ static inline lv_color_t lv_obj_get_style_border_color(const struct _lv_obj_t * static inline lv_color_t lv_obj_get_style_border_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR)); return v.color; } @@ -270,7 +282,7 @@ static inline lv_color_t lv_obj_get_style_outline_color(const struct _lv_obj_t * static inline lv_color_t lv_obj_get_style_outline_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR)); return v.color; } @@ -318,7 +330,7 @@ static inline lv_color_t lv_obj_get_style_shadow_color(const struct _lv_obj_t * static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR)); return v.color; } @@ -342,7 +354,7 @@ static inline lv_color_t lv_obj_get_style_img_recolor(const struct _lv_obj_t * o static inline lv_color_t lv_obj_get_style_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR)); return v.color; } @@ -384,7 +396,7 @@ static inline lv_color_t lv_obj_get_style_line_color(const struct _lv_obj_t * ob static inline lv_color_t lv_obj_get_style_line_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR)); return v.color; } @@ -414,7 +426,7 @@ static inline lv_color_t lv_obj_get_style_arc_color(const struct _lv_obj_t * obj static inline lv_color_t lv_obj_get_style_arc_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR)); return v.color; } @@ -438,7 +450,7 @@ static inline lv_color_t lv_obj_get_style_text_color(const struct _lv_obj_t * ob static inline lv_color_t lv_obj_get_style_text_color_filtered(const struct _lv_obj_t * obj, uint32_t part) { - lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR_FILTERED); + lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR)); return v.color; } @@ -496,8 +508,7 @@ static inline lv_opa_t lv_obj_get_style_opa(const struct _lv_obj_t * obj, uint32 return (lv_opa_t)v.num; } -static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, - uint32_t part) +static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, uint32_t part) { lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC); return (const lv_color_filter_dsc_t *)v.ptr; @@ -509,6 +520,12 @@ static inline lv_opa_t lv_obj_get_style_color_filter_opa(const struct _lv_obj_t return (lv_opa_t)v.num; } +static inline const lv_anim_t * lv_obj_get_style_anim(const struct _lv_obj_t * obj, uint32_t part) +{ + lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM); + return (const lv_anim_t *)v.ptr; +} + static inline uint32_t lv_obj_get_style_anim_time(const struct _lv_obj_t * obj, uint32_t part) { lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_TIME); @@ -560,6 +577,8 @@ void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_s void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); +void lv_obj_set_style_transform_pivot_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); +void lv_obj_set_style_transform_pivot_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); @@ -567,10 +586,8 @@ void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_sty void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_bg_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_bg_grad_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); @@ -579,18 +596,15 @@ void lv_obj_set_style_bg_dither_mode(struct _lv_obj_t * obj, lv_dither_mode_t va void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector); void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_bg_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_border_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector); void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_outline_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); @@ -598,27 +612,22 @@ void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_ void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_shadow_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_img_recolor_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_line_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_arc_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector); void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); -void lv_obj_set_style_text_color_filtered(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector); void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector); void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); @@ -628,13 +637,12 @@ void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector); void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector); void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); -void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, - lv_style_selector_t selector); +void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector); void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector); +void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector); void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector); void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector); -void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, - lv_style_selector_t selector); +void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector); void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector); void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector); void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector); diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_tree.c b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_tree.c index cf82553d6..d3ad16ae3 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_obj_tree.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_obj_tree.c @@ -65,6 +65,7 @@ void lv_obj_del(lv_obj_t * obj) /*Call the ancestor's event handler to the parent to notify it about the child delete*/ if(par) { + lv_obj_update_layout(par); lv_obj_readjust_scroll(par, LV_ANIM_OFF); lv_obj_scrollbar_invalidate(par); lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL); diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_refr.c b/lib/libesp32_lvgl/lvgl/src/core/lv_refr.c index b9b569f90..37a437e46 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_refr.c +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_refr.c @@ -17,6 +17,7 @@ #include "../misc/lv_gc.h" #include "../draw/lv_draw.h" #include "../font/lv_font_fmt_txt.h" +#include "../extra/others/snapshot/lv_snapshot.h" #if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR #include "../widgets/lv_label.h" @@ -51,11 +52,12 @@ typedef struct { * STATIC PROTOTYPES **********************/ static void lv_refr_join_area(void); -static void lv_refr_areas(void); -static void lv_refr_area(const lv_area_t * area_p); -static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx); +static void refr_invalid_areas(void); +static void refr_area(const lv_area_t * area_p); +static void refr_area_part(lv_draw_ctx_t * draw_ctx); static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj); -static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj); +static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj); +static void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj); static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h); static void draw_buf_flush(lv_disp_t * disp); static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p); @@ -124,11 +126,8 @@ void lv_refr_now(lv_disp_t * disp) } } -void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) +void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) { - /*Do not refresh hidden objects*/ - if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return; - const lv_area_t * clip_area_ori = draw_ctx->clip_area; lv_area_t clip_coords_for_obj; @@ -137,31 +136,33 @@ void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) lv_obj_get_coords(obj, &obj_coords_ext); lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj); lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size); - if(!_lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext)) return; - - draw_ctx->clip_area = &clip_coords_for_obj; - - /*Draw the object*/ - lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, draw_ctx); - lv_event_send(obj, LV_EVENT_DRAW_MAIN, draw_ctx); - lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, draw_ctx); + bool com_clip_res = _lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext); + /*If the object is visible on the current clip area OR has overflow visible draw it. + *With overflow visible drawing should happen to apply the masks which might affect children */ + bool should_draw = com_clip_res || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE); + if(should_draw) { + draw_ctx->clip_area = &clip_coords_for_obj; + lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, draw_ctx); + lv_event_send(obj, LV_EVENT_DRAW_MAIN, draw_ctx); + lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, draw_ctx); #if LV_USE_REFR_DEBUG - lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF)); - lv_draw_rect_dsc_t draw_dsc; - lv_draw_rect_dsc_init(&draw_dsc); - draw_dsc.bg_color.full = debug_color.full; - draw_dsc.bg_opa = LV_OPA_20; - draw_dsc.border_width = 1; - draw_dsc.border_opa = LV_OPA_30; - draw_dsc.border_color = debug_color; - lv_draw_rect(draw_ctx, &draw_dsc, &obj_coords_ext); + lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF)); + lv_draw_rect_dsc_t draw_dsc; + lv_draw_rect_dsc_init(&draw_dsc); + draw_dsc.bg_color.full = debug_color.full; + draw_dsc.bg_opa = LV_OPA_20; + draw_dsc.border_width = 1; + draw_dsc.border_opa = LV_OPA_30; + draw_dsc.border_color = debug_color; + lv_draw_rect(draw_ctx, &draw_dsc, &obj_coords_ext); #endif + } /*With overflow visible keep the previous clip area to let the children visible out of this object too *With not overflow visible limit the clip are to the object's coordinates to clip the children*/ - bool refr_children = true; lv_area_t clip_coords_for_children; + bool refr_children = true; if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) { clip_coords_for_children = *clip_area_ori; } @@ -177,20 +178,24 @@ void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) uint32_t child_cnt = lv_obj_get_child_cnt(obj); for(i = 0; i < child_cnt; i++) { lv_obj_t * child = obj->spec_attr->children[i]; - lv_refr_obj(draw_ctx, child); + refr_obj(draw_ctx, child); } } - draw_ctx->clip_area = &clip_coords_for_obj; + /*If the object was visible on the clip area call the post draw events too*/ + if(should_draw) { + draw_ctx->clip_area = &clip_coords_for_obj; - /*If all the children are redrawn make 'post draw' draw*/ - lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, draw_ctx); - lv_event_send(obj, LV_EVENT_DRAW_POST, draw_ctx); - lv_event_send(obj, LV_EVENT_DRAW_POST_END, draw_ctx); + /*If all the children are redrawn make 'post draw' draw*/ + lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, draw_ctx); + lv_event_send(obj, LV_EVENT_DRAW_POST, draw_ctx); + lv_event_send(obj, LV_EVENT_DRAW_POST_END, draw_ctx); + } draw_ctx->clip_area = clip_area_ori; } + /** * Invalidate an area on display to redraw it * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas) @@ -201,6 +206,12 @@ void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p) { if(!disp) disp = lv_disp_get_default(); if(!disp) return; + if(!lv_disp_is_invalidation_enabled(disp)) return; + + if(disp->rendering_in_progress) { + LV_LOG_ERROR("detected modifying dirty areas in render"); + return; + } /*Clear the invalidate buffer if the parameter is NULL*/ if(area_p == NULL) { @@ -310,7 +321,8 @@ void _lv_disp_refr_timer(lv_timer_t * tmr) lv_refr_join_area(); - lv_refr_areas(); + refr_invalid_areas(); + /*If refresh happened ...*/ if(disp_refr->inv_p != 0) { @@ -327,6 +339,7 @@ void _lv_disp_refr_timer(lv_timer_t * tmr) disp_refr->inv_p = 0; elaps = lv_tick_elaps(start); + /*Call monitor cb if present*/ if(disp_refr->driver->monitor_cb) { disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num); @@ -365,9 +378,16 @@ void _lv_disp_refr_timer(lv_timer_t * tmr) } else { perf_monitor.perf_last_time = lv_tick_get(); - uint32_t fps_limit = 1000 / disp_refr->refr_timer->period; + uint32_t fps_limit; uint32_t fps; + if(disp_refr->refr_timer) { + fps_limit = 1000 / disp_refr->refr_timer->period; + } + else { + fps_limit = 1000 / LV_DISP_DEF_REFR_PERIOD; + } + if(perf_monitor.elaps_sum == 0) { perf_monitor.elaps_sum = 1; } @@ -413,8 +433,10 @@ void _lv_disp_refr_timer(lv_timer_t * tmr) uint32_t used_size = mon.total_size - mon.free_size;; uint32_t used_kb = used_size / 1024; uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102; - lv_label_set_text_fmt(mem_label, "%" LV_PRIu32 ".%" LV_PRIu32 " kB used (%d %%)\n" \ - "%d%% frag.", used_kb, used_kb_tenth, mon.used_pct, + lv_label_set_text_fmt(mem_label, + "%"LV_PRIu32 ".%"LV_PRIu32 " kB used (%d %%)\n" + "%d%% frag.", + used_kb, used_kb_tenth, mon.used_pct, mon.frag_pct); } #endif @@ -483,7 +505,7 @@ static void lv_refr_join_area(void) /** * Refresh the joined areas */ -static void lv_refr_areas(void) +static void refr_invalid_areas(void) { px_num = 0; @@ -499,8 +521,14 @@ static void lv_refr_areas(void) } } + /*Notify the display driven rendering has started*/ + if(disp_refr->driver->render_start_cb) { + disp_refr->driver->render_start_cb(disp_refr->driver); + } + disp_refr->driver->draw_buf->last_area = 0; disp_refr->driver->draw_buf->last_part = 0; + disp_refr->rendering_in_progress = true; for(i = 0; i < disp_refr->inv_p; i++) { /*Refresh the unjoined areas*/ @@ -508,18 +536,20 @@ static void lv_refr_areas(void) if(i == last_i) disp_refr->driver->draw_buf->last_area = 1; disp_refr->driver->draw_buf->last_part = 0; - lv_refr_area(&disp_refr->inv_areas[i]); + refr_area(&disp_refr->inv_areas[i]); px_num += lv_area_get_size(&disp_refr->inv_areas[i]); } } + + disp_refr->rendering_in_progress = false; } /** * Refresh an area if there is Virtual Display Buffer * @param area_p pointer to an area to refresh */ -static void lv_refr_area(const lv_area_t * area_p) +static void refr_area(const lv_area_t * area_p) { lv_draw_ctx_t * draw_ctx = disp_refr->driver->draw_ctx; draw_ctx->buf = disp_refr->driver->draw_buf->buf_act; @@ -534,12 +564,12 @@ static void lv_refr_area(const lv_area_t * area_p) if(disp_refr->driver->full_refresh) { disp_refr->driver->draw_buf->last_part = 1; draw_ctx->clip_area = &disp_area; - lv_refr_area_part(draw_ctx); + refr_area_part(draw_ctx); } else { disp_refr->driver->draw_buf->last_part = disp_refr->driver->draw_buf->last_area; draw_ctx->clip_area = area_p; - lv_refr_area_part(draw_ctx); + refr_area_part(draw_ctx); } return; } @@ -568,7 +598,7 @@ static void lv_refr_area(const lv_area_t * area_p) if(sub_area.y2 > y2) sub_area.y2 = y2; row_last = sub_area.y2; if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1; - lv_refr_area_part(draw_ctx); + refr_area_part(draw_ctx); } /*If the last y coordinates are not handled yet ...*/ @@ -582,11 +612,11 @@ static void lv_refr_area(const lv_area_t * area_p) draw_ctx->clip_area = &sub_area; draw_ctx->buf = disp_refr->driver->draw_buf->buf_act; disp_refr->driver->draw_buf->last_part = 1; - lv_refr_area_part(draw_ctx); + refr_area_part(draw_ctx); } } -static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx) +static void refr_area_part(lv_draw_ctx_t * draw_ctx) { lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr); @@ -596,6 +626,18 @@ static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx) while(draw_buf->flushing) { if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver); } + + /*If the screen is transparent initialize it when the flushing is ready*/ +#if LV_COLOR_SCREEN_TRANSP + if(disp_refr->driver->screen_transp) { + if(disp_refr->driver->clear_cb) { + disp_refr->driver->clear_cb(disp_refr->driver, disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size); + } + else { + lv_memset_00(disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size * LV_IMG_PX_SIZE_ALPHA_BYTE); + } + } +#endif } lv_obj_t * top_act_scr = NULL; @@ -609,6 +651,9 @@ static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx) /*Draw a display background if there is no top object*/ if(top_act_scr == NULL && top_prev_scr == NULL) { + lv_area_t a; + lv_area_set(&a, 0, 0, + lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1); if(draw_ctx->draw_bg) { lv_draw_rect_dsc_t dsc; lv_draw_rect_dsc_init(&dsc); @@ -616,15 +661,12 @@ static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx) dsc.bg_img_opa = disp_refr->bg_opa; dsc.bg_color = disp_refr->bg_color; dsc.bg_opa = disp_refr->bg_opa; - draw_ctx->draw_bg(draw_ctx, &dsc, draw_ctx->buf_area); + draw_ctx->draw_bg(draw_ctx, &dsc, &a); } else if(disp_refr->bg_img) { lv_img_header_t header; - lv_res_t res; - res = lv_img_decoder_get_info(disp_refr->bg_img, &header); + lv_res_t res = lv_img_decoder_get_info(disp_refr->bg_img, &header); if(res == LV_RES_OK) { - lv_area_t a; - lv_area_set(&a, 0, 0, header.w - 1, header.h - 1); lv_draw_img_dsc_t dsc; lv_draw_img_dsc_init(&dsc); dsc.opa = disp_refr->bg_opa; @@ -642,18 +684,31 @@ static void lv_refr_area_part(lv_draw_ctx_t * draw_ctx) lv_draw_rect(draw_ctx, &dsc, draw_ctx->buf_area); } } - /*Refresh the previous screen if any*/ - if(disp_refr->prev_scr) { - if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr; - lv_refr_obj_and_children(draw_ctx, top_prev_scr); + + if(disp_refr->draw_prev_over_act) { + if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr; + refr_obj_and_children(draw_ctx, top_act_scr); + + /*Refresh the previous screen if any*/ + if(disp_refr->prev_scr) { + if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr; + refr_obj_and_children(draw_ctx, top_prev_scr); + } + } + else { + /*Refresh the previous screen if any*/ + if(disp_refr->prev_scr) { + if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr; + refr_obj_and_children(draw_ctx, top_prev_scr); + } + + if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr; + refr_obj_and_children(draw_ctx, top_act_scr); } - if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr; - lv_refr_obj_and_children(draw_ctx, top_act_scr); - /*Also refresh top and sys layer unconditionally*/ - lv_refr_obj_and_children(draw_ctx, lv_disp_get_layer_top(disp_refr)); - lv_refr_obj_and_children(draw_ctx, lv_disp_get_layer_sys(disp_refr)); + refr_obj_and_children(draw_ctx, lv_disp_get_layer_top(disp_refr)); + refr_obj_and_children(draw_ctx, lv_disp_get_layer_sys(disp_refr)); /*In true double buffered mode flush only once when all areas were rendered. *In normal mode flush after every area*/ @@ -672,32 +727,32 @@ static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj) { lv_obj_t * found_p = NULL; + if(_lv_area_is_in(area_p, &obj->coords, 0) == false) return NULL; + if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL; + if(_lv_obj_get_layer_type(obj) != LV_LAYER_TYPE_NONE) return NULL; + /*If this object is fully cover the draw area then check the children too*/ - if(_lv_area_is_in(area_p, &obj->coords, 0) && lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN) == false) { - lv_cover_check_info_t info; - info.res = LV_COVER_RES_COVER; - info.area = area_p; - lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); - if(info.res == LV_COVER_RES_MASKED) return NULL; + lv_cover_check_info_t info; + info.res = LV_COVER_RES_COVER; + info.area = area_p; + lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); + if(info.res == LV_COVER_RES_MASKED) return NULL; - uint32_t i; - uint32_t child_cnt = lv_obj_get_child_cnt(obj); - for(i = 0; i < child_cnt; i++) { - lv_obj_t * child = obj->spec_attr->children[i]; - found_p = lv_refr_get_top_obj(area_p, child); + uint32_t i; + uint32_t child_cnt = lv_obj_get_child_cnt(obj); + for(i = 0; i < child_cnt; i++) { + lv_obj_t * child = obj->spec_attr->children[i]; + found_p = lv_refr_get_top_obj(area_p, child); - /*If a children is ok then break*/ - if(found_p != NULL) { - break; - } + /*If a children is ok then break*/ + if(found_p != NULL) { + break; } + } - /*If no better children use this object*/ - if(found_p == NULL) { - if(info.res == LV_COVER_RES_COVER) { - found_p = obj; - } - } + /*If no better children use this object*/ + if(found_p == NULL && info.res == LV_COVER_RES_COVER) { + found_p = obj; } return found_p; @@ -708,7 +763,7 @@ static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj) * @param top_p pointer to an objects. Start the drawing from it. * @param mask_p pointer to an area, the objects will be drawn only here */ -static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj) +static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj) { /*Normally always will be a top_obj (at least the screen) *but in special cases (e.g. if the screen has alpha) it won't. @@ -717,7 +772,7 @@ static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_ob if(top_obj == NULL) return; /*Shouldn't happen*/ /*Refresh the top object and its children*/ - lv_refr_obj(draw_ctx, top_obj); + refr_obj(draw_ctx, top_obj); /*Draw the 'younger' sibling objects because they can be on top_obj*/ lv_obj_t * parent; @@ -737,7 +792,7 @@ static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_ob } else { /*Refresh the objects*/ - lv_refr_obj(draw_ctx, child); + refr_obj(draw_ctx, child); } } @@ -754,6 +809,160 @@ static void lv_refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_ob } } + +static lv_res_t layer_get_area(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj, lv_layer_type_t layer_type, + lv_area_t * layer_area_out) +{ + lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj); + lv_area_t obj_coords_ext; + lv_obj_get_coords(obj, &obj_coords_ext); + lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size); + + if(layer_type == LV_LAYER_TYPE_TRANSFORM) { + /*Get the transformed area and clip it to the current clip area. + *This area needs to be updated on the screen.*/ + lv_area_t clip_coords_for_obj; + lv_area_t tranf_coords = obj_coords_ext; + lv_obj_get_transformed_area(obj, &tranf_coords, false, false); + if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &tranf_coords)) { + return LV_RES_INV; + } + + /*Transform back (inverse) the transformed area. + *It will tell which area of the non-transformed widget needs to be redrawn + *in order to cover transformed area after transformation.*/ + lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj; + lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, false, true); + if(!_lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, &obj_coords_ext)) { + return LV_RES_INV; + } + + *layer_area_out = inverse_clip_coords_for_obj; + } + else if(layer_type == LV_LAYER_TYPE_SIMPLE) { + lv_area_t clip_coords_for_obj; + if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &obj_coords_ext)) { + return LV_RES_INV; + } + *layer_area_out = clip_coords_for_obj; + } + else { + LV_LOG_WARN("Unhandled intermediate layer type"); + return LV_RES_INV; + } + + return LV_RES_OK; +} + +static void layer_alpha_test(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + bool has_alpha; + /*If globally the layer has alpha maybe this smaller section has not (e.g. not on a rounded corner) + *If turns out that this section has no alpha renderer can choose faster algorithms*/ + if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) { + /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/ + has_alpha = true; + if(_lv_area_is_in(&layer_ctx->area_act, &obj->coords, 0)) { + lv_cover_check_info_t info; + info.res = LV_COVER_RES_COVER; + info.area = &layer_ctx->area_act; + lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); + if(info.res == LV_COVER_RES_COVER) has_alpha = false; + } + + if(has_alpha) { + layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_alpha - 1; + } + } + else { + has_alpha = false; + } + + if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2; + lv_draw_layer_adjust(draw_ctx, layer_ctx, has_alpha ? LV_DRAW_LAYER_FLAG_HAS_ALPHA : LV_DRAW_LAYER_FLAG_NONE); +} + + +void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj) +{ + /*Do not refresh hidden objects*/ + if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return; + lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj); + if(layer_type == LV_LAYER_TYPE_NONE) { + lv_obj_redraw(draw_ctx, obj); + } + else { + lv_opa_t opa = lv_obj_get_style_opa(obj, 0); + if(opa < LV_OPA_MIN) return; + + lv_area_t layer_area_full; + lv_res_t res = layer_get_area(draw_ctx, obj, layer_type, &layer_area_full); + if(res != LV_RES_OK) return; + + lv_draw_layer_flags_t flags = LV_DRAW_LAYER_FLAG_HAS_ALPHA; + + if(_lv_area_is_in(&layer_area_full, &obj->coords, 0)) { + lv_cover_check_info_t info; + info.res = LV_COVER_RES_COVER; + info.area = &layer_area_full; + lv_event_send(obj, LV_EVENT_COVER_CHECK, &info); + if(info.res == LV_COVER_RES_COVER) flags &= ~LV_DRAW_LAYER_FLAG_HAS_ALPHA; + } + + if(layer_type == LV_LAYER_TYPE_SIMPLE) flags |= LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE; + + lv_draw_layer_ctx_t * layer_ctx = lv_draw_layer_create(draw_ctx, &layer_area_full, flags); + if(layer_ctx == NULL) { + LV_LOG_WARN("Couldn't create a new layer context"); + return; + } + lv_point_t pivot = { + .x = lv_obj_get_style_transform_pivot_x(obj, 0), + .y = lv_obj_get_style_transform_pivot_y(obj, 0) + }; + + lv_draw_img_dsc_t draw_dsc; + lv_draw_img_dsc_init(&draw_dsc); + draw_dsc.opa = opa; + draw_dsc.angle = lv_obj_get_style_transform_angle(obj, 0); + if(draw_dsc.angle > 3600) draw_dsc.angle -= 3600; + else if(draw_dsc.angle < 0) draw_dsc.angle += 3600; + + draw_dsc.zoom = lv_obj_get_style_transform_zoom(obj, 0); + draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0); + draw_dsc.antialias = disp_refr->driver->antialiasing; + + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_ctx->area_act = layer_ctx->area_full; + layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1; + if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2; + } + + while(layer_ctx->area_act.y1 <= layer_area_full.y2) { + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_alpha_test(obj, draw_ctx, layer_ctx, flags); + } + + lv_obj_redraw(draw_ctx, obj); + + draw_dsc.pivot.x = obj->coords.x1 + pivot.x - draw_ctx->buf_area->x1; + draw_dsc.pivot.y = obj->coords.y1 + pivot.y - draw_ctx->buf_area->y1; + + /*With LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE it should also go the next chunk*/ + lv_draw_layer_blend(draw_ctx, layer_ctx, &draw_dsc); + + if((flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) == 0) break; + + layer_ctx->area_act.y1 = layer_ctx->area_act.y2 + 1; + layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1; + } + + lv_draw_layer_destroy(draw_ctx, layer_ctx); + } +} + + static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h) { int32_t max_row = (uint32_t)disp->driver->draw_buf->size / area_w; @@ -986,6 +1195,18 @@ static void draw_buf_flush(lv_disp_t * disp) while(draw_buf->flushing) { if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver); } + + /*If the screen is transparent initialize it when the flushing is ready*/ +#if LV_COLOR_SCREEN_TRANSP + if(disp_refr->driver->screen_transp) { + if(disp_refr->driver->clear_cb) { + disp_refr->driver->clear_cb(disp_refr->driver, disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size); + } + else { + lv_memset_00(disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size * LV_IMG_PX_SIZE_ALPHA_BYTE); + } + } +#endif } draw_buf->flushing = 1; diff --git a/lib/libesp32_lvgl/lvgl/src/core/lv_refr.h b/lib/libesp32_lvgl/lvgl/src/core/lv_refr.h index 984168a96..72e8d6c39 100644 --- a/lib/libesp32_lvgl/lvgl/src/core/lv_refr.h +++ b/lib/libesp32_lvgl/lvgl/src/core/lv_refr.h @@ -61,7 +61,7 @@ void lv_refr_now(lv_disp_t * disp); * @param draw pointer to an initialized draw context * @param obj the start object from the redraw should start */ -void lv_refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj); +void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj); /** * Invalidate an area on display to redraw it diff --git a/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_draw_arm2d.mk b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_draw_arm2d.mk new file mode 100644 index 000000000..17219b07e --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_draw_arm2d.mk @@ -0,0 +1,6 @@ +CSRCS += lv_gpu_arm2d.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d" diff --git a/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.c b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.c new file mode 100644 index 000000000..7777fe21b --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.c @@ -0,0 +1,1376 @@ +/** + * @file lv_gpu_arm2d.c + * + */ + +/********************* + * INCLUDES + *********************/ +#if defined(__clang__) + #pragma clang diagnostic ignored "-Wunknown-warning-option" + #pragma clang diagnostic ignored "-Wreserved-identifier" + #pragma clang diagnostic ignored "-Wincompatible-pointer-types-discards-qualifiers" + #pragma clang diagnostic ignored "-Wmissing-variable-declarations" + #pragma clang diagnostic ignored "-Wcast-qual" + #pragma clang diagnostic ignored "-Wcast-align" + #pragma clang diagnostic ignored "-Wextra-semi-stmt" + #pragma clang diagnostic ignored "-Wsign-conversion" + #pragma clang diagnostic ignored "-Wunused-function" + #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" + #pragma clang diagnostic ignored "-Wdouble-promotion" + #pragma clang diagnostic ignored "-Wunused-parameter" + #pragma clang diagnostic ignored "-Wimplicit-float-conversion" + #pragma clang diagnostic ignored "-Wimplicit-int-conversion" + #pragma clang diagnostic ignored "-Wtautological-pointer-compare" + #pragma clang diagnostic ignored "-Wsign-compare" + #pragma clang diagnostic ignored "-Wfloat-conversion" + #pragma clang diagnostic ignored "-Wmissing-prototypes" + #pragma clang diagnostic ignored "-Wpadded" + #pragma clang diagnostic ignored "-Wundef" + #pragma clang diagnostic ignored "-Wdeclaration-after-statement" + #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" + #pragma clang diagnostic ignored "-Wunused-variable" + #pragma clang diagnostic ignored "-Wunused-but-set-variable" + #pragma clang diagnostic ignored "-Wint-conversion" +#endif + + +#include "lv_gpu_arm2d.h" +#include "../../core/lv_refr.h" + +#if LV_USE_GPU_ARM2D +#include "arm_2d.h" +#include "__arm_2d_impl.h" + + +#if defined(__IS_COMPILER_ARM_COMPILER_5__) + #pragma diag_suppress 174,177,188,68,513,144,1296 +#elif defined(__IS_COMPILER_IAR__) + #pragma diag_suppress=Pa093 +#elif defined(__IS_COMPILER_GCC__) + #pragma GCC diagnostic ignored "-Wdiscarded-qualifiers" +#endif + +/********************* + * DEFINES + *********************/ +#if ( !defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \ + || !__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \ +&& LV_COLOR_DEPTH == 32 \ +&& !defined(__ARM_2D_LVGL_CFG_NO_WARNING__) +#warning Please set macro __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__ to 1 to get more acceleration opportunities. Or you can define macro __ARM_2D_LVGL_CFG_NO_WARNING__ to suppress this warning. +#endif + +#define MAX_BUF_SIZE (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()) + +#if LV_COLOR_DEPTH == 16 +#define arm_2d_fill_colour arm_2d_rgb16_fill_colour +#define arm_2d_fill_colour_with_alpha arm_2d_rgb565_fill_colour_with_alpha +#define arm_2d_fill_colour_with_mask arm_2d_rgb565_fill_colour_with_mask +#define arm_2d_fill_colour_with_mask_and_opacity \ + arm_2d_rgb565_fill_colour_with_mask_and_opacity +#define arm_2d_tile_copy arm_2d_rgb16_tile_copy +#define arm_2d_alpha_blending arm_2d_rgb565_alpha_blending +#define arm_2d_tile_copy_with_src_mask arm_2d_rgb565_tile_copy_with_src_mask +#define arm_2d_color_t arm_2d_color_rgb565_t + +/* arm-2d direct mode apis */ +#define __arm_2d_impl_colour_filling __arm_2d_impl_rgb16_colour_filling +#define __arm_2d_impl_colour_filling_with_opacity \ + __arm_2d_impl_rgb565_colour_filling_with_opacity +#define __arm_2d_impl_colour_filling_mask \ + __arm_2d_impl_rgb565_colour_filling_mask +#define __arm_2d_impl_colour_filling_mask_opacity \ + __arm_2d_impl_rgb565_colour_filling_mask_opacity +#define __arm_2d_impl_copy __arm_2d_impl_rgb16_copy +#define __arm_2d_impl_alpha_blending __arm_2d_impl_rgb565_alpha_blending +#define __arm_2d_impl_src_msk_copy __arm_2d_impl_rgb565_src_msk_copy +#define __arm_2d_impl_src_chn_msk_copy __arm_2d_impl_rgb565_src_chn_msk_copy +#define __arm_2d_impl_cl_key_copy __arm_2d_impl_rgb16_cl_key_copy +#define __arm_2d_impl_alpha_blending_colour_keying \ + __arm_2d_impl_rgb565_alpha_blending_colour_keying +#define arm_2d_tile_transform_with_src_mask_and_opacity \ + arm_2d_rgb565_tile_transform_with_src_mask_and_opacity +#define arm_2d_tile_transform_with_opacity \ + arm_2d_rgb565_tile_transform_with_opacity + +#define __ARM_2D_PIXEL_BLENDING_OPA __ARM_2D_PIXEL_BLENDING_OPA_RGB565 + +#define color_int uint16_t + +#elif LV_COLOR_DEPTH == 32 +#define arm_2d_fill_colour arm_2d_rgb32_fill_colour +#define arm_2d_fill_colour_with_alpha arm_2d_cccn888_fill_colour_with_alpha +#define arm_2d_fill_colour_with_mask arm_2d_cccn888_fill_colour_with_mask +#define arm_2d_fill_colour_with_mask_and_opacity \ + arm_2d_cccn888_fill_colour_with_mask_and_opacity +#define arm_2d_tile_copy arm_2d_rgb32_tile_copy +#define arm_2d_alpha_blending arm_2d_cccn888_alpha_blending +#define arm_2d_tile_copy_with_src_mask arm_2d_cccn888_tile_copy_with_src_mask +#define arm_2d_color_t arm_2d_color_cccn888_t + +/* arm-2d direct mode apis */ +#define __arm_2d_impl_colour_filling __arm_2d_impl_rgb32_colour_filling +#define __arm_2d_impl_colour_filling_with_opacity \ + __arm_2d_impl_cccn888_colour_filling_with_opacity +#define __arm_2d_impl_colour_filling_mask \ + __arm_2d_impl_cccn888_colour_filling_mask +#define __arm_2d_impl_colour_filling_mask_opacity \ + __arm_2d_impl_cccn888_colour_filling_mask_opacity +#define __arm_2d_impl_copy __arm_2d_impl_rgb32_copy +#define __arm_2d_impl_alpha_blending __arm_2d_impl_cccn888_alpha_blending +#define __arm_2d_impl_src_msk_copy __arm_2d_impl_cccn888_src_msk_copy +#define __arm_2d_impl_src_chn_msk_copy __arm_2d_impl_cccn888_src_chn_msk_copy +#define __arm_2d_impl_cl_key_copy __arm_2d_impl_rgb32_cl_key_copy +#define __arm_2d_impl_alpha_blending_colour_keying \ + __arm_2d_impl_cccn888_alpha_blending_colour_keying +#define arm_2d_tile_transform_with_src_mask_and_opacity \ + arm_2d_cccn888_tile_transform_with_src_mask_and_opacity +#define arm_2d_tile_transform_with_opacity \ + arm_2d_cccn888_tile_transform_with_opacity + +#define __ARM_2D_PIXEL_BLENDING_OPA __ARM_2D_PIXEL_BLENDING_OPA_CCCN888 + +#define color_int uint32_t + +#else +#error The specified LV_COLOR_DEPTH is not supported by this version of lv_gpu_arm2d.c. +#endif + +/* *INDENT-OFF* */ +#define __PREPARE_LL_ACCELERATION__() \ + int32_t src_stride = lv_area_get_width(coords); \ + \ + uint8_t px_size_byte = cf == LV_IMG_CF_TRUE_COLOR_ALPHA \ + ? LV_IMG_PX_SIZE_ALPHA_BYTE \ + : sizeof(lv_color_t); \ + \ + const uint8_t * src_buf_tmp = src_buf; \ + src_buf_tmp += src_stride \ + * (draw_area.y1 - coords->y1) \ + * px_size_byte; \ + src_buf_tmp += (draw_area.x1 - coords->x1) * px_size_byte; \ + \ + lv_area_t blend_area2; \ + if(!_lv_area_intersect(&blend_area2, \ + &draw_area, \ + draw_ctx->clip_area)) return; \ + \ + int32_t w = lv_area_get_width(&blend_area2); \ + int32_t h = lv_area_get_height(&blend_area2); \ + \ + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); \ + \ + lv_color_t * dest_buf = draw_ctx->buf; \ + dest_buf += dest_stride * (blend_area2.y1 - draw_ctx->buf_area->y1) \ + + (blend_area2.x1 - draw_ctx->buf_area->x1); \ + \ + arm_2d_size_t copy_size = { \ + .iWidth = lv_area_get_width(&blend_area2), \ + .iHeight = lv_area_get_height(&blend_area2), \ + } + +#define __PREPARE_TARGET_TILE__(__blend_area) \ + static arm_2d_tile_t target_tile; \ + static arm_2d_region_t target_region; \ + \ + lv_color_t * dest_buf = draw_ctx->buf; \ + \ + target_tile = (arm_2d_tile_t) { \ + .tRegion = { \ + .tSize = { \ + .iWidth = lv_area_get_width(draw_ctx->buf_area), \ + .iHeight = lv_area_get_height(draw_ctx->buf_area), \ + }, \ + }, \ + .tInfo.bIsRoot = true, \ + .phwBuffer = (uint16_t *)draw_ctx->buf, \ + }; \ + \ + target_region = (arm_2d_region_t) { \ + .tLocation = { \ + .iX = (__blend_area).x1 - draw_ctx->buf_area->x1, \ + .iY = (__blend_area).y1 - draw_ctx->buf_area->y1, \ + }, \ + .tSize = { \ + .iWidth = lv_area_get_width(&(__blend_area)), \ + .iHeight = lv_area_get_height(&(__blend_area)), \ + }, \ + } + +#define __PREPARE_SOURCE_TILE__(__dsc, __blend_area) \ + static arm_2d_tile_t source_tile_orig; \ + static arm_2d_tile_t source_tile; \ + const lv_color_t * src_buf = (__dsc)->src_buf; \ + if (src_buf) { \ + source_tile_orig = (arm_2d_tile_t) { \ + .tRegion = { \ + .tSize = { \ + .iWidth = lv_area_get_width((__dsc)->blend_area), \ + .iHeight = lv_area_get_height((__dsc)->blend_area), \ + }, \ + }, \ + .tInfo.bIsRoot = true, \ + .phwBuffer = (uint16_t *)src_buf, \ + }; \ + \ + arm_2d_tile_generate_child( \ + &source_tile_orig, \ + (arm_2d_region_t []) { \ + { \ + .tLocation = { \ + .iX = (__blend_area).x1 - (__dsc)->blend_area->x1, \ + .iY = (__blend_area).y1 - (__dsc)->blend_area->y1, \ + }, \ + .tSize = source_tile_orig.tRegion.tSize, \ + } \ + }, \ + &source_tile, \ + false); \ + source_tile.tInfo.bDerivedResource = true; \ + } + +#define __PREPARE_MASK_TILE__(__dsc, __blend_area, __mask, __is_chn) \ + static arm_2d_tile_t mask_tile_orig; \ + static arm_2d_tile_t mask_tile; \ + if(NULL != (__mask)) { \ + mask_tile_orig = (arm_2d_tile_t) { \ + .tRegion = { \ + .tSize = { \ + .iWidth = lv_area_get_width((__dsc)->mask_area), \ + .iHeight = lv_area_get_height((__dsc)->mask_area), \ + }, \ + }, \ + .tInfo = { \ + .bIsRoot = true, \ + .bHasEnforcedColour = true, \ + .tColourInfo = { \ + .chScheme = (__is_chn) ? ARM_2D_CHANNEL_8in32 \ + : ARM_2D_COLOUR_8BIT, \ + }, \ + }, \ + .pchBuffer = ((uint8_t *)(__mask)) + (__is_chn) ? 3 : 0, \ + }; \ + \ + arm_2d_tile_generate_child( \ + &mask_tile_orig, \ + (arm_2d_region_t []) { \ + { \ + .tLocation = { \ + .iX = (__dsc)->mask_area->x1 - (__blend_area).x1, \ + .iY = (__dsc)->mask_area->y1 - (__blend_area).y1, \ + }, \ + .tSize = mask_tile_orig.tRegion.tSize, \ + } \ + }, \ + &mask_tile, \ + false); \ + mask_tile.tInfo.bDerivedResource = true; \ + } +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +#define __RECOLOUR_WRAPPER(...) \ + do { \ + lv_color_t *rgb_tmp_buf = NULL; \ + if(draw_dsc->recolor_opa > LV_OPA_MIN) { \ + rgb_tmp_buf \ + = lv_mem_buf_get(src_w * src_h * sizeof(lv_color_t)); \ + if (NULL == rgb_tmp_buf) { \ + LV_LOG_WARN( \ + "Failed to allocate memory for accelerating recolour, " \ + "use normal route instead."); \ + break; \ + } \ + lv_memcpy(rgb_tmp_buf, src_buf, src_w * src_h * sizeof(lv_color_t));\ + arm_2d_size_t copy_size = { \ + .iWidth = src_w, \ + .iHeight = src_h, \ + }; \ + /* apply re-colour */ \ + __arm_2d_impl_colour_filling_with_opacity( \ + (color_int *)rgb_tmp_buf, \ + src_w, \ + ©_size, \ + (color_int)draw_dsc->recolor.full, \ + draw_dsc->recolor_opa); \ + \ + /* replace src_buf for the following operation */ \ + src_buf = (const uint8_t *)rgb_tmp_buf; \ + } \ + __VA_ARGS__ \ + if (NULL != rgb_tmp_buf) { \ + lv_mem_buf_release(rgb_tmp_buf); \ + } \ + } while(0); +/* *INDENT-ON* */ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +#if __ARM_2D_HAS_HW_ACC__ +LV_ATTRIBUTE_FAST_MEM +static bool lv_draw_arm2d_fill_colour(const arm_2d_tile_t * target_tile, + const arm_2d_region_t * region, + lv_color_t color, + lv_opa_t opa, + const arm_2d_tile_t * mask_tile); + +LV_ATTRIBUTE_FAST_MEM +static bool lv_draw_arm2d_tile_copy(const arm_2d_tile_t * target_tile, + const arm_2d_region_t * region, + arm_2d_tile_t * source_tile, + lv_opa_t opa, + arm_2d_tile_t * mask_tile); +#else + +static void convert_cb(const lv_area_t * dest_area, + const void * src_buf, + lv_coord_t src_w, + lv_coord_t src_h, + lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, + lv_img_cf_t cf, + lv_color_t * cbuf, + lv_opa_t * abuf); + +LV_ATTRIBUTE_FAST_MEM +static bool arm_2d_fill_normal(lv_color_t * dest_buf, + const lv_area_t * dest_area, + lv_coord_t dest_stride, + lv_color_t color, + lv_opa_t opa, + const lv_opa_t * mask, + lv_coord_t mask_stride); + +LV_ATTRIBUTE_FAST_MEM +static bool arm_2d_copy_normal(lv_color_t * dest_buf, + const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, + lv_coord_t src_stride, + lv_opa_t opa, + const lv_opa_t * mask, + lv_coord_t mask_stride); +#endif + +LV_ATTRIBUTE_FAST_MEM +static void lv_draw_arm2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); +LV_ATTRIBUTE_FAST_MEM +static void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx); +LV_ATTRIBUTE_FAST_MEM +static void lv_draw_arm2d_img_decoded(struct _lv_draw_ctx_t * draw_ctx, + const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, + const uint8_t * src_buf, + lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_arm2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + arm_2d_init(); + + lv_draw_sw_init_ctx(drv, draw_ctx); + + lv_draw_arm2d_ctx_t * arm2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx; + + arm2d_draw_ctx->blend = lv_draw_arm2d_blend; + arm2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_arm2d_wait_cb; + +#if !__ARM_2D_HAS_HW_ACC__ + arm2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_arm2d_img_decoded; +#endif + +} + +void lv_draw_arm2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(drv); + LV_UNUSED(draw_ctx); +} + +extern void test_flush(lv_color_t * color_p); + +#if __ARM_2D_HAS_HW_ACC__ +LV_ATTRIBUTE_FAST_MEM +static void lv_draw_arm2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + const lv_opa_t * mask; + if(dsc->mask_buf == NULL) mask = NULL; + if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return; + else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; + else mask = dsc->mask_buf; + + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) { + return; + } + + bool is_accelerated = false; + + if(dsc->blend_mode == LV_BLEND_MODE_NORMAL + && lv_area_get_size(&blend_area) > 100) { + + __PREPARE_TARGET_TILE__(blend_area); + __PREPARE_SOURCE_TILE__(dsc, blend_area); + __PREPARE_MASK_TILE__(dsc, blend_area, mask, false); + + if(src_buf) { + is_accelerated = lv_draw_arm2d_tile_copy( + &target_tile, + &target_region, + &source_tile, + dsc->opa, + (NULL == mask) ? NULL : &mask_tile); + } + else { + is_accelerated = lv_draw_arm2d_fill_colour( + &target_tile, + &target_region, + dsc->color, + dsc->opa, + (NULL == mask) ? NULL : &mask_tile); + } + } + + if(!is_accelerated) { + lv_draw_sw_blend_basic(draw_ctx, dsc); + } +} + + +LV_ATTRIBUTE_FAST_MEM +static bool lv_draw_arm2d_fill_colour(const arm_2d_tile_t * target_tile, + const arm_2d_region_t * region, + lv_color_t color, + lv_opa_t opa, + const arm_2d_tile_t * mask_tile) +{ + arm_fsm_rt_t result = (arm_fsm_rt_t)ARM_2D_ERR_NONE; + + if(NULL == mask_tile) { + if(opa >= LV_OPA_MAX) { + result = arm_2d_fill_colour(target_tile, region, color.full); + } + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + result = arm_2d_fill_colour_with_alpha( + target_tile, + region, + (arm_2d_color_t) { + color.full + }, + opa); +#endif + } + } + else { + + if(opa >= LV_OPA_MAX) { + result = arm_2d_fill_colour_with_mask( + target_tile, + region, + mask_tile, + (arm_2d_color_t) { + color.full + }); + } + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + result = arm_2d_fill_colour_with_mask_and_opacity( + target_tile, + region, + mask_tile, + (arm_2d_color_t) { + color.full + }, + opa); +#endif + } + } + + if(result < 0) { + /* error detected */ + return false; + } + + return true; + +} + +LV_ATTRIBUTE_FAST_MEM +static bool lv_draw_arm2d_tile_copy(const arm_2d_tile_t * target_tile, + const arm_2d_region_t * region, + arm_2d_tile_t * source_tile, + lv_opa_t opa, + arm_2d_tile_t * mask_tile) +{ + arm_fsm_rt_t result = (arm_fsm_rt_t)ARM_2D_ERR_NONE; + + if(NULL == mask_tile) { + if(opa >= LV_OPA_MAX) { + result = arm_2d_tile_copy(source_tile, + target_tile, + region, + ARM_2D_CP_MODE_COPY); + } +#if LV_COLOR_SCREEN_TRANSP + else { + return false; /* not supported */ + } +#else + else { + result = arm_2d_alpha_blending(source_tile, + target_tile, + region, + opa); + } +#endif + } + else { +#if LV_COLOR_SCREEN_TRANSP + return false; /* not support */ +#else + + if(opa >= LV_OPA_MAX) { + result = arm_2d_tile_copy_with_src_mask(source_tile, + mask_tile, + target_tile, + region, + ARM_2D_CP_MODE_COPY); + } + else { + return false; + } +#endif + } + + if(result < 0) { + /* error detected */ + return false; + } + + return true; +} + +static void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + arm_2d_op_wait_async(NULL); + if(disp->driver && disp->driver->wait_cb) { + disp->driver->wait_cb(disp->driver); + } + lv_draw_sw_wait_for_finish(draw_ctx); +} +#else + + +LV_ATTRIBUTE_FAST_MEM +static void lv_draw_arm2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + const lv_opa_t * mask; + if(dsc->mask_buf == NULL) mask = NULL; + if(dsc->mask_buf && dsc->mask_res == LV_DRAW_MASK_RES_TRANSP) return; + else if(dsc->mask_res == LV_DRAW_MASK_RES_FULL_COVER) mask = NULL; + else mask = dsc->mask_buf; + + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); + + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return; + + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + bool is_accelerated = false; + do { + if(NULL != disp->driver->set_px_cb) { + break; + } + + lv_color_t * dest_buf = draw_ctx->buf; + dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + + (blend_area.x1 - draw_ctx->buf_area->x1); + + const lv_color_t * src_buf = dsc->src_buf; + lv_coord_t src_stride; + if(src_buf) { + src_stride = lv_area_get_width(dsc->blend_area); + src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1); + } + else { + src_stride = 0; + } + + lv_coord_t mask_stride; + if(mask) { + mask_stride = lv_area_get_width(dsc->mask_area); + mask += mask_stride * (blend_area.y1 - dsc->mask_area->y1) + (blend_area.x1 - dsc->mask_area->x1); + } + else { + mask_stride = 0; + } + + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + + if(dsc->src_buf == NULL) { + if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { + is_accelerated = arm_2d_fill_normal(dest_buf, + &blend_area, + dest_stride, + dsc->color, + dsc->opa, + mask, + mask_stride); + } +#if LV_DRAW_COMPLEX + else { + break; + } +#endif + } + else { + + if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { + is_accelerated = arm_2d_copy_normal(dest_buf, + &blend_area, + dest_stride, + src_buf, + src_stride, + dsc->opa, + mask, + mask_stride); + } +#if LV_DRAW_COMPLEX + else { + break; + } +#endif + } + } while(0); + + if(!is_accelerated) lv_draw_sw_blend_basic(draw_ctx, dsc); +} + +LV_ATTRIBUTE_FAST_MEM +static bool arm_2d_fill_normal(lv_color_t * dest_buf, + const lv_area_t * dest_area, + lv_coord_t dest_stride, + lv_color_t color, + lv_opa_t opa, + const lv_opa_t * mask, + lv_coord_t mask_stride) +{ + arm_2d_size_t target_size = { + .iWidth = lv_area_get_width(dest_area), + .iHeight = lv_area_get_height(dest_area), + }; + + /*No mask*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + __arm_2d_impl_colour_filling((color_int *)dest_buf, + dest_stride, + &target_size, + color.full); + } + /*Has opacity*/ + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + __arm_2d_impl_colour_filling_with_opacity((color_int *)dest_buf, + dest_stride, + &target_size, + color.full, + opa); +#endif + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa >= LV_OPA_MAX) { + __arm_2d_impl_colour_filling_mask((color_int *)dest_buf, + dest_stride, + (uint8_t *)mask, + mask_stride, + &target_size, + color.full); + } + /*With opacity*/ + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + __arm_2d_impl_colour_filling_mask_opacity((color_int *)dest_buf, + dest_stride, + (uint8_t *)mask, + mask_stride, + &target_size, + color.full, + opa); +#endif + } + } + + return true; +} + + +LV_ATTRIBUTE_FAST_MEM +static bool arm_2d_copy_normal(lv_color_t * dest_buf, + const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, + lv_coord_t src_stride, + lv_opa_t opa, + const lv_opa_t * mask, + lv_coord_t mask_stride) + +{ + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + arm_2d_size_t copy_size = { + .iWidth = lv_area_get_width(dest_area), + .iHeight = lv_area_get_height(dest_area), + }; + +#if LV_COLOR_SCREEN_TRANSP + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); +#endif + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + __arm_2d_impl_copy((color_int *)src_buf, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size); + } + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + __arm_2d_impl_alpha_blending((color_int *)src_buf, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size, + opa); +#endif + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa > LV_OPA_MAX) { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + __arm_2d_impl_src_msk_copy((color_int *)src_buf, + src_stride, + (uint8_t *)mask, + mask_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_size); +#endif + } + /*Handle opa and mask values too*/ + else { +#if LV_COLOR_SCREEN_TRANSP + return false; +#else + __arm_2d_impl_gray8_alpha_blending((uint8_t *)mask, + mask_stride, + (uint8_t *)mask, + mask_stride, + ©_size, + opa); + + __arm_2d_impl_src_msk_copy((color_int *)src_buf, + src_stride, + (uint8_t *)mask, + mask_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_size); +#endif + } + } + + return true; +} + +LV_ATTRIBUTE_FAST_MEM +static void lv_draw_arm2d_img_decoded(struct _lv_draw_ctx_t * draw_ctx, + const lv_draw_img_dsc_t * draw_dsc, + const lv_area_t * coords, + const uint8_t * src_buf, + lv_img_cf_t cf) +{ + /*Use the clip area as draw area*/ + lv_area_t draw_area; + lv_area_copy(&draw_area, draw_ctx->clip_area); + + bool mask_any = lv_draw_mask_is_any(&draw_area); + bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; + + lv_area_t blend_area; + lv_draw_sw_blend_dsc_t blend_dsc; + + lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); + blend_dsc.opa = draw_dsc->opa; + blend_dsc.blend_mode = draw_dsc->blend_mode; + blend_dsc.blend_area = &blend_area; + + /*The simplest case just copy the pixels into the draw_buf*/ + if(!mask_any && !transform && cf == LV_IMG_CF_TRUE_COLOR && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + blend_dsc.src_buf = (const lv_color_t *)src_buf; + + blend_dsc.blend_area = coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } + else if(!mask_any && !transform && cf == LV_IMG_CF_ALPHA_8BIT) { + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_area = coords; + blend_dsc.src_buf = NULL; + blend_dsc.color = draw_dsc->recolor; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + + blend_dsc.blend_area = coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#if LV_COLOR_DEPTH == 16 + else if(!mask_any && !transform && cf == LV_IMG_CF_RGB565A8 && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + blend_dsc.src_buf = (const lv_color_t *)src_buf; + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_buf += sizeof(lv_color_t) * src_w * src_h; + blend_dsc.blend_area = coords; + blend_dsc.mask_area = coords; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#endif + /*In the other cases every pixel need to be checked one-by-one*/ + else { + blend_area.x1 = draw_ctx->clip_area->x1; + blend_area.x2 = draw_ctx->clip_area->x2; + blend_area.y1 = draw_ctx->clip_area->y1; + blend_area.y2 = draw_ctx->clip_area->y2; + + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + lv_coord_t blend_h = lv_area_get_height(&blend_area); + lv_coord_t blend_w = lv_area_get_width(&blend_area); + + uint32_t max_buf_size = MAX_BUF_SIZE; + uint32_t blend_size = lv_area_get_size(&blend_area); + uint32_t buf_h; + uint32_t buf_w = blend_w; + if(blend_size <= max_buf_size) { + buf_h = blend_h; + } + else { + /*Round to full lines*/ + buf_h = max_buf_size / blend_w; + } + + /*Create buffers and masks*/ + uint32_t buf_size = buf_w * buf_h; + + lv_color_t * rgb_buf = lv_mem_buf_get(buf_size * sizeof(lv_color_t)); + lv_opa_t * mask_buf = lv_mem_buf_get(buf_size); + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + blend_dsc.src_buf = rgb_buf; + lv_coord_t y_last = blend_area.y2; + blend_area.y2 = blend_area.y1 + buf_h - 1; + + lv_draw_mask_res_t mask_res_def = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle || + draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? + LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; + blend_dsc.mask_res = mask_res_def; + + bool is_accelerated = false; + + if(!transform) { + if(LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf) { + /* copy with colour keying */ + + /* *INDENT-OFF* */ + __RECOLOUR_WRAPPER( + + lv_color_t chrome_key = LV_COLOR_CHROMA_KEY; + /* calculate new chrome-key colour */ + if(draw_dsc->recolor_opa > LV_OPA_MIN) { + __ARM_2D_PIXEL_BLENDING_OPA( + (color_int *) & (draw_dsc->recolor.full), + (color_int *) & (chrome_key.full), + draw_dsc->recolor_opa + ); + } + + __PREPARE_LL_ACCELERATION__(); + + if(blend_dsc.opa >= LV_OPA_MAX) { + __arm_2d_impl_cl_key_copy( + (color_int *)src_buf_tmp, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size, + (color_int)chrome_key.full); + } + else { + __arm_2d_impl_alpha_blending_colour_keying( + (color_int *)src_buf_tmp, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size, + blend_dsc.opa, + (color_int)chrome_key.full); + } + is_accelerated = true; + ) + /* *INDENT-ON* */ + } + else if((LV_COLOR_DEPTH == 32) + && !mask_any + && (cf == LV_IMG_CF_TRUE_COLOR_ALPHA)) { + /* accelerate copy-with-source-masks-and-opacity */ + + /* *INDENT-OFF* */ + __RECOLOUR_WRAPPER( + __PREPARE_LL_ACCELERATION__(); + + uint8_t * mask_temp_buf = NULL; + if(blend_dsc.opa < LV_OPA_MAX) { + mask_temp_buf = lv_mem_buf_get(copy_size.iHeight * copy_size.iWidth); + if(NULL == mask_temp_buf) { + LV_LOG_WARN( + "Failed to allocate memory for alpha mask," + " use normal route instead."); + break; + } + lv_memset_00(mask_temp_buf, copy_size.iHeight * copy_size.iWidth); + + __arm_2d_impl_gray8_colour_filling_channel_mask_opacity( + mask_temp_buf, + src_stride, + (uint32_t *) + ((uintptr_t)src_buf_tmp + LV_IMG_PX_SIZE_ALPHA_BYTE - 1), + src_stride, + ©_size, + 0xFF, + blend_dsc.opa); + + __arm_2d_impl_src_msk_copy( + (color_int *)src_buf_tmp, + src_stride, + mask_temp_buf, + src_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_size); + + lv_mem_buf_release(mask_temp_buf); + } + else { + __arm_2d_impl_src_chn_msk_copy( + (color_int *)src_buf_tmp, + src_stride, + (uint32_t *) + ((uintptr_t)src_buf_tmp + LV_IMG_PX_SIZE_ALPHA_BYTE - 1), + src_stride, + ©_size, + (color_int *)dest_buf, + dest_stride, + ©_size); + } + + is_accelerated = true; + ) + /* *INDENT-ON* */ + } + else if(!mask_any && (cf == LV_IMG_CF_TRUE_COLOR)) { + /* accelerate copy-with-source-masks-and-opacity */ + + /* *INDENT-OFF* */ + __RECOLOUR_WRAPPER( + __PREPARE_LL_ACCELERATION__(); + + if(blend_dsc.opa >= LV_OPA_MAX) { + __arm_2d_impl_copy( + (color_int *)src_buf_tmp, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size); + } + else { + __arm_2d_impl_alpha_blending( + (color_int *)src_buf_tmp, + src_stride, + (color_int *)dest_buf, + dest_stride, + ©_size, + blend_dsc.opa); + } + is_accelerated = true; + ) + /* *INDENT-ON* */ + } + } + else if(!mask_any +#if defined(__ARM_2D_HAS_ANTI_ALIAS_TRANSFORM__) && __ARM_2D_HAS_ANTI_ALIAS_TRANSFORM__ + && (draw_dsc->antialias == 1) +#else + && (draw_dsc->antialias == 0) +#endif + && (draw_dsc->recolor_opa == LV_OPA_TRANSP) + && (((LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf) + || (LV_IMG_CF_TRUE_COLOR == cf)) +#if defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) && __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__ + || ((LV_IMG_CF_TRUE_COLOR_ALPHA == cf) + && (LV_COLOR_DEPTH == 32)) +#endif + ) + ) { + + /* *INDENT-OFF* */ + __RECOLOUR_WRAPPER( + /* accelerate transform without re-color */ + + static arm_2d_tile_t target_tile_origin; + static arm_2d_tile_t target_tile; + arm_2d_region_t clip_region; + static arm_2d_region_t target_region; + + lv_color_t * dest_buf = draw_ctx->buf; + + target_tile_origin = (arm_2d_tile_t) { + .tRegion = { + .tSize = { + .iWidth = lv_area_get_width(draw_ctx->buf_area), + .iHeight = lv_area_get_height(draw_ctx->buf_area), + }, + }, + .tInfo.bIsRoot = true, + .phwBuffer = (uint16_t *)draw_ctx->buf, + }; + + clip_region = (arm_2d_region_t) { + .tLocation = { + .iX = draw_ctx->clip_area->x1 - draw_ctx->buf_area->x1, + .iY = draw_ctx->clip_area->y1 - draw_ctx->buf_area->y1, + }, + .tSize = { + .iWidth = lv_area_get_width(draw_ctx->clip_area), + .iHeight = lv_area_get_height(draw_ctx->clip_area), + }, + }; + + arm_2d_tile_generate_child(&target_tile_origin, + &clip_region, + &target_tile, + false); + + target_region = (arm_2d_region_t) { + .tLocation = { + .iX = coords->x1 - draw_ctx->clip_area->x1, + .iY = coords->y1 - draw_ctx->clip_area->y1, + }, + .tSize = { + .iWidth = lv_area_get_width(coords), + .iHeight = lv_area_get_height(coords), + }, + }; + + static arm_2d_tile_t source_tile; + + source_tile = (arm_2d_tile_t) { + .tRegion = { + .tSize = { + .iWidth = src_w, + .iHeight = src_h, + }, + }, + .tInfo.bIsRoot = true, + .pchBuffer = (uint8_t *)src_buf, + }; + + static arm_2d_tile_t mask_tile; + mask_tile = source_tile; + + mask_tile.tInfo.bHasEnforcedColour = true; + mask_tile.tInfo.tColourInfo.chScheme = ARM_2D_CHANNEL_8in32; + mask_tile.pchBuffer += 3; + + static arm_2d_location_t source_center, target_center; + source_center.iX = draw_dsc->pivot.x; + source_center.iY = draw_dsc->pivot.y; + + + if((LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED == cf) || + (LV_IMG_CF_TRUE_COLOR == cf)) { + arm_2d_tile_transform_with_opacity( + &source_tile, + &target_tile, + &target_region, + source_center, + ARM_2D_ANGLE((draw_dsc->angle / 10.0f)), + draw_dsc->zoom / 256.0f, + (color_int)LV_COLOR_CHROMA_KEY.full, + blend_dsc.opa); + + is_accelerated = true; + } + #if defined(__ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__) \ + && __ARM_2D_CFG_SUPPORT_COLOUR_CHANNEL_ACCESS__ + else if((LV_IMG_CF_TRUE_COLOR_ALPHA == cf) && + (LV_COLOR_DEPTH == 32)) { + arm_2d_tile_transform_with_src_mask_and_opacity( + &source_tile, + &mask_tile, + &target_tile, + &target_region, + source_center, + ARM_2D_ANGLE((draw_dsc->angle / 10.0f)), + draw_dsc->zoom / 256.0f, + blend_dsc.opa); + + is_accelerated = true; + } + #endif + ) + /* *INDENT-ON* */ + } + + /* *INDENT-OFF* */ + if(!is_accelerated) while(blend_area.y1 <= y_last) { + /*Apply transformations if any or separate the channels*/ + lv_area_t transform_area; + lv_area_copy(&transform_area, &blend_area); + lv_area_move(&transform_area, -coords->x1, -coords->y1); + if(transform) { + lv_draw_transform(draw_ctx, &transform_area, src_buf, src_w, src_h, src_w, + draw_dsc, cf, rgb_buf, mask_buf); + } + else { + convert_cb(&transform_area, src_buf, src_w, src_h, src_w, draw_dsc, cf, rgb_buf, mask_buf); + } + + /*Apply recolor*/ + if(draw_dsc->recolor_opa > LV_OPA_MIN) { + arm_2d_size_t copy_size = { + .iWidth = buf_w, + .iHeight = buf_h, + }; + + /* apply re-colour */ + __arm_2d_impl_colour_filling_with_opacity( + (color_int *)rgb_buf, + buf_w, + ©_size, + (color_int)draw_dsc->recolor.full, + draw_dsc->recolor_opa); + } +#if LV_DRAW_COMPLEX + /*Apply the masks if any*/ + if(mask_any) { + lv_coord_t y; + lv_opa_t * mask_buf_tmp = mask_buf; + for(y = blend_area.y1; y <= blend_area.y2; y++) { + lv_draw_mask_res_t mask_res_line; + mask_res_line = lv_draw_mask_apply(mask_buf_tmp, blend_area.x1, y, blend_w); + + if(mask_res_line == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf_tmp, blend_w); + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + else if(mask_res_line == LV_DRAW_MASK_RES_CHANGED) { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + } + mask_buf_tmp += blend_w; + } + } +#endif + + /*Blend*/ + lv_draw_sw_blend(draw_ctx, &blend_dsc); + + /*Go the the next lines*/ + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1 + buf_h - 1; + if(blend_area.y2 > y_last) blend_area.y2 = y_last; + } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(rgb_buf); + } +} + +static void lv_gpu_arm2d_wait_cb(lv_draw_ctx_t * draw_ctx) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + arm_2d_op_wait_async(NULL); + if(disp->driver && disp->driver->wait_cb) { + disp->driver->wait_cb(disp->driver); + } + lv_draw_sw_wait_for_finish(draw_ctx); +} + + +#endif + + +/********************** + * STATIC FUNCTIONS + **********************/ +/* Separate the image channels to RGB and Alpha to match LV_COLOR_DEPTH settings*/ +static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_dsc); + LV_UNUSED(src_h); + LV_UNUSED(src_w); + + const uint8_t * src_tmp8 = (const uint8_t *)src_buf; + lv_coord_t y; + lv_coord_t x; + + if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t px_cnt = lv_area_get_size(dest_area); + lv_memset_ff(abuf, px_cnt); + + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + uint32_t dest_w = lv_area_get_width(dest_area); + uint32_t dest_w_byte = dest_w * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + lv_color_t * cbuf_tmp = cbuf; + for(y = dest_area->y1; y <= dest_area->y2; y++) { + lv_memcpy(cbuf_tmp, src_tmp8, dest_w_byte); + src_tmp8 += src_stride_byte; + cbuf_tmp += dest_w; + } + + /*Make "holes" for with Chroma keying*/ + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t i; + lv_color_t chk = LV_COLOR_CHROMA_KEY; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + uint8_t * cbuf_uint = (uint8_t *)cbuf; + uint8_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 16 + uint16_t * cbuf_uint = (uint16_t *)cbuf; + uint16_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 32 + uint32_t * cbuf_uint = (uint32_t *)cbuf; + uint32_t chk_v = chk.full; +#endif + for(i = 0; i < px_cnt; i++) { + if(chk_v == cbuf_uint[i]) abuf[i] = 0x00; + } + } + } + else if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + src_tmp8 += (src_stride * dest_area->y1 * LV_IMG_PX_SIZE_ALPHA_BYTE) + dest_area->x1 * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t src_new_line_step_px = (src_stride - lv_area_get_width(dest_area)); + lv_coord_t src_new_line_step_byte = src_new_line_step_px * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + for(x = 0; x < dest_w; x++) { + abuf[x] = src_tmp8[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + cbuf[x].full = *src_tmp8; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = *src_tmp8 + ((*(src_tmp8 + 1)) << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x] = *((lv_color_t *) src_tmp8); + cbuf[x].ch.alpha = 0xff; +#endif + src_tmp8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + + } + cbuf += dest_w; + abuf += dest_w; + src_tmp8 += src_new_line_step_byte; + } + } + else if(cf == LV_IMG_CF_RGB565A8) { + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + lv_memcpy(cbuf, src_tmp8, dest_w * sizeof(lv_color_t)); + cbuf += dest_w; + src_tmp8 += src_stride_byte; + } + + src_tmp8 = (const uint8_t *)src_buf; + src_tmp8 += sizeof(lv_color_t) * src_w * src_h; + src_tmp8 += src_stride * dest_area->y1 + dest_area->x1; + for(y = 0; y < dest_h; y++) { + lv_memcpy(abuf, src_tmp8, dest_w); + abuf += dest_w; + src_tmp8 += src_stride; + } + } +} + +#if 0 +static void invalidate_cache(void) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + if(disp->driver->clean_dcache_cb) disp->driver->clean_dcache_cb(disp->driver); + else { +#if __CORTEX_M >= 0x07 + if((SCB->CCR) & (uint32_t)SCB_CCR_DC_Msk) + SCB_CleanInvalidateDCache(); +#endif + } +} +#endif + +#endif diff --git a/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.h b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.h new file mode 100644 index 000000000..50fa5a891 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/arm2d/lv_gpu_arm2d.h @@ -0,0 +1,51 @@ +/** + * @file lv_gpu_arm2d.h + * + */ + +#ifndef LV_GPU_ARM2D_H +#define LV_GPU_ARM2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_color.h" +#include "../../hal/lv_hal_disp.h" +#include "../sw/lv_draw_sw.h" + +#if LV_USE_GPU_ARM2D + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef lv_draw_sw_ctx_t lv_draw_arm2d_ctx_t; + +struct _lv_disp_drv_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_arm2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_arm2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_ARM2D*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_ARM2D_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.c index 116e3b231..823f70768 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.c @@ -39,12 +39,12 @@ void lv_draw_init(void) { - // backend_head = NULL; - // lv_draw_sw_init(); - // - //#if LV_USE_GPU_STM32_DMA2D == 0 - // lv_gpu_stm32_dma2d_init(); - //#endif + /*Nothing to init now*/ +} + +void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx) +{ + if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx); } /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.h index 8e3c6cb94..80b62e9f0 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.h @@ -27,6 +27,8 @@ extern "C" { #include "lv_draw_triangle.h" #include "lv_draw_arc.h" #include "lv_draw_mask.h" +#include "lv_draw_transform.h" +#include "lv_draw_layer.h" /********************* * DEFINES @@ -40,6 +42,19 @@ typedef struct { void * user_data; } lv_draw_mask_t; +typedef struct _lv_draw_layer_ctx_t { + lv_area_t area_full; + lv_area_t area_act; + lv_coord_t max_row_with_alpha; + lv_coord_t max_row_with_no_alpha; + void * buf; + struct { + const lv_area_t * clip_area; + lv_area_t * buf_area; + void * buf; + bool screen_transp; + } original; +} lv_draw_layer_ctx_t; typedef struct _lv_draw_ctx_t { /** @@ -80,6 +95,24 @@ typedef struct _lv_draw_ctx_t { void (*draw_polygon)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points, uint16_t point_cnt); + + /** + * Get an area of a transformed image (zoomed and/or rotated) + * @param draw_ctx pointer to a draw context + * @param dest_area get this area of the result image. It assumes that the original image is placed to the 0;0 position. + * @param src_buf the source image + * @param src_w width of the source image in [px] + * @param src_h height of the source image in [px] + * @param src_stride the stride in [px]. + * @param draw_dsc an `lv_draw_img_dsc_t` descriptor containing the transformation parameters + * @param cf the color format of `src_buf` + * @param cbuf place the colors of the pixels on `dest_area` here in RGB format + * @param abuf place the opacity of the pixels on `dest_area` here + */ + void (*draw_transform)(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); + /** * Replace the buffer with a rect without decoration like radius or borders */ @@ -88,7 +121,68 @@ typedef struct _lv_draw_ctx_t { /** * Wait until all background operations are finished. (E.g. GPU operations) */ - void (*wait_for_finish)(struct _lv_draw_ctx_t * draw); + void (*wait_for_finish)(struct _lv_draw_ctx_t * draw_ctx); + + /** + * Copy an area from buffer to an other + * @param draw_ctx pointer to a draw context + * @param dest_buf copy the buffer into this buffer + * @param dest_stride the width of the dest_buf in pixels + * @param dest_area the destination area + * @param src_buf copy from this buffer + * @param src_stride the width of src_buf in pixels + * @param src_area the source area. + * + * @note dest_area and src_area must have the same width and height + * but can have different x and y position. + * @note dest_area and src_area must be clipped to the real dimensions of the buffers + */ + void (*buffer_copy)(struct _lv_draw_ctx_t * draw_ctx, void * dest_buf, lv_coord_t dest_stride, + const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); + + /** + * Initialize a new layer context. + * The original buffer and area data are already saved from `draw_ctx` to `layer_ctx` + * @param draw_ctx pointer to the current draw context + * @param layer_area the coordinates of the layer + * @param flags OR-ed flags from @lv_draw_layer_flags_t + * @return pointer to the layer context, or NULL on error + */ + struct _lv_draw_layer_ctx_t * (*layer_init)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + + /** + * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`. + * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param flags OR-ed flags from @lv_draw_layer_flags_t + */ + void (*layer_adjust)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + + /** + * Blend a rendered layer to `layer_ctx->area_act` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param draw_dsc pointer to an image draw descriptor + */ + void (*layer_blend)(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc); + + /** + * Destroy a layer context. The original buffer and area data of the `draw_ctx` will be restored + * and the `layer_ctx` itself will be freed automatically. + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + */ + void (*layer_destroy)(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + + /** + * Size of a layer context in bytes. + */ + size_t layer_instance_size; #if LV_USE_USER_DATA void * user_data; @@ -102,6 +196,9 @@ typedef struct _lv_draw_ctx_t { void lv_draw_init(void); + +void lv_draw_wait_for_finish(lv_draw_ctx_t * draw_ctx); + /********************** * GLOBAL VARIABLES **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.mk b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.mk index 803e06494..f48f48fe0 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.mk +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw.mk @@ -5,6 +5,8 @@ CSRCS += lv_draw_label.c CSRCS += lv_draw_line.c CSRCS += lv_draw_mask.c CSRCS += lv_draw_rect.c +CSRCS += lv_draw_transform.c +CSRCS += lv_draw_layer.c CSRCS += lv_draw_triangle.c CSRCS += lv_img_buf.c CSRCS += lv_img_cache.c @@ -15,4 +17,9 @@ VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw" +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/arm2d/lv_draw_arm2d.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/lv_draw_nxp.mk include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl/lv_draw_sdl.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw/lv_draw_sw.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.c index 320913255..41dc0f039 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.c @@ -236,12 +236,21 @@ LV_ATTRIBUTE_FAST_MEM static lv_res_t decode_and_draw(lv_draw_ctx_t * draw_ctx, if(cdsc == NULL) return LV_RES_INV; - lv_img_cf_t cf; if(lv_img_cf_is_chroma_keyed(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED; + else if(LV_IMG_CF_ALPHA_8BIT == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_ALPHA_8BIT; + else if(LV_IMG_CF_RGB565A8 == cdsc->dec_dsc.header.cf) cf = LV_IMG_CF_RGB565A8; else if(lv_img_cf_has_alpha(cdsc->dec_dsc.header.cf)) cf = LV_IMG_CF_TRUE_COLOR_ALPHA; else cf = LV_IMG_CF_TRUE_COLOR; + if(cf == LV_IMG_CF_ALPHA_8BIT) { + if(draw_dsc->angle || draw_dsc->zoom != LV_IMG_ZOOM_NONE) { + /* resume normal method */ + cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + cdsc->dec_dsc.img_data = NULL; + } + } + if(cdsc->dec_dsc.error_msg != NULL) { LV_LOG_WARN("Image draw error"); diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.h index 504bedae4..a88a33caf 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_img.h @@ -31,7 +31,7 @@ extern "C" { typedef struct { - uint16_t angle; + int16_t angle; uint16_t zoom; lv_point_t pivot; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_label.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_label.c index 8569c7dd0..7f80df453 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_label.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_label.c @@ -202,7 +202,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_label(lv_draw_ctx_t * draw_ctx, const lv_draw cmd_state_t cmd_state = CMD_STATE_WAIT; uint32_t i; uint32_t par_start = 0; - lv_color_t recolor; + lv_color_t recolor = lv_color_black(); lv_color_t color = lv_color_black(); int32_t letter_w; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.c new file mode 100644 index 000000000..da35682d1 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.c @@ -0,0 +1,93 @@ +/** + * @file lv_draw_layer.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw.h" +#include "lv_draw_arc.h" +#include "../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_draw_layer_ctx_t * lv_draw_layer_create(lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area, + lv_draw_layer_flags_t flags) +{ + if(draw_ctx->layer_init == NULL) return NULL; + + lv_draw_layer_ctx_t * layer_ctx = lv_mem_alloc(draw_ctx->layer_instance_size); + LV_ASSERT_MALLOC(layer_ctx); + if(layer_ctx == NULL) { + LV_LOG_WARN("Couldn't allocate a new layer context"); + return NULL; + } + + lv_memset_00(layer_ctx, draw_ctx->layer_instance_size); + + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + layer_ctx->original.buf = draw_ctx->buf; + layer_ctx->original.buf_area = draw_ctx->buf_area; + layer_ctx->original.clip_area = draw_ctx->clip_area; + layer_ctx->original.screen_transp = disp_refr->driver->screen_transp; + layer_ctx->area_full = *layer_area; + + lv_draw_layer_ctx_t * init_layer_ctx = draw_ctx->layer_init(draw_ctx, layer_ctx, flags); + if(NULL == init_layer_ctx) { + lv_mem_free(layer_ctx); + } + return init_layer_ctx; +} + +void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + if(draw_ctx->layer_adjust) draw_ctx->layer_adjust(draw_ctx, layer_ctx, flags); +} + +void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_img_dsc_t * draw_dsc) +{ + if(draw_ctx->layer_blend) draw_ctx->layer_blend(draw_ctx, layer_ctx, draw_dsc); +} + +void lv_draw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + + lv_draw_wait_for_finish(draw_ctx); + draw_ctx->buf = layer_ctx->original.buf; + draw_ctx->buf_area = layer_ctx->original.buf_area; + draw_ctx->clip_area = layer_ctx->original.clip_area; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = layer_ctx->original.screen_transp; + + if(draw_ctx->layer_destroy) draw_ctx->layer_destroy(draw_ctx, layer_ctx); + lv_mem_free(layer_ctx); +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.h new file mode 100644 index 000000000..cd64149c4 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_layer.h @@ -0,0 +1,83 @@ +/** + * @file lv_draw_layer.h + * + */ + +#ifndef LV_DRAW_LAYER_H +#define LV_DRAW_LAYER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_draw_ctx_t; +struct _lv_draw_layer_ctx_t; + +typedef enum { + LV_DRAW_LAYER_FLAG_NONE, + LV_DRAW_LAYER_FLAG_HAS_ALPHA, + LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE, +} lv_draw_layer_flags_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a new layer context. It is used to start and independent rendering session + * with the current draw_ctx + * @param draw_ctx pointer to the current draw context + * @param layer_area the coordinates of the layer + * @param flags OR-ed flags from @lv_draw_layer_flags_t + * @return pointer to the layer context, or NULL on error + */ +struct _lv_draw_layer_ctx_t * lv_draw_layer_create(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * layer_area, + lv_draw_layer_flags_t flags); + +/** + * Adjust the layer_ctx and/or draw_ctx based on the `layer_ctx->area_act`. + * It's called only if flags has `LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param flags OR-ed flags from @lv_draw_layer_flags_t + */ +void lv_draw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +/** + * Blend a rendered layer to `layer_ctx->area_act` + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + * @param draw_dsc pointer to an image draw descriptor + */ +void lv_draw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_img_dsc_t * draw_dsc); + +/** + * Destroy a layer context. + * @param draw_ctx pointer to the current draw context + * @param layer_ctx pointer to a layer context + */ +void lv_draw_layer_destroy(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_LAYER_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.c index 430fd56ae..2170da941 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.c @@ -202,7 +202,7 @@ void * lv_draw_mask_remove_custom(void * custom_id) /** * Free the data from the parameter. - * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom` + * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom` * Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add` * and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom` * @param p pointer to a mask parameter @@ -446,9 +446,6 @@ void lv_draw_mask_angle_init(lv_draw_mask_angle_param_t * param, lv_coord_t vert if(start_angle >= 0 && start_angle < 180) { start_side = LV_DRAW_MASK_LINE_SIDE_LEFT; } - else if(start_angle >= 180 && start_angle < 360) { - start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; - } else start_side = LV_DRAW_MASK_LINE_SIDE_RIGHT; /*silence compiler*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.h index b6ec14f23..b7e4e1cd2 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_mask.h @@ -280,7 +280,7 @@ void * lv_draw_mask_remove_custom(void * custom_id); /** * Free the data from the parameter. - * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom` + * It's called inside `lv_draw_mask_remove_id` and `lv_draw_mask_remove_custom` * Needs to be called only in special cases when the mask is not added by `lv_draw_mask_add` * and not removed by `lv_draw_mask_remove_id` or `lv_draw_mask_remove_custom` * @param p pointer to a mask parameter diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.c index 8cb428824..f34854d9c 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.c @@ -38,9 +38,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_rect_dsc_init(lv_draw_rect_dsc_t * dsc) { lv_memset_00(dsc, sizeof(lv_draw_rect_dsc_t)); dsc->bg_color = lv_color_white(); -#if __STDC_VERSION__ < 201112L dsc->bg_grad.stops[0].color = lv_color_white(); -#endif dsc->bg_grad.stops[1].color = lv_color_black(); dsc->bg_grad.stops[1].frac = 0xFF; dsc->bg_grad.stops_count = 2; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.h index efa630cf2..1583e3e67 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_rect.h @@ -35,14 +35,8 @@ typedef struct { /*Background*/ lv_opa_t bg_opa; -#if __STDC_VERSION__ >= 201112L - union { -#endif - lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/ - lv_grad_dsc_t bg_grad; -#if __STDC_VERSION__ >= 201112L - }; -#endif + lv_color_t bg_color; /**< First element of a gradient is a color, so it maps well here*/ + lv_grad_dsc_t bg_grad; /*Background img*/ const void * bg_img_src; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.c new file mode 100644 index 000000000..580351d6d --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.c @@ -0,0 +1,54 @@ +/** + * @file lv_draw_transform.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw.h" +#include "lv_draw_transform.h" +#include "../misc/lv_assert.h" +#include "../misc/lv_area.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_draw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, + lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_ASSERT_NULL(draw_ctx); + if(draw_ctx->draw_transform == NULL) { + LV_LOG_WARN("draw_ctx->draw_transform == NULL"); + return; + } + + draw_ctx->draw_transform(draw_ctx, dest_area, src_buf, src_w, src_h, src_stride, draw_dsc, cf, cbuf, abuf); + +} + + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.h new file mode 100644 index 000000000..1926c2fc2 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_draw_transform.h @@ -0,0 +1,44 @@ +/** + * @file lv_draw_transform.h + * + */ + +#ifndef LV_DRAW_TRANSFORM_H +#define LV_DRAW_TRANSFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../lv_conf_internal.h" +#include "../misc/lv_area.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct _lv_draw_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_transform(struct _lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_TRANSFORM_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.c index a6d0efc36..4c939dd83 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.c @@ -54,7 +54,7 @@ lv_color_t lv_img_buf_get_px_color(lv_img_dsc_t * dsc, lv_coord_t x, lv_coord_t uint8_t * buf_u8 = (uint8_t *)dsc->data; if(dsc->header.cf == LV_IMG_CF_TRUE_COLOR || dsc->header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED || - dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + dsc->header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || dsc->header.cf == LV_IMG_CF_RGB565A8) { uint8_t px_size = lv_img_cf_get_px_size(dsc->header.cf) >> 3; uint32_t px = dsc->header.w * y * px_size + x * px_size; lv_memcpy_small(&p_color, &buf_u8[px], sizeof(lv_color_t)); @@ -386,6 +386,7 @@ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) case LV_IMG_CF_TRUE_COLOR: return LV_IMG_BUF_SIZE_TRUE_COLOR(w, h); case LV_IMG_CF_TRUE_COLOR_ALPHA: + case LV_IMG_CF_RGB565A8: return LV_IMG_BUF_SIZE_TRUE_COLOR_ALPHA(w, h); case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: return LV_IMG_BUF_SIZE_TRUE_COLOR_CHROMA_KEYED(w, h); @@ -410,58 +411,6 @@ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) } } -#if LV_DRAW_COMPLEX -/** - * Initialize a descriptor to transform an image - * @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized - */ -void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc) -{ - dsc->tmp.pivot_x_256 = dsc->cfg.pivot_x * 256; - dsc->tmp.pivot_y_256 = dsc->cfg.pivot_y * 256; - - int32_t angle_low = dsc->cfg.angle / 10; - int32_t angle_high = angle_low + 1; - int32_t angle_rem = dsc->cfg.angle - (angle_low * 10); - - int32_t s1 = lv_trigo_sin(-angle_low); - int32_t s2 = lv_trigo_sin(-angle_high); - - int32_t c1 = lv_trigo_sin(-angle_low + 90); - int32_t c2 = lv_trigo_sin(-angle_high + 90); - - dsc->tmp.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; - dsc->tmp.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; - - /*Use smaller value to avoid overflow*/ - dsc->tmp.sinma = dsc->tmp.sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); - dsc->tmp.cosma = dsc->tmp.cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); - - dsc->tmp.chroma_keyed = lv_img_cf_is_chroma_keyed(dsc->cfg.cf) ? 1 : 0; - dsc->tmp.has_alpha = lv_img_cf_has_alpha(dsc->cfg.cf) ? 1 : 0; - if(dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR || dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_ALPHA || - dsc->cfg.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { - dsc->tmp.native_color = 1; - } - else { - dsc->tmp.native_color = 0; - } - - dsc->tmp.img_dsc.data = dsc->cfg.src; - dsc->tmp.img_dsc.header.always_zero = 0; - dsc->tmp.img_dsc.header.cf = dsc->cfg.cf; - dsc->tmp.img_dsc.header.w = dsc->cfg.src_w; - dsc->tmp.img_dsc.header.h = dsc->cfg.src_h; - - /*The inverse of the zoom will be sued during the transformation - * + dsc->cfg.zoom / 2 for rounding*/ - dsc->tmp.zoom_inv = (((256 * 256) << _LV_ZOOM_INV_UPSCALE) + dsc->cfg.zoom / 2) / dsc->cfg.zoom; - - dsc->res.opa = LV_OPA_COVER; - dsc->res.color = dsc->cfg.color; -} -#endif - /** * Get the area of a rectangle if its rotated and scaled * @param res store the coordinates here @@ -483,68 +432,21 @@ void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t return; } - res->x1 = (((int32_t)(-pivot->x) * zoom) >> 8) - 1; - res->y1 = (((int32_t)(-pivot->y) * zoom) >> 8) - 1; - res->x2 = (((int32_t)(w - pivot->x) * zoom) >> 8) + 2; - res->y2 = (((int32_t)(h - pivot->y) * zoom) >> 8) + 2; + lv_point_t p[4] = { + {0, 0}, + {w, 0}, + {0, h}, + {w, h}, + }; + lv_point_transform(&p[0], angle, zoom, pivot); + lv_point_transform(&p[1], angle, zoom, pivot); + lv_point_transform(&p[2], angle, zoom, pivot); + lv_point_transform(&p[3], angle, zoom, pivot); + res->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x) - 2; + res->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x) + 2; + res->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y) - 2; + res->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y) + 2; - if(angle == 0) { - res->x1 += pivot->x; - res->y1 += pivot->y; - res->x2 += pivot->x; - res->y2 += pivot->y; - return; - } - - int32_t angle_low = angle / 10; - int32_t angle_high = angle_low + 1; - int32_t angle_rem = angle - (angle_low * 10); - - int32_t s1 = lv_trigo_sin(angle_low); - int32_t s2 = lv_trigo_sin(angle_high); - - int32_t c1 = lv_trigo_sin(angle_low + 90); - int32_t c2 = lv_trigo_sin(angle_high + 90); - - int32_t sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; - int32_t cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; - - /*Use smaller value to avoid overflow*/ - sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); - cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); - - lv_point_t lt; - lv_point_t rt; - lv_point_t lb; - lv_point_t rb; - - lv_coord_t xt; - lv_coord_t yt; - - xt = res->x1; - yt = res->y1; - lt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; - lt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; - - xt = res->x2; - yt = res->y1; - rt.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; - rt.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; - - xt = res->x1; - yt = res->y2; - lb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; - lb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; - - xt = res->x2; - yt = res->y2; - rb.x = ((cosma * xt - sinma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; - rb.y = ((sinma * xt + cosma * yt) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; - - res->x1 = LV_MIN4(lb.x, lt.x, rb.x, rt.x); - res->x2 = LV_MAX4(lb.x, lt.x, rb.x, rt.x); - res->y1 = LV_MIN4(lb.y, lt.y, rb.y, rt.y); - res->y2 = LV_MAX4(lb.y, lt.y, rb.y, rt.y); #else LV_UNUSED(angle); LV_UNUSED(zoom); @@ -556,217 +458,6 @@ void _lv_img_buf_get_transformed_area(lv_area_t * res, lv_coord_t w, lv_coord_t #endif } - -#if LV_DRAW_COMPLEX -/** - * Get which color and opa would come to a pixel if it were rotated - * @param dsc a descriptor initialized by `lv_img_buf_rotate_init` - * @param x the coordinate which color and opa should be get - * @param y the coordinate which color and opa should be get - * @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image - * @note the result is written back to `dsc->res_color` and `dsc->res_opa` - */ -bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y) -{ - const uint8_t * src_u8 = (const uint8_t *)dsc->cfg.src; - - /*Get the target point relative coordinates to the pivot*/ - int32_t xt = x - dsc->cfg.pivot_x; - int32_t yt = y - dsc->cfg.pivot_y; - - int32_t xs; - int32_t ys; - if(dsc->cfg.zoom == LV_IMG_ZOOM_NONE) { - /*Get the source pixel from the upscaled image*/ - xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_x_256; - ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT - 8)) + dsc->tmp.pivot_y_256; - } - else if(dsc->cfg.angle == 0) { - xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; - yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; - xs = xt + dsc->tmp.pivot_x_256; - ys = yt + dsc->tmp.pivot_y_256; - } - else { - xt = (int32_t)((int32_t)xt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; - yt = (int32_t)((int32_t)yt * dsc->tmp.zoom_inv) >> _LV_ZOOM_INV_UPSCALE; - xs = ((dsc->tmp.cosma * xt - dsc->tmp.sinma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_x_256; - ys = ((dsc->tmp.sinma * xt + dsc->tmp.cosma * yt) >> (_LV_TRANSFORM_TRIGO_SHIFT)) + dsc->tmp.pivot_y_256; - } - - /*Get the integer part of the source pixel*/ - int32_t xs_int = xs >> 8; - int32_t ys_int = ys >> 8; - - if(xs_int >= dsc->cfg.src_w) return false; - else if(xs_int < 0) return false; - - if(ys_int >= dsc->cfg.src_h) return false; - else if(ys_int < 0) return false; - - uint8_t px_size; - uint32_t pxi; - if(dsc->tmp.native_color) { - if(dsc->tmp.has_alpha == 0) { - px_size = LV_COLOR_SIZE >> 3; - - pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size; - lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size); - } - else { - px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; - pxi = dsc->cfg.src_w * ys_int * px_size + xs_int * px_size; - lv_memcpy_small(&dsc->res.color, &src_u8[pxi], px_size - 1); - dsc->res.opa = src_u8[pxi + px_size - 1]; - } - } - else { - pxi = 0; /*unused*/ - px_size = 0; /*unused*/ - dsc->res.color = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, xs_int, ys_int, dsc->cfg.color); - dsc->res.opa = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, xs_int, ys_int); - } - - if(dsc->tmp.chroma_keyed) { - lv_color_t ct = LV_COLOR_CHROMA_KEY; - if(dsc->res.color.full == ct.full) return false; - } - - if(dsc->cfg.antialias == false) return true; - - dsc->tmp.xs = xs; - dsc->tmp.ys = ys; - dsc->tmp.xs_int = xs_int; - dsc->tmp.ys_int = ys_int; - dsc->tmp.pxi = pxi; - dsc->tmp.px_size = px_size; - - bool ret; - ret = _lv_img_buf_transform_anti_alias(dsc); - - return ret; -} - -/** - * Continue transformation by taking the neighbors into account - * @param dsc pointer to the transformation descriptor - */ -bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc) -{ - const uint8_t * src_u8 = dsc->cfg.src; - - /*Get the fractional part of the source pixel*/ - int xs_fract = dsc->tmp.xs & 0xff; - int ys_fract = dsc->tmp.ys & 0xff; - int32_t xn; /*x neighbor*/ - lv_opa_t xr; /*x mix ratio*/ - - if(xs_fract < 0x70) { - xn = - 1; - if(dsc->tmp.xs_int + xn < 0) xn = 0; - xr = xs_fract + 0x80; - } - else if(xs_fract > 0x90) { - xn = 1; - if(dsc->tmp.xs_int + xn >= dsc->cfg.src_w) xn = 0; - xr = (0xFF - xs_fract) + 0x80; - } - else { - xn = 0; - xr = 0xFF; - } - - int32_t yn; /*x neighbor*/ - lv_opa_t yr; /*x mix ratio*/ - - if(ys_fract < 0x70) { - yn = - 1; - if(dsc->tmp.ys_int + yn < 0) yn = 0; - - yr = ys_fract + 0x80; - } - else if(ys_fract > 0x90) { - yn = 1; - if(dsc->tmp.ys_int + yn >= dsc->cfg.src_h) yn = 0; - - yr = (0xFF - ys_fract) + 0x80; - } - else { - yn = 0; - yr = 0xFF; - } - - lv_color_t c00 = dsc->res.color; - lv_color_t c01; - lv_color_t c10; - lv_color_t c11; - - lv_opa_t a00 = dsc->res.opa; - lv_opa_t a10 = 0; - lv_opa_t a01 = 0; - lv_opa_t a11 = 0; - - if(dsc->tmp.native_color) { - lv_memcpy_small(&c01, &src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn], sizeof(lv_color_t)); - lv_memcpy_small(&c10, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn], sizeof(lv_color_t)); - lv_memcpy_small(&c11, &src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn], - sizeof(lv_color_t)); - if(dsc->tmp.has_alpha) { - a10 = src_u8[dsc->tmp.pxi + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1]; - a01 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size - 1]; - a11 = src_u8[dsc->tmp.pxi + dsc->cfg.src_w * dsc->tmp.px_size * yn + dsc->tmp.px_size * xn + dsc->tmp.px_size - 1]; - } - } - else { - c01 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int, dsc->cfg.color); - c10 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn, dsc->cfg.color); - c11 = lv_img_buf_get_px_color(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn, dsc->cfg.color); - - if(dsc->tmp.has_alpha) { - a10 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int); - a01 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int, dsc->tmp.ys_int + yn); - a11 = lv_img_buf_get_px_alpha(&dsc->tmp.img_dsc, dsc->tmp.xs_int + xn, dsc->tmp.ys_int + yn); - } - } - - lv_opa_t xr0 = xr; - lv_opa_t xr1 = xr; - if(dsc->tmp.has_alpha) { - lv_opa_t a0 = (a00 * xr + (a10 * (255 - xr))) >> 8; - lv_opa_t a1 = (a01 * xr + (a11 * (255 - xr))) >> 8; - dsc->res.opa = (a0 * yr + (a1 * (255 - yr))) >> 8; - - if(a0 <= LV_OPA_MIN && a1 <= LV_OPA_MIN) return false; - if(a0 <= LV_OPA_MIN) yr = LV_OPA_TRANSP; - if(a1 <= LV_OPA_MIN) yr = LV_OPA_COVER; - if(a00 <= LV_OPA_MIN) xr0 = LV_OPA_TRANSP; - if(a10 <= LV_OPA_MIN) xr0 = LV_OPA_COVER; - if(a01 <= LV_OPA_MIN) xr1 = LV_OPA_TRANSP; - if(a11 <= LV_OPA_MIN) xr1 = LV_OPA_COVER; - } - else { - xr0 = xr; - xr1 = xr; - dsc->res.opa = LV_OPA_COVER; - } - - lv_color_t c0; - if(xr0 == LV_OPA_TRANSP) c0 = c01; - else if(xr0 == LV_OPA_COVER) c0 = c00; - else c0 = lv_color_mix(c00, c01, xr0); - - lv_color_t c1; - if(xr1 == LV_OPA_TRANSP) c1 = c11; - else if(xr1 == LV_OPA_COVER) c1 = c10; - else c1 = lv_color_mix(c10, c11, xr1); - - if(yr == LV_OPA_TRANSP) dsc->res.color = c1; - else if(yr == LV_OPA_COVER) dsc->res.color = c0; - else dsc->res.color = lv_color_mix(c0, c1, yr); - - return true; -} -#endif /********************** * STATIC FUNCTIONS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.h index 984483e5e..1be7bb652 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_buf.h @@ -45,7 +45,6 @@ extern "C" { #define LV_IMG_BUF_SIZE_INDEXED_4BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_4BIT(w, h) + 4 * 16) #define LV_IMG_BUF_SIZE_INDEXED_8BIT(w, h) (LV_IMG_BUF_SIZE_ALPHA_8BIT(w, h) + 4 * 256) -#define _LV_TRANSFORM_TRIGO_SHIFT 10 #define _LV_ZOOM_INV_UPSCALE 5 /********************** @@ -67,16 +66,23 @@ enum { LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, /**< Same as `LV_IMG_CF_TRUE_COLOR` but LV_COLOR_TRANSP pixels will be transparent*/ - LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (always chroma keyed)*/ - LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (always chroma keyed)*/ - LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (always chroma keyed)*/ - LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (always chroma keyed)*/ + LV_IMG_CF_INDEXED_1BIT, /**< Can have 2 different colors in a palette (can't be chroma keyed)*/ + LV_IMG_CF_INDEXED_2BIT, /**< Can have 4 different colors in a palette (can't be chroma keyed)*/ + LV_IMG_CF_INDEXED_4BIT, /**< Can have 16 different colors in a palette (can't be chroma keyed)*/ + LV_IMG_CF_INDEXED_8BIT, /**< Can have 256 different colors in a palette (can't be chroma keyed)*/ LV_IMG_CF_ALPHA_1BIT, /**< Can have one color and it can be drawn or not*/ LV_IMG_CF_ALPHA_2BIT, /**< Can have one color but 4 different alpha value*/ LV_IMG_CF_ALPHA_4BIT, /**< Can have one color but 16 different alpha value*/ LV_IMG_CF_ALPHA_8BIT, /**< Can have one color but 256 different alpha value*/ + LV_IMG_CF_RGB888, + LV_IMG_CF_RGBA8888, + LV_IMG_CF_RGBX8888, + LV_IMG_CF_RGB565, + LV_IMG_CF_RGBA5658, + LV_IMG_CF_RGB565A8, + LV_IMG_CF_RESERVED_15, /**< Reserved for further use.*/ LV_IMG_CF_RESERVED_16, /**< Reserved for further use.*/ LV_IMG_CF_RESERVED_17, /**< Reserved for further use.*/ @@ -138,49 +144,6 @@ typedef struct { const uint8_t * data; /**< Pointer to the data of the image*/ } lv_img_dsc_t; -typedef struct { - struct { - const void * src; /*image source (array of pixels)*/ - lv_coord_t src_w; /*width of the image source*/ - lv_coord_t src_h; /*height of the image source*/ - lv_coord_t pivot_x; /*pivot x*/ - lv_coord_t pivot_y; /*pivot y*/ - int16_t angle; /*angle to rotate*/ - uint16_t zoom; /*256 no zoom, 128 half size, 512 double size*/ - lv_color_t color; /*a color used for `LV_IMG_CF_INDEXED_1/2/4/8BIT` color formats*/ - lv_img_cf_t cf; /*color format of the image to rotate*/ - bool antialias; - } cfg; - - struct { - lv_color_t color; - lv_opa_t opa; - } res; - - struct { - lv_img_dsc_t img_dsc; - int32_t pivot_x_256; - int32_t pivot_y_256; - int32_t sinma; - int32_t cosma; - - uint8_t chroma_keyed : 1; - uint8_t has_alpha : 1; - uint8_t native_color : 1; - - uint32_t zoom_inv; - - /*Runtime data*/ - lv_coord_t xs; - lv_coord_t ys; - lv_coord_t xs_int; - lv_coord_t ys_int; - uint32_t pxi; - uint8_t px_size; - } tmp; -} lv_img_transform_dsc_t; - - /********************** * GLOBAL PROTOTYPES **********************/ @@ -263,30 +226,6 @@ void lv_img_buf_free(lv_img_dsc_t * dsc); */ uint32_t lv_img_buf_get_img_size(lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); -#if LV_DRAW_COMPLEX -/** - * Initialize a descriptor to rotate an image - * @param dsc pointer to an `lv_img_transform_dsc_t` variable whose `cfg` field is initialized - */ -void _lv_img_buf_transform_init(lv_img_transform_dsc_t * dsc); - -/** - * Continue transformation by taking the neighbors into account - * @param dsc pointer to the transformation descriptor - */ -bool _lv_img_buf_transform_anti_alias(lv_img_transform_dsc_t * dsc); - -/** - * Get which color and opa would come to a pixel if it were rotated - * @param dsc a descriptor initialized by `lv_img_buf_rotate_init` - * @param x the coordinate which color and opa should be get - * @param y the coordinate which color and opa should be get - * @return true: there is valid pixel on these x/y coordinates; false: the rotated pixel was out of the image - * @note the result is written back to `dsc->res_color` and `dsc->res_opa` - */ -bool _lv_img_buf_transform(lv_img_transform_dsc_t * dsc, lv_coord_t x, lv_coord_t y); - -#endif /** * Get the area of a rectangle if its rotated and scaled * @param res store the coordinates here diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.c b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.c index 3678a5712..13050b8b7 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.c @@ -15,8 +15,8 @@ /********************* * DEFINES *********************/ -#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR -#define CF_BUILT_IN_LAST LV_IMG_CF_ALPHA_8BIT +#define CF_BUILT_IN_FIRST LV_IMG_CF_TRUE_COLOR +#define CF_BUILT_IN_LAST LV_IMG_CF_RGB565A8 /********************** * TYPEDEFS @@ -146,7 +146,7 @@ lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_co dsc->decoder = decoder; res = decoder->open_cb(decoder, dsc); - /*Opened successfully. It is a good decoder to for this image source*/ + /*Opened successfully. It is a good decoder for this image source*/ if(res == LV_RES_OK) return res; /*Prepare for the next loop*/ @@ -361,7 +361,9 @@ lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder lv_img_cf_t cf = dsc->header.cf; /*Process true color formats*/ - if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_ALPHA || + cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED || cf == LV_IMG_CF_RGB565A8 || + cf == LV_IMG_CF_ALPHA_8BIT) { if(dsc->src_type == LV_IMG_SRC_VARIABLE) { /*In case of uncompressed formats the image stored in the ROM/RAM. *So simply give its pointer*/ @@ -426,8 +428,7 @@ lv_res_t lv_img_decoder_built_in_open(lv_img_decoder_t * decoder, lv_img_decoder return LV_RES_OK; } /*Alpha indexed images.*/ - else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT || - cf == LV_IMG_CF_ALPHA_8BIT) { + else if(cf == LV_IMG_CF_ALPHA_1BIT || cf == LV_IMG_CF_ALPHA_2BIT || cf == LV_IMG_CF_ALPHA_4BIT) { return LV_RES_OK; /*Nothing to process*/ } /*Unknown format. Can't decode it.*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.h b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.h index ebebf1091..f00f95d6a 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/lv_img_decoder.h @@ -154,8 +154,8 @@ lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header); /** * Open an image. - * Try the created image decoder one by one. Once one is able to open the image that decoder is save in `dsc` - * @param dsc describe a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable. + * Try the created image decoders one by one. Once one is able to open the image that decoder is saved in `dsc` + * @param dsc describes a decoding session. Simply a pointer to an `lv_img_decoder_dsc_t` variable. * @param src the image source. Can be * 1) File name: E.g. "S:folder/img1.png" (The drivers needs to registered via `lv_fs_add_drv()`) * 2) Variable: Pointer to an `lv_img_dsc_t` variable diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_draw_nxp.mk b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_draw_nxp.mk new file mode 100644 index 000000000..17371ac98 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_draw_nxp.mk @@ -0,0 +1,9 @@ +CSRCS += lv_gpu_nxp.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp" + +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk +include $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.c new file mode 100644 index 000000000..46da9334a --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.c @@ -0,0 +1,418 @@ +/** + * @file lv_gpu_nxp.c + * + */ + +/** + * MIT License + * + * Copyright 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_gpu_nxp.h" + +#if LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE + +/* + * allow to use both PXP and VGLITE + + * both 2D accelerators can be used at the same time: + * thus VGLITE can be used to accelerate widget drawing + * while PXP accelerates Blit & Fill operations. + */ +#if LV_USE_GPU_NXP_PXP + #include "pxp/lv_draw_pxp_blend.h" +#endif +#if LV_USE_GPU_NXP_VG_LITE + #include "vglite/lv_draw_vglite_blend.h" + #include "vglite/lv_draw_vglite_rect.h" + #include "vglite/lv_draw_vglite_arc.h" +#endif + +#if LV_COLOR_DEPTH != 32 + #include "../../core/lv_refr.h" +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_draw_nxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, + const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf); + +static void lv_draw_nxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); + +static void lv_draw_nxp_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +static lv_res_t draw_nxp_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +static void lv_draw_nxp_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + uint16_t radius, uint16_t start_angle, uint16_t end_angle); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_nxp_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + lv_draw_sw_init_ctx(drv, draw_ctx); + + lv_draw_nxp_ctx_t * nxp_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx; + + nxp_draw_ctx->base_draw.draw_arc = lv_draw_nxp_arc; + nxp_draw_ctx->base_draw.draw_rect = lv_draw_nxp_rect; + nxp_draw_ctx->base_draw.draw_img_decoded = lv_draw_nxp_img_decoded; + nxp_draw_ctx->blend = lv_draw_nxp_blend; + //nxp_draw_ctx->base_draw.wait_for_finish = lv_draw_nxp_wait_cb; +} + +void lv_draw_nxp_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + lv_draw_sw_deinit_ctx(drv, draw_ctx); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * During rendering, LVGL might initializes new draw_ctxs and start drawing into + * a separate buffer (called layer). If the content to be rendered has "holes", + * e.g. rounded corner, LVGL temporarily sets the disp_drv.screen_transp flag. + * It means the renderers should draw into an ARGB buffer. + * With 32 bit color depth it's not a big problem but with 16 bit color depth + * the target pixel format is ARGB8565 which is not supported by the GPU. + * In this case, the NXP callbacks should fallback to SW rendering. + */ +static inline bool need_argb8565_support() +{ +#if LV_COLOR_DEPTH != 32 + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + + if(disp->driver->screen_transp == 1) + return true; +#endif + + return false; +} + +static void lv_draw_nxp_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + lv_area_t blend_area; + + /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/ + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) + return; /*Fully clipped, nothing to do*/ + + /*Make the blend area relative to the buffer*/ + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + bool done = false; + + /*Fill/Blend only non masked, normal blended*/ + if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && !need_argb8565_support()) { + lv_color_t * dest_buf = draw_ctx->buf; + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); +#if LV_USE_GPU_NXP_VG_LITE + lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area); + lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area); +#endif + + const lv_color_t * src_buf = dsc->src_buf; + + if(src_buf == NULL) { +#if LV_USE_GPU_NXP_PXP + done = (lv_gpu_nxp_pxp_fill(dest_buf, dest_stride, &blend_area, + dsc->color, dsc->opa) == LV_RES_OK); + if(!done) + PXP_LOG_TRACE("PXP fill failed. Fallback."); + +#endif +#if LV_USE_GPU_NXP_VG_LITE + if(!done) { + done = (lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &blend_area, + dsc->color, dsc->opa) == LV_RES_OK); + if(!done) + VG_LITE_LOG_TRACE("VG-Lite fill failed. Fallback."); + } +#endif + } + else { +#if LV_USE_GPU_NXP_PXP + done = (lv_gpu_nxp_pxp_blit(dest_buf, &blend_area, dest_stride, src_buf, dsc->blend_area, + dsc->opa, LV_DISP_ROT_NONE) == LV_RES_OK); + if(!done) + PXP_LOG_TRACE("PXP blit failed. Fallback."); +#endif +#if LV_USE_GPU_NXP_VG_LITE + if(!done) { + lv_gpu_nxp_vglite_blit_info_t blit; + lv_coord_t src_stride = lv_area_get_width(dsc->blend_area); + + blit.src = src_buf; + blit.src_width = lv_area_get_width(dsc->blend_area); + blit.src_height = lv_area_get_height(dsc->blend_area); + blit.src_stride = src_stride * (int32_t)sizeof(lv_color_t); + blit.src_area.x1 = (blend_area.x1 - (dsc->blend_area->x1 - draw_ctx->buf_area->x1)); + blit.src_area.y1 = (blend_area.y1 - (dsc->blend_area->y1 - draw_ctx->buf_area->y1)); + blit.src_area.x2 = blit.src_area.x1 + blit.src_width - 1; + blit.src_area.y2 = blit.src_area.y1 + blit.src_height - 1; + + blit.dst = dest_buf; + blit.dst_width = dest_width; + blit.dst_height = dest_height; + blit.dst_stride = dest_stride * (int32_t)sizeof(lv_color_t); + blit.dst_area.x1 = blend_area.x1; + blit.dst_area.y1 = blend_area.y1; + blit.dst_area.x2 = blend_area.x2; + blit.dst_area.y2 = blend_area.y2; + + blit.opa = dsc->opa; + blit.zoom = LV_IMG_ZOOM_NONE; + blit.angle = 0; + + done = (lv_gpu_nxp_vglite_blit(&blit) == LV_RES_OK); + + if(!done) + VG_LITE_LOG_TRACE("VG-Lite blit failed. Fallback."); + } +#endif + } + } + + if(!done) + lv_draw_sw_blend_basic(draw_ctx, dsc); +} + +static void lv_draw_nxp_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, + const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t cf) +{ + /*Use the clip area as draw area*/ + lv_area_t draw_area; + lv_area_copy(&draw_area, draw_ctx->clip_area); + bool mask_any = lv_draw_mask_is_any(&draw_area); +#if LV_USE_GPU_NXP_VG_LITE + bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP); +#endif +#if LV_USE_GPU_NXP_PXP + bool scale = (dsc->zoom != LV_IMG_ZOOM_NONE); +#endif + bool done = false; + + lv_area_t blend_area; + /*Let's get the blend area which is the intersection of the area to fill and the clip area.*/ + if(!_lv_area_intersect(&blend_area, coords, draw_ctx->clip_area)) + return; /*Fully clipped, nothing to do*/ + + /*Make the blend area relative to the buffer*/ + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + const lv_color_t * src_buf = (const lv_color_t *)map_p; + if(!src_buf) { + lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf); + return; + } + + lv_color_t * dest_buf = draw_ctx->buf; + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); + +#if LV_USE_GPU_NXP_PXP + if(!mask_any && !scale && !need_argb8565_support() +#if LV_COLOR_DEPTH!=32 + && !lv_img_cf_has_alpha(cf) +#endif + ) { + done = (lv_gpu_nxp_pxp_blit_transform(dest_buf, &blend_area, dest_stride, src_buf, coords, + dsc, cf) == LV_RES_OK); + if(!done) + PXP_LOG_TRACE("PXP blit transform failed. Fallback."); + } +#endif + +#if LV_USE_GPU_NXP_VG_LITE + if(!done && !mask_any && !need_argb8565_support() && + !lv_img_cf_is_chroma_keyed(cf) && !recolor +#if LV_COLOR_DEPTH!=32 + && !lv_img_cf_has_alpha(cf) +#endif + ) { + lv_gpu_nxp_vglite_blit_info_t blit; + lv_coord_t src_stride = lv_area_get_width(coords); + + blit.src = src_buf; + blit.src_width = lv_area_get_width(coords); + blit.src_height = lv_area_get_height(coords); + blit.src_stride = src_stride * (int32_t)sizeof(lv_color_t); + blit.src_area.x1 = (blend_area.x1 - (coords->x1 - draw_ctx->buf_area->x1)); + blit.src_area.y1 = (blend_area.y1 - (coords->y1 - draw_ctx->buf_area->y1)); + blit.src_area.x2 = blit.src_area.x1 + blit.src_width - 1; + blit.src_area.y2 = blit.src_area.y1 + blit.src_height - 1; + + blit.dst = dest_buf; + blit.dst_width = lv_area_get_width(draw_ctx->buf_area); + blit.dst_height = lv_area_get_height(draw_ctx->buf_area); + blit.dst_stride = dest_stride * (int32_t)sizeof(lv_color_t); + blit.dst_area.x1 = blend_area.x1; + blit.dst_area.y1 = blend_area.y1; + blit.dst_area.x2 = blend_area.x2; + blit.dst_area.y2 = blend_area.y2; + + blit.opa = dsc->opa; + blit.angle = dsc->angle; + blit.pivot = dsc->pivot; + blit.zoom = dsc->zoom; + + done = (lv_gpu_nxp_vglite_blit_transform(&blit) == LV_RES_OK); + + if(!done) + VG_LITE_LOG_TRACE("VG-Lite blit transform failed. Fallback."); + } +#endif + + if(!done) + lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, cf); +} + +static void lv_draw_nxp_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + bool done = false; + lv_draw_rect_dsc_t nxp_dsc; + + lv_memcpy(&nxp_dsc, dsc, sizeof(nxp_dsc)); +#if LV_DRAW_COMPLEX + /* Draw only the shadow */ + nxp_dsc.bg_opa = 0; + nxp_dsc.bg_img_opa = 0; + nxp_dsc.border_opa = 0; + nxp_dsc.outline_opa = 0; + + lv_draw_sw_rect(draw_ctx, &nxp_dsc, coords); + + /* Draw the background */ + nxp_dsc.shadow_opa = 0; + nxp_dsc.bg_opa = dsc->bg_opa; + done = (draw_nxp_bg(draw_ctx, &nxp_dsc, coords) == LV_RES_OK); +#endif /*LV_DRAW_COMPLEX*/ + + /* Draw the remaining parts */ + nxp_dsc.shadow_opa = 0; + if(done) + nxp_dsc.bg_opa = 0; + nxp_dsc.bg_img_opa = dsc->bg_img_opa; + nxp_dsc.border_opa = dsc->border_opa; + nxp_dsc.outline_opa = dsc->outline_opa; + + lv_draw_sw_rect(draw_ctx, &nxp_dsc, coords); +} + +static lv_res_t draw_nxp_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + if(dsc->bg_opa <= LV_OPA_MIN) + return LV_RES_INV; + + lv_area_t bg_coords; + lv_area_copy(&bg_coords, coords); + + /*If the border fully covers make the bg area 1px smaller to avoid artifacts on the corners*/ + if(dsc->border_width > 1 && dsc->border_opa >= (lv_opa_t)LV_OPA_MAX && dsc->radius != 0) { + bg_coords.x1 += (dsc->border_side & LV_BORDER_SIDE_LEFT) ? 1 : 0; + bg_coords.y1 += (dsc->border_side & LV_BORDER_SIDE_TOP) ? 1 : 0; + bg_coords.x2 -= (dsc->border_side & LV_BORDER_SIDE_RIGHT) ? 1 : 0; + bg_coords.y2 -= (dsc->border_side & LV_BORDER_SIDE_BOTTOM) ? 1 : 0; + } + + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, &bg_coords, draw_ctx->clip_area)) + return LV_RES_INV; + + lv_grad_dir_t grad_dir = dsc->bg_grad.dir; + lv_color_t bg_color = grad_dir == LV_GRAD_DIR_NONE ? dsc->bg_color : dsc->bg_grad.stops[0].color; + if(bg_color.full == dsc->bg_grad.stops[1].color.full) grad_dir = LV_GRAD_DIR_NONE; + + bool mask_any = lv_draw_mask_is_any(&bg_coords); + + /* + * Most simple case: just a plain rectangle (no mask, no radius, no gradient) + * shall fallback to lv_draw_sw_blend(). + * + * Complex case: gradient or radius but no mask. + */ + if(!mask_any && ((dsc->radius != 0) || (grad_dir != LV_GRAD_DIR_NONE)) && !need_argb8565_support()) { +#if LV_USE_GPU_NXP_VG_LITE + lv_res_t res = lv_gpu_nxp_vglite_draw_bg(draw_ctx, dsc, &bg_coords); + if(res != LV_RES_OK) + VG_LITE_LOG_TRACE("VG-Lite draw bg failed. Fallback."); + + return res; +#endif + } + + return LV_RES_INV; +} + +static void lv_draw_nxp_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + uint16_t radius, uint16_t start_angle, uint16_t end_angle) +{ + bool done = false; + +#if LV_DRAW_COMPLEX + if(dsc->opa <= LV_OPA_MIN) + return; + if(dsc->width == 0) + return; + if(start_angle == end_angle) + return; + +#if LV_USE_GPU_NXP_VG_LITE + if(!need_argb8565_support()) { + done = (lv_gpu_nxp_vglite_draw_arc(draw_ctx, dsc, center, (int32_t)radius, + (int32_t)start_angle, (int32_t)end_angle) == LV_RES_OK); + if(!done) + VG_LITE_LOG_TRACE("VG-Lite draw arc failed. Fallback."); + } +#endif +#endif/*LV_DRAW_COMPLEX*/ + + if(!done) + lv_draw_sw_arc(draw_ctx, dsc, center, radius, start_angle, end_angle); +} + +#endif /*LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.h new file mode 100644 index 000000000..899aff25b --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/lv_gpu_nxp.h @@ -0,0 +1,71 @@ +/** + * @file lv_gpu_nxp.h + * + */ + +/** + * MIT License + * + * Copyright 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_GPU_NXP_H +#define LV_GPU_NXP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE +#include "../sw/lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef lv_draw_sw_ctx_t lv_draw_nxp_ctx_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_draw_nxp_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_nxp_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +/********************** + * MACROS + **********************/ +#endif /*LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_NXP_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk new file mode 100644 index 000000000..ff475a193 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_nxp_pxp.mk @@ -0,0 +1,8 @@ +CSRCS += lv_draw_pxp_blend.c +CSRCS += lv_gpu_nxp_pxp_osa.c +CSRCS += lv_gpu_nxp_pxp.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/pxp" diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c new file mode 100644 index 000000000..c0a6ecafa --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.c @@ -0,0 +1,632 @@ +/** + * @file lv_draw_pxp_blend.c + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_pxp_blend.h" + +#if LV_USE_GPU_NXP_PXP + +/********************* + * DEFINES + *********************/ + +#if LV_COLOR_16_SWAP + #error Color swap not implemented. Disable LV_COLOR_16_SWAP feature. +#endif + +#if LV_COLOR_DEPTH==16 + #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565 + #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565 + #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565 +#elif LV_COLOR_DEPTH==32 + #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatARGB8888 + #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatARGB8888 + #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB888 +#elif + #error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32. +#endif + +#if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) \ + || defined (_WIN64) || defined (__LP64__) || defined (__LLP64__) + #define ALIGN_SIZE 8 +#else + #define ALIGN_SIZE 4 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * BLock Image Transfer - copy rectangular image from src buffer to dst buffer + * with combination of transformation (rotation, scale, recolor) and opacity, alpha channel + * or color keying. This requires two steps. First step is used for transformation into + * a temporary buffer and the second one will handle the color format or opacity. + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_area area to be copied from src_buf to dst_buf + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] src_buf source buffer + * @param[in] src_area source area with absolute coordinates to draw on destination buffer + * @param[in] dsc image descriptor + * @param[in] cf color format + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +static lv_res_t lv_gpu_nxp_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf); + +/** + * BLock Image Transfer - copy rectangular image from src buffer to dst buffer + * with transformation and full opacity. + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_area area to be copied from src_buf to dst_buf + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] src_buf source buffer + * @param[in] src_area source area with absolute coordinates to draw on destination buffer + * @param[in] dsc image descriptor + * @param[in] cf color format + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +static lv_res_t lv_gpu_nxp_pxp_blit_cover(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf); + +/** + * BLock Image Transfer - copy rectangular image from src buffer to dst buffer + * without transformation but handling color format or opacity. + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_area area to be copied from src_buf to dst_buf + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] src_buf source buffer + * @param[in] src_area source area with absolute coordinates to draw on destination buffer + * @param[in] dsc image descriptor + * @param[in] cf color format + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +static lv_res_t lv_gpu_nxp_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +#define ROUND_UP(x, align) ((x + (align - 1)) & ~(align - 1)) + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t opa) +{ + uint32_t area_size = lv_area_get_size(fill_area); + lv_coord_t area_w = lv_area_get_width(fill_area); + lv_coord_t area_h = lv_area_get_height(fill_area); + + if(opa >= (lv_opa_t)LV_OPA_MAX) { + if(area_size < LV_GPU_NXP_PXP_FILL_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_PXP_FILL_SIZE_LIMIT); + return LV_RES_INV; + } + } + else { + if(area_size < LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT); + return LV_RES_INV; + } + } + + PXP_Init(LV_GPU_NXP_PXP_ID); + PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/ + + /*OUT buffer configure*/ + pxp_output_buffer_config_t outputConfig = { + .pixelFormat = PXP_OUT_PIXEL_FORMAT, + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * fill_area->y1 + fill_area->x1), + .buffer1Addr = (uint32_t)NULL, + .pitchBytes = dest_stride * sizeof(lv_color_t), + .width = area_w, + .height = area_h + }; + + PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig); + + if(opa >= (lv_opa_t)LV_OPA_MAX) { + /*Simple color fill without opacity - AS disabled*/ + PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + + } + else { + /*Fill with opacity - AS used as source (same as OUT)*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = PXP_AS_PIXEL_FORMAT, + .bufferAddr = (uint32_t)outputConfig.buffer0Addr, + .pitchBytes = outputConfig.pitchBytes + }; + + PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, area_w, area_h); + } + + /*Disable PS, use as color generator*/ + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color)); + + /** + * Configure Porter-Duff blending - src settings are unused for fill without opacity (opa = 0xff). + * + * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h: + * srcFactorMode is actually applied on PS alpha value + * dstFactorMode is actually applied on AS alpha value + */ + pxp_porter_duff_config_t pdConfig = { + .enable = 1, + .dstColorMode = kPXP_PorterDuffColorNoAlpha, + .srcColorMode = kPXP_PorterDuffColorNoAlpha, + .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha, + .srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha, + .dstFactorMode = kPXP_PorterDuffFactorStraight, + .srcFactorMode = (opa >= (lv_opa_t)LV_OPA_MAX) ? kPXP_PorterDuffFactorStraight : kPXP_PorterDuffFactorInversed, + .dstGlobalAlpha = opa, + .srcGlobalAlpha = opa, + .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/ + .srcAlphaMode = kPXP_PorterDuffAlphaStraight /*don't care*/ + }; + + PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig); + + lv_gpu_nxp_pxp_run(); /*Start PXP task*/ + + return LV_RES_OK; +} + +lv_res_t lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, lv_opa_t opa, lv_disp_rot_t angle) +{ + uint32_t dest_size = lv_area_get_size(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + + if(opa >= (lv_opa_t)LV_OPA_MAX) { + if(dest_size < LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT); + return LV_RES_INV; + } + } + else { + if(dest_size < LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT); + return LV_RES_INV; + } + } + + PXP_Init(LV_GPU_NXP_PXP_ID); + PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/ + + /* convert rotation angle */ + pxp_rotate_degree_t pxp_rot; + switch(angle) { + case LV_DISP_ROT_NONE: + pxp_rot = kPXP_Rotate0; + break; + case LV_DISP_ROT_90: + pxp_rot = kPXP_Rotate90; + break; + case LV_DISP_ROT_180: + pxp_rot = kPXP_Rotate180; + break; + case LV_DISP_ROT_270: + pxp_rot = kPXP_Rotate270; + break; + default: + pxp_rot = kPXP_Rotate0; + break; + } + PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable); + + pxp_as_blend_config_t asBlendConfig = { + .alpha = opa, + .invertAlpha = false, + .alphaMode = kPXP_AlphaRop, + .ropMode = kPXP_RopMergeAs + }; + + if(opa >= (lv_opa_t)LV_OPA_MAX) { + /*Simple blit, no effect - Disable PS buffer*/ + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + } + else { + pxp_ps_buffer_config_t psBufferConfig = { + .pixelFormat = PXP_PS_PIXEL_FORMAT, + .swapByte = false, + .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1), + .bufferAddrU = 0U, + .bufferAddrV = 0U, + .pitchBytes = dest_stride * sizeof(lv_color_t) + }; + + asBlendConfig.alphaMode = kPXP_AlphaOverride; + + PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig); + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1, dest_h - 1); + } + + lv_coord_t src_stride = lv_area_get_width(src_area); + + /*AS buffer - source image*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = PXP_AS_PIXEL_FORMAT, + .bufferAddr = (uint32_t)src_buf, + .pitchBytes = src_stride * sizeof(lv_color_t) + }; + PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U); + PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig); + PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, false); + + /*Output buffer.*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT, + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride * sizeof(lv_color_t), + .width = dest_w, + .height = dest_h + }; + PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig); + + lv_gpu_nxp_pxp_run(); /* Start PXP task */ + + return LV_RES_OK; +} + +lv_res_t lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf) +{ + uint32_t dest_size = lv_area_get_size(dest_area); + + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) { + if(dest_size < LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT); + return LV_RES_INV; + } + } + else { + if(dest_size < LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT) { + PXP_LOG_TRACE("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT); + return LV_RES_INV; + } + } + + bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP); + bool rotation = (dsc->angle != 0); + + if(rotation) { + if(dsc->angle != 0 && dsc->angle != 900 && dsc->angle != 1800 && dsc->angle != 2700) { + PXP_LOG_TRACE("Rotation angle %d is not supported. PXP can rotate only 90x angle.", dsc->angle); + return LV_RES_INV; + } + } + + if(recolor || rotation) { + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_has_alpha(cf) && !lv_img_cf_is_chroma_keyed(cf)) + return lv_gpu_nxp_pxp_blit_cover(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf); + else + /*Recolor and/or rotation with alpha or opacity is done in two steps.*/ + return lv_gpu_nxp_pxp_blit_opa(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf); + } + + return lv_gpu_nxp_pxp_blit_cf(dest_buf, dest_area, dest_stride, src_buf, src_area, dsc, cf); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_res_t lv_gpu_nxp_pxp_blit_opa(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf) +{ + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_res_t res; + uint32_t size = dest_w * dest_h * sizeof(lv_color_t); + + if(ROUND_UP(size, ALIGN_SIZE) >= LV_MEM_SIZE) + PXP_RETURN_INV("Insufficient memory for temporary buffer. Please increase LV_MEM_SIZE."); + + lv_color_t * tmp_buf = (lv_color_t *)lv_mem_buf_get(size); + if(!tmp_buf) + PXP_RETURN_INV("Allocating temporary buffer failed."); + + const lv_area_t tmp_area = { + .x1 = 0, + .y1 = 0, + .x2 = dest_w - 1, + .y2 = dest_h - 1 + }; + + /*Step 1: Transform with full opacity to temporary buffer*/ + res = lv_gpu_nxp_pxp_blit_cover(tmp_buf, &tmp_area, dest_w, src_buf, src_area, dsc, cf); + if(res != LV_RES_OK) { + PXP_LOG_TRACE("Blit cover with full opacity failed."); + lv_mem_buf_release(tmp_buf); + + return res; + } + + /*Step 2: Blit temporary results with required opacity to output*/ + res = lv_gpu_nxp_pxp_blit_cf(dest_buf, dest_area, dest_stride, tmp_buf, &tmp_area, dsc, cf); + + /*Clean-up memory*/ + lv_mem_buf_release(tmp_buf); + + return res; +} + +static lv_res_t lv_gpu_nxp_pxp_blit_cover(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf) +{ + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + + bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP); + bool rotation = (dsc->angle != 0); + + PXP_Init(LV_GPU_NXP_PXP_ID); + PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/ + + if(rotation) { + /* + * PXP is set to process 16x16 blocks to optimize the system for memory + * bandwidth and image processing time. + * The output engine essentially truncates any output pixels after the + * desired number of pixels has been written. + * When rotating a source image and the output is not divisible by the block + * size, the incorrect pixels could be truncated and the final output image + * can look shifted. + */ + if(lv_area_get_width(src_area) % 16 || lv_area_get_height(src_area) % 16) { + PXP_LOG_TRACE("Rotation is not supported for image w/o alignment to block size 16x16."); + return LV_RES_INV; + } + + /*Convert rotation angle*/ + pxp_rotate_degree_t pxp_rot; + switch(dsc->angle) { + case 0: + pxp_rot = kPXP_Rotate0; + break; + case 900: + pxp_rot = kPXP_Rotate90; + break; + case 1800: + pxp_rot = kPXP_Rotate180; + break; + case 2700: + pxp_rot = kPXP_Rotate270; + break; + default: + PXP_LOG_TRACE("Rotation angle %d is not supported. PXP can rotate only 90x angle.", dsc->angle); + return LV_RES_INV; + } + PXP_SetRotateConfig(LV_GPU_NXP_PXP_ID, kPXP_RotateOutputBuffer, pxp_rot, kPXP_FlipDisable); + } + + lv_coord_t src_stride = lv_area_get_width(src_area); + + /*AS buffer - source image*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = PXP_AS_PIXEL_FORMAT, + .bufferAddr = (uint32_t)src_buf, + .pitchBytes = src_stride * sizeof(lv_color_t) + }; + PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U); + + /*Disable PS buffer*/ + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + if(recolor) + /*Use as color generator*/ + PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(dsc->recolor)); + + /*Output buffer*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT, + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride * sizeof(lv_color_t), + .width = dest_w, + .height = dest_h + }; + PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig); + + if(recolor || lv_img_cf_has_alpha(cf)) { + /** + * Configure Porter-Duff blending. + * + * Note: srcFactorMode and dstFactorMode are inverted in fsl_pxp.h: + * srcFactorMode is actually applied on PS alpha value + * dstFactorMode is actually applied on AS alpha value + */ + pxp_porter_duff_config_t pdConfig = { + .enable = 1, + .dstColorMode = kPXP_PorterDuffColorWithAlpha, + .srcColorMode = kPXP_PorterDuffColorNoAlpha, + .dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha, + .srcGlobalAlphaMode = lv_img_cf_has_alpha(cf) ? kPXP_PorterDuffLocalAlpha : kPXP_PorterDuffGlobalAlpha, + .dstFactorMode = kPXP_PorterDuffFactorStraight, + .srcFactorMode = kPXP_PorterDuffFactorInversed, + .dstGlobalAlpha = recolor ? dsc->recolor_opa : 0x00, + .srcGlobalAlpha = 0xff, + .dstAlphaMode = kPXP_PorterDuffAlphaStraight, /*don't care*/ + .srcAlphaMode = kPXP_PorterDuffAlphaStraight + }; + PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig); + } + + lv_gpu_nxp_pxp_run(); /*Start PXP task*/ + + return LV_RES_OK; +} + +static lv_res_t lv_gpu_nxp_pxp_blit_cf(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, + const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf) +{ + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + + PXP_Init(LV_GPU_NXP_PXP_ID); + PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_SetProcessBlockSize(LV_GPU_NXP_PXP_ID, kPXP_BlockSize16); /*block size 16x16 for higher performance*/ + + pxp_as_blend_config_t asBlendConfig = { + .alpha = dsc->opa, + .invertAlpha = false, + .alphaMode = kPXP_AlphaRop, + .ropMode = kPXP_RopMergeAs + }; + + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX && !lv_img_cf_is_chroma_keyed(cf) && !lv_img_cf_has_alpha(cf)) { + /*Simple blit, no effect - Disable PS buffer*/ + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); + } + else { + /*PS must be enabled to fetch background pixels. + PS and OUT buffers are the same, blend will be done in-place*/ + pxp_ps_buffer_config_t psBufferConfig = { + .pixelFormat = PXP_PS_PIXEL_FORMAT, + .swapByte = false, + .bufferAddr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1), + .bufferAddrU = 0U, + .bufferAddrV = 0U, + .pitchBytes = dest_stride * sizeof(lv_color_t) + }; + if(dsc->opa >= (lv_opa_t)LV_OPA_MAX) { + asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaEmbedded : kPXP_AlphaOverride; + } + else { + asBlendConfig.alphaMode = lv_img_cf_has_alpha(cf) ? kPXP_AlphaMultiply : kPXP_AlphaOverride; + } + PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig); + PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1, dest_h - 1); + } + + lv_coord_t src_stride = lv_area_get_width(src_area); + + /*AS buffer - source image*/ + pxp_as_buffer_config_t asBufferConfig = { + .pixelFormat = PXP_AS_PIXEL_FORMAT, + .bufferAddr = (uint32_t)src_buf, + .pitchBytes = src_stride * sizeof(lv_color_t) + }; + PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); + PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, dest_w - 1U, dest_h - 1U); + PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig); + + if(lv_img_cf_is_chroma_keyed(cf)) { + lv_color_t colorKeyLow = LV_COLOR_CHROMA_KEY; + lv_color_t colorKeyHigh = LV_COLOR_CHROMA_KEY; + + bool recolor = (dsc->recolor_opa != LV_OPA_TRANSP); + + if(recolor) { + /* New color key after recoloring */ + lv_color_t colorKey = lv_color_mix(dsc->recolor, LV_COLOR_CHROMA_KEY, dsc->recolor_opa); + + LV_COLOR_SET_R(colorKeyLow, colorKey.ch.red != 0 ? colorKey.ch.red - 1 : 0); + LV_COLOR_SET_G(colorKeyLow, colorKey.ch.green != 0 ? colorKey.ch.green - 1 : 0); + LV_COLOR_SET_B(colorKeyLow, colorKey.ch.blue != 0 ? colorKey.ch.blue - 1 : 0); + +#if LV_COLOR_DEPTH==16 + LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0x1f ? colorKey.ch.red + 1 : 0x1f); + LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0x3f ? colorKey.ch.green + 1 : 0x3f); + LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0x1f ? colorKey.ch.blue + 1 : 0x1f); +#else /*LV_COLOR_DEPTH==32*/ + LV_COLOR_SET_R(colorKeyHigh, colorKey.ch.red != 0xff ? colorKey.ch.red + 1 : 0xff); + LV_COLOR_SET_G(colorKeyHigh, colorKey.ch.green != 0xff ? colorKey.ch.green + 1 : 0xff); + LV_COLOR_SET_B(colorKeyHigh, colorKey.ch.blue != 0xff ? colorKey.ch.blue + 1 : 0xff); +#endif + } + + PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_color_to32(colorKeyLow), + lv_color_to32(colorKeyHigh)); + } + + PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, lv_img_cf_is_chroma_keyed(cf)); + + /*Output buffer.*/ + pxp_output_buffer_config_t outputBufferConfig = { + .pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT, + .interlacedMode = kPXP_OutputProgressive, + .buffer0Addr = (uint32_t)(dest_buf + dest_stride * dest_area->y1 + dest_area->x1), + .buffer1Addr = (uint32_t)0U, + .pitchBytes = dest_stride * sizeof(lv_color_t), + .width = dest_w, + .height = dest_h + }; + PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig); + + lv_gpu_nxp_pxp_run(); /* Start PXP task */ + + return LV_RES_OK; +} + +#endif /*LV_USE_GPU_NXP_PXP*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h new file mode 100644 index 000000000..43a6440de --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_draw_pxp_blend.h @@ -0,0 +1,143 @@ +/** + * @file lv_draw_pxp_blend.h + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_DRAW_PXP_BLEND_H +#define LV_DRAW_PXP_BLEND_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_GPU_NXP_PXP +#include "lv_gpu_nxp_pxp.h" +#include "../../sw/lv_draw_sw.h" + +/********************* + * DEFINES + *********************/ + +#ifndef LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT +/** Minimum area (in pixels) for image copy with 100% opacity to be handled by PXP*/ +#define LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT 5000 +#endif + +#ifndef LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT +/** Minimum area (in pixels) for image copy with transparency to be handled by PXP*/ +#define LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT 5000 +#endif + +#ifndef LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT +/** Minimum invalidated area (in pixels) to be synchronized by PXP during buffer sync */ +#define LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT 5000 +#endif + +#ifndef LV_GPU_NXP_PXP_FILL_SIZE_LIMIT +/** Minimum area (in pixels) to be filled by PXP with 100% opacity*/ +#define LV_GPU_NXP_PXP_FILL_SIZE_LIMIT 5000 +#endif + +#ifndef LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT +/** Minimum area (in pixels) to be filled by PXP with transparency*/ +#define LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT 5000 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Fill area, with optional opacity. + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] fill_area area to fill + * @param[in] color color + * @param[in] opa transparency of the color + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +lv_res_t lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color, lv_opa_t opa); + +/** + * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with effects. + * By default, image is copied directly, with optional opacity. This function can also + * rotate the display output buffer to a specified angle (90x step). + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_area destination area + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] src_buf source buffer + * @param[in] src_area source area with absolute coordinates to draw on destination buffer + * @param[in] opa opacity of the result + * @param[in] angle display rotation angle (90x) + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +lv_res_t lv_gpu_nxp_pxp_blit(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, lv_opa_t opa, lv_disp_rot_t angle); + +/** + * BLock Image Transfer - copy rectangular image from src_buf to dst_buf with transformation. + * + * + * @param[in/out] dest_buf destination buffer + * @param[in] dest_area destination area + * @param[in] dest_stride width (stride) of destination buffer in pixels + * @param[in] src_buf source buffer + * @param[in] src_area source area with absolute coordinates to draw on destination buffer + * @param[in] dsc image descriptor + * @param[in] cf color format + * @retval LV_RES_OK Fill completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +lv_res_t lv_gpu_nxp_pxp_blit_transform(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, const lv_area_t * src_area, const lv_draw_img_dsc_t * dsc, lv_img_cf_t cf); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_NXP_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_PXP_BLEND_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c new file mode 100644 index 000000000..94d242a0d --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.c @@ -0,0 +1,116 @@ +/** + * @file lv_gpu_nxp_pxp.c + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_gpu_nxp_pxp.h" + +#if LV_USE_GPU_NXP_PXP +#include "lv_gpu_nxp_pxp_osa.h" +#include "../../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * Clean & invalidate cache. + */ +static void invalidate_cache(void); + +/********************** + * STATIC VARIABLES + **********************/ + +static lv_nxp_pxp_cfg_t * pxp_cfg; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_gpu_nxp_pxp_init(void) +{ + pxp_cfg = lv_gpu_nxp_pxp_get_cfg(); + + if(!pxp_cfg || !pxp_cfg->pxp_interrupt_deinit || !pxp_cfg->pxp_interrupt_init || !pxp_cfg->pxp_run) + PXP_RETURN_INV("PXP configuration error."); + + PXP_Init(LV_GPU_NXP_PXP_ID); + PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ + PXP_EnableInterrupts(LV_GPU_NXP_PXP_ID, kPXP_CompleteInterruptEnable); + + if(pxp_cfg->pxp_interrupt_init() != LV_RES_OK) { + PXP_Deinit(LV_GPU_NXP_PXP_ID); + PXP_RETURN_INV("PXP interrupt init failed."); + } + + return LV_RES_OK; +} + +void lv_gpu_nxp_pxp_deinit(void) +{ + pxp_cfg->pxp_interrupt_deinit(); + PXP_DisableInterrupts(PXP, kPXP_CompleteInterruptEnable); + PXP_Deinit(LV_GPU_NXP_PXP_ID); +} + +void lv_gpu_nxp_pxp_run(void) +{ + /*Clean & invalidate cache*/ + invalidate_cache(); + + pxp_cfg->pxp_run(); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void invalidate_cache(void) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + if(disp->driver->clean_dcache_cb) + disp->driver->clean_dcache_cb(disp->driver); +} + +#endif /*LV_USE_GPU_NXP_PXP*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h new file mode 100644 index 000000000..e695d8f13 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp.h @@ -0,0 +1,153 @@ +/** + * @file lv_gpu_nxp_pxp.h + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_GPU_NXP_PXP_H +#define LV_GPU_NXP_PXP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" + +#if LV_USE_GPU_NXP_PXP +#include "fsl_cache.h" +#include "fsl_pxp.h" + +#include "../../../misc/lv_log.h" + +/********************* + * DEFINES + *********************/ + +/** PXP module instance to use*/ +#define LV_GPU_NXP_PXP_ID PXP + +/** PXP interrupt line ID*/ +#define LV_GPU_NXP_PXP_IRQ_ID PXP_IRQn + +#ifndef LV_GPU_NXP_PXP_LOG_ERRORS +/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/ +#define LV_GPU_NXP_PXP_LOG_ERRORS 1 +#endif + +#ifndef LV_GPU_NXP_PXP_LOG_TRACES +/** Enable logging of PXP errors (\see LV_LOG_ERROR)*/ +#define LV_GPU_NXP_PXP_LOG_TRACES 0 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/** + * NXP PXP device configuration - call-backs used for + * interrupt init/wait/deinit. + */ +typedef struct { + /** Callback for PXP interrupt initialization*/ + lv_res_t (*pxp_interrupt_init)(void); + + /** Callback for PXP interrupt de-initialization*/ + void (*pxp_interrupt_deinit)(void); + + /** Callback that should start PXP and wait for operation complete*/ + void (*pxp_run)(void); +} lv_nxp_pxp_cfg_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Reset and initialize PXP device. This function should be called as a part + * of display init sequence. + * + * @retval LV_RES_OK PXP init completed + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_PXP_LOG_ERRORS) + */ +lv_res_t lv_gpu_nxp_pxp_init(void); + +/** + * Disable PXP device. Should be called during display deinit sequence. + */ +void lv_gpu_nxp_pxp_deinit(void); + +/** + * Start PXP job and wait for completion. + */ +void lv_gpu_nxp_pxp_run(void); + +/********************** + * MACROS + **********************/ + +#define PXP_COND_STOP(cond, txt) \ + do { \ + if (cond) { \ + LV_LOG_ERROR("%s. STOP!", txt); \ + for ( ; ; ); \ + } \ + } while(0) + +#if LV_GPU_NXP_PXP_LOG_ERRORS +#define PXP_RETURN_INV(fmt, ...) \ + do { \ + LV_LOG_ERROR(fmt, ##__VA_ARGS__); \ + return LV_RES_INV; \ + } while (0) +#else +#define PXP_RETURN_INV(fmt, ...) \ + do { \ + return LV_RES_INV; \ + }while(0) +#endif /*LV_GPU_NXP_PXP_LOG_ERRORS*/ + +#if LV_GPU_NXP_PXP_LOG_TRACES +#define PXP_LOG_TRACE(fmt, ...) \ + do { \ + LV_LOG_ERROR(fmt, ##__VA_ARGS__); \ + } while (0) +#else +#define PXP_LOG_TRACE(fmt, ...) \ + do { \ + } while (0) +#endif /*LV_GPU_NXP_PXP_LOG_TRACES*/ + +#endif /*LV_USE_GPU_NXP_PXP*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_NXP_PXP_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c similarity index 89% rename from lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.c rename to lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c index 1203434c7..c4b8dbe57 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.c @@ -6,7 +6,7 @@ /** * MIT License * - * Copyright (c) 2020 NXP + * Copyright 2020, 2022 NXP * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,11 +32,9 @@ *********************/ #include "lv_gpu_nxp_pxp_osa.h" + #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT -#include "../misc/lv_log.h" - - -#include "lv_gpu_nxp_pxp.h" +#include "../../../misc/lv_log.h" #include "fsl_pxp.h" #if defined(SDK_OS_FREE_RTOS) @@ -55,8 +53,20 @@ /********************** * STATIC PROTOTYPES **********************/ + +/** + * PXP interrupt initialization. + */ static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void); + +/** + * PXP interrupt de-initialization. + */ static void _lv_gpu_nxp_pxp_interrupt_deinit(void); + +/** + * Start the PXP job and wait for task completion. + */ static void _lv_gpu_nxp_pxp_run(void); /********************** @@ -69,6 +79,12 @@ static void _lv_gpu_nxp_pxp_run(void); static volatile bool s_pxpIdle; #endif +static lv_nxp_pxp_cfg_t pxp_default_cfg = { + .pxp_interrupt_init = _lv_gpu_nxp_pxp_interrupt_init, + .pxp_interrupt_deinit = _lv_gpu_nxp_pxp_interrupt_deinit, + .pxp_run = _lv_gpu_nxp_pxp_run +}; + /********************** * MACROS **********************/ @@ -77,9 +93,6 @@ static void _lv_gpu_nxp_pxp_run(void); * GLOBAL FUNCTIONS **********************/ -/** - * PXP device interrupt handler. Used to check PXP task completion status. - */ void PXP_IRQHandler(void) { #if defined(SDK_OS_FREE_RTOS) @@ -94,24 +107,24 @@ void PXP_IRQHandler(void) #else s_pxpIdle = true; #endif - } } +lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void) +{ + return &pxp_default_cfg; +} + /********************** * STATIC FUNCTIONS **********************/ -/** - * PXP interrupt initialization. - */ static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void) { #if defined(SDK_OS_FREE_RTOS) s_pxpIdle = xSemaphoreCreateBinary(); - if(s_pxpIdle == NULL) { + if(s_pxpIdle == NULL) return LV_RES_INV; - } NVIC_SetPriority(LV_GPU_NXP_PXP_IRQ_ID, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1); #else @@ -123,9 +136,6 @@ static lv_res_t _lv_gpu_nxp_pxp_interrupt_init(void) return LV_RES_OK; } -/** - * PXP interrupt de-initialization. - */ static void _lv_gpu_nxp_pxp_interrupt_deinit(void) { NVIC_DisableIRQ(LV_GPU_NXP_PXP_IRQ_ID); @@ -134,9 +144,6 @@ static void _lv_gpu_nxp_pxp_interrupt_deinit(void) #endif } -/** - * Function to start PXP job. This function must wait for task complete. - */ static void _lv_gpu_nxp_pxp_run(void) { #if !defined(SDK_OS_FREE_RTOS) @@ -147,20 +154,11 @@ static void _lv_gpu_nxp_pxp_run(void) PXP_Start(LV_GPU_NXP_PXP_ID); #if defined(SDK_OS_FREE_RTOS) - if(xSemaphoreTake(s_pxpIdle, portMAX_DELAY) != pdTRUE) { - LV_LOG_ERROR("xSemaphoreTake error. Task halted."); - for(; ;) ; - } + PXP_COND_STOP(!xSemaphoreTake(s_pxpIdle, portMAX_DELAY), "xSemaphoreTake failed."); #else while(s_pxpIdle == false) { } #endif } -lv_nxp_pxp_cfg_t pxp_default_cfg = { - .pxp_interrupt_init = _lv_gpu_nxp_pxp_interrupt_init, - .pxp_interrupt_deinit = _lv_gpu_nxp_pxp_interrupt_deinit, - .pxp_run = _lv_gpu_nxp_pxp_run -}; - #endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h similarity index 62% rename from lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.h rename to lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h index 89524dae5..5c87824ab 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp_osa.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/pxp/lv_gpu_nxp_pxp_osa.h @@ -6,7 +6,7 @@ /** * MIT License * - * Copyright (c) 2020 NXP + * Copyright 2020, 2022 NXP * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,21 +27,52 @@ * */ -#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_ -#define LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_ +#ifndef LV_GPU_NXP_PXP_OSA_H +#define LV_GPU_NXP_PXP_OSA_H #ifdef __cplusplus extern "C" { #endif -#include "../../lv_conf_internal.h" +/********************* + * INCLUDES + *********************/ + +#include "../../../lv_conf_internal.h" #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT -extern lv_nxp_pxp_cfg_t pxp_default_cfg; -#endif +#include "lv_gpu_nxp_pxp.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * PXP device interrupt handler. Used to check PXP task completion status. + */ +void PXP_IRQHandler(void); + +/** + * Helper function to get the PXP default configuration. + */ +lv_nxp_pxp_cfg_t * lv_gpu_nxp_pxp_get_cfg(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT*/ #ifdef __cplusplus } /*extern "C"*/ #endif -#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_PXP_OSA_H_*/ +#endif /*LV_GPU_NXP_PXP_OSA_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk new file mode 100644 index 000000000..c84e2e47a --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_nxp_vglite.mk @@ -0,0 +1,9 @@ +CSRCS += lv_draw_vglite_arc.c +CSRCS += lv_draw_vglite_blend.c +CSRCS += lv_draw_vglite_rect.c +CSRCS += lv_gpu_nxp_vglite.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/nxp/vglite" diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c new file mode 100644 index 000000000..194f03d88 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.c @@ -0,0 +1,699 @@ +/** + * @file lv_draw_vglite_arc.c + * + */ + +/** + * MIT License + * + * Copyright 2021, 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite_arc.h" + +#if LV_USE_GPU_NXP_VG_LITE +#include "math.h" + +/********************* + * DEFINES + *********************/ + +#define T_FRACTION 16384.0f + +#define DICHOTO_ITER 5 + +static const uint16_t TperDegree[90] = { + 0, 174, 348, 522, 697, 873, 1049, 1226, 1403, 1581, + 1759, 1938, 2117, 2297, 2477, 2658, 2839, 3020, 3202, 3384, + 3567, 3749, 3933, 4116, 4300, 4484, 4668, 4852, 5037, 5222, + 5407, 5592, 5777, 5962, 6148, 6334, 6519, 6705, 6891, 7077, + 7264, 7450, 7636, 7822, 8008, 8193, 8378, 8564, 8750, 8936, + 9122, 9309, 9495, 9681, 9867, 10052, 10238, 10424, 10609, 10794, + 10979, 11164, 11349, 11534, 11718, 11902, 12086, 12270, 12453, 12637, + 12819, 13002, 13184, 13366, 13547, 13728, 13909, 14089, 14269, 14448, + 14627, 14805, 14983, 15160, 15337, 15513, 15689, 15864, 16038, 16212 +}; + +/********************** + * TYPEDEFS + **********************/ + +/* intermediate arc params */ +typedef struct _vg_arc { + int32_t angle; /* angle <90deg */ + int32_t quarter; /* 0-3 counter-clockwise */ + int32_t rad; /* radius */ + int32_t p0x; /* point P0 */ + int32_t p0y; + int32_t p1x; /* point P1 */ + int32_t p1y; + int32_t p2x; /* point P2 */ + int32_t p2y; + int32_t p3x; /* point P3 */ + int32_t p3y; +} vg_arc; + +typedef struct _cubic_cont_pt { + float p0; + float p1; + float p2; + float p3; +} cubic_cont_pt; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void rotate_point(int32_t angle, int32_t * x, int32_t * y); +static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius, + int32_t start_angle, int32_t end_angle, lv_point_t center, bool cw); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_gpu_nxp_vglite_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + int32_t radius, int32_t start_angle, int32_t end_angle) +{ + + vg_lite_buffer_t vgbuf; + vg_lite_error_t err = VG_LITE_SUCCESS; + lv_color32_t col32 = {.full = lv_color_to32(dsc->color)}; /*Convert color to RGBA8888*/ + lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area); + lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area); + vg_lite_path_t path; + vg_lite_color_t vgcol; /* vglite takes ABGR */ + vg_lite_matrix_t matrix; + lv_opa_t opa = dsc->opa; + bool donut = ((end_angle - start_angle) % 360 == 0) ? true : false; + lv_point_t clip_center = {center->x - draw_ctx->buf_area->x1, center->y - draw_ctx->buf_area->y1}; + + /* path: max size = 16 cubic bezier (7 words each) */ + int32_t arc_path[16 * 7]; + lv_memset_00(arc_path, sizeof(arc_path)); + + /*** Init destination buffer ***/ + if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t), + (const lv_color_t *)draw_ctx->buf, false) != LV_RES_OK) + VG_LITE_RETURN_INV("Init buffer failed."); + + /*** Init path ***/ + lv_coord_t width = dsc->width; /* inner arc radius = outer arc radius - width */ + if(width > (lv_coord_t)radius) + width = radius; + + int pidx = 0; + int32_t cp_x, cp_y; /* control point coords */ + + /* first control point of curve */ + cp_x = radius; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_MOVE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + + /* draw 1-5 outer quarters */ + add_arc_path(arc_path, &pidx, radius, start_angle, end_angle, clip_center, true); + + if(donut) { + /* close outer circle */ + cp_x = radius; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_LINE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + /* start inner circle */ + cp_x = radius - width; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_MOVE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + + } + else if(dsc->rounded != 0U) { /* 1st rounded arc ending */ + cp_x = radius - width / 2; + cp_y = 0; + rotate_point(end_angle, &cp_x, &cp_y); + lv_point_t round_center = {clip_center.x + cp_x, clip_center.y + cp_y}; + add_arc_path(arc_path, &pidx, width / 2, end_angle, (end_angle + 180), + round_center, true); + + } + else { /* 1st flat ending */ + cp_x = radius - width; + cp_y = 0; + rotate_point(end_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_LINE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + } + + /* draw 1-5 inner quarters */ + add_arc_path(arc_path, &pidx, radius - width, start_angle, end_angle, clip_center, false); + + /* last control point of curve */ + if(donut) { /* close the loop */ + cp_x = radius - width; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_LINE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + + } + else if(dsc->rounded != 0U) { /* 2nd rounded arc ending */ + cp_x = radius - width / 2; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + lv_point_t round_center = {clip_center.x + cp_x, clip_center.y + cp_y}; + add_arc_path(arc_path, &pidx, width / 2, (start_angle + 180), (start_angle + 360), + round_center, true); + + } + else { /* 2nd flat ending */ + cp_x = radius; + cp_y = 0; + rotate_point(start_angle, &cp_x, &cp_y); + arc_path[pidx++] = VLC_OP_LINE; + arc_path[pidx++] = clip_center.x + cp_x; + arc_path[pidx++] = clip_center.y + cp_y; + } + + arc_path[pidx++] = VLC_OP_END; + + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, (uint32_t)pidx * sizeof(int32_t), arc_path, + (vg_lite_float_t) draw_ctx->clip_area->x1, (vg_lite_float_t) draw_ctx->clip_area->y1, + ((vg_lite_float_t) draw_ctx->clip_area->x2) + 1.0f, ((vg_lite_float_t) draw_ctx->clip_area->y2) + 1.0f); + VG_LITE_ERR_RETURN_INV(err, "Init path failed."); + + /* set rotation angle */ + vg_lite_identity(&matrix); + + if(opa <= (lv_opa_t)LV_OPA_MAX) { + /* Only pre-multiply color if hardware pre-multiplication is not present */ + if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) { + col32.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); + col32.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8); + col32.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8); + } + col32.ch.alpha = opa; + } + +#if LV_COLOR_DEPTH==16 + vgcol = col32.full; +#else /*LV_COLOR_DEPTH==32*/ + vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) | + (uint32_t)col32.ch.red; +#endif + + /*Clean & invalidate cache*/ + lv_vglite_invalidate_cache(); + + /*** Draw arc ***/ + err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_NON_ZERO, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + VG_LITE_ERR_RETURN_INV(err, "Draw arc failed."); + + err = vg_lite_finish(); + VG_LITE_ERR_RETURN_INV(err, "Finish failed."); + + err = vg_lite_clear_path(&path); + VG_LITE_ERR_RETURN_INV(err, "Clear path failed."); + + return LV_RES_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void copy_arc(vg_arc * dst, vg_arc * src) +{ + dst->quarter = src->quarter; + dst->rad = src->rad; + dst->angle = src->angle; + dst->p0x = src->p0x; + dst->p1x = src->p1x; + dst->p2x = src->p2x; + dst->p3x = src->p3x; + dst->p0y = src->p0y; + dst->p1y = src->p1y; + dst->p2y = src->p2y; + dst->p3y = src->p3y; +} + +/** + * Rotate the point according given rotation angle rotation center is 0,0 + */ +static void rotate_point(int32_t angle, int32_t * x, int32_t * y) +{ + int32_t ori_x = *x; + int32_t ori_y = *y; + int16_t alpha = (int16_t)angle; + *x = ((lv_trigo_cos(alpha) * ori_x) / LV_TRIGO_SIN_MAX) - ((lv_trigo_sin(alpha) * ori_y) / LV_TRIGO_SIN_MAX); + *y = ((lv_trigo_sin(alpha) * ori_x) / LV_TRIGO_SIN_MAX) + ((lv_trigo_cos(alpha) * ori_y) / LV_TRIGO_SIN_MAX); +} + +/** + * Set full arc control points depending on quarter. + * Control points match the best approximation of a circle. + * Arc Quarter position is: + * Q2 | Q3 + * ---+--- + * Q1 | Q0 + */ +static void set_full_arc(vg_arc * fullarc) +{ + /* the tangent lenght for the bezier circle approx */ + float tang = ((float)fullarc->rad) * BEZIER_OPTIM_CIRCLE; + switch(fullarc->quarter) { + case 0: + /* first quarter */ + fullarc->p0x = fullarc->rad; + fullarc->p0y = 0; + fullarc->p1x = fullarc->rad; + fullarc->p1y = (int32_t)tang; + fullarc->p2x = (int32_t)tang; + fullarc->p2y = fullarc->rad; + fullarc->p3x = 0; + fullarc->p3y = fullarc->rad; + break; + case 1: + /* second quarter */ + fullarc->p0x = 0; + fullarc->p0y = fullarc->rad; + fullarc->p1x = 0 - (int32_t)tang; + fullarc->p1y = fullarc->rad; + fullarc->p2x = 0 - fullarc->rad; + fullarc->p2y = (int32_t)tang; + fullarc->p3x = 0 - fullarc->rad; + fullarc->p3y = 0; + break; + case 2: + /* third quarter */ + fullarc->p0x = 0 - fullarc->rad; + fullarc->p0y = 0; + fullarc->p1x = 0 - fullarc->rad; + fullarc->p1y = 0 - (int32_t)tang; + fullarc->p2x = 0 - (int32_t)tang; + fullarc->p2y = 0 - fullarc->rad; + fullarc->p3x = 0; + fullarc->p3y = 0 - fullarc->rad; + break; + case 3: + /* fourth quarter */ + fullarc->p0x = 0; + fullarc->p0y = 0 - fullarc->rad; + fullarc->p1x = (int32_t)tang; + fullarc->p1y = 0 - fullarc->rad; + fullarc->p2x = fullarc->rad; + fullarc->p2y = 0 - (int32_t)tang; + fullarc->p3x = fullarc->rad; + fullarc->p3y = 0; + break; + default: + LV_LOG_ERROR("Invalid arc quarter value."); + break; + } +} + +/** + * Linear interpolation between two points 'a' and 'b' + * 't' parameter is the proportion ratio expressed in range [0 ; T_FRACTION ] + */ +static inline float lerp(float coord_a, float coord_b, uint16_t t) +{ + float tf = (float)t; + return ((T_FRACTION - tf) * coord_a + tf * coord_b) / T_FRACTION; +} + +/** + * Computes a point of bezier curve given 't' param + */ +static inline float comp_bezier_point(float t, cubic_cont_pt cp) +{ + float t_sq = t * t; + float inv_t_sq = (1.0f - t) * (1.0f - t); + float apt = (1.0f - t) * inv_t_sq * cp.p0 + 3.0f * inv_t_sq * t * cp.p1 + 3.0f * (1.0f - t) * t_sq * cp.p2 + t * t_sq * + cp.p3; + return apt; +} + +/** + * Find parameter 't' in curve at point 'pt' + * proceed by dichotomy on only 1 dimension, + * works only if the curve is monotonic + * bezier curve is defined by control points [p0 p1 p2 p3] + * 'dec' tells if curve is decreasing (true) or increasing (false) + */ +static uint16_t get_bez_t_from_pos(float pt, cubic_cont_pt cp, bool dec) +{ + /* initialize dichotomy with boundary 't' values */ + float t_low = 0.0f; + float t_mid = 0.5f; + float t_hig = 1.0f; + float a_pt; + /* dichotomy loop */ + for(int i = 0; i < DICHOTO_ITER; i++) { + a_pt = comp_bezier_point(t_mid, cp); + /* check mid-point position on bezier curve versus targeted point */ + if((a_pt > pt) != dec) { + t_hig = t_mid; + } + else { + t_low = t_mid; + } + /* define new 't' param for mid-point */ + t_mid = (t_low + t_hig) / 2.0f; + } + /* return parameter 't' in integer range [0 ; T_FRACTION] */ + return (uint16_t)floorf(t_mid * T_FRACTION + 0.5f); +} + +/** + * Gives relative coords of the control points + * for the sub-arc starting at angle with given angle span + */ +static void get_subarc_control_points(vg_arc * arc, int32_t span) +{ + vg_arc fullarc; + fullarc.angle = arc->angle; + fullarc.quarter = arc->quarter; + fullarc.rad = arc->rad; + set_full_arc(&fullarc); + + /* special case of full arc */ + if(arc->angle == 90) { + copy_arc(arc, &fullarc); + return; + } + + /* compute 1st arc using the geometric construction of curve */ + uint16_t t2 = TperDegree[arc->angle + span]; + + /* lerp for A */ + float a2x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t2); + float a2y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t2); + /* lerp for B */ + float b2x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t2); + float b2y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t2); + /* lerp for C */ + float c2x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t2); + float c2y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t2); + + /* lerp for D */ + float d2x = lerp(a2x, b2x, t2); + float d2y = lerp(a2y, b2y, t2); + /* lerp for E */ + float e2x = lerp(b2x, c2x, t2); + float e2y = lerp(b2y, c2y, t2); + + float pt2x = lerp(d2x, e2x, t2); + float pt2y = lerp(d2y, e2y, t2); + + /* compute sub-arc using the geometric construction of curve */ + uint16_t t1 = TperDegree[arc->angle]; + + /* lerp for A */ + float a1x = lerp((float)fullarc.p0x, (float)fullarc.p1x, t1); + float a1y = lerp((float)fullarc.p0y, (float)fullarc.p1y, t1); + /* lerp for B */ + float b1x = lerp((float)fullarc.p1x, (float)fullarc.p2x, t1); + float b1y = lerp((float)fullarc.p1y, (float)fullarc.p2y, t1); + /* lerp for C */ + float c1x = lerp((float)fullarc.p2x, (float)fullarc.p3x, t1); + float c1y = lerp((float)fullarc.p2y, (float)fullarc.p3y, t1); + + /* lerp for D */ + float d1x = lerp(a1x, b1x, t1); + float d1y = lerp(a1y, b1y, t1); + /* lerp for E */ + float e1x = lerp(b1x, c1x, t1); + float e1y = lerp(b1y, c1y, t1); + + float pt1x = lerp(d1x, e1x, t1); + float pt1y = lerp(d1y, e1y, t1); + + /* find the 't3' parameter for point P(t1) on the sub-arc [P0 A2 D2 P(t2)] using dichotomy + * use position of x axis only */ + uint16_t t3; + t3 = get_bez_t_from_pos(pt1x, + (cubic_cont_pt) { + .p0 = ((float)fullarc.p0x), .p1 = a2x, .p2 = d2x, .p3 = pt2x + }, + (bool)(pt2x < (float)fullarc.p0x)); + + /* lerp for B */ + float b3x = lerp(a2x, d2x, t3); + float b3y = lerp(a2y, d2y, t3); + /* lerp for C */ + float c3x = lerp(d2x, pt2x, t3); + float c3y = lerp(d2y, pt2y, t3); + + /* lerp for E */ + float e3x = lerp(b3x, c3x, t3); + float e3y = lerp(b3y, c3y, t3); + + arc->p0x = (int32_t)floorf(0.5f + pt1x); + arc->p0y = (int32_t)floorf(0.5f + pt1y); + arc->p1x = (int32_t)floorf(0.5f + e3x); + arc->p1y = (int32_t)floorf(0.5f + e3y); + arc->p2x = (int32_t)floorf(0.5f + c3x); + arc->p2y = (int32_t)floorf(0.5f + c3y); + arc->p3x = (int32_t)floorf(0.5f + pt2x); + arc->p3y = (int32_t)floorf(0.5f + pt2y); +} + +/** + * Gives relative coords of the control points + */ +static void get_arc_control_points(vg_arc * arc, bool start) +{ + vg_arc fullarc; + fullarc.angle = arc->angle; + fullarc.quarter = arc->quarter; + fullarc.rad = arc->rad; + set_full_arc(&fullarc); + + /* special case of full arc */ + if(arc->angle == 90) { + copy_arc(arc, &fullarc); + return; + } + + /* compute sub-arc using the geometric construction of curve */ + uint16_t t = TperDegree[arc->angle]; + /* lerp for A */ + float ax = lerp((float)fullarc.p0x, (float)fullarc.p1x, t); + float ay = lerp((float)fullarc.p0y, (float)fullarc.p1y, t); + /* lerp for B */ + float bx = lerp((float)fullarc.p1x, (float)fullarc.p2x, t); + float by = lerp((float)fullarc.p1y, (float)fullarc.p2y, t); + /* lerp for C */ + float cx = lerp((float)fullarc.p2x, (float)fullarc.p3x, t); + float cy = lerp((float)fullarc.p2y, (float)fullarc.p3y, t); + + /* lerp for D */ + float dx = lerp(ax, bx, t); + float dy = lerp(ay, by, t); + /* lerp for E */ + float ex = lerp(bx, cx, t); + float ey = lerp(by, cy, t); + + /* sub-arc's control points are tangents of DeCasteljau's algorithm */ + if(start) { + arc->p0x = (int32_t)floorf(0.5f + lerp(dx, ex, t)); + arc->p0y = (int32_t)floorf(0.5f + lerp(dy, ey, t)); + arc->p1x = (int32_t)floorf(0.5f + ex); + arc->p1y = (int32_t)floorf(0.5f + ey); + arc->p2x = (int32_t)floorf(0.5f + cx); + arc->p2y = (int32_t)floorf(0.5f + cy); + arc->p3x = fullarc.p3x; + arc->p3y = fullarc.p3y; + } + else { + arc->p0x = fullarc.p0x; + arc->p0y = fullarc.p0y; + arc->p1x = (int32_t)floorf(0.5f + ax); + arc->p1y = (int32_t)floorf(0.5f + ay); + arc->p2x = (int32_t)floorf(0.5f + dx); + arc->p2y = (int32_t)floorf(0.5f + dy); + arc->p3x = (int32_t)floorf(0.5f + lerp(dx, ex, t)); + arc->p3y = (int32_t)floorf(0.5f + lerp(dy, ey, t)); + } +} + +/** + * Add the arc control points into the path data for vglite, + * taking into account the real center of the arc (translation). + * arc_path: (in/out) the path data array for vglite + * pidx: (in/out) index of last element added in arc_path + * q_arc: (in) the arc data containing control points + * center: (in) the center of the circle in draw coordinates + * cw: (in) true if arc is clockwise + */ +static void add_split_arc_path(int32_t * arc_path, int * pidx, vg_arc * q_arc, lv_point_t center, bool cw) +{ + /* assumes first control point already in array arc_path[] */ + int idx = *pidx; + if(cw) { +#if BEZIER_DBG_CONTROL_POINTS + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p1x + center.x; + arc_path[idx++] = q_arc->p1y + center.y; + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p2x + center.x; + arc_path[idx++] = q_arc->p2y + center.y; + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p3x + center.x; + arc_path[idx++] = q_arc->p3y + center.y; +#else + arc_path[idx++] = VLC_OP_CUBIC; + arc_path[idx++] = q_arc->p1x + center.x; + arc_path[idx++] = q_arc->p1y + center.y; + arc_path[idx++] = q_arc->p2x + center.x; + arc_path[idx++] = q_arc->p2y + center.y; + arc_path[idx++] = q_arc->p3x + center.x; + arc_path[idx++] = q_arc->p3y + center.y; +#endif + } + else { /* reverse points order when counter-clockwise */ +#if BEZIER_DBG_CONTROL_POINTS + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p2x + center.x; + arc_path[idx++] = q_arc->p2y + center.y; + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p1x + center.x; + arc_path[idx++] = q_arc->p1y + center.y; + arc_path[idx++] = VLC_OP_LINE; + arc_path[idx++] = q_arc->p0x + center.x; + arc_path[idx++] = q_arc->p0y + center.y; +#else + arc_path[idx++] = VLC_OP_CUBIC; + arc_path[idx++] = q_arc->p2x + center.x; + arc_path[idx++] = q_arc->p2y + center.y; + arc_path[idx++] = q_arc->p1x + center.x; + arc_path[idx++] = q_arc->p1y + center.y; + arc_path[idx++] = q_arc->p0x + center.x; + arc_path[idx++] = q_arc->p0y + center.y; +#endif + } + /* update index i n path array*/ + *pidx = idx; +} + +static void add_arc_path(int32_t * arc_path, int * pidx, int32_t radius, + int32_t start_angle, int32_t end_angle, lv_point_t center, bool cw) +{ + /* set number of arcs to draw */ + vg_arc q_arc; + int32_t start_arc_angle = start_angle % 90; + int32_t end_arc_angle = end_angle % 90; + int32_t inv_start_arc_angle = (start_arc_angle > 0) ? (90 - start_arc_angle) : 0; + int32_t nbarc = (end_angle - start_angle - inv_start_arc_angle - end_arc_angle) / 90; + q_arc.rad = radius; + + /* handle special case of start & end point in the same quarter */ + if(((start_angle / 90) == (end_angle / 90)) && (nbarc <= 0)) { + q_arc.quarter = (start_angle / 90) % 4; + q_arc.angle = start_arc_angle; + get_subarc_control_points(&q_arc, end_arc_angle - start_arc_angle); + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + return; + } + + if(cw) { + /* partial starting arc */ + if(start_arc_angle > 0) { + q_arc.quarter = (start_angle / 90) % 4; + q_arc.angle = start_arc_angle; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, true); + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + /* full arcs */ + for(int32_t q = 0; q < nbarc ; q++) { + q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4; + q_arc.angle = 90; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */ + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + /* partial ending arc */ + if(end_arc_angle > 0) { + q_arc.quarter = (end_angle / 90) % 4; + q_arc.angle = end_arc_angle; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, false); + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + + } + else { /* counter clockwise */ + + /* partial ending arc */ + if(end_arc_angle > 0) { + q_arc.quarter = (end_angle / 90) % 4; + q_arc.angle = end_arc_angle; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, false); + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + /* full arcs */ + for(int32_t q = nbarc - 1; q >= 0; q--) { + q_arc.quarter = (q + ((start_angle + 89) / 90)) % 4; + q_arc.angle = 90; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, true); /* 2nd parameter 'start' ignored */ + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + /* partial starting arc */ + if(start_arc_angle > 0) { + q_arc.quarter = (start_angle / 90) % 4; + q_arc.angle = start_arc_angle; + /* get cubic points relative to center */ + get_arc_control_points(&q_arc, true); + /* put cubic points in arc_path */ + add_split_arc_path(arc_path, pidx, &q_arc, center, cw); + } + } +} + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h new file mode 100644 index 000000000..98ba8a3d0 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_arc.h @@ -0,0 +1,79 @@ +/** + * @file lv_draw_vglite_arc.h + * + */ + +/** + * MIT License + * + * Copyright 2021, 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_DRAW_VGLITE_ARC_H +#define LV_DRAW_VGLITE_ARC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_GPU_NXP_VG_LITE +#include "lv_gpu_nxp_vglite.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*** + * Draw arc shape with effects + * @param draw_ctx drawing context + * @param dsc the arc description structure (width, rounded ending, opacity) + * @param center the coordinates of the arc center + * @param radius the radius of external arc + * @param start_angle the starting angle in degrees + * @param end_angle the ending angle in degrees + */ +lv_res_t lv_gpu_nxp_vglite_draw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center, + int32_t radius, int32_t start_angle, int32_t end_angle); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_VGLITE_ARC_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c new file mode 100644 index 000000000..b59b143b3 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.c @@ -0,0 +1,618 @@ +/** + * @file lv_draw_vglite_blend.c + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite_blend.h" + +#if LV_USE_GPU_NXP_VG_LITE + +/********************* + * DEFINES + *********************/ + +/* Enable BLIT quality degradation workaround for RT595, recommended for screen's dimension > 352 pixels */ +#define RT595_BLIT_WRKRND_ENABLED 1 + +/* Internal compound symbol */ +#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \ + defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \ + RT595_BLIT_WRKRND_ENABLED +#define VG_LITE_BLIT_SPLIT_ENABLED 1 +#else +#define VG_LITE_BLIT_SPLIT_ENABLED 0 +#endif + +/** + * BLIT split threshold - BLITs with width or height higher than this value will be done + * in multiple steps. Value must be 16-aligned. Don't change. + */ +#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352 + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/** + * BLock Image Transfer - single direct BLIT. + * + * @param[in] blit Description of the transfer + * @retval LV_RES_OK Transfer complete + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) + */ +static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit); + +#if VG_LITE_BLIT_SPLIT_ENABLED + + /** + * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only. + * + * @param[in,out] area Area to be updated + * @param[in,out] buf Pointer to be updated + */ + static void _align_x(lv_area_t * area, lv_color_t ** buf); + + /** + * Move buffer pointer to the area start and update variables, Y-axis only. + * + * @param[in,out] area Area to be updated + * @param[in,out] buf Pointer to be updated + * @param[in] stridePx Buffer stride in pixels + */ + static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx); + + /** + * Software BLIT as a fall-back scenario. + * + * @param[in] blit BLIT configuration + */ + static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit); + + /** + * Verify BLIT structure - widths, stride, pointer alignment + * + * @param[in] blit BLIT configuration + * @retval LV_RES_OK + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) + */ + static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit); + + /** + * BLock Image Transfer - split BLIT. + * + * @param[in] blit BLIT configuration + * @retval LV_RES_OK Transfer complete + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) + */ + static lv_res_t _lv_gpu_nxp_vglite_blit_split(lv_gpu_nxp_vglite_blit_info_t * blit); +#endif + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, + const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa) +{ + uint32_t area_size = lv_area_get_size(fill_area); + lv_coord_t area_w = lv_area_get_width(fill_area); + lv_coord_t area_h = lv_area_get_height(fill_area); + + if(opa >= (lv_opa_t)LV_OPA_MAX) { + if(area_size < LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT); + } + else { + if(area_size < LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", area_size, LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT); + } + + vg_lite_buffer_t vgbuf; + vg_lite_rectangle_t rect; + vg_lite_error_t err = VG_LITE_SUCCESS; + lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/ + vg_lite_color_t vgcol; /* vglite takes ABGR */ + + if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t), + (const lv_color_t *)dest_buf, false) != LV_RES_OK) + VG_LITE_RETURN_INV("Init buffer failed."); + + if(opa >= (lv_opa_t)LV_OPA_MAX) { /*Opaque fill*/ + rect.x = fill_area->x1; + rect.y = fill_area->y1; + rect.width = area_w; + rect.height = area_h; + + /*Clean & invalidate cache*/ + lv_vglite_invalidate_cache(); + +#if LV_COLOR_DEPTH==16 + vgcol = col32.full; +#else /*LV_COLOR_DEPTH==32*/ + vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) | + (uint32_t)col32.ch.red; +#endif + + err = vg_lite_clear(&vgbuf, &rect, vgcol); + VG_LITE_ERR_RETURN_INV(err, "Clear failed."); + + err = vg_lite_finish(); + VG_LITE_ERR_RETURN_INV(err, "Finish failed."); + } + else { /*fill with transparency*/ + + vg_lite_path_t path; + int32_t path_data[] = { /*VG rectangular path*/ + VLC_OP_MOVE, fill_area->x1, fill_area->y1, + VLC_OP_LINE, fill_area->x2 + 1, fill_area->y1, + VLC_OP_LINE, fill_area->x2 + 1, fill_area->y2 + 1, + VLC_OP_LINE, fill_area->x1, fill_area->y2 + 1, + VLC_OP_LINE, fill_area->x1, fill_area->y1, + VLC_OP_END + }; + + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(path_data), path_data, + (vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, + ((vg_lite_float_t) fill_area->x2) + 1.0f, ((vg_lite_float_t) fill_area->y2) + 1.0f); + VG_LITE_ERR_RETURN_INV(err, "Init path failed."); + + /* Only pre-multiply color if hardware pre-multiplication is not present */ + if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) { + col32.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); + col32.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8); + col32.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8); + } + col32.ch.alpha = opa; + +#if LV_COLOR_DEPTH==16 + vgcol = col32.full; +#else /*LV_COLOR_DEPTH==32*/ + vgcol = ((uint32_t)col32.ch.alpha << 24) | ((uint32_t)col32.ch.blue << 16) | ((uint32_t)col32.ch.green << 8) | + (uint32_t)col32.ch.red; +#endif + + /*Clean & invalidate cache*/ + lv_vglite_invalidate_cache(); + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + + /*Draw rectangle*/ + err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + VG_LITE_ERR_RETURN_INV(err, "Draw rectangle failed."); + + err = vg_lite_finish(); + VG_LITE_ERR_RETURN_INV(err, "Finish failed."); + + err = vg_lite_clear_path(&path); + VG_LITE_ERR_RETURN_INV(err, "Clear path failed."); + } + + return LV_RES_OK; +} + +lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + uint32_t dest_size = lv_area_get_size(&blit->dst_area); + + if(blit->opa >= (lv_opa_t)LV_OPA_MAX) { + if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT); + } + else { + if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT); + } + +#if VG_LITE_BLIT_SPLIT_ENABLED + return _lv_gpu_nxp_vglite_blit_split(blit); +#endif /* non RT595 */ + + /* Just pass down */ + return _lv_gpu_nxp_vglite_blit_single(blit); +} + +lv_res_t lv_gpu_nxp_vglite_blit_transform(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + uint32_t dest_size = lv_area_get_size(&blit->dst_area); + + if(blit->opa >= (lv_opa_t)LV_OPA_MAX) { + if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT); + } + else { + if(dest_size < LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT) + VG_LITE_RETURN_INV("Area size %d smaller than limit %d.", dest_size, LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT); + } + + return _lv_gpu_nxp_vglite_blit_single(blit); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if VG_LITE_BLIT_SPLIT_ENABLED +static lv_res_t _lv_gpu_nxp_vglite_blit_split(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + lv_res_t rv = LV_RES_INV; + + if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) { + PRINT_BLT("Blit check failed\n"); + return LV_RES_INV; + } + + PRINT_BLT("BLIT from: " + "Area: %03d,%03d - %03d,%03d " + "Addr: %d\n\n", + blit->src_area.x1, blit->src_area.y1, + blit->src_area.x2, blit->src_area.y2, + (uintptr_t) blit->src); + + PRINT_BLT("BLIT to: " + "Area: %03d,%03d - %03d,%03d " + "Addr: %d\n\n", + blit->dst_area.x1, blit->dst_area.y1, + blit->dst_area.x2, blit->dst_area.y2, + (uintptr_t) blit->src); + + /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */ + _align_x(&blit->src_area, (lv_color_t **)&blit->src); + _align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t)); + _align_x(&blit->dst_area, (lv_color_t **)&blit->dst); + _align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t)); + + /* Stage 2: If we're in limit, do a single BLIT */ + if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) && + (blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) { + PRINT_BLT("Simple blit!\n"); + return _lv_gpu_nxp_vglite_blit_single(blit); + }; + + /* Stage 3: Split the BLIT into multiple tiles */ + PRINT_BLT("Split blit!\n"); + + PRINT_BLT("Blit " + "([%03d,%03d], [%03d,%03d]) -> " + "([%03d,%03d], [%03d,%03d]) | " + "([%03dx%03d] -> [%03dx%03d]) | " + "A:(%d -> %d)\n", + blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2, + blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2, + lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area), + lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area), + (uintptr_t) blit->src, (uintptr_t) blit->dst); + + + lv_coord_t totalWidth = lv_area_get_width(&blit->src_area); + lv_coord_t totalHeight = lv_area_get_height(&blit->src_area); + + lv_gpu_nxp_vglite_blit_info_t tileBlit; + + /* Number of tiles needed */ + int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + + /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as + * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be + * different */ + int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0; + int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0; + + PRINT_BLT("\n"); + PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX); + + tileBlit = *blit; + + for(int tileY = 0; tileY < totalTilesY; tileY++) { + + tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */ + tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */ + } + tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof( + lv_color_t); /* stride in px! */ + + tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */ + tileBlit.dst_area.y2 = tileBlit.src_area.y2; + + tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof( + lv_color_t); /* stride in px! */ + + for(int tileX = 0; tileX < totalTilesX; tileX++) { + + if(tileX == 0) { + /* 1st tile is special - there may be a gap between buffer start pointer + * and area.x1 value, as the pointer has to be aligned. + * tileBlit.src pointer - keep init value from Y-loop. + * Also, 1st tile start is not shifted! shift is applied from 2nd tile */ + tileBlit.src_area.x1 = blit->src_area.x1; + tileBlit.dst_area.x1 = blit->dst_area.x1; + } + else { + /* subsequent tiles always starts from 0, but shifted*/ + tileBlit.src_area.x1 = 0 + shiftSrcX; + tileBlit.dst_area.x1 = 0 + shiftDstX; + /* and advance start pointer + 1 tile size */ + tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; + } + + /* Clip tile end coordinates */ + tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + } + + tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { + tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; + } + + if(tileX < (totalTilesX - 1)) { + /* And adjust end coords if shifted, but not for last tile! */ + tileBlit.src_area.x2 += shiftSrcX; + tileBlit.dst_area.x2 += shiftDstX; + } + + rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit); + +#if BLIT_DBG_AREAS + lv_vglite_dbg_draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, + LV_COLOR_RED); + lv_vglite_dbg_draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area, + LV_COLOR_GREEN); +#endif + + PRINT_BLT("Tile [%d, %d]: " + "([%d,%d], [%d,%d]) -> " + "([%d,%d], [%d,%d]) | " + "([%dx%d] -> [%dx%d]) | " + "A:(0x%8X -> 0x%8X) %s\n", + tileX, tileY, + tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2, + tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2, + lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area), + lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area), + (uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst, + rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!"); + + if(rv != LV_RES_OK) { /* if anything goes wrong... */ +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS + LV_LOG_ERROR("Split blit failed. Trying SW blit instead."); +#endif + _sw_blit(&tileBlit); + rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */ + } + + } + PRINT_BLT(" \n"); + } + + return rv; /* should never fail */ +} +#endif /* VG_LITE_BLIT_SPLIT_ENABLED */ + +static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + vg_lite_buffer_t src_vgbuf, dst_vgbuf; + vg_lite_error_t err = VG_LITE_SUCCESS; + uint32_t rect[4]; + vg_lite_float_t scale = 1.0; + + if(blit == NULL) { + /*Wrong parameter*/ + return LV_RES_INV; + } + + if(blit->opa < (lv_opa_t) LV_OPA_MIN) { + return LV_RES_OK; /*Nothing to BLIT*/ + } + + /*Wrap src/dst buffer into VG-Lite buffer*/ + if(lv_vglite_init_buf(&src_vgbuf, (uint32_t)blit->src_width, (uint32_t)blit->src_height, (uint32_t)blit->src_stride, + blit->src, true) != LV_RES_OK) + VG_LITE_RETURN_INV("Init buffer failed."); + + if(lv_vglite_init_buf(&dst_vgbuf, (uint32_t)blit->dst_width, (uint32_t)blit->dst_height, (uint32_t)blit->dst_stride, + blit->dst, false) != LV_RES_OK) + VG_LITE_RETURN_INV("Init buffer failed."); + + rect[0] = (uint32_t)blit->src_area.x1; /* start x */ + rect[1] = (uint32_t)blit->src_area.y1; /* start y */ + rect[2] = (uint32_t)blit->src_area.x2 - (uint32_t)blit->src_area.x1 + 1U; /* width */ + rect[3] = (uint32_t)blit->src_area.y2 - (uint32_t)blit->src_area.y1 + 1U; /* height */ + + vg_lite_matrix_t matrix; + vg_lite_identity(&matrix); + vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix); + + if((blit->angle != 0) || (blit->zoom != LV_IMG_ZOOM_NONE)) { + vg_lite_translate(blit->pivot.x, blit->pivot.y, &matrix); + vg_lite_rotate(blit->angle / 10.0f, &matrix); /* angle is 1/10 degree */ + scale = 1.0f * blit->zoom / LV_IMG_ZOOM_NONE; + vg_lite_scale(scale, scale, &matrix); + vg_lite_translate(0.0f - blit->pivot.x, 0.0f - blit->pivot.y, &matrix); + } + + /*Clean & invalidate cache*/ + lv_vglite_invalidate_cache(); + + uint32_t color; + vg_lite_blend_t blend; + if(blit->opa >= (lv_opa_t)LV_OPA_MAX) { + color = 0xFFFFFFFFU; + blend = VG_LITE_BLEND_SRC_OVER; + src_vgbuf.transparency_mode = VG_LITE_IMAGE_TRANSPARENT; + } + else { + uint32_t opa = (uint32_t)blit->opa; + if(vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) { + color = (opa << 24) | 0x00FFFFFFU; + } + else { + color = (opa << 24) | (opa << 16) | (opa << 8) | opa; + } + blend = VG_LITE_BLEND_SRC_OVER; + src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE; + src_vgbuf.transparency_mode = VG_LITE_IMAGE_TRANSPARENT; + } + + err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT); + VG_LITE_ERR_RETURN_INV(err, "Blit rectangle failed."); + + err = vg_lite_finish(); + VG_LITE_ERR_RETURN_INV(err, "Finish failed."); + + return LV_RES_OK; +} + +#if VG_LITE_BLIT_SPLIT_ENABLED + +static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + int x, y; + + lv_coord_t w = lv_area_get_width(&blit->src_area); + lv_coord_t h = lv_area_get_height(&blit->src_area); + + int32_t srcStridePx = blit->src_stride / (int32_t)sizeof(lv_color_t); + int32_t dstStridePx = blit->dst_stride / (int32_t)sizeof(lv_color_t); + + lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1; + lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1; + + if(blit->opa >= (lv_opa_t)LV_OPA_MAX) { + /* simple copy */ + for(y = 0; y < h; y++) { + lv_memcpy(dst, src, (uint32_t)w * sizeof(lv_color_t)); + src += srcStridePx; + dst += dstStridePx; + } + } + else if(blit->opa >= LV_OPA_MIN) { + /* alpha blending */ + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + dst[x] = lv_color_mix(src[x], dst[x], blit->opa); + } + src += srcStridePx; + dst += dstStridePx; + } + } +} + +static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit) +{ + + /* Test for minimal width */ + if(lv_area_get_width(&blit->src_area) < (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) + VG_LITE_RETURN_INV("Src area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area), + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + + /* Test for minimal width */ + if(lv_area_get_width(&blit->dst_area) < (lv_coord_t)LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) + VG_LITE_RETURN_INV("Dest area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area), + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + + /* Test for pointer alignment */ + if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) + VG_LITE_RETURN_INV("Src buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE); + + /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */ + + /* Test for stride alignment */ + if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0) + VG_LITE_RETURN_INV("Src buffer stride (%d px) not aligned to %d px.", blit->src_stride, + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + + /* Test for stride alignment */ + if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != 0x0) + VG_LITE_RETURN_INV("Dest buffer stride (%d px) not aligned to %d px.", blit->dst_stride, + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + + if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) || + (lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) + VG_LITE_RETURN_INV("Src and dest buffer areas are not equal."); + + return LV_RES_OK; +} + +static void _align_x(lv_area_t * area, lv_color_t ** buf) +{ + + int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH)); + VG_LITE_COND_STOP(alignedAreaStartPx < 0, "Negative X alignment."); + + area->x1 -= alignedAreaStartPx; + area->x2 -= alignedAreaStartPx; + *buf += alignedAreaStartPx; +} + +static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx) +{ + int LineToAlignMem; + int alignedAreaStartPy; + /* find how many lines of pixels will respect memory alignment requirement */ + if(stridePx % (uint32_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0U) { + alignedAreaStartPy = area->y1; + } + else { + LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); + VG_LITE_COND_STOP(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX), + "Complex case: need gcd function."); + alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem); + VG_LITE_COND_STOP(alignedAreaStartPy < 0, "Negative Y alignment."); + } + + area->y1 -= alignedAreaStartPy; + area->y2 -= alignedAreaStartPy; + *buf += (uint32_t)alignedAreaStartPy * stridePx; +} +#endif /*VG_LITE_BLIT_SPLIT_ENABLED*/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h similarity index 74% rename from lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.h rename to lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h index 26f4c3fe4..bc448c65a 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_blend.h @@ -1,12 +1,12 @@ /** - * @file lv_gpu_nxp_vglite.h + * @file lv_draw_vglite_blend.h * */ /** * MIT License * - * Copyright (c) 2020 NXP + * Copyright 2020-2022 NXP * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,8 @@ * */ -#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_ -#define LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_ +#ifndef LV_DRAW_VGLITE_BLEND_H +#define LV_DRAW_VGLITE_BLEND_H #ifdef __cplusplus extern "C" { @@ -37,48 +37,39 @@ extern "C" { /********************* * INCLUDES *********************/ -#include "../../lv_conf_internal.h" + +#include "../../../lv_conf_internal.h" #if LV_USE_GPU_NXP_VG_LITE +#include "lv_gpu_nxp_vglite.h" /********************* * DEFINES *********************/ -/** Use this symbol as limit to disable feature (value has to be larger than supported resolution) */ -#define LV_GPU_NXP_VG_LITE_FEATURE_DISABLED (1920*1080+1) - -/** Stride in px required by VG-Lite HW. Don't change this. */ -#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U - #ifndef LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT /** Minimum area (in pixels) to be filled by VG-Lite with 100% opacity*/ -#define LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT 32 +#define LV_GPU_NXP_VG_LITE_FILL_SIZE_LIMIT 5000 #endif #ifndef LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT /** Minimum area (in pixels) to be filled by VG-Lite with transparency*/ -#define LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT 32 +#define LV_GPU_NXP_VG_LITE_FILL_OPA_SIZE_LIMIT 5000 #endif #ifndef LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT /** Minimum area (in pixels) for image copy with 100% opacity to be handled by VG-Lite*/ -#define LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT 32 +#define LV_GPU_NXP_VG_LITE_BLIT_SIZE_LIMIT 5000 #endif #ifndef LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT /** Minimum invalidated area (in pixels) to be synchronized by VG-Lite during buffer sync */ -#define LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT 32 +#define LV_GPU_NXP_VG_LITE_BUFF_SYNC_BLIT_SIZE_LIMIT 5000 #endif #ifndef LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT /** Minimum area (in pixels) for image copy with transparency to be handled by VG-Lite*/ -#define LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT 32 -#endif - -#ifndef LV_GPU_NXP_VG_LITE_LOG_ERRORS -/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/ -#define LV_GPU_NXP_VG_LITE_LOG_ERRORS 1 +#define LV_GPU_NXP_VG_LITE_BLIT_OPA_SIZE_LIMIT 5000 #endif /********************** @@ -94,30 +85,29 @@ typedef struct { lv_area_t src_area; /**< Area to be copied from source*/ lv_coord_t src_width; /**< Source buffer width*/ lv_coord_t src_height; /**< Source buffer height*/ - uint32_t src_stride; /**< Source buffer stride in bytes (must be aligned on 16 px)*/ + int32_t src_stride; /**< Source buffer stride in bytes (must be aligned on 16 px)*/ const lv_color_t * dst; /**< Destination buffer pointer (must be aligned on 32 bytes)*/ lv_area_t dst_area; /**< Target area in destination buffer (must be the same as src_area)*/ lv_coord_t dst_width; /**< Destination buffer width*/ lv_coord_t dst_height; /**< Destination buffer height*/ - uint32_t dst_stride; /**< Destination buffer stride in bytes (must be aligned on 16 px)*/ + int32_t dst_stride; /**< Destination buffer stride in bytes (must be aligned on 16 px)*/ lv_opa_t opa; /**< Opacity - alpha mix (0 = source not copied, 255 = 100% opaque)*/ - + uint32_t angle; /**< Rotation angle (1/10 of degree)*/ + uint32_t zoom; /**< 256 = no zoom (1:1 scale ratio)*/ + lv_point_t pivot; /**< The coordinates of rotation pivot in source image buffer*/ } lv_gpu_nxp_vglite_blit_info_t; /********************** - * MACROS + * GLOBAL PROTOTYPES **********************/ -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/*** - * Fills rectangular area in buffer. - * @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes) - * @param[in] dest_width Destination buffer width in pixels ((must be aligned on 16 px) +/** + * Fill area, with optional opacity. + * + * @param[in/out] dest_buf Destination buffer pointer (must be aligned on 32 bytes) + * @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px) * @param[in] dest_height Destination buffer height in pixels * @param[in] fill_area Area to be filled * @param[in] color Fill color @@ -128,18 +118,32 @@ typedef struct { lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa); -/*** +/** * BLock Image Transfer. + * * @param[in] blit Description of the transfer * @retval LV_RES_OK Transfer complete * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) */ lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit); +/** + * BLock Image Transfer with transformation. + * + * @param[in] blit Description of the transfer + * @retval LV_RES_OK Transfer complete + * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) + */ +lv_res_t lv_gpu_nxp_vglite_blit_transform(lv_gpu_nxp_vglite_blit_info_t * blit); + +/********************** + * MACROS + **********************/ + #endif /*LV_USE_GPU_NXP_VG_LITE*/ #ifdef __cplusplus } /*extern "C"*/ #endif -#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_VGLITE_H_*/ +#endif /*LV_DRAW_VGLITE_BLEND_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c new file mode 100644 index 000000000..bc1d55c85 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.c @@ -0,0 +1,244 @@ +/** + * @file lv_draw_vglite_rect.c + * + */ + +/** + * MIT License + * + * Copyright 2021, 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_draw_vglite_rect.h" + +#if LV_USE_GPU_NXP_VG_LITE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_gpu_nxp_vglite_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) +{ + vg_lite_buffer_t vgbuf; + vg_lite_error_t err = VG_LITE_SUCCESS; + lv_coord_t dest_width = lv_area_get_width(draw_ctx->buf_area); + lv_coord_t dest_height = lv_area_get_height(draw_ctx->buf_area); + vg_lite_path_t path; + vg_lite_color_t vgcol; /* vglite takes ABGR */ + vg_lite_matrix_t matrix; + lv_coord_t width = lv_area_get_width(coords); + lv_coord_t height = lv_area_get_height(coords); + vg_lite_linear_gradient_t gradient; + vg_lite_matrix_t * grad_matrix; + + if(dsc->radius < 0) + return LV_RES_INV; + + /* Make areas relative to draw buffer */ + lv_area_t rel_coords; + lv_area_copy(&rel_coords, coords); + lv_area_move(&rel_coords, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + lv_area_t rel_clip; + lv_area_copy(&rel_clip, draw_ctx->clip_area); + lv_area_move(&rel_clip, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + + /*** Init destination buffer ***/ + if(lv_vglite_init_buf(&vgbuf, (uint32_t)dest_width, (uint32_t)dest_height, (uint32_t)dest_width * sizeof(lv_color_t), + (const lv_color_t *)draw_ctx->buf, false) != LV_RES_OK) + VG_LITE_RETURN_INV("Init buffer failed."); + + /*** Init path ***/ + int32_t rad = dsc->radius; + if(dsc->radius == LV_RADIUS_CIRCLE) { + rad = (width > height) ? height / 2 : width / 2; + } + + if((dsc->radius == LV_RADIUS_CIRCLE) && (width == height)) { + float tang = ((float)rad * BEZIER_OPTIM_CIRCLE); + int32_t cpoff = (int32_t)tang; + int32_t circle_path[] = { /*VG circle path*/ + VLC_OP_MOVE, rel_coords.x1 + rad, rel_coords.y1, + VLC_OP_CUBIC_REL, cpoff, 0, rad, rad - cpoff, rad, rad, /* top-right */ + VLC_OP_CUBIC_REL, 0, cpoff, cpoff - rad, rad, 0 - rad, rad, /* bottom-right */ + VLC_OP_CUBIC_REL, 0 - cpoff, 0, 0 - rad, cpoff - rad, 0 - rad, 0 - rad, /* bottom-left */ + VLC_OP_CUBIC_REL, 0, 0 - cpoff, rad - cpoff, 0 - rad, rad, 0 - rad, /* top-left */ + VLC_OP_END + }; + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(circle_path), circle_path, + (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1, + ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f); + } + else if(dsc->radius > 0) { + float tang = ((float)rad * BEZIER_OPTIM_CIRCLE); + int32_t cpoff = (int32_t)tang; + int32_t rounded_path[] = { /*VG rounded rectangular path*/ + VLC_OP_MOVE, rel_coords.x1 + rad, rel_coords.y1, + VLC_OP_LINE, rel_coords.x2 - rad + 1, rel_coords.y1, /* top */ + VLC_OP_CUBIC_REL, cpoff, 0, rad, rad - cpoff, rad, rad, /* top-right */ + VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y2 - rad + 1, /* right */ + VLC_OP_CUBIC_REL, 0, cpoff, cpoff - rad, rad, 0 - rad, rad, /* bottom-right */ + VLC_OP_LINE, rel_coords.x1 + rad, rel_coords.y2 + 1, /* bottom */ + VLC_OP_CUBIC_REL, 0 - cpoff, 0, 0 - rad, cpoff - rad, 0 - rad, 0 - rad, /* bottom-left */ + VLC_OP_LINE, rel_coords.x1, rel_coords.y1 + rad, /* left */ + VLC_OP_CUBIC_REL, 0, 0 - cpoff, rad - cpoff, 0 - rad, rad, 0 - rad, /* top-left */ + VLC_OP_END + }; + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_HIGH, sizeof(rounded_path), rounded_path, + (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1, + ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f); + } + else { + int32_t rect_path[] = { /*VG rectangular path*/ + VLC_OP_MOVE, rel_coords.x1, rel_coords.y1, + VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y1, + VLC_OP_LINE, rel_coords.x2 + 1, rel_coords.y2 + 1, + VLC_OP_LINE, rel_coords.x1, rel_coords.y2 + 1, + VLC_OP_LINE, rel_coords.x1, rel_coords.y1, + VLC_OP_END + }; + err = vg_lite_init_path(&path, VG_LITE_S32, VG_LITE_LOW, sizeof(rect_path), rect_path, + (vg_lite_float_t) rel_clip.x1, (vg_lite_float_t) rel_clip.y1, + ((vg_lite_float_t) rel_clip.x2) + 1.0f, ((vg_lite_float_t) rel_clip.y2) + 1.0f); + } + + VG_LITE_ERR_RETURN_INV(err, "Init path failed."); + vg_lite_identity(&matrix); + + /*** Init Color/Gradient ***/ + if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) { + uint32_t colors[2]; + uint32_t stops[2]; + lv_color32_t col32[2]; + + /* Gradient setup */ + uint8_t cnt = MAX(dsc->bg_grad.stops_count, 2); + for(uint8_t i = 0; i < cnt; i++) { + col32[i].full = lv_color_to32(dsc->bg_grad.stops[i].color); /*Convert color to RGBA8888*/ + stops[i] = dsc->bg_grad.stops[i].frac; +#if LV_COLOR_DEPTH==16 + colors[i] = ((uint32_t)col32[i].ch.alpha << 24) | ((uint32_t)col32[i].ch.blue << 16) | + ((uint32_t)col32[i].ch.green << 8) | (uint32_t)col32[i].ch.red; +#else /*LV_COLOR_DEPTH==32*/ + /* watchout: red and blue color components are inverted versus vg_lite_color_t order */ + colors[i] = ((uint32_t)col32[i].ch.alpha << 24) | ((uint32_t)col32[i].ch.red << 16) | + ((uint32_t)col32[i].ch.green << 8) | (uint32_t)col32[i].ch.blue; +#endif + } + + lv_memset_00(&gradient, sizeof(vg_lite_linear_gradient_t)); + + err = vg_lite_init_grad(&gradient); + VG_LITE_ERR_RETURN_INV(err, "Init gradient failed"); + + err = vg_lite_set_grad(&gradient, cnt, colors, stops); + VG_LITE_ERR_RETURN_INV(err, "Set gradient failed."); + + err = vg_lite_update_grad(&gradient); + VG_LITE_ERR_RETURN_INV(err, "Update gradient failed."); + + grad_matrix = vg_lite_get_grad_matrix(&gradient); + vg_lite_identity(grad_matrix); + vg_lite_translate((float)rel_coords.x1, (float)rel_coords.y1, grad_matrix); + + if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_VER) { + vg_lite_scale(1.0f, (float)height / 256.0f, grad_matrix); + vg_lite_rotate(90.0f, grad_matrix); + } + else { /*LV_GRAD_DIR_HOR*/ + vg_lite_scale((float)width / 256.0f, 1.0f, grad_matrix); + } + } + + lv_opa_t bg_opa = dsc->bg_opa; + lv_color32_t bg_col32 = {.full = lv_color_to32(dsc->bg_color)}; /*Convert color to RGBA8888*/ + if(bg_opa <= (lv_opa_t)LV_OPA_MAX) { + /* Only pre-multiply color if hardware pre-multiplication is not present */ + if(!vg_lite_query_feature(gcFEATURE_BIT_VG_PE_PREMULTIPLY)) { + bg_col32.ch.red = (uint8_t)(((uint16_t)bg_col32.ch.red * bg_opa) >> 8); + bg_col32.ch.green = (uint8_t)(((uint16_t)bg_col32.ch.green * bg_opa) >> 8); + bg_col32.ch.blue = (uint8_t)(((uint16_t)bg_col32.ch.blue * bg_opa) >> 8); + } + bg_col32.ch.alpha = bg_opa; + } + +#if LV_COLOR_DEPTH==16 + vgcol = bg_col32.full; +#else /*LV_COLOR_DEPTH==32*/ + vgcol = ((uint32_t)bg_col32.ch.alpha << 24) | ((uint32_t)bg_col32.ch.blue << 16) | + ((uint32_t)bg_col32.ch.green << 8) | (uint32_t)bg_col32.ch.red; +#endif + + /*Clean & invalidate cache*/ + lv_vglite_invalidate_cache(); + + /*** Draw rectangle ***/ + if(dsc->bg_grad.dir == (lv_grad_dir_t)LV_GRAD_DIR_NONE) { + err = vg_lite_draw(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, vgcol); + } + else { + err = vg_lite_draw_gradient(&vgbuf, &path, VG_LITE_FILL_EVEN_ODD, &matrix, &gradient, VG_LITE_BLEND_SRC_OVER); + } + VG_LITE_ERR_RETURN_INV(err, "Draw gradient failed."); + + err = vg_lite_finish(); + VG_LITE_ERR_RETURN_INV(err, "Finish failed."); + + err = vg_lite_clear_path(&path); + VG_LITE_ERR_RETURN_INV(err, "Clear path failed."); + + if(dsc->bg_grad.dir != (lv_grad_dir_t)LV_GRAD_DIR_NONE) { + err = vg_lite_clear_grad(&gradient); + VG_LITE_ERR_RETURN_INV(err, "Clear gradient failed."); + } + + return LV_RES_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h new file mode 100644 index 000000000..d708e7b42 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_draw_vglite_rect.h @@ -0,0 +1,77 @@ +/** + * @file lv_draw_vglite_rect.h + * + */ + +/** + * MIT License + * + * Copyright 2021, 2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_DRAW_VGLITE_RECT_H +#define LV_DRAW_VGLITE_RECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_GPU_NXP_VG_LITE +#include "lv_gpu_nxp_vglite.h" +#include "../../lv_draw_rect.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Draw rectangle shape with effects (rounded corners, gradient) + * + * @param draw_ctx drawing context + * @param dsc description of the rectangle + * @param coords the area where rectangle is clipped + */ +lv_res_t lv_gpu_nxp_vglite_draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_DRAW_VGLITE_RECT_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c new file mode 100644 index 000000000..f65ec1d48 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.c @@ -0,0 +1,153 @@ +/** + * @file lv_gpu_nxp_vglite.c + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_gpu_nxp_vglite.h" + +#if LV_USE_GPU_NXP_VG_LITE +#include "../../../core/lv_refr.h" +#if BLIT_DBG_AREAS + #include "lv_draw_vglite_blend.h" +#endif + +/********************* + * DEFINES + *********************/ + +#if LV_COLOR_DEPTH==16 + #define VG_LITE_PX_FMT VG_LITE_RGB565 +#elif LV_COLOR_DEPTH==32 + #define VG_LITE_PX_FMT VG_LITE_BGRA8888 +#else + #error Only 16bit and 32bit color depth are supported. Set LV_COLOR_DEPTH to 16 or 32. +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_res_t lv_vglite_init_buf(vg_lite_buffer_t * vgbuf, uint32_t width, uint32_t height, uint32_t stride, + const lv_color_t * ptr, bool source) +{ + /*Test for memory alignment*/ + if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != (uintptr_t)0x0U) + VG_LITE_RETURN_INV("%s buffer (0x%x) not aligned to %d.", source ? "Src" : "Dest", + (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE); + + /*Test for stride alignment*/ + if(source && (stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) + VG_LITE_RETURN_INV("Src buffer stride (%d bytes) not aligned to %d bytes.", stride, + LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t)); + + vgbuf->format = VG_LITE_PX_FMT; + vgbuf->tiled = VG_LITE_LINEAR; + vgbuf->image_mode = VG_LITE_NORMAL_IMAGE_MODE; + vgbuf->transparency_mode = VG_LITE_IMAGE_OPAQUE; + + vgbuf->width = (int32_t)width; + vgbuf->height = (int32_t)height; + vgbuf->stride = (int32_t)stride; + + lv_memset_00(&vgbuf->yuv, sizeof(vgbuf->yuv)); + + vgbuf->memory = (void *)ptr; + vgbuf->address = (uint32_t)vgbuf->memory; + vgbuf->handle = NULL; + + return LV_RES_OK; +} + +#if BLIT_DBG_AREAS +void lv_vglite_dbg_draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, + lv_area_t * fill_area, lv_color_t color) +{ + lv_area_t a; + + /* top line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x2; + a.y1 = fill_area->y1; + a.y2 = fill_area->y1; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + + /* bottom line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x2; + a.y1 = fill_area->y2; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + /* left line */ + a.x1 = fill_area->x1; + a.x2 = fill_area->x1; + a.y1 = fill_area->y1; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); + + /* right line */ + a.x1 = fill_area->x2; + a.x2 = fill_area->x2; + a.y1 = fill_area->y1; + a.y2 = fill_area->y2; + lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); +} +#endif /* BLIT_DBG_AREAS */ + +void lv_vglite_invalidate_cache(void) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + if(disp->driver->clean_dcache_cb) + disp->driver->clean_dcache_cb(disp->driver); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h new file mode 100644 index 000000000..c22cae185 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/nxp/vglite/lv_gpu_nxp_vglite.h @@ -0,0 +1,185 @@ +/** + * @file lv_gpu_nxp_vglite.h + * + */ + +/** + * MIT License + * + * Copyright 2020-2022 NXP + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next paragraph) + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef LV_GPU_NXP_VGLITE_H +#define LV_GPU_NXP_VGLITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_GPU_NXP_VG_LITE +#include "vg_lite.h" +#include "../../sw/lv_draw_sw.h" +#include "../../../misc/lv_log.h" +#include "fsl_debug_console.h" + +/********************* + * DEFINES + *********************/ + +/** Use this symbol as limit to disable feature (value has to be larger than supported resolution) */ +#define LV_GPU_NXP_VG_LITE_FEATURE_DISABLED (1920*1080+1) + +/** Stride in px required by VG-Lite HW. Don't change this. */ +#define LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX 16U + +#ifndef LV_GPU_NXP_VG_LITE_LOG_ERRORS +/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/ +#define LV_GPU_NXP_VG_LITE_LOG_ERRORS 1 +#endif + +#ifndef LV_GPU_NXP_VG_LITE_LOG_TRACES +/** Enable logging of VG-Lite errors (\see LV_LOG_ERROR)*/ +#define LV_GPU_NXP_VG_LITE_LOG_TRACES 0 +#endif + +/* Draw rectangles around BLIT tiles */ +#define BLIT_DBG_AREAS 0 + +/* Print detailed info to SDK console (NOT to LVGL log system) */ +#define BLIT_DBG_VERBOSE 0 + +/* Verbose debug print */ +#if BLIT_DBG_VERBOSE +#define PRINT_BLT PRINTF +#else +#define PRINT_BLT(...) +#endif + +/* The optimal Bezier control point offset for radial unit + * see: https://spencermortensen.com/articles/bezier-circle/ + **/ +#define BEZIER_OPTIM_CIRCLE 0.551915024494f + +/* Draw lines for control points of Bezier curves */ +#define BEZIER_DBG_CONTROL_POINTS 0 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Fills vg_lite_buffer_t structure according given parameters. + * + * @param[in/out] vgbuf Buffer structure to be filled + * @param[in] width Width of buffer in pixels + * @param[in] height Height of buffer in pixels + * @param[in] stride Stride of the buffer in bytes + * @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements) + * @param[in] source Boolean to check if this is a source buffer + */ +lv_res_t lv_vglite_init_buf(vg_lite_buffer_t * vgbuf, uint32_t width, uint32_t height, uint32_t stride, + const lv_color_t * ptr, bool source); + +#if BLIT_DBG_AREAS +/** + * Draw a simple rectangle, 1 px line width. + * + * @param dest_buf Destination buffer + * @param dest_width Destination buffer width (must be aligned on 16px) + * @param dest_height Destination buffer height + * @param fill_area Rectangle coordinates + * @param color Rectangle color + */ +void lv_vglite_dbg_draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, + lv_area_t * fill_area, lv_color_t color); +#endif + +/** + * Clean & invalidate cache. + */ +void lv_vglite_invalidate_cache(void); + +/********************** + * MACROS + **********************/ + +#define VG_LITE_COND_STOP(cond, txt) \ + do { \ + if (cond) { \ + LV_LOG_ERROR("%s. STOP!", txt); \ + for ( ; ; ); \ + } \ + } while(0) + +#if LV_GPU_NXP_VG_LITE_LOG_ERRORS +#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \ + do { \ + if(err != VG_LITE_SUCCESS) { \ + LV_LOG_ERROR(fmt, ##__VA_ARGS__); \ + return LV_RES_INV; \ + } \ + } while (0) +#else +#define VG_LITE_ERR_RETURN_INV(err, fmt, ...) \ + do { \ + if(err != VG_LITE_SUCCESS) { \ + return LV_RES_INV; \ + } \ + }while(0) +#endif /*LV_GPU_NXP_VG_LITE_LOG_ERRORS*/ + +#if LV_GPU_NXP_VG_LITE_LOG_TRACES +#define VG_LITE_LOG_TRACE(fmt, ...) \ + do { \ + LV_LOG_ERROR(fmt, ##__VA_ARGS__); \ + } while (0) + +#define VG_LITE_RETURN_INV(fmt, ...) \ + do { \ + LV_LOG_ERROR(fmt, ##__VA_ARGS__); \ + return LV_RES_INV; \ + } while (0) +#else +#define VG_LITE_LOG_TRACE(fmt, ...) \ + do { \ + } while (0) +#define VG_LITE_RETURN_INV(fmt, ...) \ + do { \ + return LV_RES_INV; \ + }while(0) +#endif /*LV_GPU_NXP_VG_LITE_LOG_TRACES*/ + +#endif /*LV_USE_GPU_NXP_VG_LITE*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_NXP_VGLITE_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c deleted file mode 100644 index a6fb49b7b..000000000 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.c +++ /dev/null @@ -1,446 +0,0 @@ -/** - * @file lv_gpu_nxp_pxp.c - * - */ - -/** - * MIT License - * - * Copyright (c) 2020 NXP - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next paragraph) - * shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/********************* - * INCLUDES - *********************/ - -#include "lv_gpu_nxp_pxp.h" - -#if LV_USE_GPU_NXP_PXP - -#include "../misc/lv_mem.h" -#include "../misc/lv_log.h" - -#include "fsl_pxp.h" -#include "fsl_cache.h" - -/********************* - * DEFINES - *********************/ - -#if LV_COLOR_16_SWAP - #error Color swap not implemented. Disable LV_COLOR_16_SWAP feature. -#endif - -#if LV_COLOR_DEPTH==16 - #define PXP_OUT_PIXEL_FORMAT kPXP_OutputPixelFormatRGB565 - #define PXP_AS_PIXEL_FORMAT kPXP_AsPixelFormatRGB565 - #define PXP_PS_PIXEL_FORMAT kPXP_PsPixelFormatRGB565 -#else - #error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16. -#endif - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ - -static void lv_gpu_nxp_pxp_run(void); -static void lv_gpu_nxp_pxp_blit_recolor(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, - lv_coord_t src_width, - lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa, lv_color_t recolor, lv_opa_t recolorOpa); - -/********************** - * STATIC VARIABLES - **********************/ - -static bool colorKeyEnabled = false; -static uint32_t colorKey = 0x0; - -static bool recolorEnabled = false; -static lv_color_t recolor = {.full = 0x0}; -static lv_opa_t recolorOpa = 0x0; - -static lv_nxp_pxp_cfg_t pxp_cfg; - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Reset and initialize PXP device. This function should be called as a part - * of display init sequence. - * - * @return LV_RES_OK: PXP init ok; LV_RES_INV: init error. See error log for more information. - */ -lv_res_t lv_gpu_nxp_pxp_init(lv_nxp_pxp_cfg_t * cfg) -{ - if(!cfg || !cfg->pxp_interrupt_deinit || !cfg->pxp_interrupt_init || !cfg->pxp_run) { - LV_LOG_ERROR("PXP configuration error. Check callback pointers."); - return LV_RES_INV; - } - - PXP_Init(PXP); - PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/ - PXP_EnableInterrupts(PXP, kPXP_CompleteInterruptEnable); - - pxp_cfg = *cfg; - if(pxp_cfg.pxp_interrupt_init() != LV_RES_OK) { - PXP_Deinit(PXP); - LV_LOG_ERROR("PXP interrupt init error. Check pxp_interrupt_init callback."); - return LV_RES_INV; - } - - colorKey = lv_color_to32(LV_COLOR_CHROMA_KEY); - - return LV_RES_OK; -} - -/** - * Disable PXP device. Should be called during display deinit sequence. - */ -void lv_gpu_nxp_pxp_deinit(void) -{ - pxp_cfg.pxp_interrupt_deinit(); - PXP_DisableInterrupts(PXP, kPXP_CompleteInterruptEnable); - PXP_Deinit(LV_GPU_NXP_PXP_ID); -} - -/** - * Fill area, with optional opacity. - * - * @param[in/out] dest_buf destination buffer - * @param[in] dest_width width (stride) of destination buffer in pixels - * @param[in] fill_area area to fill - * @param[in] color color - * @param[in] opa transparency of the color - */ -void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color, - lv_opa_t opa) -{ - PXP_Init(LV_GPU_NXP_PXP_ID); - PXP_EnableCsc1(LV_GPU_NXP_PXP_ID, false); /*Disable CSC1, it is enabled by default.*/ - PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*Block size 16x16 for higher performance*/ - - /*OUT buffer configure*/ - pxp_output_buffer_config_t outputConfig = { - .pixelFormat = PXP_OUT_PIXEL_FORMAT, - .interlacedMode = kPXP_OutputProgressive, - .buffer0Addr = (uint32_t)(dest_buf + dest_width * fill_area->y1 + fill_area->x1), - .buffer1Addr = (uint32_t)NULL, - .pitchBytes = dest_width * sizeof(lv_color_t), - .width = fill_area->x2 - fill_area->x1 + 1, - .height = fill_area->y2 - fill_area->y1 + 1, - }; - - PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputConfig); - - if(opa > LV_OPA_MAX) { - /*Simple color fill without opacity - AS disabled, PS as color generator*/ - PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); /*Disable AS.*/ - PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); /*Disable PS.*/ - PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color)); - } - else { - /*Fill with opacity - AS used as source (same as OUT), PS used as color generator, blended together*/ - pxp_as_buffer_config_t asBufferConfig; - pxp_porter_duff_config_t pdConfig; - - /*Set AS to OUT*/ - asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT; - asBufferConfig.bufferAddr = (uint32_t)outputConfig.buffer0Addr; - asBufferConfig.pitchBytes = outputConfig.pitchBytes; - - PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); - PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, fill_area->x2 - fill_area->x1 + 1, - fill_area->y2 - fill_area->y1 + 1); - - /*Disable PS, use as color generator*/ - PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); - PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(color)); - - /*Configure Porter-Duff blending - For RGB 565 only!*/ - pdConfig.enable = 1; - pdConfig.dstColorMode = kPXP_PorterDuffColorStraight; - pdConfig.srcColorMode = kPXP_PorterDuffColorStraight; - pdConfig.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha; - pdConfig.srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha; - pdConfig.srcFactorMode = kPXP_PorterDuffFactorStraight; - pdConfig.dstFactorMode = kPXP_PorterDuffFactorStraight; - pdConfig.srcGlobalAlpha = opa; - pdConfig.dstGlobalAlpha = 255 - opa; - pdConfig.srcAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/ - pdConfig.dstAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/ - PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig); - } - - lv_gpu_nxp_pxp_run(); /*Start PXP task*/ -} - -/** - * @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with effects. - * - * By default, image is copied directly, with optional opacity configured by \p opa. - * Color keying can be enabled by calling lv_gpu_nxp_pxp_enable_color_key() before calling this function. - * Recoloring can be enabled by calling lv_gpu_nxp_pxp_enable_recolor() before calling this function. - * Note that color keying and recoloring at the same time is not supported and black rectangle is rendered. - * - * @param[in/out] dest destination buffer - * @param[in] dest_width width (stride) of destination buffer in pixels - * @param[in] src source buffer - * @param[in] src_with width (stride) of source buffer in pixels - * @param[in] copy_w width of area to be copied from src to dest - * @param[in] copy_h height of area to be copied from src to dest - * @param[in] opa opacity of the result - */ -void lv_gpu_nxp_pxp_blit(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, lv_coord_t src_width, - lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa) -{ - - if(recolorEnabled) { /*switch to recolor version of blit*/ - lv_gpu_nxp_pxp_blit_recolor(dest, dest_width, src, src_width, copy_width, copy_height, opa, recolor, recolorOpa); - return; - }; - - PXP_Init(PXP); - PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/ - PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*block size 16x16 for higher performance*/ - - pxp_output_buffer_config_t outputBufferConfig; - pxp_as_buffer_config_t asBufferConfig; - pxp_as_blend_config_t asBlendConfig; - - asBlendConfig.alpha = opa; - asBlendConfig.invertAlpha = false; - asBlendConfig.alphaMode = kPXP_AlphaRop; - asBlendConfig.ropMode = kPXP_RopMergeAs; - - if(opa >= LV_OPA_MAX && !colorKeyEnabled) { - /*Simple blit, no effect - Disable PS buffer*/ - PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); - } - else { - /*Alpha blending or color keying enabled - PS must be enabled to fetch background pixels - PS and OUT buffers are the same, blend will be done in-place*/ - pxp_ps_buffer_config_t psBufferConfig = { - .pixelFormat = PXP_PS_PIXEL_FORMAT, - .swapByte = false, - .bufferAddr = (uint32_t)dest, - .bufferAddrU = 0U, - .bufferAddrV = 0U, - .pitchBytes = dest_width * sizeof(lv_color_t) - }; - asBlendConfig.alphaMode = kPXP_AlphaOverride; - PXP_SetProcessSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &psBufferConfig); - PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1, copy_height - 1); - } - - /*AS buffer - source image*/ - asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT; - asBufferConfig.bufferAddr = (uint32_t)src; - asBufferConfig.pitchBytes = src_width * sizeof(lv_color_t); - PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); - PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1U, copy_height - 1U); - PXP_SetAlphaSurfaceBlendConfig(LV_GPU_NXP_PXP_ID, &asBlendConfig); - - if(colorKeyEnabled) { - PXP_SetAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, colorKey, colorKey); - } - PXP_EnableAlphaSurfaceOverlayColorKey(LV_GPU_NXP_PXP_ID, colorKeyEnabled); - - /*Output buffer.*/ - outputBufferConfig.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT; - outputBufferConfig.interlacedMode = kPXP_OutputProgressive; - outputBufferConfig.buffer0Addr = (uint32_t)dest; - outputBufferConfig.buffer1Addr = (uint32_t)0U; - outputBufferConfig.pitchBytes = dest_width * sizeof(lv_color_t); - outputBufferConfig.width = copy_width; - outputBufferConfig.height = copy_height; - PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig); - - lv_gpu_nxp_pxp_run(); /* Start PXP task */ -} - -/** - * @brief Enable color keying for subsequent calls to lv_gpu_nxp_pxp_blit() - * - * Color key is defined by symbol in lv_conf.h - */ -void lv_gpu_nxp_pxp_enable_color_key(void) -{ - colorKeyEnabled = true; -} - -/** - * @brief Disable color keying for subsequent calls to lv_gpu_nxp_pxp_blit() - * - */ -void lv_gpu_nxp_pxp_disable_color_key(void) -{ - colorKeyEnabled = false; -} - -/** - * @brief Enable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit() - * - * @param[in] color recolor value - * @param[in] opa effect opacity - */ -void lv_gpu_nxp_pxp_enable_recolor(lv_color_t color, lv_opa_t opa) -{ - recolorEnabled = true; - recolor = color; - recolorOpa = opa; - -} - -/** - * @brief Disable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit() - */ -void lv_gpu_nxp_pxp_disable_recolor(void) -{ - recolorEnabled = false; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -/** - * @brief Start PXP job and wait for results - * - * Function used internally to start PXP task according current device - * configuration. - */ -static void lv_gpu_nxp_pxp_run(void) -{ - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - if(disp && disp->driver->clean_dcache_cb) { /* Clean & invalidate cache */ - disp->driver->clean_dcache_cb(disp->driver); - } - - pxp_cfg.pxp_run(); -} - -/** - * @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with recoloring. - * - * Note that color keying and recoloring at the same time is not supported and black rectangle is rendered. - * - * @param[in/out] dest destination buffer - * @param[in] dest_width width (stride) of destination buffer in pixels - * @param[in] src source buffer - * @param[in] src_with width (stride) of source buffer in pixels - * @param[in] copy_w width of area to be copied from src to dest - * @param[in] copy_h height of area to be copied from src to dest - * @param[in] opa opacity of the result - * @param[in] recolor recolor value - * @param[in] recolorOpa effect opacity - */ -static void lv_gpu_nxp_pxp_blit_recolor(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, - lv_coord_t src_width, - lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa, lv_color_t recolor, lv_opa_t recolorOpa) -{ - pxp_output_buffer_config_t outputBufferConfig; - pxp_as_buffer_config_t asBufferConfig; - - if(colorKeyEnabled) { - /*should never get here, recolor & color keying not supported. Draw black box instead.*/ - const lv_area_t fill_area = {.x1 = 0, .y1 = 0, .x2 = copy_width - 1, .y2 = copy_height - 1}; - lv_gpu_nxp_pxp_fill(dest, dest_width, &fill_area, lv_color_black(), LV_OPA_MAX); - LV_LOG_WARN("Recoloring and color keying is not supported. Black rectangle rendered."); - return ; - } - else { - /*Recoloring without color keying*/ - if(opa > LV_OPA_MAX) { - /*Recolor with full opacity - AS source image, PS color generator, OUT destination*/ - PXP_Init(PXP); - PXP_EnableCsc1(PXP, false); /*Disable CSC1, it is enabled by default.*/ - PXP_SetProcessBlockSize(PXP, kPXP_BlockSize16); /*block size 16x16 for higher performance*/ - - /*AS buffer - source image*/ - asBufferConfig.pixelFormat = PXP_AS_PIXEL_FORMAT; - asBufferConfig.bufferAddr = (uint32_t)src; - asBufferConfig.pitchBytes = src_width * sizeof(lv_color_t); - PXP_SetAlphaSurfaceBufferConfig(LV_GPU_NXP_PXP_ID, &asBufferConfig); - PXP_SetAlphaSurfacePosition(LV_GPU_NXP_PXP_ID, 0U, 0U, copy_width - 1U, copy_height - 1U); - - /*Disable PS buffer, use as color generator*/ - PXP_SetProcessSurfacePosition(LV_GPU_NXP_PXP_ID, 0xFFFFU, 0xFFFFU, 0U, 0U); - PXP_SetProcessSurfaceBackGroundColor(LV_GPU_NXP_PXP_ID, lv_color_to32(recolor)); - - /*Output buffer*/ - outputBufferConfig.pixelFormat = (pxp_output_pixel_format_t)PXP_OUT_PIXEL_FORMAT; - outputBufferConfig.interlacedMode = kPXP_OutputProgressive; - outputBufferConfig.buffer0Addr = (uint32_t)dest; - outputBufferConfig.buffer1Addr = (uint32_t)0U; - outputBufferConfig.pitchBytes = dest_width * sizeof(lv_color_t); - outputBufferConfig.width = copy_width; - outputBufferConfig.height = copy_height; - PXP_SetOutputBufferConfig(LV_GPU_NXP_PXP_ID, &outputBufferConfig); - - pxp_porter_duff_config_t pdConfig; - - /*Configure Porter-Duff blending - For RGB 565 only!*/ - pdConfig.enable = 1; - pdConfig.dstColorMode = kPXP_PorterDuffColorStraight; - pdConfig.srcColorMode = kPXP_PorterDuffColorStraight; - pdConfig.dstGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha; - pdConfig.srcGlobalAlphaMode = kPXP_PorterDuffGlobalAlpha; - pdConfig.srcFactorMode = kPXP_PorterDuffFactorStraight; - pdConfig.dstFactorMode = kPXP_PorterDuffFactorStraight; - pdConfig.srcGlobalAlpha = recolorOpa; - pdConfig.dstGlobalAlpha = 255 - recolorOpa; - pdConfig.srcAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/ - pdConfig.dstAlphaMode = kPXP_PorterDuffAlphaStraight; /*don't care*/ - PXP_SetPorterDuffConfig(LV_GPU_NXP_PXP_ID, &pdConfig); - - lv_gpu_nxp_pxp_run(); /*Start PXP task*/ - - } - else { - /*Recolor with transparency*/ - - /*Step 1: Recolor with full opacity to temporary buffer*/ - lv_color_t * tmpBuf = (lv_color_t *)lv_mem_buf_get(copy_width * copy_height * sizeof(lv_color_t)); - lv_gpu_nxp_pxp_blit_recolor(tmpBuf, copy_width, src, src_width, copy_width, copy_height, LV_OPA_COVER, recolor, - recolorOpa); - - /*Step 2: BLIT temporary results with required opacity to output*/ - lv_gpu_nxp_pxp_disable_recolor(); /*make sure to take BLIT path, not the recolor*/ - lv_gpu_nxp_pxp_blit(dest, dest_width, tmpBuf, copy_width, copy_width, copy_height, opa); - lv_gpu_nxp_pxp_enable_recolor(recolor, recolorOpa); /*restore state*/ - - /*Step 3: Clean-up memory*/ - lv_mem_buf_release(tmpBuf); - } - } -} - -#endif /* LV_USE_GPU_NXP_PXP */ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.h b/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.h deleted file mode 100644 index 8d812882c..000000000 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_pxp/lv_gpu_nxp_pxp.h +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @file lv_gpu_nxp_pxp.h - * - */ - -/** - * MIT License - * - * Copyright (c) 2020 NXP - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next paragraph) - * shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_ -#define LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#include "../../lv_conf_internal.h" - -#if LV_USE_GPU_NXP_PXP - -#include "../misc/lv_area.h" -#include "../misc/lv_color.h" - -/********************* - * DEFINES - *********************/ - -/** PXP module instance to use*/ -#define LV_GPU_NXP_PXP_ID PXP - -/** PXP interrupt line I*/ -#define LV_GPU_NXP_PXP_IRQ_ID PXP_IRQn - -#ifndef LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT -/** Minimum area (in pixels) for image copy with 100% opacity to be handled by PXP*/ -#define LV_GPU_NXP_PXP_BLIT_SIZE_LIMIT 32 -#endif - -#ifndef LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT -/** Minimum area (in pixels) for image copy with transparency to be handled by PXP*/ -#define LV_GPU_NXP_PXP_BLIT_OPA_SIZE_LIMIT 16 -#endif - -#ifndef LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT -/** Minimum invalidated area (in pixels) to be synchronized by PXP during buffer sync */ -#define LV_GPU_NXP_PXP_BUFF_SYNC_BLIT_SIZE_LIMIT 32 -#endif - -#ifndef LV_GPU_NXP_PXP_FILL_SIZE_LIMIT -/** Minimum area (in pixels) to be filled by PXP with 100% opacity*/ -#define LV_GPU_NXP_PXP_FILL_SIZE_LIMIT 64 -#endif - -#ifndef LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT -/** Minimum area (in pixels) to be filled by PXP with transparency*/ -#define LV_GPU_NXP_PXP_FILL_OPA_SIZE_LIMIT 32 -#endif - -/********************** - * TYPEDEFS - **********************/ -/** - * NXP PXP device configuration - call-backs used for - * interrupt init/wait/deinit. - */ -typedef struct { - /** Callback for PXP interrupt initialization*/ - lv_res_t (*pxp_interrupt_init)(void); - - /** Callback for PXP interrupt de-initialization*/ - void (*pxp_interrupt_deinit)(void); - - /** Callback that should start PXP and wait for operation complete*/ - void (*pxp_run)(void); -} lv_nxp_pxp_cfg_t; - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * MACROS - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/** - * Reset and initialize PXP device. This function should be called as a part - * of display init sequence. - * - * @return LV_RES_OK: PXP init ok; LV_RES_INV: init error. See error log for more information. - */ -lv_res_t lv_gpu_nxp_pxp_init(lv_nxp_pxp_cfg_t * cfg); - -/** - * Disable PXP device. Should be called during display deinit sequence. - */ -void lv_gpu_nxp_pxp_deinit(void); - -/** - * Fill area, with optional opacity. - * - * @param[in/out] dest_buf destination buffer - * @param[in] dest_width width (stride) of destination buffer in pixels - * @param[in] fill_area area to fill - * @param[in] color color - * @param[in] opa transparency of the color - */ -void lv_gpu_nxp_pxp_fill(lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color, - lv_opa_t opa); - -/** - * @brief BLock Image Transfer - copy rectangular image from src buffer to dst buffer with effects. - * - * By default, image is copied directly, with optional opacity configured by \p opa. - * Color keying can be enabled by calling lv_gpu_nxp_pxp_enable_color_key() before calling this function. - * Recoloring can be enabled by calling lv_gpu_nxp_pxp_enable_recolor() before calling this function. - * Note that color keying and recoloring at the same time is not supported and black rectangle is rendered. - * - * @param[in/out] dest destination buffer - * @param[in] dest_width width (stride) of destination buffer in pixels - * @param[in] src source buffer - * @param[in] src_with width (stride) of source buffer in pixels - * @param[in] copy_w width of area to be copied from src to dest - * @param[in] copy_h height of area to be copied from src to dest - * @param[in] opa opacity of the result - */ -void lv_gpu_nxp_pxp_blit(lv_color_t * dest, lv_coord_t dest_width, const lv_color_t * src, lv_coord_t src_width, - lv_coord_t copy_width, lv_coord_t copy_height, lv_opa_t opa); - -/** - * @brief Enable color keying for subsequent calls to lv_gpu_nxp_pxp_blit() - * - * Color key is defined by LV_COLOR_TRANSP symbol in lv_conf.h - */ -void lv_gpu_nxp_pxp_enable_color_key(void); - -/** - * @brief Disable color keying for subsequent calls to lv_gpu_nxp_pxp_blit() - * - */ -void lv_gpu_nxp_pxp_disable_color_key(void); - -/** - * @brief Enable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit() - * - * @param[in] color recolor value - * @param[in] opa effect opacity - */ -void lv_gpu_nxp_pxp_enable_recolor(lv_color_t color, lv_opa_t opa); - -/** - * @brief Disable recolor feature for subsequent calls to lv_gpu_nxp_pxp_blit() - */ -void lv_gpu_nxp_pxp_disable_recolor(void); - -/********************** - * STATIC FUNCTIONS - **********************/ - -#endif /*LV_USE_GPU_NXP_PXP*/ - -#ifdef __cplusplus -} /*extern "C"*/ -#endif - -#endif /*LV_SRC_LV_GPU_LV_GPU_NXP_PXP_H_*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.c b/lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.c deleted file mode 100644 index 43dd88179..000000000 --- a/lib/libesp32_lvgl/lvgl/src/draw/nxp_vglite/lv_gpu_nxp_vglite.c +++ /dev/null @@ -1,770 +0,0 @@ -/** - * @file lv_gpu_nxp_vglite.c - * - */ - -/** - * MIT License - * - * Copyright (c) 2020 NXP - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next paragraph) - * shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE - * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/********************* - * INCLUDES - *********************/ - -#include "lv_gpu_nxp_vglite.h" - -#if LV_USE_GPU_NXP_VG_LITE - -#include "lvgl.h" -#include "../misc/lv_log.h" -#include "fsl_cache.h" -#include "vg_lite.h" -#include "fsl_debug_console.h" - -/********************* - * DEFINES - *********************/ - -#if LV_COLOR_DEPTH==16 - #define VGLITE_PX_FMT VG_LITE_RGB565 -#else - #error Only 16bit color depth is supported. Set LV_COLOR_DEPTH to 16. -#endif - -/* Enable BLIT quality degradation workaround for RT595 */ -#define RT595_BLIT_WRKRND_ENABLED 1 - -/* If LV_HOR_RES_MAX/LV_VER_RES_MAX is higher than this value, workaround will be enabled */ -#define RT595_BLIT_WRKRND_THR 352 - -/* Print detailed info to SDK console (NOT to LVGL log system) */ -#define BLIT_DBG_VERBOSE 0 - -/* Draw rectangles around BLIT tiles */ -#define BLIT_DBG_AREAS 0 - -/* Redirect PRINT to SDK PRINTF */ -#define PRINT PRINTF - -/* Verbose debug print */ -#if BLIT_DBG_VERBOSE - #define PRINT_BLT PRINTF -#else - #define PRINT_BLT(...) -#endif - -/* Internal compound symbol */ -#if (defined(CPU_MIMXRT595SFFOB) || defined(CPU_MIMXRT595SFFOB_cm33) || \ - defined(CPU_MIMXRT595SFFOC) || defined(CPU_MIMXRT595SFFOC_cm33)) && \ - ((LV_HOR_RES_MAX > RT595_BLIT_WRKRND_THR) || (LV_VER_RES_MAX > RT595_BLIT_WRKRND_THR)) && \ - RT595_BLIT_WRKRND_ENABLED -#define _BLIT_SPLIT_ENABLED 1 -#else -#define _BLIT_SPLIT_ENABLED 0 -#endif - -/* BLIT split threshold - BLITs with width or height higher than this value will be done - * in multiple steps. Value must be 16-aligned. Don't change. - * */ -#define LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR 352 - - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ - -static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, - const lv_color_t * ptr, bool source); - -static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit); -#if _BLIT_SPLIT_ENABLED -static void _align_x(lv_area_t * area, lv_color_t ** buf); -static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx); -static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit); -#if BLIT_DBG_AREAS -static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, - lv_area_t * fill_area, lv_color_t color); -#endif -static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit); -#endif - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * MACROS - **********************/ - -#define CHECK(cond, txt) \ - do { \ - if (cond) { \ - PRINT("%s. STOP!\n", txt); \ - for ( ; ; ); \ - } \ - } while(0) - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -/*** - * Fills rectangular area in buffer. - * @param[in] dest_buf Destination buffer pointer (must be aligned on 32 bytes) - * @param[in] dest_width Destination buffer width in pixels (must be aligned on 16 px) - * @param[in] dest_height Destination buffer height in pixels - * @param[in] fill_area Area to be filled - * @param[in] color Fill color - * @param[in] opa Opacity (255 = full, 128 = 50% background/50% color, 0 = no fill) - * @retval LV_RES_OK Fill completed - * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) - */ -lv_res_t lv_gpu_nxp_vglite_fill(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, - const lv_area_t * fill_area, lv_color_t color, lv_opa_t opa) -{ - vg_lite_buffer_t rt; - vg_lite_rectangle_t rect; - vg_lite_error_t err = VG_LITE_SUCCESS; - lv_color32_t col32 = {.full = lv_color_to32(color)}; /*Convert color to RGBA8888*/ - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - if(_init_vg_buf(&rt, (uint32_t) dest_width, (uint32_t) dest_height, (uint32_t) dest_width * sizeof(lv_color_t), - (const lv_color_t *) dest_buf, false) != LV_RES_OK) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("init_vg_buf reported error. Fill failed."); -#endif - return LV_RES_INV; - } - - if(opa >= (lv_opa_t) LV_OPA_MAX) { /*Opaque fill*/ - rect.x = fill_area->x1; - rect.y = fill_area->y1; - rect.width = (int32_t) fill_area->x2 - (int32_t) fill_area->x1 + 1; - rect.height = (int32_t) fill_area->y2 - (int32_t) fill_area->y1 + 1; - - if(disp != NULL && disp->driver->clean_dcache_cb != NULL) { /*Clean & invalidate cache*/ - disp->driver->clean_dcache_cb(disp->driver); - } - - err = vg_lite_clear(&rt, &rect, col32.full); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_clear reported error. Fill failed."); -#endif - return LV_RES_INV; - } - err = vg_lite_finish(); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_finish reported error. Fill failed."); -#endif - return LV_RES_INV; - } - } - else { /*fill with transparency*/ - - vg_lite_path_t path; - lv_color32_t colMix; - int16_t path_data[] = { /*VG rectangular path*/ - VLC_OP_MOVE, fill_area->x1, fill_area->y1, - VLC_OP_LINE, fill_area->x2 + 1, fill_area->y1, - VLC_OP_LINE, fill_area->x2 + 1, fill_area->y2 + 1, - VLC_OP_LINE, fill_area->x1, fill_area->y2 + 1, - VLC_OP_LINE, fill_area->x1, fill_area->y1, - VLC_OP_END - }; - - err = vg_lite_init_path(&path, VG_LITE_S16, VG_LITE_LOW, sizeof(path_data), path_data, - (vg_lite_float_t) fill_area->x1, (vg_lite_float_t) fill_area->y1, ((vg_lite_float_t) fill_area->x2) + 1.0f, - ((vg_lite_float_t) fill_area->y2) + 1.0f); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_init_path() failed."); -#endif - return LV_RES_INV; - } - - colMix.ch.red = (uint8_t)(((uint16_t)col32.ch.red * opa) >> 8); /*Pre-multiply color*/ - colMix.ch.green = (uint8_t)(((uint16_t)col32.ch.green * opa) >> 8); - colMix.ch.blue = (uint8_t)(((uint16_t)col32.ch.blue * opa) >> 8); - colMix.ch.alpha = opa; - - if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/ - disp->driver->clean_dcache_cb(disp->driver); - } - - vg_lite_matrix_t matrix; - vg_lite_identity(&matrix); - - /*Draw rectangle*/ - err = vg_lite_draw(&rt, &path, VG_LITE_FILL_EVEN_ODD, &matrix, VG_LITE_BLEND_SRC_OVER, colMix.full); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_draw() failed."); -#endif - vg_lite_clear_path(&path); - return LV_RES_INV; - } - - err = vg_lite_finish(); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_finish() failed."); -#endif - return LV_RES_INV; - } - - err = vg_lite_clear_path(&path); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_clear_path() failed."); -#endif - return LV_RES_INV; - } - } - - if(err == VG_LITE_SUCCESS) { - return LV_RES_OK; - } - else { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("VG Lite Fill failed."); -#endif - return LV_RES_INV; - } -} - -/*** - * BLock Image Transfer. - * @param[in] blit Description of the transfer - * @retval LV_RES_OK Transfer complete - * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) - */ -lv_res_t lv_gpu_nxp_vglite_blit(lv_gpu_nxp_vglite_blit_info_t * blit) -{ -#if _BLIT_SPLIT_ENABLED - - lv_res_t rv = LV_RES_INV; - - if(_lv_gpu_nxp_vglite_check_blit(blit) != LV_RES_OK) { - PRINT_BLT("Blit check failed\n"); - return LV_RES_INV; - } - - PRINT_BLT("BLIT from: " - "Area: %03d,%03d - %03d,%03d " - "Addr: %d\n\n", - blit->src_area.x1, blit->src_area.y1, - blit->src_area.x2, blit->src_area.y2, - (uintptr_t) blit->src); - - PRINT_BLT("BLIT to: " - "Area: %03d,%03d - %03d,%03d " - "Addr: %d\n\n", - blit->dst_area.x1, blit->dst_area.y1, - blit->dst_area.x2, blit->dst_area.y2, - (uintptr_t) blit->src); - - /* Stage 1: Move starting pointers as close as possible to [x1, y1], so coordinates are as small as possible. */ - _align_x(&blit->src_area, (lv_color_t **)&blit->src); - _align_y(&blit->src_area, (lv_color_t **)&blit->src, blit->src_stride / sizeof(lv_color_t)); - _align_x(&blit->dst_area, (lv_color_t **)&blit->dst); - _align_y(&blit->dst_area, (lv_color_t **)&blit->dst, blit->dst_stride / sizeof(lv_color_t)); - - /* Stage 2: If we're in limit, do a single BLIT */ - if((blit->src_area.x2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) && - (blit->src_area.y2 < LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR)) { - PRINT_BLT("Simple blit!\n"); - return _lv_gpu_nxp_vglite_blit_single(blit); - }; - - /* Stage 3: Split the BLIT into multiple tiles */ - PRINT_BLT("Split blit!\n"); - - PRINT_BLT("Blit " - "([%03d,%03d], [%03d,%03d]) -> " - "([%03d,%03d], [%03d,%03d]) | " - "([%03dx%03d] -> [%03dx%03d]) | " - "A:(%d -> %d)\n", - blit->src_area.x1, blit->src_area.y1, blit->src_area.x2, blit->src_area.y2, - blit->dst_area.x1, blit->dst_area.y1, blit->dst_area.x2, blit->dst_area.y2, - lv_area_get_width(&blit->src_area), lv_area_get_height(&blit->src_area), - lv_area_get_width(&blit->dst_area), lv_area_get_height(&blit->dst_area), - (uintptr_t) blit->src, (uintptr_t) blit->dst); - - - uint32_t totalWidth = lv_area_get_width(&blit->src_area); - uint32_t totalHeight = lv_area_get_height(&blit->src_area); - - lv_gpu_nxp_vglite_blit_info_t tileBlit; - - /* Number of tiles needed */ - int totalTilesX = (blit->src_area.x1 + totalWidth + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / - LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; - int totalTilesY = (blit->src_area.y1 + totalHeight + LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1) / - LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; - - /* src and dst buffer shift against each other. Src buffer real data [0,0] may start actually at [3,0] in buffer, as - * the buffer pointer has to be aligned, while dst buffer real data [0,0] may start at [1,0] in buffer. alignment may be - * different */ - int shiftSrcX = (blit->src_area.x1 > blit->dst_area.x1) ? (blit->src_area.x1 - blit->dst_area.x1) : 0; - int shiftDstX = (blit->src_area.x1 < blit->dst_area.x1) ? (blit->dst_area.x1 - blit->src_area.x1) : 0; - - PRINT_BLT("\n"); - PRINT_BLT("Align shift: src: %d, dst: %d\n", shiftSrcX, shiftDstX); - - tileBlit = *blit; - - for(int tileY = 0; tileY < totalTilesY; tileY++) { - - tileBlit.src_area.y1 = 0; /* no vertical alignment, always start from 0 */ - tileBlit.src_area.y2 = totalHeight - tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; - if(tileBlit.src_area.y2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { - tileBlit.src_area.y2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; /* Should never happen */ - } - tileBlit.src = blit->src + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->src_stride / sizeof( - lv_color_t); /* stride in px! */ - - tileBlit.dst_area.y1 = tileBlit.src_area.y1; /* y has no alignment, always in sync with src */ - tileBlit.dst_area.y2 = tileBlit.src_area.y2; - - tileBlit.dst = blit->dst + tileY * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR * blit->dst_stride / sizeof( - lv_color_t); /* stride in px! */ - - for(int tileX = 0; tileX < totalTilesX; tileX++) { - - if(tileX == 0) { - /* 1st tile is special - there may be a gap between buffer start pointer - * and area.x1 value, as the pointer has to be aligned. - * tileBlit.src pointer - keep init value from Y-loop. - * Also, 1st tile start is not shifted! shift is applied from 2nd tile */ - tileBlit.src_area.x1 = blit->src_area.x1; - tileBlit.dst_area.x1 = blit->dst_area.x1; - } - else { - /* subsequent tiles always starts from 0, but shifted*/ - tileBlit.src_area.x1 = 0 + shiftSrcX; - tileBlit.dst_area.x1 = 0 + shiftDstX; - /* and advance start pointer + 1 tile size */ - tileBlit.src += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; - tileBlit.dst += LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR; - } - - /* Clip tile end coordinates */ - tileBlit.src_area.x2 = totalWidth + blit->src_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; - if(tileBlit.src_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { - tileBlit.src_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; - } - - tileBlit.dst_area.x2 = totalWidth + blit->dst_area.x1 - tileX * LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; - if(tileBlit.dst_area.x2 >= LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR) { - tileBlit.dst_area.x2 = LV_GPU_NXP_VG_LITE_BLIT_SPLIT_THR - 1; - } - - if(tileX < (totalTilesX - 1)) { - /* And adjust end coords if shifted, but not for last tile! */ - tileBlit.src_area.x2 += shiftSrcX; - tileBlit.dst_area.x2 += shiftDstX; - } - - rv = _lv_gpu_nxp_vglite_blit_single(&tileBlit); - -#if BLIT_DBG_AREAS - _draw_rectangle((lv_color_t *) tileBlit.dst, tileBlit.dst_width, tileBlit.dst_height, &tileBlit.dst_area, LV_COLOR_RED); - _draw_rectangle((lv_color_t *) tileBlit.src, tileBlit.src_width, tileBlit.src_height, &tileBlit.src_area, - LV_COLOR_GREEN); -#endif - - PRINT_BLT("Tile [%d, %d]: " - "([%d,%d], [%d,%d]) -> " - "([%d,%d], [%d,%d]) | " - "([%dx%d] -> [%dx%d]) | " - "A:(0x%8X -> 0x%8X) %s\n", - tileX, tileY, - tileBlit.src_area.x1, tileBlit.src_area.y1, tileBlit.src_area.x2, tileBlit.src_area.y2, - tileBlit.dst_area.x1, tileBlit.dst_area.y1, tileBlit.dst_area.x2, tileBlit.dst_area.y2, - lv_area_get_width(&tileBlit.src_area), lv_area_get_height(&tileBlit.src_area), - lv_area_get_width(&tileBlit.dst_area), lv_area_get_height(&tileBlit.dst_area), - (uintptr_t) tileBlit.src, (uintptr_t) tileBlit.dst, - rv == LV_RES_OK ? "OK!" : "!!! FAILED !!!"); - - if(rv != LV_RES_OK) { /* if anything goes wrong... */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("Split BLIT failed. Trying SW BLIT instead."); -#endif - _sw_blit(&tileBlit); - rv = LV_RES_OK; /* Don't report error, as SW BLIT was performed */ - } - - } - PRINT_BLT(" \n"); - } - - return rv; /* should never fail */ - -#else /* non RT595 */ - /* Just pass down */ - return _lv_gpu_nxp_vglite_blit_single(blit); -#endif -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -/*** - * BLock Image Transfer - single direct BLIT. - * @param[in] blit Description of the transfer - * @retval LV_RES_OK Transfer complete - * @retval LV_RES_INV Error occurred (\see LV_GPU_NXP_VG_LITE_LOG_ERRORS) - */ -static lv_res_t _lv_gpu_nxp_vglite_blit_single(lv_gpu_nxp_vglite_blit_info_t * blit) -{ - vg_lite_buffer_t src_vgbuf, dst_vgbuf; - vg_lite_error_t err = VG_LITE_SUCCESS; - uint32_t rect[4]; - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - - if(blit == NULL) { - /*Wrong parameter*/ - return LV_RES_INV; - } - - if(blit->opa < (lv_opa_t) LV_OPA_MIN) { - return LV_RES_OK; /*Nothing to BLIT*/ - } - - /*Wrap src/dst buffer into VG-Lite buffer*/ - if(_init_vg_buf(&src_vgbuf, (uint32_t) blit->src_width, (uint32_t) blit->src_height, (uint32_t) blit->src_stride, - blit->src, true) != LV_RES_OK) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("init_vg_buf reported error. BLIT failed."); -#endif - return LV_RES_INV; - } - - if(_init_vg_buf(&dst_vgbuf, (uint32_t) blit->dst_width, (uint32_t) blit->dst_height, (uint32_t) blit->dst_stride, - blit->dst, false) != LV_RES_OK) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("init_vg_buf reported error. BLIT failed."); -#endif - return LV_RES_INV; - } - - rect[0] = (uint32_t) blit->src_area.x1; /* start x */ - rect[1] = (uint32_t) blit->src_area.y1; /* start y */ - rect[2] = (uint32_t) blit->src_area.x2 - (uint32_t) blit->src_area.x1 + 1U; /* width */ - rect[3] = (uint32_t) blit->src_area.y2 - (uint32_t) blit->src_area.y1 + 1U; /* height */ - - vg_lite_matrix_t matrix; - vg_lite_identity(&matrix); - vg_lite_translate((vg_lite_float_t)blit->dst_area.x1, (vg_lite_float_t)blit->dst_area.y1, &matrix); - - if((disp != NULL) && (disp->driver->clean_dcache_cb != NULL)) { /*Clean & invalidate cache*/ - disp->driver->clean_dcache_cb(disp->driver); - } - - uint32_t color; - vg_lite_blend_t blend; - if(blit->opa >= (uint8_t) LV_OPA_MAX) { - color = 0x0; - blend = VG_LITE_BLEND_NONE; - } - else { - uint32_t opa = (uint32_t) blit->opa; - color = (opa << 24) | (opa << 16) | (opa << 8) | opa; - blend = VG_LITE_BLEND_SRC_OVER; - src_vgbuf.image_mode = VG_LITE_MULTIPLY_IMAGE_MODE; - } - - err = vg_lite_blit_rect(&dst_vgbuf, &src_vgbuf, rect, &matrix, blend, color, VG_LITE_FILTER_POINT); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_blit_rect() failed."); -#endif - return LV_RES_INV; - } - - err = vg_lite_finish(); - if(err != VG_LITE_SUCCESS) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_finish() failed."); -#endif - return LV_RES_INV; - } - - if(err == VG_LITE_SUCCESS) { - return LV_RES_OK; - } - else { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("vg_lite_blit_rect or vg_lite_finish reported error. BLIT failed."); -#endif - return LV_RES_INV; - } -} - -/*** - * Fills vg_lite_buffer_t structure according given parameters. - * @param[out] dst Buffer structure to be filled - * @param[in] width Width of buffer in pixels - * @param[in] height Height of buffer in pixels - * @param[in] stride Stride of the buffer in bytes - * @param[in] ptr Pointer to the buffer (must be aligned according VG-Lite requirements) - */ -static lv_res_t _init_vg_buf(vg_lite_buffer_t * dst, uint32_t width, uint32_t height, uint32_t stride, - const lv_color_t * ptr, bool source) -{ - if((((uintptr_t)ptr) % (uintptr_t)LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0U) { /*Test for alignment*/ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("ptr (0x%X) not aligned to %d.", (size_t) ptr, LV_ATTRIBUTE_MEM_ALIGN_SIZE); -#endif - return LV_RES_INV; - } - - if(source && - (stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t))) != 0x0U) { /*Test for stride alignment*/ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("Buffer stride (%d px) not aligned to %d bytes.", stride, - LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * sizeof(lv_color_t)); -#endif - return LV_RES_INV; - } - - dst->format = VGLITE_PX_FMT; - dst->tiled = VG_LITE_LINEAR; - dst->image_mode = VG_LITE_NORMAL_IMAGE_MODE; - dst->transparency_mode = VG_LITE_IMAGE_OPAQUE; - - dst->width = (int32_t) width; - dst->height = (int32_t) height; - dst->stride = (int32_t) stride; - - void * r_ptr = memset(&dst->yuv, 0, sizeof(dst->yuv)); - if(r_ptr == NULL) { - return LV_RES_INV; - } - - dst->memory = (void *)ptr; - dst->address = (uint32_t) dst->memory; - dst->handle = NULL; - - return LV_RES_OK; -} - -#if _BLIT_SPLIT_ENABLED - -/** - * Software BLIT as a fall-back scenario - * @param[in] blit BLIT configuration - */ -static void _sw_blit(lv_gpu_nxp_vglite_blit_info_t * blit) -{ - int x, y; - - lv_coord_t w = lv_area_get_width(&blit->src_area); - lv_coord_t h = lv_area_get_height(&blit->src_area); - - uint32_t srcStridePx = blit->src_stride / sizeof(lv_color_t); - uint32_t dstStridePx = blit->dst_stride / sizeof(lv_color_t); - - lv_color_t * src = (lv_color_t *)blit->src + blit->src_area.y1 * srcStridePx + blit->src_area.x1; - lv_color_t * dst = (lv_color_t *)blit->dst + blit->dst_area.y1 * dstStridePx + blit->dst_area.x1; - - if(blit->opa >= LV_OPA_MAX) { - /* simple copy */ - for(y = 0; y < h; y++) { - _lv_memcpy(dst, src, w * sizeof(lv_color_t)); - src += srcStridePx; - dst += dstStridePx; - } - } - else if(blit->opa >= LV_OPA_MIN) { - /* alpha blending */ - for(y = 0; y < h; y++) { - for(x = 0; x < w; x++) { - dst[x] = lv_color_mix(src[x], dst[x], blit->opa); - } - src += srcStridePx; - dst += dstStridePx; - } - } -} - -/** - * Verify BLIT structure - widths, stride, pointer alignment - * @param[in] blit - * @return - */ -static lv_res_t _lv_gpu_nxp_vglite_check_blit(lv_gpu_nxp_vglite_blit_info_t * blit) -{ - - if(lv_area_get_width(&blit->src_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("source area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->src_area), - LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); -#endif - return LV_RES_INV; - } - - if(lv_area_get_width(&blit->dst_area) < LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) { /* Test for minimal width */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("destination area width (%d) is smaller than required (%d).", lv_area_get_width(&blit->dst_area), - LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); -#endif - return LV_RES_INV; - } - - if((((uintptr_t) blit->src) % LV_ATTRIBUTE_MEM_ALIGN_SIZE) != 0x0) { /* Test for pointer alignment */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("source buffer ptr (0x%X) not aligned to %d.", (size_t) blit->src, LV_ATTRIBUTE_MEM_ALIGN_SIZE); -#endif - return LV_RES_INV; - } - /* No alignment requirement for destination pixel buffer when using mode VG_LITE_LINEAR */ - - if((blit->src_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != - 0x0) { /* Test for stride alignment */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("source buffer stride (%d px) not aligned to %d px.", blit->src_stride, - LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); -#endif - return LV_RES_INV; - } - - if((blit->dst_stride % (LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX * LV_COLOR_DEPTH / 8)) != - 0x0) { /* Test for stride alignment */ -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("destination buffer stride (%d px) not aligned to %d px.", blit->dst_stride, - LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); -#endif - return LV_RES_INV; - } - - if((lv_area_get_width(&blit->src_area) != lv_area_get_width(&blit->dst_area)) || - (lv_area_get_height(&blit->src_area) != lv_area_get_height(&blit->dst_area))) { -#if LV_GPU_NXP_VG_LITE_LOG_ERRORS - LV_LOG_ERROR("source and destination buffer areas are not equal."); -#endif - return LV_RES_INV; - } - - return LV_RES_OK; -} - -/*** - * Move buffer pointer as close as possible to area, but with respect to alignment requirements. X-axis only. - * @param[in,out] area Area to be updated - * @param[in,out] buf Pointer to be updated - */ -static void _align_x(lv_area_t * area, lv_color_t ** buf) -{ - - int alignedAreaStartPx = area->x1 - (area->x1 % (LV_ATTRIBUTE_MEM_ALIGN_SIZE * 8 / LV_COLOR_DEPTH)); - CHECK(alignedAreaStartPx < 0, "Should never happen."); - - area->x1 -= alignedAreaStartPx; - area->x2 -= alignedAreaStartPx; - *buf += alignedAreaStartPx; -} - -/*** - * Move buffer pointer to the area start and update variables, Y-axis only. - * @param[in,out] area Area to be updated - * @param[in,out] buf Pointer to be updated - * @param[in] stridePx Buffer stride in pixels - */ -static void _align_y(lv_area_t * area, lv_color_t ** buf, uint32_t stridePx) -{ - int LineToAlignMem; - int alignedAreaStartPy; - /* find how many lines of pixels will respect memory alignment requirement */ - if(stridePx % LV_ATTRIBUTE_MEM_ALIGN_SIZE == 0) { - alignedAreaStartPy = area->y1; - } - else { - LineToAlignMem = LV_ATTRIBUTE_MEM_ALIGN_SIZE / (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX); - CHECK(LV_ATTRIBUTE_MEM_ALIGN_SIZE % (sizeof(lv_color_t) * LV_GPU_NXP_VG_LITE_STRIDE_ALIGN_PX) != 0, - "Complex case: need gcd function."); - alignedAreaStartPy = area->y1 - (area->y1 % LineToAlignMem); - CHECK(alignedAreaStartPy < 0, "Should never happen."); - } - - area->y1 -= alignedAreaStartPy; - area->y2 -= alignedAreaStartPy; - *buf += alignedAreaStartPy * stridePx; -} - -#if BLIT_DBG_AREAS -/*** - * Draws a simple rectangle, 1 px line width. - * @param dest_buf Destination buffer - * @param dest_width Destination buffer width (must be aligned on 16px) - * @param dest_height Destination buffer height - * @param fill_area Rectangle coordinates - * @param color Rectangle color - */ -static void _draw_rectangle(lv_color_t * dest_buf, lv_coord_t dest_width, lv_coord_t dest_height, - lv_area_t * fill_area, lv_color_t color) -{ - - lv_area_t a; - - /* top line */ - a.x1 = fill_area->x1; - a.x2 = fill_area->x2; - a.y1 = fill_area->y1; - a.y2 = fill_area->y1; - lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); - - - /* bottom line */ - a.x1 = fill_area->x1; - a.x2 = fill_area->x2; - a.y1 = fill_area->y2; - a.y2 = fill_area->y2; - lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); - - /* left line */ - a.x1 = fill_area->x1; - a.x2 = fill_area->x1; - a.y1 = fill_area->y1; - a.y2 = fill_area->y2; - lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); - - /* right line */ - a.x1 = fill_area->x2; - a.x2 = fill_area->x2; - a.y1 = fill_area->y1; - a.y2 = fill_area->y2; - lv_gpu_nxp_vglite_fill(dest_buf, dest_width, dest_height, &a, color, LV_OPA_COVER); -} -#endif /* BLIT_DBG_AREAS */ - -#endif /* _BLIT_SPLIT_ENABLED */ - -#endif /*LV_USE_GPU_NXP_VG_LITE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.c index 430f3a23c..e3cdf577a 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.c @@ -15,6 +15,7 @@ #include "lv_draw_sdl.h" #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_texture_cache.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -69,6 +70,10 @@ void lv_draw_sdl_init_ctx(lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx) draw_ctx->draw_arc = lv_draw_sdl_draw_arc; draw_ctx->draw_polygon = lv_draw_sdl_polygon; draw_ctx->draw_bg = lv_draw_sdl_draw_bg; + draw_ctx->layer_init = lv_draw_sdl_layer_init; + draw_ctx->layer_blend = lv_draw_sdl_layer_blend; + draw_ctx->layer_destroy = lv_draw_sdl_layer_destroy; + draw_ctx->layer_instance_size = sizeof(lv_draw_sdl_layer_ctx_t); lv_draw_sdl_ctx_t * draw_ctx_sdl = (lv_draw_sdl_ctx_t *) draw_ctx; draw_ctx_sdl->renderer = ((lv_draw_sdl_drv_param_t *) disp_drv->user_data)->renderer; draw_ctx_sdl->internals = lv_mem_alloc(sizeof(lv_draw_sdl_context_internals_t)); diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.mk b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.mk index 6609c38bc..c5c28b66b 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.mk +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl.mk @@ -11,6 +11,7 @@ CSRCS += lv_draw_sdl_rect.c CSRCS += lv_draw_sdl_stack_blur.c CSRCS += lv_draw_sdl_texture_cache.c CSRCS += lv_draw_sdl_utils.c +CSRCS += lv_draw_sdl_layer.c DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sdl diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.c index 1ee843f7a..e6007d1a5 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.c @@ -13,7 +13,6 @@ #include "../../misc/lv_gc.h" #include "../../core/lv_refr.h" #include "lv_draw_sdl_composite.h" -#include "lv_draw_sdl_mask.h" #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_priv.h" #include "lv_draw_sdl_texture_cache.h" @@ -84,15 +83,16 @@ bool lv_draw_sdl_composite_begin(lv_draw_sdl_ctx_t * ctx, const lv_area_t * coor const bool draw_blend = blend_mode != LV_BLEND_MODE_NORMAL; if(draw_mask || draw_blend) { lv_draw_sdl_context_internals_t * internals = ctx->internals; - LV_ASSERT(internals->mask == NULL && internals->composition == NULL); + LV_ASSERT(internals->mask == NULL && internals->composition == NULL && internals->target_backup == NULL); lv_coord_t w = lv_area_get_width(apply_area), h = lv_area_get_height(apply_area); internals->composition = lv_draw_sdl_composite_texture_obtain(ctx, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, w, h); - /* Don't need to worry about overflow */ + /* Don't need to worry about integral overflow */ lv_coord_t ofs_x = (lv_coord_t) - apply_area->x1, ofs_y = (lv_coord_t) - apply_area->y1; /* Offset draw area to start with (0,0) of coords */ lv_area_move(coords_out, ofs_x, ofs_y); lv_area_move(clip_out, ofs_x, ofs_y); + internals->target_backup = SDL_GetRenderTarget(ctx->renderer); SDL_SetRenderTarget(ctx->renderer, internals->composition); SDL_SetRenderDrawColor(ctx->renderer, 255, 255, 255, 0); SDL_RenderClear(ctx->renderer); @@ -140,7 +140,7 @@ void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_ SDL_Rect dst_rect; lv_area_to_sdl_rect(apply_area, &dst_rect); - SDL_SetRenderTarget(ctx->renderer, ctx->base_draw.buf); + SDL_SetRenderTarget(ctx->renderer, internals->target_backup); switch(blend_mode) { case LV_BLEND_MODE_NORMAL: SDL_SetTextureBlendMode(internals->composition, SDL_BLENDMODE_BLEND); @@ -173,7 +173,7 @@ void lv_draw_sdl_composite_end(lv_draw_sdl_ctx_t * ctx, const lv_area_t * apply_ SDL_RenderCopy(ctx->renderer, internals->composition, &src_rect, &dst_rect); } - internals->mask = internals->composition = NULL; + internals->mask = internals->composition = internals->target_backup = NULL; } SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_draw_sdl_composite_texture_id_t id, @@ -186,7 +186,10 @@ SDL_Texture * lv_draw_sdl_composite_texture_obtain(lv_draw_sdl_ctx_t * ctx, lv_d if(!result || tex_size->x < w || tex_size->y < h) { lv_coord_t size = next_pow_of_2(LV_MAX(w, h)); int access = SDL_TEXTUREACCESS_STREAMING; - if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) { + if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0) { + access = SDL_TEXTUREACCESS_TARGET; + } + else if(id >= LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0) { access = SDL_TEXTUREACCESS_TARGET; } result = SDL_CreateTexture(ctx->renderer, LV_DRAW_SDL_TEXTURE_FORMAT, access, size, size); diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.h b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.h index 3050815d7..72a2daef7 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_composite.h @@ -35,6 +35,7 @@ typedef enum lv_draw_sdl_composite_texture_id_t { LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_STREAM1, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET0, LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TARGET1, + LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0, } lv_draw_sdl_composite_texture_id_t; /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_img.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_img.c index c6782386e..59a9fc00f 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_img.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_img.c @@ -22,6 +22,7 @@ #include "lv_draw_sdl_texture_cache.h" #include "lv_draw_sdl_composite.h" #include "lv_draw_sdl_rect.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -123,11 +124,15 @@ lv_res_t lv_draw_sdl_img_core(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t /* Coords will be translated so coords will start at (0,0) */ lv_area_t t_coords = zoomed_cords, t_clip = *clip, apply_area; + bool has_composite = false; + if(!check_mask_simple_radius(&t_coords, &radius)) { - lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode, - &t_coords, &t_clip, &apply_area); + has_composite = lv_draw_sdl_composite_begin(ctx, &zoomed_cords, clip, NULL, draw_dsc->blend_mode, + &t_coords, &t_clip, &apply_area); } + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip); + SDL_Rect clip_rect, coords_rect; lv_area_to_sdl_rect(&t_clip, &clip_rect); lv_area_to_sdl_rect(&t_coords, &coords_rect); diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_label.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_label.c index a067c8af4..b8c79ae4a 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_label.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_label.c @@ -19,6 +19,7 @@ #include "lv_draw_sdl_utils.h" #include "lv_draw_sdl_texture_cache.h" #include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -77,6 +78,21 @@ void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/ letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/ LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", letter); + + /* draw placeholder */ + lv_area_t glyph_coords; + lv_draw_rect_dsc_t glyph_dsc; + lv_coord_t begin_x = pos_p->x + g.ofs_x; + lv_coord_t begin_y = pos_p->y + g.ofs_y; + lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h); + lv_draw_rect_dsc_init(&glyph_dsc); + glyph_dsc.bg_opa = LV_OPA_MIN; + glyph_dsc.outline_opa = LV_OPA_MIN; + glyph_dsc.shadow_opa = LV_OPA_MIN; + glyph_dsc.bg_img_opa = LV_OPA_MIN; + glyph_dsc.border_color = dsc->color; + glyph_dsc.border_width = 1; + draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords); } return; } @@ -119,8 +135,10 @@ void lv_draw_sdl_draw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t } lv_area_t t_letter = letter_area, t_clip = *clip_area, apply_area; - bool has_mask = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter, &t_clip, - &apply_area); + bool has_composite = lv_draw_sdl_composite_begin(ctx, &letter_area, clip_area, NULL, dsc->blend_mode, &t_letter, + &t_clip, &apply_area); + + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_letter, &t_clip); /*If the letter is completely out of mask don't draw it*/ if(!_lv_area_intersect(&draw_area, &t_letter, &t_clip)) { diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.c new file mode 100644 index 000000000..48bc1b8f9 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.c @@ -0,0 +1,132 @@ +/** + * @file lv_draw_sdl_refr.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../lv_conf_internal.h" + +#if LV_USE_GPU_SDL + +#include "../../core/lv_refr.h" + +#include "lv_draw_sdl.h" +#include "lv_draw_sdl_priv.h" +#include "lv_draw_sdl_composite.h" +#include "lv_draw_sdl_utils.h" +#include "lv_draw_sdl_layer.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + SDL_Renderer * renderer = ctx->renderer; + + lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx; + + transform_ctx->flags = flags; + transform_ctx->orig_target = SDL_GetRenderTarget(renderer); + + lv_coord_t target_w = lv_area_get_width(&layer_ctx->area_full); + lv_coord_t target_h = lv_area_get_height(&layer_ctx->area_full); + + enum lv_draw_sdl_composite_texture_id_t texture_id = LV_DRAW_SDL_COMPOSITE_TEXTURE_ID_TRANSFORM0 + + ctx->internals->transform_count; + transform_ctx->target = lv_draw_sdl_composite_texture_obtain(ctx, texture_id, target_w, target_h); + transform_ctx->target_rect.x = 0; + transform_ctx->target_rect.y = 0; + transform_ctx->target_rect.w = target_w; + transform_ctx->target_rect.h = target_h; + + SDL_SetTextureBlendMode(transform_ctx->target, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, transform_ctx->target); + SDL_RenderClear(renderer); + + /* Set proper drawing context for transform layer */ + ctx->internals->transform_count += 1; + draw_ctx->buf_area = &layer_ctx->area_full; + draw_ctx->clip_area = &layer_ctx->area_full; + + return layer_ctx; +} + +void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + lv_draw_sdl_layer_ctx_t * transform_ctx = (lv_draw_sdl_layer_ctx_t *) layer_ctx; + + SDL_Renderer * renderer = ctx->renderer; + + SDL_Rect trans_rect; + + if(transform_ctx->flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + lv_area_zoom_to_sdl_rect(&layer_ctx->area_act, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot); + } + else { + lv_area_zoom_to_sdl_rect(&layer_ctx->area_full, &trans_rect, draw_dsc->zoom, &draw_dsc->pivot); + } + + SDL_SetRenderTarget(renderer, transform_ctx->orig_target); + + /*Render off-screen texture, transformed*/ + SDL_Rect clip_rect; + lv_area_to_sdl_rect(layer_ctx->original.clip_area, &clip_rect); + SDL_Point center = {.x = draw_dsc->pivot.x, .y = draw_dsc->pivot.y}; + SDL_RenderSetClipRect(renderer, &clip_rect); + SDL_RenderCopyEx(renderer, transform_ctx->target, &transform_ctx->target_rect, &trans_rect, + draw_dsc->angle, ¢er, SDL_FLIP_NONE); + SDL_RenderSetClipRect(renderer, NULL); +} + +void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + lv_draw_sdl_ctx_t * ctx = (lv_draw_sdl_ctx_t *) draw_ctx; + ctx->internals->transform_count -= 1; +} + +void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area, + lv_area_t * coords, lv_area_t * clip) +{ + if(ctx->internals->transform_count == 0) { + return; + } + lv_area_t * area = ctx->base_draw.buf_area; + lv_area_move(coords, -area->x1, -area->y1); + lv_area_move(clip, -area->x1, -area->y1); + if(has_composite) { + lv_area_move(apply_area, -area->x1, -area->y1); + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_GPU_SDL*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.h b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.h new file mode 100644 index 000000000..b60303c47 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_layer.h @@ -0,0 +1,55 @@ +/** + * @file lv_draw_sdl_refr.h + * + */ + +#ifndef LV_TEMPL_H +#define LV_TEMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sdl.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct _lv_draw_sdl_layer_ctx_t { + lv_draw_layer_ctx_t base; + + SDL_Texture * orig_target; + SDL_Texture * target; + SDL_Rect target_rect; + lv_draw_layer_flags_t flags; +} lv_draw_sdl_layer_ctx_t; +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_draw_layer_ctx_t * lv_draw_sdl_layer_init(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sdl_layer_blend(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * transform_ctx, + const lv_draw_img_dsc_t * draw_dsc); + +void lv_draw_sdl_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + +void lv_draw_sdl_transform_areas_offset(lv_draw_sdl_ctx_t * ctx, bool has_composite, lv_area_t * apply_area, + lv_area_t * coords, lv_area_t * clip); +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_TEMPL_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_priv.h b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_priv.h index 1f44c22ac..24a876218 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_priv.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_priv.h @@ -35,6 +35,8 @@ typedef struct lv_draw_sdl_context_internals_t { lv_lru_t * texture_cache; SDL_Texture * mask; SDL_Texture * composition; + SDL_Texture * target_backup; + uint8_t transform_count; } lv_draw_sdl_context_internals_t; /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_rect.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_rect.c index 4df113efb..a303ac764 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_rect.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_rect.c @@ -21,7 +21,7 @@ #include "lv_draw_sdl_composite.h" #include "lv_draw_sdl_mask.h" #include "lv_draw_sdl_stack_blur.h" -#include "lv_draw_sdl_img.h" +#include "lv_draw_sdl_layer.h" /********************* * DEFINES @@ -124,7 +124,11 @@ void lv_draw_sdl_draw_rect(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * } /* Coords will be translated so coords will start at (0,0) */ lv_area_t t_coords = *coords, t_clip = *clip, apply_area, t_area; - lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, &apply_area); + bool has_composite = lv_draw_sdl_composite_begin(ctx, coords, clip, &extension, dsc->blend_mode, &t_coords, &t_clip, + &apply_area); + + lv_draw_sdl_transform_areas_offset(ctx, has_composite, &apply_area, &t_coords, &t_clip); + bool has_content = _lv_area_intersect(&t_area, &t_coords, &t_clip); SDL_Rect clip_rect; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c index 58ff9f374..6845addf5 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sdl/lv_draw_sdl_texture_cache.c @@ -102,7 +102,7 @@ void lv_draw_sdl_texture_cache_put_advanced(lv_draw_sdl_ctx_t * ctx, const void } if(flags & LV_DRAW_SDL_CACHE_FLAG_MANAGED) { /* Managed texture doesn't count into cache size */ - LV_LOG_INFO("cache texture %p, %d*%d@%dbpp", texture, width, height, SDL_BITSPERPIXEL(format)); + LV_LOG_INFO("cache texture %p", texture); lv_lru_set(lru, key, key_length, value, 1); return; } diff --git a/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk new file mode 100644 index 000000000..8ed00b015 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_draw_stm32_dma2d.mk @@ -0,0 +1,6 @@ +CSRCS += lv_gpu_stm32_dma2d.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/stm32_dma2d" diff --git a/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c index 20344a567..4eb1940ef 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.c @@ -103,6 +103,7 @@ void lv_draw_stm32_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) dma2d_draw_ctx->blend = lv_draw_stm32_dma2d_blend; // dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_stm32_dma2d_img_decoded; dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_stm32_dma2d_wait_cb; + dma2d_draw_ctx->base_draw.buffer_copy = lv_draw_stm32_dma2d_buffer_copy; } @@ -146,6 +147,14 @@ void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_ if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc); } +void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area) +{ + LV_UNUSED(draw_ctx); + lv_draw_stm32_dma2d_blend_map(dest_buf, dest_area, dest_stride, src_buf, src_stride, LV_OPA_MAX); +} + static void lv_draw_stm32_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format) @@ -213,8 +222,8 @@ static void lv_draw_stm32_dma2d_blend_map(lv_color_t * dest_buf, const lv_area_t DMA2D->FGMAR = (uint32_t)src_buf; DMA2D->FGOR = src_stride - dest_w; - DMA2D->OMAR = (uint32_t)src_buf; - DMA2D->OOR = src_stride - dest_w; + DMA2D->OMAR = (uint32_t)dest_buf; + DMA2D->OOR = dest_stride - dest_w; DMA2D->NLR = (dest_w << DMA2D_NLR_PL_Pos) | (dest_h << DMA2D_NLR_NL_Pos); /*start transfer*/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h index 73054ca2d..fa7070e2a 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/stm32_dma2d/lv_gpu_stm32_dma2d.h @@ -51,6 +51,10 @@ void lv_draw_stm32_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * void lv_draw_stm32_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); +void lv_draw_stm32_dma2d_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); + void lv_gpu_stm32_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx); /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.c index 21da4ee6b..1c0c6d4a2 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.c @@ -51,8 +51,17 @@ void lv_draw_sw_init_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) draw_sw_ctx->base_draw.draw_img_decoded = lv_draw_sw_img_decoded; draw_sw_ctx->base_draw.draw_line = lv_draw_sw_line; draw_sw_ctx->base_draw.draw_polygon = lv_draw_sw_polygon; +#if LV_DRAW_COMPLEX + draw_sw_ctx->base_draw.draw_transform = lv_draw_sw_transform; +#endif draw_sw_ctx->base_draw.wait_for_finish = lv_draw_sw_wait_for_finish; + draw_sw_ctx->base_draw.buffer_copy = lv_draw_sw_buffer_copy; + draw_sw_ctx->base_draw.layer_init = lv_draw_sw_layer_create; + draw_sw_ctx->base_draw.layer_adjust = lv_draw_sw_layer_adjust; + draw_sw_ctx->base_draw.layer_blend = lv_draw_sw_layer_blend; + draw_sw_ctx->base_draw.layer_destroy = lv_draw_sw_layer_destroy; draw_sw_ctx->blend = lv_draw_sw_blend_basic; + draw_ctx->layer_instance_size = sizeof(lv_draw_sw_layer_ctx_t); } void lv_draw_sw_deinit_ctx(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) @@ -69,6 +78,31 @@ void lv_draw_sw_wait_for_finish(lv_draw_ctx_t * draw_ctx) /*Nothing to wait for*/ } +void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area) +{ + LV_UNUSED(draw_ctx); + + lv_color_t * dest_bufc = dest_buf; + lv_color_t * src_bufc = src_buf; + + /*Got the first pixel of each buffer*/ + dest_bufc += dest_stride * dest_area->y1; + dest_bufc += dest_area->x1; + + src_bufc += src_stride * src_area->y1; + src_bufc += src_area->x1; + + uint32_t line_length = lv_area_get_width(dest_area) * sizeof(lv_color_t); + lv_coord_t y; + for(y = dest_area->y1; y <= dest_area->y2; y++) { + lv_memcpy(dest_bufc, src_bufc, line_length); + dest_bufc += dest_stride; + src_bufc += src_stride; + } +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.h b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.h index cba5b480b..1618649cf 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.h +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.h @@ -36,6 +36,13 @@ typedef struct { void (*blend)(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); } lv_draw_sw_ctx_t; +typedef struct { + lv_draw_layer_ctx_t base_draw; + + uint32_t buf_size_bytes: 31; + uint32_t has_alpha : 1; +} lv_draw_sw_layer_ctx_t; + /********************** * GLOBAL PROTOTYPES **********************/ @@ -63,6 +70,25 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_line(struct _lv_draw_ctx_t * draw_ctx, con void lv_draw_sw_polygon(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_point_t * points, uint16_t point_cnt); +void lv_draw_sw_buffer_copy(lv_draw_ctx_t * draw_ctx, + void * dest_buf, lv_coord_t dest_stride, const lv_area_t * dest_area, + void * src_buf, lv_coord_t src_stride, const lv_area_t * src_area); + +void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); + +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags); + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc); + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx); + /*********************** * GLOBAL VARIABLES ***********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.mk b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.mk index 386d9ed81..4625cbcfc 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.mk +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw.mk @@ -1,13 +1,15 @@ CSRCS += lv_draw_sw.c CSRCS += lv_draw_sw_arc.c CSRCS += lv_draw_sw_blend.c +CSRCS += lv_draw_sw_dither.c +CSRCS += lv_draw_sw_gradient.c CSRCS += lv_draw_sw_img.c CSRCS += lv_draw_sw_letter.c CSRCS += lv_draw_sw_line.c -CSRCS += lv_draw_sw_rect.c CSRCS += lv_draw_sw_polygon.c -CSRCS += lv_draw_sw_gradient.c -CSRCS += lv_draw_sw_dither.c +CSRCS += lv_draw_sw_rect.c +CSRCS += lv_draw_sw_transform.c +CSRCS += lv_draw_sw_layer.c DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/sw diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_arc.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_arc.c index d020fdd4f..3ed62b6ef 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_arc.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_arc.c @@ -97,8 +97,10 @@ void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, con /*Create inner the mask*/ int16_t mask_in_id = LV_MASK_ID_INV; lv_draw_mask_radius_param_t mask_in_param; + bool mask_in_param_valid = false; if(lv_area_get_width(&area_in) > 0 && lv_area_get_height(&area_in) > 0) { lv_draw_mask_radius_init(&mask_in_param, &area_in, LV_RADIUS_CIRCLE, true); + mask_in_param_valid = true; mask_in_id = lv_draw_mask_add(&mask_in_param, NULL); } @@ -115,7 +117,9 @@ void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, con if(mask_in_id != LV_MASK_ID_INV) lv_draw_mask_remove_id(mask_in_id); lv_draw_mask_free_param(&mask_out_param); - lv_draw_mask_free_param(&mask_in_param); + if(mask_in_param_valid) { + lv_draw_mask_free_param(&mask_in_param); + } return; } @@ -162,7 +166,9 @@ void lv_draw_sw_arc(lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, con lv_draw_mask_free_param(&mask_angle_param); lv_draw_mask_free_param(&mask_out_param); - lv_draw_mask_free_param(&mask_in_param); + if(mask_in_param_valid) { + lv_draw_mask_free_param(&mask_in_param); + } lv_draw_mask_remove_id(mask_angle_id); lv_draw_mask_remove_id(mask_out_id); @@ -237,7 +243,7 @@ static void draw_quarter_0(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } else if(q->start_quarter == 0 || q->end_quarter == 0) { @@ -252,7 +258,7 @@ static void draw_quarter_0(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } if(q->end_quarter == 0) { @@ -265,7 +271,7 @@ static void draw_quarter_0(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } } @@ -282,7 +288,7 @@ static void draw_quarter_0(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } q->draw_ctx->clip_area = clip_area_ori; @@ -304,7 +310,7 @@ static void draw_quarter_1(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } else if(q->start_quarter == 1 || q->end_quarter == 1) { @@ -319,7 +325,7 @@ static void draw_quarter_1(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } if(q->end_quarter == 1) { @@ -332,7 +338,7 @@ static void draw_quarter_1(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } } @@ -349,7 +355,7 @@ static void draw_quarter_1(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } q->draw_ctx->clip_area = clip_area_ori; @@ -371,7 +377,7 @@ static void draw_quarter_2(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } else if(q->start_quarter == 2 || q->end_quarter == 2) { @@ -386,7 +392,7 @@ static void draw_quarter_2(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } if(q->end_quarter == 2) { @@ -399,7 +405,7 @@ static void draw_quarter_2(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } } @@ -416,7 +422,7 @@ static void draw_quarter_2(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } q->draw_ctx->clip_area = clip_area_ori; @@ -438,7 +444,7 @@ static void draw_quarter_3(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } else if(q->start_quarter == 3 || q->end_quarter == 3) { @@ -453,7 +459,7 @@ static void draw_quarter_3(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } if(q->end_quarter == 3) { @@ -466,7 +472,7 @@ static void draw_quarter_3(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } } @@ -483,7 +489,7 @@ static void draw_quarter_3(quarter_draw_dsc_t * q) bool ok = _lv_area_intersect(&quarter_area, &quarter_area, clip_area_ori); if(ok) { q->draw_ctx->clip_area = &quarter_area; - lv_draw_rect(q->draw_ctx, q->draw_dsc, &quarter_area); + lv_draw_rect(q->draw_ctx, q->draw_dsc, q->draw_area); } } diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_blend.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_blend.c index 82d013888..428aba62c 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_blend.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_blend.c @@ -25,8 +25,16 @@ static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stide); + LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); + + +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); +#endif /*LV_COLOR_SCREEN_TRANSP*/ + #if LV_DRAW_COMPLEX static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); @@ -38,6 +46,13 @@ static void map_set_px(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_co LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride); +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode); + +#endif /*LV_COLOR_SCREEN_TRANSP*/ + #if LV_DRAW_COMPLEX static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, @@ -55,22 +70,12 @@ static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color /********************** * MACROS **********************/ -#if LV_COLOR_SCREEN_TRANSP == 0 #define FILL_NORMAL_MASK_PX(color) \ if(*mask == LV_OPA_COVER) *dest_buf = color; \ else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \ mask++; \ dest_buf++; -#else -#define FILL_NORMAL_MASK_PX(color) \ - if(*mask == LV_OPA_COVER) *dest_buf = color; \ - else if(disp->driver->screen_transp) lv_color_mix_with_alpha(*dest_buf, dest_buf->ch.alpha, color, *mask, dest_buf, &dest_buf->ch.alpha); \ - else *dest_buf = lv_color_mix(color, *dest_buf, *mask); \ - mask++; \ - dest_buf++; -#endif - #define MAP_NORMAL_MASK_PX(x) \ if(*mask_tmp_x) { \ if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \ @@ -78,15 +83,6 @@ static inline lv_color_t color_blend_true_color_multiply(lv_color_t fg, lv_color } \ mask_tmp_x++; -#define MAP_NORMAL_MASK_PX_SCR_TRANSP(x) \ - if(*mask_tmp_x) { \ - if(*mask_tmp_x == LV_OPA_COVER) dest_buf[x] = src_buf[x]; \ - else if(disp->driver->screen_transp) lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, \ - src_buf[x], *mask_tmp_x, &dest_buf[x], &dest_buf[x].ch.alpha); \ - else dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], *mask_tmp_x); \ - } \ - mask_tmp_x++; - /********************** * GLOBAL FUNCTIONS @@ -121,9 +117,19 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, cons lv_disp_t * disp = _lv_refr_get_disp_refreshing(); lv_color_t * dest_buf = draw_ctx->buf; if(disp->driver->set_px_cb == NULL) { - dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); + if(disp->driver->screen_transp == 0) { + dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); + } + else { + /*With LV_COLOR_DEPTH 16 it means ARGB8565 (3 bytes format)*/ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + dest_buf8 += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 += (blend_area.x1 - draw_ctx->buf_area->x1) * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf = (lv_color_t *)dest_buf8; + } } + const lv_color_t * src_buf = dsc->src_buf; lv_coord_t src_stride; if(src_buf) { @@ -137,7 +143,7 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, cons lv_coord_t mask_stride; if(mask) { mask_stride = lv_area_get_width(dsc->mask_area); - mask += mask_stride * (dsc->mask_area->y1 - blend_area.y1) + (dsc->mask_area->x1 - blend_area.x1); + mask += mask_stride * (blend_area.y1 - dsc->mask_area->y1) + (blend_area.x1 - dsc->mask_area->x1); } else { mask_stride = 0; @@ -154,21 +160,29 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_blend_basic(lv_draw_ctx_t * draw_ctx, cons map_set_px(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); } } - else if(dsc->src_buf == NULL) { - if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { +#if LV_COLOR_SCREEN_TRANSP + else if(disp->driver->screen_transp) { + if(dsc->src_buf == NULL) { + fill_argb(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); + } + else { + map_argb(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); + } + } +#endif + else if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { + if(dsc->src_buf == NULL) { fill_normal(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride); } -#if LV_DRAW_COMPLEX else { - fill_blended(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride, dsc->blend_mode); - } -#endif - } - else { - if(dsc->blend_mode == LV_BLEND_MODE_NORMAL) { map_normal(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride); } + } + else { #if LV_DRAW_COMPLEX + if(dsc->src_buf == NULL) { + fill_blended(dest_buf, &blend_area, dest_stride, dsc->color, dsc->opa, mask, mask_stride, dsc->blend_mode); + } else { map_blended(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa, mask, mask_stride, dsc->blend_mode); } @@ -203,6 +217,8 @@ static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_ for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x]) { + + disp->driver->set_px_cb(disp->driver, (void *)dest_buf, dest_stride, blend_area->x1 + x, blend_area->y1 + y, color, (uint32_t)((uint32_t)opa * mask[x]) >> 8); } @@ -215,7 +231,6 @@ static void fill_set_px(lv_color_t * dest_buf, const lv_area_t * blend_area, lv_ LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) { - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); int32_t w = lv_area_get_width(dest_area); int32_t h = lv_area_get_height(dest_area); @@ -235,6 +250,14 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_ar lv_color_t last_dest_color = lv_color_black(); lv_color_t last_res_color = lv_color_mix(color, last_dest_color, opa); +#if LV_COLOR_MIX_ROUND_OFS == 0 && LV_COLOR_DEPTH == 16 + /*lv_color_mix work with an optimized algorithm with 16 bit color depth. + *However, it introduces some rounded error on opa. + *Introduce the same error here too to make lv_color_premult produces the same result */ + opa = (uint32_t)((uint32_t)opa + 4) >> 3; + opa = opa << 3; +#endif + uint16_t color_premult[3]; lv_color_premult(color, opa, color_premult); lv_opa_t opa_inv = 255 - opa; @@ -243,19 +266,7 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_ar for(x = 0; x < w; x++) { if(last_dest_color.full != dest_buf[x].full) { last_dest_color = dest_buf[x]; - -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa, &last_res_color, - &last_res_color.ch.alpha); - } - else -#else - LV_UNUSED(disp); -#endif - { - last_res_color = lv_color_mix_premult(color_premult, dest_buf[x], opa_inv); - } + last_res_color = lv_color_mix_premult(color_premult, dest_buf[x], opa_inv); } dest_buf[x] = last_res_color; } @@ -335,17 +346,8 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_ar if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : (uint32_t)((uint32_t)(*mask) * opa) >> 8; if(*mask != last_mask || last_dest_color.full != dest_buf[x].full) { -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, color, opa_tmp, &last_res_color, - &last_res_color.ch.alpha); - } - else -#endif - { - if(opa_tmp == LV_OPA_COVER) last_res_color = color; - else last_res_color = lv_color_mix(color, dest_buf[x], opa_tmp); - } + if(opa_tmp == LV_OPA_COVER) last_res_color = color; + else last_res_color = lv_color_mix(color, dest_buf[x], opa_tmp); last_mask = *mask; last_dest_color.full = dest_buf[x].full; } @@ -360,6 +362,161 @@ LV_ATTRIBUTE_FAST_MEM static void fill_normal(lv_color_t * dest_buf, const lv_ar } } +#if LV_COLOR_SCREEN_TRANSP +static inline void set_px_argb(uint8_t * buf, lv_color_t color, lv_opa_t opa) +{ + lv_color_t bg_color; + lv_color_t res_color; + lv_opa_t bg_opa = buf[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 + bg_color.full = buf[0]; + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[1]); + if(buf[1] <= LV_OPA_MIN) return; + buf[0] = res_color.full; +#elif LV_COLOR_DEPTH == 16 + bg_color.full = buf[0] + (buf[1] << 8); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[2]); + if(buf[2] <= LV_OPA_MIN) return; + buf[0] = res_color.full & 0xff; + buf[1] = res_color.full >> 8; +#elif LV_COLOR_DEPTH == 32 + bg_color = *((lv_color_t *)buf); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf[3]); + if(buf[3] <= LV_OPA_MIN) return; + buf[0] = res_color.ch.blue; + buf[1] = res_color.ch.green; + buf[2] = res_color.ch.red; +#endif +} + +static inline void set_px_argb_blend(uint8_t * buf, lv_color_t color, lv_opa_t opa, lv_color_t (*blend_fp)(lv_color_t, + lv_color_t, lv_opa_t)) +{ + static lv_color_t last_dest_color; + static lv_color_t last_src_color; + static lv_color_t last_res_color; + static uint32_t last_opa = 0xffff; /*Set to an invalid value for first*/ + + lv_color_t bg_color; + + /*Get the BG color*/ +#if LV_COLOR_DEPTH == 8 + if(buf[1] <= LV_OPA_MIN) return; + bg_color.full = buf[0]; +#elif LV_COLOR_DEPTH == 16 + if(buf[2] <= LV_OPA_MIN) return; + bg_color.full = buf[0] + (buf[1] << 8); +#elif LV_COLOR_DEPTH == 32 + if(buf[3] <= LV_OPA_MIN) return; + bg_color = *((lv_color_t *)buf); +#endif + + /*Get the result color*/ + if(last_dest_color.full != bg_color.full || last_src_color.full != color.full || last_opa != opa) { + last_dest_color = bg_color; + last_src_color = color; + last_opa = opa; + last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); + } + + /*Set the result color*/ +#if LV_COLOR_DEPTH == 8 + buf[0] = res_color.full; +#elif LV_COLOR_DEPTH == 16 + buf[0] = last_res_color.full & 0xff; + buf[1] = last_res_color.full >> 8; +#elif LV_COLOR_DEPTH == 32 + buf[0] = last_res_color.ch.blue; + buf[1] = last_res_color.ch.green; + buf[2] = last_res_color.ch.red; +#endif + +} + +LV_ATTRIBUTE_FAST_MEM static void fill_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, + lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride) +{ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + uint8_t ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE]; + lv_memcpy(ctmp, &color, sizeof(lv_color_t)); + ctmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1] = opa; + + /*No mask*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + for(x = 0; x < w; x++) { + lv_memcpy(dest_buf8, ctmp, LV_IMG_PX_SIZE_ALPHA_BYTE); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + + dest_buf8 += (dest_stride - w) * LV_IMG_PX_SIZE_ALPHA_BYTE; + + for(y = 1; y < h; y++) { + lv_memcpy(dest_buf8, (uint8_t *) dest_buf, w * LV_IMG_PX_SIZE_ALPHA_BYTE); + dest_buf8 += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + /*Has opacity*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, color, opa); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + } + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa >= LV_OPA_MAX) { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, color, *mask); + mask++; + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + } + } + /*With opacity*/ + else { + /*Buffer the result color to avoid recalculating the same color*/ + lv_opa_t last_mask = LV_OPA_TRANSP; + lv_opa_t opa_tmp = LV_OPA_TRANSP; + + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + for(x = 0; x < w; x++) { + if(*mask) { + if(*mask != last_mask) opa_tmp = *mask == LV_OPA_COVER ? opa : + (uint32_t)((uint32_t)(*mask) * opa) >> 8; + + set_px_argb(dest_buf8, color, opa_tmp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + mask++; + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + mask += (mask_stride - w); + } + } + } +} +#endif + #if LV_DRAW_COMPLEX static void fill_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, lv_color_t color, lv_opa_t opa, const lv_opa_t * mask, lv_coord_t mask_stride, @@ -477,10 +634,6 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are int32_t x; int32_t y; -#if LV_COLOR_SCREEN_TRANSP - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); -#endif - /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { if(opa >= LV_OPA_MAX) { @@ -493,16 +646,7 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are else { for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa, &dest_buf[x], - &dest_buf[x].ch.alpha); - } - else -#endif - { - dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa); - } + dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa); } dest_buf += dest_stride; src_buf += src_stride; @@ -523,11 +667,7 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are } #else for(x = 0; x < w && ((lv_uintptr_t)mask_tmp_x & 0x3); x++) { -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) -#else MAP_NORMAL_MASK_PX(x) -#endif } uint32_t * mask32 = (uint32_t *)mask_tmp_x; @@ -541,17 +681,10 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are } else { mask_tmp_x = (const lv_opa_t *)mask32; -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 1) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 2) - MAP_NORMAL_MASK_PX_SCR_TRANSP(x + 3) -#else MAP_NORMAL_MASK_PX(x) MAP_NORMAL_MASK_PX(x + 1) MAP_NORMAL_MASK_PX(x + 2) MAP_NORMAL_MASK_PX(x + 3) -#endif } } mask32++; @@ -559,11 +692,7 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are mask_tmp_x = (const lv_opa_t *)mask32; for(; x < w ; x++) { -#if LV_COLOR_SCREEN_TRANSP - MAP_NORMAL_MASK_PX_SCR_TRANSP(x) -#else MAP_NORMAL_MASK_PX(x) -#endif } #endif dest_buf += dest_stride; @@ -577,16 +706,7 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are for(x = 0; x < w; x++) { if(mask[x]) { lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); -#if LV_COLOR_SCREEN_TRANSP - if(disp->driver->screen_transp) { - lv_color_mix_with_alpha(dest_buf[x], dest_buf[x].ch.alpha, src_buf[x], opa_tmp, - &dest_buf[x], &dest_buf[x].ch.alpha); - } - else -#endif - { - dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp); - } + dest_buf[x] = lv_color_mix(src_buf[x], dest_buf[x], opa_tmp); } } dest_buf += dest_stride; @@ -596,6 +716,150 @@ LV_ATTRIBUTE_FAST_MEM static void map_normal(lv_color_t * dest_buf, const lv_are } } } + + + +#if LV_COLOR_SCREEN_TRANSP +LV_ATTRIBUTE_FAST_MEM static void map_argb(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, + const lv_opa_t * mask, lv_coord_t mask_stride, lv_blend_mode_t blend_mode) + +{ + uint8_t * dest_buf8 = (uint8_t *) dest_buf; + + int32_t w = lv_area_get_width(dest_area); + int32_t h = lv_area_get_height(dest_area); + + int32_t x; + int32_t y; + + lv_color_t (*blend_fp)(lv_color_t, lv_color_t, lv_opa_t); + switch(blend_mode) { + case LV_BLEND_MODE_ADDITIVE: + blend_fp = color_blend_true_color_additive; + break; + case LV_BLEND_MODE_SUBTRACTIVE: + blend_fp = color_blend_true_color_subtractive; + break; + case LV_BLEND_MODE_MULTIPLY: + blend_fp = color_blend_true_color_multiply; + break; + default: + blend_fp = NULL; + } + + /*Simple fill (maybe with opacity), no masking*/ + if(mask == NULL) { + if(opa >= LV_OPA_MAX) { + if(blend_fp == NULL && LV_COLOR_DEPTH == 32) { + for(y = 0; y < h; y++) { + lv_memcpy(dest_buf, src_buf, w * sizeof(lv_color_t)); + dest_buf += dest_stride; + src_buf += src_stride; + } + } + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], LV_OPA_COVER); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], LV_OPA_COVER, blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + } + } + } + /*No mask but opacity*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], opa); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], opa, blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + } + } + } + /*Masked*/ + else { + /*Only the mask matters*/ + if(opa > LV_OPA_MAX) { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + set_px_argb(dest_buf8, src_buf[x], mask[x]); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + set_px_argb_blend(dest_buf8, src_buf[x], mask[x], blend_fp); + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + mask += mask_stride; + } + } + /*Handle opa and mask values too*/ + else { + uint8_t * dest_buf8_row = dest_buf8; + for(y = 0; y < h; y++) { + if(blend_fp == NULL) { + for(x = 0; x < w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + set_px_argb(dest_buf8, src_buf[x], opa_tmp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + else { + for(x = 0; x < w; x++) { + if(mask[x]) { + lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); + set_px_argb_blend(dest_buf8, src_buf[x], opa_tmp, blend_fp); + } + dest_buf8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + } + dest_buf8_row += dest_stride * LV_IMG_PX_SIZE_ALPHA_BYTE; + dest_buf8 = dest_buf8_row; + src_buf += src_stride; + mask += mask_stride; + } + } + } +} +#endif + + #if LV_DRAW_COMPLEX static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa, @@ -624,13 +888,21 @@ static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_c return; } + lv_color_t last_dest_color; + lv_color_t last_src_color; /*Simple fill (maybe with opacity), no masking*/ if(mask == NULL) { - /*The map will be indexed from `draw_area->x1` so compensate it.*/ - + last_dest_color = dest_buf[0]; + last_src_color = src_buf[0]; + lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, opa); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { - dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa); + if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full) { + last_dest_color = dest_buf[x]; + last_src_color = src_buf[x]; + last_res_color = blend_fp(last_src_color, last_dest_color, opa); + } + dest_buf[x] = last_res_color; } dest_buf += dest_stride; src_buf += src_stride; @@ -638,11 +910,21 @@ static void map_blended(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_c } /*Masked*/ else { + last_dest_color = dest_buf[0]; + last_src_color = src_buf[0]; + lv_opa_t last_opa = mask[0] >= LV_OPA_MAX ? opa : ((opa * mask[0]) >> 8); + lv_color_t last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); for(y = 0; y < h; y++) { for(x = 0; x < w; x++) { if(mask[x] == 0) continue; lv_opa_t opa_tmp = mask[x] >= LV_OPA_MAX ? opa : ((opa * mask[x]) >> 8); - dest_buf[x] = blend_fp(src_buf[x], dest_buf[x], opa_tmp); + if(last_src_color.full != src_buf[x].full || last_dest_color.full != dest_buf[x].full || last_opa != opa_tmp) { + last_dest_color = dest_buf[x]; + last_src_color = src_buf[x]; + last_opa = opa_tmp; + last_res_color = blend_fp(last_src_color, last_dest_color, last_opa); + } + dest_buf[x] = last_res_color; } dest_buf += dest_stride; src_buf += src_stride; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_gradient.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_gradient.c index c5b32073e..4e4663266 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_gradient.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_gradient.c @@ -21,23 +21,16 @@ #define GRAD_CONV(t, x) t = x #endif +#undef ALIGN #if defined(LV_ARCH_64) #define ALIGN(X) (((X) + 7) & ~7) #else #define ALIGN(X) (((X) + 3) & ~3) #endif -#define MAX_WIN_RES 1024 /**TODO: Find a way to get this information: max(horz_res, vert_res)*/ - -#if _DITHER_GRADIENT - #if LV_DITHER_ERROR_DIFFUSION == 1 - #define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t) + MAX_WIN_RES * sizeof(lv_color_t) + MAX_WIN_RES * sizeof(lv_scolor24_t) - #else - #define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t) + MAX_WIN_RES * sizeof(lv_color_t) - #endif /* LV_DITHER_ERROR_DIFFUSION */ -#else - #define LV_DEFAULT_GRAD_CACHE_SIZE sizeof(lv_gradient_cache_t) + MAX_WIN_RES * sizeof(lv_grad_color_t) -#endif /* _DITHER_GRADIENT */ +#if LV_GRAD_CACHE_DEF_SIZE != 0 && LV_GRAD_CACHE_DEF_SIZE < 256 + #error "LV_GRAD_CACHE_DEF_SIZE is too small" +#endif /********************** * STATIC PROTOTYPES @@ -94,8 +87,6 @@ static lv_grad_t * next_in_cache(lv_grad_t * item) if(item == NULL) return (lv_grad_t *)LV_GC_ROOT(_lv_grad_cache_mem); - if(item == NULL) - return NULL; size_t s = get_cache_item_size(item); /*Compute the size for this cache item*/ @@ -334,6 +325,8 @@ LV_ATTRIBUTE_FAST_MEM lv_grad_color_t lv_gradient_calculate(const lv_grad_dsc_t } } + LV_ASSERT(d != 0); + /*Then interpolate*/ frac -= min; lv_opa_t mix = (frac * 255) / d; diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_img.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_img.c index 76c308419..9578bc7cc 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_img.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_img.c @@ -17,6 +17,7 @@ /********************* * DEFINES *********************/ +#define MAX_BUF_SIZE (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()) /********************** * TYPEDEFS @@ -25,6 +26,8 @@ /********************** * STATIC PROTOTYPES **********************/ +static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf); /********************** * STATIC VARIABLES @@ -38,6 +41,7 @@ * GLOBAL FUNCTIONS **********************/ + LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc, const lv_area_t * coords, const uint8_t * src_buf, lv_img_cf_t cf) { @@ -46,276 +50,248 @@ LV_ATTRIBUTE_FAST_MEM void lv_draw_sw_img_decoded(struct _lv_draw_ctx_t * draw_c lv_area_copy(&draw_area, draw_ctx->clip_area); bool mask_any = lv_draw_mask_is_any(&draw_area); + bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; + lv_area_t blend_area; lv_draw_sw_blend_dsc_t blend_dsc; - lv_memset_00(&blend_dsc, sizeof(blend_dsc)); + + lv_memset_00(&blend_dsc, sizeof(lv_draw_sw_blend_dsc_t)); blend_dsc.opa = draw_dsc->opa; blend_dsc.blend_mode = draw_dsc->blend_mode; + blend_dsc.blend_area = &blend_area; /*The simplest case just copy the pixels into the draw_buf*/ - if(!mask_any && draw_dsc->angle == 0 && draw_dsc->zoom == LV_IMG_ZOOM_NONE && - cf == LV_IMG_CF_TRUE_COLOR && draw_dsc->recolor_opa == LV_OPA_TRANSP) { - blend_dsc.blend_area = coords; + if(!mask_any && !transform && cf == LV_IMG_CF_TRUE_COLOR && draw_dsc->recolor_opa == LV_OPA_TRANSP) { blend_dsc.src_buf = (const lv_color_t *)src_buf; + + blend_dsc.blend_area = coords; lv_draw_sw_blend(draw_ctx, &blend_dsc); } + else if(!mask_any && !transform && cf == LV_IMG_CF_ALPHA_8BIT) { + lv_area_t clipped_coords; + if(!_lv_area_intersect(&clipped_coords, coords, draw_ctx->clip_area)) return; + + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_area = coords; + blend_dsc.src_buf = NULL; + blend_dsc.color = draw_dsc->recolor; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + + blend_dsc.blend_area = coords; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#if LV_COLOR_DEPTH == 16 + else if(!mask_any && !transform && cf == LV_IMG_CF_RGB565A8 && draw_dsc->recolor_opa == LV_OPA_TRANSP) { + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + blend_dsc.src_buf = (const lv_color_t *)src_buf; + blend_dsc.mask_buf = (lv_opa_t *)src_buf; + blend_dsc.mask_buf += sizeof(lv_color_t) * src_w * src_h; + blend_dsc.blend_area = coords; + blend_dsc.mask_area = coords; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + lv_draw_sw_blend(draw_ctx, &blend_dsc); + } +#endif /*In the other cases every pixel need to be checked one-by-one*/ else { - //#if LV_DRAW_COMPLEX - /*The pixel size in byte is different if an alpha byte is added too*/ - uint8_t px_size_byte = cf == LV_IMG_CF_TRUE_COLOR_ALPHA ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); + blend_area.x1 = draw_ctx->clip_area->x1; + blend_area.x2 = draw_ctx->clip_area->x2; + blend_area.y1 = draw_ctx->clip_area->y1; + blend_area.y2 = draw_ctx->clip_area->y2; - /*Go to the first displayed pixel of the map*/ - int32_t src_stride = lv_area_get_width(coords); + lv_coord_t src_w = lv_area_get_width(coords); + lv_coord_t src_h = lv_area_get_height(coords); + lv_coord_t blend_h = lv_area_get_height(&blend_area); + lv_coord_t blend_w = lv_area_get_width(&blend_area); - lv_color_t c; - lv_color_t chroma_keyed_color = LV_COLOR_CHROMA_KEY; - uint32_t px_i = 0; - - const uint8_t * map_px; - - lv_coord_t draw_area_h = lv_area_get_height(&draw_area); - lv_coord_t draw_area_w = lv_area_get_width(&draw_area); - - lv_area_t blend_area; - blend_area.x1 = draw_area.x1; - blend_area.x2 = draw_area.x2; - blend_area.y1 = draw_area.y1; - blend_area.y2 = blend_area.y1; - blend_dsc.blend_area = &blend_area; - - bool transform = draw_dsc->angle != 0 || draw_dsc->zoom != LV_IMG_ZOOM_NONE ? true : false; - /*Simple ARGB image. Handle it as special case because it's very common*/ - if(!mask_any && !transform && cf == LV_IMG_CF_TRUE_COLOR_ALPHA && draw_dsc->recolor_opa == LV_OPA_TRANSP) { - uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); - uint32_t mask_buf_size = lv_area_get_size(&draw_area) > (uint32_t) hor_res ? hor_res : lv_area_get_size(&draw_area); - lv_color_t * src_buf_rgb = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - blend_dsc.mask_buf = mask_buf; - blend_dsc.mask_area = &blend_area; - blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; - blend_dsc.src_buf = src_buf_rgb; - - const uint8_t * src_buf_tmp = src_buf; - src_buf_tmp += src_stride * (draw_area.y1 - coords->y1) * px_size_byte; - src_buf_tmp += (draw_area.x1 - coords->x1) * px_size_byte; - - int32_t x; - int32_t y; - for(y = 0; y < draw_area_h; y++) { - map_px = src_buf_tmp; - for(x = 0; x < draw_area_w; x++, map_px += px_size_byte, px_i++) { - lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; - mask_buf[px_i] = px_opa; - if(px_opa) { -#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 - src_buf_rgb[px_i].full = map_px[0]; -#elif LV_COLOR_DEPTH == 16 - src_buf_rgb[px_i].full = map_px[0] + (map_px[1] << 8); -#elif LV_COLOR_DEPTH == 32 - src_buf_rgb[px_i].full = *((uint32_t *)map_px); -#endif - } -#if LV_COLOR_DEPTH == 32 - src_buf_rgb[px_i].ch.alpha = 0xFF; -#endif - } - - src_buf_tmp += src_stride * px_size_byte; - if(px_i + draw_area_w <= mask_buf_size) { - blend_area.y2 ++; - } - else { - lv_draw_sw_blend(draw_ctx, &blend_dsc); - - blend_area.y1 = blend_area.y2 + 1; - blend_area.y2 = blend_area.y1; - - px_i = 0; - } - } - /*Flush the last part*/ - if(blend_area.y1 != blend_area.y2) { - blend_area.y2--; - lv_draw_sw_blend(draw_ctx, &blend_dsc); - } - - lv_mem_buf_release(mask_buf); - lv_mem_buf_release(src_buf_rgb); + uint32_t max_buf_size = MAX_BUF_SIZE; + uint32_t blend_size = lv_area_get_size(&blend_area); + uint32_t buf_h; + uint32_t buf_w = blend_w; + if(blend_size <= max_buf_size) { + buf_h = blend_h; } - /*Most complicated case: transform or other mask or chroma keyed*/ else { - /*Build the image and a mask line-by-line*/ - uint32_t hor_res = (uint32_t) lv_disp_get_hor_res(_lv_refr_get_disp_refreshing()); - uint32_t mask_buf_size = lv_area_get_size(&draw_area) > hor_res ? hor_res : lv_area_get_size(&draw_area); - lv_color_t * src_buf_rgb = lv_mem_buf_get(mask_buf_size * sizeof(lv_color_t)); - lv_opa_t * mask_buf = lv_mem_buf_get(mask_buf_size); - blend_dsc.mask_buf = mask_buf; - blend_dsc.mask_area = &blend_area; - blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; - blend_dsc.src_buf = src_buf_rgb; + /*Round to full lines*/ + buf_h = max_buf_size / blend_w; + } - const uint8_t * src_buf_tmp = NULL; -#if LV_DRAW_COMPLEX - lv_img_transform_dsc_t trans_dsc; - lv_memset_00(&trans_dsc, sizeof(lv_img_transform_dsc_t)); + /*Create buffers and masks*/ + uint32_t buf_size = buf_w * buf_h; + + lv_color_t * rgb_buf = lv_mem_buf_get(buf_size * sizeof(lv_color_t)); + lv_opa_t * mask_buf = lv_mem_buf_get(buf_size); + blend_dsc.mask_buf = mask_buf; + blend_dsc.mask_area = &blend_area; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; + blend_dsc.src_buf = rgb_buf; + lv_coord_t y_last = blend_area.y2; + blend_area.y2 = blend_area.y1 + buf_h - 1; + + lv_draw_mask_res_t mask_res_def = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle || + draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? + LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; + blend_dsc.mask_res = mask_res_def; + + while(blend_area.y1 <= y_last) { + /*Apply transformations if any or separate the channels*/ + lv_area_t transform_area; + lv_area_copy(&transform_area, &blend_area); + lv_area_move(&transform_area, -coords->x1, -coords->y1); if(transform) { - trans_dsc.cfg.angle = draw_dsc->angle; - trans_dsc.cfg.zoom = draw_dsc->zoom; - trans_dsc.cfg.src = src_buf; - trans_dsc.cfg.src_w = src_stride; - trans_dsc.cfg.src_h = lv_area_get_height(coords); - trans_dsc.cfg.cf = cf; - trans_dsc.cfg.pivot_x = draw_dsc->pivot.x; - trans_dsc.cfg.pivot_y = draw_dsc->pivot.y; - trans_dsc.cfg.color = draw_dsc->recolor; - trans_dsc.cfg.antialias = draw_dsc->antialias; - - _lv_img_buf_transform_init(&trans_dsc); + lv_draw_transform(draw_ctx, &transform_area, src_buf, src_w, src_h, src_w, + draw_dsc, cf, rgb_buf, mask_buf); } else { - src_buf_tmp = src_buf; - src_buf_tmp += src_stride * (draw_area.y1 - coords->y1) * px_size_byte; - src_buf_tmp += (draw_area.x1 - coords->x1) * px_size_byte; - } -#endif - uint16_t recolor_premult[3] = {0}; - lv_opa_t recolor_opa_inv = 255 - draw_dsc->recolor_opa; - if(draw_dsc->recolor_opa != 0) { - lv_color_premult(draw_dsc->recolor, draw_dsc->recolor_opa, recolor_premult); + convert_cb(&transform_area, src_buf, src_w, src_h, src_w, draw_dsc, cf, rgb_buf, mask_buf); } - blend_dsc.mask_res = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle || - draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; - - /*Prepare the `mask_buf`if there are other masks*/ + /*Apply recolor*/ + if(draw_dsc->recolor_opa > LV_OPA_MIN) { + uint16_t premult_v[3]; + lv_opa_t recolor_opa = draw_dsc->recolor_opa; + lv_color_t recolor = draw_dsc->recolor; + lv_color_premult(recolor, recolor_opa, premult_v); + recolor_opa = 255 - recolor_opa; + uint32_t i; + for(i = 0; i < buf_size; i++) { + rgb_buf[i] = lv_color_mix_premult(premult_v, rgb_buf[i], recolor_opa); + } + } +#if LV_DRAW_COMPLEX + /*Apply the masks if any*/ if(mask_any) { - lv_memset_ff(mask_buf, mask_buf_size); - } + lv_coord_t y; + lv_opa_t * mask_buf_tmp = mask_buf; + for(y = blend_area.y1; y <= blend_area.y2; y++) { + lv_draw_mask_res_t mask_res_line; + mask_res_line = lv_draw_mask_apply(mask_buf_tmp, blend_area.x1, y, blend_w); - int32_t x; - int32_t y; -#if LV_DRAW_COMPLEX - int32_t rot_y = blend_area.y1 - coords->y1; -#endif - for(y = 0; y < draw_area_h; y++) { - map_px = src_buf_tmp; -#if LV_DRAW_COMPLEX - uint32_t px_i_start = px_i; - int32_t rot_x = blend_area.x1 - coords->x1; -#endif - - for(x = 0; x < draw_area_w; x++, px_i++, map_px += px_size_byte) { - -#if LV_DRAW_COMPLEX - if(transform) { - - /*Transform*/ - bool ret; - ret = _lv_img_buf_transform(&trans_dsc, rot_x + x, rot_y + y); - if(ret == false) { - mask_buf[px_i] = LV_OPA_TRANSP; - continue; - } - else { - mask_buf[px_i] = trans_dsc.res.opa; - c.full = trans_dsc.res.color.full; - } - } - /*No transform*/ - else -#endif - { - if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { - lv_opa_t px_opa = map_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; - mask_buf[px_i] = px_opa; - if(px_opa == 0) { -#if LV_COLOR_DEPTH == 32 - src_buf_rgb[px_i].full = 0; -#endif - continue; - } - } - else { - mask_buf[px_i] = 0xFF; - } - -#if LV_COLOR_DEPTH == 1 - c.full = map_px[0]; -#elif LV_COLOR_DEPTH == 8 - c.full = map_px[0]; -#elif LV_COLOR_DEPTH == 16 - c.full = map_px[0] + (map_px[1] << 8); -#elif LV_COLOR_DEPTH == 32 - c.full = *((uint32_t *)map_px); - c.ch.alpha = 0xFF; -#endif - if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { - if(c.full == chroma_keyed_color.full) { - mask_buf[px_i] = LV_OPA_TRANSP; -#if LV_COLOR_DEPTH == 32 - src_buf_rgb[px_i].full = 0; -#endif - continue; - } - } - - } - if(draw_dsc->recolor_opa != 0) { - c = lv_color_mix_premult(recolor_premult, c, recolor_opa_inv); - } - - src_buf_rgb[px_i].full = c.full; - } -#if LV_DRAW_COMPLEX - /*Apply the masks if any*/ - if(mask_any) { - lv_draw_mask_res_t mask_res_sub; - mask_res_sub = lv_draw_mask_apply(mask_buf + px_i_start, blend_area.x1, - y + draw_area.y1, draw_area_w); - if(mask_res_sub == LV_DRAW_MASK_RES_TRANSP) { - lv_memset_00(mask_buf + px_i_start, draw_area_w); + if(mask_res_line == LV_DRAW_MASK_RES_TRANSP) { + lv_memset_00(mask_buf_tmp, blend_w); blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; } - else if(mask_res_sub == LV_DRAW_MASK_RES_CHANGED) { + else if(mask_res_line == LV_DRAW_MASK_RES_CHANGED) { blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; } + mask_buf_tmp += blend_w; } + } #endif - src_buf_tmp += src_stride * px_size_byte; - if(px_i + draw_area_w < mask_buf_size) { - blend_area.y2 ++; - } - else { - lv_draw_sw_blend(draw_ctx, &blend_dsc); + /*Blend*/ + lv_draw_sw_blend(draw_ctx, &blend_dsc); - blend_area.y1 = blend_area.y2 + 1; - blend_area.y2 = blend_area.y1; - - px_i = 0; - blend_dsc.mask_res = (cf != LV_IMG_CF_TRUE_COLOR || draw_dsc->angle || - draw_dsc->zoom != LV_IMG_ZOOM_NONE) ? LV_DRAW_MASK_RES_CHANGED : LV_DRAW_MASK_RES_FULL_COVER; - - /*Prepare the `mask_buf`if there are other masks*/ - if(mask_any) { - lv_memset_ff(mask_buf, mask_buf_size); - } - } - } - - /*Flush the last part*/ - if(blend_area.y1 != blend_area.y2) { - blend_area.y2--; - lv_draw_sw_blend(draw_ctx, &blend_dsc); - } - - lv_mem_buf_release(mask_buf); - lv_mem_buf_release(src_buf_rgb); + /*Go the the next lines*/ + blend_area.y1 = blend_area.y2 + 1; + blend_area.y2 = blend_area.y1 + buf_h - 1; + if(blend_area.y2 > y_last) blend_area.y2 = y_last; } + + lv_mem_buf_release(mask_buf); + lv_mem_buf_release(rgb_buf); } } /********************** * STATIC FUNCTIONS **********************/ + +/* Separate the image channels to RGB and Alpha to match LV_COLOR_DEPTH settings*/ +static void convert_cb(const lv_area_t * dest_area, const void * src_buf, lv_coord_t src_w, lv_coord_t src_h, + lv_coord_t src_stride, const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_dsc); + LV_UNUSED(src_h); + LV_UNUSED(src_w); + + const uint8_t * src_tmp8 = (const uint8_t *)src_buf; + lv_coord_t y; + lv_coord_t x; + + if(cf == LV_IMG_CF_TRUE_COLOR || cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t px_cnt = lv_area_get_size(dest_area); + lv_memset_ff(abuf, px_cnt); + + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + uint32_t dest_w = lv_area_get_width(dest_area); + uint32_t dest_w_byte = dest_w * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + lv_color_t * cbuf_tmp = cbuf; + for(y = dest_area->y1; y <= dest_area->y2; y++) { + lv_memcpy(cbuf_tmp, src_tmp8, dest_w_byte); + src_tmp8 += src_stride_byte; + cbuf_tmp += dest_w; + } + + /*Make "holes" for with Chroma keying*/ + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + uint32_t i; + lv_color_t chk = LV_COLOR_CHROMA_KEY; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + uint8_t * cbuf_uint = (uint8_t *)cbuf; + uint8_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 16 + uint16_t * cbuf_uint = (uint16_t *)cbuf; + uint16_t chk_v = chk.full; +#elif LV_COLOR_DEPTH == 32 + uint32_t * cbuf_uint = (uint32_t *)cbuf; + uint32_t chk_v = chk.full; +#endif + for(i = 0; i < px_cnt; i++) { + if(chk_v == cbuf_uint[i]) abuf[i] = 0x00; + } + } + } + else if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + src_tmp8 += (src_stride * dest_area->y1 * LV_IMG_PX_SIZE_ALPHA_BYTE) + dest_area->x1 * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t src_new_line_step_px = (src_stride - lv_area_get_width(dest_area)); + lv_coord_t src_new_line_step_byte = src_new_line_step_px * LV_IMG_PX_SIZE_ALPHA_BYTE; + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + for(x = 0; x < dest_w; x++) { + abuf[x] = src_tmp8[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + cbuf[x].full = *src_tmp8; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = *src_tmp8 + ((*(src_tmp8 + 1)) << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x] = *((lv_color_t *) src_tmp8); + cbuf[x].ch.alpha = 0xff; +#endif + src_tmp8 += LV_IMG_PX_SIZE_ALPHA_BYTE; + + } + cbuf += dest_w; + abuf += dest_w; + src_tmp8 += src_new_line_step_byte; + } + } + else if(cf == LV_IMG_CF_RGB565A8) { + src_tmp8 += (src_stride * dest_area->y1 * sizeof(lv_color_t)) + dest_area->x1 * sizeof(lv_color_t); + + lv_coord_t src_stride_byte = src_stride * sizeof(lv_color_t); + + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t dest_w = lv_area_get_width(dest_area); + for(y = 0; y < dest_h; y++) { + lv_memcpy(cbuf, src_tmp8, dest_w * sizeof(lv_color_t)); + cbuf += dest_w; + src_tmp8 += src_stride_byte; + } + + src_tmp8 = (const uint8_t *)src_buf; + src_tmp8 += sizeof(lv_color_t) * src_w * src_h; + src_tmp8 += src_stride * dest_area->y1 + dest_area->x1; + for(y = 0; y < dest_h; y++) { + lv_memcpy(abuf, src_tmp8, dest_w); + abuf += dest_w; + src_tmp8 += src_stride; + } + } +} diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_layer.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_layer.c new file mode 100644 index 000000000..644012fee --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_layer.c @@ -0,0 +1,150 @@ +/** + * @file lv_draw_sw_layer.h + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../hal/lv_hal_disp.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + + +struct _lv_draw_layer_ctx_t * lv_draw_sw_layer_create(struct _lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + if(LV_COLOR_SCREEN_TRANSP == 0 && (flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA)) { + LV_LOG_WARN("Rendering this widget needs LV_COLOR_SCREEN_TRANSP 1"); + return NULL; + } + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + uint32_t px_size = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? LV_IMG_PX_SIZE_ALPHA_BYTE : sizeof(lv_color_t); + if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) { + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_BUF_SIZE; + uint32_t full_size = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + if(layer_sw_ctx->buf_size_bytes > full_size) layer_sw_ctx->buf_size_bytes = full_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + LV_LOG_WARN("Cannot allocate %"LV_PRIu32" bytes for layer buffer. Allocating %"LV_PRIu32" bytes instead. (Reduced performance)", + (uint32_t)layer_sw_ctx->buf_size_bytes, (uint32_t)LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE * px_size); + layer_sw_ctx->buf_size_bytes = LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + if(layer_sw_ctx->base_draw.buf == NULL) { + return NULL; + } + } + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->base_draw.area_act.y2 = layer_sw_ctx->base_draw.area_full.y1; + lv_coord_t w = lv_area_get_width(&layer_sw_ctx->base_draw.area_act); + layer_sw_ctx->base_draw.max_row_with_alpha = layer_sw_ctx->buf_size_bytes / w / LV_IMG_PX_SIZE_ALPHA_BYTE; + layer_sw_ctx->base_draw.max_row_with_no_alpha = layer_sw_ctx->buf_size_bytes / w / sizeof(lv_color_t); + } + else { + layer_sw_ctx->base_draw.area_act = layer_sw_ctx->base_draw.area_full; + layer_sw_ctx->buf_size_bytes = lv_area_get_size(&layer_sw_ctx->base_draw.area_full) * px_size; + layer_sw_ctx->base_draw.buf = lv_mem_alloc(layer_sw_ctx->buf_size_bytes); + lv_memset_00(layer_sw_ctx->base_draw.buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + if(layer_sw_ctx->base_draw.buf == NULL) { + return NULL; + } + + draw_ctx->buf = layer_sw_ctx->base_draw.buf; + draw_ctx->buf_area = &layer_sw_ctx->base_draw.area_act; + draw_ctx->clip_area = &layer_sw_ctx->base_draw.area_act; + + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA ? 1 : 0; + } + + return layer_ctx; +} + +void lv_draw_sw_layer_adjust(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + lv_draw_layer_flags_t flags) +{ + + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) { + lv_memset_00(layer_ctx->buf, layer_sw_ctx->buf_size_bytes); + layer_sw_ctx->has_alpha = 1; + disp_refr->driver->screen_transp = 1; + } + else { + layer_sw_ctx->has_alpha = 0; + disp_refr->driver->screen_transp = 0; + } + + draw_ctx->buf = layer_ctx->buf; + draw_ctx->buf_area = &layer_ctx->area_act; + draw_ctx->clip_area = &layer_ctx->area_act; +} + +void lv_draw_sw_layer_blend(struct _lv_draw_ctx_t * draw_ctx, struct _lv_draw_layer_ctx_t * layer_ctx, + const lv_draw_img_dsc_t * draw_dsc) +{ + lv_draw_sw_layer_ctx_t * layer_sw_ctx = (lv_draw_sw_layer_ctx_t *) layer_ctx; + + lv_img_dsc_t img; + img.data = draw_ctx->buf; + img.header.always_zero = 0; + img.header.w = lv_area_get_width(draw_ctx->buf_area); + img.header.h = lv_area_get_height(draw_ctx->buf_area); + img.header.cf = layer_sw_ctx->has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; + lv_img_cache_invalidate_src(&img); + + /*Restore the original draw_ctx*/ + draw_ctx->buf = layer_ctx->original.buf; + draw_ctx->buf_area = layer_ctx->original.buf_area; + draw_ctx->clip_area = layer_ctx->original.clip_area; + lv_disp_t * disp_refr = _lv_refr_get_disp_refreshing(); + disp_refr->driver->screen_transp = layer_ctx->original.screen_transp; + + /*Blend the layer*/ + lv_draw_img(draw_ctx, draw_dsc, &layer_ctx->area_act, &img); + lv_draw_wait_for_finish(draw_ctx); +} + +void lv_draw_sw_layer_destroy(lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx) +{ + LV_UNUSED(draw_ctx); + + lv_mem_free(layer_ctx->buf); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_letter.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_letter.c index e7578cffb..9522888c9 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_letter.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_letter.c @@ -97,13 +97,30 @@ void lv_draw_sw_letter(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc { lv_font_glyph_dsc_t g; bool g_ret = lv_font_get_glyph_dsc(dsc->font, &g, letter, '\0'); - if(g_ret == false) { + if(g_ret == false) { /*Add warning if the dsc is not found *but do not print warning for non printable ASCII chars (e.g. '\n')*/ if(letter >= 0x20 && letter != 0xf8ff && /*LV_SYMBOL_DUMMY*/ letter != 0x200c) { /*ZERO WIDTH NON-JOINER*/ - LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%X", (unsigned int)letter); + LV_LOG_WARN("lv_draw_letter: glyph dsc. not found for U+%" PRIX32, letter); + +#if LV_USE_FONT_PLACEHOLDER + /* draw placeholder */ + lv_area_t glyph_coords; + lv_draw_rect_dsc_t glyph_dsc; + lv_coord_t begin_x = pos_p->x + g.ofs_x; + lv_coord_t begin_y = pos_p->y + g.ofs_y; + lv_area_set(&glyph_coords, begin_x, begin_y, begin_x + g.box_w, begin_y + g.box_h); + lv_draw_rect_dsc_init(&glyph_dsc); + glyph_dsc.bg_opa = LV_OPA_MIN; + glyph_dsc.outline_opa = LV_OPA_MIN; + glyph_dsc.shadow_opa = LV_OPA_MIN; + glyph_dsc.bg_img_opa = LV_OPA_MIN; + glyph_dsc.border_color = dsc->color; + glyph_dsc.border_width = 1; + draw_ctx->draw_rect(draw_ctx, &glyph_dsc, &glyph_coords); +#endif } return; } @@ -157,6 +174,24 @@ LV_ATTRIBUTE_FAST_MEM static void draw_letter_normal(lv_draw_ctx_t * draw_ctx, c uint32_t shades; if(bpp == 3) bpp = 4; +#if LV_USE_IMGFONT + if(bpp == LV_IMGFONT_BPP) { //is imgfont + lv_area_t fill_area; + fill_area.x1 = pos->x; + fill_area.y1 = pos->y; + fill_area.x2 = pos->x + g->box_w - 1; + fill_area.y2 = pos->y + g->box_h - 1; + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.angle = 0; + img_dsc.zoom = LV_IMG_ZOOM_NONE; + img_dsc.opa = dsc->opa; + img_dsc.blend_mode = dsc->blend_mode; + lv_draw_img(draw_ctx, &img_dsc, &fill_area, map_p); + return; + } +#endif + switch(bpp) { case 1: bpp_opa_table_p = _lv_bpp1_opa_table; @@ -408,7 +443,7 @@ static void draw_letter_subpx(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_ #endif lv_draw_sw_blend_dsc_t blend_dsc; - lv_memset_00(&blend_dsc, sizeof(&blend_dsc)); + lv_memset_00(&blend_dsc, sizeof(blend_dsc)); blend_dsc.blend_area = &map_area; blend_dsc.mask_area = &map_area; blend_dsc.src_buf = color_buf; @@ -507,6 +542,7 @@ static void draw_letter_subpx(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_ map_area.y2 ++; } else { + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; lv_draw_sw_blend(draw_ctx, &blend_dsc); map_area.y1 = map_area.y2 + 1; @@ -526,6 +562,7 @@ static void draw_letter_subpx(lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_ /*Flush the last part*/ if(map_area.y1 != map_area.y2) { map_area.y2--; + blend_dsc.mask_res = LV_DRAW_MASK_RES_CHANGED; lv_draw_sw_blend(draw_ctx, &blend_dsc); } diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_rect.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_rect.c index d94431b0c..2b2ea9820 100644 --- a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_rect.c +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_rect.c @@ -128,7 +128,6 @@ static void draw_bg(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, co if(!mask_any && dsc->radius == 0 && (grad_dir == LV_GRAD_DIR_NONE)) { blend_dsc.blend_area = &bg_coords; blend_dsc.opa = dsc->bg_opa; - lv_draw_sw_blend(draw_ctx, &blend_dsc); return; } @@ -333,6 +332,14 @@ static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc if(dsc->bg_img_src == NULL) return; if(dsc->bg_img_opa <= LV_OPA_MIN) return; + lv_area_t clip_area; + if(!_lv_area_intersect(&clip_area, coords, draw_ctx->clip_area)) { + return; + } + + const lv_area_t * clip_area_ori = draw_ctx->clip_area; + draw_ctx->clip_area = &clip_area; + lv_img_src_t src_type = lv_img_src_get_type(dsc->bg_img_src); if(src_type == LV_IMG_SRC_SYMBOL) { lv_point_t size; @@ -353,43 +360,45 @@ static void draw_bg_img(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc else { lv_img_header_t header; lv_res_t res = lv_img_decoder_get_info(dsc->bg_img_src, &header); - if(res != LV_RES_OK) { - LV_LOG_WARN("Couldn't read the background image"); - return; - } + if(res == LV_RES_OK) { + lv_draw_img_dsc_t img_dsc; + lv_draw_img_dsc_init(&img_dsc); + img_dsc.blend_mode = dsc->blend_mode; + img_dsc.recolor = dsc->bg_img_recolor; + img_dsc.recolor_opa = dsc->bg_img_recolor_opa; + img_dsc.opa = dsc->bg_img_opa; - lv_draw_img_dsc_t img_dsc; - lv_draw_img_dsc_init(&img_dsc); - img_dsc.blend_mode = dsc->blend_mode; - img_dsc.recolor = dsc->bg_img_recolor; - img_dsc.recolor_opa = dsc->bg_img_recolor_opa; - img_dsc.opa = dsc->bg_img_opa; - - /*Center align*/ - if(dsc->bg_img_tiled == false) { - lv_area_t area; - area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; - area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; - area.x2 = area.x1 + header.w - 1; - area.y2 = area.y1 + header.h - 1; - - lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); - } - else { - lv_area_t area; - area.y1 = coords->y1; - area.y2 = area.y1 + header.h - 1; - - for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { - - area.x1 = coords->x1; + /*Center align*/ + if(dsc->bg_img_tiled == false) { + lv_area_t area; + area.x1 = coords->x1 + lv_area_get_width(coords) / 2 - header.w / 2; + area.y1 = coords->y1 + lv_area_get_height(coords) / 2 - header.h / 2; area.x2 = area.x1 + header.w - 1; - for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { - lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + area.y2 = area.y1 + header.h - 1; + + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } + else { + lv_area_t area; + area.y1 = coords->y1; + area.y2 = area.y1 + header.h - 1; + + for(; area.y1 <= coords->y2; area.y1 += header.h, area.y2 += header.h) { + + area.x1 = coords->x1; + area.x2 = area.x1 + header.w - 1; + for(; area.x1 <= coords->x2; area.x1 += header.w, area.x2 += header.w) { + lv_draw_img(draw_ctx, &img_dsc, &area, dsc->bg_img_src); + } } } } + else { + LV_LOG_WARN("Couldn't read the background image"); + } } + + draw_ctx->clip_area = clip_area_ori; } static void draw_border(lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords) diff --git a/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_transform.c b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_transform.c new file mode 100644 index 000000000..80b1e6dea --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/sw/lv_draw_sw_transform.c @@ -0,0 +1,496 @@ +/** + * @file lv_draw_sw_tranform.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_draw_sw.h" +#include "../../misc/lv_assert.h" +#include "../../misc/lv_area.h" +#include "../../core/lv_refr.h" + +#if LV_DRAW_COMPLEX +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + int32_t x_in; + int32_t y_in; + int32_t x_out; + int32_t y_out; + int32_t sinma; + int32_t cosma; + int32_t zoom; + int32_t angle; + int32_t pivot_x_256; + int32_t pivot_y_256; + lv_point_t pivot; +} point_transform_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +/** + * Transform a point with 1/256 precision (the output coordinates are upscaled by 256) + * @param t pointer to n initialized `point_transform_dsc_t` structure + * @param xin X coordinate to rotate + * @param yin Y coordinate to rotate + * @param xout upscaled, transformed X + * @param yout upscaled, transformed Y + */ +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout); + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf); +#endif + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_draw_sw_transform(lv_draw_ctx_t * draw_ctx, const lv_area_t * dest_area, const void * src_buf, + lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + const lv_draw_img_dsc_t * draw_dsc, lv_img_cf_t cf, lv_color_t * cbuf, lv_opa_t * abuf) +{ + LV_UNUSED(draw_ctx); + + point_transform_dsc_t tr_dsc; + tr_dsc.angle = -draw_dsc->angle; + tr_dsc.zoom = (256 * 256) / draw_dsc->zoom; + tr_dsc.pivot = draw_dsc->pivot; + + int32_t angle_low = tr_dsc.angle / 10; + int32_t angle_high = angle_low + 1; + int32_t angle_rem = tr_dsc.angle - (angle_low * 10); + + int32_t s1 = lv_trigo_sin(angle_low); + int32_t s2 = lv_trigo_sin(angle_high); + + int32_t c1 = lv_trigo_sin(angle_low + 90); + int32_t c2 = lv_trigo_sin(angle_high + 90); + + tr_dsc.sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + tr_dsc.cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + tr_dsc.sinma = tr_dsc.sinma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.cosma = tr_dsc.cosma >> (LV_TRIGO_SHIFT - 10); + tr_dsc.pivot_x_256 = tr_dsc.pivot.x * 256; + tr_dsc.pivot_y_256 = tr_dsc.pivot.y * 256; + + lv_coord_t dest_w = lv_area_get_width(dest_area); + lv_coord_t dest_h = lv_area_get_height(dest_area); + lv_coord_t y; + for(y = 0; y < dest_h; y++) { + int32_t xs1_ups, ys1_ups, xs2_ups, ys2_ups; + + transform_point_upscaled(&tr_dsc, dest_area->x1, dest_area->y1 + y, &xs1_ups, &ys1_ups); + transform_point_upscaled(&tr_dsc, dest_area->x2, dest_area->y1 + y, &xs2_ups, &ys2_ups); + + int32_t xs_diff = xs2_ups - xs1_ups; + int32_t ys_diff = ys2_ups - ys1_ups; + int32_t xs_step_256 = 0; + int32_t ys_step_256 = 0; + if(dest_w > 1) { + xs_step_256 = (256 * xs_diff) / (dest_w - 1); + ys_step_256 = (256 * ys_diff) / (dest_w - 1); + } + int32_t xs_ups = xs1_ups; + int32_t ys_ups = ys1_ups; + + if(draw_dsc->antialias == 0) { + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + argb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; + case LV_IMG_CF_TRUE_COLOR: + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + rgb_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + break; + +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + rgb565a8_no_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf); + break; +#endif + default: + break; + } + } + else { + argb_and_rgb_aa(src_buf, src_w, src_h, src_stride, xs_ups, ys_ups, xs_step_256, ys_step_256, dest_w, cbuf, abuf, cf); + } + + cbuf += dest_w; + abuf += dest_w; + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void rgb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + lv_color_t ck = d->driver->color_chroma_key; + + lv_memset_ff(abuf, x_end); + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + } + else { + +#if LV_COLOR_DEPTH == 8 + const uint8_t * src_tmp = src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; +#elif LV_COLOR_DEPTH == 32 + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * sizeof(lv_color_t)) + xs_int * sizeof(lv_color_t); + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + } + if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED && cbuf[x].full == ck.full) { + abuf[x] = 0x00; + } + } +} + +static void argb_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * LV_IMG_PX_SIZE_ALPHA_BYTE) + xs_int * LV_IMG_PX_SIZE_ALPHA_BYTE; + +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + abuf[x] = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } + } +} + +#if LV_COLOR_DEPTH == 16 +static void rgb565a8_no_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0; + } + else { + const lv_color_t * src_tmp = (const lv_color_t *)src; + src_tmp += ys_int * src_stride + xs_int; + cbuf[x] = *src_tmp; + + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_tmp += ys_int * src_stride + xs_int; + abuf[x] = *a_tmp; + } + } +} +#endif + + +static void argb_and_rgb_aa(const uint8_t * src, lv_coord_t src_w, lv_coord_t src_h, lv_coord_t src_stride, + int32_t xs_ups, int32_t ys_ups, int32_t xs_step, int32_t ys_step, + int32_t x_end, lv_color_t * cbuf, uint8_t * abuf, lv_img_cf_t cf) +{ + int32_t xs_ups_start = xs_ups; + int32_t ys_ups_start = ys_ups; + bool has_alpha; + int32_t px_size; + lv_color_t ck = {0}; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR: + has_alpha = false; + px_size = sizeof(lv_color_t); + break; + case LV_IMG_CF_TRUE_COLOR_ALPHA: + has_alpha = true; + px_size = LV_IMG_PX_SIZE_ALPHA_BYTE; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: { + has_alpha = true; + px_size = sizeof(lv_color_t); + lv_disp_t * d = _lv_refr_get_disp_refreshing(); + ck = d->driver->color_chroma_key; + break; + } +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + has_alpha = true; + px_size = sizeof(lv_color_t); + break; +#endif + default: + return; + } + + lv_coord_t x; + for(x = 0; x < x_end; x++) { + xs_ups = xs_ups_start + ((xs_step * x) >> 8); + ys_ups = ys_ups_start + ((ys_step * x) >> 8); + + int32_t xs_int = xs_ups >> 8; + int32_t ys_int = ys_ups >> 8; + + /*Fully out of the image*/ + if(xs_int < 0 || xs_int >= src_w || ys_int < 0 || ys_int >= src_h) { + abuf[x] = 0x00; + continue; + } + + /*Get the direction the hor and ver neighbor + *`fract` will be in range of 0x00..0xFF and `next` (+/-1) indicates the direction*/ + int32_t xs_fract = xs_ups & 0xFF; + int32_t ys_fract = ys_ups & 0xFF; + + int32_t x_next; + int32_t y_next; + if(xs_fract < 0x80) { + x_next = -1; + xs_fract = (0x7F - xs_fract) * 2; + } + else { + x_next = 1; + xs_fract = (xs_fract - 0x80) * 2; + } + if(ys_fract < 0x80) { + y_next = -1; + ys_fract = (0x7F - ys_fract) * 2; + } + else { + y_next = 1; + ys_fract = (ys_fract - 0x80) * 2; + } + + const uint8_t * src_tmp = src; + src_tmp += (ys_int * src_stride * px_size) + xs_int * px_size; + + + if(xs_int + x_next >= 0 && + xs_int + x_next <= src_w - 1 && + ys_int + y_next >= 0 && + ys_int + y_next <= src_h - 1) { + + const uint8_t * px_base = src_tmp; + const uint8_t * px_hor = src_tmp + x_next * px_size; + const uint8_t * px_ver = src_tmp + y_next * src_stride * px_size; + lv_color_t c_base; + lv_color_t c_ver; + lv_color_t c_hor; + + if(has_alpha) { + lv_opa_t a_base; + lv_opa_t a_ver; + lv_opa_t a_hor; + if(cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + a_base = px_base[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_ver = px_ver[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + a_hor = px_hor[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + } +#if LV_COLOR_DEPTH == 16 + else if(cf == LV_IMG_CF_RGB565A8) { + const lv_opa_t * a_tmp = src + src_stride * src_h * sizeof(lv_color_t); + a_base = *(a_tmp + (ys_int * src_stride) + xs_int); + a_hor = *(a_tmp + (ys_int * src_stride) + xs_int + x_next); + a_ver = *(a_tmp + ((ys_int + y_next) * src_stride) + xs_int); + } +#endif + else if(cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) { + if(((lv_color_t *)px_base)->full == ck.full || + ((lv_color_t *)px_ver)->full == ck.full || + ((lv_color_t *)px_hor)->full == ck.full) { + abuf[x] = 0x00; + continue; + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + } + else { + a_base = 0xff; + a_ver = 0xff; + a_hor = 0xff; + } + + if(a_ver != a_base) a_ver = ((a_ver * ys_fract) + (a_base * (0x100 - ys_fract))) >> 8; + if(a_hor != a_base) a_hor = ((a_hor * xs_fract) + (a_base * (0x100 - xs_fract))) >> 8; + abuf[x] = (a_ver + a_hor) >> 1; + + if(abuf[x] == 0x00) continue; + +#if LV_COLOR_DEPTH == 8 + c_base.full = px_base[0]; + c_ver.full = px_ver[0]; + c_hor.full = px_hor[0]; +#elif LV_COLOR_DEPTH == 16 + c_base.full = px_base[0] + (px_base[1] << 8); + c_ver.full = px_ver[0] + (px_ver[1] << 8); + c_hor.full = px_hor[0] + (px_hor[1] << 8); +#elif LV_COLOR_DEPTH == 32 + c_base.full = *((uint32_t *)px_base); + c_ver.full = *((uint32_t *)px_ver); + c_hor.full = *((uint32_t *)px_hor); +#endif + } + /*No alpha channel -> RGB*/ + else { + c_base = *((const lv_color_t *) px_base); + c_hor = *((const lv_color_t *) px_hor); + c_ver = *((const lv_color_t *) px_ver); + abuf[x] = 0xff; + } + + if(c_base.full == c_ver.full && c_base.full == c_hor.full) { + cbuf[x] = c_base; + } + else { + c_ver = lv_color_mix(c_ver, c_base, ys_fract); + c_hor = lv_color_mix(c_hor, c_base, xs_fract); + cbuf[x] = lv_color_mix(c_hor, c_ver, LV_OPA_50); + } + } + /*Partially out of the image*/ + else { +#if LV_COLOR_DEPTH == 8 + cbuf[x].full = src_tmp[0]; +#elif LV_COLOR_DEPTH == 16 + cbuf[x].full = src_tmp[0] + (src_tmp[1] << 8); +#elif LV_COLOR_DEPTH == 32 + cbuf[x].full = *((uint32_t *)src_tmp); +#endif + lv_opa_t a; + switch(cf) { + case LV_IMG_CF_TRUE_COLOR_ALPHA: + a = src_tmp[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; + break; + case LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED: + a = cbuf[x].full == ck.full ? 0x00 : 0xff; + break; +#if LV_COLOR_DEPTH == 16 + case LV_IMG_CF_RGB565A8: + a = *(src + src_stride * src_h * sizeof(lv_color_t) + (ys_int * src_stride) + xs_int); + break; +#endif + default: + a = 0xff; + } + + if((xs_int == 0 && x_next < 0) || (xs_int == src_w - 1 && x_next > 0)) { + abuf[x] = (a * (0xFF - xs_fract)) >> 8; + } + else if((ys_int == 0 && y_next < 0) || (ys_int == src_h - 1 && y_next > 0)) { + abuf[x] = (a * (0xFF - ys_fract)) >> 8; + } + else { + abuf[x] = 0x00; + } + } + } +} + +static void transform_point_upscaled(point_transform_dsc_t * t, int32_t xin, int32_t yin, int32_t * xout, + int32_t * yout) +{ + if(t->angle == 0 && t->zoom == LV_IMG_ZOOM_NONE) { + *xout = xin * 256; + *yout = yin * 256; + return; + } + + xin -= t->pivot.x; + yin -= t->pivot.y; + + if(t->angle == 0) { + *xout = ((int32_t)(xin * t->zoom)) + (t->pivot_x_256); + *yout = ((int32_t)(yin * t->zoom)) + (t->pivot_y_256); + } + else if(t->zoom == LV_IMG_ZOOM_NONE) { + *xout = ((t->cosma * xin - t->sinma * yin) >> 2) + (t->pivot_x_256); + *yout = ((t->sinma * xin + t->cosma * yin) >> 2) + (t->pivot_y_256); + } + else { + *xout = (((t->cosma * xin - t->sinma * yin) * t->zoom) >> 10) + (t->pivot_x_256); + *yout = (((t->sinma * xin + t->cosma * yin) * t->zoom) >> 10) + (t->pivot_y_256); + } +} + +#endif + diff --git a/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk new file mode 100644 index 000000000..bc19e3802 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_draw_swm341_dma2d.mk @@ -0,0 +1,6 @@ +CSRCS += lv_gpu_swm341_dma2d.c + +DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d +VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d + +CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/draw/swm341_dma2d" diff --git a/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c new file mode 100644 index 000000000..74a539467 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.c @@ -0,0 +1,241 @@ +/** + * @file lv_gpu_swm341_dma2d.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_gpu_swm341_dma2d.h" +#include "../../core/lv_refr.h" + +#if LV_USE_GPU_SWM341_DMA2D + +#include LV_GPU_SWM341_DMA2D_INCLUDE + +/********************* + * DEFINES + *********************/ + +#if LV_COLOR_16_SWAP + #error "Can't use DMA2D with LV_COLOR_16_SWAP 1" +#endif + +#if LV_COLOR_DEPTH == 8 + #error "Can't use DMA2D with LV_COLOR_DEPTH == 8" +#endif + +#if LV_COLOR_DEPTH == 16 + #define LV_DMA2D_COLOR_FORMAT LV_SWM341_DMA2D_RGB565 +#elif LV_COLOR_DEPTH == 32 + #define LV_DMA2D_COLOR_FORMAT LV_SWM341_DMA2D_ARGB8888 +#else + /*Can't use GPU with other formats*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void lv_draw_swm341_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color); + +static void lv_draw_swm341_dma2d_blend_map(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa); + +static void lv_draw_swm341_dma2d_img_decoded(lv_draw_ctx_t * draw, const lv_draw_img_dsc_t * dsc, + const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Turn on the peripheral and set output color mode, this only needs to be done once + */ +void lv_draw_swm341_dma2d_init(void) +{ + /*Enable DMA2D clock*/ + SYS->CLKEN0 |= (1 << SYS_CLKEN0_DMA2D_Pos); + + DMA2D->CR &= ~DMA2D_CR_WAIT_Msk; + DMA2D->CR |= (CyclesPerUs << DMA2D_CR_WAIT_Pos); + + DMA2D->IF = 0xFF; + DMA2D->IE = (0 << DMA2D_IE_DONE_Pos); + + /*set output colour mode*/ + DMA2D->L[DMA2D_LAYER_OUT].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos); +} + +void lv_draw_swm341_dma2d_ctx_init(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + + lv_draw_sw_init_ctx(drv, draw_ctx); + + lv_draw_swm341_dma2d_ctx_t * dma2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx; + + dma2d_draw_ctx->blend = lv_draw_swm341_dma2d_blend; + // dma2d_draw_ctx->base_draw.draw_img_decoded = lv_draw_swm341_dma2d_img_decoded; + dma2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_swm341_dma2d_wait_cb; +} + +void lv_draw_swm341_dma2d_ctx_deinit(lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx) +{ + LV_UNUSED(drv); + LV_UNUSED(draw_ctx); +} + +void lv_draw_swm341_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc) +{ + lv_area_t blend_area; + if(!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) + return; + + bool done = false; + + if(dsc->mask_buf == NULL && dsc->blend_mode == LV_BLEND_MODE_NORMAL && lv_area_get_size(&blend_area) > 100) { + lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area); + + lv_color_t * dest_buf = draw_ctx->buf; + dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1); + + const lv_color_t * src_buf = dsc->src_buf; + if(src_buf) { + lv_draw_sw_blend_basic(draw_ctx, dsc); + lv_coord_t src_stride; + src_stride = lv_area_get_width(dsc->blend_area); + src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1); + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + lv_draw_swm341_dma2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa); + done = true; + } + else if(dsc->opa >= LV_OPA_MAX) { + lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1); + lv_draw_swm341_dma2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color); + done = true; + } + } + + if(!done) lv_draw_sw_blend_basic(draw_ctx, dsc); +} + +static void lv_draw_swm341_dma2d_img_decoded(lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc, + const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format) +{ + /*TODO basic ARGB8888 image can be handles here*/ + + lv_draw_sw_img_decoded(draw_ctx, dsc, coords, map_p, color_format); +} + +static void lv_draw_swm341_dma2d_blend_fill(lv_color_t * dest_buf, lv_coord_t dest_stride, const lv_area_t * fill_area, + lv_color_t color) +{ + /*Simply fill an area*/ + int32_t area_w = lv_area_get_width(fill_area); + int32_t area_h = lv_area_get_height(fill_area); + +#if 1 + DMA2D->L[DMA2D_LAYER_OUT].COLOR = color.full; + + DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf; + DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - area_w; + DMA2D->NLR = ((area_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((area_h - 1) << DMA2D_NLR_NLINE_Pos); + + /*start transfer*/ + DMA2D->CR &= ~DMA2D_CR_MODE_Msk; + DMA2D->CR |= (3 << DMA2D_CR_MODE_Pos) | + (1 << DMA2D_CR_START_Pos); +#else + for(uint32_t y = 0; y < area_h; y++) { + for(uint32_t x = 0; x < area_w; x++) { + dest_buf[y * dest_stride + x] = color; + } + } +#endif +} + +static void lv_draw_swm341_dma2d_blend_map(lv_color_t * dest_buf, const lv_area_t * dest_area, lv_coord_t dest_stride, + const lv_color_t * src_buf, lv_coord_t src_stride, lv_opa_t opa) +{ + + /*Simple copy*/ + int32_t dest_w = lv_area_get_width(dest_area); + int32_t dest_h = lv_area_get_height(dest_area); + + if(opa >= LV_OPA_MAX) { +#if 1 + /*copy output colour mode, this register controls both input and output colour format*/ + DMA2D->L[DMA2D_LAYER_FG].MAR = (uint32_t)src_buf; + DMA2D->L[DMA2D_LAYER_FG].OR = src_stride - dest_w; + DMA2D->L[DMA2D_LAYER_FG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos); + + DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf; + DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - dest_w; + + DMA2D->NLR = ((dest_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((dest_h - 1) << DMA2D_NLR_NLINE_Pos); + + /*start transfer*/ + DMA2D->CR &= ~DMA2D_CR_MODE_Msk; + DMA2D->CR |= (0 << DMA2D_CR_MODE_Pos) | + (1 << DMA2D_CR_START_Pos); +#else + lv_color_t temp_buf[1024]; + for(uint32_t y = 0; y < dest_h; y++) { + memcpy(temp_buf, &src_buf[y * src_stride], dest_w * sizeof(lv_color_t)); + memcpy(&dest_buf[y * dest_stride], temp_buf, dest_w * sizeof(lv_color_t)); + } +#endif + } + else { + DMA2D->L[DMA2D_LAYER_FG].MAR = (uint32_t)src_buf; + DMA2D->L[DMA2D_LAYER_FG].OR = src_stride - dest_w; + DMA2D->L[DMA2D_LAYER_FG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos) + /*alpha mode 2, replace with foreground * alpha value*/ + | (2 << DAM2D_PFCCR_AMODE_Pos) + /*alpha value*/ + | (opa << DMA2D_PFCCR_ALPHA_Pos); + + DMA2D->L[DMA2D_LAYER_BG].MAR = (uint32_t)dest_buf; + DMA2D->L[DMA2D_LAYER_BG].OR = dest_stride - dest_w; + DMA2D->L[DMA2D_LAYER_BG].PFCCR = (LV_DMA2D_COLOR_FORMAT << DMA2D_PFCCR_CFMT_Pos); + + DMA2D->L[DMA2D_LAYER_OUT].MAR = (uint32_t)dest_buf; + DMA2D->L[DMA2D_LAYER_OUT].OR = dest_stride - dest_w; + + DMA2D->NLR = ((dest_w - 1) << DMA2D_NLR_NPIXEL_Pos) | ((dest_h - 1) << DMA2D_NLR_NLINE_Pos); + + /*start transfer*/ + DMA2D->CR &= ~DMA2D_CR_MODE_Msk; + DMA2D->CR |= (2 << DMA2D_CR_MODE_Pos) | + (1 << DMA2D_CR_START_Pos); + } +} + +void lv_gpu_swm341_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx) +{ + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + if(disp->driver && disp->driver->wait_cb) { + while(DMA2D->CR & DMA2D_CR_START_Msk) { + disp->driver->wait_cb(disp->driver); + } + } + else { + while(DMA2D->CR & DMA2D_CR_START_Msk); + } + lv_draw_sw_wait_for_finish(draw_ctx); +} + +#endif diff --git a/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h new file mode 100644 index 000000000..20b892260 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/draw/swm341_dma2d/lv_gpu_swm341_dma2d.h @@ -0,0 +1,64 @@ +/** + * @file lv_gpu_swm341_dma2d.h + * + */ + +#ifndef LV_GPU_SWM341_DMA2D_H +#define LV_GPU_SWM341_DMA2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../misc/lv_color.h" +#include "../../hal/lv_hal_disp.h" +#include "../sw/lv_draw_sw.h" + +#if LV_USE_GPU_SWM341_DMA2D + +/********************* + * DEFINES + *********************/ + +#define LV_SWM341_DMA2D_ARGB8888 0 +#define LV_SWM341_DMA2D_RGB888 1 +#define LV_SWM341_DMA2D_RGB565 2 + +/********************** + * TYPEDEFS + **********************/ +typedef lv_draw_sw_ctx_t lv_draw_swm341_dma2d_ctx_t; + +struct _lv_disp_drv_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Turn on the peripheral and set output color mode, this only needs to be done once + */ +void lv_draw_swm341_dma2d_init(void); + +void lv_draw_swm341_dma2d_ctx_init(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_swm341_dma2d_ctx_deinit(struct _lv_disp_drv_t * drv, lv_draw_ctx_t * draw_ctx); + +void lv_draw_swm341_dma2d_blend(lv_draw_ctx_t * draw_ctx, const lv_draw_sw_blend_dsc_t * dsc); + +void lv_gpu_swm341_dma2d_wait_cb(lv_draw_ctx_t * draw_ctx); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GPU_SWM341_DMA2D*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GPU_SWM341_DMA2D_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/README.md b/lib/libesp32_lvgl/lvgl/src/extra/README.md index 11742114d..80bb49d4f 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/README.md +++ b/lib/libesp32_lvgl/lvgl/src/extra/README.md @@ -1,6 +1,6 @@ # Extra components -This directory contains extra (optional) components to lvgl. +This directory contains extra (optional) components to lvgl. It's a good place for contributions as there are less strict expectations about the completeness and flexibility of the components here. In other words, if you have created a complex widget from other widgets, or modified an existing widget with special events, styles or animations, or have a new feature that could work as a plugin to lvgl feel free to the share it here. @@ -10,13 +10,13 @@ In other words, if you have created a complex widget from other widgets, or modi - Please and follow the [Coding style](https://github.com/lvgl/lvgl/blob/master/docs/CODING_STYLE.md) of LVGL - Add setter/getter functions in pair - Update [lv_conf_template.h](https://github.com/lvgl/lvgl/blob/master/lv_conf_template.h) -- Add description in the [docs](https://github.com/lvgl/lvgl/tree/master/docs) +- Add description in the [docs](https://github.com/lvgl/lvgl/tree/master/docs) - Add [examples](https://github.com/lvgl/lvgl/tree/master/examples) - Update the [changelog](https://github.com/lvgl/lvgl/tree/master/docs/CHANGELOG.md) - Add yourself to the [Contributors](#contributors) section below. ## Ideas -Here some ideas as inspiration feel free to contribute with ideas too. +Here some ideas as inspiration feel free to contribute with ideas too. - New [Calendar headers](https://github.com/lvgl/lvgl/tree/master/src/extra/widgets/calendar) - Color picker with RGB and or HSV bars - Ruler, horizontal or vertical with major and minor ticks and labels @@ -27,5 +27,5 @@ Here some ideas as inspiration feel free to contribute with ideas too. ## Contributors - lv_animimg: @ZhaoQiang-b45475 -- lv_span: @guoweilkd +- lv_span: @guoweilkd - lv_menu: @HX2003 \ No newline at end of file diff --git a/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.c b/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.c index 118034273..405a56b7f 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.c @@ -65,7 +65,7 @@ static lv_obj_t * get_next_item(lv_obj_t * cont, bool rev, int32_t * item_id); /********************** * GLOBAL VARIABLES **********************/ -uint32_t LV_LAYOUT_FLEX; +uint16_t LV_LAYOUT_FLEX; lv_style_prop_t LV_STYLE_FLEX_FLOW; lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE; lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE; @@ -93,10 +93,10 @@ void lv_flex_init(void) { LV_LAYOUT_FLEX = lv_layout_register(flex_update, NULL); - LV_STYLE_FLEX_FLOW = lv_style_register_prop(); - LV_STYLE_FLEX_MAIN_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_FLEX_CROSS_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_FLEX_TRACK_PLACE = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; + LV_STYLE_FLEX_FLOW = lv_style_register_prop(LV_STYLE_PROP_FLAG_NONE); + LV_STYLE_FLEX_MAIN_PLACE = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_FLEX_CROSS_PLACE = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_FLEX_TRACK_PLACE = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); } void lv_obj_set_flex_flow(lv_obj_t * obj, lv_flex_flow_t flow) @@ -428,6 +428,7 @@ static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, i for(i = 0; i < t->grow_item_cnt; i++) { if(t->grow_dsc[i].clamped == 0) { + LV_ASSERT(grow_value_sum != 0); grow_unit = grow_max_size / grow_value_sum; lv_coord_t size = grow_unit * t->grow_dsc[i].grow_value; lv_coord_t size_clamp = LV_CLAMP(t->grow_dsc[i].min_size, size, t->grow_dsc[i].max_size); @@ -525,7 +526,7 @@ static void children_repos(lv_obj_t * cont, flex_t * f, int32_t item_first_id, i item->coords.y1 += diff_y; item->coords.y2 += diff_y; lv_obj_invalidate(item); - lv_obj_move_children_by(item, diff_x, diff_y, true); + lv_obj_move_children_by(item, diff_x, diff_y, false); } if(!(f->row && rtl)) main_pos += area_get_main_size(&item->coords) + item_gap + place_gap; diff --git a/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.h b/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.h index 139fd4810..1499d884f 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/layouts/flex/lv_flex.h @@ -57,7 +57,7 @@ typedef enum { /********************** * GLOBAL VARIABLES **********************/ -extern uint32_t LV_LAYOUT_FLEX; +extern uint16_t LV_LAYOUT_FLEX; extern lv_style_prop_t LV_STYLE_FLEX_FLOW; extern lv_style_prop_t LV_STYLE_FLEX_MAIN_PLACE; extern lv_style_prop_t LV_STYLE_FLEX_CROSS_PLACE; diff --git a/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.c b/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.c index 6812a04a2..74f8e95fb 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.c @@ -102,7 +102,7 @@ static inline uint8_t get_grid_row_align(lv_obj_t * obj) /********************** * GLOBAL VARIABLES **********************/ -uint32_t LV_LAYOUT_GRID; +uint16_t LV_LAYOUT_GRID; lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY; lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN; lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY; @@ -131,17 +131,17 @@ void lv_grid_init(void) { LV_LAYOUT_GRID = lv_layout_register(grid_update, NULL); - LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; + LV_STYLE_GRID_COLUMN_DSC_ARRAY = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_ROW_DSC_ARRAY = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_COLUMN_ALIGN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_ROW_ALIGN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); - LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; - LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop() | LV_STYLE_PROP_LAYOUT_REFR; + LV_STYLE_GRID_CELL_ROW_SPAN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_CELL_ROW_POS = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_CELL_COLUMN_SPAN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_CELL_COLUMN_POS = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_CELL_X_ALIGN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); + LV_STYLE_GRID_CELL_Y_ALIGN = lv_style_register_prop(LV_STYLE_PROP_LAYOUT_REFR); } void lv_obj_set_grid_dsc_array(lv_obj_t * obj, const lv_coord_t col_dsc[], const lv_coord_t row_dsc[]) @@ -561,6 +561,8 @@ static void calc_rows(lv_obj_t * cont, _lv_grid_calc_t * c) if(IS_FR(x)) { lv_coord_t f = GET_FR(x); c->h[i] = (free_h * f) / row_fr_cnt; + last_fr_i = i; + last_fr_x = f; } } @@ -685,7 +687,7 @@ static void item_repos(lv_obj_t * item, _lv_grid_calc_t * c, item_repos_hint_t * item->coords.y1 += diff_y; item->coords.y2 += diff_y; lv_obj_invalidate(item); - lv_obj_move_children_by(item, diff_x, diff_y, true); + lv_obj_move_children_by(item, diff_x, diff_y, false); } } diff --git a/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.h b/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.h index cd2510ec0..5c4f76725 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/layouts/grid/lv_grid.h @@ -53,7 +53,7 @@ typedef enum { * GLOBAL VARIABLES **********************/ -extern uint32_t LV_LAYOUT_GRID; +extern uint16_t LV_LAYOUT_GRID; extern lv_style_prop_t LV_STYLE_GRID_COLUMN_DSC_ARRAY; extern lv_style_prop_t LV_STYLE_GRID_COLUMN_ALIGN; extern lv_style_prop_t LV_STYLE_GRID_ROW_DSC_ARRAY; diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.c new file mode 100644 index 000000000..f89a0a8c7 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.c @@ -0,0 +1,258 @@ +/** + * @file lv_bmp.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_BMP + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_fs_file_t f; + unsigned int px_offset; + int px_width; + int px_height; + unsigned int bpp; + int row_size_bytes; +} bmp_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header); +static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); + + +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, + lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf); + +static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_bmp_init(void) +{ + lv_img_decoder_t * dec = lv_img_decoder_create(); + lv_img_decoder_set_info_cb(dec, decoder_info); + lv_img_decoder_set_open_cb(dec, decoder_open); + lv_img_decoder_set_read_line_cb(dec, decoder_read_line); + lv_img_decoder_set_close_cb(dec, decoder_close); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get info about a PNG image + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header) +{ + LV_UNUSED(decoder); + + lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/ + + /*If it's a BMP file...*/ + if(src_type == LV_IMG_SRC_FILE) { + const char * fn = src; + if(strcmp(lv_fs_get_ext(fn), "bmp") == 0) { /*Check the extension*/ + /*Save the data in the header*/ + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return LV_RES_INV; + uint8_t headers[54]; + + lv_fs_read(&f, headers, 54, NULL); + uint32_t w; + uint32_t h; + memcpy(&w, headers + 18, 4); + memcpy(&h, headers + 22, 4); + header->w = w; + header->h = h; + header->always_zero = 0; + lv_fs_close(&f); +#if LV_COLOR_DEPTH == 32 + uint16_t bpp; + memcpy(&bpp, headers + 28, 2); + header->cf = bpp == 32 ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; +#else + header->cf = LV_IMG_CF_TRUE_COLOR; +#endif + return LV_RES_OK; + } + } + /* BMP file as data not supported for simplicity. + * Convert them to LVGL compatible C arrays directly. */ + else if(src_type == LV_IMG_SRC_VARIABLE) { + return LV_RES_INV; + } + + return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/ +} + + +/** + * Open a PNG image and return the decided image + * @param src can be file name or pointer to a C array + * @param style style of the image object (unused now but certain formats might use it) + * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed + */ +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + + /*If it's a PNG file...*/ + if(dsc->src_type == LV_IMG_SRC_FILE) { + const char * fn = dsc->src; + + if(strcmp(lv_fs_get_ext(fn), "bmp") != 0) { + return LV_RES_INV; /*Check the extension*/ + } + + bmp_dsc_t b; + memset(&b, 0x00, sizeof(b)); + + lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD); + if(res == LV_RES_OK) return LV_RES_INV; + + uint8_t header[54]; + lv_fs_read(&b.f, header, 54, NULL); + + if(0x42 != header[0] || 0x4d != header[1]) { + lv_fs_close(&b.f); + return LV_RES_INV; + } + + memcpy(&b.px_offset, header + 10, 4); + memcpy(&b.px_width, header + 18, 4); + memcpy(&b.px_height, header + 22, 4); + memcpy(&b.bpp, header + 28, 2); + b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4; + + bool color_depth_error = false; + if(LV_COLOR_DEPTH == 32 && (b.bpp != 32 && b.bpp != 24)) { + LV_LOG_WARN("LV_COLOR_DEPTH == 32 but bpp is %d (should be 32 or 24)", b.bpp); + color_depth_error = true; + } + else if(LV_COLOR_DEPTH == 16 && b.bpp != 16) { + LV_LOG_WARN("LV_COLOR_DEPTH == 16 but bpp is %d (should be 16)", b.bpp); + color_depth_error = true; + } + else if(LV_COLOR_DEPTH == 8 && b.bpp != 8) { + LV_LOG_WARN("LV_COLOR_DEPTH == 8 but bpp is %d (should be 8)", b.bpp); + color_depth_error = true; + } + + if(color_depth_error) { + dsc->error_msg = "Color depth mismatch"; + lv_fs_close(&b.f); + return LV_RES_INV; + } + + dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t)); + LV_ASSERT_MALLOC(dsc->user_data); + if(dsc->user_data == NULL) return LV_RES_INV; + memcpy(dsc->user_data, &b, sizeof(b)); + + dsc->img_data = NULL; + return LV_RES_OK; + } + /* BMP file as data not supported for simplicity. + * Convert them to LVGL compatible C arrays directly. */ + else if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + return LV_RES_INV; + } + + return LV_RES_INV; /*If not returned earlier then it failed*/ +} + + +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, + lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf) +{ + LV_UNUSED(decoder); + + bmp_dsc_t * b = dsc->user_data; + y = (b->px_height - 1) - y; /*BMP images are stored upside down*/ + uint32_t p = b->px_offset + b->row_size_bytes * y; + p += x * (b->bpp / 8); + lv_fs_seek(&b->f, p, LV_FS_SEEK_SET); + lv_fs_read(&b->f, buf, len * (b->bpp / 8), NULL); + +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 1 + for(unsigned int i = 0; i < len * (b->bpp / 8); i += 2) { + buf[i] = buf[i] ^ buf[i + 1]; + buf[i + 1] = buf[i] ^ buf[i + 1]; + buf[i] = buf[i] ^ buf[i + 1]; + } + +#elif LV_COLOR_DEPTH == 32 + if(b->bpp == 32) { + lv_coord_t i; + for(i = 0; i < len; i++) { + uint8_t b0 = buf[i * 4]; + uint8_t b1 = buf[i * 4 + 1]; + uint8_t b2 = buf[i * 4 + 2]; + uint8_t b3 = buf[i * 4 + 3]; + lv_color32_t * c = (lv_color32_t *)&buf[i * 4]; + c->ch.red = b2; + c->ch.green = b1; + c->ch.blue = b0; + c->ch.alpha = b3; + } + } + if(b->bpp == 24) { + lv_coord_t i; + + for(i = len - 1; i >= 0; i--) { + uint8_t * t = &buf[i * 3]; + lv_color32_t * c = (lv_color32_t *)&buf[i * 4]; + c->ch.red = t[2]; + c->ch.green = t[1]; + c->ch.blue = t[0]; + c->ch.alpha = 0xff; + } + } +#endif + + return LV_RES_OK; +} + + +/** + * Free the allocated resources + */ +static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + bmp_dsc_t * b = dsc->user_data; + lv_fs_close(&b->f); + lv_mem_free(dsc->user_data); + +} + +#endif /*LV_USE_BMP*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.h new file mode 100644 index 000000000..db1e54096 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/bmp/lv_bmp.h @@ -0,0 +1,42 @@ +/** + * @file lv_bmp.h + * + */ + +#ifndef LV_BMP_H +#define LV_BMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" +#if LV_USE_BMP + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_bmp_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_BMP*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BMP_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c new file mode 100644 index 000000000..efaa6925e --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.c @@ -0,0 +1,875 @@ +/** + * @file lv_ffmpeg.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_ffmpeg.h" +#if LV_USE_FFMPEG != 0 + +#include +#include +#include +#include +#include +#include + +/********************* + * DEFINES + *********************/ +#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8 + #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB8 +#elif LV_COLOR_DEPTH == 16 + #if LV_COLOR_16_SWAP == 0 + #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565LE + #else + #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565BE + #endif +#elif LV_COLOR_DEPTH == 32 + #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_BGR0 +#else + #error Unsupported LV_COLOR_DEPTH +#endif + +#define MY_CLASS &lv_ffmpeg_player_class + +#define FRAME_DEF_REFR_PERIOD 33 /*[ms]*/ + +/********************** + * TYPEDEFS + **********************/ +struct ffmpeg_context_s { + AVFormatContext * fmt_ctx; + AVCodecContext * video_dec_ctx; + AVStream * video_stream; + uint8_t * video_src_data[4]; + uint8_t * video_dst_data[4]; + struct SwsContext * sws_ctx; + AVFrame * frame; + AVPacket pkt; + int video_stream_idx; + int video_src_linesize[4]; + int video_dst_linesize[4]; + enum AVPixelFormat video_dst_pix_fmt; + bool has_alpha; +}; + +#pragma pack(1) + +struct lv_img_pixel_color_s { + lv_color_t c; + uint8_t alpha; +}; + +#pragma pack() + +/********************** + * STATIC PROTOTYPES + **********************/ + +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header); +static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); +static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); + +static struct ffmpeg_context_s * ffmpeg_open_file(const char * path); +static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx); +static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx); +static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx); +static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx); +static int ffmpeg_get_img_header(const char * path, lv_img_header_t * header); +static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx); +static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx); +static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx); +static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx); +static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt); +static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt); + +static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); + +#if LV_COLOR_DEPTH != 32 + static void convert_color_depth(uint8_t * img, uint32_t px_cnt); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +const lv_obj_class_t lv_ffmpeg_player_class = { + .constructor_cb = lv_ffmpeg_player_constructor, + .destructor_cb = lv_ffmpeg_player_destructor, + .instance_size = sizeof(lv_ffmpeg_player_t), + .base_class = &lv_img_class +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_ffmpeg_init(void) +{ + lv_img_decoder_t * dec = lv_img_decoder_create(); + lv_img_decoder_set_info_cb(dec, decoder_info); + lv_img_decoder_set_open_cb(dec, decoder_open); + lv_img_decoder_set_close_cb(dec, decoder_close); + +#if LV_FFMPEG_AV_DUMP_FORMAT == 0 + av_log_set_level(AV_LOG_QUIET); +#endif +} + +int lv_ffmpeg_get_frame_num(const char * path) +{ + int ret = -1; + struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path); + + if(ffmpeg_ctx) { + ret = ffmpeg_ctx->video_stream->nb_frames; + ffmpeg_close(ffmpeg_ctx); + } + + return ret; +} + +lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent) +{ + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + +lv_res_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_res_t res = LV_RES_INV; + + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + + if(player->ffmpeg_ctx) { + ffmpeg_close(player->ffmpeg_ctx); + player->ffmpeg_ctx = NULL; + } + + lv_timer_pause(player->timer); + + player->ffmpeg_ctx = ffmpeg_open_file(path); + + if(!player->ffmpeg_ctx) { + LV_LOG_ERROR("ffmpeg file open failed: %s", path); + goto failed; + } + + if(ffmpeg_image_allocate(player->ffmpeg_ctx) < 0) { + LV_LOG_ERROR("ffmpeg image allocate failed"); + ffmpeg_close(player->ffmpeg_ctx); + goto failed; + } + + bool has_alpha = player->ffmpeg_ctx->has_alpha; + int width = player->ffmpeg_ctx->video_dec_ctx->width; + int height = player->ffmpeg_ctx->video_dec_ctx->height; + uint32_t data_size = 0; + + if(has_alpha) { + data_size = width * height * LV_IMG_PX_SIZE_ALPHA_BYTE; + } + else { + data_size = width * height * LV_COLOR_SIZE / 8; + } + + player->imgdsc.header.always_zero = 0; + player->imgdsc.header.w = width; + player->imgdsc.header.h = height; + player->imgdsc.data_size = data_size; + player->imgdsc.header.cf = has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; + player->imgdsc.data = ffmpeg_get_img_data(player->ffmpeg_ctx); + + lv_img_set_src(&player->img.obj, &(player->imgdsc)); + + int period = ffmpeg_get_frame_refr_period(player->ffmpeg_ctx); + + if(period > 0) { + LV_LOG_INFO("frame refresh period = %d ms, rate = %d fps", + period, 1000 / period); + lv_timer_set_period(player->timer, period); + } + else { + LV_LOG_WARN("unable to get frame refresh period"); + } + + res = LV_RES_OK; + +failed: + return res; +} + +void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + + if(!player->ffmpeg_ctx) { + LV_LOG_ERROR("ffmpeg_ctx is NULL"); + return; + } + + lv_timer_t * timer = player->timer; + + switch(cmd) { + case LV_FFMPEG_PLAYER_CMD_START: + av_seek_frame(player->ffmpeg_ctx->fmt_ctx, + 0, 0, AVSEEK_FLAG_BACKWARD); + lv_timer_resume(timer); + LV_LOG_INFO("ffmpeg player start"); + break; + case LV_FFMPEG_PLAYER_CMD_STOP: + av_seek_frame(player->ffmpeg_ctx->fmt_ctx, + 0, 0, AVSEEK_FLAG_BACKWARD); + lv_timer_pause(timer); + LV_LOG_INFO("ffmpeg player stop"); + break; + case LV_FFMPEG_PLAYER_CMD_PAUSE: + lv_timer_pause(timer); + LV_LOG_INFO("ffmpeg player pause"); + break; + case LV_FFMPEG_PLAYER_CMD_RESUME: + lv_timer_resume(timer); + LV_LOG_INFO("ffmpeg player resume"); + break; + default: + LV_LOG_ERROR("Error cmd: %d", cmd); + break; + } +} + +void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + player->auto_restart = en; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header) +{ + /* Get the source type */ + lv_img_src_t src_type = lv_img_src_get_type(src); + + if(src_type == LV_IMG_SRC_FILE) { + const char * fn = src; + + if(ffmpeg_get_img_header(fn, header) < 0) { + LV_LOG_ERROR("ffmpeg can't get image header"); + return LV_RES_INV; + } + + return LV_RES_OK; + } + + /* If didn't succeeded earlier then it's an error */ + return LV_RES_INV; +} + +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + if(dsc->src_type == LV_IMG_SRC_FILE) { + const char * path = dsc->src; + + struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path); + + if(ffmpeg_ctx == NULL) { + return LV_RES_INV; + } + + if(ffmpeg_image_allocate(ffmpeg_ctx) < 0) { + LV_LOG_ERROR("ffmpeg image allocate failed"); + ffmpeg_close(ffmpeg_ctx); + return LV_RES_INV; + } + + if(ffmpeg_update_next_frame(ffmpeg_ctx) < 0) { + ffmpeg_close(ffmpeg_ctx); + LV_LOG_ERROR("ffmpeg update frame failed"); + return LV_RES_INV; + } + + ffmpeg_close_src_ctx(ffmpeg_ctx); + uint8_t * img_data = ffmpeg_get_img_data(ffmpeg_ctx); + +#if LV_COLOR_DEPTH != 32 + if(ffmpeg_ctx->has_alpha) { + convert_color_depth(img_data, dsc->header.w * dsc->header.h); + } +#endif + + dsc->user_data = ffmpeg_ctx; + dsc->img_data = img_data; + + /* The image is fully decoded. Return with its pointer */ + return LV_RES_OK; + } + + /* If not returned earlier then it failed */ + return LV_RES_INV; +} + +static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + struct ffmpeg_context_s * ffmpeg_ctx = dsc->user_data; + ffmpeg_close(ffmpeg_ctx); +} + +#if LV_COLOR_DEPTH != 32 + +static void convert_color_depth(uint8_t * img, uint32_t px_cnt) +{ + lv_color32_t * img_src_p = (lv_color32_t *)img; + struct lv_img_pixel_color_s * img_dst_p = (struct lv_img_pixel_color_s *)img; + + for(uint32_t i = 0; i < px_cnt; i++) { + lv_color32_t temp = *img_src_p; + img_dst_p->c = lv_color_hex(temp.full); + img_dst_p->alpha = temp.ch.alpha; + + img_src_p++; + img_dst_p++; + } +} + +#endif + +static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx) +{ + uint8_t * img_data = ffmpeg_ctx->video_dst_data[0]; + + if(img_data == NULL) { + LV_LOG_ERROR("ffmpeg video dst data is NULL"); + } + + return img_data; +} + +static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt) +{ + const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt); + + if(desc == NULL) { + return false; + } + + if(pix_fmt == AV_PIX_FMT_PAL8) { + return true; + } + + return (desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? true : false; +} + +static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt) +{ + const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt); + + if(desc == NULL) { + return false; + } + + return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components >= 2; +} + +static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx) +{ + int ret = -1; + + int width = ffmpeg_ctx->video_dec_ctx->width; + int height = ffmpeg_ctx->video_dec_ctx->height; + AVFrame * frame = ffmpeg_ctx->frame; + + if(frame->width != width + || frame->height != height + || frame->format != ffmpeg_ctx->video_dec_ctx->pix_fmt) { + + /* To handle this change, one could call av_image_alloc again and + * decode the following frames into another rawvideo file. + */ + LV_LOG_ERROR("Width, height and pixel format have to be " + "constant in a rawvideo file, but the width, height or " + "pixel format of the input video changed:\n" + "old: width = %d, height = %d, format = %s\n" + "new: width = %d, height = %d, format = %s\n", + width, + height, + av_get_pix_fmt_name(ffmpeg_ctx->video_dec_ctx->pix_fmt), + frame->width, frame->height, + av_get_pix_fmt_name(frame->format)); + goto failed; + } + + LV_LOG_TRACE("video_frame coded_n:%d", frame->coded_picture_number); + + /* copy decoded frame to destination buffer: + * this is required since rawvideo expects non aligned data + */ + av_image_copy(ffmpeg_ctx->video_src_data, ffmpeg_ctx->video_src_linesize, + (const uint8_t **)(frame->data), frame->linesize, + ffmpeg_ctx->video_dec_ctx->pix_fmt, width, height); + + if(ffmpeg_ctx->sws_ctx == NULL) { + int swsFlags = SWS_BILINEAR; + + if(ffmpeg_pix_fmt_is_yuv(ffmpeg_ctx->video_dec_ctx->pix_fmt)) { + + /* When the video width and height are not multiples of 8, + * and there is no size change in the conversion, + * a blurry screen will appear on the right side + * This problem was discovered in 2012 and + * continues to exist in version 4.1.3 in 2019 + * This problem can be avoided by increasing SWS_ACCURATE_RND + */ + if((width & 0x7) || (height & 0x7)) { + LV_LOG_WARN("The width(%d) and height(%d) the image " + "is not a multiple of 8, " + "the decoding speed may be reduced", + width, height); + swsFlags |= SWS_ACCURATE_RND; + } + } + + ffmpeg_ctx->sws_ctx = sws_getContext( + width, height, ffmpeg_ctx->video_dec_ctx->pix_fmt, + width, height, ffmpeg_ctx->video_dst_pix_fmt, + swsFlags, + NULL, NULL, NULL); + } + + if(!ffmpeg_ctx->has_alpha) { + int lv_linesize = sizeof(lv_color_t) * width; + int dst_linesize = ffmpeg_ctx->video_dst_linesize[0]; + if(dst_linesize != lv_linesize) { + LV_LOG_WARN("ffmpeg linesize = %d, but lvgl image require %d", + dst_linesize, + lv_linesize); + ffmpeg_ctx->video_dst_linesize[0] = lv_linesize; + } + } + + ret = sws_scale( + ffmpeg_ctx->sws_ctx, + (const uint8_t * const *)(ffmpeg_ctx->video_src_data), + ffmpeg_ctx->video_src_linesize, + 0, + height, + ffmpeg_ctx->video_dst_data, + ffmpeg_ctx->video_dst_linesize); + +failed: + return ret; +} + +static int ffmpeg_decode_packet(AVCodecContext * dec, const AVPacket * pkt, + struct ffmpeg_context_s * ffmpeg_ctx) +{ + int ret = 0; + + /* submit the packet to the decoder */ + ret = avcodec_send_packet(dec, pkt); + if(ret < 0) { + LV_LOG_ERROR("Error submitting a packet for decoding (%s)", + av_err2str(ret)); + return ret; + } + + /* get all the available frames from the decoder */ + while(ret >= 0) { + ret = avcodec_receive_frame(dec, ffmpeg_ctx->frame); + if(ret < 0) { + + /* those two return values are special and mean there is + * no output frame available, + * but there were no errors during decoding + */ + if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + return 0; + } + + LV_LOG_ERROR("Error during decoding (%s)", av_err2str(ret)); + return ret; + } + + /* write the frame data to output file */ + if(dec->codec->type == AVMEDIA_TYPE_VIDEO) { + ret = ffmpeg_output_video_frame(ffmpeg_ctx); + } + + av_frame_unref(ffmpeg_ctx->frame); + if(ret < 0) { + LV_LOG_WARN("ffmpeg_decode_packet ended %d", ret); + return ret; + } + } + + return 0; +} + +static int ffmpeg_open_codec_context(int * stream_idx, + AVCodecContext ** dec_ctx, AVFormatContext * fmt_ctx, + enum AVMediaType type) +{ + int ret; + int stream_index; + AVStream * st; + AVCodec * dec = NULL; + AVDictionary * opts = NULL; + + ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); + if(ret < 0) { + LV_LOG_ERROR("Could not find %s stream in input file", + av_get_media_type_string(type)); + return ret; + } + else { + stream_index = ret; + st = fmt_ctx->streams[stream_index]; + + /* find decoder for the stream */ + dec = avcodec_find_decoder(st->codecpar->codec_id); + if(dec == NULL) { + LV_LOG_ERROR("Failed to find %s codec", + av_get_media_type_string(type)); + return AVERROR(EINVAL); + } + + /* Allocate a codec context for the decoder */ + *dec_ctx = avcodec_alloc_context3(dec); + if(*dec_ctx == NULL) { + LV_LOG_ERROR("Failed to allocate the %s codec context", + av_get_media_type_string(type)); + return AVERROR(ENOMEM); + } + + /* Copy codec parameters from input stream to output codec context */ + if((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) { + LV_LOG_ERROR( + "Failed to copy %s codec parameters to decoder context", + av_get_media_type_string(type)); + return ret; + } + + /* Init the decoders */ + if((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) { + LV_LOG_ERROR("Failed to open %s codec", + av_get_media_type_string(type)); + return ret; + } + + *stream_idx = stream_index; + } + + return 0; +} + +static int ffmpeg_get_img_header(const char * filepath, + lv_img_header_t * header) +{ + int ret = -1; + + AVFormatContext * fmt_ctx = NULL; + AVCodecContext * video_dec_ctx = NULL; + int video_stream_idx; + + /* open input file, and allocate format context */ + if(avformat_open_input(&fmt_ctx, filepath, NULL, NULL) < 0) { + LV_LOG_ERROR("Could not open source file %s", filepath); + goto failed; + } + + /* retrieve stream information */ + if(avformat_find_stream_info(fmt_ctx, NULL) < 0) { + LV_LOG_ERROR("Could not find stream information"); + goto failed; + } + + if(ffmpeg_open_codec_context(&video_stream_idx, &video_dec_ctx, + fmt_ctx, AVMEDIA_TYPE_VIDEO) + >= 0) { + bool has_alpha = ffmpeg_pix_fmt_has_alpha(video_dec_ctx->pix_fmt); + + /* allocate image where the decoded image will be put */ + header->w = video_dec_ctx->width; + header->h = video_dec_ctx->height; + header->always_zero = 0; + header->cf = (has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR); + + ret = 0; + } + +failed: + avcodec_free_context(&video_dec_ctx); + avformat_close_input(&fmt_ctx); + + return ret; +} + +static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx) +{ + int avg_frame_rate_num = ffmpeg_ctx->video_stream->avg_frame_rate.num; + if(avg_frame_rate_num > 0) { + int period = 1000 * (int64_t)ffmpeg_ctx->video_stream->avg_frame_rate.den + / avg_frame_rate_num; + return period; + } + + return -1; +} + +static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx) +{ + int ret = 0; + + while(1) { + + /* read frames from the file */ + if(av_read_frame(ffmpeg_ctx->fmt_ctx, &(ffmpeg_ctx->pkt)) >= 0) { + bool is_image = false; + + /* check if the packet belongs to a stream we are interested in, + * otherwise skip it + */ + if(ffmpeg_ctx->pkt.stream_index == ffmpeg_ctx->video_stream_idx) { + ret = ffmpeg_decode_packet(ffmpeg_ctx->video_dec_ctx, + &(ffmpeg_ctx->pkt), ffmpeg_ctx); + is_image = true; + } + + av_packet_unref(&(ffmpeg_ctx->pkt)); + + if(ret < 0) { + LV_LOG_WARN("video frame is empty %d", ret); + break; + } + + /* Used to filter data that is not an image */ + if(is_image) { + break; + } + } + else { + ret = -1; + break; + } + } + + return ret; +} + +struct ffmpeg_context_s * ffmpeg_open_file(const char * path) +{ + if(path == NULL || strlen(path) == 0) { + LV_LOG_ERROR("file path is empty"); + return NULL; + } + + struct ffmpeg_context_s * ffmpeg_ctx = calloc(1, sizeof(struct ffmpeg_context_s)); + + if(ffmpeg_ctx == NULL) { + LV_LOG_ERROR("ffmpeg_ctx malloc failed"); + goto failed; + } + + /* open input file, and allocate format context */ + + if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) { + LV_LOG_ERROR("Could not open source file %s", path); + goto failed; + } + + /* retrieve stream information */ + + if(avformat_find_stream_info(ffmpeg_ctx->fmt_ctx, NULL) < 0) { + LV_LOG_ERROR("Could not find stream information"); + goto failed; + } + + if(ffmpeg_open_codec_context( + &(ffmpeg_ctx->video_stream_idx), + &(ffmpeg_ctx->video_dec_ctx), + ffmpeg_ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO) + >= 0) { + ffmpeg_ctx->video_stream = ffmpeg_ctx->fmt_ctx->streams[ffmpeg_ctx->video_stream_idx]; + + ffmpeg_ctx->has_alpha = ffmpeg_pix_fmt_has_alpha(ffmpeg_ctx->video_dec_ctx->pix_fmt); + + ffmpeg_ctx->video_dst_pix_fmt = (ffmpeg_ctx->has_alpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_TRUE_COLOR); + } + +#if LV_FFMPEG_AV_DUMP_FORMAT != 0 + /* dump input information to stderr */ + av_dump_format(ffmpeg_ctx->fmt_ctx, 0, path, 0); +#endif + + if(ffmpeg_ctx->video_stream == NULL) { + LV_LOG_ERROR("Could not find video stream in the input, aborting"); + goto failed; + } + + return ffmpeg_ctx; + +failed: + ffmpeg_close(ffmpeg_ctx); + return NULL; +} + +static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx) +{ + int ret; + + /* allocate image where the decoded image will be put */ + ret = av_image_alloc( + ffmpeg_ctx->video_src_data, + ffmpeg_ctx->video_src_linesize, + ffmpeg_ctx->video_dec_ctx->width, + ffmpeg_ctx->video_dec_ctx->height, + ffmpeg_ctx->video_dec_ctx->pix_fmt, + 4); + + if(ret < 0) { + LV_LOG_ERROR("Could not allocate src raw video buffer"); + return ret; + } + + LV_LOG_INFO("alloc video_src_bufsize = %d", ret); + + ret = av_image_alloc( + ffmpeg_ctx->video_dst_data, + ffmpeg_ctx->video_dst_linesize, + ffmpeg_ctx->video_dec_ctx->width, + ffmpeg_ctx->video_dec_ctx->height, + ffmpeg_ctx->video_dst_pix_fmt, + 4); + + if(ret < 0) { + LV_LOG_ERROR("Could not allocate dst raw video buffer"); + return ret; + } + + LV_LOG_INFO("allocate video_dst_bufsize = %d", ret); + + ffmpeg_ctx->frame = av_frame_alloc(); + + if(ffmpeg_ctx->frame == NULL) { + LV_LOG_ERROR("Could not allocate frame"); + return -1; + } + + /* initialize packet, set data to NULL, let the demuxer fill it */ + av_init_packet(&ffmpeg_ctx->pkt); + ffmpeg_ctx->pkt.data = NULL; + ffmpeg_ctx->pkt.size = 0; + + return 0; +} + +static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx) +{ + avcodec_free_context(&(ffmpeg_ctx->video_dec_ctx)); + avformat_close_input(&(ffmpeg_ctx->fmt_ctx)); + av_frame_free(&(ffmpeg_ctx->frame)); + if(ffmpeg_ctx->video_src_data[0] != NULL) { + av_free(ffmpeg_ctx->video_src_data[0]); + ffmpeg_ctx->video_src_data[0] = NULL; + } +} + +static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx) +{ + if(ffmpeg_ctx->video_dst_data[0] != NULL) { + av_free(ffmpeg_ctx->video_dst_data[0]); + ffmpeg_ctx->video_dst_data[0] = NULL; + } +} + +static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx) +{ + if(ffmpeg_ctx == NULL) { + LV_LOG_WARN("ffmpeg_ctx is NULL"); + return; + } + + sws_freeContext(ffmpeg_ctx->sws_ctx); + ffmpeg_close_src_ctx(ffmpeg_ctx); + ffmpeg_close_dst_ctx(ffmpeg_ctx); + free(ffmpeg_ctx); + + LV_LOG_INFO("ffmpeg_ctx closed"); +} + +static void lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer) +{ + lv_obj_t * obj = (lv_obj_t *)timer->user_data; + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + + if(!player->ffmpeg_ctx) { + return; + } + + int has_next = ffmpeg_update_next_frame(player->ffmpeg_ctx); + + if(has_next < 0) { + lv_ffmpeg_player_set_cmd(obj, player->auto_restart ? LV_FFMPEG_PLAYER_CMD_START : LV_FFMPEG_PLAYER_CMD_STOP); + return; + } + +#if LV_COLOR_DEPTH != 32 + if(player->ffmpeg_ctx->has_alpha) { + convert_color_depth((uint8_t *)(player->imgdsc.data), + player->imgdsc.header.w * player->imgdsc.header.h); + } +#endif + + lv_img_cache_invalidate_src(lv_img_get_src(obj)); + lv_obj_invalidate(obj); +} + +static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p, + lv_obj_t * obj) +{ + LV_TRACE_OBJ_CREATE("begin"); + + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + + player->auto_restart = false; + player->ffmpeg_ctx = NULL; + player->timer = lv_timer_create(lv_ffmpeg_player_frame_update_cb, + FRAME_DEF_REFR_PERIOD, obj); + lv_timer_pause(player->timer); + + LV_TRACE_OBJ_CREATE("finished"); +} + +static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p, + lv_obj_t * obj) +{ + LV_TRACE_OBJ_CREATE("begin"); + + lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj; + + if(player->timer) { + lv_timer_del(player->timer); + player->timer = NULL; + } + + lv_img_cache_invalidate_src(lv_img_get_src(obj)); + + ffmpeg_close(player->ffmpeg_ctx); + player->ffmpeg_ctx = NULL; + + LV_TRACE_OBJ_CREATE("finished"); +} + +#endif /*LV_USE_FFMPEG*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.h new file mode 100644 index 000000000..8c7fc26b1 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/ffmpeg/lv_ffmpeg.h @@ -0,0 +1,104 @@ +/** + * @file lv_ffmpeg.h + * + */ +#ifndef LV_FFMPEG_H +#define LV_FFMPEG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_FFMPEG != 0 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +struct ffmpeg_context_s; + +extern const lv_obj_class_t lv_ffmpeg_player_class; + +typedef struct { + lv_img_t img; + lv_timer_t * timer; + lv_img_dsc_t imgdsc; + bool auto_restart; + struct ffmpeg_context_s * ffmpeg_ctx; +} lv_ffmpeg_player_t; + +typedef enum { + LV_FFMPEG_PLAYER_CMD_START, + LV_FFMPEG_PLAYER_CMD_STOP, + LV_FFMPEG_PLAYER_CMD_PAUSE, + LV_FFMPEG_PLAYER_CMD_RESUME, + _LV_FFMPEG_PLAYER_CMD_LAST +} lv_ffmpeg_player_cmd_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register FFMPEG image decoder + */ +void lv_ffmpeg_init(void); + +/** + * Get the number of frames contained in the file + * @param path image or video file name + * @return Number of frames, less than 0 means failed + */ +int lv_ffmpeg_get_frame_num(const char * path); + +/** + * Create ffmpeg_player object + * @param parent pointer to an object, it will be the parent of the new player + * @return pointer to the created ffmpeg_player + */ +lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent); + +/** + * Set the path of the file to be played + * @param obj pointer to a ffmpeg_player object + * @param path video file path + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info. + */ +lv_res_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path); + +/** + * Set command control video player + * @param obj pointer to a ffmpeg_player object + * @param cmd control commands + */ +void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd); + +/** + * Set the video to automatically replay + * @param obj pointer to a ffmpeg_player object + * @param en true: enable the auto restart + */ +void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_FFMPEG*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_FFMPEG_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c index 73596ece7..cc1d2e6d6 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c @@ -161,8 +161,8 @@ static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_ * @param drv pointer to a driver where this function belongs * @param file_p pointer to a FIL variable * @param buf pointer to a buffer with the bytes to write - * @param btr Bytes To Write - * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_posix.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_posix.c index fb5fcd306..f988daeeb 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_posix.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_posix.c @@ -110,7 +110,7 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; - sprintf(buf, LV_FS_POSIX_PATH "%s", path); + lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path); int f = open(buf, flags); if(f < 0) return NULL; @@ -154,8 +154,8 @@ static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_ * @param drv pointer to a driver where this function belongs * @param file_p a file handle variable * @param buf pointer to a buffer with the bytes to write - * @param btr Bytes To Write - * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) @@ -176,8 +176,8 @@ static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) { LV_UNUSED(drv); - lseek((lv_uintptr_t)file_p, pos, whence); - return LV_FS_RES_OK; + off_t offset = lseek((lv_uintptr_t)file_p, pos, whence); + return offset < 0 ? LV_FS_RES_FS_ERR : LV_FS_RES_OK; } /** @@ -191,8 +191,9 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) { LV_UNUSED(drv); - *pos_p = lseek((lv_uintptr_t)file_p, 0, SEEK_CUR); - return LV_FS_RES_OK; + off_t offset = lseek((lv_uintptr_t)file_p, 0, SEEK_CUR); + *pos_p = offset; + return offset < 0 ? LV_FS_RES_FS_ERR : LV_FS_RES_OK; } #ifdef WIN32 @@ -212,7 +213,7 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) #ifndef WIN32 /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; - sprintf(buf, LV_FS_POSIX_PATH "%s", path); + lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s", path); return opendir(buf); #else HANDLE d = INVALID_HANDLE_VALUE; @@ -220,7 +221,7 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; - sprintf(buf, LV_FS_POSIX_PATH "%s\\*", path); + lv_snprintf(buf, sizeof(buf), LV_FS_POSIX_PATH "%s\\*", path); strcpy(next_fn, ""); d = FindFirstFile(buf, &fdata); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c index e4bf07783..c2de6880e 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_stdio.c @@ -21,10 +21,19 @@ /********************* * DEFINES *********************/ +#define MAX_PATH_LEN 256 /********************** * TYPEDEFS **********************/ +typedef struct { +#ifdef WIN32 + HANDLE dir_p; + char next_fn[MAX_PATH_LEN]; +#else + DIR * dir_p; +#endif +} dir_handle_t; /********************** * STATIC PROTOTYPES @@ -105,8 +114,8 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) /*Make the path relative to the current directory (the projects root folder)*/ - char buf[256]; - sprintf(buf, LV_FS_STDIO_PATH "%s", path); + char buf[MAX_PATH_LEN]; + lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path); return fopen(buf, flags); } @@ -147,8 +156,8 @@ static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_ * @param drv pointer to a driver where this function belongs * @param file_p pointer to a FILE variable * @param buf pointer to a buffer with the bytes to write - * @param btr Bytes To Write - * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) @@ -188,10 +197,6 @@ static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) return LV_FS_RES_OK; } -#ifdef WIN32 - static char next_fn[256]; -#endif - /** * Initialize a 'DIR' or 'HANDLE' variable for directory reading * @param drv pointer to a driver where this function belongs @@ -201,37 +206,47 @@ static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) { LV_UNUSED(drv); + dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t)); #ifndef WIN32 /*Make the path relative to the current directory (the projects root folder)*/ - char buf[256]; - sprintf(buf, LV_FS_STDIO_PATH "%s", path); - return opendir(buf); + char buf[MAX_PATH_LEN]; + lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s", path); + handle->dir_p = opendir(buf); + if(handle->dir_p == NULL) { + lv_mem_free(handle); + return NULL; + } + return handle; #else - HANDLE d = INVALID_HANDLE_VALUE; - WIN32_FIND_DATA fdata; + handle->dir_p = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAA fdata; /*Make the path relative to the current directory (the projects root folder)*/ - char buf[256]; - sprintf(buf, LV_FS_STDIO_PATH "%s\\*", path); + char buf[MAX_PATH_LEN]; + lv_snprintf(buf, sizeof(buf), LV_FS_STDIO_PATH "%s\\*", path); - strcpy(next_fn, ""); - d = FindFirstFile(buf, &fdata); + strcpy(handle->next_fn, ""); + handle->dir_p = FindFirstFileA(buf, &fdata); do { if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { continue; } else { if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - sprintf(next_fn, "/%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName); } else { - sprintf(next_fn, "%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName); } break; } - } while(FindNextFileA(d, &fdata)); + } while(FindNextFileA(handle->dir_p, &fdata)); - return d; + if(handle->dir_p == INVALID_HANDLE_VALUE) { + lv_mem_free(handle); + return INVALID_HANDLE_VALUE; + } + return handle; #endif } @@ -246,13 +261,13 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) { LV_UNUSED(drv); - + dir_handle_t * handle = (dir_handle_t *)dir_p; #ifndef WIN32 struct dirent * entry; do { - entry = readdir(dir_p); + entry = readdir(handle->dir_p); if(entry) { - if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name); + if(entry->d_type == DT_DIR) lv_snprintf(fn, MAX_PATH_LEN, "/%s", entry->d_name); else strcpy(fn, entry->d_name); } else { @@ -260,26 +275,26 @@ static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) } } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); #else - strcpy(fn, next_fn); + strcpy(fn, handle->next_fn); - strcpy(next_fn, ""); - WIN32_FIND_DATA fdata; + strcpy(handle->next_fn, ""); + WIN32_FIND_DATAA fdata; - if(FindNextFile(dir_p, &fdata) == false) return LV_FS_RES_OK; + if(FindNextFileA(handle->dir_p, &fdata) == false) return LV_FS_RES_OK; do { if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { continue; } else { if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - sprintf(next_fn, "/%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName); } else { - sprintf(next_fn, "%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName); } break; } - } while(FindNextFile(dir_p, &fdata)); + } while(FindNextFileA(handle->dir_p, &fdata)); #endif return LV_FS_RES_OK; @@ -294,11 +309,13 @@ static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) { LV_UNUSED(drv); + dir_handle_t * handle = (dir_handle_t *)dir_p; #ifndef WIN32 - closedir(dir_p); + closedir(handle->dir_p); #else - FindClose(dir_p); + FindClose(handle->dir_p); #endif + lv_mem_free(handle); return LV_FS_RES_OK; } diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_win32.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_win32.c index ad4536d40..1a59aa496 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_win32.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/fsdrv/lv_fs_win32.c @@ -16,10 +16,16 @@ /********************* * DEFINES *********************/ +#define MAX_PATH_LEN 256 /********************** * TYPEDEFS **********************/ +typedef struct { + HANDLE dir_p; + char next_fn[MAX_PATH_LEN]; + lv_fs_res_t next_error; +} dir_handle_t; /********************** * STATIC PROTOTYPES @@ -58,7 +64,7 @@ void lv_fs_win32_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ + /*Add a simple driver to open images*/ static lv_fs_drv_t fs_drv; /*A driver descriptor*/ lv_fs_drv_init(&fs_drv); @@ -213,7 +219,7 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) /*Make the path relative to the current directory (the projects root folder)*/ char buf[MAX_PATH]; - sprintf(buf, LV_FS_WIN32_PATH "%s", path); + lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s", path); return (void *)CreateFileA( buf, @@ -263,8 +269,8 @@ static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_ * @param drv pointer to a driver where this function belongs * @param file_p pointer to a FILE variable * @param buf pointer to a buffer with the bytes to write - * @param btr Bytes To Write - * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) @@ -344,9 +350,6 @@ static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) } } -static char next_fn[256]; -static lv_fs_res_t next_error = LV_FS_RES_OK; - /** * Initialize a 'DIR' or 'HANDLE' variable for directory reading * @param drv pointer to a driver where this function belongs @@ -356,38 +359,45 @@ static lv_fs_res_t next_error = LV_FS_RES_OK; static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) { LV_UNUSED(drv); - - HANDLE d = INVALID_HANDLE_VALUE; + dir_handle_t * handle = (dir_handle_t *)lv_mem_alloc(sizeof(dir_handle_t)); + handle->dir_p = INVALID_HANDLE_VALUE; + handle->next_error = LV_FS_RES_OK; WIN32_FIND_DATAA fdata; /*Make the path relative to the current directory (the projects root folder)*/ - char buf[256]; + char buf[MAX_PATH_LEN]; #ifdef LV_FS_WIN32_PATH - sprintf(buf, LV_FS_WIN32_PATH "%s\\*", path); + lv_snprintf(buf, sizeof(buf), LV_FS_WIN32_PATH "%s\\*", path); #else - sprintf(buf, "%s\\*", path); + lv_snprintf(buf, sizeof(buf), "%s\\*", path); #endif - strcpy(next_fn, ""); - d = FindFirstFileA(buf, &fdata); + strcpy(handle->next_fn, ""); + handle->dir_p = FindFirstFileA(buf, &fdata); do { if(is_dots_name(fdata.cFileName)) { continue; } else { if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - sprintf(next_fn, "/%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName); } else { - sprintf(next_fn, "%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName); } break; } + } while(FindNextFileA(handle->dir_p, &fdata)); - } while(FindNextFileA(d, &fdata)); - - next_error = fs_error_from_win32(GetLastError()); - return d; + if(handle->dir_p == INVALID_HANDLE_VALUE) { + lv_mem_free(handle); + handle->next_error = fs_error_from_win32(GetLastError()); + return INVALID_HANDLE_VALUE; + } + else { + handle->next_error = LV_FS_RES_OK; + return handle; + } } /** @@ -401,31 +411,30 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) { LV_UNUSED(drv); + dir_handle_t * handle = (dir_handle_t *)dir_p; + strcpy(fn, handle->next_fn); + lv_fs_res_t current_error = handle->next_error; + strcpy(handle->next_fn, ""); - strcpy(fn, next_fn); - lv_fs_res_t current_error = next_error; - next_error = LV_FS_RES_OK; - - strcpy(next_fn, ""); WIN32_FIND_DATAA fdata; - while(FindNextFileA(dir_p, &fdata)) { + while(FindNextFileA(handle->dir_p, &fdata)) { if(is_dots_name(fdata.cFileName)) { continue; } else { if(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - sprintf(next_fn, "/%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "/%s", fdata.cFileName); } else { - sprintf(next_fn, "%s", fdata.cFileName); + lv_snprintf(handle->next_fn, sizeof(handle->next_fn), "%s", fdata.cFileName); } break; } } - if(next_fn[0] == '\0') { - next_error = fs_error_from_win32(GetLastError()); + if(handle->next_fn[0] == '\0') { + handle->next_error = fs_error_from_win32(GetLastError()); } return current_error; @@ -440,9 +449,12 @@ static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) { LV_UNUSED(drv); - return FindClose((HANDLE)dir_p) - ? LV_FS_RES_OK - : fs_error_from_win32(GetLastError()); + dir_handle_t * handle = (dir_handle_t *)dir_p; + lv_fs_res_t res = FindClose(handle->dir_p) + ? LV_FS_RES_OK + : fs_error_from_win32(GetLastError()); + lv_mem_free(handle); + return res; } #else /*LV_USE_FS_WIN32 == 0*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.c new file mode 100644 index 000000000..68f50057e --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.c @@ -0,0 +1,659 @@ +#include "gifdec.h" +#include "../../../misc/lv_log.h" +#include "../../../misc/lv_mem.h" +#include "../../../misc/lv_color.h" +#if LV_USE_GIF + +#include +#include +#include + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +typedef struct Entry { + uint16_t length; + uint16_t prefix; + uint8_t suffix; +} Entry; + +typedef struct Table { + int bulk; + int nentries; + Entry *entries; +} Table; + +static gd_GIF * gif_open(gd_GIF * gif); +static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file); +static void f_gif_read(gd_GIF * gif, void * buf, size_t len); +static int f_gif_seek(gd_GIF * gif, size_t pos, int k); +static void f_gif_close(gd_GIF * gif); + +static uint16_t +read_num(gd_GIF * gif) +{ + uint8_t bytes[2]; + + f_gif_read(gif, bytes, 2); + return bytes[0] + (((uint16_t) bytes[1]) << 8); +} + + + +gd_GIF * +gd_open_gif_file(const char *fname) +{ + gd_GIF gif_base; + memset(&gif_base, 0, sizeof(gif_base)); + + bool res = f_gif_open(&gif_base, fname, true); + if(!res) return NULL; + + return gif_open(&gif_base); +} + + +gd_GIF * +gd_open_gif_data(const void *data) +{ + gd_GIF gif_base; + memset(&gif_base, 0, sizeof(gif_base)); + + bool res = f_gif_open(&gif_base, data, false); + if(!res) return NULL; + + return gif_open(&gif_base); +} + +static gd_GIF * gif_open(gd_GIF * gif_base) +{ + uint8_t sigver[3]; + uint16_t width, height, depth; + uint8_t fdsz, bgidx, aspect; + int i; + uint8_t *bgcolor; + int gct_sz; + gd_GIF *gif = NULL; + + /* Header */ + f_gif_read(gif_base, sigver, 3); + if (memcmp(sigver, "GIF", 3) != 0) { + LV_LOG_WARN("invalid signature\n"); + goto fail; + } + /* Version */ + f_gif_read(gif_base, sigver, 3); + if (memcmp(sigver, "89a", 3) != 0) { + LV_LOG_WARN("invalid version\n"); + goto fail; + } + /* Width x Height */ + width = read_num(gif_base); + height = read_num(gif_base); + /* FDSZ */ + f_gif_read(gif_base, &fdsz, 1); + /* Presence of GCT */ + if (!(fdsz & 0x80)) { + LV_LOG_WARN("no global color table\n"); + goto fail; + } + /* Color Space's Depth */ + depth = ((fdsz >> 4) & 7) + 1; + /* Ignore Sort Flag. */ + /* GCT Size */ + gct_sz = 1 << ((fdsz & 0x07) + 1); + /* Background Color Index */ + f_gif_read(gif_base, &bgidx, 1); + /* Aspect Ratio */ + f_gif_read(gif_base, &aspect, 1); + /* Create gd_GIF Structure. */ +#if LV_COLOR_DEPTH == 32 + gif = lv_mem_alloc(sizeof(gd_GIF) + 5 * width * height); +#elif LV_COLOR_DEPTH == 16 + gif = lv_mem_alloc(sizeof(gd_GIF) + 4 * width * height); +#elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + gif = lv_mem_alloc(sizeof(gd_GIF) + 3 * width * height); +#endif + + if (!gif) goto fail; + memcpy(gif, gif_base, sizeof(gd_GIF)); + gif->width = width; + gif->height = height; + gif->depth = depth; + /* Read GCT */ + gif->gct.size = gct_sz; + f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size); + gif->palette = &gif->gct; + gif->bgindex = bgidx; + gif->canvas = (uint8_t *) &gif[1]; +#if LV_COLOR_DEPTH == 32 + gif->frame = &gif->canvas[4 * width * height]; +#elif LV_COLOR_DEPTH == 16 + gif->frame = &gif->canvas[3 * width * height]; +#elif LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + gif->frame = &gif->canvas[2 * width * height]; +#endif + if (gif->bgindex) { + memset(gif->frame, gif->bgindex, gif->width * gif->height); + } + bgcolor = &gif->palette->colors[gif->bgindex*3]; + + for (i = 0; i < gif->width * gif->height; i++) { +#if LV_COLOR_DEPTH == 32 + gif->canvas[i*4 + 0] = *(bgcolor + 2); + gif->canvas[i*4 + 1] = *(bgcolor + 1); + gif->canvas[i*4 + 2] = *(bgcolor + 0); + gif->canvas[i*4 + 3] = 0xff; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[i*3 + 0] = c.full & 0xff; + gif->canvas[i*3 + 1] = (c.full >> 8) & 0xff; + gif->canvas[i*3 + 2] = 0xff; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[i*2 + 0] = c.full; + gif->canvas[i*2 + 1] = 0xff; +#elif LV_COLOR_DEPTH == 1 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[i*2 + 0] = c.ch.red > 128 ? 1 : 0; + gif->canvas[i*2 + 1] = 0xff; +#endif + } + gif->anim_start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + goto ok; +fail: + f_gif_close(gif_base); +ok: + return gif; +} + +static void +discard_sub_blocks(gd_GIF *gif) +{ + uint8_t size; + + do { + f_gif_read(gif, &size, 1); + f_gif_seek(gif, size, LV_FS_SEEK_CUR); + } while (size); +} + +static void +read_plain_text_ext(gd_GIF *gif) +{ + if (gif->plain_text) { + uint16_t tx, ty, tw, th; + uint8_t cw, ch, fg, bg; + size_t sub_block; + f_gif_seek(gif, 1, LV_FS_SEEK_CUR); /* block size = 12 */ + tx = read_num(gif); + ty = read_num(gif); + tw = read_num(gif); + th = read_num(gif); + f_gif_read(gif, &cw, 1); + f_gif_read(gif, &ch, 1); + f_gif_read(gif, &fg, 1); + f_gif_read(gif, &bg, 1); + sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg); + f_gif_seek(gif, sub_block, LV_FS_SEEK_SET); + } else { + /* Discard plain text metadata. */ + f_gif_seek(gif, 13, LV_FS_SEEK_CUR); + } + /* Discard plain text sub-blocks. */ + discard_sub_blocks(gif); +} + +static void +read_graphic_control_ext(gd_GIF *gif) +{ + uint8_t rdit; + + /* Discard block size (always 0x04). */ + f_gif_seek(gif, 1, LV_FS_SEEK_CUR); + f_gif_read(gif, &rdit, 1); + gif->gce.disposal = (rdit >> 2) & 3; + gif->gce.input = rdit & 2; + gif->gce.transparency = rdit & 1; + gif->gce.delay = read_num(gif); + f_gif_read(gif, &gif->gce.tindex, 1); + /* Skip block terminator. */ + f_gif_seek(gif, 1, LV_FS_SEEK_CUR); +} + +static void +read_comment_ext(gd_GIF *gif) +{ + if (gif->comment) { + size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + gif->comment(gif); + f_gif_seek(gif, sub_block, LV_FS_SEEK_SET); + } + /* Discard comment sub-blocks. */ + discard_sub_blocks(gif); +} + +static void +read_application_ext(gd_GIF *gif) +{ + char app_id[8]; + char app_auth_code[3]; + + /* Discard block size (always 0x0B). */ + f_gif_seek(gif, 1, LV_FS_SEEK_CUR); + /* Application Identifier. */ + f_gif_read(gif, app_id, 8); + /* Application Authentication Code. */ + f_gif_read(gif, app_auth_code, 3); + if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) { + /* Discard block size (0x03) and constant byte (0x01). */ + f_gif_seek(gif, 2, LV_FS_SEEK_CUR); + gif->loop_count = read_num(gif); + /* Skip block terminator. */ + f_gif_seek(gif, 1, LV_FS_SEEK_CUR); + } else if (gif->application) { + size_t sub_block = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + gif->application(gif, app_id, app_auth_code); + f_gif_seek(gif, sub_block, LV_FS_SEEK_SET); + discard_sub_blocks(gif); + } else { + discard_sub_blocks(gif); + } +} + +static void +read_ext(gd_GIF *gif) +{ + uint8_t label; + + f_gif_read(gif, &label, 1); + switch (label) { + case 0x01: + read_plain_text_ext(gif); + break; + case 0xF9: + read_graphic_control_ext(gif); + break; + case 0xFE: + read_comment_ext(gif); + break; + case 0xFF: + read_application_ext(gif); + break; + default: + LV_LOG_WARN("unknown extension: %02X\n", label); + } +} + +static Table * +new_table(int key_size) +{ + int key; + int init_bulk = MAX(1 << (key_size + 1), 0x100); + Table *table = lv_mem_alloc(sizeof(*table) + sizeof(Entry) * init_bulk); + if (table) { + table->bulk = init_bulk; + table->nentries = (1 << key_size) + 2; + table->entries = (Entry *) &table[1]; + for (key = 0; key < (1 << key_size); key++) + table->entries[key] = (Entry) {1, 0xFFF, key}; + } + return table; +} + +/* Add table entry. Return value: + * 0 on success + * +1 if key size must be incremented after this addition + * -1 if could not realloc table */ +static int +add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix) +{ + Table *table = *tablep; + if (table->nentries == table->bulk) { + table->bulk *= 2; + table = lv_mem_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk); + if (!table) return -1; + table->entries = (Entry *) &table[1]; + *tablep = table; + } + table->entries[table->nentries] = (Entry) {length, prefix, suffix}; + table->nentries++; + if ((table->nentries & (table->nentries - 1)) == 0) + return 1; + return 0; +} + +static uint16_t +get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte) +{ + int bits_read; + int rpad; + int frag_size; + uint16_t key; + + key = 0; + for (bits_read = 0; bits_read < key_size; bits_read += frag_size) { + rpad = (*shift + bits_read) % 8; + if (rpad == 0) { + /* Update byte. */ + if (*sub_len == 0) { + f_gif_read(gif, sub_len, 1); /* Must be nonzero! */ + if (*sub_len == 0) return 0x1000; + } + f_gif_read(gif, byte, 1); + (*sub_len)--; + } + frag_size = MIN(key_size - bits_read, 8 - rpad); + key |= ((uint16_t) ((*byte) >> rpad)) << bits_read; + } + /* Clear extra bits to the left. */ + key &= (1 << key_size) - 1; + *shift = (*shift + key_size) % 8; + return key; +} + +/* Compute output index of y-th input line, in frame of height h. */ +static int +interlaced_line_index(int h, int y) +{ + int p; /* number of lines in current pass */ + + p = (h - 1) / 8 + 1; + if (y < p) /* pass 1 */ + return y * 8; + y -= p; + p = (h - 5) / 8 + 1; + if (y < p) /* pass 2 */ + return y * 8 + 4; + y -= p; + p = (h - 3) / 4 + 1; + if (y < p) /* pass 3 */ + return y * 4 + 2; + y -= p; + /* pass 4 */ + return y * 2 + 1; +} + +/* Decompress image pixels. + * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */ +static int +read_image_data(gd_GIF *gif, int interlace) +{ + uint8_t sub_len, shift, byte; + int init_key_size, key_size, table_is_full=0; + int frm_off, frm_size, str_len=0, i, p, x, y; + uint16_t key, clear, stop; + int ret; + Table *table; + Entry entry = {0}; + size_t start, end; + + f_gif_read(gif, &byte, 1); + key_size = (int) byte; + start = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + discard_sub_blocks(gif); + end = f_gif_seek(gif, 0, LV_FS_SEEK_CUR); + f_gif_seek(gif, start, LV_FS_SEEK_SET); + clear = 1 << key_size; + stop = clear + 1; + table = new_table(key_size); + key_size++; + init_key_size = key_size; + sub_len = shift = 0; + key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */ + frm_off = 0; + ret = 0; + frm_size = gif->fw*gif->fh; + while (frm_off < frm_size) { + if (key == clear) { + key_size = init_key_size; + table->nentries = (1 << (key_size - 1)) + 2; + table_is_full = 0; + } else if (!table_is_full) { + ret = add_entry(&table, str_len + 1, key, entry.suffix); + if (ret == -1) { + lv_mem_free(table); + return -1; + } + if (table->nentries == 0x1000) { + ret = 0; + table_is_full = 1; + } + } + key = get_key(gif, key_size, &sub_len, &shift, &byte); + if (key == clear) continue; + if (key == stop || key == 0x1000) break; + if (ret == 1) key_size++; + entry = table->entries[key]; + str_len = entry.length; + for (i = 0; i < str_len; i++) { + p = frm_off + entry.length - 1; + x = p % gif->fw; + y = p / gif->fw; + if (interlace) + y = interlaced_line_index((int) gif->fh, y); + gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix; + if (entry.prefix == 0xFFF) + break; + else + entry = table->entries[entry.prefix]; + } + frm_off += str_len; + if (key < table->nentries - 1 && !table_is_full) + table->entries[table->nentries - 1].suffix = entry.suffix; + } + lv_mem_free(table); + if (key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */ + f_gif_seek(gif, end, LV_FS_SEEK_SET); + return 0; +} + +/* Read image. + * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */ +static int +read_image(gd_GIF *gif) +{ + uint8_t fisrz; + int interlace; + + /* Image Descriptor. */ + gif->fx = read_num(gif); + gif->fy = read_num(gif); + gif->fw = read_num(gif); + gif->fh = read_num(gif); + f_gif_read(gif, &fisrz, 1); + interlace = fisrz & 0x40; + /* Ignore Sort Flag. */ + /* Local Color Table? */ + if (fisrz & 0x80) { + /* Read LCT */ + gif->lct.size = 1 << ((fisrz & 0x07) + 1); + f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size); + gif->palette = &gif->lct; + } else + gif->palette = &gif->gct; + /* Image Data. */ + return read_image_data(gif, interlace); +} + +static void +render_frame_rect(gd_GIF *gif, uint8_t *buffer) +{ + int i, j, k; + uint8_t index, *color; + i = gif->fy * gif->width + gif->fx; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { + index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k]; + color = &gif->palette->colors[index*3]; + if (!gif->gce.transparency || index != gif->gce.tindex) { +#if LV_COLOR_DEPTH == 32 + buffer[(i+k)*4 + 0] = *(color + 2); + buffer[(i+k)*4 + 1] = *(color + 1); + buffer[(i+k)*4 + 2] = *(color + 0); + buffer[(i+k)*4 + 3] = 0xFF; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2)); + buffer[(i+k)*3 + 0] = c.full & 0xff; + buffer[(i+k)*3 + 1] = (c.full >> 8) & 0xff; + buffer[(i+k)*3 + 2] = 0xff; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2)); + buffer[(i+k)*2 + 0] = c.full; + buffer[(i+k)*2 + 1] = 0xff; +#elif LV_COLOR_DEPTH == 1 + uint8_t b = (*(color + 0)) | (*(color + 1)) | (*(color + 2)); + buffer[(i+k)*2 + 0] = b > 128 ? 1 : 0; + buffer[(i+k)*2 + 1] = 0xff; +#endif + } + } + i += gif->width; + } +} + +static void +dispose(gd_GIF *gif) +{ + int i, j, k; + uint8_t *bgcolor; + switch (gif->gce.disposal) { + case 2: /* Restore to background color. */ + bgcolor = &gif->palette->colors[gif->bgindex*3]; + + uint8_t opa = 0xff; + if(gif->gce.transparency) opa = 0x00; + + i = gif->fy * gif->width + gif->fx; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { +#if LV_COLOR_DEPTH == 32 + gif->canvas[(i+k)*4 + 0] = *(bgcolor + 2); + gif->canvas[(i+k)*4 + 1] = *(bgcolor + 1); + gif->canvas[(i+k)*4 + 2] = *(bgcolor + 0); + gif->canvas[(i+k)*4 + 3] = opa; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[(i+k)*3 + 0] = c.full & 0xff; + gif->canvas[(i+k)*3 + 1] = (c.full >> 8) & 0xff; + gif->canvas[(i+k)*3 + 2] = opa; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[(i+k)*2 + 0] = c.full; + gif->canvas[(i+k)*2 + 1] = opa; +#elif LV_COLOR_DEPTH == 1 + uint8_t b = (*(bgcolor + 0)) | (*(bgcolor + 1)) | (*(bgcolor + 2)); + gif->canvas[(i+k)*2 + 0] = b > 128 ? 1 : 0; + gif->canvas[(i+k)*2 + 1] = opa; +#endif + } + i += gif->width; + } + break; + case 3: /* Restore to previous, i.e., don't update canvas.*/ + break; + default: + /* Add frame non-transparent pixels to canvas. */ + render_frame_rect(gif, gif->canvas); + } +} + +/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */ +int +gd_get_frame(gd_GIF *gif) +{ + char sep; + + dispose(gif); + f_gif_read(gif, &sep, 1); + while (sep != ',') { + if (sep == ';') + return 0; + if (sep == '!') + read_ext(gif); + else return -1; + f_gif_read(gif, &sep, 1); + } + if (read_image(gif) == -1) + return -1; + return 1; +} + +void +gd_render_frame(gd_GIF *gif, uint8_t *buffer) +{ +// uint32_t i; +// uint32_t j; +// for(i = 0, j = 0; i < gif->width * gif->height * 3; i+= 3, j+=4) { +// buffer[j + 0] = gif->canvas[i + 2]; +// buffer[j + 1] = gif->canvas[i + 1]; +// buffer[j + 2] = gif->canvas[i + 0]; +// buffer[j + 3] = 0xFF; +// } +// memcpy(buffer, gif->canvas, gif->width * gif->height * 3); + render_frame_rect(gif, buffer); +} + +void +gd_rewind(gd_GIF *gif) +{ + f_gif_seek(gif, gif->anim_start, LV_FS_SEEK_SET); +} + +void +gd_close_gif(gd_GIF *gif) +{ + f_gif_close(gif); + lv_mem_free(gif); +} + +static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file) +{ + gif->f_rw_p = 0; + gif->data = NULL; + gif->is_file = is_file; + + if(is_file) { + lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return false; + else return true; + } else { + gif->data = path; + return true; + } +} + +static void f_gif_read(gd_GIF * gif, void * buf, size_t len) +{ + if(gif->is_file) { + lv_fs_read(&gif->fd, buf, len, NULL); + } else + { + memcpy(buf, &gif->data[gif->f_rw_p], len); + gif->f_rw_p += len; + } +} + +static int f_gif_seek(gd_GIF * gif, size_t pos, int k) +{ + if(gif->is_file) { + lv_fs_seek(&gif->fd, pos, k); + uint32_t x; + lv_fs_tell(&gif->fd, &x); + return x; + } else { + if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos; + else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos; + return gif->f_rw_p; + } +} + +static void f_gif_close(gd_GIF * gif) +{ + if(gif->is_file) { + lv_fs_close(&gif->fd); + } +} + +#endif /*LV_USE_GIF*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.h new file mode 100644 index 000000000..00f17c1da --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/gifdec.h @@ -0,0 +1,60 @@ +#ifndef GIFDEC_H +#define GIFDEC_H + +#include +#include "../../../misc/lv_fs.h" + +#if LV_USE_GIF + +typedef struct gd_Palette { + int size; + uint8_t colors[0x100 * 3]; +} gd_Palette; + +typedef struct gd_GCE { + uint16_t delay; + uint8_t tindex; + uint8_t disposal; + int input; + int transparency; +} gd_GCE; + + + +typedef struct gd_GIF { + lv_fs_file_t fd; + const char * data; + uint8_t is_file; + uint32_t f_rw_p; + int32_t anim_start; + uint16_t width, height; + uint16_t depth; + uint16_t loop_count; + gd_GCE gce; + gd_Palette *palette; + gd_Palette lct, gct; + void (*plain_text)( + struct gd_GIF *gif, uint16_t tx, uint16_t ty, + uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch, + uint8_t fg, uint8_t bg + ); + void (*comment)(struct gd_GIF *gif); + void (*application)(struct gd_GIF *gif, char id[8], char auth[3]); + uint16_t fx, fy, fw, fh; + uint8_t bgindex; + uint8_t *canvas, *frame; +} gd_GIF; + +gd_GIF * gd_open_gif_file(const char *fname); + +gd_GIF * gd_open_gif_data(const void *data); + +void gd_render_frame(gd_GIF *gif, uint8_t *buffer); + +int gd_get_frame(gd_GIF *gif); +void gd_rewind(gd_GIF *gif); +void gd_close_gif(gd_GIF *gif); + +#endif /*LV_USE_GIF*/ + +#endif /* GIFDEC_H */ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.c new file mode 100644 index 000000000..4cb2955e2 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.c @@ -0,0 +1,155 @@ +/** + * @file lv_gifenc.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_gif.h" +#if LV_USE_GIF + +#include "gifdec.h" + +/********************* + * DEFINES + *********************/ +#define MY_CLASS &lv_gif_class + +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void next_frame_task_cb(lv_timer_t * t); + +/********************** + * STATIC VARIABLES + **********************/ +const lv_obj_class_t lv_gif_class = { + .constructor_cb = lv_gif_constructor, + .destructor_cb = lv_gif_destructor, + .instance_size = sizeof(lv_gif_t), + .base_class = &lv_img_class +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_obj_t * lv_gif_create(lv_obj_t * parent) +{ + + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + +void lv_gif_set_src(lv_obj_t * obj, const void * src) +{ + lv_gif_t * gifobj = (lv_gif_t *) obj; + + /*Close previous gif if any*/ + if(gifobj->gif) { + lv_img_cache_invalidate_src(&gifobj->imgdsc); + gd_close_gif(gifobj->gif); + gifobj->gif = NULL; + gifobj->imgdsc.data = NULL; + } + + if(lv_img_src_get_type(src) == LV_IMG_SRC_VARIABLE) { + const lv_img_dsc_t * img_dsc = src; + gifobj->gif = gd_open_gif_data(img_dsc->data); + } + else if(lv_img_src_get_type(src) == LV_IMG_SRC_FILE) { + gifobj->gif = gd_open_gif_file(src); + } + if(gifobj->gif == NULL) { + LV_LOG_WARN("Could't load the source"); + return; + } + + gifobj->imgdsc.data = gifobj->gif->canvas; + gifobj->imgdsc.header.always_zero = 0; + gifobj->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + gifobj->imgdsc.header.h = gifobj->gif->height; + gifobj->imgdsc.header.w = gifobj->gif->width; + gifobj->last_call = lv_tick_get(); + + lv_img_set_src(obj, &gifobj->imgdsc); + + lv_timer_resume(gifobj->timer); + lv_timer_reset(gifobj->timer); + + next_frame_task_cb(gifobj->timer); + +} + +void lv_gif_restart(lv_obj_t * obj) +{ + lv_gif_t * gifobj = (lv_gif_t *) obj; + gd_rewind(gifobj->gif); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + + lv_gif_t * gifobj = (lv_gif_t *) obj; + + gifobj->timer = lv_timer_create(next_frame_task_cb, 10, obj); + lv_timer_pause(gifobj->timer); +} + +static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_gif_t * gifobj = (lv_gif_t *) obj; + lv_img_cache_invalidate_src(&gifobj->imgdsc); + gd_close_gif(gifobj->gif); + lv_timer_del(gifobj->timer); +} + +static void next_frame_task_cb(lv_timer_t * t) +{ + lv_obj_t * obj = t->user_data; + lv_gif_t * gifobj = (lv_gif_t *) obj; + uint32_t elaps = lv_tick_elaps(gifobj->last_call); + if(elaps < gifobj->gif->gce.delay * 10) return; + + gifobj->last_call = lv_tick_get(); + + int has_next = gd_get_frame(gifobj->gif); + if(has_next == 0) { + /*It was the last repeat*/ + if(gifobj->gif->loop_count == 1) { + lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL); + if(res != LV_FS_RES_OK) return; + } + else { + if(gifobj->gif->loop_count > 1) gifobj->gif->loop_count--; + gd_rewind(gifobj->gif); + } + } + + gd_render_frame(gifobj->gif, (uint8_t *)gifobj->imgdsc.data); + + lv_img_cache_invalidate_src(lv_img_get_src(obj)); + lv_obj_invalidate(obj); +} + +#endif /*LV_USE_GIF*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.h new file mode 100644 index 000000000..d8c93dbc0 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/gif/lv_gif.h @@ -0,0 +1,58 @@ +/** + * @file lv_gif.h + * + */ + +#ifndef LV_GIF_H +#define LV_GIF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lvgl.h" +#if LV_USE_GIF + +#include "gifdec.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_img_t img; + gd_GIF * gif; + lv_timer_t * timer; + lv_img_dsc_t imgdsc; + uint32_t last_call; +} lv_gif_t; + +extern const lv_obj_class_t lv_gif_class; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_obj_t * lv_gif_create(lv_obj_t * parent); +void lv_gif_set_src(lv_obj_t * obj, const void * src); +void lv_gif_restart(lv_obj_t * gif); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GIF*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_GIF_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/lv_libs.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/lv_libs.h index e795f4c76..6782b1d08 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/lv_libs.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/lv_libs.h @@ -13,15 +13,15 @@ extern "C" { /********************* * INCLUDES *********************/ -// #include "bmp/lv_bmp.h" -// #include "fsdrv/lv_fsdrv.h" +#include "bmp/lv_bmp.h" +#include "fsdrv/lv_fsdrv.h" #include "png/lv_png.h" -// #include "gif/lv_gif.h" +#include "gif/lv_gif.h" #include "qrcode/lv_qrcode.h" -// #include "sjpg/lv_sjpg.h" +#include "sjpg/lv_sjpg.h" #include "freetype/lv_freetype.h" -// #include "rlottie/lv_rlottie.h" -// #include "ffmpeg/lv_ffmpeg.h" +#include "rlottie/lv_rlottie.h" +#include "ffmpeg/lv_ffmpeg.h" /********************* * DEFINES diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lodepng.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lodepng.c index 65139bc5b..82e18e1a0 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lodepng.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lodepng.c @@ -119,14 +119,12 @@ to something as fast. */ static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size) { - size_t i; - for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; + lv_memcpy(dst, src, size); } static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num) { - size_t i; - for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value; + lv_memset(dst, value, num); } /* does not check memory out of bounds, do not use on untrusted data */ @@ -5756,7 +5754,7 @@ static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const adam7 = (unsigned char*)lodepng_malloc(passstart[7]); if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ - if(!error) { + if(!error && adam7) { unsigned i; Adam7_interlace(adam7, in, w, h, bpp); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lv_png.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lv_png.c index 0b88eb89a..d067ef508 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lv_png.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/png/lv_png.c @@ -70,7 +70,7 @@ static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * sr /*If it's a PNG file...*/ if(src_type == LV_IMG_SRC_FILE) { const char * fn = src; - if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/ + if(strcmp(lv_fs_get_ext(fn), "png") == 0) { /*Check the extension*/ /* Read the width and height from the file. They have a constant location: * [16..23]: width @@ -80,14 +80,18 @@ static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * sr lv_fs_file_t f; lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD); if(res != LV_FS_RES_OK) return LV_RES_INV; + lv_fs_seek(&f, 16, LV_FS_SEEK_SET); + uint32_t rn; lv_fs_read(&f, &size, 8, &rn); - if(rn != 8) return LV_RES_INV; lv_fs_close(&f); + + if(rn != 8) return LV_RES_INV; + /*Save the data in the header*/ header->always_zero = 0; - header->cf = LV_IMG_CF_RAW_ALPHA; + header->cf = LV_IMG_CF_TRUE_COLOR_ALPHA; /*The width and height are stored in Big endian format so convert them to little endian*/ header->w = (lv_coord_t)((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8); header->h = (lv_coord_t)((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8); @@ -98,7 +102,9 @@ static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * sr /*If it's a PNG file in a C array...*/ else if(src_type == LV_IMG_SRC_VARIABLE) { const lv_img_dsc_t * img_dsc = src; + const uint32_t data_size = img_dsc->data_size; const uint8_t magic[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}; + if(data_size < sizeof(magic)) return LV_RES_INV; if(memcmp(magic, img_dsc->data, sizeof(magic))) return LV_RES_INV; header->always_zero = 0; header->cf = img_dsc->header.cf; /*Save the color format*/ @@ -115,7 +121,7 @@ static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * sr * Open a PNG image and return the decided image * @param src can be file name or pointer to a C array * @param style style of the image object (unused now but certain formats might use it) - * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed + * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed */ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) { @@ -128,8 +134,7 @@ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * /*If it's a PNG file...*/ if(dsc->src_type == LV_IMG_SRC_FILE) { const char * fn = dsc->src; - - if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/ + if(strcmp(lv_fs_get_ext(fn), "png") == 0) { /*Check the extension*/ /*Load the PNG file into buffer. It's still compressed (not decoded)*/ unsigned char * png_data; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/ @@ -149,6 +154,9 @@ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size); lv_mem_free(png_data); /*Free the loaded file*/ if(error) { + if(img_data != NULL) { + lv_mem_free(img_data); + } LV_LOG_WARN("error %u: %s\n", error, lodepng_error_text(error)); return LV_RES_INV; } @@ -169,6 +177,9 @@ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size); if(error) { + if(img_data != NULL) { + lv_mem_free(img_data); + } return LV_RES_INV; } diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.c index 4b06c78e7..37ee74233 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.c @@ -1,9 +1,9 @@ -/* +/* * QR Code generator library (C) - * + * * Copyright (c) Project Nayuki. (MIT License) * https://www.nayuki.io/page/qr-code-generator-library - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.h index dceddf605..b484e9175 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/qrcode/qrcodegen.h @@ -1,9 +1,9 @@ -/* +/* * QR Code generator library (C) - * + * * Copyright (c) Project Nayuki. (MIT License) * https://www.nayuki.io/page/qr-code-generator-library - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to @@ -33,14 +33,14 @@ extern "C" { #endif -/* +/* * This library creates QR Code symbols, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. * A QR Code structure is an immutable square grid of black and white cells. * The library provides functions to create a QR Code from text or binary data. * The library covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * + * * Ways to create a QR Code object: * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). * - Low level: Custom-make the list of segments and call @@ -51,7 +51,7 @@ extern "C" { /*---- Enum and struct types----*/ -/* +/* * The error correction level in a QR Code symbol. */ enum qrcodegen_Ecc { @@ -64,7 +64,7 @@ enum qrcodegen_Ecc { }; -/* +/* * The mask pattern used in a QR Code symbol. */ enum qrcodegen_Mask { @@ -83,7 +83,7 @@ enum qrcodegen_Mask { }; -/* +/* * Describes how a segment's data bits are interpreted. */ enum qrcodegen_Mode { @@ -95,7 +95,7 @@ enum qrcodegen_Mode { }; -/* +/* * A segment of character/binary/control data in a QR Code symbol. * The mid-level way to create a segment is to take the payload data * and call a factory function such as qrcodegen_makeNumeric(). @@ -147,7 +147,7 @@ struct qrcodegen_Segment { /*---- Functions (high level) to generate QR Codes ----*/ -/* +/* * Encodes the given text string to a QR Code, returning true if encoding succeeded. * If the data is too long to fit in any version in the given range * at the given ECC level, then false is returned. @@ -170,7 +170,7 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); -/* +/* * Encodes the given binary data to a QR Code, returning true if encoding succeeded. * If the data is too long to fit in any version in the given range * at the given ECC level, then false is returned. @@ -194,7 +194,7 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod /*---- Functions (low level) to generate QR Codes ----*/ -/* +/* * Renders a QR Code representing the given segments at the given error correction level. * The smallest possible QR Code version is automatically chosen for the output. Returns true if * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level @@ -210,7 +210,7 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); -/* +/* * Renders a QR Code representing the given segments with the given encoding parameters. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. * The smallest possible QR Code version within the given range is automatically @@ -229,7 +229,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); -/* +/* * Tests whether the given string can be encoded as a segment in alphanumeric mode. * A string is encodable iff each character is in the following set: 0 to 9, A to Z * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. @@ -237,14 +237,14 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz bool qrcodegen_isAlphanumeric(const char *text); -/* +/* * Tests whether the given string can be encoded as a segment in numeric mode. * A string is encodable iff each character is in the range 0 to 9. */ bool qrcodegen_isNumeric(const char *text); -/* +/* * Returns the number of bytes (uint8_t) needed for the data buffer of a segment * containing the given number of characters using the given mode. Notes: * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or @@ -258,7 +258,7 @@ bool qrcodegen_isNumeric(const char *text); size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); -/* +/* * Returns a segment representing the given binary data encoded in * byte mode. All input byte arrays are acceptable. Any text string * can be converted to UTF-8 bytes and encoded as a byte mode segment. @@ -266,13 +266,13 @@ size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); -/* +/* * Returns a segment representing the given string of decimal digits encoded in numeric mode. */ struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); -/* +/* * Returns a segment representing the given text string encoded in alphanumeric mode. * The characters allowed are: 0 to 9, A to Z (uppercase only), space, * dollar, percent, asterisk, plus, hyphen, period, slash, colon. @@ -280,7 +280,7 @@ struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[] struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); -/* +/* * Returns a segment representing an Extended Channel Interpretation * (ECI) designator with the given assignment value. */ @@ -289,7 +289,7 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); /*---- Functions to extract raw data from QR Codes ----*/ -/* +/* * Returns the side length of the given QR Code, assuming that encoding succeeded. * The result is in the range [21, 177]. Note that the length of the array buffer * is related to the side length - every 'uint8_t qrcode[]' must have length at least @@ -298,18 +298,18 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); int qrcodegen_getSize(const uint8_t qrcode[]); -/* +/* * Returns the color of the module (pixel) at the given coordinates, which is false * for white or true for black. The top left corner has the coordinates (x=0, y=0). * If the given coordinates are out of bounds, then false (white) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); -/* +/* * Returns the qrcode size of the specified version. Returns -1 on failure */ int qrcodegen_version2size(int version); -/* +/* * Returns the min version of the data that can be stored. Returns -1 on failure */ int qrcodegen_getMinFitVersion(enum qrcodegen_Ecc ecl, size_t dataLen); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.c new file mode 100644 index 000000000..a264948c3 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.c @@ -0,0 +1,284 @@ +/** + * @file lv_rlottie.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_rlottie.h" +#if LV_USE_RLOTTIE + +#include + +/********************* +* DEFINES +*********************/ +#define MY_CLASS &lv_rlottie_class +#define LV_ARGB32 32 + +/********************** +* TYPEDEFS +**********************/ +#define LV_ARGB32 32 + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void next_frame_task_cb(lv_timer_t * t); + +/********************** + * STATIC VARIABLES + **********************/ +const lv_obj_class_t lv_rlottie_class = { + .constructor_cb = lv_rlottie_constructor, + .destructor_cb = lv_rlottie_destructor, + .instance_size = sizeof(lv_rlottie_t), + .base_class = &lv_img_class +}; + +static lv_coord_t create_width; +static lv_coord_t create_height; +static const char * rlottie_desc_create; +static const char * path_create; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, const char * path) +{ + + create_width = width; + create_height = height; + path_create = path; + rlottie_desc_create = NULL; + + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + + return obj; + +} + +lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, const char * rlottie_desc) +{ + + create_width = width; + create_height = height; + rlottie_desc_create = rlottie_desc; + path_create = NULL; + + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + + return obj; +} + +void lv_rlottie_set_play_mode(lv_obj_t * obj, const lv_rlottie_ctrl_t ctrl) +{ + lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; + rlottie->play_ctrl = ctrl; + + if(rlottie->task && (rlottie->dest_frame != rlottie->current_frame || + (rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PLAY)) { + lv_timer_resume(rlottie->task); + } +} + +void lv_rlottie_set_current_frame(lv_obj_t * obj, const size_t goto_frame) +{ + lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; + rlottie->current_frame = goto_frame < rlottie->total_frames ? goto_frame : rlottie->total_frames - 1; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_rlottie_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; + + if(rlottie_desc_create) { + rlottie->animation = lottie_animation_from_data(rlottie_desc_create, rlottie_desc_create, ""); + } + else if(path_create) { + rlottie->animation = lottie_animation_from_file(path_create); + } + if(rlottie->animation == NULL) { + LV_LOG_WARN("The aniamtion can't be opened"); + return; + } + + rlottie->total_frames = lottie_animation_get_totalframe(rlottie->animation); + rlottie->framerate = (size_t)lottie_animation_get_framerate(rlottie->animation); + rlottie->current_frame = 0; + + rlottie->scanline_width = create_width * LV_ARGB32 / 8; + + size_t allocaled_buf_size = (create_width * create_height * LV_ARGB32 / 8); + rlottie->allocated_buf = lv_mem_alloc(allocaled_buf_size); + if(rlottie->allocated_buf != NULL) { + rlottie->allocated_buffer_size = allocaled_buf_size; + memset(rlottie->allocated_buf, 0, allocaled_buf_size); + } + + rlottie->imgdsc.header.always_zero = 0; + rlottie->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + rlottie->imgdsc.header.h = create_height; + rlottie->imgdsc.header.w = create_width; + rlottie->imgdsc.data = (void *)rlottie->allocated_buf; + rlottie->imgdsc.data_size = allocaled_buf_size; + + lv_img_set_src(obj, &rlottie->imgdsc); + + rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD | LV_RLOTTIE_CTRL_PLAY | LV_RLOTTIE_CTRL_LOOP; + rlottie->dest_frame = rlottie->total_frames; /* invalid destination frame so it's possible to pause on frame 0 */ + + rlottie->task = lv_timer_create(next_frame_task_cb, 1000 / rlottie->framerate, obj); + + lv_obj_update_layout(obj); +} + + +static void lv_rlottie_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; + + if(rlottie->animation) { + lottie_animation_destroy(rlottie->animation); + rlottie->animation = 0; + rlottie->current_frame = 0; + rlottie->framerate = 0; + rlottie->scanline_width = 0; + rlottie->total_frames = 0; + } + + if(rlottie->task) { + lv_timer_del(rlottie->task); + rlottie->task = NULL; + rlottie->play_ctrl = LV_RLOTTIE_CTRL_FORWARD; + rlottie->dest_frame = 0; + } + + lv_img_cache_invalidate_src(&rlottie->imgdsc); + if(rlottie->allocated_buf) { + lv_mem_free(rlottie->allocated_buf); + rlottie->allocated_buf = NULL; + rlottie->allocated_buffer_size = 0; + } + +} + +#if LV_COLOR_DEPTH == 16 +static void convert_to_rgba5658(uint32_t * pix, const size_t width, const size_t height) +{ + /* rlottie draws in ARGB32 format, but LVGL only deal with RGB565 format with (optional 8 bit alpha channel) + so convert in place here the received buffer to LVGL format. */ + uint8_t * dest = (uint8_t *)pix; + uint32_t * src = pix; + for(size_t y = 0; y < height; y++) { + /* Convert a 4 bytes per pixel in format ARGB to R5G6B5A8 format + naive way: + r = ((c & 0xFF0000) >> 19) + g = ((c & 0xFF00) >> 10) + b = ((c & 0xFF) >> 3) + rgb565 = (r << 11) | (g << 5) | b + a = c >> 24; + That's 3 mask, 6 bitshift and 2 or operations + + A bit better: + r = ((c & 0xF80000) >> 8) + g = ((c & 0xFC00) >> 5) + b = ((c & 0xFF) >> 3) + rgb565 = r | g | b + a = c >> 24; + That's 3 mask, 3 bitshifts and 2 or operations */ + for(size_t x = 0; x < width; x++) { + uint32_t in = src[x]; +#if LV_COLOR_16_SWAP == 0 + uint16_t r = (uint16_t)(((in & 0xF80000) >> 8) | ((in & 0xFC00) >> 5) | ((in & 0xFF) >> 3)); +#else + /* We want: rrrr rrrr GGGg gggg bbbb bbbb => gggb bbbb rrrr rGGG */ + uint16_t r = (uint16_t)(((in & 0xF80000) >> 16) | ((in & 0xFC00) >> 13) | ((in & 0x1C00) << 3) | ((in & 0xF8) << 5)); +#endif + + lv_memcpy(dest, &r, sizeof(r)); + dest[sizeof(r)] = (uint8_t)(in >> 24); + dest += LV_IMG_PX_SIZE_ALPHA_BYTE; + } + src += width; + } +} +#endif + +static void next_frame_task_cb(lv_timer_t * t) +{ + lv_obj_t * obj = t->user_data; + lv_rlottie_t * rlottie = (lv_rlottie_t *) obj; + + if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_PAUSE) == LV_RLOTTIE_CTRL_PAUSE) { + if(rlottie->current_frame == rlottie->dest_frame) { + /* Pause the timer too when it has run once to avoid CPU consumption */ + lv_timer_pause(t); + return; + } + rlottie->dest_frame = rlottie->current_frame; + } + else { + if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_BACKWARD) == LV_RLOTTIE_CTRL_BACKWARD) { + if(rlottie->current_frame > 0) + --rlottie->current_frame; + else { /* Looping ? */ + if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP) + rlottie->current_frame = rlottie->total_frames - 1; + else { + lv_event_send(obj, LV_EVENT_READY, NULL); + lv_timer_pause(t); + return; + } + } + } + else { + if(rlottie->current_frame < rlottie->total_frames) + ++rlottie->current_frame; + else { /* Looping ? */ + if((rlottie->play_ctrl & LV_RLOTTIE_CTRL_LOOP) == LV_RLOTTIE_CTRL_LOOP) + rlottie->current_frame = 0; + else { + lv_event_send(obj, LV_EVENT_READY, NULL); + lv_timer_pause(t); + return; + } + } + } + } + + lottie_animation_render( + rlottie->animation, + rlottie->current_frame, + rlottie->allocated_buf, + rlottie->imgdsc.header.w, + rlottie->imgdsc.header.h, + rlottie->scanline_width + ); + +#if LV_COLOR_DEPTH == 16 + convert_to_rgba5658(rlottie->allocated_buf, rlottie->imgdsc.header.w, rlottie->imgdsc.header.h); +#endif + + lv_obj_invalidate(obj); +} + +#endif /*LV_USE_RLOTTIE*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.h new file mode 100644 index 000000000..d66dc22c0 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/rlottie/lv_rlottie.h @@ -0,0 +1,75 @@ +/** + * @file lv_rlottie.h + * + */ + +#ifndef LV_RLOTTIE_H +#define LV_RLOTTIE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_RLOTTIE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef enum { + LV_RLOTTIE_CTRL_FORWARD = 0, + LV_RLOTTIE_CTRL_BACKWARD = 1, + LV_RLOTTIE_CTRL_PAUSE = 2, + LV_RLOTTIE_CTRL_PLAY = 0, /* Yes, play = 0 is the default mode */ + LV_RLOTTIE_CTRL_LOOP = 8, +} lv_rlottie_ctrl_t; + +/** definition in lottieanimation_capi.c */ +struct Lottie_Animation_S; +typedef struct { + lv_img_t img_ext; + struct Lottie_Animation_S * animation; + lv_timer_t * task; + lv_img_dsc_t imgdsc; + size_t total_frames; + size_t current_frame; + size_t framerate; + uint32_t * allocated_buf; + size_t allocated_buffer_size; + size_t scanline_width; + lv_rlottie_ctrl_t play_ctrl; + size_t dest_frame; +} lv_rlottie_t; + +extern const lv_obj_class_t lv_rlottie_class; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_obj_t * lv_rlottie_create_from_file(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, const char * path); + +lv_obj_t * lv_rlottie_create_from_raw(lv_obj_t * parent, lv_coord_t width, lv_coord_t height, + const char * rlottie_desc); + +void lv_rlottie_set_play_mode(lv_obj_t * rlottie, const lv_rlottie_ctrl_t ctrl); +void lv_rlottie_set_current_frame(lv_obj_t * rlottie, const size_t goto_frame); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_RLOTTIE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_RLOTTIE_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.c new file mode 100644 index 000000000..5a12ea251 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.c @@ -0,0 +1,917 @@ +/** + * @file lv_sjpg.c + * + */ + +/*---------------------------------------------------------------------------------------------------------------------------------- +/ Added normal JPG support [7/10/2020] +/ ---------- +/ SJPEG is a custom created modified JPEG file format for small embedded platforms. +/ It will contain multiple JPEG fragments all embedded into a single file with a custom header. +/ This makes JPEG decoding easier using any JPEG library. Overall file size will be almost +/ similar to the parent jpeg file. We can generate sjpeg from any jpeg using a python script +/ provided along with this project. +/ (by vinodstanur | 2020 ) +/ SJPEG FILE STRUCTURE +/ -------------------------------------------------------------------------------------------------------------------------------- +/ Bytes | Value | +/ -------------------------------------------------------------------------------------------------------------------------------- +/ +/ 0 - 7 | "_SJPG__" followed by '\0' +/ +/ 8 - 13 | "V1.00" followed by '\0' [VERSION OF SJPG FILE for future compatibiliby] +/ +/ 14 - 15 | X_RESOLUTION (width) [little endian] +/ +/ 16 - 17 | Y_RESOLUTION (height) [little endian] +/ +/ 18 - 19 | TOTAL_FRAMES inside sjpeg [little endian] +/ +/ 20 - 21 | JPEG BLOCK WIDTH (16 normally) [little endian] +/ +/ 22 - [(TOTAL_FRAMES*2 )] | SIZE OF EACH JPEG SPLIT FRAGMENTS (FRAME_INFO_ARRAY) +/ +/ SJPEG data | Each JPEG frame can be extracted from SJPEG data by parsing the FRAME_INFO_ARRAY one time. +/ +/---------------------------------------------------------------------------------------------------------------------------------- +/ JPEG DECODER +/ ------------ +/ We are using TJpgDec - Tiny JPEG Decompressor library from ELM-CHAN for decoding each split-jpeg fragments. +/ The tjpgd.c and tjpgd.h is not modified and those are used as it is. So if any update comes for the tiny-jpeg, +/ just replace those files with updated files. +/---------------------------------------------------------------------------------------------------------------------------------*/ + +/********************* + * INCLUDES + *********************/ + +#include "../../../lvgl.h" +#if LV_USE_SJPG + +#include "tjpgd.h" +#include "lv_sjpg.h" +#include "../../../misc/lv_fs.h" + +/********************* + * DEFINES + *********************/ +#define TJPGD_WORKBUFF_SIZE 4096 //Recommended by TJPGD libray + +//NEVER EDIT THESE OFFSET VALUES +#define SJPEG_VERSION_OFFSET 8 +#define SJPEG_X_RES_OFFSET 14 +#define SJPEG_y_RES_OFFSET 16 +#define SJPEG_TOTAL_FRAMES_OFFSET 18 +#define SJPEG_BLOCK_WIDTH_OFFSET 20 +#define SJPEG_FRAME_INFO_ARRAY_OFFSET 22 + +/********************** + * TYPEDEFS + **********************/ + +enum io_source_type { + SJPEG_IO_SOURCE_C_ARRAY, + SJPEG_IO_SOURCE_DISK, +}; + +typedef struct { + enum io_source_type type; + lv_fs_file_t lv_file; + uint8_t * img_cache_buff; + int img_cache_x_res; + int img_cache_y_res; + uint8_t * raw_sjpg_data; //Used when type==SJPEG_IO_SOURCE_C_ARRAY. + uint32_t raw_sjpg_data_size; //Num bytes pointed to by raw_sjpg_data. + uint32_t raw_sjpg_data_next_read_pos; //Used for all types. +} io_source_t; + + +typedef struct { + uint8_t * sjpeg_data; + uint32_t sjpeg_data_size; + int sjpeg_x_res; + int sjpeg_y_res; + int sjpeg_total_frames; + int sjpeg_single_frame_height; + int sjpeg_cache_frame_index; + uint8_t ** frame_base_array; //to save base address of each split frames upto sjpeg_total_frames. + int * frame_base_offset; //to save base offset for fseek + uint8_t * frame_cache; + uint8_t * workb; //JPG work buffer for jpeg library + JDEC * tjpeg_jd; + io_source_t io; +} SJPEG; + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header); +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc); +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, + lv_coord_t len, uint8_t * buf); +static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc); +static size_t input_func(JDEC * jd, uint8_t * buff, size_t ndata); +static int is_jpg(const uint8_t * raw_data, size_t len); +static void lv_sjpg_cleanup(SJPEG * sjpeg); +static void lv_sjpg_free(SJPEG * sjpeg); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_split_jpeg_init(void) +{ + lv_img_decoder_t * dec = lv_img_decoder_create(); + lv_img_decoder_set_info_cb(dec, decoder_info); + lv_img_decoder_set_open_cb(dec, decoder_open); + lv_img_decoder_set_close_cb(dec, decoder_close); + lv_img_decoder_set_read_line_cb(dec, decoder_read_line); +} + +/********************** + * STATIC FUNCTIONS + **********************/ +/** + * Get info about an SJPG / JPG image + * @param decoder pointer to the decoder where this function belongs + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header) +{ + LV_UNUSED(decoder); + + /*Check whether the type `src` is known by the decoder*/ + /* Read the SJPG/JPG header and find `width` and `height` */ + + lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/ + + lv_res_t ret = LV_RES_OK; + + if(src_type == LV_IMG_SRC_VARIABLE) { + const lv_img_dsc_t * img_dsc = src; + uint8_t * raw_sjpeg_data = (uint8_t *)img_dsc->data; + const uint32_t raw_sjpeg_data_size = img_dsc->data_size; + + if(!strncmp((char *)raw_sjpeg_data, "_SJPG__", strlen("_SJPG__"))) { + + raw_sjpeg_data += 14; //seek to res info ... refer sjpeg format + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + + header->w = *raw_sjpeg_data++; + header->w |= *raw_sjpeg_data++ << 8; + + header->h = *raw_sjpeg_data++; + header->h |= *raw_sjpeg_data++ << 8; + + return ret; + + } + else if(is_jpg(raw_sjpeg_data, raw_sjpeg_data_size) == true) { + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + + uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(!workb_temp) return LV_RES_INV; + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY; + io_source_temp.raw_sjpg_data = raw_sjpeg_data; + io_source_temp.raw_sjpg_data_size = raw_sjpeg_data_size; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + + JDEC jd_tmp; + + JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + if(rc == JDR_OK) { + header->w = jd_tmp.width; + header->h = jd_tmp.height; + + } + else { + ret = LV_RES_INV; + goto end; + } + +end: + lv_mem_free(workb_temp); + + return ret; + + } + } + else if(src_type == LV_IMG_SRC_FILE) { + const char * fn = src; + if(strcmp(lv_fs_get_ext(fn), "sjpg") == 0) { + + uint8_t buff[22]; + memset(buff, 0, sizeof(buff)); + + lv_fs_file_t file; + lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return 78; + + uint32_t rn; + res = lv_fs_read(&file, buff, 8, &rn); + if(res != LV_FS_RES_OK || rn != 8) { + lv_fs_close(&file); + return LV_RES_INV; + } + + if(strcmp((char *)buff, "_SJPG__") == 0) { + lv_fs_seek(&file, 14, LV_FS_SEEK_SET); + res = lv_fs_read(&file, buff, 4, &rn); + if(res != LV_FS_RES_OK || rn != 4) { + lv_fs_close(&file); + return LV_RES_INV; + } + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + uint8_t * raw_sjpeg_data = buff; + header->w = *raw_sjpeg_data++; + header->w |= *raw_sjpeg_data++ << 8; + header->h = *raw_sjpeg_data++; + header->h |= *raw_sjpeg_data++ << 8; + lv_fs_close(&file); + return LV_RES_OK; + + } + } + else if(strcmp(lv_fs_get_ext(fn), "jpg") == 0) { + lv_fs_file_t file; + lv_fs_res_t res = lv_fs_open(&file, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return 78; + + uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(!workb_temp) { + lv_fs_close(&file); + return LV_RES_INV; + } + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_DISK; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + io_source_temp.img_cache_buff = NULL; + io_source_temp.lv_file = file; + JDEC jd_tmp; + + JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + lv_mem_free(workb_temp); + lv_fs_close(&file); + + if(rc == JDR_OK) { + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + header->w = jd_tmp.width; + header->h = jd_tmp.height; + return LV_RES_OK; + } + } + } + return LV_RES_INV; +} + +static int img_data_cb(JDEC * jd, void * data, JRECT * rect) +{ + io_source_t * io = jd->device; + uint8_t * cache = io->img_cache_buff; + const int xres = io->img_cache_x_res; + uint8_t * buf = data; + const int INPUT_PIXEL_SIZE = 3; + const int row_width = rect->right - rect->left + 1; // Row width in pixels. + const int row_size = row_width * INPUT_PIXEL_SIZE; // Row size (bytes). + + for(int y = rect->top; y <= rect->bottom; y++) { + int row_offset = y * xres * INPUT_PIXEL_SIZE + rect->left * INPUT_PIXEL_SIZE; + memcpy(cache + row_offset, buf, row_size); + buf += row_size; + } + + return 1; +} + +static size_t input_func(JDEC * jd, uint8_t * buff, size_t ndata) +{ + io_source_t * io = jd->device; + + if(!io) return 0; + + if(io->type == SJPEG_IO_SOURCE_C_ARRAY) { + const uint32_t bytes_left = io->raw_sjpg_data_size - io->raw_sjpg_data_next_read_pos; + const uint32_t to_read = ndata <= bytes_left ? (uint32_t)ndata : bytes_left; + if(to_read == 0) + return 0; + if(buff) { + memcpy(buff, io->raw_sjpg_data + io->raw_sjpg_data_next_read_pos, to_read); + } + io->raw_sjpg_data_next_read_pos += to_read; + return to_read; + } + else if(io->type == SJPEG_IO_SOURCE_DISK) { + + lv_fs_file_t * lv_file_p = &(io->lv_file); + + if(buff) { + uint32_t rn = 0; + lv_fs_read(lv_file_p, buff, (uint32_t)ndata, &rn); + return rn; + } + else { + uint32_t pos; + lv_fs_tell(lv_file_p, &pos); + lv_fs_seek(lv_file_p, (uint32_t)(ndata + pos), LV_FS_SEEK_SET); + return ndata; + } + } + return 0; +} + +/** + * Open SJPG image and return the decided image + * @param decoder pointer to the decoder where this function belongs + * @param dsc pointer to a descriptor which describes this decoding session + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + lv_res_t lv_ret = LV_RES_OK; + + if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + uint8_t * data; + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)dsc->src)->data_size; + if(sjpeg == NULL) { + sjpeg = lv_mem_alloc(sizeof(SJPEG)); + if(!sjpeg) return LV_RES_INV; + + memset(sjpeg, 0, sizeof(SJPEG)); + + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data; + sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size; + } + + if(!strncmp((char *) sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__"))) { + + data = sjpeg->sjpeg_data; + data += 14; + + sjpeg->sjpeg_x_res = *data++; + sjpeg->sjpeg_x_res |= *data++ << 8; + + sjpeg->sjpeg_y_res = *data++; + sjpeg->sjpeg_y_res |= *data++ << 8; + + sjpeg->sjpeg_total_frames = *data++; + sjpeg->sjpeg_total_frames |= *data++ << 8; + + sjpeg->sjpeg_single_frame_height = *data++; + sjpeg->sjpeg_single_frame_height |= *data++ << 8; + + sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames); + if(! sjpeg->frame_base_array) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->frame_base_offset = NULL; + + uint8_t * img_frame_base = data + sjpeg->sjpeg_total_frames * 2; + sjpeg->frame_base_array[0] = img_frame_base; + + for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) { + int offset = *data++; + offset |= *data++ << 8; + sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i - 1] + offset; + } + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3/*2*/); + if(! sjpeg->frame_cache) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! sjpeg->workb) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC)); + if(! sjpeg->tjpeg_jd) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY; + sjpeg->io.lv_file.file_d = NULL; + dsc->img_data = NULL; + return lv_ret; + } + else if(is_jpg(sjpeg->sjpeg_data, raw_sjpeg_data_size) == true) { + + uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! workb_temp) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY; + io_source_temp.raw_sjpg_data = sjpeg->sjpeg_data; + io_source_temp.raw_sjpg_data_size = sjpeg->sjpeg_data_size; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + + JDEC jd_tmp; + JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + lv_mem_free(workb_temp); + + + if(rc == JDR_OK) { + sjpeg->sjpeg_x_res = jd_tmp.width; + sjpeg->sjpeg_y_res = jd_tmp.height; + sjpeg->sjpeg_total_frames = 1; + sjpeg->sjpeg_single_frame_height = jd_tmp.height; + + sjpeg->frame_base_array = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames); + if(! sjpeg->frame_base_array) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->frame_base_offset = NULL; + + uint8_t * img_frame_base = sjpeg->sjpeg_data; + sjpeg->frame_base_array[0] = img_frame_base; + + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3); + if(! sjpeg->frame_cache) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! sjpeg->workb) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC)); + if(! sjpeg->tjpeg_jd) { + lv_sjpg_cleanup(sjpeg); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY; + sjpeg->io.lv_file.file_d = NULL; + dsc->img_data = NULL; + return lv_ret; + } + else { + lv_ret = LV_RES_INV; + goto end; + } + +end: + lv_mem_free(workb_temp); + + return lv_ret; + } + } + else if(dsc->src_type == LV_IMG_SRC_FILE) { + /* If all fine, then the file will be kept open */ + const char * fn = dsc->src; + uint8_t * data; + + if(strcmp(lv_fs_get_ext(fn), "sjpg") == 0) { + + uint8_t buff[22]; + memset(buff, 0, sizeof(buff)); + + + lv_fs_file_t lv_file; + lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + return 78; + } + + + uint32_t rn; + res = lv_fs_read(&lv_file, buff, 22, &rn); + if(res != LV_FS_RES_OK || rn != 22) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + + if(strcmp((char *)buff, "_SJPG__") == 0) { + + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + if(sjpeg == NULL) { + sjpeg = lv_mem_alloc(sizeof(SJPEG)); + + if(! sjpeg) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + memset(sjpeg, 0, sizeof(SJPEG)); + + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data; + sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size; + } + data = buff; + data += 14; + + sjpeg->sjpeg_x_res = *data++; + sjpeg->sjpeg_x_res |= *data++ << 8; + + sjpeg->sjpeg_y_res = *data++; + sjpeg->sjpeg_y_res |= *data++ << 8; + + sjpeg->sjpeg_total_frames = *data++; + sjpeg->sjpeg_total_frames |= *data++ << 8; + + sjpeg->sjpeg_single_frame_height = *data++; + sjpeg->sjpeg_single_frame_height |= *data++ << 8; + + sjpeg->frame_base_array = NULL;//lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames ); + sjpeg->frame_base_offset = lv_mem_alloc(sizeof(int) * sjpeg->sjpeg_total_frames); + if(! sjpeg->frame_base_offset) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames * 2); + sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now... + + for(int i = 1; i < sjpeg->sjpeg_total_frames; i++) { + res = lv_fs_read(&lv_file, buff, 2, &rn); + if(res != LV_FS_RES_OK || rn != 2) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + + data = buff; + int offset = *data++; + offset |= *data++ << 8; + sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i - 1] + offset; + } + + sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time. + sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3); + if(! sjpeg->frame_cache) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! sjpeg->workb) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC)); + if(! sjpeg->tjpeg_jd) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_DISK; + sjpeg->io.lv_file = lv_file; + dsc->img_data = NULL; + return LV_RES_OK; + } + } + else if(strcmp(lv_fs_get_ext(fn), "jpg") == 0) { + + lv_fs_file_t lv_file; + lv_fs_res_t res = lv_fs_open(&lv_file, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + return LV_RES_INV; + } + + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + if(sjpeg == NULL) { + sjpeg = lv_mem_alloc(sizeof(SJPEG)); + if(! sjpeg) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + + memset(sjpeg, 0, sizeof(SJPEG)); + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)((lv_img_dsc_t *)(dsc->src))->data; + sjpeg->sjpeg_data_size = ((lv_img_dsc_t *)(dsc->src))->data_size; + } + + uint8_t * workb_temp = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! workb_temp) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_DISK; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + io_source_temp.img_cache_buff = NULL; + io_source_temp.lv_file = lv_file; + + JDEC jd_tmp; + + JRESULT rc = jd_prepare(&jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + + lv_mem_free(workb_temp); + + + if(rc == JDR_OK) { + sjpeg->sjpeg_x_res = jd_tmp.width; + sjpeg->sjpeg_y_res = jd_tmp.height; + sjpeg->sjpeg_total_frames = 1; + sjpeg->sjpeg_single_frame_height = jd_tmp.height; + + sjpeg->frame_base_array = NULL; + sjpeg->frame_base_offset = lv_mem_alloc(sizeof(uint8_t *) * sjpeg->sjpeg_total_frames); + if(! sjpeg->frame_base_offset) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + int img_frame_start_offset = 0; + sjpeg->frame_base_offset[0] = img_frame_start_offset; + + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc(sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3); + if(! sjpeg->frame_cache) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc(TJPGD_WORKBUFF_SIZE); + if(! sjpeg->workb) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc(sizeof(JDEC)); + if(! sjpeg->tjpeg_jd) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_DISK; + sjpeg->io.lv_file = lv_file; + dsc->img_data = NULL; + return LV_RES_OK; + + } + else { + if(dsc->user_data) lv_mem_free(dsc->user_data); + lv_fs_close(&lv_file); + return LV_RES_INV; + } + } + } + + return LV_RES_INV; +} + +/** + * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`. + * Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL) + * @param decoder pointer to the decoder the function associated with + * @param dsc pointer to decoder descriptor + * @param x start x coordinate + * @param y start y coordinate + * @param len number of pixels to decode + * @param buf a buffer to store the decoded pixels + * @return LV_RES_OK: ok; LV_RES_INV: failed + */ + +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, + lv_coord_t len, uint8_t * buf) +{ + LV_UNUSED(decoder); + if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + JRESULT rc; + + int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height; + + /*If line not from cache, refresh cache */ + if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) { + sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[ sjpeg_req_frame_index ]; + if(sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) { + /*This is the last frame. */ + const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data); + sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset; + } + else { + sjpeg->io.raw_sjpg_data_size = + (uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data); + } + sjpeg->io.raw_sjpg_data_next_read_pos = 0; + rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io)); + if(rc != JDR_OK) return LV_RES_INV; + rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0); + if(rc != JDR_OK) return LV_RES_INV; + sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index; + } + + int offset = 0; + uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res * + 3; + +#if LV_COLOR_DEPTH == 32 + for(int i = 0; i < len; i++) { + buf[offset + 3] = 0xff; + buf[offset + 2] = *cache++; + buf[offset + 1] = *cache++; + buf[offset + 0] = *cache++; + offset += 4; + } + +#elif LV_COLOR_DEPTH == 16 + + for(int i = 0; i < len; i++) { + uint16_t col_16bit = (*cache++ & 0xf8) << 8; + col_16bit |= (*cache++ & 0xFC) << 3; + col_16bit |= (*cache++ >> 3); +#if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1 + buf[offset++] = col_16bit >> 8; + buf[offset++] = col_16bit & 0xff; +#else + buf[offset++] = col_16bit & 0xff; + buf[offset++] = col_16bit >> 8; +#endif // LV_BIG_ENDIAN_SYSTEM + } + +#elif LV_COLOR_DEPTH == 8 + + for(int i = 0; i < len; i++) { + uint8_t col_8bit = (*cache++ & 0xC0); + col_8bit |= (*cache++ & 0xe0) >> 2; + col_8bit |= (*cache++ & 0xe0) >> 5; + buf[offset++] = col_8bit; + } +#else +#error Unsupported LV_COLOR_DEPTH + + +#endif // LV_COLOR_DEPTH + return LV_RES_OK; + } + else if(dsc->src_type == LV_IMG_SRC_FILE) { + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + JRESULT rc; + int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height; + + lv_fs_file_t * lv_file_p = &(sjpeg->io.lv_file); + if(!lv_file_p) goto end; + + /*If line not from cache, refresh cache */ + if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) { + sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset [ sjpeg_req_frame_index ]); + lv_fs_seek(&(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET); + + rc = jd_prepare(sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io)); + if(rc != JDR_OK) return LV_RES_INV; + + rc = jd_decomp(sjpeg->tjpeg_jd, img_data_cb, 0); + if(rc != JDR_OK) return LV_RES_INV; + + sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index; + } + + int offset = 0; + uint8_t * cache = (uint8_t *)sjpeg->frame_cache + x * 3 + (y % sjpeg->sjpeg_single_frame_height) * sjpeg->sjpeg_x_res * + 3; + +#if LV_COLOR_DEPTH == 32 + for(int i = 0; i < len; i++) { + buf[offset + 3] = 0xff; + buf[offset + 2] = *cache++; + buf[offset + 1] = *cache++; + buf[offset + 0] = *cache++; + offset += 4; + } +#elif LV_COLOR_DEPTH == 16 + + for(int i = 0; i < len; i++) { + uint16_t col_8bit = (*cache++ & 0xf8) << 8; + col_8bit |= (*cache++ & 0xFC) << 3; + col_8bit |= (*cache++ >> 3); +#if LV_BIG_ENDIAN_SYSTEM == 1 || LV_COLOR_16_SWAP == 1 + buf[offset++] = col_8bit >> 8; + buf[offset++] = col_8bit & 0xff; +#else + buf[offset++] = col_8bit & 0xff; + buf[offset++] = col_8bit >> 8; +#endif // LV_BIG_ENDIAN_SYSTEM + } + +#elif LV_COLOR_DEPTH == 8 + + for(int i = 0; i < len; i++) { + uint8_t col_8bit = (*cache++ & 0xC0); + col_8bit |= (*cache++ & 0xe0) >> 2; + col_8bit |= (*cache++ & 0xe0) >> 5; + buf[offset++] = col_8bit; + } + +#else +#error Unsupported LV_COLOR_DEPTH + + +#endif // LV_COLOR_DEPTH + + return LV_RES_OK; + } +end: + return LV_RES_INV; +} + +/** + * Free the allocated resources + * @param decoder pointer to the decoder where this function belongs + * @param dsc pointer to a descriptor which describes this decoding session + */ +static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + /*Free all allocated data*/ + SJPEG * sjpeg = (SJPEG *) dsc->user_data; + if(!sjpeg) return; + + switch(dsc->src_type) { + case LV_IMG_SRC_FILE: + if(sjpeg->io.lv_file.file_d) { + lv_fs_close(&(sjpeg->io.lv_file)); + } + lv_sjpg_cleanup(sjpeg); + break; + + case LV_IMG_SRC_VARIABLE: + lv_sjpg_cleanup(sjpeg); + break; + + default: + ; + } +} + +static int is_jpg(const uint8_t * raw_data, size_t len) +{ + const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46}; + if(len < sizeof(jpg_signature)) return false; + return memcmp(jpg_signature, raw_data, sizeof(jpg_signature)) == 0; +} + +static void lv_sjpg_free(SJPEG * sjpeg) +{ + if(sjpeg->frame_cache) lv_mem_free(sjpeg->frame_cache); + if(sjpeg->frame_base_array) lv_mem_free(sjpeg->frame_base_array); + if(sjpeg->frame_base_offset) lv_mem_free(sjpeg->frame_base_offset); + if(sjpeg->tjpeg_jd) lv_mem_free(sjpeg->tjpeg_jd); + if(sjpeg->workb) lv_mem_free(sjpeg->workb); +} + +static void lv_sjpg_cleanup(SJPEG * sjpeg) +{ + if(! sjpeg) return; + + lv_sjpg_free(sjpeg); + lv_mem_free(sjpeg); +} + +#endif /*LV_USE_SJPG*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.h new file mode 100644 index 000000000..d06e80de5 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/lv_sjpg.h @@ -0,0 +1,43 @@ +/** + * @file lv_sjpg.h + * + */ + +#ifndef LV_SJPEG_H +#define LV_SJPEG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#if LV_USE_SJPG + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_split_jpeg_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_SJPG*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LV_SJPEG_H */ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.c b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.c new file mode 100644 index 000000000..47ddefb6e --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.c @@ -0,0 +1,1155 @@ +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03 (C)ChaN, 2021 +/-----------------------------------------------------------------------------/ +/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2021, ChaN, all right reserved. +/ +/ * The TJpgDec module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Oct 04, 2011 R0.01 First release. +/ Feb 19, 2012 R0.01a Fixed decompression fails when scan starts with an escape seq. +/ Sep 03, 2012 R0.01b Added JD_TBLCLIP option. +/ Mar 16, 2019 R0.01c Supprted stdint.h. +/ Jul 01, 2020 R0.01d Fixed wrong integer type usage. +/ May 08, 2021 R0.02 Supprted grayscale image. Separated configuration options. +/ Jun 11, 2021 R0.02a Some performance improvement. +/ Jul 01, 2021 R0.03 Added JD_FASTDECODE option. +/ Some performance improvement. +/----------------------------------------------------------------------------*/ + +#include "tjpgd.h" +#if LV_USE_SJPG + +#if JD_FASTDECODE == 2 +#define HUFF_BIT 10 /* Bit length to apply fast huffman decode */ +#define HUFF_LEN (1 << HUFF_BIT) +#define HUFF_MASK (HUFF_LEN - 1) +#endif + + +/*-----------------------------------------------*/ +/* Zigzag-order to raster-order conversion table */ +/*-----------------------------------------------*/ + +static const uint8_t Zig[64] = { /* Zigzag-order to raster-order conversion table */ + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + + + +/*-------------------------------------------------*/ +/* Input scale factor of Arai algorithm */ +/* (scaled up 16 bits for fixed point operations) */ +/*-------------------------------------------------*/ + +static const uint16_t Ipsf[64] = { /* See also aa_idct.png */ + (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192), + (uint16_t)(1.38704*8192), (uint16_t)(1.92388*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.08979*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.38268*8192), + (uint16_t)(1.30656*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.70711*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.36048*8192), + (uint16_t)(1.17588*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.38268*8192), (uint16_t)(1.17588*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.32442*8192), + (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192), + (uint16_t)(0.78570*8192), (uint16_t)(1.08979*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.61732*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.21677*8192), + (uint16_t)(0.54120*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.29290*8192), (uint16_t)(0.14932*8192), + (uint16_t)(0.27590*8192), (uint16_t)(0.38268*8192), (uint16_t)(0.36048*8192), (uint16_t)(0.32442*8192), (uint16_t)(0.27590*8192), (uint16_t)(0.21678*8192), (uint16_t)(0.14932*8192), (uint16_t)(0.07612*8192) +}; + + + +/*---------------------------------------------*/ +/* Conversion table for fast clipping process */ +/*---------------------------------------------*/ + +#if JD_TBLCLIP + +#define BYTECLIP(v) Clip8[(unsigned int)(v) & 0x3FF] + +static const uint8_t Clip8[1024] = { + /* 0..255 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + /* 256..511 */ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + /* -512..-257 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* -256..-1 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#else /* JD_TBLCLIP */ + +static uint8_t BYTECLIP (int val) +{ + if (val < 0) return 0; + if (val > 255) return 255; + return (uint8_t)val; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Allocate a memory block from memory pool */ +/*-----------------------------------------------------------------------*/ + +static void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */ + JDEC* jd, /* Pointer to the decompressor object */ + size_t ndata /* Number of bytes to allocate */ +) +{ + char *rp = 0; + + + ndata = (ndata + 3) & ~3; /* Align block size to the word boundary */ + + if (jd->sz_pool >= ndata) { + jd->sz_pool -= ndata; + rp = (char*)jd->pool; /* Get start of available memory pool */ + jd->pool = (void*)(rp + ndata); /* Allocate requierd bytes */ + } + + return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create de-quantization and prescaling tables with a DQT segment */ +/*-----------------------------------------------------------------------*/ + +static JRESULT create_qt_tbl ( /* 0:OK, !0:Failed */ + JDEC* jd, /* Pointer to the decompressor object */ + const uint8_t* data, /* Pointer to the quantizer tables */ + size_t ndata /* Size of input data */ +) +{ + unsigned int i, zi; + uint8_t d; + int32_t *pb; + + + while (ndata) { /* Process all tables in the segment */ + if (ndata < 65) return JDR_FMT1; /* Err: table size is unaligned */ + ndata -= 65; + d = *data++; /* Get table property */ + if (d & 0xF0) return JDR_FMT1; /* Err: not 8-bit resolution */ + i = d & 3; /* Get table ID */ + pb = alloc_pool(jd, 64 * sizeof (int32_t));/* Allocate a memory block for the table */ + if (!pb) return JDR_MEM1; /* Err: not enough memory */ + jd->qttbl[i] = pb; /* Register the table */ + for (i = 0; i < 64; i++) { /* Load the table */ + zi = Zig[i]; /* Zigzag-order to raster-order conversion */ + pb[zi] = (int32_t)((uint32_t)*data++ * Ipsf[zi]); /* Apply scale factor of Arai algorithm to the de-quantizers */ + } + } + + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create huffman code tables with a DHT segment */ +/*-----------------------------------------------------------------------*/ + +static JRESULT create_huffman_tbl ( /* 0:OK, !0:Failed */ + JDEC* jd, /* Pointer to the decompressor object */ + const uint8_t* data, /* Pointer to the packed huffman tables */ + size_t ndata /* Size of input data */ +) +{ + unsigned int i, j, b, cls, num; + size_t np; + uint8_t d, *pb, *pd; + uint16_t hc, *ph; + + + while (ndata) { /* Process all tables in the segment */ + if (ndata < 17) return JDR_FMT1; /* Err: wrong data size */ + ndata -= 17; + d = *data++; /* Get table number and class */ + if (d & 0xEE) return JDR_FMT1; /* Err: invalid class/number */ + cls = d >> 4; num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */ + pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */ + if (!pb) return JDR_MEM1; /* Err: not enough memory */ + jd->huffbits[num][cls] = pb; + for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */ + np += (pb[i] = *data++); /* Get sum of code words for each code */ + } + ph = alloc_pool(jd, np * sizeof (uint16_t));/* Allocate a memory block for the code word table */ + if (!ph) return JDR_MEM1; /* Err: not enough memory */ + jd->huffcode[num][cls] = ph; + hc = 0; + for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */ + b = pb[i]; + while (b--) ph[j++] = hc++; + hc <<= 1; + } + + if (ndata < np) return JDR_FMT1; /* Err: wrong data size */ + ndata -= np; + pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */ + if (!pd) return JDR_MEM1; /* Err: not enough memory */ + jd->huffdata[num][cls] = pd; + for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code word */ + d = *data++; + if (!cls && d > 11) return JDR_FMT1; + pd[i] = d; + } +#if JD_FASTDECODE == 2 + { /* Create fast huffman decode table */ + unsigned int span, td, ti; + uint16_t *tbl_ac = 0; + uint8_t *tbl_dc = 0; + + if (cls) { + tbl_ac = alloc_pool(jd, HUFF_LEN * sizeof (uint16_t)); /* LUT for AC elements */ + if (!tbl_ac) return JDR_MEM1; /* Err: not enough memory */ + jd->hufflut_ac[num] = tbl_ac; + memset(tbl_ac, 0xFF, HUFF_LEN * sizeof (uint16_t)); /* Default value (0xFFFF: may be long code) */ + } else { + tbl_dc = alloc_pool(jd, HUFF_LEN * sizeof (uint8_t)); /* LUT for AC elements */ + if (!tbl_dc) return JDR_MEM1; /* Err: not enough memory */ + jd->hufflut_dc[num] = tbl_dc; + memset(tbl_dc, 0xFF, HUFF_LEN * sizeof (uint8_t)); /* Default value (0xFF: may be long code) */ + } + for (i = b = 0; b < HUFF_BIT; b++) { /* Create LUT */ + for (j = pb[b]; j; j--) { + ti = ph[i] << (HUFF_BIT - 1 - b) & HUFF_MASK; /* Index of input pattern for the code */ + if (cls) { + td = pd[i++] | ((b + 1) << 8); /* b15..b8: code length, b7..b0: zero run and data length */ + for (span = 1 << (HUFF_BIT - 1 - b); span; span--, tbl_ac[ti++] = (uint16_t)td) ; + } else { + td = pd[i++] | ((b + 1) << 4); /* b7..b4: code length, b3..b0: data length */ + for (span = 1 << (HUFF_BIT - 1 - b); span; span--, tbl_dc[ti++] = (uint8_t)td) ; + } + } + } + jd->longofs[num][cls] = i; /* Code table offset for long code */ + } +#endif + } + + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Extract a huffman decoded data from input stream */ +/*-----------------------------------------------------------------------*/ + +static int huffext ( /* >=0: decoded data, <0: error code */ + JDEC* jd, /* Pointer to the decompressor object */ + unsigned int id, /* Table ID (0:Y, 1:C) */ + unsigned int cls /* Table class (0:DC, 1:AC) */ +) +{ + size_t dc = jd->dctr; + uint8_t *dp = jd->dptr; + unsigned int d, flg = 0; + +#if JD_FASTDECODE == 0 + uint8_t bm, nd, bl; + const uint8_t *hb = jd->huffbits[id][cls]; /* Bit distribution table */ + const uint16_t *hc = jd->huffcode[id][cls]; /* Code word table */ + const uint8_t *hd = jd->huffdata[id][cls]; /* Data table */ + + + bm = jd->dbit; /* Bit mask to extract */ + d = 0; bl = 16; /* Max code length */ + do { + if (!bm) { /* Next byte? */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } else { + dp++; /* Next data ptr */ + } + dc--; /* Decrement number of available bytes */ + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (*dp != 0) return 0 - (int)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ + *dp = 0xFF; /* The flag is a data 0xFF */ + } else { + if (*dp == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + bm = 0x80; /* Read from MSB */ + } + d <<= 1; /* Get a bit */ + if (*dp & bm) d++; + bm >>= 1; + + for (nd = *hb++; nd; nd--) { /* Search the code word in this bit length */ + if (d == *hc++) { /* Matched? */ + jd->dbit = bm; jd->dctr = dc; jd->dptr = dp; + return *hd; /* Return the decoded data */ + } + hd++; + } + bl--; + } while (bl); + +#else + const uint8_t *hb, *hd; + const uint16_t *hc; + unsigned int nc, bl, wbit = jd->dbit % 32; + uint32_t w = jd->wreg & ((1UL << wbit) - 1); + + + while (wbit < 16) { /* Prepare 16 bits into the working register */ + if (jd->marker) { + d = 0xFF; /* Input stream has stalled for a marker. Generate stuff bits */ + } else { + if (!dc) { /* Buffer empty, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } + d = *dp++; dc--; + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (d != 0) jd->marker = d; /* Not an escape of 0xFF but a marker */ + d = 0xFF; + } else { + if (d == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + } + w = w << 8 | d; /* Shift 8 bits in the working register */ + wbit += 8; + } + jd->dctr = dc; jd->dptr = dp; + jd->wreg = w; + +#if JD_FASTDECODE == 2 + /* Table serch for the short codes */ + d = (unsigned int)(w >> (wbit - HUFF_BIT)); /* Short code as table index */ + if (cls) { /* AC element */ + d = jd->hufflut_ac[id][d]; /* Table decode */ + if (d != 0xFFFF) { /* It is done if hit in short code */ + jd->dbit = wbit - (d >> 8); /* Snip the code length */ + return d & 0xFF; /* b7..0: zero run and following data bits */ + } + } else { /* DC element */ + d = jd->hufflut_dc[id][d]; /* Table decode */ + if (d != 0xFF) { /* It is done if hit in short code */ + jd->dbit = wbit - (d >> 4); /* Snip the code length */ + return d & 0xF; /* b3..0: following data bits */ + } + } + + /* Incremental serch for the codes longer than HUFF_BIT */ + hb = jd->huffbits[id][cls] + HUFF_BIT; /* Bit distribution table */ + hc = jd->huffcode[id][cls] + jd->longofs[id][cls]; /* Code word table */ + hd = jd->huffdata[id][cls] + jd->longofs[id][cls]; /* Data table */ + bl = HUFF_BIT + 1; +#else + /* Incremental serch for all codes */ + hb = jd->huffbits[id][cls]; /* Bit distribution table */ + hc = jd->huffcode[id][cls]; /* Code word table */ + hd = jd->huffdata[id][cls]; /* Data table */ + bl = 1; +#endif + for ( ; bl <= 16; bl++) { /* Incremental search */ + nc = *hb++; + if (nc) { + d = w >> (wbit - bl); + do { /* Search the code word in this bit length */ + if (d == *hc++) { /* Matched? */ + jd->dbit = wbit - bl; /* Snip the huffman code */ + return *hd; /* Return the decoded data */ + } + hd++; + } while (--nc); + } + } +#endif + + return 0 - (int)JDR_FMT1; /* Err: code not found (may be collapted data) */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Extract N bits from input stream */ +/*-----------------------------------------------------------------------*/ + +static int bitext ( /* >=0: extracted data, <0: error code */ + JDEC* jd, /* Pointer to the decompressor object */ + unsigned int nbit /* Number of bits to extract (1 to 16) */ +) +{ + size_t dc = jd->dctr; + uint8_t *dp = jd->dptr; + unsigned int d, flg = 0; + +#if JD_FASTDECODE == 0 + uint8_t mbit = jd->dbit; + + d = 0; + do { + if (!mbit) { /* Next byte? */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } else { + dp++; /* Next data ptr */ + } + dc--; /* Decrement number of available bytes */ + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (*dp != 0) return 0 - (int)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ + *dp = 0xFF; /* The flag is a data 0xFF */ + } else { + if (*dp == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence */ + } + } + mbit = 0x80; /* Read from MSB */ + } + d <<= 1; /* Get a bit */ + if (*dp & mbit) d |= 1; + mbit >>= 1; + nbit--; + } while (nbit); + + jd->dbit = mbit; jd->dctr = dc; jd->dptr = dp; + return (int)d; + +#else + unsigned int wbit = jd->dbit % 32; + uint32_t w = jd->wreg & ((1UL << wbit) - 1); + + + while (wbit < nbit) { /* Prepare nbit bits into the working register */ + if (jd->marker) { + d = 0xFF; /* Input stream stalled, generate stuff bits */ + } else { + if (!dc) { /* Buffer empty, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } + d = *dp++; dc--; + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (d != 0) jd->marker = d; /* Not an escape of 0xFF but a marker */ + d = 0xFF; + } else { + if (d == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + } + w = w << 8 | d; /* Get 8 bits into the working register */ + wbit += 8; + } + jd->wreg = w; jd->dbit = wbit - nbit; + jd->dctr = dc; jd->dptr = dp; + + return (int)(w >> ((wbit - nbit) % 32)); +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Process restart interval */ +/*-----------------------------------------------------------------------*/ + +static JRESULT restart ( + JDEC* jd, /* Pointer to the decompressor object */ + uint16_t rstn /* Expected restert sequense number */ +) +{ + unsigned int i; + uint8_t *dp = jd->dptr; + size_t dc = jd->dctr; + +#if JD_FASTDECODE == 0 + uint16_t d = 0; + + /* Get two bytes from the input stream */ + for (i = 0; i < 2; i++) { + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return JDR_INP; + } else { + dp++; + } + dc--; + d = d << 8 | *dp; /* Get a byte */ + } + jd->dptr = dp; jd->dctr = dc; jd->dbit = 0; + + /* Check the marker */ + if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7)) { + return JDR_FMT1; /* Err: expected RSTn marker is not detected (may be collapted data) */ + } + +#else + uint16_t marker; + + + if (jd->marker) { /* Generate a maker if it has been detected */ + marker = 0xFF00 | jd->marker; + jd->marker = 0; + } else { + marker = 0; + for (i = 0; i < 2; i++) { /* Get a restart marker */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return JDR_INP; + } + marker = (marker << 8) | *dp++; /* Get a byte */ + dc--; + } + jd->dptr = dp; jd->dctr = dc; + } + + /* Check the marker */ + if ((marker & 0xFFD8) != 0xFFD0 || (marker & 7) != (rstn & 7)) { + return JDR_FMT1; /* Err: expected RSTn marker was not detected (may be collapted data) */ + } + + jd->dbit = 0; /* Discard stuff bits */ +#endif + + jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Reset DC offset */ + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */ +/*-----------------------------------------------------------------------*/ + +static void block_idct ( + int32_t* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */ + jd_yuv_t* dst /* Pointer to the destination to store the block as byte array */ +) +{ + const int32_t M13 = (int32_t)(1.41421*4096), M2 = (int32_t)(1.08239*4096), M4 = (int32_t)(2.61313*4096), M5 = (int32_t)(1.84776*4096); + int32_t v0, v1, v2, v3, v4, v5, v6, v7; + int32_t t10, t11, t12, t13; + int i; + + /* Process columns */ + for (i = 0; i < 8; i++) { + v0 = src[8 * 0]; /* Get even elements */ + v1 = src[8 * 2]; + v2 = src[8 * 4]; + v3 = src[8 * 6]; + + t10 = v0 + v2; /* Process the even elements */ + t12 = v0 - v2; + t11 = (v1 - v3) * M13 >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + v4 = src[8 * 7]; /* Get odd elements */ + v5 = src[8 * 1]; + v6 = src[8 * 5]; + v7 = src[8 * 3]; + + t10 = v5 - v4; /* Process the odd elements */ + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = (t11 - v7) * M13 >> 12; + v7 += t11; + t13 = (t10 + t12) * M5 >> 12; + v4 = t13 - (t10 * M2 >> 12); + v6 = t13 - (t12 * M4 >> 12) - v7; + v5 -= v6; + v4 -= v5; + + src[8 * 0] = v0 + v7; /* Write-back transformed values */ + src[8 * 7] = v0 - v7; + src[8 * 1] = v1 + v6; + src[8 * 6] = v1 - v6; + src[8 * 2] = v2 + v5; + src[8 * 5] = v2 - v5; + src[8 * 3] = v3 + v4; + src[8 * 4] = v3 - v4; + + src++; /* Next column */ + } + + /* Process rows */ + src -= 8; + for (i = 0; i < 8; i++) { + v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */ + v1 = src[2]; + v2 = src[4]; + v3 = src[6]; + + t10 = v0 + v2; /* Process the even elements */ + t12 = v0 - v2; + t11 = (v1 - v3) * M13 >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + v4 = src[7]; /* Get odd elements */ + v5 = src[1]; + v6 = src[5]; + v7 = src[3]; + + t10 = v5 - v4; /* Process the odd elements */ + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = (t11 - v7) * M13 >> 12; + v7 += t11; + t13 = (t10 + t12) * M5 >> 12; + v4 = t13 - (t10 * M2 >> 12); + v6 = t13 - (t12 * M4 >> 12) - v7; + v5 -= v6; + v4 -= v5; + + /* Descale the transformed values 8 bits and output a row */ +#if JD_FASTDECODE >= 1 + dst[0] = (int16_t)((v0 + v7) >> 8); + dst[7] = (int16_t)((v0 - v7) >> 8); + dst[1] = (int16_t)((v1 + v6) >> 8); + dst[6] = (int16_t)((v1 - v6) >> 8); + dst[2] = (int16_t)((v2 + v5) >> 8); + dst[5] = (int16_t)((v2 - v5) >> 8); + dst[3] = (int16_t)((v3 + v4) >> 8); + dst[4] = (int16_t)((v3 - v4) >> 8); +#else + dst[0] = BYTECLIP((v0 + v7) >> 8); + dst[7] = BYTECLIP((v0 - v7) >> 8); + dst[1] = BYTECLIP((v1 + v6) >> 8); + dst[6] = BYTECLIP((v1 - v6) >> 8); + dst[2] = BYTECLIP((v2 + v5) >> 8); + dst[5] = BYTECLIP((v2 - v5) >> 8); + dst[3] = BYTECLIP((v3 + v4) >> 8); + dst[4] = BYTECLIP((v3 - v4) >> 8); +#endif + + dst += 8; src += 8; /* Next row */ + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load all blocks in an MCU into working buffer */ +/*-----------------------------------------------------------------------*/ + +static JRESULT mcu_load ( + JDEC* jd /* Pointer to the decompressor object */ +) +{ + int32_t *tmp = (int32_t*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */ + int d, e; + unsigned int blk, nby, i, bc, z, id, cmp; + jd_yuv_t *bp; + const int32_t *dqf; + + + nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */ + bp = jd->mcubuf; /* Pointer to the first block of MCU */ + + for (blk = 0; blk < nby + 2; blk++) { /* Get nby Y blocks and two C blocks */ + cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */ + + if (cmp && jd->ncomp != 3) { /* Clear C blocks if not exist (monochrome image) */ + for (i = 0; i < 64; bp[i++] = 128) ; + + } else { /* Load Y/C blocks from input stream */ + id = cmp ? 1 : 0; /* Huffman table ID of this component */ + + /* Extract a DC element from input stream */ + d = huffext(jd, id, 0); /* Extract a huffman coded data (bit length) */ + if (d < 0) return (JRESULT)(0 - d); /* Err: invalid code or input */ + bc = (unsigned int)d; + d = jd->dcv[cmp]; /* DC value of previous block */ + if (bc) { /* If there is any difference from previous block */ + e = bitext(jd, bc); /* Extract data bits */ + if (e < 0) return (JRESULT)(0 - e); /* Err: input */ + bc = 1 << (bc - 1); /* MSB position */ + if (!(e & bc)) e -= (bc << 1) - 1; /* Restore negative value if needed */ + d += e; /* Get current value */ + jd->dcv[cmp] = (int16_t)d; /* Save current DC value for next block */ + } + dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */ + tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ + + /* Extract following 63 AC elements from input stream */ + memset(&tmp[1], 0, 63 * sizeof (int32_t)); /* Initialize all AC elements */ + z = 1; /* Top of the AC elements (in zigzag-order) */ + do { + d = huffext(jd, id, 1); /* Extract a huffman coded value (zero runs and bit length) */ + if (d == 0) break; /* EOB? */ + if (d < 0) return (JRESULT)(0 - d); /* Err: invalid code or input error */ + bc = (unsigned int)d; + z += bc >> 4; /* Skip leading zero run */ + if (z >= 64) return JDR_FMT1; /* Too long zero run */ + if (bc &= 0x0F) { /* Bit length? */ + d = bitext(jd, bc); /* Extract data bits */ + if (d < 0) return (JRESULT)(0 - d); /* Err: input device */ + bc = 1 << (bc - 1); /* MSB position */ + if (!(d & bc)) d -= (bc << 1) - 1; /* Restore negative value if needed */ + i = Zig[z]; /* Get raster-order index */ + tmp[i] = d * dqf[i] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ + } + } while (++z < 64); /* Next AC element */ + + if (JD_FORMAT != 2 || !cmp) { /* C components may not be processed if in grayscale output */ + if (z == 1 || (JD_USE_SCALE && jd->scale == 3)) { /* If no AC element or scale ratio is 1/8, IDCT can be ommited and the block is filled with DC value */ + d = (jd_yuv_t)((*tmp / 256) + 128); + if (JD_FASTDECODE >= 1) { + for (i = 0; i < 64; bp[i++] = d) ; + } else { + memset(bp, d, 64); + } + } else { + block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */ + } + } + } + + bp += 64; /* Next block */ + } + + return JDR_OK; /* All blocks have been loaded successfully */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */ +/*-----------------------------------------------------------------------*/ + +static JRESULT mcu_output ( + JDEC* jd, /* Pointer to the decompressor object */ + int (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ + unsigned int img_x, /* MCU location in the image */ + unsigned int img_y /* MCU location in the image */ +) +{ + const int CVACC = (sizeof (int) > 2) ? 1024 : 128; /* Adaptive accuracy for both 16-/32-bit systems */ + unsigned int ix, iy, mx, my, rx, ry; + int yy, cb, cr; + jd_yuv_t *py, *pc; + uint8_t *pix; + JRECT rect; + + + mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */ + rx = (img_x + mx <= jd->width) ? mx : jd->width - img_x; /* Output rectangular size (it may be clipped at right/bottom end of image) */ + ry = (img_y + my <= jd->height) ? my : jd->height - img_y; + if (JD_USE_SCALE) { + rx >>= jd->scale; ry >>= jd->scale; + if (!rx || !ry) return JDR_OK; /* Skip this MCU if all pixel is to be rounded off */ + img_x >>= jd->scale; img_y >>= jd->scale; + } + rect.left = img_x; rect.right = img_x + rx - 1; /* Rectangular area in the frame buffer */ + rect.top = img_y; rect.bottom = img_y + ry - 1; + + + if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */ + pix = (uint8_t*)jd->workbuf; + + if (JD_FORMAT != 2) { /* RGB output (build an RGB MCU from Y/C component) */ + for (iy = 0; iy < my; iy++) { + pc = py = jd->mcubuf; + if (my == 16) { /* Double block height? */ + pc += 64 * 4 + (iy >> 1) * 8; + if (iy >= 8) py += 64; + } else { /* Single block height */ + pc += mx * 8 + iy * 8; + } + py += iy * 8; + for (ix = 0; ix < mx; ix++) { + cb = pc[0] - 128; /* Get Cb/Cr component and remove offset */ + cr = pc[64] - 128; + if (mx == 16) { /* Double block width? */ + if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */ + pc += ix & 1; /* Step forward chroma pointer every two pixels */ + } else { /* Single block width */ + pc++; /* Step forward chroma pointer every pixel */ + } + yy = *py++; /* Get Y component */ + *pix++ = /*R*/ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr) / CVACC); + *pix++ = /*G*/ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC); + *pix++ = /*B*/ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb) / CVACC); + } + } + } else { /* Monochrome output (build a grayscale MCU from Y comopnent) */ + for (iy = 0; iy < my; iy++) { + py = jd->mcubuf + iy * 8; + if (my == 16) { /* Double block height? */ + if (iy >= 8) py += 64; + } + for (ix = 0; ix < mx; ix++) { + if (mx == 16) { /* Double block width? */ + if (ix == 8) py += 64 - 8; /* Jump to next block if double block height */ + } + *pix++ = (uint8_t)*py++; /* Get and store a Y value as grayscale */ + } + } + } + + /* Descale the MCU rectangular if needed */ + if (JD_USE_SCALE && jd->scale) { + unsigned int x, y, r, g, b, s, w, a; + uint8_t *op; + + /* Get averaged RGB value of each square correcponds to a pixel */ + s = jd->scale * 2; /* Number of shifts for averaging */ + w = 1 << jd->scale; /* Width of square */ + a = (mx - w) * (JD_FORMAT != 2 ? 3 : 1); /* Bytes to skip for next line in the square */ + op = (uint8_t*)jd->workbuf; + for (iy = 0; iy < my; iy += w) { + for (ix = 0; ix < mx; ix += w) { + pix = (uint8_t*)jd->workbuf + (iy * mx + ix) * (JD_FORMAT != 2 ? 3 : 1); + r = g = b = 0; + for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */ + for (x = 0; x < w; x++) { + r += *pix++; /* Accumulate R or Y (monochrome output) */ + if (JD_FORMAT != 2) { /* RGB output? */ + g += *pix++; /* Accumulate G */ + b += *pix++; /* Accumulate B */ + } + } + pix += a; + } /* Put the averaged pixel value */ + *op++ = (uint8_t)(r >> s); /* Put R or Y (monochrome output) */ + if (JD_FORMAT != 2) { /* RGB output? */ + *op++ = (uint8_t)(g >> s); /* Put G */ + *op++ = (uint8_t)(b >> s); /* Put B */ + } + } + } + } + + } else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */ + + /* Build a 1/8 descaled RGB MCU from discrete comopnents */ + pix = (uint8_t*)jd->workbuf; + pc = jd->mcubuf + mx * my; + cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */ + cr = pc[64] - 128; + for (iy = 0; iy < my; iy += 8) { + py = jd->mcubuf; + if (iy == 8) py += 64 * 2; + for (ix = 0; ix < mx; ix += 8) { + yy = *py; /* Get Y component */ + py += 64; + if (JD_FORMAT != 2) { + *pix++ = /*R*/ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr / CVACC)); + *pix++ = /*G*/ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC); + *pix++ = /*B*/ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb / CVACC)); + } else { + *pix++ = yy; + } + } + } + } + + /* Squeeze up pixel table if a part of MCU is to be truncated */ + mx >>= jd->scale; + if (rx < mx) { /* Is the MCU spans rigit edge? */ + uint8_t *s, *d; + unsigned int x, y; + + s = d = (uint8_t*)jd->workbuf; + for (y = 0; y < ry; y++) { + for (x = 0; x < rx; x++) { /* Copy effective pixels */ + *d++ = *s++; + if (JD_FORMAT != 2) { + *d++ = *s++; + *d++ = *s++; + } + } + s += (mx - rx) * (JD_FORMAT != 2 ? 3 : 1); /* Skip truncated pixels */ + } + } + + /* Convert RGB888 to RGB565 if needed */ + if (JD_FORMAT == 1) { + uint8_t *s = (uint8_t*)jd->workbuf; + uint16_t w, *d = (uint16_t*)s; + unsigned int n = rx * ry; + + do { + w = (*s++ & 0xF8) << 8; /* RRRRR----------- */ + w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */ + w |= *s++ >> 3; /* -----------BBBBB */ + *d++ = w; + } while (--n); + } + + /* Output the rectangular */ + return outfunc(jd, jd->workbuf, &rect) ? JDR_OK : JDR_INTR; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Analyze the JPEG image and Initialize decompressor object */ +/*-----------------------------------------------------------------------*/ + +#define LDB_WORD(ptr) (uint16_t)(((uint16_t)*((uint8_t*)(ptr))<<8)|(uint16_t)*(uint8_t*)((ptr)+1)) + + +JRESULT jd_prepare ( + JDEC* jd, /* Blank decompressor object */ + size_t (*infunc)(JDEC*, uint8_t*, size_t), /* JPEG strem input function */ + void* pool, /* Working buffer for the decompression session */ + size_t sz_pool, /* Size of working buffer */ + void* dev /* I/O device identifier for the session */ +) +{ + uint8_t *seg, b; + uint16_t marker; + unsigned int n, i, ofs; + size_t len; + JRESULT rc; + + + memset(jd, 0, sizeof (JDEC)); /* Clear decompression object (this might be a problem if machine's null pointer is not all bits zero) */ + jd->pool = pool; /* Work memroy */ + jd->sz_pool = sz_pool; /* Size of given work memory */ + jd->infunc = infunc; /* Stream input function */ + jd->device = dev; /* I/O device identifier */ + + jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */ + if (!seg) return JDR_MEM1; + + ofs = marker = 0; /* Find SOI marker */ + do { + if (jd->infunc(jd, seg, 1) != 1) return JDR_INP; /* Err: SOI was not detected */ + ofs++; + marker = marker << 8 | seg[0]; + } while (marker != 0xFFD8); + + for (;;) { /* Parse JPEG segments */ + /* Get a JPEG marker */ + if (jd->infunc(jd, seg, 4) != 4) return JDR_INP; + marker = LDB_WORD(seg); /* Marker */ + len = LDB_WORD(seg + 2); /* Length field */ + if (len <= 2 || (marker >> 8) != 0xFF) return JDR_FMT1; + len -= 2; /* Segent content size */ + ofs += 4 + len; /* Number of bytes loaded */ + + switch (marker & 0xFF) { + case 0xC0: /* SOF0 (baseline JPEG) */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + jd->width = LDB_WORD(&seg[3]); /* Image width in unit of pixel */ + jd->height = LDB_WORD(&seg[1]); /* Image height in unit of pixel */ + jd->ncomp = seg[5]; /* Number of color components */ + if (jd->ncomp != 3 && jd->ncomp != 1) return JDR_FMT3; /* Err: Supports only Grayscale and Y/Cb/Cr */ + + /* Check each image component */ + for (i = 0; i < jd->ncomp; i++) { + b = seg[7 + 3 * i]; /* Get sampling factor */ + if (i == 0) { /* Y component */ + if (b != 0x11 && b != 0x22 && b != 0x21) { /* Check sampling factor */ + return JDR_FMT3; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */ + } + jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */ + } else { /* Cb/Cr component */ + if (b != 0x11) return JDR_FMT3; /* Err: Sampling factor of Cb/Cr must be 1 */ + } + jd->qtid[i] = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */ + if (jd->qtid[i] > 3) return JDR_FMT3; /* Err: Invalid ID */ + } + break; + + case 0xDD: /* DRI - Define Restart Interval */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + jd->nrst = LDB_WORD(seg); /* Get restart interval (MCUs) */ + break; + + case 0xC4: /* DHT - Define Huffman Tables */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + rc = create_huffman_tbl(jd, seg, len); /* Create huffman tables */ + if (rc) return rc; + break; + + case 0xDB: /* DQT - Define Quaitizer Tables */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + rc = create_qt_tbl(jd, seg, len); /* Create de-quantizer tables */ + if (rc) return rc; + break; + + case 0xDA: /* SOS - Start of Scan */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + if (!jd->width || !jd->height) return JDR_FMT1; /* Err: Invalid image size */ + if (seg[0] != jd->ncomp) return JDR_FMT3; /* Err: Wrong color components */ + + /* Check if all tables corresponding to each components have been loaded */ + for (i = 0; i < jd->ncomp; i++) { + b = seg[2 + 2 * i]; /* Get huffman table ID */ + if (b != 0x00 && b != 0x11) return JDR_FMT3; /* Err: Different table number for DC/AC element */ + n = i ? 1 : 0; /* Component class */ + if (!jd->huffbits[n][0] || !jd->huffbits[n][1]) { /* Check huffman table for this component */ + return JDR_FMT1; /* Err: Nnot loaded */ + } + if (!jd->qttbl[jd->qtid[i]]) { /* Check dequantizer table for this component */ + return JDR_FMT1; /* Err: Not loaded */ + } + } + + /* Allocate working buffer for MCU and pixel output */ + n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */ + if (!n) return JDR_FMT1; /* Err: SOF0 has not been loaded */ + len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */ + if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */ + jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */ + if (!jd->workbuf) return JDR_MEM1; /* Err: not enough memory */ + jd->mcubuf = alloc_pool(jd, (n + 2) * 64 * sizeof (jd_yuv_t)); /* Allocate MCU working buffer */ + if (!jd->mcubuf) return JDR_MEM1; /* Err: not enough memory */ + + /* Align stream read offset to JD_SZBUF */ + if (ofs %= JD_SZBUF) { + jd->dctr = jd->infunc(jd, seg + ofs, (size_t)(JD_SZBUF - ofs)); + } + jd->dptr = seg + ofs - (JD_FASTDECODE ? 0 : 1); + + return JDR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */ + + case 0xC1: /* SOF1 */ + case 0xC2: /* SOF2 */ + case 0xC3: /* SOF3 */ + case 0xC5: /* SOF5 */ + case 0xC6: /* SOF6 */ + case 0xC7: /* SOF7 */ + case 0xC9: /* SOF9 */ + case 0xCA: /* SOF10 */ + case 0xCB: /* SOF11 */ + case 0xCD: /* SOF13 */ + case 0xCE: /* SOF14 */ + case 0xCF: /* SOF15 */ + case 0xD9: /* EOI */ + return JDR_FMT3; /* Unsuppoted JPEG standard (may be progressive JPEG) */ + + default: /* Unknown segment (comment, exif or etc..) */ + /* Skip segment data (null pointer specifies to remove data from the stream) */ + if (jd->infunc(jd, 0, len) != len) return JDR_INP; + } + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Start to decompress the JPEG picture */ +/*-----------------------------------------------------------------------*/ + +JRESULT jd_decomp ( + JDEC* jd, /* Initialized decompression object */ + int (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ + uint8_t scale /* Output de-scaling factor (0 to 3) */ +) +{ + unsigned int x, y, mx, my; + uint16_t rst, rsc; + JRESULT rc; + + + if (scale > (JD_USE_SCALE ? 3 : 0)) return JDR_PAR; + jd->scale = scale; + + mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */ + + jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */ + rst = rsc = 0; + + rc = JDR_OK; + for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */ + for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */ + if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */ + rc = restart(jd, rsc++); + if (rc != JDR_OK) return rc; + rst = 1; + } + rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream, dequantize and apply IDCT) */ + if (rc != JDR_OK) return rc; + rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (YCbCr to RGB, scaling and output) */ + if (rc != JDR_OK) return rc; + } + } + + return rc; +} + +#endif /*LV_USE_SJPG*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.h new file mode 100644 index 000000000..b255ccfcf --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgd.h @@ -0,0 +1,93 @@ +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03 include file (C)ChaN, 2021 +/----------------------------------------------------------------------------*/ +#ifndef DEF_TJPGDEC +#define DEF_TJPGDEC + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../../lv_conf_internal.h" +#if LV_USE_SJPG + +#include "tjpgdcnf.h" +#include +#include + +#if JD_FASTDECODE >= 1 +typedef int16_t jd_yuv_t; +#else +typedef uint8_t jd_yuv_t; +#endif + + +/* Error code */ +typedef enum { + JDR_OK = 0, /* 0: Succeeded */ + JDR_INTR, /* 1: Interrupted by output function */ + JDR_INP, /* 2: Device error or wrong termination of input stream */ + JDR_MEM1, /* 3: Insufficient memory pool for the image */ + JDR_MEM2, /* 4: Insufficient stream input buffer */ + JDR_PAR, /* 5: Parameter error */ + JDR_FMT1, /* 6: Data format error (may be broken data) */ + JDR_FMT2, /* 7: Right format but not supported */ + JDR_FMT3 /* 8: Not supported JPEG standard */ +} JRESULT; + +/* Rectangular region in the output image */ +typedef struct { + uint16_t left; /* Left end */ + uint16_t right; /* Right end */ + uint16_t top; /* Top end */ + uint16_t bottom; /* Bottom end */ +} JRECT; + +/* Decompressor object structure */ +typedef struct JDEC JDEC; +struct JDEC { + size_t dctr; /* Number of bytes available in the input buffer */ + uint8_t* dptr; /* Current data read ptr */ + uint8_t* inbuf; /* Bit stream input buffer */ + uint8_t dbit; /* Number of bits availavble in wreg or reading bit mask */ + uint8_t scale; /* Output scaling ratio */ + uint8_t msx, msy; /* MCU size in unit of block (width, height) */ + uint8_t qtid[3]; /* Quantization table ID of each component, Y, Cb, Cr */ + uint8_t ncomp; /* Number of color components 1:grayscale, 3:color */ + int16_t dcv[3]; /* Previous DC element of each component */ + uint16_t nrst; /* Restart inverval */ + uint16_t width, height; /* Size of the input image (pixel) */ + uint8_t* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */ + uint16_t* huffcode[2][2]; /* Huffman code word tables [id][dcac] */ + uint8_t* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */ + int32_t* qttbl[4]; /* Dequantizer tables [id] */ +#if JD_FASTDECODE >= 1 + uint32_t wreg; /* Working shift register */ + uint8_t marker; /* Detected marker (0:None) */ +#if JD_FASTDECODE == 2 + uint8_t longofs[2][2]; /* Table offset of long code [id][dcac] */ + uint16_t* hufflut_ac[2]; /* Fast huffman decode tables for AC short code [id] */ + uint8_t* hufflut_dc[2]; /* Fast huffman decode tables for DC short code [id] */ +#endif +#endif + void* workbuf; /* Working buffer for IDCT and RGB output */ + jd_yuv_t* mcubuf; /* Working buffer for the MCU */ + void* pool; /* Pointer to available memory pool */ + size_t sz_pool; /* Size of momory pool (bytes available) */ + size_t (*infunc)(JDEC*, uint8_t*, size_t); /* Pointer to jpeg stream input function */ + void* device; /* Pointer to I/O device identifiler for the session */ +}; + + + +/* TJpgDec API functions */ +JRESULT jd_prepare (JDEC* jd, size_t (*infunc)(JDEC*,uint8_t*,size_t), void* pool, size_t sz_pool, void* dev); +JRESULT jd_decomp (JDEC* jd, int (*outfunc)(JDEC*,void*,JRECT*), uint8_t scale); + +#endif /*LV_USE_SJPG*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _TJPGDEC */ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgdcnf.h b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgdcnf.h new file mode 100644 index 000000000..6d425e6f1 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/libs/sjpg/tjpgdcnf.h @@ -0,0 +1,33 @@ +/*----------------------------------------------*/ +/* TJpgDec System Configurations R0.03 */ +/*----------------------------------------------*/ + +#define JD_SZBUF 512 +/* Specifies size of stream input buffer */ + +#define JD_FORMAT 0 +/* Specifies output pixel format. +/ 0: RGB888 (24-bit/pix) +/ 1: RGB565 (16-bit/pix) +/ 2: Grayscale (8-bit/pix) +*/ + +#define JD_USE_SCALE 1 +/* Switches output descaling feature. +/ 0: Disable +/ 1: Enable +*/ + +#define JD_TBLCLIP 1 +/* Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size. +/ 0: Disable +/ 1: Enable +*/ + +#define JD_FASTDECODE 0 +/* Optimization level +/ 0: Basic optimization. Suitable for 8/16-bit MCUs. +/ 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs. +/ 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM) +*/ + diff --git a/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.c b/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.c index b6ed79544..e7c604103 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.c @@ -42,6 +42,10 @@ void lv_extra_init(void) lv_grid_init(); #endif +#if LV_USE_MSG + lv_msg_init(); +#endif + #if LV_USE_FS_FATFS != '\0' lv_fs_fatfs_init(); #endif @@ -58,6 +62,10 @@ void lv_extra_init(void) lv_fs_win32_init(); #endif +#if LV_USE_FFMPEG + lv_ffmpeg_init(); +#endif + #if LV_USE_PNG lv_png_init(); #endif @@ -71,6 +79,7 @@ void lv_extra_init(void) #endif // TASMOTA Specific, the initialization is done in Tasmota code to adjust with PSRAM + // #if LV_USE_FREETYPE // /*Init freetype library*/ // # if LV_FREETYPE_CACHE_SIZE >= 0 @@ -79,10 +88,6 @@ void lv_extra_init(void) // lv_freetype_init(0, 0, 0); // # endif // #endif - -#if LV_USE_FFMPEG - lv_ffmpeg_init(); -#endif } /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.h b/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.h index ea9ce381b..c0306a980 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.h @@ -14,6 +14,12 @@ extern "C" { * INCLUDES *********************/ +#include "layouts/lv_layouts.h" +#include "libs/lv_libs.h" +#include "others/lv_others.h" +#include "themes/lv_themes.h" +#include "widgets/lv_widgets.h" + /********************* * DEFINES *********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/extra.mk b/lib/libesp32_lvgl/lvgl/src/extra/lv_extra.mk similarity index 100% rename from lib/libesp32_lvgl/lvgl/src/extra/extra.mk rename to lib/libesp32_lvgl/lvgl/src/extra/lv_extra.mk diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/README.md b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.c b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.c new file mode 100644 index 000000000..a2cdfadc2 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.c @@ -0,0 +1,144 @@ +/** + * @file lv_fragment.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_fragment.h" + +#if LV_USE_FRAGMENT + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void cb_delete_assertion(lv_event_t * event); + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args) +{ + LV_ASSERT_NULL(cls); + LV_ASSERT_NULL(cls->create_obj_cb); + LV_ASSERT(cls->instance_size > 0); + lv_fragment_t * instance = lv_mem_alloc(cls->instance_size); + lv_memset_00(instance, cls->instance_size); + instance->cls = cls; + instance->child_manager = lv_fragment_manager_create(instance); + if(cls->constructor_cb) { + cls->constructor_cb(instance, args); + } + return instance; +} + +void lv_fragment_del(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + if(fragment->managed) { + lv_fragment_manager_remove(fragment->managed->manager, fragment); + return; + } + if(fragment->obj) { + lv_fragment_del_obj(fragment); + } + /* Objects will leak if this function called before objects deleted */ + const lv_fragment_class_t * cls = fragment->cls; + if(cls->destructor_cb) { + cls->destructor_cb(fragment); + } + lv_fragment_manager_del(fragment->child_manager); + lv_mem_free(fragment); +} + +lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + LV_ASSERT_NULL(fragment->managed); + return fragment->managed->manager; +} + +lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + LV_ASSERT_NULL(fragment->managed); + return fragment->managed->container; +} + +lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + LV_ASSERT_NULL(fragment->managed); + return lv_fragment_manager_get_parent_fragment(fragment->managed->manager); +} + +lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container) +{ + lv_fragment_managed_states_t * states = fragment->managed; + if(states) { + states->destroying_obj = false; + } + const lv_fragment_class_t * cls = fragment->cls; + lv_obj_t * obj = cls->create_obj_cb(fragment, container); + LV_ASSERT_NULL(obj); + fragment->obj = obj; + lv_fragment_manager_create_obj(fragment->child_manager); + if(states) { + states->obj_created = true; + lv_obj_add_event_cb(obj, cb_delete_assertion, LV_EVENT_DELETE, NULL); + } + if(cls->obj_created_cb) { + cls->obj_created_cb(fragment, obj); + } + return obj; +} + +void lv_fragment_del_obj(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + lv_fragment_manager_del_obj(fragment->child_manager); + lv_fragment_managed_states_t * states = fragment->managed; + if(states) { + if(!states->obj_created) return; + states->destroying_obj = true; + bool cb_removed = lv_obj_remove_event_cb(fragment->obj, cb_delete_assertion); + LV_ASSERT(cb_removed); + } + LV_ASSERT_NULL(fragment->obj); + const lv_fragment_class_t * cls = fragment->cls; + if(cls->obj_will_delete_cb) { + cls->obj_will_delete_cb(fragment, fragment->obj); + } + lv_obj_del(fragment->obj); + if(cls->obj_deleted_cb) { + cls->obj_deleted_cb(fragment, fragment->obj); + } + if(states) { + states->obj_created = false; + } + fragment->obj = NULL; +} + +void lv_fragment_recreate_obj(lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(fragment); + LV_ASSERT_NULL(fragment->managed); + lv_fragment_del_obj(fragment); + lv_fragment_create_obj(fragment, *fragment->managed->container); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void cb_delete_assertion(lv_event_t * event) +{ + LV_UNUSED(event); + LV_ASSERT_MSG(0, "Please delete objects with lv_fragment_destroy_obj"); +} + +#endif /*LV_USE_FRAGMENT*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.h b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.h new file mode 100644 index 000000000..da30b39a5 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment.h @@ -0,0 +1,339 @@ +/** + * Public header for Fragment + * @file lv_fragment.h + */ + +#ifndef LV_FRAGMENT_H +#define LV_FRAGMENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" + +#if LV_USE_FRAGMENT + +#include "../../../core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct _lv_fragment_manager_t lv_fragment_manager_t; + +typedef struct _lv_fragment_t lv_fragment_t; +typedef struct _lv_fragment_class_t lv_fragment_class_t; +typedef struct _lv_fragment_managed_states_t lv_fragment_managed_states_t; + +struct _lv_fragment_t { + /** + * Class of this fragment + */ + const lv_fragment_class_t * cls; + /** + * Managed fragment states. If not null, then this fragment is managed. + * + * @warning Don't modify values inside this struct! + */ + lv_fragment_managed_states_t * managed; + /** + * Child fragment manager + */ + lv_fragment_manager_t * child_manager; + /** + * lv_obj returned by create_obj_cb + */ + lv_obj_t * obj; + +}; + +struct _lv_fragment_class_t { + /** + * Constructor function for fragment class + * @param self Fragment instance + * @param args Arguments assigned by fragment manager + */ + void (*constructor_cb)(lv_fragment_t * self, void * args); + + /** + * Destructor function for fragment class + * @param self Fragment instance, will be freed after this call + */ + void (*destructor_cb)(lv_fragment_t * self); + + /** + * Fragment attached to manager + * @param self Fragment instance + */ + void (*attached_cb)(lv_fragment_t * self); + + /** + * Fragment detached from manager + * @param self Fragment instance + */ + void (*detached_cb)(lv_fragment_t * self); + + /** + * Create objects + * @param self Fragment instance + * @param container Container of the objects should be created upon + * @return Created object, NULL if multiple objects has been created + */ + lv_obj_t * (*create_obj_cb)(lv_fragment_t * self, lv_obj_t * container); + + /** + * + * @param self Fragment instance + * @param obj lv_obj returned by create_obj_cb + */ + void (*obj_created_cb)(lv_fragment_t * self, lv_obj_t * obj); + + /** + * Called before objects in the fragment will be deleted. + * + * @param self Fragment instance + * @param obj object with this fragment + */ + void (*obj_will_delete_cb)(lv_fragment_t * self, lv_obj_t * obj); + + /** + * Called when the object created by fragment received `LV_EVENT_DELETE` event + * @param self Fragment instance + * @param obj object with this fragment + */ + void (*obj_deleted_cb)(lv_fragment_t * self, lv_obj_t * obj); + + /** + * Handle event + * @param self Fragment instance + * @param which User-defined ID of event + * @param data1 User-defined data + * @param data2 User-defined data + */ + bool (*event_cb)(lv_fragment_t * self, int code, void * userdata); + + /** + * *REQUIRED*: Allocation size of fragment + */ + size_t instance_size; +}; + +/** + * Fragment states + */ +typedef struct _lv_fragment_managed_states_t { + /** + * Class of the fragment + */ + const lv_fragment_class_t * cls; + /** + * Manager the fragment attached to + */ + lv_fragment_manager_t * manager; + /** + * Container object the fragment adding view to + */ + lv_obj_t * const * container; + /** + * Fragment instance + */ + lv_fragment_t * instance; + /** + * true between `create_obj_cb` and `obj_deleted_cb` + */ + bool obj_created; + /** + * true before `lv_fragment_del_obj` is called. Don't touch any object if this is true + */ + bool destroying_obj; + /** + * true if this fragment is in navigation stack that can be popped + */ + bool in_stack; +} lv_fragment_managed_states_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create fragment manager instance + * @param parent Parent fragment if this manager is placed inside another fragment, can be null. + * @return Fragment manager instance + */ +lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent); + +/** + * Destroy fragment manager instance + * @param manager Fragment manager instance + */ +void lv_fragment_manager_del(lv_fragment_manager_t * manager); + +/** + * Create object of all fragments managed by this manager. + * @param manager Fragment manager instance + */ +void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager); + +/** + * Delete object created by all fragments managed by this manager. Instance of fragments will not be deleted. + * @param manager Fragment manager instance + */ +void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager); + +/** + * Attach fragment to manager, and add to container. + * @param manager Fragment manager instance + * @param fragment Fragment instance + * @param container Pointer to container object for manager to add objects to + */ +void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container); + +/** + * Detach and destroy fragment. If fragment is in navigation stack, remove from it. + * @param manager Fragment manager instance + * @param fragment Fragment instance + */ +void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment); + +/** + * Attach fragment to manager and add to navigation stack. + * @param manager Fragment manager instance + * @param fragment Fragment instance + * @param container Pointer to container object for manager to add objects to + */ +void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container); + +/** + * Remove the top-most fragment for stack + * @param manager Fragment manager instance + * @return true if there is fragment to pop + */ +bool lv_fragment_manager_pop(lv_fragment_manager_t * manager); + +/** + * Replace fragment. Old item in the stack will be removed. + * @param manager Fragment manager instance + * @param fragment Fragment instance + * @param container Pointer to container object for manager to add objects to + */ +void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment, + lv_obj_t * const * container); + +/** + * Send event to top-most fragment + * @param manager Fragment manager instance + * @param code User-defined ID of event + * @param userdata User-defined data + * @return true if fragment returned true + */ +bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata); + +/** + * Get stack size of this fragment manager + * @param manager Fragment manager instance + * @return Stack size of this fragment manager + */ +size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager); + +/** + * Get top most fragment instance + * @param manager Fragment manager instance + * @return Top most fragment instance + */ +lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager); + +/** + * Find first fragment instance in the container + * @param manager Fragment manager instance + * @param container Container which target fragment added to + * @return First fragment instance in the container + */ +lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container); + +/** + * Get parent fragment + * @param manager Fragment manager instance + * @return Parent fragment instance + */ +lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager); + + +/** + * Create a fragment instance. + * + * @param cls Fragment class. This fragment must return non null object. + * @param args Arguments assigned by fragment manager + * @return Fragment instance + */ +lv_fragment_t * lv_fragment_create(const lv_fragment_class_t * cls, void * args); + +/** + * Destroy a fragment. + * @param fragment Fragment instance. + */ +void lv_fragment_del(lv_fragment_t * fragment); + +/** + * Get associated manager of this fragment + * @param fragment Fragment instance + * @return Fragment manager instance + */ +lv_fragment_manager_t * lv_fragment_get_manager(lv_fragment_t * fragment); + +/** + * Get container object of this fragment + * @param fragment Fragment instance + * @return Reference to container object + */ +lv_obj_t * const * lv_fragment_get_container(lv_fragment_t * fragment); + +/** + * Get parent fragment of this fragment + * @param fragment Fragment instance + * @return Parent fragment + */ +lv_fragment_t * lv_fragment_get_parent(lv_fragment_t * fragment); + +/** + * Create object by fragment. + * + * @param fragment Fragment instance. + * @param container Container of the objects should be created upon. + * @return Created object + */ +lv_obj_t * lv_fragment_create_obj(lv_fragment_t * fragment, lv_obj_t * container); + +/** + * Delete created object of a fragment + * + * @param fragment Fragment instance. + */ +void lv_fragment_del_obj(lv_fragment_t * fragment); + +/** + * Destroy obj in fragment, and recreate them. + * @param fragment Fragment instance + */ +void lv_fragment_recreate_obj(lv_fragment_t * fragment); + + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_FRAGMENT*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_FRAGMENT_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment_manager.c b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment_manager.c new file mode 100644 index 000000000..e8ee84f49 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/fragment/lv_fragment_manager.c @@ -0,0 +1,280 @@ +/** + * @file lv_fragment_manager.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_fragment.h" + +#if LV_USE_FRAGMENT + +#include "../../../misc/lv_ll.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct _lv_fragment_stack_item_t { + lv_fragment_managed_states_t * states; +} lv_fragment_stack_item_t; + +struct _lv_fragment_manager_t { + lv_fragment_t * parent; + /** + * Linked list to store attached fragments + */ + lv_ll_t attached; + /** + * Linked list to store fragments in stack + */ + lv_ll_t stack; +}; + + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void item_create_obj(lv_fragment_managed_states_t * item); + +static void item_del_obj(lv_fragment_managed_states_t * item); + +static void item_del_fragment(lv_fragment_managed_states_t * item); + +static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment, + lv_obj_t * const * container); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_fragment_manager_t * lv_fragment_manager_create(lv_fragment_t * parent) +{ + lv_fragment_manager_t * instance = lv_mem_alloc(sizeof(lv_fragment_manager_t)); + lv_memset_00(instance, sizeof(lv_fragment_manager_t)); + instance->parent = parent; + _lv_ll_init(&instance->attached, sizeof(lv_fragment_managed_states_t)); + _lv_ll_init(&instance->stack, sizeof(lv_fragment_stack_item_t)); + return instance; +} + +void lv_fragment_manager_del(lv_fragment_manager_t * manager) +{ + LV_ASSERT_NULL(manager); + lv_fragment_managed_states_t * states; + _LV_LL_READ_BACK(&manager->attached, states) { + item_del_obj(states); + item_del_fragment(states); + } + _lv_ll_clear(&manager->attached); + _lv_ll_clear(&manager->stack); + lv_mem_free(manager); +} + +void lv_fragment_manager_create_obj(lv_fragment_manager_t * manager) +{ + LV_ASSERT_NULL(manager); + lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack); + lv_fragment_managed_states_t * states = NULL; + _LV_LL_READ(&manager->attached, states) { + if(states->in_stack && top->states != states) { + /*Only create obj for top item in stack*/ + continue; + } + item_create_obj(states); + } +} + +void lv_fragment_manager_del_obj(lv_fragment_manager_t * manager) +{ + LV_ASSERT_NULL(manager); + lv_fragment_managed_states_t * states = NULL; + _LV_LL_READ_BACK(&manager->attached, states) { + item_del_obj(states); + } +} + +void lv_fragment_manager_add(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container) +{ + lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container); + if(!manager->parent || manager->parent->managed->obj_created) { + item_create_obj(states); + } +} + +void lv_fragment_manager_remove(lv_fragment_manager_t * manager, lv_fragment_t * fragment) +{ + LV_ASSERT_NULL(manager); + LV_ASSERT_NULL(fragment); + LV_ASSERT_NULL(fragment->managed); + LV_ASSERT(fragment->managed->manager == manager); + lv_fragment_managed_states_t * states = fragment->managed; + lv_fragment_managed_states_t * prev = NULL; + bool was_top = false; + if(states->in_stack) { + void * stack_top = _lv_ll_get_tail(&manager->stack); + lv_fragment_stack_item_t * item = NULL; + _LV_LL_READ_BACK(&manager->stack, item) { + if(item->states == states) { + was_top = stack_top == item; + void * stack_prev = _lv_ll_get_prev(&manager->stack, item); + if(!stack_prev) break; + prev = ((lv_fragment_stack_item_t *) stack_prev)->states; + break; + } + } + if(item) { + _lv_ll_remove(&manager->stack, item); + lv_mem_free(item); + } + } + item_del_obj(states); + item_del_fragment(states); + _lv_ll_remove(&manager->attached, states); + lv_mem_free(states); + if(prev && was_top) { + item_create_obj(prev); + } +} + +void lv_fragment_manager_push(lv_fragment_manager_t * manager, lv_fragment_t * fragment, lv_obj_t * const * container) +{ + lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack); + if(top != NULL) { + item_del_obj(top->states); + } + lv_fragment_managed_states_t * states = fragment_attach(manager, fragment, container); + states->in_stack = true; + /*Add fragment to the top of the stack*/ + lv_fragment_stack_item_t * item = _lv_ll_ins_tail(&manager->stack); + lv_memset_00(item, sizeof(lv_fragment_stack_item_t)); + item->states = states; + item_create_obj(states); +} + +bool lv_fragment_manager_pop(lv_fragment_manager_t * manager) +{ + lv_fragment_t * top = lv_fragment_manager_get_top(manager); + if(top == NULL) return false; + lv_fragment_manager_remove(manager, top); + return true; +} + +void lv_fragment_manager_replace(lv_fragment_manager_t * manager, lv_fragment_t * fragment, + lv_obj_t * const * container) +{ + lv_fragment_t * top = lv_fragment_manager_find_by_container(manager, *container); + if(top != NULL) { + lv_fragment_manager_remove(manager, top); + } + lv_fragment_manager_add(manager, fragment, container); +} + +bool lv_fragment_manager_send_event(lv_fragment_manager_t * manager, int code, void * userdata) +{ + LV_ASSERT_NULL(manager); + lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack); + if(!top) return false; + lv_fragment_managed_states_t * states = top->states; + lv_fragment_t * instance = states->instance; + if(!instance) return false; + if(lv_fragment_manager_send_event(instance->child_manager, code, userdata)) return true; + if(!states->cls->event_cb) return false; + return states->cls->event_cb(instance, code, userdata); +} + +size_t lv_fragment_manager_get_stack_size(lv_fragment_manager_t * manager) +{ + LV_ASSERT_NULL(manager); + return _lv_ll_get_len(&manager->stack); +} + +lv_fragment_t * lv_fragment_manager_get_top(lv_fragment_manager_t * manager) +{ + LV_ASSERT(manager); + lv_fragment_stack_item_t * top = _lv_ll_get_tail(&manager->stack); + if(!top)return NULL; + return top->states->instance; +} + +lv_fragment_t * lv_fragment_manager_find_by_container(lv_fragment_manager_t * manager, const lv_obj_t * container) +{ + LV_ASSERT(manager); + lv_fragment_managed_states_t * states; + _LV_LL_READ(&manager->attached, states) { + if(*states->container == container) return states->instance; + } + return NULL; +} + +lv_fragment_t * lv_fragment_manager_get_parent_fragment(lv_fragment_manager_t * manager) +{ + LV_ASSERT_NULL(manager); + return manager->parent; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void item_create_obj(lv_fragment_managed_states_t * item) +{ + LV_ASSERT(item->instance); + lv_fragment_create_obj(item->instance, item->container ? *item->container : NULL); +} + +static void item_del_obj(lv_fragment_managed_states_t * item) +{ + lv_fragment_del_obj(item->instance); +} + +/** + * Detach, then destroy fragment + * @param item fragment states + */ +static void item_del_fragment(lv_fragment_managed_states_t * item) +{ + lv_fragment_t * instance = item->instance; + if(instance->cls->detached_cb) { + instance->cls->detached_cb(instance); + } + instance->managed = NULL; + lv_fragment_del(instance); + item->instance = NULL; +} + + +static lv_fragment_managed_states_t * fragment_attach(lv_fragment_manager_t * manager, lv_fragment_t * fragment, + lv_obj_t * const * container) +{ + LV_ASSERT(manager); + LV_ASSERT(fragment); + LV_ASSERT(fragment->managed == NULL); + lv_fragment_managed_states_t * states = _lv_ll_ins_tail(&manager->attached); + lv_memset_00(states, sizeof(lv_fragment_managed_states_t)); + states->cls = fragment->cls; + states->manager = manager; + states->container = container; + states->instance = fragment; + fragment->managed = states; + if(fragment->cls->attached_cb) { + fragment->cls->attached_cb(fragment); + } + return states; +} + +#endif /*LV_USE_FRAGMENT*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.c b/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.c index 874e6a982..4eec637bd 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.c @@ -81,6 +81,27 @@ void lv_gridnav_remove(lv_obj_t * obj) lv_obj_remove_event_cb(obj, gridnav_event_cb); } +void lv_gridnav_set_focused(lv_obj_t * cont, lv_obj_t * to_focus, lv_anim_enable_t anim_en) +{ + LV_ASSERT_NULL(to_focus); + lv_gridnav_dsc_t * dsc = lv_obj_get_event_user_data(cont, gridnav_event_cb); + if(dsc == NULL) { + LV_LOG_WARN("`cont` is not a gridnav container"); + return; + } + + if(obj_is_focuable(to_focus) == false) { + LV_LOG_WARN("The object to focus is not focusable"); + return; + } + + lv_obj_clear_state(dsc->focused_obj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY); + lv_obj_add_state(to_focus, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY); + lv_obj_scroll_to_view(to_focus, anim_en); + dsc->focused_obj = to_focus; + +} + /********************** * STATIC FUNCTIONS **********************/ @@ -98,7 +119,7 @@ static void gridnav_event_cb(lv_event_t * e) if(dsc->focused_obj == NULL) dsc->focused_obj = find_first_focusable(obj); if(dsc->focused_obj == NULL) return; - uint32_t key = lv_indev_get_key(lv_indev_get_act()); + uint32_t key = lv_event_get_key(e); lv_obj_t * guess = NULL; if(key == LV_KEY_RIGHT) { @@ -327,7 +348,7 @@ static lv_obj_t * find_last_focusable(lv_obj_t * obj) { uint32_t child_cnt = lv_obj_get_child_cnt(obj); int32_t i; - for(i = child_cnt - 1; i >= 0; i++) { + for(i = child_cnt - 1; i >= 0; i--) { lv_obj_t * child = lv_obj_get_child(obj, i); if(obj_is_focuable(child)) return child; } diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.h b/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.h index ea81595de..f480ded46 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/gridnav/lv_gridnav.h @@ -103,6 +103,14 @@ void lv_gridnav_add(lv_obj_t * obj, lv_gridnav_ctrl_t ctrl); */ void lv_gridnav_remove(lv_obj_t * obj); +/** + * Manually focus an object on gridnav container + * @param cont pointer to a gridnav container + * @param to_focus pointer to an object to focus + * @param anim_en LV_ANIM_ON/OFF + */ +void lv_gridnav_set_focused(lv_obj_t * cont, lv_obj_t * to_focus, lv_anim_enable_t anim_en); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.c b/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.c new file mode 100644 index 000000000..b1661e4f7 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.c @@ -0,0 +1,1198 @@ +/** + * @file lv_ime_pinyin.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_ime_pinyin.h" +#if LV_USE_IME_PINYIN != 0 + +#include + +/********************* + * DEFINES + *********************/ +#define MY_CLASS &lv_ime_pinyin_class + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_ime_pinyin_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_ime_pinyin_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_ime_pinyin_style_change_event(lv_event_t * e); +static void lv_ime_pinyin_kb_event(lv_event_t * e); +static void lv_ime_pinyin_cand_panel_event(lv_event_t * e); + +static void init_pinyin_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict); +static void pinyin_input_proc(lv_obj_t * obj); +static void pinyin_page_proc(lv_obj_t * obj, uint16_t btn); +static char * pinyin_search_matching(lv_obj_t * obj, char * py_str, uint16_t * cand_num); +static void pinyin_ime_clear_data(lv_obj_t * obj); + +#if LV_IME_PINYIN_USE_K9_MODE + static void pinyin_k9_init_data(lv_obj_t * obj); + static void pinyin_k9_get_legal_py(lv_obj_t * obj, char * k9_input, const char * py9_map[]); + static bool pinyin_k9_is_valid_py(lv_obj_t * obj, char * py_str); + static void pinyin_k9_fill_cand(lv_obj_t * obj); + static void pinyin_k9_cand_page_proc(lv_obj_t * obj, uint16_t dir); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +const lv_obj_class_t lv_ime_pinyin_class = { + .constructor_cb = lv_ime_pinyin_constructor, + .destructor_cb = lv_ime_pinyin_destructor, + .width_def = LV_SIZE_CONTENT, + .height_def = LV_SIZE_CONTENT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, + .instance_size = sizeof(lv_ime_pinyin_t), + .base_class = &lv_obj_class +}; + +#if LV_IME_PINYIN_USE_K9_MODE +static char * lv_btnm_def_pinyin_k9_map[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 20] = {\ + ",\0", "1#\0", "abc \0", "def\0", LV_SYMBOL_BACKSPACE"\0", "\n\0", + ".\0", "ghi\0", "jkl\0", "mno\0", LV_SYMBOL_KEYBOARD"\0", "\n\0", + "?\0", "pqrs\0", "tuv\0", "wxyz\0", LV_SYMBOL_NEW_LINE"\0", "\n\0", + LV_SYMBOL_LEFT"\0", "\0" + }; + +static lv_btnmatrix_ctrl_t default_kb_ctrl_k9_map[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 16] = { 1 }; +static char lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 2][LV_IME_PINYIN_K9_MAX_INPUT] = {0}; +#endif + +static char lv_pinyin_cand_str[LV_IME_PINYIN_CAND_TEXT_NUM][4]; +static char * lv_btnm_def_pinyin_sel_map[LV_IME_PINYIN_CAND_TEXT_NUM + 3]; + +#if LV_IME_PINYIN_USE_DEFAULT_DICT +lv_pinyin_dict_t lv_ime_pinyin_def_dict[] = { + { "a", "啊" }, + { "ai", "愛" }, + { "an", "安暗案" }, + { "ba", "吧把爸八" }, + { "bai", "百白敗" }, + { "ban", "半般辦" }, + { "bang", "旁" }, + { "bao", "保薄包報" }, + { "bei", "被背悲北杯備" }, + { "ben", "本" }, + { "bi", "必比避鼻彼筆秘閉" }, + { "bian", "便邊變変辺" }, + { "biao", "表標" }, + { "bie", "別" }, + { "bing", "病並氷" }, + { "bo", "波薄泊" }, + { "bu", "不布步部捕補歩" }, + { "ca", "察" }, + { "cai", "才材菜財採" }, + { "can", "参残參" }, + { "ce", "策側" }, + { "ceng", "曾" }, + { "cha", "差查茶" }, + { "chai", "差" }, + { "chan", "產産單" }, + { "chang", "場廠" }, + { "chao", "超朝" }, + { "che", "車" }, + { "cheng", "成程乗" }, + { "chi", "尺吃持赤池遅歯" }, + { "chong", "充种重種" }, + { "chu", "出初楚触處処" }, + { "chuan", "川船傳" }, + { "chuang", "創窓" }, + { "chun", "春" }, + { "ci", "此次辞差" }, + { "cong", "從従" }, + { "cu", "卒" }, + { "cun", "存村" }, + { "cuo", "錯" }, + { "da", "大打答達" }, + { "dai", "代待帯帶貸" }, + { "dan", "但担擔誕單単" }, + { "dang", "当党當黨" }, + { "dao", "到道盗導島辺" }, + { "de", "的得" }, + { "dei", "" }, + { "deng", "等" }, + { "di", "地得低底弟第締" }, + { "dian", "点电店點電" }, + { "diao", "調" }, + { "ding", "定町" }, + { "dong", "冬東動働凍" }, + { "du", "独度都渡読" }, + { "duan", "段断短斷" }, + { "dui", "對対" }, + { "duo", "多駄" }, + { "e", "嗯悪" }, + { "en", "嗯" }, + { "er", "而耳二兒" }, + { "fa", "乏法發発髪" }, + { "fan", "反返犯番仮販飯範払" }, + { "fang", "方放房坊訪" }, + { "fei", "非飛費" }, + { "fen", "分份" }, + { "feng", "風豐" }, + { "fou", "否不" }, + { "fu", "父夫富服符付附府幅婦復複負払" }, + { "gai", "改概該" }, + { "gan", "甘感敢" }, + { "gang", "港剛" }, + { "gao", "告高" }, + { "ge", "各格歌革割個" }, + { "gei", "給" }, + { "gen", "跟根" }, + { "geng", "更" }, + { "gong", "工共供功公" }, + { "gou", "夠構溝" }, + { "gu", "古故鼓" }, + { "guai", "掛" }, + { "guan", "官管慣館觀関關" }, + { "guang", "光広" }, + { "gui", "規帰" }, + { "guo", "果国裏菓國過" }, + { "hai", "孩海害還" }, + { "han", "寒漢" }, + { "hang", "航行" }, + { "hao", "好号" }, + { "he", "合和喝何荷" }, + { "hei", "黒" }, + { "hen", "很" }, + { "heng", "行横" }, + { "hou", "厚喉候後" }, + { "hu", "乎呼湖護" }, + { "hua", "化画花話畫劃" }, + { "huai", "壊劃" }, + { "huan", "緩環歡還換" }, + { "huang", "黄" }, + { "hui", "回会慧絵揮會" }, + { "hun", "混婚" }, + { "huo", "活或火獲" }, + { "i", "" }, + { "ji", "己计及机既急季寄技即集基祭系奇紀積計記済幾際極繼績機濟" }, + { "jia", "家加價" }, + { "jian", "件建健肩見減間検簡漸" }, + { "jiang", "降強講將港" }, + { "jiao", "叫教交角覚覺較學" }, + { "jie", "介借接姐皆届界解結階節價" }, + { "jin", "今近禁金僅進" }, + { "jing", "京境景静精經経" }, + { "jiu", "就久九酒究" }, + { "ju", "句具局居決挙據舉" }, + { "jue", "角覚覺" }, + { "jun", "均" }, + { "kai", "開" }, + { "kan", "看刊" }, + { "kang", "康" }, + { "kao", "考" }, + { "ke", "可刻科克客渇課" }, + { "ken", "肯" }, + { "kong", "空控" }, + { "kou", "口" }, + { "ku", "苦庫" }, + { "kuai", "快塊会會" }, + { "kuang", "況" }, + { "kun", "困" }, + { "kuo", "括拡適" }, + { "la", "拉啦落" }, + { "lai", "来來頼" }, + { "lao", "老絡落" }, + { "le", "了楽樂" }, + { "lei", "類" }, + { "leng", "冷" }, + { "li", "力立利理例礼離麗裡勵歷" }, + { "lian", "連練臉聯" }, + { "liang", "良量涼兩両" }, + { "liao", "料" }, + { "lie", "列" }, + { "lin", "林隣賃" }, + { "ling", "另令領" }, + { "liu", "六留流" }, + { "lu", "律路録緑陸履慮" }, + { "lv", "旅" }, + { "lun", "輪論" }, + { "luo", "落絡" }, + { "ma", "媽嗎嘛" }, + { "mai", "買売" }, + { "man", "滿" }, + { "mang", "忙" }, + { "mao", "毛猫貿" }, + { "me", "麼" }, + { "mei", "美妹每沒毎媒" }, + { "men", "們" }, + { "mi", "米密秘" }, + { "mian", "免面勉眠" }, + { "miao", "描" }, + { "min", "民皿" }, + { "ming", "命明名" }, + { "mo", "末模麼" }, + { "mou", "某" }, + { "mu", "母木目模" }, + { "na", "那哪拿內南" }, + { "nan", "男南難" }, + { "nao", "腦" }, + { "ne", "那哪呢" }, + { "nei", "内那哪內" }, + { "neng", "能" }, + { "ni", "你妳呢" }, + { "nian", "年念" }, + { "niang", "娘" }, + { "nin", "您" }, + { "ning", "凝" }, + { "niu", "牛" }, + { "nong", "農濃" }, + { "nu", "女努" }, + { "nuan", "暖" }, + { "o", "" }, + { "ou", "歐" }, + { "pa", "怕" }, + { "pian", "片便" }, + { "pai", "迫派排" }, + { "pan", "判番" }, + { "pang", "旁" }, + { "pei", "配" }, + { "peng", "朋" }, + { "pi", "疲否" }, + { "pin", "品貧" }, + { "ping", "平評" }, + { "po", "迫破泊頗" }, + { "pu", "普僕" }, + { "qi", "起其奇七气期泣企妻契気" }, + { "qian", "嵌浅千前鉛錢針" }, + { "qiang", "強將" }, + { "qiao", "橋繰" }, + { "qie", "且切契" }, + { "qin", "寝勤親" }, + { "qing", "青清情晴輕頃請軽" }, + { "qiu", "求秋球" }, + { "qu", "去取趣曲區" }, + { "quan", "全犬券" }, + { "que", "缺確卻" }, + { "ran", "然" }, + { "rang", "讓" }, + { "re", "熱" }, + { "ren", "人任認" }, + { "reng", "仍" }, + { "ri", "日" }, + { "rong", "容" }, + { "rou", "弱若肉" }, + { "ru", "如入" }, + { "ruan", "軟" }, + { "sai", "賽" }, + { "san", "三" }, + { "sao", "騒繰" }, + { "se", "色" }, + { "sen", "森" }, + { "sha", "砂" }, + { "shan", "善山單" }, + { "shang", "上尚商" }, + { "shao", "少紹" }, + { "shaung", "雙" }, + { "she", "社射設捨渉" }, + { "shei", "誰" }, + { "shen", "什申深甚身伸沈神" }, + { "sheng", "生声昇勝乗聲" }, + { "shi", "是失示食时事式十石施使世实史室市始柿氏士仕拭時視師試適実實識" }, + { "shou", "手首守受授" }, + { "shu", "束数暑殊樹書屬輸術" }, + { "shui", "水説說誰" }, + { "shuo", "数説說" }, + { "si", "思寺司四私似死価" }, + { "song", "送" }, + { "su", "速宿素蘇訴" }, + { "suan", "算酸" }, + { "sui", "隨雖歲歳" }, + { "sun", "孫" }, + { "suo", "所" }, + { "ta", "她他它牠" }, + { "tai", "太台態臺" }, + { "tan", "探談曇" }, + { "tang", "糖" }, + { "tao", "桃逃套討" }, + { "te", "特" }, + { "ti", "体提替題體戻" }, + { "tian", "天田" }, + { "tiao", "条條調" }, + { "tie", "鉄" }, + { "ting", "停庭聽町" }, + { "tong", "同童通痛统統" }, + { "tou", "投透頭" }, + { "tu", "土徒茶図" }, + { "tuan", "團" }, + { "tui", "推退" }, + { "tuo", "脱駄" }, + { "u", "" }, + { "v", "" }, + { "wai", "外" }, + { "wan", "完万玩晩腕灣" }, + { "wang", "忘望亡往網" }, + { "wei", "危位未味委為謂維違圍" }, + { "wen", "文温問聞" }, + { "wo", "我" }, + { "wu", "午物五無屋亡鳥務汚" }, + { "xi", "夕息西洗喜系昔席希析嬉膝細習係" }, + { "xia", "下夏狭暇" }, + { "xian", "先限嫌洗現見線顯" }, + { "xiang", "向相香像想象降項詳響" }, + { "xiao", "小笑消效校削咲" }, + { "xie", "写携些解邪械協謝寫契" }, + { "xin", "心信新辛" }, + { "xing", "行形性幸型星興" }, + { "xiong", "兄胸" }, + { "xiu", "休秀修" }, + { "xu", "須需許續緒続" }, + { "xuan", "選懸" }, + { "xue", "学雪削靴學" }, + { "xun", "訓訊" }, + { "ya", "呀押壓" }, + { "yan", "言顔研煙嚴厳験驗塩" }, + { "yang", "央洋陽樣様" }, + { "yao", "要揺腰薬曜" }, + { "ye", "也野夜邪業葉" }, + { "yi", "一已亦依以移意医易伊役異億義議藝醫訳" }, + { "yin", "因引音飲銀" }, + { "ying", "英迎影映應營営" }, + { "yong", "永用泳擁" }, + { "you", "又有右友由尤油遊郵誘優" }, + { "yu", "予育余雨浴欲愈御宇域語於魚與込" }, + { "yuan", "元原源院員円園遠猿願" }, + { "yue", "月越約楽" }, + { "yun", "雲伝運" }, + { "za", "雑" }, + { "zai", "在再載災" }, + { "zang", "蔵" }, + { "zao", "早造" }, + { "ze", "則擇責" }, + { "zen", "怎" }, + { "zeng", "曾增増" }, + { "zha", "札" }, + { "zhai", "宅擇" }, + { "zhan", "站展戰戦" }, + { "zhang", "丈長障帳張" }, + { "zhao", "找着朝招" }, + { "zhe", "者這" }, + { "zhen", "真震針" }, + { "zheng", "正整争政爭" }, + { "zhi", "之只知支止制至治直指值置智値紙製質誌織隻識職執" }, + { "zhong", "中种終重種眾" }, + { "zhou", "周州昼宙洲週" }, + { "zhu", "助主住柱株祝逐注著諸屬術" }, + { "zhuan", "专專転" }, + { "zhuang", "状狀" }, + { "zhui", "追" }, + { "zhun", "準" }, + { "zhuo", "着" }, + { "zi", "子自字姉資" }, + { "zong", "總" }, + { "zuo", "左做昨坐座作" }, + { "zu", "足祖族卒組" }, + { "zui", "最酔" }, + { "zou", "走" }, + {NULL, NULL} +}; +#endif + + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +lv_obj_t * lv_ime_pinyin_create(lv_obj_t * parent) +{ + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the keyboard of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @param dict pointer to a Pinyin input method keyboard + */ +void lv_ime_pinyin_set_keyboard(lv_obj_t * obj, lv_obj_t * kb) +{ + if(kb) { + LV_ASSERT_OBJ(kb, &lv_keyboard_class); + } + + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + pinyin_ime->kb = kb; + lv_obj_add_event_cb(pinyin_ime->kb, lv_ime_pinyin_kb_event, LV_EVENT_VALUE_CHANGED, obj); + lv_obj_align_to(pinyin_ime->cand_panel, pinyin_ime->kb, LV_ALIGN_OUT_TOP_MID, 0, 0); +} + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @param dict pointer to a Pinyin input method dictionary + */ +void lv_ime_pinyin_set_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + init_pinyin_dict(obj, dict); +} + +/** + * Set mode, 26-key input(k26) or 9-key input(k9). + * @param obj pointer to a Pinyin input method object + * @param mode the mode from 'lv_keyboard_mode_t' + */ +void lv_ime_pinyin_set_mode(lv_obj_t * obj, lv_ime_pinyin_mode_t mode) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + LV_ASSERT_OBJ(pinyin_ime->kb, &lv_keyboard_class); + + pinyin_ime->mode = mode; + +#if LV_IME_PINYIN_USE_K9_MODE + if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K9) { + pinyin_k9_init_data(obj); + lv_keyboard_set_map(pinyin_ime->kb, LV_KEYBOARD_MODE_USER_1, (const char *)lv_btnm_def_pinyin_k9_map, + (const)default_kb_ctrl_k9_map); + lv_keyboard_set_mode(pinyin_ime->kb, LV_KEYBOARD_MODE_USER_1); + } +#endif +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin IME object + * @return pointer to the Pinyin IME keyboard + */ +lv_obj_t * lv_ime_pinyin_get_kb(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + return pinyin_ime->kb; +} + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @return pointer to the Pinyin input method candidate panel + */ +lv_obj_t * lv_ime_pinyin_get_cand_panel(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + return pinyin_ime->cand_panel; +} + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @return pointer to the Pinyin input method dictionary + */ +lv_pinyin_dict_t * lv_ime_pinyin_get_dict(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + return pinyin_ime->dict; +} + +/*===================== + * Other functions + *====================*/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_ime_pinyin_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + LV_TRACE_OBJ_CREATE("begin"); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + uint16_t py_str_i = 0; + uint16_t btnm_i = 0; + for(btnm_i = 0; btnm_i < (LV_IME_PINYIN_CAND_TEXT_NUM + 3); btnm_i++) { + if(btnm_i == 0) { + lv_btnm_def_pinyin_sel_map[btnm_i] = "<"; + } + else if(btnm_i == (LV_IME_PINYIN_CAND_TEXT_NUM + 1)) { + lv_btnm_def_pinyin_sel_map[btnm_i] = ">"; + } + else if(btnm_i == (LV_IME_PINYIN_CAND_TEXT_NUM + 2)) { + lv_btnm_def_pinyin_sel_map[btnm_i] = ""; + } + else { + lv_pinyin_cand_str[py_str_i][0] = ' '; + lv_btnm_def_pinyin_sel_map[btnm_i] = lv_pinyin_cand_str[py_str_i]; + py_str_i++; + } + } + + pinyin_ime->mode = LV_IME_PINYIN_MODE_K26; + pinyin_ime->py_page = 0; + pinyin_ime->ta_count = 0; + pinyin_ime->cand_num = 0; + lv_memset_00(pinyin_ime->input_char, sizeof(pinyin_ime->input_char)); + lv_memset_00(pinyin_ime->py_num, sizeof(pinyin_ime->py_num)); + lv_memset_00(pinyin_ime->py_pos, sizeof(pinyin_ime->py_pos)); + + lv_obj_set_size(obj, LV_PCT(100), LV_PCT(55)); + lv_obj_align(obj, LV_ALIGN_BOTTOM_MID, 0, 0); + +#if LV_IME_PINYIN_USE_DEFAULT_DICT + init_pinyin_dict(obj, lv_ime_pinyin_def_dict); +#endif + + /* Init pinyin_ime->cand_panel */ + pinyin_ime->cand_panel = lv_btnmatrix_create(lv_scr_act()); + lv_btnmatrix_set_map(pinyin_ime->cand_panel, (const char **)lv_btnm_def_pinyin_sel_map); + lv_obj_set_size(pinyin_ime->cand_panel, LV_PCT(100), LV_PCT(5)); + lv_obj_add_flag(pinyin_ime->cand_panel, LV_OBJ_FLAG_HIDDEN); + + lv_btnmatrix_set_one_checked(pinyin_ime->cand_panel, true); + + /* Set cand_panel style*/ + // Default style + lv_obj_set_style_bg_opa(pinyin_ime->cand_panel, LV_OPA_0, 0); + lv_obj_set_style_border_width(pinyin_ime->cand_panel, 0, 0); + lv_obj_set_style_pad_all(pinyin_ime->cand_panel, 8, 0); + lv_obj_set_style_pad_gap(pinyin_ime->cand_panel, 0, 0); + lv_obj_set_style_radius(pinyin_ime->cand_panel, 0, 0); + lv_obj_set_style_pad_gap(pinyin_ime->cand_panel, 0, 0); + lv_obj_set_style_base_dir(pinyin_ime->cand_panel, LV_BASE_DIR_LTR, 0); + + // LV_PART_ITEMS style + lv_obj_set_style_radius(pinyin_ime->cand_panel, 12, LV_PART_ITEMS); + lv_obj_set_style_bg_color(pinyin_ime->cand_panel, lv_color_white(), LV_PART_ITEMS); + lv_obj_set_style_bg_opa(pinyin_ime->cand_panel, LV_OPA_0, LV_PART_ITEMS); + lv_obj_set_style_shadow_opa(pinyin_ime->cand_panel, LV_OPA_0, LV_PART_ITEMS); + + // LV_PART_ITEMS | LV_STATE_PRESSED style + lv_obj_set_style_bg_opa(pinyin_ime->cand_panel, LV_OPA_COVER, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_set_style_bg_color(pinyin_ime->cand_panel, lv_color_white(), LV_PART_ITEMS | LV_STATE_PRESSED); + + /* event handler */ + lv_obj_add_event_cb(pinyin_ime->cand_panel, lv_ime_pinyin_cand_panel_event, LV_EVENT_VALUE_CHANGED, obj); + lv_obj_add_event_cb(obj, lv_ime_pinyin_style_change_event, LV_EVENT_STYLE_CHANGED, NULL); + +#if LV_IME_PINYIN_USE_K9_MODE + pinyin_ime->k9_input_str_len = 0; + pinyin_ime->k9_py_ll_pos = 0; + pinyin_ime->k9_legal_py_count = 0; + lv_memset_00(pinyin_ime->k9_input_str, LV_IME_PINYIN_K9_MAX_INPUT); + + pinyin_k9_init_data(obj); + + _lv_ll_init(&(pinyin_ime->k9_legal_py_ll), sizeof(ime_pinyin_k9_py_str_t)); +#endif +} + + +static void lv_ime_pinyin_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + if(lv_obj_is_valid(pinyin_ime->kb)) + lv_obj_del(pinyin_ime->kb); + + if(lv_obj_is_valid(pinyin_ime->cand_panel)) + lv_obj_del(pinyin_ime->cand_panel); +} + + +static void lv_ime_pinyin_kb_event(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * kb = lv_event_get_target(e); + lv_obj_t * obj = lv_event_get_user_data(e); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + +#if LV_IME_PINYIN_USE_K9_MODE + static const char * k9_py_map[8] = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; +#endif + + if(code == LV_EVENT_VALUE_CHANGED) { + uint16_t btn_id = lv_btnmatrix_get_selected_btn(kb); + if(btn_id == LV_BTNMATRIX_BTN_NONE) return; + + const char * txt = lv_btnmatrix_get_btn_text(kb, lv_btnmatrix_get_selected_btn(kb)); + if(txt == NULL) return; + +#if LV_IME_PINYIN_USE_K9_MODE + if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K9) { + lv_obj_t * ta = lv_keyboard_get_textarea(pinyin_ime->kb); + uint16_t tmp_btn_str_len = strlen(pinyin_ime->input_char); + if((btn_id >= 16) && (tmp_btn_str_len > 0) && (btn_id < (16 + LV_IME_PINYIN_K9_CAND_TEXT_NUM))) { + tmp_btn_str_len = strlen(pinyin_ime->input_char); + lv_memset_00(pinyin_ime->input_char, sizeof(pinyin_ime->input_char)); + strcat(pinyin_ime->input_char, txt); + pinyin_input_proc(obj); + + for(int index = 0; index < (pinyin_ime->ta_count + tmp_btn_str_len); index++) { + lv_textarea_del_char(ta); + } + + pinyin_ime->ta_count = tmp_btn_str_len; + pinyin_ime->k9_input_str_len = tmp_btn_str_len; + lv_textarea_add_text(ta, pinyin_ime->input_char); + + return; + } + } +#endif + + if(strcmp(txt, "Enter") == 0 || strcmp(txt, LV_SYMBOL_NEW_LINE) == 0) { + pinyin_ime_clear_data(obj); + lv_obj_add_flag(pinyin_ime->cand_panel, LV_OBJ_FLAG_HIDDEN); + } + else if(strcmp(txt, LV_SYMBOL_BACKSPACE) == 0) { + // del input char + if(pinyin_ime->ta_count > 0) { + if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K26) + pinyin_ime->input_char[pinyin_ime->ta_count - 1] = '\0'; +#if LV_IME_PINYIN_USE_K9_MODE + else + pinyin_ime->k9_input_str[pinyin_ime->ta_count - 1] = '\0'; +#endif + + pinyin_ime->ta_count = pinyin_ime->ta_count - 1; + if(pinyin_ime->ta_count <= 0) { + lv_obj_add_flag(pinyin_ime->cand_panel, LV_OBJ_FLAG_HIDDEN); +#if LV_IME_PINYIN_USE_K9_MODE + lv_memset_00(lv_pinyin_k9_cand_str, sizeof(lv_pinyin_k9_cand_str)); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM], LV_SYMBOL_RIGHT"\0"); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 1], "\0"); +#endif + } + else if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K26) { + pinyin_input_proc(obj); + } +#if LV_IME_PINYIN_USE_K9_MODE + else if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K9) { + pinyin_ime->k9_input_str_len = strlen(pinyin_ime->input_char) - 1; + pinyin_k9_get_legal_py(obj, pinyin_ime->k9_input_str, k9_py_map); + pinyin_k9_fill_cand(obj); + pinyin_input_proc(obj); + } +#endif + } + } + else if((strcmp(txt, "ABC") == 0) || (strcmp(txt, "abc") == 0) || (strcmp(txt, "1#") == 0)) { + pinyin_ime->ta_count = 0; + lv_memset_00(pinyin_ime->input_char, sizeof(pinyin_ime->input_char)); + return; + } + else if(strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) { + if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K26) { + lv_ime_pinyin_set_mode(pinyin_ime, LV_IME_PINYIN_MODE_K9); + } + else { + lv_ime_pinyin_set_mode(pinyin_ime, LV_IME_PINYIN_MODE_K26); + lv_keyboard_set_mode(pinyin_ime->kb, LV_KEYBOARD_MODE_TEXT_LOWER); + } + pinyin_ime_clear_data(obj); + } + else if(strcmp(txt, LV_SYMBOL_OK) == 0) { + pinyin_ime_clear_data(obj); + } + else if((pinyin_ime->mode == LV_IME_PINYIN_MODE_K26) && ((txt[0] >= 'a' && txt[0] <= 'z') || (txt[0] >= 'A' && + txt[0] <= 'Z'))) { + strcat(pinyin_ime->input_char, txt); + pinyin_input_proc(obj); + pinyin_ime->ta_count++; + } +#if LV_IME_PINYIN_USE_K9_MODE + else if((pinyin_ime->mode == LV_IME_PINYIN_MODE_K9) && (txt[0] >= 'a' && txt[0] <= 'z')) { + for(uint16_t i = 0; i < 8; i++) { + if((strcmp(txt, k9_py_map[i]) == 0) || (strcmp(txt, "abc ") == 0)) { + if(strcmp(txt, "abc ") == 0) pinyin_ime->k9_input_str_len += strlen(k9_py_map[i]) + 1; + else pinyin_ime->k9_input_str_len += strlen(k9_py_map[i]); + pinyin_ime->k9_input_str[pinyin_ime->ta_count] = 50 + i; + + break; + } + } + pinyin_k9_get_legal_py(obj, pinyin_ime->k9_input_str, k9_py_map); + pinyin_k9_fill_cand(obj); + pinyin_input_proc(obj); + } + else if(strcmp(txt, LV_SYMBOL_LEFT) == 0) { + pinyin_k9_cand_page_proc(obj, 0); + } + else if(strcmp(txt, LV_SYMBOL_RIGHT) == 0) { + pinyin_k9_cand_page_proc(obj, 1); + } +#endif + } +} + + +static void lv_ime_pinyin_cand_panel_event(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * cand_panel = lv_event_get_target(e); + lv_obj_t * obj = (lv_obj_t *)lv_event_get_user_data(e); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + if(code == LV_EVENT_VALUE_CHANGED) { + uint32_t id = lv_btnmatrix_get_selected_btn(cand_panel); + if(id == 0) { + pinyin_page_proc(obj, 0); + return; + } + if(id == (LV_IME_PINYIN_CAND_TEXT_NUM + 1)) { + pinyin_page_proc(obj, 1); + return; + } + + const char * txt = lv_btnmatrix_get_btn_text(cand_panel, id); + lv_obj_t * ta = lv_keyboard_get_textarea(pinyin_ime->kb); + uint16_t index = 0; + for(index = 0; index < pinyin_ime->ta_count; index++) + lv_textarea_del_char(ta); + + lv_textarea_add_text(ta, txt); + + pinyin_ime_clear_data(obj); + } +} + + +static void pinyin_input_proc(lv_obj_t * obj) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + pinyin_ime->cand_str = pinyin_search_matching(obj, pinyin_ime->input_char, &pinyin_ime->cand_num); + if(pinyin_ime->cand_str == NULL) { + return; + } + + pinyin_ime->py_page = 0; + + for(uint8_t i = 0; i < LV_IME_PINYIN_CAND_TEXT_NUM; i++) { + memset(lv_pinyin_cand_str[i], 0x00, sizeof(lv_pinyin_cand_str[i])); + lv_pinyin_cand_str[i][0] = ' '; + } + + // fill buf + for(uint8_t i = 0; (i < pinyin_ime->cand_num && i < LV_IME_PINYIN_CAND_TEXT_NUM); i++) { + for(uint8_t j = 0; j < 3; j++) { + lv_pinyin_cand_str[i][j] = pinyin_ime->cand_str[i * 3 + j]; + } + } + + lv_obj_clear_flag(pinyin_ime->cand_panel, LV_OBJ_FLAG_HIDDEN); +} + +static void pinyin_page_proc(lv_obj_t * obj, uint16_t dir) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + uint16_t page_num = pinyin_ime->cand_num / LV_IME_PINYIN_CAND_TEXT_NUM; + uint16_t sur = pinyin_ime->cand_num % LV_IME_PINYIN_CAND_TEXT_NUM; + + if(dir == 0) { + if(pinyin_ime->py_page) { + pinyin_ime->py_page--; + } + } + else { + if(sur == 0) { + page_num -= 1; + } + if(pinyin_ime->py_page < page_num) { + pinyin_ime->py_page++; + } + else return; + } + + for(uint8_t i = 0; i < LV_IME_PINYIN_CAND_TEXT_NUM; i++) { + memset(lv_pinyin_cand_str[i], 0x00, sizeof(lv_pinyin_cand_str[i])); + lv_pinyin_cand_str[i][0] = ' '; + } + + // fill buf + uint16_t offset = pinyin_ime->py_page * (3 * LV_IME_PINYIN_CAND_TEXT_NUM); + for(uint8_t i = 0; (i < pinyin_ime->cand_num && i < LV_IME_PINYIN_CAND_TEXT_NUM); i++) { + if((sur > 0) && (pinyin_ime->py_page == page_num)) { + if(i > sur) + break; + } + for(uint8_t j = 0; j < 3; j++) { + lv_pinyin_cand_str[i][j] = pinyin_ime->cand_str[offset + (i * 3) + j]; + } + } +} + + +static void lv_ime_pinyin_style_change_event(lv_event_t * e) +{ + lv_event_code_t code = lv_event_get_code(e); + lv_obj_t * obj = lv_event_get_target(e); + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + if(code == LV_EVENT_STYLE_CHANGED) { + const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); + lv_obj_set_style_text_font(pinyin_ime->cand_panel, font, 0); + } +} + + +static void init_pinyin_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + char headletter = 'a'; + uint16_t offset_sum = 0; + uint16_t offset_count = 0; + uint16_t letter_calc = 0; + + pinyin_ime->dict = dict; + + for(uint16_t i = 0; ; i++) { + if((NULL == (dict[i].py)) || (NULL == (dict[i].py_mb))) { + headletter = dict[i - 1].py[0]; + letter_calc = headletter - 'a'; + pinyin_ime->py_num[letter_calc] = offset_count; + break; + } + + if(headletter == (dict[i].py[0])) { + offset_count++; + } + else { + headletter = dict[i].py[0]; + letter_calc = headletter - 'a'; + pinyin_ime->py_num[letter_calc - 1] = offset_count; + offset_sum += offset_count; + pinyin_ime->py_pos[letter_calc] = offset_sum; + + offset_count = 1; + } + } +} + + +static char * pinyin_search_matching(lv_obj_t * obj, char * py_str, uint16_t * cand_num) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + lv_pinyin_dict_t * cpHZ; + uint8_t index, len = 0, offset; + volatile uint8_t count = 0; + + if(*py_str == '\0') return NULL; + if(*py_str == 'i') return NULL; + if(*py_str == 'u') return NULL; + if(*py_str == 'v') return NULL; + + offset = py_str[0] - 'a'; + len = strlen(py_str); + + cpHZ = &pinyin_ime->dict[pinyin_ime->py_pos[offset]]; + count = pinyin_ime->py_num[offset]; + + while(count--) { + for(index = 0; index < len; index++) { + if(*(py_str + index) != *((cpHZ->py) + index)) { + break; + } + } + + // perfect match + if(len == 1 || index == len) { + // The Chinese character in UTF-8 encoding format is 3 bytes + * cand_num = strlen((const char *)(cpHZ->py_mb)) / 3; + return (char *)(cpHZ->py_mb); + } + cpHZ++; + } + return NULL; +} + +static void pinyin_ime_clear_data(lv_obj_t * obj) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + +#if LV_IME_PINYIN_USE_K9_MODE + if(pinyin_ime->mode == LV_IME_PINYIN_MODE_K9) { + pinyin_ime->k9_input_str_len = 0; + pinyin_ime->k9_py_ll_pos = 0; + pinyin_ime->k9_legal_py_count = 0; + lv_memset_00(pinyin_ime->k9_input_str, LV_IME_PINYIN_K9_MAX_INPUT); + lv_memset_00(lv_pinyin_k9_cand_str, sizeof(lv_pinyin_k9_cand_str)); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM], LV_SYMBOL_RIGHT"\0"); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 1], "\0"); + } +#endif + + pinyin_ime->ta_count = 0; + lv_memset_00(lv_pinyin_cand_str, (sizeof(lv_pinyin_cand_str))); + lv_memset_00(pinyin_ime->input_char, sizeof(pinyin_ime->input_char)); + + lv_obj_add_flag(pinyin_ime->cand_panel, LV_OBJ_FLAG_HIDDEN); +} + + +#if LV_IME_PINYIN_USE_K9_MODE +static void pinyin_k9_init_data(lv_obj_t * obj) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + uint16_t py_str_i = 0; + uint16_t btnm_i = 0; + for(btnm_i = 19; btnm_i < (LV_IME_PINYIN_K9_CAND_TEXT_NUM + 21); btnm_i++) { + if(py_str_i == LV_IME_PINYIN_K9_CAND_TEXT_NUM) { + strcpy(lv_pinyin_k9_cand_str[py_str_i], LV_SYMBOL_RIGHT"\0"); + } + else if(py_str_i == LV_IME_PINYIN_K9_CAND_TEXT_NUM + 1) { + strcpy(lv_pinyin_k9_cand_str[py_str_i], "\0"); + } + else { + strcpy(lv_pinyin_k9_cand_str[py_str_i], " \0"); + } + + lv_btnm_def_pinyin_k9_map[btnm_i] = lv_pinyin_k9_cand_str[py_str_i]; + py_str_i++; + } + + default_kb_ctrl_k9_map[0] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[4] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[5] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[9] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[10] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[14] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[15] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; + default_kb_ctrl_k9_map[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 16] = LV_KEYBOARD_CTRL_BTN_FLAGS | 1; +} + +static void pinyin_k9_get_legal_py(lv_obj_t * obj, char * k9_input, const char * py9_map[]) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + uint16_t len = strlen(k9_input); + + if((len == 0) || (len >= LV_IME_PINYIN_K9_MAX_INPUT)) { + return; + } + + char py_comp[LV_IME_PINYIN_K9_MAX_INPUT] = {0}; + int mark[LV_IME_PINYIN_K9_MAX_INPUT] = {0}; + int index = 0; + int flag = 0; + int count = 0; + + uint32_t ll_len = 0; + ime_pinyin_k9_py_str_t * ll_index = NULL; + + ll_len = _lv_ll_get_len(&pinyin_ime->k9_legal_py_ll); + ll_index = _lv_ll_get_head(&pinyin_ime->k9_legal_py_ll); + + while(index != -1) { + if(index == len) { + if(pinyin_k9_is_valid_py(obj, py_comp)) { + if((count >= ll_len) || (ll_len == 0)) { + ll_index = _lv_ll_ins_tail(&pinyin_ime->k9_legal_py_ll); + strcpy(ll_index->py_str, py_comp); + } + else if((count < ll_len)) { + strcpy(ll_index->py_str, py_comp); + ll_index = _lv_ll_get_next(&pinyin_ime->k9_legal_py_ll, ll_index); + } + count++; + } + index--; + } + else { + flag = mark[index]; + if(flag < strlen(py9_map[k9_input[index] - '2'])) { + py_comp[index] = py9_map[k9_input[index] - '2'][flag]; + mark[index] = mark[index] + 1; + index++; + } + else { + mark[index] = 0; + index--; + } + } + } + + if(count > 0) { + pinyin_ime->ta_count++; + pinyin_ime->k9_legal_py_count = count; + } +} + + +/*true: visible; false: not visible*/ +static bool pinyin_k9_is_valid_py(lv_obj_t * obj, char * py_str) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + lv_pinyin_dict_t * cpHZ = NULL; + uint8_t index = 0, len = 0, offset = 0; + uint16_t ret = 1; + volatile uint8_t count = 0; + + if(*py_str == '\0') return false; + if(*py_str == 'i') return false; + if(*py_str == 'u') return false; + if(*py_str == 'v') return false; + + offset = py_str[0] - 'a'; + len = strlen(py_str); + + cpHZ = &pinyin_ime->dict[pinyin_ime->py_pos[offset]]; + count = pinyin_ime->py_num[offset]; + + while(count--) { + for(index = 0; index < len; index++) { + if(*(py_str + index) != *((cpHZ->py) + index)) { + break; + } + } + + // perfect match + if(len == 1 || index == len) { + return true; + } + cpHZ++; + } + return false; +} + + +static void pinyin_k9_fill_cand(lv_obj_t * obj) +{ + static uint16_t len = 0; + uint16_t index = 0, tmp_len = 0; + ime_pinyin_k9_py_str_t * ll_index = NULL; + + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + tmp_len = pinyin_ime->k9_legal_py_count; + + if(tmp_len != len) { + lv_memset_00(lv_pinyin_k9_cand_str, sizeof(lv_pinyin_k9_cand_str)); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM], LV_SYMBOL_RIGHT"\0"); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 1], "\0"); + len = tmp_len; + } + + ll_index = _lv_ll_get_head(&pinyin_ime->k9_legal_py_ll); + strcpy(pinyin_ime->input_char, ll_index->py_str); + while(ll_index) { + if((index >= LV_IME_PINYIN_K9_CAND_TEXT_NUM) || \ + (index >= pinyin_ime->k9_legal_py_count)) + break; + + strcpy(lv_pinyin_k9_cand_str[index], ll_index->py_str); + ll_index = _lv_ll_get_next(&pinyin_ime->k9_legal_py_ll, ll_index); /*Find the next list*/ + index++; + } + pinyin_ime->k9_py_ll_pos = index; + + lv_obj_t * ta = lv_keyboard_get_textarea(pinyin_ime->kb); + for(index = 0; index < pinyin_ime->k9_input_str_len; index++) { + lv_textarea_del_char(ta); + } + pinyin_ime->k9_input_str_len = strlen(pinyin_ime->input_char); + lv_textarea_add_text(ta, pinyin_ime->input_char); +} + + +static void pinyin_k9_cand_page_proc(lv_obj_t * obj, uint16_t dir) +{ + lv_ime_pinyin_t * pinyin_ime = (lv_ime_pinyin_t *)obj; + + lv_obj_t * ta = lv_keyboard_get_textarea(pinyin_ime->kb); + uint16_t ll_len = _lv_ll_get_len(&pinyin_ime->k9_legal_py_ll); + + if((ll_len > LV_IME_PINYIN_K9_CAND_TEXT_NUM) && (pinyin_ime->k9_legal_py_count > LV_IME_PINYIN_K9_CAND_TEXT_NUM)) { + ime_pinyin_k9_py_str_t * ll_index = NULL; + uint16_t tmp_btn_str_len = 0; + int count = 0; + + ll_index = _lv_ll_get_head(&pinyin_ime->k9_legal_py_ll); + while(ll_index) { + if(count >= pinyin_ime->k9_py_ll_pos) break; + + ll_index = _lv_ll_get_next(&pinyin_ime->k9_legal_py_ll, ll_index); /*Find the next list*/ + count++; + } + + if((NULL == ll_index) && (dir == 1)) return; + + lv_memset_00(lv_pinyin_k9_cand_str, sizeof(lv_pinyin_k9_cand_str)); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM], LV_SYMBOL_RIGHT"\0"); + strcpy(lv_pinyin_k9_cand_str[LV_IME_PINYIN_K9_CAND_TEXT_NUM + 1], "\0"); + + // next page + if(dir == 1) { + count = 0; + while(ll_index) { + if(count >= (LV_IME_PINYIN_K9_CAND_TEXT_NUM - 1)) + break; + + strcpy(lv_pinyin_k9_cand_str[count], ll_index->py_str); + ll_index = _lv_ll_get_next(&pinyin_ime->k9_legal_py_ll, ll_index); /*Find the next list*/ + count++; + } + pinyin_ime->k9_py_ll_pos += count - 1; + + } + // previous page + else { + count = LV_IME_PINYIN_K9_CAND_TEXT_NUM - 1; + ll_index = _lv_ll_get_prev(&pinyin_ime->k9_legal_py_ll, ll_index); + while(ll_index) { + if(count < 0) break; + + strcpy(lv_pinyin_k9_cand_str[count], ll_index->py_str); + ll_index = _lv_ll_get_prev(&pinyin_ime->k9_legal_py_ll, ll_index); /*Find the previous list*/ + count--; + } + + if(pinyin_ime->k9_py_ll_pos > LV_IME_PINYIN_K9_CAND_TEXT_NUM) + pinyin_ime->k9_py_ll_pos -= 1; + } + + lv_textarea_set_cursor_pos(ta, LV_TEXTAREA_CURSOR_LAST); + } +} + +#endif /*LV_IME_PINYIN_USE_K9_MODE*/ + +#endif /*LV_USE_IME_PINYIN*/ + diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.h b/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.h new file mode 100644 index 000000000..3ff7bb980 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/ime/lv_ime_pinyin.h @@ -0,0 +1,145 @@ +/** + * @file lv_ime_pinyin.h + * + */ +#ifndef LV_IME_PINYIN_H +#define LV_IME_PINYIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" + +#if LV_USE_IME_PINYIN != 0 + +/********************* + * DEFINES + *********************/ +#define LV_IME_PINYIN_K9_MAX_INPUT 7 + +/********************** + * TYPEDEFS + **********************/ + +typedef enum { + LV_IME_PINYIN_MODE_K26, + LV_IME_PINYIN_MODE_K9, +} lv_ime_pinyin_mode_t; + +/*Data of pinyin_dict*/ +typedef struct { + const char * const py; + const char * const py_mb; +} lv_pinyin_dict_t; + +/*Data of 9-key input(k9) mode*/ +typedef struct { + char py_str[7]; +} ime_pinyin_k9_py_str_t; + +/*Data of lv_ime_pinyin*/ +typedef struct { + lv_obj_t obj; + lv_obj_t * kb; + lv_obj_t * cand_panel; + lv_pinyin_dict_t * dict; + lv_ll_t k9_legal_py_ll; + char * cand_str; /* Candidate string */ + char input_char[16]; /* Input box character */ +#if LV_IME_PINYIN_USE_K9_MODE + char k9_input_str[LV_IME_PINYIN_K9_MAX_INPUT]; /* 9-key input(k9) mode input string */ + uint16_t k9_py_ll_pos; /* Current pinyin map pages(k9) */ + uint16_t k9_legal_py_count; /* Count of legal Pinyin numbers(k9) */ + uint16_t k9_input_str_len; /* 9-key input(k9) mode input string max len */ +#endif + uint16_t ta_count; /* The number of characters entered in the text box this time */ + uint16_t cand_num; /* Number of candidates */ + uint16_t py_page; /* Current pinyin map pages(k26) */ + uint16_t py_num[26]; /* Number and length of Pinyin */ + uint16_t py_pos[26]; /* Pinyin position */ + uint8_t mode : 1; /* Set mode, 1: 26-key input(k26), 0: 9-key input(k9). Default: 1. */ +} lv_ime_pinyin_t; + +/*********************** + * GLOBAL VARIABLES + ***********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +lv_obj_t * lv_ime_pinyin_create(lv_obj_t * parent); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the keyboard of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @param dict pointer to a Pinyin input method keyboard + */ +void lv_ime_pinyin_set_keyboard(lv_obj_t * obj, lv_obj_t * kb); + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @param dict pointer to a Pinyin input method dictionary + */ +void lv_ime_pinyin_set_dict(lv_obj_t * obj, lv_pinyin_dict_t * dict); + +/** + * Set mode, 26-key input(k26) or 9-key input(k9). + * @param obj pointer to a Pinyin input method object + * @param mode the mode from 'lv_ime_pinyin_mode_t' + */ +void lv_ime_pinyin_set_mode(lv_obj_t * obj, lv_ime_pinyin_mode_t mode); + + +/*===================== + * Getter functions + *====================*/ + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin IME object + * @return pointer to the Pinyin IME keyboard + */ +lv_obj_t * lv_ime_pinyin_get_kb(lv_obj_t * obj); + + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @return pointer to the Pinyin input method candidate panel + */ +lv_obj_t * lv_ime_pinyin_get_cand_panel(lv_obj_t * obj); + + +/** + * Set the dictionary of Pinyin input method. + * @param obj pointer to a Pinyin input method object + * @return pointer to the Pinyin input method dictionary + */ +lv_pinyin_dict_t * lv_ime_pinyin_get_dict(lv_obj_t * obj); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_IME_PINYIN*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_USE_IME_PINYIN*/ + + diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.c b/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.c new file mode 100644 index 000000000..ad4ab6023 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.c @@ -0,0 +1,126 @@ +/** + * @file lv_imgfont.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_imgfont.h" + +#if LV_USE_IMGFONT + +/********************* + * DEFINES + *********************/ +#define LV_IMGFONT_PATH_MAX_LEN 64 + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + lv_font_t * font; + lv_get_imgfont_path_cb_t path_cb; + char path[LV_IMGFONT_PATH_MAX_LEN]; +} imgfont_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static const uint8_t * imgfont_get_glyph_bitmap(const lv_font_t * font, uint32_t unicode); +static bool imgfont_get_glyph_dsc(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, + uint32_t unicode, uint32_t unicode_next); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +lv_font_t * lv_imgfont_create(uint16_t height, lv_get_imgfont_path_cb_t path_cb) +{ + LV_ASSERT_MSG(LV_IMGFONT_PATH_MAX_LEN > sizeof(lv_img_dsc_t), + "LV_IMGFONT_PATH_MAX_LEN must be greater than sizeof(lv_img_dsc_t)"); + + size_t size = sizeof(imgfont_dsc_t) + sizeof(lv_font_t); + imgfont_dsc_t * dsc = (imgfont_dsc_t *)lv_mem_alloc(size); + if(dsc == NULL) return NULL; + lv_memset_00(dsc, size); + + dsc->font = (lv_font_t *)(((char *)dsc) + sizeof(imgfont_dsc_t)); + dsc->path_cb = path_cb; + + lv_font_t * font = dsc->font; + font->dsc = dsc; + font->get_glyph_dsc = imgfont_get_glyph_dsc; + font->get_glyph_bitmap = imgfont_get_glyph_bitmap; + font->subpx = LV_FONT_SUBPX_NONE; + font->line_height = height; + font->base_line = 0; + font->underline_position = 0; + font->underline_thickness = 0; + + return dsc->font; +} + +void lv_imgfont_destroy(lv_font_t * font) +{ + if(font == NULL) { + return; + } + + imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc; + lv_mem_free(dsc); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static const uint8_t * imgfont_get_glyph_bitmap(const lv_font_t * font, uint32_t unicode) +{ + LV_UNUSED(unicode); + LV_ASSERT_NULL(font); + imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc; + return (uint8_t *)dsc->path; +} + +static bool imgfont_get_glyph_dsc(const lv_font_t * font, lv_font_glyph_dsc_t * dsc_out, + uint32_t unicode, uint32_t unicode_next) +{ + LV_ASSERT_NULL(font); + + imgfont_dsc_t * dsc = (imgfont_dsc_t *)font->dsc; + LV_ASSERT_NULL(dsc); + if(dsc->path_cb == NULL) return false; + + if(!dsc->path_cb(dsc->font, dsc->path, LV_IMGFONT_PATH_MAX_LEN, unicode, unicode_next)) { + return false; + } + + lv_img_header_t header; + if(lv_img_decoder_get_info(dsc->path, &header) != LV_RES_OK) { + return false; + } + + dsc_out->is_placeholder = 0; + dsc_out->adv_w = header.w; + dsc_out->box_w = header.w; + dsc_out->box_h = header.h; + dsc_out->bpp = LV_IMGFONT_BPP; /* is image identifier */ + dsc_out->ofs_x = 0; + dsc_out->ofs_y = 0; + + return true; +} + +#endif /*LV_USE_IMGFONT*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.h b/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.h new file mode 100644 index 000000000..5069b62f4 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/imgfont/lv_imgfont.h @@ -0,0 +1,60 @@ +/** + * @file lv_imgfont.h + * + */ + +#ifndef LV_IMGFONT_H +#define LV_IMGFONT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" + +#if LV_USE_IMGFONT + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* gets the image path name of this character */ +typedef bool (*lv_get_imgfont_path_cb_t)(const lv_font_t * font, void * img_src, + uint16_t len, uint32_t unicode, uint32_t unicode_next); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Creates a image font with info parameter specified. + * @param height font size + * @param path_cb a function to get the image path name of character. + * @return pointer to the new imgfont or NULL if create error. + */ +lv_font_t * lv_imgfont_create(uint16_t height, lv_get_imgfont_path_cb_t path_cb); + +/** + * Destroy a image font that has been created. + * @param font pointer to image font handle. + */ +void lv_imgfont_destroy(lv_font_t * font); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_IMGFONT*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /* LV_IMGFONT_H */ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/lv_others.h b/lib/libesp32_lvgl/lvgl/src/extra/others/lv_others.h index a4338666a..813e338c3 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/others/lv_others.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/lv_others.h @@ -16,6 +16,11 @@ extern "C" { #include "snapshot/lv_snapshot.h" #include "monkey/lv_monkey.h" #include "gridnav/lv_gridnav.h" +// TASMOTA specific +// #include "fragment/lv_fragment.h" +// #include "imgfont/lv_imgfont.h" +// #include "msg/lv_msg.h" +// #include "ime/lv_ime_pinyin.h" /********************* * DEFINES diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.c b/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.c new file mode 100644 index 000000000..8fd434d06 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.c @@ -0,0 +1,172 @@ +/** + * @file lv_msg.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_msg.h" +#if LV_USE_MSG + +#include "../../../misc/lv_assert.h" +#include "../../../misc/lv_ll.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + uint32_t msg_id; + lv_msg_subscribe_cb_t callback; + void * user_data; + void * _priv_data; /*Internal: used only store 'obj' in lv_obj_subscribe*/ +} sub_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void notify(lv_msg_t * m); +static void obj_notify_cb(void * s, lv_msg_t * m); +static void obj_delete_event_cb(lv_event_t * e); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_ll_t subs_ll; + +/********************** + * GLOBAL VARIABLES + **********************/ +lv_event_code_t LV_EVENT_MSG_RECEIVED; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_msg_init(void) +{ + LV_EVENT_MSG_RECEIVED = lv_event_register_id(); + _lv_ll_init(&subs_ll, sizeof(sub_dsc_t)); +} + +void * lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data) +{ + sub_dsc_t * s = _lv_ll_ins_tail(&subs_ll); + LV_ASSERT_MALLOC(s); + if(s == NULL) return NULL; + + lv_memset_00(s, sizeof(*s)); + + s->msg_id = msg_id; + s->callback = cb; + s->user_data = user_data; + return s; +} + +void * lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data) +{ + sub_dsc_t * s = lv_msg_subsribe(msg_id, obj_notify_cb, user_data); + if(s == NULL) return NULL; + s->_priv_data = obj; + + /*If not added yet, add a delete event cb which automatically unsubcribes the object*/ + sub_dsc_t * s_first = lv_obj_get_event_user_data(obj, obj_delete_event_cb); + if(s_first == NULL) { + lv_obj_add_event_cb(obj, obj_delete_event_cb, LV_EVENT_DELETE, s); + } + return s; +} + +void lv_msg_unsubscribe(void * s) +{ + LV_ASSERT_NULL(s); + _lv_ll_remove(&subs_ll, s); + lv_mem_free(s); +} + +void lv_msg_send(uint32_t msg_id, const void * payload) +{ + lv_msg_t m; + lv_memset_00(&m, sizeof(m)); + m.id = msg_id; + m.payload = payload; + notify(&m); +} + +uint32_t lv_msg_get_id(lv_msg_t * m) +{ + return m->id; +} + +const void * lv_msg_get_payload(lv_msg_t * m) +{ + return m->payload; +} + +void * lv_msg_get_user_data(lv_msg_t * m) +{ + return m->user_data; +} + +lv_msg_t * lv_event_get_msg(lv_event_t * e) +{ + if(e->code == LV_EVENT_MSG_RECEIVED) { + return lv_event_get_param(e); + } + else { + LV_LOG_WARN("Not interpreted with this event code"); + return NULL; + } +} + + + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void notify(lv_msg_t * m) +{ + sub_dsc_t * s; + _LV_LL_READ(&subs_ll, s) { + if(s->msg_id == m->id && s->callback) { + m->user_data = s->user_data; + m->_priv_data = s->_priv_data; + s->callback(s, m); + } + } +} + +static void obj_notify_cb(void * s, lv_msg_t * m) +{ + LV_UNUSED(s); + lv_event_send(m->_priv_data, LV_EVENT_MSG_RECEIVED, m); +} + +static void obj_delete_event_cb(lv_event_t * e) +{ + lv_obj_t * obj = lv_event_get_target(e); + + sub_dsc_t * s = _lv_ll_get_head(&subs_ll); + sub_dsc_t * s_next; + while(s) { + /*On unsubscribe the list changes s becomes invalid so get next item while it's surely valid*/ + s_next = _lv_ll_get_next(&subs_ll, s); + if(s->_priv_data == obj) { + lv_msg_unsubscribe(s); + } + s = s_next; + } +} + +#endif /*LV_USE_MSG*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.h b/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.h new file mode 100644 index 000000000..11a55b5a7 --- /dev/null +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/msg/lv_msg.h @@ -0,0 +1,124 @@ +/** + * @file lv_msg.h + * + */ + +#ifndef LV_MSG_H +#define LV_MSG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../core/lv_obj.h" +#if LV_USE_MSG + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + uint32_t id; /*Identifier of the message*/ + void * user_data; /*Set the the user_data set in `lv_msg_subscribe`*/ + void * _priv_data; /*Used internally*/ + const void * payload; /*Pointer to the data of the message*/ +} lv_msg_t; + +typedef void (*lv_msg_subscribe_cb_t)(void * s, lv_msg_t * msg); + +typedef void (*lv_msg_request_cb_t)(void * r, uint32_t msg_id); + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Called internally to initialize the message module + */ +void lv_msg_init(void); + +/** + * Subscribe to an `msg_id` + * @param msg_id the message ID to listen to + * @param cb callback to call if a message with `msg_id` was sent + * @param user_data arbitrary data which will be available in `cb` too + * @return pointer to a "subscribe object". It can be used the unsubscribe. + */ +void * lv_msg_subsribe(uint32_t msg_id, lv_msg_subscribe_cb_t cb, void * user_data); + +/** + * Subscribe an `lv_obj` to a message. + * `LV_EVENT_MSG_RECEIVED` will be triggered if a message with matching ID was sent + * @param msg_id the message ID to listen to + * @param obj pointer to an `lv_obj` + * @param user_data arbitrary data which will be available in `cb` too + * @return pointer to a "subscribe object". It can be used the unsubscribe. + */ +void * lv_msg_subsribe_obj(uint32_t msg_id, lv_obj_t * obj, void * user_data); + +/** + * Cancel a previous subscription + * @param s pointer to a "subscibe object". + * Return value of `lv_msg_subsribe` or `lv_msg_subsribe_obj` + */ +void lv_msg_unsubscribe(void * s); + +/** + * Send a message with a given ID and payload + * @param msg_id ID of the message to send + * @param data pointer to the data to send + */ +void lv_msg_send(uint32_t msg_id, const void * payload); + +/** + * Get the ID of a message object. Typically used in the subscriber callback. + * @param m pointer to a message object + * @return the ID of the message + */ +uint32_t lv_msg_get_id(lv_msg_t * m); + +/** + * Get the payload of a message object. Typically used in the subscriber callback. + * @param m pointer to a message object + * @return the payload of the message + */ +const void * lv_msg_get_payload(lv_msg_t * m); + +/** + * Get the user data of a message object. Typically used in the subscriber callback. + * @param m pointer to a message object + * @return the user data of the message + */ +void * lv_msg_get_user_data(lv_msg_t * m); + +/** + * Get the message object from an event object. Can be used in `LV_EVENT_MSG_RECEIVED` events. + * @param e pointer to an event object + * @return the message object or NULL if called with unrelated event code. + */ +lv_msg_t * lv_event_get_msg(lv_event_t * e); + +/********************** + * GLOBAL VARIABLES + **********************/ + +extern lv_event_code_t LV_EVENT_MSG_RECEIVED; + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_MSG*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_MSG_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/others/snapshot/lv_snapshot.c b/lib/libesp32_lvgl/lvgl/src/extra/others/snapshot/lv_snapshot.c index e9fa75109..1b2275115 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/others/snapshot/lv_snapshot.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/others/snapshot/lv_snapshot.c @@ -45,7 +45,9 @@ */ uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_img_cf_t cf) { + LV_ASSERT_NULL(obj); switch(cf) { + case LV_IMG_CF_TRUE_COLOR: case LV_IMG_CF_TRUE_COLOR_ALPHA: case LV_IMG_CF_ALPHA_1BIT: case LV_IMG_CF_ALPHA_2BIT: @@ -81,10 +83,12 @@ uint32_t lv_snapshot_buf_size_needed(lv_obj_t * obj, lv_img_cf_t cf) */ lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * dsc, void * buf, uint32_t buff_size) { - LV_ASSERT(dsc); - LV_ASSERT(buf); + LV_ASSERT_NULL(obj); + LV_ASSERT_NULL(dsc); + LV_ASSERT_NULL(buf); switch(cf) { + case LV_IMG_CF_TRUE_COLOR: case LV_IMG_CF_TRUE_COLOR_ALPHA: case LV_IMG_CF_ALPHA_1BIT: case LV_IMG_CF_ALPHA_2BIT: @@ -124,7 +128,6 @@ lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * lv_memset_00(&fake_disp, sizeof(lv_disp_t)); fake_disp.driver = &driver; - lv_draw_ctx_t * draw_ctx = lv_mem_alloc(obj_disp->driver->draw_ctx_size); LV_ASSERT_MALLOC(draw_ctx); if(draw_ctx == NULL) return LV_RES_INV; @@ -138,7 +141,7 @@ lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * lv_disp_t * refr_ori = _lv_refr_get_disp_refreshing(); _lv_refr_set_disp_refreshing(&fake_disp); - lv_refr_obj(draw_ctx, obj); + lv_obj_redraw(draw_ctx, obj); _lv_refr_set_disp_refreshing(refr_ori); obj_disp->driver->draw_ctx_deinit(fake_disp.driver, draw_ctx); @@ -160,14 +163,17 @@ lv_res_t lv_snapshot_take_to_buf(lv_obj_t * obj, lv_img_cf_t cf, lv_img_dsc_t * */ lv_img_dsc_t * lv_snapshot_take(lv_obj_t * obj, lv_img_cf_t cf) { + LV_ASSERT_NULL(obj); uint32_t buff_size = lv_snapshot_buf_size_needed(obj, cf); void * buf = lv_mem_alloc(buff_size); + LV_ASSERT_MALLOC(buf); if(buf == NULL) { return NULL; } lv_img_dsc_t * dsc = lv_mem_alloc(sizeof(lv_img_dsc_t)); + LV_ASSERT_MALLOC(buf); if(dsc == NULL) { lv_mem_free(buf); return NULL; diff --git a/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.c b/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.c index fd5bc2d02..d342455ff 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.c @@ -16,25 +16,28 @@ /********************* * DEFINES *********************/ - +#define COLOR_SCR lv_palette_lighten(LV_PALETTE_GREY, 4) #define COLOR_WHITE lv_color_white() -#define COLOR_LIGHT lv_palette_lighten(LV_PALETTE_GREY, 3) -#define COLOR_MID lv_palette_lighten(LV_PALETTE_GREY, 1) +#define COLOR_LIGHT lv_palette_lighten(LV_PALETTE_GREY, 2) #define COLOR_DARK lv_palette_main(LV_PALETTE_GREY) #define COLOR_DIM lv_palette_darken(LV_PALETTE_GREY, 2) -#define PAD_DEF LV_DPX(5) +#define SCROLLBAR_WIDTH 2 /********************** * TYPEDEFS **********************/ typedef struct { lv_style_t scr; + lv_style_t transp; + lv_style_t white; lv_style_t light; lv_style_t dark; + lv_style_t dim; lv_style_t scrollbar; - lv_style_t pressed; - lv_style_t disabled; - lv_style_t pad_zero; +#if LV_USE_ARC || LV_USE_COLORWHEEL + lv_style_t arc_line; + lv_style_t arc_knob; +#endif #if LV_USE_TEXTAREA lv_style_t ta_cursor; #endif @@ -62,74 +65,60 @@ static bool inited; * STATIC FUNCTIONS **********************/ -static lv_color_t dark_color_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t c, lv_opa_t opa) -{ - LV_UNUSED(f); - return lv_color_darken(c, opa); -} - - -static lv_color_t grey_filter_cb(const lv_color_filter_dsc_t * f, lv_color_t color, lv_opa_t opa) -{ - LV_UNUSED(f); - return lv_color_mix(lv_color_white(), color, opa); -} - static void style_init(void) { style_init_reset(&styles->scrollbar); lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_COVER); - lv_style_set_bg_color(&styles->scrollbar, lv_palette_darken(LV_PALETTE_GREY, 2)); - lv_style_set_width(&styles->scrollbar, PAD_DEF); + lv_style_set_bg_color(&styles->scrollbar, COLOR_DARK); + lv_style_set_width(&styles->scrollbar, SCROLLBAR_WIDTH); style_init_reset(&styles->scr); lv_style_set_bg_opa(&styles->scr, LV_OPA_COVER); - lv_style_set_bg_color(&styles->scr, COLOR_WHITE); + lv_style_set_bg_color(&styles->scr, COLOR_SCR); lv_style_set_text_color(&styles->scr, COLOR_DIM); - lv_style_set_pad_row(&styles->scr, PAD_DEF / 2); - lv_style_set_pad_column(&styles->scr, PAD_DEF / 2); + + + style_init_reset(&styles->transp); + lv_style_set_bg_opa(&styles->transp, LV_OPA_TRANSP); + + style_init_reset(&styles->white); + lv_style_set_bg_opa(&styles->white, LV_OPA_COVER); + lv_style_set_bg_color(&styles->white, COLOR_WHITE); + lv_style_set_line_width(&styles->white, 1); + lv_style_set_line_color(&styles->white, COLOR_WHITE); + lv_style_set_arc_width(&styles->white, 2); + lv_style_set_arc_color(&styles->white, COLOR_WHITE); style_init_reset(&styles->light); lv_style_set_bg_opa(&styles->light, LV_OPA_COVER); lv_style_set_bg_color(&styles->light, COLOR_LIGHT); - lv_style_set_border_color(&styles->light, COLOR_MID); - lv_style_set_border_width(&styles->light, 1); - lv_style_set_pad_all(&styles->light, PAD_DEF); - lv_style_set_pad_gap(&styles->light, PAD_DEF / 2); - lv_style_set_line_width(&styles->light, LV_DPX(2)); - lv_style_set_line_color(&styles->light, COLOR_MID); - lv_style_set_arc_width(&styles->light, LV_DPX(2)); - lv_style_set_arc_color(&styles->light, COLOR_MID); + lv_style_set_line_width(&styles->light, 1); + lv_style_set_line_color(&styles->light, COLOR_LIGHT); + lv_style_set_arc_width(&styles->light, 2); + lv_style_set_arc_color(&styles->light, COLOR_LIGHT); style_init_reset(&styles->dark); lv_style_set_bg_opa(&styles->dark, LV_OPA_COVER); lv_style_set_bg_color(&styles->dark, COLOR_DARK); - lv_style_set_border_color(&styles->dark, COLOR_DIM); - lv_style_set_border_width(&styles->dark, 1); - lv_style_set_pad_all(&styles->dark, PAD_DEF); - lv_style_set_pad_gap(&styles->dark, PAD_DEF / 2); - lv_style_set_line_width(&styles->dark, LV_DPX(2)); - lv_style_set_line_color(&styles->dark, COLOR_DIM); - lv_style_set_arc_width(&styles->dark, LV_DPX(2)); - lv_style_set_arc_color(&styles->dark, COLOR_DIM); + lv_style_set_line_width(&styles->dark, 1); + lv_style_set_line_color(&styles->dark, COLOR_DARK); + lv_style_set_arc_width(&styles->dark, 2); + lv_style_set_arc_color(&styles->dark, COLOR_DARK); - static lv_color_filter_dsc_t dark_filter; - lv_color_filter_dsc_init(&dark_filter, dark_color_filter_cb); + style_init_reset(&styles->dim); + lv_style_set_bg_opa(&styles->dim, LV_OPA_COVER); + lv_style_set_bg_color(&styles->dim, COLOR_DIM); + lv_style_set_line_width(&styles->dim, 1); + lv_style_set_line_color(&styles->dim, COLOR_DIM); + lv_style_set_arc_width(&styles->dim, 2); + lv_style_set_arc_color(&styles->dim, COLOR_DIM); - style_init_reset(&styles->pressed); - lv_style_set_color_filter_dsc(&styles->pressed, &dark_filter); - lv_style_set_color_filter_opa(&styles->pressed, 35); - - static lv_color_filter_dsc_t grey_filter; - lv_color_filter_dsc_init(&grey_filter, grey_filter_cb); - - style_init_reset(&styles->disabled); - lv_style_set_color_filter_dsc(&styles->disabled, &grey_filter); - lv_style_set_color_filter_opa(&styles->disabled, LV_OPA_70); - - style_init_reset(&styles->pad_zero); - lv_style_set_pad_all(&styles->pad_zero, 0); - lv_style_set_pad_gap(&styles->pad_zero, 0); +#if LV_USE_ARC || LV_USE_COLORWHEEL + style_init_reset(&styles->arc_line); + lv_style_set_arc_width(&styles->arc_line, 6); + style_init_reset(&styles->arc_knob); + lv_style_set_pad_all(&styles->arc_knob, 5); +#endif #if LV_USE_TEXTAREA style_init_reset(&styles->ta_cursor); @@ -146,15 +135,21 @@ static void style_init(void) * GLOBAL FUNCTIONS **********************/ +bool lv_theme_basic_is_inited(void) +{ + return LV_GC_ROOT(_lv_theme_basic_styles) == NULL ? false : true; +} + lv_theme_t * lv_theme_basic_init(lv_disp_t * disp) { /*This trick is required only to avoid the garbage collection of *styles' data if LVGL is used in a binding (e.g. Micropython) *In a general case styles could be in simple `static lv_style_t my_style...` variables*/ - if(!inited) { - LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t)); - styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles); + if(!lv_theme_basic_is_inited()) { + inited = false; + LV_GC_ROOT(_lv_theme_basic_styles) = lv_mem_alloc(sizeof(my_theme_styles_t)); + styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_basic_styles); } theme.disp = disp; @@ -165,9 +160,11 @@ lv_theme_t * lv_theme_basic_init(lv_disp_t * disp) style_init(); - inited = true; + if(disp == NULL || lv_disp_get_theme(disp) == &theme) { + lv_obj_report_style_change(NULL); + } - if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL); + inited = true; return (lv_theme_t *)&theme; } @@ -188,12 +185,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) lv_obj_t * parent = lv_obj_get_parent(obj); /*Tabview content area*/ if(lv_obj_check_type(parent, &lv_tabview_class)) { - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->scr, 0); return; } /*Tabview pages*/ else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) { - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->scr, 0); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); return; } @@ -212,14 +209,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) return; } #endif - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->white, 0); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); } #if LV_USE_BTN else if(lv_obj_check_type(obj, &lv_btn_class)) { lv_obj_add_style(obj, &styles->dark, 0); - lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED); - lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED); } #endif @@ -228,27 +223,23 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #if LV_USE_MSGBOX if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_msgbox_class)) { lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); return; } #endif #if LV_USE_TABVIEW if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_tabview_class)) { lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); return; } #endif - lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->dark, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &styles->white, 0); + lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); } #endif #if LV_USE_BAR else if(lv_obj_check_type(obj, &lv_bar_class)) { lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->pad_zero, 0); lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR); } #endif @@ -256,9 +247,8 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #if LV_USE_SLIDER else if(lv_obj_check_type(obj, &lv_slider_class)) { lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->pad_zero, 0); lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR); - lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB); + lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB); } #endif @@ -266,36 +256,30 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) else if(lv_obj_check_type(obj, &lv_table_class)) { lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); } #endif #if LV_USE_CHECKBOX else if(lv_obj_check_type(obj, &lv_checkbox_class)) { lv_obj_add_style(obj, &styles->light, LV_PART_INDICATOR); - lv_obj_add_style(obj, &styles->disabled, LV_PART_INDICATOR | LV_STATE_DISABLED); lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR | LV_STATE_CHECKED); - lv_obj_add_style(obj, &styles->pressed, LV_PART_INDICATOR | LV_STATE_PRESSED); } #endif #if LV_USE_SWITCH else if(lv_obj_check_type(obj, &lv_switch_class)) { lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->pad_zero, 0); - lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR); - lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB); - lv_obj_add_style(obj, &styles->pad_zero, LV_PART_KNOB); + lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB); } #endif #if LV_USE_CHART else if(lv_obj_check_type(obj, &lv_chart_class)) { - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->white, 0); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->light, LV_PART_TICKS); - lv_obj_add_style(obj, &styles->light, LV_PART_CURSOR); + lv_obj_add_style(obj, &styles->dark, LV_PART_TICKS); + lv_obj_add_style(obj, &styles->dark, LV_PART_CURSOR); } #endif @@ -308,24 +292,45 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #if LV_USE_DROPDOWN else if(lv_obj_check_type(obj, &lv_dropdown_class)) { - lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &styles->white, 0); } else if(lv_obj_check_type(obj, &lv_dropdownlist_class)) { - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->white, 0); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); lv_obj_add_style(obj, &styles->light, LV_PART_SELECTED); lv_obj_add_style(obj, &styles->dark, LV_PART_SELECTED | LV_STATE_CHECKED); - lv_obj_add_style(obj, &styles->pressed, LV_PART_SELECTED | LV_STATE_PRESSED); } #endif #if LV_USE_ARC else if(lv_obj_check_type(obj, &lv_arc_class)) { lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->transp, 0); + lv_obj_add_style(obj, &styles->arc_line, 0); lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR); - lv_obj_add_style(obj, &styles->pad_zero, LV_PART_INDICATOR); - lv_obj_add_style(obj, &styles->dark, LV_PART_KNOB); + lv_obj_add_style(obj, &styles->arc_line, LV_PART_INDICATOR); + lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB); + lv_obj_add_style(obj, &styles->arc_knob, LV_PART_KNOB); + } +#endif + +#if LV_USE_SPINNER + else if(lv_obj_check_type(obj, &lv_spinner_class)) { + lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->transp, 0); + lv_obj_add_style(obj, &styles->arc_line, 0); + lv_obj_add_style(obj, &styles->dark, LV_PART_INDICATOR); + lv_obj_add_style(obj, &styles->arc_line, LV_PART_INDICATOR); + } +#endif + +#if LV_USE_COLORWHEEL + else if(lv_obj_check_type(obj, &lv_colorwheel_class)) { + lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->transp, 0); + lv_obj_add_style(obj, &styles->arc_line, 0); + lv_obj_add_style(obj, &styles->dim, LV_PART_KNOB); + lv_obj_add_style(obj, &styles->arc_knob, LV_PART_KNOB); } #endif @@ -337,27 +342,23 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #if LV_USE_TEXTAREA else if(lv_obj_check_type(obj, &lv_textarea_class)) { - lv_obj_add_style(obj, &styles->light, 0); + lv_obj_add_style(obj, &styles->white, 0); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); - lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR); - lv_obj_add_style(obj, &styles->light, LV_PART_TEXTAREA_PLACEHOLDER); + lv_obj_add_style(obj, &styles->ta_cursor, LV_PART_CURSOR | LV_STATE_FOCUSED); } #endif #if LV_USE_CALENDAR else if(lv_obj_check_type(obj, &lv_calendar_class)) { lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS | LV_STATE_PRESSED); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); - lv_obj_add_style(obj, &styles->disabled, LV_PART_ITEMS | LV_STATE_DISABLED); } #endif #if LV_USE_KEYBOARD else if(lv_obj_check_type(obj, &lv_keyboard_class)) { - lv_obj_add_style(obj, &styles->light, 0); - lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS); - lv_obj_add_style(obj, &styles->pressed, LV_PART_ITEMS | LV_STATE_PRESSED); + lv_obj_add_style(obj, &styles->scr, 0); + lv_obj_add_style(obj, &styles->white, LV_PART_ITEMS); + lv_obj_add_style(obj, &styles->light, LV_PART_ITEMS | LV_STATE_CHECKED); } #endif #if LV_USE_LIST @@ -371,7 +372,6 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) } else if(lv_obj_check_type(obj, &lv_list_btn_class)) { lv_obj_add_style(obj, &styles->dark, 0); - lv_obj_add_style(obj, &styles->pressed, LV_STATE_PRESSED); } #endif @@ -417,8 +417,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) static void style_init_reset(lv_style_t * style) { - if(inited) lv_style_reset(style); - else lv_style_init(style); + if(inited) { + lv_style_reset(style); + } + else { + lv_style_init(style); + } } #endif diff --git a/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.h b/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.h index e47ed79ee..93a8fa841 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/themes/basic/lv_theme_basic.h @@ -36,6 +36,12 @@ extern "C" { */ lv_theme_t * lv_theme_basic_init(lv_disp_t * disp); +/** +* Check if the theme is initialized +* @return true if default theme is initialized, false otherwise +*/ +bool lv_theme_basic_is_inited(void); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/themes/default/lv_theme_default.c b/lib/libesp32_lvgl/lvgl/src/extra/themes/default/lv_theme_default.c index 479dba47f..47392b0d1 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/themes/default/lv_theme_default.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/themes/default/lv_theme_default.c @@ -125,6 +125,11 @@ typedef struct { lv_style_t colorwheel_main; #endif +#if LV_USE_MENU + lv_style_t menu_bg, menu_cont, menu_sidebar_cont, menu_main_cont, menu_page, menu_header_cont, menu_header_btn, + menu_section, menu_pressed, menu_separator; +#endif + #if LV_USE_MSGBOX lv_style_t msgbox_bg, msgbox_btn_bg, msgbox_backdrop_bg; #endif @@ -168,11 +173,11 @@ static void style_init_reset(lv_style_t * style); static my_theme_styles_t * styles; static lv_theme_t theme; static disp_size_t disp_size; -static bool inited; static lv_color_t color_scr; static lv_color_t color_text; static lv_color_t color_card; static lv_color_t color_grey; +static bool inited = false; /********************** @@ -213,27 +218,32 @@ static void style_init(void) color_card = theme.flags & MODE_DARK ? DARK_COLOR_CARD : LIGHT_COLOR_CARD; color_grey = theme.flags & MODE_DARK ? DARK_COLOR_GREY : LIGHT_COLOR_GREY; + style_init_reset(&styles->transition_delayed); + style_init_reset(&styles->transition_normal); +#if TRANSITION_TIME static lv_style_transition_dsc_t trans_delayed; lv_style_transition_dsc_init(&trans_delayed, trans_props, lv_anim_path_linear, TRANSITION_TIME, 70, NULL); static lv_style_transition_dsc_t trans_normal; lv_style_transition_dsc_init(&trans_normal, trans_props, lv_anim_path_linear, TRANSITION_TIME, 0, NULL); - style_init_reset(&styles->transition_delayed); lv_style_set_transition(&styles->transition_delayed, &trans_delayed); /*Go back to default state with delay*/ - style_init_reset(&styles->transition_normal); lv_style_set_transition(&styles->transition_normal, &trans_normal); /*Go back to default state with delay*/ +#endif style_init_reset(&styles->scrollbar); - lv_style_set_bg_color(&styles->scrollbar, (theme.flags & MODE_DARK) ? lv_palette_darken(LV_PALETTE_GREY, - 2) : lv_palette_main(LV_PALETTE_GREY)); + lv_color_t sb_color = (theme.flags & MODE_DARK) ? lv_palette_darken(LV_PALETTE_GREY, + 2) : lv_palette_main(LV_PALETTE_GREY); + lv_style_set_bg_color(&styles->scrollbar, sb_color); + lv_style_set_radius(&styles->scrollbar, LV_RADIUS_CIRCLE); - lv_style_set_pad_right(&styles->scrollbar, lv_disp_dpx(theme.disp, 7)); - lv_style_set_pad_top(&styles->scrollbar, lv_disp_dpx(theme.disp, 7)); - lv_style_set_size(&styles->scrollbar, lv_disp_dpx(theme.disp, 5)); + lv_style_set_pad_all(&styles->scrollbar, lv_disp_dpx(theme.disp, 7)); + lv_style_set_width(&styles->scrollbar, lv_disp_dpx(theme.disp, 5)); lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_40); +#if TRANSITION_TIME lv_style_set_transition(&styles->scrollbar, &trans_normal); +#endif style_init_reset(&styles->scrollbar_scrolled); lv_style_set_bg_opa(&styles->scrollbar_scrolled, LV_OPA_COVER); @@ -454,6 +464,67 @@ static void style_init(void) lv_style_set_text_color(&styles->chart_ticks, lv_palette_main(LV_PALETTE_GREY)); #endif +#if LV_USE_MENU + style_init_reset(&styles->menu_bg); + lv_style_set_pad_all(&styles->menu_bg, 0); + lv_style_set_pad_gap(&styles->menu_bg, 0); + lv_style_set_radius(&styles->menu_bg, 0); + lv_style_set_clip_corner(&styles->menu_bg, true); + lv_style_set_border_side(&styles->menu_bg, LV_BORDER_SIDE_NONE); + + style_init_reset(&styles->menu_section); + lv_style_set_radius(&styles->menu_section, RADIUS_DEFAULT); + lv_style_set_clip_corner(&styles->menu_section, true); + lv_style_set_bg_opa(&styles->menu_section, LV_OPA_COVER); + lv_style_set_bg_color(&styles->menu_section, color_card); + lv_style_set_text_color(&styles->menu_section, color_text); + + style_init_reset(&styles->menu_cont); + lv_style_set_pad_hor(&styles->menu_cont, PAD_SMALL); + lv_style_set_pad_ver(&styles->menu_cont, PAD_SMALL); + lv_style_set_pad_gap(&styles->menu_cont, PAD_SMALL); + lv_style_set_border_width(&styles->menu_cont, lv_disp_dpx(theme.disp, 1)); + lv_style_set_border_opa(&styles->menu_cont, LV_OPA_10); + lv_style_set_border_color(&styles->menu_cont, color_text); + lv_style_set_border_side(&styles->menu_cont, LV_BORDER_SIDE_NONE); + + style_init_reset(&styles->menu_sidebar_cont); + lv_style_set_pad_all(&styles->menu_sidebar_cont, 0); + lv_style_set_pad_gap(&styles->menu_sidebar_cont, 0); + lv_style_set_border_width(&styles->menu_sidebar_cont, lv_disp_dpx(theme.disp, 1)); + lv_style_set_border_opa(&styles->menu_sidebar_cont, LV_OPA_10); + lv_style_set_border_color(&styles->menu_sidebar_cont, color_text); + lv_style_set_border_side(&styles->menu_sidebar_cont, LV_BORDER_SIDE_RIGHT); + + style_init_reset(&styles->menu_main_cont); + lv_style_set_pad_all(&styles->menu_main_cont, 0); + lv_style_set_pad_gap(&styles->menu_main_cont, 0); + + style_init_reset(&styles->menu_header_cont); + lv_style_set_pad_hor(&styles->menu_header_cont, PAD_SMALL); + lv_style_set_pad_ver(&styles->menu_header_cont, PAD_TINY); + lv_style_set_pad_gap(&styles->menu_header_cont, PAD_SMALL); + + style_init_reset(&styles->menu_header_btn); + lv_style_set_pad_hor(&styles->menu_header_btn, PAD_TINY); + lv_style_set_pad_ver(&styles->menu_header_btn, PAD_TINY); + lv_style_set_shadow_opa(&styles->menu_header_btn, LV_OPA_TRANSP); + lv_style_set_bg_opa(&styles->menu_header_btn, LV_OPA_TRANSP); + lv_style_set_text_color(&styles->menu_header_btn, color_text); + + style_init_reset(&styles->menu_page); + lv_style_set_pad_hor(&styles->menu_page, 0); + lv_style_set_pad_gap(&styles->menu_page, 0); + + style_init_reset(&styles->menu_pressed); + lv_style_set_bg_opa(&styles->menu_pressed, LV_OPA_20); + lv_style_set_bg_color(&styles->menu_pressed, lv_palette_main(LV_PALETTE_GREY)); + + style_init_reset(&styles->menu_separator); + lv_style_set_bg_opa(&styles->menu_separator, LV_OPA_TRANSP); + lv_style_set_pad_ver(&styles->menu_separator, PAD_TINY); +#endif + #if LV_USE_METER style_init_reset(&styles->meter_marker); lv_style_set_line_width(&styles->meter_marker, lv_disp_dpx(theme.disp, 5)); @@ -581,7 +652,8 @@ lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, l /*This trick is required only to avoid the garbage collection of *styles' data if LVGL is used in a binding (e.g. Micropython) *In a general case styles could be in simple `static lv_style_t my_style...` variables*/ - if(!inited) { + if(!lv_theme_default_is_inited()) { + inited = false; LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t)); styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles); } @@ -601,23 +673,25 @@ lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, l style_init(); - inited = true; - if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL); + inited = true; + return (lv_theme_t *)&theme; } lv_theme_t * lv_theme_default_get(void) { - if(!inited) return NULL; + if(!lv_theme_default_is_inited()) { + return NULL; + } return (lv_theme_t *)&theme; } bool lv_theme_default_is_inited(void) { - return inited; + return LV_GC_ROOT(_lv_theme_default_styles) == NULL ? false : true; } @@ -667,10 +741,10 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #if LV_USE_CALENDAR - else if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_calendar_class)) { - /*No style*/ - return; - } + if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_calendar_class)) { + /*No style*/ + return; + } #endif lv_obj_add_style(obj, &styles->card, 0); @@ -690,6 +764,14 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) #endif lv_obj_add_style(obj, &styles->bg_color_secondary, LV_STATE_CHECKED); lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED); + +#if LV_USE_MENU + if(lv_obj_check_type(lv_obj_get_parent(obj), &lv_menu_sidebar_header_cont_class) || + lv_obj_check_type(lv_obj_get_parent(obj), &lv_menu_main_header_cont_class)) { + lv_obj_add_style(obj, &styles->menu_header_btn, 0); + lv_obj_add_style(obj, &styles->menu_pressed, LV_STATE_PRESSED); + } +#endif } #endif @@ -917,6 +999,7 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) else if(lv_obj_check_type(obj, &lv_textarea_class)) { lv_obj_add_style(obj, &styles->card, 0); lv_obj_add_style(obj, &styles->pad_small, 0); + lv_obj_add_style(obj, &styles->disabled, LV_STATE_DISABLED); lv_obj_add_style(obj, &styles->outline_primary, LV_STATE_FOCUS_KEY); lv_obj_add_style(obj, &styles->outline_secondary, LV_STATE_EDITED); lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); @@ -983,6 +1066,44 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) } #endif +#if LV_USE_MENU + else if(lv_obj_check_type(obj, &lv_menu_class)) { + lv_obj_add_style(obj, &styles->card, 0); + lv_obj_add_style(obj, &styles->menu_bg, 0); + } + else if(lv_obj_check_type(obj, &lv_menu_sidebar_cont_class)) { + lv_obj_add_style(obj, &styles->menu_sidebar_cont, 0); + lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + } + else if(lv_obj_check_type(obj, &lv_menu_main_cont_class)) { + lv_obj_add_style(obj, &styles->menu_main_cont, 0); + lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + } + else if(lv_obj_check_type(obj, &lv_menu_cont_class)) { + lv_obj_add_style(obj, &styles->menu_cont, 0); + lv_obj_add_style(obj, &styles->menu_pressed, LV_STATE_PRESSED); + lv_obj_add_style(obj, &styles->bg_color_primary_muted, LV_STATE_PRESSED | LV_STATE_CHECKED); + lv_obj_add_style(obj, &styles->bg_color_primary_muted, LV_STATE_CHECKED); + lv_obj_add_style(obj, &styles->bg_color_primary, LV_STATE_FOCUS_KEY); + } + else if(lv_obj_check_type(obj, &lv_menu_sidebar_header_cont_class) || + lv_obj_check_type(obj, &lv_menu_main_header_cont_class)) { + lv_obj_add_style(obj, &styles->menu_header_cont, 0); + } + else if(lv_obj_check_type(obj, &lv_menu_page_class)) { + lv_obj_add_style(obj, &styles->menu_page, 0); + lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR); + lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED); + } + else if(lv_obj_check_type(obj, &lv_menu_section_class)) { + lv_obj_add_style(obj, &styles->menu_section, 0); + } + else if(lv_obj_check_type(obj, &lv_menu_separator_class)) { + lv_obj_add_style(obj, &styles->menu_separator, 0); + } +#endif #if LV_USE_MSGBOX else if(lv_obj_check_type(obj, &lv_msgbox_class)) { lv_obj_add_style(obj, &styles->card, 0); @@ -1049,8 +1170,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) static void style_init_reset(lv_style_t * style) { - if(inited) lv_style_reset(style); - else lv_style_init(style); + if(inited) { + lv_style_reset(style); + } + else { + lv_style_init(style); + } } #endif diff --git a/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.c b/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.c index 637564274..b249e76d5 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.c @@ -164,6 +164,11 @@ static void style_init(bool dark_bg, const lv_font_t * font) * GLOBAL FUNCTIONS **********************/ +bool lv_theme_mono_is_inited(void) +{ + return LV_GC_ROOT(_lv_theme_default_styles) == NULL ? false : true; +} + lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font) { @@ -171,6 +176,7 @@ lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t *styles' data if LVGL is used in a binding (e.g. Micropython) *In a general case styles could be in simple `static lv_style_t my_style...` variables*/ if(!inited) { + inited = false; LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t)); styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles); } @@ -183,10 +189,10 @@ lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t style_init(dark_bg, font); - inited = true; - if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL); + inited = true; + return (lv_theme_t *)&theme; } @@ -487,8 +493,12 @@ static void theme_apply(lv_theme_t * th, lv_obj_t * obj) static void style_init_reset(lv_style_t * style) { - if(inited) lv_style_reset(style); - else lv_style_init(style); + if(inited) { + lv_style_reset(style); + } + else { + lv_style_init(style); + } } #endif diff --git a/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.h b/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.h index 789623a27..10b8f186d 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/themes/mono/lv_theme_mono.h @@ -38,6 +38,12 @@ extern "C" { */ lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font); +/** +* Check if the theme is initialized +* @return true if default theme is initialized, false otherwise +*/ +bool lv_theme_mono_is_inited(void); + /********************** * MACROS **********************/ @@ -48,4 +54,4 @@ lv_theme_t * lv_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t } /*extern "C"*/ #endif -#endif /*LV_THEME_DEFAULT_H*/ +#endif /*LV_USE_THEME_MONO_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/calendar/lv_calendar.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/calendar/lv_calendar.c index f469eae5c..b806d2529 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/calendar/lv_calendar.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/calendar/lv_calendar.c @@ -120,7 +120,7 @@ void lv_calendar_set_showed_date(lv_obj_t * obj, uint32_t year, uint32_t month) d.month = calendar->showed_date.month; d.day = calendar->showed_date.day; - uint8_t i; + uint32_t i; /*Remove the disabled state but revert it for day names*/ lv_btnmatrix_clear_btn_ctrl_all(calendar->btnm, LV_BTNMATRIX_CTRL_DISABLED); @@ -262,7 +262,7 @@ static void lv_calendar_constructor(const lv_obj_class_t * class_p, lv_obj_t * o if(i != 0 && (i + 1) % 8 == 0) { calendar->map[i] = "\n"; } - else if(i < 8) { + else if(i < 7) { calendar->map[i] = day_names_def[i]; } else { diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.c index a6e09fb45..da6c18c0e 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.c @@ -539,7 +539,6 @@ void lv_chart_set_next_value(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t invalidate_point(obj, ser->start_point); ser->start_point = (ser->start_point + 1) % chart->point_cnt; invalidate_point(obj, ser->start_point); - lv_chart_refresh(obj); } void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t x_value, lv_coord_t y_value) @@ -556,11 +555,8 @@ void lv_chart_set_next_value2(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_ ser->x_points[ser->start_point] = x_value; ser->y_points[ser->start_point] = y_value; - invalidate_point(obj, ser->start_point); ser->start_point = (ser->start_point + 1) % chart->point_cnt; invalidate_point(obj, ser->start_point); - lv_chart_refresh(obj); - } void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t value) @@ -571,7 +567,7 @@ void lv_chart_set_value_by_id(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t if(id >= chart->point_cnt) return; ser->y_points[id] = value; - lv_chart_refresh(obj); + invalidate_point(obj, id); } void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t id, lv_coord_t x_value, @@ -589,7 +585,7 @@ void lv_chart_set_value_by_id2(lv_obj_t * obj, lv_chart_series_t * ser, uint16_t if(id >= chart->point_cnt) return; ser->x_points[id] = x_value; ser->y_points[id] = y_value; - lv_chart_refresh(obj); + invalidate_point(obj, id); } void lv_chart_set_ext_y_array(lv_obj_t * obj, lv_chart_series_t * ser, lv_coord_t array[]) @@ -720,7 +716,7 @@ static void lv_chart_event(const lv_obj_class_t * class_p, lv_event_t * e) p.x -= obj->coords.x1; uint32_t id = get_index_from_x(obj, p.x + lv_obj_get_scroll_left(obj)); - if(id != chart->pressed_point_id) { + if(id != (uint32_t)chart->pressed_point_id) { invalidate_point(obj, id); invalidate_point(obj, chart->pressed_point_id); chart->pressed_point_id = id; @@ -908,7 +904,7 @@ static void draw_series_line(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) if(LV_MIN(point_w, point_h) > line_dsc_default.width / 2) line_dsc_default.raw_end = 1; if(line_dsc_default.width == 1) line_dsc_default.raw_end = 1; - /*If there are mire points than pixels draw only vertical lines*/ + /*If there are at least as much points as pixels then draw only vertical lines*/ bool crowded_mode = chart->point_cnt >= w ? true : false; /*Go through all data lines*/ @@ -1201,11 +1197,13 @@ static void draw_series_bar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) int32_t block_gap = ((int32_t)lv_obj_get_style_pad_column(obj, LV_PART_MAIN) * chart->zoom_x) >> 8; /*Gap between the column on ~adjacent X*/ lv_coord_t block_w = (w - ((chart->point_cnt - 1) * block_gap)) / chart->point_cnt; - lv_coord_t col_w = block_w / ser_cnt; int32_t ser_gap = ((int32_t)lv_obj_get_style_pad_column(obj, - LV_PART_ITEMS) * chart->zoom_x) >> 8; /*Gap between the column on the ~same X*/ - lv_coord_t x_ofs = pad_left - lv_obj_get_scroll_left(obj); - lv_coord_t y_ofs = pad_top - lv_obj_get_scroll_top(obj); + LV_PART_ITEMS) * chart->zoom_x) >> 8; /*Gap between the columns on the ~same X*/ + lv_coord_t col_w = (block_w - (ser_cnt - 1) * ser_gap) / ser_cnt; + + lv_coord_t border_w = lv_obj_get_style_border_width(obj, LV_PART_MAIN); + lv_coord_t x_ofs = pad_left - lv_obj_get_scroll_left(obj) + border_w; + lv_coord_t y_ofs = pad_top - lv_obj_get_scroll_top(obj) + border_w; lv_draw_rect_dsc_t col_dsc; lv_draw_rect_dsc_init(&col_dsc); @@ -1224,7 +1222,7 @@ static void draw_series_bar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) /*Go through all points*/ for(i = 0; i < chart->point_cnt; i++) { - lv_coord_t x_act = (int32_t)((int32_t)(w + block_gap) * i) / (chart->point_cnt) + obj->coords.x1 + x_ofs; + lv_coord_t x_act = (int32_t)((int32_t)(w - block_w) * i) / (chart->point_cnt - 1) + obj->coords.x1 + x_ofs; part_draw_dsc.id = i; @@ -1234,8 +1232,8 @@ static void draw_series_bar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) lv_coord_t start_point = chart->update_mode == LV_CHART_UPDATE_MODE_SHIFT ? ser->start_point : 0; col_a.x1 = x_act; - col_a.x2 = col_a.x1 + col_w - ser_gap - 1; - x_act += col_w; + col_a.x2 = col_a.x1 + col_w - 1; + x_act += col_w + ser_gap; if(col_a.x2 < clip_area.x1) continue; if(col_a.x1 > clip_area.x2) break; @@ -1329,7 +1327,8 @@ static void draw_cursors(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) cy += obj->coords.y1; lv_area_t point_area; - if(point_w && point_h) { + bool draw_point = point_w && point_h; + if(draw_point) { point_area.x1 = cx - point_w; point_area.x2 = cx + point_w; point_area.y1 = cy - point_h; @@ -1349,7 +1348,11 @@ static void draw_cursors(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); lv_draw_line(draw_ctx, &line_dsc_tmp, &p1, &p2); - lv_draw_rect(draw_ctx, &point_dsc_tmp, &point_area); + + if(draw_point) { + lv_draw_rect(draw_ctx, &point_dsc_tmp, &point_area); + } + lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); } @@ -1361,7 +1364,11 @@ static void draw_cursors(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); lv_draw_line(draw_ctx, &line_dsc_tmp, &p1, &p2); - lv_draw_rect(draw_ctx, &point_dsc_tmp, &point_area); + + if(draw_point) { + lv_draw_rect(draw_ctx, &point_dsc_tmp, &point_area); + } + lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); } } @@ -1515,7 +1522,6 @@ static void draw_x_ticks(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_chart_axis lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t w = ((int32_t)lv_obj_get_content_width(obj) * chart->zoom_x) >> 8; - lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(&label_dsc); lv_obj_init_draw_label_dsc(obj, LV_PART_TICKS, &label_dsc); @@ -1559,6 +1565,7 @@ static void draw_x_ticks(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_chart_axis int32_t block_gap = ((int32_t)lv_obj_get_style_pad_column(obj, LV_PART_MAIN) * chart->zoom_x) >> 8; /*Gap between the columns on ~adjacent X*/ lv_coord_t block_w = (w + block_gap) / (chart->point_cnt); + x_ofs += (block_w - block_gap) / 2; w -= block_w - block_gap; } @@ -1708,11 +1715,13 @@ static void invalidate_point(lv_obj_t * obj, uint16_t i) lv_area_t col_a; int32_t block_gap = ((int32_t)lv_obj_get_style_pad_column(obj, LV_PART_MAIN) * chart->zoom_x) >> 8; /*Gap between the column on ~adjacent X*/ + lv_coord_t block_w = (w + block_gap) / chart->point_cnt; + lv_coord_t bwidth = lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t x_act; x_act = (int32_t)((int32_t)(block_w) * i) ; - x_act += obj->coords.x1 + lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + x_act += obj->coords.x1 + bwidth + lv_obj_get_style_pad_left(obj, LV_PART_MAIN); lv_obj_get_coords(obj, &col_a); col_a.x1 = x_act - scroll_left; @@ -1721,9 +1730,6 @@ static void invalidate_point(lv_obj_t * obj, uint16_t i) lv_obj_invalidate_area(obj, &col_a); } - else if(chart->type == LV_CHART_TYPE_SCATTER) { - lv_obj_invalidate(obj); - } else { lv_obj_invalidate(obj); } diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.h b/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.h index 8a9b8cfc0..394c0e7b0 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/chart/lv_chart.h @@ -22,7 +22,11 @@ extern "C" { *********************/ /**Default value of points. Can be used to not draw a point*/ +#if LV_USE_LARGE_COORD +#define LV_CHART_POINT_NONE (INT32_MAX) +#else #define LV_CHART_POINT_NONE (INT16_MAX) +#endif LV_EXPORT_CONST_INT(LV_CHART_POINT_NONE); /********************** @@ -78,7 +82,7 @@ typedef struct { typedef struct { lv_point_t pos; - uint16_t point_id; + lv_coord_t point_id; lv_color_t color; lv_chart_series_t * ser; lv_dir_t dir; @@ -104,7 +108,7 @@ typedef struct { lv_coord_t ymax[2]; lv_coord_t xmin[2]; lv_coord_t xmax[2]; - uint16_t pressed_point_id; + lv_coord_t pressed_point_id; uint16_t hdiv_cnt; /**< Number of horizontal division lines*/ uint16_t vdiv_cnt; /**< Number of vertical division lines*/ uint16_t point_cnt; /**< Point number in a data line*/ @@ -339,7 +343,7 @@ void lv_chart_set_cursor_pos(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_po * @param obj pointer to a chart object * @param cursor pointer to the cursor * @param ser pointer to a series - * @param point_id the point's index or `LV_CHART_POINT_NONE` to not assign to any points. + * @param point_id the point's index or `LV_CHART_POINT_NONE` to not assign to any points. */ void lv_chart_set_cursor_point(lv_obj_t * chart, lv_chart_cursor_t * cursor, lv_chart_series_t * ser, uint16_t point_id); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/keyboard/lv_keyboard.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/keyboard/lv_keyboard.c index 6ba431eb9..8e052e33a 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/keyboard/lv_keyboard.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/keyboard/lv_keyboard.c @@ -285,19 +285,19 @@ void lv_keyboard_def_event_cb(lv_event_t * e) if(strcmp(txt, "abc") == 0) { keyboard->mode = LV_KEYBOARD_MODE_TEXT_LOWER; lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_LOWER]); - lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_LOWER]); + lv_keyboard_update_ctrl_map(obj); return; } else if(strcmp(txt, "ABC") == 0) { keyboard->mode = LV_KEYBOARD_MODE_TEXT_UPPER; lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_TEXT_UPPER]); - lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_TEXT_UPPER]); + lv_keyboard_update_ctrl_map(obj); return; } else if(strcmp(txt, "1#") == 0) { keyboard->mode = LV_KEYBOARD_MODE_SPECIAL; lv_btnmatrix_set_map(obj, kb_map[LV_KEYBOARD_MODE_SPECIAL]); - lv_btnmatrix_set_ctrl_map(obj, kb_ctrl[LV_KEYBOARD_MODE_SPECIAL]); + lv_keyboard_update_ctrl_map(obj); return; } else if(strcmp(txt, LV_SYMBOL_CLOSE) == 0 || strcmp(txt, LV_SYMBOL_KEYBOARD) == 0) { diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.c index ea9ced729..29355fd3c 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.c @@ -73,7 +73,7 @@ lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt) return obj; } -lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt) +lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * icon, const char * txt) { LV_LOG_INFO("begin"); lv_obj_t * obj = lv_obj_class_create_obj(&lv_list_btn_class, list); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.h b/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.h index 8b9164415..0da5595bc 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/list/lv_list.h @@ -37,7 +37,7 @@ lv_obj_t * lv_list_create(lv_obj_t * parent); lv_obj_t * lv_list_add_text(lv_obj_t * list, const char * txt); -lv_obj_t * lv_list_add_btn(lv_obj_t * list, const char * icon, const char * txt); +lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * icon, const char * txt); const char * lv_list_get_btn_text(lv_obj_t * list, lv_obj_t * btn); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/menu/lv_menu.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/menu/lv_menu.c index 9c6b5423a..78577e770 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/menu/lv_menu.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/menu/lv_menu.c @@ -124,7 +124,15 @@ lv_obj_t * lv_menu_page_create(lv_obj_t * parent, char * title) lv_obj_class_init_obj(obj); lv_menu_page_t * page = (lv_menu_page_t *)obj; - page->title = title; + if(title) { + page->title = lv_mem_alloc(strlen(title) + 1); + LV_ASSERT_MALLOC(page->title); + if(page->title == NULL) return NULL; + strcpy(page->title, title); + } + else { + page->title = NULL; + } return obj; } @@ -196,6 +204,7 @@ void lv_menu_set_page(lv_obj_t * obj, lv_obj_t * page) /* Add a new node */ lv_ll_t * history_ll = &(menu->history_ll); lv_menu_history_t * new_node = _lv_ll_ins_head(history_ll); + LV_ASSERT_MALLOC(new_node); new_node->page = page; menu->cur_depth++; @@ -352,8 +361,9 @@ void lv_menu_set_load_page_event(lv_obj_t * menu, lv_obj_t * obj, lv_obj_t * pag { LV_ASSERT_OBJ(menu, MY_CLASS); - /* Make the object clickable */ lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); /* Remove old event */ if(lv_obj_remove_event_cb(obj, lv_menu_load_page_event_cb)) { @@ -675,6 +685,11 @@ static void lv_menu_load_page_event_cb(lv_event_t * e) } lv_menu_set_page((lv_obj_t *)menu, page); + + if(lv_group_get_default() != NULL && menu->sidebar_page == NULL) { + /* Sidebar is not supported for now*/ + lv_group_focus_next(lv_group_get_default()); + } } static void lv_menu_obj_del_event_cb(lv_event_t * e) diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/meter/lv_meter.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/meter/lv_meter.c index c12dc33c9..668ab97e9 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/meter/lv_meter.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/meter/lv_meter.c @@ -379,8 +379,6 @@ static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, cons p_center.x = scale_area->x1 + r_edge; p_center.y = scale_area->y1 + r_edge; - uint8_t i; - lv_draw_line_dsc_t line_dsc; lv_draw_line_dsc_init(&line_dsc); lv_obj_init_draw_line_dsc(obj, LV_PART_TICKS, &line_dsc); @@ -406,7 +404,7 @@ static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, cons _LV_LL_READ_BACK(&meter->scale_ll, scale) { part_draw_dsc.sub_part_ptr = scale; - lv_coord_t r_out = r_edge + scale->r_mod; + lv_coord_t r_out = r_edge; lv_coord_t r_in_minor = r_out - scale->tick_length; lv_coord_t r_in_major = r_out - scale->tick_major_length; @@ -435,6 +433,7 @@ static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, cons int16_t inner_act_mask_id = LV_MASK_ID_INV; /*Will be added later*/ uint32_t minor_cnt = scale->tick_major_nth ? scale->tick_major_nth - 1 : 0xFFFF; + uint16_t i; for(i = 0; i < scale->tick_cnt; i++) { minor_cnt++; bool major = false; @@ -474,32 +473,20 @@ static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, cons } } - /*`* 256` for extra precision*/ - int32_t angle_upscale = ((i * scale->angle_range) << 8) / (scale->tick_cnt - 1); - - int32_t angle_low = (angle_upscale >> 8); - int32_t angle_high = angle_low + 1; - int32_t angle_rem = angle_upscale & 0xFF; - - /*Interpolate sine and cos*/ - int32_t sin_low = lv_trigo_sin(angle_low + scale->rotation); - int32_t sin_high = lv_trigo_sin(angle_high + scale->rotation); - int32_t sin_mid = (sin_low * (256 - angle_rem) + sin_high * angle_rem) >> 8; - - int32_t cos_low = lv_trigo_cos(angle_low + scale->rotation); - int32_t cos_high = lv_trigo_cos(angle_high + scale->rotation); - int32_t cos_mid = (cos_low * (256 - angle_rem) + cos_high * angle_rem) >> 8; + int32_t angle_upscale = ((i * scale->angle_range) * 10) / (scale->tick_cnt - 1) + + scale->rotation * 10; line_dsc.color = line_color; line_dsc.width = line_width; - /*Use the interpolated angle to get the outer x and y coordinates. - *Draw a little bit longer lines to be sure the mask will clip them correctly*/ - lv_point_t p_outer; - p_outer.x = (int32_t)(((int32_t)cos_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.x; - p_outer.y = (int32_t)(((int32_t)sin_mid * (r_out + line_width) + 127) >> (LV_TRIGO_SHIFT)) + p_center.y; - part_draw_dsc.p1 = &p_outer; + /*Draw a little bit longer lines to be sure the mask will clip them correctly + *and to get a better precision*/ + lv_point_t p_outer; + p_outer.x = p_center.x + r_out + LV_MAX(LV_DPI_DEF, r_out); + p_outer.y = p_center.y; + lv_point_transform(&p_outer, angle_upscale, 256, &p_center); + part_draw_dsc.p1 = &p_center; + part_draw_dsc.p2 = &p_outer; part_draw_dsc.id = i; part_draw_dsc.label_dsc = &label_dsc; @@ -508,8 +495,9 @@ static void draw_ticks_and_labels(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, cons lv_draw_mask_remove_id(outer_mask_id); uint32_t r_text = r_in_major - scale->label_gap; lv_point_t p; - p.x = (int32_t)((int32_t)((int32_t)cos_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.x; - p.y = (int32_t)((int32_t)((int32_t)sin_mid * r_text + 127) >> LV_TRIGO_SHIFT) + p_center.y; + p.x = p_center.x + r_text; + p.y = p_center.y; + lv_point_transform(&p, angle_upscale, 256, &p_center); lv_draw_label_dsc_t label_dsc_tmp; lv_memcpy(&label_dsc_tmp, &label_dsc, sizeof(label_dsc_tmp)); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/msgbox/lv_msgbox.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/msgbox/lv_msgbox.c index a62c9ae63..8db5df7ee 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/msgbox/lv_msgbox.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/msgbox/lv_msgbox.c @@ -74,8 +74,8 @@ lv_obj_t * lv_msgbox_create(lv_obj_t * parent, const char * title, const char * lv_obj_t * obj = lv_obj_class_create_obj(&lv_msgbox_class, parent); LV_ASSERT_MALLOC(obj); - lv_obj_class_init_obj(obj); if(obj == NULL) return NULL; + lv_obj_class_init_obj(obj); lv_msgbox_t * mbox = (lv_msgbox_t *)obj; if(auto_parent) lv_obj_add_flag(obj, LV_MSGBOX_FLAG_AUTO_PARENT); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.c index 4bba67611..96f044760 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.c @@ -209,6 +209,14 @@ void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode) lv_spangroup_refr_mode(obj); } +void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + spans->lines = lines; + lv_spangroup_refr_mode(obj); +} + /*===================== * Getter functions *====================*/ @@ -289,6 +297,13 @@ lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj) return spans->mode; } +int32_t lv_spangroup_get_lines(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_spangroup_t * spans = (lv_spangroup_t *)obj; + return spans->lines; +} + void lv_spangroup_refr_mode(lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -400,6 +415,8 @@ lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width) lv_snippet_t snippet; /* use to save cur_span info and push it to stack */ memset(&snippet, 0, sizeof(snippet)); + int32_t line_cnt = 0; + int32_t lines = spans->lines < 0 ? INT32_MAX : spans->lines; /* the loop control how many lines need to draw */ while(cur_span) { int snippet_cnt = 0; @@ -467,6 +484,10 @@ lv_coord_t lv_spangroup_get_expand_height(lv_obj_t * obj, lv_coord_t width) txt_pos.x = 0; txt_pos.y += max_line_h; max_w = max_width; + line_cnt += 1; + if(line_cnt >= lines) { + break; + } } txt_pos.y -= line_space; @@ -483,6 +504,7 @@ static void lv_spangroup_constructor(const lv_obj_class_t * class_p, lv_obj_t * lv_spangroup_t * spans = (lv_spangroup_t *)obj; _lv_ll_init(&spans->child_ll, sizeof(lv_span_t)); spans->indent = 0; + spans->lines = -1; spans->mode = LV_SPAN_MODE_EXPAND; spans->overflow = LV_SPAN_OVERFLOW_CLIP; spans->cache_w = 0; @@ -780,6 +802,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) bool is_end_line = false; bool ellipsis_valid = false; lv_coord_t max_line_h = 0; /* the max height of span-font when a line have a lot of span */ + lv_coord_t max_baseline = 0; /*baseline of the highest span*/ lv_snippet_clear(); /* the loop control to find a line and push the relevant span info into stack */ @@ -803,15 +826,6 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) snippet.line_h = lv_font_get_line_height(snippet.font) + line_space; } - if(spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS) { - /* curretn line span txt overflow, don't push */ - if(txt_pos.y + snippet.line_h - line_space > coords.y2 + 1) { - ellipsis_valid = true; - is_end_line = true; - break; - } - } - /* get current span text line info */ uint32_t next_ofs = 0; lv_coord_t use_width = 0; @@ -819,24 +833,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) max_w, txt_flag, &use_width, &next_ofs); if(isfill) { - lv_coord_t next_line_h = snippet.line_h; - if(cur_txt[cur_txt_ofs + next_ofs] == '\0') { - next_line_h = 0; - lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, cur_span); - if(next_span) { /* have the next line */ - next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space; - } - } - lv_coord_t cur_line_h = max_line_h < snippet.line_h ? snippet.line_h : max_line_h; - if(txt_pos.y + cur_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */ - if(cur_txt[cur_txt_ofs + next_ofs] != '\0') { - next_ofs = strlen(&cur_txt[cur_txt_ofs]); - use_width = lv_txt_get_width(&cur_txt[cur_txt_ofs], next_ofs, snippet.font, snippet.letter_space, txt_flag); - ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS ? true : false; - is_end_line = true; - } - } - else if(next_ofs > 0 && lv_get_snippet_cnt() > 0) { + if(next_ofs > 0 && lv_get_snippet_cnt() > 0) { /* To prevent infinite loops, the _lv_txt_get_next_line() may return incomplete words, */ /* This phenomenon should be avoided when lv_get_snippet_cnt() > 0 */ if(max_w < use_width) { @@ -860,6 +857,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) cur_txt_ofs += next_ofs; if(max_line_h < snippet.line_h) { max_line_h = snippet.line_h; + max_baseline = snippet.font->base_line; } lv_snippet_push(&snippet); @@ -869,13 +867,35 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) } } - /* start current line deal width */ + /* start current line deal with */ uint16_t item_cnt = lv_get_snippet_cnt(); if(item_cnt == 0) { /* break if stack is empty */ break; } + /* Whether the current line is the end line and does overflow processing */ + { + lv_snippet_t * last_snippet = lv_get_snippet(item_cnt - 1); + lv_coord_t next_line_h = last_snippet->line_h; + if(last_snippet->txt[last_snippet->bytes] == '\0') { + next_line_h = 0; + lv_span_t * next_span = _lv_ll_get_next(&spans->child_ll, last_snippet->span); + if(next_span) { /* have the next line */ + next_line_h = lv_font_get_line_height(lv_span_get_style_text_font(obj, next_span)) + line_space; + } + } + if(txt_pos.y + max_line_h + next_line_h - line_space > coords.y2 + 1) { /* for overflow if is end line. */ + if(last_snippet->txt[last_snippet->bytes] != '\0') { + last_snippet->bytes = strlen(last_snippet->txt); + last_snippet->txt_w = lv_txt_get_width(last_snippet->txt, last_snippet->bytes, last_snippet->font, + last_snippet->letter_space, txt_flag); + } + ellipsis_valid = spans->overflow == LV_SPAN_OVERFLOW_ELLIPSIS ? true : false; + is_end_line = true; + } + } + /*Go the first visible line*/ if(txt_pos.y + max_line_h < clip_area.y1) { goto Next_line_init; @@ -908,7 +928,7 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) lv_point_t pos; pos.x = txt_pos.x; - pos.y = txt_pos.y + max_line_h - pinfo->line_h; + pos.y = txt_pos.y + max_line_h - pinfo->line_h - (max_baseline - pinfo->font->base_line); label_draw_dsc.color = lv_span_get_style_text_color(obj, pinfo->span); label_draw_dsc.opa = lv_span_get_style_text_opa(obj, pinfo->span); label_draw_dsc.font = lv_span_get_style_text_font(obj, pinfo->span); @@ -963,13 +983,6 @@ static void lv_draw_span(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx) } } - if(ellipsis_valid && i == item_cnt - 1 && pos.x <= ellipsis_width) { - for(int ell = 0; ell < 3; ell++) { - lv_draw_letter(draw_ctx, &label_draw_dsc, &pos, '.'); - pos.x = pos.x + dot_letter_w + pinfo->letter_space; - } - } - /* draw decor */ lv_text_decor_t decor = lv_span_get_style_text_decor(obj, pinfo->span); if(decor != LV_TEXT_DECOR_NONE) { @@ -1021,8 +1034,8 @@ static void refresh_self_size(lv_obj_t * obj) { lv_spangroup_t * spans = (lv_spangroup_t *)obj; spans->refresh = 1; - lv_obj_refresh_self_size(obj); lv_obj_invalidate(obj); + lv_obj_refresh_self_size(obj); } #endif diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.h b/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.h index 418ad87e2..f00d04db7 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/span/lv_span.h @@ -50,6 +50,7 @@ typedef struct { /** Data of label*/ typedef struct { lv_obj_t obj; + int32_t lines; lv_coord_t indent; /* first line indent */ lv_coord_t cache_w; /* the cache automatically calculates the width */ lv_coord_t cache_h; /* similar cache_w */ @@ -133,6 +134,13 @@ void lv_spangroup_set_indent(lv_obj_t * obj, lv_coord_t indent); */ void lv_spangroup_set_mode(lv_obj_t * obj, lv_span_mode_t mode); +/** + * Set lines of the spangroup. + * @param obj pointer to a spangroup object. + * @param lines max lines that can be displayed in LV_SPAN_MODE_BREAK mode. < 0 means no limit. + */ +void lv_spangroup_set_lines(lv_obj_t * obj, int32_t lines); + /*===================== * Getter functions *====================*/ @@ -185,6 +193,13 @@ lv_coord_t lv_spangroup_get_indent(lv_obj_t * obj); */ lv_span_mode_t lv_spangroup_get_mode(lv_obj_t * obj); +/** + * get lines of the spangroup. + * @param obj pointer to a spangroup object. + * @return the lines value. + */ +int32_t lv_spangroup_get_lines(lv_obj_t * obj); + /** * get max line height of all span in the spangroup. * @param obj pointer to a spangroup object. diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.c index 72aca7db8..34691053e 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.c @@ -34,6 +34,7 @@ static void lv_spinbox_updatevalue(lv_obj_t * obj); const lv_obj_class_t lv_spinbox_class = { .constructor_cb = lv_spinbox_constructor, .event_cb = lv_spinbox_event, + .width_def = LV_DPI_DEF, .instance_size = sizeof(lv_spinbox_t), .editable = LV_OBJ_CLASS_EDITABLE_TRUE, .base_class = &lv_textarea_class @@ -104,7 +105,6 @@ void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t se if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT; if(separator_position >= digit_count) separator_position = 0; - if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT) separator_position = LV_SPINBOX_MAX_DIGIT_COUNT; if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) { int64_t max_val = lv_pow(10, digit_count); @@ -157,7 +157,7 @@ void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max) * @param spinbox pointer to spinbox * @param pos selected position in spinbox */ -void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos) +void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos) { LV_ASSERT_OBJ(obj, MY_CLASS); lv_spinbox_t * spinbox = (lv_spinbox_t *)obj; @@ -336,7 +336,6 @@ static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * ob lv_textarea_set_one_line(obj, true); lv_textarea_set_cursor_click_pos(obj, true); - lv_obj_set_width(obj, LV_DPI_DEF); lv_spinbox_updatevalue(obj); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.h b/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.h index 14c73ba36..1a4bc322f 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/spinbox/lv_spinbox.h @@ -105,7 +105,7 @@ void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max); * @param obj pointer to spinbox * @param pos selected position in spinbox */ -void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos); +void lv_spinbox_set_cursor_pos(lv_obj_t * obj, uint8_t pos); /** * Set direction of digit step when clicking an encoder button while in editing mode @@ -170,6 +170,10 @@ void lv_spinbox_decrement(lv_obj_t * obj); * MACROS **********************/ +/* It was ambiguous in MicroPython. See https://github.com/lvgl/lvgl/issues/3301 + * TODO remove in v9*/ +#define lv_spinbox_set_pos lv_spinbox_set_cursor_pos + #endif /*LV_USE_SPINBOX*/ #ifdef __cplusplus diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.c old mode 100644 new mode 100755 index 8ccd143e1..81addc663 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.c @@ -121,6 +121,20 @@ lv_obj_t * lv_tabview_add_tab(lv_obj_t * obj, const char * name) return page; } +void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t id, const char * new_name) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + lv_tabview_t * tabview = (lv_tabview_t *)obj; + + if(id >= tabview->tab_cnt) return; + if(tabview->tab_pos & LV_DIR_HOR) id *= 2; + + lv_mem_free(tabview->map[id]); + tabview->map[id] = lv_mem_alloc(strlen(new_name) + 1); + strcpy(tabview->map[id], new_name); + lv_obj_invalidate(obj); +} + void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -135,14 +149,22 @@ void lv_tabview_set_act(lv_obj_t * obj, uint32_t id, lv_anim_enable_t anim_en) lv_obj_t * cont = lv_tabview_get_content(obj); if(cont == NULL) return; - lv_coord_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN); - lv_coord_t w = lv_obj_get_content_width(cont); - if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) { - lv_obj_scroll_to_x(cont, id * (gap + w), anim_en); + + if((tabview->tab_pos & LV_DIR_VER) != 0) { + lv_coord_t gap = lv_obj_get_style_pad_column(cont, LV_PART_MAIN); + lv_coord_t w = lv_obj_get_content_width(cont); + if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) { + lv_obj_scroll_to_x(cont, id * (gap + w), anim_en); + } + else { + int32_t id_rtl = -(int32_t)id; + lv_obj_scroll_to_x(cont, (gap + w) * id_rtl, anim_en); + } } else { - int32_t id_rtl = -(int32_t)id; - lv_obj_scroll_to_x(cont, (gap + w) * id_rtl, anim_en); + lv_coord_t gap = lv_obj_get_style_pad_row(cont, LV_PART_MAIN); + lv_coord_t h = lv_obj_get_content_height(cont); + lv_obj_scroll_to_y(cont, id * (gap + h), anim_en); } lv_obj_t * btns = lv_tabview_get_tab_btns(obj); @@ -229,8 +251,14 @@ static void lv_tabview_constructor(const lv_obj_class_t * class_p, lv_obj_t * ob lv_group_t * g = lv_group_get_default(); if(g) lv_group_add_obj(g, btnm); - lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW); - lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER); + if((tabview->tab_pos & LV_DIR_VER) != 0) { + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_ROW); + lv_obj_set_scroll_snap_x(cont, LV_SCROLL_SNAP_CENTER); + } + else { + lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN); + lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER); + } lv_obj_add_flag(cont, LV_OBJ_FLAG_SCROLL_ONE); lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLL_ON_FOCUS); } @@ -289,18 +317,29 @@ static void cont_scroll_end_event_cb(lv_event_t * e) lv_event_code_t code = lv_event_get_code(e); lv_obj_t * tv = lv_obj_get_parent(cont); + lv_tabview_t * tv_obj = (lv_tabview_t *)tv; if(code == LV_EVENT_LAYOUT_CHANGED) { lv_tabview_set_act(tv, lv_tabview_get_tab_act(tv), LV_ANIM_OFF); } else if(code == LV_EVENT_SCROLL_END) { + lv_indev_t * indev = lv_indev_get_act(); + if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) { + return; + } + lv_point_t p; lv_obj_get_scroll_end(cont, &p); - lv_coord_t w = lv_obj_get_content_width(cont); lv_coord_t t; - - if(lv_obj_get_style_base_dir(tv, LV_PART_MAIN) == LV_BASE_DIR_RTL) t = -(p.x - w / 2) / w; - else t = (p.x + w / 2) / w; + if((tv_obj->tab_pos & LV_DIR_VER) != 0) { + lv_coord_t w = lv_obj_get_content_width(cont); + if(lv_obj_get_style_base_dir(tv, LV_PART_MAIN) == LV_BASE_DIR_RTL) t = -(p.x - w / 2) / w; + else t = (p.x + w / 2) / w; + } + else { + lv_coord_t h = lv_obj_get_content_height(cont); + t = (p.y + h / 2) / h; + } if(t < 0) t = 0; bool new_tab = false; diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.h b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.h index a01c6b9ba..388c65477 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.h +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tabview/lv_tabview.h @@ -42,6 +42,8 @@ lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab lv_obj_t * lv_tabview_add_tab(lv_obj_t * tv, const char * name); +void lv_tabview_rename_tab(lv_obj_t * obj, uint32_t tab_id, const char * new_name); + lv_obj_t * lv_tabview_get_content(lv_obj_t * tv); lv_obj_t * lv_tabview_get_tab_btns(lv_obj_t * tv); diff --git a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tileview/lv_tileview.c b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tileview/lv_tileview.c index 4f98b552a..17fdb519a 100644 --- a/lib/libesp32_lvgl/lvgl/src/extra/widgets/tileview/lv_tileview.c +++ b/lib/libesp32_lvgl/lvgl/src/extra/widgets/tileview/lv_tileview.c @@ -7,6 +7,7 @@ * INCLUDES *********************/ #include "lv_tileview.h" +#include "../../../core/lv_indev.h" #if LV_USE_TILEVIEW /********************* @@ -157,6 +158,11 @@ static void tileview_event_cb(lv_event_t * e) lv_tileview_t * tv = (lv_tileview_t *) obj; if(code == LV_EVENT_SCROLL_END) { + lv_indev_t * indev = lv_indev_get_act(); + if(indev && indev->proc.state == LV_INDEV_STATE_PRESSED) { + return; + } + lv_coord_t w = lv_obj_get_content_width(obj); lv_coord_t h = lv_obj_get_content_height(obj); diff --git a/lib/libesp32_lvgl/lvgl/src/font/lv_font.c b/lib/libesp32_lvgl/lvgl/src/font/lv_font.c index 0e7a2fe74..d4cc27e19 100644 --- a/lib/libesp32_lvgl/lvgl/src/font/lv_font.c +++ b/lib/libesp32_lvgl/lvgl/src/font/lv_font.c @@ -43,7 +43,7 @@ /** * Return with the bitmap of a font. * @param font_p pointer to a font - * @param letter an UNICODE character code + * @param letter a UNICODE character code * @return pointer to the bitmap of the letter */ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t letter) @@ -56,7 +56,7 @@ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t lett * Get the descriptor of a glyph * @param font_p pointer to font * @param dsc_out store the result descriptor here - * @param letter an UNICODE letter code + * @param letter a UNICODE letter code * @param letter_next the next letter after `letter`. Used for kerning * @return true: descriptor is successfully loaded into `dsc_out`. * false: the letter was not found, no data is loaded to `dsc_out` @@ -64,26 +64,72 @@ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t lett bool lv_font_get_glyph_dsc(const lv_font_t * font_p, lv_font_glyph_dsc_t * dsc_out, uint32_t letter, uint32_t letter_next) { + LV_ASSERT_NULL(font_p); LV_ASSERT_NULL(dsc_out); - dsc_out->resolved_font = NULL; + +#if LV_USE_FONT_PLACEHOLDER + const lv_font_t * placeholder_font = NULL; +#endif + const lv_font_t * f = font_p; - bool found = false; + + dsc_out->resolved_font = NULL; + while(f) { - found = f->get_glyph_dsc(f, dsc_out, letter, letter_next); - if(found && !dsc_out->is_placeholder) { - dsc_out->resolved_font = f; - break; + bool found = f->get_glyph_dsc(f, dsc_out, letter, letter_next); + if(found) { + if(!dsc_out->is_placeholder) { + dsc_out->resolved_font = f; + return true; + } +#if LV_USE_FONT_PLACEHOLDER + else if(placeholder_font == NULL) { + placeholder_font = f; + } +#endif } f = f->fallback; } - return found; + +#if LV_USE_FONT_PLACEHOLDER + if(placeholder_font != NULL) { + placeholder_font->get_glyph_dsc(placeholder_font, dsc_out, letter, letter_next); + dsc_out->resolved_font = placeholder_font; + return true; + } +#endif + + if(letter < 0x20 || + letter == 0xf8ff || /*LV_SYMBOL_DUMMY*/ + letter == 0x200c) { /*ZERO WIDTH NON-JOINER*/ + dsc_out->box_w = 0; + dsc_out->adv_w = 0; + } + else { +#if LV_USE_FONT_PLACEHOLDER + dsc_out->box_w = font_p->line_height / 2; + dsc_out->adv_w = dsc_out->box_w + 2; +#else + dsc_out->box_w = 0; + dsc_out->adv_w = 0; +#endif + } + + dsc_out->resolved_font = NULL; + dsc_out->box_h = font_p->line_height; + dsc_out->ofs_x = 0; + dsc_out->ofs_y = 0; + dsc_out->bpp = 1; + dsc_out->is_placeholder = true; + + return false; } /** * Get the width of a glyph with kerning * @param font pointer to a font - * @param letter an UNICODE letter + * @param letter a UNICODE letter * @param letter_next the next letter after `letter`. Used for kerning * @return the width of the glyph */ @@ -91,10 +137,8 @@ uint16_t lv_font_get_glyph_width(const lv_font_t * font, uint32_t letter, uint32 { LV_ASSERT_NULL(font); lv_font_glyph_dsc_t g; - bool ret; - ret = lv_font_get_glyph_dsc(font, &g, letter, letter_next); - if(ret) return g.adv_w; - else return 0; + lv_font_get_glyph_dsc(font, &g, letter, letter_next); + return g.adv_w; } /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/font/lv_font.h b/lib/libesp32_lvgl/lvgl/src/font/lv_font.h index 3d716dd46..e3b670c87 100644 --- a/lib/libesp32_lvgl/lvgl/src/font/lv_font.h +++ b/lib/libesp32_lvgl/lvgl/src/font/lv_font.h @@ -25,6 +25,9 @@ extern "C" { * DEFINES *********************/ +/* imgfont identifier */ +#define LV_IMGFONT_BPP 9 + /********************** * TYPEDEFS **********************/ @@ -37,7 +40,7 @@ struct _lv_font_t; /** Describes the properties of a glyph.*/ typedef struct { const struct _lv_font_t * - resolved_font; /**< Pointer to a font where the gylph was actually found after handling fallbacks*/ + resolved_font; /**< Pointer to a font where the glyph was actually found after handling fallbacks*/ uint16_t adv_w; /**< The glyph needs this space. Draw the next glyph after this width.*/ uint16_t box_w; /**< Width of the glyph's bounding box*/ uint16_t box_h; /**< Height of the glyph's bounding box*/ @@ -87,7 +90,7 @@ typedef struct _lv_font_t { /** * Return with the bitmap of a font. * @param font_p pointer to a font - * @param letter an UNICODE character code + * @param letter a UNICODE character code * @return pointer to the bitmap of the letter */ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t letter); @@ -96,7 +99,7 @@ const uint8_t * lv_font_get_glyph_bitmap(const lv_font_t * font_p, uint32_t lett * Get the descriptor of a glyph * @param font_p pointer to font * @param dsc_out store the result descriptor here - * @param letter an UNICODE letter code + * @param letter a UNICODE letter code * @param letter_next the next letter after `letter`. Used for kerning * @return true: descriptor is successfully loaded into `dsc_out`. * false: the letter was not found, no data is loaded to `dsc_out` @@ -107,7 +110,7 @@ bool lv_font_get_glyph_dsc(const lv_font_t * font_p, lv_font_glyph_dsc_t * dsc_o /** * Get the width of a glyph with kerning * @param font pointer to a font - * @param letter an UNICODE letter + * @param letter a UNICODE letter * @param letter_next the next letter after `letter`. Used for kerning * @return the width of the glyph */ @@ -243,7 +246,7 @@ LV_FONT_CUSTOM_DECLARE #endif /** - * Just a wrapper around LV_FONT_DEFAULT because it might be more convenient to use a function is some cases + * Just a wrapper around LV_FONT_DEFAULT because it might be more convenient to use a function in some cases * @return pointer to LV_FONT_DEFAULT */ static inline const lv_font_t * lv_font_default(void) diff --git a/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.c b/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.c index 452cbe912..7a36f01b8 100644 --- a/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.c +++ b/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.c @@ -73,7 +73,7 @@ static int32_t kern_pair_16_compare(const void * ref, const void * element); /** * Used as `get_glyph_bitmap` callback in LittelvGL's native font format if the font is uncompressed. * @param font pointer to font - * @param unicode_letter an unicode letter which bitmap should be get + * @param unicode_letter a unicode letter which bitmap should be get * @return pointer to the bitmap or NULL if not found */ const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unicode_letter) @@ -141,7 +141,7 @@ const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t unic * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed. * @param font_p pointer to font * @param dsc_out store the result descriptor here - * @param letter an UNICODE letter code + * @param letter a UNICODE letter code * @return true: descriptor is successfully loaded into `dsc_out`. * false: the letter was not found, no data is loaded to `dsc_out` */ diff --git a/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.h b/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.h index 9c9d422a8..86546a35f 100644 --- a/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.h +++ b/lib/libesp32_lvgl/lvgl/src/font/lv_font_fmt_txt.h @@ -170,7 +170,7 @@ typedef struct { /** * Store kerning values. - * Can be `lv_font_fmt_txt_kern_pair_t * or `lv_font_kern_classes_fmt_txt_t *` + * Can be `lv_font_fmt_txt_kern_pair_t * or `lv_font_kern_classes_fmt_txt_t *` * depending on `kern_classes` */ const void * kern_dsc; @@ -204,7 +204,7 @@ typedef struct { /** * Used as `get_glyph_bitmap` callback in LittelvGL's native font format if the font is uncompressed. * @param font pointer to font - * @param unicode_letter an unicode letter which bitmap should be get + * @param unicode_letter a unicode letter which bitmap should be get * @return pointer to the bitmap or NULL if not found */ const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t letter); @@ -213,7 +213,7 @@ const uint8_t * lv_font_get_bitmap_fmt_txt(const lv_font_t * font, uint32_t lett * Used as `get_glyph_dsc` callback in LittelvGL's native font format if the font is uncompressed. * @param font_p pointer to font * @param dsc_out store the result descriptor here - * @param letter an UNICODE letter code + * @param letter a UNICODE letter code * @return true: descriptor is successfully loaded into `dsc_out`. * false: the letter was not found, no data is loaded to `dsc_out` */ diff --git a/lib/libesp32_lvgl/lvgl/src/gpu/lv_gpu.mk b/lib/libesp32_lvgl/lvgl/src/gpu/lv_gpu.mk deleted file mode 100644 index f810e0c70..000000000 --- a/lib/libesp32_lvgl/lvgl/src/gpu/lv_gpu.mk +++ /dev/null @@ -1,10 +0,0 @@ -CSRCS += lv_gpu_nxp_pxp.c -CSRCS += lv_gpu_nxp_pxp_osa.c -CSRCS += lv_gpu_nxp_vglite.c -CSRCS += lv_gpu_stm32_dma2d.c - -DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu -VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu - -CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/gpu" - diff --git a/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.c b/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.c index 9a8e42d28..0dd8f6b30 100644 --- a/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.c +++ b/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.c @@ -21,6 +21,11 @@ #include "../draw/sw/lv_draw_sw.h" #include "../draw/sdl/lv_draw_sdl.h" #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h" +#include "../draw/swm341_dma2d/lv_gpu_swm341_dma2d.h" +#include "../draw/arm2d/lv_gpu_arm2d.h" +#if LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE + #include "../draw/nxp/lv_gpu_nxp.h" +#endif #if LV_USE_THEME_DEFAULT #include "../extra/themes/default/lv_theme_default.h" @@ -87,7 +92,7 @@ void lv_disp_drv_init(lv_disp_drv_t * driver) driver->offset_x = 0; driver->offset_y = 0; driver->antialiasing = LV_COLOR_DEPTH > 8 ? 1 : 0; - driver->screen_transp = LV_COLOR_SCREEN_TRANSP; + driver->screen_transp = 0; driver->dpi = LV_DPI_DEF; driver->color_chroma_key = LV_COLOR_CHROMA_KEY; @@ -96,18 +101,22 @@ void lv_disp_drv_init(lv_disp_drv_t * driver) driver->draw_ctx_init = lv_draw_stm32_dma2d_ctx_init; driver->draw_ctx_deinit = lv_draw_stm32_dma2d_ctx_init; driver->draw_ctx_size = sizeof(lv_draw_stm32_dma2d_ctx_t); -#elif LV_USE_GPU_NXP_PXP - driver->draw_ctx_init = lv_draw_nxp_pxp_init; - driver->draw_ctx_deinit = lv_draw_nxp_pxp_init; - driver->draw_ctx_size = sizeof(lv_draw_nxp_pxp_t); -#elif LV_USE_GPU_NXP_VG_LITE - driver->draw_ctx_init = lv_draw_nxp_vglite_init; - driver->draw_ctx_deinit = lv_draw_nxp_vglite_init; - driver->draw_ctx_size = sizeof(lv_draw_nxp_vglite_t); +#elif LV_USE_GPU_SWM341_DMA2D + driver->draw_ctx_init = lv_draw_swm341_dma2d_ctx_init; + driver->draw_ctx_deinit = lv_draw_swm341_dma2d_ctx_init; + driver->draw_ctx_size = sizeof(lv_draw_swm341_dma2d_ctx_t); +#elif LV_USE_GPU_NXP_PXP || LV_USE_GPU_NXP_VG_LITE + driver->draw_ctx_init = lv_draw_nxp_ctx_init; + driver->draw_ctx_deinit = lv_draw_nxp_ctx_deinit; + driver->draw_ctx_size = sizeof(lv_draw_nxp_ctx_t); #elif LV_USE_GPU_SDL driver->draw_ctx_init = lv_draw_sdl_init_ctx; driver->draw_ctx_deinit = lv_draw_sdl_deinit_ctx; driver->draw_ctx_size = sizeof(lv_draw_sdl_ctx_t); +#elif LV_USE_GPU_ARM2D + driver->draw_ctx_init = lv_draw_arm2d_ctx_init; + driver->draw_ctx_deinit = lv_draw_arm2d_ctx_init; + driver->draw_ctx_size = sizeof(lv_draw_arm2d_ctx_t); #else driver->draw_ctx_init = lv_draw_sw_init_ctx; driver->draw_ctx_deinit = lv_draw_sw_init_ctx; @@ -150,8 +159,8 @@ void lv_disp_draw_buf_init(lv_disp_draw_buf_t * draw_buf, void * buf1, void * bu lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver) { lv_disp_t * disp = _lv_ll_ins_head(&LV_GC_ROOT(_lv_disp_ll)); + LV_ASSERT_MALLOC(disp); if(!disp) { - LV_ASSERT_MALLOC(disp); return NULL; } @@ -168,6 +177,8 @@ lv_disp_t * lv_disp_drv_register(lv_disp_drv_t * driver) disp->driver = driver; + disp->inv_en_cnt = 1; + lv_disp_t * disp_def_tmp = disp_def; disp_def = disp; /*Temporarily change the default screen to create the default screens on the new display*/ @@ -497,18 +508,6 @@ lv_coord_t lv_disp_get_dpi(const lv_disp_t * disp) */ LV_ATTRIBUTE_FLUSH_READY void lv_disp_flush_ready(lv_disp_drv_t * disp_drv) { - /*If the screen is transparent initialize it when the flushing is ready*/ -#if LV_COLOR_SCREEN_TRANSP - if(disp_drv->screen_transp) { - if(disp_drv->clear_cb) { - disp_drv->clear_cb(disp_drv, disp_drv->draw_buf->buf_act, disp_drv->draw_buf->size); - } - else { - lv_memset_00(disp_drv->draw_buf->buf_act, disp_drv->draw_buf->size * sizeof(lv_color32_t)); - } - } -#endif - disp_drv->draw_buf->flushing = 0; disp_drv->draw_buf->flushing_last = 0; } @@ -677,28 +676,35 @@ static void set_px_alpha_generic(lv_img_dsc_t * d, lv_coord_t x, lv_coord_t y, l lv_img_buf_set_px_alpha(d, x, y, br); } -static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, +static void set_px_true_color_alpha(lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, + lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) { (void) disp_drv; /*Unused*/ - if(opa <= LV_OPA_MIN) return; - lv_img_dsc_t d; - d.data = buf; - d.header.always_zero = 0; - d.header.h = 1; /*Doesn't matter*/; - d.header.w = buf_w; - d.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + uint8_t * buf_px = buf + (buf_w * y * LV_IMG_PX_SIZE_ALPHA_BYTE + x * LV_IMG_PX_SIZE_ALPHA_BYTE); - lv_color_t bg_color = lv_img_buf_get_px_color(&d, x, y, lv_color_black()); - lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&d, x, y); - - lv_opa_t res_opa; + lv_color_t bg_color; lv_color_t res_color; + lv_opa_t bg_opa = buf_px[LV_IMG_PX_SIZE_ALPHA_BYTE - 1]; +#if LV_COLOR_DEPTH == 8 || LV_COLOR_DEPTH == 1 + bg_color.full = buf_px[0]; + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf_px[2]); + if(buf_px[1] <= LV_OPA_MIN) return; + buf_px[0] = res_color.full; +#elif LV_COLOR_DEPTH == 16 + bg_color.full = buf_px[0] + (buf_px[1] << 8); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf_px[2]); + if(buf_px[2] <= LV_OPA_MIN) return; + buf_px[0] = res_color.full & 0xff; + buf_px[1] = res_color.full >> 8; +#elif LV_COLOR_DEPTH == 32 + bg_color = *((lv_color_t *)buf_px); + lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &buf_px[3]); + if(buf_px[3] <= LV_OPA_MIN) return; + buf_px[0] = res_color.ch.blue; + buf_px[1] = res_color.ch.green; + buf_px[2] = res_color.ch.red; +#endif - lv_color_mix_with_alpha(bg_color, bg_opa, color, opa, &res_color, &res_opa); - - lv_img_buf_set_px_alpha(&d, x, y, res_opa); - lv_img_buf_set_px_color(&d, x, y, res_color); } diff --git a/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.h b/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.h index 6abbab1f9..d3425fe4e 100644 --- a/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.h +++ b/lib/libesp32_lvgl/lvgl/src/hal/lv_hal_disp.h @@ -133,6 +133,9 @@ typedef struct _lv_disp_drv_t { /** OPTIONAL: called when driver parameters are updated */ void (*drv_update_cb)(struct _lv_disp_drv_t * disp_drv); + /** OPTIONAL: called when start rendering */ + void (*render_start_cb)(struct _lv_disp_drv_t * disp_drv); + /** On CHROMA_KEYED images this color will be transparent. * `LV_COLOR_CHROMA_KEY` by default. (lv_conf.h)*/ lv_color_t color_chroma_key; @@ -170,8 +173,11 @@ typedef struct _lv_disp_t { struct _lv_obj_t * top_layer; /**< @see lv_disp_get_layer_top*/ struct _lv_obj_t * sys_layer; /**< @see lv_disp_get_layer_sys*/ uint32_t screen_cnt; +uint8_t draw_prev_over_act : + 1; /**< 1: Draw previous screen over active screen*/ uint8_t del_prev : 1; /**< 1: Automatically delete the previous screen when the screen load animation is ready*/ + uint8_t rendering_in_progress : 1; /**< 1: The current screen rendering is in progress*/ lv_opa_t bg_opa; /**disp == NULL) driver->disp = lv_disp_get_default(); if(driver->disp == NULL) { @@ -85,8 +84,8 @@ lv_indev_t * lv_indev_drv_register(lv_indev_drv_t * driver) } lv_indev_t * indev = _lv_ll_ins_head(&LV_GC_ROOT(_lv_indev_ll)); + LV_ASSERT_MALLOC(indev); if(!indev) { - LV_ASSERT_MALLOC(indev); return NULL; } diff --git a/lib/libesp32_lvgl/lvgl/src/lv_conf_internal.h b/lib/libesp32_lvgl/lvgl/src/lv_conf_internal.h index b3e6748bb..97807fe37 100644 --- a/lib/libesp32_lvgl/lvgl/src/lv_conf_internal.h +++ b/lib/libesp32_lvgl/lvgl/src/lv_conf_internal.h @@ -12,38 +12,43 @@ /* Handle special Kconfig options */ #ifndef LV_KCONFIG_IGNORE -# include "lv_conf_kconfig.h" -# ifdef CONFIG_LV_CONF_SKIP -# define LV_CONF_SKIP -# endif + #include "lv_conf_kconfig.h" + #ifdef CONFIG_LV_CONF_SKIP + #define LV_CONF_SKIP + #endif #endif /*If "lv_conf.h" is available from here try to use it later.*/ #ifdef __has_include -# if __has_include("lv_conf.h") -# ifndef LV_CONF_INCLUDE_SIMPLE -# define LV_CONF_INCLUDE_SIMPLE -# endif -# endif + #if __has_include("lv_conf.h") + #ifndef LV_CONF_INCLUDE_SIMPLE + #define LV_CONF_INCLUDE_SIMPLE + #endif + #endif #endif /*If lv_conf.h is not skipped include it*/ #ifndef LV_CONF_SKIP -# ifdef LV_CONF_PATH /*If there is a path defined for lv_conf.h use it*/ -# define __LV_TO_STR_AUX(x) #x -# define __LV_TO_STR(x) __LV_TO_STR_AUX(x) -# include __LV_TO_STR(LV_CONF_PATH) -# undef __LV_TO_STR_AUX -# undef __LV_TO_STR -# elif defined(LV_CONF_INCLUDE_SIMPLE) /*Or simply include lv_conf.h is enabled*/ -# include "lv_conf.h" -# else -# include "../../lv_conf.h" /*Else assume lv_conf.h is next to the lvgl folder*/ -# endif + #ifdef LV_CONF_PATH /*If there is a path defined for lv_conf.h use it*/ + #define __LV_TO_STR_AUX(x) #x + #define __LV_TO_STR(x) __LV_TO_STR_AUX(x) + #include __LV_TO_STR(LV_CONF_PATH) + #undef __LV_TO_STR_AUX + #undef __LV_TO_STR + #elif defined(LV_CONF_INCLUDE_SIMPLE) /*Or simply include lv_conf.h is enabled*/ + #include "lv_conf.h" + #else + #include "../../lv_conf.h" /*Else assume lv_conf.h is next to the lvgl folder*/ + #endif + #if !defined(LV_CONF_H) && !defined(LV_CONF_SUPPRESS_DEFINE_CHECK) + /* #include will sometimes silently fail when __has_include is used */ + /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80753 */ + #pragma message("Possible failure to include lv_conf.h, please read the comment in this file if you get errors") + #endif #endif #ifdef CONFIG_LV_COLOR_DEPTH -# define _LV_KCONFIG_PRESENT + #define _LV_KCONFIG_PRESENT #endif /*---------------------------------- @@ -58,50 +63,50 @@ /*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/ #ifndef LV_COLOR_DEPTH -# ifdef CONFIG_LV_COLOR_DEPTH -# define LV_COLOR_DEPTH CONFIG_LV_COLOR_DEPTH -# else -# define LV_COLOR_DEPTH 16 -# endif + #ifdef CONFIG_LV_COLOR_DEPTH + #define LV_COLOR_DEPTH CONFIG_LV_COLOR_DEPTH + #else + #define LV_COLOR_DEPTH 16 + #endif #endif /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ #ifndef LV_COLOR_16_SWAP -# ifdef CONFIG_LV_COLOR_16_SWAP -# define LV_COLOR_16_SWAP CONFIG_LV_COLOR_16_SWAP -# else -# define LV_COLOR_16_SWAP 0 -# endif + #ifdef CONFIG_LV_COLOR_16_SWAP + #define LV_COLOR_16_SWAP CONFIG_LV_COLOR_16_SWAP + #else + #define LV_COLOR_16_SWAP 0 + #endif #endif -/*Enable more complex drawing routines to manage screens transparency. - *Can be used if the UI is above another layer, e.g. an OSD menu or video player. - *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/ +/*Enable features to draw on transparent background. + *It's required if opa, and transform_* style properties are used. + *Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/ #ifndef LV_COLOR_SCREEN_TRANSP -# ifdef CONFIG_LV_COLOR_SCREEN_TRANSP -# define LV_COLOR_SCREEN_TRANSP CONFIG_LV_COLOR_SCREEN_TRANSP -# else -# define LV_COLOR_SCREEN_TRANSP 0 -# endif + #ifdef CONFIG_LV_COLOR_SCREEN_TRANSP + #define LV_COLOR_SCREEN_TRANSP CONFIG_LV_COLOR_SCREEN_TRANSP + #else + #define LV_COLOR_SCREEN_TRANSP 0 + #endif #endif /* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ #ifndef LV_COLOR_MIX_ROUND_OFS -# ifdef CONFIG_LV_COLOR_MIX_ROUND_OFS -# define LV_COLOR_MIX_ROUND_OFS CONFIG_LV_COLOR_MIX_ROUND_OFS -# else -# define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128) -# endif + #ifdef CONFIG_LV_COLOR_MIX_ROUND_OFS + #define LV_COLOR_MIX_ROUND_OFS CONFIG_LV_COLOR_MIX_ROUND_OFS + #else + #define LV_COLOR_MIX_ROUND_OFS 0 + #endif #endif /*Images pixels with this color will not be drawn if they are chroma keyed)*/ #ifndef LV_COLOR_CHROMA_KEY -# ifdef CONFIG_LV_COLOR_CHROMA_KEY -# define LV_COLOR_CHROMA_KEY CONFIG_LV_COLOR_CHROMA_KEY -# else -# define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ -# endif + #ifdef CONFIG_LV_COLOR_CHROMA_KEY + #define LV_COLOR_CHROMA_KEY CONFIG_LV_COLOR_CHROMA_KEY + #else + #define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ + #endif #endif /*========================= @@ -110,84 +115,96 @@ /*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/ #ifndef LV_MEM_CUSTOM -# ifdef CONFIG_LV_MEM_CUSTOM -# define LV_MEM_CUSTOM CONFIG_LV_MEM_CUSTOM -# else -# define LV_MEM_CUSTOM 0 -# endif + #ifdef CONFIG_LV_MEM_CUSTOM + #define LV_MEM_CUSTOM CONFIG_LV_MEM_CUSTOM + #else + #define LV_MEM_CUSTOM 0 + #endif #endif #if LV_MEM_CUSTOM == 0 -/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ -#ifndef LV_MEM_SIZE -# ifdef CONFIG_LV_MEM_SIZE -# define LV_MEM_SIZE CONFIG_LV_MEM_SIZE -# else -# define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/ -# endif -#endif + /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ + #ifndef LV_MEM_SIZE + #ifdef CONFIG_LV_MEM_SIZE + #define LV_MEM_SIZE CONFIG_LV_MEM_SIZE + #else + #define LV_MEM_SIZE (48U * 1024U) /*[bytes]*/ + #endif + #endif -/*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ -#ifndef LV_MEM_ADR -# ifdef CONFIG_LV_MEM_ADR -# define LV_MEM_ADR CONFIG_LV_MEM_ADR -# else -# define LV_MEM_ADR 0 /*0: unused*/ -# endif -#endif -/*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ -#if LV_MEM_ADR == 0 -//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/ -//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/ -#endif + /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ + #ifndef LV_MEM_ADR + #ifdef CONFIG_LV_MEM_ADR + #define LV_MEM_ADR CONFIG_LV_MEM_ADR + #else + #define LV_MEM_ADR 0 /*0: unused*/ + #endif + #endif + /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ + #if LV_MEM_ADR == 0 + #ifndef LV_MEM_POOL_INCLUDE + #ifdef CONFIG_LV_MEM_POOL_INCLUDE + #define LV_MEM_POOL_INCLUDE CONFIG_LV_MEM_POOL_INCLUDE + #else + #undef LV_MEM_POOL_INCLUDE + #endif + #endif + #ifndef LV_MEM_POOL_ALLOC + #ifdef CONFIG_LV_MEM_POOL_ALLOC + #define LV_MEM_POOL_ALLOC CONFIG_LV_MEM_POOL_ALLOC + #else + #undef LV_MEM_POOL_ALLOC + #endif + #endif + #endif #else /*LV_MEM_CUSTOM*/ -#ifndef LV_MEM_CUSTOM_INCLUDE -# ifdef CONFIG_LV_MEM_CUSTOM_INCLUDE -# define LV_MEM_CUSTOM_INCLUDE CONFIG_LV_MEM_CUSTOM_INCLUDE -# else -# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ -# endif -#endif -#ifndef LV_MEM_CUSTOM_ALLOC -# ifdef CONFIG_LV_MEM_CUSTOM_ALLOC -# define LV_MEM_CUSTOM_ALLOC CONFIG_LV_MEM_CUSTOM_ALLOC -# else -# define LV_MEM_CUSTOM_ALLOC malloc -# endif -#endif -#ifndef LV_MEM_CUSTOM_FREE -# ifdef CONFIG_LV_MEM_CUSTOM_FREE -# define LV_MEM_CUSTOM_FREE CONFIG_LV_MEM_CUSTOM_FREE -# else -# define LV_MEM_CUSTOM_FREE free -# endif -#endif -#ifndef LV_MEM_CUSTOM_REALLOC -# ifdef CONFIG_LV_MEM_CUSTOM_REALLOC -# define LV_MEM_CUSTOM_REALLOC CONFIG_LV_MEM_CUSTOM_REALLOC -# else -# define LV_MEM_CUSTOM_REALLOC realloc -# endif -#endif + #ifndef LV_MEM_CUSTOM_INCLUDE + #ifdef CONFIG_LV_MEM_CUSTOM_INCLUDE + #define LV_MEM_CUSTOM_INCLUDE CONFIG_LV_MEM_CUSTOM_INCLUDE + #else + #define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ + #endif + #endif + #ifndef LV_MEM_CUSTOM_ALLOC + #ifdef CONFIG_LV_MEM_CUSTOM_ALLOC + #define LV_MEM_CUSTOM_ALLOC CONFIG_LV_MEM_CUSTOM_ALLOC + #else + #define LV_MEM_CUSTOM_ALLOC malloc + #endif + #endif + #ifndef LV_MEM_CUSTOM_FREE + #ifdef CONFIG_LV_MEM_CUSTOM_FREE + #define LV_MEM_CUSTOM_FREE CONFIG_LV_MEM_CUSTOM_FREE + #else + #define LV_MEM_CUSTOM_FREE free + #endif + #endif + #ifndef LV_MEM_CUSTOM_REALLOC + #ifdef CONFIG_LV_MEM_CUSTOM_REALLOC + #define LV_MEM_CUSTOM_REALLOC CONFIG_LV_MEM_CUSTOM_REALLOC + #else + #define LV_MEM_CUSTOM_REALLOC realloc + #endif + #endif #endif /*LV_MEM_CUSTOM*/ /*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms. *You will see an error log message if there wasn't enough buffers. */ #ifndef LV_MEM_BUF_MAX_NUM -# ifdef CONFIG_LV_MEM_BUF_MAX_NUM -# define LV_MEM_BUF_MAX_NUM CONFIG_LV_MEM_BUF_MAX_NUM -# else -# define LV_MEM_BUF_MAX_NUM 16 -# endif + #ifdef CONFIG_LV_MEM_BUF_MAX_NUM + #define LV_MEM_BUF_MAX_NUM CONFIG_LV_MEM_BUF_MAX_NUM + #else + #define LV_MEM_BUF_MAX_NUM 16 + #endif #endif /*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/ #ifndef LV_MEMCPY_MEMSET_STD -# ifdef CONFIG_LV_MEMCPY_MEMSET_STD -# define LV_MEMCPY_MEMSET_STD CONFIG_LV_MEMCPY_MEMSET_STD -# else -# define LV_MEMCPY_MEMSET_STD 0 -# endif + #ifdef CONFIG_LV_MEMCPY_MEMSET_STD + #define LV_MEMCPY_MEMSET_STD CONFIG_LV_MEMCPY_MEMSET_STD + #else + #define LV_MEMCPY_MEMSET_STD 0 + #endif #endif /*==================== @@ -196,56 +213,56 @@ /*Default display refresh period. LVG will redraw changed areas with this period time*/ #ifndef LV_DISP_DEF_REFR_PERIOD -# ifdef CONFIG_LV_DISP_DEF_REFR_PERIOD -# define LV_DISP_DEF_REFR_PERIOD CONFIG_LV_DISP_DEF_REFR_PERIOD -# else -# define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ -# endif + #ifdef CONFIG_LV_DISP_DEF_REFR_PERIOD + #define LV_DISP_DEF_REFR_PERIOD CONFIG_LV_DISP_DEF_REFR_PERIOD + #else + #define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ + #endif #endif /*Input device read period in milliseconds*/ #ifndef LV_INDEV_DEF_READ_PERIOD -# ifdef CONFIG_LV_INDEV_DEF_READ_PERIOD -# define LV_INDEV_DEF_READ_PERIOD CONFIG_LV_INDEV_DEF_READ_PERIOD -# else -# define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ -# endif + #ifdef CONFIG_LV_INDEV_DEF_READ_PERIOD + #define LV_INDEV_DEF_READ_PERIOD CONFIG_LV_INDEV_DEF_READ_PERIOD + #else + #define LV_INDEV_DEF_READ_PERIOD 30 /*[ms]*/ + #endif #endif /*Use a custom tick source that tells the elapsed time in milliseconds. *It removes the need to manually update the tick with `lv_tick_inc()`)*/ #ifndef LV_TICK_CUSTOM -# ifdef CONFIG_LV_TICK_CUSTOM -# define LV_TICK_CUSTOM CONFIG_LV_TICK_CUSTOM -# else -# define LV_TICK_CUSTOM 0 -# endif + #ifdef CONFIG_LV_TICK_CUSTOM + #define LV_TICK_CUSTOM CONFIG_LV_TICK_CUSTOM + #else + #define LV_TICK_CUSTOM 0 + #endif #endif #if LV_TICK_CUSTOM -#ifndef LV_TICK_CUSTOM_INCLUDE -# ifdef CONFIG_LV_TICK_CUSTOM_INCLUDE -# define LV_TICK_CUSTOM_INCLUDE CONFIG_LV_TICK_CUSTOM_INCLUDE -# else -# define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ -# endif -#endif -#ifndef LV_TICK_CUSTOM_SYS_TIME_EXPR -# ifdef CONFIG_LV_TICK_CUSTOM_SYS_TIME_EXPR -# define LV_TICK_CUSTOM_SYS_TIME_EXPR CONFIG_LV_TICK_CUSTOM_SYS_TIME_EXPR -# else -# define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ -# endif -#endif + #ifndef LV_TICK_CUSTOM_INCLUDE + #ifdef CONFIG_LV_TICK_CUSTOM_INCLUDE + #define LV_TICK_CUSTOM_INCLUDE CONFIG_LV_TICK_CUSTOM_INCLUDE + #else + #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ + #endif + #endif + #ifndef LV_TICK_CUSTOM_SYS_TIME_EXPR + #ifdef CONFIG_LV_TICK_CUSTOM_SYS_TIME_EXPR + #define LV_TICK_CUSTOM_SYS_TIME_EXPR CONFIG_LV_TICK_CUSTOM_SYS_TIME_EXPR + #else + #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ + #endif + #endif #endif /*LV_TICK_CUSTOM*/ /*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. *(Not so important, you can adjust it to modify default sizes and spaces)*/ #ifndef LV_DPI_DEF -# ifdef CONFIG_LV_DPI_DEF -# define LV_DPI_DEF CONFIG_LV_DPI_DEF -# else -# define LV_DPI_DEF 130 /*[px/inch]*/ -# endif + #ifdef CONFIG_LV_DPI_DEF + #define LV_DPI_DEF CONFIG_LV_DPI_DEF + #else + #define LV_DPI_DEF 130 /*[px/inch]*/ + #endif #endif /*======================= @@ -259,146 +276,254 @@ /*Enable complex draw engine. *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/ #ifndef LV_DRAW_COMPLEX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_DRAW_COMPLEX -# define LV_DRAW_COMPLEX CONFIG_LV_DRAW_COMPLEX -# else -# define LV_DRAW_COMPLEX 0 -# endif -# else -# define LV_DRAW_COMPLEX 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_DRAW_COMPLEX + #define LV_DRAW_COMPLEX CONFIG_LV_DRAW_COMPLEX + #else + #define LV_DRAW_COMPLEX 0 + #endif + #else + #define LV_DRAW_COMPLEX 1 + #endif #endif #if LV_DRAW_COMPLEX != 0 -/*Allow buffering some shadow calculation. - *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` - *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/ -#ifndef LV_SHADOW_CACHE_SIZE -# ifdef CONFIG_LV_SHADOW_CACHE_SIZE -# define LV_SHADOW_CACHE_SIZE CONFIG_LV_SHADOW_CACHE_SIZE -# else -# define LV_SHADOW_CACHE_SIZE 0 -# endif -#endif - -/* Set number of maximally cached circle data. - * The circumference of 1/4 circle are saved for anti-aliasing - * radius * 4 bytes are used per circle (the most often used radiuses are saved) - * 0: to disable caching */ -#ifndef LV_CIRCLE_CACHE_SIZE -# ifdef CONFIG_LV_CIRCLE_CACHE_SIZE -# define LV_CIRCLE_CACHE_SIZE CONFIG_LV_CIRCLE_CACHE_SIZE -# else -# define LV_CIRCLE_CACHE_SIZE 4 -# endif -#endif + /*Allow buffering some shadow calculation. + *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius` + *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/ + #ifndef LV_SHADOW_CACHE_SIZE + #ifdef CONFIG_LV_SHADOW_CACHE_SIZE + #define LV_SHADOW_CACHE_SIZE CONFIG_LV_SHADOW_CACHE_SIZE + #else + #define LV_SHADOW_CACHE_SIZE 0 + #endif + #endif + /* Set number of maximally cached circle data. + * The circumference of 1/4 circle are saved for anti-aliasing + * radius * 4 bytes are used per circle (the most often used radiuses are saved) + * 0: to disable caching */ + #ifndef LV_CIRCLE_CACHE_SIZE + #ifdef CONFIG_LV_CIRCLE_CACHE_SIZE + #define LV_CIRCLE_CACHE_SIZE CONFIG_LV_CIRCLE_CACHE_SIZE + #else + #define LV_CIRCLE_CACHE_SIZE 4 + #endif + #endif #endif /*LV_DRAW_COMPLEX*/ +/** + * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer + * and blend it as an image with the given opacity. + * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) + * The widget can be buffered in smaller chunks to avoid using large buffers. + * + * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it + * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. + * + * Both buffer sizes are in bytes. + * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers + * and can't be drawn in chunks. So these settings affects only widgets with opacity. + */ +#ifndef LV_LAYER_SIMPLE_BUF_SIZE + #ifdef CONFIG_LV_LAYER_SIMPLE_BUF_SIZE + #define LV_LAYER_SIMPLE_BUF_SIZE CONFIG_LV_LAYER_SIMPLE_BUF_SIZE + #else + #define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) + #endif +#endif +#ifndef LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE + #ifdef CONFIG_LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE + #define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE CONFIG_LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE + #else + #define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) + #endif +#endif + /*Default image cache size. Image caching keeps the images opened. *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. *However the opened images might consume additional RAM. *0: to disable caching*/ #ifndef LV_IMG_CACHE_DEF_SIZE -# ifdef CONFIG_LV_IMG_CACHE_DEF_SIZE -# define LV_IMG_CACHE_DEF_SIZE CONFIG_LV_IMG_CACHE_DEF_SIZE -# else -# define LV_IMG_CACHE_DEF_SIZE 0 -# endif + #ifdef CONFIG_LV_IMG_CACHE_DEF_SIZE + #define LV_IMG_CACHE_DEF_SIZE CONFIG_LV_IMG_CACHE_DEF_SIZE + #else + #define LV_IMG_CACHE_DEF_SIZE 0 + #endif #endif -/*Maximum buffer size to allocate for rotation. Only used if software rotation is enabled in the display driver.*/ +/*Number of stops allowed per gradient. Increase this to allow more stops. + *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ +#ifndef LV_GRADIENT_MAX_STOPS + #ifdef CONFIG_LV_GRADIENT_MAX_STOPS + #define LV_GRADIENT_MAX_STOPS CONFIG_LV_GRADIENT_MAX_STOPS + #else + #define LV_GRADIENT_MAX_STOPS 2 + #endif +#endif + +/*Default gradient buffer size. + *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. + *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. + *If the cache is too small the map will be allocated only while it's required for the drawing. + *0 mean no caching.*/ +#ifndef LV_GRAD_CACHE_DEF_SIZE + #ifdef CONFIG_LV_GRAD_CACHE_DEF_SIZE + #define LV_GRAD_CACHE_DEF_SIZE CONFIG_LV_GRAD_CACHE_DEF_SIZE + #else + #define LV_GRAD_CACHE_DEF_SIZE 0 + #endif +#endif + +/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) + *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface + *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ +#ifndef LV_DITHER_GRADIENT + #ifdef CONFIG_LV_DITHER_GRADIENT + #define LV_DITHER_GRADIENT CONFIG_LV_DITHER_GRADIENT + #else + #define LV_DITHER_GRADIENT 0 + #endif +#endif +#if LV_DITHER_GRADIENT + /*Add support for error diffusion dithering. + *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. + *The increase in memory consumption is (24 bits * object's width)*/ + #ifndef LV_DITHER_ERROR_DIFFUSION + #ifdef CONFIG_LV_DITHER_ERROR_DIFFUSION + #define LV_DITHER_ERROR_DIFFUSION CONFIG_LV_DITHER_ERROR_DIFFUSION + #else + #define LV_DITHER_ERROR_DIFFUSION 0 + #endif + #endif +#endif + +/*Maximum buffer size to allocate for rotation. + *Only used if software rotation is enabled in the display driver.*/ #ifndef LV_DISP_ROT_MAX_BUF -# ifdef CONFIG_LV_DISP_ROT_MAX_BUF -# define LV_DISP_ROT_MAX_BUF CONFIG_LV_DISP_ROT_MAX_BUF -# else -# define LV_DISP_ROT_MAX_BUF (10*1024) -# endif + #ifdef CONFIG_LV_DISP_ROT_MAX_BUF + #define LV_DISP_ROT_MAX_BUF CONFIG_LV_DISP_ROT_MAX_BUF + #else + #define LV_DISP_ROT_MAX_BUF (10*1024) + #endif #endif /*------------- * GPU *-----------*/ +/*Use Arm's 2D acceleration library Arm-2D */ +#ifndef LV_USE_GPU_ARM2D + #ifdef CONFIG_LV_USE_GPU_ARM2D + #define LV_USE_GPU_ARM2D CONFIG_LV_USE_GPU_ARM2D + #else + #define LV_USE_GPU_ARM2D 0 + #endif +#endif + /*Use STM32's DMA2D (aka Chrom Art) GPU*/ #ifndef LV_USE_GPU_STM32_DMA2D -# ifdef CONFIG_LV_USE_GPU_STM32_DMA2D -# define LV_USE_GPU_STM32_DMA2D CONFIG_LV_USE_GPU_STM32_DMA2D -# else -# define LV_USE_GPU_STM32_DMA2D 0 -# endif + #ifdef CONFIG_LV_USE_GPU_STM32_DMA2D + #define LV_USE_GPU_STM32_DMA2D CONFIG_LV_USE_GPU_STM32_DMA2D + #else + #define LV_USE_GPU_STM32_DMA2D 0 + #endif #endif #if LV_USE_GPU_STM32_DMA2D -/*Must be defined to include path of CMSIS header of target processor -e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ -#ifndef LV_GPU_DMA2D_CMSIS_INCLUDE -# ifdef CONFIG_LV_GPU_DMA2D_CMSIS_INCLUDE -# define LV_GPU_DMA2D_CMSIS_INCLUDE CONFIG_LV_GPU_DMA2D_CMSIS_INCLUDE -# else -# define LV_GPU_DMA2D_CMSIS_INCLUDE -# endif + /*Must be defined to include path of CMSIS header of target processor + e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ + #ifndef LV_GPU_DMA2D_CMSIS_INCLUDE + #ifdef CONFIG_LV_GPU_DMA2D_CMSIS_INCLUDE + #define LV_GPU_DMA2D_CMSIS_INCLUDE CONFIG_LV_GPU_DMA2D_CMSIS_INCLUDE + #else + #define LV_GPU_DMA2D_CMSIS_INCLUDE + #endif + #endif #endif + +/*Use SWM341's DMA2D GPU*/ +#ifndef LV_USE_GPU_SWM341_DMA2D + #ifdef CONFIG_LV_USE_GPU_SWM341_DMA2D + #define LV_USE_GPU_SWM341_DMA2D CONFIG_LV_USE_GPU_SWM341_DMA2D + #else + #define LV_USE_GPU_SWM341_DMA2D 0 + #endif +#endif +#if LV_USE_GPU_SWM341_DMA2D + #ifndef LV_GPU_SWM341_DMA2D_INCLUDE + #ifdef CONFIG_LV_GPU_SWM341_DMA2D_INCLUDE + #define LV_GPU_SWM341_DMA2D_INCLUDE CONFIG_LV_GPU_SWM341_DMA2D_INCLUDE + #else + #define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h" + #endif + #endif #endif /*Use NXP's PXP GPU iMX RTxxx platforms*/ #ifndef LV_USE_GPU_NXP_PXP -# ifdef CONFIG_LV_USE_GPU_NXP_PXP -# define LV_USE_GPU_NXP_PXP CONFIG_LV_USE_GPU_NXP_PXP -# else -# define LV_USE_GPU_NXP_PXP 0 -# endif + #ifdef CONFIG_LV_USE_GPU_NXP_PXP + #define LV_USE_GPU_NXP_PXP CONFIG_LV_USE_GPU_NXP_PXP + #else + #define LV_USE_GPU_NXP_PXP 0 + #endif #endif #if LV_USE_GPU_NXP_PXP -/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c) - * and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS - * has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected. - *0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init() - */ -#ifndef LV_USE_GPU_NXP_PXP_AUTO_INIT -# ifdef CONFIG_LV_USE_GPU_NXP_PXP_AUTO_INIT -# define LV_USE_GPU_NXP_PXP_AUTO_INIT CONFIG_LV_USE_GPU_NXP_PXP_AUTO_INIT -# else -# define LV_USE_GPU_NXP_PXP_AUTO_INIT 0 -# endif -#endif + /*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c) + * and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS + * has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected. + *0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init() + */ + #ifndef LV_USE_GPU_NXP_PXP_AUTO_INIT + #ifdef CONFIG_LV_USE_GPU_NXP_PXP_AUTO_INIT + #define LV_USE_GPU_NXP_PXP_AUTO_INIT CONFIG_LV_USE_GPU_NXP_PXP_AUTO_INIT + #else + #define LV_USE_GPU_NXP_PXP_AUTO_INIT 0 + #endif + #endif #endif /*Use NXP's VG-Lite GPU iMX RTxxx platforms*/ #ifndef LV_USE_GPU_NXP_VG_LITE -# ifdef CONFIG_LV_USE_GPU_NXP_VG_LITE -# define LV_USE_GPU_NXP_VG_LITE CONFIG_LV_USE_GPU_NXP_VG_LITE -# else -# define LV_USE_GPU_NXP_VG_LITE 0 -# endif + #ifdef CONFIG_LV_USE_GPU_NXP_VG_LITE + #define LV_USE_GPU_NXP_VG_LITE CONFIG_LV_USE_GPU_NXP_VG_LITE + #else + #define LV_USE_GPU_NXP_VG_LITE 0 + #endif #endif -/*Use exnternal renderer*/ -#ifndef LV_USE_EXTERNAL_RENDERER -# ifdef CONFIG_LV_USE_EXTERNAL_RENDERER -# define LV_USE_EXTERNAL_RENDERER CONFIG_LV_USE_EXTERNAL_RENDERER -# else -# define LV_USE_EXTERNAL_RENDERER 0 -# endif -#endif - -/*Use SDL renderer API. Requires LV_USE_EXTERNAL_RENDERER*/ +/*Use SDL renderer API*/ #ifndef LV_USE_GPU_SDL -# ifdef CONFIG_LV_USE_GPU_SDL -# define LV_USE_GPU_SDL CONFIG_LV_USE_GPU_SDL -# else -# define LV_USE_GPU_SDL 0 -# endif + #ifdef CONFIG_LV_USE_GPU_SDL + #define LV_USE_GPU_SDL CONFIG_LV_USE_GPU_SDL + #else + #define LV_USE_GPU_SDL 0 + #endif #endif #if LV_USE_GPU_SDL -#ifndef LV_GPU_SDL_INCLUDE_PATH -# ifdef CONFIG_LV_GPU_SDL_INCLUDE_PATH -# define LV_GPU_SDL_INCLUDE_PATH CONFIG_LV_GPU_SDL_INCLUDE_PATH -# else -# define LV_GPU_SDL_INCLUDE_PATH -# endif -#endif + #ifndef LV_GPU_SDL_INCLUDE_PATH + #ifdef CONFIG_LV_GPU_SDL_INCLUDE_PATH + #define LV_GPU_SDL_INCLUDE_PATH CONFIG_LV_GPU_SDL_INCLUDE_PATH + #else + #define LV_GPU_SDL_INCLUDE_PATH + #endif + #endif + /*Texture cache size, 8MB by default*/ + #ifndef LV_GPU_SDL_LRU_SIZE + #ifdef CONFIG_LV_GPU_SDL_LRU_SIZE + #define LV_GPU_SDL_LRU_SIZE CONFIG_LV_GPU_SDL_LRU_SIZE + #else + #define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8) + #endif + #endif + /*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/ + #ifndef LV_GPU_SDL_CUSTOM_BLEND_MODE + #ifdef CONFIG_LV_GPU_SDL_CUSTOM_BLEND_MODE + #define LV_GPU_SDL_CUSTOM_BLEND_MODE CONFIG_LV_GPU_SDL_CUSTOM_BLEND_MODE + #else + #define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6)) + #endif + #endif #endif /*------------- @@ -407,128 +532,128 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Enable the log module*/ #ifndef LV_USE_LOG -# ifdef CONFIG_LV_USE_LOG -# define LV_USE_LOG CONFIG_LV_USE_LOG -# else -# define LV_USE_LOG 0 -# endif + #ifdef CONFIG_LV_USE_LOG + #define LV_USE_LOG CONFIG_LV_USE_LOG + #else + #define LV_USE_LOG 0 + #endif #endif #if LV_USE_LOG -/*How important log should be added: - *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information - *LV_LOG_LEVEL_INFO Log important events - *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem - *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail - *LV_LOG_LEVEL_USER Only logs added by the user - *LV_LOG_LEVEL_NONE Do not log anything*/ -#ifndef LV_LOG_LEVEL -# ifdef CONFIG_LV_LOG_LEVEL -# define LV_LOG_LEVEL CONFIG_LV_LOG_LEVEL -# else -# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN -# endif -#endif + /*How important log should be added: + *LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + *LV_LOG_LEVEL_INFO Log important events + *LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + *LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + *LV_LOG_LEVEL_USER Only logs added by the user + *LV_LOG_LEVEL_NONE Do not log anything*/ + #ifndef LV_LOG_LEVEL + #ifdef CONFIG_LV_LOG_LEVEL + #define LV_LOG_LEVEL CONFIG_LV_LOG_LEVEL + #else + #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + #endif + #endif -/*1: Print the log with 'printf'; - *0: User need to register a callback with `lv_log_register_print_cb()`*/ -#ifndef LV_LOG_PRINTF -# ifdef CONFIG_LV_LOG_PRINTF -# define LV_LOG_PRINTF CONFIG_LV_LOG_PRINTF -# else -# define LV_LOG_PRINTF 0 -# endif -#endif + /*1: Print the log with 'printf'; + *0: User need to register a callback with `lv_log_register_print_cb()`*/ + #ifndef LV_LOG_PRINTF + #ifdef CONFIG_LV_LOG_PRINTF + #define LV_LOG_PRINTF CONFIG_LV_LOG_PRINTF + #else + #define LV_LOG_PRINTF 0 + #endif + #endif -/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ -#ifndef LV_LOG_TRACE_MEM -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_MEM -# define LV_LOG_TRACE_MEM CONFIG_LV_LOG_TRACE_MEM -# else -# define LV_LOG_TRACE_MEM 0 -# endif -# else -# define LV_LOG_TRACE_MEM 1 -# endif -#endif -#ifndef LV_LOG_TRACE_TIMER -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_TIMER -# define LV_LOG_TRACE_TIMER CONFIG_LV_LOG_TRACE_TIMER -# else -# define LV_LOG_TRACE_TIMER 0 -# endif -# else -# define LV_LOG_TRACE_TIMER 1 -# endif -#endif -#ifndef LV_LOG_TRACE_INDEV -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_INDEV -# define LV_LOG_TRACE_INDEV CONFIG_LV_LOG_TRACE_INDEV -# else -# define LV_LOG_TRACE_INDEV 0 -# endif -# else -# define LV_LOG_TRACE_INDEV 1 -# endif -#endif -#ifndef LV_LOG_TRACE_DISP_REFR -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_DISP_REFR -# define LV_LOG_TRACE_DISP_REFR CONFIG_LV_LOG_TRACE_DISP_REFR -# else -# define LV_LOG_TRACE_DISP_REFR 0 -# endif -# else -# define LV_LOG_TRACE_DISP_REFR 1 -# endif -#endif -#ifndef LV_LOG_TRACE_EVENT -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_EVENT -# define LV_LOG_TRACE_EVENT CONFIG_LV_LOG_TRACE_EVENT -# else -# define LV_LOG_TRACE_EVENT 0 -# endif -# else -# define LV_LOG_TRACE_EVENT 1 -# endif -#endif -#ifndef LV_LOG_TRACE_OBJ_CREATE -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_OBJ_CREATE -# define LV_LOG_TRACE_OBJ_CREATE CONFIG_LV_LOG_TRACE_OBJ_CREATE -# else -# define LV_LOG_TRACE_OBJ_CREATE 0 -# endif -# else -# define LV_LOG_TRACE_OBJ_CREATE 1 -# endif -#endif -#ifndef LV_LOG_TRACE_LAYOUT -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_LAYOUT -# define LV_LOG_TRACE_LAYOUT CONFIG_LV_LOG_TRACE_LAYOUT -# else -# define LV_LOG_TRACE_LAYOUT 0 -# endif -# else -# define LV_LOG_TRACE_LAYOUT 1 -# endif -#endif -#ifndef LV_LOG_TRACE_ANIM -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_LOG_TRACE_ANIM -# define LV_LOG_TRACE_ANIM CONFIG_LV_LOG_TRACE_ANIM -# else -# define LV_LOG_TRACE_ANIM 0 -# endif -# else -# define LV_LOG_TRACE_ANIM 1 -# endif -#endif + /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/ + #ifndef LV_LOG_TRACE_MEM + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_MEM + #define LV_LOG_TRACE_MEM CONFIG_LV_LOG_TRACE_MEM + #else + #define LV_LOG_TRACE_MEM 0 + #endif + #else + #define LV_LOG_TRACE_MEM 1 + #endif + #endif + #ifndef LV_LOG_TRACE_TIMER + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_TIMER + #define LV_LOG_TRACE_TIMER CONFIG_LV_LOG_TRACE_TIMER + #else + #define LV_LOG_TRACE_TIMER 0 + #endif + #else + #define LV_LOG_TRACE_TIMER 1 + #endif + #endif + #ifndef LV_LOG_TRACE_INDEV + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_INDEV + #define LV_LOG_TRACE_INDEV CONFIG_LV_LOG_TRACE_INDEV + #else + #define LV_LOG_TRACE_INDEV 0 + #endif + #else + #define LV_LOG_TRACE_INDEV 1 + #endif + #endif + #ifndef LV_LOG_TRACE_DISP_REFR + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_DISP_REFR + #define LV_LOG_TRACE_DISP_REFR CONFIG_LV_LOG_TRACE_DISP_REFR + #else + #define LV_LOG_TRACE_DISP_REFR 0 + #endif + #else + #define LV_LOG_TRACE_DISP_REFR 1 + #endif + #endif + #ifndef LV_LOG_TRACE_EVENT + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_EVENT + #define LV_LOG_TRACE_EVENT CONFIG_LV_LOG_TRACE_EVENT + #else + #define LV_LOG_TRACE_EVENT 0 + #endif + #else + #define LV_LOG_TRACE_EVENT 1 + #endif + #endif + #ifndef LV_LOG_TRACE_OBJ_CREATE + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_OBJ_CREATE + #define LV_LOG_TRACE_OBJ_CREATE CONFIG_LV_LOG_TRACE_OBJ_CREATE + #else + #define LV_LOG_TRACE_OBJ_CREATE 0 + #endif + #else + #define LV_LOG_TRACE_OBJ_CREATE 1 + #endif + #endif + #ifndef LV_LOG_TRACE_LAYOUT + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_LAYOUT + #define LV_LOG_TRACE_LAYOUT CONFIG_LV_LOG_TRACE_LAYOUT + #else + #define LV_LOG_TRACE_LAYOUT 0 + #endif + #else + #define LV_LOG_TRACE_LAYOUT 1 + #endif + #endif + #ifndef LV_LOG_TRACE_ANIM + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LOG_TRACE_ANIM + #define LV_LOG_TRACE_ANIM CONFIG_LV_LOG_TRACE_ANIM + #else + #define LV_LOG_TRACE_ANIM 0 + #endif + #else + #define LV_LOG_TRACE_ANIM 1 + #endif + #endif #endif /*LV_USE_LOG*/ @@ -539,176 +664,184 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Enable asserts if an operation is failed or an invalid data is found. *If LV_USE_LOG is enabled an error message will be printed on failure*/ #ifndef LV_USE_ASSERT_NULL -# ifdef CONFIG_LV_USE_ASSERT_NULL -# define LV_USE_ASSERT_NULL CONFIG_LV_USE_ASSERT_NULL -# else -# define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_ASSERT_NULL + #define LV_USE_ASSERT_NULL CONFIG_LV_USE_ASSERT_NULL + #else + #define LV_USE_ASSERT_NULL 0 + #endif + #else + #define LV_USE_ASSERT_NULL 1 /*Check if the parameter is NULL. (Very fast, recommended)*/ + #endif #endif #ifndef LV_USE_ASSERT_MALLOC -# ifdef CONFIG_LV_USE_ASSERT_MALLOC -# define LV_USE_ASSERT_MALLOC CONFIG_LV_USE_ASSERT_MALLOC -# else -# define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_ASSERT_MALLOC + #define LV_USE_ASSERT_MALLOC CONFIG_LV_USE_ASSERT_MALLOC + #else + #define LV_USE_ASSERT_MALLOC 0 + #endif + #else + #define LV_USE_ASSERT_MALLOC 1 /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/ + #endif #endif #ifndef LV_USE_ASSERT_STYLE -# ifdef CONFIG_LV_USE_ASSERT_STYLE -# define LV_USE_ASSERT_STYLE CONFIG_LV_USE_ASSERT_STYLE -# else -# define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ -# endif + #ifdef CONFIG_LV_USE_ASSERT_STYLE + #define LV_USE_ASSERT_STYLE CONFIG_LV_USE_ASSERT_STYLE + #else + #define LV_USE_ASSERT_STYLE 0 /*Check if the styles are properly initialized. (Very fast, recommended)*/ + #endif #endif #ifndef LV_USE_ASSERT_MEM_INTEGRITY -# ifdef CONFIG_LV_USE_ASSERT_MEM_INTEGRITY -# define LV_USE_ASSERT_MEM_INTEGRITY CONFIG_LV_USE_ASSERT_MEM_INTEGRITY -# else -# define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ -# endif + #ifdef CONFIG_LV_USE_ASSERT_MEM_INTEGRITY + #define LV_USE_ASSERT_MEM_INTEGRITY CONFIG_LV_USE_ASSERT_MEM_INTEGRITY + #else + #define LV_USE_ASSERT_MEM_INTEGRITY 0 /*Check the integrity of `lv_mem` after critical operations. (Slow)*/ + #endif #endif #ifndef LV_USE_ASSERT_OBJ -# ifdef CONFIG_LV_USE_ASSERT_OBJ -# define LV_USE_ASSERT_OBJ CONFIG_LV_USE_ASSERT_OBJ -# else -# define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ -# endif + #ifdef CONFIG_LV_USE_ASSERT_OBJ + #define LV_USE_ASSERT_OBJ CONFIG_LV_USE_ASSERT_OBJ + #else + #define LV_USE_ASSERT_OBJ 0 /*Check the object's type and existence (e.g. not deleted). (Slow)*/ + #endif #endif /*Add a custom handler when assert happens e.g. to restart the MCU*/ #ifndef LV_ASSERT_HANDLER_INCLUDE -# ifdef CONFIG_LV_ASSERT_HANDLER_INCLUDE -# define LV_ASSERT_HANDLER_INCLUDE CONFIG_LV_ASSERT_HANDLER_INCLUDE -# else -# define LV_ASSERT_HANDLER_INCLUDE -# endif + #ifdef CONFIG_LV_ASSERT_HANDLER_INCLUDE + #define LV_ASSERT_HANDLER_INCLUDE CONFIG_LV_ASSERT_HANDLER_INCLUDE + #else + #define LV_ASSERT_HANDLER_INCLUDE + #endif #endif #ifndef LV_ASSERT_HANDLER -# ifdef CONFIG_LV_ASSERT_HANDLER -# define LV_ASSERT_HANDLER CONFIG_LV_ASSERT_HANDLER -# else -# define LV_ASSERT_HANDLER while(1); /*Halt by default*/ -# endif + #ifdef CONFIG_LV_ASSERT_HANDLER + #define LV_ASSERT_HANDLER CONFIG_LV_ASSERT_HANDLER + #else + #define LV_ASSERT_HANDLER while(1); /*Halt by default*/ + #endif #endif /*------------- * Others *-----------*/ -/*1: Show CPU usage and FPS count in the right bottom corner*/ +/*1: Show CPU usage and FPS count*/ #ifndef LV_USE_PERF_MONITOR -# ifdef CONFIG_LV_USE_PERF_MONITOR -# define LV_USE_PERF_MONITOR CONFIG_LV_USE_PERF_MONITOR -# else -# define LV_USE_PERF_MONITOR 0 -# endif + #ifdef CONFIG_LV_USE_PERF_MONITOR + #define LV_USE_PERF_MONITOR CONFIG_LV_USE_PERF_MONITOR + #else + #define LV_USE_PERF_MONITOR 0 + #endif #endif #if LV_USE_PERF_MONITOR -#ifndef LV_USE_PERF_MONITOR_POS -# ifdef CONFIG_LV_USE_PERF_MONITOR_POS -# define LV_USE_PERF_MONITOR_POS CONFIG_LV_USE_PERF_MONITOR_POS -# else -# define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT -# endif -#endif + #ifndef LV_USE_PERF_MONITOR_POS + #ifdef CONFIG_LV_USE_PERF_MONITOR_POS + #define LV_USE_PERF_MONITOR_POS CONFIG_LV_USE_PERF_MONITOR_POS + #else + #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT + #endif + #endif #endif -/*1: Show the used memory and the memory fragmentation in the left bottom corner +/*1: Show the used memory and the memory fragmentation * Requires LV_MEM_CUSTOM = 0*/ #ifndef LV_USE_MEM_MONITOR -# ifdef CONFIG_LV_USE_MEM_MONITOR -# define LV_USE_MEM_MONITOR CONFIG_LV_USE_MEM_MONITOR -# else -# define LV_USE_MEM_MONITOR 0 -# endif -#endif -#if LV_USE_PERF_MONITOR -#ifndef LV_USE_MEM_MONITOR_POS -# ifdef CONFIG_LV_USE_MEM_MONITOR_POS -# define LV_USE_MEM_MONITOR_POS CONFIG_LV_USE_MEM_MONITOR_POS -# else -# define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT -# endif + #ifdef CONFIG_LV_USE_MEM_MONITOR + #define LV_USE_MEM_MONITOR CONFIG_LV_USE_MEM_MONITOR + #else + #define LV_USE_MEM_MONITOR 0 + #endif #endif +#if LV_USE_MEM_MONITOR + #ifndef LV_USE_MEM_MONITOR_POS + #ifdef CONFIG_LV_USE_MEM_MONITOR_POS + #define LV_USE_MEM_MONITOR_POS CONFIG_LV_USE_MEM_MONITOR_POS + #else + #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT + #endif + #endif #endif /*1: Draw random colored rectangles over the redrawn areas*/ #ifndef LV_USE_REFR_DEBUG -# ifdef CONFIG_LV_USE_REFR_DEBUG -# define LV_USE_REFR_DEBUG CONFIG_LV_USE_REFR_DEBUG -# else -# define LV_USE_REFR_DEBUG 0 -# endif + #ifdef CONFIG_LV_USE_REFR_DEBUG + #define LV_USE_REFR_DEBUG CONFIG_LV_USE_REFR_DEBUG + #else + #define LV_USE_REFR_DEBUG 0 + #endif #endif /*Change the built in (v)snprintf functions*/ #ifndef LV_SPRINTF_CUSTOM -# ifdef CONFIG_LV_SPRINTF_CUSTOM -# define LV_SPRINTF_CUSTOM CONFIG_LV_SPRINTF_CUSTOM -# else -# define LV_SPRINTF_CUSTOM 0 -# endif + #ifdef CONFIG_LV_SPRINTF_CUSTOM + #define LV_SPRINTF_CUSTOM CONFIG_LV_SPRINTF_CUSTOM + #else + #define LV_SPRINTF_CUSTOM 0 + #endif #endif #if LV_SPRINTF_CUSTOM -#ifndef LV_SPRINTF_INCLUDE -# ifdef CONFIG_LV_SPRINTF_INCLUDE -# define LV_SPRINTF_INCLUDE CONFIG_LV_SPRINTF_INCLUDE -# else -# define LV_SPRINTF_INCLUDE -# endif -#endif -#ifndef lv_snprintf -# ifdef CONFIG_LV_SNPRINTF -# define lv_snprintf CONFIG_LV_SNPRINTF -# else -# define lv_snprintf snprintf -# endif -#endif -#ifndef lv_vsnprintf -# ifdef CONFIG_LV_VSNPRINTF -# define lv_vsnprintf CONFIG_LV_VSNPRINTF -# else -# define lv_vsnprintf vsnprintf -# endif -#endif + #ifndef LV_SPRINTF_INCLUDE + #ifdef CONFIG_LV_SPRINTF_INCLUDE + #define LV_SPRINTF_INCLUDE CONFIG_LV_SPRINTF_INCLUDE + #else + #define LV_SPRINTF_INCLUDE + #endif + #endif + #ifndef lv_snprintf + #ifdef CONFIG_LV_SNPRINTF + #define lv_snprintf CONFIG_LV_SNPRINTF + #else + #define lv_snprintf snprintf + #endif + #endif + #ifndef lv_vsnprintf + #ifdef CONFIG_LV_VSNPRINTF + #define lv_vsnprintf CONFIG_LV_VSNPRINTF + #else + #define lv_vsnprintf vsnprintf + #endif + #endif #else /*LV_SPRINTF_CUSTOM*/ -#ifndef LV_SPRINTF_USE_FLOAT -# ifdef CONFIG_LV_SPRINTF_USE_FLOAT -# define LV_SPRINTF_USE_FLOAT CONFIG_LV_SPRINTF_USE_FLOAT -# else -# define LV_SPRINTF_USE_FLOAT 0 -# endif -#endif + #ifndef LV_SPRINTF_USE_FLOAT + #ifdef CONFIG_LV_SPRINTF_USE_FLOAT + #define LV_SPRINTF_USE_FLOAT CONFIG_LV_SPRINTF_USE_FLOAT + #else + #define LV_SPRINTF_USE_FLOAT 0 + #endif + #endif #endif /*LV_SPRINTF_CUSTOM*/ #ifndef LV_USE_USER_DATA -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_USER_DATA -# define LV_USE_USER_DATA CONFIG_LV_USE_USER_DATA -# else -# define LV_USE_USER_DATA 0 -# endif -# else -# define LV_USE_USER_DATA 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_USER_DATA + #define LV_USE_USER_DATA CONFIG_LV_USE_USER_DATA + #else + #define LV_USE_USER_DATA 0 + #endif + #else + #define LV_USE_USER_DATA 1 + #endif #endif /*Garbage Collector settings *Used if lvgl is bound to higher level language and the memory is managed by that language*/ #ifndef LV_ENABLE_GC -# ifdef CONFIG_LV_ENABLE_GC -# define LV_ENABLE_GC CONFIG_LV_ENABLE_GC -# else -# define LV_ENABLE_GC 0 -# endif + #ifdef CONFIG_LV_ENABLE_GC + #define LV_ENABLE_GC CONFIG_LV_ENABLE_GC + #else + #define LV_ENABLE_GC 0 + #endif #endif #if LV_ENABLE_GC != 0 -#ifndef LV_GC_INCLUDE -# ifdef CONFIG_LV_GC_INCLUDE -# define LV_GC_INCLUDE CONFIG_LV_GC_INCLUDE -# else -# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ -# endif -#endif + #ifndef LV_GC_INCLUDE + #ifdef CONFIG_LV_GC_INCLUDE + #define LV_GC_INCLUDE CONFIG_LV_GC_INCLUDE + #else + #define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ + #endif + #endif #endif /*LV_ENABLE_GC*/ /*===================== @@ -717,116 +850,116 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*For big endian systems set to 1*/ #ifndef LV_BIG_ENDIAN_SYSTEM -# ifdef CONFIG_LV_BIG_ENDIAN_SYSTEM -# define LV_BIG_ENDIAN_SYSTEM CONFIG_LV_BIG_ENDIAN_SYSTEM -# else -# define LV_BIG_ENDIAN_SYSTEM 0 -# endif + #ifdef CONFIG_LV_BIG_ENDIAN_SYSTEM + #define LV_BIG_ENDIAN_SYSTEM CONFIG_LV_BIG_ENDIAN_SYSTEM + #else + #define LV_BIG_ENDIAN_SYSTEM 0 + #endif #endif /*Define a custom attribute to `lv_tick_inc` function*/ #ifndef LV_ATTRIBUTE_TICK_INC -# ifdef CONFIG_LV_ATTRIBUTE_TICK_INC -# define LV_ATTRIBUTE_TICK_INC CONFIG_LV_ATTRIBUTE_TICK_INC -# else -# define LV_ATTRIBUTE_TICK_INC -# endif + #ifdef CONFIG_LV_ATTRIBUTE_TICK_INC + #define LV_ATTRIBUTE_TICK_INC CONFIG_LV_ATTRIBUTE_TICK_INC + #else + #define LV_ATTRIBUTE_TICK_INC + #endif #endif /*Define a custom attribute to `lv_timer_handler` function*/ #ifndef LV_ATTRIBUTE_TIMER_HANDLER -# ifdef CONFIG_LV_ATTRIBUTE_TIMER_HANDLER -# define LV_ATTRIBUTE_TIMER_HANDLER CONFIG_LV_ATTRIBUTE_TIMER_HANDLER -# else -# define LV_ATTRIBUTE_TIMER_HANDLER -# endif + #ifdef CONFIG_LV_ATTRIBUTE_TIMER_HANDLER + #define LV_ATTRIBUTE_TIMER_HANDLER CONFIG_LV_ATTRIBUTE_TIMER_HANDLER + #else + #define LV_ATTRIBUTE_TIMER_HANDLER + #endif #endif /*Define a custom attribute to `lv_disp_flush_ready` function*/ #ifndef LV_ATTRIBUTE_FLUSH_READY -# ifdef CONFIG_LV_ATTRIBUTE_FLUSH_READY -# define LV_ATTRIBUTE_FLUSH_READY CONFIG_LV_ATTRIBUTE_FLUSH_READY -# else -# define LV_ATTRIBUTE_FLUSH_READY -# endif + #ifdef CONFIG_LV_ATTRIBUTE_FLUSH_READY + #define LV_ATTRIBUTE_FLUSH_READY CONFIG_LV_ATTRIBUTE_FLUSH_READY + #else + #define LV_ATTRIBUTE_FLUSH_READY + #endif #endif /*Required alignment size for buffers*/ #ifndef LV_ATTRIBUTE_MEM_ALIGN_SIZE -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE -# define LV_ATTRIBUTE_MEM_ALIGN_SIZE CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE -# else -# define LV_ATTRIBUTE_MEM_ALIGN_SIZE 0 -# endif -# else -# define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE + #define LV_ATTRIBUTE_MEM_ALIGN_SIZE CONFIG_LV_ATTRIBUTE_MEM_ALIGN_SIZE + #else + #define LV_ATTRIBUTE_MEM_ALIGN_SIZE 0 + #endif + #else + #define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + #endif #endif /*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default). * E.g. __attribute__((aligned(4)))*/ #ifndef LV_ATTRIBUTE_MEM_ALIGN -# ifdef CONFIG_LV_ATTRIBUTE_MEM_ALIGN -# define LV_ATTRIBUTE_MEM_ALIGN CONFIG_LV_ATTRIBUTE_MEM_ALIGN -# else -# define LV_ATTRIBUTE_MEM_ALIGN -# endif + #ifdef CONFIG_LV_ATTRIBUTE_MEM_ALIGN + #define LV_ATTRIBUTE_MEM_ALIGN CONFIG_LV_ATTRIBUTE_MEM_ALIGN + #else + #define LV_ATTRIBUTE_MEM_ALIGN + #endif #endif /*Attribute to mark large constant arrays for example font's bitmaps*/ #ifndef LV_ATTRIBUTE_LARGE_CONST -# ifdef CONFIG_LV_ATTRIBUTE_LARGE_CONST -# define LV_ATTRIBUTE_LARGE_CONST CONFIG_LV_ATTRIBUTE_LARGE_CONST -# else -# define LV_ATTRIBUTE_LARGE_CONST -# endif + #ifdef CONFIG_LV_ATTRIBUTE_LARGE_CONST + #define LV_ATTRIBUTE_LARGE_CONST CONFIG_LV_ATTRIBUTE_LARGE_CONST + #else + #define LV_ATTRIBUTE_LARGE_CONST + #endif #endif -/*Complier prefix for a big array declaration in RAM*/ +/*Compiler prefix for a big array declaration in RAM*/ #ifndef LV_ATTRIBUTE_LARGE_RAM_ARRAY -# ifdef CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY -# define LV_ATTRIBUTE_LARGE_RAM_ARRAY CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY -# else -# define LV_ATTRIBUTE_LARGE_RAM_ARRAY -# endif + #ifdef CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY + #define LV_ATTRIBUTE_LARGE_RAM_ARRAY CONFIG_LV_ATTRIBUTE_LARGE_RAM_ARRAY + #else + #define LV_ATTRIBUTE_LARGE_RAM_ARRAY + #endif #endif /*Place performance critical functions into a faster memory (e.g RAM)*/ #ifndef LV_ATTRIBUTE_FAST_MEM -# ifdef CONFIG_LV_ATTRIBUTE_FAST_MEM -# define LV_ATTRIBUTE_FAST_MEM CONFIG_LV_ATTRIBUTE_FAST_MEM -# else -# define LV_ATTRIBUTE_FAST_MEM -# endif + #ifdef CONFIG_LV_ATTRIBUTE_FAST_MEM + #define LV_ATTRIBUTE_FAST_MEM CONFIG_LV_ATTRIBUTE_FAST_MEM + #else + #define LV_ATTRIBUTE_FAST_MEM + #endif #endif /*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/ #ifndef LV_ATTRIBUTE_DMA -# ifdef CONFIG_LV_ATTRIBUTE_DMA -# define LV_ATTRIBUTE_DMA CONFIG_LV_ATTRIBUTE_DMA -# else -# define LV_ATTRIBUTE_DMA -# endif + #ifdef CONFIG_LV_ATTRIBUTE_DMA + #define LV_ATTRIBUTE_DMA CONFIG_LV_ATTRIBUTE_DMA + #else + #define LV_ATTRIBUTE_DMA + #endif #endif /*Export integer constant to binding. This macro is used with constants in the form of LV_ that *should also appear on LVGL binding API such as Micropython.*/ #ifndef LV_EXPORT_CONST_INT -# ifdef CONFIG_LV_EXPORT_CONST_INT -# define LV_EXPORT_CONST_INT CONFIG_LV_EXPORT_CONST_INT -# else -# define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ -# endif + #ifdef CONFIG_LV_EXPORT_CONST_INT + #define LV_EXPORT_CONST_INT CONFIG_LV_EXPORT_CONST_INT + #else + #define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/ + #endif #endif /*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/ #ifndef LV_USE_LARGE_COORD -# ifdef CONFIG_LV_USE_LARGE_COORD -# define LV_USE_LARGE_COORD CONFIG_LV_USE_LARGE_COORD -# else -# define LV_USE_LARGE_COORD 0 -# endif + #ifdef CONFIG_LV_USE_LARGE_COORD + #define LV_USE_LARGE_COORD CONFIG_LV_USE_LARGE_COORD + #else + #define LV_USE_LARGE_COORD 0 + #endif #endif /*================== @@ -836,260 +969,273 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Montserrat fonts with ASCII range and some symbols using bpp = 4 *https://fonts.google.com/specimen/Montserrat*/ #ifndef LV_FONT_MONTSERRAT_8 -# ifdef CONFIG_LV_FONT_MONTSERRAT_8 -# define LV_FONT_MONTSERRAT_8 CONFIG_LV_FONT_MONTSERRAT_8 -# else -# define LV_FONT_MONTSERRAT_8 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_8 + #define LV_FONT_MONTSERRAT_8 CONFIG_LV_FONT_MONTSERRAT_8 + #else + #define LV_FONT_MONTSERRAT_8 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_10 -# ifdef CONFIG_LV_FONT_MONTSERRAT_10 -# define LV_FONT_MONTSERRAT_10 CONFIG_LV_FONT_MONTSERRAT_10 -# else -# define LV_FONT_MONTSERRAT_10 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_10 + #define LV_FONT_MONTSERRAT_10 CONFIG_LV_FONT_MONTSERRAT_10 + #else + #define LV_FONT_MONTSERRAT_10 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_12 -# ifdef CONFIG_LV_FONT_MONTSERRAT_12 -# define LV_FONT_MONTSERRAT_12 CONFIG_LV_FONT_MONTSERRAT_12 -# else -# define LV_FONT_MONTSERRAT_12 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_12 + #define LV_FONT_MONTSERRAT_12 CONFIG_LV_FONT_MONTSERRAT_12 + #else + #define LV_FONT_MONTSERRAT_12 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_14 -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_FONT_MONTSERRAT_14 -# define LV_FONT_MONTSERRAT_14 CONFIG_LV_FONT_MONTSERRAT_14 -# else -# define LV_FONT_MONTSERRAT_14 0 -# endif -# else -# define LV_FONT_MONTSERRAT_14 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_FONT_MONTSERRAT_14 + #define LV_FONT_MONTSERRAT_14 CONFIG_LV_FONT_MONTSERRAT_14 + #else + #define LV_FONT_MONTSERRAT_14 0 + #endif + #else + #define LV_FONT_MONTSERRAT_14 1 + #endif #endif #ifndef LV_FONT_MONTSERRAT_16 -# ifdef CONFIG_LV_FONT_MONTSERRAT_16 -# define LV_FONT_MONTSERRAT_16 CONFIG_LV_FONT_MONTSERRAT_16 -# else -# define LV_FONT_MONTSERRAT_16 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_16 + #define LV_FONT_MONTSERRAT_16 CONFIG_LV_FONT_MONTSERRAT_16 + #else + #define LV_FONT_MONTSERRAT_16 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_18 -# ifdef CONFIG_LV_FONT_MONTSERRAT_18 -# define LV_FONT_MONTSERRAT_18 CONFIG_LV_FONT_MONTSERRAT_18 -# else -# define LV_FONT_MONTSERRAT_18 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_18 + #define LV_FONT_MONTSERRAT_18 CONFIG_LV_FONT_MONTSERRAT_18 + #else + #define LV_FONT_MONTSERRAT_18 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_20 -# ifdef CONFIG_LV_FONT_MONTSERRAT_20 -# define LV_FONT_MONTSERRAT_20 CONFIG_LV_FONT_MONTSERRAT_20 -# else -# define LV_FONT_MONTSERRAT_20 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_20 + #define LV_FONT_MONTSERRAT_20 CONFIG_LV_FONT_MONTSERRAT_20 + #else + #define LV_FONT_MONTSERRAT_20 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_22 -# ifdef CONFIG_LV_FONT_MONTSERRAT_22 -# define LV_FONT_MONTSERRAT_22 CONFIG_LV_FONT_MONTSERRAT_22 -# else -# define LV_FONT_MONTSERRAT_22 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_22 + #define LV_FONT_MONTSERRAT_22 CONFIG_LV_FONT_MONTSERRAT_22 + #else + #define LV_FONT_MONTSERRAT_22 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_24 -# ifdef CONFIG_LV_FONT_MONTSERRAT_24 -# define LV_FONT_MONTSERRAT_24 CONFIG_LV_FONT_MONTSERRAT_24 -# else -# define LV_FONT_MONTSERRAT_24 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_24 + #define LV_FONT_MONTSERRAT_24 CONFIG_LV_FONT_MONTSERRAT_24 + #else + #define LV_FONT_MONTSERRAT_24 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_26 -# ifdef CONFIG_LV_FONT_MONTSERRAT_26 -# define LV_FONT_MONTSERRAT_26 CONFIG_LV_FONT_MONTSERRAT_26 -# else -# define LV_FONT_MONTSERRAT_26 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_26 + #define LV_FONT_MONTSERRAT_26 CONFIG_LV_FONT_MONTSERRAT_26 + #else + #define LV_FONT_MONTSERRAT_26 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_28 -# ifdef CONFIG_LV_FONT_MONTSERRAT_28 -# define LV_FONT_MONTSERRAT_28 CONFIG_LV_FONT_MONTSERRAT_28 -# else -# define LV_FONT_MONTSERRAT_28 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_28 + #define LV_FONT_MONTSERRAT_28 CONFIG_LV_FONT_MONTSERRAT_28 + #else + #define LV_FONT_MONTSERRAT_28 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_30 -# ifdef CONFIG_LV_FONT_MONTSERRAT_30 -# define LV_FONT_MONTSERRAT_30 CONFIG_LV_FONT_MONTSERRAT_30 -# else -# define LV_FONT_MONTSERRAT_30 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_30 + #define LV_FONT_MONTSERRAT_30 CONFIG_LV_FONT_MONTSERRAT_30 + #else + #define LV_FONT_MONTSERRAT_30 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_32 -# ifdef CONFIG_LV_FONT_MONTSERRAT_32 -# define LV_FONT_MONTSERRAT_32 CONFIG_LV_FONT_MONTSERRAT_32 -# else -# define LV_FONT_MONTSERRAT_32 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_32 + #define LV_FONT_MONTSERRAT_32 CONFIG_LV_FONT_MONTSERRAT_32 + #else + #define LV_FONT_MONTSERRAT_32 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_34 -# ifdef CONFIG_LV_FONT_MONTSERRAT_34 -# define LV_FONT_MONTSERRAT_34 CONFIG_LV_FONT_MONTSERRAT_34 -# else -# define LV_FONT_MONTSERRAT_34 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_34 + #define LV_FONT_MONTSERRAT_34 CONFIG_LV_FONT_MONTSERRAT_34 + #else + #define LV_FONT_MONTSERRAT_34 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_36 -# ifdef CONFIG_LV_FONT_MONTSERRAT_36 -# define LV_FONT_MONTSERRAT_36 CONFIG_LV_FONT_MONTSERRAT_36 -# else -# define LV_FONT_MONTSERRAT_36 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_36 + #define LV_FONT_MONTSERRAT_36 CONFIG_LV_FONT_MONTSERRAT_36 + #else + #define LV_FONT_MONTSERRAT_36 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_38 -# ifdef CONFIG_LV_FONT_MONTSERRAT_38 -# define LV_FONT_MONTSERRAT_38 CONFIG_LV_FONT_MONTSERRAT_38 -# else -# define LV_FONT_MONTSERRAT_38 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_38 + #define LV_FONT_MONTSERRAT_38 CONFIG_LV_FONT_MONTSERRAT_38 + #else + #define LV_FONT_MONTSERRAT_38 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_40 -# ifdef CONFIG_LV_FONT_MONTSERRAT_40 -# define LV_FONT_MONTSERRAT_40 CONFIG_LV_FONT_MONTSERRAT_40 -# else -# define LV_FONT_MONTSERRAT_40 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_40 + #define LV_FONT_MONTSERRAT_40 CONFIG_LV_FONT_MONTSERRAT_40 + #else + #define LV_FONT_MONTSERRAT_40 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_42 -# ifdef CONFIG_LV_FONT_MONTSERRAT_42 -# define LV_FONT_MONTSERRAT_42 CONFIG_LV_FONT_MONTSERRAT_42 -# else -# define LV_FONT_MONTSERRAT_42 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_42 + #define LV_FONT_MONTSERRAT_42 CONFIG_LV_FONT_MONTSERRAT_42 + #else + #define LV_FONT_MONTSERRAT_42 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_44 -# ifdef CONFIG_LV_FONT_MONTSERRAT_44 -# define LV_FONT_MONTSERRAT_44 CONFIG_LV_FONT_MONTSERRAT_44 -# else -# define LV_FONT_MONTSERRAT_44 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_44 + #define LV_FONT_MONTSERRAT_44 CONFIG_LV_FONT_MONTSERRAT_44 + #else + #define LV_FONT_MONTSERRAT_44 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_46 -# ifdef CONFIG_LV_FONT_MONTSERRAT_46 -# define LV_FONT_MONTSERRAT_46 CONFIG_LV_FONT_MONTSERRAT_46 -# else -# define LV_FONT_MONTSERRAT_46 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_46 + #define LV_FONT_MONTSERRAT_46 CONFIG_LV_FONT_MONTSERRAT_46 + #else + #define LV_FONT_MONTSERRAT_46 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_48 -# ifdef CONFIG_LV_FONT_MONTSERRAT_48 -# define LV_FONT_MONTSERRAT_48 CONFIG_LV_FONT_MONTSERRAT_48 -# else -# define LV_FONT_MONTSERRAT_48 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_48 + #define LV_FONT_MONTSERRAT_48 CONFIG_LV_FONT_MONTSERRAT_48 + #else + #define LV_FONT_MONTSERRAT_48 0 + #endif #endif /*Demonstrate special features*/ #ifndef LV_FONT_MONTSERRAT_12_SUBPX -# ifdef CONFIG_LV_FONT_MONTSERRAT_12_SUBPX -# define LV_FONT_MONTSERRAT_12_SUBPX CONFIG_LV_FONT_MONTSERRAT_12_SUBPX -# else -# define LV_FONT_MONTSERRAT_12_SUBPX 0 -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_12_SUBPX + #define LV_FONT_MONTSERRAT_12_SUBPX CONFIG_LV_FONT_MONTSERRAT_12_SUBPX + #else + #define LV_FONT_MONTSERRAT_12_SUBPX 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_28_COMPRESSED -# ifdef CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED -# define LV_FONT_MONTSERRAT_28_COMPRESSED CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED -# else -# define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ -# endif + #ifdef CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED + #define LV_FONT_MONTSERRAT_28_COMPRESSED CONFIG_LV_FONT_MONTSERRAT_28_COMPRESSED + #else + #define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/ + #endif #endif #ifndef LV_FONT_DEJAVU_16_PERSIAN_HEBREW -# ifdef CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW -# define LV_FONT_DEJAVU_16_PERSIAN_HEBREW CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW -# else -# define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Perisan letters and all their forms*/ -# endif + #ifdef CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW + #define LV_FONT_DEJAVU_16_PERSIAN_HEBREW CONFIG_LV_FONT_DEJAVU_16_PERSIAN_HEBREW + #else + #define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Persian letters and all their forms*/ + #endif #endif #ifndef LV_FONT_SIMSUN_16_CJK -# ifdef CONFIG_LV_FONT_SIMSUN_16_CJK -# define LV_FONT_SIMSUN_16_CJK CONFIG_LV_FONT_SIMSUN_16_CJK -# else -# define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ -# endif + #ifdef CONFIG_LV_FONT_SIMSUN_16_CJK + #define LV_FONT_SIMSUN_16_CJK CONFIG_LV_FONT_SIMSUN_16_CJK + #else + #define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/ + #endif #endif /*Pixel perfect monospace fonts*/ #ifndef LV_FONT_UNSCII_8 -# ifdef CONFIG_LV_FONT_UNSCII_8 -# define LV_FONT_UNSCII_8 CONFIG_LV_FONT_UNSCII_8 -# else -# define LV_FONT_UNSCII_8 0 -# endif + #ifdef CONFIG_LV_FONT_UNSCII_8 + #define LV_FONT_UNSCII_8 CONFIG_LV_FONT_UNSCII_8 + #else + #define LV_FONT_UNSCII_8 0 + #endif #endif #ifndef LV_FONT_UNSCII_16 -# ifdef CONFIG_LV_FONT_UNSCII_16 -# define LV_FONT_UNSCII_16 CONFIG_LV_FONT_UNSCII_16 -# else -# define LV_FONT_UNSCII_16 0 -# endif + #ifdef CONFIG_LV_FONT_UNSCII_16 + #define LV_FONT_UNSCII_16 CONFIG_LV_FONT_UNSCII_16 + #else + #define LV_FONT_UNSCII_16 0 + #endif #endif /*Optionally declare custom fonts here. *You can use these fonts as default font too and they will be available globally. *E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/ #ifndef LV_FONT_CUSTOM_DECLARE -# ifdef CONFIG_LV_FONT_CUSTOM_DECLARE -# define LV_FONT_CUSTOM_DECLARE CONFIG_LV_FONT_CUSTOM_DECLARE -# else -# define LV_FONT_CUSTOM_DECLARE -# endif + #ifdef CONFIG_LV_FONT_CUSTOM_DECLARE + #define LV_FONT_CUSTOM_DECLARE CONFIG_LV_FONT_CUSTOM_DECLARE + #else + #define LV_FONT_CUSTOM_DECLARE + #endif #endif /*Always set a default font*/ #ifndef LV_FONT_DEFAULT -# ifdef CONFIG_LV_FONT_DEFAULT -# define LV_FONT_DEFAULT CONFIG_LV_FONT_DEFAULT -# else -# define LV_FONT_DEFAULT &lv_font_montserrat_14 -# endif + #ifdef CONFIG_LV_FONT_DEFAULT + #define LV_FONT_DEFAULT CONFIG_LV_FONT_DEFAULT + #else + #define LV_FONT_DEFAULT &lv_font_montserrat_14 + #endif #endif /*Enable handling large font and/or fonts with a lot of characters. *The limit depends on the font size, font face and bpp. *Compiler error will be triggered if a font needs it.*/ #ifndef LV_FONT_FMT_TXT_LARGE -# ifdef CONFIG_LV_FONT_FMT_TXT_LARGE -# define LV_FONT_FMT_TXT_LARGE CONFIG_LV_FONT_FMT_TXT_LARGE -# else -# define LV_FONT_FMT_TXT_LARGE 0 -# endif + #ifdef CONFIG_LV_FONT_FMT_TXT_LARGE + #define LV_FONT_FMT_TXT_LARGE CONFIG_LV_FONT_FMT_TXT_LARGE + #else + #define LV_FONT_FMT_TXT_LARGE 0 + #endif #endif /*Enables/disables support for compressed fonts.*/ #ifndef LV_USE_FONT_COMPRESSED -# ifdef CONFIG_LV_USE_FONT_COMPRESSED -# define LV_USE_FONT_COMPRESSED CONFIG_LV_USE_FONT_COMPRESSED -# else -# define LV_USE_FONT_COMPRESSED 0 -# endif + #ifdef CONFIG_LV_USE_FONT_COMPRESSED + #define LV_USE_FONT_COMPRESSED CONFIG_LV_USE_FONT_COMPRESSED + #else + #define LV_USE_FONT_COMPRESSED 0 + #endif #endif /*Enable subpixel rendering*/ #ifndef LV_USE_FONT_SUBPX -# ifdef CONFIG_LV_USE_FONT_SUBPX -# define LV_USE_FONT_SUBPX CONFIG_LV_USE_FONT_SUBPX -# else -# define LV_USE_FONT_SUBPX 0 -# endif + #ifdef CONFIG_LV_USE_FONT_SUBPX + #define LV_USE_FONT_SUBPX CONFIG_LV_USE_FONT_SUBPX + #else + #define LV_USE_FONT_SUBPX 0 + #endif #endif #if LV_USE_FONT_SUBPX -/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/ -#ifndef LV_FONT_SUBPX_BGR -# ifdef CONFIG_LV_FONT_SUBPX_BGR -# define LV_FONT_SUBPX_BGR CONFIG_LV_FONT_SUBPX_BGR -# else -# define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ -# endif + /*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/ + #ifndef LV_FONT_SUBPX_BGR + #ifdef CONFIG_LV_FONT_SUBPX_BGR + #define LV_FONT_SUBPX_BGR CONFIG_LV_FONT_SUBPX_BGR + #else + #define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ + #endif + #endif #endif + +/*Enable drawing placeholders when glyph dsc is not found*/ +#ifndef LV_USE_FONT_PLACEHOLDER + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_FONT_PLACEHOLDER + #define LV_USE_FONT_PLACEHOLDER CONFIG_LV_USE_FONT_PLACEHOLDER + #else + #define LV_USE_FONT_PLACEHOLDER 0 + #endif + #else + #define LV_USE_FONT_PLACEHOLDER 1 + #endif #endif /*================= @@ -1103,93 +1249,93 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ * - LV_TXT_ENC_ASCII */ #ifndef LV_TXT_ENC -# ifdef CONFIG_LV_TXT_ENC -# define LV_TXT_ENC CONFIG_LV_TXT_ENC -# else -# define LV_TXT_ENC LV_TXT_ENC_UTF8 -# endif + #ifdef CONFIG_LV_TXT_ENC + #define LV_TXT_ENC CONFIG_LV_TXT_ENC + #else + #define LV_TXT_ENC LV_TXT_ENC_UTF8 + #endif #endif - /*Can break (wrap) texts on these chars*/ +/*Can break (wrap) texts on these chars*/ #ifndef LV_TXT_BREAK_CHARS -# ifdef CONFIG_LV_TXT_BREAK_CHARS -# define LV_TXT_BREAK_CHARS CONFIG_LV_TXT_BREAK_CHARS -# else -# define LV_TXT_BREAK_CHARS " ,.;:-_" -# endif + #ifdef CONFIG_LV_TXT_BREAK_CHARS + #define LV_TXT_BREAK_CHARS CONFIG_LV_TXT_BREAK_CHARS + #else + #define LV_TXT_BREAK_CHARS " ,.;:-_" + #endif #endif /*If a word is at least this long, will break wherever "prettiest" *To disable, set to a value <= 0*/ #ifndef LV_TXT_LINE_BREAK_LONG_LEN -# ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_LEN -# define LV_TXT_LINE_BREAK_LONG_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_LEN -# else -# define LV_TXT_LINE_BREAK_LONG_LEN 0 -# endif + #ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_LEN + #define LV_TXT_LINE_BREAK_LONG_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_LEN + #else + #define LV_TXT_LINE_BREAK_LONG_LEN 0 + #endif #endif /*Minimum number of characters in a long word to put on a line before a break. *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ #ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN -# ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN -# define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN -# else -# define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 -# endif + #ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN + #define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN + #else + #define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + #endif #endif /*Minimum number of characters in a long word to put on a line after a break. *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/ #ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN -# ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN -# define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN -# else -# define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 -# endif + #ifdef CONFIG_LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN + #define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN CONFIG_LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN + #else + #define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + #endif #endif /*The control character to use for signalling text recoloring.*/ #ifndef LV_TXT_COLOR_CMD -# ifdef CONFIG_LV_TXT_COLOR_CMD -# define LV_TXT_COLOR_CMD CONFIG_LV_TXT_COLOR_CMD -# else -# define LV_TXT_COLOR_CMD "#" -# endif + #ifdef CONFIG_LV_TXT_COLOR_CMD + #define LV_TXT_COLOR_CMD CONFIG_LV_TXT_COLOR_CMD + #else + #define LV_TXT_COLOR_CMD "#" + #endif #endif /*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts. *The direction will be processed according to the Unicode Bidirectional Algorithm: *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/ #ifndef LV_USE_BIDI -# ifdef CONFIG_LV_USE_BIDI -# define LV_USE_BIDI CONFIG_LV_USE_BIDI -# else -# define LV_USE_BIDI 0 -# endif + #ifdef CONFIG_LV_USE_BIDI + #define LV_USE_BIDI CONFIG_LV_USE_BIDI + #else + #define LV_USE_BIDI 0 + #endif #endif #if LV_USE_BIDI -/*Set the default direction. Supported values: - *`LV_BASE_DIR_LTR` Left-to-Right - *`LV_BASE_DIR_RTL` Right-to-Left - *`LV_BASE_DIR_AUTO` detect texts base direction*/ -#ifndef LV_BIDI_BASE_DIR_DEF -# ifdef CONFIG_LV_BIDI_BASE_DIR_DEF -# define LV_BIDI_BASE_DIR_DEF CONFIG_LV_BIDI_BASE_DIR_DEF -# else -# define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO -# endif -#endif + /*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect texts base direction*/ + #ifndef LV_BIDI_BASE_DIR_DEF + #ifdef CONFIG_LV_BIDI_BASE_DIR_DEF + #define LV_BIDI_BASE_DIR_DEF CONFIG_LV_BIDI_BASE_DIR_DEF + #else + #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO + #endif + #endif #endif /*Enable Arabic/Persian processing *In these languages characters should be replaced with an other form based on their position in the text*/ #ifndef LV_USE_ARABIC_PERSIAN_CHARS -# ifdef CONFIG_LV_USE_ARABIC_PERSIAN_CHARS -# define LV_USE_ARABIC_PERSIAN_CHARS CONFIG_LV_USE_ARABIC_PERSIAN_CHARS -# else -# define LV_USE_ARABIC_PERSIAN_CHARS 0 -# endif + #ifdef CONFIG_LV_USE_ARABIC_PERSIAN_CHARS + #define LV_USE_ARABIC_PERSIAN_CHARS CONFIG_LV_USE_ARABIC_PERSIAN_CHARS + #else + #define LV_USE_ARABIC_PERSIAN_CHARS 0 + #endif #endif /*================== @@ -1199,209 +1345,225 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/ #ifndef LV_USE_ARC -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_ARC -# define LV_USE_ARC CONFIG_LV_USE_ARC -# else -# define LV_USE_ARC 0 -# endif -# else -# define LV_USE_ARC 1 -# endif -#endif - -#ifndef LV_USE_ANIMIMG -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_ANIMIMG -# define LV_USE_ANIMIMG CONFIG_LV_USE_ANIMIMG -# else -# define LV_USE_ANIMIMG 0 -# endif -# else -# define LV_USE_ANIMIMG 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_ARC + #define LV_USE_ARC CONFIG_LV_USE_ARC + #else + #define LV_USE_ARC 0 + #endif + #else + #define LV_USE_ARC 1 + #endif #endif #ifndef LV_USE_BAR -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_BAR -# define LV_USE_BAR CONFIG_LV_USE_BAR -# else -# define LV_USE_BAR 0 -# endif -# else -# define LV_USE_BAR 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_BAR + #define LV_USE_BAR CONFIG_LV_USE_BAR + #else + #define LV_USE_BAR 0 + #endif + #else + #define LV_USE_BAR 1 + #endif #endif #ifndef LV_USE_BTN -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_BTN -# define LV_USE_BTN CONFIG_LV_USE_BTN -# else -# define LV_USE_BTN 0 -# endif -# else -# define LV_USE_BTN 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_BTN + #define LV_USE_BTN CONFIG_LV_USE_BTN + #else + #define LV_USE_BTN 0 + #endif + #else + #define LV_USE_BTN 1 + #endif #endif #ifndef LV_USE_BTNMATRIX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_BTNMATRIX -# define LV_USE_BTNMATRIX CONFIG_LV_USE_BTNMATRIX -# else -# define LV_USE_BTNMATRIX 0 -# endif -# else -# define LV_USE_BTNMATRIX 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_BTNMATRIX + #define LV_USE_BTNMATRIX CONFIG_LV_USE_BTNMATRIX + #else + #define LV_USE_BTNMATRIX 0 + #endif + #else + #define LV_USE_BTNMATRIX 1 + #endif #endif #ifndef LV_USE_CANVAS -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CANVAS -# define LV_USE_CANVAS CONFIG_LV_USE_CANVAS -# else -# define LV_USE_CANVAS 0 -# endif -# else -# define LV_USE_CANVAS 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CANVAS + #define LV_USE_CANVAS CONFIG_LV_USE_CANVAS + #else + #define LV_USE_CANVAS 0 + #endif + #else + #define LV_USE_CANVAS 1 + #endif #endif #ifndef LV_USE_CHECKBOX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CHECKBOX -# define LV_USE_CHECKBOX CONFIG_LV_USE_CHECKBOX -# else -# define LV_USE_CHECKBOX 0 -# endif -# else -# define LV_USE_CHECKBOX 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CHECKBOX + #define LV_USE_CHECKBOX CONFIG_LV_USE_CHECKBOX + #else + #define LV_USE_CHECKBOX 0 + #endif + #else + #define LV_USE_CHECKBOX 1 + #endif #endif #ifndef LV_USE_DROPDOWN -# ifdef CONFIG_LV_USE_DROPDOWN -# define LV_USE_DROPDOWN CONFIG_LV_USE_DROPDOWN -# else -# define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_DROPDOWN + #define LV_USE_DROPDOWN CONFIG_LV_USE_DROPDOWN + #else + #define LV_USE_DROPDOWN 0 + #endif + #else + #define LV_USE_DROPDOWN 1 /*Requires: lv_label*/ + #endif #endif #ifndef LV_USE_IMG -# ifdef CONFIG_LV_USE_IMG -# define LV_USE_IMG CONFIG_LV_USE_IMG -# else -# define LV_USE_IMG 1 /*Requires: lv_label*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_IMG + #define LV_USE_IMG CONFIG_LV_USE_IMG + #else + #define LV_USE_IMG 0 + #endif + #else + #define LV_USE_IMG 1 /*Requires: lv_label*/ + #endif #endif #ifndef LV_USE_LABEL -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_LABEL -# define LV_USE_LABEL CONFIG_LV_USE_LABEL -# else -# define LV_USE_LABEL 0 -# endif -# else -# define LV_USE_LABEL 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_LABEL + #define LV_USE_LABEL CONFIG_LV_USE_LABEL + #else + #define LV_USE_LABEL 0 + #endif + #else + #define LV_USE_LABEL 1 + #endif #endif #if LV_USE_LABEL -#ifndef LV_LABEL_TEXT_SELECTION -# ifdef CONFIG_LV_LABEL_TEXT_SELECTION -# define LV_LABEL_TEXT_SELECTION CONFIG_LV_LABEL_TEXT_SELECTION -# else -# define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ -# endif -#endif -#ifndef LV_LABEL_LONG_TXT_HINT -# ifdef CONFIG_LV_LABEL_LONG_TXT_HINT -# define LV_LABEL_LONG_TXT_HINT CONFIG_LV_LABEL_LONG_TXT_HINT -# else -# define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ -# endif -#endif + #ifndef LV_LABEL_TEXT_SELECTION + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LABEL_TEXT_SELECTION + #define LV_LABEL_TEXT_SELECTION CONFIG_LV_LABEL_TEXT_SELECTION + #else + #define LV_LABEL_TEXT_SELECTION 0 + #endif + #else + #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/ + #endif + #endif + #ifndef LV_LABEL_LONG_TXT_HINT + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_LABEL_LONG_TXT_HINT + #define LV_LABEL_LONG_TXT_HINT CONFIG_LV_LABEL_LONG_TXT_HINT + #else + #define LV_LABEL_LONG_TXT_HINT 0 + #endif + #else + #define LV_LABEL_LONG_TXT_HINT 1 /*Store some extra info in labels to speed up drawing of very long texts*/ + #endif + #endif #endif #ifndef LV_USE_LINE -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_LINE -# define LV_USE_LINE CONFIG_LV_USE_LINE -# else -# define LV_USE_LINE 0 -# endif -# else -# define LV_USE_LINE 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_LINE + #define LV_USE_LINE CONFIG_LV_USE_LINE + #else + #define LV_USE_LINE 0 + #endif + #else + #define LV_USE_LINE 1 + #endif #endif #ifndef LV_USE_ROLLER -# ifdef CONFIG_LV_USE_ROLLER -# define LV_USE_ROLLER CONFIG_LV_USE_ROLLER -# else -# define LV_USE_ROLLER 1 /*Requires: lv_label*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_ROLLER + #define LV_USE_ROLLER CONFIG_LV_USE_ROLLER + #else + #define LV_USE_ROLLER 0 + #endif + #else + #define LV_USE_ROLLER 1 /*Requires: lv_label*/ + #endif #endif #if LV_USE_ROLLER -#ifndef LV_ROLLER_INF_PAGES -# ifdef CONFIG_LV_ROLLER_INF_PAGES -# define LV_ROLLER_INF_PAGES CONFIG_LV_ROLLER_INF_PAGES -# else -# define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/ -# endif -#endif + #ifndef LV_ROLLER_INF_PAGES + #ifdef CONFIG_LV_ROLLER_INF_PAGES + #define LV_ROLLER_INF_PAGES CONFIG_LV_ROLLER_INF_PAGES + #else + #define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/ + #endif + #endif #endif #ifndef LV_USE_SLIDER -# ifdef CONFIG_LV_USE_SLIDER -# define LV_USE_SLIDER CONFIG_LV_USE_SLIDER -# else -# define LV_USE_SLIDER 1 /*Requires: lv_bar*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_SLIDER + #define LV_USE_SLIDER CONFIG_LV_USE_SLIDER + #else + #define LV_USE_SLIDER 0 + #endif + #else + #define LV_USE_SLIDER 1 /*Requires: lv_bar*/ + #endif #endif #ifndef LV_USE_SWITCH -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_SWITCH -# define LV_USE_SWITCH CONFIG_LV_USE_SWITCH -# else -# define LV_USE_SWITCH 0 -# endif -# else -# define LV_USE_SWITCH 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_SWITCH + #define LV_USE_SWITCH CONFIG_LV_USE_SWITCH + #else + #define LV_USE_SWITCH 0 + #endif + #else + #define LV_USE_SWITCH 1 + #endif #endif #ifndef LV_USE_TEXTAREA -# ifdef CONFIG_LV_USE_TEXTAREA -# define LV_USE_TEXTAREA CONFIG_LV_USE_TEXTAREA -# else -# define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_TEXTAREA + #define LV_USE_TEXTAREA CONFIG_LV_USE_TEXTAREA + #else + #define LV_USE_TEXTAREA 0 + #endif + #else + #define LV_USE_TEXTAREA 1 /*Requires: lv_label*/ + #endif #endif #if LV_USE_TEXTAREA != 0 -#ifndef LV_TEXTAREA_DEF_PWD_SHOW_TIME -# ifdef CONFIG_LV_TEXTAREA_DEF_PWD_SHOW_TIME -# define LV_TEXTAREA_DEF_PWD_SHOW_TIME CONFIG_LV_TEXTAREA_DEF_PWD_SHOW_TIME -# else -# define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ -# endif -#endif + #ifndef LV_TEXTAREA_DEF_PWD_SHOW_TIME + #ifdef CONFIG_LV_TEXTAREA_DEF_PWD_SHOW_TIME + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME CONFIG_LV_TEXTAREA_DEF_PWD_SHOW_TIME + #else + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /*ms*/ + #endif + #endif #endif #ifndef LV_USE_TABLE -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_TABLE -# define LV_USE_TABLE CONFIG_LV_USE_TABLE -# else -# define LV_USE_TABLE 0 -# endif -# else -# define LV_USE_TABLE 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_TABLE + #define LV_USE_TABLE CONFIG_LV_USE_TABLE + #else + #define LV_USE_TABLE 0 + #endif + #else + #define LV_USE_TABLE 1 + #endif #endif /*================== @@ -1411,250 +1573,274 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*----------- * Widgets *----------*/ +#ifndef LV_USE_ANIMIMG + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_ANIMIMG + #define LV_USE_ANIMIMG CONFIG_LV_USE_ANIMIMG + #else + #define LV_USE_ANIMIMG 0 + #endif + #else + #define LV_USE_ANIMIMG 1 + #endif +#endif + #ifndef LV_USE_CALENDAR -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CALENDAR -# define LV_USE_CALENDAR CONFIG_LV_USE_CALENDAR -# else -# define LV_USE_CALENDAR 0 -# endif -# else -# define LV_USE_CALENDAR 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CALENDAR + #define LV_USE_CALENDAR CONFIG_LV_USE_CALENDAR + #else + #define LV_USE_CALENDAR 0 + #endif + #else + #define LV_USE_CALENDAR 1 + #endif #endif #if LV_USE_CALENDAR -#ifndef LV_CALENDAR_WEEK_STARTS_MONDAY -# ifdef CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY -# define LV_CALENDAR_WEEK_STARTS_MONDAY CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY -# else -# define LV_CALENDAR_WEEK_STARTS_MONDAY 0 -# endif -#endif -# if LV_CALENDAR_WEEK_STARTS_MONDAY -#ifndef LV_CALENDAR_DEFAULT_DAY_NAMES -# ifdef CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES -# define LV_CALENDAR_DEFAULT_DAY_NAMES CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES -# else -# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} -# endif -#endif -# else -#ifndef LV_CALENDAR_DEFAULT_DAY_NAMES -# ifdef CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES -# define LV_CALENDAR_DEFAULT_DAY_NAMES CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES -# else -# define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} -# endif -#endif -# endif + #ifndef LV_CALENDAR_WEEK_STARTS_MONDAY + #ifdef CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY + #define LV_CALENDAR_WEEK_STARTS_MONDAY CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY + #else + #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 + #endif + #endif + #if LV_CALENDAR_WEEK_STARTS_MONDAY + #ifndef LV_CALENDAR_DEFAULT_DAY_NAMES + #ifdef CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES + #define LV_CALENDAR_DEFAULT_DAY_NAMES CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + #endif + #endif + #else + #ifndef LV_CALENDAR_DEFAULT_DAY_NAMES + #ifdef CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES + #define LV_CALENDAR_DEFAULT_DAY_NAMES CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} + #endif + #endif + #endif -#ifndef LV_CALENDAR_DEFAULT_MONTH_NAMES -# ifdef CONFIG_LV_CALENDAR_DEFAULT_MONTH_NAMES -# define LV_CALENDAR_DEFAULT_MONTH_NAMES CONFIG_LV_CALENDAR_DEFAULT_MONTH_NAMES -# else -# define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} -# endif -#endif -#ifndef LV_USE_CALENDAR_HEADER_ARROW -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CALENDAR_HEADER_ARROW -# define LV_USE_CALENDAR_HEADER_ARROW CONFIG_LV_USE_CALENDAR_HEADER_ARROW -# else -# define LV_USE_CALENDAR_HEADER_ARROW 0 -# endif -# else -# define LV_USE_CALENDAR_HEADER_ARROW 1 -# endif -#endif -#ifndef LV_USE_CALENDAR_HEADER_DROPDOWN -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN -# define LV_USE_CALENDAR_HEADER_DROPDOWN CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN -# else -# define LV_USE_CALENDAR_HEADER_DROPDOWN 0 -# endif -# else -# define LV_USE_CALENDAR_HEADER_DROPDOWN 1 -# endif -#endif + #ifndef LV_CALENDAR_DEFAULT_MONTH_NAMES + #ifdef CONFIG_LV_CALENDAR_DEFAULT_MONTH_NAMES + #define LV_CALENDAR_DEFAULT_MONTH_NAMES CONFIG_LV_CALENDAR_DEFAULT_MONTH_NAMES + #else + #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} + #endif + #endif + #ifndef LV_USE_CALENDAR_HEADER_ARROW + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CALENDAR_HEADER_ARROW + #define LV_USE_CALENDAR_HEADER_ARROW CONFIG_LV_USE_CALENDAR_HEADER_ARROW + #else + #define LV_USE_CALENDAR_HEADER_ARROW 0 + #endif + #else + #define LV_USE_CALENDAR_HEADER_ARROW 1 + #endif + #endif + #ifndef LV_USE_CALENDAR_HEADER_DROPDOWN + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN + #define LV_USE_CALENDAR_HEADER_DROPDOWN CONFIG_LV_USE_CALENDAR_HEADER_DROPDOWN + #else + #define LV_USE_CALENDAR_HEADER_DROPDOWN 0 + #endif + #else + #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 + #endif + #endif #endif /*LV_USE_CALENDAR*/ #ifndef LV_USE_CHART -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_CHART -# define LV_USE_CHART CONFIG_LV_USE_CHART -# else -# define LV_USE_CHART 0 -# endif -# else -# define LV_USE_CHART 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_CHART + #define LV_USE_CHART CONFIG_LV_USE_CHART + #else + #define LV_USE_CHART 0 + #endif + #else + #define LV_USE_CHART 1 + #endif #endif #ifndef LV_USE_COLORWHEEL -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_COLORWHEEL -# define LV_USE_COLORWHEEL CONFIG_LV_USE_COLORWHEEL -# else -# define LV_USE_COLORWHEEL 0 -# endif -# else -# define LV_USE_COLORWHEEL 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_COLORWHEEL + #define LV_USE_COLORWHEEL CONFIG_LV_USE_COLORWHEEL + #else + #define LV_USE_COLORWHEEL 0 + #endif + #else + #define LV_USE_COLORWHEEL 1 + #endif #endif #ifndef LV_USE_IMGBTN -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_IMGBTN -# define LV_USE_IMGBTN CONFIG_LV_USE_IMGBTN -# else -# define LV_USE_IMGBTN 0 -# endif -# else -# define LV_USE_IMGBTN 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_IMGBTN + #define LV_USE_IMGBTN CONFIG_LV_USE_IMGBTN + #else + #define LV_USE_IMGBTN 0 + #endif + #else + #define LV_USE_IMGBTN 1 + #endif #endif #ifndef LV_USE_KEYBOARD -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_KEYBOARD -# define LV_USE_KEYBOARD CONFIG_LV_USE_KEYBOARD -# else -# define LV_USE_KEYBOARD 0 -# endif -# else -# define LV_USE_KEYBOARD 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_KEYBOARD + #define LV_USE_KEYBOARD CONFIG_LV_USE_KEYBOARD + #else + #define LV_USE_KEYBOARD 0 + #endif + #else + #define LV_USE_KEYBOARD 1 + #endif #endif #ifndef LV_USE_LED -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_LED -# define LV_USE_LED CONFIG_LV_USE_LED -# else -# define LV_USE_LED 0 -# endif -# else -# define LV_USE_LED 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_LED + #define LV_USE_LED CONFIG_LV_USE_LED + #else + #define LV_USE_LED 0 + #endif + #else + #define LV_USE_LED 1 + #endif #endif #ifndef LV_USE_LIST -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_LIST -# define LV_USE_LIST CONFIG_LV_USE_LIST -# else -# define LV_USE_LIST 0 -# endif -# else -# define LV_USE_LIST 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_LIST + #define LV_USE_LIST CONFIG_LV_USE_LIST + #else + #define LV_USE_LIST 0 + #endif + #else + #define LV_USE_LIST 1 + #endif +#endif + +#ifndef LV_USE_MENU + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_MENU + #define LV_USE_MENU CONFIG_LV_USE_MENU + #else + #define LV_USE_MENU 0 + #endif + #else + #define LV_USE_MENU 1 + #endif #endif #ifndef LV_USE_METER -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_METER -# define LV_USE_METER CONFIG_LV_USE_METER -# else -# define LV_USE_METER 0 -# endif -# else -# define LV_USE_METER 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_METER + #define LV_USE_METER CONFIG_LV_USE_METER + #else + #define LV_USE_METER 0 + #endif + #else + #define LV_USE_METER 1 + #endif #endif #ifndef LV_USE_MSGBOX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_MSGBOX -# define LV_USE_MSGBOX CONFIG_LV_USE_MSGBOX -# else -# define LV_USE_MSGBOX 0 -# endif -# else -# define LV_USE_MSGBOX 1 -# endif -#endif - -#ifndef LV_USE_SPINBOX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_SPINBOX -# define LV_USE_SPINBOX CONFIG_LV_USE_SPINBOX -# else -# define LV_USE_SPINBOX 0 -# endif -# else -# define LV_USE_SPINBOX 1 -# endif -#endif - -#ifndef LV_USE_SPINNER -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_SPINNER -# define LV_USE_SPINNER CONFIG_LV_USE_SPINNER -# else -# define LV_USE_SPINNER 0 -# endif -# else -# define LV_USE_SPINNER 1 -# endif -#endif - -#ifndef LV_USE_TABVIEW -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_TABVIEW -# define LV_USE_TABVIEW CONFIG_LV_USE_TABVIEW -# else -# define LV_USE_TABVIEW 0 -# endif -# else -# define LV_USE_TABVIEW 1 -# endif -#endif - -#ifndef LV_USE_TILEVIEW -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_TILEVIEW -# define LV_USE_TILEVIEW CONFIG_LV_USE_TILEVIEW -# else -# define LV_USE_TILEVIEW 0 -# endif -# else -# define LV_USE_TILEVIEW 1 -# endif -#endif - -#ifndef LV_USE_WIN -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_WIN -# define LV_USE_WIN CONFIG_LV_USE_WIN -# else -# define LV_USE_WIN 0 -# endif -# else -# define LV_USE_WIN 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_MSGBOX + #define LV_USE_MSGBOX CONFIG_LV_USE_MSGBOX + #else + #define LV_USE_MSGBOX 0 + #endif + #else + #define LV_USE_MSGBOX 1 + #endif #endif #ifndef LV_USE_SPAN -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_SPAN -# define LV_USE_SPAN CONFIG_LV_USE_SPAN -# else -# define LV_USE_SPAN 0 -# endif -# else -# define LV_USE_SPAN 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_SPAN + #define LV_USE_SPAN CONFIG_LV_USE_SPAN + #else + #define LV_USE_SPAN 0 + #endif + #else + #define LV_USE_SPAN 1 + #endif #endif #if LV_USE_SPAN -/*A line text can contain maximum num of span descriptor */ -#ifndef LV_SPAN_SNIPPET_STACK_SIZE -# ifdef CONFIG_LV_SPAN_SNIPPET_STACK_SIZE -# define LV_SPAN_SNIPPET_STACK_SIZE CONFIG_LV_SPAN_SNIPPET_STACK_SIZE -# else -# define LV_SPAN_SNIPPET_STACK_SIZE 64 -# endif + /*A line text can contain maximum num of span descriptor */ + #ifndef LV_SPAN_SNIPPET_STACK_SIZE + #ifdef CONFIG_LV_SPAN_SNIPPET_STACK_SIZE + #define LV_SPAN_SNIPPET_STACK_SIZE CONFIG_LV_SPAN_SNIPPET_STACK_SIZE + #else + #define LV_SPAN_SNIPPET_STACK_SIZE 64 + #endif + #endif #endif + +#ifndef LV_USE_SPINBOX + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_SPINBOX + #define LV_USE_SPINBOX CONFIG_LV_USE_SPINBOX + #else + #define LV_USE_SPINBOX 0 + #endif + #else + #define LV_USE_SPINBOX 1 + #endif +#endif + +#ifndef LV_USE_SPINNER + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_SPINNER + #define LV_USE_SPINNER CONFIG_LV_USE_SPINNER + #else + #define LV_USE_SPINNER 0 + #endif + #else + #define LV_USE_SPINNER 1 + #endif +#endif + +#ifndef LV_USE_TABVIEW + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_TABVIEW + #define LV_USE_TABVIEW CONFIG_LV_USE_TABVIEW + #else + #define LV_USE_TABVIEW 0 + #endif + #else + #define LV_USE_TABVIEW 1 + #endif +#endif + +#ifndef LV_USE_TILEVIEW + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_TILEVIEW + #define LV_USE_TILEVIEW CONFIG_LV_USE_TILEVIEW + #else + #define LV_USE_TILEVIEW 0 + #endif + #else + #define LV_USE_TILEVIEW 1 + #endif +#endif + +#ifndef LV_USE_WIN + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_WIN + #define LV_USE_WIN CONFIG_LV_USE_WIN + #else + #define LV_USE_WIN 0 + #endif + #else + #define LV_USE_WIN 1 + #endif #endif /*----------- @@ -1663,74 +1849,74 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*A simple, impressive and very complete theme*/ #ifndef LV_USE_THEME_DEFAULT -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_THEME_DEFAULT -# define LV_USE_THEME_DEFAULT CONFIG_LV_USE_THEME_DEFAULT -# else -# define LV_USE_THEME_DEFAULT 0 -# endif -# else -# define LV_USE_THEME_DEFAULT 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_THEME_DEFAULT + #define LV_USE_THEME_DEFAULT CONFIG_LV_USE_THEME_DEFAULT + #else + #define LV_USE_THEME_DEFAULT 0 + #endif + #else + #define LV_USE_THEME_DEFAULT 1 + #endif #endif #if LV_USE_THEME_DEFAULT -/*0: Light mode; 1: Dark mode*/ -#ifndef LV_THEME_DEFAULT_DARK -# ifdef CONFIG_LV_THEME_DEFAULT_DARK -# define LV_THEME_DEFAULT_DARK CONFIG_LV_THEME_DEFAULT_DARK -# else -# define LV_THEME_DEFAULT_DARK 0 -# endif -#endif + /*0: Light mode; 1: Dark mode*/ + #ifndef LV_THEME_DEFAULT_DARK + #ifdef CONFIG_LV_THEME_DEFAULT_DARK + #define LV_THEME_DEFAULT_DARK CONFIG_LV_THEME_DEFAULT_DARK + #else + #define LV_THEME_DEFAULT_DARK 0 + #endif + #endif -/*1: Enable grow on press*/ -#ifndef LV_THEME_DEFAULT_GROW -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_THEME_DEFAULT_GROW -# define LV_THEME_DEFAULT_GROW CONFIG_LV_THEME_DEFAULT_GROW -# else -# define LV_THEME_DEFAULT_GROW 0 -# endif -# else -# define LV_THEME_DEFAULT_GROW 1 -# endif -#endif + /*1: Enable grow on press*/ + #ifndef LV_THEME_DEFAULT_GROW + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_THEME_DEFAULT_GROW + #define LV_THEME_DEFAULT_GROW CONFIG_LV_THEME_DEFAULT_GROW + #else + #define LV_THEME_DEFAULT_GROW 0 + #endif + #else + #define LV_THEME_DEFAULT_GROW 1 + #endif + #endif -/*Default transition time in [ms]*/ -#ifndef LV_THEME_DEFAULT_TRANSITION_TIME -# ifdef CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME -# define LV_THEME_DEFAULT_TRANSITION_TIME CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME -# else -# define LV_THEME_DEFAULT_TRANSITION_TIME 80 -# endif -#endif + /*Default transition time in [ms]*/ + #ifndef LV_THEME_DEFAULT_TRANSITION_TIME + #ifdef CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME + #define LV_THEME_DEFAULT_TRANSITION_TIME CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME + #else + #define LV_THEME_DEFAULT_TRANSITION_TIME 80 + #endif + #endif #endif /*LV_USE_THEME_DEFAULT*/ /*A very simple theme that is a good starting point for a custom theme*/ #ifndef LV_USE_THEME_BASIC -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_THEME_BASIC -# define LV_USE_THEME_BASIC CONFIG_LV_USE_THEME_BASIC -# else -# define LV_USE_THEME_BASIC 0 -# endif -# else -# define LV_USE_THEME_BASIC 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_THEME_BASIC + #define LV_USE_THEME_BASIC CONFIG_LV_USE_THEME_BASIC + #else + #define LV_USE_THEME_BASIC 0 + #endif + #else + #define LV_USE_THEME_BASIC 1 + #endif #endif /*A theme designed for monochrome displays*/ #ifndef LV_USE_THEME_MONO -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_THEME_MONO -# define LV_USE_THEME_MONO CONFIG_LV_USE_THEME_MONO -# else -# define LV_USE_THEME_MONO 0 -# endif -# else -# define LV_USE_THEME_MONO 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_THEME_MONO + #define LV_USE_THEME_MONO CONFIG_LV_USE_THEME_MONO + #else + #define LV_USE_THEME_MONO 0 + #endif + #else + #define LV_USE_THEME_MONO 1 + #endif #endif /*----------- @@ -1739,143 +1925,277 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*A layout similar to Flexbox in CSS.*/ #ifndef LV_USE_FLEX -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_FLEX -# define LV_USE_FLEX CONFIG_LV_USE_FLEX -# else -# define LV_USE_FLEX 0 -# endif -# else -# define LV_USE_FLEX 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_FLEX + #define LV_USE_FLEX CONFIG_LV_USE_FLEX + #else + #define LV_USE_FLEX 0 + #endif + #else + #define LV_USE_FLEX 1 + #endif #endif /*A layout similar to Grid in CSS.*/ #ifndef LV_USE_GRID -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_GRID -# define LV_USE_GRID CONFIG_LV_USE_GRID -# else -# define LV_USE_GRID 0 -# endif -# else -# define LV_USE_GRID 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_USE_GRID + #define LV_USE_GRID CONFIG_LV_USE_GRID + #else + #define LV_USE_GRID 0 + #endif + #else + #define LV_USE_GRID 1 + #endif #endif /*--------------------- * 3rd party libraries *--------------------*/ -/*File system interfaces for common APIs - *To enable set a driver letter for that API*/ +/*File system interfaces for common APIs */ + +/*API for fopen, fread, etc*/ #ifndef LV_USE_FS_STDIO -# ifdef CONFIG_LV_USE_FS_STDIO -# define LV_USE_FS_STDIO CONFIG_LV_USE_FS_STDIO -# else -# define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/ -# endif + #ifdef CONFIG_LV_USE_FS_STDIO + #define LV_USE_FS_STDIO CONFIG_LV_USE_FS_STDIO + #else + #define LV_USE_FS_STDIO 0 + #endif +#endif +#if LV_USE_FS_STDIO + #ifndef LV_FS_STDIO_LETTER + #ifdef CONFIG_LV_FS_STDIO_LETTER + #define LV_FS_STDIO_LETTER CONFIG_LV_FS_STDIO_LETTER + #else + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_STDIO_PATH + #ifdef CONFIG_LV_FS_STDIO_PATH + #define LV_FS_STDIO_PATH CONFIG_LV_FS_STDIO_PATH + #else + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #endif + #endif + #ifndef LV_FS_STDIO_CACHE_SIZE + #ifdef CONFIG_LV_FS_STDIO_CACHE_SIZE + #define LV_FS_STDIO_CACHE_SIZE CONFIG_LV_FS_STDIO_CACHE_SIZE + #else + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif + #endif #endif -//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*API for open, read, etc*/ #ifndef LV_USE_FS_POSIX -# ifdef CONFIG_LV_USE_FS_POSIX -# define LV_USE_FS_POSIX CONFIG_LV_USE_FS_POSIX -# else -# define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/ -# endif + #ifdef CONFIG_LV_USE_FS_POSIX + #define LV_USE_FS_POSIX CONFIG_LV_USE_FS_POSIX + #else + #define LV_USE_FS_POSIX 0 + #endif +#endif +#if LV_USE_FS_POSIX + #ifndef LV_FS_POSIX_LETTER + #ifdef CONFIG_LV_FS_POSIX_LETTER + #define LV_FS_POSIX_LETTER CONFIG_LV_FS_POSIX_LETTER + #else + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_POSIX_PATH + #ifdef CONFIG_LV_FS_POSIX_PATH + #define LV_FS_POSIX_PATH CONFIG_LV_FS_POSIX_PATH + #else + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #endif + #endif + #ifndef LV_FS_POSIX_CACHE_SIZE + #ifdef CONFIG_LV_FS_POSIX_CACHE_SIZE + #define LV_FS_POSIX_CACHE_SIZE CONFIG_LV_FS_POSIX_CACHE_SIZE + #else + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif + #endif #endif -//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*API for CreateFile, ReadFile, etc*/ #ifndef LV_USE_FS_WIN32 -# ifdef CONFIG_LV_USE_FS_WIN32 -# define LV_USE_FS_WIN32 CONFIG_LV_USE_FS_WIN32 -# else -# define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/ -# endif + #ifdef CONFIG_LV_USE_FS_WIN32 + #define LV_USE_FS_WIN32 CONFIG_LV_USE_FS_WIN32 + #else + #define LV_USE_FS_WIN32 0 + #endif +#endif +#if LV_USE_FS_WIN32 + #ifndef LV_FS_WIN32_LETTER + #ifdef CONFIG_LV_FS_WIN32_LETTER + #define LV_FS_WIN32_LETTER CONFIG_LV_FS_WIN32_LETTER + #else + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_WIN32_PATH + #ifdef CONFIG_LV_FS_WIN32_PATH + #define LV_FS_WIN32_PATH CONFIG_LV_FS_WIN32_PATH + #else + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ + #endif + #endif + #ifndef LV_FS_WIN32_CACHE_SIZE + #ifdef CONFIG_LV_FS_WIN32_CACHE_SIZE + #define LV_FS_WIN32_CACHE_SIZE CONFIG_LV_FS_WIN32_CACHE_SIZE + #else + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif + #endif #endif -//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */ +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ #ifndef LV_USE_FS_FATFS -# ifdef CONFIG_LV_USE_FS_FATFS -# define LV_USE_FS_FATFS CONFIG_LV_USE_FS_FATFS -# else -# define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/ -# endif + #ifdef CONFIG_LV_USE_FS_FATFS + #define LV_USE_FS_FATFS CONFIG_LV_USE_FS_FATFS + #else + #define LV_USE_FS_FATFS 0 + #endif +#endif +#if LV_USE_FS_FATFS + #ifndef LV_FS_FATFS_LETTER + #ifdef CONFIG_LV_FS_FATFS_LETTER + #define LV_FS_FATFS_LETTER CONFIG_LV_FS_FATFS_LETTER + #else + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_FATFS_CACHE_SIZE + #ifdef CONFIG_LV_FS_FATFS_CACHE_SIZE + #define LV_FS_FATFS_CACHE_SIZE CONFIG_LV_FS_FATFS_CACHE_SIZE + #else + #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif + #endif #endif /*PNG decoder library*/ #ifndef LV_USE_PNG -# ifdef CONFIG_LV_USE_PNG -# define LV_USE_PNG CONFIG_LV_USE_PNG -# else -# define LV_USE_PNG 0 -# endif + #ifdef CONFIG_LV_USE_PNG + #define LV_USE_PNG CONFIG_LV_USE_PNG + #else + #define LV_USE_PNG 0 + #endif #endif /*BMP decoder library*/ #ifndef LV_USE_BMP -# ifdef CONFIG_LV_USE_BMP -# define LV_USE_BMP CONFIG_LV_USE_BMP -# else -# define LV_USE_BMP 0 -# endif + #ifdef CONFIG_LV_USE_BMP + #define LV_USE_BMP CONFIG_LV_USE_BMP + #else + #define LV_USE_BMP 0 + #endif #endif /* JPG + split JPG decoder library. * Split JPG is a custom format optimized for embedded systems. */ #ifndef LV_USE_SJPG -# ifdef CONFIG_LV_USE_SJPG -# define LV_USE_SJPG CONFIG_LV_USE_SJPG -# else -# define LV_USE_SJPG 0 -# endif + #ifdef CONFIG_LV_USE_SJPG + #define LV_USE_SJPG CONFIG_LV_USE_SJPG + #else + #define LV_USE_SJPG 0 + #endif #endif /*GIF decoder library*/ #ifndef LV_USE_GIF -# ifdef CONFIG_LV_USE_GIF -# define LV_USE_GIF CONFIG_LV_USE_GIF -# else -# define LV_USE_GIF 0 -# endif + #ifdef CONFIG_LV_USE_GIF + #define LV_USE_GIF CONFIG_LV_USE_GIF + #else + #define LV_USE_GIF 0 + #endif #endif /*QR code library*/ #ifndef LV_USE_QRCODE -# ifdef CONFIG_LV_USE_QRCODE -# define LV_USE_QRCODE CONFIG_LV_USE_QRCODE -# else -# define LV_USE_QRCODE 0 -# endif + #ifdef CONFIG_LV_USE_QRCODE + #define LV_USE_QRCODE CONFIG_LV_USE_QRCODE + #else + #define LV_USE_QRCODE 0 + #endif #endif /*FreeType library*/ #ifndef LV_USE_FREETYPE -# ifdef CONFIG_LV_USE_FREETYPE -# define LV_USE_FREETYPE CONFIG_LV_USE_FREETYPE -# else -# define LV_USE_FREETYPE 0 -# endif + #ifdef CONFIG_LV_USE_FREETYPE + #define LV_USE_FREETYPE CONFIG_LV_USE_FREETYPE + #else + #define LV_USE_FREETYPE 0 + #endif #endif #if LV_USE_FREETYPE -/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/ -#ifndef LV_FREETYPE_CACHE_SIZE -# ifdef CONFIG_LV_FREETYPE_CACHE_SIZE -# define LV_FREETYPE_CACHE_SIZE CONFIG_LV_FREETYPE_CACHE_SIZE -# else -# define LV_FREETYPE_CACHE_SIZE (16 * 1024) -# endif -#endif + /*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/ + #ifndef LV_FREETYPE_CACHE_SIZE + #ifdef CONFIG_LV_FREETYPE_CACHE_SIZE + #define LV_FREETYPE_CACHE_SIZE CONFIG_LV_FREETYPE_CACHE_SIZE + #else + #define LV_FREETYPE_CACHE_SIZE (16 * 1024) + #endif + #endif + #if LV_FREETYPE_CACHE_SIZE >= 0 + /* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */ + /* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */ + /* if font size >= 256, must be configured as image cache */ + #ifndef LV_FREETYPE_SBIT_CACHE + #ifdef CONFIG_LV_FREETYPE_SBIT_CACHE + #define LV_FREETYPE_SBIT_CACHE CONFIG_LV_FREETYPE_SBIT_CACHE + #else + #define LV_FREETYPE_SBIT_CACHE 0 + #endif + #endif + /* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */ + /* (0:use system defaults) */ + #ifndef LV_FREETYPE_CACHE_FT_FACES + #ifdef CONFIG_LV_FREETYPE_CACHE_FT_FACES + #define LV_FREETYPE_CACHE_FT_FACES CONFIG_LV_FREETYPE_CACHE_FT_FACES + #else + #define LV_FREETYPE_CACHE_FT_FACES 0 + #endif + #endif + #ifndef LV_FREETYPE_CACHE_FT_SIZES + #ifdef CONFIG_LV_FREETYPE_CACHE_FT_SIZES + #define LV_FREETYPE_CACHE_FT_SIZES CONFIG_LV_FREETYPE_CACHE_FT_SIZES + #else + #define LV_FREETYPE_CACHE_FT_SIZES 0 + #endif + #endif + #endif #endif /*Rlottie library*/ #ifndef LV_USE_RLOTTIE -# ifdef CONFIG_LV_USE_RLOTTIE -# define LV_USE_RLOTTIE CONFIG_LV_USE_RLOTTIE -# else -# define LV_USE_RLOTTIE 0 -# endif + #ifdef CONFIG_LV_USE_RLOTTIE + #define LV_USE_RLOTTIE CONFIG_LV_USE_RLOTTIE + #else + #define LV_USE_RLOTTIE 0 + #endif +#endif + +/*FFmpeg library for image decoding and playing videos + *Supports all major image formats so do not enable other image decoder with it*/ +#ifndef LV_USE_FFMPEG + #ifdef CONFIG_LV_USE_FFMPEG + #define LV_USE_FFMPEG CONFIG_LV_USE_FFMPEG + #else + #define LV_USE_FFMPEG 0 + #endif +#endif +#if LV_USE_FFMPEG + /*Dump input information to stderr*/ + #ifndef LV_FFMPEG_DUMP_FORMAT + #ifdef CONFIG_LV_FFMPEG_DUMP_FORMAT + #define LV_FFMPEG_DUMP_FORMAT CONFIG_LV_FFMPEG_DUMP_FORMAT + #else + #define LV_FFMPEG_DUMP_FORMAT 0 + #endif + #endif #endif /*----------- @@ -1884,17 +2204,113 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*1: Enable API to take snapshot for object*/ #ifndef LV_USE_SNAPSHOT -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_USE_SNAPSHOT -# define LV_USE_SNAPSHOT CONFIG_LV_USE_SNAPSHOT -# else -# define LV_USE_SNAPSHOT 0 -# endif -# else -# define LV_USE_SNAPSHOT 1 -# endif + #ifdef CONFIG_LV_USE_SNAPSHOT + #define LV_USE_SNAPSHOT CONFIG_LV_USE_SNAPSHOT + #else + #define LV_USE_SNAPSHOT 0 + #endif #endif +/*1: Enable Monkey test*/ +#ifndef LV_USE_MONKEY + #ifdef CONFIG_LV_USE_MONKEY + #define LV_USE_MONKEY CONFIG_LV_USE_MONKEY + #else + #define LV_USE_MONKEY 0 + #endif +#endif + +/*1: Enable grid navigation*/ +#ifndef LV_USE_GRIDNAV + #ifdef CONFIG_LV_USE_GRIDNAV + #define LV_USE_GRIDNAV CONFIG_LV_USE_GRIDNAV + #else + #define LV_USE_GRIDNAV 0 + #endif +#endif + +/*1: Enable lv_obj fragment*/ +#ifndef LV_USE_FRAGMENT + #ifdef CONFIG_LV_USE_FRAGMENT + #define LV_USE_FRAGMENT CONFIG_LV_USE_FRAGMENT + #else + #define LV_USE_FRAGMENT 0 + #endif +#endif + +/*1: Support using images as font in label or span widgets */ +#ifndef LV_USE_IMGFONT + #ifdef CONFIG_LV_USE_IMGFONT + #define LV_USE_IMGFONT CONFIG_LV_USE_IMGFONT + #else + #define LV_USE_IMGFONT 0 + #endif +#endif + +/*1: Enable a published subscriber based messaging system */ +#ifndef LV_USE_MSG + #ifdef CONFIG_LV_USE_MSG + #define LV_USE_MSG CONFIG_LV_USE_MSG + #else + #define LV_USE_MSG 0 + #endif +#endif + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#ifndef LV_USE_IME_PINYIN + #ifdef CONFIG_LV_USE_IME_PINYIN + #define LV_USE_IME_PINYIN CONFIG_LV_USE_IME_PINYIN + #else + #define LV_USE_IME_PINYIN 0 + #endif +#endif +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ + #ifndef LV_IME_PINYIN_USE_DEFAULT_DICT + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_IME_PINYIN_USE_DEFAULT_DICT + #define LV_IME_PINYIN_USE_DEFAULT_DICT CONFIG_LV_IME_PINYIN_USE_DEFAULT_DICT + #else + #define LV_IME_PINYIN_USE_DEFAULT_DICT 0 + #endif + #else + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + #endif + #endif + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #ifndef LV_IME_PINYIN_CAND_TEXT_NUM + #ifdef CONFIG_LV_IME_PINYIN_CAND_TEXT_NUM + #define LV_IME_PINYIN_CAND_TEXT_NUM CONFIG_LV_IME_PINYIN_CAND_TEXT_NUM + #else + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + #endif + #endif + + /*Use 9 key input(k9)*/ + #ifndef LV_IME_PINYIN_USE_K9_MODE + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_IME_PINYIN_USE_K9_MODE + #define LV_IME_PINYIN_USE_K9_MODE CONFIG_LV_IME_PINYIN_USE_K9_MODE + #else + #define LV_IME_PINYIN_USE_K9_MODE 0 + #endif + #else + #define LV_IME_PINYIN_USE_K9_MODE 1 + #endif + #endif + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #ifndef LV_IME_PINYIN_K9_CAND_TEXT_NUM + #ifdef CONFIG_LV_IME_PINYIN_K9_CAND_TEXT_NUM + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM CONFIG_LV_IME_PINYIN_K9_CAND_TEXT_NUM + #else + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif + #endif + #endif // LV_IME_PINYIN_USE_K9_MODE +#endif /*================== * EXAMPLES @@ -1902,15 +2318,120 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*Enable the examples to be built with the library*/ #ifndef LV_BUILD_EXAMPLES -# ifdef _LV_KCONFIG_PRESENT -# ifdef CONFIG_LV_BUILD_EXAMPLES -# define LV_BUILD_EXAMPLES CONFIG_LV_BUILD_EXAMPLES -# else -# define LV_BUILD_EXAMPLES 0 -# endif -# else -# define LV_BUILD_EXAMPLES 1 -# endif + #ifdef _LV_KCONFIG_PRESENT + #ifdef CONFIG_LV_BUILD_EXAMPLES + #define LV_BUILD_EXAMPLES CONFIG_LV_BUILD_EXAMPLES + #else + #define LV_BUILD_EXAMPLES 0 + #endif + #else + #define LV_BUILD_EXAMPLES 1 + #endif +#endif + +/*=================== + * DEMO USAGE + ====================*/ + +/*Show some widget. It might be required to increase `LV_MEM_SIZE` */ +#ifndef LV_USE_DEMO_WIDGETS + #ifdef CONFIG_LV_USE_DEMO_WIDGETS + #define LV_USE_DEMO_WIDGETS CONFIG_LV_USE_DEMO_WIDGETS + #else + #define LV_USE_DEMO_WIDGETS 0 + #endif +#endif +#if LV_USE_DEMO_WIDGETS +#ifndef LV_DEMO_WIDGETS_SLIDESHOW + #ifdef CONFIG_LV_DEMO_WIDGETS_SLIDESHOW + #define LV_DEMO_WIDGETS_SLIDESHOW CONFIG_LV_DEMO_WIDGETS_SLIDESHOW + #else + #define LV_DEMO_WIDGETS_SLIDESHOW 0 + #endif +#endif +#endif + +/*Demonstrate the usage of encoder and keyboard*/ +#ifndef LV_USE_DEMO_KEYPAD_AND_ENCODER + #ifdef CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER + #define LV_USE_DEMO_KEYPAD_AND_ENCODER CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER + #else + #define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 + #endif +#endif + +/*Benchmark your system*/ +#ifndef LV_USE_DEMO_BENCHMARK + #ifdef CONFIG_LV_USE_DEMO_BENCHMARK + #define LV_USE_DEMO_BENCHMARK CONFIG_LV_USE_DEMO_BENCHMARK + #else + #define LV_USE_DEMO_BENCHMARK 0 + #endif +#endif +#if LV_USE_DEMO_BENCHMARK +/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ +#ifndef LV_DEMO_BENCHMARK_RGB565A8 + #ifdef CONFIG_LV_DEMO_BENCHMARK_RGB565A8 + #define LV_DEMO_BENCHMARK_RGB565A8 CONFIG_LV_DEMO_BENCHMARK_RGB565A8 + #else + #define LV_DEMO_BENCHMARK_RGB565A8 0 + #endif +#endif +#endif + +/*Stress test for LVGL*/ +#ifndef LV_USE_DEMO_STRESS + #ifdef CONFIG_LV_USE_DEMO_STRESS + #define LV_USE_DEMO_STRESS CONFIG_LV_USE_DEMO_STRESS + #else + #define LV_USE_DEMO_STRESS 0 + #endif +#endif + +/*Music player demo*/ +#ifndef LV_USE_DEMO_MUSIC + #ifdef CONFIG_LV_USE_DEMO_MUSIC + #define LV_USE_DEMO_MUSIC CONFIG_LV_USE_DEMO_MUSIC + #else + #define LV_USE_DEMO_MUSIC 0 + #endif +#endif +#if LV_USE_DEMO_MUSIC + #ifndef LV_DEMO_MUSIC_SQUARE + #ifdef CONFIG_LV_DEMO_MUSIC_SQUARE + #define LV_DEMO_MUSIC_SQUARE CONFIG_LV_DEMO_MUSIC_SQUARE + #else + #define LV_DEMO_MUSIC_SQUARE 0 + #endif + #endif + #ifndef LV_DEMO_MUSIC_LANDSCAPE + #ifdef CONFIG_LV_DEMO_MUSIC_LANDSCAPE + #define LV_DEMO_MUSIC_LANDSCAPE CONFIG_LV_DEMO_MUSIC_LANDSCAPE + #else + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #endif + #endif + #ifndef LV_DEMO_MUSIC_ROUND + #ifdef CONFIG_LV_DEMO_MUSIC_ROUND + #define LV_DEMO_MUSIC_ROUND CONFIG_LV_DEMO_MUSIC_ROUND + #else + #define LV_DEMO_MUSIC_ROUND 0 + #endif + #endif + #ifndef LV_DEMO_MUSIC_LARGE + #ifdef CONFIG_LV_DEMO_MUSIC_LARGE + #define LV_DEMO_MUSIC_LARGE CONFIG_LV_DEMO_MUSIC_LARGE + #else + #define LV_DEMO_MUSIC_LARGE 0 + #endif + #endif + #ifndef LV_DEMO_MUSIC_AUTO_PLAY + #ifdef CONFIG_LV_DEMO_MUSIC_AUTO_PLAY + #define LV_DEMO_MUSIC_AUTO_PLAY CONFIG_LV_DEMO_MUSIC_AUTO_PLAY + #else + #define LV_DEMO_MUSIC_AUTO_PLAY 0 + #endif + #endif #endif @@ -1923,11 +2444,26 @@ LV_EXPORT_CONST_INT(LV_DPI_DEF); #undef _LV_KCONFIG_PRESENT -/*If running without lv_conf.h add typdesf with default value*/ + +/*Set some defines if a dependency is disabled*/ +#if LV_USE_LOG == 0 + #define LV_LOG_LEVEL LV_LOG_LEVEL_NONE + #define LV_LOG_TRACE_MEM 0 + #define LV_LOG_TRACE_TIMER 0 + #define LV_LOG_TRACE_INDEV 0 + #define LV_LOG_TRACE_DISP_REFR 0 + #define LV_LOG_TRACE_EVENT 0 + #define LV_LOG_TRACE_OBJ_CREATE 0 + #define LV_LOG_TRACE_LAYOUT 0 + #define LV_LOG_TRACE_ANIM 0 +#endif /*LV_USE_LOG*/ + + +/*If running without lv_conf.h add typedefs with default value*/ #ifdef LV_CONF_SKIP -# if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /*Disable warnings for Visual Studio*/ -# define _CRT_SECURE_NO_WARNINGS -# endif + #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /*Disable warnings for Visual Studio*/ + #define _CRT_SECURE_NO_WARNINGS + #endif #endif /*defined(LV_CONF_SKIP)*/ #endif /*LV_CONF_INTERNAL_H*/ diff --git a/lib/libesp32_lvgl/lvgl/src/lv_conf_kconfig.h b/lib/libesp32_lvgl/lvgl/src/lv_conf_kconfig.h index d12803875..7742fe77b 100644 --- a/lib/libesp32_lvgl/lvgl/src/lv_conf_kconfig.h +++ b/lib/libesp32_lvgl/lvgl/src/lv_conf_kconfig.h @@ -41,6 +41,50 @@ extern "C" { # define CONFIG_LV_MEM_SIZE (CONFIG_LV_MEM_SIZE_KILOBYTES * 1024U) #endif +/*------------------ + * MONITOR POSITION + *-----------------*/ + +#ifdef CONFIG_LV_PERF_MONITOR_ALIGN_TOP_LEFT +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_LEFT +#elif defined(CONFIG_LV_USE_PERF_MONITOR_ALIGN_TOP_MID) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_MID +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_TOP_RIGHT) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_TOP_RIGHT +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_LEFT) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_LEFT +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_MID) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_MID +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_BOTTOM_RIGHT) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_LEFT_MID) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_LEFT_MID +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_RIGHT_MID) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_RIGHT_MID +#elif defined(CONFIG_LV_PERF_MONITOR_ALIGN_CENTER) +# define CONFIG_LV_USE_PERF_MONITOR_POS LV_ALIGN_CENTER +#endif + +#ifdef CONFIG_LV_MEM_MONITOR_ALIGN_TOP_LEFT +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_LEFT +#elif defined(CONFIG_LV_USE_MEM_MONITOR_ALIGN_TOP_MID) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_MID +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_TOP_RIGHT) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_TOP_RIGHT +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_LEFT) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_MID) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_MID +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_BOTTOM_RIGHT) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_LEFT_MID) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_LEFT_MID +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_RIGHT_MID) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_RIGHT_MID +#elif defined(CONFIG_LV_MEM_MONITOR_ALIGN_CENTER) +# define CONFIG_LV_USE_MEM_MONITOR_POS LV_ALIGN_CENTER +#endif + /******************** * FONT SELECTION *******************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.c index ecb95840d..4e4253a6e 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.c @@ -147,6 +147,7 @@ bool lv_anim_del(void * var, lv_anim_exec_xcb_t exec_cb) if((a->var == var || var == NULL) && (a->exec_cb == exec_cb || exec_cb == NULL)) { _lv_ll_remove(&LV_GC_ROOT(_lv_anim_ll), a); + if(a->deleted_cb != NULL) a->deleted_cb(a); lv_mem_free(a); anim_mark_list_change(); /*Read by `anim_timer`. It need to know if a delete occurred in the linked list*/ @@ -177,6 +178,11 @@ lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb) return NULL; } +struct _lv_timer_t * lv_anim_get_timer(void) +{ + return _lv_anim_tmr; +} + uint16_t lv_anim_count_running(void) { uint16_t cnt = 0; @@ -429,6 +435,7 @@ static void anim_ready_handler(lv_anim_t * a) /*Call the callback function at the end*/ if(a->ready_cb != NULL) a->ready_cb(a); + if(a->deleted_cb != NULL) a->deleted_cb(a); lv_mem_free(a); } /*If the animation is not deleted then restart it*/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.h index 18317eb38..faef72787 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_anim.h @@ -40,6 +40,7 @@ typedef enum { } lv_anim_enable_t; struct _lv_anim_t; +struct _lv_timer_t; /** Get the current value during an animation*/ typedef int32_t (*lv_anim_path_cb_t)(const struct _lv_anim_t *); @@ -65,12 +66,16 @@ typedef void (*lv_anim_start_cb_t)(struct _lv_anim_t *); /** Callback used when the animation values are relative to get the current value*/ typedef int32_t (*lv_anim_get_value_cb_t)(struct _lv_anim_t *); +/** Callback used when the animation is deleted*/ +typedef void (*lv_anim_deleted_cb_t)(struct _lv_anim_t *); + /** Describes an animation*/ typedef struct _lv_anim_t { void * var; /**ready_cb = ready_cb; } +/** + * Set a function call when the animation is deleted. + * @param a pointer to an initialized `lv_anim_t` variable + * @param deleted_cb a function call when the animation is deleted + */ +static inline void lv_anim_set_deleted_cb(lv_anim_t * a, lv_anim_deleted_cb_t deleted_cb) +{ + a->deleted_cb = deleted_cb; +} + /** * Make the animation to play back to when the forward direction is ready * @param a pointer to an initialized `lv_anim_t` variable @@ -345,6 +360,12 @@ void lv_anim_del_all(void); */ lv_anim_t * lv_anim_get(void * var, lv_anim_exec_xcb_t exec_cb); +/** + * Get global animation refresher timer. + * @return pointer to the animation refresher timer. + */ +struct _lv_timer_t * lv_anim_get_timer(void); + /** * Delete an animation by getting the animated variable from `a`. * Only animations with `exec_cb` will be deleted. diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_area.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_area.c index 7c66a9b33..c0221f7ed 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_area.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_area.c @@ -456,6 +456,59 @@ void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t alig to_align->y2 = to_align->y1 + h - 1; } +#define _LV_TRANSFORM_TRIGO_SHIFT 10 +void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot) +{ + if(angle == 0 && zoom == 256) { + return; + } + + p->x -= pivot->x; + p->y -= pivot->y; + + if(angle == 0) { + p->x = (((int32_t)(p->x) * zoom) >> 8) + pivot->x; + p->y = (((int32_t)(p->y) * zoom) >> 8) + pivot->y; + return; + } + + static int32_t angle_prev = INT32_MIN; + static int32_t sinma; + static int32_t cosma; + if(angle_prev != angle) { + int32_t angle_limited = angle; + if(angle_limited > 3600) angle_limited -= 3600; + if(angle_limited < 0) angle_limited += 3600; + + int32_t angle_low = angle_limited / 10; + int32_t angle_high = angle_low + 1; + int32_t angle_rem = angle_limited - (angle_low * 10); + + int32_t s1 = lv_trigo_sin(angle_low); + int32_t s2 = lv_trigo_sin(angle_high); + + int32_t c1 = lv_trigo_sin(angle_low + 90); + int32_t c2 = lv_trigo_sin(angle_high + 90); + + sinma = (s1 * (10 - angle_rem) + s2 * angle_rem) / 10; + cosma = (c1 * (10 - angle_rem) + c2 * angle_rem) / 10; + sinma = sinma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); + cosma = cosma >> (LV_TRIGO_SHIFT - _LV_TRANSFORM_TRIGO_SHIFT); + angle_prev = angle; + } + int32_t x = p->x; + int32_t y = p->y; + if(zoom == 256) { + p->x = ((cosma * x - sinma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->x; + p->y = ((sinma * x + cosma * y) >> _LV_TRANSFORM_TRIGO_SHIFT) + pivot->y; + } + else { + p->x = (((cosma * x - sinma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->x; + p->y = (((sinma * x + cosma * y) * zoom) >> (_LV_TRANSFORM_TRIGO_SHIFT + 8)) + pivot->y; + } +} + + /********************** * STATIC FUNCTIONS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_area.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_area.h index 542d86bc5..137931a29 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_area.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_area.h @@ -236,6 +236,8 @@ bool _lv_area_is_equal(const lv_area_t * a, const lv_area_t * b); */ void lv_area_align(const lv_area_t * base, lv_area_t * to_align, lv_align_t align, lv_coord_t ofs_x, lv_coord_t ofs_y); +void lv_point_transform(lv_point_t * p, int32_t angle, int32_t zoom, const lv_point_t * pivot); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_async.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_async.c index 45a043154..c4941e811 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_async.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_async.c @@ -65,6 +65,33 @@ lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data) return LV_RES_OK; } +lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data) +{ + lv_timer_t * timer = lv_timer_get_next(NULL); + lv_res_t res = LV_RES_INV; + + while(timer != NULL) { + /*Find the next timer node*/ + lv_timer_t * timer_next = lv_timer_get_next(timer); + + /*Find async timer callback*/ + if(timer->timer_cb == lv_async_timer_cb) { + lv_async_info_t * info = (lv_async_info_t *)timer->user_data; + + /*Match user function callback and user data*/ + if(info->cb == async_xcb && info->user_data == user_data) { + lv_timer_del(timer); + lv_mem_free(info); + res = LV_RES_OK; + } + } + + timer = timer_next; + } + + return res; +} + /********************** * STATIC FUNCTIONS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_async.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_async.h index 3e6cb638d..4ad5756d9 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_async.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_async.h @@ -43,6 +43,13 @@ typedef void (*lv_async_cb_t)(void *); */ lv_res_t lv_async_call(lv_async_cb_t async_xcb, void * user_data); +/** + * Cancel an asynchronous function call + * @param async_xcb a callback which is the task itself. + * @param user_data custom parameter + */ +lv_res_t lv_async_call_cancel(lv_async_cb_t async_xcb, void * user_data); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_bidi.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_bidi.c index b207f9d9e..3dc3ce7f1 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_bidi.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_bidi.c @@ -339,7 +339,7 @@ static uint32_t lv_bidi_get_next_paragraph(const char * txt) /** * Get the direction of a character - * @param letter an Unicode character + * @param letter a Unicode character * @return `LV_BASE_DIR_RTL/LTR/WEAK/NEUTRAL` */ static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter) @@ -352,7 +352,7 @@ static lv_base_dir_t lv_bidi_get_letter_dir(uint32_t letter) } /** * Tell whether a character is weak or not - * @param letter an Unicode character + * @param letter a Unicode character * @return true/false */ static bool lv_bidi_letter_is_weak(uint32_t letter) @@ -371,7 +371,7 @@ static bool lv_bidi_letter_is_weak(uint32_t letter) } /** * Tell whether a character is RTL or not - * @param letter an Unicode character + * @param letter a Unicode character * @return true/false */ static bool lv_bidi_letter_is_rtl(uint32_t letter) @@ -389,7 +389,7 @@ static bool lv_bidi_letter_is_rtl(uint32_t letter) /** * Tell whether a character is neutral or not - * @param letter an Unicode character + * @param letter a Unicode character * @return true/false */ static bool lv_bidi_letter_is_neutral(uint32_t letter) diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_color.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_color.h index 37445cc5f..2cc92f277 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_color.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_color.h @@ -23,10 +23,6 @@ extern "C" { #error "LV_COLOR_DEPTH 24 is deprecated. Use LV_COLOR_DEPTH 32 instead (lv_conf.h)" #endif -#if LV_COLOR_DEPTH != 32 && LV_COLOR_SCREEN_TRANSP != 0 -#error "LV_COLOR_SCREEN_TRANSP requires LV_COLOR_DEPTH == 32. Set it in lv_conf.h" -#endif - #if LV_COLOR_DEPTH != 16 && LV_COLOR_16_SWAP != 0 #error "LV_COLOR_16_SWAP requires LV_COLOR_DEPTH == 16. Set it in lv_conf.h" #endif @@ -444,9 +440,9 @@ LV_ATTRIBUTE_FAST_MEM static inline lv_color_t lv_color_mix(lv_color_t c1, lv_co { lv_color_t ret; -#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0 +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0 && LV_COLOR_MIX_ROUND_OFS == 0 /*Source: https://stackoverflow.com/a/50012418/1999969*/ - mix = (mix + 4) >> 3; + mix = (uint32_t)((uint32_t)mix + 4) >> 3; uint32_t bg = (uint32_t)((uint32_t)c2.full | ((uint32_t)c2.full << 16)) & 0x7E0F81F; /*0b00000111111000001111100000011111*/ uint32_t fg = (uint32_t)((uint32_t)c1.full | ((uint32_t)c1.full << 16)) & 0x7E0F81F; @@ -676,7 +672,7 @@ lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r8, uint8_t g8, uint8_t b8); lv_color_hsv_t lv_color_to_hsv(lv_color_t color); /** - * Just a wrapper around LV_COLOR_CHROMA_KEY because it might be more convenient to use a function is some cases + * Just a wrapper around LV_COLOR_CHROMA_KEY because it might be more convenient to use a function in some cases * @return LV_COLOR_CHROMA_KEY */ static inline lv_color_t lv_color_chroma_key(void) diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.c index 5a7afc59a..52f3ce07d 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.c @@ -95,6 +95,8 @@ lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mo file_p->cache = lv_mem_alloc(sizeof(lv_fs_file_cache_t)); LV_ASSERT_MALLOC(file_p->cache); lv_memset_00(file_p->cache, sizeof(lv_fs_file_cache_t)); + file_p->cache->start = UINT32_MAX; /*Set an invalid range by default*/ + file_p->cache->end = UINT32_MAX - 1; } return LV_FS_RES_OK; @@ -138,40 +140,38 @@ static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t if(start <= file_position && file_position < end) { /* Data can be read from cache buffer */ - uint16_t buffer_offset = file_position - start; - uint16_t buffer_remaining_length = buffer_size - buffer_offset; + uint32_t buffer_remaining_length = LV_MIN((uint32_t)buffer_size - buffer_offset, (uint32_t)end - file_position); if(btr <= buffer_remaining_length) { /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/ lv_memcpy(buf, buffer + buffer_offset, btr); + *br = btr; } else { /*First part of data is in cache buffer, but we need to read rest of data from FS*/ lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length); + uint32_t bytes_read_to_buffer = 0; if(btr > buffer_size) { /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length), - btr - buffer_remaining_length, br); + btr - buffer_remaining_length, &bytes_read_to_buffer); } else { /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/ - uint32_t bytes_read_to_buffer = 0; - - /*Read into cache buffer:*/ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); - file_p->cache->start = file_p->cache->end + 1; + file_p->cache->start = file_p->cache->end; file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; - uint16_t data_chunk_remaining = btr - buffer_remaining_length; - memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining); + uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer); + lv_memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining); } + *br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr); } } else { /*Data is not in cache buffer*/ - if(btr > buffer_size) { /*If bigger data is requested, then do not use cache, instead read it directly*/ res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br); @@ -189,13 +189,14 @@ static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t file_p->cache->start = file_position; file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; - memcpy(buf, buffer, btr); + *br = LV_MIN(btr, bytes_read_to_buffer); + lv_memcpy(buf, buffer, *br); + } } if(res == LV_FS_RES_OK) { - *br = btr; - file_p->cache->file_position += btr; + file_p->cache->file_position += *br; } return res; diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.h index 0a9b24104..9f65e1b2a 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_fs.h @@ -176,8 +176,8 @@ lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * Write into a file * @param file_p pointer to a lv_fs_file_t variable * @param buf pointer to a buffer with the bytes to write - * @param btr Bytes To Write - * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written). NULL if unused. * @return LV_FS_RES_OK or any error from lv_fs_res_t enum */ lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw); diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_gc.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_gc.h index 7551252a5..9d7d1bb92 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_gc.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_gc.h @@ -59,7 +59,8 @@ extern "C" { LV_DISPATCH(f, void * , _lv_theme_default_styles) \ LV_DISPATCH(f, void * , _lv_theme_basic_styles) \ LV_DISPATCH_COND(f, uint8_t *, _lv_font_decompr_buf, LV_USE_FONT_COMPRESSED, 1) \ - LV_DISPATCH(f, uint8_t * , _lv_grad_cache_mem) + LV_DISPATCH(f, uint8_t * , _lv_grad_cache_mem) \ + LV_DISPATCH(f, uint8_t * , _lv_style_custom_prop_flag_lookup_table) #define LV_DEFINE_ROOT(root_type, root_name) root_type root_name; #define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT) diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_ll.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_ll.c index c84647ccf..e7582316b 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_ll.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_ll.c @@ -286,7 +286,7 @@ void * _lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act) } /** - * Return with the pointer of the previous node after 'n_act' + * Return with the pointer of the previous node before 'n_act' * @param ll_p pointer to linked list * @param n_act pointer a node * @return pointer to the previous node diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_log.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_log.c index 63441d6a4..d79463f2b 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_log.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_log.c @@ -112,12 +112,29 @@ void _lv_log_add(lv_log_level_t level, const char * file, int line, const char * } } -void lv_log(const char * buf) +void lv_log(const char * format, ...) { + if(LV_LOG_LEVEL >= LV_LOG_LEVEL_NONE) return; /* disable log */ + + va_list args; + va_start(args, format); + #if LV_LOG_PRINTF - puts(buf); + vprintf(format, args); +#else + if(custom_print_cb) { + char buf[512]; +#if LV_SPRINTF_CUSTOM + lv_vsnprintf(buf, sizeof(buf), format, args); +#else + lv_vaformat_t vaf = {format, &args}; + lv_snprintf(buf, sizeof(buf), "%pV", (void *)&vaf); #endif - if(custom_print_cb) custom_print_cb(buf); + custom_print_cb(buf); + } +#endif + + va_end(args); } /********************** diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_log.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_log.h index fa6f5ac6b..9a009930d 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_log.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_log.h @@ -66,9 +66,10 @@ void lv_log_register_print_cb(lv_log_print_g_cb_t print_cb); /** * Print a log message via `printf` if enabled with `LV_LOG_PRINTF` in `lv_conf.h` * and/or a print callback if registered with `lv_log_register_print_cb` - * @param buf a string message to print + * @param format printf-like format string + * @param ... parameters for `format` */ -void lv_log(const char * buf); +void lv_log(const char * format, ...) LV_FORMAT_ATTRIBUTE(1, 2); /** * Add a log @@ -125,6 +126,14 @@ void _lv_log_add(lv_log_level_t level, const char * file, int line, # endif #endif +#ifndef LV_LOG +# if LV_LOG_LEVEL < LV_LOG_LEVEL_NONE +# define LV_LOG(...) lv_log(__VA_ARGS__) +# else +# define LV_LOG(...) do {} while(0) +# endif +#endif + #else /*LV_USE_LOG*/ /*Do nothing if `LV_USE_LOG 0`*/ @@ -134,6 +143,8 @@ void _lv_log_add(lv_log_level_t level, const char * file, int line, #define LV_LOG_WARN(...) do {}while(0) #define LV_LOG_ERROR(...) do {}while(0) #define LV_LOG_USER(...) do {}while(0) +#define LV_LOG(...) do {}while(0) + #endif /*LV_USE_LOG*/ #ifdef __cplusplus diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.c index 0dcbc4415..6ff83903e 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.c @@ -48,13 +48,6 @@ static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_ /** remove an item and push it to the free items queue */ static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index); -/** - * remove the least recently used item - * - * @todo we can optimise this by finding the n lru items, where n = required_space / average_length - */ -static void lv_lru_remove_lru_item(lv_lru_t * cache); - /** pop an existing item off the free queue, or create a new one */ static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache); @@ -152,7 +145,7 @@ lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, v // see if the key already exists uint32_t hash_index = lv_lru_hash(cache, key, key_length); - size_t required = 0; + int required = 0; lv_lru_item_t * item = NULL, *prev = NULL; item = cache->items[hash_index]; @@ -163,7 +156,7 @@ lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, v if(item) { // update the value and value_lengths - required = (size_t)(value_length - item->value_length); + required = (int)(value_length - item->value_length); cache->value_free(item->value); item->value = value; item->value_length = value_length; @@ -177,7 +170,7 @@ lv_lru_res_t lv_lru_set(lv_lru_t * cache, const void * key, size_t key_length, v memcpy(item->key, key, key_length); item->value_length = value_length; item->key_length = key_length; - required = (size_t) value_length; + required = (int) value_length; if(prev) prev->next = item; @@ -241,6 +234,34 @@ lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size) return LV_LRU_OK; } +void lv_lru_remove_lru_item(lv_lru_t * cache) +{ + lv_lru_item_t * min_item = NULL, *min_prev = NULL; + lv_lru_item_t * item = NULL, *prev = NULL; + uint32_t i = 0, min_index = -1; + uint64_t min_access_count = -1; + + for(; i < cache->hash_table_size; i++) { + item = cache->items[i]; + prev = NULL; + + while(item) { + if(item->access_count < min_access_count || (int64_t) min_access_count == -1) { + min_access_count = item->access_count; + min_item = item; + min_prev = prev; + min_index = i; + } + prev = item; + item = item->next; + } + } + + if(min_item) { + lv_lru_remove_item(cache, min_prev, min_item, min_index); + } +} + /********************** * STATIC FUNCTIONS **********************/ @@ -282,18 +303,22 @@ static uint32_t lv_lru_hash(lv_lru_t * cache, const void * key, uint32_t key_len static int lv_lru_cmp_keys(lv_lru_item_t * item, const void * key, uint32_t key_length) { - if(key_length != item->key_length) + if(key_length != item->key_length) { return 1; - else + } + else { return memcmp(key, item->key, key_length); + } } static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_item_t * item, uint32_t hash_index) { - if(prev) + if(prev) { prev->next = item->next; - else + } + else { cache->items[hash_index] = (lv_lru_item_t *) item->next; + } // free memory and update the free memory counter cache->free_memory += item->value_length; @@ -306,33 +331,6 @@ static void lv_lru_remove_item(lv_lru_t * cache, lv_lru_item_t * prev, lv_lru_it cache->free_items = item; } -static void lv_lru_remove_lru_item(lv_lru_t * cache) -{ - lv_lru_item_t * min_item = NULL, *min_prev = NULL; - lv_lru_item_t * item = NULL, *prev = NULL; - uint32_t i = 0, min_index = -1; - uint64_t min_access_count = -1; - - for(; i < cache->hash_table_size; i++) { - item = cache->items[i]; - prev = NULL; - - while(item) { - if(item->access_count < min_access_count || (int64_t) min_access_count == -1) { - min_access_count = item->access_count; - min_item = item; - min_prev = prev; - min_index = i; - } - prev = item; - item = item->next; - } - } - - if(min_item) - lv_lru_remove_item(cache, min_prev, min_item, min_index); -} - static lv_lru_item_t * lv_lru_pop_or_create_item(lv_lru_t * cache) { lv_lru_item_t * item = NULL; diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.h index 07d3bd361..2d0134e5c 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_lru.h @@ -71,6 +71,12 @@ lv_lru_res_t lv_lru_get(lv_lru_t * cache, const void * key, size_t key_size, voi lv_lru_res_t lv_lru_remove(lv_lru_t * cache, const void * key, size_t key_size); +/** + * remove the least recently used item + * + * @todo we can optimise this by finding the n lru items, where n = required_space / average_length + */ +void lv_lru_remove_lru_item(lv_lru_t * cache); /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_math.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_math.c index 2144a50ca..bfb3934b2 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_math.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_math.c @@ -235,8 +235,11 @@ int64_t lv_pow(int64_t base, int8_t exp) */ int32_t lv_map(int32_t x, int32_t min_in, int32_t max_in, int32_t min_out, int32_t max_out) { - if(x >= max_in) return max_out; - if(x <= min_in) return min_out; + if(max_in >= min_in && x >= max_in) return max_out; + if(max_in >= min_in && x <= min_in) return min_out; + + if(max_in <= min_in && x <= max_in) return max_out; + if(max_in <= min_in && x >= min_in) return min_out; /** * The equation should be: diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_mem.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_mem.c index 91d50678b..b7c602f7a 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_mem.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_mem.c @@ -55,6 +55,8 @@ **********************/ #if LV_MEM_CUSTOM == 0 static lv_tlsf_t tlsf; + static uint32_t cur_used; + static uint32_t max_used; #endif static uint32_t zero_mem = ZERO_MEM_SENTINEL; /*Give the address of this variable if 0 byte should be allocated*/ @@ -135,12 +137,14 @@ void * lv_mem_alloc(size_t size) #endif if(alloc == NULL) { - LV_LOG_ERROR("couldn't allocate memory (%lu bytes)", (unsigned long)size); + LV_LOG_INFO("couldn't allocate memory (%lu bytes)", (unsigned long)size); +#if LV_LOG_LEVEL <= LV_LOG_LEVEL_INFO lv_mem_monitor_t mon; lv_mem_monitor(&mon); - LV_LOG_ERROR("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d", - (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct, - (int)mon.free_biggest_size); + LV_LOG_INFO("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d", + (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct, + (int)mon.free_biggest_size); +#endif } #if LV_MEM_ADD_JUNK else { @@ -148,7 +152,13 @@ void * lv_mem_alloc(size_t size) } #endif - MEM_TRACE("allocated at %p", alloc); + if(alloc) { +#if LV_MEM_CUSTOM == 0 + cur_used += size; + max_used = LV_MAX(cur_used, max_used); +#endif + MEM_TRACE("allocated at %p", alloc); + } return alloc; } @@ -166,7 +176,9 @@ void lv_mem_free(void * data) # if LV_MEM_ADD_JUNK lv_memset(data, 0xbb, lv_tlsf_block_size(data)); # endif - lv_tlsf_free(tlsf, data); + size_t size = lv_tlsf_free(tlsf, data); + if(cur_used > size) cur_used -= size; + else cur_used = 0; #else LV_MEM_CUSTOM_FREE(data); #endif @@ -250,6 +262,8 @@ void lv_mem_monitor(lv_mem_monitor_t * mon_p) mon_p->frag_pct = 0; /*no fragmentation if all the RAM is used*/ } + mon_p->max_used = max_used; + MEM_TRACE("finished"); #endif } diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_style.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_style.c index 8b065a8e6..419c29e48 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_style.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_style.c @@ -7,7 +7,10 @@ * INCLUDES *********************/ #include "lv_style.h" +#include "../misc/lv_gc.h" #include "../misc/lv_mem.h" +#include "lv_assert.h" +#include "lv_types.h" /********************* * DEFINES @@ -21,14 +24,121 @@ * STATIC PROTOTYPES **********************/ +static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value, + void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *)); +static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage); +static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage); + /********************** * GLOBAL VARIABLES **********************/ +const uint8_t _lv_style_builtin_prop_flag_lookup_table[_LV_STYLE_NUM_BUILT_IN_PROPS] = { + [LV_STYLE_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MIN_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MAX_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MIN_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_MAX_HEIGHT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_X] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_Y] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_ALIGN] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TRANSFORM_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_TRANSFORM_HEIGHT] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_TRANSLATE_X] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, + [LV_STYLE_TRANSLATE_Y] = LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, + [LV_STYLE_TRANSFORM_ZOOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_TRANSFORM_ANGLE] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYER_REFR, + + [LV_STYLE_PAD_TOP] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_BOTTOM] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_LEFT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_RIGHT] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_ROW] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_PAD_COLUMN] = LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + + [LV_STYLE_BG_COLOR] = 0, + [LV_STYLE_BG_OPA] = 0, + [LV_STYLE_BG_GRAD_COLOR] = 0, + [LV_STYLE_BG_GRAD_DIR] = 0, + [LV_STYLE_BG_MAIN_STOP] = 0, + [LV_STYLE_BG_GRAD_STOP] = 0, + [LV_STYLE_BG_GRAD] = 0, + [LV_STYLE_BG_DITHER_MODE] = 0, + + [LV_STYLE_BG_IMG_SRC] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_BG_IMG_OPA] = 0, + [LV_STYLE_BG_IMG_RECOLOR] = 0, + [LV_STYLE_BG_IMG_RECOLOR_OPA] = 0, + [LV_STYLE_BG_IMG_TILED] = 0, + + [LV_STYLE_BORDER_COLOR] = 0, + [LV_STYLE_BORDER_OPA] = 0, + [LV_STYLE_BORDER_WIDTH] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_BORDER_SIDE] = 0, + [LV_STYLE_BORDER_POST] = 0, + + [LV_STYLE_OUTLINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_OUTLINE_COLOR] = 0, + [LV_STYLE_OUTLINE_OPA] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_OUTLINE_PAD] = LV_STYLE_PROP_EXT_DRAW, + + [LV_STYLE_SHADOW_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_OFS_X] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_OFS_Y] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_SPREAD] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_SHADOW_COLOR] = 0, + [LV_STYLE_SHADOW_OPA] = LV_STYLE_PROP_EXT_DRAW, + + [LV_STYLE_IMG_OPA] = 0, + [LV_STYLE_IMG_RECOLOR] = 0, + [LV_STYLE_IMG_RECOLOR_OPA] = 0, + + [LV_STYLE_LINE_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_LINE_DASH_WIDTH] = 0, + [LV_STYLE_LINE_DASH_GAP] = 0, + [LV_STYLE_LINE_ROUNDED] = 0, + [LV_STYLE_LINE_COLOR] = 0, + [LV_STYLE_LINE_OPA] = 0, + + [LV_STYLE_ARC_WIDTH] = LV_STYLE_PROP_EXT_DRAW, + [LV_STYLE_ARC_ROUNDED] = 0, + [LV_STYLE_ARC_COLOR] = 0, + [LV_STYLE_ARC_OPA] = 0, + [LV_STYLE_ARC_IMG_SRC] = 0, + + [LV_STYLE_TEXT_COLOR] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_OPA] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_FONT] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_LETTER_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_LINE_SPACE] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_TEXT_DECOR] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_TEXT_ALIGN] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + + [LV_STYLE_RADIUS] = 0, + [LV_STYLE_CLIP_CORNER] = 0, + [LV_STYLE_OPA] = LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_COLOR_FILTER_DSC] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_COLOR_FILTER_OPA] = LV_STYLE_PROP_INHERIT, + [LV_STYLE_ANIM_TIME] = 0, + [LV_STYLE_ANIM_SPEED] = 0, + [LV_STYLE_TRANSITION] = 0, + [LV_STYLE_BLEND_MODE] = LV_STYLE_PROP_LAYER_REFR, + [LV_STYLE_LAYOUT] = LV_STYLE_PROP_LAYOUT_REFR, + [LV_STYLE_BASE_DIR] = LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, +}; + +uint32_t _lv_style_custom_prop_flag_lookup_table_size = 0; + /********************** * STATIC VARIABLES **********************/ +static uint16_t last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP; +static const lv_style_value_t null_style_value = { .num = 0 }; + /********************** * MACROS **********************/ @@ -55,7 +165,7 @@ void lv_style_reset(lv_style_t * style) { LV_ASSERT_STYLE(style); - if(style->is_const) { + if(style->prop1 == LV_STYLE_PROP_ANY) { LV_LOG_ERROR("Cannot reset const style"); return; } @@ -67,18 +177,52 @@ void lv_style_reset(lv_style_t * style) #endif } -lv_style_prop_t lv_style_register_prop(void) +lv_style_prop_t lv_style_register_prop(uint8_t flag) { - static uint16_t act_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP; - act_id++; - return act_id; + if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) == NULL) { + _lv_style_custom_prop_flag_lookup_table_size = 0; + last_custom_prop_id = (uint16_t)_LV_STYLE_LAST_BUILT_IN_PROP; + } + + if(((last_custom_prop_id + 1) & LV_STYLE_PROP_META_MASK) != 0) { + LV_LOG_ERROR("No more custom property IDs available"); + return LV_STYLE_PROP_INV; + } + + /* + * Allocate the lookup table if it's not yet available. + */ + size_t required_size = (last_custom_prop_id + 1 - _LV_STYLE_LAST_BUILT_IN_PROP); + if(_lv_style_custom_prop_flag_lookup_table_size < required_size) { + /* Round required_size up to the nearest 32-byte value */ + required_size = (required_size + 31) & ~31; + LV_ASSERT_MSG(required_size > 0, "required size has become 0?"); + uint8_t * old_p = LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table); + uint8_t * new_p = lv_mem_realloc(old_p, required_size * sizeof(uint8_t)); + if(new_p == NULL) { + LV_LOG_ERROR("Unable to allocate space for custom property lookup table"); + return LV_STYLE_PROP_INV; + } + LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) = new_p; + _lv_style_custom_prop_flag_lookup_table_size = required_size; + } + last_custom_prop_id++; + /* This should never happen - we should bail out above */ + LV_ASSERT_NULL(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)); + LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[last_custom_prop_id - _LV_STYLE_NUM_BUILT_IN_PROPS] = flag; + return last_custom_prop_id; +} + +lv_style_prop_t lv_style_get_num_custom_props(void) +{ + return last_custom_prop_id - _LV_STYLE_LAST_BUILT_IN_PROP; } bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) { LV_ASSERT_STYLE(style); - if(style->is_const) { + if(style->prop1 == LV_STYLE_PROP_ANY) { LV_LOG_ERROR("Cannot remove prop from const style"); return false; } @@ -86,7 +230,7 @@ bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) if(style->prop_cnt == 0) return false; if(style->prop_cnt == 1) { - if(style->prop1 == prop) { + if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) { style->prop1 = LV_STYLE_PROP_INV; style->prop_cnt = 0; return true; @@ -98,7 +242,7 @@ bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) uint16_t * old_props = (uint16_t *)tmp; uint32_t i; for(i = 0; i < style->prop_cnt; i++) { - if(old_props[i] == prop) { + if(LV_STYLE_PROP_ID_MASK(old_props[i]) == prop) { lv_style_value_t * old_values = (lv_style_value_t *)style->v_p.values_and_props; if(style->prop_cnt == 2) { @@ -137,78 +281,15 @@ bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop) void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value) { - LV_ASSERT_STYLE(style); - - if(style->is_const) { - LV_LOG_ERROR("Cannot set property of constant style"); - return; - } - - if(style->prop_cnt > 1) { - uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); - uint16_t * props = (uint16_t *)tmp; - int32_t i; - for(i = style->prop_cnt - 1; i >= 0; i--) { - if(props[i] == prop) { - lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; - values[i] = value; - return; - } - } - - size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); - uint8_t * values_and_props = lv_mem_realloc(style->v_p.values_and_props, size); - if(values_and_props == NULL) return; - style->v_p.values_and_props = values_and_props; - - tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); - props = (uint16_t *)tmp; - /*Shift all props to make place for the value before them*/ - for(i = style->prop_cnt - 1; i >= 0; i--) { - props[i + sizeof(lv_style_value_t) / sizeof(uint16_t)] = props[i]; - } - style->prop_cnt++; - - /*Go to the new position wit the props*/ - tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); - props = (uint16_t *)tmp; - lv_style_value_t * values = (lv_style_value_t *)values_and_props; - - /*Set the new property and value*/ - props[style->prop_cnt - 1] = prop; - values[style->prop_cnt - 1] = value; - } - else if(style->prop_cnt == 1) { - if(style->prop1 == prop) { - style->v_p.value1 = value; - return; - } - size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); - uint8_t * values_and_props = lv_mem_alloc(size); - if(values_and_props == NULL) return; - lv_style_value_t value_tmp = style->v_p.value1; - style->v_p.values_and_props = values_and_props; - style->prop_cnt++; - - uint8_t * tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); - uint16_t * props = (uint16_t *)tmp; - lv_style_value_t * values = (lv_style_value_t *)values_and_props; - props[0] = style->prop1; - props[1] = prop; - values[0] = value_tmp; - values[1] = value; - } - else { - style->prop_cnt = 1; - style->prop1 = prop; - style->v_p.value1 = value; - } - - uint8_t group = _lv_style_get_prop_group(prop); - style->has_group |= 1 << group; + lv_style_set_prop_internal(style, prop, value, lv_style_set_prop_helper); } -lv_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) +void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta) +{ + lv_style_set_prop_internal(style, prop | meta, null_style_value, lv_style_set_prop_meta_helper); +} + +lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value) { return lv_style_get_prop_inlined(style, prop, value); } @@ -295,6 +376,110 @@ uint8_t _lv_style_get_prop_group(lv_style_prop_t prop) return (uint8_t)group; } +uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop) +{ + extern const uint8_t _lv_style_builtin_prop_flag_lookup_table[]; + extern uint32_t _lv_style_custom_prop_flag_lookup_table_size; + if(prop == LV_STYLE_PROP_ANY) return LV_STYLE_PROP_ALL; /*Any prop can have any flags*/ + if(prop == LV_STYLE_PROP_INV) return 0; + + if(prop < _LV_STYLE_NUM_BUILT_IN_PROPS) + return _lv_style_builtin_prop_flag_lookup_table[prop]; + prop -= _LV_STYLE_NUM_BUILT_IN_PROPS; + if(LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table) != NULL && prop < _lv_style_custom_prop_flag_lookup_table_size) + return LV_GC_ROOT(_lv_style_custom_prop_flag_lookup_table)[prop]; + return 0; +} + /********************** * STATIC FUNCTIONS **********************/ + +static void lv_style_set_prop_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage) +{ + *prop_storage = prop; + *value_storage = value; +} + +static void lv_style_set_prop_meta_helper(lv_style_prop_t prop, lv_style_value_t value, uint16_t * prop_storage, + lv_style_value_t * value_storage) +{ + LV_UNUSED(value); + LV_UNUSED(value_storage); + *prop_storage = prop; /* meta is OR-ed into the prop ID already */ +} + +static void lv_style_set_prop_internal(lv_style_t * style, lv_style_prop_t prop_and_meta, lv_style_value_t value, + void (*value_adjustment_helper)(lv_style_prop_t, lv_style_value_t, uint16_t *, lv_style_value_t *)) +{ + LV_ASSERT_STYLE(style); + + if(style->prop1 == LV_STYLE_PROP_ANY) { + LV_LOG_ERROR("Cannot set property of constant style"); + return; + } + + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(prop_and_meta); + + if(style->prop_cnt > 1) { + uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + int32_t i; + for(i = style->prop_cnt - 1; i >= 0; i--) { + if(LV_STYLE_PROP_ID_MASK(props[i]) == prop_id) { + lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; + value_adjustment_helper(prop_and_meta, value, &props[i], &values[i]); + return; + } + } + + size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); + uint8_t * values_and_props = lv_mem_realloc(style->v_p.values_and_props, size); + if(values_and_props == NULL) return; + style->v_p.values_and_props = values_and_props; + + tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + props = (uint16_t *)tmp; + /*Shift all props to make place for the value before them*/ + for(i = style->prop_cnt - 1; i >= 0; i--) { + props[i + sizeof(lv_style_value_t) / sizeof(uint16_t)] = props[i]; + } + style->prop_cnt++; + + /*Go to the new position wit the props*/ + tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + props = (uint16_t *)tmp; + lv_style_value_t * values = (lv_style_value_t *)values_and_props; + + /*Set the new property and value*/ + value_adjustment_helper(prop_and_meta, value, &props[style->prop_cnt - 1], &values[style->prop_cnt - 1]); + } + else if(style->prop_cnt == 1) { + if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop_id) { + value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1); + return; + } + size_t size = (style->prop_cnt + 1) * (sizeof(lv_style_value_t) + sizeof(uint16_t)); + uint8_t * values_and_props = lv_mem_alloc(size); + if(values_and_props == NULL) return; + lv_style_value_t value_tmp = style->v_p.value1; + style->v_p.values_and_props = values_and_props; + style->prop_cnt++; + + uint8_t * tmp = values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + lv_style_value_t * values = (lv_style_value_t *)values_and_props; + props[0] = style->prop1; + values[0] = value_tmp; + value_adjustment_helper(prop_and_meta, value, &props[1], &values[1]); + } + else { + style->prop_cnt = 1; + value_adjustment_helper(prop_and_meta, value, &style->prop1, &style->v_p.value1); + } + + uint8_t group = _lv_style_get_prop_group(prop_id); + style->has_group |= 1 << group; +} + diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_style.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_style.h index 6ec97cb57..770ba1df1 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_style.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_style.h @@ -31,13 +31,17 @@ extern "C" { #define LV_STYLE_SENTINEL_VALUE 0xAABBCCDD /** - * Flags for style properties + * Flags for style behavior + * + * The rest of the flags will have _FLAG added to their name in v9. */ -#define LV_STYLE_PROP_INHERIT (1 << 10) /*Inherited*/ -#define LV_STYLE_PROP_EXT_DRAW (1 << 11) /*Requires ext. draw size update when changed*/ -#define LV_STYLE_PROP_LAYOUT_REFR (1 << 12) /*Requires layout update when changed*/ -#define LV_STYLE_PROP_PARENT_LAYOUT_REFR (1 << 13) /*Requires layout update on parent when changed*/ -#define LV_STYLE_PROP_FILTER (1 << 14) /*Apply color filter*/ +#define LV_STYLE_PROP_FLAG_NONE (0) +#define LV_STYLE_PROP_INHERIT (1 << 0) /*Inherited*/ +#define LV_STYLE_PROP_EXT_DRAW (1 << 1) /*Requires ext. draw size update when changed*/ +#define LV_STYLE_PROP_LAYOUT_REFR (1 << 2) /*Requires layout update when changed*/ +#define LV_STYLE_PROP_PARENT_LAYOUT_REFR (1 << 3) /*Requires layout update on parent when changed*/ +#define LV_STYLE_PROP_LAYER_REFR (1 << 4) /*Affects layer handling*/ +#define LV_STYLE_PROP_ALL (0x1F) /*Indicating all flags*/ /** * Other constants @@ -46,9 +50,9 @@ extern "C" { LV_EXPORT_CONST_INT(LV_IMG_ZOOM_NONE); #if LV_USE_ASSERT_STYLE -#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .sentinel = LV_STYLE_SENTINEL_VALUE, .v_p = { .const_props = prop_array }, .has_group = 0xFF, .is_const = 1 } +#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .sentinel = LV_STYLE_SENTINEL_VALUE, .v_p = { .const_props = prop_array }, .has_group = 0xFF, .prop1 = LV_STYLE_PROP_ANY } #else -#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .v_p = { .const_props = prop_array }, .has_group = 0xFF, .is_const = 1 } +#define LV_STYLE_CONST_INIT(var_name, prop_array) const lv_style_t var_name = { .v_p = { .const_props = prop_array }, .has_group = 0xFF, .prop1 = LV_STYLE_PROP_ANY } #endif /** On simple system, don't waste resources on gradients */ @@ -56,6 +60,11 @@ LV_EXPORT_CONST_INT(LV_IMG_ZOOM_NONE); #define LV_GRADIENT_MAX_STOPS 2 #endif +#define LV_STYLE_PROP_META_INHERIT 0x8000 +#define LV_STYLE_PROP_META_INITIAL 0x4000 +#define LV_STYLE_PROP_META_MASK (LV_STYLE_PROP_META_INHERIT | LV_STYLE_PROP_META_INITIAL) + +#define LV_STYLE_PROP_ID_MASK(prop) ((lv_style_prop_t)((prop) & ~LV_STYLE_PROP_META_MASK)) /********************** * TYPEDEFS @@ -153,126 +162,125 @@ typedef union { /** * Enumeration of all built in style properties + * + * Props are split into groups of 16. When adding a new prop to a group, ensure it does not overflow into the next one. */ typedef enum { - LV_STYLE_PROP_INV = 0, + LV_STYLE_PROP_INV = 0, /*Group 0*/ - LV_STYLE_WIDTH = 1 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_MIN_WIDTH = 2 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_MAX_WIDTH = 3 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_HEIGHT = 4 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_MIN_HEIGHT = 5 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_MAX_HEIGHT = 6 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_X = 7 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_Y = 8 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_ALIGN = 9 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_TRANSFORM_WIDTH = 10 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_TRANSFORM_HEIGHT = 11 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_TRANSLATE_X = 12 | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, - LV_STYLE_TRANSLATE_Y = 13 | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, - LV_STYLE_TRANSFORM_ZOOM = 14 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, - LV_STYLE_TRANSFORM_ANGLE = 15 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR, + LV_STYLE_WIDTH = 1, + LV_STYLE_MIN_WIDTH = 2, + LV_STYLE_MAX_WIDTH = 3, + LV_STYLE_HEIGHT = 4, + LV_STYLE_MIN_HEIGHT = 5, + LV_STYLE_MAX_HEIGHT = 6, + LV_STYLE_X = 7, + LV_STYLE_Y = 8, + LV_STYLE_ALIGN = 9, + LV_STYLE_LAYOUT = 10, + LV_STYLE_RADIUS = 11, /*Group 1*/ - LV_STYLE_PAD_TOP = 16 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_PAD_BOTTOM = 17 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_PAD_LEFT = 18 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_PAD_RIGHT = 19 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_PAD_ROW = 20 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_PAD_COLUMN = 21 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR, + LV_STYLE_PAD_TOP = 16, + LV_STYLE_PAD_BOTTOM = 17, + LV_STYLE_PAD_LEFT = 18, + LV_STYLE_PAD_RIGHT = 19, + LV_STYLE_PAD_ROW = 20, + LV_STYLE_PAD_COLUMN = 21, + LV_STYLE_BASE_DIR = 22, + LV_STYLE_CLIP_CORNER = 23, /*Group 2*/ - LV_STYLE_BG_COLOR = 32, - LV_STYLE_BG_COLOR_FILTERED = 32 | LV_STYLE_PROP_FILTER, - LV_STYLE_BG_OPA = 33, - LV_STYLE_BG_GRAD_COLOR = 34, - LV_STYLE_BG_GRAD_COLOR_FILTERED = 34 | LV_STYLE_PROP_FILTER, - LV_STYLE_BG_GRAD_DIR = 35, - LV_STYLE_BG_MAIN_STOP = 36, - LV_STYLE_BG_GRAD_STOP = 37, - LV_STYLE_BG_GRAD = 38, - LV_STYLE_BG_DITHER_MODE = 39, - - - LV_STYLE_BG_IMG_SRC = 40 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_BG_IMG_OPA = 41, - LV_STYLE_BG_IMG_RECOLOR = 42, - LV_STYLE_BG_IMG_RECOLOR_FILTERED = 42 | LV_STYLE_PROP_FILTER, - LV_STYLE_BG_IMG_RECOLOR_OPA = 43, - LV_STYLE_BG_IMG_TILED = 44, + LV_STYLE_BG_COLOR = 32, + LV_STYLE_BG_OPA = 33, + LV_STYLE_BG_GRAD_COLOR = 34, + LV_STYLE_BG_GRAD_DIR = 35, + LV_STYLE_BG_MAIN_STOP = 36, + LV_STYLE_BG_GRAD_STOP = 37, + LV_STYLE_BG_GRAD = 38, + LV_STYLE_BG_DITHER_MODE = 39, + LV_STYLE_BG_IMG_SRC = 40, + LV_STYLE_BG_IMG_OPA = 41, + LV_STYLE_BG_IMG_RECOLOR = 42, + LV_STYLE_BG_IMG_RECOLOR_OPA = 43, + LV_STYLE_BG_IMG_TILED = 44, /*Group 3*/ - LV_STYLE_BORDER_COLOR = 48, - LV_STYLE_BORDER_COLOR_FILTERED = 48 | LV_STYLE_PROP_FILTER, - LV_STYLE_BORDER_OPA = 49, - LV_STYLE_BORDER_WIDTH = 50 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_BORDER_SIDE = 51, - LV_STYLE_BORDER_POST = 52, - - LV_STYLE_OUTLINE_WIDTH = 58 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_OUTLINE_COLOR = 59, - LV_STYLE_OUTLINE_COLOR_FILTERED = 59 | LV_STYLE_PROP_FILTER, - LV_STYLE_OUTLINE_OPA = 60 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_OUTLINE_PAD = 61 | LV_STYLE_PROP_EXT_DRAW, + LV_STYLE_BORDER_COLOR = 48, + LV_STYLE_BORDER_OPA = 49, + LV_STYLE_BORDER_WIDTH = 50, + LV_STYLE_BORDER_SIDE = 51, + LV_STYLE_BORDER_POST = 52, + LV_STYLE_OUTLINE_WIDTH = 53, + LV_STYLE_OUTLINE_COLOR = 54, + LV_STYLE_OUTLINE_OPA = 55, + LV_STYLE_OUTLINE_PAD = 56, /*Group 4*/ - LV_STYLE_SHADOW_WIDTH = 64 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_SHADOW_OFS_X = 65 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_SHADOW_OFS_Y = 66 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_SHADOW_SPREAD = 67 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_SHADOW_COLOR = 68, - LV_STYLE_SHADOW_COLOR_FILTERED = 68 | LV_STYLE_PROP_FILTER, - LV_STYLE_SHADOW_OPA = 69 | LV_STYLE_PROP_EXT_DRAW, - - LV_STYLE_IMG_OPA = 70, - LV_STYLE_IMG_RECOLOR = 71, - LV_STYLE_IMG_RECOLOR_FILTERED = 71 | LV_STYLE_PROP_FILTER, - LV_STYLE_IMG_RECOLOR_OPA = 72, - - LV_STYLE_LINE_WIDTH = 73 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_LINE_DASH_WIDTH = 74, - LV_STYLE_LINE_DASH_GAP = 75, - LV_STYLE_LINE_ROUNDED = 76, - LV_STYLE_LINE_COLOR = 77, - LV_STYLE_LINE_COLOR_FILTERED = 77 | LV_STYLE_PROP_FILTER, - LV_STYLE_LINE_OPA = 78, + LV_STYLE_SHADOW_WIDTH = 64, + LV_STYLE_SHADOW_OFS_X = 65, + LV_STYLE_SHADOW_OFS_Y = 66, + LV_STYLE_SHADOW_SPREAD = 67, + LV_STYLE_SHADOW_COLOR = 68, + LV_STYLE_SHADOW_OPA = 69, + LV_STYLE_IMG_OPA = 70, + LV_STYLE_IMG_RECOLOR = 71, + LV_STYLE_IMG_RECOLOR_OPA = 72, + LV_STYLE_LINE_WIDTH = 73, + LV_STYLE_LINE_DASH_WIDTH = 74, + LV_STYLE_LINE_DASH_GAP = 75, + LV_STYLE_LINE_ROUNDED = 76, + LV_STYLE_LINE_COLOR = 77, + LV_STYLE_LINE_OPA = 78, /*Group 5*/ - LV_STYLE_ARC_WIDTH = 80 | LV_STYLE_PROP_EXT_DRAW, - LV_STYLE_ARC_ROUNDED = 81, - LV_STYLE_ARC_COLOR = 82, - LV_STYLE_ARC_COLOR_FILTERED = 82 | LV_STYLE_PROP_FILTER, - LV_STYLE_ARC_OPA = 83, - LV_STYLE_ARC_IMG_SRC = 84, - - LV_STYLE_TEXT_COLOR = 87 | LV_STYLE_PROP_INHERIT, - LV_STYLE_TEXT_COLOR_FILTERED = 87 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_FILTER, - LV_STYLE_TEXT_OPA = 88 | LV_STYLE_PROP_INHERIT, - LV_STYLE_TEXT_FONT = 89 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_TEXT_LETTER_SPACE = 90 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_TEXT_LINE_SPACE = 91 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_TEXT_DECOR = 92 | LV_STYLE_PROP_INHERIT, - LV_STYLE_TEXT_ALIGN = 93 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + LV_STYLE_ARC_WIDTH = 80, + LV_STYLE_ARC_ROUNDED = 81, + LV_STYLE_ARC_COLOR = 82, + LV_STYLE_ARC_OPA = 83, + LV_STYLE_ARC_IMG_SRC = 84, + LV_STYLE_TEXT_COLOR = 85, + LV_STYLE_TEXT_OPA = 86, + LV_STYLE_TEXT_FONT = 87, + LV_STYLE_TEXT_LETTER_SPACE = 88, + LV_STYLE_TEXT_LINE_SPACE = 89, + LV_STYLE_TEXT_DECOR = 90, + LV_STYLE_TEXT_ALIGN = 91, /*Group 6*/ - LV_STYLE_RADIUS = 96, - LV_STYLE_CLIP_CORNER = 97, - LV_STYLE_OPA = 98 | LV_STYLE_PROP_INHERIT, - LV_STYLE_COLOR_FILTER_DSC = 99, - LV_STYLE_COLOR_FILTER_OPA = 100, - LV_STYLE_ANIM_TIME = 101, - LV_STYLE_ANIM_SPEED = 102, - LV_STYLE_TRANSITION = 103, - LV_STYLE_BLEND_MODE = 104, - LV_STYLE_LAYOUT = 105 | LV_STYLE_PROP_LAYOUT_REFR, - LV_STYLE_BASE_DIR = 106 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR, + LV_STYLE_OPA = 96, + LV_STYLE_COLOR_FILTER_DSC = 97, + LV_STYLE_COLOR_FILTER_OPA = 98, + LV_STYLE_ANIM = 99, + LV_STYLE_ANIM_TIME = 100, + LV_STYLE_ANIM_SPEED = 101, + LV_STYLE_TRANSITION = 102, + LV_STYLE_BLEND_MODE = 103, + LV_STYLE_TRANSFORM_WIDTH = 104, + LV_STYLE_TRANSFORM_HEIGHT = 105, + LV_STYLE_TRANSLATE_X = 106, + LV_STYLE_TRANSLATE_Y = 107, + LV_STYLE_TRANSFORM_ZOOM = 108, + LV_STYLE_TRANSFORM_ANGLE = 109, + LV_STYLE_TRANSFORM_PIVOT_X = 110, + LV_STYLE_TRANSFORM_PIVOT_Y = 111, _LV_STYLE_LAST_BUILT_IN_PROP = 111, + _LV_STYLE_NUM_BUILT_IN_PROPS = _LV_STYLE_LAST_BUILT_IN_PROP + 1, - LV_STYLE_PROP_ANY = 0xFFFF + LV_STYLE_PROP_ANY = 0xFFFF, + _LV_STYLE_PROP_CONST = 0xFFFF /* magic value for const styles */ } lv_style_prop_t; +enum { + LV_STYLE_RES_NOT_FOUND, + LV_STYLE_RES_FOUND, + LV_STYLE_RES_INHERIT +}; + +typedef uint8_t lv_style_res_t; + /** * Descriptor for style transitions */ @@ -311,8 +319,7 @@ typedef struct { const lv_style_const_prop_t * const_props; } v_p; - uint16_t prop1 : 15; - uint16_t is_const : 1; + uint16_t prop1; uint8_t has_group; uint8_t prop_cnt; } lv_style_t; @@ -339,7 +346,7 @@ void lv_style_reset(lv_style_t * style); /** * Register a new style property for custom usage - * @return a new property ID. + * @return a new property ID, or LV_STYLE_PROP_INV if there are no more available. * @example * lv_style_prop_t MY_PROP; * static inline void lv_style_set_my_prop(lv_style_t * style, lv_color_t value) { @@ -350,7 +357,12 @@ void lv_style_reset(lv_style_t * style); * ... * lv_style_set_my_prop(&style1, lv_palette_main(LV_PALETTE_RED)); */ -lv_style_prop_t lv_style_register_prop(void); +lv_style_prop_t lv_style_register_prop(uint8_t flag); + +/** + * Get the number of custom properties that have been registered thus far. + */ +lv_style_prop_t lv_style_get_num_custom_props(void); /** * Remove a property from a style @@ -371,16 +383,13 @@ bool lv_style_remove_prop(lv_style_t * style, lv_style_prop_t prop); void lv_style_set_prop(lv_style_t * style, lv_style_prop_t prop, lv_style_value_t value); /** - * Get the value of a property - * @param style pointer to a style - * @param prop the ID of a property - * @param value pointer to a `lv_style_value_t` variable to store the value - * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged) - * LV_RES_OK: the property was fond, and `value` is set accordingly - * @note For performance reasons there are no sanity check on `style` + * Set a special meta state for a property in a style. + * This function shouldn't be used directly by the user. + * @param style pointer to style + * @param prop the ID of a property (e.g. `LV_STYLE_BG_COLOR`) + * @param meta the meta value to attach to the property in the style */ -lv_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value); - +void lv_style_set_prop_meta(lv_style_t * style, lv_style_prop_t prop, uint16_t meta); /** * Get the value of a property @@ -390,42 +399,8 @@ lv_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_st * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged) * LV_RES_OK: the property was fond, and `value` is set accordingly * @note For performance reasons there are no sanity check on `style` - * @note This function is the same as ::lv_style_get_prop but inlined. Use it only on performance critical places */ -static inline lv_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop, - lv_style_value_t * value) -{ - if(style->is_const) { - const lv_style_const_prop_t * const_prop; - for(const_prop = style->v_p.const_props; const_prop->prop != LV_STYLE_PROP_INV; const_prop++) { - if(const_prop->prop == prop) { - *value = const_prop->value; - return LV_RES_OK; - } - } - return LV_RES_INV; - } - - if(style->prop_cnt == 0) return LV_RES_INV; - - if(style->prop_cnt > 1) { - uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); - uint16_t * props = (uint16_t *)tmp; - uint32_t i; - for(i = 0; i < style->prop_cnt; i++) { - if(props[i] == prop) { - lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; - *value = values[i]; - return LV_RES_OK; - } - } - } - else if(style->prop1 == prop) { - *value = style->v_p.value1; - return LV_RES_OK; - } - return LV_RES_INV; -} +lv_style_res_t lv_style_get_prop(const lv_style_t * style, lv_style_prop_t prop, lv_style_value_t * value); /** * Initialize a transition descriptor. @@ -450,6 +425,64 @@ void lv_style_transition_dsc_init(lv_style_transition_dsc_t * tr, const lv_style */ lv_style_value_t lv_style_prop_get_default(lv_style_prop_t prop); +/** + * Get the value of a property + * @param style pointer to a style + * @param prop the ID of a property + * @param value pointer to a `lv_style_value_t` variable to store the value + * @return LV_RES_INV: the property wasn't found in the style (`value` is unchanged) + * LV_RES_OK: the property was fond, and `value` is set accordingly + * @note For performance reasons there are no sanity check on `style` + * @note This function is the same as ::lv_style_get_prop but inlined. Use it only on performance critical places + */ +static inline lv_style_res_t lv_style_get_prop_inlined(const lv_style_t * style, lv_style_prop_t prop, + lv_style_value_t * value) +{ + if(style->prop1 == LV_STYLE_PROP_ANY) { + const lv_style_const_prop_t * const_prop; + for(const_prop = style->v_p.const_props; const_prop->prop != LV_STYLE_PROP_INV; const_prop++) { + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(const_prop->prop); + if(prop_id == prop) { + if(const_prop->prop & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + *value = (const_prop->prop & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(prop_id) : const_prop->value; + return LV_STYLE_RES_FOUND; + } + } + return LV_STYLE_RES_NOT_FOUND; + } + + if(style->prop_cnt == 0) return LV_STYLE_RES_NOT_FOUND; + + if(style->prop_cnt > 1) { + uint8_t * tmp = style->v_p.values_and_props + style->prop_cnt * sizeof(lv_style_value_t); + uint16_t * props = (uint16_t *)tmp; + uint32_t i; + for(i = 0; i < style->prop_cnt; i++) { + lv_style_prop_t prop_id = LV_STYLE_PROP_ID_MASK(props[i]); + if(prop_id == prop) { + if(props[i] & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + if(props[i] & LV_STYLE_PROP_META_INITIAL) + *value = lv_style_prop_get_default(prop_id); + else { + lv_style_value_t * values = (lv_style_value_t *)style->v_p.values_and_props; + *value = values[i]; + } + return LV_STYLE_RES_FOUND; + } + } + } + else if(LV_STYLE_PROP_ID_MASK(style->prop1) == prop) { + if(style->prop1 & LV_STYLE_PROP_META_INHERIT) + return LV_STYLE_RES_INHERIT; + *value = (style->prop1 & LV_STYLE_PROP_META_INITIAL) ? lv_style_prop_get_default(LV_STYLE_PROP_ID_MASK( + style->prop1)) : style->v_p.value1; + return LV_STYLE_RES_FOUND; + } + return LV_STYLE_RES_NOT_FOUND; +} + /** * Checks if a style is empty (has no properties) * @param style pointer to a style @@ -465,6 +498,14 @@ bool lv_style_is_empty(const lv_style_t * style); */ uint8_t _lv_style_get_prop_group(lv_style_prop_t prop); +/** + * Get the flags of a built-in or custom property. + * + * @param prop a style property + * @return the flags of the property + */ +uint8_t _lv_style_prop_lookup_flags(lv_style_prop_t prop); + #include "lv_style_gen.h" static inline void lv_style_set_size(lv_style_t * style, lv_coord_t value) @@ -499,6 +540,20 @@ static inline void lv_style_set_pad_gap(lv_style_t * style, lv_coord_t value) lv_style_set_pad_column(style, value); } +/** + * @brief Check if the style property has a specified behavioral flag. + * + * Do not pass multiple flags to this function as backwards-compatibility is not guaranteed + * for that. + * + * @param prop Property ID + * @param flag Flag + * @return true if the flag is set for this property + */ +static inline bool lv_style_prop_has_flag(lv_style_prop_t prop, uint8_t flag) +{ + return _lv_style_prop_lookup_flags(prop) & flag; +} /************************* * GLOBAL VARIABLES diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.c index b806a996b..13d85607a 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.c @@ -120,6 +120,22 @@ void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value) lv_style_set_prop(style, LV_STYLE_TRANSFORM_ANGLE, v); } +void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_X, v); +} + +void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value) +{ + lv_style_value_t v = { + .num = (int32_t)value + }; + lv_style_set_prop(style, LV_STYLE_TRANSFORM_PIVOT_Y, v); +} + void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value) { lv_style_value_t v = { @@ -176,14 +192,6 @@ void lv_style_set_bg_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_BG_COLOR, v); } -void lv_style_set_bg_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_BG_COLOR_FILTERED, v); -} - void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -200,14 +208,6 @@ void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_BG_GRAD_COLOR, v); } -void lv_style_set_bg_grad_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_BG_GRAD_COLOR_FILTERED, v); -} - void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value) { lv_style_value_t v = { @@ -272,14 +272,6 @@ void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR, v); } -void lv_style_set_bg_img_recolor_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_BG_IMG_RECOLOR_FILTERED, v); -} - void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -304,14 +296,6 @@ void lv_style_set_border_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_BORDER_COLOR, v); } -void lv_style_set_border_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_BORDER_COLOR_FILTERED, v); -} - void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -360,14 +344,6 @@ void lv_style_set_outline_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_OUTLINE_COLOR, v); } -void lv_style_set_outline_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_OUTLINE_COLOR_FILTERED, v); -} - void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -424,14 +400,6 @@ void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_SHADOW_COLOR, v); } -void lv_style_set_shadow_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_SHADOW_COLOR_FILTERED, v); -} - void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -456,14 +424,6 @@ void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR, v); } -void lv_style_set_img_recolor_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_IMG_RECOLOR_FILTERED, v); -} - void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -512,14 +472,6 @@ void lv_style_set_line_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_LINE_COLOR, v); } -void lv_style_set_line_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_LINE_COLOR_FILTERED, v); -} - void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -552,14 +504,6 @@ void lv_style_set_arc_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_ARC_COLOR, v); } -void lv_style_set_arc_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_ARC_COLOR_FILTERED, v); -} - void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -584,14 +528,6 @@ void lv_style_set_text_color(lv_style_t * style, lv_color_t value) lv_style_set_prop(style, LV_STYLE_TEXT_COLOR, v); } -void lv_style_set_text_color_filtered(lv_style_t * style, lv_color_t value) -{ - lv_style_value_t v = { - .color = value - }; - lv_style_set_prop(style, LV_STYLE_TEXT_COLOR_FILTERED, v); -} - void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value) { lv_style_value_t v = { @@ -680,6 +616,14 @@ void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value) lv_style_set_prop(style, LV_STYLE_COLOR_FILTER_OPA, v); } +void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value) +{ + lv_style_value_t v = { + .ptr = value + }; + lv_style_set_prop(style, LV_STYLE_ANIM, v); +} + void lv_style_set_anim_time(lv_style_t * style, uint32_t value) { lv_style_value_t v = { diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.h index 04e1f1a52..8bf3b68f4 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_style_gen.h @@ -13,6 +13,8 @@ void lv_style_set_translate_x(lv_style_t * style, lv_coord_t value); void lv_style_set_translate_y(lv_style_t * style, lv_coord_t value); void lv_style_set_transform_zoom(lv_style_t * style, lv_coord_t value); void lv_style_set_transform_angle(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_pivot_x(lv_style_t * style, lv_coord_t value); +void lv_style_set_transform_pivot_y(lv_style_t * style, lv_coord_t value); void lv_style_set_pad_top(lv_style_t * style, lv_coord_t value); void lv_style_set_pad_bottom(lv_style_t * style, lv_coord_t value); void lv_style_set_pad_left(lv_style_t * style, lv_coord_t value); @@ -20,10 +22,8 @@ void lv_style_set_pad_right(lv_style_t * style, lv_coord_t value); void lv_style_set_pad_row(lv_style_t * style, lv_coord_t value); void lv_style_set_pad_column(lv_style_t * style, lv_coord_t value); void lv_style_set_bg_color(lv_style_t * style, lv_color_t value); -void lv_style_set_bg_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_bg_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_bg_grad_color(lv_style_t * style, lv_color_t value); -void lv_style_set_bg_grad_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_bg_grad_dir(lv_style_t * style, lv_grad_dir_t value); void lv_style_set_bg_main_stop(lv_style_t * style, lv_coord_t value); void lv_style_set_bg_grad_stop(lv_style_t * style, lv_coord_t value); @@ -32,18 +32,15 @@ void lv_style_set_bg_dither_mode(lv_style_t * style, lv_dither_mode_t value); void lv_style_set_bg_img_src(lv_style_t * style, const void * value); void lv_style_set_bg_img_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_bg_img_recolor(lv_style_t * style, lv_color_t value); -void lv_style_set_bg_img_recolor_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_bg_img_recolor_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_bg_img_tiled(lv_style_t * style, bool value); void lv_style_set_border_color(lv_style_t * style, lv_color_t value); -void lv_style_set_border_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_border_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_border_width(lv_style_t * style, lv_coord_t value); void lv_style_set_border_side(lv_style_t * style, lv_border_side_t value); void lv_style_set_border_post(lv_style_t * style, bool value); void lv_style_set_outline_width(lv_style_t * style, lv_coord_t value); void lv_style_set_outline_color(lv_style_t * style, lv_color_t value); -void lv_style_set_outline_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_outline_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_outline_pad(lv_style_t * style, lv_coord_t value); void lv_style_set_shadow_width(lv_style_t * style, lv_coord_t value); @@ -51,27 +48,22 @@ void lv_style_set_shadow_ofs_x(lv_style_t * style, lv_coord_t value); void lv_style_set_shadow_ofs_y(lv_style_t * style, lv_coord_t value); void lv_style_set_shadow_spread(lv_style_t * style, lv_coord_t value); void lv_style_set_shadow_color(lv_style_t * style, lv_color_t value); -void lv_style_set_shadow_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_shadow_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_img_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_img_recolor(lv_style_t * style, lv_color_t value); -void lv_style_set_img_recolor_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_img_recolor_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_line_width(lv_style_t * style, lv_coord_t value); void lv_style_set_line_dash_width(lv_style_t * style, lv_coord_t value); void lv_style_set_line_dash_gap(lv_style_t * style, lv_coord_t value); void lv_style_set_line_rounded(lv_style_t * style, bool value); void lv_style_set_line_color(lv_style_t * style, lv_color_t value); -void lv_style_set_line_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_line_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_arc_width(lv_style_t * style, lv_coord_t value); void lv_style_set_arc_rounded(lv_style_t * style, bool value); void lv_style_set_arc_color(lv_style_t * style, lv_color_t value); -void lv_style_set_arc_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_arc_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_arc_img_src(lv_style_t * style, const void * value); void lv_style_set_text_color(lv_style_t * style, lv_color_t value); -void lv_style_set_text_color_filtered(lv_style_t * style, lv_color_t value); void lv_style_set_text_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value); void lv_style_set_text_letter_space(lv_style_t * style, lv_coord_t value); @@ -83,6 +75,7 @@ void lv_style_set_clip_corner(lv_style_t * style, bool value); void lv_style_set_opa(lv_style_t * style, lv_opa_t value); void lv_style_set_color_filter_dsc(lv_style_t * style, const lv_color_filter_dsc_t * value); void lv_style_set_color_filter_opa(lv_style_t * style, lv_opa_t value); +void lv_style_set_anim(lv_style_t * style, const lv_anim_t * value); void lv_style_set_anim_time(lv_style_t * style, uint32_t value); void lv_style_set_anim_speed(lv_style_t * style, uint32_t value); void lv_style_set_transition(lv_style_t * style, const lv_style_transition_dsc_t * value); @@ -165,6 +158,16 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_TRANSFORM_ANGLE, .value = { .num = (int32_t)val } \ } +#define LV_STYLE_CONST_TRANSFORM_PIVOT_X(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_PIVOT_X, .value = { .num = (int32_t)val } \ + } + +#define LV_STYLE_CONST_TRANSFORM_PIVOT_Y(val) \ + { \ + .prop = LV_STYLE_TRANSFORM_PIVOT_Y, .value = { .num = (int32_t)val } \ + } + #define LV_STYLE_CONST_PAD_TOP(val) \ { \ .prop = LV_STYLE_PAD_TOP, .value = { .num = (int32_t)val } \ @@ -200,11 +203,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_BG_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_BG_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_BG_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_BG_OPA(val) \ { \ .prop = LV_STYLE_BG_OPA, .value = { .num = (int32_t)val } \ @@ -215,11 +213,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_BG_GRAD_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_BG_GRAD_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_BG_GRAD_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_BG_GRAD_DIR(val) \ { \ .prop = LV_STYLE_BG_GRAD_DIR, .value = { .num = (int32_t)val } \ @@ -260,11 +253,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_BG_IMG_RECOLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_BG_IMG_RECOLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_BG_IMG_RECOLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_BG_IMG_RECOLOR_OPA(val) \ { \ .prop = LV_STYLE_BG_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \ @@ -280,11 +268,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_BORDER_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_BORDER_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_BORDER_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_BORDER_OPA(val) \ { \ .prop = LV_STYLE_BORDER_OPA, .value = { .num = (int32_t)val } \ @@ -315,11 +298,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_OUTLINE_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_OUTLINE_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_OUTLINE_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_OUTLINE_OPA(val) \ { \ .prop = LV_STYLE_OUTLINE_OPA, .value = { .num = (int32_t)val } \ @@ -355,11 +333,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_SHADOW_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_SHADOW_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_SHADOW_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_SHADOW_OPA(val) \ { \ .prop = LV_STYLE_SHADOW_OPA, .value = { .num = (int32_t)val } \ @@ -375,11 +348,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_IMG_RECOLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_IMG_RECOLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_IMG_RECOLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_IMG_RECOLOR_OPA(val) \ { \ .prop = LV_STYLE_IMG_RECOLOR_OPA, .value = { .num = (int32_t)val } \ @@ -410,11 +378,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_LINE_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_LINE_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_LINE_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_LINE_OPA(val) \ { \ .prop = LV_STYLE_LINE_OPA, .value = { .num = (int32_t)val } \ @@ -435,11 +398,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_ARC_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_ARC_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_ARC_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_ARC_OPA(val) \ { \ .prop = LV_STYLE_ARC_OPA, .value = { .num = (int32_t)val } \ @@ -455,11 +413,6 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_TEXT_COLOR, .value = { .color = val } \ } -#define LV_STYLE_CONST_TEXT_COLOR_FILTERED(val) \ - { \ - .prop = LV_STYLE_TEXT_COLOR_FILTERED, .value = { .color = val } \ - } - #define LV_STYLE_CONST_TEXT_OPA(val) \ { \ .prop = LV_STYLE_TEXT_OPA, .value = { .num = (int32_t)val } \ @@ -515,6 +468,11 @@ void lv_style_set_base_dir(lv_style_t * style, lv_base_dir_t value); .prop = LV_STYLE_COLOR_FILTER_OPA, .value = { .num = (int32_t)val } \ } +#define LV_STYLE_CONST_ANIM(val) \ + { \ + .prop = LV_STYLE_ANIM, .value = { .ptr = val } \ + } + #define LV_STYLE_CONST_ANIM_TIME(val) \ { \ .prop = LV_STYLE_ANIM_TIME, .value = { .num = (int32_t)val } \ diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_timer.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_timer.h index ce94c7b04..a9a8e50de 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_timer.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_timer.h @@ -13,6 +13,7 @@ extern "C" { * INCLUDES *********************/ #include "../lv_conf_internal.h" +#include "../hal/lv_hal_tick.h" #include #include @@ -68,6 +69,24 @@ LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void); //! @endcond +/** + * Call it in the super-loop of main() or threads. It will run lv_timer_handler() + * with a given period in ms. You can use it with sleep or delay in OS environment. + * This function is used to simplify the porting. + * @param __ms the period for running lv_timer_handler() + */ +static inline LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler_run_in_period(uint32_t ms) +{ + static uint32_t last_tick = 0; + uint32_t curr_tick = lv_tick_get(); + + if((curr_tick - last_tick) >= (ms)) { + last_tick = curr_tick; + return lv_timer_handler(); + } + return 1; +} + /** * Create an "empty" timer. It needs to initialized with at least * `lv_timer_set_cb` and `lv_timer_set_period` diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.c index 64197f977..27e0a46cf 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.c @@ -1157,18 +1157,22 @@ void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t size) return block_prepare_used(control, block, adjust); } -void lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr) +size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr) { + size_t size = 0; /* Don't attempt to free a NULL pointer. */ if(ptr) { control_t * control = tlsf_cast(control_t *, tlsf); block_header_t * block = block_from_ptr(ptr); tlsf_assert(!block_is_free(block) && "block already marked as free"); + size = block->size; block_mark_as_free(block); block = block_merge_prev(control, block); block = block_merge_next(control, block); block_insert(control, block); } + + return size; } /* @@ -1204,6 +1208,10 @@ void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size) const size_t cursize = block_size(block); const size_t combined = cursize + block_size(next) + block_header_overhead; const size_t adjust = adjust_request_size(size, ALIGN_SIZE); + if(size > cursize && adjust == 0) { + /* The request is probably too large, fail */ + return NULL; + } tlsf_assert(!block_is_free(block) && "block already marked as free"); diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.h index 9380ee8ca..f12590b60 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_tlsf.h @@ -66,7 +66,7 @@ void lv_tlsf_remove_pool(lv_tlsf_t tlsf, lv_pool_t pool); void * lv_tlsf_malloc(lv_tlsf_t tlsf, size_t bytes); void * lv_tlsf_memalign(lv_tlsf_t tlsf, size_t align, size_t bytes); void * lv_tlsf_realloc(lv_tlsf_t tlsf, void * ptr, size_t size); -void lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr); +size_t lv_tlsf_free(lv_tlsf_t tlsf, const void * ptr); /* Returns internal block size, not original request size */ size_t lv_tlsf_block_size(void * ptr); diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.c b/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.c index 3879b48d9..da7eca0bb 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.c +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.c @@ -161,9 +161,10 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * * @param txt a '\0' terminated string * @param font pointer to a font * @param letter_space letter space - * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid line breaks * @param flags settings for the text from 'txt_flag_type' enum * @param[out] word_w_ptr width (in pixels) of the parsed word. May be NULL. + * @param cmd_state pointer to a txt_cmd_state_t variable which stores the current state of command processing * @param force Force return the fraction of the word that can fit in the provided space. * @return the index of the first char of the next word (in byte index not letter index. With UTF-8 they are different) */ @@ -519,8 +520,8 @@ static uint8_t lv_txt_utf8_size(const char * str) } /** - * Convert an Unicode letter to UTF-8. - * @param letter_uni an Unicode letter + * Convert a Unicode letter to UTF-8. + * @param letter_uni a Unicode letter * @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű') */ static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni) @@ -546,6 +547,9 @@ static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni) bytes[2] = ((letter_uni >> 6) & 0x3F) | 0x80; bytes[3] = ((letter_uni >> 0) & 0x3F) | 0x80; } + else { + return 0; + } uint32_t * res_p = (uint32_t *)bytes; return *res_p; @@ -761,8 +765,8 @@ static uint8_t lv_txt_iso8859_1_size(const char * str) } /** - * Convert an Unicode letter to ISO8859-1. - * @param letter_uni an Unicode letter + * Convert a Unicode letter to ISO8859-1. + * @param letter_uni a Unicode letter * @return ISO8859-1 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ű') */ static uint32_t lv_txt_unicode_to_iso8859_1(uint32_t letter_uni) @@ -794,7 +798,7 @@ static uint32_t lv_txt_iso8859_1_conv_wc(uint32_t c) */ static uint32_t lv_txt_iso8859_1_next(const char * txt, uint32_t * i) { - if(i == NULL) return txt[1]; /*Get the next char*/ + if(i == NULL) return txt[0]; /*Get the next char*/ uint8_t letter = txt[*i]; (*i)++; diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.h index 4f134ab6d..46050dc3c 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_txt.h @@ -77,7 +77,7 @@ typedef uint8_t lv_text_align_t; * @param letter_space letter space of the text * @param line_space line space of the text * @param flags settings for the text from ::lv_text_flag_t - * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid * line breaks */ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, lv_coord_t letter_space, @@ -88,7 +88,7 @@ void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * * @param txt a '\0' terminated string * @param font pointer to a font * @param letter_space letter space - * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid + * @param max_width max width of the text (break the lines to fit this size). Set COORD_MAX to avoid * line breaks * @param used_width When used_width != NULL, save the width of this line if * flag == LV_TEXT_FLAG_NONE, otherwise save -1. @@ -195,8 +195,8 @@ static inline bool _lv_txt_is_break_char(uint32_t letter) extern uint8_t (*_lv_txt_encoded_size)(const char *); /** - * Convert an Unicode letter to encoded - * @param letter_uni an Unicode letter + * Convert a Unicode letter to encoded + * @param letter_uni a Unicode letter * @return Encoded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ü') */ extern uint32_t (*_lv_txt_unicode_to_encoded)(uint32_t); diff --git a/lib/libesp32_lvgl/lvgl/src/misc/lv_types.h b/lib/libesp32_lvgl/lvgl/src/misc/lv_types.h index 9f8ef4ca6..84aee1030 100644 --- a/lib/libesp32_lvgl/lvgl/src/misc/lv_types.h +++ b/lib/libesp32_lvgl/lvgl/src/misc/lv_types.h @@ -77,7 +77,7 @@ typedef uint32_t lv_uintptr_t; #define _LV_CONCAT3(x, y, z) x ## y ## z #define LV_CONCAT3(x, y, z) _LV_CONCAT3(x, y, z) -#if defined(PYCPARSER) +#if defined(PYCPARSER) || defined(__CC_ARM) #define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) #elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4) #define LV_FORMAT_ATTRIBUTE(fmtstr, vararg) __attribute__((format(gnu_printf, fmtstr, vararg))) diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.c index 99ffb1f15..da39fdc65 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.c @@ -35,7 +35,8 @@ static void lv_arc_draw(lv_event_t * e); static void lv_arc_event(const lv_obj_class_t * class_p, lv_event_t * e); static void inv_arc_area(lv_obj_t * arc, uint16_t start_angle, uint16_t end_angle, lv_part_t part); static void inv_knob_area(lv_obj_t * obj); -static void get_center(lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r); +static void get_center(const lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r); +static lv_coord_t get_angle(const lv_obj_t * obj); static void get_knob_area(lv_obj_t * arc, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area); static void value_update(lv_obj_t * arc); @@ -317,6 +318,60 @@ lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj) return ((lv_arc_t *) obj)->type; } +/*===================== + * Other functions + *====================*/ + + +void lv_arc_align_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_align, lv_coord_t r_offset) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + LV_ASSERT_NULL(obj_to_align); + + lv_obj_update_layout(obj); + + lv_point_t center; + lv_coord_t arc_r; + get_center(obj, ¢er, &arc_r); + lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR); + lv_coord_t indic_width_half = indic_width / 2; + arc_r -= indic_width_half; + arc_r += r_offset; + + uint16_t angle = get_angle(obj); + lv_coord_t knob_x = (arc_r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT; + lv_coord_t knob_y = (arc_r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT; + lv_obj_align_to(obj_to_align, obj, LV_ALIGN_CENTER, knob_x, knob_y); +} + +void lv_arc_rotate_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_rotate, lv_coord_t r_offset) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + LV_ASSERT_NULL(obj_to_rotate); + + lv_obj_update_layout(obj); + + lv_point_t center; + lv_coord_t arc_r; + get_center(obj, ¢er, &arc_r); + lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR); + lv_coord_t indic_width_half = indic_width / 2; + arc_r -= indic_width_half; + + arc_r += r_offset; + lv_obj_align_to(obj_to_rotate, obj, LV_ALIGN_CENTER, 0, -arc_r); + + lv_obj_update_layout(obj); + + uint16_t angle = get_angle(obj); + lv_coord_t pivot_x = obj_to_rotate->coords.x1 - center.x; + lv_coord_t pivot_y = obj_to_rotate->coords.y1 - center.y; + lv_obj_set_style_transform_pivot_x(obj_to_rotate, -pivot_x, 0); + lv_obj_set_style_transform_pivot_y(obj_to_rotate, -pivot_y, 0); + lv_obj_set_style_transform_angle(obj_to_rotate, angle * 10 + 900, 0); +} + + /********************** * STATIC FUNCTIONS **********************/ @@ -345,7 +400,7 @@ static void lv_arc_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) arc->last_angle = arc->indic_angle_end; lv_obj_add_flag(obj, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN); + lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_ext_click_area(obj, LV_DPI_DEF / 10); @@ -675,7 +730,7 @@ static void inv_knob_area(lv_obj_t * obj) lv_obj_invalidate_area(obj, &a); } -static void get_center(lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r) +static void get_center(const lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r) { lv_coord_t left_bg = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); lv_coord_t right_bg = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); @@ -691,15 +746,9 @@ static void get_center(lv_obj_t * obj, lv_point_t * center, lv_coord_t * arc_r) if(arc_r) *arc_r = r; } -static void get_knob_area(lv_obj_t * obj, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area) +static lv_coord_t get_angle(const lv_obj_t * obj) { - LV_ASSERT_OBJ(obj, MY_CLASS); lv_arc_t * arc = (lv_arc_t *)obj; - - lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR); - lv_coord_t indic_width_half = indic_width / 2; - r -= indic_width_half; - uint16_t angle = arc->rotation; if(arc->type == LV_ARC_MODE_NORMAL) { angle += arc->indic_angle_end; @@ -712,6 +761,18 @@ static void get_knob_area(lv_obj_t * obj, const lv_point_t * center, lv_coord_t if(arc->value < range_midpoint) angle += arc->indic_angle_start; else angle += arc->indic_angle_end; } + + return angle; +} + + +static void get_knob_area(lv_obj_t * obj, const lv_point_t * center, lv_coord_t r, lv_area_t * knob_area) +{ + lv_coord_t indic_width = lv_obj_get_style_arc_width(obj, LV_PART_INDICATOR); + lv_coord_t indic_width_half = indic_width / 2; + r -= indic_width_half; + + lv_coord_t angle = get_angle(obj); lv_coord_t knob_x = (r * lv_trigo_sin(angle + 90)) >> LV_TRIGO_SHIFT; lv_coord_t knob_y = (r * lv_trigo_sin(angle)) >> LV_TRIGO_SHIFT; @@ -759,12 +820,13 @@ static void value_update(lv_obj_t * obj) } break; case LV_ARC_MODE_REVERSE: - angle = lv_map(arc->value, arc->min_value, arc->max_value, arc->bg_angle_start, bg_end); + angle = lv_map(arc->value, arc->min_value, arc->max_value, bg_end, arc->bg_angle_start); lv_arc_set_angles(obj, angle, arc->bg_angle_end); break; case LV_ARC_MODE_NORMAL: angle = lv_map(arc->value, arc->min_value, arc->max_value, arc->bg_angle_start, bg_end); lv_arc_set_angles(obj, arc->bg_angle_start, angle); + break; default: LV_LOG_WARN("Invalid mode: %d", arc->type); diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.h index 8ec39a4aa..fd53fc15c 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_arc.h @@ -85,83 +85,83 @@ lv_obj_t * lv_arc_create(lv_obj_t * parent); /** * Set the start angle of an arc. 0 deg: right, 90 bottom, etc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param start the start angle */ -void lv_arc_set_start_angle(lv_obj_t * arc, uint16_t start); +void lv_arc_set_start_angle(lv_obj_t * obj, uint16_t start); /** * Set the end angle of an arc. 0 deg: right, 90 bottom, etc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param end the end angle */ -void lv_arc_set_end_angle(lv_obj_t * arc, uint16_t end); +void lv_arc_set_end_angle(lv_obj_t * obj, uint16_t end); /** * Set the start and end angles - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param start the start angle * @param end the end angle */ -void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end); +void lv_arc_set_angles(lv_obj_t * obj, uint16_t start, uint16_t end); /** * Set the start angle of an arc background. 0 deg: right, 90 bottom, etc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param start the start angle */ -void lv_arc_set_bg_start_angle(lv_obj_t * arc, uint16_t start); +void lv_arc_set_bg_start_angle(lv_obj_t * obj, uint16_t start); /** * Set the start angle of an arc background. 0 deg: right, 90 bottom etc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param end the end angle */ -void lv_arc_set_bg_end_angle(lv_obj_t * arc, uint16_t end); +void lv_arc_set_bg_end_angle(lv_obj_t * obj, uint16_t end); /** * Set the start and end angles of the arc background - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param start the start angle * @param end the end angle */ -void lv_arc_set_bg_angles(lv_obj_t * arc, uint16_t start, uint16_t end); +void lv_arc_set_bg_angles(lv_obj_t * obj, uint16_t start, uint16_t end); /** * Set the rotation for the whole arc - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param rotation rotation angle */ -void lv_arc_set_rotation(lv_obj_t * arc, uint16_t rotation); +void lv_arc_set_rotation(lv_obj_t * obj, uint16_t rotation); /** * Set the type of arc. - * @param arc pointer to arc object + * @param obj pointer to arc object * @param mode arc's mode */ -void lv_arc_set_mode(lv_obj_t * arc, lv_arc_mode_t type); +void lv_arc_set_mode(lv_obj_t * obj, lv_arc_mode_t type); /** * Set a new value on the arc - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param value new value */ -void lv_arc_set_value(lv_obj_t * arc, int16_t value); +void lv_arc_set_value(lv_obj_t * obj, int16_t value); /** * Set minimum and the maximum values of an arc - * @param arc pointer to the arc object + * @param obj pointer to the arc object * @param min minimum value * @param max maximum value */ -void lv_arc_set_range(lv_obj_t * arc, int16_t min, int16_t max); +void lv_arc_set_range(lv_obj_t * obj, int16_t min, int16_t max); /** * Set a change rate to limit the speed how fast the arc should reach the pressed point. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @param rate the change rate */ -void lv_arc_set_change_rate(lv_obj_t * arc, uint16_t rate); +void lv_arc_set_change_rate(lv_obj_t * obj, uint16_t rate); /*===================== * Getter functions @@ -169,56 +169,56 @@ void lv_arc_set_change_rate(lv_obj_t * arc, uint16_t rate); /** * Get the start angle of an arc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return the start angle [0..360] */ uint16_t lv_arc_get_angle_start(lv_obj_t * obj); /** * Get the end angle of an arc. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return the end angle [0..360] */ uint16_t lv_arc_get_angle_end(lv_obj_t * obj); /** * Get the start angle of an arc background. - * @param arc pointer to an arc object - * @return the start angle [0..360] + * @param obj pointer to an arc object + * @return the start angle [0..360] */ uint16_t lv_arc_get_bg_angle_start(lv_obj_t * obj); /** * Get the end angle of an arc background. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return the end angle [0..360] */ uint16_t lv_arc_get_bg_angle_end(lv_obj_t * obj); /** * Get the value of an arc - * @param arc pointer to an arc object - * @return the value of the arc + * @param obj pointer to an arc object + * @return the value of the arc */ int16_t lv_arc_get_value(const lv_obj_t * obj); /** * Get the minimum value of an arc - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return the minimum value of the arc */ int16_t lv_arc_get_min_value(const lv_obj_t * obj); /** * Get the maximum value of an arc - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return the maximum value of the arc */ int16_t lv_arc_get_max_value(const lv_obj_t * obj); /** * Get whether the arc is type or not. - * @param arc pointer to an arc object + * @param obj pointer to an arc object * @return arc's mode */ lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj); @@ -227,6 +227,22 @@ lv_arc_mode_t lv_arc_get_mode(const lv_obj_t * obj); * Other functions *====================*/ +/** + * Align an object to the current position of the arc (knob) + * @param obj pointer to an arc object + * @param obj_to_align pointer to an object to align + * @param r_offset consider the radius larger with this value (< 0: for smaller radius) + */ +void lv_arc_align_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_align, lv_coord_t r_offset); + +/** + * Rotate an object to the current position of the arc (knob) + * @param obj pointer to an arc object + * @param obj_to_align pointer to an object to rotate + * @param r_offset consider the radius larger with this value (< 0: for smaller radius) + */ +void lv_arc_rotate_obj_to_angle(const lv_obj_t * obj, lv_obj_t * obj_to_rotate, lv_coord_t r_offset); + /********************** * MACROS **********************/ diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.c index 62f86f794..92a4d2fe3 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.c @@ -399,13 +399,10 @@ static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_point_t p; if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { - lv_coord_t * s = lv_event_get_param(e); if(has_popovers_in_top_row(obj)) { /*reserve one row worth of extra space to account for popovers in the top row*/ - *s = btnm->row_cnt > 0 ? lv_obj_get_content_height(obj) / btnm->row_cnt : 0; - } - else { - *s = 0; + lv_coord_t s = btnm->row_cnt > 0 ? lv_obj_get_content_height(obj) / btnm->row_cnt : 0; + lv_event_set_ext_draw_size(e, s); } } if(code == LV_EVENT_STYLE_CHANGED) { diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.h index cf586d723..780d57b68 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_btnmatrix.h @@ -163,7 +163,7 @@ void lv_btnmatrix_set_btn_width(lv_obj_t * obj, uint16_t btn_id, uint8_t width); /** * Make the button matrix like a selector widget (only one button may be checked at a time). * `LV_BTNMATRIX_CTRL_CHECKABLE` must be enabled on the buttons to be selected using - * `lv_btnmatrix_set_ctrl()` or `lv_btnmatrix_set_btn_ctrl_all()`. + * `lv_btnmatrix_set_ctrl()` or `lv_btnmatrix_set_btn_ctrl_all()`. * @param obj pointer to a button matrix object * @param en whether "one check" mode is enabled */ diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_canvas.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_canvas.c index a55429bdf..6b37069ab 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_canvas.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_canvas.c @@ -76,6 +76,7 @@ void lv_canvas_set_buffer(lv_obj_t * obj, void * buf, lv_coord_t w, lv_coord_t h canvas->dsc.data = buf; lv_img_set_src(obj, &canvas->dsc); + lv_img_cache_invalidate_src(&canvas->dsc); } void lv_canvas_set_px_color(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_color_t c) @@ -157,98 +158,63 @@ void lv_canvas_copy_buf(lv_obj_t * obj, const void * to_copy, lv_coord_t x, lv_c } } -void lv_canvas_transform(lv_obj_t * obj, lv_img_dsc_t * img, int16_t angle, uint16_t zoom, lv_coord_t offset_x, +void lv_canvas_transform(lv_obj_t * obj, lv_img_dsc_t * src_img, int16_t angle, uint16_t zoom, lv_coord_t offset_x, lv_coord_t offset_y, int32_t pivot_x, int32_t pivot_y, bool antialias) { #if LV_DRAW_COMPLEX LV_ASSERT_OBJ(obj, MY_CLASS); - LV_ASSERT_NULL(img); + LV_ASSERT_NULL(src_img); lv_canvas_t * canvas = (lv_canvas_t *)obj; - lv_color_t color = lv_obj_get_style_img_recolor(obj, LV_PART_MAIN); - - int32_t dest_width = canvas->dsc.header.w; - int32_t dest_height = canvas->dsc.header.h; + lv_img_dsc_t * dest_img = &canvas->dsc; int32_t x; int32_t y; - bool ret; - lv_img_transform_dsc_t dsc; - dsc.cfg.angle = angle; - dsc.cfg.zoom = zoom; - dsc.cfg.src = img->data; - dsc.cfg.src_w = img->header.w; - dsc.cfg.src_h = img->header.h; - dsc.cfg.cf = img->header.cf; - dsc.cfg.pivot_x = pivot_x; - dsc.cfg.pivot_y = pivot_y; - dsc.cfg.color = color; - dsc.cfg.antialias = antialias; - _lv_img_buf_transform_init(&dsc); + lv_draw_img_dsc_t draw_dsc; + lv_draw_img_dsc_init(&draw_dsc); + draw_dsc.angle = angle; + draw_dsc.zoom = zoom; + draw_dsc.pivot.x = pivot_x; + draw_dsc.pivot.y = pivot_y; + draw_dsc.antialias = antialias; - for(y = -offset_y; y < dest_height - offset_y; y++) { - for(x = -offset_x; x < dest_width - offset_x; x++) { + lv_area_t dest_area; + dest_area.x1 = 0; + dest_area.x2 = dest_img->header.w - 1; + dest_area.y1 = 0; + dest_area.y2 = 0; - ret = _lv_img_buf_transform(&dsc, x, y); + lv_color_t * cbuf = lv_mem_alloc(dest_img->header.w * sizeof(lv_color_t)); + lv_opa_t * abuf = lv_mem_alloc(dest_img->header.w * sizeof(lv_opa_t)); + for(y = 0; y < dest_img->header.h; y++) { + if(y + offset_y >= 0) { + lv_draw_sw_transform(NULL, &dest_area, src_img->data, src_img->header.w, src_img->header.h, src_img->header.w, + &draw_dsc, canvas->dsc.header.cf, cbuf, abuf); - if(ret == false) continue; - - if(x + offset_x >= 0 && x + offset_x < dest_width && y + offset_y >= 0 && y + offset_y < dest_height) { - /*If the image has no alpha channel just simple set the result color on the canvas*/ - if(lv_img_cf_has_alpha(img->header.cf) == false) { - lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color); - } - else { - lv_color_t bg_color = lv_img_buf_get_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.cfg.color); - - /*If the canvas has no alpha but the image has mix the image's color with - * canvas*/ - if(lv_img_cf_has_alpha(canvas->dsc.header.cf) == false) { - if(dsc.res.opa < LV_OPA_MAX) dsc.res.color = lv_color_mix(dsc.res.color, bg_color, dsc.res.opa); - lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color); - } - /*Both the image and canvas has alpha channel. Some extra calculation is - required*/ - else { - lv_opa_t bg_opa = lv_img_buf_get_px_alpha(&canvas->dsc, x + offset_x, y + offset_y); - /*Pick the foreground if it's fully opaque or the Background is fully - *transparent*/ - if(dsc.res.opa >= LV_OPA_MAX || bg_opa <= LV_OPA_MIN) { - lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.color); - lv_img_buf_set_px_alpha(&canvas->dsc, x + offset_x, y + offset_y, dsc.res.opa); - } - /*Opaque background: use simple mix*/ - else if(bg_opa >= LV_OPA_MAX) { - lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, - lv_color_mix(dsc.res.color, bg_color, dsc.res.opa)); - } - /*Both colors have alpha. Expensive calculation need to be applied*/ - else { - - /*Info: - * https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/ - lv_opa_t opa_res_2 = 255 - ((uint16_t)((uint16_t)(255 - dsc.res.opa) * (255 - bg_opa)) >> 8); - if(opa_res_2 == 0) { - opa_res_2 = 1; /*never happens, just to be sure*/ - } - lv_opa_t ratio = (uint16_t)((uint16_t)dsc.res.opa * 255) / opa_res_2; - - lv_img_buf_set_px_color(&canvas->dsc, x + offset_x, y + offset_y, - lv_color_mix(dsc.res.color, bg_color, ratio)); - lv_img_buf_set_px_alpha(&canvas->dsc, x + offset_x, y + offset_y, opa_res_2); - } - } + for(x = 0; x < dest_img->header.w; x++) { + if(x + offset_x < 0) continue; + if(x + offset_x >= dest_img->header.w) break; + if(abuf[x]) { + lv_img_buf_set_px_color(dest_img, x + offset_x, y + offset_y, cbuf[x]); + lv_img_buf_set_px_alpha(dest_img, x + offset_x, y + offset_y, abuf[x]); } } + + if(y + offset_y >= dest_img->header.h) break; + dest_area.y1++; + dest_area.y2++; } } + lv_mem_free(cbuf); + lv_mem_free(abuf); lv_obj_invalidate(obj); + #else LV_UNUSED(obj); - LV_UNUSED(img); + LV_UNUSED(src_img); LV_UNUSED(angle); LV_UNUSED(zoom); LV_UNUSED(offset_x); @@ -856,6 +822,9 @@ static void init_fake_disp(lv_obj_t * canvas, lv_disp_t * disp, lv_disp_drv_t * draw_ctx->buf = (void *)dsc->data; lv_disp_drv_use_generic_set_px_cb(disp->driver, dsc->header.cf); + if(LV_COLOR_SCREEN_TRANSP && dsc->header.cf != LV_IMG_CF_TRUE_COLOR_ALPHA) { + drv->screen_transp = 0; + } } static void deinit_fake_disp(lv_obj_t * canvas, lv_disp_t * disp) diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.c index e7e650388..241d17776 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.c @@ -398,6 +398,26 @@ void lv_dropdown_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf buf[c] = '\0'; } +int32_t lv_dropdown_get_option_index(lv_obj_t * obj, const char * option) +{ + const char * opts = lv_dropdown_get_options(obj); + uint32_t char_i = 0; + uint32_t opt_i = 0; + const char * start = opts; + + while(start[char_i] != '\0') { + for(char_i = 0; (start[char_i] != '\n') && (start[char_i] != '\0'); char_i++); + + if(memcmp(start, option, LV_MIN(strlen(option), char_i)) == 0) return opt_i; + start = &start[char_i]; + if(start[0] == '\n') start++; + opt_i++; + } + + return -1; +} + + const char * lv_dropdown_get_symbol(lv_obj_t * obj) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -515,7 +535,7 @@ void lv_dropdown_open(lv_obj_t * dropdown_obj) lv_obj_align(label, LV_ALIGN_TOP_RIGHT, 0, 0); break; case LV_TEXT_ALIGN_CENTER: - lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0); break; } diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.h index 63412f73e..0c55e862c 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_dropdown.h @@ -189,6 +189,14 @@ uint16_t lv_dropdown_get_option_cnt(const lv_obj_t * obj); */ void lv_dropdown_get_selected_str(const lv_obj_t * obj, char * buf, uint32_t buf_size); +/** + * Get the index of an option. + * @param obj pointer to drop-down object + * @param option an option as string + * @return index of `option` in the list of all options. -1 if not found. + */ +int32_t lv_dropdown_get_option_index(lv_obj_t * obj, const char * option); + /** * Get the symbol on the drop-down list. Typically a down caret or arrow. * @param obj pointer to drop-down list object diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.c index 7a1f255e9..f47a789e7 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.c @@ -9,6 +9,7 @@ #include "lv_img.h" #if LV_USE_IMG != 0 +#include "../core/lv_disp.h" #include "../misc/lv_assert.h" #include "../draw/lv_img_decoder.h" #include "../misc/lv_fs.h" @@ -134,7 +135,7 @@ void lv_img_set_src(lv_obj_t * obj, const void * src) } if(src_type == LV_IMG_SRC_SYMBOL) { - /*`lv_img_dsc_get_info` couldn't set the with and height of a font so set it here*/ + /*`lv_img_dsc_get_info` couldn't set the width and height of a font so set it here*/ const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN); lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN); @@ -190,16 +191,11 @@ void lv_img_set_angle(lv_obj_t * obj, int16_t angle) lv_img_t * img = (lv_img_t *)obj; if(angle == img->angle) return; - lv_coord_t transf_zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - transf_zoom = ((int32_t)transf_zoom * img->zoom) >> 8; - - lv_coord_t transf_angle = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ lv_coord_t w = lv_obj_get_width(obj); lv_coord_t h = lv_obj_get_height(obj); lv_area_t a; - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle + img->angle, transf_zoom, &img->pivot); + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); a.x1 += obj->coords.x1; a.y1 += obj->coords.y1; a.x2 += obj->coords.x1; @@ -207,9 +203,15 @@ void lv_img_set_angle(lv_obj_t * obj, int16_t angle) lv_obj_invalidate_area(obj, &a); img->angle = angle; - lv_obj_refresh_ext_draw_size(obj); - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle + img->angle, transf_zoom, &img->pivot); + /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate + * the whole ext draw area */ + lv_disp_t * disp = lv_obj_get_disp(obj); + lv_disp_enable_invalidation(disp, false); + lv_obj_refresh_ext_draw_size(obj); + lv_disp_enable_invalidation(disp, true); + + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); a.x1 += obj->coords.x1; a.y1 += obj->coords.y1; a.x2 += obj->coords.x1; @@ -222,17 +224,11 @@ void lv_img_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) lv_img_t * img = (lv_img_t *)obj; if(img->pivot.x == x && img->pivot.y == y) return; - lv_coord_t transf_zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - transf_zoom = ((int32_t)transf_zoom * img->zoom) >> 8; - - lv_coord_t transf_angle = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - transf_angle += img->angle; - lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ lv_coord_t w = lv_obj_get_width(obj); lv_coord_t h = lv_obj_get_height(obj); lv_area_t a; - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle, transf_zoom, &img->pivot); + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); a.x1 += obj->coords.x1; a.y1 += obj->coords.y1; a.x2 += obj->coords.x1; @@ -241,9 +237,15 @@ void lv_img_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y) img->pivot.x = x; img->pivot.y = y; - lv_obj_refresh_ext_draw_size(obj); - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle, transf_zoom, &img->pivot); + /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate + * the whole ext draw area */ + lv_disp_t * disp = lv_obj_get_disp(obj); + lv_disp_enable_invalidation(disp, false); + lv_obj_refresh_ext_draw_size(obj); + lv_disp_enable_invalidation(disp, true); + + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); a.x1 += obj->coords.x1; a.y1 += obj->coords.y1; a.x2 += obj->coords.x1; @@ -258,16 +260,11 @@ void lv_img_set_zoom(lv_obj_t * obj, uint16_t zoom) if(zoom == 0) zoom = 1; - lv_coord_t transf_zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - - lv_coord_t transf_angle = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - transf_angle += img->angle; - lv_obj_update_layout(obj); /*Be sure the object's size is calculated*/ lv_coord_t w = lv_obj_get_width(obj); lv_coord_t h = lv_obj_get_height(obj); lv_area_t a; - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle, ((int32_t)transf_zoom * img->zoom) >> 8, &img->pivot); + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom >> 8, &img->pivot); a.x1 += obj->coords.x1 - 1; a.y1 += obj->coords.y1 - 1; a.x2 += obj->coords.x1 + 1; @@ -275,9 +272,15 @@ void lv_img_set_zoom(lv_obj_t * obj, uint16_t zoom) lv_obj_invalidate_area(obj, &a); img->zoom = zoom; - lv_obj_refresh_ext_draw_size(obj); - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle, ((int32_t)transf_zoom * img->zoom) >> 8, &img->pivot); + /* Disable invalidations because lv_obj_refresh_ext_draw_size would invalidate + * the whole ext draw area */ + lv_disp_t * disp = lv_obj_get_disp(obj); + lv_disp_enable_invalidation(disp, false); + lv_obj_refresh_ext_draw_size(obj); + lv_disp_enable_invalidation(disp, true); + + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); a.x1 += obj->coords.x1 - 1; a.y1 += obj->coords.y1 - 1; a.x2 += obj->coords.x1 + 1; @@ -424,14 +427,10 @@ static lv_point_t lv_img_get_transformed_size(lv_obj_t * obj) { lv_img_t * img = (lv_img_t *)obj; - int32_t zoom_final = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - zoom_final = (zoom_final * img->zoom) >> 8; - int32_t angle_final = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - angle_final += img->angle; lv_area_t area_transform; _lv_img_buf_get_transformed_area(&area_transform, img->w, img->h, - angle_final, zoom_final, &img->pivot); + img->angle, img->zoom, &img->pivot); return (lv_point_t) { lv_area_get_width(&area_transform), lv_area_get_height(&area_transform) @@ -467,42 +466,31 @@ static void lv_img_event(const lv_obj_class_t * class_p, lv_event_t * e) else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { lv_coord_t * s = lv_event_get_param(e); - lv_coord_t transf_zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - transf_zoom = ((int32_t)transf_zoom * img->zoom) >> 8; - - lv_coord_t transf_angle = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - transf_angle += img->angle; /*If the image has angle provide enough room for the rotated corners*/ - if(transf_angle || transf_zoom != LV_IMG_ZOOM_NONE) { + if(img->angle || img->zoom != LV_IMG_ZOOM_NONE) { lv_area_t a; lv_coord_t w = lv_obj_get_width(obj); lv_coord_t h = lv_obj_get_height(obj); - _lv_img_buf_get_transformed_area(&a, w, h, transf_angle, transf_zoom, &img->pivot); - lv_coord_t pad_ori = *s; - *s = LV_MAX(*s, pad_ori - a.x1); - *s = LV_MAX(*s, pad_ori - a.y1); - *s = LV_MAX(*s, pad_ori + a.x2 - w); - *s = LV_MAX(*s, pad_ori + a.y2 - h); + _lv_img_buf_get_transformed_area(&a, w, h, img->angle, img->zoom, &img->pivot); + *s = LV_MAX(*s, -a.x1); + *s = LV_MAX(*s, -a.y1); + *s = LV_MAX(*s, a.x2 - w); + *s = LV_MAX(*s, a.y2 - h); } } else if(code == LV_EVENT_HIT_TEST) { lv_hit_test_info_t * info = lv_event_get_param(e); - lv_coord_t zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - zoom = (zoom * img->zoom) >> 8; - - lv_coord_t angle = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - angle += img->angle; /*If the object is exactly image sized (not cropped, not mosaic) and transformed *perform hit test on its transformed area*/ if(img->w == lv_obj_get_width(obj) && img->h == lv_obj_get_height(obj) && - (zoom != LV_IMG_ZOOM_NONE || angle != 0 || img->pivot.x != img->w / 2 || img->pivot.y != img->h / 2)) { + (img->zoom != LV_IMG_ZOOM_NONE || img->angle != 0 || img->pivot.x != img->w / 2 || img->pivot.y != img->h / 2)) { lv_coord_t w = lv_obj_get_width(obj); lv_coord_t h = lv_obj_get_height(obj); lv_area_t coords; - _lv_img_buf_get_transformed_area(&coords, w, h, angle, zoom, &img->pivot); + _lv_img_buf_get_transformed_area(&coords, w, h, img->angle, img->zoom, &img->pivot); coords.x1 += obj->coords.x1; coords.y1 += obj->coords.y1; coords.x2 += obj->coords.x1; @@ -556,19 +544,13 @@ static void draw_img(lv_event_t * e) return; } - int32_t angle_final = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - angle_final += img->angle; - - if(angle_final != 0) { + if(img->angle != 0) { info->res = LV_COVER_RES_NOT_COVER; return; } - int32_t zoom_final = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - zoom_final = (zoom_final * img->zoom) >> 8; - const lv_area_t * clip_area = lv_event_get_param(e); - if(zoom_final == LV_IMG_ZOOM_NONE) { + if(img->zoom == LV_IMG_ZOOM_NONE) { if(_lv_area_is_in(clip_area, &obj->coords, 0) == false) { info->res = LV_COVER_RES_NOT_COVER; return; @@ -576,7 +558,7 @@ static void draw_img(lv_event_t * e) } else { lv_area_t a; - _lv_img_buf_get_transformed_area(&a, lv_obj_get_width(obj), lv_obj_get_height(obj), 0, zoom_final, &img->pivot); + _lv_img_buf_get_transformed_area(&a, lv_obj_get_width(obj), lv_obj_get_height(obj), 0, img->zoom, &img->pivot); a.x1 += obj->coords.x1; a.y1 += obj->coords.y1; a.x2 += obj->coords.x1; @@ -590,12 +572,6 @@ static void draw_img(lv_event_t * e) } else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST) { - int32_t zoom_final = lv_obj_get_style_transform_zoom(obj, LV_PART_MAIN); - zoom_final = (zoom_final * img->zoom) >> 8; - - int32_t angle_final = lv_obj_get_style_transform_angle(obj, LV_PART_MAIN); - angle_final += img->angle; - lv_coord_t obj_w = lv_obj_get_width(obj); lv_coord_t obj_h = lv_obj_get_height(obj); @@ -616,7 +592,7 @@ static void draw_img(lv_event_t * e) } else { _lv_img_buf_get_transformed_area(&bg_coords, obj_w, obj_h, - angle_final, zoom_final, &bg_pivot); + img->angle, img->zoom, &bg_pivot); /*Modify the coordinates to draw the background for the rotated and scaled coordinates*/ bg_coords.x1 += obj->coords.x1; @@ -636,7 +612,7 @@ static void draw_img(lv_event_t * e) if(code == LV_EVENT_DRAW_MAIN) { if(img->h == 0 || img->w == 0) return; - if(zoom_final == 0) return; + if(img->zoom == 0) return; lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); @@ -666,8 +642,8 @@ static void draw_img(lv_event_t * e) lv_draw_img_dsc_init(&img_dsc); lv_obj_init_draw_img_dsc(obj, LV_PART_MAIN, &img_dsc); - img_dsc.zoom = zoom_final; - img_dsc.angle = angle_final; + img_dsc.zoom = img->zoom; + img_dsc.angle = img->angle; img_dsc.pivot.x = img->pivot.x; img_dsc.pivot.y = img->pivot.y; img_dsc.antialias = img->antialias; diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.h index 2a0056431..eb76c8d98 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_img.h @@ -17,6 +17,11 @@ extern "C" { #if LV_USE_IMG != 0 +/*Testing of dependencies*/ +#if LV_USE_LABEL == 0 +#error "lv_img: lv_label is required. Enable it in lv_conf.h (LV_USE_LABEL 1)" +#endif + #include "../core/lv_obj.h" #include "../misc/lv_fs.h" #include "../draw/lv_draw.h" @@ -109,6 +114,7 @@ void lv_img_set_offset_y(lv_obj_t * obj, lv_coord_t y); /** * Set the rotation angle of the image. * The image will be rotated around the set pivot set by `lv_img_set_pivot()` + * Note that indexed and alpha only images can't be transformed. * @param obj pointer to an image object * @param angle rotation angle in degree with 0.1 degree resolution (0..3600: clock wise) */ @@ -116,7 +122,7 @@ void lv_img_set_angle(lv_obj_t * obj, int16_t angle); /** * Set the rotation center of the image. - * The image will be rotated around this point + * The image will be rotated around this point. * @param obj pointer to an image object * @param x rotation center x of the image * @param y rotation center y of the image @@ -126,6 +132,7 @@ void lv_img_set_pivot(lv_obj_t * obj, lv_coord_t x, lv_coord_t y); /** * Set the zoom factor of the image. + * Note that indexed and alpha only images can't be transformed. * @param img pointer to an image object * @param zoom the zoom factor. * @example 256 or LV_ZOOM_IMG_NONE for no zoom diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.c index 0b8a1657a..f4fbe01a5 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.c @@ -1,4 +1,4 @@ -/** +/** * @file lv_label.c * */ @@ -1021,6 +1021,7 @@ static void lv_label_refr_text(lv_obj_t * obj) } /*In roll inf. mode keep the size but start offset animations*/ else if(label->long_mode == LV_LABEL_LONG_SCROLL_CIRCULAR) { + const lv_anim_t * anim_template = lv_obj_get_style_anim(obj, LV_PART_MAIN); uint16_t anim_speed = lv_obj_get_style_anim_speed(obj, LV_PART_MAIN); if(anim_speed == 0) anim_speed = LV_LABEL_DEF_SCROLL_SPEED; lv_anim_t a; @@ -1055,8 +1056,14 @@ static void lv_label_refr_text(lv_obj_t * obj) lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_x_anim); int32_t act_time = anim_cur ? anim_cur->act_time : 0; - if(act_time < a.time) { - a.act_time = act_time; /*To keep the old position*/ + + /*If a template animation exists, consider it's start delay and repeat delay*/ + if(anim_template) { + a.act_time = anim_template->act_time; + a.repeat_delay = anim_template->repeat_delay; + } + else if(act_time < a.time) { + a.act_time = act_time; /*To keep the old position when the label text is updated mid-scrolling*/ a.early_apply = 0; } @@ -1076,8 +1083,14 @@ static void lv_label_refr_text(lv_obj_t * obj) lv_anim_t * anim_cur = lv_anim_get(obj, set_ofs_y_anim); int32_t act_time = anim_cur ? anim_cur->act_time : 0; - if(act_time < a.time) { - a.act_time = act_time; /*To keep the old position*/ + + /*If a template animation exists, consider it's start delay and repeat delay*/ + if(anim_template) { + a.act_time = anim_template->act_time; + a.repeat_delay = anim_template->repeat_delay; + } + else if(act_time < a.time) { + a.act_time = act_time; /*To keep the old position when the label text is updated mid-scrolling*/ a.early_apply = 0; } diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.h index 342e00417..b31a63e59 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_label.h @@ -142,7 +142,7 @@ void lv_label_set_text_sel_start(lv_obj_t * obj, uint32_t index); /** * Set where text selection should end * @param obj pointer to a label object - * @param index character index where selection should end. `LV_LABEL_TEXT_SELECTION_OFF` for no selection + * @param index character index where selection should end. `LV_LABEL_TEXT_SELECTION_OFF` for no selection */ void lv_label_set_text_sel_end(lv_obj_t * obj, uint32_t index); diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_line.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_line.c index 6bb6cd975..df32bd051 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_line.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_line.c @@ -83,7 +83,7 @@ void lv_line_set_y_invert(lv_obj_t * obj, bool en) lv_line_t * line = (lv_line_t *)obj; if(line->y_inv == en) return; - line->y_inv = en == false ? 0 : 1; + line->y_inv = en ? 1U : 0U; lv_obj_invalidate(obj); } @@ -98,7 +98,7 @@ bool lv_line_get_y_invert(const lv_obj_t * obj) lv_line_t * line = (lv_line_t *)obj; - return line->y_inv == 0 ? false : true; + return line->y_inv == 1U; } /********************** @@ -143,22 +143,23 @@ static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e) else if(code == LV_EVENT_GET_SELF_SIZE) { lv_line_t * line = (lv_line_t *)obj; + if(line->point_num == 0 || line->point_array == NULL) return; + lv_point_t * p = lv_event_get_param(e); lv_coord_t w = 0; lv_coord_t h = 0; - if(line->point_num > 0) { - uint16_t i; - for(i = 0; i < line->point_num; i++) { - w = LV_MAX(line->point_array[i].x, w); - h = LV_MAX(line->point_array[i].y, h); - } - lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN); - w += line_width; - h += line_width; - p->x = w; - p->y = h; + uint16_t i; + for(i = 0; i < line->point_num; i++) { + w = LV_MAX(line->point_array[i].x, w); + h = LV_MAX(line->point_array[i].y, h); } + + lv_coord_t line_width = lv_obj_get_style_line_width(obj, LV_PART_MAIN); + w += line_width; + h += line_width; + p->x = w; + p->y = h; } else if(code == LV_EVENT_DRAW_MAIN) { lv_line_t * line = (lv_line_t *)obj; @@ -170,18 +171,17 @@ static void lv_line_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_obj_get_coords(obj, &area); lv_coord_t x_ofs = area.x1 - lv_obj_get_scroll_x(obj); lv_coord_t y_ofs = area.y1 - lv_obj_get_scroll_y(obj); - lv_point_t p1; - lv_point_t p2; lv_coord_t h = lv_obj_get_height(obj); - uint16_t i; lv_draw_line_dsc_t line_dsc; lv_draw_line_dsc_init(&line_dsc); lv_obj_init_draw_line_dsc(obj, LV_PART_MAIN, &line_dsc); /*Read all points and draw the lines*/ + uint16_t i; for(i = 0; i < line->point_num - 1; i++) { - + lv_point_t p1; + lv_point_t p2; p1.x = line->point_array[i].x + x_ofs; p2.x = line->point_array[i + 1].x + x_ofs; diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.c index d66f8595f..fd9b3948f 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.c @@ -161,17 +161,20 @@ void lv_roller_set_selected(lv_obj_t * obj, uint16_t sel_opt, lv_anim_enable_t a /*In infinite mode interpret the new ID relative to the currently visible "page"*/ if(roller->mode == LV_ROLLER_MODE_INFINITE) { - int32_t sel_opt_signed = sel_opt; - uint16_t page = roller->sel_opt_id / LV_ROLLER_INF_PAGES; - - /*`sel_opt` should be less than the number of options set by the user. - *If it's more then probably it's a reference from not the first page - *so normalize `sel_opt`*/ - if(page != 0) { - sel_opt_signed -= page * LV_ROLLER_INF_PAGES; + uint32_t real_option_cnt = roller->option_cnt / LV_ROLLER_INF_PAGES; + uint16_t current_page = roller->sel_opt_id / real_option_cnt; + /*Set by the user to e.g. 0, 1, 2, 3... + *Upscale the value to the current page*/ + if(sel_opt < real_option_cnt) { + uint16_t act_opt = roller->sel_opt_id - current_page * real_option_cnt; + int32_t sel_opt_signed = sel_opt; + /*Huge jump? Probably from last to first or first to last option.*/ + if(LV_ABS((int16_t)act_opt - sel_opt) > real_option_cnt / 2) { + if(act_opt > sel_opt) sel_opt_signed += real_option_cnt; + else sel_opt_signed -= real_option_cnt; + } + sel_opt = sel_opt_signed + real_option_cnt * current_page; } - - sel_opt = page * LV_ROLLER_INF_PAGES + sel_opt_signed; } roller->sel_opt_id = sel_opt < roller->option_cnt ? sel_opt : roller->option_cnt - 1; diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.h index d90001d91..14411dea6 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_roller.h @@ -17,6 +17,11 @@ extern "C" { #if LV_USE_ROLLER != 0 +/*Testing of dependencies*/ +#if LV_USE_LABEL == 0 +#error "lv_roller: lv_label is required. Enable it in lv_conf.h (LV_USE_ROLLER 1)" +#endif + #include "../core/lv_obj.h" #include "lv_label.h" diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_slider.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_slider.c index 82f6feea3..faee0faa8 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_slider.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_slider.c @@ -22,7 +22,7 @@ *********************/ #define MY_CLASS &lv_slider_class -#define LV_SLIDER_KNOB_COORD(hor, is_rtl, area) (hor ? (is_rtl ? area.x1 : area.x2) : (is_rtl ? area.y1 : area.y2)) +#define LV_SLIDER_KNOB_COORD(is_rtl, area) (is_rtl ? area.x1 : area.x2) /********************** * TYPEDEFS @@ -33,8 +33,9 @@ **********************/ static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e); -static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, lv_coord_t knob_size, bool hor); +static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor); static void draw_knob(lv_event_t * e); +static bool is_slider_horizontal(lv_obj_t * obj); /********************** * STATIC VARIABLES @@ -83,8 +84,8 @@ static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj /*Initialize the allocated 'slider'*/ slider->value_to_set = NULL; - slider->dragging = 0; - slider->left_knob_focus = 0; + slider->dragging = 0U; + slider->left_knob_focus = 0U; lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN); lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); @@ -194,40 +195,40 @@ static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_point_t p; lv_indev_get_point(indev, &p); - lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); - - lv_coord_t w = lv_obj_get_width(obj); - lv_coord_t h = lv_obj_get_height(obj); - - lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); - lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); - lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); - lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); - - int32_t range = slider->bar.max_value - slider->bar.min_value; int32_t new_value = 0; - int32_t real_max_value = slider->bar.max_value; - int32_t real_min_value = slider->bar.min_value; - if(w >= h) { - lv_coord_t indic_w = w - bg_left - bg_right; - if(base_dir == LV_BASE_DIR_RTL) { - new_value = (obj->coords.x2 - bg_right) - p.x; /*Make the point relative to the indicator*/ + const int32_t range = slider->bar.max_value - slider->bar.min_value; + if(is_slider_horizontal(obj)) { + const lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); + const lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); + const lv_coord_t w = lv_obj_get_width(obj); + const lv_coord_t indic_w = w - bg_left - bg_right; + + if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) { + /*Make the point relative to the indicator*/ + new_value = (obj->coords.x2 - bg_right) - p.x; } else { - new_value = p.x - (obj->coords.x1 + bg_left); /*Make the point relative to the indicator*/ + /*Make the point relative to the indicator*/ + new_value = p.x - (obj->coords.x1 + bg_left); } new_value = (new_value * range) / indic_w; new_value += slider->bar.min_value; } else { - lv_coord_t indic_h = h - bg_bottom - bg_top; - new_value = p.y - (obj->coords.y2 + bg_bottom); /*Make the point relative to the indicator*/ + const lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); + const lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); + const lv_coord_t h = lv_obj_get_height(obj); + const lv_coord_t indic_h = h - bg_bottom - bg_top; + + /*Make the point relative to the indicator*/ + new_value = p.y - (obj->coords.y2 + bg_bottom); new_value = (-new_value * range) / indic_h; new_value += slider->bar.min_value; - } + int32_t real_max_value = slider->bar.max_value; + int32_t real_min_value = slider->bar.min_value; /*Figure out the min. and max. for this mode*/ if(slider->value_to_set == &slider->bar.start_value) { real_max_value = slider->bar.cur_value; @@ -236,8 +237,8 @@ static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e) real_min_value = slider->bar.start_value; } - if(new_value < real_min_value) new_value = real_min_value; - else if(new_value > real_max_value) new_value = real_max_value; + new_value = LV_CLAMP(real_min_value, new_value, real_max_value); + if(*slider->value_to_set != new_value) { *slider->value_to_set = new_value; lv_obj_invalidate(obj); @@ -308,17 +309,17 @@ static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e) if(c == LV_KEY_RIGHT || c == LV_KEY_UP) { if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + 1, LV_ANIM_ON); else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON); - - res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); - if(res != LV_RES_OK) return; } else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) { if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) - 1, LV_ANIM_ON); else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) - 1, LV_ANIM_ON); - - res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); - if(res != LV_RES_OK) return; } + else { + return; + } + + res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); + if(res != LV_RES_OK) return; } else if(code == LV_EVENT_DRAW_MAIN) { draw_knob(e); @@ -330,51 +331,32 @@ static void draw_knob(lv_event_t * e) lv_obj_t * obj = lv_event_get_target(e); lv_slider_t * slider = (lv_slider_t *)obj; lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); - lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); - lv_coord_t objw = lv_obj_get_width(obj); - lv_coord_t objh = lv_obj_get_height(obj); - bool hor = objw >= objh ? true : false; - lv_coord_t knob_size = hor ? objh : objw; - bool sym = false; - if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 && slider->bar.max_value > 0) sym = true; + const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN); + const bool is_horizontal = is_slider_horizontal(obj); lv_area_t knob_area; - - /*Horizontal*/ - if(hor) { - if(!sym) { - knob_area.x1 = LV_SLIDER_KNOB_COORD(hor, base_dir == LV_BASE_DIR_RTL, slider->bar.indic_area); - } - else { - if(slider->bar.cur_value >= 0) { - knob_area.x1 = LV_SLIDER_KNOB_COORD(hor, base_dir == LV_BASE_DIR_RTL, slider->bar.indic_area); - } - else { - knob_area.x1 = LV_SLIDER_KNOB_COORD(hor, base_dir != LV_BASE_DIR_RTL, slider->bar.indic_area); - } - } + lv_coord_t knob_size; + if(is_horizontal) { + knob_size = lv_obj_get_height(obj); + knob_area.x1 = LV_SLIDER_KNOB_COORD(is_rtl, slider->bar.indic_area); } - /*Vertical*/ else { - if(!sym) { - knob_area.y1 = slider->bar.indic_area.y1; - } - else { - if(slider->bar.cur_value >= 0) { - knob_area.y1 = slider->bar.indic_area.y1; - } - else { - knob_area.y1 = slider->bar.indic_area.y2; - } - } + bool is_symmetrical = false; + if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 && + slider->bar.max_value > 0) is_symmetrical = true; + + knob_size = lv_obj_get_width(obj); + if(is_symmetrical && slider->bar.cur_value < 0) knob_area.y1 = slider->bar.indic_area.y2; + else knob_area.y1 = slider->bar.indic_area.y1; } lv_draw_rect_dsc_t knob_rect_dsc; lv_draw_rect_dsc_init(&knob_rect_dsc); lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc); - - position_knob(obj, &knob_area, knob_size, hor); + /* Update knob area with knob style */ + position_knob(obj, &knob_area, knob_size, is_horizontal); + /* Update right knob area with calculated knob area */ lv_area_copy(&slider->right_knob_area, &knob_area); lv_obj_draw_part_dsc_t part_draw_dsc; @@ -395,19 +377,20 @@ static void draw_knob(lv_event_t * e) /*Save the draw part_draw_dsc. because it can be modified in the event*/ lv_draw_rect_dsc_t knob_rect_dsc_tmp; lv_memcpy(&knob_rect_dsc_tmp, &knob_rect_dsc, sizeof(lv_draw_rect_dsc_t)); - + /* Draw the right knob */ lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area); lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); - /*Draw a second knob for the start_value side*/ - if(hor) { - knob_area.x1 = LV_SLIDER_KNOB_COORD(hor, base_dir != LV_BASE_DIR_RTL, slider->bar.indic_area); + /*Calculate the second knob area*/ + if(is_horizontal) { + /*use !is_rtl to get the other knob*/ + knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_rtl, slider->bar.indic_area); } else { knob_area.y1 = slider->bar.indic_area.y2; } - position_knob(obj, &knob_area, knob_size, hor); + position_knob(obj, &knob_area, knob_size, is_horizontal); lv_area_copy(&slider->left_knob_area, &knob_area); lv_memcpy(&knob_rect_dsc, &knob_rect_dsc_tmp, sizeof(lv_draw_rect_dsc_t)); @@ -422,9 +405,8 @@ static void draw_knob(lv_event_t * e) } } -static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, lv_coord_t knob_size, bool hor) +static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor) { - if(hor) { knob_area->x1 -= (knob_size >> 1); knob_area->x2 = knob_area->x1 + knob_size - 1; @@ -438,9 +420,9 @@ static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, lv_coord_t knob knob_area->x2 = obj->coords.x2; } - lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB); - lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB); - lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB); + lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB); + lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB); + lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB); lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB); lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB); @@ -453,4 +435,9 @@ static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, lv_coord_t knob knob_area->y2 += knob_bottom + transf_h; } +static bool is_slider_horizontal(lv_obj_t * obj) +{ + return lv_obj_get_width(obj) >= lv_obj_get_height(obj); +} + #endif diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_table.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_table.c index 1b032156e..5ff65ab23 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_table.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_table.c @@ -36,8 +36,17 @@ static void draw_main(lv_event_t * e); static lv_coord_t get_row_height(lv_obj_t * obj, uint16_t row_id, const lv_font_t * font, lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t cell_left, lv_coord_t cell_right, lv_coord_t cell_top, lv_coord_t cell_bottom); -static void refr_size(lv_obj_t * obj, uint32_t strat_row); +static void refr_size_form_row(lv_obj_t * obj, uint32_t start_row); +static void refr_cell_size(lv_obj_t * obj, uint32_t row, uint32_t col); static lv_res_t get_pressed_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col); +static size_t get_cell_txt_len(const char * txt); +static void copy_cell_txt(char * dst, const char * txt); +static void get_cell_area(lv_obj_t * obj, uint16_t row, uint16_t col, lv_area_t * area); + +static inline bool is_cell_empty(void * cell) +{ + return cell == NULL; +} /********************** * STATIC VARIABLES @@ -90,25 +99,16 @@ void lv_table_set_cell_value(lv_obj_t * obj, uint16_t row, uint16_t col, const c /*Save the control byte*/ if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; -#if LV_USE_ARABIC_PERSIAN_CHARS - /*Get the size of the Arabic text and process it*/ - size_t len_ap = _lv_txt_ap_calc_bytes_cnt(txt); - table->cell_data[cell] = lv_mem_realloc(table->cell_data[cell], len_ap + 1); + size_t to_allocate = get_cell_txt_len(txt); + + table->cell_data[cell] = lv_mem_realloc(table->cell_data[cell], to_allocate); LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) return; - _lv_txt_ap_proc(txt, &table->cell_data[cell][1]); -#else - table->cell_data[cell] = lv_mem_realloc(table->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/ - LV_ASSERT_MALLOC(table->cell_data[cell]); - - strcpy(table->cell_data[cell] + 1, txt); /*+1 to skip the format byte*/ -#endif + copy_cell_txt(table->cell_data[cell], txt); table->cell_data[cell][0] = ctrl; - refr_size(obj, row); - - lv_obj_invalidate(obj); + refr_cell_size(obj, row, col); } void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint16_t row, uint16_t col, const char * fmt, ...) @@ -118,8 +118,7 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint16_t row, uint16_t col, con lv_table_t * table = (lv_table_t *)obj; if(col >= table->col_cnt) { - LV_LOG_WARN("lv_table_set_cell_value: invalid column"); - return; + lv_table_set_col_cnt(obj, col + 1); } /*Auto expand*/ @@ -180,26 +179,7 @@ void lv_table_set_cell_value_fmt(lv_obj_t * obj, uint16_t row, uint16_t col, con table->cell_data[cell][0] = ctrl; - /*Refresh the row height*/ - lv_coord_t cell_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); - lv_coord_t cell_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); - lv_coord_t cell_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); - lv_coord_t cell_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); - - lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_ITEMS); - lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_ITEMS); - const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); - - lv_coord_t h = get_row_height(obj, row, font, letter_space, line_space, - cell_left, cell_right, cell_top, cell_bottom); - - - lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); - lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); - - table->row_h[row] = LV_CLAMP(minh, h, maxh); - - lv_obj_invalidate(obj); + refr_cell_size(obj, row, col); } void lv_table_set_row_cnt(lv_obj_t * obj, uint16_t row_cnt) @@ -207,6 +187,9 @@ void lv_table_set_row_cnt(lv_obj_t * obj, uint16_t row_cnt) LV_ASSERT_OBJ(obj, MY_CLASS); lv_table_t * table = (lv_table_t *)obj; + + if(table->row_cnt == row_cnt) return; + uint16_t old_row_cnt = table->row_cnt; table->row_cnt = row_cnt; @@ -235,7 +218,7 @@ void lv_table_set_row_cnt(lv_obj_t * obj, uint16_t row_cnt) lv_memset_00(&table->cell_data[old_cell_cnt], (new_cell_cnt - old_cell_cnt) * sizeof(table->cell_data[0])); } - refr_size(obj, 0) ; + refr_size_form_row(obj, 0); } void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt) @@ -243,35 +226,18 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt) LV_ASSERT_OBJ(obj, MY_CLASS); lv_table_t * table = (lv_table_t *)obj; + + if(table->col_cnt == col_cnt) return; + uint16_t old_col_cnt = table->col_cnt; table->col_cnt = col_cnt; - table->col_w = lv_mem_realloc(table->col_w, col_cnt * sizeof(table->row_h[0])); - LV_ASSERT_MALLOC(table->col_w); - if(table->col_w == NULL) return; - - /*Free the unused cells*/ - if(old_col_cnt > col_cnt) { - uint16_t old_cell_cnt = old_col_cnt * table->row_cnt; - uint32_t new_cell_cnt = table->col_cnt * table->row_cnt; - uint32_t i; - for(i = new_cell_cnt; i < old_cell_cnt; i++) { - lv_mem_free(table->cell_data[i]); - } - } char ** new_cell_data = lv_mem_alloc(table->row_cnt * table->col_cnt * sizeof(char *)); LV_ASSERT_MALLOC(new_cell_data); if(new_cell_data == NULL) return; uint32_t new_cell_cnt = table->col_cnt * table->row_cnt; - lv_memset_00(new_cell_data, new_cell_cnt * sizeof(table->cell_data[0])); - /*Initialize the new fields*/ - if(old_col_cnt < col_cnt) { - uint32_t col; - for(col = old_col_cnt; col < col_cnt; col++) { - table->col_w[col] = LV_DPI_DEF; - } - } + lv_memset_00(new_cell_data, new_cell_cnt * sizeof(table->cell_data[0])); /*The new column(s) messes up the mapping of `cell_data`*/ uint32_t old_col_start; @@ -284,13 +250,31 @@ void lv_table_set_col_cnt(lv_obj_t * obj, uint16_t col_cnt) lv_memcpy_small(&new_cell_data[new_col_start], &table->cell_data[old_col_start], sizeof(new_cell_data[0]) * min_col_cnt); + + /*Free the old cells (only if the table becomes smaller)*/ + int32_t i; + for(i = 0; i < (int32_t)old_col_cnt - col_cnt; i++) { + uint32_t idx = old_col_start + min_col_cnt + i; + lv_mem_free(table->cell_data[idx]); + table->cell_data[idx] = NULL; + } } lv_mem_free(table->cell_data); table->cell_data = new_cell_data; + /*Initialize the new column widths if any*/ + table->col_w = lv_mem_realloc(table->col_w, col_cnt * sizeof(table->col_w[0])); + LV_ASSERT_MALLOC(table->col_w); + if(table->col_w == NULL) return; - refr_size(obj, 0) ; + uint32_t col; + for(col = old_col_cnt; col < col_cnt; col++) { + table->col_w[col] = LV_DPI_DEF; + } + + + refr_size_form_row(obj, 0) ; } void lv_table_set_col_width(lv_obj_t * obj, uint16_t col_id, lv_coord_t w) @@ -303,7 +287,7 @@ void lv_table_set_col_width(lv_obj_t * obj, uint16_t col_id, lv_coord_t w) if(col_id >= table->col_cnt) lv_table_set_col_cnt(obj, col_id + 1); table->col_w[col_id] = w; - refr_size(obj, 0) ; + refr_size_form_row(obj, 0); } void lv_table_add_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table_cell_ctrl_t ctrl) @@ -318,7 +302,7 @@ void lv_table_add_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table uint32_t cell = row * table->col_cnt + col; - if(table->cell_data[cell] == NULL) { + if(is_cell_empty(table->cell_data[cell])) { table->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) return; @@ -342,7 +326,7 @@ void lv_table_clear_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_tab uint32_t cell = row * table->col_cnt + col; - if(table->cell_data[cell] == NULL) { + if(is_cell_empty(table->cell_data[cell])) { table->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ LV_ASSERT_MALLOC(table->cell_data[cell]); if(table->cell_data[cell] == NULL) return; @@ -364,12 +348,12 @@ const char * lv_table_get_cell_value(lv_obj_t * obj, uint16_t row, uint16_t col) lv_table_t * table = (lv_table_t *)obj; if(row >= table->row_cnt || col >= table->col_cnt) { - LV_LOG_WARN("lv_table_set_cell_value: invalid row or column"); + LV_LOG_WARN("invalid row or column"); return ""; } uint32_t cell = row * table->col_cnt + col; - if(table->cell_data[cell] == NULL) return ""; + if(is_cell_empty(table->cell_data[cell])) return ""; return &table->cell_data[cell][1]; /*Skip the format byte*/ } @@ -415,8 +399,8 @@ bool lv_table_has_cell_ctrl(lv_obj_t * obj, uint16_t row, uint16_t col, lv_table } uint32_t cell = row * table->col_cnt + col; - if(table->cell_data[cell] == NULL) return false; - else return (table->cell_data[cell][0] & ctrl) == ctrl ? true : false; + if(is_cell_empty(table->cell_data[cell])) return false; + else return (table->cell_data[cell][0] & ctrl) == ctrl; } void lv_table_get_selected_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col) @@ -482,7 +466,7 @@ static void lv_table_event(const lv_obj_class_t * class_p, lv_event_t * e) lv_table_t * table = (lv_table_t *)obj; if(code == LV_EVENT_STYLE_CHANGED) { - refr_size(obj, 0); + refr_size_form_row(obj, 0); } else if(code == LV_EVENT_GET_SELF_SIZE) { lv_point_t * p = lv_event_get_param(e); @@ -599,8 +583,6 @@ static void draw_main(lv_event_t * e) lv_point_t txt_size; lv_area_t cell_area; - lv_area_t txt_area; - lv_text_flag_t txt_flags; lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN); lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); @@ -608,11 +590,6 @@ static void draw_main(lv_event_t * e) lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); - lv_coord_t cell_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); - lv_coord_t cell_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); - lv_coord_t cell_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); - lv_coord_t cell_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); - lv_state_t state_ori = obj->state; obj->state = LV_STATE_DEFAULT; obj->skip_trans = 1; @@ -634,7 +611,7 @@ static void draw_main(lv_event_t * e) cell_area.y2 = obj->coords.y1 + bg_top - 1 - lv_obj_get_scroll_y(obj) + border_width; lv_coord_t scroll_x = lv_obj_get_scroll_x(obj) ; - bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL ? true : false; + bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL; /*Handle custom drawer*/ lv_obj_draw_part_dsc_t part_draw_dsc; @@ -671,15 +648,16 @@ static void draw_main(lv_event_t * e) uint16_t col_merge = 0; for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { - if(table->cell_data[cell + col_merge]) { - char * next_cell_data = table->cell_data[cell + col_merge]; - if(next_cell_data) ctrl = next_cell_data[0]; - if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) - if(rtl) cell_area.x1 -= table->col_w[col + col_merge + 1]; - else cell_area.x2 += table->col_w[col + col_merge + 1]; - else { - break; - } + char * next_cell_data = table->cell_data[cell + col_merge]; + + if(is_cell_empty(next_cell_data)) break; + + lv_table_cell_ctrl_t merge_ctrl = (lv_table_cell_ctrl_t) next_cell_data[0]; + if(merge_ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) { + lv_coord_t offset = table->col_w[col + col_merge + 1]; + + if(rtl) cell_area.x1 -= offset; + else cell_area.x2 += offset; } else { break; @@ -741,6 +719,13 @@ static void draw_main(lv_event_t * e) lv_draw_rect(draw_ctx, &rect_dsc_act, &cell_area_border); if(table->cell_data[cell]) { + const lv_coord_t cell_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); + const lv_coord_t cell_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); + const lv_coord_t cell_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); + const lv_coord_t cell_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); + lv_text_flag_t txt_flags = LV_TEXT_FLAG_NONE; + lv_area_t txt_area; + txt_area.x1 = cell_area.x1 + cell_left; txt_area.x2 = cell_area.x2 - cell_right; txt_area.y1 = cell_area.y1 + cell_top; @@ -749,7 +734,6 @@ static void draw_main(lv_event_t * e) /*Align the content to the middle if not cropped*/ bool crop = ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP ? true : false; if(crop) txt_flags = LV_TEXT_FLAG_EXPAND; - else txt_flags = LV_TEXT_FLAG_NONE; lv_txt_get_size(&txt_size, table->cell_data[cell] + 1, label_dsc_def.font, label_dsc_act.letter_space, label_dsc_act.line_space, @@ -781,31 +765,66 @@ static void draw_main(lv_event_t * e) draw_ctx->clip_area = clip_area_ori; } -static void refr_size(lv_obj_t * obj, uint32_t strat_row) +/* Refreshes size of the table starting from @start_row row */ +static void refr_size_form_row(lv_obj_t * obj, uint32_t start_row) { - lv_table_t * table = (lv_table_t *)obj; - - uint32_t i; - - lv_coord_t cell_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); - lv_coord_t cell_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); - lv_coord_t cell_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); - lv_coord_t cell_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_ITEMS); lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_ITEMS); const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); - lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); - lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); + const lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); + const lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); - for(i = strat_row; i < table->row_cnt; i++) { - table->row_h[i] = get_row_height(obj, i, font, letter_space, line_space, - cell_left, cell_right, cell_top, cell_bottom); - table->row_h[i] = LV_CLAMP(minh, table->row_h[i], maxh); + lv_table_t * table = (lv_table_t *)obj; + uint32_t i; + for(i = start_row; i < table->row_cnt; i++) { + lv_coord_t calculated_height = get_row_height(obj, i, font, letter_space, line_space, + cell_pad_left, cell_pad_right, cell_pad_top, cell_pad_bottom); + table->row_h[i] = LV_CLAMP(minh, calculated_height, maxh); } - lv_obj_refresh_self_size(obj) ; + lv_obj_refresh_self_size(obj); + lv_obj_invalidate(obj); +} + + +static void refr_cell_size(lv_obj_t * obj, uint32_t row, uint32_t col) +{ + const lv_coord_t cell_pad_left = lv_obj_get_style_pad_left(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_right = lv_obj_get_style_pad_right(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_top = lv_obj_get_style_pad_top(obj, LV_PART_ITEMS); + const lv_coord_t cell_pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_ITEMS); + + lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_ITEMS); + lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_ITEMS); + const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_ITEMS); + + const lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_ITEMS); + const lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_ITEMS); + + lv_table_t * table = (lv_table_t *)obj; + lv_coord_t calculated_height = get_row_height(obj, row, font, letter_space, line_space, + cell_pad_left, cell_pad_right, cell_pad_top, cell_pad_bottom); + + lv_coord_t prev_row_size = table->row_h[row]; + table->row_h[row] = LV_CLAMP(minh, calculated_height, maxh); + + /*If the row height havn't changed invalidate only this cell*/ + if(prev_row_size == table->row_h[row]) { + lv_area_t cell_area; + get_cell_area(obj, row, col, &cell_area); + lv_area_move(&cell_area, obj->coords.x1, obj->coords.y1); + lv_obj_invalidate_area(obj, &cell_area); + } + else { + lv_obj_refresh_self_size(obj); + lv_obj_invalidate(obj); + } } static lv_coord_t get_row_height(lv_obj_t * obj, uint16_t row_id, const lv_font_t * font, @@ -813,54 +832,61 @@ static lv_coord_t get_row_height(lv_obj_t * obj, uint16_t row_id, const lv_font_ lv_coord_t cell_left, lv_coord_t cell_right, lv_coord_t cell_top, lv_coord_t cell_bottom) { lv_table_t * table = (lv_table_t *)obj; - lv_point_t txt_size; - lv_coord_t txt_w; + lv_coord_t h_max = lv_font_get_line_height(font) + cell_top + cell_bottom; + /* Calculate the cell_data index where to start */ uint16_t row_start = row_id * table->col_cnt; + + /* Traverse the cells in the row_id row */ uint16_t cell; uint16_t col; - lv_coord_t h_max = lv_font_get_line_height(font) + cell_top + cell_bottom; - for(cell = row_start, col = 0; cell < row_start + table->col_cnt; cell++, col++) { - if(table->cell_data[cell] != NULL) { - txt_w = table->col_w[col]; - uint16_t col_merge = 0; - for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { + char * cell_data = table->cell_data[cell]; - if(table->cell_data[cell + col_merge] != NULL) { - lv_table_cell_ctrl_t ctrl = 0; - char * next_cell_data = table->cell_data[cell + col_merge]; - if(next_cell_data) ctrl = next_cell_data[0]; - if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) - txt_w += table->col_w[col + col_merge + 1]; - else - break; - } - else { - break; - } + if(is_cell_empty(cell_data)) { + continue; + } + + lv_coord_t txt_w = table->col_w[col]; + + /* Traverse the current row from the first until the penultimate column. + * Increment the text width if the cell has the LV_TABLE_CELL_CTRL_MERGE_RIGHT control, + * exit the traversal when the current cell control is not LV_TABLE_CELL_CTRL_MERGE_RIGHT */ + uint16_t col_merge = 0; + for(col_merge = 0; col_merge + col < table->col_cnt - 1; col_merge++) { + char * next_cell_data = table->cell_data[cell + col_merge]; + + if(is_cell_empty(next_cell_data)) break; + + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) next_cell_data[0]; + if(ctrl & LV_TABLE_CELL_CTRL_MERGE_RIGHT) { + txt_w += table->col_w[col + col_merge + 1]; } - - lv_table_cell_ctrl_t ctrl = 0; - if(table->cell_data[cell]) ctrl = table->cell_data[cell][0]; - - /*With text crop assume 1 line*/ - if(ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) { - h_max = LV_MAX(lv_font_get_line_height(font) + cell_top + cell_bottom, - h_max); - } - /*Without text crop calculate the height of the text in the cell*/ else { - txt_w -= cell_left + cell_right; - - lv_txt_get_size(&txt_size, table->cell_data[cell] + 1, font, - letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE); - - h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max); - cell += col_merge; - col += col_merge; + break; } } + + lv_table_cell_ctrl_t ctrl = (lv_table_cell_ctrl_t) cell_data[0]; + + /*When cropping the text we can assume the row height is equal to the line height*/ + if(ctrl & LV_TABLE_CELL_CTRL_TEXT_CROP) { + h_max = LV_MAX(lv_font_get_line_height(font) + cell_top + cell_bottom, + h_max); + } + /*Else we have to calculate the height of the cell text*/ + else { + lv_point_t txt_size; + txt_w -= cell_left + cell_right; + + lv_txt_get_size(&txt_size, table->cell_data[cell] + 1, font, + letter_space, line_space, txt_w, LV_TEXT_FLAG_NONE); + + h_max = LV_MAX(txt_size.y + cell_top + cell_bottom, h_max); + /*Skip until one element after the last merged column*/ + cell += col_merge; + col += col_merge; + } } return h_max; @@ -917,5 +943,65 @@ static lv_res_t get_pressed_cell(lv_obj_t * obj, uint16_t * row, uint16_t * col) return LV_RES_OK; } +/* Returns number of bytes to allocate based on chars configuration */ +static size_t get_cell_txt_len(const char * txt) +{ + size_t retval = 0; + +#if LV_USE_ARABIC_PERSIAN_CHARS + retval = _lv_txt_ap_calc_bytes_cnt(txt) + 1; +#else + /* cell_data layout: [ctrl][txt][trailing '\0' terminator] + * +2 because of the trailing '\0' and the ctrl */ + retval = strlen(txt) + 2; +#endif + + return retval; +} + +/* Copy txt into dst skipping the format byte */ +static void copy_cell_txt(char * dst, const char * txt) +{ +#if LV_USE_ARABIC_PERSIAN_CHARS + _lv_txt_ap_proc(txt, &dst[1]); +#else + strcpy(&dst[1], txt); +#endif +} + +static void get_cell_area(lv_obj_t * obj, uint16_t row, uint16_t col, lv_area_t * area) +{ + lv_table_t * table = (lv_table_t *)obj; + + uint32_t c; + area->x1 = 0; + for(c = 0; c < col; c++) { + area->x1 += table->col_w[c]; + } + + bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL; + if(rtl) { + area->x1 += lv_obj_get_scroll_x(obj); + lv_coord_t w = lv_obj_get_width(obj); + area->x2 = w - area->x1 - lv_obj_get_style_pad_right(obj, 0); + area->x1 = area->x2 - table->col_w[col]; + } + else { + area->x1 -= lv_obj_get_scroll_x(obj); + area->x1 += lv_obj_get_style_pad_left(obj, 0); + area->x2 = area->x1 + table->col_w[col] - 1; + } + + uint32_t r; + area->y1 = 0; + for(r = 0; r < row; r++) { + area->y1 += table->row_h[r]; + } + + area->y1 += lv_obj_get_style_pad_top(obj, 0); + area->y1 -= lv_obj_get_scroll_y(obj); + area->y2 = area->y1 + table->row_h[row] - 1; + +} #endif diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.c b/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.c index 0dff383d7..4d497e669 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.c +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.c @@ -34,6 +34,7 @@ #endif #define LV_TEXTAREA_PWD_BULLET_UNICODE 0x2022 +#define IGNORE_KERNING '\0' /********************** * TYPEDEFS @@ -57,6 +58,8 @@ static void update_cursor_position_on_click(lv_event_t * e); static lv_res_t insert_handler(lv_obj_t * obj, const char * txt); static void draw_placeholder(lv_event_t * e); static void draw_cursor(lv_event_t * e); +static void auto_hide_characters(lv_obj_t * obj); +static inline bool is_valid_but_non_printable_char(const uint32_t letter); /********************** * STATIC VARIABLES @@ -100,13 +103,16 @@ void lv_textarea_add_char(lv_obj_t * obj, uint32_t c) lv_textarea_t * ta = (lv_textarea_t *)obj; - const char * letter_buf; + if(ta->one_line && (c == '\n' || c == '\r')) { + LV_LOG_INFO("Text area: line break ignored in one-line mode"); + return; + } uint32_t u32_buf[2]; u32_buf[0] = c; u32_buf[1] = 0; - letter_buf = (char *)&u32_buf; + const char * letter_buf = (char *)&u32_buf; #if LV_BIG_ENDIAN_SYSTEM if(c != 0) while(*letter_buf == 0) ++letter_buf; @@ -115,11 +121,6 @@ void lv_textarea_add_char(lv_obj_t * obj, uint32_t c) lv_res_t res = insert_handler(obj, letter_buf); if(res != LV_RES_OK) return; - if(ta->one_line && (c == '\n' || c == '\r')) { - LV_LOG_INFO("Text area: line break ignored in one-line mode"); - return; - } - uint32_t c_uni = _lv_txt_encoded_next((const char *)&c, NULL); if(char_is_accepted(obj, c_uni) == false) { @@ -127,7 +128,7 @@ void lv_textarea_add_char(lv_obj_t * obj, uint32_t c) return; } - if(ta->pwd_mode != 0) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/ + if(ta->pwd_mode) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/ /*If the textarea is empty, invalidate it to hide the placeholder*/ if(ta->placeholder_txt) { @@ -136,30 +137,19 @@ void lv_textarea_add_char(lv_obj_t * obj, uint32_t c) } lv_label_ins_text(ta->label, ta->cursor.pos, letter_buf); /*Insert the character*/ - lv_textarea_clear_selection(obj); /*Clear selection*/ + lv_textarea_clear_selection(obj); /*Clear selection*/ - if(ta->pwd_mode != 0) { - ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + strlen(letter_buf) + 1); /*+2: the new char + \0*/ + if(ta->pwd_mode) { + /*+2: the new char + \0*/ + size_t realloc_size = strlen(ta->pwd_tmp) + strlen(letter_buf) + 1; + ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, realloc_size); LV_ASSERT_MALLOC(ta->pwd_tmp); if(ta->pwd_tmp == NULL) return; _lv_txt_ins(ta->pwd_tmp, ta->cursor.pos, (const char *)letter_buf); /*Auto hide characters*/ - if(ta->pwd_show_time == 0) { - pwd_char_hider(obj); - } - else { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, ta); - lv_anim_set_exec_cb(&a, pwd_char_hider_anim); - lv_anim_set_time(&a, ta->pwd_show_time); - lv_anim_set_values(&a, 0, 1); - lv_anim_set_path_cb(&a, lv_anim_path_step); - lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready); - lv_anim_start(&a); - } + auto_hide_characters(obj); } /*Move the cursor after the new character*/ @@ -175,7 +165,7 @@ void lv_textarea_add_text(lv_obj_t * obj, const char * txt) lv_textarea_t * ta = (lv_textarea_t *)obj; - if(ta->pwd_mode != 0) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/ + if(ta->pwd_mode) pwd_char_hider(obj); /*Make sure all the current text contains only '*'*/ /*Add the character one-by-one if not all characters are accepted or there is character limit.*/ if(lv_textarea_get_accepted_chars(obj) || lv_textarea_get_max_length(obj)) { @@ -200,28 +190,16 @@ void lv_textarea_add_text(lv_obj_t * obj, const char * txt) lv_label_ins_text(ta->label, ta->cursor.pos, txt); lv_textarea_clear_selection(obj); - if(ta->pwd_mode != 0) { - ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + strlen(txt) + 1); + if(ta->pwd_mode) { + size_t realloc_size = strlen(ta->pwd_tmp) + strlen(txt) + 1; + ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, realloc_size); LV_ASSERT_MALLOC(ta->pwd_tmp); if(ta->pwd_tmp == NULL) return; _lv_txt_ins(ta->pwd_tmp, ta->cursor.pos, txt); /*Auto hide characters*/ - if(ta->pwd_show_time == 0) { - pwd_char_hider(obj); - } - else { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, ta); - lv_anim_set_exec_cb(&a, pwd_char_hider_anim); - lv_anim_set_time(&a, ta->pwd_show_time); - lv_anim_set_values(&a, 0, 1); - lv_anim_set_path_cb(&a, lv_anim_path_step); - lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready); - lv_anim_start(&a); - } + auto_hide_characters(obj); } /*Move the cursor after the new text*/ @@ -259,7 +237,7 @@ void lv_textarea_del_char(lv_obj_t * obj) if(txt[0] == '\0') lv_obj_invalidate(obj); } - if(ta->pwd_mode != 0) { + if(ta->pwd_mode) { _lv_txt_cut(ta->pwd_tmp, ta->cursor.pos - 1, 1); ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(ta->pwd_tmp) + 1); @@ -301,7 +279,7 @@ void lv_textarea_set_text(lv_obj_t * obj, const char * txt) if(lv_textarea_get_accepted_chars(obj) || lv_textarea_get_max_length(obj)) { lv_label_set_text(ta->label, ""); lv_textarea_set_cursor_pos(obj, LV_TEXTAREA_CURSOR_LAST); - if(ta->pwd_mode != 0) { + if(ta->pwd_mode) { ta->pwd_tmp[0] = '\0'; /*Clear the password too*/ } uint32_t i = 0; @@ -321,27 +299,14 @@ void lv_textarea_set_text(lv_obj_t * obj, const char * txt) if(txt_act[0] == '\0') lv_obj_invalidate(obj); } - if(ta->pwd_mode != 0) { + if(ta->pwd_mode) { ta->pwd_tmp = lv_mem_realloc(ta->pwd_tmp, strlen(txt) + 1); LV_ASSERT_MALLOC(ta->pwd_tmp); if(ta->pwd_tmp == NULL) return; strcpy(ta->pwd_tmp, txt); /*Auto hide characters*/ - if(ta->pwd_show_time == 0) { - pwd_char_hider(obj); - } - else { - lv_anim_t a; - lv_anim_init(&a); - lv_anim_set_var(&a, ta); - lv_anim_set_exec_cb(&a, pwd_char_hider_anim); - lv_anim_set_time(&a, ta->pwd_show_time); - lv_anim_set_values(&a, 0, 1); - lv_anim_set_path_cb(&a, lv_anim_path_step); - lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready); - lv_anim_start(&a); - } + auto_hide_characters(obj); } lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL); @@ -355,29 +320,22 @@ void lv_textarea_set_placeholder_text(lv_obj_t * obj, const char * txt) lv_textarea_t * ta = (lv_textarea_t *)obj; size_t txt_len = strlen(txt); - - if(txt_len == 0) { - if(ta->placeholder_txt) { - lv_mem_free(ta->placeholder_txt); - ta->placeholder_txt = NULL; - } + if((txt_len == 0) && (ta->placeholder_txt)) { + lv_mem_free(ta->placeholder_txt); + ta->placeholder_txt = NULL; } else { - /*Allocate memory for the placeholder_txt text*/ - if(ta->placeholder_txt == NULL) { - ta->placeholder_txt = lv_mem_alloc(txt_len + 1); - } - else { - ta->placeholder_txt = lv_mem_realloc(ta->placeholder_txt, txt_len + 1); - - } + /*NOTE: Using special realloc behavior, malloc-like when data_p is NULL*/ + ta->placeholder_txt = lv_mem_realloc(ta->placeholder_txt, txt_len + 1); LV_ASSERT_MALLOC(ta->placeholder_txt); if(ta->placeholder_txt == NULL) { LV_LOG_ERROR("lv_textarea_set_placeholder_text: couldn't allocate memory for placeholder"); return; } + strcpy(ta->placeholder_txt, txt); + ta->placeholder_txt[txt_len] = '\0'; } lv_obj_invalidate(obj); @@ -440,7 +398,7 @@ void lv_textarea_set_cursor_click_pos(lv_obj_t * obj, bool en) LV_ASSERT_OBJ(obj, MY_CLASS); lv_textarea_t * ta = (lv_textarea_t *)obj; - ta->cursor.click_pos = en ? 1 : 0; + ta->cursor.click_pos = en ? 1U : 0U; } void lv_textarea_set_password_mode(lv_obj_t * obj, bool en) @@ -450,11 +408,12 @@ void lv_textarea_set_password_mode(lv_obj_t * obj, bool en) lv_textarea_t * ta = (lv_textarea_t *)obj; if(ta->pwd_mode == en) return; - ta->pwd_mode = en == false ? 0 : 1; + ta->pwd_mode = en ? 1U : 0U; /*Pwd mode is now enabled*/ - if(en != false) { - char * txt = lv_label_get_text(ta->label); + if(en) { + char * txt = lv_label_get_text(ta->label); size_t len = strlen(txt); + ta->pwd_tmp = lv_mem_alloc(len + 1); LV_ASSERT_MALLOC(ta->pwd_tmp); if(ta->pwd_tmp == NULL) return; @@ -476,6 +435,36 @@ void lv_textarea_set_password_mode(lv_obj_t * obj, bool en) refr_cursor_area(obj); } +void lv_textarea_set_password_bullet(lv_obj_t * obj, const char * bullet) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + LV_ASSERT_NULL(bullet); + + lv_textarea_t * ta = (lv_textarea_t *)obj; + + if(!bullet && (ta->pwd_bullet)) { + lv_mem_free(ta->pwd_bullet); + ta->pwd_bullet = NULL; + } + else { + size_t txt_len = strlen(bullet); + + /*Allocate memory for the pwd_bullet text*/ + /*NOTE: Using special realloc behavior, malloc-like when data_p is NULL*/ + ta->pwd_bullet = lv_mem_realloc(ta->pwd_bullet, txt_len + 1); + LV_ASSERT_MALLOC(ta->pwd_bullet); + if(ta->pwd_bullet == NULL) { + LV_LOG_ERROR("lv_textarea_set_password_bullet: couldn't allocate memory for bullet"); + return; + } + + strcpy(ta->pwd_bullet, bullet); + ta->pwd_bullet[txt_len] = '\0'; + } + + lv_obj_invalidate(obj); +} + void lv_textarea_set_one_line(lv_obj_t * obj, bool en) { LV_ASSERT_OBJ(obj, MY_CLASS); @@ -483,21 +472,21 @@ void lv_textarea_set_one_line(lv_obj_t * obj, bool en) lv_textarea_t * ta = (lv_textarea_t *)obj; if(ta->one_line == en) return; - if(en) { - ta->one_line = 1; - lv_obj_set_width(ta->label, LV_SIZE_CONTENT); - lv_obj_set_style_min_width(ta->label, lv_pct(100), 0); + ta->one_line = en ? 1U : 0U; + lv_coord_t width = en ? LV_SIZE_CONTENT : lv_pct(100); + lv_coord_t min_width_value = en ? lv_pct(100) : 0; + lv_obj_set_width(ta->label, width); + lv_obj_set_style_min_width(ta->label, min_width_value, 0); + + if(en) { lv_obj_set_height(obj, LV_SIZE_CONTENT); - lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF); } else { - ta->one_line = 0; - lv_obj_set_width(ta->label, lv_pct(100)); - lv_obj_set_style_min_width(ta->label, 0, 0); lv_obj_remove_local_style_prop(obj, LV_STYLE_HEIGHT, LV_PART_MAIN); - lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF); } + + lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF); } void lv_textarea_set_accepted_chars(lv_obj_t * obj, const char * list) @@ -628,7 +617,24 @@ bool lv_textarea_get_password_mode(const lv_obj_t * obj) LV_ASSERT_OBJ(obj, MY_CLASS); lv_textarea_t * ta = (lv_textarea_t *)obj; - return ta->pwd_mode == 0 ? false : true; + return ta->pwd_mode == 1U; +} + +const char * lv_textarea_get_password_bullet(lv_obj_t * obj) +{ + LV_ASSERT_OBJ(obj, MY_CLASS); + + lv_textarea_t * ta = (lv_textarea_t *)obj; + + if(ta->pwd_bullet) return ta->pwd_bullet; + + lv_font_glyph_dsc_t g; + const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); + + /*If the textarea's font has the bullet character use it else fallback to "*"*/ + if(lv_font_get_glyph_dsc(font, &g, LV_TEXTAREA_PWD_BULLET_UNICODE, 0)) + return LV_SYMBOL_BULLET; + return "*"; } bool lv_textarea_get_one_line(const lv_obj_t * obj) @@ -636,7 +642,7 @@ bool lv_textarea_get_one_line(const lv_obj_t * obj) LV_ASSERT_OBJ(obj, MY_CLASS); lv_textarea_t * ta = (lv_textarea_t *)obj; - return ta->one_line == 0 ? false : true; + return ta->one_line == 1U; } const char * lv_textarea_get_accepted_chars(lv_obj_t * obj) @@ -805,6 +811,7 @@ static void lv_textarea_constructor(const lv_obj_class_t * class_p, lv_obj_t * o ta->pwd_mode = 0; ta->pwd_tmp = NULL; + ta->pwd_bullet = NULL; ta->pwd_show_time = LV_TEXTAREA_DEF_PWD_SHOW_TIME; ta->accepted_chars = NULL; ta->max_length = 0; @@ -841,6 +848,10 @@ static void lv_textarea_destructor(const lv_obj_class_t * class_p, lv_obj_t * ob lv_mem_free(ta->pwd_tmp); ta->pwd_tmp = NULL; } + if(ta->pwd_bullet != NULL) { + lv_mem_free(ta->pwd_bullet); + ta->pwd_bullet = NULL; + } if(ta->placeholder_txt != NULL) { lv_mem_free(ta->placeholder_txt); ta->placeholder_txt = NULL; @@ -922,7 +933,7 @@ static void cursor_blink_anim_cb(void * obj, int32_t show) { lv_textarea_t * ta = (lv_textarea_t *)obj; if(show != ta->cursor.show) { - ta->cursor.show = show == 0 ? 0 : 1; + ta->cursor.show = show ? 1U : 0U; lv_area_t area_tmp; lv_area_copy(&area_tmp, &ta->cursor.area); area_tmp.x1 += ta->label->coords.x1; @@ -963,67 +974,56 @@ static void pwd_char_hider_anim_ready(lv_anim_t * a) static void pwd_char_hider(lv_obj_t * obj) { lv_textarea_t * ta = (lv_textarea_t *)obj; - if(ta->pwd_mode != 0) { - char * txt = lv_label_get_text(ta->label); - int32_t enc_len = _lv_txt_get_encoded_length(txt); - if(enc_len == 0) return; - - /*If the textarea's font has "bullet" character use it else fallback to "*"*/ - const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN); - lv_font_glyph_dsc_t g; - bool has_bullet; - has_bullet = lv_font_get_glyph_dsc(font, &g, LV_TEXTAREA_PWD_BULLET_UNICODE, 0); - const char * bullet; - if(has_bullet) bullet = LV_SYMBOL_BULLET; - else bullet = "*"; - - size_t bullet_len = strlen(bullet); - char * txt_tmp = lv_mem_buf_get(enc_len * bullet_len + 1); - int32_t i; - for(i = 0; i < enc_len; i++) { - lv_memcpy(&txt_tmp[i * bullet_len], bullet, bullet_len); - } - - txt_tmp[i * bullet_len] = '\0'; - - lv_label_set_text(ta->label, txt_tmp); - lv_mem_buf_release(txt_tmp); - refr_cursor_area(obj); + if(ta->pwd_mode == 0) { + return; } + + /* When ta->label is empty we get 0 back */ + char * txt = lv_label_get_text(ta->label); + uint32_t enc_len = _lv_txt_get_encoded_length(txt); + if(enc_len == 0) return; + + const char * bullet = lv_textarea_get_password_bullet(obj); + const size_t bullet_len = strlen(bullet); + char * txt_tmp = lv_mem_buf_get(enc_len * bullet_len + 1); + + uint32_t i; + for(i = 0; i < enc_len; i++) { + lv_memcpy(&txt_tmp[i * bullet_len], bullet, bullet_len); + } + txt_tmp[i * bullet_len] = '\0'; + + lv_label_set_text(ta->label, txt_tmp); + lv_mem_buf_release(txt_tmp); + + refr_cursor_area(obj); } /** - * Test an unicode character if it is accepted or not. Checks max length and accepted char list. + * Test a unicode character if it is accepted or not. Checks max length and accepted char list. * @param ta pointer to a test area object - * @param c an unicode character + * @param c a unicode character * @return true: accepted; false: rejected */ static bool char_is_accepted(lv_obj_t * obj, uint32_t c) { lv_textarea_t * ta = (lv_textarea_t *)obj; - /*If no restriction accept it*/ - if((ta->accepted_chars == NULL || ta->accepted_chars[0] == '\0') && ta->max_length == 0) return true; - /*Too many characters?*/ if(ta->max_length > 0 && _lv_txt_get_encoded_length(lv_textarea_get_text(obj)) >= ta->max_length) { return false; } + if(ta->accepted_chars == NULL || ta->accepted_chars[0] == '\0') return true; /*Accepted character?*/ - if(ta->accepted_chars) { - uint32_t i = 0; + uint32_t i = 0; - while(ta->accepted_chars[i] != '\0') { - uint32_t a = _lv_txt_encoded_next(ta->accepted_chars, &i); - if(a == c) return true; /*Accepted*/ - } + while(ta->accepted_chars[i] != '\0') { + uint32_t a = _lv_txt_encoded_next(ta->accepted_chars, &i); + if(a == c) return true; /*Accepted*/ + } - return false; /*The character wasn't in the list*/ - } - else { - return true; /*If the accepted char list in not specified the accept the character*/ - } + return false; /*The character wasn't in the list*/ } static void start_cursor_blink(lv_obj_t * obj) @@ -1058,22 +1058,17 @@ static void refr_cursor_area(lv_obj_t * obj) uint32_t cur_pos = lv_textarea_get_cursor_pos(obj); const char * txt = lv_label_get_text(ta->label); - uint32_t byte_pos; - byte_pos = _lv_txt_encoded_get_byte_id(txt, cur_pos); - + uint32_t byte_pos = _lv_txt_encoded_get_byte_id(txt, cur_pos); uint32_t letter = _lv_txt_encoded_next(&txt[byte_pos], NULL); - lv_coord_t letter_h = lv_font_get_line_height(font); - + /* Letter height and width */ + const lv_coord_t letter_h = lv_font_get_line_height(font); /*Set letter_w (set not 0 on non printable but valid chars)*/ - lv_coord_t letter_w; - if(letter == '\0' || letter == '\n' || letter == '\r') { - letter_w = lv_font_get_glyph_width(font, ' ', '\0'); - } - else { - /*`letter_next` parameter is '\0' to ignore kerning*/ - letter_w = lv_font_get_glyph_width(font, letter, '\0'); + uint32_t letter_space = letter; + if(is_valid_but_non_printable_char(letter)) { + letter_space = ' '; } + lv_coord_t letter_w = lv_font_get_glyph_width(font, letter_space, IGNORE_KERNING); lv_point_t letter_pos; lv_label_get_letter_pos(ta->label, cur_pos, &letter_pos); @@ -1081,8 +1076,9 @@ static void refr_cursor_area(lv_obj_t * obj) lv_text_align_t align = lv_obj_calculate_style_text_align(ta->label, LV_PART_MAIN, lv_label_get_text(ta->label)); /*If the cursor is out of the text (most right) draw it to the next line*/ - if(letter_pos.x + ta->label->coords.x1 + letter_w > ta->label->coords.x2 && ta->one_line == 0 && - align != LV_TEXT_ALIGN_RIGHT) { + if(((letter_pos.x + ta->label->coords.x1) + letter_w > ta->label->coords.x2) && + (ta->one_line == 0 && align != LV_TEXT_ALIGN_RIGHT)) { + letter_pos.x = 0; letter_pos.y += letter_h + line_space; @@ -1091,12 +1087,11 @@ static void refr_cursor_area(lv_obj_t * obj) letter = _lv_txt_encoded_next(&txt[byte_pos], NULL); } - if(letter == '\0' || letter == '\n' || letter == '\r') { - letter_w = lv_font_get_glyph_width(font, ' ', '\0'); - } - else { - letter_w = lv_font_get_glyph_width(font, letter, '\0'); + uint32_t tmp = letter; + if(is_valid_but_non_printable_char(letter)) { + tmp = ' '; } + letter_w = lv_font_get_glyph_width(font, tmp, IGNORE_KERNING); } /*Save the byte position. It is required to draw `LV_CURSOR_BLOCK`*/ @@ -1139,7 +1134,6 @@ static void update_cursor_position_on_click(lv_event_t * e) lv_indev_t * click_source = lv_indev_get_act(); if(click_source == NULL) return; - lv_event_code_t code = lv_event_get_code(e); lv_obj_t * obj = lv_event_get_target(e); lv_textarea_t * ta = (lv_textarea_t *)obj; if(ta->cursor.click_pos == 0) return; @@ -1161,26 +1155,27 @@ static void update_cursor_position_on_click(lv_event_t * e) rel_pos.x = point_act.x - label_coords.x1; rel_pos.y = point_act.y - label_coords.y1; - lv_coord_t label_width = lv_obj_get_width(ta->label); + const lv_event_code_t code = lv_event_get_code(e); - uint16_t char_id_at_click; + lv_coord_t label_width = lv_obj_get_width(ta->label); + uint16_t char_id_at_click = 0; #if LV_LABEL_TEXT_SELECTION lv_label_t * label_data = (lv_label_t *)ta->label; - bool click_outside_label; + bool click_outside_label = false; /*Check if the click happened on the left side of the area outside the label*/ if(rel_pos.x < 0) { char_id_at_click = 0; - click_outside_label = true; + click_outside_label = true; } /*Check if the click happened on the right side of the area outside the label*/ else if(rel_pos.x >= label_width) { char_id_at_click = LV_TEXTAREA_CURSOR_LAST; - click_outside_label = true; + click_outside_label = true; } else { char_id_at_click = lv_label_get_letter_on(ta->label, &rel_pos); - click_outside_label = !lv_label_is_char_under_pos(ta->label, &rel_pos); + click_outside_label = !lv_label_is_char_under_pos(ta->label, &rel_pos); } if(ta->text_sel_en) { @@ -1250,13 +1245,18 @@ static void update_cursor_position_on_click(lv_event_t * e) #endif } +/* Returns LV_RES_OK when no operation were performed + * Returns LV_RES_INV when a user defined text was inserted */ static lv_res_t insert_handler(lv_obj_t * obj, const char * txt) { ta_insert_replace = NULL; lv_event_send(obj, LV_EVENT_INSERT, (char *)txt); - if(ta_insert_replace) { - if(ta_insert_replace[0] == '\0') return LV_RES_INV; /*Drop this text*/ + /* Drop txt if insert replace is set to '\0' */ + if(ta_insert_replace && ta_insert_replace[0] == '\0') + return LV_RES_INV; + + if(ta_insert_replace) { /*Add the replaced text directly it's different from the original*/ if(strcmp(ta_insert_replace, txt)) { lv_textarea_add_text(obj, ta_insert_replace); @@ -1338,4 +1338,33 @@ static void draw_cursor(lv_event_t * e) } } +static void auto_hide_characters(lv_obj_t * obj) +{ + lv_textarea_t * ta = (lv_textarea_t *) obj; + + if(ta->pwd_show_time == 0) { + pwd_char_hider(obj); + } + else { + lv_anim_t a; + lv_anim_init(&a); + lv_anim_set_var(&a, ta); + lv_anim_set_exec_cb(&a, pwd_char_hider_anim); + lv_anim_set_time(&a, ta->pwd_show_time); + lv_anim_set_values(&a, 0, 1); + lv_anim_set_path_cb(&a, lv_anim_path_step); + lv_anim_set_ready_cb(&a, pwd_char_hider_anim_ready); + lv_anim_start(&a); + } +} + +static inline bool is_valid_but_non_printable_char(const uint32_t letter) +{ + if(letter == '\0' || letter == '\n' || letter == '\r') { + return true; + } + + return false; +} + #endif diff --git a/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.h b/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.h index 219e2725e..4b3289b48 100644 --- a/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.h +++ b/lib/libesp32_lvgl/lvgl/src/widgets/lv_textarea.h @@ -42,6 +42,7 @@ typedef struct { lv_obj_t * label; /*Label of the text area*/ char * placeholder_txt; /*Place holder label. only visible if text is an empty string*/ char * pwd_tmp; /*Used to store the original text in password mode*/ + char * pwd_bullet; /*Replacement characters displayed in password mode*/ const char * accepted_chars; /*Only these characters will be accepted. NULL: accept all*/ uint32_t max_length; /*The max. number of characters. 0: no limit*/ uint16_t pwd_show_time; /*Time to show characters in password mode before change them to '*'*/ @@ -154,6 +155,13 @@ void lv_textarea_set_cursor_click_pos(lv_obj_t * obj, bool en); */ void lv_textarea_set_password_mode(lv_obj_t * obj, bool en); +/** + * Set the replacement characters to show in password mode + * @param obj pointer to a text area object + * @param bullet pointer to the replacement text + */ +void lv_textarea_set_password_bullet(lv_obj_t * obj, const char * bullet); + /** * Configure the text area to one line or back to normal * @param obj pointer to a text area object @@ -254,6 +262,13 @@ bool lv_textarea_get_cursor_click_pos(lv_obj_t * obj); */ bool lv_textarea_get_password_mode(const lv_obj_t * obj); +/** + * Get the replacement characters to show in password mode + * @param obj pointer to a text area object + * @return pointer to the replacement text + */ +const char * lv_textarea_get_password_bullet(lv_obj_t * obj); + /** * Get the one line configuration attribute * @param obj pointer to a text area object diff --git a/tasmota/lvgl_berry/tasmota_lv_conf.h b/tasmota/lvgl_berry/tasmota_lv_conf.h index 695dd1e0b..bd8464e35 100644 --- a/tasmota/lvgl_berry/tasmota_lv_conf.h +++ b/tasmota/lvgl_berry/tasmota_lv_conf.h @@ -1,6 +1,6 @@ /** * @file lv_conf.h - * Configuration file for v8.2.0 + * Configuration file for v8.3.0 */ /* @@ -29,14 +29,14 @@ /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ #define LV_COLOR_16_SWAP 1 -/*Enable more complex drawing routines to manage screens transparency. - *Can be used if the UI is above another layer, e.g. an OSD menu or video player. - *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/ +/*Enable features to draw on transparent background. + *It's required if opa, and transform_* style properties are used. + *Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/ #define LV_COLOR_SCREEN_TRANSP 0 /* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */ -#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128) +#define LV_COLOR_MIX_ROUND_OFS 0 /*Images pixels with this color will not be drawn if they are chroma keyed)*/ #define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/ @@ -55,8 +55,8 @@ #define LV_MEM_ADR 0 /*0: unused*/ /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/ #if LV_MEM_ADR == 0 - //#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/ - //#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/ + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC #endif #else /*LV_MEM_CUSTOM*/ @@ -120,6 +120,22 @@ #define LV_CIRCLE_CACHE_SIZE 4 #endif /*LV_DRAW_COMPLEX*/ +/** + * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer + * and blend it as an image with the given opacity. + * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) + * The widget can be buffered in smaller chunks to avoid using large buffers. + * + * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it + * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. + * + * Both buffer sizes are in bytes. + * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers + * and can't be drawn in chunks. So these settings affects only widgets with opacity. + */ +#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) +#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) + /*Default image cache size. Image caching keeps the images opened. *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. @@ -130,24 +146,24 @@ /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ -#define LV_GRADIENT_MAX_STOPS 2 +#define LV_GRADIENT_MAX_STOPS 2 /*Default gradient buffer size. *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. *If the cache is too small the map will be allocated only while it's required for the drawing. *0 mean no caching.*/ -#define LV_GRAD_CACHE_DEF_SIZE 0 +#define LV_GRAD_CACHE_DEF_SIZE 0 /*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ -#define LV_DITHER_GRADIENT 1 +#define LV_DITHER_GRADIENT 1 #if LV_DITHER_GRADIENT /*Add support for error diffusion dithering. *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. *The increase in memory consumption is (24 bits * object's width)*/ - #define LV_DITHER_ERROR_DIFFUSION 1 + #define LV_DITHER_ERROR_DIFFUSION 1 #endif /*Maximum buffer size to allocate for rotation. @@ -158,6 +174,9 @@ * GPU *-----------*/ +/*Use Arm's 2D acceleration library Arm-2D */ +#define LV_USE_GPU_ARM2D 0 + /*Use STM32's DMA2D (aka Chrom Art) GPU*/ #define LV_USE_GPU_STM32_DMA2D 0 #if LV_USE_GPU_STM32_DMA2D @@ -166,6 +185,12 @@ #define LV_GPU_DMA2D_CMSIS_INCLUDE #endif +/*Use SWM341's DMA2D GPU*/ +#define LV_USE_GPU_SWM341_DMA2D 0 +#if LV_USE_GPU_SWM341_DMA2D + #define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h" +#endif + /*Use NXP's PXP GPU iMX RTxxx platforms*/ #define LV_USE_GPU_NXP_PXP 0 #if LV_USE_GPU_NXP_PXP @@ -419,6 +444,9 @@ #define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ #endif +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + /*================= * TEXT SETTINGS *=================*/ @@ -473,8 +501,6 @@ #define LV_USE_ARC 1 -#define LV_USE_ANIMIMG 1 - #define LV_USE_BAR 1 #define LV_USE_BTN 1 @@ -520,6 +546,8 @@ /*----------- * Widgets *----------*/ +#define LV_USE_ANIMIMG 1 + #define LV_USE_CALENDAR 0 #if LV_USE_CALENDAR #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 @@ -552,6 +580,12 @@ #define LV_USE_MSGBOX 1 +#define LV_USE_SPAN 1 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + #define LV_USE_SPINBOX 1 #define LV_USE_SPINNER 1 @@ -562,12 +596,6 @@ #define LV_USE_WIN 0 -#define LV_USE_SPAN 1 -#if LV_USE_SPAN - /*A line text can contain maximum num of span descriptor */ - #define LV_SPAN_SNIPPET_STACK_SIZE 64 -#endif - /*----------- * Themes *----------*/ @@ -613,7 +641,7 @@ #if LV_USE_FS_STDIO #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for open, read, etc*/ @@ -621,19 +649,19 @@ #if LV_USE_FS_POSIX #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for CreateFile, ReadFile, etc*/ #define LV_USE_FS_WIN32 0 #if LV_USE_FS_WIN32 - #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ -#define LV_USE_FS_FATFS 0 +#define LV_USE_FS_FATFS 0 #if LV_USE_FS_FATFS #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ @@ -677,10 +705,10 @@ /*FFmpeg library for image decoding and playing videos *Supports all major image formats so do not enable other image decoder with it*/ -#define LV_USE_FFMPEG 0 +#define LV_USE_FFMPEG 0 #if LV_USE_FFMPEG /*Dump input information to stderr*/ - #define LV_FFMPEG_AV_DUMP_FORMAT 0 + #define LV_FFMPEG_DUMP_FORMAT 0 #endif /*----------- @@ -691,10 +719,37 @@ #define LV_USE_SNAPSHOT 0 /*1: Enable Monkey test*/ -#define LV_USE_MONKEY 0 +#define LV_USE_MONKEY 0 /*1: Enable grid navigation*/ -#define LV_USE_GRIDNAV 0 +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable a published subscriber based messaging system */ +#define LV_USE_MSG 0 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif // LV_IME_PINYIN_USE_K9_MODE +#endif /*================== * EXAMPLES @@ -708,28 +763,32 @@ ====================*/ /*Show some widget. It might be required to increase `LV_MEM_SIZE` */ -#define LV_USE_DEMO_WIDGETS 0 +#define LV_USE_DEMO_WIDGETS 0 #if LV_USE_DEMO_WIDGETS -#define LV_DEMO_WIDGETS_SLIDESHOW 0 +#define LV_DEMO_WIDGETS_SLIDESHOW 0 #endif /*Demonstrate the usage of encoder and keyboard*/ -#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 /*Benchmark your system*/ -#define LV_USE_DEMO_BENCHMARK 0 +#define LV_USE_DEMO_BENCHMARK 0 +#if LV_USE_DEMO_BENCHMARK +/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ +#define LV_DEMO_BENCHMARK_RGB565A8 0 +#endif /*Stress test for LVGL*/ -#define LV_USE_DEMO_STRESS 0 +#define LV_USE_DEMO_STRESS 0 /*Music player demo*/ -#define LV_USE_DEMO_MUSIC 0 +#define LV_USE_DEMO_MUSIC 0 #if LV_USE_DEMO_MUSIC -# define LV_DEMO_MUSIC_SQUARE 0 -# define LV_DEMO_MUSIC_LANDSCAPE 0 -# define LV_DEMO_MUSIC_ROUND 0 -# define LV_DEMO_MUSIC_LARGE 0 -# define LV_DEMO_MUSIC_AUTO_PLAY 0 + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 #endif /*--END OF LV_CONF_H--*/ From 722406a4615aedb93841774452d16da1c68a4ee4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:41:02 +0200 Subject: [PATCH 124/219] Add support for Modbus bridge Add support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) --- BUILDS.md | 2 +- CHANGELOG.md | 3 +- RELEASENOTES.md | 4 ++- tasmota/include/tasmota_configurations.h | 6 +++- .../include/tasmota_configurations_ESP32.h | 3 ++ tasmota/my_user_config.h | 2 +- tasmota/tasmota_support/support_features.ino | 6 ++-- ...s_bridge.ino => xdrv_63_modbus_bridge.ino} | 30 +++++++++---------- tools/decode-status.py | 2 +- 9 files changed, 34 insertions(+), 24 deletions(-) rename tasmota/tasmota_xdrv_driver/{xdrv_100_modbus_bridge.ino => xdrv_63_modbus_bridge.ino} (96%) diff --git a/BUILDS.md b/BUILDS.md index 0ffe39764..551bed7f0 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -183,7 +183,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_NOVA_SDS | - | - / x | - | x | - | - | | USE_HPMA | - | - / x | - | x | - | - | | USE_SERIAL_BRIDGE | - | x / x | x | x | - | x | -| USE_MODBUS_BRIDGE | - | - / - | - | - | - | - | +| USE_MODBUS_BRIDGE | - | - / x | - | - | - | - | | USE_MP3_PLAYER | - | - / x | - | x | - | - | | USE_AZ7798 | - | - / - | - | - | - | - | | USE_PN532_HSU | - | - / x | - | x | - | - | diff --git a/CHANGELOG.md b/CHANGELOG.md index 050611aa1..66cbb8cbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,10 @@ All notable changes to this project will be documented in this file. ## [12.0.2.4] ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds -- ESP32 LVGL library from v8.2.0 to v8.3.0 +- Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) ### Changed +- ESP32 LVGL library from v8.2.0 to v8.3.0 ### Fixed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b0e0b4f2b..812044d7b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -117,12 +117,14 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D +- Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` [#16013](https://github.com/arendst/Tasmota/issues/16013) - ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars [#15916](https://github.com/arendst/Tasmota/issues/15916) ### Breaking Changed ### Changed -- Tasmota ESP32 Arduino core from v2.0.3 to v2.0.4 [#15940](https://github.com/arendst/Tasmota/issues/15940) +- ESP32 Arduino core from v2.0.3 to v2.0.4 [#15940](https://github.com/arendst/Tasmota/issues/15940) +- ESP32 LVGL library from v8.2.0 to v8.3.0 - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Fixed diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index 75b1d6367..c4ccd81d1 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -176,6 +176,7 @@ #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) #define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger #define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) @@ -235,7 +236,6 @@ #define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_STEPPER // Add support for A4988/DRV8825 stepper-motor-driver-circuit (+10k5 code) //#define USE_THERMOSTAT // Add support for Thermostat -//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // FIRMWARE_SENSORS @@ -448,6 +448,7 @@ #undef USE_SR04 // Disable support for HC-SR04 ultrasonic devices (+1k code) #undef USE_DYP // Disable support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MODBUS_BRIDGE // Disable support for software Modbus Bridge (+3k code) #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) @@ -576,6 +577,7 @@ #undef USE_SR04 // Disable support for HC-SR04 ultrasonic devices (+1k code) #undef USE_DYP // Disable support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MODBUS_BRIDGE // Disable support for software Modbus Bridge (+3k code) #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) @@ -722,6 +724,7 @@ #undef USE_SR04 // Disable support for HC-SR04 ultrasonic devices (+1k code) #undef USE_DYP // Disable support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MODBUS_BRIDGE // Disable support for software Modbus Bridge (+3k code) #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger #undef USE_PN532_HSU // Disable support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) @@ -862,6 +865,7 @@ #undef USE_SR04 // Disable support for HC-SR04 ultrasonic devices (+1k code) #undef USE_DYP // Disable support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MODBUS_BRIDGE // Disable support for software Modbus Bridge (+3k code) #undef USE_TCP_BRIDGE // DIsable support for Serial to TCP bridge (+1.3k code) #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h index 7d5d0e4b3..dae5619b5 100644 --- a/tasmota/include/tasmota_configurations_ESP32.h +++ b/tasmota/include/tasmota_configurations_ESP32.h @@ -115,6 +115,7 @@ #undef USE_SR04 // Disable support for HC-SR04 ultrasonic devices (+1k code) #undef USE_DYP // Disable support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_MODBUS_BRIDGE // Disable support for software Modbus Bridge (+3k code) #undef USE_TCP_BRIDGE // DIsable support for Serial to TCP bridge (+1.3k code) #undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #undef USE_AZ7798 // Disable support for AZ-Instrument 7798 CO2 datalogger @@ -429,6 +430,7 @@ //#define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger //#define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) @@ -637,6 +639,7 @@ #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) #define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop //#define USE_AZ7798 // Add support for AZ-Instrument 7798 CO2 datalogger #define USE_PN532_HSU // Add support for PN532 using HSU (Serial) interface (+1k8 code, 140 bytes mem) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2a3921def..9bf93bf55 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -753,6 +753,7 @@ #define SR04_MAX_SENSOR_DISTANCE 500 // Set sensor max detection distance //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) +//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) //#define USE_TCP_BRIDGE // Add support for Serial to TCP bridge (+1.3k code) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, pause, stop, track, volume and reset #define MP3_VOLUME 30 // Set the startup volume on init, the range can be 0..100(max) @@ -782,7 +783,6 @@ //#define USE_VINDRIKTNING // Add support for IKEA VINDRIKTNING particle concentration sensor (+0k6 code) // #define VINDRIKTNING_SHOW_PM1 // Display undocumented/supposed PM1.0 values // #define VINDRIKTNING_SHOW_PM10 // Display undocumented/supposed PM10 values -//#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge // -- Power monitoring sensors -------------------- #define USE_ENERGY_SENSOR // Add support for Energy Monitors (+14k code) diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index ee301ba9f..0b5b44eb2 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -61,9 +61,9 @@ void ResponseAppendFeatures(void) //#if (MQTT_LIBRARY_TYPE == MQTT_TASMOTAMQTT) // feature1 |= 0x00000800; // xdrv_02_mqtt.ino //#endif -//#if (MQTT_LIBRARY_TYPE == MQTT_ESPMQTTARDUINO) // Obsolete since 6.2.1.11 -// feature1 |= 0x00001000; // xdrv_02_mqtt.ino -//#endif +#ifdef USE_MODBUS_BRIDGE + feature1 |= 0x00001000; // xdrv_63_modbus_bridge.ino +#endif #if defined(USE_DISCOVERY) && defined(MQTT_HOST_DISCOVERY) feature1 |= 0x00002000; // xdrv_02_mqtt.ino #endif diff --git a/tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino similarity index 96% rename from tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino rename to tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 7a71cc3df..e095198fb 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_100_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -1,7 +1,7 @@ /* - xdrv_100_modbus_bridge.ino - modbus bridge support for Tasmota + xdrv_63_modbus_bridge.ino - modbus bridge support for Tasmota - Copyright (C) 2021 Theo Arends and Dániel Zoltán Tolnai + Copyright (C) 2021 Theo Arends and Jeroenst This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,15 +26,15 @@ * Modbus Bridge using Modbus library (TasmotaModbus) \*********************************************************************************************/ -#define XDRV_100 100 -#define HARDWARE_FALLBACK 2 -#define MBR_MAX_VALUE_LENGTH 30 -#define MBR_SPEED TM_MODBUS_BAUDRATE -#define MBR_MAX_REGISTERS 64 +#define XDRV_63 63 -#define D_CMND_MODBUS_SEND "ModbusSend" -#define D_CMND_MODBUS_SETBAUDRATE "ModbusSetBaudrate" -#define D_CMND_MODBUS_SETSERIALCONFIG "ModbusSetSerialConfig" +#define MBR_MAX_VALUE_LENGTH 30 +#define MBR_SPEED TM_MODBUS_BAUDRATE +#define MBR_MAX_REGISTERS 64 + +#define D_CMND_MODBUS_SEND "Send" +#define D_CMND_MODBUS_SETBAUDRATE "Baudrate" +#define D_CMND_MODBUS_SETSERIALCONFIG "SerialConfig" #define D_JSON_MODBUS_RECEIVED "ModbusReceived" #define D_JSON_MODBUS_DEVICE_ADDRESS "DeviceAddress" @@ -46,7 +46,7 @@ #define D_JSON_MODBUS_VALUES "Values" #define D_JSON_MODBUS_LENGTH "Length" -const char kModbusBridgeCommands[] PROGMEM = "|" // No prefix +const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; void (*const ModbusBridgeCommand[])(void) PROGMEM = { @@ -103,15 +103,15 @@ struct ModbusBridge { unsigned long polling_window = 0; int in_byte_counter = 0; - bool raw = false; ModbusBridgeFunctionCode functionCode = ModbusBridgeFunctionCode::mb_undefined; ModbusBridgeType type = ModbusBridgeType::mb_undefined; - uint8_t count = 0; uint16_t registerCount = 0; + uint16_t startAddress = 0; uint8_t deviceAddress = 0; - uint8_t startAddress = 0; + uint8_t count = 0; + bool raw = false; }; ModbusBridge modbusBridge; @@ -412,7 +412,7 @@ void CmndModbusBridgeSetConfig(void) * Interface \*********************************************************************************************/ -bool Xdrv100(uint8_t function) +bool Xdrv63(uint8_t function) { bool result = false; diff --git a/tools/decode-status.py b/tools/decode-status.py index abbd334dc..bc06a682a 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -213,7 +213,7 @@ a_features = [[ "USE_ENERGY_MARGIN_DETECTION","USE_LIGHT","USE_I2C","USE_SPI", "USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER", "WEBSERVER_ADVERTISE","USE_EMULATION_HUE","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT", - "MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812", + "USE_MODBUS_BRIDGE","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812", "USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE", "USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE", "USE_TIMERS","USE_SUNRISE","USE_TIMERS_WEB","USE_RULES", From 79161d3c430ef19dcea4555e16fe2ee023a47d41 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 20 Jul 2022 11:11:18 +0200 Subject: [PATCH 125/219] Fix BH1750 illuminance formula Fix BH1750 illuminance formula (#16022) --- tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino | 7 +++---- tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index e095198fb..faf102934 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -17,13 +17,12 @@ along with this program. If not, see . */ -/* - Example Command: modbussend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint8", "count":4} -*/ - #ifdef USE_MODBUS_BRIDGE /*********************************************************************************************\ * Modbus Bridge using Modbus library (TasmotaModbus) + * + * Example Command: + * ModbusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint8", "count":4} \*********************************************************************************************/ #define XDRV_63 63 diff --git a/tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino b/tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino index b8be7a04e..c67b0e24e 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_10_bh1750.ino @@ -101,7 +101,7 @@ bool Bh1750Read(uint32_t sensor_index) { if (2 != Wire.requestFrom(Bh1750_sensors[sensor_index].address, (uint8_t)2)) { return false; } float illuminance = (Wire.read() << 8) | Wire.read(); - illuminance /= (1.2 * (69 / (float)Bh1750_sensors[sensor_index].mtreg)); + illuminance *= 57.5 / (float)Bh1750_sensors[sensor_index].mtreg; // Fix #16022 if (1 == Bh1750Resolution(sensor_index)) { illuminance /= 2; } From de52580a18f5034fcaae69f045ce6509c108e003 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 20 Jul 2022 14:25:40 +0200 Subject: [PATCH 126/219] sml special options descriptor --- tasmota/tasmota_xsns_sensor/xsns_53_sml.ino | 177 ++++++++++++++++++-- 1 file changed, 163 insertions(+), 14 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino index 319c2b4b6..c3a9a9743 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_53_sml.ino @@ -78,6 +78,7 @@ #define DJ_VAVG "Volt_avg" #define DJ_COUNTER "Count" + struct METER_DESC { int8_t srcpin; uint8_t type; @@ -91,8 +92,28 @@ struct METER_DESC { uint8_t max_index; char *script_str; uint8_t sopt; +#ifdef USE_SML_SPECOPT + uint32_t so_obis1; + uint32_t so_obis2; + uint8_t so_fcode1; + uint8_t so_bpos1; + uint8_t so_fcode2; + uint8_t so_bpos2; +#endif }; +// max number of meters , may be adjusted +#ifndef MAX_METERS +#define MAX_METERS 5 +#endif + +#ifdef USE_SCRIPT +struct METER_DESC script_meter_desc[MAX_METERS]; +uint8_t *script_meter; +#endif + + + // this descriptor method is no longer supported // but still functional for simple meters // use scripting method instead @@ -480,10 +501,7 @@ const uint8_t meter[]= #define SML_MAX_VARS 20 #endif -// max number of meters , may be adjusted -#ifndef MAX_METERS -#define MAX_METERS 5 -#endif + double meter_vars[SML_MAX_VARS]; // calulate deltas #define MAX_DVARS MAX_METERS*2 @@ -1236,7 +1254,7 @@ void Hexdump(uint8_t *sbuff, uint32_t slen) { AddLogData(LOG_LEVEL_INFO, cbuff); } -#if defined(ED300L) || defined(AS2020) +#if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT) uint8_t sml_status[MAX_METERS]; uint8_t g_mindex; #endif @@ -1293,6 +1311,46 @@ double dval; sml_status[g_mindex]=*(cp+1); } #endif +#ifdef DTZ541 + unsigned char *cpx=cp-5; + // decode OBIS 0180 amd extract direction info + if (*cp==0x65 && *cpx==0 && *(cpx+1)==0x01 && *(cpx+2)==0x08 && *(cpx+3)==0) { + sml_status[g_mindex]=*(cp+3); + } +#endif + +#ifdef USE_SML_SPECOPT + unsigned char *cpx = cp - 5; + uint32_t ocode = (*(cpx+0)<<24) | (*(cpx+1)<<16) | (*(cpx+2)<<8) | (*(cpx+3)<<0); + + if (ocode == script_meter_desc[g_mindex].so_obis1) { + sml_status[g_mindex]&=0xfe; + uint32_t flag = 0; + uint16_t bytes = 0; + if (*cp == script_meter_desc[g_mindex].so_fcode1) { + cpx = cp + 1; + bytes = (script_meter_desc[g_mindex].so_fcode1 & 0xf) - 1; + for (uint16_t cnt = 0; cnt < bytes; cnt++) { + flag <<= 8; + flag |= *cpx++; + } + if (flag & (1 << script_meter_desc[g_mindex].so_bpos1)) { + sml_status[g_mindex]|=1; + } + } + if (*cp == script_meter_desc[g_mindex].so_fcode2) { + cpx = cp + 1; + bytes = (script_meter_desc[g_mindex].so_fcode2 & 0xf) - 1; + for (uint16_t cnt = 0; cnt < bytes; cnt++) { + flag <<= 8; + flag |= *cpx++; + } + if (flag & (1 << script_meter_desc[g_mindex].so_bpos1)) { + sml_status[g_mindex]|=1; + } + } + } +#endif cp=skip_sml(cp,&result); // check time @@ -1418,6 +1476,25 @@ double dval; } } #endif + #ifdef DTZ541 + // decode current power OBIS 00 10 07 00 + if (*cpx==0x00 && *(cpx+1)==0x10 && *(cpx+2)==0x07 && *(cpx+3)==0) { + if (sml_status[g_mindex]&0x08) { + // and invert sign on solar feed + dval*=-1; + } + } + #endif + #ifdef USE_SML_SPECOPT + ocode = (*(cpx+0)<<24) | (*(cpx+1)<<16) | (*(cpx+2)<<8) | (*(cpx+3)<<0); + if (ocode == script_meter_desc[g_mindex].so_obis2) { + if (sml_status[g_mindex] & 1) { + // and invert sign on solar feed + dval*=-1; + } + } + #endif + return dval; } @@ -1723,6 +1800,12 @@ void SML_Decode(uint8_t index) { continue; } + if (*mp == '=' && *(mp+1) == 's') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } + // =d must handle dindex if (*mp == '=' && *(mp + 1) == 'd') { if (index != mindex) { @@ -1851,6 +1934,11 @@ void SML_Decode(uint8_t index) { mp = strchr(mp, '|'); if (mp) mp++; continue; + } else if (*mp == 's') { + // skip spec option tag line + mp = strchr(mp, '|'); + if (mp) mp++; + continue; } } else { // compare value @@ -2118,8 +2206,8 @@ void SML_Decode(uint8_t index) { // matches, get value dvalid[vindex] = 1; mp++; -#if defined(ED300L) || defined(AS2020) - g_mindex=mindex; +#if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT) + g_mindex = mindex; #endif if (*mp == '#') { // get string value @@ -2333,6 +2421,11 @@ void SML_Show(boolean json) { if (mp) mp++; continue; } + if (*mp=='=' && *(mp+1)=='s') { + mp = strchr(mp, '|'); + if (mp) mp++; + continue; + } // skip compare section cp=strchr(mp,'@'); if (cp) { @@ -2519,11 +2612,6 @@ uint32_t debounce_time; } -#ifdef USE_SCRIPT -struct METER_DESC script_meter_desc[MAX_METERS]; -uint8_t *script_meter; -#endif - #ifndef METER_DEF_SIZE #define METER_DEF_SIZE 3000 #endif @@ -2583,6 +2671,40 @@ bool Gpio_used(uint8_t gpiopin) { return false; } +#ifdef USE_SML_SPECOPT +void SML_GetSpecOpt(char *cp, uint32_t mnum) { +// special option 1 +// we need 2 obis codes +// 2 flag codes + bit positions +// 1,=so1,00010800,63,7,64,11,00100700 + + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_obis1 = strtol(cp, &cp, 16); + } + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_fcode1 = strtol(cp, &cp, 16); + } + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_bpos1 = strtol(cp, &cp, 10); + } + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_fcode2 = strtol(cp, &cp, 16); + } + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_bpos2 = strtol(cp, &cp, 10); + } + if (*cp == ',') { + cp++; + script_meter_desc[mnum].so_obis2 = strtol(cp, &cp, 16); + } +} +#endif + void SML_Init(void) { meters_used=METERS_USED; meter_desc_p=meter_desc; @@ -2599,6 +2721,13 @@ void SML_Init(void) { meter_spos[cnt]=0; } +#ifdef USE_SML_SPECOPT + for (uint32_t cnt = 0; cnt < MAX_METERS; cnt++) { + script_meter_desc[cnt].so_obis1 = 0; + script_meter_desc[cnt].so_obis2 = 0; + } +#endif + #ifdef USE_SCRIPT for (uint32_t cnt=0;cntmeters_used) return 0; meter--; -#if defined(ED300L) || defined(AS2020) +#if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT) return sml_status[meter]; #else return 0; @@ -3055,6 +3203,7 @@ uint32_t SML_Status(uint32_t meter) { } + uint32_t SML_Write(uint32_t meter,char *hstr) { if (meter<1 || meter>meters_used) return 0; meter--; From df9a99944ebd323bb4e760fb8903061569e9150a Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 20 Jul 2022 20:47:42 +0200 Subject: [PATCH 127/219] add shine mp3 encoder --- lib/lib_audio/mp3_shine_esp32/COPYING | 482 ++++++++++ lib/lib_audio/mp3_shine_esp32/LICENSE | 674 ++++++++++++++ lib/lib_audio/mp3_shine_esp32/README.md | 30 + lib/lib_audio/mp3_shine_esp32/changelog.txt | 2 + lib/lib_audio/mp3_shine_esp32/component.mk | 12 + lib/lib_audio/mp3_shine_esp32/library.json | 7 + .../mp3_shine_esp32/library.properties | 9 + .../mp3_shine_esp32/src/bitstream.cpp | 77 ++ lib/lib_audio/mp3_shine_esp32/src/bitstream.h | 28 + lib/lib_audio/mp3_shine_esp32/src/huffman.cpp | 120 +++ lib/lib_audio/mp3_shine_esp32/src/huffman.h | 19 + .../mp3_shine_esp32/src/l3bitstream.cpp | 323 +++++++ .../mp3_shine_esp32/src/l3bitstream.h | 6 + lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp | 857 ++++++++++++++++++ lib/lib_audio/mp3_shine_esp32/src/l3loop.h | 9 + lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp | 134 +++ lib/lib_audio/mp3_shine_esp32/src/l3mdct.h | 7 + .../mp3_shine_esp32/src/l3subband.cpp | 104 +++ lib/lib_audio/mp3_shine_esp32/src/l3subband.h | 9 + lib/lib_audio/mp3_shine_esp32/src/layer3.cpp | 290 ++++++ lib/lib_audio/mp3_shine_esp32/src/layer3.h | 166 ++++ .../mp3_shine_esp32/src/mult_mips_gcc.h | 51 ++ .../mp3_shine_esp32/src/mult_noarch_gcc.h | 119 +++ .../mp3_shine_esp32/src/mult_sarm_gcc.h | 109 +++ .../mp3_shine_esp32/src/reservoir.cpp | 132 +++ lib/lib_audio/mp3_shine_esp32/src/reservoir.h | 9 + lib/lib_audio/mp3_shine_esp32/src/tables.cpp | 126 +++ lib/lib_audio/mp3_shine_esp32/src/tables.h | 16 + lib/lib_audio/mp3_shine_esp32/src/types.h | 179 ++++ 29 files changed, 4106 insertions(+) create mode 100755 lib/lib_audio/mp3_shine_esp32/COPYING create mode 100755 lib/lib_audio/mp3_shine_esp32/LICENSE create mode 100755 lib/lib_audio/mp3_shine_esp32/README.md create mode 100755 lib/lib_audio/mp3_shine_esp32/changelog.txt create mode 100755 lib/lib_audio/mp3_shine_esp32/component.mk create mode 100644 lib/lib_audio/mp3_shine_esp32/library.json create mode 100644 lib/lib_audio/mp3_shine_esp32/library.properties create mode 100755 lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/bitstream.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/huffman.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/huffman.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3loop.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3mdct.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/l3subband.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/layer3.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/layer3.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/reservoir.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/tables.cpp create mode 100755 lib/lib_audio/mp3_shine_esp32/src/tables.h create mode 100755 lib/lib_audio/mp3_shine_esp32/src/types.h diff --git a/lib/lib_audio/mp3_shine_esp32/COPYING b/lib/lib_audio/mp3_shine_esp32/COPYING new file mode 100755 index 000000000..7ccf9c7ff --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/COPYING @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/lib/lib_audio/mp3_shine_esp32/LICENSE b/lib/lib_audio/mp3_shine_esp32/LICENSE new file mode 100755 index 000000000..f288702d2 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/lib/lib_audio/mp3_shine_esp32/README.md b/lib/lib_audio/mp3_shine_esp32/README.md new file mode 100755 index 000000000..b594affe2 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/README.md @@ -0,0 +1,30 @@ +# mp3_shine_esp32 +Shine MP3 encoder for ESP32 - Last updated FEB 2019 - Compatibility with current IDF unknown!! + +This is a 'port' of the old Shine MP3 encoder originally written for ARM or MIPS arch devices, quite a long time ago. + +Memory allocation has been optimised for the ESP32 +Some asm has been added to boost performance in the ESP32 arch + +The are leftovers of the beginings of a dual core implementation but this was not required in the end, the code has not been cleaned up. + +You should init the encoder ASAP in your code as the encoder needs large contiguous chunks of RAM. + +A sample FreeRTOS task has been included as a guide. Change it to suit your own needs. + +For full 48000Hz Joint Stereo performance on one core your will need to compile with the -Os optimisation option. + +I will try to get an HTTP MP3 streaming demo up at some stage, but I'm currently working 80 hour weeks so don't hold your breath. + +Any questions feel free to ask. + +Cheers! +-fkn + +Limitations + +The encoding algorithm is rather simple. In particular, it does not have any Psychoacoustic Model. + +A bit of history + +This code was dug out from the dusty crates of those times before internet and github. It apparently was created by Gabriel Bouvigne sometime around the end of the 20th century. The encoder was converted circa 2001 by Pete Everett to fixed-point arithmetic for the RISC OS. Latest we know, Patrick Roberts had worked on the code to make it multi-platform and more library oriented. That was around 2006. diff --git a/lib/lib_audio/mp3_shine_esp32/changelog.txt b/lib/lib_audio/mp3_shine_esp32/changelog.txt new file mode 100755 index 000000000..c7ca1b320 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/changelog.txt @@ -0,0 +1,2 @@ +0.1.4: better input wav file handling (from Yichin Lin) +code cleanup diff --git a/lib/lib_audio/mp3_shine_esp32/component.mk b/lib/lib_audio/mp3_shine_esp32/component.mk new file mode 100755 index 000000000..586d893bb --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/component.mk @@ -0,0 +1,12 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# +# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, +# this will take the sources in the src/ directory, compile them and link them into +# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# + +COMPONENT_SRCDIRS := . +COMPONENT_PRIV_INCLUDEDIRS := . +# CFLAGS += diff --git a/lib/lib_audio/mp3_shine_esp32/library.json b/lib/lib_audio/mp3_shine_esp32/library.json new file mode 100644 index 000000000..d7de5755a --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/library.json @@ -0,0 +1,7 @@ +{ + "name": "mp3_shine_esp32", + "description": "mp3 encoder", + "keywords": "ESP32, MP3", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/mp3_shine_esp32/library.properties b/lib/lib_audio/mp3_shine_esp32/library.properties new file mode 100644 index 000000000..b1cdc253f --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/library.properties @@ -0,0 +1,9 @@ +name=mp3_shine_esp32 +version=1.0 +author= +maintainer= +sentence=mp3 encoder for ESP32 +paragraph= +category=Signal processor +url= +architectures=esp32 diff --git a/lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp b/lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp new file mode 100755 index 000000000..866e94704 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp @@ -0,0 +1,77 @@ +/* + * bit_stream.c package + * Author: Jean-Georges Fritsch, C-Cube Microsystems + * + * This package provides functions to write information to the bit stream. + * + * Removed unused functions. Feb 2001 P.Everett + */ + +#include "types.h" +#include "bitstream.h" + +#if !defined(__APPLE__) && !defined(__FreeBSD__) +#include +#endif + +/* open the device to write the bit stream into it */ +void shine_open_bit_stream(bitstream_t *bs, int size) { + bs->data = (unsigned char *)malloc(size*sizeof(unsigned char)); + bs->data_size = size; + bs->data_position = 0; + bs->cache = 0; + bs->cache_bits = 32; +} + +/*close the device containing the bit stream */ +void shine_close_bit_stream(bitstream_t *bs) { + if (bs->data) + free(bs->data); +} + +/* + * shine_putbits: + * -------- + * write N bits into the bit stream. + * bs = bit stream structure + * val = value to write into the buffer + * N = number of bits of val + */ +void shine_putbits(bitstream_t *bs, unsigned int val, unsigned int N) { +#ifdef SHINE_DEBUG + if (N > 32) { + printf("Cannot write more than 32 bits at a time.\n"); + } + if (N < 32 && (val >> N) != 0) { + printf("Upper bits (higher than %d) are not all zeros.\n", N); + } +#endif + + if (bs->cache_bits > N) { + bs->cache_bits -= N; + bs->cache |= val << bs->cache_bits; + } else { + if (bs->data_position + sizeof(unsigned int) >= bs->data_size) { + bs->data = (unsigned char *)realloc(bs->data, bs->data_size + (bs->data_size / 2)); + bs->data_size += (bs->data_size / 2); + } + + N -= bs->cache_bits; + bs->cache |= val >> N; +#ifdef SHINE_BIG_ENDIAN + *(unsigned int*)(bs->data + bs->data_position) = bs->cache; +#else + *(unsigned int*)(bs->data + bs->data_position) = SWAB32(bs->cache); +#endif + bs->data_position += sizeof(unsigned int); + bs->cache_bits = 32 - N; + if (N != 0) + bs->cache = val << bs->cache_bits; + else + bs->cache = 0; + } +} + +int shine_get_bits_count(bitstream_t *bs) { + return bs->data_position * 8 + 32 - bs->cache_bits; +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/bitstream.h b/lib/lib_audio/mp3_shine_esp32/src/bitstream.h new file mode 100755 index 000000000..10c1beadf --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/bitstream.h @@ -0,0 +1,28 @@ +#ifndef BITSTREAM_H +#define BITSTREAM_H + +typedef struct bit_stream_struc { + unsigned char *data; /* Processed data */ + int data_size; /* Total data size */ + int data_position; /* Data position */ + unsigned int cache; /* bit stream cache */ + int cache_bits; /* free bits in cache */ +} bitstream_t; + +/* "bit_stream.h" Definitions */ + +#define MINIMUM 4 /* Minimum size of the buffer in bytes */ +#define MAX_LENGTH 32 /* Maximum length of word written or + read from bit stream */ + +#define BUFFER_SIZE 4096 + +//#define MIN(A, B) ((A) < (B) ? (A) : (B)) +//#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +void shine_open_bit_stream(bitstream_t *bs,const int size); +void shine_close_bit_stream(bitstream_t *bs); +void shine_putbits(bitstream_t *bs,unsigned int val, unsigned int N); +int shine_get_bits_count(bitstream_t *bs); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/huffman.cpp b/lib/lib_audio/mp3_shine_esp32/src/huffman.cpp new file mode 100755 index 000000000..d4bd6aaf8 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/huffman.cpp @@ -0,0 +1,120 @@ +/* huffman.c */ + +#include "types.h" +#include "huffman.h" +#include "bitstream.h" +#include "l3bitstream.h" + +const HUFFBITS dmask = 1 << (((sizeof(HUFFBITS))<<3)-1); +const unsigned int hs = sizeof(HUFFBITS)<<3; + +static const HUFFBITS t1HB[] = {1, 1, 1, 0}; +static const HUFFBITS t2HB[] = {1, 2, 1, 3, 1, 1, 3, 2, 0}; +static const HUFFBITS t3HB[] = {3, 2, 1, 1, 1, 1, 3, 2, 0}; +static const HUFFBITS t5HB[] = {1, 2, 6, 5, 3, 1, 4, 4, 7, 5, 7, 1, 6, 1, 1, 0}; +static const HUFFBITS t6HB[] = {7, 3, 5, 1, 6, 2, 3, 2, 5, 4, 4, 1, 3, 3, 2, 0}; +static const HUFFBITS t7HB[] = {1, 2, 10, 19, 16, 10, 3, 3, 7, 10, 5, 3, 11, 4, 13, 17, 8, 4, 12, 11, 18, 15, 11, 2, 7, 6, 9, 14, 3, 1, 6, 4, 5, 3, 2, 0}; +static const HUFFBITS t8HB[] = {3, 4, 6, 18, 12, 5, 5, 1, 2, 16, 9, 3, 7, 3, 5, 14, 7, 3, 19, 17, 15, 13, 10, 4, 13, 5, 8, 11, 5, 1, 12, 4, 4, 1, 1, 0}; +static const HUFFBITS t9HB[] = {7, 5, 9, 14, 15, 7, 6, 4, 5, 5, 6, 7, 7, 6, 8, 8, 8, 5, 15, 6, 9, 10, 5, 1, 11, 7, 9, 6, 4, 1, 14, 4, 6, 2, 6, 0}; +static const HUFFBITS t10HB[] = {1, 2, 10, 23, 35, 30, 12, 17, 3, 3, 8, 12, 18, 21, 12, 7, 11, 9, 15, 21, 32, 40, 19, 6, 14, 13, 22, 34, 46, 23, 18, 7, 20, 19, 33, 47, 27, 22, 9, 3, 31, 22, 41, 26, 21, 20, 5, 3, 14, 13, 10, 11, 16, 6, 5, 1, 9, 8, 7, 8, 4 , 4, 2, 0}; +static const HUFFBITS t11HB[] = {3, 4, 10, 24, 34, 33, 21, 15, 5, 3, 4, 10, 32, 17, 11, 10, 11, 7, 13, 18, 30, 31, 20, 5, 25, 11, 19, 59, 27, 18, 12, 5, 35, 33, 31, 58, 30, 16, 7, 5, 28, 26, 32, 19, 17, 15, 8, 14, 14, 12, 9, 13, 14, 9, 4, 1, 11, 4, 6, 6, 6, 3, 2, 0}; +static const HUFFBITS t12HB[] = {9, 6, 16, 33, 41, 39, 38, 26, 7, 5, 6, 9, 23, 16, 26, 11, 17, 7, 11, 14, 21, 30, 10, 7, 17, 10, 15, 12, 18, 28, 14, 5, 32, 13, 22, 19, 18, 16, 9, 5, 40, 17, 31, 29, 17, 13, 4, 2, 27, 12, 11, 15, 10, 7, 4, 1, 27, 12, 8, 12 , 6, 3, 1, 0}; +static const HUFFBITS t13HB[] = {1, 5, 14, 21, 34, 51, 46, 71, 42, 52, 68, 52, 67, 44, 43, 19, 3, 4, 12, 19, 31, 26, 44, 33, 31, 24, 32, 24, 31, 35, 22, 14, 15, 13, 23, 36, 59, 49, 77, 65, 29, 40, 30, 40, 27, 33, 42, 16, 22, + 20, 37, 61, 56, 79, 73, 64, 43, 76, 56, 37, 26, 31, 25, 14, 35, 16, 60, 57, 97, 75, 114, 91, 54, 73, 55, 41, 48, 53, 23, 24, 58, 27, 50, 96, 76, 70, 93, 84, 77, 58, 79, 29, 74, 49, 41, 17, 47, + 45, 78, 74, 115, 94, 90, 79, 69, 83, 71, 50, 59, 38, 36, 15, 72, 34, 56, 95, 92, 85, 91, 90, 86, 73, 77, 65, 51, 44, 43, 42, 43, 20, 30, 44, 55, 78, 72, 87, 78, 61, 46, 54, 37, 30, 20, 16, 53, + 25, 41, 37, 44, 59, 54, 81, 66, 76, 57, 54, 37, 18, 39, 11, 35, 33, 31, 57, 42, 82, 72, 80, 47, 58, 55, 21, 22, 26, 38, 22, 53, 25, 23, 38, 70, 60, 51, 36, 55, 26, 34, 23, 27, 14, 9, 7, 34, 32, + 28, 39, 49, 75, 30, 52, 48, 40, 52, 28, 18, 17, 9, 5, 45, 21, 34, 64, 56, 50, 49, 45, 31, 19, 12, 15, 10, 7, 6, 3, 48, 23, 20, 39, 36, 35, 53, 21, 16, 23, 13, 10, 6, 1, 4, 2, 16, 15, 17, 27, 25, + 20, 29, 11, 17, 12, 16, 8, 1, 1, 0, 1}; +static const HUFFBITS t15HB[] = {7, 12, 18, 53, 47, 76, 124, 108, 89, 123, 108, 119, 107, 81, 122, 63, 13, 5, 16, 27, 46, 36, 61, 51, 42, 70, 52, 83, 65, 41, 59, 36, 19, 17, 15, 24, 41, 34, 59, 48, 40, 64, 50, 78, 62, 80, 56, + 33, 29, 28, 25, 43, 39, 63, 55, 93, 76, 59, 93, 72, 54, 75, 50, 29, 52, 22, 42, 40, 67, 57, 95, 79, 72, 57, 89, 69, 49, 66, 46, 27, 77, 37, 35, 66, 58, 52, 91, 74, 62, 48, 79, 63, 90, 62, 40, 38, + 125, 32, 60, 56, 50, 92, 78, 65, 55, 87, 71, 51, 73, 51, 70, 30, 109, 53, 49, 94, 88, 75, 66, 122, 91, 73, 56, 42, 64, 44, 21, 25, 90, 43, 41, 77, 73, 63, 56, 92, 77, 66, 47, 67, 48, 53, 36, 20, + 71, 34, 67, 60, 58, 49, 88, 76, 67, 106, 71, 54, 38, 39, 23, 15, 109, 53, 51, 47, 90, 82, 58, 57, 48, 72, 57, 41, 23, 27, 62, 9, 86, 42, 40, 37, 70, 64, 52, 43, 70, 55, 42, 25, 29, 18, 11, 11, + 118, 68, 30, 55, 50, 46, 74, 65, 49, 39, 24, 16, 22, 13, 14, 7, 91, 44, 39, 38, 34, 63, 52, 45, 31, 52, 28, 19, 14, 8, 9, 3, 123, 60, 58, 53, 47, 43, 32, 22, 37, 24, 17, 12, 15, 10, 2, 1, 71, + 37, 34, 30, 28, 20, 17, 26, 21, 16, 10, 6, 8, 6, 2, 0}; +static const HUFFBITS t16HB[] = {1, 5, 14, 44, 74, 63, 110, 93, 172, 149, 138, 242, 225, 195, 376, 17, 3, 4, 12, 20, 35, 62, 53, 47, 83, 75, 68, 119, 201, 107, 207, 9, 15, 13, 23, 38, 67, 58, 103, 90, 161, 72, 127, 117, + 110, 209, 206, 16, 45, 21, 39, 69, 64, 114, 99, 87, 158, 140, 252, 212, 199, 387, 365, 26, 75, 36, 68, 65, 115, 101, 179, 164, 155, 264, 246, 226, 395, 382, 362, 9, 66, 30, 59, 56, 102, + 185, 173, 265, 142, 253, 232, 400, 388, 378, 445, 16, 111, 54, 52, 100, 184, 178, 160, 133, 257, 244, 228, 217, 385, 366, 715, 10, 98, 48, 91, 88, 165, 157, 148, 261, 248, 407, 397, 372, + 380, 889, 884, 8, 85, 84, 81, 159, 156, 143, 260, 249, 427, 401, 392, 383, 727, 713, 708, 7, 154, 76, 73, 141, 131, 256, 245, 426, 406, 394, 384, 735, 359, 710, 352, 11, 139, 129, 67, 125, + 247, 233, 229, 219, 393, 743, 737, 720, 885, 882, 439, 4, 243, 120, 118, 115, 227, 223, 396, 746, 742, 736, 721, 712, 706, 223, 436, 6, 202, 224, 222, 218, 216, 389, 386, 381, 364, 888, + 443, 707, 440, 437, 1728, 4, 747, 211, 210, 208, 370, 379, 734, 723, 714, 1735, 883, 877, 876, 3459, 865, 2, 377, 369, 102, 187, 726, 722, 358, 711, 709, 866, 1734, 871, 3458, 870, 434, + 0, 12, 10, 7, 11, 10, 17, 11, 9, 13, 12, 10, 7, 5, 3, 1, 3}; +static const HUFFBITS t24HB[] = {15, 13, 46, 80, 146, 262, 248, 434, 426, 669, 653, 649, 621, 517, 1032, 88, 14, 12, 21, 38, 71, 130, 122, 216, 209, 198, 327, 345, 319, 297, 279, 42, 47, 22, 41, 74, 68, 128, 120, 221, + 207, 194, 182, 340, 315, 295, 541, 18, 81, 39, 75, 70, 134, 125, 116, 220, 204, 190, 178, 325, 311, 293, 271, 16, 147, 72, 69, 135, 127, 118, 112, 210, 200, 188, 352, 323, 306, 285, + 540, 14, 263, 66, 129, 126, 119, 114, 214, 202, 192, 180, 341, 317, 301, 281, 262, 12, 249, 123, 121, 117, 113, 215, 206, 195, 185, 347, 330, 308, 291, 272, 520, 10, 435, 115, 111, + 109, 211, 203, 196, 187, 353, 332, 313, 298, 283, 531, 381, 17, 427, 212, 208, 205, 201, 193, 186, 177, 169, 320, 303, 286, 268, 514, 377, 16, 335, 199, 197, 191, 189, 181, 174, 333, + 321, 305, 289, 275, 521, 379, 371, 11, 668, 184, 183, 179, 175, 344, 331, 314, 304, 290, 277, 530, 383, 373, 366, 10, 652, 346, 171, 168, 164, 318, 309, 299, 287, 276, 263, 513, 375, + 368, 362, 6, 648, 322, 316, 312, 307, 302, 292, 284, 269, 261, 512, 376, 370, 364, 359, 4, 620, 300, 296, 294, 288, 282, 273, 266, 515, 380, 374, 369, 365, 361, 357, 2, 1033, 280, 278, + 274, 267, 264, 259, 382, 378, 372, 367, 363, 360, 358, 356, 0, 43, 20, 19, 17, 15, 13, 11, 9, 7, 6, 4, 7, 5, 3, 1, 3}; +static const HUFFBITS t32HB[] = {1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1}; +static const HUFFBITS t33HB[] = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + +static const unsigned char t1l[] = {1, 3, 2, 3}; +static const unsigned char t2l[] = {1, 3, 6, 3, 3, 5, 5, 5, 6}; +static const unsigned char t3l[] = {2, 2, 6, 3, 2, 5, 5, 5, 6}; +static const unsigned char t5l[] = {1, 3, 6, 7, 3, 3, 6, 7, 6, 6, 7, 8, 7, 6, 7, 8}; +static const unsigned char t6l[] = {3, 3, 5, 7, 3, 2, 4, 5, 4, 4, 5, 6, 6, 5, 6, 7}; +static const unsigned char t7l[] = {1, 3, 6, 8, 8, 9, 3, 4, 6, 7, 7, 8, 6, 5, 7, 8, 8, 9, 7, 7, 8, 9, 9, 9, 7, 7, 8, 9, 9, 10, 8, 8, 9, 10, 10, 10}; +static const unsigned char t8l[] = {2, 3, 6, 8, 8, 9, 3, 2, 4, 8, 8, 8, 6, 4, 6, 8, 8, 9, 8, 8, 8, 9, 9, 10, 8, 7, 8, 9, 10, 10, 9, 8, 9, 9, 11, 11}; +static const unsigned char t9l[] = {3, 3, 5, 6, 8, 9, 3, 3, 4, 5, 6, 8, 4, 4, 5, 6, 7, 8, 6, 5, 6, 7, 7, 8, 7, 6, 7, 7, 8, 9, 8, 7, 8, 8, 9, 9}; +static const unsigned char t10l[] = {1, 3, 6, 8, 9, 9, 9, 10, 3, 4, 6, 7, 8, 9, 8, 8, 6, 6, 7, 8, 9, 10, 9, 9, 7, 7, 8, 9, 10, 10, 9, 10, 8, 8, 9, 10, 10, 10, 10, 10, 9, 9, 10, 10, 11, 11, 10, 11, 8, 8, 9, 10, 10, 10, 11, 11, 9, 8, 9, 10, 10, 11, 11, 11}; +static const unsigned char t11l[] = {2, 3, 5, 7, 8, 9, 8, 9, 3, 3, 4, 6, 8, 8, 7, 8, 5, 5, 6, 7, 8, 9, 8, 8, 7, 6, 7, 9, 8, 10, 8, 9, 8, 8, 8, 9, 9, 10, 9, 10, 8, 8, 9, 10, 10, 11, 10, 11, 8, 7, 7, 8, 9, 10, 10, 10, 8, 7, 8, 9, 10, 10, 10, 10}; +static const unsigned char t12l[] = {4, 3, 5, 7, 8, 9, 9, 9, 3, 3, 4, 5, 7, 7, 8, 8, 5, 4, 5, 6, 7, 8, 7, 8, 6, 5, 6, 6, 7, 8, 8, 8, 7, 6, 7, 7, 8, 8, 8, 9, 8, 7, 8, 8, 8, 9, 8, 9, 8, 7, 7, 8, 8, 9, 9, 10, 9, 8, 8, 9, 9, 9, 9, 10}; +static const unsigned char t13l[] = {1, 4, 6, 7, 8, 9, 9, 10, 9, 10, 11, 11, 12, 12, 13, 13, 3, 4, 6, 7, 8, 8, 9, 9, 9, 9, 10, 10, 11, 12, 12, 12, 6, 6, 7, 8, 9, 9, 10, 10, 9, 10, 10, 11, 11, 12, 13, 13, 7, 7, 8, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 13, 13, +8, 7, 9, 9, 10, 10, 11, 11, 10, 11, 11, 12, 12, 13, 13, 14, 9, 8, 9, 10, 10, 10, 11, 11, 11, 11, 12, 11, 13, 13, 14, 14, 9, 9, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14, 14, 10, 9, 10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 16, 16, 9, 8, 9, 10, +10, 11, 11, 12, 12, 12, 12, 13, 13, 14, 15, 15, 10, 9, 10, 10, 11, 11, 11, 13, 12, 13, 13, 14, 14, 14, 16, 15, 10, 10, 10, 11, 11, 12, 12, 13, 12, 13, 14, 13, 14, 15, 16, 17, 11, 10, 10, 11, 12, 12, 12, 12, 13, 13, 13, 14, 15, 15, 15, 16, 11, 11, 11, 12, 12, +13, 12, 13, 14, 14, 15, 15, 15, 16, 16, 16, 12, 11, 12, 13, 13, 13, 14, 14, 14, 14, 14, 15, 16, 15, 16, 16, 13, 12, 12, 13, 13, 13, 15, 14, 14, 17, 15, 15, 15, 17, 16, 16, 12, 12, 13, 14, 14, 14, 15, 14, 15, 15, 16, 16, 19, 18, 19, 16}; +static const unsigned char t15l[] = {3, 4, 5, 7, 7, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 13, 4, 3, 5, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 5, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 6, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 7, 6, 7, + 7, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 8, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 11, 11, 11, 12, 9, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 12, 12, 9, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 12, 9, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, + 12, 12, 12, 9, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 10, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 13, 12, 10, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 11, 10, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 13, 13, +11, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 12, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 12, 13, 12, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13}; +static const unsigned char t16l[] = {1, 4, 6, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12, 13, 9, 3, 4, 6, 7, 8, 9, 9, 9, 10, 10, 10, 11, 12, 11, 12, 8, 6, 6, 7, 8, 9, 9, 10, 10, 11, 10, 11, 11, 11, 12, 12, 9, 8, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 12, 13, 13, + 10, 9, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 9, 9, 8, 9, 9, 10, 11, 11, 12, 11, 12, 12, 13, 13, 13, 14, 10, 10, 9, 9, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 10, 10, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 13, 15, 15, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 10, 11, 10, 10, 11, 11, 12, 12, 13, 13, 13, 13, 14, 13, 14, 13, 11, 11, 11, 10, 11, 12, 12, 12, 12, 13, 14, 14, 14, 15, 15, 14, 10, 12, 11, 11, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 13, 14, 11, 12, 12, + 12, 12, 12, 13, 13, 13, 13, 15, 14, 14, 14, 14, 16, 11, 14, 12, 12, 12, 13, 13, 14, 14, 14, 16, 15, 15, 15, 17, 15, 11, 13, 13, 11, 12, 14, 14, 13, 14, 14, 15, 16, 15, 17, 15, 14, 11, 9, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 8}; +static const unsigned char t24l[] = {4, 4, 6, 7, 8, 9, 9, 10, 10, 11, 11, 11, 11, 11, 12, 9, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10, 10, 8, 6, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 7, 7, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 7, 8, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 7, 9, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 7, 9, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 7, 10, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 8, 10, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, +8, 10, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 8, 11, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 8, 11, 10, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 8, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 8, 11, 10, 10, + 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 8, 12, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 4}; +static const unsigned char t32l[] = {1, 4, 4, 5, 4, 6, 5, 6, 4, 5, 5, 6, 5, 6, 6, 6}; +static const unsigned char t33l[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; + +#define NOREF -1 +const struct huffcodetab shine_huffman_table[HTN] = +{ +{ 0, 0, 0, 0,NULL,NULL}, +{ 2, 2, 0, 0,t1HB, t1l}, +{ 3, 3, 0, 0,t2HB, t2l}, +{ 3, 3, 0, 0,t3HB, t3l}, +{ 0, 0, 0, 0,NULL,NULL},/* Apparently not used*/ +{ 4, 4, 0, 0,t5HB, t5l}, +{ 4, 4, 0, 0,t6HB, t6l}, +{ 6, 6, 0, 0,t7HB, t7l}, +{ 6, 6, 0, 0,t8HB, t8l}, +{ 6, 6, 0, 0,t9HB, t9l}, +{ 8, 8, 0, 0,t10HB, t10l}, +{ 8, 8, 0, 0,t11HB, t11l}, +{ 8, 8, 0, 0,t12HB, t12l}, +{16,16, 0, 0,t13HB, t13l}, +{ 0, 0, 0, 0,NULL,NULL},/* Apparently not used*/ +{16,16, 0, 0,t15HB, t15l}, +{16,16, 1, 1,t16HB, t16l}, +{16,16, 2, 3,t16HB, t16l}, +{16,16, 3, 7,t16HB, t16l}, +{16,16, 4, 15,t16HB, t16l}, +{16,16, 6, 63,t16HB, t16l}, +{16,16, 8, 255,t16HB, t16l}, +{16,16,10,1023,t16HB, t16l}, +{16,16,13,8191,t16HB, t16l}, +{16,16, 4, 15,t24HB, t24l}, +{16,16, 5, 31,t24HB, t24l}, +{16,16, 6, 63,t24HB, t24l}, +{16,16, 7, 127,t24HB, t24l}, +{16,16, 8, 255,t24HB, t24l}, +{16,16, 9, 511,t24HB, t24l}, +{16,16,11,2047,t24HB, t24l}, +{16,16,13,8191,t24HB, t24l}, +{ 1,16, 0, 0,t32HB, t32l}, +{ 1,16, 0, 0,t33HB, t33l}, +}; + + diff --git a/lib/lib_audio/mp3_shine_esp32/src/huffman.h b/lib/lib_audio/mp3_shine_esp32/src/huffman.h new file mode 100755 index 000000000..d2d5b517a --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/huffman.h @@ -0,0 +1,19 @@ +#define HUFFBITS uint16_t +#define HTN 34 +#define MXOFF 250 + +struct huffcodetab { + unsigned int xlen; /*max. x-index+ */ + unsigned int ylen; /*max. y-index+ */ + unsigned int linbits; /*number of linbits */ + unsigned int linmax; /*max number to be stored in linbits */ + const HUFFBITS *table; /*pointer to array[xlen][ylen] */ + const unsigned char *hlen; /*pointer to array[xlen][ylen] */ +}; + +extern const struct huffcodetab shine_huffman_table[HTN];/* global memory block */ + /* array of all huffcodtable headers */ + /* 0..31 Huffman code table 0..31 */ + /* 32,33 count1-tables */ + + diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp b/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp new file mode 100755 index 000000000..9952e04c7 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp @@ -0,0 +1,323 @@ +/* l3bitstrea.c */ + +#include "types.h" +#include "l3mdct.h" +#include "l3loop.h" +#include "layer3.h" +#include "huffman.h" +#include "bitstream.h" +#include "tables.h" +#include "l3bitstream.h" /* the public interface */ + +static void shine_HuffmanCode(bitstream_t *bs, int table_select, int x, int y); +static void shine_huffman_coder_count1(bitstream_t *bs, const struct huffcodetab *h, int v, int w, int x, int y); + +static void encodeSideInfo( shine_global_config *config ); +static void encodeMainData( shine_global_config *config ); +static void Huffmancodebits( shine_global_config *config, int *ix, gr_info *gi); + +/* + shine_format_bitstream() + + This is called after a frame of audio has been quantized and coded. + It will write the encoded audio to the bitstream. Note that + from a layer3 encoder's perspective the bit stream is primarily + a series of main_data() blocks, with header and side information + inserted at the proper locations to maintain framing. (See Figure A.7 + in the IS). +*/ + +void shine_format_bitstream(shine_global_config *config) { + int gr, ch, i; + + for ( ch = 0; ch < config->wave.channels; ch++ ) + for ( gr = 0; gr < config->mpeg.granules_per_frame; gr++ ) + { + int *pi = &config->l3_enc[ch][gr][0]; + int32_t *pr = &config->mdct_freq[ch][gr][0]; + for ( i = 0; i < GRANULE_SIZE; i++ ) + { + if ( (pr[i] < 0) && (pi[i] > 0) ) + pi[i] *= -1; + } + } + + encodeSideInfo( config ); + encodeMainData( config ); +} + +static void encodeMainData(shine_global_config *config) { + int gr, ch, sfb; + shine_side_info_t si = config->side_info; + + for ( gr = 0; gr < config->mpeg.granules_per_frame; gr++ ) + { + for ( ch = 0; ch < config->wave.channels; ch++ ) + { + gr_info *gi = &(si.gr[gr].ch[ch].tt); + unsigned slen1 = shine_slen1_tab[ gi->scalefac_compress ]; + unsigned slen2 = shine_slen2_tab[ gi->scalefac_compress ]; + int *ix = &config->l3_enc[ch][gr][0]; + + if ( gr == 0 || si.scfsi[ch][0] == 0 ) + for ( sfb = 0; sfb < 6; sfb++ ) + shine_putbits( &config->bs, config->scalefactor.l[gr][ch][sfb], slen1 ); + if ( gr == 0 || si.scfsi[ch][1] == 0 ) + for ( sfb = 6; sfb < 11; sfb++ ) + shine_putbits( &config->bs, config->scalefactor.l[gr][ch][sfb], slen1 ); + if ( gr == 0 || si.scfsi[ch][2] == 0 ) + for ( sfb = 11; sfb < 16; sfb++ ) + shine_putbits( &config->bs, config->scalefactor.l[gr][ch][sfb], slen2 ); + if ( gr == 0 || si.scfsi[ch][3] == 0 ) + for ( sfb = 16; sfb < 21; sfb++ ) + shine_putbits( &config->bs, config->scalefactor.l[gr][ch][sfb], slen2 ); + + Huffmancodebits( config, ix, gi ); + } + } +} + +static void encodeSideInfo( shine_global_config *config ) { + int gr, ch, scfsi_band, region; + shine_side_info_t si = config->side_info; + + shine_putbits( &config->bs, 0x7ff, 11 ); + shine_putbits( &config->bs, config->mpeg.version, 2 ); + shine_putbits( &config->bs, config->mpeg.layer, 2 ); + shine_putbits( &config->bs, !config->mpeg.crc, 1 ); + shine_putbits( &config->bs, config->mpeg.bitrate_index, 4 ); + shine_putbits( &config->bs, config->mpeg.samplerate_index % 3, 2 ); + shine_putbits( &config->bs, config->mpeg.padding, 1 ); + shine_putbits( &config->bs, config->mpeg.ext, 1 ); + shine_putbits( &config->bs, config->mpeg.mode, 2 ); + shine_putbits( &config->bs, config->mpeg.mode_ext, 2 ); + shine_putbits( &config->bs, config->mpeg.copyright, 1 ); + shine_putbits( &config->bs, config->mpeg.original, 1 ); + shine_putbits( &config->bs, config->mpeg.emph, 2 ); + + if ( config->mpeg.version == MPEG_I ) { + shine_putbits( &config->bs, 0, 9 ); + if ( config->wave.channels == 2 ) + shine_putbits( &config->bs, si.private_bits, 3 ); + else + shine_putbits( &config->bs, si.private_bits, 5 ); + } else { + shine_putbits( &config->bs, 0, 8 ); + if ( config->wave.channels == 2 ) + shine_putbits( &config->bs, si.private_bits, 2 ); + else + shine_putbits( &config->bs, si.private_bits, 1 ); + } + + if ( config->mpeg.version == MPEG_I ) + for ( ch = 0; ch < config->wave.channels; ch++ ) { + for ( scfsi_band = 0; scfsi_band < 4; scfsi_band++ ) + shine_putbits( &config->bs, si.scfsi[ch][scfsi_band], 1 ); + } + + for ( gr = 0; gr < config->mpeg.granules_per_frame; gr++ ) + for ( ch = 0; ch < config->wave.channels ; ch++ ) + { + gr_info *gi = &(si.gr[gr].ch[ch].tt); + + shine_putbits( &config->bs, gi->part2_3_length, 12 ); + shine_putbits( &config->bs, gi->big_values, 9 ); + shine_putbits( &config->bs, gi->global_gain, 8 ); + if ( config->mpeg.version == MPEG_I ) + shine_putbits( &config->bs, gi->scalefac_compress, 4 ); + else + shine_putbits( &config->bs, gi->scalefac_compress, 9 ); + shine_putbits( &config->bs, 0, 1 ); + + for ( region = 0; region < 3; region++ ) + shine_putbits( &config->bs, gi->table_select[region], 5 ); + + shine_putbits( &config->bs, gi->region0_count, 4 ); + shine_putbits( &config->bs, gi->region1_count, 3 ); + + if ( config->mpeg.version == MPEG_I ) + shine_putbits( &config->bs, gi->preflag, 1 ); + shine_putbits( &config->bs, gi->scalefac_scale, 1 ); + shine_putbits( &config->bs, gi->count1table_select, 1 ); + } +} + +/* Note the discussion of huffmancodebits() on pages 28 and 29 of the IS, as + well as the definitions of the side information on pages 26 and 27. */ +static void Huffmancodebits( shine_global_config *config, int *ix, gr_info *gi ) { + const int *scalefac = &shine_scale_fact_band_index[config->mpeg.samplerate_index][0]; + unsigned scalefac_index; + int region1Start, region2Start; + int i, bigvalues, count1End; + int v, w, x, y; + const struct huffcodetab *h; + int bits; + + bits = shine_get_bits_count(&config->bs); + + /* 1: Write the bigvalues */ + bigvalues = gi->big_values << 1; + + scalefac_index = gi->region0_count + 1; + region1Start = scalefac[ scalefac_index ]; + scalefac_index += gi->region1_count + 1; + region2Start = scalefac[ scalefac_index ]; + + for ( i = 0; i < bigvalues; i += 2 ) + { + /* get table pointer */ + int idx = (i >= region1Start) + (i >= region2Start); + unsigned tableindex = gi->table_select[idx]; + /* get huffman code */ + if ( tableindex ) + { + x = ix[i]; + y = ix[i + 1]; + shine_HuffmanCode( &config->bs, tableindex, x, y ); + } + } + + /* 2: Write count1 area */ + h = &shine_huffman_table[gi->count1table_select + 32]; + count1End = bigvalues + (gi->count1 <<2); + for ( i = bigvalues; i < count1End; i += 4 ) + { + v = ix[i]; + w = ix[i+1]; + x = ix[i+2]; + y = ix[i+3]; + shine_huffman_coder_count1( &config->bs, h, v, w, x, y ); + } + + bits = shine_get_bits_count(&config->bs) - bits; + bits = gi->part2_3_length - gi->part2_length - bits; + if (bits) + { + int stuffingWords = bits / 32; + int remainingBits = bits % 32; + + /* Due to the nature of the Huffman code tables, we will pad with ones */ + while ( stuffingWords-- ) + shine_putbits( &config->bs, ~0, 32 ); + if ( remainingBits ) + shine_putbits( &config->bs, (1UL << remainingBits) - 1, remainingBits ); + } +} + +static inline int shine_abs_and_sign( int *x ) { + if ( *x > 0 ) return 0; + *x *= -1; + return 1; +} + +static void shine_huffman_coder_count1( bitstream_t *bs, const struct huffcodetab *h, int v, int w, int x, int y ) { + unsigned int signv, signw, signx, signy; + unsigned int code = 0; + int p, cbits = 0; + + signv = shine_abs_and_sign( &v ); + signw = shine_abs_and_sign( &w ); + signx = shine_abs_and_sign( &x ); + signy = shine_abs_and_sign( &y ); + + p = v + (w << 1) + (x << 2) + (y << 3); + shine_putbits( bs, h->table[p], h->hlen[p] ); + + if ( v ) { + code = signv; + cbits = 1; + } + if ( w ) { + code = (code << 1) | signw; + cbits++; + } + if ( x ) { + code = (code << 1) | signx; + cbits++; + } + if ( y ) { + code = (code << 1) | signy; + cbits++; + } + shine_putbits( bs, code, cbits ); +} + +/* Implements the pseudocode of page 98 of the IS */ +static void shine_HuffmanCode(bitstream_t *bs, int table_select, int x, int y) { + int cbits = 0, xbits = 0; + unsigned int code = 0, ext = 0; + unsigned signx, signy, ylen, idx; + const struct huffcodetab *h; + + signx = shine_abs_and_sign( &x ); + signy = shine_abs_and_sign( &y ); + + h = &(shine_huffman_table[table_select]); + ylen = h->ylen; + + if ( table_select > 15 ) + { /* ESC-table is used */ + unsigned linbitsx = 0, linbitsy = 0, linbits = h->linbits; + + if ( x > 14 ) + { + linbitsx = x - 15; + x = 15; + } + if ( y > 14 ) + { + linbitsy = y - 15; + y = 15; + } + + idx = (x * ylen) + y; + code = h->table[idx]; + cbits = h->hlen [idx]; + if ( x > 14 ) + { + ext |= linbitsx; + xbits += linbits; + } + if ( x != 0 ) + { + ext <<= 1; + ext |= signx; + xbits += 1; + } + if ( y > 14 ) + { + ext <<= linbits; + ext |= linbitsy; + xbits += linbits; + } + if ( y != 0 ) + { + ext <<= 1; + ext |= signy; + xbits += 1; + } + + shine_putbits( bs, code, cbits); + shine_putbits( bs, ext, xbits); + } + else + { /* No ESC-words */ + idx = (x * ylen) + y; + code = h->table[idx]; + cbits = h->hlen[idx]; + if ( x != 0 ) + { + code <<= 1; + code |= signx; + cbits += 1; + } + if ( y != 0 ) + { + code <<= 1; + code |= signy; + cbits += 1; + } + + shine_putbits( bs, code, cbits); + } +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h b/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h new file mode 100755 index 000000000..6e165f54a --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h @@ -0,0 +1,6 @@ +#ifndef shine_BITSTREAM_H +#define shine_BITSTREAM_H + +void shine_format_bitstream(shine_global_config *config); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp b/lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp new file mode 100755 index 000000000..2d28216d0 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp @@ -0,0 +1,857 @@ +/* l3loop->c */ + +#include "types.h" +#include "tables.h" +#include "l3loop.h" +#include "layer3.h" +#include "huffman.h" +#include "bitstream.h" +#include "l3bitstream.h" +#include "reservoir.h" + +#define e 2.71828182845 +#define CBLIMIT 21 +#define SFB_LMAX 22 +#define en_tot_krit 10 +#define en_dif_krit 100 +#define en_scfsi_band_krit 10 +#define xm_scfsi_band_krit 10 + +static void calc_scfsi(shine_psy_xmin_t *l3_xmin, int ch, int gr, shine_global_config *config); +static int part2_length(int gr, int ch, shine_global_config *config); +static int bin_search_StepSize(int desired_rate, int ix[GRANULE_SIZE], gr_info * cod_info, shine_global_config *config); +static int count_bit(int ix[GRANULE_SIZE], unsigned int start, unsigned int end, unsigned int table ); +static int bigv_bitcount(int ix[GRANULE_SIZE], gr_info *gi); +static int new_choose_table( int ix[GRANULE_SIZE], unsigned int begin, unsigned int end ); +static void bigv_tab_select( int ix[GRANULE_SIZE], gr_info *cod_info ); +static void subdivide(gr_info *cod_info, shine_global_config *config ); +static int count1_bitcount( int ix[ GRANULE_SIZE ], gr_info *cod_info ); +static void calc_runlen( int ix[GRANULE_SIZE], gr_info *cod_info ); +static void calc_xmin( gr_info *cod_info, shine_psy_xmin_t *l3_xmin, int gr, int ch ); +static int quantize(int ix[GRANULE_SIZE], int stepsize, shine_global_config *config); + + +int32_t sqrt_int(int32_t r) { + float x; + float rr = r; + float y = rr*0.5; + *(unsigned int*)&x = (0xbe6f0000 - *(uint32_t*)&rr) >> 1; + + x = (1.5f*x) - (x*x)*(x*y); + if(r>101123) x = (1.5f*x) - (x*x)*(x*y); + + int32_t is = (int32_t)(x*rr + 0.5f); + return is + ((r - is*is)>>31); +} + +#define SQRT_MAGIC_F 0x5f3759df + +float f_sqrt(const float x) { + const float xhalf = 0.5f*x; + //float step; + union // get bits for floating value + { + float x; + int i; + } u; + u.x = x; + u.i = SQRT_MAGIC_F - (u.i >> 1); // gives initial guess y0 + + return x*u.x*(1.5f - xhalf*u.x*u.x);// Newton step, repeating increases accuracy +} +/* + * shine_inner_loop: + * ---------- + * The code selects the best quantizerStepSize for a particular set + * of scalefacs. + */ +int shine_inner_loop(int ix[GRANULE_SIZE], + int max_bits, gr_info *cod_info, int gr, int ch, + shine_global_config *config ) { + int bits, c1bits, bvbits; + + if(max_bits<0) + cod_info->quantizerStepSize--; + do + { + while(quantize(ix,++cod_info->quantizerStepSize,config) > 8192); /* within table range? */ + + calc_runlen(ix,cod_info); /* rzero,count1,big_values*/ + bits = c1bits = count1_bitcount(ix,cod_info); /* count1_table selection*/ + subdivide(cod_info, config); /* bigvalues sfb division */ + bigv_tab_select(ix,cod_info); /* codebook selection*/ + bits += bvbits = bigv_bitcount( ix, cod_info ); /* bit count */ + } + while(bits>max_bits); + return bits; +} + +/* + * shine_outer_loop: + * ----------- + * Function: The outer iteration loop controls the masking conditions + * of all scalefactorbands. It computes the best scalefac and + * global gain. This module calls the inner iteration loop. + */ + +int shine_outer_loop( int max_bits, + shine_psy_xmin_t *l3_xmin, /* the allowed distortion of the scalefactor */ + int ix[GRANULE_SIZE], /* vector of quantized values ix(0..575) */ + int gr, int ch, shine_global_config *config) { + int bits, huff_bits; + shine_side_info_t *side_info = &config->side_info; + gr_info *cod_info = &side_info->gr[gr].ch[ch].tt; + + cod_info->quantizerStepSize = bin_search_StepSize(max_bits,ix,cod_info, config); + + cod_info->part2_length = part2_length(gr,ch,config); + huff_bits = max_bits - cod_info->part2_length; + + bits = shine_inner_loop(ix, huff_bits, cod_info, gr, ch, config ); + cod_info->part2_3_length = cod_info->part2_length + bits; + + return cod_info->part2_3_length; +} + +/* + * shine_iteration_loop: + * ------------------ + */ +void shine_iteration_loop(shine_global_config *config) { + shine_psy_xmin_t l3_xmin; + gr_info *cod_info; + int max_bits; + int ch, gr, i; + int *ix; + + + for(ch=config->wave.channels; ch--; ) + { + for(gr=0; grmpeg.granules_per_frame; gr++) + { + /* setup pointers */ + ix = config->l3_enc[ch][gr]; + config->l3loop->xr = config->mdct_freq[ch][gr]; + + /* Precalculate the square, abs, and maximum, + * for use later on. + */ + for (i=GRANULE_SIZE, config->l3loop->xrmax=0; i--;) + { + config->l3loop->xrsq[i] = mulsr(config->l3loop->xr[i],config->l3loop->xr[i]); + config->l3loop->xrabs[i] = abs(config->l3loop->xr[i]); + if(config->l3loop->xrabs[i]>config->l3loop->xrmax) + config->l3loop->xrmax=config->l3loop->xrabs[i]; + } + + cod_info = (gr_info *) &(config->side_info.gr[gr].ch[ch]); + cod_info->sfb_lmax = SFB_LMAX - 1; /* gr_deco */ + + calc_xmin(cod_info, &l3_xmin, gr, ch ); + + if ( config->mpeg.version == MPEG_I ) + calc_scfsi(&l3_xmin,ch,gr,config); + + /* calculation of number of available bit( per granule ) */ + max_bits = shine_max_reservoir_bits(&config->pe[ch][gr],config); + + /* reset of iteration variables */ + memset(config->scalefactor.l[gr][ch],0,sizeof(config->scalefactor.l[gr][ch])); + memset(config->scalefactor.s[gr][ch],0,sizeof(config->scalefactor.s[gr][ch])); + + for ( i=4; i--; ) + cod_info->slen[i] = 0; + + cod_info->part2_3_length = 0; + cod_info->big_values = 0; + cod_info->count1 = 0; + cod_info->scalefac_compress = 0; + cod_info->table_select[0] = 0; + cod_info->table_select[1] = 0; + cod_info->table_select[2] = 0; + cod_info->region0_count = 0; + cod_info->region1_count = 0; + cod_info->part2_length = 0; + cod_info->preflag = 0; + cod_info->scalefac_scale = 0; + cod_info->count1table_select= 0; + + /* all spectral values zero ? */ + if(config->l3loop->xrmax) + cod_info->part2_3_length = shine_outer_loop(max_bits,&l3_xmin,ix, + gr,ch,config); + + shine_ResvAdjust(cod_info, config ); + cod_info->global_gain = cod_info->quantizerStepSize+210; + + } /* for gr */ + } /* for ch */ + + shine_ResvFrameEnd(config); +} + +/* + * calc_scfsi: + * ----------- + * calculation of the scalefactor select information ( scfsi ). + */ +void calc_scfsi( shine_psy_xmin_t *l3_xmin, int ch, int gr, + shine_global_config *config ) { + shine_side_info_t *l3_side = &config->side_info; + /* This is the scfsi_band table from 2.4.2.7 of the IS */ + static const int scfsi_band_long[5] = { 0, 6, 11, 16, 21 }; + + int scfsi_band; + unsigned scfsi_set; + + int sfb, start, end, i; + int condition = 0; + int temp; + + const int *scalefac_band_long = &shine_scale_fact_band_index[config->mpeg.samplerate_index][0]; + + // note. it goes quite a bit faster if you uncomment the next bit and exit + // early from scfsi, but you then loose the advantage of common scale factors. + + /*for(scfsi_band=0;scfsi_band<4;scfsi_band++) + l3_side->scfsi[ch][scfsi_band] = 0; + return;*/ + + + + config->l3loop->xrmaxl[gr] = config->l3loop->xrmax; + scfsi_set = 0; + + /* the total energy of the granule */ + for ( temp = 0, i =GRANULE_SIZE; i--; ) + temp += config->l3loop->xrsq[i]>>10; /* a bit of scaling to avoid overflow, (not very good) */ + if ( temp ) + config->l3loop->en_tot[gr] = log((float)temp * 4.768371584e-7) / LN2; /* 1024 / 0x7fffffff */ + else + config->l3loop->en_tot[gr] = 0; + + /* the energy of each scalefactor band, en */ + /* the allowed distortion of each scalefactor band, xm */ + + for(sfb=21; sfb--; ) + { + start = scalefac_band_long[ sfb ]; + end = scalefac_band_long[ sfb+1 ]; + + for ( temp = 0, i = start; i < end; i++ ) + temp += config->l3loop->xrsq[i]>>10; + if ( temp ) + config->l3loop->en[gr][sfb] = log((float)temp * 4.768371584e-7) / LN2; /* 1024 / 0x7fffffff */ + else + config->l3loop->en[gr][sfb] = 0; + + if ( l3_xmin->l[gr][ch][sfb]) + config->l3loop->xm[gr][sfb] = log( l3_xmin->l[gr][ch][sfb] ) / LN2; + else + config->l3loop->xm[gr][sfb] = 0; + } + + if(gr==1) + { + int gr2, tp; + + for(gr2=2; gr2--; ) + { + /* The spectral values are not all zero */ + if(config->l3loop->xrmaxl[gr2]) + condition++; + + condition++; + } + if(abs(config->l3loop->en_tot[0]-config->l3loop->en_tot[1]) < en_tot_krit) + condition++; + for(tp=0,sfb=21; sfb--; ) + tp += abs(config->l3loop->en[0][sfb]-config->l3loop->en[1][sfb]); + if (tp < en_dif_krit) + condition++; + + if(condition==6) + { + for(scfsi_band=0;scfsi_band<4;scfsi_band++) + { + int sum0 = 0, sum1 = 0; + l3_side->scfsi[ch][scfsi_band] = 0; + start = scfsi_band_long[scfsi_band]; + end = scfsi_band_long[scfsi_band+1]; + for ( sfb = start; sfb < end; sfb++ ) + { + sum0 += abs( config->l3loop->en[0][sfb] - config->l3loop->en[1][sfb] ); + sum1 += abs( config->l3loop->xm[0][sfb] - config->l3loop->xm[1][sfb] ); + } + + if(sum0scfsi[ch][scfsi_band] = 1; + scfsi_set |= (1 << scfsi_band); + } + else + l3_side->scfsi[ch][scfsi_band] = 0; + } /* for scfsi_band */ + } /* if condition == 6 */ + else + for(scfsi_band=0;scfsi_band<4;scfsi_band++) + l3_side->scfsi[ch][scfsi_band] = 0; + } /* if gr == 1 */ +} + +/* + * part2_length: + * ------------- + * calculates the number of bits needed to encode the scalefacs in the + * main data block. + */ +int part2_length(int gr, int ch, shine_global_config *config) { + int slen1, slen2, bits; + gr_info *gi = &config->side_info.gr[gr].ch[ch].tt; + + bits = 0; + + { + slen1 = shine_slen1_tab[ gi->scalefac_compress ]; + slen2 = shine_slen2_tab[ gi->scalefac_compress ]; + + if ( !gr || !(config->side_info.scfsi[ch][0]) ) + bits += (6 * slen1); + + if ( !gr || !(config->side_info.scfsi[ch][1]) ) + bits += (5 * slen1); + + if ( !gr || !(config->side_info.scfsi[ch][2]) ) + bits += (5 * slen2); + + if ( !gr || !(config->side_info.scfsi[ch][3]) ) + bits += (5 * slen2); + } + return bits; +} + +/* + * calc_xmin: + * ---------- + * Calculate the allowed distortion for each scalefactor band, + * as determined by the psychoacoustic model. + * xmin(sb) = ratio(sb) * en(sb) / bw(sb) + */ +void calc_xmin(gr_info *cod_info, + shine_psy_xmin_t *l3_xmin, + int gr, int ch ) { + int sfb; + + for ( sfb = cod_info->sfb_lmax; sfb--; ) + { + /*note. xmin will always be zero with no psychoacoustic model + + start = scalefac_band_long[ sfb ]; + end = scalefac_band_long[ sfb+1 ]; + bw = end - start; + + for ( en = 0, l = start; l < end; l++ ) + en += config->l3loop->xrsq[l]; + + l3_xmin->l[gr][ch][sfb] = ratio->l[gr][ch][sfb] * en / bw;*/ + + l3_xmin->l[gr][ch][sfb] = 0; + } +} + +/* + * shine_loop_initialise: + * ------------------- + * Calculates the look up tables used by the iteration loop. + */ +void shine_loop_initialise(shine_global_config *config) { + int i; + + /* quantize: stepsize conversion, fourth root of 2 table. + * The table is inverted (negative power) from the equation given + * in the spec because it is quicker to do x*y than x/y. + * The 0.5 is for rounding. + */ + for(i=128; i--;) + { + config->l3loop->steptab[i] = pow(2.0,(double)(127-i)/4); + if((config->l3loop->steptab[i]*2)>0x7fffffff) /* MAXINT = 2**31 = 2**(124/4) */ + config->l3loop->steptabi[i]=0x7fffffff; + else + /* The table is multiplied by 2 to give an extra bit of accuracy. + * In quantize, the long multiply does not shift it's result left one + * bit to compensate. + */ + config->l3loop->steptabi[i] = (int32_t)((config->l3loop->steptab[i]*2) + 0.5); + } + + /* quantize: vector conversion, three quarter power table. + * The 0.5 is for rounding, the .0946 comes from the spec. + */ + for(i=10000; i--;) + config->l3loop->int2idx[i] = (int)(sqrt(sqrt((double)i)*(double)i) - 0.0946 + 0.5); +} + +/* + * quantize: + * --------- + * Function: Quantization of the vector xr ( -> ix). + * Returns maximum value of ix. + */ +int quantize(int ix[GRANULE_SIZE], int stepsize, shine_global_config *config ) +{ + int i, max, ln; + int32_t scalei; + float scale, dbl; + + scalei = config->l3loop->steptabi[stepsize+127]; /* 2**(-stepsize/4) */ + + /* a quick check to see if ixmax will be less than 8192 */ + /* this speeds up the early calls to bin_search_StepSize */ + if((mulr(config->l3loop->xrmax,scalei)) > 165140) /* 8192**(4/3) */ + max = 16384; /* no point in continuing, stepsize not big enough */ + else + for(i=0, max=0;il3loop->xr[i]),scalei); + + if(ln<10000) /* ln < 10000 catches most values */ + ix[i] = config->l3loop->int2idx[ln]; /* quick look up method */ + else + { + /* outside table range so have to do it using floats */ + scale = config->l3loop->steptab[stepsize+127]; /* 2**(-stepsize/4) */ + dbl = ((float)config->l3loop->xrabs[i]) * scale * 4.656612875e-10; /* 0x7fffffff */ + //ix[i] = sqrt_int((int)(f_sqrt(dbl)*dbl)); /* dbl**(3/4) */ + ix[i] = (int)sqrt(sqrt(dbl)*dbl); /* dbl**(3/4) */ + } + + /* calculate ixmax while we're here */ + /* note. ix cannot be negative */ + if(max < ix[i]) + max = ix[i]; + } + + return max; +} + +/* + * ix_max: + * ------- + * Function: Calculate the maximum of ix from 0 to 575 + */ +static inline int ix_max( int ix[GRANULE_SIZE], unsigned int begin, unsigned int end ) { + register int i; + register int max = 0; + + for(i=begin;i 1; i -= 2 ) + if ( !ix[i-1] && !ix[i-2] ) + rzero++; + else + break; + + cod_info->count1 = 0 ; + for ( ; i > 3; i -= 4 ) + if ( ix[i-1] <= 1 + && ix[i-2] <= 1 + && ix[i-3] <= 1 + && ix[i-4] <= 1 ) + cod_info->count1++; + else + break; + + cod_info->big_values = i>>1; +} + +/* + * count1_bitcount: + * ---------------- + * Determines the number of bits to encode the quadruples. + */ +int count1_bitcount(int ix[GRANULE_SIZE], gr_info *cod_info) { + int p, i, k; + int v, w, x, y, signbits; + int sum0 = 0, + sum1 = 0; + + for(i=cod_info->big_values<<1, k=0; kcount1; i+=4, k++) + { + v = ix[i]; + w = ix[i+1]; + x = ix[i+2]; + y = ix[i+3]; + + p = v + (w<<1) + (x<<2) + (y<<3); + + signbits = 0; + if(v!=0) signbits++; + if(w!=0) signbits++; + if(x!=0) signbits++; + if(y!=0) signbits++; + + sum0 += signbits; + sum1 += signbits; + + sum0 += shine_huffman_table[32].hlen[p]; + sum1 += shine_huffman_table[33].hlen[p]; + } + + if(sum0count1table_select = 0; + return sum0; + } + else + { + cod_info->count1table_select = 1; + return sum1; + } +} + +/* + * subdivide: + * ---------- + * presumable subdivides the bigvalue region which will use separate Huffman tables. + */ +void subdivide(gr_info *cod_info, shine_global_config *config) { + static const struct + { + unsigned region0_count; + unsigned region1_count; + } subdv_table[ 23 ] = + { + {0, 0}, /* 0 bands */ + {0, 0}, /* 1 bands */ + {0, 0}, /* 2 bands */ + {0, 0}, /* 3 bands */ + {0, 0}, /* 4 bands */ + {0, 1}, /* 5 bands */ + {1, 1}, /* 6 bands */ + {1, 1}, /* 7 bands */ + {1, 2}, /* 8 bands */ + {2, 2}, /* 9 bands */ + {2, 3}, /* 10 bands */ + {2, 3}, /* 11 bands */ + {3, 4}, /* 12 bands */ + {3, 4}, /* 13 bands */ + {3, 4}, /* 14 bands */ + {4, 5}, /* 15 bands */ + {4, 5}, /* 16 bands */ + {4, 6}, /* 17 bands */ + {5, 6}, /* 18 bands */ + {5, 6}, /* 19 bands */ + {5, 7}, /* 20 bands */ + {6, 7}, /* 21 bands */ + {6, 7}, /* 22 bands */ + }; + + if (!cod_info->big_values) + { /* no big_values region */ + cod_info->region0_count = 0; + cod_info->region1_count = 0; + } + else + { + const int *scalefac_band_long = &shine_scale_fact_band_index[config->mpeg.samplerate_index][0]; + int bigvalues_region, scfb_anz, thiscount; + + bigvalues_region = 2 * cod_info->big_values; + + /* Calculate scfb_anz */ + scfb_anz = 0; + while ( scalefac_band_long[scfb_anz] < bigvalues_region ) + scfb_anz++; + + for (thiscount = subdv_table[scfb_anz].region0_count; thiscount; thiscount--) { + if (scalefac_band_long[thiscount + 1] <= bigvalues_region) + break; + } + cod_info->region0_count = thiscount; + cod_info->address1 = scalefac_band_long[thiscount + 1]; + + scalefac_band_long += cod_info->region0_count + 1; + + for (thiscount = subdv_table[scfb_anz].region1_count; thiscount; thiscount--) { + if (scalefac_band_long[thiscount + 1] <= bigvalues_region) + break; + } + cod_info->region1_count = thiscount; + cod_info->address2 = scalefac_band_long[thiscount + 1]; + + cod_info->address3 = bigvalues_region; + } +} + +/* + * bigv_tab_select: + * ---------------- + * Function: Select huffman code tables for bigvalues regions + */ +void bigv_tab_select( int ix[GRANULE_SIZE], gr_info *cod_info ) { + cod_info->table_select[0] = 0; + cod_info->table_select[1] = 0; + cod_info->table_select[2] = 0; + + { + if ( cod_info->address1 > 0 ) + cod_info->table_select[0] = new_choose_table( ix, 0, cod_info->address1 ); + + if ( cod_info->address2 > cod_info->address1 ) + cod_info->table_select[1] = new_choose_table( ix, cod_info->address1, cod_info->address2 ); + + if ( cod_info->big_values<<1 > cod_info->address2 ) + cod_info->table_select[2] = new_choose_table( ix, cod_info->address2, cod_info->big_values<<1 ); + } +} + +/* + * new_choose_table: + * ----------------- + * Choose the Huffman table that will encode ix[begin..end] with + * the fewest bits. + * Note: This code contains knowledge about the sizes and characteristics + * of the Huffman tables as defined in the IS (Table B.7), and will not work + * with any arbitrary tables. + */ +int new_choose_table( int ix[GRANULE_SIZE], unsigned int begin, unsigned int end ) { + int i, max; + int choice[2]; + int sum[2]; + + max = ix_max(ix,begin,end); + if(!max) + return 0; + + choice[0] = 0; + choice[1] = 0; + + if(max<15) + { + /* try tables with no linbits */ + for ( i =14; i--; ) + if ( shine_huffman_table[i].xlen > max ) + { + choice[0] = i; + break; + } + + sum[0] = count_bit( ix, begin, end, choice[0] ); + + switch (choice[0]) + { + case 2: + sum[1] = count_bit( ix, begin, end, 3 ); + if ( sum[1] <= sum[0] ) + choice[0] = 3; + break; + + case 5: + sum[1] = count_bit( ix, begin, end, 6 ); + if ( sum[1] <= sum[0] ) + choice[0] = 6; + break; + + case 7: + sum[1] = count_bit( ix, begin, end, 8 ); + if ( sum[1] <= sum[0] ) + { + choice[0] = 8; + sum[0] = sum[1]; + } + sum[1] = count_bit( ix, begin, end, 9 ); + if ( sum[1] <= sum[0] ) + choice[0] = 9; + break; + + case 10: + sum[1] = count_bit( ix, begin, end, 11 ); + if ( sum[1] <= sum[0] ) + { + choice[0] = 11; + sum[0] = sum[1]; + } + sum[1] = count_bit( ix, begin, end, 12 ); + if ( sum[1] <= sum[0] ) + choice[0] = 12; + break; + + case 13: + sum[1] = count_bit( ix, begin, end, 15 ); + if ( sum[1] <= sum[0] ) + choice[0] = 15; + break; + } + } + else + { + /* try tables with linbits */ + max -= 15; + + for(i=15;i<24;i++) + if(shine_huffman_table[i].linmax>=max) + { + choice[0] = i; + break; + } + + for(i=24;i<32;i++) + if(shine_huffman_table[i].linmax>=max) + { + choice[1] = i; + break; + } + + sum[0] = count_bit(ix,begin,end,choice[0]); + sum[1] = count_bit(ix,begin,end,choice[1]); + if (sum[1]table_select[0])) /* region0 */ + bits += count_bit(ix, 0, gi->address1, table ); + if( (table=gi->table_select[1])) /* region1 */ + bits += count_bit(ix, gi->address1, gi->address2, table ); + if( (table=gi->table_select[2])) /* region2 */ + bits += count_bit(ix, gi->address2, gi->address3, table ); + return bits; +} + +/* + * count_bit: + * ---------- + * Function: Count the number of bits necessary to code the subregion. + */ +int count_bit(int ix[GRANULE_SIZE], + unsigned int start, + unsigned int end, + unsigned int table ) { + unsigned linbits, ylen; + register int i, sum; + register int x,y; + const struct huffcodetab *h; + + if(!table) + return 0; + + h = &(shine_huffman_table[table]); + sum = 0; + + ylen = h->ylen; + linbits = h->linbits; + + if(table>15) + { /* ESC-table is used */ + for(i=start;i14) + { + x = 15; + sum += linbits; + } + if(y>14) + { + y = 15; + sum += linbits; + } + + sum += h->hlen[(x*ylen)+y]; + if(x) + sum++; + if(y) + sum++; + } + } + else + { /* No ESC-words */ + for(i=start;ihlen[(x*ylen)+y]; + + if(x!=0) + sum++; + if(y!=0) + sum++; + } + } + return sum; +} + +/* + * bin_search_StepSize: + * -------------------- + * Succesive approximation approach to obtaining a initial quantizer + * step size. + * The following optional code written by Seymour Shlien + * will speed up the shine_outer_loop code which is called + * by iteration_loop. When BIN_SEARCH is defined, the + * shine_outer_loop function precedes the call to the function shine_inner_loop + * with a call to bin_search gain defined below, which + * returns a good starting quantizerStepSize. + */ +int bin_search_StepSize(int desired_rate, int ix[GRANULE_SIZE], + gr_info * cod_info, shine_global_config *config) { + int bit, next, count; + + next = -120; + count = 120; + + do { + int half = count / 2; + + if (quantize(ix, next + half, config) > 8192) + bit = 100000; /* fail */ + else + { + calc_runlen(ix, cod_info); /* rzero,count1,big_values */ + bit = count1_bitcount(ix, cod_info); /* count1_table selection */ + subdivide(cod_info, config); /* bigvalues sfb division */ + bigv_tab_select(ix, cod_info); /* codebook selection */ + bit += bigv_bitcount(ix, cod_info); /* bit count */ + } + + if (bit < desired_rate) + count = half; + else + { + next += half; + count -= half; + } + } while (count > 1); + + return next; +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3loop.h b/lib/lib_audio/mp3_shine_esp32/src/l3loop.h new file mode 100755 index 000000000..0e71a9100 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3loop.h @@ -0,0 +1,9 @@ +#ifndef L3LOOP_H +#define L3LOOP_H + +void shine_loop_initialise(shine_global_config *config); + +void shine_iteration_loop(shine_global_config *config); + +#endif + diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp b/lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp new file mode 100755 index 000000000..bbe660675 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp @@ -0,0 +1,134 @@ +/* L3mdct */ + +#include "types.h" +#include "l3mdct.h" +#include "l3subband.h" + +/* This is table B.9: coefficients for aliasing reduction */ +#define MDCT_CA(coef) (int32_t)(coef / sqrt(1.0 + (coef * coef)) * 0x7fffffff) +#define MDCT_CS(coef) (int32_t)(1.0 / sqrt(1.0 + (coef * coef)) * 0x7fffffff) + +#define MDCT_CA0 MDCT_CA(-0.6) +#define MDCT_CA1 MDCT_CA(-0.535) +#define MDCT_CA2 MDCT_CA(-0.33) +#define MDCT_CA3 MDCT_CA(-0.185) +#define MDCT_CA4 MDCT_CA(-0.095) +#define MDCT_CA5 MDCT_CA(-0.041) +#define MDCT_CA6 MDCT_CA(-0.0142) +#define MDCT_CA7 MDCT_CA(-0.0037) + +#define MDCT_CS0 MDCT_CS(-0.6) +#define MDCT_CS1 MDCT_CS(-0.535) +#define MDCT_CS2 MDCT_CS(-0.33) +#define MDCT_CS3 MDCT_CS(-0.185) +#define MDCT_CS4 MDCT_CS(-0.095) +#define MDCT_CS5 MDCT_CS(-0.041) +#define MDCT_CS6 MDCT_CS(-0.0142) +#define MDCT_CS7 MDCT_CS(-0.0037) + +/* + * shine_mdct_initialise: + * ------------------- + */ +void shine_mdct_initialise(shine_global_config *config) { + int m,k; + + /* prepare the mdct coefficients */ + for(m=18; m--; ) + for(k=36; k--; ) + /* combine window and mdct coefficients into a single table */ + /* scale and convert to fixed point before storing */ + config->mdct.cos_l[m][k] = (int32_t)(sin(PI36*(k+0.5)) + * cos((PI/72)*(2*k+19)*(2*m+1)) * 0x7fffffff); +} + +/* + * shine_mdct_sub: + * ------------ + */ +void shine_mdct_sub(shine_global_config *config, int stride) { + /* note. we wish to access the array 'config->mdct_freq[2][2][576]' as + * [2][2][32][18]. (32*18=576), + */ + int32_t (*mdct_enc)[18]; + + int ch,gr,band,j,k; + int32_t mdct_in[36]; + + for(ch=config->wave.channels; ch--; ) + { + for(gr=0; grmpeg.granules_per_frame; gr++) + { + /* set up pointer to the part of config->mdct_freq we're using */ + mdct_enc = (int32_t (*)[18]) config->mdct_freq[ch][gr]; + + /* polyphase filtering */ + for(k=0; k<18; k+=2) + { + shine_window_filter_subband(&config->buffer[ch], &config->l3_sb_sample[ch][gr+1][k ][0], ch, config, stride); + shine_window_filter_subband(&config->buffer[ch], &config->l3_sb_sample[ch][gr+1][k+1][0], ch, config, stride); + /* Compensate for inversion in the analysis filter + * (every odd index of band AND k) + */ + for(band=1; band<32; band+=2) + config->l3_sb_sample[ch][gr+1][k+1][band] *= -1; + } + + /* Perform imdct of 18 previous subband samples + 18 current subband samples */ + for(band=0; band<32; band++) + { + for(k=18; k--; ) + { + mdct_in[k ] = config->l3_sb_sample[ch][gr ][k][band]; + mdct_in[k+18] = config->l3_sb_sample[ch][gr+1][k][band]; + } + + /* Calculation of the MDCT + * In the case of long blocks ( block_type 0,1,3 ) there are + * 36 coefficients in the time domain and 18 in the frequency + * domain. + */ + + for(k=18; k--; ) + { + int32_t vm; +#ifdef __BORLANDC__ + uint32_t vm_lo; +#else + uint32_t vm_lo __attribute__((unused)); +#endif + + mul0(vm, vm_lo, mdct_in[35], config->mdct.cos_l[k][35]); + for(j=35; j; j-=7) { + muladd(vm, vm_lo, mdct_in[j-1], config->mdct.cos_l[k][j-1]); + muladd(vm, vm_lo, mdct_in[j-2], config->mdct.cos_l[k][j-2]); + muladd(vm, vm_lo, mdct_in[j-3], config->mdct.cos_l[k][j-3]); + muladd(vm, vm_lo, mdct_in[j-4], config->mdct.cos_l[k][j-4]); + muladd(vm, vm_lo, mdct_in[j-5], config->mdct.cos_l[k][j-5]); + muladd(vm, vm_lo, mdct_in[j-6], config->mdct.cos_l[k][j-6]); + muladd(vm, vm_lo, mdct_in[j-7], config->mdct.cos_l[k][j-7]); + } + mulz(vm, vm_lo); + mdct_enc[band][k] = vm; + } + + /* Perform aliasing reduction butterfly */ + asm ("#cmuls:"); + if (band != 0) + { + cmuls(mdct_enc[band][0], mdct_enc[band-1][17-0], mdct_enc[band][0], mdct_enc[band-1][17-0], MDCT_CS0, MDCT_CA0); + cmuls(mdct_enc[band][1], mdct_enc[band-1][17-1], mdct_enc[band][1], mdct_enc[band-1][17-1], MDCT_CS1, MDCT_CA1); + cmuls(mdct_enc[band][2], mdct_enc[band-1][17-2], mdct_enc[band][2], mdct_enc[band-1][17-2], MDCT_CS2, MDCT_CA2); + cmuls(mdct_enc[band][3], mdct_enc[band-1][17-3], mdct_enc[band][3], mdct_enc[band-1][17-3], MDCT_CS3, MDCT_CA3); + cmuls(mdct_enc[band][4], mdct_enc[band-1][17-4], mdct_enc[band][4], mdct_enc[band-1][17-4], MDCT_CS4, MDCT_CA4); + cmuls(mdct_enc[band][5], mdct_enc[band-1][17-5], mdct_enc[band][5], mdct_enc[band-1][17-5], MDCT_CS5, MDCT_CA5); + cmuls(mdct_enc[band][6], mdct_enc[band-1][17-6], mdct_enc[band][6], mdct_enc[band-1][17-6], MDCT_CS6, MDCT_CA6); + cmuls(mdct_enc[band][7], mdct_enc[band-1][17-7], mdct_enc[band][7], mdct_enc[band-1][17-7], MDCT_CS7, MDCT_CA7); + } + } + } + + /* Save latest granule's subband samples to be used in the next mdct call */ + memcpy(config->l3_sb_sample[ch][0], config->l3_sb_sample[ch][config->mpeg.granules_per_frame], sizeof(config->l3_sb_sample[0][0])); + } +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3mdct.h b/lib/lib_audio/mp3_shine_esp32/src/l3mdct.h new file mode 100755 index 000000000..b374fd421 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3mdct.h @@ -0,0 +1,7 @@ +#ifndef shine_MDCT_H +#define shine_MDCT_H + +void shine_mdct_initialise(shine_global_config *config); +void shine_mdct_sub(shine_global_config *config, int stride); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp b/lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp new file mode 100755 index 000000000..e6d8bdaae --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp @@ -0,0 +1,104 @@ +/* L3SubBand */ + +#include "types.h" +#include "tables.h" +#include "l3subband.h" + +/* + * shine_subband_initialise: + * ---------------------- + * Calculates the analysis filterbank coefficients and rounds to the + * 9th decimal place accuracy of the filterbank tables in the ISO + * document. The coefficients are stored in #filter# + */ +void shine_subband_initialise(shine_global_config *config) { + int i,j; + double filter; + + for(i=MAX_CHANNELS; i-- ; ) { + config->subband.off[i] = 0; + memset(config->subband.x[i], 0, sizeof(config->subband.x[i])); + } + + for (i=SBLIMIT; i--; ) + for (j=64; j--; ) + { + if ((filter = 1e9*cos((double)((2*i+1)*(16-j)*PI64))) >= 0) + modf(filter+0.5, &filter); + else + modf(filter-0.5, &filter); + /* scale and convert to fixed point before storing */ + config->subband.fl[i][j] = (int32_t)(filter * (0x7fffffff * 1e-9)); + } +} + +/* + * shine_window_filter_subband: + * ------------------------- + * Overlapping window on PCM samples + * 32 16-bit pcm samples are scaled to fractional 2's complement and + * concatenated to the end of the window buffer #x#. The updated window + * buffer #x# is then windowed by the analysis window #shine_enwindow# to produce + * the windowed sample #z# + * Calculates the analysis filter bank coefficients + * The windowed samples #z# is filtered by the digital filter matrix #filter# + * to produce the subband samples #s#. This done by first selectively + * picking out values from the windowed samples, and then multiplying + * them by the filter matrix, producing 32 subband samples. + */ +void shine_window_filter_subband(int16_t **buffer, int32_t s[SBLIMIT], int ch, shine_global_config *config, int stride) { + int32_t y[64]; + int i,j; + int16_t *ptr = *buffer; + + /* replace 32 oldest samples with 32 new samples */ + for (i=32;i--;) { + config->subband.x[ch][i+config->subband.off[ch]] = ((int32_t)*ptr) << 16; + ptr += stride; + } + *buffer = ptr; + + for (i=64; i--; ) { + int32_t s_value; +#ifdef __BORLANDC__ + uint32_t s_value_lo; +#else + uint32_t s_value_lo __attribute__((unused)); +#endif + + mul0 (s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (0<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (0<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (1<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (1<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (2<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (2<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (3<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (3<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (4<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (4<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (5<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (5<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (6<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (6<<6)]); + muladd(s_value, s_value_lo, config->subband.x[ch][(config->subband.off[ch] + i + (7<<6)) & (HAN_SIZE-1)], shine_enwindow[i + (7<<6)]); + mulz (s_value, s_value_lo); + y[i] = s_value; + } + + config->subband.off[ch] = (config->subband.off[ch] + 480) & (HAN_SIZE-1); /* offset is modulo (HAN_SIZE)*/ + + for (i=SBLIMIT; i--; ) { + int32_t s_value; +#ifdef __BORLANDC__ + uint32_t s_value_lo; +#else + uint32_t s_value_lo __attribute__((unused)); +#endif + + mul0(s_value, s_value_lo, config->subband.fl[i][63], y[63]); + for (j=63; j; j-=7) { + muladd(s_value, s_value_lo, config->subband.fl[i][j-1], y[j-1]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-2], y[j-2]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-3], y[j-3]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-4], y[j-4]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-5], y[j-5]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-6], y[j-6]); + muladd(s_value, s_value_lo, config->subband.fl[i][j-7], y[j-7]); + } + mulz(s_value, s_value_lo); + s[i] = s_value; + } +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3subband.h b/lib/lib_audio/mp3_shine_esp32/src/l3subband.h new file mode 100755 index 000000000..9bfe861ee --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/l3subband.h @@ -0,0 +1,9 @@ +#ifndef L3SUBBAND_H +#define L3SUBBAND_H + +#include + +void shine_subband_initialise( shine_global_config *config ); +void shine_window_filter_subband(int16_t **buffer, int32_t s[SBLIMIT], int k, shine_global_config *config, int stride); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp new file mode 100755 index 000000000..d0ec59c3b --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp @@ -0,0 +1,290 @@ +/* layer3.c */ + +#ifdef ESP32 + +#include "types.h" +#include "tables.h" +#include "layer3.h" +#include "l3subband.h" +#include "l3mdct.h" +#include "l3loop.h" +#include "bitstream.h" +#include "l3bitstream.h" +#include "esp_heap_caps.h" +#include "esp_system.h" +#include "freertos/FreeRTOS.h" + +static uint32_t counter[5] = {0}; + +static int granules_per_frame[4] = { + 1, /* MPEG 2.5 */ + -1, /* Reserved */ + 1, /* MPEG II */ + 2 /* MPEG I */ +}; + +/* Set default values for important vars */ +void shine_set_config_mpeg_defaults(shine_mpeg_t *mpeg) { + mpeg->bitr = 128; + mpeg->emph = NONE; + mpeg->copyright = 0; + mpeg->original = 1; +} + +int shine_mpeg_version(int samplerate_index) { + /* Pick mpeg version according to samplerate index. */ + if (samplerate_index < 3) { + /* First 3 samplerates are for MPEG-I */ + return MPEG_I; + } else if (samplerate_index < 6) { + /* Then it's MPEG-II */ + return MPEG_II; + } else { + /* Finally, MPEG-2.5 */ + return MPEG_25; + } +} + +int shine_find_samplerate_index(int freq) { + int i; + + for(i=0;i<9;i++) { + if(freq==samplerates[i]) return i; + } + return -1; /* error - not a valid samplerate for encoder */ +} + +int shine_find_bitrate_index(int bitr, int mpeg_version) { + int i; + + for(i=0;i<16;i++) { + if(bitr==bitrates[i][mpeg_version]) return i; + } + return -1; /* error - not a valid samplerate for encoder */ +} + +int shine_check_config(int freq, int bitr) { + int samplerate_index, bitrate_index, mpeg_version; + + samplerate_index = shine_find_samplerate_index(freq); + if (samplerate_index < 0) { + return -1; + } + mpeg_version = shine_mpeg_version(samplerate_index); + + bitrate_index = shine_find_bitrate_index(bitr, mpeg_version); + if (bitrate_index < 0) { + return -1; + } + return mpeg_version; +} + +int shine_samples_per_pass(shine_t s) { + return s->mpeg.granules_per_frame * GRANULE_SIZE; +} + +/* Compute default encoding values. */ +shine_global_config *shine_initialise(shine_config_t *pub_config) { + double avg_slots_per_frame; + shine_global_config *config; + int x, y; + if (shine_check_config(pub_config->wave.samplerate, pub_config->mpeg.bitr) < 0) { + return NULL; + } + + config = (shine_global_config*)heap_caps_malloc(sizeof(shine_global_config), MALLOC_CAP_SPIRAM); + if (config == NULL) { + return config; + } + + memset(config, 0, sizeof(shine_global_config)); + +#ifdef SHINE_DEBUG + printf("l3_enc & mdct_freq each: %d\n", sizeof(int32_t)*GRANULE_SIZE*MAX_GRANULES*MAX_CHANNELS); +#endif + + for (x = 0; x < MAX_CHANNELS; x++) { + for (y = 0; y < MAX_GRANULES; y++) { + // 2 * 2 * 576 each + config->l3_enc[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //Significant performance hit in IRAM + config->mdct_freq[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 1% + } + } +#ifdef SHINE_DEBUG + printf("l3loop struct: %d\n", sizeof(l3loop_t)); +#endif + config->l3loop = (l3loop_t*)heap_caps_malloc(sizeof(l3loop_t), MALLOC_CAP_SPIRAM); +#ifdef SHINE_DEBUG + printf("xrsq & xrabs each: %d\n", sizeof(int32_t)*GRANULE_SIZE); +#endif + config->l3loop->xrsq = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5% + config->l3loop->xrabs = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5% + +/*typedef struct { + int32_t *xr; + int32_t *xrsq[GRANULE_SIZE]; + int32_t *xrabs[GRANULE_SIZE]; + int32_t xrmax; + int32_t en_tot[MAX_GRANULES]; + int32_t en[MAX_GRANULES][21]; + int32_t xm[MAX_GRANULES][21]; + int32_t xrmaxl[MAX_GRANULES]; + double steptab[128]; + int32_t steptabi[128]; + int16_t int2idx[10000]; +} l3loop_t;*/ + + shine_subband_initialise(config); + shine_mdct_initialise(config); + shine_loop_initialise(config); + + /* Copy public config. */ + config->wave.channels = pub_config->wave.channels; + config->wave.samplerate = pub_config->wave.samplerate; + config->mpeg.mode = pub_config->mpeg.mode; + config->mpeg.bitr = pub_config->mpeg.bitr; + config->mpeg.emph = pub_config->mpeg.emph; + config->mpeg.copyright = pub_config->mpeg.copyright; + config->mpeg.original = pub_config->mpeg.original; + + /* Set default values. */ + config->ResvMax = 0; + config->ResvSize = 0; + config->mpeg.layer = LAYER_III; + config->mpeg.crc = 0; + config->mpeg.ext = 0; + config->mpeg.mode_ext = 0; + config->mpeg.bits_per_slot = 8; + + config->mpeg.samplerate_index = shine_find_samplerate_index(config->wave.samplerate); + config->mpeg.version = shine_mpeg_version(config->mpeg.samplerate_index); + config->mpeg.bitrate_index = shine_find_bitrate_index(config->mpeg.bitr, config->mpeg.version); + config->mpeg.granules_per_frame = granules_per_frame[config->mpeg.version]; + + /* Figure average number of 'slots' per frame. */ + avg_slots_per_frame = ((double)config->mpeg.granules_per_frame * GRANULE_SIZE / + ((double)config->wave.samplerate)) * + (1000*(double)config->mpeg.bitr / + (double)config->mpeg.bits_per_slot); + + config->mpeg.whole_slots_per_frame = (int)avg_slots_per_frame; + + config->mpeg.frac_slots_per_frame = avg_slots_per_frame - (double)config->mpeg.whole_slots_per_frame; + config->mpeg.slot_lag = -config->mpeg.frac_slots_per_frame; + + if(config->mpeg.frac_slots_per_frame==0) { + config->mpeg.padding = 0; + } + + shine_open_bit_stream(&config->bs, BUFFER_SIZE); + + memset((char *)&config->side_info,0,sizeof(shine_side_info_t)); + + /* determine the mean bitrate for main data */ + if (config->mpeg.granules_per_frame == 2) { /* MPEG 1 */ + config->sideinfo_len = 8 * ((config->wave.channels==1) ? 4 + 17 : 4 + 32); + } else { /* MPEG 2 */ + config->sideinfo_len = 8 * ((config->wave.channels==1) ? 4 + 9 : 4 + 17); + } + return config; +} + + + +uint32_t *shine_get_counters() { + return counter; +} + +/* Counter results +Counters 1550541561 : 1550541629 : 1553135798 : 1555116724 : 1555309952 +68 +core 1 will do: +2594169 +core 0 will do: +1980926 +193228 + + +Counters 2664123380 : 2664123448 : 2666717886 : 2668665908 : 2668859025 +*/ + + +static unsigned char *shine_encode_buffer_internal(shine_global_config *config, int *written, int stride) { + counter[0] = xthal_get_ccount(); + if(config->mpeg.frac_slots_per_frame) { + config->mpeg.padding = (config->mpeg.slot_lag <= (config->mpeg.frac_slots_per_frame - 1.0)); + config->mpeg.slot_lag += (config->mpeg.padding - config->mpeg.frac_slots_per_frame); + } + + config->mpeg.bits_per_frame = 8*(config->mpeg.whole_slots_per_frame + config->mpeg.padding); + config->mean_bits = (config->mpeg.bits_per_frame - config->sideinfo_len)/config->mpeg.granules_per_frame; + counter[1] = xthal_get_ccount(); + /* apply mdct to the polyphase output */ + // put on core 1 + shine_mdct_sub(config, stride); + counter[2] = xthal_get_ccount(); + /* bit and noise allocation */ + //put on core 0 + shine_iteration_loop(config); + counter[3] = xthal_get_ccount(); + /* write the frame to the bitstream */ + shine_format_bitstream(config); + counter[4] = xthal_get_ccount(); + /* Return data. */ + *written = config->bs.data_position; + config->bs.data_position = 0; + + return config->bs.data; +} + +unsigned char *shine_encode_buffer(shine_global_config *config, int16_t **data, int *written) { + config->buffer[0] = data[0]; + if (config->wave.channels == 2) { + config->buffer[1] = data[1]; + } + return shine_encode_buffer_internal(config, written, 1); +} + +unsigned char *shine_encode_buffer_interleaved(shine_global_config *config, int16_t *data, int *written) { + config->buffer[0] = data; + if (config->wave.channels == 2) { + config->buffer[1] = data + 1; + } + return shine_encode_buffer_internal(config, written, config->wave.channels); +} + +unsigned char *shine_flush(shine_global_config *config, int *written) { + *written = config->bs.data_position; + config->bs.data_position = 0; + return config->bs.data; +} + +void shine_close(shine_global_config *config) { + shine_close_bit_stream(&config->bs); + + for (uint16_t x = 0; x < MAX_CHANNELS; x++) { + for (uint16_t y = 0; y < MAX_GRANULES; y++) { + if (config->l3_enc[x][y]) { + free(config->l3_enc[x][y]); + } + if (config->mdct_freq[x][y]) { + free(config->mdct_freq[x][y]); + } + } + } + + config->l3loop = (l3loop_t*)heap_caps_malloc(sizeof(l3loop_t), MALLOC_CAP_SPIRAM); + if (config->l3loop) { + free(config->l3loop); + } + if (config->l3loop->xrsq) { + free(config->l3loop->xrsq); + } + if (config->l3loop->xrabs) { + free(config->l3loop->xrabs); + } + + free(config); +} + +#endif // ESP32 diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.h b/lib/lib_audio/mp3_shine_esp32/src/layer3.h new file mode 100755 index 000000000..20be3de91 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/layer3.h @@ -0,0 +1,166 @@ +#ifndef LAYER3_H +#define LAYER3_H + +#include + +/* This is the struct used to tell the encoder about the input PCM */ + +//#define SHINE_DEBUG + +enum channels { + PCM_MONO = 1, + PCM_STEREO = 2 +}; + +enum mpeg_versions { + MPEG_I = 3, + MPEG_II = 2, + MPEG_25 = 0 +}; + +/* Only Layer III currently implemented. */ +enum mpeg_layers { + LAYER_III = 1 +}; + +typedef struct { + enum channels channels; + int samplerate; +} shine_wave_t; + +/* This is the struct the encoder uses to tell the encoder about the output MP3 */ + +enum modes { + STEREO = 0, + JOINT_STEREO = 1, + DUAL_CHANNEL = 2, + MONO = 3 +}; + +enum emph { + NONE = 0, + MU50_15 = 1, + CITT = 3 +}; + +typedef struct { + enum modes mode; /* Stereo mode */ + int bitr; /* Must conform to known bitrate */ + enum emph emph; /* De-emphasis */ + int copyright; + int original; +} shine_mpeg_t; + +typedef struct { + shine_wave_t wave; + shine_mpeg_t mpeg; +} shine_config_t; + +/* Tables of supported audio parameters & format. + * + * Valid samplerates and bitrates. + * const int samplerates[9] = { + * 44100, 48000, 32000, // MPEG-I + * 22050, 24000, 16000, // MPEG-II + * 11025, 12000, 8000 // MPEG-2.5 + * }; + * + * const int bitrates[16][4] = { + * // MPEG version: + * // 2.5, reserved, II, I + * { -1, -1, -1, -1}, + * { 8, -1, 8, 32}, + * { 16, -1, 16, 40}, + * { 24, -1, 24, 48}, + * { 32, -1, 32, 56}, + * { 40, -1, 40, 64}, + * { 48, -1, 48, 80}, + * { 56, -1, 56, 96}, + * { 64, -1, 64, 112}, + * { 80, -1, 80, 128}, + * { 96, -1, 96, 160}, + * {112, -1, 112, 192}, + * {128, -1, 128, 224}, + * {144, -1, 144, 256}, + * {160, -1, 160, 320}, + * { -1, -1, -1, -1} + * }; + * + */ + + +/* Abtract type for the shine encoder handle. */ +typedef struct shine_global_flags *shine_t; + +/* Fill in a `mpeg_t` structure with default values. */ +void shine_set_config_mpeg_defaults(shine_mpeg_t *mpeg); + +/* Check if a given bitrate is supported by the encoder (see `bitrates` above for a list + * of acceptable values. */ +int shine_find_bitrate_index(int bitr, int mpeg_version); + +/* Check if a given samplerate is supported by the encoder (see `samplerates` above for a list + * of acceptable values. */ +int shine_find_samplerate_index(int freq); + +/* Returns the MPEG version used for the given samplerate index. See above + * `mpeg_versions` for a list of possible values. */ +int shine_mpeg_version(int samplerate_index); + +/* Check if a given bitrate and samplerate is supported by the encoder (see `samplerates` + * and `bitrates` above for a list of acceptable values). + * + * Returns -1 on error, mpeg_version on success. */ +int shine_check_config(int freq, int bitr); + +/* Pass a pointer to a `config_t` structure and returns an initialized + * encoder. + * + * Configuration data is copied over to the encoder. It is not possible + * to change its values after initializing the encoder at the moment. + * + * Checking for valid configuration values is left for the application to + * implement. You can use the `shine_find_bitrate_index` and + * `shine_find_samplerate_index` functions or the `bitrates` and + * `samplerates` arrays above to check those parameters. Mone and stereo + * mode for wave and mpeg should also be consistent with each other. + * + * This function returns NULL if it was not able to allocate memory data for + * the encoder. */ +shine_t shine_initialise(shine_config_t *config); + +/* Maximun possible value for the function below. */ +#define SHINE_MAX_SAMPLES 1152 + +uint32_t *shine_get_counters(); + +/* Returns audio samples expected in each frame. */ +int shine_samples_per_pass(shine_t s); + +/* Encode audio data. Source data must have `shine_samples_per_pass(s)` audio samples per + * channels. Mono encoder only expect one channel. + * + * Returns a pointer to freshly encoded data while `written` contains the size of + * available data. This pointer's memory is handled by the library and is only valid + * until the next call to `shine_encode_buffer` or `shine_close` and may be NULL if no data + * was written. */ +unsigned char *shine_encode_buffer(shine_t s, int16_t **data, int *written); + +/* Encode interleaved audio data. Source data must have `shine_samples_per_pass(s)` audio samples per + * channels. Mono encoder only expect one channel. + * + * Returns a pointer to freshly encoded data while `written` contains the size of + * available data. This pointer's memory is handled by the library and is only valid + * until the next call to `shine_encode_buffer` or `shine_close` and may be NULL if no data + * was written. */ +unsigned char *shine_encode_buffer_interleaved(shine_t s, int16_t *data, int *written); + +/* Flush all data currently in the encoding buffer. Should be used before closing + * the encoder, to make all encoded data has been written. */ +unsigned char *shine_flush(shine_t s, int *written); + +/* Close an encoder, freeing all associated memory. Encoder handler is not + * valid after this call. */ +void shine_close(shine_t s); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h b/lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h new file mode 100755 index 000000000..092ed844e --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h @@ -0,0 +1,51 @@ +#include + +#define mul(a,b) \ +({ \ + register int32_t res; \ + __asm__ __volatile__("mult %0, %1" : : "r" (a), "r" (b)); \ + __asm__ __volatile__("mfhi %0" : "=r" (res)); \ + res; \ +}) + +#define mul0(hi,lo,a,b) \ + __asm__ __volatile__("mult %0, %1" : : "r" (a), "r" (b)) + +#define muladd(hi,lo,a,b) \ + __asm__ __volatile__("madd %0, %1" : : "r" (a), "r" (b)) + +#define mulsub(hi,lo,a,b) \ + __asm__ __volatile__("msub %0, %1" : : "r" (a), "r" (b)) + +#define mulz(hi,lo) \ +do { \ + register int32_t t; \ + __asm__ __volatile__("mfhi %0" : "=r" (t)); \ + (hi) = t; \ +} while (0) + +#define cmuls(dre, dim, are, aim, bre, bim) \ +do { \ + register int32_t t1, t2, tre; \ + __asm__ __volatile__("mult %0, %1" : : "r" (are), "r" (bre)); \ + __asm__ __volatile__("msub %0, %1" : : "r" (aim), "r" (bim)); \ + __asm__ __volatile__("mfhi %0; mflo %1" : "=r" (t1), "=r" (t2)); \ + tre = (t1 << 1) | ((uint32_t)t2 >> 31); \ + __asm__ __volatile__("mult %0, %1" : : "r" (are), "r" (bim)); \ + __asm__ __volatile__("madd %0, %1" : : "r" (bre), "r" (aim)); \ + dre = tre; \ + __asm__ __volatile__("mfhi %0; mflo %1" : "=r" (t1), "=r" (t2)); \ + dim = (t1 << 1) | ((uint32_t)t2 >> 31); \ +} while (0) + +#if __mips_isa_rev >= 2 +static inline uint32_t SWAB32(uint32_t x) +{ + __asm__( + " wsbh %0, %1 \n" + " rotr %0, %0, 16 \n" + : "=r" (x) : "r" (x)); + return x; +} +#define SWAB32 SWAB32 +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h b/lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h new file mode 100755 index 000000000..8a191ca97 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h @@ -0,0 +1,119 @@ +#include + +#ifndef mul +//#define /// mul(a,b) (int32_t) ( ( ((int64_t) a) * ((int64_t) b) ) >>32 ) + +#define mul(x,y) \ +({ \ + register int32_t result; \ + asm ("mulsh %0, %2, %1" : "=r" (result) : "r" (x), "r" (y)); \ + result ;\ +}) + +#endif + +#ifndef muls //Not sure about this +#define muls(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "mulsh %0, %2, %1\n\t" \ + "add %0, %0, %0" \ + : "=r" (result) : "r" (x), "r" (y)); \ + result ;\ +}) + + + +//#define muls(a,b) (int32_t) ( ( ((int64_t) a) * ((int64_t) b) ) >>31 ) +#endif + +#ifndef mulr //no rounding shortcut +#define mulr(x,y) \ +({ \ + register int32_t result; \ + asm ("mulsh %0, %2, %1" : "=r" (result) : "r" (x), "r" (y)); \ + result ;\ +}) + +//#define mulr(a,b) (int32_t) ( ( ( ((int64_t) a) * ((int64_t) b)) + 0x80000000LL ) >>32 ) +#endif + +#ifndef mulsr //no rounding shortcut +#define mulsr(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "mulsh %0, %2, %1\n\t" \ + "add %0, %0, %0" \ + : "=r" (result) : "r" (x), "r" (y)); \ + result ;\ +}) + +//#define mulsr(a,b) (int32_t) ( ( ( ((int64_t) a) * ((int64_t) b)) + 0x40000000LL ) >>31 ) +#endif + +#ifndef mul0 +#define mul0(hi,lo,a,b) ((hi) = mul((a), (b))) + +// This didn't seem to help either +#define muladd(hi, lo, x, y) \ + ({ \ + asm ( \ + "mulsh a7, %2, %1\n\t" \ + "add %0, %0, a7\n\t" \ + : "+r" (hi) : "r" (x), "r" (y) \ + : "a7");\ +}) + + +//#define muladd(hi,lo,a,b) ((hi) += mul((a), (b))) + #define mulsub(hi, lo, x, y) \ + ({ \ + asm ( \ + "mulsh a8, %2, %1\n\t" \ + "sub %0, %0, a8\n\t" \ + : "+r" (hi) : "r" (x), "r" (y) \ + : "a8");\ +}) +//#define mulsub(hi,lo,a,b) ((hi) -= mul((a), (b))) +#define mulz(hi,lo) +#endif + +#ifndef cmuls +/* + #define cmuls(dre, dim, are, aim, bre, bim) \ +do { \ + register int32_t tre, tim; \ + asm ( \ + "mull %0, %2, %4\n\t" \ //mulsh + "mulsh r3, %2, %4\n\t" \ //mulsh + "mull r4, %3, %5\n\t" \ //mulsh + "mulsh r5, %3, %5\n\t" \ //mulsh + "add %0, %0, %0\n\t" \ shl + + "smull r3, %0, %2, %4\n\t" \ //mulsh + "smlal r3, %0, %3, %5\n\t" \ //mulsh + add + "movs r3, r3, lsl #1\n\t" \ //add r to r + "adc %0, %0, %0\n\t" \. //add with carry + "smull r3, %1, %2, %6\n\t" \ + "smlal r3, %1, %4, %3\n\t" \ + "movs r3, r3, lsl #1\n\t" \ + "adc %1, %1, %1\n\t" \ + : "=&r" (tre), "=&r" (tim) \ + : "r" (are), "r" (aim), "r" (bre), "r" (-(bim)), "r" (bim) \ + : "r3", "cc" \ + ); \ + dre = tre; \ + dim = tim; \ +} while (0)*/ + + +#define cmuls(dre, dim, are, aim, bre, bim) \ +do { \ + int32_t tre; \ + (tre) = (int32_t) (((int64_t) (are) * (int64_t) (bre) - (int64_t) (aim) * (int64_t) (bim)) >> 31); \ + (dim) = (int32_t) (((int64_t) (are) * (int64_t) (bim) + (int64_t) (aim) * (int64_t) (bre)) >> 31); \ + (dre) = tre; \ +} while (0) +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h b/lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h new file mode 100755 index 000000000..d229effd8 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h @@ -0,0 +1,109 @@ +#include + +/* Fractional multiply */ +#if __ARM_ARCH >= 6 +#define mul(x,y) \ +({ \ + register int32_t result; \ + asm ("smmul %0, %2, %1" : "=r" (result) : "r" (x), "r" (y)); \ + result ;\ +}) +#else +#define mul(x,y) \ +({ \ + register int32_t result; \ + asm ("smull r3, %0, %2, %1" : "=r" (result) : "r" (x), "r" (y) : "r3"); \ + result ; \ +}) +#endif + +/* Fractional multiply with single bit left shift. */ +#define muls(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "smull r3, %0, %2, %1\n\t" \ + "movs r3, r3, lsl #1\n\t" \ + "adc %0, %0, %0" \ + : "=r" (result) : "r" (x), "r" (y) : "r3", "cc" \ + ); \ + result; \ +}) + + +#if __ARM_ARCH >= 6 +#define mulr(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "smmulr %0, %2, %1" : "=r" (result) : "r" (x), "r" (y) \ + ); \ + result; \ +}) +#else +#define mulr(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "smull r3, %0, %2, %1\n\t" \ + "adds r3, r3, #0x80000000\n\t" \ + "adc %0, %0, #0" \ + : "=r" (result) : "r" (x), "r" (y) : "r3", "cc" \ + ); \ + result; \ +}) +#endif + +#define mulsr(x,y) \ +({ \ + register int32_t result; \ + asm ( \ + "smull r3, %0, %1, %2\n\t" \ + "movs r3, r3, lsl #1\n\t" \ + "adc %0, %0, %0\n\t" \ + "adds r3, r3, #0x80000000\n\t" \ + "adc %0, %0, #0" \ + : "=r" (result) : "r" (x), "r" (y) : "r3", "cc" \ + ); \ + result; \ +}) + +#define mul0(hi,lo,a,b) \ + asm ("smull %0, %1, %2, %3" : "=r" (lo), "=r" (hi) : "r" (a), "r" (b)) + +#define muladd(hi,lo,a,b) \ + asm ("smlal %0, %1, %2, %3" : "+r" (lo), "+r" (hi) : "r" (a), "r" (b)) + +#define mulsub(hi,lo,a,b) \ + asm ("smlal %0, %1, %2, %3" : "+r" (lo), "+r" (hi) : "r" (a), "r" (-(b))) + +#define mulz(hi,lo) + +#define cmuls(dre, dim, are, aim, bre, bim) \ +do { \ + register int32_t tre, tim; \ + asm ( \ + "smull r3, %0, %2, %4\n\t" \ + "smlal r3, %0, %3, %5\n\t" \ + "movs r3, r3, lsl #1\n\t" \ + "adc %0, %0, %0\n\t" \ + "smull r3, %1, %2, %6\n\t" \ + "smlal r3, %1, %4, %3\n\t" \ + "movs r3, r3, lsl #1\n\t" \ + "adc %1, %1, %1\n\t" \ + : "=&r" (tre), "=&r" (tim) \ + : "r" (are), "r" (aim), "r" (bre), "r" (-(bim)), "r" (bim) \ + : "r3", "cc" \ + ); \ + dre = tre; \ + dim = tim; \ +} while (0) + +#if __ARM_ARCH >= 6 +static inline uint32_t SWAB32(uint32_t x) +{ + asm ("rev %0, %1" : "=r" (x) : "r" (x)); + return x; +} +#define SWAB32 SWAB32 +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp b/lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp new file mode 100755 index 000000000..fa2a45c09 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp @@ -0,0 +1,132 @@ +/* reservoir.c + * Layer3 bit reservoir: Described in C.1.5.4.2.2 of the IS + */ + +#include "types.h" +#include "layer3.h" +#include "l3loop.h" +#include "huffman.h" +#include "bitstream.h" +#include "l3bitstream.h" +#include "reservoir.h" + +/* + * shine_max_reservoir_bits: + * ------------ + * Called at the beginning of each granule to get the max bit + * allowance for the current granule based on reservoir size + * and perceptual entropy. + */ +int shine_max_reservoir_bits (double *pe, shine_global_config *config ) { + int more_bits, max_bits, add_bits, over_bits; + int mean_bits = config->mean_bits; + + mean_bits /= config->wave.channels; + max_bits = mean_bits; + + if(max_bits>4095) + max_bits = 4095; + if(!config->ResvMax) + return max_bits; + + more_bits = *pe * 3.1 - mean_bits; + add_bits = 0; + if(more_bits>100) + { + int frac = (config->ResvSize * 6) / 10; + + if(fracResvSize - ((config->ResvMax <<3) / 10) - add_bits; + if (over_bits>0) + add_bits += over_bits; + + max_bits += add_bits; + if(max_bits>4095) + max_bits = 4095; + return max_bits; +} + +/* + * shine_ResvAdjust: + * ----------- + * Called after a granule's bit allocation. Readjusts the size of + * the reservoir to reflect the granule's usage. + */ +void shine_ResvAdjust(gr_info *gi, shine_global_config *config ) { + config->ResvSize += (config->mean_bits / config->wave.channels) - gi->part2_3_length; +} + +/* + * shine_ResvFrameEnd: + * ------------- + * Called after all granules in a frame have been allocated. Makes sure + * that the reservoir size is within limits, possibly by adding stuffing + * bits. Note that stuffing bits are added by increasing a granule's + * part2_3_length. The bitstream formatter will detect this and write the + * appropriate stuffing bits to the bitstream. + */ +void shine_ResvFrameEnd(shine_global_config *config ) { + gr_info *gi; + int gr, ch, ancillary_pad, stuffingBits; + int over_bits; + shine_side_info_t *l3_side = &config->side_info; + + ancillary_pad = 0; + + /* just in case mean_bits is odd, this is necessary... */ + if((config->wave.channels==2) && (config->mean_bits & 1)) + config->ResvSize += 1; + + over_bits = config->ResvSize - config->ResvMax; + if(over_bits<0) + over_bits = 0; + + config->ResvSize -= over_bits; + stuffingBits = over_bits + ancillary_pad; + + /* we must be byte aligned */ + if((over_bits = config->ResvSize % 8)) + { + stuffingBits += over_bits; + config->ResvSize -= over_bits; + } + + if(stuffingBits) + { + /* + * plan a: put all into the first granule + * This was preferred by someone designing a + * real-time decoder... + */ + gi = (gr_info *) &(l3_side->gr[0].ch[0]); + + if ( gi->part2_3_length + stuffingBits < 4095 ) + gi->part2_3_length += stuffingBits; + else + { + /* plan b: distribute throughout the granules */ + for (gr = 0; gr < config->mpeg.granules_per_frame; gr++ ) + for (ch = 0; ch < config->wave.channels; ch++ ) + { + int extraBits, bitsThisGr; + gr_info *gi = (gr_info *) &(l3_side->gr[gr].ch[ch]); + if (!stuffingBits) + break; + extraBits = 4095 - gi->part2_3_length; + bitsThisGr = extraBits < stuffingBits ? extraBits : stuffingBits; + gi->part2_3_length += bitsThisGr; + stuffingBits -= bitsThisGr; + } + /* + * If any stuffing bits remain, we elect to spill them + * into ancillary data. The bitstream formatter will do this if + * l3side->resvDrain is set + */ + l3_side->resvDrain = stuffingBits; + } + } +} diff --git a/lib/lib_audio/mp3_shine_esp32/src/reservoir.h b/lib/lib_audio/mp3_shine_esp32/src/reservoir.h new file mode 100755 index 000000000..2f1ffa5e2 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/reservoir.h @@ -0,0 +1,9 @@ +#ifndef RESERVOIR_H +#define RESERVOIR_H + +void shine_ResvFrameBegin(int frameLength, shine_global_config *config); +int shine_max_reservoir_bits (double *pe, shine_global_config *config); +void shine_ResvAdjust (gr_info *gi, shine_global_config *config ); +void shine_ResvFrameEnd (shine_global_config *config ); + +#endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/tables.cpp b/lib/lib_audio/mp3_shine_esp32/src/tables.cpp new file mode 100755 index 000000000..ff7e37492 --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/tables.cpp @@ -0,0 +1,126 @@ +/* tables.c + * + * Here are MPEG1 Table B.8 and MPEG2 Table B.1 -- Layer III scalefactor bands. + * Index into this using a method such as: + * idx = fr_ps->header->sampling_frequency + (fr_ps->header->version * 3) + */ + +#include "tables.h" + +const int shine_slen1_tab[16] = { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 }; +const int shine_slen2_tab[16] = { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 }; + +/* Valid samplerates and bitrates. */ +const int samplerates[9] = { + 44100, 48000, 32000, /* MPEG-I */ + 22050, 24000, 16000, /* MPEG-II */ + 11025, 12000, 8000 /* MPEG-2.5 */ +}; + +const int bitrates[16][4] = { + /* MPEG version: + * 2.5, reserved, II, I */ + { -1, -1, -1, -1}, + { 8, -1, 8, 32}, + { 16, -1, 16, 40}, + { 24, -1, 24, 48}, + { 32, -1, 32, 56}, + { 40, -1, 40, 64}, + { 48, -1, 48, 80}, + { 56, -1, 56, 96}, + { 64, -1, 64, 112}, + { -1, -1, 80, 128}, + { -1, -1, 96, 160}, + { -1, -1, 112, 192}, + { -1, -1, 128, 224}, + { -1, -1, 144, 256}, + { -1, -1, 160, 320}, + { -1, -1, -1, -1} +}; + +const int shine_scale_fact_band_index[9][23] = +{ + /* MPEG-I */ + /* Table B.8.b: 44.1 kHz */ + {0,4,8,12,16,20,24,30,36,44,52,62,74,90,110,134,162,196,238,288,342,418,576}, + /* Table B.8.c: 48 kHz */ + {0,4,8,12,16,20,24,30,36,42,50,60,72,88,106,128,156,190,230,276,330,384,576}, + /* Table B.8.a: 32 kHz */ + {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576}, + /* MPEG-II */ + /* Table B.2.b: 22.05 kHz */ + {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, + /* Table B.2.c: 24 kHz */ + {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576}, + /* Table B.2.a: 16 kHz */ + {0,6,12,18,24,30,36,44,45,66,80,96,116,140,168,200,238,248,336,396,464,522,576}, + + /* MPEG-2.5 */ + /* 11.025 kHz */ + {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, + /* 12 kHz */ + {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, + /* MPEG-2.5 8 kHz */ + {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576}, +}; + +/* note. 0.035781 is shine_enwindow maximum value */ +/* scale and convert to fixed point before storing */ +#define SHINE_EW(x) (int32_t)((double)(x) * 0x7fffffff) +#define SHINE_EW2(a,b) SHINE_EW(a), SHINE_EW(b) +#define SHINE_EW10(a,b,c,d,e,f,g,h,i,j) SHINE_EW2(a,b), SHINE_EW2(c,d), SHINE_EW2(e,f), SHINE_EW2(g,h), SHINE_EW2(i,j) + +const int32_t shine_enwindow[] = { +SHINE_EW10( 0.000000, -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, -0.000000, -0.000001, -0.000001, -0.000001), +SHINE_EW10( -0.000001, -0.000001, -0.000001, -0.000002, -0.000002, -0.000002, -0.000002, -0.000003, -0.000003, -0.000003), +SHINE_EW10( -0.000004, -0.000004, -0.000005, -0.000005, -0.000006, -0.000007, -0.000008, -0.000008, -0.000009, -0.000010), +SHINE_EW10( -0.000011, -0.000012, -0.000014, -0.000015, -0.000017, -0.000018, -0.000020, -0.000021, -0.000023, -0.000025), +SHINE_EW10( -0.000028, -0.000030, -0.000032, -0.000035, -0.000038, -0.000041, -0.000043, -0.000046, -0.000050, -0.000053), +SHINE_EW10( -0.000056, -0.000060, -0.000063, -0.000066, -0.000070, -0.000073, -0.000077, -0.000081, -0.000084, -0.000087), +SHINE_EW10( -0.000091, -0.000093, -0.000096, -0.000099, 0.000102, 0.000104, 0.000106, 0.000107, 0.000108, 0.000109), +SHINE_EW10( 0.000109, 0.000108, 0.000107, 0.000105, 0.000103, 0.000099, 0.000095, 0.000090, 0.000084, 0.000078), +SHINE_EW10( 0.000070, 0.000061, 0.000051, 0.000040, 0.000027, 0.000014, -0.000001, -0.000017, -0.000034, -0.000053), +SHINE_EW10( -0.000073, -0.000094, -0.000116, -0.000140, -0.000165, -0.000191, -0.000219, -0.000247, -0.000277, -0.000308), +SHINE_EW10( -0.000339, -0.000371, -0.000404, -0.000438, -0.000473, -0.000507, -0.000542, -0.000577, -0.000612, -0.000647), +SHINE_EW10( -0.000681, -0.000714, -0.000747, -0.000779, -0.000810, -0.000839, -0.000866, -0.000892, -0.000915, -0.000936), +SHINE_EW10( -0.000954, -0.000969, -0.000981, -0.000989, -0.000994, -0.000995, -0.000992, -0.000984, 0.000971, 0.000954), +SHINE_EW10( 0.000931, 0.000903, 0.000869, 0.000829, 0.000784, 0.000732, 0.000674, 0.000610, 0.000539, 0.000463), +SHINE_EW10( 0.000379, 0.000288, 0.000192, 0.000088, -0.000021, -0.000137, -0.000260, -0.000388, -0.000522, -0.000662), +SHINE_EW10( -0.000807, -0.000957, -0.001111, -0.001270, -0.001432, -0.001598, -0.001767, -0.001937, -0.002110, -0.002283), +SHINE_EW10( -0.002457, -0.002631, -0.002803, -0.002974, -0.003142, -0.003307, -0.003467, -0.003623, -0.003772, -0.003914), +SHINE_EW10( -0.004049, -0.004175, -0.004291, -0.004396, -0.004490, -0.004570, -0.004638, -0.004691, -0.004728, -0.004749), +SHINE_EW10( -0.004752, -0.004737, -0.004703, -0.004649, -0.004574, -0.004477, -0.004358, -0.004215, -0.004049, -0.003859), +SHINE_EW10( -0.003643, -0.003402, 0.003135, 0.002841, 0.002522, 0.002175, 0.001801, 0.001400, 0.000971, 0.000516), +SHINE_EW10( 0.000033, -0.000476, -0.001012, -0.001574, -0.002162, -0.002774, -0.003411, -0.004072, -0.004756, -0.005462), +SHINE_EW10( -0.006189, -0.006937, -0.007703, -0.008487, -0.009288, -0.010104, -0.010933, -0.011775, -0.012628, -0.013489), +SHINE_EW10( -0.014359, -0.015234, -0.016113, -0.016994, -0.017876, -0.018757, -0.019634, -0.020507, -0.021372, -0.022229), +SHINE_EW10( -0.023074, -0.023907, -0.024725, -0.025527, -0.026311, -0.027074, -0.027815, -0.028533, -0.029225, -0.029890), +SHINE_EW10( -0.030527, -0.031133, -0.031707, -0.032248, -0.032755, -0.033226, -0.033660, -0.034056, -0.034413, -0.034730), +SHINE_EW10( -0.035007, -0.035242, -0.035435, -0.035586, -0.035694, -0.035759, 0.035781, 0.035759, 0.035694, 0.035586), +SHINE_EW10( 0.035435, 0.035242, 0.035007, 0.034730, 0.034413, 0.034056, 0.033660, 0.033226, 0.032755, 0.032248), +SHINE_EW10( 0.031707, 0.031133, 0.030527, 0.029890, 0.029225, 0.028533, 0.027815, 0.027074, 0.026311, 0.025527), +SHINE_EW10( 0.024725, 0.023907, 0.023074, 0.022229, 0.021372, 0.020507, 0.019634, 0.018757, 0.017876, 0.016994), +SHINE_EW10( 0.016113, 0.015234, 0.014359, 0.013489, 0.012628, 0.011775, 0.010933, 0.010104, 0.009288, 0.008487), +SHINE_EW10( 0.007703, 0.006937, 0.006189, 0.005462, 0.004756, 0.004072, 0.003411, 0.002774, 0.002162, 0.001574), +SHINE_EW10( 0.001012, 0.000476, -0.000033, -0.000516, -0.000971, -0.001400, -0.001801, -0.002175, -0.002522, -0.002841), +SHINE_EW10( 0.003135, 0.003402, 0.003643, 0.003859, 0.004049, 0.004215, 0.004358, 0.004477, 0.004574, 0.004649), +SHINE_EW10( 0.004703, 0.004737, 0.004752, 0.004749, 0.004728, 0.004691, 0.004638, 0.004570, 0.004490, 0.004396), +SHINE_EW10( 0.004291, 0.004175, 0.004049, 0.003914, 0.003772, 0.003623, 0.003467, 0.003307, 0.003142, 0.002974), +SHINE_EW10( 0.002803, 0.002631, 0.002457, 0.002283, 0.002110, 0.001937, 0.001767, 0.001598, 0.001432, 0.001270), +SHINE_EW10( 0.001111, 0.000957, 0.000807, 0.000662, 0.000522, 0.000388, 0.000260, 0.000137, 0.000021, -0.000088), +SHINE_EW10( -0.000192, -0.000288, -0.000379, -0.000463, -0.000539, -0.000610, -0.000674, -0.000732, -0.000784, -0.000829), +SHINE_EW10( -0.000869, -0.000903, -0.000931, -0.000954, 0.000971, 0.000984, 0.000992, 0.000995, 0.000994, 0.000989), +SHINE_EW10( 0.000981, 0.000969, 0.000954, 0.000936, 0.000915, 0.000892, 0.000866, 0.000839, 0.000810, 0.000779), +SHINE_EW10( 0.000747, 0.000714, 0.000681, 0.000647, 0.000612, 0.000577, 0.000542, 0.000507, 0.000473, 0.000438), +SHINE_EW10( 0.000404, 0.000371, 0.000339, 0.000308, 0.000277, 0.000247, 0.000219, 0.000191, 0.000165, 0.000140), +SHINE_EW10( 0.000116, 0.000094, 0.000073, 0.000053, 0.000034, 0.000017, 0.000001, -0.000014, -0.000027, -0.000040), +SHINE_EW10( -0.000051, -0.000061, -0.000070, -0.000078, -0.000084, -0.000090, -0.000095, -0.000099, -0.000103, -0.000105), +SHINE_EW10( -0.000107, -0.000108, -0.000109, -0.000109, -0.000108, -0.000107, -0.000106, -0.000104, 0.000102, 0.000099), +SHINE_EW10( 0.000096, 0.000093, 0.000091, 0.000087, 0.000084, 0.000081, 0.000077, 0.000073, 0.000070, 0.000066), +SHINE_EW10( 0.000063, 0.000060, 0.000056, 0.000053, 0.000050, 0.000046, 0.000043, 0.000041, 0.000038, 0.000035), +SHINE_EW10( 0.000032, 0.000030, 0.000028, 0.000025, 0.000023, 0.000021, 0.000020, 0.000018, 0.000017, 0.000015), +SHINE_EW10( 0.000014, 0.000012, 0.000011, 0.000010, 0.000009, 0.000008, 0.000008, 0.000007, 0.000006, 0.000005), +SHINE_EW10( 0.000005, 0.000004, 0.000004, 0.000003, 0.000003, 0.000003, 0.000002, 0.000002, 0.000002, 0.000002), +SHINE_EW10( 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000001, 0.000000, 0.000000, 0.000000, 0.000000), +SHINE_EW2 ( 0.000000, 0.000000) +}; diff --git a/lib/lib_audio/mp3_shine_esp32/src/tables.h b/lib/lib_audio/mp3_shine_esp32/src/tables.h new file mode 100755 index 000000000..5c94228cb --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/tables.h @@ -0,0 +1,16 @@ +#ifndef TABLES_H +#define TABLES_H + +#include "types.h" + +extern const int shine_slen1_tab[16]; +extern const int shine_slen2_tab[16]; + +extern const int samplerates[9]; +extern const int bitrates[16][4]; + +extern const int shine_scale_fact_band_index[9][23]; +extern const int32_t shine_enwindow[]; + +#endif + diff --git a/lib/lib_audio/mp3_shine_esp32/src/types.h b/lib/lib_audio/mp3_shine_esp32/src/types.h new file mode 100755 index 000000000..69827481c --- /dev/null +++ b/lib/lib_audio/mp3_shine_esp32/src/types.h @@ -0,0 +1,179 @@ +#ifndef PRIV_TYPES_H +#define PRIV_TYPES_H + +#include +#include +#include + +#include + +#define GRANULE_SIZE 576 + +#include "bitstream.h" + +/* Include arch-specific instructions, + * when defined. */ +#if defined(__mips__) && (__mips == 32) +#include "mult_mips_gcc.h" +#elif defined(__arm__) && !defined(__thumb__) +#include "mult_sarm_gcc.h" +#endif + +/* Include and define generic instructions, + * when not already defined above. */ +#include "mult_noarch_gcc.h" + +#ifndef SWAB32 +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +#define SWAB32(x) __builtin_bswap32(x) +#else +#define SWAB32(x) (((unsigned int)(x) >> 24) | (((unsigned int)(x) >> 8) & 0xff00) | (((unsigned int)(x) & 0xff00) << 8) | ((unsigned int)(x) << 24)) +#endif +#endif + +/* #define SHINE_DEBUG if you want the library to dump info to stdout */ + +#undef PI +#define PI 3.14159265358979 +#define PI4 0.78539816339745 +#define PI12 0.26179938779915 +#define PI36 0.087266462599717 +#define PI64 0.049087385212 +#define SQRT2 1.41421356237 +#define LN2 0.69314718 +#define LN_TO_LOG10 0.2302585093 +#define BLKSIZE 1024 +#define HAN_SIZE 512 /* for loop unrolling, require that HAN_SIZE%8==0 */ +#define SCALE_BLOCK 12 +#define SCALE_RANGE 64 +#define SCALE 32768 +#define SBLIMIT 32 + +#ifndef MAX_CHANNELS +#define MAX_CHANNELS 2 +#endif + +#ifndef MAX_GRANULES +#define MAX_GRANULES 2 +#endif + +typedef struct { + int16_t channels; + int samplerate; +} priv_shine_wave_t; + +typedef struct { + int version; + int layer; + int granules_per_frame; + int mode; /* + */ /* Stereo mode */ + int bitr; /* + */ /* Must conform to known bitrate - see Main.c */ + int emph; /* + */ /* De-emphasis */ + int padding; + int bits_per_frame; + int bits_per_slot; + double frac_slots_per_frame; + double slot_lag; + int whole_slots_per_frame; + int bitrate_index; /* + */ /* See Main.c and Layer3.c */ + int samplerate_index; /* + */ /* See Main.c and Layer3.c */ + int crc; + int ext; + int mode_ext; + int copyright; /* + */ + int original; /* + */ +} priv_shine_mpeg_t; + +typedef struct { + int32_t *xr; /* magnitudes of the spectral values */ + int32_t *xrsq; /* xr squared */ + int32_t *xrabs; /* xr absolute */ + int32_t xrmax; /* maximum of xrabs array */ + int32_t en_tot[MAX_GRANULES]; /* gr */ + int32_t en[MAX_GRANULES][21]; + int32_t xm[MAX_GRANULES][21]; + int32_t xrmaxl[MAX_GRANULES]; + double steptab[128]; /* 2**(-x/4) for x = -127..0 */ + int32_t steptabi[128]; /* 2**(-x/4) for x = -127..0 */ + int16_t int2idx[10000]; /* x**(3/4) for x = 0..9999 */ +} l3loop_t; + +typedef struct { + int32_t cos_l[18][36]; +} mdct_t; + +typedef struct { + int off[MAX_CHANNELS]; + int32_t fl[SBLIMIT][64]; + int32_t x[MAX_CHANNELS][HAN_SIZE]; +} subband_t; + +/* Side information */ +typedef struct { + unsigned part2_3_length; + unsigned big_values; + unsigned count1; + unsigned global_gain; + unsigned scalefac_compress; + unsigned table_select[3]; + unsigned region0_count; + unsigned region1_count; + unsigned preflag; + unsigned scalefac_scale; + unsigned count1table_select; + unsigned part2_length; + unsigned sfb_lmax; + unsigned address1; + unsigned address2; + unsigned address3; + int quantizerStepSize; + unsigned slen[4]; +} gr_info; + +typedef struct { + unsigned private_bits; + int resvDrain; + unsigned scfsi[MAX_CHANNELS][4]; + struct { + struct { + gr_info tt; + } ch[MAX_CHANNELS]; + } gr[MAX_GRANULES]; +} shine_side_info_t; + +typedef struct { + float l[MAX_GRANULES][MAX_CHANNELS][21]; +} shine_psy_ratio_t; + +typedef struct { + double l[MAX_GRANULES][MAX_CHANNELS][21]; +} shine_psy_xmin_t; + +typedef struct { + int32_t l[MAX_GRANULES][MAX_CHANNELS][22]; /* [cb] */ + int32_t s[MAX_GRANULES][MAX_CHANNELS][13][3]; /* [window][cb] */ +} shine_scalefac_t; + + +typedef struct shine_global_flags { + priv_shine_wave_t wave; + priv_shine_mpeg_t mpeg; + bitstream_t bs; + shine_side_info_t side_info; + int sideinfo_len; + int mean_bits; + //shine_psy_ratio_t ratio; + shine_scalefac_t scalefactor; + int16_t *buffer[MAX_CHANNELS]; + double pe[MAX_CHANNELS][MAX_GRANULES]; + int *l3_enc[MAX_CHANNELS][MAX_GRANULES]; //4% reduction in performance IRAM + int32_t l3_sb_sample[MAX_CHANNELS][MAX_GRANULES+1][18][SBLIMIT]; + int32_t *mdct_freq[MAX_CHANNELS][MAX_GRANULES]; //1% reduction in perormance IRAM + int ResvSize; + int ResvMax; + l3loop_t *l3loop; + mdct_t mdct; + subband_t subband; +} shine_global_config; + +#endif From b8902c3af79d9f7d0b948bb0f569b3865492a3b8 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Wed, 20 Jul 2022 20:49:25 +0200 Subject: [PATCH 128/219] i2srec to mp3 --- .../xdrv_42_1_i2s_audio.ino | 1128 +++++++++++++++-- .../xdrv_42_2_i2s_audio.ino | 240 ++++ .../tasmota_xdrv_driver/xdrv_42_i2s_audio.ino | 884 ------------- 3 files changed, 1270 insertions(+), 982 deletions(-) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino delete mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino index b772fbc4d..853b0d90e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino @@ -1,117 +1,1049 @@ +/* + xdrv_42_i2s_audio.ino - Audio dac support for Tasmota + + Copyright (C) 2021 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) +/*********************************************************************************************\ + * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor + * + * Uses fixed GPIOs for ESP8266: + * I2S Out Data GPIO03 (Rx) + * I2S Out Bit Clock GPIO15 + * I2S Out Word Select GPIO02 + * I2S In Data GPIO12 + * I2S In Bit Clock GPIO13 + * I2S In Word Select GPIO14 +\*********************************************************************************************/ + +#define XDRV_42 42 + +#define USE_I2S_EXTERNAL_DAC 1 +//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC +//#define USE_I2S_WEBRADIO // Add support for web radio +//#define USE_I2S_SAY_TIME // Add support for english speaking clock + +#include "AudioFileSourcePROGMEM.h" +#include "AudioFileSourceID3.h" +#include "AudioGeneratorMP3.h" +#ifdef USE_I2S_NO_DAC + #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin +#else + #include "AudioOutputI2S.h" // External I2S DAC IC +#endif // USE_I2S_NO_DAC +#include +#include "AudioFileSourceFS.h" +#ifdef USE_I2S_SAY_TIME + #include "AudioGeneratorTalkie.h" +#endif // USE_I2S_SAY_TIME +#include "AudioFileSourceICYStream.h" +#include "AudioFileSourceBuffer.h" +#include "AudioGeneratorAAC.h" #ifdef ESP32 -#ifdef ESP32S3_BOX #include -#include -#include -#include -#include +#endif -void S3boxAudioPower(uint8_t pwr) { - pinMode(46 , OUTPUT); - digitalWrite(46, pwr); -} +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON +#define AUDIO_PWR_OFF -// box lite dac init -uint32_t ES8156_init() { - uint32_t ret_val = ESP_OK; +#ifdef ESP8266 +#define i2s_port_t uint8_t +#endif - if (I2cSetDevice(ES8156_ADDR, 1)) { - I2cSetActiveFound(ES8156_ADDR, "ES8156-I2C", 1); - audio_hal_codec_config_t cfg = { - .i2s_iface = { - .mode = AUDIO_HAL_MODE_SLAVE, - .bits = AUDIO_HAL_BIT_LENGTH_16BITS, - } - }; - ret_val |= es8156_codec_init(&Wire1, &cfg); - ret_val |= es8156_codec_set_voice_volume(75); - } - return ret_val; -} +#ifdef ESP32 +#define MODE_MIC 0 +#define MODE_SPK 1 +#ifndef MICSRATE +#define MICSRATE 16000 +#endif +#endif // ESP32 -// box lite adc init -uint32_t es7243e_init() { - uint32_t ret_val = ESP_OK; +struct AUDIO_I2S_t { + uint8_t is2_volume; // should be in settings + i2s_port_t i2s_port; + int8_t mclk = -1; + int8_t bclk = -1; + int8_t ws = -1; + int8_t dout = -1; + int8_t din = -1; + AudioGeneratorMP3 *mp3 = nullptr; + AudioFileSourceFS *file; +#ifdef USE_I2S_NO_DAC + AudioOutputI2SNoDAC *out; +#else + AudioOutputI2S *out; +#endif // USE_I2S_NO_DAC + AudioFileSourceID3 *id3; + AudioGeneratorMP3 *decoder = NULL; + void *mp3ram = NULL; +#ifdef USE_I2S_WEBRADIO + AudioFileSourceICYStream *ifile = NULL; + AudioFileSourceBuffer *buff = NULL; + char wr_title[64]; + void *preallocateBuffer = NULL; + void *preallocateCodec = NULL; + uint32_t retryms = 0; +#endif // USE_I2S_WEBRADIO - if (I2cSetDevice(ES7243_ADDR, 1)) { - I2cSetActiveFound(ES7243_ADDR, "ES7243e-I2C", 1); +#ifdef ESP32 + TaskHandle_t mp3_task_h; + TaskHandle_t mic_task_h; + uint32_t mic_size; + uint32_t mic_rate; + uint8_t *mic_buff; + char mic_path[32]; + uint8_t mic_channels; + File fwp; + uint8_t mic_stop; + int8_t mic_error; +#endif // ESP32 +} audio_i2s; - audio_hal_codec_config_t cfg = { - .i2s_iface = { - .mode = AUDIO_HAL_MODE_SLAVE, - .bits = AUDIO_HAL_BIT_LENGTH_16BITS, - } - }; - ret_val |= es7243e_adc_init(&Wire1, &cfg); +// because S3 box mclk severly disturbs WLAN +// we must slow down after each sound +#ifdef ESP32S3_BOX +#undef DOWNRATE +#define DOWNRATE audio_i2s.out->SetRate(1000); +#else +#undef DOWNRATE +#define DOWNRATE +#endif + +#define MIC_CHANNELS 1 + +#ifdef USE_TTGO_WATCH +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON TTGO_audio_power(true); +#define AUDIO_PWR_OFF TTGO_audio_power(false); +#endif // USE_TTGO_WATCH + +#ifdef USE_M5STACK_CORE2 +// leave this predefined currently +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON Core2AudioPower(true); +#define AUDIO_PWR_OFF Core2AudioPower(false); +#undef DAC_IIS_BCK +#undef DAC_IIS_WS +#undef DAC_IIS_DOUT +#define DAC_IIS_BCK 12 +#define DAC_IIS_WS 0 +#define DAC_IIS_DOUT 2 +#endif // USE_M5STACK_CORE2 + + +#ifdef ESP32S3_BOX +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON S3boxAudioPower(true); +#define AUDIO_PWR_OFF S3boxAudioPower(false); +#undef MIC_CHANNELS +#define MIC_CHANNELS 2 +#endif // ESP32S3_BOX + +extern FS *ufsp; + +#ifdef ESP8266 +const int preallocateBufferSize = 5*1024; +const int preallocateCodecSize = 29192; // MP3 codec max mem needed +#endif // ESP8266 + +#ifdef ESP32 +const int preallocateBufferSize = 16*1024; +const int preallocateCodecSize = 29192; // MP3 codec max mem needed +//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed +#endif // ESP32 + + +#ifdef USE_I2S_SAY_TIME +long timezone = 2; +byte daysavetime = 1; + +uint8_t spTHE[] PROGMEM = {0x08,0xE8,0x3E,0x55,0x01,0xC3,0x86,0x27,0xAF,0x72,0x0D,0x4D,0x97,0xD5,0xBC,0x64,0x3C,0xF2,0x5C,0x51,0xF1,0x93,0x36,0x8F,0x4F,0x59,0x2A,0x42,0x7A,0x32,0xC3,0x64,0xFF,0x3F}; +uint8_t spTIME[] PROGMEM = {0x0E,0x28,0xAC,0x2D,0x01,0x5D,0xB6,0x0D,0x33,0xF3,0x54,0xB3,0x60,0xBA,0x8C,0x54,0x5C,0xCD,0x2D,0xD4,0x32,0x73,0x0F,0x8E,0x34,0x33,0xCB,0x4A,0x25,0xD4,0x25,0x83,0x2C,0x2B,0xD5,0x50,0x97,0x08,0x32,0xEC,0xD4,0xDC,0x4C,0x33,0xC8,0x70,0x73,0x0F,0x33,0xCD,0x20,0xC3,0xCB,0x43,0xDD,0x3C,0xCD,0x8C,0x20,0x77,0x89,0xF4,0x94,0xB2,0xE2,0xE2,0x35,0x22,0x5D,0xD6,0x4A,0x8A,0x96,0xCC,0x36,0x25,0x2D,0xC9,0x9A,0x7B,0xC2,0x18,0x87,0x24,0x4B,0x1C,0xC9,0x50,0x19,0x92,0x2C,0x71,0x34,0x4B,0x45,0x8A,0x8B,0xC4,0x96,0xB6,0x5A,0x29,0x2A,0x92,0x5A,0xCA,0x53,0x96,0x20,0x05,0x09,0xF5,0x92,0x5D,0xBC,0xE8,0x58,0x4A,0xDD,0xAE,0x73,0xBD,0x65,0x4B,0x8D,0x78,0xCA,0x2B,0x4E,0xD8,0xD9,0xED,0x22,0x20,0x06,0x75,0x00,0x00,0x80,0xFF,0x07}; +uint8_t spIS[] PROGMEM = {0x21,0x18,0x96,0x38,0xB7,0x14,0x8D,0x60,0x3A,0xA6,0xE8,0x51,0xB4,0xDC,0x2E,0x48,0x7B,0x5A,0xF1,0x70,0x1B,0xA3,0xEC,0x09,0xC6,0xCB,0xEB,0x92,0x3D,0xA7,0x69,0x1F,0xAF,0x71,0x89,0x9C,0xA2,0xB3,0xFC,0xCA,0x35,0x72,0x9A,0xD1,0xF0,0xAB,0x12,0xB3,0x2B,0xC6,0xCD,0x4F,0xCC,0x32,0x26,0x19,0x07,0xDF,0x0B,0x8F,0xB8,0xA4,0xED,0x7C,0xCF,0x23,0x62,0x8B,0x8E,0xF1,0x23,0x0A,0x8B,0x6E,0xCB,0xCE,0xEF,0x54,0x44,0x3C,0xDC,0x08,0x60,0x0B,0x37,0x01,0x1C,0x53,0x26,0x80,0x15,0x4E,0x14,0xB0,0x54,0x2B,0x02,0xA4,0x69,0xFF,0x7F}; +uint8_t spA_M_[] PROGMEM = {0xCD,0xEF,0x86,0xAB,0x57,0x6D,0x0F,0xAF,0x71,0xAD,0x49,0x55,0x3C,0xFC,0x2E,0xC5,0xB7,0x5C,0xF1,0xF2,0x87,0x66,0xDD,0x4E,0xC5,0xC3,0xEF,0x92,0xE2,0x3A,0x65,0xB7,0xA0,0x09,0xAA,0x1B,0x97,0x54,0x82,0x2E,0x28,0x77,0x5C,0x52,0x09,0x1A,0xA3,0xB8,0x76,0x49,0x25,0x68,0x8C,0x73,0xDB,0x24,0x95,0xA0,0x32,0xA9,0x6B,0xA7,0xD9,0x82,0x26,0xA9,0x76,0x42,0xD6,0x08,0xBA,0xE1,0xE8,0x0E,0x5A,0x2B,0xEA,0x9E,0x3D,0x27,0x18,0xAD,0xA8,0x07,0xF1,0x98,0x90,0x35,0xA2,0x96,0x44,0xA3,0x5D,0x66,0x8B,0x6B,0x12,0xCD,0x32,0x85,0x25,0xC9,0x81,0x2D,0xC3,0x64,0x85,0x34,0x58,0x89,0x94,0x52,0x1C,0x52,0x2F,0x35,0xDA,0xC7,0x51,0x48,0x23,0x97,0xCC,0x2C,0x97,0x2E,0xF3,0x5C,0xF3,0xA2,0x14,0xBA,0x2C,0x48,0xCE,0xCA,0x76,0xE8,0x32,0x2F,0x34,0xB2,0xDB,0x85,0xC9,0x83,0x90,0xA8,0x2C,0x57,0x26,0x8F,0x9C,0xBD,0xA2,0x53,0xD9,0xC2,0x54,0x59,0x28,0x99,0x4B,0x2C,0x5D,0xFF,0x3F}; +uint8_t spP_M_[] PROGMEM = {0x0E,0x98,0x41,0x54,0x00,0x43,0xA0,0x05,0xAB,0x42,0x8E,0x1D,0xA3,0x15,0xEC,0x4E,0x58,0xF7,0x92,0x66,0x70,0x1B,0x66,0xDB,0x73,0x99,0xC1,0xEB,0x98,0xED,0xD6,0x25,0x25,0x6F,0x70,0x92,0xDD,0x64,0xD8,0xFC,0x61,0xD0,0x66,0x83,0xD6,0x0A,0x86,0x23,0xAB,0x69,0xDA,0x2B,0x18,0x9E,0x3D,0x37,0x69,0x9D,0xA8,0x07,0x71,0x9F,0xA0,0xBD,0xA2,0x16,0xD5,0x7C,0x54,0xF6,0x88,0x6B,0x54,0x8B,0x34,0x49,0x2D,0x29,0x49,0x3C,0x34,0x64,0xA5,0x24,0x1B,0x36,0xD7,0x72,0x13,0x92,0xA4,0xC4,0x2D,0xC3,0xB3,0x4B,0xA3,0x62,0x0F,0x2B,0x37,0x6E,0x8B,0x5A,0xD4,0x3D,0xDD,0x9A,0x2D,0x50,0x93,0xF6,0x4C,0xAA,0xB6,0xC4,0x85,0x3B,0xB2,0xB1,0xD8,0x93,0x20,0x4D,0x8F,0x24,0xFF,0x0F}; +uint8_t spOH[] PROGMEM = {0xC6,0xC9,0x71,0x5A,0xA2,0x92,0x14,0x2F,0x6E,0x97,0x9C,0x46,0x9D,0xDC,0xB0,0x4D,0x62,0x1B,0x55,0x70,0xDD,0x55,0xBE,0x0E,0x36,0xC1,0x33,0x37,0xA9,0xA7,0x51,0x1B,0xCF,0x3C,0xA5,0x9E,0x44,0xAC,0x3C,0x7D,0x98,0x7B,0x52,0x96,0x72,0x65,0x4B,0xF6,0x1A,0xD9,0xCA,0xF5,0x91,0x2D,0xA2,0x2A,0x4B,0xF7,0xFF,0x01}; +uint8_t spOCLOCK[] PROGMEM = {0x21,0x4E,0x3D,0xB8,0x2B,0x19,0xBB,0x24,0x0E,0xE5,0xEC,0x60,0xE4,0xF2,0x90,0x13,0xD4,0x2A,0x11,0x80,0x00,0x42,0x69,0x26,0x40,0xD0,0x2B,0x04,0x68,0xE0,0x4D,0x00,0x3A,0x35,0x35,0x33,0xB6,0x51,0xD9,0x64,0x34,0x82,0xB4,0x9A,0x63,0x92,0x55,0x89,0x52,0x5B,0xCA,0x2E,0x34,0x25,0x4E,0x63,0x28,0x3A,0x50,0x95,0x26,0x8D,0xE6,0xAA,0x64,0x58,0xEA,0x92,0xCE,0xC2,0x46,0x15,0x9B,0x86,0xCD,0x2A,0x2E,0x37,0x00,0x00,0x00,0x0C,0xC8,0xDD,0x05,0x01,0xB9,0x33,0x21,0xA0,0x74,0xD7,0xFF,0x07}; +uint8_t spONE[] PROGMEM = {0xCC,0x67,0x75,0x42,0x59,0x5D,0x3A,0x4F,0x9D,0x36,0x63,0xB7,0x59,0xDC,0x30,0x5B,0x5C,0x23,0x61,0xF3,0xE2,0x1C,0xF1,0xF0,0x98,0xC3,0x4B,0x7D,0x39,0xCA,0x1D,0x2C,0x2F,0xB7,0x15,0xEF,0x70,0x79,0xBC,0xD2,0x46,0x7C,0x52,0xE5,0xF1,0x4A,0x6A,0xB3,0x71,0x47,0xC3,0x2D,0x39,0x34,0x4B,0x23,0x35,0xB7,0x7A,0x55,0x33,0x8F,0x59,0xDC,0xA2,0x44,0xB5,0xBC,0x66,0x72,0x8B,0x64,0xF5,0xF6,0x98,0xC1,0x4D,0x42,0xD4,0x27,0x62,0x38,0x2F,0x4A,0xB6,0x9C,0x88,0x68,0xBC,0xA6,0x95,0xF8,0x5C,0xA1,0x09,0x86,0x77,0x91,0x11,0x5B,0xFF,0x0F}; +uint8_t spTWO[] PROGMEM = {0x0E,0x38,0x6E,0x25,0x00,0xA3,0x0D,0x3A,0xA0,0x37,0xC5,0xA0,0x05,0x9E,0x56,0x35,0x86,0xAA,0x5E,0x8C,0xA4,0x82,0xB2,0xD7,0x74,0x31,0x22,0x69,0xAD,0x1C,0xD3,0xC1,0xD0,0xFA,0x28,0x2B,0x2D,0x47,0xC3,0x1B,0xC2,0xC4,0xAE,0xC6,0xCD,0x9C,0x48,0x53,0x9A,0xFF,0x0F}; +uint8_t spTHREE[] PROGMEM = {0x02,0xD8,0x2E,0x9C,0x01,0xDB,0xA6,0x33,0x60,0xFB,0x30,0x01,0xEC,0x20,0x12,0x8C,0xE4,0xD8,0xCA,0x32,0x96,0x73,0x63,0x41,0x39,0x89,0x98,0xC1,0x4D,0x0D,0xED,0xB0,0x2A,0x05,0x37,0x0F,0xB4,0xA5,0xAE,0x5C,0xDC,0x36,0xD0,0x83,0x2F,0x4A,0x71,0x7B,0x03,0xF7,0x38,0x59,0xCD,0xED,0x1E,0xB4,0x6B,0x14,0x35,0xB7,0x6B,0x94,0x99,0x91,0xD5,0xDC,0x26,0x48,0x77,0x4B,0x66,0x71,0x1B,0x21,0xDB,0x2D,0x8A,0xC9,0x6D,0x88,0xFC,0x26,0x28,0x3A,0xB7,0x21,0xF4,0x1F,0xA3,0x65,0xBC,0x02,0x38,0xBB,0x3D,0x8E,0xF0,0x2B,0xE2,0x08,0xB7,0x34,0xFF,0x0F}; +uint8_t spFOUR[] PROGMEM = {0x0C,0x18,0xB6,0x9A,0x01,0xC3,0x75,0x09,0x60,0xD8,0x0E,0x09,0x30,0xA0,0x9B,0xB6,0xA0,0xBB,0xB0,0xAA,0x16,0x4E,0x82,0xEB,0xEA,0xA9,0xFA,0x59,0x49,0x9E,0x59,0x23,0x9A,0x27,0x3B,0x78,0x66,0xAE,0x4A,0x9C,0x9C,0xE0,0x99,0xD3,0x2A,0xBD,0x72,0x92,0xEF,0xE6,0x88,0xE4,0x45,0x4D,0x7E,0x98,0x2D,0x62,0x67,0x37,0xF9,0xA1,0x37,0xA7,0x6C,0x94,0xE4,0xC7,0x1E,0xDC,0x3C,0xA5,0x83,0x1F,0x8B,0xEB,0x52,0x0E,0x0E,0x7E,0x2E,0x4E,0xC7,0x31,0xD2,0x79,0xA5,0x3A,0x0D,0xD9,0xC4,0xFF,0x07}; +uint8_t spFIVE[] PROGMEM = {0x02,0xE8,0x3E,0x8C,0x01,0xDD,0x65,0x08,0x60,0x98,0x4C,0x06,0x34,0x93,0xCE,0x80,0xE6,0xDA,0x9A,0x14,0x6B,0xAA,0x47,0xD1,0x5E,0x56,0xAA,0x6D,0x56,0xCD,0x78,0xD9,0xA9,0x1C,0x67,0x05,0x83,0xE1,0xA4,0xBA,0x38,0xEE,0x16,0x86,0x9B,0xFA,0x60,0x87,0x5B,0x18,0x6E,0xEE,0x8B,0x1D,0x6E,0x61,0xB9,0x69,0x36,0x65,0xBA,0x8D,0xE5,0xE5,0x3E,0x1C,0xE9,0x0E,0x96,0x9B,0x5B,0xAB,0x95,0x2B,0x58,0x6E,0xCE,0xE5,0x3A,0x6A,0xF3,0xB8,0x35,0x84,0x7B,0x05,0xA3,0xE3,0x36,0xEF,0x92,0x19,0xB4,0x86,0xDB,0xB4,0x69,0xB4,0xD1,0x2A,0x4E,0x65,0x9A,0x99,0xCE,0x28,0xD9,0x85,0x71,0x4C,0x18,0x6D,0x67,0x47,0xC6,0x5E,0x53,0x4A,0x9C,0xB5,0xE2,0x85,0x45,0x26,0xFE,0x7F}; +uint8_t spSIX[] PROGMEM = {0x0E,0xD8,0xAE,0xDD,0x03,0x0E,0x38,0xA6,0xD2,0x01,0xD3,0xB4,0x2C,0xAD,0x6A,0x35,0x9D,0xB1,0x7D,0xDC,0xEE,0xC4,0x65,0xD7,0xF1,0x72,0x47,0x24,0xB3,0x19,0xD9,0xD9,0x05,0x70,0x40,0x49,0xEA,0x02,0x98,0xBE,0x42,0x01,0xDF,0xA4,0x69,0x40,0x00,0xDF,0x95,0xFC,0x3F}; +uint8_t spSEVEN[] PROGMEM = {0x02,0xB8,0x3A,0x8C,0x01,0xDF,0xA4,0x73,0x40,0x01,0x47,0xB9,0x2F,0x33,0x3B,0x73,0x5F,0x53,0x7C,0xEC,0x9A,0xC5,0x63,0xD5,0xD1,0x75,0xAE,0x5B,0xFC,0x64,0x5C,0x35,0x87,0x91,0xF1,0x83,0x36,0xB5,0x68,0x55,0xC5,0x6F,0xDA,0x45,0x2D,0x1C,0x2D,0xB7,0x38,0x37,0x9F,0x60,0x3C,0xBC,0x9A,0x85,0xA3,0x25,0x66,0xF7,0x8A,0x57,0x1C,0xA9,0x67,0x56,0xCA,0x5E,0xF0,0xB2,0x16,0xB2,0xF1,0x89,0xCE,0x8B,0x92,0x25,0xC7,0x2B,0x33,0xCF,0x48,0xB1,0x99,0xB4,0xF3,0xFF}; +uint8_t spEIGHT[] PROGMEM = {0xC3,0x6C,0x86,0xB3,0x27,0x6D,0x0F,0xA7,0x48,0x99,0x4E,0x55,0x3C,0xBC,0x22,0x65,0x36,0x4D,0xD1,0xF0,0x32,0xD3,0xBE,0x34,0xDA,0xC3,0xEB,0x82,0xE2,0xDA,0x65,0x35,0xAF,0x31,0xF2,0x6B,0x97,0x95,0xBC,0x86,0xD8,0x6F,0x82,0xA6,0x73,0x0B,0xC6,0x9E,0x72,0x99,0xCC,0xCB,0x02,0xAD,0x3C,0x9A,0x10,0x60,0xAB,0x62,0x05,0x2C,0x37,0x84,0x00,0xA9,0x73,0x00,0x00,0xFE,0x1F}; +uint8_t spNINE[] PROGMEM = {0xCC,0xA1,0x26,0xBB,0x83,0x93,0x18,0xCF,0x4A,0xAD,0x2E,0x31,0xED,0x3C,0xA7,0x24,0x26,0xC3,0x54,0xF1,0x92,0x64,0x8B,0x8A,0x98,0xCB,0x2B,0x2E,0x34,0x53,0x2D,0x0E,0x2F,0x57,0xB3,0x0C,0x0D,0x3C,0xBC,0x3C,0x4C,0x4B,0xCA,0xF4,0xF0,0x72,0x0F,0x6E,0x49,0x53,0xCD,0xCB,0x53,0x2D,0x35,0x4D,0x0F,0x2F,0x0F,0xD7,0x0C,0x0D,0x3D,0xBC,0xDC,0x4D,0xD3,0xDD,0xC2,0xF0,0x72,0x52,0x4F,0x57,0x9B,0xC3,0xAB,0x89,0xBD,0x42,0x2D,0x0F,0xAF,0x5A,0xD1,0x71,0x91,0x55,0xBC,0x2C,0xC5,0x3B,0xD8,0x65,0xF2,0x82,0x94,0x18,0x4E,0x3B,0xC1,0x73,0x42,0x32,0x33,0x15,0x45,0x4F,0x79,0x52,0x6A,0x55,0xA6,0xA3,0xFF,0x07}; +uint8_t spTEN[] PROGMEM = {0x0E,0xD8,0xB1,0xDD,0x01,0x3D,0xA8,0x24,0x7B,0x04,0x27,0x76,0x77,0xDC,0xEC,0xC2,0xC5,0x23,0x84,0xCD,0x72,0x9A,0x51,0xF7,0x62,0x45,0xC7,0xEB,0x4E,0x35,0x4A,0x14,0x2D,0xBF,0x45,0xB6,0x0A,0x75,0xB8,0xFC,0x16,0xD9,0x2A,0xD9,0xD6,0x0A,0x5A,0x10,0xCD,0xA2,0x48,0x23,0xA8,0x81,0x35,0x4B,0x2C,0xA7,0x20,0x69,0x0A,0xAF,0xB6,0x15,0x82,0xA4,0x29,0x3C,0xC7,0x52,0x08,0xA2,0x22,0xCF,0x68,0x4B,0x2E,0xF0,0x8A,0xBD,0xA3,0x2C,0xAB,0x40,0x1B,0xCE,0xAA,0xB2,0x6C,0x82,0x40,0x4D,0x7D,0xC2,0x89,0x88,0x8A,0x61,0xCC,0x74,0xD5,0xFF,0x0F}; +uint8_t spELEVEN[] PROGMEM = {0xC3,0xCD,0x76,0x5C,0xAE,0x14,0x0F,0x37,0x9B,0x71,0xDE,0x92,0x55,0xBC,0x2C,0x27,0x70,0xD3,0x76,0xF0,0x83,0x5E,0xA3,0x5E,0x5A,0xC1,0xF7,0x61,0x58,0xA7,0x19,0x35,0x3F,0x99,0x31,0xDE,0x52,0x74,0xFC,0xA2,0x26,0x64,0x4B,0xD1,0xF1,0xAB,0xAE,0xD0,0x2D,0xC5,0xC7,0x2F,0x36,0xDD,0x27,0x15,0x0F,0x3F,0xD9,0x08,0x9F,0x62,0xE4,0xC2,0x2C,0xD4,0xD8,0xD3,0x89,0x0B,0x1B,0x57,0x11,0x0B,0x3B,0xC5,0xCF,0xD6,0xCC,0xC6,0x64,0x35,0xAF,0x18,0x73,0x1F,0xA1,0x5D,0xBC,0x62,0x45,0xB3,0x45,0x51,0xF0,0xA2,0x62,0xAB,0x4A,0x5B,0xC9,0x4B,0x8A,0x2D,0xB3,0x6C,0x06,0x2F,0x29,0xB2,0xAC,0x8A,0x18,0xBC,0x28,0xD9,0xAA,0xD2,0x92,0xF1,0xBC,0xE0,0x98,0x8C,0x48,0xCC,0x17,0x52,0xA3,0x27,0x6D,0x93,0xD0,0x4B,0x8E,0x0E,0x77,0x02,0x00,0xFF,0x0F}; +uint8_t spTWELVE[] PROGMEM = {0x06,0x28,0x46,0xD3,0x01,0x25,0x06,0x13,0x20,0xBA,0x70,0x70,0xB6,0x79,0xCA,0x36,0xAE,0x28,0x38,0xE1,0x29,0xC5,0x35,0xA3,0xE6,0xC4,0x16,0x6A,0x53,0x8C,0x97,0x9B,0x72,0x86,0x4F,0x28,0x1A,0x6E,0x0A,0x59,0x36,0xAE,0x68,0xF8,0x29,0x67,0xFA,0x06,0xA3,0x16,0xC4,0x96,0xE6,0x53,0xAC,0x5A,0x9C,0x56,0x72,0x77,0x31,0x4E,0x49,0x5C,0x8D,0x5B,0x29,0x3B,0x24,0x61,0x1E,0x6C,0x9B,0x6C,0x97,0xF8,0xA7,0x34,0x19,0x92,0x4C,0x62,0x9E,0x72,0x65,0x58,0x12,0xB1,0x7E,0x09,0xD5,0x2E,0x53,0xC5,0xBA,0x36,0x6B,0xB9,0x2D,0x17,0x05,0xEE,0x9A,0x6E,0x8E,0x05,0x50,0x6C,0x19,0x07,0x18,0x50,0xBD,0x3B,0x01,0x92,0x08,0x41,0x40,0x10,0xA6,0xFF,0x0F}; +uint8_t spTHIRTEEN[] PROGMEM = {0x08,0xE8,0x2C,0x15,0x01,0x43,0x07,0x13,0xE0,0x98,0xB4,0xA6,0x35,0xA9,0x1E,0xDE,0x56,0x8E,0x53,0x9C,0x7A,0xE7,0xCA,0x5E,0x76,0x8D,0x94,0xE5,0x2B,0xAB,0xD9,0xB5,0x62,0xA4,0x9C,0xE4,0xE6,0xB4,0x41,0x1E,0x7C,0xB6,0x93,0xD7,0x16,0x99,0x5A,0xCD,0x61,0x76,0x55,0xC2,0x91,0x61,0x1B,0xC0,0x01,0x5D,0x85,0x05,0xE0,0x68,0x51,0x07,0x1C,0xA9,0x64,0x80,0x1D,0x4C,0x9C,0x95,0x88,0xD4,0x04,0x3B,0x4D,0x4E,0x21,0x5C,0x93,0xA8,0x26,0xB9,0x05,0x4B,0x6E,0xA0,0xE2,0xE4,0x57,0xC2,0xB9,0xC1,0xB2,0x93,0x5F,0x09,0xD7,0x24,0xCB,0x4E,0x41,0x25,0x54,0x1D,0x62,0x3B,0x05,0x8D,0x52,0x57,0xAA,0xAD,0x10,0x24,0x26,0xE3,0xE1,0x36,0x5D,0x10,0x85,0xB4,0x97,0x85,0x72,0x41,0x14,0x52,0x5E,0x1A,0xCA,0xF9,0x91,0x6B,0x7A,0x5B,0xC4,0xE0,0x17,0x2D,0x54,0x1D,0x92,0x8C,0x1F,0x25,0x4B,0x8F,0xB2,0x16,0x41,0xA1,0x4A,0x3E,0xE6,0xFA,0xFF,0x01}; +uint8_t spFOURTEEN[] PROGMEM = {0x0C,0x58,0xAE,0x5C,0x01,0xD9,0x87,0x07,0x51,0xB7,0x25,0xB3,0x8A,0x15,0x2C,0xF7,0x1C,0x35,0x87,0x4D,0xB2,0xDD,0x53,0xCE,0x28,0x2B,0xC9,0x0E,0x97,0x2D,0xBD,0x2A,0x17,0x27,0x76,0x8E,0xD2,0x9A,0x6C,0x80,0x94,0x71,0x00,0x00,0x02,0xB0,0x58,0x58,0x00,0x9E,0x0B,0x0A,0xC0,0xB2,0xCE,0xC1,0xC8,0x98,0x7A,0x52,0x95,0x24,0x2B,0x11,0xED,0x36,0xD4,0x92,0xDC,0x4C,0xB5,0xC7,0xC8,0x53,0xF1,0x2A,0xE5,0x1A,0x17,0x55,0xC5,0xAF,0x94,0xBB,0xCD,0x1C,0x26,0xBF,0x52,0x9A,0x72,0x53,0x98,0xFC,0xC2,0x68,0xD2,0x4D,0x61,0xF0,0xA3,0x90,0xB6,0xD6,0x50,0xC1,0x8F,0x42,0xDA,0x4A,0x43,0x39,0x3F,0x48,0x2D,0x6B,0x33,0xF9,0xFF}; +uint8_t spFIFTEEN[] PROGMEM = {0x08,0xE8,0x2A,0x0D,0x01,0xDD,0xBA,0x31,0x60,0x6A,0xF7,0xA0,0xAE,0x54,0xAA,0x5A,0x76,0x97,0xD9,0x34,0x69,0xEF,0x32,0x1E,0x66,0xE1,0xE2,0xB3,0x43,0xA9,0x18,0x55,0x92,0x4E,0x37,0x2D,0x67,0x6F,0xDF,0xA2,0x5A,0xB6,0x04,0x30,0x55,0xA8,0x00,0x86,0x09,0xE7,0x00,0x01,0x16,0x17,0x05,0x70,0x40,0x57,0xE5,0x01,0xF8,0x21,0x34,0x00,0xD3,0x19,0x33,0x80,0x89,0x9A,0x62,0x34,0x4C,0xD5,0x49,0xAE,0x8B,0x53,0x09,0xF7,0x26,0xD9,0x6A,0x7E,0x23,0x5C,0x13,0x12,0xB3,0x04,0x9D,0x50,0x4F,0xB1,0xAD,0x14,0x15,0xC2,0xD3,0xA1,0xB6,0x42,0x94,0xA8,0x8C,0x87,0xDB,0x74,0xB1,0x70,0x59,0xE1,0x2E,0xC9,0xC5,0x81,0x5B,0x55,0xA4,0x4C,0x17,0x47,0xC1,0x6D,0xE3,0x81,0x53,0x9C,0x84,0x6A,0x46,0xD9,0x4C,0x51,0x31,0x42,0xD9,0x66,0xC9,0x44,0x85,0x29,0x6A,0x9B,0xAD,0xFF,0x07}; +uint8_t spSIXTEEN[] PROGMEM = {0x0A,0x58,0x5A,0x5D,0x00,0x93,0x97,0x0B,0x60,0xA9,0x48,0x05,0x0C,0x15,0xAE,0x80,0xAD,0x3D,0x14,0x30,0x7D,0xD9,0x50,0x92,0x92,0xAC,0x0D,0xC5,0xCD,0x2A,0x82,0xAA,0x3B,0x98,0x04,0xB3,0x4A,0xC8,0x9A,0x90,0x05,0x09,0x68,0x51,0xD4,0x01,0x23,0x9F,0x1A,0x60,0xA9,0x12,0x03,0xDC,0x50,0x81,0x80,0x22,0xDC,0x20,0x00,0xCB,0x06,0x3A,0x60,0x16,0xE3,0x64,0x64,0x42,0xDD,0xCD,0x6A,0x8A,0x5D,0x28,0x75,0x07,0xA9,0x2A,0x5E,0x65,0x34,0xED,0x64,0xBB,0xF8,0x85,0xF2,0x94,0x8B,0xAD,0xE4,0x37,0x4A,0x5B,0x21,0xB6,0x52,0x50,0x19,0xAD,0xA7,0xD8,0x4A,0x41,0x14,0xDA,0x5E,0x12,0x3A,0x04,0x91,0x4B,0x7B,0x69,0xA8,0x10,0x24,0x2E,0xE5,0xA3,0x81,0x52,0x90,0x94,0x5A,0x55,0x98,0x32,0x41,0x50,0xCC,0x93,0x2E,0x47,0x85,0x89,0x1B,0x5B,0x5A,0x62,0x04,0x44,0xE3,0x02,0x80,0x80,0x64,0xDD,0xFF,0x1F}; +uint8_t spSEVENTEEN[] PROGMEM = {0x02,0x98,0x3A,0x42,0x00,0x5B,0xA6,0x09,0x60,0xDB,0x52,0x06,0x1C,0x93,0x29,0x80,0xA9,0x52,0x87,0x9A,0xB5,0x99,0x4F,0xC8,0x3E,0x46,0xD6,0x5E,0x7E,0x66,0xFB,0x98,0xC5,0x5A,0xC6,0x9A,0x9C,0x63,0x15,0x6B,0x11,0x13,0x8A,0x9C,0x97,0xB9,0x9A,0x5A,0x39,0x71,0xEE,0xD2,0x29,0xC2,0xA6,0xB8,0x58,0x59,0x99,0x56,0x14,0xA3,0xE1,0x26,0x19,0x19,0xE3,0x8C,0x93,0x17,0xB4,0x46,0xB5,0x88,0x71,0x9E,0x97,0x9E,0xB1,0x2C,0xC5,0xF8,0x56,0xC4,0x58,0xA3,0x1C,0xE1,0x33,0x9D,0x13,0x41,0x8A,0x43,0x58,0xAD,0x95,0xA9,0xDB,0x36,0xC0,0xD1,0xC9,0x0E,0x58,0x4E,0x45,0x01,0x23,0xA9,0x04,0x37,0x13,0xAE,0x4D,0x65,0x52,0x82,0xCA,0xA9,0x37,0x99,0x4D,0x89,0xBA,0xC0,0xBC,0x14,0x36,0x25,0xEA,0x1C,0x73,0x52,0x1D,0x97,0xB8,0x33,0xAC,0x0E,0x75,0x9C,0xE2,0xCE,0xB0,0xDA,0xC3,0x51,0x4A,0x1A,0xA5,0xCA,0x70,0x5B,0x21,0xCE,0x4C,0x26,0xD2,0x6C,0xBA,0x38,0x71,0x2E,0x1F,0x2D,0xED,0xE2,0x24,0xB8,0xBC,0x3D,0x52,0x88,0xAB,0x50,0x8E,0xA8,0x48,0x22,0x4E,0x42,0xA0,0x26,0x55,0xFD,0x3F}; +uint8_t spEIGHTEEN[] PROGMEM = {0x2E,0x9C,0xD1,0x4D,0x54,0xEC,0x2C,0xBF,0x1B,0x8A,0x99,0x70,0x7C,0xFC,0x2E,0x29,0x6F,0x52,0xF6,0xF1,0xBA,0x20,0xBF,0x36,0xD9,0xCD,0xED,0x0C,0xF3,0x27,0x64,0x17,0x73,0x2B,0xA2,0x99,0x90,0x65,0xEC,0xED,0x40,0x73,0x32,0x12,0xB1,0xAF,0x30,0x35,0x0B,0xC7,0x00,0xE0,0x80,0xAE,0xDD,0x1C,0x70,0x43,0xAA,0x03,0x86,0x51,0x36,0xC0,0x30,0x64,0xCE,0x4C,0x98,0xFB,0x5C,0x65,0x07,0xAF,0x10,0xEA,0x0B,0x66,0x1B,0xFC,0x46,0xA8,0x3E,0x09,0x4D,0x08,0x2A,0xA6,0x3E,0x67,0x36,0x21,0x2A,0x98,0x67,0x9D,0x15,0xA7,0xA8,0x60,0xEE,0xB6,0x94,0x99,0xA2,0x4A,0x78,0x22,0xC2,0xA6,0x8B,0x8C,0x8E,0xCC,0x4C,0x8A,0x2E,0x8A,0x4C,0xD3,0x57,0x03,0x87,0x28,0x71,0x09,0x1F,0x2B,0xE4,0xA2,0xC4,0xC5,0x6D,0xAD,0x54,0x88,0xB2,0x63,0xC9,0xF2,0x50,0x2E,0x8A,0x4A,0x38,0x4A,0xEC,0x88,0x28,0x08,0xE3,0x28,0x49,0xF3,0xFF}; +uint8_t spNINETEEN[] PROGMEM = {0xC2,0xEA,0x8A,0x95,0x2B,0x6A,0x05,0x3F,0x71,0x71,0x5F,0x0D,0x12,0xFC,0x28,0x25,0x62,0x35,0xF0,0xF0,0xB3,0x48,0x1E,0x0F,0xC9,0xCB,0x2F,0x45,0x7C,0x2C,0x25,0x1F,0xBF,0x14,0xB3,0x2C,0xB5,0x75,0xFC,0x5A,0x5C,0xA3,0x5D,0xE1,0xF1,0x7A,0x76,0xB3,0x4E,0x45,0xC7,0xED,0x96,0x23,0x3B,0x18,0x37,0x7B,0x18,0xCC,0x09,0x51,0x13,0x4C,0xAB,0x6C,0x4C,0x4B,0x96,0xD2,0x49,0xAA,0x36,0x0B,0xC5,0xC2,0x20,0x26,0x27,0x35,0x63,0x09,0x3D,0x30,0x8B,0xF0,0x48,0x5C,0xCA,0x61,0xDD,0xCB,0xCD,0x91,0x03,0x8E,0x4B,0x76,0xC0,0xCC,0x4D,0x06,0x98,0x31,0x31,0x98,0x99,0x70,0x6D,0x2A,0xA3,0xE4,0x16,0xCA,0xBD,0xCE,0x5C,0x92,0x57,0x28,0xCF,0x09,0x69,0x2E,0x7E,0xA5,0x3C,0x63,0xA2,0x30,0x05,0x95,0xD2,0x74,0x98,0xCD,0x14,0x54,0xCA,0x53,0xA9,0x96,0x52,0x50,0x28,0x6F,0xBA,0xCB,0x0C,0x41,0x50,0xDE,0x65,0x2E,0xD3,0x05,0x89,0x4B,0x7B,0x6B,0x20,0x17,0x44,0xAE,0xED,0x23,0x81,0x52,0x90,0x85,0x73,0x57,0xD0,0x72,0x41,0xB1,0x02,0xDE,0x2E,0xDB,0x04,0x89,0x05,0x79,0xBB,0x62,0xE5,0x76,0x11,0xCA,0x61,0x0E,0xFF,0x1F}; +uint8_t spTWENTY[] PROGMEM = {0x01,0x98,0xD1,0xC2,0x00,0xCD,0xA4,0x32,0x20,0x79,0x13,0x04,0x28,0xE7,0x92,0xDC,0x70,0xCC,0x5D,0xDB,0x76,0xF3,0xD2,0x32,0x0B,0x0B,0x5B,0xC3,0x2B,0xCD,0xD4,0xDD,0x23,0x35,0xAF,0x44,0xE1,0xF0,0xB0,0x6D,0x3C,0xA9,0xAD,0x3D,0x35,0x0E,0xF1,0x0C,0x8B,0x28,0xF7,0x34,0x01,0x68,0x22,0xCD,0x00,0xC7,0xA4,0x04,0xBB,0x32,0xD6,0xAC,0x56,0x9C,0xDC,0xCA,0x28,0x66,0x53,0x51,0x70,0x2B,0xA5,0xBC,0x0D,0x9A,0xC1,0xEB,0x14,0x73,0x37,0x29,0x19,0xAF,0x33,0x8C,0x3B,0xA7,0x24,0xBC,0x42,0xB0,0xB7,0x59,0x09,0x09,0x3C,0x96,0xE9,0xF4,0x58,0xFF,0x0F}; +uint8_t spTHIRTY[] PROGMEM = {0x08,0x98,0xD6,0x15,0x01,0x43,0xBB,0x0A,0x20,0x1B,0x8B,0xE5,0x16,0xA3,0x1E,0xB6,0xB6,0x96,0x97,0x3C,0x57,0xD4,0x2A,0x5E,0x7E,0x4E,0xD8,0xE1,0x6B,0x7B,0xF8,0x39,0x63,0x0D,0x9F,0x95,0xE1,0xE7,0x4C,0x76,0xBC,0x91,0x5B,0x90,0x13,0xC6,0x68,0x57,0x4E,0x41,0x8B,0x10,0x5E,0x1D,0xA9,0x44,0xD3,0xBA,0x47,0xB8,0xDD,0xE4,0x35,0x86,0x11,0x93,0x94,0x92,0x5F,0x29,0xC7,0x4C,0x30,0x0C,0x41,0xC5,0x1C,0x3B,0x2E,0xD3,0x05,0x15,0x53,0x6C,0x07,0x4D,0x15,0x14,0x8C,0xB5,0xC9,0x6A,0x44,0x90,0x10,0x4E,0x9A,0xB6,0x21,0x81,0x23,0x3A,0x91,0x91,0xE8,0xFF,0x01}; +uint8_t spFOURTY[] PROGMEM = {0x04,0x18,0xB6,0x4C,0x00,0xC3,0x56,0x30,0xA0,0xE8,0xF4,0xA0,0x98,0x99,0x62,0x91,0xAE,0x83,0x6B,0x77,0x89,0x78,0x3B,0x09,0xAE,0xBD,0xA6,0x1E,0x63,0x3B,0x79,0x7E,0x71,0x5A,0x8F,0x95,0xE6,0xA5,0x4A,0x69,0xB9,0x4E,0x8A,0x5F,0x12,0x56,0xE4,0x58,0x69,0xE1,0x36,0xA1,0x69,0x2E,0x2B,0xF9,0x95,0x93,0x55,0x17,0xED,0xE4,0x37,0xC6,0xBA,0x93,0xB2,0x92,0xDF,0x19,0xD9,0x6E,0xC8,0x0A,0xFE,0x60,0xE8,0x37,0x21,0xC9,0xF9,0x8D,0x61,0x5F,0x32,0x13,0xE7,0x17,0x4C,0xD3,0xC6,0xB1,0x94,0x97,0x10,0x8F,0x8B,0xAD,0x11,0x7E,0xA1,0x9A,0x26,0x92,0xF6,0xFF,0x01}; +uint8_t spFIFTY[] PROGMEM = {0x08,0xE8,0x2E,0x84,0x00,0x23,0x84,0x13,0x60,0x38,0x95,0xA5,0x0F,0xCF,0xE2,0x79,0x8A,0x8F,0x37,0x02,0xB3,0xD5,0x2A,0x6E,0x5E,0x93,0x94,0x79,0x45,0xD9,0x05,0x5D,0x0A,0xB9,0x97,0x63,0x02,0x74,0xA7,0x82,0x80,0xEE,0xC3,0x10,0xD0,0x7D,0x28,0x03,0x6E,0x14,0x06,0x70,0xE6,0x0A,0xC9,0x9A,0x4E,0x37,0xD9,0x95,0x51,0xCE,0xBA,0xA2,0x14,0x0C,0x81,0x36,0x1B,0xB2,0x5C,0x30,0x38,0xFA,0x9C,0xC9,0x32,0x41,0xA7,0x18,0x3B,0xA2,0x48,0x04,0x05,0x51,0x4F,0x91,0x6D,0x12,0x04,0x20,0x9B,0x61,0x89,0xFF,0x1F}; +uint8_t spGOOD[] PROGMEM = {0x0A,0x28,0xCD,0x34,0x20,0xD9,0x1A,0x45,0x74,0xE4,0x66,0x24,0xAD,0xBA,0xB1,0x8C,0x9B,0x91,0xA5,0x64,0xE6,0x98,0x21,0x16,0x0B,0x96,0x9B,0x4C,0xE5,0xFF,0x01}; +uint8_t spMORNING[] PROGMEM = {0xCE,0x08,0x52,0x2A,0x35,0x5D,0x39,0x53,0x29,0x5B,0xB7,0x0A,0x15,0x0C,0xEE,0x2A,0x42,0x56,0x66,0xD2,0x55,0x2E,0x37,0x2F,0xD9,0x45,0xB3,0xD3,0xC5,0xCA,0x6D,0x27,0xD5,0xEE,0x50,0xF5,0x50,0x94,0x14,0x77,0x2D,0xD8,0x5D,0x49,0x92,0xFD,0xB1,0x64,0x2F,0xA9,0x49,0x0C,0x93,0x4B,0xAD,0x19,0x17,0x3E,0x66,0x1E,0xF1,0xA2,0x5B,0x84,0xE2,0x29,0x8F,0x8B,0x72,0x10,0xB5,0xB1,0x2E,0x4B,0xD4,0x45,0x89,0x4A,0xEC,0x5C,0x95,0x14,0x2B,0x8A,0x9C,0x34,0x52,0x5D,0xBC,0xCC,0xB5,0x3B,0x49,0x69,0x89,0x87,0xC1,0x98,0x56,0x3A,0x21,0x2B,0x82,0x67,0xCC,0x5C,0x85,0xB5,0x4A,0x8A,0xF6,0x64,0xA9,0x96,0xC4,0x69,0x3C,0x52,0x81,0x58,0x1C,0x97,0xF6,0x0E,0x1B,0xCC,0x0D,0x42,0x32,0xAA,0x65,0x12,0x67,0xD4,0x6A,0x61,0x52,0xFC,0xFF}; +uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x9D,0x28,0x71,0xB4,0xAC,0x9C,0x74,0x5A,0x42,0x55,0x33,0xB2,0x93,0x0A,0x09,0xD4,0xC5,0x9A,0xD6,0x44,0x45,0xE3,0x38,0x60,0x9A,0x32,0x05,0xF4,0x18,0x01,0x09,0xD8,0xA9,0xC2,0x00,0x5E,0xCA,0x24,0xD5,0x5B,0x9D,0x4A,0x95,0xEA,0x34,0xEE,0x63,0x92,0x5C,0x4D,0xD0,0xA4,0xEE,0x58,0x0C,0xB9,0x4D,0xCD,0x42,0xA2,0x3A,0x24,0x37,0x25,0x8A,0xA8,0x8E,0xA0,0x53,0xE4,0x28,0x23,0x26,0x13,0x72,0x91,0xA2,0x76,0xBB,0x72,0x38,0x45,0x0A,0x46,0x63,0xCA,0x69,0x27,0x39,0x58,0xB1,0x8D,0x60,0x1C,0x34,0x1B,0x34,0xC3,0x55,0x8E,0x73,0x45,0x2D,0x4F,0x4A,0x3A,0x26,0x10,0xA1,0xCA,0x2D,0xE9,0x98,0x24,0x0A,0x1E,0x6D,0x97,0x29,0xD2,0xCC,0x71,0xA2,0xDC,0x86,0xC8,0x12,0xA7,0x8E,0x08,0x85,0x22,0x8D,0x9C,0x43,0xA7,0x12,0xB2,0x2E,0x50,0x09,0xEF,0x51,0xC5,0xBA,0x28,0x58,0xAD,0xDB,0xE1,0xFF,0x03}; +uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; +uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; + +void sayTime(int hour, int minutes) ; + +void sayTime(int hour, int minutes) { +AudioGeneratorTalkie *talkie = nullptr; + + if (!audio_i2s.out) return; + + AUDIO_PWR_ON + talkie = new AudioGeneratorTalkie(); + talkie->begin(nullptr, audio_i2s.out); + + bool pm = (hour >= 12); + uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE, spTEN, spELEVEN }; + size_t spHourLen[] = { sizeof(spTWELVE), sizeof(spONE), sizeof(spTWO), + sizeof(spTHREE), sizeof(spFOUR), sizeof(spFIVE), + sizeof(spSIX), sizeof(spSEVEN), sizeof(spEIGHT), + sizeof(spNINE), sizeof(spTEN), sizeof(spELEVEN) }; + uint8_t *spMinDec[] = { spOH, spTEN, spTWENTY, spTHIRTY, spFOURTY, spFIFTY }; + size_t spMinDecLen[] = { sizeof(spOH), sizeof(spTEN), sizeof(spTWENTY), + sizeof(spTHIRTY), sizeof(spFOURTY), sizeof(spFIFTY) }; + uint8_t *spMinSpecial[] = { spELEVEN, spTWELVE, spTHIRTEEN, spFOURTEEN, + spFIFTEEN, spSIXTEEN, spSEVENTEEN, spEIGHTEEN, + spNINETEEN }; + size_t spMinSpecialLen[] = { sizeof(spELEVEN), sizeof(spTWELVE), + sizeof(spTHIRTEEN), sizeof(spFOURTEEN), + sizeof(spFIFTEEN), sizeof(spSIXTEEN), + sizeof(spSEVENTEEN), sizeof(spEIGHTEEN), + sizeof(spNINETEEN) }; + uint8_t *spMinLow[] = { spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE }; + size_t spMinLowLen[] = { sizeof(spONE), sizeof(spTWO), sizeof(spTHREE), + sizeof(spFOUR), sizeof(spFIVE), sizeof(spSIX), + sizeof(spSEVEN), sizeof(spEIGHT), sizeof(spNINE) }; + + talkie->say(spTHE, sizeof(spTHE)); + talkie->say(spTIME, sizeof(spTIME)); + talkie->say(spIS, sizeof(spIS)); + + hour = hour % 12; + talkie->say(spHour[hour], spHourLen[hour]); + if (minutes==0) { + talkie->say(spOCLOCK, sizeof(spOCLOCK)); + } else if (minutes<=10 || minutes >=20) { + talkie->say(spMinDec[minutes / 10], spMinDecLen[minutes /10]); + if (minutes % 10) { + talkie->say(spMinLow[(minutes % 10) - 1], spMinLowLen[(minutes % 10) - 1]); } + } else { + talkie->say(spMinSpecial[minutes - 11], spMinSpecialLen[minutes - 11]); + } + if (pm) { + talkie->say(spP_M_, sizeof(spP_M_)); + } else { + talkie->say(spA_M_, sizeof(spA_M_)); + } + delete talkie; + audio_i2s.out->stop(); + DOWNRATE + AUDIO_PWR_OFF +} +#endif // USE_I2S_SAY_TIME - return ret_val; + +enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; +enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; + +#ifdef ESP8266 +#define I2S_MCLK_MULTIPLE_128 0 +#endif + +int32_t I2S_Init_0(void) { + + audio_i2s.i2s_port = (i2s_port_t)0; + +#if USE_I2S_EXTERNAL_DAC + // use i2s +#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT)) + audio_i2s.i2s_port = (i2s_port_t)0; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(); +#endif + audio_i2s.bclk = DAC_IIS_BCK; + audio_i2s.ws = DAC_IIS_WS; + audio_i2s.dout = DAC_IIS_DOUT; +#else +#ifdef USE_I2S_NO_DAC + if (PinUsed(GPIO_I2S_DOUT)) { +#else + if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) { +#endif // USE_I2S_NO_DAC + audio_i2s.i2s_port = (i2s_port_t)0; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); +#endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK); + audio_i2s.ws = Pin(GPIO_I2S_WS); + audio_i2s.dout = Pin(GPIO_I2S_DOUT); + audio_i2s.din = Pin(GPIO_I2S_DIN); + } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT), 1) { + audio_i2s.i2s_port = (i2s_port_t)1; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); +#endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); + audio_i2s.ws = Pin(GPIO_I2S_WS, 1); + audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); + audio_i2s.din = Pin(GPIO_I2S_DIN, 1); + } else { + return -1; + } +#ifdef ESP8266 + // esp8266 have fixed pins + if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) { + return -2; + } +#endif // ESP8266 +#endif // defined(DAC_IIS_BCK) + + audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + + AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + + +#else + +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0 +#endif // USE_I2S_NO_DAC + +#endif // USE_I2S_EXTERNAL_DAC + + return 0; } -// box adc init -uint32_t es7210_init() { - uint32_t ret_val = ESP_OK; - if (I2cSetDevice(ES7210_ADDR, 1)) { - I2cSetActiveFound(ES7210_ADDR, "ES7210-I2C", 1); - audio_hal_codec_config_t cfg = { - .adc_input = AUDIO_HAL_ADC_INPUT_ALL, - .codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE, - .i2s_iface = { - .mode = AUDIO_HAL_MODE_SLAVE, - .fmt = AUDIO_HAL_I2S_NORMAL, - .samples = AUDIO_HAL_16K_SAMPLES, - .bits = AUDIO_HAL_BIT_LENGTH_16BITS, - }, + +void I2S_Init(void) { + + #if defined(ESP32) && defined(ESP32S3_BOX) + S3boxInit(); + #endif + + if (I2S_Init_0()) { + return; + } + + DOWNRATE + audio_i2s.is2_volume=10; + audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); + audio_i2s.out->stop(); + audio_i2s.mp3ram = nullptr; + +#ifdef ESP32 + if (UsePSRAM()) { + audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + +#ifdef USE_I2S_WEBRADIO + if (UsePSRAM()) { + audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + audio_i2s.preallocateBuffer = malloc(preallocateBufferSize); + audio_i2s.preallocateCodec = malloc(preallocateCodecSize); + } + if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) { + //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); + } +#endif // USE_I2S_WEBRADIO + + audio_i2s.mic_channels = MIC_CHANNELS; + audio_i2s.mic_rate = MICSRATE; + +#endif // ESP32 +} + +#ifdef ESP32 +uint32_t SpeakerMic(uint8_t spkr) { + esp_err_t err = ESP_OK; + + if (audio_i2s.out) { + audio_i2s.out->stop(); + delete audio_i2s.out; + audio_i2s.out = nullptr; + } + + i2s_driver_uninstall(audio_i2s.i2s_port); + + if (spkr == MODE_SPK) { + I2S_Init_0(); + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + audio_i2s.out->stop(); + DOWNRATE + } else { + // config mic + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER), + .sample_rate = audio_i2s.mic_rate, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, + .communication_format = I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 2, + //.dma_buf_len = 128, + .dma_buf_len = 1024, + .use_apll = 0, // Use audio PLL + .tx_desc_auto_clear = true, + .fixed_mclk = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT }; - ret_val |= es7210_adc_init(&Wire1, &cfg); - ret_val |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface); - ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2), (es7210_gain_value_t) GAIN_37_5DB); - ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4), (es7210_gain_value_t) GAIN_0DB); - ret_val |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); - } - return ret_val; -} - -// box dac init -uint32_t ES8311_init() { - uint32_t ret_val = ESP_OK; - - if (I2cSetDevice(ES8311_ADDR, 1)) { - I2cSetActiveFound(ES8311_ADDR, "ES8311-I2C", 1); - audio_hal_codec_config_t cfg = { - .dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1, - .codec_mode = AUDIO_HAL_CODEC_MODE_DECODE, - .i2s_iface = { - .mode = AUDIO_HAL_MODE_SLAVE, - .fmt = AUDIO_HAL_I2S_NORMAL, - .samples = AUDIO_HAL_16K_SAMPLES, - .bits = AUDIO_HAL_BIT_LENGTH_16BITS, - }, - }; - - ret_val |= es8311_codec_init(&Wire1, &cfg); - ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); - ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); - ret_val |= es8311_codec_set_voice_volume(75); - ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); - - } - return ret_val; -} - -void S3boxInit() { - if (TasmotaGlobal.i2c_enabled_2) { - // box lite - ES8156_init(); - es7243e_init(); - // box full - ES8311_init(); - es7210_init(); - } -} +#ifdef ESP32S3_BOX + i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; #endif + +#ifdef USE_I2S_MIC + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); + // mic select to GND + i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; #endif + +#ifdef USE_M5STACK_CORE2 + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); +#endif + + err += i2s_driver_install(audio_i2s.i2s_port, &i2s_config, 0, NULL); + + i2s_pin_config_t tx_pin_config; +#ifdef ESP32S3_BOX + tx_pin_config.mck_io_num = audio_i2s.mclk; +#else + tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE; +#endif + tx_pin_config.bck_io_num = audio_i2s.bclk; + tx_pin_config.ws_io_num = audio_i2s.ws; + tx_pin_config.data_out_num = audio_i2s.dout; + tx_pin_config.data_in_num = audio_i2s.din; + + err += i2s_set_pin(audio_i2s.i2s_port, &tx_pin_config); +#ifdef ESP32S3_BOX + err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); +#else + err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); +#endif + } + return err; +} +#endif //ESP32 + +#ifdef ESP32 + +#ifdef USE_SHINE +#include +#include + +// micro to mp3 file +void mic_task(void *arg){ + int8_t error = 0; + uint8_t *ucp; + int written; + shine_config_t config; + shine_t s = nullptr; + uint16_t samples_per_pass; + File mp3_out = (File)nullptr; + int16_t *buffer = nullptr; + uint16_t bytesize; + uint16_t bwritten; + + mp3_out = ufsp->open(audio_i2s.mic_path, "w"); + if (!mp3_out) { + error = -1; + goto exit; + } + + shine_set_config_mpeg_defaults(&config.mpeg); + + if (audio_i2s.mic_channels == 1) { + config.mpeg.mode = MONO; + } else { + config.mpeg.mode = STEREO; + } + config.mpeg.bitr = 128; + config.wave.samplerate = audio_i2s.mic_rate; + config.wave.channels = (channels)audio_i2s.mic_channels; + + if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) { + error = -3; + goto exit; + } + + s = shine_initialise(&config); + if (!s) { + error = -4; + goto exit; + } + + samples_per_pass = shine_samples_per_pass(s); + bytesize = samples_per_pass * 2 * audio_i2s.mic_channels; + + buffer = (int16_t*)malloc(bytesize); + if (!buffer) { + error = -5; + goto exit; + } + + while (!audio_i2s.mic_stop) { + uint32_t bytes_read; + i2s_read(audio_i2s.i2s_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS)); + ucp = shine_encode_buffer_interleaved(s, buffer, &written); + bwritten = mp3_out.write(ucp, written); + if (bwritten != written) { + break; + } + } + ucp = shine_flush(s, &written); + mp3_out.write(ucp, written); + +exit: + if (s) { + shine_close(s); + } + if (mp3_out) { + mp3_out.close(); + } + if (buffer) { + free(buffer); + } + + SpeakerMic(MODE_SPK); + audio_i2s.mic_stop = 0; + audio_i2s.mic_error = error; + AddLog(LOG_LEVEL_INFO, PSTR("task error: %d"), error); + audio_i2s.mic_task_h = 0; + vTaskDelete(NULL); + +} + +int32_t i2s_record_shine(char *path) { +esp_err_t err = ESP_OK; + + if (audio_i2s.decoder || audio_i2s.mp3) return 0; + + err = SpeakerMic(MODE_MIC); + if (err) { + SpeakerMic(MODE_SPK); + return err; + } + + strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path)); + + audio_i2s.mic_stop = 0; + err = xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); + + return err; +} + +#else +// micro to wav file + +#define DATA_SIZE 1024 + +void mic_task(void *arg){ + uint32_t data_offset = 0; + while (1) { + uint32_t bytes_read; + i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); + if (bytes_read != DATA_SIZE) break; + data_offset += DATA_SIZE; + if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; + } + SpeakerMic(MODE_SPK); + SaveWav(audio_i2s.mic_path, audio_i2s.mic_buff, audio_i2s.mic_size); + free(audio_i2s.mic_buff); + vTaskDelete(audio_i2s.mic_task_h); +} + +uint32_t i2s_record(char *path, uint32_t secs) { + esp_err_t err = ESP_OK; + + if (audio_i2s.decoder || audio_i2s.mp3) return 0; + + err = SpeakerMic(MODE_MIC); + if (err) { + SpeakerMic(MODE_SPK); + return err; + } + + audio_i2s.mic_size = secs * audio_i2s.mic_rate * 2 * audio_i2s.mic_channels; + + audio_i2s.mic_buff = (uint8_t*)heap_caps_malloc(audio_i2s.mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (!audio_i2s.mic_buff) return 2; + + if (*path=='+') { + path++; + strlcpy(audio_i2s.mic_path, path , sizeof(audio_i2s.mic_path)); + xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); + return 0; + } + + uint32_t data_offset = 0; + uint32_t stime=millis(); + while (1) { + uint32_t bytes_read; + i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); + if (bytes_read != DATA_SIZE) break; + data_offset += DATA_SIZE; + if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; + delay(0); + } + //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime); + SpeakerMic(MODE_SPK); + // save to path + SaveWav(path, audio_i2s.mic_buff, audio_i2s.mic_size); + free(audio_i2s.mic_buff); + return 0; +} + +static const uint8_t wavHTemplate[] PROGMEM = { // Hardcoded simple WAV header with 0xffffffff lengths all around + 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, + 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, + 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff }; + +bool SaveWav(char *path, uint8_t *buff, uint32_t size) { + File fwp = ufsp->open(path, "w"); + if (!fwp) return false; + uint8_t wavHeader[sizeof(wavHTemplate)]; + memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate)); + + uint8_t channels = audio_i2s.mic_channels; + uint32_t hertz = audio_i2s.mic_rate; + uint8_t bps = 16; + + wavHeader[22] = channels & 0xff; + wavHeader[23] = 0; + wavHeader[24] = hertz & 0xff; + wavHeader[25] = (hertz >> 8) & 0xff; + wavHeader[26] = (hertz >> 16) & 0xff; + wavHeader[27] = (hertz >> 24) & 0xff; + int byteRate = hertz * bps * channels / 8; + wavHeader[28] = byteRate & 0xff; + wavHeader[29] = (byteRate >> 8) & 0xff; + wavHeader[30] = (byteRate >> 16) & 0xff; + wavHeader[31] = (byteRate >> 24) & 0xff; + wavHeader[32] = channels * bps / 8; + wavHeader[33] = 0; + wavHeader[34] = bps; + wavHeader[35] = 0; + + fwp.write(wavHeader, sizeof(wavHeader)); + + fwp.write(buff, size); + fwp.close(); + + return true; +} +#endif // USE_SHINE + +#endif // ESP32 + +#ifdef ESP32 +void mp3_task(void *arg) { + while (1) { + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); + mp3_delete(); + audio_i2s.out->stop(); + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = 0; + } + //mp3_task_h=nullptr; + } + delay(1); + } + } +} +#endif // ESP32 + +#ifdef USE_I2S_WEBRADIO +void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { + const char *ptr = reinterpret_cast(cbData); + (void) isUnicode; // Punt this ball for now + (void) ptr; + if (strstr_P(type, PSTR("Title"))) { + strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title)); + audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0; + //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title); + } else { + // Who knows what to do? Not me! + } +} + +void StatusCallback(void *cbData, int code, const char *string) { + const char *ptr = reinterpret_cast(cbData); + (void) code; + (void) ptr; + //strncpy_P(status, string, sizeof(status)-1); + //status[sizeof(status)-1] = 0; +} + +void Webradio(const char *url) { + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; + AUDIO_PWR_ON + audio_i2s.ifile = new AudioFileSourceICYStream(url); + audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL); + audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize); + audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize); + audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out); + if (!audio_i2s.decoder->isRunning()) { + // Serial.printf_P(PSTR("Can't connect to URL")); + StopPlaying(); + // strcpy_P(status, PSTR("Unable to connect to URL")); + audio_i2s.retryms = millis() + 2000; + } + + xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); +} + +void mp3_task2(void *arg){ + while (1) { + if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { + if (!audio_i2s.decoder->loop()) { + StopPlaying(); + //retryms = millis() + 2000; + } + delay(1); + } + } +} + +void StopPlaying() { + + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = nullptr; + } + + if (audio_i2s.decoder) { + audio_i2s.decoder->stop(); + delete audio_i2s.decoder; + audio_i2s.decoder = NULL; + } + + if (audio_i2s.buff) { + audio_i2s.buff->close(); + delete audio_i2s.buff; + audio_i2s.buff = NULL; + } + + if (audio_i2s.ifile) { + audio_i2s.ifile->close(); + delete audio_i2s.ifile; + audio_i2s.ifile = NULL; + } + DOWNRATE + AUDIO_PWR_OFF +} + +void Cmd_WebRadio(void) { + if (audio_i2s.decoder) { + StopPlaying(); + } + if (XdrvMailbox.data_len > 0) { + Webradio(XdrvMailbox.data); + ResponseCmndChar(XdrvMailbox.data); + } else { + ResponseCmndChar_P(PSTR("Stopped")); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_WEBRADIO[] PROGMEM = + "{s}" "I2S_WR-Title" "{m}%s{e}"; + +void I2S_WR_Show(bool json) { + if (audio_i2s.decoder) { + if (json) { + ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title); + } else { + WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); + } + } +} +#endif // USE_WEBSERVER + +#endif // USE_I2S_WEBRADIO + +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) +#ifdef USE_SHINE +void Cmd_MicRec(void) { + + if (audio_i2s.mic_task_h) { + // stop task + audio_i2s.mic_stop = 1; + while (audio_i2s.mic_stop) { + delay(1); + } + ResponseCmndChar_P(PSTR("Stopped")); + } + if (XdrvMailbox.data_len > 0) { + i2s_record_shine(XdrvMailbox.data); + ResponseCmndChar(XdrvMailbox.data); + } +} +#else +void Cmd_MicRec(void) { + + if (audio_i2s.mic_task_h) { + // stop task + vTaskDelete(audio_i2s.mic_task_h); + audio_i2s.mic_task_h = nullptr; + ResponseCmndChar_P(PSTR("Stopped")); + } + + if (XdrvMailbox.data_len > 0) { + uint16 time = 10; + char *cp = strchr(XdrvMailbox.data, ':'); + if (cp) { + time = atoi(cp + 1); + *cp = 0; + } + if (time<10) time = 10; + if (time>30) time = 30; + i2s_record(XdrvMailbox.data, time); + ResponseCmndChar(XdrvMailbox.data); + } +} +#endif // USE_SHINE +#endif // USE_M5STACK_CORE2 + +#ifdef ESP32 +void Play_mp3(const char *path) { +#ifdef USE_UFILESYS + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; + + bool I2S_Task; + + if (*path=='+') { + I2S_Task = true; + path++; + } else { + I2S_Task = false; + } + + if (!ufsp->exists(path)) { + return; + } + + AUDIO_PWR_ON + + audio_i2s.file = new AudioFileSourceFS(*ufsp, path); + + audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); + + if (audio_i2s.mp3ram) { + audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); + } else { + audio_i2s.mp3 = new AudioGeneratorMP3(); + } + audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); + + if (I2S_Task) { + xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); + } else { + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); + break; + } + OsWatchLoop(); + } + audio_i2s.out->stop(); + mp3_delete(); + } + +#endif // USE_UFILESYS +} + +void mp3_delete(void) { + delete audio_i2s.file; + delete audio_i2s.id3; + delete audio_i2s.mp3; + audio_i2s.mp3=nullptr; + DOWNRATE + AUDIO_PWR_OFF +} +#endif // ESP32 + +void Say(char *text) { + + if (!audio_i2s.out) return; + + AUDIO_PWR_ON + + audio_i2s.out->begin(); + ESP8266SAM *sam = new ESP8266SAM; + sam->Say(audio_i2s.out, text); + delete sam; + audio_i2s.out->stop(); + + DOWNRATE + AUDIO_PWR_OFF +} + + +const char kI2SAudio_Commands[] PROGMEM = "I2S|" + "Say|Gain|Time" +#ifdef ESP32 + "|Play" +#ifdef USE_I2S_WEBRADIO + "|WR" +#endif // USE_I2S_WEBRADIO +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) + "|REC" +#ifdef WAV2MP3 + "|W2M" +#endif +#endif // USE_M5STACK_CORE2 +#endif // ESP32 + ; + +void (* const I2SAudio_Command[])(void) PROGMEM = { + &Cmd_Say, &Cmd_Gain, &Cmd_Time +#ifdef ESP32 + ,&Cmd_Play +#ifdef USE_I2S_WEBRADIO + ,&Cmd_WebRadio +#endif // USE_I2S_WEBRADIO +#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) + ,&Cmd_MicRec +#ifdef WAV2MP3 + ,&Cmd_wav2mp3 +#endif +#endif // USE_M5STACK_CORE2 + +#endif // ESP32 +}; + +void Cmd_Play(void) { + if (XdrvMailbox.data_len > 0) { + Play_mp3(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void Cmd_Gain(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (audio_i2s.out) { + audio_i2s.is2_volume=XdrvMailbox.payload; + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + } + } + ResponseCmndNumber(audio_i2s.is2_volume); +} + +#ifdef WAV2MP3 +void Cmd_wav2mp3(void) { + if (XdrvMailbox.data_len > 0) { +#ifdef USE_SHINE + wav2mp3(XdrvMailbox.data); +#endif // USE_SHINE + } + ResponseCmndChar(XdrvMailbox.data); +} +#endif // WAV2MP3 + +void Cmd_Say(void) { + if (XdrvMailbox.data_len > 0) { + Say(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void Cmd_Time(void) { +#ifdef USE_I2S_SAY_TIME + sayTime(RtcTime.hour, RtcTime.minute); +#endif // USE_I2S_SAY_TIME + ResponseCmndDone(); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv42(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); + break; + case FUNC_INIT: + I2S_Init(); + break; +#ifdef USE_WEBSERVER +#ifdef USE_I2S_WEBRADIO + case FUNC_WEB_SENSOR: + I2S_WR_Show(false); + break; +#endif // USE_I2S_WEBRADIO +#endif // USE_WEBSERVER +#ifdef USE_I2S_WEBRADIO + case FUNC_JSON_APPEND: + I2S_WR_Show(true); + break; +#endif // USE_I2S_WEBRADIO + } + return result; +} + +#endif // USE_I2S_AUDIO diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino new file mode 100644 index 000000000..46e000b5e --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino @@ -0,0 +1,240 @@ + +#ifdef ESP32 +#ifdef ESP32S3_BOX +#include +#include +#include +#include +#include + +void S3boxAudioPower(uint8_t pwr) { + pinMode(46 , OUTPUT); + digitalWrite(46, pwr); +} + +// box lite dac init +uint32_t ES8156_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES8156_ADDR, 1)) { + I2cSetActiveFound(ES8156_ADDR, "ES8156-I2C", 1); + audio_hal_codec_config_t cfg = { + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + } + }; + ret_val |= es8156_codec_init(&Wire1, &cfg); + ret_val |= es8156_codec_set_voice_volume(75); + } + return ret_val; +} + +// box lite adc init +uint32_t es7243e_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES7243_ADDR, 1)) { + I2cSetActiveFound(ES7243_ADDR, "ES7243e-I2C", 1); + + audio_hal_codec_config_t cfg = { + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + } + }; + + ret_val |= es7243e_adc_init(&Wire1, &cfg); + } + + return ret_val; +} + +// box adc init +uint32_t es7210_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES7210_ADDR, 1)) { + I2cSetActiveFound(ES7210_ADDR, "ES7210-I2C", 1); + audio_hal_codec_config_t cfg = { + .adc_input = AUDIO_HAL_ADC_INPUT_ALL, + .codec_mode = AUDIO_HAL_CODEC_MODE_ENCODE, + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .fmt = AUDIO_HAL_I2S_NORMAL, + .samples = AUDIO_HAL_16K_SAMPLES, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + }, + }; + + ret_val |= es7210_adc_init(&Wire1, &cfg); + ret_val |= es7210_adc_config_i2s(cfg.codec_mode, &cfg.i2s_iface); + ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2), (es7210_gain_value_t) GAIN_37_5DB); + ret_val |= es7210_adc_set_gain((es7210_input_mics_t)(ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4), (es7210_gain_value_t) GAIN_0DB); + ret_val |= es7210_adc_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + } + return ret_val; +} + +// box dac init +uint32_t ES8311_init() { + uint32_t ret_val = ESP_OK; + + if (I2cSetDevice(ES8311_ADDR, 1)) { + I2cSetActiveFound(ES8311_ADDR, "ES8311-I2C", 1); + audio_hal_codec_config_t cfg = { + .dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1, + .codec_mode = AUDIO_HAL_CODEC_MODE_DECODE, + .i2s_iface = { + .mode = AUDIO_HAL_MODE_SLAVE, + .fmt = AUDIO_HAL_I2S_NORMAL, + .samples = AUDIO_HAL_16K_SAMPLES, + .bits = AUDIO_HAL_BIT_LENGTH_16BITS, + }, + }; + + ret_val |= es8311_codec_init(&Wire1, &cfg); + ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); + ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); + ret_val |= es8311_codec_set_voice_volume(75); + ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + + } + return ret_val; +} + +void S3boxInit() { + if (TasmotaGlobal.i2c_enabled_2) { + // box lite + ES8156_init(); + es7243e_init(); + // box full + ES8311_init(); + es7210_init(); + } +} +#endif // ESP32S3_BOX + +#ifdef USE_SHINE +#ifdef WAV2MP3 + +#include +#include + +typedef uint8_t mp3buf_t; + + +// min freq = 16 KHz Stereo or 32 KHz Mono +int32_t wav2mp3(char *path) { + int32_t error = 0; + shine_config_t config; + shine_t s = nullptr; + File wav_in = (File)nullptr; + File mp3_out = (File)nullptr; + uint8_t *ucp; + int written; + int16_t *buffer = nullptr; + uint32_t bread; + uint16_t samples_per_pass; + char mpath[64]; + char *cp; + uint8_t chans = 1; + uint32_t sfreq = 16000; + + strlcpy(mpath, path, sizeof(mpath)); + + wav_in = ufsp->open(mpath, FS_FILE_READ); + if (!wav_in) { + error = -1; + goto exit; + } + + // script>wav2mp3("/test2.wav") + uint8_t wavHeader[sizeof(wavHTemplate)]; + wav_in.read((uint8_t*)wavHeader, sizeof(wavHTemplate)); + chans = wavHeader[22]; + sfreq = wavHeader[24]|(wavHeader[25]<<8)|(wavHeader[26]<<16)|(wavHeader[27]<<24); + + cp = strchr(mpath, '.'); + if (!cp) { + error = -6; + goto exit; + } + + strcpy(cp, ".mp3"); + + mp3_out = ufsp->open(mpath, FS_FILE_WRITE); + if (!mp3_out) { + error = -2; + goto exit; + } + + shine_set_config_mpeg_defaults(&config.mpeg); + + if (chans == 1) { + config.mpeg.mode = MONO; + } else { + config.mpeg.mode = STEREO; + } + config.mpeg.bitr = 128; + config.wave.samplerate = sfreq; + config.wave.channels = (channels)chans; + + + if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) { + error = -3; + goto exit; + } + + s = shine_initialise(&config); + if (!s) { + error = -4; + goto exit; + } + + samples_per_pass = shine_samples_per_pass(s); + + + buffer = (int16_t*)malloc(samples_per_pass * 2 * chans); + if (!buffer) { + error = -5; + goto exit; + } + + AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding %d channels with freq %d Hz"), chans, sfreq); + + while (1) { + bread = wav_in.read((uint8_t*)buffer, samples_per_pass * 2 * chans); + if (!bread) { + break; + } + ucp = shine_encode_buffer_interleaved(s, buffer, &written); + mp3_out.write(ucp, written); + } + ucp = shine_flush(s, &written); + mp3_out.write(ucp, written); + +exit: + if (s) { + shine_close(s); + } + if (wav_in) { + wav_in.close(); + } + if (mp3_out) { + mp3_out.close(); + } + + if (buffer) { + free(buffer); + } + + AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding exit with code: %d"), error); + + return error; +} +#endif // WAV2MP3 +#endif // USE_SHINE + + +#endif diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino deleted file mode 100644 index 5adc20597..000000000 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_i2s_audio.ino +++ /dev/null @@ -1,884 +0,0 @@ -/* - xdrv_42_i2s_audio.ino - Audio dac support for Tasmota - - Copyright (C) 2021 Gerhard Mutz and Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) -/*********************************************************************************************\ - * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor - * - * Uses fixed GPIOs for ESP8266: - * I2S Out Data GPIO03 (Rx) - * I2S Out Bit Clock GPIO15 - * I2S Out Word Select GPIO02 - * I2S In Data GPIO12 - * I2S In Bit Clock GPIO13 - * I2S In Word Select GPIO14 -\*********************************************************************************************/ - -#define XDRV_42 42 - -#define USE_I2S_EXTERNAL_DAC 1 -//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC -//#define USE_I2S_WEBRADIO // Add support for web radio -//#define USE_I2S_SAY_TIME // Add support for english speaking clock - -#include "AudioFileSourcePROGMEM.h" -#include "AudioFileSourceID3.h" -#include "AudioGeneratorMP3.h" -#ifdef USE_I2S_NO_DAC - #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin -#else - #include "AudioOutputI2S.h" // External I2S DAC IC -#endif // USE_I2S_NO_DAC -#include -#include "AudioFileSourceFS.h" -#ifdef USE_I2S_SAY_TIME - #include "AudioGeneratorTalkie.h" -#endif // USE_I2S_SAY_TIME -#include "AudioFileSourceICYStream.h" -#include "AudioFileSourceBuffer.h" -#include "AudioGeneratorAAC.h" - -#ifdef ESP32 -#include -#endif - -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON -#define AUDIO_PWR_OFF - -#ifdef ESP8266 -#define i2s_port_t uint8_t -#endif - -struct AUDIO_I2S { - uint8_t is2_volume; // should be in settings - i2s_port_t i2s_port; - int8_t mclk = -1; - int8_t bclk = -1; - int8_t ws = -1; - int8_t dout = -1; - int8_t din = -1; - AudioGeneratorMP3 *mp3 = nullptr; - AudioFileSourceFS *file; -#ifdef USE_I2S_NO_DAC - AudioOutputI2SNoDAC *out; -#else - AudioOutputI2S *out; -#endif // USE_I2S_NO_DAC - AudioFileSourceID3 *id3; - AudioGeneratorMP3 *decoder = NULL; - void *mp3ram = NULL; -#ifdef USE_I2S_WEBRADIO - AudioFileSourceICYStream *ifile = NULL; - AudioFileSourceBuffer *buff = NULL; - char wr_title[64]; - void *preallocateBuffer = NULL; - void *preallocateCodec = NULL; - uint32_t retryms = 0; -#endif // USE_I2S_WEBRADIO - -#ifdef ESP32 - TaskHandle_t mp3_task_h; - TaskHandle_t mic_task_h; - uint32_t mic_size; - uint8_t *mic_buff; - char mic_path[32]; -#endif - -} audio_i2s; - - -// because S3 box mclk severly disturbs WLAN -// we must slow down after each sound -#ifdef ESP32S3_BOX -#undef DOWNRATE -#define DOWNRATE audio_i2s.out->SetRate(1000); -#else -#undef DOWNRATE -#define DOWNRATE -#endif - -#define MIC_CHANNELS 1 - -#ifdef USE_TTGO_WATCH -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON TTGO_audio_power(true); -#define AUDIO_PWR_OFF TTGO_audio_power(false); -#endif // USE_TTGO_WATCH - -#ifdef USE_M5STACK_CORE2 -// leave this predefined currently -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON Core2AudioPower(true); -#define AUDIO_PWR_OFF Core2AudioPower(false); -#undef DAC_IIS_BCK -#undef DAC_IIS_WS -#undef DAC_IIS_DOUT -#define DAC_IIS_BCK 12 -#define DAC_IIS_WS 0 -#define DAC_IIS_DOUT 2 -#endif // USE_M5STACK_CORE2 - - -#ifdef ESP32S3_BOX -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON S3boxAudioPower(true); -#define AUDIO_PWR_OFF S3boxAudioPower(false); -#undef MIC_CHANNELS -#define MIC_CHANNELS 2 -#endif // ESP32S3_BOX - -extern FS *ufsp; - -#ifdef ESP8266 -const int preallocateBufferSize = 5*1024; -const int preallocateCodecSize = 29192; // MP3 codec max mem needed -#endif // ESP8266 -#ifdef ESP32 -const int preallocateBufferSize = 16*1024; -const int preallocateCodecSize = 29192; // MP3 codec max mem needed -//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed -#endif // ESP32 - - -#ifdef USE_I2S_SAY_TIME -long timezone = 2; -byte daysavetime = 1; - -uint8_t spTHE[] PROGMEM = {0x08,0xE8,0x3E,0x55,0x01,0xC3,0x86,0x27,0xAF,0x72,0x0D,0x4D,0x97,0xD5,0xBC,0x64,0x3C,0xF2,0x5C,0x51,0xF1,0x93,0x36,0x8F,0x4F,0x59,0x2A,0x42,0x7A,0x32,0xC3,0x64,0xFF,0x3F}; -uint8_t spTIME[] PROGMEM = {0x0E,0x28,0xAC,0x2D,0x01,0x5D,0xB6,0x0D,0x33,0xF3,0x54,0xB3,0x60,0xBA,0x8C,0x54,0x5C,0xCD,0x2D,0xD4,0x32,0x73,0x0F,0x8E,0x34,0x33,0xCB,0x4A,0x25,0xD4,0x25,0x83,0x2C,0x2B,0xD5,0x50,0x97,0x08,0x32,0xEC,0xD4,0xDC,0x4C,0x33,0xC8,0x70,0x73,0x0F,0x33,0xCD,0x20,0xC3,0xCB,0x43,0xDD,0x3C,0xCD,0x8C,0x20,0x77,0x89,0xF4,0x94,0xB2,0xE2,0xE2,0x35,0x22,0x5D,0xD6,0x4A,0x8A,0x96,0xCC,0x36,0x25,0x2D,0xC9,0x9A,0x7B,0xC2,0x18,0x87,0x24,0x4B,0x1C,0xC9,0x50,0x19,0x92,0x2C,0x71,0x34,0x4B,0x45,0x8A,0x8B,0xC4,0x96,0xB6,0x5A,0x29,0x2A,0x92,0x5A,0xCA,0x53,0x96,0x20,0x05,0x09,0xF5,0x92,0x5D,0xBC,0xE8,0x58,0x4A,0xDD,0xAE,0x73,0xBD,0x65,0x4B,0x8D,0x78,0xCA,0x2B,0x4E,0xD8,0xD9,0xED,0x22,0x20,0x06,0x75,0x00,0x00,0x80,0xFF,0x07}; -uint8_t spIS[] PROGMEM = {0x21,0x18,0x96,0x38,0xB7,0x14,0x8D,0x60,0x3A,0xA6,0xE8,0x51,0xB4,0xDC,0x2E,0x48,0x7B,0x5A,0xF1,0x70,0x1B,0xA3,0xEC,0x09,0xC6,0xCB,0xEB,0x92,0x3D,0xA7,0x69,0x1F,0xAF,0x71,0x89,0x9C,0xA2,0xB3,0xFC,0xCA,0x35,0x72,0x9A,0xD1,0xF0,0xAB,0x12,0xB3,0x2B,0xC6,0xCD,0x4F,0xCC,0x32,0x26,0x19,0x07,0xDF,0x0B,0x8F,0xB8,0xA4,0xED,0x7C,0xCF,0x23,0x62,0x8B,0x8E,0xF1,0x23,0x0A,0x8B,0x6E,0xCB,0xCE,0xEF,0x54,0x44,0x3C,0xDC,0x08,0x60,0x0B,0x37,0x01,0x1C,0x53,0x26,0x80,0x15,0x4E,0x14,0xB0,0x54,0x2B,0x02,0xA4,0x69,0xFF,0x7F}; -uint8_t spA_M_[] PROGMEM = {0xCD,0xEF,0x86,0xAB,0x57,0x6D,0x0F,0xAF,0x71,0xAD,0x49,0x55,0x3C,0xFC,0x2E,0xC5,0xB7,0x5C,0xF1,0xF2,0x87,0x66,0xDD,0x4E,0xC5,0xC3,0xEF,0x92,0xE2,0x3A,0x65,0xB7,0xA0,0x09,0xAA,0x1B,0x97,0x54,0x82,0x2E,0x28,0x77,0x5C,0x52,0x09,0x1A,0xA3,0xB8,0x76,0x49,0x25,0x68,0x8C,0x73,0xDB,0x24,0x95,0xA0,0x32,0xA9,0x6B,0xA7,0xD9,0x82,0x26,0xA9,0x76,0x42,0xD6,0x08,0xBA,0xE1,0xE8,0x0E,0x5A,0x2B,0xEA,0x9E,0x3D,0x27,0x18,0xAD,0xA8,0x07,0xF1,0x98,0x90,0x35,0xA2,0x96,0x44,0xA3,0x5D,0x66,0x8B,0x6B,0x12,0xCD,0x32,0x85,0x25,0xC9,0x81,0x2D,0xC3,0x64,0x85,0x34,0x58,0x89,0x94,0x52,0x1C,0x52,0x2F,0x35,0xDA,0xC7,0x51,0x48,0x23,0x97,0xCC,0x2C,0x97,0x2E,0xF3,0x5C,0xF3,0xA2,0x14,0xBA,0x2C,0x48,0xCE,0xCA,0x76,0xE8,0x32,0x2F,0x34,0xB2,0xDB,0x85,0xC9,0x83,0x90,0xA8,0x2C,0x57,0x26,0x8F,0x9C,0xBD,0xA2,0x53,0xD9,0xC2,0x54,0x59,0x28,0x99,0x4B,0x2C,0x5D,0xFF,0x3F}; -uint8_t spP_M_[] PROGMEM = {0x0E,0x98,0x41,0x54,0x00,0x43,0xA0,0x05,0xAB,0x42,0x8E,0x1D,0xA3,0x15,0xEC,0x4E,0x58,0xF7,0x92,0x66,0x70,0x1B,0x66,0xDB,0x73,0x99,0xC1,0xEB,0x98,0xED,0xD6,0x25,0x25,0x6F,0x70,0x92,0xDD,0x64,0xD8,0xFC,0x61,0xD0,0x66,0x83,0xD6,0x0A,0x86,0x23,0xAB,0x69,0xDA,0x2B,0x18,0x9E,0x3D,0x37,0x69,0x9D,0xA8,0x07,0x71,0x9F,0xA0,0xBD,0xA2,0x16,0xD5,0x7C,0x54,0xF6,0x88,0x6B,0x54,0x8B,0x34,0x49,0x2D,0x29,0x49,0x3C,0x34,0x64,0xA5,0x24,0x1B,0x36,0xD7,0x72,0x13,0x92,0xA4,0xC4,0x2D,0xC3,0xB3,0x4B,0xA3,0x62,0x0F,0x2B,0x37,0x6E,0x8B,0x5A,0xD4,0x3D,0xDD,0x9A,0x2D,0x50,0x93,0xF6,0x4C,0xAA,0xB6,0xC4,0x85,0x3B,0xB2,0xB1,0xD8,0x93,0x20,0x4D,0x8F,0x24,0xFF,0x0F}; -uint8_t spOH[] PROGMEM = {0xC6,0xC9,0x71,0x5A,0xA2,0x92,0x14,0x2F,0x6E,0x97,0x9C,0x46,0x9D,0xDC,0xB0,0x4D,0x62,0x1B,0x55,0x70,0xDD,0x55,0xBE,0x0E,0x36,0xC1,0x33,0x37,0xA9,0xA7,0x51,0x1B,0xCF,0x3C,0xA5,0x9E,0x44,0xAC,0x3C,0x7D,0x98,0x7B,0x52,0x96,0x72,0x65,0x4B,0xF6,0x1A,0xD9,0xCA,0xF5,0x91,0x2D,0xA2,0x2A,0x4B,0xF7,0xFF,0x01}; -uint8_t spOCLOCK[] PROGMEM = {0x21,0x4E,0x3D,0xB8,0x2B,0x19,0xBB,0x24,0x0E,0xE5,0xEC,0x60,0xE4,0xF2,0x90,0x13,0xD4,0x2A,0x11,0x80,0x00,0x42,0x69,0x26,0x40,0xD0,0x2B,0x04,0x68,0xE0,0x4D,0x00,0x3A,0x35,0x35,0x33,0xB6,0x51,0xD9,0x64,0x34,0x82,0xB4,0x9A,0x63,0x92,0x55,0x89,0x52,0x5B,0xCA,0x2E,0x34,0x25,0x4E,0x63,0x28,0x3A,0x50,0x95,0x26,0x8D,0xE6,0xAA,0x64,0x58,0xEA,0x92,0xCE,0xC2,0x46,0x15,0x9B,0x86,0xCD,0x2A,0x2E,0x37,0x00,0x00,0x00,0x0C,0xC8,0xDD,0x05,0x01,0xB9,0x33,0x21,0xA0,0x74,0xD7,0xFF,0x07}; -uint8_t spONE[] PROGMEM = {0xCC,0x67,0x75,0x42,0x59,0x5D,0x3A,0x4F,0x9D,0x36,0x63,0xB7,0x59,0xDC,0x30,0x5B,0x5C,0x23,0x61,0xF3,0xE2,0x1C,0xF1,0xF0,0x98,0xC3,0x4B,0x7D,0x39,0xCA,0x1D,0x2C,0x2F,0xB7,0x15,0xEF,0x70,0x79,0xBC,0xD2,0x46,0x7C,0x52,0xE5,0xF1,0x4A,0x6A,0xB3,0x71,0x47,0xC3,0x2D,0x39,0x34,0x4B,0x23,0x35,0xB7,0x7A,0x55,0x33,0x8F,0x59,0xDC,0xA2,0x44,0xB5,0xBC,0x66,0x72,0x8B,0x64,0xF5,0xF6,0x98,0xC1,0x4D,0x42,0xD4,0x27,0x62,0x38,0x2F,0x4A,0xB6,0x9C,0x88,0x68,0xBC,0xA6,0x95,0xF8,0x5C,0xA1,0x09,0x86,0x77,0x91,0x11,0x5B,0xFF,0x0F}; -uint8_t spTWO[] PROGMEM = {0x0E,0x38,0x6E,0x25,0x00,0xA3,0x0D,0x3A,0xA0,0x37,0xC5,0xA0,0x05,0x9E,0x56,0x35,0x86,0xAA,0x5E,0x8C,0xA4,0x82,0xB2,0xD7,0x74,0x31,0x22,0x69,0xAD,0x1C,0xD3,0xC1,0xD0,0xFA,0x28,0x2B,0x2D,0x47,0xC3,0x1B,0xC2,0xC4,0xAE,0xC6,0xCD,0x9C,0x48,0x53,0x9A,0xFF,0x0F}; -uint8_t spTHREE[] PROGMEM = {0x02,0xD8,0x2E,0x9C,0x01,0xDB,0xA6,0x33,0x60,0xFB,0x30,0x01,0xEC,0x20,0x12,0x8C,0xE4,0xD8,0xCA,0x32,0x96,0x73,0x63,0x41,0x39,0x89,0x98,0xC1,0x4D,0x0D,0xED,0xB0,0x2A,0x05,0x37,0x0F,0xB4,0xA5,0xAE,0x5C,0xDC,0x36,0xD0,0x83,0x2F,0x4A,0x71,0x7B,0x03,0xF7,0x38,0x59,0xCD,0xED,0x1E,0xB4,0x6B,0x14,0x35,0xB7,0x6B,0x94,0x99,0x91,0xD5,0xDC,0x26,0x48,0x77,0x4B,0x66,0x71,0x1B,0x21,0xDB,0x2D,0x8A,0xC9,0x6D,0x88,0xFC,0x26,0x28,0x3A,0xB7,0x21,0xF4,0x1F,0xA3,0x65,0xBC,0x02,0x38,0xBB,0x3D,0x8E,0xF0,0x2B,0xE2,0x08,0xB7,0x34,0xFF,0x0F}; -uint8_t spFOUR[] PROGMEM = {0x0C,0x18,0xB6,0x9A,0x01,0xC3,0x75,0x09,0x60,0xD8,0x0E,0x09,0x30,0xA0,0x9B,0xB6,0xA0,0xBB,0xB0,0xAA,0x16,0x4E,0x82,0xEB,0xEA,0xA9,0xFA,0x59,0x49,0x9E,0x59,0x23,0x9A,0x27,0x3B,0x78,0x66,0xAE,0x4A,0x9C,0x9C,0xE0,0x99,0xD3,0x2A,0xBD,0x72,0x92,0xEF,0xE6,0x88,0xE4,0x45,0x4D,0x7E,0x98,0x2D,0x62,0x67,0x37,0xF9,0xA1,0x37,0xA7,0x6C,0x94,0xE4,0xC7,0x1E,0xDC,0x3C,0xA5,0x83,0x1F,0x8B,0xEB,0x52,0x0E,0x0E,0x7E,0x2E,0x4E,0xC7,0x31,0xD2,0x79,0xA5,0x3A,0x0D,0xD9,0xC4,0xFF,0x07}; -uint8_t spFIVE[] PROGMEM = {0x02,0xE8,0x3E,0x8C,0x01,0xDD,0x65,0x08,0x60,0x98,0x4C,0x06,0x34,0x93,0xCE,0x80,0xE6,0xDA,0x9A,0x14,0x6B,0xAA,0x47,0xD1,0x5E,0x56,0xAA,0x6D,0x56,0xCD,0x78,0xD9,0xA9,0x1C,0x67,0x05,0x83,0xE1,0xA4,0xBA,0x38,0xEE,0x16,0x86,0x9B,0xFA,0x60,0x87,0x5B,0x18,0x6E,0xEE,0x8B,0x1D,0x6E,0x61,0xB9,0x69,0x36,0x65,0xBA,0x8D,0xE5,0xE5,0x3E,0x1C,0xE9,0x0E,0x96,0x9B,0x5B,0xAB,0x95,0x2B,0x58,0x6E,0xCE,0xE5,0x3A,0x6A,0xF3,0xB8,0x35,0x84,0x7B,0x05,0xA3,0xE3,0x36,0xEF,0x92,0x19,0xB4,0x86,0xDB,0xB4,0x69,0xB4,0xD1,0x2A,0x4E,0x65,0x9A,0x99,0xCE,0x28,0xD9,0x85,0x71,0x4C,0x18,0x6D,0x67,0x47,0xC6,0x5E,0x53,0x4A,0x9C,0xB5,0xE2,0x85,0x45,0x26,0xFE,0x7F}; -uint8_t spSIX[] PROGMEM = {0x0E,0xD8,0xAE,0xDD,0x03,0x0E,0x38,0xA6,0xD2,0x01,0xD3,0xB4,0x2C,0xAD,0x6A,0x35,0x9D,0xB1,0x7D,0xDC,0xEE,0xC4,0x65,0xD7,0xF1,0x72,0x47,0x24,0xB3,0x19,0xD9,0xD9,0x05,0x70,0x40,0x49,0xEA,0x02,0x98,0xBE,0x42,0x01,0xDF,0xA4,0x69,0x40,0x00,0xDF,0x95,0xFC,0x3F}; -uint8_t spSEVEN[] PROGMEM = {0x02,0xB8,0x3A,0x8C,0x01,0xDF,0xA4,0x73,0x40,0x01,0x47,0xB9,0x2F,0x33,0x3B,0x73,0x5F,0x53,0x7C,0xEC,0x9A,0xC5,0x63,0xD5,0xD1,0x75,0xAE,0x5B,0xFC,0x64,0x5C,0x35,0x87,0x91,0xF1,0x83,0x36,0xB5,0x68,0x55,0xC5,0x6F,0xDA,0x45,0x2D,0x1C,0x2D,0xB7,0x38,0x37,0x9F,0x60,0x3C,0xBC,0x9A,0x85,0xA3,0x25,0x66,0xF7,0x8A,0x57,0x1C,0xA9,0x67,0x56,0xCA,0x5E,0xF0,0xB2,0x16,0xB2,0xF1,0x89,0xCE,0x8B,0x92,0x25,0xC7,0x2B,0x33,0xCF,0x48,0xB1,0x99,0xB4,0xF3,0xFF}; -uint8_t spEIGHT[] PROGMEM = {0xC3,0x6C,0x86,0xB3,0x27,0x6D,0x0F,0xA7,0x48,0x99,0x4E,0x55,0x3C,0xBC,0x22,0x65,0x36,0x4D,0xD1,0xF0,0x32,0xD3,0xBE,0x34,0xDA,0xC3,0xEB,0x82,0xE2,0xDA,0x65,0x35,0xAF,0x31,0xF2,0x6B,0x97,0x95,0xBC,0x86,0xD8,0x6F,0x82,0xA6,0x73,0x0B,0xC6,0x9E,0x72,0x99,0xCC,0xCB,0x02,0xAD,0x3C,0x9A,0x10,0x60,0xAB,0x62,0x05,0x2C,0x37,0x84,0x00,0xA9,0x73,0x00,0x00,0xFE,0x1F}; -uint8_t spNINE[] PROGMEM = {0xCC,0xA1,0x26,0xBB,0x83,0x93,0x18,0xCF,0x4A,0xAD,0x2E,0x31,0xED,0x3C,0xA7,0x24,0x26,0xC3,0x54,0xF1,0x92,0x64,0x8B,0x8A,0x98,0xCB,0x2B,0x2E,0x34,0x53,0x2D,0x0E,0x2F,0x57,0xB3,0x0C,0x0D,0x3C,0xBC,0x3C,0x4C,0x4B,0xCA,0xF4,0xF0,0x72,0x0F,0x6E,0x49,0x53,0xCD,0xCB,0x53,0x2D,0x35,0x4D,0x0F,0x2F,0x0F,0xD7,0x0C,0x0D,0x3D,0xBC,0xDC,0x4D,0xD3,0xDD,0xC2,0xF0,0x72,0x52,0x4F,0x57,0x9B,0xC3,0xAB,0x89,0xBD,0x42,0x2D,0x0F,0xAF,0x5A,0xD1,0x71,0x91,0x55,0xBC,0x2C,0xC5,0x3B,0xD8,0x65,0xF2,0x82,0x94,0x18,0x4E,0x3B,0xC1,0x73,0x42,0x32,0x33,0x15,0x45,0x4F,0x79,0x52,0x6A,0x55,0xA6,0xA3,0xFF,0x07}; -uint8_t spTEN[] PROGMEM = {0x0E,0xD8,0xB1,0xDD,0x01,0x3D,0xA8,0x24,0x7B,0x04,0x27,0x76,0x77,0xDC,0xEC,0xC2,0xC5,0x23,0x84,0xCD,0x72,0x9A,0x51,0xF7,0x62,0x45,0xC7,0xEB,0x4E,0x35,0x4A,0x14,0x2D,0xBF,0x45,0xB6,0x0A,0x75,0xB8,0xFC,0x16,0xD9,0x2A,0xD9,0xD6,0x0A,0x5A,0x10,0xCD,0xA2,0x48,0x23,0xA8,0x81,0x35,0x4B,0x2C,0xA7,0x20,0x69,0x0A,0xAF,0xB6,0x15,0x82,0xA4,0x29,0x3C,0xC7,0x52,0x08,0xA2,0x22,0xCF,0x68,0x4B,0x2E,0xF0,0x8A,0xBD,0xA3,0x2C,0xAB,0x40,0x1B,0xCE,0xAA,0xB2,0x6C,0x82,0x40,0x4D,0x7D,0xC2,0x89,0x88,0x8A,0x61,0xCC,0x74,0xD5,0xFF,0x0F}; -uint8_t spELEVEN[] PROGMEM = {0xC3,0xCD,0x76,0x5C,0xAE,0x14,0x0F,0x37,0x9B,0x71,0xDE,0x92,0x55,0xBC,0x2C,0x27,0x70,0xD3,0x76,0xF0,0x83,0x5E,0xA3,0x5E,0x5A,0xC1,0xF7,0x61,0x58,0xA7,0x19,0x35,0x3F,0x99,0x31,0xDE,0x52,0x74,0xFC,0xA2,0x26,0x64,0x4B,0xD1,0xF1,0xAB,0xAE,0xD0,0x2D,0xC5,0xC7,0x2F,0x36,0xDD,0x27,0x15,0x0F,0x3F,0xD9,0x08,0x9F,0x62,0xE4,0xC2,0x2C,0xD4,0xD8,0xD3,0x89,0x0B,0x1B,0x57,0x11,0x0B,0x3B,0xC5,0xCF,0xD6,0xCC,0xC6,0x64,0x35,0xAF,0x18,0x73,0x1F,0xA1,0x5D,0xBC,0x62,0x45,0xB3,0x45,0x51,0xF0,0xA2,0x62,0xAB,0x4A,0x5B,0xC9,0x4B,0x8A,0x2D,0xB3,0x6C,0x06,0x2F,0x29,0xB2,0xAC,0x8A,0x18,0xBC,0x28,0xD9,0xAA,0xD2,0x92,0xF1,0xBC,0xE0,0x98,0x8C,0x48,0xCC,0x17,0x52,0xA3,0x27,0x6D,0x93,0xD0,0x4B,0x8E,0x0E,0x77,0x02,0x00,0xFF,0x0F}; -uint8_t spTWELVE[] PROGMEM = {0x06,0x28,0x46,0xD3,0x01,0x25,0x06,0x13,0x20,0xBA,0x70,0x70,0xB6,0x79,0xCA,0x36,0xAE,0x28,0x38,0xE1,0x29,0xC5,0x35,0xA3,0xE6,0xC4,0x16,0x6A,0x53,0x8C,0x97,0x9B,0x72,0x86,0x4F,0x28,0x1A,0x6E,0x0A,0x59,0x36,0xAE,0x68,0xF8,0x29,0x67,0xFA,0x06,0xA3,0x16,0xC4,0x96,0xE6,0x53,0xAC,0x5A,0x9C,0x56,0x72,0x77,0x31,0x4E,0x49,0x5C,0x8D,0x5B,0x29,0x3B,0x24,0x61,0x1E,0x6C,0x9B,0x6C,0x97,0xF8,0xA7,0x34,0x19,0x92,0x4C,0x62,0x9E,0x72,0x65,0x58,0x12,0xB1,0x7E,0x09,0xD5,0x2E,0x53,0xC5,0xBA,0x36,0x6B,0xB9,0x2D,0x17,0x05,0xEE,0x9A,0x6E,0x8E,0x05,0x50,0x6C,0x19,0x07,0x18,0x50,0xBD,0x3B,0x01,0x92,0x08,0x41,0x40,0x10,0xA6,0xFF,0x0F}; -uint8_t spTHIRTEEN[] PROGMEM = {0x08,0xE8,0x2C,0x15,0x01,0x43,0x07,0x13,0xE0,0x98,0xB4,0xA6,0x35,0xA9,0x1E,0xDE,0x56,0x8E,0x53,0x9C,0x7A,0xE7,0xCA,0x5E,0x76,0x8D,0x94,0xE5,0x2B,0xAB,0xD9,0xB5,0x62,0xA4,0x9C,0xE4,0xE6,0xB4,0x41,0x1E,0x7C,0xB6,0x93,0xD7,0x16,0x99,0x5A,0xCD,0x61,0x76,0x55,0xC2,0x91,0x61,0x1B,0xC0,0x01,0x5D,0x85,0x05,0xE0,0x68,0x51,0x07,0x1C,0xA9,0x64,0x80,0x1D,0x4C,0x9C,0x95,0x88,0xD4,0x04,0x3B,0x4D,0x4E,0x21,0x5C,0x93,0xA8,0x26,0xB9,0x05,0x4B,0x6E,0xA0,0xE2,0xE4,0x57,0xC2,0xB9,0xC1,0xB2,0x93,0x5F,0x09,0xD7,0x24,0xCB,0x4E,0x41,0x25,0x54,0x1D,0x62,0x3B,0x05,0x8D,0x52,0x57,0xAA,0xAD,0x10,0x24,0x26,0xE3,0xE1,0x36,0x5D,0x10,0x85,0xB4,0x97,0x85,0x72,0x41,0x14,0x52,0x5E,0x1A,0xCA,0xF9,0x91,0x6B,0x7A,0x5B,0xC4,0xE0,0x17,0x2D,0x54,0x1D,0x92,0x8C,0x1F,0x25,0x4B,0x8F,0xB2,0x16,0x41,0xA1,0x4A,0x3E,0xE6,0xFA,0xFF,0x01}; -uint8_t spFOURTEEN[] PROGMEM = {0x0C,0x58,0xAE,0x5C,0x01,0xD9,0x87,0x07,0x51,0xB7,0x25,0xB3,0x8A,0x15,0x2C,0xF7,0x1C,0x35,0x87,0x4D,0xB2,0xDD,0x53,0xCE,0x28,0x2B,0xC9,0x0E,0x97,0x2D,0xBD,0x2A,0x17,0x27,0x76,0x8E,0xD2,0x9A,0x6C,0x80,0x94,0x71,0x00,0x00,0x02,0xB0,0x58,0x58,0x00,0x9E,0x0B,0x0A,0xC0,0xB2,0xCE,0xC1,0xC8,0x98,0x7A,0x52,0x95,0x24,0x2B,0x11,0xED,0x36,0xD4,0x92,0xDC,0x4C,0xB5,0xC7,0xC8,0x53,0xF1,0x2A,0xE5,0x1A,0x17,0x55,0xC5,0xAF,0x94,0xBB,0xCD,0x1C,0x26,0xBF,0x52,0x9A,0x72,0x53,0x98,0xFC,0xC2,0x68,0xD2,0x4D,0x61,0xF0,0xA3,0x90,0xB6,0xD6,0x50,0xC1,0x8F,0x42,0xDA,0x4A,0x43,0x39,0x3F,0x48,0x2D,0x6B,0x33,0xF9,0xFF}; -uint8_t spFIFTEEN[] PROGMEM = {0x08,0xE8,0x2A,0x0D,0x01,0xDD,0xBA,0x31,0x60,0x6A,0xF7,0xA0,0xAE,0x54,0xAA,0x5A,0x76,0x97,0xD9,0x34,0x69,0xEF,0x32,0x1E,0x66,0xE1,0xE2,0xB3,0x43,0xA9,0x18,0x55,0x92,0x4E,0x37,0x2D,0x67,0x6F,0xDF,0xA2,0x5A,0xB6,0x04,0x30,0x55,0xA8,0x00,0x86,0x09,0xE7,0x00,0x01,0x16,0x17,0x05,0x70,0x40,0x57,0xE5,0x01,0xF8,0x21,0x34,0x00,0xD3,0x19,0x33,0x80,0x89,0x9A,0x62,0x34,0x4C,0xD5,0x49,0xAE,0x8B,0x53,0x09,0xF7,0x26,0xD9,0x6A,0x7E,0x23,0x5C,0x13,0x12,0xB3,0x04,0x9D,0x50,0x4F,0xB1,0xAD,0x14,0x15,0xC2,0xD3,0xA1,0xB6,0x42,0x94,0xA8,0x8C,0x87,0xDB,0x74,0xB1,0x70,0x59,0xE1,0x2E,0xC9,0xC5,0x81,0x5B,0x55,0xA4,0x4C,0x17,0x47,0xC1,0x6D,0xE3,0x81,0x53,0x9C,0x84,0x6A,0x46,0xD9,0x4C,0x51,0x31,0x42,0xD9,0x66,0xC9,0x44,0x85,0x29,0x6A,0x9B,0xAD,0xFF,0x07}; -uint8_t spSIXTEEN[] PROGMEM = {0x0A,0x58,0x5A,0x5D,0x00,0x93,0x97,0x0B,0x60,0xA9,0x48,0x05,0x0C,0x15,0xAE,0x80,0xAD,0x3D,0x14,0x30,0x7D,0xD9,0x50,0x92,0x92,0xAC,0x0D,0xC5,0xCD,0x2A,0x82,0xAA,0x3B,0x98,0x04,0xB3,0x4A,0xC8,0x9A,0x90,0x05,0x09,0x68,0x51,0xD4,0x01,0x23,0x9F,0x1A,0x60,0xA9,0x12,0x03,0xDC,0x50,0x81,0x80,0x22,0xDC,0x20,0x00,0xCB,0x06,0x3A,0x60,0x16,0xE3,0x64,0x64,0x42,0xDD,0xCD,0x6A,0x8A,0x5D,0x28,0x75,0x07,0xA9,0x2A,0x5E,0x65,0x34,0xED,0x64,0xBB,0xF8,0x85,0xF2,0x94,0x8B,0xAD,0xE4,0x37,0x4A,0x5B,0x21,0xB6,0x52,0x50,0x19,0xAD,0xA7,0xD8,0x4A,0x41,0x14,0xDA,0x5E,0x12,0x3A,0x04,0x91,0x4B,0x7B,0x69,0xA8,0x10,0x24,0x2E,0xE5,0xA3,0x81,0x52,0x90,0x94,0x5A,0x55,0x98,0x32,0x41,0x50,0xCC,0x93,0x2E,0x47,0x85,0x89,0x1B,0x5B,0x5A,0x62,0x04,0x44,0xE3,0x02,0x80,0x80,0x64,0xDD,0xFF,0x1F}; -uint8_t spSEVENTEEN[] PROGMEM = {0x02,0x98,0x3A,0x42,0x00,0x5B,0xA6,0x09,0x60,0xDB,0x52,0x06,0x1C,0x93,0x29,0x80,0xA9,0x52,0x87,0x9A,0xB5,0x99,0x4F,0xC8,0x3E,0x46,0xD6,0x5E,0x7E,0x66,0xFB,0x98,0xC5,0x5A,0xC6,0x9A,0x9C,0x63,0x15,0x6B,0x11,0x13,0x8A,0x9C,0x97,0xB9,0x9A,0x5A,0x39,0x71,0xEE,0xD2,0x29,0xC2,0xA6,0xB8,0x58,0x59,0x99,0x56,0x14,0xA3,0xE1,0x26,0x19,0x19,0xE3,0x8C,0x93,0x17,0xB4,0x46,0xB5,0x88,0x71,0x9E,0x97,0x9E,0xB1,0x2C,0xC5,0xF8,0x56,0xC4,0x58,0xA3,0x1C,0xE1,0x33,0x9D,0x13,0x41,0x8A,0x43,0x58,0xAD,0x95,0xA9,0xDB,0x36,0xC0,0xD1,0xC9,0x0E,0x58,0x4E,0x45,0x01,0x23,0xA9,0x04,0x37,0x13,0xAE,0x4D,0x65,0x52,0x82,0xCA,0xA9,0x37,0x99,0x4D,0x89,0xBA,0xC0,0xBC,0x14,0x36,0x25,0xEA,0x1C,0x73,0x52,0x1D,0x97,0xB8,0x33,0xAC,0x0E,0x75,0x9C,0xE2,0xCE,0xB0,0xDA,0xC3,0x51,0x4A,0x1A,0xA5,0xCA,0x70,0x5B,0x21,0xCE,0x4C,0x26,0xD2,0x6C,0xBA,0x38,0x71,0x2E,0x1F,0x2D,0xED,0xE2,0x24,0xB8,0xBC,0x3D,0x52,0x88,0xAB,0x50,0x8E,0xA8,0x48,0x22,0x4E,0x42,0xA0,0x26,0x55,0xFD,0x3F}; -uint8_t spEIGHTEEN[] PROGMEM = {0x2E,0x9C,0xD1,0x4D,0x54,0xEC,0x2C,0xBF,0x1B,0x8A,0x99,0x70,0x7C,0xFC,0x2E,0x29,0x6F,0x52,0xF6,0xF1,0xBA,0x20,0xBF,0x36,0xD9,0xCD,0xED,0x0C,0xF3,0x27,0x64,0x17,0x73,0x2B,0xA2,0x99,0x90,0x65,0xEC,0xED,0x40,0x73,0x32,0x12,0xB1,0xAF,0x30,0x35,0x0B,0xC7,0x00,0xE0,0x80,0xAE,0xDD,0x1C,0x70,0x43,0xAA,0x03,0x86,0x51,0x36,0xC0,0x30,0x64,0xCE,0x4C,0x98,0xFB,0x5C,0x65,0x07,0xAF,0x10,0xEA,0x0B,0x66,0x1B,0xFC,0x46,0xA8,0x3E,0x09,0x4D,0x08,0x2A,0xA6,0x3E,0x67,0x36,0x21,0x2A,0x98,0x67,0x9D,0x15,0xA7,0xA8,0x60,0xEE,0xB6,0x94,0x99,0xA2,0x4A,0x78,0x22,0xC2,0xA6,0x8B,0x8C,0x8E,0xCC,0x4C,0x8A,0x2E,0x8A,0x4C,0xD3,0x57,0x03,0x87,0x28,0x71,0x09,0x1F,0x2B,0xE4,0xA2,0xC4,0xC5,0x6D,0xAD,0x54,0x88,0xB2,0x63,0xC9,0xF2,0x50,0x2E,0x8A,0x4A,0x38,0x4A,0xEC,0x88,0x28,0x08,0xE3,0x28,0x49,0xF3,0xFF}; -uint8_t spNINETEEN[] PROGMEM = {0xC2,0xEA,0x8A,0x95,0x2B,0x6A,0x05,0x3F,0x71,0x71,0x5F,0x0D,0x12,0xFC,0x28,0x25,0x62,0x35,0xF0,0xF0,0xB3,0x48,0x1E,0x0F,0xC9,0xCB,0x2F,0x45,0x7C,0x2C,0x25,0x1F,0xBF,0x14,0xB3,0x2C,0xB5,0x75,0xFC,0x5A,0x5C,0xA3,0x5D,0xE1,0xF1,0x7A,0x76,0xB3,0x4E,0x45,0xC7,0xED,0x96,0x23,0x3B,0x18,0x37,0x7B,0x18,0xCC,0x09,0x51,0x13,0x4C,0xAB,0x6C,0x4C,0x4B,0x96,0xD2,0x49,0xAA,0x36,0x0B,0xC5,0xC2,0x20,0x26,0x27,0x35,0x63,0x09,0x3D,0x30,0x8B,0xF0,0x48,0x5C,0xCA,0x61,0xDD,0xCB,0xCD,0x91,0x03,0x8E,0x4B,0x76,0xC0,0xCC,0x4D,0x06,0x98,0x31,0x31,0x98,0x99,0x70,0x6D,0x2A,0xA3,0xE4,0x16,0xCA,0xBD,0xCE,0x5C,0x92,0x57,0x28,0xCF,0x09,0x69,0x2E,0x7E,0xA5,0x3C,0x63,0xA2,0x30,0x05,0x95,0xD2,0x74,0x98,0xCD,0x14,0x54,0xCA,0x53,0xA9,0x96,0x52,0x50,0x28,0x6F,0xBA,0xCB,0x0C,0x41,0x50,0xDE,0x65,0x2E,0xD3,0x05,0x89,0x4B,0x7B,0x6B,0x20,0x17,0x44,0xAE,0xED,0x23,0x81,0x52,0x90,0x85,0x73,0x57,0xD0,0x72,0x41,0xB1,0x02,0xDE,0x2E,0xDB,0x04,0x89,0x05,0x79,0xBB,0x62,0xE5,0x76,0x11,0xCA,0x61,0x0E,0xFF,0x1F}; -uint8_t spTWENTY[] PROGMEM = {0x01,0x98,0xD1,0xC2,0x00,0xCD,0xA4,0x32,0x20,0x79,0x13,0x04,0x28,0xE7,0x92,0xDC,0x70,0xCC,0x5D,0xDB,0x76,0xF3,0xD2,0x32,0x0B,0x0B,0x5B,0xC3,0x2B,0xCD,0xD4,0xDD,0x23,0x35,0xAF,0x44,0xE1,0xF0,0xB0,0x6D,0x3C,0xA9,0xAD,0x3D,0x35,0x0E,0xF1,0x0C,0x8B,0x28,0xF7,0x34,0x01,0x68,0x22,0xCD,0x00,0xC7,0xA4,0x04,0xBB,0x32,0xD6,0xAC,0x56,0x9C,0xDC,0xCA,0x28,0x66,0x53,0x51,0x70,0x2B,0xA5,0xBC,0x0D,0x9A,0xC1,0xEB,0x14,0x73,0x37,0x29,0x19,0xAF,0x33,0x8C,0x3B,0xA7,0x24,0xBC,0x42,0xB0,0xB7,0x59,0x09,0x09,0x3C,0x96,0xE9,0xF4,0x58,0xFF,0x0F}; -uint8_t spTHIRTY[] PROGMEM = {0x08,0x98,0xD6,0x15,0x01,0x43,0xBB,0x0A,0x20,0x1B,0x8B,0xE5,0x16,0xA3,0x1E,0xB6,0xB6,0x96,0x97,0x3C,0x57,0xD4,0x2A,0x5E,0x7E,0x4E,0xD8,0xE1,0x6B,0x7B,0xF8,0x39,0x63,0x0D,0x9F,0x95,0xE1,0xE7,0x4C,0x76,0xBC,0x91,0x5B,0x90,0x13,0xC6,0x68,0x57,0x4E,0x41,0x8B,0x10,0x5E,0x1D,0xA9,0x44,0xD3,0xBA,0x47,0xB8,0xDD,0xE4,0x35,0x86,0x11,0x93,0x94,0x92,0x5F,0x29,0xC7,0x4C,0x30,0x0C,0x41,0xC5,0x1C,0x3B,0x2E,0xD3,0x05,0x15,0x53,0x6C,0x07,0x4D,0x15,0x14,0x8C,0xB5,0xC9,0x6A,0x44,0x90,0x10,0x4E,0x9A,0xB6,0x21,0x81,0x23,0x3A,0x91,0x91,0xE8,0xFF,0x01}; -uint8_t spFOURTY[] PROGMEM = {0x04,0x18,0xB6,0x4C,0x00,0xC3,0x56,0x30,0xA0,0xE8,0xF4,0xA0,0x98,0x99,0x62,0x91,0xAE,0x83,0x6B,0x77,0x89,0x78,0x3B,0x09,0xAE,0xBD,0xA6,0x1E,0x63,0x3B,0x79,0x7E,0x71,0x5A,0x8F,0x95,0xE6,0xA5,0x4A,0x69,0xB9,0x4E,0x8A,0x5F,0x12,0x56,0xE4,0x58,0x69,0xE1,0x36,0xA1,0x69,0x2E,0x2B,0xF9,0x95,0x93,0x55,0x17,0xED,0xE4,0x37,0xC6,0xBA,0x93,0xB2,0x92,0xDF,0x19,0xD9,0x6E,0xC8,0x0A,0xFE,0x60,0xE8,0x37,0x21,0xC9,0xF9,0x8D,0x61,0x5F,0x32,0x13,0xE7,0x17,0x4C,0xD3,0xC6,0xB1,0x94,0x97,0x10,0x8F,0x8B,0xAD,0x11,0x7E,0xA1,0x9A,0x26,0x92,0xF6,0xFF,0x01}; -uint8_t spFIFTY[] PROGMEM = {0x08,0xE8,0x2E,0x84,0x00,0x23,0x84,0x13,0x60,0x38,0x95,0xA5,0x0F,0xCF,0xE2,0x79,0x8A,0x8F,0x37,0x02,0xB3,0xD5,0x2A,0x6E,0x5E,0x93,0x94,0x79,0x45,0xD9,0x05,0x5D,0x0A,0xB9,0x97,0x63,0x02,0x74,0xA7,0x82,0x80,0xEE,0xC3,0x10,0xD0,0x7D,0x28,0x03,0x6E,0x14,0x06,0x70,0xE6,0x0A,0xC9,0x9A,0x4E,0x37,0xD9,0x95,0x51,0xCE,0xBA,0xA2,0x14,0x0C,0x81,0x36,0x1B,0xB2,0x5C,0x30,0x38,0xFA,0x9C,0xC9,0x32,0x41,0xA7,0x18,0x3B,0xA2,0x48,0x04,0x05,0x51,0x4F,0x91,0x6D,0x12,0x04,0x20,0x9B,0x61,0x89,0xFF,0x1F}; -uint8_t spGOOD[] PROGMEM = {0x0A,0x28,0xCD,0x34,0x20,0xD9,0x1A,0x45,0x74,0xE4,0x66,0x24,0xAD,0xBA,0xB1,0x8C,0x9B,0x91,0xA5,0x64,0xE6,0x98,0x21,0x16,0x0B,0x96,0x9B,0x4C,0xE5,0xFF,0x01}; -uint8_t spMORNING[] PROGMEM = {0xCE,0x08,0x52,0x2A,0x35,0x5D,0x39,0x53,0x29,0x5B,0xB7,0x0A,0x15,0x0C,0xEE,0x2A,0x42,0x56,0x66,0xD2,0x55,0x2E,0x37,0x2F,0xD9,0x45,0xB3,0xD3,0xC5,0xCA,0x6D,0x27,0xD5,0xEE,0x50,0xF5,0x50,0x94,0x14,0x77,0x2D,0xD8,0x5D,0x49,0x92,0xFD,0xB1,0x64,0x2F,0xA9,0x49,0x0C,0x93,0x4B,0xAD,0x19,0x17,0x3E,0x66,0x1E,0xF1,0xA2,0x5B,0x84,0xE2,0x29,0x8F,0x8B,0x72,0x10,0xB5,0xB1,0x2E,0x4B,0xD4,0x45,0x89,0x4A,0xEC,0x5C,0x95,0x14,0x2B,0x8A,0x9C,0x34,0x52,0x5D,0xBC,0xCC,0xB5,0x3B,0x49,0x69,0x89,0x87,0xC1,0x98,0x56,0x3A,0x21,0x2B,0x82,0x67,0xCC,0x5C,0x85,0xB5,0x4A,0x8A,0xF6,0x64,0xA9,0x96,0xC4,0x69,0x3C,0x52,0x81,0x58,0x1C,0x97,0xF6,0x0E,0x1B,0xCC,0x0D,0x42,0x32,0xAA,0x65,0x12,0x67,0xD4,0x6A,0x61,0x52,0xFC,0xFF}; -uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x9D,0x28,0x71,0xB4,0xAC,0x9C,0x74,0x5A,0x42,0x55,0x33,0xB2,0x93,0x0A,0x09,0xD4,0xC5,0x9A,0xD6,0x44,0x45,0xE3,0x38,0x60,0x9A,0x32,0x05,0xF4,0x18,0x01,0x09,0xD8,0xA9,0xC2,0x00,0x5E,0xCA,0x24,0xD5,0x5B,0x9D,0x4A,0x95,0xEA,0x34,0xEE,0x63,0x92,0x5C,0x4D,0xD0,0xA4,0xEE,0x58,0x0C,0xB9,0x4D,0xCD,0x42,0xA2,0x3A,0x24,0x37,0x25,0x8A,0xA8,0x8E,0xA0,0x53,0xE4,0x28,0x23,0x26,0x13,0x72,0x91,0xA2,0x76,0xBB,0x72,0x38,0x45,0x0A,0x46,0x63,0xCA,0x69,0x27,0x39,0x58,0xB1,0x8D,0x60,0x1C,0x34,0x1B,0x34,0xC3,0x55,0x8E,0x73,0x45,0x2D,0x4F,0x4A,0x3A,0x26,0x10,0xA1,0xCA,0x2D,0xE9,0x98,0x24,0x0A,0x1E,0x6D,0x97,0x29,0xD2,0xCC,0x71,0xA2,0xDC,0x86,0xC8,0x12,0xA7,0x8E,0x08,0x85,0x22,0x8D,0x9C,0x43,0xA7,0x12,0xB2,0x2E,0x50,0x09,0xEF,0x51,0xC5,0xBA,0x28,0x58,0xAD,0xDB,0xE1,0xFF,0x03}; -uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; -uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; - -void sayTime(int hour, int minutes) ; - -void sayTime(int hour, int minutes) { -AudioGeneratorTalkie *talkie = nullptr; - - if (!audio_i2s.out) return; - - AUDIO_PWR_ON - talkie = new AudioGeneratorTalkie(); - talkie->begin(nullptr, audio_i2s.out); - - bool pm = (hour >= 12); - uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, - spSEVEN, spEIGHT, spNINE, spTEN, spELEVEN }; - size_t spHourLen[] = { sizeof(spTWELVE), sizeof(spONE), sizeof(spTWO), - sizeof(spTHREE), sizeof(spFOUR), sizeof(spFIVE), - sizeof(spSIX), sizeof(spSEVEN), sizeof(spEIGHT), - sizeof(spNINE), sizeof(spTEN), sizeof(spELEVEN) }; - uint8_t *spMinDec[] = { spOH, spTEN, spTWENTY, spTHIRTY, spFOURTY, spFIFTY }; - size_t spMinDecLen[] = { sizeof(spOH), sizeof(spTEN), sizeof(spTWENTY), - sizeof(spTHIRTY), sizeof(spFOURTY), sizeof(spFIFTY) }; - uint8_t *spMinSpecial[] = { spELEVEN, spTWELVE, spTHIRTEEN, spFOURTEEN, - spFIFTEEN, spSIXTEEN, spSEVENTEEN, spEIGHTEEN, - spNINETEEN }; - size_t spMinSpecialLen[] = { sizeof(spELEVEN), sizeof(spTWELVE), - sizeof(spTHIRTEEN), sizeof(spFOURTEEN), - sizeof(spFIFTEEN), sizeof(spSIXTEEN), - sizeof(spSEVENTEEN), sizeof(spEIGHTEEN), - sizeof(spNINETEEN) }; - uint8_t *spMinLow[] = { spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, - spSEVEN, spEIGHT, spNINE }; - size_t spMinLowLen[] = { sizeof(spONE), sizeof(spTWO), sizeof(spTHREE), - sizeof(spFOUR), sizeof(spFIVE), sizeof(spSIX), - sizeof(spSEVEN), sizeof(spEIGHT), sizeof(spNINE) }; - - talkie->say(spTHE, sizeof(spTHE)); - talkie->say(spTIME, sizeof(spTIME)); - talkie->say(spIS, sizeof(spIS)); - - hour = hour % 12; - talkie->say(spHour[hour], spHourLen[hour]); - if (minutes==0) { - talkie->say(spOCLOCK, sizeof(spOCLOCK)); - } else if (minutes<=10 || minutes >=20) { - talkie->say(spMinDec[minutes / 10], spMinDecLen[minutes /10]); - if (minutes % 10) { - talkie->say(spMinLow[(minutes % 10) - 1], spMinLowLen[(minutes % 10) - 1]); - } - } else { - talkie->say(spMinSpecial[minutes - 11], spMinSpecialLen[minutes - 11]); - } - if (pm) { - talkie->say(spP_M_, sizeof(spP_M_)); - } else { - talkie->say(spA_M_, sizeof(spA_M_)); - } - delete talkie; - audio_i2s.out->stop(); - DOWNRATE - AUDIO_PWR_OFF -} -#endif // USE_I2S_SAY_TIME - - -enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; -enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; - -#ifdef ESP8266 -#define I2S_MCLK_MULTIPLE_128 0 -#endif - -int32_t I2S_Init_0(void) { - - audio_i2s.i2s_port = (i2s_port_t)0; - -#if USE_I2S_EXTERNAL_DAC - // use i2s -#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT)) - audio_i2s.i2s_port = (i2s_port_t)0; -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - audio_i2s.out = new AudioOutputI2S(); -#endif - audio_i2s.bclk = DAC_IIS_BCK; - audio_i2s.ws = DAC_IIS_WS; - audio_i2s.dout = DAC_IIS_DOUT; -#else -#ifdef USE_I2S_NO_DAC - if (PinUsed(GPIO_I2S_DOUT)) { -#else - if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) { -#endif // USE_I2S_NO_DAC - audio_i2s.i2s_port = (i2s_port_t)0; - #ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); - #else - //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); - #endif // USE_I2S_NO_DAC - audio_i2s.mclk = Pin(GPIO_I2S_MCLK); - audio_i2s.bclk = Pin(GPIO_I2S_BCLK); - audio_i2s.ws = Pin(GPIO_I2S_WS); - audio_i2s.dout = Pin(GPIO_I2S_DOUT); - audio_i2s.din = Pin(GPIO_I2S_DIN); - } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT), 1) { - audio_i2s.i2s_port = (i2s_port_t)1; - #ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); - #else - //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); - #endif // USE_I2S_NO_DAC - audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); - audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); - audio_i2s.ws = Pin(GPIO_I2S_WS, 1); - audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); - audio_i2s.din = Pin(GPIO_I2S_DIN, 1); - } else { - return -1; - } -#ifdef ESP8266 - // esp8266 have fixed pins - if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) { - return -2; - } -#endif // ESP8266 -#endif // defined(DAC_IIS_BCK) - - audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); - - AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); - - -#else - -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0 -#endif // USE_I2S_NO_DAC -#endif // USE_I2S_EXTERNAL_DAC - - return 0; -} - - - -void I2S_Init(void) { - - #if defined(ESP32) && defined(ESP32S3_BOX) - S3boxInit(); - #endif - - if (I2S_Init_0()) { - return; - } - - DOWNRATE - audio_i2s.is2_volume=10; - audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); - audio_i2s.out->stop(); - audio_i2s.mp3ram = nullptr; - -#ifdef ESP32 - if (UsePSRAM()) { - audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - } - -#ifdef USE_I2S_WEBRADIO - if (UsePSRAM()) { - audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - } else { - audio_i2s.preallocateBuffer = malloc(preallocateBufferSize); - audio_i2s.preallocateCodec = malloc(preallocateCodecSize); - } - if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) { - //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); - } -#endif // USE_I2S_WEBRADIO -#endif // ESP32 -} - - -#ifdef ESP32 -#define MODE_MIC 0 -#define MODE_SPK 1 -//#define MICSRATE 44100 -#define MICSRATE 16000 - -uint32_t SpeakerMic(uint8_t spkr) { - esp_err_t err = ESP_OK; - - if (audio_i2s.out) { - audio_i2s.out->stop(); - delete audio_i2s.out; - audio_i2s.out = nullptr; - } - - i2s_driver_uninstall(audio_i2s.i2s_port); - - if (spkr == MODE_SPK) { - I2S_Init_0(); - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); - audio_i2s.out->stop(); - DOWNRATE - } else { - // config mic - i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER), - .sample_rate = MICSRATE, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, - .communication_format = I2S_COMM_FORMAT_I2S, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 2, - //.dma_buf_len = 128, - .dma_buf_len = 1024, - .use_apll = 0, // Use audio PLL - .tx_desc_auto_clear = true, - .fixed_mclk = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 - .bits_per_chan = I2S_BITS_PER_CHAN_16BIT - }; - -#ifdef ESP32S3_BOX - i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); - i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; -#endif - -#ifdef USE_I2S_MIC - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); - // mic select to GND - i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; - i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; -#endif - -#ifdef USE_M5STACK_CORE2 - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); -#endif - - err += i2s_driver_install(audio_i2s.i2s_port, &i2s_config, 0, NULL); - - i2s_pin_config_t tx_pin_config; -#ifdef ESP32S3_BOX - tx_pin_config.mck_io_num = audio_i2s.mclk; -#else - tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE; -#endif - tx_pin_config.bck_io_num = audio_i2s.bclk; - tx_pin_config.ws_io_num = audio_i2s.ws; - tx_pin_config.data_out_num = audio_i2s.dout; - tx_pin_config.data_in_num = audio_i2s.din; - - err += i2s_set_pin(audio_i2s.i2s_port, &tx_pin_config); -#ifdef ESP32S3_BOX - err += i2s_set_clk(audio_i2s.i2s_port, MICSRATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); -#else - err += i2s_set_clk(audio_i2s.i2s_port, MICSRATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); -#endif - } - return err; -} - -#define DATA_SIZE 1024 - -void mic_task(void *arg){ - uint32_t data_offset = 0; - while (1) { - uint32_t bytes_read; - i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); - if (bytes_read != DATA_SIZE) break; - data_offset += DATA_SIZE; - if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; - } - SpeakerMic(MODE_SPK); - SaveWav(audio_i2s.mic_path, audio_i2s.mic_buff, audio_i2s.mic_size); - free(audio_i2s.mic_buff); - vTaskDelete(audio_i2s.mic_task_h); -} - -uint32_t i2s_record(char *path, uint32_t secs) { - esp_err_t err = ESP_OK; - - if (audio_i2s.decoder || audio_i2s.mp3) return 0; - - err = SpeakerMic(MODE_MIC); - if (err) { - SpeakerMic(MODE_SPK); - return err; - } - - audio_i2s.mic_size = secs * MICSRATE * 2 * MIC_CHANNELS; - - audio_i2s.mic_buff = (uint8_t*)heap_caps_malloc(audio_i2s.mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (!audio_i2s.mic_buff) return 2; - - if (*path=='+') { - path++; - strlcpy(audio_i2s.mic_path, path , sizeof(audio_i2s.mic_path)); - xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); - return 0; - } - - uint32_t data_offset = 0; - uint32_t stime=millis(); - while (1) { - uint32_t bytes_read; - i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); - if (bytes_read != DATA_SIZE) break; - data_offset += DATA_SIZE; - if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; - delay(0); - } - //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime); - SpeakerMic(MODE_SPK); - // save to path - SaveWav(path, audio_i2s.mic_buff, audio_i2s.mic_size); - free(audio_i2s.mic_buff); - return 0; -} - -static const uint8_t wavHTemplate[] PROGMEM = { // Hardcoded simple WAV header with 0xffffffff lengths all around - 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, - 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, - 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff }; - -bool SaveWav(char *path, uint8_t *buff, uint32_t size) { - File fwp = ufsp->open(path, "w"); - if (!fwp) return false; - uint8_t wavHeader[sizeof(wavHTemplate)]; - memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate)); - - uint8_t channels = MIC_CHANNELS; - uint32_t hertz = MICSRATE; - uint8_t bps = 16; - - wavHeader[22] = channels & 0xff; - wavHeader[23] = 0; - wavHeader[24] = hertz & 0xff; - wavHeader[25] = (hertz >> 8) & 0xff; - wavHeader[26] = (hertz >> 16) & 0xff; - wavHeader[27] = (hertz >> 24) & 0xff; - int byteRate = hertz * bps * channels / 8; - wavHeader[28] = byteRate & 0xff; - wavHeader[29] = (byteRate >> 8) & 0xff; - wavHeader[30] = (byteRate >> 16) & 0xff; - wavHeader[31] = (byteRate >> 24) & 0xff; - wavHeader[32] = channels * bps / 8; - wavHeader[33] = 0; - wavHeader[34] = bps; - wavHeader[35] = 0; - - fwp.write(wavHeader, sizeof(wavHeader)); - - fwp.write(buff, size); - fwp.close(); - - return true; -} - -#endif // ESP32 - -#ifdef ESP32 - -void mp3_task(void *arg) { - while (1) { - while (audio_i2s.mp3->isRunning()) { - if (!audio_i2s.mp3->loop()) { - audio_i2s.mp3->stop(); - mp3_delete(); - audio_i2s.out->stop(); - if (audio_i2s.mp3_task_h) { - vTaskDelete(audio_i2s.mp3_task_h); - audio_i2s.mp3_task_h = 0; - } - //mp3_task_h=nullptr; - } - delay(1); - } - } -} -#endif // ESP32 - -#ifdef USE_I2S_WEBRADIO -void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { - const char *ptr = reinterpret_cast(cbData); - (void) isUnicode; // Punt this ball for now - (void) ptr; - if (strstr_P(type, PSTR("Title"))) { - strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title)); - audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0; - //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title); - } else { - // Who knows what to do? Not me! - } -} - -void StatusCallback(void *cbData, int code, const char *string) { - const char *ptr = reinterpret_cast(cbData); - (void) code; - (void) ptr; - //strncpy_P(status, string, sizeof(status)-1); - //status[sizeof(status)-1] = 0; -} - -void Webradio(const char *url) { - if (audio_i2s.decoder || audio_i2s.mp3) return; - if (!audio_i2s.out) return; - AUDIO_PWR_ON - audio_i2s.ifile = new AudioFileSourceICYStream(url); - audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL); - audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize); - audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL); - audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize); - audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL); - audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out); - if (!audio_i2s.decoder->isRunning()) { - // Serial.printf_P(PSTR("Can't connect to URL")); - StopPlaying(); - // strcpy_P(status, PSTR("Unable to connect to URL")); - audio_i2s.retryms = millis() + 2000; - } - - xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); -} - -void mp3_task2(void *arg){ - while (1) { - if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { - if (!audio_i2s.decoder->loop()) { - StopPlaying(); - //retryms = millis() + 2000; - } - delay(1); - } - } -} - -void StopPlaying() { - - if (audio_i2s.mp3_task_h) { - vTaskDelete(audio_i2s.mp3_task_h); - audio_i2s.mp3_task_h = nullptr; - } - - if (audio_i2s.decoder) { - audio_i2s.decoder->stop(); - delete audio_i2s.decoder; - audio_i2s.decoder = NULL; - } - - if (audio_i2s.buff) { - audio_i2s.buff->close(); - delete audio_i2s.buff; - audio_i2s.buff = NULL; - } - - if (audio_i2s.ifile) { - audio_i2s.ifile->close(); - delete audio_i2s.ifile; - audio_i2s.ifile = NULL; - } - DOWNRATE - AUDIO_PWR_OFF -} - -void Cmd_WebRadio(void) { - if (audio_i2s.decoder) { - StopPlaying(); - } - if (XdrvMailbox.data_len > 0) { - Webradio(XdrvMailbox.data); - ResponseCmndChar(XdrvMailbox.data); - } else { - ResponseCmndChar_P(PSTR("Stopped")); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_WEBRADIO[] PROGMEM = - "{s}" "I2S_WR-Title" "{m}%s{e}"; - -void I2S_WR_Show(bool json) { - if (audio_i2s.decoder) { - if (json) { - ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title); - } else { - WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); - } - } -} -#endif // USE_WEBSERVER - -#endif // USE_I2S_WEBRADIO - -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) -void Cmd_MicRec(void) { - if (XdrvMailbox.data_len > 0) { - uint16 time = 10; - char *cp = strchr(XdrvMailbox.data, ':'); - if (cp) { - time = atoi(cp + 1); - *cp = 0; - } - if (time<10) time = 10; - if (time>30) time = 30; - i2s_record(XdrvMailbox.data, time); - ResponseCmndChar(XdrvMailbox.data); - } -} -#endif // USE_M5STACK_CORE2 - -#ifdef ESP32 -void Play_mp3(const char *path) { -#ifdef USE_UFILESYS - if (audio_i2s.decoder || audio_i2s.mp3) return; - if (!audio_i2s.out) return; - - bool I2S_Task; - - if (*path=='+') { - I2S_Task = true; - path++; - } else { - I2S_Task = false; - } - - if (!ufsp->exists(path)) { - return; - } - - AUDIO_PWR_ON - - audio_i2s.file = new AudioFileSourceFS(*ufsp, path); - - audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); - - if (audio_i2s.mp3ram) { - audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); - } else { - audio_i2s.mp3 = new AudioGeneratorMP3(); - } - audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); - - if (I2S_Task) { - xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); - } else { - while (audio_i2s.mp3->isRunning()) { - if (!audio_i2s.mp3->loop()) { - audio_i2s.mp3->stop(); - break; - } - OsWatchLoop(); - } - audio_i2s.out->stop(); - mp3_delete(); - } - -#endif // USE_UFILESYS -} - -void mp3_delete(void) { - delete audio_i2s.file; - delete audio_i2s.id3; - delete audio_i2s.mp3; - audio_i2s.mp3=nullptr; - DOWNRATE - AUDIO_PWR_OFF -} -#endif // ESP32 - -void Say(char *text) { - - if (!audio_i2s.out) return; - - AUDIO_PWR_ON - - audio_i2s.out->begin(); - ESP8266SAM *sam = new ESP8266SAM; - sam->Say(audio_i2s.out, text); - delete sam; - audio_i2s.out->stop(); - - DOWNRATE - AUDIO_PWR_OFF -} - - -const char kI2SAudio_Commands[] PROGMEM = "I2S|" - "Say|Gain|Time" -#ifdef ESP32 - "|Play" -#ifdef USE_I2S_WEBRADIO - "|WR" -#endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) - "|REC" -#endif // USE_M5STACK_CORE2 -#endif // ESP32 - ; - -void (* const I2SAudio_Command[])(void) PROGMEM = { - &Cmd_Say, &Cmd_Gain, &Cmd_Time -#ifdef ESP32 - ,&Cmd_Play -#ifdef USE_I2S_WEBRADIO - ,&Cmd_WebRadio -#endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) - ,&Cmd_MicRec -#endif // USE_M5STACK_CORE2 -#endif // ESP32 -}; - - - -void Cmd_Play(void) { - if (XdrvMailbox.data_len > 0) { - Play_mp3(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void Cmd_Gain(void) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - if (audio_i2s.out) { - audio_i2s.is2_volume=XdrvMailbox.payload; - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); - } - } - ResponseCmndNumber(audio_i2s.is2_volume); -} - -void Cmd_Say(void) { - if (XdrvMailbox.data_len > 0) { - Say(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void Cmd_Time(void) { -#ifdef USE_I2S_SAY_TIME - sayTime(RtcTime.hour, RtcTime.minute); -#endif // USE_I2S_SAY_TIME - ResponseCmndDone(); -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xdrv42(uint8_t function) { - bool result = false; - - switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); - break; - case FUNC_INIT: - I2S_Init(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_I2S_WEBRADIO - case FUNC_WEB_SENSOR: - I2S_WR_Show(false); - break; -#endif // USE_I2S_WEBRADIO -#endif // USE_WEBSERVER -#ifdef USE_I2S_WEBRADIO - case FUNC_JSON_APPEND: - I2S_WR_Show(true); - break; -#endif // USE_I2S_WEBRADIO - } - return result; -} - -#endif // USE_I2S_AUDIO From d7086cbcf73616bf69002614e2fb339c208e91cf Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Thu, 21 Jul 2022 06:31:43 +0200 Subject: [PATCH 129/219] Update platformio_tasmota_env32.ini --- platformio_tasmota_env32.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 48a10c264..51e42d0d6 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -157,6 +157,7 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy + mp3_shine_esp32 [env:tasmota32c3] extends = env:tasmota32_base @@ -170,6 +171,7 @@ lib_ignore = TTGO TWatch Library Micro-RTSP epdiy + mp3_shine_esp32 [env:tasmota32c3cdc-safeboot] extends = env:tasmota32c3-safeboot From dda1889a7c77e40553dc3ff20b80d7e8916ba405 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Thu, 21 Jul 2022 09:54:37 +0200 Subject: [PATCH 130/219] Update esp8266toEsp32.h --- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index a4c6a602e..a8a014abf 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -26,7 +26,9 @@ // input range is in full range, ledc needs bits void analogWriteRange(uint32_t range); +void analogWriteRange(uint32_t range, uint8_t pin); void analogWriteFreq(uint32_t freq); +void analogWriteFreq(uint32_t freq, uint8_t pin); int32_t analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated void analogWrite(uint8_t pin, int val); From c1fba57bb6b71cfb60879bafb047118692f103e3 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Thu, 21 Jul 2022 10:05:11 +0200 Subject: [PATCH 131/219] Update esp8266toEsp32.cpp --- .../src/esp8266toEsp32.cpp | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index 563095a36..f451efb16 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -68,12 +68,19 @@ int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated return -1; } -void _analogWriteFreqRange(void) { +void _analogWriteFreqRange(uint8_t pin) { _analogInit(); // make sure the mapping array is initialized - for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { - if (pwm_channel[channel] < 255) { - ledcSetup(channel, pwm_frequency, pwm_bit_num); - } + if (pin = 255) { + for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { + if (pwm_channel[channel] < 255) { + ledcSetup(channel, pwm_frequency, pwm_bit_num); + } + } + } else { + int32_t chan = _analog_pin2chan(pin); + if (chan >= 0) { + ledcSetup(chan, pwm_frequency, pwm_bit_num); + } } } @@ -89,12 +96,22 @@ uint32_t _analogGetResolution(uint32_t x) { void analogWriteRange(uint32_t range) { pwm_bit_num = _analogGetResolution(range); - _analogWriteFreqRange(); + _analogWriteFreqRange(255); +} + +void analogWriteRange(uint32_t range, uint8_t pin) { + pwm_bit_num = _analogGetResolution(range); + _analogWriteFreqRange(pin); } void analogWriteFreq(uint32_t freq) { pwm_frequency = freq; - _analogWriteFreqRange(); + _analogWriteFreqRange(255); +} + +void analogWriteFreq(uint32_t freq, uint8_t pin) { + pwm_frequency = freq; + _analogWriteFreqRange(pin); } int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed From 1d3bf157bf477e2244fb85ddb34ef581d0242bfb Mon Sep 17 00:00:00 2001 From: barbudor Date: Thu, 21 Jul 2022 22:07:22 +0200 Subject: [PATCH 132/219] fix removal of negative values --- tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino b/tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino index c391afe16..f08c730b0 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_75_prometheus.ino @@ -298,7 +298,7 @@ void HandleMetrics(void) { JsonParserObject Object3 = value2.getObject(); for (auto key3 : Object3) { const char *value = key3.getValue().getStr(nullptr); - if (value != nullptr && isdigit(value[0])) { + if (value != nullptr && (isdigit(value[0]) || (value[0] == '-') || (value[0] == '.'))) { String sensor = FormatMetricName(key2.getStr()); String type = FormatMetricName(key3.getStr()); @@ -311,7 +311,7 @@ void HandleMetrics(void) { } } else { const char *value = value2.getStr(nullptr); - if (value != nullptr && isdigit(value[0])) { + if (value != nullptr && (isdigit(value[0]) || (value[0] == '-') || (value[0] == '.'))) { String sensor = FormatMetricName(key1.getStr()); String type = FormatMetricName(key2.getStr()); if (strcmp(type.c_str(), "totalstarttime") != 0) { // this metric causes Prometheus of fail @@ -336,7 +336,7 @@ void HandleMetrics(void) { const char *value = value1.getStr(nullptr); String sensor = FormatMetricName(key1.getStr()); - if (value != nullptr && isdigit(value[0] && strcmp(sensor.c_str(), "time") != 0)) { //remove false 'time' metric + if (value != nullptr && (isdigit(value[0]) || (value[0] == '-') || (value[0] == '.')) && strcmp(sensor.c_str(), "time") != 0) { //remove false 'time' metric WritePromMetricStr(PSTR("sensors"), kPromMetricGauge, value, PSTR("sensor"), sensor.c_str(), nullptr); From 51472ef84231ae3c0a4171179d4a4e08a4673144 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Fri, 22 Jul 2022 14:45:10 +0200 Subject: [PATCH 133/219] Update esp8266toEsp32.cpp --- .../src/esp8266toEsp32.cpp | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index f451efb16..d3927fc2f 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -11,7 +11,6 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - */ #ifdef ESP32 @@ -70,16 +69,16 @@ int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated void _analogWriteFreqRange(uint8_t pin) { _analogInit(); // make sure the mapping array is initialized - if (pin = 255) { + if (pin == 255) { for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { if (pwm_channel[channel] < 255) { ledcSetup(channel, pwm_frequency, pwm_bit_num); } } } else { - int32_t chan = _analog_pin2chan(pin); - if (chan >= 0) { - ledcSetup(chan, pwm_frequency, pwm_bit_num); + int32_t channel = _analog_pin2chan(pin); + if (channel >= 0) { + ledcSetup(channel, pwm_frequency, pwm_bit_num); } } } @@ -126,7 +125,7 @@ int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc chan // ledcAttachPin(pin, channel); -- replicating here because we want the default duty uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4); - + // AddLog(LOG_LEVEL_INFO, "PWM: ledc_channel pin=%i out_invert=%i", pin, output_invert); ledc_channel_config_t ledc_channel = { (int)pin, // gpio @@ -140,7 +139,6 @@ int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc chan }; ledc_channel_config(&ledc_channel); - ledcSetup(channel, pwm_frequency, pwm_bit_num); // Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel); return channel; @@ -160,19 +158,18 @@ extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) { /* The primary goal of this function is to add phase control to PWM ledc functions. - + Phase control allows to stress less the power supply of LED lights. By default all phases are starting at the same moment. This means the the power supply always takes a power hit at the start of each new cycle, even if the average power is low. - - Phase control is also of major importance for H-bridge where + Phase control is also of major importance for H-bridge where both PWM lines should NEVER be active at the same time. - + Unfortunately Arduino Core does not allow any customization nor extendibility for the ledc/analogWrite functions. We have therefore no other choice than duplicating part of Arduino code. - + WARNING: this means it can easily break if ever Arduino internal implementation changes. */ From 271d16ae100039e6250409e9cbbe97d9fbc9591b Mon Sep 17 00:00:00 2001 From: stefanbode Date: Fri, 22 Jul 2022 14:48:08 +0200 Subject: [PATCH 134/219] Update xdrv_27_shutter.ino --- .../tasmota_xdrv_driver/xdrv_27_shutter.ino | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index 8cdea103a..39e9c8d68 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -182,8 +182,9 @@ void ShutterRtc50mS(void) startWaveformClockCycles(Pin(GPIO_PWM1, i), cc/2, cc/2, 0, -1, 0, false); #endif // ESP8266 #ifdef ESP32 - analogWriteFreq(Shutter[i].pwm_velocity); - analogWrite(Pin(GPIO_PWM1, i), 50); + analogWriteFreq(Shutter[i].pwm_velocity,Pin(GPIO_PWM1, i)); + TasmotaGlobal.pwm_value[i] = 512; + PwmApplyGPIO(false); #endif // ESP32 } break; @@ -467,7 +468,6 @@ void ShutterDecellerateForStop(uint8_t i) delay(50); AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Velocity %ld, Delta %d"), Shutter[i].pwm_velocity, Shutter[i].accelerator ); // Control will be done in RTC Ticker. - } if (ShutterGlobal.position_mode == SHT_COUNTER){ missing_steps = ((Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND) - RtcSettings.pulse_counter[i]; @@ -477,7 +477,13 @@ void ShutterDecellerateForStop(uint8_t i) //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain %d count %d -> target %d, dir %d"), missing_steps, RtcSettings.pulse_counter[i], (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND, Shutter[i].direction); while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND && missing_steps > 0) { } +#ifdef ESP8266 analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog +#endif +#ifdef ESP32 + TasmotaGlobal.pwm_value[i] = 0; + PwmApplyGPIO(false); +#endif // ESP32 Shutter[i].real_position = ShutterCalculatePosition(i); //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d"), missing_steps); AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Real %d, Pulsecount %d, tobe %d, Start %d"), Shutter[i].real_position,RtcSettings.pulse_counter[i], (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND, Shutter[i].start_position); @@ -635,8 +641,15 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos) switch (ShutterGlobal.position_mode) { #ifdef SHUTTER_STEPPER case SHT_COUNTER: +#ifdef ESP8266 analogWriteFreq(Shutter[i].pwm_velocity); analogWrite(Pin(GPIO_PWM1, i), 0); +#endif +#ifdef ESP32 + analogWriteFreq(PWM_MIN,Pin(GPIO_PWM1, i)); + TasmotaGlobal.pwm_value[i] = 0; + PwmApplyGPIO(false); +#endif RtcSettings.pulse_counter[i] = 0; break; #endif From c17e73aa9b7d2fe36d2260f59cf9a34a4263ce1d Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 22 Jul 2022 15:13:41 +0200 Subject: [PATCH 135/219] Refactor analogWrite library --- .../src/esp8266toEsp32.cpp | 45 +++++++------------ .../src/esp8266toEsp32.h | 10 ++--- 2 files changed, 22 insertions(+), 33 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index d3927fc2f..1438c306a 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -57,7 +57,7 @@ void _analogInit(void) { pwm_impl_inited = true; } -int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated +int _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated _analogInit(); // make sure the mapping array is initialized for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { if ((pwm_channel[channel] < 255) && (pwm_channel[channel] == pin)) { @@ -67,16 +67,16 @@ int32_t _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated return -1; } -void _analogWriteFreqRange(uint8_t pin) { +void _analogWriteFreqRange(uint32_t pin) { _analogInit(); // make sure the mapping array is initialized - if (pin == 255) { - for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { - if (pwm_channel[channel] < 255) { - ledcSetup(channel, pwm_frequency, pwm_bit_num); - } - } + if (255 == pin) { + for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { + if (pwm_channel[channel] < 255) { + ledcSetup(channel, pwm_frequency, pwm_bit_num); + } + } } else { - int32_t channel = _analog_pin2chan(pin); + int channel = _analog_pin2chan(pin); if (channel >= 0) { ledcSetup(channel, pwm_frequency, pwm_bit_num); } @@ -93,30 +93,20 @@ uint32_t _analogGetResolution(uint32_t x) { return bits; } -void analogWriteRange(uint32_t range) { - pwm_bit_num = _analogGetResolution(range); - _analogWriteFreqRange(255); -} - -void analogWriteRange(uint32_t range, uint8_t pin) { +void analogWriteRange(uint32_t range, uint32_t pin) { pwm_bit_num = _analogGetResolution(range); _analogWriteFreqRange(pin); } -void analogWriteFreq(uint32_t freq) { - pwm_frequency = freq; - _analogWriteFreqRange(255); -} - -void analogWriteFreq(uint32_t freq, uint8_t pin) { +void analogWriteFreq(uint32_t freq, uint32_t pin) { pwm_frequency = freq; _analogWriteFreqRange(pin); } -int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed +int analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed _analogInit(); // make sure the mapping array is initialized // Find if pin is already attached - int32_t chan = _analog_pin2chan(pin); + int chan = _analog_pin2chan(pin); if (chan >= 0) { return chan; } // Find an empty channel for (chan = 0; chan < MAX_PWMS; chan++) { @@ -154,22 +144,21 @@ extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) { analogWritePhase(pin, val, 0); // if unspecified, use phase = 0 } - /* The primary goal of this function is to add phase control to PWM ledc functions. - + Phase control allows to stress less the power supply of LED lights. By default all phases are starting at the same moment. This means the the power supply always takes a power hit at the start of each new cycle, even if the average power is low. Phase control is also of major importance for H-bridge where both PWM lines should NEVER be active at the same time. - + Unfortunately Arduino Core does not allow any customization nor extendibility for the ledc/analogWrite functions. We have therefore no other choice than duplicating part of Arduino code. - + WARNING: this means it can easily break if ever Arduino internal implementation changes. */ @@ -179,7 +168,7 @@ extern uint8_t channels_resolution[MAX_PWMS]; void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase) { - int32_t chan = _analog_pin2chan(pin); + int chan = _analog_pin2chan(pin); if (chan < 0) { // not yet allocated, try to allocate chan = analogAttach(pin); if (chan < 0) { return; } // failed diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index a8a014abf..c10f111fb 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -25,11 +25,11 @@ // input range is in full range, ledc needs bits -void analogWriteRange(uint32_t range); -void analogWriteRange(uint32_t range, uint8_t pin); -void analogWriteFreq(uint32_t freq); -void analogWriteFreq(uint32_t freq, uint8_t pin); -int32_t analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated +//void analogWriteRange(uint32_t range); +void analogWriteRange(uint32_t range, uint32_t pin = 255); +//void analogWriteFreq(uint32_t freq); +void analogWriteFreq(uint32_t freq, uint32_t pin = 255); +int analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated void analogWrite(uint8_t pin, int val); // Extended version that also allows to change phase From b89fd72638dce4f49466f25c08c2911698a52b83 Mon Sep 17 00:00:00 2001 From: Martin Dummer Date: Fri, 22 Jul 2022 20:49:08 +0200 Subject: [PATCH 136/219] tasmota_xdrv_driver/xdrv_36_keeloq.ino: fix compile error When compiling with USE_KEELOQ and -DDEBUG_TASMOTA_DRIVER, it fails with some messages like ... error: cannot convert 'LoggingLevels' to 'const char*' for argument '2' to 'void AddLog(uint32_t, const char*, ...)' #define DEBUG_DRIVER_LOG(...) AddLog(LOG_LEVEL_DEBUG, __VA_ARGS__) This patch fixes this compile errors --- .../tasmota_xdrv_driver/xdrv_36_keeloq.ino | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino b/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino index cb1da911c..0db56bce4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino @@ -72,7 +72,7 @@ void CmdSet(void) for (uint32_t i = 0; i < 3; i++) { if (param[i] < 1) { param[i] = 1; } // msb, lsb, serial, counter } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); + DEBUG_DRIVER_LOG(PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); Settings->keeloq_master_msb = param[0]; Settings->keeloq_master_lsb = param[1]; Settings->keeloq_serial = param[2]; @@ -84,10 +84,10 @@ void CmdSet(void) GenerateDeviceCryptKey(); ResponseCmndDone(); } else { - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no payload")); + DEBUG_DRIVER_LOG(PSTR("no payload")); } } else { - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("no param")); + DEBUG_DRIVER_LOG(PSTR("no param")); } } @@ -110,10 +110,11 @@ void CmdSendButton(void) if (XdrvMailbox.payload > 0) { jaroliftDevice.button = strtoul(XdrvMailbox.data, nullptr, 0); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("msb: %08x"), jaroliftDevice.device_key_msb); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("serial: %08x"), jaroliftDevice.serial); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("disc: %08x"), jaroliftDevice.disc); + DEBUG_DRIVER_LOG(PSTR("msb: %08x"), jaroliftDevice.device_key_msb); + DEBUG_DRIVER_LOG(PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); + DEBUG_DRIVER_LOG(PSTR("serial: %08x"), jaroliftDevice.serial); + DEBUG_DRIVER_LOG(PSTR("disc: %08x"), jaroliftDevice.disc); + DEBUG_DRIVER_LOG(PSTR("button: %08x"), jaroliftDevice.button); AddLog(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count); CreateKeeloqPacket(); @@ -132,7 +133,7 @@ void CmdSendButton(void) SendBit(bitsToSend & 0x0000000000000001); bitsToSend >>= 1; } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("finished sending bits at %d"), micros()); delay(16); // delay in loop context is save for wdt } @@ -165,7 +166,7 @@ void SendBit(byte bitToSend) void CmndSendRaw(void) { - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cmd send called at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("cmd send called at %d"), micros()); noInterrupts(); entertx(); for(int repeat = 0; repeat <= 1; repeat++) @@ -181,7 +182,7 @@ void CmndSendRaw(void) { SendBit(XdrvMailbox.data[i] == '1'); } - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("finished sending bits at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("finished sending bits at %d"), micros()); delay(16); // delay in loop context is save for wdt } @@ -245,7 +246,7 @@ void KeeloqInit() jaroliftDevice.port_tx = Pin(GPIO_CC1101_GDO2); // Output port for transmission jaroliftDevice.port_rx = Pin(GPIO_CC1101_GDO0); // Input port for reception - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("cc1101.init()")); + DEBUG_DRIVER_LOG(PSTR("cc1101.init()")); delay(100); cc1101.init(); AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done.")); @@ -277,7 +278,7 @@ bool Xdrv36(uint8_t function) break; case FUNC_INIT: KeeloqInit(); - DEBUG_DRIVER_LOG(LOG_LEVEL_DEBUG_MORE, PSTR("init done.")); + DEBUG_DRIVER_LOG(PSTR("init done.")); break; } From 533d42fc070072a62d928c5b3825cc38df135abf Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 24 Jul 2022 14:48:03 +0200 Subject: [PATCH 137/219] Refactor keeloq logging --- .../tasmota_xdrv_driver/xdrv_36_keeloq.ino | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino b/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino index 0db56bce4..1ea0f3bfc 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_36_keeloq.ino @@ -72,7 +72,7 @@ void CmdSet(void) for (uint32_t i = 0; i < 3; i++) { if (param[i] < 1) { param[i] = 1; } // msb, lsb, serial, counter } - DEBUG_DRIVER_LOG(PSTR("params: %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); + DEBUG_DRIVER_LOG(PSTR("KLQ: params %08x %08x %08x %08x"), param[0], param[1], param[2], param[3]); Settings->keeloq_master_msb = param[0]; Settings->keeloq_master_lsb = param[1]; Settings->keeloq_serial = param[2]; @@ -84,10 +84,10 @@ void CmdSet(void) GenerateDeviceCryptKey(); ResponseCmndDone(); } else { - DEBUG_DRIVER_LOG(PSTR("no payload")); + DEBUG_DRIVER_LOG(PSTR("KLQ: no payload")); } } else { - DEBUG_DRIVER_LOG(PSTR("no param")); + DEBUG_DRIVER_LOG(PSTR("KLQ: no param")); } } @@ -97,7 +97,7 @@ void GenerateDeviceCryptKey() jaroliftDevice.device_key_msb = k.decrypt(jaroliftDevice.serial | 0x60000000L); jaroliftDevice.device_key_lsb = k.decrypt(jaroliftDevice.serial | 0x20000000L); - AddLog(LOG_LEVEL_DEBUG, PSTR("generated device keys: %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); + AddLog(LOG_LEVEL_DEBUG, PSTR("KLQ: generated device keys %08x %08x"), jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb); } void CmdSendButton(void) @@ -110,12 +110,9 @@ void CmdSendButton(void) if (XdrvMailbox.payload > 0) { jaroliftDevice.button = strtoul(XdrvMailbox.data, nullptr, 0); - DEBUG_DRIVER_LOG(PSTR("msb: %08x"), jaroliftDevice.device_key_msb); - DEBUG_DRIVER_LOG(PSTR("lsb: %08x"), jaroliftDevice.device_key_lsb); - DEBUG_DRIVER_LOG(PSTR("serial: %08x"), jaroliftDevice.serial); - DEBUG_DRIVER_LOG(PSTR("disc: %08x"), jaroliftDevice.disc); - DEBUG_DRIVER_LOG(PSTR("button: %08x"), jaroliftDevice.button); - AddLog(LOG_LEVEL_DEBUG, PSTR("KLQ: count: %08x"), jaroliftDevice.count); + DEBUG_DRIVER_LOG(PSTR("KLQ: msb %08x, lsb %08x, serial %08x, disc %08x, button %08x"), + jaroliftDevice.device_key_msb, jaroliftDevice.device_key_lsb, jaroliftDevice.serial, jaroliftDevice.disc, jaroliftDevice.button); + AddLog(LOG_LEVEL_DEBUG, PSTR("KLQ: count %08x"), jaroliftDevice.count); CreateKeeloqPacket(); jaroliftDevice.count++; @@ -133,7 +130,7 @@ void CmdSendButton(void) SendBit(bitsToSend & 0x0000000000000001); bitsToSend >>= 1; } - DEBUG_DRIVER_LOG(PSTR("finished sending bits at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("KLQ: finished sending bits at %d"), micros()); delay(16); // delay in loop context is save for wdt } @@ -166,7 +163,7 @@ void SendBit(byte bitToSend) void CmndSendRaw(void) { - DEBUG_DRIVER_LOG(PSTR("cmd send called at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("KLQ: cmd send called at %d"), micros()); noInterrupts(); entertx(); for(int repeat = 0; repeat <= 1; repeat++) @@ -182,7 +179,7 @@ void CmndSendRaw(void) { SendBit(XdrvMailbox.data[i] == '1'); } - DEBUG_DRIVER_LOG(PSTR("finished sending bits at %d"), micros()); + DEBUG_DRIVER_LOG(PSTR("KLQ: finished sending bits at %d"), micros()); delay(16); // delay in loop context is save for wdt } @@ -237,8 +234,7 @@ void CreateKeeloqPacket() jaroliftDevice.enc = k.encrypt(result); jaroliftDevice.pack |= jaroliftDevice.enc; - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("pack high: %08x"), jaroliftDevice.pack>>32); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("pack low: %08x"), jaroliftDevice.pack); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("KLQ: pack high %08x, low %08x"), jaroliftDevice.pack>>32, jaroliftDevice.pack); } void KeeloqInit() @@ -246,10 +242,10 @@ void KeeloqInit() jaroliftDevice.port_tx = Pin(GPIO_CC1101_GDO2); // Output port for transmission jaroliftDevice.port_rx = Pin(GPIO_CC1101_GDO0); // Input port for reception - DEBUG_DRIVER_LOG(PSTR("cc1101.init()")); + DEBUG_DRIVER_LOG(PSTR("KLQ: cc1101.init()")); delay(100); cc1101.init(); - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("CC1101 done.")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("KLQ: CC1101 done")); cc1101.setSyncWord(SYNC_WORD, false); cc1101.setCarrierFreq(CFREQ_433); cc1101.disableAddressCheck(); @@ -273,12 +269,12 @@ bool Xdrv36(uint8_t function) switch (function) { case FUNC_COMMAND: - AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("calling command")); + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("KLQ: calling command")); result = DecodeCommand(kJaroliftCommands, jaroliftCommand); break; case FUNC_INIT: KeeloqInit(); - DEBUG_DRIVER_LOG(PSTR("init done.")); + DEBUG_DRIVER_LOG(PSTR("KLQ: init done")); break; } From d7a210bf818c9c433d7306eb6151e7d3504a8059 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 24 Jul 2022 15:30:19 +0200 Subject: [PATCH 138/219] Increase dummy energy monitor max values Increase dummy energy monitor max values (#16059) --- tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino b/tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino index c01062c26..065c909af 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_30_dummy.ino @@ -83,28 +83,28 @@ bool NrgDummyCommand(void) { } else if (CMND_POWERSET == Energy.command_code) { if (XdrvMailbox.data_len) { - if ((abs_value > 100) && (abs_value < 200000)) { // Between 1.00 and 2000.00 W + if ((abs_value >= 100) && (abs_value <= 16000000)) { // Between 1.00 and 160000.00 W Settings->energy_power_calibration = abs_value; } } } else if (CMND_VOLTAGESET == Energy.command_code) { if (XdrvMailbox.data_len) { - if ((abs_value > 10000) && (abs_value < 26000)) { // Between 100.00 and 260.00 V + if ((abs_value >= 10000) && (abs_value <= 40000)) { // Between 100.00 and 400.00 V Settings->energy_voltage_calibration = abs_value; } } } else if (CMND_CURRENTSET == Energy.command_code) { if (XdrvMailbox.data_len) { - if ((abs_value > 1000) && (abs_value < 1000000)) { // Between 10.00 mA and 10.00000 A + if ((abs_value >= 1000) && (abs_value <= 40000000)) { // Between 10.00 mA and 400.00000 A Settings->energy_current_calibration = abs_value; } } } else if (CMND_FREQUENCYSET == Energy.command_code) { if (XdrvMailbox.data_len) { - if ((abs_value > 4500) && (abs_value < 6500)) { // Between 45.00 and 65.00 Hz + if ((abs_value >= 4500) && (abs_value <= 6500)) { // Between 45.00 and 65.00 Hz Settings->energy_frequency_calibration = abs_value; } } From 619c6d4588e67513ba40c79b0dc8bcc71e054039 Mon Sep 17 00:00:00 2001 From: Gabriele Lauricella Date: Mon, 25 Jul 2022 00:54:52 +0200 Subject: [PATCH 139/219] sendmail override domain for SMTP EHLO command --- tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino index 13d0a99de..42bb029a4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_1_webserver_mail.ino @@ -24,6 +24,10 @@ * * if email body consist of a single * and scripter is present * and a section >m is found, the lines in this section (until #) are sent as email body + * + * Some mail servers do not accept the IP address in the HELO (or EHLO) message but only a fully qualified + * domain name (FQDN). To overcome this, use the following define to override this behavior and enter the desired FQDN + * #define EMAIL_USER_DOMAIN "googlemail.com" * * sendmail works with pre2.6 using Light BearSSL * HW Watchdog 8.44 sec. @@ -132,7 +136,11 @@ bool SendEmail::send(const String& from, const String& to, const String& subject if (!buffer.startsWith(F("220"))) { return false; } buffer = F("EHLO "); +#ifdef EMAIL_USER_DOMAIN + buffer += EMAIL_USER_DOMAIN; +#else buffer += client->localIP().toString(); +#endif client->println(buffer); #ifdef DEBUG_EMAIL_PORT MailWriteAddLogBuffer(&buffer); From d475ccd5c59502be736c4da636f06e744768bdf4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 25 Jul 2022 10:00:12 +0200 Subject: [PATCH 140/219] Update decode-status.py --- tools/decode-status.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/decode-status.py b/tools/decode-status.py index bc06a682a..b9ad7287e 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -83,20 +83,20 @@ a_setoption = [[ "(HAss) enforce autodiscovery as light (1)", "(Wifi, MQTT) Control link led blinking (1)" ],[ - "(Button) Key hold time (ms)", - "(Pow) Sonoff POW Max_Power_Retry", - "(Backlog) Delay (ms)", + "(Button/Switch) Key hold time detection in decaseconds (default 40)", + "(Energy) Maximum number of retries before deciding power limit overflow (default 5)", + "(Backlog) Minimal delay in milliseconds between executing backlog commands (default 200)", "(not used) mDNS delayed start (Sec)", - "(Boot loop) Retry offset (0 = disable)", - "(Light) RGBWW remap", - "(IR) Unknown threshold", - "(CSE7766) invalid power margin", - "(Button) Ignore hold time (s)", - "(Wifi) Gratuitous ARP repeat time", - "(Temperature) Over temperature threshold (celsius)", - "(Rotary) Max allowed steps", - "(Bistable) Pulse time for two coil bistable latching relays (default 40)", - "(not used) Tuya MCU current Id", + "(Restart) Number of restarts to start detecting boot loop (default 1)", + "(Light) RGB and White channel separation (default 0)", + "(IR) Set the smallest sized UNKNOWN message packets we actually care about (default 6, max 255)", + "(CSE7766) Number of invalid power measurements before declaring it invalid allowing low load measurments (default 128)", + "(Button/Shutter) Ignore button change in seconds (default 0)", + "(Wifi) Interval in seconds between gratuitous ARP requests (default 60)", + "(Energy) Turn all power off at or above this temperature (default 90C)", + "(Rotary) Rotary step boundary (default 10)", + "(IR) Base tolerance percentage for matching incoming IR messages (default 25, max 100)", + "(Bistable) Pulse time in milliseconds for two coil bistable latching relays (default 40)", "(not used) Tuya MCU power Id", "(not used) Energy Tariff1 start hour", "(not used) Energy Tariff2 start hour", From bcaac8208fc3e5fc791f1e698d2c9fbe46707633 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Mon, 25 Jul 2022 09:56:35 +0200 Subject: [PATCH 141/219] Adding modbus bridge TCP Removed (u)int8 because modbus registers are always 16 bits and changed bit8 to bit Solved memory leak, changed logging Improved initialisation and configuring serial port Solved bug that addresses above 4 didn't reply Removed logging Added mandatory comment to USE_MODBUS_TCP_BRIDGE Using TasmotaModbus->Begin instead of begin Added bytecount to modbus tcp reply message Added comments Put modustcp variables in ModbusBridgeTCP struct. --- tasmota/my_user_config.h | 1 + .../xdrv_63_modbus_bridge.ino | 412 +++++++++++++++--- 2 files changed, 347 insertions(+), 66 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 9bf93bf55..eeaa50aba 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -754,6 +754,7 @@ //#define USE_DYP // Add support for DYP ME-007 ultrasonic distance sensor, serial port version (+0k5 code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) //#define USE_MODBUS_BRIDGE // Add support for software Modbus Bridge (+3k code) +//#define USE_MODBUS_BRIDGE_TCP // Add support for software Modbus TCP Bridge (Also enable Modbus Bridge!) (? code) //#define USE_TCP_BRIDGE // Add support for Serial to TCP bridge (+1.3k code) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, pause, stop, track, volume and reset #define MP3_VOLUME 30 // Set the startup volume on init, the range can be 0..100(max) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index faf102934..af4d39896 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -17,19 +17,24 @@ along with this program. If not, see . */ -#ifdef USE_MODBUS_BRIDGE +#if defined(USE_MODBUS_BRIDGE) /*********************************************************************************************\ * Modbus Bridge using Modbus library (TasmotaModbus) * + * Can be used trough web/mqtt commands and also via direct TCP connection (when defined) + * + * When USE_MODBUS_BRIDGE_TCP is also defined, this bridge can also be used as an ModbusTCP + * bridge. + * * Example Command: - * ModbusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint8", "count":4} + * ModbusSend {"deviceaddress": 1, "functioncode": 3, "startaddress": 1, "type":"uint16", "count":2} \*********************************************************************************************/ -#define XDRV_63 63 +#define XDRV_63 63 -#define MBR_MAX_VALUE_LENGTH 30 -#define MBR_SPEED TM_MODBUS_BAUDRATE -#define MBR_MAX_REGISTERS 64 +#define MBR_MAX_VALUE_LENGTH 30 +#define MBR_SPEED TM_MODBUS_BAUDRATE +#define MBR_MAX_REGISTERS 64 #define D_CMND_MODBUS_SEND "Send" #define D_CMND_MODBUS_SETBAUDRATE "Baudrate" @@ -45,11 +50,41 @@ #define D_JSON_MODBUS_VALUES "Values" #define D_JSON_MODBUS_LENGTH "Length" +#ifndef USE_MODBUS_BRIDGE_TCP const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; void (*const ModbusBridgeCommand[])(void) PROGMEM = { &CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig}; +#endif + +#ifdef USE_MODBUS_BRIDGE_TCP + +#define MODBUS_BRIDGE_TCP_CONNECTIONS 1 // number of maximum parallel connections, only 1 supported with modbus +#define MODBUS_BRIDGE_TCP_BUF_SIZE 255 // size of the buffer, above 132 required for efficient XMODEM + +#define D_CMND_MODBUS_TCP_START "TCPStart" +#define D_CMND_MODBUS_TCP_CONNECT "TCPConnect" + +const char kModbusBridgeCommands[] PROGMEM = "Modbus|" // Prefix + D_CMND_MODBUS_TCP_START "|" D_CMND_MODBUS_TCP_CONNECT "|" D_CMND_MODBUS_SEND "|" D_CMND_MODBUS_SETBAUDRATE "|" D_CMND_MODBUS_SETSERIALCONFIG; + +void (*const ModbusBridgeCommand[])(void) PROGMEM = { + &CmndModbusTCPStart, &CmndModbusTCPConnect, + &CmndModbusBridgeSend, &CmndModbusBridgeSetBaudrate, &CmndModbusBridgeSetConfig}; + +struct ModbusBridgeTCP +{ + WiFiServer *server_tcp = nullptr; + WiFiClient client_tcp[MODBUS_BRIDGE_TCP_CONNECTIONS]; + uint8_t client_next = 0; + uint8_t *tcp_buf = nullptr; // data transfer buffer + IPAddress ip_filter; + uint16_t tcp_transaction_id = 0; +}; + +ModbusBridgeTCP modbusBridgeTCP; +#endif #include TasmotaModbus *tasmotaModbus = nullptr; @@ -80,15 +115,13 @@ enum class ModbusBridgeFunctionCode enum class ModbusBridgeType { mb_undefined, - mb_uint8, mb_uint16, mb_uint32, - mb_int8, mb_int16, mb_int32, mb_float, mb_raw, - mb_bit8 + mb_bit }; enum class ModbusBridgeEndian @@ -116,10 +149,21 @@ struct ModbusBridge ModbusBridge modbusBridge; /********************************************************************************************/ - -bool SetModbusBridgeBegin(void) +// +// Applies serial configuration to modbus serial port +// +bool ModbusBridgeBegin(void) { - return tasmotaModbus->begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate + int result = tasmotaModbus->Begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate + if (result) + { + if (2 == result) + { + ClaimSerial(); + } + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), Settings->baudrate * 300); + } + return result; } void SetModbusBridgeConfig(uint32_t serial_config) @@ -131,33 +175,83 @@ void SetModbusBridgeConfig(uint32_t serial_config) if (serial_config != Settings->sserial_config) { Settings->sserial_config = serial_config; - SetModbusBridgeBegin(); + ModbusBridgeBegin(); + } +} + +void SetModbusBridgeBaudrate(uint32_t baudrate) +{ + if (baudrate >= 300) + { + Settings->baudrate = baudrate / 300; + ModbusBridgeBegin(); } } /********************************************************************************************/ - +// +// Handles data received from tasmota modbus wrapper and send this to (TCP or) MQTT client +// void ModbusBridgeHandle(void) { bool data_ready = tasmotaModbus->ReceiveReady(); if (data_ready) { uint8_t *buffer; - buffer = (uint8_t *)malloc(5 + modbusBridge.registerCount); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2) + buffer = (uint8_t *)malloc(5 + (modbusBridge.registerCount * 2)); // Addres(1), Function(1), Length(1), Data(1..n), CRC(2) uint32_t error = tasmotaModbus->ReceiveBuffer(buffer, modbusBridge.registerCount); - ModbusBridgeError errorcode = ModbusBridgeError::noerror; if (error) { AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR Driver error %d"), error); + free(buffer); + return; } - else if (modbusBridge.deviceAddress == 0) + +#ifdef USE_MODBUS_BRIDGE_TCP + for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) + { + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + if (client) + { + uint8_t MBAP_Header[7]; + MBAP_Header[0] = modbusBridgeTCP.tcp_transaction_id >> 8; + MBAP_Header[1] = modbusBridgeTCP.tcp_transaction_id; + MBAP_Header[2] = 0; + MBAP_Header[3] = 0; + MBAP_Header[4] = ((modbusBridge.registerCount * 2) + 3) >> 8; + MBAP_Header[5] = (modbusBridge.registerCount * 2) + 3; + MBAP_Header[6] = buffer[0]; // Send slave address + client.write(MBAP_Header, 7); + client.write(buffer + 1, 1); // Send Functioncode + uint8_t bytecount[1]; + bytecount[0] = modbusBridge.registerCount * 2; + client.write(bytecount, 1); // Send length of rtu data + client.write(buffer + 3, (modbusBridge.registerCount * 2)); // Don't send CRC + client.flush(); + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBRTCP from Modbus deviceAddress %d, writing %d bytes to client"), buffer[0], (modbusBridge.registerCount * 2) + 9); + } + } +#endif + + ModbusBridgeError errorcode = ModbusBridgeError::noerror; + if (modbusBridge.deviceAddress == 0) + { +#ifdef USE_MODBUS_BRIDGE_TCP + // If tcp client connected don't log error and exit this function (do not process) + if (nitems(modbusBridgeTCP.client_tcp)) + { + free(buffer); + return; + } +#endif errorcode = ModbusBridgeError::nodataexpected; + } else if (modbusBridge.deviceAddress != (uint8_t)buffer[0]) errorcode = ModbusBridgeError::wrongdeviceaddress; else if ((uint8_t)modbusBridge.functionCode != (uint8_t)buffer[1]) errorcode = ModbusBridgeError::wrongfunctioncode; - else if ((uint8_t)modbusBridge.registerCount != (uint8_t)buffer[2]) + else if ((uint8_t)modbusBridge.registerCount * 2 != (uint8_t)buffer[2]) errorcode = ModbusBridgeError::wrongregistercount; else { @@ -174,7 +268,7 @@ void ModbusBridgeHandle(void) ResponseJsonEnd(); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); } - else if ((buffer[1] > 0) && (buffer[0] < 5)) // Read Registers, writing is not supported at this moment + else if ((buffer[1] > 0) && (buffer[1] < 5)) // Read Registers, writing is not supported at this moment { Response_P(PSTR("{\"" D_JSON_MODBUS_RECEIVED "\":{")); ResponseAppend_P(PSTR("\"" D_JSON_MODBUS_DEVICE_ADDRESS "\":%d,"), buffer[0]); @@ -222,15 +316,7 @@ void ModbusBridgeHandle(void) else snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", value); } - else if ((modbusBridge.type == ModbusBridgeType::mb_int8) || - (modbusBridge.type == ModbusBridgeType::mb_uint8)) - { - if (modbusBridge.type == ModbusBridgeType::mb_int8) - snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d", (int8_t)(buffer[3 + count])); - else - snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%u", (uint8_t)(buffer[3 + count])); - } - else if (modbusBridge.type == ModbusBridgeType::mb_bit8) + else if (modbusBridge.type == ModbusBridgeType::mb_bit) { uint8_t value = (uint8_t)(buffer[3 + count]); snprintf(svalue, MBR_MAX_VALUE_LENGTH, "%d%d%d%d%d%d%d%d", ((value >> 7) & 1), ((value >> 6) & 1), ((value >> 5) & 1), ((value >> 4) & 1), ((value >> 3) & 1), ((value >> 2) & 1), ((value >> 1) & 1), (value & 1)); @@ -247,6 +333,8 @@ void ModbusBridgeHandle(void) if (errorcode == ModbusBridgeError::noerror) MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_MODBUS_RECEIVED)); } + else + errorcode = ModbusBridgeError::wrongfunctioncode; } if (errorcode != ModbusBridgeError::noerror) { @@ -258,25 +346,120 @@ void ModbusBridgeHandle(void) } /********************************************************************************************/ - +// +// Inits the tasmota modbus driver, sets serialport and if TCP enabled allocates a TCP buffer +// void ModbusBridgeInit(void) { if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) { tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); - uint8_t result = tasmotaModbus->Begin(MBR_SPEED); - if (result) + SetModbusBridgeConfig(TS_SERIAL_8E1); + SetModbusBridgeBaudrate(MBR_SPEED); + +#ifdef USE_MODBUS_BRIDGE_TCP + // If TCP bridge is enabled allocate a TCP receive buffer + modbusBridgeTCP.tcp_buf = (uint8_t *)malloc(MODBUS_BRIDGE_TCP_BUF_SIZE); + if (!modbusBridgeTCP.tcp_buf) { - if (2 == result) - { - Serial.begin(MBR_SPEED, SERIAL_8E1); - ClaimSerial(); - } - AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), MBR_SPEED); + AddLog(LOG_LEVEL_ERROR, PSTR("MBS: MBRTCP could not allocate buffer")); + return; } +#endif } } +#ifdef USE_MODBUS_BRIDGE_TCP +/********************************************************************************************/ +// +// Handles data for TCP server and TCP client. Sends requests to Modbus Devices +// +void ModbusTCPHandle(void) +{ + uint8_t c; + bool busy; // did we transfer some data? + int32_t buf_len; + + if (!tasmotaModbus) + return; + + // check for a new client connection + if ((modbusBridgeTCP.server_tcp) && (modbusBridgeTCP.server_tcp->hasClient())) + { + WiFiClient new_client = modbusBridgeTCP.server_tcp->available(); + + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Got connection from %s"), new_client.remoteIP().toString().c_str()); + // Check for IP filtering if it's enabled. + if (modbusBridgeTCP.ip_filter) + { + if (modbusBridgeTCP.ip_filter != new_client.remoteIP()) + { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Rejected due to filtering")); + new_client.stop(); + } + else + { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Allowed through filter")); + } + } + + // find an empty slot + uint32_t i; + for (i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) + { + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + if (!client) + { + client = new_client; + break; + } + } + if (i >= nitems(modbusBridgeTCP.client_tcp)) + { + i = modbusBridgeTCP.client_next++ % nitems(modbusBridgeTCP.client_tcp); + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + client.stop(); + client = new_client; + } + } + + do + { + busy = false; // exit loop if no data was transferred + + // handle data received from TCP + for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) + { + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + buf_len = 0; + while (client && (buf_len < MODBUS_BRIDGE_TCP_BUF_SIZE) && (client.available())) + { + c = client.read(); + if (c >= 0) + { + modbusBridgeTCP.tcp_buf[buf_len++] = c; + busy = true; + } + } + if (buf_len == 12) + { + uint8_t mbdeviceaddress = (uint8_t)modbusBridgeTCP.tcp_buf[6]; + uint8_t mbfunctioncode = (uint8_t)modbusBridgeTCP.tcp_buf[7]; + uint16_t mbstartaddress = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[8]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[9])); + modbusBridge.registerCount = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[10]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[11])); + modbusBridgeTCP.tcp_transaction_id = (uint16_t)((((uint16_t)modbusBridgeTCP.tcp_buf[0]) << 8) | ((uint16_t)modbusBridgeTCP.tcp_buf[1])); + + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("MBS: MBRTCP to Modbus Transactionid:%d, deviceAddress:%d, functionCode:%d, startAddress:%d, Count:%d"), + modbusBridgeTCP.tcp_transaction_id, mbdeviceaddress, mbfunctioncode, mbstartaddress, modbusBridge.registerCount); + + tasmotaModbus->Send(mbdeviceaddress, mbfunctioncode, mbstartaddress, modbusBridge.registerCount); + } + } + yield(); // avoid WDT if heavy traffic + } while (busy); +} +#endif + /*********************************************************************************************\ * Commands \*********************************************************************************************/ @@ -309,55 +492,46 @@ void CmndModbusBridgeSend(void) } modbusBridge.type = ModbusBridgeType::mb_undefined; - if (strcmp(stype, "int8") == 0) - { - modbusBridge.type = ModbusBridgeType::mb_int8; - modbusBridge.registerCount = 1 * modbusBridge.count; - } - else if (strcmp(stype, "int16") == 0) + if (strcmp(stype, "int16") == 0) { modbusBridge.type = ModbusBridgeType::mb_int16; - modbusBridge.registerCount = 2 * modbusBridge.count; + modbusBridge.registerCount = modbusBridge.count; } else if (strcmp(stype, "int32") == 0) { modbusBridge.type = ModbusBridgeType::mb_int32; - modbusBridge.registerCount = 4 * modbusBridge.count; - } - else if (strcmp(stype, "uint8") == 0) - { - modbusBridge.type = ModbusBridgeType::mb_uint8; - modbusBridge.registerCount = 1 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "uint16") == 0) { modbusBridge.type = ModbusBridgeType::mb_uint16; - modbusBridge.registerCount = 2 * modbusBridge.count; + modbusBridge.registerCount = modbusBridge.count; } else if (strcmp(stype, "uint32") == 0) { modbusBridge.type = ModbusBridgeType::mb_uint32; - modbusBridge.registerCount = 4 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "float") == 0) { modbusBridge.type = ModbusBridgeType::mb_float; - modbusBridge.registerCount = 4 * modbusBridge.count; + modbusBridge.registerCount = 2 * modbusBridge.count; } else if (strcmp(stype, "raw") == 0) { modbusBridge.type = ModbusBridgeType::mb_raw; modbusBridge.registerCount = modbusBridge.count; } - else if (strcmp(stype, "bit8") == 0) + else if (strcmp(stype, "bit") == 0) { - modbusBridge.type = ModbusBridgeType::mb_bit8; + modbusBridge.type = ModbusBridgeType::mb_bit; modbusBridge.registerCount = modbusBridge.count; } else errorcode = ModbusBridgeError::wrongtype; - if (modbusBridge.registerCount > MBR_MAX_REGISTERS) errorcode = ModbusBridgeError::wrongcount; + if (modbusBridge.registerCount > MBR_MAX_REGISTERS) + errorcode = ModbusBridgeError::wrongcount; if (errorcode != ModbusBridgeError::noerror) { @@ -371,13 +545,8 @@ void CmndModbusBridgeSend(void) void CmndModbusBridgeSetBaudrate(void) { - if (XdrvMailbox.payload >= 300) - { - XdrvMailbox.payload /= 300; // Make it a valid baudrate - Settings->sbaudrate = XdrvMailbox.payload; - SetModbusBridgeBegin(); - } - ResponseCmndNumber(Settings->sbaudrate * 300); + SetModbusBridgeBaudrate(XdrvMailbox.payload); + ResponseCmndNumber(Settings->baudrate * 300); } void CmndModbusBridgeSetConfig(void) @@ -407,6 +576,114 @@ void CmndModbusBridgeSetConfig(void) ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); } +#ifdef USE_MODBUS_BRIDGE_TCP +// +// Command `TCPStart` +// Params: port, +// +void CmndModbusTCPStart(void) +{ + + if (!tasmotaModbus) + { + return; + } + + int32_t tcp_port = XdrvMailbox.payload; + if (ArgC() == 2) + { + char sub_string[XdrvMailbox.data_len]; + modbusBridgeTCP.ip_filter.fromString(ArgV(sub_string, 2)); + } + else + { + // Disable whitelist if previously set + modbusBridgeTCP.ip_filter = (uint32_t)0; + } + + if (modbusBridgeTCP.server_tcp) + { + AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Stopping server")); + modbusBridgeTCP.server_tcp->stop(); + delete modbusBridgeTCP.server_tcp; + modbusBridgeTCP.server_tcp = nullptr; + + for (uint32_t i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) + { + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + client.stop(); + } + } + if (tcp_port > 0) + { + AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Starting server on port %d"), tcp_port); + if (modbusBridgeTCP.ip_filter) + { + AddLog(LOG_LEVEL_INFO, PSTR("MBS: MBRTCP Filtering %s"), modbusBridgeTCP.ip_filter.toString().c_str()); + } + modbusBridgeTCP.server_tcp = new WiFiServer(tcp_port); + modbusBridgeTCP.server_tcp->begin(); // start TCP server + modbusBridgeTCP.server_tcp->setNoDelay(true); + } + + ResponseCmndDone(); +} + +// +// Command `Connect` +// Params: port, +// +void CmndModbusTCPConnect(void) +{ + int32_t tcp_port = XdrvMailbox.payload; + + if (!tasmotaModbus) + { + return; + } + + if (ArgC() == 2) + { + char sub_string[XdrvMailbox.data_len]; + WiFiClient new_client; + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP Connecting to %s on port %d"), ArgV(sub_string, 2), tcp_port); + if (new_client.connect(ArgV(sub_string, 2), tcp_port)) + { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP connected!")); + } + else + { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBRTCP error connecting!")); + } + + // find an empty slot + uint32_t i; + for (i = 0; i < nitems(modbusBridgeTCP.client_tcp); i++) + { + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + if (!client) + { + client = new_client; + break; + } + } + if (i >= nitems(modbusBridgeTCP.client_tcp)) + { + i = modbusBridgeTCP.client_next++ % nitems(modbusBridgeTCP.client_tcp); + WiFiClient &client = modbusBridgeTCP.client_tcp[i]; + client.stop(); + client = new_client; + } + } + else + { + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_TCP "MBS: MBR Usage: port,ip_address")); + } + + ResponseCmndDone(); +} +#endif + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -423,12 +700,15 @@ bool Xdrv63(uint8_t function) { switch (function) { - case FUNC_EVERY_250_MSECOND: - ModbusBridgeHandle(); - break; case FUNC_COMMAND: result = DecodeCommand(kModbusBridgeCommands, ModbusBridgeCommand); break; + case FUNC_LOOP: + ModbusBridgeHandle(); +#ifdef USE_MODBUS_BRIDGE_TCP + ModbusTCPHandle(); +#endif + break; } } return result; From 7e02b61e460f3f6b38ac33e72e03e4577487691b Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Mon, 25 Jul 2022 19:18:30 +0200 Subject: [PATCH 142/219] Baudrate and serial config for modbus_bridge are now persistent --- tasmota/include/tasmota_types.h | 4 ++- .../xdrv_63_modbus_bridge.ino | 28 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index 78f92720a..e95aa8736 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -833,8 +833,10 @@ typedef struct { uint8_t shd_warmup_time; // F5E uint8_t tcp_config; // F5F uint8_t light_step_pixels; // F60 + uint8_t modbus_sbaudrate; // F61 + uint8_t modbus_sconfig; // F62 - uint8_t free_f61[19]; // F61 - Decrement if adding new Setting variables just above and below + uint8_t free_f63[17]; // F63 - Decrement if adding new Setting variables just above and below // Only 32 bit boundary variables below SOBitfield6 flag6; // F74 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index af4d39896..6f94f0485 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -33,7 +33,7 @@ #define XDRV_63 63 #define MBR_MAX_VALUE_LENGTH 30 -#define MBR_SPEED TM_MODBUS_BAUDRATE +#define MBR_BAUDRATE TM_MODBUS_BAUDRATE #define MBR_MAX_REGISTERS 64 #define D_CMND_MODBUS_SEND "Send" @@ -154,14 +154,17 @@ ModbusBridge modbusBridge; // bool ModbusBridgeBegin(void) { - int result = tasmotaModbus->Begin(Settings->baudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize modbus port with new baud rate + if ((Settings->modbus_sbaudrate < 300 / 300) || (Settings->modbus_sbaudrate > 115200 / 300)) Settings->modbus_sbaudrate = (uint8_t)MBR_BAUDRATE / 300; + if (Settings->modbus_sconfig > TS_SERIAL_8O2) Settings->modbus_sconfig = TS_SERIAL_8N1; + + int result = tasmotaModbus->Begin(Settings->modbus_sbaudrate * 300, ConvertSerialConfig(Settings->modbus_sconfig)); // Reinitialize modbus port with new baud rate if (result) { if (2 == result) { ClaimSerial(); } - AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), Settings->baudrate * 300); + AddLog(LOG_LEVEL_DEBUG, PSTR("MBS: MBR %s ser init at %d baud"), (2 == result ? "HW" : "SW"), Settings->modbus_sbaudrate * 300); } return result; } @@ -172,19 +175,22 @@ void SetModbusBridgeConfig(uint32_t serial_config) { serial_config = TS_SERIAL_8N1; } - if (serial_config != Settings->sserial_config) + if (serial_config != Settings->modbus_sconfig) { - Settings->sserial_config = serial_config; + Settings->modbus_sconfig = serial_config; ModbusBridgeBegin(); } } void SetModbusBridgeBaudrate(uint32_t baudrate) { - if (baudrate >= 300) + if ((baudrate >= 300) && (baudrate <= 115200)) { - Settings->baudrate = baudrate / 300; - ModbusBridgeBegin(); + if (baudrate / 300 != Settings->modbus_sbaudrate) + { + Settings->modbus_sbaudrate = baudrate / 300; + ModbusBridgeBegin(); + } } } @@ -354,8 +360,6 @@ void ModbusBridgeInit(void) if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) { tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); - SetModbusBridgeConfig(TS_SERIAL_8E1); - SetModbusBridgeBaudrate(MBR_SPEED); #ifdef USE_MODBUS_BRIDGE_TCP // If TCP bridge is enabled allocate a TCP receive buffer @@ -546,7 +550,7 @@ void CmndModbusBridgeSend(void) void CmndModbusBridgeSetBaudrate(void) { SetModbusBridgeBaudrate(XdrvMailbox.payload); - ResponseCmndNumber(Settings->baudrate * 300); + ResponseCmndNumber(Settings->modbus_sbaudrate * 300); } void CmndModbusBridgeSetConfig(void) @@ -573,7 +577,7 @@ void CmndModbusBridgeSetConfig(void) } } } - ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); + ResponseCmndChar(GetSerialConfig(Settings->modbus_sconfig).c_str()); } #ifdef USE_MODBUS_BRIDGE_TCP From 197e25132cafb63db52b9f8871e548b3b0d4f7eb Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 25 Jul 2022 22:32:26 +0200 Subject: [PATCH 143/219] Berry fix reference when exeception is raised --- lib/libesp32/berry/src/be_exec.c | 5 +++++ lib/libesp32/berry/src/be_exec.h | 1 + lib/libesp32/berry/tests/reference.be | 31 +++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 lib/libesp32/berry/tests/reference.be diff --git a/lib/libesp32/berry/src/be_exec.c b/lib/libesp32/berry/src/be_exec.c index 2278ab2fa..9be2793d7 100644 --- a/lib/libesp32/berry/src/be_exec.c +++ b/lib/libesp32/berry/src/be_exec.c @@ -61,6 +61,7 @@ struct pcall { struct vmstate { int top, reg, depth; + int refcount; }; struct strbuf { @@ -125,6 +126,7 @@ static void vm_state_save(bvm *vm, struct vmstate *state) state->depth = be_stack_count(&vm->callstack); state->top = cast_int(vm->top - vm->stack); state->reg = cast_int(vm->reg - vm->stack); + state->refcount = vm->refstack.count; } static void copy_exception(bvm *vm, int res, int dstindex) @@ -143,6 +145,7 @@ static void copy_exception(bvm *vm, int res, int dstindex) static void vm_state_restore(bvm *vm, const struct vmstate *state, int res) { vm->reg = vm->stack + state->reg; + be_vector_resize(vm, &vm->refstack, state->refcount); /* copy exception information to top */ copy_exception(vm, res, state->top); be_assert(be_stack_count(&vm->callstack) >= state->depth); @@ -444,6 +447,7 @@ void be_except_block_setup(bvm *vm) /* set longjmp() jump point */ frame->errjmp.status = 0; frame->errjmp.prev = vm->errjmp; /* save long jump list */ + frame->refcount = vm->refstack.count; /* save reference pointer */ vm->errjmp = &frame->errjmp; fixup_exceptstack(vm, lbase); } @@ -455,6 +459,7 @@ void be_except_block_resume(bvm *vm) struct bexecptframe *frame = be_stack_top(&vm->exceptstack); if (errorcode == BE_EXCEPTION) { vm->errjmp = vm->errjmp->prev; + be_vector_resize(vm, &vm->refstack, frame->refcount); /* jump to except instruction */ vm->ip = frame->ip + IGET_sBx(frame->ip[-1]); if (be_stack_count(&vm->callstack) > frame->depth) { diff --git a/lib/libesp32/berry/src/be_exec.h b/lib/libesp32/berry/src/be_exec.h index ffb85fa87..af10bf255 100644 --- a/lib/libesp32/berry/src/be_exec.h +++ b/lib/libesp32/berry/src/be_exec.h @@ -45,6 +45,7 @@ struct bexecptframe { struct blongjmp errjmp; /* long jump information */ int depth; /* function call stack depth */ binstruction *ip; /* instruction pointer */ + int refcount; /* save object reference stack */ }; void be_throw(bvm *vm, int errorcode); diff --git a/lib/libesp32/berry/tests/reference.be b/lib/libesp32/berry/tests/reference.be new file mode 100644 index 000000000..f63bab459 --- /dev/null +++ b/lib/libesp32/berry/tests/reference.be @@ -0,0 +1,31 @@ +# try to exercise bug in reference + +class failable + var fail # if 'true', tostring() raises an exception + + def tostring() + if self.fail + raise "internal_error", "FAIL" + return "FAIL" + else + return "SUCCESS" + end + end +end +f = failable() + +l1 = [1, 2, f] +l2 = ["foo", l1] +l1.push(l1) + +assert(str(l2) == "['foo', [1, 2, SUCCESS, [...]]]") +assert(str(l1) == "[1, 2, SUCCESS, [...]]") + +f.fail = true +try + print(str(l1)) +except .. +end + +f.fail = false +assert(str(l1) == "[1, 2, SUCCESS, [...]]") # FAILS From 0b7d8ac66828b96aadd935b8e15a6ad6a4efd6bd Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 25 Jul 2022 22:49:58 +0200 Subject: [PATCH 144/219] Berry remove unnecessary ref push --- lib/libesp32/berry/src/be_baselib.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/libesp32/berry/src/be_baselib.c b/lib/libesp32/berry/src/be_baselib.c index f556b4ab3..592d74a53 100644 --- a/lib/libesp32/berry/src/be_baselib.c +++ b/lib/libesp32/berry/src/be_baselib.c @@ -333,7 +333,6 @@ static int l_call(bvm *vm) be_moveto(vm, top + 1, top + 1 + list_size); be_moveto(vm, top, top + list_size); - be_refpush(vm, -2); be_pushiter(vm, -1); while (be_iter_hasnext(vm, -2)) { be_iter_next(vm, -2); @@ -342,7 +341,6 @@ static int l_call(bvm *vm) be_pop(vm, 1); } be_pop(vm, 1); /* remove iterator */ - be_refpop(vm); } be_pop(vm, 2); arg_count = arg_count - 1 + list_size; From 7fab74bc8f0c4a034c7710682cd45995dbb161c6 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Tue, 26 Jul 2022 09:38:57 +0200 Subject: [PATCH 145/219] ModbusBridgeBegin was not called at (boot) init --- tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 6f94f0485..1a58736a2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -360,7 +360,7 @@ void ModbusBridgeInit(void) if (PinUsed(GPIO_MBR_RX) && PinUsed(GPIO_MBR_TX)) { tasmotaModbus = new TasmotaModbus(Pin(GPIO_MBR_RX), Pin(GPIO_MBR_TX)); - + ModbusBridgeBegin(); #ifdef USE_MODBUS_BRIDGE_TCP // If TCP bridge is enabled allocate a TCP receive buffer modbusBridgeTCP.tcp_buf = (uint8_t *)malloc(MODBUS_BRIDGE_TCP_BUF_SIZE); From 9ac38cb9d23c869b26a736602728cc97b72856f9 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 26 Jul 2022 09:51:38 +0200 Subject: [PATCH 146/219] Berry remove unused registry code --- lib/libesp32/berry/src/be_api.c | 26 -------------------------- lib/libesp32/berry/src/be_gc.c | 1 - lib/libesp32/berry/src/be_vm.h | 1 - lib/libesp32/berry/src/berry.h | 5 ----- 4 files changed, 33 deletions(-) diff --git a/lib/libesp32/berry/src/be_api.c b/lib/libesp32/berry/src/be_api.c index 79b41edc9..763187430 100644 --- a/lib/libesp32/berry/src/be_api.c +++ b/lib/libesp32/berry/src/be_api.c @@ -1142,29 +1142,3 @@ BERRY_API bbool be_isge(bvm *vm) be_assert(vm->reg + 2 <= vm->top); return be_vm_isge(vm, vm->top - 2, vm->top - 1); } - -BERRY_API int be_register(bvm *vm, int index) -{ - bvalue *v; - if (!vm->registry) { - vm->registry = be_list_new(vm); - be_list_pool_init(vm, vm->registry); - } - be_assert(vm->registry != NULL); - v = be_indexof(vm, index); - return be_list_pool_alloc(vm, vm->registry, v); -} - -BERRY_API void be_unregister(bvm *vm, int id) -{ - be_assert(vm->registry != NULL); - be_list_pool_free(vm->registry, id); -} - -BERRY_API void be_getregister(bvm *vm, int id) -{ - blist *reg = vm->registry; - be_assert(reg && id > 0 && id < be_list_count(reg)); - var_setval(vm->top, be_list_at(reg, id)); - be_incrtop(vm); -} diff --git a/lib/libesp32/berry/src/be_gc.c b/lib/libesp32/berry/src/be_gc.c index cd4e7b0b4..0254c8a19 100644 --- a/lib/libesp32/berry/src/be_gc.c +++ b/lib/libesp32/berry/src/be_gc.c @@ -370,7 +370,6 @@ static void premark_internal(bvm *vm) mark_gray(vm, gc_object(vm->module.loaded)); mark_gray(vm, gc_object(vm->module.path)); mark_gray(vm, gc_object(vm->ntvclass)); - mark_gray(vm, gc_object(vm->registry)); #if BE_USE_DEBUG_HOOK if (be_isgcobj(&vm->hook)) { mark_gray(vm, gc_object(var_toobj(&vm->hook))); diff --git a/lib/libesp32/berry/src/be_vm.h b/lib/libesp32/berry/src/be_vm.h index 17a21b6c6..26d9f8b13 100644 --- a/lib/libesp32/berry/src/be_vm.h +++ b/lib/libesp32/berry/src/be_vm.h @@ -102,7 +102,6 @@ struct bvm { struct bstringtable strtab; /* short string table */ bstack tracestack; /* call state trace-stack */ bmap *ntvclass; /* native class table */ - blist *registry; /* registry list */ struct bgc gc; bctypefunc ctypefunc; /* handler to ctype_func */ bbyte compopt; /* compilation options */ diff --git a/lib/libesp32/berry/src/berry.h b/lib/libesp32/berry/src/berry.h index 814210ed6..e1eb2a777 100644 --- a/lib/libesp32/berry/src/berry.h +++ b/lib/libesp32/berry/src/berry.h @@ -580,11 +580,6 @@ BERRY_API void be_module_path_set(bvm *vm, const char *path); BERRY_API void* be_pushbytes(bvm *vm, const void *buf, size_t len); BERRY_API const void* be_tobytes(bvm *vm, int index, size_t *len); -/* registry operation */ -BERRY_API int be_register(bvm *vm, int index); -BERRY_API void be_unregister(bvm *vm, int id); -BERRY_API void be_getregister(bvm *vm, int id); - /* debug APIs */ BERRY_API void be_sethook(bvm *vm, const char *mask); BERRY_API void be_setntvhook(bvm *vm, bntvhook hook, void *data, int mask); From d8fbc5eaf2645f0435fe297af79bc0257d7953c7 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 26 Jul 2022 15:25:00 +0200 Subject: [PATCH 147/219] fixes memory problems --- lib/lib_audio/mp3_shine_esp32/src/layer3.cpp | 25 ++++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp index d0ec59c3b..80d7b0626 100755 --- a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp +++ b/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp @@ -106,8 +106,16 @@ shine_global_config *shine_initialise(shine_config_t *pub_config) { for (x = 0; x < MAX_CHANNELS; x++) { for (y = 0; y < MAX_GRANULES; y++) { // 2 * 2 * 576 each - config->l3_enc[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //Significant performance hit in IRAM - config->mdct_freq[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 1% + config->l3_enc[x][y] = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //Significant performance hit in IRAM + if (!config->l3_enc[x][y]) { + // error should never occur because of spiram size + //config->l3_enc[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE,MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); + } + config->mdct_freq[x][y] = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 1% + if (!config->mdct_freq[x][y]) { + // error + //config->mdct_freq[x][y] = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); + } } } #ifdef SHINE_DEBUG @@ -117,8 +125,16 @@ shine_global_config *shine_initialise(shine_config_t *pub_config) { #ifdef SHINE_DEBUG printf("xrsq & xrabs each: %d\n", sizeof(int32_t)*GRANULE_SIZE); #endif - config->l3loop->xrsq = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5% - config->l3loop->xrabs = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT); //OK 0.5% + config->l3loop->xrsq = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5% + if (!config->l3loop->xrsq) { + // error + //config->l3loop->xrsq = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5% + } + + config->l3loop->xrabs = (int*)heap_caps_malloc_prefer(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_32BIT, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5% + if (!config->l3loop->xrabs) { + //config->l3loop->xrabs = (int*)heap_caps_malloc(sizeof(int32_t)*GRANULE_SIZE, MALLOC_CAP_SPIRAM|MALLOC_CAP_32BIT); //OK 0.5% + } /*typedef struct { int32_t *xr; @@ -273,7 +289,6 @@ void shine_close(shine_global_config *config) { } } - config->l3loop = (l3loop_t*)heap_caps_malloc(sizeof(l3loop_t), MALLOC_CAP_SPIRAM); if (config->l3loop) { free(config->l3loop); } From 3945cc6fc089484ac2cc015a279404b97e4b4edb Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 26 Jul 2022 15:26:30 +0200 Subject: [PATCH 148/219] audio restructured --- .../xdrv_42_0_i2s_audio.ino | 668 +++++++++++ .../xdrv_42_1_i2s_audio.ino | 1049 ----------------- .../xdrv_42_1_i2s_mp3mic.ino | 297 +++++ .../xdrv_42_2_i2s_mp3stream.ino | 58 + ..._i2s_audio.ino => xdrv_42_4_i2s_s3box.ino} | 152 +-- 5 files changed, 1049 insertions(+), 1175 deletions(-) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino delete mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino rename tasmota/tasmota_xdrv_driver/{xdrv_42_2_i2s_audio.ino => xdrv_42_4_i2s_s3box.ino} (56%) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino new file mode 100644 index 000000000..8dc835059 --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino @@ -0,0 +1,668 @@ +/* + xdrv_42_i2s_audio.ino - Audio dac support for Tasmota + + Copyright (C) 2021 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) +/*********************************************************************************************\ + * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor + * + * Uses fixed GPIOs for ESP8266: + * I2S Out Data GPIO03 (Rx) + * I2S Out Bit Clock GPIO15 + * I2S Out Word Select GPIO02 + * I2S In Data GPIO12 + * I2S In Bit Clock GPIO13 + * I2S In Word Select GPIO14 +\*********************************************************************************************/ + +#define XDRV_42 42 + +#define USE_I2S_EXTERNAL_DAC 1 +//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC +//#define USE_I2S_WEBRADIO // Add support for web radio +//#define USE_I2S_SAY_TIME // Add support for english speaking clock + +#include "AudioFileSourcePROGMEM.h" +#include "AudioFileSourceID3.h" +#include "AudioGeneratorMP3.h" +#ifdef USE_I2S_NO_DAC + #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin +#else + #include "AudioOutputI2S.h" // External I2S DAC IC +#endif // USE_I2S_NO_DAC +#include +#include "AudioFileSourceFS.h" +#ifdef USE_I2S_SAY_TIME + #include "AudioGeneratorTalkie.h" +#endif // USE_I2S_SAY_TIME +#include "AudioFileSourceICYStream.h" +#include "AudioFileSourceBuffer.h" +#include "AudioGeneratorAAC.h" + +#ifdef ESP32 +#include +#endif + +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON +#define AUDIO_PWR_OFF + +#ifdef ESP8266 +#define i2s_port_t uint8_t +#endif + +#ifdef ESP32 +#define MODE_MIC 0 +#define MODE_SPK 1 +#ifndef MICSRATE +#define MICSRATE 16000 +#endif +#endif // ESP32 + +struct AUDIO_I2S_t { + uint8_t is2_volume; // should be in settings + i2s_port_t i2s_port; + int8_t mclk = -1; + int8_t bclk = -1; + int8_t ws = -1; + int8_t dout = -1; + int8_t din = -1; + AudioGeneratorMP3 *mp3 = nullptr; + AudioFileSourceFS *file; +#ifdef USE_I2S_NO_DAC + AudioOutputI2SNoDAC *out; +#else + AudioOutputI2S *out; +#endif // USE_I2S_NO_DAC + AudioFileSourceID3 *id3; + AudioGeneratorMP3 *decoder = NULL; + void *mp3ram = NULL; +#ifdef USE_I2S_WEBRADIO + AudioFileSourceICYStream *ifile = NULL; + AudioFileSourceBuffer *buff = NULL; + char wr_title[64]; + void *preallocateBuffer = NULL; + void *preallocateCodec = NULL; + uint32_t retryms = 0; +#endif // USE_I2S_WEBRADIO + +#ifdef ESP32 + TaskHandle_t mp3_task_h; + TaskHandle_t mic_task_h; + uint32_t mic_size; + uint32_t mic_rate; + uint8_t *mic_buff; + char mic_path[32]; + uint8_t mic_channels; + File fwp; + uint8_t mic_stop; + int8_t mic_error; + int8_t mic_mclk = -1; + int8_t mic_bclk = -1; + int8_t mic_ws = -1; + int8_t mic_din = -1; + int8_t mic_dout = -1; + bool use_stream = false; + i2s_port_t mic_port; +#endif // ESP32 + +#ifdef USE_SHINE + uint32_t recdur; + uint8_t stream_active; + uint8_t stream_enable; + WiFiClient client; + ESP8266WebServer *MP3Server; +#endif + +} audio_i2s; + + +#define MIC_CHANNELS 1 + +#ifdef USE_TTGO_WATCH +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON TTGO_audio_power(true); +#define AUDIO_PWR_OFF TTGO_audio_power(false); +#endif // USE_TTGO_WATCH + +#ifdef USE_M5STACK_CORE2 +// leave this predefined currently +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON Core2AudioPower(true); +#define AUDIO_PWR_OFF Core2AudioPower(false); +#undef DAC_IIS_BCK +#undef DAC_IIS_WS +#undef DAC_IIS_DOUT +#undef DAC_IIS_DIN +#define DAC_IIS_BCK 12 +#define DAC_IIS_WS 0 +#define DAC_IIS_DOUT 2 +#define DAC_IIS_DIN 34 +#undef MICSRATE +#define MICSRATE 32000 +#undef MIC_CHANNELS +#define MIC_CHANNELS 1 +#endif // USE_M5STACK_CORE2 + + +#ifdef ESP32S3_BOX +#undef AUDIO_PWR_ON +#undef AUDIO_PWR_OFF +#define AUDIO_PWR_ON S3boxAudioPower(true); +#define AUDIO_PWR_OFF S3boxAudioPower(false); +#undef MIC_CHANNELS +#define MIC_CHANNELS 2 +#endif // ESP32S3_BOX + +extern FS *ufsp; +extern FS *ffsp; + +#ifdef ESP8266 +const int preallocateBufferSize = 5*1024; +const int preallocateCodecSize = 29192; // MP3 codec max mem needed +#endif // ESP8266 + +#ifdef ESP32 +const int preallocateBufferSize = 16*1024; +const int preallocateCodecSize = 29192; // MP3 codec max mem needed +//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed +#endif // ESP32 + +enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; +enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; + +#ifdef ESP8266 +#define I2S_MCLK_MULTIPLE_128 0 +#endif + +void sayTime(int hour, int minutes); +void Cmd_MicRec(void); +void Cmd_wav2mp3(void); +void Cmd_Time(void); + +int32_t I2S_Init_0(void) { + + audio_i2s.i2s_port = (i2s_port_t)0; + audio_i2s.mic_port = (i2s_port_t)0; + +#if USE_I2S_EXTERNAL_DAC + // use i2s +#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT)) + audio_i2s.i2s_port = (i2s_port_t)0; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(); +#endif + audio_i2s.bclk = DAC_IIS_BCK; + audio_i2s.ws = DAC_IIS_WS; + audio_i2s.dout = DAC_IIS_DOUT; + audio_i2s.din = DAC_IIS_DIN; +#else +#ifdef USE_I2S_NO_DAC + if (PinUsed(GPIO_I2S_DOUT)) { +#else + if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) { +#endif // USE_I2S_NO_DAC + audio_i2s.i2s_port = (i2s_port_t)0; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); +#endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK); + audio_i2s.ws = Pin(GPIO_I2S_WS); + audio_i2s.dout = Pin(GPIO_I2S_DOUT); + audio_i2s.din = Pin(GPIO_I2S_DIN); + + audio_i2s.mic_mclk = audio_i2s.mclk; + audio_i2s.mic_bclk = audio_i2s.bclk; + audio_i2s.mic_ws = audio_i2s.ws; + audio_i2s.mic_dout = audio_i2s.dout; + audio_i2s.mic_din = audio_i2s.din; + audio_i2s.mic_port = (i2s_port_t)0; + + // check if 2 ports used, use second for micro + if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DIN, 1)) { + audio_i2s.mic_bclk = -1; + audio_i2s.mic_bclk = Pin(GPIO_I2S_BCLK, 1); + audio_i2s.mic_ws = Pin(GPIO_I2S_WS, 1); + audio_i2s.mic_dout = -1; + audio_i2s.mic_din = Pin(GPIO_I2S_DIN, 1); + audio_i2s.mic_port = (i2s_port_t)1; + } + } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT, 1)) { + audio_i2s.i2s_port = (i2s_port_t)1; +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); + audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); +#endif // USE_I2S_NO_DAC + audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); + audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); + audio_i2s.ws = Pin(GPIO_I2S_WS, 1); + audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); + audio_i2s.din = Pin(GPIO_I2S_DIN, 1); + + audio_i2s.mic_mclk = audio_i2s.mclk; + audio_i2s.mic_bclk = audio_i2s.bclk; + audio_i2s.mic_ws = audio_i2s.ws; + audio_i2s.mic_dout = audio_i2s.dout; + audio_i2s.mic_din = audio_i2s.din; + audio_i2s.mic_port = (i2s_port_t)1; + + } else { + return -1; + } +#ifdef ESP8266 + // esp8266 have fixed pins + if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) { + return -2; + } +#endif // ESP8266 +#endif // defined(DAC_IIS_BCK) + + audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + + AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); + if (audio_i2s.mic_port != 0) { + AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S mic: port=%d, bclk=%d, ws=%d, din=%d"), audio_i2s.mic_port, audio_i2s.mic_bclk, audio_i2s.mic_ws, audio_i2s.mic_din); + } +#else + +#ifdef USE_I2S_NO_DAC + audio_i2s.out = new AudioOutputI2SNoDAC(); +#else + audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0 +#endif // USE_I2S_NO_DAC + +#endif // USE_I2S_EXTERNAL_DAC + + return 0; +} + +void I2S_Init(void) { + + #if defined(ESP32) && defined(ESP32S3_BOX) + S3boxInit(); + #endif + + if (I2S_Init_0()) { + return; + } + + audio_i2s.is2_volume=10; + audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); + audio_i2s.out->stop(); + audio_i2s.mp3ram = nullptr; + +#ifdef ESP32 + if (UsePSRAM()) { + audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } + +#ifdef USE_I2S_WEBRADIO + if (UsePSRAM()) { + audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + } else { + audio_i2s.preallocateBuffer = malloc(preallocateBufferSize); + audio_i2s.preallocateCodec = malloc(preallocateCodecSize); + } + if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) { + //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); + } +#endif // USE_I2S_WEBRADIO + + audio_i2s.mic_channels = MIC_CHANNELS; + audio_i2s.mic_rate = MICSRATE; + +#endif // ESP32 +} + +#ifdef ESP32 +void mp3_task(void *arg) { + while (1) { + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); + mp3_delete(); + audio_i2s.out->stop(); + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = 0; + } + //mp3_task_h=nullptr; + } + delay(1); + } + } +} +#endif // ESP32 + +#ifdef USE_I2S_WEBRADIO +void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { + const char *ptr = reinterpret_cast(cbData); + (void) isUnicode; // Punt this ball for now + (void) ptr; + if (strstr_P(type, PSTR("Title"))) { + strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title)); + audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0; + //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title); + } else { + // Who knows what to do? Not me! + } +} + +void StatusCallback(void *cbData, int code, const char *string) { + const char *ptr = reinterpret_cast(cbData); + (void) code; + (void) ptr; + //strncpy_P(status, string, sizeof(status)-1); + //status[sizeof(status)-1] = 0; +} + +void Webradio(const char *url) { + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; + AUDIO_PWR_ON + audio_i2s.ifile = new AudioFileSourceICYStream(url); + audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL); + audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize); + audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize); + audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL); + audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out); + if (!audio_i2s.decoder->isRunning()) { + // Serial.printf_P(PSTR("Can't connect to URL")); + StopPlaying(); + // strcpy_P(status, PSTR("Unable to connect to URL")); + audio_i2s.retryms = millis() + 2000; + } + + xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); +} + +void mp3_task2(void *arg){ + while (1) { + if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { + if (!audio_i2s.decoder->loop()) { + StopPlaying(); + //retryms = millis() + 2000; + } + delay(1); + } + } +} + +void StopPlaying() { + + if (audio_i2s.mp3_task_h) { + vTaskDelete(audio_i2s.mp3_task_h); + audio_i2s.mp3_task_h = nullptr; + } + + if (audio_i2s.decoder) { + audio_i2s.decoder->stop(); + delete audio_i2s.decoder; + audio_i2s.decoder = NULL; + } + + if (audio_i2s.buff) { + audio_i2s.buff->close(); + delete audio_i2s.buff; + audio_i2s.buff = NULL; + } + + if (audio_i2s.ifile) { + audio_i2s.ifile->close(); + delete audio_i2s.ifile; + audio_i2s.ifile = NULL; + } + AUDIO_PWR_OFF +} + +void Cmd_WebRadio(void) { + if (!audio_i2s.out) return; + + if (audio_i2s.decoder) { + StopPlaying(); + } + if (XdrvMailbox.data_len > 0) { + Webradio(XdrvMailbox.data); + ResponseCmndChar(XdrvMailbox.data); + } else { + ResponseCmndChar_P(PSTR("Stopped")); + } +} + +#ifdef USE_WEBSERVER +const char HTTP_WEBRADIO[] PROGMEM = + "{s}" "I2S_WR-Title" "{m}%s{e}"; + +void I2S_WR_Show(bool json) { + if (audio_i2s.decoder) { + if (json) { + ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title); + } else { + WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); + } + } +} +#endif // USE_WEBSERVER + +#endif // USE_I2S_WEBRADIO + + +#ifdef ESP32 +void Play_mp3(const char *path) { +#ifdef USE_UFILESYS + if (audio_i2s.decoder || audio_i2s.mp3) return; + if (!audio_i2s.out) return; + + bool I2S_Task; + + if (*path=='+') { + I2S_Task = true; + path++; + } else { + I2S_Task = false; + } + + FS *mp3fsp = ufsp; + + if (!strncmp(path, "/ffs", 4)) { + path += 4; + mp3fsp = ffsp; + } + + if (!mp3fsp->exists(path)) { + return; + } + + AUDIO_PWR_ON + + audio_i2s.file = new AudioFileSourceFS(*mp3fsp, path); + + audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); + + if (audio_i2s.mp3ram) { + audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); + } else { + audio_i2s.mp3 = new AudioGeneratorMP3(); + } + audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); + + if (I2S_Task) { + xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); + } else { + while (audio_i2s.mp3->isRunning()) { + if (!audio_i2s.mp3->loop()) { + audio_i2s.mp3->stop(); + break; + } + OsWatchLoop(); + } + audio_i2s.out->stop(); + mp3_delete(); + } + +#endif // USE_UFILESYS +} + +void mp3_delete(void) { + delete audio_i2s.file; + delete audio_i2s.id3; + delete audio_i2s.mp3; + audio_i2s.mp3=nullptr; + AUDIO_PWR_OFF +} +#endif // ESP32 + +void Say(char *text) { + + if (!audio_i2s.out) return; + + AUDIO_PWR_ON + + audio_i2s.out->begin(); + ESP8266SAM *sam = new ESP8266SAM; + sam->Say(audio_i2s.out, text); + delete sam; + audio_i2s.out->stop(); + + AUDIO_PWR_OFF +} + + +const char kI2SAudio_Commands[] PROGMEM = "I2S|" + "Say|Gain" +#ifdef USE_I2S_SAY_TIME + "|Time" +#endif // USE_I2S_SAY_TIME +#ifdef ESP32 + "|Play" +#ifdef USE_I2S_WEBRADIO + "|WR" +#endif // USE_I2S_WEBRADIO +#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) + "|REC" +#ifdef MP3_MIC_STREAM + "|STREAM" +#endif // MP3_MIC_STREAM +#endif // USE_SHINE +#endif // ESP32 +; + +void (* const I2SAudio_Command[])(void) PROGMEM = { + &Cmd_Say, &Cmd_Gain +#ifdef USE_I2S_SAY_TIME + ,&Cmd_Time +#endif // USE_I2S_SAY_TIME +#ifdef ESP32 + ,&Cmd_Play +#ifdef USE_I2S_WEBRADIO + ,&Cmd_WebRadio +#endif // USE_I2S_WEBRADIO +#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) + ,&Cmd_MicRec +#ifdef MP3_MIC_STREAM + ,&Cmd_MP3Stream +#endif // MP3_MIC_STREAM +#endif // USE_SHINE +#endif // ESP32 +}; + +void Cmd_Play(void) { + if (XdrvMailbox.data_len > 0) { + Play_mp3(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +void Cmd_Gain(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { + if (audio_i2s.out) { + audio_i2s.is2_volume=XdrvMailbox.payload; + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + } + } + ResponseCmndNumber(audio_i2s.is2_volume); +} + +void Cmd_Say(void) { + if (XdrvMailbox.data_len > 0) { + Say(XdrvMailbox.data); + } + ResponseCmndChar(XdrvMailbox.data); +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +void i2s_mp3_loop(void); +void i2s_mp3_init(void); +void MP3ShowStream(void); + +bool Xdrv42(uint8_t function) { + bool result = false; + + switch (function) { + case FUNC_COMMAND: + result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); + break; + case FUNC_INIT: + I2S_Init(); + break; +#if defined(MP3_MIC_STREAM) +//#if defined(USE_SHINE) && defined(MP3_MIC_STREAM) + case FUNC_WEB_ADD_MAIN_BUTTON: + //MP3ShowStream(); + break; + case FUNC_LOOP: + i2s_mp3_loop(); + break; + case FUNC_WEB_ADD_HANDLER: + audio_i2s.stream_enable = 1; + i2s_mp3_init(1); + break; +#endif +#ifdef USE_WEBSERVER +#ifdef USE_I2S_WEBRADIO + case FUNC_WEB_SENSOR: + I2S_WR_Show(false); + break; +#endif // USE_I2S_WEBRADIO +#endif // USE_WEBSERVER +#ifdef USE_I2S_WEBRADIO + case FUNC_JSON_APPEND: + I2S_WR_Show(true); + break; +#endif // USE_I2S_WEBRADIO + } + return result; +} + +#endif // USE_I2S_AUDIO diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino deleted file mode 100644 index 853b0d90e..000000000 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_audio.ino +++ /dev/null @@ -1,1049 +0,0 @@ -/* - xdrv_42_i2s_audio.ino - Audio dac support for Tasmota - - Copyright (C) 2021 Gerhard Mutz and Theo Arends - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) -/*********************************************************************************************\ - * I2S support using an external DAC or a speaker connected to GPIO03 using a transistor - * - * Uses fixed GPIOs for ESP8266: - * I2S Out Data GPIO03 (Rx) - * I2S Out Bit Clock GPIO15 - * I2S Out Word Select GPIO02 - * I2S In Data GPIO12 - * I2S In Bit Clock GPIO13 - * I2S In Word Select GPIO14 -\*********************************************************************************************/ - -#define XDRV_42 42 - -#define USE_I2S_EXTERNAL_DAC 1 -//#define USE_I2S_NO_DAC // Add support for transistor-based output without DAC -//#define USE_I2S_WEBRADIO // Add support for web radio -//#define USE_I2S_SAY_TIME // Add support for english speaking clock - -#include "AudioFileSourcePROGMEM.h" -#include "AudioFileSourceID3.h" -#include "AudioGeneratorMP3.h" -#ifdef USE_I2S_NO_DAC - #include "AudioOutputI2SNoDAC.h" // Transistor-driven lower quality connected to RX pin -#else - #include "AudioOutputI2S.h" // External I2S DAC IC -#endif // USE_I2S_NO_DAC -#include -#include "AudioFileSourceFS.h" -#ifdef USE_I2S_SAY_TIME - #include "AudioGeneratorTalkie.h" -#endif // USE_I2S_SAY_TIME -#include "AudioFileSourceICYStream.h" -#include "AudioFileSourceBuffer.h" -#include "AudioGeneratorAAC.h" - -#ifdef ESP32 -#include -#endif - -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON -#define AUDIO_PWR_OFF - -#ifdef ESP8266 -#define i2s_port_t uint8_t -#endif - -#ifdef ESP32 -#define MODE_MIC 0 -#define MODE_SPK 1 -#ifndef MICSRATE -#define MICSRATE 16000 -#endif -#endif // ESP32 - -struct AUDIO_I2S_t { - uint8_t is2_volume; // should be in settings - i2s_port_t i2s_port; - int8_t mclk = -1; - int8_t bclk = -1; - int8_t ws = -1; - int8_t dout = -1; - int8_t din = -1; - AudioGeneratorMP3 *mp3 = nullptr; - AudioFileSourceFS *file; -#ifdef USE_I2S_NO_DAC - AudioOutputI2SNoDAC *out; -#else - AudioOutputI2S *out; -#endif // USE_I2S_NO_DAC - AudioFileSourceID3 *id3; - AudioGeneratorMP3 *decoder = NULL; - void *mp3ram = NULL; -#ifdef USE_I2S_WEBRADIO - AudioFileSourceICYStream *ifile = NULL; - AudioFileSourceBuffer *buff = NULL; - char wr_title[64]; - void *preallocateBuffer = NULL; - void *preallocateCodec = NULL; - uint32_t retryms = 0; -#endif // USE_I2S_WEBRADIO - -#ifdef ESP32 - TaskHandle_t mp3_task_h; - TaskHandle_t mic_task_h; - uint32_t mic_size; - uint32_t mic_rate; - uint8_t *mic_buff; - char mic_path[32]; - uint8_t mic_channels; - File fwp; - uint8_t mic_stop; - int8_t mic_error; -#endif // ESP32 -} audio_i2s; - - -// because S3 box mclk severly disturbs WLAN -// we must slow down after each sound -#ifdef ESP32S3_BOX -#undef DOWNRATE -#define DOWNRATE audio_i2s.out->SetRate(1000); -#else -#undef DOWNRATE -#define DOWNRATE -#endif - -#define MIC_CHANNELS 1 - -#ifdef USE_TTGO_WATCH -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON TTGO_audio_power(true); -#define AUDIO_PWR_OFF TTGO_audio_power(false); -#endif // USE_TTGO_WATCH - -#ifdef USE_M5STACK_CORE2 -// leave this predefined currently -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON Core2AudioPower(true); -#define AUDIO_PWR_OFF Core2AudioPower(false); -#undef DAC_IIS_BCK -#undef DAC_IIS_WS -#undef DAC_IIS_DOUT -#define DAC_IIS_BCK 12 -#define DAC_IIS_WS 0 -#define DAC_IIS_DOUT 2 -#endif // USE_M5STACK_CORE2 - - -#ifdef ESP32S3_BOX -#undef AUDIO_PWR_ON -#undef AUDIO_PWR_OFF -#define AUDIO_PWR_ON S3boxAudioPower(true); -#define AUDIO_PWR_OFF S3boxAudioPower(false); -#undef MIC_CHANNELS -#define MIC_CHANNELS 2 -#endif // ESP32S3_BOX - -extern FS *ufsp; - -#ifdef ESP8266 -const int preallocateBufferSize = 5*1024; -const int preallocateCodecSize = 29192; // MP3 codec max mem needed -#endif // ESP8266 - -#ifdef ESP32 -const int preallocateBufferSize = 16*1024; -const int preallocateCodecSize = 29192; // MP3 codec max mem needed -//const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed -#endif // ESP32 - - -#ifdef USE_I2S_SAY_TIME -long timezone = 2; -byte daysavetime = 1; - -uint8_t spTHE[] PROGMEM = {0x08,0xE8,0x3E,0x55,0x01,0xC3,0x86,0x27,0xAF,0x72,0x0D,0x4D,0x97,0xD5,0xBC,0x64,0x3C,0xF2,0x5C,0x51,0xF1,0x93,0x36,0x8F,0x4F,0x59,0x2A,0x42,0x7A,0x32,0xC3,0x64,0xFF,0x3F}; -uint8_t spTIME[] PROGMEM = {0x0E,0x28,0xAC,0x2D,0x01,0x5D,0xB6,0x0D,0x33,0xF3,0x54,0xB3,0x60,0xBA,0x8C,0x54,0x5C,0xCD,0x2D,0xD4,0x32,0x73,0x0F,0x8E,0x34,0x33,0xCB,0x4A,0x25,0xD4,0x25,0x83,0x2C,0x2B,0xD5,0x50,0x97,0x08,0x32,0xEC,0xD4,0xDC,0x4C,0x33,0xC8,0x70,0x73,0x0F,0x33,0xCD,0x20,0xC3,0xCB,0x43,0xDD,0x3C,0xCD,0x8C,0x20,0x77,0x89,0xF4,0x94,0xB2,0xE2,0xE2,0x35,0x22,0x5D,0xD6,0x4A,0x8A,0x96,0xCC,0x36,0x25,0x2D,0xC9,0x9A,0x7B,0xC2,0x18,0x87,0x24,0x4B,0x1C,0xC9,0x50,0x19,0x92,0x2C,0x71,0x34,0x4B,0x45,0x8A,0x8B,0xC4,0x96,0xB6,0x5A,0x29,0x2A,0x92,0x5A,0xCA,0x53,0x96,0x20,0x05,0x09,0xF5,0x92,0x5D,0xBC,0xE8,0x58,0x4A,0xDD,0xAE,0x73,0xBD,0x65,0x4B,0x8D,0x78,0xCA,0x2B,0x4E,0xD8,0xD9,0xED,0x22,0x20,0x06,0x75,0x00,0x00,0x80,0xFF,0x07}; -uint8_t spIS[] PROGMEM = {0x21,0x18,0x96,0x38,0xB7,0x14,0x8D,0x60,0x3A,0xA6,0xE8,0x51,0xB4,0xDC,0x2E,0x48,0x7B,0x5A,0xF1,0x70,0x1B,0xA3,0xEC,0x09,0xC6,0xCB,0xEB,0x92,0x3D,0xA7,0x69,0x1F,0xAF,0x71,0x89,0x9C,0xA2,0xB3,0xFC,0xCA,0x35,0x72,0x9A,0xD1,0xF0,0xAB,0x12,0xB3,0x2B,0xC6,0xCD,0x4F,0xCC,0x32,0x26,0x19,0x07,0xDF,0x0B,0x8F,0xB8,0xA4,0xED,0x7C,0xCF,0x23,0x62,0x8B,0x8E,0xF1,0x23,0x0A,0x8B,0x6E,0xCB,0xCE,0xEF,0x54,0x44,0x3C,0xDC,0x08,0x60,0x0B,0x37,0x01,0x1C,0x53,0x26,0x80,0x15,0x4E,0x14,0xB0,0x54,0x2B,0x02,0xA4,0x69,0xFF,0x7F}; -uint8_t spA_M_[] PROGMEM = {0xCD,0xEF,0x86,0xAB,0x57,0x6D,0x0F,0xAF,0x71,0xAD,0x49,0x55,0x3C,0xFC,0x2E,0xC5,0xB7,0x5C,0xF1,0xF2,0x87,0x66,0xDD,0x4E,0xC5,0xC3,0xEF,0x92,0xE2,0x3A,0x65,0xB7,0xA0,0x09,0xAA,0x1B,0x97,0x54,0x82,0x2E,0x28,0x77,0x5C,0x52,0x09,0x1A,0xA3,0xB8,0x76,0x49,0x25,0x68,0x8C,0x73,0xDB,0x24,0x95,0xA0,0x32,0xA9,0x6B,0xA7,0xD9,0x82,0x26,0xA9,0x76,0x42,0xD6,0x08,0xBA,0xE1,0xE8,0x0E,0x5A,0x2B,0xEA,0x9E,0x3D,0x27,0x18,0xAD,0xA8,0x07,0xF1,0x98,0x90,0x35,0xA2,0x96,0x44,0xA3,0x5D,0x66,0x8B,0x6B,0x12,0xCD,0x32,0x85,0x25,0xC9,0x81,0x2D,0xC3,0x64,0x85,0x34,0x58,0x89,0x94,0x52,0x1C,0x52,0x2F,0x35,0xDA,0xC7,0x51,0x48,0x23,0x97,0xCC,0x2C,0x97,0x2E,0xF3,0x5C,0xF3,0xA2,0x14,0xBA,0x2C,0x48,0xCE,0xCA,0x76,0xE8,0x32,0x2F,0x34,0xB2,0xDB,0x85,0xC9,0x83,0x90,0xA8,0x2C,0x57,0x26,0x8F,0x9C,0xBD,0xA2,0x53,0xD9,0xC2,0x54,0x59,0x28,0x99,0x4B,0x2C,0x5D,0xFF,0x3F}; -uint8_t spP_M_[] PROGMEM = {0x0E,0x98,0x41,0x54,0x00,0x43,0xA0,0x05,0xAB,0x42,0x8E,0x1D,0xA3,0x15,0xEC,0x4E,0x58,0xF7,0x92,0x66,0x70,0x1B,0x66,0xDB,0x73,0x99,0xC1,0xEB,0x98,0xED,0xD6,0x25,0x25,0x6F,0x70,0x92,0xDD,0x64,0xD8,0xFC,0x61,0xD0,0x66,0x83,0xD6,0x0A,0x86,0x23,0xAB,0x69,0xDA,0x2B,0x18,0x9E,0x3D,0x37,0x69,0x9D,0xA8,0x07,0x71,0x9F,0xA0,0xBD,0xA2,0x16,0xD5,0x7C,0x54,0xF6,0x88,0x6B,0x54,0x8B,0x34,0x49,0x2D,0x29,0x49,0x3C,0x34,0x64,0xA5,0x24,0x1B,0x36,0xD7,0x72,0x13,0x92,0xA4,0xC4,0x2D,0xC3,0xB3,0x4B,0xA3,0x62,0x0F,0x2B,0x37,0x6E,0x8B,0x5A,0xD4,0x3D,0xDD,0x9A,0x2D,0x50,0x93,0xF6,0x4C,0xAA,0xB6,0xC4,0x85,0x3B,0xB2,0xB1,0xD8,0x93,0x20,0x4D,0x8F,0x24,0xFF,0x0F}; -uint8_t spOH[] PROGMEM = {0xC6,0xC9,0x71,0x5A,0xA2,0x92,0x14,0x2F,0x6E,0x97,0x9C,0x46,0x9D,0xDC,0xB0,0x4D,0x62,0x1B,0x55,0x70,0xDD,0x55,0xBE,0x0E,0x36,0xC1,0x33,0x37,0xA9,0xA7,0x51,0x1B,0xCF,0x3C,0xA5,0x9E,0x44,0xAC,0x3C,0x7D,0x98,0x7B,0x52,0x96,0x72,0x65,0x4B,0xF6,0x1A,0xD9,0xCA,0xF5,0x91,0x2D,0xA2,0x2A,0x4B,0xF7,0xFF,0x01}; -uint8_t spOCLOCK[] PROGMEM = {0x21,0x4E,0x3D,0xB8,0x2B,0x19,0xBB,0x24,0x0E,0xE5,0xEC,0x60,0xE4,0xF2,0x90,0x13,0xD4,0x2A,0x11,0x80,0x00,0x42,0x69,0x26,0x40,0xD0,0x2B,0x04,0x68,0xE0,0x4D,0x00,0x3A,0x35,0x35,0x33,0xB6,0x51,0xD9,0x64,0x34,0x82,0xB4,0x9A,0x63,0x92,0x55,0x89,0x52,0x5B,0xCA,0x2E,0x34,0x25,0x4E,0x63,0x28,0x3A,0x50,0x95,0x26,0x8D,0xE6,0xAA,0x64,0x58,0xEA,0x92,0xCE,0xC2,0x46,0x15,0x9B,0x86,0xCD,0x2A,0x2E,0x37,0x00,0x00,0x00,0x0C,0xC8,0xDD,0x05,0x01,0xB9,0x33,0x21,0xA0,0x74,0xD7,0xFF,0x07}; -uint8_t spONE[] PROGMEM = {0xCC,0x67,0x75,0x42,0x59,0x5D,0x3A,0x4F,0x9D,0x36,0x63,0xB7,0x59,0xDC,0x30,0x5B,0x5C,0x23,0x61,0xF3,0xE2,0x1C,0xF1,0xF0,0x98,0xC3,0x4B,0x7D,0x39,0xCA,0x1D,0x2C,0x2F,0xB7,0x15,0xEF,0x70,0x79,0xBC,0xD2,0x46,0x7C,0x52,0xE5,0xF1,0x4A,0x6A,0xB3,0x71,0x47,0xC3,0x2D,0x39,0x34,0x4B,0x23,0x35,0xB7,0x7A,0x55,0x33,0x8F,0x59,0xDC,0xA2,0x44,0xB5,0xBC,0x66,0x72,0x8B,0x64,0xF5,0xF6,0x98,0xC1,0x4D,0x42,0xD4,0x27,0x62,0x38,0x2F,0x4A,0xB6,0x9C,0x88,0x68,0xBC,0xA6,0x95,0xF8,0x5C,0xA1,0x09,0x86,0x77,0x91,0x11,0x5B,0xFF,0x0F}; -uint8_t spTWO[] PROGMEM = {0x0E,0x38,0x6E,0x25,0x00,0xA3,0x0D,0x3A,0xA0,0x37,0xC5,0xA0,0x05,0x9E,0x56,0x35,0x86,0xAA,0x5E,0x8C,0xA4,0x82,0xB2,0xD7,0x74,0x31,0x22,0x69,0xAD,0x1C,0xD3,0xC1,0xD0,0xFA,0x28,0x2B,0x2D,0x47,0xC3,0x1B,0xC2,0xC4,0xAE,0xC6,0xCD,0x9C,0x48,0x53,0x9A,0xFF,0x0F}; -uint8_t spTHREE[] PROGMEM = {0x02,0xD8,0x2E,0x9C,0x01,0xDB,0xA6,0x33,0x60,0xFB,0x30,0x01,0xEC,0x20,0x12,0x8C,0xE4,0xD8,0xCA,0x32,0x96,0x73,0x63,0x41,0x39,0x89,0x98,0xC1,0x4D,0x0D,0xED,0xB0,0x2A,0x05,0x37,0x0F,0xB4,0xA5,0xAE,0x5C,0xDC,0x36,0xD0,0x83,0x2F,0x4A,0x71,0x7B,0x03,0xF7,0x38,0x59,0xCD,0xED,0x1E,0xB4,0x6B,0x14,0x35,0xB7,0x6B,0x94,0x99,0x91,0xD5,0xDC,0x26,0x48,0x77,0x4B,0x66,0x71,0x1B,0x21,0xDB,0x2D,0x8A,0xC9,0x6D,0x88,0xFC,0x26,0x28,0x3A,0xB7,0x21,0xF4,0x1F,0xA3,0x65,0xBC,0x02,0x38,0xBB,0x3D,0x8E,0xF0,0x2B,0xE2,0x08,0xB7,0x34,0xFF,0x0F}; -uint8_t spFOUR[] PROGMEM = {0x0C,0x18,0xB6,0x9A,0x01,0xC3,0x75,0x09,0x60,0xD8,0x0E,0x09,0x30,0xA0,0x9B,0xB6,0xA0,0xBB,0xB0,0xAA,0x16,0x4E,0x82,0xEB,0xEA,0xA9,0xFA,0x59,0x49,0x9E,0x59,0x23,0x9A,0x27,0x3B,0x78,0x66,0xAE,0x4A,0x9C,0x9C,0xE0,0x99,0xD3,0x2A,0xBD,0x72,0x92,0xEF,0xE6,0x88,0xE4,0x45,0x4D,0x7E,0x98,0x2D,0x62,0x67,0x37,0xF9,0xA1,0x37,0xA7,0x6C,0x94,0xE4,0xC7,0x1E,0xDC,0x3C,0xA5,0x83,0x1F,0x8B,0xEB,0x52,0x0E,0x0E,0x7E,0x2E,0x4E,0xC7,0x31,0xD2,0x79,0xA5,0x3A,0x0D,0xD9,0xC4,0xFF,0x07}; -uint8_t spFIVE[] PROGMEM = {0x02,0xE8,0x3E,0x8C,0x01,0xDD,0x65,0x08,0x60,0x98,0x4C,0x06,0x34,0x93,0xCE,0x80,0xE6,0xDA,0x9A,0x14,0x6B,0xAA,0x47,0xD1,0x5E,0x56,0xAA,0x6D,0x56,0xCD,0x78,0xD9,0xA9,0x1C,0x67,0x05,0x83,0xE1,0xA4,0xBA,0x38,0xEE,0x16,0x86,0x9B,0xFA,0x60,0x87,0x5B,0x18,0x6E,0xEE,0x8B,0x1D,0x6E,0x61,0xB9,0x69,0x36,0x65,0xBA,0x8D,0xE5,0xE5,0x3E,0x1C,0xE9,0x0E,0x96,0x9B,0x5B,0xAB,0x95,0x2B,0x58,0x6E,0xCE,0xE5,0x3A,0x6A,0xF3,0xB8,0x35,0x84,0x7B,0x05,0xA3,0xE3,0x36,0xEF,0x92,0x19,0xB4,0x86,0xDB,0xB4,0x69,0xB4,0xD1,0x2A,0x4E,0x65,0x9A,0x99,0xCE,0x28,0xD9,0x85,0x71,0x4C,0x18,0x6D,0x67,0x47,0xC6,0x5E,0x53,0x4A,0x9C,0xB5,0xE2,0x85,0x45,0x26,0xFE,0x7F}; -uint8_t spSIX[] PROGMEM = {0x0E,0xD8,0xAE,0xDD,0x03,0x0E,0x38,0xA6,0xD2,0x01,0xD3,0xB4,0x2C,0xAD,0x6A,0x35,0x9D,0xB1,0x7D,0xDC,0xEE,0xC4,0x65,0xD7,0xF1,0x72,0x47,0x24,0xB3,0x19,0xD9,0xD9,0x05,0x70,0x40,0x49,0xEA,0x02,0x98,0xBE,0x42,0x01,0xDF,0xA4,0x69,0x40,0x00,0xDF,0x95,0xFC,0x3F}; -uint8_t spSEVEN[] PROGMEM = {0x02,0xB8,0x3A,0x8C,0x01,0xDF,0xA4,0x73,0x40,0x01,0x47,0xB9,0x2F,0x33,0x3B,0x73,0x5F,0x53,0x7C,0xEC,0x9A,0xC5,0x63,0xD5,0xD1,0x75,0xAE,0x5B,0xFC,0x64,0x5C,0x35,0x87,0x91,0xF1,0x83,0x36,0xB5,0x68,0x55,0xC5,0x6F,0xDA,0x45,0x2D,0x1C,0x2D,0xB7,0x38,0x37,0x9F,0x60,0x3C,0xBC,0x9A,0x85,0xA3,0x25,0x66,0xF7,0x8A,0x57,0x1C,0xA9,0x67,0x56,0xCA,0x5E,0xF0,0xB2,0x16,0xB2,0xF1,0x89,0xCE,0x8B,0x92,0x25,0xC7,0x2B,0x33,0xCF,0x48,0xB1,0x99,0xB4,0xF3,0xFF}; -uint8_t spEIGHT[] PROGMEM = {0xC3,0x6C,0x86,0xB3,0x27,0x6D,0x0F,0xA7,0x48,0x99,0x4E,0x55,0x3C,0xBC,0x22,0x65,0x36,0x4D,0xD1,0xF0,0x32,0xD3,0xBE,0x34,0xDA,0xC3,0xEB,0x82,0xE2,0xDA,0x65,0x35,0xAF,0x31,0xF2,0x6B,0x97,0x95,0xBC,0x86,0xD8,0x6F,0x82,0xA6,0x73,0x0B,0xC6,0x9E,0x72,0x99,0xCC,0xCB,0x02,0xAD,0x3C,0x9A,0x10,0x60,0xAB,0x62,0x05,0x2C,0x37,0x84,0x00,0xA9,0x73,0x00,0x00,0xFE,0x1F}; -uint8_t spNINE[] PROGMEM = {0xCC,0xA1,0x26,0xBB,0x83,0x93,0x18,0xCF,0x4A,0xAD,0x2E,0x31,0xED,0x3C,0xA7,0x24,0x26,0xC3,0x54,0xF1,0x92,0x64,0x8B,0x8A,0x98,0xCB,0x2B,0x2E,0x34,0x53,0x2D,0x0E,0x2F,0x57,0xB3,0x0C,0x0D,0x3C,0xBC,0x3C,0x4C,0x4B,0xCA,0xF4,0xF0,0x72,0x0F,0x6E,0x49,0x53,0xCD,0xCB,0x53,0x2D,0x35,0x4D,0x0F,0x2F,0x0F,0xD7,0x0C,0x0D,0x3D,0xBC,0xDC,0x4D,0xD3,0xDD,0xC2,0xF0,0x72,0x52,0x4F,0x57,0x9B,0xC3,0xAB,0x89,0xBD,0x42,0x2D,0x0F,0xAF,0x5A,0xD1,0x71,0x91,0x55,0xBC,0x2C,0xC5,0x3B,0xD8,0x65,0xF2,0x82,0x94,0x18,0x4E,0x3B,0xC1,0x73,0x42,0x32,0x33,0x15,0x45,0x4F,0x79,0x52,0x6A,0x55,0xA6,0xA3,0xFF,0x07}; -uint8_t spTEN[] PROGMEM = {0x0E,0xD8,0xB1,0xDD,0x01,0x3D,0xA8,0x24,0x7B,0x04,0x27,0x76,0x77,0xDC,0xEC,0xC2,0xC5,0x23,0x84,0xCD,0x72,0x9A,0x51,0xF7,0x62,0x45,0xC7,0xEB,0x4E,0x35,0x4A,0x14,0x2D,0xBF,0x45,0xB6,0x0A,0x75,0xB8,0xFC,0x16,0xD9,0x2A,0xD9,0xD6,0x0A,0x5A,0x10,0xCD,0xA2,0x48,0x23,0xA8,0x81,0x35,0x4B,0x2C,0xA7,0x20,0x69,0x0A,0xAF,0xB6,0x15,0x82,0xA4,0x29,0x3C,0xC7,0x52,0x08,0xA2,0x22,0xCF,0x68,0x4B,0x2E,0xF0,0x8A,0xBD,0xA3,0x2C,0xAB,0x40,0x1B,0xCE,0xAA,0xB2,0x6C,0x82,0x40,0x4D,0x7D,0xC2,0x89,0x88,0x8A,0x61,0xCC,0x74,0xD5,0xFF,0x0F}; -uint8_t spELEVEN[] PROGMEM = {0xC3,0xCD,0x76,0x5C,0xAE,0x14,0x0F,0x37,0x9B,0x71,0xDE,0x92,0x55,0xBC,0x2C,0x27,0x70,0xD3,0x76,0xF0,0x83,0x5E,0xA3,0x5E,0x5A,0xC1,0xF7,0x61,0x58,0xA7,0x19,0x35,0x3F,0x99,0x31,0xDE,0x52,0x74,0xFC,0xA2,0x26,0x64,0x4B,0xD1,0xF1,0xAB,0xAE,0xD0,0x2D,0xC5,0xC7,0x2F,0x36,0xDD,0x27,0x15,0x0F,0x3F,0xD9,0x08,0x9F,0x62,0xE4,0xC2,0x2C,0xD4,0xD8,0xD3,0x89,0x0B,0x1B,0x57,0x11,0x0B,0x3B,0xC5,0xCF,0xD6,0xCC,0xC6,0x64,0x35,0xAF,0x18,0x73,0x1F,0xA1,0x5D,0xBC,0x62,0x45,0xB3,0x45,0x51,0xF0,0xA2,0x62,0xAB,0x4A,0x5B,0xC9,0x4B,0x8A,0x2D,0xB3,0x6C,0x06,0x2F,0x29,0xB2,0xAC,0x8A,0x18,0xBC,0x28,0xD9,0xAA,0xD2,0x92,0xF1,0xBC,0xE0,0x98,0x8C,0x48,0xCC,0x17,0x52,0xA3,0x27,0x6D,0x93,0xD0,0x4B,0x8E,0x0E,0x77,0x02,0x00,0xFF,0x0F}; -uint8_t spTWELVE[] PROGMEM = {0x06,0x28,0x46,0xD3,0x01,0x25,0x06,0x13,0x20,0xBA,0x70,0x70,0xB6,0x79,0xCA,0x36,0xAE,0x28,0x38,0xE1,0x29,0xC5,0x35,0xA3,0xE6,0xC4,0x16,0x6A,0x53,0x8C,0x97,0x9B,0x72,0x86,0x4F,0x28,0x1A,0x6E,0x0A,0x59,0x36,0xAE,0x68,0xF8,0x29,0x67,0xFA,0x06,0xA3,0x16,0xC4,0x96,0xE6,0x53,0xAC,0x5A,0x9C,0x56,0x72,0x77,0x31,0x4E,0x49,0x5C,0x8D,0x5B,0x29,0x3B,0x24,0x61,0x1E,0x6C,0x9B,0x6C,0x97,0xF8,0xA7,0x34,0x19,0x92,0x4C,0x62,0x9E,0x72,0x65,0x58,0x12,0xB1,0x7E,0x09,0xD5,0x2E,0x53,0xC5,0xBA,0x36,0x6B,0xB9,0x2D,0x17,0x05,0xEE,0x9A,0x6E,0x8E,0x05,0x50,0x6C,0x19,0x07,0x18,0x50,0xBD,0x3B,0x01,0x92,0x08,0x41,0x40,0x10,0xA6,0xFF,0x0F}; -uint8_t spTHIRTEEN[] PROGMEM = {0x08,0xE8,0x2C,0x15,0x01,0x43,0x07,0x13,0xE0,0x98,0xB4,0xA6,0x35,0xA9,0x1E,0xDE,0x56,0x8E,0x53,0x9C,0x7A,0xE7,0xCA,0x5E,0x76,0x8D,0x94,0xE5,0x2B,0xAB,0xD9,0xB5,0x62,0xA4,0x9C,0xE4,0xE6,0xB4,0x41,0x1E,0x7C,0xB6,0x93,0xD7,0x16,0x99,0x5A,0xCD,0x61,0x76,0x55,0xC2,0x91,0x61,0x1B,0xC0,0x01,0x5D,0x85,0x05,0xE0,0x68,0x51,0x07,0x1C,0xA9,0x64,0x80,0x1D,0x4C,0x9C,0x95,0x88,0xD4,0x04,0x3B,0x4D,0x4E,0x21,0x5C,0x93,0xA8,0x26,0xB9,0x05,0x4B,0x6E,0xA0,0xE2,0xE4,0x57,0xC2,0xB9,0xC1,0xB2,0x93,0x5F,0x09,0xD7,0x24,0xCB,0x4E,0x41,0x25,0x54,0x1D,0x62,0x3B,0x05,0x8D,0x52,0x57,0xAA,0xAD,0x10,0x24,0x26,0xE3,0xE1,0x36,0x5D,0x10,0x85,0xB4,0x97,0x85,0x72,0x41,0x14,0x52,0x5E,0x1A,0xCA,0xF9,0x91,0x6B,0x7A,0x5B,0xC4,0xE0,0x17,0x2D,0x54,0x1D,0x92,0x8C,0x1F,0x25,0x4B,0x8F,0xB2,0x16,0x41,0xA1,0x4A,0x3E,0xE6,0xFA,0xFF,0x01}; -uint8_t spFOURTEEN[] PROGMEM = {0x0C,0x58,0xAE,0x5C,0x01,0xD9,0x87,0x07,0x51,0xB7,0x25,0xB3,0x8A,0x15,0x2C,0xF7,0x1C,0x35,0x87,0x4D,0xB2,0xDD,0x53,0xCE,0x28,0x2B,0xC9,0x0E,0x97,0x2D,0xBD,0x2A,0x17,0x27,0x76,0x8E,0xD2,0x9A,0x6C,0x80,0x94,0x71,0x00,0x00,0x02,0xB0,0x58,0x58,0x00,0x9E,0x0B,0x0A,0xC0,0xB2,0xCE,0xC1,0xC8,0x98,0x7A,0x52,0x95,0x24,0x2B,0x11,0xED,0x36,0xD4,0x92,0xDC,0x4C,0xB5,0xC7,0xC8,0x53,0xF1,0x2A,0xE5,0x1A,0x17,0x55,0xC5,0xAF,0x94,0xBB,0xCD,0x1C,0x26,0xBF,0x52,0x9A,0x72,0x53,0x98,0xFC,0xC2,0x68,0xD2,0x4D,0x61,0xF0,0xA3,0x90,0xB6,0xD6,0x50,0xC1,0x8F,0x42,0xDA,0x4A,0x43,0x39,0x3F,0x48,0x2D,0x6B,0x33,0xF9,0xFF}; -uint8_t spFIFTEEN[] PROGMEM = {0x08,0xE8,0x2A,0x0D,0x01,0xDD,0xBA,0x31,0x60,0x6A,0xF7,0xA0,0xAE,0x54,0xAA,0x5A,0x76,0x97,0xD9,0x34,0x69,0xEF,0x32,0x1E,0x66,0xE1,0xE2,0xB3,0x43,0xA9,0x18,0x55,0x92,0x4E,0x37,0x2D,0x67,0x6F,0xDF,0xA2,0x5A,0xB6,0x04,0x30,0x55,0xA8,0x00,0x86,0x09,0xE7,0x00,0x01,0x16,0x17,0x05,0x70,0x40,0x57,0xE5,0x01,0xF8,0x21,0x34,0x00,0xD3,0x19,0x33,0x80,0x89,0x9A,0x62,0x34,0x4C,0xD5,0x49,0xAE,0x8B,0x53,0x09,0xF7,0x26,0xD9,0x6A,0x7E,0x23,0x5C,0x13,0x12,0xB3,0x04,0x9D,0x50,0x4F,0xB1,0xAD,0x14,0x15,0xC2,0xD3,0xA1,0xB6,0x42,0x94,0xA8,0x8C,0x87,0xDB,0x74,0xB1,0x70,0x59,0xE1,0x2E,0xC9,0xC5,0x81,0x5B,0x55,0xA4,0x4C,0x17,0x47,0xC1,0x6D,0xE3,0x81,0x53,0x9C,0x84,0x6A,0x46,0xD9,0x4C,0x51,0x31,0x42,0xD9,0x66,0xC9,0x44,0x85,0x29,0x6A,0x9B,0xAD,0xFF,0x07}; -uint8_t spSIXTEEN[] PROGMEM = {0x0A,0x58,0x5A,0x5D,0x00,0x93,0x97,0x0B,0x60,0xA9,0x48,0x05,0x0C,0x15,0xAE,0x80,0xAD,0x3D,0x14,0x30,0x7D,0xD9,0x50,0x92,0x92,0xAC,0x0D,0xC5,0xCD,0x2A,0x82,0xAA,0x3B,0x98,0x04,0xB3,0x4A,0xC8,0x9A,0x90,0x05,0x09,0x68,0x51,0xD4,0x01,0x23,0x9F,0x1A,0x60,0xA9,0x12,0x03,0xDC,0x50,0x81,0x80,0x22,0xDC,0x20,0x00,0xCB,0x06,0x3A,0x60,0x16,0xE3,0x64,0x64,0x42,0xDD,0xCD,0x6A,0x8A,0x5D,0x28,0x75,0x07,0xA9,0x2A,0x5E,0x65,0x34,0xED,0x64,0xBB,0xF8,0x85,0xF2,0x94,0x8B,0xAD,0xE4,0x37,0x4A,0x5B,0x21,0xB6,0x52,0x50,0x19,0xAD,0xA7,0xD8,0x4A,0x41,0x14,0xDA,0x5E,0x12,0x3A,0x04,0x91,0x4B,0x7B,0x69,0xA8,0x10,0x24,0x2E,0xE5,0xA3,0x81,0x52,0x90,0x94,0x5A,0x55,0x98,0x32,0x41,0x50,0xCC,0x93,0x2E,0x47,0x85,0x89,0x1B,0x5B,0x5A,0x62,0x04,0x44,0xE3,0x02,0x80,0x80,0x64,0xDD,0xFF,0x1F}; -uint8_t spSEVENTEEN[] PROGMEM = {0x02,0x98,0x3A,0x42,0x00,0x5B,0xA6,0x09,0x60,0xDB,0x52,0x06,0x1C,0x93,0x29,0x80,0xA9,0x52,0x87,0x9A,0xB5,0x99,0x4F,0xC8,0x3E,0x46,0xD6,0x5E,0x7E,0x66,0xFB,0x98,0xC5,0x5A,0xC6,0x9A,0x9C,0x63,0x15,0x6B,0x11,0x13,0x8A,0x9C,0x97,0xB9,0x9A,0x5A,0x39,0x71,0xEE,0xD2,0x29,0xC2,0xA6,0xB8,0x58,0x59,0x99,0x56,0x14,0xA3,0xE1,0x26,0x19,0x19,0xE3,0x8C,0x93,0x17,0xB4,0x46,0xB5,0x88,0x71,0x9E,0x97,0x9E,0xB1,0x2C,0xC5,0xF8,0x56,0xC4,0x58,0xA3,0x1C,0xE1,0x33,0x9D,0x13,0x41,0x8A,0x43,0x58,0xAD,0x95,0xA9,0xDB,0x36,0xC0,0xD1,0xC9,0x0E,0x58,0x4E,0x45,0x01,0x23,0xA9,0x04,0x37,0x13,0xAE,0x4D,0x65,0x52,0x82,0xCA,0xA9,0x37,0x99,0x4D,0x89,0xBA,0xC0,0xBC,0x14,0x36,0x25,0xEA,0x1C,0x73,0x52,0x1D,0x97,0xB8,0x33,0xAC,0x0E,0x75,0x9C,0xE2,0xCE,0xB0,0xDA,0xC3,0x51,0x4A,0x1A,0xA5,0xCA,0x70,0x5B,0x21,0xCE,0x4C,0x26,0xD2,0x6C,0xBA,0x38,0x71,0x2E,0x1F,0x2D,0xED,0xE2,0x24,0xB8,0xBC,0x3D,0x52,0x88,0xAB,0x50,0x8E,0xA8,0x48,0x22,0x4E,0x42,0xA0,0x26,0x55,0xFD,0x3F}; -uint8_t spEIGHTEEN[] PROGMEM = {0x2E,0x9C,0xD1,0x4D,0x54,0xEC,0x2C,0xBF,0x1B,0x8A,0x99,0x70,0x7C,0xFC,0x2E,0x29,0x6F,0x52,0xF6,0xF1,0xBA,0x20,0xBF,0x36,0xD9,0xCD,0xED,0x0C,0xF3,0x27,0x64,0x17,0x73,0x2B,0xA2,0x99,0x90,0x65,0xEC,0xED,0x40,0x73,0x32,0x12,0xB1,0xAF,0x30,0x35,0x0B,0xC7,0x00,0xE0,0x80,0xAE,0xDD,0x1C,0x70,0x43,0xAA,0x03,0x86,0x51,0x36,0xC0,0x30,0x64,0xCE,0x4C,0x98,0xFB,0x5C,0x65,0x07,0xAF,0x10,0xEA,0x0B,0x66,0x1B,0xFC,0x46,0xA8,0x3E,0x09,0x4D,0x08,0x2A,0xA6,0x3E,0x67,0x36,0x21,0x2A,0x98,0x67,0x9D,0x15,0xA7,0xA8,0x60,0xEE,0xB6,0x94,0x99,0xA2,0x4A,0x78,0x22,0xC2,0xA6,0x8B,0x8C,0x8E,0xCC,0x4C,0x8A,0x2E,0x8A,0x4C,0xD3,0x57,0x03,0x87,0x28,0x71,0x09,0x1F,0x2B,0xE4,0xA2,0xC4,0xC5,0x6D,0xAD,0x54,0x88,0xB2,0x63,0xC9,0xF2,0x50,0x2E,0x8A,0x4A,0x38,0x4A,0xEC,0x88,0x28,0x08,0xE3,0x28,0x49,0xF3,0xFF}; -uint8_t spNINETEEN[] PROGMEM = {0xC2,0xEA,0x8A,0x95,0x2B,0x6A,0x05,0x3F,0x71,0x71,0x5F,0x0D,0x12,0xFC,0x28,0x25,0x62,0x35,0xF0,0xF0,0xB3,0x48,0x1E,0x0F,0xC9,0xCB,0x2F,0x45,0x7C,0x2C,0x25,0x1F,0xBF,0x14,0xB3,0x2C,0xB5,0x75,0xFC,0x5A,0x5C,0xA3,0x5D,0xE1,0xF1,0x7A,0x76,0xB3,0x4E,0x45,0xC7,0xED,0x96,0x23,0x3B,0x18,0x37,0x7B,0x18,0xCC,0x09,0x51,0x13,0x4C,0xAB,0x6C,0x4C,0x4B,0x96,0xD2,0x49,0xAA,0x36,0x0B,0xC5,0xC2,0x20,0x26,0x27,0x35,0x63,0x09,0x3D,0x30,0x8B,0xF0,0x48,0x5C,0xCA,0x61,0xDD,0xCB,0xCD,0x91,0x03,0x8E,0x4B,0x76,0xC0,0xCC,0x4D,0x06,0x98,0x31,0x31,0x98,0x99,0x70,0x6D,0x2A,0xA3,0xE4,0x16,0xCA,0xBD,0xCE,0x5C,0x92,0x57,0x28,0xCF,0x09,0x69,0x2E,0x7E,0xA5,0x3C,0x63,0xA2,0x30,0x05,0x95,0xD2,0x74,0x98,0xCD,0x14,0x54,0xCA,0x53,0xA9,0x96,0x52,0x50,0x28,0x6F,0xBA,0xCB,0x0C,0x41,0x50,0xDE,0x65,0x2E,0xD3,0x05,0x89,0x4B,0x7B,0x6B,0x20,0x17,0x44,0xAE,0xED,0x23,0x81,0x52,0x90,0x85,0x73,0x57,0xD0,0x72,0x41,0xB1,0x02,0xDE,0x2E,0xDB,0x04,0x89,0x05,0x79,0xBB,0x62,0xE5,0x76,0x11,0xCA,0x61,0x0E,0xFF,0x1F}; -uint8_t spTWENTY[] PROGMEM = {0x01,0x98,0xD1,0xC2,0x00,0xCD,0xA4,0x32,0x20,0x79,0x13,0x04,0x28,0xE7,0x92,0xDC,0x70,0xCC,0x5D,0xDB,0x76,0xF3,0xD2,0x32,0x0B,0x0B,0x5B,0xC3,0x2B,0xCD,0xD4,0xDD,0x23,0x35,0xAF,0x44,0xE1,0xF0,0xB0,0x6D,0x3C,0xA9,0xAD,0x3D,0x35,0x0E,0xF1,0x0C,0x8B,0x28,0xF7,0x34,0x01,0x68,0x22,0xCD,0x00,0xC7,0xA4,0x04,0xBB,0x32,0xD6,0xAC,0x56,0x9C,0xDC,0xCA,0x28,0x66,0x53,0x51,0x70,0x2B,0xA5,0xBC,0x0D,0x9A,0xC1,0xEB,0x14,0x73,0x37,0x29,0x19,0xAF,0x33,0x8C,0x3B,0xA7,0x24,0xBC,0x42,0xB0,0xB7,0x59,0x09,0x09,0x3C,0x96,0xE9,0xF4,0x58,0xFF,0x0F}; -uint8_t spTHIRTY[] PROGMEM = {0x08,0x98,0xD6,0x15,0x01,0x43,0xBB,0x0A,0x20,0x1B,0x8B,0xE5,0x16,0xA3,0x1E,0xB6,0xB6,0x96,0x97,0x3C,0x57,0xD4,0x2A,0x5E,0x7E,0x4E,0xD8,0xE1,0x6B,0x7B,0xF8,0x39,0x63,0x0D,0x9F,0x95,0xE1,0xE7,0x4C,0x76,0xBC,0x91,0x5B,0x90,0x13,0xC6,0x68,0x57,0x4E,0x41,0x8B,0x10,0x5E,0x1D,0xA9,0x44,0xD3,0xBA,0x47,0xB8,0xDD,0xE4,0x35,0x86,0x11,0x93,0x94,0x92,0x5F,0x29,0xC7,0x4C,0x30,0x0C,0x41,0xC5,0x1C,0x3B,0x2E,0xD3,0x05,0x15,0x53,0x6C,0x07,0x4D,0x15,0x14,0x8C,0xB5,0xC9,0x6A,0x44,0x90,0x10,0x4E,0x9A,0xB6,0x21,0x81,0x23,0x3A,0x91,0x91,0xE8,0xFF,0x01}; -uint8_t spFOURTY[] PROGMEM = {0x04,0x18,0xB6,0x4C,0x00,0xC3,0x56,0x30,0xA0,0xE8,0xF4,0xA0,0x98,0x99,0x62,0x91,0xAE,0x83,0x6B,0x77,0x89,0x78,0x3B,0x09,0xAE,0xBD,0xA6,0x1E,0x63,0x3B,0x79,0x7E,0x71,0x5A,0x8F,0x95,0xE6,0xA5,0x4A,0x69,0xB9,0x4E,0x8A,0x5F,0x12,0x56,0xE4,0x58,0x69,0xE1,0x36,0xA1,0x69,0x2E,0x2B,0xF9,0x95,0x93,0x55,0x17,0xED,0xE4,0x37,0xC6,0xBA,0x93,0xB2,0x92,0xDF,0x19,0xD9,0x6E,0xC8,0x0A,0xFE,0x60,0xE8,0x37,0x21,0xC9,0xF9,0x8D,0x61,0x5F,0x32,0x13,0xE7,0x17,0x4C,0xD3,0xC6,0xB1,0x94,0x97,0x10,0x8F,0x8B,0xAD,0x11,0x7E,0xA1,0x9A,0x26,0x92,0xF6,0xFF,0x01}; -uint8_t spFIFTY[] PROGMEM = {0x08,0xE8,0x2E,0x84,0x00,0x23,0x84,0x13,0x60,0x38,0x95,0xA5,0x0F,0xCF,0xE2,0x79,0x8A,0x8F,0x37,0x02,0xB3,0xD5,0x2A,0x6E,0x5E,0x93,0x94,0x79,0x45,0xD9,0x05,0x5D,0x0A,0xB9,0x97,0x63,0x02,0x74,0xA7,0x82,0x80,0xEE,0xC3,0x10,0xD0,0x7D,0x28,0x03,0x6E,0x14,0x06,0x70,0xE6,0x0A,0xC9,0x9A,0x4E,0x37,0xD9,0x95,0x51,0xCE,0xBA,0xA2,0x14,0x0C,0x81,0x36,0x1B,0xB2,0x5C,0x30,0x38,0xFA,0x9C,0xC9,0x32,0x41,0xA7,0x18,0x3B,0xA2,0x48,0x04,0x05,0x51,0x4F,0x91,0x6D,0x12,0x04,0x20,0x9B,0x61,0x89,0xFF,0x1F}; -uint8_t spGOOD[] PROGMEM = {0x0A,0x28,0xCD,0x34,0x20,0xD9,0x1A,0x45,0x74,0xE4,0x66,0x24,0xAD,0xBA,0xB1,0x8C,0x9B,0x91,0xA5,0x64,0xE6,0x98,0x21,0x16,0x0B,0x96,0x9B,0x4C,0xE5,0xFF,0x01}; -uint8_t spMORNING[] PROGMEM = {0xCE,0x08,0x52,0x2A,0x35,0x5D,0x39,0x53,0x29,0x5B,0xB7,0x0A,0x15,0x0C,0xEE,0x2A,0x42,0x56,0x66,0xD2,0x55,0x2E,0x37,0x2F,0xD9,0x45,0xB3,0xD3,0xC5,0xCA,0x6D,0x27,0xD5,0xEE,0x50,0xF5,0x50,0x94,0x14,0x77,0x2D,0xD8,0x5D,0x49,0x92,0xFD,0xB1,0x64,0x2F,0xA9,0x49,0x0C,0x93,0x4B,0xAD,0x19,0x17,0x3E,0x66,0x1E,0xF1,0xA2,0x5B,0x84,0xE2,0x29,0x8F,0x8B,0x72,0x10,0xB5,0xB1,0x2E,0x4B,0xD4,0x45,0x89,0x4A,0xEC,0x5C,0x95,0x14,0x2B,0x8A,0x9C,0x34,0x52,0x5D,0xBC,0xCC,0xB5,0x3B,0x49,0x69,0x89,0x87,0xC1,0x98,0x56,0x3A,0x21,0x2B,0x82,0x67,0xCC,0x5C,0x85,0xB5,0x4A,0x8A,0xF6,0x64,0xA9,0x96,0xC4,0x69,0x3C,0x52,0x81,0x58,0x1C,0x97,0xF6,0x0E,0x1B,0xCC,0x0D,0x42,0x32,0xAA,0x65,0x12,0x67,0xD4,0x6A,0x61,0x52,0xFC,0xFF}; -uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x9D,0x28,0x71,0xB4,0xAC,0x9C,0x74,0x5A,0x42,0x55,0x33,0xB2,0x93,0x0A,0x09,0xD4,0xC5,0x9A,0xD6,0x44,0x45,0xE3,0x38,0x60,0x9A,0x32,0x05,0xF4,0x18,0x01,0x09,0xD8,0xA9,0xC2,0x00,0x5E,0xCA,0x24,0xD5,0x5B,0x9D,0x4A,0x95,0xEA,0x34,0xEE,0x63,0x92,0x5C,0x4D,0xD0,0xA4,0xEE,0x58,0x0C,0xB9,0x4D,0xCD,0x42,0xA2,0x3A,0x24,0x37,0x25,0x8A,0xA8,0x8E,0xA0,0x53,0xE4,0x28,0x23,0x26,0x13,0x72,0x91,0xA2,0x76,0xBB,0x72,0x38,0x45,0x0A,0x46,0x63,0xCA,0x69,0x27,0x39,0x58,0xB1,0x8D,0x60,0x1C,0x34,0x1B,0x34,0xC3,0x55,0x8E,0x73,0x45,0x2D,0x4F,0x4A,0x3A,0x26,0x10,0xA1,0xCA,0x2D,0xE9,0x98,0x24,0x0A,0x1E,0x6D,0x97,0x29,0xD2,0xCC,0x71,0xA2,0xDC,0x86,0xC8,0x12,0xA7,0x8E,0x08,0x85,0x22,0x8D,0x9C,0x43,0xA7,0x12,0xB2,0x2E,0x50,0x09,0xEF,0x51,0xC5,0xBA,0x28,0x58,0xAD,0xDB,0xE1,0xFF,0x03}; -uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; -uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; - -void sayTime(int hour, int minutes) ; - -void sayTime(int hour, int minutes) { -AudioGeneratorTalkie *talkie = nullptr; - - if (!audio_i2s.out) return; - - AUDIO_PWR_ON - talkie = new AudioGeneratorTalkie(); - talkie->begin(nullptr, audio_i2s.out); - - bool pm = (hour >= 12); - uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, - spSEVEN, spEIGHT, spNINE, spTEN, spELEVEN }; - size_t spHourLen[] = { sizeof(spTWELVE), sizeof(spONE), sizeof(spTWO), - sizeof(spTHREE), sizeof(spFOUR), sizeof(spFIVE), - sizeof(spSIX), sizeof(spSEVEN), sizeof(spEIGHT), - sizeof(spNINE), sizeof(spTEN), sizeof(spELEVEN) }; - uint8_t *spMinDec[] = { spOH, spTEN, spTWENTY, spTHIRTY, spFOURTY, spFIFTY }; - size_t spMinDecLen[] = { sizeof(spOH), sizeof(spTEN), sizeof(spTWENTY), - sizeof(spTHIRTY), sizeof(spFOURTY), sizeof(spFIFTY) }; - uint8_t *spMinSpecial[] = { spELEVEN, spTWELVE, spTHIRTEEN, spFOURTEEN, - spFIFTEEN, spSIXTEEN, spSEVENTEEN, spEIGHTEEN, - spNINETEEN }; - size_t spMinSpecialLen[] = { sizeof(spELEVEN), sizeof(spTWELVE), - sizeof(spTHIRTEEN), sizeof(spFOURTEEN), - sizeof(spFIFTEEN), sizeof(spSIXTEEN), - sizeof(spSEVENTEEN), sizeof(spEIGHTEEN), - sizeof(spNINETEEN) }; - uint8_t *spMinLow[] = { spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, - spSEVEN, spEIGHT, spNINE }; - size_t spMinLowLen[] = { sizeof(spONE), sizeof(spTWO), sizeof(spTHREE), - sizeof(spFOUR), sizeof(spFIVE), sizeof(spSIX), - sizeof(spSEVEN), sizeof(spEIGHT), sizeof(spNINE) }; - - talkie->say(spTHE, sizeof(spTHE)); - talkie->say(spTIME, sizeof(spTIME)); - talkie->say(spIS, sizeof(spIS)); - - hour = hour % 12; - talkie->say(spHour[hour], spHourLen[hour]); - if (minutes==0) { - talkie->say(spOCLOCK, sizeof(spOCLOCK)); - } else if (minutes<=10 || minutes >=20) { - talkie->say(spMinDec[minutes / 10], spMinDecLen[minutes /10]); - if (minutes % 10) { - talkie->say(spMinLow[(minutes % 10) - 1], spMinLowLen[(minutes % 10) - 1]); - } - } else { - talkie->say(spMinSpecial[minutes - 11], spMinSpecialLen[minutes - 11]); - } - if (pm) { - talkie->say(spP_M_, sizeof(spP_M_)); - } else { - talkie->say(spA_M_, sizeof(spA_M_)); - } - delete talkie; - audio_i2s.out->stop(); - DOWNRATE - AUDIO_PWR_OFF -} -#endif // USE_I2S_SAY_TIME - - -enum : int { APLL_AUTO = -1, APLL_ENABLE = 1, APLL_DISABLE = 0 }; -enum : int { EXTERNAL_I2S = 0, INTERNAL_DAC = 1, INTERNAL_PDM = 2 }; - -#ifdef ESP8266 -#define I2S_MCLK_MULTIPLE_128 0 -#endif - -int32_t I2S_Init_0(void) { - - audio_i2s.i2s_port = (i2s_port_t)0; - -#if USE_I2S_EXTERNAL_DAC - // use i2s -#if (defined(USE_I2S_NO_DAC) && defined(DAC_IIS_DOUT)) || (defined(DAC_IIS_BCK) && defined(DAC_IIS_WS) && defined(DAC_IIS_DOUT)) - audio_i2s.i2s_port = (i2s_port_t)0; -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - audio_i2s.out = new AudioOutputI2S(); -#endif - audio_i2s.bclk = DAC_IIS_BCK; - audio_i2s.ws = DAC_IIS_WS; - audio_i2s.dout = DAC_IIS_DOUT; -#else -#ifdef USE_I2S_NO_DAC - if (PinUsed(GPIO_I2S_DOUT)) { -#else - if (PinUsed(GPIO_I2S_BCLK) && PinUsed(GPIO_I2S_WS) && PinUsed(GPIO_I2S_DOUT)) { -#endif // USE_I2S_NO_DAC - audio_i2s.i2s_port = (i2s_port_t)0; -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); -#endif // USE_I2S_NO_DAC - audio_i2s.mclk = Pin(GPIO_I2S_MCLK); - audio_i2s.bclk = Pin(GPIO_I2S_BCLK); - audio_i2s.ws = Pin(GPIO_I2S_WS); - audio_i2s.dout = Pin(GPIO_I2S_DOUT); - audio_i2s.din = Pin(GPIO_I2S_DIN); - } else if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DOUT), 1) { - audio_i2s.i2s_port = (i2s_port_t)1; -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - //audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port); - audio_i2s.out = new AudioOutputI2S(audio_i2s.i2s_port, EXTERNAL_I2S, 8, APLL_DISABLE, I2S_MCLK_MULTIPLE_128, 12000000); -#endif // USE_I2S_NO_DAC - audio_i2s.mclk = Pin(GPIO_I2S_MCLK, 1); - audio_i2s.bclk = Pin(GPIO_I2S_BCLK, 1); - audio_i2s.ws = Pin(GPIO_I2S_WS, 1); - audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); - audio_i2s.din = Pin(GPIO_I2S_DIN, 1); - } else { - return -1; - } -#ifdef ESP8266 - // esp8266 have fixed pins - if ((audio_i2s.bclk != 15) || (audio_i2s.ws != 2) || (audio_i2s.dout != 3)) { - return -2; - } -#endif // ESP8266 -#endif // defined(DAC_IIS_BCK) - - audio_i2s.out->SetPinout(audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); - - AddLog(LOG_LEVEL_INFO, PSTR("Init audio I2S: port=%d, bclk=%d, ws=%d, dout=%d, mclk=%d, din=%d"), audio_i2s.i2s_port, audio_i2s.bclk, audio_i2s.ws, audio_i2s.dout, audio_i2s.mclk, audio_i2s.din); - - -#else - -#ifdef USE_I2S_NO_DAC - audio_i2s.out = new AudioOutputI2SNoDAC(); -#else - audio_i2s.out = new AudioOutputI2S(0, 1); // Internal DAC port 0 -#endif // USE_I2S_NO_DAC - -#endif // USE_I2S_EXTERNAL_DAC - - return 0; -} - - - -void I2S_Init(void) { - - #if defined(ESP32) && defined(ESP32S3_BOX) - S3boxInit(); - #endif - - if (I2S_Init_0()) { - return; - } - - DOWNRATE - audio_i2s.is2_volume=10; - audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); - audio_i2s.out->stop(); - audio_i2s.mp3ram = nullptr; - -#ifdef ESP32 - if (UsePSRAM()) { - audio_i2s.mp3ram = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - } - -#ifdef USE_I2S_WEBRADIO - if (UsePSRAM()) { - audio_i2s.preallocateBuffer = heap_caps_malloc(preallocateBufferSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - audio_i2s.preallocateCodec = heap_caps_malloc(preallocateCodecSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - } else { - audio_i2s.preallocateBuffer = malloc(preallocateBufferSize); - audio_i2s.preallocateCodec = malloc(preallocateCodecSize); - } - if (!audio_i2s.preallocateBuffer || !audio_i2s.preallocateCodec) { - //Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize); - } -#endif // USE_I2S_WEBRADIO - - audio_i2s.mic_channels = MIC_CHANNELS; - audio_i2s.mic_rate = MICSRATE; - -#endif // ESP32 -} - -#ifdef ESP32 -uint32_t SpeakerMic(uint8_t spkr) { - esp_err_t err = ESP_OK; - - if (audio_i2s.out) { - audio_i2s.out->stop(); - delete audio_i2s.out; - audio_i2s.out = nullptr; - } - - i2s_driver_uninstall(audio_i2s.i2s_port); - - if (spkr == MODE_SPK) { - I2S_Init_0(); - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); - audio_i2s.out->stop(); - DOWNRATE - } else { - // config mic - i2s_config_t i2s_config = { - .mode = (i2s_mode_t)(I2S_MODE_MASTER), - .sample_rate = audio_i2s.mic_rate, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, - .communication_format = I2S_COMM_FORMAT_I2S, - .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, - .dma_buf_count = 2, - //.dma_buf_len = 128, - .dma_buf_len = 1024, - .use_apll = 0, // Use audio PLL - .tx_desc_auto_clear = true, - .fixed_mclk = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 - .bits_per_chan = I2S_BITS_PER_CHAN_16BIT - }; - -#ifdef ESP32S3_BOX - i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); - i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; -#endif - -#ifdef USE_I2S_MIC - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); - // mic select to GND - i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; - i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; -#endif - -#ifdef USE_M5STACK_CORE2 - i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); -#endif - - err += i2s_driver_install(audio_i2s.i2s_port, &i2s_config, 0, NULL); - - i2s_pin_config_t tx_pin_config; -#ifdef ESP32S3_BOX - tx_pin_config.mck_io_num = audio_i2s.mclk; -#else - tx_pin_config.mck_io_num = I2S_PIN_NO_CHANGE; -#endif - tx_pin_config.bck_io_num = audio_i2s.bclk; - tx_pin_config.ws_io_num = audio_i2s.ws; - tx_pin_config.data_out_num = audio_i2s.dout; - tx_pin_config.data_in_num = audio_i2s.din; - - err += i2s_set_pin(audio_i2s.i2s_port, &tx_pin_config); -#ifdef ESP32S3_BOX - err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO); -#else - err += i2s_set_clk(audio_i2s.i2s_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); -#endif - } - return err; -} -#endif //ESP32 - -#ifdef ESP32 - -#ifdef USE_SHINE -#include -#include - -// micro to mp3 file -void mic_task(void *arg){ - int8_t error = 0; - uint8_t *ucp; - int written; - shine_config_t config; - shine_t s = nullptr; - uint16_t samples_per_pass; - File mp3_out = (File)nullptr; - int16_t *buffer = nullptr; - uint16_t bytesize; - uint16_t bwritten; - - mp3_out = ufsp->open(audio_i2s.mic_path, "w"); - if (!mp3_out) { - error = -1; - goto exit; - } - - shine_set_config_mpeg_defaults(&config.mpeg); - - if (audio_i2s.mic_channels == 1) { - config.mpeg.mode = MONO; - } else { - config.mpeg.mode = STEREO; - } - config.mpeg.bitr = 128; - config.wave.samplerate = audio_i2s.mic_rate; - config.wave.channels = (channels)audio_i2s.mic_channels; - - if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) { - error = -3; - goto exit; - } - - s = shine_initialise(&config); - if (!s) { - error = -4; - goto exit; - } - - samples_per_pass = shine_samples_per_pass(s); - bytesize = samples_per_pass * 2 * audio_i2s.mic_channels; - - buffer = (int16_t*)malloc(bytesize); - if (!buffer) { - error = -5; - goto exit; - } - - while (!audio_i2s.mic_stop) { - uint32_t bytes_read; - i2s_read(audio_i2s.i2s_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS)); - ucp = shine_encode_buffer_interleaved(s, buffer, &written); - bwritten = mp3_out.write(ucp, written); - if (bwritten != written) { - break; - } - } - ucp = shine_flush(s, &written); - mp3_out.write(ucp, written); - -exit: - if (s) { - shine_close(s); - } - if (mp3_out) { - mp3_out.close(); - } - if (buffer) { - free(buffer); - } - - SpeakerMic(MODE_SPK); - audio_i2s.mic_stop = 0; - audio_i2s.mic_error = error; - AddLog(LOG_LEVEL_INFO, PSTR("task error: %d"), error); - audio_i2s.mic_task_h = 0; - vTaskDelete(NULL); - -} - -int32_t i2s_record_shine(char *path) { -esp_err_t err = ESP_OK; - - if (audio_i2s.decoder || audio_i2s.mp3) return 0; - - err = SpeakerMic(MODE_MIC); - if (err) { - SpeakerMic(MODE_SPK); - return err; - } - - strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path)); - - audio_i2s.mic_stop = 0; - err = xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); - - return err; -} - -#else -// micro to wav file - -#define DATA_SIZE 1024 - -void mic_task(void *arg){ - uint32_t data_offset = 0; - while (1) { - uint32_t bytes_read; - i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); - if (bytes_read != DATA_SIZE) break; - data_offset += DATA_SIZE; - if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; - } - SpeakerMic(MODE_SPK); - SaveWav(audio_i2s.mic_path, audio_i2s.mic_buff, audio_i2s.mic_size); - free(audio_i2s.mic_buff); - vTaskDelete(audio_i2s.mic_task_h); -} - -uint32_t i2s_record(char *path, uint32_t secs) { - esp_err_t err = ESP_OK; - - if (audio_i2s.decoder || audio_i2s.mp3) return 0; - - err = SpeakerMic(MODE_MIC); - if (err) { - SpeakerMic(MODE_SPK); - return err; - } - - audio_i2s.mic_size = secs * audio_i2s.mic_rate * 2 * audio_i2s.mic_channels; - - audio_i2s.mic_buff = (uint8_t*)heap_caps_malloc(audio_i2s.mic_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); - if (!audio_i2s.mic_buff) return 2; - - if (*path=='+') { - path++; - strlcpy(audio_i2s.mic_path, path , sizeof(audio_i2s.mic_path)); - xTaskCreatePinnedToCore(mic_task, "MIC", 4096, NULL, 3, &audio_i2s.mic_task_h, 1); - return 0; - } - - uint32_t data_offset = 0; - uint32_t stime=millis(); - while (1) { - uint32_t bytes_read; - i2s_read(audio_i2s.i2s_port, (char *)(audio_i2s.mic_buff + data_offset), DATA_SIZE, &bytes_read, (100 / portTICK_RATE_MS)); - if (bytes_read != DATA_SIZE) break; - data_offset += DATA_SIZE; - if (data_offset >= audio_i2s.mic_size-DATA_SIZE) break; - delay(0); - } - //AddLog(LOG_LEVEL_INFO, PSTR("rectime: %d ms"), millis()-stime); - SpeakerMic(MODE_SPK); - // save to path - SaveWav(path, audio_i2s.mic_buff, audio_i2s.mic_size); - free(audio_i2s.mic_buff); - return 0; -} - -static const uint8_t wavHTemplate[] PROGMEM = { // Hardcoded simple WAV header with 0xffffffff lengths all around - 0x52, 0x49, 0x46, 0x46, 0xff, 0xff, 0xff, 0xff, 0x57, 0x41, 0x56, 0x45, - 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x22, 0x56, 0x00, 0x00, 0x88, 0x58, 0x01, 0x00, 0x04, 0x00, 0x10, 0x00, - 0x64, 0x61, 0x74, 0x61, 0xff, 0xff, 0xff, 0xff }; - -bool SaveWav(char *path, uint8_t *buff, uint32_t size) { - File fwp = ufsp->open(path, "w"); - if (!fwp) return false; - uint8_t wavHeader[sizeof(wavHTemplate)]; - memcpy_P(wavHeader, wavHTemplate, sizeof(wavHTemplate)); - - uint8_t channels = audio_i2s.mic_channels; - uint32_t hertz = audio_i2s.mic_rate; - uint8_t bps = 16; - - wavHeader[22] = channels & 0xff; - wavHeader[23] = 0; - wavHeader[24] = hertz & 0xff; - wavHeader[25] = (hertz >> 8) & 0xff; - wavHeader[26] = (hertz >> 16) & 0xff; - wavHeader[27] = (hertz >> 24) & 0xff; - int byteRate = hertz * bps * channels / 8; - wavHeader[28] = byteRate & 0xff; - wavHeader[29] = (byteRate >> 8) & 0xff; - wavHeader[30] = (byteRate >> 16) & 0xff; - wavHeader[31] = (byteRate >> 24) & 0xff; - wavHeader[32] = channels * bps / 8; - wavHeader[33] = 0; - wavHeader[34] = bps; - wavHeader[35] = 0; - - fwp.write(wavHeader, sizeof(wavHeader)); - - fwp.write(buff, size); - fwp.close(); - - return true; -} -#endif // USE_SHINE - -#endif // ESP32 - -#ifdef ESP32 -void mp3_task(void *arg) { - while (1) { - while (audio_i2s.mp3->isRunning()) { - if (!audio_i2s.mp3->loop()) { - audio_i2s.mp3->stop(); - mp3_delete(); - audio_i2s.out->stop(); - if (audio_i2s.mp3_task_h) { - vTaskDelete(audio_i2s.mp3_task_h); - audio_i2s.mp3_task_h = 0; - } - //mp3_task_h=nullptr; - } - delay(1); - } - } -} -#endif // ESP32 - -#ifdef USE_I2S_WEBRADIO -void MDCallback(void *cbData, const char *type, bool isUnicode, const char *str) { - const char *ptr = reinterpret_cast(cbData); - (void) isUnicode; // Punt this ball for now - (void) ptr; - if (strstr_P(type, PSTR("Title"))) { - strncpy(audio_i2s.wr_title, str, sizeof(audio_i2s.wr_title)); - audio_i2s.wr_title[sizeof(audio_i2s.wr_title)-1] = 0; - //AddLog(LOG_LEVEL_INFO,PSTR("WR-Title: %s"),wr_title); - } else { - // Who knows what to do? Not me! - } -} - -void StatusCallback(void *cbData, int code, const char *string) { - const char *ptr = reinterpret_cast(cbData); - (void) code; - (void) ptr; - //strncpy_P(status, string, sizeof(status)-1); - //status[sizeof(status)-1] = 0; -} - -void Webradio(const char *url) { - if (audio_i2s.decoder || audio_i2s.mp3) return; - if (!audio_i2s.out) return; - AUDIO_PWR_ON - audio_i2s.ifile = new AudioFileSourceICYStream(url); - audio_i2s.ifile->RegisterMetadataCB(MDCallback, NULL); - audio_i2s.buff = new AudioFileSourceBuffer(audio_i2s.ifile, audio_i2s.preallocateBuffer, preallocateBufferSize); - audio_i2s.buff->RegisterStatusCB(StatusCallback, NULL); - audio_i2s.decoder = new AudioGeneratorMP3(audio_i2s.preallocateCodec, preallocateCodecSize); - audio_i2s.decoder->RegisterStatusCB(StatusCallback, NULL); - audio_i2s.decoder->begin(audio_i2s.buff, audio_i2s.out); - if (!audio_i2s.decoder->isRunning()) { - // Serial.printf_P(PSTR("Can't connect to URL")); - StopPlaying(); - // strcpy_P(status, PSTR("Unable to connect to URL")); - audio_i2s.retryms = millis() + 2000; - } - - xTaskCreatePinnedToCore(mp3_task2, "MP3-2", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); -} - -void mp3_task2(void *arg){ - while (1) { - if (audio_i2s.decoder && audio_i2s.decoder->isRunning()) { - if (!audio_i2s.decoder->loop()) { - StopPlaying(); - //retryms = millis() + 2000; - } - delay(1); - } - } -} - -void StopPlaying() { - - if (audio_i2s.mp3_task_h) { - vTaskDelete(audio_i2s.mp3_task_h); - audio_i2s.mp3_task_h = nullptr; - } - - if (audio_i2s.decoder) { - audio_i2s.decoder->stop(); - delete audio_i2s.decoder; - audio_i2s.decoder = NULL; - } - - if (audio_i2s.buff) { - audio_i2s.buff->close(); - delete audio_i2s.buff; - audio_i2s.buff = NULL; - } - - if (audio_i2s.ifile) { - audio_i2s.ifile->close(); - delete audio_i2s.ifile; - audio_i2s.ifile = NULL; - } - DOWNRATE - AUDIO_PWR_OFF -} - -void Cmd_WebRadio(void) { - if (audio_i2s.decoder) { - StopPlaying(); - } - if (XdrvMailbox.data_len > 0) { - Webradio(XdrvMailbox.data); - ResponseCmndChar(XdrvMailbox.data); - } else { - ResponseCmndChar_P(PSTR("Stopped")); - } -} - -#ifdef USE_WEBSERVER -const char HTTP_WEBRADIO[] PROGMEM = - "{s}" "I2S_WR-Title" "{m}%s{e}"; - -void I2S_WR_Show(bool json) { - if (audio_i2s.decoder) { - if (json) { - ResponseAppend_P(PSTR(",\"WebRadio\":{\"Title\":\"%s\"}"), audio_i2s.wr_title); - } else { - WSContentSend_PD(HTTP_WEBRADIO,audio_i2s.wr_title); - } - } -} -#endif // USE_WEBSERVER - -#endif // USE_I2S_WEBRADIO - -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) -#ifdef USE_SHINE -void Cmd_MicRec(void) { - - if (audio_i2s.mic_task_h) { - // stop task - audio_i2s.mic_stop = 1; - while (audio_i2s.mic_stop) { - delay(1); - } - ResponseCmndChar_P(PSTR("Stopped")); - } - if (XdrvMailbox.data_len > 0) { - i2s_record_shine(XdrvMailbox.data); - ResponseCmndChar(XdrvMailbox.data); - } -} -#else -void Cmd_MicRec(void) { - - if (audio_i2s.mic_task_h) { - // stop task - vTaskDelete(audio_i2s.mic_task_h); - audio_i2s.mic_task_h = nullptr; - ResponseCmndChar_P(PSTR("Stopped")); - } - - if (XdrvMailbox.data_len > 0) { - uint16 time = 10; - char *cp = strchr(XdrvMailbox.data, ':'); - if (cp) { - time = atoi(cp + 1); - *cp = 0; - } - if (time<10) time = 10; - if (time>30) time = 30; - i2s_record(XdrvMailbox.data, time); - ResponseCmndChar(XdrvMailbox.data); - } -} -#endif // USE_SHINE -#endif // USE_M5STACK_CORE2 - -#ifdef ESP32 -void Play_mp3(const char *path) { -#ifdef USE_UFILESYS - if (audio_i2s.decoder || audio_i2s.mp3) return; - if (!audio_i2s.out) return; - - bool I2S_Task; - - if (*path=='+') { - I2S_Task = true; - path++; - } else { - I2S_Task = false; - } - - if (!ufsp->exists(path)) { - return; - } - - AUDIO_PWR_ON - - audio_i2s.file = new AudioFileSourceFS(*ufsp, path); - - audio_i2s.id3 = new AudioFileSourceID3(audio_i2s.file); - - if (audio_i2s.mp3ram) { - audio_i2s.mp3 = new AudioGeneratorMP3(audio_i2s.mp3ram, preallocateCodecSize); - } else { - audio_i2s.mp3 = new AudioGeneratorMP3(); - } - audio_i2s.mp3->begin(audio_i2s.id3, audio_i2s.out); - - if (I2S_Task) { - xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); - } else { - while (audio_i2s.mp3->isRunning()) { - if (!audio_i2s.mp3->loop()) { - audio_i2s.mp3->stop(); - break; - } - OsWatchLoop(); - } - audio_i2s.out->stop(); - mp3_delete(); - } - -#endif // USE_UFILESYS -} - -void mp3_delete(void) { - delete audio_i2s.file; - delete audio_i2s.id3; - delete audio_i2s.mp3; - audio_i2s.mp3=nullptr; - DOWNRATE - AUDIO_PWR_OFF -} -#endif // ESP32 - -void Say(char *text) { - - if (!audio_i2s.out) return; - - AUDIO_PWR_ON - - audio_i2s.out->begin(); - ESP8266SAM *sam = new ESP8266SAM; - sam->Say(audio_i2s.out, text); - delete sam; - audio_i2s.out->stop(); - - DOWNRATE - AUDIO_PWR_OFF -} - - -const char kI2SAudio_Commands[] PROGMEM = "I2S|" - "Say|Gain|Time" -#ifdef ESP32 - "|Play" -#ifdef USE_I2S_WEBRADIO - "|WR" -#endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) - "|REC" -#ifdef WAV2MP3 - "|W2M" -#endif -#endif // USE_M5STACK_CORE2 -#endif // ESP32 - ; - -void (* const I2SAudio_Command[])(void) PROGMEM = { - &Cmd_Say, &Cmd_Gain, &Cmd_Time -#ifdef ESP32 - ,&Cmd_Play -#ifdef USE_I2S_WEBRADIO - ,&Cmd_WebRadio -#endif // USE_I2S_WEBRADIO -#if defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC) - ,&Cmd_MicRec -#ifdef WAV2MP3 - ,&Cmd_wav2mp3 -#endif -#endif // USE_M5STACK_CORE2 - -#endif // ESP32 -}; - -void Cmd_Play(void) { - if (XdrvMailbox.data_len > 0) { - Play_mp3(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void Cmd_Gain(void) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 100)) { - if (audio_i2s.out) { - audio_i2s.is2_volume=XdrvMailbox.payload; - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); - } - } - ResponseCmndNumber(audio_i2s.is2_volume); -} - -#ifdef WAV2MP3 -void Cmd_wav2mp3(void) { - if (XdrvMailbox.data_len > 0) { -#ifdef USE_SHINE - wav2mp3(XdrvMailbox.data); -#endif // USE_SHINE - } - ResponseCmndChar(XdrvMailbox.data); -} -#endif // WAV2MP3 - -void Cmd_Say(void) { - if (XdrvMailbox.data_len > 0) { - Say(XdrvMailbox.data); - } - ResponseCmndChar(XdrvMailbox.data); -} - -void Cmd_Time(void) { -#ifdef USE_I2S_SAY_TIME - sayTime(RtcTime.hour, RtcTime.minute); -#endif // USE_I2S_SAY_TIME - ResponseCmndDone(); -} - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ - -bool Xdrv42(uint8_t function) { - bool result = false; - - switch (function) { - case FUNC_COMMAND: - result = DecodeCommand(kI2SAudio_Commands, I2SAudio_Command); - break; - case FUNC_INIT: - I2S_Init(); - break; -#ifdef USE_WEBSERVER -#ifdef USE_I2S_WEBRADIO - case FUNC_WEB_SENSOR: - I2S_WR_Show(false); - break; -#endif // USE_I2S_WEBRADIO -#endif // USE_WEBSERVER -#ifdef USE_I2S_WEBRADIO - case FUNC_JSON_APPEND: - I2S_WR_Show(true); - break; -#endif // USE_I2S_WEBRADIO - } - return result; -} - -#endif // USE_I2S_AUDIO diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino new file mode 100644 index 000000000..53550300c --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino @@ -0,0 +1,297 @@ +/* + xdrv_42_i2s_audio.ino - Audio dac support for Tasmota + + Copyright (C) 2021 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#ifdef ESP32 +#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) + +#define MP3_BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" + +uint32_t SpeakerMic(uint8_t spkr) { +esp_err_t err = ESP_OK; + + + if (spkr == MODE_SPK) { + if (audio_i2s.mic_port == 0) { + I2S_Init_0(); + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + audio_i2s.out->stop(); + } + return 0; + } + + // set micro + if (audio_i2s.mic_port == 0) { + // close audio out + if (audio_i2s.out) { + audio_i2s.out->stop(); + delete audio_i2s.out; + audio_i2s.out = nullptr; + } + i2s_driver_uninstall(audio_i2s.i2s_port); + } + + // config mic + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER), + .sample_rate = audio_i2s.mic_rate, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, + .communication_format = I2S_COMM_FORMAT_I2S, + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 2, + //.dma_buf_len = 128, + .dma_buf_len = 1024, + .use_apll = 0, // Use audio PLL + .tx_desc_auto_clear = true, + .fixed_mclk = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, // I2S_MCLK_MULTIPLE_128 + .bits_per_chan = I2S_BITS_PER_CHAN_16BIT + }; + +#ifdef ESP32S3_BOX + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX); + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; +#endif + +#ifdef USE_I2S_MIC + // mic select to GND + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX); + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S; +#endif + +#ifdef USE_M5STACK_CORE2 + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); +#endif + + if (audio_i2s.mic_channels == 1) { + i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; + } else { + i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + } + + err += i2s_driver_install(audio_i2s.mic_port, &i2s_config, 0, NULL); + + i2s_pin_config_t tx_pin_config; + + tx_pin_config.mck_io_num = audio_i2s.mic_mclk; + tx_pin_config.bck_io_num = audio_i2s.mic_bclk; + tx_pin_config.ws_io_num = audio_i2s.mic_ws; + tx_pin_config.data_out_num = audio_i2s.mic_dout; + tx_pin_config.data_in_num = audio_i2s.mic_din; + + err += i2s_set_pin(audio_i2s.mic_port, &tx_pin_config); + + i2s_channel_t mode = I2S_CHANNEL_MONO; + if (audio_i2s.mic_channels > 1) { + mode = I2S_CHANNEL_STEREO; + } + err += i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, mode); + + return err; +} + + +#ifdef USE_SHINE +#include +#include + +#define MP3HANDLECLIENT audio_i2s.MP3Server->handleClient(); + +// micro to mp3 file or stream +void mic_task(void *arg){ + int8_t error = 0; + uint8_t *ucp; + int written; + shine_config_t config; + shine_t s = nullptr; + uint16_t samples_per_pass; + File mp3_out = (File)nullptr; + int16_t *buffer = nullptr; + uint16_t bytesize; + uint16_t bwritten; + uint32_t ctime; + + if (!audio_i2s.use_stream) { + mp3_out = ufsp->open(audio_i2s.mic_path, "w"); + if (!mp3_out) { + error = 1; + goto exit; + } + } else { + if (!audio_i2s.stream_active) { + error = 2; + audio_i2s.use_stream = 0; + goto exit; + } + audio_i2s.client.flush(); + audio_i2s.client.setTimeout(3); + audio_i2s.client.print("HTTP/1.1 200 OK\r\n" + "Content-Type: audio/mpeg;\r\n\r\n"); + MP3HANDLECLIENT + } + + shine_set_config_mpeg_defaults(&config.mpeg); + + if (audio_i2s.mic_channels == 1) { + config.mpeg.mode = MONO; + } else { + config.mpeg.mode = STEREO; + } + config.mpeg.bitr = 128; + config.wave.samplerate = audio_i2s.mic_rate; + config.wave.channels = (channels)audio_i2s.mic_channels; + + if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) { + error = 3; + goto exit; + } + + s = shine_initialise(&config); + if (!s) { + error = 4; + goto exit; + } + + samples_per_pass = shine_samples_per_pass(s); + bytesize = samples_per_pass * 2 * audio_i2s.mic_channels; + + buffer = (int16_t*)malloc(bytesize); + if (!buffer) { + error = 5; + goto exit; + } + + ctime = TasmotaGlobal.uptime; + + while (!audio_i2s.mic_stop) { + uint32_t bytes_read; + i2s_read(audio_i2s.mic_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS)); + ucp = shine_encode_buffer_interleaved(s, buffer, &written); + + if (!audio_i2s.use_stream) { + bwritten = mp3_out.write(ucp, written); + if (bwritten != written) { + break; + } + } else { + audio_i2s.client.write((const char*)ucp, written); + MP3HANDLECLIENT + if (!audio_i2s.client.connected()) { + break; + } + } + audio_i2s.recdur = TasmotaGlobal.uptime - ctime; + } + + ucp = shine_flush(s, &written); + + if (!audio_i2s.use_stream) { + mp3_out.write(ucp, written); + } else { + audio_i2s.client.write((const char*)ucp, written); + MP3HANDLECLIENT + } + + +exit: + if (s) { + shine_close(s); + } + if (mp3_out) { + mp3_out.close(); + } + if (buffer) { + free(buffer); + } + + if (audio_i2s.use_stream) { + audio_i2s.client.stop(); + MP3HANDLECLIENT + } + + SpeakerMic(MODE_SPK); + audio_i2s.mic_stop = 0; + audio_i2s.mic_error = error; + AddLog(LOG_LEVEL_INFO, PSTR("mp3task error: %d"), error); + audio_i2s.mic_task_h = 0; + audio_i2s.recdur = 0; + audio_i2s.stream_active = 0; + vTaskDelete(NULL); + +} + +int32_t i2s_record_shine(char *path) { +esp_err_t err = ESP_OK; + + if (audio_i2s.mic_port == 0) { + if (audio_i2s.decoder || audio_i2s.mp3) return 0; + } + + err = SpeakerMic(MODE_MIC); + if (err) { + if (audio_i2s.mic_port == 0) { + SpeakerMic(MODE_SPK); + } + AddLog(LOG_LEVEL_INFO, PSTR("mic init error: %d"), err); + return err; + } + + strlcpy(audio_i2s.mic_path, path, sizeof(audio_i2s.mic_path)); + + audio_i2s.mic_stop = 0; + + uint32_t stack = 4096; + + audio_i2s.use_stream = !strcmp(audio_i2s.mic_path, "stream.mp3"); + + if (audio_i2s.use_stream) { + stack = 8000; + } + + err = xTaskCreatePinnedToCore(mic_task, "MIC", stack, NULL, 3, &audio_i2s.mic_task_h, 1); + + return err; +} + +void Cmd_MicRec(void) { + + if (XdrvMailbox.data_len > 0) { + if (!strncmp(XdrvMailbox.data, "-?", 2)) { + Response_P("{\"I2SREC-duration\":%d}", audio_i2s.recdur); + } else { + i2s_record_shine(XdrvMailbox.data); + ResponseCmndChar(XdrvMailbox.data); + } + } else { + if (audio_i2s.mic_task_h) { + // stop task + audio_i2s.mic_stop = 1; + while (audio_i2s.mic_stop) { + delay(1); + } + ResponseCmndChar_P(PSTR("Stopped")); + } + } + +} + +#endif // USE_SHINE +#endif // USE_I2S_AUDIO +#endif // ESP32 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino new file mode 100644 index 000000000..72495edb9 --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino @@ -0,0 +1,58 @@ + +#ifdef ESP32 +#if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) + +#ifdef MP3_MIC_STREAM + +void Stream_mp3(void) { + if (!audio_i2s.stream_enable) { + return; + } + + if (audio_i2s.stream_active) { + return; + } + AddLog(LOG_LEVEL_INFO, PSTR("I2S: Handle mp3server")); + audio_i2s.stream_active = 1; + audio_i2s.client = audio_i2s.MP3Server->client(); + AddLog(LOG_LEVEL_INFO, PSTR("I2S: Create client")); + i2s_record_shine((char*)"stream.mp3"); +} + +void i2s_mp3_loop(void) { + if (audio_i2s.MP3Server) { + audio_i2s.MP3Server->handleClient(); + } +} + +void i2s_mp3_init(uint32_t on) { + if (on) { + if (!audio_i2s.MP3Server) { + audio_i2s.MP3Server = new ESP8266WebServer(81); + audio_i2s.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3); + audio_i2s.MP3Server->begin(); + AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created")); + } + } else { + if (audio_i2s.MP3Server) { + audio_i2s.MP3Server->stop(); + delete audio_i2s.MP3Server; + audio_i2s.MP3Server = nullptr; + AddLog(LOG_LEVEL_INFO, PSTR("MP3: server deleted")); + } + } +} + + +void Cmd_MP3Stream(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 1)) { + audio_i2s.stream_enable = XdrvMailbox.payload; + } + i2s_mp3_init(audio_i2s.stream_enable); + ResponseCmndNumber(audio_i2s.stream_enable); +} +#endif // MP3_MIC_STREAM + + +#endif // USE_SHINE +#endif // ESP32 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino similarity index 56% rename from tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino rename to tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino index 46e000b5e..8bf927fc2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino @@ -1,4 +1,23 @@ +/* + audio is2 support for ESP32-S3 box and box lite + + Copyright (C) 2022 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + #ifdef ESP32 #ifdef ESP32S3_BOX #include @@ -7,9 +26,11 @@ #include #include + +#define S3BOX_APWR_GPIO 46 + void S3boxAudioPower(uint8_t pwr) { - pinMode(46 , OUTPUT); - digitalWrite(46, pwr); + digitalWrite(S3BOX_APWR_GPIO, pwr); } // box lite dac init @@ -111,130 +132,9 @@ void S3boxInit() { // box full ES8311_init(); es7210_init(); + + pinMode(S3BOX_APWR_GPIO , OUTPUT); } } #endif // ESP32S3_BOX - -#ifdef USE_SHINE -#ifdef WAV2MP3 - -#include -#include - -typedef uint8_t mp3buf_t; - - -// min freq = 16 KHz Stereo or 32 KHz Mono -int32_t wav2mp3(char *path) { - int32_t error = 0; - shine_config_t config; - shine_t s = nullptr; - File wav_in = (File)nullptr; - File mp3_out = (File)nullptr; - uint8_t *ucp; - int written; - int16_t *buffer = nullptr; - uint32_t bread; - uint16_t samples_per_pass; - char mpath[64]; - char *cp; - uint8_t chans = 1; - uint32_t sfreq = 16000; - - strlcpy(mpath, path, sizeof(mpath)); - - wav_in = ufsp->open(mpath, FS_FILE_READ); - if (!wav_in) { - error = -1; - goto exit; - } - - // script>wav2mp3("/test2.wav") - uint8_t wavHeader[sizeof(wavHTemplate)]; - wav_in.read((uint8_t*)wavHeader, sizeof(wavHTemplate)); - chans = wavHeader[22]; - sfreq = wavHeader[24]|(wavHeader[25]<<8)|(wavHeader[26]<<16)|(wavHeader[27]<<24); - - cp = strchr(mpath, '.'); - if (!cp) { - error = -6; - goto exit; - } - - strcpy(cp, ".mp3"); - - mp3_out = ufsp->open(mpath, FS_FILE_WRITE); - if (!mp3_out) { - error = -2; - goto exit; - } - - shine_set_config_mpeg_defaults(&config.mpeg); - - if (chans == 1) { - config.mpeg.mode = MONO; - } else { - config.mpeg.mode = STEREO; - } - config.mpeg.bitr = 128; - config.wave.samplerate = sfreq; - config.wave.channels = (channels)chans; - - - if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0) { - error = -3; - goto exit; - } - - s = shine_initialise(&config); - if (!s) { - error = -4; - goto exit; - } - - samples_per_pass = shine_samples_per_pass(s); - - - buffer = (int16_t*)malloc(samples_per_pass * 2 * chans); - if (!buffer) { - error = -5; - goto exit; - } - - AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding %d channels with freq %d Hz"), chans, sfreq); - - while (1) { - bread = wav_in.read((uint8_t*)buffer, samples_per_pass * 2 * chans); - if (!bread) { - break; - } - ucp = shine_encode_buffer_interleaved(s, buffer, &written); - mp3_out.write(ucp, written); - } - ucp = shine_flush(s, &written); - mp3_out.write(ucp, written); - -exit: - if (s) { - shine_close(s); - } - if (wav_in) { - wav_in.close(); - } - if (mp3_out) { - mp3_out.close(); - } - - if (buffer) { - free(buffer); - } - - AddLog(LOG_LEVEL_INFO, PSTR("mp3 encoding exit with code: %d"), error); - - return error; -} -#endif // WAV2MP3 -#endif // USE_SHINE - - -#endif +#endif // ESP32 From c4e0250c426e40658a9c0040213e25e432544f69 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 26 Jul 2022 15:32:35 +0200 Subject: [PATCH 149/219] Create xdrv_42_3_i2s_saytime.ino --- .../xdrv_42_3_i2s_saytime.ino | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino new file mode 100644 index 000000000..91583effa --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_3_i2s_saytime.ino @@ -0,0 +1,128 @@ + +/* + audio is2 say time + + Copyright (C) 2022 Gerhard Mutz and Theo Arends + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#if (defined(USE_I2S_AUDIO) || defined(USE_TTGO_WATCH) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX)) +#ifdef USE_I2S_SAY_TIME +long timezone = 2; +byte daysavetime = 1; + +uint8_t spTHE[] PROGMEM = {0x08,0xE8,0x3E,0x55,0x01,0xC3,0x86,0x27,0xAF,0x72,0x0D,0x4D,0x97,0xD5,0xBC,0x64,0x3C,0xF2,0x5C,0x51,0xF1,0x93,0x36,0x8F,0x4F,0x59,0x2A,0x42,0x7A,0x32,0xC3,0x64,0xFF,0x3F}; +uint8_t spTIME[] PROGMEM = {0x0E,0x28,0xAC,0x2D,0x01,0x5D,0xB6,0x0D,0x33,0xF3,0x54,0xB3,0x60,0xBA,0x8C,0x54,0x5C,0xCD,0x2D,0xD4,0x32,0x73,0x0F,0x8E,0x34,0x33,0xCB,0x4A,0x25,0xD4,0x25,0x83,0x2C,0x2B,0xD5,0x50,0x97,0x08,0x32,0xEC,0xD4,0xDC,0x4C,0x33,0xC8,0x70,0x73,0x0F,0x33,0xCD,0x20,0xC3,0xCB,0x43,0xDD,0x3C,0xCD,0x8C,0x20,0x77,0x89,0xF4,0x94,0xB2,0xE2,0xE2,0x35,0x22,0x5D,0xD6,0x4A,0x8A,0x96,0xCC,0x36,0x25,0x2D,0xC9,0x9A,0x7B,0xC2,0x18,0x87,0x24,0x4B,0x1C,0xC9,0x50,0x19,0x92,0x2C,0x71,0x34,0x4B,0x45,0x8A,0x8B,0xC4,0x96,0xB6,0x5A,0x29,0x2A,0x92,0x5A,0xCA,0x53,0x96,0x20,0x05,0x09,0xF5,0x92,0x5D,0xBC,0xE8,0x58,0x4A,0xDD,0xAE,0x73,0xBD,0x65,0x4B,0x8D,0x78,0xCA,0x2B,0x4E,0xD8,0xD9,0xED,0x22,0x20,0x06,0x75,0x00,0x00,0x80,0xFF,0x07}; +uint8_t spIS[] PROGMEM = {0x21,0x18,0x96,0x38,0xB7,0x14,0x8D,0x60,0x3A,0xA6,0xE8,0x51,0xB4,0xDC,0x2E,0x48,0x7B,0x5A,0xF1,0x70,0x1B,0xA3,0xEC,0x09,0xC6,0xCB,0xEB,0x92,0x3D,0xA7,0x69,0x1F,0xAF,0x71,0x89,0x9C,0xA2,0xB3,0xFC,0xCA,0x35,0x72,0x9A,0xD1,0xF0,0xAB,0x12,0xB3,0x2B,0xC6,0xCD,0x4F,0xCC,0x32,0x26,0x19,0x07,0xDF,0x0B,0x8F,0xB8,0xA4,0xED,0x7C,0xCF,0x23,0x62,0x8B,0x8E,0xF1,0x23,0x0A,0x8B,0x6E,0xCB,0xCE,0xEF,0x54,0x44,0x3C,0xDC,0x08,0x60,0x0B,0x37,0x01,0x1C,0x53,0x26,0x80,0x15,0x4E,0x14,0xB0,0x54,0x2B,0x02,0xA4,0x69,0xFF,0x7F}; +uint8_t spA_M_[] PROGMEM = {0xCD,0xEF,0x86,0xAB,0x57,0x6D,0x0F,0xAF,0x71,0xAD,0x49,0x55,0x3C,0xFC,0x2E,0xC5,0xB7,0x5C,0xF1,0xF2,0x87,0x66,0xDD,0x4E,0xC5,0xC3,0xEF,0x92,0xE2,0x3A,0x65,0xB7,0xA0,0x09,0xAA,0x1B,0x97,0x54,0x82,0x2E,0x28,0x77,0x5C,0x52,0x09,0x1A,0xA3,0xB8,0x76,0x49,0x25,0x68,0x8C,0x73,0xDB,0x24,0x95,0xA0,0x32,0xA9,0x6B,0xA7,0xD9,0x82,0x26,0xA9,0x76,0x42,0xD6,0x08,0xBA,0xE1,0xE8,0x0E,0x5A,0x2B,0xEA,0x9E,0x3D,0x27,0x18,0xAD,0xA8,0x07,0xF1,0x98,0x90,0x35,0xA2,0x96,0x44,0xA3,0x5D,0x66,0x8B,0x6B,0x12,0xCD,0x32,0x85,0x25,0xC9,0x81,0x2D,0xC3,0x64,0x85,0x34,0x58,0x89,0x94,0x52,0x1C,0x52,0x2F,0x35,0xDA,0xC7,0x51,0x48,0x23,0x97,0xCC,0x2C,0x97,0x2E,0xF3,0x5C,0xF3,0xA2,0x14,0xBA,0x2C,0x48,0xCE,0xCA,0x76,0xE8,0x32,0x2F,0x34,0xB2,0xDB,0x85,0xC9,0x83,0x90,0xA8,0x2C,0x57,0x26,0x8F,0x9C,0xBD,0xA2,0x53,0xD9,0xC2,0x54,0x59,0x28,0x99,0x4B,0x2C,0x5D,0xFF,0x3F}; +uint8_t spP_M_[] PROGMEM = {0x0E,0x98,0x41,0x54,0x00,0x43,0xA0,0x05,0xAB,0x42,0x8E,0x1D,0xA3,0x15,0xEC,0x4E,0x58,0xF7,0x92,0x66,0x70,0x1B,0x66,0xDB,0x73,0x99,0xC1,0xEB,0x98,0xED,0xD6,0x25,0x25,0x6F,0x70,0x92,0xDD,0x64,0xD8,0xFC,0x61,0xD0,0x66,0x83,0xD6,0x0A,0x86,0x23,0xAB,0x69,0xDA,0x2B,0x18,0x9E,0x3D,0x37,0x69,0x9D,0xA8,0x07,0x71,0x9F,0xA0,0xBD,0xA2,0x16,0xD5,0x7C,0x54,0xF6,0x88,0x6B,0x54,0x8B,0x34,0x49,0x2D,0x29,0x49,0x3C,0x34,0x64,0xA5,0x24,0x1B,0x36,0xD7,0x72,0x13,0x92,0xA4,0xC4,0x2D,0xC3,0xB3,0x4B,0xA3,0x62,0x0F,0x2B,0x37,0x6E,0x8B,0x5A,0xD4,0x3D,0xDD,0x9A,0x2D,0x50,0x93,0xF6,0x4C,0xAA,0xB6,0xC4,0x85,0x3B,0xB2,0xB1,0xD8,0x93,0x20,0x4D,0x8F,0x24,0xFF,0x0F}; +uint8_t spOH[] PROGMEM = {0xC6,0xC9,0x71,0x5A,0xA2,0x92,0x14,0x2F,0x6E,0x97,0x9C,0x46,0x9D,0xDC,0xB0,0x4D,0x62,0x1B,0x55,0x70,0xDD,0x55,0xBE,0x0E,0x36,0xC1,0x33,0x37,0xA9,0xA7,0x51,0x1B,0xCF,0x3C,0xA5,0x9E,0x44,0xAC,0x3C,0x7D,0x98,0x7B,0x52,0x96,0x72,0x65,0x4B,0xF6,0x1A,0xD9,0xCA,0xF5,0x91,0x2D,0xA2,0x2A,0x4B,0xF7,0xFF,0x01}; +uint8_t spOCLOCK[] PROGMEM = {0x21,0x4E,0x3D,0xB8,0x2B,0x19,0xBB,0x24,0x0E,0xE5,0xEC,0x60,0xE4,0xF2,0x90,0x13,0xD4,0x2A,0x11,0x80,0x00,0x42,0x69,0x26,0x40,0xD0,0x2B,0x04,0x68,0xE0,0x4D,0x00,0x3A,0x35,0x35,0x33,0xB6,0x51,0xD9,0x64,0x34,0x82,0xB4,0x9A,0x63,0x92,0x55,0x89,0x52,0x5B,0xCA,0x2E,0x34,0x25,0x4E,0x63,0x28,0x3A,0x50,0x95,0x26,0x8D,0xE6,0xAA,0x64,0x58,0xEA,0x92,0xCE,0xC2,0x46,0x15,0x9B,0x86,0xCD,0x2A,0x2E,0x37,0x00,0x00,0x00,0x0C,0xC8,0xDD,0x05,0x01,0xB9,0x33,0x21,0xA0,0x74,0xD7,0xFF,0x07}; +uint8_t spONE[] PROGMEM = {0xCC,0x67,0x75,0x42,0x59,0x5D,0x3A,0x4F,0x9D,0x36,0x63,0xB7,0x59,0xDC,0x30,0x5B,0x5C,0x23,0x61,0xF3,0xE2,0x1C,0xF1,0xF0,0x98,0xC3,0x4B,0x7D,0x39,0xCA,0x1D,0x2C,0x2F,0xB7,0x15,0xEF,0x70,0x79,0xBC,0xD2,0x46,0x7C,0x52,0xE5,0xF1,0x4A,0x6A,0xB3,0x71,0x47,0xC3,0x2D,0x39,0x34,0x4B,0x23,0x35,0xB7,0x7A,0x55,0x33,0x8F,0x59,0xDC,0xA2,0x44,0xB5,0xBC,0x66,0x72,0x8B,0x64,0xF5,0xF6,0x98,0xC1,0x4D,0x42,0xD4,0x27,0x62,0x38,0x2F,0x4A,0xB6,0x9C,0x88,0x68,0xBC,0xA6,0x95,0xF8,0x5C,0xA1,0x09,0x86,0x77,0x91,0x11,0x5B,0xFF,0x0F}; +uint8_t spTWO[] PROGMEM = {0x0E,0x38,0x6E,0x25,0x00,0xA3,0x0D,0x3A,0xA0,0x37,0xC5,0xA0,0x05,0x9E,0x56,0x35,0x86,0xAA,0x5E,0x8C,0xA4,0x82,0xB2,0xD7,0x74,0x31,0x22,0x69,0xAD,0x1C,0xD3,0xC1,0xD0,0xFA,0x28,0x2B,0x2D,0x47,0xC3,0x1B,0xC2,0xC4,0xAE,0xC6,0xCD,0x9C,0x48,0x53,0x9A,0xFF,0x0F}; +uint8_t spTHREE[] PROGMEM = {0x02,0xD8,0x2E,0x9C,0x01,0xDB,0xA6,0x33,0x60,0xFB,0x30,0x01,0xEC,0x20,0x12,0x8C,0xE4,0xD8,0xCA,0x32,0x96,0x73,0x63,0x41,0x39,0x89,0x98,0xC1,0x4D,0x0D,0xED,0xB0,0x2A,0x05,0x37,0x0F,0xB4,0xA5,0xAE,0x5C,0xDC,0x36,0xD0,0x83,0x2F,0x4A,0x71,0x7B,0x03,0xF7,0x38,0x59,0xCD,0xED,0x1E,0xB4,0x6B,0x14,0x35,0xB7,0x6B,0x94,0x99,0x91,0xD5,0xDC,0x26,0x48,0x77,0x4B,0x66,0x71,0x1B,0x21,0xDB,0x2D,0x8A,0xC9,0x6D,0x88,0xFC,0x26,0x28,0x3A,0xB7,0x21,0xF4,0x1F,0xA3,0x65,0xBC,0x02,0x38,0xBB,0x3D,0x8E,0xF0,0x2B,0xE2,0x08,0xB7,0x34,0xFF,0x0F}; +uint8_t spFOUR[] PROGMEM = {0x0C,0x18,0xB6,0x9A,0x01,0xC3,0x75,0x09,0x60,0xD8,0x0E,0x09,0x30,0xA0,0x9B,0xB6,0xA0,0xBB,0xB0,0xAA,0x16,0x4E,0x82,0xEB,0xEA,0xA9,0xFA,0x59,0x49,0x9E,0x59,0x23,0x9A,0x27,0x3B,0x78,0x66,0xAE,0x4A,0x9C,0x9C,0xE0,0x99,0xD3,0x2A,0xBD,0x72,0x92,0xEF,0xE6,0x88,0xE4,0x45,0x4D,0x7E,0x98,0x2D,0x62,0x67,0x37,0xF9,0xA1,0x37,0xA7,0x6C,0x94,0xE4,0xC7,0x1E,0xDC,0x3C,0xA5,0x83,0x1F,0x8B,0xEB,0x52,0x0E,0x0E,0x7E,0x2E,0x4E,0xC7,0x31,0xD2,0x79,0xA5,0x3A,0x0D,0xD9,0xC4,0xFF,0x07}; +uint8_t spFIVE[] PROGMEM = {0x02,0xE8,0x3E,0x8C,0x01,0xDD,0x65,0x08,0x60,0x98,0x4C,0x06,0x34,0x93,0xCE,0x80,0xE6,0xDA,0x9A,0x14,0x6B,0xAA,0x47,0xD1,0x5E,0x56,0xAA,0x6D,0x56,0xCD,0x78,0xD9,0xA9,0x1C,0x67,0x05,0x83,0xE1,0xA4,0xBA,0x38,0xEE,0x16,0x86,0x9B,0xFA,0x60,0x87,0x5B,0x18,0x6E,0xEE,0x8B,0x1D,0x6E,0x61,0xB9,0x69,0x36,0x65,0xBA,0x8D,0xE5,0xE5,0x3E,0x1C,0xE9,0x0E,0x96,0x9B,0x5B,0xAB,0x95,0x2B,0x58,0x6E,0xCE,0xE5,0x3A,0x6A,0xF3,0xB8,0x35,0x84,0x7B,0x05,0xA3,0xE3,0x36,0xEF,0x92,0x19,0xB4,0x86,0xDB,0xB4,0x69,0xB4,0xD1,0x2A,0x4E,0x65,0x9A,0x99,0xCE,0x28,0xD9,0x85,0x71,0x4C,0x18,0x6D,0x67,0x47,0xC6,0x5E,0x53,0x4A,0x9C,0xB5,0xE2,0x85,0x45,0x26,0xFE,0x7F}; +uint8_t spSIX[] PROGMEM = {0x0E,0xD8,0xAE,0xDD,0x03,0x0E,0x38,0xA6,0xD2,0x01,0xD3,0xB4,0x2C,0xAD,0x6A,0x35,0x9D,0xB1,0x7D,0xDC,0xEE,0xC4,0x65,0xD7,0xF1,0x72,0x47,0x24,0xB3,0x19,0xD9,0xD9,0x05,0x70,0x40,0x49,0xEA,0x02,0x98,0xBE,0x42,0x01,0xDF,0xA4,0x69,0x40,0x00,0xDF,0x95,0xFC,0x3F}; +uint8_t spSEVEN[] PROGMEM = {0x02,0xB8,0x3A,0x8C,0x01,0xDF,0xA4,0x73,0x40,0x01,0x47,0xB9,0x2F,0x33,0x3B,0x73,0x5F,0x53,0x7C,0xEC,0x9A,0xC5,0x63,0xD5,0xD1,0x75,0xAE,0x5B,0xFC,0x64,0x5C,0x35,0x87,0x91,0xF1,0x83,0x36,0xB5,0x68,0x55,0xC5,0x6F,0xDA,0x45,0x2D,0x1C,0x2D,0xB7,0x38,0x37,0x9F,0x60,0x3C,0xBC,0x9A,0x85,0xA3,0x25,0x66,0xF7,0x8A,0x57,0x1C,0xA9,0x67,0x56,0xCA,0x5E,0xF0,0xB2,0x16,0xB2,0xF1,0x89,0xCE,0x8B,0x92,0x25,0xC7,0x2B,0x33,0xCF,0x48,0xB1,0x99,0xB4,0xF3,0xFF}; +uint8_t spEIGHT[] PROGMEM = {0xC3,0x6C,0x86,0xB3,0x27,0x6D,0x0F,0xA7,0x48,0x99,0x4E,0x55,0x3C,0xBC,0x22,0x65,0x36,0x4D,0xD1,0xF0,0x32,0xD3,0xBE,0x34,0xDA,0xC3,0xEB,0x82,0xE2,0xDA,0x65,0x35,0xAF,0x31,0xF2,0x6B,0x97,0x95,0xBC,0x86,0xD8,0x6F,0x82,0xA6,0x73,0x0B,0xC6,0x9E,0x72,0x99,0xCC,0xCB,0x02,0xAD,0x3C,0x9A,0x10,0x60,0xAB,0x62,0x05,0x2C,0x37,0x84,0x00,0xA9,0x73,0x00,0x00,0xFE,0x1F}; +uint8_t spNINE[] PROGMEM = {0xCC,0xA1,0x26,0xBB,0x83,0x93,0x18,0xCF,0x4A,0xAD,0x2E,0x31,0xED,0x3C,0xA7,0x24,0x26,0xC3,0x54,0xF1,0x92,0x64,0x8B,0x8A,0x98,0xCB,0x2B,0x2E,0x34,0x53,0x2D,0x0E,0x2F,0x57,0xB3,0x0C,0x0D,0x3C,0xBC,0x3C,0x4C,0x4B,0xCA,0xF4,0xF0,0x72,0x0F,0x6E,0x49,0x53,0xCD,0xCB,0x53,0x2D,0x35,0x4D,0x0F,0x2F,0x0F,0xD7,0x0C,0x0D,0x3D,0xBC,0xDC,0x4D,0xD3,0xDD,0xC2,0xF0,0x72,0x52,0x4F,0x57,0x9B,0xC3,0xAB,0x89,0xBD,0x42,0x2D,0x0F,0xAF,0x5A,0xD1,0x71,0x91,0x55,0xBC,0x2C,0xC5,0x3B,0xD8,0x65,0xF2,0x82,0x94,0x18,0x4E,0x3B,0xC1,0x73,0x42,0x32,0x33,0x15,0x45,0x4F,0x79,0x52,0x6A,0x55,0xA6,0xA3,0xFF,0x07}; +uint8_t spTEN[] PROGMEM = {0x0E,0xD8,0xB1,0xDD,0x01,0x3D,0xA8,0x24,0x7B,0x04,0x27,0x76,0x77,0xDC,0xEC,0xC2,0xC5,0x23,0x84,0xCD,0x72,0x9A,0x51,0xF7,0x62,0x45,0xC7,0xEB,0x4E,0x35,0x4A,0x14,0x2D,0xBF,0x45,0xB6,0x0A,0x75,0xB8,0xFC,0x16,0xD9,0x2A,0xD9,0xD6,0x0A,0x5A,0x10,0xCD,0xA2,0x48,0x23,0xA8,0x81,0x35,0x4B,0x2C,0xA7,0x20,0x69,0x0A,0xAF,0xB6,0x15,0x82,0xA4,0x29,0x3C,0xC7,0x52,0x08,0xA2,0x22,0xCF,0x68,0x4B,0x2E,0xF0,0x8A,0xBD,0xA3,0x2C,0xAB,0x40,0x1B,0xCE,0xAA,0xB2,0x6C,0x82,0x40,0x4D,0x7D,0xC2,0x89,0x88,0x8A,0x61,0xCC,0x74,0xD5,0xFF,0x0F}; +uint8_t spELEVEN[] PROGMEM = {0xC3,0xCD,0x76,0x5C,0xAE,0x14,0x0F,0x37,0x9B,0x71,0xDE,0x92,0x55,0xBC,0x2C,0x27,0x70,0xD3,0x76,0xF0,0x83,0x5E,0xA3,0x5E,0x5A,0xC1,0xF7,0x61,0x58,0xA7,0x19,0x35,0x3F,0x99,0x31,0xDE,0x52,0x74,0xFC,0xA2,0x26,0x64,0x4B,0xD1,0xF1,0xAB,0xAE,0xD0,0x2D,0xC5,0xC7,0x2F,0x36,0xDD,0x27,0x15,0x0F,0x3F,0xD9,0x08,0x9F,0x62,0xE4,0xC2,0x2C,0xD4,0xD8,0xD3,0x89,0x0B,0x1B,0x57,0x11,0x0B,0x3B,0xC5,0xCF,0xD6,0xCC,0xC6,0x64,0x35,0xAF,0x18,0x73,0x1F,0xA1,0x5D,0xBC,0x62,0x45,0xB3,0x45,0x51,0xF0,0xA2,0x62,0xAB,0x4A,0x5B,0xC9,0x4B,0x8A,0x2D,0xB3,0x6C,0x06,0x2F,0x29,0xB2,0xAC,0x8A,0x18,0xBC,0x28,0xD9,0xAA,0xD2,0x92,0xF1,0xBC,0xE0,0x98,0x8C,0x48,0xCC,0x17,0x52,0xA3,0x27,0x6D,0x93,0xD0,0x4B,0x8E,0x0E,0x77,0x02,0x00,0xFF,0x0F}; +uint8_t spTWELVE[] PROGMEM = {0x06,0x28,0x46,0xD3,0x01,0x25,0x06,0x13,0x20,0xBA,0x70,0x70,0xB6,0x79,0xCA,0x36,0xAE,0x28,0x38,0xE1,0x29,0xC5,0x35,0xA3,0xE6,0xC4,0x16,0x6A,0x53,0x8C,0x97,0x9B,0x72,0x86,0x4F,0x28,0x1A,0x6E,0x0A,0x59,0x36,0xAE,0x68,0xF8,0x29,0x67,0xFA,0x06,0xA3,0x16,0xC4,0x96,0xE6,0x53,0xAC,0x5A,0x9C,0x56,0x72,0x77,0x31,0x4E,0x49,0x5C,0x8D,0x5B,0x29,0x3B,0x24,0x61,0x1E,0x6C,0x9B,0x6C,0x97,0xF8,0xA7,0x34,0x19,0x92,0x4C,0x62,0x9E,0x72,0x65,0x58,0x12,0xB1,0x7E,0x09,0xD5,0x2E,0x53,0xC5,0xBA,0x36,0x6B,0xB9,0x2D,0x17,0x05,0xEE,0x9A,0x6E,0x8E,0x05,0x50,0x6C,0x19,0x07,0x18,0x50,0xBD,0x3B,0x01,0x92,0x08,0x41,0x40,0x10,0xA6,0xFF,0x0F}; +uint8_t spTHIRTEEN[] PROGMEM = {0x08,0xE8,0x2C,0x15,0x01,0x43,0x07,0x13,0xE0,0x98,0xB4,0xA6,0x35,0xA9,0x1E,0xDE,0x56,0x8E,0x53,0x9C,0x7A,0xE7,0xCA,0x5E,0x76,0x8D,0x94,0xE5,0x2B,0xAB,0xD9,0xB5,0x62,0xA4,0x9C,0xE4,0xE6,0xB4,0x41,0x1E,0x7C,0xB6,0x93,0xD7,0x16,0x99,0x5A,0xCD,0x61,0x76,0x55,0xC2,0x91,0x61,0x1B,0xC0,0x01,0x5D,0x85,0x05,0xE0,0x68,0x51,0x07,0x1C,0xA9,0x64,0x80,0x1D,0x4C,0x9C,0x95,0x88,0xD4,0x04,0x3B,0x4D,0x4E,0x21,0x5C,0x93,0xA8,0x26,0xB9,0x05,0x4B,0x6E,0xA0,0xE2,0xE4,0x57,0xC2,0xB9,0xC1,0xB2,0x93,0x5F,0x09,0xD7,0x24,0xCB,0x4E,0x41,0x25,0x54,0x1D,0x62,0x3B,0x05,0x8D,0x52,0x57,0xAA,0xAD,0x10,0x24,0x26,0xE3,0xE1,0x36,0x5D,0x10,0x85,0xB4,0x97,0x85,0x72,0x41,0x14,0x52,0x5E,0x1A,0xCA,0xF9,0x91,0x6B,0x7A,0x5B,0xC4,0xE0,0x17,0x2D,0x54,0x1D,0x92,0x8C,0x1F,0x25,0x4B,0x8F,0xB2,0x16,0x41,0xA1,0x4A,0x3E,0xE6,0xFA,0xFF,0x01}; +uint8_t spFOURTEEN[] PROGMEM = {0x0C,0x58,0xAE,0x5C,0x01,0xD9,0x87,0x07,0x51,0xB7,0x25,0xB3,0x8A,0x15,0x2C,0xF7,0x1C,0x35,0x87,0x4D,0xB2,0xDD,0x53,0xCE,0x28,0x2B,0xC9,0x0E,0x97,0x2D,0xBD,0x2A,0x17,0x27,0x76,0x8E,0xD2,0x9A,0x6C,0x80,0x94,0x71,0x00,0x00,0x02,0xB0,0x58,0x58,0x00,0x9E,0x0B,0x0A,0xC0,0xB2,0xCE,0xC1,0xC8,0x98,0x7A,0x52,0x95,0x24,0x2B,0x11,0xED,0x36,0xD4,0x92,0xDC,0x4C,0xB5,0xC7,0xC8,0x53,0xF1,0x2A,0xE5,0x1A,0x17,0x55,0xC5,0xAF,0x94,0xBB,0xCD,0x1C,0x26,0xBF,0x52,0x9A,0x72,0x53,0x98,0xFC,0xC2,0x68,0xD2,0x4D,0x61,0xF0,0xA3,0x90,0xB6,0xD6,0x50,0xC1,0x8F,0x42,0xDA,0x4A,0x43,0x39,0x3F,0x48,0x2D,0x6B,0x33,0xF9,0xFF}; +uint8_t spFIFTEEN[] PROGMEM = {0x08,0xE8,0x2A,0x0D,0x01,0xDD,0xBA,0x31,0x60,0x6A,0xF7,0xA0,0xAE,0x54,0xAA,0x5A,0x76,0x97,0xD9,0x34,0x69,0xEF,0x32,0x1E,0x66,0xE1,0xE2,0xB3,0x43,0xA9,0x18,0x55,0x92,0x4E,0x37,0x2D,0x67,0x6F,0xDF,0xA2,0x5A,0xB6,0x04,0x30,0x55,0xA8,0x00,0x86,0x09,0xE7,0x00,0x01,0x16,0x17,0x05,0x70,0x40,0x57,0xE5,0x01,0xF8,0x21,0x34,0x00,0xD3,0x19,0x33,0x80,0x89,0x9A,0x62,0x34,0x4C,0xD5,0x49,0xAE,0x8B,0x53,0x09,0xF7,0x26,0xD9,0x6A,0x7E,0x23,0x5C,0x13,0x12,0xB3,0x04,0x9D,0x50,0x4F,0xB1,0xAD,0x14,0x15,0xC2,0xD3,0xA1,0xB6,0x42,0x94,0xA8,0x8C,0x87,0xDB,0x74,0xB1,0x70,0x59,0xE1,0x2E,0xC9,0xC5,0x81,0x5B,0x55,0xA4,0x4C,0x17,0x47,0xC1,0x6D,0xE3,0x81,0x53,0x9C,0x84,0x6A,0x46,0xD9,0x4C,0x51,0x31,0x42,0xD9,0x66,0xC9,0x44,0x85,0x29,0x6A,0x9B,0xAD,0xFF,0x07}; +uint8_t spSIXTEEN[] PROGMEM = {0x0A,0x58,0x5A,0x5D,0x00,0x93,0x97,0x0B,0x60,0xA9,0x48,0x05,0x0C,0x15,0xAE,0x80,0xAD,0x3D,0x14,0x30,0x7D,0xD9,0x50,0x92,0x92,0xAC,0x0D,0xC5,0xCD,0x2A,0x82,0xAA,0x3B,0x98,0x04,0xB3,0x4A,0xC8,0x9A,0x90,0x05,0x09,0x68,0x51,0xD4,0x01,0x23,0x9F,0x1A,0x60,0xA9,0x12,0x03,0xDC,0x50,0x81,0x80,0x22,0xDC,0x20,0x00,0xCB,0x06,0x3A,0x60,0x16,0xE3,0x64,0x64,0x42,0xDD,0xCD,0x6A,0x8A,0x5D,0x28,0x75,0x07,0xA9,0x2A,0x5E,0x65,0x34,0xED,0x64,0xBB,0xF8,0x85,0xF2,0x94,0x8B,0xAD,0xE4,0x37,0x4A,0x5B,0x21,0xB6,0x52,0x50,0x19,0xAD,0xA7,0xD8,0x4A,0x41,0x14,0xDA,0x5E,0x12,0x3A,0x04,0x91,0x4B,0x7B,0x69,0xA8,0x10,0x24,0x2E,0xE5,0xA3,0x81,0x52,0x90,0x94,0x5A,0x55,0x98,0x32,0x41,0x50,0xCC,0x93,0x2E,0x47,0x85,0x89,0x1B,0x5B,0x5A,0x62,0x04,0x44,0xE3,0x02,0x80,0x80,0x64,0xDD,0xFF,0x1F}; +uint8_t spSEVENTEEN[] PROGMEM = {0x02,0x98,0x3A,0x42,0x00,0x5B,0xA6,0x09,0x60,0xDB,0x52,0x06,0x1C,0x93,0x29,0x80,0xA9,0x52,0x87,0x9A,0xB5,0x99,0x4F,0xC8,0x3E,0x46,0xD6,0x5E,0x7E,0x66,0xFB,0x98,0xC5,0x5A,0xC6,0x9A,0x9C,0x63,0x15,0x6B,0x11,0x13,0x8A,0x9C,0x97,0xB9,0x9A,0x5A,0x39,0x71,0xEE,0xD2,0x29,0xC2,0xA6,0xB8,0x58,0x59,0x99,0x56,0x14,0xA3,0xE1,0x26,0x19,0x19,0xE3,0x8C,0x93,0x17,0xB4,0x46,0xB5,0x88,0x71,0x9E,0x97,0x9E,0xB1,0x2C,0xC5,0xF8,0x56,0xC4,0x58,0xA3,0x1C,0xE1,0x33,0x9D,0x13,0x41,0x8A,0x43,0x58,0xAD,0x95,0xA9,0xDB,0x36,0xC0,0xD1,0xC9,0x0E,0x58,0x4E,0x45,0x01,0x23,0xA9,0x04,0x37,0x13,0xAE,0x4D,0x65,0x52,0x82,0xCA,0xA9,0x37,0x99,0x4D,0x89,0xBA,0xC0,0xBC,0x14,0x36,0x25,0xEA,0x1C,0x73,0x52,0x1D,0x97,0xB8,0x33,0xAC,0x0E,0x75,0x9C,0xE2,0xCE,0xB0,0xDA,0xC3,0x51,0x4A,0x1A,0xA5,0xCA,0x70,0x5B,0x21,0xCE,0x4C,0x26,0xD2,0x6C,0xBA,0x38,0x71,0x2E,0x1F,0x2D,0xED,0xE2,0x24,0xB8,0xBC,0x3D,0x52,0x88,0xAB,0x50,0x8E,0xA8,0x48,0x22,0x4E,0x42,0xA0,0x26,0x55,0xFD,0x3F}; +uint8_t spEIGHTEEN[] PROGMEM = {0x2E,0x9C,0xD1,0x4D,0x54,0xEC,0x2C,0xBF,0x1B,0x8A,0x99,0x70,0x7C,0xFC,0x2E,0x29,0x6F,0x52,0xF6,0xF1,0xBA,0x20,0xBF,0x36,0xD9,0xCD,0xED,0x0C,0xF3,0x27,0x64,0x17,0x73,0x2B,0xA2,0x99,0x90,0x65,0xEC,0xED,0x40,0x73,0x32,0x12,0xB1,0xAF,0x30,0x35,0x0B,0xC7,0x00,0xE0,0x80,0xAE,0xDD,0x1C,0x70,0x43,0xAA,0x03,0x86,0x51,0x36,0xC0,0x30,0x64,0xCE,0x4C,0x98,0xFB,0x5C,0x65,0x07,0xAF,0x10,0xEA,0x0B,0x66,0x1B,0xFC,0x46,0xA8,0x3E,0x09,0x4D,0x08,0x2A,0xA6,0x3E,0x67,0x36,0x21,0x2A,0x98,0x67,0x9D,0x15,0xA7,0xA8,0x60,0xEE,0xB6,0x94,0x99,0xA2,0x4A,0x78,0x22,0xC2,0xA6,0x8B,0x8C,0x8E,0xCC,0x4C,0x8A,0x2E,0x8A,0x4C,0xD3,0x57,0x03,0x87,0x28,0x71,0x09,0x1F,0x2B,0xE4,0xA2,0xC4,0xC5,0x6D,0xAD,0x54,0x88,0xB2,0x63,0xC9,0xF2,0x50,0x2E,0x8A,0x4A,0x38,0x4A,0xEC,0x88,0x28,0x08,0xE3,0x28,0x49,0xF3,0xFF}; +uint8_t spNINETEEN[] PROGMEM = {0xC2,0xEA,0x8A,0x95,0x2B,0x6A,0x05,0x3F,0x71,0x71,0x5F,0x0D,0x12,0xFC,0x28,0x25,0x62,0x35,0xF0,0xF0,0xB3,0x48,0x1E,0x0F,0xC9,0xCB,0x2F,0x45,0x7C,0x2C,0x25,0x1F,0xBF,0x14,0xB3,0x2C,0xB5,0x75,0xFC,0x5A,0x5C,0xA3,0x5D,0xE1,0xF1,0x7A,0x76,0xB3,0x4E,0x45,0xC7,0xED,0x96,0x23,0x3B,0x18,0x37,0x7B,0x18,0xCC,0x09,0x51,0x13,0x4C,0xAB,0x6C,0x4C,0x4B,0x96,0xD2,0x49,0xAA,0x36,0x0B,0xC5,0xC2,0x20,0x26,0x27,0x35,0x63,0x09,0x3D,0x30,0x8B,0xF0,0x48,0x5C,0xCA,0x61,0xDD,0xCB,0xCD,0x91,0x03,0x8E,0x4B,0x76,0xC0,0xCC,0x4D,0x06,0x98,0x31,0x31,0x98,0x99,0x70,0x6D,0x2A,0xA3,0xE4,0x16,0xCA,0xBD,0xCE,0x5C,0x92,0x57,0x28,0xCF,0x09,0x69,0x2E,0x7E,0xA5,0x3C,0x63,0xA2,0x30,0x05,0x95,0xD2,0x74,0x98,0xCD,0x14,0x54,0xCA,0x53,0xA9,0x96,0x52,0x50,0x28,0x6F,0xBA,0xCB,0x0C,0x41,0x50,0xDE,0x65,0x2E,0xD3,0x05,0x89,0x4B,0x7B,0x6B,0x20,0x17,0x44,0xAE,0xED,0x23,0x81,0x52,0x90,0x85,0x73,0x57,0xD0,0x72,0x41,0xB1,0x02,0xDE,0x2E,0xDB,0x04,0x89,0x05,0x79,0xBB,0x62,0xE5,0x76,0x11,0xCA,0x61,0x0E,0xFF,0x1F}; +uint8_t spTWENTY[] PROGMEM = {0x01,0x98,0xD1,0xC2,0x00,0xCD,0xA4,0x32,0x20,0x79,0x13,0x04,0x28,0xE7,0x92,0xDC,0x70,0xCC,0x5D,0xDB,0x76,0xF3,0xD2,0x32,0x0B,0x0B,0x5B,0xC3,0x2B,0xCD,0xD4,0xDD,0x23,0x35,0xAF,0x44,0xE1,0xF0,0xB0,0x6D,0x3C,0xA9,0xAD,0x3D,0x35,0x0E,0xF1,0x0C,0x8B,0x28,0xF7,0x34,0x01,0x68,0x22,0xCD,0x00,0xC7,0xA4,0x04,0xBB,0x32,0xD6,0xAC,0x56,0x9C,0xDC,0xCA,0x28,0x66,0x53,0x51,0x70,0x2B,0xA5,0xBC,0x0D,0x9A,0xC1,0xEB,0x14,0x73,0x37,0x29,0x19,0xAF,0x33,0x8C,0x3B,0xA7,0x24,0xBC,0x42,0xB0,0xB7,0x59,0x09,0x09,0x3C,0x96,0xE9,0xF4,0x58,0xFF,0x0F}; +uint8_t spTHIRTY[] PROGMEM = {0x08,0x98,0xD6,0x15,0x01,0x43,0xBB,0x0A,0x20,0x1B,0x8B,0xE5,0x16,0xA3,0x1E,0xB6,0xB6,0x96,0x97,0x3C,0x57,0xD4,0x2A,0x5E,0x7E,0x4E,0xD8,0xE1,0x6B,0x7B,0xF8,0x39,0x63,0x0D,0x9F,0x95,0xE1,0xE7,0x4C,0x76,0xBC,0x91,0x5B,0x90,0x13,0xC6,0x68,0x57,0x4E,0x41,0x8B,0x10,0x5E,0x1D,0xA9,0x44,0xD3,0xBA,0x47,0xB8,0xDD,0xE4,0x35,0x86,0x11,0x93,0x94,0x92,0x5F,0x29,0xC7,0x4C,0x30,0x0C,0x41,0xC5,0x1C,0x3B,0x2E,0xD3,0x05,0x15,0x53,0x6C,0x07,0x4D,0x15,0x14,0x8C,0xB5,0xC9,0x6A,0x44,0x90,0x10,0x4E,0x9A,0xB6,0x21,0x81,0x23,0x3A,0x91,0x91,0xE8,0xFF,0x01}; +uint8_t spFOURTY[] PROGMEM = {0x04,0x18,0xB6,0x4C,0x00,0xC3,0x56,0x30,0xA0,0xE8,0xF4,0xA0,0x98,0x99,0x62,0x91,0xAE,0x83,0x6B,0x77,0x89,0x78,0x3B,0x09,0xAE,0xBD,0xA6,0x1E,0x63,0x3B,0x79,0x7E,0x71,0x5A,0x8F,0x95,0xE6,0xA5,0x4A,0x69,0xB9,0x4E,0x8A,0x5F,0x12,0x56,0xE4,0x58,0x69,0xE1,0x36,0xA1,0x69,0x2E,0x2B,0xF9,0x95,0x93,0x55,0x17,0xED,0xE4,0x37,0xC6,0xBA,0x93,0xB2,0x92,0xDF,0x19,0xD9,0x6E,0xC8,0x0A,0xFE,0x60,0xE8,0x37,0x21,0xC9,0xF9,0x8D,0x61,0x5F,0x32,0x13,0xE7,0x17,0x4C,0xD3,0xC6,0xB1,0x94,0x97,0x10,0x8F,0x8B,0xAD,0x11,0x7E,0xA1,0x9A,0x26,0x92,0xF6,0xFF,0x01}; +uint8_t spFIFTY[] PROGMEM = {0x08,0xE8,0x2E,0x84,0x00,0x23,0x84,0x13,0x60,0x38,0x95,0xA5,0x0F,0xCF,0xE2,0x79,0x8A,0x8F,0x37,0x02,0xB3,0xD5,0x2A,0x6E,0x5E,0x93,0x94,0x79,0x45,0xD9,0x05,0x5D,0x0A,0xB9,0x97,0x63,0x02,0x74,0xA7,0x82,0x80,0xEE,0xC3,0x10,0xD0,0x7D,0x28,0x03,0x6E,0x14,0x06,0x70,0xE6,0x0A,0xC9,0x9A,0x4E,0x37,0xD9,0x95,0x51,0xCE,0xBA,0xA2,0x14,0x0C,0x81,0x36,0x1B,0xB2,0x5C,0x30,0x38,0xFA,0x9C,0xC9,0x32,0x41,0xA7,0x18,0x3B,0xA2,0x48,0x04,0x05,0x51,0x4F,0x91,0x6D,0x12,0x04,0x20,0x9B,0x61,0x89,0xFF,0x1F}; +uint8_t spGOOD[] PROGMEM = {0x0A,0x28,0xCD,0x34,0x20,0xD9,0x1A,0x45,0x74,0xE4,0x66,0x24,0xAD,0xBA,0xB1,0x8C,0x9B,0x91,0xA5,0x64,0xE6,0x98,0x21,0x16,0x0B,0x96,0x9B,0x4C,0xE5,0xFF,0x01}; +uint8_t spMORNING[] PROGMEM = {0xCE,0x08,0x52,0x2A,0x35,0x5D,0x39,0x53,0x29,0x5B,0xB7,0x0A,0x15,0x0C,0xEE,0x2A,0x42,0x56,0x66,0xD2,0x55,0x2E,0x37,0x2F,0xD9,0x45,0xB3,0xD3,0xC5,0xCA,0x6D,0x27,0xD5,0xEE,0x50,0xF5,0x50,0x94,0x14,0x77,0x2D,0xD8,0x5D,0x49,0x92,0xFD,0xB1,0x64,0x2F,0xA9,0x49,0x0C,0x93,0x4B,0xAD,0x19,0x17,0x3E,0x66,0x1E,0xF1,0xA2,0x5B,0x84,0xE2,0x29,0x8F,0x8B,0x72,0x10,0xB5,0xB1,0x2E,0x4B,0xD4,0x45,0x89,0x4A,0xEC,0x5C,0x95,0x14,0x2B,0x8A,0x9C,0x34,0x52,0x5D,0xBC,0xCC,0xB5,0x3B,0x49,0x69,0x89,0x87,0xC1,0x98,0x56,0x3A,0x21,0x2B,0x82,0x67,0xCC,0x5C,0x85,0xB5,0x4A,0x8A,0xF6,0x64,0xA9,0x96,0xC4,0x69,0x3C,0x52,0x81,0x58,0x1C,0x97,0xF6,0x0E,0x1B,0xCC,0x0D,0x42,0x32,0xAA,0x65,0x12,0x67,0xD4,0x6A,0x61,0x52,0xFC,0xFF}; +uint8_t spAFTERNOON[] PROGMEM = {0xC7,0xCE,0xCE,0x3A,0xCB,0x58,0x1F,0x3B,0x07,0x9D,0x28,0x71,0xB4,0xAC,0x9C,0x74,0x5A,0x42,0x55,0x33,0xB2,0x93,0x0A,0x09,0xD4,0xC5,0x9A,0xD6,0x44,0x45,0xE3,0x38,0x60,0x9A,0x32,0x05,0xF4,0x18,0x01,0x09,0xD8,0xA9,0xC2,0x00,0x5E,0xCA,0x24,0xD5,0x5B,0x9D,0x4A,0x95,0xEA,0x34,0xEE,0x63,0x92,0x5C,0x4D,0xD0,0xA4,0xEE,0x58,0x0C,0xB9,0x4D,0xCD,0x42,0xA2,0x3A,0x24,0x37,0x25,0x8A,0xA8,0x8E,0xA0,0x53,0xE4,0x28,0x23,0x26,0x13,0x72,0x91,0xA2,0x76,0xBB,0x72,0x38,0x45,0x0A,0x46,0x63,0xCA,0x69,0x27,0x39,0x58,0xB1,0x8D,0x60,0x1C,0x34,0x1B,0x34,0xC3,0x55,0x8E,0x73,0x45,0x2D,0x4F,0x4A,0x3A,0x26,0x10,0xA1,0xCA,0x2D,0xE9,0x98,0x24,0x0A,0x1E,0x6D,0x97,0x29,0xD2,0xCC,0x71,0xA2,0xDC,0x86,0xC8,0x12,0xA7,0x8E,0x08,0x85,0x22,0x8D,0x9C,0x43,0xA7,0x12,0xB2,0x2E,0x50,0x09,0xEF,0x51,0xC5,0xBA,0x28,0x58,0xAD,0xDB,0xE1,0xFF,0x03}; +uint8_t spEVENING[] PROGMEM = {0xCD,0x6D,0x98,0x73,0x47,0x65,0x0D,0x6D,0x10,0xB2,0x5D,0x93,0x35,0x94,0xC1,0xD0,0x76,0x4D,0x66,0x93,0xA7,0x04,0xBD,0x71,0xD9,0x45,0xAE,0x92,0xD5,0xAC,0x53,0x07,0x6D,0xA5,0x76,0x63,0x51,0x92,0xD4,0xA1,0x83,0xD4,0xCB,0xB2,0x51,0x88,0xCD,0xF5,0x50,0x45,0xCE,0xA2,0x2E,0x27,0x28,0x54,0x15,0x37,0x0A,0xCF,0x75,0x61,0x5D,0xA2,0xC4,0xB5,0xC7,0x44,0x55,0x8A,0x0B,0xA3,0x6E,0x17,0x95,0x21,0xA9,0x0C,0x37,0xCD,0x15,0xBA,0xD4,0x2B,0x6F,0xB3,0x54,0xE4,0xD2,0xC8,0x64,0xBC,0x4C,0x91,0x49,0x12,0xE7,0xB2,0xB1,0xD0,0x22,0x0D,0x9C,0xDD,0xAB,0x62,0xA9,0x38,0x53,0x11,0xA9,0x74,0x2C,0xD2,0xCA,0x59,0x34,0xA3,0xE5,0xFF,0x03}; +uint8_t spPAUSE1[] PROGMEM = {0x00,0x00,0x00,0x00,0xFF,0x0F}; + +void sayTime(int hour, int minutes) ; + +void sayTime(int hour, int minutes) { +AudioGeneratorTalkie *talkie = nullptr; + + if (!audio_i2s.out) return; + + AUDIO_PWR_ON + talkie = new AudioGeneratorTalkie(); + talkie->begin(nullptr, audio_i2s.out); + + bool pm = (hour >= 12); + uint8_t *spHour[] = { spTWELVE, spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE, spTEN, spELEVEN }; + size_t spHourLen[] = { sizeof(spTWELVE), sizeof(spONE), sizeof(spTWO), + sizeof(spTHREE), sizeof(spFOUR), sizeof(spFIVE), + sizeof(spSIX), sizeof(spSEVEN), sizeof(spEIGHT), + sizeof(spNINE), sizeof(spTEN), sizeof(spELEVEN) }; + uint8_t *spMinDec[] = { spOH, spTEN, spTWENTY, spTHIRTY, spFOURTY, spFIFTY }; + size_t spMinDecLen[] = { sizeof(spOH), sizeof(spTEN), sizeof(spTWENTY), + sizeof(spTHIRTY), sizeof(spFOURTY), sizeof(spFIFTY) }; + uint8_t *spMinSpecial[] = { spELEVEN, spTWELVE, spTHIRTEEN, spFOURTEEN, + spFIFTEEN, spSIXTEEN, spSEVENTEEN, spEIGHTEEN, + spNINETEEN }; + size_t spMinSpecialLen[] = { sizeof(spELEVEN), sizeof(spTWELVE), + sizeof(spTHIRTEEN), sizeof(spFOURTEEN), + sizeof(spFIFTEEN), sizeof(spSIXTEEN), + sizeof(spSEVENTEEN), sizeof(spEIGHTEEN), + sizeof(spNINETEEN) }; + uint8_t *spMinLow[] = { spONE, spTWO, spTHREE, spFOUR, spFIVE, spSIX, + spSEVEN, spEIGHT, spNINE }; + size_t spMinLowLen[] = { sizeof(spONE), sizeof(spTWO), sizeof(spTHREE), + sizeof(spFOUR), sizeof(spFIVE), sizeof(spSIX), + sizeof(spSEVEN), sizeof(spEIGHT), sizeof(spNINE) }; + + talkie->say(spTHE, sizeof(spTHE)); + talkie->say(spTIME, sizeof(spTIME)); + talkie->say(spIS, sizeof(spIS)); + + hour = hour % 12; + talkie->say(spHour[hour], spHourLen[hour]); + if (minutes==0) { + talkie->say(spOCLOCK, sizeof(spOCLOCK)); + } else if (minutes<=10 || minutes >=20) { + talkie->say(spMinDec[minutes / 10], spMinDecLen[minutes /10]); + if (minutes % 10) { + talkie->say(spMinLow[(minutes % 10) - 1], spMinLowLen[(minutes % 10) - 1]); + } + } else { + talkie->say(spMinSpecial[minutes - 11], spMinSpecialLen[minutes - 11]); + } + if (pm) { + talkie->say(spP_M_, sizeof(spP_M_)); + } else { + talkie->say(spA_M_, sizeof(spA_M_)); + } + delete talkie; + audio_i2s.out->stop(); + AUDIO_PWR_OFF +} + +void Cmd_Time(void) { + if (!audio_i2s.out) return; + sayTime(RtcTime.hour, RtcTime.minute); + ResponseCmndDone(); +} +#endif // USE_I2S_SAY_TIME +#endif // is2audio From 33861c8aa08776613674eea1fc50f6d62b0a23d4 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 26 Jul 2022 15:34:43 +0200 Subject: [PATCH 150/219] fixes + background webserver --- .../tasmota_xdrv_driver/xdrv_10_scripter.ino | 230 ++++++++++++++---- 1 file changed, 187 insertions(+), 43 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino index 3fd2f8eb8..cf09e387d 100755 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino @@ -78,6 +78,10 @@ int32_t web_send_file(char mc, char *file); #define SPECIAL_EEPMODE_SIZE 6200 +#ifndef STASK_STACK +#define STASK_STACK 8192-2048 +#endif + #ifdef USE_UFILESYS #undef USE_SCRIPT_FATFS @@ -2318,17 +2322,21 @@ chknext: if (!strncmp(lp, "adc(", 4)) { lp = GetNumericArgument(lp + 4, OPER_EQU, &fvar, gv); while (*lp==' ') lp++; - float fvar1 = 1; + float pin = 1; if (*lp!=')') { - lp = GetNumericArgument(lp, OPER_EQU, &fvar1, gv); - if (fvar1<32 || fvar1>39) fvar1 = 32; + lp = GetNumericArgument(lp, OPER_EQU, &pin, gv); +#ifdef CONFIG_IDF_TARGET_ESP32S3 + if (pin<1 || pin>20) pin = 1; +#else + if (pin<32 || pin>39) pin = 32; +#endif } lp++; if (fvar > 7) fvar = 7; #ifdef ESP32 // ESP32 #ifdef USE_ADC - fvar = AdcRead(fvar1, fvar); + fvar = AdcRead(pin, fvar); #else fvar = 999.999; #endif // USE_ADC @@ -2466,11 +2474,17 @@ chknext: while (*lp==' ') lp++; float fvar2; lp = GetNumericArgument(lp, OPER_EQU, &fvar2, gv); + SCRIPT_SKIP_SPACES float prio = STASK_PRIO; if (*lp!=')') { lp = GetNumericArgument(lp, OPER_EQU, &prio, gv); } - fvar = scripter_create_task(fvar, fvar1, fvar2, prio); + SCRIPT_SKIP_SPACES + float stack = STASK_STACK; + if (*lp!=')') { + lp = GetNumericArgument(lp, OPER_EQU, &stack, gv); + } + fvar = scripter_create_task(fvar, fvar1, fvar2, prio, stack); goto nfuncexit; } #endif //USE_SCRIPT_TASK @@ -3794,16 +3808,34 @@ chknext: } goto nfuncexit; } -#if defined(ESP32) && (defined(USE_M5STACK_CORE2)) +/* +#if defined(ESP32) && (defined(USE_I2S_AUDIO) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) || defined(USE_I2S_MIC)) if (!strncmp(lp, "rec(", 4)) { char str[SCRIPT_MAXSSIZE]; lp = GetStringArgument(lp + 4, OPER_EQU, str, 0); - SCRIPT_SKIP_SPACES - lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); - fvar = i2s_record(str, fvar); + //SCRIPT_SKIP_SPACES + //lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + fvar = i2s_record_shine(str); len++; goto exit; } +#endif +*/ + +#ifdef ESP32 + if (!strncmp(lp, "rr(", 3)) { + lp+=4; + len = 0; + const char *cp = GetResetReason().c_str(); + if (sp) { + if (cp) { + strlcpy(sp, cp, glob_script_mem.max_ssize); + } else { + strlcpy(sp, "-", glob_script_mem.max_ssize); + } + } + goto strexit; + } #endif break; @@ -4883,6 +4915,14 @@ extern char *SML_GetSVal(uint32_t index); fvar = !TasmotaGlobal.global_state.wifi_down; goto exit; } +#ifdef xUSE_SHINE + if (!strncmp(vname, "wav2mp3(", 8)) { + char path[SCRIPT_MAXSSIZE]; + lp = GetStringArgument(lp + 8, OPER_EQU, path, 0); + fvar = wav2mp3(path); + goto nfuncexit; + } +#endif break; case 'y': if (!strncmp(vname, "year", 4)) { @@ -8208,6 +8248,81 @@ String ScriptUnsubscribe(const char * data, int data_len) #endif // SUPPORT_MQTT_EVENT +#if defined(ESP32) && defined(USE_UFILESYS) && defined(USE_SCRIPT_ALT_DOWNLOAD) +ESP8266WebServer *http82_Server; +bool download82_busy; + +void script_download_task82(void *path) { + SendFile_sub((char*) path, 1); + free(path); + download82_busy = false; + //AddLog(LOG_LEVEL_INFO, PSTR("UFS 82: Download finished")); + vTaskDelete( NULL ); +} +void ScriptServeFile82(void) { + String stmp = http82_Server->uri(); + + char *cp = strstr_P(stmp.c_str(), PSTR("/ufs/")); + if (cp) { + cp += 4; + if (ufsp) { + if (ufsp->exists(cp)) { + if (download82_busy == true) { + AddLog(LOG_LEVEL_INFO, PSTR("UFS 82: Download is busy")); + return; + } + download82_busy = true; + char *path = (char*)malloc(128); + strcpy(path, cp); + xTaskCreatePinnedToCore(script_download_task82, "DT", 6000, (void*)path, 3, NULL, 1); + //AddLog(LOG_LEVEL_INFO, PSTR("Sendfile 82 started")); + return; + } + } + } + + Handle82NotFound(); +} + +void Handle82NotFound(void) { + Send82Header(404, "not found"); +} + +void Handle82Root(void) { + Send82Header(403, "forbidden"); +} + +void WebServer82Loop(void) { + if (http82_Server != nullptr) { + http82_Server->handleClient(); + } +} + +void Send82Header(uint32_t type, const char *message) { + http82_Server->client().printf_P(PSTR("HTTP/1.1 %d\r\n"), type); + http82_Server->client().printf_P(PSTR("Content-type: text/plain\r\n\r\n")); + http82_Server->client().printf_P(PSTR("%s\n"), message); +} + +void WebServer82Init(void) { + if (http82_Server != nullptr) { + return; + } + http82_Server = new ESP8266WebServer(82); + if (http82_Server != nullptr) { + http82_Server->on(UriGlob("/ufs/*"), HTTP_GET, ScriptServeFile82); + http82_Server->on("/", HTTP_GET, Handle82Root); + http82_Server->onNotFound(Handle82NotFound); + http82_Server->begin(); + AddLog(LOG_LEVEL_INFO, PSTR("HTTP Server 82 started")); + } else { + AddLog(LOG_LEVEL_INFO, PSTR("HTTP Server 82 failed")); + } +} + +#endif // USE_SCRIPT_ALT_DOWNLOAD + + #ifdef USE_SCRIPT_WEB_DISPLAY @@ -8251,7 +8366,7 @@ bool script_download_busy; void SendFile(char *fname) { #ifdef ESP8266 - SendFile_sub(fname); + SendFile_sub(fname, 0); #endif // ESP8266 #ifdef ESP32 @@ -8265,7 +8380,7 @@ void SendFile(char *fname) { strcpy(path, fname); xTaskCreatePinnedToCore(script_download_task, "DT", 6000, (void*)path, 3, NULL, 1); #else - SendFile_sub(fname); + SendFile_sub(fname, 0); #endif #endif // ESP32 @@ -8273,7 +8388,7 @@ void SendFile(char *fname) { #ifdef USE_DLTASK void script_download_task(void *path) { - SendFile_sub((char*) path); + SendFile_sub((char*) path, 0); free(path); script_download_busy = false; vTaskDelete( NULL ); @@ -8282,26 +8397,29 @@ void script_download_task(void *path) { #define REVERT_M5EPD -void SendFile_sub(char *fname) { +void SendFile_sub(char *path, uint8_t stype) { char buff[512]; - uint8_t sflg = 0; +WiFiClient client; +uint8_t sflg = 0; +File file; +uint32_t fsize; #ifdef USE_DISPLAY_DUMP - char *sbmp = strstr_P(fname, PSTR("scrdmp.bmp")); + char *sbmp = strstr_P(path, PSTR("scrdmp.bmp")); if (sbmp) { sflg = 1; } #endif // USE_DISPLAY_DUMP - if ( strstr_P(fname, PSTR(".jpg"))) { + if ( strstr_P(path, PSTR(".jpg"))) { strcpy_P(buff,PSTR("image/jpeg")); - } else if (strstr_P(fname, PSTR(".bmp"))) { + } else if (strstr_P(path, PSTR(".bmp"))) { strcpy_P(buff,PSTR("image/bmp")); - } else if (strstr_P(fname, PSTR(".html"))) { + } else if (strstr_P(path, PSTR(".html"))) { strcpy_P(buff,PSTR("text/html")); - } else if (strstr_P(fname, PSTR(".txt"))) { + } else if (strstr_P(path, PSTR(".txt"))) { strcpy_P(buff,PSTR("text/plain")); - } else if (strstr_P(fname, PSTR(".pdf"))) { + } else if (strstr_P(path, PSTR(".pdf"))) { strcpy_P(buff,PSTR("application/pdf")); } else { strcpy_P(buff,PSTR("text/plain")); @@ -8309,9 +8427,33 @@ char buff[512]; if (!buff[0]) return; - WSContentSend_P(HTTP_SCRIPT_MIMES, buff); - WSContentFlush(); + if (!sflg) { + file = ufsp->open(path, FS_FILE_READ); + fsize = file.size(); + } + if (0 == stype) { + WSContentSend_P(HTTP_SCRIPT_MIMES, buff); + WSContentFlush(); + client = Webserver->client(); + } else { +#ifdef USE_SCRIPT_ALT_DOWNLOAD + client = http82_Server->client(); +#else + client = Webserver->client(); +#endif + client.printf_P(PSTR("HTTP/1.1 200 OK\r\n")); + char *cp = path; + for (uint32_t cnt = strlen(path) - 1; cnt >= 0; cnt--) { + if (path[cnt] == '/') { + cp = &path[cnt + 1]; + break; + } + } + client.printf_P(PSTR("Content-Disposition: attachment; filename=\"%s\"\r\n"), cp); + client.printf_P(PSTR("Content-Length: %d\r\n"), fsize); + client.printf_P(PSTR("Content-type: application/octet-stream\r\n\r\n")); + } if (sflg) { #ifdef USE_DISPLAY_DUMP @@ -8335,10 +8477,10 @@ char buff[512]; uint8_t *lbp; uint8_t fileHeader[fileHeaderSize]; createBitmapFileHeader(Settings->display_height , Settings->display_width , fileHeader); - Webserver->client().write((uint8_t *)fileHeader, fileHeaderSize); + client.write((uint8_t *)fileHeader, fileHeaderSize); uint8_t infoHeader[infoHeaderSize]; createBitmapInfoHeader(Settings->display_height, Settings->display_width, infoHeader ); - Webserver->client().write((uint8_t *)infoHeader, infoHeaderSize); + client.write((uint8_t *)infoHeader, infoHeaderSize); if (bpp < 0) { for (uint32_t lins = Settings->display_height - 1; lins >= 0 ; lins--) { lbp = lbuf; @@ -8351,7 +8493,7 @@ char buff[512]; *lbp++ = pixel; *lbp++ = pixel; } - Webserver->client().write((const char*)lbuf, Settings->display_width * 3); + client.write((const char*)lbuf, Settings->display_width * 3); } } else { for (uint32_t lins = 0; lins < Settings->display_height; lins++) { @@ -8421,25 +8563,23 @@ char buff[512]; bp++; } } - Webserver->client().write((const char*)lbuf, Settings->display_width * 3); + client.write((const char*)lbuf, Settings->display_width * 3); } } if (lbuf) free(lbuf); - Webserver->client().stop(); + client.stop(); } #endif // USE_DISPLAY_DUMP } else { - File file = ufsp->open(fname, FS_FILE_READ); - uint32_t siz = file.size(); uint32_t len = sizeof(buff); - while (siz > 0) { - if (len > siz) len = siz; + while (fsize > 0) { + if (len > fsize) len = fsize; file.read((uint8_t *)buff, len); - Webserver->client().write((const char*)buff, len); - siz -= len; + client.write((const char*)buff, len); + fsize -= len; } file.close(); - Webserver->client().stop(); + client.stop(); } } #endif // USE_UFILESYS @@ -9994,9 +10134,7 @@ int32_t retval; #ifdef ESP32 #ifdef USE_SCRIPT_TASK -#ifndef STASK_STACK -#define STASK_STACK 8192-2048 -#endif + struct ESP32_Task { uint16_t task_timer; @@ -10039,7 +10177,7 @@ void script_task2(void *arg) { } } } -uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, int32_t prio) { +uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, int32_t prio, int32_t stack) { //return 0; BaseType_t res = 0; if (core > 1) { core = 1; } @@ -10056,12 +10194,12 @@ uint32_t scripter_create_task(uint32_t num, uint32_t time, uint32_t core, int32_ if (!num) { if (Run_Scripter1(">t1", -3, 0) == 99) { sp = glob_script_mem.section_ptr + 2; - res = xTaskCreatePinnedToCore(script_task1, "T1", STASK_STACK, NULL, prio, &esp32_tasks[num].task_t, core); + res = xTaskCreatePinnedToCore(script_task1, "T1", stack, NULL, prio, &esp32_tasks[num].task_t, core); } } else { if (Run_Scripter1(">t2", -3, 0) == 99) { sp = glob_script_mem.section_ptr + 2; - res = xTaskCreatePinnedToCore(script_task2, "T2", STASK_STACK, NULL, prio, &esp32_tasks[num].task_t, core); + res = xTaskCreatePinnedToCore(script_task2, "T2", stack, NULL, prio, &esp32_tasks[num].task_t, core); } } esp32_tasks[num].tstart = sp; @@ -11044,8 +11182,11 @@ bool Xdrv10(uint8_t function) #if defined(USE_UFILESYS) && defined(USE_SCRIPT_WEB_DISPLAY) Webserver->on(UriGlob("/ufs/*"), HTTP_GET, ScriptServeFile); #endif -#endif // USE_WEBSERVER +#if defined(USE_UFILESYS) && defined(USE_SCRIPT_ALT_DOWNLOAD) + WebServer82Init(); +#endif // USE_SCRIPT_ALT_DOWNLOAD break; +#endif // USE_WEBSERVER case FUNC_SAVE_BEFORE_RESTART: if (bitRead(Settings->rule_enabled, 0)) { @@ -11094,11 +11235,14 @@ bool Xdrv10(uint8_t function) break; #endif //USE_BUTTON_EVENT -#ifdef USE_SCRIPT_GLOBVARS case FUNC_LOOP: +#ifdef USE_SCRIPT_GLOBVARS Script_PollUdp(); - break; #endif //USE_SCRIPT_GLOBVARS +#ifdef USE_SCRIPT_ALT_DOWNLOAD + WebServer82Loop(); +#endif + break; } return result; From a8d9acbfd224fa5c4b8e035c168733a527574c6c Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Fri, 29 Jul 2022 07:47:21 +0200 Subject: [PATCH 151/219] current cmd buffer adjustable --- .../tasmota_xdrv_driver/xdrv_10_scripter.ino | 91 +++++++++++++------ 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino index cf09e387d..3c05b0afd 100755 --- a/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino @@ -60,6 +60,11 @@ keywords if then else endif, or, and are better readable for beginners (others m #define SCRIPT_MAXSSIZE 48 #endif +#ifndef SCRIPT_CMDMEM +#define SCRIPT_CMDMEM 512 +#endif +#define MAX_SCRIPT_CMDBUFFER 4096 + #define SCRIPT_EOL '\n' #define SCRIPT_FLOAT_PRECISION 2 @@ -235,7 +240,7 @@ extern Renderer *renderer; #endif enum {OPER_EQU=1,OPER_PLS,OPER_MIN,OPER_MUL,OPER_DIV,OPER_PLSEQU,OPER_MINEQU,OPER_MULEQU,OPER_DIVEQU,OPER_EQUEQU,OPER_NOTEQU,OPER_GRTEQU,OPER_LOWEQU,OPER_GRT,OPER_LOW,OPER_PERC,OPER_XOR,OPER_AND,OPER_OR,OPER_ANDEQU,OPER_OREQU,OPER_XOREQU,OPER_PERCEQU,OPER_SHLEQU,OPER_SHREQU,OPER_SHL,OPER_SHR}; -enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD,SCRIPT_EVENT_HANDLED,SML_JSON_ENABLE,SCRIPT_EPOFFS}; +enum {SCRIPT_LOGLEVEL=1,SCRIPT_TELEPERIOD,SCRIPT_EVENT_HANDLED,SML_JSON_ENABLE,SCRIPT_EPOFFS,SCRIPT_CBSIZE}; #ifdef USE_UFILESYS @@ -425,6 +430,7 @@ struct SCRIPT_MEM { uint8_t *script_pram; uint16_t script_pram_size; uint8_t numvars; + uint8_t arres; void *script_mem; uint16_t script_mem_size; uint8_t script_dprec; @@ -438,6 +444,7 @@ struct SCRIPT_MEM { uint8_t siro_num[3]; uint8_t sind_num; char *last_index_string[3]; + uint16_t cmdbuffer_size = SCRIPT_CMDMEM / 2; #ifdef USE_SCRIPT_FATFS File files[SFS_MAX]; @@ -1293,9 +1300,9 @@ float *get_array_by_name(char *name, uint16_t *alen) { float Get_MFVal(uint8_t index, int16_t bind) { uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; - for (uint8_t count = 0; countnumvals & AND_FILT_MASK; if (!bind) { return mflp->index; @@ -1320,12 +1327,11 @@ float Get_MFVal(uint8_t index, int16_t bind) { void Set_MFVal(uint8_t index, uint16_t bind, float val) { uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; - for (uint8_t count = 0; countnumvals & AND_FILT_MASK; if (!bind) { - if (val < 0) { // shift whole array by value } else { @@ -1335,7 +1341,7 @@ void Set_MFVal(uint8_t index, uint16_t bind, float val) { } } else { if (bind >= 1 && bind <= maxind) { - mflp->rbuff[bind-1] = val; + mflp->rbuff[bind - 1] = val; } } return; @@ -1347,9 +1353,9 @@ void Set_MFVal(uint8_t index, uint16_t bind, float val) { float Get_MFilter(uint8_t index) { uint8_t *mp = (uint8_t*)glob_script_mem.mfilt; - for (uint8_t count = 0; countnumvals & OR_FILT_MASK) { // moving average return mflp->maccu / (mflp->numvals & AND_FILT_MASK); @@ -2110,6 +2116,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, olen = strlen(dvnam); } + glob_script_mem.arres = 0; for (count = 0; count < glob_script_mem.numvars; count++) { char *cp = glob_script_mem.glob_vnp + glob_script_mem.vnp_offset[count]; uint8_t slen = strlen(cp); @@ -2129,6 +2136,7 @@ char *isvar(char *lp, uint8_t *vtype, struct T_INDEX *tind, float *fp, char *sp, len = 1; } else { fvar = Get_MFilter(index); + glob_script_mem.arres = 1; } } else { if (ja) continue; @@ -2514,7 +2522,11 @@ chknext: goto strexit; } #endif // USE_FEXTRACT - + if (!strncmp(lp, "cbs", 3)) { + fvar = glob_script_mem.cmdbuffer_size; + tind->index = SCRIPT_CBSIZE; + goto exit_settable; + } break; case 'd': if (!strncmp(vname, "day", 3)) { @@ -6282,12 +6294,11 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } char *slp = lp; SCRIPT_SKIP_SPACES - #define SCRIPT_CMDMEM 512 - char *cmdmem = (char*)malloc(SCRIPT_CMDMEM); + char *cmdmem = (char*)malloc(glob_script_mem.cmdbuffer_size * 2); if (cmdmem) { char *cmd = cmdmem; uint16_t count; - for (count = 0; countflag.mqtt_enabled = svmqtt; // SetOption3 - Enable MQTT Settings->weblog_level = swll; } @@ -6371,6 +6382,7 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { // found variable as result globvindex = ind.index; // save destination var index here if (gv) globaindex = gv->numind; + else globaindex = -1; uint8_t index = glob_script_mem.type[ind.index].index; if ((vtype&STYPE)==0) { // numeric result @@ -6381,7 +6393,6 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { } else { sysv_type = 0; } - } else { dfvar = &glob_script_mem.fvars[index]; sysv_type = 0; @@ -6395,15 +6406,20 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { lp = eval_sub(lp, &fvar, 0); } else { #endif - char *slp = lp; - glob_script_mem.glob_error = 0; - //Serial.printf("Stack 1: %d\n",GetStack()); - lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); - if (glob_script_mem.glob_error == 1) { - // mismatch was string, not number - // get the string and convert to number - lp = isvar(slp, &vtype, &ind, 0, cmpstr, gv); - fvar = CharToFloat(cmpstr); + SCRIPT_SKIP_SPACES + if ( (glob_script_mem.arres > 0) && (lastop == OPER_EQU) && (*lp == '{') ) { + glob_script_mem.arres = 2; + } else { + char *slp = lp; + glob_script_mem.glob_error = 0; + //Serial.printf("Stack 1: %d\n",GetStack()); + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + if (glob_script_mem.glob_error == 1) { + // mismatch was string, not number + // get the string and convert to number + lp = isvar(slp, &vtype, &ind, 0, cmpstr, gv); + fvar = CharToFloat(cmpstr); + } } #ifdef SCRIPT_LM_SUB } @@ -6461,7 +6477,20 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { if (globaindex >= 0) { Set_MFVal(glob_script_mem.type[globvindex].index, globaindex, *dfvar); } else { - Set_MFilter(glob_script_mem.type[globvindex].index, *dfvar); + if (glob_script_mem.arres == 2) { + // fetch var preset + lp++; + while (*lp && *lp != SCRIPT_EOL) { + if (*lp == '}') { + lp++; + break; + } + lp = GetNumericArgument(lp, OPER_EQU, &fvar, gv); + Set_MFilter(glob_script_mem.type[globvindex].index, fvar); + } + } else { + Set_MFilter(glob_script_mem.type[globvindex].index, *dfvar); + } } } @@ -6481,6 +6510,12 @@ int16_t Run_script_sub(const char *type, int8_t tlen, struct GVARS *gv) { case SCRIPT_EPOFFS: glob_script_mem.epoch_offset = *dfvar; break; + case SCRIPT_CBSIZE: + if (*dfvar > MAX_SCRIPT_CMDBUFFER) { + *dfvar = MAX_SCRIPT_CMDBUFFER; + } + glob_script_mem.cmdbuffer_size = *dfvar; + break; #if defined(USE_SML_M) && defined (USE_SML_SCRIPT_CMD) case SML_JSON_ENABLE: sml_json_enable = *dfvar; From 5cdbcb4343106b4f3229db560fcc1ebd2936ed74 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:21:39 +0200 Subject: [PATCH 152/219] Tasmota Core 2.0.4.1 --- platformio_tasmota32.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index 4d62ccf92..b735fee57 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -38,20 +38,20 @@ extra_scripts = pre:pio-tools/add_c_flags.py ${esp_defaults.extra_scripts} [core32] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-2.0.4.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip platform_packages = build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} [core32solo1] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-solo1-2.0.4.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-solo1-2.0.4.1.zip platform_packages = build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} [core32itead] -platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4/platform-espressif32-ITEAD-2.0.4.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-ITEAD-2.0.4.1.zip platform_packages = build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} From 29d35f544238ba37ec9a810898990f6537502213 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:22:52 +0200 Subject: [PATCH 153/219] Tasmota ESP32 Core 2.0.4.1 --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 10ada3243..44032a982 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ - [ ] Only relevant files were touched - [ ] Only one feature/fix was added per PR and the code change compiles without warnings - [ ] The code change is tested and works with Tasmota core ESP8266 V.2.7.4.9 - - [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.4 + - [ ] The code change is tested and works with Tasmota core ESP32 V.2.0.4.1 - [ ] I accept the [CLA](https://github.com/arendst/Tasmota/blob/development/CONTRIBUTING.md#contributor-license-agreement-cla). _NOTE: The code change must pass CI tests. **Your PR cannot be merged unless tests pass**_ From 47b1f40245ce0eb0bfdc605f12851950be073a89 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 1 Aug 2022 19:27:49 +0200 Subject: [PATCH 154/219] Fix PWM to allow 4 different frequencies --- .../src/esp8266toEsp32.cpp | 373 ++++++++++++++---- .../src/esp8266toEsp32.h | 93 ++++- tasmota/tasmota_support/support_pwm.ino | 89 ++++- .../tasmota_xdrv_driver/xdrv_27_shutter.ino | 16 +- 4 files changed, 477 insertions(+), 94 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index 1438c306a..b5e8450e9 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -25,6 +25,21 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D // ESP Stuff +// This is from Arduino code -- not sure why it is necessary +//Use XTAL clock if possible to avoid timer frequency error when setting APB clock < 80 Mhz +//Need to be fixed in ESP-IDF +#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK +#define LEDC_DEFAULT_CLK LEDC_USE_XTAL_CLK +#else +#define LEDC_DEFAULT_CLK LEDC_AUTO_CLK +#endif +#define LEDC_MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM + +// define our limits to ease any change from esp-idf +#define MAX_TIMERS LEDC_TIMER_MAX // 4 timers for all ESP32 variants +#define HAS_HIGHSPEED SOC_LEDC_SUPPORT_HS_MODE // are there 2 banks of timers/ledc + + // replicated from `tasmota.h` #if defined(CONFIG_IDF_TARGET_ESP32) const uint8_t MAX_PWMS = 16; // ESP32: 16 ledc PWM channels in total - TODO for now @@ -38,49 +53,98 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D const uint8_t MAX_PWMS = 5; // Unknown - revert to 5 PWM max #endif +// current configuration of timers: frequency and resolution + +static uint32_t timer_freq_hz[MAX_TIMERS] = {0}; +static uint8_t timer_duty_resolution[MAX_TIMERS] = {0}; + // channel mapping -static uint8_t pwm_channel[MAX_PWMS]; -static uint32_t pwm_frequency = 977; // Default 977Hz -static uint8_t pwm_bit_num = 10; // Default 1023 +static int8_t pin_to_channel[SOC_GPIO_PIN_COUNT] = { 0 }; // contains the channel assigned to each pin, 0 means unassigned, substract 1 +static uint8_t pwm_timer[MAX_PWMS] = {0}; // contains the timer assigned to each channel + +static const uint32_t pwm_def_frequency = 977; // Default 977Hz +static const ledc_timer_bit_t pwm_def_bit_num = LEDC_TIMER_10_BIT; // Default 1023 static bool pwm_impl_inited = false; // trigger initialization /*********************************************************************************************\ * ESP32 analogWrite emulation support \*********************************************************************************************/ +// apply the configuration of timer number `timer` to the actual timer +// it should be called whenever you change the configuration of a Timer +void _analog_applyTimerConfig(int32_t timer) { + esp_err_t ret; + if (timer < 0 || timer >= MAX_TIMERS) { return; } // avoid overflow or underflow + + // AddLog(LOG_LEVEL_INFO, "PWM: ledc_timer_config(res=%i timer=%i freq=%i)", timer_duty_resolution[timer], timer, timer_freq_hz[timer]); + // we apply configuration to timer + ledc_timer_config_t cfg = { + (ledc_mode_t) 0, // speed mode - first bank + (ledc_timer_bit_t) timer_duty_resolution[timer], // duty_resolution + (ledc_timer_t) timer, // timer_num + timer_freq_hz[timer], // freq_hz + LEDC_DEFAULT_CLK // clk_cfg + }; + ret = ledc_timer_config(&cfg); + if (ret != ESP_OK) { + AddLog(LOG_LEVEL_ERROR, "PWM: ledc_timer_config %i failed ret=%i", timer, ret); + } +#ifdef HAS_HIGHSPEED + // apply the same parameter to the low-speed timer as well + cfg.speed_mode = (ledc_mode_t) 1; // first bank + ret = ledc_timer_config(&cfg); + if (ret != ESP_OK) { + AddLog(LOG_LEVEL_ERROR, "PWM: ledc_timer_config %i failed ret=%i", timer + MAX_TIMERS, ret); + } +#endif +} + +// initialize all timers and memory structures void _analogInit(void) { - if (pwm_impl_inited) return; + if (pwm_impl_inited) { return; } // set all channels to unaffected (255) - for (uint32_t i = 0; i < MAX_PWMS; i++) { - pwm_channel[i] = 255; + + // On ESP32 there are 2 groups of timers. 0..LEDC_TIMER_MAX-1 and LEDC_TIMER_MAX..2*LEDC_TIMER_MAX-1 + for (uint32_t i = 0; i < MAX_TIMERS; i++) { + timer_freq_hz[i] = pwm_def_frequency; + timer_duty_resolution[i] = pwm_def_bit_num; + _analog_applyTimerConfig(i); // apply settings to Timer 0 } pwm_impl_inited = true; } -int _analog_pin2chan(uint32_t pin) { // returns -1 if uallocated - _analogInit(); // make sure the mapping array is initialized - for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { - if ((pwm_channel[channel] < 255) && (pwm_channel[channel] == pin)) { - return channel; +// set the timer number for a GPIO, ignore if the GPIO is not set or Timer number is invalid +// Timer range is 0..3 +void ledcSetTimer(uint8_t chan, uint8_t timer) { + if (timer >= MAX_TIMERS || chan > MAX_PWMS) { return; } + uint8_t cur_timer = pwm_timer[chan]; + + if (timer != cur_timer) { // ignore if the timer number is the same + pwm_timer[chan] = timer; // change the timer value + // apply to hardware + uint8_t group=(chan/8); + uint8_t channel=(chan%8); + esp_err_t ret = ledc_bind_channel_timer((ledc_mode_t) group, (ledc_channel_t) channel, (ledc_timer_t) timer); + if (ret != ESP_OK) { + AddLog(LOG_LEVEL_ERROR, "PWM: ledc_bind_channel_timer %i failed ret=%i", timer, ret); } } - return -1; } -void _analogWriteFreqRange(uint32_t pin) { +// return the channel number for a GPIO, -1 if none +int32_t analogGetChannel2(uint32_t pin) { // returns -1 if uallocated + if (pin >= SOC_GPIO_PIN_COUNT) { return -1; } + return pin_to_channel[pin] - 1; +} + +/* Convert a GPIO number to the pointer of the Timer number */ +int32_t _analog_pin2timer(uint32_t pin) { // returns -1 if uallocated _analogInit(); // make sure the mapping array is initialized - if (255 == pin) { - for (uint32_t channel = 0; channel < MAX_PWMS; channel++) { - if (pwm_channel[channel] < 255) { - ledcSetup(channel, pwm_frequency, pwm_bit_num); - } - } - } else { - int channel = _analog_pin2chan(pin); - if (channel >= 0) { - ledcSetup(channel, pwm_frequency, pwm_bit_num); - } - } + int chan = analogGetChannel2(pin); + if (chan < 0) { return -1; } + int32_t timer = pwm_timer[chan]; + if (timer > MAX_TIMERS) { timer = 0; } + return timer; } // input range is in full range, ledc needs bits @@ -93,52 +157,163 @@ uint32_t _analogGetResolution(uint32_t x) { return bits; } -void analogWriteRange(uint32_t range, uint32_t pin) { - pwm_bit_num = _analogGetResolution(range); - _analogWriteFreqRange(pin); -} - -void analogWriteFreq(uint32_t freq, uint32_t pin) { - pwm_frequency = freq; - _analogWriteFreqRange(pin); -} - -int analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed +void analogWriteRange(uint32_t range, int32_t pin) { + // AddLog(LOG_LEVEL_INFO, "PWM: analogWriteRange range=%i pin=%i", range, pin); _analogInit(); // make sure the mapping array is initialized - // Find if pin is already attached - int chan = _analog_pin2chan(pin); - if (chan >= 0) { return chan; } - // Find an empty channel - for (chan = 0; chan < MAX_PWMS; chan++) { - if (255 == pwm_channel[chan]) { - pwm_channel[chan] = pin; + int32_t timer = (pin < 0) ? 0 : _analog_pin2timer(pin); + if (timer < 0) { return; } - // ledcAttachPin(pin, channel); -- replicating here because we want the default duty - uint8_t group=(chan/8), channel=(chan%8), timer=((chan/2)%4); + uint32_t pwm_bit_num = _analogGetResolution(range); + if (pwm_bit_num > LEDC_MAX_BIT_WIDTH || pwm_bit_num == 0) { + AddLog(LOG_LEVEL_ERROR, "PWM: range is invalid: %i", range); + return; + } + timer_duty_resolution[timer] = (ledc_timer_bit_t) pwm_bit_num; + _analog_applyTimerConfig(timer); +} - // AddLog(LOG_LEVEL_INFO, "PWM: ledc_channel pin=%i out_invert=%i", pin, output_invert); - ledc_channel_config_t ledc_channel = { - (int)pin, // gpio - (ledc_mode_t)group, // speed-mode - (ledc_channel_t)channel, // channel - (ledc_intr_type_t)LEDC_INTR_DISABLE, // intr_type - (ledc_timer_t)timer, // timer_sel - 0, // duty - 0, // hpoint - { output_invert ? 1u : 0u },// output_invert - }; - ledc_channel_config(&ledc_channel); +// change both freq and range +// `0`: set to global value +// `-1`: keep unchanged +// if pin < 0 then change global value for timer 0 +void analogWriteFreqRange(int32_t freq, int32_t range, int32_t pin) { + AddLog(LOG_LEVEL_INFO, "PWM: analogWriteFreqRange freq=%i range=%i pin=%i", freq, range, pin); + _analogInit(); // make sure the mapping array is initialized + uint32_t timer0_freq = timer_freq_hz[0]; // global values + uint8_t timer0_res = timer_duty_resolution[0]; + + int32_t timer = 0; + int32_t res = timer0_res; + if (pin < 0) { + if (freq <= 0) { freq = timer0_freq; } + if (range > 0) { + res = _analogGetResolution(range); + if (res >= LEDC_TIMER_BIT_MAX) { return; } + } + } else { + int32_t chan = analogGetChannel2(pin); + if (chan < 0) { return; } + timer = pwm_timer[chan]; + if (freq < 0) { freq = timer_freq_hz[timer]; } + if (freq == 0) { freq = timer0_freq; } - ledcSetup(channel, pwm_frequency, pwm_bit_num); - // Serial.printf("PWM: New attach pin %d to channel %d\n", pin, channel); - return channel; + res = timer0_res; + if (range < 0) { res = timer_duty_resolution[timer]; } + if (range != 0) { res = _analogGetResolution(range); } + if (res >= LEDC_TIMER_BIT_MAX) { return; } + + if (freq == timer0_freq && res == timer0_res) { + // settings match with the global value + if (timer != 0) { + ledcSetTimer(chan, 0); + timer = 0; + } + // else nothing to change + } else { + // specific (non-global) values, require a specific timer + if (timer == 0) { // currently using the global timer, need to change + // we need to allocate a new timer to this pin + int32_t next_timer = analogNextFreeTimer(); + if (next_timer < 0) { + AddLog(LOG_LEVEL_ERROR, "PWM: failed to assign a timer to GPIO %i", pin); + } else { + ledcSetTimer(chan, next_timer); + timer = next_timer; + } + } + } + pwm_timer[chan] = timer; + } + + // AddLog(LOG_LEVEL_INFO, "PWM: analogWriteFreq actual freq=%i res=%i pin=%i timer=%i", freq, res, pin, timer); + if (timer_freq_hz[timer] != freq || timer_duty_resolution[timer] != res) { + timer_freq_hz[timer] = freq; + timer_duty_resolution[timer] = res; + _analog_applyTimerConfig(timer); + } +} + +// set the frequency, in pin == -1 then change the global value of timer 0 +void analogWriteFreq(uint32_t freq, int32_t pin) { + analogWriteFreqRange(freq, 0, pin); +} + +// find next unassigned channel, or -1 if none available +static int32_t findEmptyChannel() { + bool chan_used[MAX_PWMS] = {0}; + for (uint32_t pin = 0; pin < SOC_GPIO_PIN_COUNT; pin++) { + if (pin_to_channel[pin] > 0) { + chan_used[pin_to_channel[pin] - 1] = true; + } + } + + // find empty slot + for (uint32_t chan = 0; chan < MAX_PWMS; chan++) { + if (!chan_used[chan]) { + return chan; } } - // No more channels available - AddLog(LOG_LEVEL_INFO, "PWM: no more PWM (ledc) channel for GPIO %i", pin); return -1; } +int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc channel used, or -1 if failed + _analogInit(); // make sure the mapping array is initialized + // Find if pin is already attached + int32_t chan = analogGetChannel2(pin); + if (chan >= 0) { return chan; } + // Find an empty channel + chan = findEmptyChannel(); + if (chan < 0) { + AddLog(LOG_LEVEL_INFO, "PWM: no more PWM (ledc) channel for GPIO %i", pin); + return -1; + } + + // new channel attached to pin + pin_to_channel[pin] = chan + 1; + + // ledcAttachPin(pin, channel); -- replicating here because we want the default duty + // timer0 used by default + uint8_t group=(chan/8); + uint8_t channel=(chan%8); + uint8_t timer=0; + + // AddLog(LOG_LEVEL_INFO, "PWM: ledc_channel pin=%i out_invert=%i", pin, output_invert); + ledc_channel_config_t ledc_channel = { + (int)pin, // gpio + (ledc_mode_t)group, // speed-mode + (ledc_channel_t)channel, // channel + (ledc_intr_type_t)LEDC_INTR_DISABLE, // intr_type + (ledc_timer_t)timer, // timer_sel + 0, // duty + 0, // hpoint + { output_invert ? 1u : 0u },// output_invert + }; + ledc_channel_config(&ledc_channel); + + // AddLog(LOG_LEVEL_INFO, "PWM: New attach pin %d to channel %d", pin, channel); + return chan; +} + +uint32_t ledcReadFreq2(uint8_t chan) { +// _Z13ledcReadFreqh +// extern "C" uint32_t _Z13ledcReadFreqh(uint8_t chan) { + if (chan > MAX_PWMS) { + return 0; // wrong channel + } + int32_t timer = pwm_timer[chan]; + int32_t freq = timer_freq_hz[timer]; + return freq; +} + +uint8_t ledcReadResolution(uint8_t chan) { + if (chan > MAX_PWMS) { + return 0; // wrong channel + } + int32_t timer = pwm_timer[chan]; + int32_t res = timer_duty_resolution[timer]; + return res; +} + // void analogWrite(uint8_t pin, int val); extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) { analogWritePhase(pin, val, 0); // if unspecified, use phase = 0 @@ -163,17 +338,24 @@ extern "C" void __wrap__Z11analogWritehi(uint8_t pin, int val) { implementation changes. */ -// exported from Arduno Core -extern uint8_t channels_resolution[MAX_PWMS]; - void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase) { - int chan = _analog_pin2chan(pin); + int32_t chan = analogGetChannel2(pin); if (chan < 0) { // not yet allocated, try to allocate chan = analogAttach(pin); - if (chan < 0) { return; } // failed + if (chan < 0) { + AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase invalid chan=%i", chan); + return; + } // failed } - // AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase pin=%i chan=%i duty=%03X phase=%03X", pin, chan, duty, phase); + int32_t timer = _analog_pin2timer(pin); + if (timer < 0) { + AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase invalid timer=%i", timer); + return; + } + + int32_t pwm_bit_num = timer_duty_resolution[timer]; + // AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase pin=%i chan=%i duty=%03X phase=%03X pwm_bit_num=%i", pin, chan, duty, phase, pwm_bit_num); if (duty >> (pwm_bit_num-1) ) ++duty; // input is 0..1023 but PWM takes 0..1024 - so we skip at mid-range. It creates a small non-linearity if (phase >> (pwm_bit_num-1) ) ++phase; @@ -181,7 +363,7 @@ void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase) uint8_t group=(chan/8), channel=(chan%8); //Fixing if all bits in resolution is set = LEDC FULL ON - uint32_t max_duty = (1 << channels_resolution[chan]) - 1; + uint32_t max_duty = (1 << pwm_bit_num) - 1; phase = phase & max_duty; esp_err_t err1, err2; @@ -190,4 +372,57 @@ void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase) // AddLog(LOG_LEVEL_INFO, "PWM: err1=%i err2=%i", err1, err2); } +// get the timer number for a GPIO, -1 if not found +int32_t analogGetTimer(uint8_t pin) { + return _analog_pin2timer(pin); +} + +int32_t analogGetTimerForChannel(uint8_t chan) { + _analogInit(); // make sure the mapping array is initialized + if (chan > MAX_PWMS) { return -1; } + int32_t timer = pwm_timer[chan]; + if (timer > MAX_TIMERS) { timer = 0; } + return timer; +} + + +// get the next unused timer, returns -1 if no free timer is available +// Keep in mind that Timer 0 is reserved, which leaves only 3 timers available +// +// This function does not reserve the timer, it is reserved only when you assign a GPIO to it +int32_t analogNextFreeTimer() { + _analogInit(); // make sure the mapping array is initialized + bool assigned[MAX_TIMERS] = {}; + assigned[0] = true; + + for (uint32_t chan = 0; chan < MAX_PWMS; chan++) { + assigned[pwm_timer[chan]] = true; + } + + // find first free + for (uint32_t j = 0; j < MAX_TIMERS; j++) { + if (!assigned[j]) { + // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer next_timer=%i", j); + return j; + } + } + // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer no free timer"); + return -1; // none available +} + +// Get timer resolution (in bits) - default 10 +uint8_t analogGetTimerResolution(uint8_t timer) { + _analogInit(); // make sure the mapping array is initialized + if (timer >= MAX_TIMERS) { timer = 0; } + return timer_duty_resolution[timer]; +} + +// Get timer frequency (in Hz) - default 977 +uint32_t analogGetTimerFrequency(uint8_t timer) { + _analogInit(); // make sure the mapping array is initialized + if (timer >= MAX_TIMERS) { timer = 0; } + return timer_freq_hz[timer]; // TODO check validity of value +} + + #endif // ESP32 diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index c10f111fb..bc6e17c8b 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -23,18 +23,101 @@ // #include +/*******************************************************************************************\ + * ESP32/S2/S3/C3... PWM analog support + * + * The following supersedes Arduino framework and provides more granular control: + * - fine grained phase control (in addition to duty cycle) + * - fine control of which PWM uses which timers + * - fine control of frequency and resolution per timer + * + * By default, all PWM are using the same timer called Timer0. + * Changes in frequency of resolution apply to all PWM using Timer0. + * + * You can specify a different timer per GPIO (Timer1 to Timer3). + * Then any change of resolution or frequency to these GPIOs alter only + * the corresponding timer. + * + * Note: on ESP32-only, there are 2 groups of PWM and 2 groups of timers. + * - PWM 0..7 are using Timer 0..3 + * - PWM 8..15 are using Timer 4..7 +\*******************************************************************************************/ -// input range is in full range, ledc needs bits -//void analogWriteRange(uint32_t range); -void analogWriteRange(uint32_t range, uint32_t pin = 255); -//void analogWriteFreq(uint32_t freq); -void analogWriteFreq(uint32_t freq, uint32_t pin = 255); +uint32_t ledcReadFreq2(uint8_t chan); +uint8_t ledcReadResolution(uint8_t chan); +// +// analogAttach - attach a GPIO to a hardware PWM +// +// Calling explcitly analogAttach() allows to specify the `output_invert` flag +// However it is called implicitly if `analogWrite()` is called and the GPIO +// was not yet attached. +// +// Returns: hardware channel number, or -1 if it failed int analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated + +// +// analogWriteRange - change the range of PWM +// +// Internally the range is converted to a number of bits, so range must be a power of 2. +// By default, the resolution is 10 bits, i.e. a range of 1024. +// +// When changing this value, it changes the underlying Timer. This means +// that the range changes for all PWM using the same timer. +// +// If `pin == 255`, the value is changed for the default Timer(s) +// I.e. `Timer 0` (and `Timer 4` on ESP32) +void analogWriteRange(uint32_t range, int32_t pin = -1); + +// +// analogWriteFreq - change the frequency of PWM in Hz +// +// Default value is 977Hz. TODO: use the value in `my_user_config.h` +// +// When changing this value, it changes the underlying Timer. This means +// that the range changes for all PWM using the same timer. +// +// If `pin == 255`, the value is changed for the default Timer(s) +// I.e. `Timer 0` (and `Timer 4` on ESP32) +void analogWriteFreq(uint32_t freq, int32_t pin = -1); + +// +// analogWrite - change the value of PWM +// +// val must be in range. void analogWrite(uint8_t pin, int val); // Extended version that also allows to change phase extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0); +// return the channel assigned to a GPIO, or -1 if none +extern int32_t analogGetChannel2(uint32_t pin); + +/*******************************************************************************************\ + * Low-level Timer management +\*******************************************************************************************/ +// get the timer number for a GPIO, -1 if not found +int32_t analogGetTimer(uint8_t pin); +int32_t analogGetTimerForChannel(uint8_t chan); + +// set the timer number for a GPIO, ignore if the GPIO is not set or Timer number is invalid +// Timer range is 0..3 +void analogSetTimer(uint8_t pin, uint8_t timer); + +// get the next unused timer, returns -1 if no free timer is available +// Keep in mind that Timer 0 is reserved, which leaves only 3 timers available +// +// This function does not reserve the timer, it is reserved only when you assign a GPIO to it +int32_t analogNextFreeTimer(); + +// Get timer resolution (in bits) - default 10 +uint8_t analogGetTimerResolution(uint8_t timer); +// void analogSetTimerResolution(uint8_t timer, uint8_t resolution); + +// Get timer frequency (in Hz) - default 977 +uint32_t analogGetTimerFrequency(uint8_t timer); +// void analogSetTimerFrequency(uint8_t timer, uint32_t freq); + +// void analogSetTimerResFreq(uint8_t timer, uint8_t resolution, uint32_t freq); /*********************************************************************************************/ diff --git a/tasmota/tasmota_support/support_pwm.ino b/tasmota/tasmota_support/support_pwm.ino index b5ec67d72..962d7e2bf 100644 --- a/tasmota/tasmota_support/support_pwm.ino +++ b/tasmota/tasmota_support/support_pwm.ino @@ -68,31 +68,49 @@ void PwmSaveToSettings(void) { void PwmApplyGPIO(bool force_update_all) { uint32_t pwm_phase_accumulator = 0; // dephase each PWM channel with the value of the previous + uint8_t timer0_resolution = analogGetTimerResolution(0); + uint32_t timer0_freq = analogGetTimerFrequency(0); + + // AddLog(LOG_LEVEL_INFO, "PWM: resol0=%i freq0=%i", timer0_resolution, timer0_freq); + for (uint32_t i = 0; i < MAX_PWMS; i++) { + // compute `pwm_val`, the virtual value of PWM (not taking into account inverted) uint32_t pwm_val = TasmotaGlobal.pwm_cur_value[i]; // logical value of PWM, 0..1023 - if (TasmotaGlobal.pwm_value[i] >= 0) { pwm_val = TasmotaGlobal.pwm_value[i]; } // new value explicitly specified - if (pwm_val > Settings->pwm_range) { pwm_val = Settings->pwm_range; } // prevent overflow - - // compute phase uint32_t pwm_phase = TasmotaGlobal.pwm_cur_phase[i]; // pwm_phase is the logical phase of the active pulse, ignoring inverted - if (TasmotaGlobal.pwm_phase[i] >= 0) { - pwm_phase = TasmotaGlobal.pwm_phase[i]; // if explicit set explicitly, - } else if (Settings->flag5.pwm_force_same_phase) { - pwm_phase = 0; // if auto-phase is off - } else { - // compute auto-phase - pwm_phase = pwm_phase_accumulator; - // accumulate phase for next GPIO - pwm_phase_accumulator = (pwm_phase + pwm_val) & Settings->pwm_range; - } // apply new values to GPIO if GPIO is set - // AddLog(LOG_LEVEL_INFO, "PWM: i=%i used=%i pwm_val=%03X vs %03X pwm_phase=%03X vs %03X", i, PinUsed(GPIO_PWM1, i), pwm_val, TasmotaGlobal.pwm_cur_value[i], pwm_phase, TasmotaGlobal.pwm_cur_phase[i]); if (PinUsed(GPIO_PWM1, i)) { + int32_t pin = Pin(GPIO_PWM1, i); + int32_t chan = analogGetChannel2(pin); + // int32_t timer = analogGetTimer(pin); + uint32_t res = ledcReadResolution(chan); + uint32_t range = (1 << res) - 1; + uint32_t freq = ledcReadFreq2(chan); + + // AddLog(LOG_LEVEL_INFO, "PWM: res0=%i freq0=%i pin=%i chan=%i res=%i timer=%i range=%i freq=%i", timer0_resolution, timer0_freq, pin, chan, res, analogGetTimerForChannel(chan), range, freq); + + if (TasmotaGlobal.pwm_value[i] >= 0) { pwm_val = TasmotaGlobal.pwm_value[i]; } // new value explicitly specified + if (pwm_val > range) { pwm_val = range; } // prevent overflow + + // compute phase + if (TasmotaGlobal.pwm_phase[i] >= 0) { + pwm_phase = TasmotaGlobal.pwm_phase[i]; // if explicit set explicitly, + } else if (Settings->flag5.pwm_force_same_phase) { + pwm_phase = 0; // if auto-phase is off + } else { + if (freq == timer0_freq && res == timer0_resolution) { // only apply if the frequency is equl to global one + // compute auto-phase only if default frequency + pwm_phase = pwm_phase_accumulator; + // accumulate phase for next GPIO + pwm_phase_accumulator = (pwm_phase + pwm_val) & range; + } + } + // AddLog(LOG_LEVEL_INFO, "PWM: i=%i used=%i pwm_val=%03X vs %03X pwm_phase=%03X vs %03X", i, PinUsed(GPIO_PWM1, i), pwm_val, TasmotaGlobal.pwm_cur_value[i], pwm_phase, TasmotaGlobal.pwm_cur_phase[i]); + if (force_update_all || (pwm_val != TasmotaGlobal.pwm_cur_value[i]) || (pwm_phase != TasmotaGlobal.pwm_cur_phase[i])) { // GPIO has PWM and there is a chnage to apply, apply it - analogWritePhase(Pin(GPIO_PWM1, i), pwm_val, pwm_phase); + analogWritePhase(pin, pwm_val, pwm_phase); // AddLog(LOG_LEVEL_INFO, "PWM: analogWritePhase i=%i val=%03X phase=%03X", i, pwm_val, pwm_phase); } } @@ -131,6 +149,7 @@ void GpioInitPwm(void) { for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only if (PinUsed(GPIO_PWM1, i)) { analogAttach(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i)); + if (i < TasmotaGlobal.light_type) { // force PWM GPIOs to black TasmotaGlobal.pwm_value[i] = 0; @@ -157,6 +176,40 @@ void ResetPwm(void) PwmApplyGPIO(true); } +void CmndPwmfrequency(void) +{ + int32_t pwm_frequency = Settings->pwm_frequency; + int32_t pwm = -1; // PWM being targeted, or -1 for global value applied to Timer 0 + + // check if index if above 100, meaning we target only a specific PWM channel + uint32_t parm[2] = { 0, 0 }; + ParseParameters(2, parm); + + if (parm[1]) { // we have a second parameter + pwm = parm[1] - 1; + if (pwm < 0 || pwm >= MAX_PWMS) { pwm = -1; } // if invalid, revert to global value + } + + // AddLog(LOG_LEVEL_INFO, "PWM: payload=%i index=%i pwm=%i pwm_freqency=%i", XdrvMailbox.payload, XdrvMailbox.index , pwm, pwm_frequency); + if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { + pwm_frequency = (1 == XdrvMailbox.payload) ? PWM_FREQ : XdrvMailbox.payload; + + if (pwm >= 0 && PinUsed(GPIO_PWM1, pwm)) { + analogWriteFreq(pwm_frequency, Pin(GPIO_PWM1, pwm)); + } else { + // apply to all default PWM + // AddLog(LOG_LEVEL_INFO, "PWM: apply global freq=%i", pwm_frequency); + Settings->pwm_frequency = pwm_frequency; + analogWriteFreq(pwm_frequency); // Default is 977 + } +#ifdef USE_LIGHT + LightReapplyColor(); + LightAnimate(); +#endif // USE_LIGHT + } + ResponseCmndNumber(pwm_frequency); +} + #else // now for ESP8266 void PwmRearmChanges(void) {} @@ -210,8 +263,6 @@ void ResetPwm(void) } } -#endif // ESP8266 - void CmndPwmfrequency(void) { if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload >= PWM_MIN) && (XdrvMailbox.payload <= PWM_MAX))) { @@ -225,6 +276,8 @@ void CmndPwmfrequency(void) ResponseCmndNumber(Settings->pwm_frequency); } +#endif // ESP8266 + void CmndPwmrange(void) { // Support only 8 (=255), 9 (=511) and 10 (=1023) bits resolution if ((1 == XdrvMailbox.payload) || ((XdrvMailbox.payload > 254) && (XdrvMailbox.payload < 1024))) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index 39e9c8d68..8a85c0c2d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -156,6 +156,9 @@ void ShutterUpdateVelocity(uint8_t i) void ShutterRtc50mS(void) { +#ifdef ESP32 + bool pwm_apply = false; // ESP32 only, do we need to apply PWM changes +#endif // No Logging allowed. RTC Timer for (uint8_t i = 0; i < TasmotaGlobal.shutters_present; i++) { if (Shutter[i].direction) { @@ -184,13 +187,16 @@ void ShutterRtc50mS(void) #ifdef ESP32 analogWriteFreq(Shutter[i].pwm_velocity,Pin(GPIO_PWM1, i)); TasmotaGlobal.pwm_value[i] = 512; - PwmApplyGPIO(false); + pwm_apply = true; #endif // ESP32 } break; } } // if (Shutter[i].direction) } +#ifdef ESP32 + if (pwm_apply) { PwmApplyGPIO(false); } +#endif } int32_t ShutterPercentToRealPosition(int16_t percent, uint32_t index) @@ -458,6 +464,9 @@ void ShutterCalculateAccelerator(uint8_t i) void ShutterDecellerateForStop(uint8_t i) { +#ifdef ESP32 + bool pwm_apply = false; // ESP32 only, do we need to apply PWM changes +#endif switch (ShutterGlobal.position_mode) { case SHT_PWM_VALUE: case SHT_COUNTER: @@ -482,7 +491,7 @@ void ShutterDecellerateForStop(uint8_t i) #endif #ifdef ESP32 TasmotaGlobal.pwm_value[i] = 0; - PwmApplyGPIO(false); + pwm_apply = true; #endif // ESP32 Shutter[i].real_position = ShutterCalculatePosition(i); //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Remain steps %d"), missing_steps); @@ -492,6 +501,9 @@ void ShutterDecellerateForStop(uint8_t i) Shutter[i].pwm_velocity = 0; break; } +#ifdef ESP32 + if (pwm_apply) { PwmApplyGPIO(false); } +#endif } void ShutterPowerOff(uint8_t i) From f45cd5e905d13b5bcc0f269fdb12f76e18b216af Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 2 Aug 2022 10:07:55 +0200 Subject: [PATCH 155/219] Fixed comments and cleaned up --- .../src/esp8266toEsp32.cpp | 47 ++++++------ .../src/esp8266toEsp32.h | 73 +++++++++---------- tasmota/tasmota_support/support_pwm.ino | 1 - 3 files changed, 59 insertions(+), 62 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index b5e8450e9..dd47d8166 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -147,6 +147,30 @@ int32_t _analog_pin2timer(uint32_t pin) { // returns -1 if uallocated return timer; } +// get the next unused timer, returns -1 if no free timer is available +// Keep in mind that Timer 0 is reserved, which leaves only 3 timers available +// +// This function does not reserve the timer, it is reserved only when you assign a GPIO to it +static int32_t analogNextFreeTimer() { + _analogInit(); // make sure the mapping array is initialized + bool assigned[MAX_TIMERS] = {}; + assigned[0] = true; + + for (uint32_t chan = 0; chan < MAX_PWMS; chan++) { + assigned[pwm_timer[chan]] = true; + } + + // find first free + for (uint32_t j = 0; j < MAX_TIMERS; j++) { + if (!assigned[j]) { + // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer next_timer=%i", j); + return j; + } + } + // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer no free timer"); + return -1; // none available +} + // input range is in full range, ledc needs bits uint32_t _analogGetResolution(uint32_t x) { uint32_t bits = 0; @@ -177,7 +201,7 @@ void analogWriteRange(uint32_t range, int32_t pin) { // `-1`: keep unchanged // if pin < 0 then change global value for timer 0 void analogWriteFreqRange(int32_t freq, int32_t range, int32_t pin) { - AddLog(LOG_LEVEL_INFO, "PWM: analogWriteFreqRange freq=%i range=%i pin=%i", freq, range, pin); + // AddLog(LOG_LEVEL_INFO, "PWM: analogWriteFreqRange freq=%i range=%i pin=%i", freq, range, pin); _analogInit(); // make sure the mapping array is initialized uint32_t timer0_freq = timer_freq_hz[0]; // global values uint8_t timer0_res = timer_duty_resolution[0]; @@ -389,27 +413,6 @@ int32_t analogGetTimerForChannel(uint8_t chan) { // get the next unused timer, returns -1 if no free timer is available // Keep in mind that Timer 0 is reserved, which leaves only 3 timers available // -// This function does not reserve the timer, it is reserved only when you assign a GPIO to it -int32_t analogNextFreeTimer() { - _analogInit(); // make sure the mapping array is initialized - bool assigned[MAX_TIMERS] = {}; - assigned[0] = true; - - for (uint32_t chan = 0; chan < MAX_PWMS; chan++) { - assigned[pwm_timer[chan]] = true; - } - - // find first free - for (uint32_t j = 0; j < MAX_TIMERS; j++) { - if (!assigned[j]) { - // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer next_timer=%i", j); - return j; - } - } - // AddLog(LOG_LEVEL_INFO, "PWM: analogNextFreeTimer no free timer"); - return -1; // none available -} - // Get timer resolution (in bits) - default 10 uint8_t analogGetTimerResolution(uint8_t timer) { _analogInit(); // make sure the mapping array is initialized diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index bc6e17c8b..b43dd354d 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -28,19 +28,19 @@ * * The following supersedes Arduino framework and provides more granular control: * - fine grained phase control (in addition to duty cycle) - * - fine control of which PWM uses which timers - * - fine control of frequency and resolution per timer + * - fine control of PWM frequency and resolution per GPIO * - * By default, all PWM are using the same timer called Timer0. - * Changes in frequency of resolution apply to all PWM using Timer0. - * - * You can specify a different timer per GPIO (Timer1 to Timer3). - * Then any change of resolution or frequency to these GPIOs alter only - * the corresponding timer. + * By default, all PWM are using the same timer called Timer 0. + * Changes in frequency of resolution apply to all PWM using Timer 0. * + * You can specify a different a different resolution/frequency for + * specific GPIOs, this will internally assign a new timer to the GPIO. + * The limit is 3 specific values in addition to the global value. + * * Note: on ESP32-only, there are 2 groups of PWM and 2 groups of timers. - * - PWM 0..7 are using Timer 0..3 - * - PWM 8..15 are using Timer 4..7 + * Although there are internally 8 timers, to simplifiy management, + * Timer 4..7 are mirrored from Timer 0..3. + * So it all happens like if there were only 4 timers and a single group of PWM channels. \*******************************************************************************************/ uint32_t ledcReadFreq2(uint8_t chan); @@ -55,29 +55,38 @@ uint8_t ledcReadResolution(uint8_t chan); // Returns: hardware channel number, or -1 if it failed int analogAttach(uint32_t pin, bool output_invert = false); // returns the ledc channel, or -1 if failed. This is implicitly called by analogWrite if the channel was not already allocated +// change both freq and range +// `0`: set to global value +// `-1`: keep unchanged +// if pin < 0 then change global value for timer 0 + +// +// analogWriteFreqRange - change the range and/or frequency of a GPIO +// +// `void analogWriteFreqRange(int32_t freq, int32_t range, int32_t pin)` +// +// The range is converted to a number of bits, so range must be a power of 2 minus 1. +// By default, the resolution is 10 bits, i.e. a range of 1023. +// +// Special cases: +// - if `pin < 0`, changes the global value for Timer 0 and all PWM using default +// - if `range == 0` or `freq == 0`, revert to using Timer 0 (i.e. reassign to global values) +// - if `range < 0` or `freq < 0`, keep the previous value unchanged +// - if `pin` is unassigned, silently ignore +void analogWriteFreqRange(int32_t freq, int32_t range, int32_t pin = -1); + // // analogWriteRange - change the range of PWM // -// Internally the range is converted to a number of bits, so range must be a power of 2. -// By default, the resolution is 10 bits, i.e. a range of 1024. -// -// When changing this value, it changes the underlying Timer. This means -// that the range changes for all PWM using the same timer. -// -// If `pin == 255`, the value is changed for the default Timer(s) -// I.e. `Timer 0` (and `Timer 4` on ESP32) +// short-cut for: +// `analogWriteFreqRange(-1, range, pin)` void analogWriteRange(uint32_t range, int32_t pin = -1); // // analogWriteFreq - change the frequency of PWM in Hz // -// Default value is 977Hz. TODO: use the value in `my_user_config.h` -// -// When changing this value, it changes the underlying Timer. This means -// that the range changes for all PWM using the same timer. -// -// If `pin == 255`, the value is changed for the default Timer(s) -// I.e. `Timer 0` (and `Timer 4` on ESP32) +// short-cut for: +// `analogWriteFreqRange(-1, range, pin)` void analogWriteFreq(uint32_t freq, int32_t pin = -1); // @@ -99,25 +108,11 @@ extern int32_t analogGetChannel2(uint32_t pin); int32_t analogGetTimer(uint8_t pin); int32_t analogGetTimerForChannel(uint8_t chan); -// set the timer number for a GPIO, ignore if the GPIO is not set or Timer number is invalid -// Timer range is 0..3 -void analogSetTimer(uint8_t pin, uint8_t timer); - -// get the next unused timer, returns -1 if no free timer is available -// Keep in mind that Timer 0 is reserved, which leaves only 3 timers available -// -// This function does not reserve the timer, it is reserved only when you assign a GPIO to it -int32_t analogNextFreeTimer(); - // Get timer resolution (in bits) - default 10 uint8_t analogGetTimerResolution(uint8_t timer); -// void analogSetTimerResolution(uint8_t timer, uint8_t resolution); // Get timer frequency (in Hz) - default 977 uint32_t analogGetTimerFrequency(uint8_t timer); -// void analogSetTimerFrequency(uint8_t timer, uint32_t freq); - -// void analogSetTimerResFreq(uint8_t timer, uint8_t resolution, uint32_t freq); /*********************************************************************************************/ diff --git a/tasmota/tasmota_support/support_pwm.ino b/tasmota/tasmota_support/support_pwm.ino index 962d7e2bf..f729b628a 100644 --- a/tasmota/tasmota_support/support_pwm.ino +++ b/tasmota/tasmota_support/support_pwm.ino @@ -83,7 +83,6 @@ void PwmApplyGPIO(bool force_update_all) { if (PinUsed(GPIO_PWM1, i)) { int32_t pin = Pin(GPIO_PWM1, i); int32_t chan = analogGetChannel2(pin); - // int32_t timer = analogGetTimer(pin); uint32_t res = ledcReadResolution(chan); uint32_t range = (1 << res) - 1; uint32_t freq = ledcReadFreq2(chan); From 678bccda4edc069ffa5946dd313250a80e33c124 Mon Sep 17 00:00:00 2001 From: barbudor Date: Tue, 2 Aug 2022 19:23:07 +0200 Subject: [PATCH 156/219] Update BUILDS.md about TAS_DISCO vs HS_DISCO --- BUILDS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BUILDS.md b/BUILDS.md index 551bed7f0..6a6217fba 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -13,7 +13,8 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_UFILESYS | - | - / x | - | - | - | - | | USE_ARDUINO_OTA | - | - / - | - | - | - | - | | USE_DOMOTICZ | - | x / x | x | x | x | - | -| USE_HOME_ASSISTANT | - | x / x | x | x | x | - | +| USE_HOME_ASSISTANT | - | - / - | - | - | - | - | +| USE_TASMOTA_DISCOVERY | x | x / x | x | x | x | x | | USE_MQTT_TLS | - | - / - | - | - | - | - | | USE_MQTT_AWS_IOT | - | - / - | - | - | - | - | | USE_4K_RSA | - | - / - | - | - | - | - | From 63ded1fd7554f15c17e886f74f950cf6b8c37f1c Mon Sep 17 00:00:00 2001 From: barbudor Date: Tue, 2 Aug 2022 19:26:54 +0200 Subject: [PATCH 157/219] About MQTT_TLS on ESP32 --- BUILDS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BUILDS.md b/BUILDS.md index 6a6217fba..b0e6584e6 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -15,7 +15,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_DOMOTICZ | - | x / x | x | x | x | - | | USE_HOME_ASSISTANT | - | - / - | - | - | - | - | | USE_TASMOTA_DISCOVERY | x | x / x | x | x | x | x | -| USE_MQTT_TLS | - | - / - | - | - | - | - | +| USE_MQTT_TLS* | - | - / x | - | - | - | - | | USE_MQTT_AWS_IOT | - | - / - | - | - | - | - | | USE_4K_RSA | - | - / - | - | - | - | - | | USE_TELEGRAM | - | - / - | - | - | - | - | @@ -251,3 +251,5 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up | USE_I2S_AUDIO | | / - | | | | | | USE_TTGO_WATCH | | / - | | | | | | USE_SONOFF_SPM | | / x | | | | | + +* USE_MQTT_TLS is enabled by default in every ESP32 variants \ No newline at end of file From abf352c8f85cee94d345c2d2a779158c89a50b8d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 2 Aug 2022 19:39:27 +0200 Subject: [PATCH 158/219] Fix remapping of ledcReadFreq --- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp | 5 ++--- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h | 2 +- platformio_tasmota32.ini | 1 + tasmota/tasmota_support/support_pwm.ino | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index dd47d8166..83c51c32d 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -318,9 +318,8 @@ int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc chan return chan; } -uint32_t ledcReadFreq2(uint8_t chan) { -// _Z13ledcReadFreqh -// extern "C" uint32_t _Z13ledcReadFreqh(uint8_t chan) { +// uint32_t ledcReadFreq2(uint8_t chan) { +extern "C" uint32_t __wrap_ledcReadFreq(uint8_t chan) { if (chan > MAX_PWMS) { return 0; // wrong channel } diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index b43dd354d..3bfb412a5 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -43,7 +43,7 @@ * So it all happens like if there were only 4 timers and a single group of PWM channels. \*******************************************************************************************/ -uint32_t ledcReadFreq2(uint8_t chan); +extern "C" uint32_t ledcReadFreq(uint8_t chan); uint8_t ledcReadResolution(uint8_t chan); // // analogAttach - attach a GPIO to a hardware PWM diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index b735fee57..81bdf580b 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -33,6 +33,7 @@ build_flags = ${esp_defaults.build_flags} ; wrappers for the crash-recorder -Wl,--wrap=panicHandler -Wl,--wrap=xt_unhandled_exception -Wl,--wrap=_Z11analogWritehi ; `analogWrite(unsigned char, int)` use the Tasmota version of analogWrite for deeper integration and phase control + -Wl,--wrap=ledcReadFreq ; `uint32_t ledcReadFreq(uint8_t chan)` extra_scripts = pre:pio-tools/add_c_flags.py post:pio-tools/post_esp32.py ${esp_defaults.extra_scripts} diff --git a/tasmota/tasmota_support/support_pwm.ino b/tasmota/tasmota_support/support_pwm.ino index f729b628a..f34a82cfc 100644 --- a/tasmota/tasmota_support/support_pwm.ino +++ b/tasmota/tasmota_support/support_pwm.ino @@ -85,7 +85,7 @@ void PwmApplyGPIO(bool force_update_all) { int32_t chan = analogGetChannel2(pin); uint32_t res = ledcReadResolution(chan); uint32_t range = (1 << res) - 1; - uint32_t freq = ledcReadFreq2(chan); + uint32_t freq = ledcReadFreq(chan); // AddLog(LOG_LEVEL_INFO, "PWM: res0=%i freq0=%i pin=%i chan=%i res=%i timer=%i range=%i freq=%i", timer0_resolution, timer0_freq, pin, chan, res, analogGetTimerForChannel(chan), range, freq); From 9f29e7c40dec08c66a9235145f5eaad1cb36461f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 2 Aug 2022 22:52:35 +0200 Subject: [PATCH 159/219] Support for multiple `IRsend` GPIOs --- CHANGELOG.md | 1 + tasmota/include/i18n.h | 1 + tasmota/include/tasmota.h | 1 + tasmota/include/tasmota_template.h | 2 +- .../xdrv_05_irremote_full.ino | 98 ++++++++++++------- 5 files changed, 64 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66cbb8cbd..5a99ee2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) +- Support for multiple `IRsend` GPIOs ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index a4eb333da..3a8a7382c 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -511,6 +511,7 @@ #define D_JSON_IR_HASH "Hash" #define D_JSON_IR_RAWDATA "RawData" #define D_JSON_IR_REPEAT "Repeat" + #define D_JSON_IR_CHANNEL "Channel" #define D_CMND_IRHVAC "IRHVAC" #define D_JSON_IRHVAC_VENDOR "Vendor" #define D_JSON_IRHVAC_PROTOCOL "Protocol" diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index b210e0932..e993167e6 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -93,6 +93,7 @@ const uint8_t MAX_SHUTTERS = 4; // Max number of shutters const uint8_t MAX_SHUTTER_KEYS = 4; // Max number of shutter keys or buttons const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices const uint8_t MAX_DS3502 = 4; // Max number of DS3502 digitsal potentiometer devices +const uint8_t MAX_IRSEND = 16; // Max number of IRSEND GPIOs const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules const uint16_t VL53LXX_MAX_SENSORS = 8; // Max number of VL53L0X sensors diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 06bbfb0a6..59fd24f54 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -721,7 +721,7 @@ const uint16_t kGpioNiceList[] PROGMEM = { \*-------------------------------------------------------------------------------------------*/ #if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL) - AGPIO(GPIO_IRSEND), // IR remote + AGPIO(GPIO_IRSEND) + MAX_IRSEND, // IR remote #if defined(USE_IR_RECEIVE) || defined(USE_IR_REMOTE_FULL) AGPIO(GPIO_IRRECV), // IR receiver #endif diff --git a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino index 53e157c86..0c6da380f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino @@ -80,6 +80,9 @@ const char kIrRemoteCommands[] PROGMEM = "|" void (* const IrRemoteCommand[])(void) PROGMEM = { &CmndIrHvac, &CmndIrSend }; +bool ir_send_active = false; // do we have a GPIO configured for IR_SEND +bool ir_recv_active = false; // do we have a GPIO configured for IR_RECV + /*********************************************************************************************\ * Class used to make a compact IR Raw format. * @@ -122,7 +125,6 @@ protected: * IR Send \*********************************************************************************************/ -IRsend *irsend = nullptr; // some ACs send toggle messages rather than state. we need to help IRremoteESP8266 keep track of the state // have a flag that is a variable, can be later used to convert this functionality to an option (as in SetOptionXX) bool irhvac_stateful = true; @@ -134,10 +136,18 @@ enum class StateModes { SEND_ONLY, // just send the IR signal, this is the defau SEND_STORE }; // send IR signal but also update stored state. this is for use cases when there is just one transmitter and there is no receiver in the device. StateModes strToStateMode(class JsonParserToken token, StateModes def); // declate to prevent errors related to ino files -void IrSendInit(void) -{ - irsend = new IRsend(Pin(GPIO_IRSEND), IR_SEND_INVERTED, IR_SEND_USE_MODULATION); // an IR led is at GPIO_IRSEND - irsend->begin(); +// initialize an `IRsend` static instance +// channel is the IRSEND channel number (from 0..) +class IRsend IrSendInitGPIO(int32_t channel = -1) { + if (channel < 0) { channel = GPIO_ANY; } // take first available GPIO + int32_t pin = Pin(GPIO_IRSEND, channel); + if (pin < 0) { + pin = Pin(GPIO_IRSEND, GPIO_ANY); + AddLog(LOG_LEVEL_INFO, PSTR("IR : GPIO 'IRsend-%i' not assigned, revert to GPIO %i"), channel+1, pin); + } + IRsend irsend(pin, IR_SEND_INVERTED, IR_SEND_USE_MODULATION); // an IR led is at GPIO_IRSEND + irsend.begin(); + return irsend; } // from https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte @@ -279,8 +289,7 @@ void sendIRJsonState(const struct decode_results &results) { } } -void IrReceiveCheck(void) -{ +void IrReceiveCheck(void) { decode_results results; if (irrecv->decode(&results)) { @@ -462,7 +471,14 @@ uint32_t IrRemoteCmndIrHvacJson(void) if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } if (stateMode == StateModes::SEND_ONLY || stateMode == StateModes::SEND_STORE) { - IRac ac(Pin(GPIO_IRSEND), IR_SEND_INVERTED, IR_SEND_USE_MODULATION); + int8_t channel = root.getUInt(PSTR(D_JSON_IR_CHANNEL), 1) - 1; + if (channel < 0) { channel = GPIO_ANY; } // take first available GPIO + int32_t pin = Pin(GPIO_IRSEND, channel); + if (pin < 0) { + pin = Pin(GPIO_IRSEND, GPIO_ANY); + AddLog(LOG_LEVEL_INFO, PSTR("IR : GPIO 'IRsend-%i' not assigned, revert to GPIO %i"), channel+1, pin); + } + IRac ac(pin, IR_SEND_INVERTED, IR_SEND_USE_MODULATION); bool success = ac.sendAc(state, irhvac_stateful && irac_prev_state.protocol == state.protocol ? &irac_prev_state : nullptr); if (!success) { return IE_SYNTAX_IRHVAC; } } @@ -511,6 +527,7 @@ uint32_t IrRemoteCmndIrSendJson(void) uint16_t bits = root.getUInt(PSTR(D_JSON_IR_BITS), 0); uint16_t repeat = root.getUInt(PSTR(D_JSON_IR_REPEAT), 0); + int8_t channel = root.getUInt(PSTR(D_JSON_IR_CHANNEL), 1) - 1; uint64_t data; value = root[PSTR(D_JSON_IR_DATALSB)]; @@ -528,7 +545,8 @@ uint32_t IrRemoteCmndIrSendJson(void) // protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } - bool success = irsend->send(protocol, data, bits, repeat); + IRsend irsend = IrSendInitGPIO(channel); + bool success = irsend.send(protocol, data, bits, repeat); if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } if (!success) { @@ -553,8 +571,9 @@ uint32_t IrRemoteSendGC(char ** pp, uint32_t count, uint32_t repeat) { if (!GC[i]) { return IE_INVALID_RAWDATA; } } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendGC(GC, count+1); + irsend.sendGC(GC, count+1); } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } return IE_NO_ERROR; @@ -608,11 +627,12 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { } } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw count=%d, space=%d, mark=%d, freq=%d"), count, space, mark, freq); - irsend->sendRaw(raw_array, i, freq); + irsend.sendRaw(raw_array, i, freq); if (r < repeat) { // if it's not the last message - irsend->space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap + irsend.space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap } } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } @@ -635,11 +655,12 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { } raw_array[i++] = parm[2]; // Trailing mark if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw %d %d %d %d %d %d"), raw_array[0], raw_array[1], raw_array[2], raw_array[3], raw_array[4], raw_array[5]); - irsend->sendRaw(raw_array, i, freq); + irsend.sendRaw(raw_array, i, freq); if (r < repeat) { // if it's not the last message - irsend->space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap + irsend.space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap } } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } @@ -712,8 +733,9 @@ uint32_t IrRemoteSendRawStandard(char ** pp, uint16_t freq, uint32_t count, uint if (0 == count) { return IE_INVALID_RAWDATA; } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { - irsend->sendRaw(arr, count, freq); + irsend.sendRaw(arr, count, freq); } if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } @@ -772,8 +794,7 @@ uint32_t IrRemoteCmndIrSendRaw(void) } } -void CmndIrSend(void) -{ +void CmndIrSend(void) { uint8_t error = IE_SYNTAX_IRSEND; if (XdrvMailbox.data_len) { @@ -815,6 +836,11 @@ void IrRemoteCmndResponse(uint32_t error) } } +void IrInit(void) { + ir_send_active = PinUsed(GPIO_IRSEND, GPIO_ANY); + ir_recv_active = PinUsed(GPIO_IRRECV, GPIO_ANY); +} + /*********************************************************************************************\ * Interface \*********************************************************************************************/ @@ -823,27 +849,23 @@ bool Xdrv05(uint8_t function) { bool result = false; - if (PinUsed(GPIO_IRSEND) || PinUsed(GPIO_IRRECV)) { - switch (function) { - case FUNC_PRE_INIT: - if (PinUsed(GPIO_IRSEND)) { - IrSendInit(); - } - if (PinUsed(GPIO_IRRECV)) { - IrReceiveInit(); - } - break; - case FUNC_EVERY_50_MSECOND: - if (PinUsed(GPIO_IRRECV)) { - IrReceiveCheck(); // check if there's anything on IR side - } - break; - case FUNC_COMMAND: - if (PinUsed(GPIO_IRSEND)) { - result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); - } - break; - } + switch (function) { + case FUNC_PRE_INIT: + IrInit(); + if (ir_recv_active) { + IrReceiveInit(); + } + break; + case FUNC_EVERY_50_MSECOND: + if (ir_recv_active) { + IrReceiveCheck(); // check if there's anything on IR side + } + break; + case FUNC_COMMAND: + if (ir_send_active) { + result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand); + } + break; } return result; } From 4dc6adf37f0627924caeacb2a1cd9e1d041055d6 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 2 Aug 2022 23:21:12 +0200 Subject: [PATCH 160/219] Temp fix for ledcReadFreq() --- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp | 4 ++-- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h | 2 +- tasmota/tasmota_support/support_pwm.ino | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index 83c51c32d..e4703a435 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -318,8 +318,8 @@ int32_t analogAttach(uint32_t pin, bool output_invert) { // returns ledc chan return chan; } -// uint32_t ledcReadFreq2(uint8_t chan) { -extern "C" uint32_t __wrap_ledcReadFreq(uint8_t chan) { +extern "C" uint32_t ledcReadFreq2(uint8_t chan) { +// extern "C" uint32_t __wrap_ledcReadFreq(uint8_t chan) { if (chan > MAX_PWMS) { return 0; // wrong channel } diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h index 3bfb412a5..f6a6338c6 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.h @@ -43,7 +43,7 @@ * So it all happens like if there were only 4 timers and a single group of PWM channels. \*******************************************************************************************/ -extern "C" uint32_t ledcReadFreq(uint8_t chan); +extern "C" uint32_t ledcReadFreq2(uint8_t chan); uint8_t ledcReadResolution(uint8_t chan); // // analogAttach - attach a GPIO to a hardware PWM diff --git a/tasmota/tasmota_support/support_pwm.ino b/tasmota/tasmota_support/support_pwm.ino index f34a82cfc..f729b628a 100644 --- a/tasmota/tasmota_support/support_pwm.ino +++ b/tasmota/tasmota_support/support_pwm.ino @@ -85,7 +85,7 @@ void PwmApplyGPIO(bool force_update_all) { int32_t chan = analogGetChannel2(pin); uint32_t res = ledcReadResolution(chan); uint32_t range = (1 << res) - 1; - uint32_t freq = ledcReadFreq(chan); + uint32_t freq = ledcReadFreq2(chan); // AddLog(LOG_LEVEL_INFO, "PWM: res0=%i freq0=%i pin=%i chan=%i res=%i timer=%i range=%i freq=%i", timer0_resolution, timer0_freq, pin, chan, res, analogGetTimerForChannel(chan), range, freq); From c6a5cabc72db6dcc77a3eac544daed5085429aa1 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 2 Aug 2022 23:32:23 +0200 Subject: [PATCH 161/219] Fix IRsend GPIO init --- tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino index 0c6da380f..bd757144a 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino @@ -839,6 +839,13 @@ void IrRemoteCmndResponse(uint32_t error) void IrInit(void) { ir_send_active = PinUsed(GPIO_IRSEND, GPIO_ANY); ir_recv_active = PinUsed(GPIO_IRRECV, GPIO_ANY); + if (ir_send_active) { + for (uint32_t chan = 0; chan < MAX_IRSEND; chan++) { + if (PinUsed(GPIO_IRSEND, chan)) { + IrSendInitGPIO(chan); + } + } + } } /*********************************************************************************************\ From 02aa43238416a966ecd1c00ab8065e7bc1e93758 Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Tue, 2 Aug 2022 22:25:20 -0700 Subject: [PATCH 162/219] Move Wifi Struct definition to a header file Move Wifi Struct definition to a header file in order to be accessible from functions outside support_wifi.ino file --- tasmota/include/tasmota_globals.h | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tasmota/include/tasmota_globals.h b/tasmota/include/tasmota_globals.h index abdb5deb2..deaebd316 100644 --- a/tasmota/include/tasmota_globals.h +++ b/tasmota/include/tasmota_globals.h @@ -593,4 +593,38 @@ bool first_device_group_is_local = true; /*********************************************************************************************/ + +enum WifiTestOptions { WIFI_NOT_TESTING, WIFI_TESTING, WIFI_TEST_FINISHED, WIFI_TEST_FINISHED_BAD }; + +struct WIFI { + uint32_t last_event = 0; // Last wifi connection event + uint32_t downtime = 0; // Wifi down duration + uint16_t link_count = 0; // Number of wifi re-connect + uint8_t counter; + uint8_t retry_init; + uint8_t retry; + uint8_t max_retry; + uint8_t status; + uint8_t config_type = 0; + uint8_t config_counter = 0; + uint8_t scan_state; + uint8_t bssid[6]; + int8_t best_network_db; + uint8_t wifiTest = WIFI_NOT_TESTING; + uint8_t wifi_test_counter = 0; + uint16_t save_data_counter = 0; + uint8_t old_wificonfig = MAX_WIFI_OPTION; // means "nothing yet saved here" + bool wifi_test_AP_TIMEOUT = false; + bool wifi_Test_Restart = false; + bool wifi_Test_Save_SSID2 = false; +} Wifi; + +// Reference. WiFi.encryptionType = +// 2 : ENC_TYPE_TKIP - WPA / PSK +// 4 : ENC_TYPE_CCMP - WPA2 / PSK +// 5 : ENC_TYPE_WEP - WEP +// 7 : ENC_TYPE_NONE - open network +// 8 : ENC_TYPE_AUTO - WPA / WPA2 / PSK +const char kWifiEncryptionTypes[] PROGMEM = "0" "|" "1" "|" "WPA/PSK" "|" "3" "|" "WPA2/PSK" "|" "WEP" "|" "6" "|" "OPEN" "|" "WPA/WPA2/PSK"; + #endif // _TASMOTA_GLOBALS_H_ From de84f6781f11723bd44ffd6cf6fa8f41b8f14c3d Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Tue, 2 Aug 2022 22:28:59 -0700 Subject: [PATCH 163/219] Add new commands definitions (WifiScan, WifiTest) --- tasmota/include/i18n.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index 3a8a7382c..bb8e27f3d 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -207,6 +207,11 @@ #define D_JSON_WEIGHT "Weight" #define D_JSON_WIFI "Wifi" #define D_JSON_WIFI_MODE "Mode" +#define D_JSON_NOT_STARTED "Not Started" +#define D_JSON_SCANNING "Scanning" +#define D_JSON_TESTING "Testing" +#define D_JSON_BUSY "Busy" +#define D_JSON_ENCRYPTION "Encryption" #define D_JSON_WRONG "Wrong" #define D_JSON_WRONG_PARAMETERS "Wrong parameters" #define D_JSON_YESTERDAY "Yesterday" @@ -309,6 +314,8 @@ #define D_CMND_PASSWORD "Password" #define D_CMND_HOSTNAME "Hostname" #define D_CMND_WIFI "Wifi" +#define D_CMND_WIFISCAN "WiFiScan" +#define D_CMND_WIFITEST "WiFiTest" #define D_CMND_ETHERNET "Ethernet" #define D_CMND_WIFICONFIG "WifiConfig" #define D_WCFG_0_RESTART "Restart" From ca2bc5ae70dd4f034fe2bb92f123046e5d65f8ed Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Tue, 2 Aug 2022 22:31:29 -0700 Subject: [PATCH 164/219] Add new logic for wifiscan command --- tasmota/tasmota_support/support_wifi.ino | 92 ++++++++++++++++++------ 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 7215e4682..e715b0429 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -45,22 +45,6 @@ const uint8_t WIFI_RETRY_OFFSET_SEC = WIFI_RETRY_SECONDS; // seconds #include // IPv6 DualStack #endif // LWIP_IPV6=1 -struct WIFI { - uint32_t last_event = 0; // Last wifi connection event - uint32_t downtime = 0; // Wifi down duration - uint16_t link_count = 0; // Number of wifi re-connect - uint8_t counter; - uint8_t retry_init; - uint8_t retry; - uint8_t max_retry; - uint8_t status; - uint8_t config_type = 0; - uint8_t config_counter = 0; - uint8_t scan_state; - uint8_t bssid[6]; - int8_t best_network_db; -} Wifi; - int WifiGetRssiAsQuality(int rssi) { int quality = 0; @@ -351,6 +335,75 @@ void WifiBeginAfterScan(void) } } } + + // Init scan for wifiscan command + if (6 == Wifi.scan_state) { + if (wifi_scan_result != WIFI_SCAN_RUNNING) { + WiFi.scanNetworks(true); // Start wifi scan async + Wifi.scan_state++; + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network scan started...")); + return; + } + } + // Check scan done + if (7 == Wifi.scan_state) { + if (wifi_scan_result != WIFI_SCAN_RUNNING) { + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network scan finished...")); + Wifi.scan_state++; + return; + } + } + // Scan done. Show SSId's scan result by MQTT and in console + if (7 < Wifi.scan_state) { + Wifi.scan_state++; + + ResponseClear(); + + uint32_t initial_item = (Wifi.scan_state - 9)*10; + + if ( wifi_scan_result > initial_item ) { + // Sort networks by RSSI + uint32_t indexes[wifi_scan_result]; + for (uint32_t i = 0; i < wifi_scan_result; i++) { + indexes[i] = i; + } + for (uint32_t i = 0; i < wifi_scan_result; i++) { + for (uint32_t j = i + 1; j < wifi_scan_result; j++) { + if (WiFi.RSSI(indexes[j]) > WiFi.RSSI(indexes[i])) { + std::swap(indexes[i], indexes[j]); + } + } + } + delay(0); + + // Publish the list + char stemp1[20]; + uint32_t end_item = ( wifi_scan_result > initial_item + 10 ) ? initial_item + 10 : wifi_scan_result; + for (uint32_t i = initial_item; i < end_item; i++) { + Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL + "\":\"%d\", \"" D_JSON_SIGNAL "\":\"%d\", \"" D_RSSI "\":\"%d\", \"" D_JSON_ENCRYPTION "\":\"%s\"}}}"), + i+1, + WiFi.SSID(indexes[i]).c_str(), + WiFi.BSSIDstr(indexes[i]).c_str(), + WiFi.channel(indexes[i]), + WiFi.RSSI(indexes[i]), + WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), + GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN)); + } + } else if (9 == Wifi.scan_state) { + Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":\"" D_NO_NETWORKS_FOUND "\"}")); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN)); + } + delay(0); + } + // Wait 1 minute before cleaning the results so the user can ask for the them using wifiscan command (HTTP use-case) + if (69 == Wifi.scan_state) { + //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "Network scan results deleted...")); + Wifi.scan_state = 0; + WiFi.scanDelete(); // Clean up Ram + } + } uint16_t WifiLinkCount(void) @@ -523,8 +576,6 @@ void WifiCheck(uint8_t param) TasmotaGlobal.restart_flag = 2; } } else { - if (Wifi.scan_state) { WifiBeginAfterScan(); } - if (Wifi.counter <= 0) { AddLog(LOG_LEVEL_DEBUG_MORE, PSTR(D_LOG_WIFI D_CHECKING_CONNECTION)); Wifi.counter = WIFI_CHECK_SEC; @@ -534,7 +585,7 @@ void WifiCheck(uint8_t param) WifiSetState(1); if (Settings->flag3.use_wifi_rescan) { // SetOption57 - Scan wifi network every 44 minutes for configured AP's if (!(TasmotaGlobal.uptime % (60 * WIFI_RESCAN_MINUTES))) { - Wifi.scan_state = 2; + if (!Wifi.scan_state) { Wifi.scan_state = 2; } // If wifi scan routine is free, use it. Otherwise, wait for next RESCAN TIME } } } else { @@ -542,6 +593,7 @@ void WifiCheck(uint8_t param) Mdns.begun = 0; } } + if (Wifi.scan_state) { WifiBeginAfterScan(); } } } @@ -881,5 +933,3 @@ uint32_t WifiGetNtp(void) { ntp_server_id++; // Next server next time return 0; } - - From 922ece0959f4a4f8aa25927b7f6a02616afc7df5 Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Tue, 2 Aug 2022 22:32:55 -0700 Subject: [PATCH 165/219] Add WifiScan and WifiTest commands --- tasmota/tasmota_support/support_command.ino | 152 +++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index a41560e12..5750e279b 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -33,7 +33,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|" D_CMND_WIFI "|" D_CMND_DNSTIMEOUT "|" D_CMND_DEVICENAME "|" D_CMND_FN "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_LEDPWM_ON "|" D_CMND_LEDPWM_OFF "|" D_CMND_LEDPWM_MODE "|" - D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_GLOBAL_PRESS "|" D_CMND_SWITCHTEXT "|" + D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|" D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM"|" D_CMND_GLOBAL_PRESS "|" D_CMND_SWITCHTEXT "|" D_CMND_WIFISCAN "|" D_CMND_WIFITEST "|" #ifdef USE_I2C D_CMND_I2CSCAN "|" D_CMND_I2CDRIVER "|" #endif @@ -68,7 +68,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = { &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig, &CmndWifi, &CmndDnsTimeout, &CmndDevicename, &CmndFriendlyname, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd, &CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndLedPwmOn, &CmndLedPwmOff, &CmndLedPwmMode, - &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndGlobalPress, &CmndSwitchText, + &CmndWifiPower, &CmndTempOffset, &CmndHumOffset, &CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum, &CmndGlobalPress, &CmndSwitchText, &CmndWifiScan, &CmndWifiTest, #ifdef USE_I2C &CmndI2cScan, &CmndI2cDriver, #endif @@ -91,6 +91,154 @@ const char kWifiConfig[] PROGMEM = /********************************************************************************************/ +#ifndef FIRMWARE_MINIMAL_ONLY +void CmndWifiScan(void) +{ + if (XdrvMailbox.data_len > 0) { + if ( !Wifi.scan_state || Wifi.scan_state > 7 ) { + ResponseCmndChar(D_JSON_SCANNING); + Wifi.scan_state = 6; + } else { + ResponseCmndChar(D_JSON_BUSY); + } + } else { + if ( !Wifi.scan_state ) { + ResponseCmndChar(D_JSON_NOT_STARTED); + } else if ( Wifi.scan_state >= 1 && Wifi.scan_state <= 5 ) { + ResponseCmndChar(D_JSON_BUSY); + } else if ( Wifi.scan_state >= 6 && Wifi.scan_state <= 7 ) { + ResponseCmndChar(D_JSON_SCANNING); + } else { //show scan result + Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":")); + + if (WiFi.scanComplete() > 0) { + // Sort networks by RSSI + uint32_t indexes[WiFi.scanComplete()]; + for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { + indexes[i] = i; + } + for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { + for (uint32_t j = i + 1; j < WiFi.scanComplete(); j++) { + if (WiFi.RSSI(indexes[j]) > WiFi.RSSI(indexes[i])) { + std::swap(indexes[i], indexes[j]); + } + } + } + delay(0); + + char stemp1[20]; + for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { + ResponseAppend_P(PSTR("{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL + "\":\"%d\", \"" D_JSON_SIGNAL "\":\"%d\", \"" D_RSSI "\":\"%d\", \"" D_JSON_ENCRYPTION "\":\"%s\"}"), + i+1, + WiFi.SSID(indexes[i]).c_str(), + WiFi.BSSIDstr(indexes[i]).c_str(), + WiFi.channel(indexes[i]), + WiFi.RSSI(indexes[i]), + WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), + GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); + if ( ResponseSize() < ResponseLength() + 300 ) { break; } + if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(", ")); } + //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "MAX SIZE: %d, SIZE: %d"),ResponseSize(),ResponseLength()); + } + ResponseJsonEnd(); + } else { + ResponseAppend_P(PSTR("\"" D_NO_NETWORKS_FOUND "\"")); + } + ResponseJsonEnd(); + MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN)); + } + } +} + +void CmndWifiTest(void) +{ + // Test WIFI Connection to Router if Tasmota is in AP mode since in AP mode, a STA connection can be established + // at the same time for testing the connection. + +#ifdef USE_WEBSERVER + if (!WifiIsInManagerMode()) { ResponseCmndError(); return; } + + if ( (XdrvMailbox.data_len > 0) ) { + + if (Wifi.wifiTest != WIFI_TESTING) { // Start Test + char* pos = strchr(XdrvMailbox.data, '+'); + if (pos != nullptr) { + char ssid_test[XdrvMailbox.data_len]; + char pswd_test[XdrvMailbox.data_len]; + subStr(ssid_test, XdrvMailbox.data, "+", 1); + subStr(pswd_test, XdrvMailbox.data, "+", 2); + ResponseCmndIdxChar(D_JSON_TESTING); + //Response_P(PSTR("{\"%s%d\":{\"Network\":\"%s,\"PASS\":\"%s\"}}"), XdrvMailbox.command, XdrvMailbox.index, ssid_test, pswd_test); + + if (WIFI_NOT_TESTING == Wifi.wifiTest) { + if (MAX_WIFI_OPTION == Wifi.old_wificonfig) { Wifi.old_wificonfig = Settings->sta_config; } + TasmotaGlobal.wifi_state_flag = Settings->sta_config = WIFI_MANAGER; + Wifi.save_data_counter = TasmotaGlobal.save_data_counter; + } + + Wifi.wifi_test_counter = 9; // seconds to test user's proposed AP + Wifi.wifiTest = WIFI_TESTING; + TasmotaGlobal.save_data_counter = 0; // Stop auto saving data - Updating Settings + Settings->save_data = 0; + TasmotaGlobal.sleep = 0; // Disable sleep + TasmotaGlobal.restart_flag = 0; // No restart + TasmotaGlobal.ota_state_flag = 0; // No OTA + + Wifi.wifi_Test_Restart = false; + Wifi.wifi_Test_Save_SSID2 = false; + if (0 == XdrvMailbox.index) { Wifi.wifi_Test_Restart = true; } // If WifiTest is successful, save data on SSID1 and restart + if (2 == XdrvMailbox.index) { Wifi.wifi_Test_Save_SSID2 = true; } // If WifiTest is successful, save data on SSID2 + + SettingsUpdateText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1, ssid_test); + SettingsUpdateText(Wifi.wifi_Test_Save_SSID2 ? SET_STAPWD2 : SET_STAPWD1, pswd_test); + + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECTING_TO_AP " %s " D_AS " %s ..."), + SettingsText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1), TasmotaGlobal.hostname); + + WiFi.begin(SettingsText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1), SettingsText(Wifi.wifi_Test_Save_SSID2 ? SET_STAPWD2 : SET_STAPWD1)); + } + } else { + ResponseCmndChar(D_JSON_BUSY); + } + + } else { + switch (Wifi.wifiTest) { + case WIFI_TESTING: + ResponseCmndChar(D_JSON_TESTING); + break; + case WIFI_NOT_TESTING: + ResponseCmndChar(D_JSON_NOT_STARTED); + break; + case WIFI_TEST_FINISHED: + ResponseCmndChar(Wifi.wifi_test_AP_TIMEOUT ? D_CONNECT_FAILED_AP_TIMEOUT : D_JSON_SUCCESSFUL); + break; + case WIFI_TEST_FINISHED_BAD: + + switch (WiFi.status()) { + case WL_CONNECTED: + ResponseCmndChar(D_CONNECT_FAILED_NO_IP_ADDRESS); + break; + case WL_NO_SSID_AVAIL: + ResponseCmndChar(D_CONNECT_FAILED_AP_NOT_REACHED); + break; + case WL_CONNECT_FAILED: + ResponseCmndChar(D_CONNECT_FAILED_WRONG_PASSWORD); + break; + default: // WL_IDLE_STATUS and WL_DISCONNECTED - SSId in range but no answer from the router + ResponseCmndChar(D_CONNECT_FAILED_AP_TIMEOUT); + } + + break; + } + } +#else + ResponseCmndError(); +#endif //USE_WEBSERVER +} + +#endif // not defined FIRMWARE_MINIMAL_ONLY + void ResponseCmndNumber(int value) { Response_P(S_JSON_COMMAND_NVALUE, XdrvMailbox.command, value); } From f514d1702d288729db53be31ddc38ff78e0e3f01 Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Tue, 2 Aug 2022 22:35:16 -0700 Subject: [PATCH 166/219] Move ScanNetwork variables to wifi struct --- .../xdrv_01_9_webserver.ino | 99 +++++++++---------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino index f6e81f76b..b169af2a4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_01_9_webserver.ino @@ -426,14 +426,12 @@ const char kUploadErrors[] PROGMEM = D_UPLOAD_ERR_1 "|" D_UPLOAD_ERR_2 "|" D_UPLOAD_ERR_3 "|" D_UPLOAD_ERR_4 "| |" D_UPLOAD_ERR_6 "|" D_UPLOAD_ERR_7 "|" D_UPLOAD_ERR_8 "|" D_UPLOAD_ERR_9; const uint16_t DNS_PORT = 53; -enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY}; -enum WifiTestOptions {WIFI_NOT_TESTING, WIFI_TESTING, WIFI_TEST_FINISHED, WIFI_TEST_FINISHED_BAD}; - +enum HttpOptions { HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER, HTTP_MANAGER_RESET_ONLY }; enum WebCmndStatus { WEBCMND_DONE=0, WEBCMND_WRONG_PARAMETERS, WEBCMND_CONNECT_FAILED, WEBCMND_HOST_NOT_FOUND, WEBCMND_MEMORY_ERROR #ifdef USE_WEBGETCONFIG , WEBCMND_FILE_NOT_FOUND, WEBCMND_OTHER_HTTP_ERROR, WEBCMND_CONNECTION_LOST, WEBCMND_INVALID_FILE #endif // USE_WEBGETCONFIG -}; + }; DNSServer *DnsServer; ESP8266WebServer *Webserver; @@ -448,11 +446,6 @@ struct WEB { bool upload_services_stopped = false; bool reset_web_log_flag = false; // Reset web console log bool initial_config = false; - uint8_t wifiTest = WIFI_NOT_TESTING; - uint8_t wifi_test_counter = 0; - uint16_t save_data_counter = 0; - uint8_t old_wificonfig = MAX_WIFI_OPTION; // means "nothing yet saved here" - bool wifi_test_AP_TIMEOUT = false; } Web; // Helper function to avoid code duplication (saves 4k Flash) @@ -607,6 +600,11 @@ void StartWebserver(int type, IPAddress ipweb) XdrvCall(FUNC_WEB_ADD_HANDLER); XsnsCall(FUNC_WEB_ADD_HANDLER); #endif // Not FIRMWARE_MINIMAL + + if (!Web.initial_config) { + Web.initial_config = !strlen(SettingsText(SET_STASSID1)) || !strlen(SettingsText(SET_STASSID2)); + if (Web.initial_config) { AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Blank Device - Initial Configuration")); } + } } Web.reset_web_log_flag = false; @@ -1135,10 +1133,6 @@ void HandleRoot(void) HandleWifiLogin(); } else { if (!strlen(SettingsText(SET_WEBPWD)) || (((Webserver->arg(F("USER1")) == WEB_USERNAME ) && (Webserver->arg(F("PASS1")) == SettingsText(SET_WEBPWD) )) || HTTP_MANAGER_RESET_ONLY == Web.state)) { - if (!Web.initial_config) { - Web.initial_config = !strlen(SettingsText(SET_STASSID1)); - if (Web.initial_config) { AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Blank Device - Initial Configuration")); } - } HandleWifiConfiguration(); } else { // wrong user and pass @@ -1864,14 +1858,14 @@ void HandleWifiConfiguration(void) { // Test WIFI Connection to Router // As Tasmota is in this case in AP mode, a STA connection can be established too at the same time - if (WIFI_NOT_TESTING == Web.wifiTest) { - if (MAX_WIFI_OPTION == Web.old_wificonfig) { Web.old_wificonfig = Settings->sta_config; } + if (WIFI_NOT_TESTING == Wifi.wifiTest) { + if (MAX_WIFI_OPTION == Wifi.old_wificonfig) { Wifi.old_wificonfig = Settings->sta_config; } TasmotaGlobal.wifi_state_flag = Settings->sta_config = WIFI_MANAGER; - Web.save_data_counter = TasmotaGlobal.save_data_counter; + Wifi.save_data_counter = TasmotaGlobal.save_data_counter; } - Web.wifi_test_counter = 9; // seconds to test user's proposed AP - Web.wifiTest = WIFI_TESTING; + Wifi.wifi_test_counter = 9; // seconds to test user's proposed AP + Wifi.wifiTest = WIFI_TESTING; TasmotaGlobal.save_data_counter = 0; // Stop auto saving data - Updating Settings Settings->save_data = 0; TasmotaGlobal.sleep = 0; // Disable sleep @@ -1899,9 +1893,9 @@ void HandleWifiConfiguration(void) { return; } - if ( WIFI_TEST_FINISHED == Web.wifiTest ) { - Web.wifiTest = WIFI_NOT_TESTING; - if (Web.wifi_test_AP_TIMEOUT) { + if ( WIFI_TEST_FINISHED == Wifi.wifiTest ) { + Wifi.wifiTest = WIFI_NOT_TESTING; + if (Wifi.wifi_test_AP_TIMEOUT) { WebRestart(1); // Save credentials and Force Restart in STA only mode (11n-only routers) } else { #if (RESTART_AFTER_INITIAL_WIFI_CONFIG) @@ -1915,7 +1909,7 @@ void HandleWifiConfiguration(void) { WSContentStart_P(PSTR(D_CONFIGURE_WIFI), !WifiIsInManagerMode()); WSContentSend_P(HTTP_SCRIPT_WIFI); if (WifiIsInManagerMode()) { WSContentSend_P(HTTP_SCRIPT_HIDE); } - if (WIFI_TESTING == Web.wifiTest) { WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_RESTART_RECONNECT_TIME); } + if (WIFI_TESTING == Wifi.wifiTest) { WSContentSend_P(HTTP_SCRIPT_RELOAD_TIME, HTTP_RESTART_RECONNECT_TIME); } #ifdef USE_ENHANCED_GUI_WIFI_SCAN WSContentSendStyle_P(HTTP_HEAD_STYLE_SSI, WebColor(COL_TEXT)); #else @@ -1924,7 +1918,7 @@ void HandleWifiConfiguration(void) { bool limitScannedNetworks = true; if (HTTP_MANAGER_RESET_ONLY != Web.state) { - if (WIFI_TESTING == Web.wifiTest) { + if (WIFI_TESTING == Wifi.wifiTest) { limitScannedNetworks = false; } else { if (Webserver->hasArg(F("scan"))) { limitScannedNetworks = false; } @@ -2078,14 +2072,14 @@ void HandleWifiConfiguration(void) { WSContentTextCenterStart(WebColor(COL_TEXT_WARNING)); WSContentSend_P(PSTR("

")); - if (WIFI_TESTING == Web.wifiTest) { + if (WIFI_TESTING == Wifi.wifiTest) { WSContentSend_P(PSTR(D_TRYING_TO_CONNECT "
%s

"), SettingsText(SET_STASSID1)); - } else if (WIFI_TEST_FINISHED_BAD == Web.wifiTest) { + } else if (WIFI_TEST_FINISHED_BAD == Wifi.wifiTest) { WSContentSend_P(PSTR(D_CONNECT_FAILED_TO " %s
" D_CHECK_CREDENTIALS ""), SettingsText(SET_STASSID1)); } // More Options Button WSContentSend_P(PSTR("

"), - (WIFI_TEST_FINISHED_BAD == Web.wifiTest) ? "none" : Web.initial_config ? "block" : "none", Web.initial_config ? "block" : "none" + (WIFI_TEST_FINISHED_BAD == Wifi.wifiTest) ? "none" : Web.initial_config ? "block" : "none", Web.initial_config ? "block" : "none" ); WSContentSpaceButton(BUTTON_RESTORE, !Web.initial_config); WSContentButton(BUTTON_RESET_CONFIGURATION, !Web.initial_config); @@ -3711,44 +3705,47 @@ bool Xdrv01(uint8_t function) if (Web.initial_config) { Wifi.config_counter = 200; // Do not restart the device if it has SSId Blank } - if (Web.wifi_test_counter) { - Web.wifi_test_counter--; + if (Wifi.wifi_test_counter) { + Wifi.wifi_test_counter--; AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI D_TRYING_TO_CONNECT " %s"), SettingsText(SET_STASSID1)); if (WifiHasIP()) { // Got IP - Connection Established - Web.wifi_test_AP_TIMEOUT = false; - Web.wifi_test_counter = 0; - Web.wifiTest = WIFI_TEST_FINISHED; - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_SSID "1 %s: " D_CONNECTED " - " D_IP_ADDRESS " %_I"), SettingsText(SET_STASSID1), (uint32_t)WiFi.localIP()); + Wifi.wifi_test_AP_TIMEOUT = false; + Wifi.wifi_test_counter = 0; + Wifi.wifiTest = WIFI_TEST_FINISHED; + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_SSID " %s: " D_CONNECTED " - " D_IP_ADDRESS " %_I"), SettingsText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1), (uint32_t)WiFi.localIP()); // TasmotaGlobal.blinks = 255; // Signal wifi connection with blinks - if (MAX_WIFI_OPTION != Web.old_wificonfig) { - TasmotaGlobal.wifi_state_flag = Settings->sta_config = Web.old_wificonfig; + if (MAX_WIFI_OPTION != Wifi.old_wificonfig) { + TasmotaGlobal.wifi_state_flag = Settings->sta_config = Wifi.old_wificonfig; } - TasmotaGlobal.save_data_counter = Web.save_data_counter; - Settings->save_data = Web.save_data_counter; + TasmotaGlobal.save_data_counter = Wifi.save_data_counter; + Settings->save_data = Wifi.save_data_counter; SettingsSaveAll(); + + if ( Wifi.wifi_Test_Restart ) { TasmotaGlobal.restart_flag = 2; } + #if (!RESTART_AFTER_INITIAL_WIFI_CONFIG) Web.initial_config = false; Web.state = HTTP_ADMIN; #endif - } else if (!Web.wifi_test_counter) { // Test TimeOut - Web.wifi_test_counter = 0; - Web.wifiTest = WIFI_TEST_FINISHED_BAD; + } else if (!Wifi.wifi_test_counter) { // Test TimeOut + Wifi.wifi_test_counter = 0; + Wifi.wifiTest = WIFI_TEST_FINISHED_BAD; switch (WiFi.status()) { case WL_CONNECTED: AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_NO_IP_ADDRESS)); - Web.wifi_test_AP_TIMEOUT = false; + Wifi.wifi_test_AP_TIMEOUT = false; break; case WL_NO_SSID_AVAIL: AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_NOT_REACHED)); - Web.wifi_test_AP_TIMEOUT = false; + Wifi.wifi_test_AP_TIMEOUT = false; break; case WL_CONNECT_FAILED: AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_WRONG_PASSWORD)); - Web.wifi_test_AP_TIMEOUT = false; + Wifi.wifi_test_AP_TIMEOUT = false; break; default: // WL_IDLE_STATUS and WL_DISCONNECTED - SSId in range but no answer from the router AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CONNECT_FAILED_AP_TIMEOUT)); - // If this error occurs twice, Tasmota will connect directly to the router without testing crendentials. + // If this error occurs twice, Tasmota will connect directly to the router without testing credentials. // ESP8266 in AP+STA mode can manage only 11b and 11g, so routers that are 11n-ONLY won't respond. // For this case, the user will see in the UI a message to check credentials. After that, if the user hits // save and connect again, and the CONNECT_FAILED_AP_TIMEOUT is shown again, Credentials will be saved and @@ -3756,17 +3753,17 @@ bool Xdrv01(uint8_t function) // // If it fails again, depending on the WIFICONFIG settings, the user will need to wait or will need to // push 6 times the button to enable Tasmota AP mode again. - if (Web.wifi_test_AP_TIMEOUT) { - Web.wifiTest = WIFI_TEST_FINISHED; - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_SSID "1 %s: " D_ATTEMPTING_CONNECTION), SettingsText(SET_STASSID1) ); - if (MAX_WIFI_OPTION != Web.old_wificonfig) { - TasmotaGlobal.wifi_state_flag = Settings->sta_config = Web.old_wificonfig; + if (Wifi.wifi_test_AP_TIMEOUT) { + Wifi.wifiTest = WIFI_TEST_FINISHED; + AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_WIFI D_CMND_SSID " %s: " D_ATTEMPTING_CONNECTION), SettingsText(Wifi.wifi_Test_Save_SSID2 ? SET_STASSID2 : SET_STASSID1) ); + if (MAX_WIFI_OPTION != Wifi.old_wificonfig) { + TasmotaGlobal.wifi_state_flag = Settings->sta_config = Wifi.old_wificonfig; } - TasmotaGlobal.save_data_counter = Web.save_data_counter; - Settings->save_data = Web.save_data_counter; + TasmotaGlobal.save_data_counter = Wifi.save_data_counter; + Settings->save_data = Wifi.save_data_counter; SettingsSaveAll(); } - Web.wifi_test_AP_TIMEOUT = true; + Wifi.wifi_test_AP_TIMEOUT = true; } WiFi.scanNetworks(); // restart scan } From 058f9ecea9c5b3d99e08846695fa66efb1f7ca15 Mon Sep 17 00:00:00 2001 From: Adrian Scillato Date: Wed, 3 Aug 2022 02:39:51 -0700 Subject: [PATCH 167/219] Fix WifiScan JSON Response --- tasmota/tasmota_support/support_command.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 5750e279b..026073422 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -127,8 +127,9 @@ void CmndWifiScan(void) delay(0); char stemp1[20]; + ResponseAppend_P(PSTR("{")); for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { - ResponseAppend_P(PSTR("{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL + ResponseAppend_P(PSTR("\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL "\":\"%d\", \"" D_JSON_SIGNAL "\":\"%d\", \"" D_RSSI "\":\"%d\", \"" D_JSON_ENCRYPTION "\":\"%s\"}"), i+1, WiFi.SSID(indexes[i]).c_str(), From d545d9cac66d8b8e375ab05cf9cf2e7f5852a134 Mon Sep 17 00:00:00 2001 From: HardwareBoffin <110466589+HardwareBoffin@users.noreply.github.com> Date: Wed, 3 Aug 2022 12:01:38 +0200 Subject: [PATCH 168/219] Update README.md Removed official tasmota logo, added repo intention. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cb330d44b..7160b7a40 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg) +This is a FORK providing ( in a few days I hope ) the translation file for the Catalan language. + Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) and [ESP32](https://en.wikipedia.org/wiki/ESP32) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**. _Written for PlatformIO._ From 2e24aeccd705a7bdec04196ab44e4d58740d8c50 Mon Sep 17 00:00:00 2001 From: HardwareBoffin <110466589+HardwareBoffin@users.noreply.github.com> Date: Wed, 3 Aug 2022 12:03:43 +0200 Subject: [PATCH 169/219] Add files via upload Work in progress for testing integration --- tasmota/language/ca_AD.h | 1132 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1132 insertions(+) create mode 100644 tasmota/language/ca_AD.h diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h new file mode 100644 index 000000000..ec02e00f9 --- /dev/null +++ b/tasmota/language/ca_AD.h @@ -0,0 +1,1132 @@ +/* + ca-AD.h - localization for Catalan - Països Catalans for Tasmota + + Copyright (C) 2022 Albert Gonzalez + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _LANGUAGE_CA_AD_H_ +#define _LANGUAGE_CA_AD_H_ + +/*************************** ATTENTION *******************************\ + * + * Due to memory constraints only UTF-8 is supported. + * To save code space keep text as short as possible. + * Time and Date provided by SDK can not be localized (yet). + * Use online command StateText to translate ON, OFF, HOLD and TOGGLE. + * Use online command Prefix to translate cmnd, stat and tele. + * + * Updated until v9.4.0.1 +\*********************************************************************/ + +#define LANGUAGE_MODULE_NAME // Enable to display "Module Generic" (ie Spanish), Disable to display "Generic Module" (ie English) +// https://www.science.co.il/language/Locale-codes.php +#define LANGUAGE_LCID 1027 +// HTML (ISO 639-1) Language Code +#define D_HTML_LANGUAGE "ca" + +// "2017-03-07T11:08:02" - ISO8601:2004 +#define D_YEAR_MONTH_SEPARATOR "-" +#define D_MONTH_DAY_SEPARATOR "-" +#define D_DATE_TIME_SEPARATOR "T" +#define D_HOUR_MINUTE_SEPARATOR ":" +#define D_MINUTE_SECOND_SEPARATOR ":" + +#define D_DAY3LIST "DiuDllDmaDmeDijDivDis" +#define D_MONTH3LIST "GenFebMarAbrMaiJunJulAgoSetOctNovDec" + +// Non JSON decimal separator +#define D_DECIMAL_SEPARATOR "," + +// Common +#define D_ADMIN "Admin" +#define D_AIR_QUALITY "Qualitat Aire" +#define D_AP "PA" // Access Point +#define D_AS "com" +#define D_AUTO "AUTO" +#define D_BATT "Bat" // Short for Battery +#define D_BLINK "Espurna" +#define D_BLINKOFF "Espurna Fora" +#define D_BOOT_COUNT "Compte Arrencs" +#define D_BRIGHTLIGHT "Brillant" +#define D_BSSID "BSSId" +#define D_BUTTON "Botó" +#define D_BY "per" // Written by me +#define D_BYTES "Bytes" +#define D_CELSIUS "Celsius" +#define D_CHANNEL "Canal" +#define D_CO2 "Diòxid de Carboni" +#define D_CODE "codi" // Button code +#define D_COLDLIGHT "Freda" +#define D_COMMAND "Ordre" +#define D_CONNECTED "Connectat" +#define D_CORS_DOMAIN "Domini CORS" +#define D_COUNT "Compta" +#define D_COUNTER "Comptador" +#define D_CT_POWER "Energia CT" +#define D_CURRENT "Intensitat" // As in Voltage and Current +#define D_CURRENT_NEUTRAL "Intensitat Neutre" +#define D_DATA "Data" +#define D_DARKLIGHT "Fosc" +#define D_DEBUG "Depuració" +#define D_DEWPOINT "Punt de rossada" +#define D_DISABLED "Deshabilitat" +#define D_DISTANCE "Distancia" +#define D_DNS_SERVER "Servidor DNS" +#define D_DO "Oxígen dissolt" +#define D_DONE "Fet" +#define D_DST_TIME "DST" +#define D_EC "EC" +#define D_ECO2 "eCO₂" +#define D_EMULATION "Emulació" +#define D_ENABLED "Habilitat" +#define D_ERASE "Esborra" +#define D_ERROR "Error" +#define D_FAHRENHEIT "Fahrenheit" +#define D_FAILED "Fallada" +#define D_FALLBACK "Recuperació" +#define D_FALLBACK_TOPIC "Tema de recuperació" +#define D_FALSE "Fals" +#define D_FILE "Arxiu" +#define D_FLOW_RATE "Cabal" +#define D_FRAGMENTATION "frag." // Lower case abbreviated version of fragmentation used in "memory fragmentation" +#define D_FRAME_RATE "Frame rate" +#define D_FREE_MEMORY "Memòria lliure" +#define D_PSR_MAX_MEMORY "Memòria PS-RAM" +#define D_PSR_FREE_MEMORY "Memòria PS-RAM lliure" +#define D_FREQUENCY "Freqüència" +#define D_GAS "Gas" +#define D_GATEWAY "Portal" +#define D_GROUP "Grup" +#define D_HOST "Hoste" +#define D_HALL_EFFECT "Efecte Hall" +#define D_HOSTNAME "Nom de màquina" +#define D_HUMIDITY "Humitat" +#define D_ILLUMINANCE "Iluminància" +#define D_IMMEDIATE "immediat" // Button immediate +#define D_INDEX "Índex" +#define D_INFO "Info" +#define D_INFRARED "Infrarrojos" +#define D_INITIALIZED "Initialitzat" +#define D_IP_ADDRESS "Adreça IP" +#define D_LIGHT "Llum" +#define D_LWT "LWT" +#define D_LQI "LQI" // Zigbee Link Quality Index +#define D_MODULE "Mòdul" +#define D_MOISTURE "Humitat" +#define D_MQTT "MQTT" +#define D_MULTI_PRESS "multi-pressió" +#define D_NOISE "Soroll" +#define D_NONE "Cap" +#define D_O2 "Oxygen" +#define D_OFF "Apagat" +#define D_OFFLINE "Desconnectat" +#define D_OK "Ok" +#define D_ON "Engegat" +#define D_ONLINE "Conectat" +#define D_ORP "ORP" +#define D_PASSWORD "Mot de pas" +#define D_PH "pH" +#define D_MQ "MQ" +#define D_PARTITION "Partició" // As in flash and firmware partition +#define D_PORT "Port" +#define D_POWER_FACTOR "Factor de poténcia" +#define D_POWERUSAGE "Poténcia" +#define D_POWERUSAGE_ACTIVE "Poténcia Activa" +#define D_POWERUSAGE_APPARENT "Poténcia Aparent" +#define D_POWERUSAGE_REACTIVE "Poténcia Reactiva" +#define D_PRESSURE "Pressió" +#define D_PRESSUREATSEALEVEL "Pressió al mar" +#define D_PROGRAM_FLASH_SIZE "Mida de codi a Flash" +#define D_PROGRAM_SIZE "Mida de codi" +#define D_PROJECT "Projecte" +#define D_RAIN "Pluja" +#define D_RANGE "Rang" +#define D_RECEIVED "Rebut" +#define D_RESTART "Reinici" +#define D_RESTARTING "Reinciant" +#define D_RESTART_REASON "Raó de resinici" +#define D_RESTORE "recuperar" +#define D_RETAINED "retingut" +#define D_RULE "Regla" +#define D_SAVE "Guardar" +#define D_SENSOR "Sensor" +#define D_SSID "SSId" +#define D_START "Iniciar" +#define D_STD_TIME "STD" +#define D_STOP "Aturar" +#define D_SUBNET_MASK "Màscara de sub-xarxa" +#define D_SUBSCRIBE_TO "Subscriu a" +#define D_UNSUBSCRIBE_FROM "Des subscriu de" +#define D_SUCCESSFUL "Resultat correcte" +#define D_SUNRISE "Albada" +#define D_SUNSET "Posta de sol" +#define D_TEMPERATURE "Temperatura" +#define D_TO "a" +#define D_TOGGLE "Canvia" +#define D_TOPIC "Tòpic" +#define D_TOTAL_USAGE "Ús Total" +#define D_TRANSMIT "Transmet" +#define D_TRUE "Cert" +#define D_TVOC "TVOC" +#define D_UPGRADE "actualitza" +#define D_UPLOAD "Envia" +#define D_UPTIME "Temps engegat" +#define D_USED "usat" +#define D_USER "Usuari" +#define D_UTC_TIME "UTC" +#define D_UV_INDEX "Índex UV" +#define D_UV_INDEX_1 "Baix" +#define D_UV_INDEX_2 "Mig" +#define D_UV_INDEX_3 "Alt" +#define D_UV_INDEX_4 "Perill" +#define D_UV_INDEX_5 "Cremada L1/2" +#define D_UV_INDEX_6 "Cremada L3" +#define D_UV_INDEX_7 "Fora de rang" // Out of Range +#define D_UV_LEVEL "Nivell UV" +#define D_UV_POWER "Potència UV" +#define D_VERSION "Versió" +#define D_VOLTAGE "Tensió" +#define D_VOLUME "Volum" +#define D_WEIGHT "Pes" +#define D_WARMLIGHT "Calent" +#define D_WEB_SERVER "Servidor Web" +#define D_SOC "Estat de canvi" +#define D_SOH "Estat de salut" + +// tasmota.ino +#define D_WARNING_MINIMAL_VERSION "Avís : Aquesta versió no suporta configuració persistent" +#define D_LEVEL_10 "nivell 1-0" +#define D_LEVEL_01 "nivell 0-1" +#define D_SERIAL_LOGGING_DISABLED "Registre Sèrie deshabilitat" +#define D_SYSLOG_LOGGING_REENABLED "Registre Syslog re-habilitat" + +#define D_SET_BAUDRATE_TO "Posa bauds a" +#define D_RECEIVED_TOPIC "Tòpic rebut" +#define D_DATA_SIZE "Mida de dades" +#define D_ANALOG_INPUT "Analògic" + +// support.ino +#define D_OSWATCH "osWatch" +#define D_BLOCKED_LOOP "Bucle bloquejat" +#define D_WPS_FAILED_WITH_STATUS "Configuració WPS ha fallat per" +#define D_ACTIVE_FOR_3_MINUTES "actiu durant 3 minuts" +#define D_FAILED_TO_START "fNo he pogut iniciar" +#define D_PATCH_ISSUE_2186 "Patch issue 2186" +#define D_CONNECTING_TO_AP "Connectant al PA" +#define D_IN_MODE "en modus" +#define D_CONNECT_FAILED_NO_IP_ADDRESS "Connexió fallida ja que no hem rebut adreça IP" +#define D_CONNECT_FAILED_AP_NOT_REACHED "Connexió fallida ja que no hem localitzar el PA" +#define D_CONNECT_FAILED_WRONG_PASSWORD "Connexió fallida per mot de pas" +#define D_CONNECT_FAILED_AP_TIMEOUT "Connexió fallida per temps d'espera excedit" +#define D_ATTEMPTING_CONNECTION "Provant de fer connexió..." +#define D_CHECKING_CONNECTION "Verificant connexió..." +#define D_QUERY_DONE "Consulta feta. Hem trobat serveis MQTT" +#define D_MQTT_SERVICE_FOUND "Servei MQTT trobat a " +#define D_FOUND_AT "trobat a" +#define D_SYSLOG_HOST_NOT_FOUND "No hem trobat l'hoste de Syslog" + +// settings.ino +#define D_SAVED_TO_FLASH_AT "Guardat a flash a" +#define D_LOADED_FROM_FLASH_AT "Carregat des de flash a" +#define D_USE_DEFAULTS "Usa predeterminats" +#define D_ERASED_SECTOR "Sector esborrat" + +// xdrv_02_webserver.ino +#define D_NOSCRIPT "Per fer anar Tasmota, si us plau, habilita JavaScript" +#define D_SAFEBOOT "SAFEBOOT" +#define D_MINIMAL_FIRMWARE_PLEASE_UPGRADE "Firmware MÍNIM
si us plau, actualitza'm" +#define D_WEBSERVER_ACTIVE_ON "Servei Web actiu a" +#define D_WITH_IP_ADDRESS "amb adreça IP" +#define D_WEBSERVER_STOPPED "Servei Web aturat" +#define D_FILE_NOT_FOUND "Arxiu no trobat" +#define D_REDIRECTED "Redirigit al portal captiu" +#define D_WIFIMANAGER_SET_ACCESSPOINT_AND_STATION "Gestor wifi estableix PA i Estació" +#define D_WIFIMANAGER_SET_ACCESSPOINT "Gestor wifi estableix PA" +#define D_TRYING_TO_CONNECT "Provant de connectar a la xarxa" + +#define D_RESTART_IN "Reinici en" +#define D_SECONDS "segons" +#define D_DEVICE_WILL_RESTART "Em reiniciaré en pocs segons" +#define D_BUTTON_TOGGLE "Canvia" +#define D_CONFIGURATION "Configuració" +#define D_INFORMATION "Informació" +#define D_FIRMWARE_UPGRADE "Actualització de programari" +#define D_MANAGEMENT "Consoles" +#define D_CONSOLE "Consola" +#define D_CONFIRM_RESTART "Confirma el Reinici" + +#define D_CONFIGURE_MODULE "Configura Mòdul" +#define D_CONFIGURE_WIFI "Configura WiFi" +#define D_CONFIGURE_MQTT "Configura MQTT" +#define D_CONFIGURE_DOMOTICZ "Configura Domoticz" +#define D_CONFIGURE_LOGGING "Configura Logging" +#define D_CONFIGURE_OTHER "Configura Altres" +#define D_CONFIRM_RESET_CONFIGURATION "Confirma reiniciar la Configuració" +#define D_RESET_CONFIGURATION "Reiniciar la Configuració" +#define D_BACKUP_CONFIGURATION "Guardar la Configuració" +#define D_RESTORE_CONFIGURATION "Restaurar la Configuració" +#define D_MAIN_MENU "Menú Principal" + +#define D_MODULE_PARAMETERS "Paràmetes del Mòdul" +#define D_MODULE_TYPE "Tupus de Mòdul" +#define D_PULLUP_ENABLE "Habilitar pull-up" +#define D_ADC "ADC" +#define D_GPIO "GPIO" +#define D_SERIAL_IN "Entrada Sèrie" +#define D_SERIAL_OUT "Sortida sèrie" + +#define D_WIFI_PARAMETERS "Paràmetes Wifi" +#define D_SCAN_FOR_WIFI_NETWORKS "Cerca xarxes wifi" +#define D_SCAN_DONE "Recerca feta" +#define D_NO_NETWORKS_FOUND "No n'he trobat cap" +#define D_REFRESH_TO_SCAN_AGAIN "Refresca per tornar a cercar" +#define D_DUPLICATE_ACCESSPOINT "Punt d'accès Duplicat" +#define D_SKIPPING_LOW_QUALITY "Massa baixa qualitat" +#define D_RSSI "RSSI" +#define D_WEP "WEP" +#define D_WPA_PSK "WPA PSK" +#define D_WPA2_PSK "WPA2 PSK" +#define D_AP1_SSID "Xarxa WiFi" +#define D_AP1_SSID_HELP "Escriu o selecciona la xarxa Wifi" +#define D_AP2_SSID "Xarxa WiFi 2" +#define D_AP2_SSID_HELP "Escriu la Xarxa wifi Alternativa" +#define D_AP_PASSWORD "Mot de pas WiFi" +#define D_AP_PASSWORD_HELP "Escriu el mot de pas Wifi" +#define D_SELECT_YOUR_WIFI_NETWORK "Escull la teva xarxa wifi" +#define D_SHOW_MORE_WIFI_NETWORKS "Cerca totes les xarxes wifi" +#define D_SHOW_MORE_OPTIONS "Més opcions" +#define D_CHECK_CREDENTIALS "Prego revisis les credencials de xarxa" +#define D_SUCCESSFUL_WIFI_CONNECTION "Connexió Wifi aconseguida" +#define D_NOW_YOU_CAN_CLOSE_THIS_WINDOW "Ara pots tancar aquesta finestra" +#define D_REDIRECTING_TO_NEW_IP "Redirigint a la nova adreça IP" + +#define D_MQTT_PARAMETERS "Paràmetes MQTT" +#define D_CLIENT "Client" +#define D_FULL_TOPIC "Tema compert" + +#define D_LOGGING_PARAMETERS "Paràmetes de regsitre" +#define D_SERIAL_LOG_LEVEL "Nivell de registre sèrie" +#define D_MQTT_LOG_LEVEL "Nivell de registre Mqtt" +#define D_WEB_LOG_LEVEL "Nivell de registre Web" +#define D_SYS_LOG_LEVEL "Nivell de registre Syslog" +#define D_MORE_DEBUG "Més depuració" +#define D_SYSLOG_HOST "hoste de Syslog" +#define D_SYSLOG_PORT "port de Syslog" +#define D_TELEMETRY_PERIOD "Període de telemetria" + +#define D_OTHER_PARAMETERS "Altres paràmetres" +#define D_TEMPLATE "Plantilla" +#define D_ACTIVATE "Activar" +#define D_DEVICE_NAME "Nom del dispositiu" +#define D_WEB_ADMIN_PASSWORD "Mot de pas Admin Web" +#define D_MQTT_ENABLE "Habilitar MQTT" +#define D_MQTT_TLS_ENABLE "TLS MQTT" +#define D_HTTP_API "API HTTP" +#define D_HTTP_API_ENABLE "Habilitar HTTP API" +#define D_FRIENDLY_NAME "Nom senzill" +#define D_BELKIN_WEMO "Belkin WeMo" +#define D_HUE_BRIDGE "Hue Bridge" +#define D_SINGLE_DEVICE "dispositiu senzill" +#define D_MULTI_DEVICE "dispositiu múltiple" + +#define D_CONFIGURE_TEMPLATE "Plantilla de configuració" +#define D_TEMPLATE_PARAMETERS "Paràmetes de la plantilla" +#define D_TEMPLATE_NAME "Nom" +#define D_BASE_TYPE "Basada en" +#define D_TEMPLATE_FLAGS "Opcions" + +#define D_SAVE_CONFIGURATION "Guarda la configuració" +#define D_CONFIGURATION_SAVED "Configuració guardada" +#define D_CONFIGURATION_RESET "Configuració inicialitzada" + +#define D_PROGRAM_VERSION "Versió del programari" +#define D_BUILD_DATE_AND_TIME "Data i hora de construcció" +#define D_CORE_AND_SDK_VERSION "Versió del Nucli/SDK" +#define D_FLASH_WRITE_COUNT "Nombre d'escriptrues a la Flash" +#define D_MAC_ADDRESS "Adreça MAC" +#define D_MQTT_HOST "Hoste MQTT" +#define D_MQTT_PORT "Port MQTT" +#define D_MQTT_CLIENT "Client MQTT" +#define D_MQTT_USER "Usuari MQTT" +#define D_MQTT_TOPIC "Tema MQTT" +#define D_MQTT_GROUP_TOPIC "Tema de grup MQTT" +#define D_MQTT_FULL_TOPIC "Tema complert MQTT" +#define D_MQTT_NO_RETAIN "Sense retenció MQTT" +#define D_MDNS_DISCOVERY "Descobriment mDNS" +#define D_MDNS_ADVERTISE "Anunciar mDNS" +#define D_ESP_CHIP_ID "Id de xip ESP" +#define D_FLASH_CHIP_ID "Id de xip Flash" +#define D_FLASH_CHIP_SIZE "Mida de la Flash" +#define D_FREE_PROGRAM_SPACE "Espai lliure per programari" + +#define D_UPGRADE_BY_WEBSERVER "Actualitza amb servidor Web" +#define D_OTA_URL "URL OTA" +#define D_START_UPGRADE "Iniciar actualització" +#define D_UPGRADE_BY_FILE_UPLOAD "Actualització amb fitxer" +#define D_UPLOAD_FACTORY "Canviant a la partició d'arrenc segur" +#define D_UPLOAD_STARTED "Pujada iniciada" +#define D_UPGRADE_STARTED "Actualització iniciada" +#define D_UPLOAD_DONE "Actualització feta" +#define D_UPLOAD_TRANSFER "Transferència d'actualització" +#define D_TRANSFER_STARTED "Transferència iniciada" +#define D_UPLOAD_ERR_1 "No has sel·laccionat cap fitxer" +#define D_UPLOAD_ERR_2 "No queda prou espai" +#define D_UPLOAD_ERR_3 "Signatura de fitxer invàlida" +#define D_UPLOAD_ERR_4 "La mida del programa és superior a la flash, no hi entra" +#define D_UPLOAD_ERR_5 "Error de dades al buffer de pujada" +#define D_UPLOAD_ERR_6 "Pujada fallida. Habilita el registre de nivell 3" +#define D_UPLOAD_ERR_7 "Pujada abortada" +#define D_UPLOAD_ERR_8 "Arxiu invàlid" +#define D_UPLOAD_ERR_9 "Arxiu massa gran" +#define D_UPLOAD_ERR_10 "No he pogut incialitzar el xip de radiofreqüència" +#define D_UPLOAD_ERR_11 "No he pogut esborrar el xip de radiofreqüència" +#define D_UPLOAD_ERR_12 "No he pogut rescriure al xip de radiofreqüència" +#define D_UPLOAD_ERR_13 "No he pogut decodificar el programari de radiofreqüència" +#define D_UPLOAD_ERR_14 "No compatible" +#define D_UPLOAD_ERROR_CODE "Codi d'error de pujada" + +#define D_ENTER_COMMAND "Escriu ordre" +#define D_ENABLE_WEBLOG_FOR_RESPONSE "Habilitat weblog 2 si esperes resposta" +#define D_NEED_USER_AND_PASSWORD "Neecessito user=&password=" + +// xdrv_01_mqtt.ino +#define D_FINGERPRINT "Verifica la petjada TLS ..." +#define D_TLS_CONNECT_FAILED_TO "TLS Connect no ha pogut" +#define D_RETRY_IN "Reintent en" +#define D_VERIFIED "Verificat fet servir petjada" +#define D_INSECURE "Connexió no segura per que la petjada és invàlida" +#define D_CONNECT_FAILED_TO "Connectar ha fallat de" + +// xplg_wemohue.ino +#define D_MULTICAST_DISABLED "Multicast deshabilitat" +#define D_MULTICAST_REJOINED "Multicast (re)unit" +#define D_MULTICAST_JOIN_FAILED "Unió a Multicast falalda" +#define D_FAILED_TO_SEND_RESPONSE "No he pogut enviar resposta" + +#define D_WEMO "WeMo" +#define D_WEMO_BASIC_EVENT "event bàsic WeMo" +#define D_WEMO_EVENT_SERVICE "event de servei WeMo" +#define D_WEMO_META_SERVICE "meta servei WeMo" +#define D_WEMO_SETUP "Configuració WeMo" +#define D_RESPONSE_SENT "Responsta enviada" + +#define D_HUE "Hue" +#define D_HUE_BRIDGE_SETUP "Hue setup" +#define D_HUE_API_NOT_IMPLEMENTED "Hue API not implemented" +#define D_HUE_API "Hue API" +#define D_HUE_POST_ARGS "Hue POST args" +#define D_3_RESPONSE_PACKETS_SENT "3 response packets sent" + +// xdrv_07_domoticz.ino +#define D_DOMOTICZ_PARAMETERS "Domoticz parameters" +#define D_DOMOTICZ_IDX "Idx" +#define D_DOMOTICZ_KEY_IDX "Key idx" +#define D_DOMOTICZ_SWITCH_IDX "Switch idx" +#define D_DOMOTICZ_SENSOR_IDX "Sensor idx" + #define D_DOMOTICZ_TEMP "Temp" + #define D_DOMOTICZ_TEMP_HUM "Temp,Hum" + #define D_DOMOTICZ_TEMP_HUM_BARO "Temp,Hum,Baro" + #define D_DOMOTICZ_POWER_ENERGY "Power,Energy" + #define D_DOMOTICZ_ILLUMINANCE "Illuminance" + #define D_DOMOTICZ_COUNT "Count/PM1" + #define D_DOMOTICZ_VOLTAGE "Voltage/PM2.5" + #define D_DOMOTICZ_CURRENT "Current/PM10" + #define D_DOMOTICZ_AIRQUALITY "AirQuality" + #define D_DOMOTICZ_P1_SMART_METER "P1SmartMeter" +#define D_DOMOTICZ_UPDATE_TIMER "Update timer" + +// xdrv_09_timers.ino +#define D_CONFIGURE_TIMER "Configure Timer" +#define D_TIMER_PARAMETERS "Timer parameters" +#define D_TIMER_ENABLE "Enable Timers" +#define D_TIMER_ARM "Enable" +#define D_TIMER_TIME "Time" +#define D_TIMER_DAYS "Days" +#define D_TIMER_REPEAT "Repeat" +#define D_TIMER_OUTPUT "Output" +#define D_TIMER_ACTION "Action" + +// xdrv_10_knx.ino +#define D_CONFIGURE_KNX "Configure KNX" +#define D_KNX_PARAMETERS "KNX Parameters" +#define D_KNX_GENERAL_CONFIG "General" +#define D_KNX_PHYSICAL_ADDRESS "Physical Address" +#define D_KNX_PHYSICAL_ADDRESS_NOTE "( Must be unique on the KNX network )" +#define D_KNX_ENABLE "Enable KNX" +#define D_KNX_GROUP_ADDRESS_TO_WRITE "Data to Send to Group Addresses" +#define D_ADD "Add" +#define D_DELETE "Delete" +#define D_REPLY "Reply" +#define D_KNX_GROUP_ADDRESS_TO_READ "Group Addresses to Receive Data from" +#define D_RECEIVED_FROM "Received from" +#define D_KNX_COMMAND_WRITE "Write" +#define D_KNX_COMMAND_READ "Read" +#define D_KNX_COMMAND_OTHER "Other" +#define D_SENT_TO "sent to" +#define D_KNX_WARNING "The group address ( 0 / 0 / 0 ) is reserved and can not be used." +#define D_KNX_ENHANCEMENT "Communication Enhancement" +#define D_KNX_TX_SLOT "KNX TX" +#define D_KNX_RX_SLOT "KNX RX" +#define D_KNX_TX_SCENE "KNX SCENE TX" +#define D_KNX_RX_SCENE "KNX SCENE RX" + +// xdrv_23_zigbee +#define D_ZIGBEE_PERMITJOIN_ACTIVE "Devices allowed to join" +#define D_ZIGBEE_MAPPING_TITLE "Tasmota Zigbee Mapping" +#define D_ZIGBEE_NOT_STARTED "Zigbee not started" +#define D_ZIGBEE_MAPPING_IN_PROGRESS_SEC "Mapping in progress (%d s. remaining)" +#define D_ZIGBEE_MAPPING_NOT_PRESENT "No mapping" +#define D_ZIGBEE_MAP_REFRESH "Zigbee Map Refresh" +#define D_ZIGBEE_MAP "Zigbee Map" +#define D_ZIGBEE_PERMITJOIN "Zigbee Permit Join" +#define D_ZIGBEE_GENERATE_KEY "generating random Zigbee network key" +#define D_ZIGBEE_UNKNOWN_DEVICE "Unknown device" +#define D_ZIGBEE_UNKNOWN_ATTRIBUTE "Unknown attribute" +#define D_ZIGBEE_INVALID_PARAM "Invalid parameter" +#define D_ZIGBEE_MISSING_PARAM "Missing parameters" +#define D_ZIGBEE_UNKNWON_ATTRIBUTE "Unknown attribute name (ignored): %s" +#define D_ZIGBEE_TOO_MANY_CLUSTERS "No more than one cluster id per command" +#define D_ZIGBEE_WRONG_DELIMITER "Wrong delimiter for payload" +#define D_ZIGBEE_UNRECOGNIZED_COMMAND "Unrecognized zigbee command: %s" +#define D_ZIGBEE_TOO_MANY_COMMANDS "Only 1 command allowed (%d)" +#define D_ZIGBEE_NO_ATTRIBUTE "No attribute in list" +#define D_ZIGBEE_UNSUPPORTED_ATTRIBUTE_TYPE "Unsupported attribute type" +#define D_ZIGBEE_JSON_REQUIRED "Config requires JSON objects" +#define D_ZIGBEE_RESET_1_OR_2 "1 or 2 to reset" +#define D_ZIGBEE_EEPROM_FOUND_AT_ADDRESS "ZBBridge EEPROM found at address" +#define D_ZIGBEE_RANDOMIZING_ZBCONFIG "Randomizing Zigbee parameters, please check with 'ZbConfig'" + +// xdrv_03_energy.ino +#define D_ENERGY_TODAY "Energy Today" +#define D_ENERGY_YESTERDAY "Energy Yesterday" +#define D_ENERGY_TOTAL "Energy Total" + +// xdrv_27_shutter.ino +#define D_OPEN "Open" +#define D_CLOSE "Close" +#define D_DOMOTICZ_SHUTTER "Shutter" + +// xdrv_28_pcf8574.ino +#define D_CONFIGURE_PCF8574 "Configure PCF8574" +#define D_PCF8574_PARAMETERS "PCF8574 parameters" +#define D_INVERT_PORTS "Invert Ports" +#define D_DEVICE "Device" +#define D_DEVICE_INPUT "Input" +#define D_DEVICE_OUTPUT "Output" + +// xsns_05_ds18b20.ino +#define D_SENSOR_BUSY "Sensor busy" +#define D_SENSOR_CRC_ERROR "Sensor CRC error" +#define D_SENSORS_FOUND "Sensors found" + +// xsns_06_dht.ino +#define D_TIMEOUT_WAITING_FOR "Timeout waiting for" +#define D_START_SIGNAL_LOW "start signal low" +#define D_START_SIGNAL_HIGH "start signal high" +#define D_PULSE "pulse" +#define D_CHECKSUM_FAILURE "Checksum failure" + +// xsns_07_sht1x.ino +#define D_SENSOR_DID_NOT_ACK_COMMAND "Sensor did not ACK command" +#define D_SHT1X_FOUND "SHT1X found" + +// xsns_18_pms5003.ino +#define D_STANDARD_CONCENTRATION "CF-1 PM" // Standard Particle CF-1 Particle Matter +#define D_ENVIRONMENTAL_CONCENTRATION "PM" // Environmetal Particle Matter +#define D_PARTICALS_BEYOND "Particles" + +// xsns_27_apds9960.ino +#define D_GESTURE "Gesture" +#define D_COLOR_RED "Red" +#define D_COLOR_GREEN "Green" +#define D_COLOR_BLUE "Blue" +#define D_CCT "CCT" +#define D_PROXIMITY "Proximity" + +// xsns_32_mpu6050.ino +#define D_AX_AXIS "Accel. X-Axis" +#define D_AY_AXIS "Accel. Y-Axis" +#define D_AZ_AXIS "Accel. Z-Axis" +#define D_GX_AXIS "Gyro X-Axis" +#define D_GY_AXIS "Gyro Y-Axis" +#define D_GZ_AXIS "Gyro Z-Axis" + +// xsns_34_hx711.ino +#define D_HX_CAL_REMOVE "Remove weight" +#define D_HX_CAL_REFERENCE "Load reference weight" +#define D_HX_CAL_DONE "Calibrated" +#define D_HX_CAL_FAIL "Calibration failed" +#define D_RESET_HX711 "Reset Scale" +#define D_CONFIGURE_HX711 "Configure Scale" +#define D_HX711_PARAMETERS "Scale parameters" +#define D_ITEM_WEIGHT "Item weight" +#define D_REFERENCE_WEIGHT "Reference weight" +#define D_CALIBRATE "Calibrate" +#define D_CALIBRATION "Calibration" + +//xsns_35_tx20.ino +#define D_TX20_WIND_DIRECTION "Wind Direction" +#define D_TX20_WIND_SPEED "Wind Speed" +#define D_TX20_WIND_SPEED_MIN "Wind Speed Min" +#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_NORTH "N" +#define D_TX20_EAST "E" +#define D_TX20_SOUTH "S" +#define D_TX20_WEST "O" + +// xsns_53_sml.ino +#define D_TPWRIN "Total-In" +#define D_TPWRIN0 "Total-In T0" +#define D_TPWRIN1 "Total-In T1" +#define D_TPWROUT "Total-Out" +#define D_TPWRCURR "Current-In/Out" +#define D_TPWRCURR1 "Current-In p1" +#define D_TPWRCURR2 "Current-In p2" +#define D_TPWRCURR3 "Current-In p3" +#define D_Strom_L1 "Current L1" +#define D_Strom_L2 "Current L2" +#define D_Strom_L3 "Current L3" +#define D_Spannung_L1 "Voltage L1" +#define D_Spannung_L2 "Voltage L2" +#define D_Spannung_L3 "Voltage L3" +#define D_METERNR "Meter_number" +#define D_METERSID "Service ID" +#define D_GasIN "Counter" // Gas-Verbrauch +#define D_H2oIN "Counter" // H2o-Verbrauch +#define D_StL1L2L3 "Current L1+L2+L3" +#define D_SpL1L2L3 "Voltage L1+L2+L3/3" + +// xsns_86_tfminiplus.ino +#define D_SIGNALSTRENGTH "Signal Strength" +#define D_CHIPTEMPERATURE "Chip Temperature" + +// tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box +#define D_SENSOR_NONE "Cap" +#define D_SENSOR_USER "Usuari" +#define D_SENSOR_OPTION "Option" +#define D_SENSOR_DHT11 "DHT11" +#define D_SENSOR_AM2301 "AM2301" +#define D_SENSOR_SI7021 "SI7021" +#define D_SENSOR_MS01 "MS01" +#define D_SENSOR_DS18X20 "DS18x20" +#define D_SENSOR_I2C_SCL "I2C SCL" +#define D_SENSOR_I2C_SDA "I2C SDA" +#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" +#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" +#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" +#define D_SENSOR_I2S_IN_DATA "I2S In Data" +#define D_SENSOR_I2S_IN_CLK "I2S In Clk" +#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_WS2812 "WS2812" +#define D_SENSOR_DFR562 "Reproductor MP3" +#define D_SENSOR_IRSEND "IRsend" +#define D_SENSOR_SWITCH "Interruptor" // Suffix "1" +#define D_SENSOR_BUTTON "Botó" // Suffix "1" +#define D_SENSOR_RELAY "Relé" // Suffix "1i" +#define D_SENSOR_LED "Led" // Suffix "1i" +#define D_SENSOR_LED_LINK "EnllaçLed" // Suffix "i" +#define D_SENSOR_PWM "PWM" // Suffix "1" +#define D_SENSOR_COUNTER "Comptador" // Suffix "1" +#define D_SENSOR_INTERRUPT "Interrupció" +#define D_SENSOR_INPUT "Entrada" +#define D_SENSOR_IRRECV "Receptor IR" +#define D_SENSOR_MHZ_RX "MHZ Rx" +#define D_SENSOR_MHZ_TX "MHZ Tx" +#define D_SENSOR_PZEM004_RX "PZEM004 Rx" +#define D_SENSOR_PZEM016_RX "PZEM016 Rx" +#define D_SENSOR_PZEM017_RX "PZEM017 Rx" +#define D_SENSOR_PZEM0XX_TX "PZEM0XX Tx" +#define D_SENSOR_SAIR_RX "SAir Rx" +#define D_SENSOR_SAIR_TX "SAir Tx" +#define D_SENSOR_SPI_CS "SPI CS" +#define D_SENSOR_SPI_DC "SPI DC" +#define D_SENSOR_SPI_MISO "SPI MISO" +#define D_SENSOR_SPI_MOSI "SPI MOSI" +#define D_SENSOR_SPI_CLK "SPI CLK" +#define D_SENSOR_SDIO_CMD "SDIO CMD" +#define D_SENSOR_SDIO_CLK "SDIO CLK" +#define D_SENSOR_SDIO_D0 "SDIO D0" +#define D_SENSOR_SDIO_D1 "SDIO D1" +#define D_SENSOR_SDIO_D2 "SDIO D2" +#define D_SENSOR_SDIO_D3 "SDIO D3" +#define D_SENSOR_BACKLIGHT "Llum de fons" +#define D_SENSOR_PMS5003_TX "PMS5003 Tx" +#define D_SENSOR_PMS5003_RX "PMS5003 Rx" +#define D_SENSOR_SDS0X1_RX "SDS0X1 Rx" +#define D_SENSOR_SDS0X1_TX "SDS0X1 Tx" +#define D_SENSOR_HPMA_RX "HPMA Rx" +#define D_SENSOR_HPMA_TX "HPMA Tx" +#define D_SENSOR_SBR_RX "SerBr Rx" +#define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_SR04_TRIG "SR04 Tri/TX" +#define D_SENSOR_SR04_ECHO "SR04 Ech/RX" +#define D_SENSOR_SDM72_TX "SDM72 Tx" +#define D_SENSOR_SDM72_RX "SDM72 Rx" +#define D_SENSOR_SDM120_TX "SDMx20 Tx" +#define D_SENSOR_SDM120_RX "SDMx20 Rx" +#define D_SENSOR_SDM230_TX "SDM230 Tx" +#define D_SENSOR_SDM230_RX "SDM230 Rx" +#define D_SENSOR_SDM630_TX "SDM630 Tx" +#define D_SENSOR_SDM630_RX "SDM630 Rx" +#define D_SENSOR_WE517_TX "WE517 Tx" +#define D_SENSOR_WE517_RX "WE517 Rx" +#define D_SENSOR_TM1637_CLK "TM1637 CLK" +#define D_SENSOR_TM1637_DIO "TM1637 DIO" +#define D_SENSOR_TM1638_CLK "TM1638 CLK" +#define D_SENSOR_TM1638_DIO "TM1638 DIO" +#define D_SENSOR_TM1638_STB "TM1638 STB" +#define D_SENSOR_MAX7219_DIN "MAX7219 DIN" +#define D_SENSOR_MAX7219_CS "MAX7219 CS" +#define D_SENSOR_MAX7219_CLK "MAX7219 CLK" +#define D_SENSOR_HX711_SCK "HX711 SCK" +#define D_SENSOR_HX711_DAT "HX711 DAT" +#define D_SENSOR_FTC532 "FTC532" +#define D_SENSOR_BS814_CLK "BS814 CLK" +#define D_SENSOR_BS814_DAT "BS814 DAT" +#define D_SENSOR_TX2X_TX "TX2x" +#define D_SENSOR_RFSEND "RFSend" +#define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" +#define D_SENSOR_MGC3130_XFER "MGC3130 Xfr" +#define D_SENSOR_MGC3130_RESET "MGC3130 Rst" +#define D_SENSOR_SSPI_MISO "SSPI MISO" +#define D_SENSOR_SSPI_MOSI "SSPI MOSI" +#define D_SENSOR_SSPI_SCLK "SSPI SCLK" +#define D_SENSOR_SSPI_CS "SSPI CS" +#define D_SENSOR_SSPI_DC "SSPI DC" +#define D_SENSOR_RF_SENSOR "RF Sensor" +#define D_SENSOR_AZ_RX "AZ Rx" +#define D_SENSOR_AZ_TX "AZ Tx" +#define D_SENSOR_MAX31855_CS "MX31855 CS" +#define D_SENSOR_MAX31855_CLK "MX31855 CLK" +#define D_SENSOR_MAX31855_DO "MX31855 DO" +#define D_SENSOR_MAX31865_CS "MX31865 CS" +#define D_SENSOR_NRG_SEL "HLWBL SEL" // Suffix "i" +#define D_SENSOR_NRG_CF1 "HLWBL CF1" +#define D_SENSOR_HLW_CF "HLW8012 CF" +#define D_SENSOR_HJL_CF "BL0937 CF" +#define D_SENSOR_MCP39F5_TX "MCP39F5 Tx" +#define D_SENSOR_MCP39F5_RX "MCP39F5 Rx" +#define D_SENSOR_MCP39F5_RST "MCP39F5 Rst" +#define D_SENSOR_CSE7761_TX "CSE7761 Tx" +#define D_SENSOR_CSE7761_RX "CSE7761 Rx" +#define D_SENSOR_CSE7766_TX "CSE7766 Tx" +#define D_SENSOR_CSE7766_RX "CSE7766 Rx" +#define D_SENSOR_BL0939_RX "BL0939 Rx" +#define D_SENSOR_BL0942_RX "BL0942 Rx" +#define D_SENSOR_HM330X_SET "HM330X SET" +#define D_SENSOR_PN532_TX "PN532 Tx" +#define D_SENSOR_PN532_RX "PN532 Rx" +#define D_SENSOR_SM16716_CLK "SM16716 CLK" +#define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_POWER "SM16716 PWR" +#define D_SENSOR_P9813_CLK "P9813 Clk" +#define D_SENSOR_P9813_DAT "P9813 Dat" +#define D_SENSOR_MY92X1_DI "MY92x1 DI" +#define D_SENSOR_MY92X1_DCKI "MY92x1 DCKI" +#define D_SENSOR_ARIRFRCV "ALux IrRcv" +#define D_SENSOR_ARIRFSEL "ALux IrSel" +#define D_SENSOR_TXD "Serial Tx" +#define D_SENSOR_RXD "Serial Rx" +#define D_SENSOR_ROTARY "Rotary" // Suffix "1A" +#define D_SENSOR_HRE_CLOCK "HRE Clock" +#define D_SENSOR_HRE_DATA "HRE Data" +#define D_SENSOR_ADE7880_IRQ "ADE7880 IRQ" +#define D_SENSOR_ADE7953_IRQ "ADE7953 IRQ" +#define D_SENSOR_BUZZER "Brunzidor" +#define D_SENSOR_DISP_RESET "Display Rst" +#define D_SENSOR_ZIGBEE_TXD "Zigbee Tx" +#define D_SENSOR_ZIGBEE_RXD "Zigbee Rx" +#define D_SENSOR_ZIGBEE_RST "Zigbee Rst" +#define D_SENSOR_SOLAXX1_TX "SolaxX1 Tx" +#define D_SENSOR_SOLAXX1_RX "SolaxX1 Rx" +#define D_SENSOR_SOLAXX1_RTS "SolaxX1 RTS" +#define D_SENSOR_IBEACON_TX "iBeacon TX" +#define D_SENSOR_IBEACON_RX "iBeacon RX" +#define D_SENSOR_RDM6300_RX "RDM6300 RX" +#define D_SENSOR_CC1101_CS "CC1101 CS" +#define D_SENSOR_A4988_DIR "A4988 DIR" +#define D_SENSOR_A4988_STP "A4988 STP" +#define D_SENSOR_A4988_ENA "A4988 ENA" +#define D_SENSOR_A4988_MS1 "A4988 MS1" +#define D_SENSOR_OUTPUT_HI "Output Hi" +#define D_SENSOR_OUTPUT_LO "Output Lo" +#define D_SENSOR_AS608_TX "AS608 Tx" +#define D_SENSOR_AS608_RX "AS608 Rx" +#define D_SENSOR_DDS2382_TX "DDS238-2 Tx" +#define D_SENSOR_DDS2382_RX "DDS238-2 Rx" +#define D_SENSOR_DDSU666_TX "DDSU666 Tx" +#define D_SENSOR_DDSU666_RX "DDSU666 Rx" +#define D_SENSOR_SM2135_CLK "SM2135 Clk" +#define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_BP5758D_CLK "BP5758D Clk" +#define D_SENSOR_BP5758D_DAT "BP5758D Dat" +#define D_SENSOR_DEEPSLEEP "DeepSleep" +#define D_SENSOR_EXS_ENABLE "EXS Enable" +#define D_SENSOR_CLIENT_TX "Client TX" +#define D_SENSOR_CLIENT_RX "Client RX" +#define D_SENSOR_CLIENT_RESET "Client RST" +#define D_SENSOR_GPS_RX "GPS RX" +#define D_SENSOR_GPS_TX "GPS TX" +#define D_SENSOR_HM10_RX "HM10 RX" +#define D_SENSOR_HM10_TX "HM10 TX" +#define D_SENSOR_LE01MR_RX "LE-01MR Rx" +#define D_SENSOR_LE01MR_TX "LE-01MR Tx" +#define D_SENSOR_BL0940_RX "BL0940 Rx" +#define D_SENSOR_CC1101_GDO0 "CC1101 GDO0" +#define D_SENSOR_CC1101_GDO2 "CC1101 GDO2" +#define D_SENSOR_HRXL_RX "HRXL Rx" +#define D_SENSOR_DYP_RX "DYP Rx" +#define D_SENSOR_ELECTRIQ_MOODL "MOODL Tx" +#define D_SENSOR_AS3935 "AS3935" +#define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" +#define D_SENSOR_TELEINFO_RX "TInfo Rx" +#define D_SENSOR_TELEINFO_ENABLE "TInfo EN" +#define D_SENSOR_LMT01_PULSE "LMT01 Pulse" +#define D_SENSOR_ADC_INPUT "ADC Input" +#define D_SENSOR_ADC_TEMP "ADC Temp" +#define D_SENSOR_ADC_LIGHT "ADC Light" +#define D_SENSOR_ADC_BUTTON "ADC Button" +#define D_SENSOR_ADC_RANGE "ADC Range" +#define D_SENSOR_ADC_CT_POWER "ADC CT Power" +#define D_SENSOR_ADC_JOYSTICK "ADC Joystick" +#define D_SENSOR_ADC_PH "ADC pH" +#define D_SENSOR_ADC_MQ "ADC MQ" +#define D_GPIO_WEBCAM_PWDN "CAM_PWDN" +#define D_GPIO_WEBCAM_RESET "CAM_RESET" +#define D_GPIO_WEBCAM_XCLK "CAM_XCLK" +#define D_GPIO_WEBCAM_SIOD "CAM_SIOD" +#define D_GPIO_WEBCAM_SIOC "CAM_SIOC" +#define D_GPIO_WEBCAM_DATA "CAM_DATA" +#define D_GPIO_WEBCAM_VSYNC "CAM_VSYNC" +#define D_GPIO_WEBCAM_HREF "CAM_HREF" +#define D_GPIO_WEBCAM_PCLK "CAM_PCLK" +#define D_GPIO_WEBCAM_PSCLK "CAM_PSCLK" +#define D_GPIO_WEBCAM_HSD "CAM_HSD" +#define D_GPIO_WEBCAM_PSRCS "CAM_PSRCS" +#define D_SENSOR_ETH_PHY_POWER "ETH POWER" +#define D_SENSOR_ETH_PHY_MDC "ETH MDC" +#define D_SENSOR_ETH_PHY_MDIO "ETH MDIO" +#define D_SENSOR_TCP_TXD "TCP Tx" +#define D_SENSOR_TCP_RXD "TCP Rx" +#define D_SENSOR_IEM3000_TX "iEM3000 TX" +#define D_SENSOR_IEM3000_RX "iEM3000 RX" +#define D_SENSOR_MIEL_HVAC_TX "MiEl HVAC Tx" +#define D_SENSOR_MIEL_HVAC_RX "MiEl HVAC Rx" +#define D_SENSOR_PROJECTOR_CTRL_TX "DLP Tx" +#define D_SENSOR_PROJECTOR_CTRL_RX "DLP Rx" +#define D_SENSOR_SHELLY_DIMMER_BOOT0 "SHD Boot 0" +#define D_SENSOR_SHELLY_DIMMER_RST_INV "SHD Reset" +#define D_SENSOR_RC522_RST "RC522 Rst" +#define D_SENSOR_RC522_CS "RC522 CS" +#define D_SENSOR_NRF24_CS "NRF24 CS" +#define D_SENSOR_NRF24_DC "NRF24 DC" +#define D_SENSOR_XPT2046_CS "XPT2046 CS" +#define D_SENSOR_ILI9341_CS "ILI9341 CS" +#define D_SENSOR_ILI9341_DC "ILI9341 DC" +#define D_SENSOR_ILI9488_CS "ILI9488 CS" +#define D_SENSOR_EPAPER29_CS "EPaper29 CS" +#define D_SENSOR_EPAPER42_CS "EPaper42 CS" +#define D_SENSOR_SSD1351_CS "SSD1351 CS" +#define D_SENSOR_SSD1351_DC "SSD1351 DC" +#define D_SENSOR_RA8876_CS "RA8876 CS" +#define D_SENSOR_ST7789_CS "ST7789 CS" +#define D_SENSOR_ST7789_DC "ST7789 DC" +#define D_SENSOR_SSD1331_CS "SSD1331 CS" +#define D_SENSOR_SSD1331_DC "SSD1331 DC" +#define D_SENSOR_SDCARD_CS "SDCard CS" +#define D_SENSOR_WIEGAND_D0 "Wiegand D0" +#define D_SENSOR_WIEGAND_D1 "Wiegand D1" +#define D_SENSOR_NEOPOOL_TX "NeoPool Tx" +#define D_SENSOR_NEOPOOL_RX "NeoPool Rx" +#define D_SENSOR_VL53LXX_XSHUT "VL53LXX XSHUT" +#define D_SENSOR_TFMINIPLUS_TX "TFmini+ TX" +#define D_SENSOR_TFMINIPLUS_RX "TFmini+ RX" +#define D_SENSOR_ZEROCROSS "ZC Pulse" +#define D_SENSOR_HALLEFFECT "HallEffect" +#define D_SENSOR_EPD_DATA "EPD Data" +#define D_SENSOR_MCP2515_CS "MCP2515 CS" +#define D_SENSOR_HRG15_RX "HRG15 Rx" +#define D_SENSOR_HRG15_TX "HRG15 Tx" +#define D_SENSOR_VINDRIKTNING_RX "VINDRIKTNING" +#define D_SENSOR_BL6523_TX "BL6523 Tx" +#define D_SENSOR_BL6523_RX "BL6523 Rx" +#define D_SENSOR_HEARTBEAT "Pulsació" +#define D_SENSOR_RESET "Reset" +#define D_GPIO_SHIFT595_SRCLK "74x595 SRCLK" +#define D_GPIO_SHIFT595_RCLK "74x595 RCLK" +#define D_GPIO_SHIFT595_OE "74x595 OE" +#define D_GPIO_SHIFT595_SER "74x595 SER" +#define D_SENSOR_CM11_TX "CM110x TX" +#define D_SENSOR_CM11_RX "CM110x RX" +#define D_SENSOR_FLOWRATEMETER "Cabal" + +// Units +#define D_UNIT_AMPERE "A" +#define D_UNIT_CELSIUS "C" +#define D_UNIT_CENTIMETER "cm" +#define D_UNIT_DEGREE "°" +#define D_UNIT_FAHRENHEIT "F" +#define D_UNIT_FPS "FPS" +#define D_UNIT_HERTZ "Hz" +#define D_UNIT_HOUR "h" +#define D_UNIT_GALLONS "gal" +#define D_UNIT_GALLONS_PER_MIN "g/m" +#define D_UNIT_INCH_MERCURY "inHg" +#define D_UNIT_INCREMENTS "inc" +#define D_UNIT_KELVIN "K" +#define D_UNIT_KILOMETER "km" +#define D_UNIT_KILOGRAM "kg" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" +#define D_UNIT_KILOOHM "kΩ" +#define D_UNIT_KILOWATTHOUR "kWh" +#define D_UNIT_LITERS "L" +#define D_UNIT_LITERS_PER_MIN "L/m" +#define D_UNIT_LUX "lx" +#define D_UNIT_MICROGRAM_PER_CUBIC_METER "µg/m³" +#define D_UNIT_MICROMETER "µm" +#define D_UNIT_MICROSECOND "µs" +#define D_UNIT_MICROSIEMENS_PER_CM "µS/cm" +#define D_UNIT_MILLIAMPERE "mA" +#define D_UNIT_MILLILITERS "ml" +#define D_UNIT_MILLIMETER "mm" +#define D_UNIT_MILLIMETER_MERCURY "mmHg" +#define D_UNIT_MILLISECOND "ms" +#define D_UNIT_MILLIVOLT "mV" +#define D_UNIT_MINUTE "Min" +#define D_UNIT_PARTS_PER_BILLION "ppb" +#define D_UNIT_PARTS_PER_DECILITER "ppd" +#define D_UNIT_PARTS_PER_MILLION "ppm" +#define D_UNIT_MILIGRAMS_PER_LITER "mg/L" +#define D_UNIT_PERCENT "%%" +#define D_UNIT_PRESSURE "hPa" +#define D_UNIT_SECOND "sec" +#define D_UNIT_SECTORS "sectors" +#define D_UNIT_VA "VA" +#define D_UNIT_VAR "VAr" +#define D_UNIT_VOLT "V" +#define D_UNIT_WATT "W" +#define D_UNIT_WATTHOUR "Wh" +#define D_UNIT_WATT_METER_QUADRAT "W/m²" +#define D_UNIT_LITER_PER_MINUTE "l/min" +#define D_UNIT_CUBICMETER_PER_HOUR "m³/h" + +#define D_NEW_ADDRESS "Setting address to" +#define D_OUT_OF_RANGE "Out of Range" +#define D_SENSOR_DETECTED "detected" + +//SDM220, SDM120, SDM72, LE01MR, SDM230 +#define D_EXPORT_POWER "Export Power" +#define D_IMPORT_POWER "Import Power" +#define D_PHASE_ANGLE "Phase Angle" +#define D_IMPORT_ACTIVE "Import Active" +#define D_EXPORT_ACTIVE "Export Active" +#define D_IMPORT_REACTIVE "Import Reactive" +#define D_EXPORT_REACTIVE "Export Reactive" +#define D_TOTAL_REACTIVE "Total Reactive" +#define D_UNIT_KWARH "kVArh" +#define D_UNIT_ANGLE "Deg" +#define D_TOTAL_ACTIVE "Total Active" +#define D_RESETTABLE_TOTAL_ACTIVE "Total Active (RST)" + +//SOLAXX1 +#define D_PV1_VOLTAGE "PV1 Voltage" +#define D_PV1_CURRENT "PV1 Current" +#define D_PV1_POWER "PV1 Power" +#define D_PV2_VOLTAGE "PV2 Voltage" +#define D_PV2_CURRENT "PV2 Current" +#define D_PV2_POWER "PV2 Power" +#define D_SOLAR_POWER "Solar Power" +#define D_INVERTER_POWER "Inverter Power" +#define D_STATUS "Status" +#define D_SOLAX_MODE_0 "Waiting" +#define D_SOLAX_MODE_1 "Checking" +#define D_SOLAX_MODE_2 "Working" +#define D_SOLAX_MODE_3 "Failure" +#define D_SOLAX_MODE_4 "Permanent Failure" +#define D_SOLAX_MODE_5 "Software Update" +#define D_SOLAX_MODE_6 "Selftest" +#define D_SOLAX_ERROR_0 "No Error" +#define D_SOLAX_ERROR_1 "Grid Lost Fault" +#define D_SOLAX_ERROR_2 "Grid Voltage Fault" +#define D_SOLAX_ERROR_3 "Grid Frequency Fault" +#define D_SOLAX_ERROR_4 "PV Voltage too high" +#define D_SOLAX_ERROR_5 "PV Isolation Fault" +#define D_SOLAX_ERROR_6 "Over Temperature Fault" +#define D_SOLAX_ERROR_7 "Fan Fault" +#define D_SOLAX_ERROR_8 "Other Device Fault" + +//xdrv_10_scripter.ino +#define D_CONFIGURE_SCRIPT "Edit Script" +#define D_SCRIPT "edit script" +#define D_SDCARD_UPLOAD "file upload" +#define D_UFSDIR "ufs directory" +#define D_UPL_DONE "Done" +#define D_SCRIPT_CHARS_LEFT "chars left" +#define D_SCRIPT_CHARS_NO_MORE "no more chars" +#define D_SCRIPT_DOWNLOAD "Download" +#define D_SCRIPT_ENABLE "Script enable" +#define D_SCRIPT_UPLOAD "Upload" +#define D_SCRIPT_UPLOAD_FILES "Upload files" + +//xdrv_50_filesystem.ino +#define D_MANAGE_FILE_SYSTEM "Manage File system" +#define D_FS_SIZE "Size" +#define D_FS_FREE "Free" +#define D_NEW_FILE "newfile.txt" +#define D_CREATE_NEW_FILE "Create and edit new file" +#define D_EDIT_FILE "Edit File" +#define D_CONFIRM_FILE_DEL "Confirm file deletion" +#define D_SHOW_HIDDEN_FILES "Show hidden files" + +//xsns_67_as3935.ino +#define D_AS3935_GAIN "gain:" +#define D_AS3935_ENERGY "energy:" +#define D_AS3935_DISTANCE "distance:" +#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_VRMS "µVrms:" +#define D_AS3935_APRX "aprx.:" +#define D_AS3935_AWAY "away" +#define D_AS3935_LIGHT "lightning" +#define D_AS3935_OUT "lightning out of range" +#define D_AS3935_NOT "distance not determined" +#define D_AS3935_ABOVE "lightning overhead" +#define D_AS3935_NOISE "noise detected" +#define D_AS3935_DISTDET "disturber detected" +#define D_AS3935_INTNOEV "Interrupt with no Event!" +#define D_AS3935_FLICKER "IRQ Pin flicker!" +#define D_AS3935_POWEROFF "Powerd Off" +#define D_AS3935_NOMESS "listening..." +#define D_AS3935_ON "On" +#define D_AS3935_OFF "Off" +#define D_AS3935_INDOORS "Indoors" +#define D_AS3935_OUTDOORS "Outdoors" +#define D_AS3935_CAL_FAIL "calibration failed" +#define D_AS3935_CAL_OK "calibration set to:" + +//xsns_68_opentherm.ino +#define D_SENSOR_BOILER_OT_RX "OpenTherm RX" +#define D_SENSOR_BOILER_OT_TX "OpenTherm TX" + +// xnrg_15_teleinfo Denky (Teleinfo) +#define D_CONTRACT "Contract" +#define D_POWER_LOAD "Power load" +#define D_CURRENT_TARIFF "Current Tariff" +#define D_TARIFF "Tariff" +#define D_OVERLOAD "ADPS" +#define D_MAX_POWER "Max Power" +#define D_MAX_CURRENT "Max Current" + +// xsns_79_as608.ino +#define D_FP_ENROLL_PLACEFINGER "Place finger" +#define D_FP_ENROLL_REMOVEFINGER "Remove finger" +#define D_FP_ENROLL_PLACESAMEFINGER "Place same finger again" +#define D_FP_ENROLL_RETRY "Error so retry" +#define D_FP_ENROLL_RESTART "Restart" +#define D_FP_ENROLL_ERROR "Error" +#define D_FP_ENROLL_RESET "Reset" +#define D_FP_ENROLL_ACTIVE "Active" +#define D_FP_ENROLL_INACTIVE "Inactive" +// Indexed by Adafruit_Fingerprint.h defines +#define D_FP_PACKETRECIEVEERR "Comms error" // 0x01 Error when receiving data package +#define D_FP_NOFINGER "" // 0x02 No finger on the sensor +#define D_FP_IMAGEFAIL "Imaging error" // 0x03 Failed to enroll the finger +#define D_FP_IMAGEMESS "Image too messy" // 0x06 Failed to generate character file due to overly disorderly fingerprint image +#define D_FP_FEATUREFAIL "Fingerprint too small" // 0x07 Failed to generate character file due to the lack of character point or small fingerprint image +#define D_FP_NOMATCH "No match" // 0x08 Finger doesn't match +#define D_FP_NOTFOUND "Did not find a match" // 0x09 Failed to find matching finger +#define D_FP_ENROLLMISMATCH "Fingerprint did not match" // 0x0A Failed to combine the character files +#define D_FP_BADLOCATION "Bad location" // 0x0B Addressed PageID is beyond the finger library +#define D_FP_DBRANGEFAIL "DB range error" // 0x0C Error when reading template from library or invalid template +#define D_FP_UPLOADFEATUREFAIL "Upload feature error" // 0x0D Error when uploading template +#define D_FP_PACKETRESPONSEFAIL "Packet response error" // 0x0E Module failed to receive the following data packages +#define D_FP_UPLOADFAIL "Upload error" // 0x0F Error when uploading image +#define D_FP_DELETEFAIL "Delete error" // 0x10 Failed to delete the template +#define D_FP_DBCLEARFAIL "DB Clear error" // 0x11 Failed to clear finger library +#define D_FP_PASSFAIL "Password error" // 0x13 Find whether the fingerprint passed or failed +#define D_FP_INVALIDIMAGE "Image invalid" // 0x15 Failed to generate image because of lac of valid primary image +#define D_FP_FLASHERR "Flash write error" // 0x18 Error when writing flash +#define D_FP_INVALIDREG "Invalid number" // 0x1A Invalid register number +#define D_FP_ADDRCODE "Address code" // 0x20 Address code +#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed +#define D_FP_UNKNOWNERROR "Error" // Any other error + +// xsns_96_flowratemeter.ino +#define D_FLOWRATEMETER_NAME "Flowrate" + +// xsns_83_neopool.ino +#define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names +#define D_NEOPOOL_MACH_HIDROLIFE "Hidrolife" +#define D_NEOPOOL_MACH_AQUASCENIC "Aquascenic" +#define D_NEOPOOL_MACH_OXILIFE "Oxilife" +#define D_NEOPOOL_MACH_BIONET "Bionet" +#define D_NEOPOOL_MACH_HIDRONISER "Hidroniser" +#define D_NEOPOOL_MACH_UVSCENIC "UVScenic" +#define D_NEOPOOL_MACH_STATION "Station" +#define D_NEOPOOL_MACH_BRILIX "Brilix" +#define D_NEOPOOL_MACH_GENERIC "Generic" +#define D_NEOPOOL_MACH_BAYROL "Bayrol" +#define D_NEOPOOL_MACH_HAY "Hay" +#define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes +#define D_NEOPOOL_FILTRATION_AUTO "Auto" +#define D_NEOPOOL_FILTRATION_HEATING "Heating" +#define D_NEOPOOL_FILTRATION_SMART "Smart" +#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent" +#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash" +#define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level +#define D_NEOPOOL_FILTRATION_SLOW "slow" +#define D_NEOPOOL_FILTRATION_MEDIUM "medium" +#define D_NEOPOOL_FILTRATION_FAST "fast" +#define D_NEOPOOL_TYPE "Type" // Sensor & relais names +#define D_NEOPOOL_REDOX "Redox" +#define D_NEOPOOL_CHLORINE "Chlorine" +#define D_NEOPOOL_CONDUCTIVITY "Conductivity" +#define D_NEOPOOL_IONIZATION "Ionization" +#define D_NEOPOOL_HYDROLYSIS "Hydrolysis" +#define D_NEOPOOL_RELAY "Relay" +#define D_NEOPOOL_RELAY_FILTRATION "Filtration" // Relay assignment +#define D_NEOPOOL_RELAY_LIGHT "Light" +#define D_NEOPOOL_RELAY_PH_ACID "Acid pump" +#define D_NEOPOOL_RELAY_PH_BASE "Base pump" +#define D_NEOPOOL_RELAY_RX "Redox level" +#define D_NEOPOOL_RELAY_CL "Chlorine pump" +#define D_NEOPOOL_RELAY_CD "Conductivity" +#define D_NEOPOOL_RELAY_HEATING "Heating" +#define D_NEOPOOL_RELAY_UV "UV" +#define D_NEOPOOL_RELAY_VALVE "Valve" +#define D_NEOPOOL_RELAY_AUX "Aux" +#define D_NEOPOOL_TIME "Time" +#define D_NEOPOOL_FILT_MODE "Filtration mode" +#define D_NEOPOOL_CELL_RUNTIME "Cell runtime" +#define D_NEOPOOL_POLARIZATION "Pol" // Sensor status +#define D_NEOPOOL_PR_OFF "PrOff" +#define D_NEOPOOL_SETPOINT_OK "Ok" +#define D_NEOPOOL_COVER "Cover" +#define D_NEOPOOL_SHOCK "Boost" +#define D_NEOPOOL_STATUS_ON "ON" +#define D_NEOPOOL_STATUS_OFF "OFF" +#define D_NEOPOOL_STATUS_WAIT "WAIT" +#define D_NEOPOOL_STATUS_TANK "TANK" +#define D_NEOPOOL_STATUS_FLOW "Flow" +#define D_NEOPOOL_LOW "Low" +#define D_NEOPOOL_FLOW1 "FL1" +#define D_NEOPOOL_FLOW2 "FL2" +#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms +#define D_NEOPOOL_PH_LOW "too low" +#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded" + +#endif // _LANGUAGE_CA_AD_H_ From 28e95ad13e13a8acb1c4b1616aafda0c4159f7a1 Mon Sep 17 00:00:00 2001 From: HardwareBoffin <110466589+HardwareBoffin@users.noreply.github.com> Date: Wed, 3 Aug 2022 13:35:22 +0200 Subject: [PATCH 170/219] Updated ca_AD with data from dev branch --- tasmota/language/ca_AD.h | 406 ++++++++++++++++++++------------------- 1 file changed, 208 insertions(+), 198 deletions(-) diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h index ec02e00f9..6dfeec7e9 100644 --- a/tasmota/language/ca_AD.h +++ b/tasmota/language/ca_AD.h @@ -565,53 +565,53 @@ #define D_GZ_AXIS "Gyro Z-Axis" // xsns_34_hx711.ino -#define D_HX_CAL_REMOVE "Remove weight" -#define D_HX_CAL_REFERENCE "Load reference weight" -#define D_HX_CAL_DONE "Calibrated" -#define D_HX_CAL_FAIL "Calibration failed" -#define D_RESET_HX711 "Reset Scale" -#define D_CONFIGURE_HX711 "Configure Scale" -#define D_HX711_PARAMETERS "Scale parameters" -#define D_ITEM_WEIGHT "Item weight" -#define D_REFERENCE_WEIGHT "Reference weight" -#define D_CALIBRATE "Calibrate" -#define D_CALIBRATION "Calibration" +#define D_HX_CAL_REMOVE "Treu el pes" +#define D_HX_CAL_REFERENCE "Posa el pes de referència" +#define D_HX_CAL_DONE "Calibrat" +#define D_HX_CAL_FAIL "Error a la Calibració" +#define D_RESET_HX711 "Reinicia la bàscula" +#define D_CONFIGURE_HX711 "Configura la bàscula" +#define D_HX711_PARAMETERS "Paràmetres de la bàscula" +#define D_ITEM_WEIGHT "Pes de l'element" +#define D_REFERENCE_WEIGHT "Pes de referència" +#define D_CALIBRATE "Calibrar" +#define D_CALIBRATION "Calibració" //xsns_35_tx20.ino -#define D_TX20_WIND_DIRECTION "Wind Direction" -#define D_TX20_WIND_SPEED "Wind Speed" -#define D_TX20_WIND_SPEED_MIN "Wind Speed Min" -#define D_TX20_WIND_SPEED_MAX "Wind Speed Max" +#define D_TX20_WIND_DIRECTION "Direcció del vent" +#define D_TX20_WIND_SPEED "Velocitat del vent" +#define D_TX20_WIND_SPEED_MIN "Velocitat del vent Mín" +#define D_TX20_WIND_SPEED_MAX "Velocitat del vent Màx" #define D_TX20_NORTH "N" #define D_TX20_EAST "E" #define D_TX20_SOUTH "S" #define D_TX20_WEST "O" // xsns_53_sml.ino -#define D_TPWRIN "Total-In" -#define D_TPWRIN0 "Total-In T0" -#define D_TPWRIN1 "Total-In T1" -#define D_TPWROUT "Total-Out" -#define D_TPWRCURR "Current-In/Out" -#define D_TPWRCURR1 "Current-In p1" -#define D_TPWRCURR2 "Current-In p2" -#define D_TPWRCURR3 "Current-In p3" -#define D_Strom_L1 "Current L1" -#define D_Strom_L2 "Current L2" -#define D_Strom_L3 "Current L3" -#define D_Spannung_L1 "Voltage L1" -#define D_Spannung_L2 "Voltage L2" -#define D_Spannung_L3 "Voltage L3" -#define D_METERNR "Meter_number" -#define D_METERSID "Service ID" -#define D_GasIN "Counter" // Gas-Verbrauch -#define D_H2oIN "Counter" // H2o-Verbrauch -#define D_StL1L2L3 "Current L1+L2+L3" -#define D_SpL1L2L3 "Voltage L1+L2+L3/3" +#define D_TPWRIN "Potència Total-entrada" +#define D_TPWRIN0 "Potència Total-In T0" +#define D_TPWRIN1 "Potència Total-In T1" +#define D_TPWROUT "Potència Total-Out" +#define D_TPWRCURR "Potència Actual-In/Out" +#define D_TPWRCURR1 "Potència Actual-In p1" +#define D_TPWRCURR2 "Potència Actual-In p2" +#define D_TPWRCURR3 "Potència Actual-In p3" +#define D_Strom_L1 "Intensitat L1" +#define D_Strom_L2 "Intensitat L2" +#define D_Strom_L3 "Intensitat L3" +#define D_Spannung_L1 "Tensió L1" +#define D_Spannung_L2 "Tensió L2" +#define D_Spannung_L3 "Tensió L3" +#define D_METERNR "Núm. comptador" +#define D_METERSID "ID de servei" +#define D_GasIN "Comptador" // Gas-Verbrauch +#define D_H2oIN "Comptador" // H2o-Verbrauch +#define D_StL1L2L3 "Intensitat L1+L2+L3" +#define D_SpL1L2L3 "Tensió L1+L2+L3/3" // xsns_86_tfminiplus.ino -#define D_SIGNALSTRENGTH "Signal Strength" -#define D_CHIPTEMPERATURE "Chip Temperature" +#define D_SIGNALSTRENGTH "Nivell del senyal" +#define D_CHIPTEMPERATURE "Temperatura del Xip" // tasmota_template.h - keep them as short as possible to be able to fit them in GUI drop down box #define D_SENSOR_NONE "Cap" @@ -624,20 +624,22 @@ #define D_SENSOR_DS18X20 "DS18x20" #define D_SENSOR_I2C_SCL "I2C SCL" #define D_SENSOR_I2C_SDA "I2C SDA" -#define D_SENSOR_I2S_OUT_DATA "I2S Out Data" -#define D_SENSOR_I2S_OUT_CLK "I2S Out Clk" -#define D_SENSOR_I2S_OUT_SLCT "I2S Out Slct" -#define D_SENSOR_I2S_IN_DATA "I2S In Data" -#define D_SENSOR_I2S_IN_CLK "I2S In Clk" -#define D_SENSOR_I2S_IN_SLCT "I2S In Slct" +#define D_SENSOR_I2S_MCLK "I2S MCLK" +#define D_SENSOR_I2S_BCLK "I2S BCLK" +#define D_SENSOR_I2S_WS_IN "I2S BCLK IN" +#define D_SENSOR_I2S_WS "I2S WS" +#define D_SENSOR_I2S_BCLK_IN "I2S WS IN" +#define D_SENSOR_I2S_DIN "I2S DIN" +#define D_SENSOR_I2S_DOUT "I2S DOUT" #define D_SENSOR_WS2812 "WS2812" #define D_SENSOR_DFR562 "Reproductor MP3" +#define D_SENSOR_DFR562_BUSY "MP3 Ocupat" #define D_SENSOR_IRSEND "IRsend" #define D_SENSOR_SWITCH "Interruptor" // Suffix "1" #define D_SENSOR_BUTTON "Botó" // Suffix "1" #define D_SENSOR_RELAY "Relé" // Suffix "1i" #define D_SENSOR_LED "Led" // Suffix "1i" -#define D_SENSOR_LED_LINK "EnllaçLed" // Suffix "i" +#define D_SENSOR_LED_LINK "Enllaç Led" // Suffix "i" #define D_SENSOR_PWM "PWM" // Suffix "1" #define D_SENSOR_COUNTER "Comptador" // Suffix "1" #define D_SENSOR_INTERRUPT "Interrupció" @@ -671,6 +673,8 @@ #define D_SENSOR_HPMA_TX "HPMA Tx" #define D_SENSOR_SBR_RX "SerBr Rx" #define D_SENSOR_SBR_TX "SerBr Tx" +#define D_SENSOR_MBR_TX "ModBr Tx" +#define D_SENSOR_MBR_RX "ModBr Rx" #define D_SENSOR_SR04_TRIG "SR04 Tri/TX" #define D_SENSOR_SR04_ECHO "SR04 Ech/RX" #define D_SENSOR_SDM72_TX "SDM72 Tx" @@ -683,6 +687,10 @@ #define D_SENSOR_SDM630_RX "SDM630 Rx" #define D_SENSOR_WE517_TX "WE517 Tx" #define D_SENSOR_WE517_RX "WE517 Rx" +#define D_GPIO_TM1621_CS "TM1621 CS" +#define D_GPIO_TM1621_WR "TM1621 WR" +#define D_GPIO_TM1621_RD "TM1621 RD" +#define D_GPIO_TM1621_DAT "TM1621 DAT" #define D_SENSOR_TM1637_CLK "TM1637 CLK" #define D_SENSOR_TM1637_DIO "TM1637 DIO" #define D_SENSOR_TM1638_CLK "TM1638 CLK" @@ -763,8 +771,8 @@ #define D_SENSOR_A4988_STP "A4988 STP" #define D_SENSOR_A4988_ENA "A4988 ENA" #define D_SENSOR_A4988_MS1 "A4988 MS1" -#define D_SENSOR_OUTPUT_HI "Output Hi" -#define D_SENSOR_OUTPUT_LO "Output Lo" +#define D_SENSOR_OUTPUT_HI "Sortida alta" +#define D_SENSOR_OUTPUT_LO "Sortida baixa" #define D_SENSOR_AS608_TX "AS608 Tx" #define D_SENSOR_AS608_RX "AS608 Rx" #define D_SENSOR_DDS2382_TX "DDS238-2 Tx" @@ -773,6 +781,8 @@ #define D_SENSOR_DDSU666_RX "DDSU666 Rx" #define D_SENSOR_SM2135_CLK "SM2135 Clk" #define D_SENSOR_SM2135_DAT "SM2135 Dat" +#define D_SENSOR_SM2335_CLK "SM2335 Clk" +#define D_SENSOR_SM2335_DAT "SM2335 Dat" #define D_SENSOR_BP5758D_CLK "BP5758D Clk" #define D_SENSOR_BP5758D_DAT "BP5758D Dat" #define D_SENSOR_DEEPSLEEP "DeepSleep" @@ -796,12 +806,12 @@ #define D_SENSOR_WINDMETER_SPEED "WindMeter Spd" #define D_SENSOR_TELEINFO_RX "TInfo Rx" #define D_SENSOR_TELEINFO_ENABLE "TInfo EN" -#define D_SENSOR_LMT01_PULSE "LMT01 Pulse" -#define D_SENSOR_ADC_INPUT "ADC Input" -#define D_SENSOR_ADC_TEMP "ADC Temp" -#define D_SENSOR_ADC_LIGHT "ADC Light" -#define D_SENSOR_ADC_BUTTON "ADC Button" -#define D_SENSOR_ADC_RANGE "ADC Range" +#define D_SENSOR_LMT01_PULSE "Puls LMT01" +#define D_SENSOR_ADC_INPUT "Entrada ADC" +#define D_SENSOR_ADC_TEMP "Temp ADC" +#define D_SENSOR_ADC_LIGHT "Llum ADC" +#define D_SENSOR_ADC_BUTTON "Botó ADC" +#define D_SENSOR_ADC_RANGE "Rang ADC" #define D_SENSOR_ADC_CT_POWER "ADC CT Power" #define D_SENSOR_ADC_JOYSTICK "ADC Joystick" #define D_SENSOR_ADC_PH "ADC pH" @@ -925,148 +935,148 @@ #define D_UNIT_LITER_PER_MINUTE "l/min" #define D_UNIT_CUBICMETER_PER_HOUR "m³/h" -#define D_NEW_ADDRESS "Setting address to" -#define D_OUT_OF_RANGE "Out of Range" -#define D_SENSOR_DETECTED "detected" +#define D_NEW_ADDRESS "Posant l'adreça a" +#define D_OUT_OF_RANGE "Fora de rang" +#define D_SENSOR_DETECTED "detectat" //SDM220, SDM120, SDM72, LE01MR, SDM230 -#define D_EXPORT_POWER "Export Power" -#define D_IMPORT_POWER "Import Power" -#define D_PHASE_ANGLE "Phase Angle" -#define D_IMPORT_ACTIVE "Import Active" -#define D_EXPORT_ACTIVE "Export Active" -#define D_IMPORT_REACTIVE "Import Reactive" -#define D_EXPORT_REACTIVE "Export Reactive" -#define D_TOTAL_REACTIVE "Total Reactive" +#define D_EXPORT_POWER "Potència Exportada" +#define D_IMPORT_POWER "Potència Importada" +#define D_PHASE_ANGLE "Angle de fase" +#define D_IMPORT_ACTIVE "Importat Activa" +#define D_EXPORT_ACTIVE "Exportat Activa" +#define D_IMPORT_REACTIVE "Importat Reactiva" +#define D_EXPORT_REACTIVE "Exportat Reactiva" +#define D_TOTAL_REACTIVE "Total Reactiva" #define D_UNIT_KWARH "kVArh" -#define D_UNIT_ANGLE "Deg" -#define D_TOTAL_ACTIVE "Total Active" -#define D_RESETTABLE_TOTAL_ACTIVE "Total Active (RST)" +#define D_UNIT_ANGLE "Graus" +#define D_TOTAL_ACTIVE "Total Activa" +#define D_RESETTABLE_TOTAL_ACTIVE "Total Activa (RST)" //SOLAXX1 -#define D_PV1_VOLTAGE "PV1 Voltage" -#define D_PV1_CURRENT "PV1 Current" -#define D_PV1_POWER "PV1 Power" -#define D_PV2_VOLTAGE "PV2 Voltage" -#define D_PV2_CURRENT "PV2 Current" -#define D_PV2_POWER "PV2 Power" -#define D_SOLAR_POWER "Solar Power" -#define D_INVERTER_POWER "Inverter Power" -#define D_STATUS "Status" -#define D_SOLAX_MODE_0 "Waiting" -#define D_SOLAX_MODE_1 "Checking" -#define D_SOLAX_MODE_2 "Working" -#define D_SOLAX_MODE_3 "Failure" -#define D_SOLAX_MODE_4 "Permanent Failure" -#define D_SOLAX_MODE_5 "Software Update" -#define D_SOLAX_MODE_6 "Selftest" -#define D_SOLAX_ERROR_0 "No Error" -#define D_SOLAX_ERROR_1 "Grid Lost Fault" -#define D_SOLAX_ERROR_2 "Grid Voltage Fault" -#define D_SOLAX_ERROR_3 "Grid Frequency Fault" -#define D_SOLAX_ERROR_4 "PV Voltage too high" -#define D_SOLAX_ERROR_5 "PV Isolation Fault" -#define D_SOLAX_ERROR_6 "Over Temperature Fault" -#define D_SOLAX_ERROR_7 "Fan Fault" -#define D_SOLAX_ERROR_8 "Other Device Fault" +#define D_PV1_VOLTAGE "Tensió PV1" +#define D_PV1_CURRENT "Intensitat PV1" +#define D_PV1_POWER "Potència PV1" +#define D_PV2_VOLTAGE "Tensió PV2" +#define D_PV2_CURRENT "Intensitat PV2" +#define D_PV2_POWER "Potència PV2" +#define D_SOLAR_POWER "Potència Solar" +#define D_INVERTER_POWER "Potència Inversor" +#define D_STATUS "Estat" +#define D_SOLAX_MODE_0 "Esperant" +#define D_SOLAX_MODE_1 "Comprovant" +#define D_SOLAX_MODE_2 "Treballant" +#define D_SOLAX_MODE_3 "Fallada" +#define D_SOLAX_MODE_4 "Fallada Permanent" +#define D_SOLAX_MODE_5 "Actualització de programari" +#define D_SOLAX_MODE_6 "Autotest" +#define D_SOLAX_ERROR_0 "Cap Error" +#define D_SOLAX_ERROR_1 "Error : Xarxa perduda" +#define D_SOLAX_ERROR_2 "Error de Tensió de Xarxa" +#define D_SOLAX_ERROR_3 "Error de freqüència de Xarxa" +#define D_SOLAX_ERROR_4 "Tensió de PVmassa alta" +#define D_SOLAX_ERROR_5 "Error d'aïllament al PV" +#define D_SOLAX_ERROR_6 "M'he sobre escalfat" +#define D_SOLAX_ERROR_7 "Error al ventilador" +#define D_SOLAX_ERROR_8 "Altre error intern" //xdrv_10_scripter.ino -#define D_CONFIGURE_SCRIPT "Edit Script" -#define D_SCRIPT "edit script" -#define D_SDCARD_UPLOAD "file upload" -#define D_UFSDIR "ufs directory" -#define D_UPL_DONE "Done" -#define D_SCRIPT_CHARS_LEFT "chars left" -#define D_SCRIPT_CHARS_NO_MORE "no more chars" -#define D_SCRIPT_DOWNLOAD "Download" -#define D_SCRIPT_ENABLE "Script enable" -#define D_SCRIPT_UPLOAD "Upload" -#define D_SCRIPT_UPLOAD_FILES "Upload files" +#define D_CONFIGURE_SCRIPT "Edita Script" +#define D_SCRIPT "edita script" +#define D_SDCARD_UPLOAD "puja arxiu" +#define D_UFSDIR "directori UFS" +#define D_UPL_DONE "Fet" +#define D_SCRIPT_CHARS_LEFT "caràcters lliures" +#define D_SCRIPT_CHARS_NO_MORE "no caben més caràcters" +#define D_SCRIPT_DOWNLOAD "Baixar" +#define D_SCRIPT_ENABLE "Habilitar Script" +#define D_SCRIPT_UPLOAD "Pujar" +#define D_SCRIPT_UPLOAD_FILES "Pujar arxius" //xdrv_50_filesystem.ino -#define D_MANAGE_FILE_SYSTEM "Manage File system" -#define D_FS_SIZE "Size" -#define D_FS_FREE "Free" -#define D_NEW_FILE "newfile.txt" -#define D_CREATE_NEW_FILE "Create and edit new file" -#define D_EDIT_FILE "Edit File" -#define D_CONFIRM_FILE_DEL "Confirm file deletion" -#define D_SHOW_HIDDEN_FILES "Show hidden files" +#define D_MANAGE_FILE_SYSTEM "Gestionar el sistema d'arxius" +#define D_FS_SIZE "Mida" +#define D_FS_FREE "Lliure" +#define D_NEW_FILE "nouarxiu.txt" +#define D_CREATE_NEW_FILE "Crear i editar un nou arxiu" +#define D_EDIT_FILE "Edita Arxiu" +#define D_CONFIRM_FILE_DEL "Confirma esborrar arxiu" +#define D_SHOW_HIDDEN_FILES "Mostra arxius ocults" //xsns_67_as3935.ino -#define D_AS3935_GAIN "gain:" -#define D_AS3935_ENERGY "energy:" -#define D_AS3935_DISTANCE "distance:" -#define D_AS3935_DISTURBER "disturber:" +#define D_AS3935_GAIN "guany:" +#define D_AS3935_ENERGY "energia:" +#define D_AS3935_DISTANCE "distància:" +#define D_AS3935_DISTURBER "pertorbador:" #define D_AS3935_VRMS "µVrms:" #define D_AS3935_APRX "aprx.:" #define D_AS3935_AWAY "away" -#define D_AS3935_LIGHT "lightning" -#define D_AS3935_OUT "lightning out of range" -#define D_AS3935_NOT "distance not determined" -#define D_AS3935_ABOVE "lightning overhead" -#define D_AS3935_NOISE "noise detected" -#define D_AS3935_DISTDET "disturber detected" -#define D_AS3935_INTNOEV "Interrupt with no Event!" -#define D_AS3935_FLICKER "IRQ Pin flicker!" -#define D_AS3935_POWEROFF "Powerd Off" -#define D_AS3935_NOMESS "listening..." -#define D_AS3935_ON "On" -#define D_AS3935_OFF "Off" -#define D_AS3935_INDOORS "Indoors" -#define D_AS3935_OUTDOORS "Outdoors" -#define D_AS3935_CAL_FAIL "calibration failed" -#define D_AS3935_CAL_OK "calibration set to:" +#define D_AS3935_LIGHT "llamp" +#define D_AS3935_OUT "llamp fora de rang" +#define D_AS3935_NOT "distància no determinada" +#define D_AS3935_ABOVE "llamp al damunt" +#define D_AS3935_NOISE "soroll detectat" +#define D_AS3935_DISTDET "pertorbador detectat" +#define D_AS3935_INTNOEV "Interrupció sense Event!" +#define D_AS3935_FLICKER "Transició a IRQ!" +#define D_AS3935_POWEROFF "Apagat" +#define D_AS3935_NOMESS "escoltant..." +#define D_AS3935_ON "Engegat" +#define D_AS3935_OFF "Apagat" +#define D_AS3935_INDOORS "Interior" +#define D_AS3935_OUTDOORS "Exterior" +#define D_AS3935_CAL_FAIL "error de calibració" +#define D_AS3935_CAL_OK "calibració posada a:" //xsns_68_opentherm.ino #define D_SENSOR_BOILER_OT_RX "OpenTherm RX" #define D_SENSOR_BOILER_OT_TX "OpenTherm TX" // xnrg_15_teleinfo Denky (Teleinfo) -#define D_CONTRACT "Contract" -#define D_POWER_LOAD "Power load" -#define D_CURRENT_TARIFF "Current Tariff" -#define D_TARIFF "Tariff" +#define D_CONTRACT "Contracte" +#define D_POWER_LOAD "Càrrega de potència" +#define D_CURRENT_TARIFF "Tarifa actual" +#define D_TARIFF "Tarifa" #define D_OVERLOAD "ADPS" -#define D_MAX_POWER "Max Power" -#define D_MAX_CURRENT "Max Current" +#define D_MAX_POWER "Potència Màxima" +#define D_MAX_CURRENT "Intensitat màxima" // xsns_79_as608.ino -#define D_FP_ENROLL_PLACEFINGER "Place finger" -#define D_FP_ENROLL_REMOVEFINGER "Remove finger" -#define D_FP_ENROLL_PLACESAMEFINGER "Place same finger again" -#define D_FP_ENROLL_RETRY "Error so retry" -#define D_FP_ENROLL_RESTART "Restart" +#define D_FP_ENROLL_PLACEFINGER "Posa el dit" +#define D_FP_ENROLL_REMOVEFINGER "Treu el dit" +#define D_FP_ENROLL_PLACESAMEFINGER "Posa el mateix dit un altre cop" +#define D_FP_ENROLL_RETRY "Error : Has de reintentar" +#define D_FP_ENROLL_RESTART "Reinici" #define D_FP_ENROLL_ERROR "Error" -#define D_FP_ENROLL_RESET "Reset" -#define D_FP_ENROLL_ACTIVE "Active" -#define D_FP_ENROLL_INACTIVE "Inactive" +#define D_FP_ENROLL_RESET "Esborrat" +#define D_FP_ENROLL_ACTIVE "Actiu" +#define D_FP_ENROLL_INACTIVE "Inactiu" // Indexed by Adafruit_Fingerprint.h defines -#define D_FP_PACKETRECIEVEERR "Comms error" // 0x01 Error when receiving data package +#define D_FP_PACKETRECIEVEERR "Error de Comunicació" // 0x01 Error when receiving data package #define D_FP_NOFINGER "" // 0x02 No finger on the sensor -#define D_FP_IMAGEFAIL "Imaging error" // 0x03 Failed to enroll the finger -#define D_FP_IMAGEMESS "Image too messy" // 0x06 Failed to generate character file due to overly disorderly fingerprint image -#define D_FP_FEATUREFAIL "Fingerprint too small" // 0x07 Failed to generate character file due to the lack of character point or small fingerprint image -#define D_FP_NOMATCH "No match" // 0x08 Finger doesn't match -#define D_FP_NOTFOUND "Did not find a match" // 0x09 Failed to find matching finger -#define D_FP_ENROLLMISMATCH "Fingerprint did not match" // 0x0A Failed to combine the character files -#define D_FP_BADLOCATION "Bad location" // 0x0B Addressed PageID is beyond the finger library -#define D_FP_DBRANGEFAIL "DB range error" // 0x0C Error when reading template from library or invalid template -#define D_FP_UPLOADFEATUREFAIL "Upload feature error" // 0x0D Error when uploading template -#define D_FP_PACKETRESPONSEFAIL "Packet response error" // 0x0E Module failed to receive the following data packages -#define D_FP_UPLOADFAIL "Upload error" // 0x0F Error when uploading image -#define D_FP_DELETEFAIL "Delete error" // 0x10 Failed to delete the template -#define D_FP_DBCLEARFAIL "DB Clear error" // 0x11 Failed to clear finger library -#define D_FP_PASSFAIL "Password error" // 0x13 Find whether the fingerprint passed or failed -#define D_FP_INVALIDIMAGE "Image invalid" // 0x15 Failed to generate image because of lac of valid primary image -#define D_FP_FLASHERR "Flash write error" // 0x18 Error when writing flash -#define D_FP_INVALIDREG "Invalid number" // 0x1A Invalid register number -#define D_FP_ADDRCODE "Address code" // 0x20 Address code -#define D_FP_PASSVERIFY "Password verified" // 0x21 Verify the fingerprint passed +#define D_FP_IMAGEFAIL "Error d'imatge" // 0x03 Failed to enroll the finger +#define D_FP_IMAGEMESS "Imatge massa complexa" // 0x06 Failed to generate character file due to overly disorderly fingerprint image +#define D_FP_FEATUREFAIL "Petjada massa petita" // 0x07 Failed to generate character file due to the lack of character point or small fingerprint image +#define D_FP_NOMATCH "Cap coincidència" // 0x08 Finger doesn't match +#define D_FP_NOTFOUND "No he tronat Cap coincidència" // 0x09 Failed to find matching finger +#define D_FP_ENROLLMISMATCH "La petjada no coincideix" // 0x0A Failed to combine the character files +#define D_FP_BADLOCATION "Mal lloc" // 0x0B Addressed PageID is beyond the finger library +#define D_FP_DBRANGEFAIL "Error de rang a la base de dades" // 0x0C Error when reading template from library or invalid template +#define D_FP_UPLOADFEATUREFAIL "Error de pujada de patró" // 0x0D Error when uploading template +#define D_FP_PACKETRESPONSEFAIL "Error de Paquet de resposta" // 0x0E Module failed to receive the following data packages +#define D_FP_UPLOADFAIL "Error de pujada" // 0x0F Error when uploading image +#define D_FP_DELETEFAIL "Error esborrant" // 0x10 Failed to delete the template +#define D_FP_DBCLEARFAIL "Error en netejar la base de dades" // 0x11 Failed to clear finger library +#define D_FP_PASSFAIL "Error de mot de pas" // 0x13 Find whether the fingerprint passed or failed +#define D_FP_INVALIDIMAGE "Imatge invàlida" // 0x15 Failed to generate image because of lac of valid primary image +#define D_FP_FLASHERR "Error escrivint a la Flash" // 0x18 Error when writing flash +#define D_FP_INVALIDREG "Nombre invàlid" // 0x1A Invalid register number +#define D_FP_ADDRCODE "Codi d'adreça" // 0x20 Address code +#define D_FP_PASSVERIFY "Mot de pas verificat" // 0x21 Verify the fingerprint passed #define D_FP_UNKNOWNERROR "Error" // Any other error // xsns_96_flowratemeter.ino -#define D_FLOWRATEMETER_NAME "Flowrate" +#define D_FLOWRATEMETER_NAME "Cabal" // xsns_83_neopool.ino #define D_NEOPOOL_MACH_NONE "NeoPool" // Machine names @@ -1083,35 +1093,35 @@ #define D_NEOPOOL_MACH_HAY "Hay" #define D_NEOPOOL_FILTRATION_MANUAL "Manual" // Filtration modes #define D_NEOPOOL_FILTRATION_AUTO "Auto" -#define D_NEOPOOL_FILTRATION_HEATING "Heating" -#define D_NEOPOOL_FILTRATION_SMART "Smart" -#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intelligent" -#define D_NEOPOOL_FILTRATION_BACKWASH "Backwash" +#define D_NEOPOOL_FILTRATION_HEATING "Escalfant" +#define D_NEOPOOL_FILTRATION_SMART "Llesta" +#define D_NEOPOOL_FILTRATION_INTELLIGENT "Intel·ligent" +#define D_NEOPOOL_FILTRATION_BACKWASH "Rentat" #define D_NEOPOOL_FILTRATION_NONE "" // Filtration speed level -#define D_NEOPOOL_FILTRATION_SLOW "slow" -#define D_NEOPOOL_FILTRATION_MEDIUM "medium" -#define D_NEOPOOL_FILTRATION_FAST "fast" -#define D_NEOPOOL_TYPE "Type" // Sensor & relais names +#define D_NEOPOOL_FILTRATION_SLOW "lent" +#define D_NEOPOOL_FILTRATION_MEDIUM "mitjà" +#define D_NEOPOOL_FILTRATION_FAST "ràpid" +#define D_NEOPOOL_TYPE "Tipus" // Sensor & relais names #define D_NEOPOOL_REDOX "Redox" -#define D_NEOPOOL_CHLORINE "Chlorine" -#define D_NEOPOOL_CONDUCTIVITY "Conductivity" -#define D_NEOPOOL_IONIZATION "Ionization" -#define D_NEOPOOL_HYDROLYSIS "Hydrolysis" -#define D_NEOPOOL_RELAY "Relay" -#define D_NEOPOOL_RELAY_FILTRATION "Filtration" // Relay assignment -#define D_NEOPOOL_RELAY_LIGHT "Light" -#define D_NEOPOOL_RELAY_PH_ACID "Acid pump" -#define D_NEOPOOL_RELAY_PH_BASE "Base pump" -#define D_NEOPOOL_RELAY_RX "Redox level" -#define D_NEOPOOL_RELAY_CL "Chlorine pump" -#define D_NEOPOOL_RELAY_CD "Conductivity" -#define D_NEOPOOL_RELAY_HEATING "Heating" +#define D_NEOPOOL_CHLORINE "Clor" +#define D_NEOPOOL_CONDUCTIVITY "Conductivitat" +#define D_NEOPOOL_IONIZATION "Ionització" +#define D_NEOPOOL_HYDROLYSIS "Hidròlisi" +#define D_NEOPOOL_RELAY "Relé" +#define D_NEOPOOL_RELAY_FILTRATION "Filtrat" // Relay assignment +#define D_NEOPOOL_RELAY_LIGHT "Llum" +#define D_NEOPOOL_RELAY_PH_ACID "Aportador d'Àcid" +#define D_NEOPOOL_RELAY_PH_BASE "Aportador de Base" +#define D_NEOPOOL_RELAY_RX "Nivell Redox" +#define D_NEOPOOL_RELAY_CL "Bomba de clor" +#define D_NEOPOOL_RELAY_CD "Conductivitat" +#define D_NEOPOOL_RELAY_HEATING "Escalfant" #define D_NEOPOOL_RELAY_UV "UV" -#define D_NEOPOOL_RELAY_VALVE "Valve" -#define D_NEOPOOL_RELAY_AUX "Aux" -#define D_NEOPOOL_TIME "Time" -#define D_NEOPOOL_FILT_MODE "Filtration mode" -#define D_NEOPOOL_CELL_RUNTIME "Cell runtime" +#define D_NEOPOOL_RELAY_VALVE "Vàlvula" +#define D_NEOPOOL_RELAY_AUX "Auxiliar" +#define D_NEOPOOL_TIME "Temps" +#define D_NEOPOOL_FILT_MODE "Mode de filtrar" +#define D_NEOPOOL_CELL_RUNTIME "Temps de marxa de la cel·la" #define D_NEOPOOL_POLARIZATION "Pol" // Sensor status #define D_NEOPOOL_PR_OFF "PrOff" #define D_NEOPOOL_SETPOINT_OK "Ok" @@ -1119,14 +1129,14 @@ #define D_NEOPOOL_SHOCK "Boost" #define D_NEOPOOL_STATUS_ON "ON" #define D_NEOPOOL_STATUS_OFF "OFF" -#define D_NEOPOOL_STATUS_WAIT "WAIT" -#define D_NEOPOOL_STATUS_TANK "TANK" -#define D_NEOPOOL_STATUS_FLOW "Flow" -#define D_NEOPOOL_LOW "Low" +#define D_NEOPOOL_STATUS_WAIT "ESPERA" +#define D_NEOPOOL_STATUS_TANK "TANC" +#define D_NEOPOOL_STATUS_FLOW "Cabal" +#define D_NEOPOOL_LOW "Baix" #define D_NEOPOOL_FLOW1 "FL1" #define D_NEOPOOL_FLOW2 "FL2" -#define D_NEOPOOL_PH_HIGH "too high" // ph Alarms -#define D_NEOPOOL_PH_LOW "too low" -#define D_NEOPOOL_PUMP_TIME_EXCEEDED "pump time exceeded" +#define D_NEOPOOL_PH_HIGH "massa alt" // ph Alarms +#define D_NEOPOOL_PH_LOW "massa baix" +#define D_NEOPOOL_PUMP_TIME_EXCEEDED "temps de bomba excedit" #endif // _LANGUAGE_CA_AD_H_ From 72e88d1be336852d0fb38509a40ee14812ae9310 Mon Sep 17 00:00:00 2001 From: HardwareBoffin <110466589+HardwareBoffin@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:04:08 +0200 Subject: [PATCH 171/219] Updated my_user_config.h to include the option ca_AD Just added that commented config option. I tested it works beforehand. --- tasmota/my_user_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index eeaa50aba..5d8dafe43 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -373,6 +373,7 @@ // If non selected the default en-GB will be used //#define MY_LANGUAGE af_AF // Afrikaans in South Africa //#define MY_LANGUAGE bg_BG // Bulgarian in Bulgaria +//#define MY_LANGUAGE ca_AD // Catalan in All catalan speaking countries ( Andorra ) //#define MY_LANGUAGE cs_CZ // Czech in Czech //#define MY_LANGUAGE de_DE // German in Germany //#define MY_LANGUAGE el_GR // Greek in Greece From 73797b3994671f6f43475a36b80679f1011967a5 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Wed, 3 Aug 2022 17:20:09 +0200 Subject: [PATCH 172/219] Zigbee added recording of when the battery was last reported --- CHANGELOG.md | 1 + .../xdrv_23_zigbee_2_devices.ino | 14 ++- .../xdrv_23_zigbee_2a_devices_impl.ino | 3 + .../xdrv_23_zigbee_4b_data.ino | 7 +- .../xdrv_23_zigbee_A_impl.ino | 110 ++++++++++++------ 5 files changed, 98 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a99ee2ef..d3428a49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) - Support for multiple `IRsend` GPIOs +- Zigbee added recording of when the battery was last reported ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino index ad4c892b2..f5830f2ea 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino @@ -734,6 +734,8 @@ public: // other status - device wide data is 8 bytes // START OF DEVICE WIDE DATA uint32_t last_seen; // Last seen time (epoch) + uint32_t batt_last_seen; // Time when we last received battery status (epoch), 0 means unknown, 0xFFFFFFFF means that the device has no battery + uint32_t batt_last_probed; // Time when the device was last probed for batteyr values uint8_t lqi; // lqi from last message, 0xFF means unknown uint8_t batterypercent; // battery percentage (0..100), 0xFF means unknwon uint16_t reserved_for_alignment; @@ -760,6 +762,8 @@ public: reachable(false), data(), last_seen(0), + batt_last_seen(0), + batt_last_probed(0), lqi(0xFF), batterypercent(0xFF), reserved_for_alignment(0xFFFF), @@ -779,6 +783,7 @@ public: inline bool validLqi(void) const { return 0xFF != lqi; } inline bool validBatteryPercent(void) const { return 0xFF != batterypercent; } inline bool validLastSeen(void) const { return 0x0 != last_seen; } + inline bool validBattLastSeen(void) const { return (0x0 != batt_last_seen) && (0xFFFFFFFF != batt_last_seen); } inline void setReachable(bool _reachable) { reachable = _reachable; } inline bool getReachable(void) const { return reachable; } @@ -789,7 +794,14 @@ public: inline void setRouter(bool router) { is_router = router; } inline void setLQI(uint8_t _lqi) { lqi = _lqi; } - inline void setBatteryPercent(uint8_t bp) { batterypercent = bp; } + inline void setBatteryPercent(uint8_t bp) { + batterypercent = bp; + if (Rtc.utc_time >= START_VALID_TIME) { + batt_last_seen = Rtc.utc_time; + } + } + inline void setHasNoBattery(void) { batt_last_seen = 0xFFFFFFFF; } + inline bool hasNoBattery(void) const { return 0xFFFFFFFF != batt_last_seen; } // Add an endpoint to a device bool addEndpoint(uint8_t endpoint); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino index afa448761..a8f077e72 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino @@ -725,6 +725,9 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const { void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const { attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable()); if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batterypercent); } + if (validBattLastSeen()) { + attr_list.addAttributePMEM(PSTR("BatteryLastSeenEpoch")).setUInt(batt_last_seen); + } if (validLastSeen()) { if (Rtc.utc_time >= last_seen) { attr_list.addAttributePMEM(PSTR("LastSeen")).setUInt(Rtc.utc_time - last_seen); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino index 95eb7096d..76fb96a96 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino @@ -47,6 +47,9 @@ int32_t hydrateDeviceWideData(class Z_Device & device, const SBuffer & buf, size device.last_seen = buf.get32(start+1); device.lqi = buf.get8(start + 5); device.batterypercent = buf.get8(start + 6); + if (segment_len >= 10) { + device.batt_last_seen = buf.get32(start+7); + } return segment_len + 1; } @@ -118,10 +121,12 @@ SBuffer hibernateDeviceData(const struct Z_Device & device) { buf.add16(device.shortaddr); // device wide data - buf.add8(6); // 6 bytes + buf.add8(10); // 10 bytes buf.add32(device.last_seen); buf.add8(device.lqi); buf.add8(device.batterypercent); + // now storing batt_last_seen + buf.add32(device.batt_last_seen); for (const auto & data_elt : device.data) { size_t item_len = data_elt.DataTypeToLength(data_elt.getType()); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino index 8ba99cc42..9679fa44e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -1776,7 +1776,7 @@ const char ZB_WEB_U[] PROGMEM = "\0" // +++++++++++++++++++++++++++++++++++++++++++++++++++++++ //=ZB_WEB_BATTERY - "" + "" "\0" // +++++++++++++++++++++++++++++++++++++++++++++++++++++++ //=ZB_WEB_LAST_SEEN @@ -1818,16 +1818,16 @@ enum { ZB_WEB_MAP_REFRESH=1164, ZB_WEB_STATUS_LINE=1230, ZB_WEB_BATTERY=1338, - ZB_WEB_LAST_SEEN=1388, - ZB_WEB_COLOR_RGB=1436, - ZB_WEB_LINE_START=1496, - ZB_WEB_LIGHT_CT=1536, - ZB_WEB_END_STATUS=1591, - ZB_WEB_LINE_END=1608, + ZB_WEB_LAST_SEEN=1410, + ZB_WEB_COLOR_RGB=1458, + ZB_WEB_LINE_START=1518, + ZB_WEB_LIGHT_CT=1558, + ZB_WEB_END_STATUS=1613, + ZB_WEB_LINE_END=1630, }; -// Compressed from 1627 to 1118, -31.3% -const char ZB_WEB[] PROGMEM = "\x00\x66\x3D\x0E\xCA\xB1\xC1\x33\xF0\xF6\xD1\xEE\x3D\x3D\x46\x41\x33\xF0\xE8\x6D" +// Compressed from 1649 to 1124, -31.8% +const char ZB_WEB[] PROGMEM = "\x00\x68\x3D\x0E\xCA\xB1\xC1\x33\xF0\xF6\xD1\xEE\x3D\x3D\x46\x41\x33\xF0\xE8\x6D" "\xA1\x15\x08\x79\xF6\x51\xDD\x3C\xCC\x6F\xFD\x47\x58\x62\xB4\x21\x0E\xF1\xED\x1F" "\xD1\x28\x51\xE6\x72\x99\x0C\x36\x1E\x0C\x67\x51\xD7\xED\x36\xB3\xCC\xE7\x99\xF4" "\x7D\x1E\xE2\x04\x3C\x40\x2B\x04\x3C\x28\x10\xB0\x93\x99\xA4\x30\xD8\x08\x36\x8E" @@ -1873,16 +1873,17 @@ const char ZB_WEB[] PROGMEM = "\x00\x66\x3D\x0E\xCA\xB1\xC1\x33\xF0\xF6\xD1\xEE\ "\x64\x6C\x3E\x8E\x3C\x22\x36\x23\xEB\xC8\xEB\x47\xD7\x81\x07\xA0\x7E\x38\xFC\x3D" "\x0E\xCA\x10\xFC\x3D\x28\x43\xF0\xFA\xF0\x22\x47\x3D\x04\xD3\x30\x43\xC4\x88\x22" "\x35\x16\xA3\xEB\xC7\xD8\x21\xE7\x1E\xD3\xEC\xFC\x9C\x2F\x9E\x9A\x08\x52\xCF\x60" - "\xEA\x3D\x80\x85\x82\x9E\xC3\xE8\x43\xE8\xFA\x04\x4E\x7F\x8E\xB3\xAC\x70\x47\x99" - "\xF4\x20\xC3\x61\xEC\x3F\x0F\x43\xB3\x4F\xC9\xC2\xF9\xE9\x42\x02\x1D\x70\x44\xE8" - "\xA7\x1C\xA2\x36\x1F\x47\x1D\x11\xB0\xFA\x38\xE8\x8D\x87\xB0\xFC\x3F\x47\x91\xB0" - "\xE4\x22\x30\x73\x77\xC7\x83\xE9\xD1\x08\x7D\x07\x38\x5F\x40\x8D\x9F\x9B\x01\x1B" - "\x32\x0C\x23\xCC\xF2\x3E\x8E\x3A\x22\x36\x1F\x47\x1D\x11\x1B\x0F\xA3\x8E\x88\x8D" - "\x80\x83\x9D\x82\x44\xF0\x47\xE1\x98\x10\xF8\x62\x41\xE0\x5E\x19\x7C\x7C\x3D\x87" - "\x30\xF6\x1F\x87\xE8\xF2\x59\xEF\x9E\x0A\x70\xBE\x08\x5D\x15\xA0\x42\xE0\x6C\x83" - "\x2A\x2B\x47\xD0\x87\xB0\xFC\x3D\x3C\x36\xC2\x08\xFC\x3F\x47\x91\xC5\xF5\xF3\xC1" - "\xDC\x3D\x0E\xC2\x04\x19\x87\xD0\x84\x68\x08\x5D\x16\xC9\xC2\xF8\x21\x74\x18\x4E" - "\xCA\x10\xFC\x3E\xBC\x7B\x59\xEE\x9C\x2F\x82\x3F\x4E\x90\x10\x79\x23\x9C\x2F\x9B"; + "\xEA\x3D\x80\x85\x82\x9E\xC3\xE8\x43\xE8\xFA\x3E\xBC\x08\x9D\x2A\x01\x03\xAC\xEB" + "\x1C\x11\xE6\x7D\x08\x30\xD8\x08\x7C\xFA\x1F\x47\x1D\x11\xB0\xFA\x38\xE8\x8D\x87" + "\xD1\xC7\x44\x6C\x3D\x87\xE1\xE8\x76\x69\xF9\x38\x5F\x3D\x28\x40\x43\xC2\xC1\x43" + "\x01\x3F\x47\x91\xB0\xE4\x22\x30\x73\x77\xC7\x83\xE9\xD1\x08\x7D\x07\x38\x5F\x40" + "\x8D\xAA\x9B\x01\x1B\x46\x0C\x23\xCC\xF2\x3E\x8E\x3A\x22\x36\x1F\x47\x1D\x11\x1B" + "\x0F\xA3\x8E\x88\x8D\x80\x83\x9D\x82\x44\xF0\x47\xE1\xF0\x10\xF8\x78\x41\xE0\x5E" + "\x19\x7C\x7C\x3D\x87\x30\xF6\x1F\x87\xE8\xF2\x59\xEF\x9E\x0A\x70\xBE\x08\x5D\x17" + "\x2A\x01\x42\xE0\xC4\x83\x2A\x2B\x47\xD0\x87\xB0\xFC\x3D\x3C\x36\xC2\x08\xFC\x3F" + "\x47\x91\xC5\xF5\xF3\xC1\xDC\x3D\x0E\xC2\x04\x19\x87\xD0\x84\x68\x08\x5D\x18\x29" + "\xC2\xF8\x21\x74\x1D\xCE\xCA\x10\xFC\x3E\xBC\x7B\x59\xEE\x9C\x2F\x82\x3F\x4E\xE8" + "\x10\x79\x39\x9C\x2F\x9B"; // ++++++++++++++++++++^^^^^^^^^^^^^^^^^^^++++++++++++++++++++ // ++++++++++++++++++++ DO NOT EDIT ABOVE ++++++++++++++++++++ @@ -1915,16 +1916,44 @@ int device_cmp(uint8_t a, uint8_t b) { // - char for unit (d for day, h for hour, m for minute) // - the hex color to be used to display the text // -uint32_t convert_seconds_to_dhm(uint32_t seconds, char *unit, uint8_t *color){ - static uint32_t conversions[3] = {24 * 3600, 3600, 60}; - static char units[3] = { 'd', 'h', 'm'}; // day, hour, minute - uint8_t color_text_8 = WebColor(COL_TEXT) & 0xFF; // color of text on 8 bits - uint8_t color_back_8 = WebColor(COL_BACKGROUND) & 0xFF; // color of background on 8 bits - uint8_t colors[3] = { (uint8_t) changeUIntScale(6, 0, 16, color_back_8, color_text_8), // 6/16 of text - (uint8_t) changeUIntScale(10, 0, 16, color_back_8, color_text_8), // 10/16 of text color - color_text_8}; - for(int i = 0; i < 3; ++i) { - *color = colors[i]; +uint32_t convert_seconds_to_dhm(uint32_t seconds, char *unit, uint32_t *color, bool days = false){ + static const uint32_t conversions[3] = {24 * 3600, 3600, 60}; + static const char units[3] = { 'd', 'h', 'm'}; // day, hour, minute + static const uint32_t color_threshold_hours[2] = {24 * 3600, 3600}; // 0 - 1 hour - 1 day + static const uint32_t color_threshold_days[2] = {7 * 24 * 3600, 2 * 24 * 3600}; // 0 - 2 days - 7 days + + uint32_t color_text_8 = WebColor(COL_TEXT); // color of text on 8 bits + uint8_t color_text_8_r = (color_text_8 & 0xFF0000) >> 16; + uint8_t color_text_8_g = (color_text_8 & 0x00FF00) >> 8; + uint8_t color_text_8_b = (color_text_8 & 0x0000FF); + + uint32_t color_back_8 = WebColor(COL_BACKGROUND); // color of background on 8 bits + uint8_t color_back_8_r = (color_back_8 & 0xFF0000) >> 16; + uint8_t color_back_8_g = (color_back_8 & 0x00FF00) >> 8; + uint8_t color_back_8_b = (color_back_8 & 0x0000FF); + + int32_t colors[3] = { + ((changeUIntScale( 6, 0, 16, color_back_8_r, color_text_8_r) & 0xFF) << 16U) | // 6/16 of text + ((changeUIntScale( 6, 0, 16, color_back_8_g, color_text_8_g) & 0xFF) << 8U) | // 6/16 of text + ( changeUIntScale( 6, 0, 16, color_back_8_b, color_text_8_r) & 0xFF), // 6/16 of text + + ((changeUIntScale(10, 0, 16, color_back_8_r, color_text_8_r) & 0xFF) << 16U) | // 10/16 of text + ((changeUIntScale(10, 0, 16, color_back_8_g, color_text_8_g) & 0xFF) << 8U) | // 10/16 of text + ( changeUIntScale(10, 0, 16, color_back_8_b, color_text_8_r) & 0xFF), // 10/16 of text + + (color_text_8_r << 16U) | + (color_text_8_g << 8U) | + (color_text_8_b) + }; + + *color = (uint32_t)colors[2]; + for (uint32_t i = 0; i < 2; i++) { + if (seconds > (days ? color_threshold_days[i] : color_threshold_hours[i])) { + *color = (uint32_t)colors[i]; + break; + } + } + for(uint32_t i = 0; i < 3; ++i) { *unit = units[i]; if (seconds > conversions[i]) { // always pass even if 00m return seconds / conversions[i]; @@ -1996,14 +2025,25 @@ void ZigbeeShow(bool json) } char sbatt[64]; + char dhm[48]; snprintf_P(sbatt, sizeof(sbatt), PSTR(" ")); if (device.validBatteryPercent()) { + char unit; + uint32_t color = WebColor(COL_TEXT); // color of text + dhm[0] = 0; // start with empty string + if (device.validBattLastSeen()) { + uint16_t val = convert_seconds_to_dhm(now - device.batt_last_seen, &unit, &color, true); + if (val < 100) { + snprintf_P(dhm, sizeof(dhm), PSTR(" (%02d%c)"), val, unit); + } + } snprintf_P(sbatt, sizeof(sbatt), msg[ZB_WEB_BATTERY], - device.batterypercent, changeUIntScale(device.batterypercent, 0, 100, 0, 14) + device.batterypercent, dhm, + changeUIntScale(device.batterypercent, 0, 100, 0, 14), + (color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) ); } - uint32_t num_bars = 0; char slqi[4]; @@ -2025,15 +2065,15 @@ void ZigbeeShow(bool json) WSContentSend_PD(PSTR(""), j, (num_bars < j) ? PSTR(" o30") : PSTR("")); } } - char dhm[48]; snprintf_P(dhm, sizeof(dhm), PSTR(" ")); if (device.validLastSeen()) { char unit; - uint8_t color; + uint32_t color; uint16_t val = convert_seconds_to_dhm(now - device.last_seen, &unit, &color); if (val < 100) { - snprintf_P(dhm, sizeof(dhm), msg[ZB_WEB_LAST_SEEN], - color, color, color, val, unit); + snprintf_P(dhm, sizeof(dhm), msg[ZB_WEB_LAST_SEEN], + (color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF), + val, unit); } } From f637dd14aad00231958fb9c211cc38b277d816a9 Mon Sep 17 00:00:00 2001 From: stefanbode Date: Wed, 3 Aug 2022 19:03:42 +0200 Subject: [PATCH 173/219] fix #15851 buffer overflow --- tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index 8a85c0c2d..1cc517940 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -1767,7 +1767,7 @@ bool Xdrv27(uint8_t function) } break; case FUNC_BUTTON_PRESSED: - if (Settings->shutter_button[XdrvMailbox.index] & (1<<31)) { + if (XdrvMailbox.index < MAX_SHUTTER_KEYS && Settings->shutter_button[XdrvMailbox.index] & (1<<31)) { ShutterButtonHandler(); result = true; } From 2d1121931b3d74cd740d9cb9b538268c06bb321c Mon Sep 17 00:00:00 2001 From: HardwareBoffin <110466589+HardwareBoffin@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:26:42 +0200 Subject: [PATCH 174/219] VSCode didn't push the git changes.... --- README.md | 5 ++--- platformio_tasmota_env.ini | 3 +++ platformio_tasmota_env32.ini | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7160b7a40..20166b412 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -This is a FORK providing ( in a few days I hope ) the translation file for the Catalan language. - +![Tasmota logo](/tools/logo/TASMOTA_FullLogo_Vector.svg) Alternative firmware for [ESP8266](https://en.wikipedia.org/wiki/ESP8266) and [ESP32](https://en.wikipedia.org/wiki/ESP32) based devices with **easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX**. _Written for PlatformIO._ @@ -169,4 +168,4 @@ People helping to keep the show on the road: ## License -This program is licensed under GPL-3.0 +This program is licensed under GPL-3.0 \ No newline at end of file diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index e84fe8d25..46f6412a8 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -83,6 +83,9 @@ build_flags = ${env.build_flags} -DMY_LANGUAGE=pt_BR [env:tasmota-CN] build_flags = ${env.build_flags} -DMY_LANGUAGE=zh_CN +[env:tasmota-CA] +build_flags = ${env.build_flags} -DMY_LANGUAGE=ca_AD + [env:tasmota-CZ] build_flags = ${env.build_flags} -DMY_LANGUAGE=cs_CZ diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 51e42d0d6..f5f30922f 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -254,6 +254,10 @@ build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=pt_BR extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=zh_CN -DFIRMWARE_TASMOTA32 +[env:tasmota32-CA] +extends = env:tasmota32_base +build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=ca_AD -DFIRMWARE_TASMOTA32 + [env:tasmota32-CZ] extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=cs_CZ -DFIRMWARE_TASMOTA32 From bb530d539634d7b3596c0193dc3b404eeec0c275 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:35:07 +0200 Subject: [PATCH 175/219] add CA --- .github/workflows/Tasmota_build_devel.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Tasmota_build_devel.yml b/.github/workflows/Tasmota_build_devel.yml index d349b30f0..184cca854 100644 --- a/.github/workflows/Tasmota_build_devel.yml +++ b/.github/workflows/Tasmota_build_devel.yml @@ -77,7 +77,7 @@ jobs: strategy: matrix: variant: [ tasmota, tasmota32 ] - language: [ AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v3 with: From d189b5d8a54721a61e989040f966bcda6f3f687b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:35:42 +0200 Subject: [PATCH 176/219] add CA --- .github/workflows/Tasmota_build_master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml index 6ef80ad29..26e0089c1 100644 --- a/.github/workflows/Tasmota_build_master.yml +++ b/.github/workflows/Tasmota_build_master.yml @@ -76,7 +76,7 @@ jobs: strategy: matrix: variant: [ tasmota, tasmota32 ] - language: [ AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v3 with: From 893914849026f60ab58aa63b2ec694361655a92b Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:36:22 +0200 Subject: [PATCH 177/219] add CA --- .github/workflows/build_all_the_things.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_all_the_things.yml b/.github/workflows/build_all_the_things.yml index 1d52a1375..aa7f5e7c2 100644 --- a/.github/workflows/build_all_the_things.yml +++ b/.github/workflows/build_all_the_things.yml @@ -80,7 +80,7 @@ jobs: strategy: matrix: variant: [ tasmota ] - language: [ AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v2 - name: Set up Python From d482f4900228f02f74afd9be6426efde79d0d856 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 4 Aug 2022 11:15:53 +0200 Subject: [PATCH 178/219] Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) --- CHANGELOG.md | 1 + tasmota/include/tasmota_types.h | 2 +- tasmota/my_user_config.h | 2 + .../xdrv_23_zigbee_2_devices.ino | 5 ++- .../xdrv_23_zigbee_2a_devices_impl.ino | 7 ++++ .../xdrv_23_zigbee_6_commands.ino | 5 +++ .../xdrv_23_zigbee_8_parsers.ino | 40 ++++++++++++++++++- .../xdrv_23_zigbee_A_impl.ino | 2 +- 8 files changed, 59 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3428a49b..7c1e62f8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) - Support for multiple `IRsend` GPIOs - Zigbee added recording of when the battery was last reported +- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index e95aa8736..020203940 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -170,7 +170,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t mqtt_persistent : 1; // bit 26 (v11.1.0.1) - SetOption140 - (MQTT) MQTT clean session (0 = default) or persistent session (1) uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1) uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) - uint32_t spare29 : 1; // bit 29 + uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding uint32_t spare30 : 1; // bit 30 uint32_t spare31 : 1; // bit 31 }; diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index eeaa50aba..39d9875c4 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -898,6 +898,8 @@ #define USE_ZIGBEE_DEBOUNCE_COMMANDS 200 // if commands are received from the same device/endpoint with same ZCL transaction number, discard packet in this time window (ms) #define USE_ZIGBEE_MODELID "Tasmota Z2T" // reported "ModelId" (cluster 0000 / attribute 0005) #define USE_ZIGBEE_MANUFACTURER "Tasmota" // reported "Manufacturer" (cluster 0000 / attribute 0004) + #define USE_ZIGBEE_BATT_REPROBE (24*3600) // Period in seconds during which we don't ask again for battery, default 1 day + #define USE_ZIGBEE_BATT_REPROBE_PAUSE (3600) // Min wait period when sending an autoprobe, default: wait at least 1 hour #define USE_ZBBRIDGE_TLS // TLS support for zbbridge #define USE_ZIGBEE_ZBBRIDGE_EEPROM 0x50 // I2C id for the ZBBridge EEPROM // #define USE_ZIGBEE_FORCE_NO_CHILDREN // This feature forces `CONFIG_MAX_END_DEVICE_CHILDREN` to zero which means that the coordinator does not accept any direct child. End-devices must pair through a router. diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino index f5830f2ea..441cc9165 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino @@ -801,7 +801,7 @@ public: } } inline void setHasNoBattery(void) { batt_last_seen = 0xFFFFFFFF; } - inline bool hasNoBattery(void) const { return 0xFFFFFFFF != batt_last_seen; } + inline bool hasNoBattery(void) const { return 0xFFFFFFFF == batt_last_seen; } // Add an endpoint to a device bool addEndpoint(uint8_t endpoint); @@ -959,6 +959,9 @@ public: // device is reachable void deviceWasReached(uint16_t shortaddr); + // device has no battery + void deviceHasNoBattery(uint16_t shortaddr); + // Timers void resetTimersForDevice(uint16_t shortaddr, uint16_t groupaddr, uint8_t category, uint16_t cluster = 0xFFFF, uint8_t endpoint = 0xFF); void setTimer(uint16_t shortaddr, uint16_t groupaddr, uint32_t wait_ms, uint16_t cluster, uint8_t endpoint, uint8_t category, uint32_t value, Z_DeviceTimer func); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino index a8f077e72..7a4989dff 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino @@ -338,6 +338,13 @@ void Z_Devices::deviceWasReached(uint16_t shortaddr) { } } +void Z_Devices::deviceHasNoBattery(uint16_t shortaddr) { + Z_Device & device = findShortAddr(shortaddr); + if (device.valid()) { + device.setHasNoBattery(); // mark device as reachable + } +} + // get the next sequance number for the device, or use the global seq number if device is unknown uint8_t Z_Devices::getNextSeqNumber(uint16_t shortaddr) { Z_Device & device = findShortAddr(shortaddr); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino index 3921a9aef..5c4ad28f8 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino @@ -150,6 +150,7 @@ const Z_CommandConverter Z_Commands[] PROGMEM = { #define ZLE(x) ((x) & 0xFF), ((x) >> 8) // Little Endian // Below are the attributes we wand to read from each cluster +const uint8_t CLUSTER_0001[] = { ZLE(0x0020), ZLE(0x0021) }; // BatteryVoltage, BatteryPercentage const uint8_t CLUSTER_0006[] = { ZLE(0x0000) }; // Power const uint8_t CLUSTER_0008[] = { ZLE(0x0000) }; // CurrentLevel const uint8_t CLUSTER_0009[] = { ZLE(0x0000) }; // AlarmCount @@ -161,6 +162,10 @@ void Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster const uint8_t* attrs = nullptr; switch (cluster) { + case 0x0001: + attrs = CLUSTER_0001; + attrs_len = sizeof(CLUSTER_0001); + break; case 0x0006: // for On/Off attrs = CLUSTER_0006; attrs_len = sizeof(CLUSTER_0006); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino index 382cf1377..a4c47f42c 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino @@ -727,7 +727,8 @@ void Z_AutoBindDefer(uint16_t shortaddr, uint8_t endpoint, const SBuffer &buf, for (uint32_t i=0; iflag5.zigbee_no_batt_autoprobe) || !Z_BatteryReportingDeviceSpecific(shortaddr)) { continue; } zigbee_devices.queueTimer(shortaddr, 0 /* groupaddr */, 2000, cluster, endpoint, Z_CAT_CONFIG_ATTR, 0 /* value */, &Z_AutoConfigReportingForCluster); } } @@ -951,11 +952,16 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const SBuffer &buf) { // device is reachable zigbee_devices.deviceWasReached(nwkAddr); + bool power_source = (capabilities & 0x04); + if (power_source) { + zigbee_devices.deviceHasNoBattery(nwkAddr); + } + Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{" "\"Status\":%d,\"IEEEAddr\":\"0x%_X\",\"ShortAddr\":\"0x%04X\"" ",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"), ZIGBEE_STATUS_DEVICE_ANNOUNCE, &ieeeAddr, nwkAddr, - (capabilities & 0x04) ? PSTR("true") : PSTR("false"), + power_source ? PSTR("true") : PSTR("false"), (capabilities & 0x08) ? PSTR("true") : PSTR("false"), (capabilities & 0x40) ? PSTR("true") : PSTR("false") ); @@ -1673,6 +1679,12 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { // Build the ZbReceive list if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId() || ZCL_WRITE_ATTRIBUTES == zcl_received.getCmdId())) { zcl_received.parseReportAttributes(attr_list); // Zigbee report attributes from sensors + + // since we receive a sensor value, and the device is still awake, + // try to read the battery value + if (clusterid != 0x0001) { // avoid sending Battery probe if we already received info from cluster 0x0001 + Z_Query_Battery(srcaddr); + } if (clusterid && (ZCL_REPORT_ATTRIBUTES == zcl_received.getCmdId())) { defer_attributes = true; } // don't defer system Cluster=0 messages or Write Attribute } else if ( (!zcl_received.isClusterSpecificCommand()) && (ZCL_READ_ATTRIBUTES_RESPONSE == zcl_received.getCmdId())) { zcl_received.parseReadAttributesResponse(attr_list); @@ -2071,6 +2083,30 @@ void Z_Query_Bulb(uint16_t shortaddr, uint32_t &wait_ms) { } } +// +// Query the status of the battery (auto-probe) +// +void Z_Query_Battery(uint16_t shortaddr) { + if (Settings->flag5.zigbee_no_batt_autoprobe) { return; } // don't do auto-probe if `SetOption143 1` + if (0 == shortaddr) { return; } + Z_Device & device = zigbee_devices.findShortAddr(shortaddr); + if (device.valid()) { + uint32_t now = Rtc.utc_time; + if (now < START_VALID_TIME) { return; } // internal time is not valid + if (device.hasNoBattery()) { return; } // device is known to have no battery + if (device.batt_last_seen + USE_ZIGBEE_BATT_REPROBE > now) { return; } // battery status is fresh enough + if (device.batt_last_probed + USE_ZIGBEE_BATT_REPROBE_PAUSE > now) { return; } // battery has been probed soon enough + + uint8_t endpoint = zigbee_devices.findFirstEndpoint(shortaddr); + if (endpoint) { // send only if we know the endpoint + device.batt_last_probed = now; // we are probing now + zigbee_devices.setTimer(shortaddr, 0 /* groupaddr */, 0 /* now */, 0x0001, endpoint, Z_CAT_READ_CLUSTER, 0 /* value */, &Z_ReadAttrCallback); + AddLog(LOG_LEVEL_INFO, PSTR("ZIG: Battery auto-probe " + "`ZbSend {\"Device\":\"0x%04X\",\"Read\":{\"BatteryPercentage\":true,\"BatteryVoltage\":true}}`"), shortaddr); + } + } +} + // // Send messages to query the state of each Hue emulated light // diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino index 9679fa44e..9f1fdd0a4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -2024,7 +2024,7 @@ void ZigbeeShow(bool json) name = sdevice; } - char sbatt[64]; + char sbatt[96]; char dhm[48]; snprintf_P(sbatt, sizeof(sbatt), PSTR(" ")); if (device.validBatteryPercent()) { From d112800dc3a50e1dee22669d3f8ba962f9a9730f Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Thu, 4 Aug 2022 18:33:31 +0200 Subject: [PATCH 179/219] Zigbee add received commands as battery auto-probe triggers --- tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino | 1 + 1 file changed, 1 insertion(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino index a4c47f42c..fb7f8cdb7 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino @@ -1700,6 +1700,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { zcl_received.parseWriteAttributesResponse(attr_list); } else if (zcl_received.isClusterSpecificCommand()) { zcl_received.parseClusterSpecificCommand(attr_list); + Z_Query_Battery(srcaddr); // do battery auto-probing when receiving commands } AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str()); From 299cc5a87b89dda269df10439a881b0dce6b1cb3 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Fri, 5 Aug 2022 10:11:56 +0200 Subject: [PATCH 180/219] Fix PWM for ESP32 variants --- lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp index e4703a435..419442290 100644 --- a/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp +++ b/lib/libesp32/ESP32-to-ESP8266-compat/src/esp8266toEsp32.cpp @@ -37,7 +37,9 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D // define our limits to ease any change from esp-idf #define MAX_TIMERS LEDC_TIMER_MAX // 4 timers for all ESP32 variants -#define HAS_HIGHSPEED SOC_LEDC_SUPPORT_HS_MODE // are there 2 banks of timers/ledc +#ifdef SOC_LEDC_SUPPORT_HS_MODE + #define PWM_HAS_HIGHSPEED SOC_LEDC_SUPPORT_HS_MODE // are there 2 banks of timers/ledc +#endif // replicated from `tasmota.h` @@ -89,7 +91,7 @@ void _analog_applyTimerConfig(int32_t timer) { if (ret != ESP_OK) { AddLog(LOG_LEVEL_ERROR, "PWM: ledc_timer_config %i failed ret=%i", timer, ret); } -#ifdef HAS_HIGHSPEED +#ifdef PWM_HAS_HIGHSPEED // apply the same parameter to the low-speed timer as well cfg.speed_mode = (ledc_mode_t) 1; // first bank ret = ledc_timer_config(&cfg); From a4d7148a5220e488efec14fe7a017ed39eaa1023 Mon Sep 17 00:00:00 2001 From: Pascal Gollor Date: Fri, 5 Aug 2022 14:21:11 +0200 Subject: [PATCH 181/219] add option to change host url for OTA upload --- tools/espupload.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tools/espupload.py b/tools/espupload.py index 0b2766cd0..625e20229 100644 --- a/tools/espupload.py +++ b/tools/espupload.py @@ -19,8 +19,8 @@ HOST_ADDR = "domus1" HOST_PORT = 80 HOST_URL = "/api/upload-arduino.php" -def upload(hostAddr, hostPort, filename): - url = 'http://%s:%d%s' % (hostAddr, hostPort, HOST_URL) +def upload(hostAddr, hostPort, hostUrl, filename): + url = 'http://%s:%d%s' % (hostAddr, hostPort, hostUrl) c = pycurl.Curl() c.setopt(c.URL, url) # The "Expect:" is there to suppress "Expect: 100-continue" behaviour that is @@ -36,20 +36,26 @@ def parser(): description = "Upload image to over the air Host server for the esp8266 module with OTA support." ) - # destination ip and port + # destination ip, port and url group = optparse.OptionGroup(parser, "Destination") group.add_option("-i", "--host_ip", dest = "host_ip", action = "store", - help = "Host IP Address.", + help = "Host IP Address. Default: " + HOST_ADDR, default = HOST_ADDR ) group.add_option("-p", "--host_port", dest = "host_port", type = "int", - help = "Host server ota Port. Default 80", + help = "Host server ota Port. Default: " + str(HOST_PORT), default = HOST_PORT ) + group.add_option("-u", "--host_url", + dest = "host_url", + action = "store", + help = "Host Url with / at beginning. Default: '" + HOST_URL + "'", + default = HOST_URL + ) parser.add_option_group(group) # image @@ -104,9 +110,9 @@ def main(args): return 1 # end if - upload(options.host_ip, options.host_port, options.image) + upload(options.host_ip, options.host_port, options.host_url, options.image) # end main if __name__ == '__main__': sys.exit(main(sys.argv)) -# end if \ No newline at end of file +# end if From 02d7eb1d7d664379894d0690c11aac9c1a46bc19 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:25:39 +0200 Subject: [PATCH 182/219] Fix energy values after power cycle Fix energy values after power cycle (#16118) --- tasmota/include/tasmota_types.h | 6 ++-- tasmota/tasmota.ino | 4 +-- tasmota/tasmota_support/settings.ino | 29 ++++++++++++---- tasmota/tasmota_support/support.ino | 4 +-- .../tasmota_xdrv_driver/xdrv_03_energy.ino | 34 ++++++------------- .../tasmota_xnrg_energy/xnrg_15_teleinfo.ino | 1 - 6 files changed, 40 insertions(+), 38 deletions(-) diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index 020203940..fc54a728d 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -550,8 +550,8 @@ typedef struct { uint32_t energy_power_calibration; // 364 uint32_t energy_voltage_calibration; // 368 uint32_t energy_current_calibration; // 36C - uint32_t energy_kWhtoday; // 370 - uint32_t energy_kWhyesterday; // 374 + uint32_t ex_energy_kWhtoday; // 370 + uint32_t ex_energy_kWhyesterday; // 374 uint16_t energy_kWhdoy; // 378 uint16_t energy_min_power; // 37A uint16_t energy_max_power; // 37C @@ -843,7 +843,7 @@ typedef struct { uint16_t flowratemeter_calibration[2];// F78 int32_t energy_kWhexport_ph[3]; // F7C uint32_t eth_ipv4_address[5]; // F88 - uint32_t energy_kWhtotal; // F9C + uint32_t ex_energy_kWhtotal; // F9C SBitfield1 sbflag1; // FA0 TeleinfoCfg teleinfo; // FA4 uint64_t rf_protocol_mask; // FA8 diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 4e72a5b0c..64edd25f5 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -110,8 +110,8 @@ typedef struct { uint16_t valid; // 290 (RTC memory offset 100) uint8_t oswatch_blocked_loop; // 292 uint8_t ota_loader; // 293 - uint32_t energy_kWhtoday; // 294 - uint32_t energy_kWhtotal; // 298 + uint32_t ex_energy_kWhtoday; // 294 + uint32_t ex_energy_kWhtotal; // 298 volatile uint32_t pulse_counter[MAX_COUNTERS]; // 29C - See #9521 why volatile power_t power; // 2AC EnergyUsage energy_usage; // 2B0 diff --git a/tasmota/tasmota_support/settings.ino b/tasmota/tasmota_support/settings.ino index 90cd2f7bd..460fc6a42 100644 --- a/tasmota/tasmota_support/settings.ino +++ b/tasmota/tasmota_support/settings.ino @@ -45,8 +45,8 @@ void RtcSettingsSave(void) { if (RTC_MEM_VALID != RtcSettings.valid) { memset(&RtcSettings, 0, sizeof(RtcSettings)); RtcSettings.valid = RTC_MEM_VALID; - RtcSettings.energy_kWhtoday = Settings->energy_kWhtoday; - RtcSettings.energy_kWhtotal = Settings->energy_kWhtotal; +// RtcSettings.ex_energy_kWhtoday = Settings->ex_energy_kWhtoday; +// RtcSettings.ex_energy_kWhtotal = Settings->ex_energy_kWhtotal; for (uint32_t i = 0; i < 3; i++) { RtcSettings.energy_kWhtoday_ph[i] = Settings->energy_kWhtoday_ph[i]; RtcSettings.energy_kWhtotal_ph[i] = Settings->energy_kWhtotal_ph[i]; @@ -61,6 +61,8 @@ void RtcSettingsSave(void) { RtcSettings.baudrate = APP_BAUDRATE; } +// AddLog(LOG_LEVEL_INFO, PSTR("DBG: energy_kWhtoday_ph[0] %d/%d"), RtcSettings.energy_kWhtoday_ph[0], Settings->energy_kWhtoday_ph[0]); + #ifdef ESP8266 ESP.rtcUserMemoryWrite(100, (uint32_t*)&RtcSettings, sizeof(RtcSettings)); #endif // ESP8266 @@ -1037,8 +1039,12 @@ void SettingsDefaultSet2(void) { Settings->energy_power_calibration = HLW_PREF_PULSE; Settings->energy_voltage_calibration = HLW_UREF_PULSE; Settings->energy_current_calibration = HLW_IREF_PULSE; -// Settings->energy_kWhtoday = 0; -// Settings->energy_kWhyesterday = 0; +// Settings->energy_kWhtoday_ph[0] = 0; +// Settings->energy_kWhtoday_ph[1] = 0; +// Settings->energy_kWhtoday_ph[2] = 0; +// Settings->energy_kWhyesterday_ph[0] = 0; +// Settings->energy_kWhyesterday_ph[1] = 0; +// Settings->energy_kWhyesterday_ph[2] = 0; // Settings->energy_kWhdoy = 0; // Settings->energy_min_power = 0; // Settings->energy_max_power = 0; @@ -1054,8 +1060,12 @@ void SettingsDefaultSet2(void) { Settings->energy_max_power_safe_limit_window = SAFE_POWER_WINDOW; // Settings->energy_max_energy = 0; // MaxEnergy // Settings->energy_max_energy_start = 0; // MaxEnergyStart -// Settings->energy_kWhtotal = 0; - RtcSettings.energy_kWhtotal = 0; +// Settings->energy_kWhtotal_ph[0] = 0; +// Settings->energy_kWhtotal_ph[1] = 0; +// Settings->energy_kWhtotal_ph[2] = 0; + RtcSettings.energy_kWhtotal_ph[0] = 0; + RtcSettings.energy_kWhtotal_ph[1] = 0; + RtcSettings.energy_kWhtotal_ph[2] = 0; // memset((char*)&Settings->energy_usage, 0x00, sizeof(Settings->energy_usage)); memset((char*)&RtcSettings.energy_usage, 0x00, sizeof(RtcSettings.energy_usage)); Settings->param[P_OVER_TEMP] = ENERGY_OVERTEMP; @@ -1482,7 +1492,7 @@ void SettingsDelta(void) { memset(&Settings->sensors, 0xFF, 16); // Enable all possible sensors } if (Settings->version < 0x09050004) { - Settings->energy_kWhtotal = Settings->ipv4_address[4]; + Settings->ex_energy_kWhtotal = Settings->ipv4_address[4]; ParseIPv4(&Settings->ipv4_address[4], PSTR(WIFI_DNS2)); } if (Settings->version < 0x09050005) { @@ -1507,6 +1517,11 @@ void SettingsDelta(void) { if (Settings->version < 0x09050009) { // 9.5.0.9 memset(&Settings->energy_kWhtoday_ph, 0, 36); memset(&RtcSettings.energy_kWhtoday_ph, 0, 24); + Settings->energy_kWhtotal_ph[0] = Settings->ex_energy_kWhtotal; + Settings->energy_kWhtoday_ph[0] = Settings->ex_energy_kWhtoday; + Settings->energy_kWhyesterday_ph[0] = Settings->ex_energy_kWhyesterday; + RtcSettings.energy_kWhtoday_ph[0] = RtcSettings.ex_energy_kWhtoday; + RtcSettings.energy_kWhtotal_ph[0] = RtcSettings.ex_energy_kWhtotal; } if (Settings->version < 0x0A000003) { // 10.0.0.3 if (0 == Settings->param[P_ARP_GRATUITOUS]) { diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino index 27c9007f3..451448141 100755 --- a/tasmota/tasmota_support/support.ino +++ b/tasmota/tasmota_support/support.ino @@ -2473,7 +2473,7 @@ void SyslogAsync(bool refresh) { if (!WifiHostByName(SettingsText(SET_SYSLOG_HOST), temp_syslog_host_addr)) { // If sleep enabled this might result in exception so try to do it once using hash TasmotaGlobal.syslog_level = 0; TasmotaGlobal.syslog_timer = SYSLOG_TIMER; - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); + AddLog(LOG_LEVEL_INFO, PSTR("SLG: " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); return; } syslog_host_hash = current_hash; @@ -2482,7 +2482,7 @@ void SyslogAsync(bool refresh) { if (!PortUdp.beginPacket(syslog_host_addr, Settings->syslog_port)) { TasmotaGlobal.syslog_level = 0; TasmotaGlobal.syslog_timer = SYSLOG_TIMER; - AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); + AddLog(LOG_LEVEL_INFO, PSTR("SLG: " D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER); return; } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino index a9880ef6d..862ebca7c 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_03_energy.ino @@ -1040,7 +1040,7 @@ void EnergyDrvInit(void) { #endif // USE_ENERGY_MARGIN_DETECTION TasmotaGlobal.energy_driver = ENERGY_NONE; - XnrgCall(FUNC_PRE_INIT); // Find first energy driver + XnrgCall(FUNC_PRE_INIT); // Find first energy driver } void EnergySnsInit(void) @@ -1048,31 +1048,19 @@ void EnergySnsInit(void) XnrgCall(FUNC_INIT); if (TasmotaGlobal.energy_driver) { - - // Update for split phase totals (v9.5.0.9) - if ((Settings->energy_kWhtotal > 0) && (0 == Settings->energy_kWhtotal_ph[0])) { - Settings->energy_kWhtotal_ph[0] = Settings->energy_kWhtotal; - Settings->energy_kWhtoday_ph[0] = Settings->energy_kWhtoday; - Settings->energy_kWhyesterday_ph[0] = Settings->energy_kWhyesterday; - RtcSettings.energy_kWhtoday_ph[0] = RtcSettings.energy_kWhtoday; - RtcSettings.energy_kWhtotal_ph[0] = RtcSettings.energy_kWhtotal; - Settings->energy_kWhtotal = 0; - } - -// Energy.kWhtoday_offset = 0; - // Do not use at Power On as Rtc was invalid (but has been restored from Settings already) - if ((ResetReason() != REASON_DEFAULT_RST) && RtcSettingsValid()) { - for (uint32_t i = 0; i < 3; i++) { - Energy.kWhtoday_offset[i] = RtcSettings.energy_kWhtoday_ph[i]; - } - Energy.kWhtoday_offset_init = true; - } for (uint32_t i = 0; i < 3; i++) { -// Energy.kWhtoday_ph[i] = 0; -// Energy.kWhtoday_delta[i] = 0; +// Energy.kWhtoday_offset[i] = 0; // Reset by EnergyDrvInit() + // 20220805 - Change from https://github.com/arendst/Tasmota/issues/16118 + if (RtcSettingsValid()) { + Energy.kWhtoday_offset[i] = RtcSettings.energy_kWhtoday_ph[i]; + RtcSettings.energy_kWhtoday_ph[i] = 0; + Energy.kWhtoday_offset_init = true; + } +// Energy.kWhtoday_ph[i] = 0; // Reset by EnergyDrvInit() +// Energy.kWhtoday_delta[i] = 0; // Reset by EnergyDrvInit() Energy.period[i] = Energy.kWhtoday_offset[i]; if (Energy.local_energy_active_export) { - Energy.export_active[i] = 0; + Energy.export_active[i] = 0; // Was set to NAN by EnergyDrvInit() } } EnergyUpdateToday(); diff --git a/tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino b/tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino index 733d1b091..86d74d9d4 100644 --- a/tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino +++ b/tasmota/tasmota_xnrg_energy/xnrg_15_teleinfo.ino @@ -570,7 +570,6 @@ void TInfoDrvInit(void) { Energy.phase_count = 1; // init hardware energy counters Settings->flag3.hardware_energy_total = true; - Settings->energy_kWhtotal = 0; } } From 202ec86453024e17ae5b26704ba253068721515f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 5 Aug 2022 15:52:53 +0200 Subject: [PATCH 183/219] Update changelogs --- CHANGELOG.md | 3 ++- RELEASENOTES.md | 4 ++++ tools/decode-status.py | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c1e62f8f..58350e672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ All notable changes to this project will be documented in this file. ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) -- Support for multiple `IRsend` GPIOs +- Support for multiple ``IRsend`` GPIOs - Zigbee added recording of when the battery was last reported - Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - ESP32 LVGL library from v8.2.0 to v8.3.0 ### Fixed +- Restore EnergyToday after using command ``restart 2`` and power cycle (#16118) ### Removed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 812044d7b..b54e3fb5d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -118,6 +118,9 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` [#16013](https://github.com/arendst/Tasmota/issues/16013) +- Support for multiple ``IRsend`` GPIOs +- Zigbee added recording of when the battery was last reported +- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) - ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars [#15916](https://github.com/arendst/Tasmota/issues/15916) ### Breaking Changed @@ -128,6 +131,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Fixed +- Restore EnergyToday after using command ``restart 2`` and power cycle [#16118](https://github.com/arendst/Tasmota/issues/16118) - ESP32 SendMail not working over ethernet [#15794](https://github.com/arendst/Tasmota/issues/15794) ### Removed diff --git a/tools/decode-status.py b/tools/decode-status.py index b9ad7287e..11e72981c 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -197,7 +197,8 @@ a_setoption = [[ "(MQTT) MQTT clean session (0 = default) or persistent session (1)", "(GUI) Disable display of GUI module name (1)", "(Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1)", - "","","" + "(Zigbee) Disable Battery auto-probe and using auto-binding", + "","" ],[ "","","","", "","","","", From b15e8041dd65a28cff8b3717febcd99c21491ee8 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Sat, 6 Aug 2022 10:12:52 +0200 Subject: [PATCH 184/219] rm deprecated colorizer --- .gitpod.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 170b688e0..0425a416b 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -7,6 +7,5 @@ image: vscode: extensions: - Atishay-Jain.All-Autocomplete - - CoenraadS.bracket-pair-colorizer - esbenp.prettier-vscode - shardulm94.trailing-spaces From cf20a9c1c82ec5a8dfd1f46abfa6d685e2b60ba5 Mon Sep 17 00:00:00 2001 From: JeroenSt Date: Sat, 6 Aug 2022 11:55:56 +0200 Subject: [PATCH 185/219] Solved crash when modbusbridge was configured for the first time --- tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino index 1a58736a2..b5127deb2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_63_modbus_bridge.ino @@ -154,7 +154,7 @@ ModbusBridge modbusBridge; // bool ModbusBridgeBegin(void) { - if ((Settings->modbus_sbaudrate < 300 / 300) || (Settings->modbus_sbaudrate > 115200 / 300)) Settings->modbus_sbaudrate = (uint8_t)MBR_BAUDRATE / 300; + if ((Settings->modbus_sbaudrate < 300 / 300) || (Settings->modbus_sbaudrate > 115200 / 300)) Settings->modbus_sbaudrate = (uint8_t)((uint32_t)MBR_BAUDRATE / 300); if (Settings->modbus_sconfig > TS_SERIAL_8O2) Settings->modbus_sconfig = TS_SERIAL_8N1; int result = tasmotaModbus->Begin(Settings->modbus_sbaudrate * 300, ConvertSerialConfig(Settings->modbus_sconfig)); // Reinitialize modbus port with new baud rate From f18091c53b6a48b67eb26fe04d124972badaddbb Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Sat, 6 Aug 2022 14:27:58 +0200 Subject: [PATCH 186/219] Zigbee include "BatteryPercentage" in all messages --- CHANGELOG.md | 1 + tasmota/include/i18n.h | 1 + .../xdrv_23_zigbee_1z_libs.ino | 28 +++++++++++++------ .../xdrv_23_zigbee_2_devices.ino | 25 ++++++++++------- .../xdrv_23_zigbee_2a_devices_impl.ino | 15 ++++++---- .../xdrv_23_zigbee_4b_data.ino | 17 +++++++++-- .../xdrv_23_zigbee_8_parsers.ino | 2 +- .../xdrv_23_zigbee_A_impl.ino | 4 +-- .../xdrv_52_3_berry_zigbee.ino | 2 +- 9 files changed, 66 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58350e672..827b7bc0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Support for multiple ``IRsend`` GPIOs - Zigbee added recording of when the battery was last reported - Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) +- Zigbee include "BatteryPercentage" in all messages ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index bb8e27f3d..54711b451 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -628,6 +628,7 @@ #define D_CMND_ZIGBEE_SAVE "Save" #define D_CMND_ZIGBEE_LINKQUALITY "LinkQuality" #define D_CMND_ZIGBEE_CLUSTER "Cluster" + #define D_CMND_ZIGBEE_BATTPERCENT "BatteryPercentage" #define D_CMND_ZIGBEE_ENDPOINT "Endpoint" #define D_CMND_ZIGBEE_GROUP "Group" #define D_CMND_ZIGBEE_MANUF "Manuf" diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino index 98a9f3a7d..4cf5fae4e 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1z_libs.ino @@ -231,6 +231,7 @@ public: uint8_t src_ep; // source endpoint, 0xFF if unknown uint8_t lqi; // linkquality, 0xFF if unknown uint16_t group_id; // group address OxFFFF if inknown + uint8_t batt_percent; // battery percentage Z_attribute_list(): LList(), // call superclass constructor @@ -248,11 +249,15 @@ public: src_ep = 0xFF; lqi = 0xFF; group_id = 0xFFFF; + batt_percent = 0xFF; } - inline bool isValidSrcEp(void) const { return 0xFF != src_ep; } - inline bool isValidLQI(void) const { return 0xFF != lqi; } - inline bool isValidGroupId(void) const { return 0xFFFF != group_id; } + inline bool validSrcEp(void) const { return 0xFF != src_ep; } + inline bool validLQI(void) const { return 0xFF != lqi; } + inline bool validGroupId(void) const { return 0xFFFF != group_id; } + inline bool validBattPercent(void) const { return 0xFF != batt_percent; } + + inline void setBattPercent(uint8_t batt) { batt_percent = batt; } // the following addAttribute() compute the suffix and increments it // Add attribute to the list, given cluster and attribute id @@ -273,7 +278,7 @@ public: // dump the entire structure as JSON, starting from head (as parameter) // does not start not end with a comma // do we enclosed in brackets '{' '}' - String toString(bool enclose_brackets = false) const; + String toString(bool enclose_brackets = false, bool include_battery = false) const; // find if attribute with same key already exists, return null if not found const Z_attribute * findAttribute(uint16_t cluster, uint16_t attr_id, uint8_t suffix = 0) const; @@ -765,7 +770,7 @@ Z_attribute & Z_attribute_list::addAttribute(const char * name, const char * nam return attr; } -String Z_attribute_list::toString(bool enclose_brackets) const { +String Z_attribute_list::toString(bool enclose_brackets, bool include_battery) const { String res = ""; if (enclose_brackets) { res += '{'; } bool prefix_comma = false; @@ -773,22 +778,29 @@ String Z_attribute_list::toString(bool enclose_brackets) const { res += attr.toString(prefix_comma); prefix_comma = true; } + // add battery percentage if available, and if BatteryPercentage is not already present + if (include_battery && validBattPercent() && countAttribute(PSTR(D_CMND_ZIGBEE_BATTPERCENT)) == 0) { + if (prefix_comma) { res += ','; } + prefix_comma = true; + res += F("\"" D_CMND_ZIGBEE_BATTPERCENT "\":"); + res += batt_percent; + } // add source endpoint - if (0xFF != src_ep) { + if (validSrcEp()) { if (prefix_comma) { res += ','; } prefix_comma = true; res += F("\"" D_CMND_ZIGBEE_ENDPOINT "\":"); res += src_ep; } // add group address - if (0xFFFF != group_id) { + if (validGroupId()) { if (prefix_comma) { res += ','; } prefix_comma = true; res += F("\"" D_CMND_ZIGBEE_GROUP "\":"); res += group_id; } // add lqi - if (0xFF != lqi) { + if (validLQI()) { if (prefix_comma) { res += ','; } prefix_comma = true; res += F("\"" D_CMND_ZIGBEE_LINKQUALITY "\":"); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino index 441cc9165..befa1e22f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino @@ -730,14 +730,14 @@ public: // Light information for Hue integration integration, last known values // New version of device data handling - Z_Data_Set data; // Linkedlist of device data per endpoint + Z_Data_Set data; // Linkedlist of device data per endpoint // other status - device wide data is 8 bytes // START OF DEVICE WIDE DATA - uint32_t last_seen; // Last seen time (epoch) - uint32_t batt_last_seen; // Time when we last received battery status (epoch), 0 means unknown, 0xFFFFFFFF means that the device has no battery - uint32_t batt_last_probed; // Time when the device was last probed for batteyr values - uint8_t lqi; // lqi from last message, 0xFF means unknown - uint8_t batterypercent; // battery percentage (0..100), 0xFF means unknwon + uint32_t last_seen; // Last seen time (epoch) + uint32_t batt_last_seen; // Time when we last received battery status (epoch), 0 means unknown, 0xFFFFFFFF means that the device has no battery + uint32_t batt_last_probed; // Time when the device was last probed for batteyr values + uint8_t lqi; // lqi from last message, 0xFF means unknown + uint8_t batt_percent; // battery percentage (0..100), 0xFF means unknwon uint16_t reserved_for_alignment; // Debounce informmation when receiving commands // If we receive the same ZCL transaction number from the same device and the same endpoint within 300ms @@ -765,7 +765,7 @@ public: batt_last_seen(0), batt_last_probed(0), lqi(0xFF), - batterypercent(0xFF), + batt_percent(0xFF), reserved_for_alignment(0xFFFF), debounce_endpoint(0), debounce_transact(0) @@ -781,7 +781,7 @@ public: inline bool validPower(uint8_t ep =0) const; inline bool validLqi(void) const { return 0xFF != lqi; } - inline bool validBatteryPercent(void) const { return 0xFF != batterypercent; } + inline bool validBatteryPercent(void) const { return 0xFF != batt_percent; } inline bool validLastSeen(void) const { return 0x0 != last_seen; } inline bool validBattLastSeen(void) const { return (0x0 != batt_last_seen) && (0xFFFFFFFF != batt_last_seen); } @@ -794,8 +794,13 @@ public: inline void setRouter(bool router) { is_router = router; } inline void setLQI(uint8_t _lqi) { lqi = _lqi; } - inline void setBatteryPercent(uint8_t bp) { - batterypercent = bp; + // set battery percentage to new value - and mark timestamp only if time is valid + // trigger an immediate `ZbSave` since it's important information to keep + void setBatteryPercent(uint8_t bp) { + if (batt_percent != bp) { + batt_percent = bp; + Z_Set_Save_Data_Timer_Once(0); + } if (Rtc.utc_time >= START_VALID_TIME) { batt_last_seen = Rtc.utc_time; } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino index 7a4989dff..2bb0a231a 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino @@ -491,12 +491,12 @@ bool Z_Devices::jsonIsConflict(uint16_t shortaddr, const Z_attribute_list &attr_ } // compare groups - if (device.attr_list.isValidGroupId() && attr_list.isValidGroupId()) { + if (device.attr_list.validGroupId() && attr_list.validGroupId()) { if (device.attr_list.group_id != attr_list.group_id) { return true; } // groups are in conflict } // compare src_ep - if (device.attr_list.isValidSrcEp() && attr_list.isValidSrcEp()) { + if (device.attr_list.validSrcEp() && attr_list.validSrcEp()) { if (device.attr_list.src_ep != attr_list.src_ep) { return true; } } @@ -547,7 +547,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l ResponseAppend_P(PSTR("\"" D_JSON_ZIGBEE_NAME "\":\"%s\","), EscapeJSONString(friendlyName).c_str()); } // Add all other attributes - ResponseAppend_P(PSTR("%s}"), attr_list.toString(false).c_str()); + ResponseAppend_P(PSTR("%s}"), attr_list.toString(false, true).c_str()); // (false, true) - include battery if (!Settings->flag5.zb_omit_json_addr) { ResponseAppend_P(PSTR("}")); @@ -577,7 +577,7 @@ void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_l } } if (Settings->flag5.zb_topic_endpoint) { - if (attr_list.isValidSrcEp()) { + if (attr_list.validSrcEp()) { snprintf_P(subtopic, sizeof(subtopic), PSTR("%s_%d"), subtopic, attr_list.src_ep); } } @@ -604,6 +604,11 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { gZbLastMessage.groupaddr = attr_list.group_id; // %zbgroup% gZbLastMessage.endpoint = attr_list.src_ep; // %zbendpoint% + // add battery percentage from last known value + if (device.validBatteryPercent()) { + attr_list.setBattPercent(device.batt_percent); + } + device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list); attr_list.reset(); // clear the attributes } @@ -731,7 +736,7 @@ void Z_Device::jsonAddDataAttributes(Z_attribute_list & attr_list) const { // Add "BatteryPercentage", "LastSeen", "LastSeenEpoch", "LinkQuality" void Z_Device::jsonAddDeviceAttributes(Z_attribute_list & attr_list) const { attr_list.addAttributePMEM(PSTR("Reachable")).setBool(getReachable()); - if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batterypercent); } + if (validBatteryPercent()) { attr_list.addAttributePMEM(PSTR("BatteryPercentage")).setUInt(batt_percent); } if (validBattLastSeen()) { attr_list.addAttributePMEM(PSTR("BatteryLastSeenEpoch")).setUInt(batt_last_seen); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino index 76fb96a96..62f2d7557 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_4b_data.ino @@ -46,7 +46,7 @@ int32_t hydrateDeviceWideData(class Z_Device & device, const SBuffer & buf, size } device.last_seen = buf.get32(start+1); device.lqi = buf.get8(start + 5); - device.batterypercent = buf.get8(start + 6); + device.batt_percent = buf.get8(start + 6); if (segment_len >= 10) { device.batt_last_seen = buf.get32(start+7); } @@ -124,7 +124,7 @@ SBuffer hibernateDeviceData(const struct Z_Device & device) { buf.add8(10); // 10 bytes buf.add32(device.last_seen); buf.add8(device.lqi); - buf.add8(device.batterypercent); + buf.add8(device.batt_percent); // now storing batt_last_seen buf.add32(device.batt_last_seen); @@ -280,6 +280,19 @@ void Z_SaveDataTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, u Z_Set_Save_Data_Timer(0); // set a new timer } +// +// Callback for saving all data once, used after receiving important events +// +int32_t Z_Set_Save_Data_Timer_Once(uint8_t value) { + zigbee_devices.setTimer(0x0000, 0, 0 /* now */, 0, 0, Z_CAT_ALWAYS, 0 /* value */, &Z_SaveDataTimerOnce); + return 0; // continue +} + +void Z_SaveDataTimerOnce(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) { + hibernateAllData(); +} + + #ifdef USE_ZIGBEE_EEPROM void ZFS_Erase(void) { if (zigbee.eeprom_present) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino index fb7f8cdb7..14b64109a 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_8_parsers.ino @@ -1703,7 +1703,7 @@ void Z_IncomingMessage(class ZCLFrame &zcl_received) { Z_Query_Battery(srcaddr); // do battery auto-probing when receiving commands } - AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString().c_str()); + AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED ": {\"0x%04X\":{%s}}"), srcaddr, attr_list.toString(false, false).c_str()); // don't include battery // discard the message if it was sent by us (broadcast or group loopback) if (srcaddr == localShortAddr) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino index 9f1fdd0a4..631f77f40 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -2039,8 +2039,8 @@ void ZigbeeShow(bool json) } snprintf_P(sbatt, sizeof(sbatt), msg[ZB_WEB_BATTERY], - device.batterypercent, dhm, - changeUIntScale(device.batterypercent, 0, 100, 0, 14), + device.batt_percent, dhm, + changeUIntScale(device.batt_percent, 0, 100, 0, 14), (color & 0xFF0000) >> 16, (color & 0x00FF00) >> 8, (color & 0x0000FF) ); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino index e0ff79ab2..37f506c0d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino @@ -66,7 +66,7 @@ extern "C" { return d->lqi == 255 ? -1 : d->lqi; } int32_t zd_battery(const class Z_Device* d) { - return d->batterypercent == 255 ? -1 : d->batterypercent; + return d->batt_percent == 255 ? -1 : d->batt_percent; } int32_t zd_battery_lastseen(const class Z_Device* d) { return 0; // TODO not yet known From 0a616df706dcf7fb2ea72bc2ff5117dd7a818449 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 11:18:57 +0200 Subject: [PATCH 187/219] Update changelogs --- CHANGELOG.md | 11 +++++++---- RELEASENOTES.md | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 827b7bc0a..1a394efd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,16 @@ All notable changes to this project will be documented in this file. ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` (#16013) -- Support for multiple ``IRsend`` GPIOs -- Zigbee added recording of when the battery was last reported -- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) +- Support for multiple ``IRsend`` GPIOs (#16138) +- Zigbee added recording of when the battery was last reported (#16146) +- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) (#16148) - Zigbee include "BatteryPercentage" in all messages +- Commands ``WifiScan`` and ``WifiTest`` (#16141) +- Support for Catalan language translations by HardwareBoffin (#16145) ### Changed -- ESP32 LVGL library from v8.2.0 to v8.3.0 +- ESP32 LVGL library from v8.2.0 to v8.3.0 (#16019) +- Tasmota ESP32 Arduino core from v2.0.4 to v2.0.4.1 (#16110) ### Fixed - Restore EnergyToday after using command ``restart 2`` and power cycle (#16118) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b54e3fb5d..01b9b6df9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -33,9 +33,9 @@ While fallback or downgrading is common practice it was never supported due to S This release will be supported from ESP8266/Arduino library Core version **2.7.4.9** due to reported security and stability issues on previous Core version. This will also support gzipped binaries. -This release will be supported from ESP32/Arduino library Core version **2.0.3**. +This release will be supported from ESP32/Arduino library Core version **2.0.4.1**. -Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.3 have been removed. +Support of ESP8266 Core versions before 2.7.4.9 and ESP32 Core versions before 2.0.4.1 have been removed. ## Support of TLS @@ -77,7 +77,7 @@ Historical binaries can be downloaded from The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmota.com/tasmota/release/tasmota.bin.gz`` ### ESP32, ESP32-C3, ESP32-S2 and ESP32-S3 based -The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.4**. +The following binary downloads have been compiled with ESP32/Arduino library core version **2.0.4.1**. - **tasmota32.bin** = The Tasmota version with most drivers including additional sensors and KNX for 4M+ flash. **RECOMMENDED RELEASE BINARY** - **tasmota32xy.bin** = The Tasmota version with most drivers including additional sensors and KNX for ESP32-C3/S2/S3 and 4M+ flash. @@ -114,20 +114,22 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Command ``GlobalHum2 1..250`` to select Global Humidity source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``Sleep2 !`` to cancel pending one-shot speed setting [#15954](https://github.com/arendst/Tasmota/issues/15954) +- Commands ``WifiScan`` and ``WifiTest`` [#16141](https://github.com/arendst/Tasmota/issues/16141) - Support for 5-channel light dimmer driver SM2335 used in SwitchBot Color Bulbs [#15839](https://github.com/arendst/Tasmota/issues/15839) - Support for Sonoff POWR3xxD and THR3xxD [#15856](https://github.com/arendst/Tasmota/issues/15856) - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` [#16013](https://github.com/arendst/Tasmota/issues/16013) -- Support for multiple ``IRsend`` GPIOs -- Zigbee added recording of when the battery was last reported -- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) +- Support for multiple ``IRsend`` GPIOs [#16138](https://github.com/arendst/Tasmota/issues/16138) +- Support for Catalan language translations by HardwareBoffin [#16145](https://github.com/arendst/Tasmota/issues/16145) +- Zigbee added recording of when the battery was last reported [#16146](https://github.com/arendst/Tasmota/issues/16146) +- Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) [#16148](https://github.com/arendst/Tasmota/issues/16148) - ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars [#15916](https://github.com/arendst/Tasmota/issues/15916) ### Breaking Changed ### Changed -- ESP32 Arduino core from v2.0.3 to v2.0.4 [#15940](https://github.com/arendst/Tasmota/issues/15940) -- ESP32 LVGL library from v8.2.0 to v8.3.0 +- ESP32 Arduino core from v2.0.3 to v2.0.4.1 [#15940](https://github.com/arendst/Tasmota/issues/15940) +- ESP32 LVGL library from v8.2.0 to v8.3.0 [#16019](https://github.com/arendst/Tasmota/issues/16019) - Driver DHT v6 consolidation for both ESP8266 and ESP32 to support SI7021, THS01 and MS01 on ESP32 [#15856](https://github.com/arendst/Tasmota/issues/15856) ### Fixed From 4037184ee05e1ba4b85782cec68390c59cfdbf40 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 11:33:32 +0200 Subject: [PATCH 188/219] Fix catalan workflows --- .github/workflows/Tasmota_build_devel.yml | 2 +- .github/workflows/Tasmota_build_master.yml | 2 +- .github/workflows/build_all_the_things.yml | 2 +- CHANGELOG.md | 2 +- RELEASENOTES.md | 2 +- platformio_tasmota_env.ini | 6 +++--- platformio_tasmota_env32.ini | 8 ++++---- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/Tasmota_build_devel.yml b/.github/workflows/Tasmota_build_devel.yml index 184cca854..b7bc2224e 100644 --- a/.github/workflows/Tasmota_build_devel.yml +++ b/.github/workflows/Tasmota_build_devel.yml @@ -77,7 +77,7 @@ jobs: strategy: matrix: variant: [ tasmota, tasmota32 ] - language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AD, AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/Tasmota_build_master.yml b/.github/workflows/Tasmota_build_master.yml index 26e0089c1..8d747bf2f 100644 --- a/.github/workflows/Tasmota_build_master.yml +++ b/.github/workflows/Tasmota_build_master.yml @@ -76,7 +76,7 @@ jobs: strategy: matrix: variant: [ tasmota, tasmota32 ] - language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AD, AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/build_all_the_things.yml b/.github/workflows/build_all_the_things.yml index aa7f5e7c2..65671604e 100644 --- a/.github/workflows/build_all_the_things.yml +++ b/.github/workflows/build_all_the_things.yml @@ -80,7 +80,7 @@ jobs: strategy: matrix: variant: [ tasmota ] - language: [ AF, BG, BR, CA, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] + language: [ AD, AF, BG, BR, CN, CZ, DE, ES, FR, FY, GR, HE, HU, IT, KO, NL, PL, PT, RO, RU, SE, SK, TR, TW, UK, VN ] steps: - uses: actions/checkout@v2 - name: Set up Python diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a394efd4..bfd8d1328 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ All notable changes to this project will be documented in this file. - Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) (#16148) - Zigbee include "BatteryPercentage" in all messages - Commands ``WifiScan`` and ``WifiTest`` (#16141) -- Support for Catalan language translations by HardwareBoffin (#16145) +- Support for Catalan language translations by Albert Gonzalez (#16145) ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 (#16019) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 01b9b6df9..9e1e1015b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -120,7 +120,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - Support for bistable (latching) relays mixed with monostable relays using GPIO Relay_b or Relay_bi as used by Sonoff POWR320D and THR320D - Support for Modbus bridge adding commands ``ModbusSend``, ``ModbusBaudrate`` and ``ModbusSerialConfig`` [#16013](https://github.com/arendst/Tasmota/issues/16013) - Support for multiple ``IRsend`` GPIOs [#16138](https://github.com/arendst/Tasmota/issues/16138) -- Support for Catalan language translations by HardwareBoffin [#16145](https://github.com/arendst/Tasmota/issues/16145) +- Support for Catalan language translations by Albert Gonzalez [#16145](https://github.com/arendst/Tasmota/issues/16145) - Zigbee added recording of when the battery was last reported [#16146](https://github.com/arendst/Tasmota/issues/16146) - Zigbee add Battery auto-probe (can be disabled with ``SetOption143 1``) [#16148](https://github.com/arendst/Tasmota/issues/16148) - ESP32 Support for Ultra Low Power (ULP) coprocessor via Berry by Christian Staars [#15916](https://github.com/arendst/Tasmota/issues/15916) diff --git a/platformio_tasmota_env.ini b/platformio_tasmota_env.ini index 46f6412a8..65a5490c7 100644 --- a/platformio_tasmota_env.ini +++ b/platformio_tasmota_env.ini @@ -71,6 +71,9 @@ build_flags = ${env.build_flags} -DUSE_ZIGBEE -DUSE_CCLOADER -DUSE_U board = esp8266_4M2M board_build.f_cpu = 160000000L +[env:tasmota-AD] +build_flags = ${env.build_flags} -DMY_LANGUAGE=ca_AD + [env:tasmota-AF] build_flags = ${env.build_flags} -DMY_LANGUAGE=af_AF @@ -83,9 +86,6 @@ build_flags = ${env.build_flags} -DMY_LANGUAGE=pt_BR [env:tasmota-CN] build_flags = ${env.build_flags} -DMY_LANGUAGE=zh_CN -[env:tasmota-CA] -build_flags = ${env.build_flags} -DMY_LANGUAGE=ca_AD - [env:tasmota-CZ] build_flags = ${env.build_flags} -DMY_LANGUAGE=cs_CZ diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index f5f30922f..44c5de0df 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -238,6 +238,10 @@ board = esp32s3cdc extends = env:tasmota32s3 board = esp32s3cdc +[env:tasmota32-AD] +extends = env:tasmota32_base +build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=ca_AD -DFIRMWARE_TASMOTA32 + [env:tasmota32-AF] extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=af_AF -DFIRMWARE_TASMOTA32 @@ -254,10 +258,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=pt_BR extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=zh_CN -DFIRMWARE_TASMOTA32 -[env:tasmota32-CA] -extends = env:tasmota32_base -build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=ca_AD -DFIRMWARE_TASMOTA32 - [env:tasmota32-CZ] extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DMY_LANGUAGE=cs_CZ -DFIRMWARE_TASMOTA32 From e327c6194fe9a8f0be2b88a923e05c3ff2f08ac6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 14:23:27 +0200 Subject: [PATCH 189/219] Cleanup JSON message --- tasmota/tasmota_support/support_command.ino | 6 +++--- tasmota/tasmota_support/support_wifi.ino | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 026073422..e82314f00 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -129,8 +129,8 @@ void CmndWifiScan(void) char stemp1[20]; ResponseAppend_P(PSTR("{")); for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { - ResponseAppend_P(PSTR("\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL - "\":\"%d\", \"" D_JSON_SIGNAL "\":\"%d\", \"" D_RSSI "\":\"%d\", \"" D_JSON_ENCRYPTION "\":\"%s\"}"), + ResponseAppend_P(PSTR("\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\",\"" D_BSSID "\":\"%s\",\"" D_CHANNEL + "\":\"%d\",\"" D_JSON_SIGNAL "\":\"%d\",\"" D_RSSI "\":\"%d\",\"" D_JSON_ENCRYPTION "\":\"%s\"}"), i+1, WiFi.SSID(indexes[i]).c_str(), WiFi.BSSIDstr(indexes[i]).c_str(), @@ -139,7 +139,7 @@ void CmndWifiScan(void) WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); if ( ResponseSize() < ResponseLength() + 300 ) { break; } - if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(", ")); } + if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(",")); } //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "MAX SIZE: %d, SIZE: %d"),ResponseSize(),ResponseLength()); } ResponseJsonEnd(); diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index e715b0429..9b51525de 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -358,7 +358,7 @@ void WifiBeginAfterScan(void) Wifi.scan_state++; ResponseClear(); - + uint32_t initial_item = (Wifi.scan_state - 9)*10; if ( wifi_scan_result > initial_item ) { @@ -380,8 +380,8 @@ void WifiBeginAfterScan(void) char stemp1[20]; uint32_t end_item = ( wifi_scan_result > initial_item + 10 ) ? initial_item + 10 : wifi_scan_result; for (uint32_t i = initial_item; i < end_item; i++) { - Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\", \"" D_BSSID "\":\"%s\", \"" D_CHANNEL - "\":\"%d\", \"" D_JSON_SIGNAL "\":\"%d\", \"" D_RSSI "\":\"%d\", \"" D_JSON_ENCRYPTION "\":\"%s\"}}}"), + Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\",\"" D_BSSID "\":\"%s\",\"" D_CHANNEL + "\":\"%d\",\"" D_JSON_SIGNAL "\":\"%d\",\"" D_RSSI "\":\"%d\",\"" D_JSON_ENCRYPTION "\":\"%s\"}}}"), i+1, WiFi.SSID(indexes[i]).c_str(), WiFi.BSSIDstr(indexes[i]).c_str(), From fc86a63417712293b8ee96f032764d264fd728e5 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 15:52:23 +0200 Subject: [PATCH 190/219] Fix wifiscan encryption types Fix wifiscan encryption types (#16141) --- tasmota/include/tasmota.h | 2 ++ tasmota/include/tasmota_globals.h | 34 --------------------- tasmota/tasmota.ino | 23 ++++++++++++++ tasmota/tasmota_support/support_command.ino | 3 +- tasmota/tasmota_support/support_wifi.ino | 32 +++++++++++++++++-- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/tasmota/include/tasmota.h b/tasmota/include/tasmota.h index e993167e6..4a2aaa813 100644 --- a/tasmota/include/tasmota.h +++ b/tasmota/include/tasmota.h @@ -314,6 +314,8 @@ enum InitStates {INIT_NONE, INIT_GPIOS, INIT_DONE}; enum WifiConfigOptions {WIFI_RESTART, EX_WIFI_SMARTCONFIG, WIFI_MANAGER, EX_WIFI_WPSCONFIG, WIFI_RETRY, WIFI_WAIT, WIFI_SERIAL, WIFI_MANAGER_RESET_ONLY, MAX_WIFI_OPTION}; +enum WifiTestOptions {WIFI_NOT_TESTING, WIFI_TESTING, WIFI_TEST_FINISHED, WIFI_TEST_FINISHED_BAD}; + enum SwitchModeOptions {TOGGLE, FOLLOW, FOLLOW_INV, PUSHBUTTON, PUSHBUTTON_INV, PUSHBUTTONHOLD, PUSHBUTTONHOLD_INV, PUSHBUTTON_TOGGLE, TOGGLEMULTI, FOLLOWMULTI, FOLLOWMULTI_INV, PUSHHOLDMULTI, PUSHHOLDMULTI_INV, PUSHON, PUSHON_INV, PUSH_IGNORE, MAX_SWITCH_OPTION}; diff --git a/tasmota/include/tasmota_globals.h b/tasmota/include/tasmota_globals.h index deaebd316..abdb5deb2 100644 --- a/tasmota/include/tasmota_globals.h +++ b/tasmota/include/tasmota_globals.h @@ -593,38 +593,4 @@ bool first_device_group_is_local = true; /*********************************************************************************************/ - -enum WifiTestOptions { WIFI_NOT_TESTING, WIFI_TESTING, WIFI_TEST_FINISHED, WIFI_TEST_FINISHED_BAD }; - -struct WIFI { - uint32_t last_event = 0; // Last wifi connection event - uint32_t downtime = 0; // Wifi down duration - uint16_t link_count = 0; // Number of wifi re-connect - uint8_t counter; - uint8_t retry_init; - uint8_t retry; - uint8_t max_retry; - uint8_t status; - uint8_t config_type = 0; - uint8_t config_counter = 0; - uint8_t scan_state; - uint8_t bssid[6]; - int8_t best_network_db; - uint8_t wifiTest = WIFI_NOT_TESTING; - uint8_t wifi_test_counter = 0; - uint16_t save_data_counter = 0; - uint8_t old_wificonfig = MAX_WIFI_OPTION; // means "nothing yet saved here" - bool wifi_test_AP_TIMEOUT = false; - bool wifi_Test_Restart = false; - bool wifi_Test_Save_SSID2 = false; -} Wifi; - -// Reference. WiFi.encryptionType = -// 2 : ENC_TYPE_TKIP - WPA / PSK -// 4 : ENC_TYPE_CCMP - WPA2 / PSK -// 5 : ENC_TYPE_WEP - WEP -// 7 : ENC_TYPE_NONE - open network -// 8 : ENC_TYPE_AUTO - WPA / WPA2 / PSK -const char kWifiEncryptionTypes[] PROGMEM = "0" "|" "1" "|" "WPA/PSK" "|" "3" "|" "WPA2/PSK" "|" "WEP" "|" "6" "|" "OPEN" "|" "WPA/WPA2/PSK"; - #endif // _TASMOTA_GLOBALS_H_ diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index 64edd25f5..ba44c8192 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -96,6 +96,29 @@ const uint32_t VERSION_MARKER[] PROGMEM = { 0x5AA55AA5, 0xFFFFFFFF, 0xA55AA55A }; +struct WIFI { + uint32_t last_event = 0; // Last wifi connection event + uint32_t downtime = 0; // Wifi down duration + uint16_t link_count = 0; // Number of wifi re-connect + uint8_t counter; + uint8_t retry_init; + uint8_t retry; + uint8_t max_retry; + uint8_t status; + uint8_t config_type = 0; + uint8_t config_counter = 0; + uint8_t scan_state; + uint8_t bssid[6]; + int8_t best_network_db; + uint8_t wifiTest = WIFI_NOT_TESTING; + uint8_t wifi_test_counter = 0; + uint16_t save_data_counter = 0; + uint8_t old_wificonfig = MAX_WIFI_OPTION; // means "nothing yet saved here" + bool wifi_test_AP_TIMEOUT = false; + bool wifi_Test_Restart = false; + bool wifi_Test_Save_SSID2 = false; +} Wifi; + typedef struct { uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT)) uint8_t fast_reboot_count; // 282 diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index e82314f00..2772cdcd6 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -137,7 +137,8 @@ void CmndWifiScan(void) WiFi.channel(indexes[i]), WiFi.RSSI(indexes[i]), WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), - GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); +// GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); + WifiEncryptionType(indexes[i]).c_str()); if ( ResponseSize() < ResponseLength() + 300 ) { break; } if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(",")); } //AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_WIFI "MAX SIZE: %d, SIZE: %d"),ResponseSize(),ResponseLength()); diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 9b51525de..00608dc1d 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -45,8 +45,7 @@ const uint8_t WIFI_RETRY_OFFSET_SEC = WIFI_RETRY_SECONDS; // seconds #include // IPv6 DualStack #endif // LWIP_IPV6=1 -int WifiGetRssiAsQuality(int rssi) -{ +int WifiGetRssiAsQuality(int rssi) { int quality = 0; if (rssi <= -100) { @@ -59,6 +58,32 @@ int WifiGetRssiAsQuality(int rssi) return quality; } +// 0 1 2 3 4 +const char kWifiEncryptionTypes[] PROGMEM = "OPEN|WEP|WPA/PSK|WPA2/PSK|WPA/WPA2/PSK" +#ifdef ESP32 +// 5 6 7 8 + "|WPA2ENTERPRISE|WPA3/PSK|WPA2/WPA3/PSK|WAPI/PSK" +#endif // ESP32 +; + +String WifiEncryptionType(uint32_t i) { +#ifdef ESP8266 + // Reference. WiFi.encryptionType = + // 2 : ENC_TYPE_TKIP - WPA / PSK + // 4 : ENC_TYPE_CCMP - WPA2 / PSK + // 5 : ENC_TYPE_WEP - WEP + // 7 : ENC_TYPE_NONE - open network + // 8 : ENC_TYPE_AUTO - WPA / WPA2 / PSK + uint8_t typea[] = { 0,2,0,3,1,0,0,4 }; + uint32_t type = typea[WiFi.encryptionType(i) -1 &7]; +#else + uint32_t type = WiFi.encryptionType(i); +#endif + char stemp1[20]; + GetTextIndexed(stemp1, sizeof(stemp1), type, kWifiEncryptionTypes); + return stemp1; +} + bool WifiConfigCounter(void) { if (Wifi.config_counter) { @@ -388,7 +413,8 @@ void WifiBeginAfterScan(void) WiFi.channel(indexes[i]), WiFi.RSSI(indexes[i]), WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), - GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); +// GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); + WifiEncryptionType(indexes[i]).c_str()); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN)); } } else if (9 == Wifi.scan_state) { From 00012c78937680383c6f86d3c58656a7785881d7 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 15:55:44 +0200 Subject: [PATCH 191/219] Clean up --- tasmota/tasmota_support/support_command.ino | 2 -- tasmota/tasmota_support/support_wifi.ino | 2 -- 2 files changed, 4 deletions(-) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index 2772cdcd6..ac5c11f00 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -126,7 +126,6 @@ void CmndWifiScan(void) } delay(0); - char stemp1[20]; ResponseAppend_P(PSTR("{")); for (uint32_t i = 0; i < WiFi.scanComplete(); i++) { ResponseAppend_P(PSTR("\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\",\"" D_BSSID "\":\"%s\",\"" D_CHANNEL @@ -137,7 +136,6 @@ void CmndWifiScan(void) WiFi.channel(indexes[i]), WiFi.RSSI(indexes[i]), WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), -// GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); WifiEncryptionType(indexes[i]).c_str()); if ( ResponseSize() < ResponseLength() + 300 ) { break; } if ( i < WiFi.scanComplete() -1 ) { ResponseAppend_P(PSTR(",")); } diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 00608dc1d..68c9502ea 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -402,7 +402,6 @@ void WifiBeginAfterScan(void) delay(0); // Publish the list - char stemp1[20]; uint32_t end_item = ( wifi_scan_result > initial_item + 10 ) ? initial_item + 10 : wifi_scan_result; for (uint32_t i = initial_item; i < end_item; i++) { Response_P(PSTR("{\"" D_CMND_WIFISCAN "\":{\"" D_STATUS5_NETWORK "%d\":{\"" D_SSID "\":\"%s\",\"" D_BSSID "\":\"%s\",\"" D_CHANNEL @@ -413,7 +412,6 @@ void WifiBeginAfterScan(void) WiFi.channel(indexes[i]), WiFi.RSSI(indexes[i]), WifiGetRssiAsQuality(WiFi.RSSI(indexes[i])), -// GetTextIndexed(stemp1, sizeof(stemp1), WiFi.encryptionType(indexes[i]), kWifiEncryptionTypes)); WifiEncryptionType(indexes[i]).c_str()); MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_STAT, PSTR(D_CMND_WIFISCAN)); } From ec3e8eb95aec5229b71ee16357a277c83a8e6d4f Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 7 Aug 2022 16:24:59 +0200 Subject: [PATCH 192/219] Add size check --- tasmota/tasmota_support/support_wifi.ino | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tasmota/tasmota_support/support_wifi.ino b/tasmota/tasmota_support/support_wifi.ino index 68c9502ea..495fe71b8 100644 --- a/tasmota/tasmota_support/support_wifi.ino +++ b/tasmota/tasmota_support/support_wifi.ino @@ -61,8 +61,8 @@ int WifiGetRssiAsQuality(int rssi) { // 0 1 2 3 4 const char kWifiEncryptionTypes[] PROGMEM = "OPEN|WEP|WPA/PSK|WPA2/PSK|WPA/WPA2/PSK" #ifdef ESP32 -// 5 6 7 8 - "|WPA2ENTERPRISE|WPA3/PSK|WPA2/WPA3/PSK|WAPI/PSK" +// 5 6 7 8 + "|WPA2-Enterprise|WPA3/PSK|WPA2/WPA3/PSK|WAPI/PSK" #endif // ESP32 ; @@ -75,10 +75,11 @@ String WifiEncryptionType(uint32_t i) { // 7 : ENC_TYPE_NONE - open network // 8 : ENC_TYPE_AUTO - WPA / WPA2 / PSK uint8_t typea[] = { 0,2,0,3,1,0,0,4 }; - uint32_t type = typea[WiFi.encryptionType(i) -1 &7]; + int type = typea[WiFi.encryptionType(i) -1 &7]; #else - uint32_t type = WiFi.encryptionType(i); + int type = WiFi.encryptionType(i); #endif + if ((type < 0) || (type > 8)) { type = 0; } char stemp1[20]; GetTextIndexed(stemp1, sizeof(stemp1), type, kWifiEncryptionTypes); return stemp1; From 59fd20097c3f5d07154038fc5d9de65d31913d8e Mon Sep 17 00:00:00 2001 From: stefanbode Date: Sun, 7 Aug 2022 18:32:23 +0200 Subject: [PATCH 193/219] Enhance behavior of tilt changes at end points 0% and 100% #15974 mentioned there is unexpected behavior when direction changes and end points are not left. --- tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino index 1cc517940..153a54f38 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_27_shutter.ino @@ -980,6 +980,10 @@ void ShutterButtonHandler(void) XdrvMailbox.payload = XdrvMailbox.index; CmndShutterToggle(); } else { + if (position == ShutterRealToPercentPosition(Shutter[XdrvMailbox.index-1].real_position, XdrvMailbox.index-1) ) { + Shutter[XdrvMailbox.index -1].tilt_target_pos = position==0? Shutter[XdrvMailbox.index -1].tilt_config[0]:(position==100?Shutter[XdrvMailbox.index -1].tilt_config[1]:Shutter[XdrvMailbox.index -1].tilt_target_pos); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("SHT: Shtr%d -> Endpoint movement detected at %d. Set Tilt: %d"), shutter_index+1, position, Shutter[XdrvMailbox.index -1].tilt_target_pos); + } CmndShutterPosition(); } if (Settings->shutter_button[button_index] & ((0x01<<26)< Date: Mon, 8 Aug 2022 13:58:34 +0200 Subject: [PATCH 194/219] add codec wm8960 --- lib/lib_audio/wm8960/library.json | 7 + lib/lib_audio/wm8960/library.properties | 9 + lib/lib_audio/wm8960/src/wm8960.cpp | 114 ++++++++ lib/lib_audio/wm8960/src/wm8960.h | 372 ++++++++++++++++++++++++ 4 files changed, 502 insertions(+) create mode 100644 lib/lib_audio/wm8960/library.json create mode 100644 lib/lib_audio/wm8960/library.properties create mode 100755 lib/lib_audio/wm8960/src/wm8960.cpp create mode 100755 lib/lib_audio/wm8960/src/wm8960.h diff --git a/lib/lib_audio/wm8960/library.json b/lib/lib_audio/wm8960/library.json new file mode 100644 index 000000000..7825e53d5 --- /dev/null +++ b/lib/lib_audio/wm8960/library.json @@ -0,0 +1,7 @@ +{ + "name": "WM8960", + "description": "Audio codec", + "keywords": "ESP8266, ESP32, MP3, AAC, WAV, MOD, FLAC, RTTTL, MIDI, I2S, DAC, Delta-Sigma, TTS", + "version": "1.0.0", + "frameworks": "Arduino" +} diff --git a/lib/lib_audio/wm8960/library.properties b/lib/lib_audio/wm8960/library.properties new file mode 100644 index 000000000..933df97b3 --- /dev/null +++ b/lib/lib_audio/wm8960/library.properties @@ -0,0 +1,9 @@ +name=WM8960 +version=1.0 +author= +maintainer= +sentence=Audio codec for ESP32 +paragraph= +category=Signal Output +url= +architectures=esp32 diff --git a/lib/lib_audio/wm8960/src/wm8960.cpp b/lib/lib_audio/wm8960/src/wm8960.cpp new file mode 100755 index 000000000..d8211831c --- /dev/null +++ b/lib/lib_audio/wm8960/src/wm8960.cpp @@ -0,0 +1,114 @@ + +#ifdef ESP32 + +#include +#include +#include "esp_log.h" +#include "wm8960.h" + +static TwoWire *ws8960wire; + + +void W8960_Write(uint8_t reg_addr, uint16_t data) { + reg_addr <<= 1; + reg_addr |= ((data >> 8) & 1); + data &= 0xff; + ws8960wire->beginTransmission(W8960_ADDR); + ws8960wire->write(reg_addr); + ws8960wire->write(data); + ws8960wire->endTransmission(); +} + +void W8960_Init(TwoWire *tw) { + +ws8960wire = tw; + + // reset + W8960_Write(0x0f, 0x0000); + delay(10); + + // enable dac and adc + W8960_Write(0x19, (1<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)); + // left speaker not used + W8960_Write(0x1A, (1<<8)|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(1<<3)); + W8960_Write(0x2F, (1<<5)|(1<<4)|(1<<3)|(1<<2)); + + // Configure clock + W8960_Write(0x04, 0x0000); + + // Configure ADC/DAC + W8960_Write(0x05, 0x0000); + + // Configure audio interface + // I2S format 16 bits word length + //W8960_Write(0x07 0x0002) + W8960_Write(0x07, 0x0022); + + // Configure HP_L and HP_R OUTPUTS + W8960_Write(0x02, 0x017f); + W8960_Write(0x03, 0x017f); + + // Configure SPK_RP and SPK_RN + W8960_Write(0x28, 0x0177); + W8960_Write(0x29, 0x0177); + + // Enable the OUTPUTS, only right speaker is wired + W8960_Write(0x31, 0x0080); + + // Configure DAC volume + W8960_Write(0x0a, 0x01FF); + W8960_Write(0x0b, 0x01FF); + + // Configure MIXER + W8960_Write(0x22, (1<<8)|(1<<7)); + W8960_Write(0x25, (1<<8)|(1<<7)); + + // Jack Detect + //W8960_Write(0x18, (1<<6)|(0<<5)); + //W8960_Write(0x17, 0x01C3); + //W8960_Write(0x30, 0x0009); + + // input volume + W8960_Write(0x00, 0x0127); + W8960_Write(0x01, 0x0127); + + // set ADC Volume + W8960_Write(0x15, 0x01c3); + W8960_Write(0x16, 0x01c3); + + // disable bypass switch + W8960_Write(0x2d, 0x0000); + W8960_Write(0x2e, 0x0000); + + // connect LINPUT1 to PGA and set PGA Boost Gain. + W8960_Write(0x20, 0x0020|(1<<8)|(1<<3)); + W8960_Write(0x21, 0x0020|(1<<8)|(1<<3)); + +} + +void W8960_SetGain(uint8_t sel, uint16_t value) { + switch (sel) { + case 0: + // output dac in 0.5 db steps + value &= 0x00ff; + value |= 0x0100; + W8960_Write(0x0a, value); + W8960_Write(0x0b, value); + break; + case 1: + // input pga in 0.75 db steps + value &= 0x001f; + value |= 0x0120; + W8960_Write(0x00, value); + W8960_Write(0x01, value); + break; + case 2: + // adc in 0.5 db steps + value &= 0x00ff; + value |= 0x0100; + W8960_Write(0x15, value); + W8960_Write(0x16, value); + break; + } +} +#endif // ESP32 diff --git a/lib/lib_audio/wm8960/src/wm8960.h b/lib/lib_audio/wm8960/src/wm8960.h new file mode 100755 index 000000000..7d07cc0e6 --- /dev/null +++ b/lib/lib_audio/wm8960/src/wm8960.h @@ -0,0 +1,372 @@ +#ifndef _WM8960_H_ +#define _WM8960_H_ + +#define W8960_ADDR 0x1a + +//static const char *TAG = "WM8960"; +#define SDA_PIN 21 +#define SCL_PIN 22 +#define I2C_BUS_NO 0 +#define ACK_CHECK_EN 0 +#define ACK_CHECK_DIS 0 +#define ACK_VAL 0 +#define NACK_VAL 1 + + +#define R0_LEFT_INPUT_VOLUME_ADR 0x00 +#define R1_RIGHT_INPUT_VOLUME_ADR 0x01 +#define R2_LOUT1_VOLUME_ADR 0x02 +#define R3_ROUT1_VOLUME_ADR 0x03 +#define R4_CLOCKING_1_ADR 0x04 +#define R5_ADC_DAC_CONTROL_CTR1_ADR 0x05 +#define R6_ADC_DAC_CONTROL_CTR2_ADR 0x06 +#define R7_AUDIO_INTERFACE_1_ADR 0x07 +#define R8_CLOCKING_2_ADR 0x08 +#define R10_LEFT_DAC_VOLUME_ADR 0x09 +#define R11_RIGHT_DAC_VOLUME_ADR 0x0A +#define R15_RESET_ADR 0x0F +#define R16_3D_CONTROL_ADR 0x10 +#define R17_ALC1_ADR 0x11 +#define R18_ALC2_ADR 0x12 +#define R19_ALC3_ADR 0x13 +#define R20_NOISE_GATE_ADR 0x14 +#define R21_LEFT_ADC_VOLUME_ADR 0x15 +#define R22_RIGHT_ADC_VOLUME_ADR 0x16 +#define R23_ADDITIONAL_CONTROL_1_ADR 0x17 +#define R24_ADDITIONAL_CONTROL_2_ADR 0x18 +#define R25_PWR_MGMT_1_ADR 0x19 +#define R26_PWR_MGMT_2_ADR 0x1A +#define R27_ADDITIONAL_CONTROL_3_ADR 0x1B +#define R28_ANTI_POP_1_ADR 0x1C +#define R29_ANTI_POP_2_ADR 0x1D +#define R32_ADCL_SIGNAL_PATH 0x20 +#define R33_ADCR_SIGNAL_PATH 0x21 +#define R34_LEFT_OUT_MIX_2 0x22 +#define R37_RIGHT_OUT_MIX_2 0x23 +#define R38_MONO_OUT_MIX_1 0x26 +#define R39_MONO_OUT_MIX_2 0x27 +#define R40_LOUT2_VOLUME 0x28 +#define R41_ROUT2_VOLUME 0x29 +#define R42_MONOOUT_VOLUME 0x2A +#define R43_INPUT_BOOST_MIXER_1 0x2B +#define R44_INPUT_BOOST_MIXER_2 0x2C +#define R45_BYPASS_1 0x2D +#define R46_BYPASS_2 0x2E +#define R47_PWR_MGMT_3 0x2F +#define R48_ADDITONAL_CTRL_4 0x30 +#define R49_CLASS_D_CTRL_1 0x31 +#define R51_CLASS_D_CTRL_3 0x33 + + + +typedef struct R0_LEFT_INPUT_VOLUME_t +{ + uint16_t LINVOL :6; //Bits 5:0 + uint16_t LIZC :1; //Bits 6 + uint16_t LINMUTE :1; //Bits 7 + uint16_t IPUV :1; //Bits 8 + +} __attribute__((packed, aligned(2))) R0_LEFT_INPUT_VOLUME_t; + + +typedef struct R1_RIGHT_INPUT_VOLUME_t +{ + uint16_t RINVOL :6; //Bits 5:0 + uint16_t RIZC :1; //Bits 6 + uint16_t RINMUTE :1; //Bits 7 + uint16_t IPUV :1; //Bits 8 + +} __attribute__((packed, aligned(2))) R1_RIGHT_INPUT_VOLUME_t; + + +typedef struct R2_LOUT1_VOLUME_t +{ + uint16_t LOUT1VOL :7; //Bits 6:0 + uint16_t LO1ZC :1; //Bits 7 + uint16_t OUT1VU :1; //Bits 8 +} __attribute__((packed, aligned(2))) R2_LOUT1_VOLUME_t; + +typedef struct R3_ROUT1_VOLUME_t +{ + uint16_t ROUT1VOL :7; //Bits 6:0 + uint16_t RO1ZC :1; //Bits 7 + uint16_t OUT1VU :1; //Bits 8 +} __attribute__((packed, aligned(2))) R3_ROUT1_VOLUME_t; + +typedef struct R4_CLOCKING_1_t{ + + uint16_t ADCDIV :3; //Bits 8:6 + uint16_t DACDIV :3; //Bits 5:3 + uint16_t SYSCLKDIV :2; //Bits 2:1 + uint16_t CLKSEL :1; //Bits 0 +} __attribute__((packed, aligned(2))) R4_CLOCKING_1_t; + +typedef struct R5_ADC_DAC_CONTROL_CTR1_t{ + + uint16_t ADCHPD :1; //Bits 0 + uint16_t DEEMPH :2; //Bits 2:1 + uint16_t DACMU :1; //Bits 3 + uint16_t R5RES_4 :1; + uint16_t ADCPOL :2; //Bits 6:5 + uint16_t DACDIV2 :1; //Bits 7 + uint16_t R5RES_8 :1; +}__attribute__((packed, aligned(2))) R5_ADC_DAC_CONTROL_CTR1_t ; + +typedef struct R6_ADC_DAC_CONTROL_CTR2_t{ + + uint16_t DACSLOPE :1; //Bits 1 + uint16_t DACMR :1; //Bits 2 + uint16_t DACSMM :1; //Bits 3 + uint16_t DACPOL :2; //Bits 6:5 +}__attribute__((packed, aligned(2))) R6_ADC_DAC_CONTROL_CTR2_t; + +typedef struct R7_AUDIO_INTERFACE_t{ + uint16_t FORMAT :2; // bit 1:0 + uint16_t WL :2; // bit 1:0 + uint16_t LRP :1; // bit 4 + uint16_t DLRSWAP :1; // bit 5 + uint16_t MS :1; // bit 6 + uint16_t BCLKINV :1; // bit 7 + uint16_t ALRSWAP :1; // bit 8 +}__attribute__((packed, aligned(2))) R7_AUDIO_INTERFACE_t; + +typedef struct R8_CLOCKING_2_t{ + uint16_t BCLKDIV :4; // bit 3:0 + uint16_t DCLKDIV :3; // bit 2:0 +}__attribute__((packed, aligned(2))) R8_CLOCKING_2_t; + +typedef struct R9_AUDIO_INTERFACE_t{ + uint16_t LOOPBACK :1; // bit 0 + uint16_t ADCCOMP :2; // bits 1:0 + uint16_t DACCOMP :2; // bits 1:0 + uint16_t WL8 :1; // bit 5 + uint16_t ALRCGPIO :1; // bit 6 +}__attribute__((packed, aligned(2))) R9_AUDIO_INTERFACE_t; + +typedef struct R10_LEFT_DAC_VOLUME_t{ + uint16_t LDACVOL :8; // bit 7:0 + uint16_t DACVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R10_LEFT_DAC_VOLUME_t; + +typedef struct R11_RIGHT_DAC_VOLUME_t{ + uint16_t RDACVOL :8; // bit 7:0 + uint16_t DACVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R11_RIGHT_DAC_VOLUME_t; + +// typedef struct R15_RESET_t{}; + +typedef struct R16_3D_CONTROL_t{ + uint16_t D3EN :1; // bit 0 + uint16_t D3DEPTH :4; // bits 3:0 + uint16_t D3LC :1; // bit 5 + uint16_t D3UC :1; // bit 6 +}__attribute__((packed, aligned(2))) R16_3D_CONTROL_t; + +typedef struct R17_ALC1_t{ + uint16_t ALCL :4; // bits 3:0 + uint16_t MAXGAIN :3; // bits 2:0 + uint16_t ALCSEL :2; // bits 1:0 +}__attribute__((packed, aligned(2))) R17_ALC1_t; + +typedef struct R18_ALC2_t{ + uint16_t HLD :4; // bits 3:0 + uint16_t MINGAIN :3; // bits 2:0 +}__attribute__((packed, aligned(2))) R18_ALC2_t; + +typedef struct R19_ALC3_t{ + uint16_t ATK :4; // bits 3:0 + uint16_t DCY :4; // bits 3:0 +}__attribute__((packed, aligned(2))) R19_ALC3_t; + +typedef struct R20_NOISE_GATE_t{ + uint16_t NGAT :1; // bit 0 + uint16_t NGTH :5; // bit 4:0 +}__attribute__((packed, aligned(2))) R20_NOISE_GATE_t; + +typedef struct R21_LEFT_ADC_VOLUME_t{ + uint16_t LADCVOL :8; // bits 7:0 + uint16_t ADCVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R21_LEFT_ADC_VOLUME_t; + +typedef struct R22_RIGHT_ADC_VOLUME_t{ + uint16_t RADCVOL :8; // bits 7:0 + uint16_t ADCVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R22_RIGHT_ADC_VOLUME_t; + +typedef struct R23_ADDITIONAL_CONTROL_1_t{ + uint16_t TOEN :1; // bit 1 + uint16_t TOCLKSEL :1; // bit 2 + uint16_t DATSEL :2; // bits 1:0 + uint16_t DMONOMIX :1; // bit 4 + uint16_t VSEL :2; // bits 1:0 + uint16_t TSDEN :1; // bit 8 +}__attribute__((packed, aligned(2))) R23_ADDITIONAL_CONTROL_1_t; + +typedef struct R24_ADDITIONAL_CONTROL_2_t{ + uint16_t LRCM :1; // bit 2 + uint16_t TRIS :1; // bit 3 + uint16_t HPSWPOL :1; // bit 5 + uint16_t HPSWEN :1; // bit 6 +}__attribute__((packed, aligned(2))) R24_ADDITIONAL_CONTROL_2_t; + +typedef struct R25_PWR_MGMT_1_t{ + uint16_t DIGENB :1; // bit 0 + uint16_t MICB :1; // bit 1 + uint16_t ADCR :1; // bit 2 + uint16_t ADCL :1; // bit 3 + uint16_t AINR :1; // bit 4 + uint16_t AINL :1; // bit 5 + uint16_t VREF :1; // bit 6 + uint16_t VMIDSEL :2; // bit 1:0 +}__attribute__((packed, aligned(2))) R25_PWR_MGMT_1_t; + +typedef struct R26_PWR_MGMT_2_t{ + uint16_t PLL_EN :1; // bit 0 + uint16_t OUT3 :1; // bit 1 + uint16_t SPKR :1; // bit 3 + uint16_t SPKL :1; // bit 4 + uint16_t ROUT1 :1; // bit 5 + uint16_t LOUT1 :1; // bit 6 + uint16_t DACR :1; // bit 7 + uint16_t DACL :1; // bit 8 +}__attribute__((packed, aligned(2))) R26_PWR_MGMT_2_t; + +typedef struct R27_ADDITIONAL_CONTROL_3_t{ + uint16_t ADC_ALC_SR :3; // bits 2:0 + uint16_t OUT3CAP :1; // bit 3 + uint16_t VROI :1; // bit 6 +}__attribute__((packed, aligned(2))) R27_ADDITIONAL_CONTROL_3_t; + +typedef struct R28_ANTI_POP_1_t{ + uint16_t HPSTBY :1; // bit 0 + uint16_t SOFT_ST :1; // bit 2 + uint16_t BUFIOEN :1; // bit 3 + uint16_t BUFDCOPEN :1; // bit 4 + uint16_t POBCTRL :1; // bit 7 +}__attribute__((packed, aligned(2))) R28_ANTI_POP_1_t; + +typedef struct R29_ANTI_POP_2_t{ + uint16_t DRES :2; // bits 1:0 + uint16_t DISOP :1; // bit 5 +}__attribute__((packed, aligned(2))) R29_ANTI_POP_2_t; + +typedef struct R32_ADCL_SIGNAL_PATH_t{ + uint16_t LMIC2B :1; // bit 3 + uint16_t LMICBOOST :2; // bits 1:0 + uint16_t LMP2 :1; // bit 6 + uint16_t LMP3 :1; // bit 7 + uint16_t LMN1 :1; // bit 8 +}__attribute__((packed, aligned(2))) R32_ADCL_SIGNAL_PATH_t; + +typedef struct R33_ADCR_SIGNAL_PATH_t{ + uint16_t RMIC2B :1; // bit 3 + uint16_t RMICBOOST :2; // bits 1:0 + uint16_t RMP2 :1; // bit 6 + uint16_t RMP3 :1; // bit 7 + uint16_t RMN1 :1; // bit 8 +}__attribute__((packed, aligned(2))) R33_ADCR_SIGNAL_PATH_t; + +typedef struct R34_LEFT_OUT_MIX_1_t{ + uint16_t LI2LOVOL :3; // bits 2:0 + uint16_t LI2LO :1; // bit 7 + uint16_t LD2LO :1; // bit 8 +}__attribute__((packed, aligned(2))) R34_LEFT_OUT_MIX_1_t; + +typedef struct R37_RIGHT_OUT_MIX_2_t{ + uint16_t RI2ROVOL :3; // bits 2:0 + uint16_t RI2RO :1; // bit 7 + uint16_t RD2RO :1; // bit 8 +}__attribute__((packed, aligned(2))) R37_RIGHT_OUT_MIX_2_t; + +typedef struct R38_MONO_OUT_MIX_1_t{ + uint16_t L2MO :1; // bit 7 +}__attribute__((packed, aligned(2))) R38_MONO_OUT_MIX_1_t; + +typedef struct R39_MONO_OUT_MIX_2_t{ + uint16_t R2MO :1; // bit 7 +}__attribute__((packed, aligned(2))) R39_MONO_OUT_MIX_2_t; + +typedef struct R40_LOUT2_VOLUME_t{ + uint16_t SPKLVOL :7; // bits 6:0 + uint16_t SPKLZC :1; // bit 7 + uint16_t SPKVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R40_LOUT2_VOLUME_t; + +typedef struct R41_ROUT2_VOLUME_t{ + uint16_t SPKRVOL :7; // bits 6:0 + uint16_t SPKLZC :1; // bit 7 + uint16_t SPKVU :1; // bit 8 +}__attribute__((packed, aligned(2))) R41_ROUT2_VOLUME_t; + +typedef struct R42_MONOOUT_VOLUME_t{ + uint16_t MOUTVOL :1; //bit 6 +}__attribute__((packed, aligned(2))) R42_MONOOUT_VOLUME_t; + +typedef struct R43_INPUT_BOOST_MIXER_1_t{ + uint16_t LIN2BOOST :3; // bits 2:0 + uint16_t LIN3BOOST :3; // bits 2:0 +}__attribute__((packed, aligned(2))) R43_INPUT_BOOST_MIXER_1_t; + +typedef struct R44_INPUT_BOOST_MIXER_2_t{ + uint16_t RIN2BOOST :3; // bits 2:0 + uint16_t RIN3BOOST :3; // bits 2:0 +}__attribute__((packed, aligned(2))) R44_INPUT_BOOST_MIXER_2_t; + +typedef struct R45_BYPASS_1_t{ + uint16_t LB2LOVOL :3; // bits 2:0 + uint16_t LB2LO :1; // bit 5 +}__attribute__((packed, aligned(2))) R45_BYPASS_1_t; + +typedef struct R46_BYPASS_2_t{ + uint16_t RB2ROVOL :3; // bits 2:0 + uint16_t RB2RO :1; // bit 5 +}__attribute__((packed, aligned(2))) R46_BYPASS_2_t; + +typedef struct R47_PWR_MGMT_3_t{ + uint16_t ROMIX :1; // bit 2 + uint16_t LOMIX :1; // bit 3 + uint16_t RMIC :1; // bit 4 + uint16_t LMIC :1; // bit 5 +}__attribute__((packed, aligned(2))) R47_PWR_MGMT_3_t; + +typedef struct R48_ADDITIONAL_CONTROL_4_t{ + uint16_t MBSEL :1; // bit 0 + uint16_t TSENSEN :1; // bit 1 + uint16_t HPSEL :2; // bits 1:0 + uint16_t GPIOSEL :3; // bits 2:0 + uint16_t GPIOPOL :1; // bit 7 +}__attribute__((packed, aligned(2))) R48_ADDITIONAL_CONTROL_4_t; + +typedef struct R49_CLASS_D_CONTROL_1_t{ + uint16_t SPK_OP_EN :2; // bits 1:0 +}__attribute__((packed, aligned(2))) R49_CLASS_D_CONTROL_1_t; + +typedef struct R51_CLASS_D_CONTROL_3_t{ + uint16_t ACGAIN :3; // bits 2:0 + uint16_t DCGAIN :3; // bits 2:0 +}__attribute__((packed, aligned(2))) R51_CLASS_D_CONTROL_3_t; + +typedef struct R52_PLL_N_t{ + uint16_t PLLN :4; // bits 3:0 + uint16_t PLLRESCALE :1; // bit 4 + uint16_t SDM :1; // bit 5 + uint16_t OPCLKDIV :3; // bits 2:0 +}__attribute__((packed, aligned(2))) R52_PLL_N_t; + +typedef struct R53_PLL_K_1_t{ + uint16_t PLLK :8; // bits 23:16 +}__attribute__((packed, aligned(2))) R53_PLL_K_1_t; + +typedef struct R54_PLL_K_2_t{ + uint16_t PLLK :8; // bits 15:8 +}__attribute__((packed, aligned(2))) R54_PLL_K_2_t; + +typedef struct R55_PLL_K_3_t{ + uint16_t PLLK :8; // bits 7:0 +}__attribute__((packed, aligned(2))) R55_PLL_K_3_t; + + +void W8960_Init(TwoWire *tw); +void W8960_SetGain(uint8_t sel, uint16_t value); + +#endif From 7831c72be5d3a7a2ac60c0a5eed8c15bf4788cc0 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 8 Aug 2022 13:58:54 +0200 Subject: [PATCH 195/219] add setgain --- lib/lib_audio/es7243e/src/es7243e.cpp | 7 +++++++ lib/lib_audio/es7243e/src/es7243e.h | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/lib_audio/es7243e/src/es7243e.cpp b/lib/lib_audio/es7243e/src/es7243e.cpp index 536c237ec..6ea7824fb 100644 --- a/lib/lib_audio/es7243e/src/es7243e.cpp +++ b/lib/lib_audio/es7243e/src/es7243e.cpp @@ -95,4 +95,11 @@ esp_err_t es7243e_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg) } return ret; } + +void es7243e_setgain(uint8_t gain) { + uint8_t gaintab[8] = {0x10, 0x12, 0x20, 0x22, 0x04, 0x40, 0x06, 0x42}; + es7243e_write_reg(0x08, gaintab[gain & 7] | 0x09); +} + + #endif diff --git a/lib/lib_audio/es7243e/src/es7243e.h b/lib/lib_audio/es7243e/src/es7243e.h index bef4b3ba6..b96889810 100644 --- a/lib/lib_audio/es7243e/src/es7243e.h +++ b/lib/lib_audio/es7243e/src/es7243e.h @@ -72,6 +72,7 @@ esp_err_t es7243e_adc_init(TwoWire *tw, audio_hal_codec_config_t *codec_cfg); */ // esp_err_t es7243_adc_set_gain(es7243_input_mics_t mic_mask, es7243_gain_value_t gain); +void es7243e_setgain(uint8_t gain); #ifdef __cplusplus } From 4dc910391f7d03e76d75551af5098677c708b828 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 8 Aug 2022 14:00:55 +0200 Subject: [PATCH 196/219] update i2s audio --- .../xdrv_42_0_i2s_audio.ino | 71 +++++++++++++------ .../xdrv_42_1_i2s_mp3mic.ino | 47 ++++++++---- .../xdrv_42_2_i2s_mp3stream.ino | 10 ++- ...i2s_s3box.ino => xdrv_42_4_i2s_codecs.ino} | 19 ++++- 4 files changed, 108 insertions(+), 39 deletions(-) rename tasmota/tasmota_xdrv_driver/{xdrv_42_4_i2s_s3box.ino => xdrv_42_4_i2s_codecs.ino} (93%) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino index 8dc835059..935a95633 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino @@ -67,13 +67,13 @@ #define i2s_port_t uint8_t #endif -#ifdef ESP32 #define MODE_MIC 0 #define MODE_SPK 1 + #ifndef MICSRATE -#define MICSRATE 16000 +#define MICSRATE 32000 #endif -#endif // ESP32 + struct AUDIO_I2S_t { uint8_t is2_volume; // should be in settings @@ -105,6 +105,7 @@ struct AUDIO_I2S_t { #ifdef ESP32 TaskHandle_t mp3_task_h; TaskHandle_t mic_task_h; +#endif // ESP32 uint32_t mic_size; uint32_t mic_rate; uint8_t *mic_buff; @@ -118,9 +119,10 @@ struct AUDIO_I2S_t { int8_t mic_ws = -1; int8_t mic_din = -1; int8_t mic_dout = -1; + uint8_t mic_gain = 1; bool use_stream = false; i2s_port_t mic_port; -#endif // ESP32 + #ifdef USE_SHINE uint32_t recdur; @@ -130,10 +132,13 @@ struct AUDIO_I2S_t { ESP8266WebServer *MP3Server; #endif + uint8_t mode; + } audio_i2s; - +#ifndef MIC_CHANNELS #define MIC_CHANNELS 1 +#endif #ifdef USE_TTGO_WATCH #undef AUDIO_PWR_ON @@ -198,6 +203,15 @@ void Cmd_MicRec(void); void Cmd_wav2mp3(void); void Cmd_Time(void); +void copy_micpars(uint32_t port) { + audio_i2s.mic_mclk = audio_i2s.mclk; + audio_i2s.mic_bclk = audio_i2s.bclk; + audio_i2s.mic_ws = audio_i2s.ws; + audio_i2s.mic_dout = audio_i2s.dout; + audio_i2s.mic_din = audio_i2s.din; + audio_i2s.mic_port = (i2s_port_t)port; +} + int32_t I2S_Init_0(void) { audio_i2s.i2s_port = (i2s_port_t)0; @@ -216,6 +230,9 @@ int32_t I2S_Init_0(void) { audio_i2s.ws = DAC_IIS_WS; audio_i2s.dout = DAC_IIS_DOUT; audio_i2s.din = DAC_IIS_DIN; + + copy_micpars(0); + #else #ifdef USE_I2S_NO_DAC if (PinUsed(GPIO_I2S_DOUT)) { @@ -235,12 +252,7 @@ int32_t I2S_Init_0(void) { audio_i2s.dout = Pin(GPIO_I2S_DOUT); audio_i2s.din = Pin(GPIO_I2S_DIN); - audio_i2s.mic_mclk = audio_i2s.mclk; - audio_i2s.mic_bclk = audio_i2s.bclk; - audio_i2s.mic_ws = audio_i2s.ws; - audio_i2s.mic_dout = audio_i2s.dout; - audio_i2s.mic_din = audio_i2s.din; - audio_i2s.mic_port = (i2s_port_t)0; + copy_micpars(0); // check if 2 ports used, use second for micro if (PinUsed(GPIO_I2S_BCLK, 1) && PinUsed(GPIO_I2S_WS, 1) && PinUsed(GPIO_I2S_DIN, 1)) { @@ -265,12 +277,7 @@ int32_t I2S_Init_0(void) { audio_i2s.dout = Pin(GPIO_I2S_DOUT, 1); audio_i2s.din = Pin(GPIO_I2S_DIN, 1); - audio_i2s.mic_mclk = audio_i2s.mclk; - audio_i2s.mic_bclk = audio_i2s.bclk; - audio_i2s.mic_ws = audio_i2s.ws; - audio_i2s.mic_dout = audio_i2s.dout; - audio_i2s.mic_din = audio_i2s.din; - audio_i2s.mic_port = (i2s_port_t)1; + copy_micpars(1); } else { return -1; @@ -299,21 +306,32 @@ int32_t I2S_Init_0(void) { #endif // USE_I2S_EXTERNAL_DAC + audio_i2s.mode = MODE_SPK; + +#idef USE_I2S_COMMON_IO + audio_i2s.out->SetRate(MICSRATE); +#endif + + return 0; } void I2S_Init(void) { - #if defined(ESP32) && defined(ESP32S3_BOX) - S3boxInit(); - #endif - if (I2S_Init_0()) { return; } - audio_i2s.is2_volume=10; - audio_i2s.out->SetGain(((float)audio_i2s.is2_volume/100.0)*4.0); +#if defined(ESP32) && defined(ESP32S3_BOX) + S3boxInit(); +#endif + +#ifdef USE_W8960 + W8960_Init(); +#endif + + audio_i2s.is2_volume = 10; + audio_i2s.out->SetGain(((float)audio_i2s.is2_volume / 100.0) * 4.0); audio_i2s.out->stop(); audio_i2s.mp3ram = nullptr; @@ -517,12 +535,17 @@ void Play_mp3(const char *path) { if (I2S_Task) { xTaskCreatePinnedToCore(mp3_task, "MP3", 8192, NULL, 3, &audio_i2s.mp3_task_h, 1); } else { +#define MP3_TIMEOUT 30000 + uint32_t tout = millis(); while (audio_i2s.mp3->isRunning()) { if (!audio_i2s.mp3->loop()) { audio_i2s.mp3->stop(); break; } OsWatchLoop(); + if (millis()-tout > MP3_TIMEOUT) { + break; + } } audio_i2s.out->stop(); mp3_delete(); @@ -568,6 +591,7 @@ const char kI2SAudio_Commands[] PROGMEM = "I2S|" #endif // USE_I2S_WEBRADIO #if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) "|REC" + "|MGain" #ifdef MP3_MIC_STREAM "|STREAM" #endif // MP3_MIC_STREAM @@ -587,6 +611,7 @@ void (* const I2SAudio_Command[])(void) PROGMEM = { #endif // USE_I2S_WEBRADIO #if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) ,&Cmd_MicRec + ,&Cmd_MicGain #ifdef MP3_MIC_STREAM ,&Cmd_MP3Stream #endif // MP3_MIC_STREAM diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino index 53550300c..d9ba85259 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_1_i2s_mp3mic.ino @@ -21,30 +21,36 @@ #ifdef ESP32 #if defined(USE_SHINE) && ( (defined(USE_I2S_AUDIO) && defined(USE_I2S_MIC)) || defined(USE_M5STACK_CORE2) || defined(ESP32S3_BOX) ) -#define MP3_BOUNDARY "e8b8c539-047d-4777-a985-fbba6edff11e" uint32_t SpeakerMic(uint8_t spkr) { esp_err_t err = ESP_OK; +#ifndef USE_I2S_COMMON_IO if (spkr == MODE_SPK) { - if (audio_i2s.mic_port == 0) { + if (audio_i2s.i2s_port == audio_i2s.mic_port) { + if (audio_i2s.mode != MODE_SPK) { + i2s_driver_uninstall(audio_i2s.mic_port); + } I2S_Init_0(); - audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume-2)/100.0)*4.0); + audio_i2s.out->SetGain(((float)(audio_i2s.is2_volume - 2) / 100.0) * 4.0); audio_i2s.out->stop(); } + audio_i2s.mode = spkr; return 0; } // set micro - if (audio_i2s.mic_port == 0) { + if (audio_i2s.i2s_port == audio_i2s.mic_port) { // close audio out if (audio_i2s.out) { audio_i2s.out->stop(); delete audio_i2s.out; audio_i2s.out = nullptr; } - i2s_driver_uninstall(audio_i2s.i2s_port); + if (audio_i2s.mode == MODE_SPK) { + i2s_driver_uninstall(audio_i2s.i2s_port); + } } // config mic @@ -104,6 +110,10 @@ esp_err_t err = ESP_OK; } err += i2s_set_clk(audio_i2s.mic_port, audio_i2s.mic_rate, I2S_BITS_PER_SAMPLE_16BIT, mode); +#endif // USE_I2S_COMMON_IO + + audio_i2s.mode = spkr; + return err; } @@ -112,8 +122,6 @@ esp_err_t err = ESP_OK; #include #include -#define MP3HANDLECLIENT audio_i2s.MP3Server->handleClient(); - // micro to mp3 file or stream void mic_task(void *arg){ int8_t error = 0; @@ -144,7 +152,9 @@ void mic_task(void *arg){ audio_i2s.client.setTimeout(3); audio_i2s.client.print("HTTP/1.1 200 OK\r\n" "Content-Type: audio/mpeg;\r\n\r\n"); - MP3HANDLECLIENT + + // Webserver->send(200, "application/octet-stream", ""); + //"Content-Type: audio/mp3;\r\n\r\n"); } shine_set_config_mpeg_defaults(&config.mpeg); @@ -183,6 +193,13 @@ void mic_task(void *arg){ while (!audio_i2s.mic_stop) { uint32_t bytes_read; i2s_read(audio_i2s.mic_port, (char *)buffer, bytesize, &bytes_read, (100 / portTICK_RATE_MS)); + + if (audio_i2s.mic_gain > 1) { + // set gain + for (uint32_t cnt = 0; cnt < bytes_read / 2; cnt++) { + buffer[cnt] *= audio_i2s.mic_gain; + } + } ucp = shine_encode_buffer_interleaved(s, buffer, &written); if (!audio_i2s.use_stream) { @@ -192,7 +209,7 @@ void mic_task(void *arg){ } } else { audio_i2s.client.write((const char*)ucp, written); - MP3HANDLECLIENT + if (!audio_i2s.client.connected()) { break; } @@ -206,7 +223,6 @@ void mic_task(void *arg){ mp3_out.write(ucp, written); } else { audio_i2s.client.write((const char*)ucp, written); - MP3HANDLECLIENT } @@ -223,13 +239,12 @@ exit: if (audio_i2s.use_stream) { audio_i2s.client.stop(); - MP3HANDLECLIENT } SpeakerMic(MODE_SPK); audio_i2s.mic_stop = 0; audio_i2s.mic_error = error; - AddLog(LOG_LEVEL_INFO, PSTR("mp3task error: %d"), error); + AddLog(LOG_LEVEL_INFO, PSTR("mp3task result code: %d"), error); audio_i2s.mic_task_h = 0; audio_i2s.recdur = 0; audio_i2s.stream_active = 0; @@ -292,6 +307,14 @@ void Cmd_MicRec(void) { } +// mic gain in factor not percent +void Cmd_MicGain(void) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 256)) { + audio_i2s.mic_gain = XdrvMailbox.payload; + } + ResponseCmndNumber(audio_i2s.mic_gain); +} + #endif // USE_SHINE #endif // USE_I2S_AUDIO #endif // ESP32 diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino index 72495edb9..af4a873bf 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_2_i2s_mp3stream.ino @@ -4,6 +4,11 @@ #ifdef MP3_MIC_STREAM +#ifndef MP3_STREAM_PORT +#define MP3_STREAM_PORT 81 +#endif + + void Stream_mp3(void) { if (!audio_i2s.stream_enable) { return; @@ -28,10 +33,11 @@ void i2s_mp3_loop(void) { void i2s_mp3_init(uint32_t on) { if (on) { if (!audio_i2s.MP3Server) { - audio_i2s.MP3Server = new ESP8266WebServer(81); + audio_i2s.MP3Server = new ESP8266WebServer(MP3_STREAM_PORT); audio_i2s.MP3Server->on(PSTR("/stream.mp3"), Stream_mp3); + audio_i2s.MP3Server->on(PSTR("/stream.m3a"), Stream_mp3); audio_i2s.MP3Server->begin(); - AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created")); + AddLog(LOG_LEVEL_INFO, PSTR("MP3: server created on port: %d "), MP3_STREAM_PORT); } } else { if (audio_i2s.MP3Server) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_codecs.ino similarity index 93% rename from tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino rename to tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_codecs.ino index 8bf927fc2..1b66edbf9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_s3box.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_4_i2s_codecs.ino @@ -70,7 +70,6 @@ uint32_t es7243e_init() { return ret_val; } - // box adc init uint32_t es7210_init() { uint32_t ret_val = ESP_OK; @@ -124,7 +123,7 @@ uint32_t ES8311_init() { return ret_val; } -void S3boxInit() { +void S3boxInit(void) { if (TasmotaGlobal.i2c_enabled_2) { // box lite ES8156_init(); @@ -137,4 +136,20 @@ void S3boxInit() { } } #endif // ESP32S3_BOX + + +#ifdef USE_W8960 + +#include + +void W8960_Init(void) { + if (TasmotaGlobal.i2c_enabled_2) { + if (I2cSetDevice(W8960_ADDR, 1)) { + I2cSetActiveFound(W8960_ADDR, "W8960-I2C", 1); + W8960_Init(&Wire1); + } + } +} +#endif // USE_W8960 + #endif // ESP32 From 73a4174d4e4eef6844da6c1f14c5d8a7c346d270 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Mon, 8 Aug 2022 14:09:51 +0200 Subject: [PATCH 197/219] Update xdrv_42_0_i2s_audio.ino --- tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino index 935a95633..8c1259d0f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_42_0_i2s_audio.ino @@ -308,7 +308,7 @@ int32_t I2S_Init_0(void) { audio_i2s.mode = MODE_SPK; -#idef USE_I2S_COMMON_IO +#ifdef USE_I2S_COMMON_IO audio_i2s.out->SetRate(MICSRATE); #endif From 603dd28944a88d92b8abdedfc19c8462671cdbc2 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 8 Aug 2022 14:11:57 +0200 Subject: [PATCH 198/219] Fixed IR crash on ESP32 (#16173) --- CHANGELOG.md | 1 + .../IRremoteESP8266/src/IRrecv.cpp | 12 ++++++++++ .../IRremoteESP8266/src/IRrecv.h | 1 + tasmota/include/tasmota_types.h | 2 +- .../tasmota_xdrv_driver/xdrv_05_irremote.ino | 6 ++--- .../xdrv_05_irremote_full.ino | 24 +++++++++---------- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd8d1328..d4fd1e387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Restore EnergyToday after using command ``restart 2`` and power cycle (#16118) +- Fixed IR crash on ESP32 (#16173) ### Removed diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp index a0407132c..fc4113d81 100644 --- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp +++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.cpp @@ -405,6 +405,17 @@ void IRrecv::disableIRIn(void) { #endif // UNIT_TEST } +/// Pause collection of received IR data. +/// @see IRrecv class constructor +void IRrecv::pause(void) { + params.rcvstate = kStopState; + params.rawlen = 0; + params.overflow = false; +#if defined(ESP32) + gpio_intr_disable((gpio_num_t)params.recvpin); +#endif // ESP32 +} + /// Resume collection of received IR data. /// @note This is required if `decode()` is successful and `save_buffer` was /// not set when the class was instanciated. @@ -415,6 +426,7 @@ void IRrecv::resume(void) { params.overflow = false; #if defined(ESP32) timerAlarmDisable(timer); + gpio_intr_enable((gpio_num_t)params.recvpin); #endif // ESP32 } diff --git a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h index 69a21ad49..9b833f704 100644 --- a/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h +++ b/lib/lib_basic/IRremoteESP8266/IRremoteESP8266/src/IRrecv.h @@ -137,6 +137,7 @@ class IRrecv { uint8_t max_skip = 0, uint16_t noise_floor = 0); void enableIRIn(const bool pullup = false); void disableIRIn(void); + void pause(void); void resume(void); uint16_t getBufSize(void); #if DECODE_HASH diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index fc54a728d..ec1c4fe55 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -171,7 +171,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1) uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding - uint32_t spare30 : 1; // bit 30 + uint32_t ir_rcv_while_sending : 1; // bit 30 (v12.0.2.4) - SetOption144 - (IR) Receive IR while sending IR and decode our own signals uint32_t spare31 : 1; // bit 31 }; } SOBitfield5; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino index 1ac9e5cbd..ba403f72f 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote.ino @@ -349,7 +349,7 @@ uint32_t IrRemoteCmndIrSendJson(void) // protocol_text, protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat, protocol_code); #ifdef USE_IR_RECEIVE - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } #endif // USE_IR_RECEIVE switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t @@ -367,12 +367,12 @@ uint32_t IrRemoteCmndIrSendJson(void) #endif default: #ifdef USE_IR_RECEIVE - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } #endif // USE_IR_RECEIVE return IE_PROTO_UNSUPPORTED; } #ifdef USE_IR_RECEIVE - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } #endif // USE_IR_RECEIVE return IE_NO_ERROR; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino index bd757144a..a74270de2 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_05_irremote_full.ino @@ -469,7 +469,7 @@ uint32_t IrRemoteCmndIrHvacJson(void) state.sleep = root.getInt(PSTR(D_JSON_IRHVAC_SLEEP), state.sleep); //if (json[D_JSON_IRHVAC_CLOCK]) { state.clock = json[D_JSON_IRHVAC_CLOCK]; } // not sure it's useful to support 'clock' - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } if (stateMode == StateModes::SEND_ONLY || stateMode == StateModes::SEND_STORE) { int8_t channel = root.getUInt(PSTR(D_JSON_IR_CHANNEL), 1) - 1; if (channel < 0) { channel = GPIO_ANY; } // take first available GPIO @@ -485,7 +485,7 @@ uint32_t IrRemoteCmndIrHvacJson(void) if (stateMode == StateModes::STORE_ONLY || stateMode == StateModes::SEND_STORE) { // store state in memory irac_prev_state = state; } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } Response_P(PSTR("{\"" D_CMND_IRHVAC "\":%s}"), sendACJsonState(state).c_str()); return IE_RESPONSE_PROVIDED; @@ -544,10 +544,10 @@ uint32_t IrRemoteCmndIrSendJson(void) // AddLog(LOG_LEVEL_DEBUG, PSTR("IRS: protocol %d, bits %d, data 0x%s (%s), repeat %d"), // protocol, bits, ulltoa(data, dvalue, 10), Uint64toHex(data, hvalue, bits), repeat); - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } IRsend irsend = IrSendInitGPIO(channel); bool success = irsend.send(protocol, data, bits, repeat); - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } if (!success) { ResponseCmndChar(D_JSON_PROTOCOL_NOT_SUPPORTED); @@ -570,12 +570,12 @@ uint32_t IrRemoteSendGC(char ** pp, uint32_t count, uint32_t repeat) { GC[i] = strtol(strtok_r(nullptr, ",", pp), nullptr, 0); if (!GC[i]) { return IE_INVALID_RAWDATA; } } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { irsend.sendGC(GC, count+1); } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } return IE_NO_ERROR; } @@ -626,7 +626,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { raw_array[i++] = mark; // Mark } } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw count=%d, space=%d, mark=%d, freq=%d"), count, space, mark, freq); @@ -635,7 +635,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { irsend.space(40000); // since we don't know the inter-message gap, place an arbitrary 40ms gap } } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } } else if (6 == count) { // NEC Protocol // IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100 uint16_t raw_array[strlen(*pp)*2+3]; // Header + bits + end @@ -654,7 +654,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { } } raw_array[i++] = parm[2]; // Trailing mark - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { // AddLog(LOG_LEVEL_DEBUG, PSTR("sendRaw %d %d %d %d %d %d"), raw_array[0], raw_array[1], raw_array[2], raw_array[3], raw_array[4], raw_array[5]); @@ -663,7 +663,7 @@ uint32_t IrRemoteSendRawFormatted(char ** pp, uint32_t count, uint32_t repeat) { irsend.space(inter_message); // since we don't know the inter-message gap, place an arbitrary 40ms gap } } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } } else { return IE_INVALID_RAWDATA; } // Invalid number of parameters return IE_NO_ERROR; @@ -732,12 +732,12 @@ uint32_t IrRemoteSendRawStandard(char ** pp, uint16_t freq, uint32_t count, uint // AddLog(LOG_LEVEL_DEBUG, PSTR("Arr %d %d %d %d %d %d %d %d"), arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7]); if (0 == count) { return IE_INVALID_RAWDATA; } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->disableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->pause(); } IRsend irsend = IrSendInitGPIO(); for (uint32_t r = 0; r <= repeat; r++) { irsend.sendRaw(arr, count, freq); } - if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->enableIRIn(); } + if (!IR_RCV_WHILE_SENDING && (irrecv != nullptr)) { irrecv->resume(); } if (nullptr != arr) { free(arr); From cfdf22e55cd4ffe0eff34dfa317fba07c05cc426 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Mon, 8 Aug 2022 14:14:26 +0200 Subject: [PATCH 199/219] Remove SO144 (finally unsued) --- tasmota/include/tasmota_types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index ec1c4fe55..fc54a728d 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -171,7 +171,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1) uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding - uint32_t ir_rcv_while_sending : 1; // bit 30 (v12.0.2.4) - SetOption144 - (IR) Receive IR while sending IR and decode our own signals + uint32_t spare30 : 1; // bit 30 uint32_t spare31 : 1; // bit 31 }; } SOBitfield5; From 4b82389a810f3b36f2a65d92f895d91d0cb7c9ce Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 8 Aug 2022 22:40:28 +0200 Subject: [PATCH 200/219] support `CONFIG_CAMERA_CONVERTER_ENABLED` --- .../esp32-camera/driver/include/esp_camera.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/libesp32/esp32-camera/driver/include/esp_camera.h b/lib/libesp32/esp32-camera/driver/include/esp_camera.h index e9981671f..14fdd2378 100644 --- a/lib/libesp32/esp32-camera/driver/include/esp_camera.h +++ b/lib/libesp32/esp32-camera/driver/include/esp_camera.h @@ -70,6 +70,7 @@ #include "driver/ledc.h" #include "sensor.h" #include "sys/time.h" +#include "sdkconfig.h" #ifdef __cplusplus extern "C" { @@ -91,6 +92,19 @@ typedef enum { CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */ } camera_fb_location_t; +#if CONFIG_CAMERA_CONVERTER_ENABLED +/** + * @brief Camera RGB\YUV conversion mode + */ +typedef enum { + CONV_DISABLE, + RGB565_TO_YUV422, + + YUV422_TO_RGB565, + YUV422_TO_YUV420 +} camera_conv_mode_t; +#endif + /** * @brief Configuration structure for camera initialization */ @@ -124,6 +138,9 @@ typedef struct { size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */ camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */ camera_grab_mode_t grab_mode; /*!< When buffers should be filled */ +#if CONFIG_CAMERA_CONVERTER_ENABLED + camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */ +#endif } camera_config_t; /** From ceed6525ec0114f7bcdd4e6f253f7567c9d66622 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Mon, 8 Aug 2022 22:42:15 +0200 Subject: [PATCH 201/219] add cameras --- lib/libesp32/esp32-camera/driver/include/sensor.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/libesp32/esp32-camera/driver/include/sensor.h b/lib/libesp32/esp32-camera/driver/include/sensor.h index 1f99c1541..d5ec7463f 100644 --- a/lib/libesp32/esp32-camera/driver/include/sensor.h +++ b/lib/libesp32/esp32-camera/driver/include/sensor.h @@ -26,6 +26,10 @@ typedef enum { GC2145_PID = 0x2145, GC032A_PID = 0x232a, GC0308_PID = 0x9b, + BF3005_PID = 0x30, + BF20A6_PID = 0x20a6, + SC101IOT_PID = 0xda4a, + SC030IOT_PID = 0x9a46, } camera_pid_t; typedef enum { @@ -38,6 +42,10 @@ typedef enum { CAMERA_GC2145, CAMERA_GC032A, CAMERA_GC0308, + CAMERA_BF3005, + CAMERA_BF20A6, + CAMERA_SC101IOT, + CAMERA_SC030IOT, CAMERA_MODEL_MAX, CAMERA_NONE, } camera_model_t; @@ -52,11 +60,16 @@ typedef enum { GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1 GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1 GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1 + BF3005_SCCB_ADDR = 0x6E, + BF20A6_SCCB_ADDR = 0x6E, + SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 + SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1 } camera_sccb_addr_t; typedef enum { PIXFORMAT_RGB565, // 2BPP/RGB565 PIXFORMAT_YUV422, // 2BPP/YUV422 + PIXFORMAT_YUV420, // 1.5BPP/YUV420 PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE PIXFORMAT_JPEG, // JPEG/COMPRESSED PIXFORMAT_RGB888, // 3BPP/RGB888 From fdda6e6c2a4b344fbed8146a08000ed9e6386ff7 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 9 Aug 2022 08:02:53 +0200 Subject: [PATCH 202/219] move library --- lib/{lib_audio => libesp32_audio}/es7210/library.json | 0 .../es7210/library.properties | 0 lib/{lib_audio => libesp32_audio}/es7210/src/es7210.cpp | 4 ++-- lib/{lib_audio => libesp32_audio}/es7210/src/es7210.h | 0 lib/{lib_audio => libesp32_audio}/es7243e/library.json | 0 .../es7243e/library.properties | 0 lib/{lib_audio => libesp32_audio}/es7243e/src/es7243e.cpp | 0 lib/{lib_audio => libesp32_audio}/es7243e/src/es7243e.h | 0 lib/{lib_audio => libesp32_audio}/es8156/library.json | 0 .../es8156/library.properties | 0 lib/{lib_audio => libesp32_audio}/es8156/src/audio_hal.h | 0 lib/{lib_audio => libesp32_audio}/es8156/src/es8156.cpp | 0 lib/{lib_audio => libesp32_audio}/es8156/src/es8156.h | 0 .../es8156/src/esxxx_common.h | 0 lib/{lib_audio => libesp32_audio}/es8311/library.json | 0 .../es8311/library.properties | 0 lib/{lib_audio => libesp32_audio}/es8311/src/es8311.cpp | 0 lib/{lib_audio => libesp32_audio}/es8311/src/es8311.h | 0 lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/COPYING | 0 lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/LICENSE | 0 .../mp3_shine_esp32/README.md | 0 .../mp3_shine_esp32/changelog.txt | 0 .../mp3_shine_esp32/component.mk | 0 .../mp3_shine_esp32/library.json | 7 ++++--- .../mp3_shine_esp32/library.properties | 0 .../mp3_shine_esp32/src/bitstream.cpp | 1 - .../mp3_shine_esp32/src/bitstream.h | 0 .../mp3_shine_esp32/src/huffman.cpp | 0 .../mp3_shine_esp32/src/huffman.h | 0 .../mp3_shine_esp32/src/l3bitstream.cpp | 0 .../mp3_shine_esp32/src/l3bitstream.h | 0 .../mp3_shine_esp32/src/l3loop.cpp | 0 .../mp3_shine_esp32/src/l3loop.h | 0 .../mp3_shine_esp32/src/l3mdct.cpp | 0 .../mp3_shine_esp32/src/l3mdct.h | 0 .../mp3_shine_esp32/src/l3subband.cpp | 0 .../mp3_shine_esp32/src/l3subband.h | 0 .../mp3_shine_esp32/src/layer3.cpp | 0 .../mp3_shine_esp32/src/layer3.h | 0 .../mp3_shine_esp32/src/mult_mips_gcc.h | 0 .../mp3_shine_esp32/src/mult_noarch_gcc.h | 0 .../mp3_shine_esp32/src/mult_sarm_gcc.h | 0 .../mp3_shine_esp32/src/reservoir.cpp | 0 .../mp3_shine_esp32/src/reservoir.h | 0 .../mp3_shine_esp32/src/tables.cpp | 0 .../mp3_shine_esp32/src/tables.h | 0 .../mp3_shine_esp32/src/types.h | 0 lib/{lib_audio => libesp32_audio}/wm8960/library.json | 0 .../wm8960/library.properties | 0 lib/{lib_audio => libesp32_audio}/wm8960/src/wm8960.cpp | 0 lib/{lib_audio => libesp32_audio}/wm8960/src/wm8960.h | 0 51 files changed, 6 insertions(+), 6 deletions(-) rename lib/{lib_audio => libesp32_audio}/es7210/library.json (100%) rename lib/{lib_audio => libesp32_audio}/es7210/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/es7210/src/es7210.cpp (99%) rename lib/{lib_audio => libesp32_audio}/es7210/src/es7210.h (100%) rename lib/{lib_audio => libesp32_audio}/es7243e/library.json (100%) rename lib/{lib_audio => libesp32_audio}/es7243e/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/es7243e/src/es7243e.cpp (100%) rename lib/{lib_audio => libesp32_audio}/es7243e/src/es7243e.h (100%) rename lib/{lib_audio => libesp32_audio}/es8156/library.json (100%) rename lib/{lib_audio => libesp32_audio}/es8156/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/es8156/src/audio_hal.h (100%) rename lib/{lib_audio => libesp32_audio}/es8156/src/es8156.cpp (100%) rename lib/{lib_audio => libesp32_audio}/es8156/src/es8156.h (100%) rename lib/{lib_audio => libesp32_audio}/es8156/src/esxxx_common.h (100%) rename lib/{lib_audio => libesp32_audio}/es8311/library.json (100%) rename lib/{lib_audio => libesp32_audio}/es8311/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/es8311/src/es8311.cpp (100%) rename lib/{lib_audio => libesp32_audio}/es8311/src/es8311.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/COPYING (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/LICENSE (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/README.md (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/changelog.txt (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/component.mk (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/library.json (50%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/bitstream.cpp (99%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/bitstream.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/huffman.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/huffman.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3bitstream.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3bitstream.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3loop.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3loop.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3mdct.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3mdct.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3subband.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/l3subband.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/layer3.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/layer3.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/mult_mips_gcc.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/mult_noarch_gcc.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/mult_sarm_gcc.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/reservoir.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/reservoir.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/tables.cpp (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/tables.h (100%) rename lib/{lib_audio => libesp32_audio}/mp3_shine_esp32/src/types.h (100%) rename lib/{lib_audio => libesp32_audio}/wm8960/library.json (100%) rename lib/{lib_audio => libesp32_audio}/wm8960/library.properties (100%) rename lib/{lib_audio => libesp32_audio}/wm8960/src/wm8960.cpp (100%) rename lib/{lib_audio => libesp32_audio}/wm8960/src/wm8960.h (100%) diff --git a/lib/lib_audio/es7210/library.json b/lib/libesp32_audio/es7210/library.json similarity index 100% rename from lib/lib_audio/es7210/library.json rename to lib/libesp32_audio/es7210/library.json diff --git a/lib/lib_audio/es7210/library.properties b/lib/libesp32_audio/es7210/library.properties similarity index 100% rename from lib/lib_audio/es7210/library.properties rename to lib/libesp32_audio/es7210/library.properties diff --git a/lib/lib_audio/es7210/src/es7210.cpp b/lib/libesp32_audio/es7210/src/es7210.cpp similarity index 99% rename from lib/lib_audio/es7210/src/es7210.cpp rename to lib/libesp32_audio/es7210/src/es7210.cpp index 3bf680308..65b2c0cb7 100644 --- a/lib/lib_audio/es7210/src/es7210.cpp +++ b/lib/libesp32_audio/es7210/src/es7210.cpp @@ -45,7 +45,7 @@ static es7210_gain_value_t gain; /* * Clock coefficient structer */ -struct _coeff_div { +struct _coeff_div_es7210 { uint32_t mclk; /* mclk frequency */ uint32_t lrck; /* lrck */ uint8_t ss_ds; @@ -75,7 +75,7 @@ static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 * lrckh: 0x04 * lrckl: 0x05 */ -static const struct _coeff_div coeff_div[] = { +static const struct _coeff_div_es7210 coeff_div[] = { //mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl /* 8k */ {12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, diff --git a/lib/lib_audio/es7210/src/es7210.h b/lib/libesp32_audio/es7210/src/es7210.h similarity index 100% rename from lib/lib_audio/es7210/src/es7210.h rename to lib/libesp32_audio/es7210/src/es7210.h diff --git a/lib/lib_audio/es7243e/library.json b/lib/libesp32_audio/es7243e/library.json similarity index 100% rename from lib/lib_audio/es7243e/library.json rename to lib/libesp32_audio/es7243e/library.json diff --git a/lib/lib_audio/es7243e/library.properties b/lib/libesp32_audio/es7243e/library.properties similarity index 100% rename from lib/lib_audio/es7243e/library.properties rename to lib/libesp32_audio/es7243e/library.properties diff --git a/lib/lib_audio/es7243e/src/es7243e.cpp b/lib/libesp32_audio/es7243e/src/es7243e.cpp similarity index 100% rename from lib/lib_audio/es7243e/src/es7243e.cpp rename to lib/libesp32_audio/es7243e/src/es7243e.cpp diff --git a/lib/lib_audio/es7243e/src/es7243e.h b/lib/libesp32_audio/es7243e/src/es7243e.h similarity index 100% rename from lib/lib_audio/es7243e/src/es7243e.h rename to lib/libesp32_audio/es7243e/src/es7243e.h diff --git a/lib/lib_audio/es8156/library.json b/lib/libesp32_audio/es8156/library.json similarity index 100% rename from lib/lib_audio/es8156/library.json rename to lib/libesp32_audio/es8156/library.json diff --git a/lib/lib_audio/es8156/library.properties b/lib/libesp32_audio/es8156/library.properties similarity index 100% rename from lib/lib_audio/es8156/library.properties rename to lib/libesp32_audio/es8156/library.properties diff --git a/lib/lib_audio/es8156/src/audio_hal.h b/lib/libesp32_audio/es8156/src/audio_hal.h similarity index 100% rename from lib/lib_audio/es8156/src/audio_hal.h rename to lib/libesp32_audio/es8156/src/audio_hal.h diff --git a/lib/lib_audio/es8156/src/es8156.cpp b/lib/libesp32_audio/es8156/src/es8156.cpp similarity index 100% rename from lib/lib_audio/es8156/src/es8156.cpp rename to lib/libesp32_audio/es8156/src/es8156.cpp diff --git a/lib/lib_audio/es8156/src/es8156.h b/lib/libesp32_audio/es8156/src/es8156.h similarity index 100% rename from lib/lib_audio/es8156/src/es8156.h rename to lib/libesp32_audio/es8156/src/es8156.h diff --git a/lib/lib_audio/es8156/src/esxxx_common.h b/lib/libesp32_audio/es8156/src/esxxx_common.h similarity index 100% rename from lib/lib_audio/es8156/src/esxxx_common.h rename to lib/libesp32_audio/es8156/src/esxxx_common.h diff --git a/lib/lib_audio/es8311/library.json b/lib/libesp32_audio/es8311/library.json similarity index 100% rename from lib/lib_audio/es8311/library.json rename to lib/libesp32_audio/es8311/library.json diff --git a/lib/lib_audio/es8311/library.properties b/lib/libesp32_audio/es8311/library.properties similarity index 100% rename from lib/lib_audio/es8311/library.properties rename to lib/libesp32_audio/es8311/library.properties diff --git a/lib/lib_audio/es8311/src/es8311.cpp b/lib/libesp32_audio/es8311/src/es8311.cpp similarity index 100% rename from lib/lib_audio/es8311/src/es8311.cpp rename to lib/libesp32_audio/es8311/src/es8311.cpp diff --git a/lib/lib_audio/es8311/src/es8311.h b/lib/libesp32_audio/es8311/src/es8311.h similarity index 100% rename from lib/lib_audio/es8311/src/es8311.h rename to lib/libesp32_audio/es8311/src/es8311.h diff --git a/lib/lib_audio/mp3_shine_esp32/COPYING b/lib/libesp32_audio/mp3_shine_esp32/COPYING similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/COPYING rename to lib/libesp32_audio/mp3_shine_esp32/COPYING diff --git a/lib/lib_audio/mp3_shine_esp32/LICENSE b/lib/libesp32_audio/mp3_shine_esp32/LICENSE similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/LICENSE rename to lib/libesp32_audio/mp3_shine_esp32/LICENSE diff --git a/lib/lib_audio/mp3_shine_esp32/README.md b/lib/libesp32_audio/mp3_shine_esp32/README.md similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/README.md rename to lib/libesp32_audio/mp3_shine_esp32/README.md diff --git a/lib/lib_audio/mp3_shine_esp32/changelog.txt b/lib/libesp32_audio/mp3_shine_esp32/changelog.txt similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/changelog.txt rename to lib/libesp32_audio/mp3_shine_esp32/changelog.txt diff --git a/lib/lib_audio/mp3_shine_esp32/component.mk b/lib/libesp32_audio/mp3_shine_esp32/component.mk similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/component.mk rename to lib/libesp32_audio/mp3_shine_esp32/component.mk diff --git a/lib/lib_audio/mp3_shine_esp32/library.json b/lib/libesp32_audio/mp3_shine_esp32/library.json similarity index 50% rename from lib/lib_audio/mp3_shine_esp32/library.json rename to lib/libesp32_audio/mp3_shine_esp32/library.json index d7de5755a..f73928c85 100644 --- a/lib/lib_audio/mp3_shine_esp32/library.json +++ b/lib/libesp32_audio/mp3_shine_esp32/library.json @@ -1,7 +1,8 @@ { - "name": "mp3_shine_esp32", + "name":"mp3_shine_esp32", + "version": "1.0.0", "description": "mp3 encoder", "keywords": "ESP32, MP3", - "version": "1.0.0", - "frameworks": "Arduino" + "frameworks": "Arduino", + "platforms": "espressif32" } diff --git a/lib/lib_audio/mp3_shine_esp32/library.properties b/lib/libesp32_audio/mp3_shine_esp32/library.properties similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/library.properties rename to lib/libesp32_audio/mp3_shine_esp32/library.properties diff --git a/lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/bitstream.cpp similarity index 99% rename from lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/bitstream.cpp index 866e94704..81b6d3b98 100755 --- a/lib/lib_audio/mp3_shine_esp32/src/bitstream.cpp +++ b/lib/libesp32_audio/mp3_shine_esp32/src/bitstream.cpp @@ -9,7 +9,6 @@ #include "types.h" #include "bitstream.h" - #if !defined(__APPLE__) && !defined(__FreeBSD__) #include #endif diff --git a/lib/lib_audio/mp3_shine_esp32/src/bitstream.h b/lib/libesp32_audio/mp3_shine_esp32/src/bitstream.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/bitstream.h rename to lib/libesp32_audio/mp3_shine_esp32/src/bitstream.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/huffman.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/huffman.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/huffman.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/huffman.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/huffman.h b/lib/libesp32_audio/mp3_shine_esp32/src/huffman.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/huffman.h rename to lib/libesp32_audio/mp3_shine_esp32/src/huffman.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/l3bitstream.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3bitstream.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/l3bitstream.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h b/lib/libesp32_audio/mp3_shine_esp32/src/l3bitstream.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3bitstream.h rename to lib/libesp32_audio/mp3_shine_esp32/src/l3bitstream.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/l3loop.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3loop.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/l3loop.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3loop.h b/lib/libesp32_audio/mp3_shine_esp32/src/l3loop.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3loop.h rename to lib/libesp32_audio/mp3_shine_esp32/src/l3loop.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/l3mdct.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3mdct.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/l3mdct.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3mdct.h b/lib/libesp32_audio/mp3_shine_esp32/src/l3mdct.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3mdct.h rename to lib/libesp32_audio/mp3_shine_esp32/src/l3mdct.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/l3subband.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3subband.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/l3subband.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/l3subband.h b/lib/libesp32_audio/mp3_shine_esp32/src/l3subband.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/l3subband.h rename to lib/libesp32_audio/mp3_shine_esp32/src/l3subband.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/layer3.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/layer3.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/layer3.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/layer3.h b/lib/libesp32_audio/mp3_shine_esp32/src/layer3.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/layer3.h rename to lib/libesp32_audio/mp3_shine_esp32/src/layer3.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h b/lib/libesp32_audio/mp3_shine_esp32/src/mult_mips_gcc.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/mult_mips_gcc.h rename to lib/libesp32_audio/mp3_shine_esp32/src/mult_mips_gcc.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h b/lib/libesp32_audio/mp3_shine_esp32/src/mult_noarch_gcc.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/mult_noarch_gcc.h rename to lib/libesp32_audio/mp3_shine_esp32/src/mult_noarch_gcc.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h b/lib/libesp32_audio/mp3_shine_esp32/src/mult_sarm_gcc.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/mult_sarm_gcc.h rename to lib/libesp32_audio/mp3_shine_esp32/src/mult_sarm_gcc.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/reservoir.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/reservoir.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/reservoir.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/reservoir.h b/lib/libesp32_audio/mp3_shine_esp32/src/reservoir.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/reservoir.h rename to lib/libesp32_audio/mp3_shine_esp32/src/reservoir.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/tables.cpp b/lib/libesp32_audio/mp3_shine_esp32/src/tables.cpp similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/tables.cpp rename to lib/libesp32_audio/mp3_shine_esp32/src/tables.cpp diff --git a/lib/lib_audio/mp3_shine_esp32/src/tables.h b/lib/libesp32_audio/mp3_shine_esp32/src/tables.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/tables.h rename to lib/libesp32_audio/mp3_shine_esp32/src/tables.h diff --git a/lib/lib_audio/mp3_shine_esp32/src/types.h b/lib/libesp32_audio/mp3_shine_esp32/src/types.h similarity index 100% rename from lib/lib_audio/mp3_shine_esp32/src/types.h rename to lib/libesp32_audio/mp3_shine_esp32/src/types.h diff --git a/lib/lib_audio/wm8960/library.json b/lib/libesp32_audio/wm8960/library.json similarity index 100% rename from lib/lib_audio/wm8960/library.json rename to lib/libesp32_audio/wm8960/library.json diff --git a/lib/lib_audio/wm8960/library.properties b/lib/libesp32_audio/wm8960/library.properties similarity index 100% rename from lib/lib_audio/wm8960/library.properties rename to lib/libesp32_audio/wm8960/library.properties diff --git a/lib/lib_audio/wm8960/src/wm8960.cpp b/lib/libesp32_audio/wm8960/src/wm8960.cpp similarity index 100% rename from lib/lib_audio/wm8960/src/wm8960.cpp rename to lib/libesp32_audio/wm8960/src/wm8960.cpp diff --git a/lib/lib_audio/wm8960/src/wm8960.h b/lib/libesp32_audio/wm8960/src/wm8960.h similarity index 100% rename from lib/lib_audio/wm8960/src/wm8960.h rename to lib/libesp32_audio/wm8960/src/wm8960.h From de84f3a2151f6ff2bee4e0ddc4f7be0895463e88 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Tue, 9 Aug 2022 08:05:05 +0200 Subject: [PATCH 203/219] Update platformio_tasmota_env32.ini --- platformio_tasmota_env32.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 44c5de0df..d8e90f58c 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -18,6 +18,7 @@ lib_extra_dirs = ${common.lib_extra_dirs} lib/libesp32_lvgl lib/libesp32_div lib/libesp32_eink + lib/libesp32_audio lib_ignore = HTTPUpdateServer ESP RainMaker From cb190d45e3ee4478b83aca65650656859685cd3c Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 9 Aug 2022 11:06:04 +0200 Subject: [PATCH 204/219] Zigbee fix Tuya for writing attributes --- CHANGELOG.md | 1 + .../xdrv_23_zigbee_5__constants.ino | 856 +++++++++--------- .../xdrv_23_zigbee_6_commands.ino | 5 +- .../xdrv_23_zigbee_A_impl.ino | 4 +- 4 files changed, 439 insertions(+), 427 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4fd1e387..4ba63afbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ All notable changes to this project will be documented in this file. ### Fixed - Restore EnergyToday after using command ``restart 2`` and power cycle (#16118) - Fixed IR crash on ESP32 (#16173) +- Zigbee fix Tuya for writing attributes ### Removed diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino index eaec0d99d..df2d3c0be 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5__constants.ino @@ -80,6 +80,7 @@ const char Z_strings[] PROGMEM = "00xx0A00" "\x00" "00xxxx000000000000" "\x00" "01" "\x00" + "0101" "\x00" "01190200" "\x00" "01xx0A00" "\x00" "01xxxx" "\x00" @@ -451,11 +452,13 @@ const char Z_strings[] PROGMEM = "TuyaEcoTemp" "\x00" "TuyaFanMode" "\x00" "TuyaForceMode" "\x00" + "TuyaMCUVersion" "\x00" "TuyaMaxTemp" "\x00" "TuyaMinTemp" "\x00" "TuyaMotorReversal" "\x00" "TuyaMovingState" "\x00" "TuyaPreset" "\x00" + "TuyaQuery" "\x00" "TuyaScheduleHolidays" "\x00" "TuyaScheduleWorkdays" "\x00" "TuyaTempTarget" "\x00" @@ -513,431 +516,434 @@ enum Z_offsets { Zo_00xx0A00 = 13, Zo_00xxxx000000000000 = 22, Zo_01 = 41, - Zo_01190200 = 44, - Zo_01xx0A00 = 53, - Zo_01xxxx = 62, - Zo_01xxxx000000000000 = 69, - Zo_01xxxx0A0000000000 = 88, - Zo_03xx0A00 = 107, - Zo_03xxxx000000000000 = 116, - Zo_03xxxx0A0000000000 = 135, - Zo_AccelerationTimeLift = 154, - Zo_ActivePower = 175, - Zo_ActuatorEnabled = 187, - Zo_AddGroup = 203, - Zo_AddScene = 212, - Zo_AlarmCount = 221, - Zo_AnalogApplicationType = 232, - Zo_AnalogDescription = 254, - Zo_AnalogEngineeringUnits = 272, - Zo_AnalogInApplicationType = 295, - Zo_AnalogInDescription = 319, - Zo_AnalogInEngineeringUnits = 339, - Zo_AnalogInMaxValue = 364, - Zo_AnalogInMinValue = 381, - Zo_AnalogInOutOfService = 398, - Zo_AnalogInReliability = 419, - Zo_AnalogInResolution = 439, - Zo_AnalogInStatusFlags = 458, - Zo_AnalogOutApplicationType = 478, - Zo_AnalogOutDescription = 503, - Zo_AnalogOutEngineeringUnits = 524, - Zo_AnalogOutMaxValue = 550, - Zo_AnalogOutMinValue = 568, - Zo_AnalogOutOfService = 586, - Zo_AnalogOutOutOfService = 605, - Zo_AnalogOutReliability = 627, - Zo_AnalogOutRelinquishDefault = 648, - Zo_AnalogOutResolution = 675, - Zo_AnalogOutStatusFlags = 695, - Zo_AnalogOutValue = 716, - Zo_AnalogPriorityArray = 731, - Zo_AnalogReliability = 751, - Zo_AnalogRelinquishDefault = 769, - Zo_AnalogStatusFlags = 793, - Zo_AnalogValue = 811, - Zo_AppVersion = 823, - Zo_ApparentPower = 834, - Zo_AqaraAccelerometer = 848, - Zo_AqaraRotate = 867, - Zo_AqaraVibration505 = 879, - Zo_AqaraVibrationMode = 897, - Zo_AqaraVibrationsOrAngle = 916, - Zo_Aqara_FF05 = 939, - Zo_ArrowClick = 950, - Zo_ArrowHold = 961, - Zo_ArrowRelease = 971, - Zo_AvailablePower = 984, - Zo_BatteryPercentage = 999, - Zo_BatteryVoltage = 1017, - Zo_BinaryActiveText = 1032, - Zo_BinaryApplicationType = 1049, - Zo_BinaryDescription = 1071, - Zo_BinaryInActiveText = 1089, - Zo_BinaryInApplicationType = 1108, - Zo_BinaryInDescription = 1132, - Zo_BinaryInInactiveText = 1152, - Zo_BinaryInOutOfService = 1173, - Zo_BinaryInPolarity = 1194, - Zo_BinaryInReliability = 1211, - Zo_BinaryInStatusFlags = 1231, - Zo_BinaryInValue = 1251, - Zo_BinaryInactiveText = 1265, - Zo_BinaryMinimumOffTime = 1284, - Zo_BinaryMinimumOnTime = 1305, - Zo_BinaryOutActiveText = 1325, - Zo_BinaryOutApplicationType = 1345, - Zo_BinaryOutDescription = 1370, - Zo_BinaryOutInactiveText = 1391, - Zo_BinaryOutMinimumOffTime = 1413, - Zo_BinaryOutMinimumOnTime = 1437, - Zo_BinaryOutOfService = 1460, - Zo_BinaryOutOutOfService = 1479, - Zo_BinaryOutPolarity = 1501, - Zo_BinaryOutReliability = 1519, - Zo_BinaryOutRelinquishDefault = 1540, - Zo_BinaryOutStatusFlags = 1567, - Zo_BinaryOutValue = 1588, - Zo_BinaryReliability = 1603, - Zo_BinaryRelinquishDefault = 1621, - Zo_BinaryStatusFlags = 1645, - Zo_BinaryValue = 1663, - Zo_CIE = 1675, - Zo_CO = 1679, - Zo_CT = 1682, - Zo_CheckinInterval = 1685, - Zo_CheckinIntervalMin = 1701, - Zo_ClosedLimit = 1720, - Zo_Color = 1732, - Zo_ColorMode = 1738, - Zo_ColorMove = 1748, - Zo_ColorPointBIntensity = 1758, - Zo_ColorPointBX = 1779, - Zo_ColorPointBY = 1792, - Zo_ColorPointGIntensity = 1805, - Zo_ColorPointGX = 1826, - Zo_ColorPointGY = 1839, - Zo_ColorPointRIntensity = 1852, - Zo_ColorPointRX = 1873, - Zo_ColorPointRY = 1886, - Zo_ColorStep = 1899, - Zo_ColorTempMove = 1909, - Zo_ColorTempMoveDown = 1923, - Zo_ColorTempMoveStop = 1941, - Zo_ColorTempMoveUp = 1959, - Zo_ColorTempStep = 1975, - Zo_ColorTempStepDown = 1989, - Zo_ColorTempStepUp = 2007, - Zo_CompanyName = 2023, - Zo_CompensationText = 2035, - Zo_ConfigStatus = 2052, - Zo_Contact = 2065, - Zo_ControlSequenceOfOperation = 2073, - Zo_CurrentGroup = 2100, - Zo_CurrentPositionLift = 2113, - Zo_CurrentPositionLiftPercentage = 2133, - Zo_CurrentPositionTilt = 2163, - Zo_CurrentPositionTiltPercentage = 2183, - Zo_CurrentScene = 2213, - Zo_CurrentTemperature = 2226, - Zo_CurrentTemperatureSetPoint = 2245, - Zo_CustomerName = 2272, - Zo_DataQualityID = 2285, - Zo_DateCode = 2299, - Zo_DecelerationTimeLift = 2308, - Zo_Dimmer = 2329, - Zo_DimmerDown = 2336, - Zo_DimmerMove = 2347, - Zo_DimmerOptions = 2358, - Zo_DimmerRemainingTime = 2372, - Zo_DimmerStep = 2392, - Zo_DimmerStepDown = 2403, - Zo_DimmerStepUp = 2418, - Zo_DimmerStop = 2431, - Zo_DimmerUp = 2442, - Zo_DoorClosedEvents = 2451, - Zo_DoorOpenEvents = 2468, - Zo_DoorState = 2483, - Zo_DriftCompensation = 2493, - Zo_DstEnd = 2511, - Zo_DstShift = 2518, - Zo_DstStart = 2527, - Zo_EnergyFormatting = 2536, - Zo_EnergyRemote = 2553, - Zo_EnergyTotal = 2566, - Zo_EurotronicErrors = 2578, - Zo_EurotronicHostFlags = 2595, - Zo_FastPollTimeout = 2615, - Zo_FastPollTimeoutMax = 2631, - Zo_Fire = 2650, - Zo_FlowMaxMeasuredValue = 2655, - Zo_FlowMinMeasuredValue = 2676, - Zo_FlowRate = 2697, - Zo_FlowTolerance = 2706, - Zo_GenericDeviceClass = 2720, - Zo_GenericDeviceType = 2739, - Zo_GetAllGroups = 2757, - Zo_GetGroup = 2770, - Zo_GetSceneMembership = 2779, - Zo_GlassBreak = 2798, - Zo_GroupNameSupport = 2809, - Zo_HWVersion = 2826, - Zo_Hue = 2836, - Zo_HueMove = 2840, - Zo_HueSat = 2848, - Zo_HueStep = 2855, - Zo_HueStepDown = 2863, - Zo_HueStepUp = 2875, - Zo_Humidity = 2885, - Zo_HumidityMaxMeasuredValue = 2894, - Zo_HumidityMinMeasuredValue = 2919, - Zo_HumidityTolerance = 2944, - Zo_Identify = 2962, - Zo_IdentifyQuery = 2971, - Zo_IdentifyTime = 2985, - Zo_Illuminance = 2998, - Zo_IlluminanceLevelStatus = 3010, - Zo_IlluminanceLightSensorType = 3033, - Zo_IlluminanceMaxMeasuredValue = 3060, - Zo_IlluminanceMinMeasuredValue = 3088, - Zo_IlluminanceTargetLevel = 3116, - Zo_IlluminanceTolerance = 3139, - Zo_InstalledClosedLimitLift = 3160, - Zo_InstalledClosedLimitTilt = 3185, - Zo_InstalledOpenLimitLift = 3210, - Zo_InstalledOpenLimitTilt = 3233, - Zo_IntermediateSetpointsLift = 3256, - Zo_IntermediateSetpointsTilt = 3282, - Zo_LastMessageLQI = 3308, - Zo_LastMessageRSSI = 3323, - Zo_LastSetTime = 3339, - Zo_LegrandHeatingMode = 3351, - Zo_LegrandMode = 3370, - Zo_LegrandOpt1 = 3382, - Zo_LegrandOpt2 = 3394, - Zo_LegrandOpt3 = 3406, - Zo_LidlPower = 3418, - Zo_LocalTemperature = 3428, - Zo_LocalTemperatureCalibration = 3445, - Zo_LocalTime = 3473, - Zo_LocationAge = 3483, - Zo_LocationMethod = 3495, - Zo_LocationType = 3510, - Zo_LockState = 3523, - Zo_LockType = 3533, - Zo_LongPollInterval = 3542, - Zo_LongPollIntervalMin = 3559, - Zo_MainsFrequency = 3579, - Zo_MainsVoltage = 3594, - Zo_Manufacturer = 3607, - Zo_MaxTempExperienced = 3620, - Zo_MeterTypeID = 3639, - Zo_MinTempExperienced = 3651, - Zo_Mode = 3670, - Zo_Model = 3675, - Zo_ModelId = 3681, - Zo_MotorStepSize = 3689, - Zo_Movement = 3703, - Zo_MullerLightMode = 3712, - Zo_MultiApplicationType = 3728, - Zo_MultiDescription = 3749, - Zo_MultiInApplicationType = 3766, - Zo_MultiInDescription = 3789, - Zo_MultiInNumberOfStates = 3808, - Zo_MultiInOutOfService = 3830, - Zo_MultiInReliability = 3850, - Zo_MultiInStatusFlags = 3869, - Zo_MultiInValue = 3888, - Zo_MultiNumberOfStates = 3901, - Zo_MultiOutApplicationType = 3921, - Zo_MultiOutDescription = 3945, - Zo_MultiOutNumberOfStates = 3965, - Zo_MultiOutOfService = 3988, - Zo_MultiOutOutOfService = 4006, - Zo_MultiOutReliability = 4027, - Zo_MultiOutRelinquishDefault = 4047, - Zo_MultiOutStatusFlags = 4073, - Zo_MultiOutValue = 4093, - Zo_MultiReliability = 4107, - Zo_MultiRelinquishDefault = 4124, - Zo_MultiStatusFlags = 4147, - Zo_MultiValue = 4164, - Zo_MultipleScheduling = 4175, - Zo_NumberOfDevices = 4194, - Zo_NumberOfPrimaries = 4210, - Zo_NumberOfResets = 4228, - Zo_NumberofActuationsLift = 4243, - Zo_NumberofActuationsTilt = 4266, - Zo_Occupancy = 4289, - Zo_OccupancySensorType = 4299, - Zo_OccupiedCoolingSetpoint = 4319, - Zo_OccupiedHeatingSetpoint = 4343, - Zo_OnOffTransitionTime = 4367, - Zo_OpenPeriod = 4387, - Zo_OppleMode = 4398, - Zo_OutdoorTemperature = 4408, - Zo_OverTempTotalDwell = 4427, - Zo_PICoolingDemand = 4446, - Zo_PIHeatingDemand = 4462, - Zo_POD = 4478, - Zo_Panic = 4482, - Zo_PartNumber = 4488, - Zo_PersistentMemoryWrites = 4499, - Zo_PersonalAlarm = 4522, - Zo_PhysicalClosedLimit = 4536, - Zo_PhysicalClosedLimitLift = 4556, - Zo_PhysicalClosedLimitTilt = 4580, - Zo_Power = 4604, - Zo_Power2 = 4610, - Zo_Power3 = 4617, - Zo_Power4 = 4624, - Zo_PowerOffEffect = 4631, - Zo_PowerOnRecall = 4646, - Zo_PowerOnTimer = 4660, - Zo_PowerSource = 4673, - Zo_PowerThreshold = 4685, - Zo_Pressure = 4700, - Zo_PressureMaxMeasuredValue = 4709, - Zo_PressureMaxScaledValue = 4734, - Zo_PressureMinMeasuredValue = 4757, - Zo_PressureMinScaledValue = 4782, - Zo_PressureScale = 4805, - Zo_PressureScaledTolerance = 4819, - Zo_PressureScaledValue = 4843, - Zo_PressureTolerance = 4863, - Zo_Primary1Intensity = 4881, - Zo_Primary1X = 4899, - Zo_Primary1Y = 4909, - Zo_Primary2Intensity = 4919, - Zo_Primary2X = 4937, - Zo_Primary2Y = 4947, - Zo_Primary3Intensity = 4957, - Zo_Primary3X = 4975, - Zo_Primary3Y = 4985, - Zo_ProductCode = 4995, - Zo_ProductRevision = 5007, - Zo_ProductURL = 5023, - Zo_QualityMeasure = 5034, - Zo_RGB = 5049, - Zo_RMSCurrent = 5053, - Zo_RMSVoltage = 5064, - Zo_ReactivePower = 5075, - Zo_RecallScene = 5089, - Zo_RemainingTime = 5101, - Zo_RemoteSensing = 5115, - Zo_RemoveAllGroups = 5129, - Zo_RemoveAllScenes = 5145, - Zo_RemoveGroup = 5161, - Zo_RemoveScene = 5173, - Zo_ResetAlarm = 5185, - Zo_ResetAllAlarms = 5196, - Zo_SWBuildID = 5211, - Zo_Sat = 5221, - Zo_SatMove = 5225, - Zo_SatStep = 5233, - Zo_SceneCount = 5241, - Zo_SceneValid = 5252, - Zo_ScheduleMode = 5263, - Zo_SeaPressure = 5276, - Zo_ShortPollInterval = 5288, - Zo_Shutter = 5306, - Zo_ShutterClose = 5314, - Zo_ShutterLift = 5327, - Zo_ShutterOpen = 5339, - Zo_ShutterStop = 5351, - Zo_ShutterTilt = 5363, - Zo_SoftwareRevision = 5375, - Zo_StackVersion = 5392, - Zo_StandardTime = 5405, - Zo_StartUpOnOff = 5418, - Zo_Status = 5431, - Zo_StoreScene = 5438, - Zo_SwitchType = 5449, - Zo_SystemMode = 5460, - Zo_TRVBoost = 5471, - Zo_TRVChildProtection = 5480, - Zo_TRVMirrorDisplay = 5499, - Zo_TRVMode = 5516, - Zo_TRVWindowOpen = 5524, - Zo_TempTarget = 5538, - Zo_Temperature = 5549, - Zo_TemperatureMaxMeasuredValue = 5561, - Zo_TemperatureMinMeasuredValue = 5589, - Zo_TemperatureTolerance = 5617, - Zo_TerncyDuration = 5638, - Zo_TerncyRotate = 5653, - Zo_ThSetpoint = 5666, - Zo_Time = 5677, - Zo_TimeEpoch = 5682, - Zo_TimeStatus = 5692, - Zo_TimeZone = 5703, - Zo_TotalProfileNum = 5712, - Zo_TuyaAutoLock = 5728, - Zo_TuyaAwayDays = 5741, - Zo_TuyaAwayTemp = 5754, - Zo_TuyaBattery = 5767, - Zo_TuyaBoostTime = 5779, - Zo_TuyaCalibration = 5793, - Zo_TuyaCalibrationTime = 5809, - Zo_TuyaChildLock = 5829, - Zo_TuyaComfortTemp = 5843, - Zo_TuyaEcoTemp = 5859, - Zo_TuyaFanMode = 5871, - Zo_TuyaForceMode = 5883, - Zo_TuyaMaxTemp = 5897, - Zo_TuyaMinTemp = 5909, - Zo_TuyaMotorReversal = 5921, - Zo_TuyaMovingState = 5939, - Zo_TuyaPreset = 5955, - Zo_TuyaScheduleHolidays = 5966, - Zo_TuyaScheduleWorkdays = 5987, - Zo_TuyaTempTarget = 6008, - Zo_TuyaValveDetection = 6023, - Zo_TuyaValvePosition = 6042, - Zo_TuyaWeekSelect = 6060, - Zo_TuyaWindowDetection = 6075, - Zo_UnoccupiedCoolingSetpoint = 6095, - Zo_UnoccupiedHeatingSetpoint = 6121, - Zo_UtilityName = 6147, - Zo_ValidUntilTime = 6159, - Zo_ValvePosition = 6174, - Zo_VelocityLift = 6188, - Zo_ViewGroup = 6201, - Zo_ViewScene = 6211, - Zo_Water = 6221, - Zo_WhitePointX = 6227, - Zo_WhitePointY = 6239, - Zo_WindowCoveringType = 6251, - Zo_X = 6270, - Zo_Y = 6272, - Zo_ZCLVersion = 6274, - Zo_ZoneState = 6285, - Zo_ZoneStatus = 6295, - Zo_ZoneStatusChange = 6306, - Zo_ZoneType = 6323, - Zo__ = 6332, - Zo_xx = 6334, - Zo_xx000A00 = 6337, - Zo_xx0A = 6346, - Zo_xx0A00 = 6351, - Zo_xx19 = 6358, - Zo_xx190A = 6363, - Zo_xx190A00 = 6370, - Zo_xxxx = 6379, - Zo_xxxx00 = 6384, - Zo_xxxx0A00 = 6391, - Zo_xxxxyy = 6400, - Zo_xxxxyyyy = 6407, - Zo_xxxxyyyy0A00 = 6416, - Zo_xxxxyyzz = 6429, - Zo_xxyy = 6438, - Zo_xxyy0A00 = 6443, - Zo_xxyyyy = 6452, - Zo_xxyyyy000000000000 = 6459, - Zo_xxyyyy0A0000000000 = 6478, - Zo_xxyyyyzz = 6497, - Zo_xxyyyyzzzz = 6506, - Zo_xxyyzzzz = 6517, + Zo_0101 = 44, + Zo_01190200 = 49, + Zo_01xx0A00 = 58, + Zo_01xxxx = 67, + Zo_01xxxx000000000000 = 74, + Zo_01xxxx0A0000000000 = 93, + Zo_03xx0A00 = 112, + Zo_03xxxx000000000000 = 121, + Zo_03xxxx0A0000000000 = 140, + Zo_AccelerationTimeLift = 159, + Zo_ActivePower = 180, + Zo_ActuatorEnabled = 192, + Zo_AddGroup = 208, + Zo_AddScene = 217, + Zo_AlarmCount = 226, + Zo_AnalogApplicationType = 237, + Zo_AnalogDescription = 259, + Zo_AnalogEngineeringUnits = 277, + Zo_AnalogInApplicationType = 300, + Zo_AnalogInDescription = 324, + Zo_AnalogInEngineeringUnits = 344, + Zo_AnalogInMaxValue = 369, + Zo_AnalogInMinValue = 386, + Zo_AnalogInOutOfService = 403, + Zo_AnalogInReliability = 424, + Zo_AnalogInResolution = 444, + Zo_AnalogInStatusFlags = 463, + Zo_AnalogOutApplicationType = 483, + Zo_AnalogOutDescription = 508, + Zo_AnalogOutEngineeringUnits = 529, + Zo_AnalogOutMaxValue = 555, + Zo_AnalogOutMinValue = 573, + Zo_AnalogOutOfService = 591, + Zo_AnalogOutOutOfService = 610, + Zo_AnalogOutReliability = 632, + Zo_AnalogOutRelinquishDefault = 653, + Zo_AnalogOutResolution = 680, + Zo_AnalogOutStatusFlags = 700, + Zo_AnalogOutValue = 721, + Zo_AnalogPriorityArray = 736, + Zo_AnalogReliability = 756, + Zo_AnalogRelinquishDefault = 774, + Zo_AnalogStatusFlags = 798, + Zo_AnalogValue = 816, + Zo_AppVersion = 828, + Zo_ApparentPower = 839, + Zo_AqaraAccelerometer = 853, + Zo_AqaraRotate = 872, + Zo_AqaraVibration505 = 884, + Zo_AqaraVibrationMode = 902, + Zo_AqaraVibrationsOrAngle = 921, + Zo_Aqara_FF05 = 944, + Zo_ArrowClick = 955, + Zo_ArrowHold = 966, + Zo_ArrowRelease = 976, + Zo_AvailablePower = 989, + Zo_BatteryPercentage = 1004, + Zo_BatteryVoltage = 1022, + Zo_BinaryActiveText = 1037, + Zo_BinaryApplicationType = 1054, + Zo_BinaryDescription = 1076, + Zo_BinaryInActiveText = 1094, + Zo_BinaryInApplicationType = 1113, + Zo_BinaryInDescription = 1137, + Zo_BinaryInInactiveText = 1157, + Zo_BinaryInOutOfService = 1178, + Zo_BinaryInPolarity = 1199, + Zo_BinaryInReliability = 1216, + Zo_BinaryInStatusFlags = 1236, + Zo_BinaryInValue = 1256, + Zo_BinaryInactiveText = 1270, + Zo_BinaryMinimumOffTime = 1289, + Zo_BinaryMinimumOnTime = 1310, + Zo_BinaryOutActiveText = 1330, + Zo_BinaryOutApplicationType = 1350, + Zo_BinaryOutDescription = 1375, + Zo_BinaryOutInactiveText = 1396, + Zo_BinaryOutMinimumOffTime = 1418, + Zo_BinaryOutMinimumOnTime = 1442, + Zo_BinaryOutOfService = 1465, + Zo_BinaryOutOutOfService = 1484, + Zo_BinaryOutPolarity = 1506, + Zo_BinaryOutReliability = 1524, + Zo_BinaryOutRelinquishDefault = 1545, + Zo_BinaryOutStatusFlags = 1572, + Zo_BinaryOutValue = 1593, + Zo_BinaryReliability = 1608, + Zo_BinaryRelinquishDefault = 1626, + Zo_BinaryStatusFlags = 1650, + Zo_BinaryValue = 1668, + Zo_CIE = 1680, + Zo_CO = 1684, + Zo_CT = 1687, + Zo_CheckinInterval = 1690, + Zo_CheckinIntervalMin = 1706, + Zo_ClosedLimit = 1725, + Zo_Color = 1737, + Zo_ColorMode = 1743, + Zo_ColorMove = 1753, + Zo_ColorPointBIntensity = 1763, + Zo_ColorPointBX = 1784, + Zo_ColorPointBY = 1797, + Zo_ColorPointGIntensity = 1810, + Zo_ColorPointGX = 1831, + Zo_ColorPointGY = 1844, + Zo_ColorPointRIntensity = 1857, + Zo_ColorPointRX = 1878, + Zo_ColorPointRY = 1891, + Zo_ColorStep = 1904, + Zo_ColorTempMove = 1914, + Zo_ColorTempMoveDown = 1928, + Zo_ColorTempMoveStop = 1946, + Zo_ColorTempMoveUp = 1964, + Zo_ColorTempStep = 1980, + Zo_ColorTempStepDown = 1994, + Zo_ColorTempStepUp = 2012, + Zo_CompanyName = 2028, + Zo_CompensationText = 2040, + Zo_ConfigStatus = 2057, + Zo_Contact = 2070, + Zo_ControlSequenceOfOperation = 2078, + Zo_CurrentGroup = 2105, + Zo_CurrentPositionLift = 2118, + Zo_CurrentPositionLiftPercentage = 2138, + Zo_CurrentPositionTilt = 2168, + Zo_CurrentPositionTiltPercentage = 2188, + Zo_CurrentScene = 2218, + Zo_CurrentTemperature = 2231, + Zo_CurrentTemperatureSetPoint = 2250, + Zo_CustomerName = 2277, + Zo_DataQualityID = 2290, + Zo_DateCode = 2304, + Zo_DecelerationTimeLift = 2313, + Zo_Dimmer = 2334, + Zo_DimmerDown = 2341, + Zo_DimmerMove = 2352, + Zo_DimmerOptions = 2363, + Zo_DimmerRemainingTime = 2377, + Zo_DimmerStep = 2397, + Zo_DimmerStepDown = 2408, + Zo_DimmerStepUp = 2423, + Zo_DimmerStop = 2436, + Zo_DimmerUp = 2447, + Zo_DoorClosedEvents = 2456, + Zo_DoorOpenEvents = 2473, + Zo_DoorState = 2488, + Zo_DriftCompensation = 2498, + Zo_DstEnd = 2516, + Zo_DstShift = 2523, + Zo_DstStart = 2532, + Zo_EnergyFormatting = 2541, + Zo_EnergyRemote = 2558, + Zo_EnergyTotal = 2571, + Zo_EurotronicErrors = 2583, + Zo_EurotronicHostFlags = 2600, + Zo_FastPollTimeout = 2620, + Zo_FastPollTimeoutMax = 2636, + Zo_Fire = 2655, + Zo_FlowMaxMeasuredValue = 2660, + Zo_FlowMinMeasuredValue = 2681, + Zo_FlowRate = 2702, + Zo_FlowTolerance = 2711, + Zo_GenericDeviceClass = 2725, + Zo_GenericDeviceType = 2744, + Zo_GetAllGroups = 2762, + Zo_GetGroup = 2775, + Zo_GetSceneMembership = 2784, + Zo_GlassBreak = 2803, + Zo_GroupNameSupport = 2814, + Zo_HWVersion = 2831, + Zo_Hue = 2841, + Zo_HueMove = 2845, + Zo_HueSat = 2853, + Zo_HueStep = 2860, + Zo_HueStepDown = 2868, + Zo_HueStepUp = 2880, + Zo_Humidity = 2890, + Zo_HumidityMaxMeasuredValue = 2899, + Zo_HumidityMinMeasuredValue = 2924, + Zo_HumidityTolerance = 2949, + Zo_Identify = 2967, + Zo_IdentifyQuery = 2976, + Zo_IdentifyTime = 2990, + Zo_Illuminance = 3003, + Zo_IlluminanceLevelStatus = 3015, + Zo_IlluminanceLightSensorType = 3038, + Zo_IlluminanceMaxMeasuredValue = 3065, + Zo_IlluminanceMinMeasuredValue = 3093, + Zo_IlluminanceTargetLevel = 3121, + Zo_IlluminanceTolerance = 3144, + Zo_InstalledClosedLimitLift = 3165, + Zo_InstalledClosedLimitTilt = 3190, + Zo_InstalledOpenLimitLift = 3215, + Zo_InstalledOpenLimitTilt = 3238, + Zo_IntermediateSetpointsLift = 3261, + Zo_IntermediateSetpointsTilt = 3287, + Zo_LastMessageLQI = 3313, + Zo_LastMessageRSSI = 3328, + Zo_LastSetTime = 3344, + Zo_LegrandHeatingMode = 3356, + Zo_LegrandMode = 3375, + Zo_LegrandOpt1 = 3387, + Zo_LegrandOpt2 = 3399, + Zo_LegrandOpt3 = 3411, + Zo_LidlPower = 3423, + Zo_LocalTemperature = 3433, + Zo_LocalTemperatureCalibration = 3450, + Zo_LocalTime = 3478, + Zo_LocationAge = 3488, + Zo_LocationMethod = 3500, + Zo_LocationType = 3515, + Zo_LockState = 3528, + Zo_LockType = 3538, + Zo_LongPollInterval = 3547, + Zo_LongPollIntervalMin = 3564, + Zo_MainsFrequency = 3584, + Zo_MainsVoltage = 3599, + Zo_Manufacturer = 3612, + Zo_MaxTempExperienced = 3625, + Zo_MeterTypeID = 3644, + Zo_MinTempExperienced = 3656, + Zo_Mode = 3675, + Zo_Model = 3680, + Zo_ModelId = 3686, + Zo_MotorStepSize = 3694, + Zo_Movement = 3708, + Zo_MullerLightMode = 3717, + Zo_MultiApplicationType = 3733, + Zo_MultiDescription = 3754, + Zo_MultiInApplicationType = 3771, + Zo_MultiInDescription = 3794, + Zo_MultiInNumberOfStates = 3813, + Zo_MultiInOutOfService = 3835, + Zo_MultiInReliability = 3855, + Zo_MultiInStatusFlags = 3874, + Zo_MultiInValue = 3893, + Zo_MultiNumberOfStates = 3906, + Zo_MultiOutApplicationType = 3926, + Zo_MultiOutDescription = 3950, + Zo_MultiOutNumberOfStates = 3970, + Zo_MultiOutOfService = 3993, + Zo_MultiOutOutOfService = 4011, + Zo_MultiOutReliability = 4032, + Zo_MultiOutRelinquishDefault = 4052, + Zo_MultiOutStatusFlags = 4078, + Zo_MultiOutValue = 4098, + Zo_MultiReliability = 4112, + Zo_MultiRelinquishDefault = 4129, + Zo_MultiStatusFlags = 4152, + Zo_MultiValue = 4169, + Zo_MultipleScheduling = 4180, + Zo_NumberOfDevices = 4199, + Zo_NumberOfPrimaries = 4215, + Zo_NumberOfResets = 4233, + Zo_NumberofActuationsLift = 4248, + Zo_NumberofActuationsTilt = 4271, + Zo_Occupancy = 4294, + Zo_OccupancySensorType = 4304, + Zo_OccupiedCoolingSetpoint = 4324, + Zo_OccupiedHeatingSetpoint = 4348, + Zo_OnOffTransitionTime = 4372, + Zo_OpenPeriod = 4392, + Zo_OppleMode = 4403, + Zo_OutdoorTemperature = 4413, + Zo_OverTempTotalDwell = 4432, + Zo_PICoolingDemand = 4451, + Zo_PIHeatingDemand = 4467, + Zo_POD = 4483, + Zo_Panic = 4487, + Zo_PartNumber = 4493, + Zo_PersistentMemoryWrites = 4504, + Zo_PersonalAlarm = 4527, + Zo_PhysicalClosedLimit = 4541, + Zo_PhysicalClosedLimitLift = 4561, + Zo_PhysicalClosedLimitTilt = 4585, + Zo_Power = 4609, + Zo_Power2 = 4615, + Zo_Power3 = 4622, + Zo_Power4 = 4629, + Zo_PowerOffEffect = 4636, + Zo_PowerOnRecall = 4651, + Zo_PowerOnTimer = 4665, + Zo_PowerSource = 4678, + Zo_PowerThreshold = 4690, + Zo_Pressure = 4705, + Zo_PressureMaxMeasuredValue = 4714, + Zo_PressureMaxScaledValue = 4739, + Zo_PressureMinMeasuredValue = 4762, + Zo_PressureMinScaledValue = 4787, + Zo_PressureScale = 4810, + Zo_PressureScaledTolerance = 4824, + Zo_PressureScaledValue = 4848, + Zo_PressureTolerance = 4868, + Zo_Primary1Intensity = 4886, + Zo_Primary1X = 4904, + Zo_Primary1Y = 4914, + Zo_Primary2Intensity = 4924, + Zo_Primary2X = 4942, + Zo_Primary2Y = 4952, + Zo_Primary3Intensity = 4962, + Zo_Primary3X = 4980, + Zo_Primary3Y = 4990, + Zo_ProductCode = 5000, + Zo_ProductRevision = 5012, + Zo_ProductURL = 5028, + Zo_QualityMeasure = 5039, + Zo_RGB = 5054, + Zo_RMSCurrent = 5058, + Zo_RMSVoltage = 5069, + Zo_ReactivePower = 5080, + Zo_RecallScene = 5094, + Zo_RemainingTime = 5106, + Zo_RemoteSensing = 5120, + Zo_RemoveAllGroups = 5134, + Zo_RemoveAllScenes = 5150, + Zo_RemoveGroup = 5166, + Zo_RemoveScene = 5178, + Zo_ResetAlarm = 5190, + Zo_ResetAllAlarms = 5201, + Zo_SWBuildID = 5216, + Zo_Sat = 5226, + Zo_SatMove = 5230, + Zo_SatStep = 5238, + Zo_SceneCount = 5246, + Zo_SceneValid = 5257, + Zo_ScheduleMode = 5268, + Zo_SeaPressure = 5281, + Zo_ShortPollInterval = 5293, + Zo_Shutter = 5311, + Zo_ShutterClose = 5319, + Zo_ShutterLift = 5332, + Zo_ShutterOpen = 5344, + Zo_ShutterStop = 5356, + Zo_ShutterTilt = 5368, + Zo_SoftwareRevision = 5380, + Zo_StackVersion = 5397, + Zo_StandardTime = 5410, + Zo_StartUpOnOff = 5423, + Zo_Status = 5436, + Zo_StoreScene = 5443, + Zo_SwitchType = 5454, + Zo_SystemMode = 5465, + Zo_TRVBoost = 5476, + Zo_TRVChildProtection = 5485, + Zo_TRVMirrorDisplay = 5504, + Zo_TRVMode = 5521, + Zo_TRVWindowOpen = 5529, + Zo_TempTarget = 5543, + Zo_Temperature = 5554, + Zo_TemperatureMaxMeasuredValue = 5566, + Zo_TemperatureMinMeasuredValue = 5594, + Zo_TemperatureTolerance = 5622, + Zo_TerncyDuration = 5643, + Zo_TerncyRotate = 5658, + Zo_ThSetpoint = 5671, + Zo_Time = 5682, + Zo_TimeEpoch = 5687, + Zo_TimeStatus = 5697, + Zo_TimeZone = 5708, + Zo_TotalProfileNum = 5717, + Zo_TuyaAutoLock = 5733, + Zo_TuyaAwayDays = 5746, + Zo_TuyaAwayTemp = 5759, + Zo_TuyaBattery = 5772, + Zo_TuyaBoostTime = 5784, + Zo_TuyaCalibration = 5798, + Zo_TuyaCalibrationTime = 5814, + Zo_TuyaChildLock = 5834, + Zo_TuyaComfortTemp = 5848, + Zo_TuyaEcoTemp = 5864, + Zo_TuyaFanMode = 5876, + Zo_TuyaForceMode = 5888, + Zo_TuyaMCUVersion = 5902, + Zo_TuyaMaxTemp = 5917, + Zo_TuyaMinTemp = 5929, + Zo_TuyaMotorReversal = 5941, + Zo_TuyaMovingState = 5959, + Zo_TuyaPreset = 5975, + Zo_TuyaQuery = 5986, + Zo_TuyaScheduleHolidays = 5996, + Zo_TuyaScheduleWorkdays = 6017, + Zo_TuyaTempTarget = 6038, + Zo_TuyaValveDetection = 6053, + Zo_TuyaValvePosition = 6072, + Zo_TuyaWeekSelect = 6090, + Zo_TuyaWindowDetection = 6105, + Zo_UnoccupiedCoolingSetpoint = 6125, + Zo_UnoccupiedHeatingSetpoint = 6151, + Zo_UtilityName = 6177, + Zo_ValidUntilTime = 6189, + Zo_ValvePosition = 6204, + Zo_VelocityLift = 6218, + Zo_ViewGroup = 6231, + Zo_ViewScene = 6241, + Zo_Water = 6251, + Zo_WhitePointX = 6257, + Zo_WhitePointY = 6269, + Zo_WindowCoveringType = 6281, + Zo_X = 6300, + Zo_Y = 6302, + Zo_ZCLVersion = 6304, + Zo_ZoneState = 6315, + Zo_ZoneStatus = 6325, + Zo_ZoneStatusChange = 6336, + Zo_ZoneType = 6353, + Zo__ = 6362, + Zo_xx = 6364, + Zo_xx000A00 = 6367, + Zo_xx0A = 6376, + Zo_xx0A00 = 6381, + Zo_xx19 = 6388, + Zo_xx190A = 6393, + Zo_xx190A00 = 6400, + Zo_xxxx = 6409, + Zo_xxxx00 = 6414, + Zo_xxxx0A00 = 6421, + Zo_xxxxyy = 6430, + Zo_xxxxyyyy = 6437, + Zo_xxxxyyyy0A00 = 6446, + Zo_xxxxyyzz = 6459, + Zo_xxyy = 6468, + Zo_xxyy0A00 = 6473, + Zo_xxyyyy = 6482, + Zo_xxyyyy000000000000 = 6489, + Zo_xxyyyy0A0000000000 = 6508, + Zo_xxyyyyzz = 6527, + Zo_xxyyyyzzzz = 6536, + Zo_xxyyzzzz = 6547, }; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino index 5c4ad28f8..f951e09e9 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_6_commands.ino @@ -91,8 +91,11 @@ const Z_CommandConverter Z_Commands[] PROGMEM = { { Z_(Shutter), 0x0102, 0xFF, 0x01, Z_() }, // Legrand - Manuf 1021 { Z_(LegrandMode), 0xFC40, 0x00, 0x01, Z_(xx) }, + // Tuya Generix + { Z_(TuyaQuery), 0xEF00, 0x03, 0x01, Z_()}, // Generic Tuya command to ask for reporting of all attributes + { Z_(TuyaMCUVersion), 0xEF00, 0x10, 0x01, Z_(0101)}, // Generic Tuya command to ask MCU version (includes a dummy seq number for now) // Blitzwolf PIR - { Z_(Occupancy), 0xEF00, 0x01, 0x82, Z_()}, // Specific decoder for Blitzwolf PIR, empty name means special treatment + { Z_(Occupancy), 0xEF00, 0x01, 0x82, Z_()}, // Specific decoder for Blitzwolf PIR, empty name means special treatment // Decoders only - normally not used to send, and names may be masked by previous definitions { Z_(Dimmer), 0x0008, 0x00, 0x01, Z_(xx) }, { Z_(DimmerMove), 0x0008, 0x01, 0x01, Z_(xx0A) }, diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino index 631f77f40..c7182d609 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino @@ -215,6 +215,8 @@ void zigbeeZCLSendCmd(class ZCLFrame &zcl) { } } +// Definitive doc for Tuya protocol: +// https://developer.tuya.com/en/docs/iot-device-dev/tuya-zigbee-universal-docking-access-standard?id=K9ik6zvofpzql#subtitle-6-Private%20cluster // Special encoding for multiplier: // multiplier == 0: ignore // multiplier == 1: ignore @@ -246,8 +248,8 @@ bool ZbTuyaWrite(SBuffer & buf, const Z_attribute & attr) { uint8_t tuyatype = (attr.key.id.attr_id >> 8); uint8_t dpid = (attr.key.id.attr_id & 0xFF); - buf.add8(tuyatype); buf.add8(dpid); + buf.add8(tuyatype); // the next attribute is length 16 bits in big endian // high byte is always 0x00 From f9707217e6a2e1f7ad4ce1f87d765e01fc71e758 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 9 Aug 2022 11:46:25 +0200 Subject: [PATCH 205/219] Move DHT ESP32 lib to ESP32 lib --- lib/{default => libesp32}/DHT-sensor-library/.gitignore | 0 .../DHT-sensor-library/CONTRIBUTING.md | 0 lib/{default => libesp32}/DHT-sensor-library/DHT.cpp | 0 lib/{default => libesp32}/DHT-sensor-library/DHT.h | 0 lib/{default => libesp32}/DHT-sensor-library/README.md | 0 .../DHT-sensor-library/code-of-conduct.md | 0 .../examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino | 0 .../DHT-sensor-library/examples/DHTtester/DHTtester.ino | 0 lib/{default => libesp32}/DHT-sensor-library/keywords.txt | 0 .../DHT-sensor-library/library.properties | 2 +- lib/{default => libesp32}/DHT-sensor-library/license.txt | 0 platformio_override_sample.ini | 6 ++++-- 12 files changed, 5 insertions(+), 3 deletions(-) rename lib/{default => libesp32}/DHT-sensor-library/.gitignore (100%) rename lib/{default => libesp32}/DHT-sensor-library/CONTRIBUTING.md (100%) rename lib/{default => libesp32}/DHT-sensor-library/DHT.cpp (100%) rename lib/{default => libesp32}/DHT-sensor-library/DHT.h (100%) rename lib/{default => libesp32}/DHT-sensor-library/README.md (100%) rename lib/{default => libesp32}/DHT-sensor-library/code-of-conduct.md (100%) rename lib/{default => libesp32}/DHT-sensor-library/examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino (100%) rename lib/{default => libesp32}/DHT-sensor-library/examples/DHTtester/DHTtester.ino (100%) rename lib/{default => libesp32}/DHT-sensor-library/keywords.txt (100%) rename lib/{default => libesp32}/DHT-sensor-library/library.properties (94%) rename lib/{default => libesp32}/DHT-sensor-library/license.txt (100%) diff --git a/lib/default/DHT-sensor-library/.gitignore b/lib/libesp32/DHT-sensor-library/.gitignore similarity index 100% rename from lib/default/DHT-sensor-library/.gitignore rename to lib/libesp32/DHT-sensor-library/.gitignore diff --git a/lib/default/DHT-sensor-library/CONTRIBUTING.md b/lib/libesp32/DHT-sensor-library/CONTRIBUTING.md similarity index 100% rename from lib/default/DHT-sensor-library/CONTRIBUTING.md rename to lib/libesp32/DHT-sensor-library/CONTRIBUTING.md diff --git a/lib/default/DHT-sensor-library/DHT.cpp b/lib/libesp32/DHT-sensor-library/DHT.cpp similarity index 100% rename from lib/default/DHT-sensor-library/DHT.cpp rename to lib/libesp32/DHT-sensor-library/DHT.cpp diff --git a/lib/default/DHT-sensor-library/DHT.h b/lib/libesp32/DHT-sensor-library/DHT.h similarity index 100% rename from lib/default/DHT-sensor-library/DHT.h rename to lib/libesp32/DHT-sensor-library/DHT.h diff --git a/lib/default/DHT-sensor-library/README.md b/lib/libesp32/DHT-sensor-library/README.md similarity index 100% rename from lib/default/DHT-sensor-library/README.md rename to lib/libesp32/DHT-sensor-library/README.md diff --git a/lib/default/DHT-sensor-library/code-of-conduct.md b/lib/libesp32/DHT-sensor-library/code-of-conduct.md similarity index 100% rename from lib/default/DHT-sensor-library/code-of-conduct.md rename to lib/libesp32/DHT-sensor-library/code-of-conduct.md diff --git a/lib/default/DHT-sensor-library/examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino b/lib/libesp32/DHT-sensor-library/examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino similarity index 100% rename from lib/default/DHT-sensor-library/examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino rename to lib/libesp32/DHT-sensor-library/examples/DHT_Unified_Sensor/DHT_Unified_Sensor.ino diff --git a/lib/default/DHT-sensor-library/examples/DHTtester/DHTtester.ino b/lib/libesp32/DHT-sensor-library/examples/DHTtester/DHTtester.ino similarity index 100% rename from lib/default/DHT-sensor-library/examples/DHTtester/DHTtester.ino rename to lib/libesp32/DHT-sensor-library/examples/DHTtester/DHTtester.ino diff --git a/lib/default/DHT-sensor-library/keywords.txt b/lib/libesp32/DHT-sensor-library/keywords.txt similarity index 100% rename from lib/default/DHT-sensor-library/keywords.txt rename to lib/libesp32/DHT-sensor-library/keywords.txt diff --git a/lib/default/DHT-sensor-library/library.properties b/lib/libesp32/DHT-sensor-library/library.properties similarity index 94% rename from lib/default/DHT-sensor-library/library.properties rename to lib/libesp32/DHT-sensor-library/library.properties index 5c24c5aa9..f97eda3a6 100644 --- a/lib/default/DHT-sensor-library/library.properties +++ b/lib/libesp32/DHT-sensor-library/library.properties @@ -6,5 +6,5 @@ sentence=Arduino library for DHT11, DHT22, etc Temp & Humidity Sensors paragraph=Arduino library for DHT11, DHT22, etc Temp & Humidity Sensors category=Sensors url=https://github.com/adafruit/DHT-sensor-library -architectures=* +architectures=esp32 depends=Adafruit Unified Sensor diff --git a/lib/default/DHT-sensor-library/license.txt b/lib/libesp32/DHT-sensor-library/license.txt similarity index 100% rename from lib/default/DHT-sensor-library/license.txt rename to lib/libesp32/DHT-sensor-library/license.txt diff --git a/platformio_override_sample.ini b/platformio_override_sample.ini index 5549503f0..c2c8f16fa 100644 --- a/platformio_override_sample.ini +++ b/platformio_override_sample.ini @@ -96,8 +96,8 @@ lib_extra_dirs = ${library.lib_extra_dirs} [env:tasmota32_base] ; *** Uncomment next lines ";" to enable development Tasmota Arduino version ESP32 -;platform = https://github.com/tasmota/platform-espressif32/releases/download/v.2.0.3/platform-espressif32-v.2.0.3.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/847/framework-arduinoespressif32-v4.4_dev-d327997163.tar.gz +;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/927/framework-arduinoespressif32-442_esp41-13eca437d1.tar.gz build_unflags = ${esp32_defaults.build_unflags} build_flags = ${esp32_defaults.build_flags} @@ -139,6 +139,8 @@ lib_extra_dirs = ${library.lib_extra_dirs} lib/libesp32 ; *** comment the following line if you dont use LVGL in a Tasmota32 build. Reduces compile time lib/libesp32_lvgl +; *** comment the following line if you dont use ESP32 Audio in a Tasmota32 build. Reduces compile time +; lib/libesp32_audio ; *** uncomment the following line if you use Bluetooth or Apple Homekit in a Tasmota32 build. Reduces compile time ; lib/libesp32_div ; *** uncomment the following line if you use Epaper driver epidy in your Tasmota32 build. Reduces compile time From ab05511f4d5e8a46e4ba724d1cdf4551db8c619e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:06:07 +0200 Subject: [PATCH 206/219] Ignore not needed libs for safeboot --- platformio_tasmota_env32.ini | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index d8e90f58c..758626383 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -50,6 +50,8 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy + ESP Mail Client + DHT sensor library [env:tasmota32] extends = env:tasmota32_base @@ -121,6 +123,8 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy + ESP Mail Client + DHT sensor library [env:tasmota32-zbbrdgpro] extends = env:tasmota32_base @@ -158,7 +162,8 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy - mp3_shine_esp32 + ESP Mail Client + DHT sensor library [env:tasmota32c3] extends = env:tasmota32_base @@ -192,6 +197,8 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy + ESP Mail Client + DHT sensor library [env:tasmota32s2] extends = env:tasmota32_base @@ -221,6 +228,8 @@ lib_ignore = NimBLE-Arduino Micro-RTSP epdiy + ESP Mail Client + DHT sensor library [env:tasmota32s3] extends = env:tasmota32_base From e8b5c81d385e89aacd9c1de09e61b290d62a05f4 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:21:27 +0200 Subject: [PATCH 207/219] rm unneeded ignore --- platformio_tasmota_env32.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 758626383..9286e8fc1 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -49,7 +49,6 @@ lib_ignore = TTGO TWatch Library NimBLE-Arduino Micro-RTSP - epdiy ESP Mail Client DHT sensor library @@ -122,7 +121,6 @@ lib_ignore = TTGO TWatch Library NimBLE-Arduino Micro-RTSP - epdiy ESP Mail Client DHT sensor library @@ -161,7 +159,6 @@ lib_ignore = TTGO TWatch Library NimBLE-Arduino Micro-RTSP - epdiy ESP Mail Client DHT sensor library @@ -196,7 +193,6 @@ lib_ignore = TTGO TWatch Library NimBLE-Arduino Micro-RTSP - epdiy ESP Mail Client DHT sensor library @@ -227,7 +223,6 @@ lib_ignore = TTGO TWatch Library NimBLE-Arduino Micro-RTSP - epdiy ESP Mail Client DHT sensor library From 0b53b545873fc9dd8c30c6473b05a980c2523282 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:27:08 +0200 Subject: [PATCH 208/219] remove redundant NimBLE ignore --- platformio_tasmota_env32.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index 9286e8fc1..ad3a1e4c8 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -47,7 +47,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = TTGO TWatch Library - NimBLE-Arduino Micro-RTSP ESP Mail Client DHT sensor library @@ -119,7 +118,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = TTGO TWatch Library - NimBLE-Arduino Micro-RTSP ESP Mail Client DHT sensor library @@ -157,7 +155,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = TTGO TWatch Library - NimBLE-Arduino Micro-RTSP ESP Mail Client DHT sensor library @@ -191,7 +188,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = TTGO TWatch Library - NimBLE-Arduino Micro-RTSP ESP Mail Client DHT sensor library @@ -221,7 +217,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = TTGO TWatch Library - NimBLE-Arduino Micro-RTSP ESP Mail Client DHT sensor library From 597a0ec45a8ad3c2c44956500ffd7174a6ec457a Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Tue, 9 Aug 2022 12:31:21 +0200 Subject: [PATCH 209/219] entry TTGO TWatch Library is redundant too --- platformio_tasmota_env32.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/platformio_tasmota_env32.ini b/platformio_tasmota_env32.ini index ad3a1e4c8..bcec4502b 100644 --- a/platformio_tasmota_env32.ini +++ b/platformio_tasmota_env32.ini @@ -46,7 +46,6 @@ extends = env:tasmota32_base build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = - TTGO TWatch Library Micro-RTSP ESP Mail Client DHT sensor library @@ -117,7 +116,6 @@ platform_packages = ${core32solo1.platform_packages} build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = - TTGO TWatch Library Micro-RTSP ESP Mail Client DHT sensor library @@ -154,7 +152,6 @@ build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT -fno-lto lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = - TTGO TWatch Library Micro-RTSP ESP Mail Client DHT sensor library @@ -187,7 +184,6 @@ board = esp32s2 build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = - TTGO TWatch Library Micro-RTSP ESP Mail Client DHT sensor library @@ -216,7 +212,6 @@ board = esp32s3 build_flags = ${env:tasmota32_base.build_flags} -DFIRMWARE_SAFEBOOT lib_extra_dirs = lib/lib_ssl, lib/libesp32 lib_ignore = - TTGO TWatch Library Micro-RTSP ESP Mail Client DHT sensor library From 55d8c859d9aadd8d6ff5d728371c27193305a35d Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 9 Aug 2022 17:39:28 +0200 Subject: [PATCH 210/219] Zigbee fix Aqara wrong battery --- .../xdrv_23_zigbee_5_converters.ino | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino index dda3b7c4a..94095188d 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_5_converters.ino @@ -2102,7 +2102,22 @@ void Z_postProcessAttributes(uint16_t shortaddr, uint16_t src_ep, class Z_attrib switch (ccccaaaa) { case 0x00000004: device.setManufId(attr.getStr()); break; case 0x00000005: device.setModelId(attr.getStr()); break; - case 0x00010021: device.setBatteryPercent(uval16 / 2); break; + case 0x00010021: + { + // if both BatteryVoltage and BatteryPercentage are zero, ignore + // Behavior seen on Aqara device when requesting cluster 0x0001 + bool valid_batt_voltage = true; + const Z_attribute * battVoltage_attr = attr_list.findAttribute(0x0001, 0x0020); + if (battVoltage_attr != nullptr) { + if (battVoltage_attr->getUInt() == 0) { + valid_batt_voltage = false; + } + } + if (valid_batt_voltage || uval16 > 0) { + device.setBatteryPercent(uval16 / 2); // set value only if both are not zero + } + } + break; case 0x00060000: case 0x00068000: device.setPower(attr.getBool(), src_ep); break; } From 2a087f40f9760cb72bdf9c125d3cac11b235a262 Mon Sep 17 00:00:00 2001 From: Stephan Hadinger Date: Tue, 9 Aug 2022 18:30:01 +0200 Subject: [PATCH 211/219] Zigbee ``SetOption144 1`` includes a timestamp in `ZbReceived` messages --- CHANGELOG.md | 1 + tasmota/include/tasmota_types.h | 2 +- .../xdrv_23_zigbee_2_devices.ino | 2 +- .../xdrv_23_zigbee_2a_devices_impl.ino | 14 +++++++++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba63afbb..99ed78f57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. - Zigbee include "BatteryPercentage" in all messages - Commands ``WifiScan`` and ``WifiTest`` (#16141) - Support for Catalan language translations by Albert Gonzalez (#16145) +- Zigbee ``SetOption144 1`` includes a timestamp in `ZbReceived` messages ### Changed - ESP32 LVGL library from v8.2.0 to v8.3.0 (#16019) diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index fc54a728d..38247b377 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -171,7 +171,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1) uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding - uint32_t spare30 : 1; // bit 30 + uint32_t zigbee_include_time : 1; // bit 30 (v12.0.2.4) - SetOption144 - (Zigbee) include time in `ZbReceived` messages like other sensors uint32_t spare31 : 1; // bit 31 }; } SOBitfield5; diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino index befa1e22f..cf11e2405 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2_devices.ino @@ -828,7 +828,7 @@ public: void jsonAddDataAttributes(Z_attribute_list & attr_list) const; void jsonAddDeviceAttributes(Z_attribute_list & attr_list) const; void jsonDumpSingleDevice(Z_attribute_list & attr_list, uint32_t dump_mode, bool add_name) const; - void jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list) const; + void jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list, bool include_time = false) const; void jsonLightState(Z_attribute_list & attr_list) const; // dump device attributes to ZbData diff --git a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino index 2bb0a231a..02df96cf7 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_2a_devices_impl.ino @@ -522,14 +522,22 @@ void Z_Devices::jsonAppend(uint16_t shortaddr, const Z_attribute_list &attr_list // // internal function to publish device information with respect to all `SetOption`s // -void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list) const { +void Z_Device::jsonPublishAttrList(const char * json_prefix, const Z_attribute_list &attr_list, bool include_time) const { bool use_fname = (Settings->flag4.zigbee_use_names) && (friendlyName); // should we replace shortaddr with friendlyname? ResponseClear(); // clear string + // Do we prefix with `ZbReceived`? if (!Settings->flag4.remove_zbreceived && !Settings->flag5.zb_received_as_subtopic) { - Response_P(PSTR("{\"%s\":"), json_prefix); + if (include_time && Rtc.utc_time >= START_VALID_TIME) { + // Add time if needed (and if time is valide) + ResponseAppendTimeFormat(Settings->flag2.time_format); + ResponseAppend_P(PSTR(",\"%s\":"), json_prefix); + } else { + ResponseAppend_P(PSTR("{\"%s\":"), json_prefix); + } } + // What key do we use, shortaddr or name? if (!Settings->flag5.zb_omit_json_addr) { if (use_fname) { @@ -609,7 +617,7 @@ void Z_Devices::jsonPublishFlush(uint16_t shortaddr) { attr_list.setBattPercent(device.batt_percent); } - device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list); + device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_RECEIVED), attr_list, Settings->flag5.zigbee_include_time); attr_list.reset(); // clear the attributes } } From 73166cace74e72dc20188947c7249d3ed91bb6aa Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 10 Aug 2022 13:01:01 +0200 Subject: [PATCH 212/219] Allow overriding reset operation via project file to make it possible to use `usb_reset`. Needed by CDC devices. Infos https://github.com/platformio/platform-espressif32/issues/874 --- pio-tools/post_esp32.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index fde5f30e7..f06fe0403 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -178,8 +178,8 @@ def esp32_create_combined_bin(source, target, env): "--chip", chip, "--port", '"$UPLOAD_PORT"', "--baud", "$UPLOAD_SPEED", - "--before", "default_reset", - "--after", "hard_reset", + "--before", board.get("upload.before_reset", "default_reset"), + "--after", board.get("upload.after_reset", "hard_reset"), "write_flash", "-z", "--flash_mode", "${__get_board_flash_mode(__env__)}", "--flash_freq", "${__get_board_f_flash(__env__)}", From bdbd33aecfa1f33216dfaef6a169c9b576db0e00 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 10 Aug 2022 14:11:47 +0200 Subject: [PATCH 213/219] fix `board` not defined --- pio-tools/post_esp32.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pio-tools/post_esp32.py b/pio-tools/post_esp32.py index f06fe0403..eb1da1285 100644 --- a/pio-tools/post_esp32.py +++ b/pio-tools/post_esp32.py @@ -171,6 +171,8 @@ def esp32_create_combined_bin(source, target, env): if(fs_offset != -1): fs_bin = join(env.subst("$BUILD_DIR"),"littlefs.bin") if exists(fs_bin): + before_reset = env.BoardConfig().get("upload.before_reset", "default_reset") + after_reset = env.BoardConfig().get("upload.after_reset", "hard_reset") print(f" - {hex(fs_offset)}| {fs_bin}") cmd += [hex(fs_offset), fs_bin] env.Replace( @@ -178,8 +180,8 @@ def esp32_create_combined_bin(source, target, env): "--chip", chip, "--port", '"$UPLOAD_PORT"', "--baud", "$UPLOAD_SPEED", - "--before", board.get("upload.before_reset", "default_reset"), - "--after", board.get("upload.after_reset", "hard_reset"), + "--before", before_reset, + "--after", after_reset, "write_flash", "-z", "--flash_mode", "${__get_board_flash_mode(__env__)}", "--flash_freq", "${__get_board_f_flash(__env__)}", From 52db292f6857bead591996790ccdf8e6f8f1293e Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:15:27 +0200 Subject: [PATCH 214/219] Use esptool command "before_reset usb_reset" usb_reset is the correct way to get cdc in bootloader mode --- boards/esp32c3cdc.json | 1 + boards/esp32s2cdc.json | 1 + boards/esp32s3cdc-box.json | 1 + boards/esp32s3cdc.json | 1 + 4 files changed, 4 insertions(+) diff --git a/boards/esp32c3cdc.json b/boards/esp32c3cdc.json index 5371a407e..ccd2da9d1 100644 --- a/boards/esp32c3cdc.json +++ b/boards/esp32c3cdc.json @@ -36,6 +36,7 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, + "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", diff --git a/boards/esp32s2cdc.json b/boards/esp32s2cdc.json index c78313418..b480113b7 100644 --- a/boards/esp32s2cdc.json +++ b/boards/esp32s2cdc.json @@ -36,6 +36,7 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, + "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/hw-reference/esp32s2/user-guide-saola-1-v1.2.html", diff --git a/boards/esp32s3cdc-box.json b/boards/esp32s3cdc-box.json index d422133d9..cbeafdc39 100644 --- a/boards/esp32s3cdc-box.json +++ b/boards/esp32s3cdc-box.json @@ -31,6 +31,7 @@ "maximum_ram_size": 327680, "maximum_size": 16777216, "require_upload_port": true, + "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", diff --git a/boards/esp32s3cdc.json b/boards/esp32s3cdc.json index cd6482403..a5280fb9d 100644 --- a/boards/esp32s3cdc.json +++ b/boards/esp32s3cdc.json @@ -38,6 +38,7 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, + "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/hw-reference/esp32s3/", From 4170d79a5b136567455294eb8e77b594152e6a49 Mon Sep 17 00:00:00 2001 From: Jason2866 <24528715+Jason2866@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:58:58 +0200 Subject: [PATCH 215/219] fix OTA URL --- .../Sonoff_ZBPro.autoconf | Bin 392 -> 350 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/fw_SonoffZigbeeBridgePro_cc2652/Sonoff_ZBPro.autoconf b/tools/fw_SonoffZigbeeBridgePro_cc2652/Sonoff_ZBPro.autoconf index 342c3d5aa0131a29cbbefc534438ef3b012bbc86..ebe13613ba58f2ecea60fc4fa8bd4f9dbd2c986c 100644 GIT binary patch delta 91 zcmeBRzQ-gH;LXe;!Tl`R#yC;ph+DChzJ5_+xn6o^Nk(Z>X>n>% za(-S(YF>$6a(=FUNn&wseo3N!W?pegVor{JZenJhep+TxZh2x+s(xu+ep*^)a%N)A V#2;#WsAf;rVbo!>0b0Yr004SIEG+;4 From 729dc29807f0ca9b1c32c490b095ed6555e02965 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Wed, 10 Aug 2022 12:14:58 -0500 Subject: [PATCH 216/219] Set LED state on SO87 change --- tasmota/tasmota_support/support_command.ino | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index ac5c11f00..e8a406163 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -1386,6 +1386,9 @@ void CmndSetoptionBase(bool indexed) { case 25: // SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) TasmotaGlobal.restart_flag = 2; break; + case 5: // SetOption87 - (PWM Dimmer) Turn red LED on (1) when powered off + TasmotaGlobal.restore_powered_off_led_counter = 1; + break; } } else if (5 == ptype) { // SetOption114 .. 145 From 20da738788abdd9c123f5b7e944442bedd4d67b6 Mon Sep 17 00:00:00 2001 From: Paul C Diem Date: Wed, 10 Aug 2022 14:56:30 -0500 Subject: [PATCH 217/219] Wrap restore_powered_off_led_counter in #ifdef --- tasmota/tasmota_support/support_command.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tasmota/tasmota_support/support_command.ino b/tasmota/tasmota_support/support_command.ino index e8a406163..2fa54a43f 100644 --- a/tasmota/tasmota_support/support_command.ino +++ b/tasmota/tasmota_support/support_command.ino @@ -1386,9 +1386,11 @@ void CmndSetoptionBase(bool indexed) { case 25: // SetOption107 - Virtual CT Channel - signals whether the hardware white is cold CW (true) or warm WW (false) TasmotaGlobal.restart_flag = 2; break; +#ifdef USE_PWM_DIMMER case 5: // SetOption87 - (PWM Dimmer) Turn red LED on (1) when powered off TasmotaGlobal.restore_powered_off_led_counter = 1; break; +#endif // USE_PWM_DIMMER } } else if (5 == ptype) { // SetOption114 .. 145 From 8b32a9818aaabfea97fc74e2048bdbec0c0e31d5 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 11 Aug 2022 14:16:30 +0200 Subject: [PATCH 218/219] Fix legacy command resolved status Fix legacy command resolved status (#15900) --- tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino | 2 ++ tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino | 2 ++ tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino | 32 ++++++++++--------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino b/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino index dc7ec35a3..f06fddb2f 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino @@ -356,6 +356,8 @@ bool Scd30CommandSensor() serviced = false; break; } + } else { + serviced = false; } return serviced; } diff --git a/tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino b/tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino index 5220322ed..b5e3db4e2 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_48_chirp.ino @@ -509,6 +509,8 @@ bool ChirpCmd(void) { serviced = false; break; } + } else { + serviced = false; } return serviced; } diff --git a/tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino b/tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino index d0e8a5e18..2acebe399 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_92_scd40.ino @@ -77,7 +77,7 @@ const char S_JSON_SCD40_COMMAND_NVALUE[] PROGMEM = "{\"" D_CMND_SCD40 "%s\":%d}" const char kSCD40_Commands[] PROGMEM = "Alt|Auto|Toff|Pres|Cal|Test|StLP|Strt|Stop|Pers|Rein|Fact|Sing|SRHT"; /*********************************************************************************************\ - * enumerations + * enumerations \*********************************************************************************************/ enum SCD40_Commands { // commands useable in console or rules @@ -358,56 +358,56 @@ bool Scd40CommandSensor() } break; - case CMND_SCD40_START_MEASUREMENT_LOW_POWER: + case CMND_SCD40_START_MEASUREMENT_LOW_POWER: { - error = scd40.startLowPowerPeriodicMeasurement(); + error = scd40.startLowPowerPeriodicMeasurement(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_START_MEASUREMENT: + case CMND_SCD40_START_MEASUREMENT: { - error = scd40.startPeriodicMeasurement(); + error = scd40.startPeriodicMeasurement(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_STOP_MEASUREMENT: + case CMND_SCD40_STOP_MEASUREMENT: { - error = scd40.stopPeriodicMeasurement(); + error = scd40.stopPeriodicMeasurement(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_PERSIST: + case CMND_SCD40_PERSIST: { - error = scd40.persistSettings(); + error = scd40.persistSettings(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_REINIT: + case CMND_SCD40_REINIT: { - error = scd40.reinit(); + error = scd40.reinit(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_FACTORYRESET: + case CMND_SCD40_FACTORYRESET: { - error = scd40.performFactoryReset(); + error = scd40.performFactoryReset(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_SINGLESHOT: + case CMND_SCD40_SINGLESHOT: { error = scd40.measureSingleShot(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); } break; - case CMND_SCD40_SINGLESHOT_RHT_ONLY: + case CMND_SCD40_SINGLESHOT_RHT_ONLY: { error = scd40.measureSingleShotRhtOnly(); Response_P(S_JSON_SCD40_COMMAND_NVALUE, command, error?-1:0); @@ -419,6 +419,8 @@ bool Scd40CommandSensor() serviced = false; break; } + } else { + serviced = false; } return serviced; } From 2cdfcf5345e774b0c64eb0f3e70bd06d692ce1d9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 11 Aug 2022 14:47:31 +0200 Subject: [PATCH 219/219] Update changelog and templates --- MODULES.md | 2 +- RELEASENOTES.md | 1 + TEMPLATES.md | 65 +++++++++++++++++++++++++++------ tasmota/include/tasmota_types.h | 4 +- tools/decode-status.py | 3 +- 5 files changed, 59 insertions(+), 16 deletions(-) diff --git a/MODULES.md b/MODULES.md index 8a30d0ad3..6058c385d 100644 --- a/MODULES.md +++ b/MODULES.md @@ -94,4 +94,4 @@ Module | LCode | Description 06 TTGO Watch | x | TTGO Watch 07 M5Stack Core2 | x | M5Stack Core2 -Over 2370 additional devices are supported using [templates](TEMPLATES.md). +Over 2400 additional devices are supported using [templates](TEMPLATES.md). diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9e1e1015b..87ca083bb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -110,6 +110,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo ## Changelog v12.0.2.4 ### Added - Command ``SetOption45 1..250`` to change default bistable latching relay pulse length of 40 milliseconds +- Command ``SetOption144 1`` includes a timestamp in zigbee `ZbReceived` messages - Command ``GlobalTemp2 1..250`` to select Global Temperature source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalHum2 1..250`` to select Global Humidity source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) - Command ``GlobalPress2 1..250`` to select Global Pressure source indexed from teleperiod occurance data [#15834](https://github.com/arendst/Tasmota/issues/15834) diff --git a/TEMPLATES.md b/TEMPLATES.md index f2791417d..102b9fa25 100644 --- a/TEMPLATES.md +++ b/TEMPLATES.md @@ -2,7 +2,7 @@ # Templates -Find below the available templates as of June 2022. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) +Find below the available templates as of August 2022. More template information can be found in the [Tasmota Device Templates Repository](http://blakadder.github.io/templates) ## Addressable LED ``` @@ -43,6 +43,7 @@ Syska 8W {"NAME":"Syska SMW-8W-C","GPIO":[0,0,0,0,420,419,0, ## CCT ``` AICase 800lm {"NAME":"AICase Smart L","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} +AiYaTo 12W {"NAME":"AiYaTo CW","GPIO":[0,0,0,0,416,0,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} Ajax Online 380lm {"NAME":"AjaxOnline","GPIO":[32,0,0,0,0,416,0,0,417,0,0,0,0,0],"FLAG":0,"BASE":38} Ajax Online 7W Vintage {"NAME":"AjaxOnline-7W","GPIO":[0,0,0,0,0,0,0,0,417,0,416,0,0,0],"FLAG":0,"BASE":18} Anoopsyche 9W 800lm {"NAME":"Anoop-CW-WW","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} @@ -192,6 +193,7 @@ Zemismart Door Window {"NAME":"Zemismart","GPIO":[1,2272,1,2304,1,1,0,0,1 ## Curtain Module ``` +Athom {"NAME":"Athom Curtain Module","GPIO":[0,0,0,160,161,32,0,0,224,576,225,0,0,0],"FLAG":0,"BASE":18,"CMND":"SO80 1 | ShutterMode 1 | SwitchDebounce 300 | ButtonDebounce 300"} BlitzWolf {"NAME":"BlitzWolf SS6 2 Gang","GPIO":[0,0,32,0,480,0,0,0,161,160,224,225,0,0],"FLAG":0,"BASE":18} LoraTap In-Wall {"NAME":"SC500W","GPIO":[0,0,0,576,160,161,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18} LoraTap In-Wall {"NAME":"SC511WSC","GPIO":[0,1,0,320,32,34,0,0,224,33,226,225,0,0],"FLAG":0,"BASE":18} @@ -222,7 +224,7 @@ Teepao {"NAME":"Teepao","GPIO":[576,322,226,33,225,34,0,0, WF-CS01 {"NAME":"ShutterSwitch","GPIO":[544,227,289,34,226,33,0,0,32,224,290,225,288,0],"FLAG":0,"BASE":18,"CMND":"Rule1 1"} WF-CS02 {"NAME":"WF-CS02 Tuya","GPIO":[544,0,289,162,226,33,0,0,32,224,290,225,288,0],"FLAG":0,"BASE":18} WuyunElek {"NAME":"MILOS Jalousie","GPIO":[1,0,224,225,1,320,0,0,32,33,34,226,0,0],"FLAG":0,"BASE":18} -Zemismart {"NAME":"Zemismart","GPIO":[544,0,0,162,290,161,0,0,160,224,226,225,0,0],"FLAG":0,"BASE":18} +Zemismart {"NAME":"Zemismart","GPIO":[544,0,289,162,290,161,0,0,160,224,226,225,288,0],"FLAG":0,"BASE":18} Zemismart Backlit {"NAME":"WF-CS01","GPIO":[544,227,289,34,226,161,0,0,160,224,290,225,288,0],"FLAG":0,"BASE":18} ``` @@ -236,7 +238,8 @@ Sinotimer {"NAME":"Sinotimer TM60","GPIO":[0,0,0,0,0,288,0,0, SMTONOFF 63A {"NAME":"SMTONOFF","GPIO":[32,0,0,3104,0,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":43} Sohan DIN Circuit Breaker 1P 50A {"NAME":"RDCBC-1P","GPIO":[32,0,0,0,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":1} Sonoff Smart Stackable Power Meter {"NAME":"Sonoff SPM","GPIO":[0,0,0,0,3200,5536,0,0,672,704,736,0,3232,0,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,544,0,0,32,0,0,0,0],"FLAG":0,"BASE":1} -TOMZN 2P 63/80A Circuit Breaker {"NAME":"TOMZN","GPIO":[0,0,0,0,0,0,0,0,64,224,0,0,288,0],"FLAG":0,"BASE":18} +TOMZN 2P 63A/80A Circuit Breaker {"NAME":"TOWBC-2P-T","GPIO":[0,0,0,0,0,0,0,0,64,224,0,0,288,0],"FLAG":0,"BASE":18} +TOMZN 4P 80A Circuit Breaker {"NAME":"TOWBC-4P-T","GPIO":[288,0,0,0,0,0,0,0,32,224,0,0,0,0],"FLAG":0,"BASE":18} TOMZN 4P 80A Circuit Breaker {"NAME":"TOMZN4","GPIO":[32,0,0,0,0,0,0,0,224,288,0,0,0,0],"FLAG":0,"BASE":18} ``` @@ -254,6 +257,7 @@ Adafruit HUZZAH32 ESP32 Feather {"NAME":"HUZZAH32","GPIO":[0,0,0,0,4709,0,1,1,1 Adafruit QT Py ESP32 Pico {"NAME":"QTPy ESP32 Pico","GPIO":[32,3200,0,3232,1,1376,0,0,1,1,1,1,0,0,0,608,0,0,640,0,0,1,1,1,0,1,3840,0,1,1,0,0,0,0,0,0],"FLAG":0,"BASE":1} Ai-Thinker Camera {"NAME":"AITHINKER CAM","GPIO":[4992,1,672,1,416,5088,1,1,1,6720,736,704,1,1,5089,5090,0,5091,5184,5152,0,5120,5024,5056,0,0,0,0,4928,576,5094,5095,5092,0,0,5093],"FLAG":0,"BASE":2} AZ-Envy Environmental Sensor {"NAME":"AZ Envy","GPIO":[32,0,320,0,640,608,0,0,0,0,0,0,0,4704],"FLAG":0,"BASE":18} +Coiaca Tasmota {"NAME":"AWR01t","GPIO":[576,1,1,128,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} ESP32 Lite V1.0.0 {"NAME":"ESP32 Lite V1.0.0","GPIO":[1,0,1,0,1,1,0,0,1,1,1,1,1,1,1,1,0,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0],"FLAG":0,"BASE":1} Freenove ESP32-WROVER.DEV Camera {"NAME":"Freenove-WROVER-Cam","GPIO":[1,1,1,1,5088,5089,0,0,1,1,1,1,1,1,5090,5091,1,4992,5184,5152,1,5120,5024,5056,0,0,0,0,1,1,5094,5095,5092,1,1,5093],"FLAG":0,"BASE":1} LC Technology MicroPython Maker {"NAME":"LC-ESP-Python","GPIO":[1,1,544,1,1,1,1,1,1,1,1,1,1,1],"FLAG":0,"BASE":18} @@ -355,6 +359,7 @@ Smitch 10W 6500K {"NAME":"Smitch Ambience SB-0110","GPIO":[0,0,0,0,4 TCP Smart 806lm Warm White {"NAME":"TCP Smart Clas","GPIO":[0,0,0,0,0,0,0,0,0,416,0,0,0,0],"FLAG":0,"BASE":1} TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18} TCP Smart 810lm Filament {"NAME":"TCP Filament","GPIO":[0,0,0,0,0,0,0,0,0,0,448,0,0,0],"FLAG":0,"BASE":18} +WiZ 8W 2700K {"NAME":"WiZ A60","GPIO":[0,0,416,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} Xiaomi Philips MUE4088RT {"NAME":"Xiaomi Philips","GPIO":[0,0,0,0,0,0,0,0,0,0,0,416,0,0],"FLAG":0,"BASE":18} ``` @@ -386,7 +391,7 @@ Tessan MJ-SD02 {"NAME":"MJ-SD02","GPIO":[34,33,0,323,576,322,0,0,3 TopGreener TGWF500D {"NAME":"TopGreener-Dimmer","GPIO":[0,0,0,0,0,0,0,0,0,2304,0,2272,0,0],"FLAG":0,"BASE":54} TreatLife 400W {"NAME":"DS02S Dimmer","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} TreatLife 400W Single Pole {"NAME":"DS02S Dimmer","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} -Treatlife Dual Outdoor Dimmer {"NAME":"DP12","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"SO97 1 | TuyaMCU 11,1 | TuyaMCU 12,7 | TuyaMCU 21,2 | TuyaMCU 22,8 | SO20 1 | SO54 1"} +Treatlife Dual Outdoor {"NAME":"DP12","GPIO":[1,2272,1,2304,1,1,0,0,1,1,1,1,1,0],"FLAG":0,"BASE":54,"CMND":"SO97 1 | TuyaMCU 11,1 | TuyaMCU 12,7 | TuyaMCU 21,2 | TuyaMCU 22,8 | SO20 1 | SO54 1"} TreatLife Light and Fan {"NAME":"DS03 Fan/Light","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} Virage Labs VirageDimmer {"NAME":"VirageDimmer","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} WF-DS01 {"NAME":"Dimmer WF-DS01","GPIO":[1,1,1,1,1,1,0,0,1,1,290,1,1,0],"FLAG":0,"BASE":54} @@ -413,7 +418,7 @@ Sonoff D1 {"NAME":"Sonoff D1","GPIO":[1,3200,0,3232,0,0,0,0,0 ## Display ``` Heltec WiFi Kit 32 {"NAME":"WiFi Kit 32","GPIO":[1,1,1,1,640,1,1,1,1,1,1,608,3840,1,1,1,0,1,1,1,0,224,1,1,0,0,0,0,1,1,1,1,1,0,0,1],"FLAG":0,"BASE":1} -Heltec WiFi Kit 8 {"NAME":"HTIT-W8266","GPIO":[1,1,1,1,640,608,0,0,1,1,1,1,1024,1],"FLAG":0,"BASE":18} +Heltec WiFi Kit 8 {"NAME":"WifiKit8","GPIO":[1,1,1,1,640,608,1,1,1,1,1,1,1024,1],"FLAG":0,"BASE":18} LilyGO T5 4.7 inch E-paper {"NAME":"LilyGO T5-4.7","GPIO":[0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,7616,0,0,0,0,0,0,0,0,0,0,33,34,4704,0,0,32],"FLAG":0,"BASE":1} LilyGo TTGO ESP32 {"NAME":"TTGO T-Journal ESP32 Camera","GPIO":[4928,1,1,1,1,5091,1,1,1,608,640,4960,1,5088,5093,5095,0,5184,5120,5056,0,5024,5152,4992,0,0,0,0,1,1,5090,5089,5094,0,0,5092],"FLAG":0,"BASE":1} LilyGO TTGO ESP8266 0.91 inch OLED SSD1306 {"NAME":"TTGO-0.91","GPIO":[1,1,640,1,1024,0,0,0,1,1,608,1,0,1],"FLAG":0,"BASE":18} @@ -502,6 +507,7 @@ BrilliantSmart Light and {"NAME":"Brilliant Fan","GPIO":[0,2272,0,2304,0,0,0 Qiachip 110V/220V Light and {"NAME":"Qiachip FLC","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 11,1 | TuyaMCU 12,9 | TuyaMCU 81,7"} Sonoff iFan02 Light and Ceiling {"NAME":"Sonoff iFan02","GPIO":[32,1,0,1,226,225,33,34,224,320,35,227,0,0],"FLAG":0,"BASE":44} Sonoff iFan03 220V Light and Ceiling {"NAME":"SonoffiFan03","GPIO":[32,3200,0,3232,0,0,256,512,226,320,225,227,0,0],"FLAG":0,"BASE":71} +Sonoff iFan04-H 220V Light and Ceiling {"NAME":"iFan04-H","GPIO":[32,3200,0,3232,0,0,256,512,226,320,225,227,0,0],"FLAG":0,"BASE":71} Sonoff iFan04-L 110V Light and Ceiling {"NAME":"iFan04-L","GPIO":[32,3200,0,3232,0,0,256,512,226,320,225,227,0,0],"FLAG":0,"BASE":71} ``` @@ -544,7 +550,7 @@ A1 Universal Remote Control {"NAME":"A1 IR Controller","GPIO":[1,1,1,1,320,1088 AI Universal Remote {"NAME":"YTF IR Controller","GPIO":[1,1,1,1,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} Alfawise KS1 {"NAME":"KS1","GPIO":[1,1792,32,1824,32,1088,0,0,320,0,1056,0,0,4704],"FLAG":0,"BASE":62} Antsig Universal Remote Controller {"NAME":"Antsig Smart Wi-Fi IR","GPIO":[1,1,1,1,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} -Automate Things IR Bridge {"NAME":"AT-IRBR-1.4","GPIO":[1088,0,0,0,1056,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Automate Things IR Bridge {"NAME":"AT-IRBR-1.4","GPIO":[1088,0,0,0,1056,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"} auvisio S06 {"NAME":"NX-4519-675","GPIO":[0,0,0,0,288,1088,0,0,0,0,1056,0,0,0],"FLAG":0,"BASE":18} Avatto Temperature and Humidity Sensor and {"NAME":"S08Pro","GPIO":[0,640,0,608,288,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":18} BlitzWolf BW-RC1 {"NAME":"BW-RC1","GPIO":[0,0,0,0,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} @@ -563,6 +569,7 @@ Nedis Universal Remote Control {"NAME":"Nedis IR Controller","GPIO":[1,1,1,1,32 NEO Coolcam Remote Controller {"NAME":"Neo Coolcam IR","GPIO":[1,3200,1,3232,576,1088,0,0,320,32,1056,0,0,0],"FLAG":0,"BASE":62} Nivian Smart Infrared Controller {"NAME":"Nivian NVS-SMARTIR-W2","GPIO":[0,3200,0,3232,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} Orvibo Magic Cube {"NAME":"Orvibo CT10W","GPIO":[0,0,0,0,32,1088,0,0,288,0,1056,289,0,0],"FLAG":0,"BASE":18} +Phlipton Universal Remote {"NAME":"Phliptron IR","GPIO":[1,3200,1,3232,576,1088,0,0,320,32,1056,0,0,0],"FLAG":0,"BASE":62} Remote Control {"NAME":"DC-QRA2","GPIO":[0,0,0,0,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} RM mini {"NAME":"RM mini","GPIO":[1,1,1,1,320,1088,0,0,0,32,1056,0,0,0],"FLAG":0,"BASE":62} Smartpoint Smart Remote {"NAME":"Smartpoint SPCNTRL-WM","GPIO":[0,0,0,0,288,1088,0,0,0,0,1056,0,0,0],"FLAG":0,"BASE":18} @@ -577,7 +584,7 @@ YTF Universal Remote {"NAME":"YTF IR Controller","GPIO":[1,1,1,1,320,108 ## IR Gateway ``` -Automate Things IR Bridge {"NAME":"AT-IRBR-1.0","GPIO":[0,0,0,0,1056,1088,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} +Automate Things IR Bridge {"NAME":"AT-IRBR-1.0","GPIO":[0,0,0,0,1056,1088,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"Module 0"} ``` ## Illuminance Sensor @@ -607,6 +614,7 @@ Athom RGB {"NAME":"LS5050C-TAS","GPIO":[32,0,0,0,417,0,0,0,41 Bakeey RGB {"NAME":"Bakeey Strip Controller","GPIO":[32,0,0,0,416,420,0,0,418,0,417,419,0,0],"FLAG":0,"BASE":18} CCT 12-24V {"NAME":"WS02","GPIO":[0,0,0,0,0,0,0,0,0,417,416,0,0,0],"FLAG":0,"BASE":18} CIN-03 96W RGB {"NAME":"CIN03-03 Strip","GPIO":[0,0,0,0,417,0,0,0,416,0,418,0,0,0],"FLAG":0,"BASE":18} +CIN04 240W {"NAME":"CIN04-03 Strip","GPIO":[0,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18} DD001-MINI(G)-IR-V03 {"NAME":"WIFI-RGB","GPIO":[32,0,0,0,0,0,0,0,417,418,416,0,0,0],"FLAG":0,"BASE":40} DD001-MINI(G)-IR-V08 {"NAME":"WIFI-RGB","GPIO":[0,0,0,0,416,0,0,0,417,0,418,0,0,0],"FLAG":0,"BASE":18} Electrodragon ESP LED Strip Board, Mosfet Drive {"NAME":"LEDBoard RGBW","GPIO":[0,0,0,0,0,0,0,0,418,417,419,416,288,0],"FLAG":0,"BASE":18} @@ -711,7 +719,7 @@ Arlec Smart 15W Security Floodlight {"NAME":"ArlecFlood","GPIO":[0,0,0,0,0,0,0, Arlec Smart 20W Movement Activated Security {"NAME":"Arlec MAL300HA","GPIO":[0,0,0,0,224,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Arlec Smart 40W 4000lm LED Batten {"NAME":"Arlec Batten","GPIO":[0,0,0,0,0,416,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} Arlec Smart Portable Floodlight 10.5W {"NAME":"Arlec GLD301HA","GPIO":[0,0,0,0,0,416,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} -Arlec Smart Security Light {"NAME":"Arlec MAL315HA","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} +Arlec Smart Security {"NAME":"Arlec MAL315HA","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} Arlec Smart Up & Down LED Wall {"NAME":"Arlec Up Down CCT","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} BAZZ 4 in. RGB Recessed Fixture {"NAME":"SLMR4RGBWWFW","GPIO":[0,0,0,0,417,416,0,0,420,418,419,0,0,0],"FLAG":0,"BASE":18} BAZZ 6 in. CCT Recessed Fixture {"NAME":"SLDSK6TNWWF","GPIO":[0,0,0,0,0,416,0,0,0,417,0,0,0,0],"FLAG":0,"BASE":18} @@ -779,7 +787,7 @@ PCI-e Desktop PC Remote Control {"NAME":"PC-Switch-01","GPIO":[32,0,0,0,0,0,0,0 Proscenic T21 Air Fryer {"NAME":"Proscenic T21","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54} RainPoint Indoor Water Pump {"NAME":"RainPoint","GPIO":[0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 81,107|TuyaMCU 12,109|TuyaMCU 11,1|TuyaMCU 82,104"} Sinilink PCIe Computer Remote {"NAME":"XY-WPCE","GPIO":[1,1,320,1,32,224,0,0,160,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"SO114 1 | SwitchMode1 2"} -Sinilink USB Computer Remote {"NAME":"XY-WPCL","GPIO":[1,1,320,1,32,224,0,0,160,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"SO114 1 | SwitchMode1 2"} +Sinilink USB Computer Remote {"NAME":"XY-WPCL","GPIO":[0,0,320,0,0,224,0,32,160,0,0,0,0,0],"FLAG":0,"BASE":18,"CMND":"SO114 1 | Pulsetime 10 | SwitchMode1 2"} Xystec USB3.0 4 Port Hub {"NAME":"Xystec USB Hub","GPIO":[0,0,0,0,224,0,0,0,226,227,225,0,0,0],"FLAG":0,"BASE":18} ``` @@ -796,6 +804,7 @@ ESP-M4 {"NAME":"ESP-M4","GPIO":[1,1,0,1,1,1,0,0,1,1,1,1,1, ESP8266 5V UART Communication {"NAME":"ESP-WROOM-5V2L","GPIO":[0,1,0,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} LC Technology PSF-B04 Ewelink 4 Channel Switch {"NAME":"LC-EWL-B04-MB","GPIO":[1,1,1,1,1,1,0,0,1,288,1,1,0,0],"FLAG":0,"BASE":18} M5Stack M5Stamp Pico {"NAME":"M5Stamp Pico","GPIO":[1,1,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,0,0,1,0,1376,0,0,0,0,1,1,0,0,1,0,0,32],"FLAG":0,"BASE":1} +Moes 10A {"NAME":"Moes MS-101","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} MTools 16 Channel ESP32 Relay Driver 5V DC {"NAME":"16ch Board","GPIO":[1,1,237,1,232,1,1,1,228,231,1,1,233,230,234,235,0,238,239,236,0,224,227,226,0,0,0,0,229,225,1,1,1,0,0,1],"FLAG":0,"BASE":1} Shelly Universal Input/Output {"NAME":"Shelly Uni","GPIO":[320,0,0,0,225,1216,0,0,192,193,0,224,0,4864],"FLAG":0,"BASE":18} Sinilink MODBUS Interface {"NAME":"XY-WFPOW","GPIO":[0,1,0,1,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":18} @@ -841,6 +850,8 @@ Brennenstuhl WA 3000 {"NAME":"WA 3000 XS02","GPIO":[0,0,0,0,224,32,0,0,5 C119 {"NAME":"Subosi C119","GPIO":[0,0,0,0,225,320,0,0,224,544,32,0,321,0],"FLAG":0,"BASE":18} C137 IP55 {"NAME":"C137 Outdoor","GPIO":[0,32,0,320,2720,2656,0,0,224,2624,225,0,0,1],"FLAG":0,"BASE":18} C168 IP64 {"NAME":"C188","GPIO":[320,0,321,0,33,0,0,0,224,32,544,225,0,0],"FLAG":0,"BASE":18} +Coolseer {"NAME":"Coolseer Plug (SK01WE)","GPIO":[0,3200,0,7520,0,32,0,0,0,544,224,0,0,0],"FLAG":0,"BASE":18} +Dewenwils Heavy Duty 40A {"NAME":"Dewenwils HOWT01A","GPIO":[0,0,544,0,0,0,0,0,0,32,0,224,0,0],"FLAG":0,"BASE":18} ECF-SOP03 {"NAME":"Outdoor3Outlet","GPIO":[0,0,0,226,320,0,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18} Ecoolbuy 4 Socket IP44 {"NAME":"ECCOLBUY 4","GPIO":[0,0,0,0,225,226,0,0,224,321,32,0,227,0],"FLAG":0,"BASE":18} Edimax 2AC {"NAME":"EDI SP-1122WTO","GPIO":[0,0,0,0,225,576,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18} @@ -949,6 +960,7 @@ Arlec Smart 2.1A USB Charger {"NAME":"Arlec Single","GPIO":[0,0,0,0,321,0,0,0,2 Arlec Smart PC189HA {"NAME":"Arlec Single","GPIO":[0,0,0,0,321,0,0,0,224,0,64,0,0,0],"FLAG":0,"BASE":18} Arlec Socket With USB {"NAME":"PC192HA","GPIO":[0,0,0,32,0,0,0,0,224,0,225,0,0,0],"FLAG":0,"BASE":18} Arlec Twin PC288HA {"NAME":"Arlec Twin","GPIO":[0,32,0,225,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} +Arlec Twin Socket with Energy Meter {"NAME":"Arlec PC287HA","GPIO":[0,32,0,320,2624,2656,0,0,224,2720,225,0,0,0],"FLAG":0,"BASE":18} Athom 16A {"NAME":"Athom Power Monitoring Plug","GPIO":[0,0,0,32,2720,2656,0,0,2624,544,224,0,0,1],"FLAG":0,"BASE":18} Athom 16A AU {"NAME":"Athom PG05-AU16A","GPIO":[0,0,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":1} Athom 16A AU {"NAME":"Athom Plug V2","GPIO":[0,0,0,3104,0,32,0,0,224,576,0,0,0,0],"FLAG":0,"BASE":18} @@ -1101,6 +1113,7 @@ Febite {"NAME":"Febite","GPIO":[320,0,0,0,0,2720,0,0,224,3 Feit Electric PLUG/WIFI {"NAME":"Feit Wifi Plug","GPIO":[0,0,0,320,0,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18} FK-PW901U {"NAME":"FK-PW901U","GPIO":[320,1,1,1,1,226,0,0,224,32,227,225,1,0],"FLAG":0,"BASE":18} FLHS-ZN04 {"NAME":"FLHS-ZN04","GPIO":[321,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45} +Floureon {"NAME":"FLOUREON","GPIO":[0,0,0,0,320,0,1,1,224,32,0,0,0,0],"FLAG":0,"BASE":18} Fontastic 16A {"NAME":"P22-2036563-DZB117435","GPIO":[0,0,0,0,288,321,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Fontastic SH01 {"NAME":"Fontastic","GPIO":[1,0,1,0,320,0,0,0,224,32,0,0,0,4704],"FLAG":0,"BASE":18} Foreet {"NAME":"Foreet_120V","GPIO":[0,0,0,0,224,32,0,0,289,288,0,0,0,0],"FLAG":0,"BASE":18} @@ -1156,6 +1169,7 @@ HaoDeng {"NAME":"HaoDeng","GPIO":[0,0,0,0,224,32,0,0,320,28 Hauppauge 01647 {"NAME":"SL-1642","GPIO":[0,0,0,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} HBN 13A {"NAME":"BNC-50/E75T","GPIO":[0,0,0,0,576,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} HBN BNC-60/U152T {"NAME":"BNC-60/U152T","GPIO":[0,0,0,0,320,0,1,1,224,32,0,0,0,0],"FLAG":0,"BASE":18} +HerePow {"NAME":"HerePow ZHX-ZNOC","GPIO":[0,544,0,2624,2720,2656,0,0,224,32,320,0,0,0],"FLAG":0,"BASE":68} Heygo 02 {"NAME":"Heygo 02","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} HiHome WPP-10S1 {"NAME":"HIhome WPP-10S","GPIO":[320,0,576,1,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":49} HiHome WPP-10S2 {"NAME":"HiHome WPP-10S","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49} @@ -1241,6 +1255,7 @@ Local Bytes (UK) {"NAME":"LocalBytes PM","GPIO":[0,0,0,32,2720,2656, LogiLink {"NAME":"LogilinkPA0199","GPIO":[0,0,0,64,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} LogiLink PA0200 {"NAME":"LogilinkPA0200","GPIO":[0,0,0,64,0,0,0,0,0,320,224,0,0,4896],"FLAG":0,"BASE":18} Lohas Nightlight + USB {"NAME":"Lohas LED Mini Plug","GPIO":[0,321,0,288,322,226,1,1,224,32,225,0,544,0],"FLAG":0,"BASE":18} +Lombex Actis Pro {"NAME":"U11-Socket","GPIO":[0,0,0,0,289,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Lonsonho 10A Type E {"NAME":"Lonsonho10ALed","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} LoraTap SP400W-IT {"NAME":"LoraTap SP400W","GPIO":[0,0,0,0,544,320,0,0,224,32,0,0,0,1],"FLAG":0,"BASE":18} LSC Power {"NAME":"LSC Smart Plug","GPIO":[0,0,0,0,320,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} @@ -1344,6 +1359,8 @@ RenPho RF-SM004 {"NAME":"RenPho RFSM004","GPIO":[0,0,0,0,544,320,0, Robaxo {"NAME":"Robaxo RSP-025","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} RSH-WS005 {"NAME":"RSH-WS005 Energy Monitor","GPIO":[320,0,576,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18} RSH-WS007-EU {"NAME":"RSH-WS007","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} +RSmart {"NAME":"RSmart Plug","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} +S-Link Swapp 16A {"NAME":"S-Link S01","GPIO":[0,0,320,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":18} S126 {"NAME":"tuya-plug","GPIO":[0,0,0,0,0,0,0,0,224,35,0,0,0,0],"FLAG":0,"BASE":8} SA-001 {"NAME":"SA-001","GPIO":[32,3072,0,3104,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":41} SA-P202A {"NAME":"SA-P202A","GPIO":[0,0,0,0,0,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} @@ -1369,6 +1386,7 @@ Slitinto NX-SP202 {"NAME":"Slitinto SP202","GPIO":[32,0,0,0,2720,2656 SM-PW701K {"NAME":"SM-PW701K","GPIO":[0,0,0,0,288,0,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Smaho {"NAME":"SMAHO WiFi P.","GPIO":[32,0,0,0,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} Smart 15A {"NAME":"JH-G09U","GPIO":[0,0,0,0,224,32,0,0,288,0,0,0,0,0],"FLAG":0,"BASE":18} +Smart Plug {"NAME":"SO08WP","GPIO":[1,1,1,32,2688,2656,1,1,2624,288,224,1,1,1],"FLAG":0,"BASE":18} SmartDGM PP-W162 {"NAME":"SmartDGM Plug","GPIO":[0,0,0,32,2720,2656,0,0,2624,288,224,0,0,0],"FLAG":0,"BASE":18} SmartGrade {"NAME":"SmartGrade AC","GPIO":[32,0,0,0,2688,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":49} SmartVU Home {"NAME":"SHWSP1","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":52} @@ -1433,10 +1451,12 @@ TP20 {"NAME":"TP20","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0 TP24 {"NAME":"TP24","GPIO":[0,0,0,32,0,0,0,0,0,320,224,0,0,0],"FLAG":0,"BASE":18} TP28Y {"NAME":"TP28Y","GPIO":[0,0,0,32,2720,2656,0,0,2624,320,224,0,0,0],"FLAG":0,"BASE":18} Treatlife Dimmable {"NAME":"DP20","GPIO":[0,2272,0,2304,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":54,"CMND":"TuyaMCU 21,2 | SO20 1 | SO54 1"} +Treatlife Smart {"NAME":"Treatlife SK50","GPIO":[1,1,1,1,320,576,1,1,224,1,32,1,1,1],"FLAG":0,"BASE":18} Tuya 16A Nightlight {"NAME":"Nightlight","GPIO":[225,0,320,0,226,227,0,0,34,64,0,224,0,0],"FLAG":0,"BASE":18} U10 Series {"NAME":"WIFI-Socket","GPIO":[1,32,1,1,1,1,1,1,1,320,224,1,1,4704],"FLAG":0,"BASE":18} UltraBrite {"NAME":"UltraBrite Smart Plug","GPIO":[1,1,1,1,288,289,1,1,224,32,1,1,1,1],"FLAG":0,"BASE":18} Ultralink UL-P01W {"NAME":"UL-P01W","GPIO":[0,288,0,32,2720,2656,0,0,2624,544,224,0,0,0],"FLAG":0,"BASE":18} +Unlocked Automation 15A {"NAME":"UA 100","GPIO":[0,0,0,0,320,321,0,0,224,32,0,0,0,1],"FLAG":0,"BASE":18} Upstone {"NAME":"UPSTONE","GPIO":[1,1,544,1,320,1,0,0,224,32,1,1,1,1],"FLAG":0,"BASE":18} US212 {"NAME":"US212","GPIO":[320,0,0,2624,0,2720,0,0,224,32,2656,225,0,0],"FLAG":0,"BASE":18} Varna Crafts 16A {"NAME":"VC Plug","GPIO":[544,0,0,0,0,2720,0,0,2624,32,2656,224,0,0],"FLAG":0,"BASE":45} @@ -1573,6 +1593,7 @@ LeFun SK2 {"NAME":"LeFun SK2","GPIO":[0,0,0,32,225,224,0,0,22 LITEdge Smart Power Strip {"NAME":"LITEEdge Power Strip","GPIO":[227,0,0,0,288,289,0,0,224,32,225,226,228,0],"FLAG":0,"BASE":18} Luminea 3AC+4USB 16A {"NAME":"Luminea-NX4473","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,0],"FLAG":0,"BASE":18} Maxcio ZLD-34EU-W {"NAME":"MAXCIO","GPIO":[0,320,0,32,225,224,0,0,0,226,227,0,0,4704],"FLAG":0,"BASE":18} +Merkury Innovations Smart Surge {"NAME":"Merkury Power Strip MIC-SW002-199L","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,259,0],"FLAG":0,"BASE":18} Merkury Innovations SmartSurge {"NAME":"Merkury MI-SW001","GPIO":[288,0,289,0,228,32,0,0,225,224,226,0,227,0],"FLAG":0,"BASE":18} Meross 4AC 4USB {"NAME":"HamaStrip","GPIO":[0,544,0,32,225,224,0,0,226,227,228,0,0,0],"FLAG":0,"BASE":18} Meross MSS425 {"NAME":"Meross MSS425","GPIO":[260,0,0,0,320,0,0,0,224,32,225,226,259,0],"FLAG":0,"BASE":18} @@ -1591,6 +1612,7 @@ Prokord 4AC 4USB {"NAME":"PSH-WT003-EU","GPIO":[0,320,0,32,226,227,0 S2199EU {"NAME":"S2199EU","GPIO":[0,32,0,288,226,228,0,0,224,227,225,0,0,4704],"FLAG":0,"BASE":18} SA-P402A {"NAME":"SA-P402A","GPIO":[0,32,0,320,226,228,224,227,225,0,0,0,0,4704],"FLAG":0,"BASE":18} Smart Tech 4AC 4USB {"NAME":"ST-T-SPS1","GPIO":[0,576,320,0,228,225,0,0,227,32,226,224,0,0],"FLAG":0,"BASE":18} +SmartVU Home 4AC 2USB {"NAME":"SHWSP4","GPIO":[0,288,0,32,224,225,0,0,227,228,226,0,0,0],"FLAG":0,"BASE":18} Soundance 3AC 2USB {"NAME":"Soundance C198","GPIO":[256,0,0,0,320,289,0,0,225,0,226,227,260,0],"FLAG":0,"BASE":18} STITCH 4 AC, 2 Always-On USB {"NAME":"MP Stitch 34082","GPIO":[320,0,0,0,227,32,0,0,225,226,224,0,0,0],"FLAG":0,"BASE":18} Surge Protector 3AC 2USB {"NAME":"C158","GPIO":[260,0,0,0,261,230,0,0,224,0,225,226,259,0],"FLAG":0,"BASE":18} @@ -1604,6 +1626,7 @@ Tonbux SM-SO301-U {"NAME":"Tonbux SM-SO30","GPIO":[320,0,0,0,256,0,0, Useelink {"NAME":"Useelink","GPIO":[288,0,0,321,256,32,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18} Vivitar HA-1007 {"NAME":"Vivitar HA-1007 Power Strip","GPIO":[544,0,0,0,227,228,0,0,225,224,226,0,35,1],"FLAG":0,"BASE":18} Vivitar HA-1007-AU {"NAME":"HA-1007-AU","GPIO":[320,32,0,322,256,321,0,0,258,257,259,0,228,0],"FLAG":0,"BASE":18} +wesmartify essentials 3-socket 2 USB {"NAME":"Essentials Smart Home 3-socket USB Power Strip","GPIO":[0,0,0,0,544,226,0,0,224,32,225,0,0,0],"FLAG":0,"BASE":18} wesmartify essentials 4AC+4USB {"NAME":"essential_4_po","GPIO":[320,0,0,0,227,228,0,0,225,224,226,0,32,0],"FLAG":0,"BASE":18} Wipro Smart Extension {"NAME":"Generic","GPIO":[321,0,0,0,259,0,0,0,257,258,256,0,228,0],"FLAG":0,"BASE":18} Wolf Armor 4AC 4USB {"NAME":"Wolf Armor SP26","GPIO":[0,320,0,32,227,226,0,0,224,228,225,0,0,0],"FLAG":0,"BASE":18} @@ -1655,9 +1678,11 @@ Wipro Garnet NS7001 480lm {"NAME":"WiproSmartBulb","GPIO":[0,0,0,0,416,419,0, ``` Aigital LE13 800lm {"NAME":"Aigital 9W RGB","GPIO":[0,0,0,0,420,417,0,0,418,0,419,416,0,0],"FLAG":0,"BASE":18} Aisirer 10W 1000lm {"NAME":"Aisirer RGBCW","GPIO":[160,0,0,0,0,0,0,0,0,4032,4064,0,0,0],"FLAG":0,"BASE":18} +AiYaTo 12W {"NAME":"AiYaTo RGBCW","GPIO":[0,0,0,0,419,418,0,0,416,420,417,0,0,0],"FLAG":0,"BASE":18} Alfawise LE12 9W 900LM {"NAME":"Alfawise LE12 ","GPIO":[0,0,0,0,420,417,0,0,418,0,419,416,0,0],"FLAG":0,"BASE":18} Aoycocr JL81 5W 400lm {"NAME":"AoycocrJLB1","GPIO":[0,0,0,0,418,0,0,0,417,420,416,419,0,0],"FLAG":0,"BASE":18} Aoycocr Q10CWM BR30 9W 720lm {"NAME":"AoycocrBR30","GPIO":[0,0,0,0,0,418,0,0,417,0,416,419,0,0],"FLAG":0,"BASE":18} +Arlec Smart 10W 830lm {"NAME":"Arlec GLD320HA","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Arlec Smart 9.5W 806lm {"NAME":"Arlec RGBWW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Arlec Smart 9.5W 806lm {"NAME":"Arlec RGBWW","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Athom 12W 1000lm {"NAME":"LB03-12W-E27-TAS","GPIO":[0,0,0,0,416,419,0,0,417,452,418,0,0,0],"FLAG":0,"BASE":18,"CMND":"SO92 1 | DimmerRange 24,100"} @@ -1767,6 +1792,7 @@ LVWIT G45 5W 470Lm {"NAME":"LVWIT E14 5W G45 RGBWCCT","GPIO":[0,0,0,0, Lyhope 7W 650lm {"NAME":"Lyhope 014BB06","GPIO":[0,0,0,0,417,416,0,0,420,418,419,0,0,0],"FLAG":0,"BASE":18} MagicHome 7W {"NAME":"MagicHome E27","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Makion 7W 600lm {"NAME":"Makion Smart LED Bulb","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} +Melery MR16 {"NAME":"Melery MR16 GU5.3","GPIO":[0,0,0,0,417,416,0,0,420,418,419,0,0,0],"FLAG":0,"BASE":18} Mirabella Genio 5W 450lm Candle {"NAME":"MiraBellaGenio","GPIO":[0,0,0,0,0,0,0,0,4068,0,4032,0,0,0],"FLAG":0,"BASE":18} Moes 9W 800lm {"NAME":"Moes 9w","GPIO":[0,0,0,0,416,419,0,0,417,420,418,0,0,0],"FLAG":0,"BASE":18} Nishica JBT 9W 806lm {"NAME":"Nishica","GPIO":[0,0,0,0,417,416,0,0,420,418,419,0,0,0],"FLAG":0,"BASE":18} @@ -2054,6 +2080,7 @@ LilyGO TTGO 4 Channel ESP32 {"NAME":"T-Relay ESP32","GPIO":[0,0,1,0,1,227,0,0,1 LinkNode R4 {"NAME":"LinkNode R4","GPIO":[0,0,0,0,0,0,0,0,224,225,226,0,227,0],"FLAG":0,"BASE":18} LinkNode R8 {"NAME":"LinkNode R8","GPIO":[0,0,0,0,228,229,0,231,226,227,225,230,224,0],"FLAG":0,"BASE":18} Mhcozy 5V {"NAME":"Portail","GPIO":[160,0,0,0,0,0,0,0,224,320,0,0,0,4704],"FLAG":0,"BASE":18} +Mhcozy 5V 2 Channel {"NAME":"MHCOZY RF 2ch","GPIO":[32,0,0,0,0,225,33,0,224,320,0,0,0,0],"FLAG":0,"BASE":18} Sinilink 6-32V Real Time Clock {"NAME":"Sinilink XY-WFBJ","GPIO":[0,0,544,0,7584,224,0,0,608,640,32,0,288,0],"FLAG":0,"BASE":18} Sinilink DC5V Module {"NAME":"Sinilink XY-WF5V","GPIO":[0,0,0,0,224,1,0,0,32,288,0,0,1,0],"FLAG":0,"BASE":18} Sinilink DC6-36V Module {"NAME":"Sinilink XY-WF5V","GPIO":[0,0,0,0,224,1,0,0,32,288,0,0,1,0],"FLAG":0,"BASE":18} @@ -2218,7 +2245,7 @@ Lerlink 3 Gang {"NAME":"X803A","GPIO":[0,0,0,33,32,34,0,0,224,288, Lerlink 3 Gang No Neutral {"NAME":"X803K-L 3 Gang","GPIO":[0,0,320,0,32,34,33,0,224,0,225,226,0,0],"FLAG":0,"BASE":18} Lightstory WT02S {"NAME":"WT02S","GPIO":[0,0,0,0,321,320,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":50} Linkind 2-Way {"NAME":"Linkind WS240010008","GPIO":[0,0,0,0,0,224,0,0,0,0,288,0,0,0,0,0,0,0,0,0,0,576,321,0,0,0,0,0,33,32,0,0,0,0,0,0],"FLAG":0,"BASE":1} -Linkind Dimmer {"NAME":"Linkind Dimmer","GPIO":[6213,8448,0,0,640,0,0,0,0,0,288,0,0,0,0,0,0,608,0,0,0,0,544,0,0,0,0,0,33,32,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Linkind Dimmer {"NAME":"Linkind Dimmer WS240010108","GPIO":[6213,8448,0,0,640,0,0,0,0,0,288,0,0,0,0,0,0,0,608,0,0,0,544,0,0,0,0,0,33,32,0,0,0,0,0,0],"FLAG":0,"BASE":1} LoGiX Multi Button RGB Scene Controller {"NAME":"Logix3","GPIO":[1,1,1,1,34,33,1,1,1376,32,1312,1,1,1],"FLAG":0,"BASE":18} Lonsonho SK3-01 {"NAME":"Tuya 1 Channel","GPIO":[0,0,0,0,0,32,0,0,0,0,0,224,288,0],"FLAG":0,"BASE":18} Lonsonho SK3-02 {"NAME":"Tuya 2 Channel","GPIO":[0,0,0,0,225,0,0,0,32,224,33,0,288,0],"FLAG":0,"BASE":18} @@ -2277,7 +2304,7 @@ NaamaSmart KS602 {"NAME":"KS-602","GPIO":[32,0,0,0,0,0,0,0,224,576,0 Nedis Dual {"NAME":"SM-SW102U-2","GPIO":[576,0,0,33,225,0,0,0,32,224,0,0,0,4704],"FLAG":0,"BASE":18} Nexete DS-123 {"NAME":"DS-123","GPIO":[544,321,1,32,224,33,0,0,1,225,320,1,1,0],"FLAG":0,"BASE":18} Nexete DS-123 Single {"NAME":"DS-123","GPIO":[544,0,1,33,0,32,0,0,1,224,320,1,1,0],"FLAG":0,"BASE":18} -Novadigital Interruptor Touch Led 1 Boto {"NAME":"Nova Digital Switch 1 Gang","GPIO":[544,0,0,32,224,0,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18} +Novadigital Interruptor Touch Led 1 Botno {"NAME":"Nova Digital Switch 1 Gang","GPIO":[544,0,0,32,224,0,0,0,0,0,288,0,0,0],"FLAG":0,"BASE":18} Prosto {"NAME":"Prosto WFS-T10","GPIO":[0,0,0,0,0,224,0,0,320,0,64,0,0,0],"FLAG":0,"BASE":18} Push Button 1/2/3/4 Gang {"NAME":"DS-122","GPIO":[321,0,0,32,0,0,0,0,0,224,288,0,0,0],"FLAG":0,"BASE":18} Q-touch 1 Gang {"NAME":"Qtouch","GPIO":[289,0,0,32,0,0,0,0,224,0,0,0,0,0],"FLAG":0,"BASE":1} @@ -2473,9 +2500,16 @@ Sonoff Dual R3 v2 {"NAME":"Sonoff Dual R3 v2","GPIO":[32,0,0,0,0,0,0, Sonoff Mini {"NAME":"Sonoff Mini","GPIO":[32,0,0,0,160,0,0,0,224,320,0,0,1,0],"FLAG":0,"BASE":1} Sonoff Mini R2 {"NAME":"Sonoff MINIR2","GPIO":[32,0,0,0,160,0,0,0,224,544,0,0,0,0],"FLAG":0,"BASE":1} Sonoff Pow {"NAME":"Sonoff Pow","GPIO":[32,0,0,0,0,2592,0,0,224,2656,2688,288,0,0],"FLAG":0,"BASE":6} +Sonoff POW Elite 16A {"NAME":"Sonoff POWR316D","GPIO":[32,0,0,0,0,576,0,0,0,224,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Sonoff POW Elite 20A {"NAME":"Sonoff POWR320D","GPIO":[32,0,9313,0,9312,576,0,0,0,0,9280,0,3104,0,320,0,0,0,0,0,0,9184,9248,9216,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Sonoff POW Origin 16A {"NAME":"Sonoff POWR316","GPIO":[32,0,0,0,0,576,0,0,0,224,0,0,3104,0,320,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} Sonoff Pow R2 {"NAME":"Sonoff Pow R2","GPIO":[32,3072,0,3104,0,0,0,0,224,320,0,0,0,0],"FLAG":0,"BASE":43} Sonoff POW R3 25A 5500W {"NAME":"Sonoff POWR3","GPIO":[32,3072,0,3104,0,0,0,0,256,320,0,1,1,1],"FLAG":0,"BASE":43} Sonoff RF {"NAME":"Sonoff RF","GPIO":[32,1,1,1,1,0,0,0,224,320,1,0,0,0],"FLAG":0,"BASE":2} +Sonoff TH Elite 16A Temperature and Humidity Monitoring {"NAME":"Sonoff THR316D","GPIO":[32,0,0,0,225,9280,0,0,0,321,0,576,320,9184,9216,0,0,224,0,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Sonoff TH Elite 20A Temperature and Humidity Monitoring {"NAME":"Sonoff THR320D","GPIO":[32,0,0,0,226,9280,0,0,0,321,0,576,320,9184,9216,9312,0,0,9313,9248,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Sonoff TH Origin 16A {"NAME":"Sonoff THR316","GPIO":[32,0,0,0,0,0,0,0,0,321,0,576,320,0,0,0,0,224,0,0,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} +Sonoff TH Origin 20A {"NAME":"Sonoff THR320","GPIO":[32,0,0,0,0,0,0,0,0,321,0,576,320,0,0,9312,0,0,9313,0,0,1,0,3840,0,0,0,0,0,0,0,0,0,0,0,0],"FLAG":0,"BASE":1} Sonoff TH10/TH16 {"NAME":"Sonoff TH","GPIO":[32,1,0,1,1,0,0,0,224,320,1,0,0,0],"FLAG":0,"BASE":4} Splatura USB Device Power Switch {"NAME":"Splatura USB","GPIO":[0,0,288,0,0,0,0,0,0,224,0,96,0,0],"FLAG":0,"BASE":18} SS-8839-02 {"NAME":"SS-8839-02","GPIO":[0,1,0,1,320,0,0,0,224,0,32,0,0,0],"FLAG":0,"BASE":18} @@ -2514,7 +2548,7 @@ Hoenyzy DN20 3/4 {"NAME":"DN20 Valve","GPIO":[288,0,0,0,0,0,0,0,32,2 Jinvoo SM-AW713 {"NAME":"Jinvoo Valve","GPIO":[0,0,0,0,0,288,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Jinvoo SM-PW713 {"NAME":"Jinvoo Valve","GPIO":[0,0,0,0,224,288,0,0,32,289,0,0,0,4704],"FLAG":0,"BASE":18} Moes Gas-Water {"NAME":"MoesHouse WV-B","GPIO":[0,0,0,0,0,0,0,0,32,224,0,0,0,0],"FLAG":0,"BASE":18} -Owfeel EN71 {"NAME":"SmartValve","GPIO":[224,0,0,0,0,0,0,0,32,288,0,0,0,0],"FLAG":0,"BASE":18} +Owfeel EN71 {"NAME":"SmartValve","GPIO":[288,0,0,0,0,0,0,0,32,224,0,0,0,0],"FLAG":0,"BASE":18} Steren Valvula {"NAME":"Steren_SHOME-150","GPIO":[0,0,0,0,0,0,0,0,544,0,32,0,224,0],"FLAG":0,"BASE":18} Tuya Gas/Water {"NAME":"Valve FM101","GPIO":[320,0,0,0,224,0,0,0,0,0,32,0,225,0],"FLAG":0,"BASE":18} ``` @@ -2523,6 +2557,7 @@ Tuya Gas/Water {"NAME":"Valve FM101","GPIO":[320,0,0,0,224,0,0,0,0 ``` 2AC 1USB {"NAME":"SM-SW801U","GPIO":[0,0,0,0,288,32,0,0,224,0,225,0,226,0],"FLAG":0,"BASE":18} Aseer THWFS01 {"NAME":"ASEER-THWFS01","GPIO":[320,33,544,323,2720,2656,0,0,2624,225,321,224,32,0],"FLAG":0,"BASE":18} +Athom {"NAME":"Athom SK01","GPIO":[0,0,0,3104,0,32,0,0,224,576,0,0,0,0],"FLAG":0,"BASE":18} Bestten LO-2-W {"NAME":"BESTTEN LO-2-W","GPIO":[0,0,0,0,576,32,0,0,224,0,0,0,0,0],"FLAG":0,"BASE":18} BingoElec 16A {"NAME":"BingoElecPower","GPIO":[0,0,0,0,288,289,1,1,224,32,0,0,1,1],"FLAG":0,"BASE":18} BlitzWolf SHP8 {"NAME":"SHP8","GPIO":[0,320,0,32,2720,2656,0,0,2624,289,224,0,0,0],"FLAG":0,"BASE":64} @@ -2546,6 +2581,7 @@ Makegood MG-AUWF01 {"NAME":"MG-AUWF01","GPIO":[320,161,544,323,2720,26 Makegood MG-UKWSG01 {"NAME":"Aseer 2-Gang","GPIO":[321,160,544,323,2720,2656,0,0,2624,224,320,225,161,0],"FLAG":0,"BASE":18} Makegood MG-UKWSW/B {"NAME":"Aseer 1-Gang","GPIO":[320,0,544,321,2720,2656,0,0,2624,0,0,224,160,0],"FLAG":0,"BASE":18} Master Contacto WiFi de pared {"NAME":"Master_IOT-WLSOCKET","GPIO":[32,0,0,0,0,225,33,0,224,320,0,0,0,0],"FLAG":0,"BASE":18} +Milfra UK Double USB Chager Twin {"NAME":"Milfra TBU02","GPIO":[0,0,0,0,288,33,0,0,257,256,258,0,32,0],"FLAG":0,"BASE":18} Moes WWK Glass Panel {"NAME":"Smart Socket","GPIO":[0,0,0,0,288,289,0,0,224,32,0,0,0,0],"FLAG":0,"BASE":18} Oittm 120 {"NAME":"Oittm WS01","GPIO":[32,0,0,0,0,2592,0,0,224,2656,2688,288,0,0],"FLAG":0,"BASE":18} PS-1607 {"NAME":"PS-1607","GPIO":[32,0,0,0,0,225,33,0,224,0,0,0,0,0],"FLAG":0,"BASE":18} @@ -2578,3 +2614,8 @@ Sonoff ZBBridge Pro {"NAME":"Sonoff Zigbee Pro","GPIO":[0,0,576,0,480,0 Tube's CC2652P2 Ethernet {"NAME":"Tube ZB CC2652","GPIO":[0,0,0,3840,0,3584,0,0,0,0,0,0,5536,3552,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,3840,5792,0,0,0,0,0,0],"FLAG":0,"BASE":1} Tube's EFR32 Ethernet {"NAME":"Tube ZB EFR32","GPIO":[0,0,0,3840,0,3552,1,0,0,0,0,0,5536,3584,5600,0,0,0,0,5568,0,0,0,0,0,0,0,0,5793,5792,0,0,0,0,0,0],"FLAG":0,"BASE":1} ``` + +## module +``` +Ewelink RF No Neutral 3 Channel {"NAME":"Ewelink 3 Gang Module","GPIO":[32,0,0,0,225,226,33,34,224,544,0,0,0,0],"FLAG":0,"BASE":18} +``` diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index 38247b377..cfd73de8e 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -162,7 +162,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t tls_use_fingerprint : 1; // bit 18 (v10.0.0.4) - SetOption132 - (TLS) Use fingerprint validation instead of CA based uint32_t shift595_invert_outputs : 1; // bit 19 (v10.0.0.4) - SetOption133 - (Shift595) Invert outputs of 74x595 shift registers uint32_t pwm_force_same_phase : 1; // bit 20 (v10.1.0.6) - SetOption134 - (PWM) force PWM lights to start at same phase, default is to spread phases to minimze overlap (also needed for H-bridge) - uint32_t display_no_splash : 1; // bit 21 (v11.0.0.2) - SetOption135 - (Display & LVGL) forece disbabling default splash screen + uint32_t display_no_splash : 1; // bit 21 (v11.0.0.2) - SetOption135 - (Display & LVGL) force disabling default splash screen uint32_t tuyasns_no_immediate : 1; // bit 22 (v11.0.0.4) - SetOption136 - (TuyaSNS) When ON disable publish single SNS value on Tuya Receive (keep Teleperiod) uint32_t tuya_exclude_from_mqtt : 1; // bit 23 (v11.0.0.5) - SetOption137 - (Tuya) When Set, avoid the (MQTT-) publish of defined Tuya CMDs (see xdrv_16_tuyamcu.ino) if SetOption66 is active uint32_t gui_table_align : 1; // bit 24 (v11.0.0.7) - SetOption138 - (GUI) Align (energy) table values left (0) or right (1) @@ -171,7 +171,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t gui_module_name : 1; // bit 27 (v11.1.0.3) - SetOption141 - (GUI) Disable display of GUI module name (1) uint32_t wait_for_wifi_result : 1; // bit 28 (v11.1.0.4) - SetOption142 - (Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1) uint32_t zigbee_no_batt_autoprobe : 1; // bit 29 (v12.0.2.4) - SetOption143 - (Zigbee) Disable Battery auto-probe and using auto-binding - uint32_t zigbee_include_time : 1; // bit 30 (v12.0.2.4) - SetOption144 - (Zigbee) include time in `ZbReceived` messages like other sensors + uint32_t zigbee_include_time : 1; // bit 30 (v12.0.2.4) - SetOption144 - (Zigbee) Include time in `ZbReceived` messages like other sensors uint32_t spare31 : 1; // bit 31 }; } SOBitfield5; diff --git a/tools/decode-status.py b/tools/decode-status.py index 11e72981c..b0e162f6e 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -198,7 +198,8 @@ a_setoption = [[ "(GUI) Disable display of GUI module name (1)", "(Wifi) Wait 1 second for wifi connection solving some FRITZ!Box modem issues (1)", "(Zigbee) Disable Battery auto-probe and using auto-binding", - "","" + "(Zigbee) Include time in `ZbReceived` messages like other sensors", + "" ],[ "","","","", "","","","",