28 Commits

Author SHA1 Message Date
e4770f2fa2 added flashscript 2024-06-03 01:34:30 +02:00
cbcdc34e6c fixed Bug leading to Stackoverflow 2024-06-02 23:37:38 +02:00
48774a42f4 improvement of webui-function 2024-06-02 23:37:04 +02:00
73c486be73 added some missing new lines in debug 2024-06-02 23:35:57 +02:00
6574947a80 hardened WiFi-String stuff 2024-06-02 23:35:20 +02:00
956cc49e6f reworked part of debugger 2024-06-02 22:38:52 +02:00
b1da9449ad sanitycheck autocorrect of EEPROM now default. 2024-06-02 21:59:38 +02:00
e54cadcc7c Bugfix - EE-Reset to defaults not pointing to HOST_NAME 2024-06-02 21:50:51 +02:00
0b2245d7f6 removed version file from flash, because auto generated during build 2024-05-31 18:56:28 +02:00
3ceab44a96 fixed Bug in Battery-Type-Setting and disabled WiFiClient 2024-05-31 18:51:05 +02:00
91de9f0785 incereased Version after Tag-creation 2024-05-31 14:27:06 +02:00
837fd7f558 updated Version before tag 2024-05-31 14:05:07 +02:00
6cacd8451f missed import in Buildscript 2024-05-31 14:04:57 +02:00
5ae054f314 improved UART Parser for LoRa 2024-05-31 13:59:11 +02:00
3e571a515d some Init-Stuff optimized and made Log nicer 2024-05-31 13:58:36 +02:00
a2aa302121 optimized dtc-table in WebUI 2024-05-31 12:50:01 +02:00
d04489819d highlite active Faction in WEbUI 2024-05-31 12:49:38 +02:00
52026296f2 updated DeviceName handling 2024-05-31 12:49:06 +02:00
a22f71649a updated Buildscripts to have central Versioning 2024-05-31 12:47:45 +02:00
ae8eef52ef missed to relplace some strings 2024-05-31 03:07:37 +02:00
d9ee193e26 missed to add function to websocket.js 2024-05-31 03:06:00 +02:00
170c66ff49 updated plaformio.ini 2024-05-31 03:04:41 +02:00
2ad835966c websocket-parser now supports div 2024-05-31 03:04:27 +02:00
a20a351002 adjusted systemStatus variants to this Project 2024-05-31 03:04:07 +02:00
c9fba23e70 got most of the WebUI updated and working 2024-05-31 03:03:47 +02:00
9301607468 added reboot-command 2024-05-31 01:19:47 +02:00
fc592c4342 some debugging regarding EEPROM 2024-05-31 01:16:26 +02:00
ab2ab0e0c1 Updated javasccript of WebUI 2024-05-30 23:55:54 +02:00
44 changed files with 1637 additions and 783 deletions

1
Software/.gitignore vendored
View File

@@ -1,4 +1,5 @@
data/ data/
data_src/version
.pio .pio
.vscode/.browse.c_cpp.db* .vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json

View File

@@ -0,0 +1,97 @@
# Flash ESP8266 Script
## Version: 1.0.0
### Author: Marcel Peterkau
### Copyright: 2024
### License: MIT
---
## Beschreibung
Dieses Skript entpackt ein ZIP-Archiv, das Firmware- und Dateisystem-Dateien für einen ESP8266 enthält, und flasht diese auf das Gerät. Nach dem Flashen werden die entpackten Dateien automatisch gelöscht.
---
## Benutzung
```sh
python flash_esp8266.py <zip_file> [-v]
```
### Parameter
- `<zip_file>`: Der Name der ZIP-Datei, die die Firmware- und Dateisystem-Dateien enthält.
- `-v`: Aktiviert Debug-Ausgaben (optional).
### Beispiel
```sh
python flash_esp8266.py firmware_1.07_cbcdc34.zip -v
```
---
## Voraussetzungen
- Python 3.x
- Die folgenden Python-Bibliotheken müssen installiert sein:
- esptool
- pyserial
Installiere die benötigten Bibliotheken mit:
```sh
pip install esptool pyserial
```
---
## Funktionen
- **get_com_ports**: Ermittelt die verfügbaren COM-Ports.
- **find_new_com_port**: Findet den neuen COM-Port, wenn der ESP8266 angeschlossen wird.
- **extract_files**: Entpackt die ZIP-Datei und gibt die enthaltenen Dateien zurück.
- **decompress_gz**: Entpackt eine GZ-Datei.
- **clean_up**: Löscht die angegebenen Dateien nach dem Flashen.
---
## Ablauf
1. Das Skript überprüft, ob die angegebene ZIP-Datei existiert.
2. Die ZIP-Datei wird entpackt und die Firmware- und Dateisystem-Dateien werden identifiziert.
3. Die Dateisystem-Datei (GZ) wird entpackt.
4. Der Benutzer wird aufgefordert, den ESP8266 abzustecken und erneut anzustecken, um den neuen COM-Port zu ermitteln.
5. Die Firmware- und Dateisystem-Dateien werden auf den ESP8266 geflasht.
6. Die temporären Dateien werden nach dem erfolgreichen Flashen gelöscht.
---
## Fehlerbehebung
- **ZIP-Datei nicht gefunden**: Stelle sicher, dass der Pfad zur ZIP-Datei korrekt angegeben ist.
- **Erforderliche Dateien nicht im ZIP-Archiv**: Überprüfe, ob die ZIP-Datei die richtigen Dateien (`.fw.bin` und `.fs.gz`) enthält.
- **Kein neuer COM-Port gefunden**: Stelle sicher, dass der ESP8266 korrekt angeschlossen ist und warte, bis der neue COM-Port erkannt wird.
---
## Lizenz
Dieses Projekt ist unter der MIT-Lizenz lizenziert
---
## Versionshistorie
- **1.0.0** - Initiale Version
---
## Haftungsausschluss
Dieses Skript wird ohne Garantie bereitgestellt. Der Autor übernimmt keine Verantwortung für Schäden oder Datenverlust, die durch die Nutzung dieses Skripts entstehen könnten.
---

View File

@@ -0,0 +1,156 @@
"""
flash_esp8266.py
Version: 1.0.0
Author: Marcel Peterkau
Copyright: 2024
License: MIT
Beschreibung:
Dieses Skript entpackt ein ZIP-Archiv mit Firmware- und Dateisystem-Dateien und flasht diese auf einen ESP8266.
Die entpackten Dateien werden nach dem Flashen gelöscht.
Versionshistorie:
1.0.0 - Initiale Version
Benutzung:
python flash_esp8266.py <zip_file> [-v]
Optionen:
-v Aktiviert Debug-Ausgaben
"""
import esptool
import sys
import serial.tools.list_ports
import zipfile
import os
import gzip
import shutil
BAUD_RATE = 921600 # Erhöhte Baudrate
DEBUG = '-v' in sys.argv
def debug_print(message):
if DEBUG:
print(message)
def get_com_ports():
ports = [comport.device for comport in serial.tools.list_ports.comports()]
debug_print(f"Verfügbare COM-Ports: {ports}")
return ports
def find_new_com_port(old_ports, timeout=15):
debug_print("Suche nach neuen COM-Ports...")
import time
start_time = time.time()
while time.time() - start_time < timeout:
new_ports = get_com_ports()
added_ports = list(set(new_ports) - set(old_ports))
if added_ports:
new_port = added_ports[0]
print(f"Neuer COM-Port gefunden: {new_port}")
return new_port
time.sleep(1)
return None
def extract_files(zip_file):
with zipfile.ZipFile(zip_file, 'r') as zip_ref:
zip_ref.extractall()
debug_print(f"Entpackt: {zip_ref.namelist()}")
return zip_ref.namelist()
def decompress_gz(file_path):
output_file = file_path.replace('.gz', '')
with gzip.open(file_path, 'rb') as f_in:
with open(output_file, 'wb') as f_out:
shutil.copyfileobj(f_in, f_out)
debug_print(f"Entpackt {file_path} zu {output_file}")
return output_file
def clean_up(files):
for file in files:
if os.path.isfile(file):
os.remove(file)
debug_print(f"Gelöscht: {file}")
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Verwendung: python flash_esp8266.py <zip_file> [-v]")
sys.exit(1)
ZIP_FILE = sys.argv[1]
# Überprüfe, ob das ZIP-Archiv existiert
if not os.path.isfile(ZIP_FILE):
print(f"ZIP-Archiv {ZIP_FILE} nicht gefunden.")
sys.exit(1)
# Entpacke das ZIP-Archiv
extracted_files = extract_files(ZIP_FILE)
# Finde die .fw.bin- und .fs.gz-Dateien
bin_file = None
fs_file = None
for file in extracted_files:
if file.endswith('.fw.bin'):
bin_file = file
elif file.endswith('.fs.gz'):
fs_file = file
if not bin_file or not fs_file:
print(f"Die erforderlichen Dateien (.fw.bin und .fs.gz) wurden nicht im ZIP-Archiv {ZIP_FILE} gefunden.")
sys.exit(1)
print(f"Gefundene Firmware-Datei: {bin_file}")
print(f"Gefundene Dateisystem-Datei: {fs_file}")
# Entpacke die .fs.gz-Datei
decompressed_fs_file = decompress_gz(fs_file)
print("Bitte stecke den ESP8266 ab, falls er angeschlossen ist, und drücke Enter.")
input()
print("Suche nach verfügbaren COM-Ports...")
old_ports = get_com_ports()
print("Bitte stecke den ESP8266 jetzt an und warte, bis der neue COM-Port erkannt wird...")
port = find_new_com_port(old_ports, timeout=15)
if port is None:
print("Kein neuer COM-Port gefunden. Bitte versuche es erneut.")
sys.exit(1)
print(f"Neuer COM-Port gefunden: {port}")
# Benutze esptool zum Flashen der Firmware und des Dateisystems
try:
# Flashen der Firmware
esptool_args_bin = [
'--port', port,
'--baud', str(BAUD_RATE),
'write_flash', '-fm', 'dout', '0x00000', bin_file
]
esptool.main(esptool_args_bin)
print("Firmware erfolgreich geflasht!")
# Flashen des Dateisystems
esptool_args_fs = [
'--port', port,
'--baud', str(BAUD_RATE),
'write_flash', '0x300000', decompressed_fs_file
]
esptool.main(esptool_args_fs)
print("Dateisystem erfolgreich geflasht!")
# Bereinigen der entpackten Dateien
clean_up(extracted_files)
clean_up([decompressed_fs_file])
except Exception as e:
print(f"Fehler beim Flashen: {e}")
sys.exit(1)
input("Drücke Enter, um das Fenster zu schließen...") # Hält das Fenster offen

View File

@@ -1,8 +0,0 @@
import subprocess
revision = (
subprocess.check_output(["git", "rev-parse", "--short=10", "HEAD"])
.strip()
.decode("utf-8")
)
print("-DGIT_REV='\"%s\"'" % revision)

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")
@@ -167,10 +164,15 @@ def gzip_webfiles(source, target, env):
shutil.rmtree(data_temp_dir_path) shutil.rmtree(data_temp_dir_path)
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

