reworked String Handling of Enums

This commit is contained in:
2025-08-24 14:10:27 +02:00
parent f735ea7b0d
commit 9cb3a61184
8 changed files with 214 additions and 104 deletions

View File

@@ -68,6 +68,7 @@
// -> 6.90µl / Pulse // -> 6.90µl / Pulse
#define DEFAULT_PUMP_DOSE 7 #define DEFAULT_PUMP_DOSE 7
// --- System status enum with sentinel ---
typedef enum eSystem_Status typedef enum eSystem_Status
{ {
sysStat_Startup, sysStat_Startup,
@@ -76,7 +77,8 @@ typedef enum eSystem_Status
sysStat_Wash, sysStat_Wash,
sysStat_Purge, sysStat_Purge,
sysStat_Error, sysStat_Error,
sysStat_Shutdown sysStat_Shutdown,
SYSSTAT_COUNT // <- sentinel (must be last)
} tSystem_Status; } tSystem_Status;
// Enum for different sources of speed input // Enum for different sources of speed input
@@ -89,13 +91,10 @@ typedef enum SpeedSource_e
SOURCE_GPS, SOURCE_GPS,
SOURCE_CAN, SOURCE_CAN,
SOURCE_OBD2_KLINE, SOURCE_OBD2_KLINE,
SOURCE_OBD2_CAN SOURCE_OBD2_CAN,
SPEEDSOURCE_COUNT // <- sentinel (must be last)
} SpeedSource_t; } SpeedSource_t;
// String representation of SpeedSource enum
extern const char *SpeedSourceString[];
extern const size_t SpeedSourceString_Elements;
// Enum for GPS baud rates // Enum for GPS baud rates
typedef enum GPSBaudRate_e typedef enum GPSBaudRate_e
{ {
@@ -104,23 +103,29 @@ typedef enum GPSBaudRate_e
BAUD_19200, BAUD_19200,
BAUD_38400, BAUD_38400,
BAUD_57600, BAUD_57600,
BAUD_115200 BAUD_115200,
GPSBAUDRATE_COUNT // <- sentinel (must be last)
} GPSBaudRate_t; } GPSBaudRate_t;
// String representation of GPSBaudRate enum
extern const char *GPSBaudRateString[];
extern const size_t GPSBaudRateString_Elements;
// Enum for CAN bus sources // Enum for CAN bus sources
typedef enum CANSource_e typedef enum CANSource_e
{ {
KTM_890_ADV_R_2021, KTM_890_ADV_R_2021,
KTM_1290_SD_R_2023 KTM_1290_SD_R_2023,
CANSOURCE_COUNT // <- sentinel (must be last)
} CANSource_t; } CANSource_t;
// String representation of CANSource enum // String tables (kept internal to the module)
extern const char *CANSourceString[]; extern const char * const SystemStatusString[SYSSTAT_COUNT];
extern const size_t CANSourceString_Elements; extern const char * const SpeedSourceString[SPEEDSOURCE_COUNT];
extern const char * const GPSBaudRateString[GPSBAUDRATE_COUNT];
extern const char * const CANSourceString[CANSOURCE_COUNT];
// Safe getters (centralized bounds check)
const char* ToString(SpeedSource_t v);
const char* ToString(GPSBaudRate_t v);
const char* ToString(CANSource_t v);
const char* ToString(tSystem_Status v);
#define STARTUP_DELAY 2500 #define STARTUP_DELAY 2500
#define SHUTDOWN_DELAY_MS 2500 #define SHUTDOWN_DELAY_MS 2500

View File

