432 lines
13 KiB
C
432 lines
13 KiB
C
//todo:
|
|
// - more accurate TX/RX indication (for security concern), e.g. favicon is served static, thus not display TX/RX arrow.
|
|
// - Login and apikey access for next version
|
|
|
|
#include "wifi.h"
|
|
#include "upsCfg.h"
|
|
|
|
#ifndef UPSIO_H
|
|
#include "upsIo.h"
|
|
#endif
|
|
|
|
//#include <AsyncTCP.h> //for esp32
|
|
#include <ESPAsyncTCP.h> //for esp8266
|
|
#include <ESPAsyncWebServer.h>
|
|
|
|
int8_t wifiStatePrev = 99;
|
|
|
|
bool configChanged = false;
|
|
|
|
AsyncWebServer server(80);
|
|
|
|
void applyConfigs(){
|
|
autoPowerOn = (cfgAutoOn >= AUTO_POWER_ON); //auto power on
|
|
|
|
//cfgChargeMode: charge mode:
|
|
switch (cfgChargeMode){
|
|
case CHARGE_MODE_ALWAYS:{
|
|
chargeAlwaysActive = true;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_90_100:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 100;
|
|
chargePercentageForResume = 90;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_85_100:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 100;
|
|
chargePercentageForResume = 85;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_80_100:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 100;
|
|
chargePercentageForResume = 80;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_90_95:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = false;
|
|
chargePercentageForStop = 95;
|
|
chargePercentageForResume = 90;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_85_95:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 95;
|
|
chargePercentageForResume = 85;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_80_95:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 95;
|
|
chargePercentageForResume = 80;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_85_90:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 90;
|
|
chargePercentageForResume = 85;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_80_90:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 90;
|
|
chargePercentageForResume = 80;
|
|
}
|
|
break;
|
|
case CHARGE_MODE_RANGE_80_85:{
|
|
chargeAlwaysActive = false;
|
|
chargeToFull = true;
|
|
chargePercentageForStop = 85;
|
|
chargePercentageForResume = 80;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//cfgInputLostTriggerChargeMode:
|
|
switch (cfgInputLostTriggerChargeMode){
|
|
case INPUT_LOST_TRIGGER_CHARGE_NONE:{
|
|
chargeAfterInputLost = false;
|
|
}
|
|
break;
|
|
case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_ALWAYS:{
|
|
chargeAfterInputLost = true;
|
|
fullChargeAfterInputLost = true;
|
|
}
|
|
break;
|
|
case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_95:{
|
|
chargeAfterInputLost = true;
|
|
fullChargeAfterInputLost = false;
|
|
triggerFullChargeAfterInputLostAtPercentage = 95;
|
|
}
|
|
break;
|
|
case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_90:{
|
|
chargeAfterInputLost = true;
|
|
fullChargeAfterInputLost = false;
|
|
triggerFullChargeAfterInputLostAtPercentage = 90;
|
|
}
|
|
break;
|
|
case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_85:{
|
|
chargeAfterInputLost = true;
|
|
fullChargeAfterInputLost = false;
|
|
triggerFullChargeAfterInputLostAtPercentage = 85;
|
|
}
|
|
break;
|
|
case INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_80:{
|
|
chargeAfterInputLost = true;
|
|
fullChargeAfterInputLost = false;
|
|
triggerFullChargeAfterInputLostAtPercentage = 80;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//cfgChargeOvervoltageProtection:
|
|
chargeOvervoltageProtection = (cfgChargeOvervoltageProtection > 0);
|
|
//cfgChargeAutomaticReducer:
|
|
chargeReduceAtBatteryFull = (cfgChargeAutomaticReducer > 0);
|
|
}
|
|
|
|
bool getShutdownSuggestionFlag(){
|
|
if (cfgShutdownSuggestion==SHUTDOWN_SUGGESTION_DISABLED) return false;
|
|
if (!startupReady) return false;
|
|
switch(cfgShutdownSuggestion){
|
|
case SHUTDOWN_SUGGESTION_ON_BATT:{
|
|
return powerOutputActive && !inputVoltageKeepup;
|
|
}
|
|
break;
|
|
case SHUTDOWN_SUGGESTION_ON_BATT_75:{
|
|
return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=3);
|
|
}
|
|
break;
|
|
case SHUTDOWN_SUGGESTION_ON_BATT_50:{
|
|
return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=2);
|
|
}
|
|
break;
|
|
case SHUTDOWN_SUGGESTION_ON_BATT_25:{
|
|
return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=1);
|
|
}
|
|
break;
|
|
case SHUTDOWN_SUGGESTION_ON_BATT_CRITICAL:{
|
|
return powerOutputActive && !inputVoltageKeepup && (batteryLevel<=1) && isUpsBatteryCritical();
|
|
}
|
|
break;
|
|
default:{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void handleGetState(AsyncWebServerRequest *request) {
|
|
setNetworkIndicatorInTransmission(true, false);
|
|
bool isRefresh = true;
|
|
String v = "";
|
|
uint8_t i = 0;
|
|
|
|
if (request->hasParam("on")) {
|
|
isRefresh = false;
|
|
v = request->getParam("on")->value();
|
|
bool tgtState = v != "0";
|
|
Serial.print("Switch request: ");
|
|
Serial.println(tgtState);
|
|
if (tgtState != powerOutputActive) {
|
|
setOutputPowerState(tgtState);
|
|
Serial.println("Switching...");
|
|
}
|
|
}
|
|
|
|
if (request->hasParam("auto")) {
|
|
isRefresh = false;
|
|
v = request->getParam("auto")->value();
|
|
uint8_t tmpAutoOnMode = v.toInt();
|
|
if ((tmpAutoOnMode != cfgAutoOn) && (tmpAutoOnMode <= AUTO_POWER_PERSIST) ) {
|
|
cfgAutoOn = tmpAutoOnMode;
|
|
configChanged = true;
|
|
}
|
|
}
|
|
|
|
if (request->hasParam("shutdown")) {
|
|
isRefresh = false;
|
|
v = request->getParam("shutdown")->value();
|
|
uint16_t tSec = v.toInt();
|
|
if (tSec > 999) tSec = 999;
|
|
uint32_t nanoT = ((uint32_t)tSec) * 1000000UL;
|
|
if (nanoT < 999999) nanoT = 999999;
|
|
bool ok = setPersistentShutdown(nanoT);
|
|
if ((cfgShutdownInSeconds != tSec && ok)){
|
|
cfgShutdownInSeconds = tSec;
|
|
configChanged = true;
|
|
}
|
|
Serial.print("Timed Shutdown request: "); Serial.println(tSec);
|
|
//Serial.println(ok);
|
|
}
|
|
|
|
if (request->hasParam("shutlv")) {
|
|
isRefresh = false;
|
|
v = request->getParam("shutlv")->value();
|
|
int16_t lv = v.toInt();
|
|
if (lv >= SHUTDOWN_SUGGESTION_DISABLED && lv <= SHUTDOWN_SUGGESTION_ON_BATT_CRITICAL) {
|
|
if (lv != cfgShutdownSuggestion) {
|
|
cfgShutdownSuggestion = lv;
|
|
configChanged = true;
|
|
Serial.print("Shutdown level changed: ");
|
|
Serial.println(lv);
|
|
}
|
|
} else {
|
|
Serial.print("Invalid shutdown level: ");
|
|
Serial.println(v);
|
|
}
|
|
}
|
|
|
|
//battery management params:
|
|
//cfgChargeMode (charge mode):
|
|
if (request->hasParam("cm")) {
|
|
isRefresh = false;
|
|
v = request->getParam("cm")->value();
|
|
i = v.toInt();
|
|
if (i<=CHARGE_MODE_RANGE_80_85){
|
|
cfgChargeMode = i;
|
|
configChanged = true;
|
|
Serial.print("CM: "); Serial.println(i);
|
|
}
|
|
}
|
|
|
|
//cfgInputLostTriggerChargeMode (charge on input lost):
|
|
if (request->hasParam("coil")) {
|
|
isRefresh = false;
|
|
v = request->getParam("coil")->value();
|
|
i = v.toInt();
|
|
if (i<=INPUT_LOST_TRIGGER_CHARGE_ACTIVE_FULL_80){
|
|
cfgInputLostTriggerChargeMode = i;
|
|
configChanged = true;
|
|
Serial.print("COIL: "); Serial.println(i);
|
|
}
|
|
}
|
|
|
|
//cfgChargeOvervoltageProtection (charge overvoltage protection):
|
|
if (request->hasParam("covp")) {
|
|
isRefresh = false;
|
|
v = request->getParam("covp")->value();
|
|
i = v.toInt();
|
|
if (i>1) i = 1;
|
|
if (i!=cfgChargeOvervoltageProtection){
|
|
cfgChargeOvervoltageProtection = i;
|
|
configChanged = true;
|
|
Serial.print("COVP: "); Serial.println(i);
|
|
}
|
|
}
|
|
//reset "On-Going Full Charge" triggered after input lost (reset ongoing full charge):
|
|
if (request->hasParam("rofc")) {
|
|
isRefresh = false;
|
|
v = request->getParam("rofc")->value();
|
|
i = v.toInt();
|
|
if (i>0 && needFullChargeOnce) {
|
|
resetOnGoingFullChargeAfterLineLost();
|
|
Serial.print("ROFC: "); Serial.println(i);
|
|
}
|
|
}
|
|
|
|
//cfgChargeAutomaticReducer (charge automatic reducer)
|
|
if (request->hasParam("car")) {
|
|
isRefresh = false;
|
|
v = request->getParam("car")->value();
|
|
i = v.toInt();
|
|
if (i>1) i = 1;
|
|
if (i!=cfgChargeAutomaticReducer){
|
|
cfgChargeAutomaticReducer = i;
|
|
configChanged = true;
|
|
Serial.print("CAR: "); Serial.println(i);
|
|
}
|
|
}
|
|
|
|
DynamicJsonDocument jsonDoc(1024);
|
|
String jsonResponse;
|
|
//String stateOn = isUpsOn()?"1":"0";
|
|
//String autoOn = String(cfgAutoOn);
|
|
//String voltageValue = String(batteryVoltage, 2);
|
|
//2 battery, 1 Input line or AC, 0 OFF
|
|
//String lineActive = isUpsPoweredFromBattery()?"2":isInputLinePowered()?"1":isUpsOn()?"2":"0";
|
|
uint8_t lineActiveState = isUpsPoweredFromBattery()?2:isInputLinePowered()?1:isUpsOn()?2:0;
|
|
//String batt = String(batteryLevel);
|
|
//String battMax = "4";
|
|
// String battBlink = IsUpsChargingOrBlink()?"1":"0";
|
|
// String chargeLv = IsUpsChargingOrBlink()?(String(UpsBattMinimalLevelUnderChargingOrBlink())):(String(UpsBatteryLevel()));
|
|
// String battCritical = IsUpsBatteryCritical()?"1":"0";
|
|
// String isTimedShut = timedShutdownFlag?"1":"0";
|
|
// String onShutRemaining = String(GetShutdownCountdown());
|
|
// String shutdownSuggest = GetShutdownSuggestionFlag()?"1":"0";
|
|
// String shutdownSuggestMode = String(cfgShutdownSuggestion);
|
|
// String shutdownLastPeriod = String(timeShutdownLastPeriod);
|
|
|
|
//json vars:
|
|
jsonDoc["success"] = isRefresh ? 2 : 1;
|
|
jsonDoc["on"] = powerOutputActive?1:0;;
|
|
jsonDoc["state"] = isUpsOn()?1:0;
|
|
jsonDoc["v"] = String(batteryVoltage, 2);
|
|
jsonDoc["batt"] = batteryLevel;
|
|
jsonDoc["battP"] = batteryPercentage;
|
|
jsonDoc["line"] = lineActiveState;
|
|
|
|
jsonDoc["auto"] = cfgAutoOn;
|
|
jsonDoc["battBlink"] = isUpsBatteryIndicatorChargingOrBlink()?1:0;
|
|
jsonDoc["battCritical"] = isUpsBatteryCritical()?1:0;
|
|
jsonDoc["chargeLv"] = getRawBatteryLevelFromPercentage(batteryPercentage); //real level
|
|
jsonDoc["batt"] = batteryLevel; //staged level
|
|
jsonDoc["battMax"] = 4;
|
|
jsonDoc["timedShutdown"] = (persistentShutdown && powerOutputActive)?1:0;
|
|
jsonDoc["shutdownRemain"] = getPersistentShutdownTick()/1000000;
|
|
jsonDoc["shutdownSuggestMode"] = cfgShutdownSuggestion;
|
|
jsonDoc["shutdownSuggest"] = getShutdownSuggestionFlag()?1:0; //internal flag raised if cfgShutdownSuggestion fullfill the condition
|
|
jsonDoc["shutLastTimer"] = cfgShutdownInSeconds;
|
|
//charge configuration:
|
|
jsonDoc["chgError"] = chargeError; //error state
|
|
jsonDoc["charging"] = getChargingState(); //on charging state
|
|
jsonDoc["chgMode"] = cfgChargeMode;
|
|
jsonDoc["chgOVP"] = cfgChargeOvervoltageProtection; //over voltage protection
|
|
jsonDoc["chgReducer"] = cfgChargeAutomaticReducer;
|
|
jsonDoc["chgFullTrig"] = cfgInputLostTriggerChargeMode;
|
|
jsonDoc["chgOFC"] = needFullChargeOnce?1:0; //when input lost and "Ongoing Full Charge" flag triggered
|
|
|
|
serializeJson(jsonDoc, jsonResponse);
|
|
request->send(200, "application/json", jsonResponse);
|
|
|
|
setNetworkIndicatorInTransmission(true, true);
|
|
}
|
|
|
|
void onRootRequest(AsyncWebServerRequest *request) {
|
|
setNetworkIndicatorInTransmission(true, false);
|
|
Serial.println("onRootRequest");
|
|
bool isOK = false;
|
|
if (spiffsOk) {
|
|
if (SPIFFS.exists("/index.html")) {
|
|
isOK = true;
|
|
request->send(SPIFFS, "/index.html", "text/html");
|
|
setNetworkIndicatorInTransmission(true, true);
|
|
}
|
|
}
|
|
if (!isOK) {
|
|
request->send(404, "text/text", "404: Not Found");
|
|
setNetworkIndicatorInTransmission(true, true);
|
|
}
|
|
}
|
|
|
|
void upsServerInit() {
|
|
|
|
bool b = initConfigs();
|
|
if (b){
|
|
loadCfg();
|
|
applyConfigs();
|
|
}
|
|
|
|
//transfer configuration to ups library:
|
|
if (cfgAutoOn >= AUTO_POWER_ON) {
|
|
autoPowerOn = true;
|
|
restartAutoPowerOn();
|
|
} else {
|
|
autoPowerOn = false;
|
|
}
|
|
|
|
//for favicon
|
|
server.serveStatic("/favicon.ico", SPIFFS, "/favicon.ico");
|
|
server.serveStatic("*/favicon.ico", SPIFFS, "/favicon.ico");
|
|
|
|
//for RESTapi
|
|
server.on("/state", HTTP_GET, handleGetState);
|
|
|
|
//root
|
|
server.on("/", onRootRequest);
|
|
server.on("/*", onRootRequest);
|
|
//Redirect all not-found requests to the root
|
|
server.onNotFound(onRootRequest);
|
|
|
|
server.begin();
|
|
Serial.println("Server is running!");
|
|
}
|
|
|
|
void upsServerRoutine() {
|
|
WiFiManagerRoutine();
|
|
|
|
//updating wifi state:
|
|
if (wifiCurrentState != wifiStatePrev) {
|
|
wifiStatePrev = wifiCurrentState;
|
|
setNetworkState(wifiStatePrev);
|
|
}
|
|
|
|
//update signal:
|
|
indicatorNetworkSignalLevel = wifiSignalLevel;
|
|
|
|
//manage auto on mode:
|
|
if (cfgAutoOn == AUTO_POWER_PERSIST) {
|
|
if (inputVoltageKeepup && inputVoltageOK) {
|
|
if (restartAutoPowerOn()) Serial.println("Automatic ON restarted");
|
|
}
|
|
}
|
|
|
|
//if button long pressed -------------------------------------------
|
|
if (buttonLongPressIgnore && buttonLongPress){
|
|
buttonLongPressIgnore = true;
|
|
Serial.println("Long Pressed Button Action is Reserved!");
|
|
//(not used)
|
|
}
|
|
//------------------------------------------------------------------
|
|
|
|
//saving configuration:
|
|
if (configChanged) {
|
|
configChanged = false;
|
|
saveCfg();
|
|
applyConfigs();
|
|
}
|
|
|
|
}
|