diff --git a/Software/ChainLube/platformio.ini b/Software/ChainLube/platformio.ini index d765d61..2f5d77d 100644 --- a/Software/ChainLube/platformio.ini +++ b/Software/ChainLube/platformio.ini @@ -26,7 +26,7 @@ upload_speed = 921600 build_flags = !python git_rev_macro.py - -DWIFI_NOCLIENT + -DWIFI_CLIENT -DWIFI_SSID=${wifi_cred.wifi_ssid} -DWIFI_PASSWORD=${wifi_cred.wifi_password} -DADMIN_PASSWORD=${wifi_cred.admin_password} diff --git a/Software/ChainLube/src/config.cpp b/Software/ChainLube/src/config.cpp index 5e555ac..36bbfc5 100644 --- a/Software/ChainLube/src/config.cpp +++ b/Software/ChainLube/src/config.cpp @@ -1,7 +1,12 @@ -#include #include "config.h" LubeConfig_t LubeConfig; +persistenceData_t PersistenceData; +uint16_t eePersistenceMarker = 0; +uint16_t eeVersion = 0; // inc + +const uint16_t startofLubeConfig = sizeof(eePersistenceMarker); +const uint16_t startofPersistence = sizeof(LubeConfig); void StoreConfig_EEPROM() { @@ -9,7 +14,7 @@ void StoreConfig_EEPROM() LubeConfig.checksum = Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)); EEPROM.begin(512); - EEPROM.put(0, LubeConfig); + EEPROM.put(startofLubeConfig, LubeConfig); EEPROM.commit(); EEPROM.end(); } @@ -17,16 +22,81 @@ void StoreConfig_EEPROM() void GetConfig_EEPROM() { EEPROM.begin(512); - EEPROM.get(0, LubeConfig); + EEPROM.get(startofLubeConfig, LubeConfig); EEPROM.end(); uint32_t checksum = LubeConfig.checksum; LubeConfig.checksum = 0; if (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) == checksum) - Serial.printf("Checksum OK"); + Serial.printf("CFG EEPROM Checksum OK\n"); else - Serial.printf("Checksum BAD"); + Serial.printf("CFG EEPROM Checksum BAD\n"); + LubeConfig.checksum = checksum; +} + +void StorePersistence_EEPROM() +{ + EEPROM.begin(512); + + if (PersistenceData.writeCycleCounter == 0xFFFF) + { + PersistenceData.writeCycleCounter = 0; + eePersistenceMarker += sizeof(PersistenceData); + EEPROM.put(0, eePersistenceMarker); + } + else + { + PersistenceData.writeCycleCounter++; + } + + uint16_t PersistenceDataAddress = startofPersistence + eePersistenceMarker; + + PersistenceData.checksum = 0; + PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)); + + EEPROM.put(PersistenceDataAddress, PersistenceData); + EEPROM.commit(); + EEPROM.end(); +} + +void GetPersistence_EEPROM() +{ + EEPROM.begin(512); + EEPROM.get(0, eePersistenceMarker); + uint16_t PersistenceDataAddress = startofPersistence + eePersistenceMarker; + + EEPROM.get(PersistenceDataAddress, PersistenceData); + EEPROM.end(); + + uint32_t checksum = PersistenceData.checksum; + PersistenceData.checksum = 0; + + if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum) + { + Serial.printf("Persistance EEPROM Checksum OK\n"); + } + else + { + Serial.printf("Persistance EEPROM Checksum BAD\n"); + FormatPersistence_EEPROM(); + } + PersistenceData.checksum = checksum; +} + +void FormatConfig_EEPROM() +{ + LubeConfig_t defaults; + LubeConfig = defaults; + StoreConfig_EEPROM(); +} + +void FormatPersistence_EEPROM() +{ + persistenceData_t defaults; + PersistenceData = defaults; + eePersistenceMarker = 0; + StorePersistence_EEPROM(); } uint32_t Checksum_EEPROM(uint8_t const *data, size_t len) diff --git a/Software/ChainLube/src/config.h b/Software/ChainLube/src/config.h index 68f9e57..0687a77 100644 --- a/Software/ChainLube/src/config.h +++ b/Software/ChainLube/src/config.h @@ -4,13 +4,58 @@ #include #include +typedef enum SpeedSource_e +{ + SOURCE_TIME, + SOURCE_IMPULSE, + SOURCE_GPS, + SOURCE_CAN +} SpeedSource_t; + +typedef enum GPSBaudRate_e +{ + BAUD_9600, + BAUD_115200 +} GPSBaudRate_t; + +const char GPSBaudRateString[][7] = { + "9600", + "115200"}; + +const size_t GPSBaudRateString_Elements = sizeof(GPSBaudRateString) / sizeof(GPSBaudRateString[0]); + +typedef enum CANSource_e +{ + KTM_890_ADV_R_2021 +} CANSource_t; + +const char CANSourceString[][28] = { + "KTM 890 Adventure R (2021)"}; + +const char CANSourceString_Elements = sizeof(CANSourceString) / sizeof(CANSourceString[0]); + +const char SpeedSourceString[][8] = { + "Timer", + "Impuls", + "GPS", + "CAN-Bus"}; + +const size_t SpeedSourceString_Elements = sizeof(SpeedSourceString) / sizeof(SpeedSourceString[0]); + +typedef struct +{ + uint16_t writeCycleCounter = 0; + uint32_t tankRemain_µl = 0; + uint32_t distanceTraveled_m = 0; + uint32_t checksum = 0; +} persistenceData_t; + typedef struct { uint32_t DistancePerLube_Default = 0; uint32_t DistancePerLube_Rain = 0; uint32_t tankCapacity_ml = 320; uint32_t amountPerDose_µl = 0; - uint32_t tankRemain_µl = 0; uint8_t TankRemindAtPercentage = 30; uint8_t PulsePerRevolution = 1; uint32_t TireWidth_mm = 150; @@ -18,13 +63,21 @@ typedef struct uint32_t RimDiameter_Inch = 18; uint32_t DistancePerRevolution_mm = 2000; uint8_t BleedingPulses = 25; + SpeedSource_t SpeedSource = SOURCE_IMPULSE; + GPSBaudRate_t GPSBaudRate = BAUD_9600; + CANSource_t CANSource = KTM_890_ADV_R_2021; uint32_t checksum = 0; } LubeConfig_t; void StoreConfig_EEPROM(); void GetConfig_EEPROM(); +void StorePersistence_EEPROM(); +void GetPersistence_EEPROM(); +void FormatConfig_EEPROM(); +void FormatPersistence_EEPROM(); uint32_t Checksum_EEPROM(uint8_t const *data, size_t len); extern LubeConfig_t LubeConfig; - +extern persistenceData_t PersistenceData; +extern uint16_t eePersistenceMarker; #endif // _CONFIG_H_ \ No newline at end of file diff --git a/Software/ChainLube/src/globals.h b/Software/ChainLube/src/globals.h index 61bbdab..31200ba 100644 --- a/Software/ChainLube/src/globals.h +++ b/Software/ChainLube/src/globals.h @@ -5,12 +5,12 @@ typedef enum eSystem_Status { - sysStat_NOP, sysStat_Startup, sysStat_Normal, sysStat_Rain, sysStat_Purge, - sysStat_Error + sysStat_Error, + sysStat_Shutdown } tSystem_Status; diff --git a/Software/ChainLube/src/main.cpp b/Software/ChainLube/src/main.cpp index 992f4c3..9a8fa54 100644 --- a/Software/ChainLube/src/main.cpp +++ b/Software/ChainLube/src/main.cpp @@ -46,14 +46,18 @@ CRGB leds[1]; // Function-Prototypes String IpAddress2String(const IPAddress &ipAddress); void processCmdRemoteDebug(); +void RemoteDebug_formatCFG(); +void RemoteDebug_formatPersistence(); void RemotDebug_printSystemInfo(); void RemoteDebug_printWifiInfo(); +void RemoteDebug_CheckEEPOM(); void updateWebUITicker_callback(); void IRAM_ATTR trigger_ISR(); void LED_Process(uint8_t override = false, CRGB setColor = CRGB::White); void Display_Process(); void Button_Process(); -void startWiFiAP(); +void toggleWiFiAP(boolean shutdown = false); +void SystemShutdown(); #ifdef WIFI_CLIENT void wifiMaintainConnectionTicker_callback(); @@ -85,6 +89,7 @@ void setup() Serial.println(DeviceName); GetConfig_EEPROM(); + GetPersistence_EEPROM(); u8x8.begin(); u8x8.setFont(u8x8_font_chroma48medium8_r); @@ -105,6 +110,8 @@ void setup() Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes) Debug.showColors(true); // Colors Debug.setPassword(QUOTE(ADMIN_PASSWORD)); + Debug.setSerialEnabled(true); + Debug.showDebugLevel(true); Debug.setHelpProjectsCmds(helpCmd); Debug.setCallBackProjectCmds(&processCmdRemoteDebug); @@ -148,6 +155,7 @@ void setup() initWebUI(); UpdateWebUITicker.start(); + Serial.println("Setup Done"); } void loop() @@ -164,6 +172,8 @@ void loop() #ifdef WIFI_CLIENT WiFiMaintainConnectionTicker.update(); #endif + if (globals.systemStatus == sysStat_Shutdown) + SystemShutdown(); yield(); } @@ -183,6 +193,24 @@ void processCmdRemoteDebug() RemotDebug_printSystemInfo(); else if (lastCmd == "netinfo") RemoteDebug_printWifiInfo(); + else if (lastCmd == "formatCFG") + RemoteDebug_formatCFG(); + else if (lastCmd == "formatPDS") + RemoteDebug_formatPersistence(); + else if (lastCmd == "checkEE") + RemoteDebug_CheckEEPOM(); +} + +void RemoteDebug_formatCFG() +{ + debugA("Formatting Config-EEPROM and reseting to default"); + FormatConfig_EEPROM(); +} + +void RemoteDebug_formatPersistence() +{ + debugA("Formatting Persistence-EEPROM and reseting to default"); + FormatPersistence_EEPROM(); } void RemotDebug_printSystemInfo() @@ -204,12 +232,36 @@ void RemotDebug_printSystemInfo() : ideMode == FM_DOUT ? "DOUT" : "UNKNOWN")); debugA("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD)); + debugA("Git-Revison: %s", GIT_REV); } void RemoteDebug_printWifiInfo() { } +void RemoteDebug_CheckEEPOM() +{ + uint32_t checksum = PersistenceData.checksum; + PersistenceData.checksum = 0; + + if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum) + debugA("PersistenceData EEPROM Checksum OK\n"); + else + debugA("PersistenceData EEPROM Checksum BAD\n"); + + PersistenceData.checksum = checksum; + + checksum = LubeConfig.checksum; + LubeConfig.checksum = 0; + + if (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) == checksum) + debugA("LubeConfig EEPROM Checksum OK\n"); + else + debugA("LubeConfig EEPROM Checksum BAD\n"); + + LubeConfig.checksum = checksum; +} + #ifdef WIFI_CLIENT void wifiMaintainConnectionTicker_callback() { @@ -229,7 +281,7 @@ void wifiMaintainConnectionTicker_callback() else { debugV("WiFi not connected! - Start AP"); - startWiFiAP(); + toggleWiFiAP(); } } } @@ -260,7 +312,7 @@ void LED_Process(uint8_t override, CRGB SetColor) LED_Override } tLED_Status; - static tSystem_Status oldSysStatus = sysStat_NOP; + static tSystem_Status oldSysStatus = sysStat_Startup; static tLED_Status LED_Status = LED_Startup; static CRGB LED_override_color = 0; static tLED_Status LED_ResumeOverrideStatus = LED_Startup; @@ -316,14 +368,14 @@ void LED_Process(uint8_t override, CRGB SetColor) LED_Status = LED_Error; debugV("sysStat: Error"); break; - case sysStat_NOP: + case sysStat_Shutdown: default: break; } oldSysStatus = globals.systemStatus; } - uint32_t percentage = LubeConfig.tankRemain_µl / (LubeConfig.tankCapacity_ml * 10); + uint32_t percentage = PersistenceData.tankRemain_µl / (LubeConfig.tankCapacity_ml * 10); switch (LED_Status) { @@ -348,7 +400,12 @@ void LED_Process(uint8_t override, CRGB SetColor) break; case LED_Normal: - leds[0] = timer % 2000 > 1950 ? CRGB(0, 255, 0) : CRGB(0, 4, 0); + if (timer % 2000 > 1950) + leds[0] = CRGB(0, 255, 0); + else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850) + leds[0] = CRGB(255, 128, 0); + else + leds[0] = CRGB(0, 4, 0); break; case LED_Confirm_Rain: @@ -363,7 +420,12 @@ void LED_Process(uint8_t override, CRGB SetColor) break; case LED_Rain: - leds[0] = timer % 2000 > 1950 ? CRGB(0, 0, 255) : CRGB(0, 0, 4); + if (timer % 2000 > 1950) + leds[0] = CRGB(0, 0, 255); + else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850) + leds[0] = CRGB(255, 128, 0); + else + leds[0] = CRGB(0, 0, 4); break; case LED_Purge: @@ -395,9 +457,12 @@ void Display_Process() u8x8.setCursor(0, 3); uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain; DistRemain -= TravelDistance_highRes / 1000; - u8x8.printf("Mode: %10s\n\n", globals.systemStatustxt); - u8x8.printf("next Lube: %4dm\n\n", DistRemain); - u8x8.printf("Tank: %8dml\n\n", LubeConfig.tankRemain_µl / 1000); + u8x8.printf("Mode: %10s\n", globals.systemStatustxt); + u8x8.printf("next Lube: %4dm\n", DistRemain); + u8x8.printf("Tank: %8dml\n", PersistenceData.tankRemain_µl / 1000); + u8x8.printf("WiFi: %10s\n", (WiFi.getMode() == WIFI_AP ? "AP" : WiFi.getMode() == WIFI_OFF ? "OFF" + : WiFi.getMode() == WIFI_STA ? "CLIENT" + : "UNKNOWN")); u8x8.refreshDisplay(); } @@ -456,7 +521,7 @@ void Button_Process() switch (buttonAction) { case BTN_TOGGLEWIFI: - startWiFiAP(); + toggleWiFiAP(); debugV("Starting WiFi AP"); break; @@ -496,12 +561,15 @@ void Button_Process() } } -void startWiFiAP() +void toggleWiFiAP(boolean shutdown) { if (WiFi.getMode() != WIFI_OFF) { WiFi.mode(WIFI_OFF); debugV("WiFi turned off"); +#ifdef WIFI_CLIENT + WiFiMaintainConnectionTicker.stop(); +#endif } else { @@ -515,4 +583,10 @@ void startWiFiAP() debugV("WiFi AP started"); #endif } +} + +void SystemShutdown() +{ + StoreConfig_EEPROM(); + ESP.restart(); } \ No newline at end of file diff --git a/Software/ChainLube/src/rmtdbghelp.h b/Software/ChainLube/src/rmtdbghelp.h index 7f2356e..3a1a5ba 100644 --- a/Software/ChainLube/src/rmtdbghelp.h +++ b/Software/ChainLube/src/rmtdbghelp.h @@ -1,2 +1,5 @@ const char helpCmd[] = "sysinfo - System Info\r\n" - "netinfo - WiFi Info\r\n"; \ No newline at end of file + "netinfo - WiFi Info\r\n" + "formatPDS - Format Persistence EEPROM Data\r\n" + "formatCFG - Format Configuration EEPROM Data\r\n" + "checkEE - Check EEPROM with checksum\r\n"; \ No newline at end of file diff --git a/Software/ChainLube/src/webui.cpp b/Software/ChainLube/src/webui.cpp index 25a1121..6ae8d3f 100644 --- a/Software/ChainLube/src/webui.cpp +++ b/Software/ChainLube/src/webui.cpp @@ -10,6 +10,9 @@ uint16_t num_lubedist_normal; uint16_t num_lubedist_rain; uint16_t button_lubedist; +uint16_t option_speedsource; +uint16_t button_changeSource; + uint16_t num_wheel_width; uint16_t num_wheel_ratio; uint16_t num_wheel_rim; @@ -17,6 +20,10 @@ uint16_t button_wheelcalc; uint16_t num_wheel_ppr; uint16_t num_wheel_dist; +uint16_t option_gps_baudrate; + +uint16_t option_can_source; + uint16_t num_tank_capacity; uint16_t num_tank_notify; uint16_t num_dose_per_pulse; @@ -54,6 +61,8 @@ void SettingChanged_Callback(Control *sender, int type) LubeConfig.amountPerDose_µl = ESPUI.getControl(num_dose_per_pulse)->value.toInt(); else if (sender->id == num_purge_pulses) LubeConfig.BleedingPulses = ESPUI.getControl(num_purge_pulses)->value.toInt(); + else if (sender->id == option_gps_baudrate) + LubeConfig.GPSBaudRate = (GPSBaudRate_t)ESPUI.getControl(option_gps_baudrate)->value.toInt(); } void buttons_Callback(Control *sender, int type) @@ -72,8 +81,8 @@ void buttons_Callback(Control *sender, int type) if (sender->id == button_reset_tank) { - LubeConfig.tankRemain_µl = LubeConfig.tankCapacity_ml * 1000; - ESPUI.print(label_tankRemain, String(LubeConfig.tankRemain_µl / 1000)); + PersistenceData.tankRemain_µl = LubeConfig.tankCapacity_ml * 1000; + ESPUI.print(label_tankRemain, String(PersistenceData.tankRemain_µl / 1000)); } if (sender->id == button_purge) @@ -97,6 +106,20 @@ void buttons_Callback(Control *sender, int type) } } +void speedSourceSelect_callback(Control *sender, int type) +{ + if (sender->id == option_speedsource) + { + LubeConfig.SpeedSource = (SpeedSource_t)ESPUI.getControl(option_speedsource)->value.toInt(); + Serial.printf("LubeConfig.SpeedSource: %d", LubeConfig.SpeedSource); + } + + if (sender->id == button_changeSource) + { + globals.systemStatus = sysStat_Shutdown; + } +} + void initWebUI() { tab_lube = ESPUI.addControl(ControlType::Tab, "Dosierung", "Dosierung"); @@ -108,18 +131,46 @@ void initWebUI() num_lubedist_normal = ESPUI.addControl(ControlType::Number, "Öl-Impuls alle x Meter (Normal)", String(LubeConfig.DistancePerLube_Default), ControlColor::Emerald, tab_lube, &SettingChanged_Callback); num_lubedist_rain = ESPUI.addControl(ControlType::Number, "Öl-Impuls alle x Meter (Regen)", String(LubeConfig.DistancePerLube_Rain), ControlColor::Emerald, tab_lube, &SettingChanged_Callback); - num_wheel_width = ESPUI.addControl(ControlType::Number, "Reifenbreite (mm)", String(LubeConfig.TireWidth_mm), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); - num_wheel_ratio = ESPUI.addControl(ControlType::Number, "Höhe/Breite-Verhältniss", String(LubeConfig.TireWidthHeight_Ratio), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); - num_wheel_rim = ESPUI.addControl(ControlType::Number, "Felgendurchmesser (Zoll)", String(LubeConfig.RimDiameter_Inch), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); - button_wheelcalc = ESPUI.addControl(ControlType::Button, "Abrollumfang aus Reifendaten berechnen", "Berechnen", ControlColor::Peterriver, tab_wheel, &buttons_Callback); - num_wheel_dist = ESPUI.addControl(ControlType::Number, "Wegstrecke pro Radumdrehung (mm)", String(LubeConfig.DistancePerRevolution_mm), ControlColor::Wetasphalt, tab_wheel, &SettingChanged_Callback); - num_wheel_ppr = ESPUI.addControl(ControlType::Number, "Sensorimpulse pro Umdrehung", String(LubeConfig.PulsePerRevolution), ControlColor::Wetasphalt, tab_wheel, &SettingChanged_Callback); + option_speedsource = ESPUI.addControl(ControlType::Select, "Geschwindigkeit Quelle", "", ControlColor::Wetasphalt, tab_wheel, &speedSourceSelect_callback); + button_changeSource = ESPUI.addControl(ControlType::Button, "neue Quelle übernehmen. ACHTUNG: Gerät startet neu!", "Übernehmen", ControlColor::Wetasphalt, tab_wheel, &speedSourceSelect_callback); + for (int i = 0; i < SpeedSourceString_Elements; i++) + { + ESPUI.addControl(ControlType::Option, SpeedSourceString[i], String(i), ControlColor::Wetasphalt, option_speedsource); + } + + switch (LubeConfig.SpeedSource) + { + case SOURCE_IMPULSE: + num_wheel_width = ESPUI.addControl(ControlType::Number, "Reifenbreite (mm)", String(LubeConfig.TireWidth_mm), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + num_wheel_ratio = ESPUI.addControl(ControlType::Number, "Höhe/Breite-Verhältniss", String(LubeConfig.TireWidthHeight_Ratio), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + num_wheel_rim = ESPUI.addControl(ControlType::Number, "Felgendurchmesser (Zoll)", String(LubeConfig.RimDiameter_Inch), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + button_wheelcalc = ESPUI.addControl(ControlType::Button, "Abrollumfang aus Reifendaten berechnen", "Berechnen", ControlColor::Peterriver, tab_wheel, &buttons_Callback); + num_wheel_dist = ESPUI.addControl(ControlType::Number, "Wegstrecke pro Radumdrehung (mm)", String(LubeConfig.DistancePerRevolution_mm), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + num_wheel_ppr = ESPUI.addControl(ControlType::Number, "Sensorimpulse pro Umdrehung", String(LubeConfig.PulsePerRevolution), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + break; + case SOURCE_GPS: + option_gps_baudrate = ESPUI.addControl(ControlType::Select, "Baudrate GPS-Modul", "", ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + for (int i = 0; i < GPSBaudRateString_Elements; i++) + { + ESPUI.addControl(ControlType::Option, GPSBaudRateString[i], String(i), ControlColor::Peterriver, option_gps_baudrate); + } + break; + case SOURCE_CAN: + option_can_source = ESPUI.addControl(ControlType::Select, "CAN-Bus Quelle", "", ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback); + for (int i = 0; i < CANSourceString_Elements; i++) + { + ESPUI.addControl(ControlType::Option, CANSourceString[i], String(i), ControlColor::Peterriver, option_can_source); + } + break; + case SOURCE_TIME: + break; + } num_tank_capacity = ESPUI.addControl(ControlType::Number, "Tankinhalt maximal (ml)", String(LubeConfig.tankCapacity_ml), ControlColor::Carrot, tab_tank, &SettingChanged_Callback); num_tank_notify = ESPUI.addControl(ControlType::Number, "Tankinhalt Warnung (%)", String(LubeConfig.TankRemindAtPercentage), ControlColor::Carrot, tab_tank, &SettingChanged_Callback); num_dose_per_pulse = ESPUI.addControl(ControlType::Number, "Menge pro Pumpenimpuls (µl)", String(LubeConfig.amountPerDose_µl), ControlColor::Carrot, tab_tank, &SettingChanged_Callback); - label_tankRemain = ESPUI.addControl(ControlType::Label, "Tankinhalt verbleibend (ml, geschätzt)", String(LubeConfig.tankRemain_µl / 1000), ControlColor::Alizarin, tab_maintenance); + label_tankRemain = ESPUI.addControl(ControlType::Label, "Tankinhalt verbleibend (ml, geschätzt)", String(PersistenceData.tankRemain_µl / 1000), ControlColor::Alizarin, tab_maintenance); button_reset_tank = ESPUI.addControl(ControlType::Button, "Tankinhalt zurücksetzen", "Reset", ControlColor::Alizarin, tab_maintenance, &buttons_Callback); num_purge_pulses = ESPUI.addControl(ControlType::Number, "Entlüftung Impulse", String(LubeConfig.BleedingPulses), ControlColor::Alizarin, tab_maintenance, &SettingChanged_Callback); button_purge = ESPUI.addControl(ControlType::Button, "Leitung Entlüften", "Start", ControlColor::Alizarin, tab_maintenance, &buttons_Callback); @@ -133,5 +184,5 @@ void initWebUI() void UpdateWebUI() { - ESPUI.print(label_tankRemain, String(LubeConfig.tankRemain_µl / 1000) + " ml" ); + ESPUI.print(label_tankRemain, String(PersistenceData.tankRemain_µl / 1000) + " ml"); } \ No newline at end of file