8 Commits

15 changed files with 178 additions and 65 deletions

View File

@@ -1,13 +1,10 @@
# SCRIPT TO GZIP CRITICAL FILES FOR ACCELERATED WEBSERVING
# see also https://community.platformio.org/t/question-esp32-compress-files-in-data-to-gzip-before-upload-possible-to-spiffs/6274/10
import glob import glob
import shutil import shutil
import gzip import gzip
import os import os
import subprocess import subprocess
import platform import platform
from os import popen
Import("env") Import("env")
Import("projenv") Import("projenv")
@@ -169,8 +166,13 @@ def gzip_webfiles(source, target, env):
return return
def gzip_binffiles(source, target, env): def gzip_binffiles(source, target, env):
git_revision = popen('git rev-parse --short HEAD').read().strip()
custom_flash_version = env.GetProjectOption("custom_flash_version", "0.99")
# Format the target file name
target_filename = f"filesystem_{custom_flash_version}_{git_revision}.fs"
littlefsbin = target[0].get_abspath() littlefsbin = target[0].get_abspath()
targetbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs') targetbin = os.path.join(os.path.dirname(littlefsbin), target_filename)
shutil.copyfile(littlefsbin, targetbin) shutil.copyfile(littlefsbin, targetbin)
gzip_file(targetbin, os.path.join(str(targetbin) + '.gz')) gzip_file(targetbin, os.path.join(str(targetbin) + '.gz'))
os.remove(targetbin) os.remove(targetbin)

View File

@@ -7,8 +7,27 @@ from os import popen
git_revision = popen('git rev-parse --short HEAD').read().strip() git_revision = popen('git rev-parse --short HEAD').read().strip()
env.Replace(PROGNAME="firmware_%s.fw" % git_revision) # Versionsnummern aus platformio.ini holen
env.Append(CPPDEFINES=[('GIT_REV', '\\"{}\\"'.format(git_revision))]) custom_firmware_version = env.GetProjectOption("custom_firmware_version", "0.99")
custom_flash_version = env.GetProjectOption("custom_flash_version", "0.99")
# Versionsnummern aufteilen in Major und Minor
fw_major, fw_minor = custom_firmware_version.split('.')
fl_major, fl_minor = custom_flash_version.split('.')
# Version in Datei "version" im Ordner "data_src" überschreiben
with open('data_src/version', 'w') as version_file:
version_file.write(custom_flash_version)
# Build-Flags setzen
env.Replace(PROGNAME="firmware_%s_%s.fw" % (custom_firmware_version, git_revision))
env.Append(CPPDEFINES=[
('GIT_REV', '\\"{}\\"'.format(git_revision)),
('FW_MAJOR', fw_major),
('FW_MINOR', fw_minor),
('FL_MAJOR', fl_major),
('FL_MINOR', fl_minor)
])
struct2json.struct2json() struct2json.struct2json()
dtcs.build_dtcs() dtcs.build_dtcs()

View File

