From 652566b110a82dbf8285d239b8a382262708b1f0 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 16 Feb 2024 22:24:40 +0100 Subject: [PATCH 01/50] [P139] Add support for AXP2101 Powermanagement --- lib/AXP2101/LICENSE | 121 ++++ lib/AXP2101/library.properties | 10 + lib/AXP2101/src/AXP2101.cpp | 724 +++++++++++++++++++++ lib/AXP2101/src/AXP2101.h | 535 +++++++++++++++ src/_P139_AXP2101.ino | 418 ++++++++++++ src/src/CustomBuild/define_plugin_sets.h | 16 +- src/src/PluginStructs/P137_data_struct.cpp | 4 +- src/src/PluginStructs/P139_data_struct.cpp | 489 ++++++++++++++ src/src/PluginStructs/P139_data_struct.h | 150 +++++ 9 files changed, 2463 insertions(+), 4 deletions(-) create mode 100644 lib/AXP2101/LICENSE create mode 100644 lib/AXP2101/library.properties create mode 100644 lib/AXP2101/src/AXP2101.cpp create mode 100644 lib/AXP2101/src/AXP2101.h create mode 100644 src/_P139_AXP2101.ino create mode 100644 src/src/PluginStructs/P139_data_struct.cpp create mode 100644 src/src/PluginStructs/P139_data_struct.h diff --git a/lib/AXP2101/LICENSE b/lib/AXP2101/LICENSE new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/lib/AXP2101/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/lib/AXP2101/library.properties b/lib/AXP2101/library.properties new file mode 100644 index 0000000000..e1afb412fd --- /dev/null +++ b/lib/AXP2101/library.properties @@ -0,0 +1,10 @@ +name=AXP2101 Power management (I2C) +version=0.1.0 +author=M5Stack & Ton Huisman for ESPEasy +maintainer=Ton Huisman +sentence=This is a library for AXP2101, Power Management with I2C connectivity. Based on a M5Stack source, rewritten by tonhuisman. +paragraph=This is a library for AXP2101, Power Management with I2C connectivity. Based on a M5Stack source, rewritten by tonhuisman. +category=Device Control +url=https://github.com/tonhuisman/AXP2101 +architectures=* +includes=AXP2101.h diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp new file mode 100644 index 0000000000..e60af6ac81 --- /dev/null +++ b/lib/AXP2101/src/AXP2101.cpp @@ -0,0 +1,724 @@ +#include "AXP2101.h" + +/** + * Utility functions + */ +const __FlashStringHelper* toString(AXP2101_device_model_e device, + bool displayString) { + switch (device) { + case AXP2101_device_model_e::Unselected: return displayString ? F("Select an option to set default values") : F("Unselected"); + case AXP2101_device_model_e::M5Stack_Core2_v1_1: return displayString ? F("M5Stack Core2 v1.1") : F("M5Core2v11"); + case AXP2101_device_model_e::M5Stack_CoreS3: return displayString ? F("M5Stack CoreS3") : F("M5CoreS3"); + case AXP2101_device_model_e::LilyGO_TBeam_v1_2: return displayString ? F("LilyGo TBeam v1.2") : F("TBeamv12"); + case AXP2101_device_model_e::LilyGO_TBeamS3_v3: return displayString ? F("LilyGo TBeam S3 v3") : F("TBeamS3v3"); + case AXP2101_device_model_e::LilyGO_TPCie_v1_2: return displayString ? F("LilyGo TPCie v1.2") : F("TPCiev12"); + case AXP2101_device_model_e::UserDefined: return displayString ? F("User defined") : F("Userdefined"); + case AXP2101_device_model_e::MAX: break; + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_registers_e reg, + bool displayString) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return displayString ? F("DCDC1") : F("dcdc1"); + case AXP2101_registers_e::dcdc2: return displayString ? F("DCDC2") : F("dcdc2"); + case AXP2101_registers_e::dcdc3: return displayString ? F("DCDC3") : F("dcdc3"); + case AXP2101_registers_e::dcdc4: return displayString ? F("DCDC4") : F("dcdc4"); + case AXP2101_registers_e::dcdc5: return displayString ? F("DCDC5") : F("dcdc5"); + case AXP2101_registers_e::aldo1: return displayString ? F("ALDO1") : F("aldo1"); + case AXP2101_registers_e::aldo2: return displayString ? F("ALDO2") : F("aldo2"); + case AXP2101_registers_e::aldo3: return displayString ? F("ALDO3") : F("aldo3"); + case AXP2101_registers_e::aldo4: return displayString ? F("ALDO4") : F("aldo4"); + case AXP2101_registers_e::bldo1: return displayString ? F("BLDO1") : F("bldo1"); + case AXP2101_registers_e::bldo2: return displayString ? F("BLDO2") : F("bldo2"); + case AXP2101_registers_e::dldo1: return displayString ? F("DLDO1") : F("dldo1"); + case AXP2101_registers_e::dldo2: return displayString ? F("DLDO2") : F("dldo2"); + case AXP2101_registers_e::cpuldos: return displayString ? F("CPULDOS") : F("cpuldos"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP_pin_s pin) { + switch (pin) { + case AXP_pin_s::Off: return F("Off"); + case AXP_pin_s::On: return F("On"); + case AXP_pin_s::Default: return F("Default"); + case AXP_pin_s::Disabled: return F("Disabled"); + case AXP_pin_s::Protected: return F("Protected"); + } + return F(""); +} + +AXP2101_registers_e AXP2101_intToRegister(int reg) { + switch (reg) { + case 0: return AXP2101_registers_e::dcdc1; + case 1: return AXP2101_registers_e::dcdc2; + case 2: return AXP2101_registers_e::dcdc3; + case 3: return AXP2101_registers_e::dcdc4; + case 4: return AXP2101_registers_e::dcdc5; + case 5: return AXP2101_registers_e::aldo1; + case 6: return AXP2101_registers_e::aldo2; + case 7: return AXP2101_registers_e::aldo3; + case 8: return AXP2101_registers_e::aldo4; + case 9: return AXP2101_registers_e::bldo1; + case 10: return AXP2101_registers_e::bldo2; + case 11: return AXP2101_registers_e::dldo1; + case 12: return AXP2101_registers_e::dldo2; + case 13: return AXP2101_registers_e::cpuldos; + } + return AXP2101_registers_e::dcdc1; // we shouldn't get here +} + +uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return AXP2101_DCDC1_MIN; + case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MIN; + case AXP2101_registers_e::dcdc2: + case AXP2101_registers_e::dcdc3: + case AXP2101_registers_e::dcdc4: + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MIN; + } + return 0u; +} + +uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: + case AXP2101_registers_e::dcdc3: return AXP2101_DCDC3_MAX; + case AXP2101_registers_e::dcdc2: return AXP2101_DCDC2_MAX; + case AXP2101_registers_e::dcdc4: return AXP2101_DCDC4_MAX; + case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MAX; + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: return AXP2101_DLDO1_MAX; + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MAX; + } + return 0u; +} + +/** + * Is the pin Default, Disabled or Protected, then don't initialize + */ +bool AXP2101_isPinDefault(AXP_pin_s pin) { + return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin || AXP_pin_s::Default == pin; +} + +/** + * Is the pin Disabled or Protected, then don't change + */ +bool AXP2101_isPinProtected(AXP_pin_s pin) { + return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin; +} + +/** + * AXP2101_settings struct + */ + +// constructor +AXP2101_settings::AXP2101_settings() {} + +// constructor +AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _dcdc3, uint16_t _dcdc4, uint16_t _dcdc5, + uint16_t _aldo1, uint16_t _aldo2, uint16_t _aldo3, uint16_t _aldo4, + uint16_t _bldo1, uint16_t _bldo2, uint16_t _dldo1, uint16_t _dldo2, uint16_t _cpuldos, + AXP_pin_s _en_dcdc1, AXP_pin_s _en_dcdc2, AXP_pin_s _en_dcdc3, AXP_pin_s _en_dcdc4, AXP_pin_s _en_dcdc5, + AXP_pin_s _en_aldo1, AXP_pin_s _en_aldo2, AXP_pin_s _en_aldo3, AXP_pin_s _en_aldo4, + AXP_pin_s _en_bldo1, AXP_pin_s _en_bldo2, AXP_pin_s _en_dldo1, AXP_pin_s _en_dldo2, AXP_pin_s _en_cpuldos) +{ + registers.dcdc1 = _dcdc1; registers.dcdc2 = _dcdc2; registers.dcdc3 = _dcdc3; registers.dcdc4 = _dcdc4; registers.dcdc5 = _dcdc5; + registers.aldo1 = _aldo1; registers.aldo2 = _aldo2; registers.aldo3 = _aldo3; registers.aldo4 = _aldo4; + registers.bldo1 = _bldo1; registers.bldo1 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; + + pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); + pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); + pinStates.en_dcdc5 = static_cast(_en_dcdc5); pinStates.en_aldo1 = static_cast(_en_aldo1); + pinStates.en_aldo2 = static_cast(_en_aldo2); pinStates.en_aldo3 = static_cast(_en_aldo3); + pinStates.en_aldo4 = static_cast(_en_aldo4); pinStates.en_bldo1 = static_cast(_en_bldo1); + pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); + pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); +} + +void AXP2101_settings::setVoltage(AXP2101_registers_e reg, + int voltage) { + if (-1 == voltage) { voltage = 0xFFFF; } + + switch (reg) { + case AXP2101_registers_e::dcdc1: registers.dcdc1 = voltage; break; + case AXP2101_registers_e::dcdc2: registers.dcdc2 = voltage; break; + case AXP2101_registers_e::dcdc3: registers.dcdc3 = voltage; break; + case AXP2101_registers_e::dcdc4: registers.dcdc4 = voltage; break; + case AXP2101_registers_e::dcdc5: registers.dcdc5 = voltage; break; + case AXP2101_registers_e::aldo1: registers.aldo1 = voltage; break; + case AXP2101_registers_e::aldo2: registers.aldo2 = voltage; break; + case AXP2101_registers_e::aldo3: registers.aldo3 = voltage; break; + case AXP2101_registers_e::aldo4: registers.aldo4 = voltage; break; + case AXP2101_registers_e::bldo1: registers.bldo1 = voltage; break; + case AXP2101_registers_e::bldo2: registers.bldo2 = voltage; break; + case AXP2101_registers_e::dldo1: registers.dldo1 = voltage; break; + case AXP2101_registers_e::dldo2: registers.dldo2 = voltage; break; + case AXP2101_registers_e::cpuldos: registers.cpuldos = voltage; break; + } +} + +int AXP2101_settings::getVoltage(AXP2101_registers_e reg, + bool realValue) { + int result = -1; + + switch (reg) { + case AXP2101_registers_e::dcdc1: result = registers.dcdc1; break; + case AXP2101_registers_e::dcdc2: result = registers.dcdc2; break; + case AXP2101_registers_e::dcdc3: result = registers.dcdc3; break; + case AXP2101_registers_e::dcdc4: result = registers.dcdc4; break; + case AXP2101_registers_e::dcdc5: result = registers.dcdc5; break; + case AXP2101_registers_e::aldo1: result = registers.aldo1; break; + case AXP2101_registers_e::aldo2: result = registers.aldo2; break; + case AXP2101_registers_e::aldo3: result = registers.aldo3; break; + case AXP2101_registers_e::aldo4: result = registers.aldo4; break; + case AXP2101_registers_e::bldo1: result = registers.bldo1; break; + case AXP2101_registers_e::bldo2: result = registers.bldo2; break; + case AXP2101_registers_e::dldo1: result = registers.dldo1; break; + case AXP2101_registers_e::dldo2: result = registers.dldo2; break; + case AXP2101_registers_e::cpuldos: result = registers.cpuldos; break; + } + return 0xFFFFF == result ? (realValue ? 0 : -1) : result; +} + +void AXP2101_settings::setState(AXP2101_registers_e reg, + AXP_pin_s state) { + const uint8_t value = static_cast(state) & 0x03; + + switch (reg) { + case AXP2101_registers_e::dcdc1: pinStates.en_dcdc1 = value; break; + case AXP2101_registers_e::dcdc2: pinStates.en_dcdc2 = value; break; + case AXP2101_registers_e::dcdc3: pinStates.en_dcdc3 = value; break; + case AXP2101_registers_e::dcdc4: pinStates.en_dcdc4 = value; break; + case AXP2101_registers_e::dcdc5: pinStates.en_dcdc5 = value; break; + case AXP2101_registers_e::aldo1: pinStates.en_aldo1 = value; break; + case AXP2101_registers_e::aldo2: pinStates.en_aldo2 = value; break; + case AXP2101_registers_e::aldo3: pinStates.en_aldo3 = value; break; + case AXP2101_registers_e::aldo4: pinStates.en_aldo4 = value; break; + case AXP2101_registers_e::bldo1: pinStates.en_bldo1 = value; break; + case AXP2101_registers_e::bldo2: pinStates.en_bldo2 = value; break; + case AXP2101_registers_e::dldo1: pinStates.en_dldo1 = value; break; + case AXP2101_registers_e::dldo2: pinStates.en_dldo2 = value; break; + case AXP2101_registers_e::cpuldos: pinStates.en_cpuldos = value; break; + } +} + +AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return static_cast(pinStates.en_dcdc1); + case AXP2101_registers_e::dcdc2: return static_cast(pinStates.en_dcdc2); + case AXP2101_registers_e::dcdc3: return static_cast(pinStates.en_dcdc3); + case AXP2101_registers_e::dcdc4: return static_cast(pinStates.en_dcdc4); + case AXP2101_registers_e::dcdc5: return static_cast(pinStates.en_dcdc5); + case AXP2101_registers_e::aldo1: return static_cast(pinStates.en_aldo1); + case AXP2101_registers_e::aldo2: return static_cast(pinStates.en_aldo2); + case AXP2101_registers_e::aldo3: return static_cast(pinStates.en_aldo3); + case AXP2101_registers_e::aldo4: return static_cast(pinStates.en_aldo4); + case AXP2101_registers_e::bldo1: return static_cast(pinStates.en_bldo1); + case AXP2101_registers_e::bldo2: return static_cast(pinStates.en_bldo2); + case AXP2101_registers_e::dldo1: return static_cast(pinStates.en_dldo1); + case AXP2101_registers_e::dldo2: return static_cast(pinStates.en_dldo2); + case AXP2101_registers_e::cpuldos: return static_cast(pinStates.en_cpuldos); + } + return AXP_pin_s::Default; +} + +/** + * AXP2101 device class + */ + +// *INDENT-OFF* +AXP2101_settings AXP2101_deviceSettingsArray[] = +{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | aldo3 | aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos +/* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default }, +/* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, +/* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, +/* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default }, +/* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, +/* LilyGo TPCie v1.2 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, +/* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default }, +}; +// *INDENT-ON* + +bool AXP2101::begin(TwoWire *wire, + uint8_t addr, + AXP2101_device_model_e device) { + _wire = wire; + _addr = addr; + _device = device; + _wire->beginTransmission(_addr); + return 0 == _wire->endTransmission(); +} + +void AXP2101::setDevice(AXP2101_device_model_e device) { + _device = device; +} + +bool AXP2101::readRegister(uint8_t addr, + uint8_t reg, + uint8_t *result, + uint16_t length) { + uint8_t index = 0; + + _wire->beginTransmission(addr); + _wire->write(reg); + const uint8_t err = _wire->endTransmission(); + + _wire->requestFrom(addr, length); + + for (int i = 0; i < length; ++i) { + result[index++] = _wire->read(); + } + + return err == 0; +} + +uint8_t AXP2101::readRegister8(uint8_t addr, + uint8_t reg) { + _wire->beginTransmission(addr); + _wire->write(reg); + _wire->endTransmission(); + _wire->requestFrom(addr, 1); + return _wire->read(); +} + +bool AXP2101::writeRegister8(uint8_t addr, + uint8_t reg, + uint8_t data) { + _wire->beginTransmission(addr); + _wire->write(reg); + _wire->write(data); + return 0 == _wire->endTransmission(); +} + +bool AXP2101::bitOn(uint8_t addr, + uint8_t reg, + uint8_t data) { + const uint8_t temp = readRegister8(addr, reg); + const uint8_t write_back = (temp | data); + + return writeRegister8(addr, reg, write_back); +} + +bool AXP2101::bitOff(uint8_t addr, + uint8_t reg, + uint8_t data) { + const uint8_t temp = readRegister8(addr, reg); + const uint8_t write_back = (temp & (~data)); + + return writeRegister8(addr, reg, write_back); +} + +bool AXP2101::bitGet(uint8_t reg, + uint8_t data) { + const uint8_t temp = readRegister8(AXP2101_ADDR, reg); + + return (temp & data) == data; +} + +bool AXP2101::bitOnOff(bool sw, + uint8_t creg, + uint8_t mask) { + bool result = false; + + if (sw) { + result = bitOn(AXP2101_ADDR, creg, mask); + } else { + result = bitOff(AXP2101_ADDR, creg, mask); + } + + return result; +} + +/** + * Convert a voltage to the indicated register-data, using the matching offset and range(s) + */ +uint8_t voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg) { + uint16_t min = 500; + uint16_t max = 0; + + switch (reg) { + case AXP2101_registers_e::dcdc2: + + if (0 == max) { max = 1540; } + case AXP2101_registers_e::dcdc3: + + if (0 == max) { max = 3400; } + + if (voltage <= min) { + return 0u; + } + else if (voltage > max) { + voltage = max; + } + else if ((voltage > 1540) && (voltage < 1600)) { + voltage = 1540u; + } + + if (voltage <= 1220) { + return (voltage - 500) / 10; + } + else if (voltage <= 1540) { + return (voltage - 1220) / 20 + (uint8_t)0b01000111; + } + return (voltage - 1600) / 100 + (uint8_t)0b01011000; + + case AXP2101_registers_e::dcdc4: + + if (voltage <= min) { + return 0; + } + else if (voltage > 1840) { + voltage = 1840u; + } + + if (voltage <= 1220) { + return (voltage - 500) / 10; + } + return (voltage - 1220) / 20 + (uint8_t)0b01000111; + + case AXP2101_registers_e::dcdc1: + + if (0 == max) { + min = 1500; + max = 3400; + } + case AXP2101_registers_e::dcdc5: + + if (0 == max) { + min = 1400; + max = 3700; + } + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: + + if (0 == max) { max = 3400; } + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: + + if (0 == max) { max = 1400; } + + if (voltage <= min) { return 0u; } + + if (voltage > max) { voltage = max; } + + return (voltage - min) / 100; + } + return 0u; +} + +/** + * Convert read data from a register to a voltage for the indicated output + */ +uint16_t AXP2101::registerToVoltage(uint8_t data, + AXP2101_registers_e reg) { + uint16_t off = 0; + uint8_t mask = 0; + + switch (reg) { + case AXP2101_registers_e::dcdc2: + case AXP2101_registers_e::dcdc3: + data &= 0x7F; + + if (data < 0b01000111) { + return static_cast(data * 10) + 500; + } + else if (data < 0b01011000) { + return static_cast(data * 20) - 200; + } + return static_cast(data * 100) - 7200; + + case AXP2101_registers_e::dcdc4: + + if (data < 0b01000111) { + return static_cast(data * 10) + 500; + } + return static_cast(data * 20) - 200; + + case AXP2101_registers_e::dcdc1: + + if (0 == off) { off = 1500; } + case AXP2101_registers_e::dcdc5: + + if (0 == off) { off = 1400; } + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: + + if (0 == off) { off = 500; } + return off + (data * 100); + } + return 0u; +} + +/** + * Set a voltage to a port (output pin) of the AXP2101 + */ +bool AXP2101::setPortVoltage(uint16_t voltage, + AXP2101_registers_e reg) { + const uint8_t data = voltageToRegister(voltage, reg); + const uint8_t creg = static_cast(reg); + + return writeRegister8(AXP2101_ADDR, creg, data); +} + +/** + * Get the voltage of a port (output pin) of the AXP2101 + */ +uint16_t AXP2101::getPortVoltage(AXP2101_registers_e reg) { + const uint8_t creg = static_cast(reg); + const uint8_t data = readRegister8(AXP2101_ADDR, + creg); + + return registerToVoltage(data, + reg); +} + +/** + * Set the on/off state of a port (output pin) of the AXP2101 + */ +bool AXP2101::setPortState(bool sw, + AXP2101_registers_e reg) { + uint8_t ctrl = 0; + uint8_t mask = 0; + bool result = false; + + getControlRegisterMask(reg, ctrl, mask); + + if (ctrl) { + result = bitOnOff(sw, ctrl, mask); + } + return result; +} + +/** + * Get the on/off state of a port (output pin) of the AXP2101 + */ +bool AXP2101::getPortState(AXP2101_registers_e reg) { + uint8_t ctrl = 0; + uint8_t mask = 0; + bool result = false; + + getControlRegisterMask(reg, ctrl, mask); + + if (ctrl) { + result = bitGet(ctrl, mask); + } + return result; +} + +/** + * Compound functions, device model dependent + */ + +// TODO Enable/disable these specific per device/models +void AXP2101::set_bus_3v3(uint16_t voltage) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (!voltage) { + set_dcdc1_on_off(false); + set_dcdc3_on_off(false); + } else { + set_dcdc1_on_off(true); + set_dcdc3_on_off(true); + set_dcdc1_voltage(voltage); + set_dcdc3_voltage(voltage); + } + } // else... +} + +void AXP2101::set_lcd_back_light_voltage(uint16_t voltage) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (!voltage) { + set_bldo1_on_off(false); + } else { + set_bldo1_on_off(true); + set_bldo1_voltage(voltage); + } + } // else... +} + +void AXP2101::set_bus_5v(uint8_t sw) { + if (sw) { + set_bldo2_on_off(true); + set_bldo2_voltage(3300); + } else { + set_bldo2_on_off(false); + } +} + +void AXP2101::set_spk(bool sw) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (sw) { + set_aldo3_on_off(true); + set_aldo3_voltage(3300); + } else { + set_aldo3_on_off(false); + } + } // else... +} + +void AXP2101::set_lcd_rst(bool sw) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (sw) { + set_aldo2_on_off(true); + set_aldo2_voltage(3300); + } else { + set_aldo2_on_off(false); + } + } // else... +} + +void AXP2101::set_lcd_and_tf_voltage(uint16_t voltage) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (!voltage) { + set_aldo4_on_off(false); + } else { + set_aldo4_on_off(true); + set_aldo4_voltage(voltage); + } + } // else... +} + +void AXP2101::set_vib_motor_voltage(uint16_t voltage) { + if (AXP2101_device_model_e::M5Stack_Core2_v1_1 == _device) { + if (!voltage) { + set_dldo1_on_off(false); + } else { + set_dldo1_on_off(true); + set_dldo1_voltage(voltage); + } + } // else... +} + +/** + * Universal functions + */ +bool AXP2101::set_sys_led(bool sw) { + return bitOnOff(sw, AXP2101_CHGLED_REG, 0b00110000); +} + +bool AXP2101::set_charger_term_current_to_zero(void) { + return bitOff(AXP2101_ADDR, AXP2101_CHARGER_SETTING_REG, 0b00001111); +} + +bool AXP2101::set_charger_constant_current_to_50mA(void) { + return writeRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG, 2); +} + +void AXP2101::set_bat_charge(bool enable) { + uint8_t val = 0; + + if (readRegister(AXP2101_ADDR, AXP2101_CHARG_FGAUG_WDOG_REG, &val, 1)) { + writeRegister8(AXP2101_ADDR, AXP2101_CHARG_FGAUG_WDOG_REG, (val & 0xFD) + (enable << 1)); + } +} + +bool AXP2101::enable_pwrok_resets(void) { + return bitOn(AXP2101_ADDR, + AXP2101_PMU_CONFIG_REG, + 1 << 3); +} + +void AXP2101::power_off(void) { + // 1. AXP2101 Power off + bitOn(AXP2101_ADDR, AXP2101_IRQ_EN_1_REG, 1 << 1); // POWERON Negative Edge IRQ(ponne_irq_en) enable + writeRegister8(AXP2101_ADDR, AXP2101_PWROK_PWROFF_REG, 0b00011011); // sleep and wait for wakeup + delay(100); + writeRegister8(AXP2101_ADDR, AXP2101_PMU_CONFIG_REG, 0b00110001); // power off +} + +uint8_t AXP2101::get_dcdc_status(void) { + return readRegister8(_addr, + AXP2101_DCDC_CTRL_REG); +} + +void AXP2101::getControlRegisterMask(AXP2101_registers_e reg, + uint8_t & ctrl, + uint8_t & mask) { + switch (reg) { + case AXP2101_registers_e::dcdc1: + ctrl = AXP2101_DCDC_CTRL_REG; + mask = AXP2101_DCDC1_CTRL_MASK; + break; + case AXP2101_registers_e::dcdc2: + ctrl = AXP2101_DCDC_CTRL_REG; + mask = AXP2101_DCDC2_CTRL_MASK; + break; + case AXP2101_registers_e::dcdc3: + ctrl = AXP2101_DCDC_CTRL_REG; + mask = AXP2101_DCDC3_CTRL_MASK; + break; + case AXP2101_registers_e::dcdc4: + ctrl = AXP2101_DCDC_CTRL_REG; + mask = AXP2101_DCDC4_CTRL_MASK; + break; + case AXP2101_registers_e::dcdc5: + ctrl = AXP2101_DCDC_CTRL_REG; + mask = AXP2101_DCDC5_CTRL_MASK; + break; + case AXP2101_registers_e::aldo1: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_ALDO1_CTRL_MASK; + break; + case AXP2101_registers_e::aldo2: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_ALDO2_CTRL_MASK; + break; + case AXP2101_registers_e::aldo3: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_ALDO3_CTRL_MASK; + break; + case AXP2101_registers_e::aldo4: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_ALDO4_CTRL_MASK; + break; + case AXP2101_registers_e::bldo1: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_BLDO1_CTRL_MASK; + break; + case AXP2101_registers_e::bldo2: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_BLDO2_CTRL_MASK; + break; + case AXP2101_registers_e::dldo1: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_DLDO1_CTRL_MASK; + break; + case AXP2101_registers_e::dldo2: + ctrl = AXP2101_LDO_CTRL_REG1; + mask = AXP2101_DLDO2_CTRL_MASK; + break; + case AXP2101_registers_e::cpuldos: + ctrl = AXP2101_LDO_CTRL_REG; + mask = AXP2101_CPUSLDO_CTRL_MASK; + break; + } +} diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h new file mode 100644 index 0000000000..8d9fd0421d --- /dev/null +++ b/lib/AXP2101/src/AXP2101.h @@ -0,0 +1,535 @@ +#ifndef __AXP2101_H +#define __AXP2101_H + +/** + * AXP2102 library adjusted for ESPEasy + * 2024-02-04 tonhuisman: Start. + * + * Based on the AXP2102 driver included in https://github.com/m5stack/M5Core2 + */ + +#include +#include + +#define AXP2101_ADDR (0x34) + +#define AXP2101_DCDC_CTRL_REG (0x80) +#define AXP2101_LDO_CTRL_REG (0x90) +#define AXP2101_LDO_CTRL_REG1 (0x91) + +#define AXP2101_DCDC1_VOLTAGE_REG (0x82) +#define AXP2101_DCDC2_VOLTAGE_REG (0x83) +#define AXP2101_DCDC3_VOLTAGE_REG (0x84) +#define AXP2101_DCDC4_VOLTAGE_REG (0x85) +#define AXP2101_DCDC5_VOLTAGE_REG (0x86) +#define AXP2101_ALDO1_VOLTAGE_REG (0x92) +#define AXP2101_ALDO2_VOLTAGE_REG (0x93) +#define AXP2101_ALDO3_VOLTAGE_REG (0x94) +#define AXP2101_ALDO4_VOLTAGE_REG (0x95) +#define AXP2101_BLDO1_VOLTAGE_REG (0x96) +#define AXP2101_BLDO2_VOLTAGE_REG (0x97) +#define AXP2101_DLDO1_VOLTAGE_REG (0x99) +#define AXP2101_DLDO2_VOLTAGE_REG (0x9A) +#define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) + +#define AXP2101_PMU_CONFIG_REG (0x10) +#define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) +#define AXP2101_PWROK_PWROFF_REG (0x25) +#define AXP2101_ADC_ENABLE_REG (0x30) +#define AXP2101_IRQ_EN_1_REG (0x41) +#define AXP2101_ICC_CHARGER_SETTING_REG (0x62) +#define AXP2101_CHARGER_SETTING_REG (0x63) +#define AXP2101_CHGLED_REG (0x69) + +#define AXP2101_DCDC1_CTRL_MASK (1 << 0) +#define AXP2101_DCDC2_CTRL_MASK (1 << 1) +#define AXP2101_DCDC3_CTRL_MASK (1 << 2) +#define AXP2101_DCDC4_CTRL_MASK (1 << 3) +#define AXP2101_DCDC5_CTRL_MASK (1 << 4) +#define AXP2101_ALDO1_CTRL_MASK (1 << 0) +#define AXP2101_ALDO2_CTRL_MASK (1 << 1) +#define AXP2101_ALDO3_CTRL_MASK (1 << 2) +#define AXP2101_ALDO4_CTRL_MASK (1 << 3) +#define AXP2101_BLDO1_CTRL_MASK (1 << 4) +#define AXP2101_BLDO2_CTRL_MASK (1 << 5) +#define AXP2101_DLDO1_CTRL_MASK (1 << 7) +#define AXP2101_DLDO2_CTRL_MASK (1 << 0) +#define AXP2101_CPUSLDO_CTRL_MASK (1 << 6) + +#define AXP2101_DCDC1_MIN (1500) +#define AXP2101_DCDC2_MIN (500) +#define AXP2101_DCDC3_MIN (500) +#define AXP2101_DCDC4_MIN (500) +#define AXP2101_DCDC5_MIN (1400) +#define AXP2101_ALDO1_MIN (500) +#define AXP2101_ALDO2_MIN (500) +#define AXP2101_ALDO3_MIN (500) +#define AXP2101_ALDO4_MIN (500) +#define AXP2101_BLDO1_MIN (500) +#define AXP2101_BLDO2_MIN (500) +#define AXP2101_DLDO1_MIN (500) +#define AXP2101_DLDO2_MIN (500) +#define AXP2101_CPUSLDO_MIN (500) + +#define AXP2101_DCDC1_MAX (3400) +#define AXP2101_DCDC2_MAX (1540) +#define AXP2101_DCDC3_MAX (3400) +#define AXP2101_DCDC4_MAX (1840) +#define AXP2101_DCDC5_MAX (3700) +#define AXP2101_ALDO1_MAX (3500) +#define AXP2101_ALDO2_MAX (3500) +#define AXP2101_ALDO3_MAX (3500) +#define AXP2101_ALDO4_MAX (3500) +#define AXP2101_BLDO1_MAX (3500) +#define AXP2101_BLDO2_MAX (3500) +#define AXP2101_DLDO1_MAX (3500) +#define AXP2101_DLDO2_MAX (1400) +#define AXP2101_CPUSLDO_MAX (1400) + +enum class AXP2101_device_model_e : uint8_t { + Unselected = 0u, // >>>>> Don't change these values, they are stored in user-settings <<<<< + M5Stack_Core2_v1_1 = 1u, // https://docs.m5stack.com/en/core/Core2%20v1.1 + M5Stack_CoreS3 = 2u, // https://docs.m5stack.com/en/core/CoreS3 + LilyGO_TBeam_v1_2 = 3u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_V1.2.pdf + LilyGO_TBeamS3_v3 = 4u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_S3_Core_V3.0.pdf + LilyGO_TPCie_v1_2 = 5u, // https://github.com/Xinyuan-LilyGO/LilyGo-T-PCIE/blob/master/schematic/T-PCIE-V1.2.pdf + + MAX, // Keep MAX as first after last device, devices must be sequentially numbered + UserDefined = 99u, // Keep UserDefined as last!!! +}; + +// The voltage registers mapped into an enum +enum class AXP2101_registers_e : uint8_t { + dcdc1 = AXP2101_DCDC1_VOLTAGE_REG, + dcdc2 = AXP2101_DCDC2_VOLTAGE_REG, + dcdc3 = AXP2101_DCDC3_VOLTAGE_REG, + dcdc4 = AXP2101_DCDC4_VOLTAGE_REG, + dcdc5 = AXP2101_DCDC5_VOLTAGE_REG, + aldo1 = AXP2101_ALDO1_VOLTAGE_REG, + aldo2 = AXP2101_ALDO2_VOLTAGE_REG, + aldo3 = AXP2101_ALDO3_VOLTAGE_REG, + aldo4 = AXP2101_ALDO4_VOLTAGE_REG, + bldo1 = AXP2101_BLDO1_VOLTAGE_REG, + bldo2 = AXP2101_BLDO2_VOLTAGE_REG, + dldo1 = AXP2101_DLDO1_VOLTAGE_REG, + dldo2 = AXP2101_DLDO2_VOLTAGE_REG, + cpuldos = AXP2101_CPUSLDO_VOLTAGE_REG, + + // Above are settable pinstates/voltages of the AXP2101 + // TODO Below are read-only values of the AXP2101, also update AXP2101_register_count when adding values +}; +constexpr int AXP2101_settings_count = 14; // Changeable settings +constexpr int AXP2101_register_count = 14; // All registers + +enum class AXP_pin_s : uint8_t { + Off = 0x00, // Max. 3 bits can be stored in settings! + On = 0x01, + Default = 0x02, // Don't update value or state on boot + Disabled = 0x03, // Port not connected, don't use + Protected = 0x07 // Don't try to change port value, can make the unit fail! +}; + +AXP2101_registers_e AXP2101_intToRegister(int reg); +uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); +uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); +bool AXP2101_isPinDefault(AXP_pin_s pin); // Default, Disabled or Protected +bool AXP2101_isPinProtected(AXP_pin_s pin); // Disabled or Protected + +const __FlashStringHelper* toString(AXP2101_registers_e reg, + bool displayString = true); +const __FlashStringHelper* toString(AXP2101_device_model_e device, + bool displayString = true); +const __FlashStringHelper* toString(AXP_pin_s pin); + +class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. +public: + + AXP2101_settings(); + AXP2101_settings(uint16_t _dcdc1, + uint16_t _dcdc2, + uint16_t _dcdc3, + uint16_t _dcdc4, + uint16_t _dcdc5, + uint16_t _aldo1, + uint16_t _aldo2, + uint16_t _aldo3, + uint16_t _aldo4, + uint16_t _bldo1, + uint16_t _bldo2, + uint16_t _dldo1, + uint16_t _dldo2, + uint16_t _cpuldos, + AXP_pin_s _en_dcdc1, + AXP_pin_s _en_dcdc2, + AXP_pin_s _en_dcdc3, + AXP_pin_s _en_dcdc4, + AXP_pin_s _en_dcdc5, + AXP_pin_s _en_aldo1, + AXP_pin_s _en_aldo2, + AXP_pin_s _en_aldo3, + AXP_pin_s _en_aldo4, + AXP_pin_s _en_bldo1, + AXP_pin_s _en_bldo2, + AXP_pin_s _en_dldo1, + AXP_pin_s _en_dldo2, + AXP_pin_s _en_cpuldos); + void setVoltage(AXP2101_registers_e reg, + int voltage); + int getVoltage(AXP2101_registers_e reg, + bool realValue = true); + void setState(AXP2101_registers_e reg, + AXP_pin_s state); + AXP_pin_s getState(AXP2101_registers_e reg); + +private: + + union { + struct { + uint16_t dcdc1; + uint16_t dcdc2; + uint16_t dcdc3; + uint16_t dcdc4; + uint16_t dcdc5; + uint16_t aldo1; + uint16_t aldo2; + uint16_t aldo3; + uint16_t aldo4; + uint16_t bldo1; + uint16_t bldo2; + uint16_t dldo1; + uint16_t dldo2; + uint16_t cpuldos; + } registers; + uint16_t registers_[AXP2101_settings_count]{}; + }; + union { + struct { // AXP_pin_s: Off / On / default / disabled / unavailable? / unused? / Protected + uint64_t en_dcdc1 : 3; // bit 0/1/2 + uint64_t en_dcdc2 : 3; // bit 3/4/5 + uint64_t en_dcdc3 : 3; // bit 6/7/8 + uint64_t en_dcdc4 : 3; // bit 9/10/11 + uint64_t en_dcdc5 : 3; // bit 12/13/14 + uint64_t en_aldo1 : 3; // bit 15/16/17 + uint64_t en_aldo2 : 3; // bit 18/19/20 + uint64_t en_aldo3 : 3; // bit 21/22/23 + uint64_t en_aldo4 : 3; // bit 24/25/26 + uint64_t en_bldo1 : 3; // bit 27/28/29 + uint64_t en_bldo2 : 3; // bit 30/31/32 + uint64_t en_dldo1 : 3; // bit 33/34/35 + uint64_t en_dldo2 : 3; // bit 36/37/38 + uint64_t en_cpuldos : 3; // bit 39/40/41 + uint64_t en_unused : 21; // bit 42..63 // All bits defined + } pinStates; + uint64_t pinStates_{}; // 8 bytes + }; +}; + +extern AXP2101_settings AXP2101_deviceSettingsArray[]; + +class AXP2101 { +private: + + uint8_t _addr; + TwoWire *_wire; + AXP2101_device_model_e _device = AXP2101_device_model_e::Unselected; + +public: + + AXP2101() {} + + ~AXP2101() {} + + bool begin(TwoWire *wire = & Wire, + uint8_t addr = AXP2101_ADDR, + AXP2101_device_model_e device = AXP2101_device_model_e::Unselected); + + void setDevice(AXP2101_device_model_e device); + +private: + + bool readRegister(uint8_t addr, + uint8_t reg, + uint8_t *result, + uint16_t length); + uint8_t readRegister8(uint8_t addr, + uint8_t reg); + bool writeRegister8(uint8_t addr, + uint8_t reg, + uint8_t data); + bool bitOn(uint8_t addr, + uint8_t reg, + uint8_t data); + bool bitOff(uint8_t addr, + uint8_t reg, + uint8_t data); + bool bitOnOff(bool sw, + uint8_t creg, + uint8_t mask); + bool bitGet(uint8_t reg, + uint8_t data); + void getControlRegisterMask(AXP2101_registers_e reg, + uint8_t & ctrl, + uint8_t & mask); + +public: + + // Utility + uint8_t voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t registerToVoltage(uint8_t data, + AXP2101_registers_e reg); + uint8_t get_dcdc_status(void); + bool setPortVoltage(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t getPortVoltage(AXP2101_registers_e reg); + bool setPortState(bool sw, + AXP2101_registers_e reg); + bool getPortState(AXP2101_registers_e reg); + + // Device common functions + void set_bus_3v3(uint16_t voltage); + void set_lcd_back_light_voltage(uint16_t voltage); + void set_bus_5v(uint8_t sw); + bool set_sys_led(bool sw); + void set_spk(bool sw); + void set_lcd_rst(bool sw); + void set_lcd_and_tf_voltage(uint16_t voltage); + void set_vib_motor_voltage(uint16_t voltage); + void set_bat_charge(bool enable); + void power_off(void); + bool set_charger_term_current_to_zero(void); + bool set_charger_constant_current_to_50mA(void); + bool enable_pwrok_resets(void); + + + // Low-level output functions + bool set_dcdc1_voltage(uint16_t voltage) { // 1.5 - 3.4V 100mV/step, 20 steps + return setPortVoltage(voltage, AXP2101_registers_e::dcdc1); + } + + uint16_t get_dcdc1_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dcdc1); + } + + bool set_dcdc2_voltage(uint16_t voltage) { // 0.5 - 1.2V 10mV/step, 71 steps, + // 1.22 - 1.54V 20mV/step, 17 steps, + return setPortVoltage(voltage, AXP2101_registers_e::dcdc2); + } + + uint16_t get_dcdc2_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dcdc2); + } + + bool set_dcdc3_voltage(uint16_t voltage) { // 0.5 - 1.2V 10mV/step, 71 steps, + // 1.22 - 1.54V 20mV/step, 17 steps, + // 1.6 - 3.4V 100mV/step, 19 steps + return setPortVoltage(voltage, AXP2101_registers_e::dcdc3); + } + + uint16_t get_dcdc3_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dcdc3); + } + + bool set_dcdc4_voltage(uint16_t voltage) { // 0.5 - 1.2V 10mV/step, 71 steps, + // 1.22 - 1.84V 20mV/step, 32 steps + return setPortVoltage(voltage, AXP2101_registers_e::dcdc4); + } + + uint16_t get_dcdc4_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dcdc4); + } + + bool set_dcdc5_voltage(uint16_t voltage) { // 1.4 - 3.7V 100mV/step, 24 steps + return setPortVoltage(voltage, AXP2101_registers_e::dcdc5); + } + + uint16_t get_dcdc5_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dcdc5); + } + + bool set_aldo1_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::aldo1); + } + + uint16_t get_aldo1_voltage(void) { + return getPortVoltage(AXP2101_registers_e::aldo1); + } + + bool set_aldo2_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::aldo2); + } + + uint16_t get_aldo2_voltage(void) { + return getPortVoltage(AXP2101_registers_e::aldo2); + } + + bool set_aldo3_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::aldo3); + } + + uint16_t get_aldo3_voltage(void) { + return getPortVoltage(AXP2101_registers_e::aldo3); + } + + bool set_aldo4_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::aldo4); + } + + uint16_t get_aldo4_voltage(void) { + return getPortVoltage(AXP2101_registers_e::aldo4); + } + + bool set_bldo1_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::bldo1); + } + + uint16_t get_bldo1_voltage(void) { + return getPortVoltage(AXP2101_registers_e::bldo1); + } + + bool set_bldo2_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::bldo2); + } + + uint16_t get_bldo2_voltage(void) { + return getPortVoltage(AXP2101_registers_e::bldo2); + } + + bool set_dldo1_voltage(uint16_t voltage) { // 0.5 - 3.5V 100mV/step, 31 steps + return setPortVoltage(voltage, AXP2101_registers_e::dldo1); + } + + uint16_t get_dldo1_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dldo1); + } + + bool set_dldo2_voltage(uint16_t voltage) { // 0.5 - 1.4V 50mV/step, 20 steps + return setPortVoltage(voltage, AXP2101_registers_e::dldo2); + } + + uint16_t get_dldo2_voltage(void) { + return getPortVoltage(AXP2101_registers_e::dldo2); + } + + bool set_cpuldos_voltage(uint16_t voltage) { // 0.5 - 1.4V 50mV/step, 20 steps + return setPortVoltage(voltage, AXP2101_registers_e::cpuldos); + } + + uint16_t get_cpuldos_voltage(void) { + return getPortVoltage(AXP2101_registers_e::cpuldos); + } + + bool set_dcdc1_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dcdc1); + } + + bool set_dcdc2_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dcdc2); + } + + bool set_dcdc3_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dcdc3); + } + + bool set_dcdc4_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dcdc4); + } + + bool set_dcdc5_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dcdc5); + } + + bool set_aldo1_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::aldo1); + } + + bool set_aldo2_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::aldo2); + } + + bool set_aldo3_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::aldo3); + } + + bool set_aldo4_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::aldo4); + } + + bool set_bldo1_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::bldo1); + } + + bool set_bldo2_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::bldo2); + } + + bool set_dldo1_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dldo1); + } + + bool set_dldo2_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::dldo2); + } + + bool set_cpuldos_on_off(bool sw) { + return setPortState(sw, AXP2101_registers_e::cpuldos); + } + + bool get_dcdc1_on_off() { + return getPortState(AXP2101_registers_e::dcdc1); + } + + bool get_dcdc2_on_off() { + return getPortState(AXP2101_registers_e::dcdc2); + } + + bool get_dcdc3_on_off() { + return getPortState(AXP2101_registers_e::dcdc3); + } + + bool get_dcdc4_on_off() { + return getPortState(AXP2101_registers_e::dcdc4); + } + + bool get_dcdc5_on_off() { + return getPortState(AXP2101_registers_e::dcdc5); + } + + bool get_aldo1_on_off() { + return getPortState(AXP2101_registers_e::aldo1); + } + + bool get_aldo2_on_off() { + return getPortState(AXP2101_registers_e::aldo2); + } + + bool get_aldo3_on_off() { + return getPortState(AXP2101_registers_e::aldo3); + } + + bool get_aldo4_on_off() { + return getPortState(AXP2101_registers_e::aldo4); + } + + bool get_bldo1_on_off() { + return getPortState(AXP2101_registers_e::bldo1); + } + + bool get_bldo2_on_off() { + return getPortState(AXP2101_registers_e::bldo2); + } + + bool get_dldo1_on_off() { + return getPortState(AXP2101_registers_e::dldo1); + } + + bool get_dldo2_on_off() { + return getPortState(AXP2101_registers_e::dldo2); + } + + bool get_cpuldos_on_off() { + return getPortState(AXP2101_registers_e::cpuldos); + } +}; + +#endif // ifndef __AXP2101_H diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino new file mode 100644 index 0000000000..966e11f2c2 --- /dev/null +++ b/src/_P139_AXP2101.ino @@ -0,0 +1,418 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P139 + +# ifdef ESP32 + +// ####################################################################################################### +// ################################### Plugin 139: AXP2101 Powermanagement ############################### +// ####################################################################################################### + +/** + * Changelog: + * 2024-02-15 tonhuisman: First plugin version, in ReadOnly mode only, no data is written to the AXP2101, only the register to read + * 2024-02-04 tonhuisman: Initial plugin development, only available for ESP32 + **/ + +/** + * Supported commands: (using same command as P137 AXP192 as no hardware overlap is possible) + * axp,readchip : Read current settings from AXP2101 chip and list values and state to the log at INFO level + * axp,voltage,, : Set port to given voltage and on, or turn off if below minimum value + * axp,on, : Turn On port + * axp,off, : Turn Off port + * axp,percentage,, : Set port to percentage of Low to High range (min/max or set range per port) + * axp,range,,, : Define low/high range for port. Low and High must be withing technical range of port + * axp,range : List current range configuration (or when providing an out of range low/high argument) + * TODO: Add more commands? + **/ +/** + * Get Config options: + * [#dcdc1] : Returns the voltage from the named port. + * [#dcdc2] : Can also read the status by using [#dcdc1.status] (text: On/Off/Default/Disabled/Protected) + * [#dcdc3] : Can also read the numeric status by using [#dcdc1.state] (0/1/2/3/7) + * [#dcdc4] : + * [#dcdc5] : + * [#aldo1] : + * [#aldo2] : + * [#aldo3] : + * [#aldo4] : + * [#bldo1] : + * [#bldo2] : + * [#dldo1] : + * [#dldo2] : + * [#cpuldos] : + * TODO: Define additional values? + **/ +/** + * Events: + * TODO: Define events? + */ + + +# define PLUGIN_139 +# define PLUGIN_ID_139 139 +# define PLUGIN_NAME_139 "Power mgt - AXP2101 Power management" + +# include "./src/PluginStructs/P139_data_struct.h" + +boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_139; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; + Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; + Device[deviceCount].PowerManager = true; // So it can be started before SPI is initialized + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 4; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].PluginStats = true; + break; + } + + case PLUGIN_PRIORITY_INIT: + { + const bool isPowerManagerTask = Settings.isPowerManagerTask(event->TaskIndex); + # ifndef BUILD_NO_DEBUG + addLogMove(LOG_LEVEL_DEBUG, F("P139: PLUGIN_PRIORITY_INIT")); + # endif // ifndef BUILD_NO_DEBUG + success = isPowerManagerTask; // Are we the PowerManager task? + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_139); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { + if (i < P139_NR_OUTPUT_VALUES) { + ExtraTaskSettings.setTaskDeviceValueName(i, toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); + } else { + ExtraTaskSettings.clearTaskDeviceValueName(i); + } + } + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = P139_NR_OUTPUT_VALUES; + success = true; + break; + } + + case PLUGIN_GET_DEVICEVTYPE: + { + event->sensorType = static_cast(PCONFIG(P139_SENSOR_TYPE_INDEX)); + event->idx = P139_SENSOR_TYPE_INDEX; + success = true; + break; + } + + case PLUGIN_I2C_HAS_ADDRESS: + { + success = event->Par1 == AXP2101_ADDR; + break; + } + + # if FEATURE_I2C_GET_ADDRESS + case PLUGIN_I2C_GET_ADDRESS: + { + event->Par1 = AXP2101_ADDR; + success = true; + break; + } + # endif // if FEATURE_I2C_GET_ADDRESS + + case PLUGIN_SET_DEFAULTS: + { + PCONFIG(0) = static_cast(AXP2101_registers_e::dcdc1); + PCONFIG(1) = static_cast(AXP2101_registers_e::dcdc3); + PCONFIG(2) = static_cast(AXP2101_registers_e::aldo1); + PCONFIG(3) = static_cast(AXP2101_registers_e::dldo1); + PCONFIG(P139_SENSOR_TYPE_INDEX) = static_cast(Sensor_VType::SENSOR_TYPE_QUAD); + P139_CONFIG_DECIMALS = 2; // 2 decimals for all get config values + AXP2101_device_model_e device = AXP2101_device_model_e::M5Stack_Core2_v1_1; + P139_CONFIG_PREDEFINED = static_cast(device); // M5Stack Core2 v1.1 + + P139_data_struct *P139_data = new (std::nothrow) P139_data_struct(event); + + if (nullptr != P139_data) { + P139_data->applySettings(device); + P139_data->saveSettings(event); + delete P139_data; + } + P139_CONFIG_PREDEFINED = 0; // Settings applied + + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + const bool isPowerManagerTask = Settings.isPowerManagerTask(event->TaskIndex); + bool created_new = false; + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P139_data) { + P139_data = new (std::nothrow) P139_data_struct(event); + created_new = true; + } + + if (nullptr == P139_data) { + break; + } + + // addFormNumericBox(F("Decimals for config values"), F("decimals"), P139_CONFIG_DECIMALS, 0, 4); + + addFormSubHeader(F("Hardware outputs AXP2101")); + + { + if (P139_CONFIG_PREDEFINED > 0) { + P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; + P139_data->applySettings(static_cast(P139_CONFIG_PREDEFINED)); + } + const __FlashStringHelper *predefinedNames[] = { + toString(AXP2101_device_model_e::Unselected), + toString(AXP2101_device_model_e::M5Stack_Core2_v1_1), + toString(AXP2101_device_model_e::M5Stack_CoreS3), + toString(AXP2101_device_model_e::LilyGO_TBeam_v1_2), + toString(AXP2101_device_model_e::LilyGO_TBeamS3_v3), + toString(AXP2101_device_model_e::LilyGO_TPCie_v1_2), + toString(AXP2101_device_model_e::UserDefined) // keep last !! + }; + const int predefinedValues[] = { + static_cast(AXP2101_device_model_e::Unselected), + static_cast(AXP2101_device_model_e::M5Stack_Core2_v1_1), + static_cast(AXP2101_device_model_e::M5Stack_CoreS3), + static_cast(AXP2101_device_model_e::LilyGO_TBeam_v1_2), + static_cast(AXP2101_device_model_e::LilyGO_TBeamS3_v3), + static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), + static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! + addFormSelector(F("Predefined device configuration"), F("predef"), + NR_ELEMENTS(predefinedValues), + predefinedNames, predefinedValues, 0, !isPowerManagerTask); + + if (!isPowerManagerTask) { + addFormNote(F("Page will reload when selection is changed.")); + } + + const AXP2101_device_model_e device = static_cast(P139_CURRENT_PREDEFINED); + + if (AXP2101_device_model_e::Unselected != device) { + addFormNote(concat(F("Last selected: "), toString(device))); + + if (AXP2101_device_model_e::UserDefined == device) { + addHtml(F( + "
Warning: Configuring invalid values can damage your device or render it useless!
")); + } + } + } + + { + const __FlashStringHelper *bootStates[] = { + toString(AXP_pin_s::Off), + toString(AXP_pin_s::On), + toString(AXP_pin_s::Default), + }; + const int bootStateValues[] = { + static_cast(AXP_pin_s::Off), + static_cast(AXP_pin_s::On), + static_cast(AXP_pin_s::Default), + }; + + // Don't include Disabled or Protected here, not user-selectable + constexpr int bootStatesCount = NR_ELEMENTS(bootStates); + + addRowLabel(F("Output ports")); + + html_table(EMPTY_STRING); + html_table_header(F("Name"), 100); + html_table_header(F("Voltage (mV)"), 270); + html_table_header(F("Pin state"), 150); + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + const AXP_pin_s pin = P139_data->_settings.getState(reg); + html_TR_TD(); + addHtml(toString(reg)); + html_TD(); + addNumericBox(toString(reg, false), + P139_data->_settings.getVoltage(reg, false), + -1, + AXP2101_maxVoltage(reg), + AXP2101_isPinDefault(pin)); + addUnit(strformat(F("range %d - %d"), AXP2101_minVoltage(reg), AXP2101_maxVoltage(reg))); + html_TD(); + + if (AXP2101_isPinProtected(pin)) { + addUnit(toString(pin)); + } else { + addSelector(concat(F("ps"), toString(reg, false)), + bootStatesCount, + bootStates, + bootStateValues, + nullptr, + static_cast(pin)); + } + } + html_end_table(); + + // addFormNote(F("Values < min. range will switch off the output. Set to -1 to not initialize/unused.")); + addFormNote(F("Check your device documentation for what is connected to each output.")); + } + + { + // Keep this setting hidden from the UI + // addHtml(F("")); + } + + if (created_new) { + delete P139_data; + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR: + { + const __FlashStringHelper *valOptions[AXP2101_register_count + 1]; + int valValues[AXP2101_register_count + 1]; + + valOptions[0] = F("None"); + valValues[0] = 0; + + for (int r = 0; r < AXP2101_register_count; ++r) { + AXP2101_registers_e reg = AXP2101_intToRegister(r); + valOptions[r + 1] = toString(reg); + valValues[r + 1] = static_cast(reg); + } + + for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { + sensorTypeHelper_loadOutputSelector(event, + P139_CONFIG_BASE + i, + i, + NR_ELEMENTS(valValues), + valOptions, + valValues); + } + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { + sensorTypeHelper_saveOutputSelector(event, P139_CONFIG_BASE + i, i, + toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); + } + + // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(getFormItemInt(F("pdcdc2")), P139_CONST_MAX_DCDC2) << 16) | + // P139_valueToSetting(getFormItemInt(F("pldo2")), P139_CONST_MAX_LDO); + // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(getFormItemInt(F("pdcdc3")), P139_CONST_MAX_DCDC) << 16) | + // P139_valueToSetting(getFormItemInt(F("pldo3")), P139_CONST_MAX_LDO); + // P139_REG_LDOIO = P139_valueToSetting(getFormItemInt(F("ldoiovolt")), P139_CONST_MAX_LDOIO); + + // for (int i = 0; i < 5; i++) { // GPIO0..4 + // P139_SET_GPIO_FLAGS(i, getFormItemInt(concat(F("pgpio"), i))); + // } + + // P139_CONFIG_DECIMALS = getFormItemInt(F("decimals")); + P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + + // P139_CONFIG_DISABLEBITS = getFormItemInt(F("pbits"), static_cast(P139_CONFIG_DISABLEBITS)); // Keep previous value if not + // found + + success = true; + break; + } + + case PLUGIN_INIT: + { + P139_data_struct *P139_init = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_init) { + # ifndef BUILD_NO_DEBUG + addLogMove(LOG_LEVEL_INFO, F("P139: Already initialized, skipped.")); + # endif // ifndef BUILD_NO_DEBUG + // has been initialized so nothing to do here + success = true; // Still was successful (to keep plugin enabled!) + } else { + # ifndef BUILD_NO_DEBUG + addLogMove(LOG_LEVEL_DEBUG, F("P139: PLUGIN_INIT")); + # endif // ifndef BUILD_NO_DEBUG + success = initPluginTaskData(event->TaskIndex, new (std::nothrow) P139_data_struct(event)); + } + + break; + } + + case PLUGIN_READ: + { + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_data) { + success = P139_data->plugin_read(event); + } + break; + } + + case PLUGIN_TEN_PER_SECOND: + { + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_data) { + success = P139_data->plugin_ten_per_second(event); + } + break; + } + + case PLUGIN_FIFTY_PER_SECOND: + { + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_data) { + success = P139_data->plugin_fifty_per_second(event); + } + break; + } + + case PLUGIN_WRITE: + { + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_data) { + success = P139_data->plugin_write(event, string); + } + break; + } + + case PLUGIN_GET_CONFIG_VALUE: + { + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P139_data) { + success = P139_data->plugin_get_config_value(event, string); // GetConfig operation, handle variables + } + break; + } + } + + return success; +} + +# endif // ifdef ESP32 +#endif // USES_P139 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 5a46bc0905..383c068931 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1507,6 +1507,9 @@ To create/register a plugin, you have to : #if !defined(USES_P138) && defined(ESP32) #define USES_P138 // IP5306 #endif + #if !defined(USES_P139) && defined(ESP32) + #define USES_P139 // AXP2101 + #endif #endif #ifdef PLUGIN_SET_COLLECTION_A @@ -1685,6 +1688,9 @@ To create/register a plugin, you have to : #endif #if !defined(USES_P138) && defined(ESP32) #define USES_P138 // IP5306 + #endif + #if !defined(USES_P139) && defined(ESP32) + #define USES_P139 // AXP2101 #endif #if !defined(USES_P148) && defined(ESP32) #define USES_P148 // Sonoff POWR3xxD and THR3xxD display @@ -1764,6 +1770,9 @@ To create/register a plugin, you have to : #if !defined(USES_P138) && defined(ESP32) #define USES_P138 // IP5306 #endif + #if !defined(USES_P139) && defined(ESP32) + #define USES_P139 // AXP2101 + #endif #ifndef USES_P141 #define USES_P141 // PCD8544 Nokia 5110 #endif @@ -1952,6 +1961,9 @@ To create/register a plugin, you have to : #if !defined(USES_P137) && defined(ESP32) #define USES_P137 // AXP192 #endif + #if !defined(USES_P139) && defined(ESP32) + #define USES_P139 // AXP2101 + #endif #if FEATURE_PLUGIN_STATS && defined(ESP8266) // Does not fit in build #undef FEATURE_PLUGIN_STATS @@ -2256,7 +2268,7 @@ To create/register a plugin, you have to : #define USES_P138 // IP5306 #endif #ifndef USES_P139 -// #define USES_P139 // + #define USES_P139 // AXP2101 #endif #ifndef USES_P140 // #define USES_P140 // @@ -3134,7 +3146,7 @@ To create/register a plugin, you have to : #endif // ifndef USES_ESPEASY_CONSOLE_FALLBACK_PORT -#if !FEATURE_PLUGIN_PRIORITY && (defined(USES_P137) /*|| defined(USES_Pxxx)*/) +#if !FEATURE_PLUGIN_PRIORITY && (defined(USES_P137) || defined(USES_P139)) #undef FEATURE_PLUGIN_PRIORITY #define FEATURE_PLUGIN_PRIORITY 1 #endif diff --git a/src/src/PluginStructs/P137_data_struct.cpp b/src/src/PluginStructs/P137_data_struct.cpp index 543132c886..8c35d0d218 100644 --- a/src/src/PluginStructs/P137_data_struct.cpp +++ b/src/src/PluginStructs/P137_data_struct.cpp @@ -135,8 +135,8 @@ const __FlashStringHelper* toString(const P137_PredefinedDevices_e device) { switch (device) { case P137_PredefinedDevices_e::Unselected: return F("Select an option to set default values"); case P137_PredefinedDevices_e::M5Stack_StickC: return F("M5Stack StickC"); - case P137_PredefinedDevices_e::M5Stack_Core2: return F("M5Stack Core2 (Default)"); - case P137_PredefinedDevices_e::LilyGO_TBeam: return F("LilyGO T-Beam"); + case P137_PredefinedDevices_e::M5Stack_Core2: return F("M5Stack Core2 v1.0 (Default)"); + case P137_PredefinedDevices_e::LilyGO_TBeam: return F("LilyGO T-Beam v1.1"); case P137_PredefinedDevices_e::UserDefined: return F("User defined"); } return F("*Undefined*"); diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp new file mode 100644 index 0000000000..021de4ceb1 --- /dev/null +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -0,0 +1,489 @@ +#include "../PluginStructs/P139_data_struct.h" + +#ifdef USES_P139 + +# ifdef ESP32 + +// int16_t P139_settingToValue(uint16_t data, +// uint16_t threshold) { +// if (data > threshold) { +// return threshold - data; +// } +// return data; +// } + +// uint16_t P139_valueToSetting(int data, +// uint16_t threshold) { +// if (data <= -1) { +// return threshold - data; +// } +// return static_cast(data); +// } + +/** + * Apply default parameters based on P139_CONFIG_PREDEFINED setting. + * This will be 0 by default, so nothing is changed. + * P139_CONFIG_DISABLEBITS: (set to 1 to disable that setting) + * Bit = Feature + * 0 = LDO2 + * 1 = LDO3 + * 2 = LDOIO + * 3 = GPIO0 + * 4 = GPIO1 + * 5 = GPIO2 + * 6 = GPIO3 + * 7 = GPIO4 + * 8 = DCDC2 + * 9 = DCDC3 + */ +void P139_CheckPredefinedParameters(struct EventStruct *event) { + if (P139_CONFIG_PREDEFINED > 0) { + P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; + + // // Set defaults + // for (int i = 0; i < 5; i++) { // GPI0..4 + // P139_SET_GPIO_FLAGS(i, static_cast(P139_GPIOBootState_e::Default)); + // } + + switch (static_cast(P139_CONFIG_PREDEFINED)) { + case AXP2101_device_model_e::M5Stack_Core2_v1_1: // M5Stack Core2 V1.1 and newer + { + // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3000, + // P139_CONST_MAX_LDO); + // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(3000, + // P139_CONST_MAX_LDO); + // P139_REG_LDOIO = P139_valueToSetting(2800, P139_CONST_MAX_LDOIO); + // P139_CONFIG_DISABLEBITS = 0b1111110000; // NC pins disabled + break; + } + case AXP2101_device_model_e::M5Stack_CoreS3: // M5Stack CoreS3 + { + // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, + // P139_CONST_MAX_LDO); + // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(0, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(0, P139_CONST_MAX_LDO); + // P139_REG_LDOIO = P139_valueToSetting(3300, P139_CONST_MAX_LDOIO); + // P139_CONFIG_DISABLEBITS = 0b0101000000; // NC pins disabled + // // Specials: GPIO1 High = LED off, GPIO4 High = Enable TFT + // P139_SET_GPIO_FLAGS(1, static_cast(P139_GPIOBootState_e::Output_high)); + // P139_SET_GPIO_FLAGS(4, static_cast(P139_GPIOBootState_e::Output_high)); + break; + } + case AXP2101_device_model_e::LilyGO_TBeam_v1_2: // LilyGO T-Beam V1.2 and newer + case AXP2101_device_model_e::LilyGO_TBeamS3_v3: // LilyGO T-BeamS3 V3 and newer + case AXP2101_device_model_e::LilyGO_TPCie_v1_2: // LilyGO T-PCie V1.2 and newer + { + // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, + // P139_CONST_MAX_LDO); + // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(3300, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(3300, + // P139_CONST_MAX_LDO); + // P139_REG_LDOIO = P139_valueToSetting(3300, P139_CONST_MAX_LDOIO); + // P139_CONFIG_DISABLEBITS = 0b1111111000; // NC pins disabled + break; + } + case AXP2101_device_model_e::UserDefined: // User defined + { + // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, + // P139_CONST_MAX_LDO); + // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(-1, + // P139_CONST_MAX_LDO); + // P139_REG_LDOIO = P139_valueToSetting(-1, P139_CONST_MAX_LDOIO); + // P139_CONFIG_DISABLEBITS = 0b0000000000; // All pins enabled + break; + } + case AXP2101_device_model_e::Unselected: + case AXP2101_device_model_e::MAX: + // Fall through + break; + } + } +} + +// **************************************************************************/ +// toString: convert P139_valueOptions_e enum to value-name or display-name +// **************************************************************************/ +const __FlashStringHelper* toString(const P139_valueOptions_e value, + bool displayString) { + switch (value) { + case P139_valueOptions_e::None: return displayString ? F("None") : F("none"); + case P139_valueOptions_e::BatteryVoltage: return displayString ? F("Battery voltage") : F("batteryvoltage"); + case P139_valueOptions_e::BatteryDischargeCurrent: return displayString ? F("Battery discharge current") : F("batterydischargecurrent"); + case P139_valueOptions_e::BatteryChargeCurrent: return displayString ? F("Battery charge current") : F("batterychargecurrent"); + case P139_valueOptions_e::BatteryPower: return displayString ? F("Battery power") : F("batterypower"); + case P139_valueOptions_e::AcinVoltage: return displayString ? F("Input voltage") : F("inputvoltage"); + case P139_valueOptions_e::AcinCurrent: return displayString ? F("Input current") : F("inputcurrent"); + case P139_valueOptions_e::VbusVoltage: return displayString ? F("VBus voltage") : F("vbusvoltage"); + case P139_valueOptions_e::VbusCurrent: return displayString ? F("VBus current") : F("vbuscurrent"); + case P139_valueOptions_e::InternalTemperature: return displayString ? F("Internal temperature") : F("internaltemperature"); + case P139_valueOptions_e::ApsVoltage: return displayString ? F("APS Voltage") : F("apsvoltage"); + case P139_valueOptions_e::LDO2: return displayString ? F("LDO2 Voltage") : F("ldo2voltage"); + case P139_valueOptions_e::LDO3: return displayString ? F("LDO3 Voltage") : F("ldo3voltage"); + case P139_valueOptions_e::LDOIO: return displayString ? F("LDOIO Voltage") : F("gpiovoltage"); + case P139_valueOptions_e::DCDC2: return displayString ? F("DCDC2 Voltage") : F("dcdc2voltage"); + case P139_valueOptions_e::DCDC3: return displayString ? F("DCDC3 Voltage") : F("dcdc3voltage"); + } + return F("*Undefined*"); +} + +// **************************************************************************/ +// toString: convert P139_GPIOBootState_e enum to string +// **************************************************************************/ +const __FlashStringHelper* toString(const P139_GPIOBootState_e value) { + switch (value) { + case P139_GPIOBootState_e::Default: return F("Default"); + case P139_GPIOBootState_e::Output_low: return F("Output, low"); + case P139_GPIOBootState_e::Output_high: return F("Output, high"); + case P139_GPIOBootState_e::Input: return F("Input"); + case P139_GPIOBootState_e::PWM: return F("PWM"); + } + return F("*Undefined*"); +} + +// **************************************************************************/ +// Constructor +// **************************************************************************/ +P139_data_struct::P139_data_struct(struct EventStruct *event) { + axp2101 = new (std::nothrow) AXP2101(); // Default address and I2C Wire object + + if (isInitialized()) { // Functions based on: + // I2C_AXP192_InitDef initDef = { // M5Stack StickC / M5Stack Core2 / LilyGO T-Beam + // .EXTEN = true, // Enable ESP Power + // .BACKUP = true, // Enable RTC power + // .DCDC1 = 3300, // ESP Power / ESP Power (Fixed) / OLed + // .DCDC2 = P139_GET_CONFIG_DCDC2, // Unused / Unused / Unused + // .DCDC3 = P139_GET_CONFIG_DCDC3, // Unused / LCD Backlight / ESP Power + // .LDO2 = P139_GET_CONFIG_LDO2, // Backlight power (3000 mV) / Periferal VDD / LoRa + // .LDO3 = P139_GET_CONFIG_LDO3, // TFT Power (3000 mV) / Vibration motor / GPS + // .LDOIO = P139_GET_CONFIG_LDOIO, // LDOIO voltage (2800 mV) + // .GPIO0 = static_cast(P139_GET_FLAG_GPIO0 - 1), // Microphone power / Bus pwr enable / Unused + // .GPIO1 = static_cast(P139_GET_FLAG_GPIO1 - 1), // Unused / Sys Led (green) / Unused + // .GPIO2 = static_cast(P139_GET_FLAG_GPIO2 - 1), // Unused / Speaker enable / Unused + // .GPIO3 = static_cast(P139_GET_FLAG_GPIO3 - 1), // Unused / Unused / Unused + // .GPIO4 = static_cast(P139_GET_FLAG_GPIO4 - 1), // Unused / TFT Reset / Unused + // }; + // ldo2_value = P139_GET_CONFIG_LDO2; + // ldo3_value = P139_GET_CONFIG_LDO3; + // ldoio_value = P139_GET_CONFIG_LDOIO; + // dcdc2_value = P139_GET_CONFIG_DCDC2; + // dcdc3_value = P139_GET_CONFIG_DCDC3; + + // axp2101->begin(initDef); + axp2101->begin(&Wire, AXP2101_ADDR, static_cast(P139_CONFIG_PREDEFINED)); + loadSettings(event); + outputSettings(event); + } else { + addLog(LOG_LEVEL_ERROR, F("AXP2101: Initialization failed")); + } +} + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +P139_data_struct::~P139_data_struct() { + delete axp2101; +} + +// **************************************************************************/ +// loadSettings: Load the (custom)settings from flash +// **************************************************************************/ +String P139_data_struct::loadSettings(struct EventStruct *event) { + String result; + + if (!_settingsLoaded) { + result = LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast(&_settings), sizeof(_settings)); + _settingsLoaded = true; + } + return result; +} + +// **************************************************************************/ +// outputSettings: Write the current settings to AXP2101 +// **************************************************************************/ +void P139_data_struct::outputSettings(struct EventStruct *event) { + uint8_t count = 0; + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + const AXP_pin_s pinState = _settings.getState(reg); + + if (!AXP2101_isPinDefault(pinState)) { + if (AXP_pin_s::On == pinState) { + // axp2101->setPortVoltage(_settings.getVoltage(reg), reg); + // axp2101->setPortState(true, reg); // Turn on after setting the voltage + } else { + // axp2101->setPortState(false, reg); // Turn off + } + ++count; + } + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set %d values to port(s)"), count)); + } +} + +// **************************************************************************/ +// saveSettings: Save the settings to the custom settings area +// **************************************************************************/ +String P139_data_struct::saveSettings(struct EventStruct *event) { + String result = SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&_settings), sizeof(_settings)); + + _settingsLoaded = true; // When freshly saved == loaded :-) + return result; +} + +// **************************************************************************/ +// applySettings: Update settings to defaults for selected device +// **************************************************************************/ +bool P139_data_struct::applySettings(AXP2101_device_model_e device) { + const int idx = static_cast(AXP2101_device_model_e::UserDefined == device ? + AXP2101_device_model_e::MAX : + device); + + if ((idx > static_cast(AXP2101_device_model_e::Unselected)) && + (idx <= static_cast(AXP2101_device_model_e::MAX))) { + _settings = AXP2101_deviceSettingsArray[idx]; + return true; + } + return false; +} + +// **************************************************************************/ +// plugin_read: Read the values and send to controller(s) +// **************************************************************************/ +bool P139_data_struct::plugin_read(struct EventStruct *event) { + bool success = true; + + const uint8_t valueCount = P139_NR_OUTPUT_VALUES; + + for (uint8_t i = 0; i < valueCount; ++i) { + UserVar.setFloat(event->TaskIndex, i, read_value(static_cast(PCONFIG(P139_CONFIG_BASE + i)))); + } + + return success; +} + +// **************************************************************************/ +// read_value: Read the requested value +// **************************************************************************/ +float P139_data_struct::read_value(AXP2101_registers_e value) { + if (isInitialized()) { + return static_cast(axp2101->getPortVoltage(value)); + } + return 0.0f; +} + +// **************************************************************************/ +// plugin_ten_per_second: Check state and generate events +// **************************************************************************/ +bool P139_data_struct::plugin_ten_per_second(struct EventStruct *event) { + // TODO + return false; +} + +// **************************************************************************/ +// plugin_fifty_per_second: Check state and generate events +// **************************************************************************/ +bool P139_data_struct::plugin_fifty_per_second(struct EventStruct *event) { + // TODO + return false; +} + +// **************************************************************************/ +// plugin_write: Process commands +// **************************************************************************/ +const char P139_subcommands[] PROGMEM = "readchip|voltage|off|on|percentage|range"; + +enum class P139_subcommands_e : int8_t { + invalid = -1, + readchip = 0, + voltage, + off, + on, + percentage, + range, +}; + +bool P139_data_struct::plugin_write(struct EventStruct *event, + const String & string) { + bool success = false; + String cmd = parseString(string, 1); + + if (isInitialized() && equals(cmd, F("axp"))) { // Command trigger + cmd = parseString(string, 2); // sub command + const int subcommand_i = GetCommandCode(cmd.c_str(), P139_subcommands); + + if (-1 == subcommand_i) { return success; } // Shortcut + + const P139_subcommands_e subcmd = static_cast(subcommand_i); + + const String var3 = parseString(string, 3); + const bool empty3 = var3.isEmpty(); + const bool empty4 = parseString(string, 4).isEmpty(); + + switch (subcmd) { + case P139_subcommands_e::invalid: break; + case P139_subcommands_e::readchip: + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, F("AXP2101: Port voltages (mV), state and range:")); + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + addLog(LOG_LEVEL_INFO, strformat(F("Port: %7s: %4dmV, state: %d, range: %d - %dmV"), + toString(reg), + axp2101->getPortVoltage(reg), + axp2101->getPortState(reg), + AXP2101_minVoltage(reg), + AXP2101_maxVoltage(reg))); + } + } else { + addLog(LOG_LEVEL_ERROR, F("AXP2101: 'readchip' needs logging level INFO")); + } + + success = true; + break; + + case P139_subcommands_e::voltage: + + if (!empty3 && !empty4) { + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (equals(var3, toString(reg, false))) { + const int min_ = AXP2101_minVoltage(reg); + + if (0 == event->Par3 /* < min_ */) { + // TODO Q: Turn off when A) 0 or B) below minimum voltage? + // axp2101->setPortState(false, reg); + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn off port %s"), toString(reg))); + } + success = true; + } else + if ((event->Par3 >= min_) && (event->Par3 <= AXP2101_maxVoltage(reg))) { + // axp2101->setPortVoltage(event->Par3, reg); + // axp2101->setPortState(true, reg); // Turn on after setting the voltage + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set port %s to %dmV"), toString(reg), event->Par3)); + } + success = true; + } + } + } + } + break; + + case P139_subcommands_e::off: + case P139_subcommands_e::on: + + if (!empty3) { + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (equals(var3, toString(reg, false))) { + const bool stateOn = P139_subcommands_e::on == subcmd; + const AXP_pin_s pinState = _settings.getState(reg); + + if (AXP2101_isPinProtected(pinState)) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat(F("AXP2101: Port %s is %s"), toString(reg), toString(pinState))); + } + } else { + // axp2101->setPortState(stateOn, reg); + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn %s port %s"), (stateOn ? F("On") : F("Off")), toString(reg))); + } + success = true; + } + } + } + } + break; + + case P139_subcommands_e::percentage: + + if ((event->Par3 >= 0) && (event->Par3 <= P139_CONST_100_PERCENT) && !empty3 && empty4) { + for (int s = 0; s < AXP2101_settings_count && !success; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (equals(var3, toString(reg, false)) && !AXP2101_isPinProtected(_settings.getState(reg))) { + if (event->Par3 > 0) { + const uint16_t _value = map(event->Par3, + P139_CONST_1_PERCENT, P139_CONST_100_PERCENT, + _ranges[s][0], _ranges[s][1]); + + // axp2101->setPortVoltage(_value, reg); + // axp2101->setPortState(true, reg); // Turn on after setting the voltage + } else { + // axp2101->setPortState(false, reg); // Turn off + } + success = true; + } + } + } + break; + + case P139_subcommands_e::range: + + if ((event->Par4 >= 0) && (event->Par4 <= P139_CONST_MAX_LDO) && + (event->Par3 < event->Par4) && !empty3 && !empty4) { + for (int s = 0; s < AXP2101_settings_count && !success; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (equals(var3, toString(reg, false)) && + (event->Par3 >= AXP2101_minVoltage(reg)) && + (event->Par4 <= AXP2101_maxVoltage(reg))) { + _ranges[s][0] = event->Par3; + _ranges[s][1] = event->Par4; + } + } + } else { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + for (int s = 0; s < AXP2101_settings_count && !success; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: %7s Percentage range: %d - %dmV (State: %s)"), + toString(reg), + _ranges[s][0], + _ranges[s][1], + toString(_settings.getState(reg)))); + } + } + } + success = true; + break; + } // switch + } + + return success; +} + +/**************************************************************************** + * plugin_get_config_value: Retrieve values like [#] + ***************************************************************************/ +bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, + String & string) { + bool success = false; + const String command = parseString(string, 1); + + for (int r = 0; r < AXP2101_register_count && !success; ++r) { + const AXP2101_registers_e reg = AXP2101_intToRegister(r); + + if (equals(command, toString(reg, false))) { // Voltage (mV) + string = axp2101->getPortVoltage(reg); + success = true; + } else + if (command.equals(concat(toString(reg, false), F(".status")))) { // Status (name) + string = toString(axp2101->getPortState(reg)); + success = true; + } else + if (command.equals(concat(toString(reg, false), F(".state")))) { // State (int) + string = static_cast(axp2101->getPortState(reg)); + success = true; + } + } + + return success; +} + +# endif // ifdef ESP32 +#endif // ifdef USES_P139 diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h new file mode 100644 index 0000000000..c6ba39d56b --- /dev/null +++ b/src/src/PluginStructs/P139_data_struct.h @@ -0,0 +1,150 @@ +#ifndef PLUGINSTRUCTS_P139_DATA_STRUCT_H +#define PLUGINSTRUCTS_P139_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P139 + +# ifdef ESP32 + +# include + +# define P139_DEBUG_LOG // Enable for some (extra) logging + +# define P139_CONFIG_BASE 0 // Uses PCONFIG(0)..PCONFIG(3) to store the selection for 4 output values +# define P139_SENSOR_TYPE_INDEX (P139_CONFIG_BASE + VARS_PER_TASK) +# define P139_NR_OUTPUT_VALUES getValueCountFromSensorType(static_cast(PCONFIG(P139_SENSOR_TYPE_INDEX))) +# define P139_CONFIG_DECIMALS PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 1) +# define P139_CONFIG_PREDEFINED PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 2) +# define P139_CURRENT_PREDEFINED PCONFIG_FLOAT(0) + +// # define P139_CONFIG_DISABLEBITS PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 3) + +# define P139_CONST_1_PERCENT 1 // Lowest used percentage, 0 = off +# define P139_CONST_100_PERCENT 100 // Max percentage +# define P139_CONST_MIN_LDO 500 // Min. output voltage +# define P139_CONST_MAX_LDO 3700 // Max. output voltage +// # define P139_CONST_MIN_LDO 1800 // Min. accepted LDO output voltage +// # define P139_CONST_MIN_LDOIO 1800 // Min. accepted GPIO output voltage +// # define P139_CONST_MAX_LDOIO 3300 // Max. GPIO output voltage +// # define P139_CONST_MIN_DCDC 700 // Min. accepted DCDC output voltage +// # define P139_CONST_MAX_DCDC 3500 // Max. DCDC output voltage +// # define P139_CONST_MAX_DCDC2 2750 // Max. DCDC2 output voltage + +// to break comment indenting +# define P139_dummy + +// # define P139_REG_DCDC2_LDO2 PCONFIG_ULONG(0) // DCDC2 & LDO2 +// # define P139_REG_DCDC3_LDO3 PCONFIG_ULONG(1) // DCDC3 & LDO3 +// # define P139_REG_LDOIO PCONFIG_ULONG(2) // GPIO +// # define P139_CONFIG_FLAGS PCONFIG_ULONG(3) // Other flags + +// # define P139_GET_CONFIG_LDO2 P139_settingToValue(P139_REG_DCDC2_LDO2 & 0xFFFF, P139_CONST_MAX_LDO) // LDO2 +// # define P139_GET_CONFIG_LDO3 P139_settingToValue(P139_REG_DCDC3_LDO3 & 0xFFFF, P139_CONST_MAX_LDO) // LDO3 +// # define P139_GET_CONFIG_DCDC2 P139_settingToValue((P139_REG_DCDC2_LDO2 >> 16) & 0xFFFF, P139_CONST_MAX_DCDC2) // DCDC2 +// # define P139_GET_CONFIG_DCDC3 P139_settingToValue((P139_REG_DCDC3_LDO3 >> 16) & 0xFFFF, P139_CONST_MAX_DCDC) // DCDC3 +// # define P139_GET_CONFIG_LDOIO P139_settingToValue(P139_REG_LDOIO & 0xFFFF, P139_CONST_MAX_LDOIO) // GPIO + +// # define P139_GET_GPIO_FLAGS(i) (static_cast(get3BitFromUL(P139_CONFIG_FLAGS, (i) * 3))) +// # define P139_SET_GPIO_FLAGS(i, v) set3BitToUL(P139_CONFIG_FLAGS, (i) * 3, (v)) +// # define P139_GET_FLAG_GPIO0 P139_GET_GPIO_FLAGS(0) +// # define P139_GET_FLAG_GPIO1 P139_GET_GPIO_FLAGS(1) +// # define P139_GET_FLAG_GPIO2 P139_GET_GPIO_FLAGS(2) +// # define P139_GET_FLAG_GPIO3 P139_GET_GPIO_FLAGS(3) +// # define P139_GET_FLAG_GPIO4 P139_GET_GPIO_FLAGS(4) + +enum class P139_valueOptions_e : uint8_t { + None = 0x00, + BatteryVoltage = 0x01, + BatteryDischargeCurrent = 0x02, + BatteryChargeCurrent = 0x03, + BatteryPower = 0x04, + AcinVoltage = 0x05, + AcinCurrent = 0x06, + VbusVoltage = 0x07, + VbusCurrent = 0x08, + InternalTemperature = 0x09, + ApsVoltage = 0x0A, + LDO2 = 0x0B, + LDO3 = 0x0C, + LDOIO = 0x0D, + DCDC2 = 0x12, + DCDC3 = 0x13, +}; + +enum class P139_GPIOBootState_e: uint8_t { // Will be applied by subtracting 1 !! + Default = 0u, + Output_low = 1u, + Output_high = 2u, + Input = 3u, + PWM = 4u, +}; + +// int16_t P139_settingToValue(uint16_t data, +// uint16_t threshold); +// uint16_t P139_valueToSetting(int data, +// uint16_t threshold); +void P139_CheckPredefinedParameters(struct EventStruct *event); + +const __FlashStringHelper* toString(const P139_valueOptions_e value, + bool displayString = true); +const __FlashStringHelper* toString(const P139_GPIOBootState_e value); + + +struct P139_data_struct : public PluginTaskData_base { +public: + + P139_data_struct(struct EventStruct *event); + P139_data_struct() = delete; + ~P139_data_struct(); + + bool plugin_read(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + bool plugin_get_config_value(struct EventStruct *event, + String & string); + bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_fifty_per_second(struct EventStruct *event); + String loadSettings(struct EventStruct *event); + String saveSettings(struct EventStruct *event); + void outputSettings(struct EventStruct *event); + bool applySettings(AXP2101_device_model_e device); + + AXP2101_settings _settings; + +private: + + AXP2101 *axp2101 = nullptr; + + bool isInitialized() { + return nullptr != axp2101; + } + + float read_value(AXP2101_registers_e value); + + // *INDENT-OFF* + // Map range 0..100% + uint16_t _ranges[AXP2101_settings_count][2] = { + { AXP2101_DCDC1_MIN, AXP2101_DCDC1_MAX }, + { AXP2101_DCDC2_MIN, AXP2101_DCDC2_MAX }, + { AXP2101_DCDC3_MIN, AXP2101_DCDC3_MAX }, + { AXP2101_DCDC4_MIN, AXP2101_DCDC4_MAX }, + { AXP2101_DCDC5_MIN, AXP2101_DCDC5_MAX }, + { AXP2101_ALDO1_MIN, AXP2101_ALDO1_MAX }, + { AXP2101_ALDO2_MIN, AXP2101_ALDO2_MAX }, + { AXP2101_ALDO3_MIN, AXP2101_ALDO3_MAX }, + { AXP2101_ALDO4_MIN, AXP2101_ALDO4_MAX }, + { AXP2101_BLDO1_MIN, AXP2101_BLDO1_MAX }, + { AXP2101_BLDO2_MIN, AXP2101_BLDO2_MAX }, + { AXP2101_DLDO1_MIN, AXP2101_DLDO1_MAX }, + { AXP2101_DLDO2_MIN, AXP2101_DLDO2_MAX }, + { AXP2101_CPUSLDO_MIN, AXP2101_CPUSLDO_MAX }, + }; + + // *INDENT-ON* + + bool _settingsLoaded = false; +}; + +# endif // ifdef ESP32 +#endif // ifdef USES_P139 +#endif // ifndef PLUGINSTRUCTS_P139_DATA_STRUCT_H From 82d01646a101757800998931b123673575df3503 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 16 Feb 2024 23:48:39 +0100 Subject: [PATCH 02/50] [P139] Exclude AXP2101 from ESP8266 builds --- platformio_core_defs.ini | 1 + platformio_esp82xx_base.ini | 1 + 2 files changed, 2 insertions(+) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 142a15b1b7..4511c6e811 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -59,6 +59,7 @@ lib_ignore = ESP32_ping ArduinoOTA ESP8266mDNS I2C AXP192 Power management + AXP2101 Power management (I2C) ; EspSoftwareSerial diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 90a1b999e4..c64d7e2851 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -146,6 +146,7 @@ lib_ignore = ESP32_ping ESP8266mDNS I2C AXP192 Power management EspSoftwareSerial + AXP2101 Power management (I2C) From 9c205a73e1a56481610f52f8dd0f6ca099cdd5e4 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 17 Feb 2024 13:37:59 +0100 Subject: [PATCH 03/50] Cherry pick: [IPv6] Reduce events to keep webserver alive [Memory] Flush buffer after sending web page to reduce memory used (code by @TD-er) --- src/src/ESPEasyCore/ESPEasyEth_ProcessEvent.cpp | 2 +- src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp | 2 +- src/src/WebServer/ESPEasy_WebServer.cpp | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasyEth_ProcessEvent.cpp b/src/src/ESPEasyCore/ESPEasyEth_ProcessEvent.cpp index 574c2aa879..3b8fc938ea 100644 --- a/src/src/ESPEasyCore/ESPEasyEth_ProcessEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyEth_ProcessEvent.cpp @@ -255,7 +255,7 @@ void processEthernetGotIPv6() { addLog(LOG_LEVEL_INFO, String(F("ETH event: Got IP6 ")) + EthEventData.unprocessed_IP6.toString(true)); EthEventData.processedGotIP6 = true; #if FEATURE_ESPEASY_P2P - updateUDPport(true); + // updateUDPport(true); #endif } diff --git a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp index 4a038eb601..67d94bd097 100644 --- a/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp +++ b/src/src/ESPEasyCore/ESPEasyWifi_ProcessEvent.cpp @@ -466,7 +466,7 @@ void processGotIPv6() { if (loglevelActiveFor(LOG_LEVEL_INFO)) addLog(LOG_LEVEL_INFO, String(F("WIFI : STA got IP6 ")) + WiFiEventData.unprocessed_IP6.toString(true)); #if FEATURE_ESPEASY_P2P - updateUDPport(true); + // updateUDPport(true); #endif } } diff --git a/src/src/WebServer/ESPEasy_WebServer.cpp b/src/src/WebServer/ESPEasy_WebServer.cpp index 644c76849e..b138d4dc56 100644 --- a/src/src/WebServer/ESPEasy_WebServer.cpp +++ b/src/src/WebServer/ESPEasy_WebServer.cpp @@ -158,6 +158,11 @@ void sendHeadandTail_stdtemplate(bool Tail, bool rebooting) { */ #endif // ifndef BUILD_NO_DEBUG } + // We have sent a lot of data at once. + // try to flush it to the connected client to free up some RAM + // from pending transfers + TXBuffer.flush(); + delay(10); } bool captivePortal() { From f82c0e7a1681d6cf76caaa5354b0d71ad364a57d Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 17 Feb 2024 13:39:13 +0100 Subject: [PATCH 04/50] [P139] Add AXP2101 to I2C Scanner --- src/src/WebServer/I2C_Scanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index e9428ad504..f7b7e36653 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -221,7 +221,7 @@ String getKnownI2Cdevice(uint8_t address) { result += F("VL53L0X,VL53L1X"); break; case 0x34: - result += F("AXP192"); + result += F("AXP192,AXP2101"); break; case 0x36: result += F("MAX1704x,Adafruit Rotary enc"); From b1decb94cccc261af8792b586e03248a0f28a34a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 17 Feb 2024 23:03:23 +0100 Subject: [PATCH 05/50] [P139] Add chargeled and batcharge options, Code cleanup --- lib/AXP2101/src/AXP2101.cpp | 116 +++++++++-- lib/AXP2101/src/AXP2101.h | 160 ++++++++------ src/_P139_AXP2101.ino | 64 ++++-- src/src/PluginStructs/P139_data_struct.cpp | 232 ++++++--------------- src/src/PluginStructs/P139_data_struct.h | 67 ------ 5 files changed, 311 insertions(+), 328 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index e60af6ac81..71c01c5578 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -1,5 +1,11 @@ #include "AXP2101.h" +#ifdef ESP32 + +// To check if we have implemented all cases of the enums +# pragma GCC diagnostic push +# pragma GCC diagnostic warning "-Wswitch-enum" + /** * Utility functions */ @@ -35,6 +41,8 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, case AXP2101_registers_e::dldo1: return displayString ? F("DLDO1") : F("dldo1"); case AXP2101_registers_e::dldo2: return displayString ? F("DLDO2") : F("dldo2"); case AXP2101_registers_e::cpuldos: return displayString ? F("CPULDOS") : F("cpuldos"); + case AXP2101_registers_e::chargeled: return displayString ? F("ChargeLed") : F("chargeled"); + case AXP2101_registers_e::batcharge: return displayString ? F("BatCharge") : F("batcharge"); } return F(""); } @@ -50,6 +58,17 @@ const __FlashStringHelper* toString(AXP_pin_s pin) { return F(""); } +const __FlashStringHelper* toString(AXP2101_chargeled_d led) { + switch (led) { + case AXP2101_chargeled_d::Off: return F("Off"); + case AXP2101_chargeled_d::Flash_1Hz: return F("Flash 1Hz"); + case AXP2101_chargeled_d::Flash_4Hz: return F("Flash 4Hz"); + case AXP2101_chargeled_d::Steady_On: return F("Steady On"); + case AXP2101_chargeled_d::Protected: return F("Protected"); + } + return F(""); +} + AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; @@ -66,6 +85,8 @@ AXP2101_registers_e AXP2101_intToRegister(int reg) { case 11: return AXP2101_registers_e::dldo1; case 12: return AXP2101_registers_e::dldo2; case 13: return AXP2101_registers_e::cpuldos; + case 14: return AXP2101_registers_e::chargeled; + case 15: return AXP2101_registers_e::batcharge; } return AXP2101_registers_e::dcdc1; // we shouldn't get here } @@ -86,6 +107,10 @@ uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::dldo1: case AXP2101_registers_e::dldo2: case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MIN; + + // not a voltage register + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: return 0u; } return 0u; } @@ -106,6 +131,10 @@ uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::dldo1: return AXP2101_DLDO1_MAX; case AXP2101_registers_e::dldo2: case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MAX; + + // not a voltage register + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: return 0u; } return 0u; } @@ -137,19 +166,21 @@ AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _d uint16_t _bldo1, uint16_t _bldo2, uint16_t _dldo1, uint16_t _dldo2, uint16_t _cpuldos, AXP_pin_s _en_dcdc1, AXP_pin_s _en_dcdc2, AXP_pin_s _en_dcdc3, AXP_pin_s _en_dcdc4, AXP_pin_s _en_dcdc5, AXP_pin_s _en_aldo1, AXP_pin_s _en_aldo2, AXP_pin_s _en_aldo3, AXP_pin_s _en_aldo4, - AXP_pin_s _en_bldo1, AXP_pin_s _en_bldo2, AXP_pin_s _en_dldo1, AXP_pin_s _en_dldo2, AXP_pin_s _en_cpuldos) + AXP_pin_s _en_bldo1, AXP_pin_s _en_bldo2, AXP_pin_s _en_dldo1, AXP_pin_s _en_dldo2, AXP_pin_s _en_cpuldos, + AXP2101_chargeled_d _chargeled) { registers.dcdc1 = _dcdc1; registers.dcdc2 = _dcdc2; registers.dcdc3 = _dcdc3; registers.dcdc4 = _dcdc4; registers.dcdc5 = _dcdc5; registers.aldo1 = _aldo1; registers.aldo2 = _aldo2; registers.aldo3 = _aldo3; registers.aldo4 = _aldo4; registers.bldo1 = _bldo1; registers.bldo1 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; - pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); - pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); - pinStates.en_dcdc5 = static_cast(_en_dcdc5); pinStates.en_aldo1 = static_cast(_en_aldo1); - pinStates.en_aldo2 = static_cast(_en_aldo2); pinStates.en_aldo3 = static_cast(_en_aldo3); - pinStates.en_aldo4 = static_cast(_en_aldo4); pinStates.en_bldo1 = static_cast(_en_bldo1); - pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); - pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); + pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); + pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); + pinStates.en_dcdc5 = static_cast(_en_dcdc5); pinStates.en_aldo1 = static_cast(_en_aldo1); + pinStates.en_aldo2 = static_cast(_en_aldo2); pinStates.en_aldo3 = static_cast(_en_aldo3); + pinStates.en_aldo4 = static_cast(_en_aldo4); pinStates.en_bldo1 = static_cast(_en_bldo1); + pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); + pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); + pinStates.chargeled = static_cast(_chargeled); } void AXP2101_settings::setVoltage(AXP2101_registers_e reg, @@ -171,6 +202,8 @@ void AXP2101_settings::setVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::dldo1: registers.dldo1 = voltage; break; case AXP2101_registers_e::dldo2: registers.dldo2 = voltage; break; case AXP2101_registers_e::cpuldos: registers.cpuldos = voltage; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: break; } } @@ -193,6 +226,8 @@ int AXP2101_settings::getVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::dldo1: result = registers.dldo1; break; case AXP2101_registers_e::dldo2: result = registers.dldo2; break; case AXP2101_registers_e::cpuldos: result = registers.cpuldos; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: result = 0; break; } return 0xFFFFF == result ? (realValue ? 0 : -1) : result; } @@ -216,6 +251,8 @@ void AXP2101_settings::setState(AXP2101_registers_e reg, case AXP2101_registers_e::dldo1: pinStates.en_dldo1 = value; break; case AXP2101_registers_e::dldo2: pinStates.en_dldo2 = value; break; case AXP2101_registers_e::cpuldos: pinStates.en_cpuldos = value; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: break; } } @@ -235,24 +272,34 @@ AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { case AXP2101_registers_e::dldo1: return static_cast(pinStates.en_dldo1); case AXP2101_registers_e::dldo2: return static_cast(pinStates.en_dldo2); case AXP2101_registers_e::cpuldos: return static_cast(pinStates.en_cpuldos); + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: return AXP_pin_s::Protected; } return AXP_pin_s::Default; } +void AXP2101_settings::setChargeLed(AXP2101_chargeled_d led) { + pinStates.chargeled = static_cast(led); +} + +AXP2101_chargeled_d AXP2101_settings::getChargeLed() { + return static_cast(pinStates.chargeled); +} + /** * AXP2101 device class */ // *INDENT-OFF* AXP2101_settings AXP2101_deviceSettingsArray[] = -{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | aldo3 | aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos -/* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default }, -/* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, -/* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, -/* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default }, -/* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, -/* LilyGo TPCie v1.2 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled }, -/* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default }, +{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | aldo3 | aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos | chargeled +/* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, +/* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, +/* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TPCie v1.2 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, }; // *INDENT-ON* @@ -425,6 +472,9 @@ uint8_t voltageToRegister(uint16_t voltage, if (voltage > max) { voltage = max; } return (voltage - min) / 100; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + break; } return 0u; } @@ -475,6 +525,9 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, if (0 == off) { off = 500; } return off + (data * 100); + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + return 0u; } return 0u; } @@ -625,6 +678,25 @@ bool AXP2101::set_sys_led(bool sw) { return bitOnOff(sw, AXP2101_CHGLED_REG, 0b00110000); } +bool AXP2101::setChargeLed(AXP2101_chargeled_d led) { + if (AXP2101_chargeled_d::Protected != led) { + const uint8_t temp = readRegister8(_addr, AXP2101_CHGLED_REG); + const uint8_t data = (static_cast(led) & 0x03) << 4; + const uint8_t write_back = ((temp & 0b11001111) | data); + + return writeRegister8(_addr, AXP2101_CHGLED_REG, write_back); + } + return false; +} + +AXP2101_chargeled_d AXP2101::getChargeLed() { + return static_cast((readRegister8(_addr, AXP2101_CHGLED_REG) >> 4) & 0x03); +} + +uint8_t AXP2101::getBatCharge() { + return readRegister8(_addr, AXP2101_BAT_CHARGE_REG); +} + bool AXP2101::set_charger_term_current_to_zero(void) { return bitOff(AXP2101_ADDR, AXP2101_CHARGER_SETTING_REG, 0b00001111); } @@ -720,5 +792,17 @@ void AXP2101::getControlRegisterMask(AXP2101_registers_e reg, ctrl = AXP2101_LDO_CTRL_REG; mask = AXP2101_CPUSLDO_CTRL_MASK; break; + case AXP2101_registers_e::chargeled: + ctrl = AXP2101_CHGLED_REG; + mask = AXP2101_CHGLED_CTRL_MASK; + break; + case AXP2101_registers_e::batcharge: + ctrl = AXP2101_BAT_CHARGE_REG; + mask = 0xFF; + break; } } + +# pragma GCC diagnostic pop + +#endif // ifdef ESP32 diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index 8d9fd0421d..b5b8828156 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -2,10 +2,19 @@ #define __AXP2101_H /** - * AXP2102 library adjusted for ESPEasy + * AXP2101 library adjusted for ESPEasy * 2024-02-04 tonhuisman: Start. * - * Based on the AXP2102 driver included in https://github.com/m5stack/M5Core2 + * Based on the AXP2101 driver included in https://github.com/m5stack/M5Core2 + */ + +/** Changelog: + * 2024-02-17 tonhuisman: Add support for Charge-led and battery charge level, limit to ESP32, as this chip is only available + * on ESP32 based units + * 2024-02-16 tonhuisman: Initial 'release' with AXP2101 plugin for ESPEasy, implementing all output pins + * Including predefined settings for M5Stack Core2 v1.1, M5Stack CoreS3, LilyGO_TBeam_v1_2, + * LilyGO_TBeamS3_v3, LilyGO_TPCie_v1_2. + * 2024-02-04 tonhuisman: Start development of the library */ #include @@ -32,6 +41,7 @@ #define AXP2101_DLDO2_VOLTAGE_REG (0x9A) #define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) +#define AXP2101_BAT_CHARGE_REG (0x04) #define AXP2101_PMU_CONFIG_REG (0x10) #define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) #define AXP2101_PWROK_PWROFF_REG (0x25) @@ -55,6 +65,7 @@ #define AXP2101_DLDO1_CTRL_MASK (1 << 7) #define AXP2101_DLDO2_CTRL_MASK (1 << 0) #define AXP2101_CPUSLDO_CTRL_MASK (1 << 6) +#define AXP2101_CHGLED_CTRL_MASK (0x03 << 4) #define AXP2101_DCDC1_MIN (1500) #define AXP2101_DCDC2_MIN (500) @@ -87,7 +98,7 @@ #define AXP2101_CPUSLDO_MAX (1400) enum class AXP2101_device_model_e : uint8_t { - Unselected = 0u, // >>>>> Don't change these values, they are stored in user-settings <<<<< + Unselected = 0u, // >>>>> Don't change these values, they are probably stored in user-settings <<<<< M5Stack_Core2_v1_1 = 1u, // https://docs.m5stack.com/en/core/Core2%20v1.1 M5Stack_CoreS3 = 2u, // https://docs.m5stack.com/en/core/CoreS3 LilyGO_TBeam_v1_2 = 3u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_V1.2.pdf @@ -116,10 +127,12 @@ enum class AXP2101_registers_e : uint8_t { cpuldos = AXP2101_CPUSLDO_VOLTAGE_REG, // Above are settable pinstates/voltages of the AXP2101 - // TODO Below are read-only values of the AXP2101, also update AXP2101_register_count when adding values + // Below are non-voltage and read-only values of the AXP2101, also update AXP2101_register_count when adding values + chargeled = AXP2101_CHGLED_REG, + batcharge = AXP2101_BAT_CHARGE_REG, }; constexpr int AXP2101_settings_count = 14; // Changeable settings -constexpr int AXP2101_register_count = 14; // All registers +constexpr int AXP2101_register_count = 16; // All registers enum class AXP_pin_s : uint8_t { Off = 0x00, // Max. 3 bits can be stored in settings! @@ -129,6 +142,14 @@ enum class AXP_pin_s : uint8_t { Protected = 0x07 // Don't try to change port value, can make the unit fail! }; +enum class AXP2101_chargeled_d : uint8_t { + Off = 0x00, + Flash_1Hz = 0x01, + Flash_4Hz = 0x02, + Steady_On = 0x03, + Protected = 0x07 // Don't try to change or not connected +}; + AXP2101_registers_e AXP2101_intToRegister(int reg); uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); @@ -140,46 +161,50 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, const __FlashStringHelper* toString(AXP2101_device_model_e device, bool displayString = true); const __FlashStringHelper* toString(AXP_pin_s pin); +const __FlashStringHelper* toString(AXP2101_chargeled_d led); class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: AXP2101_settings(); - AXP2101_settings(uint16_t _dcdc1, - uint16_t _dcdc2, - uint16_t _dcdc3, - uint16_t _dcdc4, - uint16_t _dcdc5, - uint16_t _aldo1, - uint16_t _aldo2, - uint16_t _aldo3, - uint16_t _aldo4, - uint16_t _bldo1, - uint16_t _bldo2, - uint16_t _dldo1, - uint16_t _dldo2, - uint16_t _cpuldos, - AXP_pin_s _en_dcdc1, - AXP_pin_s _en_dcdc2, - AXP_pin_s _en_dcdc3, - AXP_pin_s _en_dcdc4, - AXP_pin_s _en_dcdc5, - AXP_pin_s _en_aldo1, - AXP_pin_s _en_aldo2, - AXP_pin_s _en_aldo3, - AXP_pin_s _en_aldo4, - AXP_pin_s _en_bldo1, - AXP_pin_s _en_bldo2, - AXP_pin_s _en_dldo1, - AXP_pin_s _en_dldo2, - AXP_pin_s _en_cpuldos); - void setVoltage(AXP2101_registers_e reg, - int voltage); - int getVoltage(AXP2101_registers_e reg, - bool realValue = true); - void setState(AXP2101_registers_e reg, - AXP_pin_s state); - AXP_pin_s getState(AXP2101_registers_e reg); + AXP2101_settings(uint16_t _dcdc1, + uint16_t _dcdc2, + uint16_t _dcdc3, + uint16_t _dcdc4, + uint16_t _dcdc5, + uint16_t _aldo1, + uint16_t _aldo2, + uint16_t _aldo3, + uint16_t _aldo4, + uint16_t _bldo1, + uint16_t _bldo2, + uint16_t _dldo1, + uint16_t _dldo2, + uint16_t _cpuldos, + AXP_pin_s _en_dcdc1, + AXP_pin_s _en_dcdc2, + AXP_pin_s _en_dcdc3, + AXP_pin_s _en_dcdc4, + AXP_pin_s _en_dcdc5, + AXP_pin_s _en_aldo1, + AXP_pin_s _en_aldo2, + AXP_pin_s _en_aldo3, + AXP_pin_s _en_aldo4, + AXP_pin_s _en_bldo1, + AXP_pin_s _en_bldo2, + AXP_pin_s _en_dldo1, + AXP_pin_s _en_dldo2, + AXP_pin_s _en_cpuldos, + AXP2101_chargeled_d _chargeled); + void setVoltage(AXP2101_registers_e reg, + int voltage); + int getVoltage(AXP2101_registers_e reg, + bool realValue = true); + void setState(AXP2101_registers_e reg, + AXP_pin_s state); + AXP_pin_s getState(AXP2101_registers_e reg); + void setChargeLed(AXP2101_chargeled_d led); + AXP2101_chargeled_d getChargeLed(); private: @@ -218,7 +243,8 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the uint64_t en_dldo1 : 3; // bit 33/34/35 uint64_t en_dldo2 : 3; // bit 36/37/38 uint64_t en_cpuldos : 3; // bit 39/40/41 - uint64_t en_unused : 21; // bit 42..63 // All bits defined + uint64_t chargeled : 3; // bit 42/43/44 + uint64_t en_unused : 18; // bit 45..63 // All bits defined } pinStates; uint64_t pinStates_{}; // 8 bytes }; @@ -274,32 +300,36 @@ class AXP2101 { public: // Utility - uint8_t voltageToRegister(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t registerToVoltage(uint8_t data, - AXP2101_registers_e reg); - uint8_t get_dcdc_status(void); - bool setPortVoltage(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t getPortVoltage(AXP2101_registers_e reg); - bool setPortState(bool sw, - AXP2101_registers_e reg); - bool getPortState(AXP2101_registers_e reg); + uint8_t voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t registerToVoltage(uint8_t data, + AXP2101_registers_e reg); + uint8_t get_dcdc_status(void); + bool setPortVoltage(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t getPortVoltage(AXP2101_registers_e reg); + bool setPortState(bool sw, + AXP2101_registers_e reg); + bool getPortState(AXP2101_registers_e reg); + + bool setChargeLed(AXP2101_chargeled_d led); + AXP2101_chargeled_d getChargeLed(); + uint8_t getBatCharge(); // Device common functions - void set_bus_3v3(uint16_t voltage); - void set_lcd_back_light_voltage(uint16_t voltage); - void set_bus_5v(uint8_t sw); - bool set_sys_led(bool sw); - void set_spk(bool sw); - void set_lcd_rst(bool sw); - void set_lcd_and_tf_voltage(uint16_t voltage); - void set_vib_motor_voltage(uint16_t voltage); - void set_bat_charge(bool enable); - void power_off(void); - bool set_charger_term_current_to_zero(void); - bool set_charger_constant_current_to_50mA(void); - bool enable_pwrok_resets(void); + void set_bus_3v3(uint16_t voltage); + void set_lcd_back_light_voltage(uint16_t voltage); + void set_bus_5v(uint8_t sw); + bool set_sys_led(bool sw); + void set_spk(bool sw); + void set_lcd_rst(bool sw); + void set_lcd_and_tf_voltage(uint16_t voltage); + void set_vib_motor_voltage(uint16_t voltage); + void set_bat_charge(bool enable); + void power_off(void); + bool set_charger_term_current_to_zero(void); + bool set_charger_constant_current_to_50mA(void); + bool enable_pwrok_resets(void); // Low-level output functions diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 966e11f2c2..ca84096368 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -9,6 +9,8 @@ /** * Changelog: + * 2024-02-17 tonhuisman: Add setting for Charge led and battery charge level, fix saving adjusted port settings, + * set to 0 decimals as we're using mV values * 2024-02-15 tonhuisman: First plugin version, in ReadOnly mode only, no data is written to the AXP2101, only the register to read * 2024-02-04 tonhuisman: Initial plugin development, only available for ESP32 **/ @@ -22,6 +24,7 @@ * axp,percentage,, : Set port to percentage of Low to High range (min/max or set range per port) * axp,range,,, : Define low/high range for port. Low and High must be withing technical range of port * axp,range : List current range configuration (or when providing an out of range low/high argument) + * axp,chargeled, : Set charge-led state, 0 : off, 1 : flash 1Hz, 2 : flash 4Hz, 3 : on * TODO: Add more commands? **/ /** @@ -40,6 +43,8 @@ * [#dldo1] : * [#dldo2] : * [#cpuldos] : + * [#chargeled] : + * [#batcharge] : (Doesn't support the .status and .state variants of the variable) * TODO: Define additional values? **/ /** @@ -100,6 +105,7 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } else { ExtraTaskSettings.clearTaskDeviceValueName(i); } + ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; // No values have decimals } break; } @@ -172,7 +178,24 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) break; } - // addFormNumericBox(F("Decimals for config values"), F("decimals"), P139_CONFIG_DECIMALS, 0, 4); + { + const __FlashStringHelper *chargeledNames[] = { + toString(AXP2101_chargeled_d::Off), + toString(AXP2101_chargeled_d::Flash_1Hz), + toString(AXP2101_chargeled_d::Flash_4Hz), + toString(AXP2101_chargeled_d::Steady_On), + }; + const int chargeledValues[] = { + static_cast(AXP2101_chargeled_d::Off), + static_cast(AXP2101_chargeled_d::Flash_1Hz), + static_cast(AXP2101_chargeled_d::Flash_4Hz), + static_cast(AXP2101_chargeled_d::Steady_On), + }; + addFormSelector(F("Charge LED"), F("led"), + NR_ELEMENTS(chargeledValues), + chargeledNames, chargeledValues, + static_cast(P139_data->_settings.getChargeLed())); + } addFormSubHeader(F("Hardware outputs AXP2101")); @@ -320,21 +343,36 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); } - // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(getFormItemInt(F("pdcdc2")), P139_CONST_MAX_DCDC2) << 16) | - // P139_valueToSetting(getFormItemInt(F("pldo2")), P139_CONST_MAX_LDO); - // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(getFormItemInt(F("pdcdc3")), P139_CONST_MAX_DCDC) << 16) | - // P139_valueToSetting(getFormItemInt(F("pldo3")), P139_CONST_MAX_LDO); - // P139_REG_LDOIO = P139_valueToSetting(getFormItemInt(F("ldoiovolt")), P139_CONST_MAX_LDOIO); + P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + + bool created_new = false; + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); - // for (int i = 0; i < 5; i++) { // GPIO0..4 - // P139_SET_GPIO_FLAGS(i, getFormItemInt(concat(F("pgpio"), i))); - // } + if (nullptr == P139_data) { + P139_data = new (std::nothrow) P139_data_struct(event); + created_new = true; + } - // P139_CONFIG_DECIMALS = getFormItemInt(F("decimals")); - P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + if (nullptr == P139_data) { + break; + } + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (!AXP2101_isPinProtected(P139_data->_settings.getState(reg))) { + P139_data->_settings.setVoltage(reg, getFormItemInt(toString(reg, false))); + P139_data->_settings.setState(reg, static_cast(getFormItemInt(concat(F("ps"), toString(reg, false))))); + } + } + + P139_data->_settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + + P139_data->saveSettings(event); - // P139_CONFIG_DISABLEBITS = getFormItemInt(F("pbits"), static_cast(P139_CONFIG_DISABLEBITS)); // Keep previous value if not - // found + if (created_new) { + delete P139_data; + } success = true; break; diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 021de4ceb1..1efa6e0990 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -4,140 +4,6 @@ # ifdef ESP32 -// int16_t P139_settingToValue(uint16_t data, -// uint16_t threshold) { -// if (data > threshold) { -// return threshold - data; -// } -// return data; -// } - -// uint16_t P139_valueToSetting(int data, -// uint16_t threshold) { -// if (data <= -1) { -// return threshold - data; -// } -// return static_cast(data); -// } - -/** - * Apply default parameters based on P139_CONFIG_PREDEFINED setting. - * This will be 0 by default, so nothing is changed. - * P139_CONFIG_DISABLEBITS: (set to 1 to disable that setting) - * Bit = Feature - * 0 = LDO2 - * 1 = LDO3 - * 2 = LDOIO - * 3 = GPIO0 - * 4 = GPIO1 - * 5 = GPIO2 - * 6 = GPIO3 - * 7 = GPIO4 - * 8 = DCDC2 - * 9 = DCDC3 - */ -void P139_CheckPredefinedParameters(struct EventStruct *event) { - if (P139_CONFIG_PREDEFINED > 0) { - P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; - - // // Set defaults - // for (int i = 0; i < 5; i++) { // GPI0..4 - // P139_SET_GPIO_FLAGS(i, static_cast(P139_GPIOBootState_e::Default)); - // } - - switch (static_cast(P139_CONFIG_PREDEFINED)) { - case AXP2101_device_model_e::M5Stack_Core2_v1_1: // M5Stack Core2 V1.1 and newer - { - // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3000, - // P139_CONST_MAX_LDO); - // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(3000, - // P139_CONST_MAX_LDO); - // P139_REG_LDOIO = P139_valueToSetting(2800, P139_CONST_MAX_LDOIO); - // P139_CONFIG_DISABLEBITS = 0b1111110000; // NC pins disabled - break; - } - case AXP2101_device_model_e::M5Stack_CoreS3: // M5Stack CoreS3 - { - // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, - // P139_CONST_MAX_LDO); - // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(0, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(0, P139_CONST_MAX_LDO); - // P139_REG_LDOIO = P139_valueToSetting(3300, P139_CONST_MAX_LDOIO); - // P139_CONFIG_DISABLEBITS = 0b0101000000; // NC pins disabled - // // Specials: GPIO1 High = LED off, GPIO4 High = Enable TFT - // P139_SET_GPIO_FLAGS(1, static_cast(P139_GPIOBootState_e::Output_high)); - // P139_SET_GPIO_FLAGS(4, static_cast(P139_GPIOBootState_e::Output_high)); - break; - } - case AXP2101_device_model_e::LilyGO_TBeam_v1_2: // LilyGO T-Beam V1.2 and newer - case AXP2101_device_model_e::LilyGO_TBeamS3_v3: // LilyGO T-BeamS3 V3 and newer - case AXP2101_device_model_e::LilyGO_TPCie_v1_2: // LilyGO T-PCie V1.2 and newer - { - // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, - // P139_CONST_MAX_LDO); - // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(3300, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(3300, - // P139_CONST_MAX_LDO); - // P139_REG_LDOIO = P139_valueToSetting(3300, P139_CONST_MAX_LDOIO); - // P139_CONFIG_DISABLEBITS = 0b1111111000; // NC pins disabled - break; - } - case AXP2101_device_model_e::UserDefined: // User defined - { - // P139_REG_DCDC2_LDO2 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC2) << 16) | P139_valueToSetting(3300, - // P139_CONST_MAX_LDO); - // P139_REG_DCDC3_LDO3 = (P139_valueToSetting(-1, P139_CONST_MAX_DCDC) << 16) | P139_valueToSetting(-1, - // P139_CONST_MAX_LDO); - // P139_REG_LDOIO = P139_valueToSetting(-1, P139_CONST_MAX_LDOIO); - // P139_CONFIG_DISABLEBITS = 0b0000000000; // All pins enabled - break; - } - case AXP2101_device_model_e::Unselected: - case AXP2101_device_model_e::MAX: - // Fall through - break; - } - } -} - -// **************************************************************************/ -// toString: convert P139_valueOptions_e enum to value-name or display-name -// **************************************************************************/ -const __FlashStringHelper* toString(const P139_valueOptions_e value, - bool displayString) { - switch (value) { - case P139_valueOptions_e::None: return displayString ? F("None") : F("none"); - case P139_valueOptions_e::BatteryVoltage: return displayString ? F("Battery voltage") : F("batteryvoltage"); - case P139_valueOptions_e::BatteryDischargeCurrent: return displayString ? F("Battery discharge current") : F("batterydischargecurrent"); - case P139_valueOptions_e::BatteryChargeCurrent: return displayString ? F("Battery charge current") : F("batterychargecurrent"); - case P139_valueOptions_e::BatteryPower: return displayString ? F("Battery power") : F("batterypower"); - case P139_valueOptions_e::AcinVoltage: return displayString ? F("Input voltage") : F("inputvoltage"); - case P139_valueOptions_e::AcinCurrent: return displayString ? F("Input current") : F("inputcurrent"); - case P139_valueOptions_e::VbusVoltage: return displayString ? F("VBus voltage") : F("vbusvoltage"); - case P139_valueOptions_e::VbusCurrent: return displayString ? F("VBus current") : F("vbuscurrent"); - case P139_valueOptions_e::InternalTemperature: return displayString ? F("Internal temperature") : F("internaltemperature"); - case P139_valueOptions_e::ApsVoltage: return displayString ? F("APS Voltage") : F("apsvoltage"); - case P139_valueOptions_e::LDO2: return displayString ? F("LDO2 Voltage") : F("ldo2voltage"); - case P139_valueOptions_e::LDO3: return displayString ? F("LDO3 Voltage") : F("ldo3voltage"); - case P139_valueOptions_e::LDOIO: return displayString ? F("LDOIO Voltage") : F("gpiovoltage"); - case P139_valueOptions_e::DCDC2: return displayString ? F("DCDC2 Voltage") : F("dcdc2voltage"); - case P139_valueOptions_e::DCDC3: return displayString ? F("DCDC3 Voltage") : F("dcdc3voltage"); - } - return F("*Undefined*"); -} - -// **************************************************************************/ -// toString: convert P139_GPIOBootState_e enum to string -// **************************************************************************/ -const __FlashStringHelper* toString(const P139_GPIOBootState_e value) { - switch (value) { - case P139_GPIOBootState_e::Default: return F("Default"); - case P139_GPIOBootState_e::Output_low: return F("Output, low"); - case P139_GPIOBootState_e::Output_high: return F("Output, high"); - case P139_GPIOBootState_e::Input: return F("Input"); - case P139_GPIOBootState_e::PWM: return F("PWM"); - } - return F("*Undefined*"); -} - // **************************************************************************/ // Constructor // **************************************************************************/ @@ -145,28 +11,6 @@ P139_data_struct::P139_data_struct(struct EventStruct *event) { axp2101 = new (std::nothrow) AXP2101(); // Default address and I2C Wire object if (isInitialized()) { // Functions based on: - // I2C_AXP192_InitDef initDef = { // M5Stack StickC / M5Stack Core2 / LilyGO T-Beam - // .EXTEN = true, // Enable ESP Power - // .BACKUP = true, // Enable RTC power - // .DCDC1 = 3300, // ESP Power / ESP Power (Fixed) / OLed - // .DCDC2 = P139_GET_CONFIG_DCDC2, // Unused / Unused / Unused - // .DCDC3 = P139_GET_CONFIG_DCDC3, // Unused / LCD Backlight / ESP Power - // .LDO2 = P139_GET_CONFIG_LDO2, // Backlight power (3000 mV) / Periferal VDD / LoRa - // .LDO3 = P139_GET_CONFIG_LDO3, // TFT Power (3000 mV) / Vibration motor / GPS - // .LDOIO = P139_GET_CONFIG_LDOIO, // LDOIO voltage (2800 mV) - // .GPIO0 = static_cast(P139_GET_FLAG_GPIO0 - 1), // Microphone power / Bus pwr enable / Unused - // .GPIO1 = static_cast(P139_GET_FLAG_GPIO1 - 1), // Unused / Sys Led (green) / Unused - // .GPIO2 = static_cast(P139_GET_FLAG_GPIO2 - 1), // Unused / Speaker enable / Unused - // .GPIO3 = static_cast(P139_GET_FLAG_GPIO3 - 1), // Unused / Unused / Unused - // .GPIO4 = static_cast(P139_GET_FLAG_GPIO4 - 1), // Unused / TFT Reset / Unused - // }; - // ldo2_value = P139_GET_CONFIG_LDO2; - // ldo3_value = P139_GET_CONFIG_LDO3; - // ldoio_value = P139_GET_CONFIG_LDOIO; - // dcdc2_value = P139_GET_CONFIG_DCDC2; - // dcdc3_value = P139_GET_CONFIG_DCDC3; - - // axp2101->begin(initDef); axp2101->begin(&Wire, AXP2101_ADDR, static_cast(P139_CONFIG_PREDEFINED)); loadSettings(event); outputSettings(event); @@ -267,6 +111,12 @@ bool P139_data_struct::plugin_read(struct EventStruct *event) { // **************************************************************************/ float P139_data_struct::read_value(AXP2101_registers_e value) { if (isInitialized()) { + if (AXP2101_registers_e::chargeled == value) { + return static_cast(axp2101->getChargeLed()); + } else + if (AXP2101_registers_e::batcharge == value) { + return static_cast(axp2101->getBatCharge()); + } return static_cast(axp2101->getPortVoltage(value)); } return 0.0f; @@ -291,7 +141,7 @@ bool P139_data_struct::plugin_fifty_per_second(struct EventStruct *event) { // **************************************************************************/ // plugin_write: Process commands // **************************************************************************/ -const char P139_subcommands[] PROGMEM = "readchip|voltage|off|on|percentage|range"; +const char P139_subcommands[] PROGMEM = "readchip|voltage|off|on|percentage|range|chargeled"; enum class P139_subcommands_e : int8_t { invalid = -1, @@ -301,6 +151,7 @@ enum class P139_subcommands_e : int8_t { on, percentage, range, + chargeled, }; bool P139_data_struct::plugin_write(struct EventStruct *event, @@ -327,12 +178,24 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, F("AXP2101: Port voltages (mV), state and range:")); - for (int s = 0; s < AXP2101_settings_count; ++s) { + for (int s = 0; s < AXP2101_register_count; ++s) { const AXP2101_registers_e reg = AXP2101_intToRegister(s); + uint16_t value; + uint8_t state = 0u; + + if (AXP2101_registers_e::chargeled == reg) { + value = static_cast(axp2101->getChargeLed()); + } else + if (AXP2101_registers_e::batcharge == reg) { + value = axp2101->getBatCharge(); + } else { + value = axp2101->getPortVoltage(reg); + state = axp2101->getPortState(reg); + } addLog(LOG_LEVEL_INFO, strformat(F("Port: %7s: %4dmV, state: %d, range: %d - %dmV"), toString(reg), - axp2101->getPortVoltage(reg), - axp2101->getPortState(reg), + value, + state, AXP2101_minVoltage(reg), AXP2101_maxVoltage(reg))); } @@ -435,11 +298,12 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, (event->Par4 <= AXP2101_maxVoltage(reg))) { _ranges[s][0] = event->Par3; _ranges[s][1] = event->Par4; + success = true; } } } else { if (loglevelActiveFor(LOG_LEVEL_INFO)) { - for (int s = 0; s < AXP2101_settings_count && !success; ++s) { + for (int s = 0; s < AXP2101_settings_count; ++s) { const AXP2101_registers_e reg = AXP2101_intToRegister(s); addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: %7s Percentage range: %d - %dmV (State: %s)"), toString(reg), @@ -451,6 +315,17 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, } success = true; break; + + case P139_subcommands_e::chargeled: + + if (event->Par2 >= 0 and event->Par2 <= 3) { // Only allowed options + AXP2101_chargeled_d led = static_cast(event->Par2); + + // axp2101->setChargeLed(led); + _settings.setChargeLed(led); // Store in settings, but don't save yet + success = true; + } + break; } // switch } @@ -468,17 +343,40 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, for (int r = 0; r < AXP2101_register_count && !success; ++r) { const AXP2101_registers_e reg = AXP2101_intToRegister(r); - if (equals(command, toString(reg, false))) { // Voltage (mV) - string = axp2101->getPortVoltage(reg); + if (equals(command, toString(reg, false))) { // Voltage (mV) / numeric state + if (r >= AXP2101_settings_count) { + if (AXP2101_registers_e::chargeled == reg) { + string = static_cast(axp2101->getChargeLed()); + } else + if (AXP2101_registers_e::batcharge == reg) { + string = axp2101->getBatCharge(); + } + } else { + string = axp2101->getPortVoltage(reg); + } success = true; } else if (command.equals(concat(toString(reg, false), F(".status")))) { // Status (name) - string = toString(axp2101->getPortState(reg)); - success = true; + if (r >= AXP2101_settings_count) { + if (AXP2101_registers_e::chargeled == reg) { + string = toString(axp2101->getChargeLed()); + success = true; + } + } else { + string = toString(axp2101->getPortState(reg)); + success = true; + } } else if (command.equals(concat(toString(reg, false), F(".state")))) { // State (int) - string = static_cast(axp2101->getPortState(reg)); - success = true; + if (r >= AXP2101_settings_count) { + if (AXP2101_registers_e::chargeled == reg) { + string = static_cast(axp2101->getChargeLed()); + success = true; + } + } else { + string = static_cast(axp2101->getPortState(reg)); + success = true; + } } } diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index c6ba39d56b..f0331a280f 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -17,77 +17,10 @@ # define P139_CONFIG_PREDEFINED PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 2) # define P139_CURRENT_PREDEFINED PCONFIG_FLOAT(0) -// # define P139_CONFIG_DISABLEBITS PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 3) - # define P139_CONST_1_PERCENT 1 // Lowest used percentage, 0 = off # define P139_CONST_100_PERCENT 100 // Max percentage # define P139_CONST_MIN_LDO 500 // Min. output voltage # define P139_CONST_MAX_LDO 3700 // Max. output voltage -// # define P139_CONST_MIN_LDO 1800 // Min. accepted LDO output voltage -// # define P139_CONST_MIN_LDOIO 1800 // Min. accepted GPIO output voltage -// # define P139_CONST_MAX_LDOIO 3300 // Max. GPIO output voltage -// # define P139_CONST_MIN_DCDC 700 // Min. accepted DCDC output voltage -// # define P139_CONST_MAX_DCDC 3500 // Max. DCDC output voltage -// # define P139_CONST_MAX_DCDC2 2750 // Max. DCDC2 output voltage - -// to break comment indenting -# define P139_dummy - -// # define P139_REG_DCDC2_LDO2 PCONFIG_ULONG(0) // DCDC2 & LDO2 -// # define P139_REG_DCDC3_LDO3 PCONFIG_ULONG(1) // DCDC3 & LDO3 -// # define P139_REG_LDOIO PCONFIG_ULONG(2) // GPIO -// # define P139_CONFIG_FLAGS PCONFIG_ULONG(3) // Other flags - -// # define P139_GET_CONFIG_LDO2 P139_settingToValue(P139_REG_DCDC2_LDO2 & 0xFFFF, P139_CONST_MAX_LDO) // LDO2 -// # define P139_GET_CONFIG_LDO3 P139_settingToValue(P139_REG_DCDC3_LDO3 & 0xFFFF, P139_CONST_MAX_LDO) // LDO3 -// # define P139_GET_CONFIG_DCDC2 P139_settingToValue((P139_REG_DCDC2_LDO2 >> 16) & 0xFFFF, P139_CONST_MAX_DCDC2) // DCDC2 -// # define P139_GET_CONFIG_DCDC3 P139_settingToValue((P139_REG_DCDC3_LDO3 >> 16) & 0xFFFF, P139_CONST_MAX_DCDC) // DCDC3 -// # define P139_GET_CONFIG_LDOIO P139_settingToValue(P139_REG_LDOIO & 0xFFFF, P139_CONST_MAX_LDOIO) // GPIO - -// # define P139_GET_GPIO_FLAGS(i) (static_cast(get3BitFromUL(P139_CONFIG_FLAGS, (i) * 3))) -// # define P139_SET_GPIO_FLAGS(i, v) set3BitToUL(P139_CONFIG_FLAGS, (i) * 3, (v)) -// # define P139_GET_FLAG_GPIO0 P139_GET_GPIO_FLAGS(0) -// # define P139_GET_FLAG_GPIO1 P139_GET_GPIO_FLAGS(1) -// # define P139_GET_FLAG_GPIO2 P139_GET_GPIO_FLAGS(2) -// # define P139_GET_FLAG_GPIO3 P139_GET_GPIO_FLAGS(3) -// # define P139_GET_FLAG_GPIO4 P139_GET_GPIO_FLAGS(4) - -enum class P139_valueOptions_e : uint8_t { - None = 0x00, - BatteryVoltage = 0x01, - BatteryDischargeCurrent = 0x02, - BatteryChargeCurrent = 0x03, - BatteryPower = 0x04, - AcinVoltage = 0x05, - AcinCurrent = 0x06, - VbusVoltage = 0x07, - VbusCurrent = 0x08, - InternalTemperature = 0x09, - ApsVoltage = 0x0A, - LDO2 = 0x0B, - LDO3 = 0x0C, - LDOIO = 0x0D, - DCDC2 = 0x12, - DCDC3 = 0x13, -}; - -enum class P139_GPIOBootState_e: uint8_t { // Will be applied by subtracting 1 !! - Default = 0u, - Output_low = 1u, - Output_high = 2u, - Input = 3u, - PWM = 4u, -}; - -// int16_t P139_settingToValue(uint16_t data, -// uint16_t threshold); -// uint16_t P139_valueToSetting(int data, -// uint16_t threshold); -void P139_CheckPredefinedParameters(struct EventStruct *event); - -const __FlashStringHelper* toString(const P139_valueOptions_e value, - bool displayString = true); -const __FlashStringHelper* toString(const P139_GPIOBootState_e value); struct P139_data_struct : public PluginTaskData_base { From 66382226d3388135726f8361183b3967735574ff Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 19 Feb 2024 22:29:27 +0100 Subject: [PATCH 06/50] [P139] Add generate events option, chargestate and battery detect, fix issues --- lib/AXP2101/src/AXP2101.cpp | 75 +++++++++++++++--- lib/AXP2101/src/AXP2101.h | 80 +++++++++++-------- src/_P139_AXP2101.ino | 54 ++++++------- src/src/PluginStructs/P139_data_struct.cpp | 90 +++++++++++++++++----- src/src/PluginStructs/P139_data_struct.h | 29 +++++-- 5 files changed, 231 insertions(+), 97 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 71c01c5578..73b04884f9 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -43,6 +43,8 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, case AXP2101_registers_e::cpuldos: return displayString ? F("CPULDOS") : F("cpuldos"); case AXP2101_registers_e::chargeled: return displayString ? F("ChargeLed") : F("chargeled"); case AXP2101_registers_e::batcharge: return displayString ? F("BatCharge") : F("batcharge"); + case AXP2101_registers_e::charging: return displayString ? F("ChargingState") : F("chargingstate"); + case AXP2101_registers_e::batpresent: return displayString ? F("BatPresent") : F("batpresent"); } return F(""); } @@ -69,6 +71,15 @@ const __FlashStringHelper* toString(AXP2101_chargeled_d led) { return F(""); } +const __FlashStringHelper* toString(AXP2101_chargingState_e state) { + switch (state) { + case AXP2101_chargingState_e::Discharging: return F("Discharging"); + case AXP2101_chargingState_e::Standby: return F("Standby"); + case AXP2101_chargingState_e::Charging: return F("Charging"); + } + return F(""); +} + AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; @@ -87,8 +98,10 @@ AXP2101_registers_e AXP2101_intToRegister(int reg) { case 13: return AXP2101_registers_e::cpuldos; case 14: return AXP2101_registers_e::chargeled; case 15: return AXP2101_registers_e::batcharge; + case 16: return AXP2101_registers_e::charging; + case 17: return AXP2101_registers_e::batpresent; } - return AXP2101_registers_e::dcdc1; // we shouldn't get here + return AXP2101_registers_e::dcdc1; // we shouldn't get here, just defaulting to the first value } uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { @@ -110,7 +123,10 @@ uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { // not a voltage register case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: return 0u; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + break; } return 0u; } @@ -134,7 +150,10 @@ uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { // not a voltage register case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: return 0u; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + break; } return 0u; } @@ -203,7 +222,10 @@ void AXP2101_settings::setVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::dldo2: registers.dldo2 = voltage; break; case AXP2101_registers_e::cpuldos: registers.cpuldos = voltage; break; case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: break; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + break; } } @@ -227,7 +249,10 @@ int AXP2101_settings::getVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::dldo2: result = registers.dldo2; break; case AXP2101_registers_e::cpuldos: result = registers.cpuldos; break; case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: result = 0; break; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + return 0; } return 0xFFFFF == result ? (realValue ? 0 : -1) : result; } @@ -252,7 +277,10 @@ void AXP2101_settings::setState(AXP2101_registers_e reg, case AXP2101_registers_e::dldo2: pinStates.en_dldo2 = value; break; case AXP2101_registers_e::cpuldos: pinStates.en_cpuldos = value; break; case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: break; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + break; } } @@ -273,7 +301,10 @@ AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { case AXP2101_registers_e::dldo2: return static_cast(pinStates.en_dldo2); case AXP2101_registers_e::cpuldos: return static_cast(pinStates.en_cpuldos); case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: return AXP_pin_s::Protected; + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + return AXP_pin_s::Protected; } return AXP_pin_s::Default; } @@ -474,6 +505,8 @@ uint8_t voltageToRegister(uint16_t voltage, return (voltage - min) / 100; case AXP2101_registers_e::chargeled: case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: break; } return 0u; @@ -527,7 +560,9 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, return off + (data * 100); case AXP2101_registers_e::chargeled: case AXP2101_registers_e::batcharge: - return 0u; + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + break; } return 0u; } @@ -537,10 +572,10 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, */ bool AXP2101::setPortVoltage(uint16_t voltage, AXP2101_registers_e reg) { - const uint8_t data = voltageToRegister(voltage, reg); + const uint8_t data = voltageToRegister(voltage, reg); const uint8_t creg = static_cast(reg); - return writeRegister8(AXP2101_ADDR, creg, data); + return writeRegister8(AXP2101_ADDR, creg, data); } /** @@ -690,13 +725,23 @@ bool AXP2101::setChargeLed(AXP2101_chargeled_d led) { } AXP2101_chargeled_d AXP2101::getChargeLed() { - return static_cast((readRegister8(_addr, AXP2101_CHGLED_REG) >> 4) & 0x03); + return static_cast((readRegister8(_addr, AXP2101_CHGLED_REG) >> 4) & 0x07); } uint8_t AXP2101::getBatCharge() { return readRegister8(_addr, AXP2101_BAT_CHARGE_REG); } +AXP2101_chargingState_e AXP2101::getChargingState() { + const uint8_t level = (readRegister8(_addr, AXP2101_COM_STAT1_REG) >> 5) & 0x03; + + return static_cast(0x01 == level ? 1 : (0x02 == level ? -1 : 0)); +} + +bool AXP2101::isBatteryDetected() { + return (readRegister8(_addr, AXP2101_COM_STAT0_REG) >> 3) & 0x01; +} + bool AXP2101::set_charger_term_current_to_zero(void) { return bitOff(AXP2101_ADDR, AXP2101_CHARGER_SETTING_REG, 0b00001111); } @@ -800,6 +845,14 @@ void AXP2101::getControlRegisterMask(AXP2101_registers_e reg, ctrl = AXP2101_BAT_CHARGE_REG; mask = 0xFF; break; + case AXP2101_registers_e::charging: + ctrl = AXP2101_COM_STAT1_REG; + mask = 0b01100000; + break; + case AXP2101_registers_e::batpresent: + ctrl = AXP2101_COM_STAT0_REG; + mask = 0b00001000; + break; } } diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index b5b8828156..5d79d574d6 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -9,6 +9,7 @@ */ /** Changelog: + * 2024-02-18 tonhuisman: Add support for ChargingState, isBatteryDetected * 2024-02-17 tonhuisman: Add support for Charge-led and battery charge level, limit to ESP32, as this chip is only available * on ESP32 based units * 2024-02-16 tonhuisman: Initial 'release' with AXP2101 plugin for ESPEasy, implementing all output pins @@ -41,7 +42,8 @@ #define AXP2101_DLDO2_VOLTAGE_REG (0x9A) #define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) -#define AXP2101_BAT_CHARGE_REG (0x04) +#define AXP2101_COM_STAT0_REG (0x00) +#define AXP2101_COM_STAT1_REG (0x01) #define AXP2101_PMU_CONFIG_REG (0x10) #define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) #define AXP2101_PWROK_PWROFF_REG (0x25) @@ -50,6 +52,7 @@ #define AXP2101_ICC_CHARGER_SETTING_REG (0x62) #define AXP2101_CHARGER_SETTING_REG (0x63) #define AXP2101_CHGLED_REG (0x69) +#define AXP2101_BAT_CHARGE_REG (0xA4) /* pdf has a duplicate listed for register 0x04, should be 0xA4 */ #define AXP2101_DCDC1_CTRL_MASK (1 << 0) #define AXP2101_DCDC2_CTRL_MASK (1 << 1) @@ -109,7 +112,7 @@ enum class AXP2101_device_model_e : uint8_t { UserDefined = 99u, // Keep UserDefined as last!!! }; -// The voltage registers mapped into an enum +// The voltage registers mapped into an enum, don't change order without also changing AXP2101_intToRegister() enum class AXP2101_registers_e : uint8_t { dcdc1 = AXP2101_DCDC1_VOLTAGE_REG, dcdc2 = AXP2101_DCDC2_VOLTAGE_REG, @@ -128,11 +131,13 @@ enum class AXP2101_registers_e : uint8_t { // Above are settable pinstates/voltages of the AXP2101 // Below are non-voltage and read-only values of the AXP2101, also update AXP2101_register_count when adding values - chargeled = AXP2101_CHGLED_REG, - batcharge = AXP2101_BAT_CHARGE_REG, + chargeled = AXP2101_CHGLED_REG, + batcharge = AXP2101_BAT_CHARGE_REG, + charging = AXP2101_COM_STAT1_REG, + batpresent = AXP2101_COM_STAT0_REG, }; constexpr int AXP2101_settings_count = 14; // Changeable settings -constexpr int AXP2101_register_count = 16; // All registers +constexpr int AXP2101_register_count = 18; // All registers enum class AXP_pin_s : uint8_t { Off = 0x00, // Max. 3 bits can be stored in settings! @@ -150,6 +155,12 @@ enum class AXP2101_chargeled_d : uint8_t { Protected = 0x07 // Don't try to change or not connected }; +enum class AXP2101_chargingState_e : int8_t { + Discharging = -1, + Standby = 0, + Charging = 1, +}; + AXP2101_registers_e AXP2101_intToRegister(int reg); uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); @@ -162,6 +173,7 @@ const __FlashStringHelper* toString(AXP2101_device_model_e device, bool displayString = true); const __FlashStringHelper* toString(AXP_pin_s pin); const __FlashStringHelper* toString(AXP2101_chargeled_d led); +const __FlashStringHelper* toString(AXP2101_chargingState_e state); class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: @@ -300,36 +312,38 @@ class AXP2101 { public: // Utility - uint8_t voltageToRegister(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t registerToVoltage(uint8_t data, - AXP2101_registers_e reg); - uint8_t get_dcdc_status(void); - bool setPortVoltage(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t getPortVoltage(AXP2101_registers_e reg); - bool setPortState(bool sw, - AXP2101_registers_e reg); - bool getPortState(AXP2101_registers_e reg); - - bool setChargeLed(AXP2101_chargeled_d led); - AXP2101_chargeled_d getChargeLed(); - uint8_t getBatCharge(); + uint8_t voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t registerToVoltage(uint8_t data, + AXP2101_registers_e reg); + uint8_t get_dcdc_status(void); + bool setPortVoltage(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t getPortVoltage(AXP2101_registers_e reg); + bool setPortState(bool sw, + AXP2101_registers_e reg); + bool getPortState(AXP2101_registers_e reg); + + bool setChargeLed(AXP2101_chargeled_d led); + AXP2101_chargeled_d getChargeLed(); + uint8_t getBatCharge(); + AXP2101_chargingState_e getChargingState(); + bool isBatteryDetected(); // Device common functions - void set_bus_3v3(uint16_t voltage); - void set_lcd_back_light_voltage(uint16_t voltage); - void set_bus_5v(uint8_t sw); - bool set_sys_led(bool sw); - void set_spk(bool sw); - void set_lcd_rst(bool sw); - void set_lcd_and_tf_voltage(uint16_t voltage); - void set_vib_motor_voltage(uint16_t voltage); - void set_bat_charge(bool enable); - void power_off(void); - bool set_charger_term_current_to_zero(void); - bool set_charger_constant_current_to_50mA(void); - bool enable_pwrok_resets(void); + void set_bus_3v3(uint16_t voltage); + void set_lcd_back_light_voltage(uint16_t voltage); + void set_bus_5v(uint8_t sw); + bool set_sys_led(bool sw); + void set_spk(bool sw); + void set_lcd_rst(bool sw); + void set_lcd_and_tf_voltage(uint16_t voltage); + void set_vib_motor_voltage(uint16_t voltage); + void set_bat_charge(bool enable); + void power_off(void); + bool set_charger_term_current_to_zero(void); + bool set_charger_constant_current_to_50mA(void); + bool enable_pwrok_resets(void); // Low-level output functions diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index ca84096368..168dcc3209 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -9,6 +9,7 @@ /** * Changelog: + * 2024-02-18 tonhuisman: Add setting for Generate events, support for chargestate and isBatteryDetected, fix some issues * 2024-02-17 tonhuisman: Add setting for Charge led and battery charge level, fix saving adjusted port settings, * set to 0 decimals as we're using mV values * 2024-02-15 tonhuisman: First plugin version, in ReadOnly mode only, no data is written to the AXP2101, only the register to read @@ -45,10 +46,13 @@ * [#cpuldos] : * [#chargeled] : * [#batcharge] : (Doesn't support the .status and .state variants of the variable) + * [#chargingstate] : Charging state, -1 = discharging, 0 = standby, 1 = charging + * [#batpresent] : (Doesn't support the .status and .state variants of the variable) * TODO: Define additional values? **/ /** * Events: + * #ChargingState=, : On change of the charging-state, new/old values: -1 = discharging, 0 = standby, 1 = charging * TODO: Define events? */ @@ -147,18 +151,18 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) PCONFIG(2) = static_cast(AXP2101_registers_e::aldo1); PCONFIG(3) = static_cast(AXP2101_registers_e::dldo1); PCONFIG(P139_SENSOR_TYPE_INDEX) = static_cast(Sensor_VType::SENSOR_TYPE_QUAD); - P139_CONFIG_DECIMALS = 2; // 2 decimals for all get config values + P139_CONFIG_DECIMALS = 2; // 2 decimals for all get config values AXP2101_device_model_e device = AXP2101_device_model_e::M5Stack_Core2_v1_1; - P139_CONFIG_PREDEFINED = static_cast(device); // M5Stack Core2 v1.1 + P139_CURRENT_PREDEFINED = static_cast(device); // M5Stack Core2 v1.1 + P139_SET_GENERATE_EVENTS(true); - P139_data_struct *P139_data = new (std::nothrow) P139_data_struct(event); + P139_data_struct *P139_data = new (std::nothrow) P139_data_struct(); if (nullptr != P139_data) { P139_data->applySettings(device); P139_data->saveSettings(event); delete P139_data; } - P139_CONFIG_PREDEFINED = 0; // Settings applied break; } @@ -170,7 +174,8 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr == P139_data) { - P139_data = new (std::nothrow) P139_data_struct(event); + P139_data = new (std::nothrow) P139_data_struct(); + P139_data->loadSettings(event); created_new = true; } @@ -197,12 +202,15 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) static_cast(P139_data->_settings.getChargeLed())); } + addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); + addFormSubHeader(F("Hardware outputs AXP2101")); { if (P139_CONFIG_PREDEFINED > 0) { P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; - P139_data->applySettings(static_cast(P139_CONFIG_PREDEFINED)); + P139_CONFIG_PREDEFINED = 0; + P139_data->applySettings(static_cast(P139_CURRENT_PREDEFINED)); } const __FlashStringHelper *predefinedNames[] = { toString(AXP2101_device_model_e::Unselected), @@ -235,8 +243,8 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) addFormNote(concat(F("Last selected: "), toString(device))); if (AXP2101_device_model_e::UserDefined == device) { - addHtml(F( - "
Warning: Configuring invalid values can damage your device or render it useless!
")); + addHtml(F("
Warning: " + "Configuring invalid values can damage your device or render it useless!
")); } } } @@ -254,7 +262,7 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) }; // Don't include Disabled or Protected here, not user-selectable - constexpr int bootStatesCount = NR_ELEMENTS(bootStates); + constexpr int bootStatesCount = NR_ELEMENTS(bootStateValues); addRowLabel(F("Output ports")); @@ -290,17 +298,9 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } html_end_table(); - // addFormNote(F("Values < min. range will switch off the output. Set to -1 to not initialize/unused.")); addFormNote(F("Check your device documentation for what is connected to each output.")); } - { - // Keep this setting hidden from the UI - // addHtml(F("")); - } - if (created_new) { delete P139_data; } @@ -344,12 +344,14 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + P139_SET_GENERATE_EVENTS(isFormItemChecked(F("events"))); bool created_new = false; P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr == P139_data) { - P139_data = new (std::nothrow) P139_data_struct(event); + P139_data = new (std::nothrow) P139_data_struct(); + P139_data->loadSettings(event); created_new = true; } @@ -408,15 +410,15 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_TEN_PER_SECOND: - { - P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + // case PLUGIN_TEN_PER_SECOND: + // { + // P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P139_data) { - success = P139_data->plugin_ten_per_second(event); - } - break; - } + // if (nullptr != P139_data) { + // success = P139_data->plugin_ten_per_second(event); + // } + // break; + // } case PLUGIN_FIFTY_PER_SECOND: { diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 1efa6e0990..c09c2f3db0 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -5,13 +5,13 @@ # ifdef ESP32 // **************************************************************************/ -// Constructor +// Constructors // **************************************************************************/ P139_data_struct::P139_data_struct(struct EventStruct *event) { axp2101 = new (std::nothrow) AXP2101(); // Default address and I2C Wire object if (isInitialized()) { // Functions based on: - axp2101->begin(&Wire, AXP2101_ADDR, static_cast(P139_CONFIG_PREDEFINED)); + axp2101->begin(&Wire, AXP2101_ADDR, static_cast(P139_CURRENT_PREDEFINED)); loadSettings(event); outputSettings(event); } else { @@ -19,6 +19,8 @@ P139_data_struct::P139_data_struct(struct EventStruct *event) { } } +P139_data_struct::P139_data_struct() {} + // **************************************************************************/ // Destructor // **************************************************************************/ @@ -33,7 +35,9 @@ String P139_data_struct::loadSettings(struct EventStruct *event) { String result; if (!_settingsLoaded) { - result = LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast(&_settings), sizeof(_settings)); + result = LoadCustomTaskSettings(event->TaskIndex, + reinterpret_cast(&_settings), + sizeof(_settings)); _settingsLoaded = true; } return result; @@ -57,6 +61,15 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { // axp2101->setPortState(false, reg); // Turn off } ++count; + + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, + strformat(F("AXP2101: Port: %s, output: %dmV, pin state: %s"), + toString(reg), _settings.getVoltage(reg), toString(pinState))); + } + # endif // ifndef BUILD_NO_DEBUG } } @@ -69,7 +82,9 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { // saveSettings: Save the settings to the custom settings area // **************************************************************************/ String P139_data_struct::saveSettings(struct EventStruct *event) { - String result = SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&_settings), sizeof(_settings)); + const String result = SaveCustomTaskSettings(event->TaskIndex, + reinterpret_cast(&_settings), + sizeof(_settings)); _settingsLoaded = true; // When freshly saved == loaded :-) return result; @@ -79,9 +94,9 @@ String P139_data_struct::saveSettings(struct EventStruct *event) { // applySettings: Update settings to defaults for selected device // **************************************************************************/ bool P139_data_struct::applySettings(AXP2101_device_model_e device) { - const int idx = static_cast(AXP2101_device_model_e::UserDefined == device ? - AXP2101_device_model_e::MAX : - device); + const int idx = static_cast(AXP2101_device_model_e::UserDefined == device ? + AXP2101_device_model_e::MAX : + device); if ((idx > static_cast(AXP2101_device_model_e::Unselected)) && (idx <= static_cast(AXP2101_device_model_e::MAX))) { @@ -116,6 +131,12 @@ float P139_data_struct::read_value(AXP2101_registers_e value) { } else if (AXP2101_registers_e::batcharge == value) { return static_cast(axp2101->getBatCharge()); + } else + if (AXP2101_registers_e::charging == value) { + return static_cast(axp2101->getChargingState()); + } else + if (AXP2101_registers_e::batpresent == value) { + return static_cast(axp2101->isBatteryDetected()); } return static_cast(axp2101->getPortVoltage(value)); } @@ -125,16 +146,25 @@ float P139_data_struct::read_value(AXP2101_registers_e value) { // **************************************************************************/ // plugin_ten_per_second: Check state and generate events // **************************************************************************/ -bool P139_data_struct::plugin_ten_per_second(struct EventStruct *event) { - // TODO - return false; -} +// bool P139_data_struct::plugin_ten_per_second(struct EventStruct *event) { +// // TODO ? +// return false; +// } // **************************************************************************/ // plugin_fifty_per_second: Check state and generate events // **************************************************************************/ bool P139_data_struct::plugin_fifty_per_second(struct EventStruct *event) { - // TODO + if (isInitialized() && P139_GET_GENERATE_EVENTS) { + const AXP2101_chargingState_e charging = axp2101->getChargingState(); + + if (_chargingState != charging) { + eventQueue.add(event->TaskIndex, F("ChargingState"), // Event: #ChargingState=, (numeric) + strformat(F("%d,%d"), static_cast(charging), static_cast(_chargingState))); + _chargingState = charging; + } + return true; + } return false; } @@ -180,24 +210,33 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, for (int s = 0; s < AXP2101_register_count; ++s) { const AXP2101_registers_e reg = AXP2101_intToRegister(s); - uint16_t value; - uint8_t state = 0u; + + int32_t value = -1; + uint8_t state = 0u; + String data; if (AXP2101_registers_e::chargeled == reg) { - value = static_cast(axp2101->getChargeLed()); + data = strformat(F(", Led: %s"), toString(axp2101->getChargeLed())); } else if (AXP2101_registers_e::batcharge == reg) { - value = axp2101->getBatCharge(); + data = strformat(F(", Battery: %d%%"), axp2101->getBatCharge()); + } else + if (AXP2101_registers_e::charging == reg) { + data = strformat(F(", Battery: %s"), toString(axp2101->getChargingState())); + } else + if (AXP2101_registers_e::batpresent == reg) { + data = strformat(F(", Battery: %s"), boolToString(axp2101->isBatteryDetected())); } else { value = axp2101->getPortVoltage(reg); state = axp2101->getPortState(reg); } - addLog(LOG_LEVEL_INFO, strformat(F("Port: %7s: %4dmV, state: %d, range: %d - %dmV"), + addLog(LOG_LEVEL_INFO, strformat(F("Port: %7s: %4dmV, state: %d, range: %d - %dmV%s"), toString(reg), value, state, AXP2101_minVoltage(reg), - AXP2101_maxVoltage(reg))); + AXP2101_maxVoltage(reg), + data.c_str())); } } else { addLog(LOG_LEVEL_ERROR, F("AXP2101: 'readchip' needs logging level INFO")); @@ -216,7 +255,7 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, const int min_ = AXP2101_minVoltage(reg); if (0 == event->Par3 /* < min_ */) { - // TODO Q: Turn off when A) 0 or B) below minimum voltage? + // TODO Q: Turn off when A) 0 or B) below minimum voltage? Current answer: A) // axp2101->setPortState(false, reg); if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn off port %s"), toString(reg))); @@ -254,7 +293,7 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, } else { // axp2101->setPortState(stateOn, reg); if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn %s port %s"), (stateOn ? F("On") : F("Off")), toString(reg))); + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Switch port %s: %s"), toString(reg), (stateOn ? F("On") : F("Off")))); } success = true; } @@ -337,6 +376,7 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, ***************************************************************************/ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, String & string) { + if (!isInitialized()) { return false; } bool success = false; const String command = parseString(string, 1); @@ -350,6 +390,12 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, } else if (AXP2101_registers_e::batcharge == reg) { string = axp2101->getBatCharge(); + } else + if (AXP2101_registers_e::charging == reg) { + string = static_cast(axp2101->getChargingState()); + } else + if (AXP2101_registers_e::batpresent == reg) { + string = axp2101->isBatteryDetected(); } } else { string = axp2101->getPortVoltage(reg); @@ -361,6 +407,10 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, if (AXP2101_registers_e::chargeled == reg) { string = toString(axp2101->getChargeLed()); success = true; + } else + if (AXP2101_registers_e::charging == reg) { + string = toString(axp2101->getChargingState()); + success = true; } } else { string = toString(axp2101->getPortState(reg)); diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index f0331a280f..2f2f2c6845 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -17,6 +17,18 @@ # define P139_CONFIG_PREDEFINED PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 2) # define P139_CURRENT_PREDEFINED PCONFIG_FLOAT(0) +# define P139_FLAGS PCONFIG_ULONG(0) + +# define P139_FLAG_GENERATE_EVENTS 0 + +// # define P139_FLAG_RAW_DATA_ONLY 1 + +# define P139_GET_GENERATE_EVENTS bitRead(P139_FLAGS, P139_FLAG_GENERATE_EVENTS) +# define P139_SET_GENERATE_EVENTS(x) bitWrite(P139_FLAGS, P139_FLAG_GENERATE_EVENTS, (x)) + +// # define P139_GET_RAW_DATA_ONLY bitRead(P139_FLAGS, P139_FLAG_RAW_DATA_ONLY) +// # define P139_SET_RAW_DATA_ONLY(x) bitWrite(P139_FLAGS, P139_FLAG_RAW_DATA_ONLY, (x)) + # define P139_CONST_1_PERCENT 1 // Lowest used percentage, 0 = off # define P139_CONST_100_PERCENT 100 // Max percentage # define P139_CONST_MIN_LDO 500 // Min. output voltage @@ -27,15 +39,16 @@ struct P139_data_struct : public PluginTaskData_base { public: P139_data_struct(struct EventStruct *event); - P139_data_struct() = delete; + P139_data_struct(); ~P139_data_struct(); - bool plugin_read(struct EventStruct *event); - bool plugin_write(struct EventStruct *event, - const String & string); - bool plugin_get_config_value(struct EventStruct *event, - String & string); - bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_read(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + bool plugin_get_config_value(struct EventStruct *event, + String & string); + + // bool plugin_ten_per_second(struct EventStruct *event); bool plugin_fifty_per_second(struct EventStruct *event); String loadSettings(struct EventStruct *event); String saveSettings(struct EventStruct *event); @@ -76,6 +89,8 @@ struct P139_data_struct : public PluginTaskData_base { // *INDENT-ON* bool _settingsLoaded = false; + + AXP2101_chargingState_e _chargingState = AXP2101_chargingState_e::Standby; }; # endif // ifdef ESP32 From c7ab9a412c53312ad1bff9ff310d85cd62886b3f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 21 Feb 2024 22:41:45 +0100 Subject: [PATCH 07/50] [P139] Add chipid and chargingdetail support --- lib/AXP2101/src/AXP2101.cpp | 59 +++++++++++++++ lib/AXP2101/src/AXP2101.h | 85 ++++++++++++++-------- src/_P139_AXP2101.ino | 3 + src/src/PluginStructs/P139_data_struct.cpp | 26 +++++++ 4 files changed, 142 insertions(+), 31 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 73b04884f9..ed64ada7d2 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -45,6 +45,8 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, case AXP2101_registers_e::batcharge: return displayString ? F("BatCharge") : F("batcharge"); case AXP2101_registers_e::charging: return displayString ? F("ChargingState") : F("chargingstate"); case AXP2101_registers_e::batpresent: return displayString ? F("BatPresent") : F("batpresent"); + case AXP2101_registers_e::chipid: return displayString ? F("ChipID") : F("chipid"); + case AXP2101_registers_e::chargedet: return displayString ? F("ChargingDetail") : F("chargingdet"); } return F(""); } @@ -80,6 +82,25 @@ const __FlashStringHelper* toString(AXP2101_chargingState_e state) { return F(""); } +const __FlashStringHelper* toString(AXP2101_chipid_e chip) { + switch (chip) { + case AXP2101_chipid_e::axp2101: return F("AXP2101"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge) { + switch (charge) { + case AXP2101_chargingDetail_e::tricharge: return F("tri-charge"); + case AXP2101_chargingDetail_e::precharge: return F("pre-charge"); + case AXP2101_chargingDetail_e::constcharge: return F("constant charge (CC)"); + case AXP2101_chargingDetail_e::constvoltage: return F("constant voltage (CV)"); + case AXP2101_chargingDetail_e::done: return F("charge done"); + case AXP2101_chargingDetail_e::notcharging: return F("not charging"); + } + return F(""); +} + AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; @@ -100,6 +121,8 @@ AXP2101_registers_e AXP2101_intToRegister(int reg) { case 15: return AXP2101_registers_e::batcharge; case 16: return AXP2101_registers_e::charging; case 17: return AXP2101_registers_e::batpresent; + case 18: return AXP2101_registers_e::chipid; + case 19: return AXP2101_registers_e::chargedet; } return AXP2101_registers_e::dcdc1; // we shouldn't get here, just defaulting to the first value } @@ -126,6 +149,8 @@ uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } return 0u; @@ -153,6 +178,8 @@ uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } return 0u; @@ -225,6 +252,8 @@ void AXP2101_settings::setVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } } @@ -252,6 +281,8 @@ int AXP2101_settings::getVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: return 0; } return 0xFFFFF == result ? (realValue ? 0 : -1) : result; @@ -280,6 +311,8 @@ void AXP2101_settings::setState(AXP2101_registers_e reg, case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } } @@ -304,6 +337,8 @@ AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: return AXP_pin_s::Protected; } return AXP_pin_s::Default; @@ -507,6 +542,8 @@ uint8_t voltageToRegister(uint16_t voltage, case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } return 0u; @@ -562,6 +599,8 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, case AXP2101_registers_e::batcharge: case AXP2101_registers_e::charging: case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: break; } return 0u; @@ -742,6 +781,18 @@ bool AXP2101::isBatteryDetected() { return (readRegister8(_addr, AXP2101_COM_STAT0_REG) >> 3) & 0x01; } +AXP2101_chargingDetail_e AXP2101::getChargingDetail() { + return static_cast(readRegister8(_addr, AXP2101_COM_STAT1_REG) & 0x07); +} + +uint8_t AXP2101::getChipIDRaw() { + return readRegister8(_addr, AXP2101_CHIP_ID_REG); +} + +AXP2101_chipid_e AXP2101::getChipID() { + return static_cast(getChipIDRaw()); +} + bool AXP2101::set_charger_term_current_to_zero(void) { return bitOff(AXP2101_ADDR, AXP2101_CHARGER_SETTING_REG, 0b00001111); } @@ -853,6 +904,14 @@ void AXP2101::getControlRegisterMask(AXP2101_registers_e reg, ctrl = AXP2101_COM_STAT0_REG; mask = 0b00001000; break; + case AXP2101_registers_e::chipid: + ctrl = AXP2101_CHIP_ID_REG; + mask = 0b11111111; + break; + case AXP2101_registers_e::chargedet: + ctrl = AXP2101_COM_STAT1_REG; + mask = 0b00000111; + break; } } diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index 5d79d574d6..bcc736f0bf 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -9,6 +9,7 @@ */ /** Changelog: + * 2024-02-21 tonhuisman: Add support for ChipId and ChargingDetail state * 2024-02-18 tonhuisman: Add support for ChargingState, isBatteryDetected * 2024-02-17 tonhuisman: Add support for Charge-led and battery charge level, limit to ESP32, as this chip is only available * on ESP32 based units @@ -44,6 +45,8 @@ #define AXP2101_COM_STAT0_REG (0x00) #define AXP2101_COM_STAT1_REG (0x01) +#define AXP2101_CHIP_ID_REG (0x03) +#define AXP2101_CHARGE_DET_REG (0x04) // Fake, not a usable register! #define AXP2101_PMU_CONFIG_REG (0x10) #define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) #define AXP2101_PWROK_PWROFF_REG (0x25) @@ -135,9 +138,11 @@ enum class AXP2101_registers_e : uint8_t { batcharge = AXP2101_BAT_CHARGE_REG, charging = AXP2101_COM_STAT1_REG, batpresent = AXP2101_COM_STAT0_REG, + chipid = AXP2101_CHIP_ID_REG, + chargedet = AXP2101_CHARGE_DET_REG, }; constexpr int AXP2101_settings_count = 14; // Changeable settings -constexpr int AXP2101_register_count = 18; // All registers +constexpr int AXP2101_register_count = 20; // All registers enum class AXP_pin_s : uint8_t { Off = 0x00, // Max. 3 bits can be stored in settings! @@ -161,6 +166,19 @@ enum class AXP2101_chargingState_e : int8_t { Charging = 1, }; +enum class AXP2101_chipid_e : uint8_t { + axp2101 = 0b01000111, // AXP2101 +}; + +enum class AXP2101_chargingDetail_e : uint8_t { + tricharge = 0b000, + precharge = 0b001, + constcharge = 0b010, + constvoltage = 0b011, + done = 0b100, + notcharging = 0b101, +}; + AXP2101_registers_e AXP2101_intToRegister(int reg); uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); @@ -174,6 +192,8 @@ const __FlashStringHelper* toString(AXP2101_device_model_e device, const __FlashStringHelper* toString(AXP_pin_s pin); const __FlashStringHelper* toString(AXP2101_chargeled_d led); const __FlashStringHelper* toString(AXP2101_chargingState_e state); +const __FlashStringHelper* toString(AXP2101_chipid_e chip); +const __FlashStringHelper* toString(AXP2101_chargingDetail_e chip); class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: @@ -312,38 +332,41 @@ class AXP2101 { public: // Utility - uint8_t voltageToRegister(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t registerToVoltage(uint8_t data, - AXP2101_registers_e reg); - uint8_t get_dcdc_status(void); - bool setPortVoltage(uint16_t voltage, - AXP2101_registers_e reg); - uint16_t getPortVoltage(AXP2101_registers_e reg); - bool setPortState(bool sw, - AXP2101_registers_e reg); - bool getPortState(AXP2101_registers_e reg); - - bool setChargeLed(AXP2101_chargeled_d led); - AXP2101_chargeled_d getChargeLed(); - uint8_t getBatCharge(); - AXP2101_chargingState_e getChargingState(); - bool isBatteryDetected(); + uint8_t voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t registerToVoltage(uint8_t data, + AXP2101_registers_e reg); + uint8_t get_dcdc_status(void); + bool setPortVoltage(uint16_t voltage, + AXP2101_registers_e reg); + uint16_t getPortVoltage(AXP2101_registers_e reg); + bool setPortState(bool sw, + AXP2101_registers_e reg); + bool getPortState(AXP2101_registers_e reg); + + bool setChargeLed(AXP2101_chargeled_d led); + AXP2101_chargeled_d getChargeLed(); + uint8_t getBatCharge(); + AXP2101_chargingState_e getChargingState(); + bool isBatteryDetected(); + AXP2101_chargingDetail_e getChargingDetail(); + uint8_t getChipIDRaw(); + AXP2101_chipid_e getChipID(); // Device common functions - void set_bus_3v3(uint16_t voltage); - void set_lcd_back_light_voltage(uint16_t voltage); - void set_bus_5v(uint8_t sw); - bool set_sys_led(bool sw); - void set_spk(bool sw); - void set_lcd_rst(bool sw); - void set_lcd_and_tf_voltage(uint16_t voltage); - void set_vib_motor_voltage(uint16_t voltage); - void set_bat_charge(bool enable); - void power_off(void); - bool set_charger_term_current_to_zero(void); - bool set_charger_constant_current_to_50mA(void); - bool enable_pwrok_resets(void); + void set_bus_3v3(uint16_t voltage); + void set_lcd_back_light_voltage(uint16_t voltage); + void set_bus_5v(uint8_t sw); + bool set_sys_led(bool sw); + void set_spk(bool sw); + void set_lcd_rst(bool sw); + void set_lcd_and_tf_voltage(uint16_t voltage); + void set_vib_motor_voltage(uint16_t voltage); + void set_bat_charge(bool enable); + void power_off(void); + bool set_charger_term_current_to_zero(void); + bool set_charger_constant_current_to_50mA(void); + bool enable_pwrok_resets(void); // Low-level output functions diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 168dcc3209..e0c1126f27 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -9,6 +9,7 @@ /** * Changelog: + * 2024-02-21 tonhuisman: Add support for ChipID and ChargingDetail data supplied by AXP2101 * 2024-02-18 tonhuisman: Add setting for Generate events, support for chargestate and isBatteryDetected, fix some issues * 2024-02-17 tonhuisman: Add setting for Charge led and battery charge level, fix saving adjusted port settings, * set to 0 decimals as we're using mV values @@ -48,6 +49,8 @@ * [#batcharge] : (Doesn't support the .status and .state variants of the variable) * [#chargingstate] : Charging state, -1 = discharging, 0 = standby, 1 = charging * [#batpresent] : (Doesn't support the .status and .state variants of the variable) + * [#chipid] : (Doesn't support the .state variant of the variable) + * [#chargingdet] : (Doesn't support the .state variant of the variable) * TODO: Define additional values? **/ /** diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index c09c2f3db0..1eb76a504f 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -137,6 +137,12 @@ float P139_data_struct::read_value(AXP2101_registers_e value) { } else if (AXP2101_registers_e::batpresent == value) { return static_cast(axp2101->isBatteryDetected()); + } else + if (AXP2101_registers_e::chipid == value) { + return static_cast(axp2101->getChipIDRaw()); + } else + if (AXP2101_registers_e::chargedet == value) { + return static_cast(axp2101->getChargingDetail()); } return static_cast(axp2101->getPortVoltage(value)); } @@ -226,6 +232,12 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, } else if (AXP2101_registers_e::batpresent == reg) { data = strformat(F(", Battery: %s"), boolToString(axp2101->isBatteryDetected())); + } else + if (AXP2101_registers_e::chipid == reg) { + data = strformat(F(", ChipID: %s (0x%02x)"), toString(axp2101->getChipID()), axp2101->getChipIDRaw()); + } else + if (AXP2101_registers_e::chargedet == reg) { + data = strformat(F(", ChargingDetail: %s"), toString(axp2101->getChargingDetail())); } else { value = axp2101->getPortVoltage(reg); state = axp2101->getPortState(reg); @@ -396,6 +408,12 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, } else if (AXP2101_registers_e::batpresent == reg) { string = axp2101->isBatteryDetected(); + } else + if (AXP2101_registers_e::chipid == reg) { + string = axp2101->getChipIDRaw(); + } else + if (AXP2101_registers_e::chargedet == reg) { + string = static_cast(axp2101->getChargingDetail()); } } else { string = axp2101->getPortVoltage(reg); @@ -411,6 +429,14 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, if (AXP2101_registers_e::charging == reg) { string = toString(axp2101->getChargingState()); success = true; + } else + if (AXP2101_registers_e::chipid == reg) { + string = toString(axp2101->getChipID()); + success = true; + } else + if (AXP2101_registers_e::chargedet == reg) { + string = toString(axp2101->getChargingDetail()); + success = true; } } else { string = toString(axp2101->getPortState(reg)); From d94b90497ef38c5f13f71c384d86142b0b69ff3c Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Feb 2024 20:47:18 +0100 Subject: [PATCH 08/50] [String] Add `FsP` macro to convert Flash string to String.c_str() pointer --- src/include/ESPEasy_config.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/include/ESPEasy_config.h b/src/include/ESPEasy_config.h index bc2f8207b9..8ccfe1643c 100644 --- a/src/include/ESPEasy_config.h +++ b/src/include/ESPEasy_config.h @@ -142,6 +142,25 @@ constexpr unsigned CEIL_LOG2(unsigned x) # endif // ifdef ESP8266 +# ifdef ESP8266 + + // (ESP8266) FsP: FlashstringHelper to String-Pointer + # define FsP(F) String(F).c_str() +# endif // ifdef ESP8266 + +# ifdef ESP32 + # if defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C6) + + // (ESP32) FsP: FlashstringHelper to String-Pointer + # define FsP + # endif // if defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C6) + # if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) + + // (ESP32) FsP: FlashstringHelper to String-Pointer + # define FsP + # endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) +# endif // ifdef ESP32 + // User configuration // Include Custom.h before ESPEasyDefaults.h. # ifdef USE_CUSTOM_H From 4e64e061c688acd4351b0aa2993d7fc535c27990 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Feb 2024 20:50:22 +0100 Subject: [PATCH 09/50] [P139] Add I2C-enabled check, implement FsP macro --- src/_P139_AXP2101.ino | 21 ++++++++++------- src/src/PluginStructs/P139_data_struct.cpp | 26 +++++++++++----------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index e0c1126f27..0d0298e031 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -9,6 +9,7 @@ /** * Changelog: + * 2024-02-25 tonhuisman: Add I2C-enabled check on plugin startup, implement FsP macro * 2024-02-21 tonhuisman: Add support for ChipID and ChargingDetail data supplied by AXP2101 * 2024-02-18 tonhuisman: Add setting for Generate events, support for chargestate and isBatteryDetected, fix some issues * 2024-02-17 tonhuisman: Add setting for Charge led and battery charge level, fix saving adjusted port settings, @@ -385,19 +386,23 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - P139_data_struct *P139_init = static_cast(getPluginTaskData(event->TaskIndex)); + if (Settings.isI2CEnabled()) { + P139_data_struct *P139_init = static_cast(getPluginTaskData(event->TaskIndex)); - if (nullptr != P139_init) { + if (nullptr != P139_init) { # ifndef BUILD_NO_DEBUG - addLogMove(LOG_LEVEL_INFO, F("P139: Already initialized, skipped.")); + addLogMove(LOG_LEVEL_INFO, F("P139: Already initialized, skipped.")); # endif // ifndef BUILD_NO_DEBUG - // has been initialized so nothing to do here - success = true; // Still was successful (to keep plugin enabled!) - } else { + // has been initialized so nothing to do here + success = true; // Still was successful (to keep plugin enabled!) + } else { # ifndef BUILD_NO_DEBUG - addLogMove(LOG_LEVEL_DEBUG, F("P139: PLUGIN_INIT")); + addLogMove(LOG_LEVEL_DEBUG, F("P139: PLUGIN_INIT")); # endif // ifndef BUILD_NO_DEBUG - success = initPluginTaskData(event->TaskIndex, new (std::nothrow) P139_data_struct(event)); + success = initPluginTaskData(event->TaskIndex, new (std::nothrow) P139_data_struct(event)); + } + } else { + addLog(LOG_LEVEL_ERROR, F("AXP2101: I2C not enabled, initialization failed!")); } break; diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 1eb76a504f..a7626086d6 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -67,7 +67,7 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("AXP2101: Port: %s, output: %dmV, pin state: %s"), - toString(reg), _settings.getVoltage(reg), toString(pinState))); + FsP(toString(reg)), _settings.getVoltage(reg), FsP(toString(pinState)))); } # endif // ifndef BUILD_NO_DEBUG } @@ -222,28 +222,28 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, String data; if (AXP2101_registers_e::chargeled == reg) { - data = strformat(F(", Led: %s"), toString(axp2101->getChargeLed())); + data = strformat(F(", Led: %s"), FsP(toString(axp2101->getChargeLed()))); } else if (AXP2101_registers_e::batcharge == reg) { data = strformat(F(", Battery: %d%%"), axp2101->getBatCharge()); } else if (AXP2101_registers_e::charging == reg) { - data = strformat(F(", Battery: %s"), toString(axp2101->getChargingState())); + data = strformat(F(", Battery: %s"), FsP(toString(axp2101->getChargingState()))); } else if (AXP2101_registers_e::batpresent == reg) { - data = strformat(F(", Battery: %s"), boolToString(axp2101->isBatteryDetected())); + data = strformat(F(", Battery: %s"), FsP(boolToString(axp2101->isBatteryDetected()))); } else if (AXP2101_registers_e::chipid == reg) { - data = strformat(F(", ChipID: %s (0x%02x)"), toString(axp2101->getChipID()), axp2101->getChipIDRaw()); + data = strformat(F(", ChipID: %s (0x%02x)"), FsP(toString(axp2101->getChipID())), axp2101->getChipIDRaw()); } else if (AXP2101_registers_e::chargedet == reg) { - data = strformat(F(", ChargingDetail: %s"), toString(axp2101->getChargingDetail())); + data = strformat(F(", ChargingDetail: %s"), FsP(toString(axp2101->getChargingDetail()))); } else { value = axp2101->getPortVoltage(reg); state = axp2101->getPortState(reg); } addLog(LOG_LEVEL_INFO, strformat(F("Port: %7s: %4dmV, state: %d, range: %d - %dmV%s"), - toString(reg), + FsP(toString(reg)), value, state, AXP2101_minVoltage(reg), @@ -270,7 +270,7 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, // TODO Q: Turn off when A) 0 or B) below minimum voltage? Current answer: A) // axp2101->setPortState(false, reg); if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn off port %s"), toString(reg))); + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn off port %s"), FsP(toString(reg)))); } success = true; } else @@ -278,7 +278,7 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, // axp2101->setPortVoltage(event->Par3, reg); // axp2101->setPortState(true, reg); // Turn on after setting the voltage if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set port %s to %dmV"), toString(reg), event->Par3)); + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set port %s to %dmV"), FsP(toString(reg)), event->Par3)); } success = true; } @@ -300,12 +300,12 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, if (AXP2101_isPinProtected(pinState)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat(F("AXP2101: Port %s is %s"), toString(reg), toString(pinState))); + addLog(LOG_LEVEL_ERROR, strformat(F("AXP2101: Port %s is %s"), FsP(toString(reg)), FsP(toString(pinState)))); } } else { // axp2101->setPortState(stateOn, reg); if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Switch port %s: %s"), toString(reg), (stateOn ? F("On") : F("Off")))); + addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Switch port %s: %s"), FsP(toString(reg)), FsP(stateOn ? F("On") : F("Off")))); } success = true; } @@ -357,10 +357,10 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, for (int s = 0; s < AXP2101_settings_count; ++s) { const AXP2101_registers_e reg = AXP2101_intToRegister(s); addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: %7s Percentage range: %d - %dmV (State: %s)"), - toString(reg), + FsP(toString(reg)), _ranges[s][0], _ranges[s][1], - toString(_settings.getState(reg)))); + FsP(toString(_settings.getState(reg))))); } } } From a4ae2cb3af85068c27dae667c494ba8f92e78970 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 18 Jan 2025 12:34:08 +0100 Subject: [PATCH 10/50] [P139] Apply some code optimizations --- src/_P139_AXP2101.ino | 43 ++++++++++++---------- src/src/PluginStructs/P139_data_struct.cpp | 2 - src/src/PluginStructs/P139_data_struct.h | 6 +-- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 0d0298e031..701b52c7b3 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -75,17 +75,18 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) { case PLUGIN_DEVICE_ADD: { - Device[++deviceCount].Number = PLUGIN_ID_139; - Device[deviceCount].Type = DEVICE_TYPE_I2C; - Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; - Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; - Device[deviceCount].PowerManager = true; // So it can be started before SPI is initialized - Device[deviceCount].FormulaOption = true; - Device[deviceCount].ValueCount = 4; - Device[deviceCount].SendDataOption = true; - Device[deviceCount].TimerOption = true; - Device[deviceCount].TimerOptional = true; - Device[deviceCount].PluginStats = true; + auto& dev = Device[++deviceCount]; + def.Number = PLUGIN_ID_139; + dev.Type = DEVICE_TYPE_I2C; + dev.VType = Sensor_VType::SENSOR_TYPE_QUAD; + dev.OutputDataType = Output_Data_type_t::Simple; + dev.PowerManager = true; // So it can be started before SPI is initialized + dev.FormulaOption = true; + dev.ValueCount = 4; + dev.SendDataOption = true; + dev.TimerOption = true; + dev.TimerOptional = true; + dev.PluginStats = true; break; } @@ -109,7 +110,8 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) { for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { if (i < P139_NR_OUTPUT_VALUES) { - ExtraTaskSettings.setTaskDeviceValueName(i, toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); + const uint8_t choice = PCONFIG(P139_CONFIG_BASE + i); + ExtraTaskSettings.setTaskDeviceValueName(i, toString(static_cast(choice), false)); } else { ExtraTaskSettings.clearTaskDeviceValueName(i); } @@ -200,8 +202,9 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) static_cast(AXP2101_chargeled_d::Flash_4Hz), static_cast(AXP2101_chargeled_d::Steady_On), }; + constexpr uint8_t valueCount = NR_ELEMENTS(chargeledValues); addFormSelector(F("Charge LED"), F("led"), - NR_ELEMENTS(chargeledValues), + valueCount, chargeledNames, chargeledValues, static_cast(P139_data->_settings.getChargeLed())); } @@ -233,8 +236,9 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) static_cast(AXP2101_device_model_e::LilyGO_TBeamS3_v3), static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! + constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); addFormSelector(F("Predefined device configuration"), F("predef"), - NR_ELEMENTS(predefinedValues), + valueCount, predefinedNames, predefinedValues, 0, !isPowerManagerTask); if (!isPowerManagerTask) { @@ -326,12 +330,13 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) valOptions[r + 1] = toString(reg); valValues[r + 1] = static_cast(reg); } + constexpr uint8_t valueCount = NR_ELEMENTS(valValues); for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { sensorTypeHelper_loadOutputSelector(event, P139_CONFIG_BASE + i, i, - NR_ELEMENTS(valValues), + valueCount, valOptions, valValues); } @@ -390,15 +395,15 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) P139_data_struct *P139_init = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P139_init) { - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_INFO, F("P139: Already initialized, skipped.")); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG // has been initialized so nothing to do here success = true; // Still was successful (to keep plugin enabled!) } else { - # ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLogMove(LOG_LEVEL_DEBUG, F("P139: PLUGIN_INIT")); - # endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG success = initPluginTaskData(event->TaskIndex, new (std::nothrow) P139_data_struct(event)); } } else { diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index a7626086d6..d9e2824686 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -19,8 +19,6 @@ P139_data_struct::P139_data_struct(struct EventStruct *event) { } } -P139_data_struct::P139_data_struct() {} - // **************************************************************************/ // Destructor // **************************************************************************/ diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index 2f2f2c6845..f5424e0c56 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -13,8 +13,8 @@ # define P139_CONFIG_BASE 0 // Uses PCONFIG(0)..PCONFIG(3) to store the selection for 4 output values # define P139_SENSOR_TYPE_INDEX (P139_CONFIG_BASE + VARS_PER_TASK) # define P139_NR_OUTPUT_VALUES getValueCountFromSensorType(static_cast(PCONFIG(P139_SENSOR_TYPE_INDEX))) -# define P139_CONFIG_DECIMALS PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 1) -# define P139_CONFIG_PREDEFINED PCONFIG(P139_CONFIG_BASE + VARS_PER_TASK + 2) +# define P139_CONFIG_DECIMALS PCONFIG(P139_SENSOR_TYPE_INDEX + 1) +# define P139_CONFIG_PREDEFINED PCONFIG(P139_SENSOR_TYPE_INDEX + 2) # define P139_CURRENT_PREDEFINED PCONFIG_FLOAT(0) # define P139_FLAGS PCONFIG_ULONG(0) @@ -39,7 +39,7 @@ struct P139_data_struct : public PluginTaskData_base { public: P139_data_struct(struct EventStruct *event); - P139_data_struct(); + P139_data_struct() = delete; ~P139_data_struct(); bool plugin_read(struct EventStruct *event); From 5433d5ae87263c4682aa9b448db9fc7298253c1e Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 18 Jan 2025 13:38:49 +0100 Subject: [PATCH 11/50] [P139] Fix silly typos --- src/_P139_AXP2101.ino | 2 +- src/src/PluginStructs/P139_data_struct.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 701b52c7b3..2244f4deb0 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -76,7 +76,7 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_DEVICE_ADD: { auto& dev = Device[++deviceCount]; - def.Number = PLUGIN_ID_139; + dev.Number = PLUGIN_ID_139; dev.Type = DEVICE_TYPE_I2C; dev.VType = Sensor_VType::SENSOR_TYPE_QUAD; dev.OutputDataType = Output_Data_type_t::Simple; diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index f5424e0c56..8e6bf49853 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -39,7 +39,8 @@ struct P139_data_struct : public PluginTaskData_base { public: P139_data_struct(struct EventStruct *event); - P139_data_struct() = delete; + P139_data_struct() {} + ~P139_data_struct(); bool plugin_read(struct EventStruct *event); From 655e638c56fffe6bb522df25359084e77c777546 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 19 Jan 2025 00:02:09 +0100 Subject: [PATCH 12/50] [Libs] AXP2101 corrections --- lib/AXP2101/library.properties | 1 + lib/AXP2101/src/AXP2101.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/AXP2101/library.properties b/lib/AXP2101/library.properties index e1afb412fd..f20e161c9d 100644 --- a/lib/AXP2101/library.properties +++ b/lib/AXP2101/library.properties @@ -8,3 +8,4 @@ category=Device Control url=https://github.com/tonhuisman/AXP2101 architectures=* includes=AXP2101.h +depends= diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index ed64ada7d2..378e5f76af 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -462,8 +462,8 @@ bool AXP2101::bitOnOff(bool sw, /** * Convert a voltage to the indicated register-data, using the matching offset and range(s) */ -uint8_t voltageToRegister(uint16_t voltage, - AXP2101_registers_e reg) { +uint8_t AXP2101::voltageToRegister(uint16_t voltage, + AXP2101_registers_e reg) { uint16_t min = 500; uint16_t max = 0; From 21c7c37c321068b508f09ed3b078f40e088ba494 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 19 Jan 2025 00:02:39 +0100 Subject: [PATCH 13/50] [P139] Enable writing to AXP2101 chip --- src/_P139_AXP2101.ino | 1 + src/src/PluginStructs/P139_data_struct.cpp | 25 ++++++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 2244f4deb0..caaa4126f3 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -9,6 +9,7 @@ /** * Changelog: + * 2025-01-18 tonhuisman: Enable writing values to chip. * 2024-02-25 tonhuisman: Add I2C-enabled check on plugin startup, implement FsP macro * 2024-02-21 tonhuisman: Add support for ChipID and ChargingDetail data supplied by AXP2101 * 2024-02-18 tonhuisman: Add setting for Generate events, support for chargestate and isBatteryDetected, fix some issues diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index d9e2824686..b1acb3ebd0 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -53,10 +53,10 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { if (!AXP2101_isPinDefault(pinState)) { if (AXP_pin_s::On == pinState) { - // axp2101->setPortVoltage(_settings.getVoltage(reg), reg); - // axp2101->setPortState(true, reg); // Turn on after setting the voltage + axp2101->setPortVoltage(_settings.getVoltage(reg), reg); + axp2101->setPortState(true, reg); // Turn on after setting the voltage } else { - // axp2101->setPortState(false, reg); // Turn off + axp2101->setPortState(false, reg); // Turn off } ++count; @@ -265,16 +265,18 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, const int min_ = AXP2101_minVoltage(reg); if (0 == event->Par3 /* < min_ */) { - // TODO Q: Turn off when A) 0 or B) below minimum voltage? Current answer: A) - // axp2101->setPortState(false, reg); + // Q: Turn off when A) 0 or B) below minimum voltage? Selected answer: A) + axp2101->setPortState(false, reg); + if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Turn off port %s"), FsP(toString(reg)))); } success = true; } else if ((event->Par3 >= min_) && (event->Par3 <= AXP2101_maxVoltage(reg))) { - // axp2101->setPortVoltage(event->Par3, reg); - // axp2101->setPortState(true, reg); // Turn on after setting the voltage + axp2101->setPortVoltage(event->Par3, reg); + axp2101->setPortState(true, reg); // Turn on after setting the voltage + if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set port %s to %dmV"), FsP(toString(reg)), event->Par3)); } @@ -301,7 +303,8 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, addLog(LOG_LEVEL_ERROR, strformat(F("AXP2101: Port %s is %s"), FsP(toString(reg)), FsP(toString(pinState)))); } } else { - // axp2101->setPortState(stateOn, reg); + axp2101->setPortState(stateOn, reg); + if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Switch port %s: %s"), FsP(toString(reg)), FsP(stateOn ? F("On") : F("Off")))); } @@ -324,10 +327,10 @@ bool P139_data_struct::plugin_write(struct EventStruct *event, P139_CONST_1_PERCENT, P139_CONST_100_PERCENT, _ranges[s][0], _ranges[s][1]); - // axp2101->setPortVoltage(_value, reg); - // axp2101->setPortState(true, reg); // Turn on after setting the voltage + axp2101->setPortVoltage(_value, reg); + axp2101->setPortState(true, reg); // Turn on after setting the voltage } else { - // axp2101->setPortState(false, reg); // Turn off + axp2101->setPortState(false, reg); // Turn off } success = true; } From 8d02956e0de67db2a021049191b5902647e84646 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 19 Jan 2025 20:28:14 +0100 Subject: [PATCH 14/50] [P139] Add documentation --- docs/source/Plugin/P139.rst | 185 ++++++++++++++++++ docs/source/Plugin/P139_ChargeLEDOptions.png | Bin 0 -> 32085 bytes .../Plugin/P139_DeviceConfiguration.png | Bin 0 -> 121739 bytes .../Plugin/P139_InitialStateOptions.png | Bin 0 -> 5743 bytes .../Plugin/P139_PredefinedDeviceOptions.png | Bin 0 -> 26926 bytes docs/source/Plugin/P139_ValueOptions.png | Bin 0 -> 86204 bytes docs/source/Plugin/P139_commands.repl | 53 +++++ docs/source/Plugin/P139_events.repl | 12 ++ docs/source/Plugin/P139_values.repl | 54 +++++ docs/source/Plugin/_Plugin.rst | 1 + docs/source/Plugin/_plugin_categories.repl | 2 +- .../Plugin/_plugin_substitutions_p13x.repl | 13 ++ docs/source/Reference/Command.rst | 5 + docs/source/Reference/Events.rst | 5 + 14 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 docs/source/Plugin/P139.rst create mode 100644 docs/source/Plugin/P139_ChargeLEDOptions.png create mode 100644 docs/source/Plugin/P139_DeviceConfiguration.png create mode 100644 docs/source/Plugin/P139_InitialStateOptions.png create mode 100644 docs/source/Plugin/P139_PredefinedDeviceOptions.png create mode 100644 docs/source/Plugin/P139_ValueOptions.png create mode 100644 docs/source/Plugin/P139_commands.repl create mode 100644 docs/source/Plugin/P139_events.repl create mode 100644 docs/source/Plugin/P139_values.repl diff --git a/docs/source/Plugin/P139.rst b/docs/source/Plugin/P139.rst new file mode 100644 index 0000000000..f166c8f1f1 --- /dev/null +++ b/docs/source/Plugin/P139.rst @@ -0,0 +1,185 @@ +.. include:: ../Plugin/_plugin_substitutions_p13x.repl +.. _P139_page: + +|P139_typename| +================================================== + +|P139_shortinfo| + +Plugin details +-------------- + +Type: |P139_type| + +Name: |P139_name| + +Status: |P139_status| + +GitHub: |P139_github|_ + +Maintainer: |P139_maintainer| + +Used libraries: |P139_usedlibraries| + +Description +----------- + +I2C Power management controller. As found in some ESP32 models of M5Stack and TTGO. + +Plugin is only available in ESP32 builds, as the library uses some software constructs that aren't available for ESP8266, and this controller is currently only found to be used with ESP32 boards. + +Configuration +-------------- + +.. image:: P139_DeviceConfiguration.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. + +.. include:: PriorityTask.rst + +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` + +Device Settings +^^^^^^^^^^^^^^^^ + +* **Charge LED**: Select the working of the Charge LED (if that's available on your device). Available options: + +.. image:: P139_ChargeLEDOptions.png + +* *Off*: Do not turn the LED on during charging. + +* *Flash 1Hz*: The LED will flash in a slow 1Hz tempo during charging. + +* *Flash 4Hz*: The LED will flash in a somewhat faster 4Hz rate during charging. + +* *Steady On*: The LED will be on during charging. + +.. + +* **Generate events**: Generate events when the charging state changes. There are 2 event values for this event: ``#ChargingState=,``. + +Hardware ports AXP2101 +^^^^^^^^^^^^^^^^^^^^^^ + +* **Predefined device configuration** The plugin provides presets for some specific devices: + +.. image:: P139_PredefinedDeviceOptions.png + +* *Select an option to set default values*: This option will always be selected when opening the page. + +* *M5Stack Core2 v1.1*: Settings for the M5Stack Core2 v1.1, and some derived special models, hardware (not for v1.0 hardware). + +* *M5Stack Core S3*: Settings for the M5Stack S3 hardware. + +* *LilyGO T-Beam v1.2*: Settings for the LilyGO T-Beam v1.2 series of GPS/LoRa devices with optional OLed display (not for v1.0 or v1.1 models). + +* *LilyGO T-Beam S3 v3*: Settings for the LilyGO T-Beam S3 v3 series hardware. + +* *LilyGO T-Pcie v1.2*: Settings for the LilyGO T-Pcie v1.2 series hardware. + +* *User defined*: To be able to configure all available ports the User defined option is added, f.e. when using a custom designed, or not yet supported, hardware setup. + +When available, new predefined devices will be added. The User defined option will stay the last option. + +Depending on the selected configuration, the ports of the AXP2101 can be set at a predefined value. When setting a value below the minimum value (shown next to the voltage selector), the port will be turned off. Above the maximum voltage is not supported/allowed. Stepsize/resolution depends on the port, and can be found in the technical documentation of the AXP2101. + +.. warning:: Check board documentation for what each port is actually connected to, and the allowed voltage range for that port. + +For the available ports, the initial state can be selected. The available options are: + +.. image:: P139_InitialStateOptions.png + +* *Off*: Sets the state to off level (0 V). + +* *On*: Sets the state to on (high) level. + +* *Default*: Doesn't set the state of the port. + +If the column shows ``[Protected]`` or ``[Disabled]``, that port is not available for use in the current configuration or in the commands (see below). + +Output Configuration +^^^^^^^^^^^^^^^^^^^^ + +* **Number Output Values**: Select the number of values that have to be available. The default is set to *Quad*, as there are far more than 4 values available for display. + +Available options: *Single* (1), *Dual* (2), *Triple* (3) and *Quad* (4). + +* **Value 1..4**: Select the desired value to be available in the Values fields. The name of the Values will be set to a matching default automatically, but can be changed afterward. + +.. image:: P139_ValueOptions.png + +Available options: + +* *None*: To leave the value empty/0.00 + +* *DCDC1* .. *CPULDOS*: The voltage of the port. + +* *ChargeLed*: The state of the Charge LED, 0 = Off, 1 = Flash 1Hz, 2 = Flash 4Hz, 3 = Steady On. + +* *BatCharge*: The current that the battery is being charged or discharged with. + +* *ChargingState*: The state of charging, -1 = discharging, 0 = standby, 1 = charging. + +* *BatPresent*: Is a battery present (1) or disconnected (0). + +* *ChipID*: The ID set in the chip, 0 = not found, 71 = AXP2101. + +* *ChargingDetail*: The detailed state of charging. + + * 0: ``tri-charge`` + * 1: ``pre-charge`` + * 2: ``constant charge (CC)`` + * 3: ``constant voltage (CV)`` + * 4: ``charge done`` + * 5: ``not charging`` + +.. note:: Not all options hold usable values for all boards, some may even be not connected. Check the board documentation for available values. + +Data Acquisition +^^^^^^^^^^^^^^^^ + +This group of settings, **Single event with all values**, **Send to Controller** and **Interval** settings are standard available configuration items. Send to Controller is only visible when one or more Controllers are configured. + +* **Interval** By default, Interval will be set to 0 sec. as it is optional. When set > 0 it is the frequency used to read sensor values and send these to any Controllers configured for this device. + +Values +^^^^^^ + +The measured values are available in variables, where the initial names are determined by the selected **Value 1..4** settings. A formula can be set to recalculate. The number of decimals can be set as desired, and defaults to 2. + +In selected builds, per Value **Stats** options are available, that when enabled, will gather the measured data and present most recent data in a graph, as described here: :ref:`Task Value Statistics: ` + +Commands +~~~~~~~~ + +.. include:: P139_commands.repl + + +Extra values +~~~~~~~~~~~~ + +As this plugin has more values available than can be set in the regular Values fields, all values are also available from the Get Config feature. + +.. include:: P139_values.repl + + +Extra events +~~~~~~~~~~~~ + +Besides the regular events, of ``#=`` or ``#All=,..`` when the **Interval** is set, there can be an extra event generated: + +.. include:: P139_events.repl + + +Change log +---------- + +.. versionchanged:: 2.0 + + |added| 2025-01-19 Initially added. diff --git a/docs/source/Plugin/P139_ChargeLEDOptions.png b/docs/source/Plugin/P139_ChargeLEDOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1f155e7f69a971867ecc029f726cb37756e7c5 GIT binary patch literal 32085 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 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_DeviceConfiguration.png b/docs/source/Plugin/P139_DeviceConfiguration.png new file mode 100644 index 0000000000000000000000000000000000000000..be65f35e55b34be4203d529455ba1287eccc56d1 GIT binary patch literal 121739 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ⅅ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 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_InitialStateOptions.png b/docs/source/Plugin/P139_InitialStateOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..1628edddb0797ea1e591cdacf79e60d99c47dd6c GIT binary patch literal 5743 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 D1U{Gr literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_PredefinedDeviceOptions.png b/docs/source/Plugin/P139_PredefinedDeviceOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4034418dcff5f110b779bde3aed7491c93c8cb GIT binary patch literal 26926 zcmaI71yq#n_cd&QbPY)7FbLAAATZPrLnA06r6?jQ-7O$614s;wGziilEiecSCEcCU z-5uWz&+qxi`@ZX&wPb`fbH};Pb>H`6u1JJWi+}6ZEh4xQ?CGsrcaXsU$8hff zKmR-((E{G?nW!kjfOp_^B3JDNyy00Z>Dk@7MU1)rcRPxUm=5@(g1!6;dzh)+8(VX0 zdvhzxTW{XmT3MJ9;R}fI@C)z=@(YSEb2AGFKM@vrA|gbDFR!gBB&aRKBOoZi%)_i` zWo7oslv(cPJMAVmy0>mI--5$rbexUWYlrP1Z4OuBR;P3YjF_-F@Ub-$^<&TO-HmPTx!HPyYAC*PZ1nB5G(8x1O{UJ_m>1x zcx2drUe>Y@Em_&LjAAreon1X4B`e7_5WO2!i0i{Fp%WHlZl2YsIWnLN>WaQ`b$dG;sz_;!F{ri^W#eTp2-lAGkIRy02fg#?EFkG-k zj}J5M!X22FD@A{RQred*L(Bje`BUxd#q70)p?OiHCq>l7z$X@Q|o4N@>ivQJ6ekyEx}HCvkdc=QdfZqt5Og?&NT4s%0&0sWQucAuHarV6e#{jcdu+Vmj4va7OHR zv#GpQy%A*>@W>U4@`UsaWwPvfYOq_}*nQ)sYk)Apow zK&RI`hxU*2;(dZY+?l)!FncCJ9c_otIUfYOY}weg<(>hb+YgY|N`7NecFOx)ORZUm z@Aie{MDx)|P=Sib;Jd8Brw+)vHQo<&-zNeDWgr3`5S28IQdxnqhg!jtma-Hj>hQEr z5E~MCEsI}4HnUn$KP=#p{8|Z+dku34hWR<^S+KS(oN!I67pE7EE$W`S>4^I@I^a&kSFC_U}(m*p8}|tjHuvo>A&` zxgU)jg>3Ofh}Q<6aIar1ZJ1bm^GXh~aMP|M2;Yr$UKQk2Q3yg|WJwxkZ;Osws!F?~ zs2TnoT5ySyE=J>#V18Ndj4S62-*$W@g5&)BUA!+JQ($P`8%Xt|Ul;3wQy$wyk6TJ* zdJi->LMxu(_s&|(9P*cJjGDTR|DN=(&igP-+kF%Ur-@=6m0q`=Ey5{i?0wTx%;U0R+94^9xVfdL*|>8YHDAF7=XkL zO@jzL=|;%t;QHLnaR}QZ)W$K1)aP>}EiZ465%77%6zO8wLHWJ8Cc?#_cg)8W^Lt532|-=SvtkpLV3V=*LewY^V^x; zlRK?PP3*`|UK1AI7LE@;x$F({Raulw@2x9u3Pw@O!_yqBok-4uw|~#k&*b@1N+Gaq zUo(7e@O3`?y%yQ#%75ZL7GM^aLb&$Ze4ZuSl6dh}Qr#umV#n0>;7!`AKSjjz>XYT# zUZF5H4}bcsmX6WnZShDQFEa)bfelJ?{iUZv4W!_3iw zr9GR&LR`50pH~)`qmNt_eC|xUJDpaZ+FtMK4)p8fSy@>}SDe3^#~y81d5!C70NaCt z7uhAlFRG-_t+lVVry{RYG*aJJ9jyQOa*7?<`wQl_R0C=r`a6!xzFIzW(C*Jl;cB9z z)W4o?Ml%hKKXElQO1VGfvGb{sxX@W|mM5a3wovle@#L%(JzASpg$pa8hVP#j&93d7 zik{63b(_0A_qQ6itcs#)f`eyj)qh8kY{`T(Sc7o{y6X`!e$R9`Uv&L&e*FNDIB@9?exWns$vpuUXI7 zdOPt%oEh15s*k=RxxAR$(5!^Gu9*%u9T{?h_9-u$tg z-1SUrksgXjV{fA0UZ3KtC8R3(@yVGavtBlQ#Xu8<#c9y+B({ZqG+t25(EWKvSQqsLIYDgX1@TB|OJ5`^L{m+CCN=D-=d z9rvZgEywusBlku3arNscsHFL=6blyl-j8A7l{l2?;3IiQ7u!XGV3fcagZNl1t};1& z*3fpey=e5!JCe4F#v67he^a67P6s1ClpmPp5kC>yAG|$3HqcLrx3+rvZN+7c)^5`f z_8zsCte{kJaKWJiN-Rg_dRgk*Tsf_<30TrO(%t8G4Z9O!aQ(PAZ5#ymCM~cl&k#x=CD{3SAPQ6D-cLDYK{&HHUnjm1H(+=gfzkj=jo)^cp7dEqPx#x0b6^7VMq)XGCEt zPr?t?ju`C!a1I`&zkE`7I|c(4-@q)3+`sakX*9Iw`b8^}=(b`3OvBXJ^B*8(36{vGMGU z3`rBY$EGc%hsF%^A=W|M@aip>juHcDCdpJ1M2p0pEZCv7xVSicVT2WD|B+k1IbChN zYbZUzG?N=_aDnVebSYuF?r?H`V|$;ePA_>a-rAG#mBBN^1DMs}fwy9a*!b|7X(pAH zSH=39eZ(bUv7wWglPlpb-b@4ZSL)p%kssfLjIG*Btqd+`KIyZac@~-0EZF7=CM>X? zy_?yfv7oxBof}tu&$@KgrgH4{yce7WoK6_padl`~Q{T1m3+EY_gml9a2D87iT zzFL)oE~OHdw64Ee6j`HSt1tHs5O`f{KhCO*oAs$(Fa5&PY~# zBw#e+gZQ@fG0VXDXd3^@{ejwe6znkWZOrfTs#`xdu=-JuUSr21qnGetbNzCS&GI&F z%LVKhSQ5sAzQ2~mdtk@KJ0;s!gK4S-&yYD(VGbw?TQdBvDR#sO^qwh>+XU*&<~umY zI;JfHQcstP2+xK$S*@Qq14Q~m{EU7yy?1cH7o6`qul|?u+5)xUpQaf1=5nM z6M8SA@zCb2&qlhU5$oCC^`LQob>|@@LQPc4rmma^>D>O7b3)fi+%m2WwVZ9`D9QV4 z^C-T;qabl&>+?UA(fD#VCK1ySQRQo=`G<^*;iKM}bPGx3`R}ZyaU2m|f+Rs!oKI-? z*3(nh==9k~A@Sz5#sr6W3!eFpV;zM>f1AY=N4w4c5aE&K^H4h~da<@*o9FOzJSVO2 zV66ScR^kTM!n?J1f1KyvJC1ijEwV13S7ly~T6{CjK`YdDoeG%dF%f-X*H_q{GRi#q z=-sj14M7*It&BZNh#q$wdG2^ITAon8?;(|7+vxFgtr2?HtXcref^0H$;%MU1GQcOm zwB$-ob@TYi;?*#vLHCu;{M7&}Mgh8URh)Ro1@9V(uE#>awol1_yP1>fncuNew@ZdO z=>vJD0tT~t+)iS7r9x{j>9`I>3Yf-kh$jb`G3gm5Ud-+#3VPI!BUFgKb+>c-K~~`k zgpC)wk#rZE*)G2(-oovnT(uHcw_o$5XSuAlj7NLKPFdK&-yZNR9S;_IelKPVXMgvb z#7HcdP=1H8C;x7qKLh08cDNb=aUXnPk+}7CG{wkT3uBneMKP2-CJho~F|A$kiN?5O z+>hr?OzM$0Jk7uC_VlpH8dG45M(NM-K-?6PDKy&Q*sOK+SJ(JwD^`g9bWxY_n+781 z3l|sl>g`iEG!xn);JSYqhGM#ZbGNKjSrWXxg(la^Hg&Kb(rRDXCHR!yijKc9J(zkR zaW%rM8&|^?PG5}TV9|Ow8^w!cAc=Tu0iSP+W#6=&U+nxArP4jTeAO&-jxbANUcYmG zm2EJKI&L?<6bx!LaR)%Yj^?&e?S!x~Um482u{9;pz#`1q#pKcUT@|*X^77}G_E%Os z$gL-HaqPHkU#`4#?nsc(R6=MW695}X?Mg&R?lQzZv2@OSy}CgvM11+H)6Po}3u?|ilDSzrH@N3nZ%(DQHe zS2`cf;_WTFDsuL(ty-HQuvT!z<9r-!Z-!In^%GAA>FPv7HY^nuZr%5tSj4ZK6 z0#xSwq}!_?Vq057av@4if9Uvjt|lcF6}HTeSI1ZO*#b}7_DF5XUC9L2gA<zN-Q_R!9;JrF#6)2+F`95$&y-sP1O(L$=jS!dxYdMLQWFoI zjf&+d#Bi%{#a5)Q_M{Hn<~buO0?C>#AVFXx2y`aw+_zUB@m+&7+`9f_VXZ`godu|QGAP! z|B;&NU0_uE($S#GR-W_=!{@8Q4a!y#9v+^7;bBE`a`GosEPS`;Ip#TAO|oHR>x0+C zaU`IgJb^@9X7}|wHv~;vG{M~dzOZ5KkkZw()A$O*RrR;aWQ}*rSEt*bnQy{2_z;Ghmjzj0-nM*`|m3eh5qRz!u> z^n(eiuk74x3x9&m@1lvOe{vnwOm~lLy^@tYfhtP1ys9?fPB-}$ z0YewMOR6QncDtu&X-UAp)OA3UELK-n{AWqdkcf%^CniVS`5aMrX}3x#n}9ln3sfx) zbX^rqLa{YePg>cLHB`Omv=em>gucGMymFeyZ#$B6dNbU3l4pB&Upe#^VtHA^GWtFc@IlWo3xyHBl~jm32%)BPk1p_>j_%)z{w3XX|cDZ_!<>EUZ8kq^id7aTV4Om zoat6Vc0KIc?Z+DTXtp4&&sy~b=M$uE(okvpOXNMMy@^U%A(g=60eV6qI+=-)5ssIj z+gmg;+rIh}-Vod0J8dLXzDiH-_I zJn6Bg-!`m^?VFmjji1sGqkrw3E{!UzJH9w;E`7FZmbiFUOpa@md#_=ATtZM=05lOJNs@-Uytu`wN= zO7+K91r{nGI}8cYDGBth)+`c0wl5SbQnRQWQq;ha_~h9128O6Lro1ICC+5hw2~V-J znbNo{A_>8lFD@p3Mo7tNFbG#Yau^4{o^-ePjrbGB^7R%`#`)C3qUoCews1ajeXo-I zPZV6pooPIMB?21B5xlV6?Y~0^Fq3T;J5roA(7o#Yi922O{hRECID?h%YRH~IdI?3| z#L~bWJv*}sFe_a5<)BMEh+Qo{F!A79JV0L61PL8{6xM4G_^3&XHBXTckqV4>~ z;UK*S$;@J?Wpm#|6wFwE@jm0_U+?y(29VO*td?kFOLsCpaIFrmhg+l6 z@SX3d0Lm3K4ZT6+A6M~Sj$jTSXaju{69y}6JB_`*Z=dohQHDZx?-o|O?|MH{XZ%UQ zk458UeGo~f@1@V-zCbK`wy^HYicEThKxA5ppR8_0Gn~;hBXg)&!JgU3M)SM=#;>#w zAquaO^>emb07>qdzjkPBclS|T(_sBW_RJ1<>et-%wP2Y7Kr~Is?^H`{ujJUZBloBc zd=%2b_S?=9E6Gbvri^Vcl5BoEWzp=7?VmdGmH3mN;0?*Eev9)<5cFGLT$o?mXJx z-Z8D*7Cn14ES5=>Caa@*(dn4i9b89Zvlj z6B)<@ad^5EP8*~zEXc>lC1&;(YyBZ4resGlIg%*rjf9z7>a9t)V0$x#HJ)_!Fj4(z ztrv3rz1S{}v5-@I2x;dX=OadZ8m&=N^_YY7c2tayjb?2OOkWZU49X(S4Wj$a==&qesXP^vN_;R_$nc;xz-9uq}}r2T0zUIEp(pvxB+AF>QCT;BPq@ z`A2O9$7%Ozx}&`@cv!AwK|be@4sUg~#<%(=Co#QJ|K$6JCxQObQ%29K1V~%CqetY6 zqWAth$n;zCc!Rk|g){Z+pwdq9qBdyb&9@Je!z^)D%c*8J%Ct^BHb0R$M8F9Ow)C(w zX3x*>BzkFBC9}6@Ckz!Rz_R)L#MO0q4JRl9_SM1qk3z?7NvF?5U@(;dNX9v=%Rg&k z{r2b=k6R@}8k96#1E*Rx51;-VIaC|1I$4}?)tCRxAJ{Y(=;@#^xD2^ z*(_Gp-ucXRX!gMAi*Lp4Y5(RNKJcIi$i;i$QBA%$uXFP%~rPtsxmS!?kKrGLZYs7lLEDZC?x48L%+V4-u}PKA$e@Er*j)o)=6 z2$K2(Iy7}rzqHsh!jhY>x1)oCD3nlJ*O*Vl(Z0_N?b!owZczVA29hBKc|&-4I(ki_ zT|8Dh`uaeVm1MYoUyk(S+H_pjx@f;~WqizROX)h+{3~fozI)mlvVUlCKG{dr|HdC@voN6Y9% zIdfp^4p1%f%oe)j!)(pX15bd)^--;05OkJs_%gRrzrF(+Fpms`Wi{ zYS>dA{~W?kW;UkHn+44lFY#4R?K-qjz0G#<8A5FK#Js35=pIEJno*wb;K=N~wRM<(K*0OA_FojDghRf5eYidR`3Rpa;_qK_ zVS&^hLB#Nf`1r>;LkwD`gd{}0nyuU0{9l7(1zQeWy`}wkf5i1k#>Xxq~SpSxSU5Ebd_m&xFI&GCU1Koljz|N-Sat| zll~t^u~I*VHgrv;CC-FBaykf2PtVL>y^@Y<|8Apt^y-OfilUwWA~TXJ72@g&a=PgV zoT8QI-@m_tW7JBcAafZX5`=^vKTeN$QFmH4F<1!G^2W4B6f@(>R(Tr?de$b5(H1_{ z?-%)CVVO}rMESwWDm|o2oh@i`Aa|ZO9Yg$6lpnX4fco>T!vKz1f<>ZxKaQ5aexW}+ zwAXghrE>7IcRJ#@?Z=;ULc<#5Z9UdNO}hX59!iu>$;7d8RFd6>{Tc_R`S`XQzubkJ zSck$U>bPny)SiQ^IN=6qE^J8Zmo!h2`Bv{^UHuh+WRv*;An5qjZted22NkpUB8GJe zooZ?WV|}s~8yf?o*%<{{ibuC(?-{~jh0JgHmKnY%eE|E6WV^A8N7*~hm4UjG7)3Oq zvKLC2Oq+th{uwODejz*IP&?4@kon&8&4&9ET-OCe7AydSiLDG0?*^GWeC%ZDhpNnj z*gQ}66-x8mAPx!c`@LLwJrBHJ|2<;*yZ^5e`+RgrfpM+s*J0XHNI5lpy`jj?ieG{d z*?8i|;wq}wX2R_ED6AQ}x<>4kDko60ELw5IGXh35=8wARby<>^TkCf^OI)I{Cumvd~v6h z%e!Y#Oid+ELit4J}H&axy6!sn03wggN7FECWfQ4hJyp`!q zV1k0g<8R{Y$2S>if7I)kn!yJXC|Wg|qL`H#IYb|7ARkTW}@0nSIWlpjfdgU zgSoalN|pqiy*xkaz(^lvoja$LqKO-kD=V4as{a0wBeMz82Hba2zs1S$F?L~N`qj7X zDfSQ&63?(T)b)Cf0-L0Yr=R@m1^8OtTOtY#abpeXLsSXU$TiPe?{Hhj65$DJn9GRe zI{(5?`Uwhin^Pv1lys+`=3zrPZB3H6k{xVV&5w2hspaAWY`{w9%m~a3lFP~RO{+`9 zkk!;cXjsGF@7uB2Y<}0N9(Buhu$4KfTo6Y7=8dqJKx%;V4%Yqi<)$}B%yu6?RxJrz z`F9%{8P{beKk-xCo_+XmSuY`u*p<>w(tp{*V8CVPUQ%t|pQ`NkdZ8q(fSI_?QM1Qu zb;|cx4|Mu<(6x3;ypz==GMHzbg=TJK)BLrFx-q!?wXXDI1ZYW=ya;m$nTA1?*Od|f zd>)Dh^nlSm)VSLD-a}8*n-#@l)>YF`=cxMD7uu&En>^h8tU1(Ze~zU>2Df;9W4t2r z$E23@h~-)@MZ&jQG&)mJ$8wqM{yz6yxGTZ0Ve5zV571Qw^R@(%-XA0*ZH{;+Yn?Py zracG}y2xwLibn#*snfYNxeMEa!^@ zT*3$6^MGoJ&$>n~Dc!y3Lerh{O>+#tz%Gj`QH5pdFn?Gh4#^z>jLVl(#R_y8by75F zepEPi;*)JzV!IRTHRs_J21>VgdnymYg-t6thFU3YMGaFV{ey1X{@pQ;9wY~KJlH`)-e@5g3DC+yw>L)4>@vF4b6--vf^UQl2v4bKeG?!-vUcU znvb)e$;m*pMxd?FM6+n6EIY*lVnb@)5tuLsgw+^?PJb-SK3XOk5bNFNy(fpEwYz*J z3d?KKTpBb;TG&qLn00Y?+ULi;P8NJ3cAJdP?8zpOUPjyI!lgbX6AR{NHCgzQ>dKH0 zWiMXXhM9~AgoV$WPRzbn@d2AOEYchoS)-g6HzeE3{KQFqJeu=~E%HbiPxCpd@F40x8Qc^)n2aCou=>q!}k}8?!EUW4jCqc$Zp1kr~lyHOdI% zL3l$}Ur$>f4^J?1a|J(oH)@S|Vf+@Qh~;&_6^iTXOnuF-v{LKIm1v!9IoaX+2j?>Q z?zS@td`sjEDk-}MXt9R56%o6xp#A;z(FNEaRS|=PG^c@PRMx9_nqS=;0p?ln3sc1H z*Vn({T+&$A?2%*o%=ajqD_C%`0oIpre>f9~v_q40+&s2jO zXdvs$9w%%q!_5V^hdiYI;Qz{6%({Er4jbO+Hs&s9{--3<%}pdu)HP-&dPO-drl?>A zB9OTh5>-FWV;OJmW`>CCbSgLdn_V{*omG3=YESrhjThJNwF;OQT3-tIL+<}B3g9Qs zTI%U+g#Hn^NgsY^ANbvvMu^Nl0NRh5q3pc|vj(*Aw{vd{FXEMnB~T`fZcG*|%Ajaz zB_ajT9z5z{31>HHiG0$jB68fbl*kYs6yBZ>SrL)jbs3v}C|(dUbl6uKB%ib8%{-s_ zBMFup!@vA6*yx4Hn_aH%dU)i=YoS5s)izedJzm83NLa#ji3dDbnFs=Q<<*CZDMf?K zzSWrZQAW3(AX2FnTtO~{St`$Y<+M7C3(rj#jUS;US@%lLnyn$d7P$~`a)epyR5K9f zj5DB82!0QT>Eg3G+ggJbXs_G$eQJsNjC#1qco-~U{wsUor%zb5h{~_9hX8{lrh{;> zQ?JZ3=R+*T?vUT7oMK(nXTK_67xX=CA<&lYaID??Th7Ey9{w>I(rnqt!{t@2|Drba z1j0Y<&a{HY@d@iFP%`=R`kh?WwYmwzzNf)|Vom3Cj`cB|_Pdl1P`(58|e8yBotDm62_)%g+E3AXh zOsk4pnRMfzL7%n?^sUi2^eqhJ_w-ePWjR}~!Q^`N#n-j~Ke);3#?BP_n*_S!%O2yt zJ^w5ZvddmTErZb}P-aC|y?&|BPcLR&zrLee0I-)L%rGOr^MNB>oIohnj{}hLNIdWf1as~2R=}X zsE>HQjkzPSN72p069qYS?XR&V6|)M93TLB8!)0`_v5aXP)*dI0K`Le09oQReK{!&J zLbmkFjR?^mL-#!4SVBVGHm+L8 z&++M3KlIoJh@uR3Fh1X#SD#d_@o_^+65)}2zKc;1)2z};uL`aO1N za^Di`6q;clT#&!YvN$?Q6CWF!k{bRx9414`d50D0l3e+7BS;sYyvdgEreYB!z1RhA zOW;~0 zzW=cAP!J|MggD3LIRuz4>?#6|o1651v5i3iGX?x;m`AU&_1d><5kucF)en0g9HM;% zP){cZEjbk~kDNt4b{YZz0Dt~#8>@oj9jjElUknp2N%C&KX zB2M<29@+$h?yGj{g1`iXcGllRqcDt##{RzorMdkD2viXYt5*qJ_wz*N@Xw-}hZp91lQc*% z5^{J*qN!-*|I$^^mVo|xCFgSn2M6;Y@pK>LKpVgh3XjJFA>{H&W4PL-v@SliTv?ef@Wt{y8J)XTz@1<63>?3|HLe z?EpLeuSDl?7KHn-XKyH(SSMrjORKzFcJuqbE||W25t~(2B^ICf>_^==3X{XhCa;Cp z@l>DTPk^MPV<3BLp(qr(-@eBuhH$cDKwsgTRe9kd>IspdE0yXVh2{WU9YyFIqW+j&2Qb<B3+f^%7KPbJv<$ogbfE+;z0)Qw+qkI?;v zC|siRXYpL@M*=5CO$!XR!0R*V3K<7AOQsUj07*ai+JZ&3sQVlT?gC&AJ-yMU!e)>V zCJFd`KBTjw9S4%yR>3MwIZvrbsgzW{$PmCMMl#Qt=?q@_97WGN!&)eRxYt^R^tib0 zC+vYGCEXmw!UeU7sIrzB%J3?ZBLsx(lgCN=)wN#R1)vf=xKcJm&r3jGZ2xP2Ds97n zt(H;Fg;YuqHN$D6fZwkOl|~ur=?&Hfjz_~3Q9h~f5VTYzM>UeVVZt?8o$L{r5>o`N zej&(oCmO4`f=Cy8f=2`rDKAd~%?0pbn4D8zt@Q{4EpkIhjHIIp5V9~fp z$5=>>`MV~wj)wr?G767N3`}$z5VBu>Hyqu zYZc={25tbV{0C+p1%;<@lRQzI4i`k8SzmPa{~zSwnGFilU7sl5o1-b9`eJ+SE&iW} z7H2`eYZY6DbAN^0t2GZJP|{QvROUkJhhSiA0-5X`tVsCy&vwGqWQ}-}^0h>!ii=lq z2!k_-fFs>QCDza8(PUo&zWmRX$4g33euz~1sjguH_y4v7jZDt@F#%%>FC<6Z@AzU$ zxDJl{0XcPBNn4Uxd?=OKk+C2Gx4mDb`QY6d+785uYF#reS`f@!SCVWf5268Uupjtr zTT252=bucSWLE#tYa5C^?ZS1~)xH)`vY@)Y(Lt3vqZ|m{z};wHbR-0d{s>P0&Yg9}JXx?EW>lO+PG-*Wj^mHtYMpp+$Q&;dbd{w8N$8Q$y@jRq29; z##<;@_z?%K-h9J*m2?fpzc4|(q+BOj&%7eCYX%-RBS?*N#m)z4hQnn%NWdr>p&J+h zK!q=p*j4x%fwEH#zPH?eB6>mG9~lR6UYCCFMWol`X7Gg5N9gzeZn9&a75vD0%_dmt zP5Fs=1Ax4<>kSm6BJ)AK6&Dxhx`7X$4MaplA^COi1g5KGV@`xJr;$V; zkx1^=Y4nuN=i%4~|1(G^STz+H9v0p`I61N`I~#rtnF4QBt#-r-*&nX!J#pHkPn7a9 z(g0jY=_)b2S{@#fhG_S=0)r}9-9n@9{{HuVIc~%+o*t~lH#axqwt7DTJeo$#StR6$ z;_$yAA;df_q;GJG!(aWMCys;>s-#en)j`9R!rq2ShXZy5Kzzjt%k|uiNbb?_I@NYP z95q`plRBeekZ{)AYarlzPt5QnL(Kh{pFzy_O9-Q6j+o;-&F^3&&wk5}}kPbse- z9og>?>VA*D*s$t%LhmJVUtLUJ<)3Ui)zP&dp^qf_rmrrKm^h`5>q&&A&Syzh6s1nX zDHoQKy^IL>=P7n^w`u!tU@fGQ^mNAdg)$ri@1XvWl$baOTw1|zej-l`)|G!A7-81+ zSk+EW147ilAJ^@!us7Q6`FwBd>T)Z-h0%?>{cot0dh*%C(ogrxz2sr{Sps8@SU%64 zpzPzm7-Aez0O;_2G0AXF32zTq^?R9FSfj-JZA1PrI2`T`|3gWQ%TIBz}=di zv?@ZMQZQXCD{AlXUG4ITH@83!1x_*S&{Q@WSwb84-8OayK2BG)+eUBxvlFOfnGA?k zfI-Yp8WduW%*G9uP~@-seL}@oxC>myCxJKG_-)%~4h|xBf3~V0`7Eb>2CBuEu$JXm#b-a$H?^CeG7FYJ&UF^;xfepoEdO zc6$9s)oV!=s6?{MzU{9QAkBE-7}(k2(ESZ>ae_`cYONN(X~Z{94F84LAgb6bGnkhSEKLR)07r~aE-7gJjoj2D+%SDw*Q$G%cM(u_n<i{f2z7cIAqSp1WH2aiyWq+)0!8Pn!b?9W&AZ(M#ebS1 zz+*t`M8_JqtcIzIqPD#}tGjaZXgaLBT&r8vV)^e%<_FXf%w!mBBG}Q4EnF)Y;&0pB z#HOvikcj5lFE@G1ZfW$Q4&V$c}S+1uimHjp!fCPcrW}y&zMK4Oqz|N=O|pGOF^}+5fd;`V$sG{v zHn%xj8trlikOrr-2lF=J^$VvKxA~E*Ar1WqbLmvSZt! zcH}8}o%^M$KLk&wp$4(;3V9Tumz)^-qp4>)qS6BXcHu#VJQ<3^hhrVA0J6}UP=mY2 z{s+2vRK}-M#iQN%C}Rj zKuI0>jOml&!~VU_oBU8lVfskDcvG2ieTZzICD1n`UuUzC{5<6U%zU|$eEG0;!@?@v zsdmk9Kk29@;BuDf(i^%P9@@h$bpk(KHluNzjV_n-R_G*B)R~& zeFHp`32gsQV)IX_*?pn2oRo;M#+iI8kK9dpcc74`cJq1e_bRRcZG_2VKb`5j2GD!= zuX`=+zqJpS*N>hzK>Q(cakLX)#3@TjZXrpos}C>Q_hyw$lI+N^Wk{(3cPpVSUT}mM zZuIT6GKQXrBYx+z@#z9=GN8SGxcpXkLU+34b!m2b+#*#F5Xa6mN7!{2vDB%6&yaTS?ZXnp-@7Gw%*tG`W8&)cRSZ?LI4N|aXFLA5G z$L{zO^UJ2)R@P7niXOLP0FvmF2+5K51|eRX?$bj7Z}WZ=pQo1Zf6&qG`tM(CDNPdNJAQi%q8$!0`U_KaKbP+csSC-@^NOw{a z;a=k-Lt)ZV;q#5@ON+LXn_e5R<#lyd(%5mFdKFdZ?)TxthwI0mZkPwD0lt_4sgo>d z=LLFU!8ATTJ`IhAet7>rg`8cz=jy!sYB@;vW;73=LuR=DHR#CjI?eLxYb?06iv`{G zCh5TekQF!6X-|M~-e+?K(8kKQ z>GKC3z!D72h-4yxN)qW}&9Z;vlBK=o$R43UFMDh`0l3&`u91CoG$nwA6BunTr;gBB zUSIeA5$Av**{b1w%I#JVFlTac7OfjIsM)zTio1^72XtTPQB}9;$@!8L6M({wWj}Nj zS<*7we8*yyGQL>VT|u%Z^-wWF_d103eVNWbdjbj8Tha~vH*Y!i{@L{EN^{N4_pTL? zi3$3zAt9@KphBZsKP-R^Hc z1n`LB-ZX$p^{mI#K*ubdCznTcb!*b+ze&7~<#>FgE><)gE}^dbKNx9bp*a6p8&gID zTn}rRiHC3lL&HH`Q4MwVKHyPlfPnNaw8yOMuk>G&g#W0et3* zZfli7UmTJT<_)-QL40VX0AhwddZ_!0WM3j1L3(jVRc(F!HS6=^9T@X#bLW-sG0I&4 za4*9JTfGs+rD8Fvvh^!4u2TnW>;LXGL6U2n!MyneVrgl)8l-D&G`GH@Iv%0i5B*X3_N?%Aor$}*~qmA>yPr*jhLxc=O)2LPfbG^UNO=;0)rla zI8fO@NQbS&-Tw6gT#xCaR+>sodz4bhq*$F43W7CRNbV>_^5}~4w0ur5_mx=vyE$3y zBEvZKFl_0zE!4J-=-(~&aNb&Mf(Q=l!xWlAv>}6f2VDBs`Kx@Z>dz*FaIH_Pd6+Eg z7X_^15HczKb@w#WnFiBMnRbDy$9NH0PWWf^4k6b}nFi;EAN1p2BXor^I0|9hr<8Ua z_0@P>fg&ghGq!ryJ|)LrFv;cijSl-vByPsTLWDt7Q?0Da_5}A&y;2TLe>GIlZlbuxzV* z54*cLtfQ+PzU^-G;drM};W~oB*Dd~1qZVj286SXw@FypA5KWVk2?vkCb3u`3Q!C@L zR$L0uzl(kcd-%F~r1+1n5@cF=4t69g)cn`&?BWUwM30qnzz0=D2glX#p z^!-M#7bH&NcHhn@8Al;t)g>rusfqO{oc^`@hk^_DDR4E*;+e=&in2Hf{s*+o67qaM z3_#1H0-CDauf&6^8!&$>4~L(Ke5WTMslTE~4N~oyt95J^Dd6#{YMSfHZ%QwrcRT}U z&xefpyodBcWFRnz0z?U-3{i!s+s*#jJcKl>r0Md5`_Ffy5aH}9&8E&q zd*iKdwRu9H=%QY43F?u(mGqc0ho8knEc%Rrj-lzeNf#r3*pJ+g^+@})pTbqCEf-y4 zf&1J}NcMjUxJ|B8=&I$Yo*aLs&AeMVO_4uMQq`~hOC1zDlAPUV)J36Bv^?Xaf@<`! zl?L5M65g-R8NhwfI!oL&PEo^gZ+Ny!%_u5#`PDZ!lKL|&2xKMB6EG&g6BM%dc>`@~ z18wH0RYPN7Xm^UEM5J>CZ#Hq8&z?xMuCOGzh_!V9lP98UiY7?>Pw9LN7Y6^p_HDms zJRHP>+G$@ZG*d#=KstgD6fs4(?>ro#X_O*f8YXRQyR zbt^(tT+wcCS!nwA*qjg%@-U_ViaX+QFcGvz9y%uDADw)`NRdWxK21ZDvGO`Y8-ek(9 z2Vi!2-T_2zN!ivEf%tJzSz!Vg(mVYCy=#K$)Flbg%N)xO@;bwIf8g`#Hk_WGF}--_ zVf@oH(YnueE_MFhum;qXnba(Mk3id7opydmsDL{_2>uthMG|_kCaI^*hf=ue~k%Z9EbB z#6f9!i%DV{)f4Sy^zGz*WYq}Z<)|G)O~%}AmtsHN^`nIiv-@OZU#Q46hjx%+wQVk?Iact}4yD~rtZ5i<{tpqOiXhXQ z0$j#)Z-xl8s<{@f2t`o;?)em(R0|$11y-yCIxEzUp8^-kE+T#f9*0Ww#>d z2+>50CxI%`9V+3LsY$mPY|R6*mRpt2ugb(@D#^@;1-*4)izS`uJda#>F%5N4HKqpY zy%qOkLWyG4uNIPDylc6x9#;?dbX@BN;$52kYCoEq@^3!`4`yZ(l3#cPRhq zr(z7IyDN14(vJ3@A+Dbf4h#CWo5i(~h!YG`qp#JAXq>)Uy8d_ za=L-%By03O|KMlsb22(=%^jgSxomA`?T&oV{>}Cem2Btk9%*fDU4^sT)dfkEC#7em z>g+$T^|LU3_mvJl0jv6z50}XuWx2;}u*5w)yX*E$_gT^2=W`!^e@DmnO3Js^Ty|L3 z%_jh~IgR2-kz!jCy2~o#vm^q)!0Wqk$n}Fm{#^m7lNER)j6s{2%?0qyAlqJlh&vP& z0#3_~(NVLoi(p))CxyIfSfaUy9^xAtNbHIpo*7WM5}YSoym-A4!E?u*x=-!B3P)H9 zPz=m3M0;WD+=G*DFDI~R^pEe*-Z(tcEi8`}Or^R(MK!q75rOR6)!Itvk1nzz69Np1 zYBG&7k)~@SU6_Mm?8WgEdim(k4*r&YeqHw2HYM6YOUW$hm61}ZTu!yjM^g8oLYrl~ z)pjN)$C<+Vqt+Ry|A>dr`P{Yi6k_8ISj~V!KB(4(b6`yB0eR5e{H~lIB@29uKU2at zt4u^#*IQF5>K1&?Rq8I#PU4F64%(+3IZa=O@GZ1#2&>0L_iKGcR&MdGIl7>!6>V(L zk1E1X6ZowK;~H4kw(QZ>)zez|K2!za^(&Z={ zyez>0R9^4gmgjyFer})t9U~JM!kbl}zgQAac1_t94%g2ftBJ&9jEQ|=_(Y2Gd1>Z{ z3QuE4bVUivNh#9hDG29)KVcNUn|Q<{|4~w;6X7X#aB_IQaBadNe|@a1f9W*JGj~fm zQ>wfOlZRD1&;iyoH4;x*gbeSe1m|&5~0?o5{ks zOeG=TG&6v5PjjGn$YyoJmothX@$sXZ4eku-qnWN(xIHEMY1=P^b){jt5N#aw)TD@ zWI{5-1>n!C@GB;belXZe789tZc_vbtGu)=P;Te~iq{g=xmRyB!nt`ZrnxWjhZ!6=7 zc+Wj65i^QcnV^5vRt)uwOX^j;>?aL5KcF=;3zOd&ZEYAvEGTkD9vD&YGOU}32WmSh zmwM!yQY?$uJ36Abvi<_bU14a!^T}}?5k``50)ON2!oKIuNibo0zi((ioO)>9^4sb$tg1WJ!)TA! zG~|^jDvDuk<$S`!Q|bGBak9q4X1xq)b@Yfko|jZ6mR9v~;Cv7W=i%k4;WmHH$(ee? z^8(J}<#jaFu|ogkaasTAvJ)0ydDuT65k*BxU}FTB0R)Xezi%Lqg;@-cJ|``lYmVFb zxy|fp87cV_sf=k|GyD82 npCKL;jr9ThFlEBn7PeCuMz@}y4A2SPt&Hg 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 Xe7_6W#{=t$KYIjI)PR=BzY6+a$j_|1 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_ValueOptions.png b/docs/source/Plugin/P139_ValueOptions.png new file mode 100644 index 0000000000000000000000000000000000000000..85a0a2479f32488941fe2d14e58de6c6cb1d61c8 GIT binary patch literal 86204 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 literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_commands.repl b/docs/source/Plugin/P139_commands.repl new file mode 100644 index 0000000000..b9fbe22dac --- /dev/null +++ b/docs/source/Plugin/P139_commands.repl @@ -0,0 +1,53 @@ +.. csv-table:: + :header: "Command Syntax", "Extra information" + :widths: 20, 30 + + " + | Commands to control the AXP2101 port voltages & states. + "," + | **Attention**: Commands will fail if the selected port is 'Protected' or 'Disabled' according to the selected Predefined device configuration. + " + " + | ``axp,readchip`` + + "," + | List the current values as configured in the chip. The logging level must be set to ``INFO``. This data can be used to configure additional predefined devices. + " + " + | ``axp,voltage,,`` + + | ``port`` one of the available ports. + | ``voltage`` range: 0.. mV. + "," + | Set selected port to the specified voltage. When set to 0 mV the port will be turned off. + | Ports of the AXP2101: ``DCDC1`` .. ``DCDC4``, ``ALDO1`` .. ``ALDO4``, ``BLDO1``, ``BLDO2``, ``DLDO1``, ``DLDO2`` or ``CPULDOS``. + + | The max port voltage depends on the selected port, and is available in the technical documentation of the AXP2101. + " + " + | ``axp,on,`` + + | ``port`` one of the available ports. + "," + | Set the selected AXP2101 port ON. + | Ports of the AXP2101: ``DCDC1`` .. ``DCDC4``, ``ALDO1`` .. ``ALDO4``, ``BLDO1``, ``BLDO2``, ``DLDO1``, ``DLDO2`` or ``CPULDOS``. + " + " + | ``axp,off,`` + + | ``port`` one of the available ports. + "," + | Set the selected AXP2101 port OFF. + | Ports of the AXP2101: ``DCDC1`` .. ``DCDC4``, ``ALDO1`` .. ``ALDO4``, ``BLDO1``, ``BLDO2``, ``DLDO1``, ``DLDO2`` or ``CPULDOS``. + " + " + | ``axp,percentage,,`` + + | ``port`` one of the available Ports. + | ``percentage``: 0 (off/low) or 1..100% + "," + | Set the selected port to Off for 0%, or On in range .. for 1..100%. + | Ports of the AXP2101: ``DCDC1`` .. ``DCDC4``, ``ALDO1`` .. ``ALDO4``, ``BLDO1``, ``BLDO2``, ``DLDO1``, ``DLDO2`` or ``CPULDOS``. + + | The min and max port voltages depend on the selected port, and are available in the technical documentation of the AXP2101. + " diff --git a/docs/source/Plugin/P139_events.repl b/docs/source/Plugin/P139_events.repl new file mode 100644 index 0000000000..5ccd127cdc --- /dev/null +++ b/docs/source/Plugin/P139_events.repl @@ -0,0 +1,12 @@ +.. csv-table:: + :header: "Event", "Extra information" + :widths: 20, 30 + + " + | ``#ChargingState=,`` + + | ``new_state`` / ``old_state``: ``-1`` (Discharging), ``0`` (Standby) or ``1`` (Charging). + + "," + | Event generated when the **Generate events** checkbox is enabled. + " diff --git a/docs/source/Plugin/P139_values.repl b/docs/source/Plugin/P139_values.repl new file mode 100644 index 0000000000..b3c4290670 --- /dev/null +++ b/docs/source/Plugin/P139_values.repl @@ -0,0 +1,54 @@ +.. csv-table:: + :escape: ^ + :widths: 20, 30 + + " + Every value option can be appended with ``.status`` (or ``.state`` for ``BatCharge`` and any of the ports) + "," + | ``.status``: A text representation of the value will be returned. + | ``.state``: A numeric representation of the value will be returned (usually the same as if the plain value name was used.) + + | Example: ``[#ChargingDetail.status]`` will return ``constant charge (CC)`` for ``ChargingDetail`` = 2. + " + " + ``[#]`` + "," + | Ports of the AXP2101: ``DCDC1`` .. ``DCDC4``, ``ALDO1`` .. ``ALDO4``, ``BLDO1``, ``BLDO2``, ``DLDO1``, ``DLDO2`` or ``CPULDOS``. + " + " + ``[#ChargeLed]`` + "," + | The state of the Charge LED, 0 = ``Off``, 1 = ``Flash 1Hz``, 2 = ``Flash 4Hz``, 3 = ``Steady On``. + " + " + ``[#BatCharge]`` + "," + | The current that the battery is being charged or discharged with. + " + " + ``[#ChargingState]`` + "," + | The state of charging, -1 = ``Discharging``, 0 = ``Standby``, 1 = ``Charging``. + " + " + ``[#BatPresent]`` + "," + | Is a battery present (1) or disconnected (0). (no ``.status`` value available) + " + " + ``[#ChipID]`` + "," + | The ID set in the chip, 71 = ``AXP2101`` or any numeric value found, returning an empty value for the ``.status`` command-suffix. + " + " + ``[#ChargingDet]`` + "," + | The detailed state of charging. + + * 0: ``tri-charge`` + * 1: ``pre-charge`` + * 2: ``constant charge (CC)`` + * 3: ``constant voltage (CV)`` + * 4: ``charge done`` + * 5: ``not charging`` + " diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst index 842a6ed852..76a8cb6927 100644 --- a/docs/source/Plugin/_Plugin.rst +++ b/docs/source/Plugin/_Plugin.rst @@ -374,6 +374,7 @@ There are different released versions of ESP Easy: ":ref:`P135_page`","|P135_status|","P135" ":ref:`P137_page`","|P137_status|","P137" ":ref:`P138_page`","|P138_status|","P138" + ":ref:`P139_page`","|P139_status|","P139" ":ref:`P141_page`","|P141_status|","P141" ":ref:`P142_page`","|P142_status|","P142" ":ref:`P143_page`","|P143_status|","P143" diff --git a/docs/source/Plugin/_plugin_categories.repl b/docs/source/Plugin/_plugin_categories.repl index 6aad869d22..e7398a569b 100644 --- a/docs/source/Plugin/_plugin_categories.repl +++ b/docs/source/Plugin/_plugin_categories.repl @@ -24,7 +24,7 @@ .. |Plugin_Notify| replace:: :ref:`P055_page`, :ref:`P065_page` .. |Plugin_Output| replace:: :ref:`P029_page`, :ref:`P038_page`, :ref:`P041_page`, :ref:`P042_page`, :ref:`P043_page`, :ref:`P070_page`, :ref:`P124_page`, :ref:`P126_page`, :ref:`P128_page`, :ref:`P152_page`, :ref:`P162_page`, :ref:`P166_page` .. |Plugin_Position| replace:: :ref:`P082_page`, :ref:`P121_page`, :ref:`P142_page` -.. |Plugin_PowerMgt| replace:: :ref:`P137_page`, :ref:`P138_page` +.. |Plugin_PowerMgt| replace:: :ref:`P137_page`, :ref:`P138_page`, :ref:`P139_page` .. |Plugin_Presence| replace:: :ref:`P159_page` .. |Plugin_Regulator| replace:: :ref:`P021_page` .. |Plugin_RFID| replace:: :ref:`P008_page`, :ref:`P017_page`, :ref:`P040_page`, :ref:`P111_page` diff --git a/docs/source/Plugin/_plugin_substitutions_p13x.repl b/docs/source/Plugin/_plugin_substitutions_p13x.repl index 7816f26640..b409522581 100644 --- a/docs/source/Plugin/_plugin_substitutions_p13x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p13x.repl @@ -88,3 +88,16 @@ .. |P138_maintainer| replace:: `tonhuisman` .. |P138_compileinfo| replace:: `.` .. |P138_usedlibraries| replace:: `https://github.com/codewitch-honey-crisis/htcw_ip5306` + +.. |P139_name| replace:: :cyan:`AXP2101 Power management ESP32` +.. |P139_type| replace:: :cyan:`Power mgt` +.. |P139_typename| replace:: :cyan:`Power mgt - AXP2101 Power management ESP32` +.. |P139_porttype| replace:: `.` +.. |P139_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P139_github| replace:: P139_AXP2101.ino +.. _P139_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P139_AXP2101.ino +.. |P139_usedby| replace:: `.` +.. |P139_shortinfo| replace:: `Power management controller` +.. |P139_maintainer| replace:: `tonhuisman` +.. |P139_compileinfo| replace:: `.` +.. |P139_usedlibraries| replace:: `Own AXP2101 library, loosely based on M5Stack Core2 code` diff --git a/docs/source/Reference/Command.rst b/docs/source/Reference/Command.rst index 6dd8a0cb38..b5980ed73a 100644 --- a/docs/source/Reference/Command.rst +++ b/docs/source/Reference/Command.rst @@ -763,6 +763,11 @@ P137 :ref:`P137_page` .. include:: ../Plugin/P137_commands.repl +P139 :ref:`P139_page` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: ../Plugin/P139_commands.repl + P141 :ref:`P141_page` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/Reference/Events.rst b/docs/source/Reference/Events.rst index c49826b329..0de532e78d 100644 --- a/docs/source/Reference/Events.rst +++ b/docs/source/Reference/Events.rst @@ -550,6 +550,11 @@ P138 :ref:`P138_page` .. include:: ../Plugin/P138_events.repl +P139 :ref:`P139_page` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. include:: ../Plugin/P139_events.repl + P143 :ref:`P143_page` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 56e9bf2a93da20b4c3fe7f6c5e87f1fde8164320 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 19 Jan 2025 21:29:35 +0100 Subject: [PATCH 15/50] [Docs] Priority task updated layout --- docs/source/Plugin/P137.rst | 2 +- docs/source/Plugin/PriorityTask.rst | 30 ++++++++++++++++------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/source/Plugin/P137.rst b/docs/source/Plugin/P137.rst index 4ef35bfe2c..6ed189a235 100644 --- a/docs/source/Plugin/P137.rst +++ b/docs/source/Plugin/P137.rst @@ -65,7 +65,7 @@ Hardware outputs AXP192 * *LilyGO T-Beam* Settings for the LilyGO T-Beam series of GPS/LoRa devices with optional OLed display. -* *User defined* To be able to configure are available output pins the User defined option is available, f.e. when using a custom designed, or not yet supported, hardware setup. +* *User defined* To be able to configure all available output pins the User defined option is available, f.e. when using a custom designed, or not yet supported, hardware setup. When available, new predefined devices will be added. The User defined option will stay the last option. diff --git a/docs/source/Plugin/PriorityTask.rst b/docs/source/Plugin/PriorityTask.rst index 795a645f2b..180c792b3d 100644 --- a/docs/source/Plugin/PriorityTask.rst +++ b/docs/source/Plugin/PriorityTask.rst @@ -1,26 +1,26 @@ Priority task ^^^^^^^^^^^^^ -* **Priority task** The Priority task option is available in selected plugins only, where they can be configured to be initialized as part of the ESPEasy hardware initialization procedure. +* **Priority task**: The Priority task option is available in selected plugins only, where they can be configured to be initialized as part of the ESPEasy hardware initialization procedure. -For some ESP devices, the installed controllers need to be powered via a Power management device before they can be initialized, but as the Power management controllers are implemented as plugins, to use the available Device configuration infrastructure, a mechanism to load and initialize these plugins before parts of the hardware, like the SPI interface, are initialized, the Priority task option is available. +For some ESP devices, the installed controllers need to be powered on via a Power management device before they can be initialized, but as the Power management controllers are implemented as plugins, to use the available Device configuration infrastructure, a mechanism to load and initialize these plugins before parts of the hardware, like the SPI interface, are initialized, the Priority task option is available. Enable a Priority task ~~~~~~~~~~~~~~~~~~~~~~ -* *Configure the plugin as desired and required* All settings must be completed **before** assigning the Priority task setting, as once enabled, no settings can be changed, to avoid the unit getting into a hardware deadlock. +* **Configure the plugin as desired and required**: All settings must be completed **before** assigning the Priority task setting, as once enabled, no settings can be changed, to avoid the unit getting into a hardware deadlock. -* *Check if the settings are as intended* Just a double-check option to validate the plugin settings. This includes enabling the plugin. +* **Check if the settings are as intended**: Just a double-check option to validate the plugin settings. This includes enabling the plugin. -* *Enable the Priority task option* Once the plugin is enabled, the settings are saved in that state, and no other Power management plugin is enabled as Priority task, the checkbox will be enabled, and can be checked. +* **Enable the Priority task option**: Once the plugin is enabled, the settings are saved in that state, and no other Power management plugin is enabled as Priority task, the checkbox will be enabled, and can be checked. .. image:: PriorityTask_Unchecked.png -* *Save the plugin with enabled Priority task* Enable the checkbox, and Submit the device page. After the usual page reload, the Enabled checkbox, while it still *looks* available, can no longer be unchecked, the Predefined device configuration won't reload the page, and the *Submit* and *Delete* buttons are no longer available. +* **Save the plugin with enabled Priority task**: Enable the checkbox, and Submit the device page. After the usual page reload, the Enabled checkbox, while it still *looks* available, can no longer be unchecked, the Predefined device configuration won't reload the page, and the *Submit* and *Delete* buttons are no longer available. .. image:: PriorityTask_Buttons.png -* *Reboot the ESP* To complete the Priority task configuration, the device needs to be restarted. This can be achieved by a reboot command, or using the Reboot button on the Tools page, a power cycle, or by pressing the reset button, if available. +* **Reboot the ESP**: To complete the Priority task configuration, the device needs to be restarted. This can be achieved by a ``reboot`` command, or using the Reboot button on the Tools page, a power cycle, or by pressing the reset button, if available. When viewing the log during startup, these extra log messages are available at the Info level: @@ -31,6 +31,8 @@ When viewing the log during startup, these extra log messages are available at t 8035 : Info : INIT : Started Priority task 1, [AXP192] Power mgt - AXP192 Power management 8036 : Info : INIT : SPI not enabled +(The taskname and activated plugin can change, depending on the used Power mgt plugin.) + .. note:: An enabled Priority task can not be disabled, or re-enabled, using the ``TaskEnable,`` and ``TaskDisable,`` commands, to avoid hardware dead-lock situations. @@ -46,17 +48,17 @@ After configuring the SPI settings, any Device that needs the SPI interface, lik Disable a Priority task ~~~~~~~~~~~~~~~~~~~~~~~ -* *Prepare configuration to disable Priority task* If the Priority task needs to be disabled, it is required to execute a similar procedure as when enabling the Priority task, but in reverse order. +* **Prepare configuration to disable Priority task**: If the Priority task needs to be disabled, it is required to execute a similar procedure as when enabling the Priority task, but in reverse order. - * *Disable Devices/Tasks that use the specific hardware* Any task that uses the hardware, controlled via the Priority task, like a TFT controller, **must** be disabled (and saved). + * **Disable Devices/Tasks that use the specific hardware**: Any task that uses the hardware, controlled via the Priority task, like a TFT controller, **must** be disabled (and saved). - * *Disable the hardware interface* The same goes for the SPI interface, it **must** be disabled on the Hardware page, and the device should at least be rebooted, to activate that change. + * **Disable the hardware interface**: The same goes for the SPI interface, it **must** be disabled on the Hardware page, and the device should at least be rebooted, to activate that change. -* *Send the command to disable the priority task* To disable a priority task, a separate command is introduced: ``disableprioritytask,`` that takes 1 required argument, the name or number of a task that is a Priority task. This will disable the Priority task state in memory, **but not save that state!** +* **Send the command to disable the priority task**: To disable a priority task, a separate command is introduced: ``disableprioritytask,`` that takes 1 required argument, the name or number of a task that is a Priority task. This will disable the Priority task state in memory, **but not save that state!** -* *Open the Device configuration* To complete disabling of the former Priority task, open the device confguration page for that task. You will notice that the Enabled checkbox is still disabled (can't be clicked to change state). To complete this, the page should be saved using the Submit button. When the page is reloaded, also the task will be **Disabled** automatically! If so desired, instead of saving the page, the task can immediately be deleted by use of the Delete button. +* **Open the Device configuration**: To complete disabling of the former Priority task, open the device confguration page for that task. You will notice that the Enabled checkbox is still disabled (can't be clicked to change state). To complete this, the page should be saved using the Submit button. When the page is reloaded, also the task will be **Disabled** automatically! If so desired, instead of saving the page, the task can immediately be deleted by use of the Delete button. -* *Procedure completed* The Priority task is now disabled (or removed), and the settings are updated. After another reboot, the startup log will provide the matching information: +* **Procedure completed**: The Priority task is now disabled (or removed), and the settings are updated. After another reboot, the startup log will provide the matching information: .. code-block:: text @@ -66,3 +68,5 @@ Disable a Priority task (No mention of a Priority task being started.) + +[End of Priority task paragraph] From 678f61cce5b4fbf5800afaa584ab7b3a1018f67e Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 20 Jan 2025 16:12:47 +0100 Subject: [PATCH 16/50] [P139] Add capturing ADC inputs --- lib/AXP2101/src/AXP2101.cpp | 188 +++++++++++++++++++++ lib/AXP2101/src/AXP2101.h | 48 +++++- src/_P139_AXP2101.ino | 6 +- src/src/PluginStructs/P139_data_struct.cpp | 59 +++---- 4 files changed, 267 insertions(+), 34 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 378e5f76af..aac976ffff 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -47,6 +47,13 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, case AXP2101_registers_e::batpresent: return displayString ? F("BatPresent") : F("batpresent"); case AXP2101_registers_e::chipid: return displayString ? F("ChipID") : F("chipid"); case AXP2101_registers_e::chargedet: return displayString ? F("ChargingDetail") : F("chargingdet"); + + case AXP2101_registers_e::vbat: return displayString ? F("BatVoltage") : F("vbat"); + case AXP2101_registers_e::battemp: return displayString ? F("BatTemp") : F("battemp"); + case AXP2101_registers_e::vbus: return displayString ? F("BusVoltage") : F("vbus"); + case AXP2101_registers_e::vsys: return displayString ? F("SysVoltage") : F("vsys"); + case AXP2101_registers_e::chiptemp: return displayString ? F("ChipTemp") : F("chiptemp"); + } return F(""); } @@ -123,6 +130,12 @@ AXP2101_registers_e AXP2101_intToRegister(int reg) { case 17: return AXP2101_registers_e::batpresent; case 18: return AXP2101_registers_e::chipid; case 19: return AXP2101_registers_e::chargedet; + + case 20: return AXP2101_registers_e::vbat; + case 21: return AXP2101_registers_e::battemp; + case 22: return AXP2101_registers_e::vbus; + case 23: return AXP2101_registers_e::vsys; + case 24: return AXP2101_registers_e::chiptemp; } return AXP2101_registers_e::dcdc1; // we shouldn't get here, just defaulting to the first value } @@ -151,6 +164,12 @@ uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::batpresent: case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: break; } return 0u; @@ -180,6 +199,12 @@ uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { case AXP2101_registers_e::batpresent: case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: break; } return 0u; @@ -254,6 +279,11 @@ void AXP2101_settings::setVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::batpresent: case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: break; } } @@ -283,6 +313,11 @@ int AXP2101_settings::getVoltage(AXP2101_registers_e reg, case AXP2101_registers_e::batpresent: case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: return 0; } return 0xFFFFF == result ? (realValue ? 0 : -1) : result; @@ -313,6 +348,11 @@ void AXP2101_settings::setState(AXP2101_registers_e reg, case AXP2101_registers_e::batpresent: case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: break; } } @@ -340,6 +380,12 @@ AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: return AXP_pin_s::Protected; + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; } return AXP_pin_s::Default; } @@ -545,6 +591,14 @@ uint8_t AXP2101::voltageToRegister(uint16_t voltage, case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: break; + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } return 0u; } @@ -602,10 +656,71 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, case AXP2101_registers_e::chipid: case AXP2101_registers_e::chargedet: break; + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; } return 0u; } +// Values in mVolt, raw ADC data is expressed in 0.5 mV +constexpr uint16_t axp2101_TS_LUT[] { + 3150, // -20 + 2508, // -15 + 2013, // -10 + 1628, // -5 + 1325, // 0 + 1084, // 5 + 889, // 10 + 732, // 15 + 604, // 20 + 500, // 25 + 416, // 30 + 348, // 35 + 292, // 40 + 246, // 45 + 209, // 50 + 177, // 55 + 152}; // 60 + +uint16_t AXP2101::TS_tempToRegister(float temp_C) +{ + constexpr int NR_LUTS = sizeof(axp2101_TS_LUT) / sizeof(axp2101_TS_LUT[0]); + const int index_lo = (temp_C + 20) / 5; + const int index_hi = index_lo + 1; + if (index_lo < 0) return 0xFFFF; + if (index_hi >= NR_LUTS) return 0; + + const int mod5 = (static_cast(temp_C)) % 5; + const int offset = ((axp2101_TS_LUT[index_hi] - axp2101_TS_LUT[index_lo]) * mod5) / 5; + + // Need to apply a factor 2, to convert from mV to regvalue. + return 2* (axp2101_TS_LUT[index_lo] + offset); +} + +float AXP2101::TS_registerToTemp(uint16_t regValue) +{ + regValue /= 2; // Convert from regvalue to mV as used in LUT + constexpr int NR_LUTS = sizeof(axp2101_TS_LUT) / sizeof(axp2101_TS_LUT[0]); + if (regValue > axp2101_TS_LUT[0]) return -20.0f; + for (int index_hi = 1; index_hi < NR_LUTS; ++index_hi) { + if (regValue > axp2101_TS_LUT[index_hi]) { + const int index_lo = index_hi - 1; + const int step_lo = axp2101_TS_LUT[index_lo] - regValue; + const int step_hi = regValue - axp2101_TS_LUT[index_hi]; + float avg = axp2101_TS_LUT[index_hi] * step_hi + axp2101_TS_LUT[index_lo] * step_lo; + avg /= (step_hi + step_lo); + return avg; + } + } + + return 60.0f; +} + /** * Set a voltage to a port (output pin) of the AXP2101 */ @@ -662,6 +777,48 @@ bool AXP2101::getPortState(AXP2101_registers_e reg) { return result; } +bool AXP2101::enableADC(AXP2101_registers_e reg, bool enable) +{ + uint8_t ctrl = 0; + uint8_t mask = 0; + getControlRegisterMask(reg, ctrl, mask); + + if (ctrl != AXP2101_ADC_ENABLE_REG) { + return false; + } + + uint8_t val = readRegister8(AXP2101_ADDR, AXP2101_ADC_ENABLE_REG); + + const bool bit_set = ((val & mask) != 0); + if (bit_set != enable) { + + if (enable) { + val |= mask; + } else { + val &= ~mask; + } + writeRegister8(AXP2101_ADDR, AXP2101_ADC_ENABLE_REG, val); + } + return true; +} + +uint16_t AXP2101::getADCVoltage(AXP2101_registers_e reg) +{ + if (!enableADC(reg, true)) return 0; + if (reg == AXP2101_registers_e::vbus && !isVbusIn()) { + return 0; + } + if (reg == AXP2101_registers_e::vbat && !isBatteryDetected()) { + return 0; + } + + const uint16_t mask = reg == AXP2101_registers_e::vbat ? 0x1F : 0x3F; + + const uint16_t hi = readRegister8(AXP2101_ADDR, static_cast(reg)); + const uint16_t lo = readRegister8(AXP2101_ADDR, static_cast(reg) + 1); + return ((hi & mask) << 8) | lo; +} + /** * Compound functions, device model dependent */ @@ -781,6 +938,15 @@ bool AXP2101::isBatteryDetected() { return (readRegister8(_addr, AXP2101_COM_STAT0_REG) >> 3) & 0x01; } +bool AXP2101::isVbusGood() { + return bitGet(AXP2101_COM_STAT0_REG, (1 << 5)); +} + + +bool AXP2101::isVbusIn() { + return !bitGet(AXP2101_COM_STAT1_REG, (1 << 3)) && isVbusGood(); +} + AXP2101_chargingDetail_e AXP2101::getChargingDetail() { return static_cast(readRegister8(_addr, AXP2101_COM_STAT1_REG) & 0x07); } @@ -912,6 +1078,28 @@ void AXP2101::getControlRegisterMask(AXP2101_registers_e reg, ctrl = AXP2101_COM_STAT1_REG; mask = 0b00000111; break; + + + case AXP2101_registers_e::vbat: + ctrl = AXP2101_ADC_ENABLE_REG; + mask = AXP2101_VBAT_CTRL_MASK; + break; + case AXP2101_registers_e::battemp: + ctrl = AXP2101_ADC_ENABLE_REG; + mask = AXP2101_BATTEMP_CTRL_MASK; + break; + case AXP2101_registers_e::vbus: + ctrl = AXP2101_ADC_ENABLE_REG; + mask = AXP2101_VBUS_CTRL_MASK; + break; + case AXP2101_registers_e::vsys: + ctrl = AXP2101_ADC_ENABLE_REG; + mask = AXP2101_VSYS_CTRL_MASK; + break; + case AXP2101_registers_e::chiptemp: + ctrl = AXP2101_ADC_ENABLE_REG; + mask = AXP2101_TDIE_CTRL_MASK; + break; } } diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index bcc736f0bf..ea181656c9 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -43,6 +43,35 @@ #define AXP2101_DLDO2_VOLTAGE_REG (0x9A) #define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) +// Measure V battery +#define AXP2101_VBAT_H_ADC_REG (0x34) +#define AXP2101_VBAT_L_ADC_REG (0x35) + +// Measure (optional) temperature sensor +#define AXP2101_TS_H_ADC_REG (0x36) +#define AXP2101_TS_L_ADC_REG (0x37) + +// Measure Vbus +#define AXP2101_VBUS_H_ADC_REG (0x38) +#define AXP2101_VBUS_L_ADC_REG (0x39) + +// Measure Vsys +#define AXP2101_VSYS_H_ADC_REG (0x3A) +#define AXP2101_VSYS_L_ADC_REG (0x3B) + +// Measure chip die temperature +#define AXP2101_TDIE_H_ADC_REG (0x3C) +#define AXP2101_TDIE_L_ADC_REG (0x3D) + + +#define AXP2101_VBAT_CTRL_MASK (1 << 0) +#define AXP2101_BATTEMP_CTRL_MASK (1 << 1) +#define AXP2101_VBUS_CTRL_MASK (1 << 2) +#define AXP2101_VSYS_CTRL_MASK (1 << 3) +#define AXP2101_TDIE_CTRL_MASK (1 << 4) +// What to do with bit 5: "general purpose ADC channel enable"? + + #define AXP2101_COM_STAT0_REG (0x00) #define AXP2101_COM_STAT1_REG (0x01) #define AXP2101_CHIP_ID_REG (0x03) @@ -132,6 +161,13 @@ enum class AXP2101_registers_e : uint8_t { dldo2 = AXP2101_DLDO2_VOLTAGE_REG, cpuldos = AXP2101_CPUSLDO_VOLTAGE_REG, + // ADC inputs + vbat = AXP2101_VBAT_H_ADC_REG, + battemp = AXP2101_TS_H_ADC_REG, + vbus = AXP2101_VBUS_H_ADC_REG, + vsys = AXP2101_VSYS_H_ADC_REG, + chiptemp = AXP2101_TDIE_H_ADC_REG, + // Above are settable pinstates/voltages of the AXP2101 // Below are non-voltage and read-only values of the AXP2101, also update AXP2101_register_count when adding values chargeled = AXP2101_CHGLED_REG, @@ -142,7 +178,7 @@ enum class AXP2101_registers_e : uint8_t { chargedet = AXP2101_CHARGE_DET_REG, }; constexpr int AXP2101_settings_count = 14; // Changeable settings -constexpr int AXP2101_register_count = 20; // All registers +constexpr int AXP2101_register_count = 25; // All registers enum class AXP_pin_s : uint8_t { Off = 0x00, // Max. 3 bits can be stored in settings! @@ -336,6 +372,11 @@ class AXP2101 { AXP2101_registers_e reg); uint16_t registerToVoltage(uint8_t data, AXP2101_registers_e reg); + + // Convertion between NTC temperature sensor raw value and temperature + uint16_t TS_tempToRegister(float temp_C); + float TS_registerToTemp(uint16_t regValue); + uint8_t get_dcdc_status(void); bool setPortVoltage(uint16_t voltage, AXP2101_registers_e reg); @@ -344,11 +385,16 @@ class AXP2101 { AXP2101_registers_e reg); bool getPortState(AXP2101_registers_e reg); + bool enableADC(AXP2101_registers_e reg, bool enable); + uint16_t getADCVoltage(AXP2101_registers_e reg); + bool setChargeLed(AXP2101_chargeled_d led); AXP2101_chargeled_d getChargeLed(); uint8_t getBatCharge(); AXP2101_chargingState_e getChargingState(); bool isBatteryDetected(); + bool isVbusGood(); + bool isVbusIn(); AXP2101_chargingDetail_e getChargingDetail(); uint8_t getChipIDRaw(); AXP2101_chipid_e getChipID(); diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index caaa4126f3..0bece6a732 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -113,10 +113,14 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) if (i < P139_NR_OUTPUT_VALUES) { const uint8_t choice = PCONFIG(P139_CONFIG_BASE + i); ExtraTaskSettings.setTaskDeviceValueName(i, toString(static_cast(choice), false)); + if (choice != (static_cast(AXP2101_registers_e::battemp)) && + choice != (static_cast(AXP2101_registers_e::chiptemp))) { + ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; + } } else { ExtraTaskSettings.clearTaskDeviceValueName(i); + ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; } - ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; // No values have decimals } break; } diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index b1acb3ebd0..3a4ca77234 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -124,25 +124,35 @@ bool P139_data_struct::plugin_read(struct EventStruct *event) { // **************************************************************************/ float P139_data_struct::read_value(AXP2101_registers_e value) { if (isInitialized()) { - if (AXP2101_registers_e::chargeled == value) { + switch (value) + { + case AXP2101_registers_e::chargeled: return static_cast(axp2101->getChargeLed()); - } else - if (AXP2101_registers_e::batcharge == value) { + case AXP2101_registers_e::batcharge: return static_cast(axp2101->getBatCharge()); - } else - if (AXP2101_registers_e::charging == value) { + case AXP2101_registers_e::charging: return static_cast(axp2101->getChargingState()); - } else - if (AXP2101_registers_e::batpresent == value) { + case AXP2101_registers_e::batpresent: return static_cast(axp2101->isBatteryDetected()); - } else - if (AXP2101_registers_e::chipid == value) { + case AXP2101_registers_e::chipid: return static_cast(axp2101->getChipIDRaw()); - } else - if (AXP2101_registers_e::chargedet == value) { + case AXP2101_registers_e::chargedet: return static_cast(axp2101->getChargingDetail()); + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + return static_cast(axp2101->getADCVoltage(value)); + + case AXP2101_registers_e::battemp: + return axp2101->TS_registerToTemp(axp2101->getADCVoltage(value)); + + case AXP2101_registers_e::chiptemp: + return (22.0f + (7274 - axp2101->getADCVoltage(value)) / 20.0f); + + default: + return static_cast(axp2101->getPortVoltage(value)); } - return static_cast(axp2101->getPortVoltage(value)); } return 0.0f; } @@ -397,27 +407,12 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, const AXP2101_registers_e reg = AXP2101_intToRegister(r); if (equals(command, toString(reg, false))) { // Voltage (mV) / numeric state - if (r >= AXP2101_settings_count) { - if (AXP2101_registers_e::chargeled == reg) { - string = static_cast(axp2101->getChargeLed()); - } else - if (AXP2101_registers_e::batcharge == reg) { - string = axp2101->getBatCharge(); - } else - if (AXP2101_registers_e::charging == reg) { - string = static_cast(axp2101->getChargingState()); - } else - if (AXP2101_registers_e::batpresent == reg) { - string = axp2101->isBatteryDetected(); - } else - if (AXP2101_registers_e::chipid == reg) { - string = axp2101->getChipIDRaw(); - } else - if (AXP2101_registers_e::chargedet == reg) { - string = static_cast(axp2101->getChargingDetail()); - } + if (reg == AXP2101_registers_e::battemp || + reg == AXP2101_registers_e::chiptemp) + { + string = floatToString(read_value(reg), 2); } else { - string = axp2101->getPortVoltage(reg); + string = read_value(reg); } success = true; } else From 5ad7b41907174ea785b2b3f8d84c98d491228759 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 20 Jan 2025 22:43:54 +0100 Subject: [PATCH 17/50] [P139] Documentation updates, updated TTGO T-Pcie configuration defaults --- docs/source/Plugin/P139.rst | 15 ++++++++++- docs/source/Plugin/P139_ValueOptionsB.png | Bin 0 -> 29399 bytes docs/source/Plugin/P139_values.repl | 31 +++++++++++++++++++--- lib/AXP2101/src/AXP2101.cpp | 4 +-- 4 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 docs/source/Plugin/P139_ValueOptionsB.png diff --git a/docs/source/Plugin/P139.rst b/docs/source/Plugin/P139.rst index f166c8f1f1..2136fccd57 100644 --- a/docs/source/Plugin/P139.rst +++ b/docs/source/Plugin/P139.rst @@ -114,6 +114,8 @@ Available options: *Single* (1), *Dual* (2), *Triple* (3) and *Quad* (4). .. image:: P139_ValueOptions.png +.. image:: P139_ValueOptionsB.png + Available options: * *None*: To leave the value empty/0.00 @@ -122,7 +124,7 @@ Available options: * *ChargeLed*: The state of the Charge LED, 0 = Off, 1 = Flash 1Hz, 2 = Flash 4Hz, 3 = Steady On. -* *BatCharge*: The current that the battery is being charged or discharged with. +* *BatCharge*: The charge state percentage of the battery. * *ChargingState*: The state of charging, -1 = discharging, 0 = standby, 1 = charging. @@ -139,6 +141,17 @@ Available options: * 4: ``charge done`` * 5: ``not charging`` +* *BatVoltage*: The battery voltage. + +* *BatTemp*: The temperature of the battery, (if a battery-temperature sensor is installed). + +* *BusVoltage*: The bus-voltage. + +* *SysVoltage*: The system-voltage. + +* *ChipTemp*: The internal temperature of the chip. + + .. note:: Not all options hold usable values for all boards, some may even be not connected. Check the board documentation for available values. Data Acquisition diff --git a/docs/source/Plugin/P139_ValueOptionsB.png b/docs/source/Plugin/P139_ValueOptionsB.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8a3a4c8a4f3953d25cc7d1e849ed31f550b035 GIT binary patch literal 29399 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 IPmBZqA8Yh4(f|Me literal 0 HcmV?d00001 diff --git a/docs/source/Plugin/P139_values.repl b/docs/source/Plugin/P139_values.repl index b3c4290670..b9ff876efe 100644 --- a/docs/source/Plugin/P139_values.repl +++ b/docs/source/Plugin/P139_values.repl @@ -3,9 +3,9 @@ :widths: 20, 30 " - Every value option can be appended with ``.status`` (or ``.state`` for ``BatCharge`` and any of the ports) + Most value options can be appended with ``.status`` (or ``.state`` for ``BatCharge`` and any of the ports) "," - | ``.status``: A text representation of the value will be returned. + | ``.status``: A text representation of the value will be returned, where applicable. | ``.state``: A numeric representation of the value will be returned (usually the same as if the plain value name was used.) | Example: ``[#ChargingDetail.status]`` will return ``constant charge (CC)`` for ``ChargingDetail`` = 2. @@ -23,7 +23,7 @@ " ``[#BatCharge]`` "," - | The current that the battery is being charged or discharged with. + | The charge state percentage of the battery. " " ``[#ChargingState]`` @@ -52,3 +52,28 @@ * 4: ``charge done`` * 5: ``not charging`` " + " + ``[#BatVoltage]`` + "," + | The current voltage of the battery. + " + " + ``[#BatTemp]`` + "," + | The temperature of the battery, (if a battery-temperature sensor is installed). + " + " + ``[#VBus]`` + "," + | The bus-voltage. + " + " + ``[#VSys]`` + "," + | The charge state percentage of the battery. + " + " + ``[#ChipTemp]`` + "," + | The internal temperature of the chip. + " diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index aac976ffff..6c5b8e6f15 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -404,13 +404,13 @@ AXP2101_chargeled_d AXP2101_settings::getChargeLed() { // *INDENT-OFF* AXP2101_settings AXP2101_deviceSettingsArray[] = -{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | aldo3 | aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos | chargeled +{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | en_aldo3 | en_aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos | chargeled /* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, /* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, /* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, /* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, /* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* LilyGo TPCie v1.2 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TPCie v1.2 */ { 3300, 900, 900, 1100, 1200, 1800, 2800, 3300, 2900, 1800, 2800, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, /* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, }; // *INDENT-ON* From ae331616f6d3b9005f1a09116d363e415904d211 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 20 Jan 2025 23:17:37 +0100 Subject: [PATCH 18/50] [P139] Fix configuration constructor typo --- lib/AXP2101/src/AXP2101.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 6c5b8e6f15..d068e66279 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -242,7 +242,7 @@ AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _d { registers.dcdc1 = _dcdc1; registers.dcdc2 = _dcdc2; registers.dcdc3 = _dcdc3; registers.dcdc4 = _dcdc4; registers.dcdc5 = _dcdc5; registers.aldo1 = _aldo1; registers.aldo2 = _aldo2; registers.aldo3 = _aldo3; registers.aldo4 = _aldo4; - registers.bldo1 = _bldo1; registers.bldo1 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; + registers.bldo1 = _bldo1; registers.bldo2 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); From 5b127509a63fdde9edba3adb0acaab6b45e4b4a1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 21 Jan 2025 13:39:34 +0100 Subject: [PATCH 19/50] [P139] Add charger settings --- lib/AXP2101/src/AXP2101.cpp | 108 ++++++++++++++++++ lib/AXP2101/src/AXP2101.h | 123 ++++++++++++++++++++- src/_P139_AXP2101.ino | 8 ++ src/src/PluginStructs/P139_data_struct.cpp | 3 + 4 files changed, 241 insertions(+), 1 deletion(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index d068e66279..37309abf6d 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -398,6 +398,77 @@ AXP2101_chargeled_d AXP2101_settings::getChargeLed() { return static_cast(pinStates.chargeled); } +bool AXP2101_settings::getTS_disabled() { + return pinStates.dis_TS_pin; +} + +void AXP2101_settings::setTS_disabled(bool val) { + pinStates.dis_TS_pin = val; +} + + +uint16_t AXP2101_settings::getPreChargeCurrentLimit() const { + return chargeStates.pre_chg_cur * 25; +} + +void AXP2101_settings::setPreChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 200) { + current_mA = 200; + } + chargeStates.pre_chg_cur = current_mA / 25; +} + +uint16_t AXP2101_settings::getConstChargeCurrentLimit() const { + if (chargeStates.const_cur_lim <= 8) { + return chargeStates.const_cur_lim * 25; + } + return (chargeStates.const_cur_lim - 8) * 100 + 200; +} + +void AXP2101_settings::setConstChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 1000) { + current_mA = 1000; + } + if (current_mA <= 200) { + chargeStates.const_cur_lim = current_mA / 25; + } else { + chargeStates.const_cur_lim = ((current_mA - 200) / 100) + 8; + } +} + +uint16_t AXP2101_settings::getTerminationChargeCurrentLimit() const { + return chargeStates.term_cur_lim * 25; +} + +void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 200) { + current_mA = 200; + } + chargeStates.term_cur_lim = current_mA / 25; +} + + +AXP2101_CV_charger_voltage_e AXP2101_settings::getCV_chargeVoltage() const { + return static_cast(chargeStates.chg_volt_lim); +} + +void AXP2101_settings::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { + chargeStates.chg_volt_lim = static_cast(voltage_mV); + if (chargeStates.chg_volt_lim > static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) { + // Set to a default safe limit + chargeStates.chg_volt_lim = static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V); + } +} + +AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_settings::getCV_chargerVoltage() const { + return static_cast(chargeStates.min_sys_voltage); +} + +void AXP2101_settings::setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { + chargeStates.min_sys_voltage = static_cast(voltage); +} + + /** * AXP2101 device class */ @@ -924,6 +995,15 @@ AXP2101_chargeled_d AXP2101::getChargeLed() { return static_cast((readRegister8(_addr, AXP2101_CHGLED_REG) >> 4) & 0x07); } +bool AXP2101::getTS_disabled() { + return bitGet(AXP2101_TS_PIN_CTRL, 0b00010000); +} + +void AXP2101::setTS_disabled(bool val) { + bitOnOff(val, AXP2101_TS_PIN_CTRL, 0b00010000); +} + + uint8_t AXP2101::getBatCharge() { return readRegister8(_addr, AXP2101_BAT_CHARGE_REG); } @@ -967,6 +1047,26 @@ bool AXP2101::set_charger_constant_current_to_50mA(void) { return writeRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG, 2); } +bool AXP2101::set_charger_constant_current(uint16_t current_mA) +{ + if (current_mA > 1000) { + current_mA = 1000; + } + const uint8_t regValue = (current_mA <= 200) + ? current_mA / 25 + : ((current_mA - 200) / 100) + 8; + return writeRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG, regValue); +} + +uint16_t AXP2101::get_charger_constant_current() +{ + const uint8_t regValue = readRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG); + if (regValue <= 8) { + return regValue * 25; + } + return 200 * (100 * (regValue - 8)); +} + void AXP2101::set_bat_charge(bool enable) { uint8_t val = 0; @@ -981,6 +1081,14 @@ bool AXP2101::enable_pwrok_resets(void) { 1 << 3); } +void AXP2101::set_IRQ_enable_0(uint8_t val) { + // Clear any IRQ flags + writeRegister8(AXP2101_ADDR, AXP2101_IRQ_STATUS_0_REG, 0); + writeRegister8(AXP2101_ADDR, AXP2101_IRQ_STATUS_1_REG, 0); + writeRegister8(AXP2101_ADDR, AXP2101_IRQ_STATUS_2_REG, 0); + writeRegister8(AXP2101_ADDR, AXP2101_IRQ_EN_0_REG, val); +} + void AXP2101::power_off(void) { // 1. AXP2101 Power off bitOn(AXP2101_ADDR, AXP2101_IRQ_EN_1_REG, 1 << 1); // POWERON Negative Edge IRQ(ponne_irq_en) enable diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index ea181656c9..efa8d6f453 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -80,7 +80,13 @@ #define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) #define AXP2101_PWROK_PWROFF_REG (0x25) #define AXP2101_ADC_ENABLE_REG (0x30) +#define AXP2101_IRQ_EN_0_REG (0x40) #define AXP2101_IRQ_EN_1_REG (0x41) +#define AXP2101_IRQ_EN_2_REG (0x42) +#define AXP2101_IRQ_STATUS_0_REG (0x48) +#define AXP2101_IRQ_STATUS_1_REG (0x49) +#define AXP2101_IRQ_STATUS_2_REG (0x4A) +#define AXP2101_TS_PIN_CTRL (0x50) #define AXP2101_ICC_CHARGER_SETTING_REG (0x62) #define AXP2101_CHARGER_SETTING_REG (0x63) #define AXP2101_CHGLED_REG (0x69) @@ -215,6 +221,55 @@ enum class AXP2101_chargingDetail_e : uint8_t { notcharging = 0b101, }; +enum class AXP2101_CV_charger_voltage_e : uint8_t { + reserved = 0, + limit_4_00V = 0b001, + limit_4_10V = 0b010, + limit_4_20V = 0b011, // default + limit_4_35V = 0b100, + limit_4_40V = 0b101 +}; + +enum class AXP2101_Linear_Charger_Vsys_dpm_e : uint8_t { + vsys_4_1V = 0, + vsys_4_2V, + vsys_4_3V, + vsys_4_4V, + vsys_4_5V, + vsys_4_6V, + vsys_4_7V, + vsys_4_8V +}; + +enum class AXP2101_VINDPM_e : uint8_t { + Vin_3_88V = 0, + Vin_3_96V, + Vin_4_04V, + Vin_4_12V, + Vin_4_20V, + Vin_4_28V, + Vin_4_36V, + Vin_4_44V, + Vin_4_52V, + Vin_4_60V, + Vin_4_68V, + Vin_4_76V, + Vin_4_84V, + Vin_4_92V, + Vin_5_00V, + Vin_5_08V +}; + +enum class AXP2101_InputCurrentLimit_e : uint8_t { + limit_100mA = 0, + limit_500mA, + limit_900mA, + limit_1000mA, + limit_1500mA, + limit_2000mA +}; + + AXP2101_registers_e AXP2101_intToRegister(int reg); uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); @@ -274,6 +329,35 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the void setChargeLed(AXP2101_chargeled_d led); AXP2101_chargeled_d getChargeLed(); + bool getTS_disabled(); + void setTS_disabled(bool val); + + + // Reg 61: Iprechg Charger Settings + uint16_t getPreChargeCurrentLimit() const; + void setPreChargeCurrentLimit(uint16_t current_mA); + + // Reg 62: ICC Charger Settings + uint16_t getConstChargeCurrentLimit() const; + void setConstChargeCurrentLimit(uint16_t current_mA); + + // Reg 63: Iterm Charger Settings and Control + // Enable/Disable via chargeStates.term_cur_lim_en + uint16_t getTerminationChargeCurrentLimit() const; + void setTerminationChargeCurrentLimit(uint16_t current_mA); + + // Reg 64: CV Charger Voltage Settings + AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; + void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); + + // Reg 14: Minimum System Voltage Control + AXP2101_Linear_Charger_Vsys_dpm_e getCV_chargerVoltage() const; + void setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage); + + + + + private: union { @@ -312,10 +396,38 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the uint64_t en_dldo2 : 3; // bit 36/37/38 uint64_t en_cpuldos : 3; // bit 39/40/41 uint64_t chargeled : 3; // bit 42/43/44 - uint64_t en_unused : 18; // bit 45..63 // All bits defined + + // Settings for external temperature sensor (TS) + uint64_t dis_TS_pin : 1; // bit 45, reg50 bit 4 + uint64_t TS_cur_src : 2; // bit 46/47, reg50 bit 3:2 + uint64_t TS_current : 2; // bit 48/49, reg50 bit 1:0 + + uint64_t en_unused : 13; // bit 50..63 // All bits defined } pinStates; uint64_t pinStates_{}; // 8 bytes }; + + union { + struct { + uint64_t pre_chg_cur : 4; // reg 0x61: 25* N mA + uint64_t const_cur_lim : 5; // reg 0x62: 25* N mA if N <= 8, 200+100*(N-8) mA if N > 8 + uint64_t term_cur_lim_en : 1; // reg 0x63: Charging termination of current enable + uint64_t term_cur_lim : 4; // reg 0x63: 25* N mA + uint64_t chg_volt_lim : 3; // reg 0x64: + uint64_t thermal_thresh : 2; // reg 0x65: 00: 60deg, 01: 80deg, 10: 100deg, 11:120deg + uint64_t chg_timeout_ctrl: 8; // reg 0x67: + uint64_t bat_detection : 1; // reg 0x68: + uint64_t coincell_term_volt : 3; // reg 0x6A: 2.6~3.3V, 100mV/step, 8 steps + + uint64_t min_sys_voltage : 3; // reg 0x14: 4.1 + N*0.1V Linear Charger Vsys Voltage dpm + uint64_t inp_volt_limit : 4; // reg 0x15: Vindpm 3.88+N*0.08V + uint64_t inp_cur_limit : 3; // reg 0x16: + + + uint64_t en_unused : 23; + } chargeStates; + uint64_t chargeStates_{}; // 8 bytes + }; }; extern AXP2101_settings AXP2101_deviceSettingsArray[]; @@ -390,6 +502,11 @@ class AXP2101 { bool setChargeLed(AXP2101_chargeled_d led); AXP2101_chargeled_d getChargeLed(); + + bool getTS_disabled(); + void setTS_disabled(bool val); + + uint8_t getBatCharge(); AXP2101_chargingState_e getChargingState(); bool isBatteryDetected(); @@ -412,8 +529,12 @@ class AXP2101 { void power_off(void); bool set_charger_term_current_to_zero(void); bool set_charger_constant_current_to_50mA(void); + bool set_charger_constant_current(uint16_t current_mA); + uint16_t get_charger_constant_current(); bool enable_pwrok_resets(void); + void set_IRQ_enable_0(uint8_t val); + // Low-level output functions bool set_dcdc1_voltage(uint16_t voltage) { // 1.5 - 3.4V 100mV/step, 20 steps diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 0bece6a732..f62dfef315 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -214,6 +214,13 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) static_cast(P139_data->_settings.getChargeLed())); } + { + addFormCheckBox(F("Disable TS pin"), F("dis_TS"), P139_data->_settings.getTS_disabled()); + + //addFormNumericBox(F("Charge Current")) + + } + addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); addFormSubHeader(F("Hardware outputs AXP2101")); @@ -383,6 +390,7 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } P139_data->_settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + P139_data->_settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); P139_data->saveSettings(event); diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 3a4ca77234..2f9894b85d 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -74,6 +74,9 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set %d values to port(s)"), count)); } + axp2101->setTS_disabled(_settings.getTS_disabled()); +// axp2101->set_IRQ_enable_0(0b11110000); // Disable temperature checks + axp2101->set_charger_constant_current(800); } // **************************************************************************/ From 015ead30cd09a7de54cb2d5f2ab1646414ce1c09 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 21 Jan 2025 15:38:00 +0100 Subject: [PATCH 20/50] [P139] Split AXP2101_settings to separate file --- lib/AXP2101/src/AXP2101.cpp | 480 -------------------------- lib/AXP2101/src/AXP2101.h | 411 +--------------------- lib/AXP2101/src/AXP2101_settings.cpp | 491 +++++++++++++++++++++++++++ lib/AXP2101/src/AXP2101_settings.h | 436 ++++++++++++++++++++++++ 4 files changed, 928 insertions(+), 890 deletions(-) create mode 100644 lib/AXP2101/src/AXP2101_settings.cpp create mode 100644 lib/AXP2101/src/AXP2101_settings.h diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 37309abf6d..bd7aecf7c8 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -6,486 +6,6 @@ # pragma GCC diagnostic push # pragma GCC diagnostic warning "-Wswitch-enum" -/** - * Utility functions - */ -const __FlashStringHelper* toString(AXP2101_device_model_e device, - bool displayString) { - switch (device) { - case AXP2101_device_model_e::Unselected: return displayString ? F("Select an option to set default values") : F("Unselected"); - case AXP2101_device_model_e::M5Stack_Core2_v1_1: return displayString ? F("M5Stack Core2 v1.1") : F("M5Core2v11"); - case AXP2101_device_model_e::M5Stack_CoreS3: return displayString ? F("M5Stack CoreS3") : F("M5CoreS3"); - case AXP2101_device_model_e::LilyGO_TBeam_v1_2: return displayString ? F("LilyGo TBeam v1.2") : F("TBeamv12"); - case AXP2101_device_model_e::LilyGO_TBeamS3_v3: return displayString ? F("LilyGo TBeam S3 v3") : F("TBeamS3v3"); - case AXP2101_device_model_e::LilyGO_TPCie_v1_2: return displayString ? F("LilyGo TPCie v1.2") : F("TPCiev12"); - case AXP2101_device_model_e::UserDefined: return displayString ? F("User defined") : F("Userdefined"); - case AXP2101_device_model_e::MAX: break; - } - return F(""); -} - -const __FlashStringHelper* toString(AXP2101_registers_e reg, - bool displayString) { - switch (reg) { - case AXP2101_registers_e::dcdc1: return displayString ? F("DCDC1") : F("dcdc1"); - case AXP2101_registers_e::dcdc2: return displayString ? F("DCDC2") : F("dcdc2"); - case AXP2101_registers_e::dcdc3: return displayString ? F("DCDC3") : F("dcdc3"); - case AXP2101_registers_e::dcdc4: return displayString ? F("DCDC4") : F("dcdc4"); - case AXP2101_registers_e::dcdc5: return displayString ? F("DCDC5") : F("dcdc5"); - case AXP2101_registers_e::aldo1: return displayString ? F("ALDO1") : F("aldo1"); - case AXP2101_registers_e::aldo2: return displayString ? F("ALDO2") : F("aldo2"); - case AXP2101_registers_e::aldo3: return displayString ? F("ALDO3") : F("aldo3"); - case AXP2101_registers_e::aldo4: return displayString ? F("ALDO4") : F("aldo4"); - case AXP2101_registers_e::bldo1: return displayString ? F("BLDO1") : F("bldo1"); - case AXP2101_registers_e::bldo2: return displayString ? F("BLDO2") : F("bldo2"); - case AXP2101_registers_e::dldo1: return displayString ? F("DLDO1") : F("dldo1"); - case AXP2101_registers_e::dldo2: return displayString ? F("DLDO2") : F("dldo2"); - case AXP2101_registers_e::cpuldos: return displayString ? F("CPULDOS") : F("cpuldos"); - case AXP2101_registers_e::chargeled: return displayString ? F("ChargeLed") : F("chargeled"); - case AXP2101_registers_e::batcharge: return displayString ? F("BatCharge") : F("batcharge"); - case AXP2101_registers_e::charging: return displayString ? F("ChargingState") : F("chargingstate"); - case AXP2101_registers_e::batpresent: return displayString ? F("BatPresent") : F("batpresent"); - case AXP2101_registers_e::chipid: return displayString ? F("ChipID") : F("chipid"); - case AXP2101_registers_e::chargedet: return displayString ? F("ChargingDetail") : F("chargingdet"); - - case AXP2101_registers_e::vbat: return displayString ? F("BatVoltage") : F("vbat"); - case AXP2101_registers_e::battemp: return displayString ? F("BatTemp") : F("battemp"); - case AXP2101_registers_e::vbus: return displayString ? F("BusVoltage") : F("vbus"); - case AXP2101_registers_e::vsys: return displayString ? F("SysVoltage") : F("vsys"); - case AXP2101_registers_e::chiptemp: return displayString ? F("ChipTemp") : F("chiptemp"); - - } - return F(""); -} - -const __FlashStringHelper* toString(AXP_pin_s pin) { - switch (pin) { - case AXP_pin_s::Off: return F("Off"); - case AXP_pin_s::On: return F("On"); - case AXP_pin_s::Default: return F("Default"); - case AXP_pin_s::Disabled: return F("Disabled"); - case AXP_pin_s::Protected: return F("Protected"); - } - return F(""); -} - -const __FlashStringHelper* toString(AXP2101_chargeled_d led) { - switch (led) { - case AXP2101_chargeled_d::Off: return F("Off"); - case AXP2101_chargeled_d::Flash_1Hz: return F("Flash 1Hz"); - case AXP2101_chargeled_d::Flash_4Hz: return F("Flash 4Hz"); - case AXP2101_chargeled_d::Steady_On: return F("Steady On"); - case AXP2101_chargeled_d::Protected: return F("Protected"); - } - return F(""); -} - -const __FlashStringHelper* toString(AXP2101_chargingState_e state) { - switch (state) { - case AXP2101_chargingState_e::Discharging: return F("Discharging"); - case AXP2101_chargingState_e::Standby: return F("Standby"); - case AXP2101_chargingState_e::Charging: return F("Charging"); - } - return F(""); -} - -const __FlashStringHelper* toString(AXP2101_chipid_e chip) { - switch (chip) { - case AXP2101_chipid_e::axp2101: return F("AXP2101"); - } - return F(""); -} - -const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge) { - switch (charge) { - case AXP2101_chargingDetail_e::tricharge: return F("tri-charge"); - case AXP2101_chargingDetail_e::precharge: return F("pre-charge"); - case AXP2101_chargingDetail_e::constcharge: return F("constant charge (CC)"); - case AXP2101_chargingDetail_e::constvoltage: return F("constant voltage (CV)"); - case AXP2101_chargingDetail_e::done: return F("charge done"); - case AXP2101_chargingDetail_e::notcharging: return F("not charging"); - } - return F(""); -} - -AXP2101_registers_e AXP2101_intToRegister(int reg) { - switch (reg) { - case 0: return AXP2101_registers_e::dcdc1; - case 1: return AXP2101_registers_e::dcdc2; - case 2: return AXP2101_registers_e::dcdc3; - case 3: return AXP2101_registers_e::dcdc4; - case 4: return AXP2101_registers_e::dcdc5; - case 5: return AXP2101_registers_e::aldo1; - case 6: return AXP2101_registers_e::aldo2; - case 7: return AXP2101_registers_e::aldo3; - case 8: return AXP2101_registers_e::aldo4; - case 9: return AXP2101_registers_e::bldo1; - case 10: return AXP2101_registers_e::bldo2; - case 11: return AXP2101_registers_e::dldo1; - case 12: return AXP2101_registers_e::dldo2; - case 13: return AXP2101_registers_e::cpuldos; - case 14: return AXP2101_registers_e::chargeled; - case 15: return AXP2101_registers_e::batcharge; - case 16: return AXP2101_registers_e::charging; - case 17: return AXP2101_registers_e::batpresent; - case 18: return AXP2101_registers_e::chipid; - case 19: return AXP2101_registers_e::chargedet; - - case 20: return AXP2101_registers_e::vbat; - case 21: return AXP2101_registers_e::battemp; - case 22: return AXP2101_registers_e::vbus; - case 23: return AXP2101_registers_e::vsys; - case 24: return AXP2101_registers_e::chiptemp; - } - return AXP2101_registers_e::dcdc1; // we shouldn't get here, just defaulting to the first value -} - -uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { - switch (reg) { - case AXP2101_registers_e::dcdc1: return AXP2101_DCDC1_MIN; - case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MIN; - case AXP2101_registers_e::dcdc2: - case AXP2101_registers_e::dcdc3: - case AXP2101_registers_e::dcdc4: - case AXP2101_registers_e::aldo1: - case AXP2101_registers_e::aldo2: - case AXP2101_registers_e::aldo3: - case AXP2101_registers_e::aldo4: - case AXP2101_registers_e::bldo1: - case AXP2101_registers_e::bldo2: - case AXP2101_registers_e::dldo1: - case AXP2101_registers_e::dldo2: - case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MIN; - - // not a voltage register - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - break; - } - return 0u; -} - -uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { - switch (reg) { - case AXP2101_registers_e::dcdc1: - case AXP2101_registers_e::dcdc3: return AXP2101_DCDC3_MAX; - case AXP2101_registers_e::dcdc2: return AXP2101_DCDC2_MAX; - case AXP2101_registers_e::dcdc4: return AXP2101_DCDC4_MAX; - case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MAX; - case AXP2101_registers_e::aldo1: - case AXP2101_registers_e::aldo2: - case AXP2101_registers_e::aldo3: - case AXP2101_registers_e::aldo4: - case AXP2101_registers_e::bldo1: - case AXP2101_registers_e::bldo2: - case AXP2101_registers_e::dldo1: return AXP2101_DLDO1_MAX; - case AXP2101_registers_e::dldo2: - case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MAX; - - // not a voltage register - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - break; - } - return 0u; -} - -/** - * Is the pin Default, Disabled or Protected, then don't initialize - */ -bool AXP2101_isPinDefault(AXP_pin_s pin) { - return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin || AXP_pin_s::Default == pin; -} - -/** - * Is the pin Disabled or Protected, then don't change - */ -bool AXP2101_isPinProtected(AXP_pin_s pin) { - return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin; -} - -/** - * AXP2101_settings struct - */ - -// constructor -AXP2101_settings::AXP2101_settings() {} - -// constructor -AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _dcdc3, uint16_t _dcdc4, uint16_t _dcdc5, - uint16_t _aldo1, uint16_t _aldo2, uint16_t _aldo3, uint16_t _aldo4, - uint16_t _bldo1, uint16_t _bldo2, uint16_t _dldo1, uint16_t _dldo2, uint16_t _cpuldos, - AXP_pin_s _en_dcdc1, AXP_pin_s _en_dcdc2, AXP_pin_s _en_dcdc3, AXP_pin_s _en_dcdc4, AXP_pin_s _en_dcdc5, - AXP_pin_s _en_aldo1, AXP_pin_s _en_aldo2, AXP_pin_s _en_aldo3, AXP_pin_s _en_aldo4, - AXP_pin_s _en_bldo1, AXP_pin_s _en_bldo2, AXP_pin_s _en_dldo1, AXP_pin_s _en_dldo2, AXP_pin_s _en_cpuldos, - AXP2101_chargeled_d _chargeled) -{ - registers.dcdc1 = _dcdc1; registers.dcdc2 = _dcdc2; registers.dcdc3 = _dcdc3; registers.dcdc4 = _dcdc4; registers.dcdc5 = _dcdc5; - registers.aldo1 = _aldo1; registers.aldo2 = _aldo2; registers.aldo3 = _aldo3; registers.aldo4 = _aldo4; - registers.bldo1 = _bldo1; registers.bldo2 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; - - pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); - pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); - pinStates.en_dcdc5 = static_cast(_en_dcdc5); pinStates.en_aldo1 = static_cast(_en_aldo1); - pinStates.en_aldo2 = static_cast(_en_aldo2); pinStates.en_aldo3 = static_cast(_en_aldo3); - pinStates.en_aldo4 = static_cast(_en_aldo4); pinStates.en_bldo1 = static_cast(_en_bldo1); - pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); - pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); - pinStates.chargeled = static_cast(_chargeled); -} - -void AXP2101_settings::setVoltage(AXP2101_registers_e reg, - int voltage) { - if (-1 == voltage) { voltage = 0xFFFF; } - - switch (reg) { - case AXP2101_registers_e::dcdc1: registers.dcdc1 = voltage; break; - case AXP2101_registers_e::dcdc2: registers.dcdc2 = voltage; break; - case AXP2101_registers_e::dcdc3: registers.dcdc3 = voltage; break; - case AXP2101_registers_e::dcdc4: registers.dcdc4 = voltage; break; - case AXP2101_registers_e::dcdc5: registers.dcdc5 = voltage; break; - case AXP2101_registers_e::aldo1: registers.aldo1 = voltage; break; - case AXP2101_registers_e::aldo2: registers.aldo2 = voltage; break; - case AXP2101_registers_e::aldo3: registers.aldo3 = voltage; break; - case AXP2101_registers_e::aldo4: registers.aldo4 = voltage; break; - case AXP2101_registers_e::bldo1: registers.bldo1 = voltage; break; - case AXP2101_registers_e::bldo2: registers.bldo2 = voltage; break; - case AXP2101_registers_e::dldo1: registers.dldo1 = voltage; break; - case AXP2101_registers_e::dldo2: registers.dldo2 = voltage; break; - case AXP2101_registers_e::cpuldos: registers.cpuldos = voltage; break; - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - break; - } -} - -int AXP2101_settings::getVoltage(AXP2101_registers_e reg, - bool realValue) { - int result = -1; - - switch (reg) { - case AXP2101_registers_e::dcdc1: result = registers.dcdc1; break; - case AXP2101_registers_e::dcdc2: result = registers.dcdc2; break; - case AXP2101_registers_e::dcdc3: result = registers.dcdc3; break; - case AXP2101_registers_e::dcdc4: result = registers.dcdc4; break; - case AXP2101_registers_e::dcdc5: result = registers.dcdc5; break; - case AXP2101_registers_e::aldo1: result = registers.aldo1; break; - case AXP2101_registers_e::aldo2: result = registers.aldo2; break; - case AXP2101_registers_e::aldo3: result = registers.aldo3; break; - case AXP2101_registers_e::aldo4: result = registers.aldo4; break; - case AXP2101_registers_e::bldo1: result = registers.bldo1; break; - case AXP2101_registers_e::bldo2: result = registers.bldo2; break; - case AXP2101_registers_e::dldo1: result = registers.dldo1; break; - case AXP2101_registers_e::dldo2: result = registers.dldo2; break; - case AXP2101_registers_e::cpuldos: result = registers.cpuldos; break; - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - return 0; - } - return 0xFFFFF == result ? (realValue ? 0 : -1) : result; -} - -void AXP2101_settings::setState(AXP2101_registers_e reg, - AXP_pin_s state) { - const uint8_t value = static_cast(state) & 0x03; - - switch (reg) { - case AXP2101_registers_e::dcdc1: pinStates.en_dcdc1 = value; break; - case AXP2101_registers_e::dcdc2: pinStates.en_dcdc2 = value; break; - case AXP2101_registers_e::dcdc3: pinStates.en_dcdc3 = value; break; - case AXP2101_registers_e::dcdc4: pinStates.en_dcdc4 = value; break; - case AXP2101_registers_e::dcdc5: pinStates.en_dcdc5 = value; break; - case AXP2101_registers_e::aldo1: pinStates.en_aldo1 = value; break; - case AXP2101_registers_e::aldo2: pinStates.en_aldo2 = value; break; - case AXP2101_registers_e::aldo3: pinStates.en_aldo3 = value; break; - case AXP2101_registers_e::aldo4: pinStates.en_aldo4 = value; break; - case AXP2101_registers_e::bldo1: pinStates.en_bldo1 = value; break; - case AXP2101_registers_e::bldo2: pinStates.en_bldo2 = value; break; - case AXP2101_registers_e::dldo1: pinStates.en_dldo1 = value; break; - case AXP2101_registers_e::dldo2: pinStates.en_dldo2 = value; break; - case AXP2101_registers_e::cpuldos: pinStates.en_cpuldos = value; break; - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - break; - } -} - -AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { - switch (reg) { - case AXP2101_registers_e::dcdc1: return static_cast(pinStates.en_dcdc1); - case AXP2101_registers_e::dcdc2: return static_cast(pinStates.en_dcdc2); - case AXP2101_registers_e::dcdc3: return static_cast(pinStates.en_dcdc3); - case AXP2101_registers_e::dcdc4: return static_cast(pinStates.en_dcdc4); - case AXP2101_registers_e::dcdc5: return static_cast(pinStates.en_dcdc5); - case AXP2101_registers_e::aldo1: return static_cast(pinStates.en_aldo1); - case AXP2101_registers_e::aldo2: return static_cast(pinStates.en_aldo2); - case AXP2101_registers_e::aldo3: return static_cast(pinStates.en_aldo3); - case AXP2101_registers_e::aldo4: return static_cast(pinStates.en_aldo4); - case AXP2101_registers_e::bldo1: return static_cast(pinStates.en_bldo1); - case AXP2101_registers_e::bldo2: return static_cast(pinStates.en_bldo2); - case AXP2101_registers_e::dldo1: return static_cast(pinStates.en_dldo1); - case AXP2101_registers_e::dldo2: return static_cast(pinStates.en_dldo2); - case AXP2101_registers_e::cpuldos: return static_cast(pinStates.en_cpuldos); - case AXP2101_registers_e::chargeled: - case AXP2101_registers_e::batcharge: - case AXP2101_registers_e::charging: - case AXP2101_registers_e::batpresent: - case AXP2101_registers_e::chipid: - case AXP2101_registers_e::chargedet: - return AXP_pin_s::Protected; - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::battemp: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - case AXP2101_registers_e::chiptemp: - break; - } - return AXP_pin_s::Default; -} - -void AXP2101_settings::setChargeLed(AXP2101_chargeled_d led) { - pinStates.chargeled = static_cast(led); -} - -AXP2101_chargeled_d AXP2101_settings::getChargeLed() { - return static_cast(pinStates.chargeled); -} - -bool AXP2101_settings::getTS_disabled() { - return pinStates.dis_TS_pin; -} - -void AXP2101_settings::setTS_disabled(bool val) { - pinStates.dis_TS_pin = val; -} - - -uint16_t AXP2101_settings::getPreChargeCurrentLimit() const { - return chargeStates.pre_chg_cur * 25; -} - -void AXP2101_settings::setPreChargeCurrentLimit(uint16_t current_mA) { - if (current_mA > 200) { - current_mA = 200; - } - chargeStates.pre_chg_cur = current_mA / 25; -} - -uint16_t AXP2101_settings::getConstChargeCurrentLimit() const { - if (chargeStates.const_cur_lim <= 8) { - return chargeStates.const_cur_lim * 25; - } - return (chargeStates.const_cur_lim - 8) * 100 + 200; -} - -void AXP2101_settings::setConstChargeCurrentLimit(uint16_t current_mA) { - if (current_mA > 1000) { - current_mA = 1000; - } - if (current_mA <= 200) { - chargeStates.const_cur_lim = current_mA / 25; - } else { - chargeStates.const_cur_lim = ((current_mA - 200) / 100) + 8; - } -} - -uint16_t AXP2101_settings::getTerminationChargeCurrentLimit() const { - return chargeStates.term_cur_lim * 25; -} - -void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA) { - if (current_mA > 200) { - current_mA = 200; - } - chargeStates.term_cur_lim = current_mA / 25; -} - - -AXP2101_CV_charger_voltage_e AXP2101_settings::getCV_chargeVoltage() const { - return static_cast(chargeStates.chg_volt_lim); -} - -void AXP2101_settings::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { - chargeStates.chg_volt_lim = static_cast(voltage_mV); - if (chargeStates.chg_volt_lim > static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) { - // Set to a default safe limit - chargeStates.chg_volt_lim = static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V); - } -} - -AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_settings::getCV_chargerVoltage() const { - return static_cast(chargeStates.min_sys_voltage); -} - -void AXP2101_settings::setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { - chargeStates.min_sys_voltage = static_cast(voltage); -} - - -/** - * AXP2101 device class - */ - -// *INDENT-OFF* -AXP2101_settings AXP2101_deviceSettingsArray[] = -{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | en_aldo3 | en_aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos | chargeled -/* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, -/* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, -/* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* LilyGo TPCie v1.2 */ { 3300, 900, 900, 1100, 1200, 1800, 2800, 3300, 2900, 1800, 2800, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, -}; -// *INDENT-ON* - bool AXP2101::begin(TwoWire *wire, uint8_t addr, AXP2101_device_model_e device) { diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index efa8d6f453..dadbd7ff70 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -21,416 +21,7 @@ #include #include - -#define AXP2101_ADDR (0x34) - -#define AXP2101_DCDC_CTRL_REG (0x80) -#define AXP2101_LDO_CTRL_REG (0x90) -#define AXP2101_LDO_CTRL_REG1 (0x91) - -#define AXP2101_DCDC1_VOLTAGE_REG (0x82) -#define AXP2101_DCDC2_VOLTAGE_REG (0x83) -#define AXP2101_DCDC3_VOLTAGE_REG (0x84) -#define AXP2101_DCDC4_VOLTAGE_REG (0x85) -#define AXP2101_DCDC5_VOLTAGE_REG (0x86) -#define AXP2101_ALDO1_VOLTAGE_REG (0x92) -#define AXP2101_ALDO2_VOLTAGE_REG (0x93) -#define AXP2101_ALDO3_VOLTAGE_REG (0x94) -#define AXP2101_ALDO4_VOLTAGE_REG (0x95) -#define AXP2101_BLDO1_VOLTAGE_REG (0x96) -#define AXP2101_BLDO2_VOLTAGE_REG (0x97) -#define AXP2101_DLDO1_VOLTAGE_REG (0x99) -#define AXP2101_DLDO2_VOLTAGE_REG (0x9A) -#define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) - -// Measure V battery -#define AXP2101_VBAT_H_ADC_REG (0x34) -#define AXP2101_VBAT_L_ADC_REG (0x35) - -// Measure (optional) temperature sensor -#define AXP2101_TS_H_ADC_REG (0x36) -#define AXP2101_TS_L_ADC_REG (0x37) - -// Measure Vbus -#define AXP2101_VBUS_H_ADC_REG (0x38) -#define AXP2101_VBUS_L_ADC_REG (0x39) - -// Measure Vsys -#define AXP2101_VSYS_H_ADC_REG (0x3A) -#define AXP2101_VSYS_L_ADC_REG (0x3B) - -// Measure chip die temperature -#define AXP2101_TDIE_H_ADC_REG (0x3C) -#define AXP2101_TDIE_L_ADC_REG (0x3D) - - -#define AXP2101_VBAT_CTRL_MASK (1 << 0) -#define AXP2101_BATTEMP_CTRL_MASK (1 << 1) -#define AXP2101_VBUS_CTRL_MASK (1 << 2) -#define AXP2101_VSYS_CTRL_MASK (1 << 3) -#define AXP2101_TDIE_CTRL_MASK (1 << 4) -// What to do with bit 5: "general purpose ADC channel enable"? - - -#define AXP2101_COM_STAT0_REG (0x00) -#define AXP2101_COM_STAT1_REG (0x01) -#define AXP2101_CHIP_ID_REG (0x03) -#define AXP2101_CHARGE_DET_REG (0x04) // Fake, not a usable register! -#define AXP2101_PMU_CONFIG_REG (0x10) -#define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) -#define AXP2101_PWROK_PWROFF_REG (0x25) -#define AXP2101_ADC_ENABLE_REG (0x30) -#define AXP2101_IRQ_EN_0_REG (0x40) -#define AXP2101_IRQ_EN_1_REG (0x41) -#define AXP2101_IRQ_EN_2_REG (0x42) -#define AXP2101_IRQ_STATUS_0_REG (0x48) -#define AXP2101_IRQ_STATUS_1_REG (0x49) -#define AXP2101_IRQ_STATUS_2_REG (0x4A) -#define AXP2101_TS_PIN_CTRL (0x50) -#define AXP2101_ICC_CHARGER_SETTING_REG (0x62) -#define AXP2101_CHARGER_SETTING_REG (0x63) -#define AXP2101_CHGLED_REG (0x69) -#define AXP2101_BAT_CHARGE_REG (0xA4) /* pdf has a duplicate listed for register 0x04, should be 0xA4 */ - -#define AXP2101_DCDC1_CTRL_MASK (1 << 0) -#define AXP2101_DCDC2_CTRL_MASK (1 << 1) -#define AXP2101_DCDC3_CTRL_MASK (1 << 2) -#define AXP2101_DCDC4_CTRL_MASK (1 << 3) -#define AXP2101_DCDC5_CTRL_MASK (1 << 4) -#define AXP2101_ALDO1_CTRL_MASK (1 << 0) -#define AXP2101_ALDO2_CTRL_MASK (1 << 1) -#define AXP2101_ALDO3_CTRL_MASK (1 << 2) -#define AXP2101_ALDO4_CTRL_MASK (1 << 3) -#define AXP2101_BLDO1_CTRL_MASK (1 << 4) -#define AXP2101_BLDO2_CTRL_MASK (1 << 5) -#define AXP2101_DLDO1_CTRL_MASK (1 << 7) -#define AXP2101_DLDO2_CTRL_MASK (1 << 0) -#define AXP2101_CPUSLDO_CTRL_MASK (1 << 6) -#define AXP2101_CHGLED_CTRL_MASK (0x03 << 4) - -#define AXP2101_DCDC1_MIN (1500) -#define AXP2101_DCDC2_MIN (500) -#define AXP2101_DCDC3_MIN (500) -#define AXP2101_DCDC4_MIN (500) -#define AXP2101_DCDC5_MIN (1400) -#define AXP2101_ALDO1_MIN (500) -#define AXP2101_ALDO2_MIN (500) -#define AXP2101_ALDO3_MIN (500) -#define AXP2101_ALDO4_MIN (500) -#define AXP2101_BLDO1_MIN (500) -#define AXP2101_BLDO2_MIN (500) -#define AXP2101_DLDO1_MIN (500) -#define AXP2101_DLDO2_MIN (500) -#define AXP2101_CPUSLDO_MIN (500) - -#define AXP2101_DCDC1_MAX (3400) -#define AXP2101_DCDC2_MAX (1540) -#define AXP2101_DCDC3_MAX (3400) -#define AXP2101_DCDC4_MAX (1840) -#define AXP2101_DCDC5_MAX (3700) -#define AXP2101_ALDO1_MAX (3500) -#define AXP2101_ALDO2_MAX (3500) -#define AXP2101_ALDO3_MAX (3500) -#define AXP2101_ALDO4_MAX (3500) -#define AXP2101_BLDO1_MAX (3500) -#define AXP2101_BLDO2_MAX (3500) -#define AXP2101_DLDO1_MAX (3500) -#define AXP2101_DLDO2_MAX (1400) -#define AXP2101_CPUSLDO_MAX (1400) - -enum class AXP2101_device_model_e : uint8_t { - Unselected = 0u, // >>>>> Don't change these values, they are probably stored in user-settings <<<<< - M5Stack_Core2_v1_1 = 1u, // https://docs.m5stack.com/en/core/Core2%20v1.1 - M5Stack_CoreS3 = 2u, // https://docs.m5stack.com/en/core/CoreS3 - LilyGO_TBeam_v1_2 = 3u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_V1.2.pdf - LilyGO_TBeamS3_v3 = 4u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_S3_Core_V3.0.pdf - LilyGO_TPCie_v1_2 = 5u, // https://github.com/Xinyuan-LilyGO/LilyGo-T-PCIE/blob/master/schematic/T-PCIE-V1.2.pdf - - MAX, // Keep MAX as first after last device, devices must be sequentially numbered - UserDefined = 99u, // Keep UserDefined as last!!! -}; - -// The voltage registers mapped into an enum, don't change order without also changing AXP2101_intToRegister() -enum class AXP2101_registers_e : uint8_t { - dcdc1 = AXP2101_DCDC1_VOLTAGE_REG, - dcdc2 = AXP2101_DCDC2_VOLTAGE_REG, - dcdc3 = AXP2101_DCDC3_VOLTAGE_REG, - dcdc4 = AXP2101_DCDC4_VOLTAGE_REG, - dcdc5 = AXP2101_DCDC5_VOLTAGE_REG, - aldo1 = AXP2101_ALDO1_VOLTAGE_REG, - aldo2 = AXP2101_ALDO2_VOLTAGE_REG, - aldo3 = AXP2101_ALDO3_VOLTAGE_REG, - aldo4 = AXP2101_ALDO4_VOLTAGE_REG, - bldo1 = AXP2101_BLDO1_VOLTAGE_REG, - bldo2 = AXP2101_BLDO2_VOLTAGE_REG, - dldo1 = AXP2101_DLDO1_VOLTAGE_REG, - dldo2 = AXP2101_DLDO2_VOLTAGE_REG, - cpuldos = AXP2101_CPUSLDO_VOLTAGE_REG, - - // ADC inputs - vbat = AXP2101_VBAT_H_ADC_REG, - battemp = AXP2101_TS_H_ADC_REG, - vbus = AXP2101_VBUS_H_ADC_REG, - vsys = AXP2101_VSYS_H_ADC_REG, - chiptemp = AXP2101_TDIE_H_ADC_REG, - - // Above are settable pinstates/voltages of the AXP2101 - // Below are non-voltage and read-only values of the AXP2101, also update AXP2101_register_count when adding values - chargeled = AXP2101_CHGLED_REG, - batcharge = AXP2101_BAT_CHARGE_REG, - charging = AXP2101_COM_STAT1_REG, - batpresent = AXP2101_COM_STAT0_REG, - chipid = AXP2101_CHIP_ID_REG, - chargedet = AXP2101_CHARGE_DET_REG, -}; -constexpr int AXP2101_settings_count = 14; // Changeable settings -constexpr int AXP2101_register_count = 25; // All registers - -enum class AXP_pin_s : uint8_t { - Off = 0x00, // Max. 3 bits can be stored in settings! - On = 0x01, - Default = 0x02, // Don't update value or state on boot - Disabled = 0x03, // Port not connected, don't use - Protected = 0x07 // Don't try to change port value, can make the unit fail! -}; - -enum class AXP2101_chargeled_d : uint8_t { - Off = 0x00, - Flash_1Hz = 0x01, - Flash_4Hz = 0x02, - Steady_On = 0x03, - Protected = 0x07 // Don't try to change or not connected -}; - -enum class AXP2101_chargingState_e : int8_t { - Discharging = -1, - Standby = 0, - Charging = 1, -}; - -enum class AXP2101_chipid_e : uint8_t { - axp2101 = 0b01000111, // AXP2101 -}; - -enum class AXP2101_chargingDetail_e : uint8_t { - tricharge = 0b000, - precharge = 0b001, - constcharge = 0b010, - constvoltage = 0b011, - done = 0b100, - notcharging = 0b101, -}; - -enum class AXP2101_CV_charger_voltage_e : uint8_t { - reserved = 0, - limit_4_00V = 0b001, - limit_4_10V = 0b010, - limit_4_20V = 0b011, // default - limit_4_35V = 0b100, - limit_4_40V = 0b101 -}; - -enum class AXP2101_Linear_Charger_Vsys_dpm_e : uint8_t { - vsys_4_1V = 0, - vsys_4_2V, - vsys_4_3V, - vsys_4_4V, - vsys_4_5V, - vsys_4_6V, - vsys_4_7V, - vsys_4_8V -}; - -enum class AXP2101_VINDPM_e : uint8_t { - Vin_3_88V = 0, - Vin_3_96V, - Vin_4_04V, - Vin_4_12V, - Vin_4_20V, - Vin_4_28V, - Vin_4_36V, - Vin_4_44V, - Vin_4_52V, - Vin_4_60V, - Vin_4_68V, - Vin_4_76V, - Vin_4_84V, - Vin_4_92V, - Vin_5_00V, - Vin_5_08V -}; - -enum class AXP2101_InputCurrentLimit_e : uint8_t { - limit_100mA = 0, - limit_500mA, - limit_900mA, - limit_1000mA, - limit_1500mA, - limit_2000mA -}; - - -AXP2101_registers_e AXP2101_intToRegister(int reg); -uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); -uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); -bool AXP2101_isPinDefault(AXP_pin_s pin); // Default, Disabled or Protected -bool AXP2101_isPinProtected(AXP_pin_s pin); // Disabled or Protected - -const __FlashStringHelper* toString(AXP2101_registers_e reg, - bool displayString = true); -const __FlashStringHelper* toString(AXP2101_device_model_e device, - bool displayString = true); -const __FlashStringHelper* toString(AXP_pin_s pin); -const __FlashStringHelper* toString(AXP2101_chargeled_d led); -const __FlashStringHelper* toString(AXP2101_chargingState_e state); -const __FlashStringHelper* toString(AXP2101_chipid_e chip); -const __FlashStringHelper* toString(AXP2101_chargingDetail_e chip); - -class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. -public: - - AXP2101_settings(); - AXP2101_settings(uint16_t _dcdc1, - uint16_t _dcdc2, - uint16_t _dcdc3, - uint16_t _dcdc4, - uint16_t _dcdc5, - uint16_t _aldo1, - uint16_t _aldo2, - uint16_t _aldo3, - uint16_t _aldo4, - uint16_t _bldo1, - uint16_t _bldo2, - uint16_t _dldo1, - uint16_t _dldo2, - uint16_t _cpuldos, - AXP_pin_s _en_dcdc1, - AXP_pin_s _en_dcdc2, - AXP_pin_s _en_dcdc3, - AXP_pin_s _en_dcdc4, - AXP_pin_s _en_dcdc5, - AXP_pin_s _en_aldo1, - AXP_pin_s _en_aldo2, - AXP_pin_s _en_aldo3, - AXP_pin_s _en_aldo4, - AXP_pin_s _en_bldo1, - AXP_pin_s _en_bldo2, - AXP_pin_s _en_dldo1, - AXP_pin_s _en_dldo2, - AXP_pin_s _en_cpuldos, - AXP2101_chargeled_d _chargeled); - void setVoltage(AXP2101_registers_e reg, - int voltage); - int getVoltage(AXP2101_registers_e reg, - bool realValue = true); - void setState(AXP2101_registers_e reg, - AXP_pin_s state); - AXP_pin_s getState(AXP2101_registers_e reg); - void setChargeLed(AXP2101_chargeled_d led); - AXP2101_chargeled_d getChargeLed(); - - bool getTS_disabled(); - void setTS_disabled(bool val); - - - // Reg 61: Iprechg Charger Settings - uint16_t getPreChargeCurrentLimit() const; - void setPreChargeCurrentLimit(uint16_t current_mA); - - // Reg 62: ICC Charger Settings - uint16_t getConstChargeCurrentLimit() const; - void setConstChargeCurrentLimit(uint16_t current_mA); - - // Reg 63: Iterm Charger Settings and Control - // Enable/Disable via chargeStates.term_cur_lim_en - uint16_t getTerminationChargeCurrentLimit() const; - void setTerminationChargeCurrentLimit(uint16_t current_mA); - - // Reg 64: CV Charger Voltage Settings - AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; - void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); - - // Reg 14: Minimum System Voltage Control - AXP2101_Linear_Charger_Vsys_dpm_e getCV_chargerVoltage() const; - void setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage); - - - - - -private: - - union { - struct { - uint16_t dcdc1; - uint16_t dcdc2; - uint16_t dcdc3; - uint16_t dcdc4; - uint16_t dcdc5; - uint16_t aldo1; - uint16_t aldo2; - uint16_t aldo3; - uint16_t aldo4; - uint16_t bldo1; - uint16_t bldo2; - uint16_t dldo1; - uint16_t dldo2; - uint16_t cpuldos; - } registers; - uint16_t registers_[AXP2101_settings_count]{}; - }; - union { - struct { // AXP_pin_s: Off / On / default / disabled / unavailable? / unused? / Protected - uint64_t en_dcdc1 : 3; // bit 0/1/2 - uint64_t en_dcdc2 : 3; // bit 3/4/5 - uint64_t en_dcdc3 : 3; // bit 6/7/8 - uint64_t en_dcdc4 : 3; // bit 9/10/11 - uint64_t en_dcdc5 : 3; // bit 12/13/14 - uint64_t en_aldo1 : 3; // bit 15/16/17 - uint64_t en_aldo2 : 3; // bit 18/19/20 - uint64_t en_aldo3 : 3; // bit 21/22/23 - uint64_t en_aldo4 : 3; // bit 24/25/26 - uint64_t en_bldo1 : 3; // bit 27/28/29 - uint64_t en_bldo2 : 3; // bit 30/31/32 - uint64_t en_dldo1 : 3; // bit 33/34/35 - uint64_t en_dldo2 : 3; // bit 36/37/38 - uint64_t en_cpuldos : 3; // bit 39/40/41 - uint64_t chargeled : 3; // bit 42/43/44 - - // Settings for external temperature sensor (TS) - uint64_t dis_TS_pin : 1; // bit 45, reg50 bit 4 - uint64_t TS_cur_src : 2; // bit 46/47, reg50 bit 3:2 - uint64_t TS_current : 2; // bit 48/49, reg50 bit 1:0 - - uint64_t en_unused : 13; // bit 50..63 // All bits defined - } pinStates; - uint64_t pinStates_{}; // 8 bytes - }; - - union { - struct { - uint64_t pre_chg_cur : 4; // reg 0x61: 25* N mA - uint64_t const_cur_lim : 5; // reg 0x62: 25* N mA if N <= 8, 200+100*(N-8) mA if N > 8 - uint64_t term_cur_lim_en : 1; // reg 0x63: Charging termination of current enable - uint64_t term_cur_lim : 4; // reg 0x63: 25* N mA - uint64_t chg_volt_lim : 3; // reg 0x64: - uint64_t thermal_thresh : 2; // reg 0x65: 00: 60deg, 01: 80deg, 10: 100deg, 11:120deg - uint64_t chg_timeout_ctrl: 8; // reg 0x67: - uint64_t bat_detection : 1; // reg 0x68: - uint64_t coincell_term_volt : 3; // reg 0x6A: 2.6~3.3V, 100mV/step, 8 steps - - uint64_t min_sys_voltage : 3; // reg 0x14: 4.1 + N*0.1V Linear Charger Vsys Voltage dpm - uint64_t inp_volt_limit : 4; // reg 0x15: Vindpm 3.88+N*0.08V - uint64_t inp_cur_limit : 3; // reg 0x16: - - - uint64_t en_unused : 23; - } chargeStates; - uint64_t chargeStates_{}; // 8 bytes - }; -}; - -extern AXP2101_settings AXP2101_deviceSettingsArray[]; +#include "AXP2101_settings.h" class AXP2101 { private: diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp new file mode 100644 index 0000000000..940004f244 --- /dev/null +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -0,0 +1,491 @@ +#include "AXP2101_settings.h" + +#ifdef ESP32 + +// To check if we have implemented all cases of the enums +# pragma GCC diagnostic push +# pragma GCC diagnostic warning "-Wswitch-enum" + +/** + * Utility functions + */ +const __FlashStringHelper* toString(AXP2101_device_model_e device, + bool displayString) { + switch (device) { + case AXP2101_device_model_e::Unselected: return displayString ? F("Select an option to set default values") : F("Unselected"); + case AXP2101_device_model_e::M5Stack_Core2_v1_1: return displayString ? F("M5Stack Core2 v1.1") : F("M5Core2v11"); + case AXP2101_device_model_e::M5Stack_CoreS3: return displayString ? F("M5Stack CoreS3") : F("M5CoreS3"); + case AXP2101_device_model_e::LilyGO_TBeam_v1_2: return displayString ? F("LilyGo TBeam v1.2") : F("TBeamv12"); + case AXP2101_device_model_e::LilyGO_TBeamS3_v3: return displayString ? F("LilyGo TBeam S3 v3") : F("TBeamS3v3"); + case AXP2101_device_model_e::LilyGO_TPCie_v1_2: return displayString ? F("LilyGo TPCie v1.2") : F("TPCiev12"); + case AXP2101_device_model_e::UserDefined: return displayString ? F("User defined") : F("Userdefined"); + case AXP2101_device_model_e::MAX: break; + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_registers_e reg, + bool displayString) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return displayString ? F("DCDC1") : F("dcdc1"); + case AXP2101_registers_e::dcdc2: return displayString ? F("DCDC2") : F("dcdc2"); + case AXP2101_registers_e::dcdc3: return displayString ? F("DCDC3") : F("dcdc3"); + case AXP2101_registers_e::dcdc4: return displayString ? F("DCDC4") : F("dcdc4"); + case AXP2101_registers_e::dcdc5: return displayString ? F("DCDC5") : F("dcdc5"); + case AXP2101_registers_e::aldo1: return displayString ? F("ALDO1") : F("aldo1"); + case AXP2101_registers_e::aldo2: return displayString ? F("ALDO2") : F("aldo2"); + case AXP2101_registers_e::aldo3: return displayString ? F("ALDO3") : F("aldo3"); + case AXP2101_registers_e::aldo4: return displayString ? F("ALDO4") : F("aldo4"); + case AXP2101_registers_e::bldo1: return displayString ? F("BLDO1") : F("bldo1"); + case AXP2101_registers_e::bldo2: return displayString ? F("BLDO2") : F("bldo2"); + case AXP2101_registers_e::dldo1: return displayString ? F("DLDO1") : F("dldo1"); + case AXP2101_registers_e::dldo2: return displayString ? F("DLDO2") : F("dldo2"); + case AXP2101_registers_e::cpuldos: return displayString ? F("CPULDOS") : F("cpuldos"); + case AXP2101_registers_e::chargeled: return displayString ? F("ChargeLed") : F("chargeled"); + case AXP2101_registers_e::batcharge: return displayString ? F("BatCharge") : F("batcharge"); + case AXP2101_registers_e::charging: return displayString ? F("ChargingState") : F("chargingstate"); + case AXP2101_registers_e::batpresent: return displayString ? F("BatPresent") : F("batpresent"); + case AXP2101_registers_e::chipid: return displayString ? F("ChipID") : F("chipid"); + case AXP2101_registers_e::chargedet: return displayString ? F("ChargingDetail") : F("chargingdet"); + + case AXP2101_registers_e::vbat: return displayString ? F("BatVoltage") : F("vbat"); + case AXP2101_registers_e::battemp: return displayString ? F("BatTemp") : F("battemp"); + case AXP2101_registers_e::vbus: return displayString ? F("BusVoltage") : F("vbus"); + case AXP2101_registers_e::vsys: return displayString ? F("SysVoltage") : F("vsys"); + case AXP2101_registers_e::chiptemp: return displayString ? F("ChipTemp") : F("chiptemp"); + + } + return F(""); +} + +const __FlashStringHelper* toString(AXP_pin_s pin) { + switch (pin) { + case AXP_pin_s::Off: return F("Off"); + case AXP_pin_s::On: return F("On"); + case AXP_pin_s::Default: return F("Default"); + case AXP_pin_s::Disabled: return F("Disabled"); + case AXP_pin_s::Protected: return F("Protected"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_chargeled_d led) { + switch (led) { + case AXP2101_chargeled_d::Off: return F("Off"); + case AXP2101_chargeled_d::Flash_1Hz: return F("Flash 1Hz"); + case AXP2101_chargeled_d::Flash_4Hz: return F("Flash 4Hz"); + case AXP2101_chargeled_d::Steady_On: return F("Steady On"); + case AXP2101_chargeled_d::Protected: return F("Protected"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_chargingState_e state) { + switch (state) { + case AXP2101_chargingState_e::Discharging: return F("Discharging"); + case AXP2101_chargingState_e::Standby: return F("Standby"); + case AXP2101_chargingState_e::Charging: return F("Charging"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_chipid_e chip) { + switch (chip) { + case AXP2101_chipid_e::axp2101: return F("AXP2101"); + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge) { + switch (charge) { + case AXP2101_chargingDetail_e::tricharge: return F("tri-charge"); + case AXP2101_chargingDetail_e::precharge: return F("pre-charge"); + case AXP2101_chargingDetail_e::constcharge: return F("constant charge (CC)"); + case AXP2101_chargingDetail_e::constvoltage: return F("constant voltage (CV)"); + case AXP2101_chargingDetail_e::done: return F("charge done"); + case AXP2101_chargingDetail_e::notcharging: return F("not charging"); + } + return F(""); +} + +AXP2101_registers_e AXP2101_intToRegister(int reg) { + switch (reg) { + case 0: return AXP2101_registers_e::dcdc1; + case 1: return AXP2101_registers_e::dcdc2; + case 2: return AXP2101_registers_e::dcdc3; + case 3: return AXP2101_registers_e::dcdc4; + case 4: return AXP2101_registers_e::dcdc5; + case 5: return AXP2101_registers_e::aldo1; + case 6: return AXP2101_registers_e::aldo2; + case 7: return AXP2101_registers_e::aldo3; + case 8: return AXP2101_registers_e::aldo4; + case 9: return AXP2101_registers_e::bldo1; + case 10: return AXP2101_registers_e::bldo2; + case 11: return AXP2101_registers_e::dldo1; + case 12: return AXP2101_registers_e::dldo2; + case 13: return AXP2101_registers_e::cpuldos; + case 14: return AXP2101_registers_e::chargeled; + case 15: return AXP2101_registers_e::batcharge; + case 16: return AXP2101_registers_e::charging; + case 17: return AXP2101_registers_e::batpresent; + case 18: return AXP2101_registers_e::chipid; + case 19: return AXP2101_registers_e::chargedet; + + case 20: return AXP2101_registers_e::vbat; + case 21: return AXP2101_registers_e::battemp; + case 22: return AXP2101_registers_e::vbus; + case 23: return AXP2101_registers_e::vsys; + case 24: return AXP2101_registers_e::chiptemp; + } + return AXP2101_registers_e::dcdc1; // we shouldn't get here, just defaulting to the first value +} + +uint16_t AXP2101_minVoltage(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return AXP2101_DCDC1_MIN; + case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MIN; + case AXP2101_registers_e::dcdc2: + case AXP2101_registers_e::dcdc3: + case AXP2101_registers_e::dcdc4: + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MIN; + + // not a voltage register + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } + return 0u; +} + +uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: + case AXP2101_registers_e::dcdc3: return AXP2101_DCDC3_MAX; + case AXP2101_registers_e::dcdc2: return AXP2101_DCDC2_MAX; + case AXP2101_registers_e::dcdc4: return AXP2101_DCDC4_MAX; + case AXP2101_registers_e::dcdc5: return AXP2101_DCDC5_MAX; + case AXP2101_registers_e::aldo1: + case AXP2101_registers_e::aldo2: + case AXP2101_registers_e::aldo3: + case AXP2101_registers_e::aldo4: + case AXP2101_registers_e::bldo1: + case AXP2101_registers_e::bldo2: + case AXP2101_registers_e::dldo1: return AXP2101_DLDO1_MAX; + case AXP2101_registers_e::dldo2: + case AXP2101_registers_e::cpuldos: return AXP2101_CPUSLDO_MAX; + + // not a voltage register + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } + return 0u; +} + +/** + * Is the pin Default, Disabled or Protected, then don't initialize + */ +bool AXP2101_isPinDefault(AXP_pin_s pin) { + return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin || AXP_pin_s::Default == pin; +} + +/** + * Is the pin Disabled or Protected, then don't change + */ +bool AXP2101_isPinProtected(AXP_pin_s pin) { + return AXP_pin_s::Protected == pin || AXP_pin_s::Disabled == pin; +} + +/** + * AXP2101_settings struct + */ + +// constructor +AXP2101_settings::AXP2101_settings() {} + +// constructor +AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _dcdc3, uint16_t _dcdc4, uint16_t _dcdc5, + uint16_t _aldo1, uint16_t _aldo2, uint16_t _aldo3, uint16_t _aldo4, + uint16_t _bldo1, uint16_t _bldo2, uint16_t _dldo1, uint16_t _dldo2, uint16_t _cpuldos, + AXP_pin_s _en_dcdc1, AXP_pin_s _en_dcdc2, AXP_pin_s _en_dcdc3, AXP_pin_s _en_dcdc4, AXP_pin_s _en_dcdc5, + AXP_pin_s _en_aldo1, AXP_pin_s _en_aldo2, AXP_pin_s _en_aldo3, AXP_pin_s _en_aldo4, + AXP_pin_s _en_bldo1, AXP_pin_s _en_bldo2, AXP_pin_s _en_dldo1, AXP_pin_s _en_dldo2, AXP_pin_s _en_cpuldos, + AXP2101_chargeled_d _chargeled) +{ + registers.dcdc1 = _dcdc1; registers.dcdc2 = _dcdc2; registers.dcdc3 = _dcdc3; registers.dcdc4 = _dcdc4; registers.dcdc5 = _dcdc5; + registers.aldo1 = _aldo1; registers.aldo2 = _aldo2; registers.aldo3 = _aldo3; registers.aldo4 = _aldo4; + registers.bldo1 = _bldo1; registers.bldo2 = _bldo2; registers.dldo1 = _dldo1; registers.dldo2 = _dldo2; registers.cpuldos = _cpuldos; + + pinStates.en_dcdc1 = static_cast(_en_dcdc1); pinStates.en_dcdc2 = static_cast(_en_dcdc2); + pinStates.en_dcdc3 = static_cast(_en_dcdc3); pinStates.en_dcdc4 = static_cast(_en_dcdc4); + pinStates.en_dcdc5 = static_cast(_en_dcdc5); pinStates.en_aldo1 = static_cast(_en_aldo1); + pinStates.en_aldo2 = static_cast(_en_aldo2); pinStates.en_aldo3 = static_cast(_en_aldo3); + pinStates.en_aldo4 = static_cast(_en_aldo4); pinStates.en_bldo1 = static_cast(_en_bldo1); + pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); + pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); + pinStates.chargeled = static_cast(_chargeled); +} + +void AXP2101_settings::setVoltage(AXP2101_registers_e reg, + int voltage) { + if (-1 == voltage) { voltage = 0xFFFF; } + + switch (reg) { + case AXP2101_registers_e::dcdc1: registers.dcdc1 = voltage; break; + case AXP2101_registers_e::dcdc2: registers.dcdc2 = voltage; break; + case AXP2101_registers_e::dcdc3: registers.dcdc3 = voltage; break; + case AXP2101_registers_e::dcdc4: registers.dcdc4 = voltage; break; + case AXP2101_registers_e::dcdc5: registers.dcdc5 = voltage; break; + case AXP2101_registers_e::aldo1: registers.aldo1 = voltage; break; + case AXP2101_registers_e::aldo2: registers.aldo2 = voltage; break; + case AXP2101_registers_e::aldo3: registers.aldo3 = voltage; break; + case AXP2101_registers_e::aldo4: registers.aldo4 = voltage; break; + case AXP2101_registers_e::bldo1: registers.bldo1 = voltage; break; + case AXP2101_registers_e::bldo2: registers.bldo2 = voltage; break; + case AXP2101_registers_e::dldo1: registers.dldo1 = voltage; break; + case AXP2101_registers_e::dldo2: registers.dldo2 = voltage; break; + case AXP2101_registers_e::cpuldos: registers.cpuldos = voltage; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } +} + +int AXP2101_settings::getVoltage(AXP2101_registers_e reg, + bool realValue) { + int result = -1; + + switch (reg) { + case AXP2101_registers_e::dcdc1: result = registers.dcdc1; break; + case AXP2101_registers_e::dcdc2: result = registers.dcdc2; break; + case AXP2101_registers_e::dcdc3: result = registers.dcdc3; break; + case AXP2101_registers_e::dcdc4: result = registers.dcdc4; break; + case AXP2101_registers_e::dcdc5: result = registers.dcdc5; break; + case AXP2101_registers_e::aldo1: result = registers.aldo1; break; + case AXP2101_registers_e::aldo2: result = registers.aldo2; break; + case AXP2101_registers_e::aldo3: result = registers.aldo3; break; + case AXP2101_registers_e::aldo4: result = registers.aldo4; break; + case AXP2101_registers_e::bldo1: result = registers.bldo1; break; + case AXP2101_registers_e::bldo2: result = registers.bldo2; break; + case AXP2101_registers_e::dldo1: result = registers.dldo1; break; + case AXP2101_registers_e::dldo2: result = registers.dldo2; break; + case AXP2101_registers_e::cpuldos: result = registers.cpuldos; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + return 0; + } + return 0xFFFFF == result ? (realValue ? 0 : -1) : result; +} + +void AXP2101_settings::setState(AXP2101_registers_e reg, + AXP_pin_s state) { + const uint8_t value = static_cast(state) & 0x03; + + switch (reg) { + case AXP2101_registers_e::dcdc1: pinStates.en_dcdc1 = value; break; + case AXP2101_registers_e::dcdc2: pinStates.en_dcdc2 = value; break; + case AXP2101_registers_e::dcdc3: pinStates.en_dcdc3 = value; break; + case AXP2101_registers_e::dcdc4: pinStates.en_dcdc4 = value; break; + case AXP2101_registers_e::dcdc5: pinStates.en_dcdc5 = value; break; + case AXP2101_registers_e::aldo1: pinStates.en_aldo1 = value; break; + case AXP2101_registers_e::aldo2: pinStates.en_aldo2 = value; break; + case AXP2101_registers_e::aldo3: pinStates.en_aldo3 = value; break; + case AXP2101_registers_e::aldo4: pinStates.en_aldo4 = value; break; + case AXP2101_registers_e::bldo1: pinStates.en_bldo1 = value; break; + case AXP2101_registers_e::bldo2: pinStates.en_bldo2 = value; break; + case AXP2101_registers_e::dldo1: pinStates.en_dldo1 = value; break; + case AXP2101_registers_e::dldo2: pinStates.en_dldo2 = value; break; + case AXP2101_registers_e::cpuldos: pinStates.en_cpuldos = value; break; + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } +} + +AXP_pin_s AXP2101_settings::getState(AXP2101_registers_e reg) { + switch (reg) { + case AXP2101_registers_e::dcdc1: return static_cast(pinStates.en_dcdc1); + case AXP2101_registers_e::dcdc2: return static_cast(pinStates.en_dcdc2); + case AXP2101_registers_e::dcdc3: return static_cast(pinStates.en_dcdc3); + case AXP2101_registers_e::dcdc4: return static_cast(pinStates.en_dcdc4); + case AXP2101_registers_e::dcdc5: return static_cast(pinStates.en_dcdc5); + case AXP2101_registers_e::aldo1: return static_cast(pinStates.en_aldo1); + case AXP2101_registers_e::aldo2: return static_cast(pinStates.en_aldo2); + case AXP2101_registers_e::aldo3: return static_cast(pinStates.en_aldo3); + case AXP2101_registers_e::aldo4: return static_cast(pinStates.en_aldo4); + case AXP2101_registers_e::bldo1: return static_cast(pinStates.en_bldo1); + case AXP2101_registers_e::bldo2: return static_cast(pinStates.en_bldo2); + case AXP2101_registers_e::dldo1: return static_cast(pinStates.en_dldo1); + case AXP2101_registers_e::dldo2: return static_cast(pinStates.en_dldo2); + case AXP2101_registers_e::cpuldos: return static_cast(pinStates.en_cpuldos); + case AXP2101_registers_e::chargeled: + case AXP2101_registers_e::batcharge: + case AXP2101_registers_e::charging: + case AXP2101_registers_e::batpresent: + case AXP2101_registers_e::chipid: + case AXP2101_registers_e::chargedet: + return AXP_pin_s::Protected; + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::battemp: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + case AXP2101_registers_e::chiptemp: + break; + } + return AXP_pin_s::Default; +} + +void AXP2101_settings::setChargeLed(AXP2101_chargeled_d led) { + pinStates.chargeled = static_cast(led); +} + +AXP2101_chargeled_d AXP2101_settings::getChargeLed() { + return static_cast(pinStates.chargeled); +} + +bool AXP2101_settings::getTS_disabled() { + return pinStates.dis_TS_pin; +} + +void AXP2101_settings::setTS_disabled(bool val) { + pinStates.dis_TS_pin = val; +} + + +uint16_t AXP2101_settings::getPreChargeCurrentLimit() const { + return chargeStates.pre_chg_cur * 25; +} + +void AXP2101_settings::setPreChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 200) { + current_mA = 200; + } + chargeStates.pre_chg_cur = current_mA / 25; +} + +uint16_t AXP2101_settings::getConstChargeCurrentLimit() const { + if (chargeStates.const_cur_lim <= 8) { + return chargeStates.const_cur_lim * 25; + } + return (chargeStates.const_cur_lim - 8) * 100 + 200; +} + +void AXP2101_settings::setConstChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 1000) { + current_mA = 1000; + } + if (current_mA <= 200) { + chargeStates.const_cur_lim = current_mA / 25; + } else { + chargeStates.const_cur_lim = ((current_mA - 200) / 100) + 8; + } +} + +uint16_t AXP2101_settings::getTerminationChargeCurrentLimit() const { + return chargeStates.term_cur_lim * 25; +} + +void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 200) { + current_mA = 200; + } + chargeStates.term_cur_lim = current_mA / 25; +} + + +AXP2101_CV_charger_voltage_e AXP2101_settings::getCV_chargeVoltage() const { + return static_cast(chargeStates.chg_volt_lim); +} + +void AXP2101_settings::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { + chargeStates.chg_volt_lim = static_cast(voltage_mV); + if (chargeStates.chg_volt_lim > static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) { + // Set to a default safe limit + chargeStates.chg_volt_lim = static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V); + } +} + +AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_settings::getCV_chargerVoltage() const { + return static_cast(chargeStates.min_sys_voltage); +} + +void AXP2101_settings::setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { + chargeStates.min_sys_voltage = static_cast(voltage); +} + + +/** + * AXP2101 device class + */ + +// *INDENT-OFF* +AXP2101_settings AXP2101_deviceSettingsArray[] = +{ // voltages: dcdc1 | dcdc2 | dcdc3 | dcdc4 | dcdc5 | aldo1 | aldo2 | aldo3 | aldo4| bldo1 | bldo2 | dldo1 | dldo2 | cpuldos | en_dcdc1 | en_dcdc2 | en_dcdc3 | en_dcdc4 | en_dcdc5 | en_aldo1 | en_aldo2 | en_aldo3 | en_aldo4 | en_bldo1 | en_bldo2 | en_dldo1 | en_dldo2 | en_cpuldos | chargeled +/* Unselected */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, +/* M5Stack Core2 v1.1 */ { 3300, 0, 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, +/* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TPCie v1.2 */ { 3300, 900, 900, 1100, 1200, 1800, 2800, 3300, 2900, 1800, 2800, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, +}; +// *INDENT-ON* + +# pragma GCC diagnostic pop + +#endif // ifdef ESP32 diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h new file mode 100644 index 0000000000..caa19daeee --- /dev/null +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -0,0 +1,436 @@ +#ifndef __AXP2101_SETTINGS_H +#define __AXP2101_SETTINGS_H + +/** + * AXP2101 library adjusted for ESPEasy + * 2024-02-04 tonhuisman: Start. + * + * Based on the AXP2101 driver included in https://github.com/m5stack/M5Core2 + */ + +/** Changelog: + * 2024-02-21 tonhuisman: Add support for ChipId and ChargingDetail state + * 2024-02-18 tonhuisman: Add support for ChargingState, isBatteryDetected + * 2024-02-17 tonhuisman: Add support for Charge-led and battery charge level, limit to ESP32, as this chip is only available + * on ESP32 based units + * 2024-02-16 tonhuisman: Initial 'release' with AXP2101 plugin for ESPEasy, implementing all output pins + * Including predefined settings for M5Stack Core2 v1.1, M5Stack CoreS3, LilyGO_TBeam_v1_2, + * LilyGO_TBeamS3_v3, LilyGO_TPCie_v1_2. + * 2024-02-04 tonhuisman: Start development of the library + */ + +#include +#include + +#define AXP2101_ADDR (0x34) + +#define AXP2101_DCDC_CTRL_REG (0x80) +#define AXP2101_LDO_CTRL_REG (0x90) +#define AXP2101_LDO_CTRL_REG1 (0x91) + +#define AXP2101_DCDC1_VOLTAGE_REG (0x82) +#define AXP2101_DCDC2_VOLTAGE_REG (0x83) +#define AXP2101_DCDC3_VOLTAGE_REG (0x84) +#define AXP2101_DCDC4_VOLTAGE_REG (0x85) +#define AXP2101_DCDC5_VOLTAGE_REG (0x86) +#define AXP2101_ALDO1_VOLTAGE_REG (0x92) +#define AXP2101_ALDO2_VOLTAGE_REG (0x93) +#define AXP2101_ALDO3_VOLTAGE_REG (0x94) +#define AXP2101_ALDO4_VOLTAGE_REG (0x95) +#define AXP2101_BLDO1_VOLTAGE_REG (0x96) +#define AXP2101_BLDO2_VOLTAGE_REG (0x97) +#define AXP2101_DLDO1_VOLTAGE_REG (0x99) +#define AXP2101_DLDO2_VOLTAGE_REG (0x9A) +#define AXP2101_CPUSLDO_VOLTAGE_REG (0x98) + +// Measure V battery +#define AXP2101_VBAT_H_ADC_REG (0x34) +#define AXP2101_VBAT_L_ADC_REG (0x35) + +// Measure (optional) temperature sensor +#define AXP2101_TS_H_ADC_REG (0x36) +#define AXP2101_TS_L_ADC_REG (0x37) + +// Measure Vbus +#define AXP2101_VBUS_H_ADC_REG (0x38) +#define AXP2101_VBUS_L_ADC_REG (0x39) + +// Measure Vsys +#define AXP2101_VSYS_H_ADC_REG (0x3A) +#define AXP2101_VSYS_L_ADC_REG (0x3B) + +// Measure chip die temperature +#define AXP2101_TDIE_H_ADC_REG (0x3C) +#define AXP2101_TDIE_L_ADC_REG (0x3D) + + +#define AXP2101_VBAT_CTRL_MASK (1 << 0) +#define AXP2101_BATTEMP_CTRL_MASK (1 << 1) +#define AXP2101_VBUS_CTRL_MASK (1 << 2) +#define AXP2101_VSYS_CTRL_MASK (1 << 3) +#define AXP2101_TDIE_CTRL_MASK (1 << 4) +// What to do with bit 5: "general purpose ADC channel enable"? + + +#define AXP2101_COM_STAT0_REG (0x00) +#define AXP2101_COM_STAT1_REG (0x01) +#define AXP2101_CHIP_ID_REG (0x03) +#define AXP2101_CHARGE_DET_REG (0x04) // Fake, not a usable register! +#define AXP2101_PMU_CONFIG_REG (0x10) +#define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) +#define AXP2101_PWROK_PWROFF_REG (0x25) +#define AXP2101_ADC_ENABLE_REG (0x30) +#define AXP2101_IRQ_EN_0_REG (0x40) +#define AXP2101_IRQ_EN_1_REG (0x41) +#define AXP2101_IRQ_EN_2_REG (0x42) +#define AXP2101_IRQ_STATUS_0_REG (0x48) +#define AXP2101_IRQ_STATUS_1_REG (0x49) +#define AXP2101_IRQ_STATUS_2_REG (0x4A) +#define AXP2101_TS_PIN_CTRL (0x50) +#define AXP2101_ICC_CHARGER_SETTING_REG (0x62) +#define AXP2101_CHARGER_SETTING_REG (0x63) +#define AXP2101_CHGLED_REG (0x69) +#define AXP2101_BAT_CHARGE_REG (0xA4) /* pdf has a duplicate listed for register 0x04, should be 0xA4 */ + +#define AXP2101_DCDC1_CTRL_MASK (1 << 0) +#define AXP2101_DCDC2_CTRL_MASK (1 << 1) +#define AXP2101_DCDC3_CTRL_MASK (1 << 2) +#define AXP2101_DCDC4_CTRL_MASK (1 << 3) +#define AXP2101_DCDC5_CTRL_MASK (1 << 4) +#define AXP2101_ALDO1_CTRL_MASK (1 << 0) +#define AXP2101_ALDO2_CTRL_MASK (1 << 1) +#define AXP2101_ALDO3_CTRL_MASK (1 << 2) +#define AXP2101_ALDO4_CTRL_MASK (1 << 3) +#define AXP2101_BLDO1_CTRL_MASK (1 << 4) +#define AXP2101_BLDO2_CTRL_MASK (1 << 5) +#define AXP2101_DLDO1_CTRL_MASK (1 << 7) +#define AXP2101_DLDO2_CTRL_MASK (1 << 0) +#define AXP2101_CPUSLDO_CTRL_MASK (1 << 6) +#define AXP2101_CHGLED_CTRL_MASK (0x03 << 4) + +#define AXP2101_DCDC1_MIN (1500) +#define AXP2101_DCDC2_MIN (500) +#define AXP2101_DCDC3_MIN (500) +#define AXP2101_DCDC4_MIN (500) +#define AXP2101_DCDC5_MIN (1400) +#define AXP2101_ALDO1_MIN (500) +#define AXP2101_ALDO2_MIN (500) +#define AXP2101_ALDO3_MIN (500) +#define AXP2101_ALDO4_MIN (500) +#define AXP2101_BLDO1_MIN (500) +#define AXP2101_BLDO2_MIN (500) +#define AXP2101_DLDO1_MIN (500) +#define AXP2101_DLDO2_MIN (500) +#define AXP2101_CPUSLDO_MIN (500) + +#define AXP2101_DCDC1_MAX (3400) +#define AXP2101_DCDC2_MAX (1540) +#define AXP2101_DCDC3_MAX (3400) +#define AXP2101_DCDC4_MAX (1840) +#define AXP2101_DCDC5_MAX (3700) +#define AXP2101_ALDO1_MAX (3500) +#define AXP2101_ALDO2_MAX (3500) +#define AXP2101_ALDO3_MAX (3500) +#define AXP2101_ALDO4_MAX (3500) +#define AXP2101_BLDO1_MAX (3500) +#define AXP2101_BLDO2_MAX (3500) +#define AXP2101_DLDO1_MAX (3500) +#define AXP2101_DLDO2_MAX (1400) +#define AXP2101_CPUSLDO_MAX (1400) + +enum class AXP2101_device_model_e : uint8_t { + Unselected = 0u, // >>>>> Don't change these values, they are probably stored in user-settings <<<<< + M5Stack_Core2_v1_1 = 1u, // https://docs.m5stack.com/en/core/Core2%20v1.1 + M5Stack_CoreS3 = 2u, // https://docs.m5stack.com/en/core/CoreS3 + LilyGO_TBeam_v1_2 = 3u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_V1.2.pdf + LilyGO_TBeamS3_v3 = 4u, // https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/blob/master/schematic/LilyGo_TBeam_S3_Core_V3.0.pdf + LilyGO_TPCie_v1_2 = 5u, // https://github.com/Xinyuan-LilyGO/LilyGo-T-PCIE/blob/master/schematic/T-PCIE-V1.2.pdf + + MAX, // Keep MAX as first after last device, devices must be sequentially numbered + UserDefined = 99u, // Keep UserDefined as last!!! +}; + +// The voltage registers mapped into an enum, don't change order without also changing AXP2101_intToRegister() +enum class AXP2101_registers_e : uint8_t { + dcdc1 = AXP2101_DCDC1_VOLTAGE_REG, + dcdc2 = AXP2101_DCDC2_VOLTAGE_REG, + dcdc3 = AXP2101_DCDC3_VOLTAGE_REG, + dcdc4 = AXP2101_DCDC4_VOLTAGE_REG, + dcdc5 = AXP2101_DCDC5_VOLTAGE_REG, + aldo1 = AXP2101_ALDO1_VOLTAGE_REG, + aldo2 = AXP2101_ALDO2_VOLTAGE_REG, + aldo3 = AXP2101_ALDO3_VOLTAGE_REG, + aldo4 = AXP2101_ALDO4_VOLTAGE_REG, + bldo1 = AXP2101_BLDO1_VOLTAGE_REG, + bldo2 = AXP2101_BLDO2_VOLTAGE_REG, + dldo1 = AXP2101_DLDO1_VOLTAGE_REG, + dldo2 = AXP2101_DLDO2_VOLTAGE_REG, + cpuldos = AXP2101_CPUSLDO_VOLTAGE_REG, + + // ADC inputs + vbat = AXP2101_VBAT_H_ADC_REG, + battemp = AXP2101_TS_H_ADC_REG, + vbus = AXP2101_VBUS_H_ADC_REG, + vsys = AXP2101_VSYS_H_ADC_REG, + chiptemp = AXP2101_TDIE_H_ADC_REG, + + // Above are settable pinstates/voltages of the AXP2101 + // Below are non-voltage and read-only values of the AXP2101, also update AXP2101_register_count when adding values + chargeled = AXP2101_CHGLED_REG, + batcharge = AXP2101_BAT_CHARGE_REG, + charging = AXP2101_COM_STAT1_REG, + batpresent = AXP2101_COM_STAT0_REG, + chipid = AXP2101_CHIP_ID_REG, + chargedet = AXP2101_CHARGE_DET_REG, +}; +constexpr int AXP2101_settings_count = 14; // Changeable settings +constexpr int AXP2101_register_count = 25; // All registers + +enum class AXP_pin_s : uint8_t { + Off = 0x00, // Max. 3 bits can be stored in settings! + On = 0x01, + Default = 0x02, // Don't update value or state on boot + Disabled = 0x03, // Port not connected, don't use + Protected = 0x07 // Don't try to change port value, can make the unit fail! +}; + +enum class AXP2101_chargeled_d : uint8_t { + Off = 0x00, + Flash_1Hz = 0x01, + Flash_4Hz = 0x02, + Steady_On = 0x03, + Protected = 0x07 // Don't try to change or not connected +}; + +enum class AXP2101_chargingState_e : int8_t { + Discharging = -1, + Standby = 0, + Charging = 1, +}; + +enum class AXP2101_chipid_e : uint8_t { + axp2101 = 0b01000111, // AXP2101 +}; + +enum class AXP2101_chargingDetail_e : uint8_t { + tricharge = 0b000, + precharge = 0b001, + constcharge = 0b010, + constvoltage = 0b011, + done = 0b100, + notcharging = 0b101, +}; + +enum class AXP2101_CV_charger_voltage_e : uint8_t { + reserved = 0, + limit_4_00V = 0b001, + limit_4_10V = 0b010, + limit_4_20V = 0b011, // default + limit_4_35V = 0b100, + limit_4_40V = 0b101 +}; + +enum class AXP2101_Linear_Charger_Vsys_dpm_e : uint8_t { + vsys_4_1V = 0, + vsys_4_2V, + vsys_4_3V, + vsys_4_4V, + vsys_4_5V, + vsys_4_6V, + vsys_4_7V, + vsys_4_8V +}; + +enum class AXP2101_VINDPM_e : uint8_t { + Vin_3_88V = 0, + Vin_3_96V, + Vin_4_04V, + Vin_4_12V, + Vin_4_20V, + Vin_4_28V, + Vin_4_36V, + Vin_4_44V, + Vin_4_52V, + Vin_4_60V, + Vin_4_68V, + Vin_4_76V, + Vin_4_84V, + Vin_4_92V, + Vin_5_00V, + Vin_5_08V +}; + +enum class AXP2101_InputCurrentLimit_e : uint8_t { + limit_100mA = 0, + limit_500mA, + limit_900mA, + limit_1000mA, + limit_1500mA, + limit_2000mA +}; + + +AXP2101_registers_e AXP2101_intToRegister(int reg); +uint16_t AXP2101_maxVoltage(AXP2101_registers_e reg); +uint16_t AXP2101_minVoltage(AXP2101_registers_e reg); +bool AXP2101_isPinDefault(AXP_pin_s pin); // Default, Disabled or Protected +bool AXP2101_isPinProtected(AXP_pin_s pin); // Disabled or Protected + +const __FlashStringHelper* toString(AXP2101_registers_e reg, + bool displayString = true); +const __FlashStringHelper* toString(AXP2101_device_model_e device, + bool displayString = true); +const __FlashStringHelper* toString(AXP_pin_s pin); +const __FlashStringHelper* toString(AXP2101_chargeled_d led); +const __FlashStringHelper* toString(AXP2101_chargingState_e state); +const __FlashStringHelper* toString(AXP2101_chipid_e chip); +const __FlashStringHelper* toString(AXP2101_chargingDetail_e chip); + +class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. +public: + + AXP2101_settings(); + AXP2101_settings(uint16_t _dcdc1, + uint16_t _dcdc2, + uint16_t _dcdc3, + uint16_t _dcdc4, + uint16_t _dcdc5, + uint16_t _aldo1, + uint16_t _aldo2, + uint16_t _aldo3, + uint16_t _aldo4, + uint16_t _bldo1, + uint16_t _bldo2, + uint16_t _dldo1, + uint16_t _dldo2, + uint16_t _cpuldos, + AXP_pin_s _en_dcdc1, + AXP_pin_s _en_dcdc2, + AXP_pin_s _en_dcdc3, + AXP_pin_s _en_dcdc4, + AXP_pin_s _en_dcdc5, + AXP_pin_s _en_aldo1, + AXP_pin_s _en_aldo2, + AXP_pin_s _en_aldo3, + AXP_pin_s _en_aldo4, + AXP_pin_s _en_bldo1, + AXP_pin_s _en_bldo2, + AXP_pin_s _en_dldo1, + AXP_pin_s _en_dldo2, + AXP_pin_s _en_cpuldos, + AXP2101_chargeled_d _chargeled); + void setVoltage(AXP2101_registers_e reg, + int voltage); + int getVoltage(AXP2101_registers_e reg, + bool realValue = true); + void setState(AXP2101_registers_e reg, + AXP_pin_s state); + AXP_pin_s getState(AXP2101_registers_e reg); + void setChargeLed(AXP2101_chargeled_d led); + AXP2101_chargeled_d getChargeLed(); + + bool getTS_disabled(); + void setTS_disabled(bool val); + + + // Reg 61: Iprechg Charger Settings + uint16_t getPreChargeCurrentLimit() const; + void setPreChargeCurrentLimit(uint16_t current_mA); + + // Reg 62: ICC Charger Settings + uint16_t getConstChargeCurrentLimit() const; + void setConstChargeCurrentLimit(uint16_t current_mA); + + // Reg 63: Iterm Charger Settings and Control + // Enable/Disable via chargeStates.term_cur_lim_en + uint16_t getTerminationChargeCurrentLimit() const; + void setTerminationChargeCurrentLimit(uint16_t current_mA); + + // Reg 64: CV Charger Voltage Settings + AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; + void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); + + // Reg 14: Minimum System Voltage Control + AXP2101_Linear_Charger_Vsys_dpm_e getCV_chargerVoltage() const; + void setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage); + + + + + +private: + + union { + struct { + uint16_t dcdc1; + uint16_t dcdc2; + uint16_t dcdc3; + uint16_t dcdc4; + uint16_t dcdc5; + uint16_t aldo1; + uint16_t aldo2; + uint16_t aldo3; + uint16_t aldo4; + uint16_t bldo1; + uint16_t bldo2; + uint16_t dldo1; + uint16_t dldo2; + uint16_t cpuldos; + } registers; + uint16_t registers_[AXP2101_settings_count]{}; + }; + union { + struct { // AXP_pin_s: Off / On / default / disabled / unavailable? / unused? / Protected + uint64_t en_dcdc1 : 3; // bit 0/1/2 + uint64_t en_dcdc2 : 3; // bit 3/4/5 + uint64_t en_dcdc3 : 3; // bit 6/7/8 + uint64_t en_dcdc4 : 3; // bit 9/10/11 + uint64_t en_dcdc5 : 3; // bit 12/13/14 + uint64_t en_aldo1 : 3; // bit 15/16/17 + uint64_t en_aldo2 : 3; // bit 18/19/20 + uint64_t en_aldo3 : 3; // bit 21/22/23 + uint64_t en_aldo4 : 3; // bit 24/25/26 + uint64_t en_bldo1 : 3; // bit 27/28/29 + uint64_t en_bldo2 : 3; // bit 30/31/32 + uint64_t en_dldo1 : 3; // bit 33/34/35 + uint64_t en_dldo2 : 3; // bit 36/37/38 + uint64_t en_cpuldos : 3; // bit 39/40/41 + uint64_t chargeled : 3; // bit 42/43/44 + + // Settings for external temperature sensor (TS) + uint64_t dis_TS_pin : 1; // bit 45, reg50 bit 4 + uint64_t TS_cur_src : 2; // bit 46/47, reg50 bit 3:2 + uint64_t TS_current : 2; // bit 48/49, reg50 bit 1:0 + + uint64_t en_unused : 13; // bit 50..63 // All bits defined + } pinStates; + uint64_t pinStates_{}; // 8 bytes + }; + + union { + struct { + uint64_t pre_chg_cur : 4; // reg 0x61: 25* N mA + uint64_t const_cur_lim : 5; // reg 0x62: 25* N mA if N <= 8, 200+100*(N-8) mA if N > 8 + uint64_t term_cur_lim_en : 1; // reg 0x63: Charging termination of current enable + uint64_t term_cur_lim : 4; // reg 0x63: 25* N mA + uint64_t chg_volt_lim : 3; // reg 0x64: + uint64_t thermal_thresh : 2; // reg 0x65: 00: 60deg, 01: 80deg, 10: 100deg, 11:120deg + uint64_t chg_timeout_ctrl: 8; // reg 0x67: + uint64_t bat_detection : 1; // reg 0x68: + uint64_t coincell_term_volt : 3; // reg 0x6A: 2.6~3.3V, 100mV/step, 8 steps + + uint64_t min_sys_voltage : 3; // reg 0x14: 4.1 + N*0.1V Linear Charger Vsys Voltage dpm + uint64_t inp_volt_limit : 4; // reg 0x15: Vindpm 3.88+N*0.08V + uint64_t inp_cur_limit : 3; // reg 0x16: + + + uint64_t en_unused : 23; + } chargeStates; + uint64_t chargeStates_{}; // 8 bytes + }; +}; + +extern AXP2101_settings AXP2101_deviceSettingsArray[]; + + +#endif // ifndef __AXP2101_SETTINGS_H From 20297f39f703e745abcacd31fb8336e9b87b2816 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 21 Jan 2025 17:05:01 +0100 Subject: [PATCH 21/50] [P139] Add charger settings to webform load/save --- lib/AXP2101/src/AXP2101_settings.cpp | 84 +++++++++++++++++- lib/AXP2101/src/AXP2101_settings.h | 18 +++- src/_P139_AXP2101.ino | 126 ++++++++++++++++++++++++++- 3 files changed, 220 insertions(+), 8 deletions(-) diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index 940004f244..329ee70ea5 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -108,6 +108,69 @@ const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge) { return F(""); } +const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage) { + switch (voltage) { + case AXP2101_CV_charger_voltage_e::limit_4_00V: return F("4.0"); + case AXP2101_CV_charger_voltage_e::limit_4_10V: return F("4.1"); + case AXP2101_CV_charger_voltage_e::limit_4_20V: return F("4.2"); + case AXP2101_CV_charger_voltage_e::limit_4_35V: return F("4.35"); + case AXP2101_CV_charger_voltage_e::limit_4_40V: return F("4.4"); + case AXP2101_CV_charger_voltage_e::reserved: break; + } + return F(""); +} + +const __FlashStringHelper* toString(AXP2101_Linear_Charger_Vsys_dpm_e vsys) { + switch (vsys) { + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V: return F("4.1"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V: return F("4.2"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V: return F("4.3"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V: return F("4.4"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V: return F("4.5"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V: return F("4.6"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V: return F("4.7"); + case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V: return F("4.8"); + } + return F(""); +} + + +const __FlashStringHelper* toString(AXP2101_VINDPM_e voltage) { + switch (voltage) { + case AXP2101_VINDPM_e::Vin_3_88V: return F("3.88"); + case AXP2101_VINDPM_e::Vin_3_96V: return F("3.96"); + case AXP2101_VINDPM_e::Vin_4_04V: return F("4.04"); + case AXP2101_VINDPM_e::Vin_4_12V: return F("4.12"); + case AXP2101_VINDPM_e::Vin_4_20V: return F("4.20"); + case AXP2101_VINDPM_e::Vin_4_28V: return F("4.28"); + case AXP2101_VINDPM_e::Vin_4_36V: return F("4.36"); + case AXP2101_VINDPM_e::Vin_4_44V: return F("4.44"); + case AXP2101_VINDPM_e::Vin_4_52V: return F("4.52"); + case AXP2101_VINDPM_e::Vin_4_60V: return F("4.60"); + case AXP2101_VINDPM_e::Vin_4_68V: return F("4.68"); + case AXP2101_VINDPM_e::Vin_4_76V: return F("4.76"); + case AXP2101_VINDPM_e::Vin_4_84V: return F("4.84"); + case AXP2101_VINDPM_e::Vin_4_92V: return F("4.92"); + case AXP2101_VINDPM_e::Vin_5_00V: return F("5.00"); + case AXP2101_VINDPM_e::Vin_5_08V: return F("5.08"); + } + return F(""); +} + + +const __FlashStringHelper* toString(AXP2101_InputCurrentLimit_e current) { + switch (current) { + case AXP2101_InputCurrentLimit_e::limit_100mA: return F("100"); + case AXP2101_InputCurrentLimit_e::limit_500mA: return F("500"); + case AXP2101_InputCurrentLimit_e::limit_900mA: return F("900"); + case AXP2101_InputCurrentLimit_e::limit_1000mA: return F("1000"); + case AXP2101_InputCurrentLimit_e::limit_1500mA: return F("1500"); + case AXP2101_InputCurrentLimit_e::limit_2000mA: return F("2000"); + } + return F(""); +} + + AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; @@ -460,15 +523,32 @@ void AXP2101_settings::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_ } } -AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_settings::getCV_chargerVoltage() const { +AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_settings::getLinear_Charger_Vsys_dpm() const { return static_cast(chargeStates.min_sys_voltage); } -void AXP2101_settings::setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { +void AXP2101_settings::setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { chargeStates.min_sys_voltage = static_cast(voltage); } +AXP2101_VINDPM_e AXP2101_settings::getVin_DPM() const { + return static_cast(chargeStates.inp_volt_limit); +} + +void AXP2101_settings::setVin_DPM(AXP2101_VINDPM_e voltage) { + chargeStates.inp_volt_limit = static_cast(voltage); +} + +AXP2101_InputCurrentLimit_e AXP2101_settings::getInputCurrentLimit() const { + return static_cast(chargeStates.inp_cur_limit); +} + +void AXP2101_settings::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { + chargeStates.inp_cur_limit = static_cast(current); +} + + /** * AXP2101 device class */ diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index caa19daeee..046375939a 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -284,7 +284,12 @@ const __FlashStringHelper* toString(AXP_pin_s pin); const __FlashStringHelper* toString(AXP2101_chargeled_d led); const __FlashStringHelper* toString(AXP2101_chargingState_e state); const __FlashStringHelper* toString(AXP2101_chipid_e chip); -const __FlashStringHelper* toString(AXP2101_chargingDetail_e chip); +const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge); +const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage); +const __FlashStringHelper* toString(AXP2101_Linear_Charger_Vsys_dpm_e vsys); +const __FlashStringHelper* toString(AXP2101_VINDPM_e voltage); +const __FlashStringHelper* toString(AXP2101_InputCurrentLimit_e current); + class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: @@ -351,11 +356,16 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); // Reg 14: Minimum System Voltage Control - AXP2101_Linear_Charger_Vsys_dpm_e getCV_chargerVoltage() const; - void setCV_chargerVoltage(AXP2101_Linear_Charger_Vsys_dpm_e voltage); + AXP2101_Linear_Charger_Vsys_dpm_e getLinear_Charger_Vsys_dpm() const; + void setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage); - + // Reg 15: Input Voltage Limit + AXP2101_VINDPM_e getVin_DPM() const; + void setVin_DPM(AXP2101_VINDPM_e voltage); + // Reg 16: Input Current Limit + AXP2101_InputCurrentLimit_e getInputCurrentLimit() const; + void setInputCurrentLimit(AXP2101_InputCurrentLimit_e current); private: diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index f62dfef315..9849e7fa52 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -215,12 +215,128 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } { - addFormCheckBox(F("Disable TS pin"), F("dis_TS"), P139_data->_settings.getTS_disabled()); + const __FlashStringHelper *names[] = { + toString(AXP2101_CV_charger_voltage_e::limit_4_00V), + toString(AXP2101_CV_charger_voltage_e::limit_4_10V), + toString(AXP2101_CV_charger_voltage_e::limit_4_20V), + toString(AXP2101_CV_charger_voltage_e::limit_4_35V), + toString(AXP2101_CV_charger_voltage_e::limit_4_40V), + }; + const int values[] = { + static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_10V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_35V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("CV Charger Voltage"), F("cv_volt"), + valueCount, + names, values, + static_cast(P139_data->_settings.getCV_chargeVoltage())); + addUnit(F("V")); + } + { + const __FlashStringHelper *names[] = { + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), + }; + const int values[] = { + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Minimum System Voltage"), F("min_vsys"), + valueCount, + names, values, + static_cast(P139_data->_settings.getLinear_Charger_Vsys_dpm())); + addUnit(F("V")); + } - //addFormNumericBox(F("Charge Current")) + { + const __FlashStringHelper *names[] = { + toString(AXP2101_VINDPM_e::Vin_3_88V), + toString(AXP2101_VINDPM_e::Vin_3_96V), + toString(AXP2101_VINDPM_e::Vin_4_04V), + toString(AXP2101_VINDPM_e::Vin_4_12V), + toString(AXP2101_VINDPM_e::Vin_4_20V), + toString(AXP2101_VINDPM_e::Vin_4_28V), + toString(AXP2101_VINDPM_e::Vin_4_36V), + toString(AXP2101_VINDPM_e::Vin_4_44V), + toString(AXP2101_VINDPM_e::Vin_4_52V), + toString(AXP2101_VINDPM_e::Vin_4_60V), + toString(AXP2101_VINDPM_e::Vin_4_68V), + toString(AXP2101_VINDPM_e::Vin_4_76V), + toString(AXP2101_VINDPM_e::Vin_4_84V), + toString(AXP2101_VINDPM_e::Vin_4_92V), + toString(AXP2101_VINDPM_e::Vin_5_00V), + toString(AXP2101_VINDPM_e::Vin_5_08V), + }; + const int values[] = { + static_cast(AXP2101_VINDPM_e::Vin_3_88V), + static_cast(AXP2101_VINDPM_e::Vin_3_96V), + static_cast(AXP2101_VINDPM_e::Vin_4_04V), + static_cast(AXP2101_VINDPM_e::Vin_4_12V), + static_cast(AXP2101_VINDPM_e::Vin_4_20V), + static_cast(AXP2101_VINDPM_e::Vin_4_28V), + static_cast(AXP2101_VINDPM_e::Vin_4_36V), + static_cast(AXP2101_VINDPM_e::Vin_4_44V), + static_cast(AXP2101_VINDPM_e::Vin_4_52V), + static_cast(AXP2101_VINDPM_e::Vin_4_60V), + static_cast(AXP2101_VINDPM_e::Vin_4_68V), + static_cast(AXP2101_VINDPM_e::Vin_4_76V), + static_cast(AXP2101_VINDPM_e::Vin_4_84V), + static_cast(AXP2101_VINDPM_e::Vin_4_92V), + static_cast(AXP2101_VINDPM_e::Vin_5_00V), + static_cast(AXP2101_VINDPM_e::Vin_5_08V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Min Vin DPM Voltage"), F("vin_dpm"), + valueCount, + names, values, + static_cast(P139_data->_settings.getVin_DPM())); + addUnit(F("V")); + } + { + const __FlashStringHelper *names[] = { + toString(AXP2101_InputCurrentLimit_e::limit_100mA), + toString(AXP2101_InputCurrentLimit_e::limit_500mA), + toString(AXP2101_InputCurrentLimit_e::limit_900mA), + toString(AXP2101_InputCurrentLimit_e::limit_1000mA), + toString(AXP2101_InputCurrentLimit_e::limit_1500mA), + toString(AXP2101_InputCurrentLimit_e::limit_2000mA), + }; + const int values[] = { + static_cast(AXP2101_InputCurrentLimit_e::limit_100mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_500mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_900mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_1000mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_1500mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_2000mA), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Input Current Limit"), F("cur_lim_in"), + valueCount, + names, values, + static_cast(P139_data->_settings.getInputCurrentLimit())); + addUnit(F("mA")); } + addFormCheckBox(F("Disable TS pin"), F("dis_TS"), P139_data->_settings.getTS_disabled()); + addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); addFormSubHeader(F("Hardware outputs AXP2101")); @@ -390,6 +506,12 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) } P139_data->_settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + + P139_data->_settings.setCV_chargeVoltage(static_cast(getFormItemInt(F("cv_volt")))); + P139_data->_settings.setLinear_Charger_Vsys_dpm(static_cast(getFormItemInt(F("min_vsys")))); + P139_data->_settings.setVin_DPM(static_cast(getFormItemInt(F("vin_dpm")))); + P139_data->_settings.setInputCurrentLimit(static_cast(getFormItemInt(F("cur_lim_in")))); + P139_data->_settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); P139_data->saveSettings(event); From df6e39f1d5adf0971902c36c94f749813ecc9eb1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 21 Jan 2025 17:23:10 +0100 Subject: [PATCH 22/50] [P139] Move webform_load and _save to P139_data_struct class --- src/_P139_AXP2101.ino | 290 +-------------------- src/src/PluginStructs/P139_data_struct.cpp | 279 ++++++++++++++++++++ src/src/PluginStructs/P139_data_struct.h | 3 + 3 files changed, 292 insertions(+), 280 deletions(-) diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 9849e7fa52..aa92ce5d3e 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -180,7 +180,6 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { - const bool isPowerManagerTask = Settings.isPowerManagerTask(event->TaskIndex); bool created_new = false; P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -190,258 +189,16 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) created_new = true; } - if (nullptr == P139_data) { - break; - } - - { - const __FlashStringHelper *chargeledNames[] = { - toString(AXP2101_chargeled_d::Off), - toString(AXP2101_chargeled_d::Flash_1Hz), - toString(AXP2101_chargeled_d::Flash_4Hz), - toString(AXP2101_chargeled_d::Steady_On), - }; - const int chargeledValues[] = { - static_cast(AXP2101_chargeled_d::Off), - static_cast(AXP2101_chargeled_d::Flash_1Hz), - static_cast(AXP2101_chargeled_d::Flash_4Hz), - static_cast(AXP2101_chargeled_d::Steady_On), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(chargeledValues); - addFormSelector(F("Charge LED"), F("led"), - valueCount, - chargeledNames, chargeledValues, - static_cast(P139_data->_settings.getChargeLed())); - } - - { - const __FlashStringHelper *names[] = { - toString(AXP2101_CV_charger_voltage_e::limit_4_00V), - toString(AXP2101_CV_charger_voltage_e::limit_4_10V), - toString(AXP2101_CV_charger_voltage_e::limit_4_20V), - toString(AXP2101_CV_charger_voltage_e::limit_4_35V), - toString(AXP2101_CV_charger_voltage_e::limit_4_40V), - }; - const int values[] = { - static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_10V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_35V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("CV Charger Voltage"), F("cv_volt"), - valueCount, - names, values, - static_cast(P139_data->_settings.getCV_chargeVoltage())); - addUnit(F("V")); - } - { - const __FlashStringHelper *names[] = { - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), - }; - const int values[] = { - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Minimum System Voltage"), F("min_vsys"), - valueCount, - names, values, - static_cast(P139_data->_settings.getLinear_Charger_Vsys_dpm())); - addUnit(F("V")); - } - - { - const __FlashStringHelper *names[] = { - toString(AXP2101_VINDPM_e::Vin_3_88V), - toString(AXP2101_VINDPM_e::Vin_3_96V), - toString(AXP2101_VINDPM_e::Vin_4_04V), - toString(AXP2101_VINDPM_e::Vin_4_12V), - toString(AXP2101_VINDPM_e::Vin_4_20V), - toString(AXP2101_VINDPM_e::Vin_4_28V), - toString(AXP2101_VINDPM_e::Vin_4_36V), - toString(AXP2101_VINDPM_e::Vin_4_44V), - toString(AXP2101_VINDPM_e::Vin_4_52V), - toString(AXP2101_VINDPM_e::Vin_4_60V), - toString(AXP2101_VINDPM_e::Vin_4_68V), - toString(AXP2101_VINDPM_e::Vin_4_76V), - toString(AXP2101_VINDPM_e::Vin_4_84V), - toString(AXP2101_VINDPM_e::Vin_4_92V), - toString(AXP2101_VINDPM_e::Vin_5_00V), - toString(AXP2101_VINDPM_e::Vin_5_08V), - }; - const int values[] = { - static_cast(AXP2101_VINDPM_e::Vin_3_88V), - static_cast(AXP2101_VINDPM_e::Vin_3_96V), - static_cast(AXP2101_VINDPM_e::Vin_4_04V), - static_cast(AXP2101_VINDPM_e::Vin_4_12V), - static_cast(AXP2101_VINDPM_e::Vin_4_20V), - static_cast(AXP2101_VINDPM_e::Vin_4_28V), - static_cast(AXP2101_VINDPM_e::Vin_4_36V), - static_cast(AXP2101_VINDPM_e::Vin_4_44V), - static_cast(AXP2101_VINDPM_e::Vin_4_52V), - static_cast(AXP2101_VINDPM_e::Vin_4_60V), - static_cast(AXP2101_VINDPM_e::Vin_4_68V), - static_cast(AXP2101_VINDPM_e::Vin_4_76V), - static_cast(AXP2101_VINDPM_e::Vin_4_84V), - static_cast(AXP2101_VINDPM_e::Vin_4_92V), - static_cast(AXP2101_VINDPM_e::Vin_5_00V), - static_cast(AXP2101_VINDPM_e::Vin_5_08V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Min Vin DPM Voltage"), F("vin_dpm"), - valueCount, - names, values, - static_cast(P139_data->_settings.getVin_DPM())); - addUnit(F("V")); - } - - { - const __FlashStringHelper *names[] = { - toString(AXP2101_InputCurrentLimit_e::limit_100mA), - toString(AXP2101_InputCurrentLimit_e::limit_500mA), - toString(AXP2101_InputCurrentLimit_e::limit_900mA), - toString(AXP2101_InputCurrentLimit_e::limit_1000mA), - toString(AXP2101_InputCurrentLimit_e::limit_1500mA), - toString(AXP2101_InputCurrentLimit_e::limit_2000mA), - }; - const int values[] = { - static_cast(AXP2101_InputCurrentLimit_e::limit_100mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_500mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_900mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_1000mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_1500mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_2000mA), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Input Current Limit"), F("cur_lim_in"), - valueCount, - names, values, - static_cast(P139_data->_settings.getInputCurrentLimit())); - addUnit(F("mA")); - } - - addFormCheckBox(F("Disable TS pin"), F("dis_TS"), P139_data->_settings.getTS_disabled()); - - addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); - - addFormSubHeader(F("Hardware outputs AXP2101")); - - { - if (P139_CONFIG_PREDEFINED > 0) { - P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; - P139_CONFIG_PREDEFINED = 0; - P139_data->applySettings(static_cast(P139_CURRENT_PREDEFINED)); - } - const __FlashStringHelper *predefinedNames[] = { - toString(AXP2101_device_model_e::Unselected), - toString(AXP2101_device_model_e::M5Stack_Core2_v1_1), - toString(AXP2101_device_model_e::M5Stack_CoreS3), - toString(AXP2101_device_model_e::LilyGO_TBeam_v1_2), - toString(AXP2101_device_model_e::LilyGO_TBeamS3_v3), - toString(AXP2101_device_model_e::LilyGO_TPCie_v1_2), - toString(AXP2101_device_model_e::UserDefined) // keep last !! - }; - const int predefinedValues[] = { - static_cast(AXP2101_device_model_e::Unselected), - static_cast(AXP2101_device_model_e::M5Stack_Core2_v1_1), - static_cast(AXP2101_device_model_e::M5Stack_CoreS3), - static_cast(AXP2101_device_model_e::LilyGO_TBeam_v1_2), - static_cast(AXP2101_device_model_e::LilyGO_TBeamS3_v3), - static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), - static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! - constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); - addFormSelector(F("Predefined device configuration"), F("predef"), - valueCount, - predefinedNames, predefinedValues, 0, !isPowerManagerTask); - - if (!isPowerManagerTask) { - addFormNote(F("Page will reload when selection is changed.")); - } - - const AXP2101_device_model_e device = static_cast(P139_CURRENT_PREDEFINED); - - if (AXP2101_device_model_e::Unselected != device) { - addFormNote(concat(F("Last selected: "), toString(device))); - - if (AXP2101_device_model_e::UserDefined == device) { - addHtml(F("

Warning: " - "Configuring invalid values can damage your device or render it useless!
")); - } - } - } + if (nullptr != P139_data) { + P139_data->webform_load(event); - { - const __FlashStringHelper *bootStates[] = { - toString(AXP_pin_s::Off), - toString(AXP_pin_s::On), - toString(AXP_pin_s::Default), - }; - const int bootStateValues[] = { - static_cast(AXP_pin_s::Off), - static_cast(AXP_pin_s::On), - static_cast(AXP_pin_s::Default), - }; - - // Don't include Disabled or Protected here, not user-selectable - constexpr int bootStatesCount = NR_ELEMENTS(bootStateValues); - - addRowLabel(F("Output ports")); - - html_table(EMPTY_STRING); - html_table_header(F("Name"), 100); - html_table_header(F("Voltage (mV)"), 270); - html_table_header(F("Pin state"), 150); - - for (int s = 0; s < AXP2101_settings_count; ++s) { - const AXP2101_registers_e reg = AXP2101_intToRegister(s); - const AXP_pin_s pin = P139_data->_settings.getState(reg); - html_TR_TD(); - addHtml(toString(reg)); - html_TD(); - addNumericBox(toString(reg, false), - P139_data->_settings.getVoltage(reg, false), - -1, - AXP2101_maxVoltage(reg), - AXP2101_isPinDefault(pin)); - addUnit(strformat(F("range %d - %d"), AXP2101_minVoltage(reg), AXP2101_maxVoltage(reg))); - html_TD(); - - if (AXP2101_isPinProtected(pin)) { - addUnit(toString(pin)); - } else { - addSelector(concat(F("ps"), toString(reg, false)), - bootStatesCount, - bootStates, - bootStateValues, - nullptr, - static_cast(pin)); - } + if (created_new) { + delete P139_data; } - html_end_table(); - addFormNote(F("Check your device documentation for what is connected to each output.")); - } - - if (created_new) { - delete P139_data; + success = true; } - success = true; break; } @@ -475,14 +232,6 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_SAVE: { - for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { - sensorTypeHelper_saveOutputSelector(event, P139_CONFIG_BASE + i, i, - toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); - } - - P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); - P139_SET_GENERATE_EVENTS(isFormItemChecked(F("events"))); - bool created_new = false; P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); @@ -492,35 +241,16 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) created_new = true; } - if (nullptr == P139_data) { - break; - } - - for (int s = 0; s < AXP2101_settings_count; ++s) { - const AXP2101_registers_e reg = AXP2101_intToRegister(s); + if (nullptr != P139_data) { + P139_data->webform_save(event); - if (!AXP2101_isPinProtected(P139_data->_settings.getState(reg))) { - P139_data->_settings.setVoltage(reg, getFormItemInt(toString(reg, false))); - P139_data->_settings.setState(reg, static_cast(getFormItemInt(concat(F("ps"), toString(reg, false))))); + if (created_new) { + delete P139_data; } - } - - P139_data->_settings.setChargeLed(static_cast(getFormItemInt(F("led")))); - P139_data->_settings.setCV_chargeVoltage(static_cast(getFormItemInt(F("cv_volt")))); - P139_data->_settings.setLinear_Charger_Vsys_dpm(static_cast(getFormItemInt(F("min_vsys")))); - P139_data->_settings.setVin_DPM(static_cast(getFormItemInt(F("vin_dpm")))); - P139_data->_settings.setInputCurrentLimit(static_cast(getFormItemInt(F("cur_lim_in")))); - - P139_data->_settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); - - P139_data->saveSettings(event); - - if (created_new) { - delete P139_data; + success = true; } - success = true; break; } diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 2f9894b85d..e863c9f279 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -107,6 +107,285 @@ bool P139_data_struct::applySettings(AXP2101_device_model_e device) { return false; } +void P139_data_struct::webform_load(struct EventStruct *event) { + const bool isPowerManagerTask = Settings.isPowerManagerTask(event->TaskIndex); + + { + const __FlashStringHelper *chargeledNames[] = { + toString(AXP2101_chargeled_d::Off), + toString(AXP2101_chargeled_d::Flash_1Hz), + toString(AXP2101_chargeled_d::Flash_4Hz), + toString(AXP2101_chargeled_d::Steady_On), + }; + const int chargeledValues[] = { + static_cast(AXP2101_chargeled_d::Off), + static_cast(AXP2101_chargeled_d::Flash_1Hz), + static_cast(AXP2101_chargeled_d::Flash_4Hz), + static_cast(AXP2101_chargeled_d::Steady_On), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(chargeledValues); + addFormSelector(F("Charge LED"), F("led"), + valueCount, + chargeledNames, chargeledValues, + static_cast(_settings.getChargeLed())); + } + + { + const __FlashStringHelper *names[] = { + toString(AXP2101_CV_charger_voltage_e::limit_4_00V), + toString(AXP2101_CV_charger_voltage_e::limit_4_10V), + toString(AXP2101_CV_charger_voltage_e::limit_4_20V), + toString(AXP2101_CV_charger_voltage_e::limit_4_35V), + toString(AXP2101_CV_charger_voltage_e::limit_4_40V), + }; + const int values[] = { + static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_10V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_35V), + static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("CV Charger Voltage"), F("cv_volt"), + valueCount, + names, values, + static_cast(_settings.getCV_chargeVoltage())); + addUnit(F("V")); + } + { + const __FlashStringHelper *names[] = { + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), + toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), + }; + const int values[] = { + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), + static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Minimum System Voltage"), F("min_vsys"), + valueCount, + names, values, + static_cast(_settings.getLinear_Charger_Vsys_dpm())); + addUnit(F("V")); + } + + { + const __FlashStringHelper *names[] = { + toString(AXP2101_VINDPM_e::Vin_3_88V), + toString(AXP2101_VINDPM_e::Vin_3_96V), + toString(AXP2101_VINDPM_e::Vin_4_04V), + toString(AXP2101_VINDPM_e::Vin_4_12V), + toString(AXP2101_VINDPM_e::Vin_4_20V), + toString(AXP2101_VINDPM_e::Vin_4_28V), + toString(AXP2101_VINDPM_e::Vin_4_36V), + toString(AXP2101_VINDPM_e::Vin_4_44V), + toString(AXP2101_VINDPM_e::Vin_4_52V), + toString(AXP2101_VINDPM_e::Vin_4_60V), + toString(AXP2101_VINDPM_e::Vin_4_68V), + toString(AXP2101_VINDPM_e::Vin_4_76V), + toString(AXP2101_VINDPM_e::Vin_4_84V), + toString(AXP2101_VINDPM_e::Vin_4_92V), + toString(AXP2101_VINDPM_e::Vin_5_00V), + toString(AXP2101_VINDPM_e::Vin_5_08V), + }; + const int values[] = { + static_cast(AXP2101_VINDPM_e::Vin_3_88V), + static_cast(AXP2101_VINDPM_e::Vin_3_96V), + static_cast(AXP2101_VINDPM_e::Vin_4_04V), + static_cast(AXP2101_VINDPM_e::Vin_4_12V), + static_cast(AXP2101_VINDPM_e::Vin_4_20V), + static_cast(AXP2101_VINDPM_e::Vin_4_28V), + static_cast(AXP2101_VINDPM_e::Vin_4_36V), + static_cast(AXP2101_VINDPM_e::Vin_4_44V), + static_cast(AXP2101_VINDPM_e::Vin_4_52V), + static_cast(AXP2101_VINDPM_e::Vin_4_60V), + static_cast(AXP2101_VINDPM_e::Vin_4_68V), + static_cast(AXP2101_VINDPM_e::Vin_4_76V), + static_cast(AXP2101_VINDPM_e::Vin_4_84V), + static_cast(AXP2101_VINDPM_e::Vin_4_92V), + static_cast(AXP2101_VINDPM_e::Vin_5_00V), + static_cast(AXP2101_VINDPM_e::Vin_5_08V), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Min Vin DPM Voltage"), F("vin_dpm"), + valueCount, + names, values, + static_cast(_settings.getVin_DPM())); + addUnit(F("V")); + } + + { + const __FlashStringHelper *names[] = { + toString(AXP2101_InputCurrentLimit_e::limit_100mA), + toString(AXP2101_InputCurrentLimit_e::limit_500mA), + toString(AXP2101_InputCurrentLimit_e::limit_900mA), + toString(AXP2101_InputCurrentLimit_e::limit_1000mA), + toString(AXP2101_InputCurrentLimit_e::limit_1500mA), + toString(AXP2101_InputCurrentLimit_e::limit_2000mA), + }; + const int values[] = { + static_cast(AXP2101_InputCurrentLimit_e::limit_100mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_500mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_900mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_1000mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_1500mA), + static_cast(AXP2101_InputCurrentLimit_e::limit_2000mA), + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Input Current Limit"), F("cur_lim_in"), + valueCount, + names, values, + static_cast(_settings.getInputCurrentLimit())); + addUnit(F("mA")); + } + + addFormCheckBox(F("Disable TS pin"), F("dis_TS"), _settings.getTS_disabled()); + + addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); + + addFormSubHeader(F("Hardware outputs AXP2101")); + + { + if (P139_CONFIG_PREDEFINED > 0) { + P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; + P139_CONFIG_PREDEFINED = 0; + applySettings(static_cast(P139_CURRENT_PREDEFINED)); + } + const __FlashStringHelper *predefinedNames[] = { + toString(AXP2101_device_model_e::Unselected), + toString(AXP2101_device_model_e::M5Stack_Core2_v1_1), + toString(AXP2101_device_model_e::M5Stack_CoreS3), + toString(AXP2101_device_model_e::LilyGO_TBeam_v1_2), + toString(AXP2101_device_model_e::LilyGO_TBeamS3_v3), + toString(AXP2101_device_model_e::LilyGO_TPCie_v1_2), + toString(AXP2101_device_model_e::UserDefined) // keep last !! + }; + const int predefinedValues[] = { + static_cast(AXP2101_device_model_e::Unselected), + static_cast(AXP2101_device_model_e::M5Stack_Core2_v1_1), + static_cast(AXP2101_device_model_e::M5Stack_CoreS3), + static_cast(AXP2101_device_model_e::LilyGO_TBeam_v1_2), + static_cast(AXP2101_device_model_e::LilyGO_TBeamS3_v3), + static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), + static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! + constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); + addFormSelector(F("Predefined device configuration"), F("predef"), + valueCount, + predefinedNames, predefinedValues, 0, !isPowerManagerTask); + + if (!isPowerManagerTask) { + addFormNote(F("Page will reload when selection is changed.")); + } + + const AXP2101_device_model_e device = static_cast(P139_CURRENT_PREDEFINED); + + if (AXP2101_device_model_e::Unselected != device) { + addFormNote(concat(F("Last selected: "), toString(device))); + + if (AXP2101_device_model_e::UserDefined == device) { + addHtml(F("
Warning: " + "Configuring invalid values can damage your device or render it useless!
")); + } + } + } + + { + const __FlashStringHelper *bootStates[] = { + toString(AXP_pin_s::Off), + toString(AXP_pin_s::On), + toString(AXP_pin_s::Default), + }; + const int bootStateValues[] = { + static_cast(AXP_pin_s::Off), + static_cast(AXP_pin_s::On), + static_cast(AXP_pin_s::Default), + }; + + // Don't include Disabled or Protected here, not user-selectable + constexpr int bootStatesCount = NR_ELEMENTS(bootStateValues); + + addRowLabel(F("Output ports")); + + html_table(EMPTY_STRING); + html_table_header(F("Name"), 100); + html_table_header(F("Voltage (mV)"), 270); + html_table_header(F("Pin state"), 150); + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + const AXP_pin_s pin = _settings.getState(reg); + html_TR_TD(); + addHtml(toString(reg)); + html_TD(); + addNumericBox(toString(reg, false), + _settings.getVoltage(reg, false), + -1, + AXP2101_maxVoltage(reg), + AXP2101_isPinDefault(pin)); + addUnit(strformat(F("range %d - %d"), AXP2101_minVoltage(reg), AXP2101_maxVoltage(reg))); + html_TD(); + + if (AXP2101_isPinProtected(pin)) { + addUnit(toString(pin)); + } else { + addSelector(concat(F("ps"), toString(reg, false)), + bootStatesCount, + bootStates, + bootStateValues, + nullptr, + static_cast(pin)); + } + } + html_end_table(); + + addFormNote(F("Check your device documentation for what is connected to each output.")); + } +} + + +void P139_data_struct::webform_save(struct EventStruct *event) { + for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { + sensorTypeHelper_saveOutputSelector(event, P139_CONFIG_BASE + i, i, + toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); + } + + P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + P139_SET_GENERATE_EVENTS(isFormItemChecked(F("events"))); + + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); + + if (!AXP2101_isPinProtected(_settings.getState(reg))) { + _settings.setVoltage(reg, getFormItemInt(toString(reg, false))); + _settings.setState(reg, static_cast(getFormItemInt(concat(F("ps"), toString(reg, false))))); + } + } + + _settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + + _settings.setCV_chargeVoltage(static_cast(getFormItemInt(F("cv_volt")))); + _settings.setLinear_Charger_Vsys_dpm(static_cast(getFormItemInt(F("min_vsys")))); + _settings.setVin_DPM(static_cast(getFormItemInt(F("vin_dpm")))); + _settings.setInputCurrentLimit(static_cast(getFormItemInt(F("cur_lim_in")))); + + _settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); + + saveSettings(event); +} + + // **************************************************************************/ // plugin_read: Read the values and send to controller(s) // **************************************************************************/ diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index 8e6bf49853..a024a5c392 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -56,6 +56,9 @@ struct P139_data_struct : public PluginTaskData_base { void outputSettings(struct EventStruct *event); bool applySettings(AXP2101_device_model_e device); + void webform_load(struct EventStruct *event); + void webform_save(struct EventStruct *event); + AXP2101_settings _settings; private: From 5485373dabbf03a78a1a6076d49a8f698b3c1970 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 21 Jan 2025 18:43:05 +0100 Subject: [PATCH 23/50] [P139] Add functions to apply settings to AXP2101 --- lib/AXP2101/src/AXP2101.cpp | 126 ++++++++++++++++++++++++++++- lib/AXP2101/src/AXP2101.h | 30 +++++++ lib/AXP2101/src/AXP2101_settings.h | 7 +- 3 files changed, 160 insertions(+), 3 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index bd7aecf7c8..d1f6c0c55c 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -516,13 +516,135 @@ AXP2101_chargeled_d AXP2101::getChargeLed() { } bool AXP2101::getTS_disabled() { - return bitGet(AXP2101_TS_PIN_CTRL, 0b00010000); + return bitGet(AXP2101_TS_PIN_CTRL_REG, 0b00010000); } void AXP2101::setTS_disabled(bool val) { - bitOnOff(val, AXP2101_TS_PIN_CTRL, 0b00010000); + bitOnOff(val, AXP2101_TS_PIN_CTRL_REG, 0b00010000); } +// Reg 61: Iprechg Charger settings +uint16_t AXP2101::getPreChargeCurrentLimit() { + // AXP2101_IPRECHG_REG + // bit 3:0 + uint8_t reg = readRegister8(_addr,AXP2101_IPRECHG_REG); + return (reg & 0b1111) * 25; +} +void AXP2101::setPreChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 200) { + current_mA = 200; + } + writeRegister8(_addr, AXP2101_IPRECHG_REG, current_mA / 25); +} + +// Reg 62: ICC Charger settings +uint16_t AXP2101::getChargeCurrentLimit() { + // AXP2101_ICC_CHARGER_SETTING_REG + // bit 4:0 + uint8_t reg = readRegister8(_addr, AXP2101_ICC_CHARGER_SETTING_REG); + reg &= 0b11111; + if (reg <= 8) { + return reg * 25; + } + return (reg - 8) * 100 + 200; +} +void AXP2101::setChargeCurrentLimit(uint16_t current_mA) { + if (current_mA > 1000) { + current_mA = 1000; + } + + const uint8_t reg = (current_mA <= 200) + ? current_mA / 25 + : ((current_mA - 200) / 100) + 8; + writeRegister8(_addr, AXP2101_ICC_CHARGER_SETTING_REG, reg); +} + +// Reg 63: Iterm Charger settings and Control +// Enable/Disable via chargeStates.term_cur_lim_en +uint16_t AXP2101::getTerminationChargeCurrentLimit() { + // AXP2101_CHARGER_SETTING_REG + // bit 4: enable/disable + // bit 3:0 + uint8_t reg = readRegister8(_addr,AXP2101_CHARGER_SETTING_REG); + return (reg & 0b1111) * 25; +} +void AXP2101::setTerminationChargeCurrentLimit(uint16_t current_mA) { + setTerminationChargeCurrentLimit + setTerminationChargeCurrentLimit(current_mA) + if (current_mA > 200) { + current_mA = 200; + } + constexpr uint8_t enable_mask = 0b00010000; + const bool enabled = bitGet(AXP2101_CHARGER_SETTING_REG, enable_mask); + uint8_t reg = current_mA / 25; + if (enabled) { + reg &= enable_mask; + } + writeRegister8(_addr, AXP2101_CHARGER_SETTING_REG, reg); +} + +void AXP2101::setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable) { + if (current_mA > 200) { + current_mA = 200; + } + constexpr uint8_t enable_mask = 0b00010000; + uint8_t reg = current_mA / 25; + if (enable) { + reg &= enable_mask; + } + writeRegister8(_addr, AXP2101_CHARGER_SETTING_REG, reg); +} + + +// Reg 64: CV Charger Voltage settings +AXP2101_CV_charger_voltage_e AXP2101::getCV_chargeVoltage() { + // AXP2101_CV_CHARGER_SETTING_REG + // bit 2:0 + uint8_t reg = readRegister8(_addr,AXP2101_CV_CHARGER_SETTING_REG); + reg &= 0b111; + return static_cast(reg); +} +void AXP2101::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { + uint8_t reg = static_cast(voltage_mV); + if (reg > static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) { + // Set to a default safe limit + reg = static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V); + } + writeRegister8(_addr, AXP2101_CV_CHARGER_SETTING_REG, reg); +} + +// Reg 14: Minimum System Voltage Control +AXP2101_Linear_Charger_Vsys_dpm_e AXP2101::getLinear_Charger_Vsys_dpm() { + // AXP2101_MIN_VSYS_REG + // bit 6:4 + uint8_t reg = readRegister8(_addr, AXP2101_MIN_VSYS_REG); + reg &= 0b01110000; + reg >>= 4; + return static_cast(reg); +} +void AXP2101::setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage) { + uint8_t reg = static_cast(voltage); + reg <<= 4; + writeRegister8(_addr, AXP2101_MIN_VSYS_REG, reg); +} + +// Reg 15: Input Voltage Limit +AXP2101_VINDPM_e AXP2101::getVin_DPM() { +// AXP2101_VIN_DPM_REG +// bit 3:0 +} +void AXP2101::setVin_DPM(AXP2101_VINDPM_e voltage) { + +} + +// Reg 16: Input Current Limit +AXP2101_InputCurrentLimit_e AXP2101::getInputCurrentLimit() { +// AXP2101_IN_CURRENT_LIMIT_REG +// bit 2:0 +} +void AXP2101::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { + +} uint8_t AXP2101::getBatCharge() { return readRegister8(_addr, AXP2101_BAT_CHARGE_REG); diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index dadbd7ff70..8e26b9b5e0 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -97,6 +97,36 @@ class AXP2101 { bool getTS_disabled(); void setTS_disabled(bool val); + // Reg 61: Iprechg Charger Settings + uint16_t getPreChargeCurrentLimit() ; + void setPreChargeCurrentLimit(uint16_t current_mA); + + // Reg 62: ICC Charger Settings + uint16_t getChargeCurrentLimit() ; + void setChargeCurrentLimit(uint16_t current_mA); + + // Reg 63: Iterm Charger Settings and Control + // Enable/Disable via chargeStates.term_cur_lim_en + uint16_t getTerminationChargeCurrentLimit() ; + void setTerminationChargeCurrentLimit(uint16_t current_mA); + void setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable); + + // Reg 64: CV Charger Voltage Settings + AXP2101_CV_charger_voltage_e getCV_chargeVoltage() ; + void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); + + // Reg 14: Minimum System Voltage Control + AXP2101_Linear_Charger_Vsys_dpm_e getLinear_Charger_Vsys_dpm() ; + void setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage); + + // Reg 15: Input Voltage Limit + AXP2101_VINDPM_e getVin_DPM() ; + void setVin_DPM(AXP2101_VINDPM_e voltage); + + // Reg 16: Input Current Limit + AXP2101_InputCurrentLimit_e getInputCurrentLimit() ; + void setInputCurrentLimit(AXP2101_InputCurrentLimit_e current); + uint8_t getBatCharge(); AXP2101_chargingState_e getChargingState(); diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index 046375939a..75a0a81d4f 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -77,6 +77,9 @@ #define AXP2101_CHIP_ID_REG (0x03) #define AXP2101_CHARGE_DET_REG (0x04) // Fake, not a usable register! #define AXP2101_PMU_CONFIG_REG (0x10) +#define AXP2101_MIN_VSYS_REG (0x14) +#define AXP2101_VIN_DPM_REG (0x15) +#define AXP2101_IN_CURRENT_LIMIT_REG (0x16) #define AXP2101_CHARG_FGAUG_WDOG_REG (0x18) #define AXP2101_PWROK_PWROFF_REG (0x25) #define AXP2101_ADC_ENABLE_REG (0x30) @@ -86,9 +89,11 @@ #define AXP2101_IRQ_STATUS_0_REG (0x48) #define AXP2101_IRQ_STATUS_1_REG (0x49) #define AXP2101_IRQ_STATUS_2_REG (0x4A) -#define AXP2101_TS_PIN_CTRL (0x50) +#define AXP2101_TS_PIN_CTRL_REG (0x50) +#define AXP2101_IPRECHG_REG (0x61) #define AXP2101_ICC_CHARGER_SETTING_REG (0x62) #define AXP2101_CHARGER_SETTING_REG (0x63) +#define AXP2101_CV_CHARGER_SETTING_REG (0x64) #define AXP2101_CHGLED_REG (0x69) #define AXP2101_BAT_CHARGE_REG (0xA4) /* pdf has a duplicate listed for register 0x04, should be 0xA4 */ From 9017809280acf18d7ca90f25108865523a15d3c0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Jan 2025 18:07:45 +0100 Subject: [PATCH 24/50] [P139] Apply setttings to device --- lib/AXP2101/src/AXP2101.cpp | 69 +++++------ lib/AXP2101/src/AXP2101.h | 8 +- lib/AXP2101/src/AXP2101_settings.cpp | 7 +- lib/AXP2101/src/AXP2101_settings.h | 3 +- src/_P139_AXP2101.ino | 2 +- src/src/PluginStructs/P139_data_struct.cpp | 130 ++++++++++++++++++++- src/src/PluginStructs/P139_data_struct.h | 4 +- src/src/WebServer/Markup_Forms.cpp | 14 +++ src/src/WebServer/Markup_Forms.h | 2 + 9 files changed, 180 insertions(+), 59 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index d1f6c0c55c..9182873ec2 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -538,7 +538,7 @@ void AXP2101::setPreChargeCurrentLimit(uint16_t current_mA) { } // Reg 62: ICC Charger settings -uint16_t AXP2101::getChargeCurrentLimit() { +uint16_t AXP2101::getConstChargeCurrentLimit() { // AXP2101_ICC_CHARGER_SETTING_REG // bit 4:0 uint8_t reg = readRegister8(_addr, AXP2101_ICC_CHARGER_SETTING_REG); @@ -548,7 +548,7 @@ uint16_t AXP2101::getChargeCurrentLimit() { } return (reg - 8) * 100 + 200; } -void AXP2101::setChargeCurrentLimit(uint16_t current_mA) { +void AXP2101::setConstChargeCurrentLimit(uint16_t current_mA) { if (current_mA > 1000) { current_mA = 1000; } @@ -568,28 +568,20 @@ uint16_t AXP2101::getTerminationChargeCurrentLimit() { uint8_t reg = readRegister8(_addr,AXP2101_CHARGER_SETTING_REG); return (reg & 0b1111) * 25; } + void AXP2101::setTerminationChargeCurrentLimit(uint16_t current_mA) { - setTerminationChargeCurrentLimit - setTerminationChargeCurrentLimit(current_mA) - if (current_mA > 200) { - current_mA = 200; - } constexpr uint8_t enable_mask = 0b00010000; const bool enabled = bitGet(AXP2101_CHARGER_SETTING_REG, enable_mask); - uint8_t reg = current_mA / 25; - if (enabled) { - reg &= enable_mask; - } - writeRegister8(_addr, AXP2101_CHARGER_SETTING_REG, reg); + setTerminationChargeCurrentLimit(current_mA, enabled); } void AXP2101::setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable) { if (current_mA > 200) { current_mA = 200; } - constexpr uint8_t enable_mask = 0b00010000; uint8_t reg = current_mA / 25; if (enable) { + constexpr uint8_t enable_mask = 0b00010000; reg &= enable_mask; } writeRegister8(_addr, AXP2101_CHARGER_SETTING_REG, reg); @@ -600,9 +592,9 @@ void AXP2101::setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable) AXP2101_CV_charger_voltage_e AXP2101::getCV_chargeVoltage() { // AXP2101_CV_CHARGER_SETTING_REG // bit 2:0 - uint8_t reg = readRegister8(_addr,AXP2101_CV_CHARGER_SETTING_REG); - reg &= 0b111; - return static_cast(reg); + uint8_t reg = readRegister8(_addr,AXP2101_CV_CHARGER_SETTING_REG); + reg &= 0b111; + return static_cast(reg); } void AXP2101::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { uint8_t reg = static_cast(voltage_mV); @@ -630,20 +622,31 @@ void AXP2101::setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e volta // Reg 15: Input Voltage Limit AXP2101_VINDPM_e AXP2101::getVin_DPM() { -// AXP2101_VIN_DPM_REG -// bit 3:0 + // AXP2101_VIN_DPM_REG + // bit 3:0 + uint8_t reg = readRegister8(_addr, AXP2101_VIN_DPM_REG); + reg &= 0b00001111; + return static_cast(reg); } void AXP2101::setVin_DPM(AXP2101_VINDPM_e voltage) { - + uint8_t reg = static_cast(voltage); + reg &= 0b00001111; + writeRegister8(_addr, AXP2101_VIN_DPM_REG, reg); } // Reg 16: Input Current Limit AXP2101_InputCurrentLimit_e AXP2101::getInputCurrentLimit() { -// AXP2101_IN_CURRENT_LIMIT_REG -// bit 2:0 + // AXP2101_IN_CURRENT_LIMIT_REG + // bit 2:0 + uint8_t reg = readRegister8(_addr, AXP2101_IN_CURRENT_LIMIT_REG); + reg &= 0b00000111; + return static_cast(reg); } -void AXP2101::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { +void AXP2101::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { + uint8_t reg = static_cast(current); + reg &= 0b00000111; + writeRegister8(_addr, AXP2101_IN_CURRENT_LIMIT_REG, reg); } uint8_t AXP2101::getBatCharge() { @@ -685,30 +688,10 @@ bool AXP2101::set_charger_term_current_to_zero(void) { return bitOff(AXP2101_ADDR, AXP2101_CHARGER_SETTING_REG, 0b00001111); } -bool AXP2101::set_charger_constant_current_to_50mA(void) { +bool AXP2101::setConstChargeCurrentLimit_to_50mA(void) { return writeRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG, 2); } -bool AXP2101::set_charger_constant_current(uint16_t current_mA) -{ - if (current_mA > 1000) { - current_mA = 1000; - } - const uint8_t regValue = (current_mA <= 200) - ? current_mA / 25 - : ((current_mA - 200) / 100) + 8; - return writeRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG, regValue); -} - -uint16_t AXP2101::get_charger_constant_current() -{ - const uint8_t regValue = readRegister8(AXP2101_ADDR, AXP2101_ICC_CHARGER_SETTING_REG); - if (regValue <= 8) { - return regValue * 25; - } - return 200 * (100 * (regValue - 8)); -} - void AXP2101::set_bat_charge(bool enable) { uint8_t val = 0; diff --git a/lib/AXP2101/src/AXP2101.h b/lib/AXP2101/src/AXP2101.h index 8e26b9b5e0..7ef6151a27 100644 --- a/lib/AXP2101/src/AXP2101.h +++ b/lib/AXP2101/src/AXP2101.h @@ -102,8 +102,8 @@ class AXP2101 { void setPreChargeCurrentLimit(uint16_t current_mA); // Reg 62: ICC Charger Settings - uint16_t getChargeCurrentLimit() ; - void setChargeCurrentLimit(uint16_t current_mA); + uint16_t getConstChargeCurrentLimit() ; + void setConstChargeCurrentLimit(uint16_t current_mA); // Reg 63: Iterm Charger Settings and Control // Enable/Disable via chargeStates.term_cur_lim_en @@ -149,9 +149,7 @@ class AXP2101 { void set_bat_charge(bool enable); void power_off(void); bool set_charger_term_current_to_zero(void); - bool set_charger_constant_current_to_50mA(void); - bool set_charger_constant_current(uint16_t current_mA); - uint16_t get_charger_constant_current(); + bool setConstChargeCurrentLimit_to_50mA(void); bool enable_pwrok_resets(void); void set_IRQ_enable_0(uint8_t val); diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index 329ee70ea5..e5aba9b9ad 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -503,11 +503,16 @@ uint16_t AXP2101_settings::getTerminationChargeCurrentLimit() const { return chargeStates.term_cur_lim * 25; } -void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA) { +bool AXP2101_settings::getTerminationChargeCurrentLimitEnable() const { + return chargeStates.term_cur_lim_en; +} + +void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable) { if (current_mA > 200) { current_mA = 200; } chargeStates.term_cur_lim = current_mA / 25; + chargeStates.term_cur_lim_en = enable; } diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index 75a0a81d4f..380f5e5627 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -354,7 +354,8 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the // Reg 63: Iterm Charger Settings and Control // Enable/Disable via chargeStates.term_cur_lim_en uint16_t getTerminationChargeCurrentLimit() const; - void setTerminationChargeCurrentLimit(uint16_t current_mA); + bool getTerminationChargeCurrentLimitEnable() const; + void setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable); // Reg 64: CV Charger Voltage Settings AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index aa92ce5d3e..819fd334d2 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -170,7 +170,7 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) P139_data_struct *P139_data = new (std::nothrow) P139_data_struct(); if (nullptr != P139_data) { - P139_data->applySettings(device); + P139_data->applyDeviceModelTemplate(device); P139_data->saveSettings(event); delete P139_data; } diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index e863c9f279..cb91127c53 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -13,7 +13,7 @@ P139_data_struct::P139_data_struct(struct EventStruct *event) { if (isInitialized()) { // Functions based on: axp2101->begin(&Wire, AXP2101_ADDR, static_cast(P139_CURRENT_PREDEFINED)); loadSettings(event); - outputSettings(event); + applySettings(event); } else { addLog(LOG_LEVEL_ERROR, F("AXP2101: Initialization failed")); } @@ -42,9 +42,11 @@ String P139_data_struct::loadSettings(struct EventStruct *event) { } // **************************************************************************/ -// outputSettings: Write the current settings to AXP2101 +// applySettings: Write the current settings to AXP2101 // **************************************************************************/ -void P139_data_struct::outputSettings(struct EventStruct *event) { +void P139_data_struct::applySettings(struct EventStruct *event) { + if (!isInitialized()) + return; uint8_t count = 0; for (int s = 0; s < AXP2101_settings_count; ++s) { @@ -74,9 +76,33 @@ void P139_data_struct::outputSettings(struct EventStruct *event) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, strformat(F("AXP2101: Set %d values to port(s)"), count)); } + axp2101->setChargeLed(_settings.getChargeLed()); + + // Reg 61: Iprechg Charger Settings + axp2101->setPreChargeCurrentLimit(_settings.getPreChargeCurrentLimit()); + + // Reg 62: ICC Charger Settings + axp2101->setConstChargeCurrentLimit(_settings.getConstChargeCurrentLimit()); + + // Reg 63: Iterm Charger Settings and Control + axp2101->setTerminationChargeCurrentLimit(_settings.getTerminationChargeCurrentLimit(), _settings.getTerminationChargeCurrentLimitEnable()); + + // Reg 64: CV Charger Voltage Settings + axp2101->setCV_chargeVoltage(_settings.getCV_chargeVoltage()); + + // Reg 14: Minimum System Voltage Control + axp2101->setLinear_Charger_Vsys_dpm(_settings.getLinear_Charger_Vsys_dpm()); + + // Reg 15: Input Voltage Limit + axp2101->setVin_DPM(_settings.getVin_DPM()); + + // Reg 16: Input Current Limit + axp2101->setInputCurrentLimit(_settings.getInputCurrentLimit()); axp2101->setTS_disabled(_settings.getTS_disabled()); + + // axp2101->set_IRQ_enable_0(0b11110000); // Disable temperature checks - axp2101->set_charger_constant_current(800); + axp2101->setConstChargeCurrentLimit(_settings.getConstChargeCurrentLimit()); } // **************************************************************************/ @@ -94,7 +120,7 @@ String P139_data_struct::saveSettings(struct EventStruct *event) { // **************************************************************************/ // applySettings: Update settings to defaults for selected device // **************************************************************************/ -bool P139_data_struct::applySettings(AXP2101_device_model_e device) { +bool P139_data_struct::applyDeviceModelTemplate(AXP2101_device_model_e device) { const int idx = static_cast(AXP2101_device_model_e::UserDefined == device ? AXP2101_device_model_e::MAX : device); @@ -129,8 +155,79 @@ void P139_data_struct::webform_load(struct EventStruct *event) { chargeledNames, chargeledValues, static_cast(_settings.getChargeLed())); } + { + // Reg 61: Iprechg Charger Settings + // 0 .. 200 mA in 25 mA steps + const int values[] = { + 0, + 25, + 50, + 75, + 100, + 125, + 150, + 175, + 200 + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Pre Charge Current"), F("iprechg"), + valueCount, + values, + static_cast(_settings.getPreChargeCurrentLimit())); + addUnit(F("mA")); + } + { + // Reg 62: ICC Charger Settings + const int values[] = { + 0, + 25, + 50, + 75, + 100, + 125, + 150, + 175, + 200, + 300, + 400, + 500, + 600, + 700, + 800, + 900, + 1000 + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Constant Current Charge Limit"), F("iccchg"), + valueCount, + values, + static_cast(_settings.getConstChargeCurrentLimit())); + addUnit(F("mA")); + } + { + // Reg 63: Iterm Charger Settings and Control + // 0 .. 200 mA in 25 mA steps + enable checkbox + const int values[] = { + 0, + 25, + 50, + 75, + 100, + 125, + 150, + 175, + 200 + }; + constexpr uint8_t valueCount = NR_ELEMENTS(values); + addFormSelector(F("Term Charge Current"), F("iterm"), + valueCount, + values, + static_cast(_settings.getTerminationChargeCurrentLimit())); + addUnit(F("mA")); + } { + // Reg 64: CV Charger Voltage Settings const __FlashStringHelper *names[] = { toString(AXP2101_CV_charger_voltage_e::limit_4_00V), toString(AXP2101_CV_charger_voltage_e::limit_4_10V), @@ -153,6 +250,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { addUnit(F("V")); } { + // Reg 14: Minimum System Voltage Control const __FlashStringHelper *names[] = { toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), @@ -182,6 +280,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { } { + // Reg 15: Input Voltage Limit const __FlashStringHelper *names[] = { toString(AXP2101_VINDPM_e::Vin_3_88V), toString(AXP2101_VINDPM_e::Vin_3_96V), @@ -227,6 +326,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { } { + // Reg 16: Input Current Limit const __FlashStringHelper *names[] = { toString(AXP2101_InputCurrentLimit_e::limit_100mA), toString(AXP2101_InputCurrentLimit_e::limit_500mA), @@ -261,7 +361,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { if (P139_CONFIG_PREDEFINED > 0) { P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; P139_CONFIG_PREDEFINED = 0; - applySettings(static_cast(P139_CURRENT_PREDEFINED)); + applyDeviceModelTemplate(static_cast(P139_CURRENT_PREDEFINED)); } const __FlashStringHelper *predefinedNames[] = { toString(AXP2101_device_model_e::Unselected), @@ -375,9 +475,27 @@ void P139_data_struct::webform_save(struct EventStruct *event) { _settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + // Reg 61: Iprechg Charger Settings + _settings.setPreChargeCurrentLimit(getFormItemInt(F("iprechg"))); + + // Reg 62: ICC Charger Settings + _settings.setConstChargeCurrentLimit(getFormItemInt(F("iccchg"))); + + // Reg 63: Iterm Charger Settings and Control + _settings.setTerminationChargeCurrentLimit( + getFormItemInt(F("iterm")), + isFormItemChecked(F("iterm_en"))); + + // Reg 64: CV Charger Voltage Settings _settings.setCV_chargeVoltage(static_cast(getFormItemInt(F("cv_volt")))); + + // Reg 14: Minimum System Voltage Control _settings.setLinear_Charger_Vsys_dpm(static_cast(getFormItemInt(F("min_vsys")))); + + // Reg 15: Input Voltage Limit _settings.setVin_DPM(static_cast(getFormItemInt(F("vin_dpm")))); + + // Reg 16: Input Current Limit _settings.setInputCurrentLimit(static_cast(getFormItemInt(F("cur_lim_in")))); _settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index a024a5c392..04a30cbd8f 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -53,8 +53,8 @@ struct P139_data_struct : public PluginTaskData_base { bool plugin_fifty_per_second(struct EventStruct *event); String loadSettings(struct EventStruct *event); String saveSettings(struct EventStruct *event); - void outputSettings(struct EventStruct *event); - bool applySettings(AXP2101_device_model_e device); + void applySettings(struct EventStruct *event); + bool applyDeviceModelTemplate(AXP2101_device_model_e device); void webform_load(struct EventStruct *event); void webform_save(struct EventStruct *event); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 28ad4a2639..ff7090f50c 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -506,6 +506,20 @@ void addFormSelectorI2C(const String& id, addSelector_Foot(); } +void addFormSelector(const __FlashStringHelper * label, + const __FlashStringHelper * id, + int optionCount, + const int indices[], + int selectedIndex, + bool reloadonchange) +{ + String options[optionCount]{}; + for (int i = 0; i < optionCount; ++i) { + options[i] = indices[i]; + } + addFormSelector(label, id, optionCount, options, indices, selectedIndex, reloadonchange); +} + void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange) { addFormSelector(String(label), String(id), optionCount, options, indices, nullptr, selectedIndex, reloadonchange); diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 71a5ea34a9..83f295ac22 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -303,6 +303,8 @@ void addFormSelector(const String& label, #endif ); +void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const int indices[], int selectedIndex, bool reloadonchange = false); + void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange = false); void addFormSelector(const __FlashStringHelper * label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange = false); void addFormSelector(const String& label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex); From 8f05a545ea3b03f05d2db52e87f2986da7dc9a73 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Jan 2025 21:45:38 +0100 Subject: [PATCH 25/50] [P139] Fix build ESP8266 --- src/src/WebServer/Markup_Forms.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index ff7090f50c..1f6ad008b6 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -513,7 +513,7 @@ void addFormSelector(const __FlashStringHelper * label, int selectedIndex, bool reloadonchange) { - String options[optionCount]{}; + String options[optionCount]; for (int i = 0; i < optionCount; ++i) { options[i] = indices[i]; } From c57cdea0876d056e5d5f70bb32879ae2c67ca513 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 23 Jan 2025 22:59:45 +0100 Subject: [PATCH 26/50] [P139] Fix setting defaults for charger settings --- lib/AXP2101/src/AXP2101_settings.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index e5aba9b9ad..c232d2c9f1 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -292,7 +292,20 @@ bool AXP2101_isPinProtected(AXP_pin_s pin) { */ // constructor -AXP2101_settings::AXP2101_settings() {} +AXP2101_settings::AXP2101_settings() { + chargeStates.pre_chg_cur = 0b0101; + chargeStates.const_cur_lim = 0b01001; // 300 mA, however can be set via EFUSE + chargeStates.term_cur_lim_en = 1; + chargeStates.term_cur_lim = 0b0101; // 125 mA + chargeStates.chg_volt_lim = 0b011; // 4.2V + chargeStates.thermal_thresh = 0b10; // 100 deg + chargeStates.chg_timeout_ctrl = 0b11100110; + chargeStates.bat_detection = 1; + chargeStates.coincell_term_volt = 0b011; // 2.9V + chargeStates.min_sys_voltage = 0b110; // 4.7V + chargeStates.inp_volt_limit = 0b0110; // 4.36V + chargeStates.inp_cur_limit = 0b100; // 1500 mA +} // constructor AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _dcdc3, uint16_t _dcdc4, uint16_t _dcdc5, @@ -315,6 +328,19 @@ AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _d pinStates.en_bldo2 = static_cast(_en_bldo2); pinStates.en_dldo1 = static_cast(_en_dldo1); pinStates.en_dldo2 = static_cast(_en_dldo2); pinStates.en_cpuldos = static_cast(_en_cpuldos); pinStates.chargeled = static_cast(_chargeled); + + chargeStates.pre_chg_cur = 0b0101; + chargeStates.const_cur_lim = 0b01001; // 300 mA, however can be set via EFUSE + chargeStates.term_cur_lim_en = 1; + chargeStates.term_cur_lim = 0b0101; // 125 mA + chargeStates.chg_volt_lim = 0b011; // 4.2V + chargeStates.thermal_thresh = 0b10; // 100 deg + chargeStates.chg_timeout_ctrl = 0b11100110; + chargeStates.bat_detection = 1; + chargeStates.coincell_term_volt = 0b011; // 2.9V + chargeStates.min_sys_voltage = 0b110; // 4.7V + chargeStates.inp_volt_limit = 0b0110; // 4.36V + chargeStates.inp_cur_limit = 0b100; // 1500 mA } void AXP2101_settings::setVoltage(AXP2101_registers_e reg, From a2bc54ac010dff842eba6a2e704df588c1a651a1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 14:01:58 +0100 Subject: [PATCH 27/50] [Cleanup] Add FormSelector class to simplify P139 charge options --- src/src/DataTypes/FormSelectorOptions.cpp | 186 ++++++++++++++++++ src/src/DataTypes/FormSelectorOptions.h | 67 +++++++ src/src/PluginStructs/P139_data_struct.cpp | 88 +-------- src/src/PluginStructs/P139_data_struct.h | 1 + .../P139_data_struct_formselectors.cpp | 133 +++++++++++++ .../P139_data_struct_formselectors.h | 76 +++++++ src/src/WebServer/Markup_Forms.h | 1 + 7 files changed, 473 insertions(+), 79 deletions(-) create mode 100644 src/src/DataTypes/FormSelectorOptions.cpp create mode 100644 src/src/DataTypes/FormSelectorOptions.h create mode 100644 src/src/PluginStructs/P139_data_struct_formselectors.cpp create mode 100644 src/src/PluginStructs/P139_data_struct_formselectors.h diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp new file mode 100644 index 0000000000..ecbbc9504c --- /dev/null +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -0,0 +1,186 @@ +#include "../DataTypes/FormSelectorOptions.h" + +#include "../WebServer/Markup.h" + +FormSelectorOptions::FormSelectorOptions( + int optionCount, + const int indices[], + const String attr[]) : _optionCount(optionCount) +{ + _indices = new int[optionCount]; + + if (attr != nullptr) { + _attr_str = new String[optionCount]; + } + + for (int i = 0; i < optionCount; ++i) { + _indices[i] = indices[i]; + + if (attr != nullptr) { + _attr_str[i] = attr[i]; + } + } +} + +FormSelectorOptions::FormSelectorOptions( + int optionCount, + const String options[], + const int indices[], + const String attr[]) : _optionCount(optionCount) +{ + _names_str = new String[optionCount]; + + if (indices != nullptr) { + _indices = new int[optionCount]; + } + + if (attr != nullptr) { + _attr_str = new String[optionCount]; + } + + + for (int i = 0; i < optionCount; ++i) { + _names_str[i] = options[i]; + + if (indices != nullptr) { + _indices[i] = indices[i]; + } + + if (attr != nullptr) { + _attr_str[i] = attr[i]; + } + } +} + +FormSelectorOptions::FormSelectorOptions( + int optionCount, + const __FlashStringHelper *options[], + const int indices[], + const String attr[]) : _optionCount(optionCount) +{ + _names_f = new const __FlashStringHelper *[optionCount]; + + if (indices != nullptr) { + _indices = new int[optionCount]; + } + + if (attr != nullptr) { + _attr_str = new String[optionCount]; + } + + for (int i = 0; i < optionCount; ++i) { + _names_f[i] = options[i]; + + if (indices != nullptr) { + _indices[i] = indices[i]; + } + + if (attr != nullptr) { + _attr_str[i] = attr[i]; + } + } +} + +FormSelectorOptions::~FormSelectorOptions() { + #define DELETE_ARR(A) \ + if (A != nullptr) { delete[] A; A = nullptr; } + DELETE_ARR(_names_f) + DELETE_ARR(_names_str) + DELETE_ARR(_indices) + DELETE_ARR(_attr_str) + #undef DELETE_ARR +} + +String FormSelectorOptions::getOptionString(int index) const +{ + if (index >= _optionCount) { + return EMPTY_STRING; + } + + if (_names_f != nullptr) { + return String(_names_f[index]); + } + + if (_names_str != nullptr) { + return String(_names_str[index]); + } + + if (_indices != nullptr) { + return String(_indices[index]); + } + return String(index); +} + +int FormSelectorOptions::getIndexValue(int index) const +{ + if (index >= _optionCount) { + return -1; + } + + if (_indices != nullptr) { + return _indices[index]; + } + return index; +} + +void FormSelectorOptions::addFormSelector( + const __FlashStringHelper *label, + const __FlashStringHelper *id, + int selectedIndex) +{ + addFormSelector(String(label), String(id), selectedIndex); +} + +void FormSelectorOptions::addFormSelector( + const String & label, + const __FlashStringHelper *id, + int selectedIndex) +{ + addFormSelector(label, String(id), selectedIndex); +} + +void FormSelectorOptions::addFormSelector( + const __FlashStringHelper *label, + const String & id, + int selectedIndex) +{ + addFormSelector(String(label), id, selectedIndex); +} + +void FormSelectorOptions::addFormSelector( + const String& label, + const String& id, + int selectedIndex) +{ + addRowLabel_tr_id(label, id); + + // FIXME TD-er Change bool 'enabled' to disabled + if (reloadonchange) + { + addSelector_Head_reloadOnChange(id, classname, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } else { + do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + #if FEATURE_TOOLTIPS + , tooltip + #endif // if FEATURE_TOOLTIPS + ); + } + + for (int i = 0; i < _optionCount; ++i) + { + const int index = getIndexValue(i); + addSelector_Item( + getOptionString(i), + index, + selectedIndex == index, + false, + _attr_str ? _attr_str[i] : EMPTY_STRING); + + if ((i & 0x07) == 0) { delay(0); } + } + addSelector_Foot(); +} diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h new file mode 100644 index 0000000000..470541dcbc --- /dev/null +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -0,0 +1,67 @@ +#ifndef DATATYPES_FORMSELECTOROPTIONS_H +#define DATATYPES_FORMSELECTOROPTIONS_H + +#include "../../ESPEasy_common.h" + +class FormSelectorOptions { +public: + + FormSelectorOptions(int optionCount) : _optionCount(optionCount) {} + + FormSelectorOptions(int optionCount, + const int indices[], + const String attr[] = nullptr); + + FormSelectorOptions(int optionCount, + const String options[], + const int indices[] = nullptr, + const String attr[] = nullptr); + FormSelectorOptions(int optionCount, + const __FlashStringHelper *options[], + const int indices[] = nullptr, + const String attr[] = nullptr); + + + virtual ~FormSelectorOptions(); + + // Return either a string representation of the current index, + // or the override implementation in a class inheriting of this class + virtual String getOptionString(int index) const; + + virtual int getIndexValue(int index) const; + + void addFormSelector(const __FlashStringHelper *label, + const __FlashStringHelper *id, + int selectedIndex); + + void addFormSelector(const String&label, + const __FlashStringHelper *id, + int selectedIndex); + + void addFormSelector(const __FlashStringHelper *label, + const String& id, + int selectedIndex); + + void addFormSelector(const String& label, + const String& id, + int selectedIndex); + + + bool reloadonchange = false; + bool enabled = true; + const __FlashStringHelper * classname = F("wide"); +#if FEATURE_TOOLTIPS + String tooltip; +#endif // if FEATURE_TOOLTIPS + +protected: + + int _optionCount{}; + const __FlashStringHelper **_names_f{ nullptr }; + String *_names_str{ nullptr }; + int *_indices{ nullptr }; + String *_attr_str{ nullptr }; +}; + + +#endif // ifndef DATATYPES_FORMSELECTOROPTIONS_H diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index cb91127c53..769f3b46e5 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -4,6 +4,8 @@ # ifdef ESP32 +#include "../PluginStructs/P139_data_struct_formselectors.h" + // **************************************************************************/ // Constructors // **************************************************************************/ @@ -158,96 +160,23 @@ void P139_data_struct::webform_load(struct EventStruct *event) { { // Reg 61: Iprechg Charger Settings // 0 .. 200 mA in 25 mA steps - const int values[] = { - 0, - 25, - 50, - 75, - 100, - 125, - 150, - 175, - 200 - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Pre Charge Current"), F("iprechg"), - valueCount, - values, - static_cast(_settings.getPreChargeCurrentLimit())); - addUnit(F("mA")); + AXP2101_PreChargeCurrentLimit_FormSelector selector(_settings.getPreChargeCurrentLimit()); } { // Reg 62: ICC Charger Settings - const int values[] = { - 0, - 25, - 50, - 75, - 100, - 125, - 150, - 175, - 200, - 300, - 400, - 500, - 600, - 700, - 800, - 900, - 1000 - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Constant Current Charge Limit"), F("iccchg"), - valueCount, - values, - static_cast(_settings.getConstChargeCurrentLimit())); - addUnit(F("mA")); + AXP2101_ConstChargeCurrentLimit_FormSelector selector(_settings.getConstChargeCurrentLimit()); } { // Reg 63: Iterm Charger Settings and Control // 0 .. 200 mA in 25 mA steps + enable checkbox - const int values[] = { - 0, - 25, - 50, - 75, - 100, - 125, - 150, - 175, - 200 - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Term Charge Current"), F("iterm"), - valueCount, - values, - static_cast(_settings.getTerminationChargeCurrentLimit())); - addUnit(F("mA")); + AXP2101_PreChargeCurrentLimit_FormSelector selector(_settings.getTerminationChargeCurrentLimit()); + + // TODO TD-er: Must add 'enabled' checkbox } { // Reg 64: CV Charger Voltage Settings - const __FlashStringHelper *names[] = { - toString(AXP2101_CV_charger_voltage_e::limit_4_00V), - toString(AXP2101_CV_charger_voltage_e::limit_4_10V), - toString(AXP2101_CV_charger_voltage_e::limit_4_20V), - toString(AXP2101_CV_charger_voltage_e::limit_4_35V), - toString(AXP2101_CV_charger_voltage_e::limit_4_40V), - }; - const int values[] = { - static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_10V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_35V), - static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("CV Charger Voltage"), F("cv_volt"), - valueCount, - names, values, - static_cast(_settings.getCV_chargeVoltage())); - addUnit(F("V")); + AXP2101_CV_charger_voltage_FormSelector selector(_settings.getCV_chargeVoltage()); } { // Reg 14: Minimum System Voltage Control @@ -352,6 +281,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { } addFormCheckBox(F("Disable TS pin"), F("dis_TS"), _settings.getTS_disabled()); + addFormNote(F("Make sure to disable TS pin when no NTC is used, or else the battery will not charge")); addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index 04a30cbd8f..69331c924d 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -35,6 +35,7 @@ # define P139_CONST_MAX_LDO 3700 // Max. output voltage + struct P139_data_struct : public PluginTaskData_base { public: diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.cpp b/src/src/PluginStructs/P139_data_struct_formselectors.cpp new file mode 100644 index 0000000000..2e12be151e --- /dev/null +++ b/src/src/PluginStructs/P139_data_struct_formselectors.cpp @@ -0,0 +1,133 @@ +#include "../PluginStructs/P139_data_struct_formselectors.h" + +#ifdef USES_P139 + + +// ********************************************************************** +// Reg 61: Iprechg Charger Settings +// 0 .. 200 mA in 25 mA steps +// ********************************************************************** +AXP2101_PreChargeCurrentLimit_FormSelector::AXP2101_PreChargeCurrentLimit_FormSelector( + int selected) : FormSelectorOptions(9) +{ + addFormSelector( + F("Term Charge Current"), F("iterm"), + static_cast(selected)); + addUnit(F("mA")); +} + +int get_AXP2101_0_to_200mA_ChargeCurrentLimit(int index) { + if (index < 0) { return 0; } + const int res = 25 * index; + + if (res > 200) { return 200; } + return res; +} + +String AXP2101_PreChargeCurrentLimit_FormSelector::getOptionString(int index) const +{ + return String(get_AXP2101_0_to_200mA_ChargeCurrentLimit(index)); +} + +int AXP2101_PreChargeCurrentLimit_FormSelector::getIndexValue(int index) const +{ + return get_AXP2101_0_to_200mA_ChargeCurrentLimit(index); +} + + + +// ********************************************************************** +// Reg 62: ICC Charger Settings +// 0 .. 200 mA in 25 mA steps +// 200 ... 1000 mA in 100 mA steps +// ********************************************************************** +AXP2101_ConstChargeCurrentLimit_FormSelector::AXP2101_ConstChargeCurrentLimit_FormSelector( + int selected) : FormSelectorOptions(17) +{ + addFormSelector( + F("Constant Current Charge Limit"), F("iccchg"), + static_cast(selected)); + addUnit(F("mA")); +} + +int get_AXP2101_ConstChargeCurrentLimit(int index) { + if (index < 0) { return 0; } + const int res = index <= 8 + ? 25 * index + : (index - 8) * 100 + 200; + if (res > 1000) { return 1000; } + return res; +} + +String AXP2101_ConstChargeCurrentLimit_FormSelector::getOptionString(int index) const +{ + return String(get_AXP2101_ConstChargeCurrentLimit(index)); +} + +int AXP2101_ConstChargeCurrentLimit_FormSelector::getIndexValue(int index) const +{ + return get_AXP2101_ConstChargeCurrentLimit(index); +} + + + +// ********************************************************************** +// Reg 63: Iterm Charger Settings and Control +// 0 .. 200 mA in 25 mA steps + enable checkbox +// ********************************************************************** +AXP2101_TerminationChargeCurrentLimit_FormSelector::AXP2101_TerminationChargeCurrentLimit_FormSelector( + int selected) : FormSelectorOptions(9) +{ + addFormSelector( + F("Pre Charge Current"), F("iprechg"), + static_cast(selected)); + addUnit(F("mA")); +} + +String AXP2101_TerminationChargeCurrentLimit_FormSelector::getOptionString(int index) const +{ + return String(get_AXP2101_0_to_200mA_ChargeCurrentLimit(index)); +} + +int AXP2101_TerminationChargeCurrentLimit_FormSelector::getIndexValue(int index) const +{ + return get_AXP2101_0_to_200mA_ChargeCurrentLimit(index); +} + + + + +// ********************************************************************** +// Reg 64: CV Charger Voltage Settings +// ********************************************************************** +AXP2101_CV_charger_voltage_FormSelector::AXP2101_CV_charger_voltage_FormSelector( + AXP2101_CV_charger_voltage_e selected) : + FormSelectorOptions(static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) +{ + addFormSelector( + F("CV Charger Voltage"), F("cv_volt"), + static_cast(selected)); + addUnit(F("V")); +} + +AXP2101_CV_charger_voltage_e get_AXP2101_CV_charger_voltage_e(int index) { + if (index < 0) { return AXP2101_CV_charger_voltage_e::reserved; } + constexpr int offset = static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V); + constexpr int max = static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V) + offset; + index += offset; + + if (index > max) { return AXP2101_CV_charger_voltage_e::reserved; } + return static_cast(index); +} + +String AXP2101_CV_charger_voltage_FormSelector::getOptionString(int index) const +{ + return toString(get_AXP2101_CV_charger_voltage_e(index)); +} + +int AXP2101_CV_charger_voltage_FormSelector::getIndexValue(int index) const +{ + return static_cast(get_AXP2101_CV_charger_voltage_e(index)); +} + +#endif // ifdef USES_P139 diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.h b/src/src/PluginStructs/P139_data_struct_formselectors.h new file mode 100644 index 0000000000..5aa4327c02 --- /dev/null +++ b/src/src/PluginStructs/P139_data_struct_formselectors.h @@ -0,0 +1,76 @@ +#pragma once + +#include "../../_Plugin_Helper.h" + +#ifdef USES_P139 + +#include + +// ********************************************************************** +// Reg 61: Iprechg Charger Settings +// 0 .. 200 mA in 25 mA steps +// ********************************************************************** +class AXP2101_PreChargeCurrentLimit_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_PreChargeCurrentLimit_FormSelector(int selected); + virtual ~AXP2101_PreChargeCurrentLimit_FormSelector() {} + + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; + +}; + + +// ********************************************************************** +// Reg 62: ICC Charger Settings +// 0 .. 200 mA in 25 mA steps +// 200 ... 1000 mA in 100 mA steps +// ********************************************************************** +class AXP2101_ConstChargeCurrentLimit_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_ConstChargeCurrentLimit_FormSelector(int selected); + virtual ~AXP2101_ConstChargeCurrentLimit_FormSelector() {} + + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; + +}; + + + +// ********************************************************************** +// Reg 63: Iterm Charger Settings and Control +// 0 .. 200 mA in 25 mA steps + enable checkbox +// ********************************************************************** +class AXP2101_TerminationChargeCurrentLimit_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_TerminationChargeCurrentLimit_FormSelector(int selected); + virtual ~AXP2101_TerminationChargeCurrentLimit_FormSelector() {} + + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; + +}; + + +// ********************************************************************** +// Reg 64: CV Charger Voltage Settings +// ********************************************************************** +class AXP2101_CV_charger_voltage_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_CV_charger_voltage_FormSelector(AXP2101_CV_charger_voltage_e selected); + virtual ~AXP2101_CV_charger_voltage_FormSelector() {} + + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; + +}; + + + + +#endif \ No newline at end of file diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 83f295ac22..6c9b10eb63 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -3,6 +3,7 @@ #include "../WebServer/common.h" +#include "../DataTypes/FormSelectorOptions.h" #include "../DataStructs/MAC_address.h" #include "../Globals/Plugins.h" #include "../Helpers/StringGenerator_GPIO.h" From 0a684b02b75c0ec11c4844574a7ca16249a0a497 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 16:10:34 +0100 Subject: [PATCH 28/50] [Cleanup] Convert other charging related settings to use FormSelector --- lib/AXP2101/src/AXP2101_settings.cpp | 51 -------- lib/AXP2101/src/AXP2101_settings.h | 16 +-- src/src/PluginStructs/P139_data_struct.cpp | 111 +--------------- .../P139_data_struct_formselectors.cpp | 119 ++++++++++++++++-- .../P139_data_struct_formselectors.h | 59 +++++++++ 5 files changed, 179 insertions(+), 177 deletions(-) diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index c232d2c9f1..2728c813a7 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -120,57 +120,6 @@ const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage) { return F(""); } -const __FlashStringHelper* toString(AXP2101_Linear_Charger_Vsys_dpm_e vsys) { - switch (vsys) { - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V: return F("4.1"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V: return F("4.2"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V: return F("4.3"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V: return F("4.4"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V: return F("4.5"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V: return F("4.6"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V: return F("4.7"); - case AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V: return F("4.8"); - } - return F(""); -} - - -const __FlashStringHelper* toString(AXP2101_VINDPM_e voltage) { - switch (voltage) { - case AXP2101_VINDPM_e::Vin_3_88V: return F("3.88"); - case AXP2101_VINDPM_e::Vin_3_96V: return F("3.96"); - case AXP2101_VINDPM_e::Vin_4_04V: return F("4.04"); - case AXP2101_VINDPM_e::Vin_4_12V: return F("4.12"); - case AXP2101_VINDPM_e::Vin_4_20V: return F("4.20"); - case AXP2101_VINDPM_e::Vin_4_28V: return F("4.28"); - case AXP2101_VINDPM_e::Vin_4_36V: return F("4.36"); - case AXP2101_VINDPM_e::Vin_4_44V: return F("4.44"); - case AXP2101_VINDPM_e::Vin_4_52V: return F("4.52"); - case AXP2101_VINDPM_e::Vin_4_60V: return F("4.60"); - case AXP2101_VINDPM_e::Vin_4_68V: return F("4.68"); - case AXP2101_VINDPM_e::Vin_4_76V: return F("4.76"); - case AXP2101_VINDPM_e::Vin_4_84V: return F("4.84"); - case AXP2101_VINDPM_e::Vin_4_92V: return F("4.92"); - case AXP2101_VINDPM_e::Vin_5_00V: return F("5.00"); - case AXP2101_VINDPM_e::Vin_5_08V: return F("5.08"); - } - return F(""); -} - - -const __FlashStringHelper* toString(AXP2101_InputCurrentLimit_e current) { - switch (current) { - case AXP2101_InputCurrentLimit_e::limit_100mA: return F("100"); - case AXP2101_InputCurrentLimit_e::limit_500mA: return F("500"); - case AXP2101_InputCurrentLimit_e::limit_900mA: return F("900"); - case AXP2101_InputCurrentLimit_e::limit_1000mA: return F("1000"); - case AXP2101_InputCurrentLimit_e::limit_1500mA: return F("1500"); - case AXP2101_InputCurrentLimit_e::limit_2000mA: return F("2000"); - } - return F(""); -} - - AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index 380f5e5627..29278250e6 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -232,7 +232,8 @@ enum class AXP2101_CV_charger_voltage_e : uint8_t { limit_4_10V = 0b010, limit_4_20V = 0b011, // default limit_4_35V = 0b100, - limit_4_40V = 0b101 + limit_4_40V = 0b101, + MAX }; enum class AXP2101_Linear_Charger_Vsys_dpm_e : uint8_t { @@ -243,7 +244,8 @@ enum class AXP2101_Linear_Charger_Vsys_dpm_e : uint8_t { vsys_4_5V, vsys_4_6V, vsys_4_7V, - vsys_4_8V + vsys_4_8V, + MAX }; enum class AXP2101_VINDPM_e : uint8_t { @@ -262,7 +264,8 @@ enum class AXP2101_VINDPM_e : uint8_t { Vin_4_84V, Vin_4_92V, Vin_5_00V, - Vin_5_08V + Vin_5_08V, + MAX }; enum class AXP2101_InputCurrentLimit_e : uint8_t { @@ -271,7 +274,8 @@ enum class AXP2101_InputCurrentLimit_e : uint8_t { limit_900mA, limit_1000mA, limit_1500mA, - limit_2000mA + limit_2000mA, + MAX }; @@ -291,10 +295,6 @@ const __FlashStringHelper* toString(AXP2101_chargingState_e state); const __FlashStringHelper* toString(AXP2101_chipid_e chip); const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge); const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage); -const __FlashStringHelper* toString(AXP2101_Linear_Charger_Vsys_dpm_e vsys); -const __FlashStringHelper* toString(AXP2101_VINDPM_e voltage); -const __FlashStringHelper* toString(AXP2101_InputCurrentLimit_e current); - class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 769f3b46e5..f95dec1f9e 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -139,23 +139,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { const bool isPowerManagerTask = Settings.isPowerManagerTask(event->TaskIndex); { - const __FlashStringHelper *chargeledNames[] = { - toString(AXP2101_chargeled_d::Off), - toString(AXP2101_chargeled_d::Flash_1Hz), - toString(AXP2101_chargeled_d::Flash_4Hz), - toString(AXP2101_chargeled_d::Steady_On), - }; - const int chargeledValues[] = { - static_cast(AXP2101_chargeled_d::Off), - static_cast(AXP2101_chargeled_d::Flash_1Hz), - static_cast(AXP2101_chargeled_d::Flash_4Hz), - static_cast(AXP2101_chargeled_d::Steady_On), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(chargeledValues); - addFormSelector(F("Charge LED"), F("led"), - valueCount, - chargeledNames, chargeledValues, - static_cast(_settings.getChargeLed())); + AXP2101_ChargeLED_FormSelector selector(_settings.getChargeLed()); } { // Reg 61: Iprechg Charger Settings @@ -180,104 +164,17 @@ void P139_data_struct::webform_load(struct EventStruct *event) { } { // Reg 14: Minimum System Voltage Control - const __FlashStringHelper *names[] = { - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), - toString(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), - }; - const int values[] = { - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_1V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_2V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_3V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_4V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_5V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_6V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_7V), - static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::vsys_4_8V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Minimum System Voltage"), F("min_vsys"), - valueCount, - names, values, - static_cast(_settings.getLinear_Charger_Vsys_dpm())); - addUnit(F("V")); + AXP2101_Linear_Charger_Vsys_dpm_FormSelector selector(_settings.getLinear_Charger_Vsys_dpm()); } { // Reg 15: Input Voltage Limit - const __FlashStringHelper *names[] = { - toString(AXP2101_VINDPM_e::Vin_3_88V), - toString(AXP2101_VINDPM_e::Vin_3_96V), - toString(AXP2101_VINDPM_e::Vin_4_04V), - toString(AXP2101_VINDPM_e::Vin_4_12V), - toString(AXP2101_VINDPM_e::Vin_4_20V), - toString(AXP2101_VINDPM_e::Vin_4_28V), - toString(AXP2101_VINDPM_e::Vin_4_36V), - toString(AXP2101_VINDPM_e::Vin_4_44V), - toString(AXP2101_VINDPM_e::Vin_4_52V), - toString(AXP2101_VINDPM_e::Vin_4_60V), - toString(AXP2101_VINDPM_e::Vin_4_68V), - toString(AXP2101_VINDPM_e::Vin_4_76V), - toString(AXP2101_VINDPM_e::Vin_4_84V), - toString(AXP2101_VINDPM_e::Vin_4_92V), - toString(AXP2101_VINDPM_e::Vin_5_00V), - toString(AXP2101_VINDPM_e::Vin_5_08V), - }; - const int values[] = { - static_cast(AXP2101_VINDPM_e::Vin_3_88V), - static_cast(AXP2101_VINDPM_e::Vin_3_96V), - static_cast(AXP2101_VINDPM_e::Vin_4_04V), - static_cast(AXP2101_VINDPM_e::Vin_4_12V), - static_cast(AXP2101_VINDPM_e::Vin_4_20V), - static_cast(AXP2101_VINDPM_e::Vin_4_28V), - static_cast(AXP2101_VINDPM_e::Vin_4_36V), - static_cast(AXP2101_VINDPM_e::Vin_4_44V), - static_cast(AXP2101_VINDPM_e::Vin_4_52V), - static_cast(AXP2101_VINDPM_e::Vin_4_60V), - static_cast(AXP2101_VINDPM_e::Vin_4_68V), - static_cast(AXP2101_VINDPM_e::Vin_4_76V), - static_cast(AXP2101_VINDPM_e::Vin_4_84V), - static_cast(AXP2101_VINDPM_e::Vin_4_92V), - static_cast(AXP2101_VINDPM_e::Vin_5_00V), - static_cast(AXP2101_VINDPM_e::Vin_5_08V), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Min Vin DPM Voltage"), F("vin_dpm"), - valueCount, - names, values, - static_cast(_settings.getVin_DPM())); - addUnit(F("V")); + AXP2101_Vin_DPM_FormSelector selector(_settings.getVin_DPM()); } { // Reg 16: Input Current Limit - const __FlashStringHelper *names[] = { - toString(AXP2101_InputCurrentLimit_e::limit_100mA), - toString(AXP2101_InputCurrentLimit_e::limit_500mA), - toString(AXP2101_InputCurrentLimit_e::limit_900mA), - toString(AXP2101_InputCurrentLimit_e::limit_1000mA), - toString(AXP2101_InputCurrentLimit_e::limit_1500mA), - toString(AXP2101_InputCurrentLimit_e::limit_2000mA), - }; - const int values[] = { - static_cast(AXP2101_InputCurrentLimit_e::limit_100mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_500mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_900mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_1000mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_1500mA), - static_cast(AXP2101_InputCurrentLimit_e::limit_2000mA), - }; - constexpr uint8_t valueCount = NR_ELEMENTS(values); - addFormSelector(F("Input Current Limit"), F("cur_lim_in"), - valueCount, - names, values, - static_cast(_settings.getInputCurrentLimit())); - addUnit(F("mA")); + AXP2101_InputCurrentLimit_FormSelector selector(_settings.getInputCurrentLimit()); } addFormCheckBox(F("Disable TS pin"), F("dis_TS"), _settings.getTS_disabled()); diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.cpp b/src/src/PluginStructs/P139_data_struct_formselectors.cpp index 2e12be151e..a0aaddb025 100644 --- a/src/src/PluginStructs/P139_data_struct_formselectors.cpp +++ b/src/src/PluginStructs/P139_data_struct_formselectors.cpp @@ -3,6 +3,37 @@ #ifdef USES_P139 +// ********************************************************************** +// Charge LED settings +// ********************************************************************** +AXP2101_ChargeLED_FormSelector::AXP2101_ChargeLED_FormSelector( + AXP2101_chargeled_d selected) : + FormSelectorOptions(5) +{ + addFormSelector( + F("Charge LED"), F("led"), + static_cast(selected)); +} + +AXP2101_chargeled_d get_AXP2101_chargeled_d(int index) { + if (index < 0) { return AXP2101_chargeled_d::Off; } + + if (index <= static_cast(AXP2101_chargeled_d::Steady_On)) { + return static_cast(index); + } + return AXP2101_chargeled_d::Protected; +} + +String AXP2101_ChargeLED_FormSelector::getOptionString(int index) const +{ + return toString(get_AXP2101_chargeled_d(index)); +} + +int AXP2101_ChargeLED_FormSelector::getIndexValue(int index) const +{ + return static_cast(get_AXP2101_chargeled_d(index)); +} + // ********************************************************************** // Reg 61: Iprechg Charger Settings // 0 .. 200 mA in 25 mA steps @@ -34,8 +65,6 @@ int AXP2101_PreChargeCurrentLimit_FormSelector::getIndexValue(int index) const return get_AXP2101_0_to_200mA_ChargeCurrentLimit(index); } - - // ********************************************************************** // Reg 62: ICC Charger Settings // 0 .. 200 mA in 25 mA steps @@ -52,9 +81,10 @@ AXP2101_ConstChargeCurrentLimit_FormSelector::AXP2101_ConstChargeCurrentLimit_Fo int get_AXP2101_ConstChargeCurrentLimit(int index) { if (index < 0) { return 0; } - const int res = index <= 8 + const int res = index <= 8 ? 25 * index : (index - 8) * 100 + 200; + if (res > 1000) { return 1000; } return res; } @@ -69,8 +99,6 @@ int AXP2101_ConstChargeCurrentLimit_FormSelector::getIndexValue(int index) const return get_AXP2101_ConstChargeCurrentLimit(index); } - - // ********************************************************************** // Reg 63: Iterm Charger Settings and Control // 0 .. 200 mA in 25 mA steps + enable checkbox @@ -94,15 +122,12 @@ int AXP2101_TerminationChargeCurrentLimit_FormSelector::getIndexValue(int index) return get_AXP2101_0_to_200mA_ChargeCurrentLimit(index); } - - - // ********************************************************************** // Reg 64: CV Charger Voltage Settings // ********************************************************************** AXP2101_CV_charger_voltage_FormSelector::AXP2101_CV_charger_voltage_FormSelector( AXP2101_CV_charger_voltage_e selected) : - FormSelectorOptions(static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) + FormSelectorOptions(static_cast(AXP2101_CV_charger_voltage_e::MAX) - 1) { addFormSelector( F("CV Charger Voltage"), F("cv_volt"), @@ -113,10 +138,10 @@ AXP2101_CV_charger_voltage_FormSelector::AXP2101_CV_charger_voltage_FormSelector AXP2101_CV_charger_voltage_e get_AXP2101_CV_charger_voltage_e(int index) { if (index < 0) { return AXP2101_CV_charger_voltage_e::reserved; } constexpr int offset = static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V); - constexpr int max = static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V) + offset; + constexpr int max = static_cast(AXP2101_CV_charger_voltage_e::MAX); index += offset; - if (index > max) { return AXP2101_CV_charger_voltage_e::reserved; } + if (index >= max) { return AXP2101_CV_charger_voltage_e::reserved; } return static_cast(index); } @@ -130,4 +155,76 @@ int AXP2101_CV_charger_voltage_FormSelector::getIndexValue(int index) const return static_cast(get_AXP2101_CV_charger_voltage_e(index)); } +// ********************************************************************** +// Reg 14: Minimum System Voltage Control +// ********************************************************************** +AXP2101_Linear_Charger_Vsys_dpm_FormSelector::AXP2101_Linear_Charger_Vsys_dpm_FormSelector( + AXP2101_Linear_Charger_Vsys_dpm_e selected) : + FormSelectorOptions(static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::MAX)) +{ + addFormSelector( + F("Minimum System Voltage"), F("min_vsys"), + static_cast(selected)); + addUnit(F("V")); +} + +String AXP2101_Linear_Charger_Vsys_dpm_FormSelector::getOptionString(int index) const +{ + constexpr int max = static_cast(AXP2101_Linear_Charger_Vsys_dpm_e::MAX); + + if ((index >= max) || (index < 0)) { + return EMPTY_STRING; + } + return concat(F("4."), index + 1); +} + +// ********************************************************************** +// Reg 15: Input Voltage Limit +// ********************************************************************** +AXP2101_Vin_DPM_FormSelector::AXP2101_Vin_DPM_FormSelector( + AXP2101_VINDPM_e selected) : + FormSelectorOptions(static_cast(AXP2101_VINDPM_e::MAX)) +{ + addFormSelector( + F("Min Vin DPM Voltage"), F("vin_dpm"), + static_cast(selected)); + addUnit(F("V")); +} + +String AXP2101_Vin_DPM_FormSelector::getOptionString(int index) const +{ + constexpr int max = static_cast(AXP2101_VINDPM_e::MAX); + + if ((index >= max) || (index < 0)) { + return EMPTY_STRING; + } + return toString(3.88f + 0.08f * index, 2); +} + +// ********************************************************************** +// Reg 16: Input Current Limit +// ********************************************************************** +AXP2101_InputCurrentLimit_FormSelector::AXP2101_InputCurrentLimit_FormSelector( + AXP2101_InputCurrentLimit_e selected) : + FormSelectorOptions(static_cast(AXP2101_InputCurrentLimit_e::MAX)) +{ + addFormSelector( + F("Input Current Limit"), F("cur_lim_in"), + static_cast(selected)); + addUnit(F("mA")); +} + +String AXP2101_InputCurrentLimit_FormSelector::getOptionString(int index) const +{ + constexpr int max = static_cast(AXP2101_InputCurrentLimit_e::MAX); + + if ((index >= max) || (index < 0)) { + return EMPTY_STRING; + } + const int value = (index < 3) + ? 100 + (400 * index) + : 1000 + ((index - 3)) * 500; + return String(value); +} + #endif // ifdef USES_P139 diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.h b/src/src/PluginStructs/P139_data_struct_formselectors.h index 5aa4327c02..5587e62388 100644 --- a/src/src/PluginStructs/P139_data_struct_formselectors.h +++ b/src/src/PluginStructs/P139_data_struct_formselectors.h @@ -6,6 +6,21 @@ #include +// ********************************************************************** +// Charge LED settings +// ********************************************************************** +class AXP2101_ChargeLED_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_ChargeLED_FormSelector(AXP2101_chargeled_d selected); + virtual ~AXP2101_ChargeLED_FormSelector() {} + + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; + +}; + + // ********************************************************************** // Reg 61: Iprechg Charger Settings // 0 .. 200 mA in 25 mA steps @@ -72,5 +87,49 @@ class AXP2101_CV_charger_voltage_FormSelector : public FormSelectorOptions +// ********************************************************************** +// Reg 14: Minimum System Voltage Control +// ********************************************************************** +class AXP2101_Linear_Charger_Vsys_dpm_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_Linear_Charger_Vsys_dpm_FormSelector(AXP2101_Linear_Charger_Vsys_dpm_e selected); + virtual ~AXP2101_Linear_Charger_Vsys_dpm_FormSelector() {} + + virtual String getOptionString(int index) const override; + +}; + + + +// ********************************************************************** +// Reg 15: Input Voltage Limit +// ********************************************************************** +class AXP2101_Vin_DPM_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_Vin_DPM_FormSelector(AXP2101_VINDPM_e selected); + virtual ~AXP2101_Vin_DPM_FormSelector() {} + + virtual String getOptionString(int index) const override; + +}; + + + +// ********************************************************************** +// Reg 16: Input Current Limit +// ********************************************************************** +class AXP2101_InputCurrentLimit_FormSelector : public FormSelectorOptions +{ +public: + AXP2101_InputCurrentLimit_FormSelector(AXP2101_InputCurrentLimit_e selected); + virtual ~AXP2101_InputCurrentLimit_FormSelector() {} + + virtual String getOptionString(int index) const override; + +}; + + #endif \ No newline at end of file From 65320c8dee82d414fc9e5f872398cb72c847ee6a Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 19:37:51 +0100 Subject: [PATCH 29/50] [P139] Fix ESP8266 build + show device state --- lib/AXP2101/src/AXP2101.cpp | 1 + lib/AXP2101/src/AXP2101_settings.cpp | 12 -- lib/AXP2101/src/AXP2101_settings.h | 1 - src/src/DataTypes/FormSelectorOptions.cpp | 9 ++ src/src/DataTypes/FormSelectorOptions.h | 4 +- src/src/PluginStructs/P139_data_struct.cpp | 119 +++++++++++------- src/src/PluginStructs/P139_data_struct.h | 1 - .../P139_data_struct_formselectors.cpp | 66 ++++++++-- .../P139_data_struct_formselectors.h | 69 +++++----- 9 files changed, 181 insertions(+), 101 deletions(-) diff --git a/lib/AXP2101/src/AXP2101.cpp b/lib/AXP2101/src/AXP2101.cpp index 9182873ec2..1246026860 100644 --- a/lib/AXP2101/src/AXP2101.cpp +++ b/lib/AXP2101/src/AXP2101.cpp @@ -259,6 +259,7 @@ uint16_t AXP2101::registerToVoltage(uint8_t data, } // Values in mVolt, raw ADC data is expressed in 0.5 mV +// LUT is based on 10k NTC with 50 uA current constexpr uint16_t axp2101_TS_LUT[] { 3150, // -20 2508, // -15 diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index 2728c813a7..a1b569228a 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -108,18 +108,6 @@ const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge) { return F(""); } -const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage) { - switch (voltage) { - case AXP2101_CV_charger_voltage_e::limit_4_00V: return F("4.0"); - case AXP2101_CV_charger_voltage_e::limit_4_10V: return F("4.1"); - case AXP2101_CV_charger_voltage_e::limit_4_20V: return F("4.2"); - case AXP2101_CV_charger_voltage_e::limit_4_35V: return F("4.35"); - case AXP2101_CV_charger_voltage_e::limit_4_40V: return F("4.4"); - case AXP2101_CV_charger_voltage_e::reserved: break; - } - return F(""); -} - AXP2101_registers_e AXP2101_intToRegister(int reg) { switch (reg) { case 0: return AXP2101_registers_e::dcdc1; diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index 29278250e6..c2e0cc6633 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -294,7 +294,6 @@ const __FlashStringHelper* toString(AXP2101_chargeled_d led); const __FlashStringHelper* toString(AXP2101_chargingState_e state); const __FlashStringHelper* toString(AXP2101_chipid_e chip); const __FlashStringHelper* toString(AXP2101_chargingDetail_e charge); -const __FlashStringHelper* toString(AXP2101_CV_charger_voltage_e voltage); class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the AXP2101 pin/port used. public: diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index ecbbc9504c..1a7a968c23 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -2,11 +2,18 @@ #include "../WebServer/Markup.h" +FormSelectorOptions::FormSelectorOptions(int optionCount) +: _optionCount(optionCount) +{ + classname = F("wide"); +} + FormSelectorOptions::FormSelectorOptions( int optionCount, const int indices[], const String attr[]) : _optionCount(optionCount) { + classname = F("wide"); _indices = new int[optionCount]; if (attr != nullptr) { @@ -28,6 +35,7 @@ FormSelectorOptions::FormSelectorOptions( const int indices[], const String attr[]) : _optionCount(optionCount) { + classname = F("wide"); _names_str = new String[optionCount]; if (indices != nullptr) { @@ -58,6 +66,7 @@ FormSelectorOptions::FormSelectorOptions( const int indices[], const String attr[]) : _optionCount(optionCount) { + classname = F("wide"); _names_f = new const __FlashStringHelper *[optionCount]; if (indices != nullptr) { diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index 470541dcbc..99cd2dc5f5 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -6,7 +6,7 @@ class FormSelectorOptions { public: - FormSelectorOptions(int optionCount) : _optionCount(optionCount) {} + FormSelectorOptions(int optionCount); FormSelectorOptions(int optionCount, const int indices[], @@ -49,7 +49,7 @@ class FormSelectorOptions { bool reloadonchange = false; bool enabled = true; - const __FlashStringHelper * classname = F("wide"); + const __FlashStringHelper * classname; #if FEATURE_TOOLTIPS String tooltip; #endif // if FEATURE_TOOLTIPS diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index f95dec1f9e..87ac98e148 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -4,7 +4,7 @@ # ifdef ESP32 -#include "../PluginStructs/P139_data_struct_formselectors.h" +# include "../PluginStructs/P139_data_struct_formselectors.h" // **************************************************************************/ // Constructors @@ -47,8 +47,9 @@ String P139_data_struct::loadSettings(struct EventStruct *event) { // applySettings: Write the current settings to AXP2101 // **************************************************************************/ void P139_data_struct::applySettings(struct EventStruct *event) { - if (!isInitialized()) + if (!isInitialized()) { return; + } uint8_t count = 0; for (int s = 0; s < AXP2101_settings_count; ++s) { @@ -103,7 +104,7 @@ void P139_data_struct::applySettings(struct EventStruct *event) { axp2101->setTS_disabled(_settings.getTS_disabled()); -// axp2101->set_IRQ_enable_0(0b11110000); // Disable temperature checks + // axp2101->set_IRQ_enable_0(0b11110000); // Disable temperature checks axp2101->setConstChargeCurrentLimit(_settings.getConstChargeCurrentLimit()); } @@ -153,9 +154,9 @@ void P139_data_struct::webform_load(struct EventStruct *event) { { // Reg 63: Iterm Charger Settings and Control // 0 .. 200 mA in 25 mA steps + enable checkbox - AXP2101_PreChargeCurrentLimit_FormSelector selector(_settings.getTerminationChargeCurrentLimit()); - - // TODO TD-er: Must add 'enabled' checkbox + AXP2101_TerminationChargeCurrentLimit_FormSelector selector(_settings.getTerminationChargeCurrentLimit()); + addFormCheckBox(F("Enable CV Charging"), F("iterm_en"), _settings.getTerminationChargeCurrentLimitEnable()); + addFormNote(F("When enabled, the last part of the charge cycle is done using constant voltage (CV)")); } { @@ -178,7 +179,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { } addFormCheckBox(F("Disable TS pin"), F("dis_TS"), _settings.getTS_disabled()); - addFormNote(F("Make sure to disable TS pin when no NTC is used, or else the battery will not charge")); + addFormNote(F("Make sure to disable TS pin when no battery temperature sensor is used, or else the battery will not charge")); addFormCheckBox(F("Generate events"), F("events"), P139_GET_GENERATE_EVENTS); @@ -275,13 +276,42 @@ void P139_data_struct::webform_load(struct EventStruct *event) { static_cast(pin)); } } + html_end_table(); addFormNote(F("Check your device documentation for what is connected to each output.")); + + if (isInitialized()) { + addFormSubHeader(F("Current State")); + + const AXP2101_registers_e registers[] = { + AXP2101_registers_e::vbat, + AXP2101_registers_e::vbus, + AXP2101_registers_e::vsys, + AXP2101_registers_e::battemp, + AXP2101_registers_e::chiptemp + }; + + for (size_t i = 0; i < NR_ELEMENTS(registers); ++i) { + addRowLabel(toString(registers[i], true)); + + if ((registers[i] == AXP2101_registers_e::battemp) || + (registers[i] == AXP2101_registers_e::chiptemp)) + { + addHtmlFloat(read_value(registers[i])); + addUnit(F("°C")); + } else { + addHtmlInt(static_cast(read_value(registers[i]))); + addUnit(F("mV")); + } + } + + addRowLabel(F("Charging State")); + addHtml(toString(axp2101->getChargingDetail())); + } } } - void P139_data_struct::webform_save(struct EventStruct *event) { for (uint8_t i = 0; i < P139_NR_OUTPUT_VALUES; ++i) { sensorTypeHelper_saveOutputSelector(event, P139_CONFIG_BASE + i, i, @@ -300,37 +330,36 @@ void P139_data_struct::webform_save(struct EventStruct *event) { } } - _settings.setChargeLed(static_cast(getFormItemInt(F("led")))); + _settings.setChargeLed(AXP2101_ChargeLED_FormSelector::get()); // Reg 61: Iprechg Charger Settings - _settings.setPreChargeCurrentLimit(getFormItemInt(F("iprechg"))); + _settings.setPreChargeCurrentLimit(AXP2101_PreChargeCurrentLimit_FormSelector::get()); // Reg 62: ICC Charger Settings - _settings.setConstChargeCurrentLimit(getFormItemInt(F("iccchg"))); + _settings.setConstChargeCurrentLimit(AXP2101_ConstChargeCurrentLimit_FormSelector::get()); // Reg 63: Iterm Charger Settings and Control _settings.setTerminationChargeCurrentLimit( - getFormItemInt(F("iterm")), + AXP2101_TerminationChargeCurrentLimit_FormSelector::get(), isFormItemChecked(F("iterm_en"))); // Reg 64: CV Charger Voltage Settings - _settings.setCV_chargeVoltage(static_cast(getFormItemInt(F("cv_volt")))); + _settings.setCV_chargeVoltage(AXP2101_CV_charger_voltage_FormSelector::get()); // Reg 14: Minimum System Voltage Control - _settings.setLinear_Charger_Vsys_dpm(static_cast(getFormItemInt(F("min_vsys")))); + _settings.setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_FormSelector::get()); // Reg 15: Input Voltage Limit - _settings.setVin_DPM(static_cast(getFormItemInt(F("vin_dpm")))); + _settings.setVin_DPM(AXP2101_Vin_DPM_FormSelector::get()); // Reg 16: Input Current Limit - _settings.setInputCurrentLimit(static_cast(getFormItemInt(F("cur_lim_in")))); + _settings.setInputCurrentLimit(AXP2101_InputCurrentLimit_FormSelector::get()); _settings.setTS_disabled(isFormItemChecked(F("dis_TS"))); saveSettings(event); } - // **************************************************************************/ // plugin_read: Read the values and send to controller(s) // **************************************************************************/ @@ -353,32 +382,32 @@ float P139_data_struct::read_value(AXP2101_registers_e value) { if (isInitialized()) { switch (value) { - case AXP2101_registers_e::chargeled: - return static_cast(axp2101->getChargeLed()); - case AXP2101_registers_e::batcharge: - return static_cast(axp2101->getBatCharge()); - case AXP2101_registers_e::charging: - return static_cast(axp2101->getChargingState()); - case AXP2101_registers_e::batpresent: - return static_cast(axp2101->isBatteryDetected()); - case AXP2101_registers_e::chipid: - return static_cast(axp2101->getChipIDRaw()); - case AXP2101_registers_e::chargedet: - return static_cast(axp2101->getChargingDetail()); - - case AXP2101_registers_e::vbat: - case AXP2101_registers_e::vbus: - case AXP2101_registers_e::vsys: - return static_cast(axp2101->getADCVoltage(value)); - - case AXP2101_registers_e::battemp: - return axp2101->TS_registerToTemp(axp2101->getADCVoltage(value)); - - case AXP2101_registers_e::chiptemp: - return (22.0f + (7274 - axp2101->getADCVoltage(value)) / 20.0f); - - default: - return static_cast(axp2101->getPortVoltage(value)); + case AXP2101_registers_e::chargeled: + return static_cast(axp2101->getChargeLed()); + case AXP2101_registers_e::batcharge: + return static_cast(axp2101->getBatCharge()); + case AXP2101_registers_e::charging: + return static_cast(axp2101->getChargingState()); + case AXP2101_registers_e::batpresent: + return static_cast(axp2101->isBatteryDetected()); + case AXP2101_registers_e::chipid: + return static_cast(axp2101->getChipIDRaw()); + case AXP2101_registers_e::chargedet: + return static_cast(axp2101->getChargingDetail()); + + case AXP2101_registers_e::vbat: + case AXP2101_registers_e::vbus: + case AXP2101_registers_e::vsys: + return static_cast(axp2101->getADCVoltage(value)); + + case AXP2101_registers_e::battemp: + return axp2101->TS_registerToTemp(axp2101->getADCVoltage(value)); + + case AXP2101_registers_e::chiptemp: + return 22.0f + (7274 - axp2101->getADCVoltage(value)) / 20.0f; + + default: + return static_cast(axp2101->getPortVoltage(value)); } } return 0.0f; @@ -634,8 +663,8 @@ bool P139_data_struct::plugin_get_config_value(struct EventStruct *event, const AXP2101_registers_e reg = AXP2101_intToRegister(r); if (equals(command, toString(reg, false))) { // Voltage (mV) / numeric state - if (reg == AXP2101_registers_e::battemp || - reg == AXP2101_registers_e::chiptemp) + if ((reg == AXP2101_registers_e::battemp) || + (reg == AXP2101_registers_e::chiptemp)) { string = floatToString(read_value(reg), 2); } else { diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index 69331c924d..04a30cbd8f 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -35,7 +35,6 @@ # define P139_CONST_MAX_LDO 3700 // Max. output voltage - struct P139_data_struct : public PluginTaskData_base { public: diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.cpp b/src/src/PluginStructs/P139_data_struct_formselectors.cpp index a0aaddb025..c4506096d9 100644 --- a/src/src/PluginStructs/P139_data_struct_formselectors.cpp +++ b/src/src/PluginStructs/P139_data_struct_formselectors.cpp @@ -15,6 +15,11 @@ AXP2101_ChargeLED_FormSelector::AXP2101_ChargeLED_FormSelector( static_cast(selected)); } +AXP2101_chargeled_d AXP2101_ChargeLED_FormSelector::get() +{ + return static_cast(getFormItemInt(F("led"))); +} + AXP2101_chargeled_d get_AXP2101_chargeled_d(int index) { if (index < 0) { return AXP2101_chargeled_d::Off; } @@ -42,9 +47,15 @@ AXP2101_PreChargeCurrentLimit_FormSelector::AXP2101_PreChargeCurrentLimit_FormSe int selected) : FormSelectorOptions(9) { addFormSelector( - F("Term Charge Current"), F("iterm"), + F("Pre Charge Current"), F("iprechg"), static_cast(selected)); addUnit(F("mA")); + addFormNote(F("When V_bat < 3V, the battery will be charged using pre-charge current")); +} + +int AXP2101_PreChargeCurrentLimit_FormSelector::get() +{ + return getFormItemInt(F("iprechg")); } int get_AXP2101_0_to_200mA_ChargeCurrentLimit(int index) { @@ -79,6 +90,11 @@ AXP2101_ConstChargeCurrentLimit_FormSelector::AXP2101_ConstChargeCurrentLimit_Fo addUnit(F("mA")); } +int AXP2101_ConstChargeCurrentLimit_FormSelector::get() +{ + return getFormItemInt(F("iccchg")); +} + int get_AXP2101_ConstChargeCurrentLimit(int index) { if (index < 0) { return 0; } const int res = index <= 8 @@ -107,9 +123,15 @@ AXP2101_TerminationChargeCurrentLimit_FormSelector::AXP2101_TerminationChargeCur int selected) : FormSelectorOptions(9) { addFormSelector( - F("Pre Charge Current"), F("iprechg"), + F("Termination Charge Current"), F("iterm"), static_cast(selected)); addUnit(F("mA")); + addFormNote(F("Charge current threshold to switch to CV charging or stop charging, when battery is nearly full")); +} + +int AXP2101_TerminationChargeCurrentLimit_FormSelector::get() +{ + return getFormItemInt(F("iterm")); } String AXP2101_TerminationChargeCurrentLimit_FormSelector::getOptionString(int index) const @@ -133,13 +155,18 @@ AXP2101_CV_charger_voltage_FormSelector::AXP2101_CV_charger_voltage_FormSelector F("CV Charger Voltage"), F("cv_volt"), static_cast(selected)); addUnit(F("V")); + addFormNote(F("Target charge voltage of battery")); +} + +AXP2101_CV_charger_voltage_e AXP2101_CV_charger_voltage_FormSelector::get() +{ + return static_cast(getFormItemInt(F("cv_volt"))); } AXP2101_CV_charger_voltage_e get_AXP2101_CV_charger_voltage_e(int index) { if (index < 0) { return AXP2101_CV_charger_voltage_e::reserved; } - constexpr int offset = static_cast(AXP2101_CV_charger_voltage_e::limit_4_00V); - constexpr int max = static_cast(AXP2101_CV_charger_voltage_e::MAX); - index += offset; + constexpr int max = static_cast(AXP2101_CV_charger_voltage_e::MAX); + index += 1; if (index >= max) { return AXP2101_CV_charger_voltage_e::reserved; } return static_cast(index); @@ -147,7 +174,13 @@ AXP2101_CV_charger_voltage_e get_AXP2101_CV_charger_voltage_e(int index) { String AXP2101_CV_charger_voltage_FormSelector::getOptionString(int index) const { - return toString(get_AXP2101_CV_charger_voltage_e(index)); + const AXP2101_CV_charger_voltage_e val = get_AXP2101_CV_charger_voltage_e(index); + int decimal = index; + + if (AXP2101_CV_charger_voltage_e::limit_4_35V == val) { + index = 35; + } + return concat(F("4."), index); } int AXP2101_CV_charger_voltage_FormSelector::getIndexValue(int index) const @@ -166,6 +199,12 @@ AXP2101_Linear_Charger_Vsys_dpm_FormSelector::AXP2101_Linear_Charger_Vsys_dpm_Fo F("Minimum System Voltage"), F("min_vsys"), static_cast(selected)); addUnit(F("V")); + addFormNote(F("Minimum system voltage to allow charging the battery")); +} + +AXP2101_Linear_Charger_Vsys_dpm_e AXP2101_Linear_Charger_Vsys_dpm_FormSelector::get() +{ + return static_cast(getFormItemInt(F("min_vsys"))); } String AXP2101_Linear_Charger_Vsys_dpm_FormSelector::getOptionString(int index) const @@ -186,9 +225,15 @@ AXP2101_Vin_DPM_FormSelector::AXP2101_Vin_DPM_FormSelector( FormSelectorOptions(static_cast(AXP2101_VINDPM_e::MAX)) { addFormSelector( - F("Min Vin DPM Voltage"), F("vin_dpm"), + F("Min Vin_dpm Voltage"), F("vin_dpm"), static_cast(selected)); addUnit(F("V")); + addFormNote(F("When Vbus reaches Vin_dpm, the charge current will decrease until zero")); +} + +AXP2101_VINDPM_e AXP2101_Vin_DPM_FormSelector::get() +{ + return static_cast(getFormItemInt(F("vin_dpm"))); } String AXP2101_Vin_DPM_FormSelector::getOptionString(int index) const @@ -212,6 +257,13 @@ AXP2101_InputCurrentLimit_FormSelector::AXP2101_InputCurrentLimit_FormSelector( F("Input Current Limit"), F("cur_lim_in"), static_cast(selected)); addUnit(F("mA")); + // If I_sys is over the input power supply capability, V_sys will drop. + // If V_bat is above V_sys, PMU will enter supplement mode. +} + +AXP2101_InputCurrentLimit_e AXP2101_InputCurrentLimit_FormSelector::get() +{ + return static_cast(getFormItemInt(F("cur_lim_in"))); } String AXP2101_InputCurrentLimit_FormSelector::getOptionString(int index) const diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.h b/src/src/PluginStructs/P139_data_struct_formselectors.h index 5587e62388..dac697161a 100644 --- a/src/src/PluginStructs/P139_data_struct_formselectors.h +++ b/src/src/PluginStructs/P139_data_struct_formselectors.h @@ -4,20 +4,21 @@ #ifdef USES_P139 -#include +# include // ********************************************************************** // Charge LED settings // ********************************************************************** -class AXP2101_ChargeLED_FormSelector : public FormSelectorOptions -{ +class AXP2101_ChargeLED_FormSelector : public FormSelectorOptions { public: + AXP2101_ChargeLED_FormSelector(AXP2101_chargeled_d selected); virtual ~AXP2101_ChargeLED_FormSelector() {} - virtual String getOptionString(int index) const override; - virtual int getIndexValue(int index) const override; + static AXP2101_chargeled_d get(); + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; }; @@ -25,15 +26,16 @@ class AXP2101_ChargeLED_FormSelector : public FormSelectorOptions // Reg 61: Iprechg Charger Settings // 0 .. 200 mA in 25 mA steps // ********************************************************************** -class AXP2101_PreChargeCurrentLimit_FormSelector : public FormSelectorOptions -{ +class AXP2101_PreChargeCurrentLimit_FormSelector : public FormSelectorOptions { public: + AXP2101_PreChargeCurrentLimit_FormSelector(int selected); virtual ~AXP2101_PreChargeCurrentLimit_FormSelector() {} + static int get(); + virtual String getOptionString(int index) const override; virtual int getIndexValue(int index) const override; - }; @@ -42,94 +44,95 @@ class AXP2101_PreChargeCurrentLimit_FormSelector : public FormSelectorOptions // 0 .. 200 mA in 25 mA steps // 200 ... 1000 mA in 100 mA steps // ********************************************************************** -class AXP2101_ConstChargeCurrentLimit_FormSelector : public FormSelectorOptions -{ +class AXP2101_ConstChargeCurrentLimit_FormSelector : public FormSelectorOptions { public: + AXP2101_ConstChargeCurrentLimit_FormSelector(int selected); virtual ~AXP2101_ConstChargeCurrentLimit_FormSelector() {} + static int get(); + virtual String getOptionString(int index) const override; virtual int getIndexValue(int index) const override; - }; - // ********************************************************************** // Reg 63: Iterm Charger Settings and Control // 0 .. 200 mA in 25 mA steps + enable checkbox // ********************************************************************** -class AXP2101_TerminationChargeCurrentLimit_FormSelector : public FormSelectorOptions -{ +class AXP2101_TerminationChargeCurrentLimit_FormSelector : public FormSelectorOptions { public: + AXP2101_TerminationChargeCurrentLimit_FormSelector(int selected); virtual ~AXP2101_TerminationChargeCurrentLimit_FormSelector() {} + static int get(); + virtual String getOptionString(int index) const override; virtual int getIndexValue(int index) const override; - }; // ********************************************************************** // Reg 64: CV Charger Voltage Settings // ********************************************************************** -class AXP2101_CV_charger_voltage_FormSelector : public FormSelectorOptions -{ +class AXP2101_CV_charger_voltage_FormSelector : public FormSelectorOptions { public: + AXP2101_CV_charger_voltage_FormSelector(AXP2101_CV_charger_voltage_e selected); virtual ~AXP2101_CV_charger_voltage_FormSelector() {} - virtual String getOptionString(int index) const override; - virtual int getIndexValue(int index) const override; + static AXP2101_CV_charger_voltage_e get(); + virtual String getOptionString(int index) const override; + virtual int getIndexValue(int index) const override; }; - // ********************************************************************** // Reg 14: Minimum System Voltage Control // ********************************************************************** -class AXP2101_Linear_Charger_Vsys_dpm_FormSelector : public FormSelectorOptions -{ +class AXP2101_Linear_Charger_Vsys_dpm_FormSelector : public FormSelectorOptions { public: + AXP2101_Linear_Charger_Vsys_dpm_FormSelector(AXP2101_Linear_Charger_Vsys_dpm_e selected); virtual ~AXP2101_Linear_Charger_Vsys_dpm_FormSelector() {} - virtual String getOptionString(int index) const override; + static AXP2101_Linear_Charger_Vsys_dpm_e get(); + virtual String getOptionString(int index) const override; }; - // ********************************************************************** // Reg 15: Input Voltage Limit // ********************************************************************** -class AXP2101_Vin_DPM_FormSelector : public FormSelectorOptions -{ +class AXP2101_Vin_DPM_FormSelector : public FormSelectorOptions { public: + AXP2101_Vin_DPM_FormSelector(AXP2101_VINDPM_e selected); virtual ~AXP2101_Vin_DPM_FormSelector() {} - virtual String getOptionString(int index) const override; + static AXP2101_VINDPM_e get(); + virtual String getOptionString(int index) const override; }; - // ********************************************************************** // Reg 16: Input Current Limit // ********************************************************************** -class AXP2101_InputCurrentLimit_FormSelector : public FormSelectorOptions -{ +class AXP2101_InputCurrentLimit_FormSelector : public FormSelectorOptions { public: + AXP2101_InputCurrentLimit_FormSelector(AXP2101_InputCurrentLimit_e selected); virtual ~AXP2101_InputCurrentLimit_FormSelector() {} - virtual String getOptionString(int index) const override; + static AXP2101_InputCurrentLimit_e get(); + virtual String getOptionString(int index) const override; }; - -#endif \ No newline at end of file +#endif // ifdef USES_P139 From b85f19d0392387bab53c237001c9a710a5dd7742 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 20:11:54 +0100 Subject: [PATCH 30/50] [P139] Clarify charging state --- src/src/PluginStructs/P139_data_struct.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 87ac98e148..8c7b663974 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -285,9 +285,10 @@ void P139_data_struct::webform_load(struct EventStruct *event) { addFormSubHeader(F("Current State")); const AXP2101_registers_e registers[] = { - AXP2101_registers_e::vbat, AXP2101_registers_e::vbus, AXP2101_registers_e::vsys, + AXP2101_registers_e::vbat, + AXP2101_registers_e::batcharge, AXP2101_registers_e::battemp, AXP2101_registers_e::chiptemp }; @@ -302,12 +303,21 @@ void P139_data_struct::webform_load(struct EventStruct *event) { addUnit(F("°C")); } else { addHtmlInt(static_cast(read_value(registers[i]))); - addUnit(F("mV")); + addUnit((registers[i] == AXP2101_registers_e::batcharge) + ? F("%") : F("mV")); } } addRowLabel(F("Charging State")); - addHtml(toString(axp2101->getChargingDetail())); + + const AXP2101_chargingState_e chargingState = axp2101->getChargingState(); + + if (chargingState != AXP2101_chargingState_e::Charging) { + addHtml(toString(chargingState)); + } + else { + addHtml(toString(axp2101->getChargingDetail())); + } } } } From cca1a26957e0295099e5d0cac1b8c808f47efeb2 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 20:38:06 +0100 Subject: [PATCH 31/50] [Cleanup] Simplify addSelector calls --- src/src/DataTypes/FormSelectorOptions.cpp | 2 +- src/src/DataTypes/FormSelectorOptions.h | 1 + src/src/WebServer/Markup_Forms.cpp | 53 ++++++++++------------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 1a7a968c23..df2610ee3c 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -172,7 +172,7 @@ void FormSelectorOptions::addFormSelector( #endif // if FEATURE_TOOLTIPS ); } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled + do_addSelector_Head(id, classname, onChangeCall, !enabled #if FEATURE_TOOLTIPS , tooltip #endif // if FEATURE_TOOLTIPS diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index 99cd2dc5f5..d7b25ba09c 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -53,6 +53,7 @@ class FormSelectorOptions { #if FEATURE_TOOLTIPS String tooltip; #endif // if FEATURE_TOOLTIPS + String onChangeCall; protected: diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 1f6ad008b6..d423288eae 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -513,11 +513,9 @@ void addFormSelector(const __FlashStringHelper * label, int selectedIndex, bool reloadonchange) { - String options[optionCount]; - for (int i = 0; i < optionCount; ++i) { - options[i] = indices[i]; - } - addFormSelector(label, id, optionCount, options, indices, selectedIndex, reloadonchange); + FormSelectorOptions selector(optionCount, indices); + selector.reloadonchange = reloadonchange; + selector.addFormSelector(label, id, selectedIndex); } void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange) @@ -578,8 +576,9 @@ void addFormSelector(const String& label, int selectedIndex, bool reloadonchange) { - addRowLabel_tr_id(label, id); - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, true); + FormSelectorOptions selector(optionCount, options, indices, attr); + selector.reloadonchange = reloadonchange; + selector.addFormSelector(label, id, selectedIndex); } void addFormSelector(const String& label, @@ -614,12 +613,12 @@ void addFormSelector(const String & label, #endif // if FEATURE_TOOLTIPS ) { - addRowLabel_tr_id(label, id); - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, true, F("wide") - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); + FormSelectorOptions selector(optionCount, options, indices, attr); + selector.reloadonchange = reloadonchange; +#if FEATURE_TOOLTIPS + selector.tooltip = tooltip; +#endif + selector.addFormSelector(label, id, selectedIndex); } void addFormSelector_script(const __FlashStringHelper * label, @@ -635,14 +634,12 @@ void addFormSelector_script(const __FlashStringHelper * label, #endif // if FEATURE_TOOLTIPS ) { - addRowLabel_tr_id(label, id); - do_addSelector_Head(id, F("wide"), onChangeCall, false - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); + FormSelectorOptions selector(optionCount, options, indices, attr); +#if FEATURE_TOOLTIPS + selector.tooltip = tooltip; +#endif + selector.onChangeCall = onChangeCall; + selector.addFormSelector(label, id, selectedIndex); } void addFormSelector_script(const __FlashStringHelper * label, @@ -658,14 +655,12 @@ void addFormSelector_script(const __FlashStringHelper * label, #endif // if FEATURE_TOOLTIPS ) { - addRowLabel_tr_id(label, id); - do_addSelector_Head(id, F("wide"), onChangeCall, false - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); + FormSelectorOptions selector(optionCount, options, indices, attr); +#if FEATURE_TOOLTIPS + selector.tooltip = tooltip; +#endif + selector.onChangeCall = onChangeCall; + selector.addFormSelector(label, id, selectedIndex); } void addFormSelector_YesNo(const __FlashStringHelper * label, From b60a6adb28de708599afd845921a49ef7e36eba2 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 24 Jan 2025 22:57:21 +0100 Subject: [PATCH 32/50] [Cleanup] Change addFormSelector calls to use new class --- src/_C011.cpp | 8 +- src/_P003_Pulse.ino | 6 +- src/_P004_Dallas.ino | 22 +++-- src/_P005_DHT.ino | 6 +- src/_P007_PCF8591.ino | 6 +- src/_P009_MCP.ino | 3 +- src/_P010_BH1750.ino | 3 +- src/_P011_PME.ino | 3 +- src/_P012_LCD.ino | 6 +- src/_P013_HCSR04.ino | 9 +- src/_P014_SI70xx.ino | 3 +- src/_P015_TSL2561.ino | 17 ++-- src/_P019_PCF8574.ino | 3 +- src/_P020_Ser2Net.ino | 12 ++- src/_P021_Level.ino | 7 +- src/_P022_PCA9685.ino | 7 +- src/_P023_OLED.ino | 3 +- src/_P024_MLX90614.ino | 3 +- src/_P027_INA219.ino | 6 +- src/_P028_BME280.ino | 13 +-- src/_P036_FrameOLED.ino | 28 ++++-- src/_P038_NeoPixel.ino | 3 +- src/_P059_Encoder.ino | 3 +- src/_P091_SerSwitch.ino | 8 +- src/src/Controller_config/C018_config.cpp | 28 +++--- src/src/DataTypes/FormSelectorOptions.cpp | 91 +++++-------------- src/src/DataTypes/FormSelectorOptions.h | 8 +- src/src/Helpers/AdafruitGFX_helper.cpp | 18 ++-- src/src/Helpers/ESPEasy_TouchHandler.cpp | 18 ++-- src/src/Helpers/OLed_helper.cpp | 37 +++++--- src/src/Helpers/_CPlugin_Helper_webform.cpp | 20 ++-- .../Helpers/_Internal_GPIO_pulseHelper.cpp | 3 +- src/src/Helpers/_Plugin_Helper_serial.cpp | 20 ++-- src/src/Helpers/_Plugin_Helper_webform.cpp | 18 ++-- src/src/Helpers/_Plugin_SensorTypeHelper.cpp | 20 ++-- src/src/PluginStructs/P139_data_struct.cpp | 15 +-- src/src/WebServer/AdvancedConfigPage.cpp | 18 ++-- src/src/WebServer/DevicesPage.cpp | 30 +++--- src/src/WebServer/HardwarePage.cpp | 25 ++--- src/src/WebServer/Markup.cpp | 5 +- src/src/WebServer/Markup_Forms.cpp | 72 +++------------ src/src/WebServer/Markup_Forms.h | 28 ------ 42 files changed, 312 insertions(+), 350 deletions(-) diff --git a/src/_C011.cpp b/src/_C011.cpp index b4d1aab50b..2cfc1e4cd4 100644 --- a/src/_C011.cpp +++ b/src/_C011.cpp @@ -102,13 +102,17 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String& uint8_t choice = 0; const __FlashStringHelper * methods[] = { F("GET"), F("POST"), F("PUT"), F("HEAD"), F("PATCH") }; - for (uint8_t i = 0; i < 5; i++) + constexpr int nrOptions = NR_ELEMENTS(methods); + + for (uint8_t i = 0; i < nrOptions; i++) { if (HttpMethod.equals(methods[i])) { choice = i; } } - addFormSelector(F("Method"), F("P011httpmethod"), 5, methods, nullptr, choice); + + FormSelectorOptions selector(nrOptions, methods); + selector.addFormSelector(F("Method"), F("P011httpmethod"), choice); } addFormTextBox(F("URI"), F("P011httpuri"), HttpUri, C011_HTTP_URI_MAX_LEN - 1); diff --git a/src/_P003_Pulse.ino b/src/_P003_Pulse.ino index dfbeeee601..0e86aa83c6 100644 --- a/src/_P003_Pulse.ino +++ b/src/_P003_Pulse.ino @@ -163,8 +163,10 @@ boolean Plugin_003(uint8_t function, struct EventStruct *event, String& string) F("Time/Delta"), # endif // if P003_USE_EXTRA_COUNTERTYPES }; - constexpr size_t optionCount = NR_ELEMENTS(options); - addFormSelector(F("Counter Type"), F("countertype"), optionCount, options, nullptr, choice); + FormSelectorOptions selector( + NR_ELEMENTS(options), + options); + selector.addFormSelector(F("Counter Type"), F("countertype"), choice); if (choice != 0) { addHtml(F("Total count is not persistent!")); diff --git a/src/_P004_Dallas.ino b/src/_P004_Dallas.ino index 01b1ce4105..eadcfa5efd 100644 --- a/src/_P004_Dallas.ino +++ b/src/_P004_Dallas.ino @@ -138,20 +138,24 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) int resolutionChoice = P004_RESOLUTION; if ((resolutionChoice < 9) || (resolutionChoice > 12)) { resolutionChoice = activeRes; } - const __FlashStringHelper *resultsOptions[] = { F("9"), F("10"), F("11"), F("12") }; - const int resultsOptionValues[] = { 9, 10, 11, 12 }; - constexpr size_t optionCount = NR_ELEMENTS(resultsOptionValues); - addFormSelector(F("Device Resolution"), F("res"), optionCount, resultsOptions, resultsOptionValues, resolutionChoice); - addHtml(F(" Bit")); + constexpr int resultsOptionValues[] { 9, 10, 11, 12 }; + + FormSelectorOptions selector( + NR_ELEMENTS(resultsOptionValues), + resultsOptionValues); + selector.addFormSelector(F("Device Resolution"), F("res"), resolutionChoice); + addUnit(F("bit")); } { // Value in case of Error const __FlashStringHelper *resultsOptions[] = { F("NaN"), F("-127"), F("0"), F("125"), F("Ignore") }; - int resultsOptionValues[] = - { P004_ERROR_NAN, P004_ERROR_MIN_RANGE, P004_ERROR_ZERO, P004_ERROR_MAX_RANGE, P004_ERROR_IGNORE }; - constexpr size_t optionCount = NR_ELEMENTS(resultsOptionValues); - addFormSelector(F("Error State Value"), F("err"), optionCount, resultsOptions, resultsOptionValues, P004_ERROR_STATE_OUTPUT); + constexpr int resultsOptionValues[] { P004_ERROR_NAN, P004_ERROR_MIN_RANGE, P004_ERROR_ZERO, P004_ERROR_MAX_RANGE, P004_ERROR_IGNORE }; + + FormSelectorOptions selector( + NR_ELEMENTS(resultsOptionValues), + resultsOptions, resultsOptionValues); + selector.addFormSelector(F("Error State Value"), F("err"), P004_ERROR_STATE_OUTPUT); } addFormNote(F("External pull up resistor is needed, see docs!")); diff --git a/src/_P005_DHT.ino b/src/_P005_DHT.ino index 592fd08aa9..c2c2cbc645 100644 --- a/src/_P005_DHT.ino +++ b/src/_P005_DHT.ino @@ -62,11 +62,11 @@ boolean Plugin_005(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *options[] = { F("DHT 11"), F("DHT 22"), F("DHT 12"), F("Sonoff am2301"), F("Sonoff si7021"), F("Sonoff MS01") }; - const int indices[] = { P005_DHT11, P005_DHT22, P005_DHT12, P005_AM2301, P005_SI7021, P005_MS01 }; - + constexpr int indices[]{ P005_DHT11, P005_DHT22, P005_DHT12, P005_AM2301, P005_SI7021, P005_MS01 }; constexpr size_t nrElements = NR_ELEMENTS(indices); - addFormSelector(F("Sensor model"), F("dhttype"), nrElements, options, indices, PCONFIG(0)); + FormSelectorOptions selector(nrElements, options, indices); + selector.addFormSelector(F("Sensor model"), F("dhttype"), PCONFIG(0)); success = true; break; diff --git a/src/_P007_PCF8591.ino b/src/_P007_PCF8591.ino index 241fb92214..c56baf4dbf 100644 --- a/src/_P007_PCF8591.ino +++ b/src/_P007_PCF8591.ino @@ -105,7 +105,8 @@ boolean Plugin_007(uint8_t function, struct EventStruct *event, String& string) portNames[x] += x; } addFormSelectorI2C(F("pi2c"), 8, i2cAddressValues, address); - addFormSelector(F("Port"), F("pport"), 4, portNames, portValues, port); + FormSelectorOptions selector(4, portNames, portValues); + selector.addFormSelector(F("Port"), F("pport"), port); addFormNote(F( "Selected Port value will be stored in first 'Values' field and consecutively for 'Number Output Values' > Single.")); } else { @@ -142,7 +143,8 @@ boolean Plugin_007(uint8_t function, struct EventStruct *event, String& string) 0b00110000, }; constexpr size_t optionCount = NR_ELEMENTS(inputModeValues); - addFormSelector(F("Input mode"), F("input_mode"), optionCount, inputModeOptions, inputModeValues, P007_INPUT_MODE); + FormSelectorOptions selector(optionCount, inputModeOptions, inputModeValues); + selector.addFormSelector(F("Input mode"), F("input_mode"), P007_INPUT_MODE); addFormCheckBox(F("Enable Analog output (AOUT)"), F("output_mode"), P007_OUTPUT_MODE == P007_OUTPUT_ENABLED); diff --git a/src/_P009_MCP.ino b/src/_P009_MCP.ino index 8bbfb00785..d6a3941117 100644 --- a/src/_P009_MCP.ino +++ b/src/_P009_MCP.ino @@ -71,7 +71,8 @@ boolean Plugin_009(uint8_t function, struct EventStruct *event, String& string) portNames[x] += (x < 8 ? x : x - 8); } addFormSelectorI2C(F("pi2c"), 8, i2cAddressValues, address); - addFormSelector(F("Port"), F("pport"), 16, portNames, portValues, port); + FormSelectorOptions selector(16, portNames, portValues); + selector.addFormSelector(F("Port"), F("pport"), port); } else { success = intArrayContains(8, i2cAddressValues, event->Par1); } diff --git a/src/_P010_BH1750.ino b/src/_P010_BH1750.ino index b62be65a69..4a8eaa480a 100644 --- a/src/_P010_BH1750.ino +++ b/src/_P010_BH1750.ino @@ -85,7 +85,8 @@ boolean Plugin_010(uint8_t function, struct EventStruct *event, String& string) RESOLUTION_AUTO_HIGH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode); - addFormSelector(F("Measurement mode"), F("pmode"), optionCount, optionsMode, optionValuesMode, PCONFIG(1)); + FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode); + selector.addFormSelector(F("Measurement mode"), F("pmode"), PCONFIG(1)); addFormCheckBox(F("Send sensor to sleep"), F("psleep"), PCONFIG(2)); diff --git a/src/_P011_PME.ino b/src/_P011_PME.ino index f867281c10..92a64623e3 100644 --- a/src/_P011_PME.ino +++ b/src/_P011_PME.ino @@ -84,7 +84,8 @@ boolean Plugin_011(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options[] = { F("Digital"), F("Analog"), F("Input (switch)") }; const int optionValues[] = { P011_TYPE_DIGITAL, P011_TYPE_ANALOG, P011_TYPE_SWITCH }; constexpr size_t optionCount = NR_ELEMENTS(options); - addFormSelector(F("Port Type"), F("p011"), optionCount, options, optionValues, P011_PORT_TYPE); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Port Type"), F("p011"), P011_PORT_TYPE); success = true; break; diff --git a/src/_P012_LCD.ino b/src/_P012_LCD.ino index 416b83036f..be3fe2b296 100644 --- a/src/_P012_LCD.ino +++ b/src/_P012_LCD.ino @@ -97,7 +97,8 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues2[2] = { 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - addFormSelector(F("Display Size"), F("psize"), optionCount, options2, optionValues2, P012_SIZE); + FormSelectorOptions selector(optionCount, options2, optionValues2); + selector.addFormSelector(F("Display Size"), F("psize"), P012_SIZE); } { @@ -125,7 +126,8 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues3[] = { 0, 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues3); - addFormSelector(F("LCD command Mode"), F("pmode"), optionCount, options3, optionValues3, P012_MODE); + FormSelectorOptions selector(optionCount, options3, optionValues3); + selector.addFormSelector(F("LCD command Mode"), F("pmode"), P012_MODE); } success = true; diff --git a/src/_P013_HCSR04.ino b/src/_P013_HCSR04.ino index 1a2f15902e..3f7cf799e5 100644 --- a/src/_P013_HCSR04.ino +++ b/src/_P013_HCSR04.ino @@ -131,7 +131,8 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even # endif // if P013_FEATURE_COMBINED_MODE }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesOpMode); - addFormSelector(F("Mode"), F("pmode"), optionCount, optionsOpMode, optionValuesOpMode, P013_OPERATINGMODE); + FormSelectorOptions selector(optionCount, optionsOpMode, optionValuesOpMode); + selector.addFormSelector(F("Mode"), F("pmode"), P013_OPERATINGMODE); } if ((P013_OPERATINGMODE == OPMODE_STATE) @@ -155,7 +156,8 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even F("Imperial"), }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesUnit); - addFormSelector(F("Unit"), F("pUnit"), optionCount, optionsUnit, optionValuesUnit, P013_MEASURINGUNIT); + FormSelectorOptions selector(optionCount, optionsUnit, optionValuesUnit); + selector.addFormSelector(F("Unit"), F("pUnit"), P013_MEASURINGUNIT); } { @@ -165,7 +167,8 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even F("Median"), }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesFilter); - addFormSelector(F("Filter"), F("fltr"), optionCount, optionsFilter, optionValuesFilter, P013_FILTERTYPE); + FormSelectorOptions selector(optionCount, optionsFilter, optionValuesFilter); + selector.addFormSelector(F("Filter"), F("fltr"), P013_FILTERTYPE); } // enable filtersize option if filter is used, diff --git a/src/_P014_SI70xx.ino b/src/_P014_SI70xx.ino index 20aca65c4d..c6e420cd46 100644 --- a/src/_P014_SI70xx.ino +++ b/src/_P014_SI70xx.ino @@ -117,7 +117,8 @@ boolean Plugin_014(uint8_t function, struct EventStruct *event, String& string) SI70xx_RESOLUTION_11T_11RH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Resolution"), F("pres"), optionCount, options, optionValues, P014_RESOLUTION); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Resolution"), F("pres"), P014_RESOLUTION); addFormNumericBox("ADC Filter Power", F("pfilter"), P014_FILTER_POWER, 0, 4); diff --git a/src/_P015_TSL2561.ino b/src/_P015_TSL2561.ino index aed807dca1..fa6a19b6b2 100644 --- a/src/_P015_TSL2561.ino +++ b/src/_P015_TSL2561.ino @@ -97,12 +97,14 @@ boolean Plugin_015(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *options[] = { - F("13.7 ms"), - F("101 ms"), - F("402 ms"), + F("13.7"), + F("101"), + F("402"), }; constexpr size_t optionCount = NR_ELEMENTS(options); - addFormSelector(F("Integration time"), F("pintegration"), optionCount, options, nullptr, P015_INTEGRATION); + FormSelectorOptions selector(optionCount, options); + selector.addFormSelector(F("Integration time"), F("pintegration"), P015_INTEGRATION); + addUnit(F("ms")); } addFormCheckBox(F("Send sensor to sleep:"), F("psleep"), @@ -115,14 +117,17 @@ boolean Plugin_015(uint8_t function, struct EventStruct *event, String& string) F("Auto Gain"), F("Extended Auto Gain"), }; + /* const int optionValues[] = { P015_NO_GAIN, P015_16X_GAIN, P015_AUTO_GAIN, P015_EXT_AUTO_GAIN, }; - constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Gain"), F("pgain"), optionCount, options, optionValues, P015_GAIN); + */ + constexpr size_t optionCount = NR_ELEMENTS(options); + FormSelectorOptions selector(optionCount, options/*, optionValues*/); + selector.addFormSelector(F("Gain"), F("pgain"), P015_GAIN); } success = true; diff --git a/src/_P019_PCF8574.ino b/src/_P019_PCF8574.ino index 46c204eda8..8622f5539b 100644 --- a/src/_P019_PCF8574.ino +++ b/src/_P019_PCF8574.ino @@ -70,7 +70,8 @@ boolean Plugin_019(uint8_t function, struct EventStruct *event, String& string) portNames[x] += x; } addFormSelectorI2C(F("pi2c"), 16, i2cAddressValues, address); - addFormSelector(F("Port"), F("pport"), 8, portNames, portValues, port); + FormSelectorOptions selector(8, portNames, portValues); + selector.addFormSelector(F("Port"), F("pport"), port); addFormNote(F("PCF8574 uses addresses 0x20..0x27, PCF8574A uses addresses 0x38..0x3F.")); } else { success = intArrayContains(16, i2cAddressValues, event->Par1); diff --git a/src/_P020_Ser2Net.ino b/src/_P020_Ser2Net.ino index 70d58e0465..799f84ae8d 100644 --- a/src/_P020_Ser2Net.ino +++ b/src/_P020_Ser2Net.ino @@ -192,16 +192,20 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) F("RFLink"), F("P1 WiFi Gateway") }; +/* const int optionValues[] = { static_cast(P020_Events::None), static_cast(P020_Events::Generic), static_cast(P020_Events::RFLink), static_cast(P020_Events::P1WiFiGateway), }; - constexpr int optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Event processing"), F("pevents"), - optionCount, options, optionValues, - P020_SERIAL_PROCESSING); +*/ + constexpr int optionCount = NR_ELEMENTS(options); + FormSelectorOptions selector(optionCount, options /*, optionValues*/); + selector.addFormSelector( + F("Event processing"), + F("pevents"), + P020_SERIAL_PROCESSING); } addFormCheckBox(F("P1 #data event with message"), F("pp1event"), P020_GET_P1_EVENT_DATA); # ifndef LIMIT_BUILD_SIZE diff --git a/src/_P021_Level.ino b/src/_P021_Level.ino index 942bd4d829..ec71092f26 100644 --- a/src/_P021_Level.ino +++ b/src/_P021_Level.ino @@ -234,10 +234,13 @@ boolean Plugin_021(uint8_t function, struct EventStruct *event, String& string) // FormSelector with all operation mode options const __FlashStringHelper *options[] = { F("Classic"), F("Off"), F("Standby"), F("On"), F("Local"), F("Remote") }; + /* const int optionValues[] = { P021_OPMODE_CLASSIC, P021_OPMODE_OFF, P021_OPMODE_STANDBY, P021_OPMODE_ON, P021_OPMODE_TEMP, P021_OPMODE_REMOTE }; - constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Control mode"), F(P021_GUID_OPMODE), optionCount, options, optionValues, P021_OPMODE); + */ + constexpr size_t optionCount = NR_ELEMENTS(options); + FormSelectorOptions selector(optionCount, options/*, optionValues*/); + selector.addFormSelector(F("Control mode"), F(P021_GUID_OPMODE), P021_OPMODE); // Add timer values depending on build size // - minimum build size: units are always in seconds; drop the units on the form diff --git a/src/_P022_PCA9685.ino b/src/_P022_PCA9685.ino index 14af526a3d..660e393a17 100644 --- a/src/_P022_PCA9685.ino +++ b/src/_P022_PCA9685.ino @@ -109,18 +109,19 @@ boolean Plugin_022(uint8_t function, struct EventStruct *event, String& string) // To prevent stack overflow issues, each selection has its own scope. { String m2Options[PCA9685_MODE2_VALUES]; - int m2Values[PCA9685_MODE2_VALUES]; + //int m2Values[PCA9685_MODE2_VALUES]; for (int i = 0; i < PCA9685_MODE2_VALUES; ++i) { - m2Values[i] = i; + //m2Values[i] = i; m2Options[i] = formatToHex_decimal(i); if (i == 0x10) { m2Options[i] += F(" - (default)"); } } - addFormSelector(F("MODE2"), F("pmode2"), PCA9685_MODE2_VALUES, m2Options, m2Values, mode2); + FormSelectorOptions selector(PCA9685_MODE2_VALUES, m2Options/*, m2Values*/); + selector.addFormSelector(F("MODE2"), F("pmode2"), mode2); } addFormNumericBox( strformat(F("Frequency (%d-%d)"), PCA9685_MIN_FREQUENCY, PCA9685_MAX_FREQUENCY), diff --git a/src/_P023_OLED.ino b/src/_P023_OLED.ino index 5c192ca020..0618652204 100644 --- a/src/_P023_OLED.ino +++ b/src/_P023_OLED.ino @@ -106,7 +106,8 @@ boolean Plugin_023(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options4[] = { F("Normal"), F("Optimized") }; const int optionValues4[] = { 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - addFormSelector(F("Font Width"), F("font_spacing"), optionCount, options4, optionValues4, PCONFIG(4)); + FormSelectorOptions selector(optionCount, options4, optionValues4); + selector.addFormSelector(F("Font Width"), F("font_spacing"), PCONFIG(4)); } { String strings[P23_Nlines]; diff --git a/src/_P024_MLX90614.ino b/src/_P024_MLX90614.ino index 10fc1dfd09..986192c7e5 100644 --- a/src/_P024_MLX90614.ino +++ b/src/_P024_MLX90614.ino @@ -81,7 +81,8 @@ boolean Plugin_024(uint8_t function, struct EventStruct *event, String& string) (0x06) }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Option"), F("option"), optionCount, options, optionValues, PCONFIG(0)); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Option"), F("option"), PCONFIG(0)); success = true; break; diff --git a/src/_P027_INA219.ino b/src/_P027_INA219.ino index 1aca517611..820b573d1f 100644 --- a/src/_P027_INA219.ino +++ b/src/_P027_INA219.ino @@ -132,11 +132,13 @@ boolean Plugin_027(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *optionsMode[] = { F("32V, 2A"), F("32V, 1A"), F("16V, 0.4A"), F("26V, 8A") }; - addFormSelector(F("Measure range"), F("range"), 4, optionsMode, nullptr, PCONFIG(0)); + FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode); + selector.addFormSelector(F("Measure range"), F("range"), PCONFIG(0)); } { const __FlashStringHelper *options[] = { F("Voltage"), F("Current"), F("Power"), F("Voltage/Current/Power") }; - addFormSelector(F("Measurement Type"), F("measuretype"), 4, options, nullptr, PCONFIG(2)); + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(F("Measurement Type"), F("measuretype"), PCONFIG(2)); } # if P027_FEATURE_POWERDOWN addFormCheckBox(F("Use Powerdown mode"), F("pwrdwn"), PCONFIG(3) == 1); diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index 61dabdab43..09ab4ac824 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -190,7 +190,8 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) static_cast(P028_data_struct::BMx_DetectMode::BME280), static_cast(P028_data_struct::BMx_DetectMode::BMP280), }; - addFormSelector(F("Output values mode"), F("det"), 2, detectOptionList, detectOptions, P028_DETECTION_MODE); + FormSelectorOptions selector(NR_ELEMENTS(detectOptionList), detectOptionList, detectOptions); + selector.addFormSelector(F("Output values mode"), F("det"), P028_DETECTION_MODE); success = true; } @@ -253,11 +254,11 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) # endif // ifndef LIMIT_BUILD_SIZE }; constexpr int P028_ERROR_STATE_COUNT = NR_ELEMENTS(resultsOptions); - addFormSelector(F("Temperature Error Value"), - F("err"), - P028_ERROR_STATE_COUNT, - resultsOptions, - resultsOptionValues, + FormSelectorOptions selector( + P028_ERROR_STATE_COUNT, + resultsOptions, + resultsOptionValues); + selector.addFormSelector(F("Temperature Error Value"), F("err"), P028_ERROR_STATE_OUTPUT); break; diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 16eafa20c9..956a2db86e 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -348,7 +348,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # endif // if P036_ENABLE_TICKER }; constexpr int optionCnt = NR_ELEMENTS(optionValues); - addFormSelector(F("Scroll"), F("scroll"), optionCnt, options, optionValues, P036_SCROLL); + FormSelectorOptions selector(optionCnt, options, optionValues); + selector.addFormSelector(F("Scroll"), F("scroll"), P036_SCROLL); } // FIXME TD-er: Why is this using pin3 and not pin1? And why isn't this using the normal pin selection functions? @@ -359,7 +360,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) const int optionValues[] = { static_cast(eP036pinmode::ePPM_Input), static_cast(eP036pinmode::ePPM_InputPullUp) }; - addFormSelector(F("Pin mode"), F("pinmode"), 2, options, optionValues, + FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + selector.addFormSelector(F("Pin mode"), F("pinmode"), bitRead(P036_FLAGS_0, P036_FLAG_INPUT_PULLUP)); // Bit 26 Input PullUp } @@ -388,7 +390,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("Display, Contrast, Frame, Line & Linecount") }; const int optionValues[] = { 0, 1, 3 }; // Bitmap - addFormSelector(F("Generate events"), F("generateEvents"), 3, options, optionValues, choice); + FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + selector.addFormSelector(F("Generate events"), F("generateEvents"), choice); # ifndef P036_LIMIT_BUILD_SIZE addFormNote(F("Events: <taskname> #display=1/0 (on/off), #contrast=0/1/2 (low/med/high),")); @@ -442,10 +445,15 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # endif // if P036_USERDEF_HEADERS }; constexpr int nrOptions9 = NR_ELEMENTS(options9); - addFormSelector(F("Header"), F("header"), nrOptions9, options9, optionValues9, - get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER)); // HeaderContent - addFormSelector(F("Header (alternate)"), F("headerAlternate"), nrOptions9, options9, optionValues9, - get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER_ALTERNATIVE)); // HeaderContentAlternative + FormSelectorOptions selector(nrOptions9, options9, optionValues9); + // HeaderContent + selector.addFormSelector( + F("Header"), F("header"), + get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER)); + // HeaderContentAlternative + selector.addFormSelector( + F("Header (alternate)"), F("headerAlternate"), + get8BitFromUL(P036_FLAGS_0, P036_FLAG_HEADER_ALTERNATIVE)); } # if P036_ENABLE_TIME_FORMAT { @@ -460,7 +468,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("HH:MM:SS (am/pm)"), F("HH:MM (am/pm)"), }; - addFormSelector(F("Header Time format"), F("timeFmt"), NR_ELEMENTS(options), options, nullptr, + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(F("Header Time format"), F("timeFmt"), get4BitFromUL(P036_FLAGS_1, P036_FLAG_TIME_FORMAT)); } # endif // if P036_ENABLE_TIME_FORMAT @@ -483,7 +492,8 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) static_cast(eAlignment::eCenter), static_cast(eAlignment::eRight) }; - addFormSelector(F("Align content (global)"), F("LeftAlign"), 3, optionsAlignment, optionValuesAlignment, + FormSelectorOptions selector( NR_ELEMENTS(optionValuesAlignment), optionsAlignment, optionValuesAlignment); + selector.addFormSelector(F("Align content (global)"), F("LeftAlign"), get2BitFromUL(P036_FLAGS_1, P036_FLAG_LEFT_ALIGNED)); } # endif // if P036_ENABLE_LEFT_ALIGN diff --git a/src/_P038_NeoPixel.ino b/src/_P038_NeoPixel.ino index d775f39bed..da1e72f18d 100644 --- a/src/_P038_NeoPixel.ino +++ b/src/_P038_NeoPixel.ino @@ -96,7 +96,8 @@ boolean Plugin_038(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *options[] = { F("GRB"), F("GRBW") }; int indices[] = { P038_STRIP_TYPE_RGB, P038_STRIP_TYPE_RGBW }; - addFormSelector(F("Strip Type"), F("pstrip"), 2, options, indices, P038_CONFIG_STRIPTYPE); + FormSelectorOptions selector(NR_ELEMENTS(options), options, indices); + selector.addFormSelector(F("Strip Type"), F("pstrip"), P038_CONFIG_STRIPTYPE); } if (P038_CONFIG_BRIGHTNESS == 0) { P038_CONFIG_BRIGHTNESS = 255; } diff --git a/src/_P059_Encoder.ino b/src/_P059_Encoder.ino index f05ae91980..c2d3bb35f9 100644 --- a/src/_P059_Encoder.ino +++ b/src/_P059_Encoder.ino @@ -77,10 +77,9 @@ boolean Plugin_059(uint8_t function, struct EventStruct *event, String& string) } { - const __FlashStringHelper *options[] = { F("1"), F("2"), F("4") }; const int optionValues[] = { 1, 2, 4 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Mode"), F("mode"), optionCount, options, optionValues, PCONFIG(0)); + addFormSelector(F("Mode"), F("mode"), optionCount, optionValues, PCONFIG(0)); addUnit(F("pulses per cycle")); } diff --git a/src/_P091_SerSwitch.ino b/src/_P091_SerSwitch.ino index 6f0cccdca9..3d71f8cd50 100644 --- a/src/_P091_SerSwitch.ino +++ b/src/_P091_SerSwitch.ino @@ -155,15 +155,9 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) if (PCONFIG(0) == SER_SWITCH_LCTECH) { { - const __FlashStringHelper *buttonOptions[] = { - F("1"), - F("2"), - F("3"), - F("4"), - }; const int buttonoptionValues[] = { 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(buttonoptionValues); - addFormSelector(F("Number of relays"), F("button"), optionCount, buttonOptions, buttonoptionValues, PCONFIG(1)); + addFormSelector(F("Number of relays"), F("button"), optionCount, buttonoptionValues, PCONFIG(1)); } { diff --git a/src/src/Controller_config/C018_config.cpp b/src/src/Controller_config/C018_config.cpp index c4fac1b5de..2da5f33068 100644 --- a/src/src/Controller_config/C018_config.cpp +++ b/src/src/Controller_config/C018_config.cpp @@ -73,38 +73,40 @@ void C018_ConfigStruct::webform_load(C018_data_struct *C018_data) { addFormTextBox(F("App Session Key"), F("appskey"), AppSessionKey, C018_APP_SESSION_KEY_LEN - 1); { - const __FlashStringHelper *options[2] = { F("OTAA"), F("ABP") }; - const int values[2] = { C018_USE_OTAA, C018_USE_ABP }; - addFormSelector_script(F("Activation Method"), F("joinmethod"), 2, - options, values, nullptr, joinmethod, - F("joinChanged(this)")); // Script to toggle OTAA/ABP fields visibility when changing selection. + const __FlashStringHelper *options[] = { F("OTAA"), F("ABP") }; + //const int values[] = { C018_USE_OTAA, C018_USE_ABP }; + FormSelectorOptions selector(NR_ELEMENTS(options), options); + // Script to toggle OTAA/ABP fields visibility when changing selection. + selector.onChangeCall = F("joinChanged(this)"); + selector.addFormSelector(F("Activation Method"), F("joinmethod"), joinmethod); + } html_add_script(F("document.getElementById('joinmethod').onchange();"), false); addTableSeparator(F("Connection Configuration"), 2, 3); { - const __FlashStringHelper *options[4] = { F("SINGLE_CHANNEL_EU"), F("TTN_EU"), F("TTN_US"), F("DEFAULT_EU") }; - int values[4] = + const __FlashStringHelper *options[] = { F("SINGLE_CHANNEL_EU"), F("TTN_EU"), F("TTN_US"), F("DEFAULT_EU") }; + int values[] = { RN2xx3_datatypes::Freq_plan::SINGLE_CHANNEL_EU, RN2xx3_datatypes::Freq_plan::TTN_EU, RN2xx3_datatypes::Freq_plan::TTN_US, RN2xx3_datatypes::Freq_plan::DEFAULT_EU }; - - addFormSelector(F("Frequency Plan"), F("frequencyplan"), 4, options, values, nullptr, frequencyplan, false); + FormSelectorOptions selector( NR_ELEMENTS(options), options, values); + selector.addFormSelector(F("Frequency Plan"), F("frequencyplan"), frequencyplan); addFormNumericBox(F("RX2 Frequency"), F("rx2freq"), rx2_freq, 0); addUnit(F("Hz")); addFormNote(F("0 = default, or else override default")); } { - const __FlashStringHelper *options[2] = { F("TTN v2"), F("TTN v3") }; - int values[2] = { + const __FlashStringHelper *options[] = { F("TTN v2"), F("TTN v3") }; + constexpr int values[] { RN2xx3_datatypes::TTN_stack_version::TTN_v2, RN2xx3_datatypes::TTN_stack_version::TTN_v3 }; - - addFormSelector(F("TTN Stack"), F("ttnstack"), 2, options, values, nullptr, stackVersion, false); + FormSelectorOptions selector(NR_ELEMENTS(options), options, values); + selector.addFormSelector(F("TTN Stack"), F("ttnstack"), stackVersion); } addFormNumericBox(F("Spread Factor"), F("sf"), sf, 7, 12); diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index df2610ee3c..fcfc8267c5 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -1,9 +1,10 @@ #include "../DataTypes/FormSelectorOptions.h" #include "../WebServer/Markup.h" +#include "../WebServer/Markup_Forms.h" -FormSelectorOptions::FormSelectorOptions(int optionCount) -: _optionCount(optionCount) +FormSelectorOptions::FormSelectorOptions(int optionCount) + : _optionCount(optionCount) { classname = F("wide"); } @@ -11,94 +12,41 @@ FormSelectorOptions::FormSelectorOptions(int optionCount) FormSelectorOptions::FormSelectorOptions( int optionCount, const int indices[], - const String attr[]) : _optionCount(optionCount) + const String attr[]) : + _optionCount(optionCount), + _indices(indices), + _attr_str(attr) { classname = F("wide"); - _indices = new int[optionCount]; - - if (attr != nullptr) { - _attr_str = new String[optionCount]; - } - - for (int i = 0; i < optionCount; ++i) { - _indices[i] = indices[i]; - - if (attr != nullptr) { - _attr_str[i] = attr[i]; - } - } } FormSelectorOptions::FormSelectorOptions( int optionCount, const String options[], const int indices[], - const String attr[]) : _optionCount(optionCount) + const String attr[]) : + _optionCount(optionCount), + _names_str(options), + _indices(indices), + _attr_str(attr) { classname = F("wide"); - _names_str = new String[optionCount]; - - if (indices != nullptr) { - _indices = new int[optionCount]; - } - - if (attr != nullptr) { - _attr_str = new String[optionCount]; - } - - - for (int i = 0; i < optionCount; ++i) { - _names_str[i] = options[i]; - - if (indices != nullptr) { - _indices[i] = indices[i]; - } - - if (attr != nullptr) { - _attr_str[i] = attr[i]; - } - } } FormSelectorOptions::FormSelectorOptions( int optionCount, const __FlashStringHelper *options[], const int indices[], - const String attr[]) : _optionCount(optionCount) + const String attr[]) : + _optionCount(optionCount), + _names_f(options), + _indices(indices), + _attr_str(attr) { classname = F("wide"); - _names_f = new const __FlashStringHelper *[optionCount]; - - if (indices != nullptr) { - _indices = new int[optionCount]; - } - - if (attr != nullptr) { - _attr_str = new String[optionCount]; - } - - for (int i = 0; i < optionCount; ++i) { - _names_f[i] = options[i]; - - if (indices != nullptr) { - _indices[i] = indices[i]; - } - - if (attr != nullptr) { - _attr_str[i] = attr[i]; - } - } } -FormSelectorOptions::~FormSelectorOptions() { - #define DELETE_ARR(A) \ - if (A != nullptr) { delete[] A; A = nullptr; } - DELETE_ARR(_names_f) - DELETE_ARR(_names_str) - DELETE_ARR(_indices) - DELETE_ARR(_attr_str) - #undef DELETE_ARR -} +FormSelectorOptions::~FormSelectorOptions() {} String FormSelectorOptions::getOptionString(int index) const { @@ -192,4 +140,7 @@ void FormSelectorOptions::addFormSelector( if ((i & 0x07) == 0) { delay(0); } } addSelector_Foot(); + if (reloadonchange) { + addFormNote(F("Page will reload when selection is changed.")); + } } diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index d7b25ba09c..86e822d9c1 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -57,11 +57,11 @@ class FormSelectorOptions { protected: - int _optionCount{}; + const int _optionCount{}; const __FlashStringHelper **_names_f{ nullptr }; - String *_names_str{ nullptr }; - int *_indices{ nullptr }; - String *_attr_str{ nullptr }; + const String *_names_str{ nullptr }; + const int *_indices{ nullptr }; + const String *_attr_str{ nullptr }; }; diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index fb9171c026..02ab9eeabe 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -210,14 +210,17 @@ void AdaGFXFormTextPrintMode(const __FlashStringHelper *id, toString(AdaGFXTextPrintMode::ClearThenTruncate), toString(AdaGFXTextPrintMode::TruncateExceedingCentered), }; + /* const int textModeOptions[] = { static_cast(AdaGFXTextPrintMode::ContinueToNextLine), static_cast(AdaGFXTextPrintMode::TruncateExceedingMessage), static_cast(AdaGFXTextPrintMode::ClearThenTruncate), static_cast(AdaGFXTextPrintMode::TruncateExceedingCentered), }; + */ - addFormSelector(F("Text print Mode"), id, sizeof(textModeOptions) / sizeof(int), textModes, textModeOptions, selectedIndex); + FormSelectorOptions selector(NR_ELEMENTS(textModes), textModes); + selector.addFormSelector(F("Text print Mode"), id, selectedIndex); } void AdaGFXFormColorDepth(const __FlashStringHelper *id, @@ -275,9 +278,9 @@ void AdaGFXFormColorDepth(const __FlashStringHelper *id, void AdaGFXFormRotation(const __FlashStringHelper *id, uint8_t selectedIndex) { const __FlashStringHelper *rotationOptions[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; - const int rotationOptionValues[] = { 0, 1, 2, 3 }; - - addFormSelector(F("Rotation"), id, 4, rotationOptions, rotationOptionValues, selectedIndex); +// const int rotationOptionValues[] = { 0, 1, 2, 3 }; + FormSelectorOptions selector(NR_ELEMENTS(rotationOptions), rotationOptions); + selector.addFormSelector(F("Rotation"), id, selectedIndex); } /***************************************************************************************** @@ -399,7 +402,7 @@ void AdaGFXFormFontScaling(const __FlashStringHelper *fontScalingId, void AdaGFXFormLineSpacing(const __FlashStringHelper *id, uint8_t selectedIndex) { String lineSpacings[16]; - int lineSpacingOptions[16]; +// int lineSpacingOptions[16]; for (uint8_t i = 0; i < 16; ++i) { if (15 == i) { @@ -411,9 +414,10 @@ void AdaGFXFormLineSpacing(const __FlashStringHelper *id, } else { lineSpacings[i] = i; } - lineSpacingOptions[i] = i; +// lineSpacingOptions[i] = i; } - addFormSelector(F("Linespacing"), id, 16, lineSpacings, lineSpacingOptions, selectedIndex); + FormSelectorOptions selector(16, lineSpacings); + selector.addFormSelector(F("Linespacing"), id, selectedIndex); addUnit(F("px")); } diff --git a/src/src/Helpers/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index b78e5adb87..50ea19898f 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -889,7 +889,8 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { # endif // if TOUCH_FEATURE_EXTENDED_TOUCH }; const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! - addFormSelector(F("Events"), F("events"), NR_ELEMENTS(optionValues3), options3, optionValues3, choice3); + FormSelectorOptions selector(NR_ELEMENTS(optionValues3), options3, optionValues3); + selector.addFormSelector(F("Events"), F("events"), choice3); addFormCheckBox(F("Draw buttons when started"), F("initobj"), bitRead(Touch_Settings.flags, TOUCH_FLAGS_INIT_OBJECTEVENT)); # ifndef LIMIT_BUILD_SIZE @@ -915,17 +916,10 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { addFormSubHeader(F("Calibration")); - { - const __FlashStringHelper *noYesOptions[2] = { F("No"), F("Yes") }; - const int noYesOptionValues[2] = { 0, 1 }; - addFormSelector(F("Calibrate to screen resolution"), - F("usecalib"), - 2, - noYesOptions, - noYesOptionValues, - Touch_Settings.calibrationEnabled ? 1 : 0, - true); - } + addFormSelector_YesNo(F("Calibrate to screen resolution"), + F("usecalib"), + Touch_Settings.calibrationEnabled ? 1 : 0, + true); // reloadonchange if (Touch_Settings.calibrationEnabled) { addRowLabel(F("Calibration")); diff --git a/src/src/Helpers/OLed_helper.cpp b/src/src/Helpers/OLed_helper.cpp index 9e0d379d0d..60e1a17cf2 100644 --- a/src/src/Helpers/OLed_helper.cpp +++ b/src/src/Helpers/OLed_helper.cpp @@ -6,15 +6,16 @@ void OLedFormController(const __FlashStringHelper *id, const int *values, uint8_t selectedIndex) { - const __FlashStringHelper *controllerOptions[2] = { + const __FlashStringHelper *controllerOptions[] = { F("SSD1306 (128x64 dot controller)"), F("SH1106 (132x64 dot controller)") }; - const int controllerValues[2] = { - 1, - 2 }; - - addFormSelector(F("Controller"), id, 2, controllerOptions, values == nullptr ? controllerValues : values, selectedIndex); + const int controllerValues[] = { 1, 2 }; + FormSelectorOptions selector( + NR_ELEMENTS(controllerOptions), + controllerOptions, + values == nullptr ? controllerValues : values); + selector.addFormSelector(F("Controller"), id, selectedIndex); } /************************************************************************** @@ -28,8 +29,10 @@ void OLedFormRotation(const __FlashStringHelper *id, const int rotationValues[] = { 1, 2 }; - - addFormSelector(F("Rotation"), id, 2, rotationOptions, rotationValues, selectedIndex); + FormSelectorOptions selector( + NR_ELEMENTS(rotationOptions), + rotationOptions, rotationValues); + selector.addFormSelector(F("Rotation"), id, selectedIndex); } /************************************************************************** @@ -45,8 +48,14 @@ void OLedFormContrast(const __FlashStringHelper *id, OLED_CONTRAST_LOW, OLED_CONTRAST_MED, OLED_CONTRAST_HIGH }; + FormSelectorOptions selector( + NR_ELEMENTS(contrastOptions), + contrastOptions, contrastValues); - addFormSelector(F("Contrast"), id, 3, contrastOptions, contrastValues, selectedIndex == 0 ? OLED_CONTRAST_HIGH : selectedIndex); + selector.addFormSelector( + F("Contrast"), + id, + selectedIndex == 0 ? OLED_CONTRAST_HIGH : selectedIndex); } /************************************************************************** @@ -56,12 +65,16 @@ void OLedFormSizes(const __FlashStringHelper *id, const int *values, uint8_t selectedIndex, bool reloadOnChange) { - const __FlashStringHelper *options3[3] = { + const __FlashStringHelper *options3[] = { F("128x64"), F("128x32"), F("64x48") }; - - addFormSelector(F("Display Size"), id, 3, options3, values, selectedIndex, reloadOnChange); + FormSelectorOptions selector(NR_ELEMENTS(options3), options3, values); + selector.reloadonchange = reloadOnChange; + selector.addFormSelector( + F("Display Size"), + id, + selectedIndex); } /************************************************************************** diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 02342f16ee..8df4b141a3 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -169,11 +169,13 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett switch (varType) { case ControllerSettingsStruct::CONTROLLER_USE_DNS: { - const __FlashStringHelper *options[2] = { + const __FlashStringHelper *options[] = { F("Use IP address"), F("Use Hostname") }; - addFormSelector(displayName, internalName, 2, options, nullptr, nullptr, ControllerSettings.UseDNS, true); + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.reloadonchange = true; + selector.addFormSelector(displayName, internalName, ControllerSettings.UseDNS); break; } case ControllerSettingsStruct::CONTROLLER_HOSTNAME: @@ -220,7 +222,9 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett static_cast(TLS_types::TLS_insecure) }; constexpr int nrOptions = NR_ELEMENTS(indices); - addFormSelector(displayName, internalName, nrOptions, options, indices, choice, true); + FormSelectorOptions selector(nrOptions, options, indices); + selector.reloadonchange = true; + selector.addFormSelector(displayName, internalName, choice); addCertificateFileNote(ControllerSettings, F("Certificate or FingerPrint must be stored on the filesystem in"), ControllerSettings.TLStype()); @@ -300,11 +304,12 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett } case ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION: { - const __FlashStringHelper *options[2] { + const __FlashStringHelper *options[] { F("Ignore New"), F("Delete Oldest") }; - addFormSelector(displayName, internalName, 2, options, nullptr, nullptr, ControllerSettings.DeleteOldest, false); + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(displayName, internalName, ControllerSettings.DeleteOldest); break; } case ControllerSettingsStruct::CONTROLLER_ALLOW_EXPIRE: @@ -318,11 +323,12 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett break; case ControllerSettingsStruct::CONTROLLER_CHECK_REPLY: { - const __FlashStringHelper *options[2] = { + const __FlashStringHelper *options[] = { F("Ignore Acknowledgement"), F("Check Acknowledgement") }; - addFormSelector(displayName, internalName, 2, options, nullptr, nullptr, ControllerSettings.MustCheckReply, false); + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(displayName, internalName, ControllerSettings.MustCheckReply); break; } case ControllerSettingsStruct::CONTROLLER_CLIENT_ID: diff --git a/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp b/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp index 047e826ff6..3d882fcfa0 100644 --- a/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp +++ b/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp @@ -47,7 +47,8 @@ void Internal_GPIO_pulseHelper::addGPIOtriggerMode(const __FlashStringHelper *la for (int i = 0; i < NR_TRIGGER_MODES; ++i) { options[i] = Internal_GPIO_pulseHelper::toString(static_cast(optionValues[i])); } - addFormSelector(label, id, NR_TRIGGER_MODES, options, optionValues, static_cast(currentSelection)); + FormSelectorOptions selector( NR_TRIGGER_MODES, options, optionValues); + selector.addFormSelector(label, id, static_cast(currentSelection)); } Internal_GPIO_pulseHelper::Internal_GPIO_pulseHelper(Internal_GPIO_pulseHelper::pulseCounterConfig configuration) diff --git a/src/src/Helpers/_Plugin_Helper_serial.cpp b/src/src/Helpers/_Plugin_Helper_serial.cpp index 1575a266b4..5fb71b99fe 100644 --- a/src/src/Helpers/_Plugin_Helper_serial.cpp +++ b/src/src/Helpers/_Plugin_Helper_serial.cpp @@ -153,15 +153,18 @@ void serialHelper_addI2CuartSelectors(int address, int channel) { if ((channel != SC16IS752_CHANNEL_A) && (channel != SC16IS752_CHANNEL_B)) { channel = SC16IS752_CHANNEL_A; } - const __FlashStringHelper *chOptions[SC16IS752_CHANNELS] = { + const __FlashStringHelper *chOptions[] = { F("A"), F("B"), }; - const int chValues[SC16IS752_CHANNELS] = { + /* + const int chValues[] = { SC16IS752_CHANNEL_A, SC16IS752_CHANNEL_B, }; - addFormSelector(F("Channel"), F("i2cuart_ch"), SC16IS752_CHANNELS, chOptions, chValues, channel); + */ + FormSelectorOptions selector(NR_ELEMENTS(chOptions), chOptions); + selector.addFormSelector(F("Channel"), F("i2cuart_ch"), channel); } } @@ -301,10 +304,13 @@ void serialHelper_webformLoad(ESPEasySerialPort port, int rxPinDef, int txPinDef #endif options[i] = option; } - addFormSelector_script(F("Serial Port"), F("serPort"), NR_ESPEASY_SERIAL_TYPES, - options, ids, nullptr, - static_cast(ESPeasySerialType::getSerialType(port, rxPinDef, txPinDef)), - F("serialPortChanged(this)")); // Script to toggle GPIO visibility when changing selection. + FormSelectorOptions selector(NR_ESPEASY_SERIAL_TYPES, options, ids); + + // Script to toggle GPIO visibility when changing selection. + selector.onChangeCall = F("serialPortChanged(this)"); + selector.addFormSelector( + F("Serial Port"), F("serPort"), + static_cast(ESPeasySerialType::getSerialType(port, rxPinDef, txPinDef))); #if USES_I2C_SC16IS752 serialHelper_addI2CuartSelectors(rxPinDef, txPinDef); #endif // ifndef DISABLE_SC16IS752_Serial diff --git a/src/src/Helpers/_Plugin_Helper_webform.cpp b/src/src/Helpers/_Plugin_Helper_webform.cpp index d49fdde76c..17c1be8e7a 100644 --- a/src/src/Helpers/_Plugin_Helper_webform.cpp +++ b/src/src/Helpers/_Plugin_Helper_webform.cpp @@ -52,12 +52,12 @@ void SwitchWebformLoad( SWITCH_DC_HIGH, SWITCH_DC_BOTH }; - addFormSelector( + FormSelectorOptions selector(NR_ELEMENTS(buttonDCValues), + buttonDC, + buttonDCValues); + selector.addFormSelector( F("Doubleclick event"), F("sw_dc"), - NR_ELEMENTS(buttonDCValues), - buttonDC, - buttonDCValues, doubleClickEvent); } @@ -84,12 +84,14 @@ void SwitchWebformLoad( SWITCH_LONGPRESS_LOW, SWITCH_LONGPRESS_HIGH, SWITCH_LONGPRESS_BOTH }; - addFormSelector( - F("Longpress event"), - F("sw_lp"), + + FormSelectorOptions selector( NR_ELEMENTS(buttonLPValues), buttonLP, - buttonLPValues, + buttonLPValues); + selector.addFormSelector( + F("Longpress event"), + F("sw_lp"), longPressEvent); } diff --git a/src/src/Helpers/_Plugin_SensorTypeHelper.cpp b/src/src/Helpers/_Plugin_SensorTypeHelper.cpp index d51c4dd4b6..7f2c7b35e1 100644 --- a/src/src/Helpers/_Plugin_SensorTypeHelper.cpp +++ b/src/src/Helpers/_Plugin_SensorTypeHelper.cpp @@ -142,13 +142,13 @@ void sensorTypeHelper_loadOutputSelector( if (pconfigIndex < 0 || pconfigIndex >= PLUGIN_CONFIGVAR_MAX) { return; } - - addFormSelector( - concat(F("Value "), valuenr + 1), - sensorTypeHelper_webformID(pconfigIndex), + FormSelectorOptions selector( optionCount, options, - indices, + indices); + selector.addFormSelector( + concat(F("Value "), valuenr + 1), + sensorTypeHelper_webformID(pconfigIndex), PCONFIG(pconfigIndex)); } @@ -159,13 +159,13 @@ void sensorTypeHelper_loadOutputSelector( if (pconfigIndex < 0 || pconfigIndex >= PLUGIN_CONFIGVAR_MAX) { return; } - - addFormSelector( - concat(F("Value "), valuenr + 1), - sensorTypeHelper_webformID(pconfigIndex), + FormSelectorOptions selector( optionCount, options, - indices, + indices); + selector.addFormSelector( + concat(F("Value "), valuenr + 1), + sensorTypeHelper_webformID(pconfigIndex), PCONFIG(pconfigIndex)); } diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 8c7b663974..67b237d52f 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -209,13 +209,14 @@ void P139_data_struct::webform_load(struct EventStruct *event) { static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); - addFormSelector(F("Predefined device configuration"), F("predef"), - valueCount, - predefinedNames, predefinedValues, 0, !isPowerManagerTask); - - if (!isPowerManagerTask) { - addFormNote(F("Page will reload when selection is changed.")); - } + FormSelectorOptions selector( + valueCount, + predefinedNames, predefinedValues); + selector.reloadonchange = !isPowerManagerTask; + selector.addFormSelector( + F("Predefined device configuration"), + F("predef"), + 0); const AXP2101_device_model_e device = static_cast(P139_CURRENT_PREDEFINED); diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 4009769e7e..6bd61831c9 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -333,14 +333,16 @@ void handle_advanced() { F("Light"), F("Dark"), }; - const int cssModeOptions[] = { 0, 1, 2}; - constexpr int nrCssModeOptions = NR_ELEMENTS(cssModeOptions); - addFormSelector(getLabel(LabelType::ENABLE_AUTO_DARK_MODE), - getInternalLabel(LabelType::ENABLE_AUTO_DARK_MODE), - nrCssModeOptions, - cssModeNames, - cssModeOptions, - Settings.getCssMode()); + //const int cssModeOptions[] = { 0, 1, 2}; + constexpr int nrCssModeOptions = NR_ELEMENTS(cssModeNames); + FormSelectorOptions selector( + nrCssModeOptions, + cssModeNames/*, + cssModeOptions*/); + selector.addFormSelector( + getLabel(LabelType::ENABLE_AUTO_DARK_MODE), + getInternalLabel(LabelType::ENABLE_AUTO_DARK_MODE), + Settings.getCssMode()); #endif // FEATURE_AUTO_DARK_MODE #if FEATURE_RULES_EASY_COLOR_CODE diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index ac62ebd169..e7652d06b3 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1239,13 +1239,15 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } else { i2c_mux_channelCount++; } - addFormSelector(F("Multiplexer channels"), - F("taskdeviceflags1"), - i2c_mux_channelCount, - i2c_mux_channels, - i2c_mux_channelOptions, - multipleMuxPorts ? 1 : 0, - true); + FormSelectorOptions selector( + i2c_mux_channelCount, + i2c_mux_channels, + i2c_mux_channelOptions); + selector.reloadonchange = true; + selector.addFormSelector( + F("Multiplexer channels"), + F("taskdeviceflags1"), + multipleMuxPorts ? 1 : 0); } if (multipleMuxPorts) { @@ -1279,12 +1281,14 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } if (taskDeviceI2CMuxPort >= static_cast(mux_max)) { taskDeviceI2CMuxPort = -1; } // Reset if out of range - addFormSelector(F("Connected to"), - F("taskdevicei2cmuxport"), - mux_max + 1, - i2c_mux_portoptions, - i2c_mux_portchoices, - taskDeviceI2CMuxPort); + FormSelectorOptions selector( + mux_max + 1, + i2c_mux_portoptions, + i2c_mux_portchoices); + selector.addFormSelector( + F("Connected to"), + F("taskdevicei2cmuxport"), + taskDeviceI2CMuxPort); } } # endif // if FEATURE_I2CMULTIPLEXER diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 7542abce7e..5e3312276e 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -143,7 +143,6 @@ void handle_hardware() { addFormSubHeader(F("I2C Multiplexer")); // Select the type of multiplexer to use { - # define I2C_MULTIPLEXER_OPTIONCOUNT 5 // Nr. of supported devices + 'None' const __FlashStringHelper *i2c_muxtype_options[] = { F("- None -"), F("TCA9548a - 8 channel"), @@ -158,8 +157,9 @@ void handle_hardware() { I2C_MULTIPLEXER_TCA9543A, I2C_MULTIPLEXER_PCA9540 }; - addFormSelector(F("I2C Multiplexer type"), F("pi2cmuxtype"), I2C_MULTIPLEXER_OPTIONCOUNT, - i2c_muxtype_options, i2c_muxtype_choices, Settings.I2C_Multiplexer_Type); + FormSelectorOptions selector(NR_ELEMENTS(i2c_muxtype_choices), + i2c_muxtype_options, i2c_muxtype_choices); + selector.addFormSelector(F("I2C Multiplexer type"), F("pi2cmuxtype"), Settings.I2C_Multiplexer_Type); } // Select the I2C address for a port multiplexer { @@ -180,7 +180,8 @@ void handle_hardware() { } i2c_mux_choices[mux_opt] = 0x70 + x; } - addFormSelector(F("I2C Multiplexer address"), F("pi2cmuxaddr"), mux_opt + 1, i2c_mux_options, i2c_mux_choices, Settings.I2C_Multiplexer_Addr); + FormSelectorOptions selector(mux_opt + 1, i2c_mux_options, i2c_mux_choices); + selector.addFormSelector(F("I2C Multiplexer address"), F("pi2cmuxaddr"), Settings.I2C_Multiplexer_Addr); } addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), F("pi2cmuxreset"), Settings.I2C_Multiplexer_ResetPin); addFormNote(F("Will be pulled low to force a reset. Reset is not available on PCA9540.")); @@ -214,7 +215,9 @@ void handle_hardware() { static_cast(SPI_Options_e::UserDefined) }; constexpr size_t nrOptions = NR_ELEMENTS(spi_index); - addFormSelector_script(F("Init SPI"), F("initspi"), nrOptions, spi_options, spi_index, nullptr, Settings.InitSPI, F("spiOptionChanged(this)")); + 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); @@ -311,14 +314,14 @@ void handle_hardware() { ? static_cast(Settings.ETH_Phy_Type) : static_cast(EthPhyType_t::notSet); - addFormSelector( - F("Ethernet PHY type"), - F("ethtype"), + FormSelectorOptions selector( nrItems, ethPhyTypes, - ethPhyTypes_index, - choice, - false); + ethPhyTypes_index); + selector.addFormSelector( + F("Ethernet PHY type"), + F("ethtype"), + choice); } #if CONFIG_ETH_USE_SPI_ETHERNET && CONFIG_ETH_USE_ESP32_EMAC diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 6e95390e18..b73bf55fb7 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -424,9 +424,8 @@ void addSelector_Foot() void addUnit(const __FlashStringHelper *unit) { - addHtml(F(" [")); - addHtml(unit); - addHtml(']'); + // Needed so we can check whether it is empty + addUnit(String(unit)); } void addUnit(const String& unit) diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index d423288eae..b972c3ea82 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -621,48 +621,6 @@ void addFormSelector(const String & label, selector.addFormSelector(label, id, selectedIndex); } -void addFormSelector_script(const __FlashStringHelper * label, - const __FlashStringHelper * id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - const String attr[], - int selectedIndex, - const __FlashStringHelper * onChangeCall - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - FormSelectorOptions selector(optionCount, options, indices, attr); -#if FEATURE_TOOLTIPS - selector.tooltip = tooltip; -#endif - selector.onChangeCall = onChangeCall; - selector.addFormSelector(label, id, selectedIndex); -} - -void addFormSelector_script(const __FlashStringHelper * label, - const __FlashStringHelper * id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const __FlashStringHelper * onChangeCall - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - FormSelectorOptions selector(optionCount, options, indices, attr); -#if FEATURE_TOOLTIPS - selector.tooltip = tooltip; -#endif - selector.onChangeCall = onChangeCall; - selector.addFormSelector(label, id, selectedIndex); -} - void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, int selectedIndex, @@ -677,8 +635,10 @@ void addFormSelector_YesNo(const __FlashStringHelper * label, bool reloadonchange) { const __FlashStringHelper *optionsNoYes[] = { F("No"), F("Yes") }; - int optionValuesNoYes[] = { 0, 1 }; - addFormSelector(label, id, NR_ELEMENTS(optionValuesNoYes), optionsNoYes, optionValuesNoYes, selectedIndex, reloadonchange); + //int optionValuesNoYes[] = { 0, 1 }; + FormSelectorOptions selector(NR_ELEMENTS(optionsNoYes), optionsNoYes); + selector.reloadonchange = reloadonchange; + selector.addFormSelector(label, id, selectedIndex); } @@ -702,12 +662,6 @@ void addFormPinStateSelect(int gpio, int choice) bool input, output, warning; if (getGpioInfo(gpio, pinnr, input, output, warning)) { - const String id = String('p') + gpio; - addRowLabel_tr_id( - concat( - F("Pin mode "), - createGPIO_label(gpio, pinnr, input, output, warning)), - id); bool hasPullUp, hasPullDown; getGpioPullResistor(gpio, hasPullUp, hasPullDown); int nr_options = 0; @@ -745,13 +699,17 @@ void addFormPinStateSelect(int gpio, int choice) ++nr_options; } } - addSelector(id, nr_options, options, option_val, nullptr, choice, false, enabled); - { - const String conflict = getConflictingUse(gpio); - if (!conflict.isEmpty()) { - addUnit(conflict); - } - } + FormSelectorOptions selector(nr_options, options, option_val); + selector.enabled = enabled; + + const String id = String('p') + gpio; + selector.addFormSelector( + concat( + F("Pin mode "), + createGPIO_label(gpio, pinnr, input, output, warning)), + id, + choice); + addUnit(getConflictingUse(gpio)); } } diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index 6c9b10eb63..e6d544abbc 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -356,34 +356,6 @@ void addFormSelector(const String& label, #endif ); -void addFormSelector_script(const __FlashStringHelper * label, - const __FlashStringHelper * id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - const String attr[], - int selectedIndex, - const __FlashStringHelper * onChangeCall - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif - ); - - -void addFormSelector_script(const __FlashStringHelper * label, - const __FlashStringHelper * id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const __FlashStringHelper * onChangeCall - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif - ); void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, From d0540dd9326e69d41f12d09af5ecab5c0d72a536 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 25 Jan 2025 13:23:15 +0100 Subject: [PATCH 33/50] [P139] Uncrustify formatting --- lib/AXP2101/src/AXP2101_settings.cpp | 45 ++++++------ lib/AXP2101/src/AXP2101_settings.h | 101 ++++++++++++++------------- src/_P139_AXP2101.ino | 11 +-- 3 files changed, 78 insertions(+), 79 deletions(-) diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index a1b569228a..e0acdc18d8 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -53,7 +53,6 @@ const __FlashStringHelper* toString(AXP2101_registers_e reg, case AXP2101_registers_e::vbus: return displayString ? F("BusVoltage") : F("vbus"); case AXP2101_registers_e::vsys: return displayString ? F("SysVoltage") : F("vsys"); case AXP2101_registers_e::chiptemp: return displayString ? F("ChipTemp") : F("chiptemp"); - } return F(""); } @@ -233,15 +232,15 @@ AXP2101_settings::AXP2101_settings() { chargeStates.pre_chg_cur = 0b0101; chargeStates.const_cur_lim = 0b01001; // 300 mA, however can be set via EFUSE chargeStates.term_cur_lim_en = 1; - chargeStates.term_cur_lim = 0b0101; // 125 mA - chargeStates.chg_volt_lim = 0b011; // 4.2V - chargeStates.thermal_thresh = 0b10; // 100 deg + chargeStates.term_cur_lim = 0b0101; // 125 mA + chargeStates.chg_volt_lim = 0b011; // 4.2V + chargeStates.thermal_thresh = 0b10; // 100 deg chargeStates.chg_timeout_ctrl = 0b11100110; chargeStates.bat_detection = 1; - chargeStates.coincell_term_volt = 0b011; // 2.9V - chargeStates.min_sys_voltage = 0b110; // 4.7V - chargeStates.inp_volt_limit = 0b0110; // 4.36V - chargeStates.inp_cur_limit = 0b100; // 1500 mA + chargeStates.coincell_term_volt = 0b011; // 2.9V + chargeStates.min_sys_voltage = 0b110; // 4.7V + chargeStates.inp_volt_limit = 0b0110; // 4.36V + chargeStates.inp_cur_limit = 0b100; // 1500 mA } // constructor @@ -269,15 +268,15 @@ AXP2101_settings::AXP2101_settings(uint16_t _dcdc1, uint16_t _dcdc2, uint16_t _d chargeStates.pre_chg_cur = 0b0101; chargeStates.const_cur_lim = 0b01001; // 300 mA, however can be set via EFUSE chargeStates.term_cur_lim_en = 1; - chargeStates.term_cur_lim = 0b0101; // 125 mA - chargeStates.chg_volt_lim = 0b011; // 4.2V - chargeStates.thermal_thresh = 0b10; // 100 deg + chargeStates.term_cur_lim = 0b0101; // 125 mA + chargeStates.chg_volt_lim = 0b011; // 4.2V + chargeStates.thermal_thresh = 0b10; // 100 deg chargeStates.chg_timeout_ctrl = 0b11100110; chargeStates.bat_detection = 1; - chargeStates.coincell_term_volt = 0b011; // 2.9V - chargeStates.min_sys_voltage = 0b110; // 4.7V - chargeStates.inp_volt_limit = 0b0110; // 4.36V - chargeStates.inp_cur_limit = 0b100; // 1500 mA + chargeStates.coincell_term_volt = 0b011; // 2.9V + chargeStates.min_sys_voltage = 0b110; // 4.7V + chargeStates.inp_volt_limit = 0b0110; // 4.36V + chargeStates.inp_cur_limit = 0b100; // 1500 mA } void AXP2101_settings::setVoltage(AXP2101_registers_e reg, @@ -432,7 +431,6 @@ void AXP2101_settings::setTS_disabled(bool val) { pinStates.dis_TS_pin = val; } - uint16_t AXP2101_settings::getPreChargeCurrentLimit() const { return chargeStates.pre_chg_cur * 25; } @@ -455,6 +453,7 @@ void AXP2101_settings::setConstChargeCurrentLimit(uint16_t current_mA) { if (current_mA > 1000) { current_mA = 1000; } + if (current_mA <= 200) { chargeStates.const_cur_lim = current_mA / 25; } else { @@ -474,17 +473,17 @@ void AXP2101_settings::setTerminationChargeCurrentLimit(uint16_t current_mA, boo if (current_mA > 200) { current_mA = 200; } - chargeStates.term_cur_lim = current_mA / 25; + chargeStates.term_cur_lim = current_mA / 25; chargeStates.term_cur_lim_en = enable; } - AXP2101_CV_charger_voltage_e AXP2101_settings::getCV_chargeVoltage() const { return static_cast(chargeStates.chg_volt_lim); } void AXP2101_settings::setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV) { chargeStates.chg_volt_lim = static_cast(voltage_mV); + if (chargeStates.chg_volt_lim > static_cast(AXP2101_CV_charger_voltage_e::limit_4_40V)) { // Set to a default safe limit chargeStates.chg_volt_lim = static_cast(AXP2101_CV_charger_voltage_e::limit_4_20V); @@ -499,24 +498,22 @@ void AXP2101_settings::setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dp chargeStates.min_sys_voltage = static_cast(voltage); } - -AXP2101_VINDPM_e AXP2101_settings::getVin_DPM() const { +AXP2101_VINDPM_e AXP2101_settings::getVin_DPM() const { return static_cast(chargeStates.inp_volt_limit); } -void AXP2101_settings::setVin_DPM(AXP2101_VINDPM_e voltage) { +void AXP2101_settings::setVin_DPM(AXP2101_VINDPM_e voltage) { chargeStates.inp_volt_limit = static_cast(voltage); } -AXP2101_InputCurrentLimit_e AXP2101_settings::getInputCurrentLimit() const { +AXP2101_InputCurrentLimit_e AXP2101_settings::getInputCurrentLimit() const { return static_cast(chargeStates.inp_cur_limit); } -void AXP2101_settings::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { +void AXP2101_settings::setInputCurrentLimit(AXP2101_InputCurrentLimit_e current) { chargeStates.inp_cur_limit = static_cast(current); } - /** * AXP2101 device class */ diff --git a/lib/AXP2101/src/AXP2101_settings.h b/lib/AXP2101/src/AXP2101_settings.h index c2e0cc6633..891d1d345d 100644 --- a/lib/AXP2101/src/AXP2101_settings.h +++ b/lib/AXP2101/src/AXP2101_settings.h @@ -69,6 +69,7 @@ #define AXP2101_VBUS_CTRL_MASK (1 << 2) #define AXP2101_VSYS_CTRL_MASK (1 << 3) #define AXP2101_TDIE_CTRL_MASK (1 << 4) + // What to do with bit 5: "general purpose ADC channel enable"? @@ -227,10 +228,10 @@ enum class AXP2101_chargingDetail_e : uint8_t { }; enum class AXP2101_CV_charger_voltage_e : uint8_t { - reserved = 0, + reserved = 0, limit_4_00V = 0b001, limit_4_10V = 0b010, - limit_4_20V = 0b011, // default + limit_4_20V = 0b011, // default limit_4_35V = 0b100, limit_4_40V = 0b101, MAX @@ -344,34 +345,34 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the // Reg 61: Iprechg Charger Settings uint16_t getPreChargeCurrentLimit() const; - void setPreChargeCurrentLimit(uint16_t current_mA); + void setPreChargeCurrentLimit(uint16_t current_mA); // Reg 62: ICC Charger Settings uint16_t getConstChargeCurrentLimit() const; - void setConstChargeCurrentLimit(uint16_t current_mA); + void setConstChargeCurrentLimit(uint16_t current_mA); // Reg 63: Iterm Charger Settings and Control // Enable/Disable via chargeStates.term_cur_lim_en uint16_t getTerminationChargeCurrentLimit() const; - bool getTerminationChargeCurrentLimitEnable() const; - void setTerminationChargeCurrentLimit(uint16_t current_mA, bool enable); + bool getTerminationChargeCurrentLimitEnable() const; + void setTerminationChargeCurrentLimit(uint16_t current_mA, + bool enable); // Reg 64: CV Charger Voltage Settings - AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; - void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); + AXP2101_CV_charger_voltage_e getCV_chargeVoltage() const; + void setCV_chargeVoltage(AXP2101_CV_charger_voltage_e voltage_mV); // Reg 14: Minimum System Voltage Control AXP2101_Linear_Charger_Vsys_dpm_e getLinear_Charger_Vsys_dpm() const; - void setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage); + void setLinear_Charger_Vsys_dpm(AXP2101_Linear_Charger_Vsys_dpm_e voltage); // Reg 15: Input Voltage Limit - AXP2101_VINDPM_e getVin_DPM() const; - void setVin_DPM(AXP2101_VINDPM_e voltage); + AXP2101_VINDPM_e getVin_DPM() const; + void setVin_DPM(AXP2101_VINDPM_e voltage); // Reg 16: Input Current Limit - AXP2101_InputCurrentLimit_e getInputCurrentLimit() const; - void setInputCurrentLimit(AXP2101_InputCurrentLimit_e current); - + AXP2101_InputCurrentLimit_e getInputCurrentLimit() const; + void setInputCurrentLimit(AXP2101_InputCurrentLimit_e current); private: @@ -395,53 +396,53 @@ class AXP2101_settings { // Voltages in mV, range 0..3700, max. depending on the uint16_t registers_[AXP2101_settings_count]{}; }; union { - struct { // AXP_pin_s: Off / On / default / disabled / unavailable? / unused? / Protected - uint64_t en_dcdc1 : 3; // bit 0/1/2 - uint64_t en_dcdc2 : 3; // bit 3/4/5 - uint64_t en_dcdc3 : 3; // bit 6/7/8 - uint64_t en_dcdc4 : 3; // bit 9/10/11 - uint64_t en_dcdc5 : 3; // bit 12/13/14 - uint64_t en_aldo1 : 3; // bit 15/16/17 - uint64_t en_aldo2 : 3; // bit 18/19/20 - uint64_t en_aldo3 : 3; // bit 21/22/23 - uint64_t en_aldo4 : 3; // bit 24/25/26 - uint64_t en_bldo1 : 3; // bit 27/28/29 - uint64_t en_bldo2 : 3; // bit 30/31/32 - uint64_t en_dldo1 : 3; // bit 33/34/35 - uint64_t en_dldo2 : 3; // bit 36/37/38 - uint64_t en_cpuldos : 3; // bit 39/40/41 - uint64_t chargeled : 3; // bit 42/43/44 + struct { // AXP_pin_s: Off / On / default / disabled / unavailable? / unused? / Protected + uint64_t en_dcdc1 : 3; // bit 0/1/2 + uint64_t en_dcdc2 : 3; // bit 3/4/5 + uint64_t en_dcdc3 : 3; // bit 6/7/8 + uint64_t en_dcdc4 : 3; // bit 9/10/11 + uint64_t en_dcdc5 : 3; // bit 12/13/14 + uint64_t en_aldo1 : 3; // bit 15/16/17 + uint64_t en_aldo2 : 3; // bit 18/19/20 + uint64_t en_aldo3 : 3; // bit 21/22/23 + uint64_t en_aldo4 : 3; // bit 24/25/26 + uint64_t en_bldo1 : 3; // bit 27/28/29 + uint64_t en_bldo2 : 3; // bit 30/31/32 + uint64_t en_dldo1 : 3; // bit 33/34/35 + uint64_t en_dldo2 : 3; // bit 36/37/38 + uint64_t en_cpuldos : 3; // bit 39/40/41 + uint64_t chargeled : 3; // bit 42/43/44 // Settings for external temperature sensor (TS) - uint64_t dis_TS_pin : 1; // bit 45, reg50 bit 4 - uint64_t TS_cur_src : 2; // bit 46/47, reg50 bit 3:2 - uint64_t TS_current : 2; // bit 48/49, reg50 bit 1:0 + uint64_t dis_TS_pin : 1; // bit 45, reg50 bit 4 + uint64_t TS_cur_src : 2; // bit 46/47, reg50 bit 3:2 + uint64_t TS_current : 2; // bit 48/49, reg50 bit 1:0 - uint64_t en_unused : 13; // bit 50..63 // All bits defined + uint64_t en_unused : 13; // bit 50..63 // All bits defined } pinStates; - uint64_t pinStates_{}; // 8 bytes + uint64_t pinStates_{}; // 8 bytes }; - union { - struct { - uint64_t pre_chg_cur : 4; // reg 0x61: 25* N mA - uint64_t const_cur_lim : 5; // reg 0x62: 25* N mA if N <= 8, 200+100*(N-8) mA if N > 8 - uint64_t term_cur_lim_en : 1; // reg 0x63: Charging termination of current enable - uint64_t term_cur_lim : 4; // reg 0x63: 25* N mA - uint64_t chg_volt_lim : 3; // reg 0x64: - uint64_t thermal_thresh : 2; // reg 0x65: 00: 60deg, 01: 80deg, 10: 100deg, 11:120deg - uint64_t chg_timeout_ctrl: 8; // reg 0x67: - uint64_t bat_detection : 1; // reg 0x68: + union { + struct { + uint64_t pre_chg_cur : 4; // reg 0x61: 25* N mA + uint64_t const_cur_lim : 5; // reg 0x62: 25* N mA if N <= 8, 200+100*(N-8) mA if N > 8 + uint64_t term_cur_lim_en : 1; // reg 0x63: Charging termination of current enable + uint64_t term_cur_lim : 4; // reg 0x63: 25* N mA + uint64_t chg_volt_lim : 3; // reg 0x64: + uint64_t thermal_thresh : 2; // reg 0x65: 00: 60deg, 01: 80deg, 10: 100deg, 11:120deg + uint64_t chg_timeout_ctrl : 8; // reg 0x67: + uint64_t bat_detection : 1; // reg 0x68: uint64_t coincell_term_volt : 3; // reg 0x6A: 2.6~3.3V, 100mV/step, 8 steps - uint64_t min_sys_voltage : 3; // reg 0x14: 4.1 + N*0.1V Linear Charger Vsys Voltage dpm - uint64_t inp_volt_limit : 4; // reg 0x15: Vindpm 3.88+N*0.08V - uint64_t inp_cur_limit : 3; // reg 0x16: + uint64_t min_sys_voltage : 3; // reg 0x14: 4.1 + N*0.1V Linear Charger Vsys Voltage dpm + uint64_t inp_volt_limit : 4; // reg 0x15: Vindpm 3.88+N*0.08V + uint64_t inp_cur_limit : 3; // reg 0x16: - uint64_t en_unused : 23; + uint64_t en_unused : 23; } chargeStates; - uint64_t chargeStates_{}; // 8 bytes + uint64_t chargeStates_{}; // 8 bytes }; }; diff --git a/src/_P139_AXP2101.ino b/src/_P139_AXP2101.ino index 819fd334d2..b817e7c09a 100644 --- a/src/_P139_AXP2101.ino +++ b/src/_P139_AXP2101.ino @@ -113,9 +113,10 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) if (i < P139_NR_OUTPUT_VALUES) { const uint8_t choice = PCONFIG(P139_CONFIG_BASE + i); ExtraTaskSettings.setTaskDeviceValueName(i, toString(static_cast(choice), false)); - if (choice != (static_cast(AXP2101_registers_e::battemp)) && - choice != (static_cast(AXP2101_registers_e::chiptemp))) { - ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; + + if ((choice != (static_cast(AXP2101_registers_e::battemp))) && + (choice != (static_cast(AXP2101_registers_e::chiptemp)))) { + ExtraTaskSettings.TaskDeviceValueDecimals[i] = 0; } } else { ExtraTaskSettings.clearTaskDeviceValueName(i); @@ -180,8 +181,8 @@ boolean Plugin_139(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { - bool created_new = false; - P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); + bool created_new = false; + P139_data_struct *P139_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr == P139_data) { P139_data = new (std::nothrow) P139_data_struct(); From f0ce5f272b8a771aa4c0c76c2f48f2af6ade4d8c Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 25 Jan 2025 13:24:14 +0100 Subject: [PATCH 34/50] [P139] Fix saving settings & predefined, also when task is not enabled --- src/src/PluginStructs/P139_data_struct.cpp | 26 +++++++++++----------- src/src/PluginStructs/P139_data_struct.h | 3 ++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 67b237d52f..2c0359ad37 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -186,11 +186,6 @@ void P139_data_struct::webform_load(struct EventStruct *event) { addFormSubHeader(F("Hardware outputs AXP2101")); { - if (P139_CONFIG_PREDEFINED > 0) { - P139_CURRENT_PREDEFINED = P139_CONFIG_PREDEFINED; - P139_CONFIG_PREDEFINED = 0; - applyDeviceModelTemplate(static_cast(P139_CURRENT_PREDEFINED)); - } const __FlashStringHelper *predefinedNames[] = { toString(AXP2101_device_model_e::Unselected), toString(AXP2101_device_model_e::M5Stack_Core2_v1_1), @@ -208,13 +203,13 @@ void P139_data_struct::webform_load(struct EventStruct *event) { static_cast(AXP2101_device_model_e::LilyGO_TBeamS3_v3), static_cast(AXP2101_device_model_e::LilyGO_TPCie_v1_2), static_cast(AXP2101_device_model_e::UserDefined) }; // keep last !! - constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); + constexpr uint8_t valueCount = NR_ELEMENTS(predefinedValues); FormSelectorOptions selector( valueCount, predefinedNames, predefinedValues); selector.reloadonchange = !isPowerManagerTask; selector.addFormSelector( - F("Predefined device configuration"), + F("Predefined device configuration"), F("predef"), 0); @@ -329,15 +324,20 @@ void P139_data_struct::webform_save(struct EventStruct *event) { toString(static_cast(PCONFIG(P139_CONFIG_BASE + i)), false)); } - P139_CONFIG_PREDEFINED = getFormItemInt(F("predef")); + const int predefined = getFormItemInt(F("predef")); P139_SET_GENERATE_EVENTS(isFormItemChecked(F("events"))); - for (int s = 0; s < AXP2101_settings_count; ++s) { - const AXP2101_registers_e reg = AXP2101_intToRegister(s); + if (predefined > 0) { // Apply new template to save it, even when task is disabled + P139_CURRENT_PREDEFINED = predefined; + applyDeviceModelTemplate(static_cast(P139_CURRENT_PREDEFINED)); + } else { + for (int s = 0; s < AXP2101_settings_count; ++s) { + const AXP2101_registers_e reg = AXP2101_intToRegister(s); - if (!AXP2101_isPinProtected(_settings.getState(reg))) { - _settings.setVoltage(reg, getFormItemInt(toString(reg, false))); - _settings.setState(reg, static_cast(getFormItemInt(concat(F("ps"), toString(reg, false))))); + if (!AXP2101_isPinProtected(_settings.getState(reg))) { + _settings.setVoltage(reg, getFormItemInt(toString(reg, false))); + _settings.setState(reg, static_cast(getFormItemInt(concat(F("ps"), toString(reg, false))))); + } } } diff --git a/src/src/PluginStructs/P139_data_struct.h b/src/src/PluginStructs/P139_data_struct.h index 04a30cbd8f..abd0c9d149 100644 --- a/src/src/PluginStructs/P139_data_struct.h +++ b/src/src/PluginStructs/P139_data_struct.h @@ -14,7 +14,8 @@ # define P139_SENSOR_TYPE_INDEX (P139_CONFIG_BASE + VARS_PER_TASK) # define P139_NR_OUTPUT_VALUES getValueCountFromSensorType(static_cast(PCONFIG(P139_SENSOR_TYPE_INDEX))) # define P139_CONFIG_DECIMALS PCONFIG(P139_SENSOR_TYPE_INDEX + 1) -# define P139_CONFIG_PREDEFINED PCONFIG(P139_SENSOR_TYPE_INDEX + 2) + +// # define P139_CONFIG_PREDEFINED PCONFIG(P139_SENSOR_TYPE_INDEX + 2) // available for re-use # define P139_CURRENT_PREDEFINED PCONFIG_FLOAT(0) # define P139_FLAGS PCONFIG_ULONG(0) From 6301cbeba730040a85038c68e69145b4fa31680b Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 25 Jan 2025 13:26:48 +0100 Subject: [PATCH 35/50] [P139] Updated TTGO T-Pcie configuration defaults --- lib/AXP2101/src/AXP2101_settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AXP2101/src/AXP2101_settings.cpp b/lib/AXP2101/src/AXP2101_settings.cpp index e0acdc18d8..c44ae79cd9 100644 --- a/lib/AXP2101/src/AXP2101_settings.cpp +++ b/lib/AXP2101/src/AXP2101_settings.cpp @@ -526,7 +526,7 @@ AXP2101_settings AXP2101_deviceSettingsArray[] = /* M5Stack CoreS3 */ { 3300, 0, 3300, 0, 0, 1800, 3300, 3300, 3300, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, /* LilyGo TBeam v1.2 */ { 3300, 0, 2500, 0, 0, 0, 3300, 3300, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, /* LilyGo TBeamS3 */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, -/* LilyGo TPCie v1.2 */ { 3300, 900, 900, 1100, 1200, 1800, 2800, 3300, 2900, 1800, 2800, 0, 0, 0, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, +/* LilyGo TPCie v1.2 */ { 3300, 900, 900, 1100, 1200, 1800, 2800, 3300, 2900, 1800, 2800, 500, 1900, 1300, AXP_pin_s::Protected, AXP_pin_s::Disabled, AXP_pin_s::Protected, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP_pin_s::Disabled, AXP2101_chargeled_d::Off }, /* Userdefined */ { 3300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP_pin_s::Default, AXP2101_chargeled_d::Off }, }; // *INDENT-ON* From 3bc83be1cde118dabfcfc6dddbe5f320925c195b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Jan 2025 17:30:18 +0100 Subject: [PATCH 36/50] [Cleanup] Show reload icon next to selection box triggering page reload --- src/src/DataTypes/FormSelectorOptions.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index fcfc8267c5..046e3a84dd 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -2,6 +2,7 @@ #include "../WebServer/Markup.h" #include "../WebServer/Markup_Forms.h" +#include "../WebServer/HTML_wrappers.h" FormSelectorOptions::FormSelectorOptions(int optionCount) : _optionCount(optionCount) @@ -141,6 +142,7 @@ void FormSelectorOptions::addFormSelector( } addSelector_Foot(); if (reloadonchange) { - addFormNote(F("Page will reload when selection is changed.")); + addHtml(F("🔄")); +// addFormNote(F("Page will reload when selection is changed.")); } } From a28eccc1036e6a74639c63b3443a5030c8195db1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Jan 2025 17:31:07 +0100 Subject: [PATCH 37/50] [Cleanup] Move more code to use new FormSelectorOptions class --- src/_P001_Switch.ino | 13 ++-- src/_P039_Thermosensors.ino | 34 ++++++---- src/_P042_Candle.ino | 5 +- src/_P045_MPU6050.ino | 6 +- src/_P046_VentusW266.ino | 7 ++- src/_P047_i2c-soil-moisture-sensor.ino | 4 +- src/_P049_MHZ19.ino | 10 +-- src/_P050_TCS34725.ino | 25 +++++--- src/_P052_SenseAir.ino | 11 ++-- src/_P053_PMSx003.ino | 32 +++++----- src/_P057_HT16K33_LED.ino | 5 +- src/_P059_Encoder.ino | 3 +- src/_P061_KeyPad.ino | 3 +- src/_P062_MPR121_KeyPad.ino | 3 +- src/_P064_APDS9960.ino | 87 ++++++++++++++++---------- src/_P066_VEML6040.ino | 6 +- src/_P067_HX711_Load_Cell.ino | 6 +- src/_P073_7DGT.ino | 9 ++- src/_P074_TSL2591.ino | 12 ++-- src/_P075_Nextion.ino | 3 +- src/_P076_HLW8012.ino | 68 ++++++++++---------- src/_P078_Eastron.ino | 6 +- src/_P079_Wemos_Motorshield.ino | 3 +- src/_P082_GPS.ino | 6 +- src/_P084_VEML6070.ino | 3 +- src/_P085_AcuDC243.ino | 3 +- src/_P086_Homie.ino | 5 +- src/_P087_SerialProxy.ino | 15 ++--- src/_P090_CCS811.ino | 6 +- src/_P091_SerSwitch.ino | 16 +++-- src/_P092_DLbus.ino | 18 +++--- src/_P095_ILI9341.ino | 21 +++---- 32 files changed, 267 insertions(+), 187 deletions(-) diff --git a/src/_P001_Switch.ino b/src/_P001_Switch.ino index 2d16e8858c..78535e1630 100644 --- a/src/_P001_Switch.ino +++ b/src/_P001_Switch.ino @@ -86,7 +86,8 @@ boolean Plugin_001(uint8_t function, struct EventStruct *event, String& string) const int optionValues[] = { PLUGIN_001_TYPE_SWITCH, PLUGIN_001_TYPE_DIMMER }; const uint8_t switchtype = P001_data_struct::P001_getSwitchType(event); constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Switch Type"), F("type"), optionCount, options, optionValues, switchtype); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Switch Type"), F("type"), switchtype); if (switchtype == PLUGIN_001_TYPE_DIMMER) { @@ -99,16 +100,18 @@ boolean Plugin_001(uint8_t function, struct EventStruct *event, String& string) F("Normal Switch"), F("Push Button Active Low"), F("Push Button Active High") }; +/* const int buttonOptionValues[] = { SWITCH_TYPE_NORMAL_SWITCH, SWITCH_TYPE_PUSH_ACTIVE_LOW, SWITCH_TYPE_PUSH_ACTIVE_HIGH }; - addFormSelector( +*/ + FormSelectorOptions selector( + NR_ELEMENTS(buttonOptions), + buttonOptions); // buttonOptionValues); + selector.addFormSelector( F("Switch Button Type"), F("button"), - NR_ELEMENTS(buttonOptionValues), - buttonOptions, - buttonOptionValues, P001_BUTTON_TYPE); } diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index a016f68d07..d7ab0bf10b 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -434,7 +434,9 @@ 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); - addFormSelector(F("Sensor Family Type"), F("famtype"), optionCount, Foptions, FoptionValues, family, true); // auto reload activated + FormSelectorOptions selector( optionCount, Foptions, FoptionValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Sensor Family Type"), F("famtype"), family); } const uint8_t choice = P039_MAX_TYPE; @@ -446,7 +448,9 @@ 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); - addFormSelector(F("Adapter IC"), F("maxtype"), optionCount, options, optionValues, choice, true); // auto reload activated + FormSelectorOptions selector( optionCount, options, optionValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Adapter IC"), F("maxtype"), choice); } if (choice == P039_MAX31856) { @@ -472,13 +476,15 @@ 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); - addFormSelector(F("Thermocouple type"), F("tctype"), optionCount, Toptions, ToptionValues, P039_TC_TYPE); + FormSelectorOptions selector( optionCount, Toptions, ToptionValues); + selector.addFormSelector(F("Thermocouple type"), F("tctype"), P039_TC_TYPE); } { const __FlashStringHelper *Coptions[] = { F("1"), F("2"), F("4"), F("8"), F("16") }; const int CoptionValues[] = { 0, 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(CoptionValues); - addFormSelector(F("Averaging"), F("contype"), optionCount, Coptions, CoptionValues, P039_CONFIG_4); + FormSelectorOptions selector(optionCount, Coptions, CoptionValues); + selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); addUnit(F("sample(s)")); } P039_AddMainsFrequencyFilterSelection(event); @@ -489,7 +495,9 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *TPoptions[] = { F("MAX 31865"), F("LM7x") }; const int TPoptionValues[] = { P039_MAX31865, P039_LM7x }; constexpr size_t optionCount = NR_ELEMENTS(TPoptionValues); - addFormSelector(F("Adapter IC"), F("maxtype"), optionCount, TPoptions, TPoptionValues, choice, true); // auto reload activated + FormSelectorOptions selector(optionCount, TPoptions, TPoptionValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Adapter IC"), F("maxtype"), choice); addFormNote(F("LM7x support is experimental.")); } @@ -503,12 +511,14 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *PToptions[] = { F("PT100"), F("PT1000") }; const int PToptionValues[] = { MAX31865_PT100, MAX31865_PT1000 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); - addFormSelector(F("Resistor Type"), F("rtdtype"), optionCount, PToptions, PToptionValues, P039_RTD_TYPE); + FormSelectorOptions selector(optionCount, PToptions, PToptionValues); + 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); - addFormSelector(F("Connection Type"), F("contype"), optionCount, Coptions, nullptr, P039_CONFIG_4); + FormSelectorOptions selector( optionCount, Coptions); + selector.addFormSelector(F("Connection Type"), F("contype"), P039_CONFIG_4); addUnit(F("wire")); } @@ -539,7 +549,8 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { F("LM70"), F("LM71"), F("LM74"), F("TMP121"), F("TMP122"), F("TMP123"), F("TMP124"), F("TMP125") }; const int PToptionValues[] = { LM7x_SD70, LM7x_SD71, LM7x_SD74, LM7x_SD121, LM7x_SD122, LM7x_SD123, LM7x_SD124, LM7x_SD125 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); - addFormSelector(F("LM7x device details"), F("rtd_lm_type"), optionCount, PToptions, PToptionValues, P039_RTD_LM_TYPE); + FormSelectorOptions selector(optionCount, PToptions, PToptionValues); + selector.addFormSelector(F("LM7x device details"), F("rtd_lm_type"), P039_RTD_LM_TYPE); addFormNote(F("TMP122/124 Limited support -> fixed 12 Bit res, no advanced options")); } { @@ -808,10 +819,11 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event) { - const __FlashStringHelper *FToptions[2] = { F("60"), F("50") }; - const int FToptionValues[2] = { 0, 1 }; + const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; + const int FToptionValues[] = { 0, 1 }; - addFormSelector(F("Supply Frequency Filter"), F("filttype"), 2, FToptions, FToptionValues, P039_RTD_FILT_TYPE); + 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)")); diff --git a/src/_P042_Candle.ino b/src/_P042_Candle.ino index 6cc8c62086..5c65a2bc67 100644 --- a/src/_P042_Candle.ino +++ b/src/_P042_Candle.ino @@ -139,7 +139,7 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Led Count"), P042_WEBVAR_PIXELCOUNT, P042_CONFIG_PIXELCOUNT, 1, P042_MAX_PIXELS); { - const __FlashStringHelper *options[P042_FLAME_OPTIONS] = { + const __FlashStringHelper *options[] = { F("Off"), F("Static Light"), F("Simple Candle"), @@ -155,7 +155,8 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) } // Candle Type Selection - addFormSelector(F("Flame Type"), P042_WEBVAR_CANDLETYPE, P042_FLAME_OPTIONS, options, nullptr, P042_CONFIG_CANDLETYPE); + FormSelectorOptions selector(P042_FLAME_OPTIONS, options, nullptr); + selector.addFormSelector(F("Flame Type"), P042_WEBVAR_CANDLETYPE, P042_CONFIG_CANDLETYPE); } // Advanced Color options diff --git a/src/_P045_MPU6050.ino b/src/_P045_MPU6050.ino index b6cad82981..25d27ecfb2 100644 --- a/src/_P045_MPU6050.ino +++ b/src/_P045_MPU6050.ino @@ -157,7 +157,7 @@ boolean Plugin_045(uint8_t function, struct EventStruct *event, String& string) { uint8_t choice = PCONFIG(1); { - const __FlashStringHelper *options[10] = { + const __FlashStringHelper *options[] = { F("Movement detection"), F("Range acceleration X"), F("Range acceleration Y"), @@ -169,7 +169,9 @@ boolean Plugin_045(uint8_t function, struct EventStruct *event, String& string) F("G-force Y"), F("G-force Z") }; - addFormSelector(F("Function"), F("pfunction"), 10, options, nullptr, choice); + + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(F("Function"), F("pfunction"), choice); } if (choice == 0) { diff --git a/src/_P046_VentusW266.ino b/src/_P046_VentusW266.ino index 5143597d43..99d8034bc5 100644 --- a/src/_P046_VentusW266.ino +++ b/src/_P046_VentusW266.ino @@ -161,8 +161,7 @@ boolean Plugin_046(uint8_t function, struct EventStruct *event, String& string) { uint8_t choice = PCONFIG(0); { - const uint8_t nrchoices = 9; - const __FlashStringHelper * options[nrchoices] = { + const __FlashStringHelper * options[] = { F("Main + Temp/Hygro"), F("Wind"), F("Rain"), @@ -175,7 +174,9 @@ boolean Plugin_046(uint8_t function, struct EventStruct *event, String& string) F("Unknown 3, uint8_t 19"), }; - addFormSelector(F("Plugin function"), F("p046"), nrchoices, options, nullptr, choice, true); + 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.")); } diff --git a/src/_P047_i2c-soil-moisture-sensor.ino b/src/_P047_i2c-soil-moisture-sensor.ino index 735f7d96b9..02fe780ece 100644 --- a/src/_P047_i2c-soil-moisture-sensor.ino +++ b/src/_P047_i2c-soil-moisture-sensor.ino @@ -151,7 +151,9 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) # endif // if P047_FEATURE_ADAFRUIT }; constexpr size_t P047_MODEL_OPTIONS = NR_ELEMENTS(SensorModelIds); - addFormSelector(F("Sensor model"), F("model"), P047_MODEL_OPTIONS, SensorModels, SensorModelIds, P047_MODEL, true); + FormSelectorOptions selector( P047_MODEL_OPTIONS, SensorModels, SensorModelIds); + selector.reloadonchange = true; + selector.addFormSelector(F("Sensor model"), F("model"), P047_MODEL); addFormNote(F("Changing the Sensor model will reload the page.")); } diff --git a/src/_P049_MHZ19.ino b/src/_P049_MHZ19.ino index a993ca59a1..5c364e0100 100644 --- a/src/_P049_MHZ19.ino +++ b/src/_P049_MHZ19.ino @@ -92,9 +92,10 @@ boolean Plugin_049(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { { - const __FlashStringHelper *options[2] = { F("Normal"), F("ABC disabled") }; - const int optionValues[2] = { P049_ABC_enabled, P049_ABC_disabled }; - addFormSelector(F("Auto Base Calibration"), F("abcdisable"), 2, options, optionValues, PCONFIG(0)); + const __FlashStringHelper *options[] = { F("Normal"), F("ABC disabled") }; + const int optionValues[] = { P049_ABC_enabled, P049_ABC_disabled }; + FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + selector.addFormSelector(F("Auto Base Calibration"), F("abcdisable"), PCONFIG(0)); } { const __FlashStringHelper *filteroptions[5] = @@ -105,7 +106,8 @@ boolean Plugin_049(uint8_t function, struct EventStruct *event, String& string) PLUGIN_049_FILTER_FAST, PLUGIN_049_FILTER_MEDIUM, PLUGIN_049_FILTER_SLOW }; - addFormSelector(F("Filter"), F("filter"), 5, filteroptions, filteroptionValues, PCONFIG(1)); + FormSelectorOptions selector(NR_ELEMENTS(filteroptions), filteroptions, filteroptionValues); + selector.addFormSelector(F("Filter"), F("filter"), PCONFIG(1)); } P049_html_show_stats(event); diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index d4f6a11393..b4ef88e28d 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -114,12 +114,12 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *optionsMode[] = { - F("2.4 ms"), - F("24 ms"), - F("50 ms"), - F("101 ms"), - F("154 ms"), - F("700 ms"), + F("2.4"), + F("24"), + F("50"), + F("101"), + F("154"), + F("700"), }; const int optionValuesMode[] = { TCS34725_INTEGRATIONTIME_2_4MS, @@ -129,7 +129,9 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) TCS34725_INTEGRATIONTIME_154MS, TCS34725_INTEGRATIONTIME_700MS, }; - addFormSelector(F("Integration Time"), F("inttime"), 6, optionsMode, optionValuesMode, PCONFIG(0)); + FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode, optionValuesMode); + selector.addFormSelector(F("Integration Time"), F("inttime"), PCONFIG(0)); + addUnit(F("ms")); } { @@ -145,7 +147,8 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) TCS34725_GAIN_16X, TCS34725_GAIN_60X, }; - addFormSelector(F("Gain"), F("gain"), 4, optionsMode2, optionValuesMode2, PCONFIG(1)); + FormSelectorOptions selector(NR_ELEMENTS(optionsMode2), optionsMode2, optionValuesMode2); + selector.addFormSelector(F("Gain"), F("gain"), PCONFIG(1)); } addFormSubHeader(F("Output settings")); @@ -163,7 +166,8 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) // const int optionValuesRGB[P050_RGB_OPTIONS] = { 0, 1, 2, 3, 4, 5 }; constexpr size_t valueCount = NR_ELEMENTS(optionsRGB); - addFormSelector(F("Output RGB Values"), F("outputrgb"), valueCount, optionsRGB, nullptr, PCONFIG(2)); + FormSelectorOptions selector(valueCount, optionsRGB); + selector.addFormSelector(F("Output RGB Values"), F("outputrgb"), PCONFIG(2)); # ifndef LIMIT_BUILD_SIZE addFormNote(F("For 'normalized' or 'transformed' options, the Red/Green/Blue Decimals should best be increased.")); # endif // ifndef LIMIT_BUILD_SIZE @@ -190,7 +194,8 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) // const int optionValuesOutput[P050_VALUE4_OPTIONS] = { 0, 1, 2, 3 }; constexpr size_t valueCount = NR_ELEMENTS(optionsOutput); - addFormSelector(F("Output at Values #4"), F("output4"), valueCount, optionsOutput, nullptr, PCONFIG(3)); + FormSelectorOptions selector(valueCount, optionsOutput); + selector.addFormSelector(F("Output at Values #4"), F("output4"), PCONFIG(3)); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Optionally adjust Values #4 name accordingly.")); # endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/_P052_SenseAir.ino b/src/_P052_SenseAir.ino index ba018e1409..c022ad6470 100644 --- a/src/_P052_SenseAir.ino +++ b/src/_P052_SenseAir.ino @@ -223,8 +223,9 @@ boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) if (P052_data->readHoldingRegister(P052_HR11_MEASUREMENT_MODE, value)) { // Disable selector for now, since single measurement not yet supported. - const __FlashStringHelper *options[2] = { F("Continuous"), F("Single Measurement") }; - addFormSelector(F("Measurement Mode"), F("mode"), 2, options, nullptr, value); + const __FlashStringHelper *options[] = { F("Continuous"), F("Single Measurement") }; + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(F("Measurement Mode"), F("mode"), value); } */ @@ -246,10 +247,10 @@ boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) // ABC functionality disabled for now, due to a bug in the firmware. // See https://github.com/letscontrolit/ESPEasy/issues/759 uint8_t choiceABCperiod = PCONFIG(4); - const __FlashStringHelper * optionsABCperiod[9] = { F("disable"), F("1 h"), F("12 h"), F("1 + const __FlashStringHelper * optionsABCperiod[] = { F("disable"), F("1 h"), F("12 h"), F("1 day"), F("2 days"), F("4 days"), F("7 days"), F("14 days"), F("30 days") }; - addFormSelector(F("ABC period"), F("ABC_period"), 9, optionsABCperiod, - nullptr, choiceABCperiod); + FormSelectorOptions selector(NR_ELEMENTS(optionsABCperiod), optionsABCperiod); + selector.addFormSelector(F("ABC period"), F("ABC_period"), choiceABCperiod); */ diff --git a/src/_P053_PMSx003.ino b/src/_P053_PMSx003.ino index dc7abfe18b..0b168edbd7 100644 --- a/src/_P053_PMSx003.ino +++ b/src/_P053_PMSx003.ino @@ -224,14 +224,16 @@ boolean Plugin_053(uint8_t function, struct EventStruct *event, String& string) static_cast(PMSx003_output_selection::PM2_5_TempHum_Formaldehyde), static_cast(PMSx003_output_selection::ParticlesCount_100ml_cnt0_3__cnt_2_5), static_cast(PMSx003_output_selection::ParticlesCount_100ml_cnt1_0_cnt2_5_cnt10) }; - addFormSelector(F("Output values"), - F("output"), - NR_ELEMENTS(outputOptionValues), - outputOptions, - outputOptionValues, - PLUGIN_053_OUTPUT_SELECTOR, - true); - addFormNote(F("Changing this reloads the page and updates task value names + nr decimals.")); + + FormSelectorOptions selector( + NR_ELEMENTS(outputOptionValues), + outputOptions, + outputOptionValues); + selector.reloadonchange = true; + selector.addFormSelector( + F("Output values"), + F("output"), + PLUGIN_053_OUTPUT_SELECTOR); } { const __FlashStringHelper *eventOptions[] = { @@ -244,12 +246,14 @@ boolean Plugin_053(uint8_t function, struct EventStruct *event, String& string) static_cast(PMSx003_event_datatype::Event_PMxx_TempHum_Formaldehyde), static_cast(PMSx003_event_datatype::Event_All_count_bins), static_cast(PMSx003_event_datatype::Event_All) }; - addFormSelector(F("Events for non-output values"), - F("events"), - NR_ELEMENTS(eventOptionValues), - eventOptions, - eventOptionValues, - PLUGIN_053_EVENT_OUT_SELECTOR); + FormSelectorOptions selector( + NR_ELEMENTS(eventOptionValues), + eventOptions, + eventOptionValues); + selector.addFormSelector( + F("Events for non-output values"), + F("events"), + PLUGIN_053_EVENT_OUT_SELECTOR); addFormNote(F("Only generates the 'missing' events, " "(taskname#temp/humi/hcho, " "taskname#pm1.0/pm2.5/pm10, " diff --git a/src/_P057_HT16K33_LED.ino b/src/_P057_HT16K33_LED.ino index 4500296195..a2ec10579c 100644 --- a/src/_P057_HT16K33_LED.ino +++ b/src/_P057_HT16K33_LED.ino @@ -125,8 +125,9 @@ boolean Plugin_057(uint8_t function, struct EventStruct *event, String& string) addFormSubHeader(F("7-Seg. Clock")); { - const __FlashStringHelper *options[3] = { F("none"), F("7-Seg. HH:MM (24 hour)"), F("7-Seg. HH:MM (12 hour)") }; - addFormSelector(F("Clock Type"), F("clocktype"), 3, options, nullptr, PCONFIG(1)); + const __FlashStringHelper *options[] = { F("none"), F("7-Seg. HH:MM (24 hour)"), F("7-Seg. HH:MM (12 hour)") }; + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addFormSelector(F("Clock Type"), F("clocktype"), PCONFIG(1)); } addFormNumericBox(F("Seg. for Xx:xx"), F("csh10"), PCONFIG(2), 0, 7); diff --git a/src/_P059_Encoder.ino b/src/_P059_Encoder.ino index c2d3bb35f9..938601f8f7 100644 --- a/src/_P059_Encoder.ino +++ b/src/_P059_Encoder.ino @@ -79,7 +79,8 @@ boolean Plugin_059(uint8_t function, struct EventStruct *event, String& string) { const int optionValues[] = { 1, 2, 4 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Mode"), F("mode"), optionCount, optionValues, PCONFIG(0)); + FormSelectorOptions selector(optionCount, optionValues); + selector.addFormSelector(F("Mode"), F("mode"), PCONFIG(0)); addUnit(F("pulses per cycle")); } diff --git a/src/_P061_KeyPad.ino b/src/_P061_KeyPad.ino index 541366ba13..5ce7f9390d 100644 --- a/src/_P061_KeyPad.ino +++ b/src/_P061_KeyPad.ino @@ -154,7 +154,8 @@ boolean Plugin_061(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P061_ENABLE_PCF8575 }; constexpr int optionsCount = NR_ELEMENTS(options); - addFormSelector(F("Chip (Mode)"), F("chip"), optionsCount, options, nullptr, P061_CONFIG_KEYPAD_TYPE); + FormSelectorOptions selector(optionsCount, options); + selector.addFormSelector(F("Chip (Mode)"), F("chip"), P061_CONFIG_KEYPAD_TYPE); success = true; break; diff --git a/src/_P062_MPR121_KeyPad.ino b/src/_P062_MPR121_KeyPad.ino index 8de0cffbb5..8769f94650 100644 --- a/src/_P062_MPR121_KeyPad.ino +++ b/src/_P062_MPR121_KeyPad.ino @@ -124,7 +124,8 @@ boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string) MPR212_EXTRA_SENSITIVITY }; constexpr size_t optionCount = NR_ELEMENTS(sensitivityValues); - addFormSelector(F("Panel sensitivity"), F("psens"), optionCount, sensitivityOptions, sensitivityValues, PCONFIG(4)); + FormSelectorOptions selector(optionCount, sensitivityOptions, sensitivityValues); + selector.addFormSelector(F("Panel sensitivity"), F("psens"), PCONFIG(4)); } { bool canCalibrate = true; diff --git a/src/_P064_APDS9960.ino b/src/_P064_APDS9960.ino index 61d0fc33c5..c7b064caee 100644 --- a/src/_P064_APDS9960.ino +++ b/src/_P064_APDS9960.ino @@ -129,7 +129,9 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) F("R/G/B Colors") }; const int optionsPluginModeValues[] = { PLUGIN_MODE_GPL_064, PLUGIN_MODE_RGB_064 }; constexpr size_t optionCount = NR_ELEMENTS(optionsPluginModeValues); - addFormSelector(F("Plugin Mode"), F("mode"), optionCount, optionsPluginMode, optionsPluginModeValues, P064_MODE, true); + FormSelectorOptions selector(optionCount, optionsPluginMode, optionsPluginModeValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Plugin Mode"), F("mode"), P064_MODE); # ifndef BUILD_NO_DEBUG addFormNote(F("After changing Plugin Mode you may want to change the Values names, below.")); # endif // ifndef BUILD_NO_DEBUG @@ -175,10 +177,10 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) // Led_Drive options, all Led_Drive optionsets in SparkFun_APDS9960.h have the same valueset, so only defined once here const __FlashStringHelper *optionsLedDrive[] = { - F("100 mA (default)"), - F("50 mA"), - F("25 mA"), - F("12.5 mA") }; + F("100 (default)"), + F("50"), + F("25"), + F("12.5") }; const int optionsLedDriveValues[] = { LED_DRIVE_100MA, LED_DRIVE_50MA, LED_DRIVE_25MA, LED_DRIVE_12_5MA }; constexpr size_t optionsLedDriveCount = NR_ELEMENTS(optionsLedDriveValues); @@ -188,20 +190,27 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) if (P064_IS_GPL_SENSOR) { // Gesture/Proximity/ALS mode addFormSubHeader(F("Gesture parameters")); - - addFormSelector(F("Gesture Gain"), - F("ggain"), - optionsGainCount, - optionsGain, - optionsGainValues, - P064_GGAIN); - - addFormSelector(F("Gesture LED Drive"), - F("gldrive"), - optionsLedDriveCount, - optionsLedDrive, - optionsLedDriveValues, - P064_GLDRIVE); + { + FormSelectorOptions selector( + optionsGainCount, + optionsGain, + optionsGainValues); + selector.addFormSelector( + F("Gesture Gain"), + F("ggain"), + P064_GGAIN); + } + { + FormSelectorOptions selector( + optionsLedDriveCount, + optionsLedDrive, + optionsLedDriveValues); + selector.addFormSelector( + F("Gesture LED Drive"), + F("gldrive"), + P064_GLDRIVE); + addUnit(F("mA")); + } { // Gesture Led-boost values const __FlashStringHelper *optionsLedBoost[] = { @@ -211,17 +220,21 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) F("300 % (default)") }; const int optionsLedBoostValues[] = { LED_BOOST_100, LED_BOOST_150, LED_BOOST_200, LED_BOOST_300 }; constexpr size_t optionsLedBoostCount = NR_ELEMENTS(optionsLedBoostValues); - addFormSelector(F("Gesture LED Boost"), - F("lboost"), - optionsLedBoostCount, - optionsLedBoost, - optionsLedBoostValues, - P064_LED_BOOST); + FormSelectorOptions selector( + optionsLedBoostCount, + optionsLedBoost, + optionsLedBoostValues); + selector.addFormSelector( + F("Gesture LED Boost"), + F("lboost"), + P064_LED_BOOST); } addFormSubHeader(F("Proximity & Ambient Light Sensor parameters")); - - addFormSelector(F("Proximity Gain"), F("pgain"), optionsGainCount, optionsGain, optionsGainValues, P064_PGAIN); + { + FormSelectorOptions selector(optionsGainCount, optionsGain, optionsGainValues); + selector.addFormSelector(F("Proximity Gain"), F("pgain"), P064_PGAIN); + } lightSensorGainLabel = F("Ambient Light Sensor Gain"); lightSensorDriveLabel = F("Proximity & ALS LED Drive"); @@ -238,14 +251,20 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) F("4x (default)"), F("16x"), F("64x") }; - addFormSelector(lightSensorGainLabel, F("again"), optionsGainCount, optionsALSGain, optionsGainValues, P064_AGAIN); + FormSelectorOptions selector(optionsGainCount, optionsALSGain, optionsGainValues); + selector.addFormSelector(lightSensorGainLabel, F("again"), P064_AGAIN); + } + { + FormSelectorOptions selector( + optionsLedDriveCount, + optionsLedDrive, + optionsLedDriveValues); + selector.addFormSelector( + lightSensorDriveLabel, + F("ldrive"), + P064_LDRIVE); + addUnit(F("mA")); } - addFormSelector(lightSensorDriveLabel, - F("ldrive"), - optionsLedDriveCount, - optionsLedDrive, - optionsLedDriveValues, - P064_LDRIVE); } addFormSubHeader(F("Event generation")); diff --git a/src/_P066_VEML6040.ino b/src/_P066_VEML6040.ino index 082a73bf8f..e5fc0f3864 100644 --- a/src/_P066_VEML6040.ino +++ b/src/_P066_VEML6040.ino @@ -93,7 +93,8 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) F("1280ms (515)"), }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - addFormSelector(F("Integration Time (Max Lux)"), F("itime"), optionCount, optionsMode, nullptr, PCONFIG(1)); + FormSelectorOptions selector(optionCount, optionsMode); + selector.addFormSelector(F("Integration Time (Max Lux)"), F("itime"), PCONFIG(1)); } { @@ -106,7 +107,8 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) F("Color Temperature [K], Ambient Light [Lux], Y, W"), }; constexpr size_t optionCount = NR_ELEMENTS(optionsVarMap); - addFormSelector(F("Value Mapping"), F("map"), optionCount, optionsVarMap, nullptr, PCONFIG(2)); + FormSelectorOptions selector(optionCount, optionsVarMap); + selector.addFormSelector(F("Value Mapping"), F("map"), PCONFIG(2)); } success = true; diff --git a/src/_P067_HX711_Load_Cell.ino b/src/_P067_HX711_Load_Cell.ino index 1321ec4f44..cb82720ea9 100644 --- a/src/_P067_HX711_Load_Cell.ino +++ b/src/_P067_HX711_Load_Cell.ino @@ -86,7 +86,8 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsModeChanA[] = { F("Off"), F("Gain 64"), F("Gain 128") }; - addFormSelector(F("Mode"), F("modeChA"), 3, optionsModeChanA, nullptr, P067_GET_CHANNEL_A_MODE); + FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanA), optionsModeChanA); + selector.addFormSelector(F("Mode"), F("modeChA"), P067_GET_CHANNEL_A_MODE); } P067_int2float(P067_OFFSET_CHANNEL_A_1, P067_OFFSET_CHANNEL_A_2, &valFloat); @@ -101,7 +102,8 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsModeChanB[] = { F("Off"), F("Gain 32") }; - addFormSelector(F("Mode"), F("modeChB"), 2, optionsModeChanB, nullptr, P067_GET_CHANNEL_B_MODE); + FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanB), optionsModeChanB); + selector.addFormSelector(F("Mode"), F("modeChB"), P067_GET_CHANNEL_B_MODE); } P067_int2float(P067_OFFSET_CHANNEL_B_1, P067_OFFSET_CHANNEL_B_2, &valFloat); diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index b96abca7c5..8fb5735b23 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -119,7 +119,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("TM1637 - 6 digit"), F("MAX7219 - 8 digit") }; constexpr size_t optionCount = NR_ELEMENTS(displtype); - addFormSelector(F("Display Type"), F("displtype"), optionCount, displtype, nullptr, PCONFIG(0)); + FormSelectorOptions selector(optionCount, displtype); + selector.addFormSelector(F("Display Type"), F("displtype"), PCONFIG(0)); } { const __FlashStringHelper *displout[] = { F("Manual"), @@ -129,7 +130,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("Clock 12h - No Blink"), F("Date") }; constexpr size_t optionCount = NR_ELEMENTS(displout); - addFormSelector(F("Display Output"), F("displout"), optionCount, displout, nullptr, PCONFIG(1)); + FormSelectorOptions selector(optionCount, displout); + selector.addFormSelector(F("Display Output"), F("displout"), PCONFIG(1)); } addFormNumericBox(F("Brightness"), F("brightness"), PCONFIG(2), 0, 15); @@ -142,7 +144,8 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7") }; constexpr size_t optionCount = NR_ELEMENTS(fontset); - addFormSelector(F("Font set"), F("fontset"), optionCount, fontset, nullptr, PCONFIG(4)); + FormSelectorOptions selector(optionCount, fontset); + selector.addFormSelector(F("Font set"), F("fontset"), PCONFIG(4)); addFormNote(F("Check documentation for examples of the font sets.")); } # endif // P073_EXTRA_FONTS diff --git a/src/_P074_TSL2591.ino b/src/_P074_TSL2591.ino index ef373e10c4..45d25b485b 100644 --- a/src/_P074_TSL2591.ino +++ b/src/_P074_TSL2591.ino @@ -94,11 +94,11 @@ boolean Plugin_074(uint8_t function, struct EventStruct *event, String& string) // integration time (dim light) // } { - const __FlashStringHelper *optionsMode[6] = { F("100"), F("200"), F("300"), + const __FlashStringHelper *optionsMode[] = { F("100"), F("200"), F("300"), F("400"), F("500"), F("600") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - addFormSelector(F("Integration Time"), F("itime"), optionCount, optionsMode, - nullptr, PCONFIG(1)); + FormSelectorOptions selector( optionCount, optionsMode); + selector.addFormSelector(F("Integration Time"), F("itime"), PCONFIG(1)); addUnit(F("ms")); } @@ -107,11 +107,11 @@ boolean Plugin_074(uint8_t function, struct EventStruct *event, String& string) // TSL2591_GAIN_HIGH = 0x20, // medium gain (428x) // TSL2591_GAIN_MAX = 0x30, // max gain (9876x) { - const __FlashStringHelper *optionsGain[4] = { F("low gain (1x)"), F("medium gain (25x)"), + const __FlashStringHelper *optionsGain[] = { F("low gain (1x)"), F("medium gain (25x)"), F("medium gain (428x)"), F("max gain (9876x)") }; constexpr size_t optionCount = NR_ELEMENTS(optionsGain); - addFormSelector(F("Value Mapping"), F("gain"), optionCount, optionsGain, nullptr, - PCONFIG(2)); + FormSelectorOptions selector( optionCount, optionsGain); + selector.addFormSelector(F("Value Mapping"), F("gain"), PCONFIG(2)); } success = true; diff --git a/src/_P075_Nextion.ino b/src/_P075_Nextion.ino index f1e3789786..9e43dd28ab 100644 --- a/src/_P075_Nextion.ino +++ b/src/_P075_Nextion.ino @@ -97,7 +97,8 @@ boolean Plugin_075(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(options); - addFormSelector(F("Baud Rate"), F("baud"), optionCount, options, nullptr, P075_BaudRate); + FormSelectorOptions selector(optionCount, options); + selector.addFormSelector(F("Baud Rate"), F("baud"), P075_BaudRate); addUnit(F("baud")); break; } diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index 938e5a05c1..5d71fdd228 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -333,38 +333,13 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) #endif // ifdef ESP32 }; constexpr int nrElements = NR_ELEMENTS(predefinedId); - addFormSelector(F("Device"), - F("preDefDevSel"), nrElements, - predefinedNames, predefinedId, devicePinSettings); + FormSelectorOptions selector(nrElements, predefinedNames, predefinedId); + selector.addFormSelector(F("Device"), F("preDefDevSel"), devicePinSettings); addFormNote(F("Enable device and select device type first")); } { // Place this in a scope, to keep memory usage low. - const __FlashStringHelper *modeRaise[] = { - F("LOW"), - F("CHANGE"), - F("RISING"), - F("FALLING"), - }; - - const int modeValues[] = { - LOW, - CHANGE, - RISING, - FALLING, - }; - - const __FlashStringHelper *modeCurr[] = { - F("LOW"), - F("HIGH"), - }; - - const int modeCurrValues[] = { - LOW, - HIGH, - }; - uint8_t currentRead = P076_SEL_CUR_READ; if ((currentRead != LOW) && (currentRead != HIGH)) @@ -372,12 +347,39 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) currentRead = LOW; } addFormSubHeader(F("Custom Pin settings (choose Custom above)")); - addFormSelector(F("SEL Current (A) Reading"), F("curr_read"), 2, - modeCurr, modeCurrValues, currentRead); - addFormSelector(F("CF1 Interrupt Edge"), F("cf1_edge"), 4, - modeRaise, modeValues, P076_CF1_TRIGGER); - addFormSelector(F("CF Interrupt Edge"), F("cf_edge"), 4, - modeRaise, modeValues, P076_CF_TRIGGER); + { + const __FlashStringHelper *modeCurr[] = { + F("LOW"), + F("HIGH"), + }; + + const int modeCurrValues[] = { + LOW, + HIGH, + }; + + FormSelectorOptions selector(NR_ELEMENTS(modeCurr), modeCurr, modeCurrValues); + selector.addFormSelector(F("SEL Current (A) Reading"), F("curr_read"), currentRead); + } + { + const __FlashStringHelper *modeRaise[] = { + F("LOW"), + F("CHANGE"), + F("RISING"), + F("FALLING"), + }; + + const int modeValues[] = { + LOW, + CHANGE, + RISING, + FALLING, + }; + + FormSelectorOptions selector(NR_ELEMENTS(modeRaise), modeRaise, modeValues); + selector.addFormSelector(F("CF1 Interrupt Edge"), F("cf1_edge"), P076_CF1_TRIGGER); + selector.addFormSelector(F("CF Interrupt Edge"), F("cf_edge"), P076_CF_TRIGGER); + } } ESPEASY_RULES_FLOAT_TYPE current, voltage, power; diff --git a/src/_P078_Eastron.ino b/src/_P078_Eastron.ino index 4784d825dc..39e5b44326 100644 --- a/src/_P078_Eastron.ino +++ b/src/_P078_Eastron.ino @@ -120,7 +120,8 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) options_baudrate[i] = p078_storageValueToBaudrate(i); } constexpr size_t optionCount = NR_ELEMENTS(options_baudrate); - addFormSelector(F("Baud Rate"), P078_BAUDRATE_LABEL, optionCount, options_baudrate, nullptr, P078_BAUDRATE); + FormSelectorOptions selector(optionCount, options_baudrate); + selector.addFormSelector(F("Baud Rate"), P078_BAUDRATE_LABEL, P078_BAUDRATE); addUnit(F("baud")); } @@ -173,7 +174,8 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) F("SDM320C") }; constexpr size_t nrOptions = NR_ELEMENTS(options_model); - addFormSelector(F("Model Type"), P078_MODEL_LABEL, nrOptions, options_model, nullptr, P078_MODEL); + FormSelectorOptions selector(nrOptions, options_model); + selector.addFormSelector(F("Model Type"), P078_MODEL_LABEL, P078_MODEL); addFormNote(F("Submit after changing the modell to update Output Configuration.")); } success = true; diff --git a/src/_P079_Wemos_Motorshield.ino b/src/_P079_Wemos_Motorshield.ino index f79e967c9e..ce18aab139 100644 --- a/src/_P079_Wemos_Motorshield.ino +++ b/src/_P079_Wemos_Motorshield.ino @@ -125,7 +125,8 @@ boolean Plugin_079(uint8_t function, struct EventStruct *event, String& string) static_cast(P079_BoardType::LolinMotorshield) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - addFormSelector(F("Motor Shield Type"), F("shield_type"), optionCount, options, indices, SHIELD_VER_PCFG_P079); + FormSelectorOptions selector(optionCount, options, indices); + selector.addFormSelector(F("Motor Shield Type"), F("shield_type"), SHIELD_VER_PCFG_P079); } if (Plugin_079_MotorShield_type == P079_BoardType::WemosMotorshield) { diff --git a/src/_P082_GPS.ino b/src/_P082_GPS.ino index 41aa8e2863..62e8b55672 100644 --- a/src/_P082_GPS.ino +++ b/src/_P082_GPS.ino @@ -191,7 +191,8 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) static_cast(P082_PowerMode::Eco) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - addFormSelector(F("Power Mode"), F("pwrmode"), optionCount, options, indices, P082_POWER_MODE); + FormSelectorOptions selector(optionCount, options, indices); + selector.addFormSelector(F("Power Mode"), F("pwrmode"), P082_POWER_MODE); } { @@ -220,7 +221,8 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) static_cast(P082_DynamicModel::Bike) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - addFormSelector(F("Dynamic Platform Model"), F("dynmodel"), optionCount, options, indices, P082_DYNAMIC_MODEL); + FormSelectorOptions selector(optionCount, options, indices); + selector.addFormSelector(F("Dynamic Platform Model"), F("dynmodel"), P082_DYNAMIC_MODEL); } # endif // P082_USE_U_BLOX_SPECIFIC diff --git a/src/_P084_VEML6070.ino b/src/_P084_VEML6070.ino index 02334ad44f..297b864a93 100644 --- a/src/_P084_VEML6070.ino +++ b/src/_P084_VEML6070.ino @@ -88,7 +88,8 @@ boolean Plugin_084(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsMode[] = { F("1/2T"), F("1T"), F("2T"), F("4T (Default)") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - addFormSelector(F("Refresh Time Determination"), F("itime"), optionCount, optionsMode, nullptr, PCONFIG(0)); + FormSelectorOptions selector(optionCount, optionsMode); + selector.addFormSelector(F("Refresh Time Determination"), F("itime"), PCONFIG(0)); success = true; break; diff --git a/src/_P085_AcuDC243.ino b/src/_P085_AcuDC243.ino index e542bfc9c3..c2fb29ca1e 100644 --- a/src/_P085_AcuDC243.ino +++ b/src/_P085_AcuDC243.ino @@ -97,7 +97,8 @@ boolean Plugin_085(uint8_t function, struct EventStruct *event, String& string) for (int i = 0; i < 6; ++i) { options_baudrate[i] = String(p085_storageValueToBaudrate(i)); } - addFormSelector(F("Baud Rate"), P085_BAUDRATE_LABEL, 6, options_baudrate, nullptr, P085_BAUDRATE); + FormSelectorOptions selector(6, options_baudrate); + selector.addFormSelector(F("Baud Rate"), P085_BAUDRATE_LABEL, P085_BAUDRATE); addUnit(F("baud")); addFormNumericBox(F("Modbus Address"), P085_DEV_ID_LABEL, P085_DEV_ID, 1, 247); break; diff --git a/src/_P086_Homie.ino b/src/_P086_Homie.ino index 5fbef766f2..1ef8893203 100644 --- a/src/_P086_Homie.ino +++ b/src/_P086_Homie.ino @@ -98,8 +98,9 @@ boolean Plugin_086(uint8_t function, struct EventStruct *event, String& string) addFormTextBox(F("Event Name"), getPluginCustomArgName((i * 10) + 0), Cache.getTaskDeviceValueName(event->TaskIndex, i), NAME_FORMULA_LENGTH_MAX); - addFormSelector(F("Parameter Type"), getPluginCustomArgName((i * 10) + 1), - PLUGIN_086_VALUE_TYPES, options, optionValues, PCONFIG(i)); + + FormSelectorOptions selector(PLUGIN_086_VALUE_TYPES, options, optionValues); + selector.addFormSelector(F("Parameter Type"), getPluginCustomArgName((i * 10) + 1), PCONFIG(i)); addFormNumericBox(F("Min"), getPluginCustomArgName((i * 10) + 2), Cache.getTaskDevicePluginConfig(event->TaskIndex, i)); diff --git a/src/_P087_SerialProxy.ino b/src/_P087_SerialProxy.ino index 01aca2edd0..9a1552dc8f 100644 --- a/src/_P087_SerialProxy.ino +++ b/src/_P087_SerialProxy.ino @@ -348,13 +348,14 @@ void P087_html_show_matchForms(struct EventStruct *event) { optionValues[i] = matchType; } P087_Match_Type choice = P087_data->getMatchType(); - addFormSelector(F("Match Type"), - getPluginCustomArgName(P087_MATCH_TYPE_POS), - P087_Match_Type_NR_ELEMENTS, - options, - optionValues, - choice, - false); + FormSelectorOptions selector( + P087_Match_Type_NR_ELEMENTS, + options, + optionValues); + selector.addFormSelector( + F("Match Type"), + getPluginCustomArgName(P087_MATCH_TYPE_POS), + choice); addFormNote(F("Capture filter can only be used on Global Match")); } diff --git a/src/_P090_CCS811.ino b/src/_P090_CCS811.ino index b234f51586..71cd586f6d 100644 --- a/src/_P090_CCS811.ino +++ b/src/_P090_CCS811.ino @@ -111,7 +111,8 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { const __FlashStringHelper *options[] = { F("0x5A (ADDR pin is LOW)"), F("0x5B (ADDR pin is HIGH)") }; constexpr size_t optionCount = NR_ELEMENTS(options); - addFormSelector(F("I2C Address"), F("i2c_addr"), optionCount, options, i2cAddressValues, P090_I2C_ADDR); + FormSelectorOptions selector(optionCount, options, i2cAddressValues); + selector.addFormSelector(F("I2C Address"), F("i2c_addr"), P090_I2C_ADDR); } else { success = intArrayContains(2, i2cAddressValues, event->Par1); } @@ -135,7 +136,8 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *frequencyOptions[] = { F("1 second"), F("10 seconds"), F("60 seconds") }; const int frequencyValues[] = { 1, 2, 3 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - addFormSelector(F("Take reading every"), F("temp_freq"), optionCount, frequencyOptions, frequencyValues, frequencyChoice); + FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + selector.addFormSelector(F("Take reading every"), F("temp_freq"), frequencyChoice); } addFormSeparator(2); diff --git a/src/_P091_SerSwitch.ino b/src/_P091_SerSwitch.ino index 3d71f8cd50..1b08885c76 100644 --- a/src/_P091_SerSwitch.ino +++ b/src/_P091_SerSwitch.ino @@ -125,7 +125,8 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues[] = { SER_SWITCH_YEWE, SER_SWITCH_SONOFFDUAL, SER_SWITCH_LCTECH, SER_SWITCH_WIFIDIMMER }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Switch Type"), F("type"), optionCount, options, optionValues, PCONFIG(0)); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Switch Type"), F("type"), PCONFIG(0)); } if (PCONFIG(0) == SER_SWITCH_YEWE) @@ -138,7 +139,8 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) }; const int buttonoptionValues[] = { 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(buttonoptionValues); - addFormSelector(F("Number of relays"), F("button"), optionCount, buttonOptions, buttonoptionValues, PCONFIG(1)); + FormSelectorOptions selector(optionCount, buttonOptions, buttonoptionValues); + selector.addFormSelector(F("Number of relays"), F("button"), PCONFIG(1)); } if (PCONFIG(0) == SER_SWITCH_SONOFFDUAL) @@ -149,7 +151,8 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) F("Simultaneous mode"), }; constexpr size_t optionCount = NR_ELEMENTS(modeoptions); - addFormSelector(F("Relay working mode"), F("mode"), optionCount, modeoptions, nullptr, PCONFIG(1)); + FormSelectorOptions selector(optionCount, modeoptions); + selector.addFormSelector(F("Relay working mode"), F("mode"), PCONFIG(1)); } if (PCONFIG(0) == SER_SWITCH_LCTECH) @@ -157,7 +160,8 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) { const int buttonoptionValues[] = { 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(buttonoptionValues); - addFormSelector(F("Number of relays"), F("button"), optionCount, buttonoptionValues, PCONFIG(1)); + FormSelectorOptions selector(optionCount, buttonoptionValues); + selector.addFormSelector(F("Number of relays"), F("button"), PCONFIG(1)); } { @@ -172,7 +176,9 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) F("57600"), }; constexpr size_t optionCount = NR_ELEMENTS(speedOptions); - addFormSelector(F("Serial speed"), F("speed"), optionCount, speedOptions, nullptr, PCONFIG(2)); + FormSelectorOptions selector(optionCount, speedOptions); + selector.addFormSelector(F("Serial speed"), F("speed"), PCONFIG(2)); + addUnit(F("baud")); } addFormCheckBox(F("Use command doubling"), F("dbl"), PCONFIG(3)); diff --git a/src/_P092_DLbus.ino b/src/_P092_DLbus.ino index aa911f85a1..b1e4cf6e3e 100644 --- a/src/_P092_DLbus.ino +++ b/src/_P092_DLbus.ino @@ -138,7 +138,8 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string) static_cast(eP092pinmode::ePPM_Input), static_cast(eP092pinmode::ePPM_InputPullUp) }; - addFormSelector(F("Pin mode"), F("ppinmode"), 2, options, optionValues, choice); + FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + selector.addFormSelector(F("Pin mode"), F("ppinmode"), choice); } { const __FlashStringHelper *Devices[] = { @@ -151,7 +152,9 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string) const int DevTypes[] = { 21, 31, 42, 1611, 6132, 6133 }; constexpr size_t optionCount = NR_ELEMENTS(Devices); - addFormSelector(F("DL-Bus Type"), F("pdlbtype"), optionCount, Devices, DevTypes, nullptr, PCONFIG(0), true); + FormSelectorOptions selector(optionCount, Devices, DevTypes); + selector.reloadonchange = true; + selector.addFormSelector(F("DL-Bus Type"), F("pdlbtype"), PCONFIG(0)); } { int P092_ValueType, P092_ValueIdx; @@ -236,14 +239,9 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string) P092_ValueType = PCONFIG(1) >> 8; P092_ValueIdx = PCONFIG(1) & 0x00FF; - addFormSelector(plugin_092_DefValueName, - F("pValue"), - optionCount, - Options, - P092_OptionTypes, - nullptr, - P092_ValueType, - true); + FormSelectorOptions selector(optionCount, Options, P092_OptionTypes); + selector.reloadonchange = true; + selector.addFormSelector(plugin_092_DefValueName, F("pValue"), P092_ValueType); if (P092_MaxIdx[P092_ValueType] > 1) { int CurIdx = P092_ValueIdx; diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index bb0a7e148b..d8b9124367 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -261,12 +261,13 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) # endif // if P095_ENABLE_ILI948X }; constexpr size_t optionCount = NR_ELEMENTS(hardwareOptions); - addFormSelector(F("TFT display model"), - F("dsptype"), - optionCount, + FormSelectorOptions selector(optionCount, hardwareTypes, - hardwareOptions, - P095_CONFIG_FLAG_GET_TYPE); + hardwareOptions); + selector.addFormSelector( + F("TFT display model"), + F("dsptype"), + P095_CONFIG_FLAG_GET_TYPE); } addFormCheckBox(F("Invert display"), F("invert"), P095_CONFIG_FLAG_GET_INVERTDISPLAY); @@ -311,12 +312,10 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) # endif // if P095_ENABLE_ILI948X }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - addFormSelector(F("Write Command trigger"), - F("commandtrigger"), - optionCount, - commandTriggers, - commandTriggerOptions, - P095_CONFIG_FLAG_GET_CMD_TRIGGER); + FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + selector.addFormSelector( + F("Write Command trigger"), + F("commandtrigger"), P095_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Select the command that is used to handle commands for this display.")); # endif // ifndef LIMIT_BUILD_SIZE From bcea8ef9fa1e432ad731be1ef1fdf7b6439773cb Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Jan 2025 19:14:30 +0100 Subject: [PATCH 38/50] [Cleanup] Convert more code to use new FormSelectorOptions class --- src/_P096_eInk.ino | 28 +++++------ src/_P098_PWM_motor.ino | 3 +- src/_P099_XPT2046Touch.ino | 10 ++-- src/_P100_DS2423_counter.ino | 3 +- src/_P102_PZEM004Tv3.ino | 10 ++-- src/_P105_AHT.ino | 4 +- src/_P108_DDS238.ino | 3 +- src/_P109_ThermOLED.ino | 3 +- src/_P110_VL53L0X.ino | 6 ++- src/_P111_RC522_RFID.ino | 3 +- src/_P112_AS7265x.ino | 59 ++++++++++++++--------- src/_P113_VL53L1X.ino | 19 +++++--- src/_P114_VEML6075.ino | 17 ++++--- src/_P115_MAX1704x_v2.ino | 3 +- src/_P116_ST77xx.ino | 17 +++---- src/_P119_ITG3205_Gyro.ino | 3 +- src/_P122_SHT2x.ino | 3 +- src/_P123_I2CTouch.ino | 9 ++-- src/_P124_MultiRelay.ino | 6 ++- src/_P126_74HC595.ino | 3 +- src/_P129_74HC165.ino | 28 +++++------ src/_P131_NeoPixelMatrix.ino | 42 ++++++++-------- src/_P132_INA3221.ino | 35 +++++--------- src/_P133_LTR390.ino | 14 ++++-- src/_P135_SCD4x.ino | 4 +- src/_P137_AXP192.ino | 6 +-- src/_P141_PCD8544_Nokia5110.ino | 9 ++-- src/_P142_AS5600.ino | 40 ++++----------- src/_P143_I2C_Rotary.ino | 34 ++++--------- src/_P145_MQxxx.ino | 3 +- src/_P146_CacheControllerReader.ino | 3 +- src/_P147_SGP4x.ino | 4 +- src/_P148_POWRxxD_THR3xxD.ino | 4 +- src/_P150_TMP117.ino | 17 +++---- src/_P153_SHT4x.ino | 20 +++----- src/_P166_GP8403.ino | 8 +-- src/_P167_Vindstyrka.ino | 6 +-- src/src/DataTypes/FormSelectorOptions.cpp | 8 +-- src/src/DataTypes/FormSelectorOptions.h | 20 ++++---- 39 files changed, 247 insertions(+), 270 deletions(-) diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 534268c7e2..5503e6a158 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -273,11 +273,12 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) # endif // if P096_USE_WAVESHARE_2IN7 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - addFormSelector(F("eInk display model"), + FormSelectorOptions selector( + optionCount, + options4, + optionValues4); + selector.addFormSelector(F("eInk display model"), F("_type"), - optionCount, - options4, - optionValues4, P096_CONFIG_FLAG_GET_DISPLAYTYPE); } @@ -291,7 +292,8 @@ 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); - addFormSelector(F("Rotation"), F("_rotate"), optionCount, options2, optionValues2, P096_CONFIG_ROTATION); + FormSelectorOptions selector( optionCount, options2, optionValues2); + selector.addFormSelector(F("Rotation"), F("_rotate"), P096_CONFIG_ROTATION); } # endif // ifdef P096_USE_ADA_GRAPHICS @@ -338,12 +340,9 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) P096_CONFIG_FLAGS = lSettings; } constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions); - addFormSelector(F("Greyscale levels"), - F("_colorDepth"), - optionCount, - colorDepths, - colorDepthOptions, - P096_CONFIG_FLAG_GET_COLORDEPTH); + FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); + selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"), + P096_CONFIG_FLAG_GET_COLORDEPTH); } AdaGFXFormTextPrintMode(F("_mode"), P096_CONFIG_FLAG_GET_MODE); @@ -378,11 +377,8 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) # endif // if P096_USE_WAVESHARE_2IN7 }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - addFormSelector(F("Write Command trigger"), - F("_commandtrigger"), - optionCount, - commandTriggers, - commandTriggerOptions, + FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"), P096_CONFIG_FLAG_GET_CMD_TRIGGER); addFormNote(F("Select the command that is used to handle commands for this display.")); } diff --git a/src/_P098_PWM_motor.ino b/src/_P098_PWM_motor.ino index 4fb4a54fa8..9839027540 100644 --- a/src/_P098_PWM_motor.ino +++ b/src/_P098_PWM_motor.ino @@ -181,7 +181,8 @@ boolean Plugin_098(uint8_t function, struct EventStruct *event, String& string) options[i] = P098_config_struct::toString(static_cast(i)); optionValues[i] = i; } - addFormSelector(F("Motor Control"), F("motor_contr"), P098_PWM_MODE_TYPES, options, optionValues, P098_MOTOR_CONTROL); + FormSelectorOptions selector(P098_PWM_MODE_TYPES, options, optionValues); + selector.addFormSelector(F("Motor Control"), F("motor_contr"), P098_MOTOR_CONTROL); } addFormNumericBox(F("PWM Frequency"), F("pwm_freq"), P098_PWM_FREQ, 50, 100000); addUnit(F("Hz")); diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index cfe6f688de..8631c30dcc 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -128,7 +128,8 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) const int optionValues2[] = { 0, 1, 2, 3 }; // Rotation similar to the // TFT ILI9341 rotation constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - addFormSelector(F("Rotation"), F("protate"), optionCount, options2, optionValues2, choice2); + FormSelectorOptions selector(optionCount, options2, optionValues2); + selector.addFormSelector(F("Rotation"), F("protate"), choice2); } const bool bRotationFlipped = bitRead(P099_CONFIG_FLAGS, P099_FLAGS_ROTATION_FLIPPED); @@ -153,7 +154,8 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) F("Objectnames, X, Y and Z") }; const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! constexpr size_t optionCount = NR_ELEMENTS(optionValues3); - addFormSelector(F("Events"), F("pevents"), optionCount, options3, optionValues3, choice3); + FormSelectorOptions selector(optionCount, options3, optionValues3); + selector.addFormSelector(F("Events"), F("pevents"), choice3); } if (!Settings.UseRules) { @@ -215,7 +217,9 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options5[] = { F("None"), F("8"), F("16"), F("24"), F("32"), F("40") }; const int optionValues5[] = { -1, 8, 16, 24, 32, 40 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues5); - addFormSelector(F("# of objects"), F("pobjectcount"), optionCount, options5, optionValues5, choice5, true); + FormSelectorOptions selector(optionCount, options5, optionValues5); + selector.reloadonchange = true; + selector.addFormSelector(F("# of objects"), F("pobjectcount"), choice5); } } diff --git a/src/_P100_DS2423_counter.ino b/src/_P100_DS2423_counter.ino index 5ab8fa770e..46fd52091e 100644 --- a/src/_P100_DS2423_counter.ino +++ b/src/_P100_DS2423_counter.ino @@ -64,7 +64,8 @@ boolean Plugin_100(uint8_t function, struct EventStruct *event, String& string) // Counter select const __FlashStringHelper *resultsOptions[] = { F("A"), F("B") }; constexpr size_t optionCount = NR_ELEMENTS(resultsOptions); - addFormSelector(F("Counter"), F("counter"), optionCount, resultsOptions, nullptr, PCONFIG(0)); + FormSelectorOptions selector(optionCount, resultsOptions); + selector.addFormSelector(F("Counter"), F("counter"), PCONFIG(0)); addFormNote(F("Counter value is incremental")); } success = true; diff --git a/src/_P102_PZEM004Tv3.ino b/src/_P102_PZEM004Tv3.ino index bff9c8a47f..f1c5c518f2 100644 --- a/src/_P102_PZEM004Tv3.ino +++ b/src/_P102_PZEM004Tv3.ino @@ -150,7 +150,8 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_model[] = { F("Read_value"), F("Reset_Energy"), F("Program_adress") }; constexpr size_t optionCount = NR_ELEMENTS(options_model); - addFormSelector(F("PZEM Mode"), F("PZEM_mode"), optionCount, options_model, nullptr, P102_PZEM_mode); + FormSelectorOptions selector(optionCount, options_model); + selector.addFormSelector(F("PZEM Mode"), F("PZEM_mode"), P102_PZEM_mode); } if (P102_PZEM_mode == 2) @@ -160,8 +161,8 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_confirm[] = { F("NO"), F("YES") }; constexpr size_t optionCount = NR_ELEMENTS(options_confirm); - addFormSelector(F("Confirm address programming ?"), F("PZEM_addr_set"), optionCount, options_confirm, nullptr, - P102_PZEM_ADDR_SET); + FormSelectorOptions selector(optionCount, options_confirm); + selector.addFormSelector(F("Confirm address programming ?"), F("PZEM_addr_set"), P102_PZEM_ADDR_SET); } addFormNumericBox(F("Address of PZEM"), F("PZEM_addr"), (P102_PZEM_ADDR < 1) ? 1 : P102_PZEM_ADDR, 1, 247); addHtml(F("Select the address to set PZEM. Programming address 0 is forbidden.")); @@ -184,7 +185,8 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_model[] = { F("Read_value"), F("Reset_Energy") }; constexpr size_t optionCount = NR_ELEMENTS(options_model); - addFormSelector(F("PZEM Mode"), F("PZEM_mode"), optionCount, options_model, nullptr, P102_PZEM_mode); + FormSelectorOptions selector(optionCount, options_model); + selector.addFormSelector(F("PZEM Mode"), F("PZEM_mode"), P102_PZEM_mode); } addHtml(F(" Tx/Rx Pins config disabled: Configuration is available in the first PZEM plugin.
")); addFormNumericBox(F("Address of PZEM"), F("PZEM_addr"), P102_PZEM_ADDR, 1, 247); diff --git a/src/_P105_AHT.ino b/src/_P105_AHT.ino index a229613c0f..1930233f0f 100644 --- a/src/_P105_AHT.ino +++ b/src/_P105_AHT.ino @@ -152,7 +152,9 @@ boolean Plugin_105(uint8_t function, struct EventStruct *event, String& string) static_cast(AHTx_device_type::AHT20_DEVICE), static_cast(AHTx_device_type::AHT21_DEVICE) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - addFormSelector(F("Sensor model"), F("ahttype"), optionCount, options, indices, P105_AHT_TYPE, true); + 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.")); if (static_cast(AHTx_device_type::AHT10_DEVICE) == P105_AHT_TYPE) { diff --git a/src/_P108_DDS238.ino b/src/_P108_DDS238.ino index d914f225c5..d0fd6d6712 100644 --- a/src/_P108_DDS238.ino +++ b/src/_P108_DDS238.ino @@ -106,7 +106,8 @@ boolean Plugin_108(uint8_t function, struct EventStruct *event, String& string) for (int i = 0; i < 4; ++i) { options_baudrate[i] = String(p108_storageValueToBaudrate(i)); } - addFormSelector(F("Baud Rate"), P108_BAUDRATE_LABEL, 4, options_baudrate, nullptr, P108_BAUDRATE); + FormSelectorOptions selector(4, options_baudrate); + selector.addFormSelector(F("Baud Rate"), P108_BAUDRATE_LABEL, P108_BAUDRATE); addUnit(F("baud")); addFormNumericBox(F("Modbus Address"), P108_DEV_ID_LABEL, P108_DEV_ID, 1, 247); break; diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index b07946ceb7..d5dc3d8c70 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -181,7 +181,8 @@ boolean Plugin_109(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options4[] = { F("0.2"), F("0.5"), F("1") }; const int optionValues4[] = { 2, 5, 10 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - addFormSelector(F("Hysteresis"), F("hyst"), optionCount, options4, optionValues4, static_cast(P109_CONFIG_HYSTERESIS * 10.0f)); + FormSelectorOptions selector(optionCount, options4, optionValues4); + selector.addFormSelector(F("Hysteresis"), F("hyst"), static_cast(P109_CONFIG_HYSTERESIS * 10.0f)); } { diff --git a/src/_P110_VL53L0X.ino b/src/_P110_VL53L0X.ino index 72320e7a14..5ccc6d157a 100644 --- a/src/_P110_VL53L0X.ino +++ b/src/_P110_VL53L0X.ino @@ -100,7 +100,8 @@ boolean Plugin_110(uint8_t function, struct EventStruct *event, String& string) F("Accurate") }; const int optionValuesMode2[] = { 80, 20, 320 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - addFormSelector(F("Timing"), F("ptiming"), optionCount, optionsMode2, optionValuesMode2, P110_TIMING); + FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + selector.addFormSelector(F("Timing"), F("ptiming"), P110_TIMING); } { @@ -108,7 +109,8 @@ boolean Plugin_110(uint8_t function, struct EventStruct *event, String& string) F("Normal"), F("Long") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode3); - addFormSelector(F("Range"), F("prange"), optionCount, optionsMode3, nullptr, P110_RANGE); + FormSelectorOptions selector(optionCount, optionsMode3); + selector.addFormSelector(F("Range"), F("prange"), P110_RANGE); } addFormCheckBox(F("Send event when value unchanged"), F("notchanged"), P110_SEND_ALWAYS == 1); addFormNote(F("When checked, 'Trigger delta' setting is ignored!")); diff --git a/src/_P111_RC522_RFID.ino b/src/_P111_RC522_RFID.ino index a865e1d106..c357adad9d 100644 --- a/src/_P111_RC522_RFID.ino +++ b/src/_P111_RC522_RFID.ino @@ -87,7 +87,8 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) # endif // P111_USE_REMOVAL }; constexpr size_t P111_removaltypes = NR_ELEMENTS(removalopts); - addFormSelector(F("Tag removal mode"), F("autotagremoval"), P111_removaltypes, removaltype, removalopts, P111_TAG_AUTOREMOVAL); + FormSelectorOptions selector(P111_removaltypes, removaltype, removalopts); + selector.addFormSelector(F("Tag removal mode"), F("autotagremoval"), P111_TAG_AUTOREMOVAL); } addFormNumericBox(F("Tag removal Time-out"), F("removaltimeout"), P111_REMOVALTIMEOUT, 0, 60000); // 0 to 60 seconds diff --git a/src/_P112_AS7265x.ino b/src/_P112_AS7265x.ino index b515e4c7c5..dd8c4af7c5 100644 --- a/src/_P112_AS7265x.ino +++ b/src/_P112_AS7265x.ino @@ -117,19 +117,20 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) AS7265X_GAIN_64X, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode); - addFormSelector(F("Gain"), F("Gain"), optionCount, optionsMode, optionValuesMode, PCONFIG_LONG(0)); + FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode); + selector.addFormSelector(F("Gain"), F("Gain"), PCONFIG_LONG(0)); } { // Integration cycles from 0 (2.78ms) to 255 (711ms) // sensor.setIntegrationCycles(49); //Default: 50*2.8ms = 140ms per reading // sensor.setIntegrationCycles(1); //2*2.8ms = 5.6ms per reading const __FlashStringHelper *optionsMode2[] = { - F("2.8 ms"), - F("28 ms"), - F("56 ms"), - F("140 ms"), - F("280 ms"), - F("711 ms (default)"), + F("2.8"), + F("28"), + F("56"), + F("140"), + F("280"), + F("711 (default)"), }; const int optionValuesMode2[] = { 0, @@ -140,7 +141,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) 254, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - addFormSelector(F("Integration Time"), F("IntegrationTime"), optionCount, optionsMode2, optionValuesMode2, PCONFIG_LONG(1)); + FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + selector.addFormSelector(F("Integration Time"), F("IntegrationTime"), PCONFIG_LONG(1)); + addUnit(F("ms")); } # ifndef BUILD_NO_DEBUG addFormNote(F("Raw Readings shall not reach the upper limit of 65535 (Sensor Saturation).")); @@ -155,10 +158,10 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_4MA); // sensor.setIndicatorCurrent(AS7265X_INDICATOR_CURRENT_LIMIT_8MA); //Default const __FlashStringHelper *optionsMode3[] = { - F("1 mA"), - F("2 mA"), - F("4 mA"), - F("8 mA (default)"), + F("1"), + F("2"), + F("4"), + F("8 (default)"), }; const int optionValuesMode3[] = { AS7265X_INDICATOR_CURRENT_LIMIT_1MA, @@ -167,7 +170,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) AS7265X_INDICATOR_CURRENT_LIMIT_8MA, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode3); - addFormSelector(EMPTY_STRING, PCONFIG_LABEL(1), optionCount, optionsMode3, optionValuesMode3, PCONFIG(1)); + FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3); + selector.addFormSelector(EMPTY_STRING, PCONFIG_LABEL(1), PCONFIG(1)); + addUnit(F("mA")); } addHtml(F(" Current Limit")); # ifndef BUILD_NO_DEBUG @@ -181,10 +186,10 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_WHITE); //Allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_WHITE); //Allowed const __FlashStringHelper *optionsMode4[] = { - F("12.5 mA (default)"), - F("25 mA"), - F("50 mA"), - F("100 mA"), + F("12.5 (default)"), + F("25"), + F("50"), + F("100"), }; const int optionValuesMode4[] = { AS7265X_LED_CURRENT_LIMIT_12_5MA, @@ -193,7 +198,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) AS7265X_LED_CURRENT_LIMIT_100MA, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode4); - addFormSelector(F("White"), PCONFIG_LABEL(2), optionCount, optionsMode4, optionValuesMode4, PCONFIG(2)); + FormSelectorOptions selector(optionCount, optionsMode4, optionValuesMode4); + selector.addFormSelector(F("White"), PCONFIG_LABEL(2), PCONFIG(2)); + addUnit(F("mA")); } addHtml(F(" Current Limit")); @@ -204,9 +211,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_IR); //Allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_IR-bad); //Not allowed const __FlashStringHelper *optionsMode5[] = { - F("12.5 mA (default)"), - F("25 mA"), - F("50 mA"), + F("12.5 (default)"), + F("25"), + F("50"), }; const int optionValuesMode5[] = { AS7265X_LED_CURRENT_LIMIT_12_5MA, @@ -214,7 +221,9 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) AS7265X_LED_CURRENT_LIMIT_50MA, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode5); - addFormSelector(F("IR"), PCONFIG_LABEL(3), optionCount, optionsMode5, optionValuesMode5, PCONFIG(3)); + FormSelectorOptions selector(optionCount, optionsMode5, optionValuesMode5); + selector.addFormSelector(F("IR"), PCONFIG_LABEL(3), PCONFIG(3)); + addUnit(F("mA")); } { @@ -223,10 +232,12 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_25MA, AS7265x_LED_UV-bad); //Not allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_UV-bad); //Not allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_UV-bad); //Not allowed - const __FlashStringHelper *optionsMode6[] = { F("12.5 mA (default)") }; + const __FlashStringHelper *optionsMode6[] = { F("12.5 (default)") }; const int optionValuesMode6[] = { AS7265X_LED_CURRENT_LIMIT_12_5MA }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode6); - addFormSelector(F("UV"), PCONFIG_LABEL(4), optionCount, optionsMode6, optionValuesMode6, PCONFIG(4)); + FormSelectorOptions selector(optionCount, optionsMode6, optionValuesMode6); + selector.addFormSelector(F("UV"), PCONFIG_LABEL(4), PCONFIG(4)); + addUnit(F("mA")); } addFormNote(F("Control Gain and Integration Time after any change to avoid Sensor Saturation!")); diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index f431101556..385084e377 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -102,16 +102,18 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *optionsMode2[] = { - F("100ms (Normal)"), - F("20ms (Fastest)"), - F("33ms (Fast)"), - F("50ms"), - F("200ms (Accurate)"), - F("500ms"), + F("100 (Normal)"), + F("20 (Fastest)"), + F("33 (Fast)"), + F("50"), + F("200 (Accurate)"), + F("500"), }; const int optionValuesMode2[] = { 100, 20, 33, 50, 200, 500 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - addFormSelector(F("Timing"), F("timing"), optionCount, optionsMode2, optionValuesMode2, P113_TIMING); + FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + selector.addFormSelector(F("Timing"), F("timing"), P113_TIMING); + addUnit(F("ms")); } { @@ -121,7 +123,8 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) }; const int optionValuesMode3[2] = { 0, 1 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode3); - addFormSelector(F("Range"), F("range"), optionCount, optionsMode3, optionValuesMode3, P113_RANGE); + FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3); + selector.addFormSelector(F("Range"), F("range"), P113_RANGE); } addFormCheckBox(F("Send event when value unchanged"), F("notchanged"), P113_SEND_ALWAYS == 1); addFormNote(F("When checked, 'Trigger delta' setting is ignored!")); diff --git a/src/_P114_VEML6075.ino b/src/_P114_VEML6075.ino index 740bf75832..9a2af708a9 100644 --- a/src/_P114_VEML6075.ino +++ b/src/_P114_VEML6075.ino @@ -80,11 +80,11 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *optionsMode2[] = { - F("50 ms"), - F("100 ms"), - F("200 ms"), - F("400 ms"), - F("800 ms"), + F("50"), + F("100"), + F("200"), + F("400"), + F("800"), }; const int optionValuesMode2[] = { P114_IT_50, @@ -94,7 +94,9 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) P114_IT_800, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - addFormSelector(F("Integration Time"), F("it"), optionCount, optionsMode2, optionValuesMode2, PCONFIG(1)); + FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + selector.addFormSelector(F("Integration Time"), F("it"), PCONFIG(1)); + addUnit(F("ms")); } { @@ -103,7 +105,8 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) F("High Dynamic") } ; constexpr size_t optionCount = NR_ELEMENTS(optionsMode3); - addFormSelector(F("Dynamic Setting"), F("hd"), optionCount, optionsMode3, nullptr, PCONFIG(2)); + FormSelectorOptions selector(optionCount, optionsMode3); + selector.addFormSelector(F("Dynamic Setting"), F("hd"), PCONFIG(2)); } success = true; diff --git a/src/_P115_MAX1704x_v2.ino b/src/_P115_MAX1704x_v2.ino index 0623f7cc76..64d7aaa089 100644 --- a/src/_P115_MAX1704x_v2.ino +++ b/src/_P115_MAX1704x_v2.ino @@ -107,7 +107,8 @@ boolean Plugin_115(uint8_t function, struct EventStruct *event, String& string) MAX1704X_MAX17048, MAX1704X_MAX17049 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Device"), F("device"), optionCount, options, optionValues, choice); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Device"), F("device"), choice); } addFormNumericBox(F("Alert threshold"), F("threshold"), P115_THRESHOLD, 1, 32); diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index 8e1e34ec0a..d19a66e9ea 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -180,11 +180,9 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) static_cast(ST77xx_type_e::ST7796s_320x480) }; constexpr int optCount4 = NR_ELEMENTS(optionValues4); - addFormSelector(F("TFT display model"), + FormSelectorOptions selector(optCount4, options4, optionValues4); + selector.addFormSelector(F("TFT display model"), F("type"), - optCount4, - options4, - optionValues4, P116_CONFIG_FLAG_GET_TYPE); } @@ -218,12 +216,11 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) static_cast(P116_CommandTrigger::st7796) }; constexpr int cmdCount = NR_ELEMENTS(commandTriggerOptions); - addFormSelector(F("Write Command trigger"), - F("commandtrigger"), - cmdCount, - commandTriggers, - commandTriggerOptions, - P116_CONFIG_FLAG_GET_CMD_TRIGGER); + FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); + selector.addFormSelector( + F("Write Command trigger"), + F("commandtrigger"), + P116_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Select the command that is used to handle commands for this display.")); # endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/_P119_ITG3205_Gyro.ino b/src/_P119_ITG3205_Gyro.ino index 1aa867205b..247416aca7 100644 --- a/src/_P119_ITG3205_Gyro.ino +++ b/src/_P119_ITG3205_Gyro.ino @@ -124,7 +124,8 @@ boolean Plugin_119(uint8_t function, struct EventStruct *event, String& string) F("50") }; const int frequencyValues[] = { P119_FREQUENCY_10, P119_FREQUENCY_50 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - addFormSelector(F("Measuring frequency"), F("frequency"), optionCount, frequencyOptions, frequencyValues, P119_FREQUENCY); + FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + selector.addFormSelector(F("Measuring frequency"), F("frequency"), P119_FREQUENCY); addUnit(F("Hz")); success = true; diff --git a/src/_P122_SHT2x.ino b/src/_P122_SHT2x.ino index 96402421cd..9504a1620f 100644 --- a/src/_P122_SHT2x.ino +++ b/src/_P122_SHT2x.ino @@ -138,7 +138,8 @@ boolean Plugin_122(uint8_t function, struct EventStruct *event, String& string) P122_RESOLUTION_11T_11RH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Resolution"), P122_RESOLUTION_LABEL, optionCount, options, optionValues, P122_RESOLUTION); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("Resolution"), P122_RESOLUTION_LABEL, P122_RESOLUTION); # ifndef LIMIT_BUILD_SIZE P122_data_struct *P122_data = static_cast(getPluginTaskData(event->TaskIndex)); diff --git a/src/_P123_I2CTouch.ino b/src/_P123_I2CTouch.ino index e97b65e21c..cd047760ad 100644 --- a/src/_P123_I2CTouch.ino +++ b/src/_P123_I2CTouch.ino @@ -201,12 +201,9 @@ boolean Plugin_123(uint8_t function, struct EventStruct *event, String& string) static_cast(P123_TouchType_e::Automatic), }; constexpr size_t optionCount = NR_ELEMENTS(touchTypeOptions); - addFormSelector(F("Touchscreen type (address)"), - F("ttype"), - optionCount, - touchTypes, - touchTypeOptions, - P123_GET_TOUCH_TYPE); + FormSelectorOptions selector(optionCount, touchTypes, touchTypeOptions); + selector.addFormSelector( + F("Touchscreen type (address)"), F("ttype"), P123_GET_TOUCH_TYPE); if (nullptr != P123_data) { addUnit(concat(F("Detected: "), toString(P123_data->getTouchType()))); diff --git a/src/_P124_MultiRelay.ino b/src/_P124_MultiRelay.ino index 8220b8ff55..fda3486984 100644 --- a/src/_P124_MultiRelay.ino +++ b/src/_P124_MultiRelay.ino @@ -105,13 +105,17 @@ boolean Plugin_124(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_WEBFORM_LOAD: { + /* const __FlashStringHelper *optionsMode2[] = { F("2"), F("4"), F("8") }; + */ const int optionValuesMode2[] { 2, 4, 8 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - addFormSelector(F("Number of relays"), F("relays"), optionCount, optionsMode2, optionValuesMode2, P124_CONFIG_RELAY_COUNT, true); + FormSelectorOptions selector(optionCount, /*optionsMode2,*/ optionValuesMode2); + selector.reloadonchange = true; + selector.addFormSelector(F("Number of relays"), F("relays"), P124_CONFIG_RELAY_COUNT); addFormSelector_YesNo(F("Initialize relays on startup"), getPluginCustomArgName(P124_FLAGS_INIT_RELAYS), diff --git a/src/_P126_74HC595.ino b/src/_P126_74HC595.ino index 81bd15365b..1e58826fbd 100644 --- a/src/_P126_74HC595.ino +++ b/src/_P126_74HC595.ino @@ -167,7 +167,8 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string) F("Hex/bin only") }; const int outputValues[] = { P126_OUTPUT_BOTH, P126_OUTPUT_DEC_ONLY, P126_OUTPUT_HEXBIN }; constexpr size_t optionCount = NR_ELEMENTS(outputValues); - addFormSelector(F("Output selection"), F("output"), optionCount, outputOptions, outputValues, P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION); + FormSelectorOptions selector(optionCount, outputOptions, outputValues); + selector.addFormSelector(F("Output selection"), F("output"), P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION); addFormCheckBox(F("Restore Values on warm boot"), F("valrestore"), P126_CONFIG_FLAGS_GET_VALUES_RESTORE); diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino index 60a7bf5589..3abdc75a79 100644 --- a/src/_P129_74HC165.ino +++ b/src/_P129_74HC165.ino @@ -155,20 +155,17 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) addFormSubHeader(F("Device configuration")); { - String chipCount[P129_MAX_CHIP_COUNT]; + //String chipCount[P129_MAX_CHIP_COUNT]; int chipOption[P129_MAX_CHIP_COUNT]; for (uint8_t i = 0; i < P129_MAX_CHIP_COUNT; ++i) { - chipCount[i] = i + 1; + //chipCount[i] = i + 1; chipOption[i] = i + 1; } - addFormSelector(F("Number of chips (Q7 → DS)"), - F("chipcnt"), - P129_MAX_CHIP_COUNT, - chipCount, - chipOption, - P129_CONFIG_CHIP_COUNT, - true); + FormSelectorOptions selector(P129_MAX_CHIP_COUNT, /*chipCount,*/ chipOption); + selector.reloadonchange = true; + selector.addFormSelector( + 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.")); @@ -180,12 +177,9 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("50/sec (20 msec)") }; const int frequencyValues[] = { P129_FREQUENCY_10, P129_FREQUENCY_50 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - addFormSelector(F("Sample frequency"), - F("frequency"), - optionCount, - frequencyOptions, - frequencyValues, - P129_CONFIG_FLAGS_GET_READ_FREQUENCY); + FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + selector.addFormSelector( + F("Sample frequency"), F("frequency"), P129_CONFIG_FLAGS_GET_READ_FREQUENCY); addFormSubHeader(F("Display and output")); @@ -199,8 +193,8 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("Hex/bin only") }; const int outputValues[] = { P129_OUTPUT_BOTH, P129_OUTPUT_DEC_ONLY, P129_OUTPUT_HEXBIN }; constexpr size_t outputCount = NR_ELEMENTS(outputValues); - addFormSelector(F("Output selection"), F("outputsel"), outputCount, outputOptions, outputValues, - P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION); + FormSelectorOptions selector_output(outputCount, outputOptions, outputValues); + selector_output.addFormSelector(F("Output selection"), F("outputsel"), P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION); addFormCheckBox(F("Separate events per pin"), F("separate_events"), P129_CONFIG_FLAGS_GET_SEPARATE_EVENTS == 1); diff --git a/src/_P131_NeoPixelMatrix.ino b/src/_P131_NeoPixelMatrix.ino index 24371d69d0..6318ce2862 100644 --- a/src/_P131_NeoPixelMatrix.ino +++ b/src/_P131_NeoPixelMatrix.ino @@ -92,7 +92,8 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; // Selection copied from P038 - addFormSelector(F("Strip Type"), F("striptype"), 2, stripOptions, optionValuesZeroOne, P131_CONFIG_FLAGS_GET_STRIP_TYPE); + FormSelectorOptions selector(2, stripOptions, optionValuesZeroOne); + selector.addFormSelector(F("Strip Type"), F("striptype"), P131_CONFIG_FLAGS_GET_STRIP_TYPE); } { @@ -118,14 +119,18 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Matrix height"), F("mxheight"), P131_CONFIG_MATRIX_HEIGHT, 1, 100); - addFormSelector(F("Matrix start-pixel"), F("mxstart"), 4, optionsTop, optionValuesTop, - get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_TOP)); + FormSelectorOptions selTop(4, optionsTop, optionValuesTop); + FormSelectorOptions selRowCol(2, optionsRowCol, optionValuesZeroOne); + FormSelectorOptions selProZig(2, optionsProZig, optionValuesZeroOne); - addFormSelector(F("Matrix Rows/Columns mode"), F("mxrowcol"), 2, optionsRowCol, optionValuesZeroOne, - bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_RC)); + selTop.addFormSelector(F("Matrix start-pixel"), F("mxstart"), + get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_TOP)); - addFormSelector(F("Matrix flow direction"), F("mxprozig"), 2, optionsProZig, optionValuesZeroOne, - bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_PZ)); + selRowCol.addFormSelector(F("Matrix Rows/Columns mode"), F("mxrowcol"), + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_RC)); + + selProZig.addFormSelector(F("Matrix flow direction"), F("mxprozig"), + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_PZ)); addFormSubHeader(F("Multiple matrices: Tiles")); @@ -134,14 +139,14 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Tile matrix height"), F("tlheight"), P131_CONFIG_TILE_HEIGHT, 1, P131_Nlines); - addFormSelector(F("Tile start-matrix"), F("tlstart"), 4, optionsTop, optionValuesTop, - get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_TOP)); + selTop.addFormSelector(F("Tile start-matrix"), F("tlstart"), + get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_TOP)); - addFormSelector(F("Tile Rows/Columns mode"), F("tlrowcol"), 2, optionsRowCol, optionValuesZeroOne, - bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_RC)); + selRowCol.addFormSelector(F("Tile Rows/Columns mode"), F("tlrowcol"), + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_RC)); - addFormSelector(F("Tile flow direction"), F("tlprozig"), 2, optionsProZig, optionValuesZeroOne, - bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_PZ)); + selProZig.addFormSelector(F("Tile flow direction"), F("tlprozig"), + bitRead(P131_CONFIG_FLAGS, P131_FLAGS_TILE_TYPE_PZ)); } addFormSubHeader(F("Display")); @@ -174,13 +179,10 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) static_cast(P131_CommandTrigger::neomatrix), static_cast(P131_CommandTrigger::neo) }; - constexpr int cmdCount = sizeof(commandTriggerOptions) / sizeof(commandTriggerOptions[0]); - addFormSelector(F("Write Command trigger"), - F("cmdtrigger"), - cmdCount, - commandTriggers, - commandTriggerOptions, - P131_CONFIG_FLAG_GET_CMD_TRIGGER); + constexpr int cmdCount = NR_ELEMENTS(commandTriggerOptions); + FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); + selector.addFormSelector( + F("Write Command trigger"), F("cmdtrigger"), P131_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Select the command that is used to handle commands for this display.")); # endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 314970893a..994d5cd1ba 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -111,9 +111,13 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(varOptions); + FormSelectorOptions selector(optionCount, varOptions); + for (uint8_t r = 0; r < VARS_PER_TASK; ++r) { - addFormSelector(concat(F("Power value "), r + 1), - getPluginCustomArgName(r), optionCount, varOptions, NULL, PCONFIG(P132_CONFIG_BASE + r)); + selector.addFormSelector( + concat(F("Power value "), r + 1), + getPluginCustomArgName(r), + PCONFIG(P132_CONFIG_BASE + r)); } } @@ -128,7 +132,8 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) }; const int shuntvalue[] = { 1, 10, 20 }; constexpr size_t optionCount = NR_ELEMENTS(shuntvalue); - addFormSelector(F("Shunt resistor"), F("shunt"), optionCount, varshuntptions, shuntvalue, P132_SHUNT); + FormSelectorOptions selector(optionCount, varshuntptions, shuntvalue); + selector.addFormSelector(F("Shunt resistor"), F("shunt"), P132_SHUNT); addFormNote(F("Select as is installed on the board.")); } @@ -147,12 +152,8 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) }; const int averageValue[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; constexpr size_t optionCount = NR_ELEMENTS(averageValue); - addFormSelector(F("Averaging samples"), - F("average"), - optionCount, - averagingSamples, - averageValue, - P132_GET_AVERAGE); + FormSelectorOptions selector(optionCount, averagingSamples, averageValue); + 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!")); } @@ -171,19 +172,9 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) // 140us 204us 332us 588us 1.1ms 2.1ms 4.1ms 8.2ms const int conversionValues[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; constexpr size_t optionCount = NR_ELEMENTS(conversionValues); - addFormSelector(F("Conversion rate Voltage"), - F("conv_v"), - optionCount, - conversionRates, - conversionValues, - P132_GET_CONVERSION_B); - - addFormSelector(F("Conversion rate Current"), - F("conv_c"), - optionCount, - conversionRates, - conversionValues, - P132_GET_CONVERSION_S); + FormSelectorOptions selector(optionCount, conversionRates, conversionValues); + selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), P132_GET_CONVERSION_B); + selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), P132_GET_CONVERSION_S); } success = true; diff --git a/src/_P133_LTR390.ino b/src/_P133_LTR390.ino index 39390ec521..9b1976ba99 100644 --- a/src/_P133_LTR390.ino +++ b/src/_P133_LTR390.ino @@ -97,7 +97,9 @@ boolean Plugin_133(uint8_t function, struct EventStruct *event, String& string) static_cast(P133_selectMode_e::ALSMode) }; constexpr size_t optionCount = NR_ELEMENTS(selectModeValues); - addFormSelector(F("Read mode"), F("mode"), optionCount, selectModeOptions, selectModeValues, P133_SELECT_MODE, true); + FormSelectorOptions selector(optionCount, selectModeOptions, selectModeValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Read mode"), F("mode"), P133_SELECT_MODE); } const __FlashStringHelper *gainOptions[] = { F("1x"), F("3x"), F("6x"), F("9x"), F("18x") }; @@ -127,15 +129,17 @@ boolean Plugin_133(uint8_t function, struct EventStruct *event, String& string) LTR390_RESOLUTION_13BIT, }; constexpr size_t resolutionCount = NR_ELEMENTS(resolutionValues); + FormSelectorOptions selGain(gainCount, gainOptions, gainValues); + FormSelectorOptions selRes(resolutionCount, resolutionOptions, resolutionValues); if (static_cast(P133_SELECT_MODE) != P133_selectMode_e::ALSMode) { - addFormSelector(F("UV Gain"), F("uvgain"), gainCount, gainOptions, gainValues, P133_UVGAIN); - addFormSelector(F("UV Resolution"), F("uvres"), resolutionCount, resolutionOptions, resolutionValues, P133_UVRESOLUTION); + selGain.addFormSelector(F("UV Gain"), F("uvgain"), P133_UVGAIN); + selRes.addFormSelector(F("UV Resolution"), F("uvres"), P133_UVRESOLUTION); } if (static_cast(P133_SELECT_MODE) != P133_selectMode_e::UVMode) { - addFormSelector(F("Ambient Gain"), F("alsgain"), gainCount, gainOptions, gainValues, P133_ALSGAIN); - addFormSelector(F("Ambient Resolution"), F("alsres"), resolutionCount, resolutionOptions, resolutionValues, P133_ALSRESOLUTION); + selGain.addFormSelector(F("Ambient Gain"), F("alsgain"), P133_ALSGAIN); + selRes.addFormSelector(F("Ambient Resolution"), F("alsres"), P133_ALSRESOLUTION); } addFormCheckBox(F("Reset sensor on init"), F("initreset"), P133_INITRESET == 1); diff --git a/src/_P135_SCD4x.ino b/src/_P135_SCD4x.ino index 4f5f2d51e8..354d8f9b8d 100644 --- a/src/_P135_SCD4x.ino +++ b/src/_P135_SCD4x.ino @@ -105,7 +105,9 @@ boolean Plugin_135(uint8_t function, struct EventStruct *event, String& string) static_cast(scd4x_sensor_type_e::SCD4x_SENSOR_SCD41), }; constexpr size_t optionCount = NR_ELEMENTS(sensorTypeOptions); - addFormSelector(F("Sensor model"), F("ptype"), optionCount, sensorTypes, sensorTypeOptions, P135_SENSOR_TYPE, true); + FormSelectorOptions selector(optionCount, sensorTypes, sensorTypeOptions); + selector.reloadonchange = true; + selector.addFormSelector(F("Sensor model"), F("ptype"), P135_SENSOR_TYPE); # ifndef LIMIT_BUILD_SIZE 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 02b214217a..e9fcc85c26 100644 --- a/src/_P137_AXP192.ino +++ b/src/_P137_AXP192.ino @@ -190,9 +190,9 @@ boolean Plugin_137(uint8_t function, struct EventStruct *event, String& string) static_cast(P137_PredefinedDevices_e::LilyGO_TBeam), static_cast(P137_PredefinedDevices_e::UserDefined) }; // keep last and at 99 !! constexpr size_t optionCount = NR_ELEMENTS(predefinedValues); - addFormSelector(F("Predefined device configuration"), F("predef"), - optionCount, - predefinedNames, predefinedValues, 0, !Settings.isPowerManagerTask(event->TaskIndex)); + FormSelectorOptions selector(optionCount, predefinedNames, predefinedValues); + 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.")); diff --git a/src/_P141_PCD8544_Nokia5110.ino b/src/_P141_PCD8544_Nokia5110.ino index c56f909411..8d9cb32a1a 100644 --- a/src/_P141_PCD8544_Nokia5110.ino +++ b/src/_P141_PCD8544_Nokia5110.ino @@ -148,12 +148,9 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) static_cast(P141_CommandTrigger::lcd), }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - addFormSelector(F("Write Command trigger"), - F("pcmdtrigger"), - optionCount, - commandTriggers, - commandTriggerOptions, - P141_CONFIG_FLAG_GET_CMD_TRIGGER); + FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + selector.addFormSelector( + F("Write Command trigger"), F("pcmdtrigger"), P141_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Select the command that is used to handle commands for this display.")); # endif // ifndef LIMIT_BUILD_SIZE diff --git a/src/_P142_AS5600.ino b/src/_P142_AS5600.ino index 260d8b27cb..4e29c63ec5 100644 --- a/src/_P142_AS5600.ino +++ b/src/_P142_AS5600.ino @@ -173,12 +173,8 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_MODE_RADIANS, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Output range"), - F("range"), - optionCount, - configurations, - configurationOptions, - P142_GET_OUTPUT_MODE); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Output range"), F("range"), P142_GET_OUTPUT_MODE); } addFormCheckBox(F("Generate Events only when changed"), F("diff"), P142_GET_UPDATE_DIFF_ONLY); addFormCheckBox(F("Generate Events only when magnet detected"), F("cmag"), P142_GET_CHECK_MAGNET); @@ -212,12 +208,8 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_POWERMODE_LOW3, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Power mode"), - F("pow"), - optionCount, - configurations, - configurationOptions, - P142_GET_POWER_MODE); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Power mode"), F("pow"), P142_GET_POWER_MODE); } addFormCheckBox(F("Power watchdog"), F("wdog"), P142_GET_WATCHDOG); addFormNote(F("Switches to 'Low power mode 3' after 1 minute of less than 4 LSBs change")); @@ -237,12 +229,8 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_HYST_LSB3, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Hysteresis"), - F("hyst"), - optionCount, - configurations, - configurationOptions, - P142_GET_HYSTERESIS); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Hysteresis"), F("hyst"), P142_GET_HYSTERESIS); } { const __FlashStringHelper *configurations[] = { @@ -258,12 +246,8 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_SLOW_FILT_2X, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Slow filter"), - F("sflt"), - optionCount, - configurations, - configurationOptions, - P142_GET_SLOW_FILTER); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Slow filter"), F("sflt"), P142_GET_SLOW_FILTER); } { const __FlashStringHelper *configurations[] = { @@ -287,12 +271,8 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_FAST_FILT_LSB10, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Fast filter"), - F("fflt"), - optionCount, - configurations, - configurationOptions, - P142_GET_FAST_FILTER); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Fast filter"), F("fflt"), P142_GET_FAST_FILTER); } success = true; break; diff --git a/src/_P143_I2C_Rotary.ino b/src/_P143_I2C_Rotary.ino index cfe4748341..fb0d3e8efa 100644 --- a/src/_P143_I2C_Rotary.ino +++ b/src/_P143_I2C_Rotary.ino @@ -172,13 +172,9 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) # endif // if P143_FEATURE_INCLUDE_DFROBOT }; constexpr size_t optionCount = NR_ELEMENTS(selectModeValues); - addFormSelector(F("Encoder type"), - F("pdevice"), - optionCount, - selectModeOptions, - selectModeValues, - P143_ENCODER_TYPE, - true); + 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!")); } @@ -241,12 +237,8 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_M5StackLed_e::Led2Only), }; constexpr size_t optionCount = NR_ELEMENTS(selectLedModeValues); - addFormSelector(F("Color map Leds"), - F("pledsel"), - optionCount, - selectLedModeOptions, - selectLedModeValues, - P143_M5STACK_SELECTION); + FormSelectorOptions selector(optionCount, selectLedModeOptions, selectLedModeValues); + selector.addFormSelector(F("Color map Leds"), F("pledsel"), P143_M5STACK_SELECTION); } # endif // if P143_FEATURE_INCLUDE_M5STACK break; @@ -287,12 +279,8 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_ButtonAction_e::ToggleSwitch), }; constexpr size_t optionCount = NR_ELEMENTS(selectButtonValues); - addFormSelector(F("Button action"), - F("pbutton"), - optionCount, - selectButtonOptions, - selectButtonValues, - P143_PLUGIN_BUTTON_ACTION); + FormSelectorOptions selector(optionCount, selectButtonOptions, selectButtonValues); + selector.addFormSelector(F("Button action"), F("pbutton"), P143_PLUGIN_BUTTON_ACTION); # if P143_FEATURE_INCLUDE_DFROBOT @@ -330,12 +318,8 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_CounterMapping_e::ColorGradient), }; constexpr size_t optionCount = NR_ELEMENTS(selectCounterValues); - addFormSelector(F("Counter color mapping"), - F("pmap"), - optionCount, - selectCounterOptions, - selectCounterValues, - P143_PLUGIN_COUNTER_MAPPING); + FormSelectorOptions selector(optionCount, selectCounterOptions, selectCounterValues); + selector.addFormSelector(F("Counter color mapping"), F("pmap"), P143_PLUGIN_COUNTER_MAPPING); } { String strings[P143_STRINGS]; diff --git a/src/_P145_MQxxx.ino b/src/_P145_MQxxx.ino index 5e5e895797..95f543959c 100644 --- a/src/_P145_MQxxx.ino +++ b/src/_P145_MQxxx.ino @@ -191,7 +191,8 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string) { options[i] = concat(concat(P145_data_struct::getTypeName(i), F(" - ")), P145_data_struct::getGasName(i)); } - addFormSelector(F("Sensor type"), F(P145_GUID_TYPE), x, options, nullptr, P145_PCONFIG_SENSORT); + FormSelectorOptions selector(x, options); + selector.addFormSelector(F("Sensor type"), F(P145_GUID_TYPE), P145_PCONFIG_SENSORT); # ifdef ESP32 diff --git a/src/_P146_CacheControllerReader.ino b/src/_P146_CacheControllerReader.ino index 83edccd923..10d6b30e0d 100644 --- a/src/_P146_CacheControllerReader.ino +++ b/src/_P146_CacheControllerReader.ino @@ -221,7 +221,8 @@ boolean Plugin_146(uint8_t function, struct EventStruct *event, String& string) ';' }; constexpr size_t optionCount = NR_ELEMENTS(separatorOptions); - addFormSelector(F("Separator"), F("separator"), optionCount, separatorLabels, separatorOptions, P146_SEPARATOR_CHARACTER); + FormSelectorOptions selector(optionCount, separatorLabels, separatorOptions); + selector.addFormSelector(F("Separator"), F("separator"), P146_SEPARATOR_CHARACTER); } addFormCheckBox(F("Join Samples with same Timestamp"), F("jointimestamp"), P146_GET_JOIN_TIMESTAMP); addFormCheckBox(F("Export only enabled tasks"), F("onlysettasks"), P146_GET_ONLY_SET_TASKS); diff --git a/src/_P147_SGP4x.ino b/src/_P147_SGP4x.ino index 98b859b677..9e084fedb0 100644 --- a/src/_P147_SGP4x.ino +++ b/src/_P147_SGP4x.ino @@ -119,7 +119,9 @@ boolean Plugin_147(uint8_t function, struct EventStruct *event, String& string) static_cast(P147_sensor_e::SGP41), }; constexpr size_t optionCount = NR_ELEMENTS(sensorTypeOptions); - addFormSelector(F("Sensor model"), F("ptype"), optionCount, sensorTypes, sensorTypeOptions, P147_SENSOR_TYPE, true); + FormSelectorOptions selector(optionCount, sensorTypes, sensorTypeOptions); + selector.reloadonchange = true; + selector.addFormSelector(F("Sensor model"), F("ptype"), P147_SENSOR_TYPE); # ifndef BUILD_NO_DEBUG addFormNote(F("Page will reload on change.")); # endif // ifndef BUILD_NO_DEBUG diff --git a/src/_P148_POWRxxD_THR3xxD.ino b/src/_P148_POWRxxD_THR3xxD.ino index 235e631356..710b629453 100644 --- a/src/_P148_POWRxxD_THR3xxD.ino +++ b/src/_P148_POWRxxD_THR3xxD.ino @@ -121,8 +121,8 @@ boolean Plugin_148(uint8_t function, struct EventStruct *event, String& string) static_cast(P148_data_struct::Tm1621Device::THR3xxD) }; constexpr size_t nrElements = NR_ELEMENTS(optionValues); - - addFormSelector(F("Device Template"), F("devtmpl"), nrElements, options, optionValues, P148_DEVICE_SELECTOR); + FormSelectorOptions selector(nrElements, options, optionValues); + selector.addFormSelector(F("Device Template"), F("devtmpl"), P148_DEVICE_SELECTOR); addFormNote(F("GPIO settings will be ignored when selecting other than 'Custom'")); } diff --git a/src/_P150_TMP117.ino b/src/_P150_TMP117.ino index a0bb743723..402eebb5dd 100644 --- a/src/_P150_TMP117.ino +++ b/src/_P150_TMP117.ino @@ -135,7 +135,8 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) P150_AVERAGING_64_SAMPLES, }; constexpr size_t optionCount = NR_ELEMENTS(averagingOptions); - addFormSelector(F("Averaging"), F("avg"), optionCount, averagingCaptions, averagingOptions, P150_GET_CONF_AVERAGING); + FormSelectorOptions selector(optionCount, averagingCaptions, averagingOptions); + selector.addFormSelector(F("Averaging"), F("avg"), P150_GET_CONF_AVERAGING); } { @@ -148,13 +149,9 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) P150_CONVERSION_ONE_SHOT, }; constexpr size_t optionCount = NR_ELEMENTS(conversionOptions); - addFormSelector(F("Conversion mode"), - F("conv"), - optionCount, - conversionCaptions, - conversionOptions, - P150_GET_CONF_CONVERSION_MODE, - true); + FormSelectorOptions selector(optionCount, conversionCaptions, conversionOptions); + 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.")); # endif // ifndef BUILD_NO_DEBUG @@ -182,8 +179,8 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) P150_CYCLE_16_SEC, }; constexpr size_t optionCount = NR_ELEMENTS(cycleOptions); - addFormSelector(F("Continuous conversion cycle time"), F("cycle"), optionCount, cycleCaptions, cycleOptions, - P150_GET_CONF_CYCLE_BITS); + FormSelectorOptions selector(optionCount, cycleCaptions, cycleOptions); + selector.addFormSelector(F("Continuous conversion cycle time"), F("cycle"), P150_GET_CONF_CYCLE_BITS); } addFormSubHeader(F("Output")); diff --git a/src/_P153_SHT4x.ino b/src/_P153_SHT4x.ino index 22e2c55626..4a84fd13ab 100644 --- a/src/_P153_SHT4x.ino +++ b/src/_P153_SHT4x.ino @@ -147,23 +147,19 @@ boolean Plugin_153(uint8_t function, struct EventStruct *event, String& string) static_cast(P153_configuration_e::HighResolution20mW100msec), }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Startup Configuration"), - F("startup"), - optionCount, - configurations, - configurationOptions, - P153_STARTUP_CONFIGURATION); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Startup Configuration"), F("startup"), P153_STARTUP_CONFIGURATION); addFormNote(F("Heater should not exceed 10% dutycycle, so 1 sec. heater must have Interval > 10 sec.!")); addFormNumericBox(F("Use Normal Configuration after"), F("loops"), P153_INTERVAL_LOOPS, 0, 10); addUnit(F("Interval runs (0..10)")); - addFormSelector(F("Normal Configuration"), - F("normal"), - 3, // Only non-heater options - configurations, - configurationOptions, - P153_NORMAL_CONFIGURATION); + FormSelectorOptions selector_normal( + 3, // Only non-heater options + configurations, configurationOptions); + + selector_normal.addFormSelector( + F("Normal Configuration"), F("normal"), P153_NORMAL_CONFIGURATION); } success = true; diff --git a/src/_P166_GP8403.ino b/src/_P166_GP8403.ino index 81c2557e72..f248158553 100644 --- a/src/_P166_GP8403.ino +++ b/src/_P166_GP8403.ino @@ -127,12 +127,8 @@ boolean Plugin_166(uint8_t function, struct EventStruct *event, String& string) static_cast(DFRobot_GP8403::eOutPutRange_t::eOutputRange10V), }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - addFormSelector(F("Output range"), - F("range"), - optionCount, - configurations, - configurationOptions, - P166_MAX_VOLTAGE); + FormSelectorOptions selector(optionCount, configurations, configurationOptions); + selector.addFormSelector(F("Output range"), F("range"), P166_MAX_VOLTAGE); } addFormCheckBox(F("Restore output on warm boot"), F("prstr"), P166_RESTORE_VALUES == 1); diff --git a/src/_P167_Vindstyrka.ino b/src/_P167_Vindstyrka.ino index c31dfcbd92..2f03248567 100644 --- a/src/_P167_Vindstyrka.ino +++ b/src/_P167_Vindstyrka.ino @@ -176,9 +176,9 @@ boolean Plugin_167(uint8_t function, struct EventStruct *event, String& string) P167_MODEL_SEN55, }; constexpr uint8_t optCount = NR_ELEMENTS(options_model_value); - - addFormSelector(F("Model Type"), P167_MODEL_LABEL, optCount, - options_model, options_model_value, P167_MODEL, true); + 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.")); if (P167_MODEL == P167_MODEL_VINDSTYRKA) { diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 046e3a84dd..c65de55bd5 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -84,7 +84,7 @@ int FormSelectorOptions::getIndexValue(int index) const void FormSelectorOptions::addFormSelector( const __FlashStringHelper *label, const __FlashStringHelper *id, - int selectedIndex) + int selectedIndex) const { addFormSelector(String(label), String(id), selectedIndex); } @@ -92,7 +92,7 @@ void FormSelectorOptions::addFormSelector( void FormSelectorOptions::addFormSelector( const String & label, const __FlashStringHelper *id, - int selectedIndex) + int selectedIndex) const { addFormSelector(label, String(id), selectedIndex); } @@ -100,7 +100,7 @@ void FormSelectorOptions::addFormSelector( void FormSelectorOptions::addFormSelector( const __FlashStringHelper *label, const String & id, - int selectedIndex) + int selectedIndex) const { addFormSelector(String(label), id, selectedIndex); } @@ -108,7 +108,7 @@ void FormSelectorOptions::addFormSelector( void FormSelectorOptions::addFormSelector( const String& label, const String& id, - int selectedIndex) + int selectedIndex) const { addRowLabel_tr_id(label, id); diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index 86e822d9c1..3db54431cc 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -30,21 +30,21 @@ class FormSelectorOptions { virtual int getIndexValue(int index) const; - void addFormSelector(const __FlashStringHelper *label, - const __FlashStringHelper *id, - int selectedIndex); + void addFormSelector(const __FlashStringHelper *label, + const __FlashStringHelper *id, + int selectedIndex) const; - void addFormSelector(const String&label, - const __FlashStringHelper *id, - int selectedIndex); + void addFormSelector(const String&label, + const __FlashStringHelper *id, + int selectedIndex) const; - void addFormSelector(const __FlashStringHelper *label, - const String& id, - int selectedIndex); + void addFormSelector(const __FlashStringHelper *label, + const String& id, + int selectedIndex) const; void addFormSelector(const String& label, const String& id, - int selectedIndex); + int selectedIndex) const; bool reloadonchange = false; From ec5d0e348e396f2daab9614d272efc84241e0617 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Jan 2025 22:43:31 +0100 Subject: [PATCH 39/50] [Cleanup] Move more plugin code to use FormSelectorOptions class --- src/_P168_VEML6030_7700.ino | 45 +++++++------------ src/_P169_AS3935_LightningDetector.ino | 16 +++---- src/src/PluginStructs/P002_data_struct.cpp | 6 ++- src/src/PluginStructs/P025_data_struct.cpp | 7 +-- src/src/PluginStructs/P073_data_struct.cpp | 8 ++-- src/src/PluginStructs/P104_data_struct.cpp | 50 +++++++++++----------- src/src/PluginStructs/P120_data_struct.cpp | 11 +++-- src/src/PluginStructs/P148_data_struct.cpp | 11 ++--- src/src/PluginStructs/P165_data_struct.cpp | 20 ++++----- src/src/WebServer/Markup_Forms.cpp | 5 ++- src/src/WebServer/Markup_Forms.h | 4 +- 11 files changed, 90 insertions(+), 93 deletions(-) diff --git a/src/_P168_VEML6030_7700.ino b/src/_P168_VEML6030_7700.ino index c5f7e23648..dd78a99fc0 100644 --- a/src/_P168_VEML6030_7700.ino +++ b/src/_P168_VEML6030_7700.ino @@ -114,12 +114,8 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) VEML_LUX_CORRECTED_NOWAIT, }; constexpr size_t optionCount = NR_ELEMENTS(readMethodOptions); - addFormSelector(F("Lux Read-method"), - F("rmth"), - optionCount, - readMethod, - readMethodOptions, - P168_READLUX_MODE); + FormSelectorOptions selector(optionCount, readMethod, readMethodOptions); + selector.addFormSelector(F("Lux Read-method"), F("rmth"), P168_READLUX_MODE); addFormNote(F("For 'Auto' Read-method, the Gain factor and Integration time settings are ignored.")); } { @@ -136,21 +132,17 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) 0b11, }; constexpr size_t optionCount = NR_ELEMENTS(alsGainOptions); - addFormSelector(F("Gain factor"), - F("gain"), - optionCount, - alsGain, - alsGainOptions, - P168_ALS_GAIN); + FormSelectorOptions selector(optionCount, alsGain, alsGainOptions); + selector.addFormSelector(F("Gain factor"), F("gain"), P168_ALS_GAIN); } { const __FlashStringHelper *alsIntegration[] = { - F("25 ms"), - F("50 ms"), - F("100 ms"), - F("200 ms"), - F("400 ms"), - F("800 ms"), + F("25"), + F("50"), + F("100"), + F("200"), + F("400"), + F("800"), }; const int alsIntegrationOptions[] = { 0b1100, @@ -161,12 +153,9 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) 0b0011, }; constexpr size_t optionCount = NR_ELEMENTS(alsIntegrationOptions); - addFormSelector(F("Integration time"), - F("int"), - optionCount, - alsIntegration, - alsIntegrationOptions, - P168_ALS_INTEGRATION); + FormSelectorOptions selector(optionCount, alsIntegration, alsIntegrationOptions); + selector.addFormSelector(F("Integration time"), F("int"), P168_ALS_INTEGRATION); + addUnit(F("ms")); } addFormSeparator(2); { @@ -185,12 +174,8 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) static_cast(P168_power_save_mode_e::Mode4), }; constexpr size_t optionCount = NR_ELEMENTS(psmModeOptions); - addFormSelector(F("Power Save Mode"), - F("psm"), - optionCount, - psmMode, - psmModeOptions, - P168_PSM_MODE); + FormSelectorOptions selector(optionCount, psmMode, psmModeOptions); + selector.addFormSelector(F("Power Save Mode"), F("psm"), P168_PSM_MODE); } success = true; diff --git a/src/_P169_AS3935_LightningDetector.ino b/src/_P169_AS3935_LightningDetector.ino index 7655a70fec..f5a3850ed0 100644 --- a/src/_P169_AS3935_LightningDetector.ino +++ b/src/_P169_AS3935_LightningDetector.ino @@ -127,12 +127,11 @@ boolean Plugin_169(uint8_t function, struct EventStruct *event, String& string) AS3935MI::AS3935_MNL_9, AS3935MI::AS3935_MNL_16 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("Lightning Threshold"), - P169_LIGHTNING_THRESHOLD_LABEL, - optionCount, - options, - optionValues, - P169_LIGHTNING_THRESHOLD); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector( + F("Lightning Threshold"), + P169_LIGHTNING_THRESHOLD_LABEL, + P169_LIGHTNING_THRESHOLD); addFormNote(F("Minimum number of lightning strikes in the last 15 minutes")); } { @@ -159,8 +158,9 @@ boolean Plugin_169(uint8_t function, struct EventStruct *event, String& string) 18 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - addFormSelector(F("AFE Gain Min"), P169_AFE_GAIN_LOW_LABEL, optionCount, options, optionValues, P169_AFE_GAIN_LOW); - addFormSelector(F("AFE Gain Max"), P169_AFE_GAIN_HIGH_LABEL, optionCount, options, optionValues, P169_AFE_GAIN_HIGH); + FormSelectorOptions selector(optionCount, options, optionValues); + selector.addFormSelector(F("AFE Gain Min"), P169_AFE_GAIN_LOW_LABEL, P169_AFE_GAIN_LOW); + selector.addFormSelector(F("AFE Gain Max"), P169_AFE_GAIN_HIGH_LABEL, P169_AFE_GAIN_HIGH); addFormNote(F("Lower and upper limit for the Analog Frond-End auto gain to use.")); } diff --git a/src/src/PluginStructs/P002_data_struct.cpp b/src/src/PluginStructs/P002_data_struct.cpp index 32ea337d42..5d94fb8cf6 100644 --- a/src/src/PluginStructs/P002_data_struct.cpp +++ b/src/src/PluginStructs/P002_data_struct.cpp @@ -154,7 +154,8 @@ void P002_data_struct::webformLoad(struct EventStruct *event) P002_ADC_0db }; constexpr int nrOptions = NR_ELEMENTS(outputOptionValues); - addFormSelector(F("Attenuation"), F("attn"), nrOptions, outputOptions, outputOptionValues, P002_ATTENUATION); + FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); + selector.addFormSelector(F("Attenuation"), F("attn"), P002_ATTENUATION); } # endif // ifdef ESP32 @@ -175,7 +176,8 @@ void P002_data_struct::webformLoad(struct EventStruct *event) # endif // ifndef LIMIT_BUILD_SIZE }; constexpr int nrOptions = NR_ELEMENTS(outputOptionValues); - addFormSelector(F("Oversampling"), F("oversampling"), nrOptions, outputOptions, outputOptionValues, P002_OVERSAMPLING); + FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); + selector.addFormSelector(F("Oversampling"), F("oversampling"), P002_OVERSAMPLING); } # ifdef ESP32 diff --git a/src/src/PluginStructs/P025_data_struct.cpp b/src/src/PluginStructs/P025_data_struct.cpp index 709e2b09d2..62df92ed48 100644 --- a/src/src/PluginStructs/P025_data_struct.cpp +++ b/src/src/PluginStructs/P025_data_struct.cpp @@ -326,7 +326,8 @@ bool P025_data_struct::webformLoad(struct EventStruct *event) }; constexpr size_t ADS1115_PGA_OPTIONS = NR_ELEMENTS(pgaOptions); - addFormSelector(F("Gain"), F("gain"), ADS1115_PGA_OPTIONS, pgaOptions, nullptr, P025_GAIN); + FormSelectorOptions selector(ADS1115_PGA_OPTIONS, pgaOptions); + selector.addFormSelector(F("Gain"), F("gain"), P025_GAIN); addFormNote(F("Do not apply more than VDD + 0.3 V to the analog inputs of the device.")); } { @@ -341,8 +342,8 @@ bool P025_data_struct::webformLoad(struct EventStruct *event) F("860 / 3300"), }; constexpr size_t NR_OPTIONS = NR_ELEMENTS(P025_SPSOptions); - - addFormSelector(F("Sample Rate"), F("sps"), NR_OPTIONS, P025_SPSOptions, nullptr, p025_variousBits.getSampleRate()); + FormSelectorOptions selector(NR_OPTIONS, P025_SPSOptions); + selector.addFormSelector(F("Sample Rate"), F("sps"), p025_variousBits.getSampleRate()); addUnit(F("SPS")); addFormNote(F("Lower values for ADS1115, higher values for ADS1015")); diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 80ecaa19c8..23dd939065 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -115,8 +115,8 @@ void P073_display_output_selector(const __FlashStringHelper *id, int16_t value) P073_DISP_CLOCK12, P073_DISP_DATE, }; - - addFormSelector(F("Display Output"), id, NR_ELEMENTS(disploutOptions), displout, disploutOptions, value); + FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + selector.addFormSelector(F("Display Output"), id, value); } # ifdef P073_EXTRA_FONTS @@ -127,8 +127,8 @@ void P073_font_selector(const __FlashStringHelper *id, int16_t value) { F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7"), }; - - addFormSelector(F("Font set"), id, NR_ELEMENTS(fontset), fontset, nullptr, value); + FormSelectorOptions selector(NR_ELEMENTS(fontset), fontset); + selector.addFormSelector(F("Font set"), id, value); addFormNote(F("Check documentation for examples of the font sets.")); } diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index db6c497493..64b2a65b3d 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -1890,12 +1890,11 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { static_cast(MD_MAX72XX::moduleType_t::DR0CR1RR1_HW), static_cast(MD_MAX72XX::moduleType_t::DR1CR0RR1_HW) }; - addFormSelector(F("Hardware type"), - F("hardware"), - P104_hardwareTypeCount, - hardwareTypes, - hardwareOptions, - P104_CONFIG_HARDWARETYPE); + FormSelectorOptions selector( + P104_hardwareTypeCount, + hardwareTypes, + hardwareOptions); + selector.addFormSelector(F("Hardware type"), F("hardware"), P104_CONFIG_HARDWARETYPE); # ifdef P104_ADD_SETTINGS_NOTES addFormNote(F("DR = Digits as Rows, CR = Column Reversed, RR = Row Reversed; 0 = no, 1 = yes.")); # endif // ifdef P104_ADD_SETTINGS_NOTES @@ -1929,10 +1928,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { P104_DATE_FORMAT_US, P104_DATE_FORMAT_JP }; - addFormSelector(F("Date format"), F("datefmt"), - 3, - dateFormats, dateFormatOptions, - get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_FORMAT)); + FormSelectorOptions selector(3, dateFormats, dateFormatOptions); + selector.addFormSelector(F("Date format"), F("datefmt"), + get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_FORMAT)); } { // Date separator const __FlashStringHelper *dateSeparators[] = { @@ -1947,10 +1945,9 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { P104_DATE_SEPARATOR_DASH, P104_DATE_SEPARATOR_DOT }; - addFormSelector(F("Date separator"), F("datesep"), - 4, - dateSeparators, dateSeparatorOptions, - get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_SEP_CHAR)); + FormSelectorOptions selector(4, dateSeparators, dateSeparatorOptions); + selector.addFormSelector(F("Date separator"), F("datesep"), + get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_SEP_CHAR)); addFormCheckBox(F("Year uses 4 digits"), F("year4dgt"), bitRead(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_YEAR4DGT)); } @@ -1974,11 +1971,13 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { # endif // ifdef P104_USE_ZONE_ORDERING " will save and reload the page."); # endif // if defined(P104_USE_TOOLTIPS) || defined(P104_ADD_SETTINGS_NOTES) - addFormSelector(F("Zones"), F("zonecnt"), P104_MAX_ZONES, zonesList, zonesOptions, nullptr, P104_CONFIG_ZONE_COUNT, true - # ifdef P104_USE_TOOLTIPS - , zonetip - # endif // ifdef P104_USE_TOOLTIPS - ); + + FormSelectorOptions selector(P104_MAX_ZONES, zonesList, zonesOptions); + selector.reloadonchange = true; + # ifdef P104_USE_TOOLTIPS + selector.tooltip = zonetip; + #endif + selector.addFormSelector(F("Zones"), F("zonecnt"), P104_CONFIG_ZONE_COUNT); # ifdef P104_USE_ZONE_ORDERING const String orderTypes[] = { @@ -1986,12 +1985,13 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { F("Display order (n..1)") }; const int orderOptions[] = { 0, 1 }; - addFormSelector(F("Zone order"), F("zoneorder"), 2, orderTypes, orderOptions, nullptr, - bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0, true - # ifdef P104_USE_TOOLTIPS - , zonetip - # endif // ifdef P104_USE_TOOLTIPS - ); + FormSelectorOptions selector_zoneordering(2, orderTypes, orderOptions); + selector.reloadonchange = true; + # ifdef P104_USE_TOOLTIPS + selector.tooltip = zonetip; + #endif + selector_zoneordering.addFormSelector(F("Zone order"), F("zoneorder"), + bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0); # endif // ifdef P104_USE_ZONE_ORDERING # ifdef P104_ADD_SETTINGS_NOTES addFormNote(zonetip); diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index b1ae66dead..5f965f01f3 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -519,7 +519,8 @@ bool P120_data_struct::plugin_webform_loadOutputSelector(struct EventStruct *eve const int values[] = { 0, 1 }; const int choice = bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_ANGLE_IN_RAD); - addFormSelector(F("Angle Units"), F("angle_rad"), 2, options, values, choice); + FormSelectorOptions selector(2, options, values); + selector.addFormSelector(F("Angle Units"), F("angle_rad"), choice); } return true; } @@ -533,8 +534,9 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("8g"), F("16g (default)") }; int rangeValues[] = { P120_RANGE_2G, P120_RANGE_4G, P120_RANGE_8G, P120_RANGE_16G }; - addFormSelector(F("Range"), F("range"), 4, rangeOptions, rangeValues, - get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); + FormSelectorOptions selector(4, rangeOptions, rangeValues); + selector.addFormSelector(F("Range"), F("range"), + get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); } // Axis selection @@ -627,7 +629,8 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("10"), F("50") }; int frequencyValues[] = { P120_FREQUENCY_10, P120_FREQUENCY_50 }; - addFormSelector(F("Measuring frequency"), F("frequency"), 2, frequencyOptions, frequencyValues, P120_FREQUENCY); + 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.")); } diff --git a/src/src/PluginStructs/P148_data_struct.cpp b/src/src/PluginStructs/P148_data_struct.cpp index 9faa757a23..4828ba9ddf 100644 --- a/src/src/PluginStructs/P148_data_struct.cpp +++ b/src/src/PluginStructs/P148_data_struct.cpp @@ -158,10 +158,10 @@ void P148_data_struct::MonitorTaskValue_t::webformLoad(int index) const { static_cast(P148_data_struct::Tm1621UnitOfMeasure::kWh_Watt) }; constexpr size_t nrElements = sizeof(optionValues) / sizeof(optionValues[0]); - - addFormSelector( + FormSelectorOptions selector(nrElements, options, optionValues); + selector.addFormSelector( F("Unit Symbols"), concat(F("punit"), index), - nrElements, options, optionValues, static_cast(unit)); + static_cast(unit)); } else { const __FlashStringHelper *options[] = { F("None"), @@ -177,9 +177,10 @@ void P148_data_struct::MonitorTaskValue_t::webformLoad(int index) const { }; constexpr size_t nrElements = sizeof(optionValues) / sizeof(optionValues[0]); - addFormSelector( + FormSelectorOptions selector(nrElements, options, optionValues); + selector.addFormSelector( F("Unit Symbols"), concat(F("punit"), index), - nrElements, options, optionValues, static_cast(unit)); + static_cast(unit)); } } else { addFormCheckBox(F("Clear Line"), concat(F("pshowname"), index), showname); diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 48d4eebda8..dec63c704f 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -201,7 +201,8 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; const int stripOptionValues[] = { P165_STRIP_TYPE_RGB, P165_STRIP_TYPE_RGBW }; - addFormSelector(F("Strip Type"), F("stripe"), NR_ELEMENTS(stripOptionValues), stripOptions, stripOptionValues, P165_CONFIG_STRIP_TYPE); + FormSelectorOptions selector(NR_ELEMENTS(stripOptionValues), stripOptions, stripOptionValues); + selector.addFormSelector(F("Strip Type"), F("stripe"), P165_CONFIG_STRIP_TYPE); } if ((0 == P165_CONFIG_DEF_BRIGHT) && (0 == P165_CONFIG_MAX_BRIGHT)) { @@ -234,8 +235,8 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { P165_DISP_CLOCK12, P165_DISP_DATE, }; - addFormSelector(F("Display Output"), F("dspout"), NR_ELEMENTS(disploutOptions), - displout, disploutOptions, P165_CONFIG_OUTPUTTYPE); + FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + addFormSelector(F("Display Output"), F("dspout"), P165_CONFIG_OUTPUTTYPE); # endif // if P165_FEATURE_P073 int dgtCount = 0; @@ -283,13 +284,12 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { ? EMPTY_STRING : AdaGFXrgb565ToWebColor(P165_CONFIG_FG_COLOR); - addFormSelector(F("Number of Groups *"), - F("grps"), - NR_ELEMENTS(digitOptionValues), - digitOptions, - digitOptionValues, - grpCount, - true); + FormSelectorOptions selector( + NR_ELEMENTS(digitOptionValues), + digitOptions, + digitOptionValues); + selector.reloadonchange = true; + selector.addFormSelector(F("Number of Groups *"), F("grps"), grpCount); AdaGFXFormForeAndBackColors(F("fgcolor"), P165_CONFIG_FG_COLOR, diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index b972c3ea82..bff3a5ec0b 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -427,7 +427,8 @@ void addFormSeparatorCharInput(const __FlashStringHelper *rowLabel, charList[i + 1] = charset[i]; charOpts[i + 1] = static_cast(charset[i]); } - addFormSelector(rowLabel, id, len, charList, charOpts, value); + FormSelectorOptions selector(len, charList, charOpts); + selector.addFormSelector(rowLabel, id, value); if (!String(additionalText).isEmpty()) { addUnit(additionalText); @@ -506,6 +507,7 @@ void addFormSelectorI2C(const String& id, addSelector_Foot(); } +/* void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, @@ -620,6 +622,7 @@ void addFormSelector(const String & label, #endif selector.addFormSelector(label, id, selectedIndex); } +*/ void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index e6d544abbc..b20a26fad0 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -292,6 +292,8 @@ void addFormSelectorI2C(const String& id, #endif ); +/* + void addFormSelector(const String& label, const String& id, int optionCount, @@ -355,7 +357,7 @@ void addFormSelector(const String& label, const String& tooltip = EMPTY_STRING #endif ); - +*/ void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, From 8ee4fa8580cc5ddd614d73324a1c787e20776f38 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Jan 2025 23:18:06 +0100 Subject: [PATCH 40/50] [P139] Remove PMIC plugins from IRext builds --- src/src/CustomBuild/define_plugin_sets.h | 21 +++++++++++++-------- src/src/DataTypes/FormSelectorOptions.cpp | 4 +++- 2 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 2d9b7a603c..0f695283d5 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1535,15 +1535,20 @@ To create/register a plugin, you have to : #if !defined(USES_P095) && defined(ESP32) && !defined(PLUGIN_BUILD_IR_EXTENDED) #define USES_P095 // TFT ILI9xxx #endif - #if !defined(USES_P137) && defined(ESP32) - #define USES_P137 // AXP192 + #if !defined(PLUGIN_BUILD_NORMAL_IRext) + // IRext builds do need quite a lot of build space + // Also it is quite unlikely those are running from a battery powered unit + // which are the boards that need these power management ICs ("PMIC") + #if !defined(USES_P137) && defined(ESP32) + #define USES_P137 // AXP192 + #endif + #if !defined(USES_P138) && defined(ESP32) + #define USES_P138 // IP5306 + #endif + #if !defined(USES_P139) && defined(ESP32) + #define USES_P139 // AXP2101 + #endif #endif - #if !defined(USES_P138) && defined(ESP32) - #define USES_P138 // IP5306 - #endif - #if !defined(USES_P139) && defined(ESP32) - #define USES_P139 // AXP2101 - #endif #endif #ifdef PLUGIN_SET_COLLECTION_A diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index c65de55bd5..006ae24dfb 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -141,8 +141,10 @@ void FormSelectorOptions::addFormSelector( if ((i & 0x07) == 0) { delay(0); } } addSelector_Foot(); + if (reloadonchange) { addHtml(F("🔄")); -// addFormNote(F("Page will reload when selection is changed.")); + + // addFormNote(F("Page will reload when selection is changed.")); } } From 944d3145aa3e654cbcd34e7fbe600d2285cb1bc1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 00:19:18 +0100 Subject: [PATCH 41/50] [Cleanup] Fix build issue on ESP8266 --- src/src/PluginStructs/P165_data_struct.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index dec63c704f..616452062c 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -236,7 +236,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { P165_DISP_DATE, }; FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); - addFormSelector(F("Display Output"), F("dspout"), P165_CONFIG_OUTPUTTYPE); + selector.addFormSelector(F("Display Output"), F("dspout"), P165_CONFIG_OUTPUTTYPE); # endif // if P165_FEATURE_P073 int dgtCount = 0; From f2d004a134e3eb4c6b16cfc2dd7fcc02d1aea074 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 03:06:14 +0100 Subject: [PATCH 42/50] [Cleanup] Use new addSelector function of FormSelectorOptions class --- src/_P016_IR.ino | 15 +- src/_P036_FrameOLED.ino | 53 +++-- src/_P043_ClkOutput.ino | 11 +- src/_P053_PMSx003.ino | 23 +- src/_P087_SerialProxy.ino | 4 +- src/_P137_AXP192.ino | 13 +- src/src/DataTypes/FormSelectorOptions.cpp | 12 + src/src/DataTypes/FormSelectorOptions.h | 7 + src/src/Helpers/AdafruitGFX_helper.cpp | 4 +- src/src/Helpers/ESPEasy_TouchHandler.cpp | 93 ++++---- src/src/PluginStructs/P037_data_struct.cpp | 14 +- src/src/PluginStructs/P094_Filter.cpp | 24 +- src/src/PluginStructs/P104_data_struct.cpp | 252 ++++++++++----------- src/src/PluginStructs/P139_data_struct.cpp | 15 +- src/src/PluginStructs/P165_data_struct.cpp | 28 ++- src/src/WebServer/AccessControl.cpp | 7 +- src/src/WebServer/AdvancedConfigPage.cpp | 30 ++- src/src/WebServer/DevicesPage.cpp | 7 +- src/src/WebServer/HardwarePage.cpp | 8 +- 19 files changed, 340 insertions(+), 280 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index df10524aa7..c33b83a246 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -427,8 +427,11 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addHtmlInt(varNr + 1); // # html_TD(); { // Decode type - addSelector(getPluginCustomArgName(rowCnt + 0), protocolCount, &decodeTypes[0], &decodeTypeOptions[0], nullptr, - static_cast(line.CodeDecodeType), false, true, F("")); + FormSelectorOptions selector(protocolCount, &decodeTypes[0], &decodeTypeOptions[0]); + selector.classname = F(""); + selector.addSelector( + getPluginCustomArgName(rowCnt + 0), + static_cast(line.CodeDecodeType)); } html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 1), bitRead(line.CodeFlags, P16_FLAGS_REPEAT)); @@ -442,8 +445,12 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); { - addSelector(getPluginCustomArgName(rowCnt + 3), protocolCount, &decodeTypes[0], &decodeTypeOptions[0], nullptr, - static_cast(line.AlternativeCodeDecodeType), false, true, F("")); + FormSelectorOptions selector(protocolCount, &decodeTypes[0], &decodeTypeOptions[0]); + selector.classname = F(""); + + selector.addSelector( + getPluginCustomArgName(rowCnt + 3), + static_cast(line.AlternativeCodeDecodeType)); } html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 4), bitRead(line.AlternativeCodeFlags, P16_FLAGS_REPEAT)); diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 956a2db86e..8b13ffb7fe 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -557,31 +557,34 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) EMPTY_STRING, // pattern, F("xwide") // class name ); - html_TD(); // font - const uint8_t FontChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, P036_FLAG_ModifyLayout_Font); - addSelector(getPluginCustomArgName(varNr + 100), - 5, - optionsFont, - optionValuesFont, - nullptr, // attr[], - FontChoice, // selectedIndex, - false, // reloadonchange, - true, // enabled, - F("") // class name - ); - html_TD(); // alignment - const uint8_t AlignmentChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, - P036_FLAG_ModifyLayout_Alignment); - addSelector(getPluginCustomArgName(varNr + 200), - 4, - optionsAlignment, - optionValuesAlignment, - nullptr, // attr[], - AlignmentChoice, // selectedIndex, - false, // reloadonchange, - true, // enabled, - F("") // class name - ); + { + html_TD(); // font + + FormSelectorOptions selector( + 5, + optionsFont, + optionValuesFont); + selector.classname = F(""); + + const uint8_t FontChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, P036_FLAG_ModifyLayout_Font); + selector.addSelector( + getPluginCustomArgName(varNr + 100), + FontChoice); // selectedIndex, + } + { + html_TD(); // alignment + FormSelectorOptions selector( + 4, + optionsAlignment, + optionValuesAlignment); + selector.classname = F(""); + + const uint8_t AlignmentChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, + P036_FLAG_ModifyLayout_Alignment); + selector.addSelector( + getPluginCustomArgName(varNr + 200), + AlignmentChoice); // selectedIndex, + } } html_end_table(); } diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index 695c9e1c48..e95ec6993d 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -159,7 +159,11 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) int thisDay = weekDays.indexOf(timeStr.substring(0, 3)); if (thisDay > 0) { thisDay /= 3; } - addSelector(concat(F("day"), x), daysCount, days, nullptr, nullptr, thisDay, false, true, F("")); + FormSelectorOptions selector(daysCount, days); + selector.classname = F(""); + selector.addSelector( + concat(F("day"), x), + thisDay); addHtml(','); addTextBox(concat(F("clock"), x), parseString(timeStr, 2), 32 @@ -177,7 +181,10 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) if (validGpio(CONFIG_PIN1) || (P043_SIMPLE_VALUE == 1)) { addHtml(' '); const uint8_t choice = Cache.getTaskDevicePluginConfig(event->TaskIndex, x); - addSelector(concat(F("state"), x), optionsCount, options, nullptr, nullptr, choice); + FormSelectorOptions selector(optionsCount, options); + selector.addSelector( + concat(F("state"), x), + choice); } else { addHtml(strformat(F("Value %d:"), x + 1)); diff --git a/src/_P053_PMSx003.ino b/src/_P053_PMSx003.ino index 0b168edbd7..7aff9d293a 100644 --- a/src/_P053_PMSx003.ino +++ b/src/_P053_PMSx003.ino @@ -197,19 +197,16 @@ boolean Plugin_053(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef USES_P175 }; addRowLabel(F("Sensor model")); - addSelector(F("model"), - NR_ELEMENTS(unitModelOptions), - unitModels, - unitModelOptions, - nullptr, - PLUGIN_053_SENSOR_MODEL_SELECTOR, - false, - # ifdef USES_P175 - !P053_for_P175 - # else // ifdef USES_P175 - true - # endif // ifdef USES_P175 - ); + + FormSelectorOptions selector( + NR_ELEMENTS(unitModelOptions), + unitModels, + unitModelOptions); +# ifdef USES_P175 + selector.enabled = !P053_for_P175; +# endif // ifdef USES_P175 + + selector.addSelector(F("model"), PLUGIN_053_SENSOR_MODEL_SELECTOR); } addFormSubHeader(F("Output")); diff --git a/src/_P087_SerialProxy.ino b/src/_P087_SerialProxy.ino index 9a1552dc8f..f135dbc85e 100644 --- a/src/_P087_SerialProxy.ino +++ b/src/_P087_SerialProxy.ino @@ -387,7 +387,9 @@ void P087_html_show_matchForms(struct EventStruct *event) { options[P087_Filter_Comp::Equal] = F("=="); options[P087_Filter_Comp::NotEqual] = F("!="); const int optionValues[] = { P087_Filter_Comp::Equal, P087_Filter_Comp::NotEqual }; - addSelector(id, 2, options, optionValues, nullptr, static_cast(comparator), false, true, F("")); + FormSelectorOptions selector(2, options, optionValues); + selector.classname = F(""); + selector.addSelector(id, static_cast(comparator)); break; } case 2: diff --git a/src/_P137_AXP192.ino b/src/_P137_AXP192.ino index e9fcc85c26..f7244f96ee 100644 --- a/src/_P137_AXP192.ino +++ b/src/_P137_AXP192.ino @@ -271,14 +271,11 @@ boolean Plugin_137(uint8_t function, struct EventStruct *event, String& string) for (int i = 0; i < 5; ++i) { // GPIO0..4 const String id = concat(F("pgpio"), i); addRowLabel(concat(F("Initial state GPIO"), i)); - addSelector(id, optionCount, - bootStates, bootStateValues, bootStateAttributes, - get3BitFromUL(P137_CONFIG_FLAGS, i * 3), - false, !bitRead(P137_CONFIG_DISABLEBITS, i + 3), F("") - # if FEATURE_TOOLTIPS - , EMPTY_STRING - # endif // if FEATURE_TOOLTIPS - ); + FormSelectorOptions selector( + optionCount, bootStates, bootStateValues, bootStateAttributes); + selector.enabled = !bitRead(P137_CONFIG_DISABLEBITS, i + 3); + selector.classname = F(""); + selector.addSelector(id, get3BitFromUL(P137_CONFIG_FLAGS, i * 3)); if (bitRead(P137_CONFIG_DISABLEBITS, i + 3)) { addUnit(notConnected); diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 006ae24dfb..5408288047 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -111,7 +111,19 @@ void FormSelectorOptions::addFormSelector( int selectedIndex) const { addRowLabel_tr_id(label, id); + addSelector(id, selectedIndex); +} + + +void FormSelectorOptions::addSelector(const __FlashStringHelper *id, + int selectedIndex) const +{ + addSelector(String(id), selectedIndex); +} +void FormSelectorOptions::addSelector(const String& id, + int selectedIndex) const +{ // FIXME TD-er Change bool 'enabled' to disabled if (reloadonchange) { diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index 3db54431cc..e33f46568d 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -47,6 +47,13 @@ class FormSelectorOptions { int selectedIndex) const; + void addSelector(const __FlashStringHelper *id, + int selectedIndex) const; + + void addSelector(const String& id, + int selectedIndex) const; + + bool reloadonchange = false; bool enabled = true; const __FlashStringHelper * classname; diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index 02ab9eeabe..d4a4748e5b 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -269,7 +269,9 @@ void AdaGFXFormColorDepth(const __FlashStringHelper *id, }; addRowLabel_tr_id(F("Display Color-depth"), id); - addSelector(id, colorDepthCount, colorDepths, colorDepthOptions, NULL, selectedIndex, false, enabled); + FormSelectorOptions selector(colorDepthCount, colorDepths, colorDepthOptions); + selector.enabled = enabled; + selector.addSelector(id, selectedIndex); } /***************************************************************************************** diff --git a/src/src/Helpers/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index 50ea19898f..0048fe0254 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -890,7 +890,7 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { }; const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! FormSelectorOptions selector(NR_ELEMENTS(optionValues3), options3, optionValues3); - selector.addFormSelector(F("Events"), F("events"), choice3); + selector.addFormSelector(F("Events"), F("events"), choice3); addFormCheckBox(F("Draw buttons when started"), F("initobj"), bitRead(Touch_Settings.flags, TOUCH_FLAGS_INIT_OBJECTEVENT)); # ifndef LIMIT_BUILD_SIZE @@ -917,9 +917,9 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { addFormSubHeader(F("Calibration")); addFormSelector_YesNo(F("Calibrate to screen resolution"), - F("usecalib"), - Touch_Settings.calibrationEnabled ? 1 : 0, - true); // reloadonchange + F("usecalib"), + Touch_Settings.calibrationEnabled ? 1 : 0, + true); // reloadonchange if (Touch_Settings.calibrationEnabled) { addRowLabel(F("Calibration")); @@ -1231,27 +1231,39 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { ); html_TD(); // (on/off) button (type) # if TOUCH_FEATURE_EXTENDED_TOUCH - addSelector(getPluginCustomArgName(objectNr + 800), - sizeof(buttonTypeValues) / sizeof(int), - buttonTypeOptions, - buttonTypeValues, - nullptr, - get4BitFromUL(TouchObjects[objectNr].flags, TOUCH_OBJECT_FLAG_BUTTONTYPE), false, true, F("widenumber") - # if TOUCH_FEATURE_TOOLTIPS - , F("Button") - # endif // if TOUCH_FEATURE_TOOLTIPS - ); - html_TD(); // button alignment - addSelector(getPluginCustomArgName(objectNr + 900), - sizeof(buttonLayoutValues) / sizeof(int), - buttonLayoutOptions, - buttonLayoutValues, - nullptr, - get4BitFromUL(TouchObjects[objectNr].flags, TOUCH_OBJECT_FLAG_BUTTONALIGN) << 4, false, true, F("widenumber") - # if TOUCH_FEATURE_TOOLTIPS - , F("Layout") - # endif // if TOUCH_FEATURE_TOOLTIPS - ); + { + FormSelectorOptions selector( + NR_ELEMENTS(buttonTypeValues), + buttonTypeOptions, + buttonTypeValues); + selector.classname = F("widenumber"); +# if TOUCH_FEATURE_TOOLTIPS + selector.tooltip = F("Button"); +# endif // if TOUCH_FEATURE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(objectNr + 800), + get4BitFromUL( + TouchObjects[objectNr].flags, + TOUCH_OBJECT_FLAG_BUTTONTYPE)); + } + { + html_TD(); // button alignment + FormSelectorOptions selector( + NR_ELEMENTS(buttonLayoutOptions), + buttonLayoutOptions, + buttonLayoutValues); + selector.classname = F("widenumber"); +# if TOUCH_FEATURE_TOOLTIPS + selector.tooltip = F("Layout"); +# endif // if TOUCH_FEATURE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(objectNr + 900), + get4BitFromUL( + TouchObjects[objectNr].flags, + TOUCH_OBJECT_FLAG_BUTTONALIGN) << 4); + } # else // if TOUCH_FEATURE_EXTENDED_TOUCH addCheckBox(getPluginCustomArgName(objectNr + 600), bitRead(TouchObjects[objectNr].flags, TOUCH_OBJECT_FLAG_BUTTON), false @@ -1302,20 +1314,23 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { # endif // if TOUCH_FEATURE_TOOLTIPS , F("adagfx65kcolors") ); - html_TD(); // button action - addSelector(getPluginCustomArgName(objectNr + 2000), - sizeof(touchActionValues) / sizeof(int), - touchActionOptions, - touchActionValues, - nullptr, - get4BitFromUL(TouchObjects[objectNr].groupFlags, TOUCH_OBJECT_GROUP_ACTION), - false, - true, - F("widenumber") - # if TOUCH_FEATURE_TOOLTIPS - , F("Touch action") - # endif // if TOUCH_FEATURE_TOOLTIPS - ); + { + html_TD(); // button action + FormSelectorOptions selector( + NR_ELEMENTS(touchActionOptions), + touchActionOptions, + touchActionValues); + selector.classname = F("widenumber"); +# if TOUCH_FEATURE_TOOLTIPS + selector.tooltip = F("Touch action"); +# endif // if TOUCH_FEATURE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(objectNr + 2000), + get4BitFromUL( + TouchObjects[objectNr].groupFlags, + TOUCH_OBJECT_GROUP_ACTION)); + } # endif // if TOUCH_FEATURE_EXTENDED_TOUCH html_TR_TD(); // Start new row diff --git a/src/src/PluginStructs/P037_data_struct.cpp b/src/src/PluginStructs/P037_data_struct.cpp index 7b818b4b2d..fa72224b8c 100644 --- a/src/src/PluginStructs/P037_data_struct.cpp +++ b/src/src/PluginStructs/P037_data_struct.cpp @@ -290,7 +290,9 @@ bool P037_data_struct::webform_load( { html_TD(); filterIndex = filters.indexOf(parseString(valueArray[filterOffset], 2, P037_VALUE_SEPARATOR)); - addSelector(getPluginCustomArgName(idx + 100 + 1), P037_FILTER_COUNT, filterOptions, filterIndices, NULL, filterIndex); + + FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); + selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), parseStringKeepCase(valueArray[filterOffset], 3, P037_VALUE_SEPARATOR), @@ -327,7 +329,8 @@ bool P037_data_struct::webform_load( } { html_TD(); - addSelector(getPluginCustomArgName(idx + 100 + 1), P037_FILTER_COUNT, filterOptions, filterIndices, NULL, filterIndex); + FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); + selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), EMPTY_STRING, 32, false, false, EMPTY_STRING, EMPTY_STRING); @@ -400,7 +403,9 @@ bool P037_data_struct::webform_load( { html_TD(); operandIndex = operands.indexOf(parseString(valueArray[mappingOffset], 2, P037_VALUE_SEPARATOR)); - addSelector(getPluginCustomArgName(idx + 1), P037_OPERAND_COUNT, operandOptions, operandIndices, NULL, operandIndex); + + FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); + selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 2), parseStringKeepCase(valueArray[mappingOffset], 3, P037_VALUE_SEPARATOR), @@ -434,7 +439,8 @@ bool P037_data_struct::webform_load( } { html_TD(); - addSelector(getPluginCustomArgName(idx + 1), P037_OPERAND_COUNT, operandOptions, operandIndices, NULL, operandIndex); + FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); + selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 2), EMPTY_STRING, 32, false, false, EMPTY_STRING, F("")); diff --git a/src/src/PluginStructs/P094_Filter.cpp b/src/src/PluginStructs/P094_Filter.cpp index 5de2ec70c0..f922ce81cf 100644 --- a/src/src/PluginStructs/P094_Filter.cpp +++ b/src/src/PluginStructs/P094_Filter.cpp @@ -368,19 +368,17 @@ void P094_filter::WebformLoad(uint8_t filterIndex) const const P094_Filter_Window filterWindow = static_cast(optionValues[i]); options[i] = Filter_WindowToString(filterWindow); } - addSelector(P094_FILTER_WEBARG_FILTER_WINDOW(filterIndex), - nrOptions, - options, - optionValues, - nullptr, - _filter._filterWindow, - false, - true, - F("widenumber") -# if FEATURE_TOOLTIPS - , F("Filter Window") -# endif // if FEATURE_TOOLTIPS - ); + FormSelectorOptions selector( + nrOptions, + options, + optionValues); + selector.classname = F("widenumber"); + # if FEATURE_TOOLTIPS + selector.tooltip = F("Filter Window"); + # endif + selector.addSelector( + P094_FILTER_WEBARG_FILTER_WINDOW(filterIndex), + _filter._filterWindow); } } diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 64b2a65b3d..b6e59f9793 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -1930,7 +1930,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { }; FormSelectorOptions selector(3, dateFormats, dateFormatOptions); selector.addFormSelector(F("Date format"), F("datefmt"), - get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_FORMAT)); + get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_FORMAT)); } { // Date separator const __FlashStringHelper *dateSeparators[] = { @@ -1947,7 +1947,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { }; FormSelectorOptions selector(4, dateSeparators, dateSeparatorOptions); selector.addFormSelector(F("Date separator"), F("datesep"), - get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_SEP_CHAR)); + get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_SEP_CHAR)); addFormCheckBox(F("Year uses 4 digits"), F("year4dgt"), bitRead(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_YEAR4DGT)); } @@ -1976,8 +1976,8 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { selector.reloadonchange = true; # ifdef P104_USE_TOOLTIPS selector.tooltip = zonetip; - #endif - selector.addFormSelector(F("Zones"), F("zonecnt"), P104_CONFIG_ZONE_COUNT); + # endif // ifdef P104_USE_TOOLTIPS + selector.addFormSelector(F("Zones"), F("zonecnt"), P104_CONFIG_ZONE_COUNT); # ifdef P104_USE_ZONE_ORDERING const String orderTypes[] = { @@ -1987,11 +1987,11 @@ 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 + # ifdef P104_USE_TOOLTIPS selector.tooltip = zonetip; - #endif - selector_zoneordering.addFormSelector(F("Zone order"), F("zoneorder"), - bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0); + # endif // ifdef P104_USE_TOOLTIPS + selector_zoneordering.addFormSelector(F("Zone order"), F("zoneorder"), + bitRead(P104_CONFIG_FLAGS, P104_CONFIG_FLAG_ZONE_ORDER) ? 1 : 0); # endif // ifdef P104_USE_ZONE_ORDERING # ifdef P104_ADD_SETTINGS_NOTES addFormNote(zonetip); @@ -2323,44 +2323,36 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { false, EMPTY_STRING, F("")); - - html_TD(); // Content - addSelector(getPluginCustomArgName(index + P104_OFFSET_CONTENT), - P104_CONTENT_count, - contentTypes, - contentOptions, - nullptr, - zones[zone].content, - false, - true, - F("")); - - html_TD(); // Alignment - addSelector(getPluginCustomArgName(index + P104_OFFSET_ALIGNMENT), - 3, - alignmentTypes, - alignmentOptions, - nullptr, - zones[zone].alignment, - false, - true, - F("")); - + { + html_TD(); // Content + FormSelectorOptions selector( + P104_CONTENT_count, contentTypes, contentOptions); + selector.classname = F(""); + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_CONTENT), + zones[zone].content); + } + { + html_TD(); // Alignment + FormSelectorOptions selector(3, alignmentTypes, alignmentOptions); + selector.classname = F(""); + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_ALIGNMENT), + zones[zone].alignment); + } { html_TD(); // Animation In (without None by passing the second element index) - addSelector(getPluginCustomArgName(index + P104_OFFSET_ANIM_IN), - animationCount - 1, - &animationTypes[1], - &animationOptions[1], - nullptr, - zones[zone].animationIn, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Animation In") - # endif // ifdef P104_USE_TOOLTIPS - ); + FormSelectorOptions selector( + animationCount - 1, + &animationTypes[1], + &animationOptions[1]); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Animation In"); + # endif // ifdef P104_USE_TOOLTIPS + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_ANIM_IN), + zones[zone].animationIn); } html_TD(); // Speed In @@ -2370,37 +2362,36 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { , F("Speed") // title # endif // ifdef P104_USE_TOOLTIPS ); - - html_TD(); // Font - addSelector(getPluginCustomArgName(index + P104_OFFSET_FONT), - fontCount, - fontTypes, - fontOptions, - nullptr, - zones[zone].font, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Font") // title - # endif // ifdef P104_USE_TOOLTIPS - ); - - html_TD(); // Inverted - addSelector(getPluginCustomArgName(index + P104_OFFSET_INVERTED), - invertedCount, - invertedTypes, - invertedOptions, - nullptr, - zones[zone].inverted, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Inverted") // title - # endif // ifdef P104_USE_TOOLTIPS - ); - + { + html_TD(); // Font + FormSelectorOptions selector( + fontCount, + fontTypes, + fontOptions); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Font"); + # endif // ifdef P104_USE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_FONT), + zones[zone].font); + } + { + html_TD(); // Inverted + FormSelectorOptions selector( + invertedCount, + invertedTypes, + invertedOptions); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Inverted"); + # endif // ifdef P104_USE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_INVERTED), + zones[zone].inverted); + } html_TD(3); // Fill columns # ifdef P104_USE_ZONE_ACTIONS @@ -2427,19 +2418,18 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { { html_TD(); // Animation Out - addSelector(getPluginCustomArgName(index + P104_OFFSET_ANIM_OUT), - animationCount, - animationTypes, - animationOptions, - nullptr, - zones[zone].animationOut, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Animation Out") - # endif // ifdef P104_USE_TOOLTIPS - ); + FormSelectorOptions selector( + animationCount, + animationTypes, + animationOptions); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Animation Out"); + # endif // ifdef P104_USE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_ANIM_OUT), + zones[zone].animationOut); } html_TD(); // Pause after Animation In @@ -2449,37 +2439,36 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { , F("Pause") // title # endif // ifdef P104_USE_TOOLTIPS ); - - html_TD(); // Layout - addSelector(getPluginCustomArgName(index + P104_OFFSET_LAYOUT), - layoutCount, - layoutTypes, - layoutOptions, - nullptr, - zones[zone].layout, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Layout") // title - # endif // ifdef P104_USE_TOOLTIPS - ); - - html_TD(); // Special effects - addSelector(getPluginCustomArgName(index + P104_OFFSET_SPEC_EFFECT), - specialEffectCount, - specialEffectTypes, - specialEffectOptions, - nullptr, - zones[zone].specialEffect, - false, - true, - F("") - # ifdef P104_USE_TOOLTIPS - , F("Special Effects") // title - # endif // ifdef P104_USE_TOOLTIPS - ); - + { + html_TD(); // Layout + FormSelectorOptions selector( + layoutCount, + layoutTypes, + layoutOptions); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Layout"); + # endif // ifdef P104_USE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_LAYOUT), + zones[zone].layout); + } + { + html_TD(); // Special effects + FormSelectorOptions selector( + specialEffectCount, + specialEffectTypes, + specialEffectOptions); + selector.classname = F(""); + # ifdef P104_USE_TOOLTIPS + selector.tooltip = F("Special Effects"); + # endif // ifdef P104_USE_TOOLTIPS + + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_SPEC_EFFECT), + zones[zone].specialEffect); + } html_TD(); // Offset addNumericBox(getPluginCustomArgName(index + P104_OFFSET_OFFSET), zones[zone].offset, 0, 254); @@ -2500,19 +2489,20 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { ); # ifdef P104_USE_ZONE_ACTIONS - html_TD(); // Spacer + html_TD(); // Spacer addHtml('|'); - - html_TD(); // Action - addSelector(getPluginCustomArgName(index + P104_OFFSET_ACTION), - actionCount, - actionTypes, - actionOptions, - nullptr, - P104_ACTION_NONE, // Always start with None - true, - true, - F("")); + { + html_TD(); // Action + FormSelectorOptions selector( + actionCount, + actionTypes, + actionOptions); + selector.classname = F(""); + selector.reloadonchange = true; + selector.addSelector( + getPluginCustomArgName(index + P104_OFFSET_ACTION), + P104_ACTION_NONE); // Always start with None + } # endif // ifdef P104_USE_ZONE_ACTIONS delay(0); diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 2c0359ad37..7d67fa1216 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -240,6 +240,12 @@ void P139_data_struct::webform_load(struct EventStruct *event) { // Don't include Disabled or Protected here, not user-selectable constexpr int bootStatesCount = NR_ELEMENTS(bootStateValues); + FormSelectorOptions selector( + bootStatesCount, + bootStates, + bootStateValues); + + addRowLabel(F("Output ports")); html_table(EMPTY_STRING); @@ -264,12 +270,9 @@ void P139_data_struct::webform_load(struct EventStruct *event) { if (AXP2101_isPinProtected(pin)) { addUnit(toString(pin)); } else { - addSelector(concat(F("ps"), toString(reg, false)), - bootStatesCount, - bootStates, - bootStateValues, - nullptr, - static_cast(pin)); + selector.addSelector( + concat(F("ps"), toString(reg, false)), + static_cast(pin)); } } diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 616452062c..e310ccace0 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -355,12 +355,15 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { html_table(F("")); addRowLabel(F("Number of Digits *")); - addSelector(concat(F("dgts"), grp10), - NR_ELEMENTS(digitOptionValues), - digitOptions, - digitOptionValues, nullptr, - grpDgts, - true, !numberPlan); // 1st and 2nd column + FormSelectorOptions selector( + NR_ELEMENTS(digitOptionValues), + digitOptions, + digitOptionValues); + selector.reloadonchange = true; + selector.enabled = !numberPlan; // 1st and 2nd column + selector.addSelector( + concat(F("dgts"), grp10), + grpDgts); { // 3rd column = "Digit " / "(Extra)" for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { @@ -477,12 +480,13 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { const uint8_t strt = grpStart + grpGstrt << 1; addRowLabel(F("Starting segment")); - addSelector(concat(F("strt"), grp10), - NR_ELEMENTS(startPixelOptions), - startPixelOptions, - nullptr, nullptr, - strt, false, - !numberPlan); + FormSelectorOptions selector( + NR_ELEMENTS(startPixelOptions), + startPixelOptions); + selector.enabled = !numberPlan; + selector.addSelector( + concat(F("strt"), grp10), + strt); addFormCheckBox(F("Split g-segment pixels"), concat(F("spltg"), grp10), P165_GET_CONFIG_SPLTG(grp), numberPlan || grpGstrt); diff --git a/src/src/WebServer/AccessControl.cpp b/src/src/WebServer/AccessControl.cpp index 2723391ad0..56e598ac95 100644 --- a/src/src/WebServer/AccessControl.cpp +++ b/src/src/WebServer/AccessControl.cpp @@ -1,5 +1,7 @@ #include "../WebServer/AccessControl.h" +#include "../DataTypes/FormSelectorOptions.h" + #include "../ESPEasyCore/ESPEasy_Log.h" #include "../ESPEasyCore/ESPEasyNetwork.h" #include "../ESPEasyCore/ESPEasyWifi.h" @@ -142,7 +144,8 @@ void clearAccessBlock() // ******************************************************************************** void addIPaccessControlSelect(const String& name, int choice) { - const __FlashStringHelper * options[3] = { F("Allow All"), F("Allow Local Subnet"), F("Allow IP range") }; + const __FlashStringHelper * options[] = { F("Allow All"), F("Allow Local Subnet"), F("Allow IP range") }; - addSelector(name, 3, options, nullptr, nullptr, choice); + FormSelectorOptions selector(NR_ELEMENTS(options), options); + selector.addSelector(name, choice); } diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 6bd61831c9..9ee39350a0 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -420,18 +420,21 @@ void addFormDstSelect(bool isStart, uint16_t choice) { addRowLabel(concat( isStart ? F("Start") : F("End"), F(" (week, dow, month)"))); - addSelector( + + FormSelectorOptions selector(NR_ELEMENTS(weekValues), week, weekValues); + selector.addSelector( isStart ? F("dststartweek") : F("dstendweek"), - NR_ELEMENTS(weekValues), week, weekValues, nullptr, rule.week); + rule.week); } html_BR(); { const __FlashStringHelper * dow[] = { F("Sun"), F("Mon"), F("Tue"), F("Wed"), F("Thu"), F("Fri"), F("Sat") }; constexpr int dowValues[] = { 1, 2, 3, 4, 5, 6, 7 }; - addSelector( + FormSelectorOptions selector(NR_ELEMENTS(dowValues), dow, dowValues); + selector.addSelector( isStart ? F("dststartdow") : F("dstenddow"), - NR_ELEMENTS(dowValues), dow, dowValues, nullptr, rule.dow); + rule.dow); } html_BR(); { @@ -439,8 +442,10 @@ void addFormDstSelect(bool isStart, uint16_t choice) { "Dec") }; constexpr int monthValues[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - addSelector(isStart ? F("dststartmonth") : F("dstendmonth"), - NR_ELEMENTS(monthValues), month, monthValues, nullptr, rule.month); + FormSelectorOptions selector(NR_ELEMENTS(monthValues), month, monthValues); + selector.addSelector( + isStart ? F("dststartmonth") : F("dstendmonth"), + rule.month); } addFormNumericBox( @@ -463,7 +468,8 @@ void addFormExtTimeSourceSelect(const __FlashStringHelper * label, const __Flash static_cast(ExtTimeSource_e::PCF8563) }; - addSelector(id, NR_ELEMENTS(optionValues), options, optionValues, nullptr, static_cast(choice)); + FormSelectorOptions selector(NR_ELEMENTS(optionValues), options, optionValues); + selector.addSelector(id, static_cast(choice)); } @@ -482,19 +488,21 @@ void addFormLogLevelSelect(LabelType::Enum label, int choice) for (int i = 0; i < LOG_LEVEL_NRELEMENTS; ++i) { options[i + 1] = getLogLevelDisplayStringFromIndex(i, optionValues[i + 1]); } - addSelector(getInternalLabel(label), LOG_LEVEL_NRELEMENTS + 1, options, optionValues, nullptr, choice); + FormSelectorOptions selector(LOG_LEVEL_NRELEMENTS + 1, options, optionValues); + selector.addSelector(getInternalLabel(label), choice); } void addFormLogFacilitySelect(const __FlashStringHelper * label, const __FlashStringHelper * id, int choice) { addRowLabel(label); - const __FlashStringHelper * options[12] = + const __FlashStringHelper * options[] = { F("Kernel"), F("User"), F("Daemon"), F("Message"), F("Local0"), F("Local1"), F("Local2"), F("Local3"), F("Local4"), F("Local5"), F("Local6"), F("Local7") }; - const int optionValues[12] = { 0, 1, 3, 5, 16, 17, 18, 19, 20, 21, 22, 23 }; + const int optionValues[] = { 0, 1, 3, 5, 16, 17, 18, 19, 20, 21, 22, 23 }; - addSelector(id, 12, options, optionValues, nullptr, choice); + FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + selector.addSelector(id, choice); } #endif // ifdef WEBSERVER_ADVANCED diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index e7652d06b3..052f471364 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1554,12 +1554,9 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde selected += 4; } - addSelector( + FormSelectorOptions selector(NR_ELEMENTS(chartAxis), chartAxis); + selector.addSelector( getPluginCustomArgName(F("TDSA"), varNr), - NR_ELEMENTS(chartAxis), - chartAxis, - nullptr, - nullptr, selected); } # endif // if FEATURE_PLUGIN_STATS diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index 5e3312276e..a870bebc01 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -244,7 +244,8 @@ void handle_hardware() { toString(NetworkMedium_t::WIFI), toString(NetworkMedium_t::Ethernet) }; - addSelector(F("ethwifi"), 2, ethWifiOptions, nullptr, nullptr, static_cast(Settings.NetworkMedium), false, true); + FormSelectorOptions selector(2, ethWifiOptions); + selector.addSelector(F("ethwifi"), static_cast(Settings.NetworkMedium)); } addFormNote(F("Change Switch between WiFi and Ethernet requires reboot to activate")); { @@ -356,13 +357,14 @@ void handle_hardware() { #if CONFIG_ETH_USE_ESP32_EMAC addRowLabel_tr_id(F("Ethernet Clock"), F("ethclock")); { - const __FlashStringHelper * ethClockOptions[4] = { + const __FlashStringHelper * ethClockOptions[] = { toString(EthClockMode_t::Ext_crystal_osc), toString(EthClockMode_t::Int_50MHz_GPIO_0), toString(EthClockMode_t::Int_50MHz_GPIO_16), toString(EthClockMode_t::Int_50MHz_GPIO_17_inv) }; - addSelector(F("ethclock"), 4, ethClockOptions, nullptr, nullptr, static_cast(Settings.ETH_Clock_Mode), false, true); + FormSelectorOptions selector(NR_ELEMENTS(ethClockOptions), ethClockOptions); + selector.addSelector(F("ethclock"), static_cast(Settings.ETH_Clock_Mode)); } #endif #endif // if FEATURE_ETHERNET From ec9172ed49d269b97214b5f72247273ca6f01225 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 03:16:07 +0100 Subject: [PATCH 43/50] [Cleanup] Remove no longer used markup functions --- src/src/WebServer/Markup.cpp | 162 ----------------------------------- src/src/WebServer/Markup.h | 85 ------------------ src/src/WebServer/Rules.cpp | 15 ++-- 3 files changed, 7 insertions(+), 255 deletions(-) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index b73bf55fb7..f5a1788ec2 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -15,168 +15,6 @@ #include "../../ESPEasy_common.h" -// ******************************************************************************** -// Add Selector -// ******************************************************************************** -void addSelector(const __FlashStringHelper *id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(String(id), optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled) -{ - addSelector(id, optionCount, options, indices, attr, selectedIndex, reloadonchange, enabled, F("wide")); -} - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String & tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_reloadOnChange( - const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const String& onChangeCall, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - do_addSelector_Head(id, classname, onChangeCall, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - - -void addSelector(const String & id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - // FIXME TD-er Change bool to disabled - if (reloadonchange) - { - addSelector_Head_reloadOnChange(id, classname, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } else { - do_addSelector_Head(id, classname, EMPTY_STRING, !enabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); - } - addSelector_options(optionCount, options, indices, attr, selectedIndex); - addSelector_Foot(); -} - -void addSelector_options(int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - -void addSelector_options(int optionCount, const String options[], const int indices[], const String attr[], int selectedIndex) -{ - for (uint8_t x = 0; x < optionCount; ++x) - { - const int index = indices ? indices[x] : x; - addSelector_Item( - options[x], - index, - selectedIndex == index, - false, - attr ? attr[x] : EMPTY_STRING); - if ((x & 0x07) == 0) delay(0); - } -} - void addSelector_Head(const String& id) { do_addSelector_Head(id, F("wide"), EMPTY_STRING, false #if FEATURE_TOOLTIPS diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index 46ae74d2e9..3b719d7115 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -11,91 +11,6 @@ // ******************************************************************************** // Add Selector // ******************************************************************************** -void addSelector(const __FlashStringHelper *id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange = false, - bool enabled = true); - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange = false, - bool enabled = true); - -void addSelector(const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange = false, - bool enabled = true); - - -void addSelector(const String & id, - int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String & tooltip = EMPTY_STRING - #endif // if FEATURE_TOOLTIPS - ); - -void addSelector(const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif // if FEATURE_TOOLTIPS - ); - -void addSelector_reloadOnChange( - const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - const String& onChangeCall, - bool enabled, - const __FlashStringHelper * classname - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif // if FEATURE_TOOLTIPS - ); - - -void addSelector_options(int optionCount, - const __FlashStringHelper *options[], - const int indices[], - const String attr[], - int selectedIndex); -void addSelector_options(int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex); void addSelector_Head(const String& id); diff --git a/src/src/WebServer/Rules.cpp b/src/src/WebServer/Rules.cpp index 28a5ce04cb..37a466d846 100644 --- a/src/src/WebServer/Rules.cpp +++ b/src/src/WebServer/Rules.cpp @@ -81,16 +81,15 @@ void handle_rules() { optionValues[x] = x + 1; } - addSelector_reloadOnChange( - F("set"), + FormSelectorOptions selector( RULESETS_MAX, options, - optionValues, - nullptr, - choice, - F("return rules_set_onchange(rulesselect)"), - true, - F("wide")); + optionValues); + + selector.onChangeCall = F("return rules_set_onchange(rulesselect)"); + selector.addSelector( + F("set"), + choice); addHelpButton(F("Tutorial_Rules")); addRTDHelpButton(F("Rules/Rules.html")); } From 314fa1710f493b3c7be147b32f29f9e40c9a7c93 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 18:11:34 +0100 Subject: [PATCH 44/50] [Cleanup] Add "reloadOnChange" icon for all occasions change will reload --- src/src/DataTypes/FormSelectorOptions.cpp | 8 +- src/src/WebServer/ControllerPage.cpp | 2 +- src/src/WebServer/CustomPage.cpp | 2 +- src/src/WebServer/DevicesPage.cpp | 2 +- src/src/WebServer/FactoryResetPage.cpp | 2 +- src/src/WebServer/Markup.cpp | 5 +- src/src/WebServer/Markup.h | 2 +- src/src/WebServer/Markup_Forms.cpp | 117 ---------------------- src/src/WebServer/Markup_Forms.h | 67 ------------- src/src/WebServer/NotificationPage.cpp | 2 +- 10 files changed, 11 insertions(+), 198 deletions(-) diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 5408288047..5221c84f64 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -152,11 +152,5 @@ void FormSelectorOptions::addSelector(const String& id, if ((i & 0x07) == 0) { delay(0); } } - addSelector_Foot(); - - if (reloadonchange) { - addHtml(F("🔄")); - - // addFormNote(F("Page will reload when selection is changed.")); - } + addSelector_Foot(reloadonchange); } diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 72b6a8e3ad..93752aff43 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -318,7 +318,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex disabled); ++protocolIndex; } - addSelector_Foot(); + addSelector_Foot(true); addHelpButton(F("EasyProtocols")); diff --git a/src/src/WebServer/CustomPage.cpp b/src/src/WebServer/CustomPage.cpp index b1224c6ceb..59db449fb5 100644 --- a/src/src/WebServer/CustomPage.cpp +++ b/src/src/WebServer/CustomPage.cpp @@ -98,7 +98,7 @@ bool handle_custom(const String& path) { addSelector_Item(name, it->first, choice == it->first); } } - addSelector_Foot(); + addSelector_Foot(true); // create <> navigation buttons uint8_t prev = Settings.Unit; diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index 052f471364..a357bd01cc 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -252,7 +252,7 @@ void addDeviceSelect(const __FlashStringHelper *name, pluginID_t choice) ++x; } - addSelector_Foot(); + addSelector_Foot(true); } // ******************************************************************************** diff --git a/src/src/WebServer/FactoryResetPage.cpp b/src/src/WebServer/FactoryResetPage.cpp index ee1f2ffe38..290616467b 100644 --- a/src/src/WebServer/FactoryResetPage.cpp +++ b/src/src/WebServer/FactoryResetPage.cpp @@ -131,7 +131,7 @@ void addPreDefinedConfigSelector() { model == active_model); } } - addSelector_Foot(); + addSelector_Foot(true); } #ifdef WEBSERVER_NEW_UI diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index f5a1788ec2..cd58a1f23e 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -255,9 +255,12 @@ void addSelector_Item(const String& option, int index, bool selected, bool addHtml(F("")); } -void addSelector_Foot() +void addSelector_Foot(bool reloadonchange) { addHtml(F("")); + if (reloadonchange) { + addHtml(F("🔄")); + } } void addUnit(const __FlashStringHelper *unit) diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index 3b719d7115..83ec9ad257 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -65,7 +65,7 @@ void addSelector_Item(const String& option, bool disabled = false, const String& attr = EMPTY_STRING); -void addSelector_Foot(); +void addSelector_Foot(bool reloadonchange = false); void addUnit(const __FlashStringHelper *unit); void addUnit(const String& unit); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index bff3a5ec0b..298afd0f24 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -507,123 +507,6 @@ void addFormSelectorI2C(const String& id, addSelector_Foot(); } -/* -void addFormSelector(const __FlashStringHelper * label, - const __FlashStringHelper * id, - int optionCount, - const int indices[], - int selectedIndex, - bool reloadonchange) -{ - FormSelectorOptions selector(optionCount, indices); - selector.reloadonchange = reloadonchange; - selector.addFormSelector(label, id, selectedIndex); -} - -void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange) -{ - addFormSelector(String(label), String(id), optionCount, options, indices, nullptr, selectedIndex, reloadonchange); -} - -void addFormSelector(const __FlashStringHelper * label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange) -{ - addFormSelector(String(label), id, optionCount, options, indices, nullptr, selectedIndex, reloadonchange); -} - -void addFormSelector(const String& label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex) -{ - addFormSelector(label, id, optionCount, options, indices, nullptr, selectedIndex, false); -} - -void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const String options[], const int indices[], int selectedIndex) -{ - addFormSelector(String(label), String(id), optionCount, options, indices, nullptr, selectedIndex, false); -} - -void addFormSelector(const String & label, - const String & id, - int optionCount, - const String options[], - const int indices[], - int selectedIndex - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addFormSelector(label, id, optionCount, options, indices, nullptr, selectedIndex, false - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - int selectedIndex, - bool reloadonchange) -{ - addFormSelector(label, id, optionCount, options, indices, nullptr, selectedIndex, reloadonchange); -} - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange) -{ - FormSelectorOptions selector(optionCount, options, indices, attr); - selector.reloadonchange = reloadonchange; - selector.addFormSelector(label, id, selectedIndex); -} - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const String options[], - const int indices[], - int selectedIndex, - bool reloadonchange - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - addFormSelector(label, id, optionCount, options, indices, nullptr, selectedIndex, reloadonchange - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); -} - -void addFormSelector(const String & label, - const String & id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange - #if FEATURE_TOOLTIPS - , const String& tooltip - #endif // if FEATURE_TOOLTIPS - ) -{ - FormSelectorOptions selector(optionCount, options, indices, attr); - selector.reloadonchange = reloadonchange; -#if FEATURE_TOOLTIPS - selector.tooltip = tooltip; -#endif - selector.addFormSelector(label, id, selectedIndex); -} -*/ - void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, int selectedIndex, diff --git a/src/src/WebServer/Markup_Forms.h b/src/src/WebServer/Markup_Forms.h index b20a26fad0..a467dc3200 100644 --- a/src/src/WebServer/Markup_Forms.h +++ b/src/src/WebServer/Markup_Forms.h @@ -292,73 +292,6 @@ void addFormSelectorI2C(const String& id, #endif ); -/* - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const String options[], - const int indices[], - int selectedIndex - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif - ); - -void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const int indices[], int selectedIndex, bool reloadonchange = false); - -void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange = false); -void addFormSelector(const __FlashStringHelper * label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex, bool reloadonchange = false); -void addFormSelector(const String& label, const String& id, int optionCount, const __FlashStringHelper * options[], const int indices[], int selectedIndex); -void addFormSelector(const __FlashStringHelper * label, const __FlashStringHelper * id, int optionCount, const String options[], const int indices[], int selectedIndex); - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - int selectedIndex, - bool reloadonchange); - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const __FlashStringHelper * options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange); - - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const String options[], - const int indices[], - int selectedIndex, - bool reloadonchange - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif - ); - -void addFormSelector(const String& label, - const String& id, - int optionCount, - const String options[], - const int indices[], - const String attr[], - int selectedIndex, - bool reloadonchange - #if FEATURE_TOOLTIPS - , - const String& tooltip = EMPTY_STRING - #endif - ); -*/ - void addFormSelector_YesNo(const __FlashStringHelper * label, const __FlashStringHelper * id, int selectedIndex, diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index 79f94e4f96..f5e8309010 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -205,7 +205,7 @@ void handle_notifications() { Notification[x].Number, choice == Notification[x].Number); } - addSelector_Foot(); + addSelector_Foot(true); addHelpButton(F("EasyNotifications")); addRTDHelpButton(F("Notify/_Notifications.html")); From 2b1e79334a1e21b819985a18a6b0f2b597ba6a87 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 18:29:14 +0100 Subject: [PATCH 45/50] [Cleanup] Move duplicate 'tooltip' code to separate function --- src/src/WebServer/Markup.cpp | 42 ++++++++++++------------------ src/src/WebServer/Markup.h | 4 +++ src/src/WebServer/Markup_Forms.cpp | 5 +--- 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index cd58a1f23e..100238fb4c 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -15,6 +15,17 @@ #include "../../ESPEasy_common.h" + +#if FEATURE_TOOLTIPS +void addTooltip(const String& tooltip) +{ + if (tooltip.length() > 0) { + addHtmlAttribute(F("title"), tooltip); + } +} +#endif + + void addSelector_Head(const String& id) { do_addSelector_Head(id, F("wide"), EMPTY_STRING, false #if FEATURE_TOOLTIPS @@ -72,10 +83,7 @@ void do_addSelector_Head(const String& id, const __FlashStringHelper * classname addHtmlAttribute(F("id"), id); #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS if (disabled) { @@ -491,10 +499,7 @@ void addCheckBox(const String& id, bool checked, bool disabled if (disabled) { addDisabled(); } addHtml('\''); #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS addHtml(F(">")); } @@ -525,10 +530,7 @@ void addNumericBox(const String& id, int value, int min, int max addHtmlAttribute(F("id"), id); #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS if (disabled) { @@ -593,11 +595,7 @@ void addFloatNumberBox(const String& id, float value, float min, float max, unsi addHtmlFloat(value, nrDecimals); #if FEATURE_TOOLTIPS - - if (!tooltip.isEmpty()) { - addHtml(strformat( - F("title='%s' "), tooltip.c_str())); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS addHtml('>'); } @@ -653,10 +651,7 @@ void addTextBox(const String & id, } #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS addHtml('>'); } @@ -705,10 +700,7 @@ void addTextArea(const String & id, } #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS addHtml('>'); addHtml(value); diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index 83ec9ad257..1182660f60 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -8,6 +8,10 @@ #include "../Globals/Plugins.h" #include "../Helpers/StringGenerator_GPIO.h" +#if FEATURE_TOOLTIPS +void addTooltip(const String& tooltip); +#endif + // ******************************************************************************** // Add Selector // ******************************************************************************** diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 298afd0f24..488432341c 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -339,10 +339,7 @@ void addFormPasswordBox(const String& label, const String& id, const String& pas addHtmlAttribute(F("maxlength"), maxlength); #if FEATURE_TOOLTIPS - - if (tooltip.length() > 0) { - addHtmlAttribute(F("title"), tooltip); - } + addTooltip(tooltip); #endif // if FEATURE_TOOLTIPS addHtmlAttribute(F("value"), (password.length() == 0) ? F("") : F("*****")); addHtml('>'); From 4801202a06563785d9272a6373338411160be501 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 21:08:49 +0100 Subject: [PATCH 46/50] [Cleanup] Add tooltip about ReloadOnChange icon --- src/src/WebServer/Markup.cpp | 32 ++++++++++++++++++++---------- src/src/WebServer/Markup_Forms.cpp | 4 ++-- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index 100238fb4c..e3dbfae293 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -27,11 +27,7 @@ void addTooltip(const String& tooltip) void addSelector_Head(const String& id) { - do_addSelector_Head(id, F("wide"), EMPTY_STRING, false - #if FEATURE_TOOLTIPS - , F("") - #endif // if FEATURE_TOOLTIPS - ); + do_addSelector_Head(id, F("wide"), EMPTY_STRING, false); } void addSelector_Head_reloadOnChange(const __FlashStringHelper * id) { @@ -51,14 +47,21 @@ void addSelector_Head_reloadOnChange(const String& id, , const String& tooltip #endif // if FEATURE_TOOLTIPS ) { - do_addSelector_Head(id, classname, F("return dept_onchange(frmselect)"), disabled - #if FEATURE_TOOLTIPS - , tooltip - #endif // if FEATURE_TOOLTIPS - ); + addSelector_Head_reloadOnChange( + id, + classname, + F("return dept_onchange(frmselect)"), + disabled +#if FEATURE_TOOLTIPS + , tooltip +#endif // if FEATURE_TOOLTIPS + ); } -void addSelector_Head_reloadOnChange(const String& id, const __FlashStringHelper * classname, const String& onChangeCall, bool disabled +void addSelector_Head_reloadOnChange(const String& id, + const __FlashStringHelper * classname, + const String& onChangeCall, + bool disabled #if FEATURE_TOOLTIPS , const String& tooltip #endif // if FEATURE_TOOLTIPS @@ -267,7 +270,13 @@ void addSelector_Foot(bool reloadonchange) { addHtml(F("")); if (reloadonchange) { +#if FEATURE_TOOLTIPS + addHtml(F("🔄")); +#else addHtml(F("🔄")); +#endif } } @@ -287,6 +296,7 @@ void addUnit(const String& unit) void addUnit(char unit) { + if (unit == '\0') return; addHtml(F(" [")); addHtml(unit); addHtml(']'); diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 488432341c..74360efa80 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -159,9 +159,9 @@ void addFormNumericBox(const String& label, const String& id, int value, int min ) { addRowLabel_tr_id(label, id); - addNumericBox(id, value, min, max + addNumericBox(id, value, min, max, F("widenumber") #if FEATURE_TOOLTIPS - , F("widenumber"), tooltip + , tooltip #endif // if FEATURE_TOOLTIPS , disabled ); From e1b7fbc1b1e8bc385f6b3e39a943a91adb6be1fb Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 26 Jan 2025 21:22:23 +0100 Subject: [PATCH 47/50] [Cleanup] Reduce build size when using empty class name on FormSelector --- src/_P016_IR.ino | 4 ++-- src/_P036_FrameOLED.ino | 4 ++-- src/_P043_ClkOutput.ino | 2 +- src/_P087_SerialProxy.ino | 2 +- src/_P137_AXP192.ino | 2 +- src/src/DataTypes/FormSelectorOptions.cpp | 5 +++++ src/src/DataTypes/FormSelectorOptions.h | 2 ++ src/src/PluginStructs/P104_data_struct.cpp | 16 ++++++++-------- 8 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index c33b83a246..bda4899e81 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -428,7 +428,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); { // Decode type FormSelectorOptions selector(protocolCount, &decodeTypes[0], &decodeTypeOptions[0]); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector( getPluginCustomArgName(rowCnt + 0), static_cast(line.CodeDecodeType)); @@ -446,7 +446,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); { FormSelectorOptions selector(protocolCount, &decodeTypes[0], &decodeTypeOptions[0]); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector( getPluginCustomArgName(rowCnt + 3), diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 8b13ffb7fe..533d5e0a47 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -564,7 +564,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) 5, optionsFont, optionValuesFont); - selector.classname = F(""); + selector.clearClassName(); const uint8_t FontChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, P036_FLAG_ModifyLayout_Font); selector.addSelector( @@ -577,7 +577,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) 4, optionsAlignment, optionValuesAlignment); - selector.classname = F(""); + selector.clearClassName(); const uint8_t AlignmentChoice = get3BitFromUL(P036_lines.DisplayLinesV1[varNr].ModifyLayout, P036_FLAG_ModifyLayout_Alignment); diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index e95ec6993d..aec57b8f95 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -160,7 +160,7 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) if (thisDay > 0) { thisDay /= 3; } FormSelectorOptions selector(daysCount, days); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector( concat(F("day"), x), thisDay); diff --git a/src/_P087_SerialProxy.ino b/src/_P087_SerialProxy.ino index f135dbc85e..1d5a1ce041 100644 --- a/src/_P087_SerialProxy.ino +++ b/src/_P087_SerialProxy.ino @@ -388,7 +388,7 @@ void P087_html_show_matchForms(struct EventStruct *event) { options[P087_Filter_Comp::NotEqual] = F("!="); const int optionValues[] = { P087_Filter_Comp::Equal, P087_Filter_Comp::NotEqual }; FormSelectorOptions selector(2, options, optionValues); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector(id, static_cast(comparator)); break; } diff --git a/src/_P137_AXP192.ino b/src/_P137_AXP192.ino index f7244f96ee..32eb4cb2d4 100644 --- a/src/_P137_AXP192.ino +++ b/src/_P137_AXP192.ino @@ -274,7 +274,7 @@ boolean Plugin_137(uint8_t function, struct EventStruct *event, String& string) FormSelectorOptions selector( optionCount, bootStates, bootStateValues, bootStateAttributes); selector.enabled = !bitRead(P137_CONFIG_DISABLEBITS, i + 3); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector(id, get3BitFromUL(P137_CONFIG_FLAGS, i * 3)); if (bitRead(P137_CONFIG_DISABLEBITS, i + 3)) { diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 5221c84f64..9e3566e729 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -81,6 +81,11 @@ int FormSelectorOptions::getIndexValue(int index) const return index; } +void FormSelectorOptions::clearClassName() +{ + classname = F(""); +} + void FormSelectorOptions::addFormSelector( const __FlashStringHelper *label, const __FlashStringHelper *id, diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index e33f46568d..a5b5fa15f6 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -30,6 +30,8 @@ class FormSelectorOptions { virtual int getIndexValue(int index) const; + void clearClassName(); + void addFormSelector(const __FlashStringHelper *label, const __FlashStringHelper *id, int selectedIndex) const; diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index b6e59f9793..2a1a8f3028 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -2327,7 +2327,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { html_TD(); // Content FormSelectorOptions selector( P104_CONTENT_count, contentTypes, contentOptions); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector( getPluginCustomArgName(index + P104_OFFSET_CONTENT), zones[zone].content); @@ -2335,7 +2335,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { { html_TD(); // Alignment FormSelectorOptions selector(3, alignmentTypes, alignmentOptions); - selector.classname = F(""); + selector.clearClassName(); selector.addSelector( getPluginCustomArgName(index + P104_OFFSET_ALIGNMENT), zones[zone].alignment); @@ -2346,7 +2346,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { animationCount - 1, &animationTypes[1], &animationOptions[1]); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Animation In"); # endif // ifdef P104_USE_TOOLTIPS @@ -2368,7 +2368,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { fontCount, fontTypes, fontOptions); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Font"); # endif // ifdef P104_USE_TOOLTIPS @@ -2383,7 +2383,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { invertedCount, invertedTypes, invertedOptions); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Inverted"); # endif // ifdef P104_USE_TOOLTIPS @@ -2422,7 +2422,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { animationCount, animationTypes, animationOptions); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Animation Out"); # endif // ifdef P104_USE_TOOLTIPS @@ -2445,7 +2445,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { layoutCount, layoutTypes, layoutOptions); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Layout"); # endif // ifdef P104_USE_TOOLTIPS @@ -2460,7 +2460,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { specialEffectCount, specialEffectTypes, specialEffectOptions); - selector.classname = F(""); + selector.clearClassName(); # ifdef P104_USE_TOOLTIPS selector.tooltip = F("Special Effects"); # endif // ifdef P104_USE_TOOLTIPS From 7b7c2b8e224293207bae02fac08a9ac862fe3b6b Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Jan 2025 01:17:09 +0100 Subject: [PATCH 48/50] [Cleanup] Reduce bin size using FormSelectOptions class --- src/_C011.cpp | 2 +- src/_P001_Switch.ino | 4 +- src/_P003_Pulse.ino | 2 +- src/_P004_Dallas.ino | 4 +- src/_P005_DHT.ino | 2 +- src/_P007_PCF8591.ino | 4 +- src/_P009_MCP.ino | 2 +- src/_P010_BH1750.ino | 2 +- src/_P011_PME.ino | 2 +- src/_P012_LCD.ino | 4 +- src/_P013_HCSR04.ino | 6 +-- src/_P014_SI70xx.ino | 2 +- src/_P015_TSL2561.ino | 4 +- src/_P019_PCF8574.ino | 2 +- src/_P020_Ser2Net.ino | 2 +- src/_P021_Level.ino | 2 +- src/_P022_PCA9685.ino | 5 +- src/_P023_OLED.ino | 2 +- src/_P024_MLX90614.ino | 2 +- src/_P027_INA219.ino | 4 +- src/_P028_BME280.ino | 4 +- src/_P036_FrameOLED.ino | 12 ++--- src/_P038_NeoPixel.ino | 2 +- src/_P039_Thermosensors.ino | 12 ++--- src/_P042_Candle.ino | 2 +- src/_P043_ClkOutput.ino | 2 +- src/_P045_MPU6050.ino | 2 +- src/_P047_i2c-soil-moisture-sensor.ino | 1 + src/_P049_MHZ19.ino | 4 +- src/_P050_TCS34725.ino | 8 +-- src/_P052_SenseAir.ino | 4 +- src/_P053_PMSx003.ino | 2 +- src/_P057_HT16K33_LED.ino | 2 +- src/_P059_Encoder.ino | 2 +- src/_P061_KeyPad.ino | 2 +- src/_P062_MPR121_KeyPad.ino | 2 +- src/_P064_APDS9960.ino | 26 ++++++---- src/_P066_VEML6040.ino | 4 +- src/_P067_HX711_Load_Cell.ino | 4 +- src/_P073_7DGT.ino | 6 +-- src/_P074_TSL2591.ino | 4 +- src/_P075_Nextion.ino | 2 +- src/_P076_HLW8012.ino | 6 +-- src/_P078_Eastron.ino | 4 +- src/_P079_Wemos_Motorshield.ino | 2 +- src/_P082_GPS.ino | 4 +- src/_P084_VEML6070.ino | 2 +- src/_P085_AcuDC243.ino | 2 +- src/_P086_Homie.ino | 2 +- src/_P087_SerialProxy.ino | 2 +- src/_P090_CCS811.ino | 4 +- src/_P091_SerSwitch.ino | 10 ++-- src/_P092_DLbus.ino | 2 +- src/_P095_ILI9341.ino | 4 +- src/_P096_eInk.ino | 8 +-- src/_P098_PWM_motor.ino | 2 +- src/_P099_XPT2046Touch.ino | 4 +- src/_P100_DS2423_counter.ino | 2 +- src/_P102_PZEM004Tv3.ino | 6 +-- src/_P108_DDS238.ino | 2 +- src/_P109_ThermOLED.ino | 2 +- src/_P110_VL53L0X.ino | 4 +- src/_P111_RC522_RFID.ino | 2 +- src/_P112_AS7265x.ino | 18 ++++--- src/_P113_VL53L1X.ino | 4 +- src/_P114_VEML6075.ino | 4 +- src/_P115_MAX1704x_v2.ino | 2 +- src/_P116_ST77xx.ino | 4 +- src/_P119_ITG3205_Gyro.ino | 2 +- src/_P122_SHT2x.ino | 2 +- src/_P123_I2CTouch.ino | 2 +- src/_P126_74HC595.ino | 2 +- src/_P129_74HC165.ino | 4 +- src/_P131_NeoPixelMatrix.ino | 10 ++-- src/_P132_INA3221.ino | 17 +++--- src/_P133_LTR390.ino | 4 +- src/_P141_PCD8544_Nokia5110.ino | 2 +- src/_P142_AS5600.ino | 10 ++-- src/_P143_I2C_Rotary.ino | 6 +-- src/_P145_MQxxx.ino | 2 +- src/_P146_CacheControllerReader.ino | 2 +- src/_P148_POWRxxD_THR3xxD.ino | 2 +- src/_P150_TMP117.ino | 4 +- src/_P153_SHT4x.ino | 4 +- src/_P166_GP8403.ino | 2 +- src/_P168_VEML6030_7700.ino | 8 +-- src/_P169_AS3935_LightningDetector.ino | 4 +- src/src/Controller_config/C018_config.cpp | 4 +- src/src/DataTypes/FormSelectorOptions.cpp | 52 ++++++++++++------- src/src/DataTypes/FormSelectorOptions.h | 32 ++++++++---- src/src/Helpers/AdafruitGFX_helper.cpp | 6 +-- src/src/Helpers/ESPEasy_TouchHandler.cpp | 2 +- src/src/Helpers/OLed_helper.cpp | 6 +-- src/src/Helpers/_CPlugin_Helper_webform.cpp | 8 +-- .../Helpers/_Internal_GPIO_pulseHelper.cpp | 2 +- src/src/Helpers/_Plugin_Helper_serial.cpp | 2 +- src/src/Helpers/_Plugin_Helper_webform.cpp | 4 +- src/src/Helpers/_Plugin_SensorTypeHelper.cpp | 4 +- src/src/PluginStructs/P002_data_struct.cpp | 4 +- src/src/PluginStructs/P025_data_struct.cpp | 4 +- src/src/PluginStructs/P037_data_struct.cpp | 8 +-- src/src/PluginStructs/P047_data_struct.cpp | 2 +- src/src/PluginStructs/P073_data_struct.cpp | 4 +- src/src/PluginStructs/P104_data_struct.cpp | 6 +-- src/src/PluginStructs/P120_data_struct.cpp | 14 ++--- src/src/PluginStructs/P139_data_struct.cpp | 2 +- .../P139_data_struct_formselectors.h | 8 --- src/src/PluginStructs/P148_data_struct.cpp | 4 +- src/src/PluginStructs/P165_data_struct.cpp | 4 +- src/src/WebServer/AccessControl.cpp | 2 +- src/src/WebServer/AdvancedConfigPage.cpp | 14 ++--- src/src/WebServer/DevicesPage.cpp | 4 +- src/src/WebServer/HardwarePage.cpp | 10 ++-- src/src/WebServer/Markup_Forms.cpp | 7 +-- 114 files changed, 309 insertions(+), 275 deletions(-) diff --git a/src/_C011.cpp b/src/_C011.cpp index 2cfc1e4cd4..ef641abd6d 100644 --- a/src/_C011.cpp +++ b/src/_C011.cpp @@ -111,7 +111,7 @@ bool CPlugin_011(CPlugin::Function function, struct EventStruct *event, String& } } - FormSelectorOptions selector(nrOptions, methods); + const FormSelectorOptions selector(nrOptions, methods); selector.addFormSelector(F("Method"), F("P011httpmethod"), choice); } diff --git a/src/_P001_Switch.ino b/src/_P001_Switch.ino index 78535e1630..bc5fd78087 100644 --- a/src/_P001_Switch.ino +++ b/src/_P001_Switch.ino @@ -86,7 +86,7 @@ boolean Plugin_001(uint8_t function, struct EventStruct *event, String& string) const int optionValues[] = { PLUGIN_001_TYPE_SWITCH, PLUGIN_001_TYPE_DIMMER }; const uint8_t switchtype = P001_data_struct::P001_getSwitchType(event); constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Switch Type"), F("type"), switchtype); if (switchtype == PLUGIN_001_TYPE_DIMMER) @@ -106,7 +106,7 @@ boolean Plugin_001(uint8_t function, struct EventStruct *event, String& string) SWITCH_TYPE_PUSH_ACTIVE_LOW, SWITCH_TYPE_PUSH_ACTIVE_HIGH }; */ - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(buttonOptions), buttonOptions); // buttonOptionValues); selector.addFormSelector( diff --git a/src/_P003_Pulse.ino b/src/_P003_Pulse.ino index 0e86aa83c6..18bc731dde 100644 --- a/src/_P003_Pulse.ino +++ b/src/_P003_Pulse.ino @@ -163,7 +163,7 @@ boolean Plugin_003(uint8_t function, struct EventStruct *event, String& string) F("Time/Delta"), # endif // if P003_USE_EXTRA_COUNTERTYPES }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(options), options); selector.addFormSelector(F("Counter Type"), F("countertype"), choice); diff --git a/src/_P004_Dallas.ino b/src/_P004_Dallas.ino index eadcfa5efd..1c4a740054 100644 --- a/src/_P004_Dallas.ino +++ b/src/_P004_Dallas.ino @@ -140,7 +140,7 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) if ((resolutionChoice < 9) || (resolutionChoice > 12)) { resolutionChoice = activeRes; } constexpr int resultsOptionValues[] { 9, 10, 11, 12 }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(resultsOptionValues), resultsOptionValues); selector.addFormSelector(F("Device Resolution"), F("res"), resolutionChoice); @@ -152,7 +152,7 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *resultsOptions[] = { F("NaN"), F("-127"), F("0"), F("125"), F("Ignore") }; constexpr int resultsOptionValues[] { P004_ERROR_NAN, P004_ERROR_MIN_RANGE, P004_ERROR_ZERO, P004_ERROR_MAX_RANGE, P004_ERROR_IGNORE }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(resultsOptionValues), resultsOptions, resultsOptionValues); selector.addFormSelector(F("Error State Value"), F("err"), P004_ERROR_STATE_OUTPUT); diff --git a/src/_P005_DHT.ino b/src/_P005_DHT.ino index c2c2cbc645..9b83d51692 100644 --- a/src/_P005_DHT.ino +++ b/src/_P005_DHT.ino @@ -65,7 +65,7 @@ boolean Plugin_005(uint8_t function, struct EventStruct *event, String& string) constexpr int indices[]{ P005_DHT11, P005_DHT22, P005_DHT12, P005_AM2301, P005_SI7021, P005_MS01 }; constexpr size_t nrElements = NR_ELEMENTS(indices); - FormSelectorOptions selector(nrElements, options, indices); + const FormSelectorOptions selector(nrElements, options, indices); selector.addFormSelector(F("Sensor model"), F("dhttype"), PCONFIG(0)); success = true; diff --git a/src/_P007_PCF8591.ino b/src/_P007_PCF8591.ino index c56baf4dbf..e1e1378fed 100644 --- a/src/_P007_PCF8591.ino +++ b/src/_P007_PCF8591.ino @@ -105,7 +105,7 @@ boolean Plugin_007(uint8_t function, struct EventStruct *event, String& string) portNames[x] += x; } addFormSelectorI2C(F("pi2c"), 8, i2cAddressValues, address); - FormSelectorOptions selector(4, portNames, portValues); + const FormSelectorOptions selector(4, portNames, portValues); selector.addFormSelector(F("Port"), F("pport"), port); addFormNote(F( "Selected Port value will be stored in first 'Values' field and consecutively for 'Number Output Values' > Single.")); @@ -143,7 +143,7 @@ boolean Plugin_007(uint8_t function, struct EventStruct *event, String& string) 0b00110000, }; constexpr size_t optionCount = NR_ELEMENTS(inputModeValues); - FormSelectorOptions selector(optionCount, inputModeOptions, inputModeValues); + const FormSelectorOptions selector(optionCount, inputModeOptions, inputModeValues); selector.addFormSelector(F("Input mode"), F("input_mode"), P007_INPUT_MODE); addFormCheckBox(F("Enable Analog output (AOUT)"), F("output_mode"), P007_OUTPUT_MODE == P007_OUTPUT_ENABLED); diff --git a/src/_P009_MCP.ino b/src/_P009_MCP.ino index d6a3941117..88e590d3ef 100644 --- a/src/_P009_MCP.ino +++ b/src/_P009_MCP.ino @@ -71,7 +71,7 @@ boolean Plugin_009(uint8_t function, struct EventStruct *event, String& string) portNames[x] += (x < 8 ? x : x - 8); } addFormSelectorI2C(F("pi2c"), 8, i2cAddressValues, address); - FormSelectorOptions selector(16, portNames, portValues); + const FormSelectorOptions selector(16, portNames, portValues); selector.addFormSelector(F("Port"), F("pport"), port); } else { success = intArrayContains(8, i2cAddressValues, event->Par1); diff --git a/src/_P010_BH1750.ino b/src/_P010_BH1750.ino index 4a8eaa480a..67c731b0cd 100644 --- a/src/_P010_BH1750.ino +++ b/src/_P010_BH1750.ino @@ -85,7 +85,7 @@ boolean Plugin_010(uint8_t function, struct EventStruct *event, String& string) RESOLUTION_AUTO_HIGH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode); - FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode); + const FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode); selector.addFormSelector(F("Measurement mode"), F("pmode"), PCONFIG(1)); addFormCheckBox(F("Send sensor to sleep"), F("psleep"), PCONFIG(2)); diff --git a/src/_P011_PME.ino b/src/_P011_PME.ino index 92a64623e3..47c97c57aa 100644 --- a/src/_P011_PME.ino +++ b/src/_P011_PME.ino @@ -84,7 +84,7 @@ boolean Plugin_011(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options[] = { F("Digital"), F("Analog"), F("Input (switch)") }; const int optionValues[] = { P011_TYPE_DIGITAL, P011_TYPE_ANALOG, P011_TYPE_SWITCH }; constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Port Type"), F("p011"), P011_PORT_TYPE); success = true; diff --git a/src/_P012_LCD.ino b/src/_P012_LCD.ino index be3fe2b296..11d5b63843 100644 --- a/src/_P012_LCD.ino +++ b/src/_P012_LCD.ino @@ -97,7 +97,7 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues2[2] = { 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - FormSelectorOptions selector(optionCount, options2, optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); selector.addFormSelector(F("Display Size"), F("psize"), P012_SIZE); } @@ -126,7 +126,7 @@ boolean Plugin_012(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues3[] = { 0, 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues3); - FormSelectorOptions selector(optionCount, options3, optionValues3); + const FormSelectorOptions selector(optionCount, options3, optionValues3); selector.addFormSelector(F("LCD command Mode"), F("pmode"), P012_MODE); } diff --git a/src/_P013_HCSR04.ino b/src/_P013_HCSR04.ino index 3f7cf799e5..688e9e0a8a 100644 --- a/src/_P013_HCSR04.ino +++ b/src/_P013_HCSR04.ino @@ -131,7 +131,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even # endif // if P013_FEATURE_COMBINED_MODE }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesOpMode); - FormSelectorOptions selector(optionCount, optionsOpMode, optionValuesOpMode); + const FormSelectorOptions selector(optionCount, optionsOpMode, optionValuesOpMode); selector.addFormSelector(F("Mode"), F("pmode"), P013_OPERATINGMODE); } @@ -156,7 +156,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even F("Imperial"), }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesUnit); - FormSelectorOptions selector(optionCount, optionsUnit, optionValuesUnit); + const FormSelectorOptions selector(optionCount, optionsUnit, optionValuesUnit); selector.addFormSelector(F("Unit"), F("pUnit"), P013_MEASURINGUNIT); } @@ -167,7 +167,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even F("Median"), }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesFilter); - FormSelectorOptions selector(optionCount, optionsFilter, optionValuesFilter); + const FormSelectorOptions selector(optionCount, optionsFilter, optionValuesFilter); selector.addFormSelector(F("Filter"), F("fltr"), P013_FILTERTYPE); } diff --git a/src/_P014_SI70xx.ino b/src/_P014_SI70xx.ino index c6e420cd46..904148bf54 100644 --- a/src/_P014_SI70xx.ino +++ b/src/_P014_SI70xx.ino @@ -117,7 +117,7 @@ boolean Plugin_014(uint8_t function, struct EventStruct *event, String& string) SI70xx_RESOLUTION_11T_11RH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Resolution"), F("pres"), P014_RESOLUTION); addFormNumericBox("ADC Filter Power", F("pfilter"), P014_FILTER_POWER, 0, 4); diff --git a/src/_P015_TSL2561.ino b/src/_P015_TSL2561.ino index fa6a19b6b2..b1ea93d6f4 100644 --- a/src/_P015_TSL2561.ino +++ b/src/_P015_TSL2561.ino @@ -102,7 +102,7 @@ boolean Plugin_015(uint8_t function, struct EventStruct *event, String& string) F("402"), }; constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options); + const FormSelectorOptions selector(optionCount, options); selector.addFormSelector(F("Integration time"), F("pintegration"), P015_INTEGRATION); addUnit(F("ms")); } @@ -126,7 +126,7 @@ boolean Plugin_015(uint8_t function, struct EventStruct *event, String& string) }; */ constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options/*, optionValues*/); + const FormSelectorOptions selector(optionCount, options/*, optionValues*/); selector.addFormSelector(F("Gain"), F("pgain"), P015_GAIN); } diff --git a/src/_P019_PCF8574.ino b/src/_P019_PCF8574.ino index 8622f5539b..48ec2b2eb0 100644 --- a/src/_P019_PCF8574.ino +++ b/src/_P019_PCF8574.ino @@ -70,7 +70,7 @@ boolean Plugin_019(uint8_t function, struct EventStruct *event, String& string) portNames[x] += x; } addFormSelectorI2C(F("pi2c"), 16, i2cAddressValues, address); - FormSelectorOptions selector(8, portNames, portValues); + const FormSelectorOptions selector(8, portNames, portValues); selector.addFormSelector(F("Port"), F("pport"), port); addFormNote(F("PCF8574 uses addresses 0x20..0x27, PCF8574A uses addresses 0x38..0x3F.")); } else { diff --git a/src/_P020_Ser2Net.ino b/src/_P020_Ser2Net.ino index 799f84ae8d..fc38b4d4f4 100644 --- a/src/_P020_Ser2Net.ino +++ b/src/_P020_Ser2Net.ino @@ -201,7 +201,7 @@ boolean Plugin_020(uint8_t function, struct EventStruct *event, String& string) }; */ constexpr int optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options /*, optionValues*/); + const FormSelectorOptions selector(optionCount, options /*, optionValues*/); selector.addFormSelector( F("Event processing"), F("pevents"), diff --git a/src/_P021_Level.ino b/src/_P021_Level.ino index ec71092f26..8a9a45ec14 100644 --- a/src/_P021_Level.ino +++ b/src/_P021_Level.ino @@ -239,7 +239,7 @@ boolean Plugin_021(uint8_t function, struct EventStruct *event, String& string) { P021_OPMODE_CLASSIC, P021_OPMODE_OFF, P021_OPMODE_STANDBY, P021_OPMODE_ON, P021_OPMODE_TEMP, P021_OPMODE_REMOTE }; */ constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options/*, optionValues*/); + const FormSelectorOptions selector(optionCount, options/*, optionValues*/); selector.addFormSelector(F("Control mode"), F(P021_GUID_OPMODE), P021_OPMODE); // Add timer values depending on build size diff --git a/src/_P022_PCA9685.ino b/src/_P022_PCA9685.ino index 660e393a17..206e16e343 100644 --- a/src/_P022_PCA9685.ino +++ b/src/_P022_PCA9685.ino @@ -115,12 +115,9 @@ boolean Plugin_022(uint8_t function, struct EventStruct *event, String& string) { //m2Values[i] = i; m2Options[i] = formatToHex_decimal(i); - - if (i == 0x10) { - m2Options[i] += F(" - (default)"); - } } FormSelectorOptions selector(PCA9685_MODE2_VALUES, m2Options/*, m2Values*/); + selector.default_index = 0x10; selector.addFormSelector(F("MODE2"), F("pmode2"), mode2); } addFormNumericBox( diff --git a/src/_P023_OLED.ino b/src/_P023_OLED.ino index 0618652204..eae8fb7bee 100644 --- a/src/_P023_OLED.ino +++ b/src/_P023_OLED.ino @@ -106,7 +106,7 @@ boolean Plugin_023(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options4[] = { F("Normal"), F("Optimized") }; const int optionValues4[] = { 1, 2 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - FormSelectorOptions selector(optionCount, options4, optionValues4); + const FormSelectorOptions selector(optionCount, options4, optionValues4); selector.addFormSelector(F("Font Width"), F("font_spacing"), PCONFIG(4)); } { diff --git a/src/_P024_MLX90614.ino b/src/_P024_MLX90614.ino index 986192c7e5..efc29ed7f8 100644 --- a/src/_P024_MLX90614.ino +++ b/src/_P024_MLX90614.ino @@ -81,7 +81,7 @@ boolean Plugin_024(uint8_t function, struct EventStruct *event, String& string) (0x06) }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Option"), F("option"), PCONFIG(0)); success = true; diff --git a/src/_P027_INA219.ino b/src/_P027_INA219.ino index 820b573d1f..2c56a4bd70 100644 --- a/src/_P027_INA219.ino +++ b/src/_P027_INA219.ino @@ -132,12 +132,12 @@ boolean Plugin_027(uint8_t function, struct EventStruct *event, String& string) { { const __FlashStringHelper *optionsMode[] = { F("32V, 2A"), F("32V, 1A"), F("16V, 0.4A"), F("26V, 8A") }; - FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode); + const FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode); selector.addFormSelector(F("Measure range"), F("range"), PCONFIG(0)); } { const __FlashStringHelper *options[] = { F("Voltage"), F("Current"), F("Power"), F("Voltage/Current/Power") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(F("Measurement Type"), F("measuretype"), PCONFIG(2)); } # if P027_FEATURE_POWERDOWN diff --git a/src/_P028_BME280.ino b/src/_P028_BME280.ino index 09ab4ac824..34532f21c4 100644 --- a/src/_P028_BME280.ino +++ b/src/_P028_BME280.ino @@ -190,7 +190,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) static_cast(P028_data_struct::BMx_DetectMode::BME280), static_cast(P028_data_struct::BMx_DetectMode::BMP280), }; - FormSelectorOptions selector(NR_ELEMENTS(detectOptionList), detectOptionList, detectOptions); + const FormSelectorOptions selector(NR_ELEMENTS(detectOptionList), detectOptionList, detectOptions); selector.addFormSelector(F("Output values mode"), F("det"), P028_DETECTION_MODE); success = true; @@ -254,7 +254,7 @@ boolean Plugin_028(uint8_t function, struct EventStruct *event, String& string) # endif // ifndef LIMIT_BUILD_SIZE }; constexpr int P028_ERROR_STATE_COUNT = NR_ELEMENTS(resultsOptions); - FormSelectorOptions selector( + const FormSelectorOptions selector( P028_ERROR_STATE_COUNT, resultsOptions, resultsOptionValues); diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 533d5e0a47..3e2e969c1f 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -348,7 +348,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # endif // if P036_ENABLE_TICKER }; constexpr int optionCnt = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCnt, options, optionValues); + const FormSelectorOptions selector(optionCnt, options, optionValues); selector.addFormSelector(F("Scroll"), F("scroll"), P036_SCROLL); } @@ -360,7 +360,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) const int optionValues[] = { static_cast(eP036pinmode::ePPM_Input), static_cast(eP036pinmode::ePPM_InputPullUp) }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); selector.addFormSelector(F("Pin mode"), F("pinmode"), bitRead(P036_FLAGS_0, P036_FLAG_INPUT_PULLUP)); // Bit 26 Input PullUp } @@ -390,7 +390,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("Display, Contrast, Frame, Line & Linecount") }; const int optionValues[] = { 0, 1, 3 }; // Bitmap - FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); selector.addFormSelector(F("Generate events"), F("generateEvents"), choice); # ifndef P036_LIMIT_BUILD_SIZE @@ -445,7 +445,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) # endif // if P036_USERDEF_HEADERS }; constexpr int nrOptions9 = NR_ELEMENTS(options9); - FormSelectorOptions selector(nrOptions9, options9, optionValues9); + const FormSelectorOptions selector(nrOptions9, options9, optionValues9); // HeaderContent selector.addFormSelector( F("Header"), F("header"), @@ -468,7 +468,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) F("HH:MM:SS (am/pm)"), F("HH:MM (am/pm)"), }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(F("Header Time format"), F("timeFmt"), get4BitFromUL(P036_FLAGS_1, P036_FLAG_TIME_FORMAT)); } @@ -492,7 +492,7 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) static_cast(eAlignment::eCenter), static_cast(eAlignment::eRight) }; - FormSelectorOptions selector( NR_ELEMENTS(optionValuesAlignment), optionsAlignment, optionValuesAlignment); + const FormSelectorOptions selector( NR_ELEMENTS(optionValuesAlignment), optionsAlignment, optionValuesAlignment); selector.addFormSelector(F("Align content (global)"), F("LeftAlign"), get2BitFromUL(P036_FLAGS_1, P036_FLAG_LEFT_ALIGNED)); } diff --git a/src/_P038_NeoPixel.ino b/src/_P038_NeoPixel.ino index da1e72f18d..b4b9a7f65a 100644 --- a/src/_P038_NeoPixel.ino +++ b/src/_P038_NeoPixel.ino @@ -96,7 +96,7 @@ boolean Plugin_038(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *options[] = { F("GRB"), F("GRBW") }; int indices[] = { P038_STRIP_TYPE_RGB, P038_STRIP_TYPE_RGBW }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, indices); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, indices); selector.addFormSelector(F("Strip Type"), F("pstrip"), P038_CONFIG_STRIPTYPE); } diff --git a/src/_P039_Thermosensors.ino b/src/_P039_Thermosensors.ino index d7ab0bf10b..367e4a8668 100644 --- a/src/_P039_Thermosensors.ino +++ b/src/_P039_Thermosensors.ino @@ -476,14 +476,14 @@ 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); - FormSelectorOptions selector( optionCount, Toptions, ToptionValues); + const FormSelectorOptions selector( optionCount, Toptions, ToptionValues); selector.addFormSelector(F("Thermocouple type"), F("tctype"), P039_TC_TYPE); } { const __FlashStringHelper *Coptions[] = { F("1"), F("2"), F("4"), F("8"), F("16") }; const int CoptionValues[] = { 0, 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(CoptionValues); - FormSelectorOptions selector(optionCount, Coptions, CoptionValues); + const FormSelectorOptions selector(optionCount, Coptions, CoptionValues); selector.addFormSelector(F("Averaging"), F("contype"), P039_CONFIG_4); addUnit(F("sample(s)")); } @@ -511,13 +511,13 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *PToptions[] = { F("PT100"), F("PT1000") }; const int PToptionValues[] = { MAX31865_PT100, MAX31865_PT1000 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); - FormSelectorOptions selector(optionCount, PToptions, PToptionValues); + const FormSelectorOptions selector(optionCount, PToptions, PToptionValues); 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); - FormSelectorOptions selector( optionCount, Coptions); + const FormSelectorOptions selector( optionCount, Coptions); selector.addFormSelector(F("Connection Type"), F("contype"), P039_CONFIG_4); addUnit(F("wire")); } @@ -549,7 +549,7 @@ boolean Plugin_039(uint8_t function, struct EventStruct *event, String& string) { F("LM70"), F("LM71"), F("LM74"), F("TMP121"), F("TMP122"), F("TMP123"), F("TMP124"), F("TMP125") }; const int PToptionValues[] = { LM7x_SD70, LM7x_SD71, LM7x_SD74, LM7x_SD121, LM7x_SD122, LM7x_SD123, LM7x_SD124, LM7x_SD125 }; constexpr size_t optionCount = NR_ELEMENTS(PToptionValues); - FormSelectorOptions selector(optionCount, PToptions, PToptionValues); + const FormSelectorOptions selector(optionCount, PToptions, PToptionValues); selector.addFormSelector(F("LM7x device details"), F("rtd_lm_type"), P039_RTD_LM_TYPE); addFormNote(F("TMP122/124 Limited support -> fixed 12 Bit res, no advanced options")); } @@ -822,7 +822,7 @@ void P039_AddMainsFrequencyFilterSelection(struct EventStruct *event) const __FlashStringHelper *FToptions[] = { F("60"), F("50") }; const int FToptionValues[] = { 0, 1 }; - FormSelectorOptions selector(NR_ELEMENTS(FToptions), FToptions, FToptionValues); + 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 diff --git a/src/_P042_Candle.ino b/src/_P042_Candle.ino index 5c65a2bc67..7a161f2713 100644 --- a/src/_P042_Candle.ino +++ b/src/_P042_Candle.ino @@ -155,7 +155,7 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) } // Candle Type Selection - FormSelectorOptions selector(P042_FLAME_OPTIONS, options, nullptr); + const FormSelectorOptions selector(P042_FLAME_OPTIONS, options, nullptr); selector.addFormSelector(F("Flame Type"), P042_WEBVAR_CANDLETYPE, P042_CONFIG_CANDLETYPE); } diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index aec57b8f95..816fb252d3 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -181,7 +181,7 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) if (validGpio(CONFIG_PIN1) || (P043_SIMPLE_VALUE == 1)) { addHtml(' '); const uint8_t choice = Cache.getTaskDevicePluginConfig(event->TaskIndex, x); - FormSelectorOptions selector(optionsCount, options); + const FormSelectorOptions selector(optionsCount, options); selector.addSelector( concat(F("state"), x), choice); diff --git a/src/_P045_MPU6050.ino b/src/_P045_MPU6050.ino index 25d27ecfb2..9b116a9040 100644 --- a/src/_P045_MPU6050.ino +++ b/src/_P045_MPU6050.ino @@ -170,7 +170,7 @@ boolean Plugin_045(uint8_t function, struct EventStruct *event, String& string) F("G-force Z") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(F("Function"), F("pfunction"), choice); } diff --git a/src/_P047_i2c-soil-moisture-sensor.ino b/src/_P047_i2c-soil-moisture-sensor.ino index 02fe780ece..377c114f91 100644 --- a/src/_P047_i2c-soil-moisture-sensor.ino +++ b/src/_P047_i2c-soil-moisture-sensor.ino @@ -152,6 +152,7 @@ boolean Plugin_047(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t P047_MODEL_OPTIONS = NR_ELEMENTS(SensorModelIds); FormSelectorOptions selector( P047_MODEL_OPTIONS, SensorModels, SensorModelIds); + 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.")); diff --git a/src/_P049_MHZ19.ino b/src/_P049_MHZ19.ino index 5c364e0100..4240a26c93 100644 --- a/src/_P049_MHZ19.ino +++ b/src/_P049_MHZ19.ino @@ -94,7 +94,7 @@ boolean Plugin_049(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *options[] = { F("Normal"), F("ABC disabled") }; const int optionValues[] = { P049_ABC_enabled, P049_ABC_disabled }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); selector.addFormSelector(F("Auto Base Calibration"), F("abcdisable"), PCONFIG(0)); } { @@ -106,7 +106,7 @@ boolean Plugin_049(uint8_t function, struct EventStruct *event, String& string) PLUGIN_049_FILTER_FAST, PLUGIN_049_FILTER_MEDIUM, PLUGIN_049_FILTER_SLOW }; - FormSelectorOptions selector(NR_ELEMENTS(filteroptions), filteroptions, filteroptionValues); + const FormSelectorOptions selector(NR_ELEMENTS(filteroptions), filteroptions, filteroptionValues); selector.addFormSelector(F("Filter"), F("filter"), PCONFIG(1)); } P049_html_show_stats(event); diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index b4ef88e28d..2c8d776837 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -129,7 +129,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) TCS34725_INTEGRATIONTIME_154MS, TCS34725_INTEGRATIONTIME_700MS, }; - FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode, optionValuesMode); + const FormSelectorOptions selector(NR_ELEMENTS(optionsMode), optionsMode, optionValuesMode); selector.addFormSelector(F("Integration Time"), F("inttime"), PCONFIG(0)); addUnit(F("ms")); } @@ -147,7 +147,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) TCS34725_GAIN_16X, TCS34725_GAIN_60X, }; - FormSelectorOptions selector(NR_ELEMENTS(optionsMode2), optionsMode2, optionValuesMode2); + const FormSelectorOptions selector(NR_ELEMENTS(optionsMode2), optionsMode2, optionValuesMode2); selector.addFormSelector(F("Gain"), F("gain"), PCONFIG(1)); } @@ -166,7 +166,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) // const int optionValuesRGB[P050_RGB_OPTIONS] = { 0, 1, 2, 3, 4, 5 }; constexpr size_t valueCount = NR_ELEMENTS(optionsRGB); - FormSelectorOptions selector(valueCount, optionsRGB); + const FormSelectorOptions selector(valueCount, optionsRGB); selector.addFormSelector(F("Output RGB Values"), F("outputrgb"), PCONFIG(2)); # ifndef LIMIT_BUILD_SIZE addFormNote(F("For 'normalized' or 'transformed' options, the Red/Green/Blue Decimals should best be increased.")); @@ -194,7 +194,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) // const int optionValuesOutput[P050_VALUE4_OPTIONS] = { 0, 1, 2, 3 }; constexpr size_t valueCount = NR_ELEMENTS(optionsOutput); - FormSelectorOptions selector(valueCount, optionsOutput); + const FormSelectorOptions selector(valueCount, optionsOutput); selector.addFormSelector(F("Output at Values #4"), F("output4"), PCONFIG(3)); # ifndef LIMIT_BUILD_SIZE addFormNote(F("Optionally adjust Values #4 name accordingly.")); diff --git a/src/_P052_SenseAir.ino b/src/_P052_SenseAir.ino index c022ad6470..dfd4682fb0 100644 --- a/src/_P052_SenseAir.ino +++ b/src/_P052_SenseAir.ino @@ -224,7 +224,7 @@ boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) // Disable selector for now, since single measurement not yet supported. const __FlashStringHelper *options[] = { F("Continuous"), F("Single Measurement") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(F("Measurement Mode"), F("mode"), value); } */ @@ -249,7 +249,7 @@ boolean Plugin_052(uint8_t function, struct EventStruct *event, String& string) uint8_t choiceABCperiod = PCONFIG(4); const __FlashStringHelper * optionsABCperiod[] = { F("disable"), F("1 h"), F("12 h"), F("1 day"), F("2 days"), F("4 days"), F("7 days"), F("14 days"), F("30 days") }; - FormSelectorOptions selector(NR_ELEMENTS(optionsABCperiod), optionsABCperiod); + const FormSelectorOptions selector(NR_ELEMENTS(optionsABCperiod), optionsABCperiod); selector.addFormSelector(F("ABC period"), F("ABC_period"), choiceABCperiod); */ diff --git a/src/_P053_PMSx003.ino b/src/_P053_PMSx003.ino index 7aff9d293a..afc49bf32b 100644 --- a/src/_P053_PMSx003.ino +++ b/src/_P053_PMSx003.ino @@ -243,7 +243,7 @@ boolean Plugin_053(uint8_t function, struct EventStruct *event, String& string) static_cast(PMSx003_event_datatype::Event_PMxx_TempHum_Formaldehyde), static_cast(PMSx003_event_datatype::Event_All_count_bins), static_cast(PMSx003_event_datatype::Event_All) }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(eventOptionValues), eventOptions, eventOptionValues); diff --git a/src/_P057_HT16K33_LED.ino b/src/_P057_HT16K33_LED.ino index a2ec10579c..2773e1e0b1 100644 --- a/src/_P057_HT16K33_LED.ino +++ b/src/_P057_HT16K33_LED.ino @@ -126,7 +126,7 @@ boolean Plugin_057(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *options[] = { F("none"), F("7-Seg. HH:MM (24 hour)"), F("7-Seg. HH:MM (12 hour)") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(F("Clock Type"), F("clocktype"), PCONFIG(1)); } diff --git a/src/_P059_Encoder.ino b/src/_P059_Encoder.ino index 938601f8f7..debb0c98e0 100644 --- a/src/_P059_Encoder.ino +++ b/src/_P059_Encoder.ino @@ -79,7 +79,7 @@ boolean Plugin_059(uint8_t function, struct EventStruct *event, String& string) { const int optionValues[] = { 1, 2, 4 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, optionValues); + const FormSelectorOptions selector(optionCount, optionValues); selector.addFormSelector(F("Mode"), F("mode"), PCONFIG(0)); addUnit(F("pulses per cycle")); } diff --git a/src/_P061_KeyPad.ino b/src/_P061_KeyPad.ino index 5ce7f9390d..cc386777a0 100644 --- a/src/_P061_KeyPad.ino +++ b/src/_P061_KeyPad.ino @@ -154,7 +154,7 @@ boolean Plugin_061(uint8_t function, struct EventStruct *event, String& string) # endif // ifdef P061_ENABLE_PCF8575 }; constexpr int optionsCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionsCount, options); + const FormSelectorOptions selector(optionsCount, options); selector.addFormSelector(F("Chip (Mode)"), F("chip"), P061_CONFIG_KEYPAD_TYPE); success = true; diff --git a/src/_P062_MPR121_KeyPad.ino b/src/_P062_MPR121_KeyPad.ino index 8769f94650..9339866356 100644 --- a/src/_P062_MPR121_KeyPad.ino +++ b/src/_P062_MPR121_KeyPad.ino @@ -124,7 +124,7 @@ boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string) MPR212_EXTRA_SENSITIVITY }; constexpr size_t optionCount = NR_ELEMENTS(sensitivityValues); - FormSelectorOptions selector(optionCount, sensitivityOptions, sensitivityValues); + const FormSelectorOptions selector(optionCount, sensitivityOptions, sensitivityValues); selector.addFormSelector(F("Panel sensitivity"), F("psens"), PCONFIG(4)); } { diff --git a/src/_P064_APDS9960.ino b/src/_P064_APDS9960.ino index c7b064caee..6c5d92d2ab 100644 --- a/src/_P064_APDS9960.ino +++ b/src/_P064_APDS9960.ino @@ -170,20 +170,21 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *optionsGain[] = { F("1x"), F("2x"), - F("4x (default)"), + F("4x"), F("8x") }; const int optionsGainValues[] = { PGAIN_1X, PGAIN_2X, PGAIN_4X, PGAIN_8X }; // Also used for optionsALSGain constexpr size_t optionsGainCount = NR_ELEMENTS(optionsGainValues); + constexpr int optionsGain_default_index = PGAIN_4X; // Led_Drive options, all Led_Drive optionsets in SparkFun_APDS9960.h have the same valueset, so only defined once here const __FlashStringHelper *optionsLedDrive[] = { - F("100 (default)"), + F("100"), F("50"), F("25"), F("12.5") }; const int optionsLedDriveValues[] = { LED_DRIVE_100MA, LED_DRIVE_50MA, LED_DRIVE_25MA, LED_DRIVE_12_5MA }; constexpr size_t optionsLedDriveCount = NR_ELEMENTS(optionsLedDriveValues); - + constexpr int optionsLedDrive_default_index = LED_DRIVE_100MA; String lightSensorGainLabel; String lightSensorDriveLabel; @@ -195,6 +196,7 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) optionsGainCount, optionsGain, optionsGainValues); + selector.default_index = optionsGain_default_index; selector.addFormSelector( F("Gesture Gain"), F("ggain"), @@ -205,6 +207,7 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) optionsLedDriveCount, optionsLedDrive, optionsLedDriveValues); + selector.default_index = optionsLedDrive_default_index; selector.addFormSelector( F("Gesture LED Drive"), F("gldrive"), @@ -214,25 +217,27 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) { // Gesture Led-boost values const __FlashStringHelper *optionsLedBoost[] = { - F("100 %"), - F("150 %"), - F("200 %"), - F("300 % (default)") }; + F("100"), + F("150"), + F("200"), + F("300") }; const int optionsLedBoostValues[] = { LED_BOOST_100, LED_BOOST_150, LED_BOOST_200, LED_BOOST_300 }; constexpr size_t optionsLedBoostCount = NR_ELEMENTS(optionsLedBoostValues); FormSelectorOptions selector( optionsLedBoostCount, optionsLedBoost, optionsLedBoostValues); + selector.default_index = LED_BOOST_300; selector.addFormSelector( F("Gesture LED Boost"), F("lboost"), P064_LED_BOOST); + addUnit('%'); } addFormSubHeader(F("Proximity & Ambient Light Sensor parameters")); { - FormSelectorOptions selector(optionsGainCount, optionsGain, optionsGainValues); + const FormSelectorOptions selector(optionsGainCount, optionsGain, optionsGainValues); selector.addFormSelector(F("Proximity Gain"), F("pgain"), P064_PGAIN); } @@ -248,14 +253,15 @@ boolean Plugin_064(uint8_t function, struct EventStruct *event, String& string) // Ambient Light Sensor Gain options, values are equal to PGAIN values, so again avoid duplication const __FlashStringHelper *optionsALSGain[] = { F("1x"), - F("4x (default)"), + F("4x"), F("16x"), F("64x") }; FormSelectorOptions selector(optionsGainCount, optionsALSGain, optionsGainValues); + selector.default_index = 1; // 4x selector.addFormSelector(lightSensorGainLabel, F("again"), P064_AGAIN); } { - FormSelectorOptions selector( + const FormSelectorOptions selector( optionsLedDriveCount, optionsLedDrive, optionsLedDriveValues); diff --git a/src/_P066_VEML6040.ino b/src/_P066_VEML6040.ino index e5fc0f3864..339c81644b 100644 --- a/src/_P066_VEML6040.ino +++ b/src/_P066_VEML6040.ino @@ -93,7 +93,7 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) F("1280ms (515)"), }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - FormSelectorOptions selector(optionCount, optionsMode); + const FormSelectorOptions selector(optionCount, optionsMode); selector.addFormSelector(F("Integration Time (Max Lux)"), F("itime"), PCONFIG(1)); } @@ -107,7 +107,7 @@ boolean Plugin_066(uint8_t function, struct EventStruct *event, String& string) F("Color Temperature [K], Ambient Light [Lux], Y, W"), }; constexpr size_t optionCount = NR_ELEMENTS(optionsVarMap); - FormSelectorOptions selector(optionCount, optionsVarMap); + const FormSelectorOptions selector(optionCount, optionsVarMap); selector.addFormSelector(F("Value Mapping"), F("map"), PCONFIG(2)); } diff --git a/src/_P067_HX711_Load_Cell.ino b/src/_P067_HX711_Load_Cell.ino index cb82720ea9..64548e4b77 100644 --- a/src/_P067_HX711_Load_Cell.ino +++ b/src/_P067_HX711_Load_Cell.ino @@ -86,7 +86,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsModeChanA[] = { F("Off"), F("Gain 64"), F("Gain 128") }; - FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanA), optionsModeChanA); + const FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanA), optionsModeChanA); selector.addFormSelector(F("Mode"), F("modeChA"), P067_GET_CHANNEL_A_MODE); } @@ -102,7 +102,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsModeChanB[] = { F("Off"), F("Gain 32") }; - FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanB), optionsModeChanB); + const FormSelectorOptions selector(NR_ELEMENTS(optionsModeChanB), optionsModeChanB); selector.addFormSelector(F("Mode"), F("modeChB"), P067_GET_CHANNEL_B_MODE); } diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index 8fb5735b23..53c221cda1 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -119,7 +119,7 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("TM1637 - 6 digit"), F("MAX7219 - 8 digit") }; constexpr size_t optionCount = NR_ELEMENTS(displtype); - FormSelectorOptions selector(optionCount, displtype); + const FormSelectorOptions selector(optionCount, displtype); selector.addFormSelector(F("Display Type"), F("displtype"), PCONFIG(0)); } { @@ -130,7 +130,7 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("Clock 12h - No Blink"), F("Date") }; constexpr size_t optionCount = NR_ELEMENTS(displout); - FormSelectorOptions selector(optionCount, displout); + const FormSelectorOptions selector(optionCount, displout); selector.addFormSelector(F("Display Output"), F("displout"), PCONFIG(1)); } @@ -144,7 +144,7 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7") }; constexpr size_t optionCount = NR_ELEMENTS(fontset); - FormSelectorOptions selector(optionCount, fontset); + const FormSelectorOptions selector(optionCount, fontset); selector.addFormSelector(F("Font set"), F("fontset"), PCONFIG(4)); addFormNote(F("Check documentation for examples of the font sets.")); } diff --git a/src/_P074_TSL2591.ino b/src/_P074_TSL2591.ino index 45d25b485b..e03d669f9a 100644 --- a/src/_P074_TSL2591.ino +++ b/src/_P074_TSL2591.ino @@ -97,7 +97,7 @@ boolean Plugin_074(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *optionsMode[] = { F("100"), F("200"), F("300"), F("400"), F("500"), F("600") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - FormSelectorOptions selector( optionCount, optionsMode); + const FormSelectorOptions selector( optionCount, optionsMode); selector.addFormSelector(F("Integration Time"), F("itime"), PCONFIG(1)); addUnit(F("ms")); } @@ -110,7 +110,7 @@ boolean Plugin_074(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *optionsGain[] = { F("low gain (1x)"), F("medium gain (25x)"), F("medium gain (428x)"), F("max gain (9876x)") }; constexpr size_t optionCount = NR_ELEMENTS(optionsGain); - FormSelectorOptions selector( optionCount, optionsGain); + const FormSelectorOptions selector( optionCount, optionsGain); selector.addFormSelector(F("Value Mapping"), F("gain"), PCONFIG(2)); } diff --git a/src/_P075_Nextion.ino b/src/_P075_Nextion.ino index 9e43dd28ab..9e7d5b6b34 100644 --- a/src/_P075_Nextion.ino +++ b/src/_P075_Nextion.ino @@ -97,7 +97,7 @@ boolean Plugin_075(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options); + const FormSelectorOptions selector(optionCount, options); selector.addFormSelector(F("Baud Rate"), F("baud"), P075_BaudRate); addUnit(F("baud")); break; diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index 5d71fdd228..47e58d146e 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -333,7 +333,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) #endif // ifdef ESP32 }; constexpr int nrElements = NR_ELEMENTS(predefinedId); - FormSelectorOptions selector(nrElements, predefinedNames, predefinedId); + const FormSelectorOptions selector(nrElements, predefinedNames, predefinedId); selector.addFormSelector(F("Device"), F("preDefDevSel"), devicePinSettings); addFormNote(F("Enable device and select device type first")); } @@ -358,7 +358,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) HIGH, }; - FormSelectorOptions selector(NR_ELEMENTS(modeCurr), modeCurr, modeCurrValues); + const FormSelectorOptions selector(NR_ELEMENTS(modeCurr), modeCurr, modeCurrValues); selector.addFormSelector(F("SEL Current (A) Reading"), F("curr_read"), currentRead); } { @@ -376,7 +376,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String &string) FALLING, }; - FormSelectorOptions selector(NR_ELEMENTS(modeRaise), modeRaise, modeValues); + const FormSelectorOptions selector(NR_ELEMENTS(modeRaise), modeRaise, modeValues); selector.addFormSelector(F("CF1 Interrupt Edge"), F("cf1_edge"), P076_CF1_TRIGGER); selector.addFormSelector(F("CF Interrupt Edge"), F("cf_edge"), P076_CF_TRIGGER); } diff --git a/src/_P078_Eastron.ino b/src/_P078_Eastron.ino index 39e5b44326..d3f53fa7da 100644 --- a/src/_P078_Eastron.ino +++ b/src/_P078_Eastron.ino @@ -120,7 +120,7 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) options_baudrate[i] = p078_storageValueToBaudrate(i); } constexpr size_t optionCount = NR_ELEMENTS(options_baudrate); - FormSelectorOptions selector(optionCount, options_baudrate); + const FormSelectorOptions selector(optionCount, options_baudrate); selector.addFormSelector(F("Baud Rate"), P078_BAUDRATE_LABEL, P078_BAUDRATE); addUnit(F("baud")); } @@ -174,7 +174,7 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) F("SDM320C") }; constexpr size_t nrOptions = NR_ELEMENTS(options_model); - FormSelectorOptions selector(nrOptions, options_model); + const FormSelectorOptions selector(nrOptions, options_model); selector.addFormSelector(F("Model Type"), P078_MODEL_LABEL, P078_MODEL); addFormNote(F("Submit after changing the modell to update Output Configuration.")); } diff --git a/src/_P079_Wemos_Motorshield.ino b/src/_P079_Wemos_Motorshield.ino index ce18aab139..db6c4f66d7 100644 --- a/src/_P079_Wemos_Motorshield.ino +++ b/src/_P079_Wemos_Motorshield.ino @@ -125,7 +125,7 @@ boolean Plugin_079(uint8_t function, struct EventStruct *event, String& string) static_cast(P079_BoardType::LolinMotorshield) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - FormSelectorOptions selector(optionCount, options, indices); + const FormSelectorOptions selector(optionCount, options, indices); selector.addFormSelector(F("Motor Shield Type"), F("shield_type"), SHIELD_VER_PCFG_P079); } diff --git a/src/_P082_GPS.ino b/src/_P082_GPS.ino index 62e8b55672..d5bf404bba 100644 --- a/src/_P082_GPS.ino +++ b/src/_P082_GPS.ino @@ -191,7 +191,7 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) static_cast(P082_PowerMode::Eco) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - FormSelectorOptions selector(optionCount, options, indices); + const FormSelectorOptions selector(optionCount, options, indices); selector.addFormSelector(F("Power Mode"), F("pwrmode"), P082_POWER_MODE); } @@ -221,7 +221,7 @@ boolean Plugin_082(uint8_t function, struct EventStruct *event, String& string) static_cast(P082_DynamicModel::Bike) }; constexpr size_t optionCount = NR_ELEMENTS(indices); - FormSelectorOptions selector(optionCount, options, indices); + const FormSelectorOptions selector(optionCount, options, indices); selector.addFormSelector(F("Dynamic Platform Model"), F("dynmodel"), P082_DYNAMIC_MODEL); } # endif // P082_USE_U_BLOX_SPECIFIC diff --git a/src/_P084_VEML6070.ino b/src/_P084_VEML6070.ino index 297b864a93..aa9c718605 100644 --- a/src/_P084_VEML6070.ino +++ b/src/_P084_VEML6070.ino @@ -88,7 +88,7 @@ boolean Plugin_084(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *optionsMode[] = { F("1/2T"), F("1T"), F("2T"), F("4T (Default)") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode); - FormSelectorOptions selector(optionCount, optionsMode); + const FormSelectorOptions selector(optionCount, optionsMode); selector.addFormSelector(F("Refresh Time Determination"), F("itime"), PCONFIG(0)); success = true; diff --git a/src/_P085_AcuDC243.ino b/src/_P085_AcuDC243.ino index c2fb29ca1e..28dc7e900c 100644 --- a/src/_P085_AcuDC243.ino +++ b/src/_P085_AcuDC243.ino @@ -97,7 +97,7 @@ boolean Plugin_085(uint8_t function, struct EventStruct *event, String& string) for (int i = 0; i < 6; ++i) { options_baudrate[i] = String(p085_storageValueToBaudrate(i)); } - FormSelectorOptions selector(6, options_baudrate); + const FormSelectorOptions selector(6, options_baudrate); selector.addFormSelector(F("Baud Rate"), P085_BAUDRATE_LABEL, P085_BAUDRATE); addUnit(F("baud")); addFormNumericBox(F("Modbus Address"), P085_DEV_ID_LABEL, P085_DEV_ID, 1, 247); diff --git a/src/_P086_Homie.ino b/src/_P086_Homie.ino index 1ef8893203..9748910796 100644 --- a/src/_P086_Homie.ino +++ b/src/_P086_Homie.ino @@ -99,7 +99,7 @@ boolean Plugin_086(uint8_t function, struct EventStruct *event, String& string) addFormTextBox(F("Event Name"), getPluginCustomArgName((i * 10) + 0), Cache.getTaskDeviceValueName(event->TaskIndex, i), NAME_FORMULA_LENGTH_MAX); - FormSelectorOptions selector(PLUGIN_086_VALUE_TYPES, options, optionValues); + const FormSelectorOptions selector(PLUGIN_086_VALUE_TYPES, options, optionValues); selector.addFormSelector(F("Parameter Type"), getPluginCustomArgName((i * 10) + 1), PCONFIG(i)); addFormNumericBox(F("Min"), getPluginCustomArgName((i * 10) + 2), diff --git a/src/_P087_SerialProxy.ino b/src/_P087_SerialProxy.ino index 1d5a1ce041..658bdd47f1 100644 --- a/src/_P087_SerialProxy.ino +++ b/src/_P087_SerialProxy.ino @@ -348,7 +348,7 @@ void P087_html_show_matchForms(struct EventStruct *event) { optionValues[i] = matchType; } P087_Match_Type choice = P087_data->getMatchType(); - FormSelectorOptions selector( + const FormSelectorOptions selector( P087_Match_Type_NR_ELEMENTS, options, optionValues); diff --git a/src/_P090_CCS811.ino b/src/_P090_CCS811.ino index 71cd586f6d..80a69562df 100644 --- a/src/_P090_CCS811.ino +++ b/src/_P090_CCS811.ino @@ -111,7 +111,7 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { const __FlashStringHelper *options[] = { F("0x5A (ADDR pin is LOW)"), F("0x5B (ADDR pin is HIGH)") }; constexpr size_t optionCount = NR_ELEMENTS(options); - FormSelectorOptions selector(optionCount, options, i2cAddressValues); + const FormSelectorOptions selector(optionCount, options, i2cAddressValues); selector.addFormSelector(F("I2C Address"), F("i2c_addr"), P090_I2C_ADDR); } else { success = intArrayContains(2, i2cAddressValues, event->Par1); @@ -136,7 +136,7 @@ boolean Plugin_090(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *frequencyOptions[] = { F("1 second"), F("10 seconds"), F("60 seconds") }; const int frequencyValues[] = { 1, 2, 3 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); selector.addFormSelector(F("Take reading every"), F("temp_freq"), frequencyChoice); } diff --git a/src/_P091_SerSwitch.ino b/src/_P091_SerSwitch.ino index 1b08885c76..7cd07b1132 100644 --- a/src/_P091_SerSwitch.ino +++ b/src/_P091_SerSwitch.ino @@ -125,7 +125,7 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) }; const int optionValues[] = { SER_SWITCH_YEWE, SER_SWITCH_SONOFFDUAL, SER_SWITCH_LCTECH, SER_SWITCH_WIFIDIMMER }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Switch Type"), F("type"), PCONFIG(0)); } @@ -139,7 +139,7 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) }; const int buttonoptionValues[] = { 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(buttonoptionValues); - FormSelectorOptions selector(optionCount, buttonOptions, buttonoptionValues); + const FormSelectorOptions selector(optionCount, buttonOptions, buttonoptionValues); selector.addFormSelector(F("Number of relays"), F("button"), PCONFIG(1)); } @@ -151,7 +151,7 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) F("Simultaneous mode"), }; constexpr size_t optionCount = NR_ELEMENTS(modeoptions); - FormSelectorOptions selector(optionCount, modeoptions); + const FormSelectorOptions selector(optionCount, modeoptions); selector.addFormSelector(F("Relay working mode"), F("mode"), PCONFIG(1)); } @@ -160,7 +160,7 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) { const int buttonoptionValues[] = { 1, 2, 3, 4 }; constexpr size_t optionCount = NR_ELEMENTS(buttonoptionValues); - FormSelectorOptions selector(optionCount, buttonoptionValues); + const FormSelectorOptions selector(optionCount, buttonoptionValues); selector.addFormSelector(F("Number of relays"), F("button"), PCONFIG(1)); } @@ -176,7 +176,7 @@ boolean Plugin_091(uint8_t function, struct EventStruct *event, String& string) F("57600"), }; constexpr size_t optionCount = NR_ELEMENTS(speedOptions); - FormSelectorOptions selector(optionCount, speedOptions); + const FormSelectorOptions selector(optionCount, speedOptions); selector.addFormSelector(F("Serial speed"), F("speed"), PCONFIG(2)); addUnit(F("baud")); } diff --git a/src/_P092_DLbus.ino b/src/_P092_DLbus.ino index b1e4cf6e3e..4a8a0e06e7 100644 --- a/src/_P092_DLbus.ino +++ b/src/_P092_DLbus.ino @@ -138,7 +138,7 @@ boolean Plugin_092(uint8_t function, struct EventStruct *event, String& string) static_cast(eP092pinmode::ePPM_Input), static_cast(eP092pinmode::ePPM_InputPullUp) }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); selector.addFormSelector(F("Pin mode"), F("ppinmode"), choice); } { diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index d8b9124367..4babc42fd7 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -261,7 +261,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) # endif // if P095_ENABLE_ILI948X }; constexpr size_t optionCount = NR_ELEMENTS(hardwareOptions); - FormSelectorOptions selector(optionCount, + const FormSelectorOptions selector(optionCount, hardwareTypes, hardwareOptions); selector.addFormSelector( @@ -312,7 +312,7 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) # endif // if P095_ENABLE_ILI948X }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector( F("Write Command trigger"), F("commandtrigger"), P095_CONFIG_FLAG_GET_CMD_TRIGGER); diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 5503e6a158..c698c2793a 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -273,7 +273,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) # endif // if P096_USE_WAVESHARE_2IN7 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - FormSelectorOptions selector( + const FormSelectorOptions selector( optionCount, options4, optionValues4); @@ -292,7 +292,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); - 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 @@ -340,7 +340,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) P096_CONFIG_FLAGS = lSettings; } constexpr size_t optionCount = NR_ELEMENTS(colorDepthOptions); - FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); + const FormSelectorOptions selector(optionCount, colorDepths, colorDepthOptions); selector.addFormSelector(F("Greyscale levels"),F("_colorDepth"), P096_CONFIG_FLAG_GET_COLORDEPTH); } @@ -377,7 +377,7 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) # endif // if P096_USE_WAVESHARE_2IN7 }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector(F("Write Command trigger"), F("_commandtrigger"), P096_CONFIG_FLAG_GET_CMD_TRIGGER); addFormNote(F("Select the command that is used to handle commands for this display.")); diff --git a/src/_P098_PWM_motor.ino b/src/_P098_PWM_motor.ino index 9839027540..e35190a046 100644 --- a/src/_P098_PWM_motor.ino +++ b/src/_P098_PWM_motor.ino @@ -181,7 +181,7 @@ boolean Plugin_098(uint8_t function, struct EventStruct *event, String& string) options[i] = P098_config_struct::toString(static_cast(i)); optionValues[i] = i; } - FormSelectorOptions selector(P098_PWM_MODE_TYPES, options, optionValues); + const FormSelectorOptions selector(P098_PWM_MODE_TYPES, options, optionValues); selector.addFormSelector(F("Motor Control"), F("motor_contr"), P098_MOTOR_CONTROL); } addFormNumericBox(F("PWM Frequency"), F("pwm_freq"), P098_PWM_FREQ, 50, 100000); diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index 8631c30dcc..272c22417d 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -128,7 +128,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) const int optionValues2[] = { 0, 1, 2, 3 }; // Rotation similar to the // TFT ILI9341 rotation constexpr size_t optionCount = NR_ELEMENTS(optionValues2); - FormSelectorOptions selector(optionCount, options2, optionValues2); + const FormSelectorOptions selector(optionCount, options2, optionValues2); selector.addFormSelector(F("Rotation"), F("protate"), choice2); } @@ -154,7 +154,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) F("Objectnames, X, Y and Z") }; const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! constexpr size_t optionCount = NR_ELEMENTS(optionValues3); - FormSelectorOptions selector(optionCount, options3, optionValues3); + const FormSelectorOptions selector(optionCount, options3, optionValues3); selector.addFormSelector(F("Events"), F("pevents"), choice3); } diff --git a/src/_P100_DS2423_counter.ino b/src/_P100_DS2423_counter.ino index 46fd52091e..946520ff54 100644 --- a/src/_P100_DS2423_counter.ino +++ b/src/_P100_DS2423_counter.ino @@ -64,7 +64,7 @@ boolean Plugin_100(uint8_t function, struct EventStruct *event, String& string) // Counter select const __FlashStringHelper *resultsOptions[] = { F("A"), F("B") }; constexpr size_t optionCount = NR_ELEMENTS(resultsOptions); - FormSelectorOptions selector(optionCount, resultsOptions); + const FormSelectorOptions selector(optionCount, resultsOptions); selector.addFormSelector(F("Counter"), F("counter"), PCONFIG(0)); addFormNote(F("Counter value is incremental")); } diff --git a/src/_P102_PZEM004Tv3.ino b/src/_P102_PZEM004Tv3.ino index f1c5c518f2..0ef7aa71e8 100644 --- a/src/_P102_PZEM004Tv3.ino +++ b/src/_P102_PZEM004Tv3.ino @@ -150,7 +150,7 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_model[] = { F("Read_value"), F("Reset_Energy"), F("Program_adress") }; constexpr size_t optionCount = NR_ELEMENTS(options_model); - FormSelectorOptions selector(optionCount, options_model); + const FormSelectorOptions selector(optionCount, options_model); selector.addFormSelector(F("PZEM Mode"), F("PZEM_mode"), P102_PZEM_mode); } @@ -161,7 +161,7 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_confirm[] = { F("NO"), F("YES") }; constexpr size_t optionCount = NR_ELEMENTS(options_confirm); - FormSelectorOptions selector(optionCount, options_confirm); + const FormSelectorOptions selector(optionCount, options_confirm); selector.addFormSelector(F("Confirm address programming ?"), F("PZEM_addr_set"), P102_PZEM_ADDR_SET); } addFormNumericBox(F("Address of PZEM"), F("PZEM_addr"), (P102_PZEM_ADDR < 1) ? 1 : P102_PZEM_ADDR, 1, 247); @@ -185,7 +185,7 @@ boolean Plugin_102(uint8_t function, struct EventStruct *even { const __FlashStringHelper *options_model[] = { F("Read_value"), F("Reset_Energy") }; constexpr size_t optionCount = NR_ELEMENTS(options_model); - FormSelectorOptions selector(optionCount, options_model); + const FormSelectorOptions selector(optionCount, options_model); selector.addFormSelector(F("PZEM Mode"), F("PZEM_mode"), P102_PZEM_mode); } addHtml(F(" Tx/Rx Pins config disabled: Configuration is available in the first PZEM plugin.
")); diff --git a/src/_P108_DDS238.ino b/src/_P108_DDS238.ino index d0fd6d6712..a57c26337c 100644 --- a/src/_P108_DDS238.ino +++ b/src/_P108_DDS238.ino @@ -106,7 +106,7 @@ boolean Plugin_108(uint8_t function, struct EventStruct *event, String& string) for (int i = 0; i < 4; ++i) { options_baudrate[i] = String(p108_storageValueToBaudrate(i)); } - FormSelectorOptions selector(4, options_baudrate); + const FormSelectorOptions selector(4, options_baudrate); selector.addFormSelector(F("Baud Rate"), P108_BAUDRATE_LABEL, P108_BAUDRATE); addUnit(F("baud")); addFormNumericBox(F("Modbus Address"), P108_DEV_ID_LABEL, P108_DEV_ID, 1, 247); diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index d5dc3d8c70..08b5a70d32 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -181,7 +181,7 @@ boolean Plugin_109(uint8_t function, struct EventStruct *event, String& string) const __FlashStringHelper *options4[] = { F("0.2"), F("0.5"), F("1") }; const int optionValues4[] = { 2, 5, 10 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues4); - FormSelectorOptions selector(optionCount, options4, optionValues4); + const FormSelectorOptions selector(optionCount, options4, optionValues4); selector.addFormSelector(F("Hysteresis"), F("hyst"), static_cast(P109_CONFIG_HYSTERESIS * 10.0f)); } diff --git a/src/_P110_VL53L0X.ino b/src/_P110_VL53L0X.ino index 5ccc6d157a..ad7b520861 100644 --- a/src/_P110_VL53L0X.ino +++ b/src/_P110_VL53L0X.ino @@ -100,7 +100,7 @@ boolean Plugin_110(uint8_t function, struct EventStruct *event, String& string) F("Accurate") }; const int optionValuesMode2[] = { 80, 20, 320 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + const FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); selector.addFormSelector(F("Timing"), F("ptiming"), P110_TIMING); } @@ -109,7 +109,7 @@ boolean Plugin_110(uint8_t function, struct EventStruct *event, String& string) F("Normal"), F("Long") }; constexpr size_t optionCount = NR_ELEMENTS(optionsMode3); - FormSelectorOptions selector(optionCount, optionsMode3); + const FormSelectorOptions selector(optionCount, optionsMode3); selector.addFormSelector(F("Range"), F("prange"), P110_RANGE); } addFormCheckBox(F("Send event when value unchanged"), F("notchanged"), P110_SEND_ALWAYS == 1); diff --git a/src/_P111_RC522_RFID.ino b/src/_P111_RC522_RFID.ino index c357adad9d..403482c91c 100644 --- a/src/_P111_RC522_RFID.ino +++ b/src/_P111_RC522_RFID.ino @@ -87,7 +87,7 @@ boolean Plugin_111(uint8_t function, struct EventStruct *event, String& string) # endif // P111_USE_REMOVAL }; constexpr size_t P111_removaltypes = NR_ELEMENTS(removalopts); - FormSelectorOptions selector(P111_removaltypes, removaltype, removalopts); + const FormSelectorOptions selector(P111_removaltypes, removaltype, removalopts); selector.addFormSelector(F("Tag removal mode"), F("autotagremoval"), P111_TAG_AUTOREMOVAL); } diff --git a/src/_P112_AS7265x.ino b/src/_P112_AS7265x.ino index dd8c4af7c5..16afcb216b 100644 --- a/src/_P112_AS7265x.ino +++ b/src/_P112_AS7265x.ino @@ -106,7 +106,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setGain(AS7265X_GAIN_64X); const __FlashStringHelper *optionsMode[] = { F("1x"), - F("3.7x (default)"), + F("3.7x"), F("16x"), F("64x"), }; @@ -118,6 +118,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode); FormSelectorOptions selector(optionCount, optionsMode, optionValuesMode); + selector.default_index = AS7265X_GAIN_37X; selector.addFormSelector(F("Gain"), F("Gain"), PCONFIG_LONG(0)); } { @@ -130,7 +131,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) F("56"), F("140"), F("280"), - F("711 (default)"), + F("711"), }; const int optionValuesMode2[] = { 0, @@ -142,6 +143,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + selector.default_index = 254; // "711" selector.addFormSelector(F("Integration Time"), F("IntegrationTime"), PCONFIG_LONG(1)); addUnit(F("ms")); } @@ -161,7 +163,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) F("1"), F("2"), F("4"), - F("8 (default)"), + F("8"), }; const int optionValuesMode3[] = { AS7265X_INDICATOR_CURRENT_LIMIT_1MA, @@ -171,6 +173,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode3); FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3); + selector.default_index = AS7265X_INDICATOR_CURRENT_LIMIT_8MA; selector.addFormSelector(EMPTY_STRING, PCONFIG_LABEL(1), PCONFIG(1)); addUnit(F("mA")); } @@ -186,7 +189,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_WHITE); //Allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_WHITE); //Allowed const __FlashStringHelper *optionsMode4[] = { - F("12.5 (default)"), + F("12.5"), F("25"), F("50"), F("100"), @@ -199,6 +202,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode4); FormSelectorOptions selector(optionCount, optionsMode4, optionValuesMode4); + selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA; selector.addFormSelector(F("White"), PCONFIG_LABEL(2), PCONFIG(2)); addUnit(F("mA")); } @@ -211,7 +215,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_IR); //Allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_IR-bad); //Not allowed const __FlashStringHelper *optionsMode5[] = { - F("12.5 (default)"), + F("12.5"), F("25"), F("50"), }; @@ -222,6 +226,7 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode5); FormSelectorOptions selector(optionCount, optionsMode5, optionValuesMode5); + selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA; selector.addFormSelector(F("IR"), PCONFIG_LABEL(3), PCONFIG(3)); addUnit(F("mA")); } @@ -232,10 +237,11 @@ boolean Plugin_112(uint8_t function, struct EventStruct *event, String& string) // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_25MA, AS7265x_LED_UV-bad); //Not allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_50MA, AS7265x_LED_UV-bad); //Not allowed // sensor.setBulbCurrent(AS7265X_LED_CURRENT_LIMIT_100MA, AS7265x_LED_UV-bad); //Not allowed - const __FlashStringHelper *optionsMode6[] = { F("12.5 (default)") }; + const __FlashStringHelper *optionsMode6[] = { F("12.5") }; const int optionValuesMode6[] = { AS7265X_LED_CURRENT_LIMIT_12_5MA }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode6); FormSelectorOptions selector(optionCount, optionsMode6, optionValuesMode6); + selector.default_index = AS7265X_LED_CURRENT_LIMIT_12_5MA; selector.addFormSelector(F("UV"), PCONFIG_LABEL(4), PCONFIG(4)); addUnit(F("mA")); } diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index 385084e377..ffdae3b74f 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -111,7 +111,7 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) }; const int optionValuesMode2[] = { 100, 20, 33, 50, 200, 500 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + const FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); selector.addFormSelector(F("Timing"), F("timing"), P113_TIMING); addUnit(F("ms")); } @@ -123,7 +123,7 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) }; const int optionValuesMode3[2] = { 0, 1 }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode3); - FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3); + const FormSelectorOptions selector(optionCount, optionsMode3, optionValuesMode3); selector.addFormSelector(F("Range"), F("range"), P113_RANGE); } addFormCheckBox(F("Send event when value unchanged"), F("notchanged"), P113_SEND_ALWAYS == 1); diff --git a/src/_P114_VEML6075.ino b/src/_P114_VEML6075.ino index 9a2af708a9..efa38d4870 100644 --- a/src/_P114_VEML6075.ino +++ b/src/_P114_VEML6075.ino @@ -94,7 +94,7 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) P114_IT_800, }; constexpr size_t optionCount = NR_ELEMENTS(optionValuesMode2); - FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); + const FormSelectorOptions selector(optionCount, optionsMode2, optionValuesMode2); selector.addFormSelector(F("Integration Time"), F("it"), PCONFIG(1)); addUnit(F("ms")); } @@ -105,7 +105,7 @@ boolean Plugin_114(uint8_t function, struct EventStruct *event, String& string) F("High Dynamic") } ; constexpr size_t optionCount = NR_ELEMENTS(optionsMode3); - FormSelectorOptions selector(optionCount, optionsMode3); + const FormSelectorOptions selector(optionCount, optionsMode3); selector.addFormSelector(F("Dynamic Setting"), F("hd"), PCONFIG(2)); } diff --git a/src/_P115_MAX1704x_v2.ino b/src/_P115_MAX1704x_v2.ino index 64d7aaa089..ab1f08274d 100644 --- a/src/_P115_MAX1704x_v2.ino +++ b/src/_P115_MAX1704x_v2.ino @@ -107,7 +107,7 @@ boolean Plugin_115(uint8_t function, struct EventStruct *event, String& string) MAX1704X_MAX17048, MAX1704X_MAX17049 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Device"), F("device"), choice); } diff --git a/src/_P116_ST77xx.ino b/src/_P116_ST77xx.ino index d19a66e9ea..f7a618d16d 100644 --- a/src/_P116_ST77xx.ino +++ b/src/_P116_ST77xx.ino @@ -180,7 +180,7 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) static_cast(ST77xx_type_e::ST7796s_320x480) }; constexpr int optCount4 = NR_ELEMENTS(optionValues4); - FormSelectorOptions selector(optCount4, options4, optionValues4); + const FormSelectorOptions selector(optCount4, options4, optionValues4); selector.addFormSelector(F("TFT display model"), F("type"), P116_CONFIG_FLAG_GET_TYPE); @@ -216,7 +216,7 @@ boolean Plugin_116(uint8_t function, struct EventStruct *event, String& string) static_cast(P116_CommandTrigger::st7796) }; constexpr int cmdCount = NR_ELEMENTS(commandTriggerOptions); - FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); + const FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); selector.addFormSelector( F("Write Command trigger"), F("commandtrigger"), diff --git a/src/_P119_ITG3205_Gyro.ino b/src/_P119_ITG3205_Gyro.ino index 247416aca7..2c67cfdd21 100644 --- a/src/_P119_ITG3205_Gyro.ino +++ b/src/_P119_ITG3205_Gyro.ino @@ -124,7 +124,7 @@ boolean Plugin_119(uint8_t function, struct EventStruct *event, String& string) F("50") }; const int frequencyValues[] = { P119_FREQUENCY_10, P119_FREQUENCY_50 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); selector.addFormSelector(F("Measuring frequency"), F("frequency"), P119_FREQUENCY); addUnit(F("Hz")); diff --git a/src/_P122_SHT2x.ino b/src/_P122_SHT2x.ino index 9504a1620f..e842e20fe4 100644 --- a/src/_P122_SHT2x.ino +++ b/src/_P122_SHT2x.ino @@ -138,7 +138,7 @@ boolean Plugin_122(uint8_t function, struct EventStruct *event, String& string) P122_RESOLUTION_11T_11RH, }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("Resolution"), P122_RESOLUTION_LABEL, P122_RESOLUTION); # ifndef LIMIT_BUILD_SIZE diff --git a/src/_P123_I2CTouch.ino b/src/_P123_I2CTouch.ino index cd047760ad..5b5a06db90 100644 --- a/src/_P123_I2CTouch.ino +++ b/src/_P123_I2CTouch.ino @@ -201,7 +201,7 @@ boolean Plugin_123(uint8_t function, struct EventStruct *event, String& string) static_cast(P123_TouchType_e::Automatic), }; constexpr size_t optionCount = NR_ELEMENTS(touchTypeOptions); - FormSelectorOptions selector(optionCount, touchTypes, touchTypeOptions); + const FormSelectorOptions selector(optionCount, touchTypes, touchTypeOptions); selector.addFormSelector( F("Touchscreen type (address)"), F("ttype"), P123_GET_TOUCH_TYPE); diff --git a/src/_P126_74HC595.ino b/src/_P126_74HC595.ino index 1e58826fbd..a7ff7c4e12 100644 --- a/src/_P126_74HC595.ino +++ b/src/_P126_74HC595.ino @@ -167,7 +167,7 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string) F("Hex/bin only") }; const int outputValues[] = { P126_OUTPUT_BOTH, P126_OUTPUT_DEC_ONLY, P126_OUTPUT_HEXBIN }; constexpr size_t optionCount = NR_ELEMENTS(outputValues); - FormSelectorOptions selector(optionCount, outputOptions, outputValues); + const FormSelectorOptions selector(optionCount, outputOptions, outputValues); selector.addFormSelector(F("Output selection"), F("output"), P126_CONFIG_FLAGS_GET_OUTPUT_SELECTION); addFormCheckBox(F("Restore Values on warm boot"), F("valrestore"), P126_CONFIG_FLAGS_GET_VALUES_RESTORE); diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino index 3abdc75a79..32d36b7e84 100644 --- a/src/_P129_74HC165.ino +++ b/src/_P129_74HC165.ino @@ -177,7 +177,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("50/sec (20 msec)") }; const int frequencyValues[] = { P129_FREQUENCY_10, P129_FREQUENCY_50 }; constexpr size_t optionCount = NR_ELEMENTS(frequencyValues); - FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); + const FormSelectorOptions selector(optionCount, frequencyOptions, frequencyValues); selector.addFormSelector( F("Sample frequency"), F("frequency"), P129_CONFIG_FLAGS_GET_READ_FREQUENCY); @@ -193,7 +193,7 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) F("Hex/bin only") }; const int outputValues[] = { P129_OUTPUT_BOTH, P129_OUTPUT_DEC_ONLY, P129_OUTPUT_HEXBIN }; constexpr size_t outputCount = NR_ELEMENTS(outputValues); - FormSelectorOptions selector_output(outputCount, outputOptions, outputValues); + const FormSelectorOptions selector_output(outputCount, outputOptions, outputValues); selector_output.addFormSelector(F("Output selection"), F("outputsel"), P129_CONFIG_FLAGS_GET_OUTPUT_SELECTION); addFormCheckBox(F("Separate events per pin"), F("separate_events"), P129_CONFIG_FLAGS_GET_SEPARATE_EVENTS == 1); diff --git a/src/_P131_NeoPixelMatrix.ino b/src/_P131_NeoPixelMatrix.ino index 6318ce2862..8810888934 100644 --- a/src/_P131_NeoPixelMatrix.ino +++ b/src/_P131_NeoPixelMatrix.ino @@ -92,7 +92,7 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; // Selection copied from P038 - FormSelectorOptions selector(2, stripOptions, optionValuesZeroOne); + const FormSelectorOptions selector(2, stripOptions, optionValuesZeroOne); selector.addFormSelector(F("Strip Type"), F("striptype"), P131_CONFIG_FLAGS_GET_STRIP_TYPE); } @@ -119,9 +119,9 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) addFormNumericBox(F("Matrix height"), F("mxheight"), P131_CONFIG_MATRIX_HEIGHT, 1, 100); - FormSelectorOptions selTop(4, optionsTop, optionValuesTop); - FormSelectorOptions selRowCol(2, optionsRowCol, optionValuesZeroOne); - FormSelectorOptions selProZig(2, optionsProZig, optionValuesZeroOne); + const FormSelectorOptions selTop(4, optionsTop, optionValuesTop); + const FormSelectorOptions selRowCol(2, optionsRowCol, optionValuesZeroOne); + const FormSelectorOptions selProZig(2, optionsProZig, optionValuesZeroOne); selTop.addFormSelector(F("Matrix start-pixel"), F("mxstart"), get2BitFromUL(P131_CONFIG_FLAGS, P131_FLAGS_MATRIX_TYPE_TOP)); @@ -180,7 +180,7 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) static_cast(P131_CommandTrigger::neo) }; constexpr int cmdCount = NR_ELEMENTS(commandTriggerOptions); - FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); + const FormSelectorOptions selector(cmdCount, commandTriggers, commandTriggerOptions); selector.addFormSelector( F("Write Command trigger"), F("cmdtrigger"), P131_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE diff --git a/src/_P132_INA3221.ino b/src/_P132_INA3221.ino index 994d5cd1ba..81934354ee 100644 --- a/src/_P132_INA3221.ino +++ b/src/_P132_INA3221.ino @@ -111,7 +111,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) }; constexpr size_t optionCount = NR_ELEMENTS(varOptions); - FormSelectorOptions selector(optionCount, varOptions); + const FormSelectorOptions selector(optionCount, varOptions); for (uint8_t r = 0; r < VARS_PER_TASK; ++r) { selector.addFormSelector( @@ -126,14 +126,15 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *varshuntptions[] = { - F("0.1 ohm"), - F("0.01 ohm"), - F("0.005 ohm"), + F("0.1"), + F("0.01"), + F("0.005"), }; const int shuntvalue[] = { 1, 10, 20 }; constexpr size_t optionCount = NR_ELEMENTS(shuntvalue); - FormSelectorOptions selector(optionCount, varshuntptions, shuntvalue); + const FormSelectorOptions selector(optionCount, varshuntptions, shuntvalue); selector.addFormSelector(F("Shunt resistor"), F("shunt"), P132_SHUNT); + addUnit(F("Ohm")); addFormNote(F("Select as is installed on the board.")); } @@ -141,7 +142,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) { const __FlashStringHelper *averagingSamples[] = { - F("1 (default)"), + F("1"), F("4"), F("16"), F("64"), @@ -153,6 +154,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const int averageValue[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; 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); addFormNote(F("Samples > 16 then min. Interval: 64= 4, 128= 7, 256= 14, 512= 26, 1024= 52 seconds!")); } @@ -163,7 +165,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) F("204 µsec"), F("332 µsec"), F("588 µsec"), - F("1.1 msec (default)"), + F("1.1 msec"), F("2.116 msec"), F("4.156 msec"), F("8.244 msec"), @@ -173,6 +175,7 @@ boolean Plugin_132(uint8_t function, struct EventStruct *event, String& string) const int conversionValues[] = { 0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111 }; constexpr size_t optionCount = NR_ELEMENTS(conversionValues); FormSelectorOptions selector(optionCount, conversionRates, conversionValues); + selector.default_index = 0b100; // 1.1ms selector.addFormSelector(F("Conversion rate Voltage"), F("conv_v"), P132_GET_CONVERSION_B); selector.addFormSelector(F("Conversion rate Current"), F("conv_c"), P132_GET_CONVERSION_S); } diff --git a/src/_P133_LTR390.ino b/src/_P133_LTR390.ino index 9b1976ba99..f22a1e73a8 100644 --- a/src/_P133_LTR390.ino +++ b/src/_P133_LTR390.ino @@ -129,8 +129,8 @@ boolean Plugin_133(uint8_t function, struct EventStruct *event, String& string) LTR390_RESOLUTION_13BIT, }; constexpr size_t resolutionCount = NR_ELEMENTS(resolutionValues); - FormSelectorOptions selGain(gainCount, gainOptions, gainValues); - FormSelectorOptions selRes(resolutionCount, resolutionOptions, resolutionValues); + const FormSelectorOptions selGain(gainCount, gainOptions, gainValues); + const FormSelectorOptions selRes(resolutionCount, resolutionOptions, resolutionValues); if (static_cast(P133_SELECT_MODE) != P133_selectMode_e::ALSMode) { selGain.addFormSelector(F("UV Gain"), F("uvgain"), P133_UVGAIN); diff --git a/src/_P141_PCD8544_Nokia5110.ino b/src/_P141_PCD8544_Nokia5110.ino index 8d9cb32a1a..7c79d281ad 100644 --- a/src/_P141_PCD8544_Nokia5110.ino +++ b/src/_P141_PCD8544_Nokia5110.ino @@ -148,7 +148,7 @@ boolean Plugin_141(uint8_t function, struct EventStruct *event, String& string) static_cast(P141_CommandTrigger::lcd), }; constexpr size_t optionCount = NR_ELEMENTS(commandTriggerOptions); - FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); + const FormSelectorOptions selector(optionCount, commandTriggers, commandTriggerOptions); selector.addFormSelector( F("Write Command trigger"), F("pcmdtrigger"), P141_CONFIG_FLAG_GET_CMD_TRIGGER); # ifndef LIMIT_BUILD_SIZE diff --git a/src/_P142_AS5600.ino b/src/_P142_AS5600.ino index 4e29c63ec5..d4e07480b5 100644 --- a/src/_P142_AS5600.ino +++ b/src/_P142_AS5600.ino @@ -173,7 +173,7 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_MODE_RADIANS, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Output range"), F("range"), P142_GET_OUTPUT_MODE); } addFormCheckBox(F("Generate Events only when changed"), F("diff"), P142_GET_UPDATE_DIFF_ONLY); @@ -208,7 +208,7 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_POWERMODE_LOW3, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Power mode"), F("pow"), P142_GET_POWER_MODE); } addFormCheckBox(F("Power watchdog"), F("wdog"), P142_GET_WATCHDOG); @@ -229,7 +229,7 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_HYST_LSB3, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Hysteresis"), F("hyst"), P142_GET_HYSTERESIS); } { @@ -246,7 +246,7 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_SLOW_FILT_2X, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Slow filter"), F("sflt"), P142_GET_SLOW_FILTER); } { @@ -271,7 +271,7 @@ boolean Plugin_142(uint8_t function, struct EventStruct *event, String& string) AS5600_FAST_FILT_LSB10, }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Fast filter"), F("fflt"), P142_GET_FAST_FILTER); } success = true; diff --git a/src/_P143_I2C_Rotary.ino b/src/_P143_I2C_Rotary.ino index fb0d3e8efa..3ebf0d7fd8 100644 --- a/src/_P143_I2C_Rotary.ino +++ b/src/_P143_I2C_Rotary.ino @@ -237,7 +237,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_M5StackLed_e::Led2Only), }; constexpr size_t optionCount = NR_ELEMENTS(selectLedModeValues); - FormSelectorOptions selector(optionCount, selectLedModeOptions, selectLedModeValues); + const FormSelectorOptions selector(optionCount, selectLedModeOptions, selectLedModeValues); selector.addFormSelector(F("Color map Leds"), F("pledsel"), P143_M5STACK_SELECTION); } # endif // if P143_FEATURE_INCLUDE_M5STACK @@ -279,7 +279,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_ButtonAction_e::ToggleSwitch), }; constexpr size_t optionCount = NR_ELEMENTS(selectButtonValues); - FormSelectorOptions selector(optionCount, selectButtonOptions, selectButtonValues); + const FormSelectorOptions selector(optionCount, selectButtonOptions, selectButtonValues); selector.addFormSelector(F("Button action"), F("pbutton"), P143_PLUGIN_BUTTON_ACTION); # if P143_FEATURE_INCLUDE_DFROBOT @@ -318,7 +318,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) static_cast(P143_CounterMapping_e::ColorGradient), }; constexpr size_t optionCount = NR_ELEMENTS(selectCounterValues); - FormSelectorOptions selector(optionCount, selectCounterOptions, selectCounterValues); + const FormSelectorOptions selector(optionCount, selectCounterOptions, selectCounterValues); selector.addFormSelector(F("Counter color mapping"), F("pmap"), P143_PLUGIN_COUNTER_MAPPING); } { diff --git a/src/_P145_MQxxx.ino b/src/_P145_MQxxx.ino index 95f543959c..41855fb61b 100644 --- a/src/_P145_MQxxx.ino +++ b/src/_P145_MQxxx.ino @@ -191,7 +191,7 @@ boolean Plugin_145(byte function, struct EventStruct *event, String& string) { options[i] = concat(concat(P145_data_struct::getTypeName(i), F(" - ")), P145_data_struct::getGasName(i)); } - FormSelectorOptions selector(x, options); + const FormSelectorOptions selector(x, options); selector.addFormSelector(F("Sensor type"), F(P145_GUID_TYPE), P145_PCONFIG_SENSORT); # ifdef ESP32 diff --git a/src/_P146_CacheControllerReader.ino b/src/_P146_CacheControllerReader.ino index 10d6b30e0d..816f6a8cc4 100644 --- a/src/_P146_CacheControllerReader.ino +++ b/src/_P146_CacheControllerReader.ino @@ -221,7 +221,7 @@ boolean Plugin_146(uint8_t function, struct EventStruct *event, String& string) ';' }; constexpr size_t optionCount = NR_ELEMENTS(separatorOptions); - FormSelectorOptions selector(optionCount, separatorLabels, separatorOptions); + const FormSelectorOptions selector(optionCount, separatorLabels, separatorOptions); selector.addFormSelector(F("Separator"), F("separator"), P146_SEPARATOR_CHARACTER); } addFormCheckBox(F("Join Samples with same Timestamp"), F("jointimestamp"), P146_GET_JOIN_TIMESTAMP); diff --git a/src/_P148_POWRxxD_THR3xxD.ino b/src/_P148_POWRxxD_THR3xxD.ino index 710b629453..f9948c6ad3 100644 --- a/src/_P148_POWRxxD_THR3xxD.ino +++ b/src/_P148_POWRxxD_THR3xxD.ino @@ -121,7 +121,7 @@ boolean Plugin_148(uint8_t function, struct EventStruct *event, String& string) static_cast(P148_data_struct::Tm1621Device::THR3xxD) }; constexpr size_t nrElements = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(nrElements, options, optionValues); + const FormSelectorOptions selector(nrElements, options, optionValues); selector.addFormSelector(F("Device Template"), F("devtmpl"), P148_DEVICE_SELECTOR); addFormNote(F("GPIO settings will be ignored when selecting other than 'Custom'")); } diff --git a/src/_P150_TMP117.ino b/src/_P150_TMP117.ino index 402eebb5dd..2a21280e14 100644 --- a/src/_P150_TMP117.ino +++ b/src/_P150_TMP117.ino @@ -135,7 +135,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) P150_AVERAGING_64_SAMPLES, }; constexpr size_t optionCount = NR_ELEMENTS(averagingOptions); - FormSelectorOptions selector(optionCount, averagingCaptions, averagingOptions); + const FormSelectorOptions selector(optionCount, averagingCaptions, averagingOptions); selector.addFormSelector(F("Averaging"), F("avg"), P150_GET_CONF_AVERAGING); } @@ -179,7 +179,7 @@ boolean Plugin_150(uint8_t function, struct EventStruct *event, String& string) P150_CYCLE_16_SEC, }; constexpr size_t optionCount = NR_ELEMENTS(cycleOptions); - FormSelectorOptions selector(optionCount, cycleCaptions, cycleOptions); + const FormSelectorOptions selector(optionCount, cycleCaptions, cycleOptions); selector.addFormSelector(F("Continuous conversion cycle time"), F("cycle"), P150_GET_CONF_CYCLE_BITS); } diff --git a/src/_P153_SHT4x.ino b/src/_P153_SHT4x.ino index 4a84fd13ab..7d7b92eb8d 100644 --- a/src/_P153_SHT4x.ino +++ b/src/_P153_SHT4x.ino @@ -147,14 +147,14 @@ boolean Plugin_153(uint8_t function, struct EventStruct *event, String& string) static_cast(P153_configuration_e::HighResolution20mW100msec), }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Startup Configuration"), F("startup"), P153_STARTUP_CONFIGURATION); addFormNote(F("Heater should not exceed 10% dutycycle, so 1 sec. heater must have Interval > 10 sec.!")); addFormNumericBox(F("Use Normal Configuration after"), F("loops"), P153_INTERVAL_LOOPS, 0, 10); addUnit(F("Interval runs (0..10)")); - FormSelectorOptions selector_normal( + const FormSelectorOptions selector_normal( 3, // Only non-heater options configurations, configurationOptions); diff --git a/src/_P166_GP8403.ino b/src/_P166_GP8403.ino index f248158553..e00eae4cd6 100644 --- a/src/_P166_GP8403.ino +++ b/src/_P166_GP8403.ino @@ -127,7 +127,7 @@ boolean Plugin_166(uint8_t function, struct EventStruct *event, String& string) static_cast(DFRobot_GP8403::eOutPutRange_t::eOutputRange10V), }; constexpr size_t optionCount = NR_ELEMENTS(configurationOptions); - FormSelectorOptions selector(optionCount, configurations, configurationOptions); + const FormSelectorOptions selector(optionCount, configurations, configurationOptions); selector.addFormSelector(F("Output range"), F("range"), P166_MAX_VOLTAGE); } diff --git a/src/_P168_VEML6030_7700.ino b/src/_P168_VEML6030_7700.ino index dd78a99fc0..1a9aa30bcc 100644 --- a/src/_P168_VEML6030_7700.ino +++ b/src/_P168_VEML6030_7700.ino @@ -114,7 +114,7 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) VEML_LUX_CORRECTED_NOWAIT, }; constexpr size_t optionCount = NR_ELEMENTS(readMethodOptions); - FormSelectorOptions selector(optionCount, readMethod, readMethodOptions); + const FormSelectorOptions selector(optionCount, readMethod, readMethodOptions); selector.addFormSelector(F("Lux Read-method"), F("rmth"), P168_READLUX_MODE); addFormNote(F("For 'Auto' Read-method, the Gain factor and Integration time settings are ignored.")); } @@ -132,7 +132,7 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) 0b11, }; constexpr size_t optionCount = NR_ELEMENTS(alsGainOptions); - FormSelectorOptions selector(optionCount, alsGain, alsGainOptions); + const FormSelectorOptions selector(optionCount, alsGain, alsGainOptions); selector.addFormSelector(F("Gain factor"), F("gain"), P168_ALS_GAIN); } { @@ -153,7 +153,7 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) 0b0011, }; constexpr size_t optionCount = NR_ELEMENTS(alsIntegrationOptions); - FormSelectorOptions selector(optionCount, alsIntegration, alsIntegrationOptions); + const FormSelectorOptions selector(optionCount, alsIntegration, alsIntegrationOptions); selector.addFormSelector(F("Integration time"), F("int"), P168_ALS_INTEGRATION); addUnit(F("ms")); } @@ -174,7 +174,7 @@ boolean Plugin_168(uint8_t function, struct EventStruct *event, String& string) static_cast(P168_power_save_mode_e::Mode4), }; constexpr size_t optionCount = NR_ELEMENTS(psmModeOptions); - FormSelectorOptions selector(optionCount, psmMode, psmModeOptions); + const FormSelectorOptions selector(optionCount, psmMode, psmModeOptions); selector.addFormSelector(F("Power Save Mode"), F("psm"), P168_PSM_MODE); } diff --git a/src/_P169_AS3935_LightningDetector.ino b/src/_P169_AS3935_LightningDetector.ino index f5a3850ed0..1b15e9ee40 100644 --- a/src/_P169_AS3935_LightningDetector.ino +++ b/src/_P169_AS3935_LightningDetector.ino @@ -127,7 +127,7 @@ boolean Plugin_169(uint8_t function, struct EventStruct *event, String& string) AS3935MI::AS3935_MNL_9, AS3935MI::AS3935_MNL_16 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector( F("Lightning Threshold"), P169_LIGHTNING_THRESHOLD_LABEL, @@ -158,7 +158,7 @@ boolean Plugin_169(uint8_t function, struct EventStruct *event, String& string) 18 }; constexpr size_t optionCount = NR_ELEMENTS(optionValues); - FormSelectorOptions selector(optionCount, options, optionValues); + const FormSelectorOptions selector(optionCount, options, optionValues); selector.addFormSelector(F("AFE Gain Min"), P169_AFE_GAIN_LOW_LABEL, P169_AFE_GAIN_LOW); selector.addFormSelector(F("AFE Gain Max"), P169_AFE_GAIN_HIGH_LABEL, P169_AFE_GAIN_HIGH); addFormNote(F("Lower and upper limit for the Analog Frond-End auto gain to use.")); diff --git a/src/src/Controller_config/C018_config.cpp b/src/src/Controller_config/C018_config.cpp index 2da5f33068..77369d0849 100644 --- a/src/src/Controller_config/C018_config.cpp +++ b/src/src/Controller_config/C018_config.cpp @@ -93,7 +93,7 @@ void C018_ConfigStruct::webform_load(C018_data_struct *C018_data) { RN2xx3_datatypes::Freq_plan::TTN_US, RN2xx3_datatypes::Freq_plan::DEFAULT_EU }; - FormSelectorOptions selector( NR_ELEMENTS(options), options, values); + const FormSelectorOptions selector( NR_ELEMENTS(options), options, values); selector.addFormSelector(F("Frequency Plan"), F("frequencyplan"), frequencyplan); addFormNumericBox(F("RX2 Frequency"), F("rx2freq"), rx2_freq, 0); addUnit(F("Hz")); @@ -105,7 +105,7 @@ void C018_ConfigStruct::webform_load(C018_data_struct *C018_data) { RN2xx3_datatypes::TTN_stack_version::TTN_v2, RN2xx3_datatypes::TTN_stack_version::TTN_v3 }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, values); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, values); selector.addFormSelector(F("TTN Stack"), F("ttnstack"), stackVersion); } diff --git a/src/src/DataTypes/FormSelectorOptions.cpp b/src/src/DataTypes/FormSelectorOptions.cpp index 9e3566e729..8b157d1f15 100644 --- a/src/src/DataTypes/FormSelectorOptions.cpp +++ b/src/src/DataTypes/FormSelectorOptions.cpp @@ -4,48 +4,48 @@ #include "../WebServer/Markup_Forms.h" #include "../WebServer/HTML_wrappers.h" +FormSelectorOptions::FormSelectorOptions() + : classname(F("wide")), _onlySelectorHead(true) +{} + + FormSelectorOptions::FormSelectorOptions(int optionCount) - : _optionCount(optionCount) -{ - classname = F("wide"); -} + : classname(F("wide")), _optionCount(optionCount) +{} FormSelectorOptions::FormSelectorOptions( int optionCount, const int indices[], const String attr[]) : + classname(F("wide")), _optionCount(optionCount), _indices(indices), _attr_str(attr) -{ - classname = F("wide"); -} +{} FormSelectorOptions::FormSelectorOptions( int optionCount, const String options[], const int indices[], const String attr[]) : + classname(F("wide")), _optionCount(optionCount), _names_str(options), _indices(indices), _attr_str(attr) -{ - classname = F("wide"); -} +{} FormSelectorOptions::FormSelectorOptions( int optionCount, const __FlashStringHelper *options[], const int indices[], const String attr[]) : + classname(F("wide")), _optionCount(optionCount), _names_f(options), _indices(indices), _attr_str(attr) -{ - classname = F("wide"); -} +{} FormSelectorOptions::~FormSelectorOptions() {} @@ -81,6 +81,11 @@ int FormSelectorOptions::getIndexValue(int index) const return index; } +bool FormSelectorOptions::isDone(int index) const +{ + return index >= _optionCount; +} + void FormSelectorOptions::clearClassName() { classname = F(""); @@ -119,15 +124,14 @@ void FormSelectorOptions::addFormSelector( addSelector(id, selectedIndex); } - void FormSelectorOptions::addSelector(const __FlashStringHelper *id, - int selectedIndex) const + int selectedIndex) const { addSelector(String(id), selectedIndex); } void FormSelectorOptions::addSelector(const String& id, - int selectedIndex) const + int selectedIndex) const { // FIXME TD-er Change bool 'enabled' to disabled if (reloadonchange) @@ -145,11 +149,17 @@ void FormSelectorOptions::addSelector(const String& id, ); } - for (int i = 0; i < _optionCount; ++i) + if (_onlySelectorHead) { return; } + + for (int i = 0; !isDone(i); ++i) { const int index = getIndexValue(i); + String optionString = getOptionString(i); + if (index == default_index) { + optionString += F(" (default)"); + } addSelector_Item( - getOptionString(i), + optionString, index, selectedIndex == index, false, @@ -157,5 +167,11 @@ void FormSelectorOptions::addSelector(const String& id, if ((i & 0x07) == 0) { delay(0); } } + + addSelectorFoot(); +} + +void FormSelectorOptions::addSelectorFoot() const +{ addSelector_Foot(reloadonchange); } diff --git a/src/src/DataTypes/FormSelectorOptions.h b/src/src/DataTypes/FormSelectorOptions.h index a5b5fa15f6..c09791fe45 100644 --- a/src/src/DataTypes/FormSelectorOptions.h +++ b/src/src/DataTypes/FormSelectorOptions.h @@ -6,6 +6,8 @@ class FormSelectorOptions { public: + FormSelectorOptions(); + FormSelectorOptions(int optionCount); FormSelectorOptions(int optionCount, @@ -15,11 +17,11 @@ class FormSelectorOptions { FormSelectorOptions(int optionCount, const String options[], const int indices[] = nullptr, - const String attr[] = nullptr); + const String attr[] = nullptr); FormSelectorOptions(int optionCount, const __FlashStringHelper *options[], const int indices[] = nullptr, - const String attr[] = nullptr); + const String attr[] = nullptr); virtual ~FormSelectorOptions(); @@ -30,18 +32,20 @@ class FormSelectorOptions { virtual int getIndexValue(int index) const; - void clearClassName(); + virtual bool isDone(int index) const; - void addFormSelector(const __FlashStringHelper *label, - const __FlashStringHelper *id, - int selectedIndex) const; + void clearClassName(); - void addFormSelector(const String&label, + void addFormSelector(const __FlashStringHelper *label, + const __FlashStringHelper *id, + int selectedIndex) const; + + void addFormSelector(const String & label, const __FlashStringHelper *id, int selectedIndex) const; void addFormSelector(const __FlashStringHelper *label, - const String& id, + const String & id, int selectedIndex) const; void addFormSelector(const String& label, @@ -53,17 +57,22 @@ class FormSelectorOptions { int selectedIndex) const; void addSelector(const String& id, - int selectedIndex) const; + int selectedIndex) const; + void addSelectorFoot() const; + bool reloadonchange = false; - bool enabled = true; - const __FlashStringHelper * classname; + bool enabled = true; + const __FlashStringHelper *classname; #if FEATURE_TOOLTIPS String tooltip; #endif // if FEATURE_TOOLTIPS String onChangeCall; + // Allow to add "(default)" to some option. + int default_index = -1; + protected: const int _optionCount{}; @@ -71,6 +80,7 @@ class FormSelectorOptions { const String *_names_str{ nullptr }; const int *_indices{ nullptr }; const String *_attr_str{ nullptr }; + bool _onlySelectorHead = false; }; diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index d4a4748e5b..7503ca6eaf 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -219,7 +219,7 @@ void AdaGFXFormTextPrintMode(const __FlashStringHelper *id, }; */ - FormSelectorOptions selector(NR_ELEMENTS(textModes), textModes); + const FormSelectorOptions selector(NR_ELEMENTS(textModes), textModes); selector.addFormSelector(F("Text print Mode"), id, selectedIndex); } @@ -281,7 +281,7 @@ void AdaGFXFormRotation(const __FlashStringHelper *id, uint8_t selectedIndex) { const __FlashStringHelper *rotationOptions[] = { F("Normal"), F("+90°"), F("+180°"), F("+270°") }; // const int rotationOptionValues[] = { 0, 1, 2, 3 }; - FormSelectorOptions selector(NR_ELEMENTS(rotationOptions), rotationOptions); + const FormSelectorOptions selector(NR_ELEMENTS(rotationOptions), rotationOptions); selector.addFormSelector(F("Rotation"), id, selectedIndex); } @@ -418,7 +418,7 @@ void AdaGFXFormLineSpacing(const __FlashStringHelper *id, } // lineSpacingOptions[i] = i; } - FormSelectorOptions selector(16, lineSpacings); + const FormSelectorOptions selector(16, lineSpacings); selector.addFormSelector(F("Linespacing"), id, selectedIndex); addUnit(F("px")); } diff --git a/src/src/Helpers/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index 0048fe0254..437514f2df 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -889,7 +889,7 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { # endif // if TOUCH_FEATURE_EXTENDED_TOUCH }; const int optionValues3[] = { 0, 1, 3, 4, 5, 7 }; // Already used as a bitmap! - FormSelectorOptions selector(NR_ELEMENTS(optionValues3), options3, optionValues3); + const FormSelectorOptions selector(NR_ELEMENTS(optionValues3), options3, optionValues3); selector.addFormSelector(F("Events"), F("events"), choice3); addFormCheckBox(F("Draw buttons when started"), F("initobj"), bitRead(Touch_Settings.flags, TOUCH_FLAGS_INIT_OBJECTEVENT)); diff --git a/src/src/Helpers/OLed_helper.cpp b/src/src/Helpers/OLed_helper.cpp index 60e1a17cf2..b7ffe4dc34 100644 --- a/src/src/Helpers/OLed_helper.cpp +++ b/src/src/Helpers/OLed_helper.cpp @@ -11,7 +11,7 @@ void OLedFormController(const __FlashStringHelper *id, F("SH1106 (132x64 dot controller)") }; const int controllerValues[] = { 1, 2 }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(controllerOptions), controllerOptions, values == nullptr ? controllerValues : values); @@ -29,7 +29,7 @@ void OLedFormRotation(const __FlashStringHelper *id, const int rotationValues[] = { 1, 2 }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(rotationOptions), rotationOptions, rotationValues); selector.addFormSelector(F("Rotation"), id, selectedIndex); @@ -48,7 +48,7 @@ void OLedFormContrast(const __FlashStringHelper *id, OLED_CONTRAST_LOW, OLED_CONTRAST_MED, OLED_CONTRAST_HIGH }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(contrastOptions), contrastOptions, contrastValues); diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 8df4b141a3..e83c32c4dd 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -197,7 +197,7 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { const int choice = static_cast(ControllerSettings.TLStype()); - const __FlashStringHelper *options[] = { + const __FlashStringHelper *options[]{ toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), @@ -209,7 +209,7 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett */ toString(TLS_types::TLS_insecure) }; - const int indices[] = { + constexpr int indices[] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), @@ -308,7 +308,7 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett F("Ignore New"), F("Delete Oldest") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(displayName, internalName, ControllerSettings.DeleteOldest); break; } @@ -327,7 +327,7 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett F("Ignore Acknowledgement"), F("Check Acknowledgement") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addFormSelector(displayName, internalName, ControllerSettings.MustCheckReply); break; } diff --git a/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp b/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp index 3d882fcfa0..ccba8caa89 100644 --- a/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp +++ b/src/src/Helpers/_Internal_GPIO_pulseHelper.cpp @@ -47,7 +47,7 @@ void Internal_GPIO_pulseHelper::addGPIOtriggerMode(const __FlashStringHelper *la for (int i = 0; i < NR_TRIGGER_MODES; ++i) { options[i] = Internal_GPIO_pulseHelper::toString(static_cast(optionValues[i])); } - FormSelectorOptions selector( NR_TRIGGER_MODES, options, optionValues); + const FormSelectorOptions selector( NR_TRIGGER_MODES, options, optionValues); selector.addFormSelector(label, id, static_cast(currentSelection)); } diff --git a/src/src/Helpers/_Plugin_Helper_serial.cpp b/src/src/Helpers/_Plugin_Helper_serial.cpp index 5fb71b99fe..f3a495be43 100644 --- a/src/src/Helpers/_Plugin_Helper_serial.cpp +++ b/src/src/Helpers/_Plugin_Helper_serial.cpp @@ -163,7 +163,7 @@ void serialHelper_addI2CuartSelectors(int address, int channel) { SC16IS752_CHANNEL_B, }; */ - FormSelectorOptions selector(NR_ELEMENTS(chOptions), chOptions); + const FormSelectorOptions selector(NR_ELEMENTS(chOptions), chOptions); selector.addFormSelector(F("Channel"), F("i2cuart_ch"), channel); } } diff --git a/src/src/Helpers/_Plugin_Helper_webform.cpp b/src/src/Helpers/_Plugin_Helper_webform.cpp index 17c1be8e7a..0cc1475eef 100644 --- a/src/src/Helpers/_Plugin_Helper_webform.cpp +++ b/src/src/Helpers/_Plugin_Helper_webform.cpp @@ -52,7 +52,7 @@ void SwitchWebformLoad( SWITCH_DC_HIGH, SWITCH_DC_BOTH }; - FormSelectorOptions selector(NR_ELEMENTS(buttonDCValues), + const FormSelectorOptions selector(NR_ELEMENTS(buttonDCValues), buttonDC, buttonDCValues); selector.addFormSelector( @@ -85,7 +85,7 @@ void SwitchWebformLoad( SWITCH_LONGPRESS_HIGH, SWITCH_LONGPRESS_BOTH }; - FormSelectorOptions selector( + const FormSelectorOptions selector( NR_ELEMENTS(buttonLPValues), buttonLP, buttonLPValues); diff --git a/src/src/Helpers/_Plugin_SensorTypeHelper.cpp b/src/src/Helpers/_Plugin_SensorTypeHelper.cpp index 7f2c7b35e1..434abdc48e 100644 --- a/src/src/Helpers/_Plugin_SensorTypeHelper.cpp +++ b/src/src/Helpers/_Plugin_SensorTypeHelper.cpp @@ -142,7 +142,7 @@ void sensorTypeHelper_loadOutputSelector( if (pconfigIndex < 0 || pconfigIndex >= PLUGIN_CONFIGVAR_MAX) { return; } - FormSelectorOptions selector( + const FormSelectorOptions selector( optionCount, options, indices); @@ -159,7 +159,7 @@ void sensorTypeHelper_loadOutputSelector( if (pconfigIndex < 0 || pconfigIndex >= PLUGIN_CONFIGVAR_MAX) { return; } - FormSelectorOptions selector( + const FormSelectorOptions selector( optionCount, options, indices); diff --git a/src/src/PluginStructs/P002_data_struct.cpp b/src/src/PluginStructs/P002_data_struct.cpp index 5d94fb8cf6..4c47c6d61f 100644 --- a/src/src/PluginStructs/P002_data_struct.cpp +++ b/src/src/PluginStructs/P002_data_struct.cpp @@ -154,7 +154,7 @@ void P002_data_struct::webformLoad(struct EventStruct *event) P002_ADC_0db }; constexpr int nrOptions = NR_ELEMENTS(outputOptionValues); - FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); + const FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); selector.addFormSelector(F("Attenuation"), F("attn"), P002_ATTENUATION); } @@ -176,7 +176,7 @@ void P002_data_struct::webformLoad(struct EventStruct *event) # endif // ifndef LIMIT_BUILD_SIZE }; constexpr int nrOptions = NR_ELEMENTS(outputOptionValues); - FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); + const FormSelectorOptions selector(nrOptions, outputOptions, outputOptionValues); selector.addFormSelector(F("Oversampling"), F("oversampling"), P002_OVERSAMPLING); } diff --git a/src/src/PluginStructs/P025_data_struct.cpp b/src/src/PluginStructs/P025_data_struct.cpp index 62df92ed48..402efaa4b1 100644 --- a/src/src/PluginStructs/P025_data_struct.cpp +++ b/src/src/PluginStructs/P025_data_struct.cpp @@ -326,7 +326,7 @@ bool P025_data_struct::webformLoad(struct EventStruct *event) }; constexpr size_t ADS1115_PGA_OPTIONS = NR_ELEMENTS(pgaOptions); - FormSelectorOptions selector(ADS1115_PGA_OPTIONS, pgaOptions); + const FormSelectorOptions selector(ADS1115_PGA_OPTIONS, pgaOptions); selector.addFormSelector(F("Gain"), F("gain"), P025_GAIN); addFormNote(F("Do not apply more than VDD + 0.3 V to the analog inputs of the device.")); } @@ -342,7 +342,7 @@ bool P025_data_struct::webformLoad(struct EventStruct *event) F("860 / 3300"), }; constexpr size_t NR_OPTIONS = NR_ELEMENTS(P025_SPSOptions); - FormSelectorOptions selector(NR_OPTIONS, P025_SPSOptions); + const FormSelectorOptions selector(NR_OPTIONS, P025_SPSOptions); selector.addFormSelector(F("Sample Rate"), F("sps"), p025_variousBits.getSampleRate()); addUnit(F("SPS")); addFormNote(F("Lower values for ADS1115, higher values for ADS1015")); diff --git a/src/src/PluginStructs/P037_data_struct.cpp b/src/src/PluginStructs/P037_data_struct.cpp index fa72224b8c..79c00b1251 100644 --- a/src/src/PluginStructs/P037_data_struct.cpp +++ b/src/src/PluginStructs/P037_data_struct.cpp @@ -291,7 +291,7 @@ bool P037_data_struct::webform_load( html_TD(); filterIndex = filters.indexOf(parseString(valueArray[filterOffset], 2, P037_VALUE_SEPARATOR)); - FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); + const FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); @@ -329,7 +329,7 @@ bool P037_data_struct::webform_load( } { html_TD(); - FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); + const FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), EMPTY_STRING, @@ -404,7 +404,7 @@ bool P037_data_struct::webform_load( html_TD(); operandIndex = operands.indexOf(parseString(valueArray[mappingOffset], 2, P037_VALUE_SEPARATOR)); - FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); + const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 2), @@ -439,7 +439,7 @@ bool P037_data_struct::webform_load( } { html_TD(); - FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); + const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 2), EMPTY_STRING, diff --git a/src/src/PluginStructs/P047_data_struct.cpp b/src/src/PluginStructs/P047_data_struct.cpp index 57db31757f..6ffe0d16dc 100644 --- a/src/src/PluginStructs/P047_data_struct.cpp +++ b/src/src/PluginStructs/P047_data_struct.cpp @@ -17,7 +17,7 @@ P047_data_struct::P047_data_struct(uint8_t address, const __FlashStringHelper* toString(P047_SensorModels sensor) { switch (sensor) { - case P047_SensorModels::CatnipMiceuz: return F("Catnip electronics/miceuz (default)"); + case P047_SensorModels::CatnipMiceuz: return F("Catnip electronics/miceuz"); case P047_SensorModels::BeFlE: return F("BeFlE v2.2"); # if P047_FEATURE_ADAFRUIT case P047_SensorModels::Adafruit: return F("Adafruit (4026)"); diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 23dd939065..52daaa878c 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -115,7 +115,7 @@ void P073_display_output_selector(const __FlashStringHelper *id, int16_t value) P073_DISP_CLOCK12, P073_DISP_DATE, }; - FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + const FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); selector.addFormSelector(F("Display Output"), id, value); } @@ -127,7 +127,7 @@ void P073_font_selector(const __FlashStringHelper *id, int16_t value) { F("Siekoo with uppercase 'CHNORUX'"), F("dSEG7"), }; - 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.")); } diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 2a1a8f3028..b190e42869 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -1890,7 +1890,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { static_cast(MD_MAX72XX::moduleType_t::DR0CR1RR1_HW), static_cast(MD_MAX72XX::moduleType_t::DR1CR0RR1_HW) }; - FormSelectorOptions selector( + const FormSelectorOptions selector( P104_hardwareTypeCount, hardwareTypes, hardwareOptions); @@ -1928,7 +1928,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { P104_DATE_FORMAT_US, P104_DATE_FORMAT_JP }; - FormSelectorOptions selector(3, dateFormats, dateFormatOptions); + const FormSelectorOptions selector(3, dateFormats, dateFormatOptions); selector.addFormSelector(F("Date format"), F("datefmt"), get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_FORMAT)); } @@ -1945,7 +1945,7 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { P104_DATE_SEPARATOR_DASH, P104_DATE_SEPARATOR_DOT }; - FormSelectorOptions selector(4, dateSeparators, dateSeparatorOptions); + const FormSelectorOptions selector(4, dateSeparators, dateSeparatorOptions); selector.addFormSelector(F("Date separator"), F("datesep"), get4BitFromUL(P104_CONFIG_DATETIME, P104_CONFIG_DATETIME_SEP_CHAR)); diff --git a/src/src/PluginStructs/P120_data_struct.cpp b/src/src/PluginStructs/P120_data_struct.cpp index 5f965f01f3..ae63cc6848 100644 --- a/src/src/PluginStructs/P120_data_struct.cpp +++ b/src/src/PluginStructs/P120_data_struct.cpp @@ -519,7 +519,7 @@ bool P120_data_struct::plugin_webform_loadOutputSelector(struct EventStruct *eve const int values[] = { 0, 1 }; const int choice = bitRead(P120_CONFIG_FLAGS1, P120_FLAGS1_ANGLE_IN_RAD); - FormSelectorOptions selector(2, options, values); + const FormSelectorOptions selector(2, options, values); selector.addFormSelector(F("Angle Units"), F("angle_rad"), choice); } return true; @@ -529,14 +529,16 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { // Range { const __FlashStringHelper *rangeOptions[] = { - F("2g"), - F("4g"), - F("8g"), - F("16g (default)") }; + F("2"), + F("4"), + F("8"), + F("16") }; int rangeValues[] = { P120_RANGE_2G, P120_RANGE_4G, P120_RANGE_8G, P120_RANGE_16G }; FormSelectorOptions selector(4, rangeOptions, rangeValues); + selector.default_index = P120_RANGE_16G; selector.addFormSelector(F("Range"), F("range"), get2BitFromUL(P120_CONFIG_FLAGS1, P120_FLAGS1_RANGE)); + addUnit('g'); } // Axis selection @@ -629,7 +631,7 @@ bool P120_data_struct::plugin_webform_load(struct EventStruct *event) { F("10"), F("50") }; int frequencyValues[] = { P120_FREQUENCY_10, P120_FREQUENCY_50 }; - 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.")); diff --git a/src/src/PluginStructs/P139_data_struct.cpp b/src/src/PluginStructs/P139_data_struct.cpp index 7d67fa1216..88ec7b90fe 100644 --- a/src/src/PluginStructs/P139_data_struct.cpp +++ b/src/src/PluginStructs/P139_data_struct.cpp @@ -240,7 +240,7 @@ void P139_data_struct::webform_load(struct EventStruct *event) { // Don't include Disabled or Protected here, not user-selectable constexpr int bootStatesCount = NR_ELEMENTS(bootStateValues); - FormSelectorOptions selector( + const FormSelectorOptions selector( bootStatesCount, bootStates, bootStateValues); diff --git a/src/src/PluginStructs/P139_data_struct_formselectors.h b/src/src/PluginStructs/P139_data_struct_formselectors.h index dac697161a..5da3e4a4b6 100644 --- a/src/src/PluginStructs/P139_data_struct_formselectors.h +++ b/src/src/PluginStructs/P139_data_struct_formselectors.h @@ -13,7 +13,6 @@ class AXP2101_ChargeLED_FormSelector : public FormSelectorOptions { public: AXP2101_ChargeLED_FormSelector(AXP2101_chargeled_d selected); - virtual ~AXP2101_ChargeLED_FormSelector() {} static AXP2101_chargeled_d get(); @@ -30,7 +29,6 @@ class AXP2101_PreChargeCurrentLimit_FormSelector : public FormSelectorOptions { public: AXP2101_PreChargeCurrentLimit_FormSelector(int selected); - virtual ~AXP2101_PreChargeCurrentLimit_FormSelector() {} static int get(); @@ -48,7 +46,6 @@ class AXP2101_ConstChargeCurrentLimit_FormSelector : public FormSelectorOptions public: AXP2101_ConstChargeCurrentLimit_FormSelector(int selected); - virtual ~AXP2101_ConstChargeCurrentLimit_FormSelector() {} static int get(); @@ -65,7 +62,6 @@ class AXP2101_TerminationChargeCurrentLimit_FormSelector : public FormSelectorOp public: AXP2101_TerminationChargeCurrentLimit_FormSelector(int selected); - virtual ~AXP2101_TerminationChargeCurrentLimit_FormSelector() {} static int get(); @@ -81,7 +77,6 @@ class AXP2101_CV_charger_voltage_FormSelector : public FormSelectorOptions { public: AXP2101_CV_charger_voltage_FormSelector(AXP2101_CV_charger_voltage_e selected); - virtual ~AXP2101_CV_charger_voltage_FormSelector() {} static AXP2101_CV_charger_voltage_e get(); @@ -97,7 +92,6 @@ class AXP2101_Linear_Charger_Vsys_dpm_FormSelector : public FormSelectorOptions public: AXP2101_Linear_Charger_Vsys_dpm_FormSelector(AXP2101_Linear_Charger_Vsys_dpm_e selected); - virtual ~AXP2101_Linear_Charger_Vsys_dpm_FormSelector() {} static AXP2101_Linear_Charger_Vsys_dpm_e get(); @@ -112,7 +106,6 @@ class AXP2101_Vin_DPM_FormSelector : public FormSelectorOptions { public: AXP2101_Vin_DPM_FormSelector(AXP2101_VINDPM_e selected); - virtual ~AXP2101_Vin_DPM_FormSelector() {} static AXP2101_VINDPM_e get(); @@ -127,7 +120,6 @@ class AXP2101_InputCurrentLimit_FormSelector : public FormSelectorOptions { public: AXP2101_InputCurrentLimit_FormSelector(AXP2101_InputCurrentLimit_e selected); - virtual ~AXP2101_InputCurrentLimit_FormSelector() {} static AXP2101_InputCurrentLimit_e get(); diff --git a/src/src/PluginStructs/P148_data_struct.cpp b/src/src/PluginStructs/P148_data_struct.cpp index 4828ba9ddf..378c5c11d8 100644 --- a/src/src/PluginStructs/P148_data_struct.cpp +++ b/src/src/PluginStructs/P148_data_struct.cpp @@ -158,7 +158,7 @@ void P148_data_struct::MonitorTaskValue_t::webformLoad(int index) const { static_cast(P148_data_struct::Tm1621UnitOfMeasure::kWh_Watt) }; constexpr size_t nrElements = sizeof(optionValues) / sizeof(optionValues[0]); - FormSelectorOptions selector(nrElements, options, optionValues); + const FormSelectorOptions selector(nrElements, options, optionValues); selector.addFormSelector( F("Unit Symbols"), concat(F("punit"), index), static_cast(unit)); @@ -177,7 +177,7 @@ void P148_data_struct::MonitorTaskValue_t::webformLoad(int index) const { }; constexpr size_t nrElements = sizeof(optionValues) / sizeof(optionValues[0]); - FormSelectorOptions selector(nrElements, options, optionValues); + const FormSelectorOptions selector(nrElements, options, optionValues); selector.addFormSelector( F("Unit Symbols"), concat(F("punit"), index), static_cast(unit)); diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index e310ccace0..a7ceb8e1f7 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -201,7 +201,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; const int stripOptionValues[] = { P165_STRIP_TYPE_RGB, P165_STRIP_TYPE_RGBW }; - FormSelectorOptions selector(NR_ELEMENTS(stripOptionValues), stripOptions, stripOptionValues); + const FormSelectorOptions selector(NR_ELEMENTS(stripOptionValues), stripOptions, stripOptionValues); selector.addFormSelector(F("Strip Type"), F("stripe"), P165_CONFIG_STRIP_TYPE); } @@ -235,7 +235,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { P165_DISP_CLOCK12, P165_DISP_DATE, }; - FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); + const FormSelectorOptions selector(NR_ELEMENTS(disploutOptions), displout, disploutOptions); selector.addFormSelector(F("Display Output"), F("dspout"), P165_CONFIG_OUTPUTTYPE); # endif // if P165_FEATURE_P073 diff --git a/src/src/WebServer/AccessControl.cpp b/src/src/WebServer/AccessControl.cpp index 56e598ac95..56c6eda03f 100644 --- a/src/src/WebServer/AccessControl.cpp +++ b/src/src/WebServer/AccessControl.cpp @@ -146,6 +146,6 @@ void addIPaccessControlSelect(const String& name, int choice) { const __FlashStringHelper * options[] = { F("Allow All"), F("Allow Local Subnet"), F("Allow IP range") }; - FormSelectorOptions selector(NR_ELEMENTS(options), options); + const FormSelectorOptions selector(NR_ELEMENTS(options), options); selector.addSelector(name, choice); } diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 9ee39350a0..5a46b09fd9 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -335,7 +335,7 @@ void handle_advanced() { }; //const int cssModeOptions[] = { 0, 1, 2}; constexpr int nrCssModeOptions = NR_ELEMENTS(cssModeNames); - FormSelectorOptions selector( + const FormSelectorOptions selector( nrCssModeOptions, cssModeNames/*, cssModeOptions*/); @@ -421,7 +421,7 @@ void addFormDstSelect(bool isStart, uint16_t choice) { isStart ? F("Start") : F("End"), F(" (week, dow, month)"))); - FormSelectorOptions selector(NR_ELEMENTS(weekValues), week, weekValues); + const FormSelectorOptions selector(NR_ELEMENTS(weekValues), week, weekValues); selector.addSelector( isStart ? F("dststartweek") : F("dstendweek"), rule.week); @@ -431,7 +431,7 @@ void addFormDstSelect(bool isStart, uint16_t choice) { const __FlashStringHelper * dow[] = { F("Sun"), F("Mon"), F("Tue"), F("Wed"), F("Thu"), F("Fri"), F("Sat") }; constexpr int dowValues[] = { 1, 2, 3, 4, 5, 6, 7 }; - FormSelectorOptions selector(NR_ELEMENTS(dowValues), dow, dowValues); + const FormSelectorOptions selector(NR_ELEMENTS(dowValues), dow, dowValues); selector.addSelector( isStart ? F("dststartdow") : F("dstenddow"), rule.dow); @@ -442,7 +442,7 @@ void addFormDstSelect(bool isStart, uint16_t choice) { "Dec") }; constexpr int monthValues[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; - FormSelectorOptions selector(NR_ELEMENTS(monthValues), month, monthValues); + const FormSelectorOptions selector(NR_ELEMENTS(monthValues), month, monthValues); selector.addSelector( isStart ? F("dststartmonth") : F("dstendmonth"), rule.month); @@ -468,7 +468,7 @@ void addFormExtTimeSourceSelect(const __FlashStringHelper * label, const __Flash static_cast(ExtTimeSource_e::PCF8563) }; - FormSelectorOptions selector(NR_ELEMENTS(optionValues), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(optionValues), options, optionValues); selector.addSelector(id, static_cast(choice)); } @@ -488,7 +488,7 @@ void addFormLogLevelSelect(LabelType::Enum label, int choice) for (int i = 0; i < LOG_LEVEL_NRELEMENTS; ++i) { options[i + 1] = getLogLevelDisplayStringFromIndex(i, optionValues[i + 1]); } - FormSelectorOptions selector(LOG_LEVEL_NRELEMENTS + 1, options, optionValues); + const FormSelectorOptions selector(LOG_LEVEL_NRELEMENTS + 1, options, optionValues); selector.addSelector(getInternalLabel(label), choice); } @@ -501,7 +501,7 @@ void addFormLogFacilitySelect(const __FlashStringHelper * label, const __FlashSt F("Local2"), F("Local3"), F("Local4"), F("Local5"), F("Local6"), F("Local7") }; const int optionValues[] = { 0, 1, 3, 5, 16, 17, 18, 19, 20, 21, 22, 23 }; - FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); + const FormSelectorOptions selector(NR_ELEMENTS(options), options, optionValues); selector.addSelector(id, choice); } diff --git a/src/src/WebServer/DevicesPage.cpp b/src/src/WebServer/DevicesPage.cpp index a357bd01cc..7dcc202e43 100644 --- a/src/src/WebServer/DevicesPage.cpp +++ b/src/src/WebServer/DevicesPage.cpp @@ -1281,7 +1281,7 @@ void devicePage_show_I2C_config(taskIndex_t taskIndex, deviceIndex_t DeviceIndex } if (taskDeviceI2CMuxPort >= static_cast(mux_max)) { taskDeviceI2CMuxPort = -1; } // Reset if out of range - FormSelectorOptions selector( + const FormSelectorOptions selector( mux_max + 1, i2c_mux_portoptions, i2c_mux_portchoices); @@ -1554,7 +1554,7 @@ void devicePage_show_task_values(taskIndex_t taskIndex, deviceIndex_t DeviceInde selected += 4; } - FormSelectorOptions selector(NR_ELEMENTS(chartAxis), chartAxis); + const FormSelectorOptions selector(NR_ELEMENTS(chartAxis), chartAxis); selector.addSelector( getPluginCustomArgName(F("TDSA"), varNr), selected); diff --git a/src/src/WebServer/HardwarePage.cpp b/src/src/WebServer/HardwarePage.cpp index a870bebc01..093774d096 100644 --- a/src/src/WebServer/HardwarePage.cpp +++ b/src/src/WebServer/HardwarePage.cpp @@ -157,7 +157,7 @@ void handle_hardware() { I2C_MULTIPLEXER_TCA9543A, I2C_MULTIPLEXER_PCA9540 }; - FormSelectorOptions selector(NR_ELEMENTS(i2c_muxtype_choices), + const FormSelectorOptions selector(NR_ELEMENTS(i2c_muxtype_choices), i2c_muxtype_options, i2c_muxtype_choices); selector.addFormSelector(F("I2C Multiplexer type"), F("pi2cmuxtype"), Settings.I2C_Multiplexer_Type); } @@ -180,7 +180,7 @@ void handle_hardware() { } i2c_mux_choices[mux_opt] = 0x70 + x; } - FormSelectorOptions selector(mux_opt + 1, i2c_mux_options, i2c_mux_choices); + const FormSelectorOptions selector(mux_opt + 1, i2c_mux_options, i2c_mux_choices); selector.addFormSelector(F("I2C Multiplexer address"), F("pi2cmuxaddr"), Settings.I2C_Multiplexer_Addr); } addFormPinSelect(PinSelectPurpose::Generic_output, formatGpioName_output_optional(F("Reset")), F("pi2cmuxreset"), Settings.I2C_Multiplexer_ResetPin); @@ -244,7 +244,7 @@ void handle_hardware() { toString(NetworkMedium_t::WIFI), toString(NetworkMedium_t::Ethernet) }; - FormSelectorOptions selector(2, ethWifiOptions); + const FormSelectorOptions selector(2, ethWifiOptions); selector.addSelector(F("ethwifi"), static_cast(Settings.NetworkMedium)); } addFormNote(F("Change Switch between WiFi and Ethernet requires reboot to activate")); @@ -315,7 +315,7 @@ void handle_hardware() { ? static_cast(Settings.ETH_Phy_Type) : static_cast(EthPhyType_t::notSet); - FormSelectorOptions selector( + const FormSelectorOptions selector( nrItems, ethPhyTypes, ethPhyTypes_index); @@ -363,7 +363,7 @@ void handle_hardware() { toString(EthClockMode_t::Int_50MHz_GPIO_16), toString(EthClockMode_t::Int_50MHz_GPIO_17_inv) }; - FormSelectorOptions selector(NR_ELEMENTS(ethClockOptions), ethClockOptions); + const FormSelectorOptions selector(NR_ELEMENTS(ethClockOptions), ethClockOptions); selector.addSelector(F("ethclock"), static_cast(Settings.ETH_Clock_Mode)); } #endif diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index 74360efa80..98af3a2ceb 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -159,8 +159,9 @@ void addFormNumericBox(const String& label, const String& id, int value, int min ) { addRowLabel_tr_id(label, id); - addNumericBox(id, value, min, max, F("widenumber") + addNumericBox(id, value, min, max #if FEATURE_TOOLTIPS + , F("widenumber") , tooltip #endif // if FEATURE_TOOLTIPS , disabled @@ -424,7 +425,7 @@ void addFormSeparatorCharInput(const __FlashStringHelper *rowLabel, charList[i + 1] = charset[i]; charOpts[i + 1] = static_cast(charset[i]); } - FormSelectorOptions selector(len, charList, charOpts); + const FormSelectorOptions selector(len, charList, charOpts); selector.addFormSelector(rowLabel, id, value); if (!String(additionalText).isEmpty()) { @@ -497,7 +498,7 @@ void addFormSelectorI2C(const String& id, String option = formatToHex_decimal(addresses[x]); if (((x == 0) && (defaultAddress == 0)) || (defaultAddress == addresses[x])) { - option += F(" - (default)"); + option += F(" (default)"); } addSelector_Item(option, addresses[x], addresses[x] == selectedIndex); } From 0cb87d0392b672c6cef407aaced96e2978e18822 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Jan 2025 14:32:21 +0100 Subject: [PATCH 49/50] [Build] Reduce bin size by reducing nr. arguments in addTextBox --- src/_P036_FrameOLED.ino | 3 -- src/_P042_Candle.ino | 2 +- src/_P043_ClkOutput.ino | 8 ++-- src/_P087_SerialProxy.ino | 2 +- src/_P099_XPT2046Touch.ino | 2 +- src/_P131_NeoPixelMatrix.ino | 3 -- src/_P143_I2C_Rotary.ino | 2 +- src/src/Helpers/ESPEasy_TouchHandler.cpp | 2 +- src/src/Helpers/_CPlugin_Helper_webform.cpp | 8 ---- src/src/PluginStructs/P002_data_struct.cpp | 7 +-- src/src/PluginStructs/P037_data_struct.cpp | 53 ++++++++++++--------- src/src/PluginStructs/P104_data_struct.cpp | 3 -- src/src/WebServer/Markup.cpp | 24 ++++++++++ src/src/WebServer/Markup.h | 11 +++++ 14 files changed, 77 insertions(+), 53 deletions(-) diff --git a/src/_P036_FrameOLED.ino b/src/_P036_FrameOLED.ino index 3e2e969c1f..6070a33377 100644 --- a/src/_P036_FrameOLED.ino +++ b/src/_P036_FrameOLED.ino @@ -552,9 +552,6 @@ boolean Plugin_036(uint8_t function, struct EventStruct *event, String& string) addTextBox(getPluginCustomArgName(varNr), P036_lines.DisplayLinesV1[varNr].Content, P36_NcharsV1 - 1, - false, // readonly, - false, // required, - EMPTY_STRING, // pattern, F("xwide") // class name ); { diff --git a/src/_P042_Candle.ino b/src/_P042_Candle.ino index 7a161f2713..ae44431078 100644 --- a/src/_P042_Candle.ino +++ b/src/_P042_Candle.ino @@ -155,7 +155,7 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) } // Candle Type Selection - const FormSelectorOptions selector(P042_FLAME_OPTIONS, options, nullptr); + const FormSelectorOptions selector(P042_FLAME_OPTIONS, options); selector.addFormSelector(F("Flame Type"), P042_WEBVAR_CANDLETYPE, P042_CONFIG_CANDLETYPE); } diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index 816fb252d3..bff78103af 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -166,12 +166,12 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) thisDay); addHtml(','); addTextBox(concat(F("clock"), x), - parseString(timeStr, 2), 32 - , false, false, EMPTY_STRING, F("") + parseString(timeStr, 2), 32, + F(""), # if FEATURE_TOOLTIPS - , EMPTY_STRING + EMPTY_STRING, # endif // if FEATURE_TOOLTIPS - , F("timepatternlist")); + F("timepatternlist")); # else // ifndef LIMIT_BUILD_SIZE addFormTextBox(concat(F("Day,Time "), x + 1), concat(F("clock"), x), diff --git a/src/_P087_SerialProxy.ino b/src/_P087_SerialProxy.ino index 658bdd47f1..5780c5b167 100644 --- a/src/_P087_SerialProxy.ino +++ b/src/_P087_SerialProxy.ino @@ -395,7 +395,7 @@ void P087_html_show_matchForms(struct EventStruct *event) { case 2: { // Compare with - addTextBox(id, filter, 32, false, false, EMPTY_STRING, F("")); + addTextBox(id, filter, 32, F("")); break; } } diff --git a/src/_P099_XPT2046Touch.ino b/src/_P099_XPT2046Touch.ino index 272c22417d..ed543b5003 100644 --- a/src/_P099_XPT2046Touch.ino +++ b/src/_P099_XPT2046Touch.ino @@ -243,7 +243,7 @@ boolean Plugin_099(uint8_t function, struct EventStruct *event, String& string) addTextBox(getPluginCustomArgName(objectNr), String(P099_data->StoredSettings.TouchObjects[objectNr].objectname), P099_MaxObjectNameLength - 1, - false, false, EMPTY_STRING, F("")); + F("")); html_TD(); addNumericBox(getPluginCustomArgName(objectNr + 100), P099_data->StoredSettings.TouchObjects[objectNr].top_left.x, 0, 65535); html_TD(); diff --git a/src/_P131_NeoPixelMatrix.ino b/src/_P131_NeoPixelMatrix.ino index 8810888934..4104f44915 100644 --- a/src/_P131_NeoPixelMatrix.ino +++ b/src/_P131_NeoPixelMatrix.ino @@ -222,9 +222,6 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) addTextBox(getPluginCustomArgName(varNr), parseStringKeepCaseNoTrim(strings[varNr], 1), P131_Nchars, - false, - false, - EMPTY_STRING, F("")); String opts = parseString(strings[varNr], 2); diff --git a/src/_P143_I2C_Rotary.ino b/src/_P143_I2C_Rotary.ino index 3ebf0d7fd8..6220281ccd 100644 --- a/src/_P143_I2C_Rotary.ino +++ b/src/_P143_I2C_Rotary.ino @@ -335,7 +335,7 @@ boolean Plugin_143(uint8_t function, struct EventStruct *event, String& string) addHtml('#'); addHtmlInt(varNr + 1); html_TD(); - addTextBox(getPluginCustomArgName(varNr), strings[varNr], P143_STRING_LEN, false, false, EMPTY_STRING, F("xwide")); + addTextBox(getPluginCustomArgName(varNr), strings[varNr], P143_STRING_LEN, F("xwide")); } html_end_table(); } diff --git a/src/src/Helpers/ESPEasy_TouchHandler.cpp b/src/src/Helpers/ESPEasy_TouchHandler.cpp index 437514f2df..fdc98a2fea 100644 --- a/src/src/Helpers/ESPEasy_TouchHandler.cpp +++ b/src/src/Helpers/ESPEasy_TouchHandler.cpp @@ -1214,7 +1214,7 @@ bool ESPEasy_TouchHandler::plugin_webform_load(struct EventStruct *event) { addTextBox(getPluginCustomArgName(objectNr + 100), TouchObjects[objectNr].objectName, TOUCH_MaxObjectNameLength, - false, false, EMPTY_STRING, F("wide")); + F("wide")); html_TD(); // top-x addNumericBox(getPluginCustomArgName(objectNr + 200), TouchObjects[objectNr].top_left.x, 0, 65535 diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index e83c32c4dd..f9a193ddd7 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -430,18 +430,14 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet #if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - # if FEATURE_MQTT_TLS const int current = static_cast(ControllerSettings.TLStype()); const TLS_types tls_type = static_cast(getFormItemInt(internalName, current)); ControllerSettings.TLStype(tls_type); - # endif // if FEATURE_MQTT_TLS break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { - # if FEATURE_MQTT_TLS - if (isFormItemChecked(internalName)) { String fingerprint; @@ -453,7 +449,6 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT), fingerprint); } } - # endif // if FEATURE_MQTT_TLS break; } @@ -461,8 +456,6 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - # if FEATURE_MQTT_TLS - if (isFormItemChecked(internalName)) { String cacert; @@ -470,7 +463,6 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT), cacert); } } - # endif // if FEATURE_MQTT_TLS break; } #endif // if FEATURE_MQTT_TLS diff --git a/src/src/PluginStructs/P002_data_struct.cpp b/src/src/PluginStructs/P002_data_struct.cpp index 4c47c6d61f..760cf37ede 100644 --- a/src/src/PluginStructs/P002_data_struct.cpp +++ b/src/src/PluginStructs/P002_data_struct.cpp @@ -101,7 +101,7 @@ void P002_data_struct::webformLoad_2p_calibPoint( float value) const { addRowLabel_tr_id(label, id_point); - addTextBox(id_point, String(point), 10, false, false, EMPTY_STRING, F("number")); + addTextBox(id_point, String(point), 10, F("number")); # ifdef ESP32 @@ -113,7 +113,7 @@ void P002_data_struct::webformLoad_2p_calibPoint( html_add_estimate_symbol(); const unsigned int display_nrDecimals = _nrDecimals > 3 ? _nrDecimals : 3; - addTextBox(id_value, toString(value, display_nrDecimals), 10, false, false, EMPTY_STRING, F("number")); + addTextBox(id_value, toString(value, display_nrDecimals), 10, F("number")); } void P002_data_struct::webformLoad(struct EventStruct *event) @@ -324,9 +324,6 @@ void P002_data_struct::webformLoad(struct EventStruct *event) _nrDecimals, true) : EMPTY_STRING, 0, - false, - false, - EMPTY_STRING, F("query-input widenumber")); ++line_nr; diff --git a/src/src/PluginStructs/P037_data_struct.cpp b/src/src/PluginStructs/P037_data_struct.cpp index 79c00b1251..cbc56046b8 100644 --- a/src/src/PluginStructs/P037_data_struct.cpp +++ b/src/src/PluginStructs/P037_data_struct.cpp @@ -214,12 +214,12 @@ bool P037_data_struct::webform_load( addTextBox(concat(F("template"), varNr + 1), mqttTopics[varNr], 40, - false, false, EMPTY_STRING, F("xwide")); + F("xwide")); html_TD(F("padding-right: 8px")); addTextBox(concat(F("attribute"), varNr + 1), jsonAttributes[varNr], 20, - false, false, EMPTY_STRING, F("xwide")); + F("xwide")); html_TD(); } else # endif // ifdef P037_JSON_SUPPORT @@ -267,6 +267,9 @@ bool P037_data_struct::webform_load( # endif // if P037_FILTER_COUNT >= 3 }; + const FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); + + String filters = P037_FILTER_LIST; // Anticipate more filters int8_t filterIndex; @@ -285,18 +288,18 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 0), parseStringKeepCase(valueArray[filterOffset], 1, P037_VALUE_SEPARATOR), - 32, false, false, EMPTY_STRING, F("xwide")); + 32, + F("xwide")); } { html_TD(); filterIndex = filters.indexOf(parseString(valueArray[filterOffset], 2, P037_VALUE_SEPARATOR)); - - const FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), parseStringKeepCase(valueArray[filterOffset], 3, P037_VALUE_SEPARATOR), - 32, false, false, EMPTY_STRING, F("")); + 32, + F("")); addUnit(F("Range/List: separate values with ; ")); html_TD(); } @@ -317,7 +320,7 @@ bool P037_data_struct::webform_load( # ifndef P037_FILTER_PER_TOPIC filterIndex = 0; uint8_t extraFilters = 0; - + while (extraFilters < P037_EXTRA_VALUES && idx < P037_MAX_FILTERS * 3) { { html_TR_TD(); @@ -325,15 +328,16 @@ bool P037_data_struct::webform_load( addHtmlInt(filterNr); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 0), EMPTY_STRING, - 32, false, false, EMPTY_STRING, F("xwide")); + 32, + F("xwide")); } { html_TD(); - const FormSelectorOptions selector(P037_FILTER_COUNT, filterOptions, filterIndices); selector.addSelector(getPluginCustomArgName(idx + 100 + 1), filterIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 100 + 2), EMPTY_STRING, - 32, false, false, EMPTY_STRING, EMPTY_STRING); + 32, + F("")); addUnit(F("Range/List: separate values with ; ")); html_TD(); } @@ -383,6 +387,8 @@ bool P037_data_struct::webform_load( F("map"), // map name to int F("percentage") }; // map attribute value to percentage of provided value const int operandIndices[] = { 0, 1 }; + const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); + String operands = P037_OPERAND_LIST; // Anticipate more operations int8_t operandIndex; @@ -398,18 +404,19 @@ bool P037_data_struct::webform_load( html_TD(); addTextBox(getPluginCustomArgName(idx + 0), parseStringKeepCase(valueArray[mappingOffset], 1, P037_VALUE_SEPARATOR), - 32, false, false, EMPTY_STRING, F("")); + 32, + F("")); } { html_TD(); operandIndex = operands.indexOf(parseString(valueArray[mappingOffset], 2, P037_VALUE_SEPARATOR)); - const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); addTextBox(getPluginCustomArgName(idx + 2), parseStringKeepCase(valueArray[mappingOffset], 3, P037_VALUE_SEPARATOR), - 32, false, false, EMPTY_STRING, F("")); + 32, + F("")); html_TD(); } mapNr++; @@ -434,16 +441,19 @@ bool P037_data_struct::webform_load( addHtml(F(" ")); addHtmlInt(mapNr); html_TD(); - addTextBox(getPluginCustomArgName(idx + 0), EMPTY_STRING, - 32, false, false, EMPTY_STRING, F("")); + addTextBox(getPluginCustomArgName(idx + 0), + EMPTY_STRING, + 32, + F("")); } { html_TD(); - const FormSelectorOptions selector(P037_OPERAND_COUNT, operandOptions, operandIndices); selector.addSelector(getPluginCustomArgName(idx + 1), operandIndex); html_TD(); - addTextBox(getPluginCustomArgName(idx + 2), EMPTY_STRING, - 32, false, false, EMPTY_STRING, F("")); + addTextBox(getPluginCustomArgName(idx + 2), + EMPTY_STRING, + 32, + F("")); html_TD(); } idx += 3; @@ -464,10 +474,9 @@ bool P037_data_struct::webform_load( addFormNote(F("Both Name and Value must be filled for a valid mapping. Mappings are case-sensitive.")); if (extraMappings == P037_EXTRA_VALUES) { - String moreMessage = concat(F("After filling all mappings, submitting this page will make extra mappings available (up to "), - P037_MAX_MAPPINGS); - moreMessage += F(")."); - addFormNote(moreMessage); + addFormNote(strformat( + F("After filling all mappings, submitting this page will make extra mappings available (up to %d)."), + P037_MAX_MAPPINGS)); } } # endif // if P037_MAPPING_SUPPORT diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index b190e42869..1b7e34e338 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -2319,9 +2319,6 @@ bool P104_data_struct::webform_load(struct EventStruct *event) { addTextBox(getPluginCustomArgName(index + P104_OFFSET_TEXT), zones[zone].text, P104_MAX_TEXT_LENGTH_PER_ZONE, - false, - false, - EMPTY_STRING, F("")); { html_TD(); // Content diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index e3dbfae293..ff9dc6c7ce 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -613,6 +613,30 @@ void addFloatNumberBox(const String& id, float value, float min, float max, unsi // ******************************************************************************** // Add Textbox // ******************************************************************************** +void addTextBox(const __FlashStringHelper * id, + const String& value, + int maxlength, + const __FlashStringHelper * classname) +{ + addTextBox(String(id), value, maxlength, + false, // readonly + false, // required + EMPTY_STRING, // pattern + classname); +} + +void addTextBox(const String& id, + const String& value, + int maxlength, + const __FlashStringHelper * classname) +{ + addTextBox(id, value, maxlength, + false, // readonly + false, // required + EMPTY_STRING, // pattern + classname); +} + void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, bool readonly, bool required, const String& pattern) { addTextBox(id, value, maxlength, readonly, required, pattern, F("wide")); } diff --git a/src/src/WebServer/Markup.h b/src/src/WebServer/Markup.h index 1182660f60..a2df43b3ea 100644 --- a/src/src/WebServer/Markup.h +++ b/src/src/WebServer/Markup.h @@ -185,6 +185,17 @@ void addNumericBox(const String& id, // ******************************************************************************** // Add Textbox // ******************************************************************************** +void addTextBox(const __FlashStringHelper * id, + const String& value, + int maxlength, + const __FlashStringHelper * classname); + +void addTextBox(const String& id, + const String& value, + int maxlength, + const __FlashStringHelper * classname); + + void addTextBox(const __FlashStringHelper * id, const String& value, int maxlength, From 1148a0d89b1cc589061319a7f90d47777007ed69 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 27 Jan 2025 15:52:26 +0100 Subject: [PATCH 50/50] [Build] Fix build errors on ESP8266 --- src/_P043_ClkOutput.ino | 1 + src/src/PluginStructs/P025_data_struct.cpp | 42 +++++++++------------- src/src/PluginStructs/P025_data_struct.h | 17 +++++---- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index bff78103af..589e575574 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -167,6 +167,7 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) addHtml(','); addTextBox(concat(F("clock"), x), parseString(timeStr, 2), 32, + false, false, EMPTY_STRING, F(""), # if FEATURE_TOOLTIPS EMPTY_STRING, diff --git a/src/src/PluginStructs/P025_data_struct.cpp b/src/src/PluginStructs/P025_data_struct.cpp index 402efaa4b1..d5e33ddf54 100644 --- a/src/src/PluginStructs/P025_data_struct.cpp +++ b/src/src/PluginStructs/P025_data_struct.cpp @@ -9,15 +9,8 @@ # define P025_CONFIG_REGISTER 0x01 -P025_VARIOUS_BITS_t::P025_VARIOUS_BITS_t(int16_t value) { - memcpy(this, &value, sizeof(int16_t)); -} +P025_VARIOUS_BITS_t::P025_VARIOUS_BITS_t(int16_t value) : _regValue(value) {} -int16_t P025_VARIOUS_BITS_t::pconfigvalue() const { - int16_t value{}; - memcpy(&value, this, sizeof(int16_t)); - return value; -} const __FlashStringHelper* Plugin_025_valuename(uint8_t value_nr, bool displayString) { const __FlashStringHelper *strings[] { @@ -47,30 +40,29 @@ const __FlashStringHelper* toString(P025_sensorType sensorType) } struct P025_config_register { - struct { - uint16_t comp_que : 2; - uint16_t comp_lat : 1; - uint16_t comp_pol : 1; - uint16_t compMode : 1; - uint16_t datarate : 3; - uint16_t mode : 1; - uint16_t PGA : 3; - uint16_t MUX : 3; - uint16_t operatingStatus : 1; + union { + struct { + uint16_t comp_que : 2; + uint16_t comp_lat : 1; + uint16_t comp_pol : 1; + uint16_t compMode : 1; + uint16_t datarate : 3; + uint16_t mode : 1; + uint16_t PGA : 3; + uint16_t MUX : 3; + uint16_t operatingStatus : 1; + }; + uint16_t _regValue{}; }; - P025_config_register(uint16_t regval) { - memcpy(this, ®val, sizeof(uint16_t)); - } + P025_config_register(uint16_t regval) : _regValue(regval) {} void setRegval(uint16_t regval) { - memcpy(this, ®val, sizeof(uint16_t)); + _regValue = regval; } uint16_t getRegval() const { - uint16_t regval{}; - memcpy(®val, this, sizeof(uint16_t)); - return regval; + return _regValue; } String toString() const { diff --git a/src/src/PluginStructs/P025_data_struct.h b/src/src/PluginStructs/P025_data_struct.h index 6730a0ff59..be3a1cbc41 100644 --- a/src/src/PluginStructs/P025_data_struct.h +++ b/src/src/PluginStructs/P025_data_struct.h @@ -6,17 +6,20 @@ struct P025_VARIOUS_BITS_t { - struct { - uint16_t cal : 1; - uint16_t outputVolt : 1; - uint16_t sampleRateSet : 1; - uint16_t sampleRate : 3; - uint16_t unused : 10; + union { + struct { + uint16_t cal : 1; + uint16_t outputVolt : 1; + uint16_t sampleRateSet : 1; + uint16_t sampleRate : 3; + uint16_t unused : 10; + }; + uint16_t _regValue{}; }; P025_VARIOUS_BITS_t(int16_t value); - int16_t pconfigvalue() const; + int16_t pconfigvalue() const { return _regValue; } uint16_t getSampleRate() const { if (sampleRateSet) { return sampleRate; }