From 6dfd71a4a05cbede14b2d467c69f0c843d9ccbfa Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 11 Sep 2026 18:14:19 +0200 Subject: [PATCH] [Build] Reduce ESP8266 and ESP32-C6 DisplayB build size --- lib/Adafruit_BME680/Adafruit_BME680.cpp | 2 +- lib/Adafruit_BMP3XX/Adafruit_BMP3XX.cpp | 2 +- .../examples/colorview/colorview.ino | 2 +- lib/NeoPixelBus/src/internal/NeoEase.h | 14 +- .../src/SparkFun_ADXL345.cpp | 3 +- platformio_esp32c6_envs.ini | 1 + platformio_esp82xx_envs.ini | 1 + .../NW004_data_struct_ETH_SPI.cpp | 8 +- .../NW005_data_struct_PPP_modem.cpp | 12 +- src/_P121_HMC5883L.ino | 12 +- src/src/CustomBuild/define_plugin_sets.h | 1 + src/src/DataStructs/ValueStruct.cpp | 135 +++++++++++++++--- src/src/DataStructs/ValueStruct.h | 82 +++++++---- src/src/Helpers/ESPEasy_math.cpp | 13 ++ src/src/Helpers/ESPEasy_math.h | 122 ++++++++++++---- src/src/Helpers/KeyValueWriter_JSON.cpp | 3 + src/src/Helpers/Numerical.cpp | 7 +- src/src/Helpers/Numerical.h | 2 + src/src/Helpers/StringConverter.cpp | 2 +- src/src/Helpers/SystemVariables.cpp | 10 +- src/src/Helpers/_ESPEasy_key_value_store.cpp | 12 +- src/src/PluginStructs/P073_data_struct.cpp | 4 +- src/src/PluginStructs/P157_data_struct.cpp | 4 +- .../ESPEasy_key_value_store_webform.cpp | 16 +-- .../ESPEasy_key_value_store_webform.h | 8 +- 25 files changed, 352 insertions(+), 126 deletions(-) diff --git a/lib/Adafruit_BME680/Adafruit_BME680.cpp b/lib/Adafruit_BME680/Adafruit_BME680.cpp index 0f714dd55..640e22219 100644 --- a/lib/Adafruit_BME680/Adafruit_BME680.cpp +++ b/lib/Adafruit_BME680/Adafruit_BME680.cpp @@ -278,7 +278,7 @@ float Adafruit_BME680::readAltitude(float seaLevel) { // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 float atmospheric = readPressure() / 100.0F; - return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); + return 44330.0f * (1.0f - powf(atmospheric / seaLevel, 0.1903f)); } /*! diff --git a/lib/Adafruit_BMP3XX/Adafruit_BMP3XX.cpp b/lib/Adafruit_BMP3XX/Adafruit_BMP3XX.cpp index 9cfedee56..29e478709 100644 --- a/lib/Adafruit_BMP3XX/Adafruit_BMP3XX.cpp +++ b/lib/Adafruit_BMP3XX/Adafruit_BMP3XX.cpp @@ -291,7 +291,7 @@ float Adafruit_BMP3XX::readAltitude(float seaLevel) { // http://forums.adafruit.com/viewtopic.php?f=22&t=58064 float atmospheric = readPressure() / 100.0F; - return 44330.0 * (1.0 - pow(atmospheric / seaLevel, 0.1903)); + return 44330.0f * (1.0f - powf(atmospheric / seaLevel, 0.1903f)); } /**************************************************************************/ diff --git a/lib/Adafruit_TCS34725/examples/colorview/colorview.ino b/lib/Adafruit_TCS34725/examples/colorview/colorview.ino index 15b981a6b..b319b7440 100644 --- a/lib/Adafruit_TCS34725/examples/colorview/colorview.ino +++ b/lib/Adafruit_TCS34725/examples/colorview/colorview.ino @@ -39,7 +39,7 @@ void setup() { for (int i=0; i<256; i++) { float x = i; x /= 255; - x = pow(x, 2.5); + x = powf(x, 2.5f); x *= 255; if (commonAnode) { diff --git a/lib/NeoPixelBus/src/internal/NeoEase.h b/lib/NeoPixelBus/src/internal/NeoEase.h index 77a346075..19626e875 100644 --- a/lib/NeoPixelBus/src/internal/NeoEase.h +++ b/lib/NeoPixelBus/src/internal/NeoEase.h @@ -218,12 +218,12 @@ public: static float ExponentialIn(float unitValue) { - return (pow(2, 10.0f * (unitValue - 1.0f))); + return (powf(2, 10.0f * (unitValue - 1.0f))); } static float ExponentialOut(float unitValue) { - return (-pow(2, -10.0f * unitValue) + 1.0f); + return (-powf(2, -10.0f * unitValue) + 1.0f); } static float ExponentialInOut(float unitValue) @@ -231,12 +231,12 @@ public: unitValue *= 2.0f; if (unitValue < 1.0f) { - return (0.5f * pow(2, 10.0f * (unitValue - 1.0f))); + return (0.5f * powf(2, 10.0f * (unitValue - 1.0f))); } else { unitValue -= 1.0f; - return (0.5f * (-pow(2, -10.0f * unitValue) + 2.0f)); + return (0.5f * (-powf(2, -10.0f * unitValue) + 2.0f)); } } @@ -245,12 +245,12 @@ public: unitValue *= 2.0f; if (unitValue < 1.0f) { - return (0.5f * (-pow(2, -10.0f * unitValue) + 1.0f)); + return (0.5f * (-powf(2, -10.0f * unitValue) + 1.0f)); } else { unitValue -= 2.0f; - return (0.5f * (pow(2, 10.0f * unitValue) + 1.0f)); + return (0.5f * (powf(2, 10.0f * unitValue) + 1.0f)); } } @@ -308,6 +308,6 @@ public: static float Gamma(float unitValue) { - return pow(unitValue, 1.0f / 0.45f); + return powf(unitValue, 1.0f / 0.45f); } }; \ No newline at end of file diff --git a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp index 4ebfe0415..3632c56f6 100644 --- a/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp +++ b/lib/SparkFun_ADXL345_Arduino_Library/src/SparkFun_ADXL345.cpp @@ -703,7 +703,8 @@ double ADXL345::getRate() { readFrom(ADXL345_BW_RATE, 1, &_b); _b &= 0b00001111; - return (pow(2, ((int)_b) - 6)) * 6.25; + return (2 << (int)_b) * 0.09765625; +// return (pow(2, ((int)_b) - 6)) * 6.25; } void ADXL345::setRate(double rate) { diff --git a/platformio_esp32c6_envs.ini b/platformio_esp32c6_envs.ini index b73a2ac82..dfa8e9403 100644 --- a/platformio_esp32c6_envs.ini +++ b/platformio_esp32c6_envs.ini @@ -51,6 +51,7 @@ build_flags = ${esp32c6_common_LittleFS.build_flags} -D PLUGIN_DISPLAY_B_COLLECTION -D ST7789_EXTRA_INIT=1 -D P116_EXTRA_ST7789=1 + -D FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE=0 [env:climate_A_ESP32c6_4M316k] extends = esp32c6_common_LittleFS diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 9f6b301f8..fc8eb0b7a 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -288,6 +288,7 @@ platform = ${normal.platform} platform_packages = ${normal.platform_packages} build_flags = ${normal.build_flags} ${esp8266_4M1M.build_flags} + -D FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE=0 lib_ignore = ${normal.lib_ignore} [env:normal_ESP8266_4M1M_VCC] 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 a712039a3..ae827488e 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW004_data_struct_ETH_SPI.cpp @@ -229,15 +229,15 @@ void NW004_data_struct_ETH_SPI::webform_load(EventStruct *event) FormSelectorOptions selector(NR_ELEMENTS(ids), options, ids); auto params = NW004_makeWebFormItemParams(NW004_KEY_ETH_PHY_TYPE); - params._defaultIntValue = static_cast(EthPhyType_t::notSet); - showFormSelector(*_kvs, selector, params); + params._defaultValue.setInt(static_cast(EthPhyType_t::notSet)); + showFormSelector(*_kvs, selector, std::move(params)); } { auto params = NW004_makeWebFormItemParams(NW004_KEY_ETH_PHY_ADDR); params._min = -1; params._max = 127; - params._defaultIntValue = 1; - showWebformItem(*_kvs, params); + params._defaultValue.setInt(1); + showWebformItem(*_kvs, std::move(params)); addFormNote(F("I²C-address of Ethernet PHY")); } { 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 87e7793b7..03bf905d6 100644 --- a/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp +++ b/src/ESPEasy/net/NWPluginStructs/NW005_data_struct_PPP_modem.cpp @@ -540,8 +540,8 @@ void NW005_data_struct_PPP_modem::webform_load(EventStruct *event) { auto params = NW005_makeWebFormItemParams(NW005_KEY_PIN_RESET_DELAY); params._max = 2000; - params._defaultIntValue = 200; - showWebformItem(*_kvs, params); + params._defaultValue.setInt(200); + showWebformItem(*_kvs, std::move(params)); addUnit(F("ms")); } @@ -564,21 +564,21 @@ void NW005_data_struct_PPP_modem::webform_load(EventStruct *event) { auto params = NW005_makeWebFormItemParams(NW005_KEY_BAUDRATE); params._max = 10000000; - params._defaultIntValue = 115200; - showWebformItem(*_kvs, params); + params._defaultValue.setInt(115200); + showWebformItem(*_kvs, std::move(params)); } addFormSubHeader(F("Connection Settings")); { auto params = NW005_makeWebFormItemParams(NW005_KEY_APN); params._maxLength = 64; - showWebformItem(*_kvs, params); + showWebformItem(*_kvs, std::move(params)); addFormNote(F("Optional, can be left empty")); } { auto params = NW005_makeWebFormItemParams(NW005_KEY_SIM_PIN); params._maxLength = 4; - showWebformItem(*_kvs, params); + showWebformItem(*_kvs, std::move(params)); addFormNote(F("Only numerical digits")); } diff --git a/src/_P121_HMC5883L.ino b/src/_P121_HMC5883L.ino index db03bd953..e42c0fc33 100644 --- a/src/_P121_HMC5883L.ino +++ b/src/_P121_HMC5883L.ino @@ -151,22 +151,22 @@ boolean Plugin_121(uint8_t function, struct EventStruct *event, String& string) UserVar.setFloat(event->TaskIndex, 1, s_event.magnetic.y); UserVar.setFloat(event->TaskIndex, 2, s_event.magnetic.z); - double heading = atan2(s_event.magnetic.y, s_event.magnetic.x); + ESPEASY_RULES_FLOAT_TYPE heading = atan2(s_event.magnetic.y, s_event.magnetic.x); - const double decl = PCONFIG_FLOAT(1); + const ESPEASY_RULES_FLOAT_TYPE decl = PCONFIG_FLOAT(1); if (!essentiallyZero(decl)) { heading += decl; } if (definitelyLessThan(heading, 0)) { - heading += TWO_PI; + heading += (ESPEASY_RULES_FLOAT_TYPE)TWO_PI; } else - if (definitelyGreaterThan(heading, TWO_PI)) { - heading -= TWO_PI; + if (definitelyGreaterThan(heading, (ESPEASY_RULES_FLOAT_TYPE)TWO_PI)) { + heading -= (ESPEASY_RULES_FLOAT_TYPE)TWO_PI; } - UserVar.setFloat(event->TaskIndex, 3, heading * M_PI_180); + UserVar.setFloat(event->TaskIndex, 3, heading * (ESPEASY_RULES_FLOAT_TYPE)M_PI_180); success = true; // Assume we want to send out values to controllers } diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 6df6bed0d..a88f84ceb 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -4114,6 +4114,7 @@ To create/register a plugin, you have to : // ESPEASY_RULES_FLOAT_TYPE should be either double (default) or float. // It is solely based on FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +// This can save about 9+ kByte in build size to use only float types #ifdef ESPEASY_RULES_FLOAT_TYPE #undef ESPEASY_RULES_FLOAT_TYPE #endif diff --git a/src/src/DataStructs/ValueStruct.cpp b/src/src/DataStructs/ValueStruct.cpp index af968ed3e..4af5b93d0 100644 --- a/src/src/DataStructs/ValueStruct.cpp +++ b/src/src/DataStructs/ValueStruct.cpp @@ -6,9 +6,9 @@ #include "../Helpers/StringConverter.h" #include "../Helpers/StringConverter_Numerical.h" -#define VALUE_STRUCT_SSO_FIRST_CHAR_INDEX 1 +#define VALUE_STRUCT_SSO_FIRST_CHAR_INDEX 2 #define VALUE_STRUCT_SSO_FIRST_CHAR bytes_all[VALUE_STRUCT_SSO_FIRST_CHAR_INDEX] -#define VALUE_STRUCT_SSO_MAX_SIZE 14 +#define VALUE_STRUCT_SSO_MAX_SIZE 13 const ValueStruct INVALID_VALUESTRUCT{}; @@ -17,9 +17,10 @@ const ValueStruct INVALID_VALUESTRUCT{}; // ******************************************************************************** ValueStruct::~ValueStruct() { - if (!_isSSO && - (static_cast(_valueType) == ValueStruct::ValueType::String) - && (str_val != nullptr)) { + if (!_isSSO && (str_val != nullptr) && + ((static_cast(_valueType) == ValueStruct::ValueType::String) || + (static_cast(_valueType) == ValueStruct::ValueType::IP)) + ) { free(str_val); } } @@ -49,6 +50,37 @@ ValueStruct ValueStruct::makeBinFormatted(uint64_t val, uint8_t minNrDigits) return res; } +void ValueStruct::setInt(const uint64_t& val) { this->operator=(ValueStruct(val)); } + +void ValueStruct::setInt(const int64_t& val) { this->operator=(ValueStruct(val)); } + +void ValueStruct::setInt(int val) { this->operator=(ValueStruct(val)); } + +#if defined(ESP32) && !defined(__riscv) + +void ValueStruct::setInt(int32_t val) { this->operator=(ValueStruct(val)); } + +#endif // if defined(ESP32) && !defined(__riscv) + +void ValueStruct::setInt(uint32_t val) { this->operator=(ValueStruct(val)); } + +#if defined(ESP32) && !defined(__riscv) + +void ValueStruct::setInt(size_t val) { this->operator=(ValueStruct(val)); } + +#endif // if defined(ESP32) && !defined(__riscv) + +void ValueStruct::setIPAddress(const IPAddress& ip) +{ + #ifdef ESP8266 + this->operator=(ValueStruct(ip.toString())); + #endif + #ifdef ESP32 + this->operator=(ValueStruct(ip.toString(true))); + #endif + _valueType = (uint64_t)ValueStruct::ValueType::IP; +} + void ValueStruct::setPreferredFormat(ValueStruct::PreferredFormat format) { const auto vtype = getValueType(); @@ -64,7 +96,7 @@ void ValueStruct::setCaseFormat(ValueStruct::CaseFormat caseFormat) { _caseForma void ValueStruct::clear() { if (!_isSSO - && (getValueType() == ValueStruct::ValueType::String) + && ((getValueType() == ValueStruct::ValueType::String) || (getValueType() == ValueStruct::ValueType::IP)) && (str_val != nullptr)) { free(str_val); } @@ -83,6 +115,16 @@ ValueStruct& ValueStruct::operator=(ValueStruct&& rhs) ValueStruct& ValueStruct::deepCopy(const ValueStruct& rhs) { + if (!rhs._isSSO && (rhs.getValueType() == ValueStruct::ValueType::IP)) + { + IPAddress ip; + + if (rhs.toIPAddress(ip)) { + this->setIPAddress(ip); + } + return *this; + } + if (!rhs._isSSO && (rhs.getValueType() == ValueStruct::ValueType::String)) { this->operator=(ValueStruct(rhs.toString())); @@ -100,8 +142,8 @@ ValueStruct::ValueStruct(const bool& val) : _valueType((uint64_t)ValueStruct::ValueType::Bool), _preferredFormat(0), _caseFormat(0), - _trimTrailingZeros(0), _size(1), + _trimTrailingZeros(0), u64_val(val ? 1ull : 0ull) {} @@ -176,23 +218,34 @@ ValueStruct::ValueStruct(const float& val, _valueType((uint64_t)ValueStruct::ValueType::Float), _preferredFormat(0), _caseFormat(0), - _trimTrailingZeros((uint64_t)trimTrailingZeros), _nrDecimals((uint64_t)nrDecimals), _size(sizeof(val) * 8), + _trimTrailingZeros((uint64_t)trimTrailingZeros), f_val(val) {} ValueStruct::ValueStruct(const double& val, uint8_t nrDecimals, bool trimTrailingZeros) : +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE _isSSO(0), _valueType((uint64_t)ValueStruct::ValueType::Double), _preferredFormat(0), _caseFormat(0), - _trimTrailingZeros((uint64_t)trimTrailingZeros), _nrDecimals((uint64_t)nrDecimals), _size(sizeof(val) * 8), + _trimTrailingZeros((uint64_t)trimTrailingZeros), d_val(val) +#else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + _isSSO(0), + _valueType((uint64_t)ValueStruct::ValueType::Float), + _preferredFormat(0), + _caseFormat(0), + _nrDecimals((uint64_t)nrDecimals), + _size(sizeof(f_val) * 8), + _trimTrailingZeros((uint64_t)trimTrailingZeros), + f_val(val) +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE {} ValueStruct::ValueStruct(const char*val) : @@ -311,13 +364,29 @@ ValueStruct ValueStruct::makeFromString(const String& val) String trimmedVal(val); trimmedVal.trim(); - const bool b_true = trimmedVal.equalsIgnoreCase(F("true")); - const bool b_false = trimmedVal.equalsIgnoreCase(F("false")); + if (trimmedVal.isEmpty()) { + return ValueStruct(val); + } - if (b_true || b_false) { + const bool b_true = trimmedVal.equalsIgnoreCase(F("true")); + + if (b_true || trimmedVal.equalsIgnoreCase(F("false"))) { return ValueStruct(b_true); } + { + // Check for IP before checking for other types, + // as it would otherwise be stored as a string, not IP type. + IPAddress ip; + + if (ip.fromString(val)) + { + ValueStruct v; + v.setIPAddress(ip); + return v; + } + } + if (ContainsAny(val, F(":#%[](){},\"'`"))) { return ValueStruct(val); } @@ -330,7 +399,6 @@ ValueStruct ValueStruct::makeFromString(const String& val) } } - switch (detectedType) { case NumericalType::Not_a_number: @@ -429,17 +497,22 @@ int64_t ValueStruct::toInt(int64_t defaultValue) const break; } case ValueStruct::ValueType::Float: +#if !FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + case ValueStruct::ValueType::Double: +#endif if (isValidFloat(f_val)) { return roundf(f_val); } break; +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE case ValueStruct::ValueType::Double: if (isValidDouble(d_val)) { return round(d_val); } break; +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE case ValueStruct::ValueType::String: case ValueStruct::ValueType::FlashString: @@ -448,12 +521,13 @@ int64_t ValueStruct::toInt(int64_t defaultValue) const } break; case ValueStruct::ValueType::Unset: + case ValueStruct::ValueType::IP: break; } return defaultValue; } -double ValueStruct::toFloat() const +ESPEASY_RULES_FLOAT_TYPE ValueStruct::toFloat(ESPEASY_RULES_FLOAT_TYPE defaultValue) const { switch (getValueType()) { @@ -475,14 +549,33 @@ double ValueStruct::toFloat() const } case ValueStruct::ValueType::Double: { +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE return d_val; +#else + return f_val; +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } case ValueStruct::ValueType::String: case ValueStruct::ValueType::FlashString: + case ValueStruct::ValueType::IP: case ValueStruct::ValueType::Unset: break; } - return 0.0; + return defaultValue; +} + +bool ValueStruct::toIPAddress(IPAddress& ip) const +{ + switch (getValueType()) + { + case ValueStruct::ValueType::IP: + case ValueStruct::ValueType::String: + case ValueStruct::ValueType::FlashString: + return ip.fromString(this->toString()); + default: + break; + } + return false; } size_t ValueStruct::print(Print& out) const @@ -547,7 +640,14 @@ size_t ValueStruct::print(Print& out, ValueType& valueType, bool unformatted) co } return formatCase(out, String((const __FlashStringHelper *)str_val)); } + case ValueStruct::ValueType::IP: + { + return out.write((const uint8_t *)str_val, _size); + } case ValueStruct::ValueType::Float: +#if !FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + case ValueStruct::ValueType::Double: +#endif { String res; @@ -557,6 +657,7 @@ size_t ValueStruct::print(Print& out, ValueType& valueType, bool unformatted) co } return out.print(res); } +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE case ValueStruct::ValueType::Double: { String res; @@ -567,6 +668,7 @@ size_t ValueStruct::print(Print& out, ValueType& valueType, bool unformatted) co } return out.print(res); } +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE case ValueStruct::ValueType::Int: { if ((_size > 32) || _minNrDigits) { @@ -607,7 +709,8 @@ bool ValueStruct::isEmpty() const { case ValueStruct::ValueType::String: case ValueStruct::ValueType::FlashString: - return _size == 0; + case ValueStruct::ValueType::IP: + return _size == 0 || str_val == nullptr; case ValueStruct::ValueType::Unset: return true; default: break; diff --git a/src/src/DataStructs/ValueStruct.h b/src/src/DataStructs/ValueStruct.h index e74db6521..64900d19b 100644 --- a/src/src/DataStructs/ValueStruct.h +++ b/src/src/DataStructs/ValueStruct.h @@ -3,6 +3,8 @@ #include #include +#include + // ******************************************************************************** // ValueStruct // ******************************************************************************** @@ -19,6 +21,7 @@ public: Unset = 0, String, FlashString, + IP, Float, Double, Int, @@ -28,20 +31,20 @@ public: }; #ifndef BUILD_NO_DEBUG + static const char* toShortStr(ValueType valueType) { - static const char* shortStrings = "---\0Str\0Fla\0flt\0dbl\0int\0uin\0boo\0"; + static const char*shortStrings = "---\0Str\0Fla\0IP\0\0flt\0dbl\0int\0uin\0boo\0"; constexpr uint8_t index_modulo = static_cast(ValueType::Bool) + 1; - const uint8_t index = static_cast(valueType) % index_modulo; + const uint8_t index = static_cast(valueType) % index_modulo; - return shortStrings + 4*index; + return shortStrings + 4 * index; } - #endif + #endif // ifndef BUILD_NO_DEBUG enum class PreferredFormat : uint8_t { Default = 0, - Dec, Bin, Hex @@ -90,7 +93,6 @@ public: uint8_t nrDecimals = 4, bool trimTrailingZeros = false); - ValueStruct(const char*val); ValueStruct(const String& val); @@ -104,6 +106,20 @@ public: static ValueStruct makeBinFormatted(uint64_t val, uint8_t minNrDigits); + void setInt(const uint64_t& val); + void setInt(const int64_t& val); + + void setInt(int val); +#if defined(ESP32) && !defined(__riscv) + void setInt(int32_t val); +#endif + void setInt(uint32_t val); +#if defined(ESP32) && !defined(__riscv) + void setInt(size_t val); +#endif + + void setIPAddress(const IPAddress& ip); + ValueStruct::ValueType getValueType() const { if (_isSSO) { return ValueStruct::ValueType::String; } @@ -143,35 +159,37 @@ public: // If the given string is a numerical, try to detect: // - preferred notation (Hex/Dec/Bin) // - number of decimals - static ValueStruct makeFromString(const __FlashStringHelper *val); - static ValueStruct makeFromString(const String& val); + static ValueStruct makeFromString(const __FlashStringHelper *val); + static ValueStruct makeFromString(const String& val); - String toString(bool unformatted = false) const; + String toString(bool unformatted = false) const; - String toString(ValueType& valueType, - bool unformatted = false) const; + String toString(ValueType& valueType, + bool unformatted = false) const; - int64_t toInt(int64_t defaultValue = 0) const; + int64_t toInt(int64_t defaultValue = 0) const; - double toFloat() const; + ESPEASY_RULES_FLOAT_TYPE toFloat(ESPEASY_RULES_FLOAT_TYPE defaultValue = 0) const; - size_t print(Print& out) const; + bool toIPAddress(IPAddress& ip) const; - bool isEmpty() const; + size_t print(Print& out) const; - bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; } + bool isEmpty() const; - bool equals(const __FlashStringHelper *cmdStr, - bool ignoreCase = false) const; - bool equals(const String& cmdStr, - bool ignoreCase = false) const; + bool isSet() const { return getValueType() != ValueStruct::ValueType::Unset; } - bool equalsIgnoreCase(const __FlashStringHelper *cmdStr) const { return equals(cmdStr, true); } + bool equals(const __FlashStringHelper *cmdStr, + bool ignoreCase = false) const; + bool equals(const String& cmdStr, + bool ignoreCase = false) const; - bool equalsIgnoreCase(const String& cmdStr) const { return equals(cmdStr, true); } + bool equalsIgnoreCase(const __FlashStringHelper *cmdStr) const { return equals(cmdStr, true); } + + bool equalsIgnoreCase(const String& cmdStr) const { return equals(cmdStr, true); } #ifndef BUILD_NO_DEBUG - String debug() const; + String debug() const; #endif private: @@ -191,22 +209,24 @@ private: // and decrease max. nr of SSO bytes (VALUE_STRUCT_SSO_MAX_SIZE) uint64_t _isSSO : 1; - uint64_t _valueType : 3; + uint64_t _valueType : 4; uint64_t _preferredFormat : 2; uint64_t _caseFormat : 2; + uint64_t _minNrDigits : 7; // For printing ints with leading zeroes - // --- End of 1st byte + // --- End of 2nd byte - uint64_t _trimTrailingZeros : 1; - uint64_t _minNrDigits : 7; // For printing ints with leading zeroes uint64_t _nrDecimals : 8; uint64_t _size : 16; // Length of string or nr of bits - uint64_t unused : 24; + uint64_t _trimTrailingZeros : 1; + uint64_t unused : 23; union { - void *str_val; - float f_val; - double d_val; + void *str_val; + float f_val; +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + double d_val; +#endif int64_t i64_val; uint64_t u64_val; diff --git a/src/src/Helpers/ESPEasy_math.cpp b/src/src/Helpers/ESPEasy_math.cpp index 481dd6103..f2052bc10 100644 --- a/src/src/Helpers/ESPEasy_math.cpp +++ b/src/src/Helpers/ESPEasy_math.cpp @@ -41,6 +41,19 @@ int maxNrDecimals_fpType(const float& value) return res; } +int quick_pow10(uint8_t n) +{ + static int pow10[] = { + 1, 10, 100, 1000, 10000, + 100000, 1000000, 10000000, 100000000, 1000000000 + }; + + constexpr uint8_t max_n = NR_ELEMENTS(pow10) - 1; + + if (n <= max_n) return pow10[n]; + return pow10[max_n - 1]; +} + uint64_t computeDecimalFactorForDecimals(int nrDecimals) { uint64_t factor = 1; diff --git a/src/src/Helpers/ESPEasy_math.h b/src/src/Helpers/ESPEasy_math.h index b9a57b186..fc81d8bf6 100644 --- a/src/src/Helpers/ESPEasy_math.h +++ b/src/src/Helpers/ESPEasy_math.h @@ -15,49 +15,117 @@ int maxNrDecimals_fpType(const double& value); #endif int maxNrDecimals_fpType(const float& value); +#ifdef ESP32 + +constexpr int maxNrDecimals_fpType_constexpr(const ESPEASY_RULES_FLOAT_TYPE& value) +{ +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + int res = ESPEASY_DOUBLE_NR_DECIMALS; +# else + int res = ESPEASY_FLOAT_NR_DECIMALS; +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + ESPEASY_RULES_FLOAT_TYPE factor = 1; + + while ((value / factor) > 10 && res > 2) { + factor *= 10.0f; + --res; + } + return res; +} + +#endif // ifdef ESP32 +#ifdef ESP8266 + +constexpr unsigned maxNrDecimals_fpType_constexpr_(ESPEASY_RULES_FLOAT_TYPE value, ESPEASY_RULES_FLOAT_TYPE factor, unsigned res) +{ + return ((value / factor) > 10 && res > 2) + ? maxNrDecimals_fpType_constexpr_(value, factor * 10.0f, res - 1) + : res; +} + +constexpr int maxNrDecimals_fpType_constexpr(const ESPEASY_RULES_FLOAT_TYPE& value) +{ + return maxNrDecimals_fpType_constexpr_( + value, +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE + 1.0, ESPEASY_DOUBLE_NR_DECIMALS +#else + 1.0f, ESPEASY_FLOAT_NR_DECIMALS +#endif + ); +} + +#endif // ifdef ESP8266 + +int quick_pow10(uint8_t n); + uint64_t computeDecimalFactorForDecimals(int nrDecimals); // The following definitions are from The art of computer programming by Knuth #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool approximatelyEqual(const double& a, const double& b); -#endif -bool approximatelyEqual(const float& a, const float& b); +bool approximatelyEqual(const double& a, + const double& b); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool approximatelyEqual(const float& a, + const float& b); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool approximatelyEqual(const double& a, const double& b, double estimatedEpsilon); -#endif -bool approximatelyEqual(const float& a, const float& b, float estimatedEpsilon); +bool approximatelyEqual(const double& a, + const double& b, + double estimatedEpsilon); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool approximatelyEqual(const float& a, + const float& b, + float estimatedEpsilon); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool definitelyGreaterThan(const double& a, const double& b); -#endif -bool definitelyGreaterThan(const float& a, const float& b); +bool definitelyGreaterThan(const double& a, + const double& b); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool definitelyGreaterThan(const float& a, + const float& b); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool definitelyGreaterThan(const double& a, const double& b, double estimatedEpsilon); -#endif -bool definitelyGreaterThan(const float& a, const float& b, float estimatedEpsilon); +bool definitelyGreaterThan(const double& a, + const double& b, + double estimatedEpsilon); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool definitelyGreaterThan(const float& a, + const float& b, + float estimatedEpsilon); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool definitelyLessThan(const double& a, const double& b); -#endif -bool definitelyLessThan(const float& a, const float& b); +bool definitelyLessThan(const double& a, + const double& b); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool definitelyLessThan(const float& a, + const float& b); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool definitelyLessThan(const double& a, const double& b, double estimatedEpsilon); -#endif -bool definitelyLessThan(const float& a, const float& b, float estimatedEpsilon); +bool definitelyLessThan(const double& a, + const double& b, + double estimatedEpsilon); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool definitelyLessThan(const float& a, + const float& b, + float estimatedEpsilon); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool essentiallyEqual(const double& a, const double& b); -#endif -bool essentiallyEqual(const float& a, const float& b); +bool essentiallyEqual(const double& a, + const double& b); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool essentiallyEqual(const float& a, + const float& b); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool essentiallyEqual(const double& a, const double& b, double estimatedEpsilon); -#endif -bool essentiallyEqual(const float& a, const float& b, float estimatedEpsilon); +bool essentiallyEqual(const double& a, + const double& b, + double estimatedEpsilon); +#endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE +bool essentiallyEqual(const float& a, + const float& b, + float estimatedEpsilon); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE -bool essentiallyZero(const double& a); +bool essentiallyZero(const double& a); #endif -bool essentiallyZero(const float& a); +bool essentiallyZero(const float& a); ESPEASY_RULES_FLOAT_TYPE mapADCtoFloat(ESPEASY_RULES_FLOAT_TYPE float_value, ESPEASY_RULES_FLOAT_TYPE adc1, @@ -66,4 +134,4 @@ ESPEASY_RULES_FLOAT_TYPE mapADCtoFloat(ESPEASY_RULES_FLOAT_TYPE float_value, ESPEASY_RULES_FLOAT_TYPE out2); -#endif // HELPERS_ESPEASY_MATH_H \ No newline at end of file +#endif // HELPERS_ESPEASY_MATH_H diff --git a/src/src/Helpers/KeyValueWriter_JSON.cpp b/src/src/Helpers/KeyValueWriter_JSON.cpp index 84d247998..9167f57ad 100644 --- a/src/src/Helpers/KeyValueWriter_JSON.cpp +++ b/src/src/Helpers/KeyValueWriter_JSON.cpp @@ -203,6 +203,9 @@ void KeyValueWriter_JSON::writeValue(const ValueStruct& val, bool forceString) return; } break; + case ValueStruct::ValueType::IP: + pr.print(to_json_value(str, true)); + return; } pr.print(to_json_value(str)); } diff --git a/src/src/Helpers/Numerical.cpp b/src/src/Helpers/Numerical.cpp index a782b871f..ea5b441b7 100644 --- a/src/src/Helpers/Numerical.cpp +++ b/src/src/Helpers/Numerical.cpp @@ -15,10 +15,11 @@ bool isValidFloat(float f) { return !isnan(f) && !isinf(f); #endif } - +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE bool isValidDouble(ESPEASY_RULES_FLOAT_TYPE f) { return !isnan(f) && !isinf(f); } +#endif bool validIntFromString(const String& tBuf, int32_t& result) { NumericalType detectedType; @@ -144,6 +145,7 @@ bool validDoubleFromString(const String& tBuf, ESPEASY_RULES_FLOAT_TYPE& result) } bool validDoubleFromString(const String& tBuf, ESPEASY_RULES_FLOAT_TYPE& result, int& nrDecimals) { +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE nrDecimals = -1; #if defined(CORE_POST_2_5_0) || defined(ESP32) @@ -180,6 +182,9 @@ bool validDoubleFromString(const String& tBuf, ESPEASY_RULES_FLOAT_TYPE& result, return res; #endif // if defined(CORE_POST_2_5_0) || defined(ESP32) +#else + return validFloatFromString(tBuf, result); +#endif } bool mustConsiderAsString(NumericalType detectedType) { diff --git a/src/src/Helpers/Numerical.h b/src/src/Helpers/Numerical.h index 94858c3e7..0a4018d33 100644 --- a/src/src/Helpers/Numerical.h +++ b/src/src/Helpers/Numerical.h @@ -8,7 +8,9 @@ \*********************************************************************************************/ bool isValidFloat(float f); +#if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE bool isValidDouble(ESPEASY_RULES_FLOAT_TYPE f); +#endif bool validIntFromString(const String& tBuf, int32_t& result); diff --git a/src/src/Helpers/StringConverter.cpp b/src/src/Helpers/StringConverter.cpp index ed55c8e02..6bb8f0dca 100644 --- a/src/src/Helpers/StringConverter.cpp +++ b/src/src/Helpers/StringConverter.cpp @@ -1615,7 +1615,7 @@ void parseStandardConversions(String& s, bool useURLencode) { #undef SMART_CONV #if FEATURE_STRING_VARIABLES - double tmp{}; + ESPEASY_RULES_FLOAT_TYPE tmp{}; #define SMART_CONV(T, FUN) \ while (getConvertArgumentStr((T), data)) { repl(data, (FUN)); } SMART_CONV(F("%c_isnum%"), String(validDoubleFromString(getCustomStringVar(data.str1), tmp) ? 1 : 0)) diff --git a/src/src/Helpers/SystemVariables.cpp b/src/src/Helpers/SystemVariables.cpp index 5f038486b..c3ee2c91e 100644 --- a/src/src/Helpers/SystemVariables.cpp +++ b/src/src/Helpers/SystemVariables.cpp @@ -231,18 +231,20 @@ String SystemVariables::getSystemVariable(SystemVariables::Enum enumval) { #ifndef LIMIT_BUILD_SIZE case S_PI: { constexpr ESPEASY_RULES_FLOAT_TYPE _pi = M_PI; + constexpr int maxNrDecimals = maxNrDecimals_fpType_constexpr(_pi); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - return doubleToString(_pi, maxNrDecimals_fpType(_pi)); + return doubleToString(_pi, maxNrDecimals); #else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - return toString(_pi, maxNrDecimals_fpType(_pi)); + return floatToString(_pi, maxNrDecimals); #endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } case S_E: { constexpr ESPEASY_RULES_FLOAT_TYPE _e = M_E; + constexpr int maxNrDecimals = maxNrDecimals_fpType_constexpr(_e); #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - return doubleToString(_e, maxNrDecimals_fpType(_e)); + return doubleToString(_e, maxNrDecimals); #else // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE - return toString(_e, maxNrDecimals_fpType(_e)); + return floatToString(_e, maxNrDecimals); #endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } #endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/src/Helpers/_ESPEasy_key_value_store.cpp b/src/src/Helpers/_ESPEasy_key_value_store.cpp index dbb4f168b..4b4c528c4 100644 --- a/src/src/Helpers/_ESPEasy_key_value_store.cpp +++ b/src/src/Helpers/_ESPEasy_key_value_store.cpp @@ -182,7 +182,8 @@ bool ESPEasy_key_value_store::load( // Read next block loadNextFromFile = true; } else { - bufPos += 4; + bufPos += 4; + // TODO TD-er: Use getUint16FromBigEndianByteStream bytesLeftPartialString = (buffer[bufPos] << 8) | buffer[bufPos + 1]; bufPos += 2; @@ -1029,8 +1030,17 @@ void ESPEasy_key_value_store::setValue(const KVS_StorageType::Enum& storageType, GET_4BYTE_INT_TYPE_FROM_STRING(int32_type, int32_t) GET_4BYTE_INT_TYPE_FROM_STRING(uint32_type, uint32_t) case KVS_StorageType::Enum::double_type: +# if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE setValue(key, value.toDouble()); +# else + { + // Don't use the String::toDouble() function, but we still need to store it as double in the KVS + const double value_d(value.toFloat()); + setValue(key, value_d); + } +# endif // if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE break; + case KVS_StorageType::Enum::float_type: setValue(key, value.toFloat()); break; diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 36faff2d4..a29effa60 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -899,8 +899,8 @@ void P073_data_struct::getDisplayLimits(int32_t& lLimit, } # endif // if P073_USE_74HC595 dgts -= offset; // Subtract an offset, used for extra symbol - lLimit = -pow10(dgts - 1); // Lowest value we can display - 1 - uLimit = pow10(dgts); // Highest value we can display + 1 + lLimit = -quick_pow10(dgts - 1); // Lowest value we can display - 1 + uLimit = quick_pow10(dgts); // Highest value we can display + 1 // TODO disable log // addLog(LOG_LEVEL_INFO, strformat(F("P073: limits: %d digits(%d), lower: %d, upper: %d"), dgts, offset, lLimit, uLimit)); } diff --git a/src/src/PluginStructs/P157_data_struct.cpp b/src/src/PluginStructs/P157_data_struct.cpp index aaed1a116..d7c92c3d5 100644 --- a/src/src/PluginStructs/P157_data_struct.cpp +++ b/src/src/PluginStructs/P157_data_struct.cpp @@ -937,8 +937,8 @@ void P157_data_struct::getDisplayLimits(int32_t& lLimit, uint8_t dgts = min((uint8_t)8, bufLen); dgts -= offset; // Subtract an offset, used for extra symbol - lLimit = -pow10(dgts - 1); // Lowest value we can display - 1 - uLimit = pow10(dgts); // Highest value we can display + 1 + lLimit = -quick_pow10(dgts - 1); // Lowest value we can display - 1 + uLimit = quick_pow10(dgts); // Highest value we can display + 1 // TODO disable log // addLog(LOG_LEVEL_INFO, strformat(F("P157: limits: %d digits(%d), lower: %d, upper: %d"), dgts, offset, lLimit, uLimit)); } diff --git a/src/src/WebServer/ESPEasy_key_value_store_webform.cpp b/src/src/WebServer/ESPEasy_key_value_store_webform.cpp index dbe18e245..33cd836e4 100644 --- a/src/src/WebServer/ESPEasy_key_value_store_webform.cpp +++ b/src/src/WebServer/ESPEasy_key_value_store_webform.cpp @@ -45,7 +45,7 @@ void WebFormItemParams::checkRanges() } bool showWebformItem(const ESPEasy_key_value_store& store, - WebFormItemParams params) + WebFormItemParams&& params) { params.checkRanges(); String id = params._id.isEmpty() ? concat(F("KVS_ID_"), params._key) : params._id; @@ -56,9 +56,9 @@ bool showWebformItem(const ESPEasy_key_value_store& store, { IPAddress value; - if (!store.getValue(params._key, value) && !params._defaultStringValue.isEmpty()) + if (!store.getValue(params._key, value) && !params._defaultValue.isEmpty()) { - value.fromString(params._defaultStringValue); + params._defaultValue.toIPAddress(value); } addFormIPBox( params._label, @@ -73,7 +73,7 @@ bool showWebformItem(const ESPEasy_key_value_store& store, if (!store.getValueAsString(params._key, value)) { - value = params._defaultStringValue; + value = params._defaultValue.toString(); } if (params._password) { @@ -111,7 +111,7 @@ bool showWebformItem(const ESPEasy_key_value_store& store, { int64_t value; - if (!store.getValueAsInt(params._key, value)) { value = params._defaultIntValue; } + if (!store.getValueAsInt(params._key, value)) { value = params._defaultValue.toInt(); } addFormNumericBox( params._label, id, @@ -142,7 +142,7 @@ bool showWebformItem(const ESPEasy_key_value_store& store, { float value{}; - if (!store.getValue(params._key, value)) { value = params._defaultFloatValue; } + if (!store.getValue(params._key, value)) { value = params._defaultValue.toFloat(); } addFormFloatNumberBox( params._label, id, @@ -165,7 +165,7 @@ bool showWebformItem(const ESPEasy_key_value_store& store, { bool value{}; - if (!store.getValue(params._key, value)) { value = params._defaultIntValue != 0; } + if (!store.getValue(params._key, value)) { value = params._defaultValue.toInt() != 0; } addFormCheckBox( params._label, id, @@ -185,7 +185,7 @@ void showFormSelector(const ESPEasy_key_value_store& store, { int64_t value{}; - if (!store.getValueAsInt(params._key, value)) { value = params._defaultIntValue; } + if (!store.getValueAsInt(params._key, value)) { value = params._defaultValue.toInt(); } selector.addFormSelector(params._label, params._id, value); } diff --git a/src/src/WebServer/ESPEasy_key_value_store_webform.h b/src/src/WebServer/ESPEasy_key_value_store_webform.h index d8bab0ec6..7b92acb6b 100644 --- a/src/src/WebServer/ESPEasy_key_value_store_webform.h +++ b/src/src/WebServer/ESPEasy_key_value_store_webform.h @@ -40,15 +40,11 @@ struct WebFormItemParams { bool _password{}; String _pattern; - int64_t _defaultIntValue{}; - double _defaultFloatValue{}; - String _defaultStringValue; - - + ValueStruct _defaultValue; }; bool showWebformItem(const ESPEasy_key_value_store& store, - WebFormItemParams params); + WebFormItemParams&& params); void showFormSelector(const ESPEasy_key_value_store& store, const FormSelectorOptions & selector,