Compare commits

...

4 Commits

11 changed files with 135 additions and 39 deletions

View File

@ -1,7 +1,3 @@
# 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
@ -169,8 +165,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

@ -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

@ -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.05
custom_flash_version = 1.05
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

@ -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,7 +109,7 @@ 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");
// Initialize EEPROM, load configuration, and persistence data from EEPROM // Initialize EEPROM, load configuration, and persistence data from EEPROM
@ -150,7 +151,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([]()
@ -583,7 +584,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) + ";");