@@ -3,6 +3,31 @@ env.Execute("\"$PYTHONEXE\" -m pip install jinja2")
import struct2json import struct2json
import dtcs import dtcs
from os import popen
git_revision = popen('git rev-parse --short HEAD').read().strip()
# Versionsnummern aus platformio.ini holen
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

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>%DEVICENAME%</title> <title>Dark Emergency Timer</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="static/css/bootstrap.min.css"> <link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/custom.css"> <link rel="stylesheet" href="static/css/custom.css">
@@ -11,6 +11,8 @@
<script src="static/js/jquery.min.js"></script> <script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script> <script src="static/js/bootstrap.min.js"></script>
<script src="static/js/websocket.js"></script> <script src="static/js/websocket.js"></script>
<script src="static/js/dtc_table.js"></script>
<script src="static/js/script.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
@@ -18,11 +20,22 @@
</head> </head>
<body> <body>
<!-- Connection-Overlay -->
<div id="overlay">
<div class="overlay-content">
<p>Verbinde...</p>
<span class="loader"></span>
</div>
</div>
<!-- Connection-Overlay -->
<!-- Notification-Container -->
<div id="notification-container" class="notification-container"></div>
<!-- Notification-Container -->
<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="">
%DEVICENAME% <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">
@@ -35,7 +48,6 @@
<li class="nav-item"><a class="nav-link active" role="tab" data-toggle="tab" href="#tab_home">Home</a></li> <li class="nav-item"><a class="nav-link active" role="tab" data-toggle="tab" href="#tab_home">Home</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_maintenance">Wartung</a></li> <li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_maintenance">Wartung</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_source">Einstellungen</a></li> <li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_source">Einstellungen</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_sysinfo">Systeminfo</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_fwupdate">Update</a></li> <li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_fwupdate">Update</a></li>
</ul> </ul>
@@ -51,17 +63,17 @@
<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">%DEVICENAME%</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 -->
<hr /> <hr />
<p> <p>
<h4>Akku Ladestand</h4> <h4>Akkuladestand</h4>
<div class="progress"> <div class="progress">
<div class="progress-bar text-light" role="progressbar" aria-valuenow="%BAT_REMAIN_CAPACITY%" <div id="battery_level" class="data-battery_level progress-bar text-light" role="progressbar" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100" style="width: %BAT_REMAIN_CAPACITY%&#37;"> aria-valuemin="0" aria-valuemax="100" style="width: 0%">
%BAT_REMAIN_CAPACITY%&#37; 0
</div> </div>
</div> </div>
</p> </p>
@@ -70,7 +82,7 @@
<hr /> <hr />
<p> <p>
<h4>aktueller Modus</h4> <h4>aktueller Modus</h4>
<input class="form-control" type="text" placeholder="%SYSTEM_STATUS%" readonly> <input class="data-systemstatus form-control" type="text" id="sysstatus" readonly>
</p> </p>
<!-- Div Group current Mode --> <!-- Div Group current Mode -->
<!-- Div Group Faction Points --> <!-- Div Group Faction Points -->
@@ -79,35 +91,35 @@
<h4>aktueller Punktestand</h4> <h4>aktueller Punktestand</h4>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col text-center %FACTION_1_ACTIVE% text-white p-3">%NAME_FAC_1%</div> <div id="header_faction1" class="col text-center data-name_faction1 text-white p-3">%NAME_FAC_1%</div>
<div class="col text-center %FACTION_2_ACTIVE% text-white p-3">%NAME_FAC_2%</div> <div id="header_faction2" class="col text-center data-name_faction2 text-white p-3">%NAME_FAC_2%</div>
<div class="col text-center %FACTION_3_ACTIVE% 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">
<div class="col text-center bg-secondary text-white p-3">%POINTS_FAC_1%</div> <div id="time_faction1" class="data-time_faction1 col text-center bg-secondary text-white p-3 format-time">0</div>
<div class="col text-center bg-secondary text-white p-3">%POINTS_FAC_2%</div> <div id="time_faction2" class="data-time_faction2 col text-center bg-secondary text-white p-3 format-time">0</div>
<div class="col text-center bg-secondary text-white p-3">%POINTS_FAC_3%</div> <div id="time_faction3" class="data-time_faction3 col text-center bg-secondary text-white p-3 format-time">0</div>
</div> </div>
</div> </div>
</p> </p>
<!-- Div GroupFaction Points --> <!-- Div GroupFaction Points -->
<!-- Div Group DTC Table --> <!-- Div Group DTC Table -->
<div %SHOW_DTC_TABLE%> <div id="dtc_container" hidden>
<hr /> <hr />
<p> <p>
<h4>Fehlercodes</h4> <h4>Fehlercodes</h4>
<table class="table"> <table class="table" id="dtc_table">
<tbody> <tbody>
<tr> <tr>
<th class="col-6" scope="col">Zeitstempel</th> <th class="col-6" scope="col">Zeitstempel</th>
@@ -115,7 +127,6 @@
<th class="col-2" scope="col">Schwere</th> <th class="col-2" scope="col">Schwere</th>
<th class="col-2" scope="col">Aktiv</th> <th class="col-2" scope="col">Aktiv</th>
</tr> </tr>
%DTC_TABLE%
</tbody> </tbody>
</table> </table>
</p> </p>
@@ -131,435 +142,245 @@
<hr /> <hr />
<p> <p>
<h4>Punkte zur&uuml;cksetzen</h4> <h4>Punkte zur&uuml;cksetzen</h4>
<form action="post.htm" method="POST" class="form-horizontal"> <div class="form-group row">
<div class="form-group row"> <div class="col text-center">
<div class="col text-center"> <button id="reset-timer" class="btn-wsevent btn btn-outline-primary ml-2">Timer zurücksetzen</button>
<button name="resetpoints" type="submit" class="btn btn-outline-primary">Reset</button>
</div>
</div> </div>
</form> </div>
</p> </p>
<!-- Div Group Reset Timers --> <!-- Div Group Reset Timers -->
<!-- Div Group EEPROM formatting --> <!-- Div Group LiveDebug -->
<hr /> <hr />
<p> <p>
<h4>EEPROM formatieren</h4> <h4>Live Debug</h4>
<div class="alert alert-primary alert-dismissable show fade" role="alert"> <div class="form-group row">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <textarea class="form-control" spellcheck="false" id="livedebug-out" rows="3" readonly></textarea>
<span aria-hidden="true">×</span> </div>
</button> <div class="form-group row">
<strong>Achtung!</strong><br> <div class="col text-center">
Das Formatieren der EEPROM-Bereiche sollte nur ausgeführt werden wenn es unbedingt erforderlich ist! <button id="debugstart" class="btn-wsevent btn btn-outline-primary ml-2">Start</button>
Hierdurch werden alle Einstellungen zurück gesetzt bzw. alle Betriebsdaten gehen verloren. <button id="debugstop" class="btn-wsevent btn btn-outline-primary ml-2">Stop</button>
Folgende Situationen erfordern unter anderem eine Formatierung: </div>
- Erstinitialisierung (bei neu aufgebautem Gerät) </div>
- Firmware-Update (nur wenn es die Release-Notes fordern) </p>
</div> <!-- Div Group LiveDebug -->
<form action="post.htm" method="POST" class="form-horizontal"> <!-- Div Group Device Reboot -->
<div class="form-group row"> <hr />
<div class="offset-4 col-8"> <p>
<div class="form-check"> <h4>Ger&auml;t neustarten</h4>
<input class="form-check-input" type="checkbox" name="reset_ee_cfg" id="reset_ee_cfg"> <div class="form-group row">
<label class="form-check-label" for="reset_ee_cfg"> <div class="col text-center">
Bereich "CFG" <button id="reboot" class="btn-wsevent confirm btn btn-outline-primary">Reboot</button>
</label> </div>
</div> </div>
<div class="form-check"> </p>
<input class="form-check-input" type="checkbox" name="reset_ee_pds" id="reset_ee_pds"> <!-- Div Group Device Reboot -->
<label class="form-check-label" for="reset_ee_pds"> </div>
Bereich "PDS" <!-- Div Tab Maintenance -->
</label>
<!-- Div Tab Settings-->
<div id="tab_source" class="tab-pane fade" role="tabpanel">
<h3>Einstellungen</h3>
<!-- Div Group Battery Type -->
<hr />
<p>
<h4>Akku</h4>
<div class="form-group row">
<label for="batterytype" class="control-label col-4">Akku-Variante</label>
<div class="col-8">
<select id="batterytype" class="set-wsevent data-batterytype select form-control">
<option value="Undefined">Undefined</option>
<option value="LiPo 3S">LiPo 3S</option>
<option value="LiPo 2S">LiPo 2S</option>
</select>
</div> </div>
</div> </div>
</div> </p>
<div class="form-group row"> <!-- Div Group Battery Type -->
<div class="col text-center"> <!-- Div Group Timer Settings -->
<button name="reset_ee_btn" type="submit" class="btn btn-outline-primary">EEPROM formatieren</button> <hr />
</div> <p>
</div> <h4>Timer Einstellungen</h4>
</form> <div class="form-group row">
</p> <label for="active_faction_on_reboot" class="control-label col-4">Aktive Fraktion wiederherstellen</label>
<!-- Div Group EEPROM formatting --> <div class="col-8">
<!-- Div Group Device Reboot --> <div class="form-check">
<hr /> <input class="set-wsevent data-active_faction_on_reboot form-check-input" type="checkbox" id="active_faction_on_reboot">
<p> <label class="form-check-label" for="active_faction_on_reboot">
<h4>Ger&auml;t neustarten</h4> aktivieren
<form action="post.htm" method="POST" class="form-horizontal"> </label>
<div class="form-group row"> </div>
<div class="col text-center">
<button name="reboot" type="submit" class="btn btn-outline-primary">Reboot</button>
</div>
</div>
</form>
</p>
<!-- Div Group Device Reboot -->
</div>
<!-- Div Tab Maintenance -->
<!-- Div Tab Settings-->
<div id="tab_source" class="tab-pane fade" role="tabpanel">
<h3>Einstellungen</h3>
<!-- Div Group Battery Type -->
<hr />
<p>
<form action="post.htm" method="POST" class="form-horizontal">
<h4>Akku-Variante</h4>
<div class="form-group row">
<label for="battery_select" class="control-label col-4">Akku</label>
<div class="col-8">
<select id="battery_select" name="battery_select" class="select form-control">
%BATTERY_SELECT_OPTIONS%
</select>
</div>
</div>
<h4>Timer-Einstellungen</h4>
<div class="form-group row">
<label for="factionreboot_cont" class="control-label col-4">active Faction Recovery</label>
<div class="col-8">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="factionreboot_cont" id="factionreboot_cont"
%FACTIONREBOOT_CHECKED%>
<label class="form-check-label" for="factionreboot_cont">
aktive Faktion beim booten wiederherstellen ?
</label>
</div> </div>
</div> </div>
</div> <div class="form-group row">
<h4>Faktionsbezeichnungen</h4> <label for="name_faction1" class="control-label col-4">Faktion 1</label>
<div class="alert alert-primary alert-dismissable show fade" role="alert"> <div class="col-8">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <div class="input-group">
<span aria-hidden="true">×</span> <input id="name_faction1" type="text" class="set-wsevent data-name_faction1 form-control" required="required">
</button> <div class="input-group-append">
<strong>Achtung!</strong><br> <span class="input-group-text">max. 32 Zeichen</span>
Faktionsbezeichnungen können nur aus ASCII-Zeichen bestehen, also A-Z, a-z und 0-9 </div>
</div>
<div class="form-group row">
<label for="faction_1_name" class="control-label col-4">Faktion 1</label>
<div class="col-8">
<div class="input-group">
<input id="faction_1_name" name="faction_1_name" value="%NAME_FAC_1%" type="text" class="form-control" pattern="[A-Za-z0-9 _-]{1,32}">
<div class="input-group-append">
<span class="input-group-text">max 32 Zeichen</span>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="form-group row">
<div class="form-group row"> <label for="name_faction2" class="control-label col-4">Faktion 2</label>
<label for="faction_2_name" class="control-label col-4">Faktion 2</label> <div class="col-8">
<div class="col-8"> <div class="input-group">
<div class="input-group"> <input id="name_faction2" type="text" class="set-wsevent data-name_faction2 form-control" required="required">
<input id="faction_2_name" name="faction_2_name" value="%NAME_FAC_2%" type="text" class="form-control" pattern="[A-Za-z0-9 _-]{1,32}"> <div class="input-group-append">
<div class="input-group-append"> <span class="input-group-text">max. 32 Zeichen</span>
<span class="input-group-text">max 32 Zeichen</span> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="form-group row">
<div class="form-group row"> <label for="name_faction3" class="control-label col-4">Faktion 3</label>
<label for="faction_3_name" class="control-label col-4">Faktion 3</label> <div class="col-8">
<div class="col-8"> <div class="input-group">
<div class="input-group"> <input id="name_faction3" type="text" class="set-wsevent data-name_faction3 form-control" required="required">
<input id="faction_3_name" name="faction_3_name" value="%NAME_FAC_3%" type="text" class="form-control" pattern="[A-Za-z0-9 _-]{1,32}"> <div class="input-group-append">
<div class="input-group-append"> <span class="input-group-text">max. 32 Zeichen</span>
<span class="input-group-text">max 32 Zeichen</span> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <!-- Div Group Timer Settings -->
<!-- Div Group Save Button-->
<hr />
<p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button name="settingssave" type="submit" class="btn btn-outline-primary">&Uuml;bernehmen</button> <button id="settingssave" class="btn-wsevent btn btn-outline-primary">Speichern</button>
</div> </div>
</div> </div>
</form>
</p> </p>
<!-- Div Group Battery Type -->
</div> </div>
<!-- Div Tab Settings --> <!-- Div Tab Settings -->
<!-- Div Tab SystemInfo --> <!-- Div Tab Firmware Update-->
<div id="tab_sysinfo" class="tab-pane fade" role="tabpanel"> <div id="tab_fwupdate" class="tab-pane fade" role="tabpanel">
<h3>Systeminfo</h3> <h3>Firmware</h3>
<!-- Div Group Sysinfo:Geraeteinfo --> <!-- Div Group VersionInfo -->
<hr /> <hr />
<p> <p>
<h4>Ger&auml;t</h4> <h4>Version-Info</h4>
<table class="table"> <table class="table">
<tbody> <tbody>
<tr> <tr>
<th class="col-7" scope="col">Parameter</td> <th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td> <th class="col-5" scope="col">Value</td>
</tr> </tr>
<tr> <tr>
<td>Hostname</td> <td>Firmware Version</td>
<td>%HOSTNAME%</td> <td><span class="data-fw-version"></span></td>
</tr> </tr>
<tr> <tr>
<td>Device-ID</td> <td>Flash Version</td>
<td>%DEVICENAME_ID%</td> <td><span class="data-flash-version"></span></td>
</tr> </tr>
<tr> <tr>
<td>Battery Voltage</td> <td>Git Revision</td>
<td>%BAT_VOLTAGE%V</td> <td><span class="data-git-revision"></span></td>
</tr> </tr>
<tr> </table>
<td>Battery Remain</td> </p>
<td>%BAT_REMAIN_CAPACITY%&#37;</td> <!-- Div Group VersionInfo -->
</tr> <!-- Div Group EEPROM Backup -->
</table> <hr />
</p> <p>
<!-- Div Group Sysinfo:Geraeteinfo --> <h4>EEPROM-Backup</h4>
<!-- Div Group Sysinfo:Settings --> <div class="form-group row">
<hr /> <div class="col text-center">
<p> <a class="btn btn-outline-primary" href="eejson" role="button" id="ee-backup-download">Download</a>
<h4>Einstellungen</h4> </div>
<table class="table"> </div>
<tbody> </p>
<tr> <!-- Div Group EEPROM Backup -->
<th class="col-7" scope="col">Parameter</td> <!-- Div Group EEPROM Restore -->
<th class="col-5" scope="col">Value</td> <hr />
</tr> <p>
<tr> <h4>EEPROM-Restore</h4>
<td>Battery_type</td> <form method='POST' action='eeRestore' enctype='multipart/form-data'>
<td>%BATTERY_TYPE%</td>
</tr>
<tr>
<td>Faction_recovery</td>
<td>%FACTION_RECOVERY%</td>
</tr>
<tr>
<td>EEPROM Version</td>
<td>%EEPROM_VERSION%</td>
</tr>
<tr>
<td>Checksum</td>
<td>%CONFIG_CHECKSUM%</td>
</tr>
</tbody>
</table>
</p>
<!-- Div Group Sysinfo:Settings -->
<!-- Div Group Sysinfo:Persistance -->
<hr />
<p>
<h4>Betriebsdaten</h4>
<table class="table">
<tbody>
<tr>
<th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td>
</tr>
<tr>
<td>writeCycleCounter</td>
<td>%WRITE_CYCLE_COUNT%</td>
</tr>
<tr>
<td>PersistenceMarker</td>
<td>%PERSISTENCE_MARKER%</td>
</tr>
<tr>
<td>activeFaction</td>
<td>%ACTIVE_FACTION%</td>
</tr>
<tr>
<td>faction_1_timer</td>
<td>%POINTS_FAC_1%</td>
</tr>
<tr>
<td>faction_2_timer</td>
<td>%POINTS_FAC_2%</td>
</tr>
<tr>
<td>faction_3_timer</td>
<td>%POINTS_FAC_3%</td>
</tr>
<tr>
<td>checksum</td>
<td>%PERSISTANCE_CHECKSUM%</td>
</tr>
</table>
</p>
<!-- Div Group Sysinfo:Persistance -->
<!-- Div Group LiveDebug -->
<hr />
<p>
<h4>Live Debug</h4>
<div class="form-group row"> <div class="form-group row">
<textarea class="form-control" spellcheck="false" id="livedebug-out" rows="3" readonly></textarea> <div class="custom-file">
<input type="file" name="ee-restore-file" class="custom-file-input" id="ee-restore-file" accept=".ee.json"
required />
<label class="custom-file-label" for="ee-restore-file">EEPROM-Backup ausw&auml;hlen</label>
</div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button id="btn-ws-start" class="btn btn-outline-primary">Start</button> <button name="submit" type="submit" class="btn btn-outline-primary">Restore starten</button>
<button id="btn-ws-stop" class="btn btn-outline-primary ml-2">Stop</button> </div>
</div>
</form>
</p>
<!-- Div Group EEPROM Restore -->
<!-- Div Group Firmware Update -->
<hr />
<p>
<h4>Firmware-Update</h4>
<form method='POST' action='doUpdate' enctype='multipart/form-data'>
<div class="form-group row">
<div class="custom-file">
<input type="file" name="fw-update-file" class="custom-file-input" id="fw-update-file"
accept=".fw.bin,.fs.gz" required />
<label class="custom-file-label" for="fw-update-file">Firmware-Update ausw&auml;hlen</label>
</div> </div>
</div> </div>
</p>
<!-- Div Group LiveDebug -->
</div>
<!-- Div Tab SystemInfo -->
<!-- Div Tab Firmware Update-->
<div id="tab_fwupdate" class="tab-pane fade" role="tabpanel">
<h3>Firmware</h3>
<!-- Div Group VersionInfo -->
<hr />
<p>
<h4>Version-Info</h4>
<table class="table">
<tbody>
<tr>
<th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td>
</tr>
<tr>
<td>Firmware Version</td>
<td>%SW_VERSION%</td>
</tr>
<tr>
<td>Flash Version</td>
<td>%FS_VERSION%</td>
</tr>
<tr>
<td>Git Revision</td>
<td>%GIT_REV%</td>
</tr>
</table>
</p>
<!-- Div Group VersionInfo -->
<!-- Div Group EEPROM Backup -->
<hr />
<p>
<h4>EEPROM-Backup</h4>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<a class="btn btn-outline-primary" href="eejson" role="button" id="ee-backup-download">Download</a> <button name="submit" type="submit" class="btn btn-outline-primary">Update starten</button>
</div> </div>
</div> </div>
</p> </form>
<!-- Div Group EEPROM Backup --> </p>
<!-- Div Group EEPROM Restore --> <!-- Div Group Firmware Update -->
<hr />
<p>
<h4>EEPROM-Restore</h4>
<form method='POST' action='eeRestore' enctype='multipart/form-data'>
<div class="form-group row">
<div class="custom-file">
<input type="file" name="ee-restore-file" class="custom-file-input" id="ee-restore-file" accept=".ee.json"
required />
<label class="custom-file-label" for="ee-restore-file">EEPROM-Backup ausw&auml;hlen</label>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Restore starten</button>
</div>
</div>
</form>
</p>
<!-- Div Group EEPROM Restore -->
<!-- Div Group Firmware Update -->
<hr />
<p>
<h4>Firmware-Update</h4>
<form method='POST' action='doUpdate' enctype='multipart/form-data'>
<div class="form-group row">
<div class="custom-file">
<input type="file" name="fw-update-file" class="custom-file-input" id="fw-update-file"
accept=".fw.bin,.fs.gz" required />
<label class="custom-file-label" for="fw-update-file">Firmware-Update ausw&auml;hlen</label>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Update starten</button>
</div>
</div>
</form>
</p>
<!-- Div Group Firmware Update -->
</div>
<!-- Div Tab Firmware Update-->
</div> </div>
<!-- Tabs Content --> <!-- Div Tab Firmware Update-->
</main> </div>
<!-- Tabs Content -->
</main>
<!-- Footer -->
<!-- Footer --> <footer class="page-footer navbar-dark bg-primary font-small fixed-bottom">
<div class="container-fluid text-center">
<footer class="page-footer navbar-dark bg-primary font-small fixed-bottom"> <div class="footer-copyright text-center py-3">
<div class="container-fluid text-center"> <span class="text-muted">© 2023 -
<div class="footer-copyright text-center py-3"> <a class="text-reset fw-bold" href="https://eventronics.de/">Marcel Peterkau</a></span>
<span class="text-muted">© 2023 -
<a class="text-reset fw-bold" href="https://hiabuto.de/">Hiabuto Defense Systems</a></span>
</div>
</div> </div>
</footer> </div>
</footer>
<!-- Footer --> <!-- Footer -->
<!-- Modal Dialog --> <!-- Modal Dialog -->
<div class="modal fade" id="dtcModal" tabindex="-1" role="dialog" aria-labelledby="dtcModalLabel" aria-hidden="true"> <div class="modal fade" id="dtcModal" tabindex="-1" role="dialog" aria-labelledby="dtcModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document"> <div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="dtcModalLabel">DTC-Description</h5> <h5 class="modal-title" id="dtcModalLabel">DTC-Description</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"> <button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span> <span aria-hidden="true">&times;</span>
</button> </button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p class="dtc-desc">DTC Description</p> <p class="dtc-desc">DTC Description</p>
<p class="dtc-debugval">DTC DebugVal</p> <p class="dtc-debugval">DTC DebugVal</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button> <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<!-- Modal Dialog --> <!-- Modal Dialog -->
<script>
$('.navbar-nav>li>a').on('click', function () {
$('.navbar-collapse').collapse('hide');
});
document.querySelector('.custom-file-input').addEventListener('change', function (e) {
var fileName = document.getElementById("fw-update-file").files[0].name;
var nextSibling = e.target.nextElementSibling
nextSibling.innerText = fileName
});
$(document).ready(function () {
$("tr[data-dtc]").each(function (i) {
$(this).attr('data-toggle', "modal");
$(this).attr('data-target', "#dtcModal");
});
});
$('#dtcModal').on('show.bs.modal', function (event) {
var dtctr = $(event.relatedTarget)
var dtc = dtctr.data('dtc')
var debugval = dtctr.data('debugval')
var modal = $(this)
$.getJSON('static/tt_dtc/dtc_' + dtc + '.json', function (data) {
modal.find('.modal-title').text(data.title)
modal.find('.dtc-desc').text(data.description)
if (debugval > 0) {
modal.find('.dtc-debugval').text("Debugvalue: " + debugval)
}
else {
modal.find('.dtc-debugval').remove()
}
}).fail(function () {
console.log("An error has occurred.");
modal.find('.modal-title').text("Fehler")
modal.find('.dtc-desc').text("DTC-Beschreibung konnte nicht geladen werden")
});
});
</script>
</body> </body>

