Added NCSA Universal Light Controller

This commit is contained in:
Lukas Schmid 2023-06-19 20:05:51 +02:00
parent c04c73bbd7
commit 0e346b72b6
7 changed files with 930 additions and 0 deletions

View File

@ -40,6 +40,7 @@ default_envs = nodemcuv2, esp8266_2m, esp01_1m_full, esp32dev, esp32_eth, lolin_
; default_envs = esp32dev_qio80
; default_envs = esp32_eth_ota1mapp
; default_envs = esp32s2_saola
; default_envs = esp32_ulc
src_dir = ./wled00
data_dir = ./wled00/data
@ -779,3 +780,15 @@ lib_deps =
${esp32.lib_deps}
TFT_eSPI @ ^2.3.70
board_build.partitions = ${esp32.default_partitions}
# ------------------------------------------------------------------------------
# NetCube Systems Universal Light Controller
# ------------------------------------------------------------------------------
[env:esp32_ulc]
board = esp32-poe
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp32} -D WLED_RELEASE_NAME=ESP32_ULC -D RLYPIN=-1 -D WLED_USE_ETHERNET -D WLED_ETH_DEFAULT=6 -D LEDPIN=14 -D DEFAULT_LED_COUNT=37 -D APL_MILLIAMPS_DEFAULT=65000 -D BTNPIN=-1 -D WLED_DISABLE_BLYNK -D IRPIN=-1 -D USERMOD_ULC_BATTERYMANAGEMENT
lib_deps = ${esp32.lib_deps}
platform = espressif32@3.2
board_build.partitions = ${esp32.default_partitions}

View File

