#include "webui.h" AsyncWebServer webServer(80); const char *PARAM_MESSAGE = "message"; String processor(const String &var); void WebserverPOST_Callback(AsyncWebServerRequest *request); void WebserverNotFound_Callback(AsyncWebServerRequest *request); void Webserver_Callback(AsyncWebServerRequest *request); 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); void initWebUI() { if (!LittleFS.begin()) { Serial.println("An Error has occurred while mounting LittleFS"); MaintainDTC(DTC_FLASHFS_ERROR, DTC_CRITICAL, true); return; } 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); 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); 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); webServer.begin(); } String processor(const String &var) { if (var == "TANK_REMAIN_CAPACITY") return String((PersistenceData.tankRemain_microL / 10) / LubeConfig.tankCapacity_ml); if (var == "LUBE_DISTANCE_NORMAL") return String(LubeConfig.DistancePerLube_Default); if (var == "LUBE_DISTANCE_RAIN") return String(LubeConfig.DistancePerLube_Rain); if (var == "TANK_CAPACITY") return String(LubeConfig.tankCapacity_ml); if (var == "AMOUNT_PER_DOSE") return String(LubeConfig.amountPerDose_microL); if (var == "TANK_REMIND") return String(LubeConfig.TankRemindAtPercentage); if (var == "PULSE_PER_REV") return String(LubeConfig.PulsePerRevolution); if (var == "TIRE_WIDTH_MM") return String(LubeConfig.TireWidth_mm); if (var == "TIRE_RATIO") return String(LubeConfig.TireWidthHeight_Ratio); if (var == "RIM_DIAMETER") return String(LubeConfig.RimDiameter_Inch); if (var == "DISTANCE_PER_REV") return String(LubeConfig.DistancePerRevolution_mm); if (var == "BLEEDING_PULSES") return String(LubeConfig.BleedingPulses); if (var == "SPEED_SOURCE") return String(SpeedSourceString[LubeConfig.SpeedSource]); if (var == "GPS_BAUD") #ifdef FEATURE_ENABLE_GPS return String(GPSBaudRateString[LubeConfig.GPSBaudRate]); #else return "Feature N/A"; #endif if (var == "CAN_SOURCE") #ifdef FEATURE_ENABLE_CAN return String(CANSourceString[LubeConfig.CANSource]); #else return "Feature N/A"; #endif if (var == "CONFIG_CHECKSUM") { char buffer[7]; sprintf(buffer, "0x%04X", LubeConfig.checksum); return String(buffer); } if (var == "WRITE_CYCLE_COUNT") return String(PersistenceData.writeCycleCounter); if (var == "PERSISTENCE_MARKER") return String(globals.eePersistanceAdress); if (var == "TANK_REMAIN_UL") return String(PersistenceData.tankRemain_microL); if (var == "TRAVEL_DISTANCE_HIGHRES") return String(PersistenceData.TravelDistance_highRes_mm); if (var == "ODOMETER") return String(PersistenceData.odometer); if (var == "ODOMETER_M") return String(PersistenceData.odometer_mm / 1000); if (var == "PERSISTANCE_CHECKSUM") { char buffer[7]; sprintf(buffer, "0x%04X", PersistenceData.checksum); return String(buffer); } if (var == "SHOW_IMPULSE_SETTINGS") return LubeConfig.SpeedSource == SOURCE_IMPULSE ? "" : "hidden"; if (var == "SHOW_CAN_SETTINGS") #ifdef FEATURE_ENABLE_CAN return LubeConfig.SpeedSource == SOURCE_CAN ? "" : "hidden"; #else return "hidden"; #endif if (var == "SHOW_GPS_SETTINGS") #ifdef FEATURE_ENABLE_GPS return LubeConfig.SpeedSource == SOURCE_GPS ? "" : "hidden"; #else return "hidden"; #endif if (var == "SHOW_DTC_TABLE") return globals.hasDTC ? "" : "hidden"; 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 < DTC_LAST_DTC) { 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 = temp + "" + String(buff_timestamp); temp = temp + "" + String(DTCStorage[i].Number) + ""; temp = temp + ""; 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 + ""; } } return temp; } if (var == "SOURCE_SELECT_OPTIONS") { String temp; for (uint32_t i = 0; i < SpeedSourceString_Elements; i++) { String selected = LubeConfig.SpeedSource == i ? " selected " : ""; temp = temp + ""; } return temp; } #ifdef FEATURE_ENABLE_CAN if (var == "CANSOURCE_SELECT_OPTIONS") { String temp; for (uint32_t i = 0; i < CANSourceString_Elements; i++) { String selected = LubeConfig.CANSource == i ? " selected " : ""; temp = temp + ""; } return temp; } #endif #ifdef FEATURE_EABLE_GPS if (var == "GPSBAUD_SELECT_OPTIONS") { String temp; for (uint32_t i = 0; i < GPSBaudRateString_Elements; i++) { String selected = LubeConfig.GPSBaudRate == i ? " selected " : ""; temp = temp + ""; } return temp; } #endif if (var == "SYSTEM_STATUS") return String(globals.systemStatustxt); if (var == "SW_VERSION") { return String(QUOTE(SW_VERSION)); } if (var == "FS_VERSION") return String(globals.FlashVersion); if (var == "PLACEHOLDER") return "placeholder"; return String(); } void Webserver_Callback(AsyncWebServerRequest *request) { request->send(LittleFS, "/index.htm", "text/html", false, processor); } void WebserverPOST_Callback(AsyncWebServerRequest *request) { request->send(LittleFS, "/post.htm", "text/html", false, processor); Serial.print("POST:\n"); int paramsNr = request->params(); for (int i = 0; i < paramsNr; i++) { AsyncWebParameter *p = request->getParam(i); Serial.printf("%s : %s\n", p->name().c_str(), p->value().c_str()); // begin: POST Form Source Changed if (p->name() == "sourceselect") { SpeedSource_t temp = (SpeedSource_t)p->value().toInt(); Serial.printf("temp: %d", temp); Serial.printf("SpeedSource: %d", LubeConfig.SpeedSource); if (LubeConfig.SpeedSource != temp) { LubeConfig.SpeedSource = temp; globals.systemStatus = sysStat_Shutdown; } } // end: POST Form Source Changed // begin: POST Form Source Pulse Settings if (p->name() == "tirewidth") LubeConfig.TireWidth_mm = p->value().toInt(); if (p->name() == "tireratio") LubeConfig.TireWidthHeight_Ratio = p->value().toInt(); if (p->name() == "tiredia") LubeConfig.RimDiameter_Inch = p->value().toInt(); if (p->name() == "pulserev") LubeConfig.PulsePerRevolution = p->value().toInt(); if (p->name() == "pulsesave") globals.requestEEAction = EE_CFG_SAVE; // end: POST Form Source Pulse Settings #ifdef FEATURE_EABLE_GPS // begin: POST Form Source GPS Settings if (p->name() == "gpsbaud") LubeConfig.GPSBaudRate = (GPSBaudRate_t)p->value().toInt(); if (p->name() == "gpssave") globals.requestEEAction = EE_CFG_SAVE; // end: POST Form Source GPS Settings #endif #ifdef FEATURE_EABLE_CAN // begin: POST Form Source CAN Settings if (p->name() == "cansource") LubeConfig.CANSource = (CANSource_t)p->value().toInt(); if (p->name() == "cansave") globals.requestEEAction = EE_CFG_SAVE; // end: POST Form Source CAN Settings #endif // begin: POST Form Lubrication if (p->name() == "lubedistancenormal") LubeConfig.DistancePerLube_Default = p->value().toInt(); if (p->name() == "lubedistancerain") LubeConfig.DistancePerLube_Rain = p->value().toInt(); if (p->name() == "lubesave") globals.requestEEAction = EE_CFG_SAVE; // end: POST Form Lubrication // begin: POST Form Oiltank if (p->name() == "tankcap") LubeConfig.tankCapacity_ml = p->value().toInt(); if (p->name() == "tankwarn") LubeConfig.TankRemindAtPercentage = p->value().toInt(); if (p->name() == "pumppulse") LubeConfig.amountPerDose_microL = p->value().toInt(); if (p->name() == "oilsave") globals.requestEEAction = EE_CFG_SAVE; // end: POST Form Oiltank // begin: POST Form Maintenance if (p->name() == "purgepulse") LubeConfig.BleedingPulses = p->value().toInt(); if (p->name() == "maintsave") globals.requestEEAction = EE_CFG_SAVE; if (p->name() == "resettank") { PersistenceData.tankRemain_microL = LubeConfig.tankCapacity_ml * 1000; globals.requestEEAction = EE_PDS_SAVE; } if (p->name() == "reset_ee_btn") { if (request->hasParam("reset_ee_pds", true)) { AsyncWebParameter *param = request->getParam("reset_ee_pds", true); if (param->value() == "on") globals.requestEEAction = globals.requestEEAction == EE_CFG_FORMAT ? EE_FORMAT_ALL : EE_PDS_FORMAT; } if (request->hasParam("reset_ee_cfg", true)) { AsyncWebParameter *param = request->getParam("reset_ee_cfg", true); if (param->value() == "on") globals.requestEEAction = globals.requestEEAction == EE_PDS_FORMAT ? EE_FORMAT_ALL : EE_CFG_FORMAT; } } if (p->name() == "purgenow") { globals.systemStatus = sysStat_Purge; globals.purgePulses = LubeConfig.BleedingPulses; } if(p->name() == "reboot") { globals.systemStatus = sysStat_Shutdown; } // end: POST Form Maintenance } } void WebserverNotFound_Callback(AsyncWebServerRequest *request) { request->send(404, "text/html", "Not found"); } 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"); //size_t content_len = request->contentLength(); 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"] = LubeConfig.EEPROM_Version; config["DistancePerLube_Default"] = LubeConfig.DistancePerLube_Default; config["DistancePerLube_Rain"] = LubeConfig.DistancePerLube_Rain; config["tankCapacity_ml"] = LubeConfig.tankCapacity_ml; config["amountPerDose_microL"] = LubeConfig.amountPerDose_microL; config["TankRemindAtPercentage"] = LubeConfig.TankRemindAtPercentage; config["PulsePerRevolution"] = LubeConfig.PulsePerRevolution; config["TireWidth_mm"] = LubeConfig.TireWidth_mm; config["TireWidthHeight_Ratio"] = LubeConfig.TireWidthHeight_Ratio; config["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch; config["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm; config["BleedingPulses"] = LubeConfig.BleedingPulses; config["SpeedSource"] = LubeConfig.SpeedSource; config["SpeedSource_Str"] = SpeedSourceString[LubeConfig.SpeedSource]; #ifdef FEATURE_ENABLE_GPS config["GPSBaudRate"] = LubeConfig.GPSBaudRate; config["GPSBaudRate_Str"] = GPSBaudRateString[LubeConfig.GPSBaudRate]; #endif #ifdef FEATURE_ENABLE_CAN config["CANSource"] = LubeConfig.CANSource; config["CANSource_Str"] = CANSourceString[LubeConfig.CANSource]; #endif sprintf(buffer, "0x%08X", LubeConfig.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; persis["tankRemain_microL"] = PersistenceData.tankRemain_microL; persis["TravelDistance_highRes_mm"] = PersistenceData.TravelDistance_highRes_mm; persis["odometer_mm"] = PersistenceData.odometer_mm; persis["odometer"] = PersistenceData.odometer; sprintf(buffer, "0x%08X", PersistenceData.checksum); persis["checksum"] = buffer; serializeJsonPretty(json, *response); response->addHeader("Content-disposition", "attachment; filename=backup.ee.json"); request->send(response); }