View File

@@ -1,28 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>KTM CAN Chain Oiler</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/custom.css">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
<link rel="manifest" href="static/img/site.webmanifest">
<meta http-equiv="refresh" content="3; url='index.htm'" />
</head>
<body>
<div class="container" style="display: flex; justify-content: center; align-items: center; height: 100vh">
<div class="alert alert-success">
<strong>Bitte warten!</strong> Änderungen werden übernommen.
</div>
</div>
</body>
</html>

View File

@@ -4,7 +4,7 @@
* Copyright 2011-2018 Twitter, Inc. * Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/ */
:root { :root {
--blue: #007bff; --blue: #007bff;
--indigo: #6610f2; --indigo: #6610f2;
--purple: #6f42c1; --purple: #6f42c1;

View File

@@ -8438,4 +8438,23 @@ 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

@@ -25,3 +25,66 @@ hr {
padding: 10px; padding: 10px;
} }
#overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8); /* Dunkler Hintergrund mit Transparenz */
color: white; /* Textfarbe */
justify-content: center;
align-items: center;
z-index: 9999; /* Stellen Sie sicher, dass es über anderen Elementen liegt */
}
.overlay-content {
text-align: center;
font-size: 4rem;
}
.loader {
width: 96px;
height: 96px;
border: 12px solid #FFF;
border-radius: 50%;
display: inline-block;
position: relative;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border-radius: 50%;
border: 12px solid transparent;
border-bottom-color: #FF3D00;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.notification-container {
position: fixed;
top: 30%;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
}
.notification {
margin-bottom: 20px; /* Fügen Sie bei Bedarf weitere Stile hinzu */
}

View File

