27 Commits

Author SHA1 Message Date
b4177c9b0f design fix in WebUI 2024-06-06 22:38:27 +02:00
0763fe2181 added Debug-CLI on WebUI 2024-06-06 20:33:29 +02:00
024a00e1bf switched to device-specific WiFI-AP-password 2024-06-06 20:32:13 +02:00
2fea8cfdd6 some Bugfixes in WebUI and debugger 2024-06-06 20:28:04 +02:00
25f05ed832 WiFi-Passwort now generated internally and device-specific 2024-06-04 22:10:22 +02:00
3d090dceb1 fixed Bug in version-define 2024-06-03 16:36:48 +02:00
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
29 changed files with 3196 additions and 2405 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,13 +1,10 @@
# SCRIPT TO GZIP CRITICAL FILES FOR ACCELERATED WEBSERVING
# see also https://community.platformio.org/t/question-esp32-compress-files-in-data-to-gzip-before-upload-possible-to-spiffs/6274/10
import glob import glob
import shutil import shutil
import gzip import gzip
import os import os
import subprocess import subprocess
import platform import platform
from os import popen
Import("env") Import("env")
Import("projenv") Import("projenv")
@@ -169,8 +166,13 @@ def gzip_webfiles(source, target, env):
return return
def gzip_binffiles(source, target, env): def gzip_binffiles(source, target, env):
git_revision = popen('git rev-parse --short HEAD').read().strip()
custom_flash_version = env.GetProjectOption("custom_flash_version", "0.99")
# Format the target file name
target_filename = f"filesystem_{custom_flash_version}_{git_revision}.fs"
littlefsbin = target[0].get_abspath() littlefsbin = target[0].get_abspath()
targetbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs') targetbin = os.path.join(os.path.dirname(littlefsbin), target_filename)
shutil.copyfile(littlefsbin, targetbin) shutil.copyfile(littlefsbin, targetbin)
gzip_file(targetbin, os.path.join(str(targetbin) + '.gz')) gzip_file(targetbin, os.path.join(str(targetbin) + '.gz'))
os.remove(targetbin) os.remove(targetbin)

View File

@@ -7,8 +7,32 @@ from os import popen
git_revision = popen('git rev-parse --short HEAD').read().strip() git_revision = popen('git rev-parse --short HEAD').read().strip()
env.Replace(PROGNAME="firmware_%s.fw" % git_revision) # Versionsnummern aus platformio.ini holen
env.Append(CPPDEFINES=[('GIT_REV', '\\"{}\\"'.format(git_revision))]) custom_firmware_version = env.GetProjectOption("custom_firmware_version", "0.99")
custom_flash_version = env.GetProjectOption("custom_flash_version", "0.99")
# Versionsnummern aufteilen in Major und Minor
fw_major, fw_minor = custom_firmware_version.split('.')
fl_major, fl_minor = custom_flash_version.split('.')
fw_major_int = int(fw_major)
fw_minor_int = int(fw_minor)
fl_major_int = int(fl_major)
fl_minor_int = int(fl_minor)
# 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_int),
('FW_MINOR', fw_minor_int),
('FL_MAJOR', fl_major_int),
('FL_MINOR', fl_minor_int)
])
struct2json.struct2json() struct2json.struct2json()
dtcs.build_dtcs() dtcs.build_dtcs()

View File

@@ -0,0 +1,19 @@
def djb2_hash(s):
hash = 5381
for c in s:
hash = ((hash << 5) + hash) + ord(c) # hash * 33 + c
return hash & 0xFFFFFFFF # Ensure it's a 32-bit unsigned integer
def generate_password(seed, chip_id):
combined = (seed + chip_id)[:24] # Ensure the combined string is up to 24 characters
hash_value = djb2_hash(combined)
hash_str = f"{hash_value:08X}{hash_value:08X}" # Repeat the hash to ensure at least 16 characters
return hash_str[:32] # Ensure the password is at most 32 characters
if __name__ == "__main__":
SEED = "letmelubeyou"
chip_id = input("Enter the Chip ID in hex (e.g., 1A2B3C4D): ").strip()
chip_id = chip_id.zfill(8).upper() # Ensure it's 8 characters, upper case
password = generate_password(SEED, chip_id)
print(f"Generated Password: {password}")

View File