@ -0,0 +1,584 @@
/*@brief library to interact with IP5306 over I2C communication
*/
#ifndef _IP5306_I2C_H_
#define _IP5306_I2C_H_
#include <Wire.h> //I2C library
#include "Arduino.h"
#include <stdint.h>
/*macros definition*/
#define ENABLE 1
#define DISABLE 0
#define IP5306_ADDRESS 0x75 //7 bit slave address
/****************************Registers*****************************/
#define SYS_CTL0 0x00
#define SYS_CTL1 0x01
#define SYS_CTL2 0x02
#define Charger_CTL0 0x20
#define Charger_CTL1 0x21
#define Charger_CTL2 0x22
#define Charger_CTL3 0x23
#define CHG_DIG_CTL0 0x24
#define REG_READ0 0x70
#define REG_READ1 0x71
#define REG_READ2 0x72
#define REG_UNDEF1 0x75
#define REG_READ3 0x77
#define REG_UNDEF2 0x78
#define BAT_VADC_DAT0 0xA2
#define BAT_VADC_DAT1 0xA3
#define BAT_IADC_DAT0 0xA4
#define BAT_IADC_DAT1 0xA5
/************************bit definitions***************************/
union {
struct {
uint8_t BUTTON_SHUTDOWN : 1;
uint8_t SET_BOOST_OUTPUT_ENABLE : 1;
uint8_t POWER_ON_LOAD : 1;
uint8_t RSVD : 1;
uint8_t CHARGER_ENABLE : 1;
uint8_t BOOST_ENABLE : 1;
uint8_t RSVD2 : 2;
} bits;
uint8_t reg_byte;
} reg_SYS_CTL0_t;
union {
struct {
uint8_t LOW_BATTERY_SHUTDOWN_ENABLE : 1;
uint8_t RSVD : 1;
uint8_t BOOST_AFTER_VIN : 1;
uint8_t RSVD2 : 2;
uint8_t SHORT_PRESS_BOOST_SWITCH_ENABLE : 1;
uint8_t FLASHLIGHT_CTRL_SIGNAL_SELECTION : 1;
uint8_t BOOST_CTRL_SIGNAL_SELECTION : 1;
} bits;
uint8_t reg_byte;
} reg_SYS_CTL1_t;
union {
struct {
uint8_t RSVD : 2;
uint8_t LIGHT_LOAD_SHUTDOWN_TIME : 2;
uint8_t LONG_PRESS_TIME : 1;
uint8_t RSVD2 : 3;
} bits;
uint8_t reg_byte;
} reg_SYS_CTL2_t;
#define SHUTDOWN_8s 0
#define SHUTDOWN_32s 1
#define SHUTDOWN_16s 2
#define SHUTDOWN_64s 3
union {
struct {
uint8_t CHARGING_FULL_STOP_VOLTAGE : 2;
uint8_t RSVD : 6;
} bits;
uint8_t reg_byte;
} reg_Charger_CTL0_t;
#define CUT_OFF_VOLTAGE_0 0 // 4.14/4.26/4.305/4.35 V
#define CUT_OFF_VOLTAGE_1 1 // 4.17/4.275/4.32/4.365 V
#define CUT_OFF_VOLTAGE_2 2 // 4.185/4.29/4.335/4.38 V
#define CUT_OFF_VOLTAGE_3 3 // 4.2/4.305/4.35/4.395 V
union {
struct {
uint8_t RSVD : 2;
uint8_t CHARGE_UNDER_VOLTAGE_LOOP : 3;
uint8_t RSVD2 : 1;
uint8_t END_CHARGE_CURRENT_DETECTION : 2;
} bits;
uint8_t reg_byte;
} reg_Charger_CTL1_t;
#define CURRENT_200 0
#define CURRENT_400 1
#define CURRENT_500 2
#define CURRENT_600 3
#define VOUT_0 0 //4.45
#define VOUT_1 1 //4.5
#define VOUT_2 2 //4.55
#define VOUT_3 3 //4.6
#define VOUT_4 4 //4.65
#define VOUT_5 5 //4.7
#define VOUT_6 6 //4.75
#define VOUT_7 7 //4.8
union {
struct {
uint8_t VOLTAGE_PRESSURE : 2;
uint8_t BATTERY_VOLTAGE : 2;
uint8_t RSVD : 4;
} bits;
uint8_t reg_byte;
} reg_Charger_CTL2_t;
#define BATT_VOLTAGE_3 3 //4.4
#define BATT_VOLTAGE_2 2 //4.35
#define BATT_VOLTAGE_1 1 //4.3
#define BATT_VOLTAGE_0 0 //4.2
#define Pressurized_42 3
#define Pressurized_28 2
#define Pressurized_14 1
#define Not_pressurized 0
union {
struct {
uint8_t RSVD : 5;
uint8_t CHARGE_CC_LOOP : 1;
uint8_t RSVD2 : 2;
} bits;
uint8_t reg_byte;
} reg_Charger_CTL3_t;
union {
struct {
uint8_t VIN_CURRENT : 5;
uint8_t RSVD : 3;
} bits;
uint8_t reg_byte;
} reg_CHG_DIG_CTL0_t;
union {
struct {
uint8_t RSVD : 3;
uint8_t CHARGE_ENABLE : 1;
uint8_t RSVD2 : 4;
} bits;
uint8_t reg_byte;
} reg_READ0_t;
union {
struct {
uint8_t RSVD : 3;
uint8_t BATTERY_STATUS : 1;
uint8_t RSVD2 : 4;
} bits;
uint8_t reg_byte;
} reg_READ1_t;
union {
struct {
uint8_t RSVD : 2;
uint8_t LOAD_LEVEL : 1;
uint8_t RSVD2 : 5;
} bits;
uint8_t reg_byte;
} reg_READ2_t;
union {
struct {
uint8_t SHORT_PRESS_DETECT : 1;
uint8_t LONG_PRESS_DETECT : 1;
uint8_t DOUBLE_PRESS_DETECT : 1;
uint8_t RSVD : 5;
} bits;
uint8_t reg_byte;
} reg_READ3_t;
class IP5306{
public:
/*@brief initialize i2c
@param sda and scl pin number
*/
IP5306(uint8_t sda_pin=21, uint8_t scl_pin=22) {
Wire.begin(sda_pin,scl_pin);
/*initialize register data*/
reg_SYS_CTL0_t.reg_byte = i2c_read(IP5306_ADDRESS,SYS_CTL0);
reg_SYS_CTL1_t.reg_byte = i2c_read(IP5306_ADDRESS,SYS_CTL1);
reg_SYS_CTL2_t.reg_byte = i2c_read(IP5306_ADDRESS,SYS_CTL2);
reg_Charger_CTL0_t.reg_byte = i2c_read(IP5306_ADDRESS,Charger_CTL0);
reg_Charger_CTL1_t.reg_byte = i2c_read(IP5306_ADDRESS,Charger_CTL1);
reg_Charger_CTL2_t.reg_byte = i2c_read(IP5306_ADDRESS,Charger_CTL2);
reg_Charger_CTL3_t.reg_byte = i2c_read(IP5306_ADDRESS,Charger_CTL3);
}
/*@brief write data to register
@param register address to write to,data to be written
and i2c address of the device
@retval None
*/
void i2c_write(uint8_t slave_address, uint8_t register_address, uint8_t data) {
Wire.beginTransmission(slave_address);
Wire.write(register_address);
Wire.write(data);
Wire.endTransmission();
}
/*@brief read the register value
@param register address to read,and i2c address of the device
@retval current register data
*/
uint8_t i2c_read(uint8_t slave_address, uint8_t register_address) {
uint8_t c = 0;
Wire.beginTransmission(slave_address);
Wire.write(register_address);
Wire.endTransmission();
Wire.requestFrom(slave_address, 1);
while (Wire.available()) { // slave may send less than requested
c = Wire.read(); // receive a byte as character
}
return c;
}
/*@brief select boost mode
@param enable or disable bit
@retval None
@note Default value - enable
*/
void boost_mode(uint8_t boost_en) {
reg_SYS_CTL0_t.bits.BOOST_ENABLE = boost_en;
i2c_write(IP5306_ADDRESS,SYS_CTL0,reg_SYS_CTL0_t.reg_byte);
}
/*@brief select charger mode
@param enable or disable bit
@retval None
@note Default value - enable
*/
void charger_mode(uint8_t charger_en) {
reg_SYS_CTL0_t.bits.CHARGER_ENABLE = charger_en;
i2c_write(IP5306_ADDRESS,SYS_CTL0,reg_SYS_CTL0_t.reg_byte);
}
/*@brief select auto power on once load detected
@param enable or disable bit
@retval None
@note Default value - enable
*/
void power_on_load(uint8_t power_on_en) {
reg_SYS_CTL0_t.bits.POWER_ON_LOAD = power_on_en;
i2c_write(IP5306_ADDRESS,SYS_CTL0,reg_SYS_CTL0_t.reg_byte);
}
/*@brief boost o/p normally open function
@param enable or disable bit
@retval None
@note Default value - enable
*/
void boost_output(uint8_t output_val) {
reg_SYS_CTL0_t.bits.SET_BOOST_OUTPUT_ENABLE = output_val;
i2c_write(IP5306_ADDRESS,SYS_CTL0,reg_SYS_CTL0_t.reg_byte);
}
/*@brief eneter shutdown mode using button
@param enable or disable bit
@retval None
@note Default value - disable
*/
void button_shutdown(uint8_t shutdown_val) {
reg_SYS_CTL0_t.bits.BUTTON_SHUTDOWN = shutdown_val;
i2c_write(IP5306_ADDRESS,SYS_CTL0,reg_SYS_CTL0_t.reg_byte);
}
/*@brief boost control mode using button
@param enable or disable bit
@retval None
@note Default value - disable
*/
void boost_ctrl_signal(uint8_t press_val) {
reg_SYS_CTL1_t.bits.BOOST_CTRL_SIGNAL_SELECTION = press_val;
i2c_write(IP5306_ADDRESS,SYS_CTL1,reg_SYS_CTL1_t.reg_byte);
}
/*@brief flashlight control mode using button
@param enable or disable bit
@retval None
@note Default value - disable
*/
void flashlight_ctrl_signal(uint8_t press_val) {
reg_SYS_CTL1_t.bits.FLASHLIGHT_CTRL_SIGNAL_SELECTION = press_val;
i2c_write(IP5306_ADDRESS,SYS_CTL1,reg_SYS_CTL1_t.reg_byte);
}
/*@brief
@param enable or disable bit
@retval None
@note Default value - disable
*/
void short_press_boost(uint8_t boost_en) {
reg_SYS_CTL1_t.bits.SHORT_PRESS_BOOST_SWITCH_ENABLE = boost_en;
i2c_write(IP5306_ADDRESS,SYS_CTL1,reg_SYS_CTL1_t.reg_byte);
}
/*@brief keep boost mode on after input supply removal
@param enable or disable bit
@retval None
@note Default value - enable
*/
void boost_after_vin(uint8_t val) {
reg_SYS_CTL1_t.bits.BOOST_AFTER_VIN = val;
i2c_write(IP5306_ADDRESS,SYS_CTL1,reg_SYS_CTL1_t.reg_byte);
}
/*@brief shutdown if battery voltage raches 3V
@param enable or disable bit
@retval None
@note Default value - enable
*/
void low_battery_shutdown(uint8_t shutdown_en) {
reg_SYS_CTL1_t.bits.LOW_BATTERY_SHUTDOWN_ENABLE = shutdown_en;
i2c_write(IP5306_ADDRESS,SYS_CTL1,reg_SYS_CTL1_t.reg_byte);
}
/*@brief set long press timing
@param long press time value - 0 for 2s , 1 for 3s
@retval None
@note Default value - disable
*/
void set_long_press_time(uint8_t press_time_val) {
reg_SYS_CTL2_t.bits.LONG_PRESS_TIME = press_time_val;
i2c_write(IP5306_ADDRESS,SYS_CTL2,reg_SYS_CTL2_t.reg_byte);
}
/*@brief set light load shutdown timing
@param shutdown time value - 0 for 8s, 1 for 32s, 2 for 16s and 3 for 64s
@retval None
@note Default value - disable
*/
void set_light_load_shutdown_time(uint8_t shutdown_time) {
reg_SYS_CTL2_t.bits.LIGHT_LOAD_SHUTDOWN_TIME = shutdown_time;
i2c_write(IP5306_ADDRESS,SYS_CTL2,reg_SYS_CTL2_t.reg_byte);
}
/*@brief set charging cutoff voltage range for battery
@param voltage range value - 0 for 4.14/4.26/4.305/4.35 V
1 for 4.17/4.275/4.32/4.365 V
2 for 4.185/4.29/4.335/4.38 V
3 for 4.2/4.305/4.35/4.395 V
@retval None
@note Default value - 2
*/
void set_charging_stop_voltage(uint8_t voltage_val) {
reg_Charger_CTL0_t.bits.CHARGING_FULL_STOP_VOLTAGE = voltage_val;
i2c_write(IP5306_ADDRESS,Charger_CTL0,reg_Charger_CTL0_t.reg_byte);
}
/*@brief set charging complete current detection
@param current value - 3 : 600mA
2 : 500mA
1 : 400mA
0 : 200mA
@retval None
@note Default value - 1
*/
void end_charge_current(uint8_t current_val) {
reg_Charger_CTL1_t.bits.END_CHARGE_CURRENT_DETECTION = current_val;
i2c_write(IP5306_ADDRESS,Charger_CTL1,reg_Charger_CTL1_t.reg_byte);
}
/*@brief set voltage Vout for charging
@param voltage value - 111:4.8
110:4.75
101:4.7
100:4.65
011:4.6
010:4.55
001:4.5
000:4.45
@retval None
@note Default value - 101
*/
void charger_under_voltage(uint8_t voltage_val) {
reg_Charger_CTL1_t.bits.CHARGE_UNDER_VOLTAGE_LOOP = voltage_val;
i2c_write(IP5306_ADDRESS,Charger_CTL1,reg_Charger_CTL1_t.reg_byte);
}
/*@brief set battery voltage
@param voltage value - 00:4.2
01:4.3
02:4.35
03:4.4
@retval None
@note Default value - 00
*/
void set_battery_voltage(uint8_t voltage_val) {
reg_Charger_CTL2_t.bits.BATTERY_VOLTAGE = voltage_val;
i2c_write(IP5306_ADDRESS,Charger_CTL2,reg_Charger_CTL2_t.reg_byte);
}
/*@brief set constant voltage charging setting
@param voltage value - 11: Pressurized 42mV
10: Pressurized 28mV
01: Pressurized 14mV
00: Not pressurized
@retval None
@note Default value - 01
@note :4.30V/4.35V/4.4V It is recommended to pressurize 14mV
4.2V It is recommended to pressurize 28mV
*/
void set_voltage_pressure(uint8_t voltage_val) {
reg_Charger_CTL2_t.bits.VOLTAGE_PRESSURE = voltage_val;
i2c_write(IP5306_ADDRESS,Charger_CTL2,reg_Charger_CTL2_t.reg_byte);
}
/*@brief set constant current charging setting
@param value - 1:VIN end CC Constant current
0:BAT end CC Constant current
@retval None
@note Default value - 1
*/
void set_cc_loop(uint8_t current_val) {
reg_Charger_CTL3_t.bits.CHARGE_CC_LOOP = current_val;
i2c_write(IP5306_ADDRESS,Charger_CTL3,reg_Charger_CTL3_t.reg_byte);
}
/*@brief get current charging status
@param None
@retval integer representation of charging status(1 - charging is on and 0 if charging is off)
*/
uint8_t check_charging_status(void) {
reg_READ0_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ0);
return reg_READ0_t.bits.CHARGE_ENABLE;
}
/*@brief get current battery status
@param None
@retval integer representation of battery status(1 - fully charged and 0 if still charging)
*/
uint8_t check_battery_status(void) {
reg_READ1_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ1);
return reg_READ1_t.bits.BATTERY_STATUS;
}
/*@brief get output load level
@param None
@retval integer representation of load status(1 - light load and 0 if heavy load)
*/
uint8_t check_load_level(void) {
reg_READ2_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ2);
return reg_READ2_t.bits.LOAD_LEVEL;
}
/*@brief detect if a short press occurred
@param None
@retval button status
@note clear this bit after reading by writing 1
*/
uint8_t short_press_detect(void) {
reg_READ3_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ3);
return reg_READ3_t.bits.SHORT_PRESS_DETECT;
}
/*@brief detect if a long press occurred
@param None
@retval button status
@note clear this bit after reading by writing 1
*/
uint8_t long_press_detect(void) {
reg_READ3_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ3);
return reg_READ3_t.bits.LONG_PRESS_DETECT;
}
/*@brief detect if a double press occurred
@param None
@retval button status
@note clear this bit after reading by writing 1
*/
uint8_t double_press_detect(void) {
reg_READ3_t.reg_byte = i2c_read(IP5306_ADDRESS,REG_READ3);
return reg_READ3_t.bits.DOUBLE_PRESS_DETECT;
}
uint8_t get_battery_charge(void) {
uint8_t charge = i2c_read(IP5306_ADDRESS,REG_UNDEF2);
switch (charge & 0xF0) {
case 0xF0:
return 0;
case 0xE0:
return 25;
case 0xC0:
return 50;
case 0x80:
return 75;
case 0x00:
return 100;
default:
return charge & 0xF0;
}
}
double get_battery_voltage(void) {
uint8_t low = i2c_read(IP5306_ADDRESS,BAT_VADC_DAT0);
uint8_t high = i2c_read(IP5306_ADDRESS,BAT_VADC_DAT1);
double vadc;
if ((high & 0x20) == 0x20) {
vadc = 2600 - ((~low)+(~(high & 0x1F)) * 256 + 1) * 0.26855;
} else {
vadc = 2600 + (low+high * 256) * 0.26855;
}
return vadc;
}
double get_battery_current(void) {
uint8_t low = i2c_read(IP5306_ADDRESS,BAT_IADC_DAT0);
uint8_t high = i2c_read(IP5306_ADDRESS,BAT_IADC_DAT1);
double iadc;
if ((high & 0x20) == 0x20) {
char a = ~low;
char b = (~(high & 0x1F) & 0x1F);
int c = b * 256 + a + 1;
iadc = c * 0.745985;
} else {
iadc = (high * 256 + low) * 0.745985;
}
return iadc;
}
};
#endif