@@ -0,0 +1,221 @@
const jsonFilePath = "static/dtc_table.json";
var dtcState = {};
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") {
dtcState = {};
return;
}
for (var i = 0; i < dtcArray.length; i++) {
var dtcInfo = dtcArray[i].split(",");
var errorCode = parseInt(dtcInfo[1]);
var activity = parseInt(dtcInfo[3]);
var severity = parseInt(dtcInfo[2]);
try {
var { title, description } = await getDescriptionForDTCNumber(errorCode);
switch (severity) {
case 1:
severity = "info";
break;
case 2:
severity = "warning";
break;
case 3:
severity = "danger";
break;
}
if (dtcState[errorCode]) {
// Überprüfen, ob sich der Zustand von "previous" auf "active" geändert hat
if (activity !== dtcState[errorCode].activity) {
dtcState[errorCode].activity = activity;
if (activity === 1) showNotification(description, severity);
}
} else {
// DTC ist neu, Zustand speichern und wenn active, Benachrichtigung anzeigen
dtcState[errorCode] = { activity: activity };
if (activity === 1) showNotification(description, severity);
}
} catch (error) {
console.error("Error processing DTC:", error);
}
}
}
function getDescriptionForDTCNumber(number) {
return new Promise((resolve, reject) => {
fetch(jsonFilePath)
.then((response) => response.json())
.then((data) => {
const dtcList = data.dtc_table_data;
const foundEntry = dtcList.find((entry) => entry.num === number);
if (foundEntry) {
const description = foundEntry.description;
const title = foundEntry.title;
resolve({ title, description });
} else {
// Wenn die Nummer nicht gefunden wurde, geben Sie einen Fehler zurück
reject(`Beschreibung für Nummer ${number} nicht gefunden.`);
}
})
.catch((error) => {
// Im Fehlerfall geben Sie den Fehler zurück
reject(error);
});
});
}
async function showDTCModal(event) {
var dtc = parseInt(event.currentTarget.getAttribute("data-dtc"));
var debugval = event.currentTarget.getAttribute("data-debugval");
var modal = $("#dtcModal");
try {
var { title, description } = await getDescriptionForDTCNumber(dtc);
modal.find(".modal-title").text(title);
modal.find(".dtc-desc").text(description);
if (debugval > 0) {
modal.find(".dtc-debugval").text("Debugvalue: " + debugval);
} else {
modal.find(".dtc-debugval").remove();
}
} catch (error) {
console.error("Fehler beim Abrufen der Beschreibung:", error);
modal.find(".modal-title").text("Fehler");
modal
.find(".dtc-desc")
.text("DTC-Beschreibung konnte nicht geladen werden");
}
// Modal anzeigen
modal.modal("show");
}
function fillDTCTable(dtcArray) {
// Referenz auf das Tabellen-Element
var table = document.getElementById("dtc_table");
var tablediv = document.getElementById("dtc_container");
// Prüfen, ob DTC vorhanden sind
if (dtcArray.length === 0 || dtcArray[0] == "0") {
// Verstecke das Tabellen-Div, wenn keine DTC vorhanden sind
tablediv.hidden = true;
table.innerHTML = "";
return;
}
// 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
tablediv.hidden = false;
// Tabelle leeren, bevor sie neu gefüllt wird
table.innerHTML = "";
// Überschriften für die Tabelle erstellen
var headerRow = table.insertRow(0);
// Definition der Klassen und Scopes für die Spalten
var columnDefinitions = [
{ class: "col-6", scope: "Zeitstempel" },
{ class: "col-2", scope: "Fehlercode" },
{ class: "col-2", scope: "Schwere" },
{ class: "col-2", scope: "Aktiv" },
];
for (var i = 0; i < columnDefinitions.length; i++) {
var headerCell = headerRow.insertCell(i);
headerCell.className = `th ${columnDefinitions[i].class}`;
headerCell.scope = columnDefinitions[i].scope;
headerCell.innerHTML = columnDefinitions[i].scope;
}
// DTC-Daten in die Tabelle einfügen
for (var i = 0; i < dtcArray.length; i++) {
var dtcInfo = dtcArray[i].split(",");
var row = table.insertRow(i + 1); // +1 wegen der Überschriftenzeile
// Zeitstempel
var timestampCell = row.insertCell(0);
timestampCell.innerHTML = formatTimestamp(parseInt(dtcInfo[0]));
// Fehlercode
var errorCodeCell = row.insertCell(1);
errorCodeCell.innerHTML = dtcInfo[1];
// Schwere
var severityCell = row.insertCell(2);
var severity = parseInt(dtcInfo[2]);
// Schwere
switch (severity) {
case 1:
severityCell.innerHTML = '<img src="static/img/info.png" alt="Info" />';
break;
case 2:
severityCell.innerHTML =
'<img src="static/img/warn.png" alt="Warnung" />';
break;
case 3:
severityCell.innerHTML =
'<img src="static/img/critical.png" alt="Kritisch" />';
break;
default:
severityCell.innerHTML =
'<img src="static/img/none.png" alt="Unbekannt" />';
}
row.setAttribute("data-dtc", dtcInfo[1]);
row.setAttribute("data-debugval", dtcInfo[4]);
row.addEventListener("click", showDTCModal);
// Aktivität
var activityCell = row.insertCell(3);
activityCell.innerHTML = parseInt(dtcInfo[3]) === 1 ? "active" : "previous";
}
}
function formatTimestamp(milliseconds) {
const date = new Date(milliseconds);
const days = String(date.getUTCDate() - 1).padStart(2, "0");
const hours = String(date.getUTCHours()).padStart(2, "0");
const minutes = String(date.getUTCMinutes()).padStart(2, "0");
const seconds = String(date.getUTCSeconds()).padStart(2, "0");
const millisecondsFormatted = String(date.getUTCMilliseconds()).padStart(
3,
"0"
);
return `${days}-${hours}:${minutes}:${seconds}:${millisecondsFormatted}`;
}

View File

@@ -0,0 +1,27 @@
$(document).ready(function () {
$(".navbar-nav a").on("click", function () {
$(".navbar-collapse").collapse("hide");
});
$("#show_hide_password a").on("click", function (event) {
event.preventDefault();
if ($("#show_hide_password input").attr("type") == "text") {
$("#show_hide_password input").attr("type", "password");
$("#show_hide_password i").addClass("fa-eye-slash");
$("#show_hide_password i").removeClass("fa-eye");
} else if ($("#show_hide_password input").attr("type") == "password") {
$("#show_hide_password input").attr("type", "text");
$("#show_hide_password i").removeClass("fa-eye-slash");
$("#show_hide_password i").addClass("fa-eye");
}
});
});
document
.querySelector(".custom-file-input")
.addEventListener("change", function (e) {
var fileName = document.getElementById("fw-update-file").files[0].name;
var nextSibling = e.target.nextElementSibling;
nextSibling.innerText = fileName;
});

View File

