2022-04-14 22:48:11 +02:00
|
|
|
#include "webui.h"
|
|
|
|
|
|
|
|
AsyncWebServer webServer(80);
|
|
|
|
|
2022-05-15 16:37:34 +02:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
RESPMSG_HIDE,
|
|
|
|
RESPMSG_SUCCESS,
|
|
|
|
RESPMSG_INFO,
|
|
|
|
RESPMSG_WARNING,
|
|
|
|
RESPMSG_DANGER
|
|
|
|
} statusResponseMessage_Type_t;
|
|
|
|
char StatusResponseMessage[64];
|
|
|
|
statusResponseMessage_Type_t StatusResponseMessage_Type = RESPMSG_INFO;
|
2022-04-14 22:48:11 +02:00
|
|
|
|
2023-02-20 13:51:54 +01:00
|
|
|
#ifdef CAPTIVE
|
|
|
|
DNSServer dnsServer;
|
|
|
|
#endif
|
|
|
|
AsyncWebServer server(80);
|
|
|
|
|
2022-04-14 22:48:11 +02:00
|
|
|
String processor(const String &var);
|
|
|
|
void WebserverPOST_Callback(AsyncWebServerRequest *request);
|
|
|
|
void WebserverNotFound_Callback(AsyncWebServerRequest *request);
|
|
|
|
void Webserver_Callback(AsyncWebServerRequest *request);
|
2022-05-15 16:37:34 +02:00
|
|
|
void WebserverCommands_Callback(String input);
|
2023-02-20 13:51:54 +01:00
|
|
|
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
|
|
|
|
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
|
|
|
|
void WebServerEEJSON_Callback(AsyncWebServerRequest *request);
|
|
|
|
void GetFlashVersion(char *buff, size_t buff_size);
|
2022-04-14 22:48:11 +02:00
|
|
|
|
|
|
|
void initWebUI()
|
|
|
|
{
|
|
|
|
if (!LittleFS.begin())
|
|
|
|
{
|
|
|
|
Serial.println("An Error has occurred while mounting LittleFS");
|
2023-02-20 13:51:54 +01:00
|
|
|
MaintainDTC(DTC_FLASHFS_ERROR, DTC_CRITICAL, true);
|
2022-04-14 22:48:11 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-20 13:51:54 +01:00
|
|
|
GetFlashVersion(globals.FlashVersion, sizeof(globals.FlashVersion));
|
|
|
|
|
|
|
|
if (strcmp(globals.FlashVersion, QUOTE(FLASH_FS_VERSION)))
|
|
|
|
{
|
|
|
|
MaintainDTC(DTC_FLASHFS_VERSION_ERROR, DTC_WARN, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
MDNS.begin(globals.DeviceName);
|
|
|
|
MDNS.addService("telnet", "tcp", 23);
|
|
|
|
MDNS.addService("http", "tcp", 80);
|
|
|
|
|
2022-04-14 22:48:11 +02:00
|
|
|
webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
|
|
|
|
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
|
|
|
{ request->redirect("/index.htm"); });
|
|
|
|
webServer.onNotFound(WebserverNotFound_Callback);
|
|
|
|
webServer.on("/index.htm", HTTP_GET, Webserver_Callback);
|
2023-02-20 13:51:54 +01:00
|
|
|
webServer.on("/post.htm", HTTP_POST, WebserverPOST_Callback);
|
|
|
|
webServer.on("/eejson", HTTP_GET, WebServerEEJSON_Callback);
|
|
|
|
webServer.on(
|
|
|
|
"/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverFirmwareUpdate_Callback);
|
|
|
|
webServer.on(
|
|
|
|
"/eeRestore", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverEERestore_Callback);
|
2022-04-14 22:48:11 +02:00
|
|
|
|
|
|
|
webServer.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
String processor(const String &var)
|
|
|
|
{
|
|
|
|
if (var == "SHOW_DTC_TABLE")
|
|
|
|
return globals.systemStatus == sysStat_Error ? "" : "hidden";
|
|
|
|
|
2022-05-15 16:37:34 +02:00
|
|
|
if (var == "SHOW_RESP_MESSAGE")
|
|
|
|
return StatusResponseMessage_Type != RESPMSG_HIDE ? "" : "hidden";
|
|
|
|
|
|
|
|
if (var == "RESP_MESSAGE_TYPE")
|
|
|
|
{
|
|
|
|
switch (StatusResponseMessage_Type)
|
|
|
|
{
|
|
|
|
case RESPMSG_SUCCESS:
|
|
|
|
return "success";
|
|
|
|
case RESPMSG_INFO:
|
|
|
|
return "info";
|
|
|
|
case RESPMSG_WARNING:
|
|
|
|
return "warning";
|
|
|
|
case RESPMSG_DANGER:
|
|
|
|
return "danger";
|
|
|
|
default:
|
|
|
|
return "info";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var == "RESP_MESSAGE")
|
|
|
|
return String(StatusResponseMessage);
|
|
|
|
|
2022-04-14 22:48:11 +02:00
|
|
|
if (var == "BAT_REMAIN_CAPACITY")
|
|
|
|
return String(globals.battery_level);
|
|
|
|
|
|
|
|
if (var == "DEVICE_NAME")
|
|
|
|
return String(globals.DeviceName);
|
|
|
|
|
|
|
|
if (var == "BAT_VOLTAGE")
|
2023-02-20 13:51:54 +01:00
|
|
|
return String((float)globals.loadvoltage_mV / 1000.0);
|
2022-04-14 22:48:11 +02:00
|
|
|
|
|
|
|
if (var == "DTC_TABLE")
|
|
|
|
{
|
|
|
|
String temp;
|
|
|
|
char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
|
|
|
|
{
|
|
|
|
if (DTCStorage[i].Number > 0)
|
|
|
|
{
|
|
|
|
sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
|
|
|
|
DTCStorage[i].timestamp / 86400000, // Days
|
|
|
|
DTCStorage[i].timestamp / 360000 % 24, // Hours
|
|
|
|
DTCStorage[i].timestamp / 60000 % 60, // Minutes
|
|
|
|
DTCStorage[i].timestamp / 1000 % 60, // Seconds
|
|
|
|
DTCStorage[i].timestamp % 1000); // milliseconds
|
|
|
|
|
|
|
|
temp = "<tr><td>" + String(buff_timestamp);
|
|
|
|
temp = temp + "</td><td>" + String(DTCStorage[i].Number) + "</td><td>";
|
|
|
|
|
|
|
|
if (DTCStorage[i].active == DTC_ACTIVE)
|
|
|
|
temp = temp + "active";
|
|
|
|
else if (DTCStorage[i].active == DTC_PREVIOUS)
|
|
|
|
temp = temp + "previous";
|
|
|
|
else
|
|
|
|
temp = temp + "none";
|
|
|
|
|
|
|
|
temp = temp + "</td></tr>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var == "PLACEHOLDER")
|
|
|
|
return "placeholder";
|
|
|
|
|
|
|
|
if (var == "POINTS_FAC_1")
|
|
|
|
{
|
|
|
|
char buff[12];
|
|
|
|
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
|
|
|
|
return String(buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var == "POINTS_FAC_2")
|
|
|
|
{
|
|
|
|
char buff[12];
|
|
|
|
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
|
|
|
|
return String(buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var == "POINTS_FAC_3")
|
|
|
|
{
|
|
|
|
char buff[12];
|
|
|
|
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
|
|
|
|
return String(buff);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (var == "STATUS_FAC_1")
|
|
|
|
return PersistenceData.activeFaction == FACTION_1 ? "ACTIVE" : "INACTIVE";
|
|
|
|
|
|
|
|
if (var == "STATUS_FAC_2")
|
|
|
|
return PersistenceData.activeFaction == FACTION_2 ? "ACTIVE" : "INACTIVE";
|
|
|
|
|
|
|
|
if (var == "STATUS_FAC_3")
|
|
|
|
return PersistenceData.activeFaction == FACTION_3 ? "ACTIVE" : "INACTIVE";
|
|
|
|
|
|
|
|
if (var == "NAME_FAC_1")
|
|
|
|
return FACTION_1_NAME;
|
|
|
|
|
|
|
|
if (var == "NAME_FAC_2")
|
|
|
|
return FACTION_2_NAME;
|
|
|
|
|
|
|
|
if (var == "NAME_FAC_3")
|
|
|
|
return FACTION_3_NAME;
|
|
|
|
|
|
|
|
if (var == "TITLE")
|
|
|
|
return DEVICE_NAME;
|
|
|
|
|
|
|
|
if (var == "BATTERY_LEVEL")
|
|
|
|
{
|
|
|
|
return String(globals.battery_level);
|
|
|
|
}
|
2022-05-15 16:37:34 +02:00
|
|
|
if (var == "BATTERY_TYPE")
|
|
|
|
{
|
|
|
|
return String(BatteryString[ConfigData.batteryType]);
|
|
|
|
}
|
2022-04-14 22:48:11 +02:00
|
|
|
if (var == "BATTERY_VOLTAGE")
|
|
|
|
{
|
2023-02-20 13:51:54 +01:00
|
|
|
return String((float)globals.loadvoltage_mV / 1000.0);
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return String();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Webserver_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
request->send(LittleFS, "/index.htm", "text/html", false, processor);
|
2022-05-15 16:37:34 +02:00
|
|
|
StatusResponseMessage_Type = RESPMSG_HIDE;
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WebserverPOST_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
int paramsNr = request->params();
|
|
|
|
for (int i = 0; i < paramsNr; i++)
|
|
|
|
{
|
|
|
|
AsyncWebParameter *p = request->getParam(i);
|
2022-05-15 16:37:34 +02:00
|
|
|
if (p->name() == "commandInput")
|
|
|
|
WebserverCommands_Callback(p->value());
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
2022-05-15 16:37:34 +02:00
|
|
|
request->send(LittleFS, "/index.htm", "text/html", false, processor);
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WebserverNotFound_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
request->send(404, "text/html", "Not found");
|
2022-05-15 16:37:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WebserverCommands_Callback(String input)
|
|
|
|
{
|
|
|
|
String command = input.substring(0, input.indexOf(' '));
|
2022-05-15 22:48:25 +02:00
|
|
|
command.toUpperCase();
|
2022-05-15 16:37:34 +02:00
|
|
|
StatusResponseMessage_Type = RESPMSG_HIDE;
|
|
|
|
|
2022-05-15 22:48:25 +02:00
|
|
|
if (command == "RESET")
|
2022-05-15 16:37:34 +02:00
|
|
|
{
|
|
|
|
strcpy(StatusResponseMessage, "Counter Reset done");
|
|
|
|
StatusResponseMessage_Type = RESPMSG_SUCCESS;
|
|
|
|
|
|
|
|
PersistenceData.faction_1_timer = 0;
|
|
|
|
PersistenceData.faction_2_timer = 0;
|
|
|
|
PersistenceData.faction_3_timer = 0;
|
|
|
|
PersistenceData.activeFaction = NONE;
|
|
|
|
}
|
2023-02-15 20:12:29 +01:00
|
|
|
else if (command == "BAT3S")
|
|
|
|
{
|
|
|
|
strcpy(StatusResponseMessage, "Set BatteryType to LiPo 3S");
|
|
|
|
StatusResponseMessage_Type = RESPMSG_SUCCESS;
|
|
|
|
|
|
|
|
ConfigData.batteryType = BATTERY_LIPO_3S;
|
|
|
|
globals.requestEEAction = EE_CFG_SAVE;
|
|
|
|
}
|
|
|
|
else if (command == "BAT2S")
|
|
|
|
{
|
|
|
|
strcpy(StatusResponseMessage, "Set BatteryType to LiPo 2S");
|
|
|
|
StatusResponseMessage_Type = RESPMSG_SUCCESS;
|
|
|
|
|
|
|
|
ConfigData.batteryType = BATTERY_LIPO_2S;
|
|
|
|
globals.requestEEAction = EE_CFG_SAVE;
|
|
|
|
}
|
2023-02-20 13:51:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GetFlashVersion(char *buff, size_t buff_size)
|
|
|
|
{
|
|
|
|
File this_file = LittleFS.open("version", "r");
|
|
|
|
if (!this_file)
|
|
|
|
{ // failed to open the file, retrn empty result
|
|
|
|
buff[0] = '\0';
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this_file.available())
|
|
|
|
{
|
|
|
|
int bytes_read;
|
|
|
|
bytes_read = this_file.readBytesUntil('\r', buff, buff_size - 1);
|
|
|
|
buff[bytes_read] = '\0';
|
|
|
|
}
|
|
|
|
this_file.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (!index)
|
|
|
|
{
|
|
|
|
Serial.println("Update");
|
|
|
|
size_t content_len = request->contentLength();
|
|
|
|
int cmd = (filename.indexOf(".fs") > -1) ? U_FS : U_FLASH;
|
|
|
|
Update.runAsync(true);
|
|
|
|
if (!Update.begin(content_len, cmd))
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Update.write(data, len) != len)
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Serial.printf("Progress: %d%%\n", (Update.progress() * 100) / Update.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (final)
|
|
|
|
{
|
|
|
|
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
|
|
|
|
response->addHeader("Refresh", "20");
|
|
|
|
response->addHeader("Location", "/");
|
|
|
|
request->send(response);
|
|
|
|
if (!Update.end(true))
|
|
|
|
{
|
|
|
|
Update.printError(Serial);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Serial.println("Update complete");
|
|
|
|
Serial.flush();
|
|
|
|
globals.systemStatus = sysStat_Shutdown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
|
|
{
|
|
|
|
|
|
|
|
bool ee_done = false;
|
|
|
|
bool validext = false;
|
|
|
|
|
|
|
|
if (!index)
|
|
|
|
{
|
|
|
|
Serial.println("EEPROM restore");
|
|
|
|
validext = (filename.indexOf(".ee.json") > -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validext)
|
|
|
|
{
|
|
|
|
Serial.println("Restoring EEPROM-Stuff");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (final)
|
|
|
|
{
|
|
|
|
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
|
|
|
|
response->addHeader("Refresh", "20");
|
|
|
|
response->addHeader("Location", "/");
|
|
|
|
request->send(response);
|
|
|
|
if (ee_done)
|
|
|
|
{
|
|
|
|
Serial.println("Update complete");
|
|
|
|
Serial.flush();
|
|
|
|
globals.systemStatus = sysStat_Shutdown;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
|
|
|
|
{
|
|
|
|
AsyncResponseStream *response = request->beginResponseStream("application/json");
|
|
|
|
DynamicJsonDocument json(1024);
|
|
|
|
JsonObject fwinfo = json.createNestedObject("info");
|
|
|
|
|
|
|
|
char buffer[16];
|
|
|
|
|
|
|
|
fwinfo["DeviceName"] = globals.DeviceName;
|
|
|
|
fwinfo["FW-Version"] = QUOTE(SW_VERSION);
|
|
|
|
fwinfo["FS-Version"] = globals.FlashVersion;
|
|
|
|
|
|
|
|
JsonObject config = json.createNestedObject("config");
|
|
|
|
|
|
|
|
config["EEPROM_Version"] = ConfigData.EEPROM_Version;
|
|
|
|
|
|
|
|
sprintf(buffer, "0x%08X", ConfigData.checksum);
|
|
|
|
config["checksum"] = buffer;
|
|
|
|
|
|
|
|
JsonObject eepart = json.createNestedObject("eepart");
|
|
|
|
|
|
|
|
sprintf(buffer, "0x%04X", globals.eePersistanceAdress);
|
|
|
|
eepart["PersistanceAddress"] = buffer;
|
|
|
|
|
|
|
|
JsonObject persis = json.createNestedObject("persis");
|
|
|
|
|
|
|
|
persis["writeCycleCounter"] = PersistenceData.writeCycleCounter;
|
|
|
|
|
|
|
|
sprintf(buffer, "0x%08X", PersistenceData.checksum);
|
|
|
|
persis["checksum"] = buffer;
|
|
|
|
|
|
|
|
serializeJsonPretty(json, *response);
|
|
|
|
|
|
|
|
response->addHeader("Content-disposition", "attachment; filename=backup.ee.json");
|
|
|
|
|
|
|
|
request->send(response);
|
2022-04-14 22:48:11 +02:00
|
|
|
}
|