@@ -3,20 +3,20 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>KTM CAN Chain Oiler</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" />
<link rel="stylesheet" href="static/css/tweaks.css"> <link rel="stylesheet" href="static/css/tweaks.css" />
<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/dtc_table.js"></script>
<script src="static/js/script.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" />
<link rel="manifest" href="static/img/site.webmanifest"> <link rel="manifest" href="static/img/site.webmanifest" />
</head> </head>
<body> <body>
@@ -31,93 +31,125 @@
<!-- Notification-Container --> <!-- Notification-Container -->
<div id="notification-container" class="notification-container"></div> <div id="notification-container" class="notification-container"></div>
<!-- Notification-Container --> <!-- 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="" />
DE Airsoft Timer <span class="data-devicename">DE Airsoft Timer</span>
</a> </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar" <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar"
aria-controls="collapsingNavbar" aria-expanded="false" aria-label="Toggle navigation"> aria-controls="collapsingNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="collapsingNavbar"> <div class="collapse navbar-collapse" id="collapsingNavbar">
<ul class="navbar-nav nav mr-auto mt-2 mt-lg-0"> <ul class="navbar-nav nav mr-auto mt-2 mt-lg-0">
<li class="nav-item">
<li class="nav-item"><a class="nav-link active" role="tab" data-toggle="tab" href="#tab_home">Home</a></li> <a class="nav-link active" role="tab" data-toggle="tab" href="#tab_home">Home</a>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_maintenance">Wartung</a></li> </li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_source">Einstellungen</a></li> <li class="nav-item">
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_fwupdate">Update</a></li> <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_fwupdate">Update</a>
</li>
</ul> </ul>
</div> </div>
</nav> </nav>
<main class="container"> <main class="container">
<!-- Tabs Content --> <!-- Tabs Content -->
<div class="tab-content"> <div class="tab-content">
<!-- Div Tab Home--> <!-- Div Tab Home-->
<div id="tab_home" class="tab-pane fade show active" role="tabpanel"> <div id="tab_home" class="tab-pane fade show active" role="tabpanel">
<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">KTM CAN Chain Lube</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>
<h4>Akkuladestand</h4> <h4>Akkuladestand</h4>
<div class="progress"> <div class="progress">
<div id="battery_level" class="data-battery_level progress-bar text-light" role="progressbar" aria-valuenow="0" <div id="batterylevel" class="data-batterylevel progress-bar text-light" role="progressbar" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100" style="width: 0%"> aria-valuemin="0" aria-valuemax="100" style="width: 0%">
0 0
</div> </div>
</div> </div>
</p>
<!-- Div Group Battery remain --> <!-- Div Group Battery remain -->
<!-- Div Group current Mode --> <!-- Div Group current Mode -->
<hr /> <hr />
<p>
<h4>aktueller Modus</h4> <h4>aktueller Modus</h4>
<input class="data-systemstatus form-control" type="text" id="sysstatus" readonly> <input class="data-systemstatus form-control" type="text" id="sysstatus" readonly />
</p>
<!-- Div Group current Mode --> <!-- Div Group current Mode -->
<!-- Div Group Faction Points --> <!-- Div Group Faction Points -->
<hr /> <hr />
<p>
<h4>aktueller Punktestand</h4> <h4>aktueller Punktestand</h4>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div id="header_faction1" class="col text-center data-name_faction1 text-white p-3">%NAME_FAC_1%</div> <div id="header_faction1" class="col text-center data-name_faction1 text-white p-3">
<div id="header_faction2" class="col text-center data-name_faction2 text-white p-3">%NAME_FAC_2%</div> %NAME_FAC_1%
<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 id="header_faction2" class="col text-center data-name_faction2 text-white p-3">
<div class="col bg-dark text-white p-3"><img src="static/img/logo_fac1.png" %NAME_FAC_2%
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 id="header_faction3" class="col text-center data-name_faction3 text-white p-3">
class="rounded mx-auto img-fluid d-block" alt="..."> %NAME_FAC_3%
</div>
<div class="col bg-dark text-white p-3"><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 id="time_faction1" class="data-time_faction1 col text-center bg-secondary text-white p-3 format-time">0</div> <div class="col bg-dark text-white p-3 data-activefaction faction-logo faction1">
<div id="time_faction2" class="data-time_faction2 col text-center bg-secondary text-white p-3 format-time">0</div> <div class="glow-container">
<div id="time_faction3" class="data-time_faction3 col text-center bg-secondary text-white p-3 format-time">0</div> <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 data-activefaction faction-logo faction2 glow-active-faction">
<div class="glow-container">
<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 data-activefaction faction-logo faction3">
<div class="glow-container">
<img src="static/img/logo_fac3.png" class="rounded mx-auto img-fluid d-block" alt="..." />
</div>
</div>
</div>
<div class="row">
<div id="time_faction1" class="data-time_faction1 col text-center bg-secondary text-white p-3 format-time">
0
</div>
<div id="time_faction2" class="data-time_faction2 col text-center bg-secondary text-white p-3 format-time">
0
</div>
<div id="time_faction3" class="data-time_faction3 col text-center bg-secondary text-white p-3 format-time">
0
</div>
</div>
<div class="row mt-3">
<div class="col text-center">
<button id="faction1" class="btn-wsevent btn btn-outline-primary">
Aktivieren
</button>
</div>
<div class="col text-center">
<button id="faction2" class="btn-wsevent btn btn-outline-primary">
Aktivieren
</button>
</div>
<div class="col text-center">
<button id="faction3" class="btn-wsevent btn btn-outline-primary">
Aktivieren
</button>
</div>
</div> </div>
</div> </div>
</p>
<!-- Div GroupFaction Points --> <!-- Div GroupFaction Points -->
<!-- Div Group DTC Table --> <!-- Div Group DTC Table -->
<div id="dtc_container" hidden> <div id="dtc_container" hidden>
<hr /> <hr />
<p>
<h4>Fehlercodes</h4> <h4>Fehlercodes</h4>
<table class="table" id="dtc_table"> <table class="table" id="dtc_table">
<tbody> <tbody>
@@ -129,83 +161,83 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
</p>
</div> </div>
<!-- Div Group DTC Table --> <!-- Div Group DTC Table -->
</div> </div>
<!-- Div Tab Home--> <!-- Div Tab Home-->
<!-- Div Tab Maintenance --> <!-- Div Tab Maintenance -->
<div id="tab_maintenance" class="tab-pane fade" role="tabpanel"> <div id="tab_maintenance" class="tab-pane fade" role="tabpanel">
<h3>Wartung</h3> <h3>Wartung</h3>
<!-- Div Group Reset Timers --> <!-- Div Group Reset Timers -->
<hr /> <hr />
<p>
<h4>Punkte zur&uuml;cksetzen</h4> <h4>Punkte zur&uuml;cksetzen</h4>
<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 id="reset-timer" class="btn-wsevent btn btn-outline-primary ml-2">
Timer zurücksetzen
</button>
</div> </div>
</div> </div>
</p>
<!-- Div Group Reset Timers --> <!-- Div Group Reset Timers -->
<!-- Div Group LiveDebug --> <!-- Div Group LiveDebug -->
<hr /> <hr />
<p>
<h4>Live Debug</h4> <h4>Live Debug</h4>
<div class="form-group row">
<input id="livedebug-in" type="text" class="set-wsevent data-livedebug-in form-control" />
</div>
<div class="form-group row"> <div class="form-group row">
<textarea class="form-control" spellcheck="false" id="livedebug-out" rows="3" readonly></textarea> <textarea class="form-control" spellcheck="false" id="livedebug-out" rows="3" readonly></textarea>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button id="debugstart" class="btn-wsevent btn btn-outline-primary ml-2">Start</button> <button id="debugstart" class="btn-wsevent btn btn-outline-primary ml-2">
<button id="debugstop" class="btn-wsevent btn btn-outline-primary ml-2">Stop</button> Start
</button>
<button id="debugstop" class="btn-wsevent btn btn-outline-primary ml-2">
Stop
</button>
</div> </div>
</div> </div>
</p>
<!-- Div Group LiveDebug --> <!-- Div Group LiveDebug -->
<!-- Div Group Device Reboot --> <!-- Div Group Device Reboot -->
<hr /> <hr />
<p>
<h4>Ger&auml;t neustarten</h4> <h4>Ger&auml;t neustarten</h4>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button id="reboot" class="btn-wsevent confirm btn btn-outline-primary">Reboot</button> <button id="reboot" class="btn-wsevent confirm btn btn-outline-primary">
Reboot
</button>
</div> </div>
</div> </div>
</p>
<!-- Div Group Device Reboot --> <!-- Div Group Device Reboot -->
</div> </div>
<!-- Div Tab Maintenance --> <!-- Div Tab Maintenance -->
<!-- Div Tab Settings--> <!-- Div Tab Settings-->
<div id="tab_source" class="tab-pane fade" role="tabpanel"> <div id="tab_source" class="tab-pane fade" role="tabpanel">
<h3>Einstellungen</h3> <h3>Einstellungen</h3>
<!-- Div Group Battery Type --> <!-- Div Group Battery Type -->
<hr /> <hr />
<p>
<h4>Akku</h4> <h4>Akku</h4>
<div class="form-group row"> <div class="form-group row">
<label for="batterytype" class="control-label col-4">Akku-Variante</label> <label for="batterytype" class="control-label col-4">Akku-Variante</label>
<div class="col-8"> <div class="col-8">
<select id="batterytype" class="set-wsevent data-batterytype select form-control"> <select id="batterytype" class="set-wsevent data-batterytype select form-control">
<option value="Undefined">Undefined</option> <option value="Undefined">Undefined</option>
<option value="LiPo3S">LiPo 3S</option> <option value="LiPo 3S">LiPo 3S</option>
<option value="LiPo2S">LiPo 2S</option> <option value="LiPo 2S">LiPo 2S</option>
</select> </select>
</div> </div>
</div> </div>
</p>
<!-- Div Group Battery Type --> <!-- Div Group Battery Type -->
<!-- Div Group Timer Settings --> <!-- Div Group Timer Settings -->
<hr /> <hr />
<p>
<h4>Timer Einstellungen</h4> <h4>Timer Einstellungen</h4>
<div class="form-group row"> <div class="form-group row">
<label for="active_faction_on_reboot" class="control-label col-4">Aktive Fraktion wiederherstellen</label> <label for="active_faction_on_reboot" class="control-label col-4">Aktive Fraktion wiederherstellen</label>
<div class="col-8"> <div class="col-8">
<div class="form-check"> <div class="form-check">
<input class="set-wsevent data-active_faction_on_reboot form-check-input" type="checkbox" id="active_faction_on_reboot"> <input class="set-wsevent data-active_faction_on_reboot form-check-input" type="checkbox"
id="active_faction_on_reboot" />
<label class="form-check-label" for="active_faction_on_reboot"> <label class="form-check-label" for="active_faction_on_reboot">
aktivieren aktivieren
</label> </label>
@@ -215,87 +247,91 @@
<div class="form-group row"> <div class="form-group row">
<label for="name_faction1" class="control-label col-4">Faktion 1</label> <label for="name_faction1" class="control-label col-4">Faktion 1</label>
<div class="col-8"> <div class="col-8">
<input id="name_faction1" type="text" class="set-wsevent data-name_faction1 form-control" required="required"> <div class="input-group">
<input id="name_faction1" type="text" class="set-wsevent data-name_faction1 form-control"
required="required" />
<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 class="form-group row"> <div class="form-group row">
<label for="name_faction2" class="control-label col-4">Faktion 2</label> <label for="name_faction2" class="control-label col-4">Faktion 2</label>
<div class="col-8"> <div class="col-8">
<input id="name_faction2" type="text" class="set-wsevent data-name_faction2 form-control" required="required"> <div class="input-group">
<input id="name_faction2" type="text" class="set-wsevent data-name_faction2 form-control"
required="required" />
<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 class="form-group row"> <div class="form-group row">
<label for="name_faction3" class="control-label col-4">Faktion 3</label> <label for="name_faction3" class="control-label col-4">Faktion 3</label>
<div class="col-8"> <div class="col-8">
<input id="name_faction3" type="text" class="set-wsevent data-name_faction3 form-control" required="required"> <div class="input-group">
<input id="name_faction3" type="text" class="set-wsevent data-name_faction3 form-control"
required="required" />
<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 Group Timer Settings --> <!-- Div Group Timer Settings -->
<!-- Div Group Save Button--> <!-- Div Group Save Button-->
<hr /> <hr />
<p>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button id="settingssave" class="btn-wsevent btn btn-outline-primary">Speichern</button> <button id="settingssave" class="btn-wsevent btn btn-outline-primary">
Speichern
</button>
</div> </div>
</div> </div>
</p>
</div> </div>
<!-- Div Tab Settings --> <!-- Div Tab Settings -->
<!-- Div Tab Firmware Update--> <!-- Div Tab Firmware Update-->
<div id="tab_fwupdate" class="tab-pane fade" role="tabpanel"> <div id="tab_fwupdate" class="tab-pane fade" role="tabpanel">
<h3>Firmware</h3> <h3>Firmware</h3>
<!-- Div Group VersionInfo --> <!-- Div Group VersionInfo -->
<hr /> <hr />
<p>
<h4>Version-Info</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</th>
<th class="col-5" scope="col">Value</td> <th class="col-5" scope="col">Value</th>
</tr> </tr>
<tr> <tr>
<td>Firmware Version</td> <td>Firmware Version</td>
<td>%SW_VERSION%</td> <td><span class="data-fw-version"></span></td>
</tr> </tr>
<tr> <tr>
<td>Flash Version</td> <td>Flash Version</td>
<td>%FS_VERSION%</td> <td><span class="data-flash-version"></span></td>
</tr> </tr>
<tr> <tr>
<td>Git Revision</td> <td>Git Revision</td>
<td>%GIT_REV%</td> <td><span class="data-git-revision"></span></td>
</tr> </tr>
</tbody>
</table> </table>
</p>
<!-- Div Group VersionInfo --> <!-- Div Group VersionInfo -->
<!-- Div Group EEPROM Backup --> <!-- Div Group EEPROM Backup -->
<hr /> <hr />
<p>
<h4>EEPROM-Backup</h4> <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> <a class="btn btn-outline-primary" href="eejson" role="button" id="ee-backup-download">Download</a>
</div> </div>
</div> </div>
</p>
<!-- Div Group EEPROM Backup --> <!-- Div Group EEPROM Backup -->
<!-- Div Group EEPROM Restore --> <!-- Div Group EEPROM Restore -->
<hr /> <hr />
<p>
<h4>EEPROM-Restore</h4> <h4>EEPROM-Restore</h4>
<form method='POST' action='eeRestore' enctype='multipart/form-data'> <form method="POST" action="eeRestore" enctype="multipart/form-data">
<div class="form-group row"> <div class="form-group row">
<div class="custom-file"> <div class="custom-file">
<input type="file" name="ee-restore-file" class="custom-file-input" id="ee-restore-file" accept=".ee.json" <input type="file" name="ee-restore-file" class="custom-file-input" id="ee-restore-file" accept=".ee.json"
@@ -305,17 +341,17 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Restore starten</button> <button name="submit" type="submit" class="btn btn-outline-primary">
Restore starten
</button>
</div> </div>
</div> </div>
</form> </form>
</p>
<!-- Div Group EEPROM Restore --> <!-- Div Group EEPROM Restore -->
<!-- Div Group Firmware Update --> <!-- Div Group Firmware Update -->
<hr /> <hr />
<p>
<h4>Firmware-Update</h4> <h4>Firmware-Update</h4>
<form method='POST' action='doUpdate' enctype='multipart/form-data'> <form method="POST" action="doUpdate" enctype="multipart/form-data">
<div class="form-group row"> <div class="form-group row">
<div class="custom-file"> <div class="custom-file">
<input type="file" name="fw-update-file" class="custom-file-input" id="fw-update-file" <input type="file" name="fw-update-file" class="custom-file-input" id="fw-update-file"
@@ -325,34 +361,30 @@
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col text-center"> <div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Update starten</button> <button name="submit" type="submit" class="btn btn-outline-primary">
Update starten
</button>
</div> </div>
</div> </div>
</form> </form>
</p>
<!-- Div Group Firmware Update --> <!-- Div Group Firmware Update -->
</div> </div>
<!-- Div Tab Firmware Update--> <!-- Div Tab Firmware Update-->
</div> </div>
<!-- Tabs Content --> <!-- Tabs Content -->
</main> </main>
<!-- Footer -->
<!-- Footer --> <footer class="page-footer navbar-dark bg-primary font-small fixed-bottom">
<footer class="page-footer navbar-dark bg-primary font-small fixed-bottom">
<div class="container-fluid text-center"> <div class="container-fluid text-center">
<div class="footer-copyright text-center py-3"> <div class="footer-copyright text-center py-3">
<span class="text-muted">© 2023 - <span class="text-muted">© 2023 -
<a class="text-reset fw-bold" href="https://eventronics.de/">Marcel Peterkau</a></span> <a class="text-reset fw-bold" href="https://eventronics.de/">Marcel Peterkau</a></span>
</div> </div>
</div> </div>
</footer> </footer>
<!-- Footer -->
<!-- Footer --> <!-- Modal Dialog -->
<div class="modal fade" id="dtcModal" tabindex="-1" role="dialog" aria-labelledby="dtcModalLabel" aria-hidden="true">
<!-- Modal Dialog -->
<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">
@@ -366,16 +398,14 @@
<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 -->
</body> </body>
</html> </html>

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,27 +1,42 @@
$(document).ready(function () { $(document).ready(function () {
// Event-Listener für Navbar-Links
$(".navbar-nav a").on("click", function () { $(".navbar-nav a").on("click", function () {
$(".navbar-collapse").collapse("hide"); $(".navbar-collapse").collapse("hide");
}); });
// Event-Listener für Passwort zeigen/verborgen
$("#show_hide_password a").on("click", function (event) { $("#show_hide_password a").on("click", function (event) {
event.preventDefault(); event.preventDefault();
if ($("#show_hide_password input").attr("type") == "text") { var inputField = $("#show_hide_password input");
$("#show_hide_password input").attr("type", "password"); var icon = $("#show_hide_password i");
$("#show_hide_password i").addClass("fa-eye-slash"); if (inputField.attr("type") == "text") {
$("#show_hide_password i").removeClass("fa-eye"); inputField.attr("type", "password");
} else if ($("#show_hide_password input").attr("type") == "password") { icon.addClass("fa-eye-slash");
$("#show_hide_password input").attr("type", "text"); icon.removeClass("fa-eye");
$("#show_hide_password i").removeClass("fa-eye-slash"); } else if (inputField.attr("type") == "password") {
$("#show_hide_password i").addClass("fa-eye"); inputField.attr("type", "text");
icon.removeClass("fa-eye-slash");
icon.addClass("fa-eye");
} }
}); });
});
document // Event-Listener für Datei-Upload
.querySelector(".custom-file-input") $(".custom-file-input").on("change", function (e) {
.addEventListener("change", function (e) {
var fileName = document.getElementById("fw-update-file").files[0].name; var fileName = document.getElementById("fw-update-file").files[0].name;
var nextSibling = e.target.nextElementSibling; var nextSibling = e.target.nextElementSibling;
nextSibling.innerText = fileName; nextSibling.innerText = fileName;
}); });
// Event-Listener für Live-Debug-Eingabe
$('#livedebug-in').on('keydown', function(event) {
if (event.key === 'Enter' || event.keyCode === 13) {
event.preventDefault(); // Verhindert, dass die Enter-Taste die Standardaktion ausführt (z.B. Absenden eines Formulars)
const command = $('#livedebug-in').val(); // Den Befehl aus dem Eingabefeld holen
websocket_sendDebugCommand(command); // Den Befehl an die Funktion übergeben
$('#livedebug-in').val(''); // Leert das Eingabefeld
$('#livedebug-in').focus(); // Setzt den Fokus zurück auf das Eingabefeld
}
});
});