View File

@ -0,0 +1,11 @@
# NetCube Systems Universal Light Controller - Battery Management Usermod
This usermod will read status from an IP5306-I2C battery management soc, and monitor the battery voltage via and voltage divider.
## Installation
Copy the example `platformio_override.ini` to the root directory. This file should be placed in the same directory as `platformio.ini`.
### Define Your Options
* `USERMOD_ULC_BATTERYMANAGEMENT` - define this to have this user mod included wled00\usermods_list.cpp

View File

@ -0,0 +1,279 @@
#pragma once
#include "wled.h"
#include "IP5306_I2C.h"
#include <driver/adc.h>
// the frequency to check battery voltage, 5 seconds
#ifndef USERMOD_BATTERY_MEASUREMENT_INTERVAL
#define USERMOD_BATTERY_MEASUREMENT_INTERVAL 5000
#endif
#ifndef USERMOD_BATTERY_ADC_PRECISION
#define USERMOD_BATTERY_ADC_PRECISION 4095.0f
#endif
#ifndef USERMOD_BATTERY_ADC_REF
#define USERMOD_BATTERY_ADC_REF 3.3f
#endif
#ifndef USERMOD_BATTERY_ADC_R1
#define USERMOD_BATTERY_ADC_R1 8.0f
#endif
#ifndef USERMOD_BATTERY_ADC_R2
#define USERMOD_BATTERY_ADC_R2 2.5f
#endif
#ifndef USERMOD_BATTERY_MIN_VOLTAGE
#define USERMOD_BATTERY_MIN_VOLTAGE 2.6f
#endif
#ifndef USERMOD_BATTERY_MAX_VOLTAGE
#define USERMOD_BATTERY_MAX_VOLTAGE 4.2f
#endif
#ifndef USERMOD_IP5306_SDA
#define USERMOD_IP5306_SDA 32
#endif
#ifndef USERMOD_IP5306_SCL
#define USERMOD_IP5306_SCL 33
#endif
#ifndef USERMOD_ABL_BATTERY
#define USERMOD_ABL_BATTERY 1250
#endif
#ifndef USERMOD_ABL_EXT
#define USERMOD_ABL_EXT 2500
#endif
class UsermodULCBatteryManagement : public Usermod {
private:
IP5306 *ip5306 = nullptr;
bool initDone = false;
bool initializing = true;
// GPIO pin used for battery measurment (with a default compile-time fallback)
int8_t ip5306_sda = USERMOD_IP5306_SDA;
int8_t ip5306_scl = USERMOD_IP5306_SCL;
// how often do we measure?
unsigned long readingInterval = USERMOD_BATTERY_MEASUREMENT_INTERVAL;
unsigned long nextReadTime = 0;
unsigned long lastReadTime = 0;
uint32_t ablBattery = USERMOD_ABL_BATTERY;
uint32_t ablExt = USERMOD_ABL_EXT;
// battery min. voltage
float minBatteryVoltage = USERMOD_BATTERY_MIN_VOLTAGE;
// battery max. voltage
float maxBatteryVoltage = USERMOD_BATTERY_MAX_VOLTAGE;
uint8_t batteryLevel = 0;
bool isCharging = false;
bool isPluggedIn = false;
// strings to reduce flash memory usage (used more than twice)
static const char _name[];
static const char _readInterval[];
static const char _maxCurrentBattery[];
static const char _maxCurrentExt[];
// custom map function
// https://forum.arduino.cc/t/floating-point-using-map-function/348113/2
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float truncate(float val, byte dec)
{
float x = val * pow(10, dec);
float y = round(x);
float z = x - y;
if ((int)z == 5)
{
y++;
}
x = y / pow(10, dec);
return x;
}
public:
void setup() {
DEBUG_PRINTLN(F("Allocating ip5306 pins..."));
PinManagerPinType pins[2] = { { ip5306_sda, true }, { ip5306_scl, true } };
if (pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C))
{
ip5306 = new IP5306(ip5306_sda, ip5306_scl);
DEBUG_PRINTLN(F("IP5306 allocation succeeded."));
} else {
if (ip5306_sda >= 0 && ip5306_scl >= 0) DEBUG_PRINTLN(F("IP5306 allocation failed."));
ip5306_sda = -1; // allocation failed
ip5306_scl = -1;
}
nextReadTime = millis() + 5000;
lastReadTime = millis();
if (ip5306 != nullptr) {
//set battery voltage
ip5306->set_battery_voltage(BATT_VOLTAGE_0); //4.2V
//Voltage Vout for charging
ip5306->charger_under_voltage(VOUT_5); //4.7V
//set charging complete current
ip5306->end_charge_current(CURRENT_200); //400mA
//set cutoff voltage
ip5306->set_charging_stop_voltage(CUT_OFF_VOLTAGE_3); // 4.2/4.305/4.35/4.395 V
//enable low battery shutdown mode
ip5306->low_battery_shutdown(DISABLE);
//allow boost even after removing Vin
ip5306->boost_after_vin(ENABLE);
//allow auto power on after load detection
ip5306->power_on_load(DISABLE);
//enable boost mode
ip5306->boost_mode(ENABLE);
}
initDone = true;
}
void loop() {
if(strip.isUpdating()) return;
// check the battery level every USERMOD_BATTERY_MEASUREMENT_INTERVAL (ms)
if (millis() < nextReadTime) return;
nextReadTime = millis() + readingInterval;
lastReadTime = millis();
initializing = false;
if (ip5306 != nullptr) {
isPluggedIn = ip5306->check_charging_status();
isCharging = !ip5306->check_battery_status();
batteryLevel = ip5306->get_battery_charge();
if (isPluggedIn) {
strip.ablMilliampsMax = ablExt;
} else {
strip.ablMilliampsMax = ablBattery;
}
}
}
/*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor
*/
void addToJsonInfo(JsonObject& root) {
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
// info modal display names
JsonArray batteryPercentage = user.createNestedArray("Battery Level");
JsonArray batteryState = user.createNestedArray("Battery State");
if (initializing) {
batteryPercentage.add((nextReadTime - millis()) / 1000);
batteryPercentage.add(" sec");
//batteryVoltage.add((nextReadTime - millis()) / 1000);
//batteryVoltage.add(" sec");
batteryState.add((nextReadTime - millis()) / 1000);
batteryState.add(" sec");
return;
}
if(batteryLevel < 0) {
batteryPercentage.add(F("invalid"));
} else {
batteryPercentage.add(batteryLevel);
}
batteryPercentage.add(F(" %"));
if (isPluggedIn && isCharging) {
batteryState.add("Charging");
} else if (isPluggedIn && !isCharging) {
batteryState.add("Charged");
} else if (!isPluggedIn) {
batteryState.add("Discharging");
}
}
/**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
*/
//void addToJsonState(JsonObject &root)
//{
//}
/**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients
* Read "<usermodname>_<usermodparam>" from json state and and change settings (i.e. GPIO pin) used.
*/
//void readFromJsonState(JsonObject &root) {
// if (!initDone) return; // prevent crash on boot applyPreset()
//}
/**
* addToConfig() (called from set.cpp) stores persistent properties to cfg.json
*/
void addToConfig(JsonObject &root) {
JsonObject battery = root.createNestedObject(FPSTR(_name)); // usermodname
//battery["minBatteryVoltage"] = minBatteryVoltage; // usermodparam
//battery["maxBatteryVoltage"] = maxBatteryVoltage; // usermodparam
battery[FPSTR(_readInterval)] = readingInterval;
battery[FPSTR(_maxCurrentBattery)] = ablBattery;
battery[FPSTR(_maxCurrentExt)] = ablExt;
DEBUG_PRINTLN(F("Battery config saved."));
}
/**
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*
* The function should return true if configuration was successfully loaded or false if there was no configuration.
*/
bool readFromConfig(JsonObject &root) {
JsonObject battery = root[FPSTR(_name)];
if (battery.isNull())
{
DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false;
}
readingInterval = battery[FPSTR(_readInterval)] | readingInterval;
readingInterval = max(3000, (int)readingInterval); // minimum repetition is >5000ms (5s)
ablBattery = battery[FPSTR(_maxCurrentBattery)] | ablBattery;
ablExt = battery[FPSTR(_maxCurrentExt)] | ablExt;
DEBUG_PRINT(FPSTR(_name));
return !battery[FPSTR(_readInterval)].isNull();
}
uint16_t getId()
{
return USERMOD_ID_ULC_BATTERYMANAGEMENT;
}
};
// strings to reduce flash memory usage (used more than twice)
const char UsermodULCBatteryManagement::_name[] PROGMEM = "Battery-level";
const char UsermodULCBatteryManagement::_readInterval[] PROGMEM = "Battery status update interval";
const char UsermodULCBatteryManagement::_maxCurrentBattery[] PROGMEM = "Max current when on battery";
const char UsermodULCBatteryManagement::_maxCurrentExt[] PROGMEM = "Max current when on charger";