@@ -35,7 +35,7 @@
<nav class="navbar fixed-top navbar-dark bg-primary" id="navbar1"> <nav class="navbar fixed-top navbar-dark bg-primary" id="navbar1">
<a class="navbar-brand" href="#"> <a class="navbar-brand" href="#">
<img src="static/img/logo.png" width="30" height="30" class="d-inline-block align-top mr-1" alt=""> <img src="static/img/logo.png" width="30" height="30" class="d-inline-block align-top mr-1" alt="">
DE Airsoft Timer <span class="data-devicename">DE Airsoft Timer</span>
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar"
aria-controls="collapsingNavbar" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="collapsingNavbar" aria-expanded="false" aria-label="Toggle navigation">
@@ -63,7 +63,7 @@
<div class="col text-center"> <div class="col text-center">
<div class="jumbotron"> <div class="jumbotron">
<img src="static/img/logo.png" width="120" height="120" class="img-fluid" alt=""> <img src="static/img/logo.png" width="120" height="120" class="img-fluid" alt="">
<h3 class="pt-3">Dark Emergency Timer</h3> <h3 class="pt-3"><span class="data-devicename">DE Airsoft Timer</span></h3>
</div> </div>
</div> </div>
<!-- Div Group Battery remain --> <!-- Div Group Battery remain -->
@@ -96,14 +96,14 @@
<div id="header_faction3" class="col text-center data-name_faction3 text-white p-3">%NAME_FAC_3%</div> <div id="header_faction3" class="col text-center data-name_faction3 text-white p-3">%NAME_FAC_3%</div>
</div> </div>
<div class="row"> <div class="row">
<div class="col bg-dark text-white p-3"><img src="static/img/logo_fac1.png" <div class="col bg-dark text-white p-3 data-activefaction faction-logo faction1">
class="rounded mx-auto img-fluid d-block" alt="..."> <img src="static/img/logo_fac1.png" class="rounded mx-auto img-fluid d-block" alt="...">
</div> </div>
<div class="col bg-dark text-white p-3"><img src="static/img/logo_fac2.png" <div class="col bg-dark text-white p-3 data-activefaction faction-logo faction2">
class="rounded mx-auto img-fluid d-block" alt="..."> <img src="static/img/logo_fac2.png" class="rounded mx-auto img-fluid d-block" alt="...">
</div> </div>
<div class="col bg-dark text-white p-3"><img src="static/img/logo_fac3.png" <div class="col bg-dark text-white p-3 data-activefaction faction-logo faction3">
class="rounded mx-auto img-fluid d-block" alt="..."> <img src="static/img/logo_fac3.png" class="rounded mx-auto img-fluid d-block" alt="...">
</div> </div>
</div> </div>
<div class="row"> <div class="row">

View File