@@ -22,7 +22,6 @@ typedef struct Globals_s
{ {
tSystem_Status systemStatus = sysStat_Startup; /**< Current system status */ tSystem_Status systemStatus = sysStat_Startup; /**< Current system status */
tSystem_Status resumeStatus = sysStat_Startup; /**< Status to resume after rain mode */ tSystem_Status resumeStatus = sysStat_Startup; /**< Status to resume after rain mode */
char systemStatustxt[16] = ""; /**< Text representation of system status */
uint16_t purgePulses = 0; /**< Number of purge pulses */ uint16_t purgePulses = 0; /**< Number of purge pulses */
EERequest_t requestEEAction = EE_IDLE; /**< EEPROM-related request */ EERequest_t requestEEAction = EE_IDLE; /**< EEPROM-related request */
char DeviceName[33]; /**< Device name */ char DeviceName[33]; /**< Device name */

View File

@@ -1,7 +1,20 @@
#include "common.h" #include "common.h"
static const char kUnknownStr[] = "Unknown";
// ---- System status string table ----
const char *const SystemStatusString[SYSSTAT_COUNT] = {
"Startup",
"Normal",
"Rain",
"Wash",
"Purge",
"Error",
"Shutdown",
};
// String representation of SpeedSource enum // String representation of SpeedSource enum
const char *SpeedSourceString[] = { const char *const SpeedSourceString[SPEEDSOURCE_COUNT] = {
#ifdef FEATURE_ENABLE_TIMER #ifdef FEATURE_ENABLE_TIMER
"Timer", "Timer",
#endif #endif
@@ -12,10 +25,8 @@ const char *SpeedSourceString[] = {
"OBD2 (CAN)", "OBD2 (CAN)",
}; };
const size_t SpeedSourceString_Elements = sizeof(SpeedSourceString) / sizeof(SpeedSourceString[0]);
// String representation of GPSBaudRate enum // String representation of GPSBaudRate enum
const char *GPSBaudRateString[] = { const char *const GPSBaudRateString[GPSBAUDRATE_COUNT] = {
"4800", "4800",
"9600", "9600",
"19200", "19200",
@@ -24,12 +35,49 @@ const char *GPSBaudRateString[] = {
"115200", "115200",
}; };
const size_t GPSBaudRateString_Elements = sizeof(GPSBaudRateString) / sizeof(GPSBaudRateString[0]);
// String representation of CANSource enum // String representation of CANSource enum
const char *CANSourceString[] = { const char *const CANSourceString[CANSOURCE_COUNT] = {
"KTM 890 Adventure R (2021)", "KTM 890 Adventure R (2021)",
"KTM 1290 Superduke R (2023)", "KTM 1290 Superduke R (2023)",
}; };
const size_t CANSourceString_Elements = sizeof(CANSourceString) / sizeof(CANSourceString[0]); // ---- Centralized, safe getters ----
// ---- Local helper for range check ----
static inline bool in_range(int v, int max_exclusive)
{
return (v >= 0) && (v < max_exclusive);
}
// ---- Safe getter ----
const char *ToString(tSystem_Status v)
{
const int i = static_cast<int>(v);
return in_range(i, static_cast<int>(SYSSTAT_COUNT))
? SystemStatusString[i]
: kUnknownStr;
}
const char *ToString(SpeedSource_t v)
{
const int i = static_cast<int>(v);
return in_range(i, static_cast<int>(SPEEDSOURCE_COUNT))
? SpeedSourceString[i]
: kUnknownStr;
}
const char *ToString(GPSBaudRate_t v)
{
const int i = static_cast<int>(v);
return in_range(i, static_cast<int>(GPSBAUDRATE_COUNT))
? GPSBaudRateString[i]
: kUnknownStr;
}
const char *ToString(CANSource_t v)
{
const int i = static_cast<int>(v);
return in_range(i, static_cast<int>(CANSOURCE_COUNT))
? CANSourceString[i]
: kUnknownStr;
}

View File

@@ -615,21 +615,21 @@ uint32_t ConfigSanityCheck(bool autocorrect)
LubeConfig.BleedingPulses = LubeConfig_defaults.BleedingPulses; LubeConfig.BleedingPulses = LubeConfig_defaults.BleedingPulses;
} }
if (!(LubeConfig.SpeedSource >= 0) || !(LubeConfig.SpeedSource < SpeedSourceString_Elements)) if (!(LubeConfig.SpeedSource >= 0) || !(LubeConfig.SpeedSource < SPEEDSOURCE_COUNT))
{ {
SET_BIT(setting_reset_bits, 11); SET_BIT(setting_reset_bits, 11);
if (autocorrect) if (autocorrect)
LubeConfig.SpeedSource = LubeConfig_defaults.SpeedSource; LubeConfig.SpeedSource = LubeConfig_defaults.SpeedSource;
} }
if (!(LubeConfig.GPSBaudRate >= 0) || !(LubeConfig.GPSBaudRate < GPSBaudRateString_Elements)) if (!(LubeConfig.GPSBaudRate >= 0) || !(LubeConfig.GPSBaudRate < GPSBAUDRATE_COUNT))
{ {
SET_BIT(setting_reset_bits, 12); SET_BIT(setting_reset_bits, 12);
if (autocorrect) if (autocorrect)
LubeConfig.GPSBaudRate = LubeConfig_defaults.GPSBaudRate; LubeConfig.GPSBaudRate = LubeConfig_defaults.GPSBaudRate;
} }
if (!(LubeConfig.CANSource >= 0) || !(LubeConfig.CANSource < CANSourceString_Elements)) if (!(LubeConfig.CANSource >= 0) || !(LubeConfig.CANSource < CANSOURCE_COUNT))
{ {
SET_BIT(setting_reset_bits, 13); SET_BIT(setting_reset_bits, 13);
if (autocorrect) if (autocorrect)

View File

@@ -459,7 +459,6 @@ void Debug_dumpGlobals()
{ {
Debug_pushMessage("systemStatus: %d\n", globals.systemStatus); Debug_pushMessage("systemStatus: %d\n", globals.systemStatus);
Debug_pushMessage("resumeStatus: %d\n", globals.resumeStatus); Debug_pushMessage("resumeStatus: %d\n", globals.resumeStatus);
Debug_pushMessage("systemStatustxt: %s\n", globals.systemStatustxt);
Debug_pushMessage("purgePulses: %d\n", globals.purgePulses); Debug_pushMessage("purgePulses: %d\n", globals.purgePulses);
Debug_pushMessage("requestEEAction: %d\n", globals.requestEEAction); Debug_pushMessage("requestEEAction: %d\n", globals.requestEEAction);
Debug_pushMessage("DeviceName: %s\n", globals.DeviceName); Debug_pushMessage("DeviceName: %s\n", globals.DeviceName);

View File

@@ -56,7 +56,6 @@ void RunLubeApp(uint32_t add_milimeters)
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
strcpy_P(globals.systemStatustxt, PSTR("Startup"));
LEDControl_SetBasic(LED_STARTUP_NORMAL, LED_PATTERN_BLINK); LEDControl_SetBasic(LED_STARTUP_NORMAL, LED_PATTERN_BLINK);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
globals.resumeStatus = sysStat_Startup; globals.resumeStatus = sysStat_Startup;
@@ -72,7 +71,6 @@ void RunLubeApp(uint32_t add_milimeters)
case sysStat_Normal: case sysStat_Normal:
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
strcpy_P(globals.systemStatustxt, PSTR("Normal"));
LEDControl_SetBasic(LED_NORMAL_COLOR, LED_PATTERN_ON); LEDControl_SetBasic(LED_NORMAL_COLOR, LED_PATTERN_ON);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
globals.resumeStatus = sysStat_Normal; globals.resumeStatus = sysStat_Normal;
@@ -89,7 +87,6 @@ void RunLubeApp(uint32_t add_milimeters)
case sysStat_Rain: case sysStat_Rain:
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
strcpy_P(globals.systemStatustxt, PSTR("Rain"));
LEDControl_SetBasic(LED_RAIN_COLOR, LED_PATTERN_ON); LEDControl_SetBasic(LED_RAIN_COLOR, LED_PATTERN_ON);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
globals.resumeStatus = sysStat_Rain; globals.resumeStatus = sysStat_Rain;
@@ -107,7 +104,6 @@ void RunLubeApp(uint32_t add_milimeters)
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
washModeRemainDistance = LubeConfig.WashMode_Distance; washModeRemainDistance = LubeConfig.WashMode_Distance;
strcpy_P(globals.systemStatustxt, PSTR("Wash"));
LEDControl_SetBasic(LED_WASH_COLOR, LED_PATTERN_BREATH); LEDControl_SetBasic(LED_WASH_COLOR, LED_PATTERN_BREATH);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
} }
@@ -134,7 +130,6 @@ void RunLubeApp(uint32_t add_milimeters)
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
globals.purgePulses = LubeConfig.BleedingPulses; globals.purgePulses = LubeConfig.BleedingPulses;
strcpy_P(globals.systemStatustxt, PSTR("Purge"));
LEDControl_SetBasic(LED_PURGE_COLOR, LED_PATTERN_BLINK); LEDControl_SetBasic(LED_PURGE_COLOR, LED_PATTERN_BLINK);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
} }
@@ -161,7 +156,6 @@ void RunLubeApp(uint32_t add_milimeters)
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
strcpy_P(globals.systemStatustxt, PSTR("Error"));
LEDControl_SetBasic(LED_ERROR_COLOR, LED_PATTERN_BLINK_FAST); LEDControl_SetBasic(LED_ERROR_COLOR, LED_PATTERN_BLINK_FAST);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
} }
@@ -173,7 +167,6 @@ void RunLubeApp(uint32_t add_milimeters)
if (lastSystemStatus != globals.systemStatus) if (lastSystemStatus != globals.systemStatus)
{ {
strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
LEDControl_SetBasic(LED_SHUTDOWN_COLOR, LED_PATTERN_BREATH_REVERSE); LEDControl_SetBasic(LED_SHUTDOWN_COLOR, LED_PATTERN_BREATH_REVERSE);
lastSystemStatus = globals.systemStatus; lastSystemStatus = globals.systemStatus;
} }