View File

@@ -84,7 +84,6 @@ function onMessage(event) {
processDTCNotifications(dtcArray); processDTCNotifications(dtcArray);
fillDTCTable(dtcArray); fillDTCTable(dtcArray);
} else if (data.startsWith("MAPPING_STATUS:")) { } else if (data.startsWith("MAPPING_STATUS:")) {
const data_sliced = data.slice(15); const data_sliced = data.slice(15);
statusMapping = createMapping(data_sliced); statusMapping = createMapping(data_sliced);
@@ -140,6 +139,10 @@ function websocket_sendevent(element_id, element_value) {
websocket.send(element_id + ":" + element_value); websocket.send(element_id + ":" + element_value);
} }
function websocket_sendDebugCommand(command) {
websocket.send("debug-in:" + command);
}
function do_resize(textbox) { function do_resize(textbox) {
var maxrows = 15; var maxrows = 15;
var minrows = 3; var minrows = 3;
@@ -179,9 +182,12 @@ function fillValuesToHTML(dataset) {
// Wenn das Element ein Settingsabschnitt-div ist // Wenn das Element ein Settingsabschnitt-div ist
if (dataset[key] == 0) element.style.display = "none"; if (dataset[key] == 0) element.style.display = "none";
else element.style.display = ""; else element.style.display = "";
} else if (element.tagName === "DIV") { } else if (element.tagName === "DIV" || element.tagName === "SPAN") {
if (element.classList.contains("format-time")) { if (element.classList.contains("format-time")) {
element.innerText = formatTime(dataset[key]); element.innerText = formatTime(dataset[key]);
} else if (element.classList.contains("faction-logo")) {
// Faction-Logo-Logik
updateFactionLogo(element, dataset[key]);
} else { } else {
element.innerText = dataset[key]; element.innerText = dataset[key];
} }
@@ -191,9 +197,36 @@ function fillValuesToHTML(dataset) {
} }
} }
} }
// 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";
var factionClass = "faction" + faction;
if (element.classList.contains(factionClass)) {
element.classList.add(glowClass);
} else {
element.classList.remove(glowClass);
}
}
// Funktion zum Setzen des ausgewählten Werts für Dropdowns // Funktion zum Setzen des ausgewählten Werts für Dropdowns
function setDropdownValue(selectElement, value) { function setDropdownValue(selectElement, value) {

View File

@@ -1 +0,0 @@
1.05

View File

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

View File

@@ -57,5 +57,5 @@ void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data);
void Debug_pushMessage(const char *format, ...); void Debug_pushMessage(const char *format, ...);
void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status); void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status);
void Debug_Process(); void Debug_Process();
void Debug_ProcessCommand(uint8_t *command);
#endif #endif

