From ebf552665f8782531ae85f1afe65b78f761b6406 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 23 Aug 2024 23:15:26 +0200 Subject: [PATCH 01/48] [ESP-IDF5.3] Add support for ESP-IDF5.3 and SPIFFS builds for IDF5.1 --- platformio_core_defs.ini | 43 ++++++------------------ platformio_esp32_envs.ini | 8 ++--- platformio_esp32_solo1.ini | 4 +-- src/src/DataTypes/EthernetParameters.cpp | 6 ++++ src/src/DataTypes/EthernetParameters.h | 2 ++ src/src/WebServer/HardwarePage.cpp | 6 +++- 6 files changed, 29 insertions(+), 40 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 0743e1af7e..3bb494f849 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -167,36 +167,16 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; Just for those who lost track of the extremely confusing numbering schema. ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 [core_esp32_IDF4_4__2_0_14] -;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip - -; debug boot log enabled -; See: https://github.com/letscontrolit/ESPEasy/pull/4200#issuecomment-1216929859 -;platform = https://github.com/Jason2866/platform-espressif32.git -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/936/framework-arduinoespressif32-443_esp421-9ce849ce72.tar.gz - -; debug boot log disabled -;platform = https://github.com/Jason2866/platform-espressif32.git -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/938/framework-arduinoespressif32-443_esp421-10ab11e815.tar.gz - -;platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.5.2/platform-espressif32-2.0.5.2.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2022.12.2/platform-espressif32.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.01/platform-espressif32.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1212/framework-arduinoespressif32-release_v4.4-fb9a7685e1.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.01.02/platform-espressif32.zip -;platform_packages = -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.02.00/platform-espressif32.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1243/framework-arduinoespressif32-lwip_timeout-ed6742e7f0.zip - -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.05.03/platform-espressif32.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.06.04/platform-espressif32.zip -platform = https://github.com/tasmota/platform-espressif32/releases/download/2023.10.03/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1645/framework-arduinoespressif32-release_v4.4_spiffs-e3fc63b439.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2819/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip build_flags = -DESP32_STAGE - -DESP_IDF_VERSION_MAJOR=4 - -DMUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE + ;-DETH_TYPE_JL1101_SUPPORTED +; -DCONFIG_LWIP_L2_TO_L3_COPY +; -DETH_SPI_SUPPORTS_NO_IRQ=1 -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 -DNEOPIXEL_ESP32_RMT_DEFAULT @@ -209,18 +189,15 @@ build_flags = -DESP32_STAGE lib_ignore = ; ESP_IDF 5.1 -[core_esp32_IDF5_1__3_0_0] -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.11/platform-espressif32.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2286/framework-arduinoespressif32-all-release_v5.1-11140aa.zip -;platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.04.14/platform-espressif32.zip -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2386/framework-arduinoespressif32-all-release_v5.1-324fdc1.zip -platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.07.10/platform-espressif32.zip -platform_packages = +[core_esp32_IDF5_3__3_0_4_LittleFS] +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2807/framework-arduinoespressif32-all-release_v5.3-0c6101b5.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE + -DETH_TYPE_JL1101_SUPPORTED ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 105a56172f..3fad9f5d33 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -34,7 +34,7 @@ lib_ignore = [esp32_base_idf5] -extends = common, core_esp32_IDF5_1__3_0_0 +extends = common, core_esp32_IDF5_3__3_0_4_LittleFS upload_speed = 460800 upload_before_reset = default_reset upload_after_reset = hard_reset @@ -51,7 +51,7 @@ extra_scripts = post:tools/pio/post_esp32.py ; However LTO cannot optimize builds with text section literals and thus will result in quite a lot larger builds (80k - 140k larger) build_unflags = -Wall -fno-lto -build_flags = ${core_esp32_IDF5_1__3_0_0.build_flags} +build_flags = ${core_esp32_IDF5_3__3_0_4_LittleFS.build_flags} ; ${mqtt_flags.build_flags} -DMQTT_MAX_PACKET_SIZE=2048 -DCONFIG_FREERTOS_ASSERT_DISABLE @@ -65,7 +65,7 @@ build_flags = ${core_esp32_IDF5_1__3_0_0.build_flags} -DLWIP_IPV6=1 monitor_filters = esp32_exception_decoder lib_ignore = - ${core_esp32_IDF5_1__3_0_0.lib_ignore} + ${core_esp32_IDF5_3__3_0_4_LittleFS.lib_ignore} ; -flto cannot be used for ESP32 C3! @@ -116,7 +116,7 @@ board_build.filesystem = littlefs lib_ignore = ${esp32_always.lib_ignore} ESP32_ping ESP32 BLE Arduino - ${core_esp32_IDF5_1__3_0_0.lib_ignore} + ${core_esp32_IDF5_3__3_0_4_LittleFS.lib_ignore} [esp32_IRExt] diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index b27d256b84..e31cbc9eb9 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -18,8 +18,8 @@ build_unflags = ${esp32_base.build_unflags} ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 -platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.06.11/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2525/framework-arduinoespressif32-solo1-release_v5.1-e9a74b6.zip +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2808/framework-arduinoespressif32-solo1-release_v5.3-0c6101b5.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS diff --git a/src/src/DataTypes/EthernetParameters.cpp b/src/src/DataTypes/EthernetParameters.cpp index 3f5d4b1cf8..ceab278333 100644 --- a/src/src/DataTypes/EthernetParameters.cpp +++ b/src/src/DataTypes/EthernetParameters.cpp @@ -42,7 +42,9 @@ bool isValid(EthPhyType_t phyType) { case EthPhyType_t::TLK110: # if ESP_IDF_VERSION_MAJOR > 3 case EthPhyType_t::RTL8201: +#if ETH_TYPE_JL1101_SUPPORTED case EthPhyType_t::JL1101: +#endif case EthPhyType_t::DP83848: case EthPhyType_t::KSZ8041: case EthPhyType_t::KSZ8081: @@ -94,7 +96,9 @@ eth_phy_type_t to_ESP_phy_type(EthPhyType_t phyType) case EthPhyType_t::TLK110: return ETH_PHY_TLK110; # if ESP_IDF_VERSION_MAJOR > 3 case EthPhyType_t::RTL8201: return ETH_PHY_RTL8201; +# if ETH_TYPE_JL1101_SUPPORTED case EthPhyType_t::JL1101: return ETH_PHY_JL1101; +# endif case EthPhyType_t::DP83848: return ETH_PHY_DP83848; case EthPhyType_t::KSZ8041: return ETH_PHY_KSZ8041; case EthPhyType_t::KSZ8081: return ETH_PHY_KSZ8081; @@ -128,7 +132,9 @@ const __FlashStringHelper* toString(EthPhyType_t phyType) { case EthPhyType_t::TLK110: return F("TLK110"); # if ESP_IDF_VERSION_MAJOR > 3 case EthPhyType_t::RTL8201: return F("RTL8201"); +#if ETH_TYPE_JL1101_SUPPORTED case EthPhyType_t::JL1101: return F("JL1101"); +#endif case EthPhyType_t::DP83848: return F("DP83848"); case EthPhyType_t::KSZ8041: return F("KSZ8041"); case EthPhyType_t::KSZ8081: return F("KSZ8081"); diff --git a/src/src/DataTypes/EthernetParameters.h b/src/src/DataTypes/EthernetParameters.h index b139832ab1..a017e6c629 100644 --- a/src/src/DataTypes/EthernetParameters.h +++ b/src/src/DataTypes/EthernetParameters.h @@ -27,7 +27,9 @@ enum class EthPhyType_t : uint8_t { #if ESP_IDF_VERSION_MAJOR > 3 RTL8201 = 2, +#if ETH_TYPE_JL1101_SUPPORTED JL1101 = 3, +#endif DP83848 = 4, KSZ8041 = 5, KSZ8081 = 6, diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c91e854462..7542abce7e 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -253,7 +253,9 @@ void handle_hardware() { toString(EthPhyType_t::TLK110), #if ESP_IDF_VERSION_MAJOR > 3 toString(EthPhyType_t::RTL8201), +#if ETH_TYPE_JL1101_SUPPORTED toString(EthPhyType_t::JL1101), +#endif toString(EthPhyType_t::DP83848), toString(EthPhyType_t::KSZ8041), toString(EthPhyType_t::KSZ8081), @@ -279,8 +281,10 @@ void handle_hardware() { static_cast(EthPhyType_t::LAN8720), static_cast(EthPhyType_t::TLK110), #if ESP_IDF_VERSION_MAJOR > 3 - static_cast(EthPhyType_t::RTL8201), + static_cast(EthPhyType_t::RTL8201), +#if ETH_TYPE_JL1101_SUPPORTED static_cast(EthPhyType_t::JL1101), +#endif static_cast(EthPhyType_t::DP83848), static_cast(EthPhyType_t::KSZ8041), static_cast(EthPhyType_t::KSZ8081), From c07529fa897383c4876e92de8e529de3ad7d256d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 00:02:26 +0200 Subject: [PATCH 02/48] [ESP-IDF5.3] Fix crashes on sysinfo page --- platformio_core_defs.ini | 3 +- platformio_esp32_envs.ini | 6 +- src/src/Helpers/Hardware_device_info.cpp | 157 +++++++++++++---------- 3 files changed, 91 insertions(+), 75 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 3bb494f849..944da391b8 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -166,7 +166,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4 ; Just for those who lost track of the extremely confusing numbering schema. ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 -[core_esp32_IDF4_4__2_0_14] +[core_esp32_IDF5_1__3_0_2_SPIFFS] platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2819/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip build_flags = -DESP32_STAGE @@ -174,6 +174,7 @@ build_flags = -DESP32_STAGE -DLIBRARIES_NO_LOG=1 -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE + -DESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED ;-DETH_TYPE_JL1101_SUPPORTED ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 3fad9f5d33..bdfd29ea39 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -6,7 +6,7 @@ [esp32_base] -extends = common, core_esp32_IDF4_4__2_0_14 +extends = common, core_esp32_IDF5_1__3_0_2_SPIFFS upload_speed = 460800 upload_before_reset = default_reset upload_after_reset = hard_reset @@ -19,7 +19,7 @@ extra_scripts = post:tools/pio/post_esp32.py ; pre:tools/pio/apply_patches.py build_unflags = -Wall ; -fno-lto -build_flags = ${core_esp32_IDF4_4__2_0_14.build_flags} +build_flags = ${core_esp32_IDF5_1__3_0_2_SPIFFS.build_flags} ; ${mqtt_flags.build_flags} -DMQTT_MAX_PACKET_SIZE=2048 -DCONFIG_FREERTOS_ASSERT_DISABLE @@ -30,7 +30,7 @@ build_flags = ${core_esp32_IDF4_4__2_0_14.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder lib_ignore = - ${core_esp32_IDF4_4__2_0_14.lib_ignore} + ${core_esp32_IDF5_1__3_0_2_SPIFFS.lib_ignore} [esp32_base_idf5] diff --git a/src/src/Helpers/Hardware_device_info.cpp b/src/src/Helpers/Hardware_device_info.cpp index 47d0b4ec9e..890a3c5456 100644 --- a/src/src/Helpers/Hardware_device_info.cpp +++ b/src/src/Helpers/Hardware_device_info.cpp @@ -74,7 +74,7 @@ // #include # endif // if ESP_IDF_VERSION_MAJOR >= 5 -#endif // ifdef ESP32 +#endif // ifdef ESP32 /********************************************************************************************\ Hardware information @@ -124,59 +124,62 @@ int32_t getPartitionInfo(ESP8266_partition_type ptype, uint32_t& address, int32_ bool isFlashInterfacePin_ESPEasy(int gpio) { -# if CONFIG_IDF_TARGET_ESP32 +#if CONFIG_IDF_TARGET_ESP32 // GPIO-6 ... 11: SPI flash and PSRAM // GPIO-16 & 17: CS for PSRAM, thus only unuable when PSRAM is present - return ((gpio) >= 6 && (gpio) <= 11); + return (gpio) >= 6 && (gpio) <= 11; -# elif CONFIG_IDF_TARGET_ESP32S3 +#elif CONFIG_IDF_TARGET_ESP32S3 // GPIO-26 ... 32: SPI flash and PSRAM // GPIO-33 ... 37: SPI 8 ­line mode (OPI) pins for flash or PSRAM, like ESP32-S3R8 / ESP32-S3R8V. - return ((gpio) >= 26 && (gpio) <= 32); + return (gpio) >= 26 && (gpio) <= 32; -# elif CONFIG_IDF_TARGET_ESP32S2 +#elif CONFIG_IDF_TARGET_ESP32S2 // GPIO-22 ... 25: SPI flash and PSRAM // GPIO-26: CS for PSRAM, thus only unuable when PSRAM is present // GPIO-27 ... 32: SPI 8 ­line mode (OPI) pins for flash or PSRAM (e.g. ESP32-S2FH2 and ESP32-S2FH4) - return ((gpio) >= 22 && (gpio) <= 25); + return (gpio) >= 22 && (gpio) <= 25; -# elif CONFIG_IDF_TARGET_ESP32C6 +#elif CONFIG_IDF_TARGET_ESP32C6 // FIXME TD-er: Must know whether we have internal or external flash // For chip variants with an in-package flash, this pin can not be used. - if (gpio == 10 || gpio == 11) + if ((gpio == 10) || (gpio == 11)) { return true; + } // For chip variants without an in-package flash, this pin can not be used. -// if (gpio == 14) -// return true; - + // if (gpio == 14) + // return true; + // GPIO-27: Flash voltage selector // GPIO-24 ... 30: Connected to internal flash (might be available when using external flash???) - return ((gpio) >= 24 && (gpio) <= 30 && gpio != 27); + return (gpio) >= 24 && (gpio) <= 30 && gpio != 27; -# elif CONFIG_IDF_TARGET_ESP32C3 +#elif CONFIG_IDF_TARGET_ESP32C3 // GPIO-11: Flash voltage selector // GPIO-12 ... 17: Connected to flash - return ((gpio) >= 12 && (gpio) <= 17); + return (gpio) >= 12 && (gpio) <= 17; -# elif CONFIG_IDF_TARGET_ESP32C2 +#elif CONFIG_IDF_TARGET_ESP32C2 // GPIO-11: Flash voltage selector // For chip variants with a SiP flash built in, GPIO11~ GPIO17 are dedicated to connecting SiP flash, not for other uses - return ((gpio) >= 12 && (gpio) <= 17); + return (gpio) >= 12 && (gpio) <= 17; -# elif defined(ESP8266) - if (isESP8285()) - return ((gpio) == 6 || (gpio) == 7 || (gpio) == 8 || (gpio) == 11); - return ((gpio) >= 6 && (gpio) <= 11); +#elif defined(ESP8266) -# endif // if CONFIG_IDF_TARGET_ESP32 + if (isESP8285()) { + return (gpio) == 6 || (gpio) == 7 || (gpio) == 8 || (gpio) == 11; + } + return (gpio) >= 6 && (gpio) <= 11; + +#endif // if CONFIG_IDF_TARGET_ESP32 } uint32_t getFlashChipId() { @@ -251,25 +254,25 @@ String getChipFeaturesString() { const int32_t flash_cap = getEmbeddedFlashSize(); - if (getChipFeatures().embeddedFlash || (flash_cap != 0)) { + if (getChipFeatures().embeddedFlash || (flash_cap != 0)) { if (flash_cap > 0) { features += strformat(F("%dMB "), flash_cap); } else if (flash_cap < 0) { features += strformat(F("(%d) "), flash_cap); } - features += F("Emb. Flash"); - features += F(" / "); + features += F("Emb. Flash"); + features += F(" / "); } const int32_t psram_cap = getEmbeddedPSRAMSize(); - if (getChipFeatures().embeddedPSRAM || (psram_cap != 0)) { + if (getChipFeatures().embeddedPSRAM || (psram_cap != 0)) { if (psram_cap > 0) { features += strformat(F("%dMB "), psram_cap); } else if (psram_cap < 0) { features += strformat(F("(%d) "), psram_cap); } - features += F("Emb. PSRAM"); + features += F("Emb. PSRAM"); } features.trim(); @@ -289,27 +292,35 @@ bool getFlashChipOPI_wired() { # endif // ifdef ESP32_CLASSIC } -#endif // ifdef ESP32 +#endif // ifdef ESP32 uint32_t getFlashChipSpeed() { #ifdef ESP8266 return ESP.getFlashChipSpeed(); #else // ifdef ESP8266 +# if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED + + // ESP IDF still needs to patch those SPI registers + // for which patches have been submitted and somehow they managed to merge it completely wrong. + return ESP.getFlashChipSpeed(); +# else // if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED + // All ESP32-variants have the SPI flash wired to SPI peripheral 1 const uint32_t spi_clock = REG_READ(SPI_CLOCK_REG(1)); -/* - addLog(LOG_LEVEL_INFO, strformat( - F("SPI_clock: %x FSPI: %d SPI_CLOCK_REG(1): %x"), - spi_clock, FSPI, SPI_CLOCK_REG(1))); -*/ + /* + addLog(LOG_LEVEL_INFO, strformat( + F("SPI_clock: %x FSPI: %d SPI_CLOCK_REG(1): %x"), + spi_clock, FSPI, SPI_CLOCK_REG(1))); + */ if (spi_clock & BIT(31)) { // spi_clk is equal to system clock return getApbFrequency(); } return spiClockDivToFrequency(spi_clock); +# endif // if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED #endif // ifdef ESP8266 } @@ -495,14 +506,14 @@ const __FlashStringHelper* getChipModel() { switch (pkg_version) { case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ6: - if (single_core) { return F("ESP32-S0WDQ6"); } // Max 240MHz, Single core, QFN 6*6 + if (single_core) { return F("ESP32-S0WDQ6"); } // Max 240MHz, Single core, QFN 6*6 else if (rev3) { return F("ESP32-D0WDQ6-V3"); } // Max 240MHz, Dual core, QFN 6*6 - else { return F("ESP32-D0WDQ6"); } // Max 240MHz, Dual core, QFN 6*6 + else { return F("ESP32-D0WDQ6"); } // Max 240MHz, Dual core, QFN 6*6 case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDQ5: if (single_core) { return F("ESP32-S0WD"); } // Max 160MHz, Single core, QFN 5*5, ESP32-SOLO-1, ESP32-DevKitC else if (rev3) { return F("ESP32-D0WDQ5-V3"); } // Max 240MHz, Dual core, QFN 5*5, ESP32-WROOM-32E, ESP32_WROVER-E, ESP32-DevKitC - else { return F("ESP32-D0WDQ5"); } // Max 240MHz, Dual core, QFN 5*5, ESP32-WROOM-32D, ESP32_WROVER-B, ESP32-DevKitC + else { return F("ESP32-D0WDQ5"); } // Max 240MHz, Dual core, QFN 5*5, ESP32-WROOM-32D, ESP32_WROVER-B, ESP32-DevKitC case EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5: return F("ESP32-D2WDQ5"); // Max 160MHz, Dual core, QFN 5*5, 2MB embedded flash case 3: @@ -610,13 +621,13 @@ const __FlashStringHelper* getChipModel() { the HMAC peripheral, “World Controller” */ -/* + /* - efuse_reg.h: - EFUSE_RD_MAC_SPI_SYS_0_REG = block1_addr - EFUSE_RD_MAC_SPI_SYS_3_REG = block1_addr + (4 * num_word)) // (num_word = 3) + efuse_reg.h: + EFUSE_RD_MAC_SPI_SYS_0_REG = block1_addr + EFUSE_RD_MAC_SPI_SYS_3_REG = block1_addr + (4 * num_word)) // (num_word = 3) -*/ + */ # ifdef CONFIG_IDF_TARGET_ESP32S3 # if (ESP_IDF_VERSION_MAJOR >= 5) @@ -716,20 +727,21 @@ const __FlashStringHelper* getChipModel() { return F("ESP32"); #elif defined(ESP8266) uint32_t pkg_version{}; - bool high_temp_version{}; + bool high_temp_version{}; + if (isESP8285(pkg_version, high_temp_version)) { switch (pkg_version) { case 1: - return (high_temp_version) - ? F("ESP8285H08") // 1M flash + return (high_temp_version) + ? F("ESP8285H08") // 1M flash : F("ESP8285N08"); case 2: - return (high_temp_version) - ? F("ESP8285H16") // 2M flash + return (high_temp_version) + ? F("ESP8285H16") // 2M flash : F("ESP8285N16"); case 4: - return (high_temp_version) - ? F("ESP8285H32") // 4M flash + return (high_temp_version) + ? F("ESP8285H32") // 4M flash : F("ESP8285N32"); } return F("ESP8285"); @@ -745,28 +757,32 @@ bool isESP8285(uint32_t& pkg_version, bool& high_temp_version) // https://github.com/arendst/Tasmota/blob/62675a37a0e7b46283e2fdfe459bb8fd29d1cc2a/tasmota/tasmota_support/support_esp.ino#L151 /* - ESP8266 SoCs - - 32-bit MCU & 2.4 GHz Wi-Fi - - High-performance 160 MHz single-core CPU - - +19.5 dBm output power ensures a good physical range - - Sleep current is less than 20 μA, making it suitable for battery-powered and wearable-electronics applications - - Peripherals include UART, GPIO, I2C, I2S, SDIO, PWM, ADC and SPI - */ + ESP8266 SoCs + - 32-bit MCU & 2.4 GHz Wi-Fi + - High-performance 160 MHz single-core CPU + - +19.5 dBm output power ensures a good physical range + - Sleep current is less than 20 μA, making it suitable for battery-powered and wearable-electronics applications + - Peripherals include UART, GPIO, I2C, I2S, SDIO, PWM, ADC and SPI + */ + // esptool.py get_efuses - uint32_t efuse0 = *(uint32_t*)(0x3FF00050); -// uint32_t efuse1 = *(uint32_t*)(0x3FF00054); - uint32_t efuse2 = *(uint32_t*)(0x3FF00058); - uint32_t efuse3 = *(uint32_t*)(0x3FF0005C); - - bool r0_4 = efuse0 & (1 << 4); // ESP8285 - bool r2_16 = efuse2 & (1 << 16); // ESP8285 - if (r0_4 || r2_16) { // ESP8285 + uint32_t efuse0 = *(uint32_t *)(0x3FF00050); + + // uint32_t efuse1 = *(uint32_t*)(0x3FF00054); + uint32_t efuse2 = *(uint32_t *)(0x3FF00058); + uint32_t efuse3 = *(uint32_t *)(0x3FF0005C); + + bool r0_4 = efuse0 & (1 << 4); // ESP8285 + bool r2_16 = efuse2 & (1 << 16); // ESP8285 + + if (r0_4 || r2_16) { // ESP8285 // 1M 2M 2M 4M flash size // r0_4 1 1 0 0 - bool r3_25 = efuse3 & (1 << 25); // flash matrix 0 0 1 1 - bool r3_26 = efuse3 & (1 << 26); // flash matrix 0 1 0 1 - bool r3_27 = efuse3 & (1 << 27); // flash matrix 0 0 0 0 + bool r3_25 = efuse3 & (1 << 25); // flash matrix 0 0 1 1 + bool r3_26 = efuse3 & (1 << 26); // flash matrix 0 1 0 1 + bool r3_27 = efuse3 & (1 << 27); // flash matrix 0 0 0 0 pkg_version = 0; + if (!r3_27) { if (r0_4 && !r3_25) { pkg_version = (r3_26) ? 2 : 1; @@ -775,17 +791,16 @@ bool isESP8285(uint32_t& pkg_version, bool& high_temp_version) pkg_version = (r3_26) ? 4 : 2; } } - high_temp_version = efuse0 & (1 << 5); // Max flash temperature (0 = 85C, 1 = 105C) + high_temp_version = efuse0 & (1 << 5); // Max flash temperature (0 = 85C, 1 = 105C) return true; } return false; } - bool isESP8285() { #ifdef ESP8266 uint32_t pkg_version{}; - bool high_temp_version{}; + bool high_temp_version{}; return isESP8285(pkg_version, high_temp_version); #else // ifdef ESP8266 return false; @@ -846,7 +861,7 @@ bool FoundPSRAM() { # else // if ESP_IDF_VERSION_MAJOR >= 5 # if CONFIG_IDF_TARGET_ESP32C3 return psramFound(); -# else // if CONFIG_IDF_TARGET_ESP32C3 +# else // if CONFIG_IDF_TARGET_ESP32C3 return psramFound() && esp_spiram_is_initialized(); # endif // if CONFIG_IDF_TARGET_ESP32C3 # endif // if ESP_IDF_VERSION_MAJOR >= 5 @@ -929,4 +944,4 @@ bool isPSRAMInterfacePin(int gpio) { # endif // ifndef isPSRAMInterfacePin -#endif // ESP32 +#endif // ESP32 From f648208f0566f7a49be6ee7cb6844bd2e53d33b8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 00:46:50 +0200 Subject: [PATCH 03/48] [ESP-IDF5.1] Add LTO to reduce bin size for ESP-IDF5.1 SPIFFS build --- platformio_esp32_envs.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index bdfd29ea39..13a27af924 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -18,14 +18,14 @@ extra_scripts = post:tools/pio/post_esp32.py ; more detail: https://embeddedartistry.com/blog/2020/04/13/prefer-gcc-ar-to-ar-in-your-buildsystems/ ; pre:tools/pio/apply_patches.py build_unflags = -Wall -; -fno-lto + -fno-lto build_flags = ${core_esp32_IDF5_1__3_0_2_SPIFFS.build_flags} ; ${mqtt_flags.build_flags} -DMQTT_MAX_PACKET_SIZE=2048 -DCONFIG_FREERTOS_ASSERT_DISABLE -DCONFIG_LWIP_ESP_GRATUITOUS_ARP -fno-strict-aliasing -; -flto + -flto=auto -Wswitch -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder From 9e4accf3fb31db48dfefe94e8878da7a1fa75483 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 10:24:49 +0200 Subject: [PATCH 04/48] [ESP-IDF5.3] Fix usable UART ports on ESP32-C6 --- lib/ESPEasySerial/ESPEasySerialPort.cpp | 32 +++++++++---------- lib/ESPEasySerial/ESPEasySerialPort.h | 8 ++--- lib/ESPEasySerial/ESPEasySerialType_ESP32.cpp | 16 +++++----- .../ESPEasySerial_common_defines.h | 16 ++++++++++ .../Port_ESPEasySerial_HardwareSerial.cpp | 17 +++++----- src/src/Helpers/_Plugin_Helper_serial.cpp | 10 +++--- 6 files changed, 58 insertions(+), 41 deletions(-) diff --git a/lib/ESPEasySerial/ESPEasySerialPort.cpp b/lib/ESPEasySerial/ESPEasySerialPort.cpp index 951c50de5e..e8db0e6b0e 100644 --- a/lib/ESPEasySerial/ESPEasySerialPort.cpp +++ b/lib/ESPEasySerial/ESPEasySerialPort.cpp @@ -11,12 +11,12 @@ const __FlashStringHelper* ESPEasySerialPort_toString(ESPEasySerialPort port, bo case ESPEasySerialPort::serial0_swap: return shortName ? F("serial0swap") : F("HW Serial0 swap"); #endif // ifdef ESP8266 case ESPEasySerialPort::serial0: return shortName ? F("serial0") : F("HW Serial0"); -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: return shortName ? F("serial1") : F("HW Serial1"); -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: return shortName ? F("serial2") : F("HW Serial2"); -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 #if USES_SW_SERIAL case ESPEasySerialPort::software: return shortName ? F("serialsw") : F("SW Serial"); #endif // if USES_SW_SERIAL @@ -43,12 +43,12 @@ bool isHWserial(ESPEasySerialPort port) case ESPEasySerialPort::serial0_swap: #endif // ifdef ESP8266 case ESPEasySerialPort::serial0: -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 return true; default: break; @@ -63,12 +63,12 @@ bool useGPIOpins(ESPEasySerialPort port) #ifdef ESP8266 case ESPEasySerialPort::serial0_swap: #endif // ifdef ESP8266 -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 #if USES_SW_SERIAL case ESPEasySerialPort::software: #endif // if USES_SW_SERIAL @@ -90,12 +90,12 @@ bool validSerialPort(ESPEasySerialPort port) #ifdef ESP8266 case ESPEasySerialPort::serial0_swap: #endif // ifdef ESP8266 -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 #if USES_SW_SERIAL case ESPEasySerialPort::software: #endif // if USES_SW_SERIAL diff --git a/lib/ESPEasySerial/ESPEasySerialPort.h b/lib/ESPEasySerial/ESPEasySerialPort.h index 9d6f767594..79eb51a398 100644 --- a/lib/ESPEasySerial/ESPEasySerialPort.h +++ b/lib/ESPEasySerial/ESPEasySerialPort.h @@ -14,12 +14,12 @@ enum class ESPEasySerialPort : uint8_t { #ifdef ESP8266 serial0_swap = 3, #endif // ifdef ESP8266 -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 serial1 = 4, -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 serial2 = 5, -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 #if USES_SW_SERIAL software = 6, #endif // if USES_SW_SERIAL diff --git a/lib/ESPEasySerial/ESPEasySerialType_ESP32.cpp b/lib/ESPEasySerial/ESPEasySerialType_ESP32.cpp index 6772d6fc74..cb705867e6 100644 --- a/lib/ESPEasySerial/ESPEasySerialType_ESP32.cpp +++ b/lib/ESPEasySerial/ESPEasySerialType_ESP32.cpp @@ -10,12 +10,12 @@ bool ESPeasySerialType::getSerialTypePins(ESPEasySerialPort serType, int& rxPin, switch (serType) { case ESPEasySerialPort::serial0: rxPin = SOC_RX0; txPin = SOC_TX0; return true; -# if SOC_UART_NUM > 1 +# if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: rxPin = SOC_RX1; txPin = SOC_TX1; return true; -# endif // if SOC_UART_NUM > 1 -# if SOC_UART_NUM > 2 +# endif // if USABLE_SOC_UART_NUM > 1 +# if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: rxPin = SOC_RX2; txPin = SOC_TX2; return true; -# endif // if SOC_UART_NUM > 2 +# endif // if USABLE_SOC_UART_NUM > 2 # if USES_I2C_SC16IS752 case ESPEasySerialPort::sc16is752: return true; # endif // if USES_I2C_SC16IS752 @@ -37,18 +37,18 @@ ESPEasySerialPort ESPeasySerialType::getSerialType(ESPEasySerialPort typeHint, i // Serial1 on ESP32 uses default pins connected to flash // So must make sure to set them to other pins. -# if SOC_UART_NUM > 1 +# if USABLE_SOC_UART_NUM > 1 if ((receivePin == SOC_RX1) && (transmitPin == SOC_TX1)) { return ESPEasySerialPort::serial1; // UART1 } -# endif // if SOC_UART_NUM > 1 -# if SOC_UART_NUM > 2 +# endif // if USABLE_SOC_UART_NUM > 1 +# if USABLE_SOC_UART_NUM > 2 if ((receivePin == SOC_RX2) && (transmitPin == SOC_TX2)) { return ESPEasySerialPort::serial2; // UART2 } -# endif // if SOC_UART_NUM > 2 +# endif // if USABLE_SOC_UART_NUM > 2 # if USES_I2C_SC16IS752 if ((receivePin >= 0x48) && (receivePin <= 0x57)) { diff --git a/lib/ESPEasySerial/ESPEasySerial_common_defines.h b/lib/ESPEasySerial/ESPEasySerial_common_defines.h index fff700ef03..88d2a508c4 100644 --- a/lib/ESPEasySerial/ESPEasySerial_common_defines.h +++ b/lib/ESPEasySerial/ESPEasySerial_common_defines.h @@ -15,6 +15,22 @@ static_assert(false, "Implement processor architecture"); # endif // ifdef ESP8266 #endif // ifndef SOC_UART_NUM +#ifndef USABLE_SOC_UART_NUM +# ifdef SOC_UART_HP_NUM + +// In ESP-IDF 5.3 the actual difference in high-power and low-power UART ports was defined. +# define USABLE_SOC_UART_NUM SOC_UART_HP_NUM +# else // ifdef SOC_UART_HP_NUM +# ifdef ESP32C6 + +// ESP32-C6 has 3 UARTs (2 HP UART, and 1 LP UART) +// We can only use the high-power ones +# define USABLE_SOC_UART_NUM 2 +# else // ifdef ESP32C6 +# define USABLE_SOC_UART_NUM SOC_UART_NUM +# endif // ifdef ESP32C6 +# endif // ifdef SOC_UART_HP_NUM +#endif // ifndef USABLE_SOC_UART_NUM #ifdef ESP32 diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp index e22b15318e..20f29dda7d 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_HardwareSerial.cpp @@ -7,6 +7,7 @@ #include #endif + Port_ESPEasySerial_HardwareSerial_t::Port_ESPEasySerial_HardwareSerial_t() {} Port_ESPEasySerial_HardwareSerial_t::~Port_ESPEasySerial_HardwareSerial_t() {} @@ -33,12 +34,12 @@ void Port_ESPEasySerial_HardwareSerial_t::resetConfig(const ESPEasySerialConfig& switch (config.port) { case ESPEasySerialPort::serial0: - #if SOC_UART_NUM > 1 + #if USABLE_SOC_UART_NUM > 1 case ESPEasySerialPort::serial1: - #endif // if SOC_UART_NUM > 1 - #if SOC_UART_NUM > 2 + #endif // if USABLE_SOC_UART_NUM > 1 + #if USABLE_SOC_UART_NUM > 2 case ESPEasySerialPort::serial2: - #endif // if SOC_UART_NUM > 2 + #endif // if USABLE_SOC_UART_NUM > 2 _config.port = config.port; break; default: @@ -55,14 +56,14 @@ void Port_ESPEasySerial_HardwareSerial_t::resetConfig(const ESPEasySerialConfig& } else if (_config.port == ESPEasySerialPort::serial0_swap) { _serial = &Serial; #endif // ifdef ESP8266 -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 } else if (_config.port == ESPEasySerialPort::serial1) { _serial = &Serial1; -#endif // if SOC_UART_NUM > 1 -#if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 2 } else if (_config.port == ESPEasySerialPort::serial2) { _serial = &Serial2; -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 } else { _config.port = ESPEasySerialPort::not_set; } diff --git a/src/src/Helpers/_Plugin_Helper_serial.cpp b/src/src/Helpers/_Plugin_Helper_serial.cpp index cd815129ab..1575a266b4 100644 --- a/src/src/Helpers/_Plugin_Helper_serial.cpp +++ b/src/src/Helpers/_Plugin_Helper_serial.cpp @@ -212,13 +212,13 @@ void serialHelper_webformLoad(ESPEasySerialPort port, int rxPinDef, int txPinDef //" document.querySelector('#taskdevicepin1').value =" STRINGIFY(SOC_RX0) ";" //" document.querySelector('#taskdevicepin2').value =" STRINGIFY(SOC_TX0) ";" " style = '';" -# if SOC_UART_NUM > 1 +# if USABLE_SOC_UART_NUM > 1 " } else if (elem.value == 4) {" //" document.querySelector('#taskdevicepin1').value =" STRINGIFY(SOC_RX1) ";" //" document.querySelector('#taskdevicepin2').value =" STRINGIFY(SOC_TX1) ";" " style = '';" #endif -# if SOC_UART_NUM > 2 +# if USABLE_SOC_UART_NUM > 2 " } else if (elem.value == 5) {" //" document.querySelector('#taskdevicepin1').value =" STRINGIFY(SOC_RX2) ";" //" document.querySelector('#taskdevicepin2').value =" STRINGIFY(SOC_TX2) ";" @@ -247,12 +247,12 @@ void serialHelper_webformLoad(ESPEasySerialPort port, int rxPinDef, int txPinDef #ifdef ESP8266 ,static_cast(ESPEasySerialPort::serial0_swap) #endif // ifdef ESP8266 -#if SOC_UART_NUM > 1 +#if USABLE_SOC_UART_NUM > 1 ,static_cast(ESPEasySerialPort::serial1) #endif -#if SOC_UART_NUM > 2 +#if USABLE_SOC_UART_NUM > 2 ,static_cast(ESPEasySerialPort::serial2) -#endif // if SOC_UART_NUM > 2 +#endif // if USABLE_SOC_UART_NUM > 2 #if USES_SW_SERIAL ,static_cast(ESPEasySerialPort::software) #endif // if USES_SW_SERIAL From aa42a917c7c60a39bed4c31759f69e979bc7dcfb Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 10:25:36 +0200 Subject: [PATCH 05/48] [Build] Fix build error on ESP32 Minimal OTA builds (debug/test builds) --- src/src/CustomBuild/define_plugin_sets.h | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 8af1f883f4..ef174d37fc 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3338,16 +3338,11 @@ To create/register a plugin, you have to : #endif #ifndef FEATURE_CHART_STORAGE_LAYOUT - #ifdef ESP32 + #if defined(LIMIT_BUILD_SIZE) || defined(BUILD_MINIMAL_OTA) + #define FEATURE_CHART_STORAGE_LAYOUT 0 + #else #define FEATURE_CHART_STORAGE_LAYOUT 1 #endif - #ifdef ESP8266 - #ifndef LIMIT_BUILD_SIZE - #define FEATURE_CHART_STORAGE_LAYOUT 1 - #else - #define FEATURE_CHART_STORAGE_LAYOUT 0 - #endif - #endif #endif #ifndef FEATURE_TARSTREAM_SUPPORT From 47f00cad67af5489417abc1d17ca2a76825ef92b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 10:26:33 +0200 Subject: [PATCH 06/48] [ESP-IDF5.3] Add 5 GHz WiFi modes for upcoming ESP32-C5 --- src/src/DataTypes/WiFiConnectionProtocol.cpp | 6 ++++++ src/src/DataTypes/WiFiConnectionProtocol.h | 4 ++++ src/src/ESPEasyCore/ESPEasyWifi.cpp | 22 ++++++++++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/src/DataTypes/WiFiConnectionProtocol.cpp b/src/src/DataTypes/WiFiConnectionProtocol.cpp index d56d52ff01..c5a72595fe 100644 --- a/src/src/DataTypes/WiFiConnectionProtocol.cpp +++ b/src/src/DataTypes/WiFiConnectionProtocol.cpp @@ -17,6 +17,12 @@ const __FlashStringHelper * toString(WiFiConnectionProtocol proto) { return F("802.11n (HT40)"); case WiFiConnectionProtocol::WiFi_Protocol_HE20: return F("802.11ax (HE20)"); +#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 2, 0) + case WiFiConnectionProtocol::WiFi_Protocol_11a: + return F("802.11a"); + case WiFiConnectionProtocol::WiFi_Protocol_VHT20: + return F("802.11ac (VHT20)"); +#endif case WiFiConnectionProtocol::WiFi_Protocol_LR: return F("802.11lr"); diff --git a/src/src/DataTypes/WiFiConnectionProtocol.h b/src/src/DataTypes/WiFiConnectionProtocol.h index e26acec1e6..a605bf3ec4 100644 --- a/src/src/DataTypes/WiFiConnectionProtocol.h +++ b/src/src/DataTypes/WiFiConnectionProtocol.h @@ -14,6 +14,10 @@ enum class WiFiConnectionProtocol { WiFi_Protocol_HT20, WiFi_Protocol_HT40, WiFi_Protocol_HE20, // WiFi 6 +#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 2, 0) + WiFi_Protocol_11a, // traditional (old) 5 GHz + WiFi_Protocol_VHT20, // 5 GHz WiFi 5, 802.11ac +#endif WiFi_Protocol_LR #endif }; diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index 296f660aeb..433f81b68c 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -862,6 +862,11 @@ float GetRSSIthreshold(float& maxTXpwr) { if (maxTXpwr > MAX_TX_PWR_DBM_n) maxTXpwr = MAX_TX_PWR_DBM_n; break; #ifdef ESP32 +#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 2, 0) + case WiFiConnectionProtocol::WiFi_Protocol_11a: + case WiFiConnectionProtocol::WiFi_Protocol_VHT20: + // FIXME TD-er: Must determine max. TX power for these 5 GHz modi +#endif case WiFiConnectionProtocol::WiFi_Protocol_LR: #endif case WiFiConnectionProtocol::Unknown: @@ -897,12 +902,17 @@ WiFiConnectionProtocol getConnectionProtocol() { wifi_phy_mode_t phymode; esp_wifi_sta_get_negotiated_phymode(&phymode); switch (phymode) { - case WIFI_PHY_MODE_11B: return WiFiConnectionProtocol::WiFi_Protocol_11b; - case WIFI_PHY_MODE_11G: return WiFiConnectionProtocol::WiFi_Protocol_11g; - case WIFI_PHY_MODE_HT20: return WiFiConnectionProtocol::WiFi_Protocol_HT20; - case WIFI_PHY_MODE_HT40: return WiFiConnectionProtocol::WiFi_Protocol_HT40; - case WIFI_PHY_MODE_HE20: return WiFiConnectionProtocol::WiFi_Protocol_HE20; - case WIFI_PHY_MODE_LR: return WiFiConnectionProtocol::WiFi_Protocol_LR; + case WIFI_PHY_MODE_11B: return WiFiConnectionProtocol::WiFi_Protocol_11b; + case WIFI_PHY_MODE_11G: return WiFiConnectionProtocol::WiFi_Protocol_11g; + case WIFI_PHY_MODE_HT20: return WiFiConnectionProtocol::WiFi_Protocol_HT20; + case WIFI_PHY_MODE_HT40: return WiFiConnectionProtocol::WiFi_Protocol_HT40; + case WIFI_PHY_MODE_HE20: return WiFiConnectionProtocol::WiFi_Protocol_HE20; + case WIFI_PHY_MODE_LR: return WiFiConnectionProtocol::WiFi_Protocol_LR; +#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 2, 0) + // 5 GHz + case WIFI_PHY_MODE_11A: return WiFiConnectionProtocol::WiFi_Protocol_11a; + case WIFI_PHY_MODE_VHT20: return WiFiConnectionProtocol::WiFi_Protocol_VHT20; +#endif } #endif } From 2d80a6c0e6704ded0f8c02ebb0044b2295d0e6a7 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 10:32:12 +0200 Subject: [PATCH 07/48] [HeatpumpIR] Update lib to latest code --- lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp | 356 ++++----- lib/HeatpumpIR/ElectroluxHeatpumpIR.h | 146 ++-- lib/HeatpumpIR/GreeHeatpumpIR.cpp | 716 ++++++++++++------ lib/HeatpumpIR/GreeHeatpumpIR.h | 195 ++++- lib/HeatpumpIR/HeatpumpIRFactory.cpp | 10 + lib/HeatpumpIR/HeatpumpIRFactory.h | 8 + lib/HeatpumpIR/HitachiHeatpumpIR.cpp | 12 +- lib/HeatpumpIR/IRSender.cpp | 12 +- lib/HeatpumpIR/IRSender.h | 4 +- lib/HeatpumpIR/IRSenderESP32.cpp | 19 +- lib/HeatpumpIR/IRSenderPWM.cpp | 8 +- lib/HeatpumpIR/KY26HeatpumpIR.cpp | 102 +++ lib/HeatpumpIR/KY26HeatpumpIR.h | 77 ++ lib/HeatpumpIR/NibeHeatpumpIR.cpp | 332 ++++++++ lib/HeatpumpIR/NibeHeatpumpIR.h | 84 ++ lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp | 146 ++++ lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h | 48 ++ lib/HeatpumpIR/R51MHeatpumpIR.cpp | 2 +- lib/HeatpumpIR/README.md | 2 + lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp | 34 +- lib/HeatpumpIR/ZHJG01HeatpumpIR.h | 6 +- lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp | 34 +- lib/HeatpumpIR/ZHLT01HeatpumpIR.h | 6 +- .../examples/KY26Test_Menu/KY26Test_Menu.ino | 183 +++++ lib/HeatpumpIR/keywords.txt | 1 + lib/HeatpumpIR/library.json | 4 +- lib/HeatpumpIR/library.properties | 2 +- 27 files changed, 1957 insertions(+), 592 deletions(-) create mode 100644 lib/HeatpumpIR/KY26HeatpumpIR.cpp create mode 100644 lib/HeatpumpIR/KY26HeatpumpIR.h create mode 100644 lib/HeatpumpIR/NibeHeatpumpIR.cpp create mode 100644 lib/HeatpumpIR/NibeHeatpumpIR.h create mode 100644 lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp create mode 100644 lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h create mode 100644 lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino diff --git a/lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp b/lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp index 5609d1fa0d..8f2099cd23 100644 --- a/lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp +++ b/lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp @@ -1,178 +1,178 @@ -#include "ElectroluxHeatpumpIR.h" - -// This is a protected method, i.e. generic Electrolux instances cannot be created -ElectroluxHeatpumpIR::ElectroluxHeatpumpIR() : HeatpumpIR() -{ -} - -// Support for YAL1F remote -ElectroluxYALHeatpumpIR::ElectroluxYALHeatpumpIR() : ElectroluxHeatpumpIR() -{ - static const char model[] PROGMEM = "electroluxyal"; - static const char info[] PROGMEM = "{\"mdl\":\"electroluxyal\",\"dn\":\"Electrolux YAL\",\"mT\":16,\"xT\":30,\"fs\":3}"; - - _model = model; - _info = info; - electroluxModel = ELECTROLUX_YAL; -} - -void ElectroluxHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) -{ - (void)swingVCmd; - (void)swingHCmd; - - // Sensible defaults for the heat pump mode - - uint8_t powerMode = ELECTROLUX_AIRCON1_POWER_ON; - uint8_t operatingMode = ELECTROLUX_AIRCON1_MODE_HEAT; - uint8_t fanSpeed = ELECTROLUX_AIRCON1_FAN_AUTO; - uint8_t temperature = 21; - uint8_t swingV = ELECTROLUX_VDIR_AUTO; - uint8_t swingH = ELECTROLUX_HDIR_AUTO; - - - if (powerModeCmd == POWER_OFF) - { - powerMode = ELECTROLUX_AIRCON1_POWER_OFF; - } - else - { - switch (operatingModeCmd) - { - case MODE_AUTO: - operatingMode = ELECTROLUX_AIRCON1_MODE_AUTO; - temperatureCmd = 25; - break; - case MODE_HEAT: - operatingMode = ELECTROLUX_AIRCON1_MODE_HEAT; - break; - case MODE_COOL: - operatingMode = ELECTROLUX_AIRCON1_MODE_COOL; - break; - case MODE_DRY: - operatingMode = ELECTROLUX_AIRCON1_MODE_DRY; - fanSpeedCmd = FAN_1; - break; - case MODE_FAN: - operatingMode = ELECTROLUX_AIRCON1_MODE_FAN; - break; - } - } - - switch (fanSpeedCmd) - { - case FAN_AUTO: - fanSpeed = ELECTROLUX_AIRCON1_FAN_AUTO; - break; - case FAN_1: - fanSpeed = ELECTROLUX_AIRCON1_FAN1; - break; - case FAN_2: - fanSpeed = ELECTROLUX_AIRCON1_FAN2; - break; - case FAN_3: - fanSpeed = ELECTROLUX_AIRCON1_FAN3; - break; - } - - switch (swingVCmd) - { - case VDIR_AUTO: - swingV = ELECTROLUX_VDIR_AUTO; - break; - case VDIR_SWING: - swingV = ELECTROLUX_VDIR_SWING; - break; - } - - switch (swingHCmd) - { - case HDIR_AUTO: - swingH = ELECTROLUX_HDIR_AUTO; - break; - case HDIR_SWING: - swingH = ELECTROLUX_HDIR_SWING; - break; - } - - - - - if (temperatureCmd > 15 && temperatureCmd < 31) - { - temperature = temperatureCmd - 16; - } - - sendElectrolux(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH); -} - -// Send the Electrolux code -void ElectroluxHeatpumpIR::sendElectrolux(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH) -{ - - uint8_t ElectroluxTemplate[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00 }; - // 0 1 2 3 4 5 6 7 - uint8_t i; - - // Set the Fan speed, operating mode and power state - ElectroluxTemplate[0] = fanSpeed | operatingMode | powerMode; - // Set the temperature - ElectroluxTemplate[1] = temperature; - - ElectroluxTemplate[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN - ElectroluxTemplate[3] = 0x50; // bits 4..7 always 0101 - ElectroluxTemplate[6] = 0x00; - - if (swingV == ELECTROLUX_VDIR_SWING) - { - ElectroluxTemplate[0] |= (1 << 6); // Enable swing by setting bit 6 - ElectroluxTemplate[4] |= 0x01; // Set vertical swing direction - } - if(swingH == ELECTROLUX_HDIR_SWING) - { - ElectroluxTemplate[0] |= (1 << 6); // Enable swing by setting bit 6 - ElectroluxTemplate[4] |= 0x10; // Set horizontal swing direction - } - - ElectroluxTemplate[7] = ((( - (ElectroluxTemplate[0] & 0x0F) + - (ElectroluxTemplate[1] & 0x0F) + - (ElectroluxTemplate[2] & 0x0F) + - (ElectroluxTemplate[3] & 0x0F) + - ((ElectroluxTemplate[4] & 0xF0) >> 4) + - ((ElectroluxTemplate[5] & 0xF0) >> 4) + - ((ElectroluxTemplate[6] & 0xF0) >> 4) + - 0x0A) & 0x0F) << 4) | (ElectroluxTemplate[7] & 0x0F); - - // 38 kHz PWM frequency - IR.setFrequency(38); - - // Send Header mark - IR.mark(ELECTROLUX_AIRCON1_HDR_MARK); - IR.space(ELECTROLUX_AIRCON1_HDR_SPACE); - - // Payload part #1 - for (i=0; i<4; i++) { - IR.sendIRbyte(ElectroluxTemplate[i], ELECTROLUX_AIRCON1_BIT_MARK, ELECTROLUX_AIRCON1_ZERO_SPACE, ELECTROLUX_AIRCON1_ONE_SPACE); - } - // Only three first bits of byte 4 are sent, this is always '010' - IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); - IR.space(ELECTROLUX_AIRCON1_ZERO_SPACE); - IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); - IR.space(ELECTROLUX_AIRCON1_ONE_SPACE); - IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); - IR.space(ELECTROLUX_AIRCON1_ZERO_SPACE); - - // Message space - IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); - IR.space(ELECTROLUX_AIRCON1_MSG_SPACE); - - // Payload part #2 - for (i=4; i<8; i++) { - IR.sendIRbyte(ElectroluxTemplate[i], ELECTROLUX_AIRCON1_BIT_MARK, ELECTROLUX_AIRCON1_ZERO_SPACE, ELECTROLUX_AIRCON1_ONE_SPACE); - } - - // End mark - IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); - IR.space(0); -} +#include "ElectroluxHeatpumpIR.h" + +// This is a protected method, i.e. generic Electrolux instances cannot be created +ElectroluxHeatpumpIR::ElectroluxHeatpumpIR() : HeatpumpIR() +{ +} + +// Support for YAL1F remote +ElectroluxYALHeatpumpIR::ElectroluxYALHeatpumpIR() : ElectroluxHeatpumpIR() +{ + static const char model[] PROGMEM = "electroluxyal"; + static const char info[] PROGMEM = "{\"mdl\":\"electroluxyal\",\"dn\":\"Electrolux YAL\",\"mT\":16,\"xT\":30,\"fs\":3}"; + + _model = model; + _info = info; + electroluxModel = ELECTROLUX_YAL; +} + +void ElectroluxHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) +{ + (void)swingVCmd; + (void)swingHCmd; + + // Sensible defaults for the heat pump mode + + uint8_t powerMode = ELECTROLUX_AIRCON1_POWER_ON; + uint8_t operatingMode = ELECTROLUX_AIRCON1_MODE_HEAT; + uint8_t fanSpeed = ELECTROLUX_AIRCON1_FAN_AUTO; + uint8_t temperature = 21; + uint8_t swingV = ELECTROLUX_VDIR_AUTO; + uint8_t swingH = ELECTROLUX_HDIR_AUTO; + + + if (powerModeCmd == POWER_OFF) + { + powerMode = ELECTROLUX_AIRCON1_POWER_OFF; + } + else + { + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = ELECTROLUX_AIRCON1_MODE_AUTO; + temperatureCmd = 25; + break; + case MODE_HEAT: + operatingMode = ELECTROLUX_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = ELECTROLUX_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = ELECTROLUX_AIRCON1_MODE_DRY; + fanSpeedCmd = FAN_1; + break; + case MODE_FAN: + operatingMode = ELECTROLUX_AIRCON1_MODE_FAN; + break; + } + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = ELECTROLUX_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = ELECTROLUX_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = ELECTROLUX_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = ELECTROLUX_AIRCON1_FAN3; + break; + } + + switch (swingVCmd) + { + case VDIR_AUTO: + swingV = ELECTROLUX_VDIR_AUTO; + break; + case VDIR_SWING: + swingV = ELECTROLUX_VDIR_SWING; + break; + } + + switch (swingHCmd) + { + case HDIR_AUTO: + swingH = ELECTROLUX_HDIR_AUTO; + break; + case HDIR_SWING: + swingH = ELECTROLUX_HDIR_SWING; + break; + } + + + + + if (temperatureCmd > 15 && temperatureCmd < 31) + { + temperature = temperatureCmd - 16; + } + + sendElectrolux(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH); +} + +// Send the Electrolux code +void ElectroluxHeatpumpIR::sendElectrolux(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH) +{ + + uint8_t ElectroluxTemplate[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00 }; + // 0 1 2 3 4 5 6 7 + uint8_t i; + + // Set the Fan speed, operating mode and power state + ElectroluxTemplate[0] = fanSpeed | operatingMode | powerMode; + // Set the temperature + ElectroluxTemplate[1] = temperature; + + ElectroluxTemplate[2] = 0x20; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN + ElectroluxTemplate[3] = 0x50; // bits 4..7 always 0101 + ElectroluxTemplate[6] = 0x00; + + if (swingV == ELECTROLUX_VDIR_SWING) + { + ElectroluxTemplate[0] |= (1 << 6); // Enable swing by setting bit 6 + ElectroluxTemplate[4] |= 0x01; // Set vertical swing direction + } + if(swingH == ELECTROLUX_HDIR_SWING) + { + ElectroluxTemplate[0] |= (1 << 6); // Enable swing by setting bit 6 + ElectroluxTemplate[4] |= 0x10; // Set horizontal swing direction + } + + ElectroluxTemplate[7] = ((( + (ElectroluxTemplate[0] & 0x0F) + + (ElectroluxTemplate[1] & 0x0F) + + (ElectroluxTemplate[2] & 0x0F) + + (ElectroluxTemplate[3] & 0x0F) + + ((ElectroluxTemplate[4] & 0xF0) >> 4) + + ((ElectroluxTemplate[5] & 0xF0) >> 4) + + ((ElectroluxTemplate[6] & 0xF0) >> 4) + + 0x0A) & 0x0F) << 4) | (ElectroluxTemplate[7] & 0x0F); + + // 38 kHz PWM frequency + IR.setFrequency(38); + + // Send Header mark + IR.mark(ELECTROLUX_AIRCON1_HDR_MARK); + IR.space(ELECTROLUX_AIRCON1_HDR_SPACE); + + // Payload part #1 + for (i=0; i<4; i++) { + IR.sendIRbyte(ElectroluxTemplate[i], ELECTROLUX_AIRCON1_BIT_MARK, ELECTROLUX_AIRCON1_ZERO_SPACE, ELECTROLUX_AIRCON1_ONE_SPACE); + } + // Only three first bits of byte 4 are sent, this is always '010' + IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); + IR.space(ELECTROLUX_AIRCON1_ZERO_SPACE); + IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); + IR.space(ELECTROLUX_AIRCON1_ONE_SPACE); + IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); + IR.space(ELECTROLUX_AIRCON1_ZERO_SPACE); + + // Message space + IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); + IR.space(ELECTROLUX_AIRCON1_MSG_SPACE); + + // Payload part #2 + for (i=4; i<8; i++) { + IR.sendIRbyte(ElectroluxTemplate[i], ELECTROLUX_AIRCON1_BIT_MARK, ELECTROLUX_AIRCON1_ZERO_SPACE, ELECTROLUX_AIRCON1_ONE_SPACE); + } + + // End mark + IR.mark(ELECTROLUX_AIRCON1_BIT_MARK); + IR.space(0); +} diff --git a/lib/HeatpumpIR/ElectroluxHeatpumpIR.h b/lib/HeatpumpIR/ElectroluxHeatpumpIR.h index 82c24109f4..df77eef279 100644 --- a/lib/HeatpumpIR/ElectroluxHeatpumpIR.h +++ b/lib/HeatpumpIR/ElectroluxHeatpumpIR.h @@ -1,73 +1,73 @@ -/* - Electrolux heatpump control (remote control YAL1F) Based on Gree YAC -*/ -#ifndef ElectroluxHeatpumpIR_h -#define ElectroluxHeatpumpIR_h - -#include - -// Electrolux timing constants -#define ELECTROLUX_AIRCON1_HDR_MARK 9000 -#define ELECTROLUX_AIRCON1_HDR_SPACE 4000 -#define ELECTROLUX_AIRCON1_BIT_MARK 620 -#define ELECTROLUX_AIRCON1_ONE_SPACE 1600 -#define ELECTROLUX_AIRCON1_ZERO_SPACE 540 -#define ELECTROLUX_AIRCON1_MSG_SPACE 19000 - - -// Power state -#define ELECTROLUX_AIRCON1_POWER_OFF 0x00 -#define ELECTROLUX_AIRCON1_POWER_ON 0x08 - -// Operating modes -// Electrolux codes -#define ELECTROLUX_AIRCON1_MODE_AUTO 0x00 -#define ELECTROLUX_AIRCON1_MODE_COOL 0x01 -#define ELECTROLUX_AIRCON1_MODE_DRY 0x02 -#define ELECTROLUX_AIRCON1_MODE_FAN 0x03 -#define ELECTROLUX_AIRCON1_MODE_HEAT 0x04 - -// Fan speeds. Note that some heatpumps have less than 5 fan speeds -#define ELECTROLUX_AIRCON1_FAN_AUTO 0x00 // Fan speed -#define ELECTROLUX_AIRCON1_FAN1 0x10 // * 1 -#define ELECTROLUX_AIRCON1_FAN2 0x20 // * 2 -#define ELECTROLUX_AIRCON1_FAN3 0x30 // * 3 - -// Vertical air directions. Note that these cannot be set on all heat pumps -#define ELECTROLUX_VDIR_AUTO 0x00 -#define ELECTROLUX_VDIR_SWING 0x01 - -// Horizontal air directions. Note that these cannot be set on all heat pumps -#define ELECTROLUX_HDIR_AUTO 0x00 -#define ELECTROLUX_HDIR_SWING 0x01 - -#define ELECTROLUX_YAL 0 - - -class ElectroluxHeatpumpIR : public HeatpumpIR -{ - protected: - ElectroluxHeatpumpIR(); - uint8_t electroluxModel; - - public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); - - private: - void sendElectrolux(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH); -}; - - -class ElectroluxYALHeatpumpIR : public ElectroluxHeatpumpIR -{ - public: - ElectroluxYALHeatpumpIR(); - - public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) - { - ElectroluxHeatpumpIR::send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd); - } -}; - -#endif +/* + Electrolux heatpump control (remote control YAL1F) Based on Gree YAC +*/ +#ifndef ElectroluxHeatpumpIR_h +#define ElectroluxHeatpumpIR_h + +#include + +// Electrolux timing constants +#define ELECTROLUX_AIRCON1_HDR_MARK 9000 +#define ELECTROLUX_AIRCON1_HDR_SPACE 4000 +#define ELECTROLUX_AIRCON1_BIT_MARK 620 +#define ELECTROLUX_AIRCON1_ONE_SPACE 1600 +#define ELECTROLUX_AIRCON1_ZERO_SPACE 540 +#define ELECTROLUX_AIRCON1_MSG_SPACE 19000 + + +// Power state +#define ELECTROLUX_AIRCON1_POWER_OFF 0x00 +#define ELECTROLUX_AIRCON1_POWER_ON 0x08 + +// Operating modes +// Electrolux codes +#define ELECTROLUX_AIRCON1_MODE_AUTO 0x00 +#define ELECTROLUX_AIRCON1_MODE_COOL 0x01 +#define ELECTROLUX_AIRCON1_MODE_DRY 0x02 +#define ELECTROLUX_AIRCON1_MODE_FAN 0x03 +#define ELECTROLUX_AIRCON1_MODE_HEAT 0x04 + +// Fan speeds. Note that some heatpumps have less than 5 fan speeds +#define ELECTROLUX_AIRCON1_FAN_AUTO 0x00 // Fan speed +#define ELECTROLUX_AIRCON1_FAN1 0x10 // * 1 +#define ELECTROLUX_AIRCON1_FAN2 0x20 // * 2 +#define ELECTROLUX_AIRCON1_FAN3 0x30 // * 3 + +// Vertical air directions. Note that these cannot be set on all heat pumps +#define ELECTROLUX_VDIR_AUTO 0x00 +#define ELECTROLUX_VDIR_SWING 0x01 + +// Horizontal air directions. Note that these cannot be set on all heat pumps +#define ELECTROLUX_HDIR_AUTO 0x00 +#define ELECTROLUX_HDIR_SWING 0x01 + +#define ELECTROLUX_YAL 0 + + +class ElectroluxHeatpumpIR : public HeatpumpIR +{ + protected: + ElectroluxHeatpumpIR(); + uint8_t electroluxModel; + + public: + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + + private: + void sendElectrolux(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH); +}; + + +class ElectroluxYALHeatpumpIR : public ElectroluxHeatpumpIR +{ + public: + ElectroluxYALHeatpumpIR(); + + public: + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) + { + ElectroluxHeatpumpIR::send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd); + } +}; + +#endif diff --git a/lib/HeatpumpIR/GreeHeatpumpIR.cpp b/lib/HeatpumpIR/GreeHeatpumpIR.cpp index 15628d269c..7323845631 100644 --- a/lib/HeatpumpIR/GreeHeatpumpIR.cpp +++ b/lib/HeatpumpIR/GreeHeatpumpIR.cpp @@ -1,5 +1,123 @@ #include "GreeHeatpumpIR.h" +namespace { + +void convert_params( + uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode, bool iFeelMode, + uint8_t & powerMode, uint8_t & operatingMode, uint8_t & fanSpeed, + uint8_t & temperature, uint8_t & swingV, uint8_t & swingH) +{ + // Sensible defaults for the heat pump mode + powerMode = GREE_AIRCON1_POWER_ON; + operatingMode = GREE_AIRCON1_MODE_HEAT; + fanSpeed = GREE_AIRCON1_FAN_AUTO; + temperature = 21; + swingV = GREE_VDIR_AUTO; + swingH = GREE_HDIR_AUTO; + + if (powerModeCmd == POWER_OFF) + { + powerMode = GREE_AIRCON1_POWER_OFF; + } + else + { + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = GREE_AIRCON1_MODE_AUTO; + temperatureCmd = 25; + break; + case MODE_HEAT: + operatingMode = GREE_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = GREE_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = GREE_AIRCON1_MODE_DRY; + fanSpeedCmd = FAN_1; + break; + case MODE_FAN: + operatingMode = GREE_AIRCON1_MODE_FAN; + break; + } + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = GREE_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = GREE_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = GREE_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = GREE_AIRCON1_FAN3; + break; + } + + switch (swingVCmd) + { + case VDIR_AUTO: + swingV = GREE_VDIR_AUTO; + break; + case VDIR_SWING: + swingV = GREE_VDIR_SWING; + break; + case VDIR_UP: + swingV = GREE_VDIR_UP; + break; + case VDIR_MUP: + swingV = GREE_VDIR_MUP; + break; + case VDIR_MIDDLE: + swingV = GREE_VDIR_MIDDLE; + break; + case VDIR_MDOWN: + swingV = GREE_VDIR_MDOWN; + break; + case VDIR_DOWN: + swingV = GREE_VDIR_DOWN; + break; + } + + switch (swingHCmd) + { + case HDIR_AUTO: + swingH = GREE_HDIR_AUTO; + break; + case HDIR_SWING: + swingH = GREE_HDIR_SWING; + break; + case HDIR_LEFT: + swingH = GREE_HDIR_LEFT; + break; + case HDIR_MLEFT: + swingH = GREE_HDIR_MLEFT; + break; + case HDIR_MIDDLE: + swingH = GREE_HDIR_MIDDLE; + break; + case HDIR_MRIGHT: + swingH = GREE_HDIR_MRIGHT; + break; + case HDIR_RIGHT: + swingH = GREE_HDIR_RIGHT; + break; + } + + if (temperatureCmd > 15 && temperatureCmd < 31) + { + temperature = temperatureCmd - 16; + } +} + +} + // This is a protected method, i.e. generic Gree instances cannot be created GreeHeatpumpIR::GreeHeatpumpIR() : HeatpumpIR() { @@ -12,7 +130,6 @@ GreeGenericHeatpumpIR::GreeGenericHeatpumpIR() : GreeHeatpumpIR() _model = model; _info = info; - greeModel = GREE_GENERIC; } GreeYANHeatpumpIR::GreeYANHeatpumpIR() : GreeHeatpumpIR() @@ -22,7 +139,6 @@ GreeYANHeatpumpIR::GreeYANHeatpumpIR() : GreeHeatpumpIR() _model = model; _info = info; - greeModel = GREE_YAN; } // Support for YAA1FB, FAA1FB1, YB1F2 remotes @@ -33,7 +149,6 @@ GreeYAAHeatpumpIR::GreeYAAHeatpumpIR() : GreeHeatpumpIR() _model = model; _info = info; - greeModel = GREE_YAA; } // Support for YAC1FBF remote @@ -44,7 +159,6 @@ GreeYACHeatpumpIR::GreeYACHeatpumpIR() : GreeiFeelHeatpumpIR() _model = model; _info = info; - greeModel = GREE_YAC; } // Support for YT1F remote @@ -55,291 +169,399 @@ GreeYTHeatpumpIR::GreeYTHeatpumpIR() : GreeiFeelHeatpumpIR() _model = model; _info = info; - greeModel = GREE_YT; } -void GreeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) +// Support for YAP1F remote +GreeYAPHeatpumpIR::GreeYAPHeatpumpIR() : GreeiFeelHeatpumpIR() { - send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, false); + static const char model[] PROGMEM = "greeyap"; + static const char info[] PROGMEM = "{\"mdl\":\"greeyap\",\"dn\":\"Gree YAP\",\"mT\":16,\"xT\":30,\"fs\":3}"; + + _model = model; + _info = info; } -void GreeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode) +const GreeHeatpumpIR::Timings & GreeHeatpumpIR::getTimings() const { + static Timings timings = { + 9000, + 4000, + 620, + 1600, + 540, + 19000, + 8200, + 3800, + 650, + }; + return timings; +}; + +void GreeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode, bool iFeelMode) { - send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, turboMode, false); + uint8_t powerMode, operatingMode, fanSpeed, temperature, swingV, swingH; + + convert_params( + powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, + swingVCmd, swingHCmd, turboMode, iFeelMode, + powerMode, operatingMode, fanSpeed, + temperature, swingV, swingH); + + sendGree(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH, turboMode, iFeelMode); } -void GreeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode, bool iFeelMode) +// Send the Gree code +void GreeHeatpumpIR::sendGree(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, bool turboMode, bool iFeelMode) { - (void)swingVCmd; - (void)swingHCmd; + uint8_t buffer[9]; - // Sensible defaults for the heat pump mode + generateCommand( + buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); - uint8_t powerMode = GREE_AIRCON1_POWER_ON; - uint8_t operatingMode = GREE_AIRCON1_MODE_HEAT; - uint8_t fanSpeed = GREE_AIRCON1_FAN_AUTO; - uint8_t temperature = 21; - uint8_t swingV = GREE_VDIR_AUTO; - uint8_t swingH = GREE_HDIR_AUTO; + calculateChecksum(buffer); + sendBuffer(IR, buffer); +} +void GreeHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { - if (powerModeCmd == POWER_OFF) - { - powerMode = GREE_AIRCON1_POWER_OFF; - } - else - { - switch (operatingModeCmd) - { - case MODE_AUTO: - operatingMode = GREE_AIRCON1_MODE_AUTO; - temperatureCmd = 25; - break; - case MODE_HEAT: - operatingMode = GREE_AIRCON1_MODE_HEAT; - break; - case MODE_COOL: - operatingMode = GREE_AIRCON1_MODE_COOL; - break; - case MODE_DRY: - operatingMode = GREE_AIRCON1_MODE_DRY; - fanSpeedCmd = FAN_1; - break; - case MODE_FAN: - operatingMode = GREE_AIRCON1_MODE_FAN; - break; - } - } + memset(buffer, 0, 8); - switch (fanSpeedCmd) - { - case FAN_AUTO: - fanSpeed = GREE_AIRCON1_FAN_AUTO; - break; - case FAN_1: - fanSpeed = GREE_AIRCON1_FAN1; - break; - case FAN_2: - fanSpeed = GREE_AIRCON1_FAN2; - break; - case FAN_3: - fanSpeed = GREE_AIRCON1_FAN3; - break; - } + // Set the Fan speed, operating mode and power state + buffer[0] = fanSpeed | operatingMode | powerMode; + + // Set the temperature + buffer[1] = temperature; +} +void GreeYANHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + GreeHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); + + buffer[2] = turboMode ? 0x70 : 0x60; + buffer[3] = 0x50; + if (swingV == GREE_VDIR_SWING) + swingV = GREE_VDIR_AUTO; + buffer[4] = swingV; + buffer[5] |= 0x20; +} - if (greeModel == GREE_YAN) +void GreeYAAHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + GreeHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); + + buffer[2] = GREE_LIGHT_BIT; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN + buffer[3] = 0x50; // bits 4..7 always 0101 + buffer[5] |= 0x20; + buffer[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 + + if (turboMode) { - switch (swingVCmd) - { - case VDIR_AUTO: - case VDIR_SWING: - swingV = GREE_VDIR_AUTO; - break; - case VDIR_UP: - swingV = GREE_VDIR_UP; - break; - case VDIR_MUP: - swingV = GREE_VDIR_MUP; - break; - case VDIR_MIDDLE: - swingV = GREE_VDIR_MIDDLE; - break; - case VDIR_MDOWN: - swingV = GREE_VDIR_MDOWN; - break; - case VDIR_DOWN: - swingV = GREE_VDIR_DOWN; - break; - } + buffer[2] |= GREE_TURBO_BIT; } - - if (greeModel == GREE_YAA || greeModel == GREE_YAC || greeModel == GREE_YT) + if (swingV == GREE_VDIR_SWING) { - switch (swingVCmd) - { - case VDIR_AUTO: - swingV = GREE_VDIR_AUTO; - break; - case VDIR_SWING: - swingV = GREE_VDIR_SWING; - break; - case VDIR_UP: - swingV = GREE_VDIR_UP; - break; - case VDIR_MUP: - swingV = GREE_VDIR_MUP; - break; - case VDIR_MIDDLE: - swingV = GREE_VDIR_MIDDLE; - break; - case VDIR_MDOWN: - swingV = GREE_VDIR_MDOWN; - break; - case VDIR_DOWN: - swingV = GREE_VDIR_DOWN; - break; - } - - if (greeModel == GREE_YAC) - { - switch (swingHCmd) - { - case HDIR_AUTO: - case HDIR_SWING: - swingH = GREE_HDIR_SWING; - break; - case HDIR_LEFT: - swingH = GREE_HDIR_LEFT; - break; - case HDIR_MLEFT: - swingH = GREE_HDIR_MLEFT; - break; - case HDIR_MIDDLE: - swingH = GREE_HDIR_MIDDLE; - break; - case HDIR_MRIGHT: - swingH = GREE_HDIR_MRIGHT; - break; - case HDIR_RIGHT: - swingH = GREE_HDIR_RIGHT; - break; - } - } + buffer[0] |= GREE_VSWING; // Enable swing by setting bit 6 } - - - if (temperatureCmd > 15 && temperatureCmd < 31) + else if (swingV != GREE_VDIR_AUTO) { - temperature = temperatureCmd - 16; + buffer[5] = swingV; } +} - sendGree(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH, turboMode, iFeelMode); +void GreeiFeelHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + GreeHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); + + if (iFeelMode) { + buffer[5] |= GREE_IFEEL_BIT; + } } -// Send the Gree code -void GreeHeatpumpIR::sendGree(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, bool turboMode, bool iFeelMode) -{ - (void)swingH; +void GreeYACHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + GreeiFeelHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); - uint8_t GreeTemplate[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - // 0 1 2 3 4 5 6 7 + if (swingH == GREE_HDIR_AUTO) + swingH = GREE_HDIR_SWING; - uint8_t i; + buffer[4] |= (swingH << 4); // GREE_YT will ignore packets where this is set - if (greeModel != GREE_YT) { - GreeTemplate[5] = 0x20; - } + buffer[2] = GREE_LIGHT_BIT; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN + buffer[3] = 0x50; // bits 4..7 always 0101 + buffer[5] |= 0x20; + buffer[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 - // Set the Fan speed, operating mode and power state - GreeTemplate[0] = fanSpeed | operatingMode | powerMode; - // Set the temperature - GreeTemplate[1] = temperature; - - // Gree YAN-specific - if (greeModel == GREE_YAN) + if (turboMode) { - GreeTemplate[2] = turboMode ? 0x70 : 0x60; - GreeTemplate[3] = 0x50; - GreeTemplate[4] = swingV; + buffer[2] |= GREE_TURBO_BIT; } - if (greeModel == GREE_YAC) + if (swingV == GREE_VDIR_SWING) { - GreeTemplate[4] |= (swingH << 4); // GREE_YT will ignore packets where this is set + buffer[0] |= GREE_VSWING; // Enable swing by setting bit 6 } - if (greeModel == GREE_YAC || greeModel == GREE_YT) + else if (swingV != GREE_VDIR_AUTO) { - if (iFeelMode) - { - GreeTemplate[5] |= GREE_IFEEL_BIT; - } + buffer[5] = swingV; } - if (greeModel == GREE_YT) { - GreeTemplate[2] = GREE_LIGHT_BIT | GREE_HEALTH_BIT; // HEALTH is always on for GREE_YT - GreeTemplate[3] = 0x50; // bits 4..7 always 0101 +} - if (turboMode) - { - GreeTemplate[2] |= GREE_TURBO_BIT; - } - if (swingV == GREE_VDIR_SWING) - { - GreeTemplate[0] |= GREE_VSWING; // Enable swing by setting bit 6 - GreeTemplate[4] = swingV; - } +void GreeYTHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + GreeiFeelHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); + + buffer[2] = GREE_LIGHT_BIT | GREE_HEALTH_BIT; // HEALTH is always on for GREE_YT + buffer[3] = 0x50; // bits 4..7 always 0101 + + if (turboMode) + { + buffer[2] |= GREE_TURBO_BIT; } - if (greeModel == GREE_YAA || greeModel == GREE_YAC) + if (swingV == GREE_VDIR_SWING) { - GreeTemplate[2] = GREE_LIGHT_BIT; // bits 0..3 always 0000, bits 4..7 TURBO,LIGHT,HEALTH,X-FAN - GreeTemplate[3] = 0x50; // bits 4..7 always 0101 - GreeTemplate[6] = 0x20; // YAA1FB, FAA1FB1, YB1F2 bits 4..7 always 0010 + buffer[0] |= GREE_VSWING; // Enable swing by setting bit 6 + buffer[4] = swingV; + } +} - if (turboMode) - { - GreeTemplate[2] |= GREE_TURBO_BIT; +void GreeYAPHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode, + true); +} + +void GreeYAPHeatpumpIR::generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode, + bool light, bool xfan, + bool health, bool valve, + bool sthtMode, bool enableWiFi) { + + GreeiFeelHeatpumpIR::generateCommand(buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode); + + buffer[2] = + (turboMode ? (1 << 4) : 0) | + (light ? (1 << 5) : 0) | + (health ? (1 << 6) : 0) | + (xfan ? (1 << 7) : 0); + + buffer[3] = 0x50 | (valve ? (1 << 0) : 0); // bits 4..7 always 0101 + + buffer[4] = swingV | (swingH << 4); + + buffer[5] = 0x82 | + (iFeelMode ? (1 << 2) : 0) | // note that this is different than in the other devices + (enableWiFi ? (1 << 6) : 0); + + buffer[7] = (sthtMode ? (1 << 2) : 0); + + memset(buffer + 8, 0, 16); + memcpy(buffer + 8, buffer, 3); + + buffer[8 + 3] = 0x70 | + (valve ? (1 << 0) : 0); + + buffer[16 + 3] = 0xA0; + buffer[16 + 7] = 0xA0; +} + +void GreeHeatpumpIR::calculateChecksum(uint8_t * buffer) { + buffer[8] = ((( + (buffer[0] & 0x0F) + + (buffer[1] & 0x0F) + + (buffer[2] & 0x0F) + + (buffer[3] & 0x0F) + + ((buffer[5] & 0xF0) >> 4) + + ((buffer[6] & 0xF0) >> 4) + + ((buffer[7] & 0xF0) >> 4) + + 0x0A) & 0x0F) << 4) | (buffer[7] & 0x0F); +} + +void GreeYANHeatpumpIR::calculateChecksum(uint8_t * buffer) { + buffer[8] = ( + (buffer[0] << 4) + + (buffer[1] << 4) + + 0xC0); +} + +void GreeHeatpumpIR::sendBuffer(IRSender& IR, const uint8_t * buffer, size_t len) { + const auto & timings = getTimings(); + + // 38 kHz PWM frequency + IR.setFrequency(38); + + for (size_t pos = 0; pos < len; pos += 8) { + // All but the first group must be preceded by a space + if (pos) { + IR.mark(timings.bit_mark); + IR.space(timings.msg_space); } - if (swingV == GREE_VDIR_SWING) - { - GreeTemplate[0] |= GREE_VSWING; // Enable swing by setting bit 6 + + // Send Header mark + IR.mark(timings.hdr_mark); + IR.space(timings.hdr_space); + + // Payload part #1 + for (size_t i = 0; i < 4; i++) { + IR.sendIRbyte(buffer[pos + i], timings.bit_mark, timings.zero_space, timings.one_space); } - else if (swingV != GREE_VDIR_AUTO) - { - GreeTemplate[5] = swingV; + // Only three first bits of byte 4 are sent, this is always '010' + IR.mark(timings.bit_mark); + IR.space(timings.zero_space); + IR.mark(timings.bit_mark); + IR.space(timings.one_space); + IR.mark(timings.bit_mark); + IR.space(timings.zero_space); + + // Message space + IR.mark(timings.bit_mark); + IR.space(timings.msg_space); + + // Payload part #2 + for (size_t i = 5; i < 9; i++) { + IR.sendIRbyte(buffer[pos + i], timings.bit_mark, timings.zero_space, timings.one_space); } - } - // Calculate the checksum - if (greeModel == GREE_YAN) - { - GreeTemplate[7] = ( - (GreeTemplate[0] << 4) + - (GreeTemplate[1] << 4) + - 0xC0); - } - else - { - GreeTemplate[7] = ((( - (GreeTemplate[0] & 0x0F) + - (GreeTemplate[1] & 0x0F) + - (GreeTemplate[2] & 0x0F) + - (GreeTemplate[3] & 0x0F) + - ((GreeTemplate[5] & 0xF0) >> 4) + - ((GreeTemplate[6] & 0xF0) >> 4) + - ((GreeTemplate[7] & 0xF0) >> 4) + - 0x0A) & 0x0F) << 4) | (GreeTemplate[7] & 0x0F); + // End mark + IR.mark(timings.bit_mark); + IR.space(0); } +} - // 38 kHz PWM frequency - IR.setFrequency(38); +const GreeHeatpumpIR::Timings & GreeYAPHeatpumpIR::getTimings() const { + static Timings timings = { + 9000, + 4500, + 650, + 1643, + 510, + 20000, + 6000, + 3000, + 650, + }; + return timings; +}; - // Send Header mark - IR.mark(GREE_AIRCON1_HDR_MARK); - IR.space(GREE_AIRCON1_HDR_SPACE); +// Send the Gree code +void GreeYAPHeatpumpIR::sendGree( + IRSender& IR, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) { + sendGree(IR, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode, + true); +} - // Payload part #1 - for (i=0; i<4; i++) { - IR.sendIRbyte(GreeTemplate[i], GREE_AIRCON1_BIT_MARK, GREE_AIRCON1_ZERO_SPACE, GREE_AIRCON1_ONE_SPACE); - } - // Only three first bits of byte 4 are sent, this is always '010' - IR.mark(GREE_AIRCON1_BIT_MARK); - IR.space(GREE_AIRCON1_ZERO_SPACE); - IR.mark(GREE_AIRCON1_BIT_MARK); - IR.space(GREE_AIRCON1_ONE_SPACE); - IR.mark(GREE_AIRCON1_BIT_MARK); - IR.space(GREE_AIRCON1_ZERO_SPACE); - - // Message space - IR.mark(GREE_AIRCON1_BIT_MARK); - IR.space(GREE_AIRCON1_MSG_SPACE); - - // Payload part #2 - for (i=4; i<8; i++) { - IR.sendIRbyte(GreeTemplate[i], GREE_AIRCON1_BIT_MARK, GREE_AIRCON1_ZERO_SPACE, GREE_AIRCON1_ONE_SPACE); - } +void GreeYAPHeatpumpIR::sendGree( + IRSender& IR, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode, + bool light, bool xfan, + bool health, bool valve, + bool sthtMode, bool enableWiFi) { + + uint8_t buffer[24]; + + generateCommand( + buffer, + powerMode, operatingMode, + fanSpeed, temperature, + swingV, swingH, + turboMode, iFeelMode, + light, xfan, + health, valve, + sthtMode, enableWiFi); + + calculateChecksum(buffer); + calculateChecksum(buffer + 8); + + sendBuffer(IR, buffer, 24); +} - // End mark - IR.mark(GREE_AIRCON1_BIT_MARK); - IR.space(0); +void GreeYAPHeatpumpIR::send( + IRSender& IR, + uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd, + bool turboMode, bool iFeelMode, + bool light, bool xfan, + bool health, bool valve, + bool sthtMode, bool enableWiFi) { + uint8_t powerMode, operatingMode, fanSpeed, temperature, swingV, swingH; + + convert_params( + powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, + swingVCmd, swingHCmd, turboMode, iFeelMode, + powerMode, operatingMode, fanSpeed, + temperature, swingV, swingH); + + sendGree(IR, + powerMode, operatingMode, fanSpeed, temperature, + swingV, swingH, turboMode, iFeelMode, + light, xfan, health, + valve, sthtMode, enableWiFi); } // Sends current sensed temperatures, YAC remotes/supporting units only @@ -350,18 +572,20 @@ void GreeiFeelHeatpumpIR::send(IRSender& IR, uint8_t currentTemperature) GreeTemplate[0] = currentTemperature; GreeTemplate[1] = 0xA5; + const auto & timings = getTimings(); + // 38 kHz PWM frequency IR.setFrequency(38); // Send Header mark - IR.mark(GREE_YAC_HDR_MARK); - IR.space(GREE_YAC_HDR_SPACE); + IR.mark(timings.ifeel_hdr_mark); + IR.space(timings.ifeel_hdr_space); // send payload - IR.sendIRbyte(GreeTemplate[0], GREE_YAC_BIT_MARK, GREE_AIRCON1_ZERO_SPACE, GREE_AIRCON1_ONE_SPACE); - IR.sendIRbyte(GreeTemplate[1], GREE_YAC_BIT_MARK, GREE_AIRCON1_ZERO_SPACE, GREE_AIRCON1_ONE_SPACE); + IR.sendIRbyte(GreeTemplate[0], timings.ifeel_bit_mark, timings.zero_space, timings.one_space); + IR.sendIRbyte(GreeTemplate[1], timings.ifeel_bit_mark, timings.zero_space, timings.one_space); // End mark - IR.mark(GREE_YAC_BIT_MARK); + IR.mark(timings.ifeel_bit_mark); IR.space(0); } diff --git a/lib/HeatpumpIR/GreeHeatpumpIR.h b/lib/HeatpumpIR/GreeHeatpumpIR.h index f50b8cd82c..03a63346e8 100644 --- a/lib/HeatpumpIR/GreeHeatpumpIR.h +++ b/lib/HeatpumpIR/GreeHeatpumpIR.h @@ -6,19 +6,6 @@ #include -// Gree timing constants -#define GREE_AIRCON1_HDR_MARK 9000 -#define GREE_AIRCON1_HDR_SPACE 4000 -#define GREE_AIRCON1_BIT_MARK 620 -#define GREE_AIRCON1_ONE_SPACE 1600 -#define GREE_AIRCON1_ZERO_SPACE 540 -#define GREE_AIRCON1_MSG_SPACE 19000 - -// Timing specific for YAC features (I-Feel mode) -#define GREE_YAC_HDR_MARK 6000 -#define GREE_YAC_HDR_SPACE 3000 -#define GREE_YAC_BIT_MARK 650 - // Power state #define GREE_AIRCON1_POWER_OFF 0x00 #define GREE_AIRCON1_POWER_ON 0x08 @@ -71,8 +58,6 @@ #define GREE_HEALTH_BIT (1 << 6) #define GREE_XFAN_BIT (1 << 7) // aka BLOW on some remotes - - // Gree model codes #define GREE_GENERIC 0 #define GREE_YAN 1 @@ -84,16 +69,61 @@ class GreeHeatpumpIR : public HeatpumpIR { protected: + struct Timings { + int hdr_mark; + int hdr_space; + int bit_mark; + int one_space; + int zero_space; + int msg_space; + int ifeel_hdr_mark; + int ifeel_hdr_space; + int ifeel_bit_mark; + }; + GreeHeatpumpIR(); - uint8_t greeModel; + + virtual const Timings & getTimings() const; + + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode); + + virtual void calculateChecksum(uint8_t * buffer); + + virtual void sendGree( + IRSender& IR, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode); + + virtual void sendBuffer( + IRSender& IR, + const uint8_t * buffer, + size_t len = 8); public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd , uint8_t fanSpeedCmd , uint8_t temperatureCmd , uint8_t swingVCmd , uint8_t swingHCmd, bool turboMode); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd , uint8_t fanSpeedCmd , uint8_t temperatureCmd , uint8_t swingVCmd , uint8_t swingHCmd, bool turboMode, bool iFeelMode); + void send( + IRSender& IR, + uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd) override { + send( + IR, + powerModeCmd, operatingModeCmd, + fanSpeedCmd, temperatureCmd, + swingVCmd, swingHCmd, false); + } - private: - void sendGree(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, bool turboMode, bool iFeelMode); + virtual void send( + IRSender& IR, + uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd, + bool turboMode, bool iFeelMode = false); }; class GreeGenericHeatpumpIR : public GreeHeatpumpIR @@ -107,11 +137,14 @@ class GreeYANHeatpumpIR : public GreeHeatpumpIR public: GreeYANHeatpumpIR(); - public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode) - { - GreeHeatpumpIR::send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, turboMode); - } + protected: + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; + + virtual void calculateChecksum(uint8_t * buffer) override; }; class GreeYAAHeatpumpIR : public GreeHeatpumpIR @@ -119,33 +152,123 @@ class GreeYAAHeatpumpIR : public GreeHeatpumpIR public: GreeYAAHeatpumpIR(); - public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode) - { - GreeHeatpumpIR::send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, turboMode); - } + protected: + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; }; class GreeiFeelHeatpumpIR : public GreeHeatpumpIR { public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboMode, bool iFeelMode) - { - GreeHeatpumpIR::send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, turboMode, iFeelMode); - } - void send(IRSender& IR, uint8_t currentTemperature); + using GreeHeatpumpIR::send; + void send(IRSender& IR, uint8_t currentTemperature) override; + + protected: + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; }; class GreeYACHeatpumpIR : public GreeiFeelHeatpumpIR { public: GreeYACHeatpumpIR(); + + protected: + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; }; class GreeYTHeatpumpIR : public GreeiFeelHeatpumpIR { public: GreeYTHeatpumpIR(); + + protected: + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; +}; + +class GreeYAPHeatpumpIR : public GreeiFeelHeatpumpIR +{ + public: + GreeYAPHeatpumpIR(); + + using GreeiFeelHeatpumpIR::send; + + virtual void send( + IRSender& IR, + uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd, + bool turboMode, bool iFeelMode = false) override { + send( + IR, + powerModeCmd, operatingModeCmd, + fanSpeedCmd, temperatureCmd, + swingVCmd, swingHCmd, + turboMode, iFeelMode, + true); + } + + virtual void send( + IRSender& IR, + uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, + uint8_t swingVCmd, uint8_t swingHCmd, + bool turboMode, bool iFeelMode, + bool light, bool xfan = false, + bool health = false, bool valve = false, + bool sthtMode = false, bool enableWiFi = true); + + protected: + virtual const Timings & getTimings() const override; + + virtual void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; + + void generateCommand(uint8_t * buffer, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode, + + bool light, bool xfan = false, + bool health = false, bool valve = false, + bool sthtMode = false, bool enableWiFi = true + ); + + virtual void sendGree( + IRSender& IR, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode) override; + + void sendGree( + IRSender& IR, + uint8_t powerMode, uint8_t operatingMode, + uint8_t fanSpeed, uint8_t temperature, + uint8_t swingV, uint8_t swingH, + bool turboMode, bool iFeelMode, + + bool light, bool xfan = false, + bool health = false, bool valve = false, + bool sthtMode = false, bool enableWiFi = true); }; #endif diff --git a/lib/HeatpumpIR/HeatpumpIRFactory.cpp b/lib/HeatpumpIR/HeatpumpIRFactory.cpp index eaed55e4d4..f469d65a0b 100644 --- a/lib/HeatpumpIR/HeatpumpIRFactory.cpp +++ b/lib/HeatpumpIR/HeatpumpIRFactory.cpp @@ -29,6 +29,10 @@ HeatpumpIR* HeatpumpIRFactory::create(const char *modelName) { return new GreeYAAHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("greeyan")) == 0) { return new GreeYANHeatpumpIR(); + } else if (strcmp_P(modelName, PSTR("greeyac")) == 0) { + return new GreeYACHeatpumpIR(); + } else if (strcmp_P(modelName, PSTR("greeyt")) == 0) { + return new GreeYTHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("hisense_aud")) == 0) { return new HisenseHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("hitachi")) == 0) { @@ -37,6 +41,8 @@ HeatpumpIR* HeatpumpIRFactory::create(const char *modelName) { return new HyundaiHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("ivt")) == 0) { return new IVTHeatpumpIR(); + } else if (strcmp_P(modelName, PSTR("nibe")) == 0) { + return new NibeHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("midea")) == 0) { return new MideaHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("mitsubishi_fa")) == 0) { @@ -69,6 +75,8 @@ HeatpumpIR* HeatpumpIRFactory::create(const char *modelName) { return new PanasonicLKEHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("panasonic_nke")) == 0) { return new PanasonicNKEHeatpumpIR(); + } else if (strcmp_P(modelName, PSTR("philco_phs32")) == 0) { + return new PhilcoPHS32HeatpumpIR(); } else if (strcmp_P(modelName, PSTR("samsung_aqv")) == 0) { return new SamsungAQVHeatpumpIR(); } else if (strcmp_P(modelName, PSTR("samsung_fjm")) == 0) { @@ -83,6 +91,8 @@ HeatpumpIR* HeatpumpIRFactory::create(const char *modelName) { return new ZHJG01HeatpumpIR(); } else if (strcmp_P(modelName, PSTR("ZHLT01")) == 0) { return new ZHLT01HeatpumpIR(); + } else if (strcmp_P(modelName, PSTR("KY-26")) == 0) { + return new KY26HeatpumpIR(); } return NULL; diff --git a/lib/HeatpumpIR/HeatpumpIRFactory.h b/lib/HeatpumpIR/HeatpumpIRFactory.h index b96e1872e1..9f1d322747 100644 --- a/lib/HeatpumpIR/HeatpumpIRFactory.h +++ b/lib/HeatpumpIR/HeatpumpIRFactory.h @@ -2,7 +2,9 @@ #define HeatpumpIRFactory_h #include +#include #include +#include #include #include #include @@ -15,20 +17,26 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include +#include #include #include #include #include +#include #include #include +#include class HeatpumpIRFactory { diff --git a/lib/HeatpumpIR/HitachiHeatpumpIR.cpp b/lib/HeatpumpIR/HitachiHeatpumpIR.cpp index 33b59838aa..f356d3878d 100644 --- a/lib/HeatpumpIR/HitachiHeatpumpIR.cpp +++ b/lib/HeatpumpIR/HitachiHeatpumpIR.cpp @@ -80,17 +80,17 @@ void HitachiHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operati temperature = temperatureCmd; } - switch (swingV) + switch (swingVcmd) { - case HDIR_AUTO: + case VDIR_AUTO: swingV = HITACHI_AIRCON1_VDIR_AUTO; break; - case HDIR_SWING: + case VDIR_SWING: swingV = HITACHI_AIRCON1_VDIR_SWING; break; } - switch (swingH) + switch (swingHcmd) { case HDIR_AUTO: swingH = HITACHI_AIRCON1_HDIR_AUTO; @@ -99,7 +99,7 @@ void HitachiHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operati swingH = HITACHI_AIRCON1_HDIR_SWING; break; } - + sendHitachi(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH); } @@ -125,7 +125,7 @@ void HitachiHeatpumpIR::sendHitachi(IRSender& IR, uint8_t powerMode, uint8_t ope //Checksum calculation int checksum = 1086; - for (uint8_t i = 0; i < 27; i++) { + for (byte i = 0; i < 27; i++) { checksum -= hitachiTemplate[i]; } hitachiTemplate[27] = checksum; diff --git a/lib/HeatpumpIR/IRSender.cpp b/lib/HeatpumpIR/IRSender.cpp index 17495460e3..df44715380 100644 --- a/lib/HeatpumpIR/IRSender.cpp +++ b/lib/HeatpumpIR/IRSender.cpp @@ -10,9 +10,9 @@ IRSender::IRSender(uint8_t pin) // Send a uint8_t (8 bits) over IR -void IRSender::sendIRbyte(uint8_t sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength) +void IRSender::sendIRbyte(uint8_t sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength, uint8_t bitCount) { - for (int i=0; i<8 ; i++) + for (int i=0; i= 3 while((int)(micros() - beginning) < markLength); +#if ( defined(ESP_ARDUINO_VERSION_MAJOR) && (ESP_ARDUINO_VERSION_MAJOR >= 3) ) + ledcDetach(_pin); + pinMode(_pin, OUTPUT); +#else // ESP_ARDUINO_VERSION_MAJOR >= 3 gpio_reset_pin(static_cast(_pin)); - digitalWrite(_pin, LOW); +#endif // ESP_ARDUINO_VERSION_MAJOR >= 3 + if (_inverted) + digitalWrite(_pin, HIGH); + else + digitalWrite(_pin, LOW); } // Send an IR 'space' symbol, i.e. transmitter OFF void IRSenderESP32::space(int spaceLength) { - digitalWrite(_pin, LOW); + if (_inverted) + digitalWrite(_pin, HIGH); + else + digitalWrite(_pin, LOW); if (spaceLength < 16383) { delayMicroseconds(spaceLength); diff --git a/lib/HeatpumpIR/IRSenderPWM.cpp b/lib/HeatpumpIR/IRSenderPWM.cpp index d46cff69d5..16b85de549 100644 --- a/lib/HeatpumpIR/IRSenderPWM.cpp +++ b/lib/HeatpumpIR/IRSenderPWM.cpp @@ -3,7 +3,7 @@ // ESP8266 does not have the Arduino PWM control registers -#if not defined ESP8266 && not defined ESP32 +#if not defined ESP8266 && not defined ESP32 && not defined LIBRETINY // Heavily based on Ken Shirriff's IRRemote library: // https://github.com/shirriff/Arduino-IRremote @@ -14,7 +14,7 @@ #if defined(__SAM3X8E__) || defined(__SAM3X8H__) // Arduino Due uint32_t IR_USE_PWM_PINMASK; - uint8_t IR_USE_PWM_CH; + byte IR_USE_PWM_CH; #endif IRSenderPWM::IRSenderPWM(uint8_t pin) : IRSender(pin) @@ -24,7 +24,7 @@ IRSenderPWM::IRSenderPWM(uint8_t pin) : IRSender(pin) #if defined(__SAM3X8E__) || defined(__SAM3X8H__) // Arduino Due - pmc_set_writeprotect(false); + pmc_set_writeprotect(false); switch (_pin) { @@ -32,7 +32,7 @@ IRSenderPWM::IRSenderPWM(uint8_t pin) : IRSender(pin) IR_USE_PWM_PINMASK = PIO_PC24; IR_USE_PWM_CH = 7; break; - + case 7: IR_USE_PWM_PINMASK = PIO_PC23; IR_USE_PWM_CH = 6; diff --git a/lib/HeatpumpIR/KY26HeatpumpIR.cpp b/lib/HeatpumpIR/KY26HeatpumpIR.cpp new file mode 100644 index 0000000000..ad25f7fc1e --- /dev/null +++ b/lib/HeatpumpIR/KY26HeatpumpIR.cpp @@ -0,0 +1,102 @@ +#include "KY26HeatpumpIR.h" + +KY26HeatpumpIR::KY26HeatpumpIR() : HeatpumpIR() { + static const char model[] PROGMEM = "KY-26"; + static const char info[] PROGMEM = + "{\"mdl\":\"ky26\",\"dn\":\"KY-26\",\"mT\":15,\"xT\":31,\"fs\":3}"; + + _model = model; + _info = info; +} + +void KY26HeatpumpIR::send(IRSender &IR, uint8_t powerModeCmd, + uint8_t operatingModeCmd, uint8_t fanSpeedCmd, + uint8_t temperatureCmd, uint8_t swingVCmd, + uint8_t swingHCmd, uint8_t timerHourCmd, + bool timerHalfHourCmd) { + uint8_t powerMode = KY26_POWER_OFF; + uint8_t operatingMode = KY26_MODE_AUTO; + uint8_t fanSpeed = KY26_FAN1; + uint8_t temperature = 24; + uint8_t timer = KY26_TIMER_OFF; + + // Operating mode + switch (operatingModeCmd) { + case MODE_AUTO: + operatingMode = KY26_MODE_AUTO; + break; + case MODE_COOL: + operatingMode = KY26_MODE_COOL; + break; + case MODE_DRY: + operatingMode = KY26_MODE_DRY; + break; + case MODE_FAN: + operatingMode = KY26_MODE_FAN; + break; + } + + // Fan speed + switch (fanSpeedCmd) { + case FAN_1: + fanSpeed = KY26_FAN1; + break; + case FAN_2: + fanSpeed = KY26_FAN2; + break; + case FAN_3: + fanSpeed = KY26_FAN3; + break; + } + + // Temperature + if (temperatureCmd >= 15 && temperatureCmd <= 31) { + temperature = temperatureCmd == 15 ? 0 : temperatureCmd; + } + + // Timer + if (timerHourCmd > 0 && timerHourCmd <= 12) { + timer = timerHourCmd | KY26_TIMER_ON; + + if (timerHalfHourCmd) { + timer |= KY26_TIMER_HALF_HOUR; + } + } + + // Power mode + // This heatpump does not have a power on command, but we can simulate it by + // sending a power off command followed by a power toggle command. + if (powerModeCmd == POWER_ON) { + sendKY26(IR, powerMode, operatingMode, fanSpeed, temperature, timer); + powerMode = KY26_POWER_ONOFF; + } + + return sendKY26(IR, powerMode, operatingMode, fanSpeed, temperature, timer); +} + +void KY26HeatpumpIR::sendKY26(IRSender &IR, uint8_t powerModeCmd, + uint8_t operatingModeCmd, uint8_t fanSpeedCmd, + uint8_t temperatureCmd, uint8_t timerCmd) { + uint8_t KY26Template[] = {0x00, 0x00, 0x00, 0x00}; + + KY26Template[0] |= powerModeCmd | operatingModeCmd | fanSpeedCmd; + KY26Template[1] |= timerCmd; + KY26Template[2] |= temperatureCmd; + + KY26Template[3] = (KY26Template[0] + KY26Template[1] + KY26Template[2]) % 256; + + IR.setFrequency(38); + + // Header + IR.mark(KY26_HDR_MARK); + IR.space(KY26_HDR_SPACE); + + for (unsigned int i = 0; i < sizeof(KY26Template); i++) { + IR.sendIRbyte(KY26Template[i], KY26_BIT_MARK, KY26_ZERO_SPACE, + KY26_ONE_SPACE); + } + + // Footer + IR.mark(KY26_BIT_MARK); + IR.space(0); +} diff --git a/lib/HeatpumpIR/KY26HeatpumpIR.h b/lib/HeatpumpIR/KY26HeatpumpIR.h new file mode 100644 index 0000000000..906a139a04 --- /dev/null +++ b/lib/HeatpumpIR/KY26HeatpumpIR.h @@ -0,0 +1,77 @@ +// KY-26 Remote protocol +// +// The KY-26 remote control is used in locally branded air conditioners. +// I used a ZAICON air conditioner, which also seems to be rebranded as +// SACOM, SENCYS, and possibly others. +// +// The remote sends a 4-byte message which contains all possible settings every +// time. +// +// Byte 0 contains the a power signal, operating mode, and fan speed. +// Byte 1 contains the temperature setting. +// Byte 2 contains the timer setting. +// Byte 3 contains the checksum. +// +// MSB LSB +// ??FF PPOO ??TT TTTT ???t tttt CCCC CCCC +// 0000 0000 0000 0000 0000 0000 0000 0000 +// | | | || | | | +// | | | || | | +--- Checksum +// | | | || | | +// | | | || | +--- Temperature (15 = 0, 16 = 16, ..., 31 = 31) +// | | | || | +// | | | || +--- Timer hours (0 = off, 1 = 1 hour, ..., 12 = 12 hours) +// | | | |+----- Timer half hour +// | | | +------ Timer on +// | | | +// | | +--- Operating mode (0 auto, 1 cool, 2 dry, 3 fan) +// | +----- Power (2 Toggle, 3 Turn off) +// +-------- Fan speed (1 min, 2 mid, 3 max) +// + +#ifndef KY26HeatpumpIR_h +#define KY26HeatpumpIR_h + +#include + +// Timing constants +#define KY26_HDR_MARK 9600 +#define KY26_HDR_SPACE 6100 +#define KY26_BIT_MARK 500 +#define KY26_ONE_SPACE 1700 +#define KY26_ZERO_SPACE 500 + +// Power modes +#define KY26_POWER_ONOFF 0x08 +#define KY26_POWER_OFF 0x0C + +// Operating modes +#define KY26_MODE_AUTO 0x00 +#define KY26_MODE_COOL 0x01 +#define KY26_MODE_DRY 0x02 +#define KY26_MODE_FAN 0x03 + +// Fan speeds +#define KY26_FAN1 0x10 // * low +#define KY26_FAN2 0x20 // * med +#define KY26_FAN3 0x30 // * high + +// Timer +#define KY26_TIMER_ON 0x20 +#define KY26_TIMER_OFF 0x00 +#define KY26_TIMER_HALF_HOUR 0x10 + +class KY26HeatpumpIR : public HeatpumpIR { +public: + KY26HeatpumpIR(); + void send(IRSender &IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, + uint8_t swingHCmd, uint8_t timerHourCmd = 0x00, + bool timerHalfHourCmd = false); + +protected: + void sendKY26(IRSender &IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, + uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t timerCmd); +}; + +#endif // KY26HeatpumpIR_h \ No newline at end of file diff --git a/lib/HeatpumpIR/NibeHeatpumpIR.cpp b/lib/HeatpumpIR/NibeHeatpumpIR.cpp new file mode 100644 index 0000000000..0bd93c542f --- /dev/null +++ b/lib/HeatpumpIR/NibeHeatpumpIR.cpp @@ -0,0 +1,332 @@ +/* +Info about NIBE IR remote code: + +Control Message: +Consists of 90 Bits +Message is split into +| 10x8 Data Bits | 2 Static Bit (01) | CRC 8 bit | + +Data Bits are split up the following way: +| Never Change | Never Change | Op Mode and Temp | Temp and Fan Mode | Vertical Direction and Timer | Timer | Timer | Timer and Time | Time and Special Functions | Special Functions | + +iFeel Message: +Consists of 32 Bits +| 3x8 Data Bits | CRC 8 bit| + +Last 5 Bits of the Data Bits contain the sensed temperature +from 4-35 deg C. Temperature is transmitted with an offset of 4 deg. +Data are being send every 5 min or when the sensed temperature value changes. +*/ + +#include +#include "NibeHeatpumpIR.h" + +#ifdef NIBE_USE_TIME_H +#include +#endif + +#ifdef NIBE_IR_SEND_TIME +int nibeSendHour = 12; +int nibeSendMinute = 42; +#endif + +// Reverses bit order for a uint8_t. Can modify the bitlength that needs to be reversed (needed 8, 5 bit or 2 bit reverse) +static uint8_t reverseBits8(uint8_t value, int bitLength) { + uint8_t reversedValue = 0; + + for (int i = 0; i < bitLength; i++) { + // Extract the i-th bit from the original value + uint8_t bit = (value >> i) & 1; + + // Set the (bitLength - 1 - i)-th bit in the reversed value + reversedValue |= bit << (bitLength - 1 - i); + } + + return reversedValue; +} + +// Reverses bit order for a uint16_t. Can modify the bitlength that needs to be reversed (needed 11 bit reverse) +static uint16_t reverseBits16(uint16_t value, int bitLength) { + uint16_t reversedValue = 0; + + for (int i = 0; i < bitLength; i++) { + // Extract the i-th bit from the original value + uint16_t bit = (value >> i) & 1; + + // Set the (bitLength - 1 - i)-th bit in the reversed value + reversedValue |= bit << (bitLength - 1 - i); + } + + return reversedValue; +} + +NibeHeatpumpIR::NibeHeatpumpIR() : HeatpumpIR() +{ + static const char model[] PROGMEM = "nibe"; + static const char info[] PROGMEM = "{\"mdl\":\"nibe\",\"dn\":\"Nibe\",\"mT\":10,\"xT\":32,\"fs\":5}"; + + _model = model; + _info = info; +} + +void NibeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) +{ + send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, false, false); +} + +void NibeHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboModeCmd, bool iFeelModeCmd) +{ + (void)swingHCmd; + + // Sensible defaults for the heat pump mode + uint8_t powerMode = NIBE_POWER_ON; + uint8_t operatingMode = NIBE_MODE_HEAT_ONDEMAND; // Default run unit until temp is reached, fan stops + uint8_t fanSpeed = NIBE_MODE_FAN_AUTO; + uint8_t temperature = 21; + uint8_t swingV = NIBE_VDIR_AUTO; // Set auto mode since that one is allowed for all modes + uint8_t iFeelMode = 0x00; + uint8_t filter = 0x00; + uint8_t nightMode = 0x00; + uint8_t turboMode = 0x00; + bool filterCmd = true; // Default enable air filter + + if (powerModeCmd == POWER_OFF) + { + powerMode = NIBE_POWER_OFF; + } + else + { + powerMode = NIBE_POWER_ON; + + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = NIBE_MODE_AUTO_HEAT; + //operatingMode = NIBE_MODE_AUTO_COOL; // This heatpump can be set from heating or cooling into auto mode. Effect is the same. + break; + case MODE_HEAT: + operatingMode = NIBE_MODE_HEAT_ONDEMAND; // Heats until temp is reached, turns off fan when target is reached + //operatingMode = NIBE_MODE_HEAT_CONTINOUS; // Heats until temp is reached, but keeps the fan running + break; + case MODE_COOL: + operatingMode = NIBE_MODE_COOL; + break; + case MODE_DRY: + operatingMode = NIBE_MODE_DRY; + break; + case MODE_FAN: + operatingMode = NIBE_MODE_FAN; + break; + } + } + + // NOTE Fan speed Auto can not be used in MODE_FAN + // TODO not sure this fan mapping is correct? FAN_1 = low speed and FAN_3 = high speed? + switch (fanSpeedCmd) + { + case FAN_AUTO: + if (operatingMode == NIBE_MODE_FAN) + fanSpeed = NIBE_MODE_FAN1; + else + fanSpeed = NIBE_MODE_FAN_AUTO; + break; + case FAN_1: + fanSpeed = NIBE_MODE_FAN3; + break; + case FAN_2: + fanSpeed = NIBE_MODE_FAN2; + break; + case FAN_3: + fanSpeed = NIBE_MODE_FAN1; + break; + case FAN_SILENT: + nightMode = 0x01; + break; + } + +// NOTE Operating Mode Auto allows all positions +// NOTE Operating Mode Cooling and Dry allows only pos 1-4 +// NOTE Operating Mode Heating allows only pos 3-6 +// NOTE Position 6 is not implemented! + switch (swingVCmd) + { + case VDIR_AUTO: + swingV = NIBE_VDIR_AUTO; + break; + case VDIR_SWING: + swingV = NIBE_VDIR_ALL; + break; + case VDIR_UP: + if ((operatingMode == NIBE_MODE_HEAT_CONTINOUS) || (operatingMode == NIBE_MODE_HEAT_ONDEMAND)) + swingV = NIBE_VDIR_POS3; + else + swingV = NIBE_VDIR_POS1; + break; + case VDIR_MUP: + if ((operatingMode == NIBE_MODE_HEAT_CONTINOUS) || (operatingMode == NIBE_MODE_HEAT_ONDEMAND)) + swingV = NIBE_VDIR_POS3; + else + swingV = NIBE_VDIR_POS2; + break; + case VDIR_MIDDLE: + swingV = NIBE_VDIR_POS3; + break; + case VDIR_MDOWN: + swingV = NIBE_VDIR_POS4; + break; + case VDIR_DOWN: + if ((operatingMode == NIBE_MODE_COOL) || (operatingMode == NIBE_MODE_DRY)) + swingV = NIBE_VDIR_POS4; + else + swingV = NIBE_VDIR_POS5; + break; + } + + if (iFeelModeCmd) + { + iFeelMode = 0x01; + } + + if (filterCmd) + { + filter = 0x01; + } + + if (turboModeCmd) + { + turboMode = 0x01; + } + + // Allowed temp range 10-32 deg + if ((temperatureCmd > 9) && (temperatureCmd < 33)) + { + temperature = temperatureCmd - 4; // Temp is reported as: Actual Temp - 4 + } + + sendNibe(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, iFeelMode, filter, turboMode, nightMode); +} + +// Send the Gree code +void NibeHeatpumpIR::sendNibe(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t iFeelMode, uint8_t filter, uint8_t turboMode, uint8_t nightMode) +{ + // Setting some default values that never change! + uint8_t NibeTemplate[] = { 0x35, 0xAF, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; + // 0 1 2 3 4 5 6 7 8 9 10 11 + +#ifdef NIBE_USE_TIME_H + time_t now; + struct tm * timeinfo; +#endif + uint16_t currentTime = 0; // If no USE_TIME_H or IR_SEND_TIME defined, send always time 00:00 + + // Convert Temperature + uint8_t temp_swap = reverseBits8(temperature, 5); + + // Byte 2 contains Operation Mode and part of temperature + NibeTemplate[2] |= operatingMode << 2; + NibeTemplate[2] |= (temp_swap >> 3); + + // Byte 3 contains part of temperature and fan mode + NibeTemplate[3] |= (temp_swap & 0x07) << 5; + NibeTemplate[3] |= fanSpeed << 3; + + // NOTE Part of Byte 3 and 4 (total of 5 bits) contains some data that are not decoded. They seem to change randomly. + // Same commands have been send with the remote control, while part of these 5 bits are changing. + // Observed Codes: + // 00001, 10001, 01001, 00101, 10101, 01101 + // We always use 00001 in the hope that it doesnt affect the usage of the heatpump! Maybe someone will figure it out when to change these! + // During testing no issues have been observed! + + // Byte 4 - vertical air flow direction + NibeTemplate[4] |= swingV << 3; + + // NOTE Timer functionality is not implemented here, when disabled timer fields always have the same value + // Part of Byte 4 and Byte 5 is for start timer function -> needs to be set to 00000000111 + NibeTemplate[5] |= 0x7; + // Byte 6 and part of Byte 7 is for stop timer function -> needs to be set to 00000000111 + NibeTemplate[7] |= (0x7 << 5); + // Part of Byte 7 and part of Byte 8 is for actual time (needed for timer and vacation function) +#ifdef NIBE_USE_TIME_H + time(&now); + timeinfo = localtime(&now); + currentTime = (uint16_t)(timeinfo->tm_hour * 60 + timeinfo->tm_min); +#endif +#ifdef NIBE_IR_SEND_TIME + currentTime = (uint16_t)(nibeSendHour * 60 + nibeSendMinute); +#endif + currentTime = reverseBits16(currentTime, 11); + NibeTemplate[7] |= ((uint8_t) ((currentTime & 0x7C0) >> 6)); + NibeTemplate[8] |= ((uint8_t) (currentTime & 0x3F)) << 2; + + // Part of Byte 8 and part of Byte 9 is for special functions + NibeTemplate[9] |= iFeelMode; + NibeTemplate[9] |= (powerMode << 2); + NibeTemplate[9] |= (filter << 3); + NibeTemplate[9] |= (turboMode << 4); + NibeTemplate[9] |= (nightMode << 5); + + // Calculate CRC + uint8_t checksum = 0; + for (int i = 0; i < 11; i++) + { + if (i == 10) + checksum += reverseBits8(NibeTemplate[i], 2); // Byte 10 only has 2 bits + else + checksum += reverseBits8(NibeTemplate[i], 8); + } + NibeTemplate[11] = reverseBits8(checksum, 8); + + // 38 kHz PWM frequency + IR.setFrequency(38); + + // Send Header mark + IR.mark(NIBE_HDR_MARK); + IR.space(NIBE_HDR_SPACE); + + for (int i=0; i<12; i++) + { + if (i == 10) + IR.sendIRbyte(reverseBits8(NibeTemplate[i],2), NIBE_BIT_MARK, NIBE_ZERO_SPACE, NIBE_ONE_SPACE, 2); // Byte 10 only has 2 bits + else + IR.sendIRbyte(reverseBits8(NibeTemplate[i],8), NIBE_BIT_MARK, NIBE_ZERO_SPACE, NIBE_ONE_SPACE); + } + + // End mark + IR.mark(NIBE_BIT_MARK); + IR.space(NIBE_MSG_SPACE); +} + +//Should send current sensed temperatures every 5 min or on temperature change +void NibeHeatpumpIR::send(IRSender& IR, uint8_t currentTemperature) +{ + // Example: 0011 0101 1010 1111 0100 0101 1100 0010 + uint8_t NibeTemplate[] = { 0x35, 0xAF, 0x40, 0x00 }; + + uint8_t temp_swap = reverseBits8(currentTemperature - 4, 5); + + NibeTemplate[2] |= temp_swap; + + uint8_t checksum = 0; + for (int i = 0; i < 3; i++) + { + checksum += reverseBits8(NibeTemplate[i], 8); + } + + NibeTemplate[3] = reverseBits8(checksum, 8); + + // 38 kHz PWM frequency + IR.setFrequency(38); + + // Send Header mark + IR.mark(NIBE_HDR_MARK); + IR.space(NIBE_HDR_SPACE); + + for (int i=0; i<4; i++) + { + IR.sendIRbyte(reverseBits8(NibeTemplate[i], 8), NIBE_BIT_MARK, NIBE_ZERO_SPACE, NIBE_ONE_SPACE); + } + + // End mark + IR.mark(NIBE_BIT_MARK); + IR.space(NIBE_MSG_SPACE); +} diff --git a/lib/HeatpumpIR/NibeHeatpumpIR.h b/lib/HeatpumpIR/NibeHeatpumpIR.h new file mode 100644 index 0000000000..3581d0283d --- /dev/null +++ b/lib/HeatpumpIR/NibeHeatpumpIR.h @@ -0,0 +1,84 @@ +/* + Nibe heatpump control (Tested for Nibe Model AG-WL10-4) +*/ +#ifndef NibeHeatpumpIR_h +#define NibeHeatpumpIR_h + +#include + +#define NIBE_USE_TIME_H +//#define NIBE_IR_SEND_TIME + +#define NIBE_HDR_MARK 6382 +#define NIBE_HDR_SPACE 3144 +#define NIBE_BIT_MARK 412 +#define NIBE_ONE_SPACE 2102 +#define NIBE_ZERO_SPACE 823 +#define NIBE_MSG_SPACE 0 + +// Power state (1 bit) +#define NIBE_POWER_OFF 0x00 +#define NIBE_POWER_ON 0x01 + +// Operating modes +// Nibe codes (combination of 3 bits) +#define NIBE_MODE_COOL 0x00 +#define NIBE_MODE_HEAT_ONDEMAND 0x01 +#define NIBE_MODE_HEAT_CONTINOUS 0x03 +#define NIBE_MODE_DRY 0x04 +#define NIBE_MODE_FAN 0x06 +// Auto mode can be achieved via cool or heating button. They create different codes, but should do same function +#define NIBE_MODE_AUTO_HEAT 0x05 +#define NIBE_MODE_AUTO_COOL 0x02 + +// Fan speeds (2 bits) +//NOTE Fan speed Auto can not be used in MODE_FAN +#define NIBE_MODE_FAN_AUTO 0x00 // Fan speed +#define NIBE_MODE_FAN1 0x03 // * Max +#define NIBE_MODE_FAN2 0x01 // * Medium +#define NIBE_MODE_FAN3 0x02 // * Low + +// Vertical air directions (3 bits) +// Note according to the remote control manual there are limitations: +// Operating Mode Auto allows all positions +// Operating Mode Cooling and Dry allows only pos 1-4 +// Operating Mode Heating allows only pos 3-6 +// When testing all modes accepted the different inputs. +#define NIBE_VDIR_AUTO 0x00 +#define NIBE_VDIR_POS1 0x04 +#define NIBE_VDIR_POS2 0x02 +#define NIBE_VDIR_POS3 0x06 +#define NIBE_VDIR_POS4 0x01 +#define NIBE_VDIR_POS5 0x05 +#define NIBE_VDIR_POS6 0x03 +#define NIBE_VDIR_ALL 0x07 + +//NOTE Horizontal air direction can be changed manually on the unit + +// Special Functions: +// The Remote Control comes with 4 special features +// iFeel -> Uses remote control to transmit temperature +// Night Program -> changes temperature and fan speed, 1h after it has been activated +// Filter -> Enable Air Ioniser, which effectively prevents bad odours and eliminates bacteria and microorganisms. +// Turbo Mode -> Full power Heating or Cooling + +// NOTE There are more functions such as timer and vacation mode, but they are not being used here! + +class NibeHeatpumpIR : public HeatpumpIR +{ + public: + NibeHeatpumpIR(); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboModeCmd, bool iFeelModeCmd); + void send(IRSender& IR, uint8_t currentTemperature); + + private: + void sendNibe(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t iFeelMode, uint8_t filter, uint8_t turboMode, uint8_t nightMode); +}; + +#ifdef NIBE_IR_SEND_TIME +extern int nibeSendHour; +extern int nibeSendMinute; +#endif + +#endif diff --git a/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp b/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp new file mode 100644 index 0000000000..985ac4f537 --- /dev/null +++ b/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp @@ -0,0 +1,146 @@ +#include + +PhilcoPHS32HeatpumpIR::PhilcoPHS32HeatpumpIR() : HeatpumpIR() +{ + static const char model[] PROGMEM = "philco_phs32"; + static const char info[] PROGMEM = "{\"mdl\":\"philco_phs32\",\"dn\":\"Philco PHS32\"}"; + + _model = model; + _info = info; +} + + +void PhilcoPHS32HeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd , uint8_t fanSpeedCmd , uint8_t temperatureCmd , uint8_t swingVCmd , uint8_t swingHCmd ) +{ + (void)swingVCmd; + (void)swingHCmd; + + // Sensible defaults for the heat pump mode + uint8_t powerMode = PHILCO_AIRCON1_POWER_ON; + uint8_t operatingMode = PHILCO_AIRCON1_MODE_COOL; + uint8_t fanSpeed = PHILCO_AIRCON1_FAN_AUTO; + uint8_t temperature = 24; + uint8_t swingV=0; + uint8_t swingH=0; + + if (powerModeCmd == POWER_OFF) + { + powerMode = PHILCO_AIRCON1_POWER_OFF; + temperature = PHILCO_AIRCON1_OFF_TEMP; + operatingMode = PHILCO_AIRCON1_MODE_COOL; + } + else + { + switch (operatingModeCmd) + { + case MODE_HEAT: + operatingMode = PHILCO_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = PHILCO_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = PHILCO_AIRCON1_MODE_DRY; + fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode + temperatureCmd = PHILCO_AIRCON1_DRY_TEMP; // Fixed temperature DRY mode + break; + case MODE_FAN: + operatingMode = PHILCO_AIRCON1_MODE_FAN; + temperatureCmd = PHILCO_AIRCON1_FAN_TEMP; // Fixed temperature FAN mode + if ( fanSpeedCmd == FAN_AUTO ) { + fanSpeedCmd = FAN_2; // Fan speed cannot be 'AUTO' in FAN mode + }; + break; + } + if ( temperatureCmd > 17 && temperatureCmd < 31) + { + temperature = temperatureCmd; + } + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = PHILCO_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = PHILCO_AIRCON1_FAN_LOW; + break; + case FAN_2: + fanSpeed = PHILCO_AIRCON1_FAN_LOW; + break; + case FAN_3: + fanSpeed = PHILCO_AIRCON1_FAN_MED; + break; + case FAN_4: + fanSpeed = PHILCO_AIRCON1_FAN_HIGH; + break; + case FAN_5: + fanSpeed = PHILCO_AIRCON1_FAN_HIGH; + break; + } + + sendPhilco(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH); +} + +// Send the Philco code +void PhilcoPHS32HeatpumpIR::sendPhilco(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV ,uint8_t swingH) +{ + (void)swingV; + (void)swingH; + + uint8_t PhilcoTemplate[] = { 0x83, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 }; + + uint8_t i; + + // Set Power state + PhilcoTemplate[18] = powerMode; + // Set the Fan speed, Temperature and Operating Mode + PhilcoTemplate[2] += fanSpeed; + PhilcoTemplate[3] = (temperature - 18) << 4; + PhilcoTemplate[3] += operatingMode; + + // Calculate checksums + for (int i = 2; i < 13; i++) { + PhilcoTemplate[13] ^= PhilcoTemplate[i]; + }; + for (int i = 14; i < 20; i++) { + PhilcoTemplate[20] ^= PhilcoTemplate[i]; + }; + + // 38 kHz PWM frequency + IR.setFrequency(38); + + // Send Header + IR.mark(PHILCO_AIRCON1_HDR_MARK); + IR.space(PHILCO_AIRCON1_HDR_SPACE); + + // Send 6 bytes of Data + for (unsigned int i=0; i<6; i++) { + IR.sendIRbyte(PhilcoTemplate[i], PHILCO_AIRCON1_BIT_MARK, PHILCO_AIRCON1_ZERO_SPACE, PHILCO_AIRCON1_ONE_SPACE); + } + + // Send Message space + IR.mark(PHILCO_AIRCON1_BIT_MARK); + IR.space(PHILCO_AIRCON1_MSG_SPACE); + + // Send 8 bytes of Data + for (unsigned int i=6; i<14; i++) { + IR.sendIRbyte(PhilcoTemplate[i], PHILCO_AIRCON1_BIT_MARK, PHILCO_AIRCON1_ZERO_SPACE, PHILCO_AIRCON1_ONE_SPACE); + } + + // Send Message space + IR.mark(PHILCO_AIRCON1_BIT_MARK); + IR.space(PHILCO_AIRCON1_MSG_SPACE); + + // Send 7 bytes of Data + for (unsigned int i=14; i<21; i++) { + IR.sendIRbyte(PhilcoTemplate[i], PHILCO_AIRCON1_BIT_MARK, PHILCO_AIRCON1_ZERO_SPACE, PHILCO_AIRCON1_ONE_SPACE); + } + + // End mark + IR.mark(PHILCO_AIRCON1_BIT_MARK); + IR.space(0); +} \ No newline at end of file diff --git a/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h b/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h new file mode 100644 index 0000000000..db232516e9 --- /dev/null +++ b/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h @@ -0,0 +1,48 @@ +/* + Philco PHS32 heatpump control +*/ +#ifndef PhilcoPHS32HeatpumpIR_h +#define PhilcoPHS32HeatpumpIR_h + +#include + +// Timing constants +#define PHILCO_AIRCON1_HDR_MARK 9000 +#define PHILCO_AIRCON1_HDR_SPACE 4500 +#define PHILCO_AIRCON1_MSG_SPACE 8000 +#define PHILCO_AIRCON1_BIT_MARK 562 +#define PHILCO_AIRCON1_ONE_SPACE 1687 +#define PHILCO_AIRCON1_ZERO_SPACE 562 + +// Power states +#define PHILCO_AIRCON1_POWER_ON 0x30 +#define PHILCO_AIRCON1_POWER_OFF 0x20 + +// Mode temperatures +#define PHILCO_AIRCON1_OFF_TEMP 0x12 +#define PHILCO_AIRCON1_DRY_TEMP 0x19 +#define PHILCO_AIRCON1_FAN_TEMP 0x19 + +// Operating modes +#define PHILCO_AIRCON1_MODE_HEAT 0x00 +#define PHILCO_AIRCON1_MODE_COOL 0x02 +#define PHILCO_AIRCON1_MODE_DRY 0x03 +#define PHILCO_AIRCON1_MODE_FAN 0x04 + +// Fan speeds +#define PHILCO_AIRCON1_FAN_AUTO 0x00 // * auto +#define PHILCO_AIRCON1_FAN_LOW 0x03 // * low +#define PHILCO_AIRCON1_FAN_MED 0x02 // * med +#define PHILCO_AIRCON1_FAN_HIGH 0x01 // * high + +class PhilcoPHS32HeatpumpIR : public HeatpumpIR +{ + public: + PhilcoPHS32HeatpumpIR(); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + + private: + void sendPhilco(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH); +}; + +#endif diff --git a/lib/HeatpumpIR/R51MHeatpumpIR.cpp b/lib/HeatpumpIR/R51MHeatpumpIR.cpp index 945de53d4b..0be76bd3e0 100644 --- a/lib/HeatpumpIR/R51MHeatpumpIR.cpp +++ b/lib/HeatpumpIR/R51MHeatpumpIR.cpp @@ -12,7 +12,7 @@ R51MHeatpumpIR::R51MHeatpumpIR() : HeatpumpIR() void R51MHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) { - const static uint8_t tempMap [] PROGMEM = {0,1,3,2,6,7,5,4,12,13,9,8,10,11 }; + const static byte tempMap [] PROGMEM = {0,1,3,2,6,7,5,4,12,13,9,8,10,11 }; // Sensible defaults for the heat pump mode uint8_t data[] = { 0xB2, 0x0F, 0x00 }; // The actual data is in this part diff --git a/lib/HeatpumpIR/README.md b/lib/HeatpumpIR/README.md index 83cb0316be..b063cf4d75 100644 --- a/lib/HeatpumpIR/README.md +++ b/lib/HeatpumpIR/README.md @@ -28,9 +28,11 @@ An Arduino library to control pump/split unit air conditioner. Currently support * Mitsubishi MSZ FD-25, probably also FD-35 (remote control P/N KM09D 0052376) * Also FH series has been confirmed to work * Mitsubishi Electric MSC-GA20VB, MSC-GA25VB, MSC-GA35VB (remote control P/N KP1A) +* Nibe AG-WL10-4, probably AG-WT10 * Panasonic E9/E12-CKP (Panasonic remote control P/N A75C2295) * Panasonic E9/E12-DKE (Panasonic remote control P/N A75C2616) * Panasonic E9/E12-JKE and E9/E12-NKE +* Philco PHS32 * Samsung * AQV12PSBN / AQV09ASA * AQV12MSAN, Remote Control ARH-1362 diff --git a/lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp b/lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp index 5d440caa3e..1a37066d5a 100644 --- a/lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp +++ b/lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp @@ -108,16 +108,16 @@ void ZHJG01HeatpumpIR::sendZHJG01(IRSender& IR, /******************************************************************************** * Byte[0]: Turbo, Eco, Fan, Vertical Swing - * TURBO ON: 0b0x1xxxxx - * ECO ON: 0b0x0xxxxx - * TURBO/ECO OFF: 0b1xxxxxxx - * FAN1: 0bx00xxxxx - * FAN2: 0bx01xxxxx - * FAN3: 0bx10xxxxx - * FAN AUTO: 0bx11xxxxx - * VERTICAL FIXED: 0bxxx01xxx - * VERTICAL SWING: 0bxxx10xxx - * VERTICAL WIND: 0bxxx11xxx + * TURBO ON: B0x1xxxxx + * ECO ON: B0x0xxxxx + * TURBO/ECO OFF: B1xxxxxxx + * FAN1: Bx00xxxxx + * FAN2: Bx01xxxxx + * FAN3: Bx10xxxxx + * FAN AUTO: Bx11xxxxx + * VERTICAL FIXED: Bxxx01xxx + * VERTICAL SWING: Bxxx10xxx + * VERTICAL WIND: Bxxx11xxx *******************************************************************************/ ZHJG01Template[1] = fanSpeed | swingV; ZHJG01Template[0] = ~ ZHJG01Template[1]; @@ -125,13 +125,13 @@ void ZHJG01HeatpumpIR::sendZHJG01(IRSender& IR, /******************************************************************************** * Byte[2]: Temp, Power, Mode * TEMP: Bttttxxxx - * POWER ON: 0bxxxx0xxx - * POWER OFF: 0bxxxx1xxx - * MODE HEAT: 0bxxxxx011 - * MODE VENT: 0bxxxxx100 - * MODE DRY: 0bxxxxx101 - * MODE COOL: 0bxxxxx110 - * MODE AUTO: 0bxxxxx111 + * POWER ON: Bxxxx0xxx + * POWER OFF: Bxxxx1xxx + * MODE HEAT: Bxxxxx011 + * MODE VENT: Bxxxxx100 + * MODE DRY: Bxxxxx101 + * MODE COOL: Bxxxxx110 + * MODE AUTO: Bxxxxx111 *******************************************************************************/ uint8_t tempBits = ((temperature - 17) << 4) & 0b11110000; diff --git a/lib/HeatpumpIR/ZHJG01HeatpumpIR.h b/lib/HeatpumpIR/ZHJG01HeatpumpIR.h index e88d66fdd6..c91f2d007e 100644 --- a/lib/HeatpumpIR/ZHJG01HeatpumpIR.h +++ b/lib/HeatpumpIR/ZHJG01HeatpumpIR.h @@ -22,14 +22,14 @@ * Every UNeven Byte (01,03,05,07 and 09) hold a checksum of the corresponding * command by inverting the bits, for example: * - * The identifier byte[0] = 0xD5 = 0b1101 0101 - * The checksum byte[1] = 0x2A = 0b0010 1010 + * The identifier byte[0] = 0xD5 = B1101 0101 + * The checksum byte[1] = 0x2A = B0010 1010 * * So, you can check the message by: * - inverting the bits of the checksum byte with the corresponding command, they * should be the same, or * - Summing up the checksum byte and the corresponding command, - * they should always add up to 0xFF = 0b11111111 = 255 + * they should always add up to 0xFF = B11111111 = 255 * * ****************************************************************************** * Written by: Abílio Costa diff --git a/lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp b/lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp index e1902d1bea..3030183e8a 100644 --- a/lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp +++ b/lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp @@ -149,29 +149,29 @@ void ZHLT01HeatpumpIR::sendZHLT01(IRSender& IR, uint8_t powerMode, /******************************************************************************** * Byte[07]: POWER, FAN, SLEEP, HORIZONTAL, VERTICAL - * POWER ON: 0b0xxxxx1x - * POWER OFF: 0b0xxxxx0x - * VERTICAL SWING: 0b0xxx01xx - * VERTICAL WIND: 0b0xxx00xx - * VERTICAL FIXED: 0b0xxx10xx - * HORIZONTAL SWING: 0b0xx0xxxx - * HORIZONTAL OFF: 0b0xx1xxxx - * FAN AUTO: 0b000xxxx0 - * FAN SILENT: 0b000xxxx1 - * FAN3: 0b001xxxx0 - * FAN2: 0b010xxxx0 - * FAN1: 0b011xxxx0 + * POWER ON: B0xxxxx1x + * POWER OFF: B0xxxxx0x + * VERTICAL SWING: B0xxx01xx + * VERTICAL WIND: B0xxx00xx + * VERTICAL FIXED: B0xxx10xx + * HORIZONTAL SWING: B0xx0xxxx + * HORIZONTAL OFF: B0xx1xxxx + * FAN AUTO: B000xxxx0 + * FAN SILENT: B000xxxx1 + * FAN3: B001xxxx0 + * FAN2: B010xxxx0 + * FAN1: B011xxxx0 *******************************************************************************/ ZHLT01Template[7] = fanSpeed | powerMode | swingV | swingH; ZHLT01Template[6] = ~ ZHLT01Template[7]; /******************************************************************************** * Byte[09]: Mode, Temperature - * MODE AUTO: 0b000xxxxx - * MODE COOL: 0b001xxxxx - * MODE VENT: 0b011xxxxx - * MODE DRY: 0b010xxxxx - * MODE HEAT: 0b100xxxxx + * MODE AUTO: B000xxxxx + * MODE COOL: B001xxxxx + * MODE VENT: B011xxxxx + * MODE DRY: B010xxxxx + * MODE HEAT: B100xxxxx * Temperature is determined by bit0-4: * 0x00 = 16C * 0x10 = 32C diff --git a/lib/HeatpumpIR/ZHLT01HeatpumpIR.h b/lib/HeatpumpIR/ZHLT01HeatpumpIR.h index d402afd876..33e9623e4c 100644 --- a/lib/HeatpumpIR/ZHLT01HeatpumpIR.h +++ b/lib/HeatpumpIR/ZHLT01HeatpumpIR.h @@ -41,14 +41,14 @@ * Every EVEN Byte (00,02,04,06,08 and 10) holds a checksum of the corresponding * command-, or identifier-byte by _inverting_ the bits, for example: * - * The identifier byte[11] = 0xD5 = 0b1101 0101 - * The checksum byte[10] = 0x2A = 0b0010 1010 + * The identifier byte[11] = 0xD5 = B1101 0101 + * The checksum byte[10] = 0x2A = B0010 1010 * * So, you can check the message by: * - inverting the bits of the checksum byte with the corresponding command-, or * identifier byte, they should me the same, or * - Summing up the checksum byte and the corresponding command-, or identifier byte, - * they should always add up to 0xFF = 0b11111111 = 255 + * they should always add up to 0xFF = B11111111 = 255 * * Control bytes: * [01] - Timer (1-24 hours, Off) diff --git a/lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino b/lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino new file mode 100644 index 0000000000..f2962949d9 --- /dev/null +++ b/lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino @@ -0,0 +1,183 @@ +#include + +#include + +IRSenderESP8266 irSender(4); + +KY26HeatpumpIR heatpumpIR = KY26HeatpumpIR(); + +uint8_t powers[] = { + POWER_ON, + POWER_OFF, +}; + +uint8_t modes[] = { + MODE_AUTO, + MODE_COOL, + MODE_DRY, + MODE_FAN, +}; + +uint8_t fans[] = { + FAN_1, + FAN_2, + FAN_3, +}; + +uint8_t power = powers[0]; +uint8_t mode = modes[0]; +uint8_t fan = fans[0]; +uint8_t temperature = 24; +float timer = 0; + +void printState() { + Serial.print(F("\nPower: ")); + Serial.print(power == POWER_ON ? F("On") : F("Off")); + Serial.print(F(", Mode: ")); + switch (mode) { + case MODE_AUTO: + Serial.print(F("Auto")); + break; + case MODE_COOL: + Serial.print(F("Cool")); + break; + case MODE_DRY: + Serial.print(F("Dry")); + break; + case MODE_FAN: + Serial.print(F("Fan")); + break; + } + Serial.print(F(", Fan: ")); + Serial.print(fan); + Serial.print(F(", Temperature: ")); + Serial.print(temperature); + Serial.print(F(", Timer: ")); + Serial.print(timer); + Serial.println(); +} + +void promptPower() { + Serial.println(F("\nSelect power:")); + Serial.println(F("1: On")); + Serial.println(F("2: Off")); + + while (true) { + if (Serial.available() > 0) { + int value = Serial.parseInt(); + if (value >= 1 && value <= 2) { + power = powers[value - 1]; + } + break; + } + } +} + +void promptMode() { + Serial.println(F("\nSelect mode:")); + Serial.println(F("1: Auto")); + Serial.println(F("2: Cool")); + Serial.println(F("3: Dry")); + Serial.println(F("4: Fan")); + + while (true) { + if (Serial.available() > 0) { + int value = Serial.parseInt(); + if (value >= 1 && value <= 4) { + mode = modes[value - 1]; + } + break; + } + } +} + +void promptFan() { + Serial.println(F("\nSelect fan:")); + Serial.println(F("1: Low")); + Serial.println(F("2: Medium")); + Serial.println(F("3: High")); + + while (true) { + if (Serial.available() > 0) { + int value = Serial.parseInt(); + if (value >= 1 && value <= 3) { + fan = fans[value - 1]; + } + break; + } + } +} + +void promptTemperature() { + Serial.println(F("\nSelect temperature:")); + + while (true) { + if (Serial.available() > 0) { + int value = Serial.parseInt(); + if (value >= 15 && value <= 31) { + temperature = value; + } + break; + } + } +} + +void promptTimer() { + Serial.println(F("\nSelect timer:")); + + while (true) { + if (Serial.available() > 0) { + float value = Serial.parseFloat(); + if (value >= 0 && value <= 12) { + timer = value; + } + break; + } + } +} + +void promptMenu() { + Serial.println(F("\nSelect option:")); + Serial.println(F("1: Power")); + Serial.println(F("2: Mode")); + Serial.println(F("3: Fan")); + Serial.println(F("4: Temperature")); + Serial.println(F("5: Timer")); + + while (true) { + if (Serial.available() > 0) { + int value = Serial.parseInt(); + switch (value) { + case 1: + promptPower(); + break; + case 2: + promptMode(); + break; + case 3: + promptFan(); + break; + case 4: + promptTemperature(); + break; + case 5: + promptTimer(); + break; + } + break; + } + } +} + +void setup() { + Serial.begin(9600); + delay(500); +} + +void loop() { + printState(); + promptMenu(); + + heatpumpIR.send(irSender, power, mode, fan, temperature, VDIR_AUTO, HDIR_AUTO, + timer); +} diff --git a/lib/HeatpumpIR/keywords.txt b/lib/HeatpumpIR/keywords.txt index 94e5831c44..6c28dd47b6 100644 --- a/lib/HeatpumpIR/keywords.txt +++ b/lib/HeatpumpIR/keywords.txt @@ -39,6 +39,7 @@ HitachiHeatpumpIR KEYWORD1 BalluHeatpumpIR KEYWORD1 AUXHeatpumpIR KEYWORD1 ZHLT01HeatpumpIR KEYWORD1 +KY26HeatpumpIR KEYWORD1 IRSender KEYWORD1 IRSenderPWM KEYWORD1 diff --git a/lib/HeatpumpIR/library.json b/lib/HeatpumpIR/library.json index db8041a672..349719476b 100644 --- a/lib/HeatpumpIR/library.json +++ b/lib/HeatpumpIR/library.json @@ -13,13 +13,13 @@ }, "frameworks": "arduino", "platforms": ["atmelavr", "espressif32", "espressif8266"], - "version": "1.0.23", + "version": "1.0.27", "dependencies": [ { "owner": "crankyoldgit", "name": "IRremoteESP8266", "version": "~2.8.4", - "platforms": ["espressif8266"] + "platforms": ["espressif8266", "espressif32"] } ] } diff --git a/lib/HeatpumpIR/library.properties b/lib/HeatpumpIR/library.properties index 30315771af..0b0b26aa7c 100644 --- a/lib/HeatpumpIR/library.properties +++ b/lib/HeatpumpIR/library.properties @@ -1,5 +1,5 @@ name=HeatpumpIR -version=1.0.23 +version=1.0.27 author=Toni Arte maintainer=Toni Arte sentence=Heatpump / Air Conditioner infrared control From 2da49d9b14ceedeb2ddfcb91f63416b866c8bdaa Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 11:11:53 +0200 Subject: [PATCH 08/48] [Sysinfo] Add used ESP-IDF version to sysinfo page --- src/src/Helpers/StringProvider.cpp | 11 +++++++++++ src/src/Helpers/StringProvider.h | 3 +++ src/src/WebServer/JSON.cpp | 6 ++++++ src/src/WebServer/SysInfoPage.cpp | 3 +++ 4 files changed, 23 insertions(+) diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 163ddb438e..ec1ad917a1 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -210,6 +210,9 @@ const __FlashStringHelper * getLabel(LabelType::Enum label) { case LabelType::BUILD_DESC: return F("Build"); case LabelType::GIT_BUILD: return F("Git Build"); case LabelType::SYSTEM_LIBRARIES: return F("System Libraries"); +#ifdef ESP32 + case LabelType::ESP_IDF_SDK_VERSION: return F("ESP-IDF Version"); +#endif case LabelType::PLUGIN_COUNT: return F("Plugin Count"); case LabelType::PLUGIN_DESCRIPTION: return F("Plugin Description"); case LabelType::BUILD_TIME: return F("Build Time"); @@ -543,6 +546,14 @@ String getValue(LabelType::Enum label) { return get_git_head(); } case LabelType::SYSTEM_LIBRARIES: return getSystemLibraryString(); +#ifdef ESP32 + case LabelType::ESP_IDF_SDK_VERSION: return strformat( + F("%d.%d.%d"), + ESP_IDF_VERSION_MAJOR, + ESP_IDF_VERSION_MINOR, + ESP_IDF_VERSION_PATCH); +#endif + case LabelType::PLUGIN_COUNT: retval = getDeviceCount() + 1; break; case LabelType::PLUGIN_DESCRIPTION: return getPluginDescriptionString(); case LabelType::BUILD_TIME: return String(get_build_date()) + ' ' + get_build_time(); diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 0b8e6b4b6f..7115368c3d 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -157,6 +157,9 @@ struct LabelType { BUILD_DESC, GIT_BUILD, SYSTEM_LIBRARIES, +#ifdef ESP32 + ESP_IDF_SDK_VERSION, +#endif PLUGIN_COUNT, PLUGIN_DESCRIPTION, BUILD_TIME, diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 527245c336..e510094ef1 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -163,6 +163,9 @@ void handle_json() LabelType::BUILD_DESC, LabelType::GIT_BUILD, LabelType::SYSTEM_LIBRARIES, +#ifdef ESP32 + LabelType::ESP_IDF_SDK_VERSION, +#endif LabelType::PLUGIN_COUNT, LabelType::PLUGIN_DESCRIPTION, LabelType::BUILD_TIME, @@ -665,6 +668,9 @@ void handle_buildinfo() { json_prop(LabelType::BUILD_DESC); json_prop(LabelType::GIT_BUILD); json_prop(LabelType::SYSTEM_LIBRARIES); +#ifdef ESP32 + json_prop(LabelType::ESP_IDF_SDK_VERSION); +#endif json_prop(LabelType::PLUGIN_COUNT); json_prop(LabelType::PLUGIN_DESCRIPTION); json_close(); diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 340ee7ea2a..b455cb2543 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -581,6 +581,9 @@ void handle_sysinfo_Firmware() { addHtml(F(BUILD_NOTES)); addRowLabelValue_copy(LabelType::SYSTEM_LIBRARIES); +#ifdef ESP32 + addRowLabelValue_copy(LabelType::ESP_IDF_SDK_VERSION); +#endif addRowLabelValue_copy(LabelType::GIT_BUILD); addRowLabelValue_copy(LabelType::PLUGIN_COUNT); addHtml(' '); From 32b887137d7b3330caa274bb713d86befdef810f Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 12:02:16 +0200 Subject: [PATCH 09/48] [HeatpumpIR] make the library compile again in ESP-IDF5.3 See: https://github.com/ToniA/arduino-heatpumpir/issues/181 --- lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h | 3 ++- lib/HeatpumpIR/FujitsuHeatpumpIR.h | 3 ++- lib/HeatpumpIR/GreeHeatpumpIR.h | 1 + lib/HeatpumpIR/KY26HeatpumpIR.h | 1 + lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h | 7 ++++--- lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h | 3 ++- lib/HeatpumpIR/PanasonicHeatpumpIR.h | 3 ++- lib/HeatpumpIR/VaillantHeatpumpIR.h | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h b/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h index 52ebba5779..a43c64226d 100644 --- a/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h +++ b/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h @@ -48,7 +48,8 @@ class DaikinHeatpumpARC480A14IR : public HeatpumpIR { public: DaikinHeatpumpARC480A14IR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + using HeatpumpIR::send; + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) override; void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, uint8_t comfortMode, uint8_t econo, uint8_t sensor, uint8_t quiet, uint8_t powerful); private: void sendDaikin(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t comfortMode, uint8_t econo, uint8_t sensor, uint8_t quiet, uint8_t powerful); diff --git a/lib/HeatpumpIR/FujitsuHeatpumpIR.h b/lib/HeatpumpIR/FujitsuHeatpumpIR.h index 057b60bee6..51662b8bc2 100644 --- a/lib/HeatpumpIR/FujitsuHeatpumpIR.h +++ b/lib/HeatpumpIR/FujitsuHeatpumpIR.h @@ -39,7 +39,8 @@ class FujitsuHeatpumpIR : public HeatpumpIR { public: FujitsuHeatpumpIR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + using HeatpumpIR::send; + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) override; void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ecoModeCmd); void sendFujitsuHiPower(IRSender& IR); void sendFujitsuEcoMode(IRSender& IR); diff --git a/lib/HeatpumpIR/GreeHeatpumpIR.h b/lib/HeatpumpIR/GreeHeatpumpIR.h index 03a63346e8..b44f44c42d 100644 --- a/lib/HeatpumpIR/GreeHeatpumpIR.h +++ b/lib/HeatpumpIR/GreeHeatpumpIR.h @@ -82,6 +82,7 @@ class GreeHeatpumpIR : public HeatpumpIR }; GreeHeatpumpIR(); + using HeatpumpIR::send; virtual const Timings & getTimings() const; diff --git a/lib/HeatpumpIR/KY26HeatpumpIR.h b/lib/HeatpumpIR/KY26HeatpumpIR.h index 906a139a04..1e1d07cf32 100644 --- a/lib/HeatpumpIR/KY26HeatpumpIR.h +++ b/lib/HeatpumpIR/KY26HeatpumpIR.h @@ -64,6 +64,7 @@ class KY26HeatpumpIR : public HeatpumpIR { public: KY26HeatpumpIR(); + using HeatpumpIR::send; void send(IRSender &IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, uint8_t timerHourCmd = 0x00, diff --git a/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h b/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h index f013b90b8e..3f312012fd 100644 --- a/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h +++ b/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h @@ -121,6 +121,7 @@ class MitsubishiHeavyHeatpumpIR : public HeatpumpIR { protected: // Cannot create generic MitsubishiHeavy heatpump instances MitsubishiHeavyHeatpumpIR(); + using HeatpumpIR::send; uint8_t _mitsubishiModel; // Tells whether this is ZJ or ZM (or other supported model...) public: @@ -132,7 +133,7 @@ class MitsubishiHeavyZJHeatpumpIR : public MitsubishiHeavyHeatpumpIR { public: MitsubishiHeavyZJHeatpumpIR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd) override; private: void sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode); @@ -142,7 +143,7 @@ class MitsubishiHeavyZMHeatpumpIR : public MitsubishiHeavyHeatpumpIR { public: MitsubishiHeavyZMHeatpumpIR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd) override; private: void sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode, uint8_t silentMode, uint8_t _3DAuto); @@ -152,7 +153,7 @@ class MitsubishiHeavyZMPHeatpumpIR : public MitsubishiHeavyHeatpumpIR { public: MitsubishiHeavyZMPHeatpumpIR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool cleanModeCmd, bool silentModeCmd, bool _3DAutoCmd) override; private: void sendMitsubishiHeavy(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t cleanMode); diff --git a/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h b/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h index a6bd4a7131..9dfc347f20 100644 --- a/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h +++ b/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h @@ -55,7 +55,8 @@ class PanasonicAltDKEHeatpumpIR : public HeatpumpIR { public: PanasonicAltDKEHeatpumpIR(); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + using HeatpumpIR::send; + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) override; void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ionizerCmd); void send(IRSender& IR, bool quiet, bool powerful); diff --git a/lib/HeatpumpIR/PanasonicHeatpumpIR.h b/lib/HeatpumpIR/PanasonicHeatpumpIR.h index f8eb9834b3..4e910b382a 100644 --- a/lib/HeatpumpIR/PanasonicHeatpumpIR.h +++ b/lib/HeatpumpIR/PanasonicHeatpumpIR.h @@ -59,7 +59,8 @@ class PanasonicHeatpumpIR : public HeatpumpIR uint8_t _panasonicModel; // Tells whether this is DKE, NKE or JKE (or other supported model...) public: - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + using HeatpumpIR::send; + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) override; void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool powerfulCmd, bool quietCmd); private: diff --git a/lib/HeatpumpIR/VaillantHeatpumpIR.h b/lib/HeatpumpIR/VaillantHeatpumpIR.h index c09e0c3e4d..47bca1d902 100644 --- a/lib/HeatpumpIR/VaillantHeatpumpIR.h +++ b/lib/HeatpumpIR/VaillantHeatpumpIR.h @@ -46,8 +46,9 @@ class VaillantHeatpumpIR : public HeatpumpIR { public: VaillantHeatpumpIR(); + using HeatpumpIR::send; void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, bool turboModeCmd, bool lightCmd); - void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd) override; private: void sendVaillant(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, bool turboMode, bool light); From 3e4d34b1789b6685361102b580ea8b94e04eef1d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 13:27:52 +0200 Subject: [PATCH 10/48] [ESP-IDF] Make ESP32-S2 build again --- platformio_core_defs.ini | 5 +++-- platformio_esp32_solo1.ini | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 944da391b8..0d6f90177a 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -168,7 +168,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 [core_esp32_IDF5_1__3_0_2_SPIFFS] platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2819/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2826/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 @@ -192,13 +192,14 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2807/framework-arduinoespressif32-all-release_v5.3-0c6101b5.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2823/framework-arduinoespressif32-all-release_v5.3-fe1954e5.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE -DETH_TYPE_JL1101_SUPPORTED + -DPR_9453_FLUSH_TO_CLEAR_REVERTED ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index e31cbc9eb9..fc9170c60f 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -19,7 +19,7 @@ build_unflags = ${esp32_base.build_unflags} [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2808/framework-arduinoespressif32-solo1-release_v5.3-0c6101b5.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2824/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From a628431d596aff0949494fc223b17754100c4cf1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 16:14:49 +0200 Subject: [PATCH 11/48] [Arduino] Add define as workaround for Arduino change flush() to clear() --- lib/Blynk/src/BlynkParticle.h | 2 +- platformio_core_defs.ini | 8 ++ src/_N001_Email.cpp | 4 +- src/_P020_Ser2Net.ino | 2 +- src/_P091_SerSwitch.ino | 133 +++++++++++--------- src/src/Commands/Blynk.cpp | 2 +- src/src/DataStructs/Web_StreamingBuffer.cpp | 4 +- src/src/PluginStructs/P020_data_struct.cpp | 2 +- src/src/PluginStructs/P044_data_struct.cpp | 2 +- 9 files changed, 91 insertions(+), 68 deletions(-) diff --git a/lib/Blynk/src/BlynkParticle.h b/lib/Blynk/src/BlynkParticle.h index 7137e83979..a39907c437 100644 --- a/lib/Blynk/src/BlynkParticle.h +++ b/lib/Blynk/src/BlynkParticle.h @@ -53,7 +53,7 @@ class BlynkTransportParticle return client.write((const uint8_t*)buf, len); } - void flush() { client.flush(); } + void flush() { client.PR_9453_FLUSH_TO_CLEAR(); } bool connected() { return client.connected(); } int available() { return client.available(); } diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 0d6f90177a..65ae724a6f 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -81,6 +81,7 @@ build_flags = -DNDEBUG -fno-strict-aliasing -DLIBRARIES_NO_LOG=1 -DNO_GLOBAL_I2S + -DPR_9453_FLUSH_TO_CLEAR=flush -I$PROJECT_DIR/src/include -include "ESPEasy_config.h" -O2 @@ -175,6 +176,8 @@ build_flags = -DESP32_STAGE -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE -DESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED + -DPR_9453_FLUSH_TO_CLEAR=clear + ;-DETH_TYPE_JL1101_SUPPORTED ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 @@ -200,6 +203,7 @@ build_flags = -DESP32_STAGE -DCONFIG_PM_ENABLE -DETH_TYPE_JL1101_SUPPORTED -DPR_9453_FLUSH_TO_CLEAR_REVERTED + -DPR_9453_FLUSH_TO_CLEAR=flush ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 @@ -213,3 +217,7 @@ build_flags = -DESP32_STAGE -Wnull-dereference lib_ignore = +; Regarding the define PR_9453_FLUSH_TO_CLEAR +; See: +; https://github.com/espressif/arduino-esp32/pull/8871/files +; https://github.com/espressif/arduino-esp32/pull/9453/files \ No newline at end of file diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 9736f60ab5..bebb3b4d29 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -387,7 +387,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co break; } } - client.flush(); + client.PR_9453_FLUSH_TO_CLEAR(); client.stop(); if (myStatus == true) { @@ -428,7 +428,7 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa } # endif // ifndef BUILD_NO_DEBUG - client.flush(); + client.PR_9453_FLUSH_TO_CLEAR(); if (aStr.length()) { client.println(aStr); } diff --git a/src/_P020_Ser2Net.ino b/src/_P020_Ser2Net.ino index 02c2473607..a3a254ee35 100644 --- a/src/_P020_Ser2Net.ino +++ b/src/_P020_Ser2Net.ino @@ -495,7 +495,7 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) success = true; } else if ((equals(command, F("ser2netclientsend"))) && (task->hasClientConnected())) { task->ser2netClient.print(string.substring(18)); - task->ser2netClient.flush(); + task->ser2netClient.PR_9453_FLUSH_TO_CLEAR(); success = true; } break; diff --git a/src/_P091_SerSwitch.ino b/src/_P091_SerSwitch.ino index fdf159d1fd..12bf22a8d7 100644 --- a/src/_P091_SerSwitch.ino +++ b/src/_P091_SerSwitch.ino @@ -747,13 +747,15 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) } void getmcustate() { - ESPEASY_SERIAL_0.write(0x55); // Tuya header 55AA - ESPEASY_SERIAL_0.write(0xAA); - ESPEASY_SERIAL_0.write(0x00); // version 00 - ESPEASY_SERIAL_0.write(0x08); // Tuya command 08 - request status - ESPEASY_SERIAL_0.write(0x00); - ESPEASY_SERIAL_0.write(0x00); - ESPEASY_SERIAL_0.write(0x07); + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x08, // Tuya command 08 - request status + 0x00, + 0x00, + 0x07}; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); } @@ -765,18 +767,21 @@ void sendmcucommand(uint8_t btnnum, uint8_t state, uint8_t swtype, uint8_t btnum { case SER_SWITCH_YEWE: { - ESPEASY_SERIAL_0.write(0x55); // Tuya header 55AA - ESPEASY_SERIAL_0.write(0xAA); - ESPEASY_SERIAL_0.write(0x00); // version 00 - ESPEASY_SERIAL_0.write(0x06); // Tuya command 06 - send order - ESPEASY_SERIAL_0.write(0x00); - ESPEASY_SERIAL_0.write(0x05); // following data length 0x05 - ESPEASY_SERIAL_0.write( (btnnum + 1) ); // relay number 1,2,3 - ESPEASY_SERIAL_0.write(0x01); // ? - ESPEASY_SERIAL_0.write(0x00); // ? - ESPEASY_SERIAL_0.write(0x01); // ? - ESPEASY_SERIAL_0.write( state ); // status - ESPEASY_SERIAL_0.write((13 + btnnum + state)); // checksum:sum of all bytes in packet mod 256 + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x06, // Tuya command 06 - send order + 0x00, + 0x05, // following data length 0x05 + static_cast(btnnum + 1) , // relay number 1,2,3 + 0x01, // ? + 0x00, // ? + 0x01, // ? + state , // status + static_cast(13 + btnnum + state)}; // checksum:sum of all bytes in packet mod 256 + + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); break; } @@ -793,10 +798,12 @@ void sendmcucommand(uint8_t btnnum, uint8_t state, uint8_t swtype, uint8_t btnum Plugin_091_switchstate[1] = state; } sstate = Plugin_091_switchstate[0] + (Plugin_091_switchstate[1] << 1) + (Plugin_091_switchstate[2] << 2); - ESPEASY_SERIAL_0.write(0xA0); - ESPEASY_SERIAL_0.write(0x04); - ESPEASY_SERIAL_0.write( sstate ); - ESPEASY_SERIAL_0.write(0xA1); + const uint8_t msg[] = { + 0xA0, + 0x04, + sstate, + 0xA1}; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); break; } @@ -813,22 +820,26 @@ void sendmcucommand(uint8_t btnnum, uint8_t state, uint8_t swtype, uint8_t btnum delay(1); } if (Plugin_091_ipd) { - ESPEASY_SERIAL_0.write(0x0D); - ESPEASY_SERIAL_0.write(0x0A); - ESPEASY_SERIAL_0.write(0x2B); - ESPEASY_SERIAL_0.write(0x49); - ESPEASY_SERIAL_0.write(0x50); - ESPEASY_SERIAL_0.write(0x44); - ESPEASY_SERIAL_0.write(0x2C); - ESPEASY_SERIAL_0.write(0x30); - ESPEASY_SERIAL_0.write(0x2C); - ESPEASY_SERIAL_0.write(0x34); - ESPEASY_SERIAL_0.write(0x3A); + const uint8_t msg[] = { + 0x0D, + 0x0A, + 0x2B, + 0x49, + 0x50, + 0x44, + 0x2C, + 0x30, + 0x2C, + 0x34, + 0x3A}; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); } - ESPEASY_SERIAL_0.write(0xA0); - ESPEASY_SERIAL_0.write((0x01 + btnnum)); - ESPEASY_SERIAL_0.write((0x00 + state)); - ESPEASY_SERIAL_0.write((0xA1 + state + btnnum)); + const uint8_t msg[] = { + 0xA0, + static_cast(0x01 + btnnum), + static_cast(0x00 + state), + static_cast(0xA1 + state + btnnum)}; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); } @@ -873,32 +884,36 @@ void sendmcudim(uint8_t dimvalue, uint8_t swtype) { case SER_SWITCH_YEWE: { - ESPEASY_SERIAL_0.write(0x55); // Tuya header 55AA - ESPEASY_SERIAL_0.write(0xAA); - ESPEASY_SERIAL_0.write(0x00); // version 00 - ESPEASY_SERIAL_0.write(0x06); // Tuya command 06 - send order - ESPEASY_SERIAL_0.write(0x00); - ESPEASY_SERIAL_0.write(0x08); // following data length 0x08 - ESPEASY_SERIAL_0.write(Plugin_091_numrelay); // dimmer order-id? select it at plugin settings 2/3!!! - ESPEASY_SERIAL_0.write(0x02); // type=value - ESPEASY_SERIAL_0.write(0x00); // length hi - ESPEASY_SERIAL_0.write(0x04); // length low - ESPEASY_SERIAL_0.write(0x00); // ? - ESPEASY_SERIAL_0.write(0x00); // ? - ESPEASY_SERIAL_0.write(0x00); // ? - ESPEASY_SERIAL_0.write( dimvalue ); // dim value (0-255) - ESPEASY_SERIAL_0.write( uint8_t(19 + Plugin_091_numrelay + dimvalue) ); // checksum:sum of all bytes in packet mod 256 + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x06, // Tuya command 06 - send order + 0x00, + 0x08, // following data length 0x08 + Plugin_091_numrelay, // dimmer order-id? select it at plugin settings 2/3!!! + 0x02, // type=value + 0x00, // length hi + 0x04, // length low + 0x00, // ? + 0x00, // ? + 0x00, // ? + dimvalue , // dim value (0-255) + static_cast(19 + Plugin_091_numrelay + dimvalue)}; // checksum:sum of all bytes in packet mod 256 + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); break; } case SER_SWITCH_WIFIDIMMER: { - ESPEASY_SERIAL_0.write(0xFF); // Wifidimmer header FF55 - ESPEASY_SERIAL_0.write(0x55); - ESPEASY_SERIAL_0.write( dimvalue ); // dim value (0-255) - ESPEASY_SERIAL_0.write(0x05); - ESPEASY_SERIAL_0.write(0xDC); - ESPEASY_SERIAL_0.write(0x0A); + const uint8_t msg[] = { + 0xFF, // Wifidimmer header FF55 + 0x55, + dimvalue , // dim value (0-255) + 0x05, + 0xDC, + 0x0A}; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); ESPEASY_SERIAL_0.flush(); Plugin_091_switchstate[1] = dimvalue; break; diff --git a/src/src/Commands/Blynk.cpp b/src/src/Commands/Blynk.cpp index dda35d5a54..acd47c5e23 100644 --- a/src/src/Commands/Blynk.cpp +++ b/src/src/Commands/Blynk.cpp @@ -176,7 +176,7 @@ bool Blynk_get(const String& command, controllerIndex_t controllerIndex, float * addLog(LOG_LEVEL_DEBUG, F("HTTP : closing connection (012)")); #endif - client.flush(); + client.PR_9453_FLUSH_TO_CLEAR(); client.stop(); // important - backgroundtasks - free mem diff --git a/src/src/DataStructs/Web_StreamingBuffer.cpp b/src/src/DataStructs/Web_StreamingBuffer.cpp index 565a4e1308..53a4f6fa2e 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.cpp +++ b/src/src/DataStructs/Web_StreamingBuffer.cpp @@ -268,7 +268,7 @@ void Web_StreamingBuffer::endStream() { buf.clear(); sendContentBlocking(buf); - web_server.client().flush(); + web_server.client().PR_9453_FLUSH_TO_CLEAR(); finalRam = ESP.getFreeHeap(); @@ -368,7 +368,7 @@ void Web_StreamingBuffer::sendHeaderBlocking(bool allowOriginAll, checkRAM(F("sendHeaderBlocking")); #endif - web_server.client().flush(); + web_server.client().PR_9453_FLUSH_TO_CLEAR(); #if defined(ESP8266) && defined(ARDUINO_ESP8266_RELEASE_2_3_0) web_server.setContentLength(CONTENT_LENGTH_UNKNOWN); diff --git a/src/src/PluginStructs/P020_data_struct.cpp b/src/src/PluginStructs/P020_data_struct.cpp index 63a9fc728e..5b1b2bfcaa 100644 --- a/src/src/PluginStructs/P020_data_struct.cpp +++ b/src/src/PluginStructs/P020_data_struct.cpp @@ -257,7 +257,7 @@ void P020_Task::handleSerialIn(struct EventStruct *event) { blinkLED(); rulesEngine(serial_buffer); - ser2netClient.flush(); + ser2netClient.PR_9453_FLUSH_TO_CLEAR(); clearBuffer(); # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Ser2Net: data sent!")); diff --git a/src/src/PluginStructs/P044_data_struct.cpp b/src/src/PluginStructs/P044_data_struct.cpp index 4b11d1f987..6db208ad24 100644 --- a/src/src/PluginStructs/P044_data_struct.cpp +++ b/src/src/PluginStructs/P044_data_struct.cpp @@ -299,7 +299,7 @@ void P044_Task::handleSerialIn(struct EventStruct *event) { if (done) { P1GatewayClient.print(serial_buffer); - P1GatewayClient.flush(); + P1GatewayClient.PR_9453_FLUSH_TO_CLEAR(); # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("P1 : data send!")); # endif // ifndef BUILD_NO_DEBUG From 93b1008eac4582b185a0dafcad015756aa001456 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 16:23:27 +0200 Subject: [PATCH 12/48] [Uncrustify] Cleanup _P091_SerSwitch.ino --- src/_P091_SerSwitch.ino | 1903 ++++++++++++++++++++------------------- 1 file changed, 979 insertions(+), 924 deletions(-) diff --git a/src/_P091_SerSwitch.ino b/src/_P091_SerSwitch.ino index 12bf22a8d7..14240226a0 100644 --- a/src/_P091_SerSwitch.ino +++ b/src/_P091_SerSwitch.ino @@ -1,924 +1,979 @@ -#include "_Plugin_Helper.h" - -/*########################################################################################## - ######################### Plugin 091: Serial MCU controlled switch ####################### - ########################################################################################## - - Features : - - Control serial linked devices through ESP8266 - - exactly only ONE P091 plugin can be used one time on one device! - - serial have to be ENABLED, and serial logging level set to 0 at ESPEasy settings! - - TUYA 4th button handling fixed by Mravko - - Compatible device list: - 1/ Tuya Wifi Touch wall switch (originally controlled by Tuya Smart/Smart Life app) - 2/ Tuya Wifi Dimmer Switch (originally controlled by Tuya Smart/Smart Life app) - 3/ Sonoff Dual - v1 only! (R2 has no serial MCU!) - 4/ LCTECH compatible 5V WiFi relay 1,2 and 4 relay versions also supported. - 5/ MOES Wifi Dimmer - - Relay states can be read from plugin values, the LCTech communication is only 1way, so the last stored state seen. - Tuya can report states and can be queried the actual state, Sonoff Dual may report its state, when it's hardware buttons pushed. - - Support forum thread: https://www.letscontrolit.com/forum/viewtopic.php?f=6&t=3245 - - !!! For some reasons the serial 2way communication only works with Arduino ESP8266 core 2.4.0 !!! - - List of commands : - - relay,[relay_number],[status] Set specific relay (0-3) to status (0/1) - - relaypulse,[relay_number],[status],[delay] Pulse specific relay for DELAY millisec with STATUS state, - than return to inverse state (blocking) - - relaylongpulse,[relay_number],[status],[delay] Pulse specific relay for DELAY seconds with STATUS state, - than return to inverse state (non-blocking) - - ydim,[DIM_VALUE] Set DIM_VALUE to Tuya dimmer switch (value can be 0-255, no range check!) - Of course, only the Tuya dimmer can do it... dim value can be read from plugin values. - There are no checks for is it state on or off. - - Command Examples : - - /control?cmd=relay,0,1 Switch on first relay - - /control?cmd=relay,0,0 Switch off first relay - - /control?cmd=relay,1,1 Switch on second relay - - /control?cmd=relay,1,0 Switch off second relay - - /control?cmd=relaypulse,0,1,500 Set first relay to ON for 500ms, than stay OFF - - /control?cmd=relaypulse,0,0,1000 Set first relay to OFF for 1s, than stay ON - - /control?cmd=ydim,255 Set dimmer to MAX value - - /control?cmd=ydim,25 Set dimmer to ~10% - - ------------------------------------------------------------------------------------------ - Copyleft Nagy Sándor 2019 - https://bitekmindenhol.blog.hu/ - ------------------------------------------------------------------------------------------ -*/ - -#ifdef USES_P091 - - -#define PLUGIN_091 -#define PLUGIN_ID_091 91 -#define PLUGIN_NAME_091 "Serial MCU controlled switch" -#define PLUGIN_VALUENAME1_091 "Relay0" -#define PLUGIN_VALUENAME2_091 "Relay1" -#define PLUGIN_VALUENAME3_091 "Relay2" -#define PLUGIN_VALUENAME4_091 "Relay3" - -#define BUFFER_SIZE 168 // increased for 4 button Tuya - -#define SER_SWITCH_YEWE 1 -#define SER_SWITCH_SONOFFDUAL 2 -#define SER_SWITCH_LCTECH 3 -#define SER_SWITCH_WIFIDIMMER 4 - -static uint8_t Plugin_091_switchstate[4]; -static uint8_t Plugin_091_ostate[4]; -uint8_t Plugin_091_commandstate = 0; // 0:no,1:inprogress,2:finished -Sensor_VType Plugin_091_type = Sensor_VType::SENSOR_TYPE_NONE; -uint8_t Plugin_091_numrelay = 1; -uint8_t Plugin_091_ownindex; -uint8_t Plugin_091_globalpar0; -uint8_t Plugin_091_globalpar1; -uint8_t Plugin_091_cmddbl = false; -uint8_t Plugin_091_ipd = false; -boolean Plugin_091_init = false; - -boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) -{ - boolean success = false; - - switch (function) - { - case PLUGIN_DEVICE_ADD: - { - Device[++deviceCount].Number = PLUGIN_ID_091; - Device[deviceCount].Type = DEVICE_TYPE_DUMMY; - Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; - Device[deviceCount].Ports = 0; - Device[deviceCount].PullUpOption = false; - Device[deviceCount].InverseLogicOption = false; - Device[deviceCount].FormulaOption = false; - Device[deviceCount].ValueCount = 4; - Device[deviceCount].SendDataOption = true; - Device[deviceCount].TimerOption = true; - Device[deviceCount].TimerOptional = true; - Device[deviceCount].GlobalSyncOption = true; - break; - } - case PLUGIN_GET_DEVICENAME: - { - string = F(PLUGIN_NAME_091); - break; - } - - case PLUGIN_GET_DEVICEVALUENAMES: - { - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_091)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_091)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_091)); - strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_091)); - success = true; - break; - } - - case PLUGIN_WEBFORM_LOAD: - { - { - const __FlashStringHelper * options[4] = { - F("Yewelink/TUYA"), - F("Sonoff Dual"), - F("LC TECH"), - F("Moes Wifi Dimmer") - }; - const int optionValues[4] = { SER_SWITCH_YEWE, SER_SWITCH_SONOFFDUAL, SER_SWITCH_LCTECH, SER_SWITCH_WIFIDIMMER }; - addFormSelector(F("Switch Type"), F("type"), 4, options, optionValues, PCONFIG(0)); - } - - if (PCONFIG(0) == SER_SWITCH_YEWE) - { - const __FlashStringHelper * buttonOptions[4] = { - F("1"), - F("2/Dimmer#2"), - F("3/Dimmer#3"), - F("4"), - }; - const int buttonoptionValues[4] = { 1, 2, 3, 4 }; - addFormSelector(F("Number of relays"), F("button"), 4, buttonOptions, buttonoptionValues, PCONFIG(1)); - } - - if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) - { - const __FlashStringHelper * modeoptions[3] = { - F("Normal"), - F("Exclude/Blinds mode"), - F("Simultaneous mode"), - }; - addFormSelector(F("Relay working mode"), F("mode"), 3, modeoptions, nullptr, PCONFIG(1)); - } - - if (PCONFIG(0) == SER_SWITCH_LCTECH) - { - { - const __FlashStringHelper * buttonOptions[4] = { - F("1"), - F("2"), - F("3"), - F("4"), - }; - const int buttonoptionValues[4] = { 1, 2, 3, 4 }; - addFormSelector(F("Number of relays"), F("button"), 4, buttonOptions, buttonoptionValues, PCONFIG(1)); - } - - { - const __FlashStringHelper * speedOptions[8] = { - F("9600"), - F("19200"), - F("115200"), - F("1200"), - F("2400"), - F("4800"), - F("38400"), - F("57600"), - }; - addFormSelector(F("Serial speed"), F("speed"), 8, speedOptions, nullptr, PCONFIG(2)); - } - - addFormCheckBox(F("Use command doubling"), F("dbl"), PCONFIG(3)); - addFormCheckBox(F("Use IPD preamble"), F("ipd"), PCONFIG(4)); - } - - success = true; - break; - } - - case PLUGIN_WEBFORM_SAVE: - { - - PCONFIG(0) = getFormItemInt(F("type")); - if (PCONFIG(0) == SER_SWITCH_YEWE) - { - PCONFIG(1) = getFormItemInt(F("button")); - } - if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) - { - PCONFIG(1) = getFormItemInt(F("mode")); - } - if (PCONFIG(0) == SER_SWITCH_LCTECH) - { - PCONFIG(1) = getFormItemInt(F("button")); - PCONFIG(2) = getFormItemInt(F("speed")); - PCONFIG(3) = isFormItemChecked(F("dbl")); - PCONFIG(4) = isFormItemChecked(F("ipd")); - Plugin_091_cmddbl = PCONFIG(3); - Plugin_091_ipd = PCONFIG(4); - } - - Plugin_091_globalpar0 = PCONFIG(0); - Plugin_091_globalpar1 = PCONFIG(1); - - success = true; - break; - } - - case PLUGIN_INIT: - { - String log; - Plugin_091_ownindex = event->TaskIndex; - Settings.UseSerial = true; // FIXME This is most likely very wrong... make sure that serial enabled - Settings.SerialLogLevel = 0; // and logging disabled - ESPEASY_SERIAL_0.setDebugOutput(false); // really, disable it! - log = F("SerSW : Init "); - if (PCONFIG(0) == SER_SWITCH_YEWE) - { - Plugin_091_numrelay = PCONFIG(1); - ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1); - ESPEASY_SERIAL_0.setRxBufferSize(BUFFER_SIZE); // Arduino core for ESP8266 WiFi chip 2.4.0 - delay(1); - getmcustate(); // request status on startup - log += strformat(F(" Yewe %d btn"), Plugin_091_numrelay); - } else - if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) - { - Plugin_091_numrelay = 3; // 3rd button is the "wifi" button - ESPEASY_SERIAL_0.begin(19230, SERIAL_8N1); - log += F(" Sonoff Dual"); - } else - if (PCONFIG(0) == SER_SWITCH_LCTECH) - { - Plugin_091_numrelay = PCONFIG(1); - Plugin_091_cmddbl = PCONFIG(3); - Plugin_091_ipd = PCONFIG(4); - const int bauds[] = {9600,19200,115200,1200,2400,4800,38400,57600}; - unsigned long Plugin_091_speed = bauds[PCONFIG(2)]; - ESPEASY_SERIAL_0.begin(Plugin_091_speed, SERIAL_8N1); - log += strformat(F(" LCTech %d baud %d btn"), Plugin_091_speed, Plugin_091_numrelay); - } else - if (PCONFIG(0) == SER_SWITCH_WIFIDIMMER) - { - Plugin_091_numrelay = 2; // 2nd button is the dimvalue - Plugin_091_switchstate[1] = 255; - Plugin_091_ostate[1] = 255; - ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1); - log += F(" Wifi Dimmer"); - } - - Plugin_091_globalpar0 = PCONFIG(0); - Plugin_091_globalpar1 = PCONFIG(1); - switch (Plugin_091_numrelay) - { - case 1: - Plugin_091_type = Sensor_VType::SENSOR_TYPE_SWITCH; - break; - case 2: - Plugin_091_type = Sensor_VType::SENSOR_TYPE_DUAL; - break; - case 3: - Plugin_091_type = Sensor_VType::SENSOR_TYPE_TRIPLE; - break; - case 4: - Plugin_091_type = Sensor_VType::SENSOR_TYPE_QUAD; - break; - } - addLogMove(LOG_LEVEL_INFO, log); - - success = true; - Plugin_091_init = true; - break; - } - - - case PLUGIN_SERIAL_IN: - { - int bytes_read = 0; - uint8_t serial_buf[BUFFER_SIZE]; - String log; - - if (Plugin_091_init) - { - while (ESPEASY_SERIAL_0.available() > 0) { - yield(); - if (bytes_read < BUFFER_SIZE) { - serial_buf[bytes_read] = ESPEASY_SERIAL_0.read(); - - if (bytes_read == 0) { // packet start - - Plugin_091_commandstate = 0; - switch (PCONFIG(0)) - { - case SER_SWITCH_YEWE: //decode first uint8_t of package - { - if (serial_buf[bytes_read] == 0x55) { - Plugin_091_commandstate = 1; - } - break; - } - case SER_SWITCH_SONOFFDUAL: //decode first uint8_t of package - { - if (serial_buf[bytes_read] == 0xA0) { - Plugin_091_commandstate = 1; - } - break; - } - } - } else { - - if (Plugin_091_commandstate == 1) { - - if (bytes_read == 1) { // check if packet is valid - switch (PCONFIG(0)) - { - case SER_SWITCH_YEWE: - { - if (serial_buf[bytes_read] != 0xAA) { - Plugin_091_commandstate = 0; - bytes_read = 0; - } - break; - } - case SER_SWITCH_SONOFFDUAL: - { - if ((serial_buf[bytes_read] != 0x04) && (serial_buf[bytes_read] != 0x00)) { - Plugin_091_commandstate = 0; - bytes_read = 0; - } - break; - } - } - } - - if ( (bytes_read == 2) && (PCONFIG(0) == SER_SWITCH_SONOFFDUAL)) { // decode Sonoff Dual status changes - Plugin_091_ostate[0] = Plugin_091_switchstate[0]; Plugin_091_ostate[1] = Plugin_091_switchstate[1]; Plugin_091_ostate[2] = Plugin_091_switchstate[2]; - Plugin_091_switchstate[0] = 0; Plugin_091_switchstate[1] = 0; Plugin_091_switchstate[2] = 0; - if ((serial_buf[bytes_read] & 1) == 1) { - Plugin_091_switchstate[0] = 1; - } - if ((serial_buf[bytes_read] & 2) == 2) { - Plugin_091_switchstate[1] = 1; - } - if ((serial_buf[bytes_read] & 4) == 4) { - Plugin_091_switchstate[2] = 1; - } - Plugin_091_commandstate = 2; bytes_read = 0; - - if (PCONFIG(1) == 1) - { // exclusive on mode - if ((Plugin_091_ostate[0] == 1) && (Plugin_091_switchstate[1] == 1)) { - sendmcucommand(0, 0, PCONFIG(0), PCONFIG(1)); - Plugin_091_switchstate[0] = 0; - } - if ((Plugin_091_ostate[1] == 1) && (Plugin_091_switchstate[0] == 1)) { - sendmcucommand(1, 0, PCONFIG(0), PCONFIG(1)); - Plugin_091_switchstate[1] = 0; - } - } - if (PCONFIG(1) == 2) - { // simultaneous mode - if ((Plugin_091_ostate[0] + Plugin_091_switchstate[0]) == 1) { - sendmcucommand(1, Plugin_091_switchstate[0], PCONFIG(0), PCONFIG(1)); - Plugin_091_switchstate[1] = Plugin_091_switchstate[0]; - } else { - if ((Plugin_091_ostate[1] + Plugin_091_switchstate[1]) == 1) { - sendmcucommand(0, Plugin_091_switchstate[1], PCONFIG(0), PCONFIG(1)); - Plugin_091_switchstate[0] = Plugin_091_switchstate[1]; - } - } - } - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - log = F("SerSW : State "); - } - for (int i = 0; i < 3; ++i) { - if (Plugin_091_ostate[i] != Plugin_091_switchstate[i]) { - UserVar.setFloat(event->TaskIndex, i, Plugin_091_switchstate[i]); - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - log += strformat(F(" r%d:%d"), i, Plugin_091_switchstate[i]); - } - } - } - addLogMove(LOG_LEVEL_INFO, log); - if ( (Plugin_091_ostate[0] != Plugin_091_switchstate[0]) || (Plugin_091_ostate[1] != Plugin_091_switchstate[1]) || (Plugin_091_ostate[2] != Plugin_091_switchstate[2]) || (Plugin_091_ostate[3] != Plugin_091_switchstate[3]) ) { - event->sensorType = Plugin_091_type; - sendData(event); - } - } - if (PCONFIG(0) == SER_SWITCH_YEWE) { // decode Tuya/Yewelink status report package - if ((bytes_read == 3) && (serial_buf[bytes_read] != 7)) - { - Plugin_091_commandstate = 0; // command code 7 means status reporting, we do not need other packets - bytes_read = 0; - } - if (bytes_read == 10) { - if (serial_buf[5] == 5) { - uint8_t btnnum = (serial_buf[6] - 1); - Plugin_091_ostate[btnnum] = Plugin_091_switchstate[btnnum]; - Plugin_091_switchstate[btnnum] = serial_buf[10]; - Plugin_091_commandstate = 2; bytes_read = 0; - - if (Plugin_091_ostate[btnnum] != Plugin_091_switchstate[btnnum]) { - log = F("SerSW : State"); - switch (btnnum) { - case 0: { - if (Plugin_091_numrelay > 0) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" r0:"), Plugin_091_switchstate[btnnum]); - } - break; - } - case 1: { - if (Plugin_091_numrelay > 1) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" r1:"), Plugin_091_switchstate[btnnum]); - } - break; - } - case 2: { - if (Plugin_091_numrelay > 2) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" r2:"), Plugin_091_switchstate[btnnum]); - } - break; - } - case 3: { - if (Plugin_091_numrelay > 3) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" r3:"), Plugin_091_switchstate[btnnum]); - } - break; - } - } - event->sensorType = Plugin_091_type; - addLogMove(LOG_LEVEL_INFO, log); - sendData(event); - } - } - } //10th uint8_t end (Tuya switch) - - if (bytes_read == 13) { - if (serial_buf[5] == 8) { - uint8_t btnnum = (serial_buf[6] - 1); - Plugin_091_ostate[btnnum] = Plugin_091_switchstate[btnnum]; - Plugin_091_switchstate[btnnum] = serial_buf[13]; - Plugin_091_commandstate = 2; bytes_read = 0; - - if (Plugin_091_ostate[btnnum] != Plugin_091_switchstate[btnnum]) { - log = F("SerSW : Dimmer"); - switch (btnnum) { - case 1: { - if (Plugin_091_numrelay > 1) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" d1:"), Plugin_091_switchstate[btnnum]); - } - break; - } - case 2: { - if (Plugin_091_numrelay > 2) { - UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); - log += concat(F(" d2:"), Plugin_091_switchstate[btnnum]); - } - break; - } - } - event->sensorType = Plugin_091_type; - addLogMove(LOG_LEVEL_INFO, log); - sendData(event); - } - } - } //13th uint8_t end (Tuya dimmer) - - } // yewe decode end - } // Plugin_091_commandstate 1 end - } // end of status decoding - - if (Plugin_091_commandstate == 1) { - bytes_read++; - } - } else - ESPEASY_SERIAL_0.read(); // if buffer full, dump incoming - } - } // plugin initialized end - success = true; - break; - } - - case PLUGIN_READ: - { - if (Plugin_091_init) - { - if ((PCONFIG(0) == SER_SWITCH_YEWE) && (Plugin_091_commandstate != 1)) - { // check Tuya state if anybody ask for it - addLog(LOG_LEVEL_INFO, F("SerSW : ReadState")); - getmcustate(); - } - if (PCONFIG(0) == SER_SWITCH_WIFIDIMMER) { - if (Plugin_091_switchstate[1] < 1) - { - UserVar.setFloat(event->TaskIndex, 0, 0); - } else { - UserVar.setFloat(event->TaskIndex, 0, 1); - } - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - success = true; - } - break; - } - - case PLUGIN_WRITE: - { - String log; - String command = parseString(string, 1); - uint8_t rnum = 0; - uint8_t rcmd = 0; - uint8_t par3 = 0; - - if (Plugin_091_init) - { - if ( equals(command, F("relay"))) // deal with relay change command - { - success = true; - - if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { - rnum = event->Par1; - } - if ((event->Par2 == 0) || (event->Par2 == 1)) { - rcmd = event->Par2; - } - - // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please - event->setTaskIndex(Plugin_091_ownindex); - - if (event->Par2 == 2) { // toggle - rcmd = 1 - UserVar[(event->BaseVarIndex + rnum)]; - } - - if ( Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { - par3 = Plugin_091_globalpar1; - } - sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); - if ( Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe - if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed - UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); - if (( par3 == 1) && (rcmd == 1) && (rnum < 2)) - { // exclusive on mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); - } - if (par3 == 2) { // simultaneous mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); - } - if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - event->sensorType = Plugin_091_type; - sendData(event); - } - } - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitch r%d:%d"), rnum, rcmd)); - } - } else - - if ( equals(command, F("relaypulse"))) - { - success = true; - - if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { - rnum = event->Par1; - } - if ((event->Par2 == 0) || (event->Par2 == 1)) { - rcmd = event->Par2; - } - // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please - event->setTaskIndex(Plugin_091_ownindex); - - if ( Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { - par3 = Plugin_091_globalpar1; - } - - sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // init state - delay(event->Par3); - sendmcucommand(rnum, !rcmd, Plugin_091_globalpar0, par3); // invert state - if ( Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe - if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed - UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); - if (( par3 == 1) && (rcmd == 1) && (rnum < 2)) - { // exclusive on mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); - } - if (par3 == 2) { // simultaneous mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); - } - if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - event->sensorType = Plugin_091_type; - sendData(event); - } - } - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulsed for %d mS"), rnum, rcmd, event->Par3)); - } - } else - - if ( equals(command, F("relaylongpulse"))) - { - success = true; - - if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { - rnum = event->Par1; - } - if ((event->Par2 == 0) || (event->Par2 == 1)) { - rcmd = event->Par2; - } - // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please - event->setTaskIndex(Plugin_091_ownindex); - - if ( Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { - par3 = Plugin_091_globalpar1; - } - unsigned long timer = event->Par3 * 1000; - - sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // init state - //Scheduler.setPluginTimer(timer, PLUGIN_ID_091, rnum, !rcmd); - Scheduler.setPluginTaskTimer(timer, event->TaskIndex, rnum, !rcmd); - if ( Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe - if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed - UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); - if (( par3 == 1) && (rcmd == 1) && (rnum < 2)) - { // exclusive on mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); - } - if (par3 == 2) { // simultaneous mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); - } - if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - event->sensorType = Plugin_091_type; - sendData(event); - } - } - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulse for %d sec"), rnum, rcmd, event->Par3)); - } - } else - if ( equals(command, F("ydim")) ) // deal with dimmer command - { - if (( (Plugin_091_globalpar0 == SER_SWITCH_YEWE) && (Plugin_091_numrelay > 1)) || (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER)) { // only on tuya dimmer - success = true; - - // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please - event->setTaskIndex(Plugin_091_ownindex); - - sendmcudim(event->Par1, Plugin_091_globalpar0); - if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { - if (Plugin_091_switchstate[1] < 1) // follow state - { - UserVar.setFloat(event->TaskIndex, 0, 0); - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } else { - UserVar.setFloat(event->TaskIndex, 0, 1); - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - event->sensorType = Plugin_091_type; - sendData(event); - } - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, concat(F("SerSW : SetDim "), event->Par1)); - } - } else { - SendStatus(event, F("\nYDim not supported")); - } - } - - } - - break; - } - - case PLUGIN_TASKTIMER_IN: - { - uint8_t par3 = 0; - - // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please - event->setTaskIndex(Plugin_091_ownindex); - - if ( Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { - par3 = Plugin_091_globalpar1; - } - - uint8_t rnum = event->Par1; - uint8_t rcmd = event->Par2; - - sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // invert state - if ( Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe - if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed - UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); - if (( par3 == 1) && (rcmd == 1) && (rnum < 2)) - { // exclusive on mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); - } - if (par3 == 2) { // simultaneous mode for Dual - // FIXME TD-er: Is this a valid UserVar index? - UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); - } - if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { - UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); - } - event->sensorType = Plugin_091_type; - sendData(event); - } - } - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulse ended"), rnum, rcmd)); - } - - break; - } - - - } - return success; -} - -void getmcustate() { - const uint8_t msg[] = { - 0x55, // Tuya header 55AA - 0xAA, - 0x00, // version 00 - 0x08, // Tuya command 08 - request status - 0x00, - 0x00, - 0x07}; - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); -} - -void sendmcucommand(uint8_t btnnum, uint8_t state, uint8_t swtype, uint8_t btnum_mode) // btnnum=0,1,2, state=0/1 -{ - uint8_t sstate; - - switch (swtype) - { - case SER_SWITCH_YEWE: - { - const uint8_t msg[] = { - 0x55, // Tuya header 55AA - 0xAA, - 0x00, // version 00 - 0x06, // Tuya command 06 - send order - 0x00, - 0x05, // following data length 0x05 - static_cast(btnnum + 1) , // relay number 1,2,3 - 0x01, // ? - 0x00, // ? - 0x01, // ? - state , // status - static_cast(13 + btnnum + state)}; // checksum:sum of all bytes in packet mod 256 - - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); - break; - } - case SER_SWITCH_SONOFFDUAL: - { - Plugin_091_switchstate[btnnum] = state; - if (( btnum_mode == 1) && (state == 1) && (btnnum < 2)) - { // exclusive on mode - Plugin_091_switchstate[(1 - btnnum)] = 0; - } - if (btnum_mode == 2) - { // simultaneous mode - Plugin_091_switchstate[0] = state; - Plugin_091_switchstate[1] = state; - } - sstate = Plugin_091_switchstate[0] + (Plugin_091_switchstate[1] << 1) + (Plugin_091_switchstate[2] << 2); - const uint8_t msg[] = { - 0xA0, - 0x04, - sstate, - 0xA1}; - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); - break; - } - case SER_SWITCH_LCTECH: - { - uint8_t c_d = 1; - if (Plugin_091_cmddbl) { - c_d = 2; - } - Plugin_091_switchstate[btnnum] = state; - for (uint8_t x = 0; x < c_d; ++x) // try twice to be sure - { - if (x > 0) { - delay(1); - } - if (Plugin_091_ipd) { - const uint8_t msg[] = { - 0x0D, - 0x0A, - 0x2B, - 0x49, - 0x50, - 0x44, - 0x2C, - 0x30, - 0x2C, - 0x34, - 0x3A}; - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - } - const uint8_t msg[] = { - 0xA0, - static_cast(0x01 + btnnum), - static_cast(0x00 + state), - static_cast(0xA1 + state + btnnum)}; - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); - } - - break; - - } - case SER_SWITCH_WIFIDIMMER: - { - if (btnnum == 0) { - if (state == 0) { // off - Plugin_091_switchstate[0] = 0; - if (Plugin_091_switchstate[1] < 1) { - if (Plugin_091_ostate[1] < 1) { - Plugin_091_ostate[1] = 255; - } - } else { - Plugin_091_ostate[1] = Plugin_091_switchstate[1]; - } - sendmcudim(0, SER_SWITCH_WIFIDIMMER); - } else { // on - Plugin_091_switchstate[0] = 1; - if (Plugin_091_ostate[1] < 1) { - if (Plugin_091_switchstate[1] > 0) { - sstate = Plugin_091_switchstate[1]; - } else { - sstate = 255; - } - } else { - sstate = Plugin_091_ostate[1]; - } - sendmcudim(sstate, SER_SWITCH_WIFIDIMMER); - } - } - break; - } - } -} - -void sendmcudim(uint8_t dimvalue, uint8_t swtype) -{ - switch (swtype) - { - case SER_SWITCH_YEWE: - { - const uint8_t msg[] = { - 0x55, // Tuya header 55AA - 0xAA, - 0x00, // version 00 - 0x06, // Tuya command 06 - send order - 0x00, - 0x08, // following data length 0x08 - Plugin_091_numrelay, // dimmer order-id? select it at plugin settings 2/3!!! - 0x02, // type=value - 0x00, // length hi - 0x04, // length low - 0x00, // ? - 0x00, // ? - 0x00, // ? - dimvalue , // dim value (0-255) - static_cast(19 + Plugin_091_numrelay + dimvalue)}; // checksum:sum of all bytes in packet mod 256 - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); - break; - } - case SER_SWITCH_WIFIDIMMER: - { - const uint8_t msg[] = { - 0xFF, // Wifidimmer header FF55 - 0x55, - dimvalue , // dim value (0-255) - 0x05, - 0xDC, - 0x0A}; - ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); - ESPEASY_SERIAL_0.flush(); - Plugin_091_switchstate[1] = dimvalue; - break; - } - } -} - -#endif // USES_P091 \ No newline at end of file +#include "_Plugin_Helper.h" + +/*########################################################################################## + ######################### Plugin 091: Serial MCU controlled switch ####################### + ########################################################################################## + + Features : + - Control serial linked devices through ESP8266 + - exactly only ONE P091 plugin can be used one time on one device! + - serial have to be ENABLED, and serial logging level set to 0 at ESPEasy settings! + - TUYA 4th button handling fixed by Mravko + + Compatible device list: + 1/ Tuya Wifi Touch wall switch (originally controlled by Tuya Smart/Smart Life app) + 2/ Tuya Wifi Dimmer Switch (originally controlled by Tuya Smart/Smart Life app) + 3/ Sonoff Dual - v1 only! (R2 has no serial MCU!) + 4/ LCTECH compatible 5V WiFi relay 1,2 and 4 relay versions also supported. + 5/ MOES Wifi Dimmer + + Relay states can be read from plugin values, the LCTech communication is only 1way, so the last stored state seen. + Tuya can report states and can be queried the actual state, Sonoff Dual may report its state, when it's hardware buttons pushed. + + Support forum thread: https://www.letscontrolit.com/forum/viewtopic.php?f=6&t=3245 + + !!! For some reasons the serial 2way communication only works with Arduino ESP8266 core 2.4.0 !!! + + List of commands : + - relay,[relay_number],[status] Set specific relay (0-3) to status (0/1) + - relaypulse,[relay_number],[status],[delay] Pulse specific relay for DELAY millisec with STATUS state, + than return to inverse state (blocking) + - relaylongpulse,[relay_number],[status],[delay] Pulse specific relay for DELAY seconds with STATUS state, + than return to inverse state (non-blocking) + - ydim,[DIM_VALUE] Set DIM_VALUE to Tuya dimmer switch (value can be 0-255, no range check!) + Of course, only the Tuya dimmer can do it... dim value can be read from plugin + #values. + There are no checks for is it state on or off. + + Command Examples : + - /control?cmd=relay,0,1 Switch on first relay + - /control?cmd=relay,0,0 Switch off first relay + - /control?cmd=relay,1,1 Switch on second relay + - /control?cmd=relay,1,0 Switch off second relay + - /control?cmd=relaypulse,0,1,500 Set first relay to ON for 500ms, than stay OFF + - /control?cmd=relaypulse,0,0,1000 Set first relay to OFF for 1s, than stay ON + - /control?cmd=ydim,255 Set dimmer to MAX value + - /control?cmd=ydim,25 Set dimmer to ~10% + + ------------------------------------------------------------------------------------------ + Copyleft Nagy Sándor 2019 - https://bitekmindenhol.blog.hu/ + ------------------------------------------------------------------------------------------ + */ + +#ifdef USES_P091 + + +# define PLUGIN_091 +# define PLUGIN_ID_091 91 +# define PLUGIN_NAME_091 "Serial MCU controlled switch" +# define PLUGIN_VALUENAME1_091 "Relay0" +# define PLUGIN_VALUENAME2_091 "Relay1" +# define PLUGIN_VALUENAME3_091 "Relay2" +# define PLUGIN_VALUENAME4_091 "Relay3" + +# define BUFFER_SIZE 168 // increased for 4 button Tuya + +# define SER_SWITCH_YEWE 1 +# define SER_SWITCH_SONOFFDUAL 2 +# define SER_SWITCH_LCTECH 3 +# define SER_SWITCH_WIFIDIMMER 4 + +static uint8_t Plugin_091_switchstate[4]; +static uint8_t Plugin_091_ostate[4]; +uint8_t Plugin_091_commandstate = 0; // 0:no,1:inprogress,2:finished +Sensor_VType Plugin_091_type = Sensor_VType::SENSOR_TYPE_NONE; +uint8_t Plugin_091_numrelay = 1; +uint8_t Plugin_091_ownindex; +uint8_t Plugin_091_globalpar0; +uint8_t Plugin_091_globalpar1; +uint8_t Plugin_091_cmddbl = false; +uint8_t Plugin_091_ipd = false; +boolean Plugin_091_init = false; + +boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_091; + Device[deviceCount].Type = DEVICE_TYPE_DUMMY; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = false; + Device[deviceCount].ValueCount = 4; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].GlobalSyncOption = true; + break; + } + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_091); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_091)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_091)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_091)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_091)); + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + { + const __FlashStringHelper *options[4] = { + F("Yewelink/TUYA"), + F("Sonoff Dual"), + F("LC TECH"), + F("Moes Wifi Dimmer") + }; + const int optionValues[4] = { SER_SWITCH_YEWE, SER_SWITCH_SONOFFDUAL, SER_SWITCH_LCTECH, SER_SWITCH_WIFIDIMMER }; + addFormSelector(F("Switch Type"), F("type"), 4, options, optionValues, PCONFIG(0)); + } + + if (PCONFIG(0) == SER_SWITCH_YEWE) + { + const __FlashStringHelper *buttonOptions[4] = { + F("1"), + F("2/Dimmer#2"), + F("3/Dimmer#3"), + F("4"), + }; + const int buttonoptionValues[4] = { 1, 2, 3, 4 }; + addFormSelector(F("Number of relays"), F("button"), 4, buttonOptions, buttonoptionValues, PCONFIG(1)); + } + + if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) + { + const __FlashStringHelper *modeoptions[3] = { + F("Normal"), + F("Exclude/Blinds mode"), + F("Simultaneous mode"), + }; + addFormSelector(F("Relay working mode"), F("mode"), 3, modeoptions, nullptr, PCONFIG(1)); + } + + if (PCONFIG(0) == SER_SWITCH_LCTECH) + { + { + const __FlashStringHelper *buttonOptions[4] = { + F("1"), + F("2"), + F("3"), + F("4"), + }; + const int buttonoptionValues[4] = { 1, 2, 3, 4 }; + addFormSelector(F("Number of relays"), F("button"), 4, buttonOptions, buttonoptionValues, PCONFIG(1)); + } + + { + const __FlashStringHelper *speedOptions[8] = { + F("9600"), + F("19200"), + F("115200"), + F("1200"), + F("2400"), + F("4800"), + F("38400"), + F("57600"), + }; + addFormSelector(F("Serial speed"), F("speed"), 8, speedOptions, nullptr, PCONFIG(2)); + } + + addFormCheckBox(F("Use command doubling"), F("dbl"), PCONFIG(3)); + addFormCheckBox(F("Use IPD preamble"), F("ipd"), PCONFIG(4)); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + PCONFIG(0) = getFormItemInt(F("type")); + + if (PCONFIG(0) == SER_SWITCH_YEWE) + { + PCONFIG(1) = getFormItemInt(F("button")); + } + + if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) + { + PCONFIG(1) = getFormItemInt(F("mode")); + } + + if (PCONFIG(0) == SER_SWITCH_LCTECH) + { + PCONFIG(1) = getFormItemInt(F("button")); + PCONFIG(2) = getFormItemInt(F("speed")); + PCONFIG(3) = isFormItemChecked(F("dbl")); + PCONFIG(4) = isFormItemChecked(F("ipd")); + Plugin_091_cmddbl = PCONFIG(3); + Plugin_091_ipd = PCONFIG(4); + } + + Plugin_091_globalpar0 = PCONFIG(0); + Plugin_091_globalpar1 = PCONFIG(1); + + success = true; + break; + } + + case PLUGIN_INIT: + { + String log; + Plugin_091_ownindex = event->TaskIndex; + Settings.UseSerial = true; // FIXME This is most likely very wrong... make sure that serial enabled + Settings.SerialLogLevel = 0; // and logging disabled + ESPEASY_SERIAL_0.setDebugOutput(false); // really, disable it! + log = F("SerSW : Init "); + + if (PCONFIG(0) == SER_SWITCH_YEWE) + { + Plugin_091_numrelay = PCONFIG(1); + ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1); + ESPEASY_SERIAL_0.setRxBufferSize(BUFFER_SIZE); // Arduino core for ESP8266 WiFi chip 2.4.0 + delay(1); + getmcustate(); // request status on startup + log += strformat(F(" Yewe %d btn"), Plugin_091_numrelay); + } else + if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) + { + Plugin_091_numrelay = 3; // 3rd button is the "wifi" button + ESPEASY_SERIAL_0.begin(19230, SERIAL_8N1); + log += F(" Sonoff Dual"); + } else + if (PCONFIG(0) == SER_SWITCH_LCTECH) + { + Plugin_091_numrelay = PCONFIG(1); + Plugin_091_cmddbl = PCONFIG(3); + Plugin_091_ipd = PCONFIG(4); + const int bauds[] = { 9600, 19200, 115200, 1200, 2400, 4800, 38400, 57600 }; + unsigned long Plugin_091_speed = bauds[PCONFIG(2)]; + ESPEASY_SERIAL_0.begin(Plugin_091_speed, SERIAL_8N1); + log += strformat(F(" LCTech %d baud %d btn"), Plugin_091_speed, Plugin_091_numrelay); + } else + if (PCONFIG(0) == SER_SWITCH_WIFIDIMMER) + { + Plugin_091_numrelay = 2; // 2nd button is the dimvalue + Plugin_091_switchstate[1] = 255; + Plugin_091_ostate[1] = 255; + ESPEASY_SERIAL_0.begin(9600, SERIAL_8N1); + log += F(" Wifi Dimmer"); + } + + Plugin_091_globalpar0 = PCONFIG(0); + Plugin_091_globalpar1 = PCONFIG(1); + + switch (Plugin_091_numrelay) + { + case 1: + Plugin_091_type = Sensor_VType::SENSOR_TYPE_SWITCH; + break; + case 2: + Plugin_091_type = Sensor_VType::SENSOR_TYPE_DUAL; + break; + case 3: + Plugin_091_type = Sensor_VType::SENSOR_TYPE_TRIPLE; + break; + case 4: + Plugin_091_type = Sensor_VType::SENSOR_TYPE_QUAD; + break; + } + addLogMove(LOG_LEVEL_INFO, log); + + success = true; + Plugin_091_init = true; + break; + } + + + case PLUGIN_SERIAL_IN: + { + int bytes_read = 0; + uint8_t serial_buf[BUFFER_SIZE]; + String log; + + if (Plugin_091_init) + { + while (ESPEASY_SERIAL_0.available() > 0) { + yield(); + + if (bytes_read < BUFFER_SIZE) { + serial_buf[bytes_read] = ESPEASY_SERIAL_0.read(); + + if (bytes_read == 0) { // packet start + Plugin_091_commandstate = 0; + + switch (PCONFIG(0)) + { + case SER_SWITCH_YEWE: // decode first uint8_t of package + { + if (serial_buf[bytes_read] == 0x55) { + Plugin_091_commandstate = 1; + } + break; + } + case SER_SWITCH_SONOFFDUAL: // decode first uint8_t of package + { + if (serial_buf[bytes_read] == 0xA0) { + Plugin_091_commandstate = 1; + } + break; + } + } + } else { + if (Plugin_091_commandstate == 1) { + if (bytes_read == 1) { // check if packet is valid + switch (PCONFIG(0)) + { + case SER_SWITCH_YEWE: + { + if (serial_buf[bytes_read] != 0xAA) { + Plugin_091_commandstate = 0; + bytes_read = 0; + } + break; + } + case SER_SWITCH_SONOFFDUAL: + { + if ((serial_buf[bytes_read] != 0x04) && (serial_buf[bytes_read] != 0x00)) { + Plugin_091_commandstate = 0; + bytes_read = 0; + } + break; + } + } + } + + if ((bytes_read == 2) && (PCONFIG(0) == SER_SWITCH_SONOFFDUAL)) { // decode Sonoff Dual status changes + Plugin_091_ostate[0] = Plugin_091_switchstate[0]; Plugin_091_ostate[1] = Plugin_091_switchstate[1]; + Plugin_091_ostate[2] = Plugin_091_switchstate[2]; + Plugin_091_switchstate[0] = 0; Plugin_091_switchstate[1] = 0; Plugin_091_switchstate[2] = 0; + + if ((serial_buf[bytes_read] & 1) == 1) { + Plugin_091_switchstate[0] = 1; + } + + if ((serial_buf[bytes_read] & 2) == 2) { + Plugin_091_switchstate[1] = 1; + } + + if ((serial_buf[bytes_read] & 4) == 4) { + Plugin_091_switchstate[2] = 1; + } + Plugin_091_commandstate = 2; bytes_read = 0; + + if (PCONFIG(1) == 1) + { // exclusive on mode + if ((Plugin_091_ostate[0] == 1) && (Plugin_091_switchstate[1] == 1)) { + sendmcucommand(0, 0, PCONFIG(0), PCONFIG(1)); + Plugin_091_switchstate[0] = 0; + } + + if ((Plugin_091_ostate[1] == 1) && (Plugin_091_switchstate[0] == 1)) { + sendmcucommand(1, 0, PCONFIG(0), PCONFIG(1)); + Plugin_091_switchstate[1] = 0; + } + } + + if (PCONFIG(1) == 2) + { // simultaneous mode + if ((Plugin_091_ostate[0] + Plugin_091_switchstate[0]) == 1) { + sendmcucommand(1, Plugin_091_switchstate[0], PCONFIG(0), PCONFIG(1)); + Plugin_091_switchstate[1] = Plugin_091_switchstate[0]; + } else { + if ((Plugin_091_ostate[1] + Plugin_091_switchstate[1]) == 1) { + sendmcucommand(0, Plugin_091_switchstate[1], PCONFIG(0), PCONFIG(1)); + Plugin_091_switchstate[0] = Plugin_091_switchstate[1]; + } + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + log = F("SerSW : State "); + } + + for (int i = 0; i < 3; ++i) { + if (Plugin_091_ostate[i] != Plugin_091_switchstate[i]) { + UserVar.setFloat(event->TaskIndex, i, Plugin_091_switchstate[i]); + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + log += strformat(F(" r%d:%d"), i, Plugin_091_switchstate[i]); + } + } + } + addLogMove(LOG_LEVEL_INFO, log); + + if ((Plugin_091_ostate[0] != Plugin_091_switchstate[0]) || (Plugin_091_ostate[1] != Plugin_091_switchstate[1]) || + (Plugin_091_ostate[2] != Plugin_091_switchstate[2]) || (Plugin_091_ostate[3] != Plugin_091_switchstate[3])) { + event->sensorType = Plugin_091_type; + sendData(event); + } + } + + if (PCONFIG(0) == SER_SWITCH_YEWE) { // decode Tuya/Yewelink status report package + if ((bytes_read == 3) && (serial_buf[bytes_read] != 7)) + { + Plugin_091_commandstate = 0; // command code 7 means status reporting, we do not need other packets + bytes_read = 0; + } + + if (bytes_read == 10) { + if (serial_buf[5] == 5) { + uint8_t btnnum = (serial_buf[6] - 1); + Plugin_091_ostate[btnnum] = Plugin_091_switchstate[btnnum]; + Plugin_091_switchstate[btnnum] = serial_buf[10]; + Plugin_091_commandstate = 2; bytes_read = 0; + + if (Plugin_091_ostate[btnnum] != Plugin_091_switchstate[btnnum]) { + log = F("SerSW : State"); + + switch (btnnum) { + case 0: { + if (Plugin_091_numrelay > 0) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" r0:"), Plugin_091_switchstate[btnnum]); + } + break; + } + case 1: { + if (Plugin_091_numrelay > 1) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" r1:"), Plugin_091_switchstate[btnnum]); + } + break; + } + case 2: { + if (Plugin_091_numrelay > 2) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" r2:"), Plugin_091_switchstate[btnnum]); + } + break; + } + case 3: { + if (Plugin_091_numrelay > 3) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" r3:"), Plugin_091_switchstate[btnnum]); + } + break; + } + } + event->sensorType = Plugin_091_type; + addLogMove(LOG_LEVEL_INFO, log); + sendData(event); + } + } + } // 10th uint8_t end (Tuya switch) + + if (bytes_read == 13) { + if (serial_buf[5] == 8) { + uint8_t btnnum = (serial_buf[6] - 1); + Plugin_091_ostate[btnnum] = Plugin_091_switchstate[btnnum]; + Plugin_091_switchstate[btnnum] = serial_buf[13]; + Plugin_091_commandstate = 2; bytes_read = 0; + + if (Plugin_091_ostate[btnnum] != Plugin_091_switchstate[btnnum]) { + log = F("SerSW : Dimmer"); + + switch (btnnum) { + case 1: { + if (Plugin_091_numrelay > 1) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" d1:"), Plugin_091_switchstate[btnnum]); + } + break; + } + case 2: { + if (Plugin_091_numrelay > 2) { + UserVar.setFloat(event->TaskIndex, btnnum, Plugin_091_switchstate[btnnum]); + log += concat(F(" d2:"), Plugin_091_switchstate[btnnum]); + } + break; + } + } + event->sensorType = Plugin_091_type; + addLogMove(LOG_LEVEL_INFO, log); + sendData(event); + } + } + } // 13th uint8_t end (Tuya dimmer) + } // yewe decode end + } // Plugin_091_commandstate 1 end + } // end of status decoding + + if (Plugin_091_commandstate == 1) { + bytes_read++; + } + } else { + ESPEASY_SERIAL_0.read(); // if buffer full, dump incoming + } + } + } // plugin initialized end + success = true; + break; + } + + case PLUGIN_READ: + { + if (Plugin_091_init) + { + if ((PCONFIG(0) == SER_SWITCH_YEWE) && (Plugin_091_commandstate != 1)) + { // check Tuya state if anybody ask for it + addLog(LOG_LEVEL_INFO, F("SerSW : ReadState")); + getmcustate(); + } + + if (PCONFIG(0) == SER_SWITCH_WIFIDIMMER) { + if (Plugin_091_switchstate[1] < 1) + { + UserVar.setFloat(event->TaskIndex, 0, 0); + } else { + UserVar.setFloat(event->TaskIndex, 0, 1); + } + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + success = true; + } + break; + } + + case PLUGIN_WRITE: + { + String log; + String command = parseString(string, 1); + uint8_t rnum = 0; + uint8_t rcmd = 0; + uint8_t par3 = 0; + + if (Plugin_091_init) + { + if (equals(command, F("relay"))) // deal with relay change command + { + success = true; + + if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { + rnum = event->Par1; + } + + if ((event->Par2 == 0) || (event->Par2 == 1)) { + rcmd = event->Par2; + } + + // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please + event->setTaskIndex(Plugin_091_ownindex); + + if (event->Par2 == 2) { // toggle + rcmd = 1 - UserVar[(event->BaseVarIndex + rnum)]; + } + + if (Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { + par3 = Plugin_091_globalpar1; + } + sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); + + if (Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe + if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed + UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); + + if ((par3 == 1) && (rcmd == 1) && (rnum < 2)) + { // exclusive on mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); + } + + if (par3 == 2) { // simultaneous mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); + } + + if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + event->sensorType = Plugin_091_type; + sendData(event); + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitch r%d:%d"), rnum, rcmd)); + } + } else + + if (equals(command, F("relaypulse"))) + { + success = true; + + if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { + rnum = event->Par1; + } + + if ((event->Par2 == 0) || (event->Par2 == 1)) { + rcmd = event->Par2; + } + + // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please + event->setTaskIndex(Plugin_091_ownindex); + + if (Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { + par3 = Plugin_091_globalpar1; + } + + sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // init state + delay(event->Par3); + sendmcucommand(rnum, !rcmd, Plugin_091_globalpar0, par3); // invert state + + if (Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe + if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed + UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); + + if ((par3 == 1) && (rcmd == 1) && (rnum < 2)) + { // exclusive on mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); + } + + if (par3 == 2) { // simultaneous mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); + } + + if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + event->sensorType = Plugin_091_type; + sendData(event); + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulsed for %d mS"), rnum, rcmd, event->Par3)); + } + } else + + if (equals(command, F("relaylongpulse"))) + { + success = true; + + if ((event->Par1 >= 0) && (event->Par1 < Plugin_091_numrelay)) { + rnum = event->Par1; + } + + if ((event->Par2 == 0) || (event->Par2 == 1)) { + rcmd = event->Par2; + } + + // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please + event->setTaskIndex(Plugin_091_ownindex); + + if (Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { + par3 = Plugin_091_globalpar1; + } + unsigned long timer = event->Par3 * 1000; + + sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // init state + // Scheduler.setPluginTimer(timer, PLUGIN_ID_091, rnum, !rcmd); + Scheduler.setPluginTaskTimer(timer, event->TaskIndex, rnum, !rcmd); + + if (Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe + if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed + UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); + + if ((par3 == 1) && (rcmd == 1) && (rnum < 2)) + { // exclusive on mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); + } + + if (par3 == 2) { // simultaneous mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); + } + + if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + event->sensorType = Plugin_091_type; + sendData(event); + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulse for %d sec"), rnum, rcmd, event->Par3)); + } + } else + if (equals(command, F("ydim"))) // + // deal + // with + // dimmer + // command + { + if (((Plugin_091_globalpar0 == SER_SWITCH_YEWE) && (Plugin_091_numrelay > 1)) || (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER)) { // + // only + // on + // tuya + // dimmer + success = true; + + // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please + event->setTaskIndex(Plugin_091_ownindex); + + sendmcudim(event->Par1, Plugin_091_globalpar0); + + if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { + if (Plugin_091_switchstate[1] < 1) // follow state + { + UserVar.setFloat(event->TaskIndex, 0, 0); + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } else { + UserVar.setFloat(event->TaskIndex, 0, 1); + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + event->sensorType = Plugin_091_type; + sendData(event); + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, concat(F("SerSW : SetDim "), event->Par1)); + } + } else { + SendStatus(event, F("\nYDim not supported")); + } + } + } + + break; + } + + case PLUGIN_TASKTIMER_IN: + { + uint8_t par3 = 0; + + // LoadTaskSettings(Plugin_091_ownindex); // get our own task values please + event->setTaskIndex(Plugin_091_ownindex); + + if (Plugin_091_globalpar0 < SER_SWITCH_LCTECH) { + par3 = Plugin_091_globalpar1; + } + + uint8_t rnum = event->Par1; + uint8_t rcmd = event->Par2; + + sendmcucommand(rnum, rcmd, Plugin_091_globalpar0, par3); // invert state + + if (Plugin_091_globalpar0 > SER_SWITCH_YEWE) { // report state only if not Yewe + if (UserVar[(event->BaseVarIndex + rnum)] != Plugin_091_switchstate[rnum]) { // report only if state is really changed + UserVar.setFloat(event->TaskIndex, rnum, Plugin_091_switchstate[rnum]); + + if ((par3 == 1) && (rcmd == 1) && (rnum < 2)) + { // exclusive on mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, 0); + } + + if (par3 == 2) { // simultaneous mode for Dual + // FIXME TD-er: Is this a valid UserVar index? + UserVar.setFloat(event->TaskIndex, 1 - rnum, Plugin_091_switchstate[1 - rnum]); + } + + if (Plugin_091_globalpar0 == SER_SWITCH_WIFIDIMMER) { + UserVar.setFloat(event->TaskIndex, 1, Plugin_091_switchstate[1]); + } + event->sensorType = Plugin_091_type; + sendData(event); + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, strformat(F("SerSW : SetSwitchPulse r%d:%d Pulse ended"), rnum, rcmd)); + } + + break; + } + } + return success; +} + +void getmcustate() { + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x08, // Tuya command 08 - request status + 0x00, + 0x00, + 0x07 }; + + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); +} + +void sendmcucommand(uint8_t btnnum, uint8_t state, uint8_t swtype, uint8_t btnum_mode) // btnnum=0,1,2, state=0/1 +{ + uint8_t sstate; + + switch (swtype) + { + case SER_SWITCH_YEWE: + { + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x06, // Tuya command 06 - send order + 0x00, + 0x05, // following data length 0x05 + static_cast(btnnum + 1), // relay number 1,2,3 + 0x01, // ? + 0x00, // ? + 0x01, // ? + state, // status + static_cast(13 + btnnum + state) }; // checksum:sum of all bytes in packet mod 256 + + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); + break; + } + case SER_SWITCH_SONOFFDUAL: + { + Plugin_091_switchstate[btnnum] = state; + + if ((btnum_mode == 1) && (state == 1) && (btnnum < 2)) + { // exclusive on mode + Plugin_091_switchstate[(1 - btnnum)] = 0; + } + + if (btnum_mode == 2) + { // simultaneous mode + Plugin_091_switchstate[0] = state; + Plugin_091_switchstate[1] = state; + } + sstate = Plugin_091_switchstate[0] + (Plugin_091_switchstate[1] << 1) + (Plugin_091_switchstate[2] << 2); + const uint8_t msg[] = { + 0xA0, + 0x04, + sstate, + 0xA1 }; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); + break; + } + case SER_SWITCH_LCTECH: + { + uint8_t c_d = 1; + + if (Plugin_091_cmddbl) { + c_d = 2; + } + Plugin_091_switchstate[btnnum] = state; + + for (uint8_t x = 0; x < c_d; ++x) // try twice to be sure + { + if (x > 0) { + delay(1); + } + + if (Plugin_091_ipd) { + const uint8_t msg[] = { + 0x0D, + 0x0A, + 0x2B, + 0x49, + 0x50, + 0x44, + 0x2C, + 0x30, + 0x2C, + 0x34, + 0x3A }; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + } + const uint8_t msg[] = { + 0xA0, + static_cast(0x01 + btnnum), + static_cast(0x00 + state), + static_cast(0xA1 + state + btnnum) }; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); + } + + break; + } + case SER_SWITCH_WIFIDIMMER: + { + if (btnnum == 0) { + if (state == 0) { // off + Plugin_091_switchstate[0] = 0; + + if (Plugin_091_switchstate[1] < 1) { + if (Plugin_091_ostate[1] < 1) { + Plugin_091_ostate[1] = 255; + } + } else { + Plugin_091_ostate[1] = Plugin_091_switchstate[1]; + } + sendmcudim(0, SER_SWITCH_WIFIDIMMER); + } else { // on + Plugin_091_switchstate[0] = 1; + + if (Plugin_091_ostate[1] < 1) { + if (Plugin_091_switchstate[1] > 0) { + sstate = Plugin_091_switchstate[1]; + } else { + sstate = 255; + } + } else { + sstate = Plugin_091_ostate[1]; + } + sendmcudim(sstate, SER_SWITCH_WIFIDIMMER); + } + } + break; + } + } +} + +void sendmcudim(uint8_t dimvalue, uint8_t swtype) +{ + switch (swtype) + { + case SER_SWITCH_YEWE: + { + const uint8_t msg[] = { + 0x55, // Tuya header 55AA + 0xAA, + 0x00, // version 00 + 0x06, // Tuya command 06 - send order + 0x00, + 0x08, // following data length 0x08 + Plugin_091_numrelay, // dimmer order-id? select it at plugin settings 2/3!!! + 0x02, // type=value + 0x00, // length hi + 0x04, // length low + 0x00, // ? + 0x00, // ? + 0x00, // ? + dimvalue, // dim value (0-255) + static_cast(19 + Plugin_091_numrelay + dimvalue) }; // checksum:sum of all bytes in packet mod 256 + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); + break; + } + case SER_SWITCH_WIFIDIMMER: + { + const uint8_t msg[] = { + 0xFF, // Wifidimmer header FF55 + 0x55, + dimvalue, // dim value (0-255) + 0x05, + 0xDC, + 0x0A }; + ESPEASY_SERIAL_0.write(msg, NR_ELEMENTS(msg)); + ESPEASY_SERIAL_0.flush(); + Plugin_091_switchstate[1] = dimvalue; + break; + } + } +} + +#endif // USES_P091 From 00983d7346b3ddc9bd5cf4d4b353a44df4e9f232 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 24 Aug 2024 23:54:23 +0200 Subject: [PATCH 13/48] [ESP-IDF5.3] Fix building on Windows --- .gitignore | 2 ++ platformio.ini | 1 + platformio_core_defs.ini | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3965436a7e..a7d3d15ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ src/CustomIR.h docs/source/Plugin/_plugin_sets_overview.repl *.PVS-Studio.stacktrace.txt + +.platformio/ diff --git a/platformio.ini b/platformio.ini index 2765912768..aa5f95c359 100644 --- a/platformio.ini +++ b/platformio.ini @@ -11,6 +11,7 @@ ; *********************************************************************; [platformio] +core_dir = .platformio description = Firmware for ESP82xx/ESP32/ESP32-S2/ESP32-S3/ESP32-C3 for easy IoT deployment of sensors. boards_dir = boards lib_dir = lib diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 65ae724a6f..6dabada670 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -202,7 +202,7 @@ build_flags = -DESP32_STAGE -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE -DETH_TYPE_JL1101_SUPPORTED - -DPR_9453_FLUSH_TO_CLEAR_REVERTED + ; PR_9453_FLUSH_TO_CLEAR_REVERTED -DPR_9453_FLUSH_TO_CLEAR=flush ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 From e0f6b2916dcebdd3c37e0a4dcccde129ff2d6a30 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 26 Aug 2024 12:06:28 +0200 Subject: [PATCH 14/48] [ESP-IDF 5.x] Fix detection of ESP32-rev3 --- platformio_special_envs.ini | 36 ------------------------ src/src/Helpers/Hardware_device_info.cpp | 35 ++++++++++++++++++----- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/platformio_special_envs.ini b/platformio_special_envs.ini index ec6315afeb..6b2c28ebd0 100644 --- a/platformio_special_envs.ini +++ b/platformio_special_envs.ini @@ -28,29 +28,6 @@ lib_ignore = ${regular_platform.lib_ignore} LittleFS(esp8266) extra_scripts = ${extra_scripts_esp8266.extra_scripts} -[env:spec_debug_custom_IR_ESP8266_4M1M] -extends = esp8266_4M1M -build_type = ${debug_pio.build_type} -check_tool = ${debug_pio.check_tool} -check_flags = ${debug_pio.check_flags} -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} - ${debug_pio.build_flags} - ${esp8266_4M1M.build_flags} - -DPLUGIN_BUILD_CUSTOM -lib_ignore = ESP32_ping - ESP32WebServer - ServoESP32 - ESP32HTTPUpdateServer - adafruit/Adafruit GFX Library@^1.11.1 - LOLIN_EPD - Adafruit ILI9341 - adafruit/Adafruit BusIO - Adafruit NeoPixel - Adafruit Motor Shield V2 Library -extra_scripts = ${extra_scripts_esp8266.extra_scripts} - [env:spec_debug_beta_custom_ESP8266_4M1M] extends = esp8266_4M1M @@ -167,16 +144,3 @@ board_build.filesystem = littlefs ;board = lolin_d32_pro board = esp32_16M8M - - -; Special env for memory analysis -; This may generate builds which cannot be run, so do not upload to a node. -; Has the same lib_ignore as the IR builds, or else those cannot be built for testing -[env:spec_memanalyze_ESP8266] -extends = esp8266_4M1M -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -lib_ignore = ${regular_platform.lib_ignore} -build_flags = ${esp8266_4M1M.build_flags} -DMEMORY_ANALYSIS -DPLUGIN_BUILD_CUSTOM -w -DFEATURE_NON_STANDARD_24_TASKS=1 -DTASKS_MAX=24 -extra_scripts = pre:tools/pio/pre_memanalyze.py - pre:tools/pio/generate-compiletime-defines.py diff --git a/src/src/Helpers/Hardware_device_info.cpp b/src/src/Helpers/Hardware_device_info.cpp index 890a3c5456..2c21db8967 100644 --- a/src/src/Helpers/Hardware_device_info.cpp +++ b/src/src/Helpers/Hardware_device_info.cpp @@ -441,8 +441,11 @@ const __FlashStringHelper* getChipModel() { CHIP_ESP32S3 = 9, //!< ESP32-S3 CHIP_ESP32C3 = 5, //!< ESP32-C3 CHIP_ESP32C2 = 12, //!< ESP32-C2 + CHIP_ESP32C5 = 17, //!< ESP32-C5 beta3 (MPW) + CHIP_ESP32C5 = 23, //!< ESP32-C5 MP CHIP_ESP32C6_b = 7, //!< ESP32-C6(beta) CHIP_ESP32C6 = 13, //!< ESP32-C6 + CHIP_ESP32C61 = 20, //!< ESP32-C61 CHIP_ESP32H2_b1 = 10, //!< ESP32-H2(beta1) CHIP_ESP32H2_b2 = 14, //!< ESP32-H2(beta2) CHIP_ESP32H2 = 16, //!< ESP32-H2 @@ -470,7 +473,12 @@ const __FlashStringHelper* getChipModel() { esp_chip_info(&chip_info); uint32_t chip_model = chip_info.model; - uint32_t chip_revision = chip_info.revision; + const uint32_t chip_revision = +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + chip_info.revision / 100; +#else + chip_info.revision; +#endif uint32_t pkg_version = 0; # if (ESP_IDF_VERSION_MAJOR >= 5) @@ -530,6 +538,8 @@ const __FlashStringHelper* getChipModel() { case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: return F("ESP32-PICO-V3-02"); // Max 240MHz, Dual core, LGA 7*7, 8MB embedded flash, 2MB embedded PSRAM, // ESP32-PICO-MINI-02, ESP32-PICO-DevKitM-2 + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: + return F("ESP32-D0WDR2-V3"); } # endif // if CONFIG_IDF_TARGET_ESP32 return F("ESP32"); @@ -723,6 +733,15 @@ const __FlashStringHelper* getChipModel() { # endif // CONFIG_IDF_TARGET_ESP32P4 return F("ESP32-P4"); } + else if (17 == chip_model) { // ESP32-C5 beta3 (MPW) + return F("ESP32-C5 beta3"); + } + else if (23 == chip_model) { // ESP32-C5 MP + return F("ESP32-C5"); + } + else if (20 == chip_model) { // ESP32-C61 + return F("ESP32-C61"); + } return F("ESP32"); #elif defined(ESP8266) @@ -823,11 +842,7 @@ String getChipRevision() { # endif // if ESP_IDF_VERSION_MAJOR < 5 } #endif // ifdef ESP32 - String res; - res += rev / 100; - res += '.'; - res += rev % 100; - return res; + return strformat(F("%d.%02d"), rev / 100, rev % 100); } uint32_t getSketchSize() { @@ -893,7 +908,13 @@ bool CanUsePSRAM() { esp_chip_info_t chip_info; esp_chip_info(&chip_info); - if ((CHIP_ESP32 == chip_info.model) && (chip_info.revision < 3)) { + if ((CHIP_ESP32 == chip_info.model) && +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + (chip_info.revision < 300) +#else + (chip_info.revision < 3) +#endif + ) { return false; } # if ESP_IDF_VERSION_MAJOR < 4 From 4ccfa5a1ca3b18d53518d9b09aec01ca1f9e68b4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 28 Aug 2024 22:18:31 +0200 Subject: [PATCH 15/48] [EDP-IDF] Update to latest IDF5.1 (SPIFFS) & IDF5.3(LittleFS) Arduino304 --- platformio_core_defs.ini | 4 ++-- platformio_esp32_solo1.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 6dabada670..b9a0ec3ee9 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -169,7 +169,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 [core_esp32_IDF5_1__3_0_2_SPIFFS] platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2826/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2837/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 @@ -195,7 +195,7 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2823/framework-arduinoespressif32-all-release_v5.3-fe1954e5.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2834/framework-arduinoespressif32-all-release_v5.3-fe1954e5.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index fc9170c60f..c324ffd8fd 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -19,7 +19,7 @@ build_unflags = ${esp32_base.build_unflags} [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2824/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2835/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From eb8d7c5d363bb40c28909682c0d6b167faeb38b4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 31 Aug 2024 14:34:53 +0200 Subject: [PATCH 16/48] [ESP-IDF] Simplify eco mode power management --- platformio_core_defs.ini | 2 +- src/src/DataStructs/Modbus.cpp | 10 ++--- src/src/ESPEasyCore/ESPEasy_setup.cpp | 55 ++++-------------------- src/src/Helpers/Hardware.h | 62 ++++++++++++++++++--------- 4 files changed, 55 insertions(+), 74 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index b9a0ec3ee9..fc65ac2d85 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -203,7 +203,7 @@ build_flags = -DESP32_STAGE -DCONFIG_PM_ENABLE -DETH_TYPE_JL1101_SUPPORTED ; PR_9453_FLUSH_TO_CLEAR_REVERTED - -DPR_9453_FLUSH_TO_CLEAR=flush + -DPR_9453_FLUSH_TO_CLEAR=clear ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 diff --git a/src/src/DataStructs/Modbus.cpp b/src/src/DataStructs/Modbus.cpp index 63143aab70..6c2e46ced1 100644 --- a/src/src/DataStructs/Modbus.cpp +++ b/src/src/DataStructs/Modbus.cpp @@ -11,7 +11,7 @@ Modbus::Modbus() : ModbusClient(nullptr), errcnt(0), timeout(0), Modbus::~Modbus() { if (ModbusClient) { - ModbusClient->flush(); + ModbusClient->PR_9453_FLUSH_TO_CLEAR(); ModbusClient->stop(); delete (ModbusClient); delay(1); @@ -26,7 +26,7 @@ bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, incomingValue = type; resultReceived = false; if (ModbusClient) { - ModbusClient->flush(); + ModbusClient->PR_9453_FLUSH_TO_CLEAR(); ModbusClient->stop(); delete (ModbusClient); delay(1); @@ -39,7 +39,7 @@ bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, ModbusClient->setNoDelay(true); ModbusClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); timeout = millis(); - ModbusClient->flush(); + ModbusClient->PR_9453_FLUSH_TO_CLEAR(); if (ModbusClient->connected()) { #ifndef BUILD_NO_DEBUG @@ -87,7 +87,7 @@ bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, if ((incomingValue == signed16) || (incomingValue == unsigned16)) { sendBuffer[11] = 1; } - ModbusClient->flush(); + ModbusClient->PR_9453_FLUSH_TO_CLEAR(); ModbusClient->write(&sendBuffer[0], sizeof(sendBuffer)); #ifndef BUILD_NO_DEBUG @@ -117,7 +117,7 @@ bool Modbus::handle() { // clean up; if (ModbusClient) { - ModbusClient->flush(); + ModbusClient->PR_9453_FLUSH_TO_CLEAR(); ModbusClient->stop(); delete (ModbusClient); delay(1); diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index c682d8798f..8130acc603 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -314,58 +314,19 @@ void ESPEasy_setup() #endif #ifdef ESP32 - if (Settings.EcoPowerMode()) { - // Configure dynamic frequency scaling: - // maximum and minimum frequencies are set in sdkconfig, - // automatic light sleep is enabled if tickless idle support is enabled. -#if ESP_IDF_VERSION_MAJOR < 5 -#if CONFIG_IDF_TARGET_ESP32 - esp_pm_config_esp32_t pm_config = -#elif CONFIG_IDF_TARGET_ESP32S3 - esp_pm_config_esp32s3_t pm_config = -#elif CONFIG_IDF_TARGET_ESP32S2 - esp_pm_config_esp32s2_t pm_config = -#elif CONFIG_IDF_TARGET_ESP32C6 - esp_pm_config_esp32c3_t pm_config = -#elif CONFIG_IDF_TARGET_ESP32C3 - esp_pm_config_esp32c3_t pm_config = -#elif CONFIG_IDF_TARGET_ESP32C2 - esp_pm_config_esp32c2_t pm_config = -#endif - { - .max_freq_mhz = getCPU_MaxFreqMHz(), - - .min_freq_mhz = getCPU_MinFreqMHz(), -#if CONFIG_FREERTOS_USE_TICKLESS_IDLE - .light_sleep_enable = true -#endif - }; -#else - esp_pm_config_t pm_config = { + // Configure dynamic frequency scaling: + // maximum and minimum frequencies are set in sdkconfig, + // automatic light sleep is enabled if tickless idle support is enabled. + ESP_PM_CONFIG_T pm_config = { .max_freq_mhz = getCPU_MaxFreqMHz(), - .min_freq_mhz = 80, + .min_freq_mhz = Settings.EcoPowerMode() ? getCPU_MinFreqMHz() : getCPU_MaxFreqMHz(), #if CONFIG_FREERTOS_USE_TICKLESS_IDLE - .light_sleep_enable = true + .light_sleep_enable = Settings.EcoPowerMode() #else .light_sleep_enable = false #endif - }; -#endif - esp_pm_configure(&pm_config); -#if CONFIG_IDF_TARGET_ESP32 - } else { - // Set the max/min frequency based on what's being reported by the efuses. - // Only ESP32 seems to have this function. - esp_pm_config_esp32_t pm_config = { - .max_freq_mhz = getCPU_MaxFreqMHz(), - .min_freq_mhz = getCPU_MinFreqMHz(), -#if CONFIG_FREERTOS_USE_TICKLESS_IDLE - .light_sleep_enable = false -#endif - }; - esp_pm_configure(&pm_config); -#endif - } + }; + esp_pm_configure(&pm_config); #endif diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index bfff165a25..e4fc0774b9 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -14,10 +14,10 @@ #include "../Helpers/Hardware_defines.h" #if ESP_IDF_VERSION_MAJOR >= 5 -#include -#include +# include +# include -#endif +#endif // if ESP_IDF_VERSION_MAJOR >= 5 /********************************************************************************************\ @@ -26,44 +26,63 @@ void hardwareInit(); -void checkResetFactoryPin(); +void checkResetFactoryPin(); #ifdef ESP8266 extern int lastADCvalue; // Keep track of last ADC value as it cannot be read while WiFi is connecting -int espeasy_analogRead(int pin); +int espeasy_analogRead(int pin); #endif // ifdef ESP8266 float mapADCtoFloat(float float_value, - float adc1, - float adc2, - float out1, - float out2); + float adc1, + float adc2, + float out1, + float out2); #ifdef ESP32 -void initADC(); -float applyADCFactoryCalibration( - float raw_value, - adc_atten_t attenuation); +void initADC(); +float applyADCFactoryCalibration( + float raw_value, + adc_atten_t attenuation); bool hasADC_factory_calibration(); const __FlashStringHelper* getADC_factory_calibration_type(); -float getADC_factory_calibrated_min(adc_atten_t attenuation); -float getADC_factory_calibrated_max(adc_atten_t attenuation); +float getADC_factory_calibrated_min(adc_atten_t attenuation); +float getADC_factory_calibrated_max(adc_atten_t attenuation); int getADC_num_for_gpio(int pin); -int getADC_num_for_gpio(int pin, int& channel); +int getADC_num_for_gpio(int pin, + int& channel); int espeasy_analogRead(int pin, bool readAsTouch = false); -int getCPU_MaxFreqMHz(); -int getCPU_MinFreqMHz(); +int getCPU_MaxFreqMHz(); +int getCPU_MinFreqMHz(); + +# if ESP_IDF_VERSION_MAJOR < 5 +# if CONFIG_IDF_TARGET_ESP32 +# define ESP_PM_CONFIG_T esp_pm_config_esp32_t +# elif CONFIG_IDF_TARGET_ESP32S3 +# define ESP_PM_CONFIG_T esp_pm_config_esp32s3_t +# elif CONFIG_IDF_TARGET_ESP32S2 +# define ESP_PM_CONFIG_T esp_pm_config_esp32s2_t +# elif CONFIG_IDF_TARGET_ESP32C6 +# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t +# elif CONFIG_IDF_TARGET_ESP32C3 +# define ESP_PM_CONFIG_T esp_pm_config_esp32c3_t +# elif CONFIG_IDF_TARGET_ESP32C2 +# define ESP_PM_CONFIG_T esp_pm_config_esp32c2_t +# endif // if CONFIG_IDF_TARGET_ESP32 +# else // if ESP_IDF_VERSION_MAJOR < 5 +# define ESP_PM_CONFIG_T esp_pm_config_t +# endif // if ESP_IDF_VERSION_MAJOR < 5 -#endif // ifdef ESP32 +#endif // ifdef ESP32 /*********************************************************************************************\ @@ -74,9 +93,10 @@ int getCPU_MinFreqMHz(); // Based on code from https://raw.githubusercontent.com/espressif/esp-idf/master/components/esp32/hw_random.c uint32_t HwRandom(); -long HwRandom(long howbig); +long HwRandom(long howbig); -long HwRandom(long howsmall, long howbig); +long HwRandom(long howsmall, + long howbig); /********************************************************************************************\ From e373e31aa5d9edb3dbf22ac49abf8f387d179871 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 1 Sep 2024 18:18:52 +0200 Subject: [PATCH 17/48] [ESP-IDF5.3] Update to latest IDF5.3/Arduino3.0.5 --- platformio_core_defs.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index fc65ac2d85..95aa30e1da 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -195,7 +195,7 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2834/framework-arduinoespressif32-all-release_v5.3-fe1954e5.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2861/framework-arduinoespressif32-all-release_v5.3-40822c72.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 From 3ee5a7ea12fc9b1f849b93f688dc2a3217f55d3f Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 2 Sep 2024 23:43:12 +0200 Subject: [PATCH 18/48] [ESP-IDF] Revert IDF5.3 to IDF5.1 due to WiFi scan issues --- platformio_core_defs.ini | 8 ++++++-- platformio_esp32_solo1.ini | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 95aa30e1da..8d41c43c1b 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -194,8 +194,12 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] -platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2861/framework-arduinoespressif32-all-release_v5.3-40822c72.zip +;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2861/framework-arduinoespressif32-all-release_v5.3-40822c72.zip +;platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10+rc1/platform-espressif32.zip +;platform_packages = +platform = https://github.com/Jason2866/platform-espressif32.git +platform_packages = framework-arduinoespressif32 @https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2858/framework-arduinoespressif32-all-release_v5.1-147836c.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index c324ffd8fd..3e88830187 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -18,8 +18,10 @@ build_unflags = ${esp32_base.build_unflags} ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 -platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2835/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip +;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2835/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip +platform = https://github.com/Jason2866/platform-espressif32.git +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2859/framework-arduinoespressif32-solo1-release_v5.1-147836c.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 8b514e2b39cc0575206db2d923b133c1363d63b9 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 8 Sep 2024 11:28:35 +0200 Subject: [PATCH 19/48] [WiFi] Add copy constructor to WiFi_AP_Candidate Hopefully fixing ESP8266 not able to connect to hidden AP --- src/src/DataStructs/WiFi_AP_Candidate.cpp | 23 +++++++++++++++++++++++ src/src/DataStructs/WiFi_AP_Candidate.h | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/src/DataStructs/WiFi_AP_Candidate.cpp b/src/src/DataStructs/WiFi_AP_Candidate.cpp index ddb96812cc..2ed36251d8 100644 --- a/src/src/DataStructs/WiFi_AP_Candidate.cpp +++ b/src/src/DataStructs/WiFi_AP_Candidate.cpp @@ -35,6 +35,17 @@ country({ memset(&bits, 0, sizeof(bits)); } +WiFi_AP_Candidate::WiFi_AP_Candidate(const WiFi_AP_Candidate& other) +{ + // All members other than ssid can be mem-copied + memcpy(this, &other, sizeof(WiFi_AP_Candidate)); + + // Make sure ssid isn't some kind of valid String with heap allocated memory + memset(&ssid, 0, sizeof(ssid)); + // Now copy the ssid String + ssid = other.ssid; +} + WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t index_c, const String& ssid_c) : last_seen(0), rssi(0), channel(0), index(index_c), enc_type(0) { @@ -157,6 +168,18 @@ bool WiFi_AP_Candidate::operator<(const WiFi_AP_Candidate& other) const { return rssi > other.rssi; } +WiFi_AP_Candidate& WiFi_AP_Candidate::operator=(const WiFi_AP_Candidate& other) +{ + // All members other than ssid can be mem-copied + memcpy(this, &other, sizeof(WiFi_AP_Candidate)); + + // Make sure ssid isn't some kind of valid String with heap allocated memory + memset(&ssid, 0, sizeof(ssid)); + // Now copy the ssid String + ssid = other.ssid; + return *this; +} + bool WiFi_AP_Candidate::usable() const { // Allow for empty pass // if (key.isEmpty()) return false; diff --git a/src/src/DataStructs/WiFi_AP_Candidate.h b/src/src/DataStructs/WiFi_AP_Candidate.h index bec19eeb9a..82766ca510 100644 --- a/src/src/DataStructs/WiFi_AP_Candidate.h +++ b/src/src/DataStructs/WiFi_AP_Candidate.h @@ -6,7 +6,7 @@ #include "../DataStructs/MAC_address.h" struct WiFi_AP_Candidate { WiFi_AP_Candidate(); - WiFi_AP_Candidate(const WiFi_AP_Candidate& other) = default; + WiFi_AP_Candidate(const WiFi_AP_Candidate& other); // Construct from stored credentials @@ -33,7 +33,7 @@ struct WiFi_AP_Candidate { return bssid_match(other.bssid) && ssid.equals(other.ssid); // && key.equals(other.key); } - WiFi_AP_Candidate& operator=(const WiFi_AP_Candidate& other) = default; + WiFi_AP_Candidate& operator=(const WiFi_AP_Candidate& other); // Check if the candidate data can be used to actually connect to an AP. bool usable() const; From 231e4f30f9cc2ca7f1e55505409c987a5d2b437c Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 8 Sep 2024 11:29:20 +0200 Subject: [PATCH 20/48] [Cleanup] simplify ExtraTaskSettings constructor --- .../DataStructs/ExtraTaskSettingsStruct.cpp | 13 ++++++++- src/src/DataStructs/ExtraTaskSettingsStruct.h | 28 +++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp index 3c42d964d1..c6a0db6b06 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.cpp +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.cpp @@ -11,12 +11,23 @@ #define EXTRA_TASK_SETTINGS_VERSION 1 +ExtraTaskSettingsStruct::ExtraTaskSettingsStruct() +{ + memset(this, 0, sizeof(ExtraTaskSettingsStruct)); + TaskIndex = INVALID_TASK_INDEX; + version = EXTRA_TASK_SETTINGS_VERSION; + for (int i = 0; i < VARS_PER_TASK; ++i) { + TaskDeviceValueDecimals[i] = 2; + TaskDeviceErrorValue[i] = NAN; + } +} + void ExtraTaskSettingsStruct::clear() { // Need to make sure every byte between the members is also zero // Otherwise the checksum will fail and settings will be saved too often. memset(this, 0, sizeof(ExtraTaskSettingsStruct)); TaskIndex = INVALID_TASK_INDEX; - dummy1 = 0; + //dummy1 = 0; version = EXTRA_TASK_SETTINGS_VERSION; for (int i = 0; i < VARS_PER_TASK; ++i) { TaskDeviceValueDecimals[i] = 2; diff --git a/src/src/DataStructs/ExtraTaskSettingsStruct.h b/src/src/DataStructs/ExtraTaskSettingsStruct.h index 8887022d41..2e873c902a 100644 --- a/src/src/DataStructs/ExtraTaskSettingsStruct.h +++ b/src/src/DataStructs/ExtraTaskSettingsStruct.h @@ -20,7 +20,7 @@ // FIXME @TD-er: Should think of another mechanism to make this more efficient. struct ExtraTaskSettingsStruct { - ExtraTaskSettingsStruct() = default; + ExtraTaskSettingsStruct(); void clear(); @@ -78,19 +78,19 @@ struct ExtraTaskSettingsStruct uint8_t defaultDecimals, bool displayString); - taskIndex_t TaskIndex = INVALID_TASK_INDEX; // Always < TASKS_MAX or INVALID_TASK_INDEX - char TaskDeviceName[NAME_FORMULA_LENGTH_MAX + 1]{}; - char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]{}; - char TaskDeviceValueNames[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]{}; - uint8_t dummy1 = 0; - uint8_t version = 1; - long TaskDevicePluginConfigLong[PLUGIN_EXTRACONFIGVAR_MAX]{}; - uint8_t TaskDeviceValueDecimals[VARS_PER_TASK]{}; - int16_t TaskDevicePluginConfig[PLUGIN_EXTRACONFIGVAR_MAX]{}; - float TaskDeviceMinValue[VARS_PER_TASK]{}; - float TaskDeviceMaxValue[VARS_PER_TASK]{}; - float TaskDeviceErrorValue[VARS_PER_TASK]{}; - uint32_t VariousBits[VARS_PER_TASK]{}; + taskIndex_t TaskIndex; // Always < TASKS_MAX or INVALID_TASK_INDEX + char TaskDeviceName[NAME_FORMULA_LENGTH_MAX + 1]; + char TaskDeviceFormula[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; + char TaskDeviceValueNames[VARS_PER_TASK][NAME_FORMULA_LENGTH_MAX + 1]; + uint8_t dummy1; + uint8_t version; + long TaskDevicePluginConfigLong[PLUGIN_EXTRACONFIGVAR_MAX]; + uint8_t TaskDeviceValueDecimals[VARS_PER_TASK]; + int16_t TaskDevicePluginConfig[PLUGIN_EXTRACONFIGVAR_MAX]; + float TaskDeviceMinValue[VARS_PER_TASK]; + float TaskDeviceMaxValue[VARS_PER_TASK]; + float TaskDeviceErrorValue[VARS_PER_TASK]; + uint32_t VariousBits[VARS_PER_TASK]; }; From b2fc44465a4c2aeed3db1c44ba23c1561752f489 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 14 Sep 2024 23:43:54 +0200 Subject: [PATCH 21/48] [ESP-IDF5.3] Switch back to IDF5.3 for LittleFS builds --- platformio_core_defs.ini | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 8d41c43c1b..8c589364e7 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -194,12 +194,8 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] -;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2861/framework-arduinoespressif32-all-release_v5.3-40822c72.zip -;platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10+rc1/platform-espressif32.zip -;platform_packages = -platform = https://github.com/Jason2866/platform-espressif32.git -platform_packages = framework-arduinoespressif32 @https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2858/framework-arduinoespressif32-all-release_v5.1-147836c.zip +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2953/framework-arduinoespressif32-all-release_v5.3-b2dba612.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 From 88cb1bcfbfb376306d03617b964da55806b68ed9 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 14 Sep 2024 23:45:05 +0200 Subject: [PATCH 22/48] [N001_Email] Increase parse buffer size + cleanup --- src/_N001_Email.cpp | 161 +++++++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 71 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index bebb3b4d29..3b63be66b2 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -47,7 +47,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, bool NPlugin_001_Auth(WiFiClient & client, const String& user, const String& pass, - uint16_t timeout); + uint16_t timeout); bool NPlugin_001_MTA(WiFiClient & client, const String& aStr, uint16_t aWaitForPattern, @@ -142,6 +142,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat( F("Email: Connecting to %s:%d"), @@ -172,7 +173,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co ); uint16_t clientTimeout = notificationsettings.Timeout * 1000; // Convert to mS. - if (clientTimeout < NPLUGIN_001_MIN_TM || clientTimeout > NPLUGIN_001_MAX_TM) { + + if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; } @@ -193,39 +195,44 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co // Use Notify Command's destination email address(s) if provided in Command rules. // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
How are you?
Have a good day.
" - String subAddr = ""; - String tmp_ato = ""; - int pos_brace1 = aMesg.indexOf('{'); - int pos_amper = aMesg.indexOf('@'); - int pos_brace2 = aMesg.indexOf('}'); - if(pos_brace1 == 0 && pos_amper > pos_brace1 && pos_brace2 > pos_amper) { - subAddr = aMesg.substring(pos_brace1+1, pos_brace2); - subAddr.trim(); - tmp_ato = subAddr; + String subAddr; + String tmp_ato; + int pos_brace1 = aMesg.indexOf('{'); + int pos_amper = aMesg.indexOf('@'); + int pos_brace2 = aMesg.indexOf('}'); + + if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { + subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); + subAddr.trim(); + tmp_ato = subAddr; # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); - } - # endif - String subMsg = aMesg.substring(pos_brace2+1); // Remove substitute email address from subject line. + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. + subMsg.trim(); + + if (subMsg.indexOf(',') == 0) { + subMsg = subMsg.substring(1); // Remove leading comma. subMsg.trim(); - if(subMsg.indexOf(',') == 0) { - subMsg = subMsg.substring(1); // Remove leading comma. - subMsg.trim(); - } - if(!subMsg.length()) { - subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; - } + } + + if (!subMsg.length()) { + subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; + } # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); - } - # endif - aMesg = subMsg; + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + aMesg = subMsg; } else { - tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. + tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. } // Clean up receiver address. @@ -252,73 +259,83 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout/1000)); - # endif + addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); + # endif // ifndef BUILD_NO_DEBUG while (true) { if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); } - # endif + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } if (!failFlag) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); - } - # endif - if (!NPlugin_001_MTA(client, strformat(F("EHLO %s"), notificationsettings.Domain), 250, clientTimeout)) { + addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); + + if (!NPlugin_001_MTA( + client, + astr, + 250, + clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; } } - // Must retrieve SMTP Reply Packet. Data not used, ignored. + // Must retrieve SMTP Reply Packet. Data not used, ignored. if (!failFlag) { - unsigned long timeout = millis(); + const unsigned long timer = millis() + clientTimeout; String replyStr; - String catStr = ""; - while (client.available()) { - if (millis() > timeout + clientTimeout) { + String catStr; + + bool done = false; + + while (client.available() && !done) { + if (timeOutReached(timer)) { failFlag = true; break; } - safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ, clientTimeout); + done = safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ); catStr += replyStr; } - if(!catStr.length()) { - catStr = "Empty!"; + if (!catStr.length()) { + catStr = F("Empty!"); } # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Packet Rcvd is: > %s <"),catStr.c_str())); + String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); + addLogMove(LOG_LEVEL_DEBUG, log); } - # endif + # endif // ifndef BUILD_NO_DEBUG } if (!failFlag) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); } - # endif + # endif // ifndef BUILD_NO_DEBUG + if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); - } - # endif + + addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } @@ -327,25 +344,26 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if (!failFlag) { # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); - # endif - if (!NPlugin_001_MTA(client, strformat(F("MAIL FROM:<%s>"), email_address.c_str()), 250, clientTimeout)) { + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); + + if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } } if (!failFlag) { - bool nextAddressAvailable = true; - int i = 0; + bool nextAddressAvailable = true; + int i = 0; String emailTo; const String receiver(tmp_ato); - addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"),receiver.c_str())); + addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"), receiver.c_str())); if (!getNextMailAddress(receiver, emailTo, i)) { addLog(LOG_LEVEL_ERROR, F("Email: Receiver missing!")); @@ -353,9 +371,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } while (nextAddressAvailable) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); + addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); } if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } @@ -416,13 +433,13 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass if (mta1 && mta2 && mta3) { addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); } - return (mta1 && mta2 && mta3); - + return mta1 && mta2 && mta3; } bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, aStr); } @@ -438,6 +455,7 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa backgroundtasks(); const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern); + while (true) { if (timeOutReached(timer)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { @@ -450,13 +468,14 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa delay(0); String line; - safeReadStringUntil(client, line, '\n', NPLUGIN_001_PKT_SZ, timeout); + safeReadStringUntil(client, line, '\n', 1024, timeout); - line.replace("-", " "); // Must Remove optional dash from MTA response code. + line.replace("-", " "); // Must Remove optional dash from MTA response code. const bool patternFound = line.indexOf(aWaitForPattern_str) >= 0; # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, line); } From 8af2923d171794476878971a3e22431b5d4032b8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 11:16:00 +0200 Subject: [PATCH 23/48] [ESP-IDF5.3] Fix build solo1 builds --- platformio_esp32_solo1.ini | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index 3e88830187..3e1a1f2e17 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -18,10 +18,8 @@ build_unflags = ${esp32_base.build_unflags} ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 -;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2835/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip -platform = https://github.com/Jason2866/platform-espressif32.git -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2859/framework-arduinoespressif32-solo1-release_v5.1-147836c.zip +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2954/framework-arduinoespressif32-solo1-release_v5.3-b2dba612.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 9b0fa9e53c73efc8044cb4ff654e2bd4cdd4aa1a Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 17:23:19 +0200 Subject: [PATCH 24/48] [Email] Set timeout on the connection itself --- src/_N001_Email.cpp | 8 +- .../DataStructs/NotificationSettingsStruct.h | 2 +- src/src/WebServer/NotificationPage.cpp | 89 ++++++++++++------- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 3b63be66b2..f23e53433f 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -134,11 +134,11 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 - client.setTimeout((CONTROLLER_CLIENTTIMEOUT_MAX + 500) / 1000); // in seconds!!!! + client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! Client *pClient = &client; - pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); + pClient->setTimeout(notificationsettings.Timeout_ms); # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - client.setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); // in msec as it should be! + client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS # ifndef BUILD_NO_DEBUG @@ -172,7 +172,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" ); - uint16_t clientTimeout = notificationsettings.Timeout * 1000; // Convert to mS. + uint16_t clientTimeout = notificationsettings.Timeout_ms; if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; diff --git a/src/src/DataStructs/NotificationSettingsStruct.h b/src/src/DataStructs/NotificationSettingsStruct.h index f4f23fc852..58827fcc70 100644 --- a/src/src/DataStructs/NotificationSettingsStruct.h +++ b/src/src/DataStructs/NotificationSettingsStruct.h @@ -31,7 +31,7 @@ struct NotificationSettingsStruct int8_t Pin2; char User[49]; char Pass[33]; - unsigned int Timeout; + unsigned int Timeout_ms; //its safe to extend this struct, up to 4096 bytes, default values in config are 0 }; diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index aa04973dc7..e76cc74948 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -12,20 +12,19 @@ * 2024-07-01 ThomasB : Start of changelog, older changes not logged. */ -#include "../WebServer/ESPEasy_WebServer.h" -#include "../WebServer/HTML_wrappers.h" -#include "../WebServer/Markup.h" -#include "../WebServer/Markup_Buttons.h" -#include "../WebServer/Markup_Forms.h" +# include "../WebServer/ESPEasy_WebServer.h" +# include "../WebServer/HTML_wrappers.h" +# include "../WebServer/Markup.h" +# include "../WebServer/Markup_Buttons.h" +# include "../WebServer/Markup_Forms.h" -#include "../DataStructs/ESPEasy_EventStruct.h" -#include "../DataStructs/NotificationSettingsStruct.h" +# include "../DataStructs/ESPEasy_EventStruct.h" +# include "../DataStructs/NotificationSettingsStruct.h" -#include "../Helpers/ESPEasy_Storage.h" - -#include "../Globals/ESPEasy_Scheduler.h" -#include "../Globals/Settings.h" +# include "../Helpers/ESPEasy_Storage.h" +# include "../Globals/ESPEasy_Scheduler.h" +# include "../Globals/Settings.h" // ******************************************************************************** @@ -33,13 +32,13 @@ // ******************************************************************************** -#include "../Globals/NPlugins.h" +# include "../Globals/NPlugins.h" void handle_notifications() { - #ifndef BUILD_NO_RAM_TRACKER + # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("handle_notifications")); - #endif + # endif // ifndef BUILD_NO_RAM_TRACKER if (!isLoggedIn()) { return; } navMenuIndex = MENU_INDEX_NOTIFICATIONS; @@ -52,19 +51,19 @@ void handle_notifications() { // 'index' value in the URL - uint8_t notificationindex = getFormItemInt(F("index"), 0); + uint8_t notificationindex = getFormItemInt(F("index"), 0); boolean notificationindexNotSet = notificationindex == 0; --notificationindex; const int notification_webarg_value = getFormItemInt(F("notification"), -1); - if (!notificationindexNotSet && notification_webarg_value != -1) + if (!notificationindexNotSet && (notification_webarg_value != -1)) { const npluginID_t notification = npluginID_t::toPluginID(notification_webarg_value); + if (notification == INVALID_N_PLUGIN_ID) { - Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; + Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; Settings.NotificationEnabled[notificationindex] = false; - } else { MakeNotificationSettings(NotificationSettings); @@ -82,8 +81,10 @@ void handle_notifications() { String dummyString; NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString); } - NotificationSettings.Port = getFormItemInt(F("port"), 0); - NotificationSettings.Timeout = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM/1000); + NotificationSettings.Port = getFormItemInt(F("port"), 0); + + // FIXME TD-er: Must convert this to msec in the user interface as every other timeout in ESPEasy is in msec. + NotificationSettings.Timeout_ms = 1000 * getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM / 1000); NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1); NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1); Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled")); @@ -97,7 +98,8 @@ void handle_notifications() { strncpy_webserver_arg(NotificationSettings.Body, F("body")); } } - addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), sizeof(NotificationSettingsStruct))); + addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), + sizeof(NotificationSettingsStruct))); } // Save the settings. @@ -123,7 +125,7 @@ void handle_notifications() { { html_table_class_multirow(); html_TR(); - html_table_header(F(""), 70); + html_table_header(F(""), 70); html_table_header(F("Nr"), 50); html_table_header(F("Enabled"), 100); html_table_header(F("Service")); @@ -150,8 +152,8 @@ void handle_notifications() { addEnabled(Settings.NotificationEnabled[x]); html_TD(); - uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); - String NotificationName = F("(plugin not found?)"); + uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); + String NotificationName = F("(plugin not found?)"); if (validNProtocolIndex(NotificationProtocolIndex)) { @@ -161,20 +163,21 @@ void handle_notifications() { html_TD(); addHtml(NotificationSettings.Server); html_TD(); - if (NotificationSettings.Port){ + + if (NotificationSettings.Port) { addHtmlInt(NotificationSettings.Port); } else { - //MFD: we display the GPIO + // MFD: we display the GPIO addGpioHtml(NotificationSettings.Pin1); - if (NotificationSettings.Pin2>=0) + if (NotificationSettings.Pin2 >= 0) { html_BR(); addGpioHtml(NotificationSettings.Pin2); } } } - else{ + else { html_TD(3); } } @@ -217,9 +220,33 @@ void handle_notifications() { { addFormTextBox(F("Domain"), F("domain"), NotificationSettings.Domain, sizeof(NotificationSettings.Domain) - 1); addFormTextBox(F("Server"), F("server"), NotificationSettings.Server, sizeof(NotificationSettings.Server) - 1); - addFormNumericBox(F("Port"), F("port"), NotificationSettings.Port, 1, 65535, F("NOTE: SSL/TLS servers NOT supported!")); - if (NotificationSettings.TimeoutNPLUGIN_001_MAX_TM/1000) {NotificationSettings.Timeout=NPLUGIN_001_DEF_TM/1000;} - addFormNumericBox(F("Timeout"), F("timeout"), NotificationSettings.Timeout, NPLUGIN_001_MIN_TM/1000, NPLUGIN_001_MAX_TM/1000, F("Maximum Server Response Time)")); + addFormNumericBox( + F("Port"), F("port"), + NotificationSettings.Port, + 1, + 65535 +# if FEATURE_TOOLTIPS + , F("NOTE: SSL/TLS servers NOT supported!") +# endif // if FEATURE_TOOLTIPS + ); + + if ((NotificationSettings.Timeout_ms < NPLUGIN_001_MIN_TM) || + (NotificationSettings.Timeout_ms > NPLUGIN_001_MAX_TM)) + { + NotificationSettings.Timeout_ms = NPLUGIN_001_DEF_TM; + } + + // FIXME TD-er: Must convert to msec as every other timeout used/configured in ESPEasy is in msec + addFormNumericBox( + F("Timeout"), F("timeout"), + NotificationSettings.Timeout_ms / 1000, + NPLUGIN_001_MIN_TM / 1000, + NPLUGIN_001_MAX_TM / 1000 +# if FEATURE_TOOLTIPS + , F("Maximum Server Response Time") +# endif // if FEATURE_TOOLTIPS + ); + addUnit(F("Seconds")); addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1); From 0d81a41dfc894e1a7a7b17347a25ca5656839faf Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 17:24:41 +0200 Subject: [PATCH 25/48] Do not apply nr. of decimals before calculating formula (#5123) Fixes: #5123 --- src/src/DataStructs/UserVarStruct.cpp | 9 +++++++-- src/src/DataTypes/TaskValues_Data.cpp | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp index 1c97650ada..671dfd5181 100644 --- a/src/src/DataStructs/UserVarStruct.cpp +++ b/src/src/DataStructs/UserVarStruct.cpp @@ -379,7 +379,10 @@ const TaskValues_Data_t * UserVarStruct::getRawOrComputed( if ((it == _computed.end()) || !it->second.isSet(varNr)) { // Try to compute values which do have a formula but not yet a 'computed' value cached. // FIXME TD-er: This may yield unexpected results when formula contains references to %pvalue% - const int nrDecimals = Cache.getTaskDeviceValueDecimals(taskIndex, varNr); + + + // Should not apply set nr. of decimals when calculating a formula + const uint8_t nrDecimals = 255; const String value = getAsString(taskIndex, varNr, sensorType, nrDecimals, true); constexpr bool applyNow = true; @@ -477,7 +480,9 @@ bool UserVarStruct::applyFormulaAndSet(taskIndex_t taskIndex TaskValues_Data_t tmp; tmp.set(varNr, value, sensorType); - const uint8_t nrDecimals = Cache.getTaskDeviceValueDecimals(taskIndex, varNr); + + // Should not apply set nr. of decimals when calculating a formula + const uint8_t nrDecimals = 255; const String value_str = tmp.getAsString(varNr, sensorType, nrDecimals); constexpr bool applyNow = false; diff --git a/src/src/DataTypes/TaskValues_Data.cpp b/src/src/DataTypes/TaskValues_Data.cpp index 961b630b40..ae0a6c7b91 100644 --- a/src/src/DataTypes/TaskValues_Data.cpp +++ b/src/src/DataTypes/TaskValues_Data.cpp @@ -253,11 +253,19 @@ String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, u String result; if (isFloatOutputDataType(sensorType)) { - result = toString(getFloat(varNr), nrDecimals); + const float value = getFloat(varNr); + if (nrDecimals = 255) { + nrDecimals = maxNrDecimals_fpType(value); + } + result = toString(value, nrDecimals); #if FEATURE_EXTENDED_TASK_VALUE_TYPES #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } else if (isDoubleOutputDataType(sensorType)) { - result = doubleToString(getDouble(varNr), nrDecimals); + const double value = getDouble(varNr); + if (nrDecimals = 255) { + nrDecimals = maxNrDecimals_fpType(value); + } + result = doubleToString(value, nrDecimals); #endif #endif } else if (sensorType == Sensor_VType::SENSOR_TYPE_ULONG) { From cffe50091341f4c1a905026054063460d1e58175 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 19:40:50 +0200 Subject: [PATCH 26/48] [GPIO] Revert some changes regarding send bootstate on P001 --- src/src/Helpers/_Plugin_Helper_GPIO.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/src/Helpers/_Plugin_Helper_GPIO.cpp b/src/src/Helpers/_Plugin_Helper_GPIO.cpp index 7624ed121f..2093b1903f 100644 --- a/src/src/Helpers/_Plugin_Helper_GPIO.cpp +++ b/src/src/Helpers/_Plugin_Helper_GPIO.cpp @@ -68,14 +68,6 @@ bool GPIO_plugin_helper_data_t::init( newStatus.task++; } - - // @giig1967g-20181022: set initial UserVar of the switch - if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { - UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); - } else { - UserVar.setFloat(event->TaskIndex, 0, newStatus.state); - } - // if boot pinState must be send, inverse default pinState // this is done to force the trigger in PLUGIN_TEN_PER_SECOND if (_sendBootState) { @@ -90,6 +82,14 @@ bool GPIO_plugin_helper_data_t::init( // setPinState(PLUGIN_ID_009, _mcpPin, PIN_MODE_INPUT, switchstate[event->TaskIndex]); savePortStatus(_portStatus_key, newStatus); + + // @giig1967g-20181022: set initial UserVar of the switch + if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { + UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); + } else { + UserVar.setFloat(event->TaskIndex, 0, newStatus.state); + } + return true; } From 6bbc83066837268ab0a8d7947b0d8e368b0fb5d0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 20:35:56 +0200 Subject: [PATCH 27/48] Add .platformio to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3965436a7e..a7d3d15ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ src/CustomIR.h docs/source/Plugin/_plugin_sets_overview.repl *.PVS-Studio.stacktrace.txt + +.platformio/ From b5a7c901f6edae106b52fe41acd447419a958a53 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 19:40:50 +0200 Subject: [PATCH 28/48] [GPIO] Revert some changes regarding send bootstate on P001 --- src/src/Helpers/_Plugin_Helper_GPIO.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/src/Helpers/_Plugin_Helper_GPIO.cpp b/src/src/Helpers/_Plugin_Helper_GPIO.cpp index 7624ed121f..2093b1903f 100644 --- a/src/src/Helpers/_Plugin_Helper_GPIO.cpp +++ b/src/src/Helpers/_Plugin_Helper_GPIO.cpp @@ -68,14 +68,6 @@ bool GPIO_plugin_helper_data_t::init( newStatus.task++; } - - // @giig1967g-20181022: set initial UserVar of the switch - if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { - UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); - } else { - UserVar.setFloat(event->TaskIndex, 0, newStatus.state); - } - // if boot pinState must be send, inverse default pinState // this is done to force the trigger in PLUGIN_TEN_PER_SECOND if (_sendBootState) { @@ -90,6 +82,14 @@ bool GPIO_plugin_helper_data_t::init( // setPinState(PLUGIN_ID_009, _mcpPin, PIN_MODE_INPUT, switchstate[event->TaskIndex]); savePortStatus(_portStatus_key, newStatus); + + // @giig1967g-20181022: set initial UserVar of the switch + if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { + UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); + } else { + UserVar.setFloat(event->TaskIndex, 0, newStatus.state); + } + return true; } From 02415920530984928bc35ccc24f94c05430cb554 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 16 Sep 2024 00:49:01 +0200 Subject: [PATCH 29/48] Fix formatting nr. decimals with formula (#5123) --- src/src/DataStructs/UserVarStruct.cpp | 4 ++-- src/src/DataTypes/TaskValues_Data.cpp | 4 ++-- src/src/Globals/Plugins.cpp | 2 ++ src/src/WebServer/JSON.cpp | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp index 671dfd5181..1552a4c6ad 100644 --- a/src/src/DataStructs/UserVarStruct.cpp +++ b/src/src/DataStructs/UserVarStruct.cpp @@ -382,7 +382,7 @@ const TaskValues_Data_t * UserVarStruct::getRawOrComputed( // Should not apply set nr. of decimals when calculating a formula - const uint8_t nrDecimals = 255; + const uint8_t nrDecimals = 254; const String value = getAsString(taskIndex, varNr, sensorType, nrDecimals, true); constexpr bool applyNow = true; @@ -482,7 +482,7 @@ bool UserVarStruct::applyFormulaAndSet(taskIndex_t taskIndex tmp.set(varNr, value, sensorType); // Should not apply set nr. of decimals when calculating a formula - const uint8_t nrDecimals = 255; + const uint8_t nrDecimals = 254; const String value_str = tmp.getAsString(varNr, sensorType, nrDecimals); constexpr bool applyNow = false; diff --git a/src/src/DataTypes/TaskValues_Data.cpp b/src/src/DataTypes/TaskValues_Data.cpp index ae0a6c7b91..5b3017e994 100644 --- a/src/src/DataTypes/TaskValues_Data.cpp +++ b/src/src/DataTypes/TaskValues_Data.cpp @@ -254,7 +254,7 @@ String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, u if (isFloatOutputDataType(sensorType)) { const float value = getFloat(varNr); - if (nrDecimals = 255) { + if (nrDecimals == 254) { // FIXME TD-er: Must use defines for these special situations nrDecimals = maxNrDecimals_fpType(value); } result = toString(value, nrDecimals); @@ -262,7 +262,7 @@ String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, u #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } else if (isDoubleOutputDataType(sensorType)) { const double value = getDouble(varNr); - if (nrDecimals = 255) { + if (nrDecimals == 254) { // FIXME TD-er: Must use defines for these special situations nrDecimals = maxNrDecimals_fpType(value); } result = doubleToString(value, nrDecimals); diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index 956b38d56a..db208e272f 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -784,6 +784,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) } if (Function == PLUGIN_INIT) { + clearTaskCache(event->TaskIndex); UserVar.clear_computed(event->TaskIndex); } } @@ -938,6 +939,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) if (Function == PLUGIN_EXIT) { UserVar.clear_computed(event->TaskIndex); clearPluginTaskData(event->TaskIndex); + clearTaskCache(event->TaskIndex); // initSerial(); queueTaskEvent(F("TaskExit"), event->TaskIndex, retval); diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index e510094ef1..e1df8668b6 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -452,8 +452,8 @@ void handle_json() for (uint8_t x = 0; x < valueCount; x++) { addHtml('{'); - const String value = formatUserVarNoCheck(&TempEvent, x); uint8_t nrDecimals = Cache.getTaskDeviceValueDecimals(TaskIndex, x); + const String value = formatUserVarNoCheck(&TempEvent, x); if (mustConsiderAsJSONString(value)) { // Flag as not to treat as a float From 762b94abbe83d3e67dd5659cae4d285be562f0b1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 18 Sep 2024 00:41:12 +0200 Subject: [PATCH 30/48] [Email] Early exit on failing Auth calls + some cleanup --- src/_N001_Email.cpp | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index f23e53433f..9f9a353d97 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -151,7 +151,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } # endif // ifndef BUILD_NO_DEBUG - if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, CONTROLLER_CLIENTTIMEOUT_DFLT)) { + if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, notificationsettings.Timeout_ms)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { addLog(LOG_LEVEL_ERROR, strformat( F("Email: Error connecting to %s:%d"), @@ -161,17 +161,6 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co myStatus = false; failFlag = true; } else { - String mailheader = F( - "From: $nodename <$emailfrom>\r\n" - "To: $ato\r\n" - "Subject: $subject\r\n" - "Reply-To: $nodename <$emailfrom>\r\n" - "Date: $date\r\n" - "MIME-VERSION: 1.0\r\n" - "Content-type: text/html; charset=UTF-8\r\n" - "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" - ); - uint16_t clientTimeout = notificationsettings.Timeout_ms; if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { @@ -239,6 +228,18 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co tmp_ato.replace(";", ","); tmp_ato.replace(" ", ""); + String mailheader = F( + "From: $nodename <$emailfrom>\r\n" + "To: $ato\r\n" + "Subject: $subject\r\n" + "Reply-To: $nodename <$emailfrom>\r\n" + "Date: $date\r\n" + "MIME-VERSION: 1.0\r\n" + "Content-type: text/html; charset=UTF-8\r\n" + "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" + ); + + mailheader.replace(F("$nodename"), senderName); mailheader.replace(F("$emailfrom"), email_address); mailheader.replace(F("$ato"), tmp_ato); @@ -262,7 +263,10 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); # endif // ifndef BUILD_NO_DEBUG - while (true) { + while (true) { // FIXME TD-er: Use of while here can be useful so you can + // exit using break; + // However this is way too complex using both a failFlag and break + // and not even consistently. if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { # ifndef BUILD_NO_DEBUG @@ -426,14 +430,13 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass } base64 encoder; - bool mta1 = NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout); - bool mta2 = NPlugin_001_MTA(client, encoder.encode(user), 334, timeout); - bool mta3 = NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout); - - if (mta1 && mta2 && mta3) { + if (NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(user), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); + return true; } - return mta1 && mta2 && mta3; + return false; } bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) @@ -456,7 +459,7 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern); - while (true) { + while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once if (timeOutReached(timer)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { addLogMove(LOG_LEVEL_ERROR, From 94b5716355c9aa6f2fba10bac51331e3ddaeb92c Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 18 Sep 2024 23:04:25 +0200 Subject: [PATCH 31/48] [ESP-IDF5.3] Move to latest Arduino 3.1 / ESP-IDF 5.3.1 (LittleFS build) --- platformio_core_defs.ini | 4 ++-- platformio_esp32_solo1.ini | 18 +----------------- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 8c589364e7..4da5a42dc2 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -169,7 +169,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 [core_esp32_IDF5_1__3_0_2_SPIFFS] platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2837/framework-arduinoespressif32-all-release_v5.1-e026fd1.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2962/framework-arduinoespressif32-all-release_v5.1-33fbade.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 @@ -195,7 +195,7 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2953/framework-arduinoespressif32-all-release_v5.3-b2dba612.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2963/framework-arduinoespressif32-all-release_v5.3-b2dba612.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index 3e1a1f2e17..bad1c3af74 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -1,25 +1,9 @@ - - -; IDF 4.4 -[esp32_solo1_common] -extends = esp32_base -platform_packages = framework-arduino-solo1 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1646/framework-arduinoespressif32-solo1-release_v4.4_spiffs-e3fc63b439.zip -lib_ignore = ${esp32_always.lib_ignore} - ESP32_ping - ${no_ir.lib_ignore} - ESP32 BLE Arduino -build_flags = ${esp32_base.build_flags} - -DFEATURE_ARDUINO_OTA=1 -extra_scripts = ${esp32_base.extra_scripts} -build_unflags = ${esp32_base.build_unflags} - -fexceptions - ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2954/framework-arduinoespressif32-solo1-release_v5.3-b2dba612.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2964/framework-arduinoespressif32-solo1-release_v5.3-b2dba612.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 42cf759eef7f8693c977f5e878452f87dcbb4a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Zimo=C5=84?= Date: Fri, 20 Sep 2024 04:01:05 +0200 Subject: [PATCH 32/48] if we wait for data from SMTP - do not flush received data --- src/_N001_Email.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 9f9a353d97..1d3c185685 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -448,9 +448,10 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa } # endif // ifndef BUILD_NO_DEBUG - client.PR_9453_FLUSH_TO_CLEAR(); - - if (aStr.length()) { client.println(aStr); } + if (aStr.length()) { + client.PR_9453_FLUSH_TO_CLEAR(); // have to send msg to server so flush data first + client.println(aStr); + } // Wait For Response unsigned long timer = millis() + timeout; From f69584486b6e2acb8191ebf7a72b81a154bc562f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Zimo=C5=84?= Date: Fri, 20 Sep 2024 02:47:27 +0000 Subject: [PATCH 33/48] EMail - correct response checking --- src/_N001_Email.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 1d3c185685..36bf82b3dd 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -458,8 +458,6 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa backgroundtasks(); - const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern); - while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once if (timeOutReached(timer)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { @@ -474,9 +472,14 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa String line; safeReadStringUntil(client, line, '\n', 1024, timeout); - line.replace("-", " "); // Must Remove optional dash from MTA response code. + // response could be like: '220 domain', '220-domain','220+domain' + const String pattern_str_space = strformat(F("%d "), aWaitForPattern); + const String pattern_str_minus = strformat(F("%d-"), aWaitForPattern); + const String pattern_str_plus = strformat(F("%d+"), aWaitForPattern); - const bool patternFound = line.indexOf(aWaitForPattern_str) >= 0; + const bool patternFound = line.indexOf(pattern_str_space) >= 0 + || line.indexOf(pattern_str_minus) >= 0 + || line.indexOf(pattern_str_plus) >= 0; # ifndef BUILD_NO_DEBUG From c92aadcc5026eb80f7563e07939bb42b12d6f349 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 20 Sep 2024 21:38:03 +0200 Subject: [PATCH 34/48] [Scripts] Fix some issues and upgrade to external changes --- embed_files.sh | 36 ++++++++++++++++++++++++---------- src/src/Static/WebStaticData.h | 3 ++- static/codemirror.min.css | 2 +- static/rules_save.js | 3 ++- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/embed_files.sh b/embed_files.sh index 09cdc011e7..29590c1e2d 100755 --- a/embed_files.sh +++ b/embed_files.sh @@ -15,17 +15,17 @@ rm $outputfile function minify_html { file=$1 - post_To https://www.toptal.com/developers/html-minifier/raw $file + post_To https://www.toptal.com/developers/html-minifier/api/raw $file } function minify_css { file=$1 - post_To https://www.toptal.com/developers/cssminifier/raw $file + post_To https://www.toptal.com/developers/cssminifier/api/raw $file } function minify_js { file=$1 - post_To https://www.toptal.com/developers/javascript-minifier/raw $file + post_To https://www.toptal.com/developers/javascript-minifier/api/raw $file } function post_To { @@ -34,7 +34,7 @@ function post_To { RESPONSE=/tmp/response.txt HEADERS=/tmp/headers.txt curl -X POST -s --data-urlencode "input@$file" $url -D $HEADERS -o $RESPONSE - status=$(cat $HEADERS | grep '^HTTP/1' | tail -1 | awk '{print $2}') + status=$(cat $HEADERS | grep '^HTTP/2' | tail -1 | awk '{print $2}') if [[ "$status" == "200" ]]; then cat $RESPONSE > /tmp/converter.temp rm $RESPONSE @@ -50,18 +50,34 @@ function minify_svg { svgo -i /Users/User/Desktop/icons/tools.svg -o - > /tmp/converter.temp } -function ascii2hexCstyle { +function ascii2hexCstyle { # fold at column 135 so VSCode can 'color-highlight' the array file_name=$(constFileName $1) result=$(cat /tmp/converter.temp | hexdump -ve '1/1 "0x%.2x,"') - result=$(echo $result | sed 's/,$//') - echo "static const char DATA_${file_name}[] PROGMEM = {$result,0};" + result=$(echo $result | sed 's/,$//' | fold -w 135) + echo "static const char DATA_${file_name}[] PROGMEM = {" + echo "$result,0" + echo "};" + echo + echo +} + +# split text in 132 character chunks, replace \ by \\, replace " by ', replace temp linebreak by real linebreak, wrap in quoted PROGMEM char array +function ascii2stringCstyle { + file_name=$(constFileName $1) + result=$(cat /tmp/converter.temp) + # s/\"/\\\"/g # double-quote " to escaped double-quote \", can use for .js but increases byte count + # s/\"/'"'"'/g # replace double-quote by single-quote, in a single-quoted string, can not be used for js + result=$(echo $result | sed -r 's/(.{132})/\1\x02/g; s/\\/\\\\/g; s/\"/'"'"'/g; s/\x02/\"\n\"/g') + echo "static const char DATA_${file_name}[] PROGMEM = {" + echo "\"$result\"" + echo "};" echo echo } function constFileName { extension=$(echo $1 | egrep -io "(json|svg|css|js|html)$" | tr "[:lower:]" "[:upper:]") - file=$(echo $1 | sed 's/\.json//' | sed 's/\.svg//' | sed 's/\.css//' | sed 's/\.html//' | sed 's/\.js//' | sed 's/\.\///' | tr '/' '_' | tr '.' '_' | tr '-' '_' | tr "[:lower:]" "[:upper:]") + file=$(echo $1 | sed 's/\.json//; s/\.svg//; s/\.css//; s/\.html//; s/\.js//; s/\.\///' | tr '/' '_' | tr '.' '_' | tr '-' '_' | tr "[:lower:]" "[:upper:]") underscore="_" echo $file$underscore$extension } @@ -91,12 +107,12 @@ for file in $file_list; do elif [[ "$file" == *.min.css ]]; then echo " CSS already minified" cat $file > /tmp/converter.temp - ascii2hexCstyle $file >> $outputfile + ascii2stringCstyle $file >> $outputfile elif [[ "$file" == *.css ]]; then echo " CSS minify" minify_css $file keepMinifiedFile $file - ascii2hexCstyle $file >> $outputfile + ascii2stringCstyle $file >> $outputfile elif [[ "$file" == *.html ]]; then echo " HTML minify" minify_html $file diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index b2dbd5bf04..a09be57165 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -678,7 +678,8 @@ static const char jsSaveRules[] PROGMEM = { "else{" "fetch('/upload',{" "method:'POST'," -"body:a" +"body:a," +"mode:'no-cors'" "}).then(e=>e.text()).then(l=>{" "let a='/rules'+n+'.txt?callback='+Date.now();" "fetch(a).then(e=>e.text()).then(n=>{" diff --git a/static/codemirror.min.css b/static/codemirror.min.css index 8b5e74332a..9ed6126982 100644 --- a/static/codemirror.min.css +++ b/static/codemirror.min.css @@ -1 +1 @@ -:root{--cmbg1:#fff;--cmc1:black;--cmbg2:#eee;--cmc2:#999;--cmbd:1px solid black;--cmbg3:#d7d4f0;--cmbg4:#e8f2ff;--cmc3:#708;--cmc4:#05a;--cmc5:#085;--cmc6:#7d7d7d;--cmc7:#ff8628;--cmc8:#ff9800;--cmc9:#219;--cmc10:#164;--cmc11:#0080ff;--cmc12:#1eb802;--cmc13:#555;--cmc14:#3f04b5;--cmc15:#ff5501;--cmc16:#1717ff;--cmc17:#e78a4e;--cmc18:black;--cmbg5:#08f;--cmbg6:white;--cmbg7:#eee;--cmbg8:#d9d9d9;--cmbg9:#d7d4f0;--cmc19:black;}[data-theme="dark"]{--cmbg1:black;--cmc1:#e8d1a7;--cmbg2:#212121;--cmc2:#e8d2a7;--cmbd:1px solid #FFCC00;--cmbg3:#80cbc433;--cmbg4:#00000080;--cmc3:#ea6962;--cmc4:#EEFFFF;--cmc5:#f07178;--cmc6:#6c6c6c;--cmc7:#d8a657;--cmc8:#ff9800;--cmc9:#f96407;--cmc10:#d2869b;--cmc11:#8185ae;--cmc12:#a9b665;--cmc13:#FFCB6B;--cmc14:#7caea2;--cmc15:#DECB6B;--cmc16:#5f668e;--cmc17:#e78a4e;--cmc18:#cbcbcb;--cmbg5:#44607a;--cmbg6:#444;--cmbg7:#444444;--cmbg8:#5f5f5f;--cmbg9:#5f5f5f;--cmc19:#eee;}@media (prefers-color-scheme:dark){[data-theme="auto"]{--cmbg1:black;--cmc1:#e8d1a7;--cmbg2:#212121;--cmc2:#e8d2a7;--cmbd:1px solid #FFCC00;--cmbg3:#80cbc433;--cmbg4:#00000080;--cmc3:#ea6962;--cmc4:#EEFFFF;--cmc5:#f07178;--cmc6:#6c6c6c;--cmc7:#d8a657;--cmc8:#ff9800;--cmc9:#f96407;--cmc10:#d2869b;--cmc11:#8185ae;--cmc12:#a9b665;--cmc13:#FFCB6B;--cmc14:#7caea2;--cmc15:#DECB6B;--cmc16:#5f668e;--cmc17:#e78a4e;--cmc18:#cbcbcb;--cmbg5:#44607a;--cmbg6:#444;--cmbg7:#444444;--cmbg8:#5f5f5f;--cmbg9:#5f5f5f;--cmc19:#eee}}.CodeMirror{height:60vh;color:var(--cmc1);direction:ltr;border-color:gray;border-style:solid;border-width:1px;border-radius:5px;}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{background-color:white}.CodeMirror-gutters{background:var(--cmbg2);white-space:nowrap;}.CodeMirror-linenumbers{}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:var(--cmc2);white-space:nowrap;}.CodeMirror-guttermarker{color:var(--cmc2);}.CodeMirror-guttermarker-subtle{color:var(--cmc2);}.CodeMirror-cursor{border-left:var(--cmbd);border-right:none;width:0;}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver;}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7;}.cm-fat-cursor div.CodeMirror-cursors{z-index:1;}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:transparent;}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:transparent;}.cm-fat-cursor{caret-color:transparent;}@-moz-keyframes blink{0%{}50%{background-color:transparent}100%{}}@-webkit-keyframes blink{0%{}50%{background-color:transparent}100%{}}@keyframes blink{0%{}50%{background-color:transparent}100%{}}.CodeMirror-overwrite .CodeMirror-cursor{}.cm-tab{display:inline-block;text-decoration:inherit;}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden;}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute;}.cm-s-default .cm-header{color:blue;}.cm-s-default .cm-quote{color:#090;}.cm-negative{color:#d44;}.cm-positive{color:#292;}.cm-header,.cm-strong{font-weight:bold;}.cm-em{font-style:italic;}.cm-link{text-decoration:underline;}.cm-strikethrough{text-decoration:line-through;}.cm-s-default .cm-keyword{color:var(--cmc3);}.cm-s-default .cm-atom{color:var(--cmc9);}.cm-s-default .cm-number{color:var(--cmc10);}.cm-s-default .cm-def{color:var(--cmc11);}.cm-s-default .cm-variable,.cm-s-default .cm-punctuation,.cm-s-default .cm-property,.cm-s-default .cm-qualifier{}.cm-s-default .cm-variable-2{color:var(--cmc4);}.cm-s-default .cm-variable-3,.cm-s-default .cm-type{color:var(--cmc5);}.cm-s-default .cm-comment{color:var(--cmc6);}.cm-s-default .cm-string{color:var(--cmc7);}.cm-s-default .cm-string-2{color:var(--cmc8);}.cm-s-default .cm-meta{color:var(--cmc13);}.cm-s-default .cm-operator{color:var(--cmc15);}.cm-s-default .cm-builtin{color:var(--cmc14);}.cm-s-default .cm-bracket{color:rgb(229,57,229);}.cm-s-default .cm-tag{color:var(--cmc12);}.cm-s-default .cm-attribute{color:var(--cmc16);}.cm-s-default .cm-hr{color:var(--cmc17);}.cm-s-default .cm-link{color:#00c;}.cm-s-default .cm-error{color:#f00;}.cm-invalidchar{color:#f00;}.cm-warning{color:#f00;animation:blinker 1s linear infinite;}@keyframes blinker{50%{opacity:0}}.CodeMirror-composing{border-bottom:2px solid;}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0;}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22;}.CodeMirror-matchingtag{background:rgba(255,150,0,.3);}.CodeMirror-activeline-background{background:#e8f2ff;}.CodeMirror{position:relative;overflow:hidden;background:var(--cmbg1);}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:none;position:relative;z-index:0;}.CodeMirror-sizer{position:absolute;border-right:50px solid transparent;}.CodeMirror-vscrollbar,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-gutter-filler{position:absolute;z-index:6;display:none;outline:none;}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll;}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll;}.CodeMirror-scrollbar-filler{right:0;bottom:0;}.CodeMirror-gutter-filler{left:0;bottom:0;}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3;}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px;}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:none!important;border:none!important;}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4;}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4;}.CodeMirror-gutter-wrapper::selection{background-color:transparent}.CodeMirror-gutter-wrapper::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:transparent;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual;}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal;}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0;}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-widget{}.CodeMirror-rtl pre{direction:rtl;}.CodeMirror-code{outline:none;}.CodeMirror-scroll,.CodeMirror-sizer,.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber{-moz-box-sizing:content-box;box-sizing:content-box;}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden;}.CodeMirror-cursor{position:absolute;pointer-events:none;}.CodeMirror-measure pre{position:static;}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3;}div.CodeMirror-dragcursors{visibility:visible;}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible;}.CodeMirror-selected{background:var(--cmbg8);}.CodeMirror-focused .CodeMirror-selected{background:var(--cmbg9);}.CodeMirror-crosshair{cursor:crosshair;}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:var(--cmbg3);}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:var(--cmbg3);}.cm-searching{background-color:rgba(162,162,162,.396);}.cm-force-border{padding-right:.1px;}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:'';}span.CodeMirror-selectedtext{background:none;}.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:var(--cmbg6);font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto;}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:var(--cmc18);cursor:pointer;}li.CodeMirror-hint-active{background:var(--cmbg5);color:rgb(255,255,255);}@media screen and (max-width:450px){.CodeMirror-sizer{margin-left:0!important}.CodeMirror-gutters{display:none}.CodeMirror-linenumber{display:none}}.CodeMirror-dialog{position:fixed;height:42px;left:0;width:100vw;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit;display:flex;align-items:center;}.CodeMirror-dialog input::selection{color:rgb(255,255,255);background:rgb(118,69,231);}.CodeMirror-dialog-top{border-bottom:.5px solid #444;top:0;}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0;}.CodeMirror-dialog input{width:50vw!important;color:var(--cmc19);font-family:monospace;background:var(--cmbg7);border-style:solid;border-width:1px;border-color:gray;border-radius:5px;height:28px;font-size:inherit;}.CodeMirror-dialog button{background-color:var(--cmbg5);border:none;border-radius:5px;color:#fff;margin:4px;padding:4px 16px;}span.CodeMirror-search-hint{display:none;} \ No newline at end of file +:root,[data-theme=dark]{--cmc8:#ff9800;--cmc17:#e78a4e}:root{--cmbg1:#fff;--cmc1:black;--cmbg2:#eee;--cmc2:#999;--cmbd:1px solid black;--cmbg3:#d7d4f0;--cmbg4:#e8f2ff;--cmc3:#708;--cmc4:#05a;--cmc5:#085;--cmc6:#7d7d7d;--cmc7:#ff8628;--cmc9:#219;--cmc10:#164;--cmc11:#0080ff;--cmc12:#1eb802;--cmc13:#555;--cmc14:#3f04b5;--cmc15:#ff5501;--cmc16:#1717ff;--cmc18:black;--cmbg5:#08f;--cmbg6:white;--cmbg7:#eee;--cmbg8:#d9d9d9;--cmbg9:#d7d4f0;--cmc19:black}[data-theme=dark]{--cmbg1:black;--cmc1:#e8d1a7;--cmbg2:#212121;--cmc2:#e8d2a7;--cmbd:1px solid #FFCC00;--cmbg3:#80cbc433;--cmbg4:#00000080;--cmc3:#ea6962;--cmc4:#EEFFFF;--cmc5:#f07178;--cmc6:#6c6c6c;--cmc7:#d8a657;--cmc9:#f96407;--cmc10:#d2869b;--cmc11:#8185ae;--cmc12:#a9b665;--cmc13:#FFCB6B;--cmc14:#7caea2;--cmc15:#DECB6B;--cmc16:#5f668e;--cmc18:#cbcbcb;--cmbg5:#44607a;--cmbg6:#444;--cmbg7:#444444;--cmbg8:#5f5f5f;--cmbg9:#5f5f5f;--cmc19:#eee}@media (prefers-color-scheme:dark){[data-theme=auto]{--cmbg1:black;--cmc1:#e8d1a7;--cmbg2:#212121;--cmc2:#e8d2a7;--cmbd:1px solid #FFCC00;--cmbg3:#80cbc433;--cmbg4:#00000080;--cmc3:#ea6962;--cmc4:#EEFFFF;--cmc5:#f07178;--cmc6:#6c6c6c;--cmc7:#d8a657;--cmc8:#ff9800;--cmc9:#f96407;--cmc10:#d2869b;--cmc11:#8185ae;--cmc12:#a9b665;--cmc13:#FFCB6B;--cmc14:#7caea2;--cmc15:#DECB6B;--cmc16:#5f668e;--cmc17:#e78a4e;--cmc18:#cbcbcb;--cmbg5:#44607a;--cmbg6:#444;--cmbg7:#444444;--cmbg8:#5f5f5f;--cmbg9:#5f5f5f;--cmc19:#eee}}.CodeMirror{height:60vh;color:var(--cmc1);direction:ltr;border:1px solid gray;border-radius:5px}.CodeMirror-lines{padding:4px 0}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{background:var(--cmbg2);white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:var(--cmc2);white-space:nowrap}.CodeMirror-guttermarker,.CodeMirror-guttermarker-subtle{color:var(--cmc2)}.CodeMirror-cursor{border-left:var(--cmbd);border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:transparent}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:var(--cmc3)}.cm-s-default .cm-atom{color:var(--cmc9)}.cm-s-default .cm-number{color:var(--cmc10)}.cm-s-default .cm-def{color:var(--cmc11)}.cm-s-default .cm-variable-2{color:var(--cmc4)}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:var(--cmc5)}.cm-s-default .cm-comment{color:var(--cmc6)}.cm-s-default .cm-string{color:var(--cmc7)}.cm-s-default .cm-string-2{color:var(--cmc8)}.cm-s-default .cm-meta{color:var(--cmc13)}.cm-s-default .cm-operator{color:var(--cmc15)}.cm-s-default .cm-builtin{color:var(--cmc14)}.cm-s-default .cm-bracket{color:#e539e5}.cm-s-default .cm-tag{color:var(--cmc12)}.cm-s-default .cm-attribute{color:var(--cmc16)}.cm-s-default .cm-hr{color:var(--cmc17)}.cm-s-default .cm-link{color:#00c}.cm-invalidchar,.cm-s-default .cm-error{color:red}.cm-warning{color:red;animation:1s linear infinite blinker}@keyframes blinker{50%{opacity:0}}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:var(--cmbg1)}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:absolute;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}.CodeMirror-focused div.CodeMirror-cursors,div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-selected{background:var(--cmbg8)}.CodeMirror-focused .CodeMirror-selected{background:var(--cmbg9)}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:var(--cmbg3)}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:var(--cmbg3)}.cm-searching{background-color:rgba(162,162,162,.396)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.CodeMirror-hints{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:var(--cmbg6);font-size:90%;font-family:monospace;max-height:20em;overflow-y:auto}.CodeMirror-hint{margin:0;padding:0 4px;border-radius:2px;white-space:pre;color:var(--cmc18);cursor:pointer}li.CodeMirror-hint-active{background:var(--cmbg5);color:#fff}@media screen and (max-width:450px){.CodeMirror-sizer{margin-left:0!important}.CodeMirror-gutters,.CodeMirror-linenumber{display:none}}.CodeMirror-dialog{position:fixed;height:42px;left:0;width:100vw;background:inherit;z-index:15;padding:.1em .8em;overflow:hidden;color:inherit;display:flex;align-items:center}.CodeMirror-dialog input::selection{color:#fff;background:#7645e7}.CodeMirror-dialog-top{border-bottom:.5px solid #444;top:0}.CodeMirror-dialog-bottom{border-top:1px solid #eee;bottom:0}.CodeMirror-dialog input{width:50vw!important;color:var(--cmc19);font-family:monospace;background:var(--cmbg7);border:1px solid gray;border-radius:5px;height:28px;font-size:inherit}.CodeMirror-dialog button{background-color:var(--cmbg5);border:none;border-radius:5px;color:#fff;margin:4px;padding:4px 16px}span.CodeMirror-search-hint{display:none} \ No newline at end of file diff --git a/static/rules_save.js b/static/rules_save.js index 8abc0a23a8..805eaef3e1 100644 --- a/static/rules_save.js +++ b/static/rules_save.js @@ -17,7 +17,8 @@ function saveRulesFile() { } else { fetch("/upload", { method: "POST", - body: formData + body: formData, + mode: "no-cors" }).then(response => response.text()).then(l => { let url = "/rules" + ruleNumber + ".txt?callback=" + Date.now(); fetch(url).then(res => res.text()).then(ruleTextNewCheck => { From 5849da04763b5165b87bff77a7e984bb4780f97f Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 23 Sep 2024 10:57:22 +0200 Subject: [PATCH 35/48] [ESP-IDF5.3] Move to latest Arduino/IDF code --- platformio_core_defs.ini | 2 +- platformio_esp32_solo1.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 4da5a42dc2..b0cdd2d0c1 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -195,7 +195,7 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2963/framework-arduinoespressif32-all-release_v5.3-b2dba612.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2980/framework-arduinoespressif32-all-release_v5.3-3a06c6fe.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index bad1c3af74..111adf9300 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -3,7 +3,7 @@ [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2964/framework-arduinoespressif32-solo1-release_v5.3-b2dba612.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2981/framework-arduinoespressif32-solo1-release_v5.3-3a06c6fe.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 87bca8f140d530615d6c97efb80e4161c3759519 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 23 Sep 2024 22:45:23 +0200 Subject: [PATCH 36/48] [Email] Fix issues with different mail server versions --- src/_N001_Email.cpp | 224 ++++++++++-------- .../DataStructs/NotificationSettingsStruct.h | 2 +- 2 files changed, 126 insertions(+), 100 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 9736f60ab5..36bf82b3dd 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -47,7 +47,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, bool NPlugin_001_Auth(WiFiClient & client, const String& user, const String& pass, - uint16_t timeout); + uint16_t timeout); bool NPlugin_001_MTA(WiFiClient & client, const String& aStr, uint16_t aWaitForPattern, @@ -134,14 +134,15 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 - client.setTimeout((CONTROLLER_CLIENTTIMEOUT_MAX + 500) / 1000); // in seconds!!!! + client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! Client *pClient = &client; - pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); + pClient->setTimeout(notificationsettings.Timeout_ms); # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - client.setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); // in msec as it should be! + client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat( F("Email: Connecting to %s:%d"), @@ -150,7 +151,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } # endif // ifndef BUILD_NO_DEBUG - if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, CONTROLLER_CLIENTTIMEOUT_DFLT)) { + if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, notificationsettings.Timeout_ms)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { addLog(LOG_LEVEL_ERROR, strformat( F("Email: Error connecting to %s:%d"), @@ -160,19 +161,9 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co myStatus = false; failFlag = true; } else { - String mailheader = F( - "From: $nodename <$emailfrom>\r\n" - "To: $ato\r\n" - "Subject: $subject\r\n" - "Reply-To: $nodename <$emailfrom>\r\n" - "Date: $date\r\n" - "MIME-VERSION: 1.0\r\n" - "Content-type: text/html; charset=UTF-8\r\n" - "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" - ); + uint16_t clientTimeout = notificationsettings.Timeout_ms; - uint16_t clientTimeout = notificationsettings.Timeout * 1000; // Convert to mS. - if (clientTimeout < NPLUGIN_001_MIN_TM || clientTimeout > NPLUGIN_001_MAX_TM) { + if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; } @@ -193,45 +184,62 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co // Use Notify Command's destination email address(s) if provided in Command rules. // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
How are you?
Have a good day.
" - String subAddr = ""; - String tmp_ato = ""; - int pos_brace1 = aMesg.indexOf('{'); - int pos_amper = aMesg.indexOf('@'); - int pos_brace2 = aMesg.indexOf('}'); - if(pos_brace1 == 0 && pos_amper > pos_brace1 && pos_brace2 > pos_amper) { - subAddr = aMesg.substring(pos_brace1+1, pos_brace2); - subAddr.trim(); - tmp_ato = subAddr; + String subAddr; + String tmp_ato; + int pos_brace1 = aMesg.indexOf('{'); + int pos_amper = aMesg.indexOf('@'); + int pos_brace2 = aMesg.indexOf('}'); + + if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { + subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); + subAddr.trim(); + tmp_ato = subAddr; # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); - } - # endif - String subMsg = aMesg.substring(pos_brace2+1); // Remove substitute email address from subject line. + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. + subMsg.trim(); + + if (subMsg.indexOf(',') == 0) { + subMsg = subMsg.substring(1); // Remove leading comma. subMsg.trim(); - if(subMsg.indexOf(',') == 0) { - subMsg = subMsg.substring(1); // Remove leading comma. - subMsg.trim(); - } - if(!subMsg.length()) { - subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; - } + } + + if (!subMsg.length()) { + subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; + } # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); - } - # endif - aMesg = subMsg; + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + aMesg = subMsg; } else { - tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. + tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. } // Clean up receiver address. tmp_ato.replace(";", ","); tmp_ato.replace(" ", ""); + String mailheader = F( + "From: $nodename <$emailfrom>\r\n" + "To: $ato\r\n" + "Subject: $subject\r\n" + "Reply-To: $nodename <$emailfrom>\r\n" + "Date: $date\r\n" + "MIME-VERSION: 1.0\r\n" + "Content-type: text/html; charset=UTF-8\r\n" + "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" + ); + + mailheader.replace(F("$nodename"), senderName); mailheader.replace(F("$emailfrom"), email_address); mailheader.replace(F("$ato"), tmp_ato); @@ -252,73 +260,86 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout/1000)); - # endif + addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); + # endif // ifndef BUILD_NO_DEBUG - while (true) { + while (true) { // FIXME TD-er: Use of while here can be useful so you can + // exit using break; + // However this is way too complex using both a failFlag and break + // and not even consistently. if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); } - # endif + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } if (!failFlag) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); - } - # endif - if (!NPlugin_001_MTA(client, strformat(F("EHLO %s"), notificationsettings.Domain), 250, clientTimeout)) { + addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); + + if (!NPlugin_001_MTA( + client, + astr, + 250, + clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; } } - // Must retrieve SMTP Reply Packet. Data not used, ignored. + // Must retrieve SMTP Reply Packet. Data not used, ignored. if (!failFlag) { - unsigned long timeout = millis(); + const unsigned long timer = millis() + clientTimeout; String replyStr; - String catStr = ""; - while (client.available()) { - if (millis() > timeout + clientTimeout) { + String catStr; + + bool done = false; + + while (client.available() && !done) { + if (timeOutReached(timer)) { failFlag = true; break; } - safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ, clientTimeout); + done = safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ); catStr += replyStr; } - if(!catStr.length()) { - catStr = "Empty!"; + if (!catStr.length()) { + catStr = F("Empty!"); } # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Packet Rcvd is: > %s <"),catStr.c_str())); + String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); + addLogMove(LOG_LEVEL_DEBUG, log); } - # endif + # endif // ifndef BUILD_NO_DEBUG } if (!failFlag) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); } - # endif + # endif // ifndef BUILD_NO_DEBUG + if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); - } - # endif + + addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } @@ -327,25 +348,26 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if (!failFlag) { # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); - # endif - if (!NPlugin_001_MTA(client, strformat(F("MAIL FROM:<%s>"), email_address.c_str()), 250, clientTimeout)) { + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); + + if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } } if (!failFlag) { - bool nextAddressAvailable = true; - int i = 0; + bool nextAddressAvailable = true; + int i = 0; String emailTo; const String receiver(tmp_ato); - addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"),receiver.c_str())); + addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"), receiver.c_str())); if (!getNextMailAddress(receiver, emailTo, i)) { addLog(LOG_LEVEL_ERROR, F("Email: Receiver missing!")); @@ -353,9 +375,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } while (nextAddressAvailable) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); + addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); } if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } @@ -387,7 +408,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co break; } } - client.flush(); + client.PR_9453_FLUSH_TO_CLEAR(); client.stop(); if (myStatus == true) { @@ -409,36 +430,35 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass } base64 encoder; - bool mta1 = NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout); - bool mta2 = NPlugin_001_MTA(client, encoder.encode(user), 334, timeout); - bool mta3 = NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout); - - if (mta1 && mta2 && mta3) { + if (NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(user), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); + return true; } - return (mta1 && mta2 && mta3); - + return false; } bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, aStr); } # endif // ifndef BUILD_NO_DEBUG - client.flush(); - - if (aStr.length()) { client.println(aStr); } + if (aStr.length()) { + client.PR_9453_FLUSH_TO_CLEAR(); // have to send msg to server so flush data first + client.println(aStr); + } // Wait For Response unsigned long timer = millis() + timeout; backgroundtasks(); - const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern); - while (true) { + while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once if (timeOutReached(timer)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { addLogMove(LOG_LEVEL_ERROR, @@ -450,13 +470,19 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa delay(0); String line; - safeReadStringUntil(client, line, '\n', NPLUGIN_001_PKT_SZ, timeout); + safeReadStringUntil(client, line, '\n', 1024, timeout); - line.replace("-", " "); // Must Remove optional dash from MTA response code. + // response could be like: '220 domain', '220-domain','220+domain' + const String pattern_str_space = strformat(F("%d "), aWaitForPattern); + const String pattern_str_minus = strformat(F("%d-"), aWaitForPattern); + const String pattern_str_plus = strformat(F("%d+"), aWaitForPattern); - const bool patternFound = line.indexOf(aWaitForPattern_str) >= 0; + const bool patternFound = line.indexOf(pattern_str_space) >= 0 + || line.indexOf(pattern_str_minus) >= 0 + || line.indexOf(pattern_str_plus) >= 0; # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, line); } diff --git a/src/src/DataStructs/NotificationSettingsStruct.h b/src/src/DataStructs/NotificationSettingsStruct.h index f4f23fc852..58827fcc70 100644 --- a/src/src/DataStructs/NotificationSettingsStruct.h +++ b/src/src/DataStructs/NotificationSettingsStruct.h @@ -31,7 +31,7 @@ struct NotificationSettingsStruct int8_t Pin2; char User[49]; char Pass[33]; - unsigned int Timeout; + unsigned int Timeout_ms; //its safe to extend this struct, up to 4096 bytes, default values in config are 0 }; From 6885efe2bab8e80a6b8067bd6dc951bc01175cb6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 23 Sep 2024 22:57:08 +0200 Subject: [PATCH 37/48] [Email] Add missing define --- platformio_core_defs.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 0743e1af7e..442a580ed8 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -99,6 +99,7 @@ lib_ignore = ${esp82xx_defaults.lib_ignore} build_flags = ${esp82xx_2_7_x.build_flags} -DCORE_POST_3_0_0 -Wno-deprecated-declarations + -DPR_9453_FLUSH_TO_CLEAR=flush ; -flto=auto ; -Wl,-flto build_unflags = -DDEBUG_ESP_PORT @@ -197,6 +198,7 @@ build_flags = -DESP32_STAGE -DLIBRARIES_NO_LOG=1 -DDISABLE_SC16IS752_SPI -DCONFIG_PM_ENABLE + -DPR_9453_FLUSH_TO_CLEAR=flush -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 -DNEOPIXEL_ESP32_RMT_DEFAULT @@ -223,6 +225,7 @@ build_flags = -DESP32_STAGE -DCONFIG_PM_ENABLE ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 + -DPR_9453_FLUSH_TO_CLEAR=flush -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 -DNEOPIXEL_ESP32_RMT_DEFAULT From 4222bde11bb643d087d7a7eba4c54cf30879d82e Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 23 Sep 2024 22:58:44 +0200 Subject: [PATCH 38/48] [Email] Fix ESP8266 2.7.4 builds --- platformio_core_defs.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 442a580ed8..3bdf82712b 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -87,6 +87,7 @@ build_flags = -DNDEBUG -s -DBEARSSL_SSL_BASIC -DCORE_POST_2_6_0 + -DPR_9453_FLUSH_TO_CLEAR=flush ; remove the 4-bytes alignment for PSTR() -DPSTR_ALIGN=1 -Werror=return-type @@ -99,7 +100,6 @@ lib_ignore = ${esp82xx_defaults.lib_ignore} build_flags = ${esp82xx_2_7_x.build_flags} -DCORE_POST_3_0_0 -Wno-deprecated-declarations - -DPR_9453_FLUSH_TO_CLEAR=flush ; -flto=auto ; -Wl,-flto build_unflags = -DDEBUG_ESP_PORT From 1e9422d03bf8758c9f114fd562fb37214db53d84 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 23 Sep 2024 23:10:57 +0200 Subject: [PATCH 39/48] [Email] Add missing changes in NotificationPage.cpp --- src/src/WebServer/NotificationPage.cpp | 89 +++++++++++++++++--------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index aa04973dc7..e76cc74948 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -12,20 +12,19 @@ * 2024-07-01 ThomasB : Start of changelog, older changes not logged. */ -#include "../WebServer/ESPEasy_WebServer.h" -#include "../WebServer/HTML_wrappers.h" -#include "../WebServer/Markup.h" -#include "../WebServer/Markup_Buttons.h" -#include "../WebServer/Markup_Forms.h" +# include "../WebServer/ESPEasy_WebServer.h" +# include "../WebServer/HTML_wrappers.h" +# include "../WebServer/Markup.h" +# include "../WebServer/Markup_Buttons.h" +# include "../WebServer/Markup_Forms.h" -#include "../DataStructs/ESPEasy_EventStruct.h" -#include "../DataStructs/NotificationSettingsStruct.h" +# include "../DataStructs/ESPEasy_EventStruct.h" +# include "../DataStructs/NotificationSettingsStruct.h" -#include "../Helpers/ESPEasy_Storage.h" - -#include "../Globals/ESPEasy_Scheduler.h" -#include "../Globals/Settings.h" +# include "../Helpers/ESPEasy_Storage.h" +# include "../Globals/ESPEasy_Scheduler.h" +# include "../Globals/Settings.h" // ******************************************************************************** @@ -33,13 +32,13 @@ // ******************************************************************************** -#include "../Globals/NPlugins.h" +# include "../Globals/NPlugins.h" void handle_notifications() { - #ifndef BUILD_NO_RAM_TRACKER + # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("handle_notifications")); - #endif + # endif // ifndef BUILD_NO_RAM_TRACKER if (!isLoggedIn()) { return; } navMenuIndex = MENU_INDEX_NOTIFICATIONS; @@ -52,19 +51,19 @@ void handle_notifications() { // 'index' value in the URL - uint8_t notificationindex = getFormItemInt(F("index"), 0); + uint8_t notificationindex = getFormItemInt(F("index"), 0); boolean notificationindexNotSet = notificationindex == 0; --notificationindex; const int notification_webarg_value = getFormItemInt(F("notification"), -1); - if (!notificationindexNotSet && notification_webarg_value != -1) + if (!notificationindexNotSet && (notification_webarg_value != -1)) { const npluginID_t notification = npluginID_t::toPluginID(notification_webarg_value); + if (notification == INVALID_N_PLUGIN_ID) { - Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; + Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; Settings.NotificationEnabled[notificationindex] = false; - } else { MakeNotificationSettings(NotificationSettings); @@ -82,8 +81,10 @@ void handle_notifications() { String dummyString; NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString); } - NotificationSettings.Port = getFormItemInt(F("port"), 0); - NotificationSettings.Timeout = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM/1000); + NotificationSettings.Port = getFormItemInt(F("port"), 0); + + // FIXME TD-er: Must convert this to msec in the user interface as every other timeout in ESPEasy is in msec. + NotificationSettings.Timeout_ms = 1000 * getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM / 1000); NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1); NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1); Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled")); @@ -97,7 +98,8 @@ void handle_notifications() { strncpy_webserver_arg(NotificationSettings.Body, F("body")); } } - addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), sizeof(NotificationSettingsStruct))); + addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), + sizeof(NotificationSettingsStruct))); } // Save the settings. @@ -123,7 +125,7 @@ void handle_notifications() { { html_table_class_multirow(); html_TR(); - html_table_header(F(""), 70); + html_table_header(F(""), 70); html_table_header(F("Nr"), 50); html_table_header(F("Enabled"), 100); html_table_header(F("Service")); @@ -150,8 +152,8 @@ void handle_notifications() { addEnabled(Settings.NotificationEnabled[x]); html_TD(); - uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); - String NotificationName = F("(plugin not found?)"); + uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); + String NotificationName = F("(plugin not found?)"); if (validNProtocolIndex(NotificationProtocolIndex)) { @@ -161,20 +163,21 @@ void handle_notifications() { html_TD(); addHtml(NotificationSettings.Server); html_TD(); - if (NotificationSettings.Port){ + + if (NotificationSettings.Port) { addHtmlInt(NotificationSettings.Port); } else { - //MFD: we display the GPIO + // MFD: we display the GPIO addGpioHtml(NotificationSettings.Pin1); - if (NotificationSettings.Pin2>=0) + if (NotificationSettings.Pin2 >= 0) { html_BR(); addGpioHtml(NotificationSettings.Pin2); } } } - else{ + else { html_TD(3); } } @@ -217,9 +220,33 @@ void handle_notifications() { { addFormTextBox(F("Domain"), F("domain"), NotificationSettings.Domain, sizeof(NotificationSettings.Domain) - 1); addFormTextBox(F("Server"), F("server"), NotificationSettings.Server, sizeof(NotificationSettings.Server) - 1); - addFormNumericBox(F("Port"), F("port"), NotificationSettings.Port, 1, 65535, F("NOTE: SSL/TLS servers NOT supported!")); - if (NotificationSettings.TimeoutNPLUGIN_001_MAX_TM/1000) {NotificationSettings.Timeout=NPLUGIN_001_DEF_TM/1000;} - addFormNumericBox(F("Timeout"), F("timeout"), NotificationSettings.Timeout, NPLUGIN_001_MIN_TM/1000, NPLUGIN_001_MAX_TM/1000, F("Maximum Server Response Time)")); + addFormNumericBox( + F("Port"), F("port"), + NotificationSettings.Port, + 1, + 65535 +# if FEATURE_TOOLTIPS + , F("NOTE: SSL/TLS servers NOT supported!") +# endif // if FEATURE_TOOLTIPS + ); + + if ((NotificationSettings.Timeout_ms < NPLUGIN_001_MIN_TM) || + (NotificationSettings.Timeout_ms > NPLUGIN_001_MAX_TM)) + { + NotificationSettings.Timeout_ms = NPLUGIN_001_DEF_TM; + } + + // FIXME TD-er: Must convert to msec as every other timeout used/configured in ESPEasy is in msec + addFormNumericBox( + F("Timeout"), F("timeout"), + NotificationSettings.Timeout_ms / 1000, + NPLUGIN_001_MIN_TM / 1000, + NPLUGIN_001_MAX_TM / 1000 +# if FEATURE_TOOLTIPS + , F("Maximum Server Response Time") +# endif // if FEATURE_TOOLTIPS + ); + addUnit(F("Seconds")); addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1); From cbd986f911a9f105fce4f76fe6ff0c8cd50ac671 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 24 Sep 2024 18:36:14 +0200 Subject: [PATCH 40/48] [Rules] Fix saving rules on ESP-IDF5.3 --- platformio_core_defs.ini | 1 - src/src/DataStructs/Web_StreamingBuffer.cpp | 8 +++ src/src/ESPEasyCore/ESPEasyWifi.cpp | 2 +- src/src/Static/WebStaticData.h | 35 +--------- src/src/WebServer/AccessControl.cpp | 2 +- src/src/WebServer/LoadFromFS.cpp | 2 + static/rules_save.js | 72 +++++++++++---------- 7 files changed, 52 insertions(+), 70 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index e53bd9a855..5d33e9ca2f 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -207,7 +207,6 @@ build_flags = -DESP32_STAGE -DPR_9453_FLUSH_TO_CLEAR=clear ; -DCONFIG_LWIP_L2_TO_L3_COPY ; -DETH_SPI_SUPPORTS_NO_IRQ=1 - -DPR_9453_FLUSH_TO_CLEAR=flush -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 -DNEOPIXEL_ESP32_RMT_DEFAULT diff --git a/src/src/DataStructs/Web_StreamingBuffer.cpp b/src/src/DataStructs/Web_StreamingBuffer.cpp index 53a4f6fa2e..a5fed60de1 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.cpp +++ b/src/src/DataStructs/Web_StreamingBuffer.cpp @@ -390,7 +390,15 @@ void Web_StreamingBuffer::sendHeaderBlocking(bool allowOriginAll, if (!cacheable) web_server.sendHeader(F("Cache-Control"), F("no-cache")); +#if ESP_IDF_VERSION_MAJOR>4 + if (origin.equals("*")) { + web_server.enableCORS(true); + } else +#endif if (origin.length() > 0) { +#if ESP_IDF_VERSION_MAJOR>4 + web_server.enableCORS(false); +#endif web_server.sendHeader(F("Access-Control-Allow-Origin"), origin); } web_server.send(httpCode, content_type, EMPTY_STRING); diff --git a/src/src/ESPEasyCore/ESPEasyWifi.cpp b/src/src/ESPEasyCore/ESPEasyWifi.cpp index 433f81b68c..8f5ea6c314 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi.cpp @@ -908,7 +908,7 @@ WiFiConnectionProtocol getConnectionProtocol() { case WIFI_PHY_MODE_HT40: return WiFiConnectionProtocol::WiFi_Protocol_HT40; case WIFI_PHY_MODE_HE20: return WiFiConnectionProtocol::WiFi_Protocol_HE20; case WIFI_PHY_MODE_LR: return WiFiConnectionProtocol::WiFi_Protocol_LR; -#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 2, 0) +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) // 5 GHz case WIFI_PHY_MODE_11A: return WiFiConnectionProtocol::WiFi_Protocol_11a; case WIFI_PHY_MODE_VHT20: return WiFiConnectionProtocol::WiFi_Protocol_VHT20; diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index a09be57165..f4606e6a34 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -658,40 +658,9 @@ static const char jsClipboardCopyPart3[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS // Manually minified js +// setTimeout needed or else the upload may fail right after fetching the existing rules file static const char jsSaveRules[] PROGMEM = { -"function saveRulesFile() {" -"'use strict';" -"let e=document.getElementById('size');" -"let t=document.getElementById('rules').value;" -"t=t.replace(/\\r?\\n/g, '\\r\\n');" -"let n=document.getElementById('set').value;" -"let l=new File([t],'rules'+n+'.txt',{" -"type:'text/plain'" -"});" -"let a=new FormData();" -"a.append('file',l);" -"a.append('enctype','multipart/form-data');" -"let o='/rules'+n+'.txt?callback='+Date.now();" -"fetch(o).then(e=>e.text()).then(l=>{" -"if(t===l)" -"console.log('nothing to save...');" -"else{" -"fetch('/upload',{" -"method:'POST'," -"body:a," -"mode:'no-cors'" -"}).then(e=>e.text()).then(l=>{" -"let a='/rules'+n+'.txt?callback='+Date.now();" -"fetch(a).then(e=>e.text()).then(n=>{" -"if(t===n){" -"toasting();" -"e.innerHTML=t.length;" -"}else{" -"console.log('error when saving...');" -"}});" -"});" -"}});" -"}" +"function saveRulesFile(){'use strict';let e=document.getElementById('size');let t=document.getElementById('rules').value;t=t.replace(/\\r?\\n/g,'\\r\\n');let n=document.getElementById('set').value;let l=new File([t],'rules'+n+'.txt',{type:'text/plain'});let a=new FormData();a.append('file',l);a.append('enctype','multipart/form-data');let o='/rules'+n+'.txt?callback='+Date.now();fetch(o).then(e=>e.text()).then(l=>{if(t===l){console.log('nothing to save...')}else{setTimeout(()=>fetch('/upload',{method:'POST',body:a,mode:'no-cors'}).then(e=>e.text()).then(l=>{let a='/rules'+n+'.txt?callback='+Date.now();setTimeout(()=>fetch(a).then(e=>e.text()).then(n=>{if(t===n){toasting();e.innerHTML=t.length}else{console.log('error when saving...')}}),200)}),100)}})}" }; #endif diff --git a/src/src/WebServer/AccessControl.cpp b/src/src/WebServer/AccessControl.cpp index ff50651133..2723391ad0 100644 --- a/src/src/WebServer/AccessControl.cpp +++ b/src/src/WebServer/AccessControl.cpp @@ -88,7 +88,7 @@ boolean clientIPallowed() { #if ESP_IDF_VERSION_MAJOR>=5 // FIXME TD-er: remoteIP() is reporting incorrect value - return true; +// return true; #endif // TD-er Must implement "safe time after boot" IPAddress low, high; diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 2cae14e0db..3247bde08d 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -316,12 +316,14 @@ bool loadFromFS(String path) { } web_server.streamFile(f, String(contentType)); f.close(); + web_server.client().PR_9453_FLUSH_TO_CLEAR(); } if (!fileEmbedded) { return false; } serveEmbedded(path, contentType, false); + web_server.client().PR_9453_FLUSH_TO_CLEAR(); } statusLED(true); diff --git a/static/rules_save.js b/static/rules_save.js index 805eaef3e1..0dbbae8169 100644 --- a/static/rules_save.js +++ b/static/rules_save.js @@ -1,35 +1,39 @@ function saveRulesFile() { - "use strict"; - let size = document.getElementById("size"); - let ruleTextNew = document.getElementById("rules").value; - ruleTextNew = ruleTextNew.replace(/\\r?\\n/g, "\\r\\n"); - let ruleNumber = document.getElementById("set").value; - let ruleTextFileData = new File([ruleTextNew], "rules" + ruleNumber + ".txt", { - type: "text/plain" - }); - let formData = new FormData(); - formData.append("file", ruleTextFileData); - formData.append("enctype", "multipart/form-data"); - let url = "/rules" + ruleNumber + ".txt?callback=" + Date.now(); - fetch(url).then(res => res.text()).then(ruleTextOld => { - if (ruleTextNew === ruleTextOld) { - console.log("nothing to save..."); - } else { - fetch("/upload", { - method: "POST", - body: formData, - mode: "no-cors" - }).then(response => response.text()).then(l => { - let url = "/rules" + ruleNumber + ".txt?callback=" + Date.now(); - fetch(url).then(res => res.text()).then(ruleTextNewCheck => { - if (ruleTextNew === ruleTextNewCheck) { - toasting(); - size.innerHTML = ruleTextNew.length; - } else { - console.log("error when saving..."); - } - }); - }); - } - }); -} \ No newline at end of file + 'use strict'; + let e = document.getElementById('size'); + let t = document + .getElementById('rules') + .value; + t = t.replace(/\\r?\\n/g, '\\r\\n'); + let n = document + .getElementById('set') + .value; + let l = new File([t], 'rules' + n + '.txt', {type: 'text/plain'}); + let a = new FormData(); + a.append('file', l); + a.append('enctype', 'multipart/form-data'); + let o = '/rules' + n + '.txt?callback=' + Date.now(); + fetch(o) + .then(e => e.text()) + .then(l => { + if (t === l) + console.log('nothing to save...'); + else { + setTimeout(() => fetch('/upload', { + method: 'POST', + body: a, + mode: 'no-cors' + }).then(e => e.text()).then(l => { + let a = '/rules' + n + '.txt?callback=' + Date.now(); + setTimeout(() => fetch(a).then(e => e.text()).then(n => { + if (t === n) { + toasting(); + e.innerHTML = t.length; + } else { + console.log('error when saving...'); + } + }), 200); + }), 100); + } + }); +} From e3e7101a654c047e155228204687283ab7dd521f Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 24 Sep 2024 22:48:22 +0200 Subject: [PATCH 41/48] [Cleanup] Reduce SaveRules.js --- src/src/Static/WebStaticData.h | 2 +- static/rules_save.js | 35 +++++++++++++++++----------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index f4606e6a34..f47347803d 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -660,7 +660,7 @@ static const char jsClipboardCopyPart3[] PROGMEM = { // Manually minified js // setTimeout needed or else the upload may fail right after fetching the existing rules file static const char jsSaveRules[] PROGMEM = { -"function saveRulesFile(){'use strict';let e=document.getElementById('size');let t=document.getElementById('rules').value;t=t.replace(/\\r?\\n/g,'\\r\\n');let n=document.getElementById('set').value;let l=new File([t],'rules'+n+'.txt',{type:'text/plain'});let a=new FormData();a.append('file',l);a.append('enctype','multipart/form-data');let o='/rules'+n+'.txt?callback='+Date.now();fetch(o).then(e=>e.text()).then(l=>{if(t===l){console.log('nothing to save...')}else{setTimeout(()=>fetch('/upload',{method:'POST',body:a,mode:'no-cors'}).then(e=>e.text()).then(l=>{let a='/rules'+n+'.txt?callback='+Date.now();setTimeout(()=>fetch(a).then(e=>e.text()).then(n=>{if(t===n){toasting();e.innerHTML=t.length}else{console.log('error when saving...')}}),200)}),100)}})}" +"function saveRulesFile(){'use strict';let e=document.getElementById('size'),t=document.getElementById('rules').value;t=t.replace(/\\r?\\n/g,'\\r\\n');let n=document.getElementById('set').value,l=new File([t],'rules'+n+'.txt',{type:'text/plain'}),o=new FormData;o.append('file',l),o.append('enctype','multipart/form-data');let a='/rules'+n+'.txt?callback='+Date.now();fetch(a).then((e=>e.text())).then((n=>{t===n?console.log('nothing to save...'):setTimeout((()=>fetch('/upload',{method:'POST',body:o,mode:'no-cors'}).then((e=>e.text())).then((n=>{setTimeout((()=>fetch(a).then((e=>e.text())).then((n=>{t===n?(toasting(),e.innerHTML=t.length):console.log('error when saving...')}))),200)}))),100)}))}" }; #endif diff --git a/static/rules_save.js b/static/rules_save.js index 0dbbae8169..7595b3a82c 100644 --- a/static/rules_save.js +++ b/static/rules_save.js @@ -1,34 +1,33 @@ function saveRulesFile() { 'use strict'; - let e = document.getElementById('size'); - let t = document + let size = document.getElementById('size'); + let ruleTextNew = document .getElementById('rules') .value; - t = t.replace(/\\r?\\n/g, '\\r\\n'); + ruleTextNew = ruleTextNew.replace(/\\r?\\n/g, '\\r\\n'); let n = document .getElementById('set') .value; - let l = new File([t], 'rules' + n + '.txt', {type: 'text/plain'}); - let a = new FormData(); - a.append('file', l); - a.append('enctype', 'multipart/form-data'); - let o = '/rules' + n + '.txt?callback=' + Date.now(); - fetch(o) - .then(e => e.text()) - .then(l => { - if (t === l) + let ruleTextFileData = new File([ruleTextNew], 'rules' + n + '.txt', {type: 'text/plain'}); + let formData = new FormData(); + formData.append('file', ruleTextFileData); + formData.append('enctype', 'multipart/form-data'); + let url = '/rules' + n + '.txt?callback=' + Date.now(); + fetch(url) + .then(size => size.text()) + .then(ruleTextFileData => { + if (ruleTextNew === ruleTextFileData) console.log('nothing to save...'); else { setTimeout(() => fetch('/upload', { method: 'POST', - body: a, + body: formData, mode: 'no-cors' - }).then(e => e.text()).then(l => { - let a = '/rules' + n + '.txt?callback=' + Date.now(); - setTimeout(() => fetch(a).then(e => e.text()).then(n => { - if (t === n) { + }).then(size => size.text()).then(ruleTextFileData => { + setTimeout(() => fetch(url).then(size => size.text()).then(n => { + if (ruleTextNew === n) { toasting(); - e.innerHTML = t.length; + size.innerHTML = ruleTextNew.length; } else { console.log('error when saving...'); } From b2c845d0bff67d542ea2e4664ef5afa9cc13ee86 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 13:18:51 +0200 Subject: [PATCH 42/48] Fix saving task names when rules are enabled --- platformio_core_defs.ini | 2 +- src/src/Globals/Plugins.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 5d33e9ca2f..f2e9f27a58 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -169,7 +169,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; Just for those who lost track of the extremely confusing numbering schema. ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 [core_esp32_IDF5_1__3_0_2_SPIFFS] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.08.10/platform-espressif32.zip +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2962/framework-arduinoespressif32-all-release_v5.1-33fbade.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index db208e272f..632d2ef3fe 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -939,7 +939,7 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) if (Function == PLUGIN_EXIT) { UserVar.clear_computed(event->TaskIndex); clearPluginTaskData(event->TaskIndex); - clearTaskCache(event->TaskIndex); +// clearTaskCache(event->TaskIndex); // initSerial(); queueTaskEvent(F("TaskExit"), event->TaskIndex, retval); From 2310c5589a85e830aff31035164a0410aec63483 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 13:28:05 +0200 Subject: [PATCH 43/48] Add work-around for changes in File::position() Work-around for https://github.com/espressif/arduino-esp32/commit/0ab2c58b6c14f6dbc8b9ab0e61d776cd3ac5de66 --- .../DataStructs/RTC_cache_handler_struct.cpp | 43 ++++++++++++++++--- src/src/Helpers/AdafruitGFX_helper.cpp | 8 +++- .../Helpers/Hardware_temperature_sensor.cpp | 2 +- src/src/Helpers/RulesHelper.cpp | 10 ++++- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 7d22784277..585c8ceef9 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -75,7 +75,12 @@ bool RTC_cache_handler_struct::peekDataAvailable() const { if (_peekfilenr == RTC_cache.writeFileNr) { if (fw) { - return ((_peekreadpos + 1) < fw.position()); + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + pos = 0; + } + return ((_peekreadpos + 1) < pos); } // return true; } @@ -85,8 +90,17 @@ bool RTC_cache_handler_struct::peekDataAvailable() const { int RTC_cache_handler_struct::getPeekFilePos(int& peekFileNr) { peekFileNr = _peekfilenr; if (fp) { - _peekreadpos = fp.position(); + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + _peekreadpos = 0; + return -1; + } + _peekreadpos = pos; } + + if (_peekreadpos == errorcode) + return -1; return _peekreadpos; } @@ -101,7 +115,13 @@ void RTC_cache_handler_struct::setPeekFilePos(int newPeekFileNr, int newPeekRead validateFilePos(newPeekFileNr, newPeekReadPos); if (fp) { - if (newPeekReadPos < static_cast(fp.position())) { + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + pos = 0; + } + + if (newPeekReadPos < static_cast(pos)) { _peekfilenr = newPeekFileNr; _peekreadpos = newPeekReadPos; fp.close(); @@ -135,7 +155,14 @@ void RTC_cache_handler_struct::setPeekFilePos(int newPeekFileNr, int newPeekRead if (fileSize <= newPeekReadPos) { fp.seek(0, fs::SeekEnd); - _peekreadpos = fp.position(); + + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + pos = 0; + } + + _peekreadpos = pos; return; } @@ -170,7 +197,13 @@ bool RTC_cache_handler_struct::peek(uint8_t *data, unsigned int size) { const size_t bytesRead = fp.read(data, size); - _peekreadpos = fp.position(); + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + pos = 0; + } + + _peekreadpos = pos; if (_peekreadpos >= fp.size()) { if (_peekfilenr < RTC_cache.writeFileNr) { diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index 4bab47c637..941ef646d3 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -3327,7 +3327,13 @@ bool AdafruitGFX_helper::showBmp(const String& filename, bitIn = 7 - (loadX & 7); } - if (file.position() != bmpPos) { // Need seek? + constexpr size_t errorcode = (size_t)-1; + size_t pos = fp.position(); + if (pos == errorcode) { + pos = 0; + } + + if (pos != bmpPos) { // Need seek? if (transact && canTransact) { _tft->dmaWait(); _tft->endWrite(); // End TFT SPI transaction diff --git a/src/src/Helpers/Hardware_temperature_sensor.cpp b/src/src/Helpers/Hardware_temperature_sensor.cpp index f46e08c029..64fa9aebde 100644 --- a/src/src/Helpers/Hardware_temperature_sensor.cpp +++ b/src/src/Helpers/Hardware_temperature_sensor.cpp @@ -49,7 +49,7 @@ esp_err_t do_read_internal_temperature(float& celsius) { --retries; } # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, concat(F("ESP32: Raw temperature value: "), raw)); +// addLog(LOG_LEVEL_DEBUG, concat(F("ESP32: Raw temperature value: "), raw)); # endif // ifndef BUILD_NO_DEBUG if (raw != 128) { diff --git a/src/src/Helpers/RulesHelper.cpp b/src/src/Helpers/RulesHelper.cpp index aeb9fc2b51..f39fd6612f 100644 --- a/src/src/Helpers/RulesHelper.cpp +++ b/src/src/Helpers/RulesHelper.cpp @@ -219,7 +219,9 @@ size_t RulesHelperClass::read(const String& filename, size_t& pos, uint8_t *buff } if (it->second.position() != pos) { - it->second.seek(pos); + if (!it->second.seek(pos)) { + return 0; + } } const size_t ret = it->second.read(buffer, length); @@ -366,6 +368,12 @@ String RulesHelperClass::readLn(const String& filename, const size_t startPos = pos; int len = read(filename, pos, &buf[0], RULES_BUFFER_SIZE); moreAvailable = len != 0; + // Due to change in Arduino code, pos may now also be (size_t)-1 + // See: https://github.com/espressif/arduino-esp32/commit/0ab2c58b6c14f6dbc8b9ab0e61d776cd3ac5de66 + constexpr size_t errorcode = (size_t)-1; + if (pos == errorcode) { + moreAvailable = false; + } if (!moreAvailable) { done = true; } From 40b42b3e758f32e3699ca08449ebb3a9ee50e73c Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 15:15:28 +0200 Subject: [PATCH 44/48] [Build] Fix copy/paste error --- src/src/Helpers/AdafruitGFX_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index 941ef646d3..30e2b5d298 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -3328,7 +3328,7 @@ bool AdafruitGFX_helper::showBmp(const String& filename, } constexpr size_t errorcode = (size_t)-1; - size_t pos = fp.position(); + size_t pos = file.position(); if (pos == errorcode) { pos = 0; } From 5b6a7685cf5dad8e1f61dff642b5f3e4b2c8c168 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 15:20:02 +0200 Subject: [PATCH 45/48] [Cleanup] Remove duplicate define from platformio ini file --- platformio_core_defs.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index f2e9f27a58..5200a45e78 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -81,7 +81,6 @@ build_flags = -DNDEBUG -fno-strict-aliasing -DLIBRARIES_NO_LOG=1 -DNO_GLOBAL_I2S - -DPR_9453_FLUSH_TO_CLEAR=flush -I$PROJECT_DIR/src/include -include "ESPEasy_config.h" -O2 From 88fb2bfa42074d5ddf2f5fb78887e21f286353f5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 16:33:19 +0200 Subject: [PATCH 46/48] [Build] Fix max builds --- src/src/DataStructs/RTC_cache_handler_struct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/DataStructs/RTC_cache_handler_struct.cpp b/src/src/DataStructs/RTC_cache_handler_struct.cpp index 585c8ceef9..28027fc833 100644 --- a/src/src/DataStructs/RTC_cache_handler_struct.cpp +++ b/src/src/DataStructs/RTC_cache_handler_struct.cpp @@ -89,8 +89,8 @@ bool RTC_cache_handler_struct::peekDataAvailable() const { int RTC_cache_handler_struct::getPeekFilePos(int& peekFileNr) { peekFileNr = _peekfilenr; + constexpr size_t errorcode = (size_t)-1; if (fp) { - constexpr size_t errorcode = (size_t)-1; size_t pos = fp.position(); if (pos == errorcode) { _peekreadpos = 0; From 0eb16699f54cf1e91b7aaf0b965470a5eaa96bf2 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 17:08:19 +0200 Subject: [PATCH 47/48] [Uncrustify] Update config for latest version of Uncrustify --- uncrustify.cfg | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/uncrustify.cfg b/uncrustify.cfg index 1493e5a33c..72537534e4 100644 --- a/uncrustify.cfg +++ b/uncrustify.cfg @@ -148,7 +148,7 @@ sp_paren_paren = remove # ignore/add/remove/force sp_cparen_oparen = ignore # ignore/add/remove/force # Whether to balance spaces inside nested parentheses. -sp_balance_nested_parens = false # true/false +sp_paren_paren = false # true/false # Add or remove space between ')' and '{'. sp_paren_brace = add # ignore/add/remove/force @@ -362,7 +362,7 @@ sp_before_ellipsis = ignore # ignore/add/remove/force sp_type_ellipsis = ignore # ignore/add/remove/force # (D) Add or remove space between a type and '?'. -sp_type_question = ignore # ignore/add/remove/force +sp_before_ptr_star = ignore # ignore/add/remove/force # Add or remove space between ')' and '...'. sp_paren_ellipsis = ignore # ignore/add/remove/force @@ -847,10 +847,10 @@ sp_inside_newop_paren_open = ignore # ignore/add/remove/force sp_inside_newop_paren_close = ignore # ignore/add/remove/force # Add or remove space before a trailing or embedded comment. -sp_before_tr_emb_cmt = ignore # ignore/add/remove/force +sp_before_tr_cmt = ignore # ignore/add/remove/force # Number of spaces before a trailing or embedded comment. -sp_num_before_tr_emb_cmt = 0 # unsigned number +sp_num_before_tr_cmt = 0 # unsigned number # (Java) Add or remove space between an annotation and the open parenthesis. sp_annotation_paren = ignore # ignore/add/remove/force @@ -1000,7 +1000,7 @@ indent_var_def_cont = false # true/false # Whether to indent continued shift expressions ('<<' and '>>') instead of # aligning. Set align_left_shift=false when enabling this. -indent_shift = false # true/false +indent_shift = 0 # true/false # Whether to force indentation of function definitions to start in column 1. indent_func_def_force_col1 = false # true/false @@ -1045,7 +1045,7 @@ indent_member = 0 # unsigned number indent_member_single = false # true/false # Spaces to indent single line ('//') comments on lines before code. -indent_sing_line_comments = 0 # unsigned number +indent_single_line_comments_before = 0 # unsigned number # Whether to indent trailing single line ('//') comments relative to the code # instead of trying to keep the same absolute column. @@ -1116,11 +1116,11 @@ indent_paren_after_func_call = false # true/false # Whether to indent a comma when inside a parenthesis. # If true, aligns under the open parenthesis. -indent_comma_paren = false # true/false +indent_comma_paren = 0 # true/false # Whether to indent a Boolean operator when inside a parenthesis. # If true, aligns under the open parenthesis. -indent_bool_paren = false # true/false +indent_bool_paren = 0 # true/false # Whether to indent a semicolon when inside a for parenthesis. # If true, aligns under the open for parenthesis. @@ -1828,7 +1828,7 @@ nl_after_func_body_one_liner = 2 # unsigned number # of a function body. # # 0 = No change (default). -nl_func_var_def_blk = 1 # unsigned number +nl_var_def_blk_end_func_top = 1 # unsigned number # The number of newlines before a block of typedefs. If nl_after_access_spec # is non-zero, that option takes precedence. @@ -2296,7 +2296,7 @@ align_oc_msg_spec_span = 0 # unsigned number # Whether to align macros wrapped with a backslash and a newline. This will # not work right if the macro contains a multi-line comment. -align_nl_cont = true # true/false +align_nl_cont = 1 # true/false # Whether to align macro functions and variables together. align_pp_define_together = false # true/false @@ -2480,7 +2480,7 @@ mod_full_brace_if = add # ignore/add/remove/force # blocks. # # Overrides mod_full_brace_if. -mod_full_brace_if_chain = false # true/false +mod_full_brace_if_chain = 0 # true/false # Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. # If true, mod_full_brace_if_chain will only remove braces from an 'if' that @@ -2623,7 +2623,7 @@ pp_indent_at_level = false # true/false pp_indent_count = 1 # unsigned number # Add or remove space after # based on pp_level of #if blocks. -pp_space = add # ignore/add/remove/force +pp_space_after = add # ignore/add/remove/force # Sets the number of spaces per level added with pp_space. pp_space_count = 0 # unsigned number @@ -2678,7 +2678,10 @@ pp_indent_extern = true # true/false # inside of. # # Default: true -pp_indent_brace = true # true/false +pp_indent_brace = 1 # true/false + +# Sets the number of spaces per level added with pp_space_after. +pp_space_count = 1 # unsigned number # # Sort includes options From f6a692167e6284bf1abbef102bdaf8a846f8199f Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 25 Sep 2024 17:36:56 +0200 Subject: [PATCH 48/48] [uncrustify] Apply new uncrustify version on some recently changed code --- .../Helpers/Hardware_temperature_sensor.cpp | 4 +- src/src/Helpers/Misc.cpp | 128 ++--- src/src/Helpers/RulesHelper.cpp | 13 +- src/src/WebServer/DevicesPage.cpp | 295 ++++++----- src/src/WebServer/ESPEasy_WebServer.cpp | 480 ++++++++++-------- src/src/WebServer/WebTemplateParser.cpp | 36 +- 6 files changed, 523 insertions(+), 433 deletions(-) diff --git a/src/src/Helpers/Hardware_temperature_sensor.cpp b/src/src/Helpers/Hardware_temperature_sensor.cpp index 64fa9aebde..6420ca5d6c 100644 --- a/src/src/Helpers/Hardware_temperature_sensor.cpp +++ b/src/src/Helpers/Hardware_temperature_sensor.cpp @@ -49,7 +49,8 @@ esp_err_t do_read_internal_temperature(float& celsius) { --retries; } # ifndef BUILD_NO_DEBUG -// addLog(LOG_LEVEL_DEBUG, concat(F("ESP32: Raw temperature value: "), raw)); + + // addLog(LOG_LEVEL_DEBUG, concat(F("ESP32: Raw temperature value: "), raw)); # endif // ifndef BUILD_NO_DEBUG if (raw != 128) { @@ -152,6 +153,7 @@ esp_err_t do_read_internal_temperature(float& celsius) { if (ESP_OK == result) { result = temperature_sensor_get_celsius(temp_sensor, &celsius); + if (result == ESP_FAIL) { must_reinstall = true; } diff --git a/src/src/Helpers/Misc.cpp b/src/src/Helpers/Misc.cpp index 946c12a239..966de49e38 100644 --- a/src/src/Helpers/Misc.cpp +++ b/src/src/Helpers/Misc.cpp @@ -15,8 +15,8 @@ #include "../Helpers/StringParser.h" #if FEATURE_SD -#include -#endif +# include +#endif // if FEATURE_SD bool remoteConfig(struct EventStruct *event, const String& string) @@ -103,9 +103,10 @@ bool setTaskEnableStatus(struct EventStruct *event, bool enabled) if (!enabled) { PluginCall(PLUGIN_EXIT, event, dummy); } + // Toggle enable/disable state via command // FIXME TD-er: Should this be a 'runtime' change, or actually change the intended state? - //Settings.TaskDeviceEnabled[event->TaskIndex].enabled = enabled; + // Settings.TaskDeviceEnabled[event->TaskIndex].enabled = enabled; Settings.TaskDeviceEnabled[event->TaskIndex] = enabled; if (enabled) { @@ -131,6 +132,7 @@ void taskClear(taskIndex_t taskIndex, bool save) #ifndef BUILD_NO_RAM_TRACKER checkRAM(F("taskClear")); #endif // ifndef BUILD_NO_RAM_TRACKER + if (Settings.TaskDeviceEnabled[taskIndex]) { struct EventStruct TempEvent(taskIndex); String dummy; @@ -138,7 +140,7 @@ void taskClear(taskIndex_t taskIndex, bool save) } Settings.clearTask(taskIndex); clearTaskCache(taskIndex); // Invalidate any cached values. - ExtraTaskSettings.clear(); + ExtraTaskSettings.clear(); ExtraTaskSettings.TaskIndex = taskIndex; if (save) { @@ -232,6 +234,7 @@ String getTaskDeviceName(taskIndex_t TaskIndex) { \*********************************************************************************************/ String getTaskValueName(taskIndex_t TaskIndex, uint8_t TaskValueIndex) { const int valueCount = getValueCountForTask(TaskIndex); + if (TaskValueIndex < valueCount) { return Cache.getTaskDeviceValueName(TaskIndex, TaskValueIndex); } @@ -300,39 +303,41 @@ void SendValueLogger(taskIndex_t TaskIndex) featureSD = true; # endif // if FEATURE_SD - if (featureSD + if (featureSD # ifndef BUILD_NO_DEBUG || loglevelActiveFor(LOG_LEVEL_DEBUG) - #endif - ) { + # endif // ifndef BUILD_NO_DEBUG + ) { const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(TaskIndex); if (validDeviceIndex(DeviceIndex)) { const uint8_t valueCount = getValueCountForTask(TaskIndex); - const String logline_prefix = strformat(F("%s %s,%d,%s") - , node_time.getDateString('-').c_str() - , node_time.getTimeString(':').c_str() - , Settings.Unit - , getTaskDeviceName(TaskIndex).c_str() - ); + const String logline_prefix = + strformat(F("%s %s,%d,%s") + , node_time.getDateString('-').c_str() + , node_time.getTimeString(':').c_str() + , Settings.Unit + , getTaskDeviceName(TaskIndex).c_str() + ); for (uint8_t varNr = 0; varNr < valueCount; varNr++) { logger += strformat(F("%s,%s,%s\r\n") - , logline_prefix.c_str() - , Cache.getTaskDeviceValueName(TaskIndex, varNr).c_str() - , formatUserVarNoCheck(TaskIndex, varNr).c_str() - ); + , logline_prefix.c_str() + , Cache.getTaskDeviceValueName(TaskIndex, varNr).c_str() + , formatUserVarNoCheck(TaskIndex, varNr).c_str() + ); } # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, logger); - #endif + # endif // ifndef BUILD_NO_DEBUG } } #endif // if !defined(BUILD_NO_DEBUG) || FEATURE_SD #if FEATURE_SD + if (!logger.isEmpty()) { String filename = patch_fname(F("VALUES.CSV")); fs::File logFile = SD.open(filename, "a+"); @@ -355,52 +360,54 @@ void HSV2RGB(float H, float S, float I, int rgb[3]) { // FIXME TD-er: Why not just call HSV2RGBW and leave out the W part? int rgbw[4]{}; + HSV2RGBW(H, S, I, rgbw); memcpy(rgb, rgbw, 3 * sizeof(int)); - /* - int r, g, b; + /* - H = fmod(H, 360); // cycle H around to 0-360 degrees - constexpr float deg2rad = 3.14159f / 180.0f; - H *= deg2rad; // Convert to radians. - S = S / 100; - S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1] - I = I / 100; - I = I > 0 ? (I < 1 ? I : 1) : 0; - - // Math! Thanks in part to Kyle Miller. - if (H < 2.09439f) { - r = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); - g = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); - b = 255 * I / 3 * (1 - S); - } else if (H < 4.188787f) { - H = H - 2.09439f; - g = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); - b = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); - r = 255 * I / 3 * (1 - S); - } else { - H = H - 4.188787f; - b = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); - r = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); - g = 255 * I / 3 * (1 - S); - } - rgb[0] = r; - rgb[1] = g; - rgb[2] = b; - */ + int r, g, b; + + H = fmod(H, 360); // cycle H around to 0-360 degrees + constexpr float deg2rad = 3.14159f / 180.0f; + H *= deg2rad; // Convert to radians. + S = S / 100; + S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1] + I = I / 100; + I = I > 0 ? (I < 1 ? I : 1) : 0; + + // Math! Thanks in part to Kyle Miller. + if (H < 2.09439f) { + r = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); + g = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); + b = 255 * I / 3 * (1 - S); + } else if (H < 4.188787f) { + H = H - 2.09439f; + g = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); + b = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); + r = 255 * I / 3 * (1 - S); + } else { + H = H - 4.188787f; + b = 255 * I / 3 * (1 + S * cosf(H) / cosf(1.047196667f - H)); + r = 255 * I / 3 * (1 + S * (1 - cosf(H) / cosf(1.047196667f - H))); + g = 255 * I / 3 * (1 - S); + } + rgb[0] = r; + rgb[1] = g; + rgb[2] = b; + */ } // uses H 0..360 S 1..100 I/V 1..100 (according to homie convention) // Source https://blog.saikoled.com/post/44677718712/how-to-convert-from-hsi-to-rgb-white void HSV2RGBW(float H, float S, float I, int rgbw[4]) { - H = fmod(H, 360); // cycle H around to 0-360 degrees + H = fmod(H, 360); // cycle H around to 0-360 degrees constexpr float deg2rad = 3.14159f / 180.0f; - H *= deg2rad; // Convert to radians. - S = S / 100; - S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1] - I = I / 100; - I = I > 0 ? (I < 1 ? I : 1) : 0; + H *= deg2rad; // Convert to radians. + S = S / 100; + S = S > 0 ? (S < 1 ? S : 1) : 0; // clamp S and I to interval [0,1] + I = I / 100; + I = I > 0 ? (I < 1 ? I : 1) : 0; #define RGB_ORDER 0 #define GBR_ORDER 1 @@ -427,7 +434,7 @@ void HSV2RGBW(float H, float S, float I, int rgbw[4]) { const int r = S * 255 * I / 3 * (1 + cos_h / cos_1047_h); const int g = S * 255 * I / 3 * (1 + (1 - cos_h / cos_1047_h)); const int b = 0; - rgbw[3] = 255 * (1 - S) * I; + rgbw[3] = 255 * (1 - S) * I; if (RGB_ORDER == order) { rgbw[0] = r; @@ -446,10 +453,10 @@ void HSV2RGBW(float H, float S, float I, int rgbw[4]) { // Convert RGB Color to HSV Color void RGB2HSV(uint8_t r, uint8_t g, uint8_t b, float hsv[3]) { - const float rf = static_cast(r) / 255.0f; - const float gf = static_cast(g) / 255.0f; - const float bf = static_cast(b) / 255.0f; - float maxval = rf; + const float rf = static_cast(r) / 255.0f; + const float gf = static_cast(g) / 255.0f; + const float bf = static_cast(b) / 255.0f; + float maxval = rf; if (gf > maxval) { maxval = gf; } @@ -482,8 +489,6 @@ void RGB2HSV(uint8_t r, uint8_t g, uint8_t b, float hsv[3]) { hsv[2] = v * 255.0f; } - - float getCPUload() { return 100.0f - Scheduler.getIdleTimePct(); } @@ -522,6 +527,7 @@ void logMemUsageAfter(const __FlashStringHelper *function, int value) { if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String log; + if (log.reserve(128)) { log = F("After "); log += function; diff --git a/src/src/Helpers/RulesHelper.cpp b/src/src/Helpers/RulesHelper.cpp index f39fd6612f..828b1f7035 100644 --- a/src/src/Helpers/RulesHelper.cpp +++ b/src/src/Helpers/RulesHelper.cpp @@ -314,9 +314,9 @@ String RulesHelperClass::readLn(const String& filename, if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, strformat( - F("Rules : Read %u lines from %s"), - lines.size(), - filename.c_str())); + F("Rules : Read %u lines from %s"), + lines.size(), + filename.c_str())); } # endif // ifndef BUILD_NO_DEBUG _fileHandleMap.emplace(std::make_pair(filename, std::move(lines))); @@ -345,10 +345,11 @@ String RulesHelperClass::readLn(const String& filename, bool & moreAvailable, bool searchNextOnBlock) { - #ifdef USE_SECOND_HEAP + # ifdef USE_SECOND_HEAP + // Do not store in 2nd heap, this is only temporary and needs to be as fast as possible HeapSelectDram ephemeral; - #endif // ifdef USE_SECOND_HEAP + # endif // ifdef USE_SECOND_HEAP std::vector buf; @@ -368,9 +369,11 @@ String RulesHelperClass::readLn(const String& filename, const size_t startPos = pos; int len = read(filename, pos, &buf[0], RULES_BUFFER_SIZE); moreAvailable = len != 0; + // Due to change in Arduino code, pos may now also be (size_t)-1 // See: https://github.com/espressif/arduino-esp32/commit/0ab2c58b6c14f6dbc8b9ab0e61d776cd3ac5de66 constexpr size_t errorcode = (size_t)-1; + if (pos == errorcode) { moreAvailable = false; } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 3e24c13b41..4369876ea0 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -9,9 +9,9 @@ # include "../WebServer/Markup_Forms.h" # include "../DataStructs/NodeStruct.h" -#if FEATURE_PLUGIN_STATS -#include "../DataStructs/PluginStats_Config.h" -#endif +# if FEATURE_PLUGIN_STATS +# include "../DataStructs/PluginStats_Config.h" +# endif // if FEATURE_PLUGIN_STATS # include "../Globals/CPlugins.h" @@ -32,7 +32,6 @@ # include "../Helpers/StringGenerator_GPIO.h" - # include "../../_Plugin_Helper.h" # include @@ -140,7 +139,8 @@ void handle_devices() { if (!taskIndexNotSet) { --taskIndex; -// LoadTaskSettings(taskIndex); // Make sure ExtraTaskSettings are up-to-date + + // LoadTaskSettings(taskIndex); // Make sure ExtraTaskSettings are up-to-date } // FIXME TD-er: Might have to clear any caches here. @@ -152,8 +152,9 @@ void handle_devices() { setTaskDevice_to_TaskIndex(taskdevicenumber, taskIndex); const deviceIndex_t DeviceIndex = getDeviceIndex(taskdevicenumber); - if (validDeviceIndex(DeviceIndex)) { + if (validDeviceIndex(DeviceIndex)) { const DeviceStruct& device = Device[DeviceIndex]; + if ((device.Type == DEVICE_TYPE_I2C) && device.I2CMax100kHz) { // 100 kHz-only I2C device? bitWrite(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default } @@ -226,8 +227,10 @@ void addDeviceSelect(const __FlashStringHelper *name, pluginID_t choice) deviceIndex_t x; bool done = false; + while (!done) { const deviceIndex_t deviceIndex = getDeviceIndex_sorted(x); + if (!validDeviceIndex(deviceIndex)) { done = true; } else { @@ -242,8 +245,8 @@ void addDeviceSelect(const __FlashStringHelper *name, pluginID_t choice) # endif // if defined(PLUGIN_BUILD_DEV) || defined(PLUGIN_SET_MAX) addSelector_Item(deviceName, - pluginID.value, - choice == pluginID); + pluginID.value, + choice == pluginID); } } ++x; @@ -313,11 +316,12 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task case Output_Data_type_t::Simple: case Output_Data_type_t::All: { - int pconfigIndex = checkDeviceVTypeForTask(&TempEvent); + int pconfigIndex = checkDeviceVTypeForTask(&TempEvent); Sensor_VType VType = TempEvent.sensorType; if ((pconfigIndex >= 0) && (pconfigIndex < PLUGIN_CONFIGVAR_MAX)) { - VType = static_cast(getFormItemInt(sensorTypeHelper_webformID(pconfigIndex), 0)); + VType = + static_cast(getFormItemInt(sensorTypeHelper_webformID(pconfigIndex), 0)); Settings.TaskDevicePluginConfig[taskIndex][pconfigIndex] = static_cast(VType); } ExtraTaskSettings.clearUnusedValueNames(getValueCountFromSensorType(VType)); @@ -326,35 +330,40 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task } { - int pins[] = {-1, -1, -1}; + int pins[] = { -1, -1, -1 }; + for (int i = 0; i < 3; ++i) { update_whenset_FormItemInt(concat(F("taskdevicepin"), i + 1), pins[i]); } const bool taskEnabled = isFormItemChecked(F("TDE")); setBasicTaskValues(taskIndex, taskdevicetimer, - taskEnabled, webArg(F("TDN")), - pins); + taskEnabled, webArg(F("TDN")), + pins); } - #if FEATURE_PLUGIN_PRIORITY + # if FEATURE_PLUGIN_PRIORITY + if (device.PowerManager // Check extra priority device flags when available ) { bool disablePrio = false; + for (taskIndex_t t = 0; t < TASKS_MAX && !disablePrio; t++) { if (t != taskIndex) { disablePrio = Settings.isPriorityTask(t); } } bool statePriority = isFormItemChecked(F("TPRE")); + if (device.PowerManager) { Settings.setPowerManagerTask(taskIndex, statePriority); } + // Set alternative Priority flags // Set to readonly if set as Priority task Settings.setTaskEnableReadonly(taskIndex, statePriority); } - #endif // if FEATURE_PLUGIN_PRIORITY + # endif // if FEATURE_PLUGIN_PRIORITY Settings.TaskDevicePort[taskIndex] = getFormItemInt(F("TDP"), 0); update_whenset_FormItemInt(F("remoteFeed"), Settings.TaskDeviceDataFeed[taskIndex]); Settings.CombineTaskValues_SingleEvent(taskIndex, isFormItemChecked(F("TVSE"))); @@ -389,22 +398,22 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task strncpy_webserver_arg(ExtraTaskSettings.TaskDeviceFormula[varNr], getPluginCustomArgName(F("TDF"), varNr)); update_whenset_FormItemInt(getPluginCustomArgName(F("TDVD"), varNr), ExtraTaskSettings.TaskDeviceValueDecimals[varNr]); strncpy_webserver_arg(ExtraTaskSettings.TaskDeviceValueNames[varNr], getPluginCustomArgName(F("TDVN"), varNr)); -#if FEATURE_PLUGIN_FILTER +# if FEATURE_PLUGIN_FILTER ExtraTaskSettings.enablePluginFilter(varNr, isFormItemChecked(getPluginCustomArgName(F("TDFIL"), varNr))); -#endif -#if FEATURE_PLUGIN_STATS +# endif // if FEATURE_PLUGIN_FILTER +# if FEATURE_PLUGIN_STATS PluginStats_Config_t pluginStats_Config; pluginStats_Config.setEnabled(isFormItemChecked(getPluginCustomArgName(F("TDS"), varNr))); pluginStats_Config.setHidden(isFormItemChecked(getPluginCustomArgName(F("TDSH"), varNr))); const int selectedAxis = getFormItemInt(getPluginCustomArgName(F("TDSA"), varNr)); pluginStats_Config.setAxisIndex(selectedAxis); pluginStats_Config.setAxisPosition( - ((selectedAxis >> 2) == 0) - ? PluginStats_Config_t::AxisPosition::Left + ((selectedAxis >> 2) == 0) + ? PluginStats_Config_t::AxisPosition::Left : PluginStats_Config_t::AxisPosition::Right); ExtraTaskSettings.setPluginStatsConfig(varNr, pluginStats_Config); -#endif +# endif // if FEATURE_PLUGIN_STATS } ExtraTaskSettings.clearUnusedValueNames(valueCount); @@ -415,6 +424,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task // allow the plugin to save plugin-specific form settings. { String dummy; + if (device.ExitTaskBeforeSave) { PluginCall(PLUGIN_EXIT, &TempEvent, dummy); } @@ -429,17 +439,18 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task // Make sure the task needs to reload using the new settings. if (!device.ExitTaskBeforeSave) { PluginCall(PLUGIN_EXIT, &TempEvent, dummy); - } + } } // Store all PCONFIG values on the web page // Must be done after PLUGIN_WEBFORM_SAVE, to allow tasks to clear the default task value names - // Output type selectors are typically stored in PCONFIG + // Output type selectors are typically stored in PCONFIG if (device.OutputDataType != Output_Data_type_t::Default) { for (int pconfigIndex = 0; pconfigIndex < PLUGIN_CONFIGVAR_MAX; ++pconfigIndex) { pconfig_webformSave(&TempEvent, pconfigIndex); } } + // ExtraTaskSettings may have changed during PLUGIN_WEBFORM_SAVE, so again update the cache. Cache.updateExtraTaskSettingsCache(); @@ -459,16 +470,16 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task CPluginCall(ProtocolIndex, CPlugin::Function::CPLUGIN_TASK_CHANGE_NOTIFICATION, &TempEvent, dummy); } } + // FIXME TD-er: Is this still needed as it is also cleared on PLUGIN_INIT and PLUGIN_EXIT? UserVar.clear_computed(taskIndex); } - void html_add_setPage(uint8_t page, bool isLinkToPrev) { addHtml(strformat( - F("devices?setpage=%u'>&%ct;"), - static_cast(page), - isLinkToPrev ? 'l' : 'g')); + F("devices?setpage=%u'>&%ct;"), + static_cast(page), + isLinkToPrev ? 'l' : 'g')); } // ******************************************************************************** @@ -516,9 +527,9 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) { const int pageIndex = static_cast(x + 1); addHtml(strformat( - F("devices?index=%d&page=%u'>"), - pageIndex, - static_cast(page))); + F("devices?index=%d&page=%u'>"), + pageIndex, + static_cast(page))); addHtml(pluginID_set ? F("Edit") : F("Add")); addHtml(concat(F(""), pageIndex)); html_TD(); @@ -530,7 +541,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) // Editing a task which has a non supported plugin will present the same as when assigning a new plugin to a task. if (pluginID_set) { - //LoadTaskSettings(x); + // LoadTaskSettings(x); int8_t spi_gpios[3] { -1, -1, -1 }; struct EventStruct TempEvent(x); addEnabled(Settings.TaskDeviceEnabled[x] && validDeviceIndex(DeviceIndex)); @@ -543,11 +554,12 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (validDeviceIndex(DeviceIndex)) { if (Settings.TaskDeviceDataFeed[x] != 0) { - #if FEATURE_ESPEASY_P2P + # if FEATURE_ESPEASY_P2P + // Show originating node number const uint8_t remoteUnit = Settings.TaskDeviceDataFeed[x]; format_originating_node(remoteUnit); - #endif + # endif // if FEATURE_ESPEASY_P2P } else { String portDescr; @@ -555,22 +567,23 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) addHtml(portDescr); } else { const DeviceStruct& device = Device[DeviceIndex]; + if (device.Type == DEVICE_TYPE_I2C) { format_I2C_port_description(x); } else if (device.isSPI()) { format_SPI_port_description(spi_gpios); } else if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL - addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); + addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); # else // ifdef PLUGIN_USES_SERIAL - addHtml(F("PLUGIN_USES_SERIAL not defined")); + addHtml(F("PLUGIN_USES_SERIAL not defined")); # endif // ifdef PLUGIN_USES_SERIAL } else { - // Plugin has no custom port formatting, show default one. - if (device.Ports != 0) - { - addHtml(formatToHex_decimal(Settings.TaskDevicePort[x])); - } + // Plugin has no custom port formatting, show default one. + if (device.Ports != 0) + { + addHtml(formatToHex_decimal(Settings.TaskDevicePort[x])); + } } } } @@ -597,8 +610,8 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (getProtocolStruct(ProtocolIndex).usesID && (Settings.Protocol[controllerNr] != 0)) { addHtml(strformat( - F(" (%d)"), - static_cast(Settings.TaskDeviceID[controllerNr][x]))); + F(" (%d)"), + static_cast(Settings.TaskDeviceID[controllerNr][x]))); if (Settings.TaskDeviceID[controllerNr][x] == 0) { addHtml(' '); @@ -616,10 +629,11 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (validDeviceIndex(DeviceIndex)) { const DeviceStruct& device = Device[DeviceIndex]; + if (Settings.TaskDeviceDataFeed[x] == 0) { String description; - bool pluginHasGPIODescription = pluginWebformShowGPIOdescription(x, F("
"), description); + bool pluginHasGPIODescription = pluginWebformShowGPIOdescription(x, F("
"), description); bool showpin1 = false; bool showpin2 = false; @@ -667,17 +681,19 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) case DEVICE_TYPE_SERIAL: { # ifdef PLUGIN_USES_SERIAL - const String serialDescription = serialHelper_getGpioDescription(static_cast(Settings.TaskDevicePort[x]), Settings.TaskDevicePin1[x], - Settings.TaskDevicePin2[x], F("
")); + const String serialDescription = serialHelper_getGpioDescription(static_cast(Settings.TaskDevicePort[x]), + Settings.TaskDevicePin1[x], + Settings.TaskDevicePin2[x], + F("
")); addHtml(serialDescription); # else // ifdef PLUGIN_USES_SERIAL addHtml(F("PLUGIN_USES_SERIAL not defined")); # endif // ifdef PLUGIN_USES_SERIAL if ( -#ifdef PLUGIN_USES_SERIAL - serialDescription.length() || -#endif +# ifdef PLUGIN_USES_SERIAL + serialDescription.length() || +# endif // ifdef PLUGIN_USES_SERIAL showpin3) { html_BR(); } @@ -695,6 +711,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) case DEVICE_TYPE_CUSTOM0: { showpin1 = true; + if (pluginHasGPIODescription || (device.Type == DEVICE_TYPE_CUSTOM0)) { addHtml(description); showpin1 = false; @@ -754,9 +771,9 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (validPluginID_fullcheck(Settings.getPluginID_for_task(x))) { pluginWebformShowValue( - x, - varNr, - Cache.getTaskDeviceValueName(x, varNr), + x, + varNr, + Cache.getTaskDeviceValueName(x, varNr), formatUserVarNoCheck(&TempEvent, varNr)); } } @@ -771,7 +788,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) html_end_form(); } -#if FEATURE_ESPEASY_P2P +# if FEATURE_ESPEASY_P2P void format_originating_node(uint8_t remoteUnit) { addHtml(F("Unit ")); addHtmlInt(remoteUnit); @@ -787,13 +804,15 @@ void format_originating_node(uint8_t remoteUnit) { } } } -#endif + +# endif // if FEATURE_ESPEASY_P2P void format_I2C_port_description(taskIndex_t x) { addHtml(F("I2C")); # if FEATURE_I2C_GET_ADDRESS const uint8_t i2cAddr = getTaskI2CAddress(x); + if (i2cAddr > 0) { addHtml(' '); addHtml(formatToHex(i2cAddr, 2)); @@ -816,7 +835,7 @@ void format_I2C_port_description(taskIndex_t x) } } } else { // Single channel - mux = concat(F("
Multiplexer channel "), static_cast(Settings.I2C_Multiplexer_Channel[x])); + mux = concat(F("
Multiplexer channel "), static_cast(Settings.I2C_Multiplexer_Channel[x])); } addHtml(mux); } @@ -849,13 +868,16 @@ void format_I2C_pin_description(taskIndex_t x) void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin) { if (Settings.InitSPI > static_cast(SPI_Options_e::None)) { - const __FlashStringHelper* labels[] = { F("CLK"), F("MISO"), F("MOSI") }; + const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI") }; + for (int i = 0; i < 3; ++i) { - if (i != 0) + if (i != 0) { html_BR(); + } Label_Gpio_toHtml(labels[i], formatGpioLabel(spi_gpios[i], false)); } + if (showCSpin) { html_BR(); Label_Gpio_toHtml(F("CS"), formatGpioLabel(Settings.TaskDevicePin1[x], false)); @@ -872,7 +894,7 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) const deviceIndex_t DeviceIndex = getDeviceIndex_from_TaskIndex(taskIndex); - //LoadTaskSettings(taskIndex); + // LoadTaskSettings(taskIndex); html_add_form(); html_table_class_normal(); @@ -893,6 +915,7 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) else { const DeviceStruct& device = Device[DeviceIndex]; + // remember selected device number addHtml(F("getNodeName()); @@ -951,7 +980,7 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) } addFormNote(F("0 = disable remote feed, 255 = broadcast")); // FIXME TD-er: Must verify if broadcast can be set. } - #endif + # endif // if FEATURE_ESPEASY_P2P bool addPinConfig = false; @@ -974,22 +1003,22 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) if (addPinConfig || (device.Type == DEVICE_TYPE_I2C)) { if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL - devicePage_show_serial_config(taskIndex); + devicePage_show_serial_config(taskIndex); # else // ifdef PLUGIN_USES_SERIAL - addHtml(F("PLUGIN_USES_SERIAL not defined")); + addHtml(F("PLUGIN_USES_SERIAL not defined")); # endif // ifdef PLUGIN_USES_SERIAL - devicePage_show_pin_config(taskIndex, DeviceIndex); - addPinConfig = false; + devicePage_show_pin_config(taskIndex, DeviceIndex); + addPinConfig = false; - html_add_script(F("document.getElementById('serPort').onchange();"), false); + html_add_script(F("document.getElementById('serPort').onchange();"), false); } else if (device.Type == DEVICE_TYPE_I2C) { - devicePage_show_pin_config(taskIndex, DeviceIndex); - addPinConfig = false; + devicePage_show_pin_config(taskIndex, DeviceIndex); + addPinConfig = false; - if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { - devicePage_show_I2C_config(taskIndex, DeviceIndex); - } + if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { + devicePage_show_I2C_config(taskIndex, DeviceIndex); + } } if (addPinConfig) { @@ -1002,28 +1031,31 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) // add plugins content - if (DEVICE_TYPE_DUMMY != device.Type && remoteUnit == 0) { + if ((DEVICE_TYPE_DUMMY != device.Type) && (remoteUnit == 0)) { addFormSubHeader(F("Device Settings")); } + if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { // only show additional config for local connected sensors PluginCall(PLUGIN_WEBFORM_LOAD, &TempEvent, webformLoadString); - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG + if (webformLoadString.length() > 0) { String errorMessage; PluginCall(PLUGIN_GET_DEVICENAME, &TempEvent, errorMessage); errorMessage += F(": Bug in PLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead"); addHtmlError(errorMessage); } - #endif + # endif // ifndef BUILD_NO_DEBUG } PluginCall(PLUGIN_WEBFORM_LOAD_ALWAYS, &TempEvent, webformLoadString); // Load settings also useful for remote-datafeed devices devicePage_show_output_data_type(taskIndex, DeviceIndex); - #if FEATURE_PLUGIN_STATS + # if FEATURE_PLUGIN_STATS + // Task statistics and historic data in a chart devicePage_show_task_statistics(taskIndex, DeviceIndex); - #endif // if FEATURE_PLUGIN_STATS + # endif // if FEATURE_PLUGIN_STATS // section: Data Acquisition devicePage_show_controller_config(taskIndex, DeviceIndex); @@ -1041,9 +1073,10 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addHtml(F("devices?setpage=")); addHtmlInt(page); addHtml(F("'>Close")); - #if FEATURE_PLUGIN_PRIORITY + # if FEATURE_PLUGIN_PRIORITY + if (!Settings.isPriorityTask(taskIndex)) - #endif // if FEATURE_PLUGIN_PRIORITY + # endif // if FEATURE_PLUGIN_PRIORITY { addSubmitButton(); } @@ -1052,26 +1085,28 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) // if user selected a device, add the delete button, except for Priority tasks if (validPluginID_fullcheck(Settings.getPluginID_for_task(taskIndex)) - #if FEATURE_PLUGIN_PRIORITY + # if FEATURE_PLUGIN_PRIORITY && !Settings.isPriorityTask(taskIndex) - #endif // if FEATURE_PLUGIN_PRIORITY - ) { + # endif // if FEATURE_PLUGIN_PRIORITY + ) { addSubmitButton(F("Delete"), F("del")); } html_end_table(); - #if FEATURE_PLUGIN_PRIORITY + # if FEATURE_PLUGIN_PRIORITY + if (Settings.isPriorityTask(taskIndex)) { addFormNote(F("A Priority task can't be updated or deleted. See documentation.")); } - #endif // if FEATURE_PLUGIN_PRIORITY + # endif // if FEATURE_PLUGIN_PRIORITY html_end_form(); serve_JS(JSfiles_e::SplitPasteInput); } void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { - const DeviceStruct &device = Device[DeviceIndex]; + const DeviceStruct& device = Device[DeviceIndex]; + if (device.PullUpOption) { addFormCheckBox(F("Internal PullUp"), F("TDPPU"), Settings.TaskDevicePin1PullUp[taskIndex]); // ="taskdevicepin1pullup" @@ -1130,6 +1165,7 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Serial Pin2 = GPIO ---> RX purpose = PinSelectPurpose::Serial_output; } + if (device.isSPI()) { // SPI only needs output pins @@ -1151,7 +1187,7 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } } -#ifdef PLUGIN_USES_SERIAL +# ifdef PLUGIN_USES_SERIAL void devicePage_show_serial_config(taskIndex_t taskIndex) { struct EventStruct TempEvent(taskIndex); @@ -1164,7 +1200,8 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) PluginCall(PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS, &TempEvent, webformLoadString); } -#endif + +# endif // ifdef PLUGIN_USES_SERIAL void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { @@ -1180,6 +1217,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex PluginCall(PLUGIN_WEBFORM_SHOW_I2C_PARAMS, &TempEvent, dummy); addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); + if (Device[DeviceIndex].I2CMax100kHz) { addFormNote(F("This device is specified for max. 100 kHz operation!")); } @@ -1192,9 +1230,9 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), - F("Multiple channels")}; - constexpr int i2c_mux_channelOptions[] = { 0, 1}; - int i2c_mux_channelCount = 1; + F("Multiple channels") }; + constexpr int i2c_mux_channelOptions[] = { 0, 1 }; + int i2c_mux_channelCount = 1; if (Settings.I2C_Multiplexer_Type == I2C_MULTIPLEXER_PCA9540) { multipleMuxPorts = false; // force off @@ -1228,12 +1266,12 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex html_end_table(); } else { int taskDeviceI2CMuxPort = Settings.I2C_Multiplexer_Channel[taskIndex]; - const uint32_t mux_max = I2CMultiplexerMaxChannels(); - String i2c_mux_portoptions[mux_max + 1]; - int i2c_mux_portchoices[mux_max + 1]; + const uint32_t mux_max = I2CMultiplexerMaxChannels(); + String i2c_mux_portoptions[mux_max + 1]; + int i2c_mux_portchoices[mux_max + 1]; i2c_mux_portoptions[0] = F("(Not connected via multiplexer)"); i2c_mux_portchoices[0] = -1; - + for (uint32_t x = 0; x < mux_max; x++) { const uint32_t mux_opt = x + 1; i2c_mux_portoptions[mux_opt] = concat(F("Channel "), x); @@ -1281,7 +1319,7 @@ void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t Devic PluginCall(PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR, &TempEvent, dummy); } -#if FEATURE_PLUGIN_STATS +# if FEATURE_PLUGIN_STATS void devicePage_show_task_statistics(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { if (Device[DeviceIndex].PluginStats) @@ -1292,13 +1330,13 @@ void devicePage_show_task_statistics(taskIndex_t taskIndex, deviceIndex_t Device if (taskData->hasPluginStats()) { addFormSubHeader(F("Statistics")); } - #if FEATURE_CHART_JS + # if FEATURE_CHART_JS + if (taskData->nrSamplesPresent() > 0) { addRowLabel(F("Historic data")); taskData->plot_ChartJS(); - } - #endif // if FEATURE_CHART_JS + # endif // if FEATURE_CHART_JS struct EventStruct TempEvent(taskIndex); String dummy; @@ -1311,20 +1349,20 @@ void devicePage_show_task_statistics(taskIndex_t taskIndex, deviceIndex_t Device if (somethingAdded) { if (taskData->hasPeaks()) { addFormNote(strformat( - F("Peak values recorded since last \"%s.resetpeaks\"."), - getTaskDeviceName(taskIndex).c_str())); + F("Peak values recorded since last \"%s.resetpeaks\"."), + getTaskDeviceName(taskIndex).c_str())); } } } } } -#endif // if FEATURE_PLUGIN_STATS +# endif // if FEATURE_PLUGIN_STATS void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { - if (!validDeviceIndex(DeviceIndex)) return; + if (!validDeviceIndex(DeviceIndex)) { return; } const DeviceStruct& device = Device[DeviceIndex]; @@ -1342,10 +1380,11 @@ void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t Devi addRowLabel(F("Single event with all values")); addCheckBox(F("TVSE"), Settings.CombineTaskValues_SingleEvent(taskIndex)); addFormNote(strformat( - F("Unchecked: Send event per value. Checked: Send single event (%s#All) containing all values"), - getTaskDeviceName(taskIndex).c_str())); + F("Unchecked: Send event per value. Checked: Send single event (%s#All) containing all values"), + getTaskDeviceName(taskIndex).c_str())); bool separatorAdded = false; + for (controllerIndex_t controllerNr = 0; controllerNr < CONTROLLER_MAX; controllerNr++) { if (Settings.Protocol[controllerNr] != 0) @@ -1361,7 +1400,7 @@ void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t Devi (Settings.ControllerEnabled[controllerNr] ? F("enabled") : F("disabled")))); html_TD(); - addHtml(F("")); // remove left padding 2x to align vertically with other inputs + addHtml(F("
")); // remove left padding 2x to align vertically with other inputs html_TD(F("width:50px;padding-left:0")); addCheckBox( getPluginCustomArgName(F("TDSD"), controllerNr), // ="taskdevicesenddata" @@ -1369,7 +1408,7 @@ void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t Devi protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerNr); - if (validProtocolIndex(ProtocolIndex) && + if (validProtocolIndex(ProtocolIndex) && getProtocolStruct(ProtocolIndex).usesID && (Settings.Protocol[controllerNr] != 0)) { html_TD(); addHtml(F("IDX:")); @@ -1386,7 +1425,7 @@ void devicePage_show_controller_config(taskIndex_t taskIndex, deviceIndex_t Devi void devicePage_show_interval_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { - if (!validDeviceIndex(DeviceIndex)) return; + if (!validDeviceIndex(DeviceIndex)) { return; } const DeviceStruct& device = Device[DeviceIndex]; @@ -1405,7 +1444,8 @@ void devicePage_show_interval_config(taskIndex_t taskIndex, deviceIndex_t Device void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { - if (!validDeviceIndex(DeviceIndex)) return; + if (!validDeviceIndex(DeviceIndex)) { return; } + // section: Values const uint8_t valueCount = getValueCountForTask(taskIndex); @@ -1420,7 +1460,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde // table header addHtml(F("
#")); - html_table_header(F("Name"),500); + html_table_header(F("Name"), 500); if (device.FormulaOption) { @@ -1434,22 +1474,23 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde ++colCount; } -#if FEATURE_PLUGIN_STATS +# if FEATURE_PLUGIN_STATS + if (device.PluginStats) { html_table_header(F("Stats"), 30); ++colCount; - html_table_header(F("Hide"), 30); + html_table_header(F("Hide"), 30); ++colCount; - html_table_header(F("Axis"), 30); + html_table_header(F("Axis"), 30); ++colCount; } -#endif +# endif // if FEATURE_PLUGIN_STATS - //placeholder header + // placeholder header html_table_header(F("")); ++colCount; - + // table body for (uint8_t varNr = 0; varNr < valueCount; varNr++) { @@ -1475,7 +1516,8 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde addNumericBox(id, Cache.getTaskDeviceValueDecimals(taskIndex, varNr), 0, 6); } -#if FEATURE_PLUGIN_STATS +# if FEATURE_PLUGIN_STATS + if (device.PluginStats) { PluginStats_Config_t cachedConfig = Cache.getPluginStatsConfig(taskIndex, varNr); @@ -1486,7 +1528,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde html_TD(); addCheckBox( - getPluginCustomArgName(F("TDSH"), varNr), // ="taskdevicestats Hidden" + getPluginCustomArgName(F("TDSH"), varNr), // ="taskdevicestats Hidden" cachedConfig.showHidden()); html_TD(); @@ -1503,22 +1545,23 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde }; int selected = cachedConfig.getAxisIndex(); + if (!cachedConfig.isLeft()) { selected += 4; } addSelector( getPluginCustomArgName(F("TDSA"), varNr), - NR_ELEMENTS(chartAxis), - chartAxis, + NR_ELEMENTS(chartAxis), + chartAxis, nullptr, nullptr, selected); } -#endif +# endif // if FEATURE_PLUGIN_STATS } addFormSeparator(colCount); } } -#endif // ifdef WEBSERVER_DEVICES \ No newline at end of file +#endif // ifdef WEBSERVER_DEVICES diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 8071ddf7a4..467efb1b12 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -72,16 +72,16 @@ void safe_strncpy_webserver_arg(char *dest, const String& arg, size_t max_size) { - if (hasArg(arg)) { - safe_strncpy(dest, webArg(arg).c_str(), max_size); + if (hasArg(arg)) { + safe_strncpy(dest, webArg(arg).c_str(), max_size); } } -void safe_strncpy_webserver_arg(char *dest, const __FlashStringHelper * arg, size_t max_size) { +void safe_strncpy_webserver_arg(char *dest, const __FlashStringHelper *arg, size_t max_size) { safe_strncpy_webserver_arg(dest, String(arg), max_size); } -void sendHeadandTail(const __FlashStringHelper * tmplName, bool Tail, bool rebooting) { +void sendHeadandTail(const __FlashStringHelper *tmplName, bool Tail, bool rebooting) { // This function is called twice per serving a web page. // So it must keep track of the timer longer than the scope of this function. // Therefore use a local static variable. @@ -94,12 +94,14 @@ void sendHeadandTail(const __FlashStringHelper * tmplName, bool Tail, bool reboo #endif // if FEATURE_TIMING_STATS { const String fileName = concat(tmplName, F(".htm")); - fs::File f = tryOpenFile(fileName, "r"); + fs::File f = tryOpenFile(fileName, "r"); WebTemplateParser templateParser(Tail, rebooting); + if (f) { bool success = true; - while (f.available() && success) { + + while (f.available() && success) { success = templateParser.process((char)f.read()); } f.close(); @@ -130,36 +132,38 @@ void sendHeadandTail_stdtemplate(bool Tail, bool rebooting) { } #ifndef BUILD_NO_DEBUG -/* - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - const int nrArgs = web_server.args(); - if (nrArgs > 0) { - String log = F(" Webserver "); - log += nrArgs; - log += F(" Arguments"); - - if (nrArgs > 20) { - log += F(" (First 20)"); - } - log += ':'; - - for (int i = 0; i < nrArgs && i < 20; ++i) { - log += ' '; - log += i; - log += F(": '"); - log += web_server.argName(i); - log += F("' length: "); - log += webArg(i).length(); + /* + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + const int nrArgs = web_server.args(); + + if (nrArgs > 0) { + String log = F(" Webserver "); + log += nrArgs; + log += F(" Arguments"); + + if (nrArgs > 20) { + log += F(" (First 20)"); + } + log += ':'; + + for (int i = 0; i < nrArgs && i < 20; ++i) { + log += ' '; + log += i; + log += F(": '"); + log += web_server.argName(i); + log += F("' length: "); + log += webArg(i).length(); + } + addLogMove(LOG_LEVEL_INFO, log); + } } - addLogMove(LOG_LEVEL_INFO, log); - } - } - */ + */ #endif // ifndef BUILD_NO_DEBUG } + // We have sent a lot of data at once. - // try to flush it to the connected client to free up some RAM + // try to flush it to the connected client to free up some RAM // from pending transfers TXBuffer.flush(); delay(10); @@ -167,27 +171,30 @@ void sendHeadandTail_stdtemplate(bool Tail, bool rebooting) { bool captivePortal() { const IPAddress client_localIP = web_server.client().localIP(); - const bool fromAP = client_localIP == apIP; - const bool hasWiFiCredentials = SecuritySettings.hasWiFiCredentials(); + const bool fromAP = client_localIP == apIP; + const bool hasWiFiCredentials = SecuritySettings.hasWiFiCredentials(); + if (hasWiFiCredentials || !fromAP) { return false; } - if (!isIP(web_server.hostHeader()) && web_server.hostHeader() != (NetworkGetHostname() + F(".local"))) { + + if (!isIP(web_server.hostHeader()) && (web_server.hostHeader() != (NetworkGetHostname() + F(".local")))) { String redirectURL = concat(F("http://"), formatIP(client_localIP)); #ifdef WEBSERVER_SETUP + if (fromAP && !hasWiFiCredentials) { redirectURL += F("/setup"); } - #endif + #endif // ifdef WEBSERVER_SETUP sendHeader(F("Location"), redirectURL, true); - web_server.send(302, F("text/plain"), EMPTY_STRING); // Empty content inhibits Content-length header so we have to close the socket ourselves. - web_server.client().stop(); // Stop is needed because we sent no content length + web_server.send(302, F("text/plain"), EMPTY_STRING); // Empty content inhibits Content-length header so we have to close the socket + // ourselves. + web_server.client().stop(); // Stop is needed because we sent no content length return true; } return false; } - // ******************************************************************************** // Web Interface init // ******************************************************************************** @@ -201,17 +208,18 @@ void WebServerInit() // Prepare webserver pages #ifdef WEBSERVER_ROOT - web_server.on(F("/"), handle_root); + web_server.on(F("/"), handle_root); + // Entries for several captive portal URLs. // Maybe not needed. Might be handled by notFound handler. - web_server.on(UriGlob("/generate_204*"), handle_root); // Android captive portal. Handle "/generate_204_"-like requests. - web_server.on(F("/fwlink"), handle_root); //Microsoft captive portal. + web_server.on(UriGlob("/generate_204*"), handle_root); // Android captive portal. Handle "/generate_204_"-like requests. + web_server.on(F("/fwlink"), handle_root); // Microsoft captive portal. #endif // ifdef WEBSERVER_ROOT #ifdef WEBSERVER_ADVANCED - web_server.on(F("/advanced"), handle_advanced); - #if defined(WEBSERVER_DOWNLOAD) && FEATURE_TARSTREAM_SUPPORT - web_server.on(F("/backup"), handle_full_backup); - #endif // if defined(WEBSERVER_DOWNLOAD) && FEATURE_TARSTREAM_SUPPORT + web_server.on(F("/advanced"), handle_advanced); + # if defined(WEBSERVER_DOWNLOAD) && FEATURE_TARSTREAM_SUPPORT + web_server.on(F("/backup"), handle_full_backup); + # endif // if defined(WEBSERVER_DOWNLOAD) && FEATURE_TARSTREAM_SUPPORT #endif // ifdef WEBSERVER_ADVANCED #ifdef WEBSERVER_CONFIG web_server.on(F("/config"), handle_config); @@ -231,7 +239,7 @@ void WebServerInit() #ifdef USES_C016 - web_server.on(F("/dumpcache"), handle_dumpcache); // C016 specific entrie + web_server.on(F("/dumpcache"), handle_dumpcache); // C016 specific entrie web_server.on(F("/cache_json"), handle_cache_json); // C016 specific entrie web_server.on(F("/cache_csv"), handle_cache_csv); // C016 specific entrie #endif // USES_C016 @@ -251,7 +259,7 @@ void WebServerInit() #ifdef WEBSERVER_I2C_SCANNER web_server.on(F("/i2cscanner"), handle_i2cscanner); #endif // ifdef WEBSERVER_I2C_SCANNER - web_server.on(F("/json"), handle_json); // Also part of WEBSERVER_NEW_UI + web_server.on(F("/json"), handle_json); // Also part of WEBSERVER_NEW_UI web_server.on(F("/csv"), handle_csvval); web_server.on(F("/log"), handle_log); web_server.on(F("/logjson"), handle_log_JSON); // Also part of WEBSERVER_NEW_UI @@ -311,30 +319,31 @@ void WebServerInit() web_server.on(F("/factoryreset_json"), handle_factoryreset_json); web_server.on(F("/filelist_json"), handle_filelist_json); web_server.on(F("/i2cscanner_json"), handle_i2cscanner_json); - #if FEATURE_ESPEASY_P2P + # if FEATURE_ESPEASY_P2P web_server.on(F("/node_list_json"), handle_nodes_list_json); - #endif + # endif // if FEATURE_ESPEASY_P2P web_server.on(F("/pinstates_json"), handle_pinstates_json); web_server.on(F("/timingstats_json"), handle_timingstats_json); web_server.on(F("/upload_json"), HTTP_POST, handle_upload_json, handleFileUpload); web_server.on(F("/wifiscanner_json"), handle_wifiscanner_json); #endif // WEBSERVER_NEW_UI #if SHOW_SYSINFO_JSON - web_server.on(F("/sysinfo_json"), handle_sysinfo_json); -#endif//SHOW_SYSINFO_JSON + web_server.on(F("/sysinfo_json"), handle_sysinfo_json); +#endif// SHOW_SYSINFO_JSON web_server.onNotFound(handleNotFound); // List of headers to be recorded // "If-None-Match" is used to see whether we need to serve a static file, or simply can reply with a 304 (not modified) - const char * headerkeys[] = {"If-None-Match"}; + const char *headerkeys[] = { "If-None-Match" }; constexpr size_t headerkeyssize = NR_ELEMENTS(headerkeys); - web_server.collectHeaders(headerkeys, headerkeyssize ); + web_server.collectHeaders(headerkeys, headerkeyssize); #if defined(ESP8266) || defined(ESP32) { # ifndef NO_HTTP_UPDATER uint32_t maxSketchSize; bool use2step; + // allow OTA to smaller version of ESPEasy/other firmware if (Settings.AllowOTAUnlimited() || OTA_possible(maxSketchSize, use2step)) { httpUpdater.setup(&web_server); @@ -350,15 +359,15 @@ void WebServerInit() if (Settings.UseSSDP) { web_server.on(F("/ssdp.xml"), HTTP_GET, []() { - #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 web_server.client().setTimeout((CONTROLLER_CLIENTTIMEOUT_DFLT + 500) / 1000); // in seconds!!!! - Client &pClient = web_server.client(); + Client& pClient = web_server.client(); pClient.setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); - #else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - web_server.client().setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! - #endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + web_server.client().setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); // in msec as it should be! + # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS SSDP_schema(); }); @@ -391,34 +400,35 @@ void getWebPageTemplateDefault(const String& tmplName, WebTemplateParser& parser const bool addJS = true; const bool addMeta = true; -/* - if (equals(tmplName, F("TmplAP"))) - { - - getWebPageTemplateDefaultHead(parser, addMeta, !addJS); - - if (!parser.isTail()) { - #ifndef WEBPAGE_TEMPLATE_AP_HEADER - parser.process(F("
" - "

Welcome to ESP Easy Mega AP

")); - #else - parser.process(F(WEBPAGE_TEMPLATE_AP_HEADER)); - #endif - - parser.process(F("
")); - } - getWebPageTemplateDefaultContentSection(parser); - getWebPageTemplateDefaultFooter(parser); - } - else - */ + /* + if (equals(tmplName, F("TmplAP"))) + { + + getWebPageTemplateDefaultHead(parser, addMeta, !addJS); + + if (!parser.isTail()) { + #ifndef WEBPAGE_TEMPLATE_AP_HEADER + parser.process(F("
" + "

Welcome to ESP Easy Mega AP

")); + #else + parser.process(F(WEBPAGE_TEMPLATE_AP_HEADER)); + #endif + + parser.process(F("
")); + } + getWebPageTemplateDefaultContentSection(parser); + getWebPageTemplateDefaultFooter(parser); + } + else + */ if (equals(tmplName, F("TmplMsg"))) { getWebPageTemplateDefaultHead(parser, !addMeta, !addJS); + if (!parser.isTail()) { parser.process(F("" - "" - "" - "" - "{{name}}")); + "" + "" + "" + "{{name}}")); if (addMeta) { parser.process(F("{{meta}}")); } @@ -482,15 +496,17 @@ void getWebPageTemplateDefaultHead(WebTemplateParser& parser, bool addMeta, bool "")); } -void getWebPageTemplateDefaultHeader(WebTemplateParser& parser, const __FlashStringHelper * title, bool addMenu) { +void getWebPageTemplateDefaultHeader(WebTemplateParser& parser, const __FlashStringHelper *title, bool addMenu) { { - if (parser.isTail()) return; + if (parser.isTail()) { return; } #ifndef WEBPAGE_TEMPLATE_DEFAULT_HEADER parser.process(F("

ESP Easy Mega: ")); parser.process(title); - #if BUILD_IN_WEBHEADER - parser.process(F("
Build: " GITHUB_RELEASES_LINK_PREFIX "{{date}}" GITHUB_RELEASES_LINK_SUFFIX "
")); - #endif // #if BUILD_IN_WEBHEADER + # if BUILD_IN_WEBHEADER + parser.process(F( + "
Build: " GITHUB_RELEASES_LINK_PREFIX "{{date}}" GITHUB_RELEASES_LINK_SUFFIX + "
")); + # endif // #if BUILD_IN_WEBHEADER parser.process(F("


")); #else // ifndef WEBPAGE_TEMPLATE_DEFAULT_HEADER String tmp = F(WEBPAGE_TEMPLATE_DEFAULT_HEADER); @@ -505,63 +521,61 @@ void getWebPageTemplateDefaultHeader(WebTemplateParser& parser, const __FlashStr void getWebPageTemplateDefaultContentSection(WebTemplateParser& parser) { parser.process(F("
" - "" - "{{error}}" - "" - "{{content}}" - "
" - )); + "" + "{{error}}" + "" + "{{content}}" + "" + )); } void getWebPageTemplateDefaultFooter(WebTemplateParser& parser) { - if (!parser.isTail()) return; + if (!parser.isTail()) { return; } #ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER parser.process(F("
" - "
" - "
Powered by Let's Control It community" - #if BUILD_IN_WEBFOOTER - "
Build: " GITHUB_RELEASES_LINK_PREFIX "{{build}} {{date}}" GITHUB_RELEASES_LINK_SUFFIX "
" - #endif // #if BUILD_IN_WEBFOOTER - "
" - "
" - "" - )); + "
" + "
Powered by Let's Control It community" + # if BUILD_IN_WEBFOOTER + "
Build: " GITHUB_RELEASES_LINK_PREFIX "{{build}} {{date}}" GITHUB_RELEASES_LINK_SUFFIX "
" + # endif // #if BUILD_IN_WEBFOOTER + "
" + "" + "" + )); #else // ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER parser.process(F(WEBPAGE_TEMPLATE_DEFAULT_FOOTER)); #endif // ifndef WEBPAGE_TEMPLATE_DEFAULT_FOOTER } - - void writeDefaultCSS(void) { return; // TODO -/* -#ifndef WEBSERVER_USE_CDN_JS_CSS + /* + #ifndef WEBSERVER_USE_CDN_JS_CSS - if (!fileExists(F("esp.css"))) - { - fs::File f = tryOpenFile(F("esp.css"), "w"); + if (!fileExists(F("esp.css"))) + { + fs::File f = tryOpenFile(F("esp.css"), "w"); - if (f) - { - String defaultCSS; - defaultCSS = PGMT(DATA_ESPEASY_DEFAULT_MIN_CSS); - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = F("CSS : Writing default CSS file to FS ("); - log += defaultCSS.length(); - log += F(" bytes)"); - addLog(LOG_LEVEL_INFO, log); + if (f) + { + String defaultCSS; + defaultCSS = PGMT(DATA_ESPEASY_DEFAULT_MIN_CSS); + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = F("CSS : Writing default CSS file to FS ("); + log += defaultCSS.length(); + log += F(" bytes)"); + addLog(LOG_LEVEL_INFO, log); + } + f.write((const unsigned char *)defaultCSS.c_str(), defaultCSS.length()); // note: content must be in RAM - a write of F("XXX") does + // not work + f.close(); } - f.write((const unsigned char *)defaultCSS.c_str(), defaultCSS.length()); // note: content must be in RAM - a write of F("XXX") does - // not work - f.close(); - } - } -#endif -*/ + } + #endif + */ } // ******************************************************************************** @@ -573,7 +587,7 @@ void writeDefaultCSS(void) int8_t level = 0; int8_t lastLevel = -1; -void json_quote_name(const __FlashStringHelper * val) { +void json_quote_name(const __FlashStringHelper *val) { json_quote_name(String(val)); } @@ -598,7 +612,7 @@ void json_open(bool arr) { json_open(arr, EMPTY_STRING); } -void json_open(bool arr, const __FlashStringHelper * name) { +void json_open(bool arr, const __FlashStringHelper *name) { json_quote_name(name); addHtml(arr ? '[' : '{'); lastLevel = level; @@ -627,24 +641,22 @@ void json_close(bool arr) { lastLevel = level; } -void json_number(const __FlashStringHelper * name, const String& value) +void json_number(const __FlashStringHelper *name, const String& value) { json_prop(name, value); } - void json_number(const String& name, const String& value) { json_prop(name, value); } -void json_prop(const __FlashStringHelper * name, const String& value) +void json_prop(const __FlashStringHelper *name, const String& value) { json_quote_name(name); json_quote_val(value); lastLevel = level; } - void json_prop(const String& name, const String& value) { json_quote_name(name); json_quote_val(value); @@ -664,10 +676,11 @@ void addTaskSelect(const String& name, taskIndex_t choice, const String& csscla String deviceName; addHtml(F("