View File

@@ -398,7 +398,7 @@ void Display_Process()
DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000); DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000);
// Display relevant information on the OLED screen based on system status // Display relevant information on the OLED screen based on system status
u8x8.printf(PSTR("Mode: %10s\n"), globals.systemStatustxt); u8x8.printf(PSTR("Mode: %10s\n"), ToString(globals.systemStatus));
if (globals.systemStatus == sysStat_Error) if (globals.systemStatus == sysStat_Error)
{ {
// Display the last Diagnostic Trouble Code (DTC) in case of an error // Display the last Diagnostic Trouble Code (DTC) in case of an error
@@ -412,7 +412,8 @@ void Display_Process()
u8x8.printf(PSTR("WiFi: %10s\n"), (WiFi.getMode() == WIFI_AP ? "AP" : WiFi.getMode() == WIFI_OFF ? "OFF" u8x8.printf(PSTR("WiFi: %10s\n"), (WiFi.getMode() == WIFI_AP ? "AP" : WiFi.getMode() == WIFI_OFF ? "OFF"
: WiFi.getMode() == WIFI_STA ? "CLIENT" : WiFi.getMode() == WIFI_STA ? "CLIENT"
: "UNKNOWN")); : "UNKNOWN"));
u8x8.printf(PSTR("Source: %8s\n"), SpeedSourceString[LubeConfig.SpeedSource]); u8x8.printf(PSTR("Source: %8s\n"), ToString(LubeConfig.SpeedSource));
u8x8.printf("%s\n", WiFi.localIP().toString().c_str()); u8x8.printf("%s\n", WiFi.localIP().toString().c_str());
} }