View File

@ -0,0 +1,31 @@
#include "wled.h"
/*
* Register your v2 usermods here!
*/
/*
* Add/uncomment your usermod filename here (and once more below)
* || || ||
* \/ \/ \/
*/
//#include "usermod_v2_example.h"
#if (defined(USERMOD_ULC_BATTERYMANAGEMENT) && defined(ARDUINO_ARCH_ESP32))
#include "../usermods/ULC_Battery_Management/usermod_ulc_batterymanagement.h"
#endif
//#include "usermod_v2_empty.h"
void registerUsermods()
{
/*
* Add your usermod class name here
* || || ||
* \/ \/ \/
*/
//usermods.add(new MyExampleUsermod());
#if (defined(USERMOD_ULC_BATTERYMANAGEMENT) && defined(ARDUINO_ARCH_ESP32))
usermods.add(new UsermodULCBatteryManagement());
#endif
//usermods.add(new UsermodRenameMe());
}

View File

@ -133,6 +133,7 @@
#define USERMOD_ID_PWM_OUTPUTS 38 //Usermod "usermod_pwm_outputs.h
#define USERMOD_ID_SHT 39 //Usermod "usermod_sht.h
#define USERMOD_ID_KLIPPER 40 // Usermod Klipper percentage
#define USERMOD_ID_ULC_BATTERYMANAGEMENT 41 //Usermod "usermod_ulc_batterymanagement.h"
//Access point behavior
#define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot

View File

@ -189,6 +189,13 @@
#include "../usermods/pwm_outputs/usermod_pwm_outputs.h"
#endif
#ifdef USERMOD_SHT
#include "../usermods/sht/usermod_sht.h"
#endif
#if (defined(USERMOD_ULC_BATTERYMANAGEMENT) && defined(ARDUINO_ARCH_ESP32))
#include "../usermods/ULC_Battery_Management/usermod_ulc_batterymanagement.h"
#endif
void registerUsermods()
{
@ -357,4 +364,8 @@ void registerUsermods()
#ifdef USERMOD_SHT
usermods.add(new ShtUsermod());
#endif
#if (defined(USERMOD_ULC_BATTERYMANAGEMENT) && defined(ARDUINO_ARCH_ESP32))
usermods.add(new UsermodULCBatteryManagement());
#endif
}