View File

@@ -66,8 +66,6 @@ typedef struct
char Faction_1_Name[33]; char Faction_1_Name[33];
char Faction_2_Name[33]; char Faction_2_Name[33];
char Faction_3_Name[33]; char Faction_3_Name[33];
char wifi_ap_ssid[33];
char wifi_ap_password[64];
char wifi_client_ssid[33]; char wifi_client_ssid[33];
char wifi_client_password[64]; char wifi_client_password[64];
bool wifi_autoconnect; bool wifi_autoconnect;
@@ -82,15 +80,13 @@ 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",
QUOTE(WIFI_AP_PASSWORD),
QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_SSID_CLIENT),
QUOTE(WIFI_PASSWORD_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT),
true, true,
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();
@@ -102,7 +98,6 @@ 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 writeSequentialToEEPROM(uint16_t memoryAddress, uint16_t length);
void writeZeroToEEPROM(uint16_t memoryAddress, uint16_t length); void writeZeroToEEPROM(uint16_t memoryAddress, uint16_t length);

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

@@ -1,31 +1,75 @@
#ifndef _SANITYCHECK_H_ #ifndef _SANITYCHECK_H_
#define _SANITYCHECK_H_ #define _SANITYCHECK_H_
// Utility macros for handling quotes
#define Q(x) #x
#define QUOTE(x) Q(x)
// Utility macros for checking string lengths
#define STRING_LENGTH_CHECK(str, max_length) (sizeof(str) <= (max_length) + 1)
#define IS_ALPHANUMERIC(c) ((((c) >= '0') && ((c) <= '9')) || (((c) >= 'A') && ((c) <= 'Z')) || (((c) >= 'a') && ((c) <= 'z')))
#define IS_PRINTABLE_ASCII(c) ((c) >= 32 && (c) <= 126)
#define IS_VALID_WIFI_CHAR(c) (IS_PRINTABLE_ASCII(c))
#define IS_ALPHANUMERIC_HELPER(str, i) (i >= sizeof(str) - 1 || IS_ALPHANUMERIC(str[i]))
#define IS_PRINTABLE_ASCII_HELPER(str, i) (i >= sizeof(str) - 1 || IS_PRINTABLE_ASCII(str[i]))
#define IS_VALID_WIFI_CHAR_HELPER(str, i) (i >= sizeof(str) - 1 || IS_VALID_WIFI_CHAR(str[i]))
#define IS_ALPHANUMERIC_IMPL(str, i) (IS_ALPHANUMERIC_HELPER(str, i) && IS_ALPHANUMERIC_HELPER(str, i+1) && IS_ALPHANUMERIC_HELPER(str, i+2) && IS_ALPHANUMERIC_HELPER(str, i+3) && IS_ALPHANUMERIC_HELPER(str, i+4) && IS_ALPHANUMERIC_HELPER(str, i+5) && IS_ALPHANUMERIC_HELPER(str, i+6) && IS_ALPHANUMERIC_HELPER(str, i+7) && IS_ALPHANUMERIC_HELPER(str, i+8) && IS_ALPHANUMERIC_HELPER(str, i+9) && IS_ALPHANUMERIC_HELPER(str, i+10) && IS_ALPHANUMERIC_HELPER(str, i+11) && IS_ALPHANUMERIC_HELPER(str, i+12) && IS_ALPHANUMERIC_HELPER(str, i+13) && IS_ALPHANUMERIC_HELPER(str, i+14) && IS_ALPHANUMERIC_HELPER(str, i+15) && IS_ALPHANUMERIC_HELPER(str, i+16))
#define IS_PRINTABLE_ASCII_IMPL(str, i) (IS_PRINTABLE_ASCII_HELPER(str, i) && IS_PRINTABLE_ASCII_HELPER(str, i+1) && IS_PRINTABLE_ASCII_HELPER(str, i+2) && IS_PRINTABLE_ASCII_HELPER(str, i+3) && IS_PRINTABLE_ASCII_HELPER(str, i+4) && IS_PRINTABLE_ASCII_HELPER(str, i+5) && IS_PRINTABLE_ASCII_HELPER(str, i+6) && IS_PRINTABLE_ASCII_HELPER(str, i+7) && IS_PRINTABLE_ASCII_HELPER(str, i+8) && IS_PRINTABLE_ASCII_HELPER(str, i+9) && IS_PRINTABLE_ASCII_HELPER(str, i+10) && IS_PRINTABLE_ASCII_HELPER(str, i+11) && IS_PRINTABLE_ASCII_HELPER(str, i+12) && IS_PRINTABLE_ASCII_HELPER(str, i+13) && IS_PRINTABLE_ASCII_HELPER(str, i+14) && IS_PRINTABLE_ASCII_HELPER(str, i+15) && IS_PRINTABLE_ASCII_HELPER(str, i+16) && IS_PRINTABLE_ASCII_HELPER(str, i+17) && IS_PRINTABLE_ASCII_HELPER(str, i+18) && IS_PRINTABLE_ASCII_HELPER(str, i+19) && IS_PRINTABLE_ASCII_HELPER(str, i+20) && IS_PRINTABLE_ASCII_HELPER(str, i+21) && IS_PRINTABLE_ASCII_HELPER(str, i+22) && IS_PRINTABLE_ASCII_HELPER(str, i+23) && IS_PRINTABLE_ASCII_HELPER(str, i+24))
#define IS_ALPHANUMERIC_STRING(str) (IS_ALPHANUMERIC_IMPL(str, 0))
#define IS_PRINTABLE_ASCII_STRING(str) (IS_PRINTABLE_ASCII_IMPL(str, 0))
#define IS_VALID_WIFI_STRING(str) (IS_VALID_WIFI_CHAR_HELPER(str, 0) && IS_VALID_WIFI_CHAR_HELPER(str, 1) && IS_VALID_WIFI_CHAR_HELPER(str, 2) && IS_VALID_WIFI_CHAR_HELPER(str, 3) && IS_VALID_WIFI_CHAR_HELPER(str, 4) && IS_VALID_WIFI_CHAR_HELPER(str, 5) && IS_VALID_WIFI_CHAR_HELPER(str, 6) && IS_VALID_WIFI_CHAR_HELPER(str, 7) && IS_VALID_WIFI_CHAR_HELPER(str, 8) && IS_VALID_WIFI_CHAR_HELPER(str, 9) && IS_VALID_WIFI_CHAR_HELPER(str, 10) && IS_VALID_WIFI_CHAR_HELPER(str, 11) && IS_VALID_WIFI_CHAR_HELPER(str, 12) && IS_VALID_WIFI_CHAR_HELPER(str, 13) && IS_VALID_WIFI_CHAR_HELPER(str, 14) && IS_VALID_WIFI_CHAR_HELPER(str, 15) && IS_VALID_WIFI_CHAR_HELPER(str, 16) && IS_VALID_WIFI_CHAR_HELPER(str, 17) && IS_VALID_WIFI_CHAR_HELPER(str, 18) && IS_VALID_WIFI_CHAR_HELPER(str, 19) && IS_VALID_WIFI_CHAR_HELPER(str, 20) && IS_VALID_WIFI_CHAR_HELPER(str, 21) && IS_VALID_WIFI_CHAR_HELPER(str, 22) && IS_VALID_WIFI_CHAR_HELPER(str, 23) && IS_VALID_WIFI_CHAR_HELPER(str, 24) && IS_VALID_WIFI_CHAR_HELPER(str, 25) && IS_VALID_WIFI_CHAR_HELPER(str, 26) && IS_VALID_WIFI_CHAR_HELPER(str, 27) && IS_VALID_WIFI_CHAR_HELPER(str, 28) && IS_VALID_WIFI_CHAR_HELPER(str, 29) && IS_VALID_WIFI_CHAR_HELPER(str, 30) && IS_VALID_WIFI_CHAR_HELPER(str, 31) && IS_VALID_WIFI_CHAR_HELPER(str, 32))
// ADMIN_PASSWORD check
#ifndef ADMIN_PASSWORD #ifndef ADMIN_PASSWORD
#error "You need to define ADMIN_PASSWORD for OTA-Update" #error "You need to define ADMIN_PASSWORD for OTA-Update"
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(ADMIN_PASSWORD), 16), "ADMIN_PASSWORD must be at most 16 characters long");
#endif #endif
// WIFI_AP_SSID check
#ifndef WIFI_AP_SSID #ifndef WIFI_AP_SSID
#warning "No WIFI_AP_SSID defined. Using DeviceName" #warning "No WIFI_AP_SSID defined. Using DeviceName"
#define WIFI_AP_SSID DEVICE_NAME #define WIFI_AP_SSID DEVICE_NAME
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(WIFI_AP_SSID), 32), "WIFI_AP_SSID must be at most 32 characters long");
_Static_assert(IS_VALID_WIFI_STRING(QUOTE(WIFI_AP_SSID)), "WIFI_AP_SSID must contain only valid WiFi characters");
#endif #endif
#ifndef WIFI_AP_PASSWORD // WIFI_PASS_SEED check
#error "You must define an WIFI_AP_PASSWORD for Standalone AP-Mode" #ifndef WIFI_PASS_SEED
#error "You must define an WIFI_PASS_SEED for Standalone AP-Mode"
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(WIFI_PASS_SEED), 16), "WIFI_PASS_SEED must be at most 16 characters long");
_Static_assert(IS_ALPHANUMERIC_STRING(QUOTE(WIFI_PASS_SEED)), "WIFI_PASS_SEED must contain only alphanumeric characters (a-z, A-Z, 0-9)");
#endif #endif
// DEVICE_NAME check
#ifndef DEVICE_NAME
#error "You must define DEVICE_NAME"
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(DEVICE_NAME), 24), "DEVICE_NAME must be at most 24 characters long");
_Static_assert(IS_VALID_WIFI_STRING(QUOTE(DEVICE_NAME)), "DEVICE_NAME must contain only valid WiFi characters");
#endif
// Mutual exclusion check for LoRa features
#if defined(FEATURE_ENABLE_UARTLORA) && defined(FEATURE_ENABLE_LORA) #if defined(FEATURE_ENABLE_UARTLORA) && defined(FEATURE_ENABLE_LORA)
#error "You cannot enable LoRa and UART-Protocol at the same time!" #error "You cannot enable LoRa and UART-Protocol at the same time!"
#endif #endif
// WIFI_CLIENT checks
#ifdef FEATURE_ENABLE_WIFI_CLIENT #ifdef FEATURE_ENABLE_WIFI_CLIENT
#ifndef WIFI_PASSWORD_CLIENT #ifndef WIFI_PASSWORD_CLIENT
#error "You must define an WIFI_PASSWORD for OTA-Update" #error "You must define an WIFI_PASSWORD_CLIENT for OTA-Update"
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(WIFI_PASSWORD_CLIENT), 63), "WIFI_PASSWORD_CLIENT must be at most 63 characters long");
#endif #endif
#ifndef WIFI_SSID_CLIENT #ifndef WIFI_SSID_CLIENT
#error "You must define an WIFI_SSID for OTA-Update" #error "You must define an WIFI_SSID_CLIENT for OTA-Update"
#else
_Static_assert(STRING_LENGTH_CHECK(QUOTE(WIFI_SSID_CLIENT), 32), "WIFI_SSID_CLIENT must be at most 32 characters long");
_Static_assert(IS_VALID_WIFI_STRING(QUOTE(WIFI_SSID_CLIENT)), "WIFI_SSID_CLIENT must contain only valid WiFi characters");
#endif #endif
#endif #endif
#endif // _SANITYCHECK_H_
#endif //_SANITYCHECK_H_

