From cac4500f6a1d195b3f5ff4babedf0894fbbe3eb7 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:18:58 +0200 Subject: [PATCH 001/161] [SPI] Add support for multiple SPI buses --- src/src/DataStructs/DeviceStruct.h | 7 +- src/src/DataStructs/SettingsStruct.h | 30 +++- src/src/DataStructs_templ/SettingsStruct.cpp | 143 ++++++++++++++++--- src/src/DataTypes/SPI_options.cpp | 7 +- src/src/DataTypes/SPI_options.h | 3 +- src/src/Globals/SPI1.cpp | 5 + src/src/Globals/SPI1.h | 6 + src/src/Helpers/ESPEasy_Storage.cpp | 11 ++ src/src/Helpers/ESPEasy_checks.cpp | 2 +- src/src/Helpers/Hardware.cpp | 36 ++++- src/src/Helpers/Hardware_device_info.h | 12 ++ src/src/Helpers/SPI_Helper.cpp | 32 +++++ src/src/Helpers/SPI_Helper.h | 14 ++ src/src/Helpers/StringGenerator_GPIO.cpp | 12 ++ src/src/WebServer/DevicesPage.cpp | 39 +++-- src/src/WebServer/DevicesPage.h | 4 +- src/src/WebServer/HardwarePage.cpp | 125 ++++++++++------ src/src/WebServer/Markup_Forms.cpp | 5 + src/src/WebServer/Markup_Forms.h | 1 + 19 files changed, 408 insertions(+), 86 deletions(-) create mode 100644 src/src/Globals/SPI1.cpp create mode 100644 src/src/Globals/SPI1.h create mode 100644 src/src/Helpers/SPI_Helper.cpp create mode 100644 src/src/Helpers/SPI_Helper.h diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index 09385a420..f5300a5a3 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -47,6 +47,11 @@ // #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE +// Stored in Settings.I2C_Flags !!! +#define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task +// Stored in Settings.I2C_Flags for Task 1 settings +#define SPI_FLAGS_ETH_BUS_NUMBER 2 // 2 bit, stores the configured bus for the Ethernet interface +#define SPI_FLAGS_SDCARD_BUS_NUMBER 4 // 2 bit, stores the configured bus for the SDCard /*********************************************************************************************\ * DeviceStruct @@ -116,7 +121,7 @@ struct DeviceStruct uint32_t CustomVTypeVar : 1; // User-selectable VType per value uint32_t Dummy22 : 1; // Dummy added to force alignment, can be re-used - uint32_t Dummy23 : 1; // Dummy added to force alignment, can be re-used + uint32_t SpiBusSelect : 1; // Allow selection of the SPI bus, if multiple buses are configured uint32_t Dummy24 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy25 : 1; // Dummy added to force alignment, can be re-used uint32_t Dummy26 : 1; // Dummy added to force alignment, can be re-used diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 8c2a31699..1a421a91f 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -314,21 +314,37 @@ public: PinBootState getPinBootState(int8_t gpio_pin) const; void setPinBootState(int8_t gpio_pin, PinBootState state); - bool getSPI_pins(int8_t spi_gpios[3]) const; + bool getSPI_pins(int8_t spi_gpios[3], + uint8_t spi_bus = 0, + bool noCheck = false) const; #ifdef ESP32 - spi_host_device_t getSPI_host() const; + spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const; #endif // Return true when pin is one of the SPI pins and SPI is enabled - bool isSPI_pin(int8_t pin) const; + bool isSPI_pin(int8_t pin, + uint8_t spi_bus = 0xFF) const; // Return true when SPI enabled and opt. user defined pins valid. - bool isSPI_valid() const; + bool isSPI_valid(uint8_t spi_bus = 0) const; // Return true when pin is one of the configured I2C pins. bool isI2C_pin(int8_t pin) const; + uint8_t getSPIBusForTask(taskIndex_t TaskIndex) const; + void setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus); + + #if FEATURE_ETHERNET + uint8_t getSPIBusForEth() const; + void setSPIBusForEth(uint8_t spi_bus); + #endif // if FEATURE_ETHERNET + + #if FEATURE_SD + uint8_t getSPIBusForSDCard() const; + void setSPIBusForSDCard(uint8_t spi_bus); + #endif // if FEATURE_SD + // Return true if I2C settings are correct bool isI2CEnabled(uint8_t i2cBus) const; @@ -449,7 +465,11 @@ public: int8_t I2C3_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C3_Multiplexer_Addr = -1; int8_t I2C3_Multiplexer_ResetPin = -1; - unsigned int OLD_TaskDeviceID[N_TASKS - 7] = {0}; //UNUSED: this can be reused + uint8_t InitSPI1 = 0; // 0 = disabled, 1= enabled but for ESP32 there is option 2= SPI2 9 = User defined, see src/src/WebServer/HardwarePage.h enum SPI_Options_e + int8_t SPI1_SCLK_pin = -1; + int8_t SPI1_MISO_pin = -1; + int8_t SPI1_MOSI_pin = -1; + unsigned int OLD_TaskDeviceID[N_TASKS - 8] = {0}; // UNUSED: this can be reused // FIXME TD-er: When used on ESP8266, this conversion union may not work // It might work as it is 32-bit in size. diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 060b70278..7c865a2d1 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -896,14 +896,14 @@ void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState } template -bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { +bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const { spi_gpios[0] = -1; spi_gpios[1] = -1; spi_gpios[2] = -1; - if (isSPI_valid()) { + if (noCheck || isSPI_valid(spi_bus)) { # ifdef ESP32 - const SPI_Options_e SPI_selection = static_cast(InitSPI); + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: @@ -924,9 +924,21 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { #endif case SPI_Options_e::UserDefined: { - spi_gpios[0] = SPI_SCLK_pin; - spi_gpios[1] = SPI_MISO_pin; - spi_gpios[2] = SPI_MOSI_pin; + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + spi_gpios[0] = SPI_SCLK_pin; + spi_gpios[1] = SPI_MISO_pin; + spi_gpios[2] = SPI_MOSI_pin; + } + #ifdef ESP32 + else if (1 == spi_bus) { + spi_gpios[0] = SPI1_SCLK_pin; + spi_gpios[1] = SPI1_MISO_pin; + spi_gpios[2] = SPI1_MOSI_pin; + } + #endif // ifdef ESP32 break; } case SPI_Options_e::None: @@ -943,10 +955,10 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3]) const { #ifdef ESP32 template -spi_host_device_t SettingsStruct_tmpl::getSPI_host() const +spi_host_device_t SettingsStruct_tmpl::getSPI_host(uint8_t spi_bus) const { - if (isSPI_valid()) { - const SPI_Options_e SPI_selection = static_cast(InitSPI); + if (isSPI_valid(spi_bus)) { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: { @@ -989,31 +1001,83 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host() const template -bool SettingsStruct_tmpl::isSPI_pin(int8_t pin) const { +bool SettingsStruct_tmpl::isSPI_pin(int8_t pin, uint8_t spi_bus) const { if (pin < 0) { return false; } int8_t spi_gpios[3]; - if (getSPI_pins(spi_gpios)) { + if (getSPI_pins(spi_gpios, 0u) && ((0xFF == spi_bus) || (0u == spi_bus))) { for (uint8_t i = 0; i < 3; ++i) { if (spi_gpios[i] == pin) { return true; } } } + #ifdef ESP32 + if ((getSPIBusCount() > 1u) && getSPI_pins(spi_gpios, 1u) && ((0xFF == spi_bus) || (1u == spi_bus))) { + for (uint8_t i = 0; i < 3; ++i) { + if (spi_gpios[i] == pin) { return true; } + } + } + #endif // ifdef ESP32 return false; } template -bool SettingsStruct_tmpl::isSPI_valid() const { - if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } +bool SettingsStruct_tmpl::isSPI_valid(uint8_t spi_bus) const { + int8_t spi0_pins[3]; + bool result = false; + getSPI_pins(spi0_pins, 0u, true); - if (InitSPI == static_cast(SPI_Options_e::UserDefined)) { - return !((SPI_SCLK_pin == -1) || - (SPI_MISO_pin == -1) || - (SPI_MOSI_pin == -1) || - (SPI_SCLK_pin == SPI_MISO_pin) || - (SPI_MISO_pin == SPI_MOSI_pin) || - (SPI_MOSI_pin == SPI_SCLK_pin)); + #ifdef ESP32 + if (0 == spi_bus) + #endif // ifdef ESP32 + { + if (InitSPI == static_cast(SPI_Options_e::None)) { return false; } + + result = !((spi0_pins[0] == -1) || + (spi0_pins[1] == -1) || + (spi0_pins[2] == -1) || + (spi0_pins[0] == spi0_pins[1]) || + (spi0_pins[1] == spi0_pins[2]) || + (spi0_pins[2] == spi0_pins[0])); + #ifdef ESP32 + if (result && (getSPIBusCount() > 1)) { // Cross-check pins with other bus + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u); + + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } + #endif // ifdef ESP32 } - return true; + #ifdef ESP32 + else if ((1 == spi_bus) && (getSPIBusCount() > 1)) { + if (InitSPI1 == static_cast(SPI_Options_e::None)) { return false; } + int8_t spi1_pins[3]; + getSPI_pins(spi1_pins, 1u, true); + + result = !((spi1_pins[0] == -1) || + (spi1_pins[1] == -1) || + (spi1_pins[2] == -1) || + (spi1_pins[0] == spi1_pins[1]) || + (spi1_pins[1] == spi1_pins[2]) || + (spi1_pins[2] == spi1_pins[0])); + + if (result) { // Cross-check pins + for (uint8_t i = 0; (i < 3) && result; ++i) { + for (uint8_t j = 0; (j < 3) && result; ++j) { + if (spi0_pins[i] == spi1_pins[j]) { + result = false; + } + } + } + } + } + #endif // ifdef ESP32 + return result; } template @@ -1040,6 +1104,43 @@ bool SettingsStruct_tmpl::isI2CEnabled(uint8_t i2cBus) const { (getI2CClockSpeedSlow(i2cBus) > 0); } +// stored in I2C_Flags per Task +template +uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { + return get2BitFromUL(I2C_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus) { + set2BitToUL(I2C_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); +} + +#if FEATURE_ETHERNET +// stored in I2C_Flags for Task 1 (index 0) +template +uint8_t SettingsStruct_tmpl::getSPIBusForEth() const { + return get2BitFromUL(I2C_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForEth(uint8_t spi_bus) { + set2BitToUL(I2C_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER, spi_bus); +} +#endif // FEATURE_ETHERNET + +#if FEATURE_SD +// stored in I2C_Flags for Task 1 (index 0) +template +uint8_t SettingsStruct_tmpl::getSPIBusForSDCard() const { + return get2BitFromUL(I2C_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER); +} + +template +void SettingsStruct_tmpl::setSPIBusForSDCard(uint8_t spi_bus) { + set2BitToUL(I2C_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); +} +#endif // if FEATURE_SD + template uint8_t SettingsStruct_tmpl::getI2CInterface(taskIndex_t TaskIndex) const { return get3BitFromUL(I2C_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); diff --git a/src/src/DataTypes/SPI_options.cpp b/src/src/DataTypes/SPI_options.cpp index 575611044..2ca32180f 100644 --- a/src/src/DataTypes/SPI_options.cpp +++ b/src/src/DataTypes/SPI_options.cpp @@ -78,7 +78,7 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { return F("Unknown"); } -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option) { +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus) { switch (option) { case SPI_Options_e::None: return F("Disabled"); @@ -89,7 +89,12 @@ const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option) { return F("HSPI"); #endif case SPI_Options_e::UserDefined: + #ifdef ESP32 + return concat(F("User-defined SPI Bus "), spi_bus); + #endif // ifdef ESP32 + #ifdef ESP8266 return F("User-defined SPI"); + #endif // ifdef ESP8266 } return F("Unknown"); } diff --git a/src/src/DataTypes/SPI_options.h b/src/src/DataTypes/SPI_options.h index b2a8627dc..057c43614 100644 --- a/src/src/DataTypes/SPI_options.h +++ b/src/src/DataTypes/SPI_options.h @@ -2,6 +2,7 @@ #define DATATYPES_SPI_OPTIONS_H #include "../../ESPEasy_common.h" +#include "../Helpers/StringConverter.h" // ESP32 classic has default pins for HSPI. @@ -91,7 +92,7 @@ enum class SPI_Options_e { // Do not change values as this is stored in the sett #ifdef ESP32 const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option); -const __FlashStringHelper* getSPI_optionToShortString(SPI_Options_e option); +const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus = 0); #endif // ifdef ESP32 #endif // ifndef DATATYPES_SPI_OPTIONS_H diff --git a/src/src/Globals/SPI1.cpp b/src/src/Globals/SPI1.cpp new file mode 100644 index 000000000..87ad5b001 --- /dev/null +++ b/src/src/Globals/SPI1.cpp @@ -0,0 +1,5 @@ +#include "../Globals/SPI1.h" + +#ifdef ESP32 +SPIClass SPI1(HSPI); +#endif // ifdef ESP32 diff --git a/src/src/Globals/SPI1.h b/src/src/Globals/SPI1.h new file mode 100644 index 000000000..92eef0afd --- /dev/null +++ b/src/src/Globals/SPI1.h @@ -0,0 +1,6 @@ +#pragma once +#ifdef ESP32 +# include + +extern SPIClass SPI1; +#endif // ifdef ESP32 diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 9274f2c74..01b354ed8 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -532,6 +532,17 @@ bool BuildFixes() } } + if (Settings.Build <= 21303) { // 2025-08-25 + // Add second SPI bus + if ((Settings.SPI1_SCLK_pin == 0) && + (Settings.SPI1_MISO_pin == 0) && + (Settings.SPI1_MOSI_pin == 0) ){ + Settings.SPI1_SCLK_pin = -1; + Settings.SPI1_MISO_pin = -1; + Settings.SPI1_MOSI_pin = -1; + } + } + // Starting 2022/08/18 // Use get_build_nr() value for settings transitions. // This value will also be shown when building using PlatformIO, when showing the Compile time defines diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f2ab65935..245231650 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -185,7 +185,7 @@ void run_compiletime_checks() { static_assert(198u == offsetof(SettingsStruct, TaskDeviceNumber), "NOTIFICATION_MAX has changed?"); // All settings related to N_TASKS - static_assert((228 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. + static_assert((232 + TASKS_MAX) == offsetof(SettingsStruct, OLD_TaskDeviceID), ""); // 32-bit alignment, so offset of 2 bytes. static_assert((200 + (67 * TASKS_MAX)) == offsetof(SettingsStruct, ControllerEnabled), ""); // Used to compute true offset. diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index b8f41a32e..ae19873e8 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -108,6 +108,8 @@ #include +#include "../Globals/SPI1.h" + # define GPIO_PLUGIN_ID 1 @@ -226,7 +228,7 @@ void hardwareInit() // SPI Init - bool SPI_initialized = false; + uint8_t SPI_initialized = 0; if (tryInitSPI && Settings.isSPI_valid()) { SPI.setHwCs(false); @@ -237,14 +239,22 @@ void hardwareInit() const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); int8_t spi_gpios[3] = {}; - if (Settings.getSPI_pins(spi_gpios)) { + if (Settings.getSPI_pins(spi_gpios, 0u)) { if (SPI_selection == SPI_Options_e::Vspi_Fspi) { SPI.begin(); // Default SPI bus } else { SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); } - SPI_initialized = true; + SPI_initialized |= 1; } + // Init second SPI interface (SPI1) + const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); + if (Settings.getSPI_pins(spi_gpios, 1u)) { + SPI1.setHwCs(false); + SPI1.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + SPI_initialized |= 2; + } + #else // ifdef ESP32 SPI.begin(); SPI_initialized = true; @@ -253,14 +263,34 @@ void hardwareInit() if (SPI_initialized) { + #ifdef ESP32 + for (uint8_t i = 1; i < 3; ++i) { + if (SPI_initialized & i) { + addLog(LOG_LEVEL_INFO, strformat(F("INIT : SPI Bus %d Init (without CS)"), i - 1)); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); + #endif // ifdef ESP8266 #if FEATURE_SD if (Settings.Pin_sd_cs >= 0) { + #ifdef ESP32 + const uint8_t sdspi = Settings.getSPIBusForSDCard(); + if (SD.begin(Settings.Pin_sd_cs, 0 == sdspi ? SPI : SPI1)) + #endif // ifdef ESP32 + #ifdef ESP8266 if (SD.begin(Settings.Pin_sd_cs)) + #endif // ifdef ESP8266 { + #ifdef ESP32 + addLog(LOG_LEVEL_INFO, strformat(F("SD : Init on SPI Bus %d OK"), sdspi)); + #endif // ifdef ESP32 + #ifdef ESP8266 addLog(LOG_LEVEL_INFO, F("SD : Init OK")); + #endif // ifdef ESP8266 } else { diff --git a/src/src/Helpers/Hardware_device_info.h b/src/src/Helpers/Hardware_device_info.h index 8b8610017..90129592b 100644 --- a/src/src/Helpers/Hardware_device_info.h +++ b/src/src/Helpers/Hardware_device_info.h @@ -116,6 +116,18 @@ constexpr uint8_t getI2CBusCount() { #endif } +constexpr uint8_t getSPIBusCount() { +#if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) + return 2u; +#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32H2) + return 1u; +#elif defined(ESP8266) + return 1u; +#else // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) + static_assert(false, "Implement processor architecture"); +#endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) +} + /********************************************************************************************\ PSRAM support \*********************************************************************************************/ diff --git a/src/src/Helpers/SPI_Helper.cpp b/src/src/Helpers/SPI_Helper.cpp new file mode 100644 index 000000000..79099bb84 --- /dev/null +++ b/src/src/Helpers/SPI_Helper.cpp @@ -0,0 +1,32 @@ +#include "../Helpers/SPI_Helper.h" + +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled) { + const uint8_t spiMaxBusCount = (getSPIBusCount() > 1 + ? ((Settings.isSPI_valid(1) ? 1 : 0) + ) + : 0) + (Settings.isSPI_valid(0) ? 1 : 0); + + if (spiMaxBusCount > 1) { + static uint8_t spiBusCount = 0; + static int spiBusNumbers[3]; + + if (spiBusCount != spiMaxBusCount) { + spiBusCount = 0; + spiBusNumbers[spiBusCount] = 0; + ++spiBusCount; + + if ((getSPIBusCount() > 1) && Settings.isSPI_valid(1)) { + spiBusNumbers[spiBusCount] = 1; + ++spiBusCount; + } + } + FormSelectorOptions selector(spiBusCount, + spiBusNumbers); + selector.default_index = 0; + selector.enabled = !disabled; + selector.addFormSelector(label, id, choice); + } +} diff --git a/src/src/Helpers/SPI_Helper.h b/src/src/Helpers/SPI_Helper.h new file mode 100644 index 000000000..c0568e61b --- /dev/null +++ b/src/src/Helpers/SPI_Helper.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../DataStructs/SettingsStruct.h" +#include "../DataTypes/TaskIndex.h" +#include "../Globals/Plugins.h" +#include "../Globals/Settings.h" +#include "../Helpers/Hardware_device_info.h" + +#include "../WebServer/Markup_Forms.h" + +void SPIInterfaceSelector(String label, + String id, + uint8_t choice, + bool disabled = false); diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 4841f57ec..b7ca2d534 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -307,9 +307,21 @@ const __FlashStringHelper* getConflictingUse(int gpio, PinSelectPurpose purpose, } } + #ifdef ESP32 + if (includeSPI) { + if (Settings.isSPI_pin(gpio, 0u)) { + return F("SPI (bus 0)"); + } + else if (Settings.isSPI_pin(gpio, 1u)) { + return F("SPI (bus 1)"); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 if (includeSPI && Settings.isSPI_pin(gpio)) { return F("SPI"); } + #endif // ifdef ESP8266 if (includeStatusLed && (Settings.Pin_status_led == gpio) && (-1 != gpio)) { return F("Wifi Status LED"); diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 35c3b0435..f18bcce25 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -31,6 +31,7 @@ # include "../Helpers/_Plugin_Helper_serial.h" # include "../Helpers/ESPEasy_Storage.h" # include "../Helpers/I2C_Plugin_Helper.h" +# include "../Helpers/SPI_Helper.h" # include "../Helpers/StringConverter.h" # include "../Helpers/StringGenerator_GPIO.h" @@ -277,6 +278,12 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; + #ifdef ESP32 + if (device.isSPI()) { + Settings.setSPIBusForTask(taskIndex, getFormItemInt(F("pspibus"), 0)); + } + #endif // ifdef ESP32 + if (device.Type == DEVICE_TYPE_I2C) { uint8_t flags = 0; bitWrite(flags, I2C_FLAGS_SLOW_SPEED, isFormItemChecked(F("taskdeviceflags0"))); @@ -628,7 +635,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (device.Type == DEVICE_TYPE_I2C) { format_I2C_port_description(x); } else if (device.isSPI()) { - format_SPI_port_description(spi_gpios); + format_SPI_port_description(spi_gpios, Settings.getSPIBusForTask(x)); } else if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); @@ -978,14 +985,14 @@ void format_I2C_port_description(taskIndex_t x) # endif // if FEATURE_I2CMULTIPLEXER } -void format_SPI_port_description(int8_t spi_gpios[3]) +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus) { - if (!Settings.getSPI_pins(spi_gpios)) { + if (!Settings.getSPI_pins(spi_gpios, spi_bus)) { addHtml(F("SPI (Not enabled)")); return; } # ifdef ESP32 - addHtml(getSPI_optionToShortString(static_cast(Settings.InitSPI))); + addHtml(getSPI_optionToShortString(static_cast(0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1), spi_bus)); # endif // ifdef ESP32 # ifdef ESP8266 addHtml(F("SPI")); @@ -1008,7 +1015,8 @@ 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 uint8_t spi_bus = Settings.getSPIBusForTask(x); + if ((0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1) > static_cast(SPI_Options_e::None)) { const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI") }; for (int i = 0; i < 3; ++i) { @@ -1277,9 +1285,8 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormNote(F("Will go into effect on next input change.")); } - if (device.isSPI() - && (Settings.InitSPI == static_cast(SPI_Options_e::None))) { - addFormNote(F("SPI Interface is not configured yet (Hardware page).")); + if (device.isSPI()) { + devicePage_show_SPI_config(taskIndex, DeviceIndex); } if (device.connectedToGPIOpins()) { @@ -1354,6 +1361,22 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) # endif // ifdef PLUGIN_USES_SERIAL +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) +{ + if (Device[DeviceIndex].isSPI() + && !(Settings.isSPI_valid(0u) || (getSPIBusCount() > 1 && Settings.isSPI_valid(1u)))) { + addFormNote(F("SPI Bus not configured yet (Hardware page).")); + } + #ifdef ESP32 + if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + uint8_t spiBus = Settings.getSPIBusForTask(taskIndex); + SPIInterfaceSelector(F("SPI Bus"), + F("pspibus"), + spiBus); + } + #endif // ifdef ESP32 +} + void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 8b7bece77..1b33d2ec8 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -37,7 +37,7 @@ void format_originating_node(uint8_t remoteUnit); #endif void format_I2C_port_description(taskIndex_t x); -void format_SPI_port_description(int8_t spi_gpios[3]); +void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus); void format_I2C_pin_description(taskIndex_t x); @@ -56,6 +56,8 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex void devicePage_show_serial_config(taskIndex_t taskIndex); #endif +void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); + void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 32e4d1a58..c0d9df401 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -17,6 +17,7 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -108,21 +109,34 @@ void handle_hardware() { set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); #endif // if FEATURE_I2C_MULTIPLE #ifdef ESP32 - Settings.InitSPI = getFormItemInt(F("initspi"), static_cast(SPI_Options_e::None)); - if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-define SPI GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi"), -1); - if (!Settings.isSPI_valid()) { // Checks - error += F("User-defined SPI pins not configured correctly!\n"); + Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast(SPI_Options_e::None)); + if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + if (!Settings.isSPI_valid(0u)) { // Checks + error += F("User-defined SPI bus 0 pins not configured correctly!\n"); + } + } + Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast(SPI_Options_e::None)); + if (Settings.InitSPI1 == static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + if (!Settings.isSPI_valid(1u)) { // Checks + error += F("User-defined SPI bus 1 pins not configured correctly!\n"); } } #else //for ESP8266 we keep the old UI Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init #endif + #ifdef ESP32 + Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); + #endif // ifdef ESP32 Settings.Pin_sd_cs = getFormItemInt(F("sd")); #if FEATURE_ETHERNET Settings.ETH_Phy_Addr = getFormItemInt(F("ethphy")); + Settings.setSPIBusForEth(getFormItemInt(F("ethspibus"), 0)); Settings.ETH_Pin_mdc_cs = getFormItemInt(F("ethmdc")); Settings.ETH_Pin_mdio_irq = getFormItemInt(F("ethmdio")); Settings.ETH_Pin_power_rst = getFormItemInt(F("ethpower")); @@ -280,52 +294,67 @@ void handle_hardware() { } #endif // if FEATURE_I2C_MULTIPLE - // SPI Init - addFormSubHeader(F("SPI Interface")); #ifdef ESP32 - { - // Script to show GPIO pins for User-defined SPI GPIOs - // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), - // Minified: - html_add_script(F("function spiOptionChanged(e){var i=9==e.value?'':'none';" - "document.getElementById('tr_spipinsclk').style.display=i," - "document.getElementById('tr_spipinmiso').style.display=i," - "document.getElementById('tr_spipinmosi').style.display=i}"), - false); - const __FlashStringHelper * spi_options[] = { - getSPI_optionToString(SPI_Options_e::None), - getSPI_optionToString(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - getSPI_optionToString(SPI_Options_e::Hspi), - #endif - getSPI_optionToString(SPI_Options_e::UserDefined)}; - const int spi_index[] = { - static_cast(SPI_Options_e::None), - static_cast(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - static_cast(SPI_Options_e::Hspi), - #endif - static_cast(SPI_Options_e::UserDefined) - }; - constexpr size_t nrOptions = NR_ELEMENTS(spi_index); - FormSelectorOptions selector(nrOptions, spi_options, spi_index); - selector.onChangeCall = F("spiOptionChanged(this)"); - selector.addFormSelector(F("Init SPI"), F("initspi"), Settings.InitSPI); - // User-defined pins - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), F("spipinsclk"), Settings.SPI_SCLK_pin); - addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), F("spipinmiso"), Settings.SPI_MISO_pin); - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), F("spipinmosi"), Settings.SPI_MOSI_pin); - html_add_script(F("document.getElementById('initspi').onchange();"), false); // Initial trigger onchange script - addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + // SPI Init + addFormSubHeader(concat(F("SPI Bus "), spi_bus)); + { + // Script to show GPIO pins for User-defined SPI GPIOs + // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), + // Minified: + html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';" + "document.getElementById('tr_spipinsclk%u').style.display=i," + "document.getElementById('tr_spipinmiso%u').style.display=i," + "document.getElementById('tr_spipinmosi%u').style.display=i" + "}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus), + false); + const __FlashStringHelper * spi_options[] = { + getSPI_optionToString(SPI_Options_e::None), + getSPI_optionToString(SPI_Options_e::Vspi_Fspi), + #ifdef ESP32_CLASSIC + getSPI_optionToString(SPI_Options_e::Hspi), + #endif + getSPI_optionToString(SPI_Options_e::UserDefined)}; + const int spi_index[] = { + static_cast(SPI_Options_e::None), + static_cast(SPI_Options_e::Vspi_Fspi), + #ifdef ESP32_CLASSIC + static_cast(SPI_Options_e::Hspi), + #endif + static_cast(SPI_Options_e::UserDefined) + }; + constexpr size_t nrOptions = NR_ELEMENTS(spi_index); + FormSelectorOptions selector(nrOptions, spi_options, spi_index); + selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus); + selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), concat(F("initspi"), spi_bus), 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1); + int8_t spi_gpio[3]; + Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured + // User-defined pins + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]); + addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); + html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script + addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); + } } #else //for ESP8266 we keep the existing UI + addFormSubHeader(F("SPI Bus 0")); addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); - #endif addFormNote(F("Chip Select (CS) config must be done in the plugin")); + #endif // ifdef ESP32 #if FEATURE_SD addFormSubHeader(F("SD Card")); + #ifdef ESP32 + if (getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + uint8_t spiBus = Settings.getSPIBusForSDCard(); + SPIInterfaceSelector(F("SPI Bus"), + F("sdspibus"), + spiBus); + } + #endif // ifdef ESP32 addFormPinSelect(PinSelectPurpose::SD_Card, formatGpioName_output(F("SD Card CS")), F("sd"), Settings.Pin_sd_cs); #endif // if FEATURE_SD @@ -438,6 +467,14 @@ void handle_hardware() { " (0 or 1 for LAN8720, 31 for TLK110, -1 autodetect)" #endif )); + + if (getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + uint8_t spiBus = Settings.getSPIBusForEth(); + SPIInterfaceSelector(F("SPI Bus"), + F("ethspibus"), + spiBus, true); // FIXME: Remove Disabled flag once Ethernet SPI selection is available + } + addFormPinSelect(PinSelectPurpose::Ethernet, formatGpioName_output( F(MDC_CS_PIN_DESCR)), F("ethmdc"), Settings.ETH_Pin_mdc_cs); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 4ab8d9b33..7e146507b 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -446,6 +446,11 @@ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __Fla addPinSelect(purpose, id, choice); } +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice) { + addRowLabel_tr_id(label, id); + addPinSelect(purpose, id, choice); +} + void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice) { addRowLabel_tr_id(label, id); diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 143945795..54c35e1b5 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -274,6 +274,7 @@ void addFormPinSelect(const __FlashStringHelper * label, int choice); */ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __FlashStringHelper * id, int choice); +void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice); void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice); From 0c2f09df87e42fe96dafcb075c33cde84ad713b4 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:20:26 +0200 Subject: [PATCH 002/161] [P095] Enable support for configurable SPI bus --- lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp | 17 +++++++++++++---- lib/Adafruit_ILI9341/Adafruit_ILI9341.h | 4 ++-- src/_P095_ILI9341.ino | 7 ++++++- src/src/PluginStructs/P095_data_struct.cpp | 16 ++++++++++++++-- src/src/PluginStructs/P095_data_struct.h | 12 ++++++++---- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp index 88c0e2ae5..ce52a44d5 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.cpp @@ -111,6 +111,9 @@ @param cs Chip select pin # (OK to pass -1 if CS tied to GND). @param dc Data/Command pin # (required). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ @@ -133,14 +136,20 @@ Adafruit_ILI9341::Adafruit_ILI9341(int8_t cs, int8_t dc, int8_t rst, uint8_t mod @param cs Chip select pin # (optional, pass -1 if unused and CS is tied to GND). @param rst Reset pin # (optional, pass -1 if unused). + @param model The display model to initiailize. + @param w Widht of the display in pixels. + @param h Height of the display in pixels. */ /**************************************************************************/ -// Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, -// int8_t rst) -// : Adafruit_SPITFT(ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT, spiClass, cs, dc, -// rst) {} +Adafruit_ILI9341::Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs, + int8_t rst, uint8_t model, uint16_t w, uint16_t h) + : Adafruit_SPITFT(w, h, spiClass, cs, dc, rst) { + _model = model; + _w = w; + _h = h; +} #endif // end !ESP8266 /**************************************************************************/ diff --git a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h index 349323700..68735d225 100644 --- a/lib/Adafruit_ILI9341/Adafruit_ILI9341.h +++ b/lib/Adafruit_ILI9341/Adafruit_ILI9341.h @@ -163,8 +163,8 @@ public: #if !defined(ESP8266) - // Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, - // int8_t rst = -1); + Adafruit_ILI9341(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + int8_t rst = -1, uint8_t model = 0, uint16_t w = 0, uint16_t h = 0); #endif // end !ESP8266 // Adafruit_ILI9341(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, // int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 9f7ae8a03..4e01d2018 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -15,6 +15,7 @@ /** * Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-07-07 tonhuisman: Remove explicit support for ILI9486 as all ILI9486 displays tested so far work with the ILI9488 driver, * sometimes with Invert display setting enabled (or they are actually ILI9488 displays...) * 2024-06-23 tonhuisman: Add support for ILI9488 displays by implementing an adapted library (jaretburkett/ILI9488) @@ -153,6 +154,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -451,6 +453,8 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + if (Settings.InitSPI != 0) { # if P095_ENABLE_ILI948X @@ -470,7 +474,8 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) P095_CONFIG_FLAG_GET_CMD_TRIGGER)), P095_CONFIG_GET_COLOR_FOREGROUND, P095_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P095_CONFIG_FLAGS, P095_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P095_CONFIG_DEFAULT_FONT diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index 927b4e36c..954387be5 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -99,7 +99,8 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -108,7 +109,7 @@ P095_data_struct::P095_data_struct(ILI9xxx_type_e displayType, : _displayType(displayType), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill) + _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -165,7 +166,18 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { } else # endif // if P095_ENABLE_ILI948X { + # ifdef ESP32 // PIN(0) and PIN(1) swapped! + tft = new (std::nothrow) Adafruit_ILI9341(0 == _spi_bus ? &SPI : &SPI1, + PIN(1), + PIN(0), + PIN(2), + static_cast(_displayType), + _xpix, + _ypix); + # endif // ifdef ESP32 + # ifdef ESP8266 tft = new (std::nothrow) Adafruit_ILI9341(PIN(0), PIN(1), PIN(2), static_cast(_displayType), _xpix, _ypix); + # endif // ifdef ESP8266 if (nullptr != tft) { tft->begin(); diff --git a/src/src/PluginStructs/P095_data_struct.h b/src/src/PluginStructs/P095_data_struct.h index dec258d0e..04930d989 100644 --- a/src/src/PluginStructs/P095_data_struct.h +++ b/src/src/PluginStructs/P095_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P095 -# include // include Adafruit graphics library -# include // include Adafruit ILI9341 TFT library +# include // include Adafruit graphics library +# include // include Adafruit ILI9341 TFT library + +# include "../Globals/SPI1.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" @@ -137,10 +139,11 @@ public: String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P095_data_struct() = delete; @@ -208,6 +211,7 @@ private: uint16_t _fgcolor = ADAGFX_WHITE; uint16_t _bgcolor = ADAGFX_BLACK; bool _textBackFill = false; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED From a54228181c0b04c0721272a6623492df673ad675 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:20:51 +0200 Subject: [PATCH 003/161] [P104] Enable support for configurable SPI bus --- lib/MD_Parola/src/MD_Parola.h | 17 ++++ src/_P104_max7219_Dotmatrix.ino | 104 +++++++++++---------- src/src/PluginStructs/P104_data_struct.cpp | 10 +- src/src/PluginStructs/P104_data_struct.h | 2 + 4 files changed, 81 insertions(+), 52 deletions(-) diff --git a/lib/MD_Parola/src/MD_Parola.h b/lib/MD_Parola/src/MD_Parola.h index f116b9e4a..92e812bed 100644 --- a/lib/MD_Parola/src/MD_Parola.h +++ b/lib/MD_Parola/src/MD_Parola.h @@ -1343,6 +1343,23 @@ public: _D(mod, csPin, numDevices), _numModules(numDevices) {} + /** + * Class constructor - SPI hardware interface. + * + * Instantiate a new instance of the class. The parameters passed are used to + * connect the software to the hardware using the MD_MAX72XX class. + * + * See documentation for the MD_MAX72XX library for detailed explanation of parameters. + * + * \param mod the hardware module type used in the application. One of the MD_MAX72XX::moduleType_t values. + * \param spi the SPI interface to use + * \param csPin output for selecting the device. + * \param numDevices number of devices connected. Default is 1 if not supplied. + */ + MD_Parola(MD_MAX72XX::moduleType_t mod, SPIClass &spi, uint8_t csPin, uint8_t numDevices = 1) : + _D(mod, spi, csPin, numDevices), _numModules(numDevices) + {} + /** * Initialize the object. * diff --git a/src/_P104_max7219_Dotmatrix.ino b/src/_P104_max7219_Dotmatrix.ino index c00c18b4c..3bc461e11 100644 --- a/src/_P104_max7219_Dotmatrix.ino +++ b/src/_P104_max7219_Dotmatrix.ino @@ -72,56 +72,59 @@ // Display of the selected zone is suspended until all provided dots are drawn. No quotes are needed around // the coordinate set. // -// History: -// 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts -// 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments -// Re-enable use of settings-version V3 after some more testing -// 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility -// 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by -// a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended -// CustomTaskSettings file, by using the offset as a starting location for the data elements -// b) Combine storing the size and the data-block in a single save action -// Apply toStringNoZero() converter to reduce the settings-data to be saved -// 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the -// original content when that's updated...) -// Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress -// 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces -// 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag -// 2021-10-03 tonhuisman: Add Inverted option per zone -// 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures -// 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation -// and saving/loading per zone. That should sove the numerous stack related crashes. -// 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections -// 2021-07-18 tonhuisman: Small optimizations and improvements -// 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font -// 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed -// update/correct some documentation -// 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings -// 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order -// tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements -// All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING -// Disabled P104_DEBUG_DEV by default -// 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build -// 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes -// 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes -// 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes -// 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible -// 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH -// 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure -// 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by -// P104_USE_DATETIME_OPTIONS -// Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size -// 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization -// Webform_Load now works on current settings if the plugin is active, instead of last stored settings -// Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration -// 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands -// Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, -// Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight -// 2021-06-16 tonhuisman: Implement Clock and Date (once per second) -// 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation -// 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones -// 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy -// 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + +/** History: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts + * 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments + * Re-enable use of settings-version V3 after some more testing + * 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility + * 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by + * a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended + * CustomTaskSettings file, by using the offset as a starting location for the data elements + * b) Combine storing the size and the data-block in a single save action + * Apply toStringNoZero() converter to reduce the settings-data to be saved + * 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the + * original content when that's updated...) + * Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress + * 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces + * 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag + * 2021-10-03 tonhuisman: Add Inverted option per zone + * 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures + * 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation + * and saving/loading per zone. That should sove the numerous stack related crashes. + * 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections + * 2021-07-18 tonhuisman: Small optimizations and improvements + * 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font + * 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed + * update/correct some documentation + * 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings + * 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order + * tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements + * All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING + * Disabled P104_DEBUG_DEV by default + * 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build + * 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes + * 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes + * 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes + * 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible + * 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH + * 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure + * 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by + * P104_USE_DATETIME_OPTIONS + * Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size + * 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization + * Webform_Load now works on current settings if the plugin is active, instead of last stored settings + * Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration + * 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands + * Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, + * Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight + * 2021-06-16 tonhuisman: Implement Clock and Date (once per second) + * 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation + * 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones + * 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy + * 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + */ # define PLUGIN_104 # define PLUGIN_ID_104 104 @@ -143,6 +146,7 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) dev.Type = DEVICE_TYPE_SPI; dev.VType = Sensor_VType::SENSOR_TYPE_NONE; dev.ExitTaskBeforeSave = false; + dev.SpiBusSelect = true; break; } diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 3255bbdfd..60df7d099 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -33,8 +33,14 @@ P104_data_struct::P104_data_struct(MD_MAX72XX::moduleType_t _mod, uint8_t _modules, uint8_t _zonesCount) : mod(_mod), taskIndex(_taskIndex), cs_pin(_cs_pin), modules(_modules), expectedZones(_zonesCount) { - if (Settings.isSPI_valid()) { - P = new (std::nothrow) MD_Parola(mod, cs_pin, modules); + const uint8_t spi_bus = Settings.getSPIBusForTask(taskIndex); + + if (Settings.isSPI_valid(spi_bus)) { + P = new (std::nothrow) MD_Parola(mod, + # ifdef ESP32 + 0 == spi_bus ? SPI : SPI1, + # endif // ifdef ESP32 + cs_pin, modules); } else { addLog(LOG_LEVEL_ERROR, F("DOTMATRIX: Required SPI not enabled. Initialization aborted!")); } diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index 0fde33829..cd56f6621 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -16,6 +16,8 @@ # include "../Helpers/Misc.h" # include "../Helpers/StringParser.h" +#include "../Globals/SPI1.h" + # include // # if defined(PLUGIN_SET_MAX) || defined(PLUGIN_BUILD_CUSTOM) || (defined(PLUGIN_DISPLAY_COLLECTION) && defined(ESP32)) From 2bedef4d545863d4ca64736e0ba7dbbfb3bde6d7 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:21:13 +0200 Subject: [PATCH 004/161] [P116] Enable support for configurable SPI bus --- src/_P116_ST77xx.ino | 72 ++++++++++++---------- src/src/PluginStructs/P116_data_struct.cpp | 24 ++++++-- src/src/PluginStructs/P116_data_struct.h | 12 ++-- 3 files changed, 67 insertions(+), 41 deletions(-) diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index d537bc249..12e7458bd 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -7,35 +7,37 @@ // ####################################################################################################### -// History: -// 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution -// 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution -// 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included -// 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller -// used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 -// By default (also) only enabled on ESP32 builds -// Disabled the ST7789 alternatives for now, as that's not verified on any hardware -// 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on -// some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 -// By default only enabled on ESP32 builds -// 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details -// 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) -// 2021-11-16 tonhuisman: P116: Change state from Development to Testing -// 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters -// as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution -// 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays -// Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') -// 2021-08-16 tonhuisman: P116: Add default color settings -// 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) -// 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) -// P116: Use more features of AdafruitGFX_helper -// AdafruitGFX: Apply 'Text Print Mode' options -// 2021-08 tonhuisman: Refactor into AdafruitGFX_helper -// 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines -// update to current ESPEasy state/style of development, make multi-instance possible -// 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) -// Added several features like display button, rotation -// 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 +/** History: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus + * 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution + * 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution + * 2024-05-04 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included + * 2024-03-17 tonhuisman: Add support for another alternative initialization for ST7735 displays, as the display controller + * used on the LilyGO TTGO T-Display (16 MB) seems to be a ST7735, despite being documented as ST7789 + * By default (also) only enabled on ESP32 builds + * Disabled the ST7789 alternatives for now, as that's not verified on any hardware + * 2024-03-09 tonhuisman: Add support for alternative initialization sequences for ST7789 displays, like used on + * some LilyGO models like the TTGO T-Display (16 MB Flash), and possibly the T-Display S3 + * By default only enabled on ESP32 builds + * 2023-02-27 tonhuisman: Implement support for getting config values, see AdafruitGFX_Helper.h changelog for details + * 2022-07-06 tonhuisman: Add support for ST7735sv M5Stack StickC (Inverted colors) + * 2021-11-16 tonhuisman: P116: Change state from Development to Testing + * 2021-11-08 tonhuisman: Add support for function PLUGIN_GET_DISPLAY_PARAMETERS for retrieving the display parameters + * as implemented by FT6206 touchscreen plugin. Added ST77xx_type_toResolution + * 2021-11-06 tonhuisman: P116: Add support for ST7796s 320x480 displays + * Changed name of plugin to 'Display - ST77xx TFT' (was 'Display - ST7735/ST7789 TFT') + * 2021-08-16 tonhuisman: P116: Add default color settings + * 2021-08-16 tonhuisman: P116: Reorder some device configuration options, add backlight command (triggerCmd option) + * 2021-08-15 tonhuisman: P116: Make CursorX/CursorY coordinates available as Values (no events are generated!) + * P116: Use more features of AdafruitGFX_helper + * AdafruitGFX: Apply 'Text Print Mode' options + * 2021-08 tonhuisman: Refactor into AdafruitGFX_helper + * 2021-08 tonhuisman: Continue development, added new features, font scaling, display limits, extra text lines + * update to current ESPEasy state/style of development, make multi-instance possible + * 2020-08 tonhuisman: Adaptations for multiple ST77xx chips, ST7735s, ST7789vw (shelved temporarily) + * Added several features like display button, rotation + * 2020-04 WDS (Wolfdieter): initial plugin for ST7735, based on P012 + */ # define PLUGIN_116 # define PLUGIN_ID_116 116 @@ -60,6 +62,7 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 2; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -188,8 +191,8 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) constexpr int optCount4 = NR_ELEMENTS(optionValues4); const FormSelectorOptions selector(optCount4, options4, optionValues4); selector.addFormSelector(F("TFT display model"), - F("type"), - P116_CONFIG_FLAG_GET_TYPE); + F("type"), + P116_CONFIG_FLAG_GET_TYPE); } addFormSubHeader(F("Layout")); @@ -336,7 +339,9 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P116_data_struct(static_cast(P116_CONFIG_FLAG_GET_TYPE), P116_CONFIG_FLAG_GET_ROTATION, @@ -349,7 +354,8 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) P116_CONFIG_FLAG_GET_CMD_TRIGGER)), P116_CONFIG_GET_COLOR_FOREGROUND, P116_CONFIG_GET_COLOR_BACKGROUND, - bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0 + bitRead(P116_CONFIG_FLAGS, P116_CONFIG_FLAG_BACK_FILL) == 0, + spi_bus # if ADAGFX_FONTS_INCLUDED , P116_CONFIG_DEFAULT_FONT diff --git a/src/src/PluginStructs/P116_data_struct.cpp b/src/src/PluginStructs/P116_data_struct.cpp index b803d1703..f38ba8615 100644 --- a/src/src/PluginStructs/P116_data_struct.cpp +++ b/src/src/PluginStructs/P116_data_struct.cpp @@ -118,7 +118,8 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, String commandTrigger, uint16_t fgcolor, uint16_t bgcolor, - bool textBackFill + bool textBackFill, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -126,7 +127,7 @@ P116_data_struct::P116_data_struct(ST77xx_type_e device, ) : _device(device), _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _displayTimer(displayTimer), _displayTimeout(displayTimer), - _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill) + _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), _textBackFill(textBackFill), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -187,14 +188,14 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_BLACKTAB135x240; // 135x240px } - // fall through + // fall through case ST77xx_type_e::ST7735s_172x320: if (initRoptions == 0xFF) { initRoptions = INITR_BLACKTAB172x320; // 172x320px } - // fall through + // fall through case ST77xx_type_e::ST77xxs_170x320: if (initRoptions == 0xFF) { @@ -209,7 +210,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { initRoptions = INITR_MINI160x80; // 80x160px } + # ifdef ESP32 + st7735 = new (std::nothrow) Adafruit_ST7735(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7735 = new (std::nothrow) Adafruit_ST7735(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8166 if (nullptr != st7735) { st7735->initR(initRoptions); // initialize a ST7735s chip @@ -227,7 +233,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { case ST77xx_type_e::ST7789vw3_135x240: # endif // if P116_EXTRA_ST7789 { + # ifdef ESP32 + st7789 = new (std::nothrow) Adafruit_ST7789(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7789 = new (std::nothrow) Adafruit_ST7789(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7789) { uint8_t init_seq = 0; // Default/original initialisation @@ -249,7 +260,12 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { } case ST77xx_type_e::ST7796s_320x480: { + # ifdef ESP32 + st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP32 + # ifdef ESP8266 st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(PIN(0), PIN(1), PIN(2)); + # endif // ifdef ESP8266 if (nullptr != st7796) { st7796->begin(); diff --git a/src/src/PluginStructs/P116_data_struct.h b/src/src/PluginStructs/P116_data_struct.h index deaf3cfd4..009f053e9 100644 --- a/src/src/PluginStructs/P116_data_struct.h +++ b/src/src/PluginStructs/P116_data_struct.h @@ -32,9 +32,11 @@ # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" -# define P116_Nlines 24 // The number of different lines which can be displayed +# include "../Globals/SPI1.h" + +# define P116_Nlines 24 // The number of different lines which can be displayed # define P116_Nchars 60 -# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled +# define P116_DebounceTreshold 5 // number of 20 msec (fifty per second) ticks before the button has settled // # define P116_SHOW_SPLASH // Enable to show splash (text) @@ -133,10 +135,11 @@ public: String commandTrigger, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, - bool textBackFill = true + bool textBackFill = true, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); P116_data_struct() = delete; @@ -193,6 +196,7 @@ private: uint16_t _fgcolor; uint16_t _bgcolor; bool _textBackFill; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED From a96d865e8f4b5ecf2e9ade90cd4ebc7dc987463a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:21:34 +0200 Subject: [PATCH 005/161] [P141] Enable support for configurable SPI bus --- src/_P141_PCD8544_Nokia5110.ino | 9 +++++++-- src/src/PluginStructs/P141_data_struct.cpp | 11 ++++++++--- src/src/PluginStructs/P141_data_struct.h | 14 +++++++++----- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/_P141_PCD8544_Nokia5110.ino b/src/_P141_PCD8544_Nokia5110.ino index 7c79d281a..2511a34b0 100644 --- a/src/_P141_PCD8544_Nokia5110.ino +++ b/src/_P141_PCD8544_Nokia5110.ino @@ -8,6 +8,7 @@ /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-08-12 tonhuisman: Add Default font selection setting, if AdafruitGFX_Helper fonts are included * 2022-10-08 tonhuisman: Enable PLUGIN_GET_CONFIG_VALUE event to get runtime info from plugin * 2022-09-24 tonhuisman: Store inverted setting when changed via inv subcommand (not saved) @@ -53,6 +54,7 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = false; dev.TimerOption = true; dev.TimerOptional = true; + dev.SpiBusSelect = true; break; } @@ -248,7 +250,9 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, new (std::nothrow) P141_data_struct(P141_CONFIG_FLAG_GET_ROTATION, P141_CONFIG_FLAG_GET_FONTSCALE, @@ -262,7 +266,8 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) ADAGFX_WHITE, ADAGFX_BLACK, bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_BACK_FILL) == 0, - bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1 + bitRead(P141_CONFIG_FLAGS, P141_CONFIG_FLAG_INVERTED) == 1, + spi_bus # if ADAGFX_FONTS_INCLUDED , P141_CONFIG_DEFAULT_FONT diff --git a/src/src/PluginStructs/P141_data_struct.cpp b/src/src/PluginStructs/P141_data_struct.cpp index 74a23c95a..687ccbb7b 100644 --- a/src/src/PluginStructs/P141_data_struct.cpp +++ b/src/src/PluginStructs/P141_data_struct.cpp @@ -27,7 +27,8 @@ P141_data_struct::P141_data_struct(uint8_t rotation, uint16_t fgcolor, uint16_t bgcolor, bool textBackFill, - bool displayInverted + bool displayInverted, + uint8_t spi_bus # if ADAGFX_FONTS_INCLUDED , const uint8_t defaultFontId @@ -36,7 +37,7 @@ P141_data_struct::P141_data_struct(uint8_t rotation, : _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _backlightPin(backlightPin), _backlightPercentage(backlightPercentage), _contrast(contrast), _displayTimer(displayTimer), _displayTimeout(displayTimer), _commandTrigger(commandTrigger), _fgcolor(fgcolor), _bgcolor(bgcolor), - _textBackFill(textBackFill), _displayInverted(displayInverted) + _textBackFill(textBackFill), _displayInverted(displayInverted), _spi_bus(spi_bus) # if ADAGFX_FONTS_INCLUDED , _defaultFontId(defaultFontId) # endif // if ADAGFX_FONTS_INCLUDED @@ -66,7 +67,11 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { if (nullptr == pcd8544) { addLog(LOG_LEVEL_INFO, F("PCD8544: Init start.")); - pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN); + pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPI1 + # endif // ifdef ESP32 + ); # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { diff --git a/src/src/PluginStructs/P141_data_struct.h b/src/src/PluginStructs/P141_data_struct.h index 649eb89d8..0db63c379 100644 --- a/src/src/PluginStructs/P141_data_struct.h +++ b/src/src/PluginStructs/P141_data_struct.h @@ -4,8 +4,10 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P141 -# include // include Adafruit graphics library -# include // include Adafruit PCD8544 LCD library +# include // include Adafruit graphics library +# include // include Adafruit PCD8544 LCD library + +# include "../Globals/SPI1.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object @@ -90,13 +92,14 @@ public: uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, bool textBackFill = true, - bool displayInverted = false + bool displayInverted = false, + uint8_t spi_bus = 0 # if ADAGFX_FONTS_INCLUDED , - const uint8_t defaultFontId = 0 + const uint8_t defaultFontId = 0 # endif // if ADAGFX_FONTS_INCLUDED ); - P141_data_struct() = delete; + P141_data_struct() = delete; virtual ~P141_data_struct(); bool plugin_init(struct EventStruct *event); @@ -151,6 +154,7 @@ private: uint16_t _bgcolor; bool _textBackFill; bool _displayInverted; + uint8_t _spi_bus; # if ADAGFX_FONTS_INCLUDED uint8_t _defaultFontId; # endif // if ADAGFX_FONTS_INCLUDED From e59a630f4571dc1b7efa5ea480d3c0512c0600c9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 12 Aug 2025 22:21:50 +0200 Subject: [PATCH 006/161] [P172] Enable support for configurable SPI bus --- src/_P172_BMP3xx_SPI.ino | 2 ++ src/src/PluginStructs/P154_data_struct.cpp | 12 ++++++++++-- src/src/PluginStructs/P154_data_struct.h | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/_P172_BMP3xx_SPI.ino b/src/_P172_BMP3xx_SPI.ino index 7864d7255..e179ad307 100644 --- a/src/_P172_BMP3xx_SPI.ino +++ b/src/_P172_BMP3xx_SPI.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * Use all relevant code from P154 (I2C variant of the same sensor) * 2024-06-30 tonhuisman: Start SPI plugin, based on P154, re-using most code (dependency checked in define_plugin_sets.h) @@ -36,6 +37,7 @@ boolean Plugin_172(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } diff --git a/src/src/PluginStructs/P154_data_struct.cpp b/src/src/PluginStructs/P154_data_struct.cpp index 589dcc718..ad737d42b 100644 --- a/src/src/PluginStructs/P154_data_struct.cpp +++ b/src/src/PluginStructs/P154_data_struct.cpp @@ -10,7 +10,11 @@ P154_data_struct::P154_data_struct(struct EventStruct *event) : i2cAddress(P154_I2C_ADDR), elevation(P154_ALTITUDE), csPin(PIN(0)) -{} +{ + # ifdef ESP32 + _spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 +} bool P154_data_struct::begin(bool _i2cMode) { @@ -20,7 +24,11 @@ bool P154_data_struct::begin(bool _i2cMode) return false; } - if (!i2cMode && !bmp.begin_SPI(csPin)) { + if (!i2cMode && !bmp.begin_SPI(csPin + # ifdef ESP32 + , 0 == _spi_bus ? &SPI : &SPI1 + # endif // ifdef ESP32 + )) { return false; } diff --git a/src/src/PluginStructs/P154_data_struct.h b/src/src/PluginStructs/P154_data_struct.h index 4c507ff01..907255c76 100644 --- a/src/src/PluginStructs/P154_data_struct.h +++ b/src/src/PluginStructs/P154_data_struct.h @@ -7,6 +7,8 @@ # include # include +#include "../Globals/SPI1.h" + # define P154_I2C_ADDR PCONFIG(0) # define P154_ALTITUDE PCONFIG(1) @@ -36,6 +38,7 @@ private: uint8_t i2cAddress; int16_t elevation{}; int16_t csPin{}; + uint8_t _spi_bus{}; bool initialized = false; bool i2cMode = true; From 2229b55f3323c471b4dc34eb7664798064965f7e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 11:01:41 +0200 Subject: [PATCH 007/161] [P162] Enable support for configurable SPI bus --- src/_P162_MCP42xxx.ino | 5 ++++- src/src/PluginStructs/P162_data_struct.cpp | 19 +++++++++++-------- src/src/PluginStructs/P162_data_struct.h | 11 ++++++++--- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/_P162_MCP42xxx.ino b/src/_P162_MCP42xxx.ino index 77d3454c6..769949205 100644 --- a/src/_P162_MCP42xxx.ino +++ b/src/_P162_MCP42xxx.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2024-04-16 tonhuisman: Add Send values on change option, so Interval can be set to 0, and the data will be sent when changed * 2024-04-10 tonhuisman: Initial version. Support for Digipot MCP42xxx (dual channel) and MCP41xxx (single channel). @@ -40,6 +41,7 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.FormulaOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } @@ -123,7 +125,8 @@ boolean Plugin_162(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P162_data_struct(P162_CS_PIN, P162_RST_PIN, P162_SHD_PIN, spi_bus)); P162_data_struct *P162_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P162_data) { diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index 809b020eb..f762a85c1 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -9,18 +9,21 @@ // Needed also here for PlatformIO's library finder as the .h file // is in a directory which is excluded in the src_filter -P162_data_struct::P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin) - : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin) -{} +P162_data_struct::P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus) + : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin), _spi_bus(spi_bus) +{ + _spi = 0 == _spi_bus ? SPI : SPI1; +} P162_data_struct::~P162_data_struct() { // } bool P162_data_struct::plugin_init(struct EventStruct *event) { - if (validGpio(_csPin) && Settings.isSPI_valid()) { + if (validGpio(_csPin) && Settings.isSPI_valid(_spi_bus)) { pinMode(_csPin, OUTPUT); _initialized = true; } @@ -168,8 +171,8 @@ void P162_data_struct::write_pot(uint8_t cmd, digitalWrite(_csPin, LOW); // send the command and value via SPI: - SPI.transfer(cmd); - SPI.transfer(val); + _spi.transfer(cmd); + _spi.transfer(val); // Set the CS pin high to execute the command: digitalWrite(_csPin, HIGH); diff --git a/src/src/PluginStructs/P162_data_struct.h b/src/src/PluginStructs/P162_data_struct.h index b26916a3b..5baee59f4 100644 --- a/src/src/PluginStructs/P162_data_struct.h +++ b/src/src/PluginStructs/P162_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P162 +# include "../Globals/SPI1.h" + # define P162_CS_PIN PIN(0) # define P162_RST_PIN PIN(1) # define P162_SHD_PIN PIN(2) @@ -33,9 +35,10 @@ const uint8_t P162_BOTH_POT_SHUTDOWN = 0x23; const uint8_t P162_RESET_VALUE = 0x80; // Pot setting on power-up/reset struct P162_data_struct : public PluginTaskData_base { - P162_data_struct(int8_t csPin, - int8_t rstPin, - int8_t shdPin); + P162_data_struct(int8_t csPin, + int8_t rstPin, + int8_t shdPin, + uint8_t spi_bus); P162_data_struct() = delete; virtual ~P162_data_struct(); @@ -45,6 +48,7 @@ struct P162_data_struct : public PluginTaskData_base { private: + SPIClass& _spi = SPI; bool hw_reset(); void write_pot(uint8_t cmd, uint8_t val); @@ -57,6 +61,7 @@ private: int8_t _shdPin; uint8_t _shdState = HIGH; bool _initialized = false; + uint8_t _spi_bus; }; #endif // ifdef USES_P162 From 56f33690a53ba1e5c21ddcd1aaf4e347f96ded3a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 11:18:56 +0200 Subject: [PATCH 008/161] [P162] Fix compilation for ESP8266 --- src/src/PluginStructs/P162_data_struct.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index f762a85c1..e87ed5e6f 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -15,7 +15,9 @@ P162_data_struct::P162_data_struct(int8_t csPin, uint8_t spi_bus) : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin), _spi_bus(spi_bus) { + # ifdef ESP32 _spi = 0 == _spi_bus ? SPI : SPI1; + # endif // ifdef ESP32 } P162_data_struct::~P162_data_struct() { From 55fab588235759489bb8c67e7ee60757f9761377 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 11:29:56 +0200 Subject: [PATCH 009/161] [P125] Enable support for configurable SPI bus --- .../src/SparkFun_ADXL345.cpp | 16 +++++++++------- .../src/SparkFun_ADXL345.h | 6 +++++- src/_P125_ADXL345_SPI.ino | 2 ++ src/src/PluginStructs/P120_data_struct.cpp | 14 +++++++++++--- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp index cdf22b23a..4ebfe0415 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp @@ -22,7 +22,6 @@ #include "Arduino.h" #include "SparkFun_ADXL345.h" #include -#include #define ADXL345_DEVICE_DEFAULT (0x53) // Device Address for ADXL345 #define ADXL345_DEVICE (_i2c_addr) // Device Address for ADXL345 @@ -38,7 +37,7 @@ ADXL345::ADXL345(uint8_t i2c_addr = ADXL345_DEVICE_DEFAULT) : _i2c_addr(i2c_addr I2C = true; } -ADXL345::ADXL345(int CS) { +ADXL345::ADXL345(int CS, SPIClass& spi) { status = ADXL345_OK; error_code = ADXL345_NO_ERROR; @@ -47,9 +46,12 @@ ADXL345::ADXL345(int CS) { gains[2] = 0.00349265; _CS = CS; I2C = false; - // tonhuisman: disabled as SPI is already initialized in ESPEasy core. + + // 2021-12-13 tonhuisman: disabled as SPI is already initialized in ESPEasy core. // SPI.begin(); // SPI.setDataMode(SPI_MODE3); + // 2025-08-13 tonhuisman: provide external SPI bus + _spi = spi; pinMode(_CS, OUTPUT); digitalWrite(_CS, HIGH); } @@ -168,8 +170,8 @@ void ADXL345::readFromI2C(byte address, int num, byte _buff[]) { /* Point to Destination; Write Value; Turn Off */ void ADXL345::writeToSPI(byte __reg_address, byte __val) { digitalWrite(_CS, LOW); - SPI.transfer(__reg_address); - SPI.transfer(__val); + _spi.transfer(__reg_address); + _spi.transfer(__val); digitalWrite(_CS, HIGH); } @@ -185,10 +187,10 @@ void ADXL345::readFromSPI(byte __reg_address, int num, byte _buff[]) { } digitalWrite(_CS, LOW); - SPI.transfer(_address); // Transfer Starting Reg Address To Be Read + _spi.transfer(_address); // Transfer Starting Reg Address To Be Read for (int i = 0; i < num; i++) { - _buff[i] = SPI.transfer(0x00); + _buff[i] = _spi.transfer(0x00); } digitalWrite(_CS, HIGH); } diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h index c8cfb7750..0de0b5261 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.h @@ -21,6 +21,8 @@ #ifndef ADXL345_h # define ADXL345_h +# include + /*************************** REGISTER MAP ***************************/ # define ADXL345_DEVID 0x00 // Device ID # define ADXL345_RESERVED1 0x01 // Reserved. Do Not Access. @@ -115,7 +117,8 @@ public: double gains[3]; // Counts to Gs ADXL345(uint8_t i2c_addr); - ADXL345(int CS); + ADXL345(int CS, + SPIClass& spi = SPI); void powerOn(); void powerOff(); int getDevID(); @@ -266,6 +269,7 @@ private: bool I2C = true; unsigned long SPIfreq = 5000000; uint8_t _i2c_addr; + SPIClass& _spi = SPI; }; void print_byte(byte val); #endif // ifndef ADXL345_h diff --git a/src/_P125_ADXL345_SPI.ino b/src/_P125_ADXL345_SPI.ino index d90d8ed3f..f2a9295d5 100644 --- a/src/_P125_ADXL345_SPI.ino +++ b/src/_P125_ADXL345_SPI.ino @@ -11,6 +11,7 @@ */ /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for Accelerometer) * Use all relevant code from P120 (I2C variant of the same sensor) * 2021-12-10 tonhuisman, Start SPI interface version of ADXL345 plugin, based on P120 ADXL345 I2C plugin @@ -51,6 +52,7 @@ boolean Plugin_125(uint8_t function, struct EventStruct *event, String& string) dev.TimerOptional = true; dev.PluginStats = true; dev.OutputDataType = Output_Data_type_t::Simple; + dev.SpiBusSelect = true; break; } diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index ae63cc684..c5d525e3a 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -4,6 +4,7 @@ # define P120_RAD_TO_DEG 57.295779f // 180.0/M_PI +# include "../Globals/SPI1.h" P120_data_struct::P120_data_struct(uint8_t aSize) : _aSize(aSize) @@ -235,7 +236,14 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { if (i2c_mode) { adxl345 = new (std::nothrow) ADXL345(_i2c_addr); // Init using I2C } else { - adxl345 = new (std::nothrow) ADXL345(_cs_pin); // Init using SPI + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + # endif // ifdef ESP32 + adxl345 = new (std::nothrow) ADXL345(_cs_pin + # ifdef ESP32 + , 0 == spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); // Init using SPI } if (initialized()) { @@ -537,7 +545,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { FormSelectorOptions selector(4, rangeOptions, rangeValues); selector.default_index = P120_RANGE_16G; selector.addFormSelector(F("Range"), F("range"), - get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); + get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); addUnit('g'); } @@ -631,7 +639,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("10"), F("50") }; int frequencyValues[] = { P120_FREQUENCY_10, P120_FREQUENCY_50 }; - const FormSelectorOptions selector( 2, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(2, frequencyOptions, frequencyValues); selector.addFormSelector(F("Measuring frequency"), F("frequency"), P120_FREQUENCY); addUnit(F("Hz")); addFormNote(F("Values X/Y/Z are updated 1x per second, Controller updates & Value-events are based on 'Interval' setting.")); From 9e560d2c52f412c3689aa647e0a1f8250fdddd5c Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 11:55:13 +0200 Subject: [PATCH 010/161] [P118] Enable support for configurable SPI bus --- lib/Itho/CC1101.cpp | 34 +++++++++++----------- lib/Itho/CC1101.h | 6 ++-- lib/Itho/IthoCC1101.cpp | 2 +- lib/Itho/IthoCC1101.h | 23 ++++++++------- src/_P118_Itho.ino | 6 +++- src/src/PluginStructs/P118_data_struct.cpp | 19 +++++++----- src/src/PluginStructs/P118_data_struct.h | 20 ++++++++----- 7 files changed, 64 insertions(+), 46 deletions(-) diff --git a/lib/Itho/CC1101.cpp b/lib/Itho/CC1101.cpp index 1fb2de787..7a81ffbd1 100644 --- a/lib/Itho/CC1101.cpp +++ b/lib/Itho/CC1101.cpp @@ -5,7 +5,7 @@ #include "CC1101.h" // default constructor -CC1101::CC1101(int8_t CSpin, int8_t MISOpin) : _CSpin(CSpin), _MISOpin(MISOpin) +CC1101::CC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi) : _CSpin(CSpin), _MISOpin(MISOpin), _spi(spi) { // SPI.begin(); // Done already by ESPEasy pinMode(_CSpin, OUTPUT); @@ -51,7 +51,7 @@ void CC1101::reset() select(); spi_waitMiso(); - SPI.transfer(CC1101_SRES); + _spi.transfer(CC1101_SRES); delay(10); spi_waitMiso(); deselect(); @@ -63,7 +63,7 @@ uint8_t CC1101::writeCommand(uint8_t command) select(); spi_waitMiso(); - result = SPI.transfer(command); + result = _spi.transfer(command); deselect(); return result; @@ -73,8 +73,8 @@ void CC1101::writeRegister(uint8_t address, uint8_t data) { select(); spi_waitMiso(); - SPI.transfer(address); - SPI.transfer(data); + _spi.transfer(address); + _spi.transfer(data); deselect(); } @@ -84,8 +84,8 @@ uint8_t CC1101::readRegister(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val = SPI.transfer(0); + _spi.transfer(address); + val = _spi.transfer(0); deselect(); return val; @@ -97,12 +97,12 @@ uint8_t CC1101::readRegisterMedian3(uint8_t address) select(); spi_waitMiso(); - SPI.transfer(address); - val1 = SPI.transfer(0); - SPI.transfer(address); - val2 = SPI.transfer(0); - SPI.transfer(address); - val3 = SPI.transfer(0); + _spi.transfer(address); + val1 = _spi.transfer(0); + _spi.transfer(address); + val2 = _spi.transfer(0); + _spi.transfer(address); + val3 = _spi.transfer(0); deselect(); // reverse sort (largest in val1) because this is te expected order for TX_BUFFER @@ -162,10 +162,10 @@ void CC1101::writeBurstRegister(uint8_t address, uint8_t *data, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_WRITE_BURST); + _spi.transfer(address | CC1101_WRITE_BURST); for (i = 0; i < length; i++) { - SPI.transfer(pgm_read_byte(&(data[i]))); + _spi.transfer(pgm_read_byte(&(data[i]))); } deselect(); } @@ -176,10 +176,10 @@ void CC1101::readBurstRegister(uint8_t *buffer, uint8_t address, uint8_t length) select(); spi_waitMiso(); - SPI.transfer(address | CC1101_READ_BURST); + _spi.transfer(address | CC1101_READ_BURST); for (i = 0; i < length; i++) { - buffer[i] = SPI.transfer(0x00); + buffer[i] = _spi.transfer(0x00); } deselect(); diff --git a/lib/Itho/CC1101.h b/lib/Itho/CC1101.h index 0fd489319..8c01d31f3 100644 --- a/lib/Itho/CC1101.h +++ b/lib/Itho/CC1101.h @@ -191,8 +191,9 @@ protected: public: - CC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO); + CC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI); virtual ~CC1101(); // spi @@ -230,6 +231,7 @@ private: int8_t _CSpin = PIN_SPI_SS; int8_t _MISOpin; + SPIClass& _spi = SPI; protected: diff --git a/lib/Itho/IthoCC1101.cpp b/lib/Itho/IthoCC1101.cpp index e9a27dd7f..dbd227072 100644 --- a/lib/Itho/IthoCC1101.cpp +++ b/lib/Itho/IthoCC1101.cpp @@ -42,7 +42,7 @@ #define MDMCFG2 0x02 // 16bit sync word / 16bit specific // default constructor -IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin) +IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin, spi) { this->outIthoPacket.counter = counter; this->sendTries = sendTries; diff --git a/lib/Itho/IthoCC1101.h b/lib/Itho/IthoCC1101.h index 399f279c2..17c9a9fab 100644 --- a/lib/Itho/IthoCC1101.h +++ b/lib/Itho/IthoCC1101.h @@ -78,10 +78,11 @@ private: public: - IthoCC1101(int8_t CSpin = PIN_SPI_SS, - int8_t MISOpin = MISO, - uint8_t counter = 0, - uint8_t sendTries = 3); // set initial counter value + IthoCC1101(int8_t CSpin = PIN_SPI_SS, + int8_t MISOpin = MISO, + SPIClass& spi = SPI, + uint8_t counter = 0, + uint8_t sendTries = 3); // set initial counter value ~IthoCC1101(); // init @@ -90,7 +91,8 @@ public: initReceive(); } // init,reset CC1101 - void initReceive(); + void initReceive(); + // uint8_t getLastCounter() const { // return outIthoPacket.counter; // } // counter is increased before sending a command @@ -104,7 +106,7 @@ public: } // receive - bool checkForNewPacket(); // check RX fifo for new data + bool checkForNewPacket(); // check RX fifo for new data // IthoPacket getLastPacket() const { // return inIthoPacket; // } // retrieve last received/parsed packet from remote @@ -120,14 +122,15 @@ public: // uint8_t ReadRSSI(); // bool checkID(const uint8_t *id) const; // int* getLastID(); - String getLastIDstr(bool ashex = true); + String getLastIDstr(bool ashex = true); + // String getLastMessagestr(bool ashex = true); // String LastMessageDecoded() const; // send - void sendCommand(IthoCommand command, - uint8_t srcId[3] = 0, - uint8_t destId[3] = 0); + void sendCommand(IthoCommand command, + uint8_t srcId[3] = 0, + uint8_t destId[3] = 0); void enableOrcon(bool state); diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino index 1c83be4f8..3cec889a8 100644 --- a/src/_P118_Itho.ino +++ b/src/_P118_Itho.ino @@ -17,6 +17,7 @@ // https://github.com/arjenhiemstra/IthoEcoFanRFT /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for ITHO) * Changelog is reverted and reformatted! * 05-03-2023 tonhuisman: Deprecate 'state' command, and add support for 'itho' as the main command @@ -135,6 +136,7 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; dev.ValueCount = 3; dev.SendDataOption = true; + dev.SpiBusSelect = true; break; } @@ -198,10 +200,12 @@ boolean Plugin_118(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P118_DEBUG_LOG if (validGpio(P118_CSPIN) && (P118_IRQPIN != P118_CSPIN)) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P118_data_struct(P118_CSPIN, P118_IRQPIN, P118_CONFIG_LOG == 1, - P118_CONFIG_RF_LOG == 1)); + P118_CONFIG_RF_LOG == 1, + spi_bus)); P118_data_struct *P118_data = static_cast(getPluginTaskData(event->TaskIndex)); success = (nullptr != P118_data) && P118_data->plugin_init(event); diff --git a/src/src/PluginStructs/P118_data_struct.cpp b/src/src/PluginStructs/P118_data_struct.cpp index a33615c8d..fe4815813 100644 --- a/src/src/PluginStructs/P118_data_struct.cpp +++ b/src/src/PluginStructs/P118_data_struct.cpp @@ -5,11 +5,12 @@ // **************************************************************************/ // Constructor // **************************************************************************/ -P118_data_struct::P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog) - : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog) {} +P118_data_struct::P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus) + : _csPin(csPin), _irqPin(irqPin), _log(logData), _rfLog(rfLog), _spi_bus(spi_bus) {} // **************************************************************************/ // Destructor @@ -30,9 +31,13 @@ bool P118_data_struct::plugin_init(struct EventStruct *event) { int8_t spi_pins[3]; uint32_t startInit = 0; - if (Settings.getSPI_pins(spi_pins) && validGpio(spi_pins[1])) { + if (Settings.getSPI_pins(spi_pins, _spi_bus) && validGpio(spi_pins[1])) { startInit = millis(); - _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1]); // Pass CS and MISO + _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1] + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 // defaults and SPI bus for ESP32 only + # endif // ifdef ESP32 + ); // Pass CS and MISO } else { addLog(LOG_LEVEL_ERROR, F("ITHO: SPI configuration not correct!")); } diff --git a/src/src/PluginStructs/P118_data_struct.h b/src/src/PluginStructs/P118_data_struct.h index fe6bba777..3d4798ba6 100644 --- a/src/src/PluginStructs/P118_data_struct.h +++ b/src/src/PluginStructs/P118_data_struct.h @@ -9,6 +9,8 @@ # include "IthoCC1101.h" # include "IthoPacket.h" +# include "../Globals/SPI1.h" + # define P118_DEBUG_LOG // Enable for some (extra) logging # ifndef P118_FEATURE_ORCON # define P118_FEATURE_ORCON 1 // Enable use of Orcon commands @@ -60,10 +62,11 @@ struct PLUGIN_118_ExtraSettingsStruct { struct P118_data_struct : public PluginTaskData_base { public: - P118_data_struct(int8_t csPin, - int8_t irqPin, - bool logData, - bool rfLog); + P118_data_struct(int8_t csPin, + int8_t irqPin, + bool logData, + bool rfLog, + uint8_t spi_bus); P118_data_struct() = delete; virtual ~P118_data_struct(); @@ -104,10 +107,11 @@ private: int _OldLastIDindex = 0; bool _InitRunned = false; - int8_t _csPin = -1; - int8_t _irqPin = -1; - bool _log = false; - bool _rfLog = false; + int8_t _csPin = -1; + int8_t _irqPin = -1; + bool _log = false; + bool _rfLog = false; + uint8_t _spi_bus; PLUGIN_118_ExtraSettingsStruct _ExtraSettings; From b753fea2ba8520be52cc86a40364de0178ac32e9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 12:18:03 +0200 Subject: [PATCH 011/161] [P111] Enable support for configurable SPI bus (and reformat source) --- lib/MFRC522/MFRC522.cpp | 47 +++++++++---------- lib/MFRC522/MFRC522.h | 3 +- src/_P111_RC522_RFID.ino | 5 +- src/src/PluginStructs/P111_data_struct.cpp | 53 +++++++++++++--------- src/src/PluginStructs/P111_data_struct.h | 38 +++++++++------- 5 files changed, 83 insertions(+), 63 deletions(-) diff --git a/lib/MFRC522/MFRC522.cpp b/lib/MFRC522/MFRC522.cpp index 0d6d81669..748b754d4 100644 --- a/lib/MFRC522/MFRC522.cpp +++ b/lib/MFRC522/MFRC522.cpp @@ -29,11 +29,12 @@ MFRC522::MFRC522( uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC52 * Prepares the output pins. */ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) - uint8_t resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). - ) { - _chipSelectPin = chipSelectPin; - _resetPowerDownPin = resetPowerDownPin; -} // End constructor + uint8_t resetPowerDownPin, ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). + SPIClass& spi) : + _chipSelectPin(chipSelectPin), + _resetPowerDownPin(resetPowerDownPin), + _spi(spi) +{} // End constructor ///////////////////////////////////////////////////////////////////////////////////// // Basic interface functions for communicating with the MFRC522 @@ -46,12 +47,12 @@ MFRC522::MFRC522( uint8_t chipSelectPin, ///< Arduino pin connected to MFRC522' void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. uint8_t value ///< The value to write. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. - SPI.transfer(value); + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(value); digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -62,14 +63,14 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t count, ///< The number of bytes to write to the register uint8_t *values ///< The values to write. Byte array. ) { - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + _spi.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. for (uint8_t index = 0; index < count; index++) { - SPI.transfer(values[index]); + _spi.transfer(values[index]); } digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_WriteRegister() /** @@ -79,12 +80,12 @@ void MFRC522::PCD_WriteRegister( PCD_Register reg, ///< The register to write to uint8_t MFRC522::PCD_ReadRegister( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. ) { uint8_t value; - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave - SPI.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. - value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. + _spi.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + value = _spi.transfer(0); // Read the value back. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus return value; } // End PCD_ReadRegister() @@ -103,26 +104,26 @@ void MFRC522::PCD_ReadRegister( PCD_Register reg, ///< The register to read from //Serial.print(F("Reading ")); Serial.print(count); Serial.println(F(" bytes from register.")); uint8_t address = 0x80 | reg; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. uint8_t index = 0; // Index in values array. - SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + _spi.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus digitalWrite(_chipSelectPin, LOW); // Select slave count--; // One read is performed outside of the loop - SPI.transfer(address); // Tell MFRC522 which address we want to read + _spi.transfer(address); // Tell MFRC522 which address we want to read if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] // Create bit mask for bit positions rxAlign..7 uint8_t mask = (0xFF << rxAlign) & 0xFF; // Read value and tell that we want to read the same address again. - uint8_t value = SPI.transfer(address); + uint8_t value = _spi.transfer(address); // Apply mask to both current value of values[0] and the new data in value. values[0] = (values[0] & ~mask) | (value & mask); index++; } while (index < count) { - values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. + values[index] = _spi.transfer(address); // Read value and tell that we want to read the same address again. index++; } - values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. + values[index] = _spi.transfer(0); // Read the final byte. Send 0 to stop reading. digitalWrite(_chipSelectPin, HIGH); // Release slave again - SPI.endTransaction(); // Stop using the SPI bus + _spi.endTransaction(); // Stop using the SPI bus } // End PCD_ReadRegister() /** diff --git a/lib/MFRC522/MFRC522.h b/lib/MFRC522/MFRC522.h index 0a7f0e426..15479d540 100644 --- a/lib/MFRC522/MFRC522.h +++ b/lib/MFRC522/MFRC522.h @@ -293,7 +293,7 @@ public: ///////////////////////////////////////////////////////////////////////////////////// MFRC522(); MFRC522(uint8_t resetPowerDownPin); - MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin); + MFRC522(uint8_t chipSelectPin, uint8_t resetPowerDownPin, SPIClass& spi = SPI); virtual ~MFRC522() {}; ///////////////////////////////////////////////////////////////////////////////////// @@ -389,6 +389,7 @@ public: protected: uint8_t _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) uint8_t _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + SPIClass& _spi = SPI; StatusCode MIFARE_TwoStepHelper(uint8_t command, uint8_t blockAddr, int32_t data); }; diff --git a/src/_P111_RC522_RFID.ino b/src/_P111_RC522_RFID.ino index 0f89afd59..89c6792a3 100644 --- a/src/_P111_RC522_RFID.ino +++ b/src/_P111_RC522_RFID.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-06-14 tonhuisman: Add support for Custom Value Type per task value * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for RFID) * Update changelog @@ -45,6 +46,7 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) dev.ValueCount = 1; dev.SendDataOption = true; dev.CustomVTypeVar = true; + dev.SpiBusSelect = true; break; } @@ -161,7 +163,8 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN)); + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P111_data_struct(P111_CS_PIN, P111_RST_PIN, P111_IRQ_PIN, spi_bus)); P111_data_struct *P111_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P111_data) { diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index 2f5fcebc6..af2bb9bbc 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -10,10 +10,11 @@ # include -P111_data_struct::P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin) - : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin) +P111_data_struct::P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus) + : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin), _irqPin(irqPin), _spi_bus(spi_bus) {} P111_data_struct::~P111_data_struct() { @@ -28,12 +29,17 @@ P111_data_struct::~P111_data_struct() { void P111_data_struct::init() { delete mfrc522; - mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin); // Instantiate a MFRC522 + // Instantiate a MFRC522 + mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); if (mfrc522 != nullptr) { - mfrc522->PCD_Init(); // Initialize MFRC522 reader + mfrc522->PCD_Init(); // Initialize MFRC522 reader mfrc522->PCD_WriteRegister(MFRC522::ComIEnReg, 0b10100000); // enable receiver interrupt - mfrc522->PCD_WriteRegister(MFRC522::DivIEnReg, 0x80); // Set as CMOS output pin + mfrc522->PCD_WriteRegister(MFRC522::DivIEnReg, 0x80); // Set as CMOS output pin initPhase = P111_initPhases::Ready; if (validGpio(_irqPin)) { @@ -134,7 +140,7 @@ bool P111_data_struct::reset(int8_t csPin, (initPhase == P111_initPhases::Ready)) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove( - LOG_LEVEL_INFO, + LOG_LEVEL_INFO, concat(F("MFRC522: Reset on pin: "), resetPin)); } @@ -161,9 +167,9 @@ bool P111_data_struct::reset(int8_t csPin, pinMode(csPin, OUTPUT); digitalWrite(csPin, LOW); - mfrc522->PCD_Init(csPin, resetPin); // Init MFRC522 module + mfrc522->PCD_Init(csPin, resetPin); // Init MFRC522 module mfrc522->PCD_WriteRegister(MFRC522::ComIEnReg, 0b10100000); // enable receiver interrupt - mfrc522->PCD_WriteRegister(MFRC522::DivIEnReg, 0x80); // Set as CMOS output pin + mfrc522->PCD_WriteRegister(MFRC522::DivIEnReg, 0x80); // Set as CMOS output pin // If you set Antenna Gain to Max it will increase reading distance mfrc522->PCD_SetAntennaGain(mfrc522->RxGain_max); @@ -232,23 +238,25 @@ uint8_t P111_data_struct::readPassiveTargetID(uint8_t *uid, *uidLength = 4; - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { PrintToString p2str; mfrc522->PICC_DumpToSerial(&(mfrc522->uid), p2str); + if (p2str.length()) { addLog(LOG_LEVEL_DEBUG, concat(F("MFRC522: "), p2str.get())); } } - #endif + # endif // ifndef BUILD_NO_DEBUG mfrc522->PICC_HaltA(); // Stop reading return P111_NO_ERROR; } -void P111_data_struct::mfrc522_interrupt(P111_data_struct * self) +void P111_data_struct::mfrc522_interrupt(P111_data_struct *self) { self->_irq_pin_time_micros = getMicros64(); } @@ -272,11 +280,11 @@ bool P111_data_struct::loop(struct EventStruct *event) { return success; } - counter++; // This variable replaces a static variable in the original implementation - const bool ComIrqReg_bits = (mfrc522->PCD_ReadRegister(MFRC522::ComIrqReg) & (1<<5)) != 0; + counter++; // This variable replaces a static variable in the original implementation + const bool ComIrqReg_bits = (mfrc522->PCD_ReadRegister(MFRC522::ComIrqReg) & (1 << 5)) != 0; mfrc522->PCD_WriteRegister(MFRC522::ComIrqReg, 0x34); - if (counter >= 10 || ComIrqReg_bits) { // Only every 3rd 0.1 second we do a read + if ((counter >= 10) || ComIrqReg_bits) { // Only every 3rd 0.1 second we do a read counter = 0; uint32_t key = P111_NO_KEY; @@ -346,24 +354,28 @@ bool P111_data_struct::plugin_fifty_per_second(struct EventStruct *event) { } } } + if (_irq_pin_time_micros > _last_served_irq_pin_time_micros) { _last_served_irq_pin_time_micros = _irq_pin_time_micros; - //addLog(LOG_LEVEL_INFO, F("P111: acting on interrupt")); + + // addLog(LOG_LEVEL_INFO, F("P111: acting on interrupt")); loop(event); } return true; } - String P111_data_struct::PCD_getVersion(uint8_t& v) { v = 0xFF; + if (mfrc522) { v = mfrc522->PCD_ReadRegister(MFRC522::VersionReg); - if (v != 0xFF && v != 0) { + + if ((v != 0xFF) && (v != 0)) { // Human readable version. String res = concat(formatToHex(v, 2), F(" = ")); - switch(v) { + + switch (v) { case 0xb2: res += F("FM17522_1"); break; @@ -395,5 +407,4 @@ String P111_data_struct::PCD_getVersion(uint8_t& v) { return EMPTY_STRING; } - #endif // ifdef USES_P111 diff --git a/src/src/PluginStructs/P111_data_struct.h b/src/src/PluginStructs/P111_data_struct.h index 8acabdc6f..b8c1a22d4 100644 --- a/src/src/PluginStructs/P111_data_struct.h +++ b/src/src/PluginStructs/P111_data_struct.h @@ -6,6 +6,8 @@ # include +# include "../Globals/SPI1.h" + # define P111_CS_PIN PIN(0) # define P111_RST_PIN PIN(1) # define P111_IRQ_PIN PIN(2) @@ -31,15 +33,16 @@ enum class P111_initPhases : uint8_t { }; struct P111_data_struct : public PluginTaskData_base { - P111_data_struct(int8_t csPin, - int8_t rstPin, - int8_t irqPin); + P111_data_struct(int8_t csPin, + int8_t rstPin, + int8_t irqPin, + uint8_t spi_bus); P111_data_struct() = delete; virtual ~P111_data_struct(); - void init(); - bool plugin_ten_per_second(struct EventStruct *event); - bool plugin_fifty_per_second(struct EventStruct *event); + void init(); + bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_fifty_per_second(struct EventStruct *event); String PCD_getVersion(uint8_t& v); @@ -51,21 +54,22 @@ private: uint8_t counter = 0; - String getCardName(); - uint8_t readCardStatus(uint32_t *key, - bool *removedTag); - bool reset(int8_t csPin, - int8_t resetPin); - uint8_t readPassiveTargetID(uint8_t *uid, - uint8_t *uidLength); + String getCardName(); + uint8_t readCardStatus(uint32_t *key, + bool *removedTag); + bool reset(int8_t csPin, + int8_t resetPin); + uint8_t readPassiveTargetID(uint8_t *uid, + uint8_t *uidLength); - static void mfrc522_interrupt(P111_data_struct * self); + static void mfrc522_interrupt(P111_data_struct *self); int32_t timeToWait = 0; - int8_t _csPin; - int8_t _rstPin; - int8_t _irqPin; + int8_t _csPin; + int8_t _rstPin; + int8_t _irqPin; + uint8_t _spi_bus; uint8_t errorCount = 0; bool removedState = true; // On startup, there will usually not be a tag nearby From 7e13c7bd0cf08d71655e87d8b83b9b573cdedcea Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 14:01:25 +0200 Subject: [PATCH 012/161] [P099] Enable support for configurable SPI bus --- .../XPT2046_Touchscreen.cpp | 26 ++--- lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h | 13 ++- src/_P099_XPT2046Touch.ino | 18 ++-- src/src/PluginStructs/P099_data_struct.cpp | 12 ++- src/src/PluginStructs/P099_data_struct.h | 95 ++++++++++--------- 5 files changed, 92 insertions(+), 72 deletions(-) diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp index 75ba44ee5..f884ffbfb 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.cpp @@ -32,7 +32,7 @@ void isrPin(void); bool XPT2046_Touchscreen::begin() { - SPI.begin(); + // _spi.begin(); // ESPEasy already does this pinMode(csPin, OUTPUT); digitalWrite(csPin, HIGH); if (255 != tirqPin) { @@ -105,25 +105,25 @@ void XPT2046_Touchscreen::update() uint32_t now = millis(); if (now - msraw < MSEC_THRESHOLD) return; - SPI.beginTransaction(SPI_SETTING); + _spi.beginTransaction(SPI_SETTING); digitalWrite(csPin, LOW); - SPI.transfer(0xB1 /* Z1 */); - int16_t z1 = SPI.transfer16(0xC1 /* Z2 */) >> 3; + _spi.transfer(0xB1 /* Z1 */); + int16_t z1 = _spi.transfer16(0xC1 /* Z2 */) >> 3; int z = z1 + 4095; - int16_t z2 = SPI.transfer16(0x91 /* X */) >> 3; + int16_t z2 = _spi.transfer16(0x91 /* X */) >> 3; z -= z2; if (z >= Z_THRESHOLD) { - SPI.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy - data[0] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[1] = SPI.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements - data[2] = SPI.transfer16(0xD1 /* Y */) >> 3; - data[3] = SPI.transfer16(0x91 /* X */) >> 3; + _spi.transfer16(0x91 /* X */); // dummy X measure, 1st is always noisy + data[0] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[1] = _spi.transfer16(0x91 /* X */) >> 3; // make 3 x-y measurements + data[2] = _spi.transfer16(0xD1 /* Y */) >> 3; + data[3] = _spi.transfer16(0x91 /* X */) >> 3; } else data[0] = data[1] = data[2] = data[3] = 0; // Compiler warns these values may be used unset on early exit. - data[4] = SPI.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down - data[5] = SPI.transfer16(0) >> 3; + data[4] = _spi.transfer16(0xD0 /* Y */) >> 3; // Last Y touch power down + data[5] = _spi.transfer16(0) >> 3; digitalWrite(csPin, HIGH); - SPI.endTransaction(); + _spi.endTransaction(); //Serial.printf("z=%d :: z1=%d, z2=%d ", z, z1, z2); if (z < 0) z = 0; if (z < Z_THRESHOLD) { // if ( !touched ) { diff --git a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h index d723ca134..878b15d7b 100644 --- a/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h +++ b/lib/XPT2046_Touchscreen/XPT2046_Touchscreen.h @@ -41,8 +41,8 @@ public: class XPT2046_Touchscreen { public: - constexpr XPT2046_Touchscreen(uint8_t cspin, uint8_t tirq=255) - : csPin(cspin), tirqPin(tirq) { } + constexpr XPT2046_Touchscreen(uint8_t cspin, SPIClass& spi = SPI, uint8_t tirq=255) + : csPin(cspin), _spi(spi), tirqPin(tirq) { } bool begin(); TS_Point getPoint(); bool tirqTouched(); @@ -57,8 +57,13 @@ public: private: void update(); - uint8_t csPin, tirqPin, rotation = 1; - int16_t xraw = 0, yraw = 0, zraw = 0; + uint8_t csPin; + SPIClass& _spi = SPI; + uint8_t tirqPin; + uint8_t rotation = 1; + int16_t xraw = 0; + int16_t yraw = 0; + int16_t zraw = 0; uint32_t msraw = 0x80000000; bool flipped = false; }; diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index 430c4afea..06c2bfeab 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -6,6 +6,7 @@ /** * Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported for Touch) * 2020-11-01 tonhuisman: Solved previous strange rotation settings to be compatible with TFT ILI9341 * 2020-11-01 tonhuisman: Add option to flip rotation by 180 deg, and command touch,flip,<0|1> @@ -52,10 +53,11 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_DEVICE_ADD: { auto& dev = Device[++deviceCount]; - dev.Number = PLUGIN_ID_099; - dev.Type = DEVICE_TYPE_SPI; - dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; - dev.ValueCount = 3; + dev.Number = PLUGIN_ID_099; + dev.Type = DEVICE_TYPE_SPI; + dev.VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + dev.ValueCount = 3; + dev.SpiBusSelect = true; break; } @@ -81,14 +83,14 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) break; } - # if FEATURE_MQTT_DISCOVER + #if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { event->Par1 = static_cast(Sensor_VType::SENSOR_TYPE_NONE); // Not yet supported success = true; break; } - # endif // if FEATURE_MQTT_DISCOVER + #endif // if FEATURE_MQTT_DISCOVER case PLUGIN_SET_DEFAULTS: { @@ -362,6 +364,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); initPluginTaskData(event->TaskIndex, new (std::nothrow) P099_data_struct()); P099_data_struct *P099_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -374,7 +377,8 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) bitRead(P099_CONFIG_FLAGS, P099_FLAGS_SEND_Z), bitRead(P099_CONFIG_FLAGS, P099_FLAGS_USE_CALIBRATION), P099_CONFIG_X_RES, - P099_CONFIG_Y_RES); + P099_CONFIG_Y_RES, + spi_bus); break; } diff --git a/src/src/PluginStructs/P099_data_struct.cpp b/src/src/PluginStructs/P099_data_struct.cpp index c89004787..a91d5e35c 100644 --- a/src/src/PluginStructs/P099_data_struct.cpp +++ b/src/src/PluginStructs/P099_data_struct.cpp @@ -42,7 +42,8 @@ bool P099_data_struct::init(taskIndex_t taskIndex, bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res) { + uint16_t ts_y_res, + uint8_t spi_bus) { reset(); _address_ts_cs = cs; @@ -54,8 +55,13 @@ bool P099_data_struct::init(taskIndex_t taskIndex, _useCalibration = useCalibration; _ts_x_res = ts_x_res; _ts_y_res = ts_y_res; + _spi_bus = spi_bus; - touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs); + touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); if (touchscreen != nullptr) { touchscreen->setRotation(_rotation); @@ -328,7 +334,7 @@ bool P099_data_struct::plugin_write(struct EventStruct *event, const String& str subcommand = parseString(string, 2); if (equals(command, F("touch"))) { - int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); + int command_i = GetCommandCode(subcommand.c_str(), p099_subcommands); if (command_i == -1) { // No matching subcommand found diff --git a/src/src/PluginStructs/P099_data_struct.h b/src/src/PluginStructs/P099_data_struct.h index 89814a6c1..cbe2d8cb1 100644 --- a/src/src/PluginStructs/P099_data_struct.h +++ b/src/src/PluginStructs/P099_data_struct.h @@ -8,64 +8,66 @@ # include +# include "../Globals/SPI1.h" + // #define PLUGIN_099_DEBUG // Additional debugging information // Define default values for both ESP32/lolin32 and D1 Mini # ifdef ESP32 # define P099_TS_CS 12 # else // ESP8266/ESP8285 - # define P099_TS_CS 0 // D3 + # define P099_TS_CS 0 // D3 # endif // ESP32 -# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 -# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 - // bytes calibration coordinates +# define P099_MaxObjectNameLength 15 // 14 character objectnames + terminating 0 +# define P099_MaxObjectCount 40 // This count of touchobjects should be enough, because of limited settings storage, 960 bytes + 8 + // bytes calibration coordinates -# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function -# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function +# define P099_FLAGS_ON_OFF_BUTTON 0 // TouchObjects.flags On/Off Button function +# define P099_FLAGS_INVERT_BUTTON 1 // TouchObjects.flags Inverted On/Off Button function -#define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS -#define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_XY 0 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_Z 1 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_SEND_OBJECTNAME 2 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_USE_CALIBRATION 3 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_LOG_CALIBRATION 4 // Set in P099_CONFIG_FLAGS +# define P099_FLAGS_ROTATION_FLIPPED 5 // Set in P099_CONFIG_FLAGS -#define P099_CONFIG_STATE PCONFIG(0) -#define P099_CONFIG_CS_PIN PIN(0) -#define P099_CONFIG_TRESHOLD PCONFIG(1) -#define P099_CONFIG_ROTATION PCONFIG(2) -#define P099_CONFIG_X_RES PCONFIG(3) -#define P099_CONFIG_Y_RES PCONFIG(4) -#define P099_CONFIG_OBJECTCOUNT PCONFIG(5) -#define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) -#define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags +# define P099_CONFIG_STATE PCONFIG(0) +# define P099_CONFIG_CS_PIN PIN(0) +# define P099_CONFIG_TRESHOLD PCONFIG(1) +# define P099_CONFIG_ROTATION PCONFIG(2) +# define P099_CONFIG_X_RES PCONFIG(3) +# define P099_CONFIG_Y_RES PCONFIG(4) +# define P099_CONFIG_OBJECTCOUNT PCONFIG(5) +# define P099_CONFIG_DEBOUNCE_MS PCONFIG(6) +# define P099_CONFIG_FLAGS PCONFIG_LONG(0) // 0-31 flags -#define P099_VALUE_X UserVar[event->BaseVarIndex + 0] -#define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] -#define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] +# define P099_VALUE_X UserVar[event->BaseVarIndex + 0] +# define P099_VALUE_Y UserVar[event->BaseVarIndex + 1] +# define P099_VALUE_Z UserVar[event->BaseVarIndex + 2] -#define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) -#define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) -#define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) +# define P099_SET_VALUE_X(v) UserVar.setFloat(event->TaskIndex, 0, v) +# define P099_SET_VALUE_Y(v) UserVar.setFloat(event->TaskIndex, 1, v) +# define P099_SET_VALUE_Z(v) UserVar.setFloat(event->TaskIndex, 2, v) -#define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch -#define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 -#define P099_TS_SEND_XY true // Enable X/Y events -#define P099_TS_SEND_Z false // Disable Z events -#define P099_TS_SEND_OBJECTNAME true // Enable objectname events -#define P099_TS_USE_CALIBRATION false // Disable calibration -#define P099_TS_LOG_CALIBRATION true // Enable calibration logging -#define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. -#define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on -#define P099_TS_Y_RES 320 -#define P099_INIT_OBJECTCOUNT 8 // Initial setting -#define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function +# define P099_TS_TRESHOLD 15 // Treshold before the value is registered as a proper touch +# define P099_TS_ROTATION 2 // Rotation 0-3 = 0/90/180/270 degrees, compatible with TFT ILI9341 +# define P099_TS_SEND_XY true // Enable X/Y events +# define P099_TS_SEND_Z false // Disable Z events +# define P099_TS_SEND_OBJECTNAME true // Enable objectname events +# define P099_TS_USE_CALIBRATION false // Disable calibration +# define P099_TS_LOG_CALIBRATION true // Enable calibration logging +# define P099_TS_ROTATION_FLIPPED false // Enable rotation flipped 180 deg. +# define P099_TS_X_RES 240 // Pixels, should match with the screen it is mounted on +# define P099_TS_Y_RES 320 +# define P099_INIT_OBJECTCOUNT 8 // Initial setting +# define P099_DEBOUNCE_MILLIS 150 // Debounce delay for On/Off button function -#define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that - // turn up -#define P099_TOUCH_Y_INVALID 4095 -#define P099_TOUCH_Z_INVALID 255 +# define P099_TOUCH_X_INVALID 4095 // When picking up spurious noise (or an open/not connected TS-CS pin), these are the values that + // turn up +# define P099_TOUCH_Y_INVALID 4095 +# define P099_TOUCH_Z_INVALID 255 // Data structure @@ -84,7 +86,8 @@ struct P099_data_struct : public PluginTaskData_base bool send_z, bool useCalibration, uint16_t ts_x_res, - uint16_t ts_y_res); + uint16_t ts_y_res, + uint8_t spi_bus); bool isInitialized() const; void loadTouchObjects(taskIndex_t taskIndex); bool touched(); @@ -105,7 +108,8 @@ struct P099_data_struct : public PluginTaskData_base void scaleRawToCalibrated(uint16_t& x, uint16_t& y); - bool plugin_write(struct EventStruct *event,const String& string); + bool plugin_write(struct EventStruct *event, + const String & string); // This is initialized by calling init() XPT2046_Touchscreen *touchscreen = nullptr; @@ -118,6 +122,7 @@ struct P099_data_struct : public PluginTaskData_base bool _useCalibration = 0; uint16_t _ts_x_res = 0; uint16_t _ts_y_res = 0; + uint8_t _spi_bus = 0; // This is filled during checking of a touchobject uint32_t SurfaceAreas[P099_MaxObjectCount] = { 0 }; From b61dcad3532410c98ae9be2722b838cd9699b60e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 14:42:46 +0200 Subject: [PATCH 013/161] [P096] Enable support for configurable SPI bus --- lib/LOLIN_EPD/src/LOLIN_EPD.cpp | 18 ++++++++------- lib/LOLIN_EPD/src/LOLIN_EPD.h | 5 ++-- lib/LOLIN_EPD/src/LOLIN_IL3897.cpp | 4 ++-- lib/LOLIN_EPD/src/LOLIN_IL3897.h | 4 ++-- lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp | 4 ++-- lib/LOLIN_EPD/src/LOLIN_SSD1680.h | 4 ++-- lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp | 4 ++-- lib/LOLIN_EPD/src/LOLIN_UC8151D.h | 4 ++-- lib/LOLIN_EPD/src/Waveshare_2in7.cpp | 6 ++--- lib/LOLIN_EPD/src/Waveshare_2in7.h | 19 +++++++-------- src/_P096_eInk.ino | 26 ++++++++++++++------- src/src/PluginStructs/P096_data_struct.cpp | 27 ++++++++++++++++++---- src/src/PluginStructs/P096_data_struct.h | 6 ++++- 13 files changed, 83 insertions(+), 48 deletions(-) diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp index ce160c911..4bfcb0335 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.cpp @@ -16,7 +16,7 @@ #include "Adafruit_GFX.h" #include "LOLIN_EPD.h" -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { cs = CS; rst = RST; @@ -26,9 +26,10 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t spi_mosi, int8_t spi_clock, i busy = BUSY; hwSPI = false; singleByteTxns = false; + _spi = spi; } -LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY):Adafruit_GFX(width, height) +LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi):Adafruit_GFX(width, height) { dc = DC; @@ -37,6 +38,7 @@ LOLIN_EPD::LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, in busy = BUSY; hwSPI = true; singleByteTxns = false; + _spi = spi; } /**************************************************************************/ @@ -90,9 +92,9 @@ void LOLIN_EPD::begin(bool reset) } else { - SPI.begin(); + // _spi.begin(); // Already done by ESPEasy #ifndef SPI_HAS_TRANSACTION - SPI.setClockDivider(4); + _spi.setClockDivider(4); #endif } @@ -194,12 +196,12 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) { uint8_t b; csLow(); - b = SPI.transfer(d); + b = _spi.transfer(d); csHigh(); return b; } else - return SPI.transfer(d); + return _spi.transfer(d); } else { @@ -234,7 +236,7 @@ uint8_t LOLIN_EPD::fastSPIwrite(uint8_t d) void LOLIN_EPD::csHigh() { #ifdef SPI_HAS_TRANSACTION - SPI.endTransaction(); + _spi.endTransaction(); #endif #ifdef HAVE_PORTREG *csport |= cspinmask; @@ -251,7 +253,7 @@ void LOLIN_EPD::csHigh() void LOLIN_EPD::csLow() { #ifdef SPI_HAS_TRANSACTION - SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); + _spi.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); #endif #ifdef HAVE_PORTREG *csport &= ~cspinmask; diff --git a/lib/LOLIN_EPD/src/LOLIN_EPD.h b/lib/LOLIN_EPD/src/LOLIN_EPD.h index dacd00071..677d82c94 100644 --- a/lib/LOLIN_EPD/src/LOLIN_EPD.h +++ b/lib/LOLIN_EPD/src/LOLIN_EPD.h @@ -60,8 +60,8 @@ enum class LOLIN_EPD: public Adafruit_GFX { public: - LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_EPD(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_EPD(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); virtual ~LOLIN_EPD(); virtual void begin(bool reset = true); @@ -111,6 +111,7 @@ protected: void dcLow(); private: + SPIClass& _spi = SPI; }; #include "LOLIN_IL3897.h" diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp index 8cb34af91..764e6e3b0 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.cpp @@ -184,7 +184,7 @@ const unsigned char LUT_DATA_part[] PROGMEM = { @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -202,7 +202,7 @@ LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_ red_bufsize = bw_bufsize; } -LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_IL3897::LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_IL3897.h b/lib/LOLIN_EPD/src/LOLIN_IL3897.h index a461f51e8..77847c7fb 100644 --- a/lib/LOLIN_EPD/src/LOLIN_IL3897.h +++ b/lib/LOLIN_EPD/src/LOLIN_IL3897.h @@ -20,8 +20,8 @@ class LOLIN_IL3897 : public LOLIN_EPD { public: - LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_IL3897(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_IL3897(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset=true); diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp index f51bdf3ee..b9037a1df 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_SSD1680::LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h index 9a7ebebcb..47eeafcd1 100644 --- a/lib/LOLIN_EPD/src/LOLIN_SSD1680.h +++ b/lib/LOLIN_EPD/src/LOLIN_SSD1680.h @@ -19,8 +19,8 @@ class LOLIN_SSD1680 : public LOLIN_EPD { public: - LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_SSD1680(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_SSD1680(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp index 737a22e43..cd7d48ea3 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.cpp @@ -21,7 +21,7 @@ @param BUSY the busy pin to use */ /**************************************************************************/ -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, SID, SCLK, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) @@ -39,7 +39,7 @@ LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int red_bufsize = bw_bufsize; } -LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) : LOLIN_EPD(width, height, DC, RST, CS, BUSY) +LOLIN_UC8151D::LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) diff --git a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h index f07603aa1..a3d188d87 100644 --- a/lib/LOLIN_EPD/src/LOLIN_UC8151D.h +++ b/lib/LOLIN_EPD/src/LOLIN_UC8151D.h @@ -19,8 +19,8 @@ class LOLIN_UC8151D : public LOLIN_EPD { public: - LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); - LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1); + LOLIN_UC8151D(int width, int height, int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); + LOLIN_UC8151D(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY = -1, SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp index 12b73cee3..2c1656061 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.cpp +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.cpp @@ -76,8 +76,8 @@ static const unsigned char EPD_2in7_gray_lut_bb[] = { @param BUSY the busy pin to use */ -Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY) - : LOLIN_EPD(width, height, DC, RST, CS, BUSY) { +Waveshare_2in7::Waveshare_2in7(int width, int height, int8_t DC, int8_t RST, int8_t CS, int8_t BUSY, SPIClass& spi) + : LOLIN_EPD(width, height, DC, RST, CS, BUSY, spi) { if ((height % 8) > 0) { _height_8bit = (height / 8 + 1) * 8; } else { @@ -247,7 +247,7 @@ void Waveshare_2in7::drawPixel(int16_t x, int16_t y, uint16_t color) { if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) { return; } - + // Corrections by @kretzp (Peter Kretz) 2022-03-27 // check rotation, move pixel around if necessary switch (getRotation()) { diff --git a/lib/LOLIN_EPD/src/Waveshare_2in7.h b/lib/LOLIN_EPD/src/Waveshare_2in7.h index d7f216dda..7d3eda4b3 100644 --- a/lib/LOLIN_EPD/src/Waveshare_2in7.h +++ b/lib/LOLIN_EPD/src/Waveshare_2in7.h @@ -24,10 +24,10 @@ #define WS2IN7_DATA_STOP 0x11 #define WS2IN7_DISPLAY_REFRESH 0x12 #define WS2IN7_DATA_START_TRANSMISSION_2 0x13 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 -#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_1 0x14 +#define WS2IN7_PARTIAL_DATA_START_TRANSMISSION_2 0x15 #define WS2IN7_PARTIAL_DISPLAY_REFRESH 0x16 -#define WS2IN7_LUT_FOR_VCOM 0x20 +#define WS2IN7_LUT_FOR_VCOM 0x20 #define WS2IN7_LUT_WHITE_TO_WHITE 0x21 #define WS2IN7_LUT_BLACK_TO_WHITE 0x22 #define WS2IN7_LUT_WHITE_TO_BLACK 0x23 @@ -67,12 +67,13 @@ extern const unsigned char lut_wb[]; class Waveshare_2in7 : public LOLIN_EPD { public: - Waveshare_2in7(int width, - int height, - int8_t DC, - int8_t RST, - int8_t CS, - int8_t BUSY = -1); + Waveshare_2in7(int width, + int height, + int8_t DC, + int8_t RST, + int8_t CS, + int8_t BUSY = -1, + SPIClass& spi = SPI); void begin(bool reset = true); diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index c698c2793..31deacad6 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -13,6 +13,11 @@ // #define PLUGIN_096_MAX_DISPLAY 1 // Unused +/** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus + * 2025-08-13 tonhuisman: Start changelog + */ + /* README.MD @@ -184,6 +189,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) dev.TimerOption = true; dev.TimerOptional = true; # endif // if P096_USE_EXTENDED_SETTINGS + dev.SpiBusSelect = true; break; } @@ -278,8 +284,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) options4, optionValues4); selector.addFormSelector(F("eInk display model"), - F("_type"), - P096_CONFIG_FLAG_GET_DISPLAYTYPE); + F("_type"), + P096_CONFIG_FLAG_GET_DISPLAYTYPE); } addFormSubHeader(F("Layout")); @@ -292,7 +298,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options2[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; int optionValues2[] = { 0, 1, 2, 3 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - const FormSelectorOptions selector( optionCount, options2, optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); selector.addFormSelector(F("Rotation"), F("_rotate"), P096_CONFIG_ROTATION); } # endif // ifdef P096_USE_ADA_GRAPHICS @@ -341,8 +347,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) } constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions); const FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); - selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"), - P096_CONFIG_FLAG_GET_COLORDEPTH); + selector.addFormSelector(F("Greyscale levels"), F("_colorDepth"), + P096_CONFIG_FLAG_GET_COLORDEPTH); } AdaGFXFormTextPrintMode(F("_mode"), P096_CONFIG_FLAG_GET_MODE); @@ -379,7 +385,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"), - P096_CONFIG_FLAG_GET_CMD_TRIGGER); + P096_CONFIG_FLAG_GET_CMD_TRIGGER); addFormNote(F("Select the command that is used to handle commands for this display.")); } @@ -502,7 +508,9 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Settings.InitSPI != 0) { + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + + if (Settings.isSPI_valid(spi_bus)) { initPluginTaskData(event->TaskIndex, # if P096_USE_EXTENDED_SETTINGS new (std::nothrow) P096_data_struct(static_cast(P096_CONFIG_FLAG_GET_DISPLAYTYPE), @@ -511,6 +519,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) static_cast(P096_CONFIG_FLAG_GET_MODE), P096_CommandTrigger_toString(static_cast( P096_CONFIG_FLAG_GET_CMD_TRIGGER)), + spi_bus, P096_CONFIG_GET_COLOR_FOREGROUND, P096_CONFIG_GET_COLOR_BACKGROUND, static_cast(P096_CONFIG_FLAG_GET_COLORDEPTH), @@ -522,7 +531,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) P096_CONFIG_ROTATION, P096_CONFIG_FLAG_GET_FONTSCALE, AdaGFXTextPrintMode::ContinueToNextLine, - F("epd")) + F("epd"), + spi_bus) # endif // if P096_USE_EXTENDED_SETTINGS ); P096_data_struct *P096_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/src/PluginStructs/P096_data_struct.cpp b/src/src/PluginStructs/P096_data_struct.cpp index 8d4b348cf..783ee4655 100644 --- a/src/src/PluginStructs/P096_data_struct.cpp +++ b/src/src/PluginStructs/P096_data_struct.cpp @@ -75,6 +75,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor, uint16_t bgcolor, AdaGFXColorDepth colorDepth, @@ -84,7 +85,7 @@ P096_data_struct::P096_data_struct(EPD_type_e display, _xpix(width), _ypix(height), # endif // if !P096_USE_EXTENDED_SETTINGS _rotation(rotation), _fontscaling(fontscaling), _textmode(textmode), _commandTrigger(commandTrigger), - _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) + _spi_bus(spi_bus), _fgcolor(fgcolor), _bgcolor(bgcolor), _colorDepth(colorDepth), _textBackFill(textBackFill) { _commandTrigger.toLowerCase(); _commandTriggerCmd = _commandTrigger; @@ -124,17 +125,33 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { switch (_display) { case EPD_type_e::EPD_IL3897: - eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_UC8151D: - eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); // HSPI break; case EPD_type_e::EPD_SSD1680: - eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); // HSPI break; # if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_WS2IN7: - eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3)); // HSPI + eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) + # ifdef ESP32 + , 0 == _spi_bus ? SPI : SPI1 + # endif // ifdef ESP32 + ); // HSPI break; # endif // if P096_USE_WAVESHARE_2IN7 case EPD_type_e::EPD_MAX: diff --git a/src/src/PluginStructs/P096_data_struct.h b/src/src/PluginStructs/P096_data_struct.h index 9f6a79628..4cc60158e 100644 --- a/src/src/PluginStructs/P096_data_struct.h +++ b/src/src/PluginStructs/P096_data_struct.h @@ -4,6 +4,8 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P096 +# include "../Globals/SPI1.h" + # include // include Adafruit graphics library # include // include Adafruit Lolin eInk/ePaper library @@ -110,11 +112,12 @@ public: uint8_t fontscaling, AdaGFXTextPrintMode textmode, String commandTrigger, + uint8_t spi_bus, uint16_t fgcolor = ADAGFX_WHITE, uint16_t bgcolor = ADAGFX_BLACK, AdaGFXColorDepth colorDepth = AdaGFXColorDepth::Monochrome, bool textBackFill = true); - P096_data_struct() = delete; + P096_data_struct() = delete; virtual ~P096_data_struct(); bool plugin_init(struct EventStruct *event); @@ -146,6 +149,7 @@ private: uint8_t _fontscaling; AdaGFXTextPrintMode _textmode; String _commandTrigger; + uint8_t _spi_bus; uint16_t _fgcolor; uint16_t _bgcolor; AdaGFXColorDepth _colorDepth; From 2e4e79fac49ea38904e9a967bbdb69797debd473 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 16:46:03 +0200 Subject: [PATCH 014/161] [P039] Move (nearly) all code to PluginStrucs files --- src/_P039_Thermosensors.ino | 1744 +------------------- src/src/PluginStructs/P039_data_struct.cpp | 1553 ++++++++++++++++- src/src/PluginStructs/P039_data_struct.h | 241 ++- 3 files changed, 1782 insertions(+), 1756 deletions(-) diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index 641840db6..b1fab3d45 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -19,6 +19,7 @@ // Have fun ... Dominik /** Changelog: + * 2025-08-13 tonhuisman: Move most code to P039_PluginStruct * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2025-01-03 tonhuisman: Small code size reductions, cleanup of DEBUG level logging * 2024-01-04 tonhuisman: Minor corrections, formatted source using Uncrustify @@ -77,180 +78,11 @@ // #endif -# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) -# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) - # define PLUGIN_039 # define PLUGIN_ID_039 39 # define PLUGIN_NAME_039 "Environment - Thermosensors" # define PLUGIN_VALUENAME1_039 "Temperature" -# define P039_SET true -# define P039_RESET false - -// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we -// spend 1ms -// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device -# define P039_CS_Delay() delayMicroseconds(2u) - -# define P039_MAX_TYPE PCONFIG(0) -# define P039_TC_TYPE PCONFIG(1) -# define P039_FAM_TYPE PCONFIG(2) -# define P039_RTD_TYPE PCONFIG(3) -# define P039_CONFIG_4 PCONFIG(4) -# define P039_RTD_FILT_TYPE PCONFIG(5) -# define P039_RTD_LM_TYPE PCONFIG(6) -# define P039_RTD_LM_SHTDWN PCONFIG(7) -# define P039_RTD_RES PCONFIG_LONG(0) -# define P039_FLAGS PCONFIG_ULONG(3) -# define P039_TEMP_THRESHOLD_FLAG 0 -# define P039_RTD_OFFSET PCONFIG_FLOAT(0) -# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) - -# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value -# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT -# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value -# define P039_TC 0u -# define P039_RTD 1u - -# define P039_MAX6675 1 -# define P039_MAX31855 2 -# define P039_MAX31856 3 -# define P039_MAX31865 4 -# define P039_LM7x 5 - -// MAX 6675 related defines - -// bit masks to identify failures for MAX 6675 -# define MAX6675_TC_DEVID 0x0002u -# define MAX6675_TC_OC 0x0004u - -// MAX 31855 related defines - -// bit masks to identify failures for MAX 31855 -# define MAX31855_TC_OC 0x00000001u -# define MAX31855_TC_SC 0x00000002u -# define MAX31855_TC_SCVCC 0x00000004u -# define MAX31855_TC_GENFLT 0x00010000u - - -// MAX 31856 related defines - -// base address for read/write acces to MAX 31856 -# define MAX31856_READ_ADDR_BASE 0x00u -# define MAX31856_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31856 -# define MAX31856_CR0 0u -# define MAX31856_CR1 1u -# define MAX31856_MASK 2u -# define MAX31856_CJHF 3u -# define MAX31856_CJLF 4u -# define MAX31856_LTHFTH 5u -# define MAX31856_LTHFTL 6u -# define MAX31856_LTLFTH 7u -# define MAX31856_LTLFTL 8u -# define MAX31856_CJTO 9u -# define MAX31856_CJTH 10u -# define MAX31856_CJTL 11u -# define MAX31856_LTCBH 12u -# define MAX31856_LTCBM 13u -# define MAX31856_LTCBL 14u -# define MAX31856_SR 15u - -# define MAX31856_NO_REG 16u - -// bit masks to identify failures for MAX 31856 -# define MAX31856_TC_OC 0x01u -# define MAX31856_TC_OVUV 0x02u -# define MAX31856_TC_TCLOW 0x04u -# define MAX31856_TC_TCLHIGH 0x08u -# define MAX31856_TC_CJLOW 0x10u -# define MAX31856_TC_CJHIGH 0x20u -# define MAX31856_TC_TCRANGE 0x40u -# define MAX31856_TC_CJRANGE 0x80u - -// bit masks for access of configuration bits -# define MAX31856_SET_50HZ 0x01u -# define MAX31856_CLEAR_FAULTS 0x02u -# define MAX31856_FLT_ISR_MODE 0x04u -# define MAX31856_CJ_SENS_DISABLE 0x08u -# define MAX31856_FAULT_CTRL_MASK 0x30u -# define MAX31856_SET_ONE_SHOT 0x40u -# define MAX31856_SET_CONV_AUTO 0x80u - - -// RTD related defines - -// MAX 31865 related defines - -// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) -// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done -// alternatively ONE SHOT bit could be polled (system/SPI bus load !) -# define MAX31865_CONVERSION_TIME 70ul -# define MAX31865_BIAS_WAIT_TIME 10ul - -// MAX 31865 Main States -# define MAX31865_INIT_STATE 0u -# define MAX31865_BIAS_ON_STATE 1u -# define MAX31865_RD_STATE 2u -# define MAX31865_RDY_STATE 3u - -// sensor type -# define MAX31865_PT100 0u -# define MAX31865_PT1000 1u - -// base address for read/write acces to MAX 31865 -# define MAX31865_READ_ADDR_BASE 0x00u -# define MAX31865_WRITE_ADDR_BASE 0x80u - -// register offset values for MAX 31865 -# define MAX31865_CONFIG 0u -# define MAX31865_RTD_MSB 1u -# define MAX31865_RTD_LSB 2u -# define MAX31865_HFT_MSB 3u -# define MAX31865_HFT_LSB 4u -# define MAX31865_LFT_MSB 5u -# define MAX31865_LFT_LSB 6u -# define MAX31865_FAULT 7u - -// total number of registers in MAX 31865 -# define MAX31865_NO_REG 8u - -// bit masks to identify failures for MAX 31865 -# define MAX31865_FAULT_HIGHTHRESH 0x80u -# define MAX31865_FAULT_LOWTHRESH 0x40u -# define MAX31865_FAULT_REFINLOW 0x20u -# define MAX31865_FAULT_REFINHIGH 0x10u -# define MAX31865_FAULT_RTDINLOW 0x08u -# define MAX31865_FAULT_OVUV 0x04u - -// bit masks for access of configuration bits -# define MAX31865_SET_50HZ 0x01u -# define MAX31865_CLEAR_FAULTS 0x02u -# define MAX31865_FAULT_CTRL_MASK 0x0Cu -# define MAX31865_SET_3WIRE 0x10u -# define MAX31865_SET_ONE_SHOT 0x20u -# define MAX31865_SET_CONV_AUTO 0x40u -# define MAX31865_SET_VBIAS_ON 0x80u - -// LM7x related defines - -// LM7x subtype defines -# define LM7x_SD70 0x00u -# define LM7x_SD71 0x01u -# define LM7x_SD74 0x04u -# define LM7x_SD121 0x05u -# define LM7x_SD122 0x06u -# define LM7x_SD123 0x07u -# define LM7x_SD124 0x08u -# define LM7x_SD125 0x09u - -// bit masks for access of configuration bits -# define LM7x_CONV_RDY 0x02u - - -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event); boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { @@ -287,7 +119,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) # if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { - success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5);; + success = getDiscoveryVType(event, Plugin_QueryVType_Temperature, 255, event->Par5); break; } # endif // if FEATURE_MQTT_DISCOVER @@ -316,121 +148,12 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) } - initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct()); + initPluginTaskData(event->TaskIndex, new (std::nothrow) P039_data_struct(event)); P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // set the slaveSelectPin as an output: - init_SPI_CS_Pin(CS_pin_no); - - // initialize SPI: - SPI.setHwCs(false); - SPI.begin(); - - // ensure MODE3 access to SPI device - SPI.setDataMode(SPI_MODE3); - - /* - if (P039_MAX_TYPE == P039_MAX6675) { - - // SPI.setBitOrder(MSBFIRST); - } - */ - if (P039_MAX_TYPE == P039_MAX31855) { - // SPI.setBitOrder(MSBFIRST); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } + if (nullptr != P039_data) { + success = P039_data->begin(event); } - - - if (P039_MAX_TYPE == P039_MAX31856) { - // init string - content accoring to inital implementation of P039 - MAX31856 read function - // write to Adress 0x80 - // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to - // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO - uint8_t sendBuffer[11] = - { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, - 0xFF, 0x80, 0x00, 0x00 }; - - transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - P039_data->sensorFault = false; - } - - // start on shot conversion for upcoming read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - } - - - if (P039_MAX_TYPE == P039_MAX31865) { - // two step initialization buffer - uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; - - // write intially 0x00 to CONFIG register - write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); - - // activate 50Hz filter, clear all faults, no auto conversion, no conversion started - change8BitRegister(CS_pin_no, - MAX31865_RD_ADDRESS(MAX31865_CONFIG), - MAX31865_WR_ADDRESS(MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // configure 2/4-wire sensor connection as default - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // set HighFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); - - // set LowFault Threshold - transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); - - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - if (nullptr != P039_data) { - // save current timer for next calculation - P039_data->timer = millis(); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - } - } - - /* - if (P039_MAX_TYPE == P039_LM7x) - { - // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 - } - */ - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); - } - # endif // ifndef BUILD_NO_DEBUG - - success = true; break; } @@ -443,7 +166,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *Foptions[] = { F("Thermocouple"), F("RTD") }; const int FoptionValues[] = { P039_TC, P039_RTD }; constexpr size_t optionCount = NR_ELEMENTS(FoptionValues); - FormSelectorOptions selector( optionCount, Foptions, FoptionValues); + FormSelectorOptions selector(optionCount, Foptions, FoptionValues); selector.reloadonchange = true; selector.addFormSelector(F("Sensor Family Type"), F("famtype"), family); } @@ -457,7 +180,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options[] = { F("MAX 6675"), F("MAX 31855"), F("MAX 31856") }; const int optionValues[] = { P039_MAX6675, P039_MAX31855, P039_MAX31856 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector( optionCount, options, optionValues); + FormSelectorOptions selector(optionCount, options, optionValues); selector.reloadonchange = true; selector.addFormSelector(F("Adapter IC"), F("maxtype"), choice); } @@ -485,7 +208,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int ToptionValues[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 12 }; constexpr size_t optionCount = NR_ELEMENTS(ToptionValues); - const FormSelectorOptions selector( optionCount, Toptions, ToptionValues); + const FormSelectorOptions selector(optionCount, Toptions, ToptionValues); selector.addFormSelector(F("Thermocouple type"), F("tctype"), P039_TC_TYPE); } { @@ -493,10 +216,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int CoptionValues[] = { 0, 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(CoptionValues); const FormSelectorOptions selector(optionCount, Coptions, CoptionValues); - selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); + selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); addUnit(F("sample(s)")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); } } else { @@ -521,17 +244,17 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const int PToptionValues[] = { MAX31865_PT100, MAX31865_PT1000 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); const FormSelectorOptions selector(optionCount, PToptions, PToptionValues); - selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); + selector.addFormSelector(F("Resistor Type"), F("rtdtype"), P039_RTD_TYPE); } { const __FlashStringHelper *Coptions[] = { F("2-/4"), F("3") }; constexpr size_t optionCount = NR_ELEMENTS(Coptions); - const FormSelectorOptions selector( optionCount, Coptions); + const FormSelectorOptions selector(optionCount, Coptions); selector.addFormSelector(F("Connection Type"), F("contype"), P039_CONFIG_4); addUnit(F("wire")); } - P039_AddMainsFrequencyFilterSelection(event); + P039_data_struct::AddMainsFrequencyFilterSelection(event); { addFormNumericBox(F("Reference Resistor"), F("res"), P039_RTD_RES, 0); @@ -610,64 +333,10 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_READ: { - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; + P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - float Plugin_039_Celsius = NAN; - - switch (MaxType) { - case P039_MAX6675: - Plugin_039_Celsius = readMax6675(event); - break; - case P039_MAX31855: - Plugin_039_Celsius = readMax31855(event); - break; - case P039_MAX31856: - Plugin_039_Celsius = readMax31856(event); - break; - case P039_MAX31865: - Plugin_039_Celsius = readMax31865(event); - break; - case P039_LM7x: - Plugin_039_Celsius = readLM7x(event); - break; - } - - if (isValidFloat(Plugin_039_Celsius)) - { - UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); - -# ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); - - const uint8_t valueCount = getValueCountForTask(event->TaskIndex); - - for (uint8_t i = 0; i < valueCount; ++i) - { - log += strformat( - F(" %s: %s"), - Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), - formatUserVarNoCheck(event, i).c_str()); - } - addLogMove(LOG_LEVEL_INFO, log); - } -# endif // ifndef BUILD_NO_DEBUG - - if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { - success = true; - } - } - else - { - UserVar.setFloat(event->TaskIndex, 0, NAN); - UserVar.setFloat(event->TaskIndex, 1, NAN); - - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); - } - success = false; + if (nullptr != P039_data) { + success = P039_data->read(event); } break; @@ -677,1391 +346,14 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // Get the MAX Type (6675 / 31855 / 31856) - uint8_t MaxType = P039_MAX_TYPE; - - switch (MaxType) - { - case P039_MAX31865: - { - if ((nullptr != P039_data)) { - switch (event->Par1) - { - case MAX31865_BIAS_ON_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // activate one shot conversion - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_ONE_SHOT, - P039_SET); - - // set next state in sequence -> READ STATE - // start time to follow up on conversion and read the conversion result - P039_data->convReady = false; - Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - break; - } - case MAX31865_RD_STATE: - { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), - getTaskDeviceName(event->TaskIndex).c_str(), - timePassedSince(P039_data->timer))); // calc delta since last call - } - # endif // ifndef BUILD_NO_DEBUG - - // save current timer for next calculation - P039_data->timer = millis(); - - // read conversion result - P039_data->conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); - - // deactivate BIAS short after read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_RESET); - - // read fault register to get a full picture - P039_data->deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); - - // mark conversion as ready - P039_data->convReady = true; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, - strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), - getTaskDeviceName(event->TaskIndex).c_str(), - formatToHex_decimal(P039_data->conversionResult).c_str(), - formatToHex_decimal(P039_data->deviceFaults).c_str(), - event->Par1)); - } - # endif // ifndef BUILD_NO_DEBUG - - - break; - } - case MAX31865_INIT_STATE: - default: - { - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " - "default; next state: MAX31865_BIAS_ON_STATE"), - getTaskDeviceName(event->TaskIndex).c_str())); - - // FIXME: Shouldn't this be in active code? // save current timer for next calculation - P039_data->timer = millis(); - } - # endif // ifndef BUILD_NO_DEBUG - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // set next state in sequence -> BIAS ON STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - - break; - } - } - } - break; - } - default: - { - break; - } + if (nullptr != P039_data) { + success = P039_data->plugin_tasktimer_in(event); } - success = true; break; } } return success; } -void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event) -{ - const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; - const int FToptionValues[] = { 0, 1 }; - - const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); - selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); - addUnit(F("Hz")); - # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Filter power net frequency (50/60 Hz)")); - # else // ifndef LIMIT_BUILD_SIZE - addUnit(F("net frequency")); - # endif // ifndef LIMIT_BUILD_SIZE -} - -float readMax6675(struct EventStruct *event) -{ - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - uint8_t messageBuffer[2] = { 0 }; - uint16_t rawvalue = 0u; - - - // "transfer" 2 bytes to SPI to get 16 Bit return value - transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); - - // merge 16Bit return value from messageBuffer - rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue, - formatToHex_decimal(messageBuffer[0]).c_str(), - formatToHex_decimal(messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - // Open Thermocouple - // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the - // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin - // as possible. - // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance - const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); - - if (Plugin_039_SensorAttached) - { - // shift RAW value 3 Bits to the right to get the data - rawvalue >>= 3; - - // calculate Celsius with device resolution 0.25 K/bit - return rawvalue * 0.25f; - } - else - { - return NAN; - } -} - -float readMax31855(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - uint8_t messageBuffer[4] = { 0 }; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // "transfer" 0x0 and read the 32 Bit conversion register from the Chip - transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); - - // merge rawvalue from 4 bytes of messageBuffer - uint32_t rawvalue = - ((static_cast(messageBuffer[0]) << - 24) | - (static_cast(messageBuffer[1]) << - 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(200u))) { // reserve value derived from example log file - log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), - String(rawvalue, BIN).c_str(), - formatToHex(rawvalue).c_str(), - rawvalue); - - for (size_t i = 0u; i < 4; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(messageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - if (nullptr != P039_data) { - // FIXED: c.k.i. : moved static fault flag to instance data structure - - // check for fault flags in LSB of 32 Bit messageBuffer - if (P039_data->sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { - // Fault code changed, log them - P039_data->sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : MAX31855 : "); - - if ((P039_data->sensorFault)) { - log += F("Fault resolved"); - } else { - log += F("Fault code :"); - - if (rawvalue & MAX31855_TC_OC) { - log += F(" Open (no connection)"); - } - - if (rawvalue & MAX31855_TC_SC) { - log += F(" Short-circuit to GND"); - } - - if (rawvalue & MAX31855_TC_SCVCC) { - log += F(" Short-circuit to Vcc"); - } - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s P039_data->sensorFault: "), - formatToHex_decimal(rawvalue).c_str(), - formatToHex_decimal(P039_data->sensorFault).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - } - - // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. - // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons - const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); - - if (Plugin_039_SensorAttached) - { - // Data is D[31:18] - // Shift RAW value 18 Bits to the right to get the data - rawvalue >>= 18; - - // Check for negative Values - // +25.00 0000 0001 1001 00 - // 0.00 0000 0000 0000 00 - // -0.25 1111 1111 1111 11 - // -1.00 1111 1111 1111 00 - // -250.00 1111 0000 0110 00 - // We're left with (32 - 18 =) 14 bits - int temperature = Plugin_039_convert_two_complement(rawvalue, 14); - - // Calculate Celsius - return temperature * 0.25f; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31856(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - - uint8_t registers[MAX31856_NO_REG] = { 0 }; - uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; - - messageBuffer[0] = MAX31856_READ_ADDR_BASE; - - // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip - transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); - - // transfer data from messageBuffer and get rid of initial address uint8_t - for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { - registers[i] = messageBuffer[i + 1]; - } - - // configure device for next conversion - // activate frequency filter according to configuration - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - // set averaging and TC type - write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); - - - // start on shot conversion for next read cycle - change8BitRegister(CS_pin_no, - (MAX31856_READ_ADDR_BASE + MAX31856_CR0), - (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), - MAX31856_SET_ONE_SHOT, - P039_SET); - - - // now derive raw value from respective registers - uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); - - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); - rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 :"); - - for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - log += F(" rawvalue: "); - log += formatToHex_decimal(rawvalue); - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // ignore TC Range Bit in case Voltage Modes are used - // datasheet: - // Thermocouple Out-of-Range fault. - // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). - // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. - // Note: The TC Range bit should be ignored in voltage mode. - uint8_t sr = registers[MAX31856_SR]; - - if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { - sr &= ~MAX31856_TC_TCRANGE; - } - - - // FIXED: c.k.i. : moved static fault flag to instance data structure - if ((nullptr != P039_data)) { - // P039_data->sensorFault = false; - - P039_data->sensorFault = (sr != 0); // Set new state - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - // FIXME TD-er: Part of expression is always false (sr == 0) - const bool faultResolved = (P039_data->sensorFault) && (sr == 0); - - if ((P039_data->sensorFault) || faultResolved) { - String log; - - if ((log.reserve(140u))) { // reserve value derived from example log file - log = F("P039 : MAX31856 : "); - - if ((P039_data->sensorFault) == 0) { - log += F("Fault resolved"); - } else { - log += F("Fault :"); - - if (sr & MAX31856_TC_OC) { - log += F(" Open (no connection)"); - } - - if (sr & MAX31856_TC_OVUV) { - log += F(" Over/Under Voltage"); - } - - if (sr & MAX31856_TC_TCLOW) { - log += F(" TC Low"); - } - - if (sr & MAX31856_TC_TCLHIGH) { - log += F(" TC High"); - } - - if (sr & MAX31856_TC_CJLOW) { - log += F(" CJ Low"); - } - - if (sr & MAX31856_TC_CJHIGH) { - log += F(" CJ High"); - } - - if (sr & MAX31856_TC_TCRANGE) { - log += F(" TC Range"); - } - - if (sr & MAX31856_TC_CJRANGE) { - log += F(" CJ Range"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - } - # endif // ifndef BUILD_NO_DEBUG - } - - - const bool Plugin_039_SensorAttached = (sr == 0); - - if (Plugin_039_SensorAttached) - { - rawvalue >>= 5; // bottom 5 bits are unused - // We're left with (24 - 5 =) 19 bits - - { - float temperature = 0; - - switch (P039_TC_TYPE) - { - case 8: - { - temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) - break; - } - case 12: - { - temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) - break; - } - default: - { - temperature = Plugin_039_convert_two_complement(rawvalue, 19); - - // Calculate Celsius - temperature /= 128.0f; - break; - } - } - - return temperature; - } - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -float readMax31865(struct EventStruct *event) -{ - P039_data_struct *P039_data = static_cast(getPluginTaskData(event->TaskIndex)); - - if (P039_data == nullptr) { - return NAN; - } - - uint8_t registers[MAX31865_NO_REG] = { 0 }; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - String log; - - if ((log.reserve(80u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 :"); - log += F(" P039_data->convReady: "); - log += boolToString(P039_data->convReady); - - addLogMove(LOG_LEVEL_DEBUG, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - - // read conversion result and faults from plugin data structure - // if pointer exists and conversion has been finished - if (P039_data->convReady) { - rawValue = P039_data->conversionResult; - registers[MAX31865_FAULT] = P039_data->deviceFaults; - } - - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(160u))) { // reserve value derived from example log file - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); - } - - log = F("P039 : MAX31865 :"); - - for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) - { - log += ' '; - log += formatToHex_decimal(registers[i]); - } - - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG - - // Prepare and start next conversion, before handling faults and rawValue - // clear all faults - MAX31865_clearFaults(CS_pin_no); - - // set frequency filter - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_50HZ, - static_cast(P039_RTD_FILT_TYPE)); - - - // configure read access with configuration from web interface - MAX31865_setConType(CS_pin_no, P039_CONFIG_4); - - // activate BIAS short before read, to reduce power consumption - change8BitRegister(CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_VBIAS_ON, - P039_SET); - - // start time to follow up on BIAS activation before starting the conversion - // and start conversion sequence via TIMER API - // save current timer for next calculation - P039_data->timer = millis(); - - // set next state to MAX31865_BIAS_ON_STATE - - Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - if (registers[MAX31865_FAULT]) - { - String log; - - if ((log.reserve(210u))) { // reserve value derived from example log file - log = F("P039 : MAX31865 : "); - - log += F("Fault : "); - log += formatToHex_decimal(registers[MAX31865_FAULT]); - log += F(" :"); - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) - { - log += F(" Under/Over voltage"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) - { - log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) - { - log += F(" REFIN- < 0.85 x Bias - FORCE- open"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) - { - log += F(" REFIN- > 0.85 x Bias"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) - { - log += F(" RTD Low Threshold"); - } - - if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) - { - log += F(" RTD High Threshold"); - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - } - # endif // ifndef BUILD_NO_DEBUG - - - bool ValueValid = false; - - if (registers[MAX31865_FAULT] == 0x00u) { - ValueValid = true; - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), - formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), - FsP(boolToString(ValueValid)))); - } - - # endif // ifndef BUILD_NO_DEBUG - - if (ValueValid) - { - rawValue >>= 1; // bottom fault bits is unused - - float temperature = Plugin_039_convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), - formatToHex_decimal(rawValue).c_str(), - temperature, - P039_RTD_TYPE, - P039_RTD_RES)); - } - - # endif // ifndef BUILD_NO_DEBUG - - // add offset handling from configuration webpage - temperature += P039_RTD_OFFSET; - - // Calculate Celsius - return temperature; - } - else - { - // Fault state, thus output no value. - return NAN; - } -} - -void MAX31865_clearFaults(int8_t l_CS_pin_no) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); - - - // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) - l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); - l_reg |= MAX31865_CLEAR_FAULTS; - - // write configuration - write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); -} - -void MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) -{ - bool l_set_reset = false; - - // configure if 3 WIRE bit will be set/reset - switch (l_conType) - { - case 0: - l_set_reset = P039_RESET; - break; - case 1: - l_set_reset = P039_SET; - break; - default: - l_set_reset = P039_RESET; - break; - } - - // change to configuration register - change8BitRegister(l_CS_pin_no, - (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), - (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), - MAX31865_SET_3WIRE, - l_set_reset); -} - -/**************************************************************************/ - -/*! - @brief Read the temperature in C from the RTD through calculation of the - resistance. Uses - http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf - technique - @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 - or 1000 - @param refResistor The value of the matching reference resistor, usually - 430 or 4300 - @returns Temperature in C - */ - -/**************************************************************************/ -float Plugin_039_convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) -{ - # define RTD_A 3.9083e-3f - # define RTD_B -5.775e-7f - - float Z1, Z2, Z3, Z4, Rt, temp; - - Rt = l_rawvalue; - Rt /= 32768u; - Rt *= refResistor; - - Z1 = -RTD_A; - Z2 = RTD_A * RTD_A - (4 * RTD_B); - Z3 = (4 * RTD_B) / RTDnominal; - Z4 = 2 * RTD_B; - - temp = Z2 + (Z3 * Rt); - temp = (sqrtf(temp) + Z1) / Z4; - - if (temp >= 0) { - return temp; - } - - Rt /= RTDnominal; - Rt *= 100; // normalize to 100 ohm - - float rpoly = Rt; - - temp = -242.02f; - temp += 2.2228f * rpoly; - rpoly *= Rt; // square - temp += 2.5859e-3f * rpoly; - rpoly *= Rt; // ^3 - temp -= 4.8260e-6f * rpoly; - rpoly *= Rt; // ^4 - temp -= 2.8183e-8f * rpoly; - rpoly *= Rt; // ^5 - temp += 1.5243e-10f * rpoly; - - return temp; -} - -uint16_t getNomResistor(uint8_t l_RType) -{ - uint16_t l_returnValue = 100u; - - switch (l_RType) - { - case MAX31865_PT100: - l_returnValue = 100u; - break; - case MAX31865_PT1000: - l_returnValue = 1000u; - break; - default: - l_returnValue = 100u; - break; - } - return l_returnValue; -} - -int Plugin_039_convert_two_complement(uint32_t value, int nr_bits) { - const bool negative = (value & (1 << (nr_bits - 1))) != 0; - int nativeInt; - - if (negative) { - // Add zeroes to the left to create the proper negative native-sized integer. - nativeInt = value | ~((1 << nr_bits) - 1); - } else { - nativeInt = value; - } - return nativeInt; -} - -float readLM7x(struct EventStruct *event) -{ - float temperature = 0.0f; - uint16_t device_id = 0u; - uint16_t rawValue = 0u; - - int8_t CS_pin_no = get_SPI_CS_Pin(event); - - // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) - // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register - - rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); - - temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) - { - addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), - formatToHex_decimal(rawValue).c_str(), - formatToHex(device_id).c_str(), - temperature)); - } - # endif // ifndef BUILD_NO_DEBUG - - return temperature; -} - -float convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) -{ - float l_returnValue = 0.0f; - float l_lsbvalue = 0.0f; - uint8_t l_noBits = 0u; - int l_intTemperature = 0; - - switch (l_LM7xsubtype) - { - case LM7x_SD70: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - case LM7x_SD71: - l_rawValue >>= 2; - l_lsbvalue = 0.03125f; - l_noBits = 14u; - break; - case LM7x_SD74: - l_rawValue >>= 3; - l_lsbvalue = 0.0625f; - l_noBits = 13u; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_rawValue >>= 4; - l_lsbvalue = 0.0625f; - l_noBits = 12u; - break; - case LM7x_SD125: - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 10u; - break; - default: // use lowest resolution as fallback if no device has been configured - l_rawValue >>= 5; - l_lsbvalue = 0.25f; - l_noBits = 11u; - break; - } - - l_intTemperature = Plugin_039_convert_two_complement(l_rawValue, l_noBits); - - l_returnValue = l_intTemperature * l_lsbvalue; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, - strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex_decimal(l_LM7xsubtype).c_str(), - formatToHex_decimal(l_rawValue).c_str(), - l_noBits, - l_lsbvalue)); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -uint16_t readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) -{ - uint16_t l_returnValue = 0u; - uint16_t l_mswaitTime = 0u; - - - switch (l_LM7xsubType) - { - case LM7x_SD70: - case LM7x_SD71: - case LM7x_SD74: - l_mswaitTime = 300; - break; - case LM7x_SD121: - case LM7x_SD122: - case LM7x_SD123: - case LM7x_SD124: - l_mswaitTime = 320; - break; - case LM7x_SD125: - l_mswaitTime = 100; - break; - default: - l_mswaitTime = 500; - break; - } - - // // activate communication -> CS low - // handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - if (l_runMode) - { - // shutdown mode active -> conversion when called - uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; - - // send inital 4 bytes to wake the device and start the conversion - transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); - - // wait specific ms for conversion to be ready (TI datasheet per devices) - delay(l_mswaitTime); - - // send remaining 8 bytes to read the device ID and shutdown the device - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); - - // // wakeup device and start conversion - // // initial read of conversion result is obsolete - // SPI.transfer16(0xFFFF); - - // // (wakeup device with "all zero2 message in the last 8 bits - // SPI.transfer16(0xFF00); - - // //wait specific ms for conversion to be ready (TI datasheet per devices) - // delay(l_mswaitTime); - - // //read temperature value (16 Bit) - // l_returnValue = SPI.transfer16(0x0000); - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits ( maybe redundant, check with test) - // SPI.transfer16(0xFFFF); - } - else - { - // shutdown mode inactive -> normal background conversion during call cycle - uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); - - // read temperature value (16 Bit) - l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); - - // read Manufatures/Device ID (16 Bit) - *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); - - - // l_returnValue = SPI.transfer16(0x0000); //read temperature value (16 Bit) - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // start conversion until next read (8 Bit sufficient) - // // 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F allowed - else device goes to test mode (not desirable here) - // SPI.transfer(0x00); - // // SPI.transfer16(0x0000); - } - - // // stop communication -> CS high - // handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), - formatToHex_decimal(l_returnValue).c_str(), - formatToHex(*(l_device_id)).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE - -/**************************************************************************/ - -/*! - @brief generic high level library to access SPI interface from plugins - with GPIO pin handled as CS - chri.kai.in 2021 - - Initial Revision - chri.kai.in 2021 - - TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library - - - /**************************************************************************/ - - -/**************************************************************************/ - -/*! - - @brief Identifying the CS pin from the event basic data structure - @param event pointer to the event structure; default GPIO is chosen as GPIO 15 - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -int get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on ESP8266 - if (CONFIG_PIN1 != -1) { - return CONFIG_PIN1; - } - return 15; // D8 -} - -/**************************************************************************/ - -/*! - @brief Initializing GPIO as OUTPUT for CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void init_SPI_CS_Pin(int8_t l_CS_pin_no) { - // set the slaveSelectPin as an output: - pinMode(l_CS_pin_no, OUTPUT); -} - -/**************************************************************************/ - -/*! - @brief Handling GPIO as CS for SPI communication - @param l_CS_pin_no the GPIO pin number used as CS - @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { - P039_CS_Delay(); // tCWH (min) >= x00ns - digitalWrite(l_CS_pin_no, l_state); - P039_CS_Delay(); // tCC (min) >= x00ns -} - -/**************************************************************************/ - -/*! - @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 8 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) -{ - uint8_t l_messageBuffer[2] = { l_address, value }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - @param value the unsigned 16 Bit message to be transferred - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) -{ - uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(value).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 8 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint8_t read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[2] = { l_address, 0x00 }; - - transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_messageBuffer[1]).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_messageBuffer[1]; -} - -/**************************************************************************/ - -/*! - @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_address the register addess of the connected SPI device - - @returns the unsigned 16 Bit message read from l_address - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -uint16_t read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) -{ - uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; - uint16_t l_returnValue; - - transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); - l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), - formatToHex(l_address).c_str(), - formatToHex_decimal(l_returnValue).c_str())); - } - - # endif // ifndef BUILD_NO_DEBUG - - return l_returnValue; -} - -/**************************************************************************/ - -/*! - @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_noBytesToSend number of bytes to read/write from/to SPI - @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send - and provide read bytes from the SPI bus after the call - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) -{ - // activate communication -> CS low - handle_SPI_CS_Pin(l_CS_pin_no, LOW); - - for (size_t i = 0u; i < l_noBytesToSend; i++) - { - l_inoutMessageBuffer[i] = SPI.transfer(l_inoutMessageBuffer[i]); - } - - // stop communication -> CS high - handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) - { - String log; - - if ((log.reserve(120u))) { // reserve value derived from example log file - log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char - - for (uint8_t i = 0; i < l_noBytesToSend; ++i) - { - log += ' '; // 1 char - log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char - } - addLogMove(LOG_LEVEL_DEBUG_MORE, log); - } - } - - # endif // ifndef BUILD_NO_DEBUG -} - -/**************************************************************************/ - -/*! - @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change16BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint16_t l_flagmask, bool l_set_reset) -{ - uint16_t l_reg = 0u; - - // read in config register - l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); - - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - -/**************************************************************************/ - -/*! - @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS - @param l_CS_pin_no the GPIO pin number used as CS - @param l_readaddress SPI read address of the device register - @param l_writeaddress SPI write address of the device register - @param l_flagmask mask set to apply on the read register - @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) - - - @returns - - Initial Revision - chri.kai.in 2021 - - /**************************************************************************/ -void change8BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, uint8_t l_flagmask, bool l_set_reset) -{ - uint8_t l_reg = 0u; - - // read in config register - l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); - - - // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead - if (l_set_reset) { - l_reg |= l_flagmask; - } - else - { - l_reg &= ~(l_flagmask); - } - - // write to configuration register - write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); -} - #endif // USES_P039 diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index 814af8073..3eaa113ee 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -2,29 +2,1552 @@ #ifdef USES_P039 -/* - P039_data_struct::P039_data_struct( - uint16_t l_conversionResult, - uint8_t l_devicefaults, - unsigned long l_timer, - bool l_sensorFault, - bool l_convReady) - : conversionResult(l_conversionResult), deviceFaults(l_devicefaults), timer(l_timer), sensorFault(l_sensorFault), convReady(l_convReady) - {} +P039_data_struct::P039_data_struct(struct EventStruct*event) {} + +bool P039_data_struct::begin(struct EventStruct*event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // set the slaveSelectPin as an output: + init_SPI_CS_Pin(CS_pin_no); + + // initialize SPI: + // SPI.setHwCs(false); // Already done + // SPI.begin(); + + // ensure MODE3 access to SPI device + SPI.setDataMode(SPI_MODE3); + + /* + if (P039_MAX_TYPE == P039_MAX6675) { + + // SPI.setBitOrder(MSBFIRST); + } + */ + if (P039_MAX_TYPE == P039_MAX31855) { + // SPI.setBitOrder(MSBFIRST); + + // FIXED: c.k.i. : moved static fault flag to instance data structure + sensorFault = false; + } + + + if (P039_MAX_TYPE == P039_MAX31856) { + // init string - content accoring to inital implementation of P039 - MAX31856 read function + // write to Adress 0x80 + // activate 50Hz filter in CR0, choose averaging and TC type from configuration in CR1, activate OV/UV/OC faults, write defaults to + // CJHF, CJLF, LTHFTH, LTHFTL, LTLFTH, LTLFTL, CJTO + uint8_t sendBuffer[11] = + { 0x80, static_cast(P039_RTD_FILT_TYPE), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE), 0xFC, 0x7F, 0xC0, 0x7F, + 0xFF, 0x80, 0x00, 0x00 }; + + transfer_n_ByteSPI(CS_pin_no, 11, &sendBuffer[0]); + + sensorFault = false; + + // start on shot conversion for upcoming read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + } + + + if (P039_MAX_TYPE == P039_MAX31865) { + // two step initialization buffer + uint8_t initSendBufferHFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + uint8_t initSendBufferLFTH[3] = { (MAX31865_WRITE_ADDR_BASE + MAX31865_HFT_MSB), 0xFF, 0xFF }; + + // write intially 0x00 to CONFIG register + write8BitRegister(CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), 0x00u); + + // activate 50Hz filter, clear all faults, no auto conversion, no conversion started + change8BitRegister(CS_pin_no, + MAX31865_RD_ADDRESS(MAX31865_CONFIG), + MAX31865_WR_ADDRESS(MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // configure 2/4-wire sensor connection as default + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // set HighFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferHFTH[0]); + + // set LowFault Threshold + transfer_n_ByteSPI(CS_pin_no, 3, &initSendBufferLFTH[0]); + + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // save current timer for next calculation + timer = millis(); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + } + + /* + if (P039_MAX_TYPE == P039_LM7x) + { + // TODO: c.k.i.: more detailed inits depending on the sub devices expected , e.g. TMP 122/124 + } + */ + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P039 : %s : SPI Init - DONE"), getTaskDeviceName(event->TaskIndex).c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + return success; +} + +bool P039_data_struct::read(EventStruct *event) { + bool success = false; + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + float Plugin_039_Celsius = NAN; + + switch (MaxType) { + case P039_MAX6675: + Plugin_039_Celsius = readMax6675(event); + break; + case P039_MAX31855: + Plugin_039_Celsius = readMax31855(event); + break; + case P039_MAX31856: + Plugin_039_Celsius = readMax31856(event); + break; + case P039_MAX31865: + Plugin_039_Celsius = readMax31865(event); + break; + case P039_LM7x: + Plugin_039_Celsius = readLM7x(event); + break; + } + + if (isValidFloat(Plugin_039_Celsius)) + { + UserVar.setFloat(event->TaskIndex, 0, Plugin_039_Celsius); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + String log = strformat(F("P039 : %s :"), getTaskDeviceName(event->TaskIndex).c_str()); + + const uint8_t valueCount = getValueCountForTask(event->TaskIndex); + + for (uint8_t i = 0; i < valueCount; ++i) + { + log += strformat( + F(" %s: %s"), + Cache.getTaskDeviceValueName(event->TaskIndex, i).c_str(), + formatUserVarNoCheck(event, i).c_str()); + } + addLogMove(LOG_LEVEL_INFO, log); + } + # endif // ifndef BUILD_NO_DEBUG + + if (definitelyGreaterThan(Plugin_039_Celsius, P039_TEMP_THRESHOLD)) { + success = true; + } + } + else + { + UserVar.setFloat(event->TaskIndex, 0, NAN); + UserVar.setFloat(event->TaskIndex, 1, NAN); + + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("P039 : %s : No Sensor attached!"), getTaskDeviceName(event->TaskIndex).c_str())); + } + success = false; + } + return success; +} + +bool P039_data_struct::plugin_tasktimer_in(EventStruct *event) { + bool success = false; + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // Get the MAX Type (6675 / 31855 / 31856) + uint8_t MaxType = P039_MAX_TYPE; + + switch (MaxType) + { + case P039_MAX31865: + { + switch (event->Par1) + { + case MAX31865_BIAS_ON_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_BIAS_ON_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // activate one shot conversion + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_ONE_SHOT, + P039_SET); + + // set next state in sequence -> READ STATE + // start time to follow up on conversion and read the conversion result + convReady = false; + Scheduler.setPluginTaskTimer(MAX31865_CONVERSION_TIME, event->TaskIndex, MAX31865_RD_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + break; + } + case MAX31865_RD_STATE: + { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("P039 : %s : current state: MAX31865_RD_STATE; delta: %d ms"), + getTaskDeviceName(event->TaskIndex).c_str(), + timePassedSince(timer))); // calc delta since last call + } + # endif // ifndef BUILD_NO_DEBUG + + // save current timer for next calculation + timer = millis(); + + // read conversion result + conversionResult = read16BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_RTD_MSB)); + + // deactivate BIAS short after read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_RESET); + + // read fault register to get a full picture + deviceFaults = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_FAULT)); + + // mark conversion as ready + convReady = true; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, + strformat(F("P039 : %s : conversionResult: %s; deviceFaults: %s; Next State: %d"), + getTaskDeviceName(event->TaskIndex).c_str(), + formatToHex_decimal(conversionResult).c_str(), + formatToHex_decimal(deviceFaults).c_str(), + event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + + + break; + } + case MAX31865_INIT_STATE: + default: + { + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : %s : current state: MAX31865_INIT_STATE, " + "default; next state: MAX31865_BIAS_ON_STATE"), + getTaskDeviceName(event->TaskIndex).c_str())); + + // FIXME: Shouldn't this be in active code? // save current timer for next calculation + timer = millis(); + } + # endif // ifndef BUILD_NO_DEBUG + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // set next state in sequence -> BIAS ON STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + + break; + } + } + break; + } + default: + { + break; + } + } + return success; +} + +void P039_data_struct::AddMainsFrequencyFilterSelection(struct EventStruct *event) +{ + const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; + const int FToptionValues[] = { 0, 1 }; + + const FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); + + selector.addFormSelector(F("Supply Frequency Filter"), F("filttype"), P039_RTD_FILT_TYPE); + addUnit(F("Hz")); + # ifndef LIMIT_BUILD_SIZE + addFormNote(F("Filter power net frequency (50/60 Hz)")); + # else // ifndef LIMIT_BUILD_SIZE + addUnit(F("net frequency")); + # endif // ifndef LIMIT_BUILD_SIZE +} + +float P039_data_struct::readMax6675(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + uint8_t messageBuffer[2] = { 0 }; + uint16_t rawvalue = 0u; + + + // "transfer" 2 bytes to SPI to get 16 Bit return value + transfer_n_ByteSPI(CS_pin_no, 2, &messageBuffer[0]); + + // merge 16Bit return value from messageBuffer + rawvalue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX6675 : RAW - BIN: %s HEX: %s DEC: %d MSB: %s LSB: %s"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue, + formatToHex_decimal(messageBuffer[0]).c_str(), + formatToHex_decimal(messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + // Open Thermocouple + // Bit D2 is normally low and goes high if the thermocouple input is open. In order to allow the operation of the + // open thermocouple detector, T- must be grounded. Make the ground connection as close to the GND pin + // as possible. + // 2021-05-11: FIXED: c.k.i.: OC Flag already checked; migrated to #define for improved maintenance + const bool Plugin_039_SensorAttached = !(rawvalue & MAX6675_TC_OC); + + if (Plugin_039_SensorAttached) + { + // shift RAW value 3 Bits to the right to get the data + rawvalue >>= 3; + + // calculate Celsius with device resolution 0.25 K/bit + return rawvalue * 0.25f; + } + else + { + return NAN; + } +} + +float P039_data_struct::readMax31855(struct EventStruct *event) +{ + uint8_t messageBuffer[4] = { 0 }; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // "transfer" 0x0 and read the 32 Bit conversion register from the Chip + transfer_n_ByteSPI(CS_pin_no, 4, &messageBuffer[0]); + + // merge rawvalue from 4 bytes of messageBuffer + uint32_t rawvalue = + ((static_cast(messageBuffer[0]) << + 24) | + (static_cast(messageBuffer[1]) << + 16) | (static_cast(messageBuffer[2]) << 8) | static_cast(messageBuffer[3])); + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(200u)) { // reserve value derived from example log file + log = strformat(F("P039 : MAX31855 : RAW - BIN: %s rawvalue,HEX: %s rawvalue,DEC: %d messageBuffer[],HEX:"), + String(rawvalue, BIN).c_str(), + formatToHex(rawvalue).c_str(), + rawvalue); + + for (size_t i = 0u; i < 4; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(messageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // check for fault flags in LSB of 32 Bit messageBuffer + if (sensorFault != ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0)) { + // Fault code changed, log them + sensorFault = ((rawvalue & (MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)) == 0); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : MAX31855 : "); + + if (sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault code :"); + + if (rawvalue & MAX31855_TC_OC) { + log += F(" Open (no connection)"); + } + + if (rawvalue & MAX31855_TC_SC) { + log += F(" Short-circuit to GND"); + } + + if (rawvalue & MAX31855_TC_SCVCC) { + log += F(" Short-circuit to Vcc"); + } + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + # endif // ifndef BUILD_NO_DEBUG + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : MAX31855 : rawvalue: %s sensorFault: "), + formatToHex_decimal(rawvalue).c_str(), + formatToHex_decimal(sensorFault).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + } + + // D16 - This bit reads at 1 when any of the SCV, SCG, or OC faults are active. Default value is 0. + // 2020-05-11: FIXED: c.k.i.: migrated plain flag mask to #defines to enhance maintainability; added all fault flags for safety reasons + const bool Plugin_039_SensorAttached = !(rawvalue & (MAX31855_TC_GENFLT | MAX31855_TC_SCVCC | MAX31855_TC_SC | MAX31855_TC_OC)); + + if (Plugin_039_SensorAttached) + { + // Data is D[31:18] + // Shift RAW value 18 Bits to the right to get the data + rawvalue >>= 18; + + // Check for negative Values + // +25.00 0000 0001 1001 00 + // 0.00 0000 0000 0000 00 + // -0.25 1111 1111 1111 11 + // -1.00 1111 1111 1111 00 + // -250.00 1111 0000 0110 00 + // We're left with (32 - 18 =) 14 bits + int temperature = convert_two_complement(rawvalue, 14); + + // Calculate Celsius + return temperature * 0.25f; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31856(struct EventStruct *event) +{ + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + + uint8_t registers[MAX31856_NO_REG] = { 0 }; + uint8_t messageBuffer[MAX31856_NO_REG + 1] = { 0 }; + + messageBuffer[0] = MAX31856_READ_ADDR_BASE; + + // "transfer" 0x0 starting at address 0x00 and read the all registers from the Chip + transfer_n_ByteSPI(CS_pin_no, (MAX31856_NO_REG + 1), &messageBuffer[0]); + + // transfer data from messageBuffer and get rid of initial address uint8_t + for (uint8_t i = 0u; i < MAX31856_NO_REG; ++i) { + registers[i] = messageBuffer[i + 1]; + } + + // configure device for next conversion + // activate frequency filter according to configuration + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + // set averaging and TC type + write8BitRegister(CS_pin_no, (MAX31856_WRITE_ADDR_BASE + MAX31856_CR1), static_cast((P039_CONFIG_4 << 4) | P039_TC_TYPE)); + + + // start on shot conversion for next read cycle + change8BitRegister(CS_pin_no, + (MAX31856_READ_ADDR_BASE + MAX31856_CR0), + (MAX31856_WRITE_ADDR_BASE + MAX31856_CR0), + MAX31856_SET_ONE_SHOT, + P039_SET); + + + // now derive raw value from respective registers + uint32_t rawvalue = static_cast(registers[MAX31856_LTCBH]); + + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBM]); + rawvalue = (rawvalue << 8) | static_cast(registers[MAX31856_LTCBL]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 :"); + + for (uint8_t i = 0; i < MAX31856_NO_REG; ++i) { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + log += F(" rawvalue: "); + log += formatToHex_decimal(rawvalue); + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // ignore TC Range Bit in case Voltage Modes are used + // datasheet: + // Thermocouple Out-of-Range fault. + // 0 = The Thermocouple Hot Junction temperature is within the normal operating range (see Table 1). + // 1 = The Thermocouple Hot Junction temperature is outside of the normal operating range. + // Note: The TC Range bit should be ignored in voltage mode. + uint8_t sr = registers[MAX31856_SR]; + + if ((8u == P039_TC_TYPE) || (12u == P039_TC_TYPE)) { + sr &= ~MAX31856_TC_TCRANGE; + } + + + // FIXED: c.k.i. : moved static fault flag to instance data structure + + sensorFault = (sr != 0); // Set new state + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + // FIXME TD-er: Part of expression is always false (sr == 0) + const bool faultResolved = sensorFault && (sr == 0); + + if (sensorFault || faultResolved) { + String log; + + if (log.reserve(140u)) { // reserve value derived from example log file + log = F("P039 : MAX31856 : "); + + if (!sensorFault) { + log += F("Fault resolved"); + } else { + log += F("Fault :"); + + if (sr & MAX31856_TC_OC) { + log += F(" Open (no connection)"); + } + + if (sr & MAX31856_TC_OVUV) { + log += F(" Over/Under Voltage"); + } + + if (sr & MAX31856_TC_TCLOW) { + log += F(" TC Low"); + } + + if (sr & MAX31856_TC_TCLHIGH) { + log += F(" TC High"); + } + + if (sr & MAX31856_TC_CJLOW) { + log += F(" CJ Low"); + } + + if (sr & MAX31856_TC_CJHIGH) { + log += F(" CJ High"); + } + + if (sr & MAX31856_TC_TCRANGE) { + log += F(" TC Range"); + } + + if (sr & MAX31856_TC_CJRANGE) { + log += F(" CJ Range"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + const bool Plugin_039_SensorAttached = (sr == 0); + + if (Plugin_039_SensorAttached) + { + rawvalue >>= 5; // bottom 5 bits are unused + // We're left with (24 - 5 =) 19 bits + + { + float temperature = 0; + + switch (P039_TC_TYPE) + { + case 8: + { + temperature = rawvalue / 1677721.6f; // datasheet: rawvalue = 8 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (8 x 1.6 x 2^17) + break; + } + case 12: + { + temperature = rawvalue / 6710886.4f; // datasheet: rawvalue = 32 x 1.6 x 2^17 x VIN -> VIN = rawvalue / (32 x 1.6 x 2^17) + break; + } + default: + { + temperature = convert_two_complement(rawvalue, 19); + + // Calculate Celsius + temperature /= 128.0f; + break; + } + } + + return temperature; + } + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +float P039_data_struct::readMax31865(struct EventStruct *event) +{ + uint8_t registers[MAX31865_NO_REG] = { 0 }; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + String log; + + if (log.reserve(80u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 :"); + log += F(" P039_data->convReady: "); + log += boolToString(convReady); + + addLogMove(LOG_LEVEL_DEBUG, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + + // read conversion result and faults from plugin data structure + // if pointer exists and conversion has been finished + if (convReady) { + rawValue = conversionResult; + registers[MAX31865_FAULT] = deviceFaults; + } + + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(160u)) { // reserve value derived from example log file + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + registers[i] = read8BitRegister(CS_pin_no, (MAX31865_READ_ADDR_BASE + i)); + } + + log = F("P039 : MAX31865 :"); + + for (uint8_t i = 0u; i < MAX31865_NO_REG; ++i) + { + log += ' '; + log += formatToHex_decimal(registers[i]); + } + + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG + + // Prepare and start next conversion, before handling faults and rawValue + // clear all faults + MAX31865_clearFaults(CS_pin_no); + + // set frequency filter + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_50HZ, + static_cast(P039_RTD_FILT_TYPE)); + + + // configure read access with configuration from web interface + MAX31865_setConType(CS_pin_no, P039_CONFIG_4); + + // activate BIAS short before read, to reduce power consumption + change8BitRegister(CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_VBIAS_ON, + P039_SET); + + // start time to follow up on BIAS activation before starting the conversion + // and start conversion sequence via TIMER API + // save current timer for next calculation + timer = millis(); + + // set next state to MAX31865_BIAS_ON_STATE + + Scheduler.setPluginTaskTimer(MAX31865_BIAS_WAIT_TIME, event->TaskIndex, MAX31865_BIAS_ON_STATE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + if (registers[MAX31865_FAULT]) + { + String log; + + if (log.reserve(210u)) { // reserve value derived from example log file + log = F("P039 : MAX31865 : "); + + log += F("Fault : "); + log += formatToHex_decimal(registers[MAX31865_FAULT]); + log += F(" :"); + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_OVUV) + { + log += F(" Under/Over voltage"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_RTDINLOW) + { + log += F(" RTDIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINHIGH) + { + log += F(" REFIN- < 0.85 x Bias - FORCE- open"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_REFINLOW) + { + log += F(" REFIN- > 0.85 x Bias"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_LOWTHRESH) + { + log += F(" RTD Low Threshold"); + } + + if (registers[MAX31865_FAULT] & MAX31865_FAULT_HIGHTHRESH) + { + log += F(" RTD High Threshold"); + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + } + # endif // ifndef BUILD_NO_DEBUG + + + bool ValueValid = false; + + if (registers[MAX31865_FAULT] == 0x00u) { + ValueValid = true; + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : registers[MAX31865_FAULT]: %s ValueValid: %s"), + formatToHex_decimal(registers[MAX31865_FAULT]).c_str(), + FsP(boolToString(ValueValid)))); + } + + # endif // ifndef BUILD_NO_DEBUG + + if (ValueValid) + { + rawValue >>= 1; // bottom fault bits is unused + + float temperature = convert_to_temperature(rawValue, getNomResistor(P039_RTD_TYPE), P039_RTD_RES); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : Temperature : rawValue: %s temperature: %.3f P039_RTD_TYPE: %d P039_RTD_RES: %d"), + formatToHex_decimal(rawValue).c_str(), + temperature, + P039_RTD_TYPE, + P039_RTD_RES)); + } + + # endif // ifndef BUILD_NO_DEBUG + + // add offset handling from configuration webpage + temperature += P039_RTD_OFFSET; + + // Calculate Celsius + return temperature; + } + else + { + // Fault state, thus output no value. + return NAN; + } +} + +void P039_data_struct::MAX31865_clearFaults(int8_t l_CS_pin_no) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG)); + + + // clear all faults ( write "0" to D2, D3, D5; write "1" to D2) + l_reg &= ~(MAX31865_SET_ONE_SHOT | MAX31865_FAULT_CTRL_MASK); + l_reg |= MAX31865_CLEAR_FAULTS; + + // write configuration + write8BitRegister(l_CS_pin_no, (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), l_reg); +} + +void P039_data_struct::MAX31865_setConType(int8_t l_CS_pin_no, uint8_t l_conType) +{ + bool l_set_reset = false; + + // configure if 3 WIRE bit will be set/reset + switch (l_conType) + { + case 0: + l_set_reset = P039_RESET; + break; + case 1: + l_set_reset = P039_SET; + break; + default: + l_set_reset = P039_RESET; + break; + } + + // change to configuration register + change8BitRegister(l_CS_pin_no, + (MAX31865_READ_ADDR_BASE + MAX31865_CONFIG), + (MAX31865_WRITE_ADDR_BASE + MAX31865_CONFIG), + MAX31865_SET_3WIRE, + l_set_reset); +} + +/**************************************************************************/ + +/*! + @brief Read the temperature in C from the RTD through calculation of the + resistance. Uses + http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf + technique + @param RTDnominal The 'nominal' resistance of the RTD sensor, usually 100 + or 1000 + @param refResistor The value of the matching reference resistor, usually + 430 or 4300 + @returns Temperature in C */ -bool P039_data_struct::begin() + +/**************************************************************************/ +float P039_data_struct::convert_to_temperature(uint32_t l_rawvalue, float RTDnominal, float refResistor) { - return false; + # define RTD_A 3.9083e-3f + # define RTD_B -5.775e-7f + + float Z1, Z2, Z3, Z4, Rt, temp; + + Rt = l_rawvalue; + Rt /= 32768u; + Rt *= refResistor; + + Z1 = -RTD_A; + Z2 = RTD_A * RTD_A - (4 * RTD_B); + Z3 = (4 * RTD_B) / RTDnominal; + Z4 = 2 * RTD_B; + + temp = Z2 + (Z3 * Rt); + temp = (sqrtf(temp) + Z1) / Z4; + + if (temp >= 0) { + return temp; + } + + Rt /= RTDnominal; + Rt *= 100; // normalize to 100 ohm + + float rpoly = Rt; + + temp = -242.02f; + temp += 2.2228f * rpoly; + rpoly *= Rt; // square + temp += 2.5859e-3f * rpoly; + rpoly *= Rt; // ^3 + temp -= 4.8260e-6f * rpoly; + rpoly *= Rt; // ^4 + temp -= 2.8183e-8f * rpoly; + rpoly *= Rt; // ^5 + temp += 1.5243e-10f * rpoly; + + return temp; } -bool P039_data_struct::read() +uint16_t P039_data_struct::getNomResistor(uint8_t l_RType) { - return false; + uint16_t l_returnValue = 100u; + + switch (l_RType) + { + case MAX31865_PT1000: + l_returnValue = 1000u; + break; + case MAX31865_PT100: // Fall through + default: + break; + } + return l_returnValue; } -bool P039_data_struct::write() +int P039_data_struct::convert_two_complement(uint32_t value, int nr_bits) { + const bool negative = (value & (1 << (nr_bits - 1))) != 0; + int nativeInt; + + if (negative) { + // Add zeroes to the left to create the proper negative native-sized integer. + nativeInt = value | ~((1 << nr_bits) - 1); + } else { + nativeInt = value; + } + return nativeInt; +} + +float P039_data_struct::readLM7x(struct EventStruct *event) { - return false; + float temperature = 0.0f; + uint16_t device_id = 0u; + uint16_t rawValue = 0u; + + int8_t CS_pin_no = get_SPI_CS_Pin(event); + + // operate LM7x devices in polling mode, assuming conversion is ready with every call of this read function ( >=210ms call cycle) + // this allows usage of multiples generations of LM7x devices, that doe not provde conversion ready information in temperature register + + rawValue = readLM7xRegisters(CS_pin_no, P039_RTD_LM_TYPE, P039_RTD_LM_SHTDWN, &device_id); + + temperature = convertLM7xTemp(rawValue, P039_RTD_LM_TYPE); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) + { + addLog(LOG_LEVEL_DEBUG, strformat(F("P039 : LM7x : readLM7x : rawValue: %s device_id: %s temperature: %.3f"), + formatToHex_decimal(rawValue).c_str(), + formatToHex(device_id).c_str(), + temperature)); + } + # endif // ifndef BUILD_NO_DEBUG + + return temperature; +} + +float P039_data_struct::convertLM7xTemp(uint16_t l_rawValue, uint16_t l_LM7xsubtype) +{ + float l_returnValue = 0.0f; + float l_lsbvalue = 0.0f; + uint8_t l_noBits = 0u; + int l_intTemperature = 0; + + switch (l_LM7xsubtype) + { + case LM7x_SD70: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + case LM7x_SD71: + l_rawValue >>= 2; + l_lsbvalue = 0.03125f; + l_noBits = 14u; + break; + case LM7x_SD74: + l_rawValue >>= 3; + l_lsbvalue = 0.0625f; + l_noBits = 13u; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_rawValue >>= 4; + l_lsbvalue = 0.0625f; + l_noBits = 12u; + break; + case LM7x_SD125: + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 10u; + break; + default: // use lowest resolution as fallback if no device has been configured + l_rawValue >>= 5; + l_lsbvalue = 0.25f; + l_noBits = 11u; + break; + } + + l_intTemperature = convert_two_complement(l_rawValue, l_noBits); + + l_returnValue = l_intTemperature * l_lsbvalue; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, + strformat(F("P039 : LM7x : convertLM7xTemp : l_returnValue: %s l_LM7xsubtype: %s l_rawValue: %s l_noBits: %d l_lsbvalue: %.3f"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex_decimal(l_LM7xsubtype).c_str(), + formatToHex_decimal(l_rawValue).c_str(), + l_noBits, + l_lsbvalue)); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xsubType, uint8_t l_runMode, uint16_t *l_device_id) +{ + uint16_t l_returnValue = 0u; + uint16_t l_mswaitTime = 0u; + + + switch (l_LM7xsubType) + { + case LM7x_SD70: + case LM7x_SD71: + case LM7x_SD74: + l_mswaitTime = 300; + break; + case LM7x_SD121: + case LM7x_SD122: + case LM7x_SD123: + case LM7x_SD124: + l_mswaitTime = 320; + break; + case LM7x_SD125: + l_mswaitTime = 100; + break; + default: + l_mswaitTime = 500; + break; + } + + // // activate communication -> CS low + // handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + if (l_runMode) + { + // shutdown mode active -> conversion when called + uint8_t messageBuffer[12] = { 0xFF, 0xFF, 0xFF, 0X00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF }; + + // send inital 4 bytes to wake the device and start the conversion + transfer_n_ByteSPI(l_CS_pin_no, 4, &messageBuffer[0]); + + // wait specific ms for conversion to be ready (TI datasheet per devices) + delay(l_mswaitTime); + + // send remaining 8 bytes to read the device ID and shutdown the device + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[4]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[4] << 8) | messageBuffer[5]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); + + // // wakeup device and start conversion + // // initial read of conversion result is obsolete + // SPI.transfer16(0xFFFF); + + // // (wakeup device with "all zero2 message in the last 8 bits + // SPI.transfer16(0xFF00); + + // //wait specific ms for conversion to be ready (TI datasheet per devices) + // delay(l_mswaitTime); + + // //read temperature value (16 Bit) + // l_returnValue = SPI.transfer16(0x0000); + // // l_returnValue <<= 8; + // // l_returnValue = SPI.transfer(0x00); + + // // set device to shutdown with "all one" message in the last 8 bits + // SPI.transfer16(0xFFFF); + + // // read Manufatures/Device ID (16 Bit) + // *(l_device_id) = SPI.transfer16(0x0000); + // // *(l_device_id) <<= 8; + // // *(l_device_id) = SPI.transfer(0x00); + + // // set device to shutdown with "all one" message in the last 8 bits ( maybe redundant, check with test) + // SPI.transfer16(0xFFFF); + } + else + { + // shutdown mode inactive -> normal background conversion during call cycle + uint8_t messageBuffer[8] = { 0x00, 0x00, 0xFF, 0XFF, 0x00, 0x00, 0x00, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 8, &messageBuffer[0]); + + // read temperature value (16 Bit) + l_returnValue = ((messageBuffer[0] << 8) | messageBuffer[1]); + + // read Manufatures/Device ID (16 Bit) + *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); + + + // l_returnValue = SPI.transfer16(0x0000); //read temperature value (16 Bit) + // // l_returnValue <<= 8; + // // l_returnValue = SPI.transfer(0x00); + + // // set device to shutdown + // SPI.transfer16(0xFFFF); + + // // read Manufatures/Device ID (16 Bit) + // *(l_device_id) = SPI.transfer16(0x0000); + // // *(l_device_id) <<= 8; + // // *(l_device_id) = SPI.transfer(0x00); + + // // start conversion until next read (8 Bit sufficient) + // // 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F allowed - else device goes to test mode (not desirable here) + // SPI.transfer(0x00); + // // SPI.transfer16(0x0000); + } + + // // stop communication -> CS high + // handle_SPI_CS_Pin(l_CS_pin_no, HIGH); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : LM7x : readLM7xRegisters : l_returnValue: %s l_device_id: %s"), + formatToHex_decimal(l_returnValue).c_str(), + formatToHex(*(l_device_id)).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +// POSSIBLE START OF GENERIC SPI HIGH LEVEL FUNCTIONS WITH POTENTIAL OF SYSTEM WIDE RE-USE + +/**************************************************************************/ + +/*! + @brief generic high level library to access SPI interface from plugins + with GPIO pin handled as CS - chri.kai.in 2021 + + Initial Revision - chri.kai.in 2021 + + TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library + + + /**************************************************************************/ + + +/**************************************************************************/ + +/*! + + @brief Identifying the CS pin from the event basic data structure + @param event pointer to the event structure; default GPIO is chosen as GPIO 15 + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +int P039_data_struct::get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on + // ESP8266 + if (CONFIG_PIN1 != -1) { + return CONFIG_PIN1; + } + return 15; // D8 +} + +/**************************************************************************/ + +/*! + @brief Initializing GPIO as OUTPUT for CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::init_SPI_CS_Pin(int8_t l_CS_pin_no) { + // set the slaveSelectPin as an output: + pinMode(l_CS_pin_no, OUTPUT); +} + +/**************************************************************************/ + +/*! + @brief Handling GPIO as CS for SPI communication + @param l_CS_pin_no the GPIO pin number used as CS + @param l_state the state of the CS pin: "HIGH/LOW" reflecting the physical level + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { + P039_CS_Delay(); // tCWH (min) >= x00ns + digitalWrite(l_CS_pin_no, l_state); + P039_CS_Delay(); // tCC (min) >= x00ns +} + +/**************************************************************************/ + +/*! + @brief write 8 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 8 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) +{ + uint8_t l_messageBuffer[2] = { l_address, value }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write8BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + @param value the unsigned 16 Bit message to be transferred + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) +{ + uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : write16BitRegister : l_address: %s value: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(value).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read 8 bits from adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 8 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +uint8_t P039_data_struct::read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[2] = { l_address, 0x00 }; + + transfer_n_ByteSPI(l_CS_pin_no, 2, l_messageBuffer); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read8BitRegister : l_address: %s returnvalue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_messageBuffer[1]).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_messageBuffer[1]; +} + +/**************************************************************************/ + +/*! + @brief write 16 bits to adress l_address on the SPI interface, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_address the register addess of the connected SPI device + + @returns the unsigned 16 Bit message read from l_address + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +uint16_t P039_data_struct::read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) +{ + uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; + uint16_t l_returnValue; + + transfer_n_ByteSPI(l_CS_pin_no, 3, l_messageBuffer); + l_returnValue = ((l_messageBuffer[1] << 8) | l_messageBuffer[2]); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + addLog(LOG_LEVEL_DEBUG_MORE, strformat(F("P039 : SPI : read16BitRegister : l_address: %s l_returnValue: %s"), + formatToHex(l_address).c_str(), + formatToHex_decimal(l_returnValue).c_str())); + } + + # endif // ifndef BUILD_NO_DEBUG + + return l_returnValue; +} + +/**************************************************************************/ + +/*! + @brief read from/write to dedicated number of bytes from/to SPI, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_noBytesToSend number of bytes to read/write from/to SPI + @param l_inoutMessageBuffer pointer to the messsage buffer to provide bytes to send + and provide read bytes from the SPI bus after the call + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) +{ + // activate communication -> CS low + handle_SPI_CS_Pin(l_CS_pin_no, LOW); + + for (size_t i = 0u; i < l_noBytesToSend; i++) + { + l_inoutMessageBuffer[i] = SPI.transfer(l_inoutMessageBuffer[i]); + } + + // stop communication -> CS high + handle_SPI_CS_Pin(l_CS_pin_no, HIGH); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) + { + String log; + + if (log.reserve(120u)) { // reserve value derived from example log file + log = F("P039 : SPI : transfer_n_ByteSPI : "); // 34 char + + for (uint8_t i = 0; i < l_noBytesToSend; ++i) + { + log += ' '; // 1 char + log += formatToHex_decimal(l_inoutMessageBuffer[i]); // 9 char + } + addLogMove(LOG_LEVEL_DEBUG_MORE, log); + } + } + + # endif // ifndef BUILD_NO_DEBUG +} + +/**************************************************************************/ + +/*! + @brief read a 16Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset) +{ + uint16_t l_reg = 0u; + + // read in config register + l_reg = read16BitRegister(l_CS_pin_no, l_readaddress); + + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write16BitRegister(l_CS_pin_no, l_writeaddress, l_reg); +} + +/**************************************************************************/ + +/*! + @brief read a 8 Bit register and change a flag, writing it back, handling a GPIO CS + @param l_CS_pin_no the GPIO pin number used as CS + @param l_readaddress SPI read address of the device register + @param l_writeaddress SPI write address of the device register + @param l_flagmask mask set to apply on the read register + @param l_set_reset controls if flag mask will be set (-> true) or reset ( -> false) + + + @returns + + Initial Revision - chri.kai.in 2021 + + /**************************************************************************/ +void P039_data_struct::change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset) +{ + uint8_t l_reg = 0u; + + // read in config register + l_reg = read8BitRegister(l_CS_pin_no, l_readaddress); + + + // TODO: c.k.i.: analyze opportunity to use arduino bitSet/Clear macros instead + if (l_set_reset) { + l_reg |= l_flagmask; + } + else + { + l_reg &= ~(l_flagmask); + } + + // write to configuration register + write8BitRegister(l_CS_pin_no, l_writeaddress, l_reg); } #endif // ifdef USES_P039 diff --git a/src/src/PluginStructs/P039_data_struct.h b/src/src/PluginStructs/P039_data_struct.h index 92305b068..9d5cd84e3 100644 --- a/src/src/PluginStructs/P039_data_struct.h +++ b/src/src/PluginStructs/P039_data_struct.h @@ -5,31 +5,242 @@ #ifdef USES_P039 +# include + +# define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) +# define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) + +# define P039_SET true +# define P039_RESET false + +// typically 500ns of wating on positive/negative edge of CS should be enough ( -> datasheet); to make sure we cover a lot of devices we +// spend 1ms +// FIX 2021-05-05: review of all covered device datasheets showed 2µs is more than enough; review with every newly added device +# define P039_CS_Delay() delayMicroseconds(2u) + +# define P039_MAX_TYPE PCONFIG(0) +# define P039_TC_TYPE PCONFIG(1) +# define P039_FAM_TYPE PCONFIG(2) +# define P039_RTD_TYPE PCONFIG(3) +# define P039_CONFIG_4 PCONFIG(4) +# define P039_RTD_FILT_TYPE PCONFIG(5) +# define P039_RTD_LM_TYPE PCONFIG(6) +# define P039_RTD_LM_SHTDWN PCONFIG(7) +# define P039_RTD_RES PCONFIG_LONG(0) +# define P039_FLAGS PCONFIG_ULONG(3) +# define P039_TEMP_THRESHOLD_FLAG 0 +# define P039_RTD_OFFSET PCONFIG_FLOAT(0) +# define P039_TEMP_THRESHOLD PCONFIG_FLOAT(1) + +# define P039_TEMP_THRESHOLD_DEFAULT (-273.15f) // Default and minimum value +# define P039_TEMP_THRESHOLD_MIN P039_TEMP_THRESHOLD_DEFAULT +# define P039_TEMP_THRESHOLD_MAX (1000.0f) // Max value +# define P039_TC 0u +# define P039_RTD 1u + +# define P039_MAX6675 1 +# define P039_MAX31855 2 +# define P039_MAX31856 3 +# define P039_MAX31865 4 +# define P039_LM7x 5 + +// MAX 6675 related defines + +// bit masks to identify failures for MAX 6675 +# define MAX6675_TC_DEVID 0x0002u +# define MAX6675_TC_OC 0x0004u + +// MAX 31855 related defines + +// bit masks to identify failures for MAX 31855 +# define MAX31855_TC_OC 0x00000001u +# define MAX31855_TC_SC 0x00000002u +# define MAX31855_TC_SCVCC 0x00000004u +# define MAX31855_TC_GENFLT 0x00010000u + + +// MAX 31856 related defines + +// base address for read/write acces to MAX 31856 +# define MAX31856_READ_ADDR_BASE 0x00u +# define MAX31856_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31856 +# define MAX31856_CR0 0u +# define MAX31856_CR1 1u +# define MAX31856_MASK 2u +# define MAX31856_CJHF 3u +# define MAX31856_CJLF 4u +# define MAX31856_LTHFTH 5u +# define MAX31856_LTHFTL 6u +# define MAX31856_LTLFTH 7u +# define MAX31856_LTLFTL 8u +# define MAX31856_CJTO 9u +# define MAX31856_CJTH 10u +# define MAX31856_CJTL 11u +# define MAX31856_LTCBH 12u +# define MAX31856_LTCBM 13u +# define MAX31856_LTCBL 14u +# define MAX31856_SR 15u + +# define MAX31856_NO_REG 16u + +// bit masks to identify failures for MAX 31856 +# define MAX31856_TC_OC 0x01u +# define MAX31856_TC_OVUV 0x02u +# define MAX31856_TC_TCLOW 0x04u +# define MAX31856_TC_TCLHIGH 0x08u +# define MAX31856_TC_CJLOW 0x10u +# define MAX31856_TC_CJHIGH 0x20u +# define MAX31856_TC_TCRANGE 0x40u +# define MAX31856_TC_CJRANGE 0x80u + +// bit masks for access of configuration bits +# define MAX31856_SET_50HZ 0x01u +# define MAX31856_CLEAR_FAULTS 0x02u +# define MAX31856_FLT_ISR_MODE 0x04u +# define MAX31856_CJ_SENS_DISABLE 0x08u +# define MAX31856_FAULT_CTRL_MASK 0x30u +# define MAX31856_SET_ONE_SHOT 0x40u +# define MAX31856_SET_CONV_AUTO 0x80u + + +// RTD related defines + +// MAX 31865 related defines + +// waiting time until "in sequence" conversion is ready (-> used in case device is set to shutdown in between call cycles) +// typically 70ms should be fine, according to datasheet maximum -> 66ms - give a little adder to "be sure" conversion is done +// alternatively ONE SHOT bit could be polled (system/SPI bus load !) +# define MAX31865_CONVERSION_TIME 70ul +# define MAX31865_BIAS_WAIT_TIME 10ul + +// MAX 31865 Main States +# define MAX31865_INIT_STATE 0u +# define MAX31865_BIAS_ON_STATE 1u +# define MAX31865_RD_STATE 2u +# define MAX31865_RDY_STATE 3u + +// sensor type +# define MAX31865_PT100 0u +# define MAX31865_PT1000 1u + +// base address for read/write acces to MAX 31865 +# define MAX31865_READ_ADDR_BASE 0x00u +# define MAX31865_WRITE_ADDR_BASE 0x80u + +// register offset values for MAX 31865 +# define MAX31865_CONFIG 0u +# define MAX31865_RTD_MSB 1u +# define MAX31865_RTD_LSB 2u +# define MAX31865_HFT_MSB 3u +# define MAX31865_HFT_LSB 4u +# define MAX31865_LFT_MSB 5u +# define MAX31865_LFT_LSB 6u +# define MAX31865_FAULT 7u + +// total number of registers in MAX 31865 +# define MAX31865_NO_REG 8u + +// bit masks to identify failures for MAX 31865 +# define MAX31865_FAULT_HIGHTHRESH 0x80u +# define MAX31865_FAULT_LOWTHRESH 0x40u +# define MAX31865_FAULT_REFINLOW 0x20u +# define MAX31865_FAULT_REFINHIGH 0x10u +# define MAX31865_FAULT_RTDINLOW 0x08u +# define MAX31865_FAULT_OVUV 0x04u + +// bit masks for access of configuration bits +# define MAX31865_SET_50HZ 0x01u +# define MAX31865_CLEAR_FAULTS 0x02u +# define MAX31865_FAULT_CTRL_MASK 0x0Cu +# define MAX31865_SET_3WIRE 0x10u +# define MAX31865_SET_ONE_SHOT 0x20u +# define MAX31865_SET_CONV_AUTO 0x40u +# define MAX31865_SET_VBIAS_ON 0x80u + +// LM7x related defines + +// LM7x subtype defines +# define LM7x_SD70 0x00u +# define LM7x_SD71 0x01u +# define LM7x_SD74 0x04u +# define LM7x_SD121 0x05u +# define LM7x_SD122 0x06u +# define LM7x_SD123 0x07u +# define LM7x_SD124 0x08u +# define LM7x_SD125 0x09u + +// bit masks for access of configuration bits +# define LM7x_CONV_RDY 0x02u + struct P039_data_struct : public PluginTaskData_base { public: - /* - P039_data_struct(uint16_t conversionResult, - uint8_t deviceFaults, - unsigned long timer, - bool sensorFault, - bool convReady); - */ - - P039_data_struct() = default; + P039_data_struct() = delete; + P039_data_struct(struct EventStruct *event); virtual ~P039_data_struct() = default; - bool begin(); + bool begin(struct EventStruct*event); // Perform read and return true when an alert has been high - bool read(); + bool read(struct EventStruct *event); - // Perform write and return true when an alert has been high - bool write(); + bool plugin_tasktimer_in(struct EventStruct*event); + + static void AddMainsFrequencyFilterSelection(struct EventStruct *event); + +private: + + float readMax6675(struct EventStruct *event); + float readMax31855(struct EventStruct *event); + float readMax31856(struct EventStruct *event); + float readMax31865(struct EventStruct *event); + void MAX31865_clearFaults(int8_t l_CS_pin_no); + void MAX31865_setConType(int8_t l_CS_pin_no, + uint8_t l_conType); + float convert_to_temperature(uint32_t l_rawvalue, + float RTDnominal, + float refResistor); + uint16_t getNomResistor(uint8_t l_RType); + int convert_two_complement(uint32_t value, + int nr_bits); + float readLM7x(struct EventStruct *event); + float convertLM7xTemp(uint16_t l_rawValue, + uint16_t l_LM7xsubtype); + uint16_t readLM7xRegisters(int8_t l_CS_pin_no, + uint8_t l_LM7xsubType, + uint8_t l_runMode, + uint16_t *l_device_id); + int get_SPI_CS_Pin(struct EventStruct *event); + void init_SPI_CS_Pin(int8_t l_CS_pin_no); + void handle_SPI_CS_Pin(int8_t l_CS_pin_no, + bool l_state); + void write8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint8_t value); + void write16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address, + uint16_t value); + uint8_t read8BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + uint16_t read16BitRegister(int8_t l_CS_pin_no, + uint8_t l_address); + void transfer_n_ByteSPI(int8_t l_CS_pin_no, + uint8_t l_noBytesToSend, + uint8_t *l_inoutMessageBuffer); + void change16BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint16_t l_flagmask, + bool l_set_reset); + void change8BitRegister(int8_t l_CS_pin_no, + uint8_t l_readaddress, + uint8_t l_writeaddress, + uint8_t l_flagmask, + bool l_set_reset); - // uint8_t mainState = 0x00u;; - // uint8_t command = 0x00u; uint16_t conversionResult = 0x0000u; uint8_t deviceFaults = 0x00u; unsigned long timer = 0; From f00eed1a56114520bd05226c6097d0ff6bda4f4e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 17:00:28 +0200 Subject: [PATCH 015/161] [P039] Enable support for configurable SPI bus --- src/_P039_Thermosensors.ino | 2 + src/src/PluginStructs/P039_data_struct.cpp | 71 +++------------------- src/src/PluginStructs/P039_data_struct.h | 4 ++ 3 files changed, 14 insertions(+), 63 deletions(-) diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index b1fab3d45..590e0c9a7 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -19,6 +19,7 @@ // Have fun ... Dominik /** Changelog: + * 2025-08-13 tonhuisman: Enable use of secondary SPI bus * 2025-08-13 tonhuisman: Move most code to P039_PluginStruct * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery * 2025-01-03 tonhuisman: Small code size reductions, cleanup of DEBUG level logging @@ -101,6 +102,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) dev.SendDataOption = true; dev.TimerOption = true; dev.PluginStats = true; + dev.SpiBusSelect = true; break; } diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index 3eaa113ee..b0fb6a47c 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -2,7 +2,12 @@ #ifdef USES_P039 -P039_data_struct::P039_data_struct(struct EventStruct*event) {} +P039_data_struct::P039_data_struct(struct EventStruct*event) { + # ifdef ESP32 + const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); + _spi = 0 == spi_bus ? SPI : SPI1; + # endif // ifdef ESP32 +} bool P039_data_struct::begin(struct EventStruct*event) { bool success = false; @@ -11,23 +16,10 @@ bool P039_data_struct::begin(struct EventStruct*event) { // set the slaveSelectPin as an output: init_SPI_CS_Pin(CS_pin_no); - // initialize SPI: - // SPI.setHwCs(false); // Already done - // SPI.begin(); - // ensure MODE3 access to SPI device - SPI.setDataMode(SPI_MODE3); + _spi.setDataMode(SPI_MODE3); - /* - if (P039_MAX_TYPE == P039_MAX6675) { - - // SPI.setBitOrder(MSBFIRST); - } - */ if (P039_MAX_TYPE == P039_MAX31855) { - // SPI.setBitOrder(MSBFIRST); - - // FIXED: c.k.i. : moved static fault flag to instance data structure sensorFault = false; } @@ -1148,32 +1140,6 @@ uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xs // read Manufatures/Device ID (16 Bit) *(l_device_id) = ((messageBuffer[8] << 8) | messageBuffer[9]); - - // // wakeup device and start conversion - // // initial read of conversion result is obsolete - // SPI.transfer16(0xFFFF); - - // // (wakeup device with "all zero2 message in the last 8 bits - // SPI.transfer16(0xFF00); - - // //wait specific ms for conversion to be ready (TI datasheet per devices) - // delay(l_mswaitTime); - - // //read temperature value (16 Bit) - // l_returnValue = SPI.transfer16(0x0000); - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // set device to shutdown with "all one" message in the last 8 bits ( maybe redundant, check with test) - // SPI.transfer16(0xFFFF); } else { @@ -1187,29 +1153,8 @@ uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xs // read Manufatures/Device ID (16 Bit) *(l_device_id) = ((messageBuffer[4] << 8) | messageBuffer[5]); - - - // l_returnValue = SPI.transfer16(0x0000); //read temperature value (16 Bit) - // // l_returnValue <<= 8; - // // l_returnValue = SPI.transfer(0x00); - - // // set device to shutdown - // SPI.transfer16(0xFFFF); - - // // read Manufatures/Device ID (16 Bit) - // *(l_device_id) = SPI.transfer16(0x0000); - // // *(l_device_id) <<= 8; - // // *(l_device_id) = SPI.transfer(0x00); - - // // start conversion until next read (8 Bit sufficient) - // // 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F allowed - else device goes to test mode (not desirable here) - // SPI.transfer(0x00); - // // SPI.transfer16(0x0000); } - // // stop communication -> CS high - // handle_SPI_CS_Pin(l_CS_pin_no, HIGH); - # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG_MORE)) @@ -1443,7 +1388,7 @@ void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesT for (size_t i = 0u; i < l_noBytesToSend; i++) { - l_inoutMessageBuffer[i] = SPI.transfer(l_inoutMessageBuffer[i]); + l_inoutMessageBuffer[i] = _spi.transfer(l_inoutMessageBuffer[i]); } // stop communication -> CS high diff --git a/src/src/PluginStructs/P039_data_struct.h b/src/src/PluginStructs/P039_data_struct.h index 9d5cd84e3..dda4fda3e 100644 --- a/src/src/PluginStructs/P039_data_struct.h +++ b/src/src/PluginStructs/P039_data_struct.h @@ -7,6 +7,8 @@ # include +# include "../Globals/SPI1.h" + # define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) # define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) @@ -246,6 +248,8 @@ private: unsigned long timer = 0; bool sensorFault = false; bool convReady = false; + + SPIClass& _spi = SPI; }; From e775bd14f2d7481473d4943c7d6355ff83ac0350 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 21:00:22 +0200 Subject: [PATCH 016/161] [SPI] Show SPI Bus selector also if only second bus enabled --- src/src/Helpers/SPI_Helper.cpp | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/src/Helpers/SPI_Helper.cpp b/src/src/Helpers/SPI_Helper.cpp index 79099bb84..ce1c291bd 100644 --- a/src/src/Helpers/SPI_Helper.cpp +++ b/src/src/Helpers/SPI_Helper.cpp @@ -3,30 +3,39 @@ void SPIInterfaceSelector(String label, String id, uint8_t choice, - bool disabled) { - const uint8_t spiMaxBusCount = (getSPIBusCount() > 1 - ? ((Settings.isSPI_valid(1) ? 1 : 0) - ) - : 0) + (Settings.isSPI_valid(0) ? 1 : 0); + bool disabled) { + const uint8_t spiCount = getSPIBusCount(); + const bool spi0valid = Settings.isSPI_valid(0); + const bool spi1valid = Settings.isSPI_valid(1); + const uint8_t spiBitmap = (spi0valid ? 1 : 0) | (spi1valid ? 2 : 0); + const uint8_t spiMaxBusCount = (spiCount > 1 + ? (spi1valid ? 1 : 0) + : 0) + (spi0valid ? 1 : 0); - if (spiMaxBusCount > 1) { - static uint8_t spiBusCount = 0; - static int spiBusNumbers[3]; + if ((spiMaxBusCount > 1) || spi1valid) { // Show selector if only bus 1 is enabled + static uint8_t spiBusCount{}; + static uint8_t spiBusBitmap{}; + static int spiBusNumbers[2]; + static String spiBusAttr[2]; - if (spiBusCount != spiMaxBusCount) { + if (spiBusBitmap != spiBitmap) { spiBusCount = 0; spiBusNumbers[spiBusCount] = 0; + spiBusAttr[0] = spi0valid ? EMPTY_STRING : F("disabled"); + spiBusBitmap = spi0valid ? 1 : 0; ++spiBusCount; - if ((getSPIBusCount() > 1) && Settings.isSPI_valid(1)) { + if ((spiCount > 1) && spi1valid) { spiBusNumbers[spiBusCount] = 1; + spiBusBitmap |= 2; ++spiBusCount; } } FormSelectorOptions selector(spiBusCount, - spiBusNumbers); + spiBusNumbers, + spiBusAttr); selector.default_index = 0; - selector.enabled = !disabled; + selector.enabled = !disabled; selector.addFormSelector(label, id, choice); } } From a38c3c04a91303a61b3880b992930f6f1b09369f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 21:01:34 +0200 Subject: [PATCH 017/161] [SPI] Fix initialization for standard FSPI bus --- src/src/DataStructs/SettingsStruct.h | 2 +- src/src/Helpers/Hardware.cpp | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 1a421a91f..0170e9d86 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -327,7 +327,7 @@ public: uint8_t spi_bus = 0xFF) const; // Return true when SPI enabled and opt. user defined pins valid. - bool isSPI_valid(uint8_t spi_bus = 0) const; + bool isSPI_valid(uint8_t spi_bus) const; // Return true when pin is one of the configured I2C pins. bool isI2C_pin(int8_t pin) const; diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index ae19873e8..7ea8364bb 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -229,22 +229,20 @@ void hardwareInit() // SPI Init uint8_t SPI_initialized = 0; - if (tryInitSPI && Settings.isSPI_valid()) + if (tryInitSPI && (Settings.isSPI_valid(0u) + #ifdef ESP32 + || Settings.isSPI_valid(1u) + #endif // ifdef ESP32 + )) { - SPI.setHwCs(false); - - // MFD: for ESP32 enable the SPI on HSPI as the default is VSPI #ifdef ESP32 const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); int8_t spi_gpios[3] = {}; if (Settings.getSPI_pins(spi_gpios, 0u)) { - if (SPI_selection == SPI_Options_e::Vspi_Fspi) { - SPI.begin(); // Default SPI bus - } else { - SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); - } + SPI.setHwCs(false); + SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration SPI_initialized |= 1; } // Init second SPI interface (SPI1) @@ -256,8 +254,9 @@ void hardwareInit() } #else // ifdef ESP32 - SPI.begin(); - SPI_initialized = true; + SPI.setHwCs(false); + SPI.begin(); // Use default GPIO configuration + SPI_initialized = 1; #endif // ifdef ESP32 } From a92dacc44c4d90d37eab74ea4381d3f2302486d8 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 13 Aug 2025 23:34:27 +0200 Subject: [PATCH 018/161] [SPI] Improvements in cross-bus GPIO config validation --- src/src/DataStructs_templ/SettingsStruct.cpp | 2 +- src/src/WebServer/HardwarePage.cpp | 34 +++++++++----------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 7c865a2d1..4a94a494d 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1041,7 +1041,7 @@ bool SettingsStruct_tmpl::isSPI_valid(uint8_t spi_bus) const { #ifdef ESP32 if (result && (getSPIBusCount() > 1)) { // Cross-check pins with other bus int8_t spi1_pins[3]; - getSPI_pins(spi1_pins, 1u); + getSPI_pins(spi1_pins, 1u, true); for (uint8_t i = 0; (i < 3) && result; ++i) { for (uint8_t j = 0; (j < 3) && result; ++j) { diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c0d9df401..20e84d308 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -109,26 +109,24 @@ void handle_hardware() { set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); #endif // if FEATURE_I2C_MULTIPLE #ifdef ESP32 - Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast(SPI_Options_e::None)); - if (Settings.InitSPI == static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI bus 0 GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); - if (!Settings.isSPI_valid(0u)) { // Checks - error += F("User-defined SPI bus 0 pins not configured correctly!\n"); - } - } - Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast(SPI_Options_e::None)); - if (Settings.InitSPI1 == static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI bus 1 GPIO pins - Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); - Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); - Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); - if (!Settings.isSPI_valid(1u)) { // Checks - error += F("User-defined SPI bus 1 pins not configured correctly!\n"); - } + Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast(SPI_Options_e::None)); + // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + + Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast(SPI_Options_e::None)); + // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + if (!Settings.isSPI_valid(spi_bus)) { // Checks + error += strformat(F("SPI bus %u pins not configured correctly!
"), spi_bus); } + } #else //for ESP8266 we keep the old UI - Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init + Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init #endif #ifdef ESP32 Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); From daeb1891d0324b2be284b22dec86f5d1bf98ab1b Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 17 Aug 2025 22:36:40 +0200 Subject: [PATCH 019/161] [Storage] Add settings for External EEPROM --- src/src/Helpers/Misc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/Helpers/Misc.h b/src/src/Helpers/Misc.h index 0f94ffad4..c3123f0b7 100644 --- a/src/src/Helpers/Misc.h +++ b/src/src/Helpers/Misc.h @@ -19,11 +19,13 @@ #define setNBitToUL(N, B, V, M) N=(((N) & ~((M) << (B))) | (static_cast((V) & (M)) << (B))) #define getNBitFromUL(number, bitnr, mask) (((number) >> (bitnr)) & (mask)) +#define set16BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFFFUL) #define set8BitToUL(N, B, V) setNBitToUL(N, B, V, 0xFFUL) #define set4BitToUL(N, B, V) setNBitToUL(N, B, V, 0x0FUL) #define set3BitToUL(N, B, V) setNBitToUL(N, B, V, 0x07UL) #define set2BitToUL(N, B, V) setNBitToUL(N, B, V, 0x03UL) +#define get16BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFFFUL) #define get8BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0xFFUL) #define get4BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x0FUL) #define get3BitFromUL(number, bitnr) getNBitFromUL(number, bitnr, 0x07UL) From ba75ff94f82aed27dc23a09dc71c9995cba97775 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 19 Aug 2025 22:19:37 +0200 Subject: [PATCH 020/161] [SPI] Rename SPI1 object to SPIe to avoid conflict for ESP32-Classic builds --- src/src/Globals/SPI1.cpp | 5 ----- src/src/Globals/SPIe.cpp | 5 +++++ src/src/Globals/{SPI1.h => SPIe.h} | 2 +- src/src/Helpers/Hardware.cpp | 10 +++++----- src/src/PluginStructs/P039_data_struct.cpp | 2 +- src/src/PluginStructs/P039_data_struct.h | 2 +- src/src/PluginStructs/P095_data_struct.cpp | 2 +- src/src/PluginStructs/P095_data_struct.h | 2 +- src/src/PluginStructs/P096_data_struct.cpp | 8 ++++---- src/src/PluginStructs/P096_data_struct.h | 2 +- src/src/PluginStructs/P099_data_struct.cpp | 2 +- src/src/PluginStructs/P099_data_struct.h | 2 +- src/src/PluginStructs/P104_data_struct.cpp | 2 +- src/src/PluginStructs/P104_data_struct.h | 2 +- src/src/PluginStructs/P111_data_struct.cpp | 2 +- src/src/PluginStructs/P111_data_struct.h | 2 +- src/src/PluginStructs/P116_data_struct.cpp | 6 +++--- src/src/PluginStructs/P116_data_struct.h | 2 +- src/src/PluginStructs/P118_data_struct.cpp | 2 +- src/src/PluginStructs/P118_data_struct.h | 2 +- src/src/PluginStructs/P120_data_struct.cpp | 4 ++-- src/src/PluginStructs/P141_data_struct.cpp | 2 +- src/src/PluginStructs/P141_data_struct.h | 2 +- src/src/PluginStructs/P154_data_struct.cpp | 2 +- src/src/PluginStructs/P154_data_struct.h | 2 +- src/src/PluginStructs/P162_data_struct.cpp | 2 +- src/src/PluginStructs/P162_data_struct.h | 2 +- 27 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 src/src/Globals/SPI1.cpp create mode 100644 src/src/Globals/SPIe.cpp rename src/src/Globals/{SPI1.h => SPIe.h} (75%) diff --git a/src/src/Globals/SPI1.cpp b/src/src/Globals/SPI1.cpp deleted file mode 100644 index 87ad5b001..000000000 --- a/src/src/Globals/SPI1.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "../Globals/SPI1.h" - -#ifdef ESP32 -SPIClass SPI1(HSPI); -#endif // ifdef ESP32 diff --git a/src/src/Globals/SPIe.cpp b/src/src/Globals/SPIe.cpp new file mode 100644 index 000000000..d17238b37 --- /dev/null +++ b/src/src/Globals/SPIe.cpp @@ -0,0 +1,5 @@ +#include "../Globals/SPIe.h" + +#ifdef ESP32 +SPIClass SPIe(HSPI); +#endif // ifdef ESP32 diff --git a/src/src/Globals/SPI1.h b/src/src/Globals/SPIe.h similarity index 75% rename from src/src/Globals/SPI1.h rename to src/src/Globals/SPIe.h index 92eef0afd..567768d74 100644 --- a/src/src/Globals/SPI1.h +++ b/src/src/Globals/SPIe.h @@ -2,5 +2,5 @@ #ifdef ESP32 # include -extern SPIClass SPI1; +extern SPIClass SPIe; #endif // ifdef ESP32 diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 7ea8364bb..954e55d03 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -108,7 +108,7 @@ #include -#include "../Globals/SPI1.h" +#include "../Globals/SPIe.h" # define GPIO_PLUGIN_ID 1 @@ -245,11 +245,11 @@ void hardwareInit() SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration SPI_initialized |= 1; } - // Init second SPI interface (SPI1) + // Init second SPI interface (SPIe) const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); if (Settings.getSPI_pins(spi_gpios, 1u)) { - SPI1.setHwCs(false); - SPI1.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + SPIe.setHwCs(false); + SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); SPI_initialized |= 2; } @@ -278,7 +278,7 @@ void hardwareInit() { #ifdef ESP32 const uint8_t sdspi = Settings.getSPIBusForSDCard(); - if (SD.begin(Settings.Pin_sd_cs, 0 == sdspi ? SPI : SPI1)) + if (SD.begin(Settings.Pin_sd_cs, 0 == sdspi ? SPI : SPIe)) #endif // ifdef ESP32 #ifdef ESP8266 if (SD.begin(Settings.Pin_sd_cs)) diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index b0fb6a47c..c7dda7ca5 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -5,7 +5,7 @@ P039_data_struct::P039_data_struct(struct EventStruct*event) { # ifdef ESP32 const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); - _spi = 0 == spi_bus ? SPI : SPI1; + _spi = 0 == spi_bus ? SPI : SPIe; # endif // ifdef ESP32 } diff --git a/src/src/PluginStructs/P039_data_struct.h b/src/src/PluginStructs/P039_data_struct.h index dda4fda3e..836cb7ba6 100644 --- a/src/src/PluginStructs/P039_data_struct.h +++ b/src/src/PluginStructs/P039_data_struct.h @@ -7,7 +7,7 @@ # include -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # define MAX31865_RD_ADDRESS(n) (MAX31865_READ_ADDR_BASE + (n)) # define MAX31865_WR_ADDRESS(n) (MAX31865_WRITE_ADDR_BASE + (n)) diff --git a/src/src/PluginStructs/P095_data_struct.cpp b/src/src/PluginStructs/P095_data_struct.cpp index 954387be5..5e65da9bb 100644 --- a/src/src/PluginStructs/P095_data_struct.cpp +++ b/src/src/PluginStructs/P095_data_struct.cpp @@ -167,7 +167,7 @@ bool P095_data_struct::plugin_init(struct EventStruct *event) { # endif // if P095_ENABLE_ILI948X { # ifdef ESP32 // PIN(0) and PIN(1) swapped! - tft = new (std::nothrow) Adafruit_ILI9341(0 == _spi_bus ? &SPI : &SPI1, + tft = new (std::nothrow) Adafruit_ILI9341(0 == _spi_bus ? &SPI : &SPIe, PIN(1), PIN(0), PIN(2), diff --git a/src/src/PluginStructs/P095_data_struct.h b/src/src/PluginStructs/P095_data_struct.h index 04930d989..23b575a61 100644 --- a/src/src/PluginStructs/P095_data_struct.h +++ b/src/src/PluginStructs/P095_data_struct.h @@ -7,7 +7,7 @@ # include // include Adafruit graphics library # include // include Adafruit ILI9341 TFT library -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" diff --git a/src/src/PluginStructs/P096_data_struct.cpp b/src/src/PluginStructs/P096_data_struct.cpp index 783ee4655..1be9706ad 100644 --- a/src/src/PluginStructs/P096_data_struct.cpp +++ b/src/src/PluginStructs/P096_data_struct.cpp @@ -127,21 +127,21 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { case EPD_type_e::EPD_IL3897: eInkScreen = new (std::nothrow) LOLIN_IL3897(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); // HSPI break; case EPD_type_e::EPD_UC8151D: eInkScreen = new (std::nothrow) LOLIN_UC8151D(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); // HSPI break; case EPD_type_e::EPD_SSD1680: eInkScreen = new (std::nothrow) LOLIN_SSD1680(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); // HSPI break; @@ -149,7 +149,7 @@ bool P096_data_struct::plugin_init(struct EventStruct *event) { case EPD_type_e::EPD_WS2IN7: eInkScreen = new (std::nothrow) Waveshare_2in7(_xpix, _ypix, PIN(1), PIN(2), PIN(0), PIN(3) # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); // HSPI break; diff --git a/src/src/PluginStructs/P096_data_struct.h b/src/src/PluginStructs/P096_data_struct.h index 4cc60158e..3150a3ae4 100644 --- a/src/src/PluginStructs/P096_data_struct.h +++ b/src/src/PluginStructs/P096_data_struct.h @@ -4,7 +4,7 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P096 -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # include // include Adafruit graphics library # include // include Adafruit Lolin eInk/ePaper library diff --git a/src/src/PluginStructs/P099_data_struct.cpp b/src/src/PluginStructs/P099_data_struct.cpp index a91d5e35c..867dce600 100644 --- a/src/src/PluginStructs/P099_data_struct.cpp +++ b/src/src/PluginStructs/P099_data_struct.cpp @@ -59,7 +59,7 @@ bool P099_data_struct::init(taskIndex_t taskIndex, touchscreen = new (std::nothrow) XPT2046_Touchscreen(_address_ts_cs # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); diff --git a/src/src/PluginStructs/P099_data_struct.h b/src/src/PluginStructs/P099_data_struct.h index cbe2d8cb1..abb73cb8c 100644 --- a/src/src/PluginStructs/P099_data_struct.h +++ b/src/src/PluginStructs/P099_data_struct.h @@ -8,7 +8,7 @@ # include -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" // #define PLUGIN_099_DEBUG // Additional debugging information diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 60df7d099..c7ee59160 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -38,7 +38,7 @@ P104_data_struct::P104_data_struct(MD_MAX72XX::moduleType_t _mod, if (Settings.isSPI_valid(spi_bus)) { P = new (std::nothrow) MD_Parola(mod, # ifdef ESP32 - 0 == spi_bus ? SPI : SPI1, + 0 == spi_bus ? SPI : SPIe, # endif // ifdef ESP32 cs_pin, modules); } else { diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index cd56f6621..e05de8eb5 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -16,7 +16,7 @@ # include "../Helpers/Misc.h" # include "../Helpers/StringParser.h" -#include "../Globals/SPI1.h" +#include "../Globals/SPIe.h" # include diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index af2bb9bbc..fb1a3726d 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -32,7 +32,7 @@ void P111_data_struct::init() { // Instantiate a MFRC522 mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 + , 0 == _spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); diff --git a/src/src/PluginStructs/P111_data_struct.h b/src/src/PluginStructs/P111_data_struct.h index b8c1a22d4..f9f01c767 100644 --- a/src/src/PluginStructs/P111_data_struct.h +++ b/src/src/PluginStructs/P111_data_struct.h @@ -6,7 +6,7 @@ # include -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # define P111_CS_PIN PIN(0) # define P111_RST_PIN PIN(1) diff --git a/src/src/PluginStructs/P116_data_struct.cpp b/src/src/PluginStructs/P116_data_struct.cpp index f38ba8615..82241209f 100644 --- a/src/src/PluginStructs/P116_data_struct.cpp +++ b/src/src/PluginStructs/P116_data_struct.cpp @@ -211,7 +211,7 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { } # ifdef ESP32 - st7735 = new (std::nothrow) Adafruit_ST7735(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + st7735 = new (std::nothrow) Adafruit_ST7735(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); # endif // ifdef ESP32 # ifdef ESP8266 st7735 = new (std::nothrow) Adafruit_ST7735(PIN(0), PIN(1), PIN(2)); @@ -234,7 +234,7 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { # endif // if P116_EXTRA_ST7789 { # ifdef ESP32 - st7789 = new (std::nothrow) Adafruit_ST7789(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + st7789 = new (std::nothrow) Adafruit_ST7789(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); # endif // ifdef ESP32 # ifdef ESP8266 st7789 = new (std::nothrow) Adafruit_ST7789(PIN(0), PIN(1), PIN(2)); @@ -261,7 +261,7 @@ bool P116_data_struct::plugin_init(struct EventStruct *event) { case ST77xx_type_e::ST7796s_320x480: { # ifdef ESP32 - st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(0 == _spi_bus ? &SPI : &SPI1, PIN(0), PIN(1), PIN(2)); + st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(0 == _spi_bus ? &SPI : &SPIe, PIN(0), PIN(1), PIN(2)); # endif // ifdef ESP32 # ifdef ESP8266 st7796 = new (std::nothrow) Adafruit_ST7796S_kbv(PIN(0), PIN(1), PIN(2)); diff --git a/src/src/PluginStructs/P116_data_struct.h b/src/src/PluginStructs/P116_data_struct.h index 009f053e9..c99bef536 100644 --- a/src/src/PluginStructs/P116_data_struct.h +++ b/src/src/PluginStructs/P116_data_struct.h @@ -32,7 +32,7 @@ # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object # include "../CustomBuild/StorageLayout.h" -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # define P116_Nlines 24 // The number of different lines which can be displayed # define P116_Nchars 60 diff --git a/src/src/PluginStructs/P118_data_struct.cpp b/src/src/PluginStructs/P118_data_struct.cpp index fe4815813..6b84bc934 100644 --- a/src/src/PluginStructs/P118_data_struct.cpp +++ b/src/src/PluginStructs/P118_data_struct.cpp @@ -35,7 +35,7 @@ bool P118_data_struct::plugin_init(struct EventStruct *event) { startInit = millis(); _rf = new (std::nothrow) IthoCC1101(_csPin, spi_pins[1] # ifdef ESP32 - , 0 == _spi_bus ? SPI : SPI1 // defaults and SPI bus for ESP32 only + , 0 == _spi_bus ? SPI : SPIe // defaults and SPI bus for ESP32 only # endif // ifdef ESP32 ); // Pass CS and MISO } else { diff --git a/src/src/PluginStructs/P118_data_struct.h b/src/src/PluginStructs/P118_data_struct.h index 3d4798ba6..c06ca7003 100644 --- a/src/src/PluginStructs/P118_data_struct.h +++ b/src/src/PluginStructs/P118_data_struct.h @@ -9,7 +9,7 @@ # include "IthoCC1101.h" # include "IthoPacket.h" -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # define P118_DEBUG_LOG // Enable for some (extra) logging # ifndef P118_FEATURE_ORCON diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index c5d525e3a..ff8c79bcb 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -4,7 +4,7 @@ # define P120_RAD_TO_DEG 57.295779f // 180.0/M_PI -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" P120_data_struct::P120_data_struct(uint8_t aSize) : _aSize(aSize) @@ -241,7 +241,7 @@ bool P120_data_struct::init_sensor(struct EventStruct *event) { # endif // ifdef ESP32 adxl345 = new (std::nothrow) ADXL345(_cs_pin # ifdef ESP32 - , 0 == spi_bus ? SPI : SPI1 + , 0 == spi_bus ? SPI : SPIe # endif // ifdef ESP32 ); // Init using SPI } diff --git a/src/src/PluginStructs/P141_data_struct.cpp b/src/src/PluginStructs/P141_data_struct.cpp index 687ccbb7b..71f1300b7 100644 --- a/src/src/PluginStructs/P141_data_struct.cpp +++ b/src/src/PluginStructs/P141_data_struct.cpp @@ -69,7 +69,7 @@ bool P141_data_struct::plugin_init(struct EventStruct *event) { pcd8544 = new (std::nothrow) Adafruit_PCD8544(P141_DC_PIN, P141_CS_PIN, P141_RST_PIN # ifdef ESP32 - , 0 == _spi_bus ? &SPI : &SPI1 + , 0 == _spi_bus ? &SPI : &SPIe # endif // ifdef ESP32 ); # ifndef BUILD_NO_DEBUG diff --git a/src/src/PluginStructs/P141_data_struct.h b/src/src/PluginStructs/P141_data_struct.h index 0db63c379..abfbd6ff6 100644 --- a/src/src/PluginStructs/P141_data_struct.h +++ b/src/src/PluginStructs/P141_data_struct.h @@ -7,7 +7,7 @@ # include // include Adafruit graphics library # include // include Adafruit PCD8544 LCD library -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object diff --git a/src/src/PluginStructs/P154_data_struct.cpp b/src/src/PluginStructs/P154_data_struct.cpp index ad737d42b..c64871f48 100644 --- a/src/src/PluginStructs/P154_data_struct.cpp +++ b/src/src/PluginStructs/P154_data_struct.cpp @@ -26,7 +26,7 @@ bool P154_data_struct::begin(bool _i2cMode) if (!i2cMode && !bmp.begin_SPI(csPin # ifdef ESP32 - , 0 == _spi_bus ? &SPI : &SPI1 + , 0 == _spi_bus ? &SPI : &SPIe # endif // ifdef ESP32 )) { return false; diff --git a/src/src/PluginStructs/P154_data_struct.h b/src/src/PluginStructs/P154_data_struct.h index 907255c76..375372e3b 100644 --- a/src/src/PluginStructs/P154_data_struct.h +++ b/src/src/PluginStructs/P154_data_struct.h @@ -7,7 +7,7 @@ # include # include -#include "../Globals/SPI1.h" +#include "../Globals/SPIe.h" # define P154_I2C_ADDR PCONFIG(0) diff --git a/src/src/PluginStructs/P162_data_struct.cpp b/src/src/PluginStructs/P162_data_struct.cpp index e87ed5e6f..65dc96280 100644 --- a/src/src/PluginStructs/P162_data_struct.cpp +++ b/src/src/PluginStructs/P162_data_struct.cpp @@ -16,7 +16,7 @@ P162_data_struct::P162_data_struct(int8_t csPin, : _csPin(csPin), _rstPin(rstPin), _shdPin(shdPin), _spi_bus(spi_bus) { # ifdef ESP32 - _spi = 0 == _spi_bus ? SPI : SPI1; + _spi = 0 == _spi_bus ? SPI : SPIe; # endif // ifdef ESP32 } diff --git a/src/src/PluginStructs/P162_data_struct.h b/src/src/PluginStructs/P162_data_struct.h index 5baee59f4..b6fdadab6 100644 --- a/src/src/PluginStructs/P162_data_struct.h +++ b/src/src/PluginStructs/P162_data_struct.h @@ -4,7 +4,7 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P162 -# include "../Globals/SPI1.h" +# include "../Globals/SPIe.h" # define P162_CS_PIN PIN(0) # define P162_RST_PIN PIN(1) From 9a052d2efa1bb83c575b0836799e0edd6ad54221 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 25 Aug 2025 22:26:16 +0200 Subject: [PATCH 021/161] [SPI] Code improvements, separate SPI functions, re-init SPI when saving Hardware page --- src/src/Commands/SDCARD.cpp | 33 ++-- src/src/DataStructs/SettingsStruct.h | 1 + src/src/DataStructs_templ/SettingsStruct.cpp | 6 + src/src/Helpers/Hardware.cpp | 155 ++----------------- src/src/Helpers/Hardware.h | 19 +-- src/src/Helpers/Hardware_SPI.cpp | 137 ++++++++++++++++ src/src/Helpers/Hardware_SPI.h | 10 ++ src/src/WebServer/HardwarePage.cpp | 7 +- 8 files changed, 190 insertions(+), 178 deletions(-) create mode 100644 src/src/Helpers/Hardware_SPI.cpp create mode 100644 src/src/Helpers/Hardware_SPI.h diff --git a/src/src/Commands/SDCARD.cpp b/src/src/Commands/SDCARD.cpp index 1a95297af..dc37ae81b 100644 --- a/src/src/Commands/SDCARD.cpp +++ b/src/src/Commands/SDCARD.cpp @@ -6,11 +6,12 @@ #if FEATURE_SD -#include "../ESPEasyCore/Serial.h" -#include "../Globals/Settings.h" -#include "../Helpers/StringConverter.h" +# include "../ESPEasyCore/Serial.h" +# include "../Globals/Settings.h" +# include "../Helpers/Hardware_SPI.h" +# include "../Helpers/StringConverter.h" -#include +# include void printDirectory(fs::File dir, int numTabs) @@ -40,22 +41,26 @@ void printDirectory(fs::File dir, int numTabs) } } - -const __FlashStringHelper * Command_SD_LS(struct EventStruct *event, const char* Line) +const __FlashStringHelper* Command_SD_LS(struct EventStruct *event, const char*Line) { - fs::File root = SD.open("/"); - root.rewindDirectory(); - printDirectory(root, 0); - root.close(); - return return_see_serial(event); + if (initSDcard()) { + fs::File root = SD.open("/"); + root.rewindDirectory(); + printDirectory(root, 0); + root.close(); + return return_see_serial(event); + } + return return_command_failed_flashstr(); } -String Command_SD_Remove(struct EventStruct *event, const char* Line) +String Command_SD_Remove(struct EventStruct *event, const char*Line) { // FIXME TD-er: This one is not using parseString* function String fname = Line; + fname = fname.substring(9); - SD.remove((char*)fname.c_str()); + SD.remove((char *)fname.c_str()); return return_result(event, concat(F("Removing:"), fname)); } -#endif + +#endif // if FEATURE_SD diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 0170e9d86..bb1ba04de 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -318,6 +318,7 @@ public: uint8_t spi_bus = 0, bool noCheck = false) const; + bool isSPI_enabled(uint8_t spi_bus) const; #ifdef ESP32 spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const; #endif diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 4a94a494d..3ad926d47 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -895,6 +895,12 @@ void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState # endif // ifdef ESP32 } +template +bool SettingsStruct_tmpl::isSPI_enabled(uint8_t spi_bus) const { + const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); + return SPI_Options_e::None != SPI_selection; +} + template bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const { spi_gpios[0] = -1; diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index 954e55d03..ab3c949a0 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -19,6 +19,7 @@ #include "../Helpers/Hardware_device_info.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware_SPI.h" #include "../Helpers/I2C_access.h" #include "../Helpers/Misc.h" #include "../Helpers/PortStatus.h" @@ -34,8 +35,6 @@ # include #endif // if defined(ESP32) -// #include "../../ESPEasy-Globals.h" - #ifdef ESP32 # include # include @@ -43,45 +42,14 @@ # include # include - # if ESP_IDF_VERSION_MAJOR == 4 - # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 - # include - # include - # include - # elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 - # include - # include - # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # include - # include - # include - # else // if CONFIG_IDF_TARGET_ESP32S3 - # error Target CONFIG_IDF_TARGET is not supported - # endif // if CONFIG_IDF_TARGET_ESP32S3 - # else // ESP32 IDF 5.x and later - # include - # include - # include - # endif // if ESP_IDF_VERSION_MAJOR == 4 + // ESP32 IDF 5.x and later + # include + # include + # include -#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 - # if ESP_IDF_VERSION_MAJOR < 5 - # define HAS_HALL_EFFECT_SENSOR 1 - # else // if ESP_IDF_VERSION_MAJOR < 5 + // Support for Hall Effect sensor was removed in ESP_IDF 5.x + # define HAS_HALL_EFFECT_SENSOR 0 -// Support for Hall Effect sensor was removed in ESP_IDF 5.x - # define HAS_HALL_EFFECT_SENSOR 0 - # endif // if ESP_IDF_VERSION_MAJOR < 5 -# else - # define HAS_HALL_EFFECT_SENSOR 0 -# endif - - -# if ESP_IDF_VERSION_MAJOR >= 5 # include # include @@ -90,29 +58,11 @@ // #include -# endif // if ESP_IDF_VERSION_MAJOR >= 5 - # include "../Helpers/Hardware_ADC_cali.h" -#if FEATURE_ETHERNET -#include -#endif - #endif // ifdef ESP32 -#if FEATURE_SD -# include -#endif // if FEATURE_SD - - -#include - -#include "../Globals/SPIe.h" - - -# define GPIO_PLUGIN_ID 1 - /********************************************************************************************\ * Initialize specific hardware settings (only global ones, others are set through devices) \*********************************************************************************************/ @@ -207,6 +157,7 @@ void hardwareInit() } } + // Initialize I2C buses initI2C(); #if FEATURE_PLUGIN_PRIORITY @@ -214,96 +165,10 @@ void hardwareInit() PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); #endif // if FEATURE_PLUGIN_PRIORITY - bool tryInitSPI = true; -#if FEATURE_ETHERNET - if ((Settings.NetworkMedium == NetworkMedium_t::Ethernet) && - isValid(Settings.ETH_Phy_Type) && - isSPI_EthernetType(Settings.ETH_Phy_Type)) - { -#if !ETH_SPI_SUPPORTS_CUSTOM - tryInitSPI = false; -#endif - } -#endif - - - // SPI Init - uint8_t SPI_initialized = 0; - if (tryInitSPI && (Settings.isSPI_valid(0u) - #ifdef ESP32 - || Settings.isSPI_valid(1u) - #endif // ifdef ESP32 - )) - { - #ifdef ESP32 - - const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); - int8_t spi_gpios[3] = {}; - - if (Settings.getSPI_pins(spi_gpios, 0u)) { - SPI.setHwCs(false); - SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration - SPI_initialized |= 1; - } - // Init second SPI interface (SPIe) - const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); - if (Settings.getSPI_pins(spi_gpios, 1u)) { - SPIe.setHwCs(false); - SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); - SPI_initialized |= 2; - } - - #else // ifdef ESP32 - SPI.setHwCs(false); - SPI.begin(); // Use default GPIO configuration - SPI_initialized = 1; - #endif // ifdef ESP32 - } - - if (SPI_initialized) - { - #ifdef ESP32 - for (uint8_t i = 1; i < 3; ++i) { - if (SPI_initialized & i) { - addLog(LOG_LEVEL_INFO, strformat(F("INIT : SPI Bus %d Init (without CS)"), i - 1)); - } - } - #endif // ifdef ESP32 - #ifdef ESP8266 - addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); - #endif // ifdef ESP8266 - #if FEATURE_SD - - if (Settings.Pin_sd_cs >= 0) - { - #ifdef ESP32 - const uint8_t sdspi = Settings.getSPIBusForSDCard(); - if (SD.begin(Settings.Pin_sd_cs, 0 == sdspi ? SPI : SPIe)) - #endif // ifdef ESP32 - #ifdef ESP8266 - if (SD.begin(Settings.Pin_sd_cs)) - #endif // ifdef ESP8266 - { - #ifdef ESP32 - addLog(LOG_LEVEL_INFO, strformat(F("SD : Init on SPI Bus %d OK"), sdspi)); - #endif // ifdef ESP32 - #ifdef ESP8266 - addLog(LOG_LEVEL_INFO, F("SD : Init OK")); - #endif // ifdef ESP8266 - } - else - { - SD.end(); - addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); - } - } -#endif // if FEATURE_SD - } else { - addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); - } + // Initialize SPI buses, also initializes SD-card when available in build and configured + initializeSPIBuses(); } - void checkResetFactoryPin() { static uint8_t factoryResetCounter = 0; diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index dc0f21bb4..c0fbc497b 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -57,23 +57,8 @@ int espeasy_analogRead(int pin, 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 +// if ESP_IDF_VERSION_MAJOR < 5 +# define ESP_PM_CONFIG_T esp_pm_config_t #endif // ifdef ESP32 diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp new file mode 100644 index 000000000..fb87e2a0a --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -0,0 +1,137 @@ + +#include "../Helpers/Hardware_SPI.h" +#include "../Globals/Settings.h" + +#if FEATURE_SD +# include +#endif // if FEATURE_SD + +#include + +#include "../Globals/SPIe.h" + +#if FEATURE_ETHERNET +# include +#endif // if FEATURE_ETHERNET + +void initializeSPIBuses() { + // SPI Init + uint8_t SPI_initialized = 0; + + #ifdef ESP32 + uint8_t skipInitSPI = 0; + #endif // ifdef ESP32 + + #if FEATURE_ETHERNET + + if ((Settings.NetworkMedium == NetworkMedium_t::Ethernet) && + isValid(Settings.ETH_Phy_Type) && + isSPI_EthernetType(Settings.ETH_Phy_Type)) + { + # if !ETH_SPI_SUPPORTS_CUSTOM + skipInitSPI = Settings.getSPIBusForEth() + 1; // Increment by 1 + # endif // if !ETH_SPI_SUPPORTS_CUSTOM + } + #endif // if FEATURE_ETHERNET + + + if (Settings.isSPI_valid(0u) + #ifdef ESP32 + || Settings.isSPI_valid(1u) + #endif // ifdef ESP32 + ) + { + #ifdef ESP32 + + const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); + int8_t spi_gpios[3] = {}; + + if ((skipInitSPI != 1) && Settings.getSPI_pins(spi_gpios, 0u)) { + SPI.end(); // Disconnect current GPIO mapping + SPI.setHwCs(false); + SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration + SPI_initialized |= 1; + } + + delay(1); + + // Init second SPI interface (SPIe) + const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); + + if ((skipInitSPI != 2) && Settings.getSPI_pins(spi_gpios, 1u)) { + SPIe.end(); // Disconnect current GPIO mapping + SPIe.setHwCs(false); + SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); + SPI_initialized |= 2; + } + + #else // ifdef ESP32 + SPI.setHwCs(false); + SPI.begin(); // Use default GPIO configuration + SPI_initialized = 1; + #endif // ifdef ESP32 + } + + if (SPI_initialized) + { + #ifdef ESP32 + + for (uint8_t i = 1; i < 3; ++i) { + if (SPI_initialized & i) { + addLog(LOG_LEVEL_INFO, strformat(F("INIT : SPI Bus %d Init (without CS)"), i - 1)); + } + } + #endif // ifdef ESP32 + #ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("INIT : SPI Init (without CS)")); + #endif // ifdef ESP8266 + + #if FEATURE_SD + initSDcard(); + #endif // if FEATURE_SD + } else { + addLog(LOG_LEVEL_INFO, F("INIT : SPI not enabled")); + } +} + +#if FEATURE_SD +bool initSDcard() { + bool result = false; + + if (Settings.Pin_sd_cs >= 0) + { + # ifdef ESP32 + const uint8_t sdspi = Settings.getSPIBusForSDCard(); + # else // ifdef ESP32 + constexpr uint8_t sdspi{}; + # endif // ifdef ESP32 + + if (Settings.isSPI_enabled(sdspi)) { + # ifdef ESP32 + + if (SD.begin(Settings.Pin_sd_cs, (0 == sdspi) ? SPI : SPIe)) + # endif // ifdef ESP32 + # ifdef ESP8266 + + if (SD.begin(Settings.Pin_sd_cs)) + # endif // ifdef ESP8266 + { + # ifdef ESP32 + addLog(LOG_LEVEL_INFO, strformat(F("SD : Init on SPI Bus %d OK"), sdspi)); + # endif // ifdef ESP32 + # ifdef ESP8266 + addLog(LOG_LEVEL_INFO, F("SD : Init OK")); + # endif // ifdef ESP8266 + result = true; + } + else + { + SD.end(); + addLog(LOG_LEVEL_ERROR, F("SD : Init failed")); + } + } + } + return result; +} + +#endif // if FEATURE_SD diff --git a/src/src/Helpers/Hardware_SPI.h b/src/src/Helpers/Hardware_SPI.h new file mode 100644 index 000000000..c104c9f0d --- /dev/null +++ b/src/src/Helpers/Hardware_SPI.h @@ -0,0 +1,10 @@ +#pragma once + +#include "../../ESPEasy_common.h" + + +void initializeSPIBuses(); + +#if FEATURE_SD +bool initSDcard(); +#endif // if FEATURE_SD diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 20e84d308..e01579d99 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -17,6 +17,7 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" #include "../Helpers/Hardware_I2C.h" +#include "../Helpers/Hardware_SPI.h" #include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -121,7 +122,7 @@ void handle_hardware() { Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { - if (!Settings.isSPI_valid(spi_bus)) { // Checks + if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks error += strformat(F("SPI bus %u pins not configured correctly!
"), spi_bus); } } @@ -163,6 +164,8 @@ void handle_hardware() { if (error.isEmpty()) { // Apply I2C settings. initI2C(); + // Apply SPI settings + initializeSPIBuses(); } } @@ -332,7 +335,7 @@ void handle_hardware() { addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script - addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + // addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); addFormNote(F("Chip Select (CS) config must be done in the plugin")); } } From a84d1d068225785f8018e59696df71d30bd03392 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 25 Aug 2025 22:26:53 +0200 Subject: [PATCH 022/161] [P039] Fix compiler warning (nested comments) --- src/src/PluginStructs/P039_data_struct.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/src/PluginStructs/P039_data_struct.cpp b/src/src/PluginStructs/P039_data_struct.cpp index c7dda7ca5..5ddee65f9 100644 --- a/src/src/PluginStructs/P039_data_struct.cpp +++ b/src/src/PluginStructs/P039_data_struct.cpp @@ -1182,7 +1182,7 @@ uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xs TODO: c.k.i.: make it generic and carve out to generic _SPI_helper.c library - /**************************************************************************/ + **************************************************************************/ /**************************************************************************/ @@ -1196,7 +1196,7 @@ uint16_t P039_data_struct::readLM7xRegisters(int8_t l_CS_pin_no, uint8_t l_LM7xs Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ int P039_data_struct::get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin is in Config we use 15 as default -> Hardware Chip Select on // ESP8266 if (CONFIG_PIN1 != -1) { @@ -1215,7 +1215,7 @@ int P039_data_struct::get_SPI_CS_Pin(struct EventStruct *event) { // If no Pin i Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::init_SPI_CS_Pin(int8_t l_CS_pin_no) { // set the slaveSelectPin as an output: pinMode(l_CS_pin_no, OUTPUT); @@ -1232,7 +1232,7 @@ void P039_data_struct::init_SPI_CS_Pin(int8_t l_CS_pin_no) { Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { P039_CS_Delay(); // tCWH (min) >= x00ns digitalWrite(l_CS_pin_no, l_state); @@ -1251,7 +1251,7 @@ void P039_data_struct::handle_SPI_CS_Pin(int8_t l_CS_pin_no, bool l_state) { Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint8_t value) { uint8_t l_messageBuffer[2] = { l_address, value }; @@ -1282,7 +1282,7 @@ void P039_data_struct::write8BitRegister(int8_t l_CS_pin_no, uint8_t l_address, Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, uint16_t value) { uint8_t l_messageBuffer[3] = { l_address, static_cast((value >> 8) & 0xFF), static_cast(value & 0xFF) }; @@ -1312,7 +1312,7 @@ void P039_data_struct::write16BitRegister(int8_t l_CS_pin_no, uint8_t l_address, Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ uint8_t P039_data_struct::read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address) { uint8_t l_messageBuffer[2] = { l_address, 0x00 }; @@ -1344,7 +1344,7 @@ uint8_t P039_data_struct::read8BitRegister(int8_t l_CS_pin_no, uint8_t l_address Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ uint16_t P039_data_struct::read16BitRegister(int8_t l_CS_pin_no, uint8_t l_address) { uint8_t l_messageBuffer[3] = { l_address, 0x00, 0x00 }; @@ -1380,7 +1380,7 @@ uint16_t P039_data_struct::read16BitRegister(int8_t l_CS_pin_no, uint8_t l_addre Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesToSend, uint8_t *l_inoutMessageBuffer) { // activate communication -> CS low @@ -1430,7 +1430,7 @@ void P039_data_struct::transfer_n_ByteSPI(int8_t l_CS_pin_no, uint8_t l_noBytesT Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::change16BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, @@ -1469,7 +1469,7 @@ void P039_data_struct::change16BitRegister(int8_t l_CS_pin_no, Initial Revision - chri.kai.in 2021 - /**************************************************************************/ + **************************************************************************/ void P039_data_struct::change8BitRegister(int8_t l_CS_pin_no, uint8_t l_readaddress, uint8_t l_writeaddress, From 21fb34049da9ed981fed43aefc0befa20034d2b9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 29 Aug 2025 13:46:23 +0200 Subject: [PATCH 023/161] [SPI] Disconnect SPI GPIO pins also when not using them --- src/src/Helpers/Hardware_SPI.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp index fb87e2a0a..c61278453 100644 --- a/src/src/Helpers/Hardware_SPI.cpp +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -46,20 +46,26 @@ void initializeSPIBuses() { const SPI_Options_e SPI_selection = static_cast(Settings.InitSPI); int8_t spi_gpios[3] = {}; + if (skipInitSPI != 1) { + SPI.end(); // Disconnect current GPIO mapping + } + if ((skipInitSPI != 1) && Settings.getSPI_pins(spi_gpios, 0u)) { - SPI.end(); // Disconnect current GPIO mapping SPI.setHwCs(false); SPI.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); // Use explicit GPIO configuration SPI_initialized |= 1; } delay(1); - + // Init second SPI interface (SPIe) const SPI_Options_e SPI1_selection = static_cast(Settings.InitSPI1); - if ((skipInitSPI != 2) && Settings.getSPI_pins(spi_gpios, 1u)) { + if (skipInitSPI != 2) { SPIe.end(); // Disconnect current GPIO mapping + } + + if ((skipInitSPI != 2) && Settings.getSPI_pins(spi_gpios, 1u)) { SPIe.setHwCs(false); SPIe.begin(spi_gpios[0], spi_gpios[1], spi_gpios[2]); SPI_initialized |= 2; From 87a449351c1a3efe3240af2f1346d3b16db15d60 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 29 Aug 2025 13:47:30 +0200 Subject: [PATCH 024/161] [P116] Fix GPIO display on Devices page, no default GPIO pins for ESP32 --- src/_P116_ST77xx.ino | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index 12e7458bd..62b04c0d8 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -8,6 +8,7 @@ /** History: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2025-06-11 tonhuisman: Add support for ST7789v3/ST7735 display with 170x320 resolution * 2025-02-20 tonhuisman: Add support for ST7735 display with 172x320 resolution @@ -87,11 +88,13 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) break; } + # ifndef LIMIT_BUILD_SIZE case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: { const char *separator = event->String1.c_str(); // contains the NewLine sequence string = strformat( - F("CS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + separator, formatGpioLabel(PIN(0), false).c_str(), separator, formatGpioLabel(PIN(1), false).c_str(), @@ -104,21 +107,15 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) success = true; break; } + # endif // ifndef LIMIT_BUILD_SIZE case PLUGIN_SET_DEFAULTS: { - # ifdef ESP32 - - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P116_TFT_CS_HSPI; - } else { - PIN(0) = P116_TFT_CS; - } - # else // ifdef ESP32 + # ifdef ESP8266 PIN(0) = P116_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P116_TFT_DC; - PIN(2) = P116_TFT_RST; + PIN(1) = P116_TFT_DC; + PIN(2) = P116_TFT_RST; + # endif // ifdef ESP8266 P116_CONFIG_BUTTON_PIN = -1; // No button connected P116_CONFIG_BACKLIGHT_PIN = P116_BACKLIGHT_PIN; P116_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight From 749f5e2eb9036941240131af468135bc0230a14d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 29 Aug 2025 13:47:59 +0200 Subject: [PATCH 025/161] [P095] Fix GPIO display on Devices page, fix typos, no default GPIO pins for ESP32 --- src/_P095_ILI9341.ino | 40 +++++++++++++++++++++++++------------- src/src/Helpers/Hardware.h | 2 +- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 4e01d2018..aaf50e95b 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -10,11 +10,11 @@ # define PLUGIN_NAME_095 "Display - TFT ILI934x/ILI948x" # define PLUGIN_VALUENAME1_095 "CursorX" # define PLUGIN_VALUENAME2_095 "CursorY" -# define PLUGIN_095_MAX_DISPLAY 1 /** * Changelog: + * 2025-08-29 tonhuisman: Fix GPIO pin display on Devices page, no default GPIO pins for ESP32 * 2025-08-12 tonhuisman: Enable use of secondary SPI bus * 2024-07-07 tonhuisman: Remove explicit support for ILI9486 as all ILI9486 displays tested so far work with the ILI9488 driver, * sometimes with Invert display setting enabled (or they are actually ILI9488 displays...) @@ -181,20 +181,34 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) break; } + # ifndef LIMIT_BUILD_SIZE + case PLUGIN_WEBFORM_SHOW_GPIO_DESCR: + { + const char *separator = event->String1.c_str(); // contains the NewLine sequence + string = strformat( + F("%sCS: %s%sDC: %s%s RES: %s%sBtn: %s%sBckl: : %s"), + separator, + formatGpioLabel(PIN(0), false).c_str(), + separator, + formatGpioLabel(PIN(1), false).c_str(), + separator, + formatGpioLabel(PIN(2), false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BUTTON_PIN, false).c_str(), + separator, + formatGpioLabel(P095_CONFIG_BACKLIGHT_PIN, false).c_str()); + success = true; + break; + } + # endif // ifndef LIMIT_BUILD_SIZE + case PLUGIN_SET_DEFAULTS: { - # ifdef ESP32 - - if (Settings.InitSPI == 2) { // When using ESP32 H(ardware-)SPI - PIN(0) = P095_TFT_CS_HSPI; - } else { - PIN(0) = P095_TFT_CS; - } - # else // ifdef ESP32 + # ifdef ESP8266 PIN(0) = P095_TFT_CS; - # endif // ifdef ESP32 - PIN(1) = P095_TFT_DC; - PIN(2) = P095_TFT_RST; + PIN(1) = P095_TFT_DC; + PIN(2) = P095_TFT_RST; + # endif // ifdef ESP8266 P095_CONFIG_BUTTON_PIN = -1; // No button connected P095_CONFIG_BACKLIGHT_PIN = P095_BACKLIGHT_PIN; P095_CONFIG_BACKLIGHT_PERCENT = 100; // Percentage backlight @@ -455,7 +469,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) { const uint8_t spi_bus = Settings.getSPIBusForTask(event->TaskIndex); - if (Settings.InitSPI != 0) { + if (Settings.isSPI_valid(spi_bus)) { # if P095_ENABLE_ILI948X if (10 == P095_CONFIG_FLAG_GET_TYPE) { // If ILI9486 was selected, reset to ILI9488 diff --git a/src/src/Helpers/Hardware.h b/src/src/Helpers/Hardware.h index c0fbc497b..68d3f406a 100644 --- a/src/src/Helpers/Hardware.h +++ b/src/src/Helpers/Hardware.h @@ -57,7 +57,7 @@ int espeasy_analogRead(int pin, int getCPU_MaxFreqMHz(); int getCPU_MinFreqMHz(); -// if ESP_IDF_VERSION_MAJOR < 5 +// if ESP_IDF_VERSION_MAJOR > 5 # define ESP_PM_CONFIG_T esp_pm_config_t From cb7199a9ec442527f418d5c2a419639bce78f06d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 29 Aug 2025 20:11:54 +0200 Subject: [PATCH 026/161] [SPI] Rename settings variable for better usage indication (review comment) --- src/src/DataStructs/DeviceStruct.h | 4 ++-- src/src/DataStructs/SettingsStruct.h | 2 +- src/src/DataStructs_templ/SettingsStruct.cpp | 20 ++++++++++---------- src/src/Globals/Plugins.cpp | 2 +- src/src/Helpers/Hardware_I2C.cpp | 6 +++--- src/src/WebServer/DevicesPage.cpp | 10 +++++----- src/src/WebServer/JSON.cpp | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index f5300a5a3..3db66f6e6 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -47,9 +47,9 @@ // #define I2C_PERIPHERAL_BUS_??? 9 // bit-offset for I2C bus used for the ??? #endif // if FEATURE_I2C_MULTIPLE -// Stored in Settings.I2C_Flags !!! +// Stored in Settings.I2C_SPI_bus_Flags !!! #define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task -// Stored in Settings.I2C_Flags for Task 1 settings +// Stored in Settings.I2C_SPI_bus_Flags for Task 1 settings #define SPI_FLAGS_ETH_BUS_NUMBER 2 // 2 bit, stores the configured bus for the Ethernet interface #define SPI_FLAGS_SDCARD_BUS_NUMBER 4 // 2 bit, stores the configured bus for the SDCard diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index bb1ba04de..9a1c81da9 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -577,7 +577,7 @@ public: int8_t I2C_Multiplexer_Type = I2C_MULTIPLEXER_NONE; int8_t I2C_Multiplexer_Addr = -1; int8_t I2C_Multiplexer_Channel[N_TASKS]{}; - uint8_t I2C_Flags[N_TASKS] = {0}; + uint8_t I2C_SPI_bus_Flags[N_TASKS] = {0}; uint32_t I2C_clockSpeed_Slow = 100000; int8_t I2C_Multiplexer_ResetPin = -1; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 3ad926d47..7dafb128f 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1110,46 +1110,46 @@ bool SettingsStruct_tmpl::isI2CEnabled(uint8_t i2cBus) const { (getI2CClockSpeedSlow(i2cBus) > 0); } -// stored in I2C_Flags per Task +// stored in I2C_SPI_bus_Flags per Task template uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { - return get2BitFromUL(I2C_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER); + return get2BitFromUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER); } template void SettingsStruct_tmpl::setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus) { - set2BitToUL(I2C_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); + set2BitToUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); } #if FEATURE_ETHERNET -// stored in I2C_Flags for Task 1 (index 0) +// stored in I2C_SPI_bus_Flags for Task 1 (index 0) template uint8_t SettingsStruct_tmpl::getSPIBusForEth() const { - return get2BitFromUL(I2C_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER); + return get2BitFromUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER); } template void SettingsStruct_tmpl::setSPIBusForEth(uint8_t spi_bus) { - set2BitToUL(I2C_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER, spi_bus); + set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER, spi_bus); } #endif // FEATURE_ETHERNET #if FEATURE_SD -// stored in I2C_Flags for Task 1 (index 0) +// stored in I2C_SPI_bus_Flags for Task 1 (index 0) template uint8_t SettingsStruct_tmpl::getSPIBusForSDCard() const { - return get2BitFromUL(I2C_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER); + return get2BitFromUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER); } template void SettingsStruct_tmpl::setSPIBusForSDCard(uint8_t spi_bus) { - set2BitToUL(I2C_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); + set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); } #endif // if FEATURE_SD template uint8_t SettingsStruct_tmpl::getI2CInterface(taskIndex_t TaskIndex) const { - return get3BitFromUL(I2C_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); + return get3BitFromUL(I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); } template diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index d71dfaeee..176cc8bab 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -229,7 +229,7 @@ bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) const uint8_t i2cBus = 0; #endif // if FEATURE_I2C_MULTIPLE - if (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)) { I2CSelectLowClockSpeed(i2cBus); // Set to slow, also switch the bus } else { I2CSelectHighClockSpeed(i2cBus); // Set to normal, also switch the bus diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index 733ecb444..9572cffe8 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -263,7 +263,7 @@ void I2CMultiplexerSelectByTaskIndex(taskIndex_t taskIndex) { const uint8_t i2cBus = 0; # endif // if FEATURE_I2C_MULTIPLE - if (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { uint8_t i = Settings.I2C_Multiplexer_Channel[taskIndex]; if (i > 7) { return; } @@ -322,8 +322,8 @@ bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex) { # endif // if FEATURE_I2C_MULTIPLE if (!isI2CMultiplexerEnabled(i2cBus)) { return false; } - return (!bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) - || (bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); + return (!bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != -1) + || (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); } #endif // if FEATURE_I2CMULTIPLEXER diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index f18bcce25..9e8e5c888 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -159,7 +159,7 @@ void handle_devices() { 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 + bitWrite(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default } } } @@ -324,7 +324,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task # endif // if FEATURE_I2CMULTIPLEXER - Settings.I2C_Flags[taskIndex] = flags; + Settings.I2C_SPI_bus_Flags[taskIndex] = flags; } // Must load from file system to make sure all caches and checksums match. @@ -966,7 +966,7 @@ void format_I2C_port_description(taskIndex_t x) if (isI2CMultiplexerEnabled(i2cBus) && I2CMultiplexerPortSelectedForTask(x)) { String mux; - if (bitRead(Settings.I2C_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel + if (bitRead(Settings.I2C_SPI_bus_Flags[x], I2C_FLAGS_MUX_MULTICHANNEL)) { // Multi-channel mux = F("
Multiplexer channel(s)"); uint8_t b = 0; // For adding lineBreaks @@ -1397,7 +1397,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex String dummy; 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)); + addFormCheckBox(F("Force Slow I2C speed"), F("taskdeviceflags0"), bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED)); if (Device[DeviceIndex].I2CMax100kHz) { addFormNote(F("This device is specified for max. 100 kHz operation!")); @@ -1419,7 +1419,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Show selector for an I2C multiplexer port if a multiplexer is configured if (isI2CMultiplexerEnabled(i2cBus)) { - bool multipleMuxPorts = bitRead(Settings.I2C_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); + bool multipleMuxPorts = bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL); { const __FlashStringHelper *i2c_mux_channels[] = { F("Single channel"), diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 4fe5245ae..ce9a36c21 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -590,7 +590,7 @@ void handle_json() stream_next_json_object_value(F("I2C_Interface"), static_cast(i2cBus + 1)); #endif int8_t channel = Settings.I2C_Multiplexer_Channel[TaskIndex]; - if (bitRead(Settings.I2C_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { addHtml(F("\"I2CBus\" : [")); uint8_t b = 0; for (uint8_t c = 0; c < I2CMultiplexerMaxChannels(i2cBus); ++c) { From e05148a4b83844588ec257c75980c422cef7fc6e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 19 Nov 2025 23:25:34 +0100 Subject: [PATCH 027/161] [Build] Fix missing FEATURE_SD check --- src/src/WebServer/HardwarePage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index e01579d99..c8c599933 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -129,9 +129,9 @@ void handle_hardware() { #else //for ESP8266 we keep the old UI Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init #endif - #ifdef ESP32 + #if defined(ESP32) && FEATURE_SD Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); - #endif // ifdef ESP32 + #endif // if defined(ESP32) && FEATURE_SD Settings.Pin_sd_cs = getFormItemInt(F("sd")); #if FEATURE_ETHERNET Settings.ETH_Phy_Addr = getFormItemInt(F("ethphy")); From 4da79371cf4dfe9223aca05c3e90d17cd8637f2a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 26 Dec 2025 21:28:53 +0100 Subject: [PATCH 028/161] [P132] Additional support for INA226, INA228, INA230, INA231 and INA260 --- lib/INA/.gitattributes | 42 + lib/INA/.gitignore | 109 ++ lib/INA/LICENSE | 674 ++++++++++ .../examples/BackgroundRead/.due.test.skip | 0 .../examples/BackgroundRead/.esp32.test.skip | 0 .../BackgroundRead/.esp8266.test.skip | 0 lib/INA/examples/BackgroundRead/.m4.test.skip | 0 .../examples/BackgroundRead/.zero.test.skip | 0 .../BackgroundRead/BackgroundRead.ino | 192 +++ .../BackgroundRead_ESP32/.due.test.skip | 0 .../BackgroundRead_ESP32/.esp8266.test.skip | 0 .../BackgroundRead_ESP32/.leonardo.test.skip | 0 .../BackgroundRead_ESP32/.m4.test.skip | 0 .../BackgroundRead_ESP32/.mega2560.test.skip | 0 .../BackgroundRead_ESP32/.uno.test.skip | 0 .../BackgroundRead_ESP32/.zero.test.skip | 0 .../BackgroundRead_ESP32.ino | 176 +++ lib/INA/examples/DataLogger/.due.test.skip | 0 lib/INA/examples/DataLogger/.esp32.test.skip | 0 .../examples/DataLogger/.esp8266.test.skip | 0 .../examples/DataLogger/.leonardo.test.skip | 0 lib/INA/examples/DataLogger/.m4.test.skip | 0 .../examples/DataLogger/.mega2560.test.skip | 0 lib/INA/examples/DataLogger/.uno.test.skip | 0 lib/INA/examples/DataLogger/.zero.test.skip | 0 lib/INA/examples/DataLogger/DataLogger.ino | 430 ++++++ .../DisplayReadings/DisplayReadings.ino | 156 +++ lib/INA/images/INA226.jpg | Bin 0 -> 16227 bytes lib/INA/images/horizontal.png | Bin 0 -> 12437 bytes lib/INA/images/horizontal_narrow.png | Bin 0 -> 12606 bytes lib/INA/images/horizontal_narrow_small.png | Bin 0 -> 9620 bytes lib/INA/images/icon.png | Bin 0 -> 7785 bytes lib/INA/images/ina.ai | 1188 +++++++++++++++++ lib/INA/images/logo.svg | 78 ++ lib/INA/images/vertical.png | Bin 0 -> 11540 bytes lib/INA/images/wiki.png | Bin 0 -> 75555 bytes lib/INA/keywords.txt | 45 + lib/INA/library.properties | 9 + lib/INA/src/INA.cpp | 1143 ++++++++++++++++ lib/INA/src/INA.h | 351 +++++ src/Custom-sample.h | 1 + src/_P132_INA3221.ino | 453 ++++++- src/src/PluginStructs/P132_data_struct.cpp | 306 ++++- src/src/PluginStructs/P132_data_struct.h | 104 +- 44 files changed, 5406 insertions(+), 51 deletions(-) create mode 100644 lib/INA/.gitattributes create mode 100644 lib/INA/.gitignore create mode 100644 lib/INA/LICENSE create mode 100644 lib/INA/examples/BackgroundRead/.due.test.skip create mode 100644 lib/INA/examples/BackgroundRead/.esp32.test.skip create mode 100644 lib/INA/examples/BackgroundRead/.esp8266.test.skip create mode 100644 lib/INA/examples/BackgroundRead/.m4.test.skip create mode 100644 lib/INA/examples/BackgroundRead/.zero.test.skip create mode 100644 lib/INA/examples/BackgroundRead/BackgroundRead.ino create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.due.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.esp8266.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.leonardo.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.m4.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.mega2560.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.uno.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/.zero.test.skip create mode 100644 lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino create mode 100644 lib/INA/examples/DataLogger/.due.test.skip create mode 100644 lib/INA/examples/DataLogger/.esp32.test.skip create mode 100644 lib/INA/examples/DataLogger/.esp8266.test.skip create mode 100644 lib/INA/examples/DataLogger/.leonardo.test.skip create mode 100644 lib/INA/examples/DataLogger/.m4.test.skip create mode 100644 lib/INA/examples/DataLogger/.mega2560.test.skip create mode 100644 lib/INA/examples/DataLogger/.uno.test.skip create mode 100644 lib/INA/examples/DataLogger/.zero.test.skip create mode 100644 lib/INA/examples/DataLogger/DataLogger.ino create mode 100644 lib/INA/examples/DisplayReadings/DisplayReadings.ino create mode 100644 lib/INA/images/INA226.jpg create mode 100644 lib/INA/images/horizontal.png create mode 100644 lib/INA/images/horizontal_narrow.png create mode 100644 lib/INA/images/horizontal_narrow_small.png create mode 100644 lib/INA/images/icon.png create mode 100644 lib/INA/images/ina.ai create mode 100644 lib/INA/images/logo.svg create mode 100644 lib/INA/images/vertical.png create mode 100644 lib/INA/images/wiki.png create mode 100644 lib/INA/keywords.txt create mode 100644 lib/INA/library.properties create mode 100644 lib/INA/src/INA.cpp create mode 100644 lib/INA/src/INA.h diff --git a/lib/INA/.gitattributes b/lib/INA/.gitattributes new file mode 100644 index 000000000..6adacb617 --- /dev/null +++ b/lib/INA/.gitattributes @@ -0,0 +1,42 @@ +######################################################################## +# This section defines which file types are associated with which # +# linguist-documentation language types. Initially this was set so that# +# the "Arduino" language was chosen, but that is no longer considered # +# a language so everything has been reverted to "c++" # +# # +# Date Author Comments # +# ========== ========== ============================================== # +# 2018-06-24 SV-Zanshin Changed file # +# # +######################################################################## +examples/ linguist-documentation=false +*.c linguist-language=c++ +*.ino linguist-language=c++ +*.pde linguist-language=c++ +*.c linguist-language=c++ +*.h linguist-language=c++ +*.cpp linguist-language=c++ + +######################################################################## +# Auto detect text files and perform LF normalization # +######################################################################## +* text=auto + +######################################################################## +# Custom setting for Visual Studio/Atmel Studio files # +######################################################################## +*.cs diff=csharp + +######################################################################## +# Standard to msysgit # +######################################################################## +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/lib/INA/.gitignore b/lib/INA/.gitignore new file mode 100644 index 000000000..b7eba2c0c --- /dev/null +++ b/lib/INA/.gitignore @@ -0,0 +1,109 @@ +######################################################################## +# This file defines which file types are to be ignored and skipped by # +# git so that they are not transferred and committed. # +# # +# Date Author Comments # +# ========== ========== ============================================== # +# 2021-12-11 SV-Zanshin New ignore for Visual Studio 2022 *.vcxitems # +# 2020-12-11 SV-Zanshin Ignores for doxygen and clang-format files # +# 2019-01-31 SV-Zanshin Ignores for doxygen # +# 2018-09-22 SV-Zanshin Ignores for MS VS 2017 # +# 2018-06-24 SV-Zanshin Changed file # +# # +######################################################################## + +######################################################################## +# Windows image file caches # +######################################################################## +Thumbs.db +ehthumbs.db + +######################################################################## +# Windows folder config file # +######################################################################## +Desktop.ini + +######################################################################## +# Recycle Bin used on file shares # +######################################################################## +$RECYCLE.BIN/ + +######################################################################## +# Windows Installer files # +######################################################################## +*.cab +*.msi +*.msm +*.msp + +######################################################################## +# Windows shortcuts # +######################################################################## +*.lnk + +######################################################################## +# Operating System Files - OSX # +######################################################################## +.DS_Store +.AppleDouble +.LSOverride + +######################################################################## +# Thumbnails # +######################################################################## +._* + +######################################################################## +# Files that might appear in the root of a volume # +######################################################################## +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +######################################################################## +# Directories potentially created on remote AFP share # +######################################################################## +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +######################################################################## +# Files and directories from the Atmel Studio Arduino IDE # +######################################################################## +.vs +__vm +Debug +Release +*.atsln +*.componentinfo.xml +*.cppproj + +######################################################################## +# Files and directories from Microsoft Visual Studio # +######################################################################## +*.sln +*.vcxproj +*.filters +*.user +*.vcxitems + +######################################################################## +# Files and directories from Visual Studio Code # +######################################################################## +.vscode + +######################################################################## +# Files and directories from doxygen # +######################################################################## +html +Doxyfile + +######################################################################## +# Clang-format files # +######################################################################## +.clang-format diff --git a/lib/INA/LICENSE b/lib/INA/LICENSE new file mode 100644 index 000000000..9e419e042 --- /dev/null +++ b/lib/INA/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/lib/INA/examples/BackgroundRead/.due.test.skip b/lib/INA/examples/BackgroundRead/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.esp32.test.skip b/lib/INA/examples/BackgroundRead/.esp32.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.esp8266.test.skip b/lib/INA/examples/BackgroundRead/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.m4.test.skip b/lib/INA/examples/BackgroundRead/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/.zero.test.skip b/lib/INA/examples/BackgroundRead/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead/BackgroundRead.ino b/lib/INA/examples/BackgroundRead/BackgroundRead.ino new file mode 100644 index 000000000..763a46a23 --- /dev/null +++ b/lib/INA/examples/BackgroundRead/BackgroundRead.ino @@ -0,0 +1,192 @@ +/*! + * + * @file BackgroundRead.ino + * + * @brief Example program for the INA Library demonstrating background reads + * + * @section BackgroundRead_section Description + * + * Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality. + * It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and + * current information in the background while allowing the main Arduino code to continue processing + * normally until it is ready to consume the readings.\n\n + * + * The example program uses the Arduino AVR-based interrupt mechanism and will not function on other + * platforms\n\n + * + * Detailed documentation can be found on the GitHub Wiki pages at + * https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of + * different types and this program demonstrates interrupts and background processing, it will limit + * itself to using the first INA226 detected. This is easily changed in the if another device type + * or device number to test is required.\n + * + * This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place, + * this is the same setup that can be found in the Adafruit INA226 breakout board. The complex + * calibration options are done at runtime using the 2 parameters specified in the "begin()" call + * and the library has gone to great lengths to avoid the use of floating point to conserve space + * and minimize runtime. This demo program uses floating point only to convert and display the data + * conveniently. The INA226 uses 15 bits of precision, and even though the current and watt + * information is returned using 32-bit integers the precision remains the same.\n The INA226 is set + * up to measure using the maximum conversion length (and maximum accuracy) and then average those + * readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2 + * times a second. The pin-change interrupt handler is called when a reading is finished and the + * INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global + * variables. The main program will do whatever processing it has to and every 5 seconds it will + * display the current averaged readings and reset them.\n + * + * The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + * contains the information required in order to hook up the device. Unfortunately it comes as a + * VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is + * quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I + * needed in order to monitor a 24V battery system which goes above 28V while charging and which is + * above the absolute limits of the INA219). It is also significantly more accurate than the INA219, + * plus has an alert pin.\n The interrupt is set to pin 8. The tests were done on an Arduino Micro, + * and the Atmel 82U4 chip only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so + * pin 8 was chosen. + * + * @section BackgroundRead_license GNU General Public License v3.0 + * + * This program is free software : you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version.This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + * have received a copy of the GNU General Public License along with this program(see + * https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + * . + * + * @section BackgroundRead_author Author + * + * Written by Arnd at https://www.github.com/SV-Zanshin + * + * @section BackgroundRead_versions Changelog + * + * Version | Date | Developer | Comments + * ------- | ---------- | ----------- | ------------------------------------------------------------ + * 1.0.5 | 2020-12-01 | SV-Zanshin | Corrected "alertOnConversion()" call + * 1.0.4 | 2019-02-16 | SV-Zanshin | ifdef so that sketch won't compile on incompatible platforms + * 1.0.3 | 2019-01-09 | SV-Zanshin | Cleaned up doxygen formatting + * 1.0.2 | 2018-12-28 | SV-Zanshin | Converted comments to doxygen format + * 1.0.0 | 2018-06-23 | SV-Zanshin | Cloned and adapted example from old deprecated INA226 + * library + * + */ +#if !defined(__AVR__) + #error Example program only functions on Atmel AVR-Based platforms +#endif + +/************************************************************************************************** +** Declare all include files ** +**************************************************************************************************/ +#include // Include the INA library + +/************************************************************************************************** +** Declare program Constants ** +**************************************************************************************************/ +const uint8_t INA_ALERT_PIN = 8; ///< Pin-Change pin used for the INA "ALERT" functionality +const uint8_t GREEN_LED_PIN = 13; ///< Arduino standard green LED +const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed + +/************************************************************************************************** +** Declare global variables and instantiate classes ** +**************************************************************************************************/ +INA_Class INA; ///< INA class instantiation +volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example +volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings +volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings +volatile uint8_t readings = 0; ///< Number of measurements taken + +ISR(PCINT0_vect) { + /*! + @brief Interrupt service routine for the PCINT0_vect + @details Routine is called whenever the INA_ALERT_PIN changes value + */ + *digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK + PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group + sei(); // Enable interrupts (for I2C calls) + digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED + sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum + sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum + readings++; + INA.waitForConversion(deviceNumber); // Wait for conversion & INA int. flag + cli(); // Disable interrupts + *digitalPinToPCMSK(INA_ALERT_PIN) |= + bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group +} // of ISR handler for INT0 group of pins + +/*! + @brief Arduino method called once at startup to initialize the system + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns + @return void +*/ +void setup() { + pinMode(GREEN_LED_PIN, OUTPUT); // Make the internal LED an output pin + digitalWrite(GREEN_LED_PIN, true); // Turn on the LED + pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with internal pull-up resistor + *digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group + Serial.begin(SERIAL_SPEED); +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, wait 2 seconds for initialization + delay(2000); +#endif + Serial.print(F("\n\nBackground INA Read V1.0.5\n")); + uint8_t devicesFound = 0; + while (deviceNumber == UINT8_MAX) // Loop until we find the first device + { + devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor + for (uint8_t i = 0; i < devicesFound; i++) { + /* Change the "INA226" in the following statement to whatever device you have attached + and want to measure */ + if (strcmp(INA.getDeviceName(i), "INA226") == 0) { + deviceNumber = i; + INA.reset(deviceNumber); // Reset device to default settings + break; + } // of if-then we have found an INA226 + } // of for-next loop through all devices found + if (deviceNumber == UINT8_MAX) { + Serial.print(F("No INA found. Waiting 5s and retrying...\n")); + delay(5000); + } // of if-then no INA226 found + } // of if-then no device found + Serial.print(F("Found INA at device number ")); + Serial.println(deviceNumber); + Serial.println(); + INA.setAveraging(64, deviceNumber); // Average each reading 64 times + INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously + INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish +} // of method setup() + +void loop() { + /*! + @brief Arduino method for the main program loop + @details This is the main program for the Arduino IDE, it is called in an infinite loop. The + INA226 measurements are triggered by the interrupt handler each time a conversion is + ready and stored in variables. The main program doesn't call any INA library functions, + that is done in the interrupt handler. Each time 10 readings have been collected the + program will output the averaged values and measurements resume from that point onwards + @return void + */ + static long lastMillis = millis(); // Store the last time we printed something + if (readings >= 10) { + Serial.print(F("Averaging readings taken over ")); + Serial.print((float)(millis() - lastMillis) / 1000, 2); + Serial.print(F(" seconds.\nBus voltage: ")); + Serial.print((float)sumBusMillVolts / readings / 1000.0, 4); + Serial.print(F("V\nBus amperage: ")); + Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4); + Serial.print(F("mA\n\n")); + lastMillis = millis(); + cli(); // Disable interrupts to reset values + readings = 0; + sumBusMillVolts = 0; + sumBusMicroAmps = 0; + sei(); // Enable interrupts again + } // of if-then we've reached the required amount of readings +} // of method loop() diff --git a/lib/INA/examples/BackgroundRead_ESP32/.due.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.esp8266.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.leonardo.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.leonardo.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.m4.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.mega2560.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.mega2560.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.uno.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.uno.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/.zero.test.skip b/lib/INA/examples/BackgroundRead_ESP32/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino b/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino new file mode 100644 index 000000000..30fb842f6 --- /dev/null +++ b/lib/INA/examples/BackgroundRead_ESP32/BackgroundRead_ESP32.ino @@ -0,0 +1,176 @@ +/*! + * + * @file BackgroundRead_ESP32.ino + * + * @brief Example program for the INA Library demonstrating background reads + * + * @section BackgroundRead_ESP32_section Description + * + * Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality. + * It uses a pin-change interrupt handler and programs any INA2xx found to to read voltage and + * current information in the background while allowing the main Arduino code to continue processing + * normally until it is ready to consume the readings.\n\n + * + * This example program is designed for the ESP32/ESP8266 and will not function on other + * platforms\n\n + * + * Detailed documentation can be found on the GitHub Wiki pages at + * https://github.com/Zanduino/INA/wiki \n\n Since the INA library allows multiple devices of + * different types and this program demonstrates interrupts and background processing, it will limit + * itself to using the first INA226 detected. This is easily changed in the if another device type + * or device number to test is required.\n + * + * This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ohm resistor in place, + * this is the same setup that can be found in the Adafruit INA226 breakout board. The complex + * calibration options are done at runtime using the 2 parameters specified in the "begin()" call + * and the library has gone to great lengths to avoid the use of floating point to conserve space + * and minimize runtime. This demo program uses floating point only to convert and display the data + * conveniently. The INA226 uses 15 bits of precision, and even though the current and watt + * information is returned using 32-bit integers the precision remains the same.\n The INA226 is set + * up to measure using the maximum conversion length (and maximum accuracy) and then average those + * readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just less than 2 + * times a second. The pin-change interrupt handler is called when a reading is finished and the + * INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the global + * variables. The main program will do whatever processing it has to and every 5 seconds it will + * display the current averaged readings and reset them.\n + * + * The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + * contains the information required in order to hook up the device. Unfortunately it comes as a + * VSSOP package but it can be soldered onto a breakout board for breadboard use. The INA226 is + * quite similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I + * needed in order to monitor a 24V battery system which goes above 28V while charging and which is + * above the absolute limits of the INA219). It is also significantly more accurate than the INA219, + * plus has an alert pin.\n + * + * The interrupt is set to pin 8. The tests were done on an Arduino Micro, and the Atmel 82U4 chip + * only allows pin change interrupt on selected pins (SS,SCK,MISO,MOSI,8) so pin 8 was chosen.\n + * + * @section BackgroundRead_ESP32_license GNU General Public License v3.0 + * + * This program is free software : you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version.This program is distributed in the hope that it + * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + * have received a copy of the GNU General Public License along with this program(see + * https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + * . + * + * @section BackgroundRead_ESP32_author Author + * + * Written by Arnd at https://www.github.com/SV-Zanshin + * + * @section BackgroundRead_ESP32_versions Changelog + * + * Version | Date | Developer | Comments + * ------- | ---------- | ----------- | -------- + * 1.0.3 | 2020-12-02 | SV-Zanshin | Corrected call to "AlertOnConversion()" + * 1.0.2 | 2020-06-30 | SV-Zanshin | Issue #58 - clang-formatted document + * 1.0.1 | 2020-03-24 | SV-Zanshin | Issue #53 - Doxygen documentation + * 1.0.0 | 2019-02-17 | SV-Zanshin | Cloned and adapted from "BackgroundRead.ino" program + * + */ +#if !defined(ESP32) + #error Example program only functions on the ESP32 / ESP8266 platforms +#endif + +/************************************************************************************************** +** Declare all include files ** +**************************************************************************************************/ +#include // Include the INA library + +/************************************************************************************************** +** Declare program Constants, global variables and instantiate classes ** +**************************************************************************************************/ +INA_Class INA; ///< INA class instantiation +const uint8_t INA_ALERT_PIN = A0; ///< Pin-Change used for INA "ALERT" functionality +const uint32_t SERIAL_SPEED = 115200; ///< Use fast serial speed +volatile uint8_t deviceNumber = UINT8_MAX; ///< Device Number to use in example +volatile uint64_t sumBusMillVolts = 0; ///< Sum of bus voltage readings +volatile int64_t sumBusMicroAmps = 0; ///< Sum of bus amperage readings +volatile uint8_t readings = 0; ///< Number of measurements taken +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; ///< Synchronization variable + +void IRAM_ATTR InterruptHandler() { + /*! + @brief Interrupt service routine for the INA pin + @details Routine is called whenever the INA_ALERT_PIN changes value + */ + portENTER_CRITICAL_ISR(&mux); + sei(); // Enable interrupts (for I2C calls) + sumBusMillVolts += INA.getBusMilliVolts(deviceNumber); // Add current value to sum + sumBusMicroAmps += INA.getBusMicroAmps(deviceNumber); // Add current value to sum + readings++; + INA.waitForConversion(deviceNumber); // Wait for conv and reset interrupt + cli(); // Disable interrupts + portEXIT_CRITICAL_ISR(&mux); +} // of ISR for handling interrupts + +void setup() { + /*! + @brief Arduino method called once at startup to initialize the system + @details This is an Arduino IDE method which is called first upon boot or restart. It is only + called one time and then control goes to the main "loop()" method, from which control + never returns + @return void + */ + pinMode(INA_ALERT_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(INA_ALERT_PIN), InterruptHandler, FALLING); + Serial.begin(SERIAL_SPEED); + Serial.print(F("\n\nBackground INA Read V1.0.1\n")); + uint8_t devicesFound = 0; + while (deviceNumber == UINT8_MAX) // Loop until we find the first device + { + devicesFound = INA.begin(1, 100000); // +/- 1 Amps maximum for 0.1 Ohm resistor + Serial.println(INA.getDeviceName(devicesFound - 1)); + for (uint8_t i = 0; i < devicesFound; i++) { + /* Change the "INA226" in the following statement to whatever device you have attached and + want to measure */ + if (strcmp(INA.getDeviceName(i), "INA219") == 0) { + deviceNumber = i; + INA.reset(deviceNumber); // Reset device to default settings + break; + } // of if-then we have found an INA226 + } // of for-next loop through all devices found + if (deviceNumber == UINT8_MAX) { + Serial.print(F("No INA found. Waiting 5s and retrying...\n")); + delay(5000); + } // of if-then no INA226 found + } // of if-then no device found + Serial.print(F("Found INA at device number ")); + Serial.println(deviceNumber); + Serial.println(); + INA.setAveraging(64, deviceNumber); // Average each reading 64 times + INA.setBusConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setShuntConversion(8244, deviceNumber); // Maximum conversion time 8.244ms + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously + INA.alertOnConversion(true, deviceNumber); // Make alert pin go low on finish +} // of method setup() + +void loop() { + /*! + @brief Arduino method for the main program loop + @details This is the main program for the Arduino IDE, it is called in an infinite loop. The + INA226 measurements are triggered by the interrupt handler each time a conversion is + ready and stored in variables. The main program doesn't call any INA library functions, + that is done in the interrupt handler. Each time 10 readings have been collected the + program will output the averaged values and measurements resume from that point onwards + @return void + */ + static long lastMillis = millis(); // Store the last time we printed something + if (readings >= 10) { + Serial.print(F("Averaging readings taken over ")); + Serial.print((float)(millis() - lastMillis) / 1000, 2); + Serial.print(F(" seconds.\nBus voltage: ")); + Serial.print((float)sumBusMillVolts / readings / 1000.0, 4); + Serial.print(F("V\nBus amperage: ")); + Serial.print((float)sumBusMicroAmps / readings / 1000.0, 4); + Serial.print(F("mA\n\n")); + lastMillis = millis(); + cli(); // Disable interrupts to reset values + readings = 0; + sumBusMillVolts = 0; + sumBusMicroAmps = 0; + sei(); // Enable interrupts again + } // of if-then we've reached the required amount of readings +} // of method loop() diff --git a/lib/INA/examples/DataLogger/.due.test.skip b/lib/INA/examples/DataLogger/.due.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.esp32.test.skip b/lib/INA/examples/DataLogger/.esp32.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.esp8266.test.skip b/lib/INA/examples/DataLogger/.esp8266.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.leonardo.test.skip b/lib/INA/examples/DataLogger/.leonardo.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.m4.test.skip b/lib/INA/examples/DataLogger/.m4.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.mega2560.test.skip b/lib/INA/examples/DataLogger/.mega2560.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.uno.test.skip b/lib/INA/examples/DataLogger/.uno.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/.zero.test.skip b/lib/INA/examples/DataLogger/.zero.test.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/INA/examples/DataLogger/DataLogger.ino b/lib/INA/examples/DataLogger/DataLogger.ino new file mode 100644 index 000000000..a1aa7667a --- /dev/null +++ b/lib/INA/examples/DataLogger/DataLogger.ino @@ -0,0 +1,430 @@ +/* + Program to demonstrate using the interrupt pin of any INA2xx which supports that functionality in + order to trigger readings in the background and using a timer on the Arduino to trigger data + averaging and storing or displaying the computed values. + + The INA226 is set up to pull the alert pin down when a measurement is ready. The program has set + the bus and shunt to the maximum conversion time of 8.244ms and then averaging to 8, so each + measurement will take about 64ms. The interrupt vector "PCINT0_vect" is called and the readings + from the INA226 are read and added to the averages. + + A timer interrupt is defined in the setup() method that triggers a call to the vector + "TIMER1_COMPA_vect" once every second. The average values collected in the "PCINT0_vect" call are + then taken and stored in memory. As the amount of RAM is limited and the absolute readings are 2 + Bytes long while the delta values to the previous measurement are usually quite small, a variable + length Huffmann coding has been implemented at a nibble (4 bit) level to provide a higher-density + method of storing data. Each array is declared at 600 Bytes (one array for voltage measurements and + one array for shunt voltage) and those 1200 Bytes total can store up to 18 minutes of per-second + data, which would otherwise occupy over 4Kb memory. The Huffmann encoding method is described in + more detail in the interrupt code below. + + This example works on Atmel-Arduinos since it uses Atmel interrupts which are different on + processors such as the ESP32. The value of ARRAY_BYTES is set at 1200, which works on Arduinos with + 2K or more of RAM, smaller processors would need to reduce this value in order to work correctly. + The example is also coded for the INA226, as a chip with an ALERT pin is required for the program + to work; additionally the hard-coded LSB values for the bus voltage and shunt voltage have been set + to those used in the INA226. + + Detailed documentation can be found on the GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki + + This example is for a INA226 set up to measure a 5-Volt load with a 0.1Ω resistor in place, this is + the same setup that can be found in the Adafruit INA226 breakout board. The complex calibration + options are done at runtime using the 2 parameters specified in the "begin()" call and the library + has gone to great lengths to avoid the use of floating point to conserve space and minimize + runtime. This demo program uses floating point only to convert and display the data conveniently. + The INA226 uses 15 bits of precision, and even though the current and watt information is returned + using 32-bit integers the precision remains the same. + + The INA226 is set up to measure using the maximum conversion length (and maximum accuracy) and then + average those readings 64 times. This results in readings taking 8.244ms x 64 = 527.616ms or just + less than 2 times a second. The pin-change interrupt handler is called when a reading is finished + and the INA226 pulls the pin down to ground, it resets the pin status and adds the readings to the + global variables. The main program will do whatever processing it has to and every 5 seconds it + will display the current averaged readings and reset them. + + The datasheet for the INA226 can be found at http://www.ti.com/lit/ds/symlink/INA226.pdf and it + contains the information required in order to hook up the device. Unfortunately it comes as a VSSOP + package but it can be soldered onto a breakout board for breadboard use. The INA226 is quite + similar to the INA219 mentioned above, but it can take bus voltages of up to 36V (which I needed in + order to monitor a 24V battery system which goes above 28V while charging and which is above the + absolute limits of the INA219). It is also significantly more accurate than the INA219, plus has an + alert pin. + + Interrupts on Arduinos can get a bit confusing, differentiating between external interrupts and pin + change interrupts. The external interrupts are limited and which pins are available are different + for each processor, see + https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/ for + additional information. Pin Change interrupts, on the other hand, can be assigned to most pins, but + these interrupts are shared in groups of pins (call "ports") and when the interrupts are triggered + they call one of 3 possible ISRs. This program makes use of PCINT0_vect and the interrupt is set to + pin 8. The tests were done on an Arduino UNO and Arduino Micro using this pin + + Sometimes the INA devices will do a soft/hard reset on voltage spikes (despite using decoupling + capacitors) and since the "PCINT0_vect" is called only when the ALERT pin is pulled low and the + default mode of the INA226 upon reset is "off, this would result in the program never collecting + statistics. For this reason the TIMER1 is used as a watchdog timer, triggering an interrupt every + second. If no measurements are detected then the INA226 is manually reset and processing continues. + + GNU General Public License 3 + ============================ + This program is free software: you can redistribute it and/or modify it under the terms of the GNU + General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should + have received a copy of the GNU General Public License along with this program (see + https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + . + + Vers. Date Developer Comments + ====== ========== ========== ============================================================== + 1.0.1 2020-06-30 SV-Zanshin Issue #58 - clang-formatted document + 1.0.0 2018-10-13 SV-Zanshin Ready for publishing + 1.0.0 2018-10-03 SV-Zanshin Cloned and adapted example +*/ +#include // INA Library + +#include "MB85_FRAM.h" // I2C FRAM Library +/************************************************************************************************** +** Declare program Constants ** +**************************************************************************************************/ +const uint8_t INA_ALERT_PIN = 8; // Pin 8. +const uint8_t GREEN_LED_PIN = 13; // Green LED (standard location) +const uint32_t SERIAL_SPEED = 115200; // Use fast serial speed +const uint16_t ARRAY_BYTES = 1200; // Bytes in data array +/************************************************************************************************** +** Declare global variables, structures and instantiate classes ** +**************************************************************************************************/ +uint8_t deviceNumber = UINT8_MAX; // Device Number to use in example +volatile uint64_t sumBusRaw = 0; // Sum of bus raw values +volatile int64_t sumShuntRaw = 0; // Sum of shunt raw values +volatile uint8_t readings = 0; // Number of measurements taken +uint8_t chips_detected = 0; // Number of I2C FRAM chips detected +volatile uint32_t framIndex = 0; // Index to the next free position +INA_Class INA; // INA class instantiation +MB85_FRAM_Class FRAM; // FRAM Memory class instantiation + +void writeNibble(uint8_t dataArray[], const uint16_t nibblePos, const uint8_t nibbleData) { + /************************************************************************************************ + ** Method "writeNibble()" will write the LSB 4 bits of "nibbleData" to the "dataArray" nibble ** + ** offset "nibblePos", each index position is 4 bits. ** + ************************************************************************************************/ + uint8_t writeByte = *(dataArray + (nibblePos / 2)); // Read the correct byte and select + if (nibblePos & 1) { // whether the LSB or MSB is to be set + writeByte = (writeByte & 0xF0) | (nibbleData & 0xF); // Keep MSB & set the LSB to value + } else { + writeByte = (nibbleData << 4) | (writeByte & 0xF); // Keep LSB & set the MSB to value + } // of if-then-else nibblePos is odd + *(dataArray + (nibblePos / 2)) = writeByte; // Write the new value to array +} // of method "writeNibble()" +uint8_t readNibble(uint8_t dataArray[], const uint16_t nibblePos) { + /************************************************************************************************ + ** Method "readNibble()" will read the nibble addressed by "nibblePos" into the write the 4 ** + ** LSB bits of the return value. Each index position of the virtual array is 4 bits. ** + ************************************************************************************************/ + uint8_t returnVal = *(dataArray + (nibblePos / 2)); // Read the correct byte and select + if (nibblePos & 1) { // whether the LSB or MSB is to be returned + returnVal = returnVal & 0xF; // Use the 4 LSB bits + } else { + returnVal = returnVal >> 4; // Use the 4 MSB bits + } // of if-then-else nibblePos is odd + return returnVal; // Return the computed nibble +} // of method "readNibble()" +ISR(PCINT0_vect) { + /************************************************************************************************ + ** Declare interrupt service routine for the pin-change interrupt on pin 8 which is set in the ** + ** setup() method ** + ************************************************************************************************/ + static uint16_t tempsumBusRaw; // Declare as static to only init 1 + static int16_t tempsumShuntRaw; // Declare as static to only init 1 + *digitalPinToPCMSK(INA_ALERT_PIN) &= ~bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Disable PCMSK + PCICR &= ~bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // disable interrupt for the group + digitalWrite(GREEN_LED_PIN, !digitalRead(GREEN_LED_PIN)); // Toggle LED to show we are working + sei(); // Enable interrupts for I2C calls + tempsumBusRaw = INA.getBusRaw(deviceNumber); // Read the current value into temp + tempsumShuntRaw = INA.getShuntRaw(deviceNumber); // Read the current value into temp + INA.waitForConversion(deviceNumber); // Resets interrupt flag and start + cli(); // Disable interrupts + sumBusRaw += tempsumBusRaw; // copy value while ints disabled + sumShuntRaw += tempsumShuntRaw; // copy value while ints disabled + readings++; // Increment the number of readings + *digitalPinToPCMSK(INA_ALERT_PIN) |= bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group +} // of ISR handler for INT0 group of pins +void writeDataToArray(uint8_t dataArray[], uint16_t &nibbleIndex, const int16_t deltaData) { + /************************************************************************************************ + ** Function "writeDataToArray()" writes 4LSB from "nibbleData" to the "nibbleIndex" nibble in ** + *"dataArray". A Huffmann-like encoding with variable length is used to write the delta values ** + ** to the appropriate array. Each array "index" element is one nibble (4 bits) and the MSB ** + ** characters of the MSB nibble denote the record type. If the MSB is "B0" then it is a ** + ** 1-nibble long value, if the 2 MSB are "B10" then it is a 2 nibble value, if "B110" then 3 ** + ** nibbles, "B1110" denotes 4 and "B1111" denotes 5 nibbles. See the table below: ** + ** ** + ** 24-Bit representation Data Bits Value Range ** + ** ===================== ============ =============== ** + ** ----------------0xxx 3 bits data -4 to 3 ** + ** ------------10xxxxxx 6 bits data -16 to 15 ** + ** --------110xxxxxxxxx 9 bits data -256 to 255 ** + ** ----1110xxxxxxxxxxxx 12 bits data -2048 to 2047 ** + ** 1111xxxxxxxxxxxxxxxx 16 bits data -32768 to 32767 ** + ************************************************************************************************/ + + if (deltaData >= -4 && deltaData <= 3) // 1N, format 0xxx + { + writeNibble(dataArray, nibbleIndex++, deltaData & B111); // Write 1N to array + } else { + if (deltaData >= -16 && deltaData <= 15) // 2N, format 10xxxxxx + { + writeNibble(dataArray, nibbleIndex++, ((deltaData >> 4) & B11) | B1000); // write MSB + writeNibble(dataArray, nibbleIndex++, deltaData); // write LSB + } else { + if (deltaData >= -256 && deltaData <= 255) // 3N, format 110xxxxxxxxx + { + writeNibble(dataArray, nibbleIndex++, ((deltaData >> 8) & 1) | B1100); // Set 3MSB 9th bit + writeNibble(dataArray, nibbleIndex++, deltaData >> 4 & B1111); // write 4 MSB bits byte 1 + writeNibble(dataArray, nibbleIndex++, deltaData); // write 4 LSB bits byte 1 + } else { + if (deltaData >= -2048 && deltaData <= 2047) // 4N, format 1110xxxxxxxxxxxx + { + writeNibble(dataArray, nibbleIndex++, B1110); // Header nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble + } else { // 5N, fmt 1111xxxxxxxxxxxxxxxx + writeNibble(dataArray, nibbleIndex++, B1111); // Header nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 12); // MSB nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 8); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData >> 4); // next nibble + writeNibble(dataArray, nibbleIndex++, deltaData); // LSB nibble + } // if-then-else value fits in 4 or 5 nibbles + } // if-then-else value fits in 3 nibbles + } // if-then-else value fits in 2 nibbles + } // if-then-else value fits in 1 nibble +} // of method "WriteDataToArray()" +int16_t readDataFromArray(uint8_t dataArray[], uint16_t &nibbleIndex) { + /************************************************************************************************ + ** Function "readDataToArray()" returns a 2-Byte signed integer from "dataArray" starting at ** + ** "nibbleIndex" and expanding the Array's internal Huffmann-encoding values. See the descrip- ** + ** tion of writeDataToArray() for details ** + ************************************************************************************************/ + + int16_t outValue = 0; // Declare return variable + uint8_t controlBits = readNibble(dataArray, nibbleIndex++); // Read the header nibble + if (controlBits >> 3 == 0) // ----------------0xxx 3 bits data - 4 to 3 + { + outValue = controlBits & B111; // mask High Bit + if (outValue >> 2 & B1) { outValue |= 0xFFF8; } // If it is a negative number + } else { + if (controlBits >> 2 == B10) // ------------10xxxxxx 6 bits data - 16 to 15 + { + outValue = (controlBits & B11) << 4; // mask 2 High Bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB + if (outValue >> 5 & B1) { outValue |= 0xFFE0; } // If it is a negative number + } else { + if (controlBits >> 1 == B110) // --------110xxxxxxxxx 9 bits data - 256 to 255 + { + outValue = (controlBits & B1) << 8; // mask 2 High Bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 LSB + if (outValue >> 8 & B1) { outValue |= 0xFE00; } // If it is a negative number + } else { + if (controlBits == B1110) // ----1110xxxxxxxxxxxx 12 bits data - 2048 to 2047 + { + outValue = readNibble(dataArray, nibbleIndex++) << 8; // move in 4 high bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits + if (outValue >> 11 & B1) { outValue |= 0xF000; } // If it is a negative number + } else { + if (controlBits == B1111) // 1111xxxxxxxxxxxxxxxx 16 bits data - 16384 to 16383 + { + outValue = readNibble(dataArray, nibbleIndex++) << 12; // move in 4 high bits + outValue |= readNibble(dataArray, nibbleIndex++) << 8; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++) << 4; // move in 4 middle bits + outValue |= readNibble(dataArray, nibbleIndex++); // move in 4 low bits + } // if-then 5 nibbles + } // if-then-else 4 nibbles + } // if-then-else 3 nibbles + } // if-then-else 2 nibbles + } // if-then-else 1 nibble + return (outValue); +} // of method "readDataFromArray()" + +ISR(TIMER1_COMPA_vect) { + /********************************************************************************************** + ** Declare interrupt service routine for TIMER1, which is set to trigger once every second ** + **********************************************************************************************/ + static int16_t deltaBus, deltaShunt; // Difference value from last + static uint16_t arrayNibbleIndex = 0; // Array index in Nibbles + static int16_t lastBusRaw = 0; // Value from last reading + static int16_t lastShuntRaw = 0; // Value from last reading + static int16_t baseBusRaw = 0; // Base value for delta readings + static int16_t baseShuntRaw = 0; // Base value for delta readings + static uint16_t arrayReadings = 0; // Number of readings in array + static uint8_t dataArray[ARRAY_BYTES]; // Array for bus and shunt readings + if (arrayNibbleIndex == 0 && millis() < 3000) { // Skip first 3 seconds + baseBusRaw = (int16_t)(sumBusRaw / readings); // after startup to allow settings + lastBusRaw = baseBusRaw; // to settle + baseShuntRaw = (int16_t)(sumShuntRaw / readings); + lastShuntRaw = baseShuntRaw; + readings = 0; // then skip readings to let the + sumBusRaw = 0; // sensor settle down + sumShuntRaw = 0; // Reset values + return; + } // of if-then first second after startup + deltaBus = ((int16_t)(sumBusRaw / readings) - lastBusRaw); // Compute the delta bus + deltaShunt = ((int16_t)(sumShuntRaw / readings) - lastShuntRaw); // Compute the delta shunt + writeDataToArray(dataArray, arrayNibbleIndex, deltaBus); // Add bus reading to array + writeDataToArray(dataArray, arrayNibbleIndex, deltaShunt); // Add shunt reading to array + arrayReadings++; // increment the counter + lastBusRaw = sumBusRaw / readings; // Reset values + lastShuntRaw = sumShuntRaw / readings; // Reset values + readings = 0; // Reset values + sumBusRaw = 0; // Reset values + sumShuntRaw = 0; // Reset values + /***************************************************************************************************************** + ** Once the array could fill up on the next reading (2x max reading of 5 nibbles) then it is + *time to flush the ** + ** the accumulated readings. ** + *****************************************************************************************************************/ + if ((arrayNibbleIndex + 10) / 2 >= ARRAY_BYTES) // // + { // // + int16_t busValue = 0; // Contains current bus value // + int16_t shuntValue = 0; // Contains current shunt value // + uint16_t workNibbleIndex = 0; // Index into array for reading // + /*************************************************************************************************************** + ** If there is a FRAM memory board attached, then copy the array contents to it ** + ***************************************************************************************************************/ + if (chips_detected > 0) // Only execute if there is memory // + { // // + if ((framIndex + sizeof(dataArray) < + FRAM.totalBytes())) // Only write when space available // + { // // + cli(); // Enable interrupts temporarily // + Serial.print(millis() / 1000 / 60); // // + Serial.print(" "); // // + Serial.print(F("Writing ")); // // + Serial.print(sizeof(dataArray)); // // + Serial.print(" Bytes to memory @"); // // + Serial.print(framIndex); // // + Serial.print(".\n"); // // + sei(); // Disable interrupts again // + FRAM.write(framIndex, dataArray); // Write the whole array to FRAM // + framIndex += sizeof(dataArray); // set index to new location // + } // of if-then there is space in the EEPROM // // + } // of if-then we have at least one EEPROM attached to the I2C bus // // + for (uint16_t readingNo = 1; readingNo <= arrayReadings; + readingNo++) // Process every reading in array // + { // // + busValue = readDataFromArray(dataArray, workNibbleIndex); // Get next bus value from array // + baseBusRaw += busValue; // apply delta value to bus base // + shuntValue = + readDataFromArray(dataArray, workNibbleIndex); // Get shunt next value from array // + baseShuntRaw += shuntValue; // apply delta value to shunt base // + /************************************************************************************************************* + ** Insert code here to save data to static RAM or to a SD-Card or elsewhere ** + *************************************************************************************************************/ + + cli(); // Enable interrupts temporarily // + Serial.print(millis() / 1000); + Serial.print(" "); + Serial.print(readingNo); + Serial.print(" "); + Serial.print(baseBusRaw * 0.00125, 4); + Serial.print("V "); + Serial.print(0.0025 * baseShuntRaw); + Serial.println("mA"); + sei(); // Disable interrupts again // + + } // of for-next each array reading // // + arrayNibbleIndex = 0; // reset // + arrayReadings = 0; // reset // + } // of if-then the internal array is full // // +} // of ISR "TIMER1_COMPA_vect" // // + +/******************************************************************************************************************* +** Method Setup(). This is an Arduino IDE method which is called first upon initial boot or +*restart. It is only ** +** called one time and all of the variables and other initialization calls are done here prior to +*entering the ** +** main loop for data measurement. ** +*******************************************************************************************************************/ +void setup() // // +{ // // + pinMode(GREEN_LED_PIN, OUTPUT); // Define the green LED as an output// + digitalWrite(GREEN_LED_PIN, true); // Turn on the LED // + pinMode(INA_ALERT_PIN, INPUT_PULLUP); // Declare pin with pull-up resistor// + *digitalPinToPCMSK(INA_ALERT_PIN) |= + bit(digitalPinToPCMSKbit(INA_ALERT_PIN)); // Enable PCMSK pin // + PCIFR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // clear any outstanding interrupt // + PCICR |= bit(digitalPinToPCICRbit(INA_ALERT_PIN)); // enable interrupt for the group // + Serial.begin(SERIAL_SPEED); // Start serial communications // +#ifdef __AVR_ATmega32U4__ // If this is a 32U4 processor, // + delay(2000); // wait 3 seconds for serial port // +#endif // interface to initialize // + Serial.print( + F("\n\nINA Data Logging with interrupts V1.0.3\n")); // Display program information // + uint8_t devicesFound = 0; // Number of INA2xx found on I2C // + while (deviceNumber == UINT8_MAX) // Loop until we find devices // + { // // + devicesFound = INA.begin(1, 100000); // ±1Amps maximum for 0.1Ω resistor // + for (uint8_t i = 0; i < devicesFound; i++) // the first INA226 device found // + { // Change "INA226" to "INA260" or // + // whichever INA2xx to measure // + if (strcmp(INA.getDeviceName(i), "INA226") == 0) // Set deviceNumber appropriately // + { // // + deviceNumber = i; // // + INA.reset(deviceNumber); // Reset device to default settings // + break; // // + } // of if-then we have found an INA226 // // + } // of for-next loop through all devices found // // + if (deviceNumber == UINT8_MAX) // Try again if no device found // + { // // + Serial.print(F("No INA226 found. Waiting 5s.\n")); // // + delay(5000); // // + } // of if-then no INA226 found // // + } // of if-then no device found // // + Serial.print(F("Found INA226 at device number ")); // // + Serial.println(deviceNumber); // // + Serial.println(); // // + INA.setAveraging(64, deviceNumber); // Average each reading 64 times // + INA.setAveraging(8, deviceNumber); // Average each reading 4 times // + INA.setBusConversion(82440, deviceNumber); // Maximum conversion time 8.244ms // + INA.setShuntConversion(82440, deviceNumber); // Maximum conversion time 8.244ms // + INA.setMode(INA_MODE_CONTINUOUS_BOTH, deviceNumber); // Bus/shunt measured continuously // + INA.AlertOnConversion(true, deviceNumber); // Make alert pin go low on finish // + chips_detected = FRAM.begin(); // return number of memories // + if (chips_detected > 0) { // // + Serial.print(F("Found ")); // // + Serial.print(chips_detected); // // + Serial.print(F(" FRAM with a total of ")); // // + uint32_t totalMemory = 0; // // + for (uint8_t i = 0; i < chips_detected; i++) { // // + totalMemory += FRAM.memSize(i); // Add memory of chip to total // + } // of for-next each memory // // + Serial.print(totalMemory / 1024); // // + Serial.println(F("KB memory.")); // // + } // if-then we have found a FRAM memory // // + cli(); // disable interrupts while setting // + TCCR1A = 0; // TCCR1A register reset // + TCCR1B = 0; // TCCR1B register reset // + TCNT1 = 0; // initialize counter // + OCR1A = 15624; // ((16*10^6) / (1*1024)) - 1 // + TCCR1B |= (1 << WGM12); // Enable CTC mode // + TCCR1B |= (1 << CS12) | (1 << CS10); // CS10 & CS12 for 1024 prescaler // + TIMSK1 |= (1 << OCIE1A); // Enable timer compare interrupt // + sei(); // re-enable interrupts // +} // of method setup() // // + +/******************************************************************************************************************* +** This is the main program for the Arduino IDE, it is called in an infinite loop. The INA226 +*measurements are ** +** triggered by the interrupt handler each time a conversion is ready, and another interrupt is +*triggered every ** +** second to store the collected readings. Thus the main program is free to do other tasks. ** +*******************************************************************************************************************/ +void loop() // // +{ // // + delay(10000); +} // of method loop //----------------------------------// diff --git a/lib/INA/examples/DisplayReadings/DisplayReadings.ino b/lib/INA/examples/DisplayReadings/DisplayReadings.ino new file mode 100644 index 000000000..207060775 --- /dev/null +++ b/lib/INA/examples/DisplayReadings/DisplayReadings.ino @@ -0,0 +1,156 @@ +/*! + @file DisplayReadings.ino + + @brief Example program for the INA Library demonstrating reading an INA device and displaying + results + + @section DisplayReadings_section Description + + Program to demonstrate the INA library for the Arduino. When started, the library searches the + I2C bus for all INA2xx devices. Then the example program goes into an infinite loop and displays + the power measurements (bus voltage and current) for all devices.\n\n + + Detailed documentation can be found on the GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki \n\n This example is for a INA set up to measure a 5-Volt + load with a 0.1 Ohm resistor in place, this is the same setup that can be found in the Adafruit + INA219 breakout board. The complex calibration options are done at runtime using the 2 + parameters specified in the "begin()" call and the library has gone to great lengths to avoid the + use of floating point to conserve space and minimize runtime. This demo program uses floating + point only to convert and display the data conveniently. The INA devices have 15 bits of + precision, and even though the current and watt information is returned using 32-bit integers the + precision remains the same.\n\n + + The library supports multiple INA devices and multiple INA device types. The Atmel's EEPROM is + used to store the 96 bytes of static information per device using + https://www.arduino.cc/en/Reference/EEPROM function calls. Although up to 16 devices could + theoretically be present on the I2C bus the actual limit is determined by the available EEPROM - + ATmega328 UNO has 1024k so can support up to 10 devices but the ATmega168 only has 512 bytes + which limits it to supporting at most 5 INAs. Support has been added for the ESP32 based + Arduinos, these use the EEPROM calls differently and need specific code. + + @section DisplayReadings_license GNU General Public License v3.0 + + This program is free software : you can redistribute it and/or modify it under the terms of the + GNU General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version.This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should + have received a copy of the GNU General Public License along with this program(see + https://github.com/Zanduino/INA/blob/master/LICENSE). If not, see + . + + @section DisplayReadings_author Author + + Written by Arnd at https://www.github.com/SV-Zanshin + + @section DisplayReadings_versions Changelog + + | Version | Date | Developer | Comments | + | ------- | ---------- | -----------| ----------------------------------------------------------- | + | 1.0.8 | 2020-12-01 | SV-Zanshin | Issue #72. Allow dynamic RAM allocation instead of EEPROM | + | 1.0.7 | 2020-06-30 | SV-Zanshin | Issue #58. Changed formatting to use clang-format | + | 1.0.6 | 2020-06-29 | SV-Zanshin | Issue #57. Changed case of functions "Alert..." | + | 1.0.5 | 2020-05-03 | SV-Zanshin | Moved setting of maxAmps and shunt to constants | + | 1.0.4 | 2019-02-16 | SV-Zanshin | Reformatted and refactored for legibility and clarity | + | 1.0.3 | 2019-02-10 | SV-Zanshin | Issue #38. Made pretty-print columns line up | + | 1.0.3 | 2019-02-09 | SV-Zanshin | Issue #38. Added device number to display | + | 1.0.2 | 2018-12-29 | SV-Zanshin | Converted comments to doxygen format | + | 1.0.1 | 2018-09-22 | SV-Zanshin | Comments corrected, add INA wait loop, removed F("") calls | + | 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release | + | 1.0.0b | 2018-06-17 | SV-Zanshin | INA219 and INA226 completed, including testing | + | 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding | +*/ + +#if ARDUINO >= 100 // Arduino IDE versions before 100 need to use the older library + #include "Arduino.h" +#else + #include "WProgram.h" +#endif +#include // Zanshin INA Library + +#if defined(_SAM3XA_) || defined(ARDUINO_ARCH_SAMD) + // The SAM3XA architecture needs to include this library, it is already included automatically on + // other platforms // + #include // Needed for the SAM3XA (Arduino Zero) +#endif + +/************************************************************************************************** +** Declare program constants, global variables and instantiate INA class ** +**************************************************************************************************/ +const uint32_t SERIAL_SPEED{115200}; ///< Use fast serial speed +const uint32_t SHUNT_MICRO_OHM{100000}; ///< Shunt resistance in Micro-Ohm, e.g. 100000 is 0.1 Ohm +const uint16_t MAXIMUM_AMPS{1}; ///< Max expected amps, clamped from 1A to a max of 1022A +uint8_t devicesFound{0}; ///< Number of INAs found +INA_Class INA; ///< INA class instantiation to use EEPROM +// INA_Class INA(0); ///< INA class instantiation to use EEPROM +// INA_Class INA(5); ///< INA class instantiation to use dynamic memory rather +// than EEPROM. Allocate storage for up to (n) devices + +void setup() { + /*! + * @brief Arduino method called once at startup to initialize the system + * @details This is an Arduino IDE method which is called first upon boot or restart. It is only + * called one time and then control goes to the "loop()" method, from which control + * never returns. The serial port is initialized and the INA.begin() method called to + * find all INA devices on the I2C bus and then the devices are initialized to given + * conversion and averaging rates. + * @return void + */ + Serial.begin(SERIAL_SPEED); +#ifdef __AVR_ATmega32U4__ // If a 32U4 processor, then wait 2 seconds to initialize serial port + delay(2000); +#endif + Serial.print("\n\nDisplay INA Readings V1.0.8\n"); + Serial.print(" - Searching & Initializing INA devices\n"); + /************************************************************************************************ + ** The INA.begin call initializes the device(s) found with an expected ±1 Amps maximum current ** + ** and for a 0.1Ohm resistor, and since no specific device is given as the 3rd parameter all ** + ** devices are initially set to these values. ** + ************************************************************************************************/ + devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance + while (devicesFound == 0) { + Serial.println(F("No INA device found, retrying in 10 seconds...")); + delay(10000); // Wait 10 seconds before retrying + devicesFound = INA.begin(MAXIMUM_AMPS, SHUNT_MICRO_OHM); // Expected max Amp & shunt resistance + } // while no devices detected + Serial.print(F(" - Detected ")); + Serial.print(devicesFound); + Serial.println(F(" INA devices on the I2C bus")); + INA.setBusConversion(8500); // Maximum conversion time 8.244ms + INA.setShuntConversion(8500); // Maximum conversion time 8.244ms + INA.setAveraging(128); // Average each reading n-times + INA.setMode(INA_MODE_CONTINUOUS_BOTH); // Bus/shunt measured continuously + INA.alertOnBusOverVoltage(true, 5000); // Trigger alert if over 5V on bus +} // method setup() + +void loop() { + /*! + * @brief Arduino method for the main program loop + * @details This is the main program for the Arduino IDE, it is an infinite loop and keeps on + * repeating. In order to format the output use is made of the "sprintf()" function, but in the + * Arduino implementation it has no support for floating point output, so the "dtostrf()" function + * is used to convert the floating point numbers into formatted strings. + * @return void + */ + static uint16_t loopCounter = 0; // Count the number of iterations + static char sprintfBuffer[100]; // Buffer to format output + static char busChar[8], shuntChar[10], busMAChar[10], busMWChar[10]; // Output buffers + + Serial.print(F("Nr Adr Type Bus Shunt Bus Bus\n")); + Serial.print(F("== === ====== ======== =========== =========== ===========\n")); + for (uint8_t i = 0; i < devicesFound; i++) // Loop through all devices + { + dtostrf(INA.getBusMilliVolts(i) / 1000.0, 7, 4, busChar); // Convert floating point to char + dtostrf(INA.getShuntMicroVolts(i) / 1000.0, 9, 4, shuntChar); // Convert floating point to char + dtostrf(INA.getBusMicroAmps(i) / 1000.0, 9, 4, busMAChar); // Convert floating point to char + dtostrf(INA.getBusMicroWatts(i) / 1000.0, 9, 4, busMWChar); // Convert floating point to char + sprintf(sprintfBuffer, "%2d %3d %s %sV %smV %smA %smW\n", i + 1, INA.getDeviceAddress(i), + INA.getDeviceName(i), busChar, shuntChar, busMAChar, busMWChar); + Serial.print(sprintfBuffer); + } // for-next each INA device loop + Serial.println(); + delay(10000); // Wait 10 seconds before next reading + Serial.print(F("Loop iteration ")); + Serial.print(++loopCounter); + Serial.print(F("\n\n")); +} // method loop() diff --git a/lib/INA/images/INA226.jpg b/lib/INA/images/INA226.jpg new file mode 100644 index 0000000000000000000000000000000000000000..960916fe785f85950173c5474645540223258aed GIT binary patch literal 16227 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 D>75mb literal 0 HcmV?d00001 diff --git a/lib/INA/images/horizontal_narrow_small.png b/lib/INA/images/horizontal_narrow_small.png new file mode 100644 index 0000000000000000000000000000000000000000..8b63bcd5a89c1e32cb9010f95efb6538084220b5 GIT binary patch literal 9620 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 literal 0 HcmV?d00001 diff --git a/lib/INA/images/ina.ai b/lib/INA/images/ina.ai new file mode 100644 index 000000000..5da4d3de2 --- /dev/null +++ b/lib/INA/images/ina.ai @@ -0,0 +1,1188 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + ina + + + + + Adobe Illustrator CS6 (Windows) + 2018-07-11T13:50:29+04:00 + 2018-07-11T13:50:29+03:00 + 2018-07-11T13:50:29+03:00 + + + + 256 + 120 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAeAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A4PHGhRfhFaDtmtJdmAKT hPJ3mh1Dpod8yMKqwtZiCD4HjkuGXcUcce8N/wCC/Nf/AFYb/wD6RJv+aMeGXcV4494d/gvzX/1Y b/8A6RJv+aMeGXcV4494d/gvzX/1Yb//AKRJv+aMeGXcV4494d/gvzX/ANWG/wD+kSb/AJox4Zdx Xjj3h3+C/Nf/AFYb/wD6RJv+aMeGXcV4494d/gvzX/1Yb/8A6RJv+aMeGXcV4494WTeUfMsETzT6 LeRRRjk8j20qqoHckrQY8Mu4rxR7whtHRP0vY/CP96Iu3+WMETumQ2L9FM2LrHYqkH5ggHyF5lB6 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 irsVdirsVdirsVdirsVdirsVdirsVdirsVf/2Q== + + + + + + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + xmp.did:46EC1C38F884E81185F0AF3C69EC7F21 + uuid:0498f244-3cf1-449e-bd7f-9a050063864a + proof:pdf + + uuid:be7b52e5-b3f7-084b-afd9-c4a7b8bb0acb + xmp.did:01801174072068118083917CE3B5B58A + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + proof:pdf + + + + + saved + xmp.iid:46EC1C38F884E81185F0AF3C69EC7F21 + 2018-07-11T13:50:26+03:00 + Adobe Illustrator CS6 (Windows) + / + + + + + + Basic RGB + Document + + + 1 + False + False + + 2272.000000 + 1424.000000 + Points + + + + Cyan + Magenta + Yellow + Black + + + + + + Default Swatch Group + 0 + + + + White + RGB + PROCESS + 255 + 255 + 255 + + + Black + RGB + PROCESS + 0 + 0 + 0 + + + RGB Red + RGB + PROCESS + 255 + 0 + 0 + + + RGB Yellow + RGB + PROCESS + 255 + 255 + 0 + + + RGB Green + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blue + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + + + + Cold + 1 + + + + C=56 M=0 Y=20 K=0 + RGB + PROCESS + 101 + 200 + 208 + + + C=51 M=43 Y=0 K=0 + RGB + PROCESS + 131 + 139 + 197 + + + C=26 M=41 Y=0 K=0 + RGB + PROCESS + 186 + 155 + 201 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + + + + Adobe PDF library 10.01 + + + + + + + + + + + + + + + + + + + + + + + + + +endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 12 0 R/TrimBox[0.0 0.0 2272.0 1424.0]/Type/Page>> endobj 8 0 obj <>stream +HWn\7 ߯,Rmܢ(},iC>f< zaYHQ/}p~wCp];~ERts?/w?A+׬KU|Rqcߗw˧%8IjUw7q 'jM''MhP\c +=~X=͝oPvkgrҬq=Q|e_MMEv:vhRj6QWNʪ۴33l_a;.l?_߼r yo&:/#KS'$ߥO'xP`h(>lNo&H]MKNU 8z`Aŷ>aHib?p}|f }VQ{5[z + | D[rnsȁ`*81cԆD;| nrAACP9d=>kGԋޙ{=i3o cie|HCLBzoH|1VM;A9[X>E4 i"yj o7 jAMIX,T"ȴ.s۔r!En@[gp\v,]i.rq)f'ȼB4hȽ.T)phFD zXQ,$Tc1BG\\M<Ǿ!ϵN; +aŴ J86CAݸi4KUX;$!ŲMDzm&MIeLg9ַP\/zsTt2̮ +d^:H!V6E$JҾ.u1 {lQ)a)hdӬt] :ކTe μ,$Q$Rlp`r6l\eBEDЊ ƚQhs" RñeMu6T_UX1X6F`Ĝ*1'P悅ed7!.D%&O) 3=[Cx 6$ÌFO֫M3(uk,;^X6Nx5ܠ#P(5:p)O /ě;ɝ7R c +ljӑUa[1΃@?,}QO2%(P!#m".BN$UH(Y +vƬ(8K̓jE6$QąUhVLjɈV5+jW5fEjiAd.(NIYH֤T;hۨJSd(94d^nPPvIaG#, +ac%A֎t2ۑaao6^G:'R_0W=ɏ/. , G ҭXCϻ䉜J؄%sWj?3nDvyU>;8YGבhNw3|+3GE˟| +endstream endobj 12 0 obj <>stream +8;Z]">EZoI#Xql)?S2OARf0aeM1A3m+]F^&kUK0#-e#=`>QGoqD;2L9gU>utGVl=L +Hs9pdAhGLipo5IDL]DrKK1ZLW;#>M=`KAI`NM??5TMT,F5Rd/eMWfZuiCN_/,W3rj +38i^q7M1IY+SC7-\>87KkcK4?C1!U+a<6Y\8Nk5q#'*#'-j%*X\gT=+Rj&CI3ct&p +(I_WHJ;mEQfbC!>jf!u*3RS5h.&iqfS2s(]0d=]d&-X%"cq#n?bI`Olm1ee4pFhDE +jq0k:PX`np"F]/"1#c4l7f$>k.]G19m%fYH77N`F,LG=?`:0iY/@@>>%I7\pFk<$I +7P*&FW?9HZ2@%mi]@;t*AEntn#K[&7_i#.??)])gl_mm\1L,p!QZq+%)i!p*ah#Zk +>3\Z5eQVg1+u@8*YNTD_W,JefcYLM%NbSeorlj%qd@gUO07RS"2rAb"egn>`;3$fH +&X+=W.rs7-.8]4BI2&Hg,$e,3ah!&N#?aP68,%&NUFc\8I@JHC:tVftafLa:KZeG+ +]9Tde.d3CrQY-AP:kgSf"8P$c=ci"9\=O%@I2+''#R(?S-!L^#4Y74;!g5[L#GH`o +DcV$ +endstream endobj 13 0 obj [/Indexed/DeviceRGB 255 14 0 R] endobj 14 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> +endstream endobj 5 0 obj <> endobj 15 0 obj [/View/Design] endobj 16 0 obj <>>> endobj 11 0 obj <> endobj 10 0 obj [/ICCBased 17 0 R] endobj 17 0 obj <>stream +HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  + 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 +V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= +x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- +ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 +N')].uJr + wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 +n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! +zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km +endstream endobj 9 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <>stream +%!PS-Adobe-3.0 +%%Creator: Adobe Illustrator(R) 16.0 +%%AI8_CreatorVersion: 16.0.0 +%%For: (Yakamoz) () +%%Title: (Untitled-1) +%%CreationDate: 7/11/2018 1:50 PM +%%Canvassize: 16383 +%%BoundingBox: -303 -683 1419 117 +%%HiResBoundingBox: -302.249 -682.5898 1418.4443 116.5898 +%%DocumentProcessColors: Cyan Magenta Yellow Black +%AI5_FileFormat 12.0 +%AI12_BuildNumber: 682 +%AI3_ColorUsage: Color +%AI7_ImageSettings: 0 +%%RGBProcessColor: 0 0 0 ([Registration]) +%AI3_Cropmarks: -578 -995 1694 429 +%AI3_TemplateBox: 421.5 -298.5 421.5 -298.5 +%AI3_TileBox: 137.04 -580.6602 978.96 14.6602 +%AI3_DocumentPreview: None +%AI5_ArtSize: 14400 14400 +%AI5_RulerUnits: 2 +%AI9_ColorModel: 1 +%AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 +%AI5_TargetResolution: 800 +%AI5_NumLayers: 1 +%AI9_OpenToView: -4546 1965 0.125 1280 575 18 0 0 32 120 0 0 0 1 1 0 1 1 0 1 +%AI5_OpenViewLayers: 7 +%%PageOrigin:21 -598 +%AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 +%AI9_Flatten: 1 +%AI12_CMSettings: 00.MS +%%EndComments + +endstream endobj 20 0 obj <>stream +%%BoundingBox: -303 -683 1419 117 +%%HiResBoundingBox: -302.249 -682.5898 1418.4443 116.5898 +%AI7_Thumbnail: 128 60 8 +%%BeginData: 9128 Hex Bytes +%0000330000660000990000CC0033000033330033660033990033CC0033FF +%0066000066330066660066990066CC0066FF009900009933009966009999 +%0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 +%00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 +%3333663333993333CC3333FF3366003366333366663366993366CC3366FF +%3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 +%33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 +%6600666600996600CC6600FF6633006633336633666633996633CC6633FF +%6666006666336666666666996666CC6666FF669900669933669966669999 +%6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 +%66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF +%9933009933339933669933999933CC9933FF996600996633996666996699 +%9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 +%99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF +%CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 +%CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 +%CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF +%CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC +%FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 +%FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 +%FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 +%000011111111220000002200000022222222440000004400000044444444 +%550000005500000055555555770000007700000077777777880000008800 +%000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB +%DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF +%00FF0000FFFFFF0000FF00FFFFFF00FFFFFF +%524C45272627204B2627204B2627204B2627204B2627204B2627204B2627 +%204B2627204B2627204B26264BFD0BFF7D262627204B2026204B2026204B +%2026204B2026204B26264BFFA14B4B514BFD05FF4B514B5151FD09FF5251 +%277DFD09FFA84B514B5127A8FD07FF204A2026204A2026204A2026204A20 +%26204A2026204A2026204A2026204A2026204A2026204A2051FD0BFF7D20 +%4A20262026202620262026202620262026202620262051FFA120262027A8 +%FFFFFFA826202620207CFD08FF5120204BFD09FF76202026202051FD07FF +%26204B262620262026202620262026202620262026202620262026202620 +%2620262026204A20264BFD0BFF7D26207DA8A87DA8A8A8A1A8A8A87DA8A8 +%A87DA8A8A84B264BFF7DFD0426FD05FF2626204B204BA8FD07FF4B262076 +%FD09FF51204A204B204BA8FD06FF202620264B7651765276517652765176 +%527651765276517652765176527651765276525120262051FD0BFF7D204B +%A8FD05FF537DFFFF527DFD07FF7DF851FF7DF8262026A8FFFFFFA8272026 +%20262052FD07FF51202651FD08FFA820262026202620A7FD06FF27264A52 +%FD1FFFA84B204A4BFD0BFF7D2027FD06FF7D52FFFF7D7DFD07FF7D264BFF +%7D26264A26FD05FF264A204B264B20A8FD06FF4B4A2076FD08FF7626264B +%204B262652FD06FF20262076FFFFA8FFFFFFA8FFFFFFA87DA8FFA8FFFF7D +%52FFFFFFA8FFFFFFA8FFFFFFA8FF27262051FD0BFFA1204BA8FFA8277DFF +%FFFFA8FFA8FFA8FFA8FFA8FFFF7DF851FF7D204A2027A8FFFFFFA84B2026 +%204A202027FD06FF51202651FD08FF4B20264B52204A204BA8FD05FF4B26 +%2652FD0AFFA8F8A8FD04FF2752FD0CFFA85120264BFD0BFF7D264BFFFFFF +%7D7DFD0EFF7D204BFF7D26204A26FD05FF264A2626204B26267DFD05FF4B +%262076FD07FFA8204A20A1A826204B20A8FD05FF20262076AFFFA8FFA8FF +%A8FFA8FFA8527DFFA8FFA8527DFFA8FFA8FFA8FFA8FFA8FFA8FF26262051 +%FD0BFF7DF84BA8FFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFFF7DF851FFA1 +%20262027A8FFFFFFA8262026272720262026A8FD04FF5120204BFD07FF52 +%202026A8FF2026202052FD05FF4B262652FD05FF7DFD19FFA85120264BFD +%0BFF7D264BFF7DFD11FF7D204BFF7D26204B26FD05FF264B20A17626264B +%207CFD04FF4B262076FD07FF5120264BFFFF76204B204BFD05FF204A2076 +%FFFFA8FFA82752FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%FFFF26262051FD0BFF7DF84BA85252FFA8FFA8FFFFFFA8FFA8FFA8FFA8FF +%FF7D2051FFA12026204BA8FFFFFFA82720207DFF2026202620A8FFFFFF51 +%20204BFD06FFA82026207DFFFF7D20202620A1FD04FF26202652FD05FFA8 +%52A8FD18FF4B20264BFD0BFF7D2027FD0BFFA8FD07FF7D204BFF7DFD0426 +%FD05FF2626207DFF7D204B26264BFFFFFF4B262076FD06FF7C262026A8FF +%FFFF2626202652FD04FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7D204BA8FFFFFF +%A8FFA8FF7D27A8FFA8FFA8FFA8FFFF7DF851FF7DF8262026A8FFFFFFA827 +%202076FFFF4B202620207DFFFF51202651FD05FFAF4B202626FD04FF5220 +%26204BA8FFFFFF27262652FD20FF4B204A4BFD0BFF7D2027FF7D7DFD06FF +%5227A8FD07FF7D264BFF7D26264A26FD05FF264A207DFFFFA826264B204B +%FFFF4B4A2076FD05FFCA204B207DFD04FF7D26204B20A8FFFFFF20262076 +%FD04FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FF27262051FD0BFFA1204BA8527DFFA8FFA8FFA8FF27F8A8FFA8FFA8FFFF +%7DF851FF7D204A2027A8FFFFFFA84B202076FFFFFF51262026207CFF5120 +%2651FD05FF76202026A8FD05FF2626202651FFFFFF4B262652FFA87DA8FD +%1BFFA85120264BFD0BFF7D264BFD0BFF5227A8FD05FF7D204BFF7D26204A +%26FD05FF264A207DFD04FF204A264A20A851262076FD05FF51204B267D7D +%A17DA17D51204A204BCAFFFF20262076FFA8F827A8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7DF84BA8FF +%A8FFA8FFA8FFA8FFA8FF27F8A8A87DFFFF7DF851FFA120262027A8FFFFFF +%A826202076FD04FF7DF8262020275120204BFD04FFA8202620262020F8FD +%042026202620A1FFFF4B262652FFFFFFA8FD1BFFA85120264BFD0BFF7D26 +%4BFD0DFF52F8275227FF7D204BFF7D26204B26FD05FF264B207DFD05FF51 +%204B264B264B2076FD04FF7C26204B264A204B264A204B264B202652FFFF +%204A2076FD04FFA8FFA8FFA8FFA8FFA8FFFFFFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFFFFF26262051FD0BFF7DF84BA8FFA8FFA8FFA8FFA8FFA8FFFF7D27 +%FFFF5252A72051FFA12026204BA8FFFFFFA827202076FD05FF7D26202620 +%4A20204BFD04FF512026202620262026202620262026204BA8FF26202652 +%FD0EFF7D52FD10FF4B20264BFD0BFF7D2027FD0DFF527DFFFF7D7D7D204B +%FF7DFD0426FD05FF2626207DFD06FF5126264A204B2076FFFFFFA8204B20 +%52FD08A87626264A20A8FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8FF27 +%F852FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF26262051FD0BFF7D204BA8FFA8 +%FFA8FFA8FFA8FFA8FFFF7DF8A8A8277DA1F851FF7DF8262026A8FFFFFFA8 +%27202076FD06FFA820262026202651FFFFFF762020207DFD09FF20262020 +%4BFF27262652FFFFFFA8FD0BFF27F852FD0EFF4B204A4BFD0BFF7D2027FD +%0EFF7D27527DFF7D264BFF7D26264A26FD05FF264A207DFD07FF7D204B20 +%4B2076FFFFFF51204B27FD0AFF76204B204BFF20262076FF7DF827A8FFA8 +%FFA8FFA8FFA8FFFFFF27F852FFFFFFA8FFA8FFA8FFA8FFA8FF27262051FD +%0BFFA1202676A87DA87DA87DA87DA87DA87DA8A8A87DA87D4B2051FF7D20 +%4A2027A8FFFFFFA84B202076FD07FFAF4B2026202651FFFFA820262076FD +%0AFF7D26202620A74B262652FFA87DA8FD0DFF27F852FD0BFFA85120264B +%FD0BFF7D262026202620262026202620262026204A2026202620264BFF7D +%20202620FD05FF2026207DFD08FF7D2620262052FFFF762020207DFD0BFF +%202620204B20262076A9FFFFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FF27F852 +%FFFFFFA8FFA8FFA8FFA8FF26262051FD0BFF7D2027202620272026202720 +%26202720262027202620272051FFA84B524B52A8FFFFFFA8524B517DFD09 +%FF5252515176FFFF7D4B5251FD0CFF7D4B5251524B262652FD13FF27F852 +%FD09FFA85120264BFD58FF204A2076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFFFFF27F852FD09FF26262051FD58FF26202652FD15FF27F8527D27 +%2727A8FFFFFF4B20264BFD58FF20262076FFFFA8FFA8FFA8FFA8FFA8FFA8 +%FFA8FFA8FFA8FFFFFF27F8F8525227F87DFFFF26262051FD58FF27262652 +%FD16FF7DF8A8FFFFFF52F8FFFF4B204A4BFD58FF20262076FFFFA8FFA8FF +%A8FFA8FFA8FFA8FFA8FFA8FFA8FFFFFFF852FFFFA8FFA8F852FF27262051 +%FD58FF4B262652FD16FF2752FD05FF2752FF5120264BFD58FF20262076AF +%FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF2727FFFFA8FFA8F87D +%FF26262051FD58FF4B262652FD16FFA8F87DFFFFA827F8FFFF5120264BFD +%58FF204A2076FFFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFF7D +%F82727F8F8A8FFFF26262051FD58FF26202652FD18FFA852527DFD04FF4B +%20264BFD58FF20262051A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF +%A8FFA8FFA9FFFFFFA8FFA8A820262051FD58FF27264B264B27514B4B2751 +%4B4B27514B4B27514B4B27514B4B27514B4B27514B4B2751264A204A4BFD +%58FF2026204A202620262026202620262026202620262026202620262026 +%202620262026202620262051FD58FF262026202620262026202620262026 +%2026202620262026202620262026202620262026202620204BFD58FF2751 +%4B5127514B5127514B5127514B5127514B5127514B5127514B5127514B51 +%27514B5127512776FDD9FFA87D7D7DFFFFFFA87D7D7DA8FD06FFA87CA1A8 +%FD07FF7D7D7DA1FD5FFF7620204BFFFFFF7DF8262051FD06FF762020A8FD +%06FF7D202026F8A8FD5EFF7DF82626FFFFFF7D202026207DFD05FF7DF826 +%7DFD06FF522026202051FD5EFF7C26204BFFFFFFA1204B264A26FD05FF7D +%2020A8FD06FF264B204B204BFD5EFF7DF82627FFFFFF7D20204A202052FD +%04FF7D2026A1FD05FF7D202051262620A8FD5DFF7D202051FFFFFFA1204B +%204B2626A8FFFFFF7C2620A8FD05FF76204BA851202652FD5DFF7D202626 +%FFFFFF7D2020522026204BFFFFFF7DF826A1FD04FFCA20204BFF51202027 +%A8FD5CFF7D262051FFFFFFA720267D7D204B20A7FFFF7C2626A8FD04FF7D +%26207DFFA8204B20A8FD5CFF7D202626FFFFFF7D2620A1A827202620FFFF +%7DF826A7FD04FF522026A8FFA84B202051FD5CFF7C26204BFFFFFFA12026 +%7DFF7C26262652FF7D2020A8FD04FF26264BFFFFFF5226204BA8FD5BFF7D +%F82626FFFFFF7D20F87DFFFF26262020A87DF8267DFFFFFF7D20207DFFFF +%FFA8202620A7FD5BFF7C26204BFFFFFFA120267DFFFFA8204B207C7D2620 +%A8FFFFFF76204A52A87DA77D4B262652FD5BFF7DF82627FFFFFF7D2020A1 +%FFFFFF522026204B2026A1FFFFCF2026FD07204A204BA8FD5AFF7D202051 +%FFFFFFA120267DFFFFFFA84B204A204B20A8FFFF7D262026202620262026 +%204B20A8FD5AFF7D202626FFFFFF7D20207DFD04FF522020262026A1FFFF +%52202676A87DA77DA87D27202051FD5AFF7D262051FFFFFFA720267DFD05 +%FF4B26204B26A8FFFF262627FD07FF5226264BA8FD59FF7D202626FFFFFF +%7D26207DFD05FFA820262026A7FF7D26207CFD07FFA7202620A1FD59FF76 +%20204BFFFFFF7DF8207DFD06FF76202020A8FF51F820A8FD08FF27F8204B +%FD59FFA8527D7DFFFFFFA87C52A8FD06FFA87D767DA8FF767C76FD09FF7D +%7C527DFD57FFFF +%%EndData + +endstream endobj 21 0 obj <>stream +%AI12_CompressedDatax^q%_ޡ`3S`"Gk,HcРٴ1lw7?)ÛW&^|?OMYn~ nRF7ox7|Vr>MiޤqW~z\u|8aZT{pw/|嫯~xvoo~77՛7͋l}+{/_|Imq?/7_+k3s^oٕ3i_n&_ܟŤ7Aֆq?k}|6g/zcΆ9ݶKl/S[v߷|35#Gׯg7WFw?Z]Y_կy߼}[j_;l~/wo9]#o_?䯾zW>gU{6ekoqw_~}'RKS _?bC߿xiװqA{n[;_gqIWo}ux߾xw/߼x ~š!N5O1On;8;Ň2-_듇\׿޽yg;>݇W[=o~:ut:?ux߼?~FrwE/ꟺWf}b=)oֿ_/?c.{/AzW0n~O/G><>|^m17֢[rfK{nſ2s[?vEmOg_{͞|瞟`{ggcYO?zV샱xxs"so;LX ?t~vXϡ_ +?6 +d{xn'ʟۣ=qv_t==~wwnZ^:uZv6uxinц3{{s  j%ۀcn m ջ +*6 Է]Ͻ*^!4[yXg#>10l=G fIn[}4nڼJڴ9YY01;=n97#v|>M'Yui}vo}цu^ֻ޾^~hc$و(62Z4Ǔm>ue5튚1_K2-ӈwd4jw}_rt7n619M ۨ.6g6'Gr1|fn!nGky8|wzcѦ G,kBmeZṍ6[=m?6xMm|*W#{ǟɝ5߃VhW}f~4OIbmdQIyܹi_ZV-ZQNZh%=[u.W^{>=zNYgYrx3c#x[arOu_=qp߯vcYf}u̳sO'zJ􉞪_}SOS解OSV_Eol ҌSJqK׎~۽߮3U]?e+m+~!vq0֠40ʎlycj0ƓYuG#ړGE{` +>{Dz$=[tOghОh"`?l-aGZӹc433~GgK|;EhpֵF*32;SPJ3{GVW ++ucC,wXwZj)?}C_v8{Fvm[\ +/,ʧ=o;un'Ss w;N97pI22 ~oW<\5Vd\?”vvm }^Gn<|qtξm+K y45Q?j|45ߎd%8ǺֆO?tO4%+#`Qܭ +cL}00.S" ? V[ s\33NC t3 \PWlJlu ]E|nۃqw} 0TJJ1U$\J'-g eQZHc+=NCG蟿w;~g;(g8;6=W&SI;hRٔ +P'~~>*0.HK؇{.fw~{gD~]?NĽ;(?$g$gwkwP|]??$$x89iؘ8uŨ!%dcf8ynm֟6M-S݆=V! ?tS9wH<<<3~{̍t={3|3s(CO׫(NFų61ØaL0&~瘸y[Ödosc*Uh~/Q^rkm79[XE{_7%avŏy֯p@*.|%//`yƧK?{ x7?oݻ77?7#b7YoUJ߶[s7BRbN!U0lP`012~Y!<ͦM& :tߦ  &DoJA.7/y^ii݆־mMstž$O 3yi{AoFͮ3{!DmvcLj͞)MF^>ϧmtuMJ`>%(x^mJ`go[u߭d 4{:nfhw|g1–oMŻuO:)Icܔdpey [:.IӵMy:-a6=E}iz&~^MLXV Rm7#mHrK۸5ERo`^nS6G)ize=y#~Kpy! 4/팔n7dqyW3Ik?5v)NojӅӌazAt^$ J&_`K=6#e h{f3O;ڑݭB<=6}>y(Úl:ҡs{>&wnn.zI]#(Sa+ֆVj>7]gRS EaA3Q>.W^l4-8Nyݺ^.* ?6f,KPhj z)Ѧ@k6*B*3CbZ9VٖD .⌁_ k ^{ H{6UX!fF4AΛX&UCe`DžVv=7怶2:SjL39wShsV;V{:uК& C+kTF@?Y!Dn29c:P0VkR@k>@sltk^n].hlwn^͉h!6AKh"A7$]=Ē#"C;$3J`왔7KD~grz`)CS|dy&-_lu^ja P#:0u]x(d9P6v5CFɀR!`EaC9vwٔb9Pq0W +s Ҍރ`e2"{uz}1mF͏,HLFʞ 钂G +Z K ^*3{ b昙3ylESkpl}͋`PgXZ]5+Yzë̈=/vV[$nZ +OraS>}=5<ȸR*v_J +# igp)7n6ecps i<)~zb $!p0/X>t*W5Mx8v8C1$,\=AZp3JZs-Eβi0|`n + +NaCKWN+c~/#N ^IZ4M`q2 ײ7)0\ϛN6s1qqBai. nB{e lK:q}U0=X3̉I)oi4z4sOyb(THŋ$Uu&-Dk^.rA`1=v+⃘:6R/Fs7 ŊKM[L7qr&/S75f +*% K3@ l+;ju))G L +{r}gnb -R»èk=CNA@EŒ/] +Q94 ZZav('hDc@ڀq#zg;D;C&>ltˈKΎ i\Af $A+ vɬȰR<]6-3[7A!?cyFB&hXgy;H`P;4FKKk0mdyt<}orL +@t;g0W:MfQSgDG c G )mf,)|P< 6Y{@ cBijJ@{@ +k>g~, 0F4MnY7{0a#Ʃ1 +aYqp4,u!U];B@5fM)hle,:W*z08 By X7frNz OƳ<&6+X0knًsؗ>O̓O% ~G:oieP`^^9<:`5B^Va"{~_ZVW8YQ`Ӵ78C adp^ Fy6+* +9$r!e:1$e!9Z$m ӃUBG2 52U*ր'e1TdAQw81BJi9h;X&,2Z"7Ll-AEdy-/!|h{mOu)Vސ5S"gCm\9)QP TVpCpİ-)~JtOG,ω(ԴQ21R V-i26H&0m +=!XLfwgTNUcN\pڶzĭ2+5500bg_n5Sc1q%k*OqfD,Qzhp,@~Q .+%>Cӑ},0: K!:#BY4BN_.[>-Z\) ]2|M^lvRolͅ`q$sl;2z/3E_.)!LlJ0Ls`')C f0,Pnp%Pk,źݡ!MGNi-e@@X@ 7e#H~U,E"t=1in (])6q挆tq^ <"l)i+Y+ঔqqLE RnPd(_W +`#su883\1D<՟Z&޷3;C6RªD.S Rn09W&3E!u^+92Nu sQS6sػ9sE0+a3<5%iSxCͭЂ~aIs.y-C(HmrsD.u `v$2ƌswI[L l.6C w +.F3mZ sG8& M3!ETgy86:Hi6]Of M2d[!u!. 7Co"p +tv\\~ O_ @e!kИ%=G+]*MN$ Dw-C_n_[($ҒtM9mŐv @}y^gxL(]|Оt@`m#5ۇ: TmMOdÄzf(]䓉-F +^JI@0}5J!X1 P2Ty`atY(Oc~ +`SMRx}W;M +JQ:qk+QM^;gHA`}~zŻCG`l@ 09 ~!KHBaI )ik+ ;4bUʏ}Sd'2OYAW#32K[HGfTO>8Y뷿˗|wM3Ҝ +#yYŗ!FbZ])%lbxvA#bJvQl^ڑ$6yWPYN! 0qC9]ngvU}=ir9Q4E6u^aSE%@ 7kq@\"l37-C }z$U@B}=o ; +X4gx%߹b4ۆRvK6//[X1)Pݵ17j{ 47apZbhe1$wHʢq{X.0q|sVq#iYs+Σ5Bxtd!n9~nOo6Y/)H0້ 1:vnU\^fc TQL9N@~0ِ5v@$u`w%݌1zĊJ +gJ,.VAITXC f0Vq"ˇ<`[O9 CEiLBhAp$ +g I6ܨ2K zwmx$¬!T75/]Q|)'ǯX.kY]*QЬE"#А+Sxe[V(,[B~#EMW @rX(m֏Q.X%k֡d,D4XKgiټ.WJ^PV@qCP<.Q?H~ZHJJMqG9n'@ ,F-%*{wbvZQy#z,3ep|'4<PX.O;a+IJd6)e'c*.282jMoUGn矗]9 5%_u(*/<y*58tt6G;eqi| Kի4&o9ʗIRFtNl쉢ӳӎjՙ>8٢Bu)7xzM{(`JXpFdhBCPQ3~^VR XKx^Ȉ(1%o~ӛ[1w4޹+1foBМ9qK~eg>M ʙ~(cõ;/c`YB&ᡓ;68ƙ'$` WAp܅wODX# +G7 0}Od@Z1 +F7DIaTg)I|+ : v/<5h@9EEuW[9F!Es|*R!g9f"a i31"!LD<08hB1Oȍ)6eȺƸ[fㆄտ,߂H'μj]ƻ/V}8i _8kB1i/>+.<Sg@ .#sGB\,鬋_+Twa.AÉirԠ-KEaBW`,)Ǩ.|br]1[19:ubxS7"* Q~%"FH!il9F3M(;6?NQR ĸYKә!`)fe3T5930*K{j- ˀ83g9XaV  F38}' b r30 aZHp}= ` V Eگ nFVBP xc.Dð{`n0t S'N3ߣ3.,M41Ln0 kS*J. - 8!GP Afad@N, xLy +WߗB+LV0PN#J`( 0H,Qt( PN30psE(L0Yty)/؄%sJ2 DHcJY<¥U.4eqs"Xv 0,#tAxH( ^R@K[k}`[?7S`oFwa څWdW `bXaYӕ/Y/`IxYxUEU0*` +^t FU ITt +LY0Eu3?pENb!w!d 4A ѡ= Z<7ʸRcڹ?m^S"@Vfh+30(%A6n/0=f+fD~9``h +&5ٹ 1Xteޜ/()(5VA TJCm6F.tXpo +6`01X**}/f7 wX"YF[LAm +Ya67XBt+$;0p,cEV!{ XM=clA[= TGnC$#Rޙ 4PB1:8}B 8LKcBfOĿBN۰tԹxyXz1`b +,sMœˋ1_JMu\x)&<{!QV&x $( vq/h)\hS o3)CC0W_2 d$_ IɶÃX Nhf."` Ur(,/# _f6_v*ĜmJj0wfeV'%,DN$b/I E콰̂Kj /Oli)!1h!aƕ|>(˾/iӼr_9k/`(g JBFi3lQp뜿<b-ř vp2:ū(mw=gr$C"-gE4Ryp͢ߑdѹ~#wrE{/s7M@9D|o|]8<}䊢LgE$5gwXg_Ȋyu_Ҟn6gl _9"cQ4g_5e/[Qkec7_{Y 1~)pfW)ïr43/_fqpaS _a!_S ܟqq_ bw_#0{q4wM*8~)tg_7 r_,X+.$s䗇0I~SP]x~!e^#x~)Dqe(`+{R-4/v_m0EV:B ),AK+DZS// +L'/_nb_U Y]f_E􋍈/PŹy_b-Guew3/J4j<_AijĒp['A_HR@?\x~!+x~!y~)a|Nkj2h~Ӌ_j|3bKt-AU4h 1A\h~wb1h~SmD4, +$h5B8/wRΜi~)4c?Pϯ%r[<лR;ϯܯT/9- +_(+/':)>{=[B91cXg_J02Cys1:{}%؝׉{p;~e8ۖuܾd;|u^ٲJL۽z$gV9=Hh YQ&qy k`G)E"o ޽` B8?b+`a`nitW]̺'F?h;t^]}c7wqV.ʁ@$}ԅԅ]f]̨ ;Nfw~], T(?`Y.#`EI-2QpNߙ)tW&ޙJHXIj8A 5|gjy{h2! ݈pCŋcCw:bPN _t Fl  wQg]t)Dj<.Y.3.}FB7Ůg +b{TN ]V, z%c.Ρ,`w]kr@N^ #Gfѝv-)t-Au?9Ns->1saefU@kwWQ97.\E|:v1G̞K U +]v5€ˀa`GG# + RP.hŝK .ܹ<$s0';w}p2LU);*2^ipBΥ,NS%,A?w `1(b=sP.Bs. b/̹r\8s.fׅ9xr~7T8>݅9)ݩs+ԹT#_s騢9g0*Y9`U=ŝKV58w.t;W)_s@*s)(ݪg"ڕ99=5xl̹8y) TC2(q)'&]O^ eQ2[ĹȽp\s!e:O:Xhs.D<]s5s!`,3s)ra%RUZxR#Pp\j3sn".Ŝrv .fsEbs7Y ;8څ9RΙsSi^̹û0BJ]003` v3t1qC칸\tUKcf^xa%!^S<;_W\[BO-\J P$ \" \Hc;si2'̹Y ̹őĜ9+-mՑh/ҿ]s!r\R]̹E\|EuZ ;Z;8Z|3g-&wNA-=zgv8Ym+Y 2v,Z.Z Gm`esWZ?i{4x,vZAm);AqܴXY!ys"@v ޲XANȾGM~ }dmoTL 9- +BMTUAMhL VӢDl6/@BAMDHf)eq,t0E. JH=eYە^L3E. nn\,,j>X/A heL VYްJ*$3Ke){\L);"録CiSt ^ +B& > ɞy0 "Y0l{:xdI5A#1ra{MXEqpȚ@\| + +Yx0 %8BHL{dؙCk${r\_ t>B' =$y0bJ/ƫCtX}a&=<5҅;wl w,܃ͩcWu"ZS- +1m5 }c( w捭.|8% 8E!hŸK[agDς4,ا^3qr.tM;,8F"L% BVgb=$GCUk>N4R7mC#?=3i <  0JA8XՕ}qp#{Id¢I<^sG<Gy)$d`:{!AR톳+J;;2BG]v+/hF +OtQ½kіM6M,pJ/1A& +n6}_6` $$GѺڿ#<(0kk0g_ QT 7` OlyB iokLp֓ua3yqwvY L1[*Ǹbn8wwYqQN|Wj܃ڑ`B +QH#3Y ^W-njg|^ hŚ(,):kZMMU$&y(`]et 1a8jzpTz2-/< 3^ Ʊ1Lו {voZ u.|0^CE1wibn%! ",NK,G=+|ߓ񀒤2U=gY8 +Ђ3E*v|3`!fJZQ +w1wQFJGryZ,MJDS&LBMypA*[2vJ\Kp9H3Ss,d}p{?HzL # -xF!bd= ˧ idiv<| +Hsn:%dW$03eY4߀sxST h%g5{g )Cc(5/)[_9!b`=S]/bi+Ѡ*l$TPLHX"k>.׋Sx]1W}rG(q-@lN+`e_Qz7J!<˾ )K?`9v@gI2k'f)7-SDa@ Xuhηz -[Z, P\}Zl r[\d,`]֊oS},slAfnMSg?<jx: -Bp;JqeZvKy(M >T[}ِgNk~=T~`ou+Z{@3nCb`mFd@[dzS79 ͐[LŜ_z{,θx rC.iAX@[t KB)S C75ST?0cn{JwY|2.؜Vz +eV 01 %bE.=6=۸ ז0mFID p,G 8u Mv=M:Ep7Gn8 9.zƍCJ{p|?5,FBh7yu׉b|TVu|ᦁ +[^J[7F6U؟ym)k<%a Cu&  +L`>bI2%AyV jr3%>;tvV, isFn . i.},.aBW ul#nMB0<*ݹQwS= 8h?TMnEHu2 8M% $2*)SYdS uy-dpC(}8*S-Uda |"3dms6`Yn üLG)CZn`SYUA0 0)F6U&R ќb ^ޔcq$.ks?&ש H##ぐ\ߋ_;@WA I)T4&BZb@"K$Ϝ B7+∃xsC8=uCInD?F^!߂l+]=JDܭamWAWa3 4^bW54_7݃S +Wf4r?t=7ɨN +? N S:|su: +`d$%"rjoI=Ϋ Qp"j첃7"8(Č7Rp=!Q($dp[Lz1f|o&ƴV> +"JwF +Y+!aCEH oHX؟YQq&[2R$DtC[(Y2S\P:#SUm PJN*$dh,Z7'J/ਙKaЮgy?rlt]6x:A}GvI>2űDa3DJ)Řqִ }GAc=☮HUS2;f* &D}@d&+6r͉xbq H43X{Xx׆+ENxNI=>ܻ;itcJ +ɼ#X~'MȄvZGou9mܠTx54 +ĄPL$Щ(VBW]ʑN^ +-[F[V cO($$Uƃo1nM 3pPuW  S}ǩ } v t7|S8mL ++D +^RQ+J(X] btKt!y6IDG2& Jz,M=6\}=įhQ7΁tǁ3J ݒ:u.=8,tL.uvqc5J=QŸpЧbn|4{aTLSZi,tHx0sN;)4|fDw?N(,({8Ueij&&nAaFP* m&@EEh^VD> B^)(8֨6{fd:Ni_00IU UE!eH +1n$nSQyvMoĴn6 NklyLt]T^a:HTT9˲E|M4g6ɋ6{.b?]?JPp=,.C̜ +ּ`i=v8@Z kSv$=wehF ^]$=JUB[{I\8q UD/j-pfdtb_g&BDc"t!1ؔbkx:B;L93.XyXDӁ^oɟJu"{]U!BBdKu簮 !?37#/`qqxGQyqK Db0':f$[e6+%[O%Y6xrIx+JB^U }G:E`v֦KbL1 %G'9ڊ=U-Z=Jb5+U+b1H1 +xq*(Nͳ*nZP+hqGz`;nYDe+{@z*ä5!LqX"ݨXjlyńYfJB:ctlq 8}ZvB)Eb*M ks7]O Pte~C܏St'h(ŗ`"Y{] gu!aѪXh~i*J/T+ZymYmX =&(fC8u8!* 5b򹐒.z+%)I66=~M4Yn}~^RryCx $Ŧ!޽p$G>`8 +GQS%x+@@MQ 2 9UIiգ5t9tWnr$_;ؓV/qB1D2]P⦌ǩ{PCϪ3oʜTr"c@ $x]@@#qő{Udp?3XZS^H fχҴ7߲/D,nAh8L,Yc0cMAP>9V0`!Bwv v/KQJs⮩)xiO  +# %J|8RKZFYe2;8耪d19cX\@𭫹M)"SEy (b^=X_F`SAqBl{E TN L` +pTVӂ-M/sorbfy$( /Ȅ)7m8҅+B|q}c@wUO"d\VHEL`@ (bE-k~[Ry:@֬\IJjő&r8u9 "}[ɚm)f1*DUAdK4'MjVY]tBJ`E/#Vg=[ix q~I̡#(|2t'@+l'91Rɰqr"4J0 Qme I<E#Y+D8k`VXrP\ JG2(΋$ٹ(6Q8VlEk`(>Y-(.LdM)3 1KrhfFdN` ţ W(l7Pd NpWQ](gYS-F$_ fDƉe *4,"#B.h uY8,츰W< AkP*$m(`42yyA9܈kQ@ZU2kqh''2C[H #>pIϨ}G|QwwMt&TD&5X Htd#qU.m`H +C20}=F"!(.`DaAvKz$U ,ڴbHt"' .yRDNU9E"G9HLnM"'2|^ry}!P +CN(It{!䪔ye7 '*?N&"I;Ia-ꂳHǩ5`\5Ժv,IaCIkbTf-pdz2T2qK1_' *q/JA r1Ǔr< 492N fdBkHyq833.#V8X+'&gE 8 w(_5Ild Xo%9T@i7_yȃn%Qӛ"6bޘ'FV0b,{$b7=c¨x'qHHXu;4amays(oS$oDBt0@~fԬx k +wjbW [Չ1 ț-Ձ?-*7G򦳋Cf@ Cg*7;ly0gD|׀i1x*` ppxS,W,C6#*pD2Wqp)7 p̐΂㍅yA_x#㍘#Ng|AtUF-oƛb$r㍠}foeQ7mP!^x#XۗME7cMŶ휔2ob'x#7Sa+i{~7V8ޠpcǛms 7"Jx+Y}MV΁,sxAM Ż%o@=Mqq3?1Pjv jMόٍ"cԤ*f725Em;R*xȜ. PAd7Rn| (\uMmHJٍ (d7 I A`bvS)f7&3N bC+HбF}P$JAA᪚snrHvB/$Yvs-}UQ`tQ)D/%p7r)D=`ʁ Rn.)6톉Ƀjvp~Z&JdWv sn&#H'2r7m]ݬ8@Os?cDPn9=~.|݈P#"ݠYw,ιf!ۍo2='ӊR34 !"; q"7Er)p7Jpz4͆n73MNGPYSnkW._.I\-D'pwNe!ݴ`5nic Rn&菢 n!**n7S4pok 39 MH.TnWTip9!3)۵! +M_w9W`\IJPp7StwGwM.EHp"EKa |Tn wB%$-N 5MiMI,_{Eڝe:AAvgeMX%`wJMuXa2i2VʨJ  A*7ap<ͱK{ [_tAKHZ&$VKn &d>D:snzM#D$[]zs +׊pn|a@967И#`VDA.N+DıT\n"DR ʊ=P4#% 3\`rgY l@#WHxMuW@@ZU`*@qwXY5&/nl "_q#45ln]s\pBxk;k*bpSqFi*7M5p[ ~Y y;|J8+vQ'mwX oT%9 vb]Rvf kMሩ,!/ P)Qqjp*ƷnSZ6u;\)$eTSp어`c,sll +FBc&  ̠:`pFCab!MqN}M.PӆԀ6v&hA4,0܀Έbb;mĞ(Da\,vN)(6Q< b\ņCAtuط%a7aI䘤.@n M@$a8QJ%xd 8lhM 1aa# T$N;+̃7ÆV@ÆJG + sDkU\ѹ6(5oRAaТY-^n`HaJT.0l,MƉQ0l:(``$EN֙a+?+6Q(me`zLP+ .2Ca , }(lg%f +/`s(lP`3 6!RpPG톄MjX@aCF5,v ̽ R6k&e_ +(L6†!x' + ͊~6\3#?2pa6FAF lo B#rϡ}* Pe +I[@a†UkU9EI]@†51 dRV +l_[@a3nT +|  +d-9`b@;ZX']@a& EN$†Xs(lvTNa@ac`!Pd96 wNAiMdZ(l<3"Ph †bP75JQUje,p 6(5LP\0&M WLg9 +6TB%8lݾK4l'&GhhP l1ʈLPH`s`k&)6ʼk#@l+lw W* 3gQ 楊"&M Ǯ"aR6LmP(lPc(l8 +bU]rMm]@aB, +4'sHR$.{؂Wo†%XDaaR,lP^F R,laP$]a6$`/5A&XF ]̡Yq6歆Mds +MR(l<6X2ft6b ,<`{3ÆGȋh=a'g`mFb\d%6.76Q|T#,UPHcS~}3?;``iQAP[`g1vA`WX"`W1:$[p]a囿Ƙņ}]%f}Mysk\` _ _+!_SI{I ^^6uH*u +u +Ίr T@3Ύ1V ' k"ӫACᚼa5y5X+DI6_MFuP5y ךdT\Zg ԚBbZSð- )Ԣ 8<#PoКxT{^)Y/J98ARkE0֤4 + P"*5:7 k2X"*5z)$<i -Il !F.s^@n%^jQ]MTdv*r(puVLn5J=LnAT@S\ +ZMOT$B j`֐"Iad2ZU +WM@6)Z5}|-FUM"inujrާ8C0AB(7 +H5e0`T;QdsjCyRj"(.A" eP$TxS>R8R1\X N6װA&pǸcfT.5qe(.5PaJ]Rw9 :LI%gtqԴ\Q'ȯ$fiA&k 4g[u:E]8%v ߦ'QKB*]i=VB]BM,U@EVltт>5n>9dOKPaOp zϤ-WNp7p)X +;M[4>4 •bN{rmӔ9CI%%ph 7'D6́-% i2rLM# uRg!UDQjMzX3Ц lZ k:yK_@M=8iBʴM'̓]Ld&,v +4<1ԣ.EDBLT НbWpT^4xH@M~K7%4Q'MK(4"wf,ݜTT`iJGc۠7@0 >E"lh +M"hј]@*T49IYC)%PBN4u + +!/a "yPQ)H]?ѕcaM A-ЄFi% +x T44K[C&\~BI a3V%)'SPhr?s &4UPh#,PU``G&1ΒpdQk(+;7Pc0%(4s%LHoy BB:|gФ%)TsG 8hȬA +030,!^ - X֠!97U36$hιA ()bM /P ],- u'/]Wͼyjs~ʟ9:yO/i/Ox_^^_+_\_^}F15aO;=w/Tii7 | rY~@{WO^ B"‹\]~wM߀B~m/{~kk<8}ʿxZ}G?-o=ߝ*>_V/K_G#r/LAIYwK_(B<@/%* +\&X~ w~<s!0p_[#SGIg:B'kwr(.(6y8v.-PЙv미T0ğwatFdR)Ye~"='*OSOTʓc:b٘I*IJ92K)u&u]:zoFEX'\gi]Д6u$+A8F~wcD?:TiS^]]?NKb}NCaΆӈm7NMaAJ 9Du]\T6Qe(ֺ6u/Nj뛧Ns_kMlڽD-=U'I$F%[@'5՝}8(4VDF2cHJ|kZ8rx&殰LHҽSzW_x E +1;h}\EQar)üI7na;6G]ˇl 1j#| g 1Q&o!I 38S:p#S*48yq*! Z3 @|Wut<.s#O'?(D P[("^%̅&=!"@w-4_J"ͺ씧f%bR [B遄ѥIOFv܂utVKѱ d[ >VxrU7]Bn1'@lDHt~TOm3ZC֖sqJLnugmTlc0U~ޔSL;b}5}x*p Ӎ4ٵuO S.0R(Fߔñ]6ZFĀ?i-r "PNЁSFfc]Qd7'z>̦ID|zؠ݃XyJy,nXyrB_=3_cdZ{ڙ3Ҋ,DlcMc"eU,APn!D">5啹jC+3ƚ=p6n62لmx늀3~X5Z{'{][a .`#og^}+ 0 s<|h~Ɨɩ0^|tJ&;e3S75BSmpB،FUZ<4{ *>\fĵQof$8ITvkT4J&*g1BHrd EOAy(wvp6prd) #L1b炌sEǗLqΤ>l=ggBIzZZz藜3r$=wybm21=@Jt|B8U̩LL7>Y 2Tá̈|(; ˻N%k ВuS-$p@h3vVa$@CTby RumU#J96Xsam)N8ѠQ.[13w.dCƅңwP`%8[[,GR͐M'QHƪGsSqPr$G3XB2@)pKܑ{Lp&Ҧ3b-fhSex*@GT3\h`p>b$Վ**} 2w*$ɩt]`Tlidm̻dVN=^j&ZJIsOVכa7Χd, ###j6Z՛CY0ipCcSUrF]h90,FGCS Tm\MMS2ݒ\.nAoE/$w@ܝUz~"4;{ЛjS2CLo +`W^ú6 Aē|83p΍\ލv(nܾq˅ҳ|NbSĞ yK ΀Ջ|h .#38:`?e(,”&%-sÖʂ T3~"f3,&Wv-E:5m$eHxomNB~FFfC0SSY=XT*b|Ȍ(_f v\h!/}KXa`H;G1| YČbBYf鉀S >BGcr 73rudZ˔ʗ`cHY:%܈YL[ܢG>[YweCMNjk<YךK/.nY.YT݇TX;(v("律BN0,# Z6{o!A}QlTB!,]"L"M&p2Zg3~7UQ RDFazǾ pc5˷))uE&LVs]Y3>b>X&-άQT84]s(ĉ⶙8 +j@S93C[oxy=͎{bT!`Ht1'"X+<3{Ln/:3pы 3U]:}w/YPS݋_$; h :4L!ZB^ ++Wxn\(=HHS!:ֹ*љDkTY=_0xx>ONIDQNai !>,ql}&PVk}3E/bt$&%Ni.Լ㣭Ѧ2/rg'nzT-뇤 8H>70AJgO3_*B< 6I,iȃ8n's2$He+ӗXOFhrt/2<GYGߒ]PRօ*=I2Ri^cLDJ /g:)q0D$Gzv dꂅR^(ey*_ fM;mB'![ ZVv)Rr0-z])$-h9˜ql: a[HU&>IE! GJOQB}cOrB9ZPE"H@`NHٛQuW8,:]RJ;9 ~T~`bZv@]}dhJAqX& a6hO1i6g&JT0J:lmiI?5 Zr&EIn$_LK&S(%9ސH)< ݄sF8`R@9N*DvRɴbr%-CYa[gz-Wv.[ ؚ|%%tt5P5U_efrT'-*oȲ8) o!P:a2Sdюٖmv$L+$ +diDNJY400 +EIedERڬ [TZ!dsujuA4whtq>WC5w\0aU2YQ^(eu^]]X?x!Mj%a/k D4NiZ΄[@4 ХQLk!ZUFXHA;tA' #zITJbg FʂOʥȻ/3| &zѿ(>JdI\^(W N?U'gXXBY3L Q~--+řLfk #C.HV{; 0A$](5D4mv;Ѯ_,BĊ̦Ɣ NZHEYtR^=́Cւ?TX*Ut1^0m8=j AH@ZH^UJ'.67iNV/vIzd$C[P!+XnllS8NBĈ2&)Kc-Dk'=n!ǘ3[u"֤񠚢Gc4s8 ("NjnPD4zϢPtɼ49QQ"n)T&LLZ*\*G117rm-]=4XJĪlfr3v$>Ŭ$npDᔅ;i%1_+C;f`Pf~Ir%ik3C ^͠/g۰ / ' ٕ*(N5X.-*eWqR +X4h.Nc&A)xOIAY$IԱ\-%o|du?S +/̀N;F 6n/jʱC4%ӂKtUY7*-_n¿jS<3 B)]^lꯛ< Z8Z$%Y%XV\QcJ)Y[d')@7Zȗ-)B6ģAiT_R,YR7CF*!H1kO|2i:"# +Kˎ:"mT˦ &w~X jG:F*,:@vzpQ$@&mH+xjsuP T}BFhLiurWy? B,ᘢ&#ah{;S] r_G6l q:?ŒpCBڂEJ]3CҥE#BQb*-2jbBG_G{Ӑ8tMS49sצ9 !Q)"LkN3Es!#]69z0rlY_cd7!螚fwpx=k$`s܉*VMJ!(S?C3'SX)rԦ+RƨIzdjG=!ΑTSTۍ'MXL nYO 3S5 eFo(Y@l&vAd+:)#B/׬A孃&S fZ+>wZ:mRvA}X|aD6-gK`iՊ1X8jƺeb%ԉGGtwm{ԹF^d-6fiSU &x !Bn[h'kH5,2&u!(g2Ya<9BȁHAq曌@:9 p!鷦bJl +i:1 DBW'#ΠTk{W<1 { X$Ŵ)h*T#(JDVg-D=)(pzQ7,6CCL :Ƶ)jtGjҒUNs_Ւ_+AUI}栣'MC P> FD-8Qc_bb1Sq`1ѐ,e9 +tJX1 +ۚ0&u8FޢAU6ZKNYbr9kbβx),_M,aYj&lQXc/Y>".$;Ng-qHf?}L#,.fAf2ԫ:RXd[ޛ7f҉o$ /xOL $)O9p6x1 63ԑLtK˥赪TXn|)S _E7[zt.y%& &d75&S6rg&.n2ۜ^:~ +3٠NHtwJۇʔzw0Ҁ%SwY Y@Sb61S`E rhf-T;IܯC&&JDxX"ѬfQ &6<1BһH7GJӸX'Dad8)6 }UKb#*%0m1%D eVACB +1zZz2DѩjԕM(vK24[+,]T0I$BU'nЅJ0V1,$J&5~jS2׺"լ6!EnSR\3Mpz)f_l`IH/ɔQ +nAahHFQGuH"MY[T?jP+KULtXEFIl`"* uNP s_DNȔD%9_59=&99DإrP< f )-Uifى$Iɢ?Y ybS&Dʀ|:3qiK3DICcuK5"=E$ md7X8g-3~cM"7W6΄ qs4rx< uj8DN bKsMuL!S8 ~$uykx|5ID 6\Gf>jcTRz ,|j;s! +~P-^YP,h,Sb$ǔ?'PmX*I 5^ꍬ߼1jmRؔ{$H)Xdbihču(aYkfPE2YϧT;?W,!LTJeqhvXgb˚4vk[F[ק!Im\: _Ib7/C_Y%sbƘ+LB3=kFhAS+s#z /Qds`ʡGzo뺰*:BVpWE;eFYb(u 8"dHtG͍-c-+nF$f1p# i +N +YBC&H X˯vDL,)=$Aۉ[&b !r,"q3K"RHTE_yUöZjhYeb.|Y˚mYA2]"r25mAI^KTt)O(-%}v>4 鏉۪.̎#{CfN: If3WT(S/21Y*Dg VU1S6^TF!M,j㖢҅̚FVu~ev)hݟ& ̂|@- yO>$4Ou;7$,?x4ʨrCF4fHbKIRO(,9-$ \7džP:vWg@/Oa;x9:y mH)%%Ŧ.6r řE$?iYJ SxHqlF}Qzڇ$KqP U?".Tk302]dR.05395 # ɑ VkAf nU-h4f]psY5͓f,0;jRdy@yAW7cOiSe[ ]W>jY\qdmټ{"6.4d$qOgn+':gbp_'k7JkaAM)[@l"@j"H݋ knl.g㞍XAuTT54Y$cr6V%}dZrj!`B͎QL?ɂTVfDWptgL9z2r9m9']LXEtD))|69(x0"Y:rɆ'i5?6? eeG{)D/7Ha7<.OCa2(JRD6ͽ G|2ԬiE,ScC< NaKPyEylLl!Ȅ\ `)q ,5nCȤ_T| HyC<}`5f"AF7_'ZHTphB +_JvIw1<+D.j! M&$jHCͦ-h>X0ĉ^ Է% +,  8&}85yܷI ZA&0Űh( +"d1Y@ݐAE-)|^XL2lPShbaCz5&&BgpHG=sx@B@uHG]ʷ[/S[c +5TS65RJҜ>&6aه9> +}1* PI\B~?7Q ig%Ē8X.=uY.lweEr +BU zoa{ihka`=?$@>dj{k2Pj$x +~I:FE3E76់45*-8 V^E7T\hfUYE2ɨFQ!DkJ5!3['[V"lՐXdFK;Tb!_PF AHb$l,L?F,?# 5*Au$> ,EF# X +Vg} +*hMH pMc%G5, ;aVYBo |^lOH7Sך9#{G,jؐ[Be~$WM yY*h#gѳ9irg +$b4·jQ&ܙ`lHc?10X-б7+V[cp'lB+ +ձ1-F +P3T"& qbЬb e!gH=_Of ʓM`0[qQOD6"T pXeL$hMӉ +X"@<Rk v;Yt"5X.Ր}2`Ԇ@iLi `ZaFa;ՐR7ۭ~" ъuj z +6wkA]d :j4Yhl 7sԕ%5Y8kASKvx-Vo>s}˗ϯ߾yP~{v/W wOOIS?zVn5~w|_wZ/}e#RE(Kb1m.AT;#MT^?̇~ V +}<D@VMC|6h ajѧHm2f]{I~@L]ܞEgB4C)}FQɷzJQ-y ~L(!!nuGt +e;Hmt3tnlS?,fSC=_,{pi;t4'q2 L;|s|C|;SI6;㬊}ݟlt@SPN;>m>v;HnwGh0kyzhj1/hm4ww? דF5d;>b;m +xα77%%w7ɽFыwv2sNXlҭG_IXcxnw{<7x}ld3N([b:[%~]g_e;_"OMòyOo>;;9/on?~dz~Ϗr?/?}./zϾy{sSu}ק<~?_ތ?n~><;T<>+'I#?pp/>= ^}CjLnb?{yzwbhOϾ}}7}zrW9A޼|m=6{g->Pt!˛>0wɳ)mfx7s6No7xwWon:_?Sg *m܏vj^9}14ӽrWNQN_]w/WgozIpovN-k{t'n5P/RNtm#]پ9N:0)=9cs%Ny&1M X7oΎ/w>n~E{}/īWޜ޼]vlh[e/P0ۊC|HVWl]f$PPJׂpQӓ}j0ڃ j 7o_~ur՞\`Jô0.~{~|}wuyz w^]9ю7wTi:Sg6y@kjP;bJیikC +:_\]me7_>e.|YdWMۮޮ]{70oG=Po\Qan8>{7wծO;C[c?ϱcO=yekװH?kgAGd861+b|D; vxuu6w=P0}ү!e=-G0%ɪO9obDx<;?"eϯ{}u~#ԡ2O|On {Ns]*.`T[m?~7[I;vr6_]&~$/nlQ$yw6]>l$fU3DOQ$| HǗ_leCͽٛΏON/N/o>?n6-3V~t7^h? +suWn-aqwƸ8mC2CJ|a>nf:q-ծpWkW8 c;{dŌ +ma)j{D?뫋--wÍ6߀>9j `~uv~U m`6@_0MGC rviKrn.<إm.]zB>vi]zDKWttcarccKn6jW8>vi_mC۟̇\'M~y~Ylvnu~<{g{4;e/,犧Lq:)))=l/x¼q{Y'OZٟ43?iI__bU<mbpg<E綞'4=yriRII\^\fGA6IGsxˠu}GGG"=._:+z|6U>Ik߼W\IwەTeYwWo:g%;va;v#<}|NC=D67$=&ѳ{|{l>-FvbD[0<$2~(&]Aծg7/NownKg<v,k{wj={l_Xqg^ݢZͮq'vvln"8J֋5Nϯxg߾t]/ׯ2Ֆko= |qA.^|lӵ*>ROު|oOO/?'] >]|r}yhn6ǽqos)nsv=EGN_x7ym; 6_}vG-H]> o;=wSNyV.j1WbbrlI6o/o-QW{KBdwbK`[%q-Q{Kuk4{KՎ[o}ak_Ҷ7톁ig +q<3d =юl19?}6ޙiܝ{w nw aJ\[T;p'bh#;:Dz}Tmq7vTqQhN_w-b,ի77.ק/;iMX هϝ,ؘb]G[RZrb[z{Kxoq[>eՐPxF':}rPZyӭ"6fgDNjC;7임^ƛ&[,x,coovjoƛc!6aM(W.mB& [}v%_ζW7}am?^'[((k=H@|{׾gC ꫛzogیtҎ^,Okb YO?ܸ6tsz1=y'==x{=zh~豟 ߼=\ R*X6JAI-šv#XpLa^ a<nz m.=:7?Y͆ +_`P_vO^?c{觛J{tGO.맿hRv[A|{uԽWP +ϺܓSQ(yu-NPP^C}p.aGR6D82AwIAw_m CrŸa]:8:oΏOɊIWXp\A?Yv._Ev"iۑ 0nN7?.sccۖJ5$[Jfn.j߿&} .p{nӯNRwo=lqA.^{l o9x#^x @fWX V.9H.˙R{/k>Ys/oW~s/=Vm/峟?_ɡ h!=́Y]Ba$K[xϣ5gjm~=ŝ{P# +;1-?|q|>do㭽X;s? 볛۔N ܴ#w#ɏN0rW字o%]4kKp[3[6aALq?y_La%V]?eeŏ[>PO7;Ā]"/{sc|J qs_ESF9;x|_=YkGk04> i1#Oͮ#HΝd''? +="m/??]?]/ro-&eG~?4J/^z`fǷ'_2zYYM_N~'>ʝ~m,c~F>|jR9t`؅ͪ >;N>/HvQ=@vcސݎ:aU~/hH/ruEoWB~/︝ꇋo1wOO?hZ}QJѸߝMW_|}6MSZ8_~ 3AOU *_=z7m SGQ^ҟE\g9U͇u`~M:t.ԝ>Z>g'{ee/x:;3*=_!7(> r˳.߮vE }nW?YJȫҦbdIVO6mFm־:&G?֗x],gS:z,Vf@xзvoV]cqyme/ 4_v @!̛T5(Zw?p;6k 7o^ fyE\><=><귝{/=zz}پ:?c\t}rpo^$!fO9k8o^?\g/ AĻ5E ׿b]*1锔M~fG/saZ'vIwE/޿V *uL_iOcSm}o۷pcM|Mܭw8˂ȺVSpm8򳳝S}VpK;ɮtQWi:({})w|w?{wow~w>{MLϲSI}+RcӰ;qkOGXj?r>Wy:=>yw%~{W{I}|6~yb)t2O诮Y!y(S? 7#q\V͗'W/{ym֩[?IWW۩ﶗWߜEWTr|tL-nuC8߇>˪}%[z\4x5 +OtvSY ZL=? - ˆDr5E85GO[i!NIϦR2.>5@/.]kg +_HiCo'r( +}Q+ő7C>з$};>>$v4|k笕t> +rttg_Ϛc6λZH%o{yhIF_z/Y!wlMp#~WAy%GcpwBl L9_ɭ:W'M9?dwR>ɱShtm/ YyuMh5V7w]cx-.}UbKZv@?G]}V2E&IK RS*o-r!@ioC>z8}UַR1ajX>e HK)ƸxwߊO'!N~d䊅Oy?5eb.\@h=#DNuZbob +#1 l9:5{:A!RJr^'CS[u˘Fcjj(N +4YӪ6edV~VSJFC!d#PiNQ6Α6f>FEؿoM)ϺL蹥3J_"O5GTi~PTk2"gά6HH5J]_[kXJ<:r4kjmny__2J~;w(}˛(akICc`۟^R%zk/]?:ߩ{J W* +Ӝl$Nrɦa Dd*!2zQz3ÉT LrӞ$~ީN4b]PI2t+h鼤(ԥ٩W w1jSgt#ӡ +=][H8%E\E`SS]>II 'uuH?~OlA:^z UY;Pc\&ޯ2 }rBp+O"UmFr$2H}C?Rg(%߷'CZwY o1ƒ$^7UBee}7^oP2MX[46}/tmYWbm,T_Re!jTՙEbU!QE_Xq_Dt +!A oAZǴ'G&վt_^6"o@;M׻\rSf3=W[j_sQaGLfe\v;{tok_ixSS7i#XC`wL!@ho5:6HcӹoǫV5O-M?.VD7fi@ &|z\smԯ-n]wm P?` DtFDΑ܉?dȽ/&6uQץ dr%1d];b0 +!tv96IdyyNnXzQZqqѬOvr&P'RFu}\ds]D~W4uWCY:oω)A"j,u&זލ@-Ht\@Kn +jo\l\snY Ȝ,TguBOq`ŋ4n*Y7Nɘ[G]dI #mŅI&5}hܻ}DC+ lӍHR]~Nܖs/4~6۷@u!ِ9:nWbmo+O(>%yNM|ru'C*"I;C'"6hgO9m[Ag %tT#Hb8]%!K]3Q?w_.f9_H|bLS !=GR&?{(ku)'϶~{$BV@mxx7Dv7A߻:{-m}'RC}wcW4.嘮<ooٜ}?tM˵Vw`*SŲ'Z_52DӭBX@r BI#F,xu7֬7']*ITFֵc_}W )Nx[v|wn)^;=Z46qpG*78Oap2~;ͧקy"aJO鬊To{"D|^@wG +*y/ 9#ۑvY}d\}qvwyauq_C6ҟ/{{v'c ᷱ;~_B$_Myql:9C+OOn>o|׫Ϗ/=^g{I(~ dLL4؄&L|M' wι;ZR$J*5ShS{fGrNM;_?u-\!|Ë)rh1Z3/hlm8ٍ:@đnOMWڧ~fNh/6&7"! wo83Lg F=>yp@זcmfWf}'&36t0&;d_޵}%~!Q>?߈7t?pL=YRkvrЕ5=:l +r3չaɭxákJIyB!&p>ЇjЦO-ä(o` SEߠ?=`xTfsOvR7)˭jRsz8QPoI.JE@%O*  P>w+B>Ua-gdn_0%2x`(`@5t hY(P>e8ҭ 9Ր&#f&ރR TmA <9WӏXi`Z”!EVbF", ȰAily@Dy$yMGز-PďnM$Ž}!kqŏ/ίlͶX>ݾM1<39=W8ʀ`~7_`n/nCDEx }{q\.9Bbm 3н^dMF_!CC`:pPd?ΛhGaf @~Bb" @(7aT&x$ă8a0!5l OHK/ `b|>/TPqǂp2 [gaa7a3`ztp8@ As²|~DG?70seԏKGl3(>;z ы&Lbv |tne3PG0\#82+17F $bGQD3pf.\_?yfD, Xs887`@h B1x)Qw#.L 0 +fVaR܋ *;F$ypCAdx/ &#Un2%iaa xa:5L'O<я^">dA("הAF)Q#$J1xaZ!K݋4VA}~ju`AtU(3:;ŅOXņfx@@ڀ7x=`L} yEY6*S]L4@ +@@Y^۔ 4A@a%PYh GF0O)gՁ@XMp&BȂ Hd##B<y40߃P1!^y& ښ Q}H յF~RPn/H Ǒ;IRZ`dQxLg%M $.ACeLKhcZ +`ДAHuMВ;O` J <n_BY L ?{(+ۇ4ژ 7<BaJt$ 0W~RPĸ>}D:lq!DKhȾ qR(8^Ar7a_YCmX8P!!o(Nz$Z$ڍA e20 Z n@S γo0-ɓ%8^(x-u!rUs;ip +]IMb{ʍN#feL<[ +̝p%K=~Y[ё)Ӑ<~OnKhz7YSMdb:/SaAig4 7'SNŚ\_HV l`x8 ح6oe(9'dPuS ^ߵ??@v5s[Ra:JAvjt榘c>3_xo2,o^T46b|H EB[BJ`-ȆȎlMUKKq(Mto2Vbaw܌Y@rM(2b`vֈ<G;Pn?ڃyַhM&i ϾmP(;1"\3k¹xqT6DLVk1{oO{B*aT-524ZN3<6Y?3ӈ"n|v~ ]sgGfp{TkM  ay\ ;=aF{ð䎪aUUX:qM{(?0]rJ#d<-[p6èW(:=_`6F I-D8XNn;EqAZ#ASip4(,$i=YՖrG`8P<,&Dz;g%>&ɛU| /Fg1 +$ t' / +X/U%[*DYC3rZ >dA{8\2\xb:%L~4ᄿWyL2;;`;4<"b. :@{[Bw:Ur`iK@bq0GR JހscF^3ZdOU] zP^m]XҌ0BH,e哇[g-Uٝ[ ޫ kBq&D؟פHh,ؒ%B'ب\xR=|\0aCUNFA),=X]6f"q}7ݽ|pmb]v3.\k8s7Q_v:;]ݖn5@Nc|W6o@&NK0 W tPOP7 ihBzrN$8zwr} XƛOB@^ +0l'* +?RnfE#ҽ֛Hh10 =Jx +rнA[/][N",AM#vk^W{qxzψ +0] \Zw{-ļv!| +oHE_r"xdzϬKzI17Yz +Гrŭ8]40D1mP<>0+3/|Eo1bd/7gt緪.)/?IqwyMV: ^#鐺2* 5!+h-GB/qŸ^"* Bt2?HP:9ŘŚ&/USLhl;W@XMR%"-V بAQڂ= +&׬f*j" ;o%{$Vʸ6bd xlnk+2zҌ\-h,pl^mbA.nw#I쎜P#f'/WW=hVWٍN3~F]6p͓Y2Sngp0Gz>4+/ $;u5s\$򢧅,q<]ՕN3R נ}&"?`B@og~lfcz=<Gڨ_reݗ3G_rw\7_^x:h8=#c4@Z;Џu{ RWK! +[\Ci2/-fotkd==? *7^YEa8ۀ!H05n~. W'2Q:7uxZΓ*U#y:h*m+tۙf)t$6Iw BY!><ՙ#ڼ{ceȏ19]g`>W=W-\fZ3838){'\( +]ۥg‘fŴrq:rr3ׇ?ד≥g/6KoL6zôǬ^T9 ? zAoF٩Mkߌ.]kG 2ެ7?z37ccbsŊz]o˷wz{5;^ 7]{=eBm/z/"~ɤ>hW8ԇ_G}8U#@I\'>4O\zLԞgߍ_hЗ{:a7xY2N/r=b]}cпyWb|vCC :^M4Y/v~eJMtp0'dЗGgq`]v fp>:,9oD+jEe7!h a5`(׆GahLCk5tOa@z+ .1? f ќ,]hhMFsia-}^ l cx3"̘jsϕ +Z06?^Oemgdo07_1n[م4&Cif8scX\Xw7lk1iM-'0ѢkFQiV.LolڟG}hͯϵG}mS6'3جX\,Մrp:$rMT]bl,dŦ%va8f52\LQ?޾ +endstream endobj 22 0 obj <>stream +A6OmbꙪ}%O:c2&rY _O.Wv|>~>5Ө=H(zRߒj[fO뇟L秃y??"c~2&Sc0S4fljɺlWKfۧS%;{9y 5\v>tn]o=2 m>_7CbELqDQ[tŲ)~>NŃv,ajkWE>YSM6O>ɖ"cv=WQiwEHVwx-bsyYA-ږm^}\fNwdbgGhxN~z! 1_pv6[ǹu^K34"Gem-?V/yjm0wEOk6l;ON3jmtٸk{5}k^Nl ^}fOߖi~z>q̿ϟ׆{꓁>/-goE6y[;#U|>D}\}|O |ƞKC7pwnl& Ƿ-P>g4Sѹ|Z75G5 ?zͤMz'jy4ŝ;N7w`Axl5Yzf)獶k"we[4=b:[lҍ/5hI*b^OW\:"u<^^bcx$7x{ +lv;g}UJn1N>a7_C_hd/ GMS*Y$={P" E-( +i[EP e逭ljKyzTVF%Ryv*N&ުAgMH. :Z !t }W|"PpUBQͯƥe +"hJ2c:)nO`iTekQNu?uRDAwL +DV SыV($SLؿK{Kw8usF-2Fq3FT}j.M"mAr-p^|rlFc|hr=۞ӵpv$1;62ҽH0=FP>-1BeZMvK;AG4"GԐ\đFH6QL[fڻba*+';PYY(m>N*D@G-$"} +Ɇ/U]wۭcF' _?~aSk>Hki,־@֌A{BSoBjO1.nybZyNYMS;7ԵǬo$OoPR<Ȟg7a|Vl8"f$`[Q"DR! ˕¬0¦aL][M Xy9Rh9rD",%t$Us=k0TJj\YNGK7Z^wU}Aɕۂ"Rv)i*+AjȄH*JkL]~Zs2DCdVk. l"E=К빆kh.蹤'$r +VZݔ?v[E):qLC RE,ㅴBVsM n\;]bH7bݕaE%*eўRwaP)d͟+%qʵܝ ] H3uQ ϓnGXGo S:IiL7 YDZrZ'0\FCvw^ Bl +SgxWnK}aXנ]?гo֖Ƞ߲^10%0;%6zuV+:7|N%I-qc4-&끧hROϓP!難1N̔ Tܹ'SdhRȴ2K-2nWm:Cc4V<~>373H0ŧ#υ\-}&7*cIhPd꽼;YwO+]3_>SQҽq䜏炵84^97`ktYЛW@osnR'K:Mf|2`> +mqKuu4=4c_E+O?͎̳)t YlO*El2৥#?-(^3 +rXf֏OqN-,`p }ۉeHc#LD,IBB"c2IVȬC 5A1-Ǎ3q;nL#ޛG}c'>!%qN;_TʆhMkXyFqsAx;B48|0DPPő,O~J:4W9ɼӗ{'h@D<6yd{ 52A h]&Z@"7k o$8r3O, >Dt#if!uL`O_^G/xM7 +Wv{b㼐ٽP5qænB6RahM-!%( Nt 9#v# %$.rɠiAhԊ鯊Mܱ};;o>iz"~nEռexkw_:쐰i秐QS`um`pF[vȓؤJ|}<4Kw9ijIwS6H.SXYnn/6>等C<th_4;berxtFߩx)M{@+>wS4&uDԔgjdlnݴ߬7!kgl-?%Jћqfߒ  f\̢7W] uCB(ʵ|_o-T'$U C7`;._$@W.ZA Eܨa]EKa&h,Q!'aw,jEXQcnAL0IyQ{HĒ]lbbUVfDuDH4)iǮ똑v-ѢDM>& N-,Df>@{k ZP eLnrOV3x6FԔmGmwnMA]]'&lm0cgFs-W2!etkkgoB4>c미뻰?l`'Ύ';}.қs=_L.d@U6unvw|_8.fv5^s+YM2i3RgRyz1-f%đ*CVR7E9Q7;m<Dyn"NrْRN>Fo3Admo-P8tO2C+,1"em)l~G[|ZЀ +PSMCq/`ĠX#/@Ô}@S9W,3ޫ;boӭn;8_03N,? +-%xfcb\׭fѥl=gR CUz&1bh[![y[杻7/Ao,|7'a/I[ ʼy_lI QLC t3iPPˬypZDPֵ g 94s.46YI1{daK$&Pt~HJaٙ8Ξ`Ϙ@/ic](Tq3F:ࡾ):si}+@x4?9 - SCORv>5-!sj;_vZadΧb]`^yjxO@$^SՐVteo"fy7ĝ%<`d? ͔b#hF{ّH 9z 84FI'ٛ)nܜ2j< ߟmm\[g'&?*oO_)qwxNrNr ȋvx4<rҜOwG*SnNH>Y{Lc=ϑ蕱;93;L:,Cx% Z$G}fh/l$ݨ|)tw2'XGvu5hw?>dl.?mLf[ڰ/,Moy <~C<ɑvP=% +v_q! ob*\n;Ei)lx!Tqx3u}gN%#VA (ݖI 뗙;0LDKDuG|  l1Q9;Z‚畦9P!R.H^Q^^솔FH0ir7~lInbXO,5 X(單›<y?kd^nQiW>+'rjeBt.;+R)$jY+3<-Aی(sS$+s%,Ӎ ttdHV_(eDŸ\n]{Bܝu{u@,Cd7-J" c_݂N;bBn='ϓ^yCɫ + zq藤]mukqWIb V0LZnWڲjJA`I Q@픁*pQ2D:oH(NDgqތZ JvGÿ>ThNs6W UEhH22E|!LT(1vl<ِHv- }<ϲ8"lgi9ڕh%53:W~;eބ{nZo /b>t#9zcq]?񗨟}~Bb)E$H/%.U\o{~蝋%x=sh[﬿.<곶#wUg0oPpO3Cdbv+jLK񾻰{ Ǐ7,Vt +tbⵧC[Ⱥ- _ .Z1Fvg@©Vflj" #xHf4tjnr+ C|-냜"[ ϰD9Z|[(|Q;g%5,4րͥj( a_פ2Ք Vhm !V=9(KE\WGkR|##y9YmSk^]0T Xk0sXy M!:~.*XO-9>ђ%MM +#&9pn ezK5% +󐩵5#gAfqg導'-54@X1OFuಊH +oqry!V+@CJo\>8qm$7VPk?#+4pˇhܒڎYXDۏIaE _6pICD<٬X{\k{=FX^uZ?v xBb +%4D*N2D~o`jNk=|~Jbl+7a\>7H/Š ⯲k+Kcۡ:b"52S.(Ax_/o-[mdGX}$k G +@ρ+3'h'hѻšxB<$<ݗBX X2'o%}<~S4[EXx+&z<+Қ)Qذ#;e-1…*u]TŻ=]`3RA3˼2!Bxաķ ëTJB6٧a-t1Z8]6Ho٧5'tܘŸbƆa2.ɼ.K.I=Yѻ]ǒ1[g^Chqj@Icu}{O?rK4ߏÐIϩ/>]|%6Br(:K,( $_8َ5zs.4 ٧ߓ@gEeKX>agFDO~u/g]2l hYljyҢq -k+HI̎|> edЬU .o3?}cwZF + >K颥; ooV6hoexlv_@,"4z *Cxv qVH+nwi,VS~b;?\ +uD딏5І;is:5V7`E{vŵm@mqDNd Ve{!F0OYd@+bh`!=ܸޭfŊ +0'z2lٷFKyH0̿6T4r#FfD"n ZrN>tr{Cxɽ-gAZddAHF9 Fyω)2Hqw4d38/ZU@f< "ii` "ߕ':m_=QV=-ych4d]tX%@싶QRZEʢnu;}'w9ա#%m0yŽ9 yyBH?&_.QAT^}P{ g 5z]yw8.xIk*5hYrK#dE%4^>:FT[8&写ˆSsa]N oSXw\N˴fiB0Cj* $8'Y57WLrtQ%)EԖW}b>*ڳѢ{}7飴i `sXln8P4C1<5A*k;ʛkɷn'DJ+;lCNj)Ir߀6re^z5Ƴpy=NS'Y'.5:h~=ֽ gu $YMoK" nԧ*I*E=a3*@SOVȢ"x=w5/A[wlSE >ɱ +ʐW`qQdHJRVEu[_k2' +4Gh@uګJx57s伴R.Zz!祅ЮX*x8)@Ppy]wv +ЭK89( H@4^VpZhEx"\A@h>'2 k&0jM,ȕB+1&LڤJ.'-5t*gP<9)vn^ + B).pXS ϲxZSgO % :֣\V,yb˂8Ijyz$.< q 71[̉d: m,gUlꀄgRG=ܔr#t CϮ[ -Mk_Տ-vqRW58s{$tσ*$aKAᶸ2GeH QG]B(b 4 퐵 +§S-Nnv:uLbDw+M%&B#eִa=9=QdقR]gbMu.TѶԠ|שl|h_ͧ٘ch/&{ùh~SZ3-t!g˭sKAYH_-a1 MQ"\2XJnI;svmI[Nׁ9a,D ~ -릏RGT1iE٫޷jIJ#~EtRM9K)Z]F;=I2+vKѥ'=44(5t]:>M2zwfeT[SK\^JLf*NGRK*VEg^1DUۥEyfmЄ^M~ +W팩)9C5Jz}@Yxu9>@/qNWȣJЫ(k& (q>99yENX5(q'((׭TΨGkK^O9|КS>(q($B[;7M:_A4[V&j&'~G4ؗyAT%Q8DB63U|c|Kש'!`e)i ߾V\% AnS4\DB=.`?ej0 1tLSF W@>ī)oLE{SAޛ[laN +ULQ /P{XBFȇ"uv ̇yoDƭ\U3!mA0[,< -D)t3bյè#3$FXZ"2j^o`&/4`Et%aQƘ@?^22줪xoNp7l}2{)g)33O2캹wf-9Wd)/3o\2 2VK{g=R;TWfzi]fnM\!^PKYSKeo)RU ++A\h TTedB4 eJ ;.x|dB-E2(zݲ')^#)d`u=*)%NW#@-#-.t̜GZS$߱U`rݗ^&fNqmK̂SJulf^oNx57~hKCZ$ّaŷvx$ُÝd!$¼ۓd!$B@.%cB +%az|y=ddڽ<X{$Iݓnu +ſfyCRߟF[<@b JW ٰX-ߋ촬[eߤ5 +jsBjp)7L-}Ћ;)[bL~[%}eb9='' 'O*Ok}R^zf{>wM2yQI{Zľ>- +oVD&I͡TՍ}bP'ku7 +ݒ'uq~ľ>L%d};O +Ds>qh{>;%i7H"TWbjB}CFJw#="h?ivYSҜ}uPYvr& U.tBD`rh#aT0ITԈtlnU(Q +mOh[WթVtlQ BjԚp䭐-٫TWvjMxwtiSOVvpor er4\Csn`̎SKS?u'$F,<ڎ{z'ŸU +?WHPT5RTU(+1jU n\*-TP[5-Ta8\9PREwZ,O-TVlzzU"lp+|_,F{_f򗢡͵u*` wր:»S@V_X6cbj=̻-T6AC:z-tl[]'F؄0VlSDzsOv'vSR ϒ4(E<<3Icd47h=[t@&3&x<G" -J rj>?d +{p+Rrl6#r3aȑ%]ڬOk) $ %p$NDSo`ō7s+NRZ4X/7)^$Hcm)UX݂0p 6Nz'&obːWiwEM)H TQJ⭷Jw Na$OH֎ +WxP6G a3t*OnAS^E}PBYLp +7Rd6`\g%]Rr6U^bI3-*UMuAx7o^2i-}ѥVZeyӢzf~,s4BB_h"-s#TSxz+TK +]GZCE)Zo4#*kHIԚ(ᇾ&%Qk>" D)~Q؅^^5)I%ȃ0ڔDNW)2}G{R= +X!4bK"u)VHg(-+|ǣxbnKB+|Tb.-Jjc(xPXPۭV7< Z<2J9!UPم$a4 *jM8G׍UVv蛛 *NuCAO. +|] +ʩā4쁤zT+x|v- +K}C!ߘln*x(Ek]ob2I<x/R+w(xk)^ࡲ#iW!M/]jzd +*Gگ *Q=P.x(17jD N!ѮO{PbR(*x'R dׅƥ}ewL[-6$K gO/XfZӃ婧^6cZo7zKj-lmц'dzvp0mLund]`;?)+UZ9$I,;e;X9u'Fk{Vs_R,,ɇQ_@rOk_WN_B{pʓB#| +^8:Utwuz8/IO,B?C./l}64R_">ҥ*bYioc .¬Ӭf[yk[<-S^隗 C>~ G:wj*\-܋ RhVd +7WG2EWzoVw~SnP9*"( !Yo띐`ϘMKgҺ}?V.*۫{_+kxD|Q @ّF9O ghFhȏ9nzTR>gzjNt#ͳu8On6"?Ԗ/^gpygO?_)*-NxUt>H)t8_!y.>ӣ#*I ?@Ս f"0Yݘ$_`dP^zXp ð7h26K/̡ob5 Z+%LFuy Zy}eH ZɉaM8A.DGh+0LzzaC=E``ޛgކ% Xy [S[ΩY"8u38 ti"hό (~8XxRJ 2"w(-7B wea-!% +wOeí e0,bxt"d斺X&ޮ(_./͵~@7:yCҚo,fWiY9ЎBU搽Y>"뼞 +ֆ|Q +By tB|;wg<7PV!0eP*PsC [$bZF@c9׳(N?Rv#ʈ]??7C9O!hKAγ!)"dКRC#w% lK*fa9_x:#,>"s8)MwOBby*["~娰@a! \=sg=܊@ ݳ\޺u;kZGh|Xfg>iS#8XZKTq0r~H,T5Oq`{յIt'BҔBY~c1Ayc3R*biJ]F4F0#t8 !Fn>H ,QzsQq}1!a H\7 ,PuW)WdmmIݬ2.rDuWWvQ=myeDf xZmUVrǕZ|i\ŰwlL +[l +͡fwp͡fP{qsIB'CM97/nU"1?R4^lb9T0Hq37Xf|͡ʦt/nU6c/0PܤD~aG&4ͧΜ\jo\!V-!4JiZaKyuY[̊v^+L +E@i6pc[/A`T{ +)Ӽ`$ê!('m{v'ukbU5p6B*CP;\ +idZbɧiB#OVo֮6}(\ƗeZNѢ׻LŊ ̝H( Uxȣtv48ѷru8֕tq9=~%SRb`<axJot9-+hRo!>h. *v䍷厯$R1Av0>hRW?&5Ae}}$"~RyH4GIMPhc&+c&5A>>ѴAs MПkA߫i&5A#M_4 :i~=>h3>hJg\}$"8ImU& =>hRTic&z1As MPuk5eCJ5E:b߶6׌jYo)͗ W7)υFmtah1U]憚Ly+^~!"#[Y(HX؎6#"+t|oveI +[:+$o{)r0YMOVT@]).ȧžeP6Fߙ꬝zTLP/jl]ʗ\q#}9҂n\Q!#C֥G,E"xtdIՅNOm1QԈ؀ +OGSv:+Saa9uF{Ȃ-73F>Wd|&Mr=ǟ#*STۼf)ڭ]+9/[U7emtRT=hw2::إeFm^< ~,n/KۻFu b2m'6A2˦r. ~ݿ>zN f^Iͧݗ{!M%nǥ^T>ҥQn#tSwxxzw|dp9Hҟ&>Xj [[Ya +4Z&*P4u3KDJO߀Cf\][d3ci;R #q YMS- CRX)]LtvkN!v,`gA]qX]qn&Rmzr50$`#4+dP:P)΄ ,r=ǔaItfq̺e@áYE0T4g6pוlѠP51!tIBl- +\.A)6i9*' ;^T&:>4'Bg5#xK҆q߷l+`X#"p +hI z&%hBl mCW5gLF⹞BL~f`4tn5[' P x+b@04a G +s "308E#1&̂Ҙ R%1=HNBp,_']P 0Gll1z#$f*a7 uE "$8}nq4|aW@I'vABʼn2&e2|/IC<,|pc<)ڍӰ=&X|#1q9 r H5Q q r|Ra단8+L 2,n". + xPB *=f!`!FΫ(%s@@lJ ($AF ů N!Uap+sgRT^^ӎ.@T6{$hy-$ 5)R\S* )iyBlL"ZT",(4Sn}LD I6Ӻ%>(3H(pZ\6(%hPa8U:78*3ȉC?t +AQ&RH-ZkE֡&CmL*#YnQ` )/ +½ SǐK g&c6@t~"Prn0p1%Ȣ,D4!Vl<*<: <X$͌()?ewJIui)5hu1&2{hpĚ`Lq`2Q%Y5q"{<*8‘zp-\bEI01w6O7?m׺{Nk:%g\J:nz Ptu6܀ +endstream endobj 6 0 obj [5 0 R] endobj 23 0 obj <> endobj xref +0 24 +0000000000 65535 f +0000000016 00000 n +0000000144 00000 n +0000042781 00000 n +0000000000 00000 f +0000046539 00000 n +0000148486 00000 n +0000042832 00000 n +0000043197 00000 n +0000049522 00000 n +0000046838 00000 n +0000046725 00000 n +0000045160 00000 n +0000045977 00000 n +0000046025 00000 n +0000046609 00000 n +0000046640 00000 n +0000046873 00000 n +0000049595 00000 n +0000049791 00000 n +0000050853 00000 n +0000060193 00000 n +0000125782 00000 n +0000148509 00000 n +trailer +<<7107660B50FB86429E9715B7B157035B>]>> +startxref +148689 +%%EOF diff --git a/lib/INA/images/logo.svg b/lib/INA/images/logo.svg new file mode 100644 index 000000000..8dfe3dd70 --- /dev/null +++ b/lib/INA/images/logo.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/INA/images/vertical.png b/lib/INA/images/vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..35039daedd6e1e10fbe18b22ce79889004055a90 GIT binary patch literal 11540 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 z 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

7#2I~wUI^8h(V6_UD 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 y^Fotn#}g^lUyq0F4>MJ* +maintainer=Arnd +sentence=Read current, voltage and power data from one or more INA2xx device(s) +paragraph=This library allows a number of INA2xx devices (mixed types allowed) to be read and controlled simultaneously. +category=Sensors +url=https://github.com/Zanduino/INA +architectures=* diff --git a/lib/INA/src/INA.cpp b/lib/INA/src/INA.cpp new file mode 100644 index 000000000..757a34b44 --- /dev/null +++ b/lib/INA/src/INA.cpp @@ -0,0 +1,1143 @@ +/*! + * @file INA.cpp + * + * @section INAcpp_intro_section Description + * + * Arduino Library for accessing the INA2xx Family of power measurement devices\n\n + * See main library header file "INA.h" for details and license information + * + */ +#include ///< Include the header definition +#include ///< I2C Library definition +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || \ + defined(STM32F1) + #if LIB_INA_EEPROM + #include ///< Include the EEPROM library for AVR-Boards + #endif // if LIB_INA_EEPROM +#endif +inaDet::inaDet() {} ///< constructor for INA Detail class +inaDet::inaDet(inaEEPROM &inaEE) { + /*! @brief INA Detail Class Constructor (Overloaded) + @details Construct the class using the saved EEPROM data structure + @param[in] inaEE Saved EEPROM Values */ + type = inaEE.type; + operatingMode = inaEE.operatingMode; + address = inaEE.address; + maxBusAmps = inaEE.maxBusAmps; + microOhmR = inaEE.microOhmR; + current_LSB = (uint64_t)maxBusAmps * 1000000000 / 32767; // Get the best possible LSB in nA + power_LSB = (uint32_t)20 * current_LSB; // Default multiplier per device + switch (type) { + case INA219: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA219_SHUNT_VOLTAGE_REGISTER; + currentRegister = INA219_CURRENT_REGISTER; + busVoltage_LSB = INA219_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA219_SHUNT_VOLTAGE_LSB; + break; + case INA226: + case INA230: + case INA231: + power_LSB = (uint32_t)25 * current_LSB; // issue #66 corrected multiplier + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA226_SHUNT_VOLTAGE_REGISTER; + currentRegister = INA226_CURRENT_REGISTER; + busVoltage_LSB = INA226_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA226_SHUNT_VOLTAGE_LSB; + break; + + case INA228: + power_LSB = 0; // TODO + busVoltageRegister = INA228_BUS_VOLTAGE_REGISTER; + busVoltage_LSB = INA228_BUS_VOLTAGE_LSB; + shuntVoltageRegister = INA228_SHUNT_VOLTAGE_REGISTER; + + currentRegister = INA226_CURRENT_REGISTER; + shuntVoltage_LSB = INA226_SHUNT_VOLTAGE_LSB; + break; + + case INA260: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA260_SHUNT_VOLTAGE_REGISTER; // Register not present + currentRegister = INA260_CURRENT_REGISTER; + busVoltage_LSB = INA260_BUS_VOLTAGE_LSB; + current_LSB = 1250000; // Fixed LSB of 1.25mv + power_LSB = 10000000; // Fixed multiplier per device + break; + case INA3221_0: + case INA3221_1: + case INA3221_2: + busVoltageRegister = INA_BUS_VOLTAGE_REGISTER; + shuntVoltageRegister = INA3221_SHUNT_VOLTAGE_REGISTER; + currentRegister = 0; // INA3221 has no current Reg + busVoltage_LSB = INA3221_BUS_VOLTAGE_LSB; + shuntVoltage_LSB = INA3221_SHUNT_VOLTAGE_LSB; + current_LSB = 0; // INA3221 has no current reg. + power_LSB = 0; // INA3221 has no power reg. + if (type == INA3221_1) { + busVoltageRegister += 2; // Reg for 2nd bus voltage + shuntVoltageRegister += 2; // Reg for 2nd shunt voltage + } else { + if (type == INA3221_2) { + busVoltageRegister += 4; // Reg for 3rd bus voltage + shuntVoltageRegister += 4; // Reg for 3rd shunt voltage + } // of if-then INA322_2 + } // of if-then-else INA3221_1 + break; + } // of switch type +} // of constructor +INA_Class::INA_Class(uint8_t expectedDevices) : _expectedDevices(expectedDevices) { + /*! +@brief Class constructor +@details If called without a parameter or with a 0 value, then the constructor does nothing, + but if a value is passed then using EEPROM is disabled and each INA-Device found + has its data (inaEEPROM structure size) stored in a array dynamically allocated during + library instatiation here. If there is not enough space then the pointer isn't init- + ialized and the program will abort later on. No error checking can be done here +@param[in] expectedDevices Number of elements to initialize array to if non-zero +*/ + if (_expectedDevices) { + _DeviceArray = new inaEEPROM[_expectedDevices]; + } // if-then use memory rather than EEPROM +} // of class constructor +INA_Class::~INA_Class() { + /*! + @brief Class destructor + @details If dynamic memory has been allocated for device storage rather than the default EEPROM, + then that memory is freed here; otherwise the destructor does nothing + */ + if (_expectedDevices) { delete[] _DeviceArray; } // if-then use memory rather than EEPROM +} // of class destructor +int16_t INA_Class::readWord(const uint8_t addr, const uint8_t deviceAddress) const { + /*! @brief Read one word (2 bytes) from the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to get the return data ready + @param[in] addr I2C address to read from + @param[in] deviceAddress Address on the I2C device to read from + @return integer value read from the I2C device */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to read + Wire.endTransmission(); // Close transmission + delayMicroseconds(I2C_DELAY); // delay required for sync + Wire.requestFrom(deviceAddress, (uint8_t)2); // Request 2 consecutive bytes + return ((uint16_t)Wire.read() << 8) | Wire.read(); +} // of method readWord() +int32_t INA_Class::read3Bytes(const uint8_t addr, const uint8_t deviceAddress) const { + /*! @brief Read 3 bytes from the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to get the return data ready + @param[in] addr I2C address to read from + @param[in] deviceAddress Address on the I2C device to read from + @return integer value read from the I2C device */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to read + Wire.endTransmission(); // Close transmission + delayMicroseconds(I2C_DELAY); // delay required for sync + Wire.requestFrom(deviceAddress, (uint8_t)3); // Request 3 consecutive bytes + return ((uint32_t)Wire.read() << 16) | ((uint32_t)Wire.read() << 8) | ((uint32_t)Wire.read()); +} // of method readWord() +void INA_Class::writeWord(const uint8_t addr, const uint16_t data, + const uint8_t deviceAddress) const { + /*! @brief Write 2 bytes to the specified I2C address + @details Standard I2C protocol is used, but a delay of I2C_DELAY microseconds has been + added to let the INAxxx devices have sufficient time to process the data + @param[in] addr I2C address to write to + @param[in] data 2 Bytes to write to the device + @param[in] deviceAddress Address on the I2C device to write to */ + Wire.beginTransmission(deviceAddress); // Address the I2C device + Wire.write(addr); // Send register address to write + Wire.write((uint8_t)(data >> 8)); // Write the first (MSB) byte + Wire.write((uint8_t)data); // and then the second byte + Wire.endTransmission(); // Close transmission and actually send data + delayMicroseconds(I2C_DELAY); // delay required for sync +} // of method writeWord() +void INA_Class::readInafromEEPROM(const uint8_t deviceNumber) { + /*! @brief Read INA device information from EEPROM + @details Retrieve the stored information for a device from EEPROM. Since this method is + private and access is controlled, no range error checking is performed + @param[in] deviceNumber Index to device array */ + if (deviceNumber == _currentINA || deviceNumber > _DeviceCount) return; // Skip if correct device +#if LIB_INA_EEPROM + if (_expectedDevices == 0) { +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || (__STM32F1__) + #ifdef __STM32F1__ // STM32F1 has no built-in EEPROM + uint16_t e = deviceNumber * sizeof(inaEE); // it uses flash memory to emulate + uint16_t *ptr = (uint16_t *)&inaEE; // "EEPROM" calls are uint16_t type + for (uint8_t n = sizeof(inaEE) + _EEPROM_offset; n; --n) // Implement EEPROM.get template + { + EEPROM.read(e++, ptr++); // for ina (inaDet type) + } // of for-next each byte + #else + EEPROM.get(_EEPROM_offset + (deviceNumber * sizeof(inaEE)), inaEE); // Read EEPROM values + #endif +#else + inaEE = _EEPROMEmulation[deviceNumber]; +#endif + } else +#endif // if LIB_INA_EEPROM + { + inaEE = _DeviceArray[deviceNumber]; + } // if-then-else use EEPROM + _currentINA = deviceNumber; + ina = inaEE; // see inaDet constructor +} // of method readInafromEEPROM() +void INA_Class::writeInatoEEPROM(const uint8_t deviceNumber) { + /*! @brief Write INA device information to EEPROM + @details Write the stored information for a device from EEPROM. Since this method is + private and access is controlled, no range error checking is performed + @param[in] deviceNumber Index to device array */ + inaEE = ina; // only save relevant part of ina to EEPROM +#if LIB_INA_EEPROM + if (_expectedDevices == 0) { +#if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || (__STM32F1__) + #ifdef __STM32F1__ // STM32F1 has no built-in EEPROM + uint16_t e = deviceNumber * sizeof(inaEE); // it uses flash memory to emulate + const uint16_t *ptr = (const uint16_t *)&inaEE; // "EEPROM" calls are uint16_t type + for (uint8_t n = sizeof(inaEE) + _EEPROM_offset; n; --n) // Implement EEPROM.put template + { + EEPROM.update(e++, *ptr++); // for ina (inaDet type) + } // for-next + #else + EEPROM.put(_EEPROM_offset + (deviceNumber * sizeof(inaEE)), inaEE); // Write the structure + #ifdef ESP32 + EEPROM.commit(); // Force write to EEPROM when ESP32 + #endif + #endif +#else + _EEPROMEmulation[deviceNumber] = inaEE; +#endif + } else +#endif // if LIB_INA_EEPROM + { + _DeviceArray[deviceNumber] = inaEE; + } // if-then-else use EEPROM to store data +} // of method writeInatoEEPROM() +void INA_Class::setI2CSpeed(const uint32_t i2cSpeed) const { + /*! @brief Set a new I2C speed + @details I2C allows various bus speeds, see the enumerated type I2C_MODES for the standard + speeds. The valid speeds are 100KHz, 400KHz, 1MHz and 3.4MHz. Default to 100KHz + when not specified. No range checking is done. + @param[in] i2cSpeed [optional] changes the I2C speed to the rate specified in Herz */ + // Wire.setClock(i2cSpeed); // Ignore, I2C speed set on ESPEasy Hardware page +} // of method setI2CSpeed +uint8_t INA_Class::begin(const uint16_t maxBusAmps, const uint32_t microOhmR, + const uint8_t deviceNumber) { + /*! @brief Initializes the contents of the class + @details Searches for possible devices and sets the INA Configuration details, without + which meaningful readings cannot be made. If it is called without the optional + deviceNumber parameter then the settings are applied to all devices, otherwise + just that specific device is targeted. If the optional third parameter, devNo, is + specified that specific device gets the two specified values set for it. Can be + called multiple times, but the 3 parameter version will only function after the 2 + parameter version finds all devices.\n + @param[in] maxBusAmps Integer value holding the maximum expected bus amperage, this value is + used to compute a device's internal power register + @param[in] microOhmR Shunt resistance in micro-ohms, this value is used to compute a + device's internal power register + @param[in] deviceNumber Device number to explicitly set the maxBusAmps and microOhmR values, + by default all devices found get set to the same initial values for these 2 params + @return The integer number of INAxxxx devices found on the I2C bus + */ + uint16_t originalRegister, tempRegister; + if (_DeviceCount == 0) // Enumerate all devices on first call + { + uint16_t maxDevices = 32; +/*************************************************************************************************** +** The AVR devices need to use EEPROM to save memory, some other devices have emulation for EEPROM** +** functionality while some devices have no such function calls. This library caters for these ** +** differences, with specialized calls for those platforms which have EEPROM calls and it makes ** +** the assumption that if the platform has no EEPROM call then it has sufficient RAM available at ** +** runtime to allocate sufficient space for 32 devices. ** +***************************************************************************************************/ +#if defined(ESP32) || defined(ESP8266) + #if LIB_INA_EEPROM + EEPROM.begin(_EEPROM_size + _EEPROM_offset); // If ESP32 then allocate 512 Bytes + #endif // if LIB_INA_EEPROM + maxDevices = (_EEPROM_size) / sizeof(inaEE); // and compute number of devices +#elif defined(__STM32F1__) // Emulated EEPROM for STM32F1 + maxDevices = (EEPROM.maxcount() - _EEPROM_offset) / sizeof(inaEE); // Compute max possible +#elif defined(CORE_TEENSY) // TEENSY doesn't have EEPROM.length + maxDevices = (2048 - _EEPROM_offset) / sizeof(inaEE); // defined, so use 2Kb as value +#elif defined(__AVR__) + maxDevices = (EEPROM.length() - _EEPROM_offset) / sizeof(inaEE); // Compute max possible +#else + maxDevices = 32; +#endif + // Wire.begin(); // I2C is already started in ESPEasy core + + if (maxDevices > 255) // Limit number of devices to an 8-bit number + { + maxDevices = 255; + } // of if-then more than 255 devices possible + for (uint8_t deviceAddress = 0x40; deviceAddress <= 0x4F; + deviceAddress++) // Loop for each I2C addr + { + Wire.beginTransmission(deviceAddress); + uint8_t good = Wire.endTransmission(); + if (good == 0 && _DeviceCount < maxDevices) // If no error and EEPROM has space + { + originalRegister = readWord(INA_CONFIGURATION_REGISTER, deviceAddress); // Save settings + writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, deviceAddress); // Force reset + tempRegister = readWord(INA_CONFIGURATION_REGISTER, deviceAddress); // Read reset reg. + if (tempRegister == INA_RESET_DEVICE) // If the register wasn't reset then not an INA + { + writeWord(INA_CONFIGURATION_REGISTER, originalRegister, deviceAddress); // restore value + } else { + if (tempRegister == 0x399F) { + inaEE.type = INA219; + } else { + if (tempRegister == 0x4127) // INA226, INA230, INA231 + { + tempRegister = readWord(INA_DIE_ID_REGISTER, deviceAddress); // Read the INA high-reg + if (tempRegister == INA226_DIE_ID_VALUE) { + inaEE.type = INA226; + } else { + if (tempRegister != 0) { + inaEE.type = INA230; + } else { + inaEE.type = INA231; + } // of if-then-else a INA230 or INA231 + } // of if-then-else an INA226 + } else { + if (tempRegister == 0x6127) { + inaEE.type = INA260; + } else { + if (tempRegister == 0x7127) { + inaEE.type = INA3221_0; + } else { + if (tempRegister == 0x0) { + inaEE.type = INA228; + } else { + inaEE.type = INA_UNKNOWN; + } // of if-then-else it is an INA228 + } // of if-then-else it is an INA3221 + } // of if-then-else it is an INA260 + } // of if-then-else it is an INA226, INA230, INA231 + } // of if-then-else it is an INA209, INA219, INA220 + if (inaEE.type != INA_UNKNOWN) // Increment device if valid INA2xx + { + inaEE.address = deviceAddress; + inaEE.maxBusAmps = maxBusAmps > 1022 ? 1022 : maxBusAmps; // Clamp to maximum of 1022A + inaEE.microOhmR = microOhmR; + ina = inaEE; // see inaDet constructor + if (inaEE.type == INA3221_0) { + ina.type = INA3221_0; // Set to INA3221 1st channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + ina.type = INA3221_1; // Set to INA3221 2nd channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + ina.type = INA3221_2; // Set to INA3221 3rd channel + initDevice(_DeviceCount); + _DeviceCount = ((_DeviceCount + 1) % maxDevices); + } else { + initDevice(_DeviceCount); // perform initialization on device + _DeviceCount = ((_DeviceCount + 1) % maxDevices); // start again at 0 if overflow + } // of if-then inaEE.type + } // of if-then we can add device + } // of if-then-else we have an INA-Type device + } // of if-then we have a device + } // for-next each possible I2C address + } else { + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + ina.maxBusAmps = maxBusAmps > 1022 ? 1022 : maxBusAmps; // Clamp to maximum of 1022A + ina.microOhmR = microOhmR; + initDevice(deviceNumber); + } // of if-then-else first call + _currentINA = UINT8_MAX; // Force read on next call + return _DeviceCount; +} // of method begin() +void INA_Class::initDevice(const uint8_t deviceNumber) { + /*! @brief Initializes the the given devices using the settings from the internal structure + @details This includes (re)computing the device's calibration values. + @param[in] deviceNumber Device number to explicitly initialize. */ + ina.operatingMode = INA_DEFAULT_OPERATING_MODE; // Default to continuous mode + writeInatoEEPROM(deviceNumber); // Store the structure to EEPROM + uint8_t programmableGain; // work variable for the programmable gain + uint16_t calibration, maxShuntmV, tempRegister; // Calibration temporary variables + switch (ina.type) { + case INA219: // Set up INA219 or INA220 + // Compute calibration register + calibration = (uint64_t)409600000 / + ((uint64_t)ina.current_LSB * (uint64_t)ina.microOhmR / (uint64_t)100000); + writeWord(INA_CALIBRATION_REGISTER, calibration, ina.address); // Write calibration + /* Determine optimal programmable gain with maximum accuracy so no chance of an overflow */ + maxShuntmV = ina.maxBusAmps * ina.microOhmR / 1000; // Compute maximum shunt mV + if (maxShuntmV <= 40) + programmableGain = 0; // gain x1 for +- 40mV + else if (maxShuntmV <= 80) + programmableGain = 1; // gain x2 for +- 80mV + else if (maxShuntmV <= 160) + programmableGain = 2; // gain x4 for +- 160mV + else + programmableGain = 3; // dflt gain x8 for +- 320mV + tempRegister = 0x399F & INA219_CONFIG_PG_MASK; // Zero programmable gain + tempRegister |= programmableGain << INA219_PG_FIRST_BIT; // Overwrite the new values + bitSet(tempRegister, INA219_BRNG_BIT); // set to 1 for 0-32 volts + writeWord(INA_CONFIGURATION_REGISTER, tempRegister, ina.address); // Write to config register + break; + case INA226: + case INA230: + case INA231: + // Compute calibration register + calibration = (uint64_t)51200000 / + ((uint64_t)ina.current_LSB * (uint64_t)ina.microOhmR / (uint64_t)100000); + writeWord(INA_CALIBRATION_REGISTER, calibration, ina.address); // Write calibration + break; + case INA260: + case INA3221_0: + case INA3221_1: + case INA3221_2: break; + } // of switch type +} // of method initDevice() +void INA_Class::setBusConversion(const uint32_t convTime, const uint8_t deviceNumber) { + /*! @brief specifies the conversion rate in microseconds, rounded to the nearest valid value + @details INA devices can have a conversion rate of up to 68100 microseconds + @param[in] convTime The conversion time in microseconds, invalid values are rounded to the + nearest valid value + @param[in] deviceNumber [optional] When specified, only that specified device number gets + changed, otherwise all devices are set to the same averaging rate + */ + uint16_t configRegister; + int16_t convRate; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber % _DeviceCount == i) // If device needs setting + { + readInafromEEPROM(i); // Load EEPROM values to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current register + switch (ina.type) { + case INA219: + if (convTime >= 68100) + convRate = 15; + else if (convTime >= 34050) + convRate = 14; + else if (convTime >= 17020) + convRate = 13; + else if (convTime >= 8510) + convRate = 12; + else if (convTime >= 4260) + convRate = 11; + else if (convTime >= 2130) + convRate = 10; + else if (convTime >= 1060) + convRate = 9; + else if (convTime >= 532) + convRate = 8; + else if (convTime >= 276) + convRate = 2; + else if (convTime >= 148) + convRate = 1; + else + convRate = 0; + configRegister &= ~INA219_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 7; // shift in the BADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (convTime >= 8244) + convRate = 7; + else if (convTime >= 4156) + convRate = 6; + else if (convTime >= 2116) + convRate = 5; + else if (convTime >= 1100) + convRate = 4; + else if (convTime >= 588) + convRate = 3; + else if (convTime >= 332) + convRate = 2; + else if (convTime >= 204) + convRate = 1; + else + convRate = 0; + if (ina.type == INA226 || ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) { + configRegister &= ~INA226_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 6; // shift in averages + } else { + configRegister &= ~INA260_CONFIG_BADC_MASK; // zero out the averages part + configRegister |= convRate << 7; // shift in the averages + } // of if-then an INA226 or INA260 + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, + ina.address); // Save new value to device + } // of if this device needs to be set + } // for-next each device loop +} // of method setBusConversion() +void INA_Class::setShuntConversion(const uint32_t convTime, const uint8_t deviceNumber) { + /*! @brief specifies the conversion rate in microseconds, rounded to the nearest valid value + @details INA devices can have a conversion rate of up to 68100 microseconds + @param[in] convTime Conversion time in microseconds. Out-of-Range values are set to the + closest valid value + @param[in] deviceNumber to return the device name for[optional] When specified, only that + specified device number gets changed, otherwise all devices are set to the same + averaging rate + */ + int16_t configRegister, convRate; + for (uint8_t i = 0; i < _DeviceCount; i++) { // Loop for each device found + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get register contents + switch (ina.type) { + case INA219: + if (convTime >= 68100) + convRate = 15; + else if (convTime >= 34050) + convRate = 14; + else if (convTime >= 17020) + convRate = 13; + else if (convTime >= 8510) + convRate = 12; + else if (convTime >= 4260) + convRate = 11; + else if (convTime >= 2130) + convRate = 10; + else if (convTime >= 1060) + convRate = 9; + else if (convTime >= 532) + convRate = 8; + else if (convTime >= 276) + convRate = 2; + else if (convTime >= 148) + convRate = 1; + else + convRate = 0; + configRegister &= ~INA219_CONFIG_SADC_MASK; // zero out the averages part + configRegister |= convRate << 3; // shift in the SADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (convTime >= 8244) + convRate = 7; + else if (convTime >= 4156) + convRate = 6; + else if (convTime >= 2116) + convRate = 5; + else if (convTime >= 1100) + convRate = 4; + else if (convTime >= 588) + convRate = 3; + else if (convTime >= 332) + convRate = 2; + else if (convTime >= 204) + convRate = 1; + else + convRate = 0; + if (ina.type == INA226 || ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) { + configRegister &= ~INA226_CONFIG_SADC_MASK; // zero out the averages part + } else { + configRegister &= ~INA260_CONFIG_SADC_MASK; // zero out the averages part + } // of if-then-else either INA226/INA3221 or a INA260 + configRegister |= convRate << 3; // shift in the averages to register + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, + ina.address); // Save new value to device + } // of if this device needs to be set + } // for-next each device loop +} // of method setShuntConversion() +const char *INA_Class::getDeviceName(const uint8_t deviceNumber) { + /*! @brief returns character buffer with the name of the device specified in the input param + @details See function definition for list of possible return values + @param[in] deviceNumber to return the device name of + @return device name */ + if (deviceNumber > _DeviceCount) return (""); + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + switch (ina.type) { + case INA219: return ("INA219"); + case INA226: return ("INA226"); + case INA228: return ("INA228"); + case INA230: return ("INA230"); + case INA231: return ("INA231"); + case INA260: return ("INA260"); + case INA3221_0: + case INA3221_1: + case INA3221_2: return ("INA3221"); + default: return ("UNKNOWN"); + } // of switch type +} // of method getDeviceName() +uint8_t INA_Class::getDeviceType(const uint8_t deviceNumber) { + /*! @brief returns uint8_t type of the device specified in the input param + @details See function definition for list of possible return values + @param[in] deviceNumber to return the device name of + @return device name */ + if (deviceNumber > _DeviceCount) return INA_UNKNOWN; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + return ina.type; +} // of method getDeviceType() +uint8_t INA_Class::getDeviceAddress(const uint8_t deviceNumber) { + /*! @brief returns a I2C address of the device specified in the input parameter + @details Return the I2C address of the specified device, if number is out of range return 0 + @param[in] deviceNumber to return the device name of + @return I2C address of the device. Returns 0 if value is out-of-range + */ + if (deviceNumber > _DeviceCount) return 0; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + return (ina.address); +} // of method getDeviceAddress() +uint16_t INA_Class::getBusMilliVolts(const uint8_t deviceNumber) { + /*! @brief returns the bus voltage in millivolts + @details The converted millivolt value is returned and if the device is in triggered mode + the next conversion is started + @param[in] deviceNumber to return the device bus millivolts for + @return uint16_t unsigned integer for the bus millivoltage */ + uint32_t busVoltage = getBusRaw(deviceNumber); // Get raw voltage from device + if (ina.type == INA228) { + // The accuracy is 20bits and 195.3125uv is the LSB + busVoltage = (uint64_t)busVoltage * 1953125 / 10000000; // conversion to get mV + } else { + busVoltage = busVoltage * ina.busVoltage_LSB / 100; // conversion to get mV + } // if-then-else an INA228 + return (busVoltage); +} // of method getBusMilliVolts() +uint32_t INA_Class::getBusRaw(const uint8_t deviceNumber) { + /*! @brief returns the raw unconverted bus voltage reading from the device + @details The raw measured value is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the raw device bus voltage reading + @return Raw bus measurement */ + readInafromEEPROM(deviceNumber); // Load EEPROM from EEPROM + uint32_t raw{0}; // define the return variable + if (ina.type == INA228) { + raw = read3Bytes(ina.busVoltageRegister, ina.address); // Get the raw value from register + raw = raw >> 4; + } else { + raw = readWord(ina.busVoltageRegister, ina.address); // Get the raw value from register + if (ina.type == INA3221_0 || ina.type == INA3221_1 || ina.type == INA3221_2 || + ina.type == INA219) { + raw = raw >> 3; // INA219 & INA3221 - the 3 LSB unused, so shift right + } // of if-then an INA219 or INA3221 + } // if-then a 3byte bus voltage buffer + if (!bitRead(ina.operatingMode, 2) && bitRead(ina.operatingMode, 1)) // Triggered & bus active + { + int16_t configRegister = + readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current value + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Write to trigger next + } // of if-then triggered mode enabled + return (raw); +} // of method getBusRaw() +int32_t INA_Class::getShuntMicroVolts(const uint8_t deviceNumber) { + /*! @brief returns the shunt reading converted to microvolts + @details The computed microvolts value is returned and if the device is in triggered mode + the next conversion is started + @param[in] deviceNumber to return the value for + @return int32_t signed integer for the shunt microvolts + */ + int32_t shuntVoltage = getShuntRaw(deviceNumber); + if (ina.type == INA260) // INA260 has a built-in shunt + { + int32_t busMicroAmps = getBusMicroAmps(deviceNumber); // Get the amps on the bus from device + shuntVoltage = busMicroAmps / 200; // 2mOhm resistor, convert with Ohm's law + } else { + if (ina.type == INA228) { + shuntVoltage = shuntVoltage * ina.shuntVoltage_LSB / 10; // Convert to microvolts + } else { + shuntVoltage = shuntVoltage * ina.shuntVoltage_LSB / 10; // Convert to microvolts + } // if-then a INA228 with 20 bit accuracy + } // of if-then-else an INA260 + return (shuntVoltage); +} // of method getShuntMicroVolts() +int32_t INA_Class::getShuntRaw(const uint8_t deviceNumber) { + /*! @brief Returns the raw shunt reading + @details The raw reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return Raw shunt reading */ + int32_t raw; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + if (ina.type == INA260) // INA260 has a built-in shunt + { + int32_t busMicroAmps = getBusMicroAmps(deviceNumber); // Get the amps on the bus + raw = busMicroAmps / 200 / 1000; // 2mOhm resistor, apply Ohm's law + } else { + if (ina.type == INA228) // INA228 has 24 bit accuracy + { + raw = read3Bytes(ina.shuntVoltageRegister, ina.address); // Get the raw value from register + // The number is two's complement, so if negative we need to pad when shifting // + if (raw & 0x800000) { + raw = (raw >> 4) | 0xFFF00000; // first 12 bits are "1" + } else { + raw = raw >> 4; + } // if-then negative + } else { + raw = readWord(ina.shuntVoltageRegister, ina.address); // Get the raw value from register + } // if-then a 24 bit register + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't use 3 LSB + { + raw = raw >> 3; // shift over 3 bits, datatype is "int" so shifts in sign bits + } // of if-then we need to shift INA3221 reading over + } // of if-then-else an INA260 with inbuilt shunt + if (!bitRead(ina.operatingMode, 2) && bitRead(ina.operatingMode, 0)) // Triggered & shunt active + { + int16_t configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current reg + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Write to trigger next + } // of if-then triggered mode enabled + return (raw); +} // of method getShuntMicroVolts() +int32_t INA_Class::getBusMicroAmps(const uint8_t deviceNumber) { + /*! @brief Returns the computed microamps measured on the bus for the specified device + @details The computed reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return int32_t signed integer for computed microamps on the bus */ + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + int32_t microAmps = 0; + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't compute Amps + { + microAmps = + (int64_t)getShuntMicroVolts(deviceNumber) * ((int64_t)1000000 / (int64_t)ina.microOhmR); + } else { + microAmps = (int64_t)readWord(ina.currentRegister, ina.address) * (int64_t)ina.current_LSB / + (int64_t)1000; + } // of if-then-else an INA3221 + return (microAmps); +} // of method getBusMicroAmps() +int64_t INA_Class::getBusMicroWatts(const uint8_t deviceNumber) { + /*! + @brief returns the computed microwatts measured on the bus for the specified device + @details The computed reading is returned and if the device is in triggered mode the next + conversion is started + @param[in] deviceNumber to return the value for + @return int64_t signed integer for computed microwatts on the bus + */ + int64_t microWatts = 0; + readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure + if (ina.type == INA3221_0 || ina.type == INA3221_1 || + ina.type == INA3221_2) // Doesn't compute Amps + { + microWatts = + ((int64_t)getShuntMicroVolts(deviceNumber) * (int64_t)1000000 / (int64_t)ina.microOhmR) * + (int64_t)getBusMilliVolts(deviceNumber) / (int64_t)1000; + } else { + microWatts = + (int64_t)readWord(INA_POWER_REGISTER, ina.address) * (int64_t)ina.power_LSB / (int64_t)1000; + if (getShuntRaw(deviceNumber) < 0) microWatts *= -1; // Invert if negative voltage + } // of if-then-else an INA3221 + return (microWatts); +} // of method getBusMicroWatts() +void INA_Class::reset(const uint8_t deviceNumber) { + /*! @brief performs a software reset for the specified device + @details If no device is specified, then all devices are reset + @param[in] deviceNumber to reset */ + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + writeWord(INA_CONFIGURATION_REGISTER, INA_RESET_DEVICE, ina.address); // Set MSB to reset + initDevice(i); // re-initialize device + } // of if this device needs to be set + } // for-next each device loop +} // of method reset +void INA_Class::setMode(const uint8_t mode, const uint8_t deviceNumber) { + /*! + @brief sets the operating mode from the list given in enum type "ina_Mode" for a device + @details If no device is specified, then all devices are set to the given mode + @param[in] mode Mode (see "ina_Mode" enumerated type for list of valid values + @param[in] deviceNumber to reset (Optional, when not set then all devices are mode changed) + */ + int16_t configRegister; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current config + configRegister &= ~INA_CONFIG_MODE_MASK; // zero out mode bits + ina.operatingMode = 0b00000111 & mode; // Mask off unused bits + writeInatoEEPROM(i); // Store back to EEPROM + configRegister |= ina.operatingMode; // shift mode settings + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Save new value + } // if-then this device needs to be set + } // for-next each device loop +} // of method setMode() +bool INA_Class::conversionFinished(const uint8_t deviceNumber) { + /*! + @brief Returns whether or not the conversion has completed + @details The device's conversion ready bit is read and returned. "true" denotes finished + conversion. + @param[in] deviceNumber to check + */ + if (_DeviceCount == 0) return false; // Return finished if invalid device. Issue #65 + readInafromEEPROM(deviceNumber % _DeviceCount); // Load EEPROM to ina structure + uint16_t cvBits = 0; + switch (ina.type) { + case INA219: + cvBits = readWord(INA_BUS_VOLTAGE_REGISTER, ina.address) & 2; // Bit 2 set denotes ready + readWord(INA_POWER_REGISTER, ina.address); // Resets the "ready" bit + break; + case INA226: + case INA230: + case INA231: + case INA260: cvBits = readWord(INA_MASK_ENABLE_REGISTER, ina.address) & (uint16_t)8; break; + case INA3221_0: + case INA3221_1: + case INA3221_2: cvBits = readWord(INA3221_MASK_REGISTER, ina.address) & (uint16_t)1; break; + default: cvBits = 1; + } // of switch type + if (cvBits != 0) + return (true); + else + return (false); +} // of method "conversionFinished()" +void INA_Class::waitForConversion(const uint8_t deviceNumber) { + /*! + @brief will not return until the conversion for the specified device is finished + @details if no device number is specified it will wait until all devices have finished their + current conversion. If the conversion has completed already then the flag (and + interrupt pin, if activated) is also reset. + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + */ + uint16_t cvBits = 0; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + cvBits = 0; + while (cvBits == 0) // Loop until the value is set + { + switch (ina.type) { + case INA219: + cvBits = + readWord(INA_BUS_VOLTAGE_REGISTER, ina.address) & 2; // Bit 2 set denotes ready + readWord(INA_POWER_REGISTER, ina.address); // Resets the "ready" bit + break; + case INA226: + case INA230: + case INA231: + case INA260: + cvBits = readWord(INA_MASK_ENABLE_REGISTER, ina.address) & (uint16_t)8; + break; + case INA3221_0: + case INA3221_1: + case INA3221_2: + cvBits = readWord(INA3221_MASK_REGISTER, ina.address) & (uint16_t)1; + break; + default: cvBits = 1; + } // of switch type + } // of while the conversion hasn't finished + } // of if this device needs to be set + } // for-next each device loop +} // of method waitForConversion() +bool INA_Class::alertOnConversion(const bool alertState, const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when a conversion is complete + @details This call is ignored and returns false when called for an invalid device as the INA219 + doesn't have this pin it won't work for that device. + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = false; // Assume the worst + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + case INA260: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) bitSet(alertRegister, INA_ALERT_CONVERSION_RDY_BIT); // Turn on the bit + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write back + returnCode = true; + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnConversion +bool INA_Class::alertOnShuntOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the shunt current exceeds the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their mode changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = false; // Assume the worst + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || deviceNumber == i) // If this device needs to be processed + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // If true, then also set threshold + { + bitSet(alertRegister, INA_ALERT_SHUNT_OVER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 1000 / ina.shuntVoltage_LSB; // Compute using LSB + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + returnCode = true; + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnShuntOverVoltage +bool INA_Class::alertOnShuntUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the shunt current goes below the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false */ + uint16_t alertRegister; + bool returnCode = true; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_SHUNT_UNDER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 1000 / ina.shuntVoltage_LSB; // Compute using LSB + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnShuntUnderVoltage +bool INA_Class::alertOnBusOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin low + when the bus voltage goes above the value given in the parameter in millivolts + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = true; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: // Devices that have an alert pin + case INA230: + case INA231: + case INA260: + alertRegister = + readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get the current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_BUS_OVER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 100 / ina.busVoltage_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnBusOverVoltageConversion +bool INA_Class::alertOnBusUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin + low when the bus current goes above the value given in the parameter in millivolts. + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliVolts alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set then all devices have their alert + changed) + @return Returns "true" on success, otherwise false */ + uint16_t alertRegister; + bool returnCode = true; // Assume success + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: // Devices that have an alert pin + case INA230: + case INA231: + case INA260: + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_BUS_UNDER_VOLT_BIT); // Turn on the bit + uint16_t threshold = milliVolts * 100 / ina.busVoltage_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnBusUnderVoltage +bool INA_Class::alertOnPowerOverLimit(const bool alertState, const int32_t milliAmps, + const uint8_t deviceNumber) { + /*! + @brief configures the INA devices which support this functionality to pull the ALERT pin + low when the power exceeds the value set in the parameter in milliamps + @details This call is ignored and returns false when called for an invalid device + @param[in] alertState Boolean true or false to denote the requested setting + @param[in] milliAmps alert level at which to trigger the alarm + @param[in] deviceNumber to reset (Optional, when not set all devices have their alert changed) + @return Returns "true" on success, otherwise false + */ + uint16_t alertRegister; + bool returnCode = true; // assume success + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to ina structure + switch (ina.type) { + case INA226: + case INA230: + case INA231: + case INA260: // Devices with alert pin + alertRegister = readWord(INA_MASK_ENABLE_REGISTER, ina.address); // Get current register + alertRegister &= INA_ALERT_MASK; // Mask off all bits + if (alertState) // Also set threshold + { + bitSet(alertRegister, INA_ALERT_POWER_OVER_WATT_BIT); // Turn on the bit + uint16_t threshold = milliAmps * 1000000 / ina.power_LSB; // Compute using LSB val + writeWord(INA_ALERT_LIMIT_REGISTER, threshold, ina.address); // Write register + } // of if we are setting a value + writeWord(INA_MASK_ENABLE_REGISTER, alertRegister, ina.address); // Write register back + break; + default: returnCode = false; + } // of switch type + } // of if this device needs to be set + } // for-next each device loop + return (returnCode); +} // of method AlertOnPowerOverLimit +void INA_Class::setAveraging(const uint16_t averages, const uint8_t deviceNumber) { + /*! + @brief sets the hardware averaging for one or all devices + @details Out-of-Range averaging is brought down to the highest allowed value + @param[in] averages Number of averages to set (0-128) + @param[in] deviceNumber to reset (Optional, when not set all devices have their averaging changed) + */ + uint16_t averageIndex; + int16_t configRegister; + for (uint8_t i = 0; i < _DeviceCount; i++) // Loop for each device found + { + if (deviceNumber == UINT8_MAX || + deviceNumber % _DeviceCount == i) // If this device needs setting + { + readInafromEEPROM(i); // Load EEPROM to struct + configRegister = readWord(INA_CONFIGURATION_REGISTER, ina.address); // Get current register + switch (ina.type) { + case INA219: + if (averages >= 128) + averageIndex = 15; + else if (averages >= 64) + averageIndex = 14; + else if (averages >= 32) + averageIndex = 13; + else if (averages >= 16) + averageIndex = 12; + else if (averages >= 8) + averageIndex = 11; + else if (averages >= 4) + averageIndex = 10; + else if (averages >= 2) + averageIndex = 9; + else + averageIndex = 8; + configRegister &= ~INA219_CONFIG_AVG_MASK; // zero out the averages part + configRegister |= averageIndex << 3; // shift in the SADC averages + configRegister |= averageIndex << 7; // shift in the BADC averages + break; + case INA226: + case INA230: + case INA231: + case INA3221_0: + case INA3221_1: + case INA3221_2: + case INA260: + if (averages >= 1024) + averageIndex = 7; + else if (averages >= 512) + averageIndex = 6; + else if (averages >= 256) + averageIndex = 5; + else if (averages >= 128) + averageIndex = 4; + else if (averages >= 64) + averageIndex = 3; + else if (averages >= 16) + averageIndex = 2; + else if (averages >= 4) + averageIndex = 1; + else + averageIndex = 0; + configRegister &= ~INA226_CONFIG_AVG_MASK; // zero out the averages part + configRegister |= averageIndex << 9; // shift in the averages to reg + break; + } // of switch type + writeWord(INA_CONFIGURATION_REGISTER, configRegister, ina.address); // Save new value + } // of if this device needs to be set + } // for-next each device loop +} // of method setAveraging() diff --git a/lib/INA/src/INA.h b/lib/INA/src/INA.h new file mode 100644 index 000000000..946ae6a7f --- /dev/null +++ b/lib/INA/src/INA.h @@ -0,0 +1,351 @@ +#pragma once + +// clang-format off +/*! + @file INA.h + + @brief INA Class library header file + + @mainpage Arduino library to support the INAxxx family of current sensors + + @section Library_intro_section Description + + Class definition header for the INA class. This library gives a common interface to various INA + power monitor devices, see https://github.com/Zanduino/INA/wiki or the code below for a full + list of currently supported devices. The INA devices have a 3-5V power supply and, depending + upon the model, can measure voltages up to 26V or 36V. They are devices with High-Side / Low-Side + Measurement, Bi-Directional Current and Power Monitor with I2C Compatible Interface. The device + documentation can be found at the following location:\n + http://www.ti.com/amplifier-circuit/current-sense/power-current-monitors/products.html\n\n + Detailed library descriptions are on the INA GitHub Wiki pages at + https://github.com/Zanduino/INA/wiki\n\n The INA devices, apart from the INA250 and INA260, + require an external shunt of known resistance to be placed across the high-side or low-side + supply or ground line and they use the small current generated by the shunt to compute the + amperage passing across the circuit. This value, coupled with the voltage measurement, allows + the amperage and wattage to be computed by the INA device and these values can be read from the + devices using the industry standard I2C protocol. + + @section Style Programming + @subsection Coding Coding and comments + OK, I admit that I'm "old school" when it comes to programming style. I am used to using a full + monitor and keyboard for development and testing, plus I like to heavily document code as it + helps me remember what I did when I revisit code after several months (or years). I make use of + the full width (which I've limited to 112 characters here) and put my comments at the end of + lines. I prefer to use descriptive variable names, which means that they tend to be long. + + @subsection StyleGuide Style Guide + Different languages have different coding styles. For the Arduino c++ language I've opted to go + with one of the big players in the industry and have adopted the coding and style rules that + Google recommends and which are documented at [Google c++ Style + Guide](https://google.github.io/styleguide/cppguide.html). I have chosen to put braces on their + own lines and include braces for even 1-liners. End braces are always commented so that + convoluted code is more easily untangled. + + @subsection Documentation + The comments have been formatted for use with [Doxygen](doxygen.nl), one of the leading + documentation systems which is not only free but covers just about anything worth documenting. + The Doxygen system parses the source files of a package and creates documentation. The default + output is a set of HTML pages although it can produce single documents. + + @subsection comments Comment Format + This package uses [Markdown](https://en.wikipedia.org/wiki/Markdown) syntax for formatting + comments, which makes for easy reading directly in the source code and well-formatted + pretty-print in postprocessing. + + @subsection ide IDE + I've opted for using Microsoft Visual Studio (version 16.2.5) for development, the community + version can be downloaded for free at [Microsoft + Downloads](https://visualstudio.microsoft.com/downloads/) and I use the fantastic [Visual + Micro](https://www.visualmicro.com/) package which give the Arduino IDE inside Visual Studio. The + base version is free or becomes only slightly annoying nagware after 90 days, but the software is + inexpensive and the price is well worth it for supporting continued development. + + @section doxygen doxygen configuration + + This library is built with the standard "Doxyfile", which is located at + https://github.com/Zanduino/Common/blob/main/Doxygen. As described on that page, there are only 5 + environment variables used, and these are set in the project's actions file, located at + https://github.com/Zanduino/INA/blob/master/.github/workflows/ci-doxygen.yml + Edit this file and set the 5 variables - PRETTYNAME, PROJECT_NAME, PROJECT_NUMBER, PROJECT_BRIEF + and PROJECT_LOGO so that these values are used in the doxygen documentation. + The local copy of the doxyfile should be in the project's root directory in order to do local + doxygen testing, but the file is ignored on upload to GitHub. + + @section clang-format + Part of the GitHub actions for CI is running every source file through "clang-format" to ensure + that coding formatting is done the same for all files. The configuration file ".clang-format" is + located at https://github.com/Zanduino/Common/tree/main/clang-format and this is used for CI tests + when pushing to GitHub. The local file, if present in the root directory, is ignored when + committing and uploading. + + @section license GNU General Public License v3.0 + + This program is free software: you can redistribute it and/or modify it under the terms of the GNU + General Public License as published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. This program is distributed in the hope that it + will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should + have received a copy of the GNU General Public License along with this program. If not, see + . + +@section author Author + +Written by Arnd at https://www.github.com/SV-Zanshin + +@section versions Changelog + +| Version | Date | Developer | Comments +| ------- | ---------- | ----------- | -------- +| | 2025-12-24 | tonhuisman | Disable reading/writing EEPROM for ESPEasy via compile flag LIB_INA_EEPROM +| 1.1.2 | 2022-01-16 | Oleg-Sob | Issue #87. getBusMicroWatts() only returns positive values +| 1.1.1 | 2021-03-12 | x3mEr | Issue #79. Documentation Update +| 1.0.14 | 2020-12-01 | SV-Zanshin | Issue #72. Allow INA structures to be in memory rather than EEPROM +| 1.0.14 | 2020-11-30 | johntaves | Issue #64. begin() does not set values on subsequent calls +| 1.0.14 | 2020-11-29 | SV-Zanshin | Issue #71. Optimize library, cleanup source code +| 1.0.14 | 2020-10-25 | gallium70 | Issue #66. Error in INA226/230/231 value for "power_LSB" +| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Added "_EEPROM_size" for ESP32 and ESP8266 +| 1.0.13 | 2020-07-13 | fg89o | Issue #62. Incorrect "_EEPROM_offset" computation +| 1.0.12 | 2020-07-13 | SV-Zanshin | Issue #41. Added "_EEPROM_offset" variable +| 1.0.12 | 2020-07-03 | sages | Issue #60. Possible Overflow getBus(MicroAmps,MicroWatts) +| 1.0.11 | 2020-06-30 | SV-Zanshin | Issue #58, changed formatting to use clang-format +| 1.0.11 | 2020-06-29 | SV-Zanshin | Issue #57. "Alert..." functions should be "alert..." +| 1.0.11 | 2020-05-05 | oliverb68 | Issue #56. Limit of +/- 2kW on getBusMicroWatts +| 1.0.10 | 2020-05-03 | we9v | Issue #54. Limit of 127A maximum current changed to 1022A +| 1.0.10 | 2020-05-01 | nathancheek | Issue #53. Extraneous conversion on getShuntMicrovolts +| 1.0.10 | 2020-03-24 | nathancheek | Issue #52. Search for all 16 possible devices +| 1.0.10 | 2020-03-22 | alphaarea | Issue #50. Wiki fix for "begin()" - MaxBusAmps overflow +| 1.0.9 | 2019-12-15 | Steamerzone | Issue #49. Added ifdef for STM32F1 device support +| 1.0.9 | 2019-10-27 | SV-Zanshin | Cleaned up Doxygen formatting +| 1.0.9 | 2019-10-17 | nathancheek | Issue #47. Added EEPROM support for teensy +| 1.0.8 | 2019-09-03 | miky2k | Issue #43. Added new method "conversionFinished()" +| 1.0.8 | 2019-05-23 | avaldebe | Issue #42. Restrict I2C scan to possible devices +| 1.0.8 | 2019-03-24 | mattlogic | Issue #40. Corrected INA226_CONFIG_SADC_MASK value +| 1.0.8 | 2019-03-17 | SV-Zanshin | Issue #19. Corrected 4 value ranges in bus/shunt conversion +| 1.0.8 | 2019-02-16 | SV-Zanshin | Corrected and tested ESP32 implementation +| 1.0.8 | 2019-02-10 | SV-Zanshin | Issue #39. Allow non-AVR processors without EEPROM to run +| 1.0.8 | 2019-02-09 | SV-Zanshin | Cleaned up doxygen comment formatting in .h and .cpp files +| 1.0.8 | 2019-02-09 | SV-Zanshin | Issue #38. Add getDeviceAddress() function +| 1.0.7 | 2019-01-20 | SV-Zanshin | Issue #36&37. Changed for Travis-CI and automated doxygen +| 1.0.7 | 2018-12-27 | SV-Zanshin | Issue #33. Change program documentation to doxygen format +| 1.0.6 | 2018-12-13 | delboy711 | Issue #32. Incorrect ESP2866 rather than ESP8266 +| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #31. Use full 0-32V Range on INA219 all the time +| 1.0.6 | 2018-10-19 | SV-Zanshin | Issue #30. Added TEENSY support & support large EEPROM +| 1.0.6 | 2018-10-14 | SV-Zanshin | Added correct wire handling for ESP8266 and ESP32 +| 1.0.6 | 2018-10-07 | SV-Zanshin | Optimized getBusRaw() and getShuntRaw() functions +| 1.0.5 | 2018-10-04 | SV-Zanshin | Added getBusRaw() and getShuntRaw() functions +| 1.0.5 | 2018-09-29 | SV-Zanshin | Reformatted comments to different c++ coding style +| 1.0.4 | 2018-09-22 | SV-Zanshin | Issue #27. EEPROM Calls don't work with ESP32 +| 1.0.4 | 2018-09-19 | SV-Zanshin | Issue #28. Ovef error when >31Amps specified in begin() +| 1.0.3 | 2018-09-04 | delboy711 | Issue #26. Incorrect INA3221 negative current readings +| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #22. Reduce EEPROM Footprint +| 1.0.3 | 2018-08-18 | SV-Zanshin | Issue #21. Rename I2C Constants to avoid redefine STM32F1 +| 1.0.2 | 2018-07-22 | SV-Zanshin | Issue #11. Reduce EEPROM footprint. Removed "deviceName", 8B. Changed datatype in structure to bit-level length defs, saving additional 3 bytes +| 1.0.2 | 2018-07-21 | avaldeve | Issue #12. Incorrect const datatype for I2C Speeds +| 1.0.2 | 2018-07-12 | coelner | Issue #9. Esplora doesn't accept "Wire.begin()" +| 1.0.2 | 2018-07-08 | SV-Zanshin | Issue #2. Finished testing INA3221 across all functions +| 1.0.2 | 2018-07-07 | dnlwgnd | Issue #4. Guard code used incorrect label +| 1.0.2 | 2018-06-30 | SV-Zanshin | Issue #3. Adding faster I2C bus support +| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library +| 1.0.2 | 2018-06-29 | SV-Zanshin | Issue #2. Adding INA3221 support to library +| 1.0.1 | 2018-06-24 | SV-Zanshin | Removed extraneous elements from ina struct, optimized code +| 1.0.1b | 2018-06-23 | SV-Zanshin | Fixed error on multiple devices with ina structure contents +| 1.0.1a | 2018-06-23 | SV-Zanshin | Removed debug mode and code +| 1.0.0 | 2018-06-22 | SV-Zanshin | Initial release +| 1.0.0b | 2018-06-17 | SV-Zanshin | Continued coding, tested on INA219 and INA226 +| 1.0.0a | 2018-06-10 | SV-Zanshin | Initial coding began +*/ + +#ifndef LIB_INA_EEPROM +#define LIB_INA_EEPROM 0 // disable reading/writing EEPROM (via EEPROM.h functions) +#endif // ifndef LIB_INA_EEPROM +// #ifndef ARDUINO +// /*! Define macro if not defined yet */ +// #define ARDUINO 0 +// #endif +// #if ARDUINO >= 100 /* Use old library if IDE is prior to V1.0 */ + #include "Arduino.h" +// #else +// #include "WProgram.h" +// #endif + +#ifndef INA__Class_h +/*! Guard code definition to prevent multiple includes */ +#define INA__Class_h +/*! typedef contains a packed bit-level defs of information stored per device */ +typedef struct { + uint8_t type : 4; ///< 0-15 see enumerated "ina_Type" for details + uint8_t operatingMode : 4; ///< 0-15 Default to continuous mode + uint32_t address : 7; ///< 0-127 I2C Address of device + uint32_t maxBusAmps : 10; ///< 0-1023 Store initialization value + uint32_t microOhmR : 20; ///< 0-1,048,575 Store initialization value +} inaEEPROM; // of structure +/*! typedef contains a packed bit-level definition of information stored on a device */ +typedef struct inaDet : inaEEPROM { + uint8_t busVoltageRegister : 3; ///< 0- 7, Bus Voltage Register + uint8_t shuntVoltageRegister : 3; ///< 0- 7, Shunt Voltage Register + uint8_t currentRegister : 3; ///< 0- 7, Current Register + uint16_t shuntVoltage_LSB; ///< Device dependent LSB factor + uint16_t busVoltage_LSB; ///< Device dependent LSB factor + uint32_t current_LSB; ///< Amperage LSB + uint32_t power_LSB; ///< Wattage LSB + inaDet(); ///< struct constructor + inaDet(inaEEPROM& inaEE); ///< for ina = inaEE; assignment +} inaDet; // of structure +/*! Enumerated list detailing the names of all supported INA devices. The INA3221 is stored + as 3 distinct devices each with their own enumerated type. */ +enum ina_Type { + INA219, + INA226, + INA228, + INA230, + INA231, + INA260, + INA3221_0, + INA3221_1, + INA3221_2, + INA_UNKNOWN +}; +/*! Enumerated list detailing the operating modes of a given device */ +enum ina_Mode { + INA_MODE_SHUTDOWN, ///< Device powered down + INA_MODE_TRIGGERED_SHUNT, ///< Triggered shunt, no bus + INA_MODE_TRIGGERED_BUS, ///< Triggered bus, no shunt + INA_MODE_TRIGGERED_BOTH, ///< Triggered bus and shunt + INA_MODE_POWER_DOWN, ///< shutdown or power-down + INA_MODE_CONTINUOUS_SHUNT, ///< Continuous shunt, no bus + INA_MODE_CONTINUOUS_BUS, ///< Continuous bus, no shunt + INA_MODE_CONTINUOUS_BOTH ///< Both continuous, default value +}; // of enumerated type +/************************************************************************************************ +** Declare constants used in the class ** +************************************************************************************************/ +#ifndef INA_I2C_MODES // I2C related constants +#define INA_I2C_MODES ///< Guard code to prevent multiple defs +const uint32_t INA_I2C_STANDARD_MODE{100000}; ///< Default normal I2C 100KHz speed +const uint32_t INA_I2C_FAST_MODE{400000}; ///< Fast mode +const uint32_t INA_I2C_FAST_MODE_PLUS{1000000}; ///< Really fast mode +const uint32_t INA_I2C_HIGH_SPEED_MODE{3400000}; ///< Turbo mode +#endif +const uint8_t INA_CONFIGURATION_REGISTER{0}; ///< Configuration Register address +const uint8_t INA_BUS_VOLTAGE_REGISTER{2}; ///< Bus Voltage Register address +const uint8_t INA_POWER_REGISTER{3}; ///< Power Register address +const uint8_t INA_CALIBRATION_REGISTER{5}; ///< Calibration Register address +const uint8_t INA_MASK_ENABLE_REGISTER{6}; ///< Mask enable Register (some devices) +const uint8_t INA_ALERT_LIMIT_REGISTER{7}; ///< Alert Limit Register (some devices) +const uint8_t INA_MANUFACTURER_ID_REGISTER{0xFE}; ///< Mfgr ID Register (some devices) +const uint8_t INA_DIE_ID_REGISTER{0xFF}; ///< Die ID Register (some devices) +const uint16_t INA_RESET_DEVICE{0x8000}; ///< Write to config to reset device +const uint16_t INA_CONVERSION_READY_MASK{0x0080}; ///< Bit 4 +const uint16_t INA_CONFIG_MODE_MASK{0x0007}; ///< Bits 0-3 +const uint16_t INA_ALERT_MASK{0x03FF}; ///< Mask off bits 0-9 +const uint8_t INA_ALERT_SHUNT_OVER_VOLT_BIT{15}; ///< Register bit +const uint8_t INA_ALERT_SHUNT_UNDER_VOLT_BIT{14}; ///< Register bit +const uint8_t INA_ALERT_BUS_OVER_VOLT_BIT{13}; ///< Register bit +const uint8_t INA_ALERT_BUS_UNDER_VOLT_BIT{12}; ///< Register bit +const uint8_t INA_ALERT_POWER_OVER_WATT_BIT{11}; ///< Register bit +const uint8_t INA_ALERT_CONVERSION_RDY_BIT{10}; ///< Register bit +const uint8_t INA_DEFAULT_OPERATING_MODE{0b111}; ///< Default continuous mode +const uint8_t INA219_SHUNT_VOLTAGE_REGISTER{1}; ///< INA219 Shunt Voltage Register +const uint8_t INA219_CURRENT_REGISTER{4}; ///< INA219 Current Register +const uint16_t INA219_BUS_VOLTAGE_LSB{400}; ///< INA219 LSB in uV *100 4.00mV +const uint16_t INA219_SHUNT_VOLTAGE_LSB{100}; ///< INA219 LSB in uV *10 10.0uV +const uint16_t INA219_CONFIG_AVG_MASK{0x07F8}; ///< INA219 Bits 3-6, 7-10 +const uint16_t INA219_CONFIG_PG_MASK{0xE7FF}; ///< INA219 Bits 11-12 masked +const uint16_t INA219_CONFIG_BADC_MASK{0x0780}; ///< INA219 Bits 7-10 masked +const uint16_t INA219_CONFIG_SADC_MASK{0x0038}; ///< INA219 Bits 3-5 +const uint8_t INA219_BRNG_BIT{13}; ///< INA219 Bit for BRNG in config reg +const uint8_t INA219_PG_FIRST_BIT{11}; ///< INA219 1st bit of Programmable Gain +const uint8_t INA226_SHUNT_VOLTAGE_REGISTER{1}; ///< INA226 Shunt Voltage Register +const uint8_t INA226_CURRENT_REGISTER{4}; ///< INA226 Current Register +const uint16_t INA226_BUS_VOLTAGE_LSB{125}; ///< INA226 LSB in uV *100 1.25mV +const uint16_t INA226_SHUNT_VOLTAGE_LSB{25}; ///< INA226 LSB in uV *10 2.5uV +const uint16_t INA226_CONFIG_AVG_MASK{0x0E00}; ///< INA226 Bits 9-11 +const uint16_t INA226_DIE_ID_VALUE{0x2260}; ///< INA226 Hard-coded Die ID for INA226 +const uint16_t INA226_CONFIG_BADC_MASK{0x01C0}; ///< INA226 Bits 6-8 masked +const uint16_t INA226_CONFIG_SADC_MASK{0x0038}; ///< INA226 Bits 3-4 + +const uint8_t INA228_DIE_ID_REGISTER{0x3F}; ///< INA228 Device_ID Register +const uint16_t INA228_DIE_ID_VALUE{0x2280}; ///< INA228 Hard-coded Die ID for INA228 +const uint8_t INA228_BUS_VOLTAGE_REGISTER{0x5}; ///< INA228 Bus Voltage Register +const uint16_t INA228_BUS_VOLTAGE_LSB{195}; ///< INA228 LSB in uV *100 1953125uV, extra code +const uint8_t INA228_SHUNT_VOLTAGE_REGISTER{4}; ///< INA228 Shunt Voltage Register +const uint8_t xINA228_CURRENT_REGISTER{4}; ///< INA228 Current Register +const uint16_t xINA228_CONFIG_AVG_MASK{0x0E00}; ///< INA228 Bits 9-11 +const uint16_t xINA228_CONFIG_BADC_MASK{0x01C0}; ///< INA228 Bits 6-8 masked +const uint16_t xINA228_CONFIG_SADC_MASK{0x0038}; ///< INA228 Bits 3-4 + +const uint8_t INA260_SHUNT_VOLTAGE_REGISTER{0}; ///< INA260 Register doesn't exist +const uint8_t INA260_CURRENT_REGISTER{1}; ///< INA260 Current Register +const uint16_t INA260_BUS_VOLTAGE_LSB{125}; ///< INA260 LSB in uV *100 1.25mV +const uint16_t INA260_CONFIG_BADC_MASK{0x01C0}; ///< INA260 Bits 6-8 masked +const uint16_t INA260_CONFIG_SADC_MASK{0x0038}; ///< INA260 Bits 3-5 masked +const uint8_t INA3221_SHUNT_VOLTAGE_REGISTER{1}; ///< INA3221 Register number 1 +const uint16_t INA3221_BUS_VOLTAGE_LSB{800}; ///< INA3221 LSB in uV *100 8mV +const uint16_t INA3221_SHUNT_VOLTAGE_LSB{400}; ///< INA3221 LSB in uV *10 40uV +const uint16_t INA3221_CONFIG_BADC_MASK{0x01C0}; ///< INA3221 Bits 7-10 masked +const uint8_t INA3221_MASK_REGISTER{0xF}; ///< INA32219 Mask register +const uint8_t I2C_DELAY{10}; ///< Microsecond delay on I2C writes +// clang-format on + +class INA_Class { + /*! + * @class INA_Class + * @brief Forward definitions for the INA_Class + */ + public: + INA_Class(uint8_t expectedDevices = 0); + ~INA_Class(); + uint8_t begin(const uint16_t maxBusAmps, const uint32_t microOhmR, + const uint8_t deviceNumber = UINT8_MAX); + void setI2CSpeed(const uint32_t i2cSpeed = INA_I2C_STANDARD_MODE) const; + void setMode(const uint8_t mode, const uint8_t deviceNumber = UINT8_MAX); + void setAveraging(const uint16_t averages, const uint8_t deviceNumber = UINT8_MAX); + void setBusConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX); + void setShuntConversion(const uint32_t convTime, const uint8_t deviceNumber = UINT8_MAX); + uint16_t getBusMilliVolts(const uint8_t deviceNumber = 0); + uint32_t getBusRaw(const uint8_t deviceNumber = 0); + int32_t getShuntMicroVolts(const uint8_t deviceNumber = 0); + int32_t getShuntRaw(const uint8_t deviceNumber = 0); + int32_t getBusMicroAmps(const uint8_t deviceNumber = 0); + int64_t getBusMicroWatts(const uint8_t deviceNumber = 0); + const char* getDeviceName(const uint8_t deviceNumber = 0); + uint8_t getDeviceAddress(const uint8_t deviceNumber = 0); + uint8_t getDeviceType(const uint8_t deviceNumber = 0); // Added for ESPEasy + void reset(const uint8_t deviceNumber = 0); + bool conversionFinished(const uint8_t deviceNumber = 0); + void waitForConversion(const uint8_t deviceNumber = UINT8_MAX); + bool alertOnConversion(const bool alertState, const uint8_t deviceNumber = UINT8_MAX); + bool alertOnShuntOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnShuntUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnBusOverVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnBusUnderVoltage(const bool alertState, const int32_t milliVolts, + const uint8_t deviceNumber = UINT8_MAX); + bool alertOnPowerOverLimit(const bool alertState, const int32_t milliAmps, + const uint8_t deviceNumber = UINT8_MAX); + uint16_t _EEPROM_offset = 0; ///< Offset to all EEPROM addresses, GitHub issue #41 + #if defined(ESP32) || defined(ESP8266) + uint16_t _EEPROM_size = 512; ///< Default EEPROM reserved space for ESP32 and ESP8266 + #endif + private: + int16_t readWord(const uint8_t addr, const uint8_t deviceAddress) const; + int32_t read3Bytes(const uint8_t addr, const uint8_t deviceAddress) const; + void writeWord(const uint8_t addr, const uint16_t data, const uint8_t deviceAddress) const; + void readInafromEEPROM(const uint8_t deviceNumber); + void writeInatoEEPROM(const uint8_t deviceNumber); + void initDevice(const uint8_t deviceNumber); + uint8_t _DeviceCount{0}; ///< Total number of devices detected + uint8_t _currentINA{UINT8_MAX}; ///< Stores current INA device number + uint8_t _expectedDevices{0}; ///< If 0 use EEPROM, otherwise use RAM for INA structures + inaEEPROM* _DeviceArray; ///< Pointer to dynamic array of devices if not using EEPROM + inaEEPROM inaEE; ///< INA device structure + inaDet ina; ///< INA device structure + #if defined(__AVR__) || defined(CORE_TEENSY) || defined(ESP32) || defined(ESP8266) || \ + defined(__STM32F1__) + #else + inaEEPROM _EEPROMEmulation[32]; ///< Actual array of up to 32 devices + #endif +}; // of INA_Class definition +#endif diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 8ec06e365..600e062e0 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -538,6 +538,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // #define USES_P131 // NeoPixel Matrix // #define USES_P132 // INA3221 +// #define P132_EXTENDED 1 // Extend support with INA219, INA226, INA228, INA230, INA231 and INA260 // #define USES_P133 // LTR390 UV // #define USES_P134 // A02YYUW // #define USES_P135 // SCD4x diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 746468adf..ed8ec5ddf 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -1,11 +1,15 @@ +#include "_Plugin_Helper.h" #ifdef USES_P132 // ####################################################################################################### -// ######################### Plugin 132: INA3221 DC Voltage/Current sensor ############################### +// ############### Plugin 132: INA3221/INA226/INA228/INA260 DC Voltage/Current sensor #################### // ####################################################################################################### /** * Changelog: + * 2025-12-25 tonhuisman: Add P132_EXTENDED to also implement support for INA219, INA226, INA228, INA230, INA231 and INA260 + * INA230/INA231 are sometimes recognized as INA226 + * Without P132_EXTENDED set the original code is used (default for ESP8266). * 2025-01-18 tonhuisman: Implement support for MQTT AutoDiscovery * 2025-01-12 tonhuisman: Add support for MQTT AutoDiscovery (not supported yet for INA3221) * 2022-04-23 tonhuisman: Add separate settings for Conversion rate Voltage and Current @@ -16,18 +20,17 @@ // Initial development: ## 25 jan 2021 Fred van Duin #### -#include "_Plugin_Helper.h" +# include "./src/PluginStructs/P132_data_struct.h" -#define PLUGIN_132 -#define PLUGIN_ID_132 132 -#define PLUGIN_NAME_132 "Energy (DC) - INA3221" -#define PLUGIN_VALUENAME1_132 "Value1" -#define PLUGIN_VALUENAME2_132 "Value2" -#define PLUGIN_VALUENAME3_132 "Value3" -#define PLUGIN_VALUENAME4_132 "Value4" - -#include "./src/PluginStructs/P132_data_struct.h" +# define PLUGIN_132 +# define PLUGIN_ID_132 132 +# define PLUGIN_VALUENAME1_132 "Value1" +# define PLUGIN_VALUENAME2_132 "Value2" +# define PLUGIN_VALUENAME3_132 "Value3" +# define PLUGIN_VALUENAME4_132 "Value4" +// See below for original code with just INA3221 support +# if P132_EXTENDED boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; @@ -63,13 +66,421 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) break; } - #if FEATURE_MQTT_DISCOVER + # if FEATURE_MQTT_DISCOVER case PLUGIN_GET_DISCOVERY_VTYPES: { - success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5);; + success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5); break; } - #endif // if FEATURE_MQTT_DISCOVER + # endif // if FEATURE_MQTT_DISCOVER + + case PLUGIN_I2C_HAS_ADDRESS: + case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: + { + const uint8_t i2cAddressValues[] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F }; + constexpr size_t optionCount = NR_ELEMENTS(i2cAddressValues); + + if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t options = P132_DeviceType::Ina3221 == deviceType ? 4 : optionCount; + addFormSelectorI2C(F("i2c_addr"), options, i2cAddressValues, P132_I2C_ADDR); + addFormNote(F("Address selection: see docs")); + } else { + success = intArrayContains(optionCount, i2cAddressValues, event->Par1); + } + break; + } + + # if FEATURE_I2C_GET_ADDRESS + case PLUGIN_I2C_GET_ADDRESS: + { + event->Par1 = P132_I2C_ADDR; + success = true; + break; + } + # endif // if FEATURE_I2C_GET_ADDRESS + + case PLUGIN_SET_DEFAULTS: + { + P132_VALUE_1 = 1; // Configure 'randomly' + P132_VALUE_2 = 0; + P132_VALUE_3 = 3; + P132_VALUE_4 = 2; + uint32_t lSettings = 0; + set3BitToUL(lSettings, P132_FLAG_AVERAGE, 0x00); + + // set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, 0x04); // Voltage + // set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, 0x04); // Current + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current + set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSIONS); // V2 + P132_CONFIG_FLAGS = lSettings; + P132_MAX_CURRENT = 10; // Guestimated + P132_SHUNT = 1; // Default != 0 + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + { + const __FlashStringHelper*typOptions[] = { + toString(P132_DeviceType::Ina3221), + toString(P132_DeviceType::Ina219), + toString(P132_DeviceType::Ina226), + toString(P132_DeviceType::Ina228), + toString(P132_DeviceType::Ina230), + toString(P132_DeviceType::Ina231), + toString(P132_DeviceType::Ina260), + }; + constexpr size_t typOptionsCount = NR_ELEMENTS(typOptions); + + const int typeOptionIndexes[] = { + static_cast(P132_DeviceType::Ina3221), + static_cast(P132_DeviceType::Ina219), + static_cast(P132_DeviceType::Ina226), + static_cast(P132_DeviceType::Ina228), + static_cast(P132_DeviceType::Ina230), + static_cast(P132_DeviceType::Ina231), + static_cast(P132_DeviceType::Ina260), + }; + FormSelectorOptions typSelector(typOptionsCount, typOptions, typeOptionIndexes); + typSelector.reloadonchange = true; + typSelector.addFormSelector(F("INA type"), F("ityp"), P132_INA_TYPE); + } + + addFormNumericBox(F("Max. Current"), F("mcur"), P132_MAX_CURRENT, 1, 1022); + addUnit(F("1..1022 A")); + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + + { + const __FlashStringHelper *varOptions[9]; + int varValues[9]; + + if (P132_DeviceType::Ina3221 == deviceType) + { + varOptions[0] = F("Voltage channel 1"); + varOptions[1] = F("Current channel 1"); + varOptions[2] = F("Power channel 1"); + varOptions[3] = F("Voltage channel 2"); + varOptions[4] = F("Current channel 2"); + varOptions[5] = F("Power channel 2"); + varOptions[6] = F("Voltage channel 3"); + varOptions[7] = F("Current channel 3"); + varOptions[8] = F("Power channel 3"); + varValues[0] = 1; + varValues[1] = 0; + varValues[2] = 6; + varValues[3] = 3; + varValues[4] = 2; + varValues[5] = 7; + varValues[6] = 5; + varValues[7] = 4; + varValues[8] = 8; + } + else + { + varOptions[0] = F("Voltage"); + varOptions[1] = F("Current"); + varOptions[2] = F("Power"); + varValues[0] = 1; + varValues[1] = 0; + varValues[2] = 6; + } + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + if (P132_INA_PREVIOUS != P132_INA_TYPE) { + P132_VALUE_1 = 1; // Configure randomly + P132_VALUE_2 = 0; + + if (P132_DeviceType::Ina3221 == deviceType) { + P132_VALUE_3 = 3; + P132_VALUE_4 = 2; + } else { + P132_VALUE_3 = 6; + } + + if (P132_DeviceType::Ina260 == deviceType) { + P132_SHUNT = 50; // INA260 has 2 mOhm shunt built-in + } + } + + const FormSelectorOptions selector(optionCount, varOptions, varValues); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + selector.addFormSelector( + concat(F("Power value "), r + 1), + getPluginCustomArgName(r), + PCONFIG(P132_CONFIG_BASE + r)); + } + } + + addFormSubHeader(F("Hardware")); + + { + const __FlashStringHelper *varShuntOptions[] = { + F("0.1"), + F("0.01"), + F("0.015"), // INA228 often used + F("0.005"), + F("0.002"), // INA260 built-in + }; + const int shuntValues[] = { 1, 10, 15, 20, 50 }; + constexpr size_t optionCount = NR_ELEMENTS(shuntValues); + FormSelectorOptions selector(optionCount, varShuntOptions, shuntValues); + selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt + selector.addFormSelector(F("Shunt resistor"), F("shunt"), P132_SHUNT); + addUnit(F("Ohm")); + addFormNote(F("Select as is installed on the board.")); + } + + addFormSubHeader(F("Measurement")); + + if (P132_DeviceType::Ina219 != deviceType) + { + const __FlashStringHelper *averagingSamples[] = { + F("1"), + F("4"), + F("16"), + F("64"), + F("128"), + F("256"), + F("512"), + F("1024"), + }; + const int averageValue[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; + constexpr size_t optionCount = NR_ELEMENTS(averageValue); + const FormSelectorOptions selector(optionCount, averagingSamples, averageValue); + + // selector.default_index = 0b000; + selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE); + + // addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!")); + } + + { + const __FlashStringHelper *conversionRates[11]; + int conversionValues[11]; + + if (P132_DeviceType::Ina219 == deviceType) { + conversionRates[0] = F("9 bits / 84 µsec"); + conversionRates[1] = F("10 bits / 148 µsec"); + conversionRates[2] = F("11 bits / 276 µsec"); + conversionRates[3] = F("12 bits / 532 µsec"); + conversionRates[4] = F("2 samples / 1.106 msec"); + conversionRates[5] = F("4 samples / 2.13 msec"); + conversionRates[6] = F("8 samples / 4.26 msec"); + conversionRates[7] = F("16 samples / 8.51 msec"); + conversionRates[8] = F("32 samples / 17.02 msec"); + conversionRates[9] = F("54 samples / 34.05 msec"); + conversionRates[10] = F("128 samples / 68.10 msec"); + conversionValues[0] = 0b0000; + conversionValues[1] = 0b0001; + conversionValues[2] = 0b0010; + conversionValues[3] = 0b1000; + conversionValues[4] = 0b1001; + conversionValues[5] = 0b1010; + conversionValues[6] = 0b1011; + conversionValues[7] = 0b1100; + conversionValues[8] = 0b1101; + conversionValues[9] = 0b1110; + conversionValues[10] = 0b1111; + } + else + { + conversionRates[0] = F("140 µsec"); + conversionRates[1] = F("204 µsec"); + conversionRates[2] = F("332 µsec"); + conversionRates[3] = F("588 µsec"); + conversionRates[4] = F("1.1 msec"); + conversionRates[5] = F("2.116 msec"); + conversionRates[6] = F("4.156 msec"); + conversionRates[7] = F("8.244 msec"); + conversionValues[0] = 0b0000; + conversionValues[1] = 0b0001; + conversionValues[2] = 0b0010; + conversionValues[3] = 0b0011; + conversionValues[4] = 0b0100; + conversionValues[5] = 0b0101; + conversionValues[6] = 0b0110; + conversionValues[7] = 0b0111; + } + + const size_t optionCount = P132_DeviceType::Ina219 == deviceType ? 11 : 8; + FormSelectorOptions selector(optionCount, conversionRates, conversionValues); + selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : 0b0100; // 12 bit / 523 usec or 1.1ms + const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), convB); + selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), convS); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P132_I2C_ADDR = getFormItemInt(F("i2c_addr")); + P132_INA_PREVIOUS = P132_INA_TYPE; + P132_INA_TYPE = getFormItemInt(F("ityp")); + P132_MAX_CURRENT = getFormItemInt(F("mcur")); + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + PCONFIG(P132_CONFIG_BASE + r) = getFormItemIntCustomArgName(r); + } + P132_SHUNT = getFormItemInt(F("shunt")); + + uint32_t lSettings = 0; + + if (P132_DeviceType::Ina219 != deviceType) + { + set3BitToUL(lSettings, P132_FLAG_AVERAGE, getFormItemInt(F("average"))); + } + + // set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, getFormItemInt(F("conv_v"))); + // set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, getFormItemInt(F("conv_c"))); + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, getFormItemInt(F("conv_v"))); + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, getFormItemInt(F("conv_c"))); + + set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // Write version update + P132_CONFIG_FLAGS = lSettings; + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P132_data_struct(event)); + P132_data_struct *P132_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P132_data) { + success = P132_data->isInitialized(); + } + + break; + } + + case PLUGIN_READ: + { + P132_data_struct *P132_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P132_data) { + return success; + } + + const P132_DeviceType deviceType = static_cast(P132_INA_TYPE); + const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); + + for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { + // VALUES 1..4 + const uint8_t reg = static_cast(PCONFIG(P132_CONFIG_BASE + r)); + uint8_t channel = 0; // + + if ((1 == reg) || (3 == reg) || (7 == reg)) { + channel = 1; + } else if ((2 == reg) || (4 == reg) || (8 == reg)) { + channel = 2; + } + + switch (reg) { + case 0: // Current + case 2: // Current + case 4: // Current + // UserVar.setFloat(event->TaskIndex, r, + // (P132_data->getShuntVoltage_mV(channel) / 100.0f) * P132_SHUNT); + + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusCurrent_mA(channel)); + break; + case 1: // Voltage + case 3: // Voltage + case 5: // Voltage + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusVoltage_V(channel) + + (P132_data->getShuntVoltage_mV(channel) / 1000.0f)); + break; + case 6: // Power + case 7: // Power + case 8: // Power + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusPower_mW(channel)); + break; + } + + // } + } + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("%s: Values: %.2f/%.2f/%.2f/%.2f"), + FsP(toString(static_cast(P132_INA_TYPE))), + UserVar.getFloat(event->TaskIndex, 0), + UserVar.getFloat(event->TaskIndex, 1), + UserVar.getFloat(event->TaskIndex, 2), + UserVar.getFloat(event->TaskIndex, 3))); + } + # endif // ifndef BUILD_NO_DEBUG + + success = true; + break; + } + } + + return success; +} + +# endif // if P132_EXTENDED + +# if !P132_EXTENDED +boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + auto& dev = Device[++deviceCount]; + dev.Number = PLUGIN_ID_132; + dev.Type = DEVICE_TYPE_I2C; + dev.VType = Sensor_VType::SENSOR_TYPE_QUAD; + dev.FormulaOption = true; + dev.ValueCount = 4; + dev.SendDataOption = true; + dev.TimerOption = true; + dev.PluginStats = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_132); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_132)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_132)); + break; + } + + # if FEATURE_MQTT_DISCOVER + case PLUGIN_GET_DISCOVERY_VTYPES: + { + success = getDiscoveryVType(event, Plugin_132_QueryVType, P132_CONFIG_BASE, event->Par5); + break; + } + # endif // if FEATURE_MQTT_DISCOVER case PLUGIN_I2C_HAS_ADDRESS: case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: @@ -85,14 +496,14 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) break; } - #if FEATURE_I2C_GET_ADDRESS + # if FEATURE_I2C_GET_ADDRESS case PLUGIN_I2C_GET_ADDRESS: { event->Par1 = P132_I2C_ADDR; success = true; break; } - #endif // if FEATURE_I2C_GET_ADDRESS + # endif // if FEATURE_I2C_GET_ADDRESS case PLUGIN_SET_DEFAULTS: { @@ -126,7 +537,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) for (uint8_t r = 0; r < VARS_PER_TASK; ++r) { selector.addFormSelector( concat(F("Power value "), r + 1), - getPluginCustomArgName(r), + getPluginCustomArgName(r), PCONFIG(P132_CONFIG_BASE + r)); } } @@ -165,7 +576,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(averageValue); FormSelectorOptions selector(optionCount, averagingSamples, averageValue); selector.default_index = 0b000; - selector.addFormSelector(F("Averaging samples"),F("average"),P132_GET_AVERAGE); + selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE); addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!")); } @@ -247,7 +658,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) } } - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("INA3221: Values: %.2f/%.2f/%.2f/%.2f"), @@ -256,7 +667,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) UserVar[event->BaseVarIndex + 2], UserVar[event->BaseVarIndex + 3])); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG success = true; break; @@ -266,4 +677,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) return success; } +# endif // if !P132_EXTENDED + #endif // USES_P132 diff --git a/src/src/PluginStructs/P132_data_struct.cpp b/src/src/PluginStructs/P132_data_struct.cpp index 5d999089e..680f7d6a9 100644 --- a/src/src/PluginStructs/P132_data_struct.cpp +++ b/src/src/PluginStructs/P132_data_struct.cpp @@ -13,29 +13,163 @@ int Plugin_132_QueryVType(uint8_t value_nr) { # endif // if FEATURE_MQTT_DISCOVER +# if P132_EXTENDED +const __FlashStringHelper* toString(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return F("INA3221"); + case P132_DeviceType::Ina219: return F("INA219"); + case P132_DeviceType::Ina226: return F("INA226"); + case P132_DeviceType::Ina228: return F("INA228"); + case P132_DeviceType::Ina230: return F("INA230"); + case P132_DeviceType::Ina231: return F("INA231"); + case P132_DeviceType::Ina260: return F("INA260"); + case P132_DeviceType::InaUnknown: return F(""); + } + return F(""); +} + +const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return INA3221_0; + case P132_DeviceType::Ina219: return INA219; + case P132_DeviceType::Ina226: return INA226; + case P132_DeviceType::Ina228: return INA228; + case P132_DeviceType::Ina230: return INA230; + case P132_DeviceType::Ina231: return INA231; + case P132_DeviceType::Ina260: return INA260; + case P132_DeviceType::InaUnknown: return INA_UNKNOWN; + } + return INA_UNKNOWN; +} + +const P132_DeviceType P132_INATypeToDeviceType(uint8_t inaType) { + switch (inaType) { + case INA219: return P132_DeviceType::Ina219; + case INA226: return P132_DeviceType::Ina226; + case INA228: return P132_DeviceType::Ina228; + case INA230: return P132_DeviceType::Ina230; + case INA231: return P132_DeviceType::Ina231; + case INA260: return P132_DeviceType::Ina260; + case INA3221_0: + case INA3221_1: + case INA3221_2: return P132_DeviceType::Ina3221; + } + return P132_DeviceType::Ina3221; +} + +const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType) { + switch (deviceType) { + case P132_DeviceType::Ina3221: return 9; // Voltage 1..3/Current 1..3/Power 1..3 + case P132_DeviceType::Ina219: + case P132_DeviceType::Ina226: + case P132_DeviceType::Ina228: + case P132_DeviceType::Ina230: + case P132_DeviceType::Ina231: + case P132_DeviceType::Ina260: return 3; // Voltage/Current/Power + case P132_DeviceType::InaUnknown: return 0; + } + return 0; +} + +# endif // if P132_EXTENDED + // **************************************************************************/ // Constructor // **************************************************************************/ +# if P132_EXTENDED +P132_data_struct::P132_data_struct(struct EventStruct *event) { + P132_DeviceType altType = P132_DeviceType::InaUnknown; + + _deviceType = static_cast(P132_INA_TYPE); + + // INA226 and INA231 are interchangeable + if (P132_DeviceType::Ina226 == _deviceType) { altType = P132_DeviceType::Ina231; } + + if (P132_DeviceType::Ina231 == _deviceType) { altType = P132_DeviceType::Ina226; } + + _i2c_address = P132_I2C_ADDR; + INA = new INA_Class(16); // Max 16 addresses can be used, trade-off: INA3221 uses 3 slots... + + if (nullptr == INA) { + addLog(LOG_LEVEL_ERROR, F("INA Class initialization failed")); + } else { + uint16_t maxCurrent = max((uint16_t)1u, (uint16_t)P132_MAX_CURRENT); + const uint8_t count = INA->begin(maxCurrent, (100 / P132_SHUNT) * 1000); + uint8_t search = 0; + + while (search < count) { + const P132_DeviceType foundType = P132_INATypeToDeviceType(INA->getDeviceType(search)); + const uint8_t addr = INA->getDeviceAddress(search); + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("INA : Detected: %s at 0x%x, device index: %d"), + INA->getDeviceName(search), INA->getDeviceAddress(search), search)); + } + # endif// ifndef BUILD_NO_DEBUG + + if (((foundType == _deviceType) || (foundType == altType)) && (addr == _i2c_address)) { + _device = search; + break; // Found, done searching + } + + if (P132_DeviceType::Ina3221 == foundType) { + search += 2; // Skip 'sub'-devices for INA3321 + } + ++search; + } + + if (0xFF == _device) { + delete INA; + INA = nullptr; + addLog(LOG_LEVEL_ERROR, strformat(F("INA : Configured %s at 0x%x not found."), + FsP(toString(_deviceType)), _i2c_address)); + } else + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("INA : Found: %s at 0x%x, device index: %d"), + INA->getDeviceName(_device), INA->getDeviceAddress(_device), _device)); + } + } + setCalibration(event); +} + +# else // if P132_EXTENDED P132_data_struct::P132_data_struct(struct EventStruct *event) { _i2c_address = P132_I2C_ADDR; setCalibration_INA3221(event); } +# endif // if P132_EXTENDED + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +# if P132_EXTENDED +P132_data_struct::~P132_data_struct() { + delete INA; + INA = nullptr; +} + +# endif // if P132_EXTENDED + // **************************************************************************/ // Gets the raw bus voltage (7FF8 / 32760) LSB 8mV // **************************************************************************/ +# if !P132_EXTENDED + int16_t P132_data_struct::getBusVoltage_raw(byte reg) { uint16_t value = I2C_read16_reg(_i2c_address, reg); // Shift to the right 3 to drop CNVR and OVF and multiply by LSB 8 mV - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: get raw bus %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return (int16_t)((value >> 3) * 8); } @@ -45,67 +179,146 @@ int16_t P132_data_struct::getBusVoltage_raw(byte reg) { int16_t P132_data_struct::getShuntVoltage_raw(byte reg) { uint16_t value = I2C_read16_reg(_i2c_address, reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG String log = strformat(F("INA3221: get raw shunt voltage %d value2 - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG // Shift to the right 3 to drop CNVR and OVF and multiply by LSB if (value > 32767) { // check value is negative // value = 0; // no negative measure value = ((value >> 3) | 0xE000); // correct int16_t value - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" value_neg - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG } else { value = (value >> 3); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" value_pos - "), value); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG } - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG log += concat(F(" reg - "), reg); addLog(LOG_LEVEL_DEBUG, log); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value; } +# endif // if !P132_EXTENDED + // **************************************************************************/ -// Gets the shunt voltage in mV (32760 so +-163.8 mV) 7ff8 LSB 40uV +// Gets the shunt voltage in mV // **************************************************************************/ +# if P132_EXTENDED +float P132_data_struct::getShuntVoltage_mV(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getShuntMicroVolts(_device + reg) * 0.001f; +} + +# else // if P132_EXTENDED float P132_data_struct::getShuntVoltage_mV(byte reg) { int16_t value = getShuntVoltage_raw(reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: shunt voltage in mV * 0.04 %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value * 0.04f; } +# endif // if P132_EXTENDED + // **************************************************************************/ // Gets the Bus voltage in volts // **************************************************************************/ +# if P132_EXTENDED +float P132_data_struct::getBusVoltage_V(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMilliVolts(_device + reg) * 0.001f; +} + +# else // if P132_EXTENDED float P132_data_struct::getBusVoltage_V(byte reg) { int16_t value = getBusVoltage_raw(reg); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("INA3221: get bus voltage %d reg - %d"), value, reg)); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG return value * 0.001f; } +# endif // if P132_EXTENDED + +# if P132_EXTENDED + // **************************************************************************/ -// Configures to INA3221 +// Gets the Bus current in milliampere // **************************************************************************/ +float P132_data_struct::getBusCurrent_mA(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMicroAmps(_device + reg) * 0.001f; +} + +// **************************************************************************/ +// Gets the Bus power in milliwatt +// **************************************************************************/ +float P132_data_struct::getBusPower_mW(uint8_t reg) { + if (!isInitialized()) { + return 0.0f; + } + return INA->getBusMicroWatts(_device + reg) * 0.001f; +} + +# endif // if P132_EXTENDED + +// **************************************************************************/ +// Configures the INA +// **************************************************************************/ +# if P132_EXTENDED +void P132_data_struct::setCalibration(struct EventStruct *event) { + if (!isInitialized()) { + return; + } + + if (P132_DeviceType::Ina219 != _deviceType) { // Averaging set via conversion + INA->setAveraging(getAverageBitsToFactor(P132_GET_AVERAGE, _deviceType), _device); + } + const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + INA->setBusConversion(getConversionBitsToFactor(convB, _deviceType), _device); + INA->setShuntConversion(getConversionBitsToFactor(convS, _deviceType), _device); + + # ifndef BUILD_NO_DEBUG + + // Config register and manufacurer id + const uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read config + const uint16_t mfgid = I2C_read16_reg(_i2c_address, 0xFE); // read manufacturer ID + const uint16_t dieid = I2C_read16_reg(_i2c_address, 0xFF); // read Die ID + + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, + strformat(F("INA : init I2C: 0x%02x mfg: 0x%x, did: 0x%x config: 0x%x, 0b%s"), + _i2c_address, mfgid, dieid, config, String(config, BIN).c_str())); + } + # endif // ifndef BUILD_NO_DEBUG +} + +# else // if P132_EXTENDED void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { // Set Config register uint32_t config = I2C_read16_reg(_i2c_address, 0x00); // read, chip default: 0x7127 @@ -115,14 +328,14 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { set3BitToUL(config, INA3221_CONVERSION_BUS_BIT, P132_GET_CONVERSION_B); set3BitToUL(config, INA3221_CONVERSION_SHUNT_BIT, P132_GET_CONVERSION_S); - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("INA3221: init I2C: 0x%02x mfg: 0x%x, config: 0x%x, 0b%s"), _i2c_address, mfgid, config, String(config, BIN).c_str())); } - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG if (mfgid != 0x5449) { addLogMove(LOG_LEVEL_ERROR, F("INA3221: Invalid Manufacturer ID! (0x5449)")); @@ -131,4 +344,61 @@ void P132_data_struct::setCalibration_INA3221(struct EventStruct *event) { I2C_write16_reg(_i2c_address, 0x00, static_cast(config)); } +# endif // if P132_EXTENDED + +# if P132_EXTENDED +uint32_t P132_data_struct::getAverageBitsToFactor(uint8_t bits, P132_DeviceType deviceType) { + if (P132_DeviceType::Ina219 == deviceType) { + if (bits) { + return 1 << bits; + } + return 0u; + } + + switch (bits) { + case 0b000: return 0u; + case 0b001: return 4u; + case 0b010: return 16u; + case 0b011: return 64u; + case 0b100: return 128u; + case 0b101: return 256u; + case 0b110: return 512u; + case 0b111: return 1024u; + } + return 0u; +} + +uint32_t P132_data_struct::getConversionBitsToFactor(uint8_t bits, P132_DeviceType deviceType) { + if (P132_DeviceType::Ina219 == deviceType) { + switch (bits) { + case 0b0000: return 0u; + case 0b0001: return 148u; + case 0b0010: return 276u; + case 0b1000: return 532u; + case 0b1001: return 1060u; + case 0b1010: return 2130u; + case 0b1011: return 4260u; + case 0b1100: return 8510u; + case 0b1101: return 17020u; + case 0b1110: return 34050u; + case 0b1111: return 68100u; + } + return 532u; + } + + switch (bits) { + case 0b0000: return 0u; + case 0b0001: return 204u; + case 0b0010: return 332u; + case 0b0011: return 588u; + case 0b0100: return 1100u; + case 0b0101: return 2116u; + case 0b0110: return 4156u; + case 0b0111: return 8244u; + } + return 1100u; +} + +# endif // if P132_EXTENDED + #endif // ifdef USES_P132 diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 49be9469d..84b2d846b 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -4,24 +4,56 @@ #include "../../_Plugin_Helper.h" #ifdef USES_P132 +# ifndef P132_EXTENDED +# ifdef ESP8266 +# define P132_EXTENDED 0 +# endif // ifdef ESP8266 +# ifdef ESP32 +# define P132_EXTENDED 1 +# endif // ifdef ESP32 +# endif // ifndef P132_EXTENDED + // # define P132_DEBUG_LOG // Enable for some (extra) logging +# if P132_EXTENDED +# define PLUGIN_NAME_132 "Energy (DC) - INA3221/INA226/INA228/INA260" +# else // if P132_EXTENDED +# define PLUGIN_NAME_132 "Energy (DC) - INA3221" +# endif // if P132_EXTENDED + # define P132_CONFIG_BASE 2 // Better not change this... +# define P132_INA_TYPE PCONFIG(0) +# define P132_INA_PREVIOUS PCONFIG(7) # define P132_I2C_ADDR PCONFIG(1) # define P132_VALUE_1 PCONFIG(P132_CONFIG_BASE) # define P132_VALUE_2 PCONFIG(P132_CONFIG_BASE + 1) # define P132_VALUE_3 PCONFIG(P132_CONFIG_BASE + 2) # define P132_VALUE_4 PCONFIG(P132_CONFIG_BASE + 3) # define P132_SHUNT PCONFIG(6) +# define P132_MAX_CURRENT PCONFIG_LONG(1) -# define P132_CONFIG_FLAGS PCONFIG_LONG(0) -# define P132_FLAG_AVERAGE 0 -# define P132_FLAG_CONVERSION_B 3 -# define P132_FLAG_CONVERSION_S 6 +# define P132_CFG_VERSION 0x1 // Config version: 0 = V1, 1 = V2 -# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE) -# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B) -# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S) +# define P132_CONFIG_FLAGS PCONFIG_ULONG(0) +# define P132_FLAG_AVERAGE 0 // 3 bits +# define P132_FLAG_CONVERSION_B 3 // 3 bits, V1 config +# define P132_FLAG_CONVERSION_S 6 // 3 bits, V1 config +# define P132_FLAG_CFG_VERSION 9 // 2 bits +# define P132_FLAG_V2_CONVERSION_B 11 // 4 bits, V2 config +# define P132_FLAG_V2_CONVERSION_S 15 // 4 bits, V2 config + +# define P132_SET_AVERAGE(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE, (S)) +# define P132_GET_AVERAGE get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_AVERAGE) +# define P132_SET_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B, (S)) +# define P132_GET_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_B) +# define P132_SET_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S, (S)) +# define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S) +# define P132_SET_CFG_VERSION(S) set2BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION, (S)) +# define P132_GET_CFG_VERSION get2BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION) +# define P132_SET_V2_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B, (S)) +# define P132_GET_V2_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B) +# define P132_SET_V2_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S, (S)) +# define P132_GET_V2_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S) # define INA3221_AVERAGE_BIT 9 # define INA3221_CONVERSION_BUS_BIT 6 @@ -31,23 +63,69 @@ int Plugin_132_QueryVType(uint8_t value_nr); # endif // if FEATURE_MQTT_DISCOVER +# if P132_EXTENDED +# include + +enum class P132_DeviceType : uint8_t { + Ina3221 = 0u, + Ina219 = 1u, + Ina226 = 2u, + Ina228 = 3u, + Ina230 = 4u, + Ina231 = 5u, + Ina260 = 6u, + InaUnknown = 255u, +}; + +const __FlashStringHelper* toString(P132_DeviceType deviceType); +const uint8_t P132_DeviceTypeToINAType(P132_DeviceType deviceType); +const uint8_t P132_DeviceTypeToMaxValues(P132_DeviceType deviceType); +# endif // if P132_EXTENDED + struct P132_data_struct : public PluginTaskData_base { public: P132_data_struct(struct EventStruct *event); - P132_data_struct() = delete; + P132_data_struct() = delete; + # if P132_EXTENDED + virtual ~P132_data_struct(); + # else // if P132_EXTENDED virtual ~P132_data_struct() = default; + # endif // if P132_EXTENDED - float getShuntVoltage_mV(byte reg); - float getBusVoltage_V(byte reg); + float getShuntVoltage_mV(uint8_t reg); + float getBusVoltage_V(uint8_t reg); + # if P132_EXTENDED + float getBusCurrent_mA(uint8_t reg); + float getBusPower_mW(uint8_t reg); - void setCalibration_INA3221(struct EventStruct *event); + void setCalibration(struct EventStruct *event); + + bool isInitialized() const { + return nullptr != INA; + } + + # else // if P132_EXTENDED + void setCalibration_INA3221(struct EventStruct *event); + # endif // if P132_EXTENDED private: - int16_t getBusVoltage_raw(byte reg); - int16_t getShuntVoltage_raw(byte reg); + # if P132_EXTENDED + INA_Class *INA = nullptr; + P132_DeviceType _deviceType = P132_DeviceType::Ina3221; // Old default + uint8_t _device = 0xFF; + + + uint32_t getAverageBitsToFactor(uint8_t bits, + P132_DeviceType deviceType); + uint32_t getConversionBitsToFactor(uint8_t bits, + P132_DeviceType deviceType); + # else // if P132_EXTENDED + int16_t getBusVoltage_raw(byte reg); + int16_t getShuntVoltage_raw(byte reg); + # endif // if P132_EXTENDED int8_t _i2c_address; }; From 4df25a401ed0b774ea4cca2ed90f8f0657f5d753 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 26 Dec 2025 22:43:13 +0100 Subject: [PATCH 029/161] [P132] Fix some typos --- src/_P132_INA3221.ino | 12 ++++++++++-- src/src/PluginStructs/P132_data_struct.h | 8 ++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index ed8ec5ddf..22e2ceb83 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -114,7 +114,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) // set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, 0x04); // Current set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current - set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSIONS); // V2 + set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // V2 P132_CONFIG_FLAGS = lSettings; P132_MAX_CURRENT = 10; // Guestimated P132_SHUNT = 1; // Default != 0 @@ -201,6 +201,14 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) P132_VALUE_3 = 6; } + if (P132_DeviceType::Ina219 == deviceType) { + P132_SET_V2_CONVERSION_B(0b1000); + P132_SET_V2_CONVERSION_S(0b1000); + } else { + P132_SET_V2_CONVERSION_B(0b0100); + P132_SET_V2_CONVERSION_S(0b0100); + } + if (P132_DeviceType::Ina260 == deviceType) { P132_SHUNT = 50; // INA260 has 2 mOhm shunt built-in } @@ -310,7 +318,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const size_t optionCount = P132_DeviceType::Ina219 == deviceType ? 11 : 8; FormSelectorOptions selector(optionCount, conversionRates, conversionValues); selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : 0b0100; // 12 bit / 523 usec or 1.1ms - const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; + const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_B : P132_GET_V2_CONVERSION_B; const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), convB); selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), convS); diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 84b2d846b..3134db9fb 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -50,10 +50,10 @@ # define P132_GET_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CONVERSION_S) # define P132_SET_CFG_VERSION(S) set2BitToUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION, (S)) # define P132_GET_CFG_VERSION get2BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_CFG_VERSION) -# define P132_SET_V2_CONVERSION_B(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B, (S)) -# define P132_GET_V2_CONVERSION_B get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B) -# define P132_SET_V2_CONVERSION_S(S) set3BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S, (S)) -# define P132_GET_V2_CONVERSION_S get3BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S) +# define P132_SET_V2_CONVERSION_B(S) set4BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B, (S)) +# define P132_GET_V2_CONVERSION_B get4BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_B) +# define P132_SET_V2_CONVERSION_S(S) set4BitToUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S, (S)) +# define P132_GET_V2_CONVERSION_S get4BitFromUL(P132_CONFIG_FLAGS, P132_FLAG_V2_CONVERSION_S) # define INA3221_AVERAGE_BIT 9 # define INA3221_CONVERSION_BUS_BIT 6 From a6cfb1bf86fff812b72fd5b58d7a9e05f9b97866 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 27 Dec 2025 14:56:54 +0100 Subject: [PATCH 030/161] [P132] Fix some more typos --- lib/INA/src/INA.cpp | 2 +- src/_P132_INA3221.ino | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/INA/src/INA.cpp b/lib/INA/src/INA.cpp index 757a34b44..f6ad7ebf4 100644 --- a/lib/INA/src/INA.cpp +++ b/lib/INA/src/INA.cpp @@ -575,7 +575,7 @@ uint8_t INA_Class::getDeviceType(const uint8_t deviceNumber) { /*! @brief returns uint8_t type of the device specified in the input param @details See function definition for list of possible return values @param[in] deviceNumber to return the device name of - @return device name */ + @return device type */ if (deviceNumber > _DeviceCount) return INA_UNKNOWN; readInafromEEPROM(deviceNumber); // Load EEPROM to ina structure return ina.type; diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 22e2ceb83..d17eef6c8 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -390,9 +390,9 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const uint8_t reg = static_cast(PCONFIG(P132_CONFIG_BASE + r)); uint8_t channel = 0; // - if ((1 == reg) || (3 == reg) || (7 == reg)) { + if ((2 == reg) || (3 == reg) || (7 == reg)) { channel = 1; - } else if ((2 == reg) || (4 == reg) || (8 == reg)) { + } else if ((4 == reg) || (5 == reg) || (8 == reg)) { channel = 2; } From 4cd7b3e75b18a92fb9b770da62ac811499b37ca7 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 27 Dec 2025 15:17:19 +0100 Subject: [PATCH 031/161] [P132] Fix one more typo and clean up code --- src/_P132_INA3221.ino | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index d17eef6c8..7bb3dfabd 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -109,15 +109,12 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) P132_VALUE_4 = 2; uint32_t lSettings = 0; set3BitToUL(lSettings, P132_FLAG_AVERAGE, 0x00); - - // set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, 0x04); // Voltage - // set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, 0x04); // Current - set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage - set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, 0x04); // Voltage + set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, 0x04); // Current set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // V2 P132_CONFIG_FLAGS = lSettings; - P132_MAX_CURRENT = 10; // Guestimated - P132_SHUNT = 1; // Default != 0 + P132_MAX_CURRENT = 10; // Guestimated + P132_SHUNT = 1; // Default != 0 break; } @@ -191,14 +188,14 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const size_t optionCount = P132_DeviceTypeToMaxValues(deviceType); if (P132_INA_PREVIOUS != P132_INA_TYPE) { - P132_VALUE_1 = 1; // Configure randomly + P132_VALUE_1 = 1; // Configure 'randomly' Voltage/Current/ P132_VALUE_2 = 0; if (P132_DeviceType::Ina3221 == deviceType) { - P132_VALUE_3 = 3; + P132_VALUE_3 = 3; // Voltage/Current P132_VALUE_4 = 2; } else { - P132_VALUE_3 = 6; + P132_VALUE_3 = 6; // Power } if (P132_DeviceType::Ina219 == deviceType) { @@ -234,7 +231,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) F("0.005"), F("0.002"), // INA260 built-in }; - const int shuntValues[] = { 1, 10, 15, 20, 50 }; + const int shuntValues[] = { 1, 10, 7, 20, 50 }; constexpr size_t optionCount = NR_ELEMENTS(shuntValues); FormSelectorOptions selector(optionCount, varShuntOptions, shuntValues); selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt @@ -245,8 +242,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) addFormSubHeader(F("Measurement")); - if (P132_DeviceType::Ina219 != deviceType) - { + if (P132_DeviceType::Ina219 != deviceType) { const __FlashStringHelper *averagingSamples[] = { F("1"), F("4"), @@ -261,10 +257,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) constexpr size_t optionCount = NR_ELEMENTS(averageValue); const FormSelectorOptions selector(optionCount, averagingSamples, averageValue); - // selector.default_index = 0b000; selector.addFormSelector(F("Averaging samples"), F("average"), P132_GET_AVERAGE); - - // addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!")); } { @@ -294,9 +287,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) conversionValues[8] = 0b1101; conversionValues[9] = 0b1110; conversionValues[10] = 0b1111; - } - else - { + } else { conversionRates[0] = F("140 µsec"); conversionRates[1] = F("204 µsec"); conversionRates[2] = F("332 µsec"); @@ -345,13 +336,10 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) uint32_t lSettings = 0; - if (P132_DeviceType::Ina219 != deviceType) - { + if (P132_DeviceType::Ina219 != deviceType) { set3BitToUL(lSettings, P132_FLAG_AVERAGE, getFormItemInt(F("average"))); } - // set3BitToUL(lSettings, P132_FLAG_CONVERSION_B, getFormItemInt(F("conv_v"))); - // set3BitToUL(lSettings, P132_FLAG_CONVERSION_S, getFormItemInt(F("conv_c"))); set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_B, getFormItemInt(F("conv_v"))); set4BitToUL(lSettings, P132_FLAG_V2_CONVERSION_S, getFormItemInt(F("conv_c"))); @@ -367,9 +355,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) initPluginTaskData(event->TaskIndex, new (std::nothrow) P132_data_struct(event)); P132_data_struct *P132_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P132_data) { - success = P132_data->isInitialized(); - } + success = nullptr != P132_data && P132_data->isInitialized(); break; } @@ -400,9 +386,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) case 0: // Current case 2: // Current case 4: // Current - // UserVar.setFloat(event->TaskIndex, r, - // (P132_data->getShuntVoltage_mV(channel) / 100.0f) * P132_SHUNT); - UserVar.setFloat(event->TaskIndex, r, P132_data->getBusCurrent_mA(channel)); break; @@ -420,8 +403,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) P132_data->getBusPower_mW(channel)); break; } - - // } } # ifndef BUILD_NO_DEBUG From efaa786ab5a58629f13a9412f3fa6ce8a798ed09 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 27 Dec 2025 17:04:20 +0100 Subject: [PATCH 032/161] [P132] Add INA231 to plugin name --- src/src/PluginStructs/P132_data_struct.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 3134db9fb..05f40a2a2 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -16,7 +16,7 @@ // # define P132_DEBUG_LOG // Enable for some (extra) logging # if P132_EXTENDED -# define PLUGIN_NAME_132 "Energy (DC) - INA3221/INA226/INA228/INA260" +# define PLUGIN_NAME_132 "Energy (DC) - INA3221/INA226/INA228/INA231/INA260" # else // if P132_EXTENDED # define PLUGIN_NAME_132 "Energy (DC) - INA3221" # endif // if P132_EXTENDED From d8fb67cc55c90fc13f38aaf4eacb1f5762f4cc49 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 27 Dec 2025 20:13:43 +0100 Subject: [PATCH 033/161] [P132] Update documentation --- docs/source/Plugin/P132.rst | 147 ++++++++++++++++++ .../P132_ConversionRateOptionsV2_INA219.png | Bin 0 -> 62836 bytes .../Plugin/P132_DeviceConfigurationV2.png | Bin 0 -> 82312 bytes docs/source/Plugin/P132_InaTypeOptionsV2.png | Bin 0 -> 28096 bytes .../Plugin/P132_PowerValueOptionsV2.png | Bin 0 -> 11695 bytes .../P132_PowerValueOptionsV2_INA3221.png | Bin 0 -> 38895 bytes .../Plugin/P132_ShuntResistorOptionsV2.png | Bin 0 -> 18756 bytes .../Plugin/_plugin_substitutions_p13x.repl | 8 +- 8 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png create mode 100644 docs/source/Plugin/P132_DeviceConfigurationV2.png create mode 100644 docs/source/Plugin/P132_InaTypeOptionsV2.png create mode 100644 docs/source/Plugin/P132_PowerValueOptionsV2.png create mode 100644 docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png create mode 100644 docs/source/Plugin/P132_ShuntResistorOptionsV2.png diff --git a/docs/source/Plugin/P132.rst b/docs/source/Plugin/P132.rst index 9e36b88a5..ebd812197 100644 --- a/docs/source/Plugin/P132.rst +++ b/docs/source/Plugin/P132.rst @@ -24,6 +24,151 @@ Used libraries: |P132_usedlibraries| Description ----------- +This plugin had support for INA3221 only, and has been extended with support for more types of INA sensors, though this extension is by default not enabled for ESP8266 builds. That version is available in :ref:`original-documentation`. + +Support for INAxxx DC Voltage/Current/Power measurement sensors, INA3221, INA219, INA226, INA228, INA230, INA231 and INA260. + +Existing plugin settings are forward compatible with the new settings. + +Configuration +------------- + +.. image:: P132_DeviceConfigurationV2.png + :alt: Device Configuration + +* **Name** A unique name should be entered here. + +* **Enabled** The device can be disabled or enabled. When not enabled the device should not use any resources. + +I2C Options +^^^^^^^^^^^^ + +The available settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page` + +* **I2C Address**: The address the sensor is using. The different addresses can be selected by connecting the appropriate signal to ``A0`` and ``A1`` when available. Often also documented on the board. + +For ``INA3221`` the range is limited to the first 4 addresses, for other INA types, 16 addresses are available: + +Selectable I2C addresses: + +.. csv-table:: + :header: "Address", "A0", "A1", "" + :widths: 10, 10, 10, 70 + + "0x40","GND","GND","" + "0x41","VS+","GND","" + "0x42","SDA","GND","" + "0x43","SCL","GND","" + "0x44","GND","VS+","" + "0x45","VS+","VS+","" + "0x46","SDA","VS+","" + "0x47","SCL","VS+","" + "0x48","GND","SDA","" + "0x49","VS+","SDA","" + "0x4A","SDA","SDA","" + "0x4B","SCL","SDA","" + "0x4C","GND","SCL","" + "0x4D","VS+","SCL","" + "0x4E","SDA","SCL","" + "0x4F","SCL","SCL","" + + +Device Settings +^^^^^^^^^^^^^^^ + +* **INA type**: Select the INA chip type you want to connect. This has to match with what is auto-detected by the library. As INA231 is often recognized as an INA226, this is an accepted alternative. And also the other way around. The difference is only in the specifications of the chip, from software side they are practically identical. + +.. image:: P132_InaTypeOptionsV2.png + +NB: INA219 is already supported by :ref:`P027_page` but is still added here as it is supported by the used library. + +* **Max. Current**: Configure the max. to be expected/measured current, so the library can optimize the configuration combined with the **Conversion rate** and **Shunt resistor** settings, below. Range is ``1..1022 A``, default set at ``10``. + +* **Power value 1..4**: Most INA types have 3 ``Power values`` available, only INA3221 has 9 values available. As a plugin only has 4 available output values, here we have to select what output receives what measured value. + +Available options for ``INA3221`` are: + +.. image:: P132_PowerValueOptionsV2_INA3221.png + :alt: Available Power Value options + +* *Voltage channel 1..3*: Measure the voltage from channel 1..3. +* *Current channel 1..3*: Measure the current from channel 1..3. +* *Power channel 1..3*: Calculated power from channel 1..3. + +Other INA types the available options are: + +.. image:: P132_PowerValueOptionsV2.png + :alt: Available Power Value options + +* *Voltage*: Measure the voltage in V. +* *Current*: Measure the current in mA. +* *Power*: Measure the power in mW. (Needs ``VBus`` to be connected to the sensor) + + +Hardware +^^^^^^^^ + +* **Shunt resistor**: As there are different hardware configurations available for the board, using different shunt resistor values, here you can select the value that is present on the board used. + +.. image:: P132_ShuntResistorOptionsV2.png + :alt: Available options for shunt resistor values + +NB: ``INA260`` has an internal shunt resistor of ``2 mOhm``, so when selecting that **INA type**, the setting is fixed at that value. + +Measurement +^^^^^^^^^^^ + +* **Averaging samples**: To increase the accuracy of measurements, the chip allows to take the average value for a set of samples. The desired number of samples can be selected in this setting, default: 1. + +.. image:: P132_AveragingSamplesOptions.png + :alt: Averaging samples options + +This setting is *not* available for ``INA219``, as this chip has the Averaging samples setting combined with the **Conversion rate**. + +When combining a higher number of samples with a longer **Conversion rate**, taking all possible measurements will require more time, affecting the **Interval** that would be feasible for a reliable measurement. The worst-case scenarios (maximum **Conversion rate** settings) have been calculated, and are shown as a note below this setting. + +* **Conversion rate Voltage/Current**: To change the accuracy of measurements, a different conversion rate can be selected. Lowering the conversion rate will decrease the accuracy, and allow a higher sampling frequency, but as the measurements are read at a smallest **Interval** of 1 second, there isn't much use in lowering the conversion rate. When increasing the conversion rate, accuracy will increase, but, also depending on the **Averaging samples** configured, increases the time it takes to complete all measurements. (Calculation formula below). + +There are separate **Conversion rate** settings for the **Voltage** and **Current** measurements, as that's available in the chip. + +.. image:: P132_ConversionRateOptions.png + :alt: Conversion rate options + +To calculate the minimally required **Interval** setting for the task, this formula can be used: + + | (3 * Averaging samples * Conversion rate Voltage) + (3 * Averaging samples * Conversion rate Current), and round that up to a seconds value. + + | Example: (Worst case) 3 * 1024 * 0.008244 + 3 * 1024 * 0.008244 = 50.65 seconds => ~51 minimum Interval. + + | Example: (Default case) 3 * 1 * 0.00144 + 3 * 1 * 0.00144 = 0.008 seconds => 1 minimum Interval. + +For ``INA219`` a different **Conversion rate** selector is available: + +.. image:: P132_ConversionRateOptionsV2_INA219.png + :alt: Conversion rate options + + +.. include:: DataAcquisition.repl + +Values +^^^^^^ + +The Values available for this sensor, are named ``Value1`` through ``Value4``, and can be adjusted as desired. The Formula field can be used to recalculate the shown results. Decimals should probably be set too. + +Per Value is a **Stats** checkbox available, that when checked, gathers the data and presents recent data in a graph, as described here: :ref:`Task Value Statistics: ` + +| + +| + +.. _original-documentation: + +Original documentation +---------------------- + +Description +----------- + The INA3221 Voltage/Current sensor can be used to measure voltage and/or current for DC input, up to 26V. It has 3 separate channels available. It can be used to replace the :ref:`p027_page`, except that the I2C protocol isn't directly compatible, so it is not a drop-in replacement. @@ -119,6 +264,8 @@ Change log .. versionchanged:: 2.0 ... + |added| 2025-12-27 Extended adding INA219, INA226, INA228, INA230, INA231 and INA260. + |added| 2022-04-23 Initial release version. diff --git a/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png new file mode 100644 index 0000000000000000000000000000000000000000..405151f88157e5f0eca33194c588a43da30ce822 GIT binary patch literal 62836 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'CcF0RxjFZk2~iYQK7Zmv4QJ3z{a%8A^{=8!6R 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 ?IR6$j7FO(^4;eep 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Ⓢ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 CK~<6f literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P132_PowerValueOptionsV2.png b/docs/source/Plugin/P132_PowerValueOptionsV2.png new file mode 100644 index 0000000000000000000000000000000000000000..02da1e2e9971bde76c9d158a0cdc0f399f54adc0 GIT binary patch literal 11695 zcmcI~cQ{*p_;>P(=U$ zXOWb1Z>o!w{aG)je#+sZjh4D95M8rIm*a>|eC} zKKb2z$Fhu{nG1QbSXkatUY2Q-=NJK?)xLjLll!X1{rmk7RH_IQ^O*?Uvu7@g4F3vU zxRZ>?ytA*1XU?A-ER4K*UitUd$tyB=fhwH)_XZ9V0RMBE_kVNdSp=lM5O}b~7ksii zxzo^bLJI(dz?T}nj+Yx7KtQ=l#xeB2pNH`CY+?`IpYyA2Y}Clqj)Gws0f1vnk2_q7 z4eQ=ShZdg!6u_^Vs@e~Kz6WGDj}w-dgQTgF=_LUG zDe5E*RUH-H(+ijwZVk(%?dP}Vt^l0umSkWd9h^nf2O z7cpPmD?wdi?M12Es4>5tt|}Oa%L=IfIe1eT=vh4MbsY|=Q-c3OS9m(|&Z<>xDc#Gj zZRzm(ausT89s>rvp;jIKJZk~$y_(7P`EI*Hu|Ub!7O{|uG;>N@VM^Okri1<2X|+d_ ze$C)dBO@@^NBe$m+(^LlwJ?Fh>=~2|G2PDRCfUFT`l`8OX~_EJK!5@ow_YLbU?3{& zI<2ikGoJq0aMA;n!(&+~wzGwgm?HJJiM0p>!fLwxD>pif`$Kv>#*e&F3o0<-@K0WM z;H|KWbw5q{5LM_Ix#V^Y(@v)k!fIr9{Cx)#l0#Bv8emlNcxc4w<;0c+EmgIAKbXN@={X0 zHNvRD)s4)PQW^aDKG4!t_9)cyaV$9e*t4lZAS+F2a)(d^F6-(C+f}YtRteyxpV&AV zNC`7KX<)Vqt{jQm{IufGUB>6pq-^(g_^m3$e{=(jXn*zxP+`*g&zoz) zXAs2NhtuvCiuig3OZjD(pwT_e`22)zrZn0>B1s22JmntrtBOXI!MSx-6PT;tt}FhE z?~x1;Iuj7d>1om-(EMv=+<%7w@br5OBr9AX=Eg+eo&rU7fAS)43nQ1e@J}>b%$ob{ zeHqI2l2=)vYpGQ(+M*t*l>dEpD;$oU&M6-c%&87r;k+(q>$B^mUdbX>F+j=$nx=Wr=5@Pp zkmZoKiw$?X_%Z>{qi+DIEd~F1nI%w193L=F2+pk25i#LJkeVNVn##|P!PqP57W(Xt zN-(d>I@YYlC%6icrLBC%34~vh6f}~u+7Lxf;2xEJJa{@za0#wD8E;>mYRknY4hEkA zuz_MQ9h=HPpKmN3jlywOTT98JE;%gepAJ1G;jd~*;(2)0$Hh8#LHo5$`_ip>ljEbt zd%v2;7@>aFSCGIdnA`0!HYy9^%ya-JGm89Q{2Ma^u3)}aipmeYpdjEKiiDi|8#5Iy z!ht#ZeJ@o3oGkxc1W{m8fr2sr)AZnQtx7PZw=pF3H$uPsEO1b9|Ck~v6hPft9T!Hb z6;*OMP;eIrPNHfHIhj7#j-2KB8-8KcCdCxEpV)nW1ptVDC-eE4%B4$}2J^+1=>9Pi zTL%g!i(J1R{3_=d{YV+ay%5xrjOLB%2LiI8^)K)I?s`u=I1QQ^h2DO?RO|r&{Nzje z49;{@92scfhyIBY#r#H=NrC8uI)XIT23jozs6- z0eM{IL#CIY*cG<_?!17Z0ytg#Gw3jZvw*BKl;!)oqsG4h*8xD8;(sg_1-J`Xp9>sb z|NAAR5160dGWu%1xb>b`Ml$kMT6VX)+2EVMmCor&9rQPG0{)u3LXqfy%$f|SO9|Q2 zi>|ZZ2%S{K)mLm8Uyl~h&9$DArrdY=&wULHP!TUxQXR*>U+8RMum0)QPR8lvZZ2h^ z0B?5x*fdY!-*_F5b1qk1rMA1h2eUN<4#0{tA7B4)`pQGsC0l<%jnUd-;B$PQZ4>#qK3B?DAAVZ~k~`nKJWk^N2?jLlz@A5gIQ-`t8~ zzUFMz8B^23cjW1RsA0a!D#0~#4UZ}ESx2$w1z-Lfk1obP{sf-ovEdv!(9EXoV4z8=rBQOrE#TUa;JKm9qL%8Uvy#QH#S6_ z`_tt+C=5@aP$e>#-uQ0IM1wYO_JZ*?-m$#6xhk;Ibd#b`?0XL1bzj10UvbC7SF9tkjaQ@1IRq{x z^s;`N-eZ#*kVs1zDJ}+MiBKFa)$8w0+!>DC%uSavGk5Fr@5v0%wjk!t%uAu`<7{yW zz917gT?uSi^ zW2+r5eB<#*_?FdJ-|zsJGm$JkRE;;fi?)s=WrQx-o_;}an16ZaKi+*{yDOVLBY!D^ zdq1a}hoV5;bd>Sbeu{wFk+#A`2Zz4yEJY+FCVu_;<0YL=!q4qZ@!Xu8krh_d;ss)y z29`&{FsWt{RJ``I0Zy}DFY|srPcWjSF}N%y+i+UZOb9=?*E&Yi`vxCcA~7ywY-EC7F{fibwMXL>b}4rd{RsD4oJOi{fl zM_ExX@+4RYP51SA*XGxs5zu9AR2&b2OHsB2Z)w;R=%E{6UeZ3W;x5d|Xi78Vo90_# zQzndaRHz>v9i?_GskMA8SE>De@?N`Nl`R$K&)^|yOgb2!5D0~2tc`KE;@zK`-B|Yj4D@CNjHrJNUz_p2jK5@Nx1RQ{Z>M>+ zg{shCD`6$Z{@`{0g74FZ4@$NQ4Ga#B^jVAI%FUd|{1e}f55c&uXf6eFyv|@T>D%`u z)Dj3qJ@`e7f?&l#(tF!B>!dFFm(Zmz=l=DZN5SK&4CyE@5c<7@7MR%Q8zr4`y|x`x z^r?@!0wN}P+}sSSu9M}HPELin!yWEuwFJo(^_d{<8#**el;RV|40ahf2pXLEG1_LO zhAzBo<*uxm8;U?>Nky7&_3mb!Bxwc%P?@B@w$2wRzJb65EKd#ozQ9qkbg|A#6;C7| zs6Loek7uYRXQpUw&azLUr6mY9O?|{C#L*+w8n@DE z?GFRJQb?#Nd`(>)_R+AxwS0Ta%vD`}{br6`d}+rx(BEv@#+mqJG(ntJ6l|2d@+e7- z;XaNI`3zYs#A-J+D=8E)1zC@|>b8R0O*PP)WCA933xaIn=Hr<9+D46Gr&QD8F04IY z_vZOT5p3ySel_53wxoUz^j*G81!-0-ZRJdQnTO=hdt+ohG&>xjz%E10Lt03}v zOce~d-8#2`;m5~p1v$hjk}m)WXXdXhnpG*PPBs?#Q)Kmq3H z`n0rb71j4D+>1^6>rLP738c&5(TIT*XIq~g=ZZf$0SwCZ}?0$Eu z_C`l-i$ImchihF6;9*DxB<`?PrO5<}&w^C9+(~_;{5huE$zHH8_>m?#g82gvvh3|Mp?tD(%8M#0 zQe#T@UYwjr$=b~!nQ7CMW!IZ5R9va5s}uB= zk>8Mb@A+AXW7u;+r_smRvAvpZ))@$G-9AsN&?u~ktX|=R)JVya8!$VMn=Pd!ZP<4W zDN{CbvkwJShGgBj59md1_q6 zM=;G$TnrOx@ku z71~{c>t0(_h$LSlS3{~hGZ$|2o|WNy*UPA&I2gDPbAMJl%S#R;%S*}=K zpP>uf-?%IG9aFi%xiR{j@k90IWj;dKn=1nsP2x&#n%Fhz6{$$c-te5NIuzTX{ZaEp zoGt&#$8QQ+q%AhSWY59EJc%Ll63lVK3kt+`C{ugRggxiM8o^Uhlz5~^?CKw&MGEvq zLYmff_>jX*rl2l3+oM>0%%^UQK(bRz;eAz)2c7WqBdQU}q^gGO<`(Gd`D&-OLK%0f z(Y47cO3o>LCplmfvAxAC7PPIVWqL`=v^%Ke{mZ(BokQAfGphs1Z8Ww*Mf3W;^K-2C zvn&VlyV{g|Ac{$+au9?qbZ9p8|)Cc<>UmM3oe)&8}B`Ob`)jh z5t^suUYZ{gsHP;8H%f3^-)XfxJTP0g-kX~nEyu?p3^{IhniMy7=p{BSw(Xq6Z?v!( z-}HfnMw@_29h?NX}-JHK6Q|%Oa}^HwG4YHDhFj(%y%l-=4uSCVa&$Z+b*w9laye(r(cS zXabelzpg#fw98ywE!gJ($3RTQ@(IuH+MYQ|3i-KtDe&;Ue&FGbVDON_$(KuqUrO`8 z-UQW>#Z)=11*Z>Aw)a?CEz|aV53C|mNQ4*ISLyby-pI4cpsqC83S%K87>UR=$6|XF z26XkFDq$agD#AzQ=0e?y%s8E2VeSlZ|Pg9BO9S+f1%4I&_2dSkBhkW^UA=W zy0cupZtp-`^I5Y&Jp<#bH%)-=y?`0)MUOVj^(KSRa`SuZ=7GfUpJ;3B$iXT$=Bwnm zQc`E=$=c_g(Eg6o3j56!`q>7@p|sZaz}={Zpwg1EmS*{Yoa5<(gh>4x)wz?8c+l0S zKa1E#6IXvXt5DpE%c7WTZZ3v4Ka1y@XRmhiUwyrIb6{8xK=Go# z*JjAfD2k?+y>3y-Ub}1-uO`rWUsl*yTCX?2UpXs3T{ydF6)lcB4!%(=Y#oNXoW-89 zy1-3KzuJcpL~%&0zp{guc!b)7B|l!+hh6)1bh6)!chazF$wGn{o)ua7*u%HO1A3E7 z%3~#V`;S=Jiegu|MO2Lh&@Y9BN(>9z`#C%sG74IW&$Bj;YNZ~b6|&z0^Yx7Na^ zVgn}L+D7nbBWoW%`{7!)6M3KF^E4@MrU^ubOX96x%;x90$t6A*l>BByzS(!Ai*9RJ zWryM*$XlykKx{JZ!X|B-p{IcUgv7wBhV_*d8~G+ZNI@<*-?Nf05&q%8%*yl$$tGi8 zLT50TGqV7fDM#HKDd?GiQJ7Lwg}Z%g<1DQs2|z(hU{I566x73^Brs$_4H5fP*Rsbm zQs5mhDX8BSy7{=5dOOq+r1>QDzK94V$=;lxFJ;f+R;4ptVQ>R0KLvAjt!aah_aC*j zn+`}-)3{_zn?Vg`#$D{&V4=JF;pwtAM9Zure}ho>6tWa)8{#}0zB zLuXq$x0;hmM0Up*7u6=VUag)etgk<*eqMn?sVtkc^LML{r%myCy5uu#OqLBdygjqJ zwg%Vl`P3Dh%ovXh&1fv8>5kz5~bL;Atb@@*!Pc*qf7 zwm|bBJVF4xpD`1Q5w!6!y0XR$O{%}Sj&>c`=31Y5tXbQWTOMx8{1$dZNo}WC0`Bpi z`5gu1EA&U1qdd!=VOOPqOzk`oAzj3$ zjz@V$dPuP&WKdQn8AS&HJQ`k}7u-n`QYotwQl8(TP0kIoH{lz%WjQho^@5V9rxhe+ zWQJzW3-22O&xUbV@g_1{DPJ*RDC>-BzgakI{@OsXn0NP>Y%8Da0o-d0b)J5hX^9SE z^331Sc}3!k+hUU%tjjS9X1jjf&{zU>1zAi-3}#aA9uHT1dli_&Z-7nDVXK9sPuOb{|qB~)$NMMMflHMXsY&$ zK%;aa?{2^o zOL?effV}3j_z9bGpnhlEq~UIDa$4Fdlkt+;`0B8~XM2%Cu6^31sky%19r%wU!+^)s zsAPpm=*xX!Nc5^RWK9!>m8=zMKqYPOja}d@^$z@CD|@GClqso;ah=F}+0;SblkP7w z?YptiY0I>bUg3S`0L{Fg;KY6=P5aCorlsX6$dkg?@?!c*v}$=b2*r6zHMIt9!@f7( zEwCjSqZQ=*Mz5u-<@IBZL=sQS_on=H>l9qf^0;jk$N_gngU9snrt#HOgc~K&0oj&C zHh4P|xA>y(g+IUWmgFTxYuUzPY&mR|>YeTN3Z-Tw(}Ro^a{u?qA`d~>)C@Nlj^pe% zBNFJL=jo-uHxXSn40mjm<&+$?S?{DeGPBfvv)fIrRai6O9g>m0mA42P3N#uDmWB_7 zObi9iqXpMbVqx`ml1b?@GpBEpu5>qeDMcmv3BJ z^VI6@XEQ4j0g|qMK?pL0ou4)e7hstI8syWbd2?~}N#aNWV~|NePZ( z&cCo<94$?RfyBf_Q>pTD^F{={_-(+9C8BvhA3m(~gWW_tvg5h)U`S-X^dxvOfG7dA zgcyAiTeUx|_PMy+T$6AdW~$A6Y-KoOm-{SiE5OvcKD{1>Z#D%DOW<7$Jr2vh>`$C> z^aY?sl(a&Tc%>0VRB_D4MrCtpp_&_kSLXd74vC-dP_|MedIl4|QOjBJ6UX~bX9 z&+)~W?Ws2JiYvn3SuQW$94ebIb|o8_dwS4usU-Q}me`o}yJ z=9E)^-~rB`V-KpZ)mRbK!i6x0$q%owbCe@X=G7)9*K%|7g#!#v^z~n>`3#i3%sNR~ z#o(XAS_V(c_OLa6JraT}?^5d`@cW3}l*f1~9Cm2yKqJ&~vBzXG68jks7S=NmPpp_y zTFG{6nJ$^Hn%Bk0AjIoVMRrW;ZoFemIvS#+xcLG(KO3qJXl=yx{S^>!1#QEstS&%%ph?uAOx-s(Q7?YIC3*n`pp!+laN9lYqeCUlZRA}xE%sHI~mbp}^I_WJD88Nr0K@=SLgWZH>Y*J!}t4ONEc z-4`j@PA7P(!40D53#FO!{ju0^@+Hn>ALcu`{KFYaq8yKL`oL#i7tz_4AeaujmHT<_Z2E(MS4AFsI^At~O!UqzEICKJZ|ZcI zC!3WfuiXltwcUVF>Mwo(DDxO^4s>U3=$WN*bQ%1fD?3poQ|Zge<`~^CJGuR?i8fr} z>7Q)rGe!!l&l&XR_y38F`F|jC{=YSK6GpDUD!}#6&d!9`B1UEX`RSEd zl_hQ5E74I`gA#(}=*$O2iz9C;0YHKCA7-V?4=#1%HQ0l5T={4(YRph59Gp-ww#xYa z@%6kT=_NQ3OV9z_Bb@ug4dtp1%kR3pHRIUHiZU`eKjN!fM8(zN-T)!wZ6500VJlWH82lT>!Rd096)DVfy6LV5)FSdBGo924nlF1 zJaqeMPrq%v*7Sz`tBqi)LEC3aM`|w*y*^|7Pvns3A-%lU=d-q)daTOzrCf4DotR<& zJ^>eF1AUNsB?P0FpOMJ5Qbe3&6Xwb|VtXnI=7a>%+O} zQ-WB!T&e==#R&Dd@+3~g*XGr!DXxDX2*;SDF()yh-_mn^eNW=Ew6OSiaL^Po zAKYyhUgz#PM?QrP*MyKP?)ya!Yn>`Yj#P(WbjnWi- zgBT*@Lw9cC`em-5t`*i?!vq3P+=Td@Us+V1iIlpIvw$}$zcbA+pm4H4jb~rDzqbYn zk%Tl!)3@miLXBmecSycJjx4}v*U!o!fX`u;A-RlTHZea2tI zj`qn+rYl?UBtoF(**MO>4wt+z%yK%`EmxsFJ=80hy^v7-ZSs`&H2SKsXw{OD^ zqpeaye_XN`;^G|MjQwVwQ2lN4ny?*1z+ZpzEwvi!agrDezn`$o1>qgZhG|dvLhNoABDk@)n6zce3b1auvup55^J7S(XHD5T_ z#!jMvNOoZk&&3*~5y6Jbzlj;5w~xJiT_5hg@tU3djD`AyF{Z?>qA!Wtl`Ui4tUMUT z>6+|YiGQ&ifQH%G=hukMpPH<{I1&n$E)VDm7K(w8_niFvCLbn}OoZ^q^qX%|Z3!bL z74m(J38qp5;_RD&5|(0yZ}s(W2{TekyXHeP`NZ>!z70wf{j-vLZ@B&tpVz3)aj(w! zzC0E})HSWuR^jalzVt6KUqbT2X8CJJW5FcCo!Ye7g3eWH5p`bR6gRZREahfH6)m_U@rL$vC5MQE+(jYeb zzLhG?)`Ft9=Uk^@gq)ibCVdB=#3ViAn69U4JJv!#*qWY2+o{ym;(*Fx#qYxhrO>Ms z8}UCSYonOKlor|2(M1(`fh8wDVV&N(YZa>RfLYl^O>ZCrgXf)$tQ;K$uU#3LomChR zi;-1x^6-c?Li==ih=7XxpT#f4%JNMx)#d?vHQ^LyMq1L0ANo$azM;Xjr>3Gp1T>@r z!y+J#YW4%%O>b+^W5w9%l1`mYZ@u0OK{o`e!pqQG;3A0Y5yTb2(@#7=2L+i?*@xCt z)xzJJTbEM!pOZv=?GIIUuCd(>8gMmYbzFRR>h#h{AMlyc1PMCMbBTr@ zau(|KTipWn(Y*#WJry7|Lk@e7as9!d)VX=gm|D0^IT2{DOkbTE{SJ;4tw2E9m2nlW z(*w+9Ju{s7IXD`N9jL0THA6tgf129^g``WYKl8uWH7n{`DlPCf9GIJ5+LXykt*tY< zvjo&3uBEo>4v!V9p)Zs;nt;y%?P7CiB)lj&tNfzKk7pxIq0E~g*rbAM?7!>-zRCkgQ;dF z0FFTL%s%3=<6%~wn>HYummtij7h9Dw5!O}<)oR%F*Z8bKF~~>u#9(wc!(ZY9KR!jg zdGa!R8`7NwHM3&i(S6-o^CtF6Io>v$l+k^4G8y-Q+f__-`{-IlrRJ{Tc`xH!KNF$Y zk7!dTltGURT-&X)Nl!~*`Q@t0Q@1dXU8}H5>PuGwC8GKnF=$}8S%eoda-Up0-Ui2n z{-GkAleU?lz(gXS(6^0`AetdIqNVJ$VpGstR)+g2v6@-z*NdvZFtD+*W4?sF6?$rsf3getG?dntKEwMG3_S{m{dtw{*V?&SrDY^gO=h-@MRMb=&$5ah|G>;HJ;dr;24 zjPzif&dATN=XK(Aew^NPMyBiN#TE=!&q7USC!B(Lq1lueO>mY$JQelSBrpXrrQK=z z%>-sE3`qw^G2G7OTnfI>=nigUYOUIgy}Yq48^3ieT3y}Q$u8nzbhL9!_ZcCLK8YIX^)ZCUMX-!jU+y2Q&hczP2?Nc^o3VDFZ)BnSz@+FtW9wkjq4Gsa0gKA- zI@5O;e@zz0Qf~v%8UeS_<-i;pFaqog1D0zOG<04o(C*Ve2=$ZMZT((c>Blj4u*cCB zdZaeIxk1~u=C8$(+xjZIHFiOD!FG9H-n93I=0$_ z0&!KLFG(J8SWn~()q@9y5A5YbqRxg1qRr9$mqGn-&i5a=cWJ5KOUX*<+A^I1k*GvO zx&<@)%ACKRoFr#~#XvNvGsK!%enAFnY?qUXp*=Lq(G;dK%xtD}Lol~j2(IvQ8#uo1 z`2eoUDH?h>n*Wu;4-gKm*8qTzF+J|7$3{G-q+e%X(^x`0+zLl2tp;d9byX|xKMen0 Dp5}48 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png b/docs/source/Plugin/P132_PowerValueOptionsV2_INA3221.png new file mode 100644 index 0000000000000000000000000000000000000000..76d62a8e051fbdefd107ebe05a3ec608c47aeae1 GIT binary patch literal 38895 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�E+fm1#pBNia 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 Date: Sun, 28 Dec 2025 13:53:08 +0100 Subject: [PATCH 034/161] [P132] Fix conversion rate settings for INA228 --- src/_P132_INA3221.ino | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 7bb3dfabd..fdf471d46 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -288,14 +288,25 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) conversionValues[9] = 0b1110; conversionValues[10] = 0b1111; } else { - conversionRates[0] = F("140 µsec"); - conversionRates[1] = F("204 µsec"); - conversionRates[2] = F("332 µsec"); - conversionRates[3] = F("588 µsec"); - conversionRates[4] = F("1.1 msec"); - conversionRates[5] = F("2.116 msec"); - conversionRates[6] = F("4.156 msec"); - conversionRates[7] = F("8.244 msec"); + if (P132_DeviceType::Ina228 == deviceType) { + conversionRates[0] = F("50 µsec"); + conversionRates[1] = F("84 µsec"); + conversionRates[2] = F("150 µsec"); + conversionRates[3] = F("280 µsec"); + conversionRates[4] = F("588 µsec"); + conversionRates[5] = F("1.052 msec"); + conversionRates[6] = F("2.074 msec"); + conversionRates[7] = F("4.120 msec"); + } else { + conversionRates[0] = F("140 µsec"); + conversionRates[1] = F("204 µsec"); + conversionRates[2] = F("332 µsec"); + conversionRates[3] = F("588 µsec"); + conversionRates[4] = F("1.1 msec"); + conversionRates[5] = F("2.116 msec"); + conversionRates[6] = F("4.156 msec"); + conversionRates[7] = F("8.244 msec"); + } conversionValues[0] = 0b0000; conversionValues[1] = 0b0001; conversionValues[2] = 0b0010; From 055cf03ce333249b054e9260bca9dd6b5eb1eb4a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 29 Dec 2025 14:13:29 +0100 Subject: [PATCH 035/161] [P132] Fix defaults for INA228 --- src/_P132_INA3221.ino | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index fdf471d46..d0c0e79cf 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -201,6 +201,9 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) if (P132_DeviceType::Ina219 == deviceType) { P132_SET_V2_CONVERSION_B(0b1000); P132_SET_V2_CONVERSION_S(0b1000); + } else if (P132_DeviceType::Ina228 == deviceType) { + P132_SET_V2_CONVERSION_B(0b0101); + P132_SET_V2_CONVERSION_S(0b0101); } else { P132_SET_V2_CONVERSION_B(0b0100); P132_SET_V2_CONVERSION_S(0b0100); @@ -319,7 +322,10 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const size_t optionCount = P132_DeviceType::Ina219 == deviceType ? 11 : 8; FormSelectorOptions selector(optionCount, conversionRates, conversionValues); - selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : 0b0100; // 12 bit / 523 usec or 1.1ms + + // INA219: 12 bit / 523 usec INA228: 1.052 msec, other INA: 1.1 msec + selector.default_index = P132_DeviceType::Ina219 == deviceType ? 0b1000 : + (P132_DeviceType::Ina228 == deviceType ? 0b0101 : 0b0100); const uint8_t convB = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_B : P132_GET_V2_CONVERSION_B; const uint8_t convS = 0 == P132_GET_CFG_VERSION ? P132_GET_CONVERSION_S : P132_GET_V2_CONVERSION_S; selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), convB); From bfd22329bfc334036e22aa443dc3aa84181b20f5 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 29 Dec 2025 14:14:04 +0100 Subject: [PATCH 036/161] [P132] Update documentation for INA228 conversion rates --- docs/source/Plugin/P132.rst | 6 ++++++ .../P132_ConversionRateOptionsV2_INA219.png | Bin 62836 -> 56168 bytes .../P132_ConversionRateOptionsV2_INA228.png | Bin 0 -> 36122 bytes 3 files changed, 6 insertions(+) create mode 100644 docs/source/Plugin/P132_ConversionRateOptionsV2_INA228.png diff --git a/docs/source/Plugin/P132.rst b/docs/source/Plugin/P132.rst index ebd812197..a14243ab2 100644 --- a/docs/source/Plugin/P132.rst +++ b/docs/source/Plugin/P132.rst @@ -148,6 +148,12 @@ For ``INA219`` a different **Conversion rate** selector is available: :alt: Conversion rate options +Also for ``INA228`` a different **Conversion rate** selector is available: + +.. image:: P132_ConversionRateOptionsV2_INA228.png + :alt: Conversion rate options + + .. include:: DataAcquisition.repl Values diff --git a/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png b/docs/source/Plugin/P132_ConversionRateOptionsV2_INA219.png index 405151f88157e5f0eca33194c588a43da30ce822..0c35fa66de9fb1b104f3ef6f64ee37b494632b8f 100644 GIT binary patch literal 56168 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Π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 literal 62836 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'CcF0RxjFZk2~iYQK7Zmv4QJ3z{a%8A^{=8!6R z3ZMk_tGRw?Gy~ij4NMi8Da|#jQn>4707dGQA;hy}pVWju;4U=GD>oHy48!2E%{l-r z%Fnh2gB1!Gs?qmZXi>gV0_A=u>UVvVzw=rQ6d1`86Hgsi)Yc9Ir z(&F@~TV|F*LShYRtBUeOU&~FWHZuot!H2k~Yt;R6(H*6$?aM`-{*ou^VA0f{P5$9h z^lG!tI&`0lNQThyEv&4g{VgvX1zn23ctu3Fx$Wge&r(T1mGL_Cm(MpFN+ZWgcZi3v zU^IIYICTo@kBJGmC%iR=!DenKgTVBb(gEC!%Qm!BkD-q~oombq?=j~wF_{L4kt890 z7DPt{#;N8%wT&Me!y7I#Grx;r;lHsFVGvmwAD%|e zA_I@DT%izXaMm=ZK|P0j$iWG$BJET?!|ye#)92R2sPh}0n#P6aCdGYQJi71SB1d^Z z^D;VQOSWx2u~c2^E!Sze*HC9WhO7t zKEa>%Gc)(%WzT&K*BaH!$ne5g16dkXSJ5{>y{|Oi3ydno8X=^lxGJCG`^X_E>~E}* zEZ=@-wR929yVq%4pPn0DF^I0)Pdsy%A#%xxuCy_O?sav=jlci;i{`bwIb-SM*XXUm z%C;9@FJYdbG)xfhu{Cn`3gD%Ux{y_(vKrrqVMF zC+IXKjXmT&|0H+RoooSPSU3zhvKkLG>kBIR5isqZDQct;+ysHe(s7phZ92IEDnffK zf61F`tH@CrUtL*~Z|D&Ks6XrlQmwtRrSLClESJW|QBkowHK`LSI2cw8_L1u<<cHDWK(E6vTE$Ttwf zisF22?QK)u3x_+%&i<63ohELHRTSn&G<|4n>L!x#Sd`F5u-6w(D+-K|@)GWL9AeKY z;1BKkXp%6o`x^1`)pmz?7B&j8uf6^`~b zvNUkJ*%?FzYQyZJVTh9S^o2Qwf7c|B7U{^rkdwQ!B#}khSe4wORxo=`?iMZcLGNGW-Jon9f>!!MFF0h{;?PyN{E91H|Br!KgFBeo<;WMo2i?6EvvB{7P;k zfByK^3WbKbm`g$&R3@G_E+oKYL;Afy72xjUqa4VFgq9NVQd~AIFq+l`<$`$|C_-dK znZ?2UlS=MzsBe9;x>^hB6TANckB>NBs!|Wj2DCjK-X@pcywHy%IN!(0DxzjAzr!~p zb#Q#y6M{}mGWS}7f66O`I`2fEttw`|GhmXExn7uRun+3@?c4831ePJw%1@=J>EEIQ zB~YfbY^R7i$*2P6xmiq#fIawFMxW@ZD#P^<4jP1Mf(D?W^NSCrM)83jAh~IZcai-P zHVk-#VzYvc4uYjWc4TN*5gAgyE6v2)VarSG_R62OGdprEYIo{u619_yLR=PQ?$h3O zJO-Sm0w51)5SKc+VYeQd5<6z45HMHTiOC+;b!QPXSBU0q#_{KVt0krP4LN}3rise~ z2#4h;YBx=bmx?$KPpiSq{I*)co`DmI!G|p02T|p-X$-GZCK7(~HG=(%^jEJ`o34#NeI z1)~^YMA<8uJa&4{!&ryMXrt_okH$N47YwkyUmhPK5Na}{c2iAbRJE=gyym9YU1CY& zG!ky}ya`2aE1b5Y*3yg{?bL}pcMkuRo+UHo#H@uekxv~JV~V@IRl~d#E!jLdsP)o3 z;=_Bu+llQ5!l|pUL9a&%;CJyB7HD1Q@5snClV#}|0;~jw;aZ_-5ii8di9CSaYb+&Ff${4QFP`2p-rOqam8R zmgs-1u%q&GXg5qSD0#UBk}5&PDd=vD2vB*rNmCOQ$$u)Wq@zpe>cGZ%U{~4KjrIKX zHNtvXLR(N}cA49a;Fluj`S}ugfJJFj__eb&nL?}mcxV;t_Ep&dd$9KC%11)f$WbIo zOgynMoUuE%qFbwrP6)2+b&vt=;f4zEruC;HFt?r#J+g7wV1~dG%tvUcxz!n0L_}W* z=lCnDG|rFP9!}sv_R3smxw`MZatE*ix>~}x zZ*se>LU`Ybwo6xjiqThE*sH%w&O7>;C%oyHl$2W3IXH5^wsQI@8u1eYyaVda8fOR`#8!zn0n|RJ7X}_3s2$h&bY1>z%A<OihSQUn{7IK#4>Dy`aBqM-0HwB*v%;S=_GR5;-dxW!;bPT&}aJ(z)=f{ zOjM|%sCFS&YR}N^`zg^v3Yd-Y5|Q>NnlTaK&%>OB2;`=1qg2w#T1(YtbM>YuQU0Hk z7U?$N3JnY(^mxe-49%vot2sj90=U@A#M z-9Gy{A>8!&G56AMnlIEj9;HzSyOJ6-JB@o~Bb7zA_v3c#=chf4C-U8vVz%AAyRUY3 zcJ*bIqfYIs!dvAF*t!^k_0SA0A5E(c(MdXnj0H^Oa=|KW&OHl%!&|swO1`(39yA6D zD`MM>6KIms=a}O#%E`$d!(I5=q3HK|rzQ3LjTf7}6$Xzs@>5Yu%H(F*u-a4s<;%F1y5BhuIKcctLcajlo7ytE z$xnc#WTcfXFIVij+c)V~U2(B^$jzGOmA2D|4_hUR`?uOqI0`WA^%DQkZkQM_p77q# zxE`0-S9@D>F=#n2*ECQj{j@|;dNF7=ZT8-NeZu3c&?YLwc+aIK>KK3LvYcG{oaTJsB={)c1l7{U zQ&v;yF29iMVL~7r4mLPM^l@{OEi9Dsu)k7|m4$hg%6WEkz#$hU5RWdY^>8b9qxp1p z_s&sPw#xkt-3K{@aAN21BT=DxpeDLYw4xd}Ga%gE11c&su|a(SeP}a#E)ROjVR)W* zmjI9c%vtA+^+ZqUZ6nlOX1v`kI^$F`gdehp@nW+OpGFX^s6+wlF$a`B?$|l=vtizi zyhg6BjIMvSi_IRcd?x*JN-ny=Wq4CjE|0RLxzQ(>xFUqXqdaGX(AEthe*YVXxQEjX zKzrYA7J}9|EoR?U;4298m}hBIYQY0jP!V9frh~pAj2z6KBvy!llhO#1O-+~-d?&$` z1g;Zo>_&{=pZ>gr$QL_`_>Na3*894@^1*hcWIy=YkwRhln?>N6%sm>-ci(LPYfGw9 zCn)i3vi7>O;DA}1Xq=nxE8>d)3c%|tj`Nk^h{8rS7GRm0_dVZk8& zasfF7`Rno1;>c!wgWbMV`YlwWUhlO17LT^@o8!>{0>DPgcJLY5Z1-*1pbN$h2z)&l zhX##BK$!$uMkpJ`d$G-~B4{Zr$i#_mcoC!pjpqDRUIkpBx@HNqH4E_WdT)De??2So zqstP=``_UYf&EG09HW!>izT2R8&VJ^+M78wSED`IiMO@^eHm7$<$B*0)qHO*OJQxi zFIAF@zHI`PHOiSpm#;di`h}q)7`qFtN}rG?PVg;N856CQ)X8~RaT6p0yZkSn`;P}B zk`cSVnBvQcc9|OY1obk!mgt?HSAG?Um9V9RhQHyfzPsaW04j0UJKXS ziC=EIyNhC)rkDU&uME&;H2WK)*u;yTF8NnAconCa|H*cjCgedL6n@G5EaVPW;im<$ zpN**%bCB?9-n{@cc1xsVL}>LR#p?fzMsMUO(8amOLc}SsnTh)VEPb#vozI<|Kcd3Ic8fX3UH4Z_#SS$5M?v&XZ zvK!LlQay0$QQ`HWUZf`ucGm{z?&Ck*^^82{P*c*$6<|?u-t5S!W> z_$A<&`~(GBNi}gR9qW$?`93oXS=K%qjhb;U8hJxESSRx&*5>F})7pt%2v#^I9VB=! z`rTe2XY}@^E{fpsx+if?=ho@7s-Nb)oia1t%a{Xr2+Axmur{b7MUn6kMMCjI5;=SH zP>@7TI*QWPDN6I#J6Lt3mGSWp_YWGQ8Af`)K#65|%Z>($pOxV>jC(v{_ka!n|EKV2 zMVxd zD_MfC0z~pSPpdos(Z8t3pSewsb>v5YJs%^%3RO;FJD%Ml^iR#fhrO3J+*HcePk+{A_ zXp&?Z8?9hqA&b}JD8r(SMNc+ny8+TW#^t|R(U<~RWe}0zrNxPBf(tWR^sj;~ zf6z?`y{^`;+f_Bg?a`3jG%v#WOHv}nnsT0mRw}cgbsuKrNV6&Z{yR}#QL3QU?&SVm z%>?+vTEGTKn!0V$%^h0!{{f7rSLY08BSt1ThEfUx2~gM((Gg=F`3nogx-yA?Le8Z$ z46XT^Vr_?g+VVAHtQJVE2oxo1LMB(k8+f^VXy>s+gKyB)T0u7i#lcmhFW~co@A(rS zbKa5cjQA|?#?!eh z%+miUpsoMT#jl8x@E9hW{XZ}l{3HCXA;rl%l}v&E{&MUXGm_)YK*+}&!-an?%iQAw z>3Vnj{B%AY2un;H>@VmW`$pIh1zl4wY^NIfDsHi#IiWwyPwAu~X%`gLu zEPmlDrO!Qmk-pztgV5^#sI%lgN)+&Wd-URVlK$@FE*JQ^PJwk*^q<(B%eNAPX#D=L zrNC*}=BC{HeYtH8bp0H6jZO?itpeiPf}pID6<}J25eT%X=^>YC1I=s<|D7>^f`M6BBO!8Y8?C;~FCL)_(33|S9{^y3O=B+L# z-9QSe!`G zGGfnx5B^#N)vo`k*hKTJ!_Cc&a+iC)h0nRM;@V5)XwGWvx;kDLUtdYh)Kr&ZS65es zSzg`;l8KEAmS?I~EB{sp{(nLm9i%_9WA_xz#uVC>ajq&Lx|El9i22{Yvb?%HF^t(c zP7n~$T|PUDo`j?CJ;tsn?^gE%lKTXjMZh}di(4dx%>T@{5Am_Hv%hsl_I7Gb$&u#s zO=N`y09rDXzdq>uu__Z$ zKCk2JHUCZ@oYoS%GS=Po%-=gx z?Guby*{9n?sV-JVvA_4PX3&laAjQ{#cDgDzLU@%;YY!tMioz7Cx0#M2L;_#JnsJ6z zafSOkH*E@jw5Loq8;SA{PpTGh9r6N3+hOotMTY!X8aN(0N`e!&yK@qcg>_}+Z@9a* z79SfM+bx>!p1c1Wy;UDkBH12bhz5-mXofQ)eN??1V(3`7$U@&t70*xBAmZ7w z`!`4@{bT*{5e#d2yKki=&Q7zB#+HYkva%3f&V#SaEW9&n>$E(U=Z&8u@9F%24w4V# zIb@El2f=?ch@bZ){EbeB4>t~@Z8U5*a_a$ZGqVhBH4+$!s>wjfK=`?4i}8KAK{HhI z{eAtHpsuxT*NZb!=m^9d-c2R&bZRd*+rRs)b5kZDyiyq4V!JTEkUt!QxiGgjJIoSF zhbSLjHSa7*{~s*?SxC+Cqi>NBI+tIREX(Fs6>7VIj{`YuB5z z1rx~PJWvSXgZ6!V>6CFI3nn`7V)z*FI zz`^Fl<1hE=@h3v@^;fo~KPL9}(0`Ow+}xKcv$NNAtRZ=rq^)Z<6ZLaIUQsy;B6x?7 z6c^>I0B~l}Gu^VC8ko(hm1CW533S8^Y;0<3zd>w$5zo#6NK_Mb)Si$&d$|exdahXn zf!oX$UzCAKuEzRC4d@L(xy$2Sv_f-1#=cx5pJ0OJI&0z2hT;hZeDv0!Cl5feFFIN; zMhUXc@2Zc4secp1)#75?EBrRUeWUUI}{P&p^hc9QW9utg%W+zC1;QZwZF$Zb~R!69(UU$#SwyAqoBF`41JL z{%uBOZg`1!*k|n6{mWFDZne>YpY~9B-gb4s(xU0eWUs62*=b<&Vjq1~9aRu{L1$Fe zm{zXgQ)pFsGz!08&Y+4&4jWZyGjY+<*%}s3gypip4+4$b2WGR0!J3+Snc{AIn*_Kt zfiY`D8O0O$+PU>Xh!`%90NC5S0-YKJviK-h^xxXsO&&evx`?8RosI`k(H-gOPw+!d z0l57vhhPegg3msh8c7G7+clfVdIo!zp8N{MT~x!v%zVl&e-7$+17M}8my!@%Q+;+R z$dB!1mar`QuT{hg)`2qp{#nfWutxkT*6T!qfcj z0+e_cq4sZG22g9nn&QW`WXk;lA^Ok&)#~vySeDus^%`ns%gZ`!1HQsQ|09nSYdAZjVMUvR zC!;4%%b1|Gb}T#dfbg7^|B*>DDk}5p3me-i_ZeDHKC(SPmcPf72oNaRa0q^u)EOti#+11MxVv--WeKu|V);PT_Ke*| zEGc1>dS+DA1X;M=NE9Au`qXB`|qyyc=RW5od}H2ac*mW9H24tmNA`m%!BIrlQQ>m zLoQnU<4N77WS(p$k$~pMK$xp9KD#M|gKl)pRZN)~B`}xO$p6zHRb~7Jc=@!B?#8&7 zL!fGMU$~#= zefPch*gw9XjFFLxWUVZj=RD4d^a96Fr9fCqn|L&&*}FPF-&@f(ag7d?D&& zW;rcu?hvLvpl;Tolf@*9Ec~)XxfLF0+&!2Oyv<_5q%U$zgDTyBPAVN5 z6{MAA+!`6;O-KK(e`x{vkmn}vx^mn7_Seh2w9G1o3_U8# zpx9Gg?xUMRw$wqQ-;XHBpxy8g1PK5`0b`+HaeRrb^I+5>m=rELt+mLE39))J=T38r z1pj^E1J=lo#TKD9$q{7pV?~??B@PIlobL6KAaqILy zpGLSEYu=laS zvMjA0p!<66Mjpf-#pum}ZcbKR=`bn!@3xl;XjlL}vIyv{EDxUHU{$MVl|=+R6`zY&ehcyChde`s72 z&{yN>_}=F3)i^>wPd`wX_mqm&UpHfapaMWI+1)$LiW21%Xqt;#e$8>6E9h1^c_l)P zhiU9|p>YTga=bW(D23j0zXq2EwMLK3?KP*ui^wul_D1RMvu6z*%6UhlZs1ZkqPADZ zrsHe_Ok2N>-<#GaL)Nh5_R?P3{Ui?8D#hw9GbgOxcdfyTS3vu|>imAR2<-ZG+xlK$ zro7tir)x?h)Wg!lAs1uv$k$&+bAAA(?*BSmwkn8bEB1u_}`Agky{ zF`RKcNF{kB&eb#vd*iEP)TM!Xkj!YS(o`eA*QocO(Wc8QOC9WSSDN3%U6L{KDV;t; z;}dAh>zE#)K#5{k!a+bl$Liri(CY>hZ=5n<;`Po+Ox04dp{Axhu{)k`9hzoQ1QnLy zK?!9Fnfhlq@i^IwP4%6+HE{4uPMfD0{zNYeQLRP+OQvDezM5qArn`78`(@YF-cd>DyrGg4}YK0l2T^Psw60uk& zO}tLggp}dStyY0_xYxV}a}Xqk><6I%!^izq0mzYE&G55vYqNp{@{+52gQCSNsk$9b zSMR8YvZ6R6x!A=K#|2o`Qh9`D1^4y`c zpc@n6VkH6#~ znlrLN&w$-l&)~-ae$kb~fd5F{_~{K&_Q)!eL%>d8&8zyuE7G&{N;J1l*-ZT7QGWB{ zN?(*LkGS@#t-)J#N^H3e=~T{MssP1+H>ZZ!{0+Z5u zbmL+F968WwZEyL{S#_$dez&ZkdxP-Jk>ATOK>PCnc(F>6rOhnfZmE+Q&ICPH}hc($C!fIhT*$vrsxXawLxv$ds${@vsAJ5r0!Z2#PO z|5wPtqvtD6hQGs~pC3NJ)f@OcB7Rok|L=vI|9$?mu=IcPMvF;#P(5FEre|qQ=u$AR z>{6-fTj5vP-)+n#OyY~n4RsD@0ga70H1E$bH%!hss|0UY^_8cDb_C#eJjAgZ z(QP;9uc6N(l(EPp!uO$B7WSndp{Au*0Y*)V!buLw(YIgJK17!ooG@iQOkLV4H2@_; zWPg3%zywKjYZI-#`dXNZ;QDebex?Kb9g9ga{#S=<&90i@b9o329(RRBjIzlc*Yq}~ z=?WPVB+Yh0VQ7R>=UY$pqm##Uw#BKA^+uz9+NXd#*Up`s`q0OU;_m8hWb-h!c_*kK zEH2e8e-nXxw)X4f^YaY6Zs-#j(W7cFa^S_u;A5zc5K@6U+Lc1r=jTtrMSqJG6x-0{ zkA&x6GM+0uS*jR+ehtNo6TQ}DfFw$EAGV<0k=XQ{;aggDe|D~c%oBtRXnn{dQaabY zCUU7rcJ(IrEX3#vr4sOs)pIssx6<)S<9o69F9a8y*kl-a_J19e!B%lpQV& z1I3OWAxo)iX85rEou(xJk{jV>rrwj8#~lB|r{^!N{Do+NI402}6p@00eE0EhoV|m? z?jO1`9U*rqkNbs(2f>3N^6THoBD4k;-Ry|eFHL*<-k7++w_e1loug(=PIQhVC$W-D;KA?G`m$xD!B)1ZNRzs7XW4juQmCdA~ z!H>>t@yli$)PH8-Dmd6}W=qU&2nDc#Lddxg1AB$cSA4op)SAnYqV_WaSv4{eBeG>U z-E9r8vo_pJizpROqrN27;k7FK5}ZQo)qGKNvCpmjG->k3kLbvD&g{eTMiAeN+uie} zA-K1zG7Ms9DSdyABJzB@Wo&K#mUmXq$tNgSoCNn2Gd%f8T3A9J4>wLfu~b*>?(b(* z0TThzfi!`GA=#z)k^i}0rAW~)pC505KLhk)Em zOpM(%xV#k~x4V0t76(kU*nz}D;^yAsxT#G_mLCwqfb_FYtW)@}miv!_^bnQsTm^sA zAb5|idH;AD4c6U-IHJ{r99tBY>y^jeZ%2rkoHe6c}c|e_e^d;!2&N(k5 zyAjNRb(%s6xxnwr;c%U9)Q!HI-p%B60*n4)ZRbD=HL)}5Shis<8ijMnC0p0IO<$MN z59!A8Rd)wNyllwyU1;6)Hqdv;%u_qaeL8_ORFVREv^2ReZaa?Dk7C*n_Lna4o zz_-6$0pB=T;=AcYpvr3aqHorATFc^-D z3bUq!G)NGHdc|I47qA7R};tlwS5Ta2Y`Jo_)d(}KEFRSL{A0x z@}S&MB+5Mb?*hqQQQ+lv z#%K8pKkrGut|$9^!h+@agar$pg+DEwVqI=|xo8GiI-KOdPYklmEK^fc9L%hxR=n9I zT**I_=rRj`ql#@Ug6-^YCOkd+5}bVtkPL(3W*EDKc_mXXBgLNtfcD~n4V+e zQatJ>S3*v);q88+Qn#VNHu+2vn}*hmQd<_WJ=>HiNK4Bv1pHYd+eQbUyNC!59_y#I z@YDEsDwSc8#;&ec-7U={i>irg;>y^f5=B!7!I&b&dnHoZQqpp+6$Mp60vJTIm+l^x zJ}x1%Xh_;fp*tG$us2~ayD?aTXxzxyc9YSaZ0lV$6pTOdno@j4+TaG0G8EPZ8ClW^ z^a9_xf$tj;Y+|VblBhC98sks1q`9t19a;B~D2a}og55ce+>3?-O*lwD7?&YZY&E;B`Q;-ym{Hs@S%V0@svK1Hy%oAjpO*_nJoYBoI{Bn{SY@wZSn8W$i&Tcs zX{8J4Rv8&tMKYGwQ6;4omgep$<~x71?uP@$e{R6MG7O|uoD-=ydMDBhihm)jnW{>V zy=~I+?Dq+i!3Kl{nc2wEE-$4_9CeH&GzD}YgRy`UP|s6pmsb#}yN3D>Q5#6a9$c_t zz7u*YrdrQ*J>2mV|rN`K|v9587dM>@9Q;m1Vm5`;JeOO68+vy zA?o1!E0%6d?4R=L0~Z%=_+Hxr0P`W_!MBd@*Vv>M%q}B=0xjK?V?rz?DGZr<>o!e1 z45jgQ(K-~m*HE_oLU&S zg4Z9RyIzvCL*eb$PFCFa0wj1eYew6I=MM|p(X1g)3#xMj%IOoPb4it+S}Z`j|DU_= zbFnz~y>X)b4oNGY#o9Zr+JwS$mv$&z8=mX>f-`yEXoJQ|a`q(3({W}+tI zi99p!B+@D)C%v%63tYcNT@0|0v(h#%PW_yMoj4=QNiU(txB(@VJvI7OC zwE?9NOvBxu&dV$`!U~yG0db=KVWI)v#0leG@9TJ2f&Sq88^jQXW;wcOXFv>*Et-v* z*7~Vq_D;*+dQ0R>FCw$KyvSQoLFUp)ciDZ1qK}sEvt96il<1lkX!e*p{<1a1nd@583vm=;YWdy6iNce*5^L2X#EQY=6Rk7P+*O$gAc#S16U!pxe*`Oi3<7EhKP^jjbac#2Pm3)J-Y-U*_&!p0JH-q@ zeFt8ZSUTe<{~*f9$;c-Xr#<282Y?JGGfue}IIw?yNhiw4HpmmVj+|N$sH8@q(<^zM z(G*^r48~DKDkLFOPyQ+@`e`7>CFj^bLpVg(1@ay3tRX?m@a2|UPCn-3FDtO{wUCg|*fxblnntzBChjLf5&2LP z$UIF*2-m!@2uRl>V4+hix|vIwa%=BtN8Qq;Js{XG^mvXSN4_5Rlt7MBHycNi z7$OhXAjOF-*qr`8H=oN%ucc%J`snfBT7AILuJ-$e8q=HSNzW%kU%%BU5<7(k9H)XMMFp1sH$JDWlv)Rmr*0ksz5RxzPDpy@>~T^d+h z;AeYZn8eAIt6RWD=Qniy?hPXe;InR;*$`22PmuopzO4zpv2;a%qQh!Oao1{Rao5VR z%@M!9ffQ_?xU4EDzxIoSuf3fK#Uek{ksm3{r}Yhlo%Z62ih7Q*h6!tFiMeJ59hmcm z&BNNz-fr7_&Dj?*`CaTjJ}ff>`a46Xs8d^K>6^Z9BWJt2+a=pB`t)@l)60p6EWP#9 zFDGFw5Hae37zM?tYkET;5x`Qo&1Ere_n4Wnh`r^t2!+s$3Uexjo6=op%8H-oxSNV1 zdTgsc(P0~4_yKYlQCA1PfeDH0^MC7Dw@}4>_AC;!2?q^8w(L@BvOt*F76pszhz|ar$@4m^2*}oMhFkY9VWy!@92F5Ne9tvatWpu z^}EKMa+Fk2>ecQQM@@%A3iJHI`$x!r_y?FIyhR_~2>5kXh! z4Sk>TgZ4=PC8+SaAU#E3ElJHhNNXl=h;nv$^w9(1wX!>a;%)^3Kt%D!`&Q{-h*DW@ zA-#FN8j$A!Q>G*68f5pKotTiOAn|jwwKKQ0K`KMQld}9CSnxAg|8s7!`C{l~ekZ@(SB{f70W-b%t_cMx01 z`1sTt>f6f0mr9Zrfe-gyGWmo z#R_q`Vp+~zb7r~SEh(1A2{m0^d{_^d;GtEW%-Ihy|CW+Qz5Pv<+FJxlrwjcS7jz3h zAL0}fn5$o%9aDnR_USHKGsM2vDqy$f=>Vev{kL@1Wtuhf_lZUYAM=aQYfylA>V01$ zdkTYqTOcvxqoud!@SZ)lcNeQnk8LTUAK_6-g_*ZugO=7TMnT3YDTKhNU3ZLNW00Sr zAp$+Zr^l-ugY&1y`^@}3^oNU9jVZsYoSsVZNjZ^BU_Zv*Ysopb)?RiY)qV^@K1Lusp#j5Nv5~3$9Y8WjLZ}%#&m8a>*p_`z z+$QHCsEE{>(-Jdv^fM2|kzx>$EbI~^j8ohQyF;9o;E{5FP6?CEPtIZGULxS(Lil!- z;wk=dmEs;%Ys=eDHF{_RS>1k%6FcNkidi+?pTFADOMOuHaOVM3yE;Xjn@2aJDozlY~dI7Xry*xriartQU4?XZls6-Vxq99(;MQ@7>-BO>S!aIp8vuZ&(`cW{ef6od(K zm}8sZvvXr}cACvLyIvUEQr&B8K4Gbg;nj+sPil%4B1nl79AK@herNXuON(=1>rKGF z;=nNPy(6+bqxa_NJ*FuY+Nw@|A+vKD47nc)w(hy)V<5@_m?Fv^bIky*&)QfE;^HS_ zlm*wRar7LXh2+-2@?((R`Yu>Xjc`*A7&&FmJTn7O+H!^33$tl;hOcyNx(e4k1@_TJ z(q``35VmFu=HIm#giwQE#$g(43QkWEq9>oK9ANMA$_Q4P9ONv73;(q;?IAS_Prd^8 zFCEA=u3i2mu)|~XGEV%2=EB7(_ya4<%$Y6DPKnMhg3W|5i9GD$l!wNICLYchHQ7N(k}!=lW^1eD}!1n^coi}#3hiL-kahld9!IK$1mH0 zX$yVGu|)*Qm8Dh?>j0x!!uw{9j-dmu%q{(m5;+#swO1!> zy@3bN&4nN?HX-bQdd#%$shK>QXlP+sD18A?Jm!`9CdZ^-tm}l(Z-oey<~rDDppUq> z@RZaZox}0KtAciDVHCxSG$dAB?JfWAP|6vqVRrFC6F51ThZmSC>O`35IgsaRB61=J zz$0VO2>I6_{4!+bujd?D9>v!)JlTKHTU_Z5o*>fJmsK`4%K2kn+m4>nYGgU{Y1_v1 z1764OB~uoiV-A*?1!T_cTfOF5R z!`vY1(~lw?g2I9ClPm82M6#v86f136W@Tq888nx);*n#XH|2eBVw3&vhp?&}q%ihF z$>2ADhyMIp{d{CHK%xv2h9GO(SJTsSgh(Ic`T7rbB;jNq+=&S0pah(dXqlgooa{db z(2@ZuD^E2mY5hSWX$RflnL)~7>)jqQt4|)^{&qjVNz9)G?-j;G z&`Ezxe?Ty7JZt?RQqMn1I;)zs$-yb0C~qV$(fuZ+PISSsQ(q2e?uYg#S-_ze6GMG+ z{{3)kGW)1gcy8zFD3>K+FOwg%EiONwN|?LOj9D-Q`uY_Uz^ZN_>74NPVY zqB{I{uAVU;#(UVK;OEuR|5m>K(PkeXQYmJ&#`)5Whk;N0^V$=(3=!E%HTs*EDU0NoOLzFF);Lt1uxjmSNQ^BY7>w^35UPPOlcx1e6ltT~5=?~uc=!PEoHOpLh=S($hBSdg< z;oU=ly*fEo9znH=2eIi()EH(02`x%r(S520&E}Pt7BAWc^7Lyy=Mg*(u%qzTt%V0K zCL-}q=gB)m8gw1Jqs0#2gL2p86y|moZ%f1_)IYEo6aIG3(-QpGSwWmq6tu2$;@;3J- ze6-uCX^Ti-VQbdt&CXHjb@;4*O_MqqURR|)IY?ZylAFi`?Hb6oJyEi@ROg*hDbkgP~a*3x%p zQr~s@n6s$hAO(|&6-;#Nh%hbhAk)Jz_>@ZjEub!?o1t|Eljliw;m6kPZJ50SQI-89 zou;c-oMrW5oziwZ-%K#Yyi0n|%F+SR@gKW4W!>avs|kMTZe%v!45S_a$7u6A#8wsM zF8$4J%#DNfA`O~<^iCv}Da_;TeM=T0cY?XuAe`FyrZ_tPM`OG7QkbNLaO1yLf*15( z%QiG`sZH>K(H^cN0CzX9o^DS`3kz!lvq_?rLiX$WXf(0~lZcj9mFfv3k+a}2CzdK+ zplbozMQ9M(T5(+V$xhEezt3mPG3D=f^)BLA)xPtdp2-rK6zd1Q9e~X@)cJq`)h%Ei zKVF2o3T3(B<$Ti4$d}i+!yzau_@^7Ex&{WwMRDscTP`dY&Lsz%?|sjQovMa`Pl@4M zxb#TdAvvPA8%c=arR-H@+f|bOtnEEYvS)x(|T^j<_(QSo-56y5Vbekmt*1~EdMzo~J)24T;6DV2Wgs)k+$s~i(D zC7t$MKXq@%4=1Z<+xTAW_r4B4l}P2)3fJBUIf2REfQ0cVsZYaLwAB^vW1WSfFemRH z?!WnJe$^q@nzQoE%W(`*m3pK! zDw8e@R zQNf#$-Or`m@|EI~r1I#^l{jNy=8u*AF(x(4R2MJ29i7ba$1}!w>P~Tco9t{s*vaf> z4rq+8BO$_m6Qz&0B-iry<*%6;&u4I5uvC`TN3zRP;z!!}Yv!#BQ$3mNy9@2J=UV#v zjGiW?*HiWtt*O73^%c5Oo_B6)ogC)=-0g+mciM#8n%C*L*A+q$;R;F$ib6PiP zL+S0>lq7rv5miJe6=-hb@G2#8l)Hz$jKK&{UiVCVi?yj!E|_|)Ud<=1Rf^AjrY_<= z|J=v6@7t|PSKmH2FT0U%`uu2(e88ca z)m)P6v$MBsyW9AIICp7YFrXA*>(JHiKe%JtEKTP?GOOg!TZ|~*1uRX;xq9@90{Sfc z@xqAq*4WvA^EChJ>K7@>LQg5JjnjCa_v%&Ehv9Q#^dDqpxb6uR??GB|>okP{kDH z?YxsaL4pME9Y~u#gD|WKB`xCnW2h+Hvudc<_e;*|GjT3dU_4xC2~(#;klImWrtA7t z0&lM5a$XIV#-fRsXVAH`1 zRSV-KD3<-A07*DEOmJHjZ*3T{Ev?7jtVJL@>kSld=2(EaWlcwe5Gh`Mk9)idX1A> zk{VW(YrMnrQ1^V7{>SN5Uw7CA>QCEjm8<3P?dz67K`|cKKx$<`=jszx_JYg#V+w{el+b>aXv$=07jwEZ*D%zE+u%PFmNCCnReNxxWx9ZY=G`dYm?;Wh}X zV$~Q6K5WlR#{W(a96SkN0$Q&;d-~rG21Z)7!aKgqC{ZD`ry?gWJ2<*gK3L7??HXwQ zoZH=zIFQM;F)&bT{kghf^Tp|*h=@Qj}OhEhjRXO<#YzwyyZCm?-xMv z!~!ocAI=o9KTuw->JY72^ct}*%q>_E3c{p5#IRD|wdeLx`#%z15GMJXaXM;*{cA<` z%CjWzMg@0G%~e;h8tlr6-*TeIKK%;eM+Fy~iD=xTF_1gSWhwP&b>?ry~6mge~G6#upE%8N_}pn$$Y!NLJ>bB)mC<|Yk1iF+ah zdqvp4Kr#3FY}_{oIpz}duu6@2>yU&=L2!*k_1YCfgrKuW${@EzAW&Dge5`RQfIv$~ zu?a~SEmb|FMek1<*Ct+Nu0vaA!(nSk_1Cx#@20BAxBSa#0*{eFPo={&KL$vjBze&? z|HHTar^xS3Z!H2e^+OX6q-qjwsWj7q2e=YZphvL#OpOX7l=HB4WzcE+AZ3pM=ZDD^#*c zGt2(u=PhfoL+JYKZG1}%#WsQ8|Tzxln)dA!W9+m^ap0t6#P zAT)cxsWCShLc$%_XY~H~>QG<}77~~WByzE%$CNPuYV%kV2ND9INV58xzPt67Ek7-= z=}!v2wYDFF>>NkTT9gg_8MigC^;FG)u1W6n)s7Ed-Bx+iZQKJ6%u5~=L!%bgqQEq> z+#)@|*71d;Z->J!d2O|!OL`gPPi{m}ST};(sTuwG*VQzT z=3ld?5{%6zAdM}9NULw4&qhf;J^4s}^w)O!a+c1{&VF)smiy@N@8n|-6KrAa2INxIW9d%(lN z+o(V&K!H-BBg6S)>!Asiyl86o>tRPUVU`<7Vca34wA`$02A(dJ8XM{&gb~1E9)&&` zv8ULAeX_vX%%q_eE7B;r)<81j>uhAhl!JRzyATR|i@;M(D;ij*h)$;SL0!4D;hNA3 zNm{q;AA$gbbDuD6n3w#6EH9_p-NX)@r~mX zukyU%B!zPGflcS%`{X#xD=QBlxJv?Vo9Tg(-gU^@9Aq}sdx#7kRtHM}aD8XyI1F&2meSJ)NbBY`w*0;XIJI+nYRtyu;L>bpUnJE1_vOlp)jX;e(c79^KLPwcpuI~cGRC(DOrD1jv3o7vb$7lwfL|eiPbG)s;1O*XY-j982Ltuu2PbY*vJq=F zJZ1Vj{nzICj~nksN(vWtlkfeb#;u`CPRFJ^S&?;Gg+ny*Ba%k2$`=W31q=G-?`w^< z41oIZ-3@l(8b$02zAG9bCax6MC-3FC`x;@sneVHj3{XTw0Lm3Mw(0Tw%1Xf~+zj7= zP+6x~{>+2@@S>^vvF3_!o#+dt!??1vu!+@hfr~aTEsg9`%q?~-{i$T+_LvMtYQ6=~ z&jAR4sDK<70Nq;5NJst1A}FS=Yqu`t>|9GK5JCwy1Nr9b0@nreBv%UC14;B|H%-ug z1-=5~P*|&}VNAGv7isTm?%m$zsQAM$l9y>NGQM@1ti!AnWCZsPNm2Syta%2WCt{Yy z8Ucd@kSeZ-Dz22`19@i23wn@UQ33$H5syfZwTSfCJp#ZqGPy-+nYUUR4ck;?d7Vvf ztwNScx*w}5wk*xuKEmKwpd?;uHEsef+Z(n&z4hV_PSu`XB$NVplw8Lxy;t3{f51Dv zcJY@Ml`iCP2NuFa`0XPBoLOR26RJ2-52ea^SIIr7p+xqw?(@Y4(@BN8Wl)L4pvSY-+I912ZntPi$xInFjMd&!KWN3Mh4?$aT$MU2YmO?_ z0aK(Uwk}^z|3yIg)$Z5TggP1C8@Hr&f{TW6EZ3iGCDcBzPmzZ{KJgzu5rZ`8I>aNl z5Jow66~^oSd?UlreJnQ99^0Z%-Qh#vAxIE96@4JSxgkJ>%$1iTI53vqX1sZXBy^## ziMJhjsTg*Sc5>dcM^M>e=f=~YmOz974eKsp9+8XAcaBR%^9I*hy6-1BdXG?dLMU(| z9$#Op0KQV<0=$a?p!340Ji@<`kJEcN!^<4$AOAxGrM)tTu&QW`XSCP%7 z5&sHfQzngs<|8jzCW&-d+N}wWRKI0*5hei$qPZ8+w3f9Nj?%sg-014UW`wu?`=7>~ z3`%g)I&;K@k9WTCs1bjdh0fC7`V*a`q@}54nxvGZm9cTux>?RA27e{c>?x`UDw6ON z&Ffv{4p$uus4vvX;Os9dE2)rcW>suC`)FozMO07FkBf^>v>q)wS?@z( z<+9%|J1?+jkEbMlzs~wW{h><=l_%09zxZDabxp1Ud4vc^XC|ZxN$6GXrb`PshgEXo z=b$N-6|UZu)6;_P(Wz6M;u}wUq{$|Ee7mHwoG521j^@#X){JpSvX}6XkEnrx76_#E zA%v;wDSwikc=I+lLo(a(fhn&_$pJD37&04k`Ov`vEJ6ATT~mRisZ=vP@;46UGS|m{ z0K77M&zMU{FGiFAHe~8o3Zt*nrSSsHb1-#NmByDH-9tv-D^Adc-0;{qdq?GS%g8~Z z%MSu2T&0jjX9DRc)s`8{eXF;WVb{tvF|J9;Y)8T)d*gY(lel>MZ{BaXMU_~XTg4qv z_WPUFMVP;K)bH&;p&!6R2c_#iZNNTt&TTpTVwtFGcfOghzwI1|&#SZ(H^hE1Yog=e zW_ogFk$!y2FNqH)RsW~;fi>IG<;i1Q73uDqYldQ zvFWNNAd(?WH>@f#QFQ<4pd=0+rC{e!Iva;!4Lc-QJ368$3hKcoCMW-V38g?TVwVQy zUvT6~v-6lUbVa#;7cax*;~z`_a@y#RZ@E7gQn$`kpYQDQ zAsftk*|}5ee+epgI2w4~iY4`mam_4A`3ZJ#O$ZNR!r1-u#^9b6Zw7G-wR2?5TQ6`v zQTGe-Zh#ON?D~L#@b}icthqGPyOsY-n_PLD+)T)(5^FAKT~`CMysbI9^*ZCS%H8_F z^u#S2Qu$DrHK_YZC0#&_kkV9)uG%y;iA(s+u#kt+(xCTO9|Jj<_3C6eJZ*Ikezfki zXK8ug5=<`kYV+FNibU_I{DZbEtBNtZ#0&AQI5?Wz_(MaDQhj7n?r*rLjFX{fX8I2nyKJ1 z8YP$T4%OC=(if063SzkVvpzwI9ryP3sVQ8F^!x7oUi%Mis5-B%AoNoN+q39DF( z3TQAV(p*dkfXgYy&wQ7hT=^a01La-3tGAC&i=Fqkzp>seeoz(Vs|RVZ&$YSvm8pJ5 zf4JZAySeaRIuX<0CUIp-4`Brf6!;KhO%}(o>`!!jp9rHsFGR>1>eBq16c@*BZDUiC zl0paeD@)JNfu|S5#&hy`xSt44BO8*gQ{V?0-BYIL@5z7{**l*m=qy2<@weWNxnJ%o zNdm4m7WKpIK{;59Q3rOWYShj3XZI$Syn{!fGID}kYyaZ~PzrbT4KAam`%@Mvje*yt z_uOltWMCJmLw0e8cJEj88t)r8pe|JEO?xN${|Iyc;4H7z2lKrveTcrE)od&>WaN1G zzxVppIUnSrKigllxuy0`Cn6+~6LAqI=FF0rLoRs$Ig}U??+%Zini$TTnkt#RB=}?H z9!4>?Y5jf;AnXa`3+x$-gNJh16l?eoHM5;_y?LWDEmc_(KlA%{-{6OPFGevD9Uqd$ z%?yQN?y!$3mju?%m16nw+osO<5?CO<|8ozbxJbIAmDi=U$=kC$3GMT#bgnzN)AY&nNb z-Ka-CR@Q0&BdyRQA-=eUy7~aZUR8s)tu5+a{EG`78pw}-5hq+xvd*15AnXb9vJG~L zQ!S)o$@|Xotekmc<5OFG^daUHAVdB+bTMNwvJRi8xRsgNx)mATm5%Dd)>g2!<+U`0 zaDl$gm&!)@?H@v}Zon|WA|PJ~n6LlX79PJ|uRt&s!Ux(HnZcPH(Wq)OuY%Fd4$}SD zG*Lwh(PnIP6pCLGyVaU`pkw?P8b>Rw$pn8e` zs9_$rMBr>7BZyQ*ACz~*o^9L}<*$SM%6ta5E7fn0b5`d3^7z93NM4plK3E+9@(?Zo z-viykjleve*5?aPznC_p*R`7K;5-nzpMOg^t){bQK;oFGy*wH($e-5?5q#R z%o%BMex#xs)-3m zxbFg#MX^#DLFd+hO3Tej$=w7uZ#A)(!AbgZa}XhF!>5r6oRFcJUCIkLHeC+Wna;=P z$Hy~P?}tCkewOfXgTMa7>1p|WDw5QRnQ1X~9Wn<8<;kPWs?O}9E&v19{*n|+_9T-D z;7@j1a~?nNrWd&5N3By(Uyx8(O_lw=si(CKa4qjAfG5lt5Cz8RmbUO9ykPH(pHf8V8Cctfo^7t9=L%RQt+B)!1vELyD7*zT0P* ze7>Knuz~ImXBjOEb_l$$_)ImNhmt@-UXMr(Mulme;&((u>c5!3$D04z?oUcz;O~bL zp9`57#O*SbWM$d+y-FfS#j2cf>7P!Eha2?C^J3egUO+ENUFF<=6GdV~sX0BK-2 zOL3C|nX;{oxwI*k*i1P*QA%>q%2+|(@i;-&~SD1%80vW(R4dv;9jMhyO`Q-ZA-;|HyrH_{xMdZ+WHHbm*GHxw zE41J#3L7k(PN4SB@V@)dJf1zvyqjUIc5 z>?=a(hV0frBi|{Eb%RJdd%_ziR@J2)rZ8i2#E+j|(Tj;U%}tDhPhUV9Xe*8{W#vqK z6l(Ul$?^)oxs6Y6jD0x{-=tKXj-l#S^RElr_@)|(!|;L%Qr$G>z6BReu=A@7`2YIY zs@`jeztgsit}`|FYJ~jTpC2gMVd6zEDG8Gtl(w^6$PGFR9HBB#FO;})=NN?F>q%!U zHh*)61Q6I^mYF`}E{V}@j62=lJa+&ifKLyi?s8t)S5SR6e_Knfac}agfmbXn#wBGL zKeA-m)Z}BQca?-M+|NQ<@e>>4Q3>9#QOnNEE0yLYencsZqJ{UD2Y?FfBc$C-?zIBq z%Sdqfy!pPMC-G?=3HsU^MugFoSn~DcMX^UA3Azw$-hYBP!x4gsUXhs@N)xgjJtjKsvX?4&^elIIJp6K;XJ}lxeRTrfU`HJ6THY|@Ffve7N8l#Q zzO7B^tU1z9EIK+W9`FY-0k9`R!|md7_|Pn{ERfnfG&5r3kSjL676$5=`FKkGDe5GW z0!!Uj%HbQtVa3sFrPE{=_#TMk##P%Q(VEN8H^U?9%8O~3wK2qo9$)rC6v`OMqVEc$ z_71nCrn{Ztyr+snIYvN?I(G`BQZsOn!c~9ERghGY*`c~vl?o0ExAI+lZ~kNCTD+n* zt^azQmt}J(FNO$_zKQzvi+S7005sb6#0T-Y&cR{5=#+pJ#sjV_kAwREoDQB%|Cw-3b(kL`WQ>b%Nw&@}6dZvLH`ee>mx z`wJQX^Fl-o2PXLVe~Np{u(qOZTR6A{x8Pc!P@E#gi__v(G)VDM9Et~Mk>I7cyB3$? zUc5nzwYa+m33Ai>o^!wN{J+n0^K<3dS=npNJ;$7Dk2xmY+)aRWex!4TwEFTnGxR1@ zAwQDs>!%Chu<8zbRFGmy8ER_ZJGsUyzyQxajm#w#!nul{<5+upl@Aqav*Cp3n!%cb zVnE@V4aR4AW9`>Rc1aJu;`H1XJ&e?UJ-`=WtZrh|XX0R*Ld(7y{p_>WSBpDDBWn19 z&l&lg$mXyPcdza}e=S`HKWzk%b!~TeHH>~#GJ9$(mKHtNO9~?+i*j5za)o=@xqGH< zm$2JiS|IJvyhl;6QRwC9h|yued{h8gZ3ncO97tht$p>f<*qoXDBnN$zkPND#XCwlT zH1ktVX2eD8;;$%uJ2*BC%W$YnABbn@!{5_n%#*7nqLv^nt;dtA4%il`p^r%QM+bxdi!ye!oh zJti%2`6bSl%JT9yT~Ers4sVRN2@Kn^mRvbuHW1Nmc7o}7R`oO4r?2uH&7lD5*!POQXe?>c>z-?LTn(_yJD zrVEROr}#0Ab{;%SbOLKBW(xH*pUixR%9@|~6o(xP%zn;r!t|I$B;(f@2t^>fXW#~} zU&ui<@S%AThQCU6IGoN?JRVC6No`&*)}z31fE0j%TsgAu8f0XDJ@Msq%ph)@5BKs~ zQm2tUI(3LS5&W17P3H({=RzGlZEejLNJEv%7YyncbJ*2QSk%SK0dx z;}o3lW*t8BZerDOQgVf47}w0I&;*Hj59tfgnbO@O3ugFs&7b-eJ6HPSe?y&2?=au% z)r|>eUI<4fAGz|yO>{?4;SRbKMvCg`Gf(MjsBzQHOvg#$m%J~q5o04f8laEv0|jn3 zUGlNQFGsf5=jDiqXHdGCLs^>o_UtyAni{m-tw>+`c1PSouL06h5DSj0R%vj;+7DDY z*gN7sY*!4_3Jp$Bwvu)|7_cSvB=WmS%}by^`51S5BKh`)o}RUVxD~lzF@}(vsZkBT zub;T5mqk=b@n@S$ZX2mh$kWgQH}}WyZ>@XW|B%0xhi!H|E}-jsi_3 z`(6SMPmAv|_Bk*qwF1}fQeS+OnHsQW+MU&PN6ppZXzkFCR>hu6hgW?p*p0fJcZ%zW zB3J456OlMNh^SI&vhEd@Q>$~*(IGYKi<`%EkYR@D2wpU>%E9s?nAPpHH2rb6P;jNo zzj>_a5ZSBJpjTUAf&pGf*^CT8)A}EcFtJvPS9G z^tOP7O@AtmZ@6)elBsD}%zKCYbe;Lb7?ww+{AxTqWInMPubX-QTb|yB%xFpS&<>0Q zf@`X=_ic0^LZZi8h|6f{H&+-&B42a>#U+`m@Pq^C>8>dXQb~neS_h}rL%P>RV^8`< ztUpYRgYQup8v)dqklzHDGXA$4*sm*n&4hQ9c+cwHrv>Dp9M9pQhlAPip`zrN2|D$} zS43UO9Z!5g#B$TX?~q-uL#_Gut5{1Y7F>)diq7=(`R+-2-etuEen78bs6%Aa@NaQ< zpw>IRm?JAd!90w}kIIrk?pNZ4Q#Y=ojD5kvTulp(F_Fa^&|$`q(>{8C26vRE_gFyx zSx`YLgJ^8&GvRvS3#QsNq1g9_=kT}vk5TW2W7oXKq>p+7p^!C#C#e~xEf3{%p1nC9 zG5X0vx`kznI}#Y&2f?mB{%dmmypEPKf(64>XpNS+nPr!OwmKq!^vq0FN^w4vBsZGr z(}EEWS3xb5%J>oRwsubxuKr=5GhvA~eQ&$j!Sa3hZVYSJ3S zhTpF;>947~bY1}Yx1;xC)Z9Byan%dcMEWK~&j~!K`*67H-eC@|`6#UqN51%@p4lVu znN$^Xe0!+_Fi3!srTBruREJxxZvHE#R?R*ZDA}pt*D?y`bNQt)u>l+2!DnI)AE|d7 ztvD9ZUO~XTO^mM}myu6LNv(M{$F`UyHqEKjHiGD}k2v6ulBg7M?}__zt>2W!M$hvX zpXeE*13^ihy^@e3!~W2MbHFGBwnofL_ZjZ@5SggvQjT*RSKC!{y}3RD_10zj^|YMN z-Cm+kVUH3OzPT%{6ZT}*qp_j!AvNY=pl#e{n+Q;a#SXt=AN^F)57<2OtC{qI$5B^~ zFhDjp8Qe*tf~fHpKewXhqD*c7PJuG>xeDGxxkxub~lYZcX#^nJ@QzRzLha~xAo4?cgSxq( zWNWQC@<5E85C^j8R_<0lpVa#@`V+ zIktuwC)-j9V+8VN9?imlIqTT38u~KNfI;S@$7%MNq9Fr9kZD=NR8;f+vS%^xltUtH z0zyBm|Cx+Q>b4TX(KI!*ihoV>uC$qUtC`h$)MpUlF$3e9ff1R{KFX=J)MkL1xshq#7d@&@&`k}S;7ocRVnZFqgA zq8Pwp2Iqt&Q%*9*7SI#nk`eIojCxLsm*vDe^CbG(z0i4_K|&i#WBbtVVi0}V){p8# z(~$xxjNVY`=F6iO^<*xXYtO|91@ZMYbF>Z&a{duga?)5XE%$H8G0d!zd3*n zEAt27xwcuXt*L6ck_&Iyi*S=u^3jdVc+loqLoW80yS^mb_;7>f_S-U56~NAt+|M!50&QZ=9*wx2+oL}gg!ysU{!xB_?911Ss&yD7toG-0L~f9;NE6nm!Md)2>RnO!W{mF zdYm!MsoWnSh*mR$MT0a@r%#_x-8^hI<8_5UBHL<)dSQ;-G5$%xl!*Zp58-(Z9kSeo z)hER5ZEmK z$~bc7cG$?0)?QiuyUeUOI@hSaBCE3~3|+5m;ymu1^Eod8zT4X@tX<2;7$q00hit{F zSKDX`SepxvV>?fu{7me1izlxXOjLOEkeZ5zPB56b;P~ zB829!eT9-u47p$$gQN~Sf+0ZjZ#^3AKRtP~a^@y$uSsv|eQtff-m~HZkUGU08fa9E zV&CdC3HA>|Uj)K#T%H5mnb|+2+G?QN#TGtd{z0!oop{|Lhuf3Veb|KJ0QmLw#h>m> zIpnzvc&55W>n3U?z1B{KR! zYet3RvSQiookCZ9=A2h&4_pC#Cq;t5>GhHm+ZiTf#dpO=Yk_!AXGDQd)(5OxSb-y) z8%3U+pH7batizW;=Vdpfkln^DEo_n;kh1?T(OPrO&p@H~&j(Ih7NM5#mcrJbCp!+}Q;V>jB)s0&w_9AWj~XA$%x^jwnAGDd z2-bFfVL*EhT&T`=bC19&PlwlKTrvCWZ)9tK=!XR{*WBDBj2TLh-k=JF1#k45TrlD7 zNqos04@cL+9@<=p54p_q|B!|Ie*V^t?t%AXg3r+$n$J=$*$fSA!*EP+!}eJY(bzm} z(USnd3TTgxRJI9rovi$sZ1D@jP&Ab3LXGO@92U43BvR%x6!?AsQa-c11cK1=xF{rW4g@66<_Sws?@T1rRu zmGKX57!wQhIS2J~j&fZ}>!?Qy1r!YL}_XBJcSn z#(c6EZ*N*?UH?mr4YTQZxj|RvIpj#d-mC++FOk1aRlpS>g)22;Y zUkAkc6)}nt-cijmSZ|$gdEIW!1cpx`I^(KLaIeZXsD?+6{ghrMm8bsarpwF9it0jS zYfo@ZIosq7?r5(~&U=bK2d^&#Gn;dVI}co2d$dU0y&|U2K`e*%k8a^q*UyoRYL_ju zAXjXTj0{OS*Ea0QCo7~gRkY|&S}SG8AfTXH1^v- zl)`ddnDReE%FG3chp887OEzJ=yJ}wSN6*A6S((Kc4Y~Z z6^#&b#pbF1>tnMFmu18wmIBGI{R|inq5~+E26}Z0E2r7M_N)zuSb2Ijz?i=I@ub#< z=LcksUK^H1o5lTJz!Y4vRmXUYyVkl-zh0bVYs2?>IGK|)kyh#~pCbCN>2YNyF}}nx zNb8sBa%x%R?&90N>Vy>3fMOIEz?ZIR{#o67!#IKz@e7b0bWC=@!Ubx2D&C1R$*X7g;&fQJMSM);Xd08pi!(R14mlKz_%59x! z6QH1!wzk7;|5b(!$eyQcvyV-Y?K;Df1KmbQPg?7gQ>tqG zUsF|#g~>A@-<=_4zjh;#oIC|AEG(oy6VvCHdEUYX^7hE9$5*l_r1`MZ+O?=W*ui zC=@9FX+$!mJvf9A*u4oMrDRkX89l4yKuJ#A-x%IXOhv|kWzJP~iHn;nK}MRNUq7sk z2oCjKSS;V`7P0#4FXVvBd&wcpVHx~0K;_Q`BErJxz?9@G$Uma^Tk3U#67p^E~)IIX{k z#&)V@8Y@bzrVFh7R{uxFONxLmI_XG3#eH@O!}C^V62+%U`r=l4ncjd}uM9g!55AL9 z?t;T%IlmCcX8D6EL7ssGC9J-_UPw@oB|SSEY1FVhT%`6%x21jyBfoMErT(E<06e8N zAg8AXEy0A#e>!;Gx|M{!sqwrMJ%|dmcy}2*8LpV5S`_jRW(GL;3jhEzR~30#UA^c3y-FCl zyQqqk5=gw*(fRjh%gH{&I1v|b^nO_8f`n-(^f+fPxc=uai~YkCwQ-UEGir49_WvHt zkYkV`swST8hz$pLY-v*^C0Tp6{86u0+e+!P=b23E{FJVpkVzG=hkGSR;rA#LD90!*~!p; zJ|Uk~5==Za&IQ}?gQ+hAwph?pX9DSkPKwlKbFHb*=QTOQEDWa`d*go0#>o#xMX$R4 zfj=(P!NH;IuuO)Zq_$2*tDBWT0F@F&xdqS*uqk^zfNb1y^DE39{E-d@u2F znC2l$5zk4pVGYcBl2mwCu6tXQ`_4p1Q&&gljWtGSK2{`zm>tB*fy?zJ#1hKn>4j!iC}P%9zN4G-5eH`UwjZ~bCe*p@L)*vO zP4ofQ3GWB3c)iu9+`oCPOLO;gUJrE0X@is)T9=A^yZ(PS3L_)40#8jIDBhkAyL0eX( zJh;D~WL`yWC-B^rw_^WSc#yS1Yb(p9aujL>10pv!5|GJ~o`{~vgpts3>WG~^)g z8SZr7dX9T5%^$Ph`|q(uno#m$9pL73wgkWL{f(>1atFJ{>j^vCVtqIGTbW6m$fejk zxdLn;D+kTX3o;sL)9M7VmcMW=2y?ck_Yq1!+~!Y0aju41l}k|dh>hsR@e#}R98Mb) zL?6)X#6|G4tzt#ZQSe}i<5bkhu;`zqnfWm|1RrN+%VyV$I(mX*Z)Vga~fI4 zaK>7Fdq=erL?&bPJH?^M@kES+O_rG~wz6m(#>9XP{M?W1#W(89E5n~|Kub2=%#sPN z+R-GWIb~Kob)+SulSv1hsEf(+H|HESbeeR!$(qoTZCGcr8>O|6eeO1A+Dcwa+y zQ7xI6;nl5Oq7Xaa!P-P-)#+EXyg%fFECP91s%4e z<_D56s03MCSEV8LE*h;zWR^?V2R%N;=XGRpBh@DJioW=yXJr>gB6*%v^u>93Ow{W+ zECp6V{9a#aKX~5F?B8yuloVSBNh_6JuTiyElM0MrB9EjHV`fZV)p!$7jL@paq$y zQNLR9cd2m>RQ2ny(4ApIukw0uAI5r(B8sBz90)FT;`tFZiuQ(qKr&uhF(~gb-zxAZ zG+@mWzCu{SuH^BfOGzBbeQy=g5KOcZy06LceYI59Se8J&zhBzal&q(OAEP$2xU3=> zKxqHz!&jT_6g$s=L@zje?{p|k0dZ97mGB*ud(%D?MwX;{by$dm2CjN0;)H?%xv)N9 z7$s6X(=kd|M8wdzp>ZIqrk=}v5>EKK8-$YedP|NF5^jO-DTE3rk5%vqw%1v(CI>n%5llL5$YekcHbFO`@%fT%x0k z;LB5hrb8h8t+-6^AkA!U1#_@3#yCB$QrMy0PPqQ-{ocZS7!2fVFF0wZ${b=laaOCED{nM${y(^j8SiH>J ztnEWIKZXIdmM3^r3XDNZ?z)Ht~kW7Inu8u7>so z`KYL<%4bGKM#V|9FO62_H-DW5Pj*Mh|3%-F$O16O)Xkyz<#{Z}3s0>tGp)ww*`|zQ zRXTlin!;ox&mshj@DcHgTK34>5bhxrerfEz?QF3io4m!?!B*&<`4i+}>vZ3mEf(9E zFOHJmFK3OlEOlqL9RWbZ*8P_FXjK+e5d2Mr8~;>Ez@_C{IM|s%vH9&ozz@9qRPCqA zled9Gqw*4hRA^1c8Ax~0`O_0zq(}VJ+1c~BByekUA9#K)so3@2fHRz2UhHf!y#KqW z5eh2-p{BPZ+U%be1%nme#K4=Br>IVZ^iy=_)z6#>k*Axuy^NK6(!1ZyU2-cAk573F zkGt$1SKD;uAMJ{cgDuiiO$(D^oEDd)3$2dvWXcuE-;9!xO}~-VD)N2$-$v9&^kN&8 zxz5XD=(HB8>v~Yp&@QS`FE}mqQcQ)El(Z80ekD7h#Wo5UUEz2DbTvj-MyokGl8t!p zV|g?3zOK4{l7}VnXJzWk3L6v+%-{M3DJ{_$>EP6omM<7atLMoERO=v3k!FCRaYN5( z(@&X(I$)|bH(LB}49f(}gl;;nI?NhU5DB(GeCmRgxz&V`v0?Eu9>+kWMCy;fg42~q zO~#B2N@W7HjbByvg56sA-rBLx{@5xn$?8%#oAC;?)&4qqXGjhJ8<_)axw%nQ4eary zw(gvLRzaKiL6IvibWg;9RA4w#uy*9iY_l_6uA}AGH1l!Ef{&3*hr!r7iHucK^@IrI z7t)4zXmqK&;!(}fPfBivhCCBl!3p1kQN zQ-h81_oIB&XANm=YEnFKyrD{obou^S|7V^4*#0{og4Kp!-SwEOR+m_Kov{H9xCkB3hr#kOem^RRVS9%qklqdQHpaqgWun4le$p*PysdJSB5GbFa%6}sEw z5SxX`!DlZoS?5UcRNsOn$t)~zcEqK6I{SM`e(Ct5x(Ho`!`2X2_&2t4=!T*bsx9ElVE>D+%BQnlceufbC)vs)9uQ{&f~1}#2em@{yY4; zKRZE*kAIyX{O(2!p70*7rSB;MuCb)^&yBB3^Ey((*lg_Woua%#OY=nxCIb!1O_h;B zcbRSx<3IdZAeB!46pxdyRQJvT>9qY?%PQ&GSvE?xKn0|lF z4NK{#y+3r?HBHb2jx2}@J;cTr=!ak9YX(W99rgXISxQK1h7{rewl8Vr=;fvax`%*i zZ-j^#Kd)Vp%P{xtp?AmhkX3ja_Vx7%;hK}Z%lxMNckG4C4EAZnorW5Uc1*GS9~*h| z+ZNZ4T<4%Qf2AK)Lk8V#aRH-8`^UTaJf-y+zN_GmeYrC^Idn==3lb&s;fW?nkQett zuYP*cQN8EbZ1?pOjUzzvunUB>u&$|g7M9rKr6t}kT6U+b5kGt`-Fv|tSD!C-$zl$Q z$4x~)Ma{J>vHMV(vPg3>eU$SU@pukf$#uSeG}x2&KUqL{qz z8NLXB0Gsx+Tr3V$q8Nhdp$R!^OFJsiuJ!6(kyJ!*b#qE^X-058Ubp?sEC%0eYV|nm zdB(nF83yn{VH1+uWLH^&|olh}#YHlD69@K-@a*1_WsD4`p|=^K4Bf$ z_D?p4O2^zgEKgknoj0D1S2LO`L!BSjgy=V2r2;aYyRf^iv431&0v4%ws0zhfMOVCj zRktJ{^>Rz$J!rm+>;7U-#l<$qeZpl*-UgJ?OWY5WbA*twyFeEr$asJ#YAQOJ)le3S zU$X2c|A@Obk_tWApM|nR}^&P>U%~B&IS9gVfY4L ztw%AGXxwjTOjescdfwer24-~LHF8}ZojghV?raA*9D;Id_Y{o&{wW$;s|8A1StJlh zr-c0v-&kut@8gYZH$q|O<@Om9(OO!7v6MZd?UdEEg%hf{0ud>U9HIcYepUnt(Zl(> zJ({$X97!96&@}hU5@sicy0u|72BQ-OI%?M^20EUe_@Tf)Ey*dDyJQLnd#60MZgn~C zztx>e z6i<)2RoNHLtLIc6#sD)w0dbW`m<2z2+iNZXB^Px#D*7%#9v#S1N%t!gF`q+zcEnfa z^6=8#2zdzv!Vf8F8`{&5ls+#4Y4^pf$Lt1kuf|!#EExtcICW}VAL~tbAhM7DmbB@4 z@2PLmmAC83l207rmRDBhw|SqqB2avpiqVF8yc zms%POh`4!W_ z{-n?>7#k=_69J;3F6>$Uxc|{=)$UU_1YNYOu$&V!N4&W#|D5e_yJuq6$`ADuvo^(( zPCU~R8j+7vPGJS&-sPZd5IV}M>v9kg6@jx^fp?&hy0p)Z)18WsSQ5BPrTO@`rjr#- zqxZGeN;f0^C84IX0c9h<-S4@XiH>qk6js385pG0~?P+x~RFU zO*X~$w)GExTt1;Oz$-rUZA;5kig?kOFXEusfr&e0U7sLFEP?B*u>M$mlq%{lungFG zfQ~0)w0+om<%dFZ)!$s2KffIfR*EW4UjO;n2|vD~@rYS+j|uAXN~%r*7gcoLTs1l# zW@OyoJ9X2CZqUK;(N;Rdy?1%!%=NaMIs=z(C@iH{wsV>}Kc2aZ?A~1+d>!=sM%d+Q zM&2wciFTa@T{`zCm-{sPSG9HYvC}W-!X~~0#%Fi|d6=1a``zge>>R77=Io6#R^yDp z*<1_DDM6*;bge@F4^zxa;5+EVvUSSa65n5ZvAGaO6AZ ze*a&$>P{8R)UMro_pI(->*=-LJ)tT}GH58oC;$Ke?TxIY8UO$r2K{`3gaG}ofSyssmU z(h-Cuiok^3!0JTthysZdB1eA?PBEpkyMMu1TKcU7O_5VpAIs*!+SOI--CHfyhdo(s z$@l+xA!}_=sHp$_3V20dQe3>4WZ34NGB7x3eRH~bz8XwXqQNlr0xwev4-bz^Na#SM zd=kY3B@B8EzJ>pi>kj;5A(JIy`Y0McOR@w>DVod+5 z5g}sU;t)f^dlu9Ydco336w5zvPK5B~4#OfrE>i0ZKP3hLkZv0 znffUwHC0audR(pecK|Lfstq65j9bV^LsE)LsLu1JGFN-qtz-Gjy1_)Fjd>|WZZAd0 z_uXE{1M*yjBy@|n^5!2Rv_yU{BAS=(R3SR5*beKPidF@L5#WujsXcv+RhVCyt1e3B zZ{Z^A38x;R%k9xfeKoW;{`uSQW;tueF=~dx<0yf-x|ZdpBo_2}TUAGy`?D4}FJ zowdrNx!oK!i^NZ6rpzzsi5=)q0X?7A%}a?0CLghZpL@!?d{T#U>22w*oDF?cxQ-^a z$}W#g0s%eaJqY~Y0&*~T9`96qiB@_=b1PbwZd?vBO=zW@4atzxqeOU` zgqS0XfHX=B@*p}o@)%($E8V`A_5`}=rHMOw8z1n0k4q`hmZaJcp@gB1k%MrVA-Wdu zJv1M;=?kP{%n^LY0~At6;!ZP1Vs9mLiyVhIRaz?gs^#z!o_ozqFQ;uWzdcT#uCJ>mv ztHD>rg&gF>JM?}_K6**aK(^e-$SZJfkGf4smya0;O1f8A5?FwEC%lc`d%Ip`#M?Z)?u`}Y z@gC`29Xdi#evc@k$fF`3S>m~P86J)!%8qgrb2+R+K!B$tI1=1Z$tpHi%$2g6>Wxu^ zpCkNmuD*M86h+oswYIi4>j<4tdc-Q{FQj#uvh_I;$k9KFV`3_rgS%KcVPUFd3*f)1 zWTG>4-gHA|qDF5ZRz8ZxN+|oE1kKuLlWhW!CTS$s?_0e6Hltr>j*7e-W18%N4A|U} z?or(RYJbF92~RpGM24`t2_4mEzQ^b~MeK=og6oMmUXKj^F_MoP%xR3E#F2+e`3gDm z-u%)>ymjLYbGy6g`vIRRZ`j7G0sN^!z`N{kz$YOc)Otr2i#yk_t+?-5F4ovD!Z_#r zjcO@t%z987-JhB=6C5busjFXIei z8+{F(m=->5LYm9ZQg;rfJ7K!^tB3GTbG1}Ku`%UBy=d{tW09vbkX zYtrZOfDs)F{v0pW(GfNO-5YQ!%76m5;~gd*o*q}X$AM-b={;x=SzL%A_rnR;!zJ8d z@OJtY89g1$OD3!~!EmXKdn;4n3uQkHxZ|z2gKrSMPQ2){g&QZ;%T}NrX4e(|MUzee zq)~n&Oea-O4K^2JGMw8np-;DO;v$NN^X@mSTdk8{jvR9vnM^Fc4I@XPzm-7KK-HY( zKlgj6c%r?U_j{P}3q5`v8JHWPgc255%LUmE5T%pP;(5%^g2#Okz19_sNSevvpOd_I z>55YL2GGNM_7;}b{Kh|LXyyiQK!lV2$3h3afN0MPuM-;wzl*Di1m-TAJ%`4|fE?rY zUwg$`I%yP;WERE9Xe8R3WIWt1i#*&wswZ+clmIjI$G~{Fu#>~p(GxcMANQ35fUshg zYYid{)|~CELcKm%CVtV+fY6=GOl=jWulX=N`=fEUg6POGT!$aAX?iGWHk%b5b7Uj+Tb&7??p`}8TW2eGJ7Pwb!q zu17(&>L*qKO+y2Q?B`hTpUAjfP8LftHEc;-nX#hm>I)X(^wOLDW@^np1-(^%dbet# zz5#SST}M4dmv6g7BfX->pC12Y*2Lk{n2h2qbGl-1a zFHdm7tjLFD?4!bw_zRs%-YP)S&yc`{|M+xSK)z38F8%4Dt?skJOCf%Q0bi>Y zYZ$$vKN;z_aFYK7H8esPa^?KEOmGHrr{sTHSqo3Irk=B7lIyV2ph3T?9&6Av@XofZJs?{||oj@zif zyf-h7gtER(aqf`Tb}n>5o!Vml;e4;K@@7)mblp2vApO@p}1s#m8tqss~B$>7k6rM(&<}R7|QPMZkSNA zCX{2!i>j;R+zx;3h_p?Tcl>q6&W<${m^Amle^o2iaF$}!`U;tw(|tP0zXbdVHq}s; z3e_q%c-1~%OQHt_DEL*~&z9JfC#Rc(kLL0RFwifksCdu@iji`WFek-C`10J5T-;2l zZM$jNwJ!EQ`?;T>ZInG@tCu&Cj<;f^t zt=K94&%u4}&majd{4KtEv7ev{A|5M4PEJpL%zA$KdM^}98PcAY=%47Q+Xk6cZJ|d! zPAT8f(RRk~i1&UNq4gvhk_U6DC)F==w?VP){`5l|(L|9(2_MxzFqud}KW}A9_|02K zeug_E|BjD91y>d%VF;pOrU??A23yb@3%GG0WTAvJOr`(3XBfE5A+#NOfN%^n+_>hN z>ffe5ZjaUk4RYM=|1u8Lf4-Fh9Ax|F$gfKw0rL_2aY?1KpKa>%yh#6=E8?Hb;9x1J z=?hMgU40P8hBoX@M5$CFzGOk?q_fsPb8GPe-40 zz)RJ!YUWD@8#+5a*?B#o0zlM{tS65Px;~U-p*->z%?lyX3xljRl=IbS-~4m*|Hb%! zj_{lT5IXVlTcOR zTfpel?S0hU&Ml9orM~yB)7s1cI+Y5@HT}OjH6CC_$6J;4upouMRbtVEe`P%t)k`&&;>|PjA{-?w>gPX8 z2@2!yU{UA@O3KO4B5w>Q$sHOF4Lua|QCD7)0j?K2@l!tu3VO!&&90YPUzm@Lpuy$i z`Wv)^l8u$}n19eb^=fGs4us~vVTB5<3U3ZsMOr zD33HR9_epqbr`xyEpro2-Ag(o?mon7Z~gY`g|APB{hIo`Tru9jzdD9SPbVR+=d6Nv z`z=zc@K=%_5_*m`Nb=f3nvxQ~zXpsqF!5cIH;eH6wqqk8=YrdZ?^Px0X~<2YNjVy| ziuoA|#o5{ELxMz+CDPXi=h&YWmeTTVh-;8-Lrb|f1hILgmALoaqvmw=Fu!gNvRS?Wv}z*nXYZvgDe)PMv!pS<)9uF%IoW?Pc$mG%Fy?Bq8e z^4ece^o-L`8n2qYv0ms6_H1vRd_?(Y4%Nbf%{aKeQ>(<_4h_JYs}8YG8Dzis37><_RE*a7+ugko*yBhAzPph3Du5Wwf`lvUTUyM25c+pp`!gx zYDIgSSf0+ty9uj`oH&UPx)O&r!D^zV7oWCu_7m@AAZFxc=}x#)lh! zGpHk8vuSVOax$O}T4K*t)7$qIrGH))f`4pEQNyZOw=3sHNF`~2YU9_{OqhUb%H!*G z-`Cdsvmu>wq-wFuu`Fx)sZNI^P+MlBVL#Mb2D&0Qb`5M}|M^l|*^J3=w_C>7#239wF4IG++FmT2G&06RFIW~Cptif1jEp%*c) z(?5rianqji+?LW0wc|!wZ`X7VvfR7GT!M0)llslP+04kQ!Qn?sAId?!4F$Z+vPhL zsbu%4YYVzs<$ZGGo3|KphTbnHQS3YdsBuI40wL_tp@L0T#w?VHWVms;DY|Es$Al-e z668=HZ1?PKp@*ULGD+{J5+SwC^fRy-3_mMwqC=%$Xj}wps*?@GPr^$`hq+xsf71!|8o4B7-7gObRCJ4WFO+aB*%dLpF>}^buRwu z-~8Mw{~I>$IZO2K)8WH78^5TYCx9GO;bZy$Yifr5J1?uRu{y86Zt3K4zzCaLLm}x8 zqyK?VP=p}BrE2%$!=Vvx3|KSMBh`E92n|%P3)oCa&6xPN%W{+9z>oZ4Z~kcaFN*QP zRPiPpFUtJ?jXB>M5yGHcl|b_kWBe;&^=B}Hq8Zd?7|%Atd$`%k%7fzBT;88)t>dq! zS;i&zn&-U~e$mjpnnm27CwEp4`1ux}YLB&gUCBZ*>D!w>(QH+BQ>0zvrgVG8*pK!Z zvFQFJG$__R-K%X@l$2pc#$^BQ_I3zTFTX%D4cfSPjNo`*zT&G)I+hWUz6=Zs zoOrLLz(nKr_%&;Fl)|&USf!YIoYNNj6CbdbSiW2#BBB&x05XIgkV141qA3`@Scc*? z-hZry3#e1~@)D@6&B#$N)GNN&c*F0w#!DvR77SRZiK%ZTfvxUzkmQ<{NVcp%(AkBLMllKm5f+*p%$7BQ{nfvP+zTY?i;yN1 zDEb)Vr;xA4?DF@(_jLpX-~^SeL${w%Dub)<_+5%ZiAjiy+S)RqTNKHQ3$)46Sy{zk zbk(y}{P%pFfd1o;_q--UV0|4}2f*MuKxu;vcG#Sk{F5N6j42^DCK@G5770aERg{b# z`o!wHYY*AUpMDhYvLa<4dufgPCxBn~QKqOaQwmjtqMU`gTygWu3Oy)cuvB){{&bz3<_N6o~KhP##Z>If_$N&GWg}+lM`7-CmHCCOv%GeIz2u5{b8!6 z;ytUT)?v1g*H?zC@ud3E#(PbkbSnySXeNqD@g)c6cWy%Xm&JV}K*DR5t`r9liMblLP z>@?!N;>#7^`FeMZgA)3}@dMf#+dZxMZx-QJdv&%N-*V#z7~ICog%UJ0TeHw7CsA4` z$B{l_shAyGf8aV+M}(A`9t%2v{n1r=w@7(q5pP=N`B8;n1PiDGRIkOlzkCn#udd3D zguxQl8b34?D9&j~AJD}UaA)t+V(%u;OXWw6z|S*y%X06|Aa1$r+(^G5MGu@?A9GgU zqCB0S4C%8Wo65;VB+|nWHIVVwmNc8oLYSZ{zO;rQqyy5i(eHL619!K^A!u-u%y0r$E?Bw=VHd}uW08g+ zi3AMG=lxhyO|BfAFe}3=>)4zKXa$3nAXD@4Og^DKEN>47^GD@pSu?{S7=+424b`TM zI=dy=_WSb}50!RgWrodKHHUW;+r3%7*%u_Q#}aK9ifdZL!DEs{M6mGDlmJuig9U9V zFQV;Bw)6d8jq2d?^bwfJgC(@%s?mTD%8tSxU`@kT|6aCj?p((7?Q)Vat0TSE&ON!aWE`a zWylf!sMq6NW?E~nKdrK*)%fNka6oUNvc>bO|K>%fer<&wbqnH(^NM|0S!r4IwJsy2 z&8~(ybpq@zSOG-K`vE)_#ZjX1H5f6hKP=D33Svbyf;c6*Bg!Et%wb5JAa_Ckx0|GP z$<9gBp?1BfbulrfJz!WC(|=u7nAK)1zwCVhUuIMrt5vQe2F-osX_n565*YhREqm7| zwmqF#BK#PFjS!F<1Oc>o*)bE@uj3CP2ecqQeV&PeFC;{}JcLDE9v~6g1$Px|qQXsQ zw?jCCltbH&kw+k)Rf7h7?6s&Fo`l)lWm%2&gYx;qy#3BzMOz;0Twz6fpmO`@59S3c z8C?{UtAhZ->5>u};$Uqs37~dfPT10vF7Om#o}x?+xiUT?eDc~I@|dvo*h_9Xbodaww??oWl1$)c4Us$ks$b-<-jtXS}6Ud-F%=` ze&DRn6e3NNO%N7kr&Vq^N_a{1<9n3|7Z?#q6@`4%=RCmo4GXWF#dSKx4+c;s^Q3v^ z1wvf*%*w!23~n`_1DJX3#yC0oy3yOL>9Nmaof|{yZU=t9OCEk2jXL$XNYHGh_2dgxhV8scmb{ZTGu2x3_6WRtNmx zk&*N8^yZjVM;zPvh{FSHgb^o%ETNhte=Kth#To9VG^_)r?4dosJlAFKH>FyHPhsIn zqn25$g_<^PIY(_zZvbu!ZlW4Q3G5LG2?;1QhbEQcl1eL+pGb1jEv^-s=ITcihxU8v z3Wu=AIL8Fj!&?-z>qE}Cw_rXul7l=&9{MhBAH8m&#mXI-nxT>QWd8Ek2h}nD9_s~P z(qHfXT)7_{Nk8^BC9uM!o&CfN_BCa36Y> zSZ8kh+WyBe0JZ42{;h!RvYkd}N0V*Z7d5-35926B=7canx~6@Y-xr{8!et@_HIyhE zP3E^kYAW7Eak{a?jagQvEYE~!Obf-j5+ocFgL>zK@9#@~X-du|^Bv1V+HJ!h%PW&U*-qM^m|>yY^kp8mCPZJ*v^_ zcq(UPAen^aiRJm+xJAu}8wQ;0`%X!?c31$rsBbUeW}TJ9YC_A!mLx=s1_P_z5HEji zIcnKQsx-+@I?MN5xb^N*|9o2CenQ~LN0fqqH6O3EJH6 zhfn42QMJKhh&F&0SDVIr`}S;m!uK$B#(rmIlIPRq!0QJZlgn^GmcO8j*V8E{aP`XO zMo?)4auqm9rq^|F99XuT1lvyyW5EL4tZaL{W#qHm46~>?uQt2gSdZEcz-TZ!YLh<- zS<1aydWsgl-ZsA_tQU_1a%8|&PTmSy`{nV#v323or1wXKZhH$V=F!I$1@aOX`LCqW; zhpsuKQ?=XWd1U@b=4!r8OtCJI_;R69-5%zqy$@j~@hgB%Q8C6O=KM8f;Q|biYw&k? zSf+2nx6#74ed6mcvpBBtIC-f{g8SBAo}csrz91>!G~q_ z%d1yMXImg)iWh3uISDE!vH3MFb8>nQJ+IFfF+?VUh8C>6Qg#xTbXAoEHElL4QUsgR{2{$qHZb1n+Ft< z{Ef@a^AI%_{Wdk~%H^*Q{=V~-DW+AQ+>Xi$fVCHsvy&g1AhsG~?T#9#%FtkJ`HT1a z=m}i%SvISo(aC8iW>TtYh8ssXM)_lRYRYG17VC$h{VchBh8Ac(mk~xP<=~f!4C2*M zzTswp{#A3u%xKt%NUk#ct#BTOf#k`H7T*rQH92slnP>L`y_FgBn$8$9vpuE#*b{xm zAu%j}emck+ZuH5>GsVao*^l$O<&?Pf@;2@KN1FXunmq@g^#q~ygnv&_5dHcjBZ613t> zJs2Xd3PuP!;nH;m6N#d4y@^CoFl$q_-b~u#l^J}`;v~iQyJh$3cDN(o1wwoVrJ<2g z@(76VR#KhlRJa<_V{*+WF=Gkx7*_(D315-fp}u&$BWscIO;0L)X%tf-i9%GMN>{(R zU?l=r8)O|vWfhbe5x!#*8uI+zxV7C#a7|dhI9SmlUeR*N-)eXEF>T>#@yb9L{kLNV zCAi^c!GIHZAp68JgNQYtoaC)Es7(cX_o8gsKs;aIN@dCyNArVlM1G{p*PMZEQ)r1y zfuTw+>{_Z9`$uz*Pva?m@TpO^$YhQ=%KAE{x$1zywCwprUU4Bb2sJzhp&W>gt*9f8 z=t=1mJ}yxvp`dWhiYWxx0ds9S>b36bc`&674>I;X@$fvHv)tY(%*=b4b^r1K$8-0U z=k8A7&W`2vH77`DgaaUSB`7qqa<&!@mXQP!eahT&hLId;mt}oGt-ONqIh{k%JG;=k zUEM;fL0;eGvCT8~HIP~Mu8K|cJ%wpK`@wj=<~%a0{^b4X@g(YHLrdXy%9b%C$=D}j z#4qXljzp*@aS9TBjziWw{Vz2l;58Am*cJco`S1eB3BkqqUAvJdRvS^JPaw>>E5btk zk~r77P!J1_1)j<8vnAg+m}BlBWALx`2qT|OO~ww2E%Ut_!MkhNtp|?rW4;P!68;X~ zn|y*BXS@$zkTvCA-$lucOY0 zlP(yH8RL;<{mu^DcUmzB@sn5zr;L4NmY=?b)+HR~327o06|}{mb1xiPHc#jIofY9H zqQHdt)pGb3%jMr*J(nR8$Z$oF{i(sISUng^x$f{KfKYt9OfN0KNvPj_#^a^mQ}E-R z-(%W^$hqIsfWw&8% zhF7^MOlN1x=Uwym+u;dW>QLAjPpfECU=m$jr+kfiK8hy#3HSUt&cfN`U^0FTzso*?ryT|S#z3VZ#19@8roq>S+&H%0s zqOQeKur$yD_G1Ms984nXXgKH-D;rWHf4&`;pnpQj7180Q$Vh@Fnzp` zVtB2Qv|Pb3m+eoqv)B2OZF?N4csTlNUJif_ln%_zZylqbjtLV(}P+xC8zI^Mgw zo~y|M62^XatxTwAIPVbr&hk^bVO6}p3`(D)c?jO*f$>gdYQ`n^j0$H0 z%9rZ5%iNdHnhZo9H*iwo1Il$Lw=(LNFt_3NQ;LfGXZ(auXdZBmZ*c<_tHGS2L>+s^ z1^R~6IjqSc^Z^w}03;od=m|W+&X$_!JzP$iGV-LbO>Q`>BM&QJ#d!p#04AdDPM5k$ zdIXuCUsop{iTRXGB{{ZVo90Ln_jGEe!EnhlZIx*89?(;<-Apk(@!Qq{6n005yJt~M z3e6_{m4SL*`A%s+Ke2}qKGAiAu*_WT^R~U-zL+se=%--?%w~#f9NS~7-c!g@o@AZp zeaSRUC!;)s2jc(`R{~A~mDdD=6oPu%p8_UC&IDVp2hz@m5wphCV0v6(!*q-t4(g4^ zQt`4TVxvSLS4+N}Ay#elmFJ%%r!m>%4t3gempb4uBLccV3d2YNDpDH1^wVX@c2E9H z|8d=sWb6d#17Cj*`a zzO?3)#$r4F$%)PGg#~y4`Q+$SAwN?Fh>2K`s2Pgr_tN{pAuKb}evZ<5Iqh}6TIeP| z$X{)=GNKdbcu;>nMNn#WvnBr)HZ;_(sh=y^`!w-qd88YkIc)HO+app;H*L)ZC#TX0 zXG(rV$|6rmh-b@ji;IxF23CwnP`72P6}5>1cvw6ml#K(@c3Lut1r7^kJ4VIP%}XP9 zt#fz<39X9akUmjdca|+e{6QdhRX1f*v5u#z79msPbcDr+m*N;|-)cP%>(T$2cmX~6 z_)}kH8hh*c*=CK|6ajYagY^SGLkxS^U2y@r;mGW~vY4vHUnxOoFfZjowX?s(|5Yd7 z)_2OSGj9ck7$OC0jMbX|Qio7-}`WM3I6pW3Eb z$1JP7%>63X@WJE#O>zQVRKEPr&x!7h=oRf@eK{E)KUIaYnq2bypo8sG_^g(AgZxff zfR}rnlKqSj$FI^QT9;u9C+Gk}U4?NgjOMD5o~AI^nWli_^)>4Sn6-*B z(Y}x$Pp*B?`r6?cARBIB0RsC{$Gs)}wUTZj7LST*Z@=_yXrDvv+m|}qIR%o}Utf<& z=}LNLFwwMX;SW4ce@zMrY;Si#^)nya<9-M8;of#eEWQ@EYWv*%cRK5g30hBn4cu7I z)sPyueTyA6#NK4QcOiKP1h}UGH;KY#gUqr_g}OH1l}dbV4C$$pdR2r5o@TBxpRvTX zLY<$iVf6mNAHrzZ+|ONULlZRpCcb^qQj1aBjNREe<876_uE;5gy5;^{>!H`}BjWzT z1Lwmx-xu|c$OrTbZ@ela%@oPFxyVoJPSsTmw$=Wlz|b5!SqH(&s4tGVa^G1S)7JGb z3|BV9_?lAQWtxI0gL;ZBW-wqt9~7^gKcuzUGZ%$c-u1c9mW=GHN1Rof_wnm)%me0U zU|rE8_syoiS#n;+EQ#yOyQ%1U1n!%+px(XGPCmG-Ghb-2oh=zg@?x}~GfNzuYk(1` zGFcG3jC%O&H=DhIIG0aT+Z?t({3%1fauM<&ySEKUn8eP8{{rPY=I8Z~oE7;*C|M=W zN!3d#p;k-L-dqUn&B)j#Oi)n3eKkxJt>; zeQTY*&Exy{C0Fm`zibt=zey6wz%!{j^!-7j_K(yb>VaRxgFl*y35DrC1(O$@gg_;s z+sCZ8KpLGphe56VI1uIcr7rwK*dht{Q|V@rngx9OI7!TUQ;fCKj2bUxT1`#%4h`+_ z%Y#z0!L^PGvswt@L^gY?~Iysv?8Ti4Yy*#a9701t=Om01oi? zdq2DtMYFZO%EC{Wn^BufA{HgBN$Z>LI$Uk>k@?8Fh0D7xbSfomXa9cRrqy{%X>Ri! zLBN%r9czs~ZT(i8&)rVPcRl~^0tq*5e8Ug=c(MP~SHg}lOjPM=Nv*3Yb(Cwl5v~1d z=ZuK}b^6qv_e~2Ob4#u7o;H1`5$zGC-Hv|OoW2*!b7C!g4Lhl+SH$T7YoHd(L5R+H z<0Gj z?}=e}rVhh9`jl++kiEhRl~iUP!mKV%Ua$xP38a`81*jLwpe3mH!q2h`2L7ETB&O6p>S;~E$w`_H9qnGs9{{bc z6(r2kVwiaLO5_H111>HV34r4(6C7PI@R!tvD26qhmiVX|@S5}HH5Zqf`UI7K((m8# z=wzMjFWrB&S+=JUlN47~iFtE^Lp?wHu=Vcl84_Q2M|(h0v(Zgj;ur=7izcP_Fo`5x zoM-GLmjT8AR#by&_w{<)G|DH>1s*1SH^L7utj8j;q(BpGezjef2=ZTN`uE#ofq9q|hzN`82~xdwZC+`V!f%k{51nOJJu zk4#+Fv@UTqb#)~ge;C{dX@ueFCV#+Ks!%B-r}8JFszbbE1Xhnkod12z2*LL-R|!dJd=b}tbN>(1@*6yjf_>y&KfN~-uZSY zPnjykl>b%Mi;P4ME6tc+$e1rG8&W^k<)>2A?>vdV7nBIgAIfPMxnk7kAr2Jl7cB_v zr5tzbl$-;okAHM*udW|7Nc6)f(op>`Q(jzNs>umWPE@N*DK0L4Eoa#^b9Hn3W+1U@ zpXmK=0$R0uG{kLtslkwYFZPFMUR709E?oYB%g*@s-J9x=FoCt_JWMD4->LSXpdi_V z@s$2Lt5oT6c2C_s1r>f%TWx2c14k3CK(YoL&Cz4U*T;7(2) z)dF3EhSU3LL|Wx)+2RTQ5R859Gu~Na6VyvKcotQVGUoSyBE!Q3#I1jNjG05HeKzXf zqYxJt_eW(F+OQx-5_p7&I{~od%4)#Qjf~ai)k~3>X%KoTyMB|JOc!7btrDyv|J|~> zy84fn8oVkBfUXaQBd5YkL+MDL=7gibX6S9R=f!;%c8fb>MbG=O(!aCe|0%Vqe-~wk zDUpl`Nhc8Uwt^&2=96hsAA6|wTfJ_C?xExVQ_HPrf0TPJ7AwNTK&z2&7Jr&T&T>CO zlO~39$ZYg-h_S0DF+1Z>$=W})o`=V~Bd%rFqovjc?D66?xmw-(>cH|@q1uj%4oETr zwvU5znCOxOT#rW6=(V^#w&F&0bSGr{u0pMt{LB9+E>8fViZWS{aJaHYXat@@TmjpE zr91yFF+G>5an;2;y^wohZ?;Mwv3trTVzGw@^?jExN=c{EHI^XB$k-BDy&cThQVO4{ z_6OGqx8;eCl2wdki`j#b;|wKN>CKv;a3SK zkI>p;Cv>(zwUUTo==^}s?PK;F(I+g6tt7Fe>m$B*L%?1plUUUq%I;wDC&9i_RTJN) zb~U2iIII#5Ml0haZNSlQPf0xZyN`D_2MM6i7|+?$*nu}w%$dCpuOt@WFFww8Y}ZE+ zGf_ard=KIDD5}2md9tQliY<)uNddJzcAa4x`uksAMMOzNBpl@AD7{%+^d%0Igg$cf z?7Ova0SWM!z!1=9_ruWx{rb1>&%cnohFyIq)34vTJP>f{^*4u2{@gyv$p{ic5T@p| z^CmI)!dXGGW7#xyye{3+E3K8}SDt0=>&&Fo%@^7m58ODhwVkWjNy6 zHx`rHjWab@g4K}-&9XeUq<67}e($E2jATHBsXOp?&9d1+Q&%fCY6Pc31_U!&*>15s zO=2?@##=Uy3FStua@E=guqoOnkHm(}cI8;}n4=+=oEE7~K?^^pj0ZqTTfSTnas%>1 zRPD@o(<5LbB2zq_Op1Egt-HXK8`IT#x0rR_1NMe7XWr{D3iqu%c$p`^ zVOXv|o$@ozdNab~9pfE1wQ@Et}NL9Rld{)^-QocFz~ zIf`iKDE^Kx225GNNYO9LRcDo84I%1puJQCFyDX|Yhz_8IYEEe|V+V$I5*07;tt=;iU0gGY5An1y-8Z2Ft70`%Fn!hsJ zNtI+hz&V4kiApE)<*8&7ow16JL&nCH3927mh?$!j2^Euvj7fSt{C^{I57 zyTiVcU+|FAodVFHKEu|>dD67V9dpDKdqzW1VM{G~@N}iC^DhyMRKehl`;J{(m+|;a z#V{L4VJz?rhfA18uB%XaVswQP4IO=Kbkv%doG8DFa~%{3`a-}mzJV)|3qbC0Of5JF z>`e0F-TmCBU4lw-WcwBcU;(p%4Ujb15(Mse^gpfj^aDN?z(lUnYtgGE?%vix5HZmt zwlAW)gLx;3=aBHVXfV9FABh_UzaC$46IgIYqk^49U1Ro~$uD3!*bNlfO_YJYL|@@Q z!WBqthKtxqxhxpff&7&^R`Xw37oKfh6Mu;s+ulY(!#H#8ti?t2yLdeAwAAQ_=JSpo?yDAV6QHMWFSTW*7v337A${33=Ult=k~AttG? z+64nPK!P%F_Xw?xXMFKdEZ=>AyHg_s5Bae11>Twb_{5q;Vj%Kz!tV8l=0=;qzV{oq zG8;h}$#)f3Gd1k#(K^ytv_sVp`Bl; zxof|4b#YM#w1BbGOEG7;%`xnPeTM1krj;0RvLa{I_Tz@ zBoVq56c7RkbA~enn8AFV>fbsD%k)G0#l7umB{sy=Ax+YdMRsvSF>lu# z5J{D`#~pFG=GMg4Z|hH=en5^6(QS%!%*_p_@a1G{yc8z_7o2U;9F4`8+U3GPIXs)C zP5OY$)}^HEi{8TV{P0n8QE7A1?QQV(Ia|`;f%_`;fCac6-a;@=IMENZM1SPGctPJhzfGrKLee<2HId8m zLC18c$Vgg7w>L5?FRsiMM3pf)1w`5@gpni(#I5_U~kOETUSS0t_GHplS`5IW5-6qk`zb{#bXXnhRu2gEYz)Z8way67d$F<*5N zv`EGR=%B32q+l4`x2-Gry`iOwo$+MzE1%Mz4GMWmT!&?mBgOF)Z6l4xIl6uk5G`y5FsB=%Bof#}$)$bN;Z3qN4mW8z)4 zc14<t8@^Ff{`GrYFCx&B%AvUkwM!_t`Zi~dl_BV$jRVuHbXshAD`d4O+rlEK z#tq?>U2`t;5T`(^d0%s zq)E2?K1EU;{iZ9asK^H*UQM*^`qa1^;~kXE%V^$Rs$V=3mlWHYI0*iv^!T(8Ss`&m znSl>!mQ_zG$NT|KJv_Cxu14MZK^rSbPdQIT>3K@yOV8%AHqW&#ydp@lTbx}LlZx)R z1j;QtFr1tinK9r0+L7aeXUw*)4_rY=)`hrr5g}X$OWQ||IoicA)`5DT_g&UE`zD;-XM- z5&aWD>@|PW4onlk`t|Q|MOeLURjGfLEu+&qj{S0rl6E2M7)@7ap9+Pmd(C)gE{G3= zaN+8zp|3CFdI5k7f(MwRQfq;Ey<;dWqF1h%0RlmY2rpXJgd!;f5E=xfdzC3sEL_s5 za4RRAlCvv|GyQ|V2l;PxJiad}FMoFovrh^EA_4?24BTFD^T3ivId}lF&>z1Y<%$KB zbFLEx2ZvzN>7*B);eZTZ1WYdXf=AJ3`QDjq6~%-`c1FdekG$IX<_cbjEMJ(`3e6*z zS&VcbQ+ZkX;v=iaY*Y0pI`c2`;KrpPP=g{!_2R;H-yKcjm)N{bz42{fJGfoR+W+p882;-3_jpTr-q;} zHNm;j4(x(cE8q{~eoF)wRlyu+Dc8DM2_RXVwQBcX$p8-pYJ_&bcZ3x2P_~gA+xa=9 z!@ea?vKj9@a3wr&oh^YsaF>jv;4>?q8#8afxFKJ$8ApB!4!TKIVB1zY!HCi&n#PRS zlGIQYW4{#r<1u!+XGJfVnmP;tmlc&hQcpz?UFE}D*-{vKQp{~W9d)QEo`j}ka@2}i z0o>33XjWmtTVlc1X=7+uhBT_N+@UgtG}`U)vDN`WW)u3`1AqQ5fDRB)oKw=;Z2cNI zGMupqE3^p`+{F9Xp5}J&ZhNlFx+FUBtlTEPd}{sW{01s{CH?wLUbCA5xX>6JY+9Nh z3RA7rfzko|SQ=;yqPf^dZLr_vi>qmB7V2U8W~IT<|R+=@YN0>(R8=3W(qhU*2e z3tIh<7??_8vdu?RLs$1r^VNR#qF1Ta5sYw@Sbbo&Tuzt-p z@U)cO*0-akxkkw7M#uyW+~9S^VwcM?Zv++4*|KIc)6&+|z7X6^D;^ugigY{=SWTU;oVQ6Lb?2@-pI8V_TCZ^G6wD9YTiK!1fc-{48HTcbU1 zGh>&#=(Kos>e%tB{^`3OjI?AO%ox~OGAD{miiUVh=^nCT(=lT!aw@5R67}T)an#OA z!cJY(>kCljS2);ecrULtDM*Hvf#0HeYf-^*;^8Y~TO|6ANBf?|=lDmSZ7WTg3)>r2 z`0sxt*)z9UVXXpP*uDm?z^}lv#meGzjkT39c=QlFJ-%l|_`dtT^dUr==%5q{bJ(}Gmv(sISzI#Xp z$zH|k++W5*$nZ1)S**7U4k5Y^e9}xW zcN(#~{4)4fcD^>;nCR!_ag_tP=A8<*KBr4v0*o0X7|G=538K!jdD z_Rg+CQUAu3#00DN=9g1Ckb+39u{9N)tkz+1#6jqN@79+IBD;F3h&Q$ZVlt_7!2uv! zFNa6B`8FRC+b(WX+jS?oh8D8?e2um^(-;^#kyEJ}wWh2gnwJr;NlCYJGl$*E3CaV) zl~O5ixVgb50PcFC`gQWj!j|+3$5+NSK}KWfENK;9uY7q|h*#!U+xRG>XF5bQM$wiw zd>u%;xn~D$qxf@uMtwg@xlGlP<$P_x;9WGTK{Y-Cf*vlufWmvX-|D}-xWpLSPC;&B zbJcz4xZF?-+5Xp)S|IvIw;6?ZDH#Rw1S4bD^f~fmlRu7~@)|>lG4o;QQ9!03Qj-B# zyZ|a{v4n(sskUA#RzD(bcIc0e0E9F(y<1|b!fQ#)X6J{Dx0|ub<^#7^(dVnkOd3*n z?3ZLoUfbrOk=(eh(@npdudzn8o%`AG?aRc0sAM=*SyRw1Nt$B)%K+^s${#7F-lkci zc6*MJ5;of%zSjskl~`4VU>3>#*{H=3v($F?RYDU?m7pSBDWZg=1U21Pzz;iaFMN%S zjNawE=S8G^wPN}MA?*4{C?v#DcoQUIVPVjoVW5-%-?Lsei4Q9U&~)E=we#EltAOm( zFD-hnUN=z=2odB5Uhc*m?YvFo?@^}VjoUKH>sB#+gsoWbmSl}?r`+;MP@XHjfQe$B2I&sJ)^ z2uc#&>zLAtSnvzVd#iLUSetF z!N5ccK1Dc70Ae(pbFEO#dV$IXbT3C!6@3F$%RMy+%LT+ewsrg*9!1$EbvlH1RYMNd z`mQ9OF!ci_gkf?dJeU@s<;I=HKY&L_ORx%eGV|}TbRT{pe)-*rCqc*f^3@;l2)&ty z^M7n>S^rnhf6*9sCB(6XSH_A^Iqkui`8LnlBIeB*sYHcno5zTFujnM(&KQ8NJ)yTP zp`D7^p}`a6oPW|nyAje2W5EFjGUII#CsDin-INK_v;aech)MdsVd z@i@c9IgJUfh-8MLQy52gqAoW=box_@s@4I=6_T$96Y8*c5pZOkF!Wx~l}4A_KpBBS z;J$f=USEFliP?JA#6#601@d3m2S)8WaV(rA25b z^ztj<-7JNsC_d^_ywJUy$>7c)8X{(m-7t&?{SA;c#>0t#FtQsR&xtKU^J-gbmm(Dr zDY^^%A~Zx@01YlKCK%@*6cUk5-WGUgA}0 z#GyIpp~n4?`)!HU8-J16D^YD~E^;>Q@mBV?JtKPDnQq@ANq5MJ>byv=lgY8o@b9uZ zD$M?%%m5W9%fMK(6{{WF)|pq0C%1l;@sxymL(y7_z{XW-5n0(8A`$4>CgLn6MTE_b zHCD_@myeCUUBW{}ElC&5=T>E>HN7y-{4?NJ*8aX>`$ z{*Vytq|+a{s2*J5);`-o)iyCX8cvrTE&%YESI>NZ!;r^`=&(a&ZyZeqy*k1ZjAnV&*-ZZGE^Ys*fDwK>aU9ihed${uROLeC8 z3!o+55)zg*XN2PsePUUR$!l5W1IA=8m)H%*N!ACT1a?&)98p4!K${X=?iM}^gNKF*7$)P$zS!?o7b*Ses$%kNkd53IsBco7+@Vifp*KRPqUk@#a5Y1>*-ymh zog>R?R){P;&;0J$U=>LZkr4q$hKbH*kxK8Y8Ae~>dz=@ORn;i;EaF=)6@ZPRxz&LO z2dR6N(CX$wbeU~dr-(E{Jl#V0({qo4dh@=$hYnNJAMcY#V=;}qHkfiCvmEcx$dX$1A$lr#m zPd#C{PV6h1A$Ehl6_r;fG@*3~eB>W~EU?j>X8($HlkiY2khcFj?}s3^u{!Xi_T^?* z_)9L|PlIl<;fhcr%nq|YI)n=d#X<-NfwGxz@PPW%lgZJcFnFR>6(9gG;7nLsRv;f7)$#nZvFEqmz`fq-o$oy5jzt*iUjWIUZ zXyweC%bM{?nO1?<^~kTB4E0S*ZC&3bu=80YT6QWiRcoRCdQoe^7rt#5IfSsRp1-yA za^!dT9=LRV z4hFShP^i;>;3U!qvnK=>HPn1R1^nD`^6+G(!CocKAzNR)@-Ym`BnTBRW^NYs&z)2|7rg zxN7a1QbQ7a?0CfMWsy^_tj45=|=2^4?=9z2Us#b2@JSCSbDV1`I+ zVd;&}ic=LrERtGBMZiP^!RUrXL{MqAWHUtPE~-5jDf~7xYH@C*!5aWv&W-(PR)I{L zJ)@uRW|q*HdTh0B^T!7!lK`?ysY4P-pzch^be=iI^j!N&vMv14(bBREtq?nJEl^QKHUR z|9EC&;cYTfeHDNUFQkuZ)xq~;*v61(4P!|dWp zC@;clbP-dBAyp+rQa|5)La#1LY=~YJ%b3h&fo!-RC=qYAy0LO?pkhg}PIod8yC)sd^FOLL~M8K{q_)w^9yTR;1k{ z0pM!>)W}S2Gq}-A-?--I(WeRnfsgnXfOBPR9erqTOn9)vYqt2)I_qKIcWGM2FI8&_ zILf>8-ro3wCe4pr{0}?cJ*?xlj1P=)-%jcefo|*T5jHAc@cX#wDO%TvN7S|-slDnNudgh&pt{U3|CF zX5Db!Qh;i+iyDgH{k9{l`v8H0AW$ zO={v)|}s6i%C9cHR?U4q%=blnYB$|CcIG3EU(&m=UQBUQqc8C{4i&L<*42p5wxR~ zVSjNK7TYE~lWLSlHczI-W$p5><(*}kPGJFu6c19mZAKUI$B$jdRyWlNLhZ_)Lc|z- zQD~ZE<*l`T=DtU*ul{TWy`a#pKLeF^Hz;x>n?TlVUY{}Vrh0a;tPW^@eB5iag59KO zS}i&W{PFpC<1vPhLvyh!rl*3Aj`Kv9D^2x%-7JquM7f~>?rn%wAr~3VHaIx@i@gc@ z3sy>jWrxtO*$?QtOE3lyzkH$`CnI8T!&!jOF1&7zy3F>xYV@Zf z;Hll}(R3Bl`+Jp7=qfKBomR$7)7-%rU3^p#i}=;g60G;AUky26?a2!1bm%ZQ@oS7W zXJX-+3RjzHYh<&BOC=#FcLq@{E2Z3Bjl}-h4DvPN4 z0z0Fo)R-*_=BTv0*YYG2*_WkErY*7#doMDq=5u?&#*fT{t0%2|B3|S{7qzoa{3_&4 z!U!QI8M^^wa$mQRL+xMq8`#}MH>W>2LNOI&VJfTmslwYeLT?~nUOuDj(K8mS_%{8m ziAz9thS3diNg1E78Je^1!qWW?6QKJ50yQ`E`2j_KppVV+y9Ht=IP4H}=zX&t+VXsP z82lHsP8o-uOaU-hq}wC!+B8zCJR>(gPy`o(zzNC3c~%i<@{aat-`3DO6p=D*U?e7|x+L~*fGF0Q~b zO`9*IbZnK~&ht8(UQzBXDAP(u&i#5J=#cgXcE^}~`0TyG5GR#6!4=P+d}w}P*hfS4 zjV}5&=*hR*j*so5LM^sNR|gKLV(&a0Ta_`I(D-1Cdk8J`eauiHmfv{T2g#g_zy6R9 ze|LgLI!lQZZ5XG6TM2+h#-3cGNC|!F_w6Wj(qFLvPT1JSCNu^#*t}!O#wrsLZL*BU0?Gpo+BLZT#|`t>>0u&n%^f4@xt}V5_^T&6{`87S1d) zF6m198^@&PmtO`inbdsdY;&U;Tva9?H}95BTMh2|opty5Mo&YW&xYl7126eLXUzPG z3$O@4?3KQ;u^P!#M7(%B(JSdO(`^>~jx}_dxO3hwd9S@Y$?O!{ccYu9Bt-Q0iT*Cr zjHx@(`vTvLHje_R+gDUBJq;hYZDyaUkNe^ZBhjhmy(@8pNzwJ_Tm(P4Z}$slfQF{H z`53aw3@^rCzCHGiEByL{JZs~*N{Q?9*|nV*8=7AXr@4vzn=g9x<8Yd%&)5?b)$u(X zUiM7?_Y*fV399E3O}=~^`G^hbQ%|gSw{3m`1YZveBt)^aZ#DLgSB2(rT%h-jU2dmg zC2j2%++fpaKK|IMHpuATN{b#f*fovK5HktO5W(N}EZc#juVmr*6GLXKBw9H{v}q^~ z_1$Ya`z{$Y?O*?Y&(hysO2EWHHOyksCvFOH{?l~B+>Cv{p{A^yN}PaCH}*PQL>5q| zp7JG95hAW-f#)@?-oa3Dds>Y7aM z;0df{4;I3cNBb}DwVRr@u*V(Qn9$N13LEE$l?lj$2p9y|sTcj>K}Q@ccG zz6hnf%w&zuke8FQ9-H%rZRC8DyizF+c=Pww9N=GwojaVY``<>;|Ke}`55VfbFl^l9 z|6?2YzeT+HC&%wq(fj?!W&3$9GpRCL7V2|<^qYvj= z=D#?*X@^X8I)%`r3@NO*yNq3x1YdcQ-`N>KW537kTbA{A1sEErbsTh(e>^DvdmpRo zPD*rQP;mPnk3^pq$Gs)K6COTN-R8jt@V;oMmx_l5JD^7Bnbgq+US0Iye?B{>}feXjBJU#g}EQN6X8gw4pmCVE0ipjc?Py59`;r6Wk z`v~Gk!i&vEp+5#~8ooZe9`t`H(u$44gv+Dt>tempZiYK3qq6A*MKy;1?Rza=Y_OS% zAcjcI0%RF|T78ybSlO1Yj3armtJVw(W+L59ux?GQzENUW_2IX6?4c(6OIlwWy^m9x zc|*ZFrxy}97@4lr-`e;vdsN<0aD{PM#yl^piT)Xd}nL0gdHgDKeK|LZ26a_zi% z!oVB`w1o1BcypipQJuUJv>k#rn-!-nPmscncj2NMj@&MlOAQAHr;s-dfL(cvGm*oR z+MNz{QV@GGmR+k!X9aAZXahm6I=;*|LWfZ=tf%ymKKy39EXa5z{Ja^kOo!^&V zy>?ow>q@-jo$jT(ObKypS{{UW$|9H6m%7?6oY8bSyiT;m;}3kkn*Kb3h%d(ZZHg(2W2)aaSv;k&;2|*KrnVTZy5G5P(Dx=3OsGLF@(N8 zUCGznvoZOTUje|HC%-n{IMU!380$LhL3~JD;WKu2T7?c9lmA_O@P5pjWgok= zdG2!m`SyJ~s7E4&fHbS~#KuCvqZE+2*TDjsa5Jw!OaFLD#{ZG2;n7f$aH2_yX))*S z*|Rg-JSdzZ5uI+wQJ(oiR8emgBu+Byq^(*k3!g4Sd8kh3j~XciBI2ffeP&kSy5r#- zoP2uk;*JfV_+Q%JypjdR5fo+)U*jT>$jc*a5%j@N-M7hXM5Ls7m_9lixW*A2nR2oK zGGI|DYIMX|R``3-5+-pSCK87QM~JiOc%&m4<9VWY+K-;hL4}YL#}NAF6MHA@MYD|& zT)$0JNSUd2>pb}`_JN2Go)6_mP+ebx%9i3+!|x^=kPHsN?7xFy$7C$oW>=uW=7VcD zj>ue}d`AAswLJfn*Y8eI#kCF%7X?n(Zk|jiRFO9NJ?PuFj|6194Y_625^EQJZC+!8 z5^y5=uNOrh;>Fz5W_if; z?HX)dfO ziTx=7_l(ReE=qaRp|0^pD-}b)>OkI(#b6}yA3S=f0NFb^91d?c$~*vsUrrM;7OLSH;5+ZaC_- z4#^fEp}}(K3X}+U?j*o{3?(BW$yc^so|#)p5upo=Bc{vu|7r%0L>*Bw0>`|JFDVCj z0GSvHcl}gn*vM(DP(lDC-cp|tN%i`>9ci`1)wj&q^i9`Aso_x9&i==+c+v{x=4uB} zuR_zyS*8MM_#lEe&REpd^xVD<5z(H$^ilo_Sz!>6uGB`c!JDh!gYd$m=wk<#9@!by80IbSo>TMD~l!U+mIY03;KuHvCBoC9mi~6 zVL?1JoF^xG)$*Tn?P8K zA4UqR(#^KU(yy(gi)o)0hd6qFvo)0x02wgU)z?oA#y$>J&i(pj1H-&?q0oI3+wLcg zoDbufbtaAUVBd=fpGrP=bb8*PX%-ZcnJr(<)l3?3m}M$46g_W-85ADQO9~?4AYnB@ zz$zgB{8viCgwB<=$47ALlRW?Af;>~dyrSXd`@dr)zy?V7oQ@zd;w9kwO!X?_dsgI% zzJ)pYcgM^mI&vGVi%~V7Z&B|j%Xqg4W#|RoepGxBIF&z`$!nBn-Xh&{Yqz_-8)<4G zQ(^g#1v8*ZDpr{fF#S27#Z5$NRxtgP#yNqk&Z#KjC*k*6RI8=1m9zqn0F=Z*fgF5D zE}B@jA1Z0Y2Izgpp{q~hr^%dK@PJ!NfOMbLWl?kR@E#EHx|cU0ZsOgX>>h!AZ^ov3 zi-tLcJmEFjMu~>JMLr?Abdf|N5x#I6(?EH+UP0v|Gmj8R*{*Pk76=7OK(RFiiniU5 zxyZ~v{arxzcKoxQgvLh(M%po-X%-zlzJGo|sabQeVT}X3H~@QV%^m!qbc6k+nRFL@ zj{y{K_QvFEG2qyEWpLglrOqGLj;4BPLvyi7fsa#n6B=d;zc1lr`^bov>S^$uo~RMJFY3MqkIM%yM*Ki@sR1YJiz#2Y=U9 z&w5mzgd&IFVH_t(P9KkKrx6yse4Z2z*|sm&DIn(Q=1AYnbThx8(&pu%tV!>egohoc zu8x|&H$5^JiS()RJBRVBAEyb;EKG{`FL>I*n&rq8TC*ni{V{F+;&)>_x z+9$ntr_s)V(@EO}Yf?Nn{54&TvRXK51PoUe7F{829=Zz0f1GEo2F79z>4^(-UC#-@$P>Lpqg z+S&UgX3$)HN{-W#-S+xjLZWR7XL8(@Ww!1LL2nPIu#pEmgaa1CuSE`3RJiI3*g*;9 zmsE@|Fk&B8Ku#k*$qJbi;}_tTH60IU=ZzSe6Y_iG8^~rtnGrb#b&(kapjE|1YEJc) z*#|qL0K-jO&dpQN6E>>c#cnktlaps)@-qnbZ8`B14VmcgLpe8wG?F=5+mcu zTxP@^u1++H**7bmA18pnGjB4H-p5QWdAU~?d;236{Sp*o;n+ZgajRM7q>C)wYWV zA$yj31L=C8E8X+Q+K56PgpMqSWlI!T_UbI_SILDuC0xo!wX|bfr4zd#hkoQZ0ilHN;}gkS zKK03J-^1w!zESbl&nx|4WYk|5=BaR`&AvkrsQc*5J9}Rd8_vWna@D(y ztG!zlj8yc1@&3`|LQqrPDLeEo)iTwWh_C!^B|1uJHe@$dGs7s`e!=&h)SCkKHy3*l zzb_1b(ePZhw;I`@t7OLCIG-JRxgqYqUNO8>P@$>d4Q6tGDJUtX`LCav`Gs;zmR@S> zZoQsz@$Rj3_NJ-$6g|2c?lt_)^zQM?*tXE{=;;x*1G`MyZjz%urBtml-q!HjV=~lu zYp*kH*U0Abt&A&TQL6f$7)sMX@#nPXlYsyUBeSOX-e8_tNcboDJH-s4cG(pR>i$(3 zpfZ$|rQ*`b-Ld5FhF6kXAS{9&h0%xOQuns~(<3tKTe|0B_Q{6EJ)>e8*B71|!*B1uP-^3?AAZ+6oibt-pZfUG@ zM1n9z=}iTeAiO;ySLJ|kJUp6H4%j)fy05Hvj7!*eM^~)Zjjew^xa$5pdd!3;H-&=n zwDo?!7dyxQb&BKHJ@NY#k5cvNQ^0C*(1T`3wGg|JKL6-uP6GhCvUqH46h=5`hv43y z&7Q1~-JcN?clc?9t7?|dnNNI__KTTM#;9$bK>O0Oj=-cugSEWIdUI*lmHVvFkl+Av z`Ay8>zX9nu`fz>@n~eE;tfxm>Z#WR}AG@)g;IU2OR%^TSk?&z<{Nr9Sz!iYwXj>bZ zWCr-J|B?EJyq7`hzYrbN8q8e`jaKf9aeLVz*@|-OBq6k6|Q+F`hmu zbJ*}(mf*zTP>#P2XjPOh+judOy4U{%n=-n8?Cd-WP^iWG|J*g87)Y-BM;M-@{BXta zL5xk~-vt2-|KQ=OZ>9eq{lgOrCnuE!oR^<}{ik0CJ@l=|O+MU4DcD*D(Zw$K;BnYe zaAa+~?nv3{o*4QzO7$*bygkdcD9I77EH}XUzSVf3P)dsMT$hmdZ?F{_fBZp3efZ6A zhgVmaAvyTD_OYi#;i2r<84f)>9~Jn(*N^xs-CqLqh3qUqq`%~XN%WjaweS1oni5=B zx=+i?Oe#eZokV(p`qe%pr~742_Z<*KvBC1#t|#^a7%*=l=2c zW(kv4&uY)i+14B$Ha%L=-Jt$xrPCFt-$y_V1|t71DyYy;8s6{+ zu8HukZ6Y@5|Kj!3^E0W!t#nir`tP%|_%@#_)P9QZJSgvsfUKTnU!MC?{&;{ss1`ZN`V( z6PFGz8n%X=NvD8MU;#hEvyvfNZ?QW>#e1HReQ;zDLNw-*OEL9MWB_CW)q}F70)T zB04fXdWEa4qc!2qEM;nH>g3@u#J_pNH?*#Q))Y1@)BK}3yQIXj84N|B07e4H@Lkf? zUJt2C+f!`#)K4H`Y&4$|>e)_$1GP5syN}Cu7eBorI=DaA225WKBdd0r(JyRwRuUJ5 zD>kMTb`N-Qvp)Ppy3o8GXxky|v9&!VXd^vyISBD%YdD_tkgJ^yc>&r1UV+afU|!{# z6btAR0XP;Pw|tJn#hmVtSaAND&A1CqV>?mW_4=%nqn7(oHa*}>{@T_fVkr5COsO_%s z^n9rGwG%}S3UYFDf=c!GJ9#<0djlI-;_IgN-@J-|lFHSxaXLke1L7v^d8TmR2=p9( z(MD8&I4!SIB6y6hc?{~}p*AX@*`Zb%uL2AaYQb^Y&&#N0&1XAdg*%rp-bH4he1nax)S~d821~2Lpjl(+zNTiWn*ktj5{si+4Zno_wR$exFe-E~{vY9gBmYF}ov$nRLz8+&f zJ^@lhArWb_ir1d=zg}f?mDbE;l+1de4wxX}ZI(jBoIqOR&)YNvH43x{9N}9g>bw4l zMvEk{Kr#c$0^anClxG-wZ5js0A*`I`fjYJug%DB!`cOqV#GgQ^kA0@K{pPFji#8;e zvp`)!1H#%Uf*&{Y{X)NlZ`LO#UxH5f9$cB8zSN^?1sVlD%Pl{90EZ9)j##`z1dO9a zObSiUL<9x(NH083`Lcwb-y)S;#VMzM+#^H~_E*L_;&Znh#%dqEya;S!V&$(Sc$O@F zs=n^dRL6P!js?`)1;bSy8%K~(bQ>nCNbgwc3--!Xr=TxZDhmC(K&kCWV4o78Fz-Je zC86E8(zR4c4ky!mPRhucIbs}rSHzc+no$rCAn{pCE4w$3=JS^+^ADy8=I-&74%9N5 zi)5T+q#U{5zkdd*XBu7(Wqka|n(IwJDFVl~Vp1@@LD%V!2aI;)_`MeB8HsjqkJ+q_ zB(QTeV`vexv}B0Wke^nf6_Sn@h2ER(CO3kJ>s0htD$db4-QH;gY~U_Z4+JUt81Iu6oliOIOBuvHvRdpcxk6GN#=Loj!!}DfE#W)V+ze2q@8{e zq=C##50nawL|73z)k!ocR%&f@y5GAx_V=zb_Yy%7y;7)QO_K2X8w*mek(?-|*wOUL!Y9Fu;t&&4Jpn z03wB4+Y+1i3fz;I=N|a^+S}qwAFak&ymzYcD&7>~P^E1Np#RnFE>xlngQiZju5HX( z9rQyC?%&4~bz6jVYq|-FLZeqtfsYuSOYGK5OG}(FpUOdcfgW~(VQ`++aGZuw20@21 z2V~=D^KpsFn7zbnoIBCij6yyzm-GdfC>Y>O^%d}0ndgSB1G}weyhQ7nz1xBRRi!S* zUIsoB>sCP%xthP;_!;BE?)8y<@=~9+AaR2(_q;D1!{+sak5!>S|+Vfk9aCBdUC@^yN+C*Vvb9sFb;mRiX<~!FS+Un>cI0q zMrE>iRRfimue8%R>?l(j+hk=fYdE>sB&o&yIVV=7yuC}G|LUf>omrF&!aS^^PgFi4 zobS4+H#vpbB$THiCPnTkpzlnKlT?=GlV+Hh?@MUFG$9{8=S{D8Vi%3DI+de1nGn8n zAv8dXl7o)JrrAiz05&{?Kc*n!HINEJPEL5N@Tqm?=c2gwk>_a##;fq~sR3U9L zvUA%`Nomsc%O8HlP4L+!qXDeE<$c7R)5?lh<-kD?*3#}h^9p?WD8mPj2bo>$S{b`(3>D9N8WNI&0#E;fn@-FJrX+OGGzGuT>#;PRvR)G@gtxqbe2YP1i)o6t4|71N)BNw<$Ii&dAH%xS z#wTARuj|bXHNlPYDXE2RP02)>3x#>Du?sD8aH`z_ zb|q>4y8evu0opwWeT>SdTfwp2wQ(H(vk}hGk+DjXZReCM^vR$0V3Av4LBWw@a+4(P z3h{1gC45o2uZEC0P;2YV;LEzfvJ_Onw5q^~z7W~jQp5otj?L05aDBd~pU#r_+;TEq}Lb_tK!a13@N z*pUEqa0wNQUF7$xk7DgXLwDa8&yn|S>=JpgNBkSLdrK)$ZB3+mV}0`N_E?o0_>$?A zrkL&$7Z*onNCyF5mbxJZas9YSu@G7=~vLgc* zM|55N#T_@e-vvB~2CiZ5Fd@HU%e=Z_gKpF%irfIcHQ$kdUQyt%4mkq_NpFvixD_~h zDnr&c1AeoYE$n4O>P!f+J|&vlQ_^5gWOeFXH$-@wXQnh06&IFgv#6FH5EYk}<)qw8 z7TX%>uA34ch_(Xg1GxY={+i@7v%_uIOowVGF@PLnS~%V}HEP@`(%Eoq|Td*~P00GR{|ZDw-H(ytDqE(pKTO)mGz?dI;MErkk2Mg!^TYR{}EiZ_TXP z^ymzDtxO2aEwTf_fh?FSnm@%yXXZK-e$9O-8$H$nQ-Ig>*K%5M`%{aOhs~_a)(#Jg z$|JM#>jNz;)axKTS~l4Jrluc7UPa1JI9d25RPKrI;HRk00}5wGUW<=WTwOaUXhv8qTn(h!Y7Z75xnL2sJ!HG|cLI`tYS~V%v9Z2Ske%kZWtewhh0Xb>P4AUHxUVx~iHtBbNE z9axnPEG-Yzf61ygdiJT?PNkofw!J8jtB|z&x>$IS(v6nM~XBPv*z*w_)^kIOkoG6m6oPS*0~GK^!Htv zzt^Xc?>EP9eplQ~Am86)K*GsivHBDh<`-MQis1Dw;ss49CsSl) z^Gc}Ufx6+L=2l77pN`s}LL?|b4g1*2_HfN~`ok#9M_9MJP0d@samy3>*j8NqO|0fK ztSxOUQZ~)_i#{v^v z4i3n(tBU3^=uFJ)$ufiWO-1O`l#xK#qL|UYD}37ve4iga2IfcoeimdO*Tl_E zgmEL|Xc`p~h<6j9^*_pn&{KUg6I0A~fmMHoo)B=C5EK>@VBFPZW5I@Cw}kiawi$oC z14#NwwAF(%cL4Mt7mgbl&s0(mvddROj>N`u$zqsm!r@5#kYgr@X>b>nM~F1zqu5ME zGm74Qh_H{>$J5n6Vb53nBKsM>XAF8V4_AhKRAIRJ%Z0jB#n*R(H5}3EuPTuWeea>$ zkfiO$`#VRz2i;Cqt==a~F-3|=Cy2yVJnYj=@@dTw8PeE(FZDU|%qXhL(X67Y_e^jAF#o3u?$&Ir@jRg-7(Y>QwjwJ-}I)mZ{Ns8JObrZ6>0 zH=ig~b_l_j1btI5=WiKPQ}ioCwqZoP8l#c)&{U0uCH0*6-w1j6{pLoQQOyE?P?yFw zV5a#DY=D+36t^vS!;@Ii=uA>14y3k@9N5JMi7aH^NkqWfPUce3<`Rt=)^9A+t9(T8 zejeFYtNa7&;SYH&FgeQ&5oqZ;b8JA80I%F}mKmgPbh7zx&}i@4f-=pxSI~T!e)>rK zujaOCd=f>6gx*=UtY5Ei8_!m`8Kf(5D08w24`BpaJFrHTeZun&t~9_8BfCGo<^_pY ztM`sL8k!m1np-2x0Ln39dNvYl4o(oAHgGeJAEh9>26Xsm+tB^nxgok|Ej6L2iMPi!!6OMbLnml#@SAEeeQsQ)6=qaeUk#A+k0(Uiqe}v7kOW zv9T0h3OzmD&zJAu-*KzYgp7Rb@w^72svi_v8(}))L|{lb0WdG$kN%K_=;n|JlKaR9 zzu>7py@Gi{JuHjV0tZW$??TL2b_zE#5KF%#A~9WfcYLAvO}Q{Bj8|7-m+bcM=Iof5oAu|5^qyJt zj~@d;93WbA{6XXBuQr^c!N`>cvsT@P9h2`c+NnN=99w~<`pxOQx9t znlI+#e`>3*NATZ$&bar8K=1B0Tneh;j9Oo~Vmv-N88%r#mX=EDb%IH7km@0Zg#(V~ z5c-eo-!jxxSu5g;+iIeUlXu{V4@11%db0^ng49~ppk627-#M#nb?{4b_*+ITtV+QR zin@%v2m^+Z#Ut}Qc7oYuaGM8bxUgZci<&Mv`sCR}v=xZ6Z`D6}-u?4X=@giKbYP-} zB|8|xgc3?){2yKYuSWE~eD}0EaYJZXf19JaM(@_$RdecAA5G80eDY+TN2*9==tZic z;ief+nr=!$0w6)AmbF2&C>du|RzIG(dvh)qvpu87WGNP+JLsX{b&ySR#Zx1P#nvP4!Vy50iCofV&M+^w|R9J7YJ!$h&9GD6Y_nt_BY= zUg;esW?=@BbTD)0q;EB^qjLY`-hVm06uB)MsFsk}^fX`}0eLegOPm@ojw(3R7EqmB zH!m_VD9;h_gj3Ks3K=&voKwcMizu_2;MTIe`PSoIotTRW@Fdb4V$3x+b{3yHr0mVi z?H3ReHUpiONSE;TUr{eY*tcYpj(EROBB#1Kwp7s*#i#-`FuRMEn@tq%aiVP>?Y&3{ z35^Gc**t}tF>E_+Utp?%0^EenSQbKrUN4_^m-?9t$_Rcs8g6{8NoZ{Dc>{}-uRNF+ zkx~?^n)#Q>j{ovkt*mZ;-VpgUCo?&wJffRgxbYG+_so1jYx{v;!03;<1TEGZ;+J?D zR#r(k&hw1PznwJM3&sXds;YmyOXd`frF592YmHksgy;|g2&P8Fzf}ic~N(PiieGG?5bMc&%P^hp`t}U15x%bbbS8TnPbNjJM){o_8YY?B zJ~eOAVv@naor%Va8N{iYSh!r)O~K1{nqpN>|EoROA7i*LamZ=JQR@ern%Z(v!K@)h z@2_Kp25Ek@gv|=E^s^}qlafwOV(PkjSzV%&%M9^tp{^`S3^i#7GUldF?0Ia@&iz_SU>b&Fib36tRQvWAY#8K7ot0 zK~BT~egHFf310G#Vd7McKry_`U+2B|euDi{TIdUYUPA@Z5n3CUA+fr5daJq9i**zc&%m_`>T#~2InLQo+f+5${u4XWeae-kCai)Nl z<{x@`BZ|zX7xZBcx8XsZyUfg1Mp2YI2SY#J;R8ld`TOe{cX1{{C})(x%&8j+&&eCEjoI8AII_qUKZ|Z-P4#pP`?pwKIY{)hW#lNKRB;@7^xri8{eef#2?#r zI9pG#xoB_-+9Ph5v^ISXEMEs5xE?saezIEdfg}m_kjQ@w3Igx7x(JMey!G#gWBq&_ znGo$@ zB~-DYNr`W7S{J!iyA7wqr}A)h^@^a@LQfud5Jq2v&P1IW4v2q+ba{4whS*}nq^=35 z%#;#C3J19mh;;YY*A;gfyd8fJ*o8?KB1taozDvVh(PcZjGhQ2CrQP)3{3FyLLSIm} z^SS?1gUGvM*0wqBhlhy|UUrWy?9UzOpy1#i`oX_yu}>$*DmKJxeJX@)R^tg1`lGLU z1H4(zh%Fj!cWydaL|!9r-;x)KObK5uO(b+UI#QgATzeLLS7-lUzbx6^{@H93{nk}u ziK^rFw!?Q#ZfoqPKOe2{?f;ql{AoMiIWC&gXyoE&*$T{UoT@UK`)zhp)L`?X0eOK7Z4RTK%vcN0y)6yZ*w*>sldk zdyd}u?ydD^d(_G(r3Dc`uN}C>?GSii6L425o6!9WyQdVNVM=6RIlIyPeGseN>X23U zeSv+rTa!U+50*W<(cipCe^a=K2AguDV}MEEF^&(M9aSGP57=~+gEF#Sp#PgU%T5W_&dp;Y3CdBroAST41&Rx%RbcROcrv(!1o%cR! z2>fFS_%c=6oZWC!ufKy`K#k{q;1RA9v=H zRzT0SkCQSI4X*>$K1T|Q^^8X?YAyY!JW_Wi27u#izyh>(L+X?aEeX~~WPgiMn{ zV_ioRbTh%K`I?|v7tkIP_|gX&3;aHA|4;CzBk1t4c;6pugS^i{y}tkN+x#!z_y3Rm m7ab0g4E)gxT$&M8^lkt2@-1(yt^GeS0D-5gpUXO@geCwJmiYDn literal 0 HcmV?d00001 From 4a96360e56a86e81c7e1a186e588ae0179fccf9a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 30 Dec 2025 22:01:45 +0100 Subject: [PATCH 037/161] [P132] Refactor Shunt settings and add more high-current options --- src/_P132_INA3221.ino | 42 ++++++++++++++++------ src/src/PluginStructs/P132_data_struct.cpp | 7 ++-- src/src/PluginStructs/P132_data_struct.h | 1 + 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index d0c0e79cf..64559be4b 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -114,7 +114,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) set2BitToUL(lSettings, P132_FLAG_CFG_VERSION, P132_CFG_VERSION); // V2 P132_CONFIG_FLAGS = lSettings; P132_MAX_CURRENT = 10; // Guestimated - P132_SHUNT = 1; // Default != 0 + P132_SHUNT_V2 = 100000; // Default != 0 break; } @@ -210,7 +210,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) } if (P132_DeviceType::Ina260 == deviceType) { - P132_SHUNT = 50; // INA260 has 2 mOhm shunt built-in + P132_SHUNT_V2 = 2000; // INA260 has 2 mOhm shunt built-in } } @@ -228,18 +228,38 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *varShuntOptions[] = { - F("0.1"), - F("0.01"), - F("0.015"), // INA228 often used - F("0.005"), - F("0.002"), // INA260 built-in + F("0.1 Ω"), + F("0.01 Ω"), + F("0.015 Ω"), // INA228 often used + F("0.005 Ω"), + F("0.002 Ω"), // INA260 built-in + F("20A"), + F("30A"), + F("50A"), + F("75A"), + F("100A"), + F("150A"), + F("200A"), + F("300A"), + F("400A"), + F("500A"), + F("600A"), + F("750A"), + F("1000A"), }; - const int shuntValues[] = { 1, 10, 7, 20, 50 }; + + /* *INDENT-OFF* */ + const int shuntValues[] = { 100000, 10000, 15000, 5000, 2000, // 'Ohm' + 3750, 2500, 1500, 1000, 750, 500, 375, 250, 187, 150, 125, 100, 75 }; // Amp@75mV + // Values in microOhm + /* *INDENT-ON* */ + constexpr size_t optionCount = NR_ELEMENTS(shuntValues); FormSelectorOptions selector(optionCount, varShuntOptions, shuntValues); selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt - selector.addFormSelector(F("Shunt resistor"), F("shunt"), P132_SHUNT); - addUnit(F("Ohm")); + const uint32_t shunt = P132_CFG_VERSION != P132_GET_CFG_VERSION ? (100 / P132_SHUNT) * 1000 : P132_SHUNT_V2; + selector.addFormSelector(F("Shunt resistor"), F("shunt"), shunt); + addUnit(F("Ohm / Ampere at 75mV")); addFormNote(F("Select as is installed on the board.")); } @@ -349,7 +369,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) for (uint8_t r = 0; r < min(optionCount, (size_t)VARS_PER_TASK); ++r) { PCONFIG(P132_CONFIG_BASE + r) = getFormItemIntCustomArgName(r); } - P132_SHUNT = getFormItemInt(F("shunt")); + P132_SHUNT_V2 = getFormItemInt(F("shunt")); uint32_t lSettings = 0; diff --git a/src/src/PluginStructs/P132_data_struct.cpp b/src/src/PluginStructs/P132_data_struct.cpp index 680f7d6a9..189b6addb 100644 --- a/src/src/PluginStructs/P132_data_struct.cpp +++ b/src/src/PluginStructs/P132_data_struct.cpp @@ -93,9 +93,10 @@ P132_data_struct::P132_data_struct(struct EventStruct *event) { if (nullptr == INA) { addLog(LOG_LEVEL_ERROR, F("INA Class initialization failed")); } else { - uint16_t maxCurrent = max((uint16_t)1u, (uint16_t)P132_MAX_CURRENT); - const uint8_t count = INA->begin(maxCurrent, (100 / P132_SHUNT) * 1000); - uint8_t search = 0; + uint16_t maxCurrent = max((uint16_t)1u, (uint16_t)P132_MAX_CURRENT); + const uint32_t shunt = P132_CFG_VERSION != P132_GET_CFG_VERSION ? (100 / P132_SHUNT) * 1000 : P132_SHUNT_V2; + const uint8_t count = INA->begin(maxCurrent, shunt); + uint8_t search = 0; while (search < count) { const P132_DeviceType foundType = P132_INATypeToDeviceType(INA->getDeviceType(search)); diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 05f40a2a2..822499eee 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -31,6 +31,7 @@ # define P132_VALUE_4 PCONFIG(P132_CONFIG_BASE + 3) # define P132_SHUNT PCONFIG(6) # define P132_MAX_CURRENT PCONFIG_LONG(1) +# define P132_SHUNT_V2 PCONFIG_ULONG(2) # define P132_CFG_VERSION 0x1 // Config version: 0 = V1, 1 = V2 From 1dcff8797cb4e066a14d4671ebbbd952c0558d93 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 30 Dec 2025 22:02:56 +0100 Subject: [PATCH 038/161] [P132] Update documentation for extended Shunt setting --- docs/source/Plugin/P132.rst | 4 +++- .../Plugin/P132_DeviceConfigurationV2.png | Bin 82312 -> 82930 bytes .../Plugin/P132_ShuntResistorOptionsV2.png | Bin 18756 -> 90812 bytes 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/Plugin/P132.rst b/docs/source/Plugin/P132.rst index a14243ab2..5ff817f7f 100644 --- a/docs/source/Plugin/P132.rst +++ b/docs/source/Plugin/P132.rst @@ -108,11 +108,13 @@ Other INA types the available options are: Hardware ^^^^^^^^ -* **Shunt resistor**: As there are different hardware configurations available for the board, using different shunt resistor values, here you can select the value that is present on the board used. +* **Shunt resistor**: Here you can select the resistance or shunt that is used, this should match closely with the **Max. Current** setting: .. image:: P132_ShuntResistorOptionsV2.png :alt: Available options for shunt resistor values +On many boards a shunt resistor is installed, that has one of the available resistance values. When larger currents are to be measured, an external shunt is mounted, and the sensor is connected via the Kelvin Connection that are available on the shunt (the small screws). The pre-installed shunt resistor must then be removed from the sensor-board, to avoid possible damage. These shunt are mostly specified for the current at wich a measurable voltage of 75 mV is reached. Some preselected values, up to the max. range of the sensor of ca. 1022 A, are selectable. + NB: ``INA260`` has an internal shunt resistor of ``2 mOhm``, so when selecting that **INA type**, the setting is fixed at that value. Measurement diff --git a/docs/source/Plugin/P132_DeviceConfigurationV2.png b/docs/source/Plugin/P132_DeviceConfigurationV2.png index 439e409c749315cedb8076d37f5f2d8dfe392e03..4260a83237b1ecff1b787586212c371ac8eb13a5 100644 GIT binary patch delta 59375 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 ZC$09?Gy|M= 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 G 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 z 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 Date: Thu, 1 Jan 2026 21:09:50 +0100 Subject: [PATCH 039/161] [P132] Fix rounding for 400A shunt --- src/_P132_INA3221.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 64559be4b..248adb8ef 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -250,7 +250,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) /* *INDENT-OFF* */ const int shuntValues[] = { 100000, 10000, 15000, 5000, 2000, // 'Ohm' - 3750, 2500, 1500, 1000, 750, 500, 375, 250, 187, 150, 125, 100, 75 }; // Amp@75mV + 3750, 2500, 1500, 1000, 750, 500, 375, 250, 188, 150, 125, 100, 75 }; // Amp@75mV // Values in microOhm /* *INDENT-ON* */ From 1dd51f7c21beeb3021e6ddd3438272d5a870f715 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 1 Jan 2026 21:41:51 +0100 Subject: [PATCH 040/161] [P132] Better selector-descriptions for shunt values --- src/_P132_INA3221.ino | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 248adb8ef..e8040fe1b 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -233,19 +233,19 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) F("0.015 Ω"), // INA228 often used F("0.005 Ω"), F("0.002 Ω"), // INA260 built-in - F("20A"), - F("30A"), - F("50A"), - F("75A"), - F("100A"), - F("150A"), - F("200A"), - F("300A"), - F("400A"), - F("500A"), - F("600A"), - F("750A"), - F("1000A"), + F("20 A @ 75mV"), + F("30 A @ 75mV"), + F("50 A @ 75mV"), + F("75 A @ 75mV"), + F("100 A @ 75mV"), + F("150 A @ 75mV"), + F("200 A @ 75mV"), + F("300 A @ 75mV"), + F("400 A @ 75mV"), + F("500 A @ 75mV"), + F("600 A @ 75mV"), + F("750 A @ 75mV"), + F("1000 A @ 75mV"), }; /* *INDENT-OFF* */ @@ -259,7 +259,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) selector.enabled = P132_DeviceType::Ina260 != deviceType; // Built-in shunt const uint32_t shunt = P132_CFG_VERSION != P132_GET_CFG_VERSION ? (100 / P132_SHUNT) * 1000 : P132_SHUNT_V2; selector.addFormSelector(F("Shunt resistor"), F("shunt"), shunt); - addUnit(F("Ohm / Ampere at 75mV")); + addUnit(F("Ohm / Ampere @ 75mV")); addFormNote(F("Select as is installed on the board.")); } From a9980c1d5149ce4a2f29cf1e2f2b71bb7536baa0 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 9 Jan 2026 15:14:41 +0100 Subject: [PATCH 041/161] Merge branch 'mega' of https://github.com/letscontrolit/ESPEasy into feature/SPI-add-support-for-multiple-buses, fix conflicts --- src/src/Helpers/Hardware_SPI.cpp | 3 ++- src/src/WebServer/JSON.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp index c61278453..56bc55f48 100644 --- a/src/src/Helpers/Hardware_SPI.cpp +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -1,4 +1,5 @@ +#include "../ESPEasy/net/DataTypes/NetworkMedium.h" #include "../Helpers/Hardware_SPI.h" #include "../Globals/Settings.h" @@ -24,7 +25,7 @@ void initializeSPIBuses() { #if FEATURE_ETHERNET - if ((Settings.NetworkMedium == NetworkMedium_t::Ethernet) && + if ((Settings.NetworkMedium == ESPEasy::net::NetworkMedium_t::Ethernet) && isValid(Settings.ETH_Phy_Type) && isSPI_EthernetType(Settings.ETH_Phy_Type)) { diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index fb98a9f5f..41218a8c6 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -632,7 +632,7 @@ void handle_json() # endif int8_t channel = Settings.I2C_Multiplexer_Channel[TaskIndex]; - if (bitRead(Settings.I2C_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { + if (bitRead(Settings.I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_MUX_MULTICHANNEL)) { KeyValueStruct kv(F("I2CBus")); From b7529e52cc63842d0ac1b0dd3ef0d1b992b199b4 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 11 Jan 2026 20:55:42 +0100 Subject: [PATCH 042/161] [SPI] Add SPI bus selector to SPI Ethernet plugin --- .../NW004_data_struct_ETH_SPI.cpp | 31 ++++++++++++++----- src/src/DataStructs/DeviceStruct.h | 2 +- src/src/DataStructs/SettingsStruct.h | 5 --- src/src/DataStructs_templ/SettingsStruct.cpp | 13 -------- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index f3ae64fe3..07cf8700b 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -3,10 +3,12 @@ #ifdef USES_NW004 # include "../../../src/Globals/Settings.h" +# include "../../../src/Globals/SPIe.h" # include "../../../src/Helpers/ESPEasy_time_calc.h" # include "../../../src/Helpers/Hardware_GPIO.h" # include "../../../src/Helpers/LongTermOnOffTimer.h" +# include "../../../src/Helpers/SPI_Helper.h" # include "../../../src/Helpers/StringConverter.h" # include "../../../src/WebServer/Markup.h" @@ -37,8 +39,9 @@ namespace eth { # define NW004_KEY_GW 8 # define NW004_KEY_SN 9 # define NW004_KEY_DNS 10 +# define NW004_KEY_SPI_BUS 11 -# define NW004_MAX_KEY 11 +# define NW004_MAX_KEY 12 const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t key, bool displayString, KVS_StorageType::Enum& storageType) { @@ -52,6 +55,7 @@ const __FlashStringHelper * NW004_data_struct_ETH_SPI::getLabelString(uint32_t k case NW004_KEY_ETH_PIN_CS: return displayString ? F("Ethernet CS pin") : F("CS"); case NW004_KEY_ETH_PIN_IRQ: return displayString ? F("Ethernet IRQ pin") : F("IRQ"); case NW004_KEY_ETH_PIN_RST: return displayString ? F("Ethernet RST pin") : F("RST"); + case NW004_KEY_SPI_BUS: return displayString ? F("SPI Bus") : F("ethspibus"); case NW004_KEY_IP: storageType = KVS_StorageType::Enum::string_type; return F("IP"); @@ -225,6 +229,15 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) addFormNote(F("I²C-address of Ethernet PHY")); } { + if ((getSPIBusCount() > 1) && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + const int key = NW004_KEY_SPI_BUS; + const uint8_t spiBus = _kvs->getValueAsInt(key); + KVS_StorageType::Enum storageType; + SPIInterfaceSelector(getLabelString(key, true, storageType), + getLabelString(key, false, storageType), + spiBus); + } + const int gpio_keys[] = { NW004_KEY_ETH_PIN_CS, NW004_KEY_ETH_PIN_IRQ, @@ -302,7 +315,7 @@ bool NW004_data_struct_ETH_SPI::write_Eth_port(KeyValueWriter *writer) int8_t spi_gpios[3]{}; - if (!Settings.getSPI_pins(spi_gpios)) { return false; } + if (!Settings.getSPI_pins(spi_gpios), (uint8_t)_kvs->getValueAsInt(NW004_KEY_SPI_BUS)) { return false; } const __FlashStringHelper*labels[] = { F("CLK"), F("MISO"), F("MOSI"), F("CS"), F("IRQ"), F("RST") }; const int pins[] = { @@ -367,7 +380,7 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { if (loglevelActiveFor(LOG_LEVEL_INFO)) { String log; - if (log.reserve(115)) { + if (log.reserve(125)) { const ESPEasy::net::EthPhyType_t phyType = static_cast(_kvs->getValueAsInt(NW004_KEY_ETH_PHY_TYPE)); @@ -375,7 +388,8 @@ void NW004_data_struct_ETH_SPI::ethPrintSettings() { log += toString(phyType); log += F(" PHY Addr: "); log += _kvs->getValueAsInt(NW004_KEY_ETH_PHY_ADDR); - log += strformat(F(" CS: %d IRQ: %d RST: %d"), + log += strformat(F(" SPI bus: %d CS: %d IRQ: %d RST: %d"), + _kvs->getValueAsInt(NW004_KEY_SPI_BUS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ), _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST)); @@ -421,13 +435,14 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { const int rstPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_RST); const int csPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_CS); const int irqPin = _kvs->getValueAsInt(NW004_KEY_ETH_PIN_IRQ); + const int spi_bus = _kvs->getValueAsInt(NW004_KEY_SPI_BUS); - spi_host_device_t SPI_host = Settings.getSPI_host(); + spi_host_device_t SPI_host = Settings.getSPI_host(spi_bus); if (SPI_host == spi_host_device_t::SPI_HOST_MAX) { addLog(LOG_LEVEL_ERROR, F("SPI not enabled")); - # ifdef ESP32C3 + # ifdef ESP32C3 // FIXME TD-er: Fallback for ETH01-EVO board SPI_host = spi_host_device_t::SPI2_HOST; @@ -435,7 +450,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { Settings.SPI_SCLK_pin = 7; Settings.SPI_MISO_pin = 3; Settings.SPI_MOSI_pin = 10; - # endif // ifdef ESP32C3 + # endif // ifdef ESP32C3 } // else @@ -447,7 +462,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { csPin, irqPin, rstPin, - SPI); + SPI_host); # else // if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( to_ESP_phy_type(phyType), diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index ad880f0e2..b1582ef0c 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -50,7 +50,7 @@ // Stored in Settings.I2C_SPI_bus_Flags !!! #define SPI_FLAGS_TASK_BUS_NUMBER 0 // 2 bit, stores the configured bus for a task // Stored in Settings.I2C_SPI_bus_Flags for Task 1 settings -#define SPI_FLAGS_ETH_BUS_NUMBER 2 // 2 bit, stores the configured bus for the Ethernet interface +// #define SPI_FLAGS_xxx_unused 2 // 2 bit, available #define SPI_FLAGS_SDCARD_BUS_NUMBER 4 // 2 bit, stores the configured bus for the SDCard /*********************************************************************************************\ diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 2ef0ae468..beac8867d 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -388,11 +388,6 @@ public: uint8_t getSPIBusForTask(taskIndex_t TaskIndex) const; void setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus); - #if FEATURE_ETHERNET - uint8_t getSPIBusForEth() const; - void setSPIBusForEth(uint8_t spi_bus); - #endif // if FEATURE_ETHERNET - #if FEATURE_SD uint8_t getSPIBusForSDCard() const; void setSPIBusForSDCard(uint8_t spi_bus); diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 6b44d5d43..4a4afaef3 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1213,19 +1213,6 @@ void SettingsStruct_tmpl::setSPIBusForTask(taskIndex_t TaskIndex, uint8 set2BitToUL(I2C_SPI_bus_Flags[TaskIndex], SPI_FLAGS_TASK_BUS_NUMBER, spi_bus); } -#if FEATURE_ETHERNET -// stored in I2C_SPI_bus_Flags for Task 1 (index 0) -template -uint8_t SettingsStruct_tmpl::getSPIBusForEth() const { - return get2BitFromUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER); -} - -template -void SettingsStruct_tmpl::setSPIBusForEth(uint8_t spi_bus) { - set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_ETH_BUS_NUMBER, spi_bus); -} -#endif // FEATURE_ETHERNET - #if FEATURE_SD // stored in I2C_SPI_bus_Flags for Task 1 (index 0) template From e3752b57558f2ee47ad2b9f2835a98ce1b38efb8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 14 Jan 2026 23:00:35 +0100 Subject: [PATCH 043/161] [Serial Console] Fix outputting data to USBCDC port for ESP32S2 See: https://github.com/espressif/arduino-esp32/issues/12178 --- src/src/ESPEasyCore/ESPEasy_Console_Port.cpp | 45 +++++++++++++++----- src/src/Helpers/LogStreamWriter.cpp | 12 +++--- src/src/Helpers/LogStreamWriter.h | 10 ++--- src/src/Helpers/SerialWriteBuffer.cpp | 8 +--- src/src/Helpers/SerialWriteBuffer.h | 2 +- 5 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp index 5f36d2aa2..d0a5c54b1 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console_Port.cpp @@ -18,10 +18,10 @@ #ifdef ESP32 -#define CONSOLE_INPUT_BUFFER_SIZE 1280 +# define CONSOLE_INPUT_BUFFER_SIZE 1280 #else -#define CONSOLE_INPUT_BUFFER_SIZE 128 -#endif +# define CONSOLE_INPUT_BUFFER_SIZE 128 +#endif // ifdef ESP32 /* @@ -31,19 +31,20 @@ */ EspEasy_Console_Port::EspEasy_Console_Port(LogDestination log_destination) -: _serialWriteBuffer(log_destination) + : _serialWriteBuffer(log_destination) { - InputBuffer_Serial = (char*)calloc(1, CONSOLE_INPUT_BUFFER_SIZE); + InputBuffer_Serial = (char *)calloc(1, CONSOLE_INPUT_BUFFER_SIZE); } EspEasy_Console_Port::~EspEasy_Console_Port() { #if FEATURE_DEFINE_SERIAL_CONSOLE_PORT + if (_serial != nullptr) { delete _serial; _serial = nullptr; } -#endif +#endif // if FEATURE_DEFINE_SERIAL_CONSOLE_PORT free(InputBuffer_Serial); } @@ -111,14 +112,36 @@ void EspEasy_Console_Port::endPort() } } - bool EspEasy_Console_Port::process_serialWriteBuffer() { if (_serial != nullptr) { #ifdef ESP32 - if (!xPortCanYield()) return false; -#endif - return _serialWriteBuffer.process(_serial, _serial->availableForWrite()); + + if (!xPortCanYield()) { return false; } +#endif // ifdef ESP32 + size_t availableForWrite = _serial->availableForWrite(); + + if (availableForWrite == 0) { return false; } + + if (availableForWrite == 1) { + // For only a single byte, just write it directly + return _serialWriteBuffer.process(_serial, availableForWrite); + } + + if (availableForWrite > 64) { + // Set to max. of 64 bytes as this is the optimum 'chunk size' for most + // serial ports, like the CDC ports and I2C to UART. + // Also it is relatively fast to allocate. + availableForWrite = 64; + } + + PrintToString str; + str.reserve(availableForWrite); + + if (_serialWriteBuffer.process(&str, availableForWrite)) { + _serial->write(str.get().c_str(), str.length()); + return true; + } } return false; } @@ -147,7 +170,7 @@ bool EspEasy_Console_Port::process_consoleInput(uint8_t SerialInByte) Logging.consolePrintln(concat('>', cmd)); #endif - ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd)}, true); + ExecuteCommand_all({ EventValueSource::Enum::VALUE_SOURCE_SERIAL, std::move(cmd) }, true); SerialInByteCounter = 0; InputBuffer_Serial[0] = 0; // serial data processed, clear buffer return true; diff --git a/src/src/Helpers/LogStreamWriter.cpp b/src/src/Helpers/LogStreamWriter.cpp index b58e56ce2..d50eb9105 100644 --- a/src/src/Helpers/LogStreamWriter.cpp +++ b/src/src/Helpers/LogStreamWriter.cpp @@ -14,7 +14,7 @@ void LogStreamWriter::clear() _readpos = 0; } -bool LogStreamWriter::process(Stream*stream, size_t availableForWrite) +bool LogStreamWriter::process(Print*stream, size_t availableForWrite) { if (stream == nullptr) { return false; } return write(*stream, availableForWrite) != 0; @@ -30,7 +30,7 @@ uint32_t LogStreamWriter::getNrMessages() const return Logging.getNrMessages(_log_destination); } -size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite) +size_t LogStreamWriter::write(Print& stream, size_t nrBytesToWrite) { size_t bytesWritten = 0; @@ -45,7 +45,7 @@ size_t LogStreamWriter::write(Stream& stream, size_t nrBytesToWrite) return bytesWritten; } -size_t LogStreamWriter::write_single_item(Stream& stream, +size_t LogStreamWriter::write_single_item(Print& stream, size_t nrBytesToWrite) { const size_t res = write_item(stream, nrBytesToWrite); @@ -54,7 +54,7 @@ size_t LogStreamWriter::write_single_item(Stream& stream, return res; } -size_t LogStreamWriter::write_item(Stream& stream, +size_t LogStreamWriter::write_item(Print& stream, size_t nrBytesToWrite) { size_t bytesWritten = 0; @@ -103,7 +103,7 @@ size_t LogStreamWriter::write_item(Stream& stream, ++_readpos; } else { if ((bytesWritten + 2) > nrBytesToWrite) { return bytesWritten; } - bytesWritten += stream.println(); + bytesWritten += stream.print(F("\r\n")); // stream.println(); // Done with entry, cleanup and leave clear(); @@ -113,6 +113,6 @@ size_t LogStreamWriter::write_item(Stream& stream, return bytesWritten; } -size_t LogStreamWriter::write_skipping(Stream& stream) { return 0; } +size_t LogStreamWriter::write_skipping(Print& stream) { return 0; } void LogStreamWriter::prepare_prefix() {} diff --git a/src/src/Helpers/LogStreamWriter.h b/src/src/Helpers/LogStreamWriter.h index 2e94453ed..b32866401 100644 --- a/src/src/Helpers/LogStreamWriter.h +++ b/src/src/Helpers/LogStreamWriter.h @@ -11,7 +11,7 @@ public: virtual ~LogStreamWriter() {} - virtual bool process(Stream* stream, size_t availableForWrite); + virtual bool process(Print* stream, size_t availableForWrite); // Only use this from derived classes, as we need a Stream to further process virtual bool process(); @@ -24,20 +24,20 @@ protected: // Write continuously until either nrBytesToWrite was reached or no new messages were available to process. // @retval Number of bytes written. Zero when no new message was available to process. - virtual size_t write(Stream& stream, + virtual size_t write(Print& stream, size_t nrBytesToWrite); // Write single item and clear() on return. // This way each call starts with a new item and long messages may get truncated based on nrBytesToWrite // @retval Number of bytes written. Zero when no new message was available to process. - virtual size_t write_single_item(Stream& stream, + virtual size_t write_single_item(Print& stream, size_t nrBytesToWrite); - virtual size_t write_item(Stream& stream, + virtual size_t write_item(Print& stream, size_t nrBytesToWrite); - virtual size_t write_skipping(Stream& stream); + virtual size_t write_skipping(Print& stream); virtual void prepare_prefix(); diff --git a/src/src/Helpers/SerialWriteBuffer.cpp b/src/src/Helpers/SerialWriteBuffer.cpp index 93a8737b6..0678982ff 100644 --- a/src/src/Helpers/SerialWriteBuffer.cpp +++ b/src/src/Helpers/SerialWriteBuffer.cpp @@ -34,14 +34,10 @@ String SerialWriteBuffer_t::colorize(const String& str) const { } -size_t SerialWriteBuffer_t::write_skipping(Stream& stream) +size_t SerialWriteBuffer_t::write_skipping(Print& stream) { - size_t bytesWritten{}; - // Mark with empty line we skipped the rest of the message. - bytesWritten += stream.println(F(" ...")); - bytesWritten += stream.println(); - return bytesWritten; + return stream.print(F(" ...\r\n\r\n")); } void SerialWriteBuffer_t::prepare_prefix() diff --git a/src/src/Helpers/SerialWriteBuffer.h b/src/src/Helpers/SerialWriteBuffer.h index 556561570..60d8f6a07 100644 --- a/src/src/Helpers/SerialWriteBuffer.h +++ b/src/src/Helpers/SerialWriteBuffer.h @@ -14,7 +14,7 @@ public: private: - size_t write_skipping(Stream& stream) override; + size_t write_skipping(Print& stream) override; void prepare_prefix() override; From b65c9bd26d9b42c240898c14c773d09463a3810a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 18 Jan 2026 23:17:01 +0100 Subject: [PATCH 044/161] [Cleanup] Remove Provisioningxxx fall-back commands and other fixes --- docs/source/Plugin/P000_commands.repl | 32 +++++++--- src/src/Commands/InternalCommands.cpp | 14 +---- src/src/Commands/InternalCommands_decoder.cpp | 10 --- src/src/Commands/InternalCommands_decoder.h | 10 --- src/src/Commands/Provisioning.cpp | 63 ++----------------- src/src/Commands/Provisioning.h | 19 ------ 6 files changed, 28 insertions(+), 120 deletions(-) diff --git a/docs/source/Plugin/P000_commands.repl b/docs/source/Plugin/P000_commands.repl index 438bebfb1..bacfd19f0 100644 --- a/docs/source/Plugin/P000_commands.repl +++ b/docs/source/Plugin/P000_commands.repl @@ -551,35 +551,47 @@ NB: No certificate validation is done! " - ProvisionConfig"," + Provision,Config"," :red:`Internal`"," Fetch ``config.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionConfig``" + ``Provision,Config``" " - ProvisionSecurity"," + Provision,Security"," :red:`Internal`"," Fetch ``security.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionSecurity``" + ``Provision,Security``" " - ProvisionNotification"," + Provision,DevSecurity"," + :red:`Internal`"," + Fetch ``devsecurity.dat`` as configured in the Provisioning configuration (see Settings Archive) + + ``Provision,DevSecurity``" + " + Provision,Notification"," :red:`Internal`"," Fetch ``notification.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionNotification``" + ``Provision,Notification``" " - ProvisionProvision"," + Provision,Provision"," :red:`Internal`"," Fetch ``provisioning.dat`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionProvision``" + ``Provision,Provision``" " - ProvisionRules"," + Provision,Rules"," :red:`Internal`"," Fetch ``rulesN.txt`` as configured in the Provisioning configuration (see Settings Archive) - ``ProvisionRules,1`` to fetch ``rules1.txt`` " + ``Provision,Rules,1`` to fetch ``rules1.txt`` " + " + Provision,Firmware"," + :red:`Internal`"," + Fetch a new firmware binary, and flash it on the unit, as configured in the Provisioning configuration (see Settings Archive) + + ``Provision,Firmware,`` to fetch and install the specified .bin file from the specified url." " Publish"," :green:`Rules`"," diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index 3d99d2984..b7431ca67 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -101,7 +101,7 @@ bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) { log += F(" ExtraArg"); } log += i; - log += '='; + log += ':'; log += parameter; } ++i; @@ -412,18 +412,6 @@ bool InternalCommands::executeInternalCommand() #endif // if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING case ESPEasy_cmd_e::provision: COMMAND_CASE_A(Command_Provisioning_Dispatcher, -1); // Provisioning.h -# ifdef PLUGIN_BUILD_MAX_ESP32 - - // FIXME DEPRECATED: Fallback for temporary backward compatibility - case ESPEasy_cmd_e::provisionconfig: COMMAND_CASE_A(Command_Provisioning_ConfigFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionsecurity: COMMAND_CASE_A(Command_Provisioning_SecurityFallback, 0); // Provisioning.h -# if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionnotification: COMMAND_CASE_A(Command_Provisioning_NotificationFallback, 0); // Provisioning.h -# endif // if FEATURE_NOTIFIER - case ESPEasy_cmd_e::provisionprovision: COMMAND_CASE_A(Command_Provisioning_ProvisionFallback, 0); // Provisioning.h - case ESPEasy_cmd_e::provisionrules: COMMAND_CASE_A(Command_Provisioning_RulesFallback, 1); // Provisioning.h - case ESPEasy_cmd_e::provisionfirmware: COMMAND_CASE_A(Command_Provisioning_FirmwareFallback, 1); // Provisioning.h -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 #endif // if FEATURE_CUSTOM_PROVISIONING case ESPEasy_cmd_e::pulse: COMMAND_CASE_A(Command_GPIO_Pulse, 3); // GPIO.h #if FEATURE_MQTT diff --git a/src/src/Commands/InternalCommands_decoder.cpp b/src/src/Commands/InternalCommands_decoder.cpp index ef04f453a..c9a612f64 100644 --- a/src/src/Commands/InternalCommands_decoder.cpp +++ b/src/src/Commands/InternalCommands_decoder.cpp @@ -193,16 +193,6 @@ const char Internal_commands_p[] PROGMEM = #endif // #if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING "provision|" - # ifdef PLUGIN_BUILD_MAX_ESP32 // FIXME DEPRECATED: Fallback for temporary backward compatibility - "provisionconfig|" - "provisionsecurity|" - # if FEATURE_NOTIFIER - "provisionnotification|" - # endif // #if FEATURE_NOTIFIER - "provisionprovision|" - "provisionrules|" - "provisionfirmware|" - # endif // #ifdef PLUGIN_BUILD_MAX_ESP32 #endif // #if FEATURE_CUSTOM_PROVISIONING "pulse|" #if FEATURE_MQTT diff --git a/src/src/Commands/InternalCommands_decoder.h b/src/src/Commands/InternalCommands_decoder.h index 8a6d12a8f..65d44006c 100644 --- a/src/src/Commands/InternalCommands_decoder.h +++ b/src/src/Commands/InternalCommands_decoder.h @@ -154,16 +154,6 @@ enum class ESPEasy_cmd_e : uint8_t { #endif // #if FEATURE_POST_TO_HTTP #if FEATURE_CUSTOM_PROVISIONING provision, -# ifdef PLUGIN_BUILD_MAX_ESP32 // FIXME DEPRECATED: Fallback for temporary backward compatibility - provisionconfig, - provisionsecurity, -# if FEATURE_NOTIFIER - provisionnotification, -# endif // #if FEATURE_NOTIFIER - provisionprovision, - provisionrules, - provisionfirmware, -# endif // #ifdef PLUGIN_BUILD_MAX_ESP32 #endif // #if FEATURE_CUSTOM_PROVISIONING pulse, #if FEATURE_MQTT diff --git a/src/src/Commands/Provisioning.cpp b/src/src/Commands/Provisioning.cpp index 1feaec561..8d246d39b 100644 --- a/src/src/Commands/Provisioning.cpp +++ b/src/src/Commands/Provisioning.cpp @@ -33,6 +33,11 @@ String Command_Provisioning_Dispatcher(struct EventStruct *event, } else if (equals(cmd, F("security"))) { return Command_Provisioning_Security(); +#if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE + } else + if (equals(cmd, F("devsecurity"))) { + return Command_Provisioning_DeviceSecurity(); +#endif // if FEATURE_STORE_CREDENTIALS_SEPARATE_FILE } return return_command_failed_flashstr(); } @@ -86,62 +91,4 @@ String Command_Provisioning_Firmware(struct EventStruct *event, const char *Line return error; } -# ifdef PLUGIN_BUILD_MAX_ESP32 -void Command_Provisioning_DeprecatedMessage(const String& param) { - addLog(LOG_LEVEL_ERROR, strformat(F("WARNING: 'Provision%s' is deprecated, change to 'Provision,%s'"), param.c_str(), param.c_str())); -} - -String Command_Provisioning_ConfigFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Config")); - return Command_Provisioning_Config(); -} - -String Command_Provisioning_SecurityFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Security")); - return Command_Provisioning_Security(); -} - -# if FEATURE_NOTIFIER -String Command_Provisioning_NotificationFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Notification")); - return Command_Provisioning_Notification(); -} - -# endif // if FEATURE_NOTIFIER - - -String Command_Provisioning_ProvisionFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Provision")); - return Command_Provisioning_Provision(); -} - -String Command_Provisioning_RulesFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Rules,")); - - if ((event->Par1 <= 0) || (event->Par1 > 4)) { - return F("ProvisionRules: rules index out of range"); - } - return downloadFileType(FileType::RULES_TXT, event->Par1 - 1); -} - -String Command_Provisioning_FirmwareFallback(struct EventStruct *event, const char *Line) -{ - Command_Provisioning_DeprecatedMessage(F("Firmware,")); - - // FIXME TD-er: Must only allow to use set prefix in the provisioning settings - const String url = parseStringToEndKeepCase(Line, 2); - String error; - - downloadFirmware(url, error); - return error; -} - - -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 - #endif // if FEATURE_CUSTOM_PROVISIONING diff --git a/src/src/Commands/Provisioning.h b/src/src/Commands/Provisioning.h index c7ebc3fd9..ef5c66088 100644 --- a/src/src/Commands/Provisioning.h +++ b/src/src/Commands/Provisioning.h @@ -25,25 +25,6 @@ String Command_Provisioning_Rules(struct EventStruct *event); String Command_Provisioning_Firmware(struct EventStruct *event, const char *Line); -// FIXME DEPRECATED! -# ifdef PLUGIN_BUILD_MAX_ESP32 -String Command_Provisioning_ConfigFallback(struct EventStruct *event, - const char *Line); -String Command_Provisioning_SecurityFallback(struct EventStruct *event, - const char *Line); -# if FEATURE_NOTIFIER -String Command_Provisioning_NotificationFallback(struct EventStruct *event, - const char *Line); -# endif // if FEATURE_NOTIFIER -String Command_Provisioning_ProvisionFallback(struct EventStruct *event, - const char *Line); -String Command_Provisioning_RulesFallback(struct EventStruct *event, - const char *Line); - -String Command_Provisioning_FirmwareFallback(struct EventStruct *event, - const char *Line); -# endif // ifdef PLUGIN_BUILD_MAX_ESP32 - #endif // if FEATURE_CUSTOM_PROVISIONING #endif // ifndef COMMANDS_PROVISIONING_H From df0b90d15f78594290075430d96b4c5969d1dbd7 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 18 Jan 2026 23:19:18 +0100 Subject: [PATCH 045/161] [Docs] Fix some small typos --- docs/source/Network/_Network.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/Network/_Network.rst b/docs/source/Network/_Network.rst index 807f8d1b7..586ebe5e5 100644 --- a/docs/source/Network/_Network.rst +++ b/docs/source/Network/_Network.rst @@ -3,7 +3,7 @@ Network ******* -A Network plugin is compable with a 'driver' for a network adapter. +A Network plugin is comparable with a 'driver' for a network adapter. The first 2 entries will be the same for every ESPEasy setup and these cannot be removed. From 9d83d6c70366ea4b44f6b87af53777d3bd8258e5 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 18 Jan 2026 23:20:14 +0100 Subject: [PATCH 046/161] [Log] Restore EVENT: (some event) in logging for BUILD_NO_DEBUG builds --- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 477497d35..44be92ceb 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -98,11 +98,11 @@ void rulesProcessing(const String& event) { #ifndef BUILD_NO_DEBUG const unsigned long timer = millis(); #endif // ifndef BUILD_NO_DEBUG -#ifndef BUILD_NO_DEBUG +// #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLogMove(LOG_LEVEL_INFO, concat(F("EVENT: "), event)); } -#endif +// #endif if (Settings.OldRulesEngine()) { bool eventHandled = false; From dbc5e87f3566498e7ffe6e939cef8502946c8149 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 19 Jan 2026 01:21:14 +0100 Subject: [PATCH 047/161] [TimingStats] Tweak timing stats for console output --- src/src/ESPEasyCore/ESPEasy_Console.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasy_Console.cpp b/src/src/ESPEasyCore/ESPEasy_Console.cpp index 8b4ff8384..306b18aef 100644 --- a/src/src/ESPEasyCore/ESPEasy_Console.cpp +++ b/src/src/ESPEasyCore/ESPEasy_Console.cpp @@ -17,7 +17,7 @@ #include #ifdef ESP32 -#include +# include #endif @@ -241,11 +241,12 @@ void EspEasy_Console_t::begin(uint32_t baudrate) if (_fallbackSerial._serial != nullptr) { _fallbackSerial._serial->begin(baudrate); -#ifdef ESP32 +# ifdef ESP32 + // Need to have this string as C-string, not F-string perimanSetPinBusExtraType(SOC_RX0, "Console"); perimanSetPinBusExtraType(SOC_TX0, "Console"); -#endif +# endif // ifdef ESP32 addLog(LOG_LEVEL_INFO, F("ESPEasy console fallback enabled")); } @@ -329,8 +330,10 @@ bool EspEasy_Console_t::process_serialWriteBuffer() { res = true; } #endif // if USES_ESPEASY_CONSOLE_FALLBACK_PORT +#if FEATURE_TIMING_STATS - STOP_TIMER(CONSOLE_WRITE_SERIAL); + if (res) { STOP_TIMER(CONSOLE_WRITE_SERIAL); } +#endif // if FEATURE_TIMING_STATS return res; } From 0d09e9a7da2e16bf67e6978dbb61bedb507e225c Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 19 Jan 2026 01:21:50 +0100 Subject: [PATCH 048/161] [Domoticz] Fix Domoticz MQTT outputting svalue wrapped in quotes --- src/src/Helpers/KeyValueWriter_JSON.cpp | 16 +++++++++++++--- src/src/Helpers/KeyValueWriter_JSON.h | 2 +- src/src/Helpers/_CPlugin_DomoticzHelper.cpp | 4 +++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/src/Helpers/KeyValueWriter_JSON.cpp b/src/src/Helpers/KeyValueWriter_JSON.cpp index 0d45907c7..1b3011858 100644 --- a/src/src/Helpers/KeyValueWriter_JSON.cpp +++ b/src/src/Helpers/KeyValueWriter_JSON.cpp @@ -121,6 +121,8 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) const size_t nrValues = kv._values.size(); + const bool forceString = kv._format == KeyValueStruct::Format::PreFormatted; + if (!kv._isArray) { // Either 1 value or empty value if (nrValues == 0) { @@ -129,7 +131,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) pr.write('"'); } else { - writeValue(kv._values[0]); + writeValue(kv._values[0], forceString); } } else { // Multiple values, so we must wrap it in [] @@ -153,7 +155,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) pr.write('\t'); #endif // ifdef USE_KWH_JSON_PRETTY_PRINT - writeValue(kv._values[i]); + writeValue(kv._values[i], forceString); } getPrint().write(']'); #ifndef USE_KWH_JSON_PRETTY_PRINT @@ -162,7 +164,7 @@ void KeyValueWriter_JSON::write(const KeyValueStruct& kv) } } -void KeyValueWriter_JSON::writeValue(const ValueStruct& val) +void KeyValueWriter_JSON::writeValue(const ValueStruct& val, bool forceString) { if (!val.isSet()) { return; } auto& pr = getPrint(); @@ -196,6 +198,14 @@ void KeyValueWriter_JSON::writeValue(const ValueStruct& val) case ValueStruct::ValueType::Unset: case ValueStruct::ValueType::String: case ValueStruct::ValueType::FlashString: + if (forceString) { + String tmp(to_json_value(str)); + if (!isWrappedWithQuotes(tmp)) { + tmp = wrap_String(tmp, '"'); + } + pr.print(tmp); + return; + } break; } pr.print(to_json_value(str)); diff --git a/src/src/Helpers/KeyValueWriter_JSON.h b/src/src/Helpers/KeyValueWriter_JSON.h index e32483d44..db853bb13 100644 --- a/src/src/Helpers/KeyValueWriter_JSON.h +++ b/src/src/Helpers/KeyValueWriter_JSON.h @@ -73,7 +73,7 @@ public: private: - void writeValue(const ValueStruct& value); + void writeValue(const ValueStruct& value, bool forceString); #ifdef USE_KWH_JSON_PRETTY_PRINT diff --git a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp index cd4a3a76d..99b3a548b 100644 --- a/src/src/Helpers/_CPlugin_DomoticzHelper.cpp +++ b/src/src/Helpers/_CPlugin_DomoticzHelper.cpp @@ -239,7 +239,9 @@ String serializeDomoticzJson(struct EventStruct *event) } } else { writer.write({ F("nvalue"), F("0") }); - writer.write({ F("svalue"), formatDomoticzSensorType(event) }); + writer.write({ F("svalue"), + formatDomoticzSensorType(event), + KeyValueStruct::Format::PreFormatted }); } } From 047b9a52d490a39b522e83850fcad0e02ca9e29d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 19 Jan 2026 21:03:37 +0100 Subject: [PATCH 049/161] [Log] Restore ACT : (rules action) in logging for BUILD_NO_DEBUG builds --- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 44be92ceb..eefacec8c 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -1049,13 +1049,13 @@ void processMatchedRule(String& action, const String& event, substitute_eventvalue(action, event); const bool executeRestricted = equals(parseString(action, 1), F("restrict")); -#ifndef BUILD_NO_DEBUG +// #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { String actionlog = executeRestricted ? F("ACT : (restricted) ") : F("ACT : "); actionlog += action; addLogMove(LOG_LEVEL_INFO, actionlog); } -#endif +// #endif if (executeRestricted) { ExecuteCommand_all({EventValueSource::Enum::VALUE_SOURCE_RULES_RESTRICTED, parseStringToEndKeepCase(action, 2)}); } else { From dff0e8c4068b47d4a192da4d73ff7159d383edcb Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 21 Jan 2026 14:06:31 +0100 Subject: [PATCH 050/161] [P073] Bugfix: Display stays blank after upgrade without settings-save (#5475) * [P073] Bugfix: Display stays blank after upgrade without settings-save * [P073] Revert to regular GPIO for TM1637 on ESP8266 * [P073] Minor code reduction (eliminate unneeded variables) --- src/_P073_7DGT.ino | 42 +++-- src/src/PluginStructs/P073_data_struct.cpp | 182 +++++++++++++-------- src/src/PluginStructs/P073_data_struct.h | 10 +- 3 files changed, 150 insertions(+), 84 deletions(-) diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index eb21f25a3..8775baa2e 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -42,6 +42,9 @@ // /** History + * 2026-01-17 tonhuisman: Revert to using 'regular' Arduino GPIO functions for TM1637 displays on ESP8266 + * 2026-01-12 tonhuisman: Fix initialization of number of digits when upgrading to 20260108 build, + * formatted source with new Uncrustify config * 2024-09-28 tonhuisman: Switch to using DIRECT_PIN_IO, to get better timing-accuracy for TM1637 displays * 2024-09-27 tonhuisman: Add option to flash dot on second digit instead of the colon for blinking time * 2024-08-24 tonhuisman: Add compiletime define P073_USE_74HC595_OVERRIDE that, when, set to 1, allows 74HC595 displays to be included @@ -96,12 +99,13 @@ # include "src/PluginStructs/P073_data_struct.h" - boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; - switch (function) { - case PLUGIN_DEVICE_ADD: { + switch (function) + { + case PLUGIN_DEVICE_ADD: + { auto& dev = Device[++deviceCount]; dev.Number = PLUGIN_ID_073; dev.Type = DEVICE_TYPE_TRIPLE; @@ -113,13 +117,15 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_GET_DEVICENAME: { + case PLUGIN_GET_DEVICENAME: + { string = F(PLUGIN_NAME_073); break; } # if P073_SCROLL_TEXT || P073_USE_74HC595 - case PLUGIN_SET_DEFAULTS: { + case PLUGIN_SET_DEFAULTS: + { # if P073_SCROLL_TEXT P073_CFG_SCROLLSPEED = 10; // Default 10 * 0.1 sec scroll speed # endif // if P073_SCROLL_TEXT @@ -134,7 +140,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) } # endif // if P073_SCROLL_TEXT || P073_USE_74HC595 - case PLUGIN_WEBFORM_LOAD: { + case PLUGIN_WEBFORM_LOAD: + { addFormNote(F("TM1637: 1st=CLK-Pin, 2nd=DIO-Pin")); addFormNote(F("MAX7219: 1st=DIN-Pin, 2nd=CLK-Pin, 3rd=CS-Pin")); # if P073_USE_74HC595 @@ -151,7 +158,7 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(displtype); const FormSelectorOptions selector(optionCount, displtype); - selector.addFormSelector(F("Display Type"), F("displtype"), PCONFIG(0)); + selector.addFormSelector(F("Display Type"), F("displtype"), P073_CFG_DISPLAYTYPE); } # if P073_USE_74HC595 @@ -258,7 +265,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WEBFORM_SAVE: { + case PLUGIN_WEBFORM_SAVE: + { P073_CFG_DISPLAYTYPE = getFormItemInt(F("displtype")); P073_CFG_OUTPUTTYPE = getFormItemInt(F("displout")); P073_CFG_BRIGHTNESS = getFormItemInt(F("brightness")); @@ -292,12 +300,14 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: { + case PLUGIN_EXIT: + { success = true; break; } - case PLUGIN_INIT: { + case PLUGIN_INIT: + { initPluginTaskData(event->TaskIndex, new (std::nothrow) P073_data_struct()); P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -317,7 +327,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WRITE: { + case PLUGIN_WRITE: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -327,7 +338,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_ONCE_A_SECOND: { + case PLUGIN_ONCE_A_SECOND: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -339,7 +351,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) } # if P073_SCROLL_TEXT - case PLUGIN_TEN_PER_SECOND: { + case PLUGIN_TEN_PER_SECOND: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -353,7 +366,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) # if P073_USE_74HC595 - case PLUGIN_TASKTIMER_IN: { + case PLUGIN_TASKTIMER_IN: + { P073_data_struct *P073_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 3d1fe2586..b99dd4ab6 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -2,7 +2,9 @@ #ifdef USES_P073 -# include +# ifdef ESP32 +# include +# endif // ifdef ESP32 uint8_t P073_getDefaultDigits(uint8_t displayModel, uint8_t digits) { @@ -34,7 +36,8 @@ uint8_t P073_mapCharToFontPosition(char character, const String specialChars = F(" -^=/_%@.,;:+*#!?'\"<>\\()|"); const String chnorux = F("CHNORUX"); - switch (fontset) { + switch (fontset) + { case 1: // Siekoo case 2: // Siekoo with uppercase 'CHNORUX' @@ -58,16 +61,24 @@ uint8_t P073_mapCharToFontPosition(char character, if (isDigit(character)) { position = character - '0'; - } else if (isAlpha(character)) { + } + else if (isAlpha(character)) { position = character - (isLowerCase(character) ? 'a' : 'A') + 16; } else { - switch (character) { - case ' ': position = 10; break; - case '-': position = 11; break; - case '^': position = 12; break; // degree - case '=': position = 13; break; - case '/': position = 14; break; - case '_': position = 15; break; + switch (character) + { + case ' ': position = 10; + break; + case '-': position = 11; + break; + case '^': position = 12; + break; // degree + case '=': position = 13; + break; + case '/': position = 14; + break; + case '_': position = 15; + break; } } # if P073_EXTRA_FONTS @@ -85,7 +96,8 @@ uint8_t P073_getFontChar(uint8_t index, uint8_t fontset) { # if P073_EXTRA_FONTS - switch (fontset) { + switch (fontset) + { case 1: // Siekoo case 2: // Siekoo uppercase CHNORUX return pgm_read_byte(&(SiekooCharTable[index])); @@ -143,10 +155,12 @@ void P073_display_output_selector(const __FlashStringHelper *id, int16_t value) P073_DISP_DATE, }; const FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + selector.addFormSelector(F("Display Output"), id, value); } # if P073_EXTRA_FONTS + void P073_font_selector(const __FlashStringHelper *id, int16_t value) { const __FlashStringHelper *fontset[] = { F("Default"), @@ -154,7 +168,8 @@ void P073_font_selector(const __FlashStringHelper *id, int16_t value) { F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7"), }; - const FormSelectorOptions selector(NR_ELEMENTS(fontset), fontset); + const FormSelectorOptions selector(NR_ELEMENTS(fontset), fontset); + selector.addFormSelector(F("Font set"), id, value); addFormNote(F("Check documentation for examples of the font sets.")); } @@ -202,9 +217,9 @@ void P073_data_struct::init(struct EventStruct *event) # endif // if P073_SCROLL_TEXT rightAlignTempMAX7219 = bitRead(P073_CFG_FLAGS, P073_OPTION_RIGHTALIGN); suppressLeading0 = bitRead(P073_CFG_FLAGS, P073_OPTION_SUPPRESS0); - #if P073_BLINK_DOT + # if P073_BLINK_DOT blinkdot = bitRead(P073_CFG_FLAGS, P073_OPTION_BLINK_DOT); - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT timesep = true; # if P073_EXTRA_FONTS fontset = P073_CFG_FONTSET; @@ -233,11 +248,20 @@ void P073_data_struct::init(struct EventStruct *event) } # endif // if P073_USE_74HC595 + if (0 == digits) { + digits = P073_getDefaultDigits(P073_CFG_DISPLAYTYPE); + } + if ((digits > 0) && (digits < 4)) { hideDegree = true; // Hide degree symbol on small displays } - switch (displayModel) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P073 : Digits: %d, model: %d, pins: %d, %d, %d"), digits, displayModel, pin1, pin2, pin3)); + } + + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -275,6 +299,7 @@ void P073_data_struct::init(struct EventStruct *event) } # if P073_USE_74HC595 + bool P073_data_struct::plugin_fifty_per_second(struct EventStruct *event) { # ifdef P073_DEBUG counter50++; @@ -289,9 +314,7 @@ bool P073_data_struct::plugin_fifty_per_second(struct EventStruct *event) { return false; } -bool P073_data_struct::is74HC595Matrix() { - return P073_74HC595_2_8DGT == displayModel && P073_HC595_MULTIPLEX; -} +bool P073_data_struct::is74HC595Matrix() { return P073_74HC595_2_8DGT == displayModel && P073_HC595_MULTIPLEX; } // ==================================== // ---- 74HC595 specific functions ---- @@ -642,6 +665,7 @@ void P073_data_struct::FillBufferWithString(const String& textToShow, } # if P073_SCROLL_TEXT + int P073_data_struct::getEffectiveTextLength(const String& text) { const int textLength = text.length(); int p = 0; @@ -757,17 +781,14 @@ void P073_data_struct::setScrollSpeed(uint8_t speed) { scrollPos = 0; } -bool P073_data_struct::isScrollEnabled() { - return txtScrolling && scrollAllowed; -} +bool P073_data_struct::isScrollEnabled() { return txtScrolling && scrollAllowed; } -void P073_data_struct::setScrollEnabled(bool scroll) { - scrollAllowed = scroll; -} +void P073_data_struct::setScrollEnabled(bool scroll) { scrollAllowed = scroll; } # endif // if P073_SCROLL_TEXT # if P073_7DBIN_COMMAND + void P073_data_struct::setBinaryData(const String& data) { binaryData = true; # if P073_SCROLL_TEXT @@ -783,6 +804,7 @@ void P073_data_struct::setBinaryData(const String& data) { # endif // if P073_7DBIN_COMMAND # ifdef P073_DEBUG + void P073_data_struct::LogBufferContent(String prefix) { String log; @@ -803,9 +825,7 @@ void P073_data_struct::LogBufferContent(String prefix) { # endif // ifdef P073_DEBUG // in case of error show all dashes -void P073_data_struct::FillBufferWithDash() { - memset(showbuffer, 11, sizeof(showbuffer)); -} +void P073_data_struct::FillBufferWithDash() { memset(showbuffer, 11, sizeof(showbuffer)); } void P073_data_struct::ClearBuffer() { memset(showbuffer, @@ -820,11 +840,9 @@ void P073_data_struct::ClearBuffer() { } uint8_t P073_data_struct::tm1637_getFontChar(uint8_t index, - uint8_t fontset) { - return P073_revert7bits(P073_getFontChar(index, fontset)); -} + uint8_t fontset) { return P073_revert7bits(P073_getFontChar(index, fontset)); } -bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { +bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { if (output == P073_DISP_MANUAL) { return false; } @@ -855,16 +873,17 @@ bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { ); } - #if P073_BLINK_DOT + # if P073_BLINK_DOT if (blinkdot && ((output == P073_DISP_CLOCK24BLNK) || (output == P073_DISP_CLOCK12BLNK))) { showperiods[1] = timesep; // Blink dot on second digit } - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -900,15 +919,18 @@ bool P073_data_struct::plugin_once_a_second(struct EventStruct *event) { } # if P073_SCROLL_TEXT + bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { if ((output != P073_DISP_MANUAL) || !isScrollEnabled()) { return false; } if (NextScroll()) { - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: - case P073_TM1637_4DGTDOTS: { + case P073_TM1637_4DGTDOTS: + { tm1637_ShowBuffer(0, 4 # if P073_7DBIN_COMMAND , binaryData @@ -916,7 +938,8 @@ bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { ); break; } - case P073_TM1637_6DGT: { + case P073_TM1637_6DGT: + { tm1637_SwapDigitInBuffer(0); // only needed for 6-digits displays tm1637_ShowBuffer(0, 6 # if P073_7DBIN_COMMAND @@ -925,13 +948,15 @@ bool P073_data_struct::plugin_ten_per_second(struct EventStruct *event) { ); break; } - case P073_MAX7219_8DGT: { + case P073_MAX7219_8DGT: + { dotpos = -1; // avoid to display the dot max7219_ShowBuffer(); break; } # if P073_USE_74HC595 - case P073_74HC595_2_8DGT: { + case P073_74HC595_2_8DGT: + { hc595_ToOutputBuffer(); if (hc595_Sequential()) { // Sequential displays don't need continuous refreshing @@ -981,6 +1006,7 @@ enum class p073_commands_e : int8_t { c7doff, c7db, c7output, + }; bool P073_data_struct::plugin_write(struct EventStruct *event, @@ -1005,7 +1031,8 @@ bool P073_data_struct::plugin_write(struct EventStruct *event, bool success = false; bool displayon = false; - switch (cmd) { + switch (cmd) + { case p073_commands_e::c7dn: return plugin_write_7dn(event, text); case p073_commands_e::c7dt: @@ -1104,7 +1131,8 @@ bool P073_data_struct::plugin_write(struct EventStruct *event, setScrollEnabled(newScroll); # endif // if P073_SCROLL_TEXT - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1167,7 +1195,8 @@ bool P073_data_struct::plugin_write_7dn(struct EventStruct *event, } } - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(TM1637_4DIGIT, 8); @@ -1234,7 +1263,8 @@ bool P073_data_struct::plugin_write_7dt(const String& text) { FillBufferWithTemp(p073_temptemp); } - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1277,6 +1307,7 @@ bool P073_data_struct::plugin_write_7dt(const String& text) { } # if P073_7DDT_COMMAND + bool P073_data_struct::plugin_write_7ddt(const String& text) { if (output != P073_DISP_MANUAL) { return false; @@ -1301,7 +1332,8 @@ bool P073_data_struct::plugin_write_7ddt(const String& text) { } # endif // ifndef BUILD_NO_DEBUG - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: case P073_TM1637_6DGT: @@ -1403,7 +1435,8 @@ bool P073_data_struct::plugin_write_7dst(struct EventStruct *event) { # endif // if P073_SUPPRESS_ZERO ); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -1447,7 +1480,8 @@ bool P073_data_struct::plugin_write_7dsd(struct EventStruct *event) { # endif // if P073_SUPPRESS_ZERO ); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowTimeTemp4(timesep, 0); @@ -1494,7 +1528,8 @@ bool P073_data_struct::plugin_write_7dtext(const String& text) { { FillBufferWithString(text); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(0, 4); @@ -1522,6 +1557,7 @@ bool P073_data_struct::plugin_write_7dtext(const String& text) { } # if P073_EXTRA_FONTS + bool P073_data_struct::plugin_write_7dfont(struct EventStruct *event, const String & text) { if (!text.isEmpty()) { @@ -1545,6 +1581,7 @@ bool P073_data_struct::plugin_write_7dfont(struct EventStruct *event, # endif // if P073_EXTRA_FONTS # if P073_7DBIN_COMMAND + bool P073_data_struct::plugin_write_7dbin(const String& text) { if (!text.isEmpty()) { String data; @@ -1576,7 +1613,8 @@ bool P073_data_struct::plugin_write_7dbin(const String& text) { { FillBufferWithString(data, true); - switch (displayModel) { + switch (displayModel) + { case P073_TM1637_4DGTCOLON: case P073_TM1637_4DGTDOTS: tm1637_ShowBuffer(0, 4); @@ -1612,10 +1650,17 @@ bool P073_data_struct::plugin_write_7dbin(const String& text) { // ---- TM1637 specific functions ---- // =================================== -# define CLK_HIGH() DIRECT_pinWrite(this->pin1, HIGH) -# define CLK_LOW() DIRECT_pinWrite(this->pin1, LOW) -# define DIO_HIGH() DIRECT_PINMODE_INPUT(this->pin2) -# define DIO_LOW() DIRECT_PINMODE_OUTPUT(this->pin2) //; DIRECT_pinWrite(this->pin2, LOW) +# ifdef ESP32 +# define CLK_HIGH() DIRECT_pinWrite(this->pin1, HIGH) +# define CLK_LOW() DIRECT_pinWrite(this->pin1, LOW) +# define DIO_HIGH() DIRECT_PINMODE_INPUT(this->pin2) +# define DIO_LOW() DIRECT_PINMODE_OUTPUT(this->pin2) // ; DIRECT_pinWrite(this->pin2, LOW) +# else // ifdef ESP32 +# define CLK_HIGH() digitalWrite(this->pin1, HIGH) +# define CLK_LOW() digitalWrite(this->pin1, LOW) +# define DIO_HIGH() pinMode(this->pin2, INPUT) +# define DIO_LOW() pinMode(this->pin2, OUTPUT) +# endif // ifdef ESP32 void P073_data_struct::tm1637_i2cStart() { # ifdef P073_DEBUG @@ -1642,7 +1687,7 @@ void P073_data_struct::tm1637_i2cStop() { void P073_data_struct::tm1637_i2cAck() { CLK_LOW(); - pinMode(pin2, INPUT_PULLUP); + pinMode(this->pin2, INPUT_PULLUP); // DIO_HIGH(); delayMicroseconds(TM1637_CLOCKDELAY); @@ -1651,7 +1696,11 @@ void P073_data_struct::tm1637_i2cAck() { # ifdef P073_DEBUG const bool dummyAck = # endif // ifdef P073_DEBUG - DIRECT_pinRead(pin2); + # ifdef ESP32 + DIRECT_pinRead(this->pin2); + # else + digitalRead(this->pin2); + # endif // ifdef ESP32 # ifdef P073_DEBUG @@ -1674,12 +1723,12 @@ void P073_data_struct::tm1637_i2cAck() { void P073_data_struct::tm1637_i2cWrite_ack(uint8_t bytesToPrint[], uint8_t length) { - #ifdef P073_DEBUG + # ifdef P073_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, concat(F("7DGT : TM1637 databuffer: 0x"), formatToHex_array(bytesToPrint, length))); } - #endif // ifdef P073_DEBUG + # endif // ifdef P073_DEBUG tm1637_i2cStart(); for (uint8_t i = 0; i < length; ++i) { @@ -1697,9 +1746,8 @@ void P073_data_struct::tm1637_i2cWrite(uint8_t bytetoprint) { # ifdef P073_DEBUG addLog(LOG_LEVEL_DEBUG, F("7DGT : WriteByte")); # endif // ifdef P073_DEBUG - uint8_t i; - for (i = 0; i < 8; ++i) { + for (uint8_t i = 0; i < 8; ++i) { CLK_LOW(); if (bytetoprint & 0b00000001) { @@ -1726,21 +1774,25 @@ void P073_data_struct::tm1637_SetPowerBrightness(uint8_t brightlvl, # ifdef P073_DEBUG addLog(LOG_LEVEL_INFO, F("7DGT : Set BRIGHT")); # endif // ifdef P073_DEBUG - uint8_t brightvalue = (brightlvl & 0b111); + brightlvl &= 0b111; if (poweron) { - brightvalue = TM1637_POWER_ON | brightvalue; + brightlvl |= TM1637_POWER_ON; } else { - brightvalue = TM1637_POWER_OFF | brightvalue; + brightlvl |= TM1637_POWER_OFF; } - const uint8_t byteToPrint = brightvalue; - tm1637_i2cWrite_ack(byteToPrint); + tm1637_i2cWrite_ack(brightlvl); } void P073_data_struct::tm1637_InitDisplay() { - DIRECT_PINMODE_OUTPUT(pin1); - DIRECT_PINMODE_OUTPUT(pin2); + # ifdef ESP32 + DIRECT_PINMODE_OUTPUT(this->pin1); + DIRECT_PINMODE_OUTPUT(this->pin2); + # else // ifdef ESP32 + pinMode(this->pin1, OUTPUT); + pinMode(this->pin2, OUTPUT); + # endif // ifdef ESP32 CLK_HIGH(); DIO_HIGH(); diff --git a/src/src/PluginStructs/P073_data_struct.h b/src/src/PluginStructs/P073_data_struct.h index 63770e6d9..3c1f186fb 100644 --- a/src/src/PluginStructs/P073_data_struct.h +++ b/src/src/PluginStructs/P073_data_struct.h @@ -308,9 +308,9 @@ public: bool rightAlignTempMAX7219 = false; bool suppressLeading0 = false; uint8_t fontset = 0; - #if P073_BLINK_DOT + # if P073_BLINK_DOT bool blinkdot = false; - #endif // if P073_BLINK_DOT + # endif // if P073_BLINK_DOT # if P073_7DBIN_COMMAND bool binaryData = false; # endif // P073_7DBIN_COMMAND @@ -404,12 +404,12 @@ private: void hc595_ShowBuffer(); void hc595_ToOutputBuffer(); void hc595_AdjustBuffer(); - bool hc595_Sequential() { - return P073_HC595_SEQUENTIAL; - } + + bool hc595_Sequential() { return P073_HC595_SEQUENTIAL; } uint8_t outputbuffer[8]{}; # endif // if P073_USE_74HC595 + }; #endif // ifdef USES_P073 From f5e3ba29a077479c12a6547abe1eed0e225d6c9e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 22 Jan 2026 21:49:44 +0100 Subject: [PATCH 051/161] [P132] Improve calculation accuracy determining shunt microVolt factor --- lib/INA/src/INA.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/INA/src/INA.cpp b/lib/INA/src/INA.cpp index f6ad7ebf4..fb97b0a5f 100644 --- a/lib/INA/src/INA.cpp +++ b/lib/INA/src/INA.cpp @@ -702,7 +702,7 @@ int32_t INA_Class::getBusMicroAmps(const uint8_t deviceNumber) { ina.type == INA3221_2) // Doesn't compute Amps { microAmps = - (int64_t)getShuntMicroVolts(deviceNumber) * ((int64_t)1000000 / (int64_t)ina.microOhmR); + (int64_t)getShuntMicroVolts(deviceNumber) * (int64_t)1000000 / (int64_t)ina.microOhmR; } else { microAmps = (int64_t)readWord(ina.currentRegister, ina.address) * (int64_t)ina.current_LSB / (int64_t)1000; @@ -794,10 +794,7 @@ bool INA_Class::conversionFinished(const uint8_t deviceNumber) { case INA3221_2: cvBits = readWord(INA3221_MASK_REGISTER, ina.address) & (uint16_t)1; break; default: cvBits = 1; } // of switch type - if (cvBits != 0) - return (true); - else - return (false); + return cvBits != 0; } // of method "conversionFinished()" void INA_Class::waitForConversion(const uint8_t deviceNumber) { /*! From 0958d88afeec13555ea84911e17cb6f62be74c83 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Thu, 22 Jan 2026 21:50:41 +0100 Subject: [PATCH 052/161] [P132] Only read measurement when conversion is ready --- src/_P132_INA3221.ino | 44 +++++++++++----------- src/src/PluginStructs/P132_data_struct.cpp | 7 ++++ src/src/PluginStructs/P132_data_struct.h | 1 + 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index e8040fe1b..9f4ef8f10 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -419,26 +419,29 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) channel = 2; } - switch (reg) { - case 0: // Current - case 2: // Current - case 4: // Current - UserVar.setFloat(event->TaskIndex, r, - P132_data->getBusCurrent_mA(channel)); - break; - case 1: // Voltage - case 3: // Voltage - case 5: // Voltage - UserVar.setFloat(event->TaskIndex, r, - P132_data->getBusVoltage_V(channel) - + (P132_data->getShuntVoltage_mV(channel) / 1000.0f)); - break; - case 6: // Power - case 7: // Power - case 8: // Power - UserVar.setFloat(event->TaskIndex, r, - P132_data->getBusPower_mW(channel)); - break; + if (P132_data->conversionFinished(channel)) { + switch (reg) { + case 0: // Current + case 2: // Current + case 4: // Current + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusCurrent_mA(channel)); + break; + case 1: // Voltage + case 3: // Voltage + case 5: // Voltage + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusVoltage_V(channel) + + (P132_data->getShuntVoltage_mV(channel) / 1000.0f)); + break; + case 6: // Power + case 7: // Power + case 8: // Power + UserVar.setFloat(event->TaskIndex, r, + P132_data->getBusPower_mW(channel)); + break; + } + success = true; // Only if we have read data } } @@ -454,7 +457,6 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) } # endif // ifndef BUILD_NO_DEBUG - success = true; break; } } diff --git a/src/src/PluginStructs/P132_data_struct.cpp b/src/src/PluginStructs/P132_data_struct.cpp index 189b6addb..64767a16d 100644 --- a/src/src/PluginStructs/P132_data_struct.cpp +++ b/src/src/PluginStructs/P132_data_struct.cpp @@ -284,6 +284,13 @@ float P132_data_struct::getBusPower_mW(uint8_t reg) { return INA->getBusMicroWatts(_device + reg) * 0.001f; } +bool P132_data_struct::conversionFinished(uint8_t reg) { + if (!isInitialized()) { + return false; + } + return INA->conversionFinished(_device + reg); +} + # endif // if P132_EXTENDED // **************************************************************************/ diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 822499eee..8c1263dac 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -100,6 +100,7 @@ public: # if P132_EXTENDED float getBusCurrent_mA(uint8_t reg); float getBusPower_mW(uint8_t reg); + bool conversionFinished(uint8_t reg); void setCalibration(struct EventStruct *event); From e60b08539b29bc119ca1584dfaa0037ba4a71f7c Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:51:47 +0100 Subject: [PATCH 053/161] [LOG] add filter --- src/src/WebServer/Log.cpp | 2 ++ static/fetch_and_parse_log.js | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/src/WebServer/Log.cpp b/src/src/WebServer/Log.cpp index 75d7fe265..1b4682d56 100644 --- a/src/src/WebServer/Log.cpp +++ b/src/src/WebServer/Log.cpp @@ -6,6 +6,7 @@ #include "../WebServer/JSON.h" #include "../WebServer/Markup.h" #include "../WebServer/Markup_Buttons.h" +#include "../WebServer/Markup_Forms.h" #include "../DataStructs/LogBuffer.h" #include "../DataStructs/TimingStats.h" @@ -35,6 +36,7 @@ void handle_log() { "

")); addHtml(F("Autoscroll: ")); addCheckBox(F("autoscroll"), true); + addFormTextBox(F("Filter"), F("logfilter"), "", 30); addHtml(F("
")); serve_JS(JSfiles_e::FetchAndParseLog); diff --git a/static/fetch_and_parse_log.js b/static/fetch_and_parse_log.js index 647842ee5..c1a0f7b85 100644 --- a/static/fetch_and_parse_log.js +++ b/static/fetch_and_parse_log.js @@ -1,5 +1,5 @@ function elId(e) { - return document.getElementById(e); + return document.getElementById(e); } function getBrowser() { var ua = navigator.userAgent, @@ -96,6 +96,7 @@ function loopDeLoop(timeForNext, activeRequests) { elId(ct1).innerHTML = ''; } elId(ct1).innerHTML += logEntriesChunk; + applyLogFilter(); } logEntriesChunk = ''; autoscroll_on = elId('autoscroll').checked; @@ -121,4 +122,27 @@ function loopDeLoop(timeForNext, activeRequests) { }; check = 1; }, timeForNext); +} + +function applyLogFilter() { + const filtername = elId('logfilter').value; + + // prepare filters: "string1;string2" + const filters = filtername + .split(';') + .map(s => s.trim().toLowerCase()) + .filter(Boolean); + + const container = document.getElementById('copyText_1'); + const entries = container.querySelectorAll('div'); + + entries.forEach(entry => { + const text = entry.textContent.toLowerCase(); + + const matches = + filters.length === 0 || filters.some(f => text.includes(f)); + + // show only matching entries + entry.style.display = matches ? '' : 'none'; + }); } \ No newline at end of file From 6c5ce7e8f8cdf377a8b5982442f8e5f9ba21cf26 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:33:30 +0100 Subject: [PATCH 054/161] [LOG] refining the filter - Added highlight filter phrases - Added ordered AND operator (&) - Added exclusion (!) --- src/src/CustomBuild/define_plugin_sets.h | 3 + src/src/Static/WebStaticData.h | 77 +++++++++++-- static/fetch_and_parse_log.js | 138 ++++++++++++++++++++--- static/github_clipboard.js | 11 +- 4 files changed, 201 insertions(+), 28 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 14983ba0a..79b90d6b6 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -464,6 +464,9 @@ To create/register a plugin, you have to : #ifndef FEATURE_RULES_EASY_COLOR_CODE #define FEATURE_RULES_EASY_COLOR_CODE 1 #endif + #ifndef WEBSERVER_INCLUDE_JS + #define WEBSERVER_INCLUDE_JS + #endif #endif #if FEATURE_FHEM diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index a0995dbed..f5ea29d48 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -341,12 +341,12 @@ static const char jsClipboardCopyPart2[] PROGMEM = { // "if(t==null){i= max_loop + 1;" "if(t==null){i=101;" "}else{" - "cb+=t.innerHTML.replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'" + "const visibleLines=Array.from(t.children).filter((e=>null!==e.offsetParent)).map((e=>e.innerHTML));visibleLines.length>0&&(cb+=visibleLines.join('\\n').replace(/<[Bb][Rr]\\s*\\/?>/gim,'\\n')+'" }; //Fix HTML static const char jsClipboardCopyPart3[] PROGMEM = { - "';}}" + "');}}" "cb=cb.replace(/<\\/[Dd][Ii][Vv]\\s*\\/?>/gim,'\\n');" "cb=cb.replace(/<[^>]*>/gim,'');" "var ti=document.createElement('textarea');" @@ -405,14 +405,71 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { - "function elId(e){return document.getElementById(e)}" - "function getBrowser(){var e,o=navigator.userAgent,r=o.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];return/trident/i.test(r[1])?{name:'IE',version:(e=/\brv[ :]+(\\d+)/g.exec(o)||[])[1]||''}:'Chrome'===r[1]&&null!=(e=o.match(/\bOPR|Edge\\/(\\d+)/))?{name:'Opera',version:e[1]}:(r=r[2]?[r[1],r[2]]:[navigator.appName,navigator.appVersion,'-?'],null!=(e=o.match(/version\\/(\\d+)/i))&&r.splice(1,1,e[1]),{name:r[0],version:r[1]})}" - "var browser=getBrowser(),currentBrowser=browser.name+browser.version;textToDisplay=(browser.name=browser.version<12)?'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':'Fetching log entries...',elId('copyText_1').innerHTML=textToDisplay,loopDeLoop(1e3,0);var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];" - "function loopDeLoop(e,o){let r='copyText_1';isNaN(o)&&(o=1),null==e&&(e=1e3),scrolling_type=e<=500?'auto':'smooth';var n,t,i='',l=0," - "s=setInterval(function(){if(l>0){clearInterval(s);return}++o>1?l=1:fetch('/logjson').then(function(o){if(200!==o.status){console.log('Looks like there was a problem. Status Code: '+o.status);return}o.json()" - ".then(function(o){var l;for(null==t&&(t=''),n=0;n'+o.Log.Entries[n].timestamp+': '+o.Log.Entries[n].text+'')}e=o.Log.TTL,''!==t&&('Fetching log entries...'==elId(r).innerHTML&&(elId(r).innerHTML=''),elId(r).innerHTML+=t),t='',!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type}),elId('current_loglevel').innerHTML='Logging: '+logLevel[o.Log.SettingsWebLogLevel]+' ('+o.Log.SettingsWebLogLevel+')',clearInterval(s)," - "loopDeLoop(e,0)})}).catch(function(o){elId(r).innerHTML+='
>> '+o.message+' <<
',autoscroll_on=elId('autoscroll').checked,elId(r).scrollTop=elId(r).scrollHeight,e=5e3,clearInterval(s)," - "loopDeLoop(e,0)}),l=1},e)}" +"function elId(e){return document.getElementById(e)}" +"function getBrowser(){var e,t=navigator.userAgent,o=t.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];" +"return/trident/i.test(o[1])?{name:'IE',version:(e=/\\brv[ :]+(\\d+)/g.exec(t)||[])[1]||''}:" +"'Chrome'===o[1]&&null!=(e=t.match(/\\bOPR|Edge\\/(\\d+)/))?" +"{name:'Opera',version:e[1]}:" +"(o=o[2]?[o[1],o[2]]:[navigator.appName,navigator.appVersion,'-?']," +"null!=(e=t.match(/version\\/(\\d+)/i))&&o.splice(1,1,e[1]),{name:o[0],version:o[1]})}" +"var browser=getBrowser(),currentBrowser=browser.name+browser.version;" +"textToDisplay=(browser.name=browser.version<12)?" +"'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':" +"'Fetching log entries...';" +"elId('copyText_1').innerHTML=textToDisplay;" +"loopDeLoop(1000,0);" +"var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];" +"function loopDeLoop(e,t){let o='copyText_1';" +"isNaN(t)&&(t=1),null==e&&(e=1000),scrolling_type=e<=500?'auto':'smooth';" +"var r,n,i='',l=0,s=setInterval(function(){" +"if(l>0){clearInterval(s);return}" +"++t>1?l=1:fetch('/logjson').then(function(t){" +"if(200!==t.status){console.log('Looks like there was a problem. Status Code: '+t.status);return}" +"t.json().then(function(t){var l;" +"for(null==n&&(n=''),r=0;r'+t.Log.Entries[r].timestamp+': '+" +"t.Log.Entries[r].text+'')}" +"e=t.Log.TTL," +"''!==n&&('Fetching log entries...'==elId(o).innerHTML&&(elId(o).innerHTML='')," +"elId(o).innerHTML+=n)," +"n='';" +"!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type});" +"applyLogFilter();" +"elId('current_loglevel').innerHTML='Logging: '+logLevel[t.Log.SettingsWebLogLevel]+' ('+t.Log.SettingsWebLogLevel+')';" +"clearInterval(s);loopDeLoop(e,0)" +"})}).catch(function(t){" +"elId(o).innerHTML+='
>> '+t.message+' <<
';" +"autoscroll_on=elId('autoscroll').checked;" +"elId(o).scrollTop=elId(o).scrollHeight;" +"e=5000;clearInterval(s);loopDeLoop(e,0)" +"}),l=1},e)}" +"function applyLogFilter(){let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean)," +"t=document.querySelectorAll('#copyText_1 > div')," +"o=e.some(e=>!e.startsWith('!'));" +"for(let r of t){r.dataset.originalHtml||(r.dataset.originalHtml=r.innerHTML);" +"let n=r.dataset.originalHtml,i=r.textContent.toLowerCase(),l=[],s=!1,a=!o;" +"for(let d of e){" +"if(d.startsWith('!')){let c=d.slice(1);" +"if(!c.includes('&')||c.startsWith('&')||c.endsWith('&')){" +"if(c&&i.includes(c.toLowerCase())){s=!0;break}}else{" +"let g=c.split('&').map(e=>e.trim()).filter(Boolean),f=-1,u=!0;" +"for(let p of g){let L=i.indexOf(p.toLowerCase(),f+1);if(-1===L){u=!1;break}f=L}if(u){s=!0;break}}continue}" +"if(d.includes('&')&&!d.startsWith('&')&&!d.endsWith('&')){" +"let h=d.split('&').map(e=>e.trim()).filter(Boolean);" +"if(h.length>=2){let m=-1,$=!0;for(let _ of h){let b=i.indexOf(_.toLowerCase(),m+1);if(-1===b){$=!1;break}m=b}if($){l.push(...h),a=!0;break}}continue}" +"if(i.includes(d.toLowerCase())){l.push(d),a=!0;break}}" +"if(s||!a){r.style.display='none';continue}r.style.display='';r.innerHTML=highlightOutsideTags(n,l)}}" +"function highlightOutsideTags(e,t){if(!t.length)return e;" +"let o='',r=!1,n=0;" +"for(;n'===i){r=!1;o+=i;n++;continue}" +"if(!r){let l=!1;for(let s of t){let a=e.slice(n,n+s.length);" +"if(a.toLowerCase()===s.toLowerCase()){o+=`${a}`;n+=s.length;l=!0;break}}if(l)continue}o+=i;n++}return o}" +"const input=document.getElementById('logfilter');" +"input&&(input.placeholder='\"!\"=exclude \";\"=or \"&\"=and (e.g. !act)');" }; #endif // WEBSERVER_INCLUDE_JS diff --git a/static/fetch_and_parse_log.js b/static/fetch_and_parse_log.js index c1a0f7b85..db2bb6577 100644 --- a/static/fetch_and_parse_log.js +++ b/static/fetch_and_parse_log.js @@ -96,7 +96,6 @@ function loopDeLoop(timeForNext, activeRequests) { elId(ct1).innerHTML = ''; } elId(ct1).innerHTML += logEntriesChunk; - applyLogFilter(); } logEntriesChunk = ''; autoscroll_on = elId('autoscroll').checked; @@ -105,6 +104,7 @@ function loopDeLoop(timeForNext, activeRequests) { behavior: scrolling_type }); } + applyLogFilter(); elId('current_loglevel').innerHTML = 'Logging: ' + logLevel[data.Log.SettingsWebLogLevel] + ' (' + data.Log.SettingsWebLogLevel + ')'; clearInterval(i); loopDeLoop(timeForNext, 0); @@ -125,24 +125,128 @@ function loopDeLoop(timeForNext, activeRequests) { } function applyLogFilter() { - const filtername = elId('logfilter').value; + const filterGroups = elId('logfilter').value + .split(';').map(s => s.trim()).filter(Boolean); - // prepare filters: "string1;string2" - const filters = filtername - .split(';') - .map(s => s.trim().toLowerCase()) - .filter(Boolean); + const entries = document.querySelectorAll('#copyText_1 > div'); + const hasPositiveFilter = filterGroups.some(g => !g.startsWith('!')); - const container = document.getElementById('copyText_1'); - const entries = container.querySelectorAll('div'); + for (const entry of entries) { + if (!entry.dataset.originalHtml) entry.dataset.originalHtml = entry.innerHTML; + let html = entry.dataset.originalHtml; - entries.forEach(entry => { - const text = entry.textContent.toLowerCase(); + const textLower = entry.textContent.toLowerCase(); + let matched = []; + let hiddenByExclusion = false; - const matches = - filters.length === 0 || filters.some(f => text.includes(f)); + let isMatch = !hasPositiveFilter; - // show only matching entries - entry.style.display = matches ? '' : 'none'; - }); -} \ No newline at end of file + for (const group of filterGroups) { + + // exclusion rule with ordered AND + if (group.startsWith('!')) { + const g = group.slice(1); + + if (g.includes('&') && !g.startsWith('&') && !g.endsWith('&')) { + const parts = g.split('&').map(s => s.trim()).filter(Boolean); + let lastIndex = -1; + let ok = true; + + for (const p of parts) { + const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1); + if (idx === -1) { + ok = false; + break; + } + lastIndex = idx; + } + + if (ok) { + hiddenByExclusion = true; + break; + } + } else { + if (g && textLower.includes(g.toLowerCase())) { + hiddenByExclusion = true; + break; + } + } + continue; + } + + // ordered AND: a&b&c + if (group.includes('&') && !group.startsWith('&') && !group.endsWith('&')) { + const parts = group.split('&').map(s => s.trim()).filter(Boolean); + if (parts.length >= 2) { + let lastIndex = -1; + let ok = true; + + for (const p of parts) { + const idx = textLower.indexOf(p.toLowerCase(), lastIndex + 1); + if (idx === -1) { + ok = false; + break; + } + lastIndex = idx; + } + if (ok) { + matched.push(...parts); + isMatch = true; + break; + } + } + continue; + } + // normal include + if (textLower.includes(group.toLowerCase())) { + matched.push(group); + isMatch = true; + break; + } + } + if (hiddenByExclusion || !isMatch) { + entry.style.display = 'none'; + continue; + } + entry.style.display = ''; + entry.innerHTML = highlightOutsideTags(html, matched); + } +} + +function highlightOutsideTags(html, terms) { + if (!terms.length) return html; + let result = ''; + let insideTag = false; + let i = 0; + + while (i < html.length) { + const char = html[i]; + if (char === '<') insideTag = true; + if (char === '>') { + insideTag = false; + result += char; + i++; + continue; + } + + if (!insideTag) { + let matched = false; + for (const term of terms) { + const slice = html.slice(i, i + term.length); + if (slice.toLowerCase() === term.toLowerCase()) { + result += `${slice}`; + i += term.length; + matched = true; + break; + } + } + if (matched) continue; + } + result += char; + i++; + } + return result; +} + +const input = document.getElementById("logfilter"); +input && (input.placeholder = '"!"=exclude ";"=or "&"=and (e.g. !act)'); \ No newline at end of file diff --git a/static/github_clipboard.js b/static/github_clipboard.js index bb2c1a8f8..a8aab1818 100644 --- a/static/github_clipboard.js +++ b/static/github_clipboard.js @@ -11,7 +11,16 @@ function setGithubClipboard() { if (i % 2 == 0) { separatorSymbol += '\n' } - clipboard += test.innerHTML.replace(/<[Bb][Rr]\s*\/?>/gim, '\n') + separatorSymbol; + // collect only visible log entry divs + const visibleLines = Array.from(test.children) + .filter(div => div.offsetParent !== null) // visible only + .map(div => div.innerHTML); + + if (visibleLines.length > 0) { + clipboard += + visibleLines.join('\n').replace(/<[Bb][Rr]\s*\/?>/gim, '\n') + + separatorSymbol; + } } } clipboard = clipboard.replace(/<\/[Dd][Ii][Vv]\s*\/?>/gim, '\n'); From c5f06cc6ea5031490306fdb6d4c2c5f036cad3d0 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Sat, 24 Jan 2026 12:49:27 +0100 Subject: [PATCH 055/161] [LOG] revert changes for plugin_sets --- src/src/CustomBuild/define_plugin_sets.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 79b90d6b6..14983ba0a 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -464,9 +464,6 @@ To create/register a plugin, you have to : #ifndef FEATURE_RULES_EASY_COLOR_CODE #define FEATURE_RULES_EASY_COLOR_CODE 1 #endif - #ifndef WEBSERVER_INCLUDE_JS - #define WEBSERVER_INCLUDE_JS - #endif #endif #if FEATURE_FHEM From 51327a7e7136b45fd9ec0f73158e8bf60f147379 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 25 Jan 2026 01:55:17 +0100 Subject: [PATCH 056/161] Update README.md --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 15dca94db..2618dd9e7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,20 @@ For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above. + +# ESPEasy, Automate (using) Common Sense, No AI. + +The main objective for ESPEasy is to make people realize they can easily control appliances and act on sensor data. + +Anyone can use it and you don't need AI. +Only requirement is common sense and appreciate the satisfaction of seeing things just work. + +If you can't build it yourself, you don't 'own' it. +So let's make sure YOU control it. + +We are more than willing to assist via our [forum](https://www.letscontrolit.com/forum). + + # ESPEasy (development branch) From 570155347eec64979f4b1dda847c53abcc6d1093 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:18:39 +0100 Subject: [PATCH 057/161] [LOG] add filter field for older builds - Added a check to determine if there is already a filter field present; otherwise, add one for older builds. --- src/9Custom.h | 607 ++++++++++++++++++++++++++++++++++ static/fetch_and_parse_log.js | 6 + tools/pio/pre_custom_esp32.py | 14 +- 3 files changed, 620 insertions(+), 7 deletions(-) create mode 100644 src/9Custom.h diff --git a/src/9Custom.h b/src/9Custom.h new file mode 100644 index 000000000..e4592e652 --- /dev/null +++ b/src/9Custom.h @@ -0,0 +1,607 @@ +#ifndef ESPEASY_CUSTOM_H +#define ESPEASY_CUSTOM_H + +/* + To modify the stock configuration without changing the EspEasy.ino file : + 1) rename this file to "Custom.h" (It is ignored by Git) + 2) define your own settings below + 3) Build one of the environments with Custom in their name, they will automatically use this file if it exists + */ + + +/* + ####################################################################################################### + Your Own Default Settings + ####################################################################################################### + You can basically ovveride ALL macro defined in ESPEasy.ino. + Don't forget to first #undef each existing #define that you add below. + But since this Custom.h is included before other defines are made, you don't have to undef a lot of defines. + Here are some examples: + */ + +// --- Feature Flagging --------------------------------------------------------- +// Can be set to 1 to enable, 0 to disable, or not set to use the default (usually via define_plugin_sets.h) + +#define FEATURE_RULES_EASY_COLOR_CODE 1 // Use code highlighting, autocompletion and command suggestions in Rules +#define FEATURE_ESPEASY_P2P 1 // (1/0) enables the ESP Easy P2P protocol +#define FEATURE_ARDUINO_OTA 1 // enables the Arduino OTA capabilities +#define FEATURE_THINGSPEAK_EVENT 1 // Generates an event when requesting last value of a field in thingspeak via SendToHTTP(e.g. sendToHTTP,api.thingspeak.com,80,/channels/1667332/fields/5/last) +#define FEATURE_OPENMETEO_EVENT 1 // Generates an event with the response of a open-meteo request (https://open-meteo.com/en/docs) +#define FEATURE_JSON_EVENT 1 // Generates an event with the values of a JSON repsonse of an HTTP call. Keys are stored in json.keys one key per line (e.g.: Body.Data.DAY_ENERGY.Values.1) +// #define FEATURE_SD 1 // Enable SD card support +// #define FEATURE_DOWNLOAD 1 // Enable downloading a file from an url + +#ifdef BUILD_GIT +# undef BUILD_GIT +#endif // ifdef BUILD_GIT + +#define BUILD_GIT "My Build: " __DATE__ " " __TIME__ + + +#define DEFAULT_NAME "MyEspEasyDevice" // Enter your device friendly name +#define UNIT 0 // Unit Number +#define DEFAULT_DELAY 60 // Sleep Delay in seconds + +// --- Wifi AP Mode (when your Wifi Network is not reachable) ---------------------------------------- +#define DEFAULT_AP_IP 192, 168, 4, 1 // Enter IP address (comma separated) for AP (config) mode +#define DEFAULT_AP_SUBNET 255, 255, 255, 0 // Enter IP address (comma separated) for AP (config) mode +#define DEFAULT_AP_KEY "configesp" // Enter network WPA key for AP (config) mode + +// --- Wifi Client Mode ----------------------------------------------------------------------------- +#define DEFAULT_SSID "sofu" // Enter your network SSID +#define DEFAULT_KEY "nototrrentplease" // Enter your network WPA key +#define DEFAULT_SSID2 "" // Enter your fallback network SSID +#define DEFAULT_KEY2 "" // Enter your fallback network WPA key +#define DEFAULT_WIFI_INCLUDE_HIDDEN_SSID false // Allow to connect to hidden SSID APs +#define DEFAULT_USE_STATIC_IP false // (true|false) enabled or disabled static IP +#define DEFAULT_IP "192.168.0.50" // Enter your IP address +#define DEFAULT_DNS "192.168.0.1" // Enter your DNS +#define DEFAULT_GW "192.168.0.1" // Enter your Gateway +#define DEFAULT_SUBNET "255.255.255.0" // Enter your Subnet +#define DEFAULT_IPRANGE_LOW "0.0.0.0" // Allowed IP range to access webserver +#define DEFAULT_IPRANGE_HIGH "255.255.255.255" // Allowed IP range to access webserver +#define DEFAULT_IP_BLOCK_LEVEL 1 // 0: ALL_ALLOWED 1: LOCAL_SUBNET_ALLOWED 2: +// ONLY_IP_RANGE_ALLOWED +#define DEFAULT_ADMIN_USERNAME "admin" +#define DEFAULT_ADMIN_PASS "" + +#define DEFAULT_WIFI_CONNECTION_TIMEOUT 10000 // minimum timeout in ms for WiFi to be connected. +#define DEFAULT_WIFI_FORCE_BG_MODE false // when set, only allow to connect in 802.11B or G mode (not N) +#define DEFAULT_WIFI_RESTART_WIFI_CONN_LOST false // Perform wifi off and on when connection was lost. +#define DEFAULT_ECO_MODE false // When set, make idle calls between executing tasks. +#define DEFAULT_WIFI_NONE_SLEEP false // When set, the wifi will be set to no longer sleep (more power +// used and need reboot to reset mode) +#define DEFAULT_GRATUITOUS_ARP false // When set, the node will send periodical gratuitous ARP + // packets to announce itself. +#define DEFAULT_TOLERANT_LAST_ARG_PARSE false // When set, the last argument of some commands will be parsed to the end of the line + // See: https://github.com/letscontrolit/ESPEasy/issues/2724 +#define DEFAULT_SEND_TO_HTTP_ACK false // Wait for ack with SendToHttp command. + +#define DEFAULT_AP_DONT_FORCE_SETUP false // Allow optional usage of Sensor without WIFI avaiable // When set you can use the Sensor in AP-Mode without beeing forced to /setup +#define DEFAULT_DONT_ALLOW_START_AP false // Usually the AP will be started when no WiFi is defined, or the defined one cannot be found. This flag may prevent it. + +// --- Default Controller ------------------------------------------------------------------------------ +#define DEFAULT_CONTROLLER false // true or false enabled or disabled, set 1st controller + // defaults +#define DEFAULT_CONTROLLER_ENABLED true // Enable default controller by default +#define DEFAULT_CONTROLLER_USER "" // Default controller user +#define DEFAULT_CONTROLLER_PASS "" // Default controller Password + +// using a default template, you also need to set a DEFAULT PROTOCOL to a suitable MQTT protocol ! +#define DEFAULT_PUB "sensors/espeasy/%sysname%/%tskname%/%valname%" // Enter your pub +#define DEFAULT_SUB "sensors/espeasy/%sysname%/#" // Enter your sub +#define DEFAULT_SERVER "192.168.0.8" // Enter your Server IP address +#define DEFAULT_SERVER_HOST "" // Server hostname +#define DEFAULT_SERVER_USEDNS false // true: Use hostname. false: use IP +#define DEFAULT_USE_EXTD_CONTROLLER_CREDENTIALS false // true: Allow longer user credentials for controllers + +#define DEFAULT_PORT 8080 // Enter your Server port value +#define DEFAULT_CONTROLLER_TIMEOUT 100 // Default timeout in msec + +#define DEFAULT_PROTOCOL 0 // Protocol used for controller communications + // 0 = Stand-alone (no controller set) + // 1 = Domoticz HTTP + // 2 = Domoticz MQTT + // 3 = Nodo Telnet + // 4 = ThingSpeak + // 5 = Home Assistant (openHAB) MQTT + // 6 = PiDome MQTT + // 7 = EmonCMS + // 8 = Generic HTTP + // 9 = FHEM HTTP + +#ifdef ESP8266 +#define DEFAULT_PIN_I2C_SDA 4 +#endif +#ifdef ESP32 +#define DEFAULT_PIN_I2C_SDA -1 // Undefined +#endif +#ifdef ESP8266 +#define DEFAULT_PIN_I2C_SCL 5 +#endif +#ifdef ESP32 +#define DEFAULT_PIN_I2C_SCL -1 // Undefined +#endif +#define DEFAULT_PIN_I2C2_SDA -1 // Undefined +#define DEFAULT_PIN_I2C3_SDA -1 // Undefined +#define DEFAULT_PIN_I2C2_SCL -1 // Undefined +#define DEFAULT_PIN_I2C3_SCL -1 // Undefined +#define DEFAULT_I2C_CLOCK_SPEED 400000 // Use 100 kHz if working with old I2C chips +#define FEATURE_I2C_DEVICE_SCAN 1 + +#define DEFAULT_SPI 0 //0=disabled 1=enabled and for ESP32 there is option 2 =HSPI + +#define DEFAULT_PIN_STATUS_LED (-1) +#define DEFAULT_PIN_STATUS_LED_INVERSED true + +#define DEFAULT_PIN_RESET_BUTTON (-1) + + +#define DEFAULT_USE_RULES false // (true|false) Enable Rules? +#define DEFAULT_RULES_OLDENGINE true + +#define DEFAULT_MQTT_RETAIN false // (true|false) Retain MQTT messages? +#define DEFAULT_CONTROLLER_DELETE_OLDEST false // (true|false) to delete oldest message when queue is full +#define DEFAULT_CONTROLLER_MUST_CHECK_REPLY false // (true|false) Check Acknowledgment +#define DEFAULT_MQTT_DELAY 100 // Time in milliseconds to retain MQTT messages +#define DEFAULT_MQTT_LWT_TOPIC "" // Default lwt topic +#define DEFAULT_MQTT_LWT_CONNECT_MESSAGE "Connected" // Default lwt message +#define DEFAULT_MQTT_LWT_DISCONNECT_MESSAGE "Connection Lost" // Default lwt message +#define DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID 0 + +#define DEFAULT_USE_NTP false // (true|false) Use NTP Server +#define DEFAULT_NTP_HOST "" // NTP Server Hostname +#define DEFAULT_TIME_ZONE 0 // Time Offset (in minutes) +#define DEFAULT_USE_DST false // (true|false) Use Daily Time Saving + +#define DEFAULT_LATITUDE 0.0f // Default Latitude +#define DEFAULT_LONGITUDE 0.0f // Default Longitude + +#define DEFAULT_SYSLOG_IP "" // Syslog IP Address +#define DEFAULT_SYSLOG_PORT 0 // Standard syslog port: 514 +#define DEFAULT_SYSLOG_FACILITY 0 // kern +#define DEFAULT_SYSLOG_LEVEL 0 // Syslog Log Level +#define DEFAULT_SERIAL_LOG_LEVEL LOG_LEVEL_INFO // Serial Log Level +#define DEFAULT_WEB_LOG_LEVEL LOG_LEVEL_INFO // Web Log Level +#define DEFAULT_SD_LOG_LEVEL 0 // SD Card Log Level +#define DEFAULT_USE_SD_LOG false // (true|false) Enable Logging to the SD card + +#define DEFAULT_USE_SERIAL true // (true|false) Enable Logging to the Serial Port +#define DEFAULT_SERIAL_BAUD 115200 // Serial Port Baud Rate + +#define DEFAULT_SYNC_UDP_PORT 8266 // Used for ESPEasy p2p. (IANA registered port: 8266) + + +// Factory Reset defaults +#define DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME true +#define DEFAULT_FACTORY_RESET_KEEP_WIFI true +#define DEFAULT_FACTORY_RESET_KEEP_NETWORK true +#define DEFAULT_FACTORY_RESET_KEEP_NTP_DST true +#define DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG true + + +#define BUILD_NO_DEBUG + +// Custom built-in url for hosting JavaScript and CSS files. +#define CUSTOM_BUILD_CDN_URL "https://cdn.jsdelivr.net/gh/letscontrolit/ESPEasy@mega/static/" + + + +// Special SSID/key setup only to be used in custom builds. + +// Deployment SSID will be used only when the configured SSIDs are not reachable and/or no credentials are set. +// This to make deployment of large number of nodes easier +#define CUSTOM_DEPLOYMENT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease deployment +#define CUSTOM_DEPLOYMENT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease deployment +#define CUSTOM_SUPPORT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease support +#define CUSTOM_SUPPORT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease support + + +// Emergency fallback SSID will only be attempted in the first 10 minutes after reboot. +// When found, the unit will connect to it and depending on the built in flag, it will either just connect to it, or clear set credentials. +// Use case: User connects to a public AP which does need to agree on an agreement page for the rules of conduct (e.g. open APs) +// This is seen as a valid connection, so the unit will not reconnect to another node and thus becomes inaccessible. +#define CUSTOM_EMERGENCY_FALLBACK_SSID "" // Enter SSID not shown in UI, to be used to regain access to the node +#define CUSTOM_EMERGENCY_FALLBACK_KEY "" // Enter key not shown in UI, to be used to regain access to the node + +#define CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS false +#define CUSTOM_EMERGENCY_FALLBACK_START_AP false + +#define CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME 10 + +// Allow for remote provisioning of a node. +// This is only allowed for custom builds. +// To setup the configuration of the provisioning file, one must also define FEATURE_SETTINGS_ARCHIVE +// Default setting is to not allow to configure a node remotely, unless explicitly enabled. +// #define FEATURE_CUSTOM_PROVISIONING 1 + +#define FEATURE_SSDP 1 + +#define FEATURE_EXT_RTC 1 // Support for external RTC clock modules like PCF8563/PCF8523/DS3231/DS1307 + +#define FEATURE_PLUGIN_STATS 1 // Support collecting historic data + computing stats on historic data +#ifdef ESP8266 +// # define PLUGIN_STATS_NR_ELEMENTS 16 +#endif // ifdef ESP8266 +# ifdef ESP32 +// # define PLUGIN_STATS_NR_ELEMENTS 64 +#endif // ifdef ESP32 +#define FEATURE_CHART_JS 1 // Support for drawing charts, like PluginStats historic data + +// Optional alternative CDN links: +// Chart.js: (only used when FEATURE_CHART_JS is enabled) +// #define CDN_URL_CHART_JS "https://cdn.jsdelivr.net/npm/chart.js@4.1.2/dist/chart.umd.min.js" +// JQuery: +// #define CDN_URL_JQUERY "https://code.jquery.com/jquery-3.6.0.min.js" + + +// #define FEATURE_SETTINGS_ARCHIVE 1 +// #define FEATURE_I2CMULTIPLEXER 1 +// #define FEATURE_TRIGONOMETRIC_FUNCTIONS_RULES 1 +// #define PLUGIN_USES_ADAFRUITGFX // Used by Display plugins using Adafruit GFX library +// #define ADAGFX_ARGUMENT_VALIDATION 0 // Disable argument validation in AdafruitGFX_helper +// #define ADAGFX_SUPPORT_7COLOR 0 // Disable the support of 7-color eInk displays by AdafruitGFX_helper +// #define FEATURE_SEND_TO_HTTP 1 // Enable availability of the SendToHTTP command +// #define FEATURE_POST_TO_HTTP 1 // Enable availability of the PostToHTTP command +// #define FEATURE_PUT_TO_HTTP 1 // Enable availability of the PutToHTTP command +// #define FEATURE_I2C_DEVICE_CHECK 0 // Disable the I2C Device check feature +// #define FEATURE_I2C_GET_ADDRESS 0 // Disable fetching the I2C address from I2C plugins. Will be enabled when FEATURE_I2C_DEVICE_CHECK is enabled +// #define FEATURE_RTTTL 1 // Enable rtttl command +// #define FEATURE_ANYRTTTL_LIB 1 // Use AnyRttl library for RTTTL handling +// #define FEATURE_ANYRTTTL_ASYNC 1 // When AnyRttl enabled, use Async (nonblocking) mode instead of the default Blocking mode +// #define FEATURE_RTTTL_EVENTS 1 // Enable RTTTL events for Async use, for blocking it doesn't make sense +// #define FEATURE_STRING_VARIABLES 1 // Enable String variable support (enabled on ESP32, NOT supported on ESP8266 for memory restrictions!) + +#if FEATURE_CUSTOM_PROVISIONING +// For device models, see src/src/DataTypes/DeviceModel.h +// #ifdef ESP32 +// #define DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL 0 // DeviceModel_default +// #endif +// #ifdef ESP8266 +// #define DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL 0 // DeviceModel_default +// #endif +// #define DEFAULT_PROVISIONING_FETCH_RULES1 false +// #define DEFAULT_PROVISIONING_FETCH_RULES2 false +// #define DEFAULT_PROVISIONING_FETCH_RULES3 false +// #define DEFAULT_PROVISIONING_FETCH_RULES4 false +// #define DEFAULT_PROVISIONING_FETCH_NOTIFICATIONS false +// #define DEFAULT_PROVISIONING_FETCH_SECURITY false +// #define DEFAULT_PROVISIONING_FETCH_CONFIG false +// #define DEFAULT_PROVISIONING_FETCH_PROVISIONING false +// #define DEFAULT_PROVISIONING_FETCH_FIRMWARE false +// #define DEFAULT_PROVISIONING_SAVE_URL false +// #define DEFAULT_PROVISIONING_SAVE_CREDENTIALS false +// #define DEFAULT_PROVISIONING_ALLOW_FETCH_COMMAND false +// #define DEFAULT_PROVISIONING_URL "" +// #define DEFAULT_PROVISIONING_USER "" +// #define DEFAULT_PROVISIONING_PASS "" +#endif + + + + +/* + ####################################################################################################### + Defining web interface + ####################################################################################################### + */ + +#define MENU_INDEX_MAIN_VISIBLE true +/* +#define MENU_INDEX_CONFIG_VISIBLE false +#define MENU_INDEX_CONTROLLERS_VISIBLE false +#define MENU_INDEX_HARDWARE_VISIBLE false +#define MENU_INDEX_DEVICES_VISIBLE false +#define MENU_INDEX_RULES_VISIBLE false +#define MENU_INDEX_NOTIFICATIONS_VISIBLE false +#define MENU_INDEX_TOOLS_VISIBLE false +*/ + +#define MAIN_PAGE_SHOW_SYSINFO_BUTTON true +#define MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON true +#define MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN false + +#define MAIN_PAGE_SHOW_NODE_LIST_BUILD true +#define MAIN_PAGE_SHOW_NODE_LIST_TYPE true + +#define SETUP_PAGE_SHOW_CONFIG_BUTTON true + +// #define FEATURE_AUTO_DARK_MODE 0 // 0 = Disable auto-dark mode +// #define FEATURE_EXTENDED_TASK_VALUE_TYPES 0 // 0 = Disable extra task value types like 64 bit ints, double, etc. in Dummy tasks +// #define FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE 0 // 0 = switch to float as floating point type for rules/formula processing. + +//#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON + +#define SHOW_SYSINFO_JSON 1 //Enables the sysinfo_json page (by default is enabled when WEBSERVER_NEW_UI is enabled too) + +/* + ####################################################################################################### + CSS / template + ####################################################################################################### + */ +/* +#define WEBPAGE_TEMPLATE_DEFAULT_HEADER "

ESP Easy Mega: {{title}}


" +#define WEBPAGE_TEMPLATE_DEFAULT_FOOTER "
" +#define WEBPAGE_TEMPLATE_AP_HEADER "

Welcome to ESP Easy Mega AP

" +#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON +*/ +// Embed Custom CSS in Custom.h: +/* +#define WEBSERVER_EMBED_CUSTOM_CSS +static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { +... +,0}; +*/ + + +/* + ####################################################################################################### + Special settings (rendering settings incompatible with other builds) + ####################################################################################################### + */ + +// #define FEATURE_NON_STANDARD_24_TASKS 1 + +/* + ####################################################################################################### + Your Own selection of plugins and controllers + ####################################################################################################### + */ + +#define CONTROLLER_SET_NONE +#define NOTIFIER_SET_NONE +#define PLUGIN_SET_NONE + + +/* + ####################################################################################################### + ########### Plugins + ####################################################################################################### + */ + +// #define FEATURE_SERVO 1 // Uncomment and set to 0 to explicitly disable SERVO support + + +#define USES_P001 // Switch +#define USES_P002 // ADC +// #define USES_P003 // Pulse +#define USES_P004 // 1-Wire Temperature (Dallas/Maxim DS18B20) +// #define USES_P005 // DHT11/12/22 SONOFF2301/7021/MS01 +// #define USES_P006 // BMP085/180 +// #define USES_P007 // PCF8591 +// #define USES_P008 // Wiegand (RFID) +// #define USES_P009 // MCP23017 + +// #define USES_P010 // BH1750 +// #define USES_P011 // ProMini Extender +// #define USES_P012 // LCD2004 +// #define USES_P013 // HC-SR04/RCW-0001 +#define USES_P014 // SI70xx/HTU21D +// #define USES_P015 // TSL2561 +// #define USES_P017 // PN532 +// #define USES_P018 // GP2Y10 +// #define USES_P019 // PCF8574 + +#define USES_P020 // Ser2Net +#define USES_P021 // Level Control +// #define USES_P022 // PCA9685 +// #define USES_P023 // OLED SSD1306 +// #define USES_P024 // MLX90614 +// #define USES_P025 // ADS1x15 +#define USES_P026 // SysInfo +#define USES_P027 // INA219 +#define USES_P028 // BMx280 +// #define USES_P029 // Domoticz MQTT Helper + +// #define USES_P031 // SHT1x +// #define USES_P032 // MS5611 (GY-63) +#define USES_P033 // Dummy Device +// #define USES_P034 // DHT12 +// #define USES_P036 // OLED SSD1306/SH1106 Framed +// #define P036_FEATURE_DISPLAY_PREVIEW 1 // Enable Preview feature, shows on-display content on Devices overview page +// #define P036_FEATURE_ALIGN_PREVIEW 1 // Enable center/right-align feature when preview is enabled (auto-disabled for 1M builds) +// #define P036_ENABLE_TICKER 1 // Enable ticker function +// #define USES_P037 // MQTT Import +// #define P037_MAPPING_SUPPORT 1 // Enable Value mapping support +// #define P037_FILTER_SUPPORT 1 // Enable filtering support +// #define P037_JSON_SUPPORT 1 // Enable Json support +// #define USES_P038 // NeoPixel +// #define P038_FEATURE_NEOPIXELFOR 1 // Enable NeoPixelFor/NeoPixelForHSV commands (default enabled for ESP32) +// #define USES_P039 // Thermocouple + +// #define USES_P040 // RFID - ID12LA/RDM6300 +// #define USES_P041 // NeoPixel (Word Clock) +// #define USES_P042 // NeoPixel (Candle) +#define USES_P043 // ClkOutput +// #define USES_P044 // P1 Wifi Gateway (Merged with P020, when P044 is enabled, then P020 is also enabled) +// #define USES_P045 // MPU6050 +// #define USES_P046 // Ventus W266 +// #define USES_P047 // Soil moisture sensor +// #define USES_P048 // Motoshield v2 +#define USES_P049 // MH-Z19 + +// #define USES_P050 // TCS34725 RGB Color Sensor with IR filter and White LED +// #define USES_P051 // AM2320 +// #define USES_P052 // SenseAir +// #define USES_P053 // PMSx003 / PMSx003ST +// #define USES_P054 // DMX512 +// #define USES_P055 // Chiming +// #define USES_P056 // SDS011/018/198 +// #define USES_P057 // HT16K33_LED +// #define USES_P058 // HT16K33_KeyPad +// #define USES_P059 // Rotary Encoder + +// #define USES_P060 // MCP3221 +// #define USES_P061 // PCF8574 / MCP23017 / PCA8575 +// #define USES_P062 // MPR121 +// #define USES_P063 // TTP229 +// #define USES_P064 // APDS9960 Gesture +// #define USES_P065 // DRF0299 +// #define USES_P066 // VEML6040 +// #define USES_P067 // HX711_Load_Cell +// #define USES_P068 // SHT3x +// #define USES_P069 // LM75A + +// #define USES_P070 // NeoPixel_Clock +// #define USES_P071 // Kamstrup401 +// #define USES_P072 // HDC1000/HDC1008/HDC1010/HDC1050/HDC1080 +// #define USES_P073 // 7-segment display +// #define USES_P074 // TSL2591 +// #define USES_P075 // Nextion +// #define USES_P076 // HLW8012/BL0937 (Shelly Plug S, Sonoff POW R1, Huafan SS, KMC 70011, Aplic WDP303075, SK03 Outdoor, BlitzWolf SHP, Teckin, Teckin US, Gosund SP1 v23) +// #define USES_P077 // CSE7766 (Sonoff S31, Sonoff POW R2, Sonoff POW R3xx(D), Sonoff Dual R3) +// #define USES_P078 // Eastron SDMxxx Modbus +// #define USES_P079 // Wemos / Lolin Motorshield + +// #define USES_P080 // iButton Sensor DS1990A +#define USES_P081 // Cron +// #define USES_P082 // GPS +// #define USES_P083 // SGP30 TVOC +// #define USES_P084 // VEML6070 +// #define USES_P085 // AcuDC24x +// #define USES_P086 // Receiving values according Homie convention. Works together with C014 Homie controller +#define USES_P087 // Serial Proxy +// #define USES_P088 // HeatpumpIR +// #define USES_P089 // Ping + +// #define USES_P090 // CCS811 TVOC +// #define USES_P091 // Serial MCU controlled switch +// #define USES_P092 // DLbus +// #define USES_P093 // Mitsubishi Heat Pump +// #define USES_P094 // CUL Reader +// #define USES_P095 // ILI934x / ILI948x +// #define USES_P096 // eInk +#define USES_P097 // ESP32 Touch +// #define USES_P098 // PWM Motor +// #define USES_P099 // XPT2046 touchscreen + +// #define USES_P100 // DS2423 counter +// #define USES_P101 // Wake On Lan +// #define USES_P102 // PZEM-004Tv30-Multiple +// #define USES_P103 // Atlas Scientific EZO Sensors (pH, ORP, EZO, DO) +// #define USES_P104 // MAX7219 dot matrix +// #define USES_P105 // AHT10/AHT2x +// #define USES_P106 // BME68x +// #define USES_P107 // SI1145 +// #define USES_P108 // DDS238-x ZN Modbus energy meters +// #define USES_P109 // ThermoOLED + +// #define USES_P110 // VL53L0X Time of Flight sensor +// #define USES_P111 // MFRC522 RFID reader +// #define USES_P112 // AS7265x +// #define USES_P113 // VL53L1X ToF +// #define USES_P114 // VEML6075 +// #define USES_P115 // MAX1704x +//#define USES_P116 // ST77xx +// #define USES_P117 // SCD30 +// #define USES_P118 // Itho +// #define USES_P119 // ITG3205 Gyro + +// #define USES_P120 // ADXL345 I2C Acceleration / Gravity +// #define USES_P121 // HMC5883L +// #define USES_P122 // SHT2x +// #define USES_P123 // I2C Touchscreens +// #define USES_P124 // I2C Multi Relay +// #define USES_P125 // ADXL345 SPI Acceleration / Gravity +// #define USES_P126 // 74HC595 Shift register +// #define USES_P127 // CDM7160 +// #define USES_P128 // NeoPixel (BusFX) +// #define P128_USES_GRB // Default +// #define P128_USES_GRBW // Select 1 option, only first one enabled from this list will be used +// #define P128_USES_RGB +// #define P128_USES_RGBW +// #define P128_USES_BRG +// #define P128_USES_BGR +// #define P128_USES_RBG +// #define P128_ENABLE_FAKETV 1 // Enable(1)/Disable(0) FakeTV effect, disabled by default on ESP8266 (.bin size issue), enabled by default on ESP32 +// #define USES_P129 // 74HC165 Input shiftregisters + +// #define USES_P131 // NeoPixel Matrix +// #define USES_P132 // INA3221 +// #define USES_P133 // LTR390 UV +// #define USES_P134 // A02YYUW +// #define USES_P135 // SCD4x +// #define P135_FEATURE_RESET_COMMANDS 1 // Enable/Disable quite spacious (~950 bytes) 'selftest' and 'factoryreset' subcommands +// #define USES_P137 // AXP192 +// #define USES_P138 // IP5306 +// #define USES_P139 // AXP2101 + +// #define USES_P140 // CardKB +// #define UN_USES_P140 // **DISABLE** I2C CardKB for ESP32 (Enabled by default for ESP32) +// #define USES_P141 // PCD8544 Nokia 5110 LCD +// #define USES_P142 // Position - AS5600 +// #define USES_P143 // I2C Rotary encoders +// #define P143_FEATURE_INCLUDE_M5STACK 0 // Enabled by default, can be turned off here +// #define P143_FEATURE_INCLUDE_DFROBOT 0 // Enabled by default, can be turned off here +// #define P143_FEATURE_COUNTER_COLORMAPPING 0 // Enabled by default, can be turned off here + +// #define USES_P144 // PM1006(K) (Vindriktning) +// #define USES_P145 // MQxxx (MQ135 CO2, MQ3 Alcohol) +// #define USES_P146 // Cache Reader +// #define USES_P147 // SGP4x +// #define P147_FEATURE_GASINDEXALGORITHM 0 // Enabled by default, can be turned off here +// #define USES_P148 // POWR3xxD/THR3xxD + +// #define USES_P150 // TMP117 Temperature +// #define USES_P151 // Honeywell Pressure +#define USES_P152 // ESP32 DAC +// #define USES_P153 // SHT4x +// #define USES_P154 // BMP3xx I2C + +// #define USES_P159 // Presence - LD2410 Radar detection + +// #define USES_P162 // Output - MCP42xxx Digipot +// #define USES_P163 // Environment - RadSens I2C radiation counter +// #define USES_P164 // Gases - ENS16x TVOC/eCO2 +// #define USES_P165 // Display - NeoPixel (7-segment) +// #define USES_P166 // Output - GP8403 Dual channel DAC (Digital Analog Converter) +// #define USES_P167 // Environment - Sensirion SEN5x / Ikea Vindstyrka +// #define USES_P168 // Light - VEML6030/VEML7700 +// #define USES_P169 // Environment - AS3935 Lightning Detector + +// #define USES_P170 // Input - I2C Liquid level sensor +// #define USES_P172 // BMP3xx SPI. +// #define USES_P173 // Environment - SHTC3 +// #define USES_P175 // Dust - PMSx003i I2C +// #define USES_P176 // Communication - Victron VE.Direct +// #define USES_P178 // LU9685 Servo controller + +/* + ####################################################################################################### + ########### Controllers + ####################################################################################################### + */ + + +#define USES_C001 // Domoticz HTTP +#define USES_C002 // Domoticz MQTT +// #define USES_C003 // Nodo telnet +// #define USES_C004 // ThingSpeak +#define USES_C005 // Home Assistant (openHAB) MQTT +// #define USES_C006 // PiDome MQTT +// #define USES_C007 // Emoncms +#define USES_C008 // Generic HTTP +// #define USES_C009 // FHEM HTTP +#define USES_C010 // Generic UDP +// #define USES_C011 // Generic HTTP Advanced +// #define USES_C012 // Blynk HTTP +#define USES_C013 // ESPEasy P2P network +// #define USES_C014 // homie 3 & 4dev MQTT +// #define USES_C015 // Blynk +// #define USES_C016 // Cache controller +// #define USES_C017 // Zabbix +// #define USES_C018 // TTN/RN2483 + + +/* + ####################################################################################################### + ########### Notifiers + ####################################################################################################### + */ + + +// #define USES_N001 // Email +// #define USES_N002 // Buzzer + + +#endif // ESPEASY_CUSTOM_H diff --git a/static/fetch_and_parse_log.js b/static/fetch_and_parse_log.js index db2bb6577..6aec69cbf 100644 --- a/static/fetch_and_parse_log.js +++ b/static/fetch_and_parse_log.js @@ -1,3 +1,9 @@ +//---------------no need to be included into WebStaticData.h--------------------------------------------- +// if no filter input exists, create it +document.getElementById("logfilter") || + (document.querySelector("section").innerHTML += + ''); +//------------------------------------------------------------ function elId(e) { return document.getElementById(e); } diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index c43244b73..10cc5f90b 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -51,19 +51,19 @@ else: # "-DUSES_P094", # CUL Reader # "-DUSES_P095", # TFT ILI9341 "-DUSES_P097", # Touch (ESP32) - "-DUSES_P106", # BME680 - "-DUSES_P107", # SI1145 UV index - "-DUSES_P110", # VL53L0x - "-DUSES_P131", # NeoPixelMatrix +# "-DUSES_P106", # BME680 +# "-DUSES_P107", # SI1145 UV index +# "-DUSES_P110", # VL53L0x +# "-DUSES_P131", # NeoPixelMatrix "-DUSES_P146", # Cache Reader "-DUSES_P169", # AS3935 Lightning Detector "-DUSES_C016", # Cache Controller - "-DUSES_C018", # TTN/RN2483 + # "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk - - "-DUSES_N001", # E-mail + "-DUSES_NW005", # TTN/RN2483 + # "-DUSES_N001", # E-mail "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", From 7c8a5ae68cd409f31d6df103e838607e6ed78287 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Sun, 25 Jan 2026 14:43:31 +0100 Subject: [PATCH 058/161] [LOG] reverting accidents --- src/9Custom.h | 607 ---------------------------------- tools/pio/pre_custom_esp32.py | 14 +- 2 files changed, 7 insertions(+), 614 deletions(-) delete mode 100644 src/9Custom.h diff --git a/src/9Custom.h b/src/9Custom.h deleted file mode 100644 index e4592e652..000000000 --- a/src/9Custom.h +++ /dev/null @@ -1,607 +0,0 @@ -#ifndef ESPEASY_CUSTOM_H -#define ESPEASY_CUSTOM_H - -/* - To modify the stock configuration without changing the EspEasy.ino file : - 1) rename this file to "Custom.h" (It is ignored by Git) - 2) define your own settings below - 3) Build one of the environments with Custom in their name, they will automatically use this file if it exists - */ - - -/* - ####################################################################################################### - Your Own Default Settings - ####################################################################################################### - You can basically ovveride ALL macro defined in ESPEasy.ino. - Don't forget to first #undef each existing #define that you add below. - But since this Custom.h is included before other defines are made, you don't have to undef a lot of defines. - Here are some examples: - */ - -// --- Feature Flagging --------------------------------------------------------- -// Can be set to 1 to enable, 0 to disable, or not set to use the default (usually via define_plugin_sets.h) - -#define FEATURE_RULES_EASY_COLOR_CODE 1 // Use code highlighting, autocompletion and command suggestions in Rules -#define FEATURE_ESPEASY_P2P 1 // (1/0) enables the ESP Easy P2P protocol -#define FEATURE_ARDUINO_OTA 1 // enables the Arduino OTA capabilities -#define FEATURE_THINGSPEAK_EVENT 1 // Generates an event when requesting last value of a field in thingspeak via SendToHTTP(e.g. sendToHTTP,api.thingspeak.com,80,/channels/1667332/fields/5/last) -#define FEATURE_OPENMETEO_EVENT 1 // Generates an event with the response of a open-meteo request (https://open-meteo.com/en/docs) -#define FEATURE_JSON_EVENT 1 // Generates an event with the values of a JSON repsonse of an HTTP call. Keys are stored in json.keys one key per line (e.g.: Body.Data.DAY_ENERGY.Values.1) -// #define FEATURE_SD 1 // Enable SD card support -// #define FEATURE_DOWNLOAD 1 // Enable downloading a file from an url - -#ifdef BUILD_GIT -# undef BUILD_GIT -#endif // ifdef BUILD_GIT - -#define BUILD_GIT "My Build: " __DATE__ " " __TIME__ - - -#define DEFAULT_NAME "MyEspEasyDevice" // Enter your device friendly name -#define UNIT 0 // Unit Number -#define DEFAULT_DELAY 60 // Sleep Delay in seconds - -// --- Wifi AP Mode (when your Wifi Network is not reachable) ---------------------------------------- -#define DEFAULT_AP_IP 192, 168, 4, 1 // Enter IP address (comma separated) for AP (config) mode -#define DEFAULT_AP_SUBNET 255, 255, 255, 0 // Enter IP address (comma separated) for AP (config) mode -#define DEFAULT_AP_KEY "configesp" // Enter network WPA key for AP (config) mode - -// --- Wifi Client Mode ----------------------------------------------------------------------------- -#define DEFAULT_SSID "sofu" // Enter your network SSID -#define DEFAULT_KEY "nototrrentplease" // Enter your network WPA key -#define DEFAULT_SSID2 "" // Enter your fallback network SSID -#define DEFAULT_KEY2 "" // Enter your fallback network WPA key -#define DEFAULT_WIFI_INCLUDE_HIDDEN_SSID false // Allow to connect to hidden SSID APs -#define DEFAULT_USE_STATIC_IP false // (true|false) enabled or disabled static IP -#define DEFAULT_IP "192.168.0.50" // Enter your IP address -#define DEFAULT_DNS "192.168.0.1" // Enter your DNS -#define DEFAULT_GW "192.168.0.1" // Enter your Gateway -#define DEFAULT_SUBNET "255.255.255.0" // Enter your Subnet -#define DEFAULT_IPRANGE_LOW "0.0.0.0" // Allowed IP range to access webserver -#define DEFAULT_IPRANGE_HIGH "255.255.255.255" // Allowed IP range to access webserver -#define DEFAULT_IP_BLOCK_LEVEL 1 // 0: ALL_ALLOWED 1: LOCAL_SUBNET_ALLOWED 2: -// ONLY_IP_RANGE_ALLOWED -#define DEFAULT_ADMIN_USERNAME "admin" -#define DEFAULT_ADMIN_PASS "" - -#define DEFAULT_WIFI_CONNECTION_TIMEOUT 10000 // minimum timeout in ms for WiFi to be connected. -#define DEFAULT_WIFI_FORCE_BG_MODE false // when set, only allow to connect in 802.11B or G mode (not N) -#define DEFAULT_WIFI_RESTART_WIFI_CONN_LOST false // Perform wifi off and on when connection was lost. -#define DEFAULT_ECO_MODE false // When set, make idle calls between executing tasks. -#define DEFAULT_WIFI_NONE_SLEEP false // When set, the wifi will be set to no longer sleep (more power -// used and need reboot to reset mode) -#define DEFAULT_GRATUITOUS_ARP false // When set, the node will send periodical gratuitous ARP - // packets to announce itself. -#define DEFAULT_TOLERANT_LAST_ARG_PARSE false // When set, the last argument of some commands will be parsed to the end of the line - // See: https://github.com/letscontrolit/ESPEasy/issues/2724 -#define DEFAULT_SEND_TO_HTTP_ACK false // Wait for ack with SendToHttp command. - -#define DEFAULT_AP_DONT_FORCE_SETUP false // Allow optional usage of Sensor without WIFI avaiable // When set you can use the Sensor in AP-Mode without beeing forced to /setup -#define DEFAULT_DONT_ALLOW_START_AP false // Usually the AP will be started when no WiFi is defined, or the defined one cannot be found. This flag may prevent it. - -// --- Default Controller ------------------------------------------------------------------------------ -#define DEFAULT_CONTROLLER false // true or false enabled or disabled, set 1st controller - // defaults -#define DEFAULT_CONTROLLER_ENABLED true // Enable default controller by default -#define DEFAULT_CONTROLLER_USER "" // Default controller user -#define DEFAULT_CONTROLLER_PASS "" // Default controller Password - -// using a default template, you also need to set a DEFAULT PROTOCOL to a suitable MQTT protocol ! -#define DEFAULT_PUB "sensors/espeasy/%sysname%/%tskname%/%valname%" // Enter your pub -#define DEFAULT_SUB "sensors/espeasy/%sysname%/#" // Enter your sub -#define DEFAULT_SERVER "192.168.0.8" // Enter your Server IP address -#define DEFAULT_SERVER_HOST "" // Server hostname -#define DEFAULT_SERVER_USEDNS false // true: Use hostname. false: use IP -#define DEFAULT_USE_EXTD_CONTROLLER_CREDENTIALS false // true: Allow longer user credentials for controllers - -#define DEFAULT_PORT 8080 // Enter your Server port value -#define DEFAULT_CONTROLLER_TIMEOUT 100 // Default timeout in msec - -#define DEFAULT_PROTOCOL 0 // Protocol used for controller communications - // 0 = Stand-alone (no controller set) - // 1 = Domoticz HTTP - // 2 = Domoticz MQTT - // 3 = Nodo Telnet - // 4 = ThingSpeak - // 5 = Home Assistant (openHAB) MQTT - // 6 = PiDome MQTT - // 7 = EmonCMS - // 8 = Generic HTTP - // 9 = FHEM HTTP - -#ifdef ESP8266 -#define DEFAULT_PIN_I2C_SDA 4 -#endif -#ifdef ESP32 -#define DEFAULT_PIN_I2C_SDA -1 // Undefined -#endif -#ifdef ESP8266 -#define DEFAULT_PIN_I2C_SCL 5 -#endif -#ifdef ESP32 -#define DEFAULT_PIN_I2C_SCL -1 // Undefined -#endif -#define DEFAULT_PIN_I2C2_SDA -1 // Undefined -#define DEFAULT_PIN_I2C3_SDA -1 // Undefined -#define DEFAULT_PIN_I2C2_SCL -1 // Undefined -#define DEFAULT_PIN_I2C3_SCL -1 // Undefined -#define DEFAULT_I2C_CLOCK_SPEED 400000 // Use 100 kHz if working with old I2C chips -#define FEATURE_I2C_DEVICE_SCAN 1 - -#define DEFAULT_SPI 0 //0=disabled 1=enabled and for ESP32 there is option 2 =HSPI - -#define DEFAULT_PIN_STATUS_LED (-1) -#define DEFAULT_PIN_STATUS_LED_INVERSED true - -#define DEFAULT_PIN_RESET_BUTTON (-1) - - -#define DEFAULT_USE_RULES false // (true|false) Enable Rules? -#define DEFAULT_RULES_OLDENGINE true - -#define DEFAULT_MQTT_RETAIN false // (true|false) Retain MQTT messages? -#define DEFAULT_CONTROLLER_DELETE_OLDEST false // (true|false) to delete oldest message when queue is full -#define DEFAULT_CONTROLLER_MUST_CHECK_REPLY false // (true|false) Check Acknowledgment -#define DEFAULT_MQTT_DELAY 100 // Time in milliseconds to retain MQTT messages -#define DEFAULT_MQTT_LWT_TOPIC "" // Default lwt topic -#define DEFAULT_MQTT_LWT_CONNECT_MESSAGE "Connected" // Default lwt message -#define DEFAULT_MQTT_LWT_DISCONNECT_MESSAGE "Connection Lost" // Default lwt message -#define DEFAULT_MQTT_USE_UNITNAME_AS_CLIENTID 0 - -#define DEFAULT_USE_NTP false // (true|false) Use NTP Server -#define DEFAULT_NTP_HOST "" // NTP Server Hostname -#define DEFAULT_TIME_ZONE 0 // Time Offset (in minutes) -#define DEFAULT_USE_DST false // (true|false) Use Daily Time Saving - -#define DEFAULT_LATITUDE 0.0f // Default Latitude -#define DEFAULT_LONGITUDE 0.0f // Default Longitude - -#define DEFAULT_SYSLOG_IP "" // Syslog IP Address -#define DEFAULT_SYSLOG_PORT 0 // Standard syslog port: 514 -#define DEFAULT_SYSLOG_FACILITY 0 // kern -#define DEFAULT_SYSLOG_LEVEL 0 // Syslog Log Level -#define DEFAULT_SERIAL_LOG_LEVEL LOG_LEVEL_INFO // Serial Log Level -#define DEFAULT_WEB_LOG_LEVEL LOG_LEVEL_INFO // Web Log Level -#define DEFAULT_SD_LOG_LEVEL 0 // SD Card Log Level -#define DEFAULT_USE_SD_LOG false // (true|false) Enable Logging to the SD card - -#define DEFAULT_USE_SERIAL true // (true|false) Enable Logging to the Serial Port -#define DEFAULT_SERIAL_BAUD 115200 // Serial Port Baud Rate - -#define DEFAULT_SYNC_UDP_PORT 8266 // Used for ESPEasy p2p. (IANA registered port: 8266) - - -// Factory Reset defaults -#define DEFAULT_FACTORY_RESET_KEEP_UNIT_NAME true -#define DEFAULT_FACTORY_RESET_KEEP_WIFI true -#define DEFAULT_FACTORY_RESET_KEEP_NETWORK true -#define DEFAULT_FACTORY_RESET_KEEP_NTP_DST true -#define DEFAULT_FACTORY_RESET_KEEP_CONSOLE_LOG true - - -#define BUILD_NO_DEBUG - -// Custom built-in url for hosting JavaScript and CSS files. -#define CUSTOM_BUILD_CDN_URL "https://cdn.jsdelivr.net/gh/letscontrolit/ESPEasy@mega/static/" - - - -// Special SSID/key setup only to be used in custom builds. - -// Deployment SSID will be used only when the configured SSIDs are not reachable and/or no credentials are set. -// This to make deployment of large number of nodes easier -#define CUSTOM_DEPLOYMENT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease deployment -#define CUSTOM_DEPLOYMENT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease deployment -#define CUSTOM_SUPPORT_SSID "" // Enter SSID not shown in UI, to be used on custom builds to ease support -#define CUSTOM_SUPPORT_KEY "" // Enter key not shown in UI, to be used on custom builds to ease support - - -// Emergency fallback SSID will only be attempted in the first 10 minutes after reboot. -// When found, the unit will connect to it and depending on the built in flag, it will either just connect to it, or clear set credentials. -// Use case: User connects to a public AP which does need to agree on an agreement page for the rules of conduct (e.g. open APs) -// This is seen as a valid connection, so the unit will not reconnect to another node and thus becomes inaccessible. -#define CUSTOM_EMERGENCY_FALLBACK_SSID "" // Enter SSID not shown in UI, to be used to regain access to the node -#define CUSTOM_EMERGENCY_FALLBACK_KEY "" // Enter key not shown in UI, to be used to regain access to the node - -#define CUSTOM_EMERGENCY_FALLBACK_RESET_CREDENTIALS false -#define CUSTOM_EMERGENCY_FALLBACK_START_AP false - -#define CUSTOM_EMERGENCY_FALLBACK_ALLOW_MINUTES_UPTIME 10 - -// Allow for remote provisioning of a node. -// This is only allowed for custom builds. -// To setup the configuration of the provisioning file, one must also define FEATURE_SETTINGS_ARCHIVE -// Default setting is to not allow to configure a node remotely, unless explicitly enabled. -// #define FEATURE_CUSTOM_PROVISIONING 1 - -#define FEATURE_SSDP 1 - -#define FEATURE_EXT_RTC 1 // Support for external RTC clock modules like PCF8563/PCF8523/DS3231/DS1307 - -#define FEATURE_PLUGIN_STATS 1 // Support collecting historic data + computing stats on historic data -#ifdef ESP8266 -// # define PLUGIN_STATS_NR_ELEMENTS 16 -#endif // ifdef ESP8266 -# ifdef ESP32 -// # define PLUGIN_STATS_NR_ELEMENTS 64 -#endif // ifdef ESP32 -#define FEATURE_CHART_JS 1 // Support for drawing charts, like PluginStats historic data - -// Optional alternative CDN links: -// Chart.js: (only used when FEATURE_CHART_JS is enabled) -// #define CDN_URL_CHART_JS "https://cdn.jsdelivr.net/npm/chart.js@4.1.2/dist/chart.umd.min.js" -// JQuery: -// #define CDN_URL_JQUERY "https://code.jquery.com/jquery-3.6.0.min.js" - - -// #define FEATURE_SETTINGS_ARCHIVE 1 -// #define FEATURE_I2CMULTIPLEXER 1 -// #define FEATURE_TRIGONOMETRIC_FUNCTIONS_RULES 1 -// #define PLUGIN_USES_ADAFRUITGFX // Used by Display plugins using Adafruit GFX library -// #define ADAGFX_ARGUMENT_VALIDATION 0 // Disable argument validation in AdafruitGFX_helper -// #define ADAGFX_SUPPORT_7COLOR 0 // Disable the support of 7-color eInk displays by AdafruitGFX_helper -// #define FEATURE_SEND_TO_HTTP 1 // Enable availability of the SendToHTTP command -// #define FEATURE_POST_TO_HTTP 1 // Enable availability of the PostToHTTP command -// #define FEATURE_PUT_TO_HTTP 1 // Enable availability of the PutToHTTP command -// #define FEATURE_I2C_DEVICE_CHECK 0 // Disable the I2C Device check feature -// #define FEATURE_I2C_GET_ADDRESS 0 // Disable fetching the I2C address from I2C plugins. Will be enabled when FEATURE_I2C_DEVICE_CHECK is enabled -// #define FEATURE_RTTTL 1 // Enable rtttl command -// #define FEATURE_ANYRTTTL_LIB 1 // Use AnyRttl library for RTTTL handling -// #define FEATURE_ANYRTTTL_ASYNC 1 // When AnyRttl enabled, use Async (nonblocking) mode instead of the default Blocking mode -// #define FEATURE_RTTTL_EVENTS 1 // Enable RTTTL events for Async use, for blocking it doesn't make sense -// #define FEATURE_STRING_VARIABLES 1 // Enable String variable support (enabled on ESP32, NOT supported on ESP8266 for memory restrictions!) - -#if FEATURE_CUSTOM_PROVISIONING -// For device models, see src/src/DataTypes/DeviceModel.h -// #ifdef ESP32 -// #define DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL 0 // DeviceModel_default -// #endif -// #ifdef ESP8266 -// #define DEFAULT_FACTORY_DEFAULT_DEVICE_MODEL 0 // DeviceModel_default -// #endif -// #define DEFAULT_PROVISIONING_FETCH_RULES1 false -// #define DEFAULT_PROVISIONING_FETCH_RULES2 false -// #define DEFAULT_PROVISIONING_FETCH_RULES3 false -// #define DEFAULT_PROVISIONING_FETCH_RULES4 false -// #define DEFAULT_PROVISIONING_FETCH_NOTIFICATIONS false -// #define DEFAULT_PROVISIONING_FETCH_SECURITY false -// #define DEFAULT_PROVISIONING_FETCH_CONFIG false -// #define DEFAULT_PROVISIONING_FETCH_PROVISIONING false -// #define DEFAULT_PROVISIONING_FETCH_FIRMWARE false -// #define DEFAULT_PROVISIONING_SAVE_URL false -// #define DEFAULT_PROVISIONING_SAVE_CREDENTIALS false -// #define DEFAULT_PROVISIONING_ALLOW_FETCH_COMMAND false -// #define DEFAULT_PROVISIONING_URL "" -// #define DEFAULT_PROVISIONING_USER "" -// #define DEFAULT_PROVISIONING_PASS "" -#endif - - - - -/* - ####################################################################################################### - Defining web interface - ####################################################################################################### - */ - -#define MENU_INDEX_MAIN_VISIBLE true -/* -#define MENU_INDEX_CONFIG_VISIBLE false -#define MENU_INDEX_CONTROLLERS_VISIBLE false -#define MENU_INDEX_HARDWARE_VISIBLE false -#define MENU_INDEX_DEVICES_VISIBLE false -#define MENU_INDEX_RULES_VISIBLE false -#define MENU_INDEX_NOTIFICATIONS_VISIBLE false -#define MENU_INDEX_TOOLS_VISIBLE false -*/ - -#define MAIN_PAGE_SHOW_SYSINFO_BUTTON true -#define MAIN_PAGE_SHOW_WiFi_SETUP_BUTTON true -#define MAIN_PAGE_SHOW_BASIC_INFO_NOT_LOGGED_IN false - -#define MAIN_PAGE_SHOW_NODE_LIST_BUILD true -#define MAIN_PAGE_SHOW_NODE_LIST_TYPE true - -#define SETUP_PAGE_SHOW_CONFIG_BUTTON true - -// #define FEATURE_AUTO_DARK_MODE 0 // 0 = Disable auto-dark mode -// #define FEATURE_EXTENDED_TASK_VALUE_TYPES 0 // 0 = Disable extra task value types like 64 bit ints, double, etc. in Dummy tasks -// #define FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE 0 // 0 = switch to float as floating point type for rules/formula processing. - -//#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON - -#define SHOW_SYSINFO_JSON 1 //Enables the sysinfo_json page (by default is enabled when WEBSERVER_NEW_UI is enabled too) - -/* - ####################################################################################################### - CSS / template - ####################################################################################################### - */ -/* -#define WEBPAGE_TEMPLATE_DEFAULT_HEADER "

ESP Easy Mega: {{title}}


" -#define WEBPAGE_TEMPLATE_DEFAULT_FOOTER "" -#define WEBPAGE_TEMPLATE_AP_HEADER "

Welcome to ESP Easy Mega AP

" -#define WEBPAGE_TEMPLATE_HIDE_HELP_BUTTON -*/ -// Embed Custom CSS in Custom.h: -/* -#define WEBSERVER_EMBED_CUSTOM_CSS -static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { -... -,0}; -*/ - - -/* - ####################################################################################################### - Special settings (rendering settings incompatible with other builds) - ####################################################################################################### - */ - -// #define FEATURE_NON_STANDARD_24_TASKS 1 - -/* - ####################################################################################################### - Your Own selection of plugins and controllers - ####################################################################################################### - */ - -#define CONTROLLER_SET_NONE -#define NOTIFIER_SET_NONE -#define PLUGIN_SET_NONE - - -/* - ####################################################################################################### - ########### Plugins - ####################################################################################################### - */ - -// #define FEATURE_SERVO 1 // Uncomment and set to 0 to explicitly disable SERVO support - - -#define USES_P001 // Switch -#define USES_P002 // ADC -// #define USES_P003 // Pulse -#define USES_P004 // 1-Wire Temperature (Dallas/Maxim DS18B20) -// #define USES_P005 // DHT11/12/22 SONOFF2301/7021/MS01 -// #define USES_P006 // BMP085/180 -// #define USES_P007 // PCF8591 -// #define USES_P008 // Wiegand (RFID) -// #define USES_P009 // MCP23017 - -// #define USES_P010 // BH1750 -// #define USES_P011 // ProMini Extender -// #define USES_P012 // LCD2004 -// #define USES_P013 // HC-SR04/RCW-0001 -#define USES_P014 // SI70xx/HTU21D -// #define USES_P015 // TSL2561 -// #define USES_P017 // PN532 -// #define USES_P018 // GP2Y10 -// #define USES_P019 // PCF8574 - -#define USES_P020 // Ser2Net -#define USES_P021 // Level Control -// #define USES_P022 // PCA9685 -// #define USES_P023 // OLED SSD1306 -// #define USES_P024 // MLX90614 -// #define USES_P025 // ADS1x15 -#define USES_P026 // SysInfo -#define USES_P027 // INA219 -#define USES_P028 // BMx280 -// #define USES_P029 // Domoticz MQTT Helper - -// #define USES_P031 // SHT1x -// #define USES_P032 // MS5611 (GY-63) -#define USES_P033 // Dummy Device -// #define USES_P034 // DHT12 -// #define USES_P036 // OLED SSD1306/SH1106 Framed -// #define P036_FEATURE_DISPLAY_PREVIEW 1 // Enable Preview feature, shows on-display content on Devices overview page -// #define P036_FEATURE_ALIGN_PREVIEW 1 // Enable center/right-align feature when preview is enabled (auto-disabled for 1M builds) -// #define P036_ENABLE_TICKER 1 // Enable ticker function -// #define USES_P037 // MQTT Import -// #define P037_MAPPING_SUPPORT 1 // Enable Value mapping support -// #define P037_FILTER_SUPPORT 1 // Enable filtering support -// #define P037_JSON_SUPPORT 1 // Enable Json support -// #define USES_P038 // NeoPixel -// #define P038_FEATURE_NEOPIXELFOR 1 // Enable NeoPixelFor/NeoPixelForHSV commands (default enabled for ESP32) -// #define USES_P039 // Thermocouple - -// #define USES_P040 // RFID - ID12LA/RDM6300 -// #define USES_P041 // NeoPixel (Word Clock) -// #define USES_P042 // NeoPixel (Candle) -#define USES_P043 // ClkOutput -// #define USES_P044 // P1 Wifi Gateway (Merged with P020, when P044 is enabled, then P020 is also enabled) -// #define USES_P045 // MPU6050 -// #define USES_P046 // Ventus W266 -// #define USES_P047 // Soil moisture sensor -// #define USES_P048 // Motoshield v2 -#define USES_P049 // MH-Z19 - -// #define USES_P050 // TCS34725 RGB Color Sensor with IR filter and White LED -// #define USES_P051 // AM2320 -// #define USES_P052 // SenseAir -// #define USES_P053 // PMSx003 / PMSx003ST -// #define USES_P054 // DMX512 -// #define USES_P055 // Chiming -// #define USES_P056 // SDS011/018/198 -// #define USES_P057 // HT16K33_LED -// #define USES_P058 // HT16K33_KeyPad -// #define USES_P059 // Rotary Encoder - -// #define USES_P060 // MCP3221 -// #define USES_P061 // PCF8574 / MCP23017 / PCA8575 -// #define USES_P062 // MPR121 -// #define USES_P063 // TTP229 -// #define USES_P064 // APDS9960 Gesture -// #define USES_P065 // DRF0299 -// #define USES_P066 // VEML6040 -// #define USES_P067 // HX711_Load_Cell -// #define USES_P068 // SHT3x -// #define USES_P069 // LM75A - -// #define USES_P070 // NeoPixel_Clock -// #define USES_P071 // Kamstrup401 -// #define USES_P072 // HDC1000/HDC1008/HDC1010/HDC1050/HDC1080 -// #define USES_P073 // 7-segment display -// #define USES_P074 // TSL2591 -// #define USES_P075 // Nextion -// #define USES_P076 // HLW8012/BL0937 (Shelly Plug S, Sonoff POW R1, Huafan SS, KMC 70011, Aplic WDP303075, SK03 Outdoor, BlitzWolf SHP, Teckin, Teckin US, Gosund SP1 v23) -// #define USES_P077 // CSE7766 (Sonoff S31, Sonoff POW R2, Sonoff POW R3xx(D), Sonoff Dual R3) -// #define USES_P078 // Eastron SDMxxx Modbus -// #define USES_P079 // Wemos / Lolin Motorshield - -// #define USES_P080 // iButton Sensor DS1990A -#define USES_P081 // Cron -// #define USES_P082 // GPS -// #define USES_P083 // SGP30 TVOC -// #define USES_P084 // VEML6070 -// #define USES_P085 // AcuDC24x -// #define USES_P086 // Receiving values according Homie convention. Works together with C014 Homie controller -#define USES_P087 // Serial Proxy -// #define USES_P088 // HeatpumpIR -// #define USES_P089 // Ping - -// #define USES_P090 // CCS811 TVOC -// #define USES_P091 // Serial MCU controlled switch -// #define USES_P092 // DLbus -// #define USES_P093 // Mitsubishi Heat Pump -// #define USES_P094 // CUL Reader -// #define USES_P095 // ILI934x / ILI948x -// #define USES_P096 // eInk -#define USES_P097 // ESP32 Touch -// #define USES_P098 // PWM Motor -// #define USES_P099 // XPT2046 touchscreen - -// #define USES_P100 // DS2423 counter -// #define USES_P101 // Wake On Lan -// #define USES_P102 // PZEM-004Tv30-Multiple -// #define USES_P103 // Atlas Scientific EZO Sensors (pH, ORP, EZO, DO) -// #define USES_P104 // MAX7219 dot matrix -// #define USES_P105 // AHT10/AHT2x -// #define USES_P106 // BME68x -// #define USES_P107 // SI1145 -// #define USES_P108 // DDS238-x ZN Modbus energy meters -// #define USES_P109 // ThermoOLED - -// #define USES_P110 // VL53L0X Time of Flight sensor -// #define USES_P111 // MFRC522 RFID reader -// #define USES_P112 // AS7265x -// #define USES_P113 // VL53L1X ToF -// #define USES_P114 // VEML6075 -// #define USES_P115 // MAX1704x -//#define USES_P116 // ST77xx -// #define USES_P117 // SCD30 -// #define USES_P118 // Itho -// #define USES_P119 // ITG3205 Gyro - -// #define USES_P120 // ADXL345 I2C Acceleration / Gravity -// #define USES_P121 // HMC5883L -// #define USES_P122 // SHT2x -// #define USES_P123 // I2C Touchscreens -// #define USES_P124 // I2C Multi Relay -// #define USES_P125 // ADXL345 SPI Acceleration / Gravity -// #define USES_P126 // 74HC595 Shift register -// #define USES_P127 // CDM7160 -// #define USES_P128 // NeoPixel (BusFX) -// #define P128_USES_GRB // Default -// #define P128_USES_GRBW // Select 1 option, only first one enabled from this list will be used -// #define P128_USES_RGB -// #define P128_USES_RGBW -// #define P128_USES_BRG -// #define P128_USES_BGR -// #define P128_USES_RBG -// #define P128_ENABLE_FAKETV 1 // Enable(1)/Disable(0) FakeTV effect, disabled by default on ESP8266 (.bin size issue), enabled by default on ESP32 -// #define USES_P129 // 74HC165 Input shiftregisters - -// #define USES_P131 // NeoPixel Matrix -// #define USES_P132 // INA3221 -// #define USES_P133 // LTR390 UV -// #define USES_P134 // A02YYUW -// #define USES_P135 // SCD4x -// #define P135_FEATURE_RESET_COMMANDS 1 // Enable/Disable quite spacious (~950 bytes) 'selftest' and 'factoryreset' subcommands -// #define USES_P137 // AXP192 -// #define USES_P138 // IP5306 -// #define USES_P139 // AXP2101 - -// #define USES_P140 // CardKB -// #define UN_USES_P140 // **DISABLE** I2C CardKB for ESP32 (Enabled by default for ESP32) -// #define USES_P141 // PCD8544 Nokia 5110 LCD -// #define USES_P142 // Position - AS5600 -// #define USES_P143 // I2C Rotary encoders -// #define P143_FEATURE_INCLUDE_M5STACK 0 // Enabled by default, can be turned off here -// #define P143_FEATURE_INCLUDE_DFROBOT 0 // Enabled by default, can be turned off here -// #define P143_FEATURE_COUNTER_COLORMAPPING 0 // Enabled by default, can be turned off here - -// #define USES_P144 // PM1006(K) (Vindriktning) -// #define USES_P145 // MQxxx (MQ135 CO2, MQ3 Alcohol) -// #define USES_P146 // Cache Reader -// #define USES_P147 // SGP4x -// #define P147_FEATURE_GASINDEXALGORITHM 0 // Enabled by default, can be turned off here -// #define USES_P148 // POWR3xxD/THR3xxD - -// #define USES_P150 // TMP117 Temperature -// #define USES_P151 // Honeywell Pressure -#define USES_P152 // ESP32 DAC -// #define USES_P153 // SHT4x -// #define USES_P154 // BMP3xx I2C - -// #define USES_P159 // Presence - LD2410 Radar detection - -// #define USES_P162 // Output - MCP42xxx Digipot -// #define USES_P163 // Environment - RadSens I2C radiation counter -// #define USES_P164 // Gases - ENS16x TVOC/eCO2 -// #define USES_P165 // Display - NeoPixel (7-segment) -// #define USES_P166 // Output - GP8403 Dual channel DAC (Digital Analog Converter) -// #define USES_P167 // Environment - Sensirion SEN5x / Ikea Vindstyrka -// #define USES_P168 // Light - VEML6030/VEML7700 -// #define USES_P169 // Environment - AS3935 Lightning Detector - -// #define USES_P170 // Input - I2C Liquid level sensor -// #define USES_P172 // BMP3xx SPI. -// #define USES_P173 // Environment - SHTC3 -// #define USES_P175 // Dust - PMSx003i I2C -// #define USES_P176 // Communication - Victron VE.Direct -// #define USES_P178 // LU9685 Servo controller - -/* - ####################################################################################################### - ########### Controllers - ####################################################################################################### - */ - - -#define USES_C001 // Domoticz HTTP -#define USES_C002 // Domoticz MQTT -// #define USES_C003 // Nodo telnet -// #define USES_C004 // ThingSpeak -#define USES_C005 // Home Assistant (openHAB) MQTT -// #define USES_C006 // PiDome MQTT -// #define USES_C007 // Emoncms -#define USES_C008 // Generic HTTP -// #define USES_C009 // FHEM HTTP -#define USES_C010 // Generic UDP -// #define USES_C011 // Generic HTTP Advanced -// #define USES_C012 // Blynk HTTP -#define USES_C013 // ESPEasy P2P network -// #define USES_C014 // homie 3 & 4dev MQTT -// #define USES_C015 // Blynk -// #define USES_C016 // Cache controller -// #define USES_C017 // Zabbix -// #define USES_C018 // TTN/RN2483 - - -/* - ####################################################################################################### - ########### Notifiers - ####################################################################################################### - */ - - -// #define USES_N001 // Email -// #define USES_N002 // Buzzer - - -#endif // ESPEASY_CUSTOM_H diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index 10cc5f90b..c43244b73 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -51,19 +51,19 @@ else: # "-DUSES_P094", # CUL Reader # "-DUSES_P095", # TFT ILI9341 "-DUSES_P097", # Touch (ESP32) -# "-DUSES_P106", # BME680 -# "-DUSES_P107", # SI1145 UV index -# "-DUSES_P110", # VL53L0x -# "-DUSES_P131", # NeoPixelMatrix + "-DUSES_P106", # BME680 + "-DUSES_P107", # SI1145 UV index + "-DUSES_P110", # VL53L0x + "-DUSES_P131", # NeoPixelMatrix "-DUSES_P146", # Cache Reader "-DUSES_P169", # AS3935 Lightning Detector "-DUSES_C016", # Cache Controller - # "-DUSES_C018", # TTN/RN2483 + "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk - "-DUSES_NW005", # TTN/RN2483 - # "-DUSES_N001", # E-mail + + "-DUSES_N001", # E-mail "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", From a1c8fa2df2e490defec9ea6af7a7919a7378e0d8 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Jan 2026 16:39:51 +0100 Subject: [PATCH 059/161] [P132] Enable extended features for Energy build ESP8266 for now --- src/src/PluginStructs/P132_data_struct.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/src/PluginStructs/P132_data_struct.h b/src/src/PluginStructs/P132_data_struct.h index 8c1263dac..b7200d6a5 100644 --- a/src/src/PluginStructs/P132_data_struct.h +++ b/src/src/PluginStructs/P132_data_struct.h @@ -6,7 +6,11 @@ # ifndef P132_EXTENDED # ifdef ESP8266 -# define P132_EXTENDED 0 +# ifdef PLUGIN_ENERGY_COLLECTION +# define P132_EXTENDED 1 // Enable for ESP8266 Energy collection, as for now it has enough space +# else // ifdef PLUGIN_ENERGY_COLLECTION +# define P132_EXTENDED 0 +# endif // ifdef PLUGIN_ENERGY_COLLECTION # endif // ifdef ESP8266 # ifdef ESP32 # define P132_EXTENDED 1 From 7f997e9cd020b4bd226a7a069c0dfa3c03f57064 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 26 Jan 2026 14:38:34 +0100 Subject: [PATCH 060/161] [Build] Fix failing custom build (#5485) Fixes: #5485 --- src/src/WebServer/Markup.cpp | 1 + src/src/WebServer/Markup_Forms.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 7153a1add..bf285c204 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -12,6 +12,7 @@ #include "../Helpers/Convert.h" #include "../Helpers/ESPEasy_UnitOfMeasure.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/StringConverter_Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 8580a8de0..03b9066ab 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -8,6 +8,7 @@ #include "../Globals/Settings.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" From 3a0325a8654c1bf2f653b5dc33c8bfad5514ab39 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 26 Jan 2026 14:38:34 +0100 Subject: [PATCH 061/161] [Build] Fix failing custom build (#5485) Fixes: #5485 --- src/src/WebServer/Markup.cpp | 1 + src/src/WebServer/Markup_Forms.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 7153a1add..bf285c204 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -12,6 +12,7 @@ #include "../Helpers/Convert.h" #include "../Helpers/ESPEasy_UnitOfMeasure.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/StringConverter_Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 8580a8de0..03b9066ab 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -8,6 +8,7 @@ #include "../Globals/Settings.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/Hardware_device_info.h" #include "../Helpers/Numerical.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" From 5f338ae6e357bd290dc70cb6363de517c5d0b8bc Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 26 Jan 2026 22:57:34 +0100 Subject: [PATCH 062/161] [Build] Fix compilation with Custom.h and TLS on ESP32 --- src/src/CustomBuild/define_plugin_sets.h | 19 ++++++++++--------- src/src/ESPEasyCore/Controller.cpp | 5 +++++ src/src/Globals/MQTT.h | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 14983ba0a..01600576b 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3541,15 +3541,6 @@ To create/register a plugin, you have to : #endif #endif -#if FEATURE_MQTT_TLS || FEATURE_EMAIL_TLS || FEATURE_HTTP_TLS - #if defined(FEATURE_TLS) && !FEATURE_TLS - #undef FEATURE_TLS - #endif - #ifndef FEATURE_TLS - #define FEATURE_TLS 1 - #endif -#endif - #if !defined(FEATURE_MQTT_DISCOVER) && FEATURE_MQTT #if defined(LIMIT_BUILD_SIZE) || defined(ESP8266) // Must enable this explicitly for ESP8266 Custom build @@ -3934,6 +3925,16 @@ To create/register a plugin, you have to : #endif #endif // ifndef FEATURE_HTTP_TLS +#if FEATURE_MQTT_TLS || FEATURE_EMAIL_TLS || FEATURE_HTTP_TLS + #if defined(FEATURE_TLS) && !FEATURE_TLS + #undef FEATURE_TLS + #endif + #ifndef FEATURE_TLS + #define FEATURE_TLS 1 + #endif +#endif + + #ifndef FEATURE_AUTO_DARK_MODE #ifdef LIMIT_BUILD_SIZE #define FEATURE_AUTO_DARK_MODE 0 diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index cba1a0498..0a09a08e8 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -34,6 +34,11 @@ #include "../Helpers/PeriodicalActions.h" #include "../Helpers/PortStatus.h" +# if FEATURE_MQTT_TLS + # include + # include "../CustomBuild/Certificate_CA.h" +# endif // if FEATURE_MQTT_TLS + constexpr pluginID_t PLUGIN_ID_MQTT_IMPORT(37); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index da293c6d1..a786958c9 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -16,6 +16,7 @@ # if FEATURE_MQTT_TLS # include + # include "../CustomBuild/Certificate_CA.h" # endif // if FEATURE_MQTT_TLS # if FEATURE_MQTT_CONNECT_BACKGROUND From 5592b1f3c14d193989e4c59a0c3da6712b831407 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 28 Jan 2026 21:28:06 +0100 Subject: [PATCH 063/161] [ESP-IDF] Update to latest 1901-1318-5.5 platform build --- 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 d93cab796..655077607 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -171,7 +171,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0401-1225-5.5/framework-arduinoespressif32-release_v5.5-ad03770f.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1901-1318-5.5/framework-arduinoespressif32-release_v5.5-3a59a3b4.tar.xz custom_remove_include = true From f40b9151dc456dfbeb452aa90f4cd23b3dce0a18 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 29 Jan 2026 06:18:41 +0100 Subject: [PATCH 064/161] [Build] Fix compile issues --- platformio_esp32c6_envs.ini | 5 +++++ src/src/Helpers/_CPlugin_Helper_webform.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/platformio_esp32c6_envs.ini b/platformio_esp32c6_envs.ini index 37856d019..b73a2ac82 100644 --- a/platformio_esp32c6_envs.ini +++ b/platformio_esp32c6_envs.ini @@ -10,6 +10,9 @@ build_unflags = ${esp32_base_idf5_5.build_unflags} -fexceptions board_build.filesystem = littlefs lib_ignore = ${esp32_base_idf5_5.lib_ignore} + PPP + HeatpumpIR + IRremoteESP8266 board = esp32c6cdc @@ -78,6 +81,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags} -D ST7789_EXTRA_INIT=1 -D P116_EXTRA_ST7789=1 extra_scripts = ${esp32c6_common_LittleFS.extra_scripts} +lib_ignore = ${esp32_base_idf5_5.lib_ignore} [env:max_ESP32c6_16M8M] @@ -91,6 +95,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags} -D ST7789_EXTRA_INIT=1 -D P116_EXTRA_ST7789=1 extra_scripts = ${esp32c6_common_LittleFS.extra_scripts} +lib_ignore = ${esp32_base_idf5_5.lib_ignore} diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 7cd4b459b..1ee7db6f3 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -12,6 +12,8 @@ #include "../WebServer/ESPEasy_WebServer.h" #include "../WebServer/Markup.h" #include "../WebServer/Markup_Forms.h" +#include "../ESPEasyCore/Controller.h" +#include "../DataStructs/ControllerSettingsStruct.h" /*********************************************************************************************\ * Functions to load and store controller settings on the web page. From 9ffe8b619625e134e25c420151734c908717f925 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 29 Jan 2026 06:19:03 +0100 Subject: [PATCH 065/161] Revert "[ESP-IDF] Update to latest 1901-1318-5.5 platform build" This reverts commit 5592b1f3c14d193989e4c59a0c3da6712b831407. --- 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 655077607..d93cab796 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -171,7 +171,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1901-1318-5.5/framework-arduinoespressif32-release_v5.5-3a59a3b4.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0401-1225-5.5/framework-arduinoespressif32-release_v5.5-ad03770f.tar.xz custom_remove_include = true From 719c41e2911045aa3a81ad1a4bfe3f2c41ff7250 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 31 Jan 2026 21:06:16 +0100 Subject: [PATCH 066/161] [Fixes] Various fixes and improvements Feb 2026 --- docs/source/Plugin/_plugin_substitutions_p16x.repl | 2 +- src/_P037_MQTTImport.ino | 2 +- src/src/PluginStructs/P104_data_struct.cpp | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/source/Plugin/_plugin_substitutions_p16x.repl b/docs/source/Plugin/_plugin_substitutions_p16x.repl index 4c533a76e..87538a280 100644 --- a/docs/source/Plugin/_plugin_substitutions_p16x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p16x.repl @@ -67,7 +67,7 @@ .. |P167_type| replace:: :cyan:`Environment` .. |P167_typename| replace:: :cyan:`Environment - Sensirion SEN5x (IKEA Vindstyrka)` .. |P167_porttype| replace:: `.` -.. |P167_status| replace:: :yellow:`CLIMATE B` +.. |P167_status| replace:: :yellow:`CLIMATE A` .. |P167_github| replace:: P167_Vindstyrka.ino .. _P167_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P167_Vindstyrka.ino .. |P167_usedby| replace:: `.` diff --git a/src/_P037_MQTTImport.ino b/src/_P037_MQTTImport.ino index c5687b4db..44f158a5a 100644 --- a/src/_P037_MQTTImport.ino +++ b/src/_P037_MQTTImport.ino @@ -569,7 +569,7 @@ boolean Plugin_037(uint8_t function, struct EventStruct *event, String& string) # if !defined(P037_LIMIT_BUILD_SIZE) || defined(P037_OVERRIDE) if (loglevelActiveFor(LOG_LEVEL_INFO)) { - String log = strformat(F("IMPT : MQTT fetched json attribute: %s payload: "), + String log = strformat(F("IMPT : MQTT fetched json attribute: %s payload: %s"), key.c_str(), Payload.c_str()); diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 3255bbdfd..8097514bc 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -1982,10 +1982,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { }; const int orderOptions[] = { 0, 1 }; FormSelectorOptions selector_zoneordering(2, orderTypes, orderOptions); - selector.reloadonchange = true; - # ifdef P104_USE_TOOLTIPS - selector.tooltip = zonetip; - # endif // ifdef P104_USE_TOOLTIPS + selector_zoneordering.reloadonchange = true; selector_zoneordering.addFormSelector(F("Zone order"), F("zoneorder"), bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0); selector_zoneordering.reloadonchange = true; From 4760c903e7c4aa9bf1f68f536e752cc7e7644d54 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 31 Jan 2026 21:54:03 +0100 Subject: [PATCH 067/161] [CI] Fix github yaml syntax issues --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6012bb07f..434aa8063 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ name: Build on: push: branches: [mega] - tags-ignore: '**' + tags-ignore: ['**'] paths-ignore: - 'dist/Release_notes.txt' - 'docs/**' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8a70cdf9..9cb7bb9d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,8 @@ name: Release on: push: - branches-ignore: '**' - tags: 'mega-*' + branches-ignore: ['**'] + tags: ['mega-*'] permissions: contents: read From 348c745a644f9b2c6d3335755ba95985f8a24643 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 31 Jan 2026 21:57:24 +0100 Subject: [PATCH 068/161] [P132] Fix MQTT Discovery for Power setting --- src/src/PluginStructs/P132_data_struct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/PluginStructs/P132_data_struct.cpp b/src/src/PluginStructs/P132_data_struct.cpp index 64767a16d..a381f867b 100644 --- a/src/src/PluginStructs/P132_data_struct.cpp +++ b/src/src/PluginStructs/P132_data_struct.cpp @@ -8,7 +8,7 @@ int Plugin_132_QueryVType(uint8_t value_nr) { const bool odd = value_nr % 2; return static_cast(odd ? Sensor_VType::SENSOR_TYPE_CURRENT_ONLY : Sensor_VType::SENSOR_TYPE_VOLTAGE_ONLY); } - return static_cast(Sensor_VType::SENSOR_TYPE_NONE); + return static_cast(Sensor_VType::SENSOR_TYPE_POWER_USG_ONLY); } # endif // if FEATURE_MQTT_DISCOVER From e91f7f98e27e8ffd31689a511de1c9df53f1393b Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 1 Feb 2026 23:17:06 +0100 Subject: [PATCH 069/161] [Docs] Fix warnings and doc-build errors, remove deprecated plugin from conf.py --- docs/source/conf.py | 12 ++++++------ docs/source/index.rst | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 96817160c..09eac6e2e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,7 +15,7 @@ # import os # import sys # sys.path.insert(0, os.path.abspath('.')) -from recommonmark.parser import CommonMarkParser +# from recommonmark.parser import CommonMarkParser import sphinx_bootstrap_theme # -- Project information ----------------------------------------------------- @@ -52,15 +52,15 @@ extensions = [ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] -source_parsers = { - '.md': CommonMarkParser, -} +# source_parsers = { +# '.md': CommonMarkParser, +# } # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # -source_suffix = ['.rst', '.md'] -#source_suffix = '.rst' +# source_suffix = ['.rst', '.md'] +source_suffix = {'.rst': 'restructuredtext'} # The master toctree document. master_doc = 'index' diff --git a/docs/source/index.rst b/docs/source/index.rst index 7adfb7c4e..87fdba648 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -56,6 +56,7 @@ Welcome to ESP Easy's documentation! Controller/C* Notify/N* Plugin/P* + Network/NW* Rules/HTTPResponseParser.rst From 66f6a598f4ac4dc64a12c221dd613d996da5619d Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:43:40 +0100 Subject: [PATCH 070/161] [LOG] autoscroll fix, code reduction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixes a long-existing autoscroll issue where the whole site would scroll instead of just the log content. - reducing the code by simplifying the „browser check" and refining the loop --- src/src/Static/WebStaticData.h | 109 +++++++++---------- static/fetch_and_parse_log.js | 191 +++++++++++++-------------------- 2 files changed, 124 insertions(+), 176 deletions(-) diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index f5ea29d48..2f5953c8b 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -406,68 +406,61 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { "function elId(e){return document.getElementById(e)}" -"function getBrowser(){var e,t=navigator.userAgent,o=t.match(/(opera|chrome|safari|firefox|msie|trident(?=\\/))\\/?\\s*(\\d+)/i)||[];" -"return/trident/i.test(o[1])?{name:'IE',version:(e=/\\brv[ :]+(\\d+)/g.exec(t)||[])[1]||''}:" -"'Chrome'===o[1]&&null!=(e=t.match(/\\bOPR|Edge\\/(\\d+)/))?" -"{name:'Opera',version:e[1]}:" -"(o=o[2]?[o[1],o[2]]:[navigator.appName,navigator.appVersion,'-?']," -"null!=(e=t.match(/version\\/(\\d+)/i))&&o.splice(1,1,e[1]),{name:o[0],version:o[1]})}" -"var browser=getBrowser(),currentBrowser=browser.name+browser.version;" -"textToDisplay=(browser.name=browser.version<12)?" -"'Error: '+currentBrowser+' is not supported! Please try a modern web browser.':" -"'Fetching log entries...';" -"elId('copyText_1').innerHTML=textToDisplay;" -"loopDeLoop(1000,0);" -"var logLevel=['Unused','Error','Info','Debug','Debug More','Undefined','Undefined','Undefined','Undefined','Debug Dev'];" -"function loopDeLoop(e,t){let o='copyText_1';" -"isNaN(t)&&(t=1),null==e&&(e=1000),scrolling_type=e<=500?'auto':'smooth';" -"var r,n,i='',l=0,s=setInterval(function(){" -"if(l>0){clearInterval(s);return}" -"++t>1?l=1:fetch('/logjson').then(function(t){" -"if(200!==t.status){console.log('Looks like there was a problem. Status Code: '+t.status);return}" -"t.json().then(function(t){var l;" -"for(null==n&&(n=''),r=0;r'+t.Log.Entries[r].timestamp+': '+" -"t.Log.Entries[r].text+'')}" -"e=t.Log.TTL," -"''!==n&&('Fetching log entries...'==elId(o).innerHTML&&(elId(o).innerHTML='')," -"elId(o).innerHTML+=n)," -"n='';" -"!0==(autoscroll_on=elId('autoscroll').checked)&&''!==i&&elId(i).scrollIntoView({behavior:scrolling_type});" +"document.getElementById('logfilter')||(document.querySelector('section').innerHTML+=" +"'');" +"const ct1=elId('copyText_1');" +"window.fetch?ct1.textContent='Fetching log entries...':" +"ct1.textContent='Error: This browser is not supported. Please use a modern browser.';" +"const logLevel={0:'Unused',1:'Error',2:'Info',3:'Debug',4:'Debug More',9:'Debug Dev'};" +"function loopDeLoop(e=1000){setTimeout(function(){fetch('/logjson')" +".then(e=>{if(!e.ok)throw Error(e.status);return e.json()})" +".then(t=>{let o='';" +"for(let l=0;l" +"${i.timestamp}: ${i.text}`)}" +"e=t.Log.TTL||e;" +"o&&(ct1.textContent==='Fetching log entries...'&&(ct1.innerHTML=''),ct1.innerHTML+=o);" "applyLogFilter();" -"elId('current_loglevel').innerHTML='Logging: '+logLevel[t.Log.SettingsWebLogLevel]+' ('+t.Log.SettingsWebLogLevel+')';" -"clearInterval(s);loopDeLoop(e,0)" -"})}).catch(function(t){" -"elId(o).innerHTML+='
>> '+t.message+' <<
';" -"autoscroll_on=elId('autoscroll').checked;" -"elId(o).scrollTop=elId(o).scrollHeight;" -"e=5000;clearInterval(s);loopDeLoop(e,0)" -"}),l=1},e)}" -"function applyLogFilter(){let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean)," +"elId('autoscroll')?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?'auto':'smooth'});" +"let n=t.Log.SettingsWebLogLevel,r=logLevel[n]??'Undefined';" +"elId('current_loglevel').textContent=`Logging: ${r} (${n})`;" +"loopDeLoop(e)})" +".catch(e=>{ct1.innerHTML+=`
>> ${e.message} <<
`;" +"ct1.scrollTop=ct1.scrollHeight;loopDeLoop(5000)})},e)}" +"function applyLogFilter(){" +"let e=elId('logfilter').value.split(';').map(e=>e.trim()).filter(Boolean)," "t=document.querySelectorAll('#copyText_1 > div')," "o=e.some(e=>!e.startsWith('!'));" -"for(let r of t){r.dataset.originalHtml||(r.dataset.originalHtml=r.innerHTML);" -"let n=r.dataset.originalHtml,i=r.textContent.toLowerCase(),l=[],s=!1,a=!o;" -"for(let d of e){" -"if(d.startsWith('!')){let c=d.slice(1);" -"if(!c.includes('&')||c.startsWith('&')||c.endsWith('&')){" -"if(c&&i.includes(c.toLowerCase())){s=!0;break}}else{" -"let g=c.split('&').map(e=>e.trim()).filter(Boolean),f=-1,u=!0;" -"for(let p of g){let L=i.indexOf(p.toLowerCase(),f+1);if(-1===L){u=!1;break}f=L}if(u){s=!0;break}}continue}" -"if(d.includes('&')&&!d.startsWith('&')&&!d.endsWith('&')){" -"let h=d.split('&').map(e=>e.trim()).filter(Boolean);" -"if(h.length>=2){let m=-1,$=!0;for(let _ of h){let b=i.indexOf(_.toLowerCase(),m+1);if(-1===b){$=!1;break}m=b}if($){l.push(...h),a=!0;break}}continue}" -"if(i.includes(d.toLowerCase())){l.push(d),a=!0;break}}" -"if(s||!a){r.style.display='none';continue}r.style.display='';r.innerHTML=highlightOutsideTags(n,l)}}" +"for(let l of t){" +"l.dataset.originalHtml||(l.dataset.originalHtml=l.innerHTML);" +"let i=l.dataset.originalHtml,n=l.textContent.toLowerCase(),r=[],s=!1,a=!o;" +"for(let c of e){" +"if(c.startsWith('!')){let g=c.slice(1);" +"if(!g.includes('&')||g.startsWith('&')||g.endsWith('&')){" +"if(g&&n.includes(g.toLowerCase())){s=!0;break}}" +"else{let f=g.split('&').map(e=>e.trim()).filter(Boolean),u=-1,d=!0;" +"for(let p of f){let h=n.indexOf(p.toLowerCase(),u+1);if(h===-1){d=!1;break}u=h}" +"if(d){s=!0;break}}continue}" +"if(c.includes('&')&&!c.startsWith('&')&&!c.endsWith('&')){" +"let L=c.split('&').map(e=>e.trim()).filter(Boolean);" +"if(L.length>=2){let _=-1,$=!0;" +"for(let b of L){let m=n.indexOf(b.toLowerCase(),_+1);if(m===-1){$=!1;break}_=m}" +"if($){r.push(...L),a=!0;break}}continue}" +"if(n.includes(c.toLowerCase())){r.push(c),a=!0;break}}" +"if(s||!a){l.style.display='none';continue}" +"l.style.display='';l.innerHTML=highlightOutsideTags(i,r)}}" "function highlightOutsideTags(e,t){if(!t.length)return e;" -"let o='',r=!1,n=0;" -"for(;n'===i){r=!1;o+=i;n++;continue}" -"if(!r){let l=!1;for(let s of t){let a=e.slice(n,n+s.length);" -"if(a.toLowerCase()===s.toLowerCase()){o+=`${a}`;n+=s.length;l=!0;break}}if(l)continue}o+=i;n++}return o}" +"let o='',l=!1,i=0;" +"for(;i'===n){l=!1;o+=n;i++;continue}" +"if(!l){let r=!1;" +"for(let s of t){let a=e.slice(i,i+s.length);" +"if(a.toLowerCase()===s.toLowerCase()){" +"o+=`${a}`;" +"i+=s.length;r=!0;break}}" +"if(r)continue}" +"o+=n;i++}return o}" +"loopDeLoop(1000);" "const input=document.getElementById('logfilter');" "input&&(input.placeholder='\"!\"=exclude \";\"=or \"&\"=and (e.g. !act)');" }; diff --git a/static/fetch_and_parse_log.js b/static/fetch_and_parse_log.js index 6aec69cbf..30241a962 100644 --- a/static/fetch_and_parse_log.js +++ b/static/fetch_and_parse_log.js @@ -1,132 +1,87 @@ //---------------no need to be included into WebStaticData.h--------------------------------------------- // if no filter input exists, create it document.getElementById("logfilter") || - (document.querySelector("section").innerHTML += - ''); + (document.querySelector("section").innerHTML += + ''); //------------------------------------------------------------ function elId(e) { return document.getElementById(e); } -function getBrowser() { - var ua = navigator.userAgent, - tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; - if (/trident/i.test(M[1])) { - tem = /\brv[ :]+(\d+)/g.exec(ua) || []; - return { - name: 'IE', - version: (tem[1] || '') - }; - } - if (M[1] === 'Chrome') { - tem = ua.match(/\bOPR|Edge\/(\d+)/); - if (tem != null) { - return { - name: 'Opera', - version: tem[1] - }; - } - } - M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; - if ((tem = ua.match(/version\/(\d+)/i)) != null) { - M.splice(1, 1, tem[1]); - } - return { - name: M[0], - version: M[1] - }; -} -var browser = getBrowser(); -var currentBrowser = browser.name + browser.version; -if (browser.name = 'IE' && browser.version < 12) { - textToDisplay = 'Error: ' + currentBrowser + ' is not supported! Please try a modern web browser.' -} else { - textToDisplay = 'Fetching log entries...'; -} -elId('copyText_1').innerHTML = textToDisplay; -loopDeLoop(1000, 0); -var logLevel = new Array('Unused', 'Error', 'Info', 'Debug', 'Debug More', 'Undefined', 'Undefined', 'Undefined', 'Undefined', 'Debug Dev'); -function loopDeLoop(timeForNext, activeRequests) { - var maximumRequests = 1; +const ct1 = elId('copyText_1'); + +if (!window.fetch) { + ct1.textContent = 'Error: This browser is not supported. Please use a modern browser.'; +} else { + ct1.textContent = 'Fetching log entries...'; +} + +const logLevel = { + 0: 'Unused', + 1: 'Error', + 2: 'Info', + 3: 'Debug', + 4: 'Debug More', + 9: 'Debug Dev' +}; + +loopDeLoop(1000); + +function loopDeLoop(timeForNext = 1000) { const url = '/logjson'; - const ct1 = 'copyText_1'; - if (isNaN(activeRequests)) { - activeRequests = maximumRequests; - } - if (timeForNext == null) { - timeForNext = 1000; - } - if (timeForNext <= 500) { - scrolling_type = 'auto'; - } else { - scrolling_type = 'smooth'; - } - var c; - var logEntriesChunk; - var currentIDtoScrollTo = ''; - var check = 0; - var i = setInterval(function() { - if (check > 0) { - clearInterval(i); - return; - } - ++activeRequests; - if (activeRequests > maximumRequests) { - check = 1; - } else { - fetch(url).then(function(response) { - if (response.status !== 200) { - console.log('Looks like there was a problem. Status Code: ' + response.status); - return; + + setTimeout(function () { + fetch(url) + .then(response => { + if (!response.ok) { + throw new Error(response.status); } - response.json().then(function(data) { - var logEntry; - if (logEntriesChunk == null) { - logEntriesChunk = ''; - } - for (c = 0; c < data.Log.nrEntries; ++c) { - try { - logEntry = data.Log.Entries[c].timestamp; - } catch (err) { - logEntry = err.name; - } finally { - if (logEntry !== "TypeError") { - currentIDtoScrollTo = data.Log.Entries[c].timestamp; - logEntriesChunk += '
' + data.Log.Entries[c].timestamp + ': ' + data.Log.Entries[c].text + '
'; - } - } - } - timeForNext = data.Log.TTL; - if (logEntriesChunk !== '') { - if (elId(ct1).innerHTML == 'Fetching log entries...') { - elId(ct1).innerHTML = ''; - } - elId(ct1).innerHTML += logEntriesChunk; - } - logEntriesChunk = ''; - autoscroll_on = elId('autoscroll').checked; - if (autoscroll_on == true && currentIDtoScrollTo !== '') { - elId(currentIDtoScrollTo).scrollIntoView({ - behavior: scrolling_type - }); - } - applyLogFilter(); - elId('current_loglevel').innerHTML = 'Logging: ' + logLevel[data.Log.SettingsWebLogLevel] + ' (' + data.Log.SettingsWebLogLevel + ')'; - clearInterval(i); - loopDeLoop(timeForNext, 0); - return; - }) - }).catch(function(err) { - elId(ct1).innerHTML += '
>> ' + err.message + ' <<
'; - autoscroll_on = elId('autoscroll').checked; - elId(ct1).scrollTop = elId(ct1).scrollHeight; - timeForNext = 5000; - clearInterval(i); - loopDeLoop(timeForNext, 0); - return; + return response.json(); }) - }; - check = 1; + .then(data => { + let logEntriesChunk = ''; + + for (let c = 0; c < data.Log.nrEntries; c++) { + const entry = data.Log.Entries[c]; + if (!entry || !entry.timestamp) continue; + + logEntriesChunk += + `
+ ${entry.timestamp}: ${entry.text} +
`; + } + + timeForNext = data.Log.TTL || timeForNext; + + if (logEntriesChunk) { + if (ct1.textContent === 'Fetching log entries...') { + ct1.innerHTML = ''; + } + ct1.innerHTML += logEntriesChunk; + } + + applyLogFilter(); + + if (elId('autoscroll')?.checked) { + ct1.scrollTo({ + top: ct1.scrollHeight, + behavior: timeForNext <= 500 ? 'auto' : 'smooth' + }); + } + + const level = data.Log.SettingsWebLogLevel; + const levelName = logLevel[level] ?? 'Undefined'; + + elId('current_loglevel').textContent = + `Logging: ${levelName} (${level})`; + + loopDeLoop(timeForNext); + }) + .catch(err => { + ct1.innerHTML += `
>> ${err.message} <<
`; + ct1.scrollTop = ct1.scrollHeight; + loopDeLoop(5000); + }); }, timeForNext); } From f05bd0797f12a30e5452d0d2a3a7f895c2b351f5 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:49:05 +0100 Subject: [PATCH 071/161] [LOG] Removed unnecessary code. --- src/src/Static/WebStaticData.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index 2f5953c8b..04cb1bdcb 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -406,9 +406,6 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { "function elId(e){return document.getElementById(e)}" -"document.getElementById('logfilter')||(document.querySelector('section').innerHTML+=" -"'');" -"const ct1=elId('copyText_1');" "window.fetch?ct1.textContent='Fetching log entries...':" "ct1.textContent='Error: This browser is not supported. Please use a modern browser.';" "const logLevel={0:'Unused',1:'Error',2:'Info',3:'Debug',4:'Debug More',9:'Debug Dev'};" From 7f14ce8d5887c99c94cb2d80d1146fdc0eb0d062 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:08:41 +0100 Subject: [PATCH 072/161] [LOG+DevicePage_JS] refining the device page JS ...while I am at it :) --- static/update_sensor_values_device_page.js | 132 +++++++++------------ 1 file changed, 54 insertions(+), 78 deletions(-) diff --git a/static/update_sensor_values_device_page.js b/static/update_sensor_values_device_page.js index 443ff89c9..ebe2d9808 100644 --- a/static/update_sensor_values_device_page.js +++ b/static/update_sensor_values_device_page.js @@ -1,88 +1,64 @@ -loopDeLoop(1000, 0); +loopDeLoop(1000); + function elId(e) { - return document.getElementById(e); + return document.getElementById(e); } -function loopDeLoop(timeForNext, activeRequests) { - var maximumRequests = 1; - var c; - var k; - var err = ''; - var url = '/json?view=sensorupdate'; - var check = 0; - if (isNaN(activeRequests)) { - activeRequests = maximumRequests; - } - if (timeForNext == null) { - timeForNext = 1000; - } - i = setInterval(function() { - if (check > 0) { - clearInterval(i); - return; - } - ++activeRequests; - if (activeRequests > maximumRequests) { - check = 1; - } else { - fetch(url).then(function(response) { - var valueEntry; - if (response.status !== 200) { - console.log('Looks like there was a problem. Status Code: ' + response.status); - return; +const SENSOR_URL = '/json?view=sensorupdate'; + +function loopDeLoop(timeForNext = 1000) { + setTimeout(function () { + fetch(SENSOR_URL) + .then(response => { + if (!response.ok) { + throw new Error(response.status); } - response.json().then(function(data) { - timeForNext = data.TTL; - var showUoM = data.ShowUoM === undefined || (data.ShowUoM && data.ShowUoM !== 'false'); - for (c = 0; c < data.Sensors.length; c++) { - if (data.Sensors[c].hasOwnProperty('TaskValues')) { - for (k = 0; k < data.Sensors[c].TaskValues.length; k++) { - try { - valueEntry = data.Sensors[c].TaskValues[k].Value; - } catch (err) { - valueEntry = err.name; - } finally { - if (valueEntry !== 'TypeError') { - var tempValue = data.Sensors[c].TaskValues[k].Value; - var decimalsValue = data.Sensors[c].TaskValues[k].NrDecimals; - if (decimalsValue < 255) { - tempValue = parseFloat(tempValue).toFixed(decimalsValue); - } - var tempUoM = data.Sensors[c].TaskValues[k].UoM; - var tempPres = data.Sensors[c].TaskValues[k].Presentation; - if (tempPres) { - tempValue = tempPres; - } else - if (tempUoM && showUoM) { - tempValue += ' ' + tempUoM; - } - var valueID = 'value_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1); - var valueNameID = 'valuename_' + (data.Sensors[c].TaskNumber - 1) + '_' + (data.Sensors[c].TaskValues[k].ValueNumber - 1); - var valueElement = elId(valueID); - var valueNameElement = elId(valueNameID); - if (valueElement) { - valueElement.innerHTML = tempValue; - } - if (valueNameElement) { - valueNameElement.innerHTML = data.Sensors[c].TaskValues[k].Name + ':'; - } - } - } - } + return response.json(); + }) + .then(data => { + timeForNext = data.TTL || timeForNext; + const showUoM = data.ShowUoM === undefined || (data.ShowUoM && data.ShowUoM !== 'false'); + + for (let c = 0; c < data.Sensors.length; c++) { + const sensor = data.Sensors[c]; + if (!sensor.TaskValues) continue; + + for (let k = 0; k < sensor.TaskValues.length; k++) { + const taskValue = sensor.TaskValues[k]; + if (!taskValue || taskValue.Value == null) continue; + + let tempValue = taskValue.Value; + const decimals = taskValue.NrDecimals; + + if (decimals < 255) { + tempValue = parseFloat(tempValue).toFixed(decimals); + } + + if (taskValue.Presentation) { + tempValue = taskValue.Presentation; + } else if (taskValue.UoM && showUoM) { + tempValue += ' ' + taskValue.UoM; + } + + const baseId = (sensor.TaskNumber - 1) + '_' + (taskValue.ValueNumber - 1); + + const valueEl = elId('value_' + baseId); + if (valueEl) { + valueEl.innerHTML = tempValue; + } + + const nameEl = elId('valuename_' + baseId); + if (nameEl) { + nameEl.innerHTML = taskValue.Name + ':'; } } - clearInterval(i); - loopDeLoop(timeForNext, 0); - return; - }); - }).catch(function(err) { + } + + loopDeLoop(timeForNext); + }) + .catch(err => { console.log(err.message); - timeForNext = 5000; - clearInterval(i); - loopDeLoop(timeForNext, 0); - return; + loopDeLoop(5000); }); - check = 1; - } }, timeForNext); } \ No newline at end of file From b17769acaef9545815ceb52650eaaed75f6905ee Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 4 Feb 2026 15:50:44 +0100 Subject: [PATCH 073/161] [P104] Add font with Cyrillic characters in high-ASCII area --- src/_P104_max7219_Dotmatrix.ino | 141 ++++++----- src/src/PluginStructs/P104_data_struct.cpp | 153 ++++++++---- src/src/PluginStructs/P104_data_struct.h | 71 ++++-- src/src/Static/Fonts/P104_font_cyr_ext.h | 261 +++++++++++++++++++++ 4 files changed, 492 insertions(+), 134 deletions(-) create mode 100644 src/src/Static/Fonts/P104_font_cyr_ext.h diff --git a/src/_P104_max7219_Dotmatrix.ino b/src/_P104_max7219_Dotmatrix.ino index c00c18b4c..b5d70fbf3 100644 --- a/src/_P104_max7219_Dotmatrix.ino +++ b/src/_P104_max7219_Dotmatrix.ino @@ -72,56 +72,60 @@ // Display of the selected zone is suspended until all provided dots are drawn. No quotes are needed around // the coordinate set. // -// History: -// 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts -// 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments -// Re-enable use of settings-version V3 after some more testing -// 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility -// 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by -// a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended -// CustomTaskSettings file, by using the offset as a starting location for the data elements -// b) Combine storing the size and the data-block in a single save action -// Apply toStringNoZero() converter to reduce the settings-data to be saved -// 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the -// original content when that's updated...) -// Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress -// 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces -// 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag -// 2021-10-03 tonhuisman: Add Inverted option per zone -// 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures -// 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation -// and saving/loading per zone. That should sove the numerous stack related crashes. -// 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections -// 2021-07-18 tonhuisman: Small optimizations and improvements -// 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font -// 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed -// update/correct some documentation -// 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings -// 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order -// tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements -// All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING -// Disabled P104_DEBUG_DEV by default -// 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build -// 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes -// 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes -// 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes -// 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible -// 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH -// 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure -// 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by -// P104_USE_DATETIME_OPTIONS -// Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size -// 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization -// Webform_Load now works on current settings if the plugin is active, instead of last stored settings -// Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration -// 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands -// Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, -// Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight -// 2021-06-16 tonhuisman: Implement Clock and Date (once per second) -// 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation -// 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones -// 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy -// 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + +/** History/Changelog: + * 2026-02-04 tonhuisman: Add ASCII font with Cyrillic extension characters + * Reformat sources with updated Uncrustify settings, use block-comment for History/Changelog + * 2025-04-03 tonhuisman: Set character spacing correctly when changing fonts + * 2023-10-15 tonhuisman: Code improvements, now using NR_ELEMENTS macro instead of multiple #ifdefs and increments + * Re-enable use of settings-version V3 after some more testing + * 2023-10-08 tonhuisman: Disable use of settings-version V3 for backward compatibility + * 2023-08-15 tonhuisman: Implement Extended CustomTaskSettings, and use that to significantly improve saving the settings on LittleFS by + * a) only storing the settings-version (V3) in regular CustomTaskSettings file, and the rest in the Extended + * CustomTaskSettings file, by using the offset as a starting location for the data elements + * b) Combine storing the size and the data-block in a single save action + * Apply toStringNoZero() converter to reduce the settings-data to be saved + * 2023-08-13 tonhuisman: Add Dot subcommand for pixel-drawing in a zone. Can be applied on any type of zone (so can be overwritten by the + * original content when that's updated...) + * Set default Hardware type to FC16, as that's the most used for modules found on Aliexpress + * 2023-03-07 tonhuisman: Parse text to display without trimming off leading and trailing spaces + * 2022-08-12 tonhuisman: Remove [DEVELOPMENT] tag + * 2021-10-03 tonhuisman: Add Inverted option per zone + * 2021-09 tonhuisman: Minor improvements, attempts to fix stack failures + * 2021-08-08 tonhuisman: Reworked loading & saving the settings from A huge fixed size pre-allocated block to dynamic allocation + * and saving/loading per zone. That should sove the numerous stack related crashes. + * 2021-08-07 tonhuisman: Review feedback: several small improvements and corrections + * 2021-07-18 tonhuisman: Small optimizations and improvements + * 2021-07-14 tonhuisman: Fix some bugs in font selection, add Text reverse content type to improve usability of Vertical font + * 2021-07-12 tonhuisman: Reduce number of reconfiguration during command handling, will be applied the next time content is displayed + * update/correct some documentation + * 2021-07-08 tonhuisman: Several bugfixes: settings defaults, fix brightness to enable 0 value, simplify storing the zone settings + * 2021-06-29-2021-07-03: Add Actions column to insert before/after zone or delete a zone, order the zones either in numeric order + * tonhuisman: or in display order ('upside-down'), fixed many bugs, refactored bar-graph method, other improvements + * All optional at compile-time by P104_USE_ZONE_ACTIONS and P104_USE_ZONE_ORDERING + * Disabled P104_DEBUG_DEV by default + * 2021-06-28 tonhuisman: Bugfixes during testing, re-enable subcommands for ESP8266 display build + * 2021-06-27 tonhuisman: Implement 'barType' option for Bar-graph, bugfixes, bugfixes, bugfixes + * 2021-06-26 tonhuisman: Implement 'direction' option for Bar-graph, bugfixes + * 2021-06-26 tonhuisman: Add update command for updating one or all zones, restart repeat timer if content is updated by command, bugfixes + * 2021-06-24 tonhuisman: Add min/max range with negative minimal value support and zero-point indication if possible + * 2021-06-23 tonhuisman: Add Bar-graph option and bar command (initial feature, options to implement) guarded with P104_USE_BAR_GRAPH + * 2021-06-22 tonhuisman: Add Bar-graph initial code-infrastructure + * 2021-06-21 tonhuisman: Add options for 'formatting' time & date, will be disabled on memory-tight configs guarded by + * P104_USE_DATETIME_OPTIONS + * Introduced guard P104_ADD_SETTINGS_NOTES to disable some addFormNotes() to further reduce code size + * 2021-06-19 tonhuisman: Implement repeat delay, add settxt command, add command reference (above), bug fixing, some source reorganization + * Webform_Load now works on current settings if the plugin is active, instead of last stored settings + * Disabled most commands a some fonts to make the build fit in the ESP8266 display configuration + * 2021-06-18 tonhuisman: Implement PLUGIN_WRITE commands + * Implement several fonts extracted from MD_Parola examples (Vertical, Extended ASCII, Full Double Height, Arabic, + * Greek, Katakana) (NB: Vertical isn't working as expected yet) Will be disabled when flash memory is tight + * 2021-06-16 tonhuisman: Implement Clock and Date (once per second) + * 2021-06-13 tonhuisman: First working version, many improvemnts to make, like command handling, repeat timer implementaation + * 2021-05 tonhuisman: Store and retrieve settings for max 8 (ESP82xx) or 16 (ESP32) zones + * 2021-04 tonhuisman: Pickup and rework to get it working with ESPEasy + * 2021-03 rixtyan : Initial plugin setup, partially based on P073 MAX7219 LED display + */ # define PLUGIN_104 # define PLUGIN_ID_104 104 @@ -132,12 +136,13 @@ # include "src/PluginStructs/P104_data_struct.h" - boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; - switch (function) { - case PLUGIN_DEVICE_ADD: { + switch (function) + { + case PLUGIN_DEVICE_ADD: + { auto& dev = Device[++deviceCount]; dev.Number = PLUGIN_ID_104; dev.Type = DEVICE_TYPE_SPI; @@ -146,23 +151,27 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_SET_DEFAULTS: { + case PLUGIN_SET_DEFAULTS: + { CONFIG_PORT = -1; P104_CONFIG_HARDWARETYPE = static_cast(MD_MAX72XX::moduleType_t::FC16_HW); break; } - case PLUGIN_GET_DEVICENAME: { + case PLUGIN_GET_DEVICENAME: + { string = F(PLUGIN_NAME_104); break; } - case PLUGIN_GET_DEVICEGPIONAMES: { + case PLUGIN_GET_DEVICEGPIONAMES: + { event->String1 = formatGpioName_output(F("CS")); break; } - case PLUGIN_WEBFORM_LOAD: { + case PLUGIN_WEBFORM_LOAD: + { int8_t spi_pins[3]; Settings.getSPI_pins(spi_pins); int pinnr = -1; @@ -213,7 +222,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WEBFORM_SAVE: { + case PLUGIN_WEBFORM_SAVE: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); P104_CONFIG_ZONE_COUNT = getFormItemInt(F("zonecount")); @@ -244,7 +254,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_INIT: { + case PLUGIN_INIT: + { uint8_t numDevices = P104_CONFIG_TOTAL_UNITS; if (numDevices == 0) { // fallback value @@ -283,7 +294,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: { + case PLUGIN_EXIT: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr == P104_data) || (nullptr == P104_data->P)) { @@ -298,7 +310,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_WRITE: { + case PLUGIN_WRITE: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P104_data) { @@ -308,7 +321,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_TEN_PER_SECOND: { + case PLUGIN_TEN_PER_SECOND: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr != P104_data) && (nullptr != P104_data->P)) { @@ -319,7 +333,8 @@ boolean Plugin_104(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_ONCE_A_SECOND: { + case PLUGIN_ONCE_A_SECOND: + { P104_data_struct *P104_data = static_cast(getPluginTaskData(event->TaskIndex)); if ((nullptr != P104_data) && (nullptr != P104_data->P)) { diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 8097514bc..7ee82240a 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -117,6 +117,7 @@ bool P104_data_struct::begin() { - ... - Max. allowed total custom settings size = 4096 */ + /************************************** * loadSettings *************************************/ @@ -337,57 +338,73 @@ void P104_data_struct::configureZones() { P->setCharSpacing(currentZone, P104_NORMAL_CHAR_SPACING); // Set default font spacing - switch (it->font) { + switch (it->font) + { # ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT - case P104_DOUBLE_HEIGHT_FONT_ID: { + case P104_DOUBLE_HEIGHT_FONT_ID: + { P->setFont(currentZone, numeric7SegDouble); P->setCharSpacing(currentZone, P104_DOUBLE_CHAR_SPACING); // double spacing as well break; } # endif // ifdef P104_USE_NUMERIC_DOUBLEHEIGHT_FONT # ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT - case P104_FULL_DOUBLEHEIGHT_FONT_ID: { + case P104_FULL_DOUBLEHEIGHT_FONT_ID: + { P->setFont(currentZone, BigFont); P->setCharSpacing(currentZone, P104_DOUBLE_CHAR_SPACING); // double spacing as well break; } # endif // ifdef P104_USE_FULL_DOUBLEHEIGHT_FONT # ifdef P104_USE_VERTICAL_FONT - case P104_VERTICAL_FONT_ID: { + case P104_VERTICAL_FONT_ID: + { P->setFont(currentZone, _fontVertical); break; } # endif // ifdef P104_USE_VERTICAL_FONT # ifdef P104_USE_EXT_ASCII_FONT - case P104_EXT_ASCII_FONT_ID: { + case P104_EXT_ASCII_FONT_ID: + { P->setFont(currentZone, ExtASCII); break; } # endif // ifdef P104_USE_EXT_ASCII_FONT # ifdef P104_USE_ARABIC_FONT - case P104_ARABIC_FONT_ID: { + case P104_ARABIC_FONT_ID: + { P->setFont(currentZone, fontArabic); break; } # endif // ifdef P104_USE_ARABIC_FONT # ifdef P104_USE_GREEK_FONT - case P104_GREEK_FONT_ID: { + case P104_GREEK_FONT_ID: + { P->setFont(currentZone, fontGreek); break; } # endif // ifdef P104_USE_GREEK_FONT # ifdef P104_USE_KATAKANA_FONT - case P104_KATAKANA_FONT_ID: { + case P104_KATAKANA_FONT_ID: + { P->setFont(currentZone, fontKatakana); break; } # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + case P104_CYRILLIC_EXT_FONT_ID: + { + P->setFont(currentZone, font_cyr_ext); + break; + } + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT // Extend above this comment with more fonts if/when available, // case P104_DEFAULT_FONT_ID: and default: clauses should be the last options. // This should also make sure the default font is set if a no longer available font was selected case P104_DEFAULT_FONT_ID: - default: { + default: + { P->setFont(currentZone, nullptr); // default font break; } @@ -789,6 +806,7 @@ void P104_data_struct::displayBarGraph(uint8_t zone, # endif // ifdef P104_USE_BAR_GRAPH # ifdef P104_USE_DOT_SET + void P104_data_struct::displayDots(uint8_t zone, const P104_zone_struct& zstruct, const String & dots) { @@ -841,7 +859,8 @@ void P104_data_struct::displayDots(uint8_t zone, bool isAnimationAvailable(uint8_t animation, bool noneIsAllowed = false) { textEffect_t selection = static_cast(animation); - switch (selection) { + switch (selection) + { case PA_NO_EFFECT: { return noneIsAllowed; @@ -988,6 +1007,7 @@ enum class p104_subcommands_e { specialeffect, // subcommand: specialeffect,, (0..3) speed, // subcommand: speed,, (0..P104_MAX_SPEED_PAUSE_VALUE) # endif // ifdef P104_USE_COMMANDS + }; /******************************************************* @@ -1038,7 +1058,8 @@ bool P104_data_struct::handlePluginWrite(taskIndex_t taskIndex, // subcommands are processed in the same order as they are presented in the UI for (auto it = zones.begin(); it != zones.end() && !success; ++it) { if ((it->zone == zoneIndex)) { // This zone - switch (subcommands_e) { + switch (subcommands_e) + { case p104_subcommands_e::clear: // subcommand: clear, { @@ -1186,6 +1207,9 @@ bool P104_data_struct::handlePluginWrite(taskIndex_t taskIndex, # ifdef P104_USE_KATAKANA_FONT || (value4 == P104_KATAKANA_FONT_ID) # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + || (value4 == P104_CYRILLIC_EXT_FONT_ID) + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT ) { reconfigure = (it->font != value4); @@ -1407,7 +1431,8 @@ void P104_data_struct::getDate(char *psz, # ifdef P104_USE_DATETIME_OPTIONS if (showYear) { - switch (dateFmt) { + switch (dateFmt) + { case P104_DATE_FORMAT_US: d = node_time.month(); m = node_time.day(); @@ -1474,7 +1499,8 @@ uint8_t P104_data_struct::getDateTime(char *psz, # ifdef P104_USE_DATETIME_OPTIONS - switch (dateFmt) { + switch (dateFmt) + { case P104_DATE_FORMAT_US: d = node_time.month(); m = node_time.day(); @@ -1500,6 +1526,7 @@ uint8_t P104_data_struct::getDateTime(char *psz, } # if defined(P104_USE_NUMERIC_DOUBLEHEIGHT_FONT) || defined(P104_USE_FULL_DOUBLEHEIGHT_FONT) + void P104_data_struct::createHString(String& string) { const uint16_t stringLen = string.length(); @@ -1545,7 +1572,8 @@ bool P104_data_struct::handlePluginOncePerSecond(struct EventStruct *event) { redisplay = false; if (P->getZoneStatus(it->zone - 1)) { // Animations done? - switch (it->content) { + switch (it->content) + { case P104_CONTENT_TIME: // time case P104_CONTENT_TIME_SEC: // time sec { @@ -2128,6 +2156,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { # ifdef P104_USE_KATAKANA_FONT , F("Katakana (7)") # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + , F("Cyrillic ext. (8)") + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT }; const int fontOptions[] = { P104_DEFAULT_FONT_ID @@ -2152,6 +2183,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { # ifdef P104_USE_KATAKANA_FONT , P104_KATAKANA_FONT_ID # endif // ifdef P104_USE_KATAKANA_FONT + # ifdef P104_USE_CYRILLIC_EXT_FONT + , P104_CYRILLIC_EXT_FONT_ID + # endif // ifdef P104_USE_CYRILLIC_EXT_FONT }; constexpr int fontCount = NR_ELEMENTS(fontTypes); @@ -2555,25 +2589,39 @@ bool P104_data_struct::webform_save(struct EventStruct *event) { P104_zone_struct::P104_zone_struct(uint8_t _zone) : text(F("\"\"")), zone(_zone) {} - bool P104_zone_struct::getIntValue(uint8_t offset, int32_t& value) const { - switch (offset) { - case P104_OFFSET_SIZE: value = size; break; + switch (offset) + { + case P104_OFFSET_SIZE: value = size; + break; case P104_OFFSET_TEXT: return false; - case P104_OFFSET_CONTENT: value = content; break; - case P104_OFFSET_ALIGNMENT: value = alignment; break; - case P104_OFFSET_ANIM_IN: value = animationIn; break; - case P104_OFFSET_SPEED: value = speed; break; - case P104_OFFSET_ANIM_OUT: value = animationOut; break; - case P104_OFFSET_PAUSE: value = pause; break; - case P104_OFFSET_FONT: value = font; break; - case P104_OFFSET_LAYOUT: value = layout; break; - case P104_OFFSET_SPEC_EFFECT: value = specialEffect; break; - case P104_OFFSET_OFFSET: value = offset; break; - case P104_OFFSET_BRIGHTNESS: value = brightness; break; - case P104_OFFSET_REPEATDELAY: value = repeatDelay; break; - case P104_OFFSET_INVERTED: value = inverted; break; + case P104_OFFSET_CONTENT: value = content; + break; + case P104_OFFSET_ALIGNMENT: value = alignment; + break; + case P104_OFFSET_ANIM_IN: value = animationIn; + break; + case P104_OFFSET_SPEED: value = speed; + break; + case P104_OFFSET_ANIM_OUT: value = animationOut; + break; + case P104_OFFSET_PAUSE: value = pause; + break; + case P104_OFFSET_FONT: value = font; + break; + case P104_OFFSET_LAYOUT: value = layout; + break; + case P104_OFFSET_SPEC_EFFECT: value = specialEffect; + break; + case P104_OFFSET_OFFSET: value = offset; + break; + case P104_OFFSET_BRIGHTNESS: value = brightness; + break; + case P104_OFFSET_REPEATDELAY: value = repeatDelay; + break; + case P104_OFFSET_INVERTED: value = inverted; + break; default: return false; @@ -2583,22 +2631,37 @@ bool P104_zone_struct::getIntValue(uint8_t offset, int32_t& value) const bool P104_zone_struct::setIntValue(uint8_t offset, int32_t value) { - switch (offset) { - case P104_OFFSET_SIZE: size = value; break; + switch (offset) + { + case P104_OFFSET_SIZE: size = value; + break; case P104_OFFSET_TEXT: return false; - case P104_OFFSET_CONTENT: content = value; break; - case P104_OFFSET_ALIGNMENT: alignment = value; break; - case P104_OFFSET_ANIM_IN: animationIn = value; break; - case P104_OFFSET_SPEED: speed = value; break; - case P104_OFFSET_ANIM_OUT: animationOut = value; break; - case P104_OFFSET_PAUSE: pause = value; break; - case P104_OFFSET_FONT: font = value; break; - case P104_OFFSET_LAYOUT: layout = value; break; - case P104_OFFSET_SPEC_EFFECT: specialEffect = value; break; - case P104_OFFSET_OFFSET: offset = value; break; - case P104_OFFSET_BRIGHTNESS: brightness = value; break; - case P104_OFFSET_REPEATDELAY: repeatDelay = value; break; - case P104_OFFSET_INVERTED: inverted = value; break; + case P104_OFFSET_CONTENT: content = value; + break; + case P104_OFFSET_ALIGNMENT: alignment = value; + break; + case P104_OFFSET_ANIM_IN: animationIn = value; + break; + case P104_OFFSET_SPEED: speed = value; + break; + case P104_OFFSET_ANIM_OUT: animationOut = value; + break; + case P104_OFFSET_PAUSE: pause = value; + break; + case P104_OFFSET_FONT: font = value; + break; + case P104_OFFSET_LAYOUT: layout = value; + break; + case P104_OFFSET_SPEC_EFFECT: specialEffect = value; + break; + case P104_OFFSET_OFFSET: offset = value; + break; + case P104_OFFSET_BRIGHTNESS: brightness = value; + break; + case P104_OFFSET_REPEATDELAY: repeatDelay = value; + break; + case P104_OFFSET_INVERTED: inverted = value; + break; default: return false; diff --git a/src/src/PluginStructs/P104_data_struct.h b/src/src/PluginStructs/P104_data_struct.h index f50975328..896771552 100644 --- a/src/src/PluginStructs/P104_data_struct.h +++ b/src/src/PluginStructs/P104_data_struct.h @@ -25,23 +25,24 @@ // # endif // if defined(PLUGIN_SET_MAX) || defined(PLUGIN_BUILD_CUSTOM) || ((defined(PLUGIN_DISPLAY_A_COLLECTION) || // defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP32)) # endif // ifdef ESP32 -# define P104_USE_FULL_DOUBLEHEIGHT_FONT // Enables the use of a full (lower ascii only) set double height font -# define P104_USE_VERTICAL_FONT // Enables the use of a vertical font -# define P104_USE_EXT_ASCII_FONT // Enables the use of an extended ascii font -# define P104_USE_ARABIC_FONT // Enables the use of a Arabic font (see usage in MD_Parola examples) -# define P104_USE_GREEK_FONT // Enables the use of a Greek font (see usage in MD_Parola examples) -# define P104_USE_KATAKANA_FONT // Enables the use of a Katakana font (see usage in MD_Parola examples) -# define P104_USE_COMMANDS // Enables the use of all commands, not just clear, txt, settxt and update -# define P104_USE_DATETIME_OPTIONS // Enables extra date/time options -# define P104_USE_BAR_GRAPH // Enables the use of Bar-graph feature -# define P104_USE_ZONE_ACTIONS // Enables the use of Actions per zone (New above/New below/Delete) -# define P104_USE_ZONE_ORDERING // Enables the use of Zone ordering (Numeric order (1..n)/Display order (n..1)) -# define P104_USE_DOT_SET // Enables the use of Dot-set feature +# define P104_USE_FULL_DOUBLEHEIGHT_FONT // Enables the use of a full (lower ascii only) set double height font +# define P104_USE_VERTICAL_FONT // Enables the use of a vertical font +# define P104_USE_EXT_ASCII_FONT // Enables the use of an extended ascii font +# define P104_USE_ARABIC_FONT // Enables the use of an Arabic font (see usage in MD_Parola examples) +# define P104_USE_GREEK_FONT // Enables the use of a Greek font (see usage in MD_Parola examples) +# define P104_USE_KATAKANA_FONT // Enables the use of a Katakana font (see usage in MD_Parola examples) +# define P104_USE_CYRILLIC_EXT_FONT // Enables the use of an ASCII font with Cyrillic extended characters +# define P104_USE_COMMANDS // Enables the use of all commands, not just clear, txt, settxt and update +# define P104_USE_DATETIME_OPTIONS // Enables extra date/time options +# define P104_USE_BAR_GRAPH // Enables the use of Bar-graph feature +# define P104_USE_ZONE_ACTIONS // Enables the use of Actions per zone (New above/New below/Delete) +# define P104_USE_ZONE_ORDERING // Enables the use of Zone ordering (Numeric order (1..n)/Display order (n..1)) +# define P104_USE_DOT_SET // Enables the use of Dot-set feature -# define P104_ADD_SETTINGS_NOTES // Adds some notes on the Settings page +# define P104_ADD_SETTINGS_NOTES // Adds some notes on the Settings page # if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) -# define P104_FEATURE_STORAGE_V3 1 // Only enable saving in storage for ESP32 +# define P104_FEATURE_STORAGE_V3 1 // Only enable saving in storage for ESP32 # else // if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) # define P104_FEATURE_STORAGE_V3 0 # endif // if FEATURE_EXTENDED_CUSTOM_SETTINGS && defined(ESP32) && defined(USE_LITTLEFS) @@ -69,6 +70,9 @@ # ifdef P104_USE_KATAKANA_FONT # undef P104_USE_KATAKANA_FONT # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT +# undef P104_USE_CYRILLIC_EXT_FONT +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT // # ifdef P104_USE_COMMANDS // # undef P104_USE_COMMANDS // # endif // ifdef P104_USE_COMMANDS @@ -85,7 +89,8 @@ # undef P104_DEBUG_DEV # endif // ifdef P104_DEBUG_DEV # define P104_MEDIUM_ANIMATIONS -# endif // if (defined(PLUGIN_DISPLAY_A_COLLECTION) || defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP8266) && !defined(LIMIT_BUILD_SIZE) +# endif // if (defined(PLUGIN_DISPLAY_A_COLLECTION) || defined(PLUGIN_DISPLAY_B_COLLECTION)) && defined(ESP8266) && +// !defined(LIMIT_BUILD_SIZE) // # define P104_MINIMAL_ANIMATIONS // disable most animations // # define P104_MEDIUM_ANIMATIONS // disable some complex animations @@ -140,6 +145,9 @@ # ifdef P104_USE_KATAKANA_FONT # undef P104_USE_KATAKANA_FONT # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT +# undef P104_USE_CYRILLIC_EXT_FONT +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT # endif // ifdef LIMIT_BUILD_SIZE # if defined(P104_USE_TOOLTIPS) && !FEATURE_TOOLTIPS @@ -317,6 +325,13 @@ const uint8_t P104_DOUBLE_CHAR_SPACING = 2; // Character spacing for double-heig # include "../Static/Fonts/P104_font_katakana.h" # endif // ifdef P104_USE_KATAKANA_FONT +# ifdef P104_USE_CYRILLIC_EXT_FONT + +# define P104_CYRILLIC_EXT_FONT_ID 8 + +# include "../Static/Fonts/P104_font_cyr_ext.h" +# endif // ifdef P104_USE_CYRILLIC_EXT_FONT + struct P104_zone_struct { P104_zone_struct() = delete; // Not used, so leave out explicitly P104_zone_struct(uint8_t _zone); @@ -350,6 +365,7 @@ struct P104_zone_struct { int32_t& value) const; bool setIntValue(uint8_t offset, int32_t value); + }; # ifdef P104_USE_BAR_GRAPH @@ -363,7 +379,9 @@ struct P104_bargraph_struct { uint8_t graph; uint8_t barType = 0u; uint8_t direction = 0u; + }; + # endif // ifdef P104_USE_BAR_GRAPH struct P104_data_struct : public PluginTaskData_base { @@ -379,20 +397,17 @@ struct P104_data_struct : public PluginTaskData_base { void loadSettings(); bool webform_load(struct EventStruct *event); bool webform_save(struct EventStruct *event); - String getError() { - return error; - } - void configureZones(); + String getError() { return error; } - void setZones(uint16_t _zones) { - expectedZones = _zones; - } + void configureZones(); - bool handlePluginWrite(taskIndex_t taskIndex, - const String& string); - bool handlePluginOncePerSecond(struct EventStruct *event); - void checkRepeatTimer(uint8_t z); + void setZones(uint16_t _zones) { expectedZones = _zones; } + + bool handlePluginWrite(taskIndex_t taskIndex, + const String& string); + bool handlePluginOncePerSecond(struct EventStruct *event); + void checkRepeatTimer(uint8_t z); MD_Parola *P = nullptr; @@ -490,9 +505,13 @@ private: struct { uint16_t P104_dataSize; char P104_data[P104_SETTINGS_BUFFER_V2 + 1]; + }; + uint8_t P104_storeThis[P104_SETTINGS_BUFFER_V2 + 1 + sizeof(uint16_t)]{}; + }; + }; #endif // ifdef USES_P104 diff --git a/src/src/Static/Fonts/P104_font_cyr_ext.h b/src/src/Static/Fonts/P104_font_cyr_ext.h new file mode 100644 index 000000000..d21abd4a0 --- /dev/null +++ b/src/src/Static/Fonts/P104_font_cyr_ext.h @@ -0,0 +1,261 @@ +#pragma once + +MD_MAX72XX::fontType_t font_cyr_ext[] PROGMEM = // ASCII with Cyrillic extension +{ + 0, // 0 - 'Unused' + 0, // 1 - 'Unused' + 0, // 2 - 'Unused' + 0, // 3 - 'Unused' + 0, // 4 - 'Unused' + 0, // 5 - 'Unused' + 0, // 6 - 'Unused' + 0, // 7 - 'Unused' + 0, // 8 - 'Unused' + 0, // 9 - 'Unused' + 0, // 10 - 'Unused' + 0, // 11 - 'Unused' + 0, // 12 - 'Unused' + 0, // 13 - 'Unused' + 0, // 14 - 'Unused' + 0, // 15 - 'Unused' + 0, // 16 - 'Unused' + 0, // 17 - 'Unused' + 0, // 18 - 'Unused' + 0, // 19 - 'Unused' + 0, // 20 - 'Unused' + 0, // 21 - 'Unused' + 0, // 22 - 'Unused' + 0, // 23 - 'Unused' + 0, // 24 - 'Unused' + 0, // 25 - 'Unused' + 0, // 26 - 'Unused' + 0, // 27 - 'Unused' + 0, // 28 - 'Unused' + 0, // 29 - 'Unused' + 0, // 30 - 'Unused' + 0, // 31 - 'Unused' + 2, 0, 0, // 32 - 'Space' + 1, 95, // 33 - '!' + 3, 7, 0, 7, // 34 - '"' + 5, 20, 127, 20, 127, 20, // 35 - '#' + 5, 36, 42, 127, 42, 18, // 36 - '$' + 5, 35, 19, 8, 100, 98, // 37 - '%' + 5, 54, 73, 86, 32, 80, // 38 - '&' + 2, 4, 3, // 39 + 3, 28, 34, 65, // 40 - '(' + 3, 65, 34, 28, // 41 - ')' + 5, 42, 28, 127, 28, 42, // 42 - '*' + 5, 8, 8, 62, 8, 8, // 43 - '+' + 2, 128, 96, // 44 - ',' + 5, 8, 8, 8, 8, 8, // 45 - '-' + 2, 96, 96, // 46 - '.' + 5, 32, 16, 8, 4, 2, // 47 - '/' + 5, 62, 81, 73, 69, 62, // 48 - '0' + 3, 66, 127, 64, // 49 - '1' + 5, 114, 73, 73, 73, 70, // 50 - '2' + 5, 33, 65, 73, 77, 51, // 51 - '3' + 5, 24, 20, 18, 127, 16, // 52 - '4' + 5, 39, 69, 69, 69, 57, // 53 - '5' + 5, 60, 74, 73, 73, 49, // 54 - '6' + 5, 65, 33, 17, 9, 7, // 55 - '7' + 5, 54, 73, 73, 73, 54, // 56 - '8' + 5, 70, 73, 73, 41, 30, // 57 - '9' + 1, 20, // 58 - ':' + 2, 128, 104, // 59 - ';' + 4, 8, 20, 34, 65, // 60 - '<' + 5, 20, 20, 20, 20, 20, // 61 - '=' + 4, 65, 34, 20, 8, // 62 - '>' + 5, 2, 1, 89, 9, 6, // 63 - '?' + 5, 62, 65, 93, 89, 78, // 64 - '@' + 5, 124, 18, 17, 18, 124, // 65 - 'A' + 5, 127, 73, 73, 73, 54, // 66 - 'B' + 5, 62, 65, 65, 65, 34, // 67 - 'C' + 5, 127, 65, 65, 65, 62, // 68 - 'D' + 5, 127, 73, 73, 73, 65, // 69 - 'E' + 5, 127, 9, 9, 9, 1, // 70 - 'F' + 5, 62, 65, 65, 81, 115, // 71 - 'G' + 5, 127, 8, 8, 8, 127, // 72 - 'H' + 3, 65, 127, 65, // 73 - 'I' + 5, 32, 64, 65, 63, 1, // 74 - 'J' + 5, 127, 8, 20, 34, 65, // 75 - 'K' + 5, 127, 64, 64, 64, 64, // 76 - 'L' + 5, 127, 2, 28, 2, 127, // 77 - 'M' + 5, 127, 4, 8, 16, 127, // 78 - 'N' + 5, 62, 65, 65, 65, 62, // 79 - 'O' + 5, 127, 9, 9, 9, 6, // 80 - 'P' + 5, 62, 65, 81, 33, 94, // 81 - 'Q' + 5, 127, 9, 25, 41, 70, // 82 - 'R' + 5, 38, 73, 73, 73, 50, // 83 - 'S' + 5, 3, 1, 127, 1, 3, // 84 - 'T' + 5, 63, 64, 64, 64, 63, // 85 - 'U' + 5, 31, 32, 64, 32, 31, // 86 - 'V' + 5, 63, 64, 56, 64, 63, // 87 - 'W' + 5, 99, 20, 8, 20, 99, // 88 - 'X' + 5, 3, 4, 120, 4, 3, // 89 - 'Y' + 5, 97, 89, 73, 77, 67, // 90 - 'Z' + 3, 127, 65, 65, // 91 - '[' + 5, 2, 4, 8, 16, 32, // 92 - '\' + 3, 65, 65, 127, // 93 - ']' + 5, 4, 2, 1, 2, 4, // 94 - '^' + 5, 64, 64, 64, 64, 64, // 95 - '_' + 2, 3, 4, // 96 - '`' + 5, 32, 84, 84, 120, 64, // 97 - 'a' + 5, 127, 40, 68, 68, 56, // 98 - 'b' + 5, 56, 68, 68, 68, 40, // 99 - 'c' + 5, 56, 68, 68, 40, 127, // 100 - 'd' + 5, 56, 84, 84, 84, 24, // 101 - 'e' + 4, 8, 126, 9, 2, // 102 - 'f' + 5, 24, 164, 164, 156, 120, // 103 - 'g' + 5, 127, 8, 4, 4, 120, // 104 - 'h' + 3, 68, 125, 64, // 105 - 'i' + 4, 64, 128, 128, 122, // 106 - 'j' + 4, 127, 16, 40, 68, // 107 - 'k' + 3, 65, 127, 64, // 108 - 'l' + 5, 124, 4, 120, 4, 120, // 109 - 'm' + 5, 124, 8, 4, 4, 120, // 110 - 'n' + 5, 56, 68, 68, 68, 56, // 111 - 'o' + 5, 252, 24, 36, 36, 24, // 112 - 'p' + 5, 24, 36, 36, 24, 252, // 113 - 'q' + 5, 124, 8, 4, 4, 8, // 114 - 'r' + 5, 72, 84, 84, 84, 36, // 115 - 's' + 4, 4, 63, 68, 36, // 116 - 't' + 5, 60, 64, 64, 32, 124, // 117 - 'u' + 5, 28, 32, 64, 32, 28, // 118 - 'v' + 5, 60, 64, 48, 64, 60, // 119 - 'w' + 5, 68, 40, 16, 40, 68, // 120 - 'x' + 5, 76, 144, 144, 144, 124, // 121 - 'y' + 5, 68, 100, 84, 76, 68, // 122 - 'z' + 3, 8, 54, 65, // 123 - '{' + 1, 119, // 124 - '|' + 3, 65, 54, 8, // 125 - '}' + 5, 2, 1, 2, 4, 2, // 126 - '~' + 0, // 127 - 'Unused' + 5, 252, 36, 36, 36, 24, // 128 - 'р' + 5, 120, 132, 132, 132, 72, // 129 - 'с' + 5, 4, 4, 252, 4, 4, // 130 - 'т' + 5, 76, 144, 144, 144, 124, // 131 - 'у' + 5, 56, 68, 254, 68, 56, // 132 - 'ф' + 5, 196, 40, 16, 40, 196, // 133 - 'х' + 5, 124, 64, 64, 124, 192, // 134 - 'ц' + 5, 28, 32, 32, 32, 252, // 135 - 'ч' + 5, 252, 128, 252, 128, 252, // 136 - 'ш' + 5, 124, 64, 124, 64, 252, // 137 - 'щ' + 5, 4, 252, 144, 144, 96, // 138 - 'ъ' + 5, 252, 144, 240, 0, 252, // 139 - 'ы' + 4, 252, 144, 144, 96, // 140 - 'ь' + 5, 72, 132, 148, 148, 120, // 141 - 'э' + 5, 252, 32, 252, 132, 252, // 142 - 'ю' + 5, 24, 164, 100, 36, 252, // 143 - 'я' + 5, 248, 36, 34, 33, 255, // 144 - 'А' + 5, 255, 137, 137, 137, 112, // 145 - 'Б' + 5, 255, 137, 137, 137, 118, // 146 - 'В' + 4, 255, 1, 1, 3, // 147 - 'Г' + 6, 192, 126, 65, 65, 127, 192, // 148 - 'Д' + 4, 255, 137, 137, 129, // 149 - 'Е' + 5, 247, 8, 255, 8, 247, // 150 - 'Ж' + 5, 66, 129, 137, 137, 118, // 151 - 'З' + 5, 255, 32, 16, 8, 255, // 152 - 'И' + 5, 254, 32, 17, 8, 254, // 153 - 'Й' + 5, 255, 8, 20, 34, 193, // 154 - 'К' + 5, 128, 124, 2, 1, 255, // 155 - 'Л' + 5, 255, 4, 24, 4, 255, // 156 - 'М' + 5, 255, 8, 8, 8, 255, // 157 - 'Н' + 5, 126, 129, 129, 129, 126, // 158 - 'О' + 5, 255, 1, 1, 1, 255, // 159 - 'П' + 5, 255, 17, 17, 17, 14, // 160 - 'Р' + 5, 126, 129, 129, 129, 66, // 161 - 'С' + 5, 3, 1, 255, 1, 3, // 162 - 'Т' + 5, 71, 136, 144, 144, 127, // 163 - 'У' + 5, 60, 66, 255, 66, 60, // 164 - 'Ф' + 5, 227, 20, 8, 20, 227, // 165 - 'Х' + 5, 127, 64, 64, 127, 192, // 166 - 'Ц' + 5, 15, 16, 16, 16, 255, // 167 - 'Ч' + 5, 255, 128, 254, 128, 255, // 168 - 'Ш' + 5, 127, 64, 126, 64, 255, // 169 - 'Щ' + 5, 1, 255, 136, 136, 112, // 170 - 'Ъ' + 5, 255, 132, 120, 0, 255, // 171 - 'Ы' + 4, 255, 136, 136, 112, // 172 - 'Ь' + 5, 66, 129, 137, 137, 126, // 173 - 'Э' + 6, 255, 16, 126, 129, 129, 126, // 174 - 'Ю' + 5, 142, 81, 49, 17, 255, // 175 - 'Я' + 5, 96, 148, 148, 248, 128, // 176 - 'а' + 4, 252, 148, 148, 96, // 177 - 'б' + 4, 252, 148, 148, 104, // 178 - 'в' + 4, 252, 4, 4, 12, // 179 - 'г' + 5, 192, 124, 68, 124, 192, // 180 - 'д' + 5, 120, 164, 164, 164, 24, // 181 - 'е' + 5, 236, 16, 252, 16, 236, // 182 - 'ж' + 5, 72, 132, 148, 148, 104, // 183 - 'з' + 5, 252, 64, 32, 16, 252, // 184 - 'и' + 5, 252, 64, 34, 16, 252, // 185 - 'й' + 5, 252, 16, 40, 68, 128, // 186 - 'к' + 5, 128, 240, 8, 4, 252, // 187 - 'л' + 5, 252, 16, 224, 16, 252, // 188 - 'м' + 5, 252, 16, 16, 16, 252, // 189 - 'н' + 5, 120, 132, 132, 132, 120, // 190 - 'о' + 5, 252, 4, 4, 4, 252, // 191 - 'п' + 0, // 192 + 0, // 193 + 0, // 194 + 0, // 195 + 0, // 196 + 0, // 197 + 0, // 198 + 0, // 199 + 0, // 200 + 0, // 201 + 0, // 202 + 0, // 203 + 0, // 204 + 0, // 205 + 0, // 206 + 0, // 207 + 0, // 208 + 0, // 209 + 0, // 210 + 0, // 211 + 0, // 212 + 0, // 213 + 0, // 214 + 0, // 215 + 0, // 216 + 0, // 217 + 0, // 218 + 0, // 219 + 0, // 220 + 0, // 221 + 0, // 222 + 0, // 223 + 0, // 224 + 0, // 225 + 0, // 226 + 0, // 227 + 0, // 228 + 0, // 229 + 0, // 230 + 0, // 231 + 0, // 232 + 0, // 233 + 0, // 234 + 0, // 235 + 0, // 236 + 0, // 237 + 0, // 238 + 0, // 239 + 0, // 240 + 0, // 241 + 0, // 242 + 0, // 243 + 0, // 244 + 0, // 245 + 0, // 246 + 0, // 247 + 0, // 248 + 0, // 249 + 0, // 250 + 0, // 251 + 0, // 252 + 0, // 253 + 0, // 254 + 0, // 255 +}; From 3304b268fc62c2a6a42e83caea935a29f9d76978 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 4 Feb 2026 16:02:54 +0100 Subject: [PATCH 074/161] [P104] Updated documentation for Cyrillic font --- docs/source/Plugin/P104-Font-Cyrillic-ext.rst | 9 +++++++++ docs/source/Plugin/P104.rst | 5 +++-- docs/source/Plugin/P104_Font_Cyrillic-ext.png | Bin 0 -> 67263 bytes docs/source/Plugin/P104_ZoneFontOptions.png | Bin 8118 -> 8023 bytes .../Plugin/_plugin_substitutions_p10x.repl | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 docs/source/Plugin/P104-Font-Cyrillic-ext.rst create mode 100644 docs/source/Plugin/P104_Font_Cyrillic-ext.png diff --git a/docs/source/Plugin/P104-Font-Cyrillic-ext.rst b/docs/source/Plugin/P104-Font-Cyrillic-ext.rst new file mode 100644 index 000000000..9a3e6b01c --- /dev/null +++ b/docs/source/Plugin/P104-Font-Cyrillic-ext.rst @@ -0,0 +1,9 @@ +.. include:: ../Plugin/_plugin_substitutions_p10x.repl +.. _P104-Font-Cyrillic-ext_page: + +|P104-Font-Cyrillic-ext_typename| +====================================================== + +A set of Cyrillic characters, as an extension to the regular ASCII characters, numerics and punctuation. + +.. image:: P104_Font_Cyrillic-ext.png \ No newline at end of file diff --git a/docs/source/Plugin/P104.rst b/docs/source/Plugin/P104.rst index d4e36df2d..65e44d36f 100644 --- a/docs/source/Plugin/P104.rst +++ b/docs/source/Plugin/P104.rst @@ -227,9 +227,9 @@ Available options: .. image:: P104_ZoneFontOptions.png :alt: Zone font options -All available fonts have been taken from the example code of the used Parola library. +Most available fonts have been taken from the example code of the used Parola library, and the Cyrillic font from a post in the arduino.ru forum. -The Arabic, Greek and Katakana fonts have all their characters in the 'high-ascii' range (> 128 ascii values), so they can (and should) **not** be used to 'translate' normal text to Arabic, Greek or Katakana. +The Arabic, Greek, Katakana and Cyrillic fonts have all their characters in the 'high-ascii' range (> 128 ascii values), so they can (and should) **not** be used to 'translate' normal text to Arabic, Greek or Katakana. On 'bin-size-challenged' builds the *Full, double height* font is replaced by the *Numeric, double height* font, to limit the bin-size a bit more. This font can display only numeric (actually hexadecimal, 0..9 and A..F) and date/time content. @@ -244,6 +244,7 @@ Font overview: * :ref:`P104-Font-Arabic_page` * :ref:`P104-Font-Greek_page` * :ref:`P104-Font-Katakana_page` +* :ref:`P104-Font-Cyrillic-ext_page` .. spacer diff --git a/docs/source/Plugin/P104_Font_Cyrillic-ext.png b/docs/source/Plugin/P104_Font_Cyrillic-ext.png new file mode 100644 index 0000000000000000000000000000000000000000..fba427c70632772680efadcb8bfea25f8e381a1f GIT binary patch literal 67263 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

f;gL?WLaIrB+`qUY1-duKfOtA$3V$|H^HbQ08{W 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 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P104_ZoneFontOptions.png b/docs/source/Plugin/P104_ZoneFontOptions.png index 037682e93fd12e6953dd1b58e2440d679217f208..d344d19c70633f57529dd150d3265f3b4767c648 100644 GIT binary patch literal 8023 zcmcI}cTkhv)-MEN0ue$lQX?Hg@4a`VD;ptOMWA_S14NGJhBs?tRPX(BbeB2}7H zsZl_B?}T!B&$;)TbM8I&&U}B|XXcr;XYDond1mk5`mM4Pj19HPNbism5D<{*>S&nW z$Xx;gLP_wgo6%2@C+tStdZ@3haU*Y@Gxg?(8$p89v3g2CK;HTHOPDS|&UB-+_0zQU z({Op}V5 zZ>{*aH`h^qGrsol#E4=pWP|SLI7j^vf3YF43W$>;dHQz}||k=Z5xn$~J5`H`Q}Bt(}`cLXWMx z(8p%y5HIuyq$@5$`u}w7xlu|ggf)=-t6fQRNQFsJajoz{nt+Jkv}`3Roq6W zn;dJVf8gUMY%Vhg|I>~?wX;~2hKzK{6a|vKQhs**s^f|XLv!b zbL0b$;!EM(jVW5z<6^}4hFJ0=Lq(F8tq$fxg^WTv%}@6=tSWC`3cM2{+t0cjIHFvV zO+V69W#dCyy~7%4{jO_>)j(xwq>g%AnS9H;0Sy&j&b}{y^U00&HNPRv>xA;(|G$Rj zoV!)+SJPqVYo!Aqwy@!rpuH#xU)3Df{P1I|gBkC3UE2G7q!*(`%96qkZ60lBf3p9? zu|0E3>gIqgMac}cU7c_ISwt&)J-a${EqBJG0-94Mdj$m8V?IQu$w*T100TxJN$>Zm zk-#=~$1NP{M;{At0Rx`;7-p0FBwxut7}nE8%|Ytn$rLq0gX#h%2(j$+#VKV6_!wTfxtl4pYNi}Q*2i;D}tt7+W$yGli z;d5d_sa7i~HR8yh^DV6n&kFNZYJ+DRsx)v7ifR{T_-&|B*HF@S8+;z*d$g&AJN!c2 zOx3ud&g&{IPXXnYY&z+#S*E6{Qgz7{T^?$!P(;_m_qj}56UCv?4_Co8=-ryRmmRn>ZI{SqG$2xm@L#8b(n>RhaAA|UX2tC^+= z22={>DRD+E$k$yap|#Q8-hHm4jX0@DG|Cq6Ese^9tNO^^Jw5Aso&!#|C%MZhH z!C)47D$BeyK z0mT;=2Sdeg)b&U=j|=_rKmMtHAbfdqxTXWJ8}#;W-M${tW~(;>7?zTJQ5Y0o$xb`T zba0kE;Ow9q$N6^yOanwM4{K+IP_DsB(6M#^qV)!Ais{MbE3F zp+Bwc*P#)YMxz%&W6Kn&j!Y;`0{LlnFH&H_s)miBEt;Y+TnAHEmF7>=o*77CewA1W zD@vRL=g7O(3ZrH~CQ~3kJ9)L2PC^SyZD}^H7JxvMp!Pe%BG~iEA2Bz_K)Y|<#oDpTRPy(JEhp}14Ol=zAjJuS2K4K_jLaw(qteuFp!n1{tP2_57 z$6Uxm^2fwq)WEmf!D&a8JR*5?g~Ff0SeQ||vd#a%a$paR9-vNgUS3DR5Dp1T!mhF~ z*n`?O?^Ie`a&Yj>e8Vv%PQ9ZPNFV$zC~;uqn>IW4U`Pr zvQI)n5(dH$5}q!QAG)NMk@5Zhlih&c#{s*BUV7ZFAe?n3mdIdITPJTE*cR}}DEchZ z()kumkWbS{z_gCs${jV{T^Hkulpjb*sw~Lgtat+oT6GjjIm6!oHb$uN|AC{Hm0L7! zAoIU8vHu%pl$B;B0|QS6e(C1RE3v%6H#h(JMVT`LG#dDIRry6(0reE(`xZYukH@PY z5D)spN7}jikP1>z>Ya|!UrM9O-Q}|$c?B(T+WxaACJh6F5wcQLQ{W;9(97R;RcVZC z*H4dla3MPHSUkv$b=k+^)KBBs$*UhvVWIPe!4kkz~KqM#JQtSopR86V=pC}PB> zvgbwv3ObG!BDiHv^F5rRFH+@%FoOeVIA=7hoW(YuNijSB<%F$Xps$LUZcFn-Wxf^-yURq2P`&|W0pI&3P zJ5U6K5|~0Fg)>#xPf=VSnCicNO2w{U9hUhT2QTi`Znq~whAM#>JHwfdp(ykfk)r>p z&pp&rBOOk1Fj72OAKRhiLVVn9lJCOqi2AypxyrUZYIU+e^=J~Xa{l>tGliwnmJ{k* z+6TpeJZ!bCznvkXLU=UNMI&57gFifZIZB$pv?#)Ic=qgIJL28<0_MXn{Y}VxwM0Bi zDkVe1qS~egZc!$k+}j|+;{w+NJ@PQWYNYRcv@gs48ts1?#J3ZsRm9X9|r3 z{zPN1a#}>cf|jF2uMj3GZ=nR^^2BvCCB-T@c8A$6bH+&AuknUO0)WPnD&dbgNdAhS z(X{&^jof;mxuNd0>AtlK-0!;3Ql?(r;Qq*p5HObLxyG!trYT~08&cDRmG0(?oH!Tp zCga__i;FX++(!ANNICNEJ`WDlQRvl$h44EZx{S)!6?%9zz}7k)4p8?{!hKGAee|iS zZECir^LYU=pelYkvjbtm|40x2Mh3V4)#UX!FhjrRVadgwjOBx%;{_Ia_@6vNYds#T zcbEDYQFpE+%L!MLmTGk&&iD+tfUSVI(dXU1c-X&UL!0^fCH}SNJzJ-8# z64kio?le|83ji>w09$KmjQ9y^YzA5z3u|vNIhFzn?Z}F3^}(~5Yo(8%Aa>*9n@fSdjuVXubE zb4RL_nLci_jF!01*e%lA(d_eQgrDzk5}Ux0azYw2((O|{Uq|vzU49|31`tM1#ng3%V28>F?&SoztG)eUB;8Dzpe0(PD8Bx0|ERCgbuoMh{;4P|uIMtZmy z0zo{Dj6hX12NEMiFGHSldU$<=tSGG8T(LL*RsixgXo_C_v`e2*{BEYnR8abFQ%XwIl+pgDUj(o2$8eps3eI zV5RkvqBM;YqpAe|RuTPSav`nyHfK^xr3ykKYW;W`d*vW2Pdg?00*54|Mc;ai8tHkv zrn!HFxnO0eZ)Nl84(x68Y!i&`@w7vjQ@d!NMeoCn8FiSIqhe12;Gh;f^vCg~`!!`# z0wC^W`oh+E?)N;TW~+_x0ez67AzK?G?-D$@8bZjX##IMR?5}$~WLt$TWpA!8-P>O+ zf41J=>ayB5G$6(4)!_Rw|74uhwq^ghGr{Q8%ibAre+hr_DA~eeT4X(d+86S7u>8M! z!+*uof0%5L+@6?tvh?m!Hm47KEflbqF23A9F`X{y=B`T<_u z+HDdTMsshAWE?&3guwg1|CFOg3ajR~*d!^gwAfh=M*ZsHPcd7Mtl5eL|5QQn8N^L3 zcYwDoh0K$_KcxYzXi0}D35oL{y}Dl@P%W_F5u(WCMQ%W}iE(13uLLV^vM8U(6mCFk zQ^N!<@~oP@?_4*2fE&=;{e(JmYnyR{2mM3viNtRsv zKnmZ(N-T-!i;``3lAH}?ST;!*b2JUG0%m>TC8Lf|Hy$rp4N^`QqGMel0`v1z#uMVLm-TkLTaMqvbB zmP!jzxaciW25H?tO3FQbsvRigf!QRnYt7cu`GK>VG8TiGGLXc&t<2BK|5s(&nySNM0;iqIVs z#d4v`TYQ5ba<^M+klB>@<K-4LS3nbv$p@^9R{f zninStW$z>%kp;7oYu-2+ctKd-gMdQrx1aF17neA(*9-}sYg@LbaD%gW^8rHAy8QC} zp<78w`h=vT%6o;bGER-mV7=p(J24X%nbg;+D+IDV*Lxeri z1f2$FzZ3vZsS8u;bUYjub3^TwN!+4n;&w|DW=gVr_w2PchUnXdD>-kx;<_CEj-Rkg z_V25eEUxM&;!Si5p}A;-n~i!}82cT)ltJhSG&%I^-bu1nVA*wKHG6@x7W~UnnfxKX z#cDxiJN>S|XQEf2y--C`ko7A(U0zx~q_;K{6(_7R20{~)N6Qr1A_N$|Vp&VZlDDlZ z@_mdUe+h>Y@FmrBBBQc znhYDC0pmi_;dWwGqH}B%EB?(ZB#9*_`!MMYlrnv)>*USmkLdmcYWz;{0zTp7B*&E9 ztIlbw)bYKnj*nvd^J&5LegyHaR;)|+a+&aQVyMv01~Y1OYp>|s!We|wv_za0V{SiV zp)g7QD&~hQl^1DWoa~pGTOMBq&HvF^a{)1GF zyy&ZzTc?7DH$}!2Fin$5K`ZjF6ZuTo|5{o6TQc$Q^!Z-}Yb*e*zm;K^aCee`qc5W;o&7q}LHH*m zlfZYG%qnAx*o)prSptg4-3rElTPU7zk=c7QkDP>_*T}0$WRobs-w97AxsbY(Bx?Ei z8E|NOs{N@+!M=6QKwg^GM$h}J-!tnVr#gvK|NP3p$X1Es-ZnTZo3;SBSzIX4l#$Hn z!BNfNKawG3FUGhk=bi68_5Pa!J^0QRkK`Z5!RX5}KSfbD2xH?nGB}Zf3OK8S8z3MX zsS@Peg<{L(dA=S0S?N{3s#uAt=Ye?iuD{BT6D@1zB(+%j5%58?wA^s4tL1}Wd$eDd z|HxpRBP-mAY6?Wev2z%A3-)Fl7(4nMg=|XHOq`DheLMn&SM`&8*e?|+>coL!-aMxe zi%th>ad@Ir0rAEAel#S^t%2^SVuWsD00Mc&jOwNOAb=FRInTQkl+QwEjEcYcn=;^8 zWcG|jRs9*3vG@AV$PF?a@1vU6s%Vg$SDCbt*!7tuq#jE@V>n+)CACYm@W{hol1SqF z%ZHvRXU>-~-<>s49}(p78sbr5r;`jJhRcq7+9}03)thZz7fR%jwS*B0UJiTpF$On= zRb;yqyq&M+bClA!3a~2$S9DF00YxjT`WYxdesA3frXr=M=D*9JKaFvF4v!d8AU7oB zHQ@c?*t$Aya+v;8kLn7~Qdk3Jql35rJ>ltP&C;H}s12h2old2HM)S+W?#5&ky!RZy z^LboW{XOIN3VrXm(F~4E+IJuQ{=k(vIXl(?UlC9r)HPCWho~KT9=#yP(U72mK^Mu%7L{5_Ec1=nT0xsS&KjHZ^IGG>C_jJK+aXHG~>?T1!M|M2%K_$5=q`FQE#8v3R=xkpEPCnY_Ma5l&EU_B5gN{!ZU?(cN zAJ?Q{2UEs4YreC)gIL8xY?(oiE$=S#v;8~1u#>m5+6 zx6KCG(y|FsH=H<7-67QH;S=1n|U9( z&El~_+rw|eF=D0w`g%45WXjI_)nFb~Z|~7fZi`;TBPCwc|MqLdy>dQSer_7M0PT*b zF9JT`aB^hSML}k&A{Xq@{v|-It-F&q-ODs)7Y@Nr#h}DEjxUD1yR473e|7Sbt#z)f zhT0h8a(@`M^R)CE4j5Gu{U1QmCB69l?PXUKfVxI;P<^fBrZHOcG!@VflQp=BHe0%e zZ&^dA(x^^F+22ckc0qK=E(Vvu6MJ$`$WcR|b!9^H zxSJcW7+$?pNe@Y(3WE0BBPn6eg!Xxcjg6!VyaJylWe4=^4e!S#4OXNsxfTTp!bf#c=-+oFGU*&M7M z6i+>TR5C2+d&pg1Q-hU-5sR@eAFa1=+?dZE7Hm6XII^}ElcBA5zf)OG!umI~{DZv} zMp=uT+c#|xSq`L-(kMP%>J{goR=b$-x7yu^H-Zjqf50BLPOq+3pKVB`-nsdWHKQwJ z_I=4>ds~Fn1JY|DclIUhLJQkH#zc%)GrC=u` zh;bPFej3UapS0#Ml@eZ)fE666t5FA8=UC)ASR&#s6ss{B)M2XQ?|`W;e(zqH1}ZO* zLCbbq9VRDFcUM@{3PWf`w7v<6- z4_oO11gp=s^>3BDDwz}L%rVK{OtXU+fOY?^@A{|#JCv_%@tjg^;#;s=aS_(L_2|LD z@r?IpBLcOabD8U$xCeTq-#a_&cK-9#-JwIRvlHnY<)r=J_v5;nh8mS>h^YSo82t4; literal 8118 zcmb7JcT^K^vj#z0kY1(NfCNw!klvegX`vfHMF>@jlmx{@MY34(|&zm_@SAYb(Uv*m69=RZORO7Z!rO*w1c z#j-+!F+n>OO}99WOV#cTeepIZKm2j$&K+aX!2=3W(Sku^meiV9^3< z5t@Ond!Mx->1m?LV$h(_zO1al8BpI;NL=FYw?j>D`QYO)4xfM_RprWt$+=qGwtFmx zLWio%hBl;TBm)84fZDdvJ)!=4dh|9YrJR{vZcI-!=?cg$iKX~aMg2!FTMO))2gRx~ z=~oUGrsClYp_aGdC)=HaM`HNS3XXf7%A~9BHrw|@h_&Sz58kd4vP#UAps(kEpvx~c zjb5rcu(F2lUS3;5NyaR4N~?yiBGLKja)tZ%PR+7q(mJWWr$xw|BjQcY^W5|kKr4IU2sPO)2Y}qC*6qZ z;T)WP(W-MvywPp*$!+p$H}Vzr14U)a9YoUxV4Yh=Xg(uJVAi7^cRT22&qPhPdr=Zf2b++a@17PLvXRFWa2l_Xr{j0jU!AQD&w=b|E(AYzg%)<05zG%;xQdVGDcn@60* z!)=bDRCQ&1xA^oF-n>3K&y{S$N)zv-n%R0{D@o(@*3aSUdFCc!e>b{fx7{O#OH z9n;43iHS%nQVA9fUciTej-7(#aK_XGPR>o+seRX0xUgyWXFR?At^8?u+4A{UX@rI+z75g zaQecos-w+KB4jBFI5U_jKNvJ6I=_L#WFW?Vzu1jTEc^iE-dIq5lA)bojz{km3}b?Lel^Fy~)gXGM)_PoiVqlR_+Cr;H_XRFc? z;@Ye$mxsp!x|5d|$L)*?mq`QSB z!`Eo&OCN=qlcSJA4!^xUBq6 zx+MImREcuo&*n~Tg~yzJ3QaSe`-1jnKWDXqo$~iqUxwb$;l(yoZ$Ak;Ram!3tE|XL zpunZ-hTp@$565g|KWMO*e0{zaw~E<#z&o%3;J^F?D8%lw&JU5Ddz8eDu+LaY4_dX) zk7?>B6-pJJU5uWV*7HCd8H4ytZQCoQp?b${2=|72OQ_)IZ0~QKt*Kp#%dBhm@f?3Q z)gk0G$$twjZ`mC=T@Rmj{Dr0NQx9j67J@u}{TCl`iN)>gjHjzzoGibKS0%Vdhs>U@ zd`>6+^f^2Vf)lgS!`6Y7nK%T(eWK;{66%FG{HLZlGU9y8dFMy^-Na9P@Ds=&C0{st z*5RTm%>5{z7(17}ByZX@rKJ}1@i0h}<@3|eUH@`q!-QUWUhL=SyfOQAJRi~ir}cPF z2oa;cZYzd95p#NhZMXU@QwX?`v{)?Jh*g~&4>(^`|21!6QG04PZmM=( z%kgF?OKA;$6?L#q*OjZTT?ncA zr?ZtL(+3b8>na<$UiRvm?SrfA&Sa?8?b6ycw^oj7ysOgGKdsYupti;>;6}#4x4Ih# z$`eBzqDnbHT;0%CW&t;qck%vf;QXwOVJmAJ(5>y~$D0#JMxk^oLrZA!Spw;<+b6OYBJ4i03_Dolg`(S!nQMu|k3R!%O} z1_ls@o0gR*0~v4|PMWm|7FiVQOs*5^N(?8O`!Mxm8dRF$XZtyqEiRBTS$a++ z&ov^bH(XXR1#BknQ3)*W2xH^Kb9lFVi7DNOR0cVbyb8nkmF&@j^aQEzta6D5MeE}E zUVTq>2Od-?&UOM3Xoo3h0IG2CP&P2yGW0;6_~054WXUJ+HAZyX;OXMC@=tDTQ}GGU zS%Spf?%|XkN>$Sf@X|EXj@6ZAHx({m72RzASyUtcdvz5w1fLJpGi#2MAw}FYuRuB~ z&R&04^1kea2Zz_lpUN5`&6H1?@hES5#mkcz5LD1tmu9r@&Tqf8RP=ctV?B|>lBK<) zLg^P4#`*WSL$ES(7n${nFEC#{i(822?am_}&3b`;aLgjY`0z9LA?yq@zxGHTb+*W9 zGa2&s2;euyX+&8P&?XdrnMk+Ne%JvjoaqjRbKQTp*w<+?vf7jGgZ(IXf*+>mynI*C zs$dB#A%pNymF}%ye0wAr=R8t*GKun+Arx-iK9Qk&e_4;4fSrxq5hF0KNW(LqwAX|G z;;ONv-BsiBHa!}I&&&6&)rMV+M~4X3Bsy`b<*V zT(K9Wxg(@6XscpCqzy}P$e zd-4y|fyWKTUyY32&n0_e%A&{+2y^#frZD+VkEN#TYK#}xSR$C`K+qXS)2e}o^dDdM zTM$8^XMqmdr?}+CeU5cy#^Pn31dn*iZd`Af{^WAgz7EMc*(*iWG_-%7`pcKa168E6 zgwCK3G;wI}Q96hYCS%zG(yy^9i~Xp0p(v%yC0W)+!TWKHI&3@QTL>k+wK#NwlOgKb zHAT)wg@uKkyBGqlXh_i)jfR|R9nc>?D_+iFazi0;oAc;0OE63>9%=Qo#0Be*!T1lt z9We!j-}GSJ(H_M8ufU3mN3s2V8+>flTg2742*dQ(fXw@I0z4Q@P|T0_NEPpb?~AXB z>Sfg|Plco;%PzThp288{5HDh!@yh_0el+ahntVt}E)B8B#BFfbvf~zZ^U>(4`<;?{ z@{Z`Z;20^PqOkPk%6QMQc{J@o^VWHQ5(D}Z!^n7AUYy*b-lIbd0e|qSD1w78e3w4z z8WoSPr4P?HyM#zu$m?)FE^I#uIj}U3M_~w}p`P4>Sxe_ja^X`Fi6ii&B#pGo*;+iN z4LC!A(;BplRfh2uNF>>7T^@O@QgrUD#G`wVhPaS#WrQ9qTW;>_m!^ugz0@wra&=k| z%B4pQt478$RTleYnOXy8F?o(P)W+fjMU!7FK@4=&^Rxmy>xPkc9+!*h`uc7p=J{L* zd`2UTsAvp(5xM_cc{hw0qaSc3%;-`A#8TUYGOIvdq`&e=2N`gDc`?XrjgsrM;?;b9 z8+x~fBdL8s69?PRlQ?W9HRz;}W3=YqHCL!gZMa3uhhz>v7}k8M){gV5O!`6n8k2!% z0~QWx)keINVRKQ31oAC3In>@Wi|@Dpo`KZax>IMQMP;9|QE1mpRG*Bis}%!Vsh_GA zUZyg6M}(gy@NJ*?+$;{)$ob_Lr(0T4_w$0IsMzxWOO7285-8&O5Es7#GZnSxFUztU zi#$;b&!xEbG34OT>J8dlq27BI?hvmkdZ&)V@$hB$UxINip@w(x*HVbI$5SG%PbklG z7^Y`S;P7S!I)hJ8J)YKn_G354p%Co8Hg4jB<-*auWSKn9mw2F41Q(ti5M7Xe(Zv8L zjr+1g){lCLlb=j7yFrkwNBAMjfBw3&7_ZQVz8?X8TTv;%M0uT>X*`)Znz0_p*;rIT zGFfProP2M%m#rQEs31~eukXKXB5N|y56rey_$qWj?dAYcg0o1vMzOgoKrC?XD-WER z%XKIJy0KN4p_`P4{s6GIN^`en1Wj75=7?t;_ZgzH20=luS>+_3&`bh_poZVrp#|uy zY8r0HganjZZ1-@r8nZ-VcW~1>#Uj=%5#98u-Vjx#W@+;9af$^uT??bh!5Ng z9u6#Ztz3s5v)#!+m%3iK0?A&3t}_MjFBkydE|K3c$t?JGfXt#CtIzg&+i5b3_5OZC z%N9NX)fB2p^txyTN<)t_1@g&mOY#_xzi&f*i8+am=?A{+0~q$oX+Wsc8Ls{~ssB^y z1OQS`Z4W{hb?(2r42kLB+gqzF5@#btz@n_Rr(}%anlhYDlgm3gGf&J4?+zmW?H$((U+5#oiyP(hu)-|c*i?tY50Ut zjZw}T_UQcHKG#m>JjrQCawBZFxx%JlqTijwCyPI!i|~a))1Sj&@?h)X>X9yJKh#!M zQU2BT#~s1+1rCRy=~MF#Ho*XZo=I%BAmvKyGj07&cZ`R|gVD%6?b|&vpZx=} zT}7Z1>&W)_fo-OGPrq=qoV-9_>R$wTj2MVxb#%R051T=IYKOdze^X!{Jy(P51~O?o zsZnn{v4k8_m57W?q}>^y!AfZcoZF?0_#g(^<8dIrk(X)2=*B{ksHN zR_7G7<2f)N;s%M&%+>BlXOL*Cw^yG;Bor7AA9;9uk_0uq(ZR+g;TB~<8v`epHxus^ z6XH7pE2wv_BXm~Z$keIY?GXd0GZ`w}^(N;Igevk(e2u6Ks;)ss;$J8aALb`A1MH<= z9n8NnA8`qcQ~TH7i0t{GTUl>DFCM;mjouBYa|_A~3b+Swxg?1-rvuI=(1Zvobn zquSj)*cjOfnC{m9VL}!uZFeo837;y7hob@9HvZSQp&N zp*gZO^+j3HcG%IXCelNI$&iKga4D|7B~(CH4_@<#Tsf$^w3Yx`NoaZ0p-;LQW)J`U z)*ZpKS1@w>m2@r%M?7RY3;Sm?WLwI~LUO^%rFboxk)UW}ooO1HrEvEd{Vrg7*E<<+Vr0uFDL6lxpkcDDiz@48dOSI)}={c8`| zEgB)God&umNpr>Nm%!8XQpj{lpHPUot2}8xP+bRa%5|UPp9nyVGl$zwCb8jejNe)7 zJuQEoCjL+)d8omh)w)}k!#YUuA`K|6fv&u9RsiR&-M}Xujs-Xh*5bHav9ppEC2=n@ zVu)<;!U*;cL*J?F=h3&-?lH;HPA7yzEIQaJ{8e<`Bwu9{CuNmjivpQ@QbBN**%}gEvG%X`4&MMjkqoZ600-@bGggW)0!c#X1uU`R50K*Un%S3LU2O0 zHQ=0#!1o*{fOQ45XUz%Wa+;Ge3=gOWNZb`%3$NW}VxZI=6$P7E*$CIEC}?-k?dMnO z8Aq@zR(*TivznIa9_3mQza@yNZ|E5rZ;w&bm*Cp_=aSR% zY6kyFvHwH55)AdWmbbU>-fwN9PHf=dr#u(ylro`WSNfYS#QAm?DVd7GH94Xq-4*;1 zJXHI%jFtKCiwLt3uSf-+>9W^5(WTY-w$PU{vBmy&&(w+UM}P);F>fAzAur`E!DA0G zPo}BI+jh3`9|3a`OM9=t1jo?|b3V!L0Unj%HUy}n5mRE$D%p1YaN}EjNNM|$U>EBV}E37SC>vGtHVK=b;Oa_sg`pIOMXVjIDK6H#A zR3U2&Bij{`L`~(nbM(ey1^j(oGAd|70=4Z2MuhQoA9Z1C z!J@79Wy?F#^wD$gN*^O0+XOCeJtN`9ogX#Arie_{{6bV=ze#>S6-3=F>~zx?gVH^b z^rPt3|2dpO3qTI$&%5_E1oqz7#Woy{! z>PK;qK&FZCc6T)U?Ea7%9)8ulYIZ)~wAuN(mW@IqpbSgPb933msUf&7gnBCAUyb4G zn34#w5_RcCu8$RCMqVt$$cE_6wxS*tIW>XKI~C|L5B~G}UG06ShWKs_CgW*wDC%udyJ`>iKp4d8Hv{yxd`t&iW9kb40*-+7fvEk@#&XAjsVOij{mi?P8t0SW+QbRPR zy?bTW;PVG|X_PJRpA=R@6jqPUl$E>-8|`oG-Bc$IHrMY4!7%FQ0&cdtOma^jl;x9< z?%+jTH8~H1qm-4DR>h_itc(GkUD_en@45238R#-uU!IF-SDk- z?qJ4GalH<%*`1+hcV}qC9(xdsdi}LAd(tL|m6`j7{6N0jn@rW9C80}gqxUY!-6;6Y z3K7v|4TzFh!HDm4^hRb*g6jXQ(f^@zxnl|@nB)QU1Z6@(b0usq^GiI}gXcD!(zk~J z_g=7vHrrOARmFyB^4NZS_{J^D5B>gp&Oasfe1QEt?z)0_nc3v^$E+s#XikaZho0GA zKX`cBCZZjyBR6t$j>e>Eig0alf1AbLm7`bPS$zh?wJy18gC0!(zF_IA>%Vc7zqV+% z>h)LzQ!iahSy{c2cs&L!mnKoX>F)w*EsIWOteb5y%^Zf4gma0JA1EMv$h?3kK=cKT z3pw|*WOC6LyHv~o4k}M!``noE`oP-xUx%$~eP1ell#oE+2S4N?YD7ca!Tu$irAx-M zFN|{9#x`|OrJ$S8XVO9y4LbFf#2f3{zt)J$;}eX1eUIHBnTeFf28N@D&WtPWEMz0L zA{;k)0JeZWEgNv5r?+#qd~Hkm`hL6Rd%0^%7}nytsUz>Xgc>CiJ^=q%Z|9Fkw3qF zCf^bP@=9;lc}|p}_tsqAupTvr7%|71eP-!k8NpW~uLnaiH_AW$igOu`{lxpYmO66` zu5Q=pe>u9o)t8v@Nx7@X#u-+}cyw`2+?i_)+d;*v-qf31{u3&9^9zmn17Wbx*YJ?f zk~SUKZSlBueA+sVmteAXGO4vmsvw-{E2M(e?_LX$UeX?baD9^ zu@Zk*&s8zV5+NUcbk)LEis0G8v;0#%Qc7w@mRFEcMf&vttTO%Mr(f7em*Y^9&(Le_ZXiF*Um0LXbQxWbh$WF=8BDb1M5 zmM>oA2)u}z@eMtm4K}=bItkcfH|zamYi-keW}8jcy4Nq!t=21acC}?EjG%oPylEOi zuvkn3X(c0>_QdoGFnecK4#|%@Ybfb?8-0^g1i-xb-dUGBc7F!<&h*~6P1&xnuE^E! zBbhV_%RGA~)Jv0Nr8aG#^F&7Xg~% A%m4rY diff --git a/docs/source/Plugin/_plugin_substitutions_p10x.repl b/docs/source/Plugin/_plugin_substitutions_p10x.repl index 9a282e375..19afeb638 100644 --- a/docs/source/Plugin/_plugin_substitutions_p10x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p10x.repl @@ -73,6 +73,7 @@ .. |P104-Font-Arabic_typename| replace:: `Arabic font characters` .. |P104-Font-Greek_typename| replace:: `Greek font characters` .. |P104-Font-Katakana_typename| replace:: `Katakana font characters` +.. |P104-Font-Cyrillic-ext_typename| replace:: `Cyrillic ext. font characters` .. |P105_name| replace:: :cyan:`AHT1x/AHT2x/DHT20/AM2301B` From 3344fc3c9c1ceaccaeb3fa1bebeced7e54ddd36e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 4 Feb 2026 23:06:34 +0100 Subject: [PATCH 075/161] [P104] Updated Cyrillic ext. font with more elaborate character set --- docs/source/Plugin/P104_Font_Cyrillic-ext.png | Bin 67263 -> 79028 bytes src/src/Static/Fonts/P104_font_cyr_ext.h | 512 +++++++++--------- 2 files changed, 256 insertions(+), 256 deletions(-) diff --git a/docs/source/Plugin/P104_Font_Cyrillic-ext.png b/docs/source/Plugin/P104_Font_Cyrillic-ext.png index fba427c70632772680efadcb8bfea25f8e381a1f..4a30d316c6f64200ef30bc7472e6f3cbbe41ed04 100644 GIT binary patch literal 79028 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 AmH+?% literal 67263 zcmeFZeLU0q|3BWTQ=KlIlS&cJ=}Hm{$3-rtYe`6jaxq0w#)Ql^HmB2tvyxd7t6U6o5yRLF!(5Eo*q8{u6J;cC*{7~5J9g0?X7y-4_z>L z#Mr>kmw(*-!!MVAOpo`R6bD9z&%D}wZFISo?GC~lt=a$H-SKJTLFjYf!>S+u&wbi$ z5G|XU=qK&bDy-f0-+5#jo?T(|omqMI@_+ki{+;*JuhDnsq;xnh{I?I(8}Sc!_uBnt zADngR-#(~c(BIpoZ{GRe+OXSs^WQf+D&J3N68_sqwQhm>u7oFxKH8u{|Lvn^byNI( zo0{*tH&V_1?c>4{l=a7reseF}bW{JgkD~e14gVwN*(3NLF@Iy4ng65B-#C;18=MC4 z&?^)X3B(}a$7Y`X@M&kJO$|HBA&qGfF+TWC;Gm>Q&L<1_nH;6jFfNDO+1J+Mmw%=? z-I&3&^>6)+n5F^mcc;SAG~xJ;kz2jt_ZlVR$MUq`kur?*ZfLI?)y>p?$4GI$RK~xA zBI#ojj34uw<#7JuW(03|s!dNKyYo@J$lM;C!;|U5`twgT_tG^}bexIhCP%3Pj6CRW zabNROl3KKb;?>D@+jE(l($Y#Pye>Yt7nS^TGPBcZMU>BHR!F-# z7?>X!U43nRD9)1D+jzPE%a`n(nnM>PW9O0s<2zp5cR0Zv4O73 zc|4WHCMF}rJFsNBC`3FW>FfJ+CgUhrd)X4$ZwFC@0>K|a;l-HASHtyoh2ocfzqI)z zB(R5ANvKcx6t>@c-6DjmnTA$V{@QR)tc3S5J1J~?G;TfqM^s5t#phT7FQ>x3el0>s zX?wF8A2aytC!XN=wf^+puGRIO$tS#|FPB_z_FGNm=L8fDMNi7;FIu$Ei+y?vw^d?f zUEy*`INovM7+Jk|4py`pKT-U~Xbzc=sG6V^&R^YnWib#P?1&2YLxF1erRoI%xX!#! z*a!3N(w)68)%L0T}9pO_KqZ9oMT?S+8_7nY%jiTO@04K?`E_x#GK|)ALKRKCTsW7 zVZZaOFqZEmck@Qaczyfax&CSTo!LCNN?e()hv|ZRnlh_Md|_K}FZxt9|3Y5*>)LwS zp5)CT{h^4n4>%iz3#ND1EWfNdCB|Bx5ik#|K5liuBX218MObnlCKi5bUWBRrXyG4t zFTvEv_q^J64?i1fN?D=_3VkY9qJE2O-0YveBDZ;?a7T;uczuywf=WB`zFUL`Nf>4L zfVW((8`ey_#QVw2E`2r!Y{%cXNu13OfBm;lKl-i+9k19W@vOnfmsn4VekMmcooPw) z#}9FLwoL9LjYTdcPMft*c(sD=z3;_Raw5GMjfD?t$^;$b-225AosA9RbK#Hel>65X zs^qM|rZ%NcmGF4Xc4c|(Lz8~B3GN?y_&y;_jTL!OZ?H6N*vCBVQfT5vTh{@iH-d4y$c#=UaEKQl8(1c%bCMtXbSPh3#KIv5y#k2 zMpiYzPrdJdR>rqUaY%c2Pi6B>bh2rD=gG(znbTlV6nWZUV2zL7@VE(B8?$DTWUN%g z;sm--Nu$dwT-Jq#`*c$}zcaH>Z@+l%p-o~Tqp(0Jg@5iaKHO0a{#J3fyXwTD<_jlC z%P)_jwQkT};Tg%?#~e=FRP~b+R6z{;>|xBgKtzpx-*xrmQ5D1}r-&i9QfrSsE6WR= zGeNLi9jsL%^1iSp)vd{XgD8Q&RZC=)Yv1v{@_wsoj(w@IPn%bJBYwr5rae`=vd{Zp zFO%soKM2+=s4@=~$Vv(OzZB`3!RMXL9E*9GWo$<$Y>8JRp5ToPHriU*d0r0V|KVma zwQSuIrNVvqppMYbzfifO%TppW?(;SZhcapG1qHA@}R}*e$9gN~=gnhdIqy3|&#kX)d3r`)!mv0y34~{K$r&MQEJ0)-SwE82UnAZj zT%Z1~deJzASQ{63bZ7DcVueZmr=dMn*7I|!1ocT)_M2~*fjgvLRC%%E2ug>@dF|v0 zb4Y(o$>S$k1Qwq*4e+e0hyC( zZX2&F;|Xl#(zM-v+j3KRIHr;jWT3v-~vP|px=MAxK<$gL)qxUy{)@OdtBF~#`lNHIaaGbfl) zzX7e+Ow7Gr47kPgdf47ka2PmN&OThh}@P0K&xS4am>?Gdo9utf7m zUhltNp!&hnooqhX+q$>2*+7z!|3-&k@mp3?hSAH3Xn<2jG_ojjPWms)DU zt&o*gf1ed8&)H0E@NVl_vs~qNAW=MpPyu(w*PXqy5Zc{eJKYxs*S2usqEjg z_$3DvdRV&S$(^&$i%P@VY*)*cO?}?9YbxTsL^lYrCbmDndu-_o=80r@+J*#tJXv#5 z$UYQ_{pon{jQ`O#wV{u<)5^uR$UE-lgROk3Bpm>Tb2xl9oP{RApWnDmfpFT0-Q^Ay)T}jkuc> z6HZ&+St9R=YjyHev#47aL^Ig}=@FPIl9T!o|4t%zyRB7O46njIkv|@ zV-enq%*0QK-$oLKD&JZ~O0%Mv_|fLtaO>TgoET|4x^H6Xa5UqfMmEpwK2g9b=y+2w zUsj6vP;nb;8?5sH4a z@BJm84Si8LT+V?-jt>mPc)E{~+3bk^1KkNjGIk&##EE&M1uSksys1T{=9}X!U}dQb zJzJ||uxrCLV#9Iz99@jGPjDHYs=~jK3O?!Dm`zDJ_|bR4-=Y=N_K{30c~9qQRO@wv zu43How0cn_bG)l_eMcIRINoyOqn1Fb$&`zo82fS6S|fuN2UDMlKZth$Nd%Q&(hmv$ zs5Rn+DEr`c{SNOE9Kk@n$qKX58kW%45QiT*^wu__nnu@l8_xSX_#@LTC@*kd`=q1hv1I=0DAz6A0w(&#r&i zu{O}^b01jF&vH!G0^V{}Dq}E?lidb{&74I*I(6QUwD4ZA5g0DzT;_^@aR!T;lM?Dp z9*)$H4W&&t;iGL9gC!>IzH2pKA$vrRWm-+wy%DYwhBZuyJe4Gm9eP>lr5c+YP|)({ zB+ufAH3{%s72XZ(30PSkJs2Cp^QH1{a9S7R{piQjHb}SZN^559Pyajo<$?}M(ikz} ze4)b?4;=t=;Z)Lt@A1Ki{6kSvH>uiE&clv!akkh#Z8bTmfC8Oref$U8K(G#-{#2~f zm@6Hk?oM-s)n!sNHg)IGxCj0g$3n5<|bx>Y+>X8YM5Ln|* zV^xy|xEa4_HxW?Vii(713;8Dyh?W|388~vF$P<=B<8BWevR$6Rp=13QD^(`l`rw)c zsdA5rBNtK<^CW_80*~;hxu?3lb_KQWTXFq|+aTx3i@wLBM^&T$$zB$<%%0U6igOyc z_G0F;JCfujyVDX?B6&XwO$Mp!fl7?2#Ws#zGr?Ps-5J3=&agE&&_Y65Oh_t&fzEcR z5*#0IUI{Uk}GJaMsjgjL^jqHNQ&ElOT6#U+lhUl?nb2k3k3`cfP434X@Rr# zndxDCHKZ;CXVphtNwR?Kje3}T1Oa{PoW$z`KJ6mV zz##*82>sT5&eDHSfy$($Qsk(0PA#<5aPku*a8nYKHM&7rKvFO=;;GnIwgH%-JCnoO z!D&Iz>U0F58y$3ppGt&|#1-I62j!v<66fFHhJT9>7OgTdf51DUJn^_jL+v49&rTrQ zE3Ia+W0gA>$!cn1B-do0*h5?8=*?6)I2>2{LCH39qijI>kl|;!hB#uDkhuyRl+3Qi z=wDcCG<=(3Zs0*aru2&SQm|W(NafDjn4tdIYnSm_gm%DMj#yO093XV{52Mg|02jPT z2{I5(PFAhLC9;RWr=^$Sxh_b2LBHY0%nZZ^wu$V+&cB0ddz_p6&qd=6{j^2~Okc}~ zuY#{lx<$1x*8-Pu7pJe`Xa!C`qqV9dUlaJ97#Ho-PA50bV!|tz?1lVK2;ae1z-ibr zql>QoFC58?RXUNI0%QF68>EjNY#H4*c<{!ojvFz=SB5~z@;3}6S@_n9J_rJ@fcsYk zk?bx`diGyA(V%R?I^LqR2_Y0h$EeHE7fVOuFT*KPuO=E{xV*NaxGg?1?{s_X*0{Zz}h5u6v6yze!--@m2z|lxdbU?`U#pc3edqTTRbccI- zjGiHSJt1WPYUpbTBK|+8Jjp4gZw-Kj{u(cy`x_*KV8lbqAHYwrPz$t>D*-H&4J8_5 z$*a0_S$Xv1nq|XgL&>XU!^5ghur5o;N-+pak>l@hT6o;ve^Bh+?=|eZ@@p>ZL$-uL zKzR%wd&>(-pd|Ez%vIU}zKZksP_4=t^wKDckl$&2^)WM3g*xl#NVDJkcMJKSu<;WY zzk#ero4i)zGF%&07bE!r@|(KMlxQksIlZjb=i38;^{`~yA9H6G;y%IUMy8ZkF9uN} z_20y3^zYP+O(T_0ERFYPE4*h(;GX!&u8wWPPoIJVl0GB~ZcQxPe{SL@XjzIzgxnui zHoQ)nyam@e*d;Wo5FMfcQ&lBvKnvApjZgzN2(8oB{zG1>#_c1z&qUB^$hK2SqiV6A zM-;Ij)>5gM@L0tg7ca9rX4`U%;e$;%M*`7I6q>JyW|YbN>@fMi$h(v=rgwOsmCI(* z&6M%V@)OFNkw6F=OkuF zXM%#bRAF`h7(Vl#mlm(3C}T0}z7TMG(&}}l0uG1*?W=bG<>LxlPbNY>jwo;vz2S03 zybf_3nbGMal8F=f1j4s5W0kB1BYk4M2v{(M8K4Fn<3GC8-peRk+umfyaQ|2+4M`Zh z+qMh*^kS?b4m{@kL_QKxnhYNte8ABSSBH+@4q^qxu)d^tZDNygRO1WQ-j0rpoF2Sqxtv|XCL+uJNaQl7q+}IXF=4ef8NvGF|s#KX8mt0v2|KUSx1OF&5$MYNS$ z^!TXwx9t|^60$bWJ4fYWI$0;G#C?QLRP++uTVBnZ8a{i)cL?j5u}Eix>9P#bVd-+Ia0n8vS2;0lpi$rlpf5T6Lc^ zetx}-0_xdSLtW{0%gb)5toa$+RuJv^URT<*xCLY3X+gLSyS{o*xEe2Foc~>As2!m8 z69daeeNZsYzJyU0J)oLf{`whgrPobmzOJ82t)DMdX(B~sEbeKpto_{qY{UHK>oHBY z>vh}@Kh}Ha3-U4A!mQ0<`jYR*q!urTE%LXtT0uZt^{q;MVxY`LEx{G$pze%n=}ccasli z)+TJhT`^;YEpz@coE5eZJ-GOJ(UMhrU|9*4)R)ftds*g=I!*0#bG!5-2#N7XA^9R9 zVGHf>p_J!grYANx_jVM&+52

    f;gL?WLaIrB+`qUY1-duKfOtA$3V$|H^HbQ08{W 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 diff --git a/src/src/Static/Fonts/P104_font_cyr_ext.h b/src/src/Static/Fonts/P104_font_cyr_ext.h index d21abd4a0..4f28b4863 100644 --- a/src/src/Static/Fonts/P104_font_cyr_ext.h +++ b/src/src/Static/Fonts/P104_font_cyr_ext.h @@ -2,260 +2,260 @@ MD_MAX72XX::fontType_t font_cyr_ext[] PROGMEM = // ASCII with Cyrillic extension { - 0, // 0 - 'Unused' - 0, // 1 - 'Unused' - 0, // 2 - 'Unused' - 0, // 3 - 'Unused' - 0, // 4 - 'Unused' - 0, // 5 - 'Unused' - 0, // 6 - 'Unused' - 0, // 7 - 'Unused' - 0, // 8 - 'Unused' - 0, // 9 - 'Unused' - 0, // 10 - 'Unused' - 0, // 11 - 'Unused' - 0, // 12 - 'Unused' - 0, // 13 - 'Unused' - 0, // 14 - 'Unused' - 0, // 15 - 'Unused' - 0, // 16 - 'Unused' - 0, // 17 - 'Unused' - 0, // 18 - 'Unused' - 0, // 19 - 'Unused' - 0, // 20 - 'Unused' - 0, // 21 - 'Unused' - 0, // 22 - 'Unused' - 0, // 23 - 'Unused' - 0, // 24 - 'Unused' - 0, // 25 - 'Unused' - 0, // 26 - 'Unused' - 0, // 27 - 'Unused' - 0, // 28 - 'Unused' - 0, // 29 - 'Unused' - 0, // 30 - 'Unused' - 0, // 31 - 'Unused' - 2, 0, 0, // 32 - 'Space' - 1, 95, // 33 - '!' - 3, 7, 0, 7, // 34 - '"' - 5, 20, 127, 20, 127, 20, // 35 - '#' - 5, 36, 42, 127, 42, 18, // 36 - '$' - 5, 35, 19, 8, 100, 98, // 37 - '%' - 5, 54, 73, 86, 32, 80, // 38 - '&' - 2, 4, 3, // 39 - 3, 28, 34, 65, // 40 - '(' - 3, 65, 34, 28, // 41 - ')' - 5, 42, 28, 127, 28, 42, // 42 - '*' - 5, 8, 8, 62, 8, 8, // 43 - '+' - 2, 128, 96, // 44 - ',' - 5, 8, 8, 8, 8, 8, // 45 - '-' - 2, 96, 96, // 46 - '.' - 5, 32, 16, 8, 4, 2, // 47 - '/' - 5, 62, 81, 73, 69, 62, // 48 - '0' - 3, 66, 127, 64, // 49 - '1' - 5, 114, 73, 73, 73, 70, // 50 - '2' - 5, 33, 65, 73, 77, 51, // 51 - '3' - 5, 24, 20, 18, 127, 16, // 52 - '4' - 5, 39, 69, 69, 69, 57, // 53 - '5' - 5, 60, 74, 73, 73, 49, // 54 - '6' - 5, 65, 33, 17, 9, 7, // 55 - '7' - 5, 54, 73, 73, 73, 54, // 56 - '8' - 5, 70, 73, 73, 41, 30, // 57 - '9' - 1, 20, // 58 - ':' - 2, 128, 104, // 59 - ';' - 4, 8, 20, 34, 65, // 60 - '<' - 5, 20, 20, 20, 20, 20, // 61 - '=' - 4, 65, 34, 20, 8, // 62 - '>' - 5, 2, 1, 89, 9, 6, // 63 - '?' - 5, 62, 65, 93, 89, 78, // 64 - '@' - 5, 124, 18, 17, 18, 124, // 65 - 'A' - 5, 127, 73, 73, 73, 54, // 66 - 'B' - 5, 62, 65, 65, 65, 34, // 67 - 'C' - 5, 127, 65, 65, 65, 62, // 68 - 'D' - 5, 127, 73, 73, 73, 65, // 69 - 'E' - 5, 127, 9, 9, 9, 1, // 70 - 'F' - 5, 62, 65, 65, 81, 115, // 71 - 'G' - 5, 127, 8, 8, 8, 127, // 72 - 'H' - 3, 65, 127, 65, // 73 - 'I' - 5, 32, 64, 65, 63, 1, // 74 - 'J' - 5, 127, 8, 20, 34, 65, // 75 - 'K' - 5, 127, 64, 64, 64, 64, // 76 - 'L' - 5, 127, 2, 28, 2, 127, // 77 - 'M' - 5, 127, 4, 8, 16, 127, // 78 - 'N' - 5, 62, 65, 65, 65, 62, // 79 - 'O' - 5, 127, 9, 9, 9, 6, // 80 - 'P' - 5, 62, 65, 81, 33, 94, // 81 - 'Q' - 5, 127, 9, 25, 41, 70, // 82 - 'R' - 5, 38, 73, 73, 73, 50, // 83 - 'S' - 5, 3, 1, 127, 1, 3, // 84 - 'T' - 5, 63, 64, 64, 64, 63, // 85 - 'U' - 5, 31, 32, 64, 32, 31, // 86 - 'V' - 5, 63, 64, 56, 64, 63, // 87 - 'W' - 5, 99, 20, 8, 20, 99, // 88 - 'X' - 5, 3, 4, 120, 4, 3, // 89 - 'Y' - 5, 97, 89, 73, 77, 67, // 90 - 'Z' - 3, 127, 65, 65, // 91 - '[' - 5, 2, 4, 8, 16, 32, // 92 - '\' - 3, 65, 65, 127, // 93 - ']' - 5, 4, 2, 1, 2, 4, // 94 - '^' - 5, 64, 64, 64, 64, 64, // 95 - '_' - 2, 3, 4, // 96 - '`' - 5, 32, 84, 84, 120, 64, // 97 - 'a' - 5, 127, 40, 68, 68, 56, // 98 - 'b' - 5, 56, 68, 68, 68, 40, // 99 - 'c' - 5, 56, 68, 68, 40, 127, // 100 - 'd' - 5, 56, 84, 84, 84, 24, // 101 - 'e' - 4, 8, 126, 9, 2, // 102 - 'f' - 5, 24, 164, 164, 156, 120, // 103 - 'g' - 5, 127, 8, 4, 4, 120, // 104 - 'h' - 3, 68, 125, 64, // 105 - 'i' - 4, 64, 128, 128, 122, // 106 - 'j' - 4, 127, 16, 40, 68, // 107 - 'k' - 3, 65, 127, 64, // 108 - 'l' - 5, 124, 4, 120, 4, 120, // 109 - 'm' - 5, 124, 8, 4, 4, 120, // 110 - 'n' - 5, 56, 68, 68, 68, 56, // 111 - 'o' - 5, 252, 24, 36, 36, 24, // 112 - 'p' - 5, 24, 36, 36, 24, 252, // 113 - 'q' - 5, 124, 8, 4, 4, 8, // 114 - 'r' - 5, 72, 84, 84, 84, 36, // 115 - 's' - 4, 4, 63, 68, 36, // 116 - 't' - 5, 60, 64, 64, 32, 124, // 117 - 'u' - 5, 28, 32, 64, 32, 28, // 118 - 'v' - 5, 60, 64, 48, 64, 60, // 119 - 'w' - 5, 68, 40, 16, 40, 68, // 120 - 'x' - 5, 76, 144, 144, 144, 124, // 121 - 'y' - 5, 68, 100, 84, 76, 68, // 122 - 'z' - 3, 8, 54, 65, // 123 - '{' - 1, 119, // 124 - '|' - 3, 65, 54, 8, // 125 - '}' - 5, 2, 1, 2, 4, 2, // 126 - '~' - 0, // 127 - 'Unused' - 5, 252, 36, 36, 36, 24, // 128 - 'р' - 5, 120, 132, 132, 132, 72, // 129 - 'с' - 5, 4, 4, 252, 4, 4, // 130 - 'т' - 5, 76, 144, 144, 144, 124, // 131 - 'у' - 5, 56, 68, 254, 68, 56, // 132 - 'ф' - 5, 196, 40, 16, 40, 196, // 133 - 'х' - 5, 124, 64, 64, 124, 192, // 134 - 'ц' - 5, 28, 32, 32, 32, 252, // 135 - 'ч' - 5, 252, 128, 252, 128, 252, // 136 - 'ш' - 5, 124, 64, 124, 64, 252, // 137 - 'щ' - 5, 4, 252, 144, 144, 96, // 138 - 'ъ' - 5, 252, 144, 240, 0, 252, // 139 - 'ы' - 4, 252, 144, 144, 96, // 140 - 'ь' - 5, 72, 132, 148, 148, 120, // 141 - 'э' - 5, 252, 32, 252, 132, 252, // 142 - 'ю' - 5, 24, 164, 100, 36, 252, // 143 - 'я' - 5, 248, 36, 34, 33, 255, // 144 - 'А' - 5, 255, 137, 137, 137, 112, // 145 - 'Б' - 5, 255, 137, 137, 137, 118, // 146 - 'В' - 4, 255, 1, 1, 3, // 147 - 'Г' - 6, 192, 126, 65, 65, 127, 192, // 148 - 'Д' - 4, 255, 137, 137, 129, // 149 - 'Е' - 5, 247, 8, 255, 8, 247, // 150 - 'Ж' - 5, 66, 129, 137, 137, 118, // 151 - 'З' - 5, 255, 32, 16, 8, 255, // 152 - 'И' - 5, 254, 32, 17, 8, 254, // 153 - 'Й' - 5, 255, 8, 20, 34, 193, // 154 - 'К' - 5, 128, 124, 2, 1, 255, // 155 - 'Л' - 5, 255, 4, 24, 4, 255, // 156 - 'М' - 5, 255, 8, 8, 8, 255, // 157 - 'Н' - 5, 126, 129, 129, 129, 126, // 158 - 'О' - 5, 255, 1, 1, 1, 255, // 159 - 'П' - 5, 255, 17, 17, 17, 14, // 160 - 'Р' - 5, 126, 129, 129, 129, 66, // 161 - 'С' - 5, 3, 1, 255, 1, 3, // 162 - 'Т' - 5, 71, 136, 144, 144, 127, // 163 - 'У' - 5, 60, 66, 255, 66, 60, // 164 - 'Ф' - 5, 227, 20, 8, 20, 227, // 165 - 'Х' - 5, 127, 64, 64, 127, 192, // 166 - 'Ц' - 5, 15, 16, 16, 16, 255, // 167 - 'Ч' - 5, 255, 128, 254, 128, 255, // 168 - 'Ш' - 5, 127, 64, 126, 64, 255, // 169 - 'Щ' - 5, 1, 255, 136, 136, 112, // 170 - 'Ъ' - 5, 255, 132, 120, 0, 255, // 171 - 'Ы' - 4, 255, 136, 136, 112, // 172 - 'Ь' - 5, 66, 129, 137, 137, 126, // 173 - 'Э' - 6, 255, 16, 126, 129, 129, 126, // 174 - 'Ю' - 5, 142, 81, 49, 17, 255, // 175 - 'Я' - 5, 96, 148, 148, 248, 128, // 176 - 'а' - 4, 252, 148, 148, 96, // 177 - 'б' - 4, 252, 148, 148, 104, // 178 - 'в' - 4, 252, 4, 4, 12, // 179 - 'г' - 5, 192, 124, 68, 124, 192, // 180 - 'д' - 5, 120, 164, 164, 164, 24, // 181 - 'е' - 5, 236, 16, 252, 16, 236, // 182 - 'ж' - 5, 72, 132, 148, 148, 104, // 183 - 'з' - 5, 252, 64, 32, 16, 252, // 184 - 'и' - 5, 252, 64, 34, 16, 252, // 185 - 'й' - 5, 252, 16, 40, 68, 128, // 186 - 'к' - 5, 128, 240, 8, 4, 252, // 187 - 'л' - 5, 252, 16, 224, 16, 252, // 188 - 'м' - 5, 252, 16, 16, 16, 252, // 189 - 'н' - 5, 120, 132, 132, 132, 120, // 190 - 'о' - 5, 252, 4, 4, 4, 252, // 191 - 'п' - 0, // 192 - 0, // 193 - 0, // 194 - 0, // 195 - 0, // 196 - 0, // 197 - 0, // 198 - 0, // 199 - 0, // 200 - 0, // 201 - 0, // 202 - 0, // 203 - 0, // 204 - 0, // 205 - 0, // 206 - 0, // 207 - 0, // 208 - 0, // 209 - 0, // 210 - 0, // 211 - 0, // 212 - 0, // 213 - 0, // 214 - 0, // 215 - 0, // 216 - 0, // 217 - 0, // 218 - 0, // 219 - 0, // 220 - 0, // 221 - 0, // 222 - 0, // 223 - 0, // 224 - 0, // 225 - 0, // 226 - 0, // 227 - 0, // 228 - 0, // 229 - 0, // 230 - 0, // 231 - 0, // 232 - 0, // 233 - 0, // 234 - 0, // 235 - 0, // 236 - 0, // 237 - 0, // 238 - 0, // 239 - 0, // 240 - 0, // 241 - 0, // 242 - 0, // 243 - 0, // 244 - 0, // 245 - 0, // 246 - 0, // 247 - 0, // 248 - 0, // 249 - 0, // 250 - 0, // 251 - 0, // 252 - 0, // 253 - 0, // 254 - 0, // 255 + 0, // 0 - Empty Cell + 5, 62, 91, 79, 91, 62, // 1 - Sad Smiley + 5, 62, 107, 79, 107, 62, // 2 - Happy Smiley + 5, 28, 62, 124, 62, 28, // 3 - Heart + 5, 24, 60, 126, 60, 24, // 4 - Diamond + 5, 28, 87, 125, 87, 28, // 5 - Clubs + 5, 28, 94, 127, 94, 28, // 6 - Spades + 3, 24, 60, 24, // 7 - Bullet Point + 5, 255, 231, 195, 231, 255, // 8 - Rev Bullet Point + 3, 24, 36, 24, // 9 - Hollow Bullet Point + 5, 255, 231, 219, 231, 255, // 10 - Rev Hollow BP + 5, 48, 72, 58, 6, 14, // 11 - Male + 5, 38, 41, 121, 41, 38, // 12 - Female + 5, 64, 127, 5, 5, 7, // 13 - Music Note 1 + 5, 64, 127, 5, 37, 63, // 14 - Music Note 2 + 5, 90, 60, 231, 60, 90, // 15 - Snowflake + 5, 127, 62, 28, 28, 8, // 16 - Right Pointer + 5, 8, 28, 28, 62, 127, // 17 - Left Pointer + 5, 20, 34, 127, 34, 20, // 18 - UpDown Arrows + 5, 95, 95, 0, 95, 95, // 19 - Double Exclamation + 5, 6, 9, 127, 1, 127, // 20 - Paragraph Mark + 4, 102, 137, 149, 106, // 21 - Section Mark + 5, 96, 96, 96, 96, 96, // 22 - Double Underline + 5, 148, 162, 255, 162, 148, // 23 - UpDown Underlined + 5, 8, 4, 126, 4, 8, // 24 - Up Arrow + 5, 16, 32, 126, 32, 16, // 25 - Down Arrow + 5, 8, 8, 42, 28, 8, // 26 - Right Arrow + 5, 8, 28, 42, 8, 8, // 27 - Left Arrow + 5, 30, 16, 16, 16, 16, // 28 - Angled + 5, 12, 30, 12, 30, 12, // 29 - Squashed # + 5, 48, 56, 62, 56, 48, // 30 - Up Pointer + 5, 6, 14, 62, 14, 6, // 31 - Down Pointer + 2, 0, 0, // 32 - Space + 1, 95, // 33 - ! + 3, 3, 0, 3, // 34 - " + 5, 20, 62, 20, 62, 20, // 35 - # + 4, 36, 106, 43, 18, // 36 - $ + 5, 99, 19, 8, 100, 99, // 37 - % + 5, 54, 73, 86, 32, 80, // 38 - & + 1, 3, // 39 - ' + 3, 28, 34, 65, // 40 - ( + 3, 65, 34, 28, // 41 - ) + 5, 40, 24, 14, 24, 40, // 42 - * + 5, 8, 8, 62, 8, 8, // 43 - + + 2, 176, 112, // 44 - , + 4, 8, 8, 8, 8, // 45 - - + 2, 96, 96, // 46 - . + 4, 96, 24, 6, 1, // 47 - / + 4, 62, 65, 65, 62, // 48 - 0 + 3, 66, 127, 64, // 49 - 1 + 4, 98, 81, 73, 70, // 50 - 2 + 4, 34, 65, 73, 54, // 51 - 3 + 4, 24, 20, 18, 127, // 52 - 4 + 4, 39, 69, 69, 57, // 53 - 5 + 4, 62, 73, 73, 48, // 54 - 6 + 4, 97, 17, 9, 7, // 55 - 7 + 4, 54, 73, 73, 54, // 56 - 8 + 4, 6, 73, 73, 62, // 57 - 9 + 1, 80, // 58 - : + 2, 128, 80, // 59 - ; + 3, 16, 40, 68, // 60 - < + 3, 20, 20, 20, // 61 - = + 3, 68, 40, 16, // 62 - > + 4, 2, 89, 9, 6, // 63 - ? + 5, 62, 73, 85, 93, 14, // 64 - @ + 4, 126, 17, 17, 126, // 65 - A + 4, 127, 73, 73, 54, // 66 - B + 4, 62, 65, 65, 34, // 67 - C + 4, 127, 65, 65, 62, // 68 - D + 4, 127, 73, 73, 65, // 69 - E + 4, 127, 9, 9, 1, // 70 - F + 4, 62, 65, 73, 122, // 71 - G + 4, 127, 8, 8, 127, // 72 - H + 3, 65, 127, 65, // 73 - I + 4, 48, 64, 65, 63, // 74 - J + 4, 127, 8, 20, 99, // 75 - K + 4, 127, 64, 64, 64, // 76 - L + 5, 127, 2, 12, 2, 127, // 77 - M + 5, 127, 4, 8, 16, 127, // 78 - N + 4, 62, 65, 65, 62, // 79 - O + 4, 127, 9, 9, 6, // 80 - P + 4, 62, 65, 65, 190, // 81 - Q + 4, 127, 9, 9, 118, // 82 - R + 4, 70, 73, 73, 50, // 83 - S + 5, 1, 1, 127, 1, 1, // 84 - T + 4, 63, 64, 64, 63, // 85 - U + 5, 15, 48, 64, 48, 15, // 86 - V + 5, 63, 64, 56, 64, 63, // 87 - W + 5, 99, 20, 8, 20, 99, // 88 - X + 5, 7, 8, 112, 8, 7, // 89 - Y + 4, 97, 81, 73, 71, // 90 - Z + 2, 127, 65, // 91 - [ + 4, 1, 6, 24, 96, // 92 - \ backslash + 2, 65, 127, // 93 - ] + 3, 2, 1, 2, // 94 - ^ + 4, 64, 64, 64, 64, // 95 - _ + 2, 1, 2, // 96 - ` + 4, 32, 84, 84, 120, // 97 - a + 4, 127, 68, 68, 56, // 98 - b + 4, 56, 68, 68, 40, // 99 - c + 4, 56, 68, 68, 127, // 100 - d + 4, 56, 84, 84, 24, // 101 - e + 3, 4, 126, 5, // 102 - f + 4, 152, 164, 164, 120, // 103 - g + 4, 127, 4, 4, 120, // 104 - h + 3, 68, 125, 64, // 105 - i + 4, 64, 128, 132, 125, // 106 - j + 4, 127, 16, 40, 68, // 107 - k + 3, 65, 127, 64, // 108 - l + 5, 124, 4, 124, 4, 120, // 109 - m + 4, 124, 4, 4, 120, // 110 - n + 4, 56, 68, 68, 56, // 111 - o + 4, 252, 36, 36, 24, // 112 - p + 4, 24, 36, 36, 252, // 113 - q + 4, 124, 8, 4, 4, // 114 - r + 4, 72, 84, 84, 36, // 115 - s + 3, 4, 63, 68, // 116 - t + 4, 60, 64, 64, 124, // 117 - u + 5, 28, 32, 64, 32, 28, // 118 - v + 5, 60, 64, 60, 64, 60, // 119 - w + 5, 68, 40, 16, 40, 68, // 120 - x + 4, 156, 160, 160, 124, // 121 - y + 3, 100, 84, 76, // 122 - z + 3, 8, 54, 65, // 123 - { + 1, 127, // 124 - | + 3, 65, 54, 8, // 125 - } + 4, 8, 4, 8, 4, // 126 - ~ + 0, // 127 - 0 + 4, 252, 36, 36, 24, // 128 - р + 4, 56, 68, 68, 40, // 129 - с + 3, 4, 124, 4, // 130 - т + 4, 156, 160, 160, 124, // 131 - у + 5, 24, 36, 252, 36, 24, // 132 - ф + 5, 68, 40, 16, 40, 68, // 133 - х + 5, 124, 64, 64, 124, 192, // 134 - ц + 4, 28, 16, 16, 124, // 135 - ч + 5, 124, 64, 112, 64, 124, // 136 - ш + 5, 124, 64, 112, 64, 252, // 137 - щ + 5, 4, 124, 72, 72, 48, // 138 - ъ + 5, 124, 72, 48, 0, 124, // 139 - ы + 4, 124, 72, 72, 48, // 140 - ь + 4, 68, 84, 84, 56, // 141 - э + 5, 124, 16, 56, 68, 56, // 142 - ю + 4, 72, 52, 20, 124, // 143 - я + 4, 126, 17, 17, 126, // 144 - А + 4, 127, 73, 73, 49, // 145 - Б + 4, 127, 73, 73, 54, // 146 - В + 4, 127, 1, 1, 1, // 147 - Г + 5, 192, 126, 65, 127, 192, // 148 - Д + 4, 127, 73, 73, 65, // 149 - Е + 5, 119, 8, 127, 8, 119, // 150 - Ж + 4, 34, 65, 73, 54, // 151 - З + 4, 127, 32, 16, 127, // 152 - И + 4, 127, 32, 17, 125, // 153 - Й + 4, 127, 8, 20, 99, // 154 - К + 4, 64, 60, 2, 127, // 155 - Л + 5, 127, 2, 12, 2, 127, // 156 - М + 4, 127, 8, 8, 127, // 157 - Н + 4, 62, 65, 65, 62, // 158 - О + 4, 127, 1, 1, 127, // 159 - П + 4, 127, 9, 9, 6, // 160 - Р + 4, 62, 65, 65, 34, // 161 - С + 5, 1, 1, 127, 1, 1, // 162 - Т + 4, 71, 72, 72, 63, // 163 - У + 5, 14, 17, 127, 17, 14, // 164 - Ф + 5, 99, 20, 8, 20, 99, // 165 - Х + 5, 127, 64, 64, 127, 192, // 166 - Ц + 4, 15, 8, 8, 127, // 167 - Ч + 5, 127, 64, 124, 64, 127, // 168 - Ш + 5, 127, 64, 124, 64, 255, // 169 - Щ + 5, 1, 127, 72, 72, 48, // 170 - Ъ + 5, 127, 72, 48, 0, 127, // 171 - Ы + 4, 127, 72, 72, 48, // 172 - Ь + 4, 65, 73, 73, 62, // 173 - Э + 5, 127, 8, 62, 65, 62, // 174 - Ю + 4, 70, 41, 25, 127, // 175 - Я + 4, 32, 84, 84, 120, // 176 - а + 4, 60, 74, 74, 49, // 177 - б + 4, 126, 89, 86, 32, // 178 - в + 4, 124, 4, 4, 4, // 179 - г + 5, 192, 120, 68, 124, 192, // 180 - д + 4, 56, 84, 84, 24, // 181 - е + 5, 108, 16, 124, 16, 108, // 182 - ж + 4, 72, 132, 148, 104, // 183 - з + 4, 124, 32, 16, 124, // 184 - и + 4, 124, 33, 17, 124, // 185 - й + 4, 124, 16, 40, 68, // 186 - к + 4, 64, 56, 4, 124, // 187 - л + 5, 124, 8, 16, 8, 124, // 188 - м + 4, 124, 16, 16, 124, // 189 - н + 4, 56, 68, 68, 56, // 190 - о + 4, 124, 4, 4, 124, // 191 - п + 4, 125, 84, 84, 85, // 192 - Ё + 4, 57, 84, 84, 73, // 193 - ё + 5, 0, 0, 0, 255, 16, // 194 - Left T + 5, 16, 16, 16, 16, 16, // 195 - Top side + 5, 16, 16, 16, 255, 16, // 196 - Center + + 5, 0, 0, 0, 255, 20, // 197 - Left side double H + 5, 0, 0, 255, 0, 255, // 198 - Left side double + 5, 0, 0, 31, 16, 23, // 199 - Bot Left double V + 5, 0, 0, 252, 4, 244, // 200 - Top Left double V + 5, 20, 20, 23, 16, 23, // 201 - Bot T double + 5, 20, 20, 244, 4, 244, // 202 - Top T double + 5, 0, 0, 255, 0, 247, // 203 - Left Side double spl + 5, 20, 20, 20, 20, 20, // 204 - Center double + 5, 20, 20, 247, 0, 247, // 205 - Center + double + 5, 20, 20, 20, 23, 20, // 206 - Bot T double H + 5, 16, 16, 31, 16, 31, // 207 - Bot Right double V + 0, // 208 - for rus fonts UTF-8 + 0, // 209 - for rus fonts UTF-8 + 5, 0, 0, 31, 16, 31, // 210 - Bot Left double V + 5, 0, 0, 0, 31, 20, // 211 - Bot Right double H + 5, 0, 0, 0, 252, 20, // 212 - Top Right double H + 5, 0, 0, 240, 16, 240, // 213 - Top Right double V + 5, 16, 16, 255, 16, 255, // 214 - Center + double V + 5, 20, 20, 20, 255, 20, // 215 - Center + double H + 5, 16, 16, 16, 16, 31, // 216 - Bot Right + 5, 0, 0, 0, 240, 16, // 217 - Top Left + 5, 255, 255, 255, 255, 255, // 218 - Full Block + 5, 240, 240, 240, 240, 240, // 219 - Half Block Bottom + 3, 255, 255, 255, // 220 - Half Block LHS + 5, 0, 0, 0, 255, 255, // 221 - Half Block RHS + 5, 15, 15, 15, 15, 15, // 222 - Half Block Top + 5, 56, 68, 68, 56, 68, // 223 - Alpha + 5, 124, 42, 42, 62, 20, // 224 - Beta + 5, 126, 2, 2, 6, 6, // 225 - Gamma + 5, 2, 126, 2, 126, 2, // 226 - Pi + 5, 99, 85, 73, 65, 99, // 227 - Sigma + 5, 56, 68, 68, 60, 4, // 228 - Theta + 5, 64, 126, 32, 30, 32, // 229 - mu + 5, 6, 2, 126, 2, 2, // 230 - Tau + 5, 153, 165, 231, 165, 153, // 231 - 0 + 5, 28, 42, 73, 42, 28, // 232 - 0 + 5, 76, 114, 1, 114, 76, // 233 - 0 + 5, 48, 74, 77, 77, 48, // 234 - 0 + 5, 48, 72, 120, 72, 48, // 235 - 0 + 5, 188, 98, 90, 70, 61, // 236 - Zero Slashed + 4, 62, 73, 73, 73, // 237 - 0 + 5, 126, 1, 1, 1, 126, // 238 - 0 + 5, 42, 42, 42, 42, 42, // 239 - 3 Bar Equals + 5, 68, 68, 95, 68, 68, // 240 - +/- + 5, 64, 81, 74, 68, 64, // 241 - >= + 5, 64, 68, 74, 81, 64, // 242 - <= + 5, 0, 0, 255, 1, 3, // 243 - Top of Integral + 3, 224, 128, 255, // 244 - Bot of Integral + 5, 8, 8, 107, 107, 8, // 245 - Divide + 5, 54, 18, 54, 36, 54, // 246 - Wavy = + 5, 6, 15, 9, 15, 6, // 247 - Degree + 4, 0, 0, 24, 24, // 248 - Math Product + 4, 0, 0, 16, 16, // 249 - Short Dash + 5, 48, 64, 255, 1, 1, // 250 - Square Root + 5, 0, 31, 1, 1, 30, // 251 - Superscript n + 5, 0, 25, 29, 23, 18, // 252 - Superscript 2 + 5, 0, 60, 60, 60, 60, // 253 - Centered Square + 5, 255, 129, 129, 129, 255, // 254 - Full Frame + 5, 255, 255, 255, 255, 255, // 255 - Full Block }; From 2c7ce5e4f34e112aaed4e349ca92252df9983d51 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:25:10 +0100 Subject: [PATCH 076/161] [JS] update WebStaticData.h --- src/src/Static/WebStaticData.h | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index 04cb1bdcb..813c8814e 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -381,25 +381,30 @@ static const char jsSplitMultipleFields[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { "function elId(e){return document.getElementById(e)}" - "function loopDeLoop(e,s){var o,a,n=0;isNaN(s)&&(s=1),null==e&&(e=1e3)," - "i=setInterval(function(){if(n>0){clearInterval(i);return}" - "++s>1||fetch('/json?view=sensorupdate').then(function(s){var n;if(200!==s.status){console.log('Looks like there was a problem. Status Code: '+s.status);return}" - "s.json().then(function(s){e=s.TTL;" + "loopDeLoop(1000);" + "const SENSOR_URL='/json?view=sensorupdate';" + "function loopDeLoop(e=1000){setTimeout(function(){fetch(SENSOR_URL)" + ".then(e=>{if(!e.ok)throw new Error(e.status);return e.json()})" + ".then(o=>{e=o.TTL||e;" #if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES) - "var r=void 0===s.ShowUoM||s.ShowUoM&&'false'!==s.ShowUoM;" + "const n=void 0===o.ShowUoM||o.ShowUoM&&'false'!==o.ShowUoM;" #endif // if (defined(FEATURE_TASKVALUE_UNIT_OF_MEASURE) && FEATURE_TASKVALUE_UNIT_OF_MEASURE) || (defined(FEATURE_STRING_VARIABLES) && FEATURE_STRING_VARIABLES) - "for(o=0;o{loopDeLoop(5000)})},e)}" }; #endif // WEBSERVER_INCLUDE_JS From d5b57218e75460e1fa04040349d79e8fc676ef48 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 9 Feb 2026 10:18:35 +0100 Subject: [PATCH 077/161] [Sysinfo] Show free space on file system --- src/src/Commands/InternalCommands.cpp | 4 ++-- src/src/Helpers/ESPEasy_checks.cpp | 2 +- src/src/Helpers/StringProvider.cpp | 33 ++++++++------------------- src/src/WebServer/JSON.cpp | 1 + src/src/WebServer/SysInfoPage.cpp | 1 + 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index 3d99d2984..d4fc20f71 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -101,13 +101,13 @@ bool checkNrArguments(const char *cmd, const String& Line, int nrArguments) { log += F(" ExtraArg"); } log += i; - log += '='; + log += ':'; log += parameter; } ++i; } } - log += F(" lineLength="); + log += F(" lineLength:"); log += Line.length(); addLogMove(LOG_LEVEL_ERROR, log); } diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index f7265d487..0e6b85486 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -168,7 +168,7 @@ void run_compiletime_checks() { #endif } - constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit); +// constexpr size_t offset_WireClockStretchLimit = offsetof(SettingsStruct, WireClockStretchLimit); constexpr size_t offset_ConnectionFailuresThreshold = offsetof(SettingsStruct, ConnectionFailuresThreshold); static_assert(184 == offset_ConnectionFailuresThreshold, ""); diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 64af7cd89..65a5c02c3 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -480,13 +480,13 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) { return KeyValueStruct(F("Show Unit of Measure"), Settings.ShowUnitOfMeasureOnDevicesPage()); } - #endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE - #if FEATURE_MQTT_CONNECT_BACKGROUND +#endif // if FEATURE_TASKVALUE_UNIT_OF_MEASURE +#if FEATURE_MQTT_CONNECT_BACKGROUND case LabelType::MQTT_CONNECT_IN_BACKGROUND: { return KeyValueStruct(F("MQTT Connect in background"), Settings.MQTTConnectInBackground()); } - #endif // if FEATURE_MQTT_CONNECT_BACKGROUND +#endif // if FEATURE_MQTT_CONNECT_BACKGROUND #if CONFIG_SOC_WIFI_SUPPORT_5G case LabelType::WIFI_BAND_MODE: @@ -544,7 +544,9 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } KeyValueStruct kv(F("RSSI"), WiFi.RSSI()); #if FEATURE_TASKVALUE_UNIT_OF_MEASURE - KV_SETUNIT(UOM_dBm); + if (!extendedValue) { + KV_SETUNIT(UOM_dBm); + } #endif return kv; } @@ -1025,11 +1027,11 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) KeyValueStruct kv(F("Sketch Size"), str); KV_SETID(F("sketch_size")); - if (!extendedValue) { #if FEATURE_TASKVALUE_UNIT_OF_MEASURE + if (!extendedValue) { KV_SETUNIT(UOM_kB); -#endif } +#endif return kv; } case LabelType::SKETCH_FREE: @@ -1043,18 +1045,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) } case LabelType::FS_SIZE: { - String size; - - if (extendedValue) { - size = strformat( - F("%d [kB] (%d kB free)"), - SpiffsTotalBytes() / 1024, - SpiffsFreeSpace() / 1024); - } - else { - size = (SpiffsTotalBytes() >> 10); - } - KeyValueStruct kv( #ifdef USE_LITTLEFS F("Little FS Size"), @@ -1063,15 +1053,10 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) #endif // ifdef USE_LITTLEFS SpiffsTotalBytes() >> 10); KV_SETID(F("fs_size")); + #if FEATURE_TASKVALUE_UNIT_OF_MEASURE KV_SETUNIT(UOM_kB); #endif - - if (!extendedValue) { -#if FEATURE_TASKVALUE_UNIT_OF_MEASURE - KV_SETUNIT(UOM_kB); -#endif - } return kv; } case LabelType::FS_FREE: diff --git a/src/src/WebServer/JSON.cpp b/src/src/WebServer/JSON.cpp index 3e1e217fd..9ae6d4de0 100644 --- a/src/src/WebServer/JSON.cpp +++ b/src/src/WebServer/JSON.cpp @@ -247,6 +247,7 @@ void handle_json() LabelType::FLASH_CHIP_SPEED, LabelType::FLASH_IDE_MODE, LabelType::FS_SIZE, + LabelType::FS_FREE, LabelType::SUNRISE, LabelType::SUNSET, diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index 7eb9929a5..0762cf97d 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -640,6 +640,7 @@ void handle_sysinfo_Storage() { LabelType::OTA_2STEP, # endif // ifdef ESP8266 LabelType::FS_SIZE, + LabelType::FS_FREE, LabelType::MAX_LABEL }; From 42443c368c6cb7837c58d17d029eccb9e0722d12 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 9 Feb 2026 13:30:30 +0100 Subject: [PATCH 078/161] [ESP-IDF] Update to latest IDF/Arduino code to test if P4 builds work --- 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 d93cab796..204635cd5 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -171,7 +171,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0401-1225-5.5/framework-arduinoespressif32-release_v5.5-ad03770f.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0602-2247-5.5/framework-arduinoespressif32-release_v5.5-7eede06a.tar.xz custom_remove_include = true From 3050243748929f7617bc36785af4a40d32d8182a Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 9 Feb 2026 13:58:35 +0100 Subject: [PATCH 079/161] [Build] Fix non-ESP32P4 builds with latest ESP-IDF/Arduino --- src/src/Helpers/Hardware_device_info.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/Hardware_device_info.cpp b/src/src/Helpers/Hardware_device_info.cpp index 527c8ec2e..8598556be 100644 --- a/src/src/Helpers/Hardware_device_info.cpp +++ b/src/src/Helpers/Hardware_device_info.cpp @@ -266,7 +266,8 @@ uint32_t getFlashChipSpeed() { // spi_clk is equal to system clock return getApbFrequency(); } - return spiClockDivToFrequency(spi_clock); + // TODO TD-er: For P4 a pointer to the SPI bus is needed. + return spiClockDivToFrequency(nullptr, spi_clock); #endif # endif // if ESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED #endif // ifdef ESP8266 From 45927223de9900c4899d43a7791bfed60daf6a16 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 9 Feb 2026 15:48:16 +0100 Subject: [PATCH 080/161] [Build] Set BUILD_NO_DEBUG for Climate-A builds to make them fit again --- src/src/CustomBuild/define_plugin_sets.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 01600576b..003c3e89a 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2179,6 +2179,10 @@ To create/register a plugin, you have to : #define PLUGIN_DESCR "Climate A" #endif + #ifndef BUILD_NO_DEBUG + #define BUILD_NO_DEBUG + #endif + // Features and plugins cherry picked from stable set #ifndef FEATURE_SERVO #define FEATURE_SERVO 1 From ff80cee2f6d850dc37c6d58d7df0ff3d457a19a5 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 9 Feb 2026 23:25:46 +0100 Subject: [PATCH 081/161] [P104] Updated Cyrillic ext. font with a more visually pleasing character set --- docs/source/Plugin/P104_Font_Cyrillic-ext.png | Bin 79028 -> 77964 bytes src/src/Static/Fonts/P104_font_cyr_ext.h | 128 +++++++++--------- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/docs/source/Plugin/P104_Font_Cyrillic-ext.png b/docs/source/Plugin/P104_Font_Cyrillic-ext.png index 4a30d316c6f64200ef30bc7472e6f3cbbe41ed04..d8b80c9009e652a06a078854e1d4079265ffb616 100644 GIT binary patch literal 77964 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 g4A3|s0EO8zK`6JIy!YfV>VkeHiT^E19 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�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 P@5sTgtg}D==JNjl5%J|4 literal 79028 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 AmH+?% diff --git a/src/src/Static/Fonts/P104_font_cyr_ext.h b/src/src/Static/Fonts/P104_font_cyr_ext.h index 4f28b4863..a036bd0fe 100644 --- a/src/src/Static/Fonts/P104_font_cyr_ext.h +++ b/src/src/Static/Fonts/P104_font_cyr_ext.h @@ -130,70 +130,70 @@ MD_MAX72XX::fontType_t font_cyr_ext[] PROGMEM = // ASCII with Cyrillic extension 3, 65, 54, 8, // 125 - } 4, 8, 4, 8, 4, // 126 - ~ 0, // 127 - 0 - 4, 252, 36, 36, 24, // 128 - р - 4, 56, 68, 68, 40, // 129 - с - 3, 4, 124, 4, // 130 - т - 4, 156, 160, 160, 124, // 131 - у - 5, 24, 36, 252, 36, 24, // 132 - ф - 5, 68, 40, 16, 40, 68, // 133 - х - 5, 124, 64, 64, 124, 192, // 134 - ц - 4, 28, 16, 16, 124, // 135 - ч - 5, 124, 64, 112, 64, 124, // 136 - ш - 5, 124, 64, 112, 64, 252, // 137 - щ - 5, 4, 124, 72, 72, 48, // 138 - ъ - 5, 124, 72, 48, 0, 124, // 139 - ы - 4, 124, 72, 72, 48, // 140 - ь - 4, 68, 84, 84, 56, // 141 - э - 5, 124, 16, 56, 68, 56, // 142 - ю - 4, 72, 52, 20, 124, // 143 - я - 4, 126, 17, 17, 126, // 144 - А - 4, 127, 73, 73, 49, // 145 - Б - 4, 127, 73, 73, 54, // 146 - В - 4, 127, 1, 1, 1, // 147 - Г - 5, 192, 126, 65, 127, 192, // 148 - Д - 4, 127, 73, 73, 65, // 149 - Е - 5, 119, 8, 127, 8, 119, // 150 - Ж - 4, 34, 65, 73, 54, // 151 - З - 4, 127, 32, 16, 127, // 152 - И - 4, 127, 32, 17, 125, // 153 - Й - 4, 127, 8, 20, 99, // 154 - К - 4, 64, 60, 2, 127, // 155 - Л - 5, 127, 2, 12, 2, 127, // 156 - М - 4, 127, 8, 8, 127, // 157 - Н - 4, 62, 65, 65, 62, // 158 - О - 4, 127, 1, 1, 127, // 159 - П - 4, 127, 9, 9, 6, // 160 - Р - 4, 62, 65, 65, 34, // 161 - С - 5, 1, 1, 127, 1, 1, // 162 - Т - 4, 71, 72, 72, 63, // 163 - У - 5, 14, 17, 127, 17, 14, // 164 - Ф - 5, 99, 20, 8, 20, 99, // 165 - Х - 5, 127, 64, 64, 127, 192, // 166 - Ц - 4, 15, 8, 8, 127, // 167 - Ч - 5, 127, 64, 124, 64, 127, // 168 - Ш - 5, 127, 64, 124, 64, 255, // 169 - Щ - 5, 1, 127, 72, 72, 48, // 170 - Ъ - 5, 127, 72, 48, 0, 127, // 171 - Ы - 4, 127, 72, 72, 48, // 172 - Ь - 4, 65, 73, 73, 62, // 173 - Э - 5, 127, 8, 62, 65, 62, // 174 - Ю - 4, 70, 41, 25, 127, // 175 - Я - 4, 32, 84, 84, 120, // 176 - а - 4, 60, 74, 74, 49, // 177 - б - 4, 126, 89, 86, 32, // 178 - в - 4, 124, 4, 4, 4, // 179 - г - 5, 192, 120, 68, 124, 192, // 180 - д - 4, 56, 84, 84, 24, // 181 - е - 5, 108, 16, 124, 16, 108, // 182 - ж - 4, 72, 132, 148, 104, // 183 - з - 4, 124, 32, 16, 124, // 184 - и - 4, 124, 33, 17, 124, // 185 - й - 4, 124, 16, 40, 68, // 186 - к - 4, 64, 56, 4, 124, // 187 - л - 5, 124, 8, 16, 8, 124, // 188 - м - 4, 124, 16, 16, 124, // 189 - н - 4, 56, 68, 68, 56, // 190 - о - 4, 124, 4, 4, 124, // 191 - п + 5, 252, 36, 36, 36, 24, // 128 - 'р' + 5, 120, 132, 132, 132, 72, // 129 - 'с' + 5, 4, 4, 252, 4, 4, // 130 - 'т' + 5, 76, 144, 144, 144, 124, // 131 - 'у' + 5, 56, 68, 254, 68, 56, // 132 - 'ф' + 5, 196, 40, 16, 40, 196, // 133 - 'х' + 5, 124, 64, 64, 124, 192, // 134 - 'ц' + 5, 28, 32, 32, 32, 252, // 135 - 'ч' + 5, 252, 128, 252, 128, 252, // 136 - 'ш' + 5, 124, 64, 124, 64, 252, // 137 - 'щ' + 5, 4, 252, 144, 144, 96, // 138 - 'ъ' + 5, 252, 144, 240, 0, 252, // 139 - 'ы' + 4, 252, 144, 144, 96, // 140 - 'ь' + 5, 72, 132, 148, 148, 120, // 141 - 'э' + 5, 252, 32, 252, 132, 252, // 142 - 'ю' + 5, 24, 164, 100, 36, 252, // 143 - 'я' + 5, 248, 36, 34, 33, 255, // 144 - 'А' + 5, 255, 137, 137, 137, 112, // 145 - 'Б' + 5, 255, 137, 137, 137, 118, // 146 - 'В' + 4, 255, 1, 1, 3, // 147 - 'Г' + 6, 192, 126, 65, 65, 127, 192, // 148 - 'Д' + 4, 255, 137, 137, 129, // 149 - 'Е' + 5, 247, 8, 255, 8, 247, // 150 - 'Ж' + 5, 66, 129, 137, 137, 118, // 151 - 'З' + 5, 255, 32, 16, 8, 255, // 152 - 'И' + 5, 254, 32, 17, 8, 254, // 153 - 'Й' + 5, 255, 8, 20, 34, 193, // 154 - 'К' + 5, 128, 124, 2, 1, 255, // 155 - 'Л' + 5, 255, 4, 24, 4, 255, // 156 - 'М' + 5, 255, 8, 8, 8, 255, // 157 - 'Н' + 5, 126, 129, 129, 129, 126, // 158 - 'О' + 5, 255, 1, 1, 1, 255, // 159 - 'П' + 5, 255, 17, 17, 17, 14, // 160 - 'Р' + 5, 126, 129, 129, 129, 66, // 161 - 'С' + 5, 3, 1, 255, 1, 3, // 162 - 'Т' + 5, 71, 136, 144, 144, 127, // 163 - 'У' + 5, 60, 66, 255, 66, 60, // 164 - 'Ф' + 5, 227, 20, 8, 20, 227, // 165 - 'Х' + 5, 127, 64, 64, 127, 192, // 166 - 'Ц' + 5, 15, 16, 16, 16, 255, // 167 - 'Ч' + 5, 255, 128, 254, 128, 255, // 168 - 'Ш' + 5, 127, 64, 126, 64, 255, // 169 - 'Щ' + 5, 1, 255, 136, 136, 112, // 170 - 'Ъ' + 5, 255, 132, 120, 0, 255, // 171 - 'Ы' + 4, 255, 136, 136, 112, // 172 - 'Ь' + 5, 66, 129, 137, 137, 126, // 173 - 'Э' + 6, 255, 16, 126, 129, 129, 126, // 174 - 'Ю' + 5, 142, 81, 49, 17, 255, // 175 - 'Я' + 5, 96, 148, 148, 248, 128, // 176 - 'а' + 4, 252, 148, 148, 96, // 177 - 'б' + 4, 252, 148, 148, 104, // 178 - 'в' + 4, 252, 4, 4, 12, // 179 - 'г' + 5, 192, 124, 68, 124, 192, // 180 - 'д' + 5, 120, 164, 164, 164, 24, // 181 - 'е' + 5, 236, 16, 252, 16, 236, // 182 - 'ж' + 5, 72, 132, 148, 148, 104, // 183 - 'з' + 5, 252, 64, 32, 16, 252, // 184 - 'и' + 5, 252, 64, 34, 16, 252, // 185 - 'й' + 5, 252, 16, 40, 68, 128, // 186 - 'к' + 5, 128, 240, 8, 4, 252, // 187 - 'л' + 5, 252, 16, 224, 16, 252, // 188 - 'м' + 5, 252, 16, 16, 16, 252, // 189 - 'н' + 5, 120, 132, 132, 132, 120, // 190 - 'о' + 5, 252, 4, 4, 4, 252, // 191 - 'п' 4, 125, 84, 84, 85, // 192 - Ё 4, 57, 84, 84, 73, // 193 - ё 5, 0, 0, 0, 255, 16, // 194 - Left T From ee123aeab7955c38b7e25f1b1b4f4e6562d8d1b8 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 10 Feb 2026 13:05:19 +0100 Subject: [PATCH 082/161] [SPI] Add support (check) for ESP32-C61 in getSPIBusCount() --- src/src/Helpers/Hardware_device_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Helpers/Hardware_device_info.h b/src/src/Helpers/Hardware_device_info.h index 4c5484ae8..60f8584d0 100644 --- a/src/src/Helpers/Hardware_device_info.h +++ b/src/src/Helpers/Hardware_device_info.h @@ -120,7 +120,7 @@ constexpr uint8_t getI2CBusCount() { constexpr uint8_t getSPIBusCount() { #if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) return 2u; -#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32H2) +#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32C61) || defined(ESP32H2) return 1u; #elif defined(ESP8266) return 1u; From cc4e8e954700890496837279070936ee63ff5b8b Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 10 Feb 2026 22:59:23 +0100 Subject: [PATCH 083/161] [SPI] Fix returning 'other' SPI bus for user defined SPI --- .../NW004_data_struct_ETH_SPI.cpp | 9 +- src/src/DataStructs/SettingsStruct.h | 1 + src/src/DataStructs_templ/SettingsStruct.cpp | 27 +++-- src/src/DataTypes/SPI_options.cpp | 103 ++++++++++++------ src/src/DataTypes/SPI_options.h | 8 +- src/src/Helpers/ESPEasy_Storage.cpp | 2 +- src/src/Helpers/Hardware_SPI.cpp | 6 +- src/src/Helpers/Hardware_device_info.h | 17 +-- src/src/WebServer/DevicesPage.cpp | 4 +- src/src/WebServer/HardwarePage.cpp | 13 ++- 10 files changed, 125 insertions(+), 65 deletions(-) diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index 07cf8700b..12db00423 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -229,13 +229,15 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) addFormNote(F("I²C-address of Ethernet PHY")); } { - if ((getSPIBusCount() > 1) && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + if (Settings.getNrConfiguredSPI_buses()) { const int key = NW004_KEY_SPI_BUS; const uint8_t spiBus = _kvs->getValueAsInt(key); KVS_StorageType::Enum storageType; SPIInterfaceSelector(getLabelString(key, true, storageType), getLabelString(key, false, storageType), spiBus); + } else { + // TODO TD-er: Add error message as we need a SPI bus } const int gpio_keys[] = { @@ -446,7 +448,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { // FIXME TD-er: Fallback for ETH01-EVO board SPI_host = spi_host_device_t::SPI2_HOST; - Settings.InitSPI = static_cast(SPI_Options_e::UserDefined); + Settings.InitSPI = static_cast(SPI_Options_e::UserDefined_VSPI); Settings.SPI_SCLK_pin = 7; Settings.SPI_MISO_pin = 3; Settings.SPI_MOSI_pin = 10; @@ -454,7 +456,8 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { } // else - { + if (SPI_host != spi_host_device_t::SPI_HOST_MAX) { + // TODO TD-er: Do we need to include the CLK, MISO, MOSI pins in the call or do we need to start the SPI bus first? # if ETH_SPI_SUPPORTS_CUSTOM success = iface->begin( to_ESP_phy_type(phyType), diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index beac8867d..b821da7e6 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -371,6 +371,7 @@ public: bool noCheck = false) const; bool isSPI_enabled(uint8_t spi_bus) const; + uint8_t getNrConfiguredSPI_buses() const; #ifdef ESP32 spi_host_device_t getSPI_host(uint8_t spi_bus = 0) const; #endif diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 4a4afaef3..60333b653 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -993,6 +993,17 @@ bool SettingsStruct_tmpl::isSPI_enabled(uint8_t spi_bus) const { return SPI_Options_e::None != SPI_selection; } +template +uint8_t SettingsStruct_tmpl::getNrConfiguredSPI_buses() const +{ + uint8_t res{}; + if (isSPI_valid(0u)) ++res; + #ifdef ESP32 + if (getSPIBusCount() > 1 && isSPI_valid(1u)) ++res; + #endif + return res; +} + template bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus, bool noCheck) const { spi_gpios[0] = -1; @@ -1020,7 +1031,10 @@ bool SettingsStruct_tmpl::getSPI_pins(int8_t spi_gpios[3], uint8_t spi_ break; } #endif - case SPI_Options_e::UserDefined: + case SPI_Options_e::UserDefined_VSPI: +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: +#endif { #ifdef ESP32 if (0 == spi_bus) @@ -1059,6 +1073,7 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host(uint8_t spi_bus) con const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); switch (SPI_selection) { case SPI_Options_e::Vspi_Fspi: + case SPI_Options_e::UserDefined_VSPI: { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 return static_cast(FSPI_HOST); @@ -1072,14 +1087,12 @@ spi_host_device_t SettingsStruct_tmpl::getSPI_host(uint8_t spi_bus) con return static_cast(HSPI_HOST); } #endif - case SPI_Options_e::UserDefined: +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: { - #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 - return static_cast(FSPI_HOST); - #else - return static_cast(VSPI_HOST); - #endif + return static_cast(HSPI_HOST); } +#endif case SPI_Options_e::None: break; } diff --git a/src/src/DataTypes/SPI_options.cpp b/src/src/DataTypes/SPI_options.cpp index d20419226..e227bd83f 100644 --- a/src/src/DataTypes/SPI_options.cpp +++ b/src/src/DataTypes/SPI_options.cpp @@ -3,6 +3,8 @@ #ifdef ESP32 +#include "../Helpers/Hardware_device_info.h" + // ESP32 VSPI: // SCK = 18 // MISO = 19 @@ -35,28 +37,31 @@ # if CONFIG_IDF_TARGET_ESP32S3 // ESP32-S3 -#define VSPI_FSPI_SHORT_STRING "FSPI" +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")" # elif CONFIG_IDF_TARGET_ESP32S2 // ESP32-S2 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C2 // ESP32-C2 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C3 // ESP32-C3 -#define VSPI_FSPI_SHORT_STRING "SPI" -# elif CONFIG_IDF_TARGET_ESP32C5 // ESP32-C5 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C6 // ESP32-C6 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32C61 // ESP32-C61 -#define VSPI_FSPI_SHORT_STRING "FSPI" -# elif CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 -#define VSPI_FSPI_SHORT_STRING "FSPI" +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(FSPI_HOST) ")" +#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32P4 +# if SOC_SPI_PERIPH_NUM > 2 +#define VSPI_FSPI_SHORT_STRING "FSPI (" STRINGIFY(VSPI_HOST) ")" +# else +#define VSPI_FSPI_SHORT_STRING "SPI" // STRINGIFY(VSPI_HOST) +# endif + # elif CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 -#define VSPI_FSPI_SHORT_STRING "VSPI" +#define VSPI_FSPI_SHORT_STRING "VSPI (" STRINGIFY(VSPI_HOST) ")" # else // if CONFIG_IDF_TARGET_ESP32S2 # error Target CONFIG_IDF_TARGET is not supported # endif // if CONFIG_IDF_TARGET_ESP32S2 +#if SOC_SPI_PERIPH_NUM > 2 +#ifdef ESP32_CLASSIC +#define HSPI_SHORT_STRING "HSPI (" STRINGIFY(HSPI_HOST) ")" +#else +#define HSPI_SHORT_STRING STRINGIFY(HSPI_HOST) +#endif +#endif + const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { switch (option) { @@ -65,43 +70,73 @@ const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option) { case SPI_Options_e::Vspi_Fspi: return F( VSPI_FSPI_SHORT_STRING - ": CLK=GPIO-" STRINGIFY(VSPI_FSPI_SCK) - ", MISO=GPIO-" STRINGIFY(VSPI_FSPI_MISO) - ", MOSI=GPIO-" STRINGIFY(VSPI_FSPI_MOSI) ); + ": CLK=" STRINGIFY(VSPI_FSPI_SCK) + ", MISO=" STRINGIFY(VSPI_FSPI_MISO) + ", MOSI=" STRINGIFY(VSPI_FSPI_MOSI) ); #ifdef ESP32_CLASSIC case SPI_Options_e::Hspi: return F( - "HSPI" - ": CLK=GPIO-" STRINGIFY(HSPI_SCLK) - ", MISO=GPIO-" STRINGIFY(HSPI_MISO) - ", MOSI=GPIO-" STRINGIFY(HSPI_MOSI) ); + HSPI_SHORT_STRING + ": CLK=" STRINGIFY(HSPI_SCLK) + ", MISO=" STRINGIFY(HSPI_MISO) + ", MOSI=" STRINGIFY(HSPI_MOSI) ); #endif - case SPI_Options_e::UserDefined: - return F("User-defined: CLK, MISO, MOSI GPIO-pins"); + case SPI_Options_e::UserDefined_VSPI: + return F("User-defined " VSPI_FSPI_SHORT_STRING); +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: + return F("User-defined " HSPI_SHORT_STRING); +#endif } return F("Unknown"); } +const __FlashStringHelper* get_vspi_fspi_str() +{ + return F(VSPI_FSPI_SHORT_STRING); +} + const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus) { +#ifdef ESP32 + String res; + switch (option) { + case SPI_Options_e::None: + return F("Disabled"); + case SPI_Options_e::Vspi_Fspi: + res = F(VSPI_FSPI_SHORT_STRING); + break; +#ifdef ESP32_CLASSIC + case SPI_Options_e::Hspi: + res = F("HSPI"); + break; +#endif + case SPI_Options_e::UserDefined_VSPI: + res = F("User-defined " VSPI_FSPI_SHORT_STRING); + break; +#if SOC_SPI_PERIPH_NUM > 2 + case SPI_Options_e::UserDefined_HSPI: + res = F("User-defined " HSPI_SHORT_STRING); + break; +#endif + } + if (!res.isEmpty()) { + if (getSPIBusCount() > 1) { + return concat(res + F(" bus "), spi_bus); + } + return res; + } +#else switch (option) { case SPI_Options_e::None: return F("Disabled"); case SPI_Options_e::Vspi_Fspi: return F(VSPI_FSPI_SHORT_STRING); -#ifdef ESP32_CLASSIC - case SPI_Options_e::Hspi: - return F("HSPI"); -#endif - case SPI_Options_e::UserDefined: - #ifdef ESP32 - return concat(F("User-defined SPI Bus "), spi_bus); - #endif // ifdef ESP32 - #ifdef ESP8266 + case SPI_Options_e::UserDefined_VSPI: return F("User-defined SPI"); - #endif // ifdef ESP8266 } +#endif return F("Unknown"); } diff --git a/src/src/DataTypes/SPI_options.h b/src/src/DataTypes/SPI_options.h index 3da9ce257..affbd8a42 100644 --- a/src/src/DataTypes/SPI_options.h +++ b/src/src/DataTypes/SPI_options.h @@ -14,7 +14,7 @@ // ESP32-S2/S3 : FSPI // ESP32 classic: -// SPI_HOST = SPI1_HOST // Only usable on ESP32 +// SPI_HOST = SPI1_HOST // Only usable on ESP32, when all functions doing SPI operations are in IRAM // HSPI_HOST = SPI2_HOST // VSPI_HOST = SPI3_HOST // @@ -73,12 +73,16 @@ enum class SPI_Options_e { // For ESP32 classic, this is called VSPI // For later versions it is called FSPI // N.B. the ESP32-C3 does not seem to name these as there is no SPI3_HOST. - UserDefined = 9 // Leave some room for other, possible future, hardware-related options + UserDefined_VSPI = 9 // Leave some room for other, possible future, hardware-related options +#if SOC_SPI_PERIPH_NUM > 2 + ,UserDefined_HSPI = 10 +#endif }; #ifdef ESP32 const __FlashStringHelper* getSPI_optionToString(SPI_Options_e option); +const __FlashStringHelper* get_vspi_fspi_str(); const String getSPI_optionToShortString(SPI_Options_e option, uint8_t spi_bus = 0); #endif // ifdef ESP32 diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 8a6f25c1f..609474adc 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -466,7 +466,7 @@ bool BuildFixes() } if (Settings.Build < 20115) { - if (Settings.InitSPI != static_cast(SPI_Options_e::UserDefined)) { // User-defined SPI pins set to None + if (Settings.InitSPI != static_cast(SPI_Options_e::UserDefined_VSPI)) { // User-defined SPI pins set to None Settings.SPI_SCLK_pin = -1; Settings.SPI_MISO_pin = -1; Settings.SPI_MOSI_pin = -1; diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp index 56bc55f48..58c69753c 100644 --- a/src/src/Helpers/Hardware_SPI.cpp +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -36,11 +36,7 @@ void initializeSPIBuses() { #endif // if FEATURE_ETHERNET - if (Settings.isSPI_valid(0u) - #ifdef ESP32 - || Settings.isSPI_valid(1u) - #endif // ifdef ESP32 - ) + if (Settings.getNrConfiguredSPI_buses()) { #ifdef ESP32 diff --git a/src/src/Helpers/Hardware_device_info.h b/src/src/Helpers/Hardware_device_info.h index 60f8584d0..6b82fe44e 100644 --- a/src/src/Helpers/Hardware_device_info.h +++ b/src/src/Helpers/Hardware_device_info.h @@ -103,6 +103,7 @@ constexpr uint8_t getI2CBusCount() { #if FEATURE_I2C_MULTIPLE // Not querying the supported nr. of I2C busses in hardware, but using software multiplexing // Assume/expect IDF 5.x + // FIXME TD-er: Maybe we should look at SOC_HP_I2C_NUM ???? // # if defined(SOC_I2C_SUPPORTED) && SOC_I2C_SUPPORTED # if FEATURE_I2C_INTERFACE_3 return 3u; // SOC_I2C_NUM; // Let's go for all I2C busses, including LP_I2C (low power, where available) @@ -117,16 +118,16 @@ constexpr uint8_t getI2CBusCount() { #endif } +// Get the number of usable SPI buses constexpr uint8_t getSPIBusCount() { -#if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) - return 2u; -#elif defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C5) || defined(ESP32C6) || defined(ESP32C61) || defined(ESP32H2) - return 1u; -#elif defined(ESP8266) - return 1u; -#else // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) +#if SOC_SPI_PERIPH_NUM > 3 static_assert(false, "Implement processor architecture"); -#endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) || defined(ESP32P4) + return 2u; +#elif SOC_SPI_PERIPH_NUM > 2 + return 2u; +#else + return 1u; +#endif } /********************************************************************************************\ diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 31f5c3a07..dcf9dba82 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1409,11 +1409,11 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { if (Device[DeviceIndex].isSPI() - && !(Settings.isSPI_valid(0u) || (getSPIBusCount() > 1 && Settings.isSPI_valid(1u)))) { + && Settings.getNrConfiguredSPI_buses() == 0) { addFormNote(F("SPI Bus not configured yet (Hardware page).")); } #ifdef ESP32 - if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + if (Device[DeviceIndex].SpiBusSelect && getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) { uint8_t spiBus = Settings.getSPIBusForTask(taskIndex); SPIInterfaceSelector(F("SPI Bus"), F("pspibus"), diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 5e511fdb4..c57167535 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -303,14 +303,21 @@ void handle_hardware() { #ifdef ESP32_CLASSIC getSPI_optionToString(SPI_Options_e::Hspi), #endif - getSPI_optionToString(SPI_Options_e::UserDefined)}; + getSPI_optionToString(SPI_Options_e::UserDefined_VSPI) +#if SOC_SPI_PERIPH_NUM > 2 + ,getSPI_optionToString(SPI_Options_e::UserDefined_HSPI) +#endif + }; const int spi_index[] = { static_cast(SPI_Options_e::None), static_cast(SPI_Options_e::Vspi_Fspi), #ifdef ESP32_CLASSIC static_cast(SPI_Options_e::Hspi), #endif - static_cast(SPI_Options_e::UserDefined) + static_cast(SPI_Options_e::UserDefined_VSPI) +#if SOC_SPI_PERIPH_NUM > 2 + ,static_cast(SPI_Options_e::UserDefined_HSPI) +#endif }; constexpr size_t nrOptions = NR_ELEMENTS(spi_index); FormSelectorOptions selector(nrOptions, spi_options, spi_index); @@ -337,7 +344,7 @@ void handle_hardware() { #if FEATURE_SD addFormSubHeader(F("SD Card")); #ifdef ESP32 - if (getSPIBusCount() > 1 && (Settings.isSPI_valid(0u) || Settings.isSPI_valid(1u))) { + if (getSPIBusCount() > 1 && (Settings.getNrConfiguredSPI_buses() != 0)) { uint8_t spiBus = Settings.getSPIBusForSDCard(); SPIInterfaceSelector(F("SPI Bus"), F("sdspibus"), From 6d070595dccdab8cdfdb8c385c17ee1a398e77ce Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 11 Feb 2026 16:13:28 +0100 Subject: [PATCH 084/161] [Interfaces] Split SPI and I2C config into new Interfaces page --- src/src/CustomBuild/define_plugin_sets.h | 3 + src/src/DataStructs/SettingsStruct.h | 2 + src/src/DataStructs_templ/SettingsStruct.cpp | 18 + src/src/Helpers/Hardware_I2C.cpp | 9 +- src/src/Helpers/I2C_access.cpp | 8 +- src/src/WebServer/DevicesPage.cpp | 9 +- src/src/WebServer/ESPEasy_WebServer.cpp | 4 + src/src/WebServer/HardwarePage.cpp | 289 ------------- src/src/WebServer/HardwarePage.h | 6 - src/src/WebServer/InterfacesPage.cpp | 404 +++++++++++++++++++ src/src/WebServer/InterfacesPage.h | 18 + src/src/WebServer/Markup_Forms.cpp | 29 ++ src/src/WebServer/Markup_Forms.h | 9 + src/src/WebServer/WebTemplateParser.cpp | 12 + src/src/WebServer/WebTemplateParser.h | 15 +- 15 files changed, 510 insertions(+), 325 deletions(-) create mode 100644 src/src/WebServer/InterfacesPage.cpp create mode 100644 src/src/WebServer/InterfacesPage.h diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 003c3e89a..10248a819 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -103,6 +103,9 @@ To create/register a plugin, you have to : #ifndef WEBSERVER_HARDWARE #define WEBSERVER_HARDWARE #endif + #ifndef WEBSERVER_INTERFACES + #define WEBSERVER_INTERFACES + #endif #ifndef WEBSERVER_PINSTATES #define WEBSERVER_PINSTATES #endif diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index b821da7e6..b9354d171 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -396,6 +396,8 @@ public: // Return true if I2C settings are correct bool isI2CEnabled(uint8_t i2cBus) const; + uint8_t getNrConfiguredI2C_buses() const; + uint8_t getI2CInterface(taskIndex_t TaskIndex) const; int8_t getI2CSdaPin(uint8_t i2cBus) const; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 60333b653..da14795eb 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1215,6 +1215,24 @@ bool SettingsStruct_tmpl::isI2CEnabled(uint8_t i2cBus) const { (getI2CClockSpeedSlow(i2cBus) > 0); } +template +uint8_t SettingsStruct_tmpl::getNrConfiguredI2C_buses() const +{ +#ifdef ESP32 + uint8_t res{}; + if (isI2CEnabled(0)) ++res; + if (getI2CBusCount() > 1) { + if (isI2CEnabled(1)) ++res; +#if FEATURE_I2C_INTERFACE_3 + if (isI2CEnabled(2)) ++res; +#endif + } + return res; +#else + return isI2CEnabled(0) ? 1 : 0; +#endif +} + // stored in I2C_SPI_bus_Flags per Task template uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index 7767958e6..ece26d623 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -13,14 +13,7 @@ void initI2C() { // configure hardware pins according to eeprom settings. - if (!Settings.isI2CEnabled(0) - #if FEATURE_I2C_MULTIPLE - && !Settings.isI2CEnabled(1) - # if FEATURE_I2C_INTERFACE_3 - && !Settings.isI2CEnabled(2) - # endif // if FEATURE_I2C_INTERFACE_3 - #endif // if FEATURE_I2C_MULTIPLE - ) + if (Settings.getNrConfiguredI2C_buses() == 0) { return; } diff --git a/src/src/Helpers/I2C_access.cpp b/src/src/Helpers/I2C_access.cpp index c55563074..be3948efe 100644 --- a/src/src/Helpers/I2C_access.cpp +++ b/src/src/Helpers/I2C_access.cpp @@ -509,13 +509,7 @@ void I2CInterfaceSelector(String label, String id, uint8_t choice, bool reloadWhenNeeded) { - const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1 - ? ((Settings.isI2CEnabled(1) ? 1 : 0) - # if FEATURE_I2C_INTERFACE_3 - + (Settings.isI2CEnabled(2) ? 1 : 0) - # endif // if FEATURE_I2C_INTERFACE_3 - ) - : 0) + (Settings.isI2CEnabled(0) ? 1 : 0); + const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); if (i2cMaxBusCount > 1) { static uint8_t i2cBusCount = 0; diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index dcf9dba82..16ea450b8 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1428,14 +1428,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormSubHeader(F("I2C options")); - if (!Settings.isI2CEnabled(0) - # if FEATURE_I2C_MULTIPLE - && (getI2CBusCount() > 1 && !Settings.isI2CEnabled(1)) - # if FEATURE_I2C_INTERFACE_3 - && (getI2CBusCount() > 2 && !Settings.isI2CEnabled(2)) - # endif // if FEATURE_I2C_INTERFACE_3 - # endif // if FEATURE_I2C_MULTIPLE - ) { + if (Settings.getNrConfiguredI2C_buses() == 0) { addFormNote(F("I2C Bus is not configured yet (Hardware page).")); } diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 12e3efad3..6b73beab4 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -16,6 +16,7 @@ #include "../WebServer/FileList.h" #include "../WebServer/HTML_wrappers.h" #include "../WebServer/HardwarePage.h" +#include "../WebServer/InterfacesPage.h" #include "../WebServer/I2C_Scanner.h" #include "../WebServer/JSON.h" #include "../WebServer/LoadFromFS.h" @@ -272,6 +273,9 @@ void WebServerInit() #ifdef WEBSERVER_HARDWARE web_server.on(F("/hardware"), handle_hardware); #endif // ifdef WEBSERVER_HARDWARE + #ifdef WEBSERVER_INTERFACES + web_server.on(F("/interfaces"), handle_interfaces); + #endif #ifdef WEBSERVER_I2C_SCANNER web_server.on(F("/i2cscanner"), handle_i2cscanner); #endif // ifdef WEBSERVER_I2C_SCANNER diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index c57167535..bcd5a60a1 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -16,16 +16,10 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" -#include "../Helpers/Hardware_I2C.h" -#include "../Helpers/Hardware_SPI.h" #include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" -#if FEATURE_I2C_MULTIPLE -#include "../Helpers/I2C_access.h" -#include "../Helpers/Hardware_device_info.h" -#endif // if FEATURE_I2C_MULTIPLE // ******************************************************************************** // Web Interface hardware page @@ -45,90 +39,6 @@ void handle_hardware() { Settings.Pin_status_led = getFormItemInt(F("pled")); Settings.Pin_status_led_Inversed = isFormItemChecked(F("pledi")); Settings.Pin_Reset = getFormItemInt(F("pres")); - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(0)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda); - update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl); - } - Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch")); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0")); - if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0")); - } else { - Settings.I2C_Multiplexer_Addr = -1; - } - Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0")); - #endif // if FEATURE_I2CMULTIPLEXER - #if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code - if (getI2CBusCount() > 1) { - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(1)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda); - update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl); - } - Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1")); - if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1")); - } else { - Settings.I2C2_Multiplexer_Addr = -1; - } - Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1")); - #endif // if FEATURE_I2CMULTIPLEXER - } - #if FEATURE_I2C_INTERFACE_3 - if (getI2CBusCount() > 2) { - #if FEATURE_PLUGIN_PRIORITY - if (!isI2CPriorityTaskActive(2)) - #endif //if FEATURE_PLUGIN_PRIORITY - { - update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda); - update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl); - } - Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED); - Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW); - #if FEATURE_I2CMULTIPLEXER - Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2")); - if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { - Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2")); - } else { - Settings.I2C3_Multiplexer_Addr = -1; - } - Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2")); - #endif // if FEATURE_I2CMULTIPLEXER - } - #endif // if FEATURE_I2C_INTERFACE_3 - set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); - #endif // if FEATURE_I2C_MULTIPLE - #ifdef ESP32 - Settings.InitSPI = getFormItemInt(F("initspi0"), static_cast(SPI_Options_e::None)); - // User-defined SPI bus 0 GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); - - Settings.InitSPI1 = getFormItemInt(F("initspi1"), static_cast(SPI_Options_e::None)); - // User-defined SPI bus 1 GPIO pins - Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); - Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); - Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); - for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { - if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks - error += strformat(F("SPI bus %u pins not configured correctly!
      "), spi_bus); - } - } - #else //for ESP8266 we keep the old UI - Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init - #endif #if defined(ESP32) && FEATURE_SD Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); #endif // if defined(ESP32) && FEATURE_SD @@ -149,12 +59,6 @@ void handle_hardware() { } error += SaveSettings(); addHtmlError(error); - if (error.isEmpty()) { - // Apply I2C settings. - initI2C(); - // Apply SPI settings - initializeSPIBuses(); - } } addHtml(F("
      ")); @@ -170,177 +74,6 @@ void handle_hardware() { addFormPinSelect(PinSelectPurpose::Reset_pin, formatGpioName_input(F("Switch")), F("pres"), Settings.Pin_Reset); addFormNote(F("Press about 10s for factory reset")); - #if FEATURE_I2CMULTIPLEXER - const __FlashStringHelper *i2c_muxtype_options[] = { - F("- None -"), - F("TCA9548a - 8 channel"), - F("TCA9546a - 4 channel"), - F("TCA9543a - 2 channel"), - F("PCA9540 - 2 channel (experimental)") - }; - const int i2c_muxtype_choices[] = { - I2C_MULTIPLEXER_NONE, - I2C_MULTIPLEXER_TCA9548A, - I2C_MULTIPLEXER_TCA9546A, - I2C_MULTIPLEXER_TCA9543A, - I2C_MULTIPLEXER_PCA9540 - }; - const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices), - i2c_muxtype_options, i2c_muxtype_choices); - -// Select the I2C address for a port multiplexer - - String i2c_mux_options[9]; - int i2c_mux_choices[9]; - uint8_t mux_opt = 0; - i2c_mux_options[mux_opt] = F("- None -"); - i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE; - mux_opt++; - for (int8_t x = 0; x < 8; x++) { - i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x); - if (x == 0) { // PCA9540 has a fixed address 0f 0x70 - i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]"); - } else if (x < 4) { - i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]"); - } else { - i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]"); - } - i2c_mux_choices[mux_opt] = 0x70 + x; - mux_opt++; - } - const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices); - #endif // if FEATURE_I2CMULTIPLEXER - - uint8_t i2cBus = 0; - #if FEATURE_I2C_MULTIPLE - for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) - #endif // if FEATURE_I2C_MULTIPLE - { - #if !FEATURE_I2C_MULTIPLE - addFormSubHeader(F("I2C Bus")); - #else // if !FEATURE_I2C_MULTIPLE - addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus)); - #endif // if !FEATURE_I2C_MULTIPLE - #if FEATURE_PLUGIN_PRIORITY - if (isI2CPriorityTaskActive(i2cBus)) { - I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus); - } else - #endif // if FEATURE_PLUGIN_PRIORITY - { - addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus)); - addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus)); - } - addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000); - addUnit(F("Hz")); - addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most.")); - addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100, 3400000); - addUnit(F("Hz")); - - if (0 == i2cBus) { // Only support Clock-stretching on Bus 1 - addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0); - #ifdef ESP8266 - addUnit(F("usec")); - #endif - #ifdef ESP32 - addUnit(F("1/80 usec")); - #endif - } - - #if FEATURE_I2CMULTIPLEXER - #if !FEATURE_I2C_MULTIPLE - addFormSubHeader(F("I2C Multiplexer")); - #else // if !FEATURE_I2C_MULTIPLE - addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus)); - #endif // if FEATURE_I2C_MULTIPLE - // Select the type of multiplexer to use - { - muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus)); - addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus), Settings.getI2CMultiplexerAddr(i2cBus)); - // addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"), i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus)); - const String id = strformat(F("pi2cmuxreset%u"), i2cBus); - addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id); - addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus)); - addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540.")); - } - #endif // if FEATURE_I2CMULTIPLEXER - } - #if FEATURE_I2C_MULTIPLE - const uint8_t i2cMaxBusCount = (getI2CBusCount() > 1 - ? ((Settings.isI2CEnabled(1) ? 1 : 0) - # if FEATURE_I2C_INTERFACE_3 - + (Settings.isI2CEnabled(2) ? 1 : 0) - # endif // if FEATURE_I2C_INTERFACE_3 - ) - : 0) + (Settings.isI2CEnabled(0) ? 1 : 0); - if (i2cMaxBusCount > 1) { - addFormSubHeader(F("PCF & MCP Direct I/O")); - const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP(); - I2CInterfaceSelector(F("I2C Bus"), - F("pi2cbuspcf"), - i2cBus, - false); - - } - #endif // if FEATURE_I2C_MULTIPLE - - #ifdef ESP32 - for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { - // SPI Init - addFormSubHeader(concat(F("SPI Bus "), spi_bus)); - { - // Script to show GPIO pins for User-defined SPI GPIOs - // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), - // Minified: - html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';" - "document.getElementById('tr_spipinsclk%u').style.display=i," - "document.getElementById('tr_spipinmiso%u').style.display=i," - "document.getElementById('tr_spipinmosi%u').style.display=i" - "}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus), - false); - const __FlashStringHelper * spi_options[] = { - getSPI_optionToString(SPI_Options_e::None), - getSPI_optionToString(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - getSPI_optionToString(SPI_Options_e::Hspi), - #endif - getSPI_optionToString(SPI_Options_e::UserDefined_VSPI) -#if SOC_SPI_PERIPH_NUM > 2 - ,getSPI_optionToString(SPI_Options_e::UserDefined_HSPI) -#endif - }; - const int spi_index[] = { - static_cast(SPI_Options_e::None), - static_cast(SPI_Options_e::Vspi_Fspi), - #ifdef ESP32_CLASSIC - static_cast(SPI_Options_e::Hspi), - #endif - static_cast(SPI_Options_e::UserDefined_VSPI) -#if SOC_SPI_PERIPH_NUM > 2 - ,static_cast(SPI_Options_e::UserDefined_HSPI) -#endif - }; - constexpr size_t nrOptions = NR_ELEMENTS(spi_index); - FormSelectorOptions selector(nrOptions, spi_options, spi_index); - selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus); - selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), concat(F("initspi"), spi_bus), 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1); - int8_t spi_gpio[3]; - Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured - // User-defined pins - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]); - addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); - addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); - html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script - // addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); - addFormNote(F("Chip Select (CS) config must be done in the plugin")); - } - } - #else //for ESP8266 we keep the existing UI - addFormSubHeader(F("SPI Bus 0")); - addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); - addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); - addFormNote(F("Chip Select (CS) config must be done in the plugin")); - #endif // ifdef ESP32 - #if FEATURE_SD addFormSubHeader(F("SD Card")); #ifdef ESP32 @@ -372,26 +105,4 @@ void handle_hardware() { TXBuffer.endStream(); } -#if FEATURE_PLUGIN_PRIORITY -bool isI2CPriorityTaskActive(uint8_t i2cBus) { - bool hasI2CPriorityTask = false; - for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) { - hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus); - } - return hasI2CPriorityTask; -} - -void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) { - int pinnr = -1; - bool input, output, warning = false; - addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus)); - addRowLabel(formatGpioName_bidirectional(F("SDA"))); - getGpioInfo(i2c_sda, pinnr, input, output, warning); - addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false)); - addRowLabel(formatGpioName_output(F("SCL"))); - getGpioInfo(i2c_scl, pinnr, input, output, warning); - addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false)); -} -#endif // if FEATURE_PLUGIN_PRIORITY - #endif // ifdef WEBSERVER_HARDWARE diff --git a/src/src/WebServer/HardwarePage.h b/src/src/WebServer/HardwarePage.h index fa42a9554..324654b90 100644 --- a/src/src/WebServer/HardwarePage.h +++ b/src/src/WebServer/HardwarePage.h @@ -10,12 +10,6 @@ // ******************************************************************************** void handle_hardware(); -# if FEATURE_PLUGIN_PRIORITY -bool isI2CPriorityTaskActive(uint8_t i2cBus); -void I2CShowSdaSclReadonly(int8_t i2c_sda, - int8_t i2c_scl, - uint8_t i2cBus); -# endif // if FEATURE_PLUGIN_PRIORITY #endif // ifdef WEBSERVER_HARDWARE #endif // ifndef WEBSERVER_WEBSERVER_HARDWAREPAGE_H diff --git a/src/src/WebServer/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp new file mode 100644 index 000000000..0557f5689 --- /dev/null +++ b/src/src/WebServer/InterfacesPage.cpp @@ -0,0 +1,404 @@ +#include "../WebServer/InterfacesPage.h" + +#ifdef WEBSERVER_INTERFACES + +# 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 "../CustomBuild/ESPEasyLimits.h" + +# include "../DataStructs/DeviceStruct.h" + +# include "../Globals/Settings.h" + +# include "../Helpers/ESPEasy_Storage.h" +# include "../Helpers/Hardware_GPIO.h" +# include "../Helpers/Hardware_I2C.h" +# include "../Helpers/Hardware_SPI.h" +# include "../Helpers/SPI_Helper.h" +# include "../Helpers/StringConverter.h" +# include "../Helpers/StringGenerator_GPIO.h" + +# if FEATURE_I2C_MULTIPLE +# include "../Helpers/I2C_access.h" +# include "../Helpers/Hardware_device_info.h" +# endif // if FEATURE_I2C_MULTIPLE + +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +void handle_interfaces() { +# ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("handle_interfaces")); +# endif + + if (!isLoggedIn()) { return; } + navMenuIndex = MENU_INDEX_INTERFACES; + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + String error; + bool updated{}; + + if (isFormItem(F("pi2csp0"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(0)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda0"), Settings.Pin_i2c_sda); + update_whenset_FormItemInt(F("pscl0"), Settings.Pin_i2c_scl); + } + Settings.I2C_clockSpeed = getFormItemInt(F("pi2csp0"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C_clockSpeed_Slow = getFormItemInt(F("pi2cspslow0"), DEFAULT_I2C_CLOCK_SPEED_SLOW); + Settings.WireClockStretchLimit = getFormItemInt(F("wirestretch")); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype0")); + + if (Settings.I2C_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr0")); + } else { + Settings.I2C_Multiplexer_Addr = -1; + } + Settings.I2C_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset0")); +# endif // if FEATURE_I2CMULTIPLEXER + } +# if FEATURE_I2C_MULTIPLE // No loop used here, to avoid adding setters to the SettingsStruct template code + + if ((getI2CBusCount() > 1) && isFormItem(F("pi2csp1"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(1)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda1"), Settings.Pin_i2c2_sda); + update_whenset_FormItemInt(F("pscl1"), Settings.Pin_i2c2_scl); + } + Settings.I2C2_clockSpeed = getFormItemInt(F("pi2csp1"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C2_clockSpeed_Slow = getFormItemInt(F("pi2cspslow1"), DEFAULT_I2C_CLOCK_SPEED_SLOW); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C2_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype1")); + + if (Settings.I2C2_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C2_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr1")); + } else { + Settings.I2C2_Multiplexer_Addr = -1; + } + Settings.I2C2_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset1")); +# endif // if FEATURE_I2CMULTIPLEXER + } + + +# if FEATURE_I2C_INTERFACE_3 + + if ((getI2CBusCount() > 2) && isFormItem(F("pi2csp2"))) { + updated = true; +# if FEATURE_PLUGIN_PRIORITY + + if (!isI2CPriorityTaskActive(2)) +# endif // if FEATURE_PLUGIN_PRIORITY + { + update_whenset_FormItemInt(F("psda2"), Settings.Pin_i2c3_sda); + update_whenset_FormItemInt(F("pscl2"), Settings.Pin_i2c3_scl); + } + Settings.I2C3_clockSpeed = getFormItemInt(F("pi2csp2"), DEFAULT_I2C_CLOCK_SPEED); + Settings.I2C3_clockSpeed_Slow = getFormItemInt(F("pi2cspslow2"), DEFAULT_I2C_CLOCK_SPEED_SLOW); +# if FEATURE_I2CMULTIPLEXER + Settings.I2C3_Multiplexer_Type = getFormItemInt(F("pi2cmuxtype2")); + + if (Settings.I2C3_Multiplexer_Type != I2C_MULTIPLEXER_NONE) { + Settings.I2C3_Multiplexer_Addr = getFormItemInt(F("pi2cmuxaddr2")); + } else { + Settings.I2C3_Multiplexer_Addr = -1; + } + Settings.I2C3_Multiplexer_ResetPin = getFormItemInt(F("pi2cmuxreset2")); +# endif // if FEATURE_I2CMULTIPLEXER + } +# endif // if FEATURE_I2C_INTERFACE_3 + + if (isFormItem(F("pi2cbuspcf"))) { + updated = true; + set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); + } +# endif // if FEATURE_I2C_MULTIPLE + + { +# ifdef ESP32 + bool SPI_updated{}; + + if (update_whenset_FormItemInt(F("initspi0"), Settings.InitSPI)) { + SPI_updated = true; + + // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + } + + if (update_whenset_FormItemInt(F("initspi1"), Settings.InitSPI1)) { + SPI_updated = true; + + // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + } + + if (SPI_updated) { + updated = true; + + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks + error += strformat(F("SPI bus %u pins not configured correctly!
      "), spi_bus); + } + } + } +# else // for ESP8266 we keep the old UI + Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init + updated = true; +# endif // ifdef ESP32 + } + + if (updated) { + error += SaveSettings(); + addHtmlError(error); + + if (error.isEmpty()) { + // Apply I2C settings. + initI2C(); + + // Apply SPI settings + initializeSPIBuses(); + } + } + + addHtml(F("")); + html_table_class_normal(); + addFormHeader(F("Interfaces Settings"), F(""), F("Interfaces/Interfaces.html")); + + +# if FEATURE_I2CMULTIPLEXER + const __FlashStringHelper *i2c_muxtype_options[] = { + F("- None -"), + F("TCA9548a - 8 channel"), + F("TCA9546a - 4 channel"), + F("TCA9543a - 2 channel"), + F("PCA9540 - 2 channel (experimental)") + }; + const int i2c_muxtype_choices[] = { + I2C_MULTIPLEXER_NONE, + I2C_MULTIPLEXER_TCA9548A, + I2C_MULTIPLEXER_TCA9546A, + I2C_MULTIPLEXER_TCA9543A, + I2C_MULTIPLEXER_PCA9540 + }; + const FormSelectorOptions muxSelector(NR_ELEMENTS(i2c_muxtype_choices), + i2c_muxtype_options, i2c_muxtype_choices); + + // Select the I2C address for a port multiplexer + + String i2c_mux_options[9]; + int i2c_mux_choices[9]; + uint8_t mux_opt = 0; + i2c_mux_options[mux_opt] = F("- None -"); + i2c_mux_choices[mux_opt] = I2C_MULTIPLEXER_NONE; + mux_opt++; + + for (int8_t x = 0; x < 8; x++) { + i2c_mux_options[mux_opt] = formatToHex_decimal(0x70 + x); + + if (x == 0) { // PCA9540 has a fixed address 0f 0x70 + i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a, PCA9540]"); + } else if (x < 4) { + i2c_mux_options[mux_opt] += F(" [TCA9543a/6a/8a]"); + } else { + i2c_mux_options[mux_opt] += F(" [TCA9546a/8a]"); + } + i2c_mux_choices[mux_opt] = 0x70 + x; + mux_opt++; + } + const FormSelectorOptions addrSelector(mux_opt, i2c_mux_options, i2c_mux_choices); +# endif // if FEATURE_I2CMULTIPLEXER + + uint8_t i2cBus = 0; +# if FEATURE_I2C_MULTIPLE + + for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) +# endif // if FEATURE_I2C_MULTIPLE + { +# if !FEATURE_I2C_MULTIPLE + addFormSubHeader(F("I2C Bus")); +# else + addFormSubHeader(strformat(F("I2C Bus %u"), i2cBus)); +# endif // if !FEATURE_I2C_MULTIPLE +# if FEATURE_PLUGIN_PRIORITY + + if (isI2CPriorityTaskActive(i2cBus)) { + I2CShowSdaSclReadonly(Settings.getI2CSdaPin(i2cBus), Settings.getI2CSclPin(i2cBus), i2cBus); + } else +# endif // if FEATURE_PLUGIN_PRIORITY + { + addFormPinSelectI2C(formatGpioName_bidirectional(F("SDA")), strformat(F("psda%u"), i2cBus), i2cBus, Settings.getI2CSdaPin(i2cBus)); + addFormPinSelectI2C(formatGpioName_output(F("SCL")), strformat(F("pscl%u"), i2cBus), i2cBus, Settings.getI2CSclPin(i2cBus)); + } + addFormNumericBox(F("Clock Speed"), strformat(F("pi2csp%u"), i2cBus), Settings.getI2CClockSpeed(i2cBus), 100, 3400000); + addUnit(F("Hz")); + addFormNote(F("Use 100 kHz for old I2C devices, 400 kHz is max for most.")); + addFormNumericBox(F("Slow device Clock Speed"), strformat(F("pi2cspslow%u"), i2cBus), Settings.getI2CClockSpeedSlow(i2cBus), 100, + 3400000); + addUnit(F("Hz")); + + if (0 == i2cBus) { // Only support Clock-stretching on Bus 1 + addFormNumericBox(F("I2C ClockStretchLimit"), F("wirestretch"), Settings.WireClockStretchLimit, 0); +# ifdef ESP8266 + addUnit(F("usec")); +# endif +# ifdef ESP32 + addUnit(F("1/80 usec")); +# endif + } + +# if FEATURE_I2CMULTIPLEXER +# if !FEATURE_I2C_MULTIPLE + addFormSubHeader(F("I2C Multiplexer")); +# else // if !FEATURE_I2C_MULTIPLE + addFormSubHeader(strformat(F("I2C Multiplexer %u"), i2cBus)); +# endif // if !FEATURE_I2C_MULTIPLE + + // Select the type of multiplexer to use + { + muxSelector.addFormSelector(F("I2C Multiplexer type"), strformat(F("pi2cmuxtype%u"), i2cBus), Settings.getI2CMultiplexerType(i2cBus)); + addrSelector.addFormSelector(F("I2C Multiplexer address"), strformat(F("pi2cmuxaddr%u"), i2cBus), + Settings.getI2CMultiplexerAddr(i2cBus)); + + // addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), strformat(F("pi2cmuxreset%u"), + // i2cBus), Settings.getI2CMultiplexerResetPin(i2cBus)); + const String id = strformat(F("pi2cmuxreset%u"), i2cBus); + addRowLabel_tr_id(formatGpioName_output_optional(F("Reset")), id); + addPinSelect(PinSelectPurpose::Generic_output, id, Settings.getI2CMultiplexerResetPin(i2cBus)); + addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540.")); + } +# endif // if FEATURE_I2CMULTIPLEXER + } +# if FEATURE_I2C_MULTIPLE + const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); + + if (i2cMaxBusCount > 1) { + addFormSubHeader(F("PCF & MCP Direct I/O")); + const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP(); + I2CInterfaceSelector(F("I2C Bus"), + F("pi2cbuspcf"), + i2cBus, + false); + + } +# endif // if FEATURE_I2C_MULTIPLE + +# ifdef ESP32 + + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + // SPI Init + addFormSubHeader(concat(F("SPI Bus "), spi_bus)); + { + // Script to show GPIO pins for User-defined SPI GPIOs + // html_add_script(F("function spiOptionChanged(elem) {var spipinstyle = elem.value == 9 ? '' : + // 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display + // = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), + // Minified: + html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';" + "document.getElementById('tr_spipinsclk%u').style.display=i," + "document.getElementById('tr_spipinmiso%u').style.display=i," + "document.getElementById('tr_spipinmosi%u').style.display=i" + "}"), spi_bus, spi_bus, spi_bus, spi_bus, spi_bus), + false); + const __FlashStringHelper *spi_options[] = { + getSPI_optionToString(SPI_Options_e::None), + getSPI_optionToString(SPI_Options_e::Vspi_Fspi), +# ifdef ESP32_CLASSIC + getSPI_optionToString(SPI_Options_e::Hspi), +# endif + getSPI_optionToString(SPI_Options_e::UserDefined_VSPI) +# if SOC_SPI_PERIPH_NUM > 2 + , getSPI_optionToString(SPI_Options_e::UserDefined_HSPI) +# endif + }; + const int spi_index[] = { + static_cast(SPI_Options_e::None), + static_cast(SPI_Options_e::Vspi_Fspi), +# ifdef ESP32_CLASSIC + static_cast(SPI_Options_e::Hspi), +# endif + static_cast(SPI_Options_e::UserDefined_VSPI) +# if SOC_SPI_PERIPH_NUM > 2 + , static_cast(SPI_Options_e::UserDefined_HSPI) +# endif + }; + constexpr size_t nrOptions = NR_ELEMENTS(spi_index); + FormSelectorOptions selector(nrOptions, spi_options, spi_index); + selector.onChangeCall = strformat(F("spi%uOptionChanged(this)"), spi_bus); + selector.addFormSelector(concat(F("Init SPI Bus "), spi_bus), + concat(F("initspi"), spi_bus), + 0 == spi_bus ? Settings.InitSPI : Settings.InitSPI1); + int8_t spi_gpio[3]; + Settings.getSPI_pins(spi_gpio, spi_bus, true); // Load GPIO pins even if wrongly configured + // User-defined pins + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("CLK")), concat(F("spipinsclk"), spi_bus), spi_gpio[0]); + addFormPinSelect(PinSelectPurpose::SPI_MISO, formatGpioName_input(F("MISO")), concat(F("spipinmiso"), spi_bus), spi_gpio[1]); + addFormPinSelect(PinSelectPurpose::SPI, formatGpioName_output(F("MOSI")), concat(F("spipinmosi"), spi_bus), spi_gpio[2]); + html_add_script(strformat(F("document.getElementById('initspi%u').onchange();"), spi_bus), false); // Initial trigger onchange script + // addFormNote(F("Changing SPI settings requires to press the hardware-reset button or power off-on!")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); + } + } +# else // for ESP8266 we keep the existing UI + addFormSubHeader(F("SPI Bus 0")); + addFormCheckBox(F("Init SPI"), F("initspi"), Settings.InitSPI > static_cast(SPI_Options_e::None)); + addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); + addFormNote(F("Chip Select (CS) config must be done in the plugin")); +# endif // ifdef ESP32 + + addFormSeparator(2); + + html_TR_TD(); + html_TD(); + addSubmitButton(); + html_TR_TD(); + html_end_table(); + html_end_form(); + + sendHeadandTail_stdtemplate(_TAIL); + TXBuffer.endStream(); +} + +# if FEATURE_PLUGIN_PRIORITY + +bool isI2CPriorityTaskActive(uint8_t i2cBus) { + bool hasI2CPriorityTask = false; + + for (taskIndex_t taskIndex = 0; taskIndex < TASKS_MAX && !hasI2CPriorityTask; taskIndex++) { + hasI2CPriorityTask |= isPluginI2CPowerManager_from_TaskIndex(taskIndex, i2cBus); + } + return hasI2CPriorityTask; +} + +void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) { + int pinnr = -1; + bool input, output, warning = false; + + addFormNote(strformat(F("I2C (%d) GPIO pins can't be changed when an I2C Priority task is configured."), i2cBus)); + addRowLabel(formatGpioName_bidirectional(F("SDA"))); + getGpioInfo(i2c_sda, pinnr, input, output, warning); + addHtml(createGPIO_label(i2c_sda, pinnr, true, true, false)); + addRowLabel(formatGpioName_output(F("SCL"))); + getGpioInfo(i2c_scl, pinnr, input, output, warning); + addHtml(createGPIO_label(i2c_scl, pinnr, true, true, false)); +} + +# endif // if FEATURE_PLUGIN_PRIORITY + +#endif // ifdef WEBSERVER_INTERFACES diff --git a/src/src/WebServer/InterfacesPage.h b/src/src/WebServer/InterfacesPage.h new file mode 100644 index 000000000..21612f7ce --- /dev/null +++ b/src/src/WebServer/InterfacesPage.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../WebServer/common.h" + +#ifdef WEBSERVER_INTERFACES + +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +void handle_interfaces(); + +# if FEATURE_PLUGIN_PRIORITY +bool isI2CPriorityTaskActive(uint8_t i2cBus); +void I2CShowSdaSclReadonly(int8_t i2c_sda, + int8_t i2c_scl, + uint8_t i2cBus); +# endif +#endif diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 0f2d7b7b3..0fcc774b3 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -660,6 +660,16 @@ bool getCheckWebserverArg_int(const String& key, int& value) { return res; } +bool getCheckWebserverArg_int(const String& key, + uint32_t & value) { + const String valueStr = webArg(key); + if (valueStr.isEmpty()) return false; + uint32_t tmp{}; + const bool res = validUIntFromString(valueStr, tmp); + value = tmp; + return res; +} + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int & value) { @@ -676,6 +686,25 @@ bool update_whenset_FormItemInt(const String& key, int& value) { return false; } +bool update_whenset_FormItemInt(const __FlashStringHelper * key, + uint32_t & value) +{ + return update_whenset_FormItemInt(String(key), value); +} + +bool update_whenset_FormItemInt(const String& key, + uint32_t & value) +{ + uint32_t tmpVal; + + if (getCheckWebserverArg_int(key, tmpVal)) { + value = tmpVal; + return true; + } + return false; +} + + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int8_t& value) { diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 6f79d75d9..cdfcd9b3f 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -318,12 +318,21 @@ int getFormItemInt(const String& key, int defaultValue); bool getCheckWebserverArg_int(const String& key, int & value); +bool getCheckWebserverArg_int(const String& key, + uint32_t & value); + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int & value); bool update_whenset_FormItemInt(const String& key, int & value); +bool update_whenset_FormItemInt(const __FlashStringHelper * key, + uint32_t & value); + +bool update_whenset_FormItemInt(const String& key, + uint32_t & value); + bool update_whenset_FormItemInt(const __FlashStringHelper * key, int8_t & value); diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index 49ead277e..de034e301 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -55,6 +55,14 @@ #endif #endif // ifndef MENU_INDEX_HARDWARE_VISIBLE +#ifndef MENU_INDEX_INTERFACES_VISIBLE +#ifdef WEBSERVER_INTERFACES + # define MENU_INDEX_INTERFACES_VISIBLE true +#else + # define MENU_INDEX_INTERFACES_VISIBLE false +#endif +#endif + #ifndef MENU_INDEX_DEVICES_VISIBLE #ifdef WEBSERVER_DEVICES # define MENU_INDEX_DEVICES_VISIBLE true @@ -107,6 +115,7 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { case MENU_INDEX_NETWORK: return ICON("🌐"); // Alternative 🖧 (not working on Apple) case MENU_INDEX_CONTROLLERS: return ICON("✆"); case MENU_INDEX_HARDWARE: return ICON("☷"); + case MENU_INDEX_INTERFACES: return ICON("☷"); case MENU_INDEX_DEVICES: return ICON("❖"); case MENU_INDEX_RULES: return ICON("⧴"); case MENU_INDEX_NOTIFICATIONS: return ICON("✉"); @@ -122,6 +131,7 @@ const __FlashStringHelper* getGpMenuLabel(uint8_t index) { case MENU_INDEX_NETWORK: return F("Network"); case MENU_INDEX_CONTROLLERS: return F("Controllers"); case MENU_INDEX_HARDWARE: return F("Hardware"); + case MENU_INDEX_INTERFACES:return F("Interfaces"); case MENU_INDEX_DEVICES: return F("Devices"); case MENU_INDEX_RULES: return F("Rules"); case MENU_INDEX_NOTIFICATIONS: return F("Notifications"); @@ -137,6 +147,7 @@ const __FlashStringHelper* getGpMenuURL(uint8_t index) { case MENU_INDEX_NETWORK: return F("/network"); case MENU_INDEX_CONTROLLERS: return F("/controllers"); case MENU_INDEX_HARDWARE: return F("/hardware"); + case MENU_INDEX_INTERFACES: return F("/interfaces"); case MENU_INDEX_DEVICES: return F("/devices"); case MENU_INDEX_RULES: return F("/rules"); case MENU_INDEX_NOTIFICATIONS: return F("/notifications"); @@ -152,6 +163,7 @@ bool GpMenuVisible(uint8_t index) { case MENU_INDEX_NETWORK: return MENU_INDEX_NETWORK_VISIBLE; case MENU_INDEX_CONTROLLERS: return MENU_INDEX_CONTROLLERS_VISIBLE; case MENU_INDEX_HARDWARE: return MENU_INDEX_HARDWARE_VISIBLE; + case MENU_INDEX_INTERFACES: return MENU_INDEX_INTERFACES_VISIBLE; case MENU_INDEX_DEVICES: return MENU_INDEX_DEVICES_VISIBLE; case MENU_INDEX_RULES: return MENU_INDEX_RULES_VISIBLE; case MENU_INDEX_NOTIFICATIONS: return MENU_INDEX_NOTIFICATIONS_VISIBLE; diff --git a/src/src/WebServer/WebTemplateParser.h b/src/src/WebServer/WebTemplateParser.h index 3d0ad9183..8135b477e 100644 --- a/src/src/WebServer/WebTemplateParser.h +++ b/src/src/WebServer/WebTemplateParser.h @@ -12,13 +12,14 @@ #define MENU_INDEX_MAIN 0 #define MENU_INDEX_CONFIG 1 #define MENU_INDEX_HARDWARE 2 -#define MENU_INDEX_NETWORK 3 -#define MENU_INDEX_CONTROLLERS 4 -#define MENU_INDEX_BUSES 5 -#define MENU_INDEX_DEVICES 6 -#define MENU_INDEX_RULES 7 -#define MENU_INDEX_NOTIFICATIONS 8 -#define MENU_INDEX_TOOLS 9 +#define MENU_INDEX_INTERFACES 3 +#define MENU_INDEX_NETWORK 4 +#define MENU_INDEX_CONTROLLERS 5 +#define MENU_INDEX_BUSES 6 +#define MENU_INDEX_DEVICES 7 +#define MENU_INDEX_RULES 8 +#define MENU_INDEX_NOTIFICATIONS 9 +#define MENU_INDEX_TOOLS 10 #define MENU_INDEX_SETUP 254 #define MENU_INDEX_CUSTOM_PAGE 255 From 3a1515ad21318b31af20a4122d48b1e88e22a243 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 11 Feb 2026 17:17:12 +0100 Subject: [PATCH 085/161] [WebUI] Add some icon to Interfaces tab + minor tweaks --- src/src/WebServer/HardwarePage.cpp | 6 ++++- src/src/WebServer/InterfacesPage.cpp | 32 +++++++++++++++---------- src/src/WebServer/WebTemplateParser.cpp | 2 +- static/espeasy_default.css | 2 +- static/espeasy_default.min.css | 2 +- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index bcd5a60a1..208d02cb5 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -68,7 +68,11 @@ void handle_hardware() { addFormSubHeader(F("Wifi Status LED")); addFormPinSelect(PinSelectPurpose::Status_led, formatGpioName_output(F("LED")), F("pled"), Settings.Pin_status_led); addFormCheckBox(F("Inversed LED"), F("pledi"), Settings.Pin_status_led_Inversed); - addFormNote(F("Use ’GPIO-2 (D4)’ with ’Inversed’ checked for onboard LED")); + addFormNote(F("Use ’GPIO-2" +#ifdef ESP8266 + " (D4)" +#endif + "’ with ’Inversed’ checked for onboard LED")); addFormSubHeader(F("Reset Pin")); addFormPinSelect(PinSelectPurpose::Reset_pin, formatGpioName_input(F("Switch")), F("pres"), Settings.Pin_Reset); diff --git a/src/src/WebServer/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp index 0557f5689..bda1cd45b 100644 --- a/src/src/WebServer/InterfacesPage.cpp +++ b/src/src/WebServer/InterfacesPage.cpp @@ -27,19 +27,9 @@ # include "../Helpers/Hardware_device_info.h" # endif // if FEATURE_I2C_MULTIPLE -// ******************************************************************************** -// Web Interface hardware page -// ******************************************************************************** -void handle_interfaces() { -# ifndef BUILD_NO_RAM_TRACKER - checkRAM(F("handle_interfaces")); -# endif - - if (!isLoggedIn()) { return; } - navMenuIndex = MENU_INDEX_INTERFACES; - TXBuffer.startStream(); - sendHeadandTail_stdtemplate(_HEAD); +void save_interfaces() { + String error; bool updated{}; @@ -176,6 +166,24 @@ void handle_interfaces() { initializeSPIBuses(); } } +} + + +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +void handle_interfaces() { +# ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("handle_interfaces")); +# endif + + if (!isLoggedIn()) { return; } + navMenuIndex = MENU_INDEX_INTERFACES; + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + save_interfaces(); + addHtml(F("")); html_table_class_normal(); diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index de034e301..655f5bbf2 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -115,7 +115,7 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { case MENU_INDEX_NETWORK: return ICON("🌐"); // Alternative 🖧 (not working on Apple) case MENU_INDEX_CONTROLLERS: return ICON("✆"); case MENU_INDEX_HARDWARE: return ICON("☷"); - case MENU_INDEX_INTERFACES: return ICON("☷"); + case MENU_INDEX_INTERFACES: return ICON("⎆"); case MENU_INDEX_DEVICES: return ICON("❖"); case MENU_INDEX_RULES: return ICON("⧴"); case MENU_INDEX_NOTIFICATIONS: return ICON("✉"); diff --git a/static/espeasy_default.css b/static/espeasy_default.css index 1540ac2b3..6c8beebd7 100644 --- a/static/espeasy_default.css +++ b/static/espeasy_default.css @@ -457,7 +457,7 @@ textarea, display: flex; position: inherit; top: 54px; - max-width: 960px; + max-width: 1180px; } a.menu { diff --git a/static/espeasy_default.min.css b/static/espeasy_default.min.css index 96f27fe28..d4da49eae 100644 --- a/static/espeasy_default.min.css +++ b/static/espeasy_default.min.css @@ -1 +1 @@ -:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;--bdc1:silver;--bg4:#e8e8e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--bdc2:#444 #444 #fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10:#07d;--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0;}[data-theme="dark"]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1;}@media (prefers-color-scheme:dark){[data-theme="auto"]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}}*:not(font,a),.closebtn,.div_r,h1,h6,.wide,textarea{color:var(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;position:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}.closebtn,.div_r{float:right}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt;margin:0}h1{font-size:16pt;margin:8px 0}h3{background-color:var(--bg2);margin:-5px;padding:6px}h6{font-size:10pt}pre{font:1em monospace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}input,select,.checkmark{background-color:var(--bg1)}.button.help,.checkmark,input,select,hr,textarea,#toastmessage,.dotmark,.logviewer{border:.1px solid gray;}input[disabled],input[disabled]:hover{background-color:var(--bg7);}.button.link.wide{display:inline-block;text-align:center;}.button.link.red{background-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius:5px}.button:hover{background:#369}input,select,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-width:85px;width:95%}textarea.wide{max-width:1000px;}.xwide{width:300px}.widenumber{max-width:100px;}.container,.container2{cursor:pointer;font-size:12pt;padding-left:35px}.container{display:block;height:30px;margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disabled{background-color:grey}.checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #fff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3)}.container2{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotmark{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2 .dotmark:after{background:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}textarea{font-family:'Lucida Console',Monaco,monospace;max-height:60vh;width:-webkit-fill-available;width:-moz-available}#toastmessage{border-radius:5px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibility:hidden;z-index:1}#toastmessage.show{animation:fadein .5s,fadeout .5s 2.5s;visibility:visible}.container input,.container2 input{cursor:pointer;opacity:0;position:absolute}.container input:checked~.checkmark:after,.container2,.container2 input:checked~.dotmark:after{display:block}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}.level_1{color:#fcff95}.level_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color:#f2ab39}.level_9{color:#f50}.logviewer{background-color:#272727;color:#f1f1f1;overflow:auto;height:60vh}tbody{overflow:auto}.multirow th,.normal th{background-color:#444;border-color:var(--bdc1);color:var(--c1);padding:5px;width:100vw}.multirow,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.normal tr{padding:5px}.normal td{height:30px}.multirow td{border-color:var(--bdc1);height:30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}.sub{border-spacing:0;max-width:100px;display:block}textarea,.even tr:nth-child(2n),.tworow tr:nth-child(4n+3),.tworow tr:nth-child(4n+4){background-color:var(--bg4)}.apheader,.headermenu{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style:italic}.headermenu{background-color:var(--bg11);border-bottom:.1px solid #444;height:100px;position:fixed;top:0;z-index:1}.bodymenu{height:100vh;padding-top:100px}.menubar{display:flex;position:inherit;top:54px;max-width:960px;}a.menu{align-items:center;display:flex;height:46px;justify-content:center;gap:2px;width:100%}.menu{color:var(--c2);border:solid transparent 1px;border-radius:5px 5px 0 0;padding:10px 10px 8px;white-space:nowrap}.menu.active{background-color:var(--bg6);border-color:var(--bdc2);color:var(--c3);border-width:.1px}.menu:hover{background:var(--bg7);color:var(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_r{background-color:var(--bg8);border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20px}.highlight td{background-color:var(--bg5)}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x:auto;padding:10px 0}footer{padding:0 5px;word-break:break-all}body{background-color:var(--bg6)}.closebtn,.div_r{color:var(--c6)}a,h1,h6{color:var(--c5)}.button,.container input:checked~.checkmark:not(.disabled),.container2 input:checked~.dotmark{background-color:var(--bg10)}canvas{filter:invert(var(--iv)) hue-rotate(var(--ro));}@media screen and (max-width:960px){.showmenulabel{display:none}.menubar{width:100%;max-width:700px;left:0}}@media screen and (max-width:450px){.normal{min-width:300px}input.wide:focus{left:4px;position:absolute;z-index:1}} +:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;--bdc1:silver;--bg4:#e8e8e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--bdc2:#444 #444 #fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10:#07d;--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0;}[data-theme="dark"]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1;}@media (prefers-color-scheme:dark){[data-theme="auto"]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}}*:not(font,a),.closebtn,.div_r,h1,h6,.wide,textarea{color:var(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;position:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}.closebtn,.div_r{float:right}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt;margin:0}h1{font-size:16pt;margin:8px 0}h3{background-color:var(--bg2);margin:-5px;padding:6px}h6{font-size:10pt}pre{font:1em monospace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}input,select,.checkmark{background-color:var(--bg1)}.button.help,.checkmark,input,select,hr,textarea,#toastmessage,.dotmark,.logviewer{border:.1px solid gray;}input[disabled],input[disabled]:hover{background-color:var(--bg7);}.button.link.wide{display:inline-block;text-align:center;}.button.link.red{background-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius:5px}.button:hover{background:#369}input,select,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-width:85px;width:95%}textarea.wide{max-width:1000px;}.xwide{width:300px}.widenumber{max-width:100px;}.container,.container2{cursor:pointer;font-size:12pt;padding-left:35px}.container{display:block;height:30px;margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disabled{background-color:grey}.checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #fff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3)}.container2{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotmark{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2 .dotmark:after{background:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}textarea{font-family:'Lucida Console',Monaco,monospace;max-height:60vh;width:-webkit-fill-available;width:-moz-available}#toastmessage{border-radius:5px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibility:hidden;z-index:1}#toastmessage.show{animation:fadein .5s,fadeout .5s 2.5s;visibility:visible}.container input,.container2 input{cursor:pointer;opacity:0;position:absolute}.container input:checked~.checkmark:after,.container2,.container2 input:checked~.dotmark:after{display:block}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}.level_1{color:#fcff95}.level_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color:#f2ab39}.level_9{color:#f50}.logviewer{background-color:#272727;color:#f1f1f1;overflow:auto;height:60vh}tbody{overflow:auto}.multirow th,.normal th{background-color:#444;border-color:var(--bdc1);color:var(--c1);padding:5px;width:100vw}.multirow,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.normal tr{padding:5px}.normal td{height:30px}.multirow td{border-color:var(--bdc1);height:30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}.sub{border-spacing:0;max-width:100px;display:block}textarea,.even tr:nth-child(2n),.tworow tr:nth-child(4n+3),.tworow tr:nth-child(4n+4){background-color:var(--bg4)}.apheader,.headermenu{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style:italic}.headermenu{background-color:var(--bg11);border-bottom:.1px solid #444;height:100px;position:fixed;top:0;z-index:1}.bodymenu{height:100vh;padding-top:100px}.menubar{display:flex;position:inherit;top:54px;max-width:1180px;}a.menu{align-items:center;display:flex;height:46px;justify-content:center;gap:2px;width:100%}.menu{color:var(--c2);border:solid transparent 1px;border-radius:5px 5px 0 0;padding:10px 10px 8px;white-space:nowrap}.menu.active{background-color:var(--bg6);border-color:var(--bdc2);color:var(--c3);border-width:.1px}.menu:hover{background:var(--bg7);color:var(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_r{background-color:var(--bg8);border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20px}.highlight td{background-color:var(--bg5)}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x:auto;padding:10px 0}footer{padding:0 5px;word-break:break-all}body{background-color:var(--bg6)}.closebtn,.div_r{color:var(--c6)}a,h1,h6{color:var(--c5)}.button,.container input:checked~.checkmark:not(.disabled),.container2 input:checked~.dotmark{background-color:var(--bg10)}canvas{filter:invert(var(--iv)) hue-rotate(var(--ro));}@media screen and (max-width:960px){.showmenulabel{display:none}.menubar{width:100%;max-width:700px;left:0}}@media screen and (max-width:450px){.normal{min-width:300px}input.wide:focus{left:4px;position:absolute;z-index:1}} From 7ba7226fd0ba14541ebd57cd76a829605b014e0c Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 11 Feb 2026 17:22:56 +0100 Subject: [PATCH 086/161] [ESP-IDF] Update to latest ESP-IDF/Arduino SDK --- 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 204635cd5..26f877ed3 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -171,7 +171,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1311-2008-5.5_orig/framework-arduinoespressif32-release_v5.5_orig-276436da.tar.xz platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF55_gcc15 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/0602-2247-5.5/framework-arduinoespressif32-release_v5.5-7eede06a.tar.xz +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/1002-1125-5.5_gcc151/framework-arduinoespressif32-release_v5.5_gcc151-7eede06a.tar.xz custom_remove_include = true From 59c3953c97997a7db03a02561a6601d71f52ff07 Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 11 Feb 2026 21:33:04 +0100 Subject: [PATCH 087/161] [Web UI] Change icon for Interfaces --- src/src/WebServer/WebTemplateParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index 655f5bbf2..7ae539c4e 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -115,7 +115,7 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { case MENU_INDEX_NETWORK: return ICON("🌐"); // Alternative 🖧 (not working on Apple) case MENU_INDEX_CONTROLLERS: return ICON("✆"); case MENU_INDEX_HARDWARE: return ICON("☷"); - case MENU_INDEX_INTERFACES: return ICON("⎆"); + case MENU_INDEX_INTERFACES: return ICON("❉"); case MENU_INDEX_DEVICES: return ICON("❖"); case MENU_INDEX_RULES: return ICON("⧴"); case MENU_INDEX_NOTIFICATIONS: return ICON("✉"); From ba41ec3d0e699f3e1122c3932f3491e34cefd82c Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 12 Feb 2026 12:35:42 +0100 Subject: [PATCH 088/161] [Build] Try to reduce build size for ESP8266 minimal, disable I2C/SPI --- platformio_esp82xx_base.ini | 102 ++++++++++ platformio_esp82xx_envs.ini | 10 +- src/src/Commands/InternalCommands.cpp | 2 + src/src/Commands/i2c.cpp | 2 + src/src/Commands/i2c.h | 4 +- src/src/CustomBuild/define_plugin_sets.h | 49 +++++ src/src/DataStructs/DeviceStruct.cpp | 13 +- src/src/DataStructs/DeviceStruct.h | 7 +- src/src/DataStructs/SettingsStruct.h | 14 +- src/src/DataStructs_templ/SettingsStruct.cpp | 14 +- src/src/ESPEasyCore/ESPEasy_loop.cpp | 2 + src/src/Globals/I2Cdev.cpp | 2 + src/src/Globals/I2Cdev.h | 3 + src/src/Globals/Plugins.cpp | 14 +- src/src/Globals/Plugins.h | 4 +- src/src/Helpers/Hardware.cpp | 4 + src/src/Helpers/Hardware_I2C.cpp | 4 +- src/src/Helpers/Hardware_I2C.h | 4 +- src/src/Helpers/Hardware_SPI.cpp | 2 + src/src/Helpers/Hardware_SPI.h | 3 +- src/src/Helpers/I2C_Plugin_Helper.cpp | 2 + src/src/Helpers/I2C_Plugin_Helper.h | 3 + src/src/Helpers/I2C_access.cpp | 3 + src/src/Helpers/I2C_access.h | 3 + src/src/Helpers/OLed_helper.cpp | 3 + src/src/Helpers/OLed_helper.h | 7 +- src/src/Helpers/PeriodicalActions.cpp | 2 + src/src/Helpers/SPI_Helper.cpp | 2 + src/src/Helpers/SPI_Helper.h | 2 + src/src/Helpers/StringGenerator_GPIO.cpp | 18 +- src/src/Helpers/StringGenerator_GPIO.h | 4 + src/src/WebServer/DevicesPage.cpp | 64 +++++- src/src/WebServer/DevicesPage.h | 13 +- src/src/WebServer/ESPEasy_WebServer.cpp | 2 + src/src/WebServer/HTML_wrappers.cpp | 12 +- src/src/WebServer/InterfacesPage.cpp | 204 +++++++++++-------- src/src/WebServer/InterfacesPage.h | 12 ++ src/src/WebServer/Log.cpp | 3 + src/src/WebServer/Log.h | 4 +- src/src/WebServer/Markup.cpp | 16 +- src/src/WebServer/Markup_Forms.cpp | 6 +- src/src/WebServer/Markup_Forms.h | 3 +- 42 files changed, 513 insertions(+), 134 deletions(-) diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 40c4fd013..d8584466f 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -305,6 +305,108 @@ build_flags = ${esp82xx_1M.build_flags} -DDISABLE_SC16IS752_Serial -DFEATURE_GPIO_USE_ESP8266_WAVEFORM=0 extra_scripts = ${esp82xx_1M.extra_scripts} +lib_ignore = ESP32_ping + ESP32WebServer + ESP32HTTPUpdateServer + ServoESP32 + TinyWireM + ESP8266SdFat + SD(esp8266) + SD + SDFS + LittleFS(esp8266) + LittleFS + ArduinoOTA + ESP8266mDNS + I2C AXP192 Power management + EspSoftwareSerial + AXP2101 Power management (I2C) + AM2320 + AS3935MI + AS5600 + AS_BH1750 + Adafruit BME680 Library + Adafruit BMP3XX Library + Adafruit GFX Library + Adafruit HMC5883 Unified + Adafruit ILI9341 ESPEasy + Adafruit MPR121 + Adafruit Motor Shield V2 Library + Adafruit NeoMatrix via NeoPixelBus + Adafruit PCD8544 Nokia 5110 LCD library + Adafruit SGP30 Sensor + Adafruit SI1145 Library + Adafruit TCS34725 + Adafruit TSL2591 Library + Adafruit Unified Sensor + Adafruit VEML7700 Library + Adafruit seesaw Library + Adafruit_ST77xx + AnyRtttl +; ArduinoJson + Blynk + CHT16K33 LED Matrix Library + CircularBuffer + ClimateGuard RadSens + DFRobot_GP8403_ESPEasy + DNSServer +; ESP8266HTTPClient +; ESP8266HTTPUpdateServer +; ESP8266WebServer +; ESP8266WiFi + ESP8266_SSD1306 +; ESPeasySerial + ESPping + ESP8266 and ESP32 Oled Driver for SSD1306 display + FrogmoreScd30 +; GPIO_DirectAccess + HLW8012 + HeatpumpIR + I2Cdevlib-Core + ILI9488 + IRremoteESP8266 + ITG3205 + Improv WiFi Library + IthoCC1101 + LOLIN_EPD + LiquidCrystal_I2C + MD_MAX72XX + MD_Parola + MFRC522 + MechInputs + Multi Channel Relay Arduino Library + NeoPixelBus + NeoPixelBus_wrapper + NewPing + Noiasca NeopixelDisplay + PZEM-004T-v30 +; PubSubClient3 + RAK12019_LTR390_UV_Light + RN2xx3 Arduino Library + RTClib + Regexp + SC16IS752 + SDM + SPI + Sensirion Gas Index Algorithm + SerialSensors + Servo + ShiftRegister74HC595_NonTemplate + SparkFun ADXL345 Arduino Library + SparkFun APDS9960 RGB and Gesture Sensor + SparkFun MAX1704x Fuel Gauge Arduino Library + SparkFun SCD4x Arduino Library + SparkFun VL53L1X 4m Laser Distance Sensor + SparkFun_AS7265x + TinyGPSPlus + VL53L0X + WakeOnLan + Wire + XPT2046_Touchscreen + bb_captouch + htcw_ip5306 + ld2410 + supertinycron ;lib_ignore = ${esp82xx_1M.lib_ignore} ; Adding the libs below to the lib_ignore will even increase build size ; Adafruit TCS34725 diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 251b82fb8..df323aead 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -375,21 +375,21 @@ build_flags = ${esp8266_1M_OTA.build_flags} extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_domoticz.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_274_ESP8266_1M_OTA_Domoticz_MQTT] extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_domoticz_MQTT.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_274_ESP8266_1M_OTA_FHEM_HA] extends = esp8266_1M_OTA, core274_platform build_flags = ${core274_platform.build_flags} ${minimal_OTA_FHEM_HA.build_flags} -lib_ignore = ${core274_platform.lib_ignore} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_312_ESP8266_1M_OTA_Domoticz] @@ -397,6 +397,8 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_domoticz.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} + [env:minimal_core_312_ESP8266_1M_OTA_Domoticz_MQTT] @@ -404,6 +406,7 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_domoticz_MQTT.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} [env:minimal_core_312_ESP8266_1M_OTA_FHEM_HA] @@ -411,6 +414,7 @@ extends = esp8266_1M_OTA, core312_platform build_flags = ${core312_platform.build_flags} ${minimal_OTA_FHEM_HA.build_flags} build_unflags = ${core312_platform.build_unflags} +lib_ignore = ${esp8266_1M_OTA.lib_ignore} diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index d4fc20f71..5d9ddbf46 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -326,7 +326,9 @@ bool InternalCommands::executeInternalCommand() #if FEATURE_WIFI case ESPEasy_cmd_e::hiddenssid: COMMAND_CASE_R(Command_Wifi_HiddenSSID, 1); // wifi.h #endif +#if FEATURE_I2C case ESPEasy_cmd_e::i2cscanner: COMMAND_CASE_R(Command_i2c_Scanner, -1); // i2c.h +#endif case ESPEasy_cmd_e::inc: COMMAND_CASE_A(Command_Rules_Inc, -1); // Rules.h case ESPEasy_cmd_e::ip: COMMAND_CASE_R(Command_IP, 1); // Network Command #if FEATURE_USE_IPV6 diff --git a/src/src/Commands/i2c.cpp b/src/src/Commands/i2c.cpp index d0e69777b..36a6d9618 100644 --- a/src/src/Commands/i2c.cpp +++ b/src/src/Commands/i2c.cpp @@ -11,6 +11,7 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C void i2c_scanI2Cbus(bool dbg, int8_t channel, uint8_t i2cBus) { uint8_t error, address; @@ -70,3 +71,4 @@ const __FlashStringHelper* Command_i2c_Scanner(struct EventStruct *event, const I2CSelectHighClockSpeed(0); // By default the bus is in standard speed return return_see_serial(event); } +#endif diff --git a/src/src/Commands/i2c.h b/src/src/Commands/i2c.h index 777308ff0..2131873db 100644 --- a/src/src/Commands/i2c.h +++ b/src/src/Commands/i2c.h @@ -2,12 +2,12 @@ #define COMMAND_I2C_H #include "../../ESPEasy_common.h" - +#if FEATURE_I2C void i2c_scanI2Cbus(bool dbg, int8_t channel, uint8_t i2cBus); const __FlashStringHelper* Command_i2c_Scanner(struct EventStruct *event, const char *Line); - +#endif #endif // COMMAND_I2C_H diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 10248a819..ba273ea52 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -55,8 +55,10 @@ To create/register a plugin, you have to : #define WEBSERVER_INCLUDE_JS #endif #ifndef WEBSERVER_LOG + #ifndef PLUGIN_BUILD_MINIMAL_OTA #define WEBSERVER_LOG #endif + #endif #ifndef WEBSERVER_JSON #define WEBSERVER_JSON #endif @@ -4471,4 +4473,51 @@ To create/register a plugin, you have to : #endif #endif +#ifndef FEATURE_I2C +#ifdef ESP32 + #define FEATURE_I2C 1 +#else + // TODO TD-er: Add check for plugins requiring I2C + // Checks: + // FEATURE_I2CMULTIPLEXER + // FEATURE_PLUGIN_PRIORITY + // FEATURE_I2C_MULTIPLE + // FEATURE_CLEAR_I2C_STUCK + // FEATURE_I2C_GET_ADDRESS + // FEATURE_EXT_RTC + // I2C Watchdog???? Settings.WDI2CAddress +#ifdef PLUGIN_BUILD_MINIMAL_OTA + #define FEATURE_I2C 0 +#else + #define FEATURE_I2C 1 +#endif +#endif +#endif + +#if !FEATURE_I2C +# ifdef WEBSERVER_I2C_SCANNER +# undef WEBSERVER_I2C_SCANNER +# endif +#endif + +#ifndef FEATURE_SPI + #if FEATURE_SD + #define FEATURE_SPI 1 + // TODO TD-er: Add check for plugins requiring SPI + #else + #if defined(USES_NW004) || defined(USES_P039) || defined(USES_P046) || defined(USES_P095) || defined(USES_P096) || defined(USES_P099) || defined(USES_P104) || defined(USES_P111) || defined(USES_P116) || defined(USES_P118) || defined(USES_P125) || defined(USES_P141) || defined(USES_P162) || defined(USES_P172) + #define FEATURE_SPI 1 + #else + #define FEATURE_SPI 0 + #endif +#endif +#endif + + +#if !FEATURE_SPI && !FEATURE_I2C +#ifdef WEBSERVER_INTERFACES +#undef WEBSERVER_INTERFACES +#endif +#endif + #endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H diff --git a/src/src/DataStructs/DeviceStruct.cpp b/src/src/DataStructs/DeviceStruct.cpp index 23d8a8ec1..43069f3e8 100644 --- a/src/src/DataStructs/DeviceStruct.cpp +++ b/src/src/DataStructs/DeviceStruct.cpp @@ -57,17 +57,23 @@ void DeviceStruct::clear() { bool DeviceStruct::connectedToGPIOpins() const { switch(Type) { case DEVICE_TYPE_SINGLE: // Single GPIO +#if FEATURE_SPI case DEVICE_TYPE_SPI: +#endif case DEVICE_TYPE_CUSTOM1: case DEVICE_TYPE_DUAL: // Dual GPIOs case DEVICE_TYPE_SERIAL: +#if FEATURE_SPI case DEVICE_TYPE_SPI2: +#endif case DEVICE_TYPE_CUSTOM2: case DEVICE_TYPE_TRIPLE: // Triple GPIOs case DEVICE_TYPE_SERIAL_PLUS1: +#if FEATURE_SPI case DEVICE_TYPE_SPI3: +#endif case DEVICE_TYPE_CUSTOM3: return true; default: @@ -81,12 +87,16 @@ bool DeviceStruct::usesTaskDevicePin(int pin) const { if (pin == 2) return connectedToGPIOpins() && !(Type == DEVICE_TYPE_SINGLE || +#if FEATURE_SPI Type == DEVICE_TYPE_SPI || +#endif Type == DEVICE_TYPE_CUSTOM1); if (pin == 3) return Type == DEVICE_TYPE_TRIPLE || Type == DEVICE_TYPE_SERIAL_PLUS1 || +#if FEATURE_SPI Type == DEVICE_TYPE_SPI3 || +#endif Type == DEVICE_TYPE_CUSTOM3; return false; } @@ -95,12 +105,13 @@ bool DeviceStruct::isSerial() const { return (Type == DEVICE_TYPE_SERIAL) || (Type == DEVICE_TYPE_SERIAL_PLUS1); } - +#if FEATURE_SPI bool DeviceStruct::isSPI() const { return (Type == DEVICE_TYPE_SPI) || (Type == DEVICE_TYPE_SPI2) || (Type == DEVICE_TYPE_SPI3); } +#endif bool DeviceStruct::isCustom() const { return (Type == DEVICE_TYPE_CUSTOM0) || diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index b1582ef0c..7433096f5 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -17,12 +17,16 @@ #define DEVICE_TYPE_DUAL 2 // connected through 2 datapins #define DEVICE_TYPE_TRIPLE 3 // connected through 3 datapins #define DEVICE_TYPE_ANALOG 10 // AIN/tout pin +#if FEATURE_I2C #define DEVICE_TYPE_I2C 20 // connected through I2C +#endif #define DEVICE_TYPE_SERIAL 21 // connected through UART/Serial #define DEVICE_TYPE_SERIAL_PLUS1 22 // connected through UART/Serial + 1 extra signal pin +#if FEATURE_SPI #define DEVICE_TYPE_SPI 23 // connected through SPI #define DEVICE_TYPE_SPI2 24 // connected through SPI, 2 GPIOs #define DEVICE_TYPE_SPI3 25 // connected through SPI, 3 GPIOs +#endif #define DEVICE_TYPE_CUSTOM0 30 // Custom labels, Not using TaskDevicePin1 ... TaskDevicePin3 #define DEVICE_TYPE_CUSTOM1 31 // Custom labels, 1 GPIO #define DEVICE_TYPE_CUSTOM2 32 // Custom labels, 2 GPIOs @@ -73,8 +77,9 @@ struct DeviceStruct } bool isSerial() const; - +#if FEATURE_SPI bool isSPI() const; +#endif bool isCustom() const; diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index b9354d171..84623b2d2 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -366,6 +366,7 @@ public: PinBootState getPinBootState(int8_t gpio_pin) const; void setPinBootState(int8_t gpio_pin, PinBootState state); +#if FEATURE_SPI bool getSPI_pins(int8_t spi_gpios[3], uint8_t spi_bus = 0, bool noCheck = false) const; @@ -383,9 +384,6 @@ public: // Return true when SPI enabled and opt. user defined pins valid. bool isSPI_valid(uint8_t spi_bus) const; - // Return true when pin is one of the configured I2C pins. - bool isI2C_pin(int8_t pin) const; - uint8_t getSPIBusForTask(taskIndex_t TaskIndex) const; void setSPIBusForTask(taskIndex_t TaskIndex, uint8_t spi_bus); @@ -393,6 +391,11 @@ public: uint8_t getSPIBusForSDCard() const; void setSPIBusForSDCard(uint8_t spi_bus); #endif // if FEATURE_SD +#endif + +#if FEATURE_I2C + // Return true when pin is one of the configured I2C pins. + bool isI2C_pin(int8_t pin) const; // Return true if I2C settings are correct bool isI2CEnabled(uint8_t i2cBus) const; @@ -417,13 +420,14 @@ public: int8_t getI2CMultiplexerAddr(uint8_t i2cBus) const; int8_t getI2CMultiplexerResetPin(uint8_t i2cBus) const; #endif // if FEATURE_I2CMULTIPLEXER - +#endif +#if FEATURE_ETHERNET // Return true when pin is one of the fixed Ethernet pins and Ethernet is enabled bool isEthernetPin(int8_t pin) const; // Return true when pin is one of the optional Ethernet pins and Ethernet is enabled bool isEthernetPinOptional(int8_t pin) const; - +#endif // Access to TaskDevicePin1 ... TaskDevicePin3 // @param pinnr 1 = TaskDevicePin1, ..., 3 = TaskDevicePin3 int8_t getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const; diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index da14795eb..860b884ba 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -986,7 +986,7 @@ void SettingsStruct_tmpl::setPinBootState(int8_t gpio_pin, PinBootState } # endif // ifdef ESP32 } - +#if FEATURE_SPI template bool SettingsStruct_tmpl::isSPI_enabled(uint8_t spi_bus) const { const SPI_Options_e SPI_selection = static_cast(0 == spi_bus ? InitSPI : InitSPI1); @@ -1190,7 +1190,8 @@ bool SettingsStruct_tmpl::isSPI_valid(uint8_t spi_bus) const { #endif // ifdef ESP32 return result; } - +#endif +#if FEATURE_I2C template bool SettingsStruct_tmpl::isI2C_pin(int8_t pin) const { if (pin < 0) { return false; } @@ -1232,7 +1233,8 @@ uint8_t SettingsStruct_tmpl::getNrConfiguredI2C_buses() const return isI2CEnabled(0) ? 1 : 0; #endif } - +#endif +#if FEATURE_SPI // stored in I2C_SPI_bus_Flags per Task template uint8_t SettingsStruct_tmpl::getSPIBusForTask(taskIndex_t TaskIndex) const { @@ -1256,7 +1258,9 @@ void SettingsStruct_tmpl::setSPIBusForSDCard(uint8_t spi_bus) { set2BitToUL(I2C_SPI_bus_Flags[0], SPI_FLAGS_SDCARD_BUS_NUMBER, spi_bus); } #endif // if FEATURE_SD +#endif +#if FEATURE_I2C template uint8_t SettingsStruct_tmpl::getI2CInterface(taskIndex_t TaskIndex) const { return get3BitFromUL(I2C_SPI_bus_Flags[TaskIndex], I2C_FLAGS_BUS_NUMBER); @@ -1400,7 +1404,8 @@ int8_t SettingsStruct_tmpl::getI2CMultiplexerResetPin(uint8_t i2cBus) c return -1; } #endif // if FEATURE_I2CMULTIPLEXER - +#endif +#if FEATURE_ETHERNET template bool SettingsStruct_tmpl::isEthernetPin(int8_t pin) const { #if FEATURE_ETHERNET @@ -1455,6 +1460,7 @@ bool SettingsStruct_tmpl::isEthernetPinOptional(int8_t pin) const { #endif // if FEATURE_ETHERNET return false; } +#endif template int8_t SettingsStruct_tmpl::getTaskDevicePin(taskIndex_t taskIndex, uint8_t pinnr) const { diff --git a/src/src/ESPEasyCore/ESPEasy_loop.cpp b/src/src/ESPEasyCore/ESPEasy_loop.cpp index 81a88fad9..857ea87b1 100644 --- a/src/src/ESPEasyCore/ESPEasy_loop.cpp +++ b/src/src/ESPEasyCore/ESPEasy_loop.cpp @@ -108,6 +108,7 @@ void ESPEasy_loop() setWebserverRunning(ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN)); // ESPEasy::net::processNetworkEvents(); +#if FEATURE_I2C #if FEATURE_CLEAR_I2C_STUCK if (Settings.EnableClearHangingI2Cbus()) { @@ -143,6 +144,7 @@ void ESPEasy_loop() } } #endif +#endif // Work around for nodes that do not have WiFi connection for a long time and may reboot after N unsuccessful connect attempts diff --git a/src/src/Globals/I2Cdev.cpp b/src/src/Globals/I2Cdev.cpp index f4062267a..58ee92526 100644 --- a/src/src/Globals/I2Cdev.cpp +++ b/src/src/Globals/I2Cdev.cpp @@ -1,4 +1,6 @@ #include "../Globals/I2Cdev.h" +#if FEATURE_I2C I2Cdev i2cdev; +#endif \ No newline at end of file diff --git a/src/src/Globals/I2Cdev.h b/src/src/Globals/I2Cdev.h index 3b5121ecc..8266a2641 100644 --- a/src/src/Globals/I2Cdev.h +++ b/src/src/Globals/I2Cdev.h @@ -1,11 +1,14 @@ #ifndef GLOBALS_I2CDEV_H #define GLOBALS_I2CDEV_H +#include "../../ESPEasy_common.h" +#if FEATURE_I2C #include extern I2Cdev i2cdev; +#endif #endif // ifndef GLOBALS_I2CDEV_H diff --git a/src/src/Globals/Plugins.cpp b/src/src/Globals/Plugins.cpp index 309f4b620..dc6e7e542 100644 --- a/src/src/Globals/Plugins.cpp +++ b/src/src/Globals/Plugins.cpp @@ -205,7 +205,7 @@ uint8_t getTaskI2CAddress(taskIndex_t taskIndex) { // Functions to assist changing I2C multiplexer port or clock speed // when addressing a task // ******************************************************************************** - +#if FEATURE_I2C bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { if (!validTaskIndex(taskIndex) || !validDeviceIndex(DeviceIndex)) { return false; @@ -264,6 +264,7 @@ void post_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { I2CSelectHighClockSpeed(i2cBus); // Reset, stay on current bus } +#endif // Add an event to the event queue. // event value 1 = taskIndex (first task = 1) @@ -362,10 +363,11 @@ bool PluginCallForTask(taskIndex_t taskIndex, uint8_t Function, EventStruct *Tem if (event != nullptr) { TempEvent->OriginTaskIndex = event->TaskIndex; } - +#if FEATURE_I2C if (!prepare_I2C_by_taskIndex(taskIndex, DeviceIndex)) { return false; } +#endif #ifndef BUILD_NO_RAM_TRACKER if (Settings.EnableRAMTracking()) { @@ -456,8 +458,9 @@ bool PluginCallForTask(taskIndex_t taskIndex, uint8_t Function, EventStruct *Tem #if FEATURE_I2C_DEVICE_CHECK } #endif // if FEATURE_I2C_DEVICE_CHECK - +#if FEATURE_I2C post_I2C_by_taskIndex(taskIndex, DeviceIndex); +#endif delay(0); // SMY: call delay(0) unconditionally } else { #if FEATURE_PLUGIN_STATS @@ -833,10 +836,11 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) #ifndef BUILD_NO_RAM_TRACKER checkRAM_PluginCall_task(event->TaskIndex, Function); #endif // ifndef BUILD_NO_RAM_TRACKER - +#if FEATURE_I2C if (!prepare_I2C_by_taskIndex(event->TaskIndex, DeviceIndex)) { return false; } +#endif bool retval = false; const bool performPluginCall = (Function != PLUGIN_READ && Function != PLUGIN_INIT) || @@ -976,7 +980,9 @@ bool PluginCall(uint8_t Function, struct EventStruct *event, String& str) #if FEATURE_I2C_DEVICE_CHECK } #endif // if FEATURE_I2C_DEVICE_CHECK +#if FEATURE_I2C post_I2C_by_taskIndex(event->TaskIndex, DeviceIndex); +#endif delay(0); // SMY: call delay(0) unconditionally return retval; diff --git a/src/src/Globals/Plugins.h b/src/src/Globals/Plugins.h index 89015f245..89ad28334 100644 --- a/src/src/Globals/Plugins.h +++ b/src/src/Globals/Plugins.h @@ -97,12 +97,12 @@ uint8_t getTaskI2CAddress(taskIndex_t taskIndex); String getPluginNameFromPluginID(pluginID_t pluginID); - +#if FEATURE_I2C // Prepare I2C bus for next call to task // Return false if task is I2C, but I2C bus is not ready bool prepare_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); void post_I2C_by_taskIndex(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); - +#endif void loadDefaultTaskValueNames_ifEmpty(taskIndex_t TaskIndex); /*********************************************************************************************\ diff --git a/src/src/Helpers/Hardware.cpp b/src/src/Helpers/Hardware.cpp index a828b1963..cf409cbf6 100644 --- a/src/src/Helpers/Hardware.cpp +++ b/src/src/Helpers/Hardware.cpp @@ -159,15 +159,19 @@ void hardwareInit() } // Initialize I2C buses +#if FEATURE_I2C initI2C(); +#endif #if FEATURE_PLUGIN_PRIORITY String dummy; PluginCall(PLUGIN_PRIORITY_INIT_ALL, nullptr, dummy); #endif // if FEATURE_PLUGIN_PRIORITY +#if FEATURE_SPI // Initialize SPI buses, also initializes SD-card when available in build and configured initializeSPIBuses(); +#endif } void checkResetFactoryPin() { diff --git a/src/src/Helpers/Hardware_I2C.cpp b/src/src/Helpers/Hardware_I2C.cpp index ece26d623..6743da518 100644 --- a/src/src/Helpers/Hardware_I2C.cpp +++ b/src/src/Helpers/Hardware_I2C.cpp @@ -7,6 +7,7 @@ #include "../Helpers/I2C_access.h" #include "../Helpers/StringConverter.h" +#if FEATURE_I2C #include @@ -319,4 +320,5 @@ bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex) { || (bitRead(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_MUX_MULTICHANNEL) && Settings.I2C_Multiplexer_Channel[taskIndex] != 0); } -#endif // if FEATURE_I2CMULTIPLEXER +#endif +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware_I2C.h b/src/src/Helpers/Hardware_I2C.h index 22b3d3947..fc546350a 100644 --- a/src/src/Helpers/Hardware_I2C.h +++ b/src/src/Helpers/Hardware_I2C.h @@ -3,6 +3,8 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C + #include "../DataTypes/TaskIndex.h" void initI2C(); @@ -37,5 +39,5 @@ void I2CMultiplexerReset(uint8_t i2cBus); bool I2CMultiplexerPortSelectedForTask(taskIndex_t taskIndex); #endif // if FEATURE_I2CMULTIPLEXER - +#endif #endif // ifndef HELPERS_HARDWARE_I2C_H diff --git a/src/src/Helpers/Hardware_SPI.cpp b/src/src/Helpers/Hardware_SPI.cpp index 58c69753c..2ac275b2c 100644 --- a/src/src/Helpers/Hardware_SPI.cpp +++ b/src/src/Helpers/Hardware_SPI.cpp @@ -3,6 +3,7 @@ #include "../Helpers/Hardware_SPI.h" #include "../Globals/Settings.h" +#if FEATURE_SPI #if FEATURE_SD # include #endif // if FEATURE_SD @@ -138,3 +139,4 @@ bool initSDcard() { } #endif // if FEATURE_SD +#endif \ No newline at end of file diff --git a/src/src/Helpers/Hardware_SPI.h b/src/src/Helpers/Hardware_SPI.h index c104c9f0d..c9c7ee0df 100644 --- a/src/src/Helpers/Hardware_SPI.h +++ b/src/src/Helpers/Hardware_SPI.h @@ -2,9 +2,10 @@ #include "../../ESPEasy_common.h" - +#if FEATURE_SPI void initializeSPIBuses(); #if FEATURE_SD bool initSDcard(); #endif // if FEATURE_SD +#endif \ No newline at end of file diff --git a/src/src/Helpers/I2C_Plugin_Helper.cpp b/src/src/Helpers/I2C_Plugin_Helper.cpp index 2f92f3b57..fc976f108 100644 --- a/src/src/Helpers/I2C_Plugin_Helper.cpp +++ b/src/src/Helpers/I2C_Plugin_Helper.cpp @@ -7,6 +7,7 @@ * outputToHtml = false: no html output is generated * Outputs an error message and returns false if not correct **********************************************************************/ +#if FEATURE_I2C bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, bool outputToHtml) { #if FEATURE_I2C_MULTIPLE @@ -28,3 +29,4 @@ bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, #endif // if FEATURE_I2CMULTIPLEXER return true; } +#endif \ No newline at end of file diff --git a/src/src/Helpers/I2C_Plugin_Helper.h b/src/src/Helpers/I2C_Plugin_Helper.h index 1f734dd52..ba7d7337c 100644 --- a/src/src/Helpers/I2C_Plugin_Helper.h +++ b/src/src/Helpers/I2C_Plugin_Helper.h @@ -4,6 +4,8 @@ #include "../../ESPEasy_common.h" +#if FEATURE_I2C + #include "../Globals/Plugins.h" #include "../Globals/Settings.h" #include "../Helpers/Hardware_I2C.h" @@ -13,4 +15,5 @@ bool checkI2CConfigValid_toHtml(taskIndex_t taskIndex, bool outputToHtml = true); +#endif #endif // ifndef _SRC_HELPERS_I2C_PLUGIN_HELPER_H diff --git a/src/src/Helpers/I2C_access.cpp b/src/src/Helpers/I2C_access.cpp index be3948efe..bd12c53b5 100644 --- a/src/src/Helpers/I2C_access.cpp +++ b/src/src/Helpers/I2C_access.cpp @@ -1,5 +1,7 @@ #include "../Helpers/I2C_access.h" +#if FEATURE_I2C + #include "../DataStructs/TimingStats.h" #include "../Globals/I2Cdev.h" #include "../Globals/Settings.h" @@ -557,3 +559,4 @@ void I2CInterfaceSelector(String label, } #endif // if FEATURE_I2C_MULTIPLE +#endif \ No newline at end of file diff --git a/src/src/Helpers/I2C_access.h b/src/src/Helpers/I2C_access.h index 2041445b7..3d05fc6e5 100644 --- a/src/src/Helpers/I2C_access.h +++ b/src/src/Helpers/I2C_access.h @@ -1,6 +1,8 @@ #ifndef HELPERS_I2C_ACCESS_H #define HELPERS_I2C_ACCESS_H +#include "../../ESPEasy_common.h" +#if FEATURE_I2C #include "../DataStructs/I2CTypes.h" #include "../DataTypes/TaskIndex.h" @@ -201,4 +203,5 @@ void I2CInterfaceSelector(String label, bool reloadWhenNeeded); #endif // if FEATURE_I2C_MULTIPLE +#endif #endif // HELPERS_I2C_ACCESS_H diff --git a/src/src/Helpers/OLed_helper.cpp b/src/src/Helpers/OLed_helper.cpp index b7ffe4dc3..4835ad366 100644 --- a/src/src/Helpers/OLed_helper.cpp +++ b/src/src/Helpers/OLed_helper.cpp @@ -1,5 +1,7 @@ #include "../Helpers/OLed_helper.h" +#if defined(USES_P023) || defined(USES_P036) || defined(USES_P109) + /************************************************************************** * Select controller type, SSD1306 or SH1106 *************************************************************************/ @@ -126,3 +128,4 @@ bool OLedI2CAddressCheck(uint8_t function, } return success; } +#endif \ No newline at end of file diff --git a/src/src/Helpers/OLed_helper.h b/src/src/Helpers/OLed_helper.h index 94cb2f15f..90ffb5280 100644 --- a/src/src/Helpers/OLed_helper.h +++ b/src/src/Helpers/OLed_helper.h @@ -1,8 +1,6 @@ #ifndef HELPERS_OLED_HELPER_H #define HELPERS_OLED_HELPER_H #include "../../_Plugin_Helper.h" -#include "SSD1306.h" -#include "SH1106Wire.h" /** * Helper with OLed related functions @@ -14,6 +12,10 @@ * and generic function OLedSetContrast */ +#if defined(USES_P023) || defined(USES_P036) || defined(USES_P109) +#include "SSD1306.h" +#include "SH1106Wire.h" + #define OLED_CONTRAST_OFF 0x01 #define OLED_CONTRAST_LOW 0x40 #define OLED_CONTRAST_MED 0xCF @@ -37,4 +39,5 @@ bool OLedI2CAddressCheck(uint8_t function, const __FlashStringHelper *id, int8_t deviceAddress); +#endif #endif // ifndef HELPERS_OLED_HELPER_H diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index d7dead409..bfdf6b64d 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -212,6 +212,7 @@ void runOncePerSecond() #endif // I2C Watchdog feed +#if FEATURE_I2C if (Settings.WDI2CAddress != 0) { #if FEATURE_I2C_MULTIPLE @@ -219,6 +220,7 @@ void runOncePerSecond() #endif // if FEATURE_I2C_MULTIPLE I2C_write8(Settings.WDI2CAddress, 0xA5); } +#endif #if FEATURE_MDNS #ifdef ESP8266 diff --git a/src/src/Helpers/SPI_Helper.cpp b/src/src/Helpers/SPI_Helper.cpp index ce1c291bd..f850358ca 100644 --- a/src/src/Helpers/SPI_Helper.cpp +++ b/src/src/Helpers/SPI_Helper.cpp @@ -1,5 +1,6 @@ #include "../Helpers/SPI_Helper.h" +#if FEATURE_SPI void SPIInterfaceSelector(String label, String id, uint8_t choice, @@ -39,3 +40,4 @@ void SPIInterfaceSelector(String label, selector.addFormSelector(label, id, choice); } } +#endif \ No newline at end of file diff --git a/src/src/Helpers/SPI_Helper.h b/src/src/Helpers/SPI_Helper.h index c0568e61b..430a29ad9 100644 --- a/src/src/Helpers/SPI_Helper.h +++ b/src/src/Helpers/SPI_Helper.h @@ -8,7 +8,9 @@ #include "../WebServer/Markup_Forms.h" +#if FEATURE_SPI void SPIInterfaceSelector(String label, String id, uint8_t choice, bool disabled = false); +#endif \ No newline at end of file diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 31425a522..1ff0b59e3 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -232,11 +232,14 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose static_assert(false, "Implement processor architecture"); #endif // ifdef ESP32S2 - +#if FEATURE_I2C bool includeI2C = true; +#endif +#if FEATURE_SPI bool includeSPI = true; #if FEATURE_SD bool includeSDCard = true; +#endif #endif bool includeSerial = Settings.UseSerial; // Only need to check if Serial Port Console is enabled bool includeStatusLed = true; @@ -244,6 +247,7 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose switch (purpose) { +#if FEATURE_I2C case PinSelectPurpose::I2C: #if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -253,10 +257,13 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose #endif // if FEATURE_I2C_MULTIPLE includeI2C = false; break; +#endif +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: includeSPI = false; break; +#endif case PinSelectPurpose::Serial_input: case PinSelectPurpose::Serial_output: includeSerial = false; @@ -282,7 +289,7 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose includeResetPin = false; break; } - +#if FEATURE_I2C if (includeI2C && Settings.isI2C_pin(gpio)) { for (uint8_t i2cBus = 0; i2cBus < getI2CBusCount(); ++i2cBus) { @@ -319,7 +326,9 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose } } } + #endif + #if FEATURE_SPI #ifdef ESP32 if (includeSPI) { if (Settings.isSPI_pin(gpio, 0u)) { @@ -331,10 +340,11 @@ const __FlashStringHelper* getConflictingUse_flashstr(int gpio, PinSelectPurpose } #endif // ifdef ESP32 #ifdef ESP8266 - if (includeSPI && Settings.isSPI_pin(gpio)) { + if (includeSPI && Settings.isSPI_pin(gpio)) { return F("SPI"); } - #endif // ifdef ESP8266 + #endif + #endif if (includeStatusLed && (Settings.Pin_status_led == gpio) && (-1 != gpio)) { return F("Wifi Status LED"); diff --git a/src/src/Helpers/StringGenerator_GPIO.h b/src/src/Helpers/StringGenerator_GPIO.h index e28515a8a..52597820a 100644 --- a/src/src/Helpers/StringGenerator_GPIO.h +++ b/src/src/Helpers/StringGenerator_GPIO.h @@ -22,6 +22,7 @@ enum class PinSelectPurpose : uint8_t { Generic_input, Generic_output, Generic_bidir, +#if FEATURE_I2C I2C, #if FEATURE_I2C_MULTIPLE I2C_2, @@ -29,8 +30,11 @@ enum class PinSelectPurpose : uint8_t { I2C_3, #endif #endif +#endif +#if FEATURE_SPI SPI, SPI_MISO, +#endif #if FEATURE_ETHERNET Ethernet, #endif diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 16ea450b8..2c441b4e3 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -156,6 +156,7 @@ void handle_devices() { { // change of device: cleanup old device and reset default settings setTaskDevice_to_TaskIndex(taskdevicenumber, taskIndex); +#if FEATURE_I2C const deviceIndex_t DeviceIndex = getDeviceIndex(taskdevicenumber); if (validDeviceIndex(DeviceIndex)) { @@ -165,6 +166,7 @@ void handle_devices() { bitWrite(Settings.I2C_SPI_bus_Flags[taskIndex], I2C_FLAGS_SLOW_SPEED, 1); // Then: Enable Force Slow I2C speed checkbox by default } } +#endif } else if (taskdevicenumber != INVALID_PLUGIN_ID) // save settings { @@ -283,11 +285,13 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task Settings.TaskDeviceNumber[taskIndex] = taskdevicenumber.value; #ifdef ESP32 + #if FEATURE_SPI if (device.isSPI()) { Settings.setSPIBusForTask(taskIndex, getFormItemInt(F("pspibus"), 0)); } + #endif #endif // ifdef ESP32 - +#if FEATURE_I2C if (device.Type == DEVICE_TYPE_I2C) { uint8_t flags = 0; bitWrite(flags, I2C_FLAGS_SLOW_SPEED, isFormItemChecked(F("taskdeviceflags0"))); @@ -331,6 +335,7 @@ void handle_devices_CopySubmittedSettings(taskIndex_t taskIndex, pluginID_t task Settings.I2C_SPI_bus_Flags[taskIndex] = flags; } +#endif // Must load from file system to make sure all caches and checksums match. ExtraTaskSettings.clear(); @@ -624,7 +629,9 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) if (pluginID_set) { // LoadTaskSettings(x); +#if FEATURE_SPI int8_t spi_gpios[3] { -1, -1, -1 }; +#endif struct EventStruct TempEvent(x); addEnabled(Settings.TaskDeviceEnabled[x] && validDeviceIndex(DeviceIndex)); @@ -649,12 +656,17 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) addHtml(portDescr); } else { const DeviceStruct& device = Device[DeviceIndex]; - +#if FEATURE_I2C if (device.Type == DEVICE_TYPE_I2C) { format_I2C_port_description(x); - } else if (device.isSPI()) { + } else +#endif +#if FEATURE_SPI + if (device.isSPI()) { format_SPI_port_description(spi_gpios, Settings.getSPIBusForTask(x)); - } else if (device.isSerial()) { + } else +#endif + if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL addHtml(serialHelper_getSerialTypeLabel(&TempEvent)); # else // ifdef PLUGIN_USES_SERIAL @@ -723,12 +735,15 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) switch (device.Type) { +#if FEATURE_I2C case DEVICE_TYPE_I2C: { format_I2C_pin_description(x); html_BR(); break; } +#endif +#if FEATURE_SPI case DEVICE_TYPE_SPI3: showpin3 = !pluginHasGPIODescription; @@ -740,6 +755,7 @@ void handle_devicess_ShowAllTasksTable(uint8_t page) case DEVICE_TYPE_SPI: format_SPI_pin_description(spi_gpios, x, !pluginHasGPIODescription); break; +#endif case DEVICE_TYPE_ANALOG: { # ifdef ESP8266 @@ -978,6 +994,7 @@ void format_originating_node(uint8_t remoteUnit) { # endif // if FEATURE_ESPEASY_P2P +#if FEATURE_I2C void format_I2C_port_description(taskIndex_t x) { addHtml(F("I2C")); @@ -1024,7 +1041,9 @@ void format_I2C_port_description(taskIndex_t x) } # endif // if FEATURE_I2CMULTIPLEXER } +#endif +#if FEATURE_SPI void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus) { if (!Settings.getSPI_pins(spi_gpios, spi_bus)) { @@ -1038,7 +1057,9 @@ void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus) addHtml(F("SPI")); # endif // ifdef ESP8266 } +#endif +#if FEATURE_I2C void format_I2C_pin_description(taskIndex_t x) { # if FEATURE_I2C_MULTIPLE @@ -1053,7 +1074,9 @@ void format_I2C_pin_description(taskIndex_t x) Label_Gpio_toHtml(F("SCL"), formatGpioLabel(Settings.getI2CSclPin(i2cBus), false)); } } +#endif +#if FEATURE_SPI void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin) { const uint8_t spi_bus = Settings.getSPIBusForTask(x); @@ -1074,6 +1097,7 @@ void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSp } } } +#endif // ******************************************************************************** // Show the task settings page @@ -1203,7 +1227,11 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addPinConfig = true; } - if (addPinConfig || (device.Type == DEVICE_TYPE_I2C)) { + if (addPinConfig +#if FEATURE_I2C + || (device.Type == DEVICE_TYPE_I2C) +#endif + ) { if (device.isSerial()) { # ifdef PLUGIN_USES_SERIAL devicePage_show_serial_config(taskIndex); @@ -1215,15 +1243,18 @@ void handle_devices_TaskSettingsPage(taskIndex_t taskIndex, uint8_t page) addPinConfig = false; html_add_script(F("document.getElementById('serPort').onchange();"), false); - } else if (device.Type == DEVICE_TYPE_I2C) { + } else +#if FEATURE_I2C + if (device.Type == DEVICE_TYPE_I2C) { devicePage_show_pin_config(taskIndex, DeviceIndex); addPinConfig = false; - +#if FEATURE_I2C if (Settings.TaskDeviceDataFeed[taskIndex] == 0) { devicePage_show_I2C_config(taskIndex, DeviceIndex); } +#endif } - +#endif if (addPinConfig) { devicePage_show_pin_config(taskIndex, DeviceIndex); } @@ -1329,9 +1360,11 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex addFormNote(F("Will go into effect on next input change.")); } +#if FEATURE_SPI if (device.isSPI()) { devicePage_show_SPI_config(taskIndex, DeviceIndex); } +#endif if (device.connectedToGPIOpins()) { // get descriptive GPIO-names from plugin @@ -1350,11 +1383,14 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex { // Pin1 = GPIO <--- TX purpose = PinSelectPurpose::Serial_input; - } else if (device.isSPI()) + } +#if FEATURE_SPI + else if (device.isSPI()) { // All selectable SPI pins are output only purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String1, F("taskdevicepin1"), Settings.TaskDevicePin1[taskIndex]); } @@ -1367,23 +1403,25 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex // Serial Pin2 = GPIO ---> RX purpose = PinSelectPurpose::Serial_output; } - +#if FEATURE_SPI if (device.isSPI()) { // SPI only needs output pins purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String2, F("taskdevicepin2"), Settings.TaskDevicePin2[taskIndex]); } if (device.usesTaskDevicePin(3)) { PinSelectPurpose purpose = device.getPinSelectPurpose(3); // PinSelectPurpose::Generic; - +#if FEATURE_SPI if (device.isSPI()) { // SPI only needs output pins purpose = PinSelectPurpose::Generic_output; } +#endif addFormPinSelect(purpose, TempEvent.String3, F("taskdevicepin3"), Settings.TaskDevicePin3[taskIndex]); } } @@ -1406,6 +1444,7 @@ void devicePage_show_serial_config(taskIndex_t taskIndex) # endif // ifdef PLUGIN_USES_SERIAL +#if FEATURE_SPI void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { if (Device[DeviceIndex].isSPI() @@ -1421,7 +1460,9 @@ void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } #endif // ifdef ESP32 } +#endif +#if FEATURE_I2C void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { struct EventStruct TempEvent(taskIndex); @@ -1525,6 +1566,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } # endif // if FEATURE_I2CMULTIPLEXER } +#endif void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex) { diff --git a/src/src/WebServer/DevicesPage.h b/src/src/WebServer/DevicesPage.h index 1b33d2ec8..17ec4f775 100644 --- a/src/src/WebServer/DevicesPage.h +++ b/src/src/WebServer/DevicesPage.h @@ -35,13 +35,20 @@ void handle_devicess_ShowAllTasksTable(uint8_t page); #if FEATURE_ESPEASY_P2P void format_originating_node(uint8_t remoteUnit); #endif +#if FEATURE_I2C void format_I2C_port_description(taskIndex_t x); +#endif +#if FEATURE_SPI void format_SPI_port_description(int8_t spi_gpios[3], uint8_t spi_bus); - +#endif +#if FEATURE_I2C void format_I2C_pin_description(taskIndex_t x); +#endif +#if FEATURE_SPI void format_SPI_pin_description(int8_t spi_gpios[3], taskIndex_t x, bool showCSpin = true); +#endif @@ -56,9 +63,13 @@ void devicePage_show_pin_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex void devicePage_show_serial_config(taskIndex_t taskIndex); #endif +#if FEATURE_SPI void devicePage_show_SPI_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); +#endif +#if FEATURE_I2C void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); +#endif void devicePage_show_output_data_type(taskIndex_t taskIndex, deviceIndex_t DeviceIndex); diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 6b73beab4..bbb8409b5 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -285,8 +285,10 @@ void WebServerInit() #ifdef WEBSERVER_CSVVAL web_server.on(F("/csv"), handle_csvval); #endif + #ifdef WEBSERVER_LOG web_server.on(F("/log"), handle_log); web_server.on(F("/logjson"), handle_log_JSON); // Also part of WEBSERVER_NEW_UI + #endif #if FEATURE_NOTIFIER web_server.on(F("/notifications"), handle_notifications); #endif // if FEATURE_NOTIFIER diff --git a/src/src/WebServer/HTML_wrappers.cpp b/src/src/WebServer/HTML_wrappers.cpp index 70525ed24..6db943674 100644 --- a/src/src/WebServer/HTML_wrappers.cpp +++ b/src/src/WebServer/HTML_wrappers.cpp @@ -598,10 +598,18 @@ void addGpioHtml(int8_t pin) { if (pin == -1) { return; } addHtml(formatGpioLabel(pin, false)); - if (Settings.isSPI_pin(pin) || + if ( +#if FEATURE_SPI + Settings.isSPI_pin(pin) || +#endif +#if FEATURE_I2C Settings.isI2C_pin(pin) || +#endif +#if FEATURE_ETHERNET Settings.isEthernetPin(pin) || - Settings.isEthernetPinOptional(pin)) { + Settings.isEthernetPinOptional(pin)|| +#endif + false) { addHtml(' '); addHtml(F(HTML_SYMBOL_WARNING)); } diff --git a/src/src/WebServer/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp index bda1cd45b..6b629e72d 100644 --- a/src/src/WebServer/InterfacesPage.cpp +++ b/src/src/WebServer/InterfacesPage.cpp @@ -27,12 +27,85 @@ # include "../Helpers/Hardware_device_info.h" # endif // if FEATURE_I2C_MULTIPLE +// ******************************************************************************** +// Web Interface hardware page +// ******************************************************************************** +void handle_interfaces() { +# ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("handle_interfaces")); +# endif + + if (!isLoggedIn()) { return; } + navMenuIndex = MENU_INDEX_INTERFACES; + TXBuffer.startStream(); + sendHeadandTail_stdtemplate(_HEAD); + + save_interfaces(); + + + addHtml(F("")); + html_table_class_normal(); + addFormHeader(F("Interfaces Settings"), F(""), F("Interfaces/Interfaces.html")); + + #if FEATURE_I2C + interfaces_show_I2C(); + #endif + #if FEATURE_SPI + interfaces_show_SPI(); + #endif + + + addFormSeparator(2); + + html_TR_TD(); + html_TD(); + addSubmitButton(); + html_TR_TD(); + html_end_table(); + html_end_form(); + + sendHeadandTail_stdtemplate(_TAIL); + TXBuffer.endStream(); +} void save_interfaces() { - + String error; bool updated{}; + #if FEATURE_I2C + + if (save_I2C(error)) { updated = true; } + #endif + + #if FEATURE_SPI + if (save_SPI(error)) { updated = true; } + #endif + + if (updated) { + error += SaveSettings(); + addHtmlError(error); + + if (error.isEmpty()) { +#if FEATURE_I2C + // Apply I2C settings. + initI2C(); +#endif + +#if FEATURE_SPI + // Apply SPI settings + initializeSPIBuses(); +#endif + } + } +} + +#if FEATURE_I2C + +bool save_I2C(String& error) { + bool updated{}; + + if (isFormItem(F("pi2csp0"))) { updated = true; # if FEATURE_PLUGIN_PRIORITY @@ -117,79 +190,54 @@ void save_interfaces() { } # endif // if FEATURE_I2C_MULTIPLE - { -# ifdef ESP32 - bool SPI_updated{}; - - if (update_whenset_FormItemInt(F("initspi0"), Settings.InitSPI)) { - SPI_updated = true; - - // User-defined SPI bus 0 GPIO pins - Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); - Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); - Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); - } - - if (update_whenset_FormItemInt(F("initspi1"), Settings.InitSPI1)) { - SPI_updated = true; - - // User-defined SPI bus 1 GPIO pins - Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); - Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); - Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); - } - - if (SPI_updated) { - updated = true; - - for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { - if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks - error += strformat(F("SPI bus %u pins not configured correctly!
      "), spi_bus); - } - } - } -# else // for ESP8266 we keep the old UI - Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init - updated = true; -# endif // ifdef ESP32 - } - - if (updated) { - error += SaveSettings(); - addHtmlError(error); - - if (error.isEmpty()) { - // Apply I2C settings. - initI2C(); - - // Apply SPI settings - initializeSPIBuses(); - } - } + return updated; } - -// ******************************************************************************** -// Web Interface hardware page -// ******************************************************************************** -void handle_interfaces() { -# ifndef BUILD_NO_RAM_TRACKER - checkRAM(F("handle_interfaces")); -# endif - - if (!isLoggedIn()) { return; } - navMenuIndex = MENU_INDEX_INTERFACES; - TXBuffer.startStream(); - sendHeadandTail_stdtemplate(_HEAD); - - save_interfaces(); +#endif - addHtml(F("")); - html_table_class_normal(); - addFormHeader(F("Interfaces Settings"), F(""), F("Interfaces/Interfaces.html")); +#if FEATURE_SPI +bool save_SPI(String& error) { +# ifdef ESP32 + bool SPI_updated{}; + if (update_whenset_FormItemInt(F("initspi0"), Settings.InitSPI)) { + SPI_updated = true; + // User-defined SPI bus 0 GPIO pins + Settings.SPI_SCLK_pin = getFormItemInt(F("spipinsclk0"), -1); + Settings.SPI_MISO_pin = getFormItemInt(F("spipinmiso0"), -1); + Settings.SPI_MOSI_pin = getFormItemInt(F("spipinmosi0"), -1); + } + + if (update_whenset_FormItemInt(F("initspi1"), Settings.InitSPI1)) { + SPI_updated = true; + + // User-defined SPI bus 1 GPIO pins + Settings.SPI1_SCLK_pin = getFormItemInt(F("spipinsclk1"), -1); + Settings.SPI1_MISO_pin = getFormItemInt(F("spipinmiso1"), -1); + Settings.SPI1_MOSI_pin = getFormItemInt(F("spipinmosi1"), -1); + } + + if (SPI_updated) { + for (uint8_t spi_bus = 0; spi_bus < getSPIBusCount(); ++spi_bus) { + if (Settings.isSPI_enabled(spi_bus) && !Settings.isSPI_valid(spi_bus)) { // Checks + error += strformat(F("SPI bus %u pins not configured correctly!
      "), spi_bus); + } + } + } + return SPI_updated; +# else // for ESP8266 we keep the old UI + const auto old_initSPI = Settings.InitSPI; + Settings.InitSPI = isFormItemChecked(F("initspi")); // SPI Init + return old_initSPI != Settings.InitSPI; +# endif // ifdef ESP32 +} + +#endif + +#if FEATURE_I2C +void interfaces_show_I2C() { # if FEATURE_I2CMULTIPLEXER const __FlashStringHelper *i2c_muxtype_options[] = { F("- None -"), @@ -306,6 +354,11 @@ void handle_interfaces() { } # endif // if FEATURE_I2C_MULTIPLE +} +#endif + +#if FEATURE_SPI +void interfaces_show_SPI() { # ifdef ESP32 @@ -369,20 +422,10 @@ void handle_interfaces() { addFormNote(F("CLK=GPIO-14 (D5), MISO=GPIO-12 (D6), MOSI=GPIO-13 (D7)")); addFormNote(F("Chip Select (CS) config must be done in the plugin")); # endif // ifdef ESP32 - - addFormSeparator(2); - - html_TR_TD(); - html_TD(); - addSubmitButton(); - html_TR_TD(); - html_end_table(); - html_end_form(); - - sendHeadandTail_stdtemplate(_TAIL); - TXBuffer.endStream(); } +#endif +#if FEATURE_I2C # if FEATURE_PLUGIN_PRIORITY bool isI2CPriorityTaskActive(uint8_t i2cBus) { @@ -408,5 +451,6 @@ void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus) { } # endif // if FEATURE_PLUGIN_PRIORITY +#endif #endif // ifdef WEBSERVER_INTERFACES diff --git a/src/src/WebServer/InterfacesPage.h b/src/src/WebServer/InterfacesPage.h index 21612f7ce..d62c9aea1 100644 --- a/src/src/WebServer/InterfacesPage.h +++ b/src/src/WebServer/InterfacesPage.h @@ -9,10 +9,22 @@ // ******************************************************************************** void handle_interfaces(); +void save_interfaces(); +#if FEATURE_I2C +bool save_I2C(String& error); +void interfaces_show_I2C(); +#endif +#if FEATURE_SPI +bool save_SPI(String& error); +void interfaces_show_SPI(); +#endif + +#if FEATURE_I2C # if FEATURE_PLUGIN_PRIORITY bool isI2CPriorityTaskActive(uint8_t i2cBus); void I2CShowSdaSclReadonly(int8_t i2c_sda, int8_t i2c_scl, uint8_t i2cBus); # endif +#endif #endif diff --git a/src/src/WebServer/Log.cpp b/src/src/WebServer/Log.cpp index 75d7fe265..6e6ead9b3 100644 --- a/src/src/WebServer/Log.cpp +++ b/src/src/WebServer/Log.cpp @@ -1,5 +1,7 @@ #include "../WebServer/Log.h" +#ifdef WEBSERVER_LOG + #include "../WebServer/ESPEasy_WebServer.h" #include "../WebServer/404.h" #include "../WebServer/HTML_wrappers.h" @@ -148,3 +150,4 @@ void handle_log_JSON() { handleNotFound(); #endif // ifdef WEBSERVER_LOG } +#endif \ No newline at end of file diff --git a/src/src/WebServer/Log.h b/src/src/WebServer/Log.h index 2c3198789..74cad153e 100644 --- a/src/src/WebServer/Log.h +++ b/src/src/WebServer/Log.h @@ -3,7 +3,7 @@ #include "../WebServer/common.h" - +#ifdef WEBSERVER_LOG // ******************************************************************************** // Web Interface log page // ******************************************************************************** @@ -13,7 +13,7 @@ void handle_log(); // Web Interface JSON log page // ******************************************************************************** void handle_log_JSON(); - +#endif #endif \ No newline at end of file diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index bf285c204..c4952aad7 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -115,8 +115,12 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int bool input, output, warning; if (getGpioInfo(gpio, pinnr, input, output, warning)) { +#if FEATURE_I2C bool includeI2C = true; +#endif +#if FEATURE_SPI bool includeSPI = true; +#endif bool includeSerial = true; #if FEATURE_ETHERNET bool includeEthernet = true; @@ -128,6 +132,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int // bool includeResetPin = true; switch (purpose) { +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: includeSPI = false; @@ -135,6 +140,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int return; } break; +#endif #if FEATURE_ETHERNET case PinSelectPurpose::Ethernet: includeEthernet = false; @@ -161,7 +167,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int return; } break; - +#if FEATURE_I2C case PinSelectPurpose::I2C: #if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -170,6 +176,7 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int #endif #endif includeI2C = false; +#endif // fallthrough case PinSelectPurpose::Generic_bidir: @@ -218,19 +225,20 @@ void addPinSelector_Item(PinSelectPurpose purpose, const String& gpio_label, int break; } - +#if FEATURE_I2C if (includeI2C && Settings.isI2C_pin(gpio)) { disabled = true; } +#endif if (includeSerial && isSerialConsolePin(gpio)) { disabled = true; } - +#if FEATURE_SPI if (includeSPI && Settings.isSPI_pin(gpio)) { disabled = true; } - +#endif // Not blocking these GPIO pins, as they may already be in dual-purpose use, just a place-holder // if (includeStatusLed && (Settings.Pin_status_led == gpio)) { // disabled = true; diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 0fcc774b3..524085b3d 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -497,7 +497,7 @@ void addFormPinSelect(const String& label, const String & id, int choice) addPinSelect(PinSelectPurpose::Generic, id, choice); } */ - +#if FEATURE_I2C void addFormPinSelectI2C(const String& label, const String& id, uint8_t i2cBus, int choice) { addRowLabel_tr_id(label, id); @@ -535,7 +535,7 @@ void addFormSelectorI2C(const String& id, } addSelector_Foot(); } - +#endif void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, int selectedIndex, @@ -569,10 +569,12 @@ void addFormPinStateSelect(int gpio, int choice) // do not add the pin state select for these pins. enabled = false; } +#if FEATURE_ETHERNET if (Settings.isEthernetPin(gpio)) { // do not add the pin state select for non-optional Ethernet pins enabled = false; } +#endif int pinnr = -1; bool input, output, warning; diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index cdfcd9b3f..29d6e2021 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -274,7 +274,7 @@ void addFormPinSelect(PinSelectPurpose purpose, const String& label, const __Fla void addFormPinSelect(PinSelectPurpose purpose, const String& label, const String& id, int choice); void addFormPinSelect(PinSelectPurpose purpose, const __FlashStringHelper * label, const __FlashStringHelper * id, int choice); - +#if FEATURE_I2C void addFormPinSelectI2C(const String& label, const String& id, uint8_t i2cBus, @@ -290,6 +290,7 @@ void addFormSelectorI2C(const String& id, const String& tooltip = EMPTY_STRING #endif ); +#endif void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, From 5da63d74b1fc4af9b981d6a2bb25822736c66fa4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 12 Feb 2026 13:23:12 +0100 Subject: [PATCH 089/161] [Build] Fix failing neopixel_ESP8266_1M_Domoticz build --- src/src/CustomBuild/define_plugin_sets.h | 40 +++++++++++++----------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index ba273ea52..73f410343 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -4474,24 +4474,28 @@ To create/register a plugin, you have to : #endif #ifndef FEATURE_I2C -#ifdef ESP32 - #define FEATURE_I2C 1 -#else - // TODO TD-er: Add check for plugins requiring I2C - // Checks: - // FEATURE_I2CMULTIPLEXER - // FEATURE_PLUGIN_PRIORITY - // FEATURE_I2C_MULTIPLE - // FEATURE_CLEAR_I2C_STUCK - // FEATURE_I2C_GET_ADDRESS - // FEATURE_EXT_RTC - // I2C Watchdog???? Settings.WDI2CAddress -#ifdef PLUGIN_BUILD_MINIMAL_OTA - #define FEATURE_I2C 0 -#else - #define FEATURE_I2C 1 -#endif -#endif + #ifdef ESP32 + #define FEATURE_I2C 1 + #else + // TODO TD-er: Add check for plugins requiring I2C + // Checks: + // FEATURE_I2CMULTIPLEXER + // FEATURE_PLUGIN_PRIORITY + // FEATURE_I2C_MULTIPLE + // FEATURE_CLEAR_I2C_STUCK + // FEATURE_I2C_GET_ADDRESS + // FEATURE_EXT_RTC + // I2C Watchdog???? Settings.WDI2CAddress + #ifdef PLUGIN_BUILD_MINIMAL_OTA + #ifdef USES_P180 + #define FEATURE_I2C 1 + #else + #define FEATURE_I2C 0 + #endif + #else + #define FEATURE_I2C 1 + #endif + #endif #endif #if !FEATURE_I2C From 8d3840fc1f15424354d0058009e0af47fcc0e7b8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 12 Feb 2026 13:38:53 +0100 Subject: [PATCH 090/161] [Build] Fix failing minimal_IRext_ESP8266_4M1M --- platformio_esp82xx_base.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index d8584466f..67f4416ea 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -246,11 +246,13 @@ lib_ignore = ESP32_ping extends = ir build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IR + -DFEATURE_I2C=1 [minimal_ir_extended] extends = ir build_flags = ${ir.build_flags} -DPLUGIN_BUILD_MINIMAL_IRext + -DFEATURE_I2C=1 [normal_ir] extends = ir From f034922af4a6dde2b9ab0c16771c4afd3940c1ca Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 12 Feb 2026 14:54:33 +0100 Subject: [PATCH 091/161] [Build] Fix failing build safeboot_ESP32c2_4M_LittleFS --- src/src/Helpers/StringGenerator_GPIO.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/src/Helpers/StringGenerator_GPIO.cpp b/src/src/Helpers/StringGenerator_GPIO.cpp index 1ff0b59e3..dd98d12e7 100644 --- a/src/src/Helpers/StringGenerator_GPIO.cpp +++ b/src/src/Helpers/StringGenerator_GPIO.cpp @@ -424,6 +424,7 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig { switch (purpose) { +#if FEATURE_I2C case PinSelectPurpose::I2C: # if FEATURE_I2C_MULTIPLE case PinSelectPurpose::I2C_2: @@ -438,6 +439,8 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig (bus_type == ESP32_BUS_TYPE_I2C_SLAVE_SCL)) { return conflict; } break; +#endif +#if FEATURE_SPI case PinSelectPurpose::SPI: case PinSelectPurpose::SPI_MISO: @@ -447,6 +450,7 @@ String getConflictingUse_fromPeriman(int gpio, PinSelectPurpose purpose, bool ig (bus_type == ESP32_BUS_TYPE_SPI_MASTER_SS)) { return conflict; } break; +#endif case PinSelectPurpose::Serial_input: case PinSelectPurpose::Serial_output: From 480ba5f1428232a930aa482b9f65792fea09112c Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:23:17 +0100 Subject: [PATCH 092/161] [LOG] Fix for the minified JS --- src/src/Static/WebStaticData.h | 3 ++- static/fetch_and_parse_log.min.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 static/fetch_and_parse_log.min.js diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index 813c8814e..d26acf243 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -411,6 +411,7 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = { #ifdef WEBSERVER_INCLUDE_JS static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { "function elId(e){return document.getElementById(e)}" +"const ct1 = elId('copyText_1');" "window.fetch?ct1.textContent='Fetching log entries...':" "ct1.textContent='Error: This browser is not supported. Please use a modern browser.';" "const logLevel={0:'Unused',1:'Error',2:'Info',3:'Debug',4:'Debug More',9:'Debug Dev'};" @@ -424,7 +425,7 @@ static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = { "o&&(ct1.textContent==='Fetching log entries...'&&(ct1.innerHTML=''),ct1.innerHTML+=o);" "applyLogFilter();" "elId('autoscroll')?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?'auto':'smooth'});" -"let n=t.Log.SettingsWebLogLevel,r=logLevel[n]??'Undefined';" +"let n=t.Log.SettingsWebLogLevel,r=logLevel[n]?" "?'Undefined';" "elId('current_loglevel').textContent=`Logging: ${r} (${n})`;" "loopDeLoop(e)})" ".catch(e=>{ct1.innerHTML+=`
      >> ${e.message} <<
      `;" diff --git a/static/fetch_and_parse_log.min.js b/static/fetch_and_parse_log.min.js new file mode 100644 index 000000000..302f34456 --- /dev/null +++ b/static/fetch_and_parse_log.min.js @@ -0,0 +1 @@ +function elId(e){return document.getElementById(e)}document.getElementById("logfilter")||(document.querySelector("section").innerHTML+='');const ct1=elId("copyText_1");window.fetch?ct1.textContent="Fetching log entries...":ct1.textContent="Error: This browser is not supported. Please use a modern browser.";const logLevel={0:"Unused",1:"Error",2:"Info",3:"Debug",4:"Debug More",9:"Debug Dev"};function loopDeLoop(e=1e3){setTimeout((function(){fetch("/logjson").then((e=>{if(!e.ok)throw new Error(e.status);return e.json()})).then((t=>{let o="";for(let e=0;e\n ${n.timestamp}: ${n.text}\n `)}e=t.Log.TTL||e,o&&("Fetching log entries..."===ct1.textContent&&(ct1.innerHTML=""),ct1.innerHTML+=o),applyLogFilter(),elId("autoscroll")?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?"auto":"smooth"});const n=t.Log.SettingsWebLogLevel,l=logLevel[n]??"Undefined";elId("current_loglevel").textContent=`Logging: ${l} (${n})`,loopDeLoop(e)})).catch((e=>{ct1.innerHTML+=`
      >> ${e.message} <<
      `,ct1.scrollTop=ct1.scrollHeight,loopDeLoop(5e3)}))}),e)}function applyLogFilter(){const e=elId("logfilter").value.split(";").map((e=>e.trim())).filter(Boolean),t=document.querySelectorAll("#copyText_1 > div"),o=e.some((e=>!e.startsWith("!")));for(const n of t){n.dataset.originalHtml||(n.dataset.originalHtml=n.innerHTML);let t=n.dataset.originalHtml;const l=n.textContent.toLowerCase();let i=[],s=!1,r=!o;for(const t of e)if(t.startsWith("!")){const e=t.slice(1);if(!e.includes("&")||e.startsWith("&")||e.endsWith("&")){if(e&&l.includes(e.toLowerCase())){s=!0;break}}else{const t=e.split("&").map((e=>e.trim())).filter(Boolean);let o=-1,n=!0;for(const e of t){const t=l.indexOf(e.toLowerCase(),o+1);if(-1===t){n=!1;break}o=t}if(n){s=!0;break}}}else if(!t.includes("&")||t.startsWith("&")||t.endsWith("&")){if(l.includes(t.toLowerCase())){i.push(t),r=!0;break}}else{const e=t.split("&").map((e=>e.trim())).filter(Boolean);if(e.length>=2){let t=-1,o=!0;for(const n of e){const e=l.indexOf(n.toLowerCase(),t+1);if(-1===e){o=!1;break}t=e}if(o){i.push(...e),r=!0;break}}}!s&&r?(n.style.display="",n.innerHTML=highlightOutsideTags(t,i)):n.style.display="none"}}function highlightOutsideTags(e,t){if(!t.length)return e;let o="",n=!1,l=0;for(;l"!==i){if(!n){let n=!1;for(const i of t){const t=e.slice(l,l+i.length);if(t.toLowerCase()===i.toLowerCase()){o+=`${t}`,l+=i.length,n=!0;break}}if(n)continue}o+=i,l++}else n=!1,o+=i,l++}return o}loopDeLoop(1e3);const input=document.getElementById("logfilter");input&&(input.placeholder='"!"=exclude ";"=or "&"=and (e.g. !act)'); \ No newline at end of file From d56bb776fda116a1bdaf612e10bc768728d0df68 Mon Sep 17 00:00:00 2001 From: chromoxdor <33860956+chromoxdor@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:25:27 +0100 Subject: [PATCH 093/161] Delete fetch_and_parse_log.min.js --- static/fetch_and_parse_log.min.js | 1 - 1 file changed, 1 deletion(-) delete mode 100644 static/fetch_and_parse_log.min.js diff --git a/static/fetch_and_parse_log.min.js b/static/fetch_and_parse_log.min.js deleted file mode 100644 index 302f34456..000000000 --- a/static/fetch_and_parse_log.min.js +++ /dev/null @@ -1 +0,0 @@ -function elId(e){return document.getElementById(e)}document.getElementById("logfilter")||(document.querySelector("section").innerHTML+='');const ct1=elId("copyText_1");window.fetch?ct1.textContent="Fetching log entries...":ct1.textContent="Error: This browser is not supported. Please use a modern browser.";const logLevel={0:"Unused",1:"Error",2:"Info",3:"Debug",4:"Debug More",9:"Debug Dev"};function loopDeLoop(e=1e3){setTimeout((function(){fetch("/logjson").then((e=>{if(!e.ok)throw new Error(e.status);return e.json()})).then((t=>{let o="";for(let e=0;e\n ${n.timestamp}: ${n.text}\n `)}e=t.Log.TTL||e,o&&("Fetching log entries..."===ct1.textContent&&(ct1.innerHTML=""),ct1.innerHTML+=o),applyLogFilter(),elId("autoscroll")?.checked&&ct1.scrollTo({top:ct1.scrollHeight,behavior:e<=500?"auto":"smooth"});const n=t.Log.SettingsWebLogLevel,l=logLevel[n]??"Undefined";elId("current_loglevel").textContent=`Logging: ${l} (${n})`,loopDeLoop(e)})).catch((e=>{ct1.innerHTML+=`
      >> ${e.message} <<
      `,ct1.scrollTop=ct1.scrollHeight,loopDeLoop(5e3)}))}),e)}function applyLogFilter(){const e=elId("logfilter").value.split(";").map((e=>e.trim())).filter(Boolean),t=document.querySelectorAll("#copyText_1 > div"),o=e.some((e=>!e.startsWith("!")));for(const n of t){n.dataset.originalHtml||(n.dataset.originalHtml=n.innerHTML);let t=n.dataset.originalHtml;const l=n.textContent.toLowerCase();let i=[],s=!1,r=!o;for(const t of e)if(t.startsWith("!")){const e=t.slice(1);if(!e.includes("&")||e.startsWith("&")||e.endsWith("&")){if(e&&l.includes(e.toLowerCase())){s=!0;break}}else{const t=e.split("&").map((e=>e.trim())).filter(Boolean);let o=-1,n=!0;for(const e of t){const t=l.indexOf(e.toLowerCase(),o+1);if(-1===t){n=!1;break}o=t}if(n){s=!0;break}}}else if(!t.includes("&")||t.startsWith("&")||t.endsWith("&")){if(l.includes(t.toLowerCase())){i.push(t),r=!0;break}}else{const e=t.split("&").map((e=>e.trim())).filter(Boolean);if(e.length>=2){let t=-1,o=!0;for(const n of e){const e=l.indexOf(n.toLowerCase(),t+1);if(-1===e){o=!1;break}t=e}if(o){i.push(...e),r=!0;break}}}!s&&r?(n.style.display="",n.innerHTML=highlightOutsideTags(t,i)):n.style.display="none"}}function highlightOutsideTags(e,t){if(!t.length)return e;let o="",n=!1,l=0;for(;l"!==i){if(!n){let n=!1;for(const i of t){const t=e.slice(l,l+i.length);if(t.toLowerCase()===i.toLowerCase()){o+=`${t}`,l+=i.length,n=!0;break}}if(n)continue}o+=i,l++}else n=!1,o+=i,l++}return o}loopDeLoop(1e3);const input=document.getElementById("logfilter");input&&(input.placeholder='"!"=exclude ";"=or "&"=and (e.g. !act)'); \ No newline at end of file From a38862256c4444a18ef1ea30c4d240d7d81d160a Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 14 Feb 2026 01:02:48 +0100 Subject: [PATCH 094/161] Update generate_web_flasher_manifest.py fix showing C6 in list When both ESP32-C6 & ESP32-C61 build was found, only C61 was listed, not C6 --- tools/pio/generate_web_flasher_manifest.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tools/pio/generate_web_flasher_manifest.py b/tools/pio/generate_web_flasher_manifest.py index 6e005f21f..306887840 100644 --- a/tools/pio/generate_web_flasher_manifest.py +++ b/tools/pio/generate_web_flasher_manifest.py @@ -2,7 +2,6 @@ import argparse import json import sys import os -import collections manifest_binfiles = {} @@ -35,14 +34,14 @@ def create_display_text(description, version, families): esp32_split.append('C3') if 'ESP32-C5' in families: esp32_split.append('C5') + if 'ESP32-C6' in families: + esp32_split.append('C6') if 'ESP32-C61' in families: esp32_split.append('C61') - elif 'ESP32-C6' in families: - esp32_split.append('C6') + if 'ESP32-H2' in families: + esp32_split.append('H2') if 'ESP32-H21' in families: esp32_split.append('H21') - elif 'ESP32-H2' in families: - esp32_split.append('H2') if 'ESP32-P4' in families: esp32_split.append('P4') @@ -383,7 +382,7 @@ def generate_manifest_files(bin_folder, output_prefix): ' \n', ' \n', ' \n', ' \n', From a98cff619df3ed523ef854c39804e6c4ea5666e1 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 14 Feb 2026 16:57:54 +0100 Subject: [PATCH 095/161] [P180] Set Cache-name field by default when empty for better performance, docs updated --- docs/source/Plugin/P180.rst | 16 ++++++++-------- .../Plugin/P180_DeviceConfiguration.png | Bin 62323 -> 70546 bytes src/_P180_I2C_generic.ino | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/source/Plugin/P180.rst b/docs/source/Plugin/P180.rst index bbd986c3c..ab727a0c4 100644 --- a/docs/source/Plugin/P180.rst +++ b/docs/source/Plugin/P180.rst @@ -82,7 +82,7 @@ For each Value1 .. Value4 output, depending on the **Output Data Type** setting, * **Value x type**: Here you can select a specific output type for each Value. This shows a selection of the **Output Data Type** selector, but only the single-value types. This will also be used for sending to Domoticz controllers and for MQTT Auto-Discovery. -* **Cache-Name x (optional)**: When set to a unique name (for this task), the parsed & pre-processed command sequence will be stored in a memory-cache, to *significantly* improve the speed of the plugin in runtime. When not set, the command sequence will be parsed every time the task is run (**Interval** setting or ``taskRun`` command). To be able to use event processing for 1ps, 10ps of 50ps, setting a Cache-Name is required! +* **Cache-Name x**: Should be set to a unique name (for this task), so the parsed & pre-processed command sequence will be stored in a memory-cache, to *significantly* improve the speed of the plugin in runtime. Initially the Cache-name is set to ``c1`` .. ``c4`` (depending on the number of values selected) and can be changed as desired. When empty it will be reset to ``cX`` again. * **I2C Commands x**: The sequence of commands to execute for this value. Nothing will be done if this is empty. The corresponding Value output can be set from another I2C Commands sequence. The currently used vs. the available space is shown next to the field. I2C Commands are explained below. @@ -373,7 +373,7 @@ The plugin should be configured with these settings: * **Output Data Type**: ``Single`` -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``qrsize``, so the I2C Command sequence is cached for much improved execution-performance. And the additional de-duplication uses the ``1ps`` event-interval, and the sequence must be cached for that event to work. +* **Cache-Name 1**: Fill with a useable name, f.e. ``qrsize``, so the I2C Command sequence is cached for much improved execution-performance. And the additional de-duplication uses the ``1ps`` event-interval, and the sequence must be cached for that event to work. * **I2C Commands 1**: @@ -455,7 +455,7 @@ The plugin should be configured with these settings: ``put.b.2.0x3f.0xf9``: Stop continuous measurement. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``sdp``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``sdp``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -517,7 +517,7 @@ The plugin should be configured with these settings: * **Values**: the name can be changed to Distance, and the number of decimals 0. Stats can be enabled, as desired. The Unit of Measure should he set to ``cm``, and the Value Type to ``Distance`` -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -544,7 +544,7 @@ The plugin should be configured with these settings: * **Values**: the first name can be changed to ``Distance``, and the number of decimals 0. Stats can be enabled, as desired. The Unit of Measure should he set to ``cm``, and the Value Type to ``Distance``, the second name can be changed to f.e. ``SignalStrength``, and number of decimals to 0. Stats can be enabled, as desired. No further settings for this value, as it has an undefined unit and value type. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``distance``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: @@ -590,15 +590,15 @@ The plugin should be configured with these settings: ``write.u8.0x0A.0x00``: Set sensor in Suspend mode. -* **Cache-Name 1 (optional)**: Fill with a useable name, f.e. ``x``, so the I2C Command sequence is cached for much improved execution-performance. +* **Cache-Name 1**: Fill with a useable name, f.e. ``x``, so the I2C Command sequence is cached for much improved execution-performance. * **I2C Commands 1**: ``read.16le.0x01`` Read 2 bytes starting at register 1, the ``x`` value, and store in first Value field. -* **Cache-Name 2 (optional)**: Fill with a useable name, f.e. ``y``. +* **Cache-Name 2**: Fill with a useable name, f.e. ``y``. * **I2C Commands 2**: ``read.16le.0x03`` Read 2 bytes starting at register 3, the ``y`` value, and store in second Value field. -* **Cache-Name 3 (optional)**: Fill with a useable name, f.e. ``z``. +* **Cache-Name 3**: Fill with a useable name, f.e. ``z``. * **I2C Commands 3**: ``read.16le.0x05`` Read 2 bytes starting at register 5, the ``z`` value, and store in third Value field. diff --git a/docs/source/Plugin/P180_DeviceConfiguration.png b/docs/source/Plugin/P180_DeviceConfiguration.png index 52be493cb9d0cc7270c9a8d056e4710a5014570b..19959d788fb4ff38d7932cde89c2bf7261360a7f 100644 GIT binary patch literal 70546 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 TVpVTG)jZtJo~S+c)2;smF`h?} literal 62323 zcmdqJcU)6hyEcqEHXLPCiVDIAA{~?>3IS%6A|ONvMY;uq(4`qd$@nOuATV^LBqI`cVU_5l<#}r@4V+Z|9tzWzhsBK*Iw&hYu)9#uX{ha zW@>Pd_XIBw56?lvE4t=9JU^j%cy=r7+XH@S(1?uyAA4>Y8|Z>haI9ahLxGR|zE^Dg zd3X-J-TuELS?-W9_)$MV?|Oi)i~nsuxNiX5$D8N2o1c%D%R%0A7ZuK`Dkz^-R{K@{ z*K?{Gsu#e&gS>jMYbwexWrcI#cZFXKe0*FzU4Fg%^`5vETqO_BuRMmjzgr+2=Z5Fw zBtveAt?Y3;eB`%%hf80_*1Pp69scPWhllUducyx_xjp>tweg>SYTcb{*l;G^xhH>7 z>mCx{d8GcA$y+c4rFGgQ|85e+hafb;6cF7?Ju4WDEq<3A2!lJR6IP- zBK|9pj%OxvO4eSE;TrN(^sHUUb^$(@gV} zwk*4Po+2~IML1Avq72*!%Pe9WV@;^kU-r9{>W&e}`Z7XL8*0z25ym`^JQJ0YgRo6e zEHL-C>YtJ`u-P(9vj$7m5k%S@wOLODsgIJ~XLTYc!x zP2FeT$2oTgj1y}CVJ5_~WkRRSvzKPHlKZNPkQ92&}W8(=M7`L_@Q^tB?)?B z1DQnP$HzFzy5Kjl$7epJz_XL0a16I>TX=w(xLEhmhT5?iG)G&FcI?yUrMk?TXdG!> zcyQrkutHMM_hFR6%phJ#m+q200!q6zE@KS%ldndLO9oqb&9uB(p2&`%Pz~v-wT%A4

      rl0Bm=Vfad>WXi3>Ct)ui*L|(3 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 IM6N<$g7NQ(fdBvi diff --git a/src/_P180_I2C_generic.ino b/src/_P180_I2C_generic.ino index af3e495fc..15bf71e8c 100644 --- a/src/_P180_I2C_generic.ino +++ b/src/_P180_I2C_generic.ino @@ -6,6 +6,7 @@ // ####################################################################################################### /** Changelog: + * 2026-02-14 tonhuisman: Set default Cache-name when empty, for improved plugin runtime performance. * 2025-12-06 tonhuisman: Enable CustomVTypeVar and MQTTStateClass device options, as any type of sensor can be connected * 2025-06-04 tonhuisman: Add [#log] to Get Config to fetch the current Parsing and execution log setting * 2025-06-03 tonhuisman: Restore PLUGIN_GET_CONFIG_VALUE support, to allow I2C operations to be executed and return a value without @@ -251,7 +252,12 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) sensorTypeHelper_webformLoad(event, P180_VALUE_OFFSET + i, singleOptions.size(), &singleOptions[0], false, i + 1); // Name - addFormTextBox(strformat(F("Cache-Name %d (optional)"), i + 1), + const String cacheName = concat(F("c"), i + 1); + + if (strings[P180_BUFFER_START_CACHE + i].isEmpty()) { + strings[P180_BUFFER_START_CACHE + i] = cacheName; + } + addFormTextBox(strformat(F("Cache-Name %d"), i + 1), getPluginCustomArgName(20 + i), strings[P180_BUFFER_START_CACHE + i], P180_MAX_NAME_LENGTH); @@ -272,8 +278,6 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) { const String addr = webArg(F("i2c_addr")); P180_I2C_ADDRESS = (uint8_t)strtol(addr.c_str(), 0, 16); - P180_ENABLE_PIN = getFormItemInt(F("taskdevicepin1")); - P180_RST_PIN = getFormItemInt(F("taskdevicepin2")); P180_LOG_DEBUG = isFormItemChecked(F("plog")) ? 1 : 0; pconfig_webformSave(event, P180_SENSOR_TYPE_INDEX); @@ -285,7 +289,11 @@ boolean Plugin_180(uint8_t function, struct EventStruct *event, String& string) String strings[P180_CUSTOM_BUFFER_SIZE]; for (uint8_t varNr = 0; varNr < VARS_PER_TASK; varNr++) { - strings[P180_BUFFER_START_CACHE + varNr] = webArg(getPluginCustomArgName(20 + varNr)); // Name + strings[P180_BUFFER_START_CACHE + varNr] = webArg(getPluginCustomArgName(20 + varNr)); // Name + + if (strings[P180_BUFFER_START_CACHE + varNr].isEmpty()) { + strings[P180_BUFFER_START_CACHE + varNr] = concat(F("c"), varNr + 1); // Default cache-name + } strings[P180_BUFFER_START_COMMANDS + varNr] = webArg(getPluginCustomArgName(30 + varNr)); // Commands } strings[P180_BUFFER_ENTRY_INIT] = webArg(getPluginCustomArgName(40)); // INIT Commands From 9d86ffb085adac234bc619c5d7716cf0079f1232 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 14 Feb 2026 21:06:08 +0100 Subject: [PATCH 096/161] [Docs] Use pioarduino plugin instead of PlatformIO for VSCode --- docs/source/Participate/PlatformIO.rst | 71 ++++++++++++++------------ 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/docs/source/Participate/PlatformIO.rst b/docs/source/Participate/PlatformIO.rst index 3b39bae42..471e40eaa 100644 --- a/docs/source/Participate/PlatformIO.rst +++ b/docs/source/Participate/PlatformIO.rst @@ -3,14 +3,14 @@ PlatformIO ********** -ESP easy can be built using the Arduino IDE or PlatformIO (PIO). -Arduino IDE is not being used during development, so it may take some more effort to get it setup for building ESPeasy. +ESP easy can be built using the PlatformIO/pioarduino IDE (PIO). +Arduino IDE is no longer supported for development though it's technically possible to be used. It may take considerable effort to get it setup for building ESPeasy. -We *strongly* advise to use PlatformIO as build environment. +We *strongly* advise to use PIO as build environment. -PlatformIO is just the build, test and upload environment for many micro controllers like the ESP8266 and ESP32 we use. +PIO is just the build, test and upload environment for many micro controllers like the ESP8266 and ESP32 we use. -On top of that you need to use an editor, or so called IDE in which PlatformIO will be used. +On top of that you need to use an editor, or so called IDE in which PIO will be used. The current choice is: @@ -21,10 +21,10 @@ VSCode is free to use and available for Windows, MacOS and Linux. Apart from VSCode, there are more available, like Eclipse, MS Visual Studio (IDE) and probably more. -PlatformIO Prerequisites -======================== +PlatformIO/pioarduino Prerequisites +=================================== -PlatformIO does need at least the following: +PIO does need at least the following: * Python * Git command line tools (`download `_) @@ -58,17 +58,18 @@ install Pyton in Windows for PlatformIO. These steps have been explained in this Youtube video: https://youtu.be/ArqwMcYSMsU though you might want to use the latest version of both the Git tools and Python. -PlatformIO with VSCode -====================== +PlatformIO/pioarduino with VSCode +================================= Install ------- -For development of ESPEasy, a number of extensions has to be installed in VS-Code: +For development of ESPEasy, a number of extensions have to be installed in VS-Code: -* PlatformIO IDE (by PlatformIO) -* C/C++ IntelliSense (by Microsoft) +* pioarduino IDE (by pioarduino) +* C/C++ (by Microsoft) * C/C++ Extension Pack (by Microsoft) +* C/C++ Themes (by Microsoft) * Uncrustify (by Zachary Flower, originally by Laurent Tréguier) Optional, but highly recommended: @@ -79,6 +80,7 @@ Optional, but highly recommended: * All Autocomplete (by Atishay Jain) * Excel Viewer (by GrapeCity) * Esbonio - An extension for editing sphinx projects (by Swyddfa) +* reStructuredText (by LeXtudio) * reStructuredText Syntax highlighting (by Trond Snekvik) * Extension pack for reStructuredText (by LeXtudio Inc.) * Markdown All in One (by Yu Zhang) @@ -109,17 +111,19 @@ After setting it as the default formatter, the hotkey Alt-Shift-F (Cmd-Shift-F o There used to be a reference to the Atom editor, but both Atom, and the PlatformIO plugin for Atom, are no longer maintained by their owners, so it was removed from this documentation. -Load a project using PlatformIO -------------------------------- +Load a project using PlatformIO/pioarduino +------------------------------------------ + +.. note:: PlatformIO has been superceded by the Community-supported **pioarduino** initiative. For simplicity, in most places the abbreviation PIO is throughout this section of the documentation. If you have PIO installed and the source tree cloned to your hard drive, then you can open the main dir of the repository. The main directory of the repository is the level with the file platformio.ini in it. -Then in a few moments after opening the directory, on the left there will appear an alien logo, the logo of PlatformIO. +Then in a few moments after opening the directory, on the left there will appear an chip-package logo, the logo of pioarduino. If you click that one, you will get a tree with lots and lots of project tasks and environments. -It is important to note that PlatformIO does everything based on environments, which are defined in the platformio.ini file. -In the PlatformIO menu (on the left) everything is grouped per environment. +It is important to note that PIO does everything based on environments, which are defined in the platformio.ini file. +In the PIO menu (on the left) everything is grouped per environment. An environment entry has several tasks, like: @@ -130,8 +134,9 @@ An environment entry has several tasks, like: * Clean * ... many more. -Some of these options only are available when you have registered with PlatformIO and some are only for paid subscriptions. -At least the basic ones used for almost any user are available with the free account. +.. .. Some of these options only are available when you have registered with PlatformIO and some are only for paid subscriptions. + +.. .. At least the basic ones used for almost any user are available with the free account. The environment definitions all have at least the used micro controller in the name and the amount of flash memory used. @@ -144,8 +149,8 @@ For example: * ..._ESP32_16M8M_LittleFS -> ESP32 with 16 MB flash and a 4 MB partition for the sketch. (8MB LittleFS) * ..._ESP32_16M1M_ETH -> ESP32 with 16 MB flash and a 4 MB partition for the sketch. (1MB SPIFFS, Wired ethernet support) -Make a custom build using PlatformIO ------------------------------------- +Make a custom build using PIO +----------------------------- The easiest is to go for the environment ``custom_ESP8266_4M1M`` and unfold that one. Then select ``Build`` to see if it will start building. @@ -213,7 +218,7 @@ Now that Linux is installed, it's strongly advised to get it up to date with the NB: The ``sudo`` command will ask for your current account password once, (and maybe later again after some time has passed). When asked for confirmation, confirm the installation of any needed updates, or add the ``-y`` parameter to the ``upgrade`` command to continue without questions. -Additionally some tools need to be installed so PlatformIO can be properly installed later, and we can use the Uncrustify plugin in VS-Code to format the code nicely: +Additionally some tools need to be installed so PIO can be properly installed later, and we can use the Uncrustify plugin in VS-Code to format the code nicely: .. highlight::sh @@ -307,7 +312,7 @@ Upload to ESP Linux ----- -For Linux, you may need to install 99-platformio-udev.rules to make PlatformIO upload tools work in vscode. +For Linux, you may need to install 99-platformio-udev.rules to make PIO upload tools work in vscode. .. highlight::sh @@ -317,12 +322,12 @@ Starter guide for (local) development on ESPEasy For those with less development experience, or less experience in using Github, this chapter is intended as a **How To** guide to get started with development on ESPEasy. -It tries to help setting up Visual Studio Code (VSCode) with the PlatformIO development environment and additional VSCode plugins that aid in easier working on code and documentation. +It tries to help setting up Visual Studio Code (VSCode) with the PIO development environment and additional VSCode plugins that aid in easier working on code and documentation. The global steps described here are: - Creating a private copy on Github -- Getting VSCode and PlatformIO set up +- Getting VSCode and PIO set up - Getting the source code from Github onto your system - Compiling the source code - Creating a branch to make your changes @@ -366,12 +371,12 @@ After this completes, you can view the fork in your Github dashboard at https:// (You have to replace [your_github_handle] with the name you selected during the Github sign-up procedure) -Install VSCode and PlatformIO +Install VSCode and PIO ------------------------------ -Earlier on this page, a complete description has been given on how to install **PlatformIO with VSCode** with the required and advised optional extensions and the git command-line tools. +Earlier on this page, a complete description has been given on how to install **PlatformIO/pioarduino with VSCode** with the required and advised optional extensions and the git command-line tools. -NB: PlatformIO is often shortened to PIO. +NB: PlatformIO/pioarduino is shortened to PIO. Clone your forked repository to your computer --------------------------------------------- @@ -422,7 +427,7 @@ Depending on your usual workflow, the current VSCode environment can be saved as Compile an ESPEasy PIO environment ---------------------------------- -ESPEasy supports several different configurations of ESP units, ESP8266, ESP8285 and ESP32, and also some predefined hardware configurations and sets of plugins & controllers. This has been turned into several different PlatformIO environments, to make managing the different builds as easy as possible. +ESPEasy supports several different configurations of ESP units, ESP8266, ESP8285 and ESP32, and also some predefined hardware configurations and sets of plugins & controllers. This has been turned into several different PIO environments, to make managing the different builds as easy as possible. To compile such 'environment' (PIO terminology), select the PIO button (it looks like an alien) in VSCode: @@ -492,10 +497,10 @@ Especially for new plugins, it is highly recommended to write documentation, as Using external libraries -~~~~~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: - Since November 2022, the PlatformIO configuration for ESPEasy was changed to *require* all libraries to be locally available, to a) prevent unexpected 'surprises' when an external library is updated, and b) greatly improve build output stability and quality. + Since November 2022, the PIO configuration for ESPEasy was changed to *require* all libraries to be locally available, to a) prevent unexpected 'surprises' when an external library is updated, and b) greatly improve build output stability and quality. While developing a plugin or some other feature, often you use an existing library to re-use that (assumably) proven and tested functionality. To include such library, there is a prerequisite, and some generic steps to take: @@ -516,7 +521,7 @@ Writing documentation Updating, or adding if it does not yet exist, the documentation is a useful activity that should be part of changing or adding to the ESPEasy code. Some of the optional VSCode extensions are specifically aimed at that task. -The documentation is created in the reStructuredText format, using mostly a ``.rst`` extension, and can be built locally by installing the sphinx tool. This can be installed manually by opening a PlatformIO Terminal window in VSCode (an already open PIO Terminal can also be used, when using WSL2 a PlatformIO Terminal is *required* to execute in the correct Python Virtual Environment (venv)) and issuing these commands: +The documentation is created in the reStructuredText format, using mostly a ``.rst`` extension, and can be built locally by installing the sphinx tool. This can be installed manually by opening a PIO Terminal window in VSCode (an already open PIO Terminal can also be used, when using WSL2 a PIO Terminal is *required* to execute in the correct Python Virtual Environment (venv)) and issuing these commands: .. code-block:: From 90f45e032b470d278b6717907e41a668d5e8c0b8 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 14 Feb 2026 21:09:26 +0100 Subject: [PATCH 097/161] [Docs] Fix error in internal commands list so it is visible again --- docs/source/Plugin/P000_commands.repl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/Plugin/P000_commands.repl b/docs/source/Plugin/P000_commands.repl index bacfd19f0..46c458ecc 100644 --- a/docs/source/Plugin/P000_commands.repl +++ b/docs/source/Plugin/P000_commands.repl @@ -549,7 +549,7 @@ :green:`Rules`"," Like ``PostToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " Provision,Config"," :red:`Internal`"," @@ -668,7 +668,7 @@ :green:`Rules`"," Like ``PutToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " Reboot"," :red:`Internal`"," @@ -748,7 +748,7 @@ :green:`Rules`"," Like ``SendToHTTP`` but using a TLS connection, by default on port 443 - NB: No certificate validation is done! + NB: No certificate validation is done!" " SendToUDP"," :green:`Rules`"," From 8b335014ca9de0b6cb252e5397ca4c86773373c9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 14 Feb 2026 21:57:35 +0100 Subject: [PATCH 098/161] [Build] Disable unneeded 'page will reload' messages --- src/_P046_VentusW266.ino | 2 +- src/_P047_i2c-soil-moisture-sensor.ino | 2 +- src/_P105_AHT.ino | 2 +- src/_P129_74HC165.ino | 2 +- src/_P135_SCD4x.ino | 2 +- src/_P137_AXP192.ino | 6 +++--- src/_P143_I2C_Rotary.ino | 2 +- src/_P147_SGP4x.ino | 4 ++-- src/_P150_TMP117.ino | 4 ++-- src/_P159_LD2410.ino | 2 +- src/_P167_Vindstyrka.ino | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/_P046_VentusW266.ino b/src/_P046_VentusW266.ino index 8a1063052..4ca28388c 100644 --- a/src/_P046_VentusW266.ino +++ b/src/_P046_VentusW266.ino @@ -189,7 +189,7 @@ boolean Plugin_046(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.reloadonchange = true; selector.addFormSelector(F("Plugin function"), F("p046"), choice); - addFormNote(F("Changing the function will reload this page.")); + // addFormNote(F("Changing the function will reload this page.")); } if (choice == 0) { diff --git a/src/_P047_i2c-soil-moisture-sensor.ino b/src/_P047_i2c-soil-moisture-sensor.ino index 95c021722..740be6e50 100644 --- a/src/_P047_i2c-soil-moisture-sensor.ino +++ b/src/_P047_i2c-soil-moisture-sensor.ino @@ -170,7 +170,7 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) selector.default_index = static_cast(P047_MODEL_CATNIP); selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("model"), P047_MODEL); - addFormNote(F("Changing the Sensor model will reload the page.")); + // addFormNote(F("Changing the Sensor model will reload the page.")); } if ((P047_MODEL_CATNIP == static_cast(P047_MODEL)) diff --git a/src/_P105_AHT.ino b/src/_P105_AHT.ino index 1852aad1c..31d046c0c 100644 --- a/src/_P105_AHT.ino +++ b/src/_P105_AHT.ino @@ -155,7 +155,7 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optionCount, options, indices); selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ahttype"), P105_AHT_TYPE); - addFormNote(F("Changing Sensor model will reload the page.")); + // addFormNote(F("Changing Sensor model will reload the page.")); if (static_cast(AHTx_device_type::AHT10_DEVICE) == P105_AHT_TYPE) { addFormCheckBox(F("AHT10 Alternative initialization"), F("altinit"), P105_ALT_INIT); diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino index 6d956559e..27f54c015 100644 --- a/src/_P129_74HC165.ino +++ b/src/_P129_74HC165.ino @@ -187,7 +187,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("Number of chips (Q7 → DS)"), F("chipcnt"), P129_CONFIG_CHIP_COUNT); addUnit(concat(F("Daisychained 1.."), P129_MAX_CHIP_COUNT)); # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); + // addFormNote(F("Changing the number of chips will reload the page and update the Event configuration.")); # endif // ifndef LIMIT_BUILD_SIZE } diff --git a/src/_P135_SCD4x.ino b/src/_P135_SCD4x.ino index 36507c325..c19a295b2 100644 --- a/src/_P135_SCD4x.ino +++ b/src/_P135_SCD4x.ino @@ -121,7 +121,7 @@ boolean Plugin_135(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ptype"), P135_SENSOR_TYPE); # ifndef LIMIT_BUILD_SIZE - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef LIMIT_BUILD_SIZE } diff --git a/src/_P137_AXP192.ino b/src/_P137_AXP192.ino index 7bdad37fb..342477f5e 100644 --- a/src/_P137_AXP192.ino +++ b/src/_P137_AXP192.ino @@ -207,9 +207,9 @@ boolean Plugin_137(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = !Settings.isPowerManagerTask(event->TaskIndex); selector.addFormSelector(F("Predefined device configuration"), F("predef"), 0); - if (!Settings.isPowerManagerTask(event->TaskIndex)) { - addFormNote(F("Page will reload when selection is changed.")); - } + // if (!Settings.isPowerManagerTask(event->TaskIndex)) { + // addFormNote(F("Page will reload when selection is changed.")); + // } const P137_PredefinedDevices_e current_ = static_cast(P137_CURRENT_PREDEFINED); diff --git a/src/_P143_I2C_Rotary.ino b/src/_P143_I2C_Rotary.ino index 9303670f5..cff8e6a03 100644 --- a/src/_P143_I2C_Rotary.ino +++ b/src/_P143_I2C_Rotary.ino @@ -194,7 +194,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optionCount, selectModeOptions, selectModeValues); selector.reloadonchange = true; selector.addFormSelector(F("Encoder type"), F("pdevice"), P143_ENCODER_TYPE); - addFormNote(F("Changing the Encoder type will reload the page and reset Encoder specific settings to default!")); + // addFormNote(F("Changing the Encoder type will reload the page and reset Encoder specific settings to default!")); } P143_DeviceType_e device = static_cast(P143_ENCODER_TYPE); diff --git a/src/_P147_SGP4x.ino b/src/_P147_SGP4x.ino index 5d74074d6..6154cd5dc 100644 --- a/src/_P147_SGP4x.ino +++ b/src/_P147_SGP4x.ino @@ -135,13 +135,13 @@ boolean Plugin_147(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Sensor model"), F("ptype"), P147_SENSOR_TYPE); # ifndef BUILD_NO_DEBUG - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef BUILD_NO_DEBUG } addFormSelector_YesNo(F("Use Compensation"), F("comp"), P147_GET_USE_COMPENSATION, true); # ifndef BUILD_NO_DEBUG - addFormNote(F("Page will reload on change.")); + // addFormNote(F("Page will reload on change.")); # endif // ifndef BUILD_NO_DEBUG if (P147_GET_USE_COMPENSATION) { diff --git a/src/_P150_TMP117.ino b/src/_P150_TMP117.ino index ab869e07d..3b924ac17 100644 --- a/src/_P150_TMP117.ino +++ b/src/_P150_TMP117.ino @@ -162,7 +162,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) selector.reloadonchange = true; selector.addFormSelector(F("Conversion mode"), F("conv"), P150_GET_CONF_CONVERSION_MODE); # ifndef BUILD_NO_DEBUG - addFormNote(F("Changing this setting will save and reload this page.")); + // addFormNote(F("Changing this setting will save and reload this page.")); # endif // ifndef BUILD_NO_DEBUG } @@ -196,7 +196,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) addFormSelector_YesNo(F("Enable 'Raw' value"), F("raw"), P150_GET_OPT_ENABLE_RAW ? 1 : 0, true); # ifndef BUILD_NO_DEBUG - addFormNote(F("Changing this setting will save and reload this page.")); + // addFormNote(F("Changing this setting will save and reload this page.")); # endif // ifndef BUILD_NO_DEBUG addFormCheckBox(F("Log measured values (INFO)"), F("log"), P150_GET_OPT_ENABLE_LOG); diff --git a/src/_P159_LD2410.ino b/src/_P159_LD2410.ino index cff0418d6..161150998 100644 --- a/src/_P159_LD2410.ino +++ b/src/_P159_LD2410.ino @@ -230,7 +230,7 @@ boolean Plugin_159(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { addFormSelector_YesNo(F("Engineering mode"), F("eng"), P159_GET_ENGINEERING_MODE, true); - addFormNote(F("When changing this setting the page will be reloaded")); + // addFormNote(F("When changing this setting the page will be reloaded")); addFormCheckBox(F("Generate Events only when changed"), F("diff"), P159_GET_UPDATE_DIFF_ONLY); diff --git a/src/_P167_Vindstyrka.ino b/src/_P167_Vindstyrka.ino index 8f65203ee..19d362ce6 100644 --- a/src/_P167_Vindstyrka.ino +++ b/src/_P167_Vindstyrka.ino @@ -190,7 +190,7 @@ boolean Plugin_167(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector(optCount, options_model, options_model_value); selector.reloadonchange = true; selector.addFormSelector(F("Model Type"), P167_MODEL_LABEL, P167_MODEL); - addFormNote(F("Changing the Model Type will reload the page.")); + // addFormNote(F("Changing the Model Type will reload the page.")); if (P167_MODEL == P167_MODEL_VINDSTYRKA) { addFormPinSelect(PinSelectPurpose::Generic_input, F("MonPin SCL"), F("taskdevicepin3"), P167_MON_SCL_PIN); From 80af69ed7a7b357187fdd4f4f8f83250db69167a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 16 Feb 2026 21:42:29 +0100 Subject: [PATCH 099/161] [Docs] Fix typo --- docs/source/Participate/PlatformIO.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/Participate/PlatformIO.rst b/docs/source/Participate/PlatformIO.rst index 471e40eaa..b84541f8d 100644 --- a/docs/source/Participate/PlatformIO.rst +++ b/docs/source/Participate/PlatformIO.rst @@ -64,7 +64,7 @@ PlatformIO/pioarduino with VSCode Install ------- -For development of ESPEasy, a number of extensions have to be installed in VS-Code: +For development of ESPEasy, a number of extensions has to be installed in VS-Code: * pioarduino IDE (by pioarduino) * C/C++ (by Microsoft) From 1e5e2cf1eed3f74ad5740e0391e21ef66f99e375 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 17 Feb 2026 13:11:45 +0100 Subject: [PATCH 100/161] [Network] Add 'Fallback Network Interface' concept. --- .../DataStructs/NWPluginData_static_runtime.h | 2 + .../NWPluginData_static_runtime_ESP32.cpp | 19 +- .../NWPluginData_static_runtime_ESP8266.cpp | 5 + src/ESPEasy/net/Globals/NWPlugins.cpp | 235 ++++++++++-------- src/ESPEasy/net/Globals/NetworkState.cpp | 2 +- src/ESPEasy/net/Globals/NetworkState.h | 1 + .../net/Helpers/NWPlugin_import_export.cpp | 2 +- .../NW002_data_struct_WiFi_AP.cpp | 2 +- .../NW003_data_struct_ETH_RMII.cpp | 8 +- .../NW004_data_struct_ETH_SPI.cpp | 8 +- .../NW005_data_struct_PPP_modem.cpp | 19 +- .../NW005_data_struct_PPP_modem.h | 3 + src/ESPEasy/net/_NW001_WiFi_STA.cpp | 5 +- src/ESPEasy/net/_NW005_PPP_modem.cpp | 10 + .../net/wifi/ESPEasyWiFi_state_machine.cpp | 2 + src/src/DataStructs/SettingsStruct.h | 8 +- src/src/DataStructs_templ/SettingsStruct.cpp | 18 +- src/src/Helpers/Misc.cpp | 46 ++-- src/src/Helpers/PeriodicalActions.cpp | 7 +- src/src/Helpers/Scheduler_NetworkTimer.cpp | 25 +- src/src/WebServer/NetworkPage.cpp | 12 +- 21 files changed, 293 insertions(+), 146 deletions(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h index 4641e8995..0e6c1d687 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime.h @@ -103,6 +103,8 @@ struct NWPluginData_static_runtime { void mark_disconnected(); void log_disconnected(); + void mark_connect_failed(); + // ============================================= // Keep track of connection durations // per host/interface diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp index 93fd941ab..bc2faf356 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP32.cpp @@ -108,7 +108,7 @@ void NWPluginData_static_runtime::mark_start() # endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) # if NW_PLUGIN_LOG_EVENTS - if (loglevelActiveFor(LOG_LEVEL_INFO)) { + if (!_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Started"), _eventInterfaceName.c_str())); @@ -121,7 +121,7 @@ void NWPluginData_static_runtime::mark_stop() _startStopStats.setOff(); # if NW_PLUGIN_LOG_EVENTS - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Stopped"), _eventInterfaceName.c_str())); @@ -134,7 +134,7 @@ void NWPluginData_static_runtime::mark_got_IP() // Set OnOffTimer to off so we can also count how often we get new IP _gotIPStats.forceSet(true); - if (!_netif) { return; } + if (!_netif || _eventInterfaceName.isEmpty()) { return; } if (!_netif->isDefault()) { nonDefaultNetworkInterface_gotIP = true; @@ -200,7 +200,7 @@ void NWPluginData_static_runtime::mark_lost_IP() # endif # if NW_PLUGIN_LOG_EVENTS - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Lost IP"), _eventInterfaceName.c_str())); @@ -235,7 +235,7 @@ void NWPluginData_static_runtime::mark_connected() void NWPluginData_static_runtime::log_connected() { - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { if (_establishConnectStats.getCycleCount()) { // Log duration addLog(LOG_LEVEL_INFO, strformat( @@ -269,7 +269,7 @@ void NWPluginData_static_runtime::mark_disconnected() void NWPluginData_static_runtime::log_disconnected() { - if (_netif && loglevelActiveFor(LOG_LEVEL_INFO)) { + if (_netif && !_eventInterfaceName.isEmpty() && loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat( F("%s: Disconnected. Connected for: %s"), _eventInterfaceName.c_str(), @@ -278,6 +278,13 @@ void NWPluginData_static_runtime::log_disconnected() } } + +void NWPluginData_static_runtime::mark_connect_failed() +{ + networkConnectionFailed = true; +} + + } // namespace net } // namespace ESPEasy diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index cbc21b166..d92fb8c86 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -115,6 +115,11 @@ void NWPluginData_static_runtime::log_disconnected() # endif // ifndef BUILD_NO_DEBUG } +void NWPluginData_static_runtime::mark_connect_failed() +{ + networkConnectionFailed = true; +} + } // namespace net } // namespace ESPEasy diff --git a/src/ESPEasy/net/Globals/NWPlugins.cpp b/src/ESPEasy/net/Globals/NWPlugins.cpp index da640fb6e..649f1fa43 100644 --- a/src/ESPEasy/net/Globals/NWPlugins.cpp +++ b/src/ESPEasy/net/Globals/NWPlugins.cpp @@ -84,9 +84,31 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (Settings.getNWPluginID_for_network(x) && checkedEnabled) { if (Function == NWPlugin::Function::NWPLUGIN_INIT_ALL) { - Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelayAtBoot(x), x); - } else { + if (!Settings.getNetworkInterface_isFallback(x)) { + Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + } + } + else { event->NetworkIndex = x; + +#ifdef ESP32 + + if ((Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) && + Settings.getNetworkInterface_isFallback(x)) + { + NetworkInterface *currentDefaultInterface = Network.getDefaultInterface(); + + if (!currentDefaultInterface) { + // No current default interface, thus we need to start this one. + Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + } else { + if (currentDefaultInterface->getRoutePrio() > Settings.getRoutePrio_for_network(x)) { + // There is some network active with higher route priority, so this one is no longer needed + Scheduler.setNetworkExitTimer(10, x); + } + } + } +#endif // ifdef ESP32 String command; if (Function == NWPlugin::Function::NWPLUGIN_WRITE) { @@ -109,10 +131,11 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) } } #ifdef ESP32 + if (Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) { CheckRunningServices(); } -#endif +#endif // ifdef ESP32 return success; } @@ -221,7 +244,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (runtime_data && runtime_data->_connectedStats.isSet()) { const bool connected = runtime_data->_connectedStats.isOn(); - auto duration = connected + auto duration = connected ? runtime_data->_connectedStats.getLastOnDuration_ms() : runtime_data->_connectedStats.getLastOffDuration_ms(); @@ -230,9 +253,9 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) event->Par64_2 = runtime_data->_connectedStats.getCycleCount(); if (event->kvWriter) { - event->kvWriter->write({ - connected ? F("Connection Duration") : F("Disconnected Duration"), - format_msec_duration_HMS(duration) }); + event->kvWriter->write({ + connected ? F("Connection Duration") : F("Disconnected Duration"), + format_msec_duration_HMS(duration) }); if (!event->kvWriter->summaryValueOnly()) { event->kvWriter->write({ F("Number of Reconnects"), event->Par64_2 }); @@ -305,105 +328,118 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if (!success && NWPlugin::canQueryViaNetworkInterface(Function)) { String dummy_str; - if (do_NWPluginCall( + if (!do_NWPluginCall( networkDriverIndex, NWPlugin::Function::NWPLUGIN_GET_INTERFACE, event, dummy_str)) { - if (event->networkInterface != nullptr) { - switch (Function) - { - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO: -# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) + if (Function == NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO) { + if (Settings.getNetworkInterface_isFallback(networkIndex)) { + // Show route prio for fallback network interfaces as it is useful for determining which might be the fallback device. + event->Par1 = Settings.getRoutePrio_for_network(networkIndex); + event->Par3 = Settings.getNetworkInterfaceStartupDelay(networkIndex); - // TODO TD-er: Must also add option to set route prio - // See: https://github.com/espressif/arduino-esp32/pull/11617 - event->Par1 = event->networkInterface->getRoutePrio(); -# else // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) - event->Par1 = event->networkInterface->route_prio(); -# endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) - event->Par2 = event->networkInterface->isDefault(); - success = true; - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: - { - success = event->networkInterface->started(); - break; - } - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: - - if (event->kvWriter) { - event->kvWriter->write({ F("Name"), event->networkInterface->desc() }); - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: - - if (event->kvWriter) { - event->kvWriter->write({ F("Hostname"), event->networkInterface->getHostname() }); - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HW_ADDRESS: - - if (event->kvWriter) { - if (event->kvWriter->summaryValueOnly()) { - event->kvWriter->write({ - EMPTY_STRING, - concat(F("MAC: "), event->networkInterface->macAddress()) }); - } - else { - event->kvWriter->write({ - F("MAC"), - event->networkInterface->macAddress(), - KeyValueStruct::Format::PreFormatted - }); - } - success = true; - } - break; - - case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_IP: - { - if (event->kvWriter) { - PrintToString prstr; - success = NWPlugin::print_IP_address( - static_cast(event->Par1), - event->networkInterface, - prstr); - event->kvWriter->write({ F("ip"), prstr.getMove() }); - success = true; - } - break; - } - - case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: - { - IPAddress client_ip; - client_ip.fromString(str); - - if ((SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) && - !Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) { - success = NWPlugin::IP_in_subnet(client_ip, event->networkInterface); - } else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) { - const IPAddress low(SecuritySettings.AllowedIPrangeLow); - const IPAddress high(SecuritySettings.AllowedIPrangeHigh); - success = NWPlugin::ipInRange(client_ip, low, high) && - NWPlugin::IP_in_subnet(low, event->networkInterface) && - NWPlugin::IP_in_subnet(high, event->networkInterface); - } else { - success = true; - } - break; - } - - default: break; + success = true; } } + } else if (event->networkInterface != nullptr) { + switch (Function) + { + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO: +# if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) + + // TODO TD-er: Must also add option to set route prio + // See: https://github.com/espressif/arduino-esp32/pull/11617 + event->Par1 = event->networkInterface->getRoutePrio(); +# else // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + event->Par1 = event->networkInterface->route_prio(); +# endif // if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + event->Par2 = event->networkInterface->isDefault(); + + if (Settings.getNetworkInterface_isFallback(networkIndex)) { + event->Par3 = Settings.getNetworkInterfaceStartupDelay(networkIndex); + } + success = true; + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: + { + success = event->networkInterface->started(); + break; + } + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: + + if (event->kvWriter) { + event->kvWriter->write({ F("Name"), event->networkInterface->desc() }); + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: + + if (event->kvWriter) { + event->kvWriter->write({ F("Hostname"), event->networkInterface->getHostname() }); + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HW_ADDRESS: + + if (event->kvWriter) { + if (event->kvWriter->summaryValueOnly()) { + event->kvWriter->write({ + EMPTY_STRING, + concat(F("MAC: "), event->networkInterface->macAddress()) }); + } + else { + event->kvWriter->write({ + F("MAC"), + event->networkInterface->macAddress(), + KeyValueStruct::Format::PreFormatted + }); + } + success = true; + } + break; + + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_IP: + { + if (event->kvWriter) { + PrintToString prstr; + success = NWPlugin::print_IP_address( + static_cast(event->Par1), + event->networkInterface, + prstr); + event->kvWriter->write({ F("ip"), prstr.getMove() }); + success = true; + } + break; + } + + case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: + { + IPAddress client_ip; + client_ip.fromString(str); + + if ((SecuritySettings.IPblockLevel == LOCAL_SUBNET_ALLOWED) && + !Settings.getNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex)) { + success = NWPlugin::IP_in_subnet(client_ip, event->networkInterface); + } else if (SecuritySettings.IPblockLevel == ONLY_IP_RANGE_ALLOWED) { + const IPAddress low(SecuritySettings.AllowedIPrangeLow); + const IPAddress high(SecuritySettings.AllowedIPrangeHigh); + success = NWPlugin::ipInRange(client_ip, low, high) && + NWPlugin::IP_in_subnet(low, event->networkInterface) && + NWPlugin::IP_in_subnet(high, event->networkInterface); + } else { + success = true; + } + break; + } + + default: break; + } + } } #endif // ifdef ESP32 @@ -533,6 +569,7 @@ const NWPluginData_static_runtime* getDefaultRoute_NWPluginData_static_runtime() if (NW_data) { const int route_prio = NW_data->_routePrio; + if (route_prio > highest_prio) { index_highest_prio = i; highest_prio = route_prio; diff --git a/src/ESPEasy/net/Globals/NetworkState.cpp b/src/ESPEasy/net/Globals/NetworkState.cpp index 1be5bf28c..49b74538a 100644 --- a/src/ESPEasy/net/Globals/NetworkState.cpp +++ b/src/ESPEasy/net/Globals/NetworkState.cpp @@ -28,4 +28,4 @@ WiFiUDP portUDP; #ifdef ESP32 bool nonDefaultNetworkInterface_gotIP{}; #endif - +bool networkConnectionFailed{}; diff --git a/src/ESPEasy/net/Globals/NetworkState.h b/src/ESPEasy/net/Globals/NetworkState.h index 91af0e97a..df768b719 100644 --- a/src/ESPEasy/net/Globals/NetworkState.h +++ b/src/ESPEasy/net/Globals/NetworkState.h @@ -32,3 +32,4 @@ extern WiFiUDP portUDP; #ifdef ESP32 extern bool nonDefaultNetworkInterface_gotIP; #endif +extern bool networkConnectionFailed; diff --git a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp index 113bbbf0e..19001fc2a 100644 --- a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp +++ b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp @@ -135,7 +135,7 @@ String NWPlugin_import_export::exportConfig( child->write({ F("enabled"), Settings.getNetworkEnabled(networkIndex) }); child->write({ F("route_prio"), Settings.getRoutePrio_for_network(networkIndex) }); child->write({ F("sn_block"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkIndex) }); - child->write({ F("start_delay"), Settings.getNetworkInterfaceStartupDelayAtBoot(networkIndex) }); + child->write({ F("start_delay"), Settings.getNetworkInterfaceStartupDelay(networkIndex) }); # if FEATURE_USE_IPV6 child->write({ F("en_ipv6"), Settings.getNetworkEnabled_IPv6(networkIndex) }); diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp index d37e1e769..7d2018687 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp @@ -27,7 +27,7 @@ namespace net { namespace wifi { # ifdef ESP32 -static NWPluginData_static_runtime stats_and_cache(true, &NW_PLUGIN_INTERFACE); +static NWPluginData_static_runtime stats_and_cache(true, &NW_PLUGIN_INTERFACE, "AP"); # else static NWPluginData_static_runtime stats_and_cache(true, "AP"); // Cannot use flash strings during init of static objects # endif // ifdef ESP32 diff --git a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp index 229bae1d7..16f2bce8d 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW003_data_struct_ETH_RMII.cpp @@ -504,7 +504,9 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (!(data && iface)) { return false; } if (data->started() && data->connected()) { - return EthLinkUp(); + if (EthLinkUp()) return true; + data->mark_connect_failed(); + return false; } ethPrintSettings(); @@ -512,6 +514,7 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { { addLog(LOG_LEVEL_ERROR, F("ETH: Settings not correct!!!")); data->mark_stop(); + data->mark_connect_failed(); return false; } @@ -565,8 +568,11 @@ bool NW003_data_struct_ETH_RMII::ETHConnectRelaxed() { if (EthLinkUp()) { // We might miss the connected event, since we are already connected. data->mark_connected(); + } else { + data->mark_connect_failed(); } } else { + data->mark_connect_failed(); addLog(LOG_LEVEL_ERROR, F("ETH : Failed to initialize ETH")); } return success; diff --git a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp index 12db00423..fd7fc0c92 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -408,7 +408,9 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (!(data && iface)) { return false; } if (data->started() && data->connected()) { - return EthLinkUp(); + if (EthLinkUp()) return true; + data->mark_connect_failed(); + return false; } ethPrintSettings(); @@ -416,6 +418,7 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { { addLog(LOG_LEVEL_ERROR, F("ETH: Settings not correct!!!")); data->mark_stop(); + data->mark_connect_failed(); return false; } @@ -498,8 +501,11 @@ bool NW004_data_struct_ETH_SPI::ETHConnectRelaxed() { if (EthLinkUp()) { // We might miss the connected event, since we are already connected. data->mark_connected(); + } else { + data->mark_connect_failed(); } } else { + data->mark_connect_failed(); addLog(LOG_LEVEL_ERROR, F("ETH : Failed to initialize ETH")); } return success; diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp index 779789473..f1e1332ef 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp @@ -865,7 +865,7 @@ void NW005_begin_modem_task(void *parameter) { delay(100); } - while (timePassedSince(start) < 5000 && !NW_PLUGIN_INTERFACE.attached()); + while (timePassedSince(start) < 10000 && !NW_PLUGIN_INTERFACE.attached()); NW_PLUGIN_INTERFACE.mode(ESP_MODEM_MODE_CMUX); modem_task_data->AT_CPSI = NW_PLUGIN_INTERFACE.cmd(F("AT+CPSI?"), 3000); @@ -874,6 +874,9 @@ void NW005_begin_modem_task(void *parameter) NW_PLUGIN_INTERFACE.cmd(F("AT&D1"), 9000); digitalWrite(modem_task_data->dtrPin, HIGH); } + if (!NW_PLUGIN_INTERFACE.attached()) modem_task_data->modem_init_failed = true; + } else { + modem_task_data->modem_init_failed = true; } modem_task_data->modem_initialized = res; } @@ -1037,6 +1040,20 @@ bool NW005_data_struct_PPP_modem::handle_nwplugin_write(EventStruct *event, Stri return success; } +bool NW005_data_struct_PPP_modem::check_connect_failed() +{ + if (_modem_task_data.modem_init_failed) + { + _modem_task_data.modem_init_failed = false; + auto stats = getNWPluginData_static_runtime(); + if (stats) { + stats->mark_connect_failed(); + } + return true; + } + return false; +} + # if FEATURE_NETWORK_STATS bool NW005_data_struct_PPP_modem::initPluginStats() diff --git a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h index b0ab4e8fd..91b89a100 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.h @@ -18,6 +18,7 @@ struct NW005_modem_task_data { int baud_rate = 115200; bool initializing{}; bool modem_initialized{}; + bool modem_init_failed{}; int dtrPin = -1; String logString; String AT_CPSI; // Result from "AT+CPSI?" @@ -66,6 +67,8 @@ static int32_t getNextKey_noCredentials(int32_t key); bool handle_nwplugin_write(EventStruct *event, String & str) override; + bool check_connect_failed(); + String write_AT_cmd(const String& cmd, int timeout = 1000); diff --git a/src/ESPEasy/net/_NW001_WiFi_STA.cpp b/src/ESPEasy/net/_NW001_WiFi_STA.cpp index 98b6b7bb5..2fa3796eb 100644 --- a/src/ESPEasy/net/_NW001_WiFi_STA.cpp +++ b/src/ESPEasy/net/_NW001_WiFi_STA.cpp @@ -241,16 +241,17 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin break; } -# ifdef ESP8266 case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: { if (event->kvWriter) { - event->kvWriter->write({ F("Name"), F("sta") }); + event->kvWriter->write({ F("Name"), F("WiFi") }); success = true; } break; } +# ifdef ESP8266 + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: { if (event->kvWriter) { diff --git a/src/ESPEasy/net/_NW005_PPP_modem.cpp b/src/ESPEasy/net/_NW005_PPP_modem.cpp index 5265a76d7..4663c2531 100644 --- a/src/ESPEasy/net/_NW005_PPP_modem.cpp +++ b/src/ESPEasy/net/_NW005_PPP_modem.cpp @@ -229,6 +229,16 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_TEN_PER_SECOND: + { + ESPEasy::net::ppp::NW005_data_struct_PPP_modem *NW_data = + static_cast(getNWPluginData(event->NetworkIndex)); + + if (NW_data) { + NW_data->check_connect_failed(); + } + break; + } case NWPlugin::Function::NWPLUGIN_FIFTY_PER_SECOND: { diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index 847b3fb2e..77c6232bc 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -145,6 +145,7 @@ void ESPEasyWiFi_t::loop() // WiFiEventData.warnedNoValidWiFiSettings = true; } + wifi_STA_data->mark_connect_failed(); wifi_STA_data->_establishConnectStats.clear(); @@ -258,6 +259,7 @@ void ESPEasyWiFi_t::loop() if (_state == WiFiState_e::STA_Connecting) { setState(WiFiState_e::STA_Reconnecting, WIFI_STATE_MACHINE_STA_CONNECTING_TIMEOUT); } else { + wifi_STA_data->mark_connect_failed(); setState(WiFiState_e::WiFiOFF); } } diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 84623b2d2..3bf58d40c 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -463,6 +463,10 @@ public: void setNetworkEnabled(ESPEasy::net::networkIndex_t index, bool enabled); + bool getNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index) const; + + void setNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index, bool enabled); + bool getNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index) const; void setNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index, bool enabled); @@ -483,7 +487,7 @@ public: void setRoutePrio_for_network(ESPEasy::net::networkIndex_t index, uint8_t prio); #endif - uint32_t getNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index) const; + uint32_t getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const; void setNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index, uint32_t delay_ms); @@ -762,7 +766,7 @@ public: uint8_t NetworkEnabled_bits{}; uint8_t NetworkInterfaceSubnetBlockClientIP_bits{}; // Client IP Block Level. Allow from subnet of this interface uint8_t NetworkEnabled_ipv6_bits{}; // Whether or not to use IPv6 for the given interface (Settings.EnableIPv6() is the global on/off for IPv6) - uint8_t NetworkUnused_3{}; + uint8_t NetworkInterface_isFallback_bits{}; #ifdef ESP32 uint8_t NetworkRoutePrio[NETWORK_MAX] = {0}; #endif diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index 860b884ba..a67ca9a9a 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1541,6 +1541,22 @@ void SettingsStruct_tmpl::setNetworkEnabled(ESPEasy::net::networkIndex_ } } +template +bool SettingsStruct_tmpl::getNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index) const +{ + if (validNetworkIndex(index)) return bitRead(NetworkInterface_isFallback_bits, index); + return false; +} + +template +void SettingsStruct_tmpl::setNetworkInterface_isFallback(ESPEasy::net::networkIndex_t index, bool enabled) +{ + if (validNetworkIndex(index)) { + bitWrite(NetworkInterface_isFallback_bits, index, enabled); + } +} + + template bool SettingsStruct_tmpl::getNetworkInterfaceSubnetBlockClientIP(ESPEasy::net::networkIndex_t index) const { if (validNetworkIndex(index)) return bitRead(NetworkInterfaceSubnetBlockClientIP_bits, index); @@ -1591,7 +1607,7 @@ void SettingsStruct_tmpl::setRoutePrio_for_network(ESPEasy::net::networ #endif template -uint32_t SettingsStruct_tmpl::getNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index) const +uint32_t SettingsStruct_tmpl::getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const { if (validNetworkIndex(index)) { return static_cast(NetworkInterfaceStartupDelayAtBoot[index]) * 10ul; diff --git a/src/src/Helpers/Misc.cpp b/src/src/Helpers/Misc.cpp index a536c13a5..1d081705f 100644 --- a/src/src/Helpers/Misc.cpp +++ b/src/src/Helpers/Misc.cpp @@ -15,11 +15,12 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringParser.h" +#include "../../ESPEasy/net/_NWPlugin_Helper.h" + #if FEATURE_SD # include #endif // if FEATURE_SD - bool remoteConfig(struct EventStruct *event, const String& string) { // FIXME TD-er: Why have an event here as argument? It is not used. @@ -80,18 +81,25 @@ bool setNetworkEnableStatus(ESPEasy::net::networkIndex_t networkIndex, bool enab #endif // ifndef BUILD_NO_RAM_TRACKER // Only enable network if it has a network interface configured - if (Settings.getNWPluginID_for_network(networkIndex) != ESPEasy::net::INVALID_NW_PLUGIN_ID || !enabled) { + if ((Settings.getNWPluginID_for_network(networkIndex) != ESPEasy::net::INVALID_NW_PLUGIN_ID) || !enabled) { struct EventStruct TempEvent; TempEvent.NetworkIndex = networkIndex; String dummy; if (!enabled) { - ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_EXIT, &TempEvent, dummy); + // Use the scheduler as this also removes any pending init calls. + Scheduler.setNetworkExitTimer(10, networkIndex); } Settings.setNetworkEnabled(networkIndex, enabled); + if (enabled) { - if (!ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_INIT, &TempEvent, dummy)) - return false; + if (ESPEasy::net::getNWPluginData(networkIndex) == nullptr) { + // Only init when not yet started + + if (!ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_INIT, &TempEvent, dummy)) { + return false; + } + } } return true; @@ -99,7 +107,6 @@ bool setNetworkEnableStatus(ESPEasy::net::networkIndex_t networkIndex, bool enab return false; } - /********************************************************************************************\ Toggle controller enabled state \*********************************************************************************************/ @@ -115,6 +122,7 @@ bool setControllerEnableStatus(controllerIndex_t controllerIndex, bool enabled) struct EventStruct TempEvent; TempEvent.ControllerIndex = controllerIndex; String dummy; + if (!enabled) { CPluginCall(CPlugin::Function::CPLUGIN_EXIT, &TempEvent, dummy); } @@ -217,6 +225,7 @@ void taskClear(taskIndex_t taskIndex, bool save) it is excluded from the calculation ! \*********************************************************************************************/ #if defined(ARDUINO_ESP8266_RELEASE_2_3_0) + void dump(uint32_t addr) { // Seems already included in core 2.4 ... serialPrint(String(addr, HEX)); serialPrint(": "); @@ -275,9 +284,7 @@ void dump(uint32_t addr) { // Seems already included in core 2.4 ... /********************************************************************************************\ Handler for keeping ExtraTaskSettings up to date using cache \*********************************************************************************************/ -String getTaskDeviceName(taskIndex_t TaskIndex) { - return Cache.getTaskDeviceName(TaskIndex); -} +String getTaskDeviceName(taskIndex_t TaskIndex) { return Cache.getTaskDeviceName(TaskIndex); } /********************************************************************************************\ Handler for getting Value Names from TaskIndex @@ -578,22 +585,16 @@ 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(); -} +float getCPUload() { return 100.0f - Scheduler.getIdleTimePct(); } -int getLoopCountPerSec() { - return loopCounterLast / 30; -} +int getLoopCountPerSec() { return loopCounterLast / 30; } -int getUptimeMinutes() { - return wdcounter / 2; -} +int getUptimeMinutes() { return wdcounter / 2; } /****************************************************************************** * scan an int array of specified size for a value *****************************************************************************/ -bool intArrayContains(const int arraySize, const int array[], const int& value) { +bool intArrayContains(const int arraySize, const int array[], const int& value) { for (int i = 0; i < arraySize; i++) { if (array[i] == value) { return true; } } @@ -608,6 +609,7 @@ bool intArrayContains(const int arraySize, const uint8_t array[], const uint8_t& } #ifndef BUILD_NO_RAM_TRACKER + void logMemUsageAfter(const __FlashStringHelper *function, int value) { // Store free memory in an int, as subtracting may sometimes result in negative value. // The recorded used memory is not an exact value, as background (or interrupt) tasks may also allocate or free heap memory. @@ -625,11 +627,13 @@ void logMemUsageAfter(const __FlashStringHelper *function, int value) { log += value; } - while (log.length() < 30) { log += ' '; } + while (log.length() < 30) { log += ' '; + } log += F("Free mem after: "); log += freemem_end; - while (log.length() < 55) { log += ' '; } + while (log.length() < 55) { log += ' '; + } log += F("diff: "); log += last_freemem - freemem_end; addLogMove(LOG_LEVEL_DEBUG, log); diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index bfdf6b64d..66e9c3a61 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -62,8 +62,13 @@ void run50TimesPerSecond() { #ifdef ESP32 static const NetworkInterface *lastDefaultInterface = nullptr; NetworkInterface * currentDefaultInterface = Network.getDefaultInterface(); - if (nonDefaultNetworkInterface_gotIP || lastDefaultInterface != currentDefaultInterface) { + if (nonDefaultNetworkInterface_gotIP || + networkConnectionFailed || + lastDefaultInterface != currentDefaultInterface) { nonDefaultNetworkInterface_gotIP = false; + + // TODO TD-er: Must do something else here on failed connect? + networkConnectionFailed = false; ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED, 0, dummy); lastDefaultInterface = currentDefaultInterface; } diff --git a/src/src/Helpers/Scheduler_NetworkTimer.cpp b/src/src/Helpers/Scheduler_NetworkTimer.cpp index 7bb085edf..d4b705e07 100644 --- a/src/src/Helpers/Scheduler_NetworkTimer.cpp +++ b/src/src/Helpers/Scheduler_NetworkTimer.cpp @@ -4,6 +4,8 @@ #include "../Globals/Settings.h" +#include "../../ESPEasy/net/_NWPlugin_Helper.h" + /*********************************************************************************************\ * Network Adapter Timer (NWPLUGIN_TASKTIMER_IN) * Can be scheduled per combo networkIndex & Par1 (20 least significant bits) @@ -11,12 +13,22 @@ void ESPEasy_Scheduler::setNetworkInitTimer(unsigned long msecFromNow, ESPEasy::net::networkIndex_t networkIndex) { - setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_INIT); + if (ESPEasy::net::getNWPluginData(networkIndex) == nullptr) { + // Only init when not yet started + setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_INIT); + } } void ESPEasy_Scheduler::setNetworkExitTimer(unsigned long msecFromNow, ESPEasy::net::networkIndex_t networkIndex) { + // First check if there are any pending INIT timers + const NWPluginTimerID timerID(networkIndex, 0, NWPlugin::Function::NWPLUGIN_INIT); + auto it = systemTimers.find(timerID.mixed_id); + if (it != systemTimers.end()) { + systemTimers.erase(it); + } + setNetworkTimer(msecFromNow, networkIndex, NWPlugin::Function::NWPLUGIN_EXIT); } @@ -35,11 +47,14 @@ void ESPEasy_Scheduler::setNetworkTimer(unsigned long msecFromNow const NWPluginTimerID timerID(networkIndex, Par1, function); - systemTimerStruct timer_data; + if (systemTimers.find(timerID.mixed_id) == systemTimers.end()) { + // Only set timer if not already set. + systemTimerStruct timer_data; - timer_data.fromEvent(networkIndex, Par1, Par2, Par3, Par4, Par5); - systemTimers[timerID.mixed_id] = timer_data; - setNewTimerAt(timerID, millis() + msecFromNow); + timer_data.fromEvent(networkIndex, Par1, Par2, Par3, Par4, Par5); + systemTimers[timerID.mixed_id] = timer_data; + setNewTimerAt(timerID, millis() + msecFromNow); + } } void ESPEasy_Scheduler::process_network_timer(SchedulerTimerID id) diff --git a/src/src/WebServer/NetworkPage.cpp b/src/src/WebServer/NetworkPage.cpp index 4a7ec01c6..0adab2569 100644 --- a/src/src/WebServer/NetworkPage.cpp +++ b/src/src/WebServer/NetworkPage.cpp @@ -160,6 +160,7 @@ void handle_networks_CopySubmittedSettings_NWPluginCall(ESPEasy::net::networkInd # endif # ifdef ESP32 Settings.setRoutePrio_for_network(networkindex, getFormItemInt(F("routeprio"), 0)); + Settings.setNetworkInterface_isFallback(networkindex, isFormItemChecked(F("fallback"))); Settings.setNetworkInterfaceSubnetBlockClientIP(networkindex, isFormItemChecked(F("block_web_access"))); # endif // ifdef ESP32 # if FEATURE_USE_IPV6 @@ -258,6 +259,9 @@ void handle_networks_ShowAllNetworksTable() if (TempEvent.Par2) { addHtml(F("(*)")); } +// if (TempEvent.Par3 > 0) { +// addHtml(strformat(F("
      %u ms"), TempEvent.Par3)); +// } } break; # endif // ifdef ESP32 @@ -376,10 +380,12 @@ void handle_networks_NetworkSettingsPage(ESPEasy::net::networkIndex_t networkind 0, 255); addFormNote(F("The active interface with highest priority will be used for default route (gateway).")); # endif // ifdef ESP32 - addFormCheckBox(F("Block Web Access"), F("block_web_access"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkindex)); - addFormNote(F("When checked, any host from a subnet on this network interface will be blocked")); - addFormNumericBox(F("Delay Startup At Boot"), F("delay_start"), Settings.getNetworkInterfaceStartupDelayAtBoot(networkindex), 0, 60000); + addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); + addFormNumericBox(F("Delay Startup"), F("delay_start"), Settings.getNetworkInterfaceStartupDelay(networkindex), 0, 60000); addUnit(F("ms")); + addFormNote(F("For fallback interface, it is the delay after connection of primary interface has failed. For non-fallback it is the delay from boot")); + addFormCheckBox(F("Block Web Access"), F("block_web_access"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkindex)); + addFormNote(F("When checked, any host from a subnet on this network interface will be blocked to access the ESPEasy web interface")); # if FEATURE_USE_IPV6 addFormCheckBox(F("Enable IPv6"), F("en_ipv6"), Settings.getNetworkEnabled_IPv6(networkindex)); From 8d0aaaee0284eb5722a132b0b3ba5f9fd41905a2 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 17 Feb 2026 19:28:27 +0100 Subject: [PATCH 101/161] [Network] Fix failing build on ESP8266 --- .../net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp | 1 + src/src/WebServer/NetworkPage.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp index d92fb8c86..31567f523 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_static_runtime_ESP8266.cpp @@ -3,6 +3,7 @@ #ifdef ESP8266 # include "../../../src/Helpers/StringConverter.h" +# include "../Globals/NetworkState.h" namespace ESPEasy { namespace net { diff --git a/src/src/WebServer/NetworkPage.cpp b/src/src/WebServer/NetworkPage.cpp index 0adab2569..ce875d1f9 100644 --- a/src/src/WebServer/NetworkPage.cpp +++ b/src/src/WebServer/NetworkPage.cpp @@ -379,11 +379,13 @@ void handle_networks_NetworkSettingsPage(ESPEasy::net::networkIndex_t networkind Settings.getRoutePrio_for_network(networkindex), 0, 255); addFormNote(F("The active interface with highest priority will be used for default route (gateway).")); -# endif // ifdef ESP32 addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); +# endif // ifdef ESP32 addFormNumericBox(F("Delay Startup"), F("delay_start"), Settings.getNetworkInterfaceStartupDelay(networkindex), 0, 60000); addUnit(F("ms")); +#ifdef ESP32 addFormNote(F("For fallback interface, it is the delay after connection of primary interface has failed. For non-fallback it is the delay from boot")); +#endif addFormCheckBox(F("Block Web Access"), F("block_web_access"), Settings.getNetworkInterfaceSubnetBlockClientIP(networkindex)); addFormNote(F("When checked, any host from a subnet on this network interface will be blocked to access the ESPEasy web interface")); From 06424d030098bdde39e6ae9d96b395cae273068f Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 17 Feb 2026 19:28:57 +0100 Subject: [PATCH 102/161] [Network] Start documenting Fallback Network Interface --- docs/source/Network/_Network.rst | 89 +++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/docs/source/Network/_Network.rst b/docs/source/Network/_Network.rst index 807f8d1b7..7749fa1a3 100644 --- a/docs/source/Network/_Network.rst +++ b/docs/source/Network/_Network.rst @@ -3,7 +3,7 @@ Network ******* -A Network plugin is compable with a 'driver' for a network adapter. +A Network plugin is comparable with a 'driver' for a network adapter. The first 2 entries will be the same for every ESPEasy setup and these cannot be removed. @@ -11,12 +11,22 @@ The first 2 entries will be the same for every ESPEasy setup and these cannot be - Wi-Fi AP: To let the ESP board act as an access point. +.. note:: + The network code of ESPEasy has been rewritten in 2025/2026. + Older builds of ESPEasy do have a different organisation of network related parameters. + Most of these were accessible via the ``Tools->Advanced`` page. + + .. _Network Plugins: Network Plugins ================== +.. note:: + ESP32 builds do have a lot more networking capabilities compared to ESP8266. + For ESP8266 we only have support for WiFi and no other network interfaces will be added for ESP8266. + .. csv-table:: :header: "Plugin name", "Plugin status", "Plugin number" :widths: 10, 8, 5 @@ -28,4 +38,79 @@ Network Plugins ":ref:`NW005_page`","|NW005_status|","NW005" Network Parameters -===================== +================== + +Route Priority +-------------- + +(ESP32 only) + +When using multiple network interfaces, like WiFi, Ethernet or PPP LTE Modem, it must be made clear which interface should be used for new connections initiated from ESPEasy to some other host. + +The connected network interface with the highest Route Priority is considered to be the default route. + +Default Route Priority values are: + +* WiFi STA = 100 +* Ethernet = 50 +* PPP (LTE modem) = 20 +* WiFi AP = 10 + + +Fallback Interface +------------------ + +(ESP32 only) + +.. note:: + The concept of a Fallback Interface is available on ESP8266, but only for WiFi AP. + This is a special case, which is discussed here: :ref:`NW002_page` . + +A network interface can be marked as "Fallback Interface". + +A fallback interface will only be scheduled to start when: + +* A non-fallback interface failed to connect. +* Route Priority changed to a value which is less than the set priority for the fallback interface. + +The scheduled delay to start the fallback interface is set as "Delay Startup" (see below). + +A fallback interface will be stopped when there is a default route with a route priority higher than the set Route Priority of the fallback interface. + +It is possible to set multiple interfaces as Fallback Interface. +The fallback order can be tweaked using the Delay Startup and Route Priority. + +N.B. Network interfaces which should be started at boot, should not be marked as Fallback Interface. + +Delay Startup +------------- + +For various reasons, it can be useful to not immediately start a network interface at boot. +For example to reduce the power consumption as most network interfaces may draw significant more power for a short time when starting. + +Another use case can be to check some sensor value before deciding to either start the network interface or enter deep sleep again. + +The set value (in msec) is the delay from boot before starting the network interface. + +Delay Startup for Fallback Interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A Fallback Interface is not started at boot. +The set Delay Startup is then used as delay to schedule starting the network interface. +See "Fallback Interface" for more information. + + +Block Web Access +---------------- + +When checked, the ESPEasy web interface cannot be accessed via the IP-range of the network interface. + +.. note:: + The PPP Network Interface (ESP32-only) will have this checked by default, since you typically can't access devices from the network of the mobile provider. + If this is possible for some odd reason, you very likely would never want to allow this. + +Enable IPv6 +----------- + +(ESP32 only) + From 8acd778259eb333c36374df3e60228223c677928 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 20 Feb 2026 11:12:40 +0100 Subject: [PATCH 103/161] [WiFi] Make WiFi AP act as fallback device when set as such. --- docs/source/Network/_Network.rst | 1 + .../net/DataStructs/NWPluginData_base.cpp | 11 +++++++ .../net/DataStructs/NWPluginData_base.h | 4 +++ src/ESPEasy/net/Globals/NWPlugins.cpp | 33 +++++++++++++++++-- .../net/Helpers/NWPlugin_import_export.cpp | 2 +- .../NW002_data_struct_WiFi_AP.cpp | 4 +-- src/ESPEasy/net/_NW001_WiFi_STA.cpp | 8 ++++- src/ESPEasy/net/_NW002_WiFi_AP.cpp | 20 +++++++---- src/ESPEasy/net/_NW003_ETH_RMII.cpp | 8 ++++- src/ESPEasy/net/_NW004_ETH_SPI.cpp | 8 ++++- src/ESPEasy/net/_NW005_PPP_modem.cpp | 8 ++++- .../net/wifi/ESPEasyWiFi_state_machine.cpp | 1 + .../net/wifi/ESPEasyWiFi_state_machine.h | 4 +++ src/ESPEasy/net/wifi/ESPEasyWifi.cpp | 4 +++ src/ESPEasy/net/wifi/ESPEasyWifi.h | 2 ++ src/src/DataStructs/SettingsStruct.h | 9 ++--- src/src/DataStructs_templ/SettingsStruct.cpp | 6 ++-- src/src/DataTypes/ESPEasy_plugin_functions.h | 1 + src/src/Helpers/StringProvider.cpp | 8 ----- src/src/Helpers/StringProvider.h | 1 - src/src/WebServer/NetworkPage.cpp | 15 ++++++++- 21 files changed, 122 insertions(+), 36 deletions(-) diff --git a/docs/source/Network/_Network.rst b/docs/source/Network/_Network.rst index 7749fa1a3..28f9e047e 100644 --- a/docs/source/Network/_Network.rst +++ b/docs/source/Network/_Network.rst @@ -114,3 +114,4 @@ Enable IPv6 (ESP32 only) +Checking this checkbox, will allow the network device to use IPv6. diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp index 14074c7d2..0646aff49 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.cpp @@ -59,6 +59,17 @@ NWPluginData_base::~NWPluginData_base() #endif // if FEATURE_STORE_NETWORK_INTERFACE_SETTINGS } +#ifdef ESP32 +bool NWPluginData_base::isDefaultRoute() const { + if (_netif) { + return _netif->isDefault(); + } + return false; +} + +#endif + + bool NWPluginData_base::hasPluginStats() const { #if FEATURE_NETWORK_STATS diff --git a/src/ESPEasy/net/DataStructs/NWPluginData_base.h b/src/ESPEasy/net/DataStructs/NWPluginData_base.h index 8ea8a01db..dad8dfa36 100644 --- a/src/ESPEasy/net/DataStructs/NWPluginData_base.h +++ b/src/ESPEasy/net/DataStructs/NWPluginData_base.h @@ -39,6 +39,10 @@ struct NWPluginData_base { virtual bool exit(EventStruct *event) = 0; +#ifdef ESP32 + virtual bool isDefaultRoute() const; +#endif + bool hasPluginStats() const; bool hasPeaks() const; diff --git a/src/ESPEasy/net/Globals/NWPlugins.cpp b/src/ESPEasy/net/Globals/NWPlugins.cpp index 649f1fa43..ee5c7aa4f 100644 --- a/src/ESPEasy/net/Globals/NWPlugins.cpp +++ b/src/ESPEasy/net/Globals/NWPlugins.cpp @@ -88,7 +88,12 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); } } - else { + + if (Function == NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN && + Settings.getNetworkInterfaceSubnetBlockClientIP(x)) + { + // Skip check for this network interface as access to the web UI should be blocked anyway + } else { event->NetworkIndex = x; #ifdef ESP32 @@ -96,15 +101,36 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) if ((Function == NWPlugin::Function::NWPLUGIN_PRIORITY_ROUTE_CHANGED) && Settings.getNetworkInterface_isFallback(x)) { + // Deal with the fallback interface(s) + // TODO TD-er: What to do with the WiFi-AP as that's a special case of a fallback interface. NetworkInterface *currentDefaultInterface = Network.getDefaultInterface(); if (!currentDefaultInterface) { - // No current default interface, thus we need to start this one. - Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + String dummy; + if (do_NWPluginCall( + getNetworkDriverIndex_from_NetworkIndex(x), + NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START, + event, + dummy)) + { + // No current default interface, thus we need to start this one. + Scheduler.setNetworkInitTimer(Settings.getNetworkInterfaceStartupDelay(x), x); + } } else { if (currentDefaultInterface->getRoutePrio() > Settings.getRoutePrio_for_network(x)) { // There is some network active with higher route priority, so this one is no longer needed Scheduler.setNetworkExitTimer(10, x); + } else { + // Check for the special case where there are multiple fallback interfaces with the same route prio. + // Then only stop the fallback interfaces which are not the default route. + auto *NW_data = ESPEasy::net::getNWPluginData(x); + + if (NW_data) { + if (!NW_data->isDefaultRoute() && + (currentDefaultInterface->getRoutePrio() == Settings.getRoutePrio_for_network(x))) { + Scheduler.setNetworkExitTimer(10, x); + } + } } } } @@ -289,6 +315,7 @@ bool NWPluginCall(NWPlugin::Function Function, EventStruct *event, String& str) case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_HOSTNAME: case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_NAME: case NWPlugin::Function::NWPLUGIN_CLIENT_IP_WEB_ACCESS_ALLOWED: + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: #ifdef ESP32 case NWPlugin::Function::NWPLUGIN_GET_INTERFACE: diff --git a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp index 19001fc2a..b533b9221 100644 --- a/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp +++ b/src/ESPEasy/net/Helpers/NWPlugin_import_export.cpp @@ -223,7 +223,7 @@ String NWPlugin_import_export::importConfig( break; case 2: Settings.setNetworkInterfaceSubnetBlockClientIP(networkIndex, bool_val); break; - case 3: Settings.setNetworkInterfaceStartupDelayAtBoot(networkIndex, value.toInt()); + case 3: Settings.setNetworkInterfaceStartupDelay(networkIndex, value.toInt()); break; # if FEATURE_USE_IPV6 case 4: Settings.setNetworkEnabled_IPv6(networkIndex, bool_val); diff --git a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp index 7d2018687..2dce0c493 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW002_data_struct_WiFi_AP.cpp @@ -93,9 +93,7 @@ bool NW002_data_struct_WiFi_AP::init(EventStruct *event) nw002_enable_NAPT = Settings.WiFi_AP_enable_NAPT(); # endif - if (Settings.StartAP_on_NW002_init()) { - ESPEasy::net::wifi::setAPinternal(true); - } + ESPEasy::net::wifi::setAPinternal(true); # ifdef ESP32 NW002_update_NAPT(); # endif diff --git a/src/ESPEasy/net/_NW001_WiFi_STA.cpp b/src/ESPEasy/net/_NW001_WiFi_STA.cpp index 2fa3796eb..04ee55543 100644 --- a/src/ESPEasy/net/_NW001_WiFi_STA.cpp +++ b/src/ESPEasy/net/_NW001_WiFi_STA.cpp @@ -78,7 +78,7 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin Settings.setRoutePrio_for_network(event->NetworkIndex, 100); # endif // ifdef ESP32 Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 1000); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 1000); Settings.ConnectFailRetryCount = 1; break; @@ -108,6 +108,12 @@ bool NWPlugin_001(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: { success = ESPEasy::net::wifi::WifiIsSTA(WiFi.getMode()); diff --git a/src/ESPEasy/net/_NW002_WiFi_AP.cpp b/src/ESPEasy/net/_NW002_WiFi_AP.cpp index ce88fb3cd..72993df91 100644 --- a/src/ESPEasy/net/_NW002_WiFi_AP.cpp +++ b/src/ESPEasy/net/_NW002_WiFi_AP.cpp @@ -62,8 +62,8 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin Settings.setRoutePrio_for_network(event->NetworkIndex, 10); # endif Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 10000); - Settings.StartAP_on_NW002_init(false); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 10000); + Settings.setNetworkInterface_isFallback(event->NetworkIndex, true); Settings.StartAPfallback_NoCredentials(true); Settings.DoNotStartAPfallback_ConnectFail(false); Settings.APfallback_autostart_max_uptime_m(0); @@ -88,11 +88,22 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin case NWPlugin::Function::NWPLUGIN_WEBSERVER_SHOULD_RUN: { - success = ESPEasy::net::wifi::wifiAPmodeActivelyUsed(); + success = ESPEasy::net::wifi::WifiIsAP(WiFi.getMode()); +// success = ESPEasy::net::wifi::wifiAPmodeActivelyUsed(); break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + if (Settings.getNetworkInterface_isFallback(event->NetworkIndex)){ + success = ESPEasy::net::wifi::shouldStartAP_fallback(); + } else { + success = true; + } + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_ACTIVE: { success = ESPEasy::net::wifi::WifiIsAP(WiFi.getMode()); @@ -246,7 +257,6 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin // Usually the AP will be started when no WiFi is defined, or the defined one cannot be found. This flag may prevent it. Settings.StartAPfallback_NoCredentials(isFormItemChecked(LabelType::WIFI_START_AP_NO_CREDENTIALS)); Settings.DoNotStartAPfallback_ConnectFail(!isFormItemChecked(LabelType::WIFI_START_AP_ON_CONNECT_FAIL)); - Settings.StartAP_on_NW002_init(isFormItemChecked(LabelType::WIFI_START_AP_ON_NW002_INIT)); Settings.APfallback_autostart_max_uptime_m(getFormItemInt(LabelType::WIFI_MAX_UPTIME_AUTO_START_AP)); Settings.APfallback_minimal_on_time_sec(getFormItemInt(LabelType::WIFI_AP_MINIMAL_ON_TIME)); @@ -260,8 +270,6 @@ bool NWPlugin_002(NWPlugin::Function function, EventStruct *event, String& strin case NWPlugin::Function::NWPLUGIN_WEBFORM_LOAD: { - addFormCheckBox(LabelType::WIFI_START_AP_ON_NW002_INIT); - addFormSubHeader(F("Wifi AP Settings")); addFormPasswordBox(F("WPA AP Mode Key"), F("apkey"), SecuritySettings.WifiAPKey, 63); addFormNote(F("WPA Key must be at least 8 characters long")); diff --git a/src/ESPEasy/net/_NW003_ETH_RMII.cpp b/src/ESPEasy/net/_NW003_ETH_RMII.cpp index 73aa70457..303c4f16f 100644 --- a/src/ESPEasy/net/_NW003_ETH_RMII.cpp +++ b/src/ESPEasy/net/_NW003_ETH_RMII.cpp @@ -60,7 +60,7 @@ bool NWPlugin_003(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 50); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); ESPEasy_key_value_store kvs; ESPEasy::net::eth::NW003_data_struct_ETH_RMII::loadDefaults( @@ -96,6 +96,12 @@ bool NWPlugin_003(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { auto iface = ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getInterface(event->NetworkIndex); diff --git a/src/ESPEasy/net/_NW004_ETH_SPI.cpp b/src/ESPEasy/net/_NW004_ETH_SPI.cpp index 7b6d6dfb0..8059ae97e 100644 --- a/src/ESPEasy/net/_NW004_ETH_SPI.cpp +++ b/src/ESPEasy/net/_NW004_ETH_SPI.cpp @@ -55,7 +55,7 @@ bool NWPlugin_004(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 50); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, false); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); ESPEasy_key_value_store kvs; ESPEasy::net::eth::NW004_data_struct_ETH_SPI::loadDefaults( @@ -91,6 +91,12 @@ bool NWPlugin_004(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { auto iface = ESPEasy::net::eth::ETH_NWPluginData_static_runtime::getInterface(event->NetworkIndex); diff --git a/src/ESPEasy/net/_NW005_PPP_modem.cpp b/src/ESPEasy/net/_NW005_PPP_modem.cpp index 4663c2531..046313611 100644 --- a/src/ESPEasy/net/_NW005_PPP_modem.cpp +++ b/src/ESPEasy/net/_NW005_PPP_modem.cpp @@ -55,7 +55,7 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin { Settings.setRoutePrio_for_network(event->NetworkIndex, 20); Settings.setNetworkInterfaceSubnetBlockClientIP(event->NetworkIndex, true); - Settings.setNetworkInterfaceStartupDelayAtBoot(event->NetworkIndex, 500 * event->NetworkIndex); + Settings.setNetworkInterfaceStartupDelay(event->NetworkIndex, 500 * event->NetworkIndex); break; } @@ -84,6 +84,12 @@ bool NWPlugin_005(NWPlugin::Function function, EventStruct *event, String& strin break; } + case NWPlugin::Function::NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START: + { + success = true; + break; + } + case NWPlugin::Function::NWPLUGIN_WEBFORM_SHOW_CONNECTED: { if (event->kvWriter) { diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp index 77c6232bc..ad426e55c 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.cpp @@ -186,6 +186,7 @@ void ESPEasyWiFi_t::loop() if (WiFi_AP_Candidates.hasCandidates()) { setState(WiFiState_e::WiFiOFF, 100); } else { + // FIXME TD-er: This might not be a responsibility of this state machine.... if (shouldStartAP_fallback()) { setState(WiFiState_e::AP_Fallback, Settings.APfallback_minimal_on_time_sec() * 1000); // TODO TD-er: Must keep track of whether the user has forced AP to be autostarted. diff --git a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h index 4f47ef814..f54090c1f 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h +++ b/src/ESPEasy/net/wifi/ESPEasyWiFi_state_machine.h @@ -73,8 +73,12 @@ private: STA_connected_state getSTA_connected_state() const; +public: + bool shouldStartAP_fallback() const; +private: + // WiFi_AP_Candidate _active_sta; // WiFi_AP_Candidate _AP_conf; diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp index c9a2c8a04..36a2e85a0 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.cpp +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.cpp @@ -171,6 +171,10 @@ void exitWiFi() { ESPEasyWiFi.disable(); } void loopWiFi() { ESPEasyWiFi.loop(); } +bool shouldStartAP_fallback() { + return ESPEasyWiFi.shouldStartAP_fallback(); +} + # ifdef BOARD_HAS_SDIO_ESP_HOSTED // ******************************************************************************** diff --git a/src/ESPEasy/net/wifi/ESPEasyWifi.h b/src/ESPEasy/net/wifi/ESPEasyWifi.h index def0dfb99..6f7eb1511 100644 --- a/src/ESPEasy/net/wifi/ESPEasyWifi.h +++ b/src/ESPEasy/net/wifi/ESPEasyWifi.h @@ -51,6 +51,8 @@ void initWiFi(); void exitWiFi(); void loopWiFi(); +bool shouldStartAP_fallback(); + # ifdef BOARD_HAS_SDIO_ESP_HOSTED // ******************************************************************************** diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 3bf58d40c..d16376482 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -260,9 +260,6 @@ class SettingsStruct_tmpl inline bool StartAPfallback_NoCredentials() const { return !VariousBits_2.StartAPfallback_NoCredentials; } inline void StartAPfallback_NoCredentials(bool value) { VariousBits_2.StartAPfallback_NoCredentials = !value; } - inline bool StartAP_on_NW002_init() const { return VariousBits_2.StartAP_on_NW002_init; } - inline void StartAP_on_NW002_init(bool value) { VariousBits_2.StartAP_on_NW002_init = value; } - inline bool DoNotStartAPfallback_ConnectFail() const { return VariousBits_1.DoNotStartAPfallback_ConnectFail; } inline void DoNotStartAPfallback_ConnectFail(bool value) { VariousBits_1.DoNotStartAPfallback_ConnectFail = value; } @@ -489,7 +486,7 @@ public: uint32_t getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const; - void setNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index, uint32_t delay_ms); + void setNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index, uint32_t delay_ms); uint32_t PID = 0; int Version = 0; @@ -745,7 +742,7 @@ public: uint32_t MQTTConnectInBackground : 1; // Bit 13 // inverted uint32_t StartAPfallback_NoCredentials : 1; // Bit 14 // inverted - uint32_t StartAP_on_NW002_init : 1; // Bit 15 + uint32_t Unused_bit15 : 1; // Bit 15 uint32_t APfallback_minimal_on_time_sec : 8; // Bit 16 - 23 uint32_t APfallback_autostart_max_uptime_m : 8; // Bit 23 - 31 '0' == disabled }; @@ -771,7 +768,7 @@ public: uint8_t NetworkRoutePrio[NETWORK_MAX] = {0}; #endif // TODO TD-er: For ESP8266 we may likely ever use upto 2 or 3 network interfaces, so maybe re-use the rest later? - uint16_t NetworkInterfaceStartupDelayAtBoot[NETWORK_MAX]{}; + uint16_t NetworkInterfaceStartupDelay[NETWORK_MAX]{}; // Try to extend settings to make the checksum 4-uint8_t aligned. diff --git a/src/src/DataStructs_templ/SettingsStruct.cpp b/src/src/DataStructs_templ/SettingsStruct.cpp index a67ca9a9a..c38957641 100644 --- a/src/src/DataStructs_templ/SettingsStruct.cpp +++ b/src/src/DataStructs_templ/SettingsStruct.cpp @@ -1610,16 +1610,16 @@ template uint32_t SettingsStruct_tmpl::getNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index) const { if (validNetworkIndex(index)) { - return static_cast(NetworkInterfaceStartupDelayAtBoot[index]) * 10ul; + return static_cast(NetworkInterfaceStartupDelay[index]) * 10ul; } return 0; } template -void SettingsStruct_tmpl::setNetworkInterfaceStartupDelayAtBoot(ESPEasy::net::networkIndex_t index, uint32_t delay_ms) +void SettingsStruct_tmpl::setNetworkInterfaceStartupDelay(ESPEasy::net::networkIndex_t index, uint32_t delay_ms) { if (validNetworkIndex(index)) { - NetworkInterfaceStartupDelayAtBoot[index] = delay_ms/10ul; + NetworkInterfaceStartupDelay[index] = delay_ms/10ul; } } diff --git a/src/src/DataTypes/ESPEasy_plugin_functions.h b/src/src/DataTypes/ESPEasy_plugin_functions.h index 224a02034..f275b2056 100644 --- a/src/src/DataTypes/ESPEasy_plugin_functions.h +++ b/src/src/DataTypes/ESPEasy_plugin_functions.h @@ -176,6 +176,7 @@ public: NWPLUGIN_EXIT, NWPLUGIN_WRITE, // Send commands to a network adapter. NWPLUGIN_CREDENTIALS_CHANGED, + NWPLUGIN_FALLBACK_INTERFACE_SHOULD_START, #ifdef ESP32 NWPLUGIN_GET_INTERFACE, NWPLUGIN_WEBFORM_SHOW_ROUTE_PRIO, diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 65a5c02c3..243fa521f 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -220,10 +220,6 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) { return KeyValueStruct(F("Start AP on Connect Fail"), !Settings.DoNotStartAPfallback_ConnectFail()); } - case LabelType::WIFI_START_AP_ON_NW002_INIT: - { - return KeyValueStruct(F("Auto Start AP"), Settings.StartAP_on_NW002_init()); - } case LabelType::WIFI_NR_RECONNECT_ATTEMPTS: { return KeyValueStruct(F("Connect Retry Attempts"), Settings.ConnectFailRetryCount); @@ -1382,10 +1378,6 @@ String getFormNote(LabelType::Enum label) break; #endif - case LabelType::WIFI_START_AP_ON_NW002_INIT: - flash_str = F("Always start AP mode when this network interface is enabled"); - break; - #endif // ifndef MINIMAL_OTA #if FEATURE_SET_WIFI_TX_PWR diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 6f89c5199..1594f63c9 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -36,7 +36,6 @@ struct LabelType { WIFI_ENABLE_CAPTIVE_PORTAL, WIFI_START_AP_NO_CREDENTIALS, WIFI_START_AP_ON_CONNECT_FAIL, - WIFI_START_AP_ON_NW002_INIT, WIFI_NR_RECONNECT_ATTEMPTS, WIFI_MAX_UPTIME_AUTO_START_AP, WIFI_AP_MINIMAL_ON_TIME, diff --git a/src/src/WebServer/NetworkPage.cpp b/src/src/WebServer/NetworkPage.cpp index ce875d1f9..69964d2b0 100644 --- a/src/src/WebServer/NetworkPage.cpp +++ b/src/src/WebServer/NetworkPage.cpp @@ -163,10 +163,17 @@ void handle_networks_CopySubmittedSettings_NWPluginCall(ESPEasy::net::networkInd Settings.setNetworkInterface_isFallback(networkindex, isFormItemChecked(F("fallback"))); Settings.setNetworkInterfaceSubnetBlockClientIP(networkindex, isFormItemChecked(F("block_web_access"))); # endif // ifdef ESP32 +# ifdef ESP8266 + if (networkindex == 1) { + // Only add fallback checkbox to AP on ESP8266 + Settings.setNetworkInterface_isFallback(networkindex, isFormItemChecked(F("fallback"))); + } +# endif // ifdef ESP32 + # if FEATURE_USE_IPV6 Settings.setNetworkEnabled_IPv6(networkindex, isFormItemChecked(F("en_ipv6"))); # endif - Settings.setNetworkInterfaceStartupDelayAtBoot(networkindex, getFormItemInt(F("delay_start"))); + Settings.setNetworkInterfaceStartupDelay(networkindex, getFormItemInt(F("delay_start"))); String dummy; ESPEasy::net::NWPluginCall(NWPlugin::Function::NWPLUGIN_WEBFORM_SAVE, &TempEvent, dummy); } @@ -380,6 +387,12 @@ void handle_networks_NetworkSettingsPage(ESPEasy::net::networkIndex_t networkind 0, 255); addFormNote(F("The active interface with highest priority will be used for default route (gateway).")); addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); +# endif +# ifdef ESP8266 + if (networkindex == 1) { + // Only add fallback checkbox to AP on ESP8266 + addFormCheckBox(F("Fallback Interface"), F("fallback"), Settings.getNetworkInterface_isFallback(networkindex)); + } # endif // ifdef ESP32 addFormNumericBox(F("Delay Startup"), F("delay_start"), Settings.getNetworkInterfaceStartupDelay(networkindex), 0, 60000); addUnit(F("ms")); From c54564f6a877ecd4a6cc0bd152f9919e26140184 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 20 Feb 2026 12:59:19 +0100 Subject: [PATCH 104/161] [USB-CDC] Fix USB disconnect on reboot for USB-JTAG/CDC boards --- boards/esp32c3cdc-16M.json | 2 +- boards/esp32c3cdc.json | 3 +-- boards/esp32c5cdc-8M.json | 2 +- boards/esp32c5cdc.json | 2 +- boards/esp32c61cdc-8M.json | 2 +- boards/esp32c6cdc-16M.json | 2 +- boards/esp32c6cdc-8M.json | 2 +- boards/esp32c6cdc.json | 2 +- boards/esp32s3cdc-qio_opi-16M.json | 2 +- boards/esp32s3cdc-qio_opi-8M.json | 2 +- boards/esp32s3cdc-qio_opi.json | 2 +- boards/esp32s3cdc-qio_qspi-16M.json | 2 +- boards/esp32s3cdc-qio_qspi-8M.json | 2 +- boards/esp32s3cdc-qio_qspi.json | 2 +- lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp | 4 ++-- 15 files changed, 16 insertions(+), 17 deletions(-) diff --git a/boards/esp32c3cdc-16M.json b/boards/esp32c3cdc-16M.json index 338909ca0..293149efd 100644 --- a/boards/esp32c3cdc-16M.json +++ b/boards/esp32c3cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", diff --git a/boards/esp32c3cdc.json b/boards/esp32c3cdc.json index 206367fb9..73fc6ca36 100644 --- a/boards/esp32c3cdc.json +++ b/boards/esp32c3cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", @@ -29,7 +29,6 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, - "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", diff --git a/boards/esp32c5cdc-8M.json b/boards/esp32c5cdc-8M.json index 42ae430a3..ebe4c7fe2 100644 --- a/boards/esp32c5cdc-8M.json +++ b/boards/esp32c5cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c5cdc.json b/boards/esp32c5cdc.json index 312d32556..4459f683f 100644 --- a/boards/esp32c5cdc.json +++ b/boards/esp32c5cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c61cdc-8M.json b/boards/esp32c61cdc-8M.json index be0ed214d..06ff0f88c 100644 --- a/boards/esp32c61cdc-8M.json +++ b/boards/esp32c61cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c61_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-16M.json b/boards/esp32c6cdc-16M.json index efa16120c..1e8c84f17 100644 --- a/boards/esp32c6cdc-16M.json +++ b/boards/esp32c6cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-8M.json b/boards/esp32c6cdc-8M.json index 9bfc4e34c..d33326421 100644 --- a/boards/esp32c6cdc-8M.json +++ b/boards/esp32c6cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc.json b/boards/esp32c6cdc.json index b6615cba4..d5e8e29dd 100644 --- a/boards/esp32c6cdc.json +++ b/boards/esp32c6cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi-16M.json b/boards/esp32s3cdc-qio_opi-16M.json index 22afb4bb5..a66c537fa 100644 --- a/boards/esp32s3cdc-qio_opi-16M.json +++ b/boards/esp32s3cdc-qio_opi-16M.json @@ -9,7 +9,7 @@ "f_flash": "80000000L", "boot": "opi", "flash_mode": "qio", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "hwids": [ [ "0x303A", diff --git a/boards/esp32s3cdc-qio_opi-8M.json b/boards/esp32s3cdc-qio_opi-8M.json index 064cb4019..b24582f91 100644 --- a/boards/esp32s3cdc-qio_opi-8M.json +++ b/boards/esp32s3cdc-qio_opi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi.json b/boards/esp32s3cdc-qio_opi.json index 03943d071..e586ed6a5 100644 --- a/boards/esp32s3cdc-qio_opi.json +++ b/boards/esp32s3cdc-qio_opi.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-16M.json b/boards/esp32s3cdc-qio_qspi-16M.json index e924d3dbd..5b65241ff 100644 --- a/boards/esp32s3cdc-qio_qspi-16M.json +++ b/boards/esp32s3cdc-qio_qspi-16M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-8M.json b/boards/esp32s3cdc-qio_qspi-8M.json index 669a50bb5..62735f885 100644 --- a/boards/esp32s3cdc-qio_qspi-8M.json +++ b/boards/esp32s3cdc-qio_qspi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi.json b/boards/esp32s3cdc-qio_qspi.json index e1ecb7910..293db5750 100644 --- a/boards/esp32s3cdc-qio_qspi.json +++ b/boards/esp32s3cdc-qio_qspi.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp index f57ae8ec4..4f59d31e4 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp @@ -58,10 +58,10 @@ Port_ESPEasySerial_USB_HWCDC_t::Port_ESPEasySerial_USB_HWCDC_t(const ESPEasySeri { _config.port = ESPEasySerialPort::usb_hw_cdc; #if !ARDUINO_USB_CDC_ON_BOOT - USB.begin(); +// USB.begin(); #endif if (_hwcdc_serial != nullptr) { - _hwcdc_serial->end(); +// _hwcdc_serial->end(); // _config.rxBuffSize = _hwcdc_serial->setRxBufferSize(_config.rxBuffSize); // _config.txBuffSize = _hwcdc_serial->setTxBufferSize(_config.txBuffSize); From a5f3b99496064f313a2b06a2e09ce91847e7774c Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 20 Feb 2026 12:59:19 +0100 Subject: [PATCH 105/161] [USB-CDC] Fix USB disconnect on reboot for USB-JTAG/CDC boards --- boards/esp32c3cdc-16M.json | 2 +- boards/esp32c3cdc.json | 3 +-- boards/esp32c5cdc-8M.json | 2 +- boards/esp32c5cdc.json | 2 +- boards/esp32c61cdc-8M.json | 2 +- boards/esp32c6cdc-16M.json | 2 +- boards/esp32c6cdc-8M.json | 2 +- boards/esp32c6cdc.json | 2 +- boards/esp32s3cdc-qio_opi-16M.json | 2 +- boards/esp32s3cdc-qio_opi-8M.json | 2 +- boards/esp32s3cdc-qio_opi.json | 2 +- boards/esp32s3cdc-qio_qspi-16M.json | 2 +- boards/esp32s3cdc-qio_qspi-8M.json | 2 +- boards/esp32s3cdc-qio_qspi.json | 2 +- lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp | 4 ++-- 15 files changed, 16 insertions(+), 17 deletions(-) diff --git a/boards/esp32c3cdc-16M.json b/boards/esp32c3cdc-16M.json index 338909ca0..293149efd 100644 --- a/boards/esp32c3cdc-16M.json +++ b/boards/esp32c3cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_16M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", diff --git a/boards/esp32c3cdc.json b/boards/esp32c3cdc.json index 206367fb9..73fc6ca36 100644 --- a/boards/esp32c3cdc.json +++ b/boards/esp32c3cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c3_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DESP32_4M -DESP32C3 -DUSE_USB_CDC_CONSOLE -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "dio", @@ -29,7 +29,6 @@ "maximum_ram_size": 327680, "maximum_size": 4194304, "require_upload_port": true, - "before_reset": "usb_reset", "speed": 460800 }, "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html", diff --git a/boards/esp32c5cdc-8M.json b/boards/esp32c5cdc-8M.json index 42ae430a3..ebe4c7fe2 100644 --- a/boards/esp32c5cdc-8M.json +++ b/boards/esp32c5cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c5cdc.json b/boards/esp32c5cdc.json index 312d32556..4459f683f 100644 --- a/boards/esp32c5cdc.json +++ b/boards/esp32c5cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c5_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C5 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c61cdc-8M.json b/boards/esp32c61cdc-8M.json index be0ed214d..06ff0f88c 100644 --- a/boards/esp32c61cdc-8M.json +++ b/boards/esp32c61cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c61_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C61 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-16M.json b/boards/esp32c6cdc-16M.json index efa16120c..1e8c84f17 100644 --- a/boards/esp32c6cdc-16M.json +++ b/boards/esp32c6cdc-16M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc-8M.json b/boards/esp32c6cdc-8M.json index 9bfc4e34c..d33326421 100644 --- a/boards/esp32c6cdc-8M.json +++ b/boards/esp32c6cdc-8M.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32c6cdc.json b/boards/esp32c6cdc.json index b6615cba4..d5e8e29dd 100644 --- a/boards/esp32c6cdc.json +++ b/boards/esp32c6cdc.json @@ -4,7 +4,7 @@ "ldscript": "esp32c6_out.ld" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32C6 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "160000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi-16M.json b/boards/esp32s3cdc-qio_opi-16M.json index 22afb4bb5..a66c537fa 100644 --- a/boards/esp32s3cdc-qio_opi-16M.json +++ b/boards/esp32s3cdc-qio_opi-16M.json @@ -9,7 +9,7 @@ "f_flash": "80000000L", "boot": "opi", "flash_mode": "qio", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "hwids": [ [ "0x303A", diff --git a/boards/esp32s3cdc-qio_opi-8M.json b/boards/esp32s3cdc-qio_opi-8M.json index 064cb4019..b24582f91 100644 --- a/boards/esp32s3cdc-qio_opi-8M.json +++ b/boards/esp32s3cdc-qio_opi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_opi.json b/boards/esp32s3cdc-qio_opi.json index 03943d071..e586ed6a5 100644 --- a/boards/esp32s3cdc-qio_opi.json +++ b/boards/esp32s3cdc-qio_opi.json @@ -5,7 +5,7 @@ "memory_type": "qio_opi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-16M.json b/boards/esp32s3cdc-qio_qspi-16M.json index e924d3dbd..5b65241ff 100644 --- a/boards/esp32s3cdc-qio_qspi-16M.json +++ b/boards/esp32s3cdc-qio_qspi-16M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_16M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi-8M.json b/boards/esp32s3cdc-qio_qspi-8M.json index 669a50bb5..62735f885 100644 --- a/boards/esp32s3cdc-qio_qspi-8M.json +++ b/boards/esp32s3cdc-qio_qspi-8M.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_8M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/boards/esp32s3cdc-qio_qspi.json b/boards/esp32s3cdc-qio_qspi.json index e1ecb7910..293db5750 100644 --- a/boards/esp32s3cdc-qio_qspi.json +++ b/boards/esp32s3cdc-qio_qspi.json @@ -5,7 +5,7 @@ "memory_type": "qio_qspi" }, "core": "esp32", - "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=1", + "extra_flags": "-DARDUINO_TASMOTA -DBOARD_HAS_PSRAM -DARDUINO_USB_MODE=1 -DUSE_USB_CDC_CONSOLE -DESP32_4M -DESP32S3 -DARDUINO_USB_CDC_ON_BOOT=0", "f_cpu": "240000000L", "f_flash": "80000000L", "flash_mode": "qio", diff --git a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp index f57ae8ec4..4f59d31e4 100644 --- a/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp +++ b/lib/ESPEasySerial/Port_ESPEasySerial_USB_HWCDC.cpp @@ -58,10 +58,10 @@ Port_ESPEasySerial_USB_HWCDC_t::Port_ESPEasySerial_USB_HWCDC_t(const ESPEasySeri { _config.port = ESPEasySerialPort::usb_hw_cdc; #if !ARDUINO_USB_CDC_ON_BOOT - USB.begin(); +// USB.begin(); #endif if (_hwcdc_serial != nullptr) { - _hwcdc_serial->end(); +// _hwcdc_serial->end(); // _config.rxBuffSize = _hwcdc_serial->setRxBufferSize(_config.rxBuffSize); // _config.txBuffSize = _hwcdc_serial->setTxBufferSize(_config.txBuffSize); From 6a609c0ea4ac9fb6a81283e6f1d1331659dda500 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 21 Feb 2026 22:00:39 +0100 Subject: [PATCH 106/161] [Feature] Split Interfaces page into separate tab per interface --- src/src/CustomBuild/define_plugin_sets.h | 16 +- src/src/Static/WebStaticData.h | 348 ++++++++++++----------- src/src/WebServer/ESPEasy_WebServer.cpp | 24 +- src/src/WebServer/HardwarePage.cpp | 20 ++ src/src/WebServer/InterfacesPage.cpp | 181 ++++++++++-- src/src/WebServer/InterfacesPage.h | 27 ++ src/src/WebServer/Markup.cpp | 9 + src/src/WebServer/Markup.h | 3 + src/src/WebServer/WebTemplateParser.cpp | 136 ++++++++- src/src/WebServer/WebTemplateParser.h | 12 + static/espeasy_default.css | 37 ++- static/espeasy_default.min.css | 2 +- 12 files changed, 593 insertions(+), 222 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 73f410343..facae8cdf 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -3753,6 +3753,18 @@ To create/register a plugin, you have to : #define FEATURE_MODBUS 0 #endif +#ifndef FEATURE_CAN +#define FEATURE_CAN 0 +#endif // ifndef FEATURE_CAN + +#ifndef FEATURE_WRMBUS // mBus (wired) +#define FEATURE_WRMBUS 0 +#endif // ifndef FEATURE_WRMBUS + +#ifndef FEATURE_WIMBUS // w-mBus (wireless) +#define FEATURE_WIMBUS 0 +#endif // ifndef FEATURE_WIMBUS + #ifndef FEATURE_MQTT #define FEATURE_MQTT 0 #endif @@ -4518,10 +4530,10 @@ To create/register a plugin, you have to : #endif -#if !FEATURE_SPI && !FEATURE_I2C +#if !FEATURE_SPI && !FEATURE_I2C && !FEATURE_MODBUS && !FEATURE_CAN && !FEATURE_WRMBUS && !FEATURE_WIMBUS #ifdef WEBSERVER_INTERFACES #undef WEBSERVER_INTERFACES #endif -#endif +#endif // if !FEATURE_SPI && !FEATURE_I2C && !FEATURE_MODBUS && !FEATURE_CAN && !FEATURE_WRMBUS && !FEATURE_WIMBUS #endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index d26acf243..5786e649e 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -112,50 +112,51 @@ static const char DATA_GITHUB_CLIPBOARD_JS[] PROGMEM = { #ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ // For the gzipped data, see below in the #else block static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { -".logviewer,tbody{overflow:auto}.container,.normal td{height:30px}.container,.container input:checked~.checkmark:after,.container2,.c" -"ontainer2 input:checked~.dotmark:after,.sub{display:block}:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;--bdc1:silver;--bg4:#e" -"8e8e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--bdc2:#444 #444 #fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10" -":#07d;--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0}[data-theme=dark]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#5454" -"54;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;--c5:#c3" -"c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}@media (prefers-color-scheme:dark){[data-theme=auto]{--bg1:#444;-" -"-bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#f" -"ff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}" -"}* :not(font,a),.closebtn,.div_r,.wide,h1,h6,textarea{color:var(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;positi" -"on:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}*{box-sizing:border-box;font-family:sans-serif;font-size:12pt" -";margin:0}h1{font-size:16pt;margin:8px 0}h3{background-color:var(--bg2);margin:-5px;padding:6px}h6{font-size:10pt}pre{font:1em monos" -"pace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}.checkmark,input,select{background-col" -"or:var(--bg1)}#toastmessage,.button.help,.checkmark,.dotmark,.logviewer,hr,input,select,textarea{border:.1px solid gray}input[disabl" -"ed],input[disabled]:hover{background-color:var(--bg7)}.button.link.wide{display:inline-block;text-align:center}.button.link.red{back" -"ground-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius" -":5px}.button:hover{background:#369}input,select,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-widt" -"h:85px;width:95%}.sub,.widenumber{max-width:100px}textarea.wide{max-width:1000px}.xwide{width:300px}.container,.container2{cursor:po" -"inter;font-size:12pt;padding-left:35px}.container{margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disable" -"d{background-color:grey}.checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #f" -"ff;border-width:0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3)" -";border-radius:5px;bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;te" -"xt-align:center;visibility:hidden;z-index:1}.container2{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotma" -"rk{background-color:#eee;border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2 .dotmark:after{backgrou" -"nd:#fff;border-radius:50%;height:8px;left:8px;top:8px;width:8px}textarea{font-family:'Lucida Console',Monaco,monospace;max-height:60" -"vh;width:-webkit-fill-available;width:-moz-available}#toastmessage.show{animation:.5s fadein,.5s 2.5s fadeout;visibility:visible}.co" -"ntainer input,.container2 input{cursor:pointer;opacity:0;position:absolute}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%" -";opacity:.9}}@keyframes fadeout{from{bottom:30%;opacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}.level_1{color:#fcff95}.lev" -"el_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color:#f2ab39}.level_9{color:#f50}.logviewer{background-color:#272727;color:#f1f1" -"f1;height:60vh}.multirow th,.normal th{background-color:#444;border-color:var(--bdc1);color:var(--c1);padding:5px;width:100vw}.multi" -"row,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.normal tr{padding:5px}.multirow td{border-color:var(--bdc1);height:" -"30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}.sub{border-spacing:0}.even tr:nth-child(2n),.tworow tr:nth" -"-child(4n+3),.tworow tr:nth-child(4n+4),textarea{background-color:var(--bg4)}.menu.active,body{background-color:var(--bg6)}.apheader" -",.headermenu{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style:italic}.headermenu{background-color:var(--bg11);border-bott" -"om:.1px solid #444;height:100px;position:fixed;top:0;z-index:1}.bodymenu{height:100vh;padding-top:100px}.menubar{position:inherit;to" -"p:54px;width:100%}a.menu{align-items:center;display:flex;height:46px;justify-content:center}.menu{color:var(--c2);border:1px solid t" -"ransparent;border-radius:5px 5px 0 0;padding:10px 10px 8px;white-space:nowrap}.menu.active{border-color:var(--bdc2);color:var(--c3);" -"border-width:.1px}.menu:hover{background:var(--bg7);color:var(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_" -"r{background-color:var(--bg8);border-radius:4px;margin:2px;padding:1px 10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20" -"px}.highlight td{background-color:var(--bg5)}.div_br{clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.c" -"losebtn{cursor:pointer;font-size:22px;line-height:20px;margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x" -":auto;padding:10px 0}footer{padding:0 5px;word-break:break-all}.closebtn,.div_r{float:right;color:var(--c6)}a,h1,h6{color:var(--c5)}" -".button,.container input:checked~.checkmark:not(.disabled),.container2 input:checked~.dotmark{background-color:var(--bg10)}canvas{fi" -"lter:invert(var(--iv)) hue-rotate(var(--ro))}@media screen and (max-width:830px){.showmenulabel{display:none}a.menu{width:11%}}@medi" -"a screen and (max-width:450px){.normal{min-width:300px}input.wide:focus{left:4px;position:absolute;z-index:1}}" +".logviewer,tbody{overflow:auto}.container,.normal td{height:30px}.container,.container input:checked~.checkmark:after,.container2,.co" +"ntainer2 input:checked~.dotmark:after,.sub{display:block}:root{--bg1:#eee;--bg2:#cacaca;--bg3:#07d;--c1:#fff;--bdc1:silver;--bg4:#e8e8" +"e8;--bg5:#dbff0075;--c2:#444;--bg6:#fff;--bdc2:#444 #444 #fff;--c3:#000;--bg7:#bdbdbd;--bg8:#029b34;--c4:inherit;--c5:#07d;--bg10" +":#07d;--bg11:#f8f8f8;--c6:#fff;--ro:0deg;--iv:0;--tpm2:140px}[data-theme=dark]{--bg1:#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;" +"--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;--c3:#fff;--bg7:#00000030;--bg8:#30553d;--c4:#c3c3c3;" +"--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;--iv:1}@media (prefers-color-scheme:dark){[data-theme=auto]{--bg1" +":#444;--bg2:#2e2e2e;--bg3:#00000085;--c1:#dfdfdf;--bdc1:#545454;--bg4:#2e2e2e;--bg5:#553044;--c2:#fff;--bg6:#222;--bdc2:transparent;" +"--c3:#fff;--bg7:#00000030;--bg8:#30553d;--bg9:#222;--c4:#c3c3c3;--c5:#c3c3c3;--bg10:#44607a;--bg11:#44607a;--c6:#c3c3c3;--ro:180deg;" +"--iv:1}}* :not(font,a),.closebtn,.div_r,.wide,h1,h6,textarea{color:var(--c4)}.checkmark:after,.dotmark:after{content:'';display:none;" +"position:absolute}.button,.menu{text-decoration:none}.div_l,.menu{float:left}*{box-sizing:border-box;font-family:sans-serif;font-size" +":12pt;margin:0}h1{font-size:16pt;margin:8px 0}h3{background-color:var(--bg2);margin:-5px;padding:6px}h6{font-size:10pt}pre{font:1em " +"monospace,monospace}.button{border:0;border-radius:5px;color:var(--c6);margin:5px;padding:5px 15px}.checkmark,input,select{background" +"-color:var(--bg1)}#toastmessage,.button.help,.checkmark,.dotmark,.logviewer,hr,input,select,textarea{border:.1px solid gray}input[disa" +"bled],input[disabled]:hover{background-color:var(--bg7)}.button.link.wide{display:inline-block;text-align:center}.button.link.red{back" +"ground-color:red}.button.help{border-radius:50%;font-family:monospace;padding:2px 7px}.checkmark,input,select,textarea{border-radius:5px}" +".button:hover{background:#369}input,select,textarea{margin:4px;padding:4px 8px}.wide{margin-bottom:5px;max-width:300px;min-width:85px;" +"width:95%}.sub,.widenumber{max-width:100px}textarea.wide{max-width:1000px}.xwide{width:300px}.container,.container2{cursor:pointer;font" +"-size:12pt;padding-left:35px}.container{margin-left:4px;margin-top:0;position:relative;user-select:none}.checkmark.disabled{background-" +"color:grey}.checkmark{height:25px;left:0;position:absolute;top:0;width:25px}.container .checkmark:after{border:solid #fff;border-width" +":0 3px 3px 0;height:10px;left:7px;top:3px;transform:rotate(45deg);width:5px}#toastmessage{background-color:var(--bg3);border-radius:5px;" +"bottom:30%;color:#fff;font-size:17px;left:282px;margin-left:-125px;min-width:250px;padding:16px;position:fixed;text-align:center;visibil" +"ity:hidden;z-index:1}.container2{margin-bottom:20px;margin-left:9px;position:relative;user-select:none}.dotmark{background-color:#eee;" +"border-radius:50%;height:26px;left:0;position:absolute;top:0;width:26px}.container2 .dotmark:after{background:#fff;border-radius:50%;height" +":8px;left:8px;top:8px;width:8px}textarea{font-family:'Lucida Console',Monaco,monospace;max-height:60vh;width:-webkit-fill-available;width" +":-moz-available}#toastmessage.show{animation:.5s fadein,.5s 2.5s fadeout;visibility:visible}.container input,.container2 input{cursor:poin" +"ter;opacity:0;position:absolute}@keyframes fadein{from{bottom:20%;opacity:0}to{bottom:30%;opacity:.9}}@keyframes fadeout{from{bottom:30%;o" +"pacity:.9}to{bottom:0;opacity:0}}.level_0{color:#f1f1f1}.level_1{color:#fcff95}.level_2{color:#9dcefe}.level_3{color:#a4fc79}.level_4{color" +":#f2ab39}.level_9{color:#f50}.logviewer{background-color:#272727;color:#f1f1f1;height:60vh}.multirow th,.normal th{background-color:#444;" +"border-color:var(--bdc1);color:var(--c1);padding:5px;width:100vw}.multirow,.normal{border-spacing:0;width:100%}.multirow tr,.normal td,.no" +"rmal tr{padding:5px}.multirow td{border-color:var(--bdc1);height:30px;padding:4px;text-align:center}.tworow td,.tworow th{text-align:left}" +".sub{border-spacing:0}.even tr:nth-child(2n),.tworow tr:nth-child(4n+3),.tworow tr:nth-child(4n+4),textarea{background-color:var(--bg4)}." +"menu.active,body{background-color:var(--bg6)}.apheader,.headermenu,.headermenu2{padding:8px 12px;width:100vw}.note{color:#7b7b7b;font-style" +":italic}.headermenu,.headermenu2{background-color:var(--bg11);border-bottom:.1px solid #444;position:fixed;top:0;z-index:1}.headermenu{" +"height:100px}.headermenu2{height:var(--tpm2)}.bodymenu{height:100vh;padding-top:100px}.bodymenu2{height:100vh;padding-top:var(--tpm2)}.menu" +"bar,.menubar2{display:flex;position:inherit;max-width:1180px}.menubar{top:54px}.menubar2{top:95px}a.menu{align-items:center;display:flex;" +"height:2.5rem;justify-content:center;gap:2px;width:100%}.menu{color:var(--c2);border:1px solid transparent;border-radius:5px 5px 0 0;pad" +"ding:10px 10px 8px;white-space:nowrap}.menu.active{border-color:var(--bdc2);color:var(--c3);border-width:.1px}.menu:hover{background:var" +"(--bg7);color:var(--c3)}.menu_button{display:none}.on{color:#029b34}.off{color:red}.div_r{background-color:var(--bg8);border-radius:4px;" +"margin:2px;padding:1px 10px}.alert,.warning{color:#fff;margin-bottom:15px;padding:20px}.highlight td{background-color:var(--bg5)}.div_br{" +"clear:both}.alert{background-color:#f44336}.warning{background-color:#ffca17}.closebtn{cursor:pointer;font-size:22px;line-height:20px;" +"margin-left:15px;transition:.3s}.closebtn:hover{color:#000}section{overflow-x:auto;padding:10px 0}footer{padding:0 5px;word-break:break-" +"all}.closebtn,.div_r{float:right;color:var(--c6)}a,h1,h6{color:var(--c5)}.button,.container input:checked~.checkmark:not(.disabled),.con" +"tainer2 input:checked~.dotmark{background-color:var(--bg10)}canvas{filter:invert(var(--iv)) hue-rotate(var(--ro))}@media screen and (max-" +"width:960px){.showmenulabel{display:none}.menubar{width:100%;max-width:700px;left:0}}@media screen and (max-width:450px){.normal{min-width" +":300px}input.wide:focus{left:4px;position:absolute;z-index:1}}" }; #else // ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ // - upload the minified .js file to https://www.mischianti.org/online-converter-file-to-cpp-gzip-byte-array-3/ @@ -166,135 +167,138 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // to "static const char DATA_ESPEASY_DEFAULT_MIN_CSS_GZ[] ..." // ------------------ //File: espeasy_default.min.css.gz, Size: 2017 -static const int espeasy_default_min_css_gz_len = 2017; +static const int espeasy_default_min_css_gz_len = 2079; static const char DATA_ESPEASY_DEFAULT_MIN_CSS_GZ[] PROGMEM = { -0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xD5, 0x18, 0xDB, 0x8E, 0xAB, 0x36, -0xF0, 0x57, 0x90, 0x56, 0xAB, 0xB3, 0xB4, 0x01, 0x99, 0x5B, 0x2E, 0x44, 0x95, 0x2A, 0xF5, 0xB5, -0xFD, 0x82, 0xAA, 0x3A, 0x32, 0x60, 0x82, 0xBB, 0x04, 0x47, 0xC6, 0xB9, 0x1D, 0xE4, 0x7E, 0x7B, -0xC7, 0x06, 0x83, 0x21, 0xC9, 0xD1, 0x79, 0xE9, 0x43, 0x17, 0xED, 0xAE, 0x3D, 0x1E, 0xCF, 0x8C, -0xE7, 0x6E, 0xFB, 0x35, 0x3B, 0x5C, 0x28, 0xB9, 0x12, 0xBE, 0x12, 0x19, 0x2B, 0xEE, 0x1D, 0xBB, -0x10, 0x5E, 0xD6, 0xEC, 0x9A, 0xE2, 0xB3, 0x60, 0xD2, 0xCF, 0x59, 0x23, 0x30, 0x6D, 0x60, 0xD9, -0x6F, 0x18, 0x3F, 0xE2, 0xDA, 0x11, 0x45, 0x57, 0x11, 0x7A, 0xA8, 0x44, 0x1A, 0xA1, 0xD3, 0x6D, -0x86, 0x31, 0x0E, 0x1D, 0xDA, 0x9C, 0xCE, 0x22, 0xCD, 0x2B, 0x92, 0x7F, 0x92, 0xE2, 0x1F, 0x5F, -0x0F, 0x8E, 0x98, 0x7F, 0xA6, 0xB8, 0x14, 0x33, 0xCC, 0xD0, 0x1E, 0x2F, 0xB7, 0x15, 0x4C, 0xD8, -0x9B, 0xDA, 0x73, 0xD6, 0x15, 0xB4, 0x3D, 0xD5, 0xF8, 0x9E, 0x66, 0x35, 0xCB, 0x3F, 0x65, 0xCA, -0x19, 0x13, 0x9D, 0xE7, 0x65, 0x87, 0x20, 0x7D, 0x23, 0x84, 0xEC, 0xD5, 0x30, 0x4C, 0xDF, 0x72, -0xAC, 0x3E, 0x3D, 0x8B, 0xD2, 0x37, 0xB4, 0x29, 0x60, 0x98, 0x03, 0x4A, 0x59, 0x96, 0x0A, 0x58, -0xC0, 0xB8, 0xA5, 0x35, 0x9C, 0x54, 0xA3, 0xC4, 0xB0, 0x77, 0xAB, 0x3E, 0x3D, 0x4B, 0xD2, 0xB7, -0x22, 0x2B, 0x4B, 0x84, 0x36, 0x89, 0xDA, 0x05, 0xD4, 0xE2, 0x38, 0xD6, 0x2B, 0x6B, 0x8B, 0x40, -0x0F, 0x76, 0xFA, 0x3F, 0x3D, 0x34, 0x57, 0xAC, 0x10, 0xD2, 0xA8, 0x9B, 0xF4, 0x2D, 0x2B, 0xD4, -0xA7, 0x67, 0x5B, 0x58, 0x08, 0x77, 0x59, 0xA4, 0xC8, 0xE4, 0x71, 0x4A, 0x9B, 0x8A, 0x70, 0x2A, -0xD4, 0x24, 0x31, 0xD2, 0xC1, 0x09, 0x90, 0x35, 0x56, 0xB2, 0x6E, 0xD5, 0xA7, 0x90, 0x46, 0xBE, -0x9C, 0xA5, 0xA8, 0x20, 0x07, 0x18, 0xD1, 0x4B, 0x8A, 0xE4, 0x9F, 0x05, 0x16, 0xD8, 0x13, 0x15, -0x39, 0x92, 0x5F, 0x0A, 0xD0, 0xD3, 0x5F, 0x46, 0x13, 0x46, 0x60, 0x10, 0x32, 0x24, 0xEA, 0x1B, -0x35, 0xA1, 0x7F, 0xB6, 0xC9, 0xA0, 0x8E, 0xA2, 0x54, 0x9F, 0xD1, 0xC8, 0x5B, 0x12, 0xAB, 0xCF, -0xA8, 0xC4, 0xDA, 0x09, 0x52, 0x26, 0x49, 0x84, 0x34, 0x55, 0x75, 0xF2, 0x41, 0x0B, 0x4A, 0x21, -0x61, 0x18, 0x1A, 0x85, 0x08, 0x8E, 0x9B, 0xF6, 0x84, 0x39, 0x69, 0xC4, 0xA0, 0x0D, 0x83, 0xB7, -0x31, 0x9C, 0x23, 0x64, 0xF4, 0x11, 0x21, 0xA0, 0x58, 0xF4, 0xFA, 0x78, 0xCB, 0x23, 0xF5, 0x0D, -0xFA, 0x18, 0x27, 0xBD, 0x4A, 0xE2, 0x78, 0x8D, 0x36, 0x78, 0xD4, 0xCA, 0x38, 0x55, 0x5A, 0x19, -0x51, 0x41, 0x31, 0xC1, 0x76, 0x52, 0x4D, 0x20, 0x7F, 0x3D, 0x92, 0x82, 0x62, 0xE7, 0xE3, 0xC4, -0x49, 0x49, 0x78, 0xEB, 0xE5, 0xAC, 0x66, 0xDC, 0x6B, 0x73, 0xA5, 0xAB, 0x54, 0xE9, 0xCA, 0xED, -0x6C, 0xED, 0x29, 0x5F, 0xFF, 0x5F, 0x6A, 0x2F, 0x3B, 0xEC, 0x0C, 0x95, 0xFF, 0x46, 0x91, 0xF2, -0x27, 0x27, 0x6D, 0x98, 0xF8, 0x28, 0x21, 0x48, 0x57, 0xD8, 0x85, 0x68, 0xAD, 0x59, 0x4B, 0x32, -0xD1, 0xAC, 0xFC, 0x82, 0x5E, 0xBE, 0x42, 0x54, 0x5E, 0x69, 0x41, 0x56, 0x55, 0xB0, 0xAA, 0xD6, -0x2B, 0x41, 0x6E, 0x02, 0x4E, 0x80, 0x3B, 0xAD, 0xEE, 0xF4, 0x82, 0xF9, 0x87, 0x92, 0xCB, 0x95, -0x8F, 0x09, 0x60, 0x16, 0xDA, 0x9D, 0x4A, 0x01, 0x70, 0xF0, 0xF4, 0xCB, 0x97, 0xBD, 0x09, 0xF0, -0x86, 0x35, 0x64, 0x7F, 0x62, 0x2D, 0x15, 0x94, 0x35, 0x29, 0xCE, 0x5A, 0x56, 0x9F, 0x05, 0x91, -0x7E, 0x76, 0x16, 0x82, 0x01, 0xF7, 0x23, 0x69, 0xCE, 0x9D, 0x62, 0xE8, 0x15, 0x24, 0x67, 0x1C, -0x6B, 0x34, 0xB5, 0x49, 0x6A, 0xC1, 0xEA, 0x01, 0x03, 0x52, 0x19, 0x16, 0x69, 0x4D, 0x4A, 0x21, -0x7F, 0xEA, 0x32, 0x76, 0xF3, 0x5A, 0xFA, 0x8D, 0x36, 0x87, 0x34, 0x63, 0xBC, 0x20, 0xDC, 0x03, -0xC8, 0x5E, 0x1D, 0xCD, 0x2B, 0xF1, 0x91, 0xD6, 0xF7, 0xB4, 0x05, 0x23, 0x78, 0x2D, 0x44, 0x67, -0xD9, 0x83, 0x01, 0x9B, 0xA4, 0x41, 0x78, 0x12, 0x7B, 0x90, 0xF6, 0x40, 0x1B, 0x88, 0xBA, 0x2A, -0xE8, 0xAC, 0xA5, 0xF5, 0xB4, 0xB4, 0x3D, 0xDD, 0x1C, 0x58, 0x8E, 0xBA, 0x0C, 0xE7, 0x9F, 0x07, -0xCE, 0xCE, 0x4D, 0xE1, 0xD9, 0x8A, 0x00, 0x8F, 0x72, 0x0D, 0xAE, 0x97, 0x9C, 0x6E, 0xFB, 0x13, -0x2E, 0x0A, 0x25, 0xCB, 0x1A, 0xD2, 0x68, 0xB5, 0xB6, 0xA9, 0xA2, 0x93, 0x90, 0xE0, 0xBA, 0x1A, -0x94, 0x06, 0xE4, 0xE8, 0x1C, 0x59, 0xC3, 0xC0, 0x3B, 0x72, 0xB2, 0x1A, 0x47, 0x46, 0x15, 0x5D, -0x7F, 0x94, 0x14, 0xED, 0x87, 0x33, 0x71, 0x5C, 0xD0, 0x73, 0x9B, 0x2A, 0x0E, 0x33, 0x3B, 0xAC, -0x47, 0xEE, 0x36, 0x73, 0x18, 0x3B, 0x41, 0xA2, 0x13, 0xB9, 0xB1, 0xD1, 0x4A, 0xA7, 0xE1, 0x55, -0x4B, 0x6A, 0x92, 0x8B, 0xD7, 0xA7, 0x09, 0x5C, 0xF9, 0x26, 0x18, 0x6E, 0xC5, 0x91, 0xB4, 0x2D, -0x3E, 0x90, 0xD5, 0x20, 0x90, 0x5F, 0x91, 0xFA, 0xB4, 0xB2, 0xC8, 0x19, 0x63, 0xAF, 0xFC, 0xA9, -0xD8, 0x54, 0x7C, 0xC6, 0x65, 0x72, 0x9D, 0xE1, 0x34, 0x7E, 0x00, 0x72, 0x81, 0xCD, 0x69, 0xE1, -0x1C, 0x38, 0xBE, 0x4B, 0x8D, 0xFC, 0x27, 0x38, 0x07, 0xCE, 0x6A, 0x52, 0xFC, 0xB5, 0x5A, 0xCC, -0xD3, 0x4A, 0xD5, 0xAD, 0xD7, 0xB2, 0x6E, 0x5C, 0xA3, 0x2E, 0xBF, 0xA6, 0xCD, 0xA7, 0x76, 0xDA, -0xB1, 0x96, 0xD0, 0x06, 0x60, 0xC4, 0xD3, 0x25, 0x65, 0xAF, 0x5D, 0x0A, 0xD7, 0xF4, 0xD0, 0xA4, -0x39, 0xB8, 0x24, 0xE1, 0xF3, 0x8D, 0x9C, 0x14, 0x8F, 0x5C, 0x00, 0x28, 0xED, 0xC3, 0x77, 0x0B, -0x4B, 0xA0, 0xF7, 0x99, 0x93, 0x8D, 0x26, 0x1C, 0x8D, 0x10, 0xC2, 0x61, 0x37, 0xAF, 0x6D, 0xB0, -0xD4, 0x8E, 0x65, 0x62, 0xC3, 0xF7, 0x41, 0x01, 0x90, 0x24, 0xD6, 0x3B, 0xF9, 0x9C, 0xCA, 0xE0, -0x06, 0xB1, 0xE5, 0x06, 0x30, 0x76, 0xB6, 0x8A, 0x9C, 0xD6, 0x4C, 0x8F, 0x00, 0xD1, 0x01, 0xA4, -0x8F, 0xDA, 0x5D, 0x8E, 0xF8, 0xE6, 0xC1, 0x92, 0xA8, 0xA0, 0xEA, 0x23, 0x35, 0x87, 0xE5, 0x7E, -0xBE, 0x55, 0xCB, 0xFD, 0x70, 0x97, 0xBC, 0x4B, 0x55, 0xA5, 0xFB, 0xA4, 0xD0, 0x9C, 0x8F, 0x19, -0x88, 0x34, 0xED, 0x0C, 0xD4, 0x4E, 0x69, 0xA4, 0x30, 0x9C, 0xAC, 0x55, 0xDD, 0x4F, 0xDC, 0x34, -0xDC, 0xE2, 0xF5, 0xBC, 0xC5, 0x08, 0xBB, 0xFC, 0xCC, 0x5B, 0x50, 0xFE, 0x89, 0x51, 0x65, 0xA6, -0x65, 0xBC, 0x0E, 0x07, 0xF3, 0x54, 0xE8, 0xA7, 0x51, 0x32, 0x23, 0x63, 0xCE, 0xA7, 0xD7, 0x62, -0x7D, 0x3A, 0x3D, 0x17, 0xEC, 0x04, 0x61, 0x34, 0x26, 0x1D, 0x4E, 0x6A, 0x48, 0x2B, 0x17, 0xB2, -0x3F, 0x43, 0x46, 0xF0, 0x7A, 0x25, 0x0E, 0x29, 0x66, 0xB4, 0x93, 0x6F, 0x5C, 0xF0, 0xD1, 0x2B, -0x0E, 0x9C, 0xDC, 0x2D, 0x4C, 0xD3, 0x35, 0x85, 0x4A, 0x5F, 0x9A, 0x33, 0x7A, 0xCC, 0x6F, 0xFB, -0x5E, 0x84, 0xFE, 0xF4, 0xE1, 0x5C, 0x6A, 0x67, 0x99, 0x45, 0x4D, 0xAC, 0xF4, 0x61, 0xA2, 0xEB, -0xC5, 0xE0, 0x1F, 0xFD, 0x7E, 0xE4, 0x44, 0x60, 0x55, 0xF5, 0x8B, 0xF6, 0x03, 0xF3, 0x00, 0x19, -0xE6, 0xE0, 0x6E, 0x9A, 0x59, 0xA4, 0xFE, 0xAB, 0xDA, 0x53, 0x42, 0x7B, 0x07, 0xFD, 0x94, 0xC0, -0x82, 0x7C, 0xC4, 0x09, 0x94, 0x00, 0x77, 0x10, 0x43, 0x49, 0x31, 0x0B, 0xF4, 0xD7, 0x61, 0x16, -0xB9, 0x4F, 0x92, 0xD0, 0xE0, 0x45, 0x11, 0x44, 0x41, 0x8F, 0xAD, 0x25, 0xB5, 0xCC, 0xB5, 0x31, -0x32, 0x85, 0xDB, 0x70, 0x32, 0x86, 0x86, 0x78, 0x81, 0x56, 0xD7, 0xE4, 0x6D, 0x61, 0x82, 0x2C, -0xAF, 0x85, 0xF4, 0x7B, 0x9B, 0x94, 0x58, 0xD2, 0x1B, 0x29, 0x1E, 0x03, 0x78, 0x7F, 0xA1, 0x2D, -0xCD, 0x68, 0x4D, 0xC5, 0x3D, 0xAD, 0x68, 0x01, 0x8E, 0xB9, 0xFF, 0xE6, 0xD1, 0xA6, 0x20, 0x37, -0xA8, 0x6F, 0xB6, 0x43, 0xCD, 0xBD, 0x3E, 0x44, 0x0B, 0x59, 0x76, 0x36, 0xAF, 0xEF, 0xF8, 0xC6, -0x90, 0xED, 0x1E, 0xB5, 0xA4, 0x3B, 0xD4, 0xC7, 0xD4, 0x60, 0xFC, 0x62, 0xFD, 0xC3, 0x7E, 0xB1, -0x9E, 0xF9, 0x45, 0xE8, 0x2C, 0xAA, 0xA9, 0x9D, 0x04, 0x2C, 0xA7, 0x78, 0x64, 0xB9, 0x35, 0x1C, -0xB7, 0x83, 0x33, 0x6C, 0xC7, 0x50, 0xDE, 0x5A, 0x91, 0xDA, 0xD9, 0xC9, 0xEB, 0xCB, 0xEF, 0xE7, -0x9C, 0x16, 0xD8, 0xF9, 0x8D, 0x35, 0x20, 0x1A, 0xF9, 0xB2, 0xFA, 0x83, 0x35, 0x38, 0x67, 0x53, -0x5D, 0xD2, 0xA9, 0x62, 0x60, 0xB0, 0x46, 0x97, 0x6A, 0x20, 0xE8, 0x5D, 0x49, 0xF6, 0x49, 0x81, -0x0C, 0xAD, 0x6B, 0x0F, 0x5F, 0x30, 0xAD, 0x55, 0xD8, 0x98, 0xC5, 0x23, 0xFB, 0x36, 0x01, 0xE7, -0xDE, 0xE6, 0xB7, 0x15, 0xBB, 0x76, 0xB8, 0xA1, 0xC7, 0xBE, 0xC6, 0xFB, 0x49, 0xEB, 0x94, 0xB8, -0x20, 0x14, 0x9A, 0x00, 0x18, 0x86, 0x66, 0xCE, 0xCE, 0xC2, 0xB6, 0xB4, 0x1E, 0xD6, 0x44, 0x2E, -0x2F, 0x25, 0x8F, 0xF7, 0x8D, 0x65, 0x22, 0x61, 0x70, 0x0A, 0x45, 0xE1, 0x89, 0x19, 0xE4, 0xAF, -0x9F, 0xE4, 0x5E, 0x72, 0x0C, 0xA2, 0x0D, 0x32, 0x74, 0x25, 0x67, 0xC7, 0x6E, 0x74, 0x99, 0xF7, -0x69, 0xB7, 0x14, 0xAC, 0xB3, 0x5C, 0xDF, 0xC0, 0xFD, 0x9D, 0x5C, 0x52, 0x01, 0xC9, 0x67, 0x64, -0x16, 0xE8, 0x13, 0x1D, 0x64, 0x51, 0x97, 0x7E, 0x4D, 0x2E, 0xA4, 0xFE, 0x8A, 0x3A, 0x13, 0x54, -0x81, 0xFA, 0x0C, 0x38, 0x18, 0xC1, 0x79, 0x59, 0xEE, 0x12, 0x03, 0x0E, 0x0D, 0x78, 0x57, 0xE4, -0xD0, 0x19, 0x1B, 0x70, 0x64, 0xC0, 0x38, 0x2E, 0xF3, 0xCD, 0xCE, 0x80, 0xE3, 0x91, 0x48, 0x88, -0xB3, 0x68, 0x04, 0xEF, 0x46, 0x70, 0x82, 0xE4, 0x54, 0xD3, 0x9F, 0x38, 0x7C, 0xB8, 0x51, 0xDF, -0x7E, 0x26, 0xE1, 0xDE, 0xF2, 0x0D, 0xE9, 0x1F, 0xCF, 0xB5, 0xA0, 0x9C, 0x5D, 0x1D, 0x51, 0x4D, -0x37, 0xCC, 0xEA, 0x09, 0x29, 0xD5, 0x95, 0x0F, 0x8E, 0x3C, 0xCB, 0x39, 0xD0, 0x7B, 0xBB, 0xF3, -0x3E, 0x07, 0xE6, 0x56, 0x73, 0xB3, 0x1F, 0xAB, 0xCB, 0xE5, 0x3A, 0xB1, 0x33, 0xBC, 0x4C, 0x41, -0x55, 0x9E, 0xAB, 0x36, 0xA0, 0x09, 0xFD, 0xDD, 0x16, 0xCE, 0xBE, 0xFE, 0x4E, 0x43, 0xDE, 0x59, -0x8C, 0x6C, 0xF4, 0xA2, 0x7B, 0x29, 0xAA, 0x75, 0x77, 0xB6, 0x8B, 0xEF, 0xB3, 0xBE, 0x43, 0x5C, -0x59, 0x4F, 0x6D, 0x35, 0x0E, 0xAB, 0xCE, 0xC2, 0xD3, 0x5D, 0xAD, 0xBE, 0x15, 0x2F, 0x4F, 0x21, -0x7D, 0x30, 0x54, 0x03, 0x02, 0xA6, 0x8D, 0xA8, 0xBC, 0xBC, 0xA2, 0x75, 0xF1, 0x11, 0x36, 0xEE, -0x44, 0xC7, 0x5E, 0x88, 0x9B, 0x9F, 0xA3, 0xD7, 0x4B, 0xB1, 0x6B, 0x75, 0x1F, 0xAF, 0xF2, 0xBE, -0x6A, 0xF1, 0x55, 0xBB, 0xED, 0xE3, 0x5C, 0x25, 0xC5, 0x95, 0x7E, 0x49, 0x78, 0x89, 0xBD, 0x06, -0x6C, 0x7C, 0xAA, 0x08, 0xB8, 0x3E, 0xE8, 0xB5, 0xFF, 0xAF, 0x9B, 0x75, 0xA3, 0x0F, 0xD5, 0x49, -0x07, 0xE1, 0xD2, 0x76, 0x70, 0x0D, 0x21, 0xC6, 0xF3, 0x36, 0x99, 0xFA, 0x86, 0x22, 0x22, 0xEE, -0x35, 0x49, 0xA9, 0x00, 0xA5, 0xE4, 0xD2, 0x26, 0xF7, 0xBA, 0x73, 0x0D, 0xC6, 0x3A, 0x35, 0x44, -0x96, 0xD5, 0x6E, 0x6A, 0x4F, 0x1B, 0xCB, 0x25, 0x7A, 0x52, 0x60, 0x74, 0x2A, 0xB6, 0x4A, 0x88, -0x3A, 0xAD, 0x66, 0x38, 0xED, 0x82, 0xB4, 0x67, 0x1A, 0x10, 0x85, 0xDE, 0x77, 0x3E, 0x5A, 0x45, -0x19, 0x06, 0xAF, 0x31, 0x04, 0xCD, 0x4B, 0x80, 0xC2, 0x49, 0x62, 0xFB, 0xC0, 0xEF, 0x12, 0xF7, -0x17, 0x18, 0x6D, 0x6A, 0x8F, 0x0A, 0x72, 0x6C, 0x4D, 0x3D, 0x33, 0x7D, 0x6B, 0x59, 0x93, 0x9B, -0x91, 0x34, 0x56, 0xD5, 0xE3, 0xEF, 0x73, 0x2B, 0x68, 0x79, 0xF7, 0xCC, 0x75, 0xCA, 0x38, 0x92, -0x26, 0x34, 0x8B, 0x91, 0xD0, 0x28, 0x20, 0x9D, 0x0E, 0x6E, 0xDF, 0x40, 0x1F, 0xAA, 0xB8, 0xA3, -0x7E, 0x11, 0x34, 0x12, 0x63, 0xE9, 0x45, 0xCA, 0x48, 0xA8, 0x6F, 0x1B, 0xF7, 0xD7, 0x0A, 0x24, -0xD4, 0xFE, 0x47, 0xA0, 0x0A, 0x5E, 0x39, 0x3E, 0xCD, 0x1C, 0xE2, 0x45, 0x38, 0x84, 0x8B, 0xC8, -0x9D, 0xDA, 0x87, 0x5E, 0x0F, 0xCA, 0x2A, 0x3D, 0x9D, 0xC7, 0xF6, 0x76, 0xEA, 0xEC, 0x97, 0x34, -0xFA, 0x1D, 0x5F, 0x87, 0xDB, 0x91, 0x7D, 0x9F, 0x94, 0x3E, 0x00, 0x06, 0x0F, 0xEA, 0x1F, 0x64, -0x00, 0x52, 0x96, 0x9D, 0xD5, 0xC5, 0xEB, 0x4B, 0xED, 0x6B, 0xCF, 0xD9, 0x2E, 0x1B, 0x9C, 0xA9, -0x7B, 0x54, 0x4D, 0xFC, 0xA4, 0x9D, 0x41, 0x39, 0xE0, 0xE8, 0x35, 0xE1, 0x50, 0x74, 0xAE, 0x98, -0x37, 0xB0, 0xD0, 0x59, 0x1D, 0xD0, 0xBC, 0xDD, 0x08, 0xEC, 0x4B, 0x59, 0xA8, 0xB7, 0x56, 0x60, -0xD9, 0x5A, 0x59, 0x57, 0x27, 0x94, 0x57, 0x22, 0x25, 0x6E, 0x2F, 0x75, 0x06, 0xD7, 0xE8, 0x9A, -0x60, 0x0E, 0x57, 0x5B, 0x51, 0x0D, 0x7C, 0x9F, 0xE4, 0xD1, 0x32, 0x8E, 0xA3, 0x68, 0x2D, 0x47, -0x81, 0x9E, 0x60, 0x94, 0x39, 0x0E, 0x36, 0x72, 0xBC, 0xE9, 0xBF, 0x6E, 0xB4, 0x43, 0x75, 0x64, -0x7D, 0x77, 0x32, 0x3D, 0xCC, 0xB2, 0x67, 0xD2, 0xA7, 0xD2, 0x8E, 0xD5, 0x3B, 0xBC, 0x1F, 0xB5, -0x13, 0xE1, 0xC1, 0xA8, 0xC6, 0x20, 0x08, 0xC9, 0x16, 0xBA, 0x28, 0x40, 0x1B, 0x1F, 0x21, 0xBD, -0x9B, 0x7E, 0x86, 0x9C, 0x3B, 0x1D, 0x92, 0x25, 0x63, 0xAA, 0xCF, 0x31, 0x50, 0xE4, 0xE8, 0x3C, -0x0F, 0x76, 0xF1, 0x32, 0x48, 0x53, 0x9F, 0xA9, 0xFE, 0x0B, 0x49, 0xB2, 0x96, 0xCB, 0xE7, 0x8A, -0xE1, 0x3D, 0x80, 0x2B, 0x69, 0x97, 0xB7, 0x63, 0x89, 0xFB, 0x67, 0x8C, 0x79, 0xA4, 0x24, 0xEE, -0xF4, 0xE6, 0xF0, 0x03, 0xEF, 0x9B, 0xEA, 0xAD, 0x64, 0xBC, 0x13, 0xB8, 0x3F, 0xF0, 0xB8, 0xF9, -0x9D, 0x2C, 0x85, 0x5C, 0x99, 0xE3, 0xE6, 0x82, 0xDB, 0x0E, 0x9A, 0x26, 0x38, 0x30, 0xA4, 0x0B, -0xD0, 0x8B, 0xF8, 0xE8, 0x11, 0xE8, 0xC5, 0x75, 0x9D, 0xEA, 0x4C, 0xBC, 0xA1, 0x67, 0xEF, 0xA1, -0x9C, 0xB9, 0xAE, 0x79, 0xFD, 0x6A, 0x73, 0x4E, 0xA0, 0x0A, 0xE0, 0xA6, 0x70, 0x3E, 0xA6, 0x7B, -0xD6, 0x66, 0x0B, 0x4A, 0x74, 0x3B, 0xDD, 0x56, 0xA9, 0x40, 0x81, 0x96, 0x8B, 0xD4, 0xF3, 0x30, -0x19, 0x52, 0xCF, 0x90, 0x8C, 0xC2, 0x77, 0xF9, 0x7D, 0x8A, 0x71, 0xD2, 0x53, 0x1C, 0x4A, 0xEA, -0xD4, 0xAC, 0xF7, 0xD7, 0x37, 0x7D, 0x6E, 0x7D, 0xDB, 0x4B, 0x4B, 0x96, 0x9F, 0xDB, 0x6E, 0xBC, -0x76, 0x3D, 0xB6, 0xB9, 0x53, 0x56, 0x95, 0xFF, 0x02, 0x39, 0x7E, 0xE4, 0xC5, 0x9A, 0x16, 0x00, -0x00 +0x1F, 0x8B, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xD5, 0x18, 0xD9, 0x8E, 0xA4, 0x38, +0xF2, 0x57, 0x90, 0x4A, 0xA5, 0x2E, 0x66, 0x13, 0x64, 0xAE, 0x3C, 0x9C, 0x1A, 0x69, 0xA4, 0x79, +0xDD, 0xFD, 0x82, 0xD1, 0xA8, 0x65, 0xC0, 0x24, 0xDE, 0x02, 0x9C, 0x32, 0xCE, 0xA3, 0x1A, 0xB1, +0xDF, 0xBE, 0x61, 0x83, 0xC1, 0x90, 0x99, 0xAD, 0x7E, 0xD9, 0x87, 0x2D, 0x54, 0x55, 0x26, 0x1C, +0x8E, 0x08, 0xC7, 0x1D, 0xF8, 0x15, 0x3F, 0x5D, 0x19, 0xBD, 0x51, 0xB1, 0x91, 0x29, 0xCF, 0xBF, +0x3A, 0x7E, 0xA5, 0xA2, 0xA8, 0xF8, 0x0D, 0x93, 0x8B, 0xE4, 0xBD, 0x9F, 0xF1, 0x46, 0x12, 0xD6, +0xC0, 0xB6, 0xDF, 0x70, 0x51, 0x93, 0xCA, 0x91, 0x79, 0x57, 0x52, 0x76, 0x2A, 0x25, 0x8E, 0xD0, +0xF9, 0xBE, 0xC0, 0x98, 0x96, 0x0E, 0x6B, 0xCE, 0x17, 0x89, 0xB3, 0x92, 0x66, 0x9F, 0x34, 0xFF, +0x8F, 0xAF, 0x17, 0x35, 0x11, 0x9F, 0x98, 0x14, 0x72, 0x81, 0x19, 0xDA, 0xEB, 0xF5, 0xB1, 0x9C, +0x4B, 0xFB, 0x50, 0x7B, 0x49, 0xBB, 0x9C, 0xB5, 0xE7, 0x8A, 0x7C, 0xE1, 0xB4, 0xE2, 0xD9, 0x67, +0x8F, 0x05, 0xE7, 0xB2, 0xF3, 0xBC, 0xF4, 0x14, 0xE0, 0x37, 0x4A, 0xE9, 0x51, 0x2D, 0x43, 0xFC, +0x96, 0x11, 0xF5, 0xE8, 0xB7, 0x08, 0xBF, 0xA1, 0x5D, 0x0E, 0xCB, 0x0C, 0x50, 0x8A, 0xA2, 0x50, +0xC0, 0x1C, 0xD6, 0x2D, 0xAB, 0xE0, 0xA6, 0x1A, 0x25, 0x86, 0xB3, 0x7B, 0xF5, 0xE8, 0xB7, 0x04, +0xBF, 0xE5, 0x69, 0x51, 0x20, 0xB4, 0x4B, 0xD4, 0x29, 0xA0, 0x16, 0xC7, 0xB1, 0xDE, 0xD9, 0x5A, +0x04, 0x06, 0xB0, 0x33, 0xFC, 0x19, 0xA0, 0x99, 0x62, 0x85, 0x90, 0x46, 0xDD, 0xE1, 0xB7, 0x34, +0x57, 0x8F, 0x7E, 0xDB, 0xC3, 0x46, 0x78, 0x48, 0x23, 0x45, 0x26, 0x8B, 0x31, 0x6B, 0x4A, 0x2A, +0x98, 0x54, 0x2F, 0x89, 0x91, 0x0E, 0x6E, 0x80, 0xAC, 0xB5, 0x92, 0x75, 0xAF, 0x1E, 0x85, 0x34, +0xF1, 0x15, 0x1C, 0xA3, 0x9C, 0x9E, 0x60, 0xC5, 0xAE, 0x58, 0x31, 0x92, 0xE7, 0x3A, 0xC4, 0x41, +0xAC, 0xEC, 0xF0, 0x57, 0x4E, 0x24, 0xF1, 0x64, 0x49, 0x6B, 0xFA, 0x7B, 0x0E, 0x4A, 0xFB, 0xDB, +0xA8, 0xC5, 0x48, 0x0F, 0x12, 0x87, 0x54, 0x3D, 0x93, 0x5A, 0xF4, 0xCF, 0x3E, 0x19, 0x75, 0x93, +0x17, 0xEA, 0x31, 0xEA, 0x79, 0x4B, 0x62, 0xF5, 0x18, 0xFD, 0x58, 0x27, 0x41, 0xE4, 0x24, 0x89, +0x90, 0xA6, 0xAA, 0xD4, 0x30, 0xAA, 0x44, 0x69, 0x27, 0x0C, 0x43, 0xA3, 0x1D, 0x29, 0x48, 0xD3, +0x9E, 0x89, 0xA0, 0x8D, 0x1C, 0x55, 0x63, 0xF0, 0x76, 0x86, 0x73, 0x84, 0x8C, 0x72, 0x22, 0x04, +0x14, 0xF3, 0x41, 0x39, 0x6F, 0x59, 0xA4, 0x9E, 0x51, 0x39, 0xD3, 0xCB, 0xA0, 0x9F, 0x38, 0xDE, +0xA2, 0x1D, 0x99, 0x54, 0x34, 0xBD, 0x2A, 0x15, 0x4D, 0xA8, 0xA0, 0xA5, 0x60, 0x3F, 0xEB, 0x29, +0xE8, 0xFF, 0xA8, 0x69, 0xCE, 0x88, 0xF3, 0x71, 0x16, 0xB4, 0xA0, 0xA2, 0xF5, 0x32, 0x5E, 0x71, +0xE1, 0xB5, 0x99, 0xD2, 0x15, 0x56, 0xBA, 0x72, 0x3B, 0x5B, 0x7B, 0xCA, 0xF1, 0xFF, 0x2F, 0xB5, +0x97, 0x9E, 0x0E, 0x86, 0xCA, 0xFF, 0x46, 0x91, 0xFD, 0x6F, 0x0E, 0x6E, 0xB8, 0xFC, 0x28, 0x20, +0x62, 0x37, 0xC4, 0x85, 0xD0, 0xAD, 0x78, 0x4B, 0x53, 0xD9, 0x6C, 0xFC, 0x9C, 0x5D, 0xBF, 0x43, +0x88, 0xDE, 0x58, 0x4E, 0x37, 0x65, 0xB0, 0x29, 0xB7, 0x1B, 0x49, 0xEF, 0x12, 0x6E, 0x40, 0x3A, +0xAD, 0x6E, 0x7C, 0x25, 0xE2, 0x43, 0xC9, 0xE5, 0xF6, 0x8F, 0xD9, 0x60, 0x11, 0xE7, 0x9D, 0xCA, +0x07, 0x70, 0x71, 0xFC, 0xED, 0xDB, 0xD1, 0x44, 0x7B, 0xC3, 0x1B, 0x7A, 0x3C, 0xF3, 0x96, 0x49, +0xC6, 0x1B, 0x4C, 0xD2, 0x96, 0x57, 0x17, 0x49, 0x7B, 0x3F, 0xBD, 0x48, 0xC9, 0x81, 0x7B, 0x4D, +0x9B, 0x4B, 0xA7, 0x18, 0x7A, 0x39, 0xCD, 0xB8, 0x20, 0x1A, 0x4D, 0x1D, 0xEA, 0xB5, 0x60, 0xD5, +0x88, 0x01, 0x79, 0x8D, 0x48, 0x5C, 0xD1, 0x42, 0xF6, 0xBF, 0x75, 0x29, 0xBF, 0x7B, 0x2D, 0xFB, +0xC1, 0x9A, 0x13, 0x4E, 0xB9, 0xC8, 0xA9, 0xF0, 0x00, 0x72, 0x54, 0x57, 0xF3, 0x0A, 0x52, 0xB3, +0xEA, 0x0B, 0xB7, 0x60, 0x04, 0xAF, 0x85, 0x50, 0x2D, 0x06, 0x30, 0x60, 0x53, 0x1C, 0x84, 0x67, +0x79, 0x04, 0x69, 0x4F, 0xAC, 0xC1, 0xA8, 0x2F, 0x83, 0xCE, 0xDA, 0xDA, 0xCE, 0x5B, 0xFB, 0xF3, +0xDD, 0x81, 0xED, 0xA8, 0x4B, 0x49, 0xF6, 0x79, 0x12, 0xFC, 0xD2, 0xE4, 0x9E, 0xAD, 0x08, 0xF0, +0x28, 0xD7, 0xE0, 0x7A, 0xC9, 0xF9, 0x7E, 0x3C, 0x93, 0x3C, 0x57, 0xB2, 0x6C, 0x21, 0x96, 0xCB, +0xAD, 0x4D, 0x15, 0x9D, 0x65, 0x0F, 0xAE, 0xAB, 0x41, 0x38, 0xA0, 0xB5, 0x53, 0xF3, 0x86, 0x83, +0x77, 0x64, 0x74, 0x33, 0xAD, 0x8C, 0x2A, 0xBA, 0xE1, 0x2A, 0x90, 0x1C, 0xC6, 0x3B, 0x09, 0x92, +0xB3, 0x4B, 0x8B, 0x15, 0x87, 0x85, 0x1D, 0xB6, 0x13, 0x77, 0x9B, 0x39, 0xAC, 0x9D, 0x20, 0xD1, +0x59, 0xDD, 0xD8, 0x68, 0xA3, 0x73, 0xF2, 0xA6, 0xA5, 0x15, 0xCD, 0xE4, 0xEB, 0xDB, 0x04, 0x6E, +0xFF, 0x26, 0x39, 0x69, 0x65, 0x4D, 0xDB, 0x96, 0x9C, 0xE8, 0x66, 0x14, 0xC8, 0x2F, 0x69, 0x75, +0xDE, 0x58, 0xE4, 0x8C, 0xB1, 0x37, 0xFE, 0x5C, 0x79, 0x4A, 0xB1, 0xE0, 0x32, 0xBB, 0xCE, 0x78, +0x1B, 0x3F, 0x00, 0xB9, 0xC0, 0xE6, 0x2C, 0x77, 0x4E, 0x82, 0x7C, 0xF5, 0x1A, 0xF9, 0x2F, 0x70, +0x0E, 0x92, 0x56, 0x34, 0xFF, 0x7B, 0xB3, 0x7A, 0xC7, 0xA5, 0x2A, 0x62, 0xAF, 0x65, 0xDD, 0xB9, +0x46, 0x5D, 0x7E, 0xC5, 0x9A, 0x4F, 0xED, 0xB4, 0x53, 0x61, 0x61, 0x0D, 0xC0, 0xA8, 0xA7, 0xEB, +0xCB, 0x51, 0xBB, 0x14, 0xA9, 0xD8, 0xA9, 0xC1, 0x19, 0xB8, 0x24, 0x15, 0xCB, 0x83, 0x82, 0xE6, +0x8F, 0x5C, 0x00, 0xD8, 0xDB, 0x97, 0xEF, 0x56, 0x96, 0x40, 0xEF, 0x0B, 0x27, 0x9B, 0x4C, 0x38, +0x19, 0x21, 0x84, 0xCB, 0xEE, 0x5E, 0xDB, 0x60, 0xAD, 0x1D, 0xCB, 0xC4, 0x86, 0xEF, 0x83, 0x02, +0x20, 0x49, 0x6C, 0x0F, 0xFD, 0x73, 0x2A, 0xA3, 0x1B, 0xC4, 0x96, 0x1B, 0xC0, 0xDA, 0xD9, 0x2B, +0x72, 0x5A, 0x33, 0x03, 0x02, 0x44, 0x07, 0x90, 0xAE, 0xB5, 0xBB, 0xD4, 0xE4, 0xEE, 0xC1, 0x96, +0x2C, 0xA1, 0x05, 0x40, 0xEA, 0x1D, 0xB6, 0x87, 0xF7, 0xBD, 0xDA, 0x1E, 0x96, 0x87, 0xE4, 0xBD, +0x57, 0x25, 0x7B, 0x48, 0x0A, 0xCD, 0xA5, 0x4E, 0x41, 0xA4, 0xF9, 0x64, 0xA0, 0x4E, 0xF6, 0x46, +0x0A, 0xC3, 0xC9, 0xDA, 0xD5, 0xCD, 0xC5, 0x5D, 0xC3, 0x2D, 0x5E, 0xCF, 0xFB, 0x8D, 0xB0, 0xCB, +0x2E, 0xA2, 0x05, 0xE5, 0x9F, 0x39, 0x53, 0x66, 0x5A, 0xC7, 0xEB, 0x78, 0x31, 0x4F, 0x85, 0x3E, +0x8E, 0x92, 0x05, 0x19, 0x73, 0x3F, 0xBD, 0x17, 0xEB, 0xDB, 0xE9, 0x77, 0xC9, 0xCF, 0x10, 0x46, +0x53, 0xD2, 0x11, 0xB4, 0x82, 0xB4, 0x72, 0xA5, 0xC7, 0x0B, 0x64, 0x04, 0x6F, 0x50, 0xE2, 0x98, +0x62, 0x26, 0x3B, 0xF9, 0xC6, 0x05, 0x1F, 0xBD, 0xE2, 0x24, 0xE8, 0x97, 0x85, 0x69, 0x5A, 0xA8, +0x50, 0xE9, 0x4B, 0x73, 0x46, 0x8F, 0xF9, 0xED, 0x38, 0x88, 0x30, 0xDC, 0x3E, 0x5C, 0x4A, 0xED, +0xAC, 0xB3, 0xA8, 0x89, 0x95, 0x21, 0x4C, 0x74, 0xBD, 0x18, 0xFD, 0x63, 0x38, 0x8F, 0x9C, 0x08, +0xAC, 0xAA, 0x7E, 0xD1, 0x71, 0x64, 0x1E, 0x20, 0xC3, 0x1C, 0xDC, 0x4D, 0x33, 0x8B, 0xD4, 0x7F, +0x55, 0x7B, 0x0A, 0xE8, 0xF5, 0xA0, 0xB9, 0x92, 0x44, 0xD2, 0x8F, 0x38, 0x81, 0x12, 0xE0, 0x8E, +0x62, 0x28, 0x29, 0x16, 0x81, 0xFE, 0x3A, 0xCC, 0x22, 0xF7, 0x49, 0x12, 0x1A, 0xBD, 0x28, 0x82, +0x28, 0x18, 0xB0, 0xB5, 0xA4, 0x96, 0xB9, 0x76, 0x46, 0xA6, 0x70, 0x1F, 0xCE, 0xC6, 0xD0, 0x10, +0x2F, 0xD0, 0xEA, 0x9A, 0xBD, 0x2D, 0x4C, 0x90, 0xE5, 0xB5, 0x90, 0x7E, 0xEF, 0xB3, 0x12, 0x0B, +0x76, 0xA7, 0xF9, 0x63, 0x00, 0x1F, 0xAF, 0xAC, 0x65, 0x29, 0xAB, 0x98, 0xFC, 0xC2, 0x25, 0xCB, +0xC1, 0x31, 0x8F, 0x3F, 0x3C, 0xD6, 0xE4, 0xF4, 0x0E, 0xF5, 0xCD, 0x76, 0xA8, 0xA5, 0xD7, 0x87, +0x68, 0x25, 0xCB, 0xC1, 0xE6, 0xF5, 0x13, 0xDF, 0x18, 0xB3, 0xDD, 0xA3, 0x96, 0x74, 0xBB, 0xFA, +0x98, 0x1A, 0x8C, 0x5F, 0x6C, 0x7F, 0xD9, 0x2F, 0xB6, 0x0B, 0xBF, 0x08, 0x9D, 0x55, 0x35, 0xB5, +0x93, 0x80, 0xE5, 0x14, 0x8F, 0x2C, 0xF7, 0x86, 0xE3, 0x7E, 0x74, 0x86, 0xFD, 0x14, 0xCA, 0x7B, +0x2B, 0x52, 0x3B, 0x3B, 0x79, 0x7D, 0xFB, 0xE7, 0x25, 0x63, 0x39, 0x71, 0xFE, 0xE4, 0x0D, 0x88, +0x46, 0xBF, 0x6D, 0xFE, 0xC5, 0x1B, 0x92, 0xF1, 0xB9, 0x2E, 0xE9, 0x54, 0x31, 0x32, 0xD8, 0xA2, +0x6B, 0x39, 0x12, 0xF4, 0x6E, 0x34, 0xFD, 0x64, 0x40, 0x86, 0x55, 0x95, 0x47, 0xAE, 0x84, 0x55, +0x2A, 0x6C, 0xCC, 0x66, 0xCD, 0x7F, 0xCC, 0xC0, 0xA5, 0xB7, 0xF9, 0x6D, 0xC9, 0x6F, 0x1D, 0x69, +0x58, 0x3D, 0xD4, 0x78, 0x3F, 0x69, 0x9D, 0x82, 0xE4, 0x94, 0x41, 0x13, 0x00, 0xCB, 0xD0, 0xBC, +0xF3, 0x8B, 0xB4, 0x2D, 0xAD, 0x97, 0x15, 0xED, 0xD7, 0x13, 0xCA, 0xE3, 0xF0, 0xB1, 0x4E, 0x24, +0x1C, 0x6E, 0xA1, 0x28, 0x3C, 0x31, 0x43, 0xFF, 0xC7, 0x27, 0xFD, 0x2A, 0x04, 0x01, 0xD1, 0x46, +0x19, 0xBA, 0x42, 0xF0, 0xBA, 0x9B, 0x5C, 0xE6, 0x7D, 0x3E, 0xDD, 0x4B, 0xDE, 0x59, 0xAE, 0x6F, +0xE0, 0xFE, 0xA1, 0x5F, 0x53, 0x01, 0xC9, 0x17, 0x64, 0x56, 0xE8, 0x33, 0x1D, 0x64, 0x51, 0xEF, +0xFD, 0x8A, 0x5E, 0x69, 0xF5, 0x1D, 0x75, 0x26, 0xA8, 0x02, 0xF5, 0x18, 0x70, 0x30, 0x81, 0xB3, +0xA2, 0x38, 0x24, 0x06, 0x1C, 0x1A, 0xF0, 0x21, 0xCF, 0xA0, 0x33, 0x36, 0xE0, 0xC8, 0x80, 0x49, +0x5C, 0x64, 0xBB, 0x83, 0x01, 0xC7, 0x13, 0x91, 0x90, 0xA4, 0xD1, 0x04, 0x3E, 0x4C, 0xE0, 0x04, +0xF5, 0x73, 0x4D, 0x7F, 0xE2, 0xF0, 0xE1, 0x4E, 0x3D, 0xC7, 0x85, 0x84, 0x47, 0xCB, 0x37, 0x7A, +0xBF, 0xBE, 0x54, 0x92, 0x09, 0x7E, 0x73, 0x64, 0x39, 0x8F, 0x9B, 0xE5, 0x13, 0x52, 0xAA, 0x2B, +0x1F, 0x1D, 0x79, 0x91, 0x73, 0xA0, 0xF7, 0x76, 0x97, 0x7D, 0x0E, 0xBC, 0x5B, 0xCD, 0xCD, 0x71, +0xAA, 0x2E, 0xD7, 0xDB, 0xCC, 0xCE, 0xF0, 0x32, 0x05, 0x55, 0x79, 0xAE, 0x3A, 0x80, 0x66, 0xF4, +0x77, 0x5B, 0x38, 0x7B, 0x16, 0x9E, 0x97, 0xA2, 0xB3, 0x18, 0xD9, 0xE8, 0x79, 0xF7, 0x52, 0x54, +0x6B, 0x90, 0xB6, 0x8B, 0xEF, 0xB3, 0xBE, 0x43, 0xDE, 0xF8, 0x40, 0x6D, 0x33, 0x2D, 0xCB, 0xCE, +0xC2, 0xD3, 0x5D, 0xAD, 0x1E, 0x91, 0xD7, 0xB7, 0xE8, 0x7D, 0x30, 0x54, 0x03, 0x02, 0xE2, 0x46, +0x96, 0x5E, 0x56, 0xB2, 0x2A, 0xFF, 0x08, 0x1B, 0x77, 0xA6, 0x63, 0x6F, 0xC4, 0xCD, 0x3F, 0xA2, +0xD7, 0x5B, 0xB1, 0x6B, 0x75, 0x1F, 0xAF, 0xF2, 0xBE, 0x6A, 0xF1, 0x55, 0xBB, 0xED, 0x93, 0x4C, +0x25, 0xC5, 0x8D, 0xFE, 0xAC, 0xF0, 0x12, 0x7B, 0x0B, 0xD8, 0xE4, 0x5C, 0x52, 0x70, 0x7D, 0xD0, +0xEB, 0xF0, 0x5F, 0x9D, 0xB6, 0xD7, 0xE1, 0xA4, 0x5A, 0xD5, 0x56, 0x07, 0xE1, 0xDA, 0x90, 0x30, +0x93, 0x50, 0xE3, 0x86, 0xBB, 0x54, 0x3D, 0x63, 0x45, 0x91, 0x5F, 0x15, 0xC5, 0x4C, 0x82, 0x86, +0xB2, 0xFE, 0x25, 0xED, 0xD7, 0x3D, 0x6D, 0x30, 0x55, 0xB0, 0x31, 0xE6, 0xAC, 0x46, 0x54, 0xFB, +0xE0, 0xBA, 0xDA, 0xE8, 0xBC, 0x6C, 0xD5, 0x93, 0x99, 0x4D, 0x37, 0xD5, 0x5C, 0xDD, 0xC4, 0xD8, +0xFC, 0xC7, 0x9D, 0x81, 0xAB, 0x9A, 0xE8, 0x55, 0x7B, 0x0A, 0x3A, 0x5B, 0x1D, 0x83, 0xE4, 0x69, +0xDA, 0x18, 0xC5, 0x67, 0x24, 0x64, 0x10, 0xC3, 0xD7, 0x98, 0x0B, 0xC2, 0x0A, 0x37, 0x25, 0x62, +0x63, 0x16, 0xE1, 0xD4, 0xF8, 0x16, 0x15, 0xB5, 0x4A, 0x9A, 0xF9, 0x46, 0x61, 0xB5, 0x64, 0x30, +0x0B, 0x6A, 0xC7, 0x1E, 0x0E, 0x76, 0x8A, 0x74, 0x12, 0x5B, 0x90, 0x50, 0x83, 0x0E, 0xCA, 0xFB, +0xC9, 0x30, 0x6F, 0x69, 0xCF, 0xF4, 0x98, 0xA4, 0x75, 0x6B, 0xCA, 0xEF, 0x82, 0x9B, 0x29, 0x76, +0x7E, 0x22, 0x68, 0x7D, 0xFC, 0xF7, 0xA5, 0x95, 0xAC, 0xF8, 0xF2, 0xCC, 0xFC, 0x37, 0x9E, 0x38, +0x91, 0x33, 0x5E, 0x58, 0xFC, 0x7D, 0xE0, 0xB8, 0x9C, 0x2B, 0x43, 0x63, 0x2A, 0x3C, 0x9B, 0xC8, +0x9E, 0xA2, 0x1F, 0x3A, 0x11, 0x47, 0xFD, 0x22, 0x68, 0x86, 0xA6, 0xF6, 0x01, 0x29, 0xDF, 0x42, +0x43, 0xEB, 0x7B, 0xBC, 0x95, 0x20, 0xB6, 0x8E, 0x21, 0x0A, 0x95, 0xFC, 0x26, 0xC8, 0x79, 0xE1, +0xD4, 0x2F, 0x42, 0x3A, 0x5C, 0x65, 0x9F, 0xB9, 0x05, 0x1A, 0xA4, 0x57, 0xFE, 0x33, 0xD0, 0x79, +0x6C, 0xD1, 0xE7, 0xE9, 0x64, 0x4D, 0x63, 0x38, 0xF1, 0x7D, 0x9C, 0xF0, 0xEC, 0x99, 0xB8, 0xF7, +0x01, 0x30, 0x3A, 0xFE, 0xF0, 0x85, 0x09, 0x20, 0x45, 0xD1, 0x59, 0x93, 0x88, 0x1E, 0xCC, 0x5F, +0xFB, 0xF8, 0x7E, 0xDD, 0xA4, 0xCD, 0x1D, 0xB0, 0xD6, 0xFA, 0xA4, 0x9D, 0x51, 0x39, 0x10, 0xAC, +0x15, 0x15, 0x50, 0x38, 0x6F, 0x44, 0x34, 0xB0, 0xD1, 0x59, 0x5D, 0xDC, 0xB2, 0x65, 0x0A, 0xEC, +0xC1, 0x32, 0x1C, 0xBC, 0x1E, 0xCC, 0x5D, 0x29, 0x93, 0xEB, 0xA4, 0xF8, 0x4A, 0xA4, 0xC4, 0x1D, +0xA4, 0x4E, 0x45, 0x97, 0x55, 0x94, 0x08, 0x18, 0xCF, 0x65, 0x39, 0xF2, 0x7D, 0x52, 0x0B, 0x8A, +0x38, 0x8E, 0xA2, 0x6D, 0x3F, 0x09, 0xF4, 0x04, 0xA3, 0xC8, 0x48, 0xB0, 0xEB, 0xA7, 0xAF, 0x15, +0xAF, 0x87, 0x85, 0x50, 0x5D, 0x59, 0xCF, 0x7F, 0xC6, 0x35, 0xD7, 0x7D, 0x9F, 0xBE, 0x95, 0x76, +0xAC, 0x21, 0x4E, 0xFC, 0xA8, 0x9D, 0x09, 0x8F, 0x46, 0x35, 0x06, 0x41, 0xA8, 0x6F, 0xA1, 0x13, +0x04, 0xB4, 0xE9, 0xAB, 0xAA, 0x77, 0xD7, 0xDF, 0x55, 0x97, 0x4E, 0x87, 0xFA, 0x82, 0x73, 0xD5, +0xAB, 0x19, 0x28, 0x72, 0x74, 0xAD, 0x02, 0xBB, 0x78, 0x29, 0xA4, 0xDA, 0x4F, 0xAC, 0xFF, 0x42, +0xA2, 0xAF, 0xFA, 0xF5, 0x27, 0x97, 0xF1, 0x9B, 0x86, 0x50, 0xD2, 0xAE, 0x27, 0xFC, 0x9E, 0x0C, +0x9F, 0x62, 0x96, 0x91, 0x92, 0xB8, 0xF3, 0x77, 0x93, 0x5F, 0xF8, 0x60, 0xAB, 0xBE, 0xF7, 0x4C, +0x73, 0x8D, 0xFB, 0x0B, 0x5F, 0x6B, 0x7F, 0x92, 0x4F, 0x91, 0xDB, 0x67, 0xA4, 0xB9, 0x92, 0xB6, +0x83, 0xC6, 0x0F, 0x2E, 0x0C, 0x59, 0x06, 0xF4, 0x22, 0x3F, 0x06, 0x04, 0x76, 0x75, 0x5D, 0xA7, +0xBC, 0x50, 0x6F, 0x9C, 0x3B, 0x06, 0xA8, 0xE0, 0xAE, 0x6B, 0xBE, 0xE0, 0xB5, 0x99, 0xA0, 0x50, +0xC9, 0x48, 0x93, 0x3B, 0x1F, 0x73, 0x62, 0x3A, 0xEC, 0x40, 0x5B, 0x6E, 0xA7, 0x5B, 0x43, 0x15, +0x28, 0xD0, 0x36, 0xD2, 0x6A, 0x15, 0x26, 0x26, 0x69, 0xCD, 0x49, 0xC4, 0xCA, 0x6C, 0x3B, 0x34, +0x0D, 0x42, 0xD0, 0x4A, 0xFD, 0x94, 0x57, 0xAC, 0x26, 0x0E, 0xE0, 0x35, 0x36, 0x0C, 0xF3, 0x28, +0x32, 0x0C, 0xA7, 0x5A, 0x23, 0x7A, 0x96, 0xC5, 0x05, 0xCF, 0x2E, 0x6D, 0x37, 0x0D, 0x95, 0x8F, +0x4D, 0xFC, 0x5C, 0x26, 0xFA, 0xFF, 0x02, 0x2B, 0x5F, 0xEC, 0x35, 0x85, 0x17, 0x00, 0x00 }; #endif // ifndef EMBED_ESPEASY_DEFAULT_MIN_CSS_USE_GZ #endif // ifdef EMBED_ESPEASY_DEFAULT_MIN_CSS diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index bbb8409b5..eaf39d92d 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -275,7 +275,25 @@ void WebServerInit() #endif // ifdef WEBSERVER_HARDWARE #ifdef WEBSERVER_INTERFACES web_server.on(F("/interfaces"), handle_interfaces); - #endif +#if FEATURE_I2C + web_server.on(F("/interfaces_i2c"), handle_interfaces_i2c); +#endif // if FEATURE_I2C +#if FEATURE_SPI + web_server.on(F("/interfaces_spi"), handle_interfaces_spi); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS + web_server.on(F("/interfaces_modbus"), handle_interfaces_modbus); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + web_server.on(F("/interfaces_can"), handle_interfaces_can); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + web_server.on(F("/interfaces_wrmbus"), handle_interfaces_wrmbus); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + web_server.on(F("/interfaces_wimbus"), handle_interfaces_wimbus); +#endif // if FEATURE_WIMBUS + #endif // ifdef WEBSERVER_INTERFACES #ifdef WEBSERVER_I2C_SCANNER web_server.on(F("/i2cscanner"), handle_i2cscanner); #endif // ifdef WEBSERVER_I2C_SCANNER @@ -498,7 +516,7 @@ void getWebPageTemplateDefault(const String& tmplName, WebTemplateParser& parser getWebPageTemplateDefaultHead(parser, addMeta, addJS); if (!parser.isTail()) { - parser.process(F("

      ESP Easy Mega: ")); + parser.process(strformat(F("

      ESP Easy Mega: "), isGpMenuSecondLevel(navMenuIndex) ? '2' : ' ')); parser.process(title); # if BUILD_IN_WEBHEADER parser.process(F( diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 208d02cb5..da114daaa 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -16,6 +16,7 @@ #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Hardware_GPIO.h" +#include "../Helpers/I2C_access.h" #include "../Helpers/SPI_Helper.h" #include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" @@ -39,6 +40,11 @@ void handle_hardware() { Settings.Pin_status_led = getFormItemInt(F("pled")); Settings.Pin_status_led_Inversed = isFormItemChecked(F("pledi")); Settings.Pin_Reset = getFormItemInt(F("pres")); + #if FEATURE_I2C_MULTIPLE + if (isFormItem(F("pi2cbuspcf"))) { + set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); + } + #endif // if FEATURE_I2C_MULTIPLE #if defined(ESP32) && FEATURE_SD Settings.setSPIBusForSDCard(getFormItemInt(F("sdspibus"), 0)); #endif // if defined(ESP32) && FEATURE_SD @@ -78,6 +84,20 @@ void handle_hardware() { addFormPinSelect(PinSelectPurpose::Reset_pin, formatGpioName_input(F("Switch")), F("pres"), Settings.Pin_Reset); addFormNote(F("Press about 10s for factory reset")); +# if FEATURE_I2C_MULTIPLE + const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); + + if (i2cMaxBusCount > 1) { + addFormSubHeader(F("PCF & MCP Direct I/O")); + const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP(); + I2CInterfaceSelector(F("I2C Bus"), + F("pi2cbuspcf"), + i2cBus, + false); + + } +# endif // if FEATURE_I2C_MULTIPLE + #if FEATURE_SD addFormSubHeader(F("SD Card")); #ifdef ESP32 diff --git a/src/src/WebServer/InterfacesPage.cpp b/src/src/WebServer/InterfacesPage.cpp index 6b629e72d..641026091 100644 --- a/src/src/WebServer/InterfacesPage.cpp +++ b/src/src/WebServer/InterfacesPage.cpp @@ -30,13 +30,71 @@ // ******************************************************************************** // Web Interface hardware page // ******************************************************************************** +#if FEATURE_I2C +void handle_interfaces_i2c() { + navMenuIndex = MENU_INDEX_INTERFACES_I2C; + handle_interfaces(); +} +#endif // if FEATURE_I2C + +#if FEATURE_SPI +void handle_interfaces_spi() { + navMenuIndex = MENU_INDEX_INTERFACES_SPI; + handle_interfaces(); +} +#endif // if FEATURE_SPI + +#if FEATURE_MODBUS +void handle_interfaces_modbus() { + navMenuIndex = MENU_INDEX_INTERFACES_MODBUS; + handle_interfaces(); +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +void handle_interfaces_can() { + navMenuIndex = MENU_INDEX_INTERFACES_CAN; + handle_interfaces(); +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +void handle_interfaces_wrmbus() { + navMenuIndex = MENU_INDEX_INTERFACES_WRMBUS; + handle_interfaces(); +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +void handle_interfaces_wimbus() { + navMenuIndex = MENU_INDEX_INTERFACES_WIMBUS; + handle_interfaces(); +} +#endif // if FEATURE_WIMBUS + void handle_interfaces() { # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("handle_interfaces")); # endif if (!isLoggedIn()) { return; } - navMenuIndex = MENU_INDEX_INTERFACES; + if ((navMenuIndex != MENU_INDEX_INTERFACES) && (navMenuIndex <= MENU_MAX_INDEX_SHOWN)) { +#if FEATURE_I2C + navMenuIndex = MENU_INDEX_INTERFACES_I2C; +#elif FEATURE_SPI + navMenuIndex = MENU_INDEX_INTERFACES_SPI; +#elif FEATURE_MODBUS + navMenuIndex = MENU_INDEX_INTERFACES_MODBUS; +#elif FEATURE_CAN + navMenuIndex = MENU_INDEX_INTERFACES_CAN; +#elif FEATURE_WRMBUS + navMenuIndex = MENU_INDEX_INTERFACES_WRMBUS; +#elif FEATURE_WIMBUS + navMenuIndex = MENU_INDEX_INTERFACES_WIMBUS; +#else + navMenuIndex = MENU_INDEX_INTERFACES; +#endif + } TXBuffer.startStream(); sendHeadandTail_stdtemplate(_HEAD); @@ -45,15 +103,43 @@ void handle_interfaces() { addHtml(F("")); html_table_class_normal(); - addFormHeader(F("Interfaces Settings"), F(""), F("Interfaces/Interfaces.html")); + addFormHeader(strformat(F("%s Interfaces Settings"), FsP(getGpMenuIcon(navMenuIndex))), F(""), F("Interfaces/Interfaces.html")); #if FEATURE_I2C - interfaces_show_I2C(); + if (navMenuIndex == MENU_INDEX_INTERFACES_I2C) { + interfaces_show_I2C(); + } #endif + #if FEATURE_SPI - interfaces_show_SPI(); + if (navMenuIndex == MENU_INDEX_INTERFACES_SPI) { + interfaces_show_SPI(); + } #endif + #if FEATURE_MODBUS + if (navMenuIndex == MENU_INDEX_INTERFACES_MODBUS) { + interfaces_show_MODBUS(); + } + #endif // if FEATURE_MODBUS + + #if FEATURE_CAN + if (navMenuIndex == MENU_INDEX_INTERFACES_CAN) { + interfaces_show_CAN(); + } + #endif // if FEATURE_CAN + + #if FEATURE_WRMBUS + if (navMenuIndex == MENU_INDEX_INTERFACES_WRMBUS) { + interfaces_show_WRMBUS(); + } + #endif // if FEATURE_WRMBUS + + #if FEATURE_WIMBUS + if (navMenuIndex == MENU_INDEX_INTERFACES_WIMBUS) { + interfaces_show_WIMBUS(); + } + #endif // if FEATURE_WIMBUS addFormSeparator(2); @@ -75,13 +161,29 @@ void save_interfaces() { #if FEATURE_I2C - if (save_I2C(error)) { updated = true; } + if ((navMenuIndex == MENU_INDEX_INTERFACES_I2C) && save_I2C(error)) { updated = true; } #endif #if FEATURE_SPI - if (save_SPI(error)) { updated = true; } + if ((navMenuIndex == MENU_INDEX_INTERFACES_SPI) && save_SPI(error)) { updated = true; } #endif + #if FEATURE_MODBUS + if ((navMenuIndex == MENU_INDEX_INTERFACES_MODBUS) && save_MODBUS(error)) { updated = true; } + #endif // if FEATURE_MODBUS + + #if FEATURE_CAN + if ((navMenuIndex == MENU_INDEX_INTERFACES_CAN) && save_CAN(error)) { updated = true; } + #endif // if FEATURE_CAN + + #if FEATURE_WRMBUS + if ((navMenuIndex == MENU_INDEX_INTERFACES_WRMBUS) && save_WRMBUS(error)) { updated = true; } + #endif // if FEATURE_WRMBUS + + #if FEATURE_WIMBUS + if ((navMenuIndex == MENU_INDEX_INTERFACES_WIMBUS) && save_WIMBUS(error)) { updated = true; } + #endif // if FEATURE_WIMBUS + if (updated) { error += SaveSettings(); addHtmlError(error); @@ -184,10 +286,6 @@ bool save_I2C(String& error) { } # endif // if FEATURE_I2C_INTERFACE_3 - if (isFormItem(F("pi2cbuspcf"))) { - updated = true; - set3BitToUL(Settings.I2C_peripheral_bus, I2C_PERIPHERAL_BUS_PCFMCP, getFormItemInt(F("pi2cbuspcf"))); - } # endif // if FEATURE_I2C_MULTIPLE return updated; @@ -236,6 +334,30 @@ bool save_SPI(String& error) { #endif +#if FEATURE_MODBUS +bool save_MODBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +bool save_CAN(String& error) { + return false; // TODO +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +bool save_WRMBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +bool save_WIMBUS(String& error) { + return false; // TODO +} +#endif // if FEATURE_WIMBUS + #if FEATURE_I2C void interfaces_show_I2C() { # if FEATURE_I2CMULTIPLEXER @@ -341,19 +463,6 @@ void interfaces_show_I2C() { } # endif // if FEATURE_I2CMULTIPLEXER } -# if FEATURE_I2C_MULTIPLE - const uint8_t i2cMaxBusCount = Settings.getNrConfiguredI2C_buses(); - - if (i2cMaxBusCount > 1) { - addFormSubHeader(F("PCF & MCP Direct I/O")); - const uint8_t i2cBus = Settings.getI2CInterfacePCFMCP(); - I2CInterfaceSelector(F("I2C Bus"), - F("pi2cbuspcf"), - i2cBus, - false); - - } -# endif // if FEATURE_I2C_MULTIPLE } #endif @@ -371,7 +480,7 @@ void interfaces_show_SPI() { // 'none';document.getElementById('tr_spipinsclk').style.display = spipinstyle;document.getElementById('tr_spipinmiso').style.display // = spipinstyle;document.getElementById('tr_spipinmosi').style.display = spipinstyle;}"), // Minified: - html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9==e.value?'':'none';" + html_add_script(strformat(F("function spi%uOptionChanged(e){var i=9<=e.value?'':'none';" "document.getElementById('tr_spipinsclk%u').style.display=i," "document.getElementById('tr_spipinmiso%u').style.display=i," "document.getElementById('tr_spipinmosi%u').style.display=i" @@ -425,6 +534,30 @@ void interfaces_show_SPI() { } #endif +#if FEATURE_MODBUS +void interfaces_show_MODBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_MODBUS + +#if FEATURE_CAN +void interfaces_show_CAN() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_CAN + +#if FEATURE_WRMBUS +void interfaces_show_WRMBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_WRMBUS + +#if FEATURE_WIMBUS +void interfaces_show_WIMBUS() { + addRowLabel(F("TODO")); // TODO +} +#endif // if FEATURE_WIMBUS + #if FEATURE_I2C # if FEATURE_PLUGIN_PRIORITY diff --git a/src/src/WebServer/InterfacesPage.h b/src/src/WebServer/InterfacesPage.h index d62c9aea1..85305bb66 100644 --- a/src/src/WebServer/InterfacesPage.h +++ b/src/src/WebServer/InterfacesPage.h @@ -11,14 +11,41 @@ void handle_interfaces(); void save_interfaces(); #if FEATURE_I2C +void handle_interfaces_i2c(); bool save_I2C(String& error); void interfaces_show_I2C(); #endif #if FEATURE_SPI +void handle_interfaces_spi(); bool save_SPI(String& error); void interfaces_show_SPI(); #endif +#if defined(FEATURE_MODBUS) && FEATURE_MODBUS +void handle_interfaces_modbus(); +bool save_MODBUS(String& error); +void interfaces_show_MODBUS(); +#endif // if defined(FEATURE_MODBUS) && FEATURE_MODBUS + +#if defined(FEATURE_CAN) && FEATURE_CAN +void handle_interfaces_can(); +bool save_CAN(String& error); +void interfaces_show_CAN(); +#endif // if defined(FEATURE_CAN) && FEATURE_CAN + +#if defined(FEATURE_WRMBUS) && FEATURE_WRMBUS +void handle_interfaces_wrmbus(); +bool save_WRMBUS(String& error); +void interfaces_show_WRMBUS(); +#endif // if defined(FEATURE_WRMBUS) && FEATURE_WRMBUS + +#if defined(FEATURE_WIMBUS) && FEATURE_WIMBUS +void handle_interfaces_wimbus(); +bool save_WIMBUS(String& error); +void interfaces_show_WIMBUS(); +#endif // if defined(FEATURE_WIMBUS) && FEATURE_WIMBUS + + #if FEATURE_I2C # if FEATURE_PLUGIN_PRIORITY bool isI2CPriorityTaskActive(uint8_t i2cBus); diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index c4952aad7..e732b947a 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -539,6 +539,15 @@ void addFormHeader(const __FlashStringHelper *header, html_table_header(F("")); } +void addFormHeader(const String& header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton) +{ + html_TR(); + html_table_header(header, helpButton, rtdHelpButton, 300); + html_table_header(F("")); +} + /* void addFormHeader(const String& header, const String& helpButton) { addFormHeader(header, helpButton, EMPTY_STRING); diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index cf2720a1b..4736416bc 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -170,6 +170,9 @@ void addFormHeader(const __FlashStringHelper *header, void addFormHeader(const __FlashStringHelper *header, const __FlashStringHelper *helpButton, const __FlashStringHelper *rtdHelpButton); +void addFormHeader(const String& header, + const __FlashStringHelper *helpButton, + const __FlashStringHelper *rtdHelpButton); /* void addFormHeader(const String& header, diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index 7ae539c4e..8934dd79d 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -116,6 +116,24 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { case MENU_INDEX_CONTROLLERS: return ICON("✆"); case MENU_INDEX_HARDWARE: return ICON("☷"); case MENU_INDEX_INTERFACES: return ICON("❉"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: return ICON("I²C"); +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: return ICON("SPI"); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS + case MENU_INDEX_INTERFACES_MODBUS: return ICON("Modbus"); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: return ICON("CAN bus"); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return ICON("mBus"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return ICON("w-mBus"); +#endif // if FEATURE_WIMBUS case MENU_INDEX_DEVICES: return ICON("❖"); case MENU_INDEX_RULES: return ICON("⧴"); case MENU_INDEX_NOTIFICATIONS: return ICON("✉"); @@ -126,16 +144,37 @@ const __FlashStringHelper* getGpMenuIcon(uint8_t index) { const __FlashStringHelper* getGpMenuLabel(uint8_t index) { switch (index) { - case MENU_INDEX_MAIN: return F("Main"); - case MENU_INDEX_CONFIG: return F("Config"); - case MENU_INDEX_NETWORK: return F("Network"); - case MENU_INDEX_CONTROLLERS: return F("Controllers"); - case MENU_INDEX_HARDWARE: return F("Hardware"); - case MENU_INDEX_INTERFACES:return F("Interfaces"); - case MENU_INDEX_DEVICES: return F("Devices"); - case MENU_INDEX_RULES: return F("Rules"); - case MENU_INDEX_NOTIFICATIONS: return F("Notifications"); - case MENU_INDEX_TOOLS: return F("Tools"); + case MENU_INDEX_MAIN: return F("Main"); + case MENU_INDEX_CONFIG: return F("Config"); + case MENU_INDEX_NETWORK: return F("Network"); + case MENU_INDEX_CONTROLLERS: return F("Controllers"); + case MENU_INDEX_HARDWARE: return F("Hardware"); + case MENU_INDEX_INTERFACES: return F("Interfaces"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: +#endif // if FEATURE_SPI +#if FEATURE_MODBUS + case MENU_INDEX_INTERFACES_MODBUS: +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: +#endif // if FEATURE_CAN +#if FEATURE_I2C || FEATURE_SPI || FEATURE_MODBUS || FEATURE_CAN + break; // No label, only an 'icon', for the second-level menu +#endif // if FEATURE_I2C || FEATURE_SPI || FEATURE_MODBUS || FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return F("(wired)"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return F("(wireless)"); +#endif // if FEATURE_WIMBUS + case MENU_INDEX_DEVICES: return F("Devices"); + case MENU_INDEX_RULES: return F("Rules"); + case MENU_INDEX_NOTIFICATIONS: return F("Notifications"); + case MENU_INDEX_TOOLS: return F("Tools"); } return F(""); } @@ -148,6 +187,24 @@ const __FlashStringHelper* getGpMenuURL(uint8_t index) { case MENU_INDEX_CONTROLLERS: return F("/controllers"); case MENU_INDEX_HARDWARE: return F("/hardware"); case MENU_INDEX_INTERFACES: return F("/interfaces"); +#if FEATURE_I2C + case MENU_INDEX_INTERFACES_I2C: return F("/interfaces_i2c"); +#endif // if FEATURE_I2C +#if FEATURE_SPI + case MENU_INDEX_INTERFACES_SPI: return F("/interfaces_spi"); +#endif // if FEATURE_SPI +#if FEATURE_MODBUS + case MENU_INDEX_INTERFACES_MODBUS: return F("/interfaces_modbus"); +#endif // if FEATURE_MODBUS +#if FEATURE_CAN + case MENU_INDEX_INTERFACES_CAN: return F("/interfaces_can"); +#endif // if FEATURE_CAN +#if FEATURE_WRMBUS + case MENU_INDEX_INTERFACES_WRMBUS: return F("/interfaces_wrmbus"); +#endif // if FEATURE_WRMBUS +#if FEATURE_WIMBUS + case MENU_INDEX_INTERFACES_WIMBUS: return F("/interfaces_wimbus"); +#endif // if FEATURE_WIMBUS case MENU_INDEX_DEVICES: return F("/devices"); case MENU_INDEX_RULES: return F("/rules"); case MENU_INDEX_NOTIFICATIONS: return F("/notifications"); @@ -164,6 +221,18 @@ bool GpMenuVisible(uint8_t index) { case MENU_INDEX_CONTROLLERS: return MENU_INDEX_CONTROLLERS_VISIBLE; case MENU_INDEX_HARDWARE: return MENU_INDEX_HARDWARE_VISIBLE; case MENU_INDEX_INTERFACES: return MENU_INDEX_INTERFACES_VISIBLE; + case MENU_INDEX_INTERFACES_I2C: return (1 == FEATURE_I2C); + case MENU_INDEX_INTERFACES_SPI: return (1 == FEATURE_SPI); + case MENU_INDEX_INTERFACES_MODBUS: return (1 == FEATURE_MODBUS); + #if defined(FEATURE_CAN) + case MENU_INDEX_INTERFACES_CAN: return (1 == FEATURE_CAN); + #endif // if defined(FEATURE_CAN) + #if defined(FEATURE_WRMBUS) + case MENU_INDEX_INTERFACES_WRMBUS: return (1 == FEATURE_WRMBUS); + #endif // if defined(FEATURE_WRMBUS) + #if defined(FEATURE_WIMBUS) + case MENU_INDEX_INTERFACES_WIMBUS: return (1 == FEATURE_WIMBUS); + #endif // if defined(FEATURE_WIMBUS) case MENU_INDEX_DEVICES: return MENU_INDEX_DEVICES_VISIBLE; case MENU_INDEX_RULES: return MENU_INDEX_RULES_VISIBLE; case MENU_INDEX_NOTIFICATIONS: return MENU_INDEX_NOTIFICATIONS_VISIBLE; @@ -172,6 +241,28 @@ bool GpMenuVisible(uint8_t index) { return false; } +bool isGpMenuSecondLevel(uint8_t index) { + switch (index) { + case MENU_INDEX_MAIN: + case MENU_INDEX_CONFIG: + case MENU_INDEX_NETWORK: + case MENU_INDEX_CONTROLLERS: + case MENU_INDEX_HARDWARE: + case MENU_INDEX_INTERFACES: + case MENU_INDEX_DEVICES: + case MENU_INDEX_RULES: + case MENU_INDEX_NOTIFICATIONS: + case MENU_INDEX_TOOLS: return false; + case MENU_INDEX_INTERFACES_WIMBUS: + case MENU_INDEX_INTERFACES_WRMBUS: + case MENU_INDEX_INTERFACES_CAN: + case MENU_INDEX_INTERFACES_MODBUS: + case MENU_INDEX_INTERFACES_SPI: + case MENU_INDEX_INTERFACES_I2C: return true; + } + return false; +} + bool WebTemplateParser::process(const char c) { switch (c) { case '{': @@ -416,9 +507,20 @@ void WebTemplateParser::getWebPageTemplateVar(const String& varName) if (equals(varName, F("menu"))) { - addHtml(F("