diff --git a/docs/source/Plugin/P138.rst b/docs/source/Plugin/P138.rst new file mode 100644 index 000000000..2fe25a207 --- /dev/null +++ b/docs/source/Plugin/P138.rst @@ -0,0 +1,120 @@ +.. include:: ../Plugin/_plugin_substitutions_p13x.repl +.. _P138_page: + +|P138_typename| +================================================== + +|P138_shortinfo| + +Plugin details +-------------- + +Type: |P138_type| + +Name: |P138_name| + +Status: |P138_status| + +GitHub: |P138_github|_ + +Maintainer: |P138_maintainer| + +Used libraries: |P138_usedlibraries| + +Description +----------- + +I2C Power management controller. As found in some ESP32 models of M5Stack and TTGO. + +Plugin is only available in ESP32 builds, as this controller is currently only found to be used with ESP32 boards. It can be used on ESP8266, though untested. + +Configuration +-------------- + +.. image:: P138_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. + +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 +^^^^^^^^^^^^^^^^ + +* **Decimals for config values**: The number of decimals to use for presenting the Config values (see below for a list of available values). + +* **Event on PowerSource change**: When checked the plugin will generate an event ``#PowerChanged`` with argument 0 or 1 for battery or external power (the new state). + +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. + +.. image:: P138_NumberOutputValuesOptions.png + +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:: P138_ValueOptions.png + +Available options: + +* *None*: To leave the value empty/0.00 + +* *Battery current*: The current measured at the Battery connection. + +* *Charge undervoltage*: The (fixed) voltage at which charging of the battery is started by the controller. + +* *Stop voltage*: The (fixed) configured voltage at which the battery is no longer being charged. + +* *Input current*: The input current used by the controller. + +* *Charge level*: The charge level of the battery. + +* *Power source*: The power source used by the ESP, 0 = battery, 1 = External (USB) power. + +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 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. + +.. Commands +.. ~~~~~~~~ + +.. .. include:: P138_commands.repl + + +Extra values +~~~~~~~~~~~~ + +As this plugin has more values available than can be set to the regular Values outputs, all values are also available from the Get Config feature. + +.. include:: P138_values.repl + +Extra events +~~~~~~~~~~~~ + +Besides the regular events, of ``#=`` or ``#All=,..`` when the **Interval** is set, there can be an extra event generated: + +.. include:: P138_events.repl + + +Change log +---------- + +.. versionchanged:: 2.0 + + |added| 2022-08-26 Initially added. diff --git a/docs/source/Plugin/P138_DeviceConfiguration.png b/docs/source/Plugin/P138_DeviceConfiguration.png new file mode 100644 index 000000000..124aefbc4 Binary files /dev/null and b/docs/source/Plugin/P138_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P138_NumberOutputValuesOptions.png b/docs/source/Plugin/P138_NumberOutputValuesOptions.png new file mode 100644 index 000000000..3b8753b4c Binary files /dev/null and b/docs/source/Plugin/P138_NumberOutputValuesOptions.png differ diff --git a/docs/source/Plugin/P138_ValueOptions.png b/docs/source/Plugin/P138_ValueOptions.png new file mode 100644 index 000000000..b7a45ae38 Binary files /dev/null and b/docs/source/Plugin/P138_ValueOptions.png differ diff --git a/docs/source/Plugin/P138_events.repl b/docs/source/Plugin/P138_events.repl new file mode 100644 index 000000000..72327d677 --- /dev/null +++ b/docs/source/Plugin/P138_events.repl @@ -0,0 +1,12 @@ +.. csv-table:: + :header: "Event", "Extra information" + :widths: 20, 30 + + " + | ``#PowerChanged=`` + + | ``source``: ``0`` (Battery power) or ``1`` (External / USB power). + + "," + | Event generated when the **Event on PowerSource change** is enabled. + " diff --git a/docs/source/Plugin/P138_values.repl b/docs/source/Plugin/P138_values.repl new file mode 100644 index 000000000..1cab53a47 --- /dev/null +++ b/docs/source/Plugin/P138_values.repl @@ -0,0 +1,34 @@ +.. csv-table:: + :escape: ^ + :widths: 20, 30 + + " + ``[#batterycurrent]`` + "," + The current measured at the Battery connection. + " + " + ``[#chargeundervoltage]`` + "," + The (fixed) voltage at which charging of the battery is started by the controller. + " + " + ``[#stopvoltage]`` + "," + The (fixed) configured voltage at which the battery is no longer being charged. + " + " + ``[#inputcurrent]`` + "," + The input current used by the controller. + " + " + ``[#chargelevel]`` + "," + The charge level of the battery. + " + " + ``[#powersource]`` + "," + The power source used by the ESP, 0 = battery, 1 = External (USB) power. + " diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst index 9afcb3f3c..38b6c501c 100644 --- a/docs/source/Plugin/_Plugin.rst +++ b/docs/source/Plugin/_Plugin.rst @@ -362,6 +362,7 @@ There are different released versions of ESP Easy: ":ref:`P133_page`","|P133_status|","P133" ":ref:`P134_page`","|P134_status|","P134" ":ref:`P135_page`","|P135_status|","P135" + ":ref:`P138_page`","|P138_status|","P138" ":ref:`P141_page`","|P141_status|","P141" ":ref:`P143_page`","|P143_status|","P143" ":ref:`P148_page`","|P148_status|","P148" @@ -511,6 +512,11 @@ Plugins: |Plugin_Position| Hardware: |P013_usedby|, |P082_usedby| +Power management +---------------- + +Plugins: |Plugin_PowerMgt| + Regulator --------- diff --git a/docs/source/Plugin/_plugin_categories.repl b/docs/source/Plugin/_plugin_categories.repl index 242e8f53e..6a38491d1 100644 --- a/docs/source/Plugin/_plugin_categories.repl +++ b/docs/source/Plugin/_plugin_categories.repl @@ -23,6 +23,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` .. |Plugin_Position| replace:: :ref:`P013_page`, :ref:`P082_page`, :ref:`P121_page` +.. |Plugin_PowerMgt| replace:: :ref:`P138_page` .. |Plugin_Regulator| replace:: :ref:`P021_page` .. |Plugin_RFID| replace:: :ref:`P008_page`, :ref:`P017_page`, :ref:`P040_page`, :ref:`P111_page` .. |Plugin_Switch_input| replace:: :ref:`P001_page`, :ref:`P009_page`, :ref:`P019_page`, :ref:`P059_page`, :ref:`P080_page`, :ref:`P091_page`, :ref:`P097_page`, :ref:`P143_page` diff --git a/docs/source/Plugin/_plugin_substitutions_p13x.repl b/docs/source/Plugin/_plugin_substitutions_p13x.repl index bdf9ca8ec..4e131c3eb 100644 --- a/docs/source/Plugin/_plugin_substitutions_p13x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p13x.repl @@ -62,3 +62,16 @@ .. |P135_maintainer| replace:: `tonhuisman` .. |P135_compileinfo| replace:: `.` .. |P135_usedlibraries| replace:: `https://github.com/sparkfun/SparkFun_SCD4x_Arduino_Library` + +.. |P138_name| replace:: :cyan:`IP5306 Power management` +.. |P138_type| replace:: :cyan:`Power mgt` +.. |P138_typename| replace:: :cyan:`Power mgt - IP5306 Power management` +.. |P138_porttype| replace:: `.` +.. |P138_status| replace:: :yellow:`COLLECTION` :yellow:`DISPLAY` :yellow:`ENERGY` :yellow:`NEOPIXEL` +.. |P138_github| replace:: P138_IP5306.ino +.. _P138_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P138_IP5306.ino +.. |P138_usedby| replace:: `.` +.. |P138_shortinfo| replace:: `Power management controller` +.. |P138_maintainer| replace:: `tonhuisman` +.. |P138_compileinfo| replace:: `.` +.. |P138_usedlibraries| replace:: `https://github.com/codewitch-honey-crisis/htcw_ip5306` diff --git a/docs/source/Reference/Events.rst b/docs/source/Reference/Events.rst index fc8259da2..cf3c9b666 100644 --- a/docs/source/Reference/Events.rst +++ b/docs/source/Reference/Events.rst @@ -528,6 +528,10 @@ P129 :ref:`P129_page` .. include:: ../Plugin/P129_events.repl +P138 :ref:`P138_page` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. include:: ../Plugin/P138_events.repl P143 :ref:`P143_page` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/htcw_ip5306/LICENSE b/lib/htcw_ip5306/LICENSE new file mode 100644 index 000000000..230ea108b --- /dev/null +++ b/lib/htcw_ip5306/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 honey the codewitch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/htcw_ip5306/README.md b/lib/htcw_ip5306/README.md new file mode 100644 index 000000000..9e67ab85c --- /dev/null +++ b/lib/htcw_ip5306/README.md @@ -0,0 +1,13 @@ +# button + +This library allows you to use the IP5306 power management module + +``` +[env:node32s] +platform = espressif32 +board = node32s +framework = arduino +lib_deps = + codewitch-honey-crisis/htcw_ip5306@^0.1.0 +lib_ldf_mode = deep +``` \ No newline at end of file diff --git a/lib/htcw_ip5306/include/ip5306.hpp b/lib/htcw_ip5306/include/ip5306.hpp new file mode 100644 index 000000000..df892a839 --- /dev/null +++ b/lib/htcw_ip5306/include/ip5306.hpp @@ -0,0 +1,108 @@ +#pragma once +#include +#include +namespace arduino { +enum struct ip5306_shutdown { + after_8s=0, + after_32s=1, + after_16s=2, + after_64s=3 +}; +enum struct ip5306_charging_full_voltage { + at_4_14v=0, + at_4_17v=1, + at_4_185v=2, + at_4_2v=3, +}; +enum struct ip5306_press_time { + press_2_seconds=0, + press_3_seconds=1 +}; +enum struct ip5306_end_charge_current { + at_200mA = 0, + at_400mA = 1, + at_500mA = 2, + at_600mA = 3 +}; +enum struct ip5306_voltage_pressure { + none = 0, + p_14mV=1, + p_28mV=2, + p_42mV=3 +}; +enum struct ip5306_charge_cutoff_voltage { + at_4_2v = 0, + at_4_3v = 1, + at_4_35v = 2, + at_4_4v = 3 +}; +enum struct ip5306_source { + battery = 0, + vin = 1 +}; +enum struct ip5306_load { + heavy = 0, + light = 1 +}; +enum struct ip5306_presses { + short_press_twice = 0, + long_press_once = 1 +}; +class ip5306 { + TwoWire& m_i2c; +public: + constexpr static const uint8_t address = 0x75; + inline ip5306(TwoWire& i2c=Wire) : m_i2c(i2c) { + + } + bool key_off() const; + void key_off(bool value); + bool boost_output() const; + void boost_output(bool value); + bool power_on_load() const; + void power_on_load(bool value); + bool charger() const; + void charger(bool value); + bool boost() const; + void boost(bool value); + bool low_battery_shutdown() const; + void low_battery_shutdown(bool value); + bool boost_after_vin() const; + void boost_after_vin(bool value); + bool short_press_boot_switch() const; + void short_press_boot_switch(bool value); + ip5306_presses flashlight_clicks() const; + void flashlight_clicks(ip5306_presses value); + ip5306_presses boost_off_clicks() const; + void boost_off_clicks(ip5306_presses value); + ip5306_shutdown light_load_shutdown_time() const; + void light_load_shutdown_time(ip5306_shutdown value); + ip5306_press_time long_press_time() const; + void long_press_time(ip5306_press_time value); + ip5306_charging_full_voltage charging_full_stop_voltage() const; + void charging_full_stop_voltage(ip5306_charging_full_voltage value); + float charge_under_voltage_loop() const; + void charge_under_voltage_loop(float value); + ip5306_end_charge_current end_charge_current_detection() const; + void end_charge_current_detection(ip5306_end_charge_current value); + ip5306_voltage_pressure voltage_pressure() const; + void voltage_pressure(ip5306_voltage_pressure value); + ip5306_charge_cutoff_voltage charge_cutoff_voltage() const; + void charge_cutoff_voltage(ip5306_charge_cutoff_voltage value); + ip5306_source charge_cc_loop() const; + void charge_cc_loop(ip5306_source value); + unsigned vin_current() const; + void vin_current(unsigned value); + bool short_press_detected() const; + void clear_short_press_detected(); + bool long_press_detected() const; + void clear_long_press_detected(); + bool double_click_detected() const; + void clear_double_click_detected(); + ip5306_source power_source() const; + bool battery_full() const; + ip5306_load output_load() const; + float charge_level() const; + +}; +} \ No newline at end of file diff --git a/lib/htcw_ip5306/library.json b/lib/htcw_ip5306/library.json new file mode 100644 index 000000000..d45768b78 --- /dev/null +++ b/lib/htcw_ip5306/library.json @@ -0,0 +1,18 @@ +{ + "name": "htcw_ip5306", + "version": "0.1.0", + "description": "Provides support for the IP5306 power module", + "keywords": ["power", "battery", "sleep"], + "authors": { + "name":"honey the codewitch", + "homepage": "https://www.codeproject.com/Members/code-witch" + }, + "license": "MIT", + "frameworks": ["arduino"], + "platforms": "*", + "repository": + { + "type": "git", + "url": "https://github.com/codewitch-honey-crisis/htcw_ip5306.git" + } +} \ No newline at end of file diff --git a/lib/htcw_ip5306/src/ip5306.cpp b/lib/htcw_ip5306/src/ip5306.cpp new file mode 100644 index 000000000..376593b68 --- /dev/null +++ b/lib/htcw_ip5306/src/ip5306.cpp @@ -0,0 +1,250 @@ +#include +namespace arduino { +// based on +// https://gist.github.com/me-no-dev/7702f08dd578de5efa47caf322250b57 + +/* +** IP5306 Power Module +*/ + +/* M5 Defaults +KeyOff: Enabled +BoostOutput: Disabled +PowerOnLoad: Enabled +Charger: Enabled +Boost: Enabled +LowBatShutdown: Enabled +ShortPressBoostSwitch: Disabled +FlashlightClicks: Double Press +BoostOffClicks: Long Press +BoostAfterVin: Open +LongPressTime: 2s +ChargeUnderVoltageLoop: 4.55V +ChargeCCLoop: Vin +VinCurrent: 2250mA +VoltagePressure: 28mV +ChargingFullStopVoltage: 4.17V +LightLoadShutdownTime: 32s +EndChargeCurrentDetection: 500mA +ChargeCutoffVoltage: 4.2V +*/ + +#define IP5306_REG_SYS_0 0x00 +#define IP5306_REG_SYS_1 0x01 +#define IP5306_REG_SYS_2 0x02 +#define IP5306_REG_CHG_0 0x20 +#define IP5306_REG_CHG_1 0x21 +#define IP5306_REG_CHG_2 0x22 +#define IP5306_REG_CHG_3 0x23 +#define IP5306_REG_CHG_4 0x24 +#define IP5306_REG_READ_0 0x70 +#define IP5306_REG_READ_1 0x71 +#define IP5306_REG_READ_2 0x72 +#define IP5306_REG_READ_3 0x77 +#define IP5306_REG_READ_4 0x78 + +#define IP5306_LEDS2PCT(byte) \ + ((byte & 0x01 ? 25 : 0) + \ + (byte & 0x02 ? 25 : 0) + \ + (byte & 0x04 ? 25 : 0) + \ + (byte & 0x08 ? 25 : 0)) + +int ip5306_get_reg(TwoWire& i2c, uint8_t reg){ + i2c.beginTransmission(ip5306::address); + i2c.write(reg); + if(i2c.endTransmission(false) == 0 && i2c.requestFrom(0x75, 1)){ + return i2c.read(); + } + return -1; +} + +int ip5306_set_reg(TwoWire& i2c, uint8_t reg, uint8_t value){ + i2c.beginTransmission(ip5306::address); + i2c.write(reg); + i2c.write(value); + if(i2c.endTransmission(true) == 0){ + return 0; + } + return -1; +} + +uint8_t ip5306_get_bits(TwoWire& i2c, uint8_t reg, uint8_t index, uint8_t bits){ + int value = ip5306_get_reg(i2c,reg); + if(value < 0){ + Serial.printf("ip5306_get_bits fail: 0x%02x\n", reg); + return 0; + } + return (value >> index) & ((1 << bits)-1); +} + +void ip5306_set_bits(TwoWire& i2c, uint8_t reg, uint8_t index, uint8_t bits, uint8_t value){ + uint8_t mask = (1 << bits) - 1; + int v = ip5306_get_reg(i2c,reg); + if(v < 0){ + Serial.printf("ip5306_get_reg fail: 0x%02x\n", reg); + return; + } + v &= ~(mask << index); + v |= ((value & mask) << index); + if(ip5306_set_reg(i2c,reg, v)){ + Serial.printf("ip5306_set_bits fail: 0x%02x\n", reg); + } +} + +bool ip5306::key_off() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_0, 0, 1); +} +void ip5306::key_off(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_0, 0, 1, value); +} +bool ip5306::boost_output() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_0, 1, 1); +} +void ip5306::boost_output(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_0, 1, 1, value); +} + +bool ip5306::power_on_load() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_0, 2, 1); +} +void ip5306::power_on_load(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_0, 2, 1, value); +} +bool ip5306::charger() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_0, 4, 1); + +} +void ip5306::charger(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_0, 4, 1, value); +} +bool ip5306::boost() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_0, 5, 1); +} +void ip5306::boost(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_0, 5, 1, value); +} +bool ip5306::low_battery_shutdown() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_1, 0, 1); +} +void ip5306::low_battery_shutdown(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_1, 0, 1, value); +} +bool ip5306::boost_after_vin() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_1, 2, 1); +} +void ip5306::boost_after_vin(bool value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_1, 2, 1, value); +} +bool ip5306::short_press_boot_switch() const { + return ip5306_get_bits(m_i2c,IP5306_REG_SYS_1, 5, 1); +} +void ip5306::short_press_boot_switch(bool value) { + return ip5306_set_bits(m_i2c,IP5306_REG_SYS_1, 5, 1, value); +} +ip5306_presses ip5306::flashlight_clicks() const { + return (ip5306_presses)ip5306_get_bits(m_i2c,IP5306_REG_SYS_1, 6, 1); +} +void ip5306::flashlight_clicks(ip5306_presses value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_1, 6, 1, (int)value); +} +ip5306_presses ip5306::boost_off_clicks() const { + return (ip5306_presses)(1-ip5306_get_bits(m_i2c,IP5306_REG_SYS_1, 7, 1)); +} +void ip5306::boost_off_clicks(ip5306_presses value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_1, 7, 1, 1-((int)value)); +} +ip5306_shutdown ip5306::light_load_shutdown_time() const { + return (ip5306_shutdown)ip5306_get_bits(m_i2c,IP5306_REG_SYS_2, 2, 2); +} +void ip5306::light_load_shutdown_time(ip5306_shutdown value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_2, 2, 2, (int)value); +} +ip5306_press_time ip5306::long_press_time() const { + return (ip5306_press_time)ip5306_get_bits(m_i2c,IP5306_REG_SYS_2, 4, 1); +} +void ip5306::long_press_time(ip5306_press_time value) { + ip5306_set_bits(m_i2c,IP5306_REG_SYS_2, 4, 1, (int)value); +} +ip5306_charging_full_voltage ip5306::charging_full_stop_voltage() const { + return (ip5306_charging_full_voltage)ip5306_get_bits(m_i2c,IP5306_REG_CHG_0, 0, 2); +} +void ip5306::charging_full_stop_voltage(ip5306_charging_full_voltage value) { + ip5306_set_bits(m_i2c,IP5306_REG_CHG_0, 0, 2, (int)value); +} +float ip5306::charge_under_voltage_loop() const { + //Automatically adjust the charging current when the voltage of VOUT is greater than the set value + //Vout=4.45V + (v * 0.05V) (default 4.55V) + //When charging at the maximum current, the charge is less than the set value. Slowly reducing the charging current to maintain this voltage + int v = ip5306_get_bits(m_i2c,IP5306_REG_CHG_1, 2, 3); + return 4.45+(v*.05); +} +void ip5306::charge_under_voltage_loop(float value) { + value-=4.45; + int i = value/.05; + ip5306_set_bits(m_i2c,IP5306_REG_CHG_1, 2, 3, i); +} +ip5306_end_charge_current ip5306::end_charge_current_detection() const { + return (ip5306_end_charge_current)ip5306_get_bits(m_i2c,IP5306_REG_CHG_1, 6, 2); +} +void ip5306::end_charge_current_detection(ip5306_end_charge_current value) { + ip5306_set_bits(m_i2c,IP5306_REG_CHG_1, 6, 2, (int)value); +} +ip5306_voltage_pressure ip5306::voltage_pressure() const { + return (ip5306_voltage_pressure)ip5306_get_bits(m_i2c,IP5306_REG_CHG_2, 0, 2); +} +void ip5306::voltage_pressure(ip5306_voltage_pressure value) { + ip5306_set_bits(m_i2c,IP5306_REG_CHG_2, 0, 2, (int)value); +} +ip5306_charge_cutoff_voltage ip5306::charge_cutoff_voltage() const { + return (ip5306_charge_cutoff_voltage)ip5306_get_bits(m_i2c,IP5306_REG_CHG_2, 2, 2); +} +void ip5306::charge_cutoff_voltage(ip5306_charge_cutoff_voltage value) { + ip5306_set_bits(m_i2c,IP5306_REG_CHG_2, 2, 2, (int)value); +} +ip5306_source ip5306::charge_cc_loop() const { + return (ip5306_source)ip5306_get_bits(m_i2c,IP5306_REG_CHG_3, 5, 1); +} +void ip5306::charge_cc_loop(ip5306_source value) { + ip5306_set_bits(m_i2c,IP5306_REG_CHG_3, 5, 1, (int)value); +} +unsigned ip5306::vin_current() const { + int v = ip5306_get_bits(m_i2c,IP5306_REG_CHG_4, 0, 5); + return (v*100)+50; +} +void ip5306::vin_current(unsigned value) { + //ImA=(v*100)+50 (default 2250mA) + ip5306_set_bits(m_i2c,IP5306_REG_CHG_4, 0, 5, (value-50)/100); +} +bool ip5306::short_press_detected() const { + return ip5306_get_bits(m_i2c,IP5306_REG_READ_3, 0, 1); +} +void ip5306::clear_short_press_detected() { + ip5306_set_bits(m_i2c,IP5306_REG_READ_3, 0, 1, 1); +} +bool ip5306::long_press_detected() const { + return ip5306_get_bits(m_i2c,IP5306_REG_READ_3, 1, 1); +} +void ip5306::clear_long_press_detected() { + ip5306_set_bits(m_i2c,IP5306_REG_READ_3, 1, 1, 1); +} +bool ip5306::double_click_detected() const { + return ip5306_get_bits(m_i2c,IP5306_REG_READ_3, 2, 1); +} +void ip5306::clear_double_click_detected() { + ip5306_set_bits(m_i2c,IP5306_REG_READ_3, 2, 1, 1); +} +ip5306_source ip5306::power_source() const { + return (ip5306_source)ip5306_get_bits(m_i2c,IP5306_REG_READ_0, 3, 1); +} +bool ip5306::battery_full() const { + return ip5306_get_bits(m_i2c,IP5306_REG_READ_1, 3, 1); +} +ip5306_load ip5306::output_load() const { + return (ip5306_load)ip5306_get_bits(m_i2c,IP5306_REG_READ_2, 2, 1); +} +float ip5306::charge_level() const { + // LED[0-4] State (inverted) + return IP5306_LEDS2PCT(((~ip5306_get_bits(m_i2c,IP5306_REG_READ_4, 4, 4)) & 0x0F))/100.0; +} + +} \ No newline at end of file diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 53aaad172..86f8ccd3b 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -474,6 +474,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // #define USES_P134 // A02YYUW // #define USES_P135 // SCD4x // #define P135_FEATURE_RESET_COMMANDS 1 // Enable/Disable quite spacious (~950 bytes) 'selftest' and 'factoryreset' subcommands +// #define USES_P138 // IP5306 // #define USES_P141 // PCD8544 Nokia 5110 LCD // #define USES_P143 // I2C Rotary encoders // #define P143_FEATURE_INCLUDE_M5STACK 0 // Enabled by default, can be turned off here diff --git a/src/_P138_IP5306.ino b/src/_P138_IP5306.ino new file mode 100644 index 000000000..eaf28b756 --- /dev/null +++ b/src/_P138_IP5306.ino @@ -0,0 +1,234 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P138 + +// ####################################################################################################### +// #################################### Plugin 138: IP5306 Powermanagement ############################### +// ####################################################################################################### + +/** + * Changelog: + * 2022-12-06 tonhuisman: Reorder Device configuration because of added PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR state + * Enable PluginStats option + * 2022-12-05 tonhuisman: Remove [Testing] tag + * 2022-11-08 tonhuisman: Fix a few typos, cleanup some comments + * 2022-08-26 tonhuisman: Initial plugin development, using codewitch-honey-crisis/htcw_ip5306 library + **/ + +/** + * Supported commands: (none yet) + **/ +/** + * Get Config options: + * [#batcurrent] : Battery current + * [#chundervolt] : Charge Undervoltage + * [#stopvolt] : Input voltage + * [#inpcurrent] : Input current + * [#pwrsource] : Powersource + * // To re-use these lines: + * [#vbuscurr] : VBus current + * [#inttemp] : Internal temperature + * [#apsvolt] : APS voltage + * [#ldo2volt] : LDO2 voltage + * [#ldo3volt] : LDO3 voltage + * [#gpio0volt] : GPIO0 voltage + **/ + + +# define PLUGIN_138 +# define PLUGIN_ID_138 138 +# define PLUGIN_NAME_138 "Power mgt - IP5306 Power management" + +# include "./src/PluginStructs/P138_data_struct.h" + +boolean Plugin_138(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_138; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_QUAD; + Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; + 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_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_138); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + for (uint8_t i = 0; i < VARS_PER_TASK; ++i) { + if (i < P138_NR_OUTPUT_VALUES) { + safe_strncpy( + ExtraTaskSettings.TaskDeviceValueNames[i], + toString(static_cast(PCONFIG(P138_CONFIG_BASE + i)), false), + sizeof(ExtraTaskSettings.TaskDeviceValueNames[i])); + } else { + ZERO_FILL(ExtraTaskSettings.TaskDeviceValueNames[i]); + } + } + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = P138_NR_OUTPUT_VALUES; + success = true; + break; + } + + case PLUGIN_GET_DEVICEVTYPE: + { + event->sensorType = static_cast(PCONFIG(P138_SENSOR_TYPE_INDEX)); + event->idx = P138_SENSOR_TYPE_INDEX; + success = true; + break; + } + + case PLUGIN_I2C_HAS_ADDRESS: + { + success = event->Par1 == 0x75; + break; + } + + case PLUGIN_SET_DEFAULTS: + { + PCONFIG(0) = static_cast(P138_valueOptions_e::StopVoltage); + PCONFIG(1) = static_cast(P138_valueOptions_e::InCurrent); + PCONFIG(2) = static_cast(P138_valueOptions_e::BatteryCurrent); + PCONFIG(3) = static_cast(P138_valueOptions_e::ChargeLevel); + PCONFIG(P138_SENSOR_TYPE_INDEX) = static_cast(Sensor_VType::SENSOR_TYPE_QUAD); + P138_CONFIG_DECIMALS = 2; // 2 decimals for all get config values + bitSet(P138_CONFIG_FLAGS, P138_FLAG_POWERCHANGE); // Enable event + + break; + } + + case PLUGIN_WEBFORM_LOAD_OUTPUT_SELECTOR: + { + { + const __FlashStringHelper *valOptions[] = { + toString(P138_valueOptions_e::None), + toString(P138_valueOptions_e::BatteryCurrent), + toString(P138_valueOptions_e::ChargeUnderVoltage), + toString(P138_valueOptions_e::StopVoltage), + toString(P138_valueOptions_e::InCurrent), + toString(P138_valueOptions_e::ChargeLevel), + toString(P138_valueOptions_e::PowerSource), + }; + const int valValues[] = { + static_cast(P138_valueOptions_e::None), + static_cast(P138_valueOptions_e::BatteryCurrent), + static_cast(P138_valueOptions_e::ChargeUnderVoltage), + static_cast(P138_valueOptions_e::StopVoltage), + static_cast(P138_valueOptions_e::InCurrent), + static_cast(P138_valueOptions_e::ChargeLevel), + static_cast(P138_valueOptions_e::PowerSource), + }; + + for (uint8_t i = 0; i < P138_NR_OUTPUT_VALUES; i++) { + sensorTypeHelper_loadOutputSelector(event, + P138_CONFIG_BASE + i, + i, + sizeof(valValues) / sizeof(int), + valOptions, + valValues); + } + } + success = true; + + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + addFormNumericBox(F("Decimals for config values"), F("decimals"), P138_CONFIG_DECIMALS, 0, 4); + + addFormCheckBox(F("Event on PowerSource change"), F("eventpwrchg"), bitRead(P138_CONFIG_FLAGS, P138_FLAG_POWERCHANGE)); + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + for (uint8_t i = 0; i < P138_NR_OUTPUT_VALUES; i++) { + sensorTypeHelper_saveOutputSelector(event, P138_CONFIG_BASE + i, i, + toString(static_cast(PCONFIG(P138_CONFIG_BASE + i)), false)); + } + + P138_CONFIG_DECIMALS = getFormItemInt(F("decimals")); + bitWrite(P138_CONFIG_FLAGS, P138_FLAG_POWERCHANGE, isFormItemChecked(F("eventpwrchg"))); + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P138_data_struct(event)); + P138_data_struct *P138_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P138_data) { + success = true; + } + + break; + } + + case PLUGIN_READ: + { + P138_data_struct *P138_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P138_data) { + success = P138_data->plugin_read(event); + } + break; + } + + case PLUGIN_FIFTY_PER_SECOND: + { + P138_data_struct *P138_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P138_data) { + success = P138_data->plugin_fifty_per_second(event); + } + break; + } + + // case PLUGIN_WRITE: + // { + // P138_data_struct *P138_data = static_cast(getPluginTaskData(event->TaskIndex)); + + // if (nullptr != P138_data) { + // success = P138_data->plugin_write(event, string); + // } + // break; + // } + + case PLUGIN_GET_CONFIG_VALUE: + { + P138_data_struct *P138_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P138_data) { + success = P138_data->plugin_get_config_value(event, string); // GetConfig operation, handle variables + } + break; + } + } + + return success; +} + +#endif // USES_P138 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 875600cd1..575e37c6d 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1405,6 +1405,9 @@ To create/register a plugin, you have to : #define USES_P081 // Cron #define USES_P082 // GPS #define USES_P089 // Ping + #if !defined(USES_P138) && defined(ESP32) + #define USES_P138 // IP5306 + #endif #endif #ifdef PLUGIN_SET_COLLECTION_A @@ -1538,6 +1541,9 @@ To create/register a plugin, you have to : #ifndef USES_P132 #define USES_P132 // INA3221 #endif + #if !defined(USES_P138) && defined(ESP32) + #define USES_P138 // IP5306 + #endif #ifndef USES_P148 #define USES_P148 // Sonoff POWR3xxD and THR3xxD display #endif @@ -1603,6 +1609,9 @@ To create/register a plugin, you have to : #ifndef USES_P116 #define USES_P116 // ST77xx #endif + #if !defined(USES_P138) && defined(ESP32) + #define USES_P138 // IP5306 + #endif #ifndef USES_P141 #define USES_P141 // PCD8544 Nokia 5110 #endif @@ -1762,6 +1771,9 @@ To create/register a plugin, you have to : #ifdef ESP8266 #define FEATURE_CHART_JS 0 #endif + #if !defined(USES_P138) && defined(ESP32) + #define USES_P138 // IP5306 + #endif #endif #ifdef CONTROLLER_SET_COLLECTION diff --git a/src/src/PluginStructs/P138_data_struct.cpp b/src/src/PluginStructs/P138_data_struct.cpp new file mode 100644 index 000000000..7c4d80d56 --- /dev/null +++ b/src/src/PluginStructs/P138_data_struct.cpp @@ -0,0 +1,150 @@ +#include "../PluginStructs/P138_data_struct.h" + +#ifdef USES_P138 + +// **************************************************************************/ +// toString: convert P138_valueOptions_e enum to value-name or display-name +// **************************************************************************/ +const __FlashStringHelper* toString(const P138_valueOptions_e value, bool displayString) { + switch (value) { + case P138_valueOptions_e::None: return displayString ? F("None") : F("none"); + case P138_valueOptions_e::BatteryCurrent: return displayString ? F("Battery current") : F("batterycurrent"); + case P138_valueOptions_e::ChargeUnderVoltage: return displayString ? F("Charge undervoltage") : F("chargeundervoltage"); + case P138_valueOptions_e::StopVoltage: return displayString ? F("Stop voltage") : F("stopvoltage"); + case P138_valueOptions_e::InCurrent: return displayString ? F("Input current") : F("inputcurrent"); + case P138_valueOptions_e::ChargeLevel: return displayString ? F("Charge level") : F("chargelevel"); + case P138_valueOptions_e::PowerSource: return displayString ? F("Power source") : F("powersource"); + } + return F("*Undefined*"); +} + +// **************************************************************************/ +// Constructor +// **************************************************************************/ +P138_data_struct::P138_data_struct(struct EventStruct *event) { + addLog(LOG_LEVEL_INFO, F("IP5306: Init.")); + + _ip5306 = new (std::nothrow) arduino::ip5306(); // Default address and I2C Wire object +} + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +P138_data_struct::~P138_data_struct() { + delete _ip5306; + _ip5306 = nullptr; +} + +// **************************************************************************/ +// plugin_read: Read the values and send to controller(s) +// **************************************************************************/ +bool P138_data_struct::plugin_read(struct EventStruct *event) { + bool success = true; + + for (uint8_t i = 0; i < P138_NR_OUTPUT_VALUES; i++) { + UserVar[event->BaseVarIndex + i] = read_value(static_cast(PCONFIG(P138_CONFIG_BASE + i))); + } + + return success; +} + +// **************************************************************************/ +// plugin_fifty_per_second: check if the powersource changed and generate an event for that +// **************************************************************************/ +bool P138_data_struct::plugin_fifty_per_second(struct EventStruct *event) { + bool success = true; + + if (bitRead(P138_CONFIG_FLAGS, P138_FLAG_POWERCHANGE) && isInitialized()) { + int8_t src = static_cast(_ip5306->power_source()); + + if (_lastPowerSource != src) { // Changed? + String eventCommand = getTaskDeviceName(event->TaskIndex); + eventCommand += F("#PowerChanged="); + eventCommand += src; // 0 = battery, 1 = Vin + eventQueue.add(eventCommand); + _lastPowerSource = src; // Keep current + } + } + return success; +} + +// **************************************************************************/ +// read_value: Read the requested value +// **************************************************************************/ +float P138_data_struct::read_value(P138_valueOptions_e value) { + if (isInitialized()) { + switch (value) { + case P138_valueOptions_e::None: return 0.0f; + case P138_valueOptions_e::BatteryCurrent: { + const float charge_current[] = { 200.0f, 400.0f, 500.0f, 600.0f }; + return charge_current[static_cast(_ip5306->end_charge_current_detection())]; + } + case P138_valueOptions_e::ChargeUnderVoltage: return _ip5306->charge_under_voltage_loop(); + case P138_valueOptions_e::StopVoltage: { + const float stop_voltage[] = { 4.14f, 4.17f, 4.185f, 4.2f }; + return stop_voltage[static_cast(_ip5306->charging_full_stop_voltage())]; + } + case P138_valueOptions_e::InCurrent: return static_cast(_ip5306->vin_current()); + case P138_valueOptions_e::ChargeLevel: return _ip5306->charge_level() * 100.0f; // Make percentage + case P138_valueOptions_e::PowerSource: return static_cast(_ip5306->power_source()); // 0 = batt, 1 = Vin + } + } + return 0.0f; +} + +// **************************************************************************/ +// plugin_write: Process commands +// **************************************************************************/ +bool P138_data_struct::plugin_write(struct EventStruct *event, + const String & string) { + bool success = false; + + // String cmd = parseString(string, 1); + + // if (isInitialized() && cmd.equals(F("ip5306"))) { // Command trigger + // cmd = parseString(string, 2); // sub command + // const bool empty3 = parseString(string, 3).isEmpty(); + // const bool empty4 = parseString(string, 4).isEmpty(); + // success = true; + + // if (cmd.equals(F("??")) && !empty3) { // ip5306,??, + // } else { + // success = false; + // } + // } + + return success; +} + +/**************************************************************************** + * plugin_get_config_value: Retrieve values like [#] + ***************************************************************************/ +bool P138_data_struct::plugin_get_config_value(struct EventStruct *event, + String & string) { + bool success = true; + String command = parseString(string, 1); + float value; + + if (command.equals(F("batcurrent"))) { // batcurrent + value = read_value(P138_valueOptions_e::BatteryCurrent); + } else if (command.equals(F("chundervolt"))) { // chundervolt + value = read_value(P138_valueOptions_e::ChargeUnderVoltage); + } else if (command.equals(F("stopvolt"))) { // stopvolt + value = read_value(P138_valueOptions_e::StopVoltage); + } else if (command.equals(F("inpcurrent"))) { // inpcurrent + value = read_value(P138_valueOptions_e::InCurrent); + } else if (command.equals(F("chargelvl"))) { // chargelvl + value = read_value(P138_valueOptions_e::ChargeLevel); + } else if (command.equals(F("pwrsource"))) { // pwrsource + value = read_value(P138_valueOptions_e::PowerSource); + } else { + success = false; + } + + if (success) { + string = toString(value, static_cast(P138_CONFIG_DECIMALS)); + } + return success; +} + +#endif // ifdef USES_P138 diff --git a/src/src/PluginStructs/P138_data_struct.h b/src/src/PluginStructs/P138_data_struct.h new file mode 100644 index 000000000..c1ec259a1 --- /dev/null +++ b/src/src/PluginStructs/P138_data_struct.h @@ -0,0 +1,59 @@ +#ifndef PLUGINSTRUCTS_P138_DATA_STRUCT_H +#define PLUGINSTRUCTS_P138_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P138 + +# include + +# define P138_DEBUG_LOG // Enable for some (extra) logging + +# define P138_CONFIG_BASE 0 // Uses PCONFIG(0)..PCONFIG(3) to store the selection for 4 output values +# define P138_SENSOR_TYPE_INDEX (P138_CONFIG_BASE + VARS_PER_TASK) +# define P138_NR_OUTPUT_VALUES getValueCountFromSensorType(static_cast(PCONFIG(P138_SENSOR_TYPE_INDEX))) +# define P138_CONFIG_DECIMALS PCONFIG(P138_CONFIG_BASE + VARS_PER_TASK + 1) + +# define P138_CONFIG_FLAGS PCONFIG_ULONG(0) +# define P138_FLAG_POWERCHANGE 0 // Flag 0: Send event on PowerChange event + +enum class P138_valueOptions_e : uint8_t { + None = 0x00, + BatteryCurrent = 0x01, + ChargeUnderVoltage = 0x02, + StopVoltage = 0x03, + InCurrent = 0x04, + ChargeLevel = 0x05, + PowerSource = 0x06, +}; + +const __FlashStringHelper* toString(const P138_valueOptions_e value, + bool displayString = true); + +struct P138_data_struct : public PluginTaskData_base { +public: + + P138_data_struct(struct EventStruct *event); + P138_data_struct() = delete; + ~P138_data_struct(); + + bool plugin_read(struct EventStruct *event); + bool plugin_fifty_per_second(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + bool plugin_get_config_value(struct EventStruct *event, + String & string); + +private: + + arduino::ip5306 *_ip5306 = nullptr; + + bool isInitialized() { + return nullptr != _ip5306; + } + + float read_value(P138_valueOptions_e value); + int8_t _lastPowerSource = -1; +}; + +#endif // ifdef USES_P138 +#endif // ifndef PLUGINSTRUCTS_P138_DATA_STRUCT_H diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 45deda0b8..3a140f0a2 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -327,9 +327,11 @@ String getKnownI2Cdevice(uint8_t address) { result += F("HT16K33,TCA9543a/6a/8a I2C multiplexer"); break; case 0x74: - case 0x75: result += F("HT16K33,TCA9546a/8a I2C multiplexer"); break; + case 0x75: + result += F("HT16K33,TCA9546a/8a I2C multiplexer,IP5306"); + break; case 0x76: result += F("BMP280,BME280,BME680,MS5607,MS5611,HT16K33,TCA9546a/8a I2C multiplexer"); break;