View File

@@ -3,10 +3,10 @@
* *
* @brief Header file for converting structs to JSON objects. * @brief Header file for converting structs to JSON objects.
* *
* @note This file is auto-generated by a script on 2024-05-30 22:54:25. * @note This file is auto-generated by a script on 2024-06-06 18:52:33.
* *
* @author Marcel Peterkau * @author Marcel Peterkau
* @date 30.05.2024 * @date 06.06.2024
*/ */
#ifndef _STRUCT2JSON_H_ #ifndef _STRUCT2JSON_H_
@@ -23,4 +23,4 @@ void generateJsonObject_PersistenceData(JsonObject data);
#endif /* _STRUCT2JSON_H_ */ #endif /* _STRUCT2JSON_H_ */
// CODEGENERATOR_CHECKSUM: 735cd4daf9a46bd773bdf5e6cd5a58d61b0d877196399bc2784a0d0ea7af717d // CODEGENERATOR_CHECKSUM: 3a3fd3024dd9dd769ff5d62f61b3ba1b781b7d06e7568a1ddac56d193dba9ee5

View File

@@ -0,0 +1,48 @@
#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);
/**
* @brief Generates a device-specific password based on a seed and the ESP8266 Chip ID.
*
* This function combines a given seed with the unique Chip ID of the ESP8266 device to create a device-specific password.
* The resulting password is stored in the provided character buffer.
*
* @param seed The seed string used for password generation. Should be up to 16 characters long.
* @param passwordBuffer The character buffer where the generated password will be stored.
* @param bufferLength The length of the password buffer. Should be large enough to hold the generated password and null terminator.
*/
void GenerateDeviceSpecificPassword(const char* seed, char* passwordBuffer, size_t bufferLength);
#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.08
custom_flash_version = 1.08
upload_protocol = esptool upload_protocol = esptool
upload_speed = 921600 upload_speed = 921600
;upload_port = 10.0.1.48 ;upload_port = 10.0.0.1
;upload_flags = ;upload_flags =
; --port=8266 ; --port=8266
; --auth=${wifi_cred.admin_password} ; --auth=${wifi_cred.admin_password}
build_flags= build_flags=
-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.admin_password} -DADMIN_PASSWORD=${wifi_cred.admin_password}
-DWIFI_SSID_CLIENT=${wifi_cred.wifi_ssid_client} -DWIFI_SSID_CLIENT=${wifi_cred.wifi_ssid_client}
-DWIFI_PASSWORD_CLIENT=${wifi_cred.wifi_password_client} -DWIFI_PASSWORD_CLIENT=${wifi_cred.wifi_password_client}
-DADMIN_PASSWORD=${wifi_cred.admin_password} -DWIFI_PASS_SEED=${wifi_cred.wifi_pass_seed}
-DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password}
-DDEVICE_NAME='"Dark Emergency Timer"' -DDEVICE_NAME='"Dark Emergency Timer"'
;build_type = debug ;build_type = debug

