TKK_E32230469/firmware_iot_esp12f/upsIO.h

1339 lines
48 KiB
C

//UPS CORE =========================================================================================================
//handle: - Button, state (short hold, long hold)
// - Keep Power State
// - ADC (voltage, battery level)
// - Input OK Threshlod Sense
// - State for: power bad (blinks -- low_output <=> as_input), power ok (led on), charge disable (led off)
//todo:
// - misc./cosmetic logic: when input line lost and charging error happens (ERROR NO BATTERY) => stop charge then reset error mode state, since it may switch to ERROR SHORTED. Can be ignored since will not happen in real condition, only happened when use voltage source from programmer. (OK)
// - charging state bugs: after power line lost, battery bar blinking and charge control pinout state is LOW, while webserver just normal.
// - charge error no longer blinking the LED in charge control pinout, since OLED are now used (OK)
// - stop charge action at least 2-3 seconds at startup to detect battery (OK)
// - Charge Error no longer blink the charigin led indicator, since now using OLED (OK)
// - Using BMS may cause capacitive reading, thus battery detection innaccurate at startup, need retweak (OK, solved by charge delay at startup)
// - directive to invert input under voltage detection should delayed 1 seconds after keep missing (since switching can cause voltage drops/spikes) - OK
// - known bugs: when no input line and power going off, charging indicator and charging control indicator may still blink or active rather than turned off (OK)
//known schematic weakness:
// - Positive side control for output power control caused brief activation states when line powered for first time (seems caused by MOSFET effect when difference voltage spiked, since keep powering esp from programmer while trigger input line give same effect).
//Workaround: Use XL4005 EN pinout control connected to GPIO5 with diode (1N4148) and resistor if necessary
#include "upsOled.h"
#include "battLib.h"
#ifndef UPSIO_H
#define UPSIO_H
#endif
#define HOLD_POWER_TICK 4000000UL ///minimum power hold since startup in nanoseconds
//pinouts:
#define KEEP_POWER_PIN 15 //keep power pinout
//minimum voltage sense pinout
#define MINIMUM_INPUT_PIN 4 //Vin 16V sense (15.6V with 5K6 resistor)
//button pinout
#define BUTTON_INPUT_PIN 16 //button sense
//charge stop or limiter pinout
#define CHARGE_STOP_PIN 2 //same as LED_BUILTIN
//power output activation pinout
#define POWER_OUTPUT_PIN 5 //power out activation
static uint32_t tickStep = 1;
static uint32_t tickSinceStarted = 0;
static uint32_t tickKeepPower = 0;
static bool keepPower = false;
static bool keepPowerState = false;
//for timer routines:
static volatile uint32_t ioAdcTick = 0;
static volatile uint8_t lowRateTick = 0;
static volatile bool lowRateRoutine = false;
#define ADC_CAP_BUFFER_NUM 32
static volatile uint16_t adcCap[ADC_CAP_BUFFER_NUM];
static volatile uint8_t adcCapCount = 0;
static volatile uint8_t adcIdx = 0;
static volatile uint16_t ioAdc = 0;
static volatile uint16_t ioAdcPrev = 0;
static volatile uint16_t ioAdcFiltered = 0;
static volatile uint16_t adcValueMin = 0;
//temps
static volatile uint8_t ioLoop = 0;
static volatile uint16_t adcTmp = 0;
static volatile uint8_t adcIdx2 = 0;
static volatile uint8_t adcSubTick = 0;
static volatile uint8_t adcSkipCounter = 0;
//for battery level calculation
static volatile float adcVoltageBase = 0.0; //base voltage (for addition)
static volatile float adcMaxVoltageCapability = 16.0; //max voltage of ADC can read
static volatile float batteryVoltageUnfiltered = 0;
static volatile float batteryVoltage = 0;
static volatile float batteryLevel = 0;
static volatile uint8_t batteryPercentage = 0;
static volatile float batteryVoltageEnableOn = battVoltageCritical;
//charge stop mode
#define CHARGE_ACTIVE_LIGHT_OFF 0 //pinout as input (may have effect charge voltage slightly below set since red led is dimmed)
#define CHARGE_ACTIVE_LIGHT_ON 1 //pinout as output, low state
#define CHARGE_STOP_LIGHT_OFF 2 //pinout as output, high state
static volatile uint8_t chargeMode = CHARGE_ACTIVE_LIGHT_OFF;
static volatile bool isChargeActive = false;
#define CHARGE_ERROR_NONE 0
#define CHARGE_ERROR_NO_BATTERY 1 //also partial cell shorted, or battery not detected
#define CHARGE_ERROR_SHORTED 2 //charger output or battery shorted
#define CHARGE_ERROR_OVERVOLTAGE 4 //or also charger error / cannot be stopped
static volatile uint8_t chargeError = CHARGE_ERROR_NONE;
static volatile uint8_t chargeErrorPrev = CHARGE_ERROR_NONE;
static volatile uint8_t chargeManagementMode = 0;
static volatile bool chargeVoltageOK = false;//used for charge logic when charge voltage sense is enough
static volatile bool powerOutputActive = false;
static volatile bool inputUnderVoltage = false; //input under voltage, or there is no input voltage at all stay for few seconds
static volatile bool inputVoltageKeepup = false;//input voltage ok, status stay for inputVoltageKeepupDuration in nanoseconds
static volatile uint32_t inputVoltageKeepupDuration = 500000UL;
static volatile uint32_t underVoltageTick = 0;
static volatile uint32_t inputVoltageKeepupTick = 0;
static volatile bool underVoltageIntterupted = false;
static volatile bool inputVoltageOK = false;//used for charge logic when input voltage sense is high
static volatile bool inputVoltageOKPrev = false;
static volatile bool startupReady = false; //delay 1 seconds from startup for
static volatile bool inputVoltageReady = false; //delay 1 seconds when input voltage resume
static volatile uint32_t inputVoltageResumeTick = 0;
//for battery state check routine
#define SHORT_VOLTAGE 2.5
static volatile uint32_t batteryCheckTick = 0;
static volatile uint32_t batteryCheckSkipTick = 0;
static volatile uint32_t chargeBlinkTick = 0;
static volatile uint32_t chargeStartedTick = 0;
static volatile bool blinkChargeError = false;
//for input button pattern
static volatile bool buttonIgnore = false; //ignore button flag
static volatile bool buttonState = false;
static volatile bool buttonStatePrev = false;
static volatile bool buttonShortPress = false; //1 sec to power ON or OFF
static volatile bool buttonLongPress = false; //8 sec while powered to reset configuration (wifi setup)
static volatile bool buttonShortPressIgnore = false;
static volatile bool buttonLongPressIgnore = false;
static volatile uint32_t buttonTick = 0;
static volatile uint32_t buttonTickIgnore = 0;
//for indicators
static volatile uint8_t flashAllBatteryLevel = 0;
static volatile bool indicatorFastBlink = false; //fast shortcut for fast blink (each 100ms)
static volatile bool indicatorMediumBlink = false;
static volatile bool indicatorSlowBlink = false;
static volatile uint32_t indicatorBlinkTick = 0;
static volatile uint8_t indicatorBlinkIndex = 0; //cycled per 10 count for 1 sec
static volatile uint32_t indicatorBlinkTickB = 0; //for fast blink
//wifi indicator
#define NET_STATE_OFF 0
#define NET_STATE_CONNECTING 1
#define NET_STATE_CONNECTED 2
static volatile uint8_t indicatorNetworkState = 0;
static volatile uint8_t indicatorNetworkSignalLevel = 0;
static volatile uint8_t indicatorNetworkBlinkerTX = 0;
static volatile uint8_t indicatorNetworkBlinkerRX = 0;
static volatile bool displayNetworkFlipFlag = false;
//battery indicator
static volatile uint8_t battIdx = 0;
//charging mode:
//#define CHARGE_MODE_DEFAULT 0 //charges to full then stop, resumed when capacity lower than 85%
//#define CHARGE_MODE_ALWAYS 1 //keep battery charging all the time
//#define CHARGE_MODE_TOPPED 2 //charges to full then stop, resumed when voltage lower than full rest
//#define CHARGE_MODE_TOPPED_CUSTOM 3 //charges to full then stop, resumed when capacity lower than n-percentage
//#define CHARGE_MODE_RANGED 4 //charges to a-percentage, resumed when capacity lower than b-percentage
static volatile bool chargeAfterInputLost = false;//force charge resume activation when input is resumed
static volatile bool fullChargeAfterInputLost = true; //force full charge after input lost (chargeAfterInputLost should be set to true)
static volatile uint8_t triggerFullChargeAfterInputLostAtPercentage = 90;//capacity left before full charge triggered (fullChargeAfterInputLost should be true)
static volatile bool chargeToFull = true; //will charge to full before stop, if false => wull use chargePercentageForStop
static volatile uint8_t chargePercentageForStop = 100;
static volatile uint8_t chargePercentageForResume = 85;
static volatile bool chargeAlwaysActive = false; //use this to charge continuosly
static volatile bool chargeOvervoltageProtection = true; //activate over voltage protection
static volatile bool chargeReduceAtBatteryFull = true; //try to reduce charge voltage by hardware when reach minimum fullest
static volatile bool chargeInputLost = true; //charge lost flags
static volatile uint32_t chargeWaitBeforeStop = 120000000UL; //in nanoseconds (120000000 = 2 minutes)
static volatile uint32_t chargeWaitStopTick = 0;
static volatile bool needFullChargeOnce = false;
//critical shutdown
static volatile uint32_t criticalDelayShutdown = 120000000UL; //in nanoseconds (120000000 = 2 minutes)
static volatile uint32_t persistentDelayShutdown = 120000000UL; //in nanoseconds (120000000 = 2 minutes)
static volatile uint32_t criticalShutdownTick = 0;
static volatile uint32_t persistentShutdownTick = 0;
static volatile bool persistentShutdown = false;
static volatile bool criticalShutdown = false;
//automatic power resume
static volatile bool autoPowerOn = false; //power on when input resumed and in stable powered?
static volatile bool forceAutoPowerAtBatteryError = true; //force power on, even no battery exist or shorted (charge system error)
static volatile uint32_t autoPowerOnDelay = 4000000UL; //default is 4 seconds
static volatile uint32_t autoPowerOnTick = 0;
static volatile bool ongoingToPowerOn = false; //var flag
static volatile bool powerEverLoss = true;
//static volatile bool interruptEnable = false;
//when not using interrupt (that caused conflict with asyncwerbserver)
static volatile uint32_t currentNs = 0;
static volatile uint32_t lastNs = 0;
//static volatile bool batterySimpleIndicator = false;
static volatile bool batteryBlinkingIndicatorState = false; //blinking shortcut (either on charge active or critical)
static volatile bool batteryLastStateCritical = false;
static volatile uint32_t displayRefreshDelay = 100000; //10 fps
static volatile uint32_t displayTick = 0;
static volatile bool onGoingDisplayOff = false;
static volatile uint32_t turnOffDisplayAfter = 0;
static volatile uint32_t turnOffDisplayTick = 0;
static volatile bool displayActive = false;
static volatile bool displayFlipFlag = false;
static volatile bool splashEverDisplayed = false;
static volatile bool displaySplashNow = false;
static volatile uint32_t splashDisplayTick = 0;
static volatile bool chargeAllowed = false;
static volatile uint32_t chargeAllowedTick = 0;
static volatile uint32_t chargeAllowedDelay = 3000000;
bool isUpsBatteryCritical(){
//staged
if (batteryLastStateCritical){
if ((batteryVoltage>battVoltageCritical) && (batteryLevel>0) && (batteryPercentage>12)){
batteryLastStateCritical = false;
}
}else{
if ((batteryVoltage<battVoltageCritical) || (batteryLevel<=0) || (batteryPercentage<=10)){
batteryLastStateCritical = true;
}
}
return batteryLastStateCritical || criticalShutdown;
}
void resetOnGoingFullChargeAfterLineLost(){
if (needFullChargeOnce) needFullChargeOnce = false;
}
static void adcClear() {
adcCapCount = 0;
adcIdx = 0;
ioAdc = 0;
adcValueMin = 0;
}
static void adcReadFiltered() {
//minimum sub samplings ================================
if (adcSubTick == 0) {
ioAdc = analogRead(A0);
} else {
adcTmp = analogRead(A0);
if (adcTmp < ioAdc) ioAdc = adcTmp;
}
adcSubTick++;
if (adcSubTick > 60) { //1:60 sps filter
adcSubTick = 0;
ioAdcFiltered = ioAdc;
} else {
if (adcSkipCounter < 60) {
//feed first 60 cycles
ioAdcFiltered = ioAdc;
adcSkipCounter++;
} else {
return; //skips
}
}
//=====================================================
//main minimum sub samplings (long filter, 1:32 sps):
//if have 1:8 changes jumps
if (ioAdc > ioAdcPrev) {
if ((ioAdc - ioAdcPrev) > 128) {
adcCapCount = 0;
adcIdx = 0;
adcValueMin = ioAdc;
}
} else {
if ((ioAdcPrev - ioAdc) > 128) {
adcCapCount = 0;
adcIdx = 0;
adcValueMin = ioAdc;
}
}
ioAdcPrev = ioAdc;
batteryVoltageUnfiltered = adcVoltageBase + (adcMaxVoltageCapability * ((float)ioAdc / 1024));
adcCap[adcIdx] = ioAdc;
adcCapCount++;
if (adcCapCount > ADC_CAP_BUFFER_NUM) adcCapCount = ADC_CAP_BUFFER_NUM;
if (adcCapCount == 1) {
adcValueMin = ioAdc;
} else if (adcCapCount < ADC_CAP_BUFFER_NUM) {
if (ioAdc < adcValueMin) adcValueMin = ioAdc;
} else {
adcTmp = ioAdc;
if (adcIdx == 0) {
adcIdx2 = ADC_CAP_BUFFER_NUM - 1;
} else {
adcIdx2 = adcIdx - 1;
}
for (ioLoop = 1; ioLoop < ADC_CAP_BUFFER_NUM; ioLoop++) {
if (adcCap[adcIdx2] < adcTmp) adcTmp = adcCap[adcIdx2];
if (adcIdx2 == 0) {
adcIdx2 = ADC_CAP_BUFFER_NUM - 1;
} else {
adcIdx2--;
}
}
adcValueMin = adcTmp;
//batteryLevel = getBatteryLevel();
}
adcIdx++;
if (adcIdx >= ADC_CAP_BUFFER_NUM) adcIdx = 0;
batteryVoltage = adcVoltageBase + (adcMaxVoltageCapability * ((float)adcValueMin / 1024));
batteryPercentage = getBatteryPercentage(batteryVoltage);
batteryLevel = getBatteryLevel();
}
void setDisplayForTurnOff(uint32_t tDelay){
onGoingDisplayOff = true;
turnOffDisplayTick = tickSinceStarted;
turnOffDisplayAfter = tDelay;
}
void turnOnDisplay(){
onGoingDisplayOff = false;
displayActive = true;
}
void turnOffDisplay(){
onGoingDisplayOff = false;
displayActive = false;
}
static void setChargeMode(uint8_t chgMode) {
if (chgMode == CHARGE_STOP_LIGHT_OFF) {
pinMode(CHARGE_STOP_PIN, OUTPUT);
digitalWrite(CHARGE_STOP_PIN, HIGH);
chargeMode = chgMode;
isChargeActive = false;
needFullChargeOnce = false;
} else if (chgMode == CHARGE_ACTIVE_LIGHT_ON) {
if (chargeAllowed){
pinMode(CHARGE_STOP_PIN, OUTPUT);
digitalWrite(CHARGE_STOP_PIN, LOW);
chargeMode = chgMode;
isChargeActive = true;
}
} else if (chgMode == CHARGE_ACTIVE_LIGHT_OFF) {
if (chargeAllowed){
pinMode(CHARGE_STOP_PIN, INPUT);
//digitalWrite(CHARGE_STOP_PIN, LOW);
chargeMode = chgMode;
if (!isChargeActive){
//reset waiting tick each time charge activated
if (chargeWaitStopTick>0) chargeWaitStopTick = 0;
}
isChargeActive = true;
}
}
}
uint8_t getChargingState(){
//0 = false, 1 = reduced, 2 = normal charge
if (inputVoltageKeepup){
if (chargeMode==CHARGE_ACTIVE_LIGHT_OFF){
return 1;
}else if (chargeMode==CHARGE_ACTIVE_LIGHT_ON){
return 2;
}
}
return 0;
}
static void setKeepPower(bool state) {
keepPower = state;
if (keepPowerState != keepPower) {
keepPowerState = keepPower;
if (keepPowerState) {
pinMode(KEEP_POWER_PIN, OUTPUT);
digitalWrite(KEEP_POWER_PIN, HIGH);
} else {
//pinMode(KEEP_POWER_PIN, OUTPUT);
//digitalWrite(KEEP_POWER_PIN, LOW);
pinMode(KEEP_POWER_PIN, INPUT);
}
}
}
static bool getKeepPower() {
return keepPower;
}
static void resetCycle(){
tickKeepPower = tickSinceStarted;
indicatorBlinkTick = 0;
indicatorFastBlink = false;
indicatorBlinkIndex = 0;
}
static void keepPowerRecycle(){
//delay keep power befor turn it off
setKeepPower(true);
keepPower = false;
resetCycle();
}
static void setOutputPowerState(bool state){
if (powerOutputActive != state) {
powerOutputActive = state;
//reset shutdown
criticalShutdown = false;
persistentShutdown = false;
ongoingToPowerOn = false;
//automatically keep power to mcu
if (powerOutputActive){
turnOnDisplay(); //automatically turn on the display
setKeepPower(true); //keep power to mcu
}else{
keepPowerRecycle(); //prepare to turn off mcu power if no input power
}
digitalWrite(POWER_OUTPUT_PIN, powerOutputActive?HIGH:LOW);
}
}
static void batteryChargeRoutine() {
//charge management:
if (isChargeActive){
//check for short
//if (chargeError == CHARGE_ERROR_NONE){
if (inputVoltageOK && inputVoltageReady && (batteryVoltageUnfiltered<SHORT_VOLTAGE) ){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge
batteryCheckSkipTick = 0;//reset skip check for faster act
chargeManagementMode = 0;
chargeError = CHARGE_ERROR_SHORTED;
}
//}
//check for overvoltage
//if (chargeError == CHARGE_ERROR_NONE){
if (chargeOvervoltageProtection && inputVoltageReady && (batteryVoltage>battOverVoltage) ){
//overvoltage detection
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge
batteryCheckSkipTick = 0;//reset skip check for faster act
chargeManagementMode = 0;
chargeError = CHARGE_ERROR_OVERVOLTAGE;
//Serial.println("CHARGE_ERROR_OVERVOLTAGE");
}
//}
if (chargeError==CHARGE_ERROR_NONE){
//stop the charges by mode ***********************************************************************************************************
if (!chargeAlwaysActive){
if (chargeToFull || needFullChargeOnce){
if (batteryVoltage>battVoltageFullChargeMinimal){
//wait n-seconds before stop the charges
if ((chargeWaitStopTick==0) && (chargeWaitBeforeStop>0)) {
chargeWaitStopTick = tickSinceStarted;
}else{
if (chargeWaitBeforeStop==0){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//instant stop
needFullChargeOnce = false;
}else{
if ((tickSinceStarted-chargeWaitStopTick)>=chargeWaitBeforeStop){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop charge
needFullChargeOnce = false;
}
}
}
}else if (batteryVoltage>battVoltageFullChargeMaximal){
setChargeMode(CHARGE_STOP_LIGHT_OFF); //instant stop
needFullChargeOnce = false;
}
}else{
//wait n-seconds before stop the charges
if (batteryPercentage>=chargePercentageForStop){
if ((chargeWaitStopTick==0) && (chargeWaitBeforeStop>0)) {
chargeWaitStopTick = tickSinceStarted;
}else{
if (chargeWaitBeforeStop==0){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//instant stop
}else{
if ((tickSinceStarted-chargeWaitStopTick)>=chargeWaitBeforeStop){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop charge
}
}
}
}
//instant stop if overshoot by +1 percent
if ( (batteryPercentage >= (chargePercentageForStop+1)) || (batteryVoltage>=battVoltageFullChargeMaximal) ){
setChargeMode(CHARGE_STOP_LIGHT_OFF);
}
}
}
//************************************************************************************************************************************
//slowing the charge when reach full using charge with light off (will slightly reduce top voltage) to prolong battery life? -------
if (isChargeActive){
//(optional by variable)
if (chargeReduceAtBatteryFull){
if ((batteryVoltage>battVoltageFullChargeMinimal) || ((!chargeAlwaysActive) && (batteryVoltage>=(chargePercentageForStop-0)))){
//reduced charge (charge indicator off)
if (chargeMode != CHARGE_ACTIVE_LIGHT_OFF) setChargeMode(CHARGE_ACTIVE_LIGHT_OFF);
}else if ((chargeAlwaysActive && (batteryVoltage<=battVoltageFullRest)) || ( (!chargeAlwaysActive) && (batteryVoltage<(chargePercentageForStop-2)) ) ){
//normal charge (charge with indicator on)
if (chargeMode != CHARGE_ACTIVE_LIGHT_ON) setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}
}else{
//normal charge indicator (on)
if (chargeMode != CHARGE_ACTIVE_LIGHT_ON) setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}
}
//----------------------------------------------------------------------------------------------------------------------------------
}
}else{
if (chargeError==CHARGE_ERROR_NONE){
//resume charge by mode:
//force charge after input lost >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if (!chargeInputLost && chargeAfterInputLost){
//if (inputUnderVoltage) {
if (!inputVoltageKeepup) {
chargeInputLost = true;
}
}
//delayed charge logic again
if (chargeInputLost && inputVoltageKeepup){
chargeAllowed = false;
chargeAllowedTick = tickSinceStarted;
}
if (chargeAllowed){
if (chargeInputLost && chargeAfterInputLost && inputVoltageKeepup){
if (fullChargeAfterInputLost && (batteryPercentage<=triggerFullChargeAfterInputLostAtPercentage)) needFullChargeOnce = true;
//only resume charge if condition meet below upper voltage criteria
if (chargeToFull || chargeAlwaysActive || needFullChargeOnce){
//charge with light indicator on
setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}else{
if (batteryPercentage<chargePercentageForStop){
//charge with light indicator on
setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}
}
chargeInputLost = false;
}else{
if (chargeInputLost) chargeInputLost = false;
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//normal charge logic ####################################################################
if (chargeAllowed){
if (inputVoltageKeepup){
if (chargeAlwaysActive || needFullChargeOnce){
setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}else{
if (batteryPercentage<=chargePercentageForResume){
setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
}
}
}
}
//########################################################################################
}
}
}
static bool setPersistentShutdown(uint32_t delayNs){
if (!persistentShutdown && powerOutputActive){
persistentDelayShutdown = delayNs;
persistentShutdown = true;
persistentShutdownTick = tickSinceStarted;
return true;
}
return false;
}
static uint32_t getPersistentShutdownTick(){
if (!persistentShutdown || !powerOutputActive) return 0;
if ((tickSinceStarted-persistentShutdownTick) > persistentDelayShutdown) return 0;
return (persistentDelayShutdown - (tickSinceStarted-persistentShutdownTick));
}
static bool autoPowerOnEligible(){
return (autoPowerOn && !powerOutputActive && (forceAutoPowerAtBatteryError || ( ((batteryVoltage>battVoltageForceShutdown) && (chargeError==CHARGE_ERROR_NONE)) || ((batteryVoltage>battVoltageCritical) && (chargeError==CHARGE_ERROR_OVERVOLTAGE))) ) );
}
static bool restartAutoPowerOn(){
//restart automatic power on when voltage input exists
if (ongoingToPowerOn) return false;
if (startupReady && inputVoltageKeepup && inputVoltageOK && autoPowerOnEligible()){
ongoingToPowerOn = true;
autoPowerOnTick = tickSinceStarted;
powerEverLoss = false;
return true;
}
return false;
}
static void powerManagementRoutine(){
//manage shutdown when battery running very low capacity (below critical) while input lost (1-2 minutes), while instant shutdown (less than 5 sec) when below force shutdown voltage
//if (inputVoltageOK){
if (inputVoltageKeepup){
//reset critical shutdown if exists ???????????????????????????????????????????????????????????????????
if (powerOutputActive && (!persistentShutdown) && criticalShutdown){
criticalShutdown = false;
criticalShutdownTick = 0;
//stop delayed mcu power detach
if ((!keepPower && keepPowerState)){
setKeepPower(true);
turnOnDisplay();
}
}
//?????????????????????????????????????????????????????????????????????????????????????????????????????
//automatic power on (only if battery exists and input power resume) *********************************
if ((powerEverLoss||ongoingToPowerOn) && powerOutputActive){
ongoingToPowerOn = false;
powerEverLoss = false; //reset
}
if (autoPowerOnEligible()){
if (ongoingToPowerOn){
//wait timer reaching delay
if ((tickSinceStarted-autoPowerOnTick) > autoPowerOnDelay){
turnOnDisplay();
ongoingToPowerOn = false;
setOutputPowerState(true);
}
}else{
if (startupReady && powerEverLoss){
ongoingToPowerOn = true;
autoPowerOnTick = tickSinceStarted;
powerEverLoss = false;
}
}
}
//****************************************************************************************************
}else{
//reset automatic power on timing if exist ******************************************
if (ongoingToPowerOn){
ongoingToPowerOn = false;
autoPowerOnTick = 0;
}
powerEverLoss = true;
//***********************************************************************************
//start power shutdown countdown when critical, or instant shutdown when voltage <= battVoltageForceShutdown
if (powerOutputActive || keepPowerState){
if (!(criticalShutdown || persistentShutdown)){
if (batteryVoltage<battVoltageForceShutdown){
//fast forced shutdown with release keep power only (delayed)
criticalShutdown = true;
setOutputPowerState(false);
criticalShutdownTick = tickSinceStarted;
keepPowerRecycle();
}
//timed shutdown if battery under critical
if (!criticalShutdown && batteryVoltage<battVoltageCritical){
criticalShutdown = true;
criticalShutdownTick = tickSinceStarted;
if (criticalDelayShutdown==0){
//fast force shutdown by power recycle
keepPowerRecycle();
}
}
}
}
}
//######################################################################################################
//shutdown timer >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
if (criticalShutdown || persistentShutdown){
if (persistentShutdown){
if (criticalShutdown) criticalShutdown = false; //clear critical shutdown flag
if ((tickSinceStarted-persistentShutdownTick) >= persistentDelayShutdown){
persistentShutdown = false;
persistentShutdownTick = 0;
setOutputPowerState(false);
keepPowerRecycle();
}
}else if (criticalShutdown){
if ((tickSinceStarted-criticalShutdownTick) >= criticalDelayShutdown){
criticalShutdown = false;
criticalShutdownTick = 0;
setOutputPowerState(false);
keepPowerRecycle();
}
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
static void batteryManagementRoutine() {
//only charge when input voltage threshold detected ------
if (!inputVoltageKeepup){
if (isChargeActive){
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge again
chargeError==CHARGE_ERROR_NONE;
chargeManagementMode = 0;
}
}
//--------------------------------------------------------
//skip check
if (batteryCheckSkipTick > 0) {
if (tickSinceStarted > batteryCheckSkipTick) {
batteryCheckSkipTick = 0;
//Serial.print("VBatt check: ");
//Serial.println(batteryVoltageUnfiltered);
} else {
//if (batteryCheckSkipTick); //reset when overflow 32-bit
return;
}
}
//used for demo:
//chargeError = CHARGE_ERROR_NONE;
//isChargeActive = false;
switch (chargeError) {
case CHARGE_ERROR_NONE: {
//if (chargeMode == CHARGE_STOP_LIGHT_OFF) {
if (!isChargeActive) {
//check battery if exists
if (chargeManagementMode == 0) {
chargeManagementMode = 1;
batteryCheckSkipTick = tickSinceStarted + 500000; //add another 0.5s
} else {
if (batteryVoltageUnfiltered <= battVoltageNotDetected) {
chargeManagementMode = 0;
chargeError = CHARGE_ERROR_NO_BATTERY;
} else {
batteryChargeRoutine();
batteryCheckSkipTick = tickSinceStarted + 500000; //add another 0.5s
}
}
} else {
batteryChargeRoutine();
batteryCheckSkipTick = tickSinceStarted + 200000; //add another 0.2s
}
}
break;
case CHARGE_ERROR_NO_BATTERY: {
//wait until battery detected to continue charge
//blinkChargeError = false;
blinkChargeError = true;
if (chargeManagementMode == 0) {
//try to charge in case battery dropped too much, then wait 1 sec
if (inputVoltageOK){//input voltage attached / powered
chargeManagementMode = 1;
setChargeMode(CHARGE_ACTIVE_LIGHT_ON); //enable charge
//blinkChargeError = true;
chargeBlinkTick = tickSinceStarted;
chargeStartedTick = tickSinceStarted;
}else{
//wait for battery voltage raised again (user attach a normal battery)
if (!isChargeActive) setChargeMode(CHARGE_ACTIVE_LIGHT_ON); //enable charge
if (batteryVoltageUnfiltered>battVoltageNotDetected){
setChargeMode(CHARGE_STOP_LIGHT_OFF);
chargeError = CHARGE_ERROR_NONE;
}
}
//batteryCheckSkipTick = tickSinceStarted + 500000; //skip 0.5 sec
} else if (chargeManagementMode == 1) {
//check if voltage raised > battVoltageNotDetected. If yes, keep for 60 sec
blinkChargeError = true;
if (tickSinceStarted - chargeStartedTick > 60000000UL) {
chargeManagementMode = 2;
chargeStartedTick = tickSinceStarted;
setChargeMode(CHARGE_STOP_LIGHT_OFF); //disable charge
} else {
if (tickSinceStarted-chargeStartedTick>2000000){ //activate after 2 sec of charging. minimal waiting time till charging module voltage raised > 3.0V
if (isChargeActive && (batteryVoltageUnfiltered<SHORT_VOLTAGE)){
//batteryChargeRoutine(); //may needed to check if charger shorted
chargeManagementMode = 0;
chargeStartedTick = tickSinceStarted;
chargeError = CHARGE_ERROR_SHORTED;
setChargeMode(CHARGE_STOP_LIGHT_OFF); //disable charge
}
}
}
} else if (chargeManagementMode == 2) {
//stop the charge again, then wait 2 sec for capacitor to be empty
//chargeManagementMode = 3;
setChargeMode(CHARGE_STOP_LIGHT_OFF); //disable charge
//batteryCheckSkipTick = tickSinceStarted + 3000000; //skip 2s
if (tickSinceStarted-chargeStartedTick>2000000){
chargeManagementMode = 3;
chargeStartedTick = tickSinceStarted;
}
} else {
//check the voltage again
chargeManagementMode = 0;
//if (batteryVoltageUnfiltered > battVoltageNotDetected) {
if (batteryVoltageUnfiltered >= battVoltageDetected) {
setChargeMode(CHARGE_STOP_LIGHT_OFF);
chargeError = CHARGE_ERROR_NONE;
chargeStartedTick = tickSinceStarted;
}
}
//blinking on charge indicator?
if (blinkChargeError) {
//NO LONGER USED
// if (tickSinceStarted - chargeBlinkTick > 100000) {
// chargeBlinkTick = tickSinceStarted;
// if (chargeMode == CHARGE_STOP_LIGHT_OFF) {
// //do nothing
// } else if (chargeMode == CHARGE_ACTIVE_LIGHT_ON) {
// setChargeMode(CHARGE_ACTIVE_LIGHT_OFF);
// } else if (chargeMode == CHARGE_ACTIVE_LIGHT_OFF) {
// setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
// }
// }
//if (chargeMode == CHARGE_STOP_LIGHT_OFF) {
// //do nothing
//} else{
// setChargeMode(indicatorFastBlink?CHARGE_ACTIVE_LIGHT_ON:CHARGE_ACTIVE_LIGHT_OFF);
//}
}
}
break;
case CHARGE_ERROR_SHORTED: {
bool blinkChargeError = false;
if (chargeManagementMode == 0) {
//wait 10 seconds before retry or resume charge
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge
chargeManagementMode = 1;
batteryCheckSkipTick = tickSinceStarted + 10000000UL; //add another 10s
} else if (chargeManagementMode == 1) {
//retry charge again
chargeManagementMode = 2;
setChargeMode(CHARGE_ACTIVE_LIGHT_ON);
batteryCheckSkipTick = tickSinceStarted + 500000UL; //add another 0.5s
} else if (chargeManagementMode == 2) {
//check voltage again (larger than 3 Volt) and act againts error
chargeVoltageOK = batteryVoltageUnfiltered>battVoltageNotDetected;
if (batteryVoltageUnfiltered > SHORT_VOLTAGE) {
chargeManagementMode = 3;
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge for a while
batteryCheckSkipTick = tickSinceStarted + 2000000UL; //add another 2s
}else{
chargeManagementMode = 0;
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge again
}
}else{
chargeManagementMode = 0;
if (batteryVoltageUnfiltered>battVoltageNotDetected){
chargeError = CHARGE_ERROR_NONE;
}else{
chargeError = CHARGE_ERROR_NO_BATTERY;
}
}
}
break;
case CHARGE_ERROR_OVERVOLTAGE: {
//only happened when charging active and over-voltage protection active
//sense also when adc capped at max (>=adcMaxVoltageCapability) or ioAdc >= 1023
//stop the charging with charge limiter indicator is on
if (chargeManagementMode == 0) {
chargeManagementMode = 1;
setChargeMode(CHARGE_STOP_LIGHT_OFF);//stop the charge
//batteryChargeRoutine();
batteryCheckSkipTick = tickSinceStarted + 15000000UL; //add another 15s skips
}else if (chargeManagementMode == 1){
//stop the error after battery level reached below 100%
if (batteryVoltage<battVoltageFullRest){
chargeManagementMode = 2;
}
batteryCheckSkipTick = tickSinceStarted + 500000UL; //add another 0.5s
}else{
chargeManagementMode = 0;
chargeError = CHARGE_ERROR_NONE;
}
}
break;
}
}
static void inputManagementRoutine(){
//input voltage sense
inputVoltageOK = digitalRead(MINIMUM_INPUT_PIN);
if (inputVoltageOK) {
inputVoltageKeepup = true;
inputVoltageKeepupTick = tickSinceStarted;//reset decay
}else{
if (inputVoltageKeepup && (tickSinceStarted-inputVoltageKeepupTick) > inputVoltageKeepupDuration){
inputVoltageKeepup = false;
}
}
if (inputVoltageOKPrev!=inputVoltageOK){
inputVoltageOKPrev = inputVoltageOK;
inputVoltageReady = false;
inputVoltageResumeTick = tickSinceStarted;
}
if (!inputVoltageOK){
underVoltageTick = tickSinceStarted;
if (startupReady) inputUnderVoltage = true;
}
if (inputVoltageOK){
//2 sec for stabilize
if (!inputVoltageReady && ((tickSinceStarted-inputVoltageResumeTick) > 2000000)){
inputVoltageReady = true;
}
//recheck again
if (inputUnderVoltage && ((tickSinceStarted-underVoltageTick)>2000000)){
inputUnderVoltage = false;
underVoltageTick = tickSinceStarted;//reset timer
}
}
//when inputVoltageKeepup but display not ON after system ready, it will be turned on
if (startupReady && inputVoltageKeepup && !displayActive){
if (!onGoingDisplayOff){
if (!splashEverDisplayed){
displaySplashNow = true;
splashDisplayTick = tickSinceStarted;
}
displayActive = true;
}
}
//button input >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
buttonState = !digitalRead(BUTTON_INPUT_PIN);
if (!buttonState && buttonTickIgnore>0 && tickSinceStarted>buttonTickIgnore) buttonTickIgnore = 0; //ignore button pressed, should be realeased to retrigger action
if (buttonState && buttonTickIgnore==0){
if (buttonTick==0) buttonTick = tickSinceStarted;
if (!buttonShortPressIgnore && (tickSinceStarted-buttonTick>1000000UL)) buttonShortPress = true;
if (!buttonLongPressIgnore && (tickSinceStarted-buttonTick>8000000UL)) buttonLongPress = true;
//manage
if (powerOutputActive){
if (!buttonShortPressIgnore && buttonShortPress){
buttonShortPressIgnore = true;
setOutputPowerState(false);
//keep power for 4 seconds before total power off (in case saving options and send off signal)
keepPowerRecycle(); //going complete off automatically after keep power cycle countdown 0
if (inputVoltageKeepup){
//keep display
//setDisplayForTurnOff(2000000);
//setDisplayForTurnOff(100000);
}else{
setDisplayForTurnOff(100000); //turn off display after 0.1s
}
Serial.println("POWER OUT: OFF");
}
}else{
//power output currently off
//check battery state or power input
if (!buttonShortPressIgnore && buttonShortPress){
buttonShortPressIgnore = true;
if (!inputUnderVoltage || (batteryVoltage>batteryVoltageEnableOn)){
turnOnDisplay();
if (!splashEverDisplayed){
displaySplashNow = true;
splashDisplayTick = tickSinceStarted;
}
setKeepPower(true);
setOutputPowerState(true);
Serial.println("POWER OUT: ON");
}else{
//set battery level to blink, ignore button for 5 seconds
keepPowerRecycle();
displayFlipFlag = false;
flashAllBatteryLevel = 4; //flash all battery level to give low battery power rejection
buttonTickIgnore = 3000000; //ignore input for 3 seconds
//turn off display after 1 seconds
turnOnDisplay();
setDisplayForTurnOff(2000000);
Serial.println("INSUFFICIENT POWER!");
}
}
}
}
//switching
if (buttonState!=buttonStatePrev){
buttonStatePrev = buttonState;
buttonTick = tickSinceStarted;
buttonShortPress = false;
buttonShortPressIgnore = false;
buttonLongPressIgnore = false;
buttonLongPress = false;
buttonTick = 0;
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>s
}
static void setNetworkIndicatorInTransmission(bool rx, bool tx){
if (tx) indicatorNetworkBlinkerTX = 4;
if (rx) indicatorNetworkBlinkerRX = 4;
}
static void setNetworkState(uint8_t state){
if (state!=indicatorNetworkState){
indicatorNetworkBlinkerTX = 0;//reset blink counter
indicatorNetworkBlinkerRX = 0;//reset blink counter
indicatorNetworkState = state;
}
}
static void indicatorRoutine(){
//manage leds indicators timing
//cycle syncer for blink flag and timing
if (tickSinceStarted-indicatorBlinkTick>=100000){
indicatorBlinkTick = tickSinceStarted;
indicatorFastBlink = !indicatorFastBlink;
//unique blink: 2X fast blink, 1 long off
//indicatorMediumBlink = (indicatorBlinkIndex<=1) || (indicatorBlinkIndex>=3 && indicatorBlinkIndex<=4) || (indicatorBlinkIndex>=6 && indicatorBlinkIndex<=7);
//evenly blink (halved)
if (indicatorFastBlink){
indicatorMediumBlink = !indicatorMediumBlink;
if (indicatorMediumBlink){
indicatorSlowBlink = !indicatorSlowBlink;
}
}
indicatorBlinkIndex++;
if (indicatorBlinkIndex>=10) indicatorBlinkIndex = 0;
}
}
static void upsRoutine(){
//manage keep power ==================================================================================================
if ((!keepPower && keepPowerState) && ((tickSinceStarted-tickKeepPower)>HOLD_POWER_TICK)){
setKeepPower(false);
Serial.println("POWER KEEP: OFF");
}
//====================================================================================================================
//turn off display?
if (onGoingDisplayOff){
if ((tickSinceStarted-turnOffDisplayTick)>=turnOffDisplayAfter){
displayActive = false;
}
}
//charge delay start
if (!chargeAllowed){
if (tickSinceStarted-chargeAllowedTick > chargeAllowedDelay){
chargeAllowedTick = tickSinceStarted;
chargeAllowed = true;
}
}
//turn on display when powered by input line (even when programmer attached)
if (!onGoingDisplayOff && !displayActive){
if (inputVoltageKeepup && startupReady){
if (!splashEverDisplayed){
displaySplashNow = true;
splashDisplayTick = tickSinceStarted;
}
displayActive = true;
}
}
}
void displayRefresh(){
//display program:
if (!displayActive) {
clearDisplay();
sendDisplay();
return;
}
//splash:
if (displaySplashNow){
splashEverDisplayed = true;
clearDisplay();
displaySplash();
sendDisplay();
if ((tickSinceStarted-splashDisplayTick)>2000000){
displaySplashNow = false;
}
return;
}
//blinking battery (weak)
if (flashAllBatteryLevel>0){
clearDisplay();
//tick flagging-----------------------------------------
if (displayFlipFlag!=indicatorMediumBlink){
displayFlipFlag = indicatorMediumBlink;
if (!indicatorMediumBlink) flashAllBatteryLevel--;
}
//------------------------------------------------------
if (flashAllBatteryLevel>0){
if (indicatorMediumBlink){
drawBatteryBlocks(55, 4, 0);
drawBatteryCase(55, 4, 2);
}
String s = "BATTERY LOW";
//drawString(stringCenterX(s),32, s);
drawString(stringRightSideX(s),32, s);
//drawString(stringCenterX("Please Charge!"),32, "Please Charge!");
//drawString(10,32, "Please Charge!");
}else{
//turn off display
turnOffDisplay();
}
sendDisplay();
return;
}
clearDisplay();
//draw power
if (powerOutputActive){
if (persistentShutdown){
if (indicatorMediumBlink) drawPower(0,0, true);
}else{
drawPower(0,0, false);
}
}
//power in state:
if (inputVoltageKeepup){
drawBolt(20,0);
}
//draw battery
if (isChargeActive) {
if (batteryVoltage>=battVoltageFullChargeMinimal){
drawBatteryBlocks(55, 4, 4);
}else{
if (batteryLevel>0){
battIdx = batteryLevel-1;
}else{
battIdx = 0;
}
if (indicatorSlowBlink){
drawBatteryBlocks(55, 4, battIdx+1);
}else{
drawBatteryBlocks(55, 4, battIdx);
}
}
}else{
if (batteryVoltage<battVoltageNotDetected){
drawBatteryBlocks(55, 4, 0);
}else{
if (batteryVoltage<=battVoltageCritical){
if (indicatorMediumBlink){
drawBatteryBlocks(55, 4, 1);
}else{
drawBatteryBlocks(55, 4, 0);
}
}else{
drawBatteryBlocks(55, 4, batteryLevel);
}
}
}
drawBatteryCase(55, 4, 2);
drawBatteryPercentage(100,32,28, batteryPercentage);
//battery charging status:
uint8_t chgState = getChargingState();
if (chgState==1){
//reduced charging
drawCharging(88, 24, true);
}else if (chgState>1){
//fast charging
drawCharging(88, 24, false);
}
//battery errror state:
if (chargeError==CHARGE_ERROR_NO_BATTERY){
if (indicatorMediumBlink) drawWarning(20,16, 1); //error
if (indicatorMediumBlink) drawString(39, 32, "NO BAT.");
}else if (chargeError==CHARGE_ERROR_SHORTED){
if (indicatorMediumBlink) drawWarning(20,16, 2); //fatal error
if (indicatorMediumBlink) drawString(39, 32, "SHORT");
}else if (chargeError==CHARGE_ERROR_OVERVOLTAGE){
if (indicatorMediumBlink) drawWarning(20,16, 0); //warn
if (indicatorMediumBlink) drawString(39, 32, "OVER.");
}
//network and tx rx indicator ---------------------------
if (indicatorNetworkState==NET_STATE_CONNECTING){
if (indicatorMediumBlink) drawWifiAndTransmission(0, 16, indicatorNetworkSignalLevel, false, false);
}else if (indicatorNetworkState==NET_STATE_CONNECTED){
//indicatorNetworkBlinker
drawWifiAndTransmission(0, 16, indicatorNetworkSignalLevel, indicatorNetworkBlinkerRX>0, indicatorNetworkBlinkerTX>0);
}
//tick flagging-----------------------------------------
if (displayNetworkFlipFlag!=indicatorFastBlink){
displayNetworkFlipFlag = indicatorFastBlink;
if (indicatorNetworkBlinkerTX>0) indicatorNetworkBlinkerTX--;
if (indicatorNetworkBlinkerRX>0) indicatorNetworkBlinkerRX--;
}
//------------------------------------------------------
sendDisplay();
//displayDemoRoutine1(); //for screen demo
}
static void upsRoutineInterrupt(){
//if (!interruptEnable){
//currentNs = ESP.getCycleCount();
//tickSinceStarted += (currentNs-lastNs) * 6;//cycles * 6.25;
//lastNs = currentNs;
currentNs = millis();
if (currentNs==lastNs) return; //skip this time
tickSinceStarted += (currentNs-lastNs) * 1000;
lastNs = currentNs;
//}
//startup init
if (!startupReady && (tickSinceStarted>=1000000)){
startupReady = true; //delay signal from startup
inputVoltageOK = digitalRead(MINIMUM_INPUT_PIN);
if (inputVoltageOK) inputVoltageKeepup = true;
inputUnderVoltage = !inputVoltageOK;
//attachVoltageFallIntterupt();
}
//adc filter handler, 16666ns = ~60 samples perseconds
if ((tickSinceStarted - ioAdcTick) > 16666) {
ioAdcTick = tickSinceStarted;
adcReadFiltered();
inputManagementRoutine();
upsRoutine();
lowRateTick++;
if (lowRateTick>=6){
lowRateTick = 0;
lowRateRoutine = true;
}
}
//333333 ns = ~4 samples perseconds
//100000 ns = ~10 samples perseconds
//if ((tickSinceStarted - batteryCheckTick) > 100000) {
if (lowRateRoutine) {
lowRateRoutine=false;
//inputVoltageOK = digitalRead(MINIMUM_INPUT_PIN);
//batteryCheckTick = tickSinceStarted;
bool b = isUpsBatteryCritical(); //refresh critical state
indicatorRoutine();
batteryManagementRoutine();
powerManagementRoutine();
//displayRefresh
displayRefresh();
}
}
static void initUpsIo() {
//interruptEnable = enableInterrupt;
digitalWrite(POWER_OUTPUT_PIN, LOW);//disable output activation
pinMode(POWER_OUTPUT_PIN, OUTPUT);
digitalWrite(POWER_OUTPUT_PIN, LOW);//disable output activation
powerOutputActive = false;
//voltage selection
batteryVoltageEnableOn = battVoltageCritical;
tickSinceStarted = 0;
chargeAllowed = false;
chargeAllowedTick = 0;
inputUnderVoltage = false;
inputVoltageKeepup = false;
startupReady = false;
adcSkipCounter = 0;
resetBatteryLevel();//reset battery soc
adcReadFiltered();//read once
initDisplay(false); //init display lib
//keep power for defined millis
keepPower = false;
if (HOLD_POWER_TICK > 0) {
keepPowerState = true;
pinMode(KEEP_POWER_PIN, OUTPUT);
digitalWrite(KEEP_POWER_PIN, HIGH);
} else {
keepPowerState = false;
pinMode(KEEP_POWER_PIN, INPUT);
}
pinMode(MINIMUM_INPUT_PIN, INPUT);
inputVoltageKeepup = digitalRead(MINIMUM_INPUT_PIN);
inputUnderVoltage = !inputVoltageKeepup;
//init charge pins
pinMode(CHARGE_STOP_PIN, OUTPUT);
setChargeMode(CHARGE_STOP_LIGHT_OFF); //disable charge first
chargeError = CHARGE_ERROR_NONE;//clear error
chargeManagementMode = 0; //reset mode
if (inputVoltageKeepup){
batteryCheckSkipTick = 2000000; //skip 200000ns (2s)
}else{
batteryCheckSkipTick = 500000; //skip 500000ns (0.5s)
}
pinMode(BUTTON_INPUT_PIN, INPUT);
}
bool isInputLinePowered(){
return inputVoltageKeepup;
}
bool isUpsPoweredFromBattery(){
return powerOutputActive && !inputVoltageKeepup;
//return (powerOutputActive || keepPowerState) && !inputVoltageKeepup;
//return !inputVoltageKeepup;
}
bool isUpsOn(){
return powerOutputActive;
}
bool isUpsBatteryIndicatorChargingOrBlink(){
if (!inputVoltageKeepup && isUpsBatteryCritical()) return true;
return batteryBlinkingIndicatorState;
//return false;
}