@@ -1,7 +1,15 @@
var gateway = `ws://${window.location.hostname}/ws`; var gateway = `ws://${window.location.hostname}/ws`;
var websocket; var websocket;
window.addEventListener("load", onLoad); var statusMapping;
var staticMapping;
var overlay;
document.addEventListener("DOMContentLoaded", function () {
// Ihr JavaScript-Code hier, einschließlich der onLoad-Funktion
overlay = document.getElementById("overlay");
onLoad();
});
function initWebSocket() { function initWebSocket() {
console.log("Trying to open a WebSocket connection..."); console.log("Trying to open a WebSocket connection...");
@@ -12,12 +20,27 @@ function initWebSocket() {
} }
function initButtons() { function initButtons() {
document var elements = document.getElementsByClassName("btn-wsevent");
.getElementById("btn-ws-stop")
.addEventListener("click", livedebug_stop); if (elements.length > 0) {
document for (var i = 0; i < elements.length; i++) {
.getElementById("btn-ws-start") let element = elements[i];
.addEventListener("click", livedebug_start); element.addEventListener("click", sendButton);
}
}
}
function initSettingInputs() {
var elements = document.getElementsByClassName("set-wsevent");
if (elements.length > 0) {
for (var i = 0; i < elements.length; i++) {
let element = elements[i];
element.addEventListener("change", function () {
websocket_sendevent("set-" + element.id, element.value);
});
}
}
} }
function onOpen(event) { function onOpen(event) {
@@ -26,28 +49,94 @@ function onOpen(event) {
function onClose(event) { function onClose(event) {
console.log("Connection closed"); console.log("Connection closed");
setTimeout(initWebSocket, 2000); setTimeout(initWebSocket, 1000);
overlay.style.display = "flex";
}
function sendButton(event) {
var targetElement = event.target;
if (
targetElement.classList.contains("confirm") &&
window.confirm("Sicher?") == false
)
return;
websocket_sendevent("btn-" + targetElement.id, targetElement.value);
} }
function onMessage(event) { function onMessage(event) {
var livedebug_out = document.getElementById("livedebug-out"); var data = event.data;
var textarea_heigth = livedebug_out.scrollHeight;
livedebug_out.value += event.data; if (data.startsWith("NOTIFY:")) {
livedebug_out.scrollTop = livedebug_out.scrollHeight; var notify_data = data.slice(7).split(";")[1];
do_resize(livedebug_out); var notify_type = data.slice(7).split(";")[0];
showNotification(notify_data, notify_type);
} else if (data.startsWith("DEBUG:")) {
var addtext = data.slice(6);
var livedebug_out = document.getElementById("livedebug-out");
livedebug_out.value += addtext;
livedebug_out.scrollTop = livedebug_out.scrollHeight;
do_resize(livedebug_out);
} else if (data.startsWith("DTC:")) {
const dtcs = data.slice(4);
const dtcArray = dtcs.trim() !== "" ? dtcs.split(";").filter(Boolean) : [];
processDTCNotifications(dtcArray);
fillDTCTable(dtcArray);
} else if (data.startsWith("MAPPING_STATUS:")) {
const data_sliced = data.slice(15);
statusMapping = createMapping(data_sliced);
} else if (data.startsWith("MAPPING_STATIC:")) {
const data_sliced = data.slice(15);
staticMapping = createMapping(data_sliced);
console.log(staticMapping);
} else if (data.startsWith("STATUS:")) {
const data_sliced = data.slice(7);
const result = processDataString(data_sliced, statusMapping);
fillValuesToHTML(result);
} else if (data.startsWith("STATIC:")) {
const data_sliced = data.slice(7);
const result = processDataString(data_sliced, staticMapping);
fillValuesToHTML(result);
console.log(result);
overlay.style.display = "none";
}
}
function createMapping(mappingString) {
const mappingArray = mappingString.split(";");
const mapping = [];
mappingArray.forEach((variable) => {
if (variable !== null) mapping.push(variable.trim());
});
return mapping;
}
function processDataString(dataString, mapping) {
const valuesArray = dataString.split(";");
const dataObject = {};
valuesArray.forEach((value, index) => {
const variable = mapping[index];
if (variable) {
dataObject[variable] = value.trim();
}
});
return dataObject;
} }
function onLoad(event) { function onLoad(event) {
initWebSocket(); initWebSocket();
initButtons(); initButtons();
initSettingInputs();
overlay.style.display = "flex";
} }
function livedebug_start() { function websocket_sendevent(element_id, element_value) {
websocket.send("start"); websocket.send(element_id + ":" + element_value);
}
function livedebug_stop() {
websocket.send("stop");
} }
function do_resize(textbox) { function do_resize(textbox) {
@@ -67,18 +156,120 @@ function do_resize(textbox) {
else textbox.rows = rows; else textbox.rows = rows;
} }
function notifyMe() { function fillValuesToHTML(dataset) {
if (!("Notification" in window)) { for (var key in dataset) {
alert("This browser does not support desktop notification"); var key_prefixed = "data-" + key;
} else if (Notification.permission === "granted") { var elements = document.getElementsByClassName(key_prefixed);
const notification = new Notification("Hi there!");
// … if (elements.length > 0) {
} else if (Notification.permission !== "denied") { for (var i = 0; i < elements.length; i++) {
Notification.requestPermission().then((permission) => { var element = elements[i];
if (permission === "granted") {
const notification = new Notification("Hi there!"); if (element.type === "checkbox") {
// // Wenn das Element ein Kontrollkästchen ist
element.checked = dataset[key] == 1 ? true : false;
} else if (element.tagName === "SELECT") {
// Wenn das Element ein Dropdown ist
setDropdownValue(element, dataset[key]);
} else if (element.classList.contains("progress-bar")) {
// Wenn das Element eine Fortschrittsleiste ist
updateProgressBar(element, dataset[key]);
} else if (element.classList.contains("hideable")) {
// Wenn das Element ein Settingsabschnitt-div ist
if (dataset[key] == 0) element.style.display = "none";
else element.style.display = "";
} else if (element.tagName === "DIV" || element.tagName === "SPAN") {
if (element.classList.contains("format-time")) {
element.innerText = formatTime(dataset[key]);
} else if (element.classList.contains("faction-logo")) {
// Faction-Logo-Logik
updateFactionLogo(element, dataset[key]);
} else {
element.innerText = dataset[key];
}
} else {
// Standardmäßig für Textfelder und andere Elemente
element.value = dataset[key];
}
} }
}); }
// Aktualisiere den <title>-Tag, wenn der Schlüssel 'devicename' im dataset vorhanden ist
if (key === "devicename") {
document.title = dataset[key];
}
} }
} }
function formatTime(seconds) {
var hrs = Math.floor(seconds / 3600);
var mins = Math.floor((seconds % 3600) / 60);
var secs = seconds % 60;
return (
String(hrs).padStart(2, "0") +
":" +
String(mins).padStart(2, "0") +
":" +
String(secs).padStart(2, "0")
);
}
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
function setDropdownValue(selectElement, value) {
for (var i = 0; i < selectElement.options.length; i++) {
if (selectElement.options[i].value === value) {
selectElement.selectedIndex = i;
break;
}
}
}
// Funktion zum Aktualisieren der Fortschrittsleiste
function updateProgressBar(progressBar, value) {
// Wert in das aria-valuenow-Attribut einfügen
progressBar.setAttribute("aria-valuenow", value);
// Breite des Fortschrittsbalkens und inneren Text aktualisieren
progressBar.style.width = value + "%";
progressBar.textContent = value + "%";
}
function showNotification(message, type) {
// Erstellen Sie ein Bootstrap-Alert-Element
var alertElement = $(
'<div class="alert alert-' +
type +
' alert-dismissible fade show notification" role="alert">' +
"<strong>" +
message +
"</strong>" +
'<button type="button" class="close" data-dismiss="alert" aria-label="Close">' +
'<span aria-hidden="true">&times;</span>' +
"</button>" +
"</div>"
);
// Fügen Sie das Alert-Element dem Container hinzu
$("#notification-container").append(alertElement);
// Nach 5 Sekunden das Alert-Element ausblenden
setTimeout(function () {
alertElement.alert("close");
}, 5000);
}

View File

@@ -1,4 +0,0 @@
{
"title": "kein EEPROM gefunden",
"description": "Es wurde kein EEPROM gefunden. Dies lässt einen Hardware-Defekt vermuten."
}

View File

@@ -1,4 +0,0 @@
{
"title": "Power-Monitor Fehler",
"description": "Es gibt ein Problem mit dem Power-Monitoring. Die Akku-Überwachung ist ohne Funktion! Bitte Hardware prüfen"
}

View File

@@ -1,4 +0,0 @@
{
"title": "Akku-Spannung niedrig",
"description": "Die Akkuspannung ist niedrig. Bitte Akku bald aufladen!"
}

View File

@@ -1,4 +0,0 @@
{
"title": "Akku-Spannung kritisch",
"description": "Die Akkuspannung ist sehr niedrig. Bitte Akku umgehend ersetzen um eine schädliche Tiefentladung zu vermeiden!"
}

View File

@@ -1,4 +0,0 @@
{
"title": "EEPROM-Migration failed",
"description": "Migration of EEPROM-Image from an other FW-Version failed - you need to reset everything manually!"
}

View File

@@ -1,4 +0,0 @@
{
"title": "EEPROM CFG Checksumme",
"description": "Die Checksumme der Config-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

View File

@@ -1,4 +0,0 @@
{
"title": "EEPROM PDS Checksumme",
"description": "Die Checksumme der Betriebsdaten-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

View File

@@ -1,4 +0,0 @@
{
"title": "EEPROM PDS Adresse",
"description": "Die Adresse der Betriebsdaten-Partition im EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

View File

@@ -1,4 +0,0 @@
{
"title": "EEPROM Version falsch",
"description": "Die Layout-Version des EEPROM stimmt nicht mit der Firmware-Version überein. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

View File

@@ -1,4 +0,0 @@
{
"title": "Flashstorage Fehler",
"description": "Der Flashstorage konnte nicht initialisiert werden. Aktualisieren sie Flash & Firmware"
}

View File

@@ -1,4 +0,0 @@
{
"title": "Flashstorage Version falsch",
"description": "Die Version des Flashstorage stimmt nicht mit der Firmware-Version überein. Aktualisieren sie den Flash mit der passenden Update-Datei"
}

View File

@@ -1,4 +0,0 @@
{
"title": "Config-Validierung",
"description": "Ein oder mehrer Einstellungswerte sind ausserhalb plausibler Werte. Prüfen Sie Ihre Einstellungen"
}

View File

@@ -1,4 +0,0 @@
{
"title": "LoRa-Modul Fehler",
"description": "Es gibt ein Problem mit dem LoRa-Modul. Es konnte keine LoRa-Verbindung aufgebaut werden. Bitte Hardware prüfen"
}

View File

@@ -1 +0,0 @@
1.04

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
@@ -50,14 +52,24 @@
typedef enum eSystem_Status typedef enum eSystem_Status
{ {
sysStat_Init,
sysStat_Startup, sysStat_Startup,
sysStat_Normal, sysStat_Normal,
sysStat_Rain,
sysStat_Purge,
sysStat_Error, sysStat_Error,
sysStat_Shutdown sysStat_Shutdown
} tSystem_Status; } tSystem_Status;
typedef enum batteryType_e
{
BATTERY_UNDEFINED,
BATTERY_LIPO_2S,
BATTERY_LIPO_3S
} batteryType_t;
// String representation of SpeedSource enum
extern const char *BatteryString[];
extern const size_t BatteryString_Elements;
#define STARTUP_DELAY 2500 #define STARTUP_DELAY 2500
#define SHUTDOWN_DELAY_MS 2500 #define SHUTDOWN_DELAY_MS 2500

View File

@@ -17,12 +17,15 @@
#include <Arduino.h> #include <Arduino.h>
#include "webui.h" #include "webui.h"
const char PROGMEM helpCmd[] = "sysinfo - System Info\n" const char PROGMEM helpCmd[] = "sysinfo - System Info\n"
"reboot - System Reboot\n"
"netinfo - WiFi Info\n" "netinfo - WiFi Info\n"
"formatPDS - Format Persistence EEPROM Data\n" "formatPDS - Format Persistence EEPROM Data\n"
"formatCFG - Format Configuration EEPROM Data\n" "formatCFG - Format Configuration EEPROM Data\n"
"checkEE - Check EEPROM with checksum\n" "checkEE - Check EEPROM with checksum\n"
"dumpEE1k - dump the first 1kb of EEPROM to Serial\n" "dumpEE1k - dump the first 1kb of EEPROM to Serial\n"
"dumpEE - dump the whole EPPROM to Serial\n" "dumpEE - dump the whole EPPROM to Serial\n"
"killEE - kill the first 1024 byte of EEPROM\n"
"zeroEE - zero the first 1024 byte of EEPROM\n"
"resetPageEE - Reset the PersistenceData Page\n" "resetPageEE - Reset the PersistenceData Page\n"
"dumpCFG - print Config struct\n" "dumpCFG - print Config struct\n"
"dumpPDS - print PersistanceStruct\n" "dumpPDS - print PersistanceStruct\n"

View File

@@ -57,20 +57,6 @@ typedef struct
uint32_t checksum; uint32_t checksum;
} persistenceData_t; } persistenceData_t;
typedef enum
{
BATTERY_UNDEFINED,
BATTERY_LIPO_2S,
BATTERY_LIPO_3S
} batteryType_t;
const char BatteryString[][10]{
"Undefined",
"LiPo 2S",
"LiPo 3S"};
const size_t BatteryString_Elements = sizeof(BatteryString) / sizeof(BatteryString[0]);
// Structure for configuration settings stored in EEPROM // Structure for configuration settings stored in EEPROM
typedef struct typedef struct
{ {
@@ -96,7 +82,7 @@ const configData_t ConfigData_defaults = {
"FACTION 1", // Faction_1_Name "FACTION 1", // Faction_1_Name
"FACTION 2", // Faction_2_Name "FACTION 2", // Faction_2_Name
"FACTION 3", // Faction_3_Name "FACTION 3", // Faction_3_Name
"ArisoftTimer", HOST_NAME,
QUOTE(WIFI_AP_PASSWORD), QUOTE(WIFI_AP_PASSWORD),
QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_SSID_CLIENT),
QUOTE(WIFI_PASSWORD_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT),
@@ -104,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();
@@ -116,7 +102,8 @@ uint32_t Checksum_EEPROM(uint8_t const *data, size_t len);
void dumpEEPROM(uint16_t memoryAddress, uint16_t length); void dumpEEPROM(uint16_t memoryAddress, uint16_t length);
void MovePersistencePage_EEPROM(boolean reset); void MovePersistencePage_EEPROM(boolean reset);
uint32_t ConfigSanityCheck(bool autocorrect = false); uint32_t ConfigSanityCheck(bool autocorrect = false);
bool validateWiFiString(char *string, size_t size); void writeSequentialToEEPROM(uint16_t memoryAddress, uint16_t length);
void writeZeroToEEPROM(uint16_t memoryAddress, uint16_t length);
extern configData_t ConfigData; extern configData_t ConfigData;
extern persistenceData_t PersistenceData; extern persistenceData_t PersistenceData;

View File

@@ -10,7 +10,8 @@ typedef struct Globals_s
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 */ 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[25]; /**< 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

@@ -19,10 +19,10 @@
#endif #endif
#ifdef FEATURE_ENABLE_WIFI_CLIENT #ifdef FEATURE_ENABLE_WIFI_CLIENT
#ifndef WIFI_CLIENT_PASSWORD #ifndef WIFI_PASSWORD_CLIENT
#error "You must define an WIFI_PASSWORD for OTA-Update" #error "You must define an WIFI_PASSWORD for OTA-Update"
#endif #endif
#ifndef WIFI_CLIENT_SSID #ifndef WIFI_SSID_CLIENT
#error "You must define an WIFI_SSID for OTA-Update" #error "You must define an WIFI_SSID for OTA-Update"
#endif #endif
#endif #endif

View File

@@ -0,0 +1,36 @@
#ifndef UTILITIES_H
#define UTILITIES_H
#include <Arduino.h>
/**
* @brief Validates whether a given string contains only characters allowed in WiFi SSIDs and passwords.
*
* This function checks each character in the provided string to ensure
* that it contains only characters allowed in WiFi SSIDs and passwords.
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
*
* @param string Pointer to the string to be validated.
* @param size Size of the string including the null-terminator.
* @return true if the string contains only allowed characters or is NULL,
* false otherwise.
*/
bool validateWiFiString(char *string, size_t size);
/**
* @brief Copies a string to a buffer, replacing invalid WiFi SSID characters with a placeholder.
*
* This function checks each character in the provided input string to ensure
* that it contains only characters allowed in WiFi SSIDs and passwords. If a character
* is invalid, it replaces it with a placeholder character (e.g., '_').
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
*
* @param input Pointer to the input string to be validated and copied.
* @param buffer Pointer to the buffer where the output string will be copied.
* @param bufferSize Size of the buffer including the null-terminator.
*/
void sanitizeWiFiString(const char *input, char *buffer, size_t bufferSize);
#endif // UTILITIES_H

View File

@@ -17,24 +17,26 @@ platform = espressif8266
framework = arduino framework = arduino
board = d1_mini board = d1_mini
custom_firmware_version = 1.07
custom_flash_version = 1.07
upload_protocol = esptool upload_protocol = esptool
upload_speed = 921600 upload_speed = 921600
;upload_port = 10.0.1.48 ;upload_port = 10.0.1.48
;upload_protocol = espota ;upload_flags =
;upload_flags = ; --port=8266
; --auth=${wifi_cred.ota_password} ; --auth=${wifi_cred.admin_password}
build_flags= build_flags=
!python codegen/git_rev_macro.py
-DATOMIC_FS_UPDATE -DATOMIC_FS_UPDATE
-DFEATURE_ENABLE_WIFI_CLIENT ;-DFEATURE_ENABLE_WIFI_CLIENT
;-DFEATURE_ENABLE_LORA ;-DFEATURE_ENABLE_LORA
-DFEATURE_ENABLE_UARTLORA -DFEATURE_ENABLE_UARTLORA
-DWIFI_AP_IP_GW=10,0,0,1 -DWIFI_AP_IP_GW=10,0,0,1
-DADMIN_PASSWORD=${wifi_cred.ota_password} -DADMIN_PASSWORD=${wifi_cred.admin_password}
-DWIFI_CLIENT_SSID=${wifi_cred.wifi_client_ssid} -DWIFI_SSID_CLIENT=${wifi_cred.wifi_ssid_client}
-DWIFI_CLIENT_PASSWORD=${wifi_cred.wifi_client_password} -DWIFI_PASSWORD_CLIENT=${wifi_cred.wifi_password_client}
-DWIFI_AP_SSID=${wifi_cred.wifi_ap_ssid} -DADMIN_PASSWORD=${wifi_cred.admin_password}
-DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password} -DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password}
-DDEVICE_NAME='"Dark Emergency Timer"' -DDEVICE_NAME='"Dark Emergency Timer"'

9
Software/src/common.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include "common.h"
const char *BatteryString[] = {
"Undefined",
"LiPo 2S",
"LiPo 3S"
};
const size_t BatteryString_Elements = sizeof(BatteryString) / sizeof(BatteryString[0]);

View File

@@ -15,7 +15,17 @@
DebugStatus_t DebuggerStatus[dbg_cntElements]; DebugStatus_t DebuggerStatus[dbg_cntElements];
void processCmdDebug(String command); // Funktionszeiger
typedef void (*CommandFunction)();
// Struktur zur Zuordnung von Commands zu Funktionen
struct CommandMapping
{
const char *command;
CommandFunction function;
};
void processCmdDebug(const char *command);
void Debug_formatCFG(); void Debug_formatCFG();
void Debug_formatPersistence(); void Debug_formatPersistence();
void Debug_printSystemInfo(); void Debug_printSystemInfo();
@@ -26,8 +36,97 @@ void Debug_dumpPersistance();
void Debug_ShowDTCs(); void Debug_ShowDTCs();
void Debug_dumpGlobals(); void Debug_dumpGlobals();
void Debug_printHelp(); void Debug_printHelp();
void Debug_Reboot();
const char *uint32_to_binary_string(uint32_t num); const char *uint32_to_binary_string(uint32_t num);
// Adapter-Functions for Debug-Commands
void adapterCheckEEPOM() { Debug_CheckEEPOM(false); }
void adapterCheckEEPOMFix() { Debug_CheckEEPOM(true); }
void adapterDumpEEPROM1k() { dumpEEPROM(0, 1024); }
void adapterDumpEEPROMAll() { dumpEEPROM(0, EEPROM_SIZE_BYTES); }
void adapterKillEEPROM() { writeSequentialToEEPROM(0, 1024); }
void adapterZeroEEPROM() { writeZeroToEEPROM(0, 1024); }
void adapterResetPageEEPROM() { MovePersistencePage_EEPROM(true); }
void adapterSaveEEPROM() { globals.requestEEAction = EE_ALL_SAVE; }
void adapterSetDebugPort() { SetDebugportStatus(dbg_Serial, enabled); }
void adapterCritDTC() { MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis()); }
void adapterWarnDTC() { MaintainDTC(DTC_FAKE_DTC_WARN, true, millis()); }
void adapterInfoDTC() { MaintainDTC(DTC_FAKE_DTC_INFO, true, millis()); }
void adapterNotifyError() { Websocket_PushNotification("Debug Error Notification", error); }
void adapterNotifyWarning() { Websocket_PushNotification("Debug Warning Notification", warning); }
void adapterNotifySuccess() { Websocket_PushNotification("Debug Success Notification", success); }
void adapterNotifyInfo() { Websocket_PushNotification("Debug Info Notification", info); }
// Definition der Command-Mapping-Tabelle
const CommandMapping commandMappings[] = {
{"help", Debug_printHelp},
{"reboot", Debug_Reboot},
{"sysinfo", Debug_printSystemInfo},
{"netinfo", Debug_printWifiInfo},
{"formatCFG", Debug_formatCFG},
{"formatPDS", Debug_formatPersistence},
{"checkEE", adapterCheckEEPOM},
{"checkEEfix", adapterCheckEEPOMFix},
{"dumpEE1k", adapterDumpEEPROM1k},
{"dumpEE", adapterDumpEEPROMAll},
{"killEE", adapterKillEEPROM},
{"zeroEE", adapterZeroEEPROM},
{"resetPageEE", adapterResetPageEEPROM},
{"dumpCFG", Debug_dumpConfig},
{"dumpPDS", Debug_dumpPersistance},
{"saveEE", adapterSaveEEPROM},
{"dumpGlobals", Debug_dumpGlobals},
{"sdbg", adapterSetDebugPort},
{"dtc_show", Debug_ShowDTCs},
{"dtc_clear", ClearAllDTC},
{"dtc_crit", adapterCritDTC},
{"dtc_warn", adapterWarnDTC},
{"dtc_info", adapterInfoDTC},
{"notify_error", adapterNotifyError},
{"notify_warning", adapterNotifyWarning},
{"notify_success", adapterNotifySuccess},
{"notify_info", adapterNotifyInfo},
};
const size_t NUM_COMMANDS = sizeof(commandMappings) / sizeof(commandMappings[0]);
const char helpText[][64] PROGMEM = {
"help - Print this help text",
"sysinfo - System Info",
"reboot - System Reboot",
"netinfo - WiFi Info",
"formatPDS - Format Persistence EEPROM Data",
"formatCFG - Format Configuration EEPROM Data",
"checkEE - Check EEPROM with checksum",
"checkEEfix - Check and fix EEPROM with checksum",
"dumpEE1k - Dump the first 1kb of EEPROM to Serial",
"dumpEE - Dump the whole EEPROM to Serial",
"killEE - Kill the first 1024 bytes of EEPROM",
"zeroEE - Zero the first 1024 bytes of EEPROM",
"resetPageEE - Reset the PersistenceData Page",
"dumpCFG - Print Config struct",
"dumpPDS - Print PersistenceStruct",
"saveEE - Save EE-Data",
"dumpGlobals - Print globals",
"sdbg - Set debug port status",
"dtc_show - Show all DTCs",
"dtc_clear - Clear all DTCs",
"dtc_crit - Maintain critical DTC",
"dtc_warn - Maintain warning DTC",
"dtc_info - Maintain info DTC",
"notify_error - Send error notification",
"notify_warning - Send warning notification",
"notify_success - Send success notification",
"notify_info - Send info notification"
};
const size_t NUM_HELP_LINES = sizeof(helpText) / sizeof(helpText[0]);
// Überprüfen, ob die Anzahl der Commands und Hilfetext-Zeilen übereinstimmen
static_assert(NUM_COMMANDS == NUM_HELP_LINES, "Number of commands and help text lines do not match!");
/** /**
* @brief Initializes the debugger by setting the initial status for different debug ports. * @brief Initializes the debugger by setting the initial status for different debug ports.
* Serial debug output is turned off. * Serial debug output is turned off.
@@ -109,7 +208,7 @@ void Debug_Process()
break; break;
case CMD_COMPLETE: case CMD_COMPLETE:
processCmdDebug(String(inputBuffer)); processCmdDebug(inputBuffer);
break; break;
case CMD_OVERFLOW: case CMD_OVERFLOW:
@@ -179,65 +278,27 @@ void Debug_pushMessage(const char *format, ...)
} }
} }
/** /**
* @brief Processes a debug command and performs corresponding actions. * @brief Processes a debug command and performs corresponding actions.
* *
* @param command The debug command to be processed. * @param command The debug command to be processed.
*/ */
void processCmdDebug(String command) void processCmdDebug(const char *command)
{ {
// Check the received command and execute corresponding actions bool commandFound = false;
if (command == "help") for (size_t i = 0; i < NUM_COMMANDS; ++i)
Debug_printHelp(); {
else if (command == "sysinfo") if (strcmp(command, commandMappings[i].command) == 0)
Debug_printSystemInfo(); {
else if (command == "netinfo") commandMappings[i].function();
Debug_printWifiInfo(); commandFound = true;
else if (command == "formatCFG") break;
Debug_formatCFG(); }
else if (command == "formatPDS") }
Debug_formatPersistence(); if (!commandFound)
else if (command == "checkEE") {
Debug_CheckEEPOM(false);
else if (command == "checkEEfix")
Debug_CheckEEPOM(true);
else if (command == "dumpEE1k")
dumpEEPROM(0, 1024);
else if (command == "dumpEE")
dumpEEPROM(0, EEPROM_SIZE_BYTES);
else if (command == "resetPageEE")
MovePersistencePage_EEPROM(true);
else if (command == "dumpCFG")
Debug_dumpConfig();
else if (command == "dumpPDS")
Debug_dumpPersistance();
else if (command == "saveEE")
globals.requestEEAction = EE_ALL_SAVE;
else if (command == "dumpGlobals")
Debug_dumpGlobals();
else if (command == "sdbg")
SetDebugportStatus(dbg_Serial, enabled);
else if (command == "dtc_show")
Debug_ShowDTCs();
else if (command == "dtc_clear")
ClearAllDTC();
else if (command == "dtc_crit")
MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis());
else if (command == "dtc_warn")
MaintainDTC(DTC_FAKE_DTC_WARN, true, millis());
else if (command == "dtc_info")
MaintainDTC(DTC_FAKE_DTC_INFO, true, millis());
else if (command == "notify_error")
Websocket_PushNotification("Debug Error Notification", error);
else if (command == "notify_warning")
Websocket_PushNotification("Debug Warning Notification", warning);
else if (command == "notify_success")
Websocket_PushNotification("Debug Success Notification", success);
else if (command == "notify_info")
Websocket_PushNotification("Debug Info Notification", info);
else
Debug_pushMessage("unknown Command\n"); Debug_pushMessage("unknown Command\n");
}
} }
/** /**
@@ -283,6 +344,18 @@ void Debug_printSystemInfo()
Debug_pushMessage("OTA-Pass: %s\n", QUOTE(ADMIN_PASSWORD)); Debug_pushMessage("OTA-Pass: %s\n", QUOTE(ADMIN_PASSWORD));
Debug_pushMessage("Git-Revision: %s\n", constants.GitHash); Debug_pushMessage("Git-Revision: %s\n", constants.GitHash);
Debug_pushMessage("Sw-Version: %d.%02d\n", constants.FW_Version_major, constants.FW_Version_minor); Debug_pushMessage("Sw-Version: %d.%02d\n", constants.FW_Version_major, constants.FW_Version_minor);
Debug_pushMessage("globals.systemStatus: %d\n", globals.systemStatus);
Debug_pushMessage("globals.resumeStatus: %d\n", globals.resumeStatus);
Debug_pushMessage("globals.systemStatustxt: %s\n", globals.systemStatustxt);
Debug_pushMessage("globals.requestEEAction: %d\n", globals.requestEEAction);
Debug_pushMessage("globals.DeviceName: %s\n", globals.DeviceName);
Debug_pushMessage("globals.FlashVersion: %s\n", globals.FlashVersion);
Debug_pushMessage("globals.eePersistanceAdress: %u\n", globals.eePersistanceAdress);
Debug_pushMessage("globals.hasDTC: %d\n", globals.hasDTC);
Debug_pushMessage("globals.loadvoltage_mV: %d\n", globals.loadvoltage_mV);
Debug_pushMessage("globals.battery_level: %d\n", globals.battery_level);
Debug_pushMessage("globals.timer_disabled: %d\n", globals.timer_disabled);
} }
/** /**
@@ -291,6 +364,15 @@ void Debug_printSystemInfo()
void Debug_dumpConfig() void Debug_dumpConfig()
{ {
Debug_pushMessage("batteryType: %d\n", ConfigData.batteryType); Debug_pushMessage("batteryType: %d\n", ConfigData.batteryType);
Debug_pushMessage("Faction_1_Name: %s\n", ConfigData.Faction_1_Name);
Debug_pushMessage("Faction_1_Name: %s\n", ConfigData.Faction_2_Name);
Debug_pushMessage("Faction_1_Name: %s\n", ConfigData.Faction_3_Name);
Debug_pushMessage("active_faction_on_reboot: %d\n", ConfigData.active_faction_on_reboot);
Debug_pushMessage("wifi_autoconnect: %d\n", ConfigData.wifi_autoconnect);
Debug_pushMessage("wifi_ap_password: %s\n", ConfigData.wifi_ap_password);
Debug_pushMessage("wifi_ap_ssid: %s\n", ConfigData.wifi_ap_ssid);
Debug_pushMessage("wifi_client_ssid: %s\n", ConfigData.wifi_client_ssid);
Debug_pushMessage("wifi_client_password: %s\n", ConfigData.wifi_client_password);
Debug_pushMessage("EEPROM_Version: %d\n", ConfigData.EEPROM_Version); Debug_pushMessage("EEPROM_Version: %d\n", ConfigData.EEPROM_Version);
Debug_pushMessage("checksum: 0x%08X\n", ConfigData.checksum); Debug_pushMessage("checksum: 0x%08X\n", ConfigData.checksum);
} }
@@ -389,7 +471,7 @@ void Debug_ShowDTCs()
char buff_active[9]; char buff_active[9];
// Header for the DTC display // Header for the DTC display
Debug_pushMessage("\n timestamp | DTC-Nr. | status | severity\n"); Debug_pushMessage("\n timestamp | DTC-Nr. | status | debugval\n");
// Iterate through DTCStorage and display each entry // Iterate through DTCStorage and display each entry
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++) for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
@@ -413,31 +495,37 @@ void Debug_ShowDTCs()
strcpy(buff_active, "none"); strcpy(buff_active, "none");
// Display DTC information // Display DTC information
Debug_pushMessage("%s %7d %8s %8d\n", buff_timestamp, DTCStorage[i].Number, buff_active); Debug_pushMessage("%s %7d %8s %8d\n", buff_timestamp, DTCStorage[i].Number, buff_active, DTCStorage[i].debugVal);
} }
} }
} }
/** /**
* @brief Displays the help commands for debugging through Serial or WebUI. * @brief Prints the help information stored in PROGMEM.
* Each command is printed individually in a formatted manner.
*/ */
void Debug_printHelp() void Debug_printHelp()
{ {
char buff[64]; char buffer[64];
for (size_t i = 0; i < NUM_HELP_LINES; ++i)
// Iterate through helpCmd and display each command
for (unsigned int i = 0; i < sizeof(helpCmd) / 63; i++)
{ {
// Copy a portion of helpCmd to buff for display strcpy_P(buffer, (PGM_P)pgm_read_word(&(helpText[i])));
memcpy_P(buff, (helpCmd + (i * 63)), 63);
buff[63] = 0;
// Display the help command // Display the help command
Debug_pushMessage(buff); Debug_pushMessage(buffer);
} }
} }
/**
* @brief Initiates a system reboot by setting the system status to shutdown.
*
* This function sets the global system status to `sysStat_Shutdown`,
* which will trigger a system reboot sequence.
*/
void Debug_Reboot()
{
globals.systemStatus = sysStat_Shutdown;
}
/** /**
* @brief Convert a uint32_t value to a binary string with nibbles separated by a space. * @brief Convert a uint32_t value to a binary string with nibbles separated by a space.
* *
@@ -451,12 +539,15 @@ void Debug_printHelp()
* @return A pointer to a const char string containing the binary representation * @return A pointer to a const char string containing the binary representation
* of the input number with nibbles separated by a space. * of the input number with nibbles separated by a space.
*/ */
const char* uint32_to_binary_string(uint32_t num) { const char *uint32_to_binary_string(uint32_t num)
{
static char binary_str[65]; // 32 bits + 31 spaces + null terminator static char binary_str[65]; // 32 bits + 31 spaces + null terminator
int i, j; int i, j;
for (i = 31, j = 0; i >= 0; i--, j++) { for (i = 31, j = 0; i >= 0; i--, j++)
{
binary_str[j] = ((num >> i) & 1) ? '1' : '0'; binary_str[j] = ((num >> i) & 1) ? '1' : '0';
if (i % 4 == 0 && i != 0) { if (i % 4 == 0 && i != 0)
{
binary_str[++j] = ' '; // Insert space after every nibble binary_str[++j] = ' '; // Insert space after every nibble
} }
} }

View File

@@ -9,6 +9,7 @@
#include "eeprom.h" #include "eeprom.h"
#include "debugger.h" #include "debugger.h"
#include "globals.h" #include "globals.h"
#include "utilities.h"
// Instance of I2C_eeprom for EEPROM access // Instance of I2C_eeprom for EEPROM access
I2C_eeprom ee(I2C_EE_ADDRESS, EEPROM_SIZE_BYTES); I2C_eeprom ee(I2C_EE_ADDRESS, EEPROM_SIZE_BYTES);
@@ -18,7 +19,7 @@ configData_t ConfigData;
persistenceData_t PersistenceData; persistenceData_t PersistenceData;
// EEPROM version identifier // EEPROM version identifier
const uint16_t eeVersion = EEPROM_STRUCTURE_REVISION; const uint16_t eeVersion = EEPROM_STRUCTURE_REVISION;
// Flag indicating whether EEPROM is available // Flag indicating whether EEPROM is available
boolean eeAvailable = false; boolean eeAvailable = false;
@@ -35,12 +36,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();
} }
/** /**
@@ -113,16 +115,23 @@ void EEPROM_Process()
*/ */
void StoreConfig_EEPROM() void StoreConfig_EEPROM()
{ {
// Berechnung der Prüfsumme
ConfigData.checksum = 0; ConfigData.checksum = 0;
ConfigData.checksum = Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData)); ConfigData.checksum = Checksum_EEPROM((uint8_t *)&ConfigData, sizeof(ConfigData));
// Überprüfung, ob der EEPROM verfügbar ist
if (!checkEEPROMavailable()) if (!checkEEPROMavailable())
return; return;
ee.updateBlock(startofConfigData, (uint8_t *)&ConfigData, sizeof(ConfigData)); // Byteweise in den EEPROM schreiben
uint8_t *dataPtr = (uint8_t *)&ConfigData;
for (uint16_t i = 0; i < sizeof(ConfigData); i++)
{
ee.writeByte(startofConfigData + i, dataPtr[i]);
}
// Sanity Check der Konfiguration
uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false); uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false);
if (ConfigSanityCheckResult > 0) if (ConfigSanityCheckResult > 0)
{ {
MaintainDTC(DTC_EEPROM_CFG_SANITY, true, ConfigSanityCheckResult); MaintainDTC(DTC_EEPROM_CFG_SANITY, true, ConfigSanityCheckResult);
@@ -149,10 +158,9 @@ void GetConfig_EEPROM()
ConfigData.checksum = checksum; ConfigData.checksum = checksum;
uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false); uint32_t ConfigSanityCheckResult = ConfigSanityCheck(true);
MaintainDTC(DTC_EEPROM_CFG_SANITY, (ConfigSanityCheckResult > 0), ConfigSanityCheckResult); MaintainDTC(DTC_EEPROM_CFG_SANITY, (ConfigSanityCheckResult > 0), ConfigSanityCheckResult);
} }
/** /**
@@ -174,7 +182,12 @@ void StorePersistence_EEPROM()
if (!checkEEPROMavailable()) if (!checkEEPROMavailable())
return; return;
ee.updateBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData)); // Byteweise in den EEPROM schreiben
uint8_t *dataPtr = (uint8_t *)&PersistenceData;
for (uint16_t i = 0; i < sizeof(PersistenceData); i++)
{
ee.writeByte(globals.eePersistanceAdress + i, dataPtr[i]);
}
} }
/** /**
@@ -306,7 +319,7 @@ void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
if (!checkEEPROMavailable()) if (!checkEEPROMavailable())
return; return;
char ascii_buf[BLOCK_TO_LENGTH + 1]; char ascii_buf[BLOCK_TO_LENGTH + 1] = {0};
sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII"); sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII");
// Print column headers // Print column headers
@@ -326,24 +339,26 @@ void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
// Print ASCII representation header for each block // Print ASCII representation header for each block
if (blockpoint == 0) if (blockpoint == 0)
{ {
ascii_buf[BLOCK_TO_LENGTH] = 0; if (i > 0) // Ensure we don't print an empty ASCII buffer on the first iteration
Debug_pushMessage(" %s", ascii_buf); {
ascii_buf[BLOCK_TO_LENGTH] = 0;
Debug_pushMessage(" %s", ascii_buf);
}
Debug_pushMessage("\n0x%05X:", memoryAddress); Debug_pushMessage("\n0x%05X:", memoryAddress);
memset(ascii_buf, ' ', BLOCK_TO_LENGTH); // Clear the ASCII buffer with spaces
} }
// Read and print each byte // Read and print each byte
ascii_buf[blockpoint] = ee.readByte(memoryAddress); uint8_t byte = ee.readByte(memoryAddress);
Debug_pushMessage(" %02X", ascii_buf[blockpoint]); ascii_buf[blockpoint] = (byte >= 0x20 && byte <= 0x7E) ? byte : '.';
Debug_pushMessage(" %02X", byte);
// Replace non-printable characters with dots in ASCII representation
if (ascii_buf[blockpoint] < 0x20 || ascii_buf[blockpoint] > 0x7E)
ascii_buf[blockpoint] = '.';
memoryAddress++; memoryAddress++;
} }
// Print a new line at the end of the dump // Print remaining ASCII buffer
Debug_pushMessage("\n"); ascii_buf[BLOCK_TO_LENGTH] = 0;
Debug_pushMessage(" %s\n", ascii_buf); // Final ASCII line
} }
/** /**
@@ -416,46 +431,44 @@ uint32_t ConfigSanityCheck(bool autocorrect)
return setting_reset_bits; return setting_reset_bits;
} }
/**
* @brief Validates whether a given string contains only characters allowed in WiFi SSIDs and passwords.
*
* This function checks each character in the provided string to ensure
* that it contains only characters allowed in WiFi SSIDs and passwords.
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
*
* @param string Pointer to the string to be validated.
* @param size Size of the string including the null-terminator.
* @return true if the string contains only allowed characters or is NULL,
* false otherwise.
*/
bool validateWiFiString(char *string, size_t size)
{
if (string == NULL)
return false;
for (size_t i = 0; i < size; i++) /**
* @brief Write sequential numbers to a portion of EEPROM.
*
* This function writes sequential numbers starting from 0 to a specified portion of EEPROM.
* If the number reaches 255, it wraps around and starts again from 1.
*
* @param memoryAddress Starting address in EEPROM.
* @param length Number of bytes to write.
*/
void writeSequentialToEEPROM(uint16_t memoryAddress, uint16_t length)
{
if (!checkEEPROMavailable())
return;
uint8_t value = 0;
for (uint16_t i = 0; i < length; i++)
{ {
char c = string[i]; ee.writeByte(memoryAddress + i, value);
if (c == '\0') value = (value == 255) ? 1 : value + 1;
{
// Reached the end of the string, all characters were valid WiFi characters.
return true;
}
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '!' || c == '"' || c == '#' ||
c == '$' || c == '%' || c == '&' || c == '\'' || c == '(' ||
c == ')' || c == '*' || c == '+' || c == ',' || c == '-' ||
c == '.' || c == '/' || c == ':' || c == ';' || c == '<' ||
c == '=' || c == '>' || c == '?' || c == '@' || c == '[' ||
c == '\\' || c == ']' || c == '^' || c == '_' || c == '`' ||
c == '{' || c == '|' || c == '}' || c == '~'))
{
// Found a character that is not a valid WiFi character.
return false;
}
} }
// If the loop completes without finding a null terminator, the string is invalid.
return false;
} }
/**
* @brief Write 0 to a portion of EEPROM.
*
* This function writes 0 to a specified portion of EEPROM.
*
* @param memoryAddress Starting address in EEPROM.
* @param length Number of bytes to write.
*/
void writeZeroToEEPROM(uint16_t memoryAddress, uint16_t length)
{
if (!checkEEPROMavailable())
return;
for (uint16_t i = 0; i < length; i++)
{
ee.writeByte(memoryAddress + i, 0);
}
}