View File

@@ -3,6 +3,7 @@
const char *BatteryString[] = { const char *BatteryString[] = {
"Undefined", "Undefined",
"LiPo 2S", "LiPo 2S",
"LiPo 3S"}; "LiPo 3S"
};
const size_t BatteryString_Elements = sizeof(BatteryString) / sizeof(BatteryString[0]); const size_t BatteryString_Elements = sizeof(BatteryString) / sizeof(BatteryString[0]);

View File

@@ -12,10 +12,22 @@
*/ */
#include "debugger.h" #include "debugger.h"
#include "utilities.h"
#include <pgmspace.h>
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 +38,94 @@ 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 +207,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:
@@ -184,65 +282,22 @@ void Debug_pushMessage(const char *format, ...)
* *
* @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 == "reboot") if (strcmp(command, commandMappings[i].command) == 0)
globals.systemStatus = sysStat_Shutdown; {
else if (command == "sysinfo") commandMappings[i].function();
Debug_printSystemInfo(); commandFound = true;
else if (command == "netinfo") break;
Debug_printWifiInfo(); }
else if (command == "formatCFG") }
Debug_formatCFG(); if (!commandFound)
else if (command == "formatPDS") {
Debug_formatPersistence();
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 == "killEE")
writeSequentialToEEPROM(0, 1024);
else if (command == "zeroEE")
writeZeroToEEPROM(0, 1024);
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");
}
} }
/** /**
@@ -270,7 +325,7 @@ void Debug_formatPersistence()
*/ */
void Debug_printSystemInfo() void Debug_printSystemInfo()
{ {
Debug_pushMessage("Hostname: %s\n", globals.DeviceName); Debug_pushMessage("Hostname: %s\n", globals.DeviceNameId);
FlashMode_t ideMode = ESP.getFlashChipMode(); FlashMode_t ideMode = ESP.getFlashChipMode();
Debug_pushMessage("Sdk version: %s\n", ESP.getSdkVersion()); Debug_pushMessage("Sdk version: %s\n", ESP.getSdkVersion());
@@ -313,8 +368,6 @@ void Debug_dumpConfig()
Debug_pushMessage("Faction_1_Name: %s\n", ConfigData.Faction_3_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("active_faction_on_reboot: %d\n", ConfigData.active_faction_on_reboot);
Debug_pushMessage("wifi_autoconnect: %d\n", ConfigData.wifi_autoconnect); 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_ssid: %s\n", ConfigData.wifi_client_ssid);
Debug_pushMessage("wifi_client_password: %s\n", ConfigData.wifi_client_password); 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);
@@ -355,7 +408,10 @@ void Debug_dumpPersistance()
*/ */
void Debug_printWifiInfo() void Debug_printWifiInfo()
{ {
char buffer[33];
GenerateDeviceSpecificPassword(QUOTE(WIFI_PASS_SEED), buffer, sizeof(buffer));
Debug_pushMessage("IP Adress: %s\n", WiFi.localIP().toString().c_str()); Debug_pushMessage("IP Adress: %s\n", WiFi.localIP().toString().c_str());
Debug_pushMessage("WiFi AP Pw: %s\n", buffer);
} }
/** /**
@@ -415,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++)
@@ -439,31 +495,50 @@ 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 strncpy_P(buffer, helpText[i], sizeof(buffer));
memcpy_P(buff, (helpCmd + (i * 63)), 63); buffer[sizeof(buffer) - 1] = '\0'; // Sicherstellen, dass der String nullterminiert ist
buff[63] = 0; Serial.println(buffer);
// Display the help command
Debug_pushMessage(buff);
} }
} }
/**
* @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 Processes a debug command.
*
* This function takes a pointer to a debug command and processes it by
* passing it to the `processCmdDebug` function. The command is expected
* to be a string, so it is cast to `const char *` before being processed.
*
* @param command Pointer to the debug command, expected to be a null-terminated string.
*/
void Debug_ProcessCommand(uint8_t *command)
{
processCmdDebug((const char *)command);
}
/** /**
* @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.
* *

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);
@@ -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();
} }
/** /**
@@ -156,7 +158,7 @@ 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);
} }
@@ -398,30 +400,16 @@ uint32_t ConfigSanityCheck(bool autocorrect)
{ {
uint32_t setting_reset_bits = 0; uint32_t setting_reset_bits = 0;
if (!validateWiFiString(ConfigData.wifi_ap_ssid, sizeof(ConfigData.wifi_ap_ssid)))
{
SET_BIT(setting_reset_bits, 1);
if (autocorrect)
strncpy(ConfigData.wifi_ap_ssid, ConfigData_defaults.wifi_ap_ssid, sizeof(ConfigData.wifi_ap_ssid));
}
if (!validateWiFiString(ConfigData.wifi_ap_password, sizeof(ConfigData.wifi_ap_password)))
{
SET_BIT(setting_reset_bits, 2);
if (autocorrect)
strncpy(ConfigData.wifi_ap_password, ConfigData_defaults.wifi_ap_password, sizeof(ConfigData.wifi_ap_password));
}
if (!validateWiFiString(ConfigData.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid))) if (!validateWiFiString(ConfigData.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid)))
{ {
SET_BIT(setting_reset_bits, 3); SET_BIT(setting_reset_bits, 0);
if (autocorrect) if (autocorrect)
strncpy(ConfigData.wifi_client_ssid, ConfigData_defaults.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid)); strncpy(ConfigData.wifi_client_ssid, ConfigData_defaults.wifi_client_ssid, sizeof(ConfigData.wifi_client_ssid));
} }
if (!validateWiFiString(ConfigData.wifi_client_password, sizeof(ConfigData.wifi_client_password))) if (!validateWiFiString(ConfigData.wifi_client_password, sizeof(ConfigData.wifi_client_password)))
{ {
SET_BIT(setting_reset_bits, 4); SET_BIT(setting_reset_bits, 1);
if (autocorrect) if (autocorrect)
strncpy(ConfigData.wifi_client_password, ConfigData_defaults.wifi_client_password, sizeof(ConfigData.wifi_client_password)); strncpy(ConfigData.wifi_client_password, ConfigData_defaults.wifi_client_password, sizeof(ConfigData.wifi_client_password));
} }
@@ -429,48 +417,6 @@ 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++)
{
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 Write sequential numbers to a portion of EEPROM. * @brief Write sequential numbers to a portion of EEPROM.

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];
@@ -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(); GetConfig_EEPROM();
GetPersistence_EEPROM(); GetPersistence_EEPROM();
Serial.print("\nEE-Init done"); Serial.printf("Initialized EEPROM at Address 0x%02X\n", I2C_EE_ADDRESS);
}
else
{
Serial.print("EEPROM not Initialized\n");
}
if (i2c_io.begin()) if (i2c_io.begin())
{ {
@@ -150,7 +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();
@@ -415,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;
} }
@@ -424,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;
@@ -434,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;
@@ -444,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;
@@ -566,6 +572,8 @@ void wifiMaintainConnectionTicker_callback()
*/ */
void toggleWiFiAP(bool shutdown) void toggleWiFiAP(bool shutdown)
{ {
char buffer[33];
char buffer_pass[33];
// Check if WiFi is currently active // Check if WiFi is currently active
if (WiFi.getMode() != WIFI_OFF) if (WiFi.getMode() != WIFI_OFF)
{ {
@@ -583,7 +591,9 @@ 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));
GenerateDeviceSpecificPassword(QUOTE(WIFI_PASS_SEED), buffer_pass, sizeof(buffer_pass));
WiFi.softAP(buffer, buffer_pass);
// 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
@@ -696,7 +706,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

@@ -3,10 +3,10 @@
* *
* @brief Implementation file for converting structs to JSON objects. * @brief Implementation file for converting structs to JSON objects.
* *
* @note This file is auto-generated by a script on 2024-05-30 22:54:25. * @note This file is auto-generated by a script on 2024-06-06 18:52:33.
* *
* @author Marcel Peterkau * @author Marcel Peterkau
* @date 30.05.2024 * @date 06.06.2024
*/ */
@@ -20,8 +20,6 @@ void generateJsonObject_ConfigData(JsonObject data)
data["Faction_1_Name"] = ConfigData.Faction_1_Name; data["Faction_1_Name"] = ConfigData.Faction_1_Name;
data["Faction_2_Name"] = ConfigData.Faction_2_Name; data["Faction_2_Name"] = ConfigData.Faction_2_Name;
data["Faction_3_Name"] = ConfigData.Faction_3_Name; data["Faction_3_Name"] = ConfigData.Faction_3_Name;
data["wifi_ap_ssid"] = ConfigData.wifi_ap_ssid;
data["wifi_ap_password"] = ConfigData.wifi_ap_password;
data["wifi_client_ssid"] = ConfigData.wifi_client_ssid; data["wifi_client_ssid"] = ConfigData.wifi_client_ssid;
data["wifi_client_password"] = ConfigData.wifi_client_password; data["wifi_client_password"] = ConfigData.wifi_client_password;
data["wifi_autoconnect"] = ConfigData.wifi_autoconnect; data["wifi_autoconnect"] = ConfigData.wifi_autoconnect;
@@ -40,4 +38,4 @@ void generateJsonObject_PersistenceData(JsonObject data)
// CODEGENERATOR_CHECKSUM: 735cd4daf9a46bd773bdf5e6cd5a58d61b0d877196399bc2784a0d0ea7af717d // CODEGENERATOR_CHECKSUM: 3a3fd3024dd9dd769ff5d62f61b3ba1b781b7d06e7568a1ddac56d193dba9ee5

126
Software/src/utilities.cpp Normal file
View File

@@ -0,0 +1,126 @@
#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 == '~' || 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 == '~' || c == ' '))
{
// Replace invalid character with placeholder
buffer[i] = '_';
}
else
{
// Copy valid character to buffer
buffer[i] = c;
}
}
// Null-terminate the buffer
buffer[i] = '\0';
}
/**
* @brief Generates a device-specific password based on a seed and the ESP8266 Chip ID.
*
* This function combines a given seed with the unique Chip ID of the ESP8266 device to create a device-specific password.
* The resulting password is stored in the provided character buffer.
*
* @param seed The seed string used for password generation. Should be up to 16 characters long.
* @param passwordBuffer The character buffer where the generated password will be stored.
* @param bufferLength The length of the password buffer. Should be large enough to hold the generated password and null terminator.
*/
void GenerateDeviceSpecificPassword(const char* seed, char* passwordBuffer, size_t bufferLength) {
uint32_t chipId = ESP.getChipId();
char chipIdStr[9]; // 8 characters + null terminator
snprintf(chipIdStr, sizeof(chipIdStr), "%08X", chipId);
char combined[33]; // seed (16) + chipId (8) + null terminator (1)
strncpy(combined, seed, 16);
strncat(combined, chipIdStr, 8);
// Simple hash function for demonstration
unsigned long hash = 5381;
int c;
char *str = combined;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; // hash * 33 + c
}
snprintf(passwordBuffer, bufferLength, "%08X%08X", hash, hash);
passwordBuffer[bufferLength - 1] = '\0'; // Ensure null termination
}

View File

@@ -71,7 +71,7 @@ void initWebUI()
} }
// Initialize mDNS and add service // Initialize mDNS and add service
MDNS.begin(globals.DeviceName); MDNS.begin(globals.DeviceNameId);
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
// Set up WebSocket event handler and attach to web server // Set up WebSocket event handler and attach to web server
@@ -295,8 +295,6 @@ void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &f
strncpy(ConfigData.Faction_1_Name, json["config"]["Faction_1_Name"].as<const char *>(), sizeof(ConfigData.Faction_1_Name)); strncpy(ConfigData.Faction_1_Name, json["config"]["Faction_1_Name"].as<const char *>(), sizeof(ConfigData.Faction_1_Name));
strncpy(ConfigData.Faction_2_Name, json["config"]["Faction_2_Name"].as<const char *>(), sizeof(ConfigData.Faction_2_Name)); strncpy(ConfigData.Faction_2_Name, json["config"]["Faction_2_Name"].as<const char *>(), sizeof(ConfigData.Faction_2_Name));
strncpy(ConfigData.Faction_3_Name, json["config"]["Faction_3_Name"].as<const char *>(), sizeof(ConfigData.Faction_3_Name)); strncpy(ConfigData.Faction_3_Name, json["config"]["Faction_3_Name"].as<const char *>(), sizeof(ConfigData.Faction_3_Name));
strncpy(ConfigData.wifi_ap_ssid, json["config"]["wifi_ap_ssid"].as<const char *>(), sizeof(ConfigData.wifi_ap_ssid));
strncpy(ConfigData.wifi_ap_password, json["config"]["wifi_ap_password"].as<const char *>(), sizeof(ConfigData.wifi_ap_password));
strncpy(ConfigData.wifi_client_ssid, json["config"]["wifi_client_ssid"].as<const char *>(), sizeof(ConfigData.wifi_client_ssid)); strncpy(ConfigData.wifi_client_ssid, json["config"]["wifi_client_ssid"].as<const char *>(), sizeof(ConfigData.wifi_client_ssid));
strncpy(ConfigData.wifi_client_password, json["config"]["wifi_client_password"].as<const char *>(), sizeof(ConfigData.wifi_client_password)); strncpy(ConfigData.wifi_client_password, json["config"]["wifi_client_password"].as<const char *>(), sizeof(ConfigData.wifi_client_password));
@@ -342,7 +340,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;
@@ -426,6 +424,10 @@ void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len)
{ {
Websocket_HandleSettings(data + strlen("set-")); Websocket_HandleSettings(data + strlen("set-"));
} }
else if (strncmp((char *)data, "debug", strlen("debug")) == 0)
{
Debug_ProcessCommand(data - strlen("debug"));
}
else else
{ {
Debug_pushMessage("Got unknown Websocket-Message '%s' from client\n", (char *)data); Debug_pushMessage("Got unknown Websocket-Message '%s' from client\n", (char *)data);
@@ -472,6 +474,18 @@ void Websocket_HandleButtons(uint8_t *data)
{ {
globals.systemStatus = sysStat_Shutdown; globals.systemStatus = sysStat_Shutdown;
} }
else if (strcmp(identifier, "faction1") == 0)
{
PersistenceData.activeFaction = FACTION_1;
}
else if (strcmp(identifier, "faction2") == 0)
{
PersistenceData.activeFaction = FACTION_2;
}
else if (strcmp(identifier, "faction3") == 0)
{
PersistenceData.activeFaction = FACTION_3;
}
else else
{ {
Debug_pushMessage("Got unknown Button-id '%s' from ws-client\n", identifier); Debug_pushMessage("Got unknown Button-id '%s' from ws-client\n", identifier);
@@ -497,7 +511,6 @@ void Websocket_HandleSettings(uint8_t *data)
{ {
ConfigData.active_faction_on_reboot = value[0] == '1' ? true : false; ConfigData.active_faction_on_reboot = value[0] == '1' ? true : false;
} }
else if (strcmp(identifier, "name_faction1") == 0) else if (strcmp(identifier, "name_faction1") == 0)
{ {
strncpy(ConfigData.Faction_1_Name, value, sizeof(ConfigData.Faction_1_Name)); strncpy(ConfigData.Faction_1_Name, value, sizeof(ConfigData.Faction_1_Name));
@@ -511,10 +524,6 @@ void Websocket_HandleSettings(uint8_t *data)
strncpy(ConfigData.Faction_3_Name, value, sizeof(ConfigData.Faction_3_Name)); strncpy(ConfigData.Faction_3_Name, value, sizeof(ConfigData.Faction_3_Name));
} }
else if (strcmp(identifier, "batterytype") == 0) else if (strcmp(identifier, "batterytype") == 0)
{
strncpy(ConfigData.wifi_client_ssid, value, sizeof(ConfigData.wifi_client_ssid));
}
else if (strcmp(identifier, "batterytype") == 0)
{ {
int index = findIndexByString(value, BatteryString, BatteryString_Elements); int index = findIndexByString(value, BatteryString, BatteryString_Elements);
batterytypePreselect = (batteryType_t)index; batterytypePreselect = (batteryType_t)index;
@@ -599,7 +608,6 @@ 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) if (send_mapping)
{ {
const char mapping[] = "MAPPING_STATUS:" const char mapping[] = "MAPPING_STATUS:"
@@ -618,22 +626,23 @@ void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
Debug_pushMessage("send MAPPING_STATUS WS-Client Data\n"); Debug_pushMessage("send MAPPING_STATUS WS-Client Data\n");
} }
String temp = "STATUS:"; char dataString[200] = {0}; // Maximal 200 Zeichen für den Data-String
temp.concat(String(globals.battery_level) + ";"); sprintf(dataString, "STATUS:%d;%s;%d;%ld;%ld;%ld;",
temp.concat(String(globals.systemStatustxt) + ";"); globals.battery_level,
temp.concat(String(PersistenceData.activeFaction) + ";"); globals.systemStatustxt,
temp.concat(String(PersistenceData.faction_1_timer) + ";"); PersistenceData.activeFaction,
temp.concat(String(PersistenceData.faction_2_timer) + ";"); PersistenceData.faction_1_timer,
temp.concat(String(PersistenceData.faction_3_timer) + ";"); PersistenceData.faction_2_timer,
PersistenceData.faction_3_timer);
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);
} }
} }
@@ -650,40 +659,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-revision;";
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,5 +1,5 @@
[wifi_cred] [wifi_cred]
admin_password = chainlube admin_password = chainlube
wifi_ap_password = wifiappass wifi_pass_seed = wifiapseed
wifi_ssid_client = wifi-ssid wifi_ssid_client = wifi-ssid
wifi_password_client = ota-password wifi_password_client = ota-password