View File

@@ -337,89 +337,145 @@ void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const Stri
} }
} }
} }
void WebserverEERestore_Callback(AsyncWebServerRequest *request,
/** const String &filename,
* @brief Callback function for handling EEPROM restore via the web server. size_t index,
* uint8_t *data,
* This function is invoked during the EEPROM restore process when a new EEPROM file size_t len,
* is received. It handles the restore process by reading the data from the received file, bool final)
* deserializing the JSON data, and updating the configuration and persistence data accordingly.
* If the restore is successful, it triggers a system shutdown.
*
* @param request Pointer to the AsyncWebServerRequest object.
* @param filename The name of the file being restored.
* @param index The index of the file being restored.
* @param data Pointer to the data buffer.
* @param len The length of the data buffer.
* @param final Boolean indicating if this is the final chunk of data.
*/
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{ {
constexpr size_t kBufCap = 1536;
bool ee_done = false; bool ee_done = false;
static bool validext = false; static bool validext = false;
static char *buffer = NULL; static char *buffer = nullptr;
static uint32_t read_ptr = 0; static uint32_t read_ptr = 0;
DeserializationError error; DeserializationError error;
// kleines Helferlein zum sicheren Kopieren & Terminieren
auto safe_copy = [](char *dst, size_t dst_sz, const char *src)
{
if (!dst || dst_sz == 0)
return;
if (!src)
{
dst[0] = '\0';
return;
}
strncpy(dst, src, dst_sz - 1);
dst[dst_sz - 1] = '\0';
};
// Grenzen/Hilfen für Enum-Ranges (Sentinel bevorzugt, sonst *_Elements)
const int maxSpeedSrc = static_cast<int>(SPEEDSOURCE_COUNT);
const int maxGPSBaud = static_cast<int>(GPSBAUDRATE_COUNT);
const int maxCANSrc = static_cast<int>(CANSOURCE_COUNT);
if (!index) if (!index)
{ {
validext = (filename.indexOf(".ee.json") > -1); validext = (filename.indexOf(".ee.json") > -1);
if (validext) if (validext)
{ {
buffer = (char *)malloc(1536); buffer = (char *)malloc(kBufCap);
read_ptr = 0; read_ptr = 0;
if (buffer == NULL) if (!buffer)
{
Debug_pushMessage("malloc() failed for EEPROM-Restore\n"); Debug_pushMessage("malloc() failed for EEPROM-Restore\n");
} }
} }
}
if (buffer != NULL && len > 0) // Chunked receive mit Cap/Trunkierungsschutz
if (buffer && len > 0)
{ {
memcpy(buffer + read_ptr, data, len); size_t remain = (read_ptr < kBufCap) ? (kBufCap - read_ptr) : 0;
read_ptr = read_ptr + len; size_t to_copy = (len <= remain) ? len : remain;
if (to_copy > 0)
{
memcpy(buffer + read_ptr, data, to_copy);
read_ptr += to_copy;
}
else
{
Debug_pushMessage("EEPROM-Restore input exceeds buffer, truncating\n");
}
} }
if (final) if (final)
{ {
if (buffer != NULL) if (buffer)
{ {
// Ensure zero-termination just in case // Null-terminieren
if (read_ptr >= 1536) if (read_ptr == kBufCap)
read_ptr = 1535; read_ptr = kBufCap - 1;
buffer[read_ptr] = '\0'; buffer[read_ptr] = '\0';
Serial.print(buffer); // Parse
JsonDocument json; JsonDocument json; // entspricht deinem bisherigen Stil
error = deserializeJson(json, buffer); error = deserializeJson(json, buffer);
if (error) if (error)
{ {
Debug_pushMessage("deserializeJson() failed: %s\n", error.f_str()); Debug_pushMessage("deserializeJson() failed: %s\n", error.f_str());
} }
else else if (validext)
{ {
// ---- Konfiguration sicher in RAM übernehmen ----
// clamp-Helfer passend zu deinen Sanity-Grenzen
auto clamp_u32 = [](uint32_t v, uint32_t lo, uint32_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
auto clamp_u16 = [](uint16_t v, uint16_t lo, uint16_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
auto clamp_u8 = [](uint8_t v, uint8_t lo, uint8_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
LubeConfig.DistancePerLube_Default = json["config"]["DistancePerLube_Default"].as<uint32_t>(); // config.*
LubeConfig.DistancePerLube_Rain = json["config"]["DistancePerLube_Rain"].as<uint32_t>(); LubeConfig.DistancePerLube_Default = clamp_u32(json["config"]["DistancePerLube_Default"].as<uint32_t>(), 0, 50000);
LubeConfig.tankCapacity_ml = json["config"]["tankCapacity_ml"].as<uint32_t>(); LubeConfig.DistancePerLube_Rain = clamp_u32(json["config"]["DistancePerLube_Rain"].as<uint32_t>(), 0, 50000);
LubeConfig.amountPerDose_microL = json["config"]["amountPerDose_microL"].as<uint32_t>(); LubeConfig.tankCapacity_ml = clamp_u32(json["config"]["tankCapacity_ml"].as<uint32_t>(), 0, 5000);
LubeConfig.TankRemindAtPercentage = json["config"]["TankRemindAtPercentage"].as<uint8_t>(); LubeConfig.amountPerDose_microL = clamp_u32(json["config"]["amountPerDose_microL"].as<uint32_t>(), 0, 100);
LubeConfig.PulsePerRevolution = json["config"]["PulsePerRevolution"].as<uint8_t>(); LubeConfig.TankRemindAtPercentage = clamp_u8(json["config"]["TankRemindAtPercentage"].as<uint8_t>(), 0, 100);
LubeConfig.TireWidth_mm = json["config"]["TireWidth_mm"].as<uint32_t>(); LubeConfig.PulsePerRevolution = clamp_u8(json["config"]["PulsePerRevolution"].as<uint8_t>(), 0, 255);
LubeConfig.TireWidthHeight_Ratio = json["config"]["TireWidthHeight_Ratio"].as<uint32_t>(); LubeConfig.TireWidth_mm = clamp_u32(json["config"]["TireWidth_mm"].as<uint32_t>(), 0, 500);
LubeConfig.RimDiameter_Inch = json["config"]["RimDiameter_Inch"].as<uint32_t>(); LubeConfig.TireWidthHeight_Ratio = clamp_u32(json["config"]["TireWidthHeight_Ratio"].as<uint32_t>(), 0, 150);
LubeConfig.DistancePerRevolution_mm = json["config"]["DistancePerRevolution_mm"].as<uint32_t>(); LubeConfig.RimDiameter_Inch = clamp_u32(json["config"]["RimDiameter_Inch"].as<uint32_t>(), 0, 30);
LubeConfig.BleedingPulses = json["config"]["BleedingPulses"].as<uint16_t>(); LubeConfig.DistancePerRevolution_mm = clamp_u32(json["config"]["DistancePerRevolution_mm"].as<uint32_t>(), 0, 10000);
LubeConfig.SpeedSource = (SpeedSource_t)json["config"]["SpeedSource"].as<int>(); LubeConfig.BleedingPulses = clamp_u16(json["config"]["BleedingPulses"].as<uint16_t>(), 0, 1000);
LubeConfig.GPSBaudRate = (GPSBaudRate_t)json["config"]["GPSBaudRate"].as<int>(); LubeConfig.WashMode_Distance = json["config"]["WashMode_Distance"].as<uint16_t>(); // ggf. Grenzen anpassen
LubeConfig.CANSource = (CANSource_t)json["config"]["CANSource"].as<int>(); LubeConfig.WashMode_Interval = json["config"]["WashMode_Interval"].as<uint16_t>(); // ggf. Grenzen anpassen
LubeConfig.LED_Mode_Flash = json["config"]["LED_Mode_Flash"].as<bool>(); LubeConfig.LED_Mode_Flash = json["config"]["LED_Mode_Flash"].as<bool>();
LubeConfig.LED_Max_Brightness = json["config"]["LED_Max_Brightness"].as<uint8_t>(); LubeConfig.LED_Max_Brightness = json["config"]["LED_Max_Brightness"].as<uint8_t>();
LubeConfig.LED_Min_Brightness = json["config"]["LED_Min_Brightness"].as<uint8_t>(); LubeConfig.LED_Min_Brightness = json["config"]["LED_Min_Brightness"].as<uint8_t>();
strncpy(LubeConfig.wifi_ap_ssid, json["config"]["wifi_ap_ssid"].as<const char *>(), sizeof(LubeConfig.wifi_ap_ssid));
strncpy(LubeConfig.wifi_ap_password, json["config"]["wifi_ap_password"].as<const char *>(), sizeof(LubeConfig.wifi_ap_password));
strncpy(LubeConfig.wifi_client_ssid, json["config"]["wifi_client_ssid"].as<const char *>(), sizeof(LubeConfig.wifi_client_ssid));
strncpy(LubeConfig.wifi_client_password, json["config"]["wifi_client_password"].as<const char *>(), sizeof(LubeConfig.wifi_client_password));
// Enums nur nach Range-Check übernehmen
{
int v = json["config"]["SpeedSource"].as<int>();
if (v >= 0 && v < maxSpeedSrc)
LubeConfig.SpeedSource = (SpeedSource_t)v;
else
Debug_pushMessage("Restore: invalid SpeedSource=%d\n", v);
}
{
int v = json["config"]["GPSBaudRate"].as<int>();
if (v >= 0 && v < maxGPSBaud)
LubeConfig.GPSBaudRate = (GPSBaudRate_t)v;
else
Debug_pushMessage("Restore: invalid GPSBaudRate=%d\n", v);
}
{
int v = json["config"]["CANSource"].as<int>();
if (v >= 0 && v < maxCANSrc)
LubeConfig.CANSource = (CANSource_t)v;
else
Debug_pushMessage("Restore: invalid CANSource=%d\n", v);
}
// Strings sicher kopieren (0-terminiert)
safe_copy(LubeConfig.wifi_ap_ssid, sizeof(LubeConfig.wifi_ap_ssid), json["config"]["wifi_ap_ssid"]);
safe_copy(LubeConfig.wifi_ap_password, sizeof(LubeConfig.wifi_ap_password), json["config"]["wifi_ap_password"]);
safe_copy(LubeConfig.wifi_client_ssid, sizeof(LubeConfig.wifi_client_ssid), json["config"]["wifi_client_ssid"]);
safe_copy(LubeConfig.wifi_client_password, sizeof(LubeConfig.wifi_client_password), json["config"]["wifi_client_password"]);
// persis.*
PersistenceData.writeCycleCounter = json["persis"]["writeCycleCounter"].as<uint16_t>(); PersistenceData.writeCycleCounter = json["persis"]["writeCycleCounter"].as<uint16_t>();
PersistenceData.tankRemain_microL = json["persis"]["tankRemain_microL"].as<uint32_t>(); PersistenceData.tankRemain_microL = json["persis"]["tankRemain_microL"].as<uint32_t>();
PersistenceData.TravelDistance_highRes_mm = json["persis"]["TravelDistance_highRes_mm"].as<uint32_t>(); PersistenceData.TravelDistance_highRes_mm = json["persis"]["TravelDistance_highRes_mm"].as<uint32_t>();
@@ -427,24 +483,33 @@ void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &f
PersistenceData.odometer = json["persis"]["odometer"].as<uint32_t>(); PersistenceData.odometer = json["persis"]["odometer"].as<uint32_t>();
PersistenceData.checksum = json["persis"]["checksum"].as<uint32_t>(); PersistenceData.checksum = json["persis"]["checksum"].as<uint32_t>();
// Optional: Sanity-Autokorrektur im RAM (keine EEPROM-Writes hier!)
{
uint32_t sanity = ConfigSanityCheck(true);
if (sanity > 0)
{
MaintainDTC(DTC_EEPROM_CFG_SANITY, true, sanity);
Debug_pushMessage("Restore: ConfigSanity corrected (mask=0x%08lX)\n", sanity);
}
}
ee_done = true; ee_done = true;
} }
}
if (buffer)
{
free(buffer); free(buffer);
buffer = NULL; buffer = nullptr;
} }
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots"); // Browser zurückleiten & ggf. Shutdown
AsyncWebServerResponse *response =
request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20"); response->addHeader("Refresh", "20");
response->addHeader("Location", "/"); response->addHeader("Location", "/");
request->send(response); request->send(response);
if (ee_done) if (ee_done)
{ {
Debug_pushMessage("Update complete\n"); Debug_pushMessage("EEPROM restore complete\n");
globals.systemStatus = sysStat_Shutdown; globals.systemStatus = sysStat_Shutdown;
} }
} }
@@ -648,24 +713,24 @@ void Websocket_HandleSettings(uint8_t *data)
} }
else if (strcmp(identifier, "speedsource") == 0) else if (strcmp(identifier, "speedsource") == 0)
{ {
int index = findIndexByString(value, SpeedSourceString, (int)SpeedSourceString_Elements); int index = findIndexByString(value, SpeedSourceString, (int)SPEEDSOURCE_COUNT);
if (validIndex(index, (int)SpeedSourceString_Elements)) if (validIndex(index, (int)SPEEDSOURCE_COUNT))
speedsourcePreselect = (SpeedSource_t)index; speedsourcePreselect = (SpeedSource_t)index;
else else
Debug_pushMessage("Invalid speedsource '%s'\n", value); Debug_pushMessage("Invalid speedsource '%s'\n", value);
} }
else if (strcmp(identifier, "cansource") == 0) else if (strcmp(identifier, "cansource") == 0)
{ {
int index = findIndexByString(value, CANSourceString, (int)CANSourceString_Elements); int index = findIndexByString(value, CANSourceString, (int)CANSOURCE_COUNT);
if (validIndex(index, (int)CANSourceString_Elements)) if (validIndex(index, (int)CANSOURCE_COUNT))
LubeConfig.CANSource = (CANSource_t)index; LubeConfig.CANSource = (CANSource_t)index;
else else
Debug_pushMessage("Invalid cansource '%s'\n", value); Debug_pushMessage("Invalid cansource '%s'\n", value);
} }
else if (strcmp(identifier, "gpsbaud") == 0) else if (strcmp(identifier, "gpsbaud") == 0)
{ {
int index = findIndexByString(value, GPSBaudRateString, (int)GPSBaudRateString_Elements); int index = findIndexByString(value, GPSBaudRateString, (int)GPSBAUDRATE_COUNT);
if (validIndex(index, (int)GPSBaudRateString_Elements)) if (validIndex(index, (int)GPSBAUDRATE_COUNT))
LubeConfig.GPSBaudRate = (GPSBaudRate_t)index; LubeConfig.GPSBaudRate = (GPSBaudRate_t)index;
else else
Debug_pushMessage("Invalid gpsbaud '%s'\n", value); Debug_pushMessage("Invalid gpsbaud '%s'\n", value);
@@ -801,7 +866,7 @@ void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
String temp = "STATUS:"; String temp = "STATUS:";
temp.concat(String(nz(globals.systemStatustxt)) + ";"); temp.concat(String(ToString(globals.systemStatus)) + ";");
// Guard against division by zero (capacity==0) // Guard against division by zero (capacity==0)
uint32_t cap = LubeConfig.tankCapacity_ml; uint32_t cap = LubeConfig.tankCapacity_ml;
@@ -856,26 +921,26 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
temp += String(LubeConfig.RimDiameter_Inch) + ";"; temp += String(LubeConfig.RimDiameter_Inch) + ";";
// speedsource + Optionen // speedsource + Optionen
temp += tableStr(SpeedSourceString, (int)LubeConfig.SpeedSource, (int)SpeedSourceString_Elements) + ";"; temp += String(ToString(LubeConfig.SpeedSource)) + ";";
{ {
String csv; String csv;
appendCsv(csv, SpeedSourceString, SpeedSourceString_Elements); appendCsv(csv, SpeedSourceString, SPEEDSOURCE_COUNT);
temp += csv + ";"; temp += csv + ";";
} }
// gpsbaud + Optionen // gpsbaud + Optionen
temp += tableStr(GPSBaudRateString, (int)LubeConfig.GPSBaudRate, (int)GPSBaudRateString_Elements) + ";"; temp += String(ToString(LubeConfig.GPSBaudRate)) + ";";
{ {
String csv; String csv;
appendCsv(csv, GPSBaudRateString, GPSBaudRateString_Elements); appendCsv(csv, GPSBaudRateString, GPSBAUDRATE_COUNT);
temp += csv + ";"; temp += csv + ";";
} }
// cansource + Optionen // cansource + Optionen
temp += tableStr(CANSourceString, (int)LubeConfig.CANSource, (int)CANSourceString_Elements) + ";"; temp += String(ToString(LubeConfig.CANSource)) + ";";
{ {
String csv; String csv;
appendCsv(csv, CANSourceString, CANSourceString_Elements); appendCsv(csv, CANSourceString, CANSOURCE_COUNT);
temp += csv + ";"; temp += csv + ";";
} }