@@ -8439,3 +8439,22 @@ a.text-dark:hover {
.navbar-dark.bg-primary { .navbar-dark.bg-primary {
background-color: #111 !important background-color: #111 !important
} }
.glow-active-faction {
border: 3px solid #FFD700; /* Goldene Umrandung */
box-shadow: 0 0 20px #FFD700; /* Leuchtender Glüheffekt */
animation: glow 1.5s infinite alternate;
border-radius: 10px; /* Abgerundete Ecken */
margin-bottom: 10px; /* Abstand nach unten */
}
@keyframes glow {
from {
box-shadow: 0 0 10px #FFD700;
}
to {
box-shadow: 0 0 20px #FFD700;
}
}

View File

@@ -3,6 +3,13 @@ const jsonFilePath = "static/dtc_table.json";
var dtcState = {}; var dtcState = {};
async function processDTCNotifications(dtcArray) { async function processDTCNotifications(dtcArray) {
// Entferne DTCs aus dtcState, die nicht im dtcArray enthalten sind
for (var key in dtcState) {
if (!dtcArray.some((dtc) => parseInt(dtc.split(",")[1]) == key)) {
delete dtcState[key];
}
}
if (dtcArray.length === 0 || dtcArray[0] == "0") { if (dtcArray.length === 0 || dtcArray[0] == "0") {
dtcState = {}; dtcState = {};
return; return;
@@ -31,14 +38,14 @@ async function processDTCNotifications(dtcArray) {
if (dtcState[errorCode]) { if (dtcState[errorCode]) {
// Überprüfen, ob sich der Zustand von "previous" auf "active" geändert hat // Überprüfen, ob sich der Zustand von "previous" auf "active" geändert hat
if (activity !== dtcState[errorCode]) { if (activity !== dtcState[errorCode].activity) {
dtcState[errorCode] = activity; dtcState[errorCode].activity = activity;
if (activity === 1) showNotification(description, severity); if (activity === 1) showNotification(description, severity);
} }
} else { } else {
// DTC ist neu, Zustand speichern und Benachrichtigung anzeigen // DTC ist neu, Zustand speichern und wenn active, Benachrichtigung anzeigen
dtcState[errorCode] = activity; dtcState[errorCode] = { activity: activity };
showNotification(description, severity); if (activity === 1) showNotification(description, severity);
} }
} catch (error) { } catch (error) {
console.error("Error processing DTC:", error); console.error("Error processing DTC:", error);
@@ -89,14 +96,15 @@ async function showDTCModal(event) {
} catch (error) { } catch (error) {
console.error("Fehler beim Abrufen der Beschreibung:", error); console.error("Fehler beim Abrufen der Beschreibung:", error);
modal.find(".modal-title").text("Fehler"); modal.find(".modal-title").text("Fehler");
modal.find(".dtc-desc").text("DTC-Beschreibung konnte nicht geladen werden"); modal
.find(".dtc-desc")
.text("DTC-Beschreibung konnte nicht geladen werden");
} }
// Modal anzeigen // Modal anzeigen
modal.modal("show"); modal.modal("show");
} }
function fillDTCTable(dtcArray) { function fillDTCTable(dtcArray) {
// Referenz auf das Tabellen-Element // Referenz auf das Tabellen-Element
var table = document.getElementById("dtc_table"); var table = document.getElementById("dtc_table");
@@ -108,10 +116,27 @@ function fillDTCTable(dtcArray) {
tablediv.hidden = true; tablediv.hidden = true;
table.innerHTML = ""; table.innerHTML = "";
return; return;
} else { }
// Prüfen, ob sich der Inhalt der Tabelle geändert hat
var tableContentChanged = false;
for (var i = 0; i < dtcArray.length; i++) {
var dtcInfo = dtcArray[i].split(",");
var errorCode = parseInt(dtcInfo[1]);
var activity = parseInt(dtcInfo[3]);
if (!dtcState[errorCode] || dtcState[errorCode].activity !== activity) {
tableContentChanged = true;
break;
}
}
if (!tableContentChanged) {
return;
}
// Zeige das Tabellen-Div, wenn DTC vorhanden sind // Zeige das Tabellen-Div, wenn DTC vorhanden sind
tablediv.hidden = false; tablediv.hidden = false;
}
// Tabelle leeren, bevor sie neu gefüllt wird // Tabelle leeren, bevor sie neu gefüllt wird
table.innerHTML = ""; table.innerHTML = "";

View File

@@ -178,9 +178,12 @@ function fillValuesToHTML(dataset) {
// Wenn das Element ein Settingsabschnitt-div ist // Wenn das Element ein Settingsabschnitt-div ist
if (dataset[key] == 0) element.style.display = "none"; if (dataset[key] == 0) element.style.display = "none";
else element.style.display = ""; else element.style.display = "";
} else if (element.tagName === "DIV") { } else if (element.tagName === "DIV" || element.tagName === "SPAN") {
if (element.classList.contains("format-time")) { if (element.classList.contains("format-time")) {
element.innerText = formatTime(dataset[key]); element.innerText = formatTime(dataset[key]);
} else if (element.classList.contains("faction-logo")) {
// Faction-Logo-Logik
updateFactionLogo(element, dataset[key]);
} else { } else {
element.innerText = dataset[key]; element.innerText = dataset[key];
} }
@@ -190,6 +193,10 @@ function fillValuesToHTML(dataset) {
} }
} }
} }
// Aktualisiere den <title>-Tag, wenn der Schlüssel 'devicename' im dataset vorhanden ist
if (key === "devicename") {
document.title = dataset[key];
}
} }
} }
@@ -207,6 +214,22 @@ function formatTime(seconds) {
); );
} }
function updateFactionLogo(element, faction) {
const glowClass = "glow-active-faction";
const factionClasses = ["faction1", "faction2", "faction3"];
factionClasses.forEach((factionClass) => {
if (
factionClass === "faction" + faction &&
element.classList.contains(factionClass)
) {
element.classList.add(glowClass);
} else {
element.classList.remove(glowClass);
}
});
}
// Funktion zum Setzen des ausgewählten Werts für Dropdowns // Funktion zum Setzen des ausgewählten Werts für Dropdowns
function setDropdownValue(selectElement, value) { function setDropdownValue(selectElement, value) {
for (var i = 0; i < selectElement.options.length; i++) { for (var i = 0; i < selectElement.options.length; i++) {

View File

@@ -1 +1 @@
1.05 1.06

View File

@@ -12,8 +12,10 @@
#define TRUE 1 #define TRUE 1
#define FALSE 0 #define FALSE 0
#ifndef HOST_NAME #ifndef DEVICE_NAME
#define HOST_NAME "AirsoftTimer_%08X" #define HOST_NAME "AirsoftTimer"
#else
#define HOST_NAME DEVICE_NAME
#endif #endif
#define SHUTDOWN_DELAY_MS 5000 #define SHUTDOWN_DELAY_MS 5000

View File

@@ -90,7 +90,7 @@ const configData_t ConfigData_defaults = {
0 // checksum 0 // checksum
}; };
void InitEEPROM(); boolean InitEEPROM();
void EEPROM_Process(); void EEPROM_Process();
void StoreConfig_EEPROM(); void StoreConfig_EEPROM();
void GetConfig_EEPROM(); void GetConfig_EEPROM();

View File

@@ -11,6 +11,7 @@ typedef struct Globals_s
char systemStatustxt[16] = ""; /**< Text representation of system status */ char systemStatustxt[16] = ""; /**< Text representation of system status */
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 */
char DeviceNameId[sizeof(DeviceName) + 8]; /**< Device name plus 8 chars chipID */
char FlashVersion[10]; /**< Flash version */ char FlashVersion[10]; /**< Flash version */
uint16_t eePersistanceAdress; /**< EEPROM persistence address */ uint16_t eePersistanceAdress; /**< EEPROM persistence address */
bool hasDTC; bool hasDTC;
@@ -31,8 +32,8 @@ typedef struct Constants_s
} Constants_t; } Constants_t;
const Constants_t constants PROGMEM = { const Constants_t constants PROGMEM = {
1, 5, // Firmware_Version FW_MAJOR, FW_MINOR, // Firmware_Version
1, 5, // Required Flash Version FL_MAJOR, FL_MINOR, // Required Flash Version
GIT_REV // Git-Hash-String GIT_REV // Git-Hash-String
}; };

View File

@@ -17,6 +17,9 @@ platform = espressif8266
framework = arduino framework = arduino
board = d1_mini board = d1_mini
custom_firmware_version = 1.06
custom_flash_version = 1.06
upload_protocol = esptool upload_protocol = esptool
upload_speed = 921600 upload_speed = 921600
;upload_port = 10.0.1.48 ;upload_port = 10.0.1.48

View File

@@ -35,12 +35,13 @@ boolean checkEEPROMavailable();
* *
* This function initializes the EEPROM using the I2C_eeprom instance and checks if it's available. * This function initializes the EEPROM using the I2C_eeprom instance and checks if it's available.
*/ */
void InitEEPROM() boolean InitEEPROM()
{ {
Wire.begin();
ConfigData = ConfigData_defaults; ConfigData = ConfigData_defaults;
PersistenceData = {0}; PersistenceData = {0};
ee.begin(); ee.begin();
checkEEPROMavailable(); return checkEEPROMavailable();
} }
/** /**

View File

@@ -79,7 +79,7 @@ void LoRa_Process()
if (e220ttl.available() > 1) if (e220ttl.available() > 1)
{ {
ResponseContainer rc = e220ttl.receiveMessageRSSI(); ResponseContainer rc = e220ttl.receiveMessageRSSI();
// Is something goes wrong print error // If something goes wrong, print error
if (rc.status.code != 1) if (rc.status.code != 1)
{ {
Serial.println(rc.status.getResponseDescription()); Serial.println(rc.status.getResponseDescription());
@@ -96,34 +96,45 @@ void LoRa_Process()
#elif defined(FEATURE_ENABLE_UARTLORA) #elif defined(FEATURE_ENABLE_UARTLORA)
static char packageInput[32]; static char packageInput[32];
static bool packageRecieved = false; static bool packageReceived = false;
static unsigned int bufferPtr = 0; static unsigned int bufferPtr = 0;
int recievedSize = 0; int receivedSize = 0;
while (SerialLoRa.available() && packageRecieved == false) while (SerialLoRa.available() && !packageReceived)
{ {
if (bufferPtr < sizeof(packageInput) - 1) if (bufferPtr < sizeof(packageInput) - 1)
{ {
packageInput[bufferPtr] = SerialLoRa.read(); char c = SerialLoRa.read();
packageInput[bufferPtr + 1] = 0; // always terminate String packageInput[bufferPtr] = c;
packageInput[bufferPtr + 1] = '\0'; // always terminate string
if (packageInput[bufferPtr] == '\n') if (c == '\n')
{ {
packageRecieved = true; packageReceived = true;
recievedSize = bufferPtr; receivedSize = bufferPtr;
bufferPtr = 0; bufferPtr = 0;
Debug_pushMessage("Got LoRa UART: %s\n", packageInput); Debug_pushMessage("Got LoRa UART: %s\n", packageInput);
} }
else if ((packageInput[bufferPtr] >= 0x30) || (packageInput[bufferPtr] <= 0x5A)) // only accept Numbers, UpperCase-Letters and some special chars else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == ' ' || c == ',' || c == '.')) // Accept numbers, uppercase letters, and some special chars
{ {
bufferPtr++; bufferPtr++;
} }
else
{
Debug_pushMessage("Invalid character received: %c\n", c);
}
}
else
{
Debug_pushMessage("Buffer overflow. Resetting buffer.\n");
bufferPtr = 0;
} }
} }
if (packageRecieved) { if (packageReceived)
Parse_LoRa_UartCommand(packageInput, recievedSize); {
packageRecieved = false; Parse_LoRa_UartCommand(packageInput, receivedSize);
packageReceived = false;
} }
#endif #endif

View File

@@ -84,7 +84,8 @@ void setup()
system_update_cpu_freq(SYS_CPU_80MHZ); system_update_cpu_freq(SYS_CPU_80MHZ);
// Generate a unique device name based on ESP chip ID // Generate a unique device name based on ESP chip ID
snprintf(globals.DeviceName, 32, HOST_NAME, ESP.getChipId()); strncpy(globals.DeviceName, HOST_NAME, sizeof(globals.DeviceName));
snprintf(globals.DeviceNameId, sizeof(globals.DeviceNameId), "%s_%08X", globals.DeviceName, ESP.getChipId());
// Disable WiFi persistent storage // Disable WiFi persistent storage
WiFi.persistent(false); WiFi.persistent(false);
@@ -95,7 +96,7 @@ void setup()
#ifdef FEATURE_ENABLE_WIFI_CLIENT #ifdef FEATURE_ENABLE_WIFI_CLIENT
// Configure WiFi settings for client mode if enabled // Configure WiFi settings for client mode if enabled
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.setHostname(globals.DeviceName); WiFi.setHostname(globals.DeviceNameId);
wifiMulti.addAP(QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT)); wifiMulti.addAP(QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT));
tmrWiFiMaintainConnection.start(); tmrWiFiMaintainConnection.start();
#else #else
@@ -108,14 +109,20 @@ void setup()
Serial.setDebugOutput(false); Serial.setDebugOutput(false);
Serial.print("\n\n-------------------START-------------------\n"); Serial.print("\n\n-------------------START-------------------\n");
Serial.print(globals.DeviceName); Serial.print(globals.DeviceNameId);
Serial.print("\nby Hiabuto Defense\n"); Serial.print("\nby Hiabuto Defense\n\n");
// Initialize EEPROM, load configuration, and persistence data from EEPROM // Initialize EEPROM, load configuration, and persistence data from EEPROM
InitEEPROM(); if (InitEEPROM())
{
GetConfig_EEPROM(); GetConfig_EEPROM();
GetPersistence_EEPROM(); GetPersistence_EEPROM();
Serial.print("\nEE-Init done"); Serial.printf("Initialized EEPROM at Address 0x%02X\n", I2C_EE_ADDRESS);
}
else
{
Serial.print("EEPROM not Initialized\n");
}
if (i2c_io.begin()) if (i2c_io.begin())
{ {
@@ -150,7 +157,7 @@ void setup()
// Set up OTA updates // Set up OTA updates
ArduinoOTA.setPort(8266); ArduinoOTA.setPort(8266);
ArduinoOTA.setHostname(globals.DeviceName); ArduinoOTA.setHostname(globals.DeviceNameId);
ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD)); ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD));
ArduinoOTA.onStart([]() ArduinoOTA.onStart([]()
@@ -208,7 +215,6 @@ void setup()
// Start cyclic EEPROM updates for Persistence Data Structure (PDS) // Start cyclic EEPROM updates for Persistence Data Structure (PDS)
tmrEEPROMCyclicPDS.start(); tmrEEPROMCyclicPDS.start();
Serial.print("\nSetup Done\n");
disp_FAC_1.init(); disp_FAC_1.init();
disp_FAC_1.setBrightness(5); disp_FAC_1.setBrightness(5);
@@ -217,7 +223,6 @@ void setup()
disp_FAC_3.init(); disp_FAC_3.init();
disp_FAC_3.setBrightness(5); disp_FAC_3.setBrightness(5);
tmrEEPROMCyclicPDS.start();
tmrFactionTicker.start(); tmrFactionTicker.start();
tmrInputGetter.start(); tmrInputGetter.start();
@@ -583,7 +588,7 @@ void toggleWiFiAP(bool shutdown)
// Start WiFi in Access Point (AP) mode // Start WiFi in Access Point (AP) mode
WiFi.mode(WIFI_AP); WiFi.mode(WIFI_AP);
WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0)); WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0));
WiFi.softAP(globals.DeviceName, QUOTE(WIFI_AP_PASSWORD)); WiFi.softAP(globals.DeviceNameId, QUOTE(WIFI_AP_PASSWORD));
// Stop WiFi maintenance connection ticker if enabled and display debug messages // Stop WiFi maintenance connection ticker if enabled and display debug messages
#ifdef FEATURE_ENABLE_WIFI_CLIENT #ifdef FEATURE_ENABLE_WIFI_CLIENT

View File

@@ -71,7 +71,7 @@ void initWebUI()
} }
// Initialize mDNS and add service // Initialize mDNS and add service
MDNS.begin(globals.DeviceName); MDNS.begin(globals.DeviceNameId);
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
// Set up WebSocket event handler and attach to web server // Set up WebSocket event handler and attach to web server
@@ -342,7 +342,7 @@ void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
char buffer[16]; char buffer[16];
info["DeviceName"] = globals.DeviceName; info["DeviceNameId"] = globals.DeviceNameId;
sprintf(buffer, "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor); sprintf(buffer, "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor);
info["FW-Version"] = buffer; info["FW-Version"] = buffer;
info["FS-Version"] = globals.FlashVersion; info["FS-Version"] = globals.FlashVersion;
@@ -653,6 +653,7 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
if (send_mapping) if (send_mapping)
{ {
const char mapping[] = "MAPPING_STATIC:" const char mapping[] = "MAPPING_STATIC:"
"devicename;"
"active_faction_on_reboot;" "active_faction_on_reboot;"
"batteryType;" "batteryType;"
"name_faction1;" "name_faction1;"
@@ -669,6 +670,7 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
String temp = "STATIC:"; String temp = "STATIC:";
temp.concat(String(globals.DeviceName) + ";");
temp.concat(String(ConfigData.active_faction_on_reboot) + ";"); temp.concat(String(ConfigData.active_faction_on_reboot) + ";");
temp.concat(String(ConfigData.batteryType) + ";"); temp.concat(String(ConfigData.batteryType) + ";");
temp.concat(String(ConfigData.Faction_1_Name) + ";"); temp.concat(String(ConfigData.Faction_1_Name) + ";");