View File

@@ -6,4 +6,5 @@ void initGlobals()
{ {
globals.systemStatus = sysStat_Startup; globals.systemStatus = sysStat_Startup;
globals.requestEEAction = EE_IDLE; globals.requestEEAction = EE_IDLE;
globals.systemStatustxt[0] = 0;
} }

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
@@ -238,7 +249,7 @@ void printParameters(struct Configuration configuration)
void Parse_LoRa_UartCommand(char input[], int size) void Parse_LoRa_UartCommand(char input[], int size)
{ {
Debug_pushMessage("Start parsing, size: %d", size); Debug_pushMessage("Start parsing, size: %d\n", size);
char delimiter[] = ";"; char delimiter[] = ";";
char *ptr; char *ptr;
char command[8]; char command[8];
@@ -251,14 +262,14 @@ void Parse_LoRa_UartCommand(char input[], int size)
while (ptr != NULL) while (ptr != NULL)
{ {
strncpy(command, ptr, sizeof(command) - 1); // Platz für Nullterminator lassen strncpy(command, ptr, sizeof(command) - 1); // Platz für Nullterminator lassen
command[sizeof(command) - 1] = '\0'; // Nullterminator setzen command[sizeof(command) - 1] = '\0'; // Nullterminator setzen
ptr = strtok(NULL, delimiter); ptr = strtok(NULL, delimiter);
if (ptr != NULL) if (ptr != NULL)
{ {
strncpy(value, ptr, sizeof(value) - 1); // Platz für Nullterminator lassen strncpy(value, ptr, sizeof(value) - 1); // Platz für Nullterminator lassen
value[sizeof(value) - 1] = '\0'; // Nullterminator setzen value[sizeof(value) - 1] = '\0'; // Nullterminator setzen
} }
else else
{ {
@@ -267,7 +278,7 @@ void Parse_LoRa_UartCommand(char input[], int size)
} }
// Hier kannst du den Wert und das Kommando verarbeiten // Hier kannst du den Wert und das Kommando verarbeiten
Debug_pushMessage("Command: %s, Value: %s", command, value); Debug_pushMessage("Command: %s, Value: %s\n", command, value);
} }
Debug_pushMessage("Parsed LoRa UART Command: %s Value: %s\n", command, value); Debug_pushMessage("Parsed LoRa UART Command: %s Value: %s\n", command, value);
@@ -275,12 +286,12 @@ void Parse_LoRa_UartCommand(char input[], int size)
if (!strcmp(command, "ENABLE")) if (!strcmp(command, "ENABLE"))
{ {
globals.timer_disabled = false; globals.timer_disabled = false;
Debug_pushMessage("Enabled by LoRa"); Debug_pushMessage("Enabled by LoRa\n");
} }
else if (!strcmp(command, "DISABLE")) else if (!strcmp(command, "DISABLE"))
{ {
globals.timer_disabled = true; globals.timer_disabled = true;
Debug_pushMessage("Disabled by LoRa"); Debug_pushMessage("Disabled by LoRa\n");
} }
else if (!strcmp(command, "RESET")) else if (!strcmp(command, "RESET"))
{ {
@@ -288,7 +299,7 @@ void Parse_LoRa_UartCommand(char input[], int size)
PersistenceData.faction_1_timer = 0; PersistenceData.faction_1_timer = 0;
PersistenceData.faction_2_timer = 0; PersistenceData.faction_2_timer = 0;
PersistenceData.faction_3_timer = 0; PersistenceData.faction_3_timer = 0;
Debug_pushMessage("Reset by LoRa"); Debug_pushMessage("Reset by LoRa\n");
} }
else if (!strcmp(command, "TMRSTP")) else if (!strcmp(command, "TMRSTP"))
{ {

View File

@@ -19,6 +19,7 @@
#include "globals.h" #include "globals.h"
#include "dtc.h" #include "dtc.h"
#include "debugger.h" #include "debugger.h"
#include "utilities.h"
#if defined(FEATURE_ENABLE_LORA) || defined(FEATURE_ENABLE_UARTLORA) #if defined(FEATURE_ENABLE_LORA) || defined(FEATURE_ENABLE_UARTLORA)
#include "lora_net.h" #include "lora_net.h"
#endif #endif
@@ -84,7 +85,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 +97,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 +110,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(); {
GetPersistence_EEPROM(); GetConfig_EEPROM();
Serial.print("\nEE-Init done"); GetPersistence_EEPROM();
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 +158,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 +216,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 +224,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();
@@ -251,7 +257,7 @@ void loop()
tmrWiFiMaintainConnection.update(); tmrWiFiMaintainConnection.update();
#endif #endif
static tSystem_Status lastStatus = sysStat_Error; static tSystem_Status lastStatus = sysStat_Init;
// Handle different system statuses // Handle different system statuses
switch (globals.systemStatus) switch (globals.systemStatus)
@@ -293,7 +299,12 @@ void loop()
} }
SystemShutdown(false); SystemShutdown(false);
break; break;
case sysStat_Error:
if (lastStatus != globals.systemStatus)
{
strcpy_P(globals.systemStatustxt, PSTR("Error"));
lastStatus = globals.systemStatus;
}
default: default:
break; break;
} }
@@ -410,7 +421,7 @@ void tmrCallback_InputGetter()
if (keysPressed > 1) if (keysPressed > 1)
{ {
Debug_pushMessage("ERROR: More than one Flag active - setting no Faction active"); Debug_pushMessage("ERROR: More than one Flag active - setting no Faction active\n");
PersistenceData.activeFaction = NONE; PersistenceData.activeFaction = NONE;
return; return;
} }
@@ -419,7 +430,7 @@ void tmrCallback_InputGetter()
{ {
if (PersistenceData.activeFaction != FACTION_1) if (PersistenceData.activeFaction != FACTION_1)
{ {
Debug_pushMessage("Faction 1 captured !"); Debug_pushMessage("Faction 1 captured !\n");
globals.requestEEAction = EE_PDS_SAVE; globals.requestEEAction = EE_PDS_SAVE;
} }
PersistenceData.activeFaction = FACTION_1; PersistenceData.activeFaction = FACTION_1;
@@ -429,7 +440,7 @@ void tmrCallback_InputGetter()
{ {
if (PersistenceData.activeFaction != FACTION_2) if (PersistenceData.activeFaction != FACTION_2)
{ {
Debug_pushMessage("Faction 2 captured !"); Debug_pushMessage("Faction 2 captured !\n");
globals.requestEEAction = EE_PDS_SAVE; globals.requestEEAction = EE_PDS_SAVE;
} }
PersistenceData.activeFaction = FACTION_2; PersistenceData.activeFaction = FACTION_2;
@@ -439,7 +450,7 @@ void tmrCallback_InputGetter()
{ {
if (PersistenceData.activeFaction != FACTION_3) if (PersistenceData.activeFaction != FACTION_3)
{ {
Debug_pushMessage("Faction 3 captured !"); Debug_pushMessage("Faction 3 captured !\n");
globals.requestEEAction = EE_PDS_SAVE; globals.requestEEAction = EE_PDS_SAVE;
} }
PersistenceData.activeFaction = FACTION_3; PersistenceData.activeFaction = FACTION_3;
@@ -561,6 +572,7 @@ void wifiMaintainConnectionTicker_callback()
*/ */
void toggleWiFiAP(bool shutdown) void toggleWiFiAP(bool shutdown)
{ {
char buffer[33];
// Check if WiFi is currently active // Check if WiFi is currently active
if (WiFi.getMode() != WIFI_OFF) if (WiFi.getMode() != WIFI_OFF)
{ {
@@ -578,7 +590,8 @@ 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)); sanitizeWiFiString(globals.DeviceNameId, buffer, sizeof(buffer));
WiFi.softAP(buffer, 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
@@ -691,7 +704,7 @@ void ProcessKeyCombos(bool *btnState)
if (keyCount_Fac2 == 2 && keyCount_Fac3 == 0) if (keyCount_Fac2 == 2 && keyCount_Fac3 == 0)
{ {
Debug_pushMessage("KeyCombo: WiFi AP ON"); Debug_pushMessage("KeyCombo: WiFi AP ON\n");
OverrideDisplay(5000, "NET ", " ", " "); OverrideDisplay(5000, "NET ", " ", " ");
toggleWiFiAP(false); toggleWiFiAP(false);
} }

View File

@@ -0,0 +1,94 @@
#include "utilities.h"
/**
* @brief Validates whether a given string contains only characters allowed in WiFi SSIDs and passwords.
*
* This function checks each character in the provided string to ensure
* that it contains only characters allowed in WiFi SSIDs and passwords.
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
*
* @param string Pointer to the string to be validated.
* @param size Size of the string including the null-terminator.
* @return true if the string contains only allowed characters or is NULL,
* false otherwise.
*/
bool validateWiFiString(char *string, size_t size)
{
if (string == NULL)
return false;
for (size_t i = 0; i < size; i++)
{
char c = string[i];
if (c == '\0')
{
// Reached the end of the string, all characters were valid WiFi characters.
return true;
}
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '!' || c == '"' || c == '#' ||
c == '$' || c == '%' || c == '&' || c == '\'' || c == '(' ||
c == ')' || c == '*' || c == '+' || c == ',' || c == '-' ||
c == '.' || c == '/' || c == ':' || c == ';' || c == '<' ||
c == '=' || c == '>' || c == '?' || c == '@' || c == '[' ||
c == '\\' || c == ']' || c == '^' || c == '_' || c == '`' ||
c == '{' || c == '|' || c == '}' || c == '~'))
{
// Found a character that is not a valid WiFi character.
return false;
}
}
// If the loop completes without finding a null terminator, the string is invalid.
return false;
}
/**
* @brief Copies a string to a buffer, replacing invalid WiFi SSID characters with a placeholder.
*
* This function checks each character in the provided input string to ensure
* that it contains only characters allowed in WiFi SSIDs and passwords. If a character
* is invalid, it replaces it with a placeholder character (e.g., '_').
* It considers characters from 'A' to 'Z', 'a' to 'z', '0' to '9', as well as
* the following special characters: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
*
* @param input Pointer to the input string to be validated and copied.
* @param buffer Pointer to the buffer where the output string will be copied.
* @param bufferSize Size of the buffer including the null-terminator.
*/
void sanitizeWiFiString(const char *input, char *buffer, size_t bufferSize)
{
if (input == NULL || buffer == NULL || bufferSize == 0)
return;
size_t i;
for (i = 0; i < bufferSize - 1; i++) // Leave space for null-terminator
{
char c = input[i];
if (c == '\0')
{
// Reached the end of the input string, terminate the buffer
buffer[i] = '\0';
return;
}
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '!' || c == '"' || c == '#' ||
c == '$' || c == '%' || c == '&' || c == '\'' || c == '(' ||
c == ')' || c == '*' || c == '+' || c == ',' || c == '-' ||
c == '.' || c == '/' || c == ':' || c == ';' || c == '<' ||
c == '=' || c == '>' || c == '?' || c == '@' || c == '[' ||
c == '\\' || c == ']' || c == '^' || c == '_' || c == '`' ||
c == '{' || c == '|' || c == '}' || c == '~'))
{
// Replace invalid character with placeholder
buffer[i] = '_';
}
else
{
// Copy valid character to buffer
buffer[i] = c;
}
}
// Null-terminate the buffer
buffer[i] = '\0';
}

View File

@@ -17,6 +17,8 @@ AsyncWebServer webServer(80);
const char *PARAM_MESSAGE = "message"; const char *PARAM_MESSAGE = "message";
batteryType_t batterytypePreselect; /**< Preselect Memory for change Batterytype */
String processor(const String &var); String processor(const String &var);
void WebserverNotFound_Callback(AsyncWebServerRequest *request); void WebserverNotFound_Callback(AsyncWebServerRequest *request);
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final); void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
@@ -69,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
@@ -340,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;
@@ -446,8 +448,14 @@ void Websocket_HandleButtons(uint8_t *data)
char value[32]; char value[32];
parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value)); parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value));
if (strcmp(identifier, "reset-timer") == 0)
if (strcmp(identifier, "debugstart") == 0) {
PersistenceData.activeFaction = NONE;
PersistenceData.faction_1_timer = 0;
PersistenceData.faction_2_timer = 0;
PersistenceData.faction_3_timer = 0;
}
else if (strcmp(identifier, "debugstart") == 0)
{ {
SetDebugportStatus(dbg_Webui, enabled); SetDebugportStatus(dbg_Webui, enabled);
} }
@@ -457,6 +465,7 @@ void Websocket_HandleButtons(uint8_t *data)
} }
else if (strcmp(identifier, "settingssave") == 0) else if (strcmp(identifier, "settingssave") == 0)
{ {
ConfigData.batteryType = batterytypePreselect;
globals.requestEEAction = EE_CFG_SAVE; globals.requestEEAction = EE_CFG_SAVE;
} }
else if (strcmp(identifier, "reboot") == 0) else if (strcmp(identifier, "reboot") == 0)
@@ -484,7 +493,28 @@ void Websocket_HandleSettings(uint8_t *data)
parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value)); parseWebsocketString((char *)data, identifier, sizeof(identifier), value, sizeof(value));
if (strcmp(identifier, "wifi-ssid") == 0) if (strcmp(identifier, "active_faction_on_reboot") == 0)
{
ConfigData.active_faction_on_reboot = value[0] == '1' ? true : false;
}
else if (strcmp(identifier, "name_faction1") == 0)
{
strncpy(ConfigData.Faction_1_Name, value, sizeof(ConfigData.Faction_1_Name));
}
else if (strcmp(identifier, "name_faction2") == 0)
{
strncpy(ConfigData.Faction_2_Name, value, sizeof(ConfigData.Faction_2_Name));
}
else if (strcmp(identifier, "name_faction3") == 0)
{
strncpy(ConfigData.Faction_3_Name, value, sizeof(ConfigData.Faction_3_Name));
}
else if (strcmp(identifier, "batterytype") == 0)
{
int index = findIndexByString(value, BatteryString, BatteryString_Elements);
batterytypePreselect = (batteryType_t)index;
}
else if (strcmp(identifier, "wifi-ssid") == 0)
{ {
strncpy(ConfigData.wifi_client_ssid, value, sizeof(ConfigData.wifi_client_ssid)); strncpy(ConfigData.wifi_client_ssid, value, sizeof(ConfigData.wifi_client_ssid));
} }
@@ -564,40 +594,45 @@ void Websocket_RefreshClientData_DTCs(uint32_t client_id)
*/ */
void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping) void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
{ {
if (send_mapping)
{
const char mapping[] = "MAPPING_STATUS:"
"batterylevel;"
"systemstatus;"
"activefaction;"
"time_faction1;"
"time_faction2;"
"time_faction3;";
if (send_mapping) if (client_id > 0)
{ webSocket.text(client_id, mapping);
const char mapping[] = "MAPPING_STATUS:" else
"systemstatus;" webSocket.textAll(mapping);
"activefaction;"
"time_faction1;" Debug_pushMessage("send MAPPING_STATUS WS-Client Data\n");
"time_faction2;" }
"time_faction3;";
char dataString[200] = {0}; // Maximal 200 Zeichen für den Data-String
sprintf(dataString, "STATUS:%d;%s;%d;%ld;%ld;%ld;",
globals.battery_level,
globals.systemStatustxt,
PersistenceData.activeFaction,
PersistenceData.faction_1_timer,
PersistenceData.faction_2_timer,
PersistenceData.faction_3_timer);
if (client_id > 0) if (client_id > 0)
webSocket.text(client_id, mapping); {
webSocket.text(client_id, dataString);
}
else else
webSocket.textAll(mapping); {
} webSocket.textAll(dataString);
}
String temp = "STATUS:";
temp.concat(String(globals.systemStatustxt) + ";");
temp.concat(String(PersistenceData.activeFaction) + ";");
temp.concat(String(PersistenceData.faction_1_timer) + ";");
temp.concat(String(PersistenceData.faction_2_timer) + ";");
temp.concat(String(PersistenceData.faction_3_timer) + ";");
if (client_id > 0)
{
webSocket.text(client_id, temp);
}
else
{
webSocket.textAll(temp);
}
} }
/** /**
* @brief Refreshes client data related to static configuration parameters on WebSocket clients. * @brief Refreshes client data related to static configuration parameters on WebSocket clients.
* *
@@ -611,40 +646,53 @@ void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping) void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
{ {
Debug_pushMessage("send STATIC WS-Client Data\n");
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;"
"name_faction2;" "name_faction2;"
"name_faction3;" "name_faction3;"
"wifi-ssid;" "wifi-ssid;"
"wifi-pass;"; "wifi-pass;"
"fw-version;"
"flash-version;"
"git-revison;";
if (client_id > 0) if (client_id > 0)
webSocket.text(client_id, mapping); webSocket.text(client_id, mapping);
else else
webSocket.textAll(mapping); webSocket.textAll(mapping);
Debug_pushMessage("send MAPPING_STATIC WS-Client Data\n");
} }
String temp = "STATIC:"; char dataString[200] = {0}; // Maximal 200 Zeichen für den Data-String
temp.concat(String(ConfigData.active_faction_on_reboot) + ";"); sprintf(dataString, "STATIC:%s;%d;%d;%s;%s;%s;%s;%s;%d.%02d;%s;%s;",
temp.concat(String(ConfigData.batteryType) + ";"); globals.DeviceName,
temp.concat(String(ConfigData.Faction_1_Name) + ";"); ConfigData.active_faction_on_reboot,
temp.concat(String(ConfigData.Faction_2_Name) + ";"); ConfigData.batteryType,
temp.concat(String(ConfigData.Faction_3_Name) + ";"); ConfigData.Faction_1_Name,
temp.concat(String(ConfigData.wifi_client_ssid) + ";"); ConfigData.Faction_2_Name,
temp.concat(String(ConfigData.wifi_client_password) + ";"); ConfigData.Faction_3_Name,
ConfigData.wifi_client_ssid,
ConfigData.wifi_client_password,
constants.FW_Version_major,
constants.FW_Version_minor,
globals.FlashVersion,
constants.GitHash);
if (client_id > 0) if (client_id > 0)
{ {
webSocket.text(client_id, temp); webSocket.text(client_id, dataString);
} }
else else
{ {
webSocket.textAll(temp); webSocket.textAll(dataString);
} }
} }

View File

@@ -1,6 +1,5 @@
[wifi_cred] [wifi_cred]
wifi_ap_ssid = wifi-ap-ssid admin_password = chainlube
wifi_ap_password = wifiappass wifi_ap_password = wifiappass
wifi_client_ssid = wifi-ssid wifi_ssid_client = wifi-ssid
wifi_client_password = wifi-pass wifi_password_client = ota-password
ota_password = ota-password