Compare commits
20 Commits
PCB_Rev_1.
...
a849e65f9a
Author | SHA1 | Date | |
---|---|---|---|
a849e65f9a | |||
b6f9de2894 | |||
68d571a747 | |||
b7ccffc157 | |||
c7af5619eb | |||
3357691a21 | |||
5638c03e76 | |||
051796b19b | |||
cf032fe516 | |||
618ee4ce80 | |||
41d38a2afc | |||
bb824f0376 | |||
d08e0cfbb3 | |||
479b1861d1 | |||
b276d74907 | |||
8c022188c6 | |||
4d020b5ced | |||
c399672755 | |||
dd34bfe645 | |||
125fc17c39 |
@@ -11738,7 +11738,7 @@
|
|||||||
(layer "Edge.Cuts")
|
(layer "Edge.Cuts")
|
||||||
(uuid "da6f4122-0ecc-496f-b0fd-e4abef534976")
|
(uuid "da6f4122-0ecc-496f-b0fd-e4abef534976")
|
||||||
)
|
)
|
||||||
(gr_text "KTM CAN ChainLube Mk1\nPCB Rev.: 1.4\n(c) 2025 - Marcel Peterkau"
|
(gr_text "KTM CAN ChainLube Mk1\nPCB Rev.: 1.5\n(c) 2025 - Marcel Peterkau"
|
||||||
(at 156.464 85.344 90)
|
(at 156.464 85.344 90)
|
||||||
(layer "B.SilkS")
|
(layer "B.SilkS")
|
||||||
(uuid "00000000-0000-0000-0000-000061de6936")
|
(uuid "00000000-0000-0000-0000-000061de6936")
|
||||||
|
119
Jenkinsfile
vendored
Normal file
119
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
BUILD_ENV = "pcb_rev_1-4_serial"
|
||||||
|
PIO_HOME_DIR = "${WORKSPACE}/.pio"
|
||||||
|
VENV_PATH = "${WORKSPACE}/Software/.venv"
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters {
|
||||||
|
choice(
|
||||||
|
name: 'BUILD_ENV',
|
||||||
|
choices: ['pcb_rev_1-2_serial', 'pcb_rev_1-3_serial', 'pcb_rev_1-4_serial'],
|
||||||
|
description: 'Firmware-Umgebung auswählen'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
|
||||||
|
stage('🧰 Setup PlatformIO') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
sh """
|
||||||
|
[ -d .venv ] || python3 -m venv .venv
|
||||||
|
${env.VENV_PATH}/bin/pip install --upgrade pip platformio
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('📄 Dummy WiFi-Creds') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
writeFile file: 'wifi_credentials.ini', text: '''
|
||||||
|
[wifi_cred]
|
||||||
|
wifi_ssid_client = DummySSID
|
||||||
|
wifi_password_client = DummyPass
|
||||||
|
admin_password = Admin123
|
||||||
|
wifi_ap_password = DummyAP
|
||||||
|
'''.stripIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('🧪 Build Firmware') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
sh """
|
||||||
|
${env.VENV_PATH}/bin/platformio run -e ${params.BUILD_ENV}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('📁 Build Filesystem (LittleFS)') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
sh """
|
||||||
|
${env.VENV_PATH}/bin/platformio run -t buildfs -e ${params.BUILD_ENV}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('📦 Find & Archive Firmware') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
script {
|
||||||
|
echo "🔍 Suche nach Firmware (.fw.bin) und Filesystem (.fs.gz) Artefakten..."
|
||||||
|
|
||||||
|
def firmwareFiles = findFiles(glob: '.pio/build/**/*.fw.bin')
|
||||||
|
def fsFiles = findFiles(glob: '.pio/build/**/*.fs.gz')
|
||||||
|
|
||||||
|
if (firmwareFiles.length == 0 && fsFiles.length == 0) {
|
||||||
|
echo "⚠️ Keine passenden Artefakte (.fw.bin / .fs.gz) gefunden – nichts zu archivieren."
|
||||||
|
} else {
|
||||||
|
firmwareFiles.each { echo "📦 Firmware: ${it.path}" }
|
||||||
|
fsFiles.each { echo "📦 Filesystem: ${it.path}" }
|
||||||
|
|
||||||
|
def allArtifacts = (firmwareFiles + fsFiles).collect { it.path }
|
||||||
|
archiveArtifacts artifacts: allArtifacts.join(', ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('🔌 Flash Hardware (Dummy)') {
|
||||||
|
steps {
|
||||||
|
echo "TODO: Flash-Script aufrufen, z. B.: python3 Hardware/flash.py /dev/ttyUSB0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('🧠 Run Tests (Dummy)') {
|
||||||
|
steps {
|
||||||
|
dir('Testing') {
|
||||||
|
echo "TODO: Testing mit z. B.: python3 test_runner.py oder pytest starten"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('🧹 Cleanup Build Output') {
|
||||||
|
steps {
|
||||||
|
dir('Software') {
|
||||||
|
sh "rm -rf .pio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
echo "✅ CI abgeschlossen – Firmware gebaut, Dummy-Stages bereit"
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
echo "❌ Fehler im Build – Logs checken, Commander Seraphon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
Software/.gitignore
vendored
5
Software/.gitignore
vendored
@@ -2,4 +2,7 @@ data/
|
|||||||
.pio
|
.pio
|
||||||
.vscode
|
.vscode
|
||||||
wifi_credentials.ini
|
wifi_credentials.ini
|
||||||
__pycache__
|
__pycache__
|
||||||
|
# Node-Tools für Build-Scripts
|
||||||
|
/tools_node/
|
||||||
|
/data_stripped/
|
||||||
|
@@ -11,23 +11,41 @@ import platform
|
|||||||
Import("env")
|
Import("env")
|
||||||
Import("projenv")
|
Import("projenv")
|
||||||
|
|
||||||
# Überprüfe die Betriebssystemplattform
|
def ensure_node_tool(package_name, binary_name=None):
|
||||||
if platform.system() == "Windows":
|
"""Installiert das Tool lokal, wenn es fehlt – mit npm init bei Bedarf"""
|
||||||
# Setze die Pfade zu den Tools für Windows
|
if binary_name is None:
|
||||||
html_minifier_path = os.path.join(os.getenv("APPDATA"), "npm", "html-minifier.cmd")
|
binary_name = package_name
|
||||||
uglifyjs_path = os.path.join(os.getenv("APPDATA"), "npm", "uglifyjs.cmd")
|
|
||||||
terser_path = os.path.join(os.getenv("APPDATA"), "npm", "terser.cmd")
|
|
||||||
cssnano_path = os.path.join(os.getenv("APPDATA"), "npm", "cssnano.cmd")
|
|
||||||
elif platform.system() == "Linux":
|
|
||||||
# Setze die Namen der Tools für Linux
|
|
||||||
html_minifier_path = "html-minifier"
|
|
||||||
uglifyjs_path = "uglifyjs"
|
|
||||||
terser_path = "terser"
|
|
||||||
cssnano_path = "cssnano"
|
|
||||||
else:
|
|
||||||
# Hier könntest du weitere Bedingungen für andere Betriebssysteme hinzufügen
|
|
||||||
raise Exception("Unterstütztes Betriebssystem nicht erkannt")
|
|
||||||
|
|
||||||
|
project_dir = env.subst('$PROJECT_DIR')
|
||||||
|
tools_dir = os.path.join(project_dir, 'tools_node')
|
||||||
|
local_bin = os.path.join(tools_dir, 'node_modules', '.bin', binary_name)
|
||||||
|
|
||||||
|
os.makedirs(tools_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Wenn Binary schon vorhanden ist, einfach zurückgeben
|
||||||
|
if os.path.isfile(local_bin):
|
||||||
|
return local_bin
|
||||||
|
|
||||||
|
print(f"🛠️ Installing missing node tool: {package_name}")
|
||||||
|
|
||||||
|
# Initialisiere npm, falls noch nicht geschehen
|
||||||
|
if not os.path.isfile(os.path.join(tools_dir, 'package.json')):
|
||||||
|
print("📦 Initializing local npm project...")
|
||||||
|
subprocess.run(['npm', 'init', '-y'], cwd=tools_dir, check=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(['npm', 'install', package_name], cwd=tools_dir, check=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Fehler beim Installieren von {package_name}: {e}")
|
||||||
|
return binary_name # Fallback: globale binary
|
||||||
|
|
||||||
|
return local_bin if os.path.isfile(local_bin) else binary_name
|
||||||
|
|
||||||
|
# Tools sicherstellen
|
||||||
|
# Tools sicherstellen – Package-Name und CLI-Binary ggf. unterschiedlich
|
||||||
|
html_minifier_path = ensure_node_tool("html-minifier")
|
||||||
|
terser_path = ensure_node_tool("terser")
|
||||||
|
cssnano_path = ensure_node_tool("cssnano-cli", "cssnano")
|
||||||
|
|
||||||
def minify_html(input_path, output_path):
|
def minify_html(input_path, output_path):
|
||||||
subprocess.run([html_minifier_path, '--collapse-whitespace', '--remove-comments', input_path, '-o', output_path])
|
subprocess.run([html_minifier_path, '--collapse-whitespace', '--remove-comments', input_path, '-o', output_path])
|
||||||
@@ -40,13 +58,7 @@ def minify_css(input_path, output_path):
|
|||||||
|
|
||||||
def process_file(src_path, dest_path):
|
def process_file(src_path, dest_path):
|
||||||
_, file_extension = os.path.splitext(src_path)
|
_, file_extension = os.path.splitext(src_path)
|
||||||
|
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
|
||||||
# Extrahiere den Ordnerpfad im Zielverzeichnis
|
|
||||||
dest_dir = os.path.dirname(dest_path)
|
|
||||||
|
|
||||||
# Erstelle den Ordner und alle dazugehörigen Unterordner, falls sie nicht existieren
|
|
||||||
os.makedirs(dest_dir, exist_ok=True)
|
|
||||||
|
|
||||||
if file_extension.lower() == '.js':
|
if file_extension.lower() == '.js':
|
||||||
minify_js(src_path, dest_path)
|
minify_js(src_path, dest_path)
|
||||||
elif file_extension.lower() == '.css':
|
elif file_extension.lower() == '.css':
|
||||||
@@ -54,128 +66,89 @@ def process_file(src_path, dest_path):
|
|||||||
elif file_extension.lower() in ['.html', '.htm']:
|
elif file_extension.lower() in ['.html', '.htm']:
|
||||||
minify_html(src_path, dest_path)
|
minify_html(src_path, dest_path)
|
||||||
else:
|
else:
|
||||||
# Kopiere nicht bearbeitbare Dateien direkt in den Zielordner
|
|
||||||
shutil.copy2(src_path, dest_path)
|
shutil.copy2(src_path, dest_path)
|
||||||
|
|
||||||
def strip_files(src_dir, dest_dir):
|
def strip_files(src_dir, dest_dir):
|
||||||
# Erstelle den Zielordner und alle dazugehörigen Unterordner, falls sie nicht existieren
|
|
||||||
os.makedirs(dest_dir, exist_ok=True)
|
os.makedirs(dest_dir, exist_ok=True)
|
||||||
|
|
||||||
# Durchlaufe alle Dateien und Unterverzeichnisse im Quellordner
|
|
||||||
for root, _, files in os.walk(src_dir):
|
for root, _, files in os.walk(src_dir):
|
||||||
for filename in files:
|
for filename in files:
|
||||||
src_path = os.path.join(root, filename)
|
src_path = os.path.join(root, filename)
|
||||||
dest_path = os.path.relpath(src_path, src_dir)
|
rel_path = os.path.relpath(src_path, src_dir)
|
||||||
dest_path = os.path.join(dest_dir, dest_path)
|
dest_path = os.path.join(dest_dir, rel_path)
|
||||||
|
|
||||||
# Verarbeite nur Dateien (keine Unterverzeichnisse)
|
|
||||||
process_file(src_path, dest_path)
|
process_file(src_path, dest_path)
|
||||||
|
|
||||||
|
|
||||||
def gzip_file(src_path, dst_path):
|
def gzip_file(src_path, dst_path):
|
||||||
|
|
||||||
with open(src_path, 'rb') as src, gzip.open(dst_path, 'wb') as dst:
|
with open(src_path, 'rb') as src, gzip.open(dst_path, 'wb') as dst:
|
||||||
for chunk in iter(lambda: src.read(4096), b""):
|
for chunk in iter(lambda: src.read(4096), b""):
|
||||||
dst.write(chunk)
|
dst.write(chunk)
|
||||||
|
|
||||||
|
|
||||||
def getListOfFiles(dirName):
|
def getListOfFiles(dirName):
|
||||||
# create a list of file and sub directories
|
entries = os.listdir(dirName)
|
||||||
# names in the given directory
|
allFiles = []
|
||||||
listOfFile = os.listdir(dirName)
|
for entry in entries:
|
||||||
allFiles = list()
|
|
||||||
# Iterate over all the entries
|
|
||||||
for entry in listOfFile:
|
|
||||||
# Create full path
|
|
||||||
fullPath = os.path.join(dirName, entry)
|
fullPath = os.path.join(dirName, entry)
|
||||||
# If entry is a directory then get the list of files in this directory
|
|
||||||
if os.path.isdir(fullPath):
|
if os.path.isdir(fullPath):
|
||||||
allFiles = allFiles + getListOfFiles(fullPath)
|
allFiles += getListOfFiles(fullPath)
|
||||||
else:
|
else:
|
||||||
allFiles.append(fullPath)
|
allFiles.append(fullPath)
|
||||||
|
|
||||||
return allFiles
|
return allFiles
|
||||||
|
|
||||||
def remove_prefix(text, prefix):
|
def safe_relpath(path, start):
|
||||||
if text.startswith(prefix):
|
return os.path.relpath(path, start).replace("\\", "/")
|
||||||
return text[len(prefix):]
|
|
||||||
return text # or whatever
|
|
||||||
|
|
||||||
# Compress files from 'data_src/' to 'data/'
|
|
||||||
|
|
||||||
|
|
||||||
def gzip_webfiles(source, target, env):
|
def gzip_webfiles(source, target, env):
|
||||||
# Filetypes to compress
|
|
||||||
filetypes_to_gzip = ['.css', '.png', '.js', '.ico', '.woff2', '.json']
|
filetypes_to_gzip = ['.css', '.png', '.js', '.ico', '.woff2', '.json']
|
||||||
print('\nGZIP: Starting gzip-Process for LittleFS-Image...\n')
|
print('\nGZIP: Starting gzip-Process for LittleFS-Image...\n')
|
||||||
data_src_dir_path = os.path.join(env.get('PROJECT_DIR'), 'data_src')
|
src_dir = os.path.join(env.get('PROJECT_DIR'), 'data_src')
|
||||||
data_temp_dir_path = os.path.join(env.get('PROJECT_DIR'), 'data_stripped')
|
temp_dir = os.path.join(env.get('PROJECT_DIR'), 'data_stripped')
|
||||||
strip_files(data_src_dir_path, data_temp_dir_path)
|
dst_dir = env.get('PROJECT_DATA_DIR')
|
||||||
data_dir_path = env.get('PROJECT_DATA_DIR')
|
|
||||||
# check if data and datasrc exist. If the first exists and not the second, it renames it
|
strip_files(src_dir, temp_dir)
|
||||||
if(os.path.exists(data_dir_path) and not os.path.exists(data_temp_dir_path)):
|
|
||||||
print('GZIP: Directory "'+data_dir_path +
|
if os.path.exists(dst_dir):
|
||||||
'" exists, "'+data_temp_dir_path+'" is not found.')
|
shutil.rmtree(dst_dir)
|
||||||
print('GZIP: Renaming "' + data_dir_path +
|
os.mkdir(dst_dir)
|
||||||
'" to "' + data_temp_dir_path + '"')
|
|
||||||
os.rename(data_dir_path, data_temp_dir_path)
|
|
||||||
# Delete the 'data' directory
|
|
||||||
if(os.path.exists(data_dir_path)):
|
|
||||||
print('GZIP: Deleting the "data" directory ' + data_dir_path)
|
|
||||||
shutil.rmtree(data_dir_path)
|
|
||||||
# Recreate empty 'data' directory
|
|
||||||
print('GZIP: Re-creating an empty data directory ' + data_dir_path)
|
|
||||||
os.mkdir(data_dir_path)
|
|
||||||
# Determine the files to compress
|
|
||||||
|
|
||||||
files_to_copy = []
|
files_to_copy = []
|
||||||
files_to_gzip = []
|
files_to_gzip = []
|
||||||
|
|
||||||
all_data_src = getListOfFiles(data_temp_dir_path)
|
for file in getListOfFiles(temp_dir):
|
||||||
for file in all_data_src:
|
_, ext = os.path.splitext(file)
|
||||||
file_name, file_extension = os.path.splitext(file)
|
if ext in filetypes_to_gzip:
|
||||||
print(file_name + " has filetype " + file_extension)
|
|
||||||
if file_extension in filetypes_to_gzip:
|
|
||||||
files_to_gzip.append(file)
|
files_to_gzip.append(file)
|
||||||
else:
|
else:
|
||||||
filename_subdir = remove_prefix(file, data_temp_dir_path)
|
files_to_copy.append(safe_relpath(file, temp_dir))
|
||||||
files_to_copy.append(filename_subdir)
|
|
||||||
|
|
||||||
for file in files_to_copy:
|
for file in files_to_copy:
|
||||||
print('GZIP: Copying file from: ' + data_temp_dir_path + file + ' to: ' + data_dir_path + file)
|
full_dst = os.path.join(dst_dir, file)
|
||||||
os.makedirs(os.path.dirname(data_dir_path + file), exist_ok=True)
|
os.makedirs(os.path.dirname(full_dst), exist_ok=True)
|
||||||
shutil.copy(data_temp_dir_path + file, data_dir_path + file)
|
shutil.copy(os.path.join(temp_dir, file), full_dst)
|
||||||
# Compress and move files
|
|
||||||
|
|
||||||
was_error = False
|
was_error = False
|
||||||
try:
|
try:
|
||||||
for source_file_path in files_to_gzip:
|
for src in files_to_gzip:
|
||||||
print('GZIP: compressing... ' + source_file_path)
|
rel_path = safe_relpath(src, temp_dir)
|
||||||
filename_subdir = remove_prefix(source_file_path, data_temp_dir_path)
|
dst_path = os.path.join(dst_dir, rel_path + '.gz')
|
||||||
target_file_path = data_dir_path + filename_subdir
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||||
os.makedirs(os.path.dirname(target_file_path), exist_ok=True)
|
print('GZIP: compressing... ' + rel_path)
|
||||||
print('GZIP: Compressed... ' + target_file_path)
|
gzip_file(src, dst_path)
|
||||||
gzip_file(source_file_path, target_file_path + ".gz")
|
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
was_error = True
|
was_error = True
|
||||||
print('GZIP: Failed to compress file: ' + source_file_path)
|
print('GZIP: Fehler beim Komprimieren:', e)
|
||||||
# print( 'GZIP: EXCEPTION... {}'.format( e ) )
|
|
||||||
if was_error:
|
if was_error:
|
||||||
print('GZIP: Failure/Incomplete.\n')
|
print('⚠️ GZIP: Nicht alle Dateien konnten verarbeitet werden.\n')
|
||||||
else:
|
else:
|
||||||
print('GZIP: Compressed correctly.\n')
|
print('✅ GZIP: Komprimierung abgeschlossen.\n')
|
||||||
shutil.rmtree(data_temp_dir_path)
|
shutil.rmtree(temp_dir)
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
def gzip_binffiles(source, target, env):
|
def gzip_binffiles(source, target, env):
|
||||||
littlefsbin = target[0].get_abspath()
|
littlefsbin = target[0].get_abspath()
|
||||||
targetbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs')
|
tmpbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs')
|
||||||
shutil.copyfile(littlefsbin, targetbin)
|
shutil.copyfile(littlefsbin, tmpbin)
|
||||||
gzip_file(targetbin, os.path.join(str(targetbin) + '.gz'))
|
gzip_file(tmpbin, tmpbin + '.gz')
|
||||||
os.remove(targetbin)
|
os.remove(tmpbin)
|
||||||
return
|
|
||||||
|
|
||||||
# IMPORTANT, this needs to be added to call the routine
|
# Hooks setzen
|
||||||
env.AddPreAction('$BUILD_DIR/littlefs.bin', gzip_webfiles)
|
env.AddPreAction('$BUILD_DIR/littlefs.bin', gzip_webfiles)
|
||||||
env.AddPostAction('$BUILD_DIR/littlefs.bin', gzip_binffiles)
|
env.AddPostAction('$BUILD_DIR/littlefs.bin', gzip_binffiles)
|
||||||
|
@@ -349,6 +349,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="washdistance" class="control-label col-4">Waschmodus Distanz</label>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="washdistance" type="text"
|
||||||
|
class="set-wsevent data-washdistance form-control" required="required">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">m</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label for="washinterval" class="control-label col-4">Waschmodus Interval</label>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="washinterval" type="text"
|
||||||
|
class="set-wsevent data-washinterval form-control" required="required">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<span class="input-group-text">m</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<!-- Div Group Lube Settings-->
|
<!-- Div Group Lube Settings-->
|
||||||
<!-- Div Group Oiltank Settings -->
|
<!-- Div Group Oiltank Settings -->
|
||||||
|
18
Software/include/button_actions.h
Normal file
18
Software/include/button_actions.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// === button_actions.h ===
|
||||||
|
#ifndef _BUTTON_ACTIONS_H_
|
||||||
|
#define _BUTTON_ACTIONS_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "buttoncontrol.h"
|
||||||
|
|
||||||
|
// Deklarationen der Button-Callbacks
|
||||||
|
void ButtonAction_ToggleMode();
|
||||||
|
void ButtonAction_StartPurge();
|
||||||
|
void ButtonAction_ToggleWiFi();
|
||||||
|
void ButtonAction_WashMode();
|
||||||
|
|
||||||
|
// Bereitstellung der Aktionsliste
|
||||||
|
extern const ButtonActionEntry buttonActions[];
|
||||||
|
extern const uint8_t buttonActionCount;
|
||||||
|
|
||||||
|
#endif
|
30
Software/include/buttoncontrol.h
Normal file
30
Software/include/buttoncontrol.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// === buttoncontrol.h ===
|
||||||
|
#ifndef _BUTTONCONTROL_H_
|
||||||
|
#define _BUTTONCONTROL_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// Aktionen, die vom Button ausgelöst werden können
|
||||||
|
enum ButtonAction
|
||||||
|
{
|
||||||
|
BTN_NONE,
|
||||||
|
BTN_CUSTOM
|
||||||
|
};
|
||||||
|
|
||||||
|
// Callback-Funktionstyp
|
||||||
|
typedef void (*ButtonCallback)();
|
||||||
|
|
||||||
|
struct ButtonActionEntry
|
||||||
|
{
|
||||||
|
uint32_t holdTimeMs;
|
||||||
|
uint32_t ledColor;
|
||||||
|
ButtonCallback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialisierung des Buttonmoduls
|
||||||
|
void ButtonControl_Init(uint8_t pin, const ButtonActionEntry *actions, uint8_t actionCount);
|
||||||
|
|
||||||
|
// Muss regelmäßig in loop() aufgerufen werden
|
||||||
|
void ButtonControl_Update();
|
||||||
|
|
||||||
|
#endif
|
@@ -41,9 +41,10 @@
|
|||||||
#elif PCB_REV == 4
|
#elif PCB_REV == 4
|
||||||
#define GPIO_BUTTON D4
|
#define GPIO_BUTTON D4
|
||||||
#define GPIO_LED D3
|
#define GPIO_LED D3
|
||||||
#define GPIO_TRIGGER D6
|
#define GPIO_TRIGGER D8
|
||||||
#define GPIO_PUMP D0
|
#define GPIO_PUMP D0
|
||||||
#define GPIO_CS_CAN D8
|
#define GPIO_CS_CAN D8
|
||||||
|
#define GPIO_CE_KLINE D8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef HOST_NAME
|
#ifndef HOST_NAME
|
||||||
@@ -72,6 +73,7 @@ typedef enum eSystem_Status
|
|||||||
sysStat_Startup,
|
sysStat_Startup,
|
||||||
sysStat_Normal,
|
sysStat_Normal,
|
||||||
sysStat_Rain,
|
sysStat_Rain,
|
||||||
|
sysStat_Wash,
|
||||||
sysStat_Purge,
|
sysStat_Purge,
|
||||||
sysStat_Error,
|
sysStat_Error,
|
||||||
sysStat_Shutdown
|
sysStat_Shutdown
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#include "dtc.h"
|
#include "dtc.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#define EEPROM_STRUCTURE_REVISION 3 // Increment this version when changing EEPROM structures
|
#define EEPROM_STRUCTURE_REVISION 4 // Increment this version when changing EEPROM structures
|
||||||
|
|
||||||
#if PCB_REV == 1 || PCB_REV == 2 || PCB_REV == 3
|
#if PCB_REV == 1 || PCB_REV == 2 || PCB_REV == 3
|
||||||
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC64
|
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC64
|
||||||
@@ -69,6 +69,8 @@ typedef struct
|
|||||||
uint32_t RimDiameter_Inch;
|
uint32_t RimDiameter_Inch;
|
||||||
uint32_t DistancePerRevolution_mm;
|
uint32_t DistancePerRevolution_mm;
|
||||||
uint16_t BleedingPulses;
|
uint16_t BleedingPulses;
|
||||||
|
uint16_t WashMode_Distance;
|
||||||
|
uint16_t WashMode_Interval;
|
||||||
SpeedSource_t SpeedSource;
|
SpeedSource_t SpeedSource;
|
||||||
GPSBaudRate_t GPSBaudRate;
|
GPSBaudRate_t GPSBaudRate;
|
||||||
CANSource_t CANSource;
|
CANSource_t CANSource;
|
||||||
@@ -85,7 +87,7 @@ typedef struct
|
|||||||
|
|
||||||
// Default configuration settings
|
// Default configuration settings
|
||||||
const LubeConfig_t LubeConfig_defaults = {
|
const LubeConfig_t LubeConfig_defaults = {
|
||||||
0, 8000, 4000, 320, DEFAULT_PUMP_DOSE, 30, 1, 150, 70, 18, 2000, 25, SOURCE_IMPULSE,
|
0, 8000, 4000, 320, DEFAULT_PUMP_DOSE, 30, 1, 150, 70, 18, 2000, 25, 500, 10, SOURCE_IMPULSE,
|
||||||
BAUD_115200,
|
BAUD_115200,
|
||||||
KTM_890_ADV_R_2021,
|
KTM_890_ADV_R_2021,
|
||||||
false,
|
false,
|
||||||
|
@@ -16,19 +16,6 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "webui.h"
|
#include "webui.h"
|
||||||
const char PROGMEM helpCmd[] = "sysinfo - System Info\n"
|
|
||||||
"netinfo - WiFi Info\n"
|
|
||||||
"formatPDS - Format Persistence EEPROM Data\n"
|
|
||||||
"formatCFG - Format Configuration EEPROM Data\n"
|
|
||||||
"checkEE - Check EEPROM with checksum\n"
|
|
||||||
"dumpEE1k - dump the first 1kb of EEPROM to Serial\n"
|
|
||||||
"dumpEE - dump the whole EPPROM to Serial\n"
|
|
||||||
"resetPageEE - Reset the PersistenceData Page\n"
|
|
||||||
"dumpCFG - print Config struct\n"
|
|
||||||
"dumpPDS - print PersistanceStruct\n"
|
|
||||||
"saveEE - save EE-Data\n"
|
|
||||||
"showdtc - Show all DTCs\n"
|
|
||||||
"dumpGlobals - print globals\n";
|
|
||||||
|
|
||||||
typedef enum DebugStatus_e
|
typedef enum DebugStatus_e
|
||||||
{
|
{
|
||||||
@@ -49,6 +36,13 @@ const char sDebugPorts[dbg_cntElements][7] = {
|
|||||||
|
|
||||||
extern DebugStatus_t DebuggerStatus[dbg_cntElements];
|
extern DebugStatus_t DebuggerStatus[dbg_cntElements];
|
||||||
|
|
||||||
|
enum LogLevel
|
||||||
|
{
|
||||||
|
LOG_INFO,
|
||||||
|
LOG_WARN,
|
||||||
|
LOG_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
void initDebugger();
|
void initDebugger();
|
||||||
void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data);
|
void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data);
|
||||||
void Debug_pushMessage(const char *format, ...);
|
void Debug_pushMessage(const char *format, ...);
|
||||||
|
@@ -24,7 +24,7 @@ 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 */
|
||||||
uint16_t purgePulses = 0; /**< Number of purge pulses */
|
uint16_t purgePulses = 0; /**< Number of purge pulses */
|
||||||
EERequest_t requestEEAction = EE_IDLE;; /**< EEPROM-related request */
|
EERequest_t requestEEAction = EE_IDLE; /**< EEPROM-related request */
|
||||||
char DeviceName[33]; /**< Device name */
|
char DeviceName[33]; /**< Device name */
|
||||||
char FlashVersion[10]; /**< Flash version */
|
char FlashVersion[10]; /**< Flash version */
|
||||||
uint16_t eePersistanceAdress; /**< EEPROM persistence address */
|
uint16_t eePersistanceAdress; /**< EEPROM persistence address */
|
||||||
@@ -33,6 +33,7 @@ typedef struct Globals_s
|
|||||||
bool measurementActive; /**< Flag indicating active measurement */
|
bool measurementActive; /**< Flag indicating active measurement */
|
||||||
uint32_t measuredPulses; /**< Number of measured pulses */
|
uint32_t measuredPulses; /**< Number of measured pulses */
|
||||||
bool toggle_wifi;
|
bool toggle_wifi;
|
||||||
|
uint16_t isr_debug;
|
||||||
} Globals_t;
|
} Globals_t;
|
||||||
|
|
||||||
extern Globals_t globals; /**< Global variable struct */
|
extern Globals_t globals; /**< Global variable struct */
|
||||||
|
@@ -37,9 +37,10 @@
|
|||||||
#define LED_STARTUP_TANKWARN COLOR_AMBER
|
#define LED_STARTUP_TANKWARN COLOR_AMBER
|
||||||
#define LED_NORMAL_COLOR COLOR_GREEN
|
#define LED_NORMAL_COLOR COLOR_GREEN
|
||||||
#define LED_RAIN_COLOR COLOR_BLUE
|
#define LED_RAIN_COLOR COLOR_BLUE
|
||||||
#define LED_WIFI_BLINK COLOR_YELLOW
|
#define LED_WASH_COLOR COLOR_JADE
|
||||||
|
#define LED_WIFI_COLOR COLOR_YELLOW
|
||||||
#define LED_PURGE_COLOR COLOR_MAGENTA
|
#define LED_PURGE_COLOR COLOR_MAGENTA
|
||||||
#define LED_ERROR_BLINK COLOR_RED
|
#define LED_ERROR_COLOR COLOR_RED
|
||||||
#define LED_SHUTDOWN_BLINK COLOR_CYAN
|
#define LED_SHUTDOWN_COLOR COLOR_CYAN
|
||||||
|
|
||||||
#endif /* _LED_COLORS_H_ */
|
#endif /* _LED_COLORS_H_ */
|
||||||
|
35
Software/include/ledcontrol.h
Normal file
35
Software/include/ledcontrol.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// === ledcontrol.h ===
|
||||||
|
#ifndef _LEDCONTROL_H_
|
||||||
|
#define _LEDCONTROL_H_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "led_colors.h"
|
||||||
|
|
||||||
|
// LED-Muster
|
||||||
|
enum LedPattern
|
||||||
|
{
|
||||||
|
LED_PATTERN_ON,
|
||||||
|
LED_PATTERN_FLASH,
|
||||||
|
LED_PATTERN_FLASH_FAST,
|
||||||
|
LED_PATTERN_BLINK,
|
||||||
|
LED_PATTERN_BLINK_FAST,
|
||||||
|
LED_PATTERN_BREATH,
|
||||||
|
LED_PATTERN_BREATH_REVERSE
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialisiert die LED-Steuerung
|
||||||
|
void LEDControl_Init(uint8_t pin);
|
||||||
|
|
||||||
|
// Setzt den Basiszustand (Farbe + Pattern), wird verwendet wenn kein Override aktiv ist
|
||||||
|
void LEDControl_SetBasic(uint32_t color, LedPattern pattern);
|
||||||
|
|
||||||
|
// Setzt ein Override mit Timeout (0 = bis explizit gecleart)
|
||||||
|
void LEDControl_SetOverride(uint32_t color, LedPattern pattern, uint32_t durationMs);
|
||||||
|
|
||||||
|
// Hebt das Override wieder auf
|
||||||
|
void LEDControl_ClearOverride();
|
||||||
|
|
||||||
|
// Muss regelmäßig aus loop() aufgerufen werden
|
||||||
|
void LEDControl_Update();
|
||||||
|
|
||||||
|
#endif
|
@@ -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-01-30 20:29:34.
|
* @note This file is auto-generated by a script on 2025-06-15 11:37:51.
|
||||||
*
|
*
|
||||||
* @author Marcel Peterkau
|
* @author Marcel Peterkau
|
||||||
* @date 30.01.2024
|
* @date 15.06.2025
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _STRUCT2JSON_H_
|
#ifndef _STRUCT2JSON_H_
|
||||||
@@ -23,4 +23,4 @@ void generateJsonObject_PersistenceData(JsonObject data);
|
|||||||
|
|
||||||
#endif /* _STRUCT2JSON_H_ */
|
#endif /* _STRUCT2JSON_H_ */
|
||||||
|
|
||||||
// CODEGENERATOR_CHECKSUM: 59f35aadffd0bbef253210ea2fbaaf9a515553a2e3cc9bf4cfa2819b63c969ce
|
// CODEGENERATOR_CHECKSUM: 4702cb49ea55617cbb34715164810bb58d3c3f46fb1653b6f47bd4fd9cb0031e
|
@@ -11,7 +11,7 @@
|
|||||||
[platformio]
|
[platformio]
|
||||||
extra_configs =
|
extra_configs =
|
||||||
wifi_credentials.ini
|
wifi_credentials.ini
|
||||||
default_envs = pcb_rev_1-3_serial, pcb_rev_1-3_ota, pcb_rev_1-2_serial, pcb_rev_1-2_ota
|
default_envs = pcb_rev_1-4_serial
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
platform = espressif8266
|
platform = espressif8266
|
||||||
@@ -42,15 +42,36 @@ monitor_speed = 115200
|
|||||||
|
|
||||||
lib_ldf_mode = deep
|
lib_ldf_mode = deep
|
||||||
lib_deps =
|
lib_deps =
|
||||||
olikraus/U8g2 @ ^2.35.9
|
olikraus/U8g2 @ ^2.36.5
|
||||||
adafruit/Adafruit NeoPixel @ ^1.12.0
|
adafruit/Adafruit NeoPixel @ ^1.15.1
|
||||||
sstaub/Ticker @ ^4.4.0
|
sstaub/Ticker @ ^4.4.0
|
||||||
robtillaart/I2C_EEPROM @ ^1.8.2
|
robtillaart/I2C_EEPROM @ ^1.9.2
|
||||||
esphome/ESPAsyncWebServer-esphome @ 3.1.0
|
esphome/ESPAsyncWebServer-esphome @ 3.3.0
|
||||||
bblanchon/ArduinoJson @ ^7.0.1
|
bblanchon/ArduinoJson @ ^7.4.1
|
||||||
coryjfowler/mcp_can @ ^1.5.0
|
coryjfowler/mcp_can @ ^1.5.1
|
||||||
mikalhart/TinyGPSPlus @ ^1.0.3
|
mikalhart/TinyGPSPlus @ ^1.1.0
|
||||||
|
|
||||||
|
[env:pcb_rev_1-4_serial]
|
||||||
|
extends = env
|
||||||
|
custom_pcb_revision = 4
|
||||||
|
upload_protocol = esptool
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DPCB_REV=${this.custom_pcb_revision}
|
||||||
|
board_build.ldscript = eagle.flash.4m1m.ld
|
||||||
|
|
||||||
|
[env:pcb_rev_1-4_ota]
|
||||||
|
extends = env
|
||||||
|
custom_pcb_revision = 4
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = 10.0.1.14
|
||||||
|
upload_flags =
|
||||||
|
--port=8266
|
||||||
|
--auth=${wifi_cred.admin_password}
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DPCB_REV=${this.custom_pcb_revision}
|
||||||
|
board_build.ldscript = eagle.flash.4m1m.ld
|
||||||
|
|
||||||
[env:pcb_rev_1-3_serial]
|
[env:pcb_rev_1-3_serial]
|
||||||
extends = env
|
extends = env
|
||||||
|
46
Software/src/button_actions.cpp
Normal file
46
Software/src/button_actions.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// === button_actions.cpp ===
|
||||||
|
#include "button_actions.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "debugger.h"
|
||||||
|
#include "led_colors.h"
|
||||||
|
|
||||||
|
void ButtonAction_ToggleMode()
|
||||||
|
{
|
||||||
|
if (globals.systemStatus == sysStat_Normal)
|
||||||
|
{
|
||||||
|
globals.systemStatus = sysStat_Rain;
|
||||||
|
}
|
||||||
|
else if (globals.systemStatus == sysStat_Rain)
|
||||||
|
{
|
||||||
|
globals.systemStatus = sysStat_Normal;
|
||||||
|
}
|
||||||
|
Debug_pushMessage("Toggling Mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonAction_StartPurge()
|
||||||
|
{
|
||||||
|
globals.systemStatus = sysStat_Purge;
|
||||||
|
Debug_pushMessage("Starting Purge\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonAction_ToggleWiFi()
|
||||||
|
{
|
||||||
|
globals.toggle_wifi = true;
|
||||||
|
Debug_pushMessage("Toggling WiFi\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonAction_WashMode()
|
||||||
|
{
|
||||||
|
globals.systemStatus = sysStat_Wash;
|
||||||
|
Debug_pushMessage("Setting WashMode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liste der Aktionen, sortiert nach Mindest-Haltezeit (ms)
|
||||||
|
const ButtonActionEntry buttonActions[] = {
|
||||||
|
{500, LED_RAIN_COLOR, ButtonAction_ToggleMode},
|
||||||
|
{3500, LED_WASH_COLOR, ButtonAction_WashMode},
|
||||||
|
{6500, LED_PURGE_COLOR, ButtonAction_StartPurge},
|
||||||
|
{9500, LED_WIFI_COLOR, ButtonAction_ToggleWiFi},
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint8_t buttonActionCount = sizeof(buttonActions) / sizeof(ButtonActionEntry);
|
64
Software/src/buttoncontrol.cpp
Normal file
64
Software/src/buttoncontrol.cpp
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
// === buttoncontrol.cpp ===
|
||||||
|
#include "buttoncontrol.h"
|
||||||
|
#include "ledcontrol.h"
|
||||||
|
|
||||||
|
static uint8_t btnPin;
|
||||||
|
static uint32_t pressStart = 0;
|
||||||
|
static bool pressed = false;
|
||||||
|
static const ButtonActionEntry *btnActions = nullptr;
|
||||||
|
static uint8_t btnActionCount = 0;
|
||||||
|
static uint8_t currentActionIndex = 0xFF;
|
||||||
|
static uint32_t lastColor = 0;
|
||||||
|
|
||||||
|
void ButtonControl_Init(uint8_t pin, const ButtonActionEntry *actions, uint8_t actionCount)
|
||||||
|
{
|
||||||
|
btnPin = pin;
|
||||||
|
pinMode(btnPin, INPUT_PULLUP);
|
||||||
|
btnActions = actions;
|
||||||
|
btnActionCount = actionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonControl_Update()
|
||||||
|
{
|
||||||
|
bool currentState = digitalRead(btnPin) == LOW;
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
if (currentState && !pressed)
|
||||||
|
{
|
||||||
|
pressStart = now;
|
||||||
|
pressed = true;
|
||||||
|
currentActionIndex = 0xFF;
|
||||||
|
lastColor = 0;
|
||||||
|
}
|
||||||
|
else if (currentState && pressed)
|
||||||
|
{
|
||||||
|
uint32_t duration = now - pressStart;
|
||||||
|
// Finde passende Aktion basierend auf Zeit
|
||||||
|
for (uint8_t i = 0; i < btnActionCount; i++)
|
||||||
|
{
|
||||||
|
if (duration >= btnActions[i].holdTimeMs)
|
||||||
|
{
|
||||||
|
if (currentActionIndex != i)
|
||||||
|
{
|
||||||
|
currentActionIndex = i;
|
||||||
|
lastColor = btnActions[i].ledColor;
|
||||||
|
// Farbe + Pattern setzen
|
||||||
|
LEDControl_SetOverride(lastColor, LED_PATTERN_BREATH, 0); // Kein Timeout, wird bei Release beendet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!currentState && pressed)
|
||||||
|
{
|
||||||
|
pressed = false;
|
||||||
|
if (currentActionIndex != 0xFF && currentActionIndex < btnActionCount)
|
||||||
|
{
|
||||||
|
if (btnActions[currentActionIndex].callback)
|
||||||
|
{
|
||||||
|
btnActions[currentActionIndex].callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LEDControl_ClearOverride(); // Override-Modus zurücksetzen
|
||||||
|
}
|
||||||
|
}
|
@@ -12,8 +12,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "debugger.h"
|
#include "debugger.h"
|
||||||
|
#include <map>
|
||||||
DebugStatus_t DebuggerStatus[dbg_cntElements];
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
void processCmdDebug(String command);
|
void processCmdDebug(String command);
|
||||||
void Debug_formatCFG();
|
void Debug_formatCFG();
|
||||||
@@ -27,7 +29,11 @@ void Debug_ShowDTCs();
|
|||||||
void Debug_dumpGlobals();
|
void Debug_dumpGlobals();
|
||||||
void Debug_printHelp();
|
void Debug_printHelp();
|
||||||
void Debug_Purge();
|
void Debug_Purge();
|
||||||
|
void Debug_refillTank();
|
||||||
const char *uint32_to_binary_string(uint32_t num);
|
const char *uint32_to_binary_string(uint32_t num);
|
||||||
|
template<typename T>
|
||||||
|
void RegisterDebugPrintAuto(const T* ptr, const String& name, uint32_t interval_ms, uint32_t duration_ms);
|
||||||
|
void Debug_UpdateWatches();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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.
|
||||||
@@ -94,10 +100,10 @@ void Debug_Process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for input buffer overflow
|
// Check for input buffer overflow
|
||||||
if (inputCnt > sizeof(inputBuffer))
|
if (inputCnt >= sizeof(inputBuffer) - 1)
|
||||||
{
|
{
|
||||||
|
inputBuffer[sizeof(inputBuffer) - 1] = '\0';
|
||||||
inputCnt = 0;
|
inputCnt = 0;
|
||||||
inputBuffer[sizeof(inputBuffer) - 1] = 0; // terminate the String
|
|
||||||
InputProcessed = CMD_OVERFLOW;
|
InputProcessed = CMD_OVERFLOW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +131,11 @@ void Debug_Process()
|
|||||||
Serial.print(">");
|
Serial.print(">");
|
||||||
|
|
||||||
InputProcessed = IDLE;
|
InputProcessed = IDLE;
|
||||||
|
|
||||||
|
Debug_UpdateWatches();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the status of a specific debug port (Serial or WebUI).
|
* @brief Sets the status of a specific debug port (Serial or WebUI).
|
||||||
* Updates the status in the DebuggerStatus array and provides debug messages.
|
* Updates the status in the DebuggerStatus array and provides debug messages.
|
||||||
@@ -147,6 +157,27 @@ void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status)
|
|||||||
Debug_pushMessage("Enabled DebugPort %s\n", sDebugPorts[port]);
|
Debug_pushMessage("Enabled DebugPort %s\n", sDebugPorts[port]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug_log(LogLevel level, const char *format, ...)
|
||||||
|
{
|
||||||
|
if ((DebuggerStatus[dbg_Serial] == enabled) || (DebuggerStatus[dbg_Webui] == enabled))
|
||||||
|
{
|
||||||
|
char buff[128];
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
vsnprintf(buff, sizeof(buff), format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (DebuggerStatus[dbg_Serial] == enabled)
|
||||||
|
{
|
||||||
|
Serial.print(buff);
|
||||||
|
}
|
||||||
|
if (DebuggerStatus[dbg_Webui] == enabled)
|
||||||
|
{
|
||||||
|
Websocket_PushLiveDebug(String(buff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Pushes a formatted debug message to the enabled debug ports (Serial or WebUI).
|
* @brief Pushes a formatted debug message to the enabled debug ports (Serial or WebUI).
|
||||||
*
|
*
|
||||||
@@ -218,68 +249,137 @@ void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// === splitArgs Helper ===
|
||||||
* @brief Processes a debug command and performs corresponding actions.
|
std::vector<String> splitArgs(const String &input)
|
||||||
*
|
|
||||||
* @param command The debug command to be processed.
|
|
||||||
*/
|
|
||||||
void processCmdDebug(String command)
|
|
||||||
{
|
{
|
||||||
// Check the received command and execute corresponding actions
|
std::vector<String> tokens;
|
||||||
if (command == "help")
|
size_t start = 0;
|
||||||
Debug_printHelp();
|
|
||||||
else if (command == "sysinfo")
|
while (true)
|
||||||
Debug_printSystemInfo();
|
{
|
||||||
else if (command == "netinfo")
|
int end = input.indexOf(' ', start);
|
||||||
Debug_printWifiInfo();
|
if (end == -1) break;
|
||||||
else if (command == "formatCFG")
|
tokens.push_back(input.substring(start, end));
|
||||||
Debug_formatCFG();
|
start = static_cast<size_t>(end) + 1;
|
||||||
else if (command == "formatPDS")
|
}
|
||||||
Debug_formatPersistence();
|
|
||||||
else if (command == "checkEE")
|
if (start < input.length())
|
||||||
Debug_CheckEEPOM(false);
|
tokens.push_back(input.substring(start));
|
||||||
else if (command == "checkEEfix")
|
return tokens;
|
||||||
Debug_CheckEEPOM(true);
|
}
|
||||||
else if (command == "dumpEE1k")
|
|
||||||
dumpEEPROM(0, 1024);
|
|
||||||
else if (command == "dumpEE")
|
// === getArg helper ===
|
||||||
dumpEEPROM(0, EEPROM_SIZE_BYTES);
|
String getArg(const std::vector<String> &args, size_t index, const String &defaultVal = "")
|
||||||
else if (command == "resetPageEE")
|
{
|
||||||
MovePersistencePage_EEPROM(true);
|
if (index < args.size())
|
||||||
else if (command == "dumpCFG")
|
return args[index];
|
||||||
Debug_dumpConfig();
|
return defaultVal;
|
||||||
else if (command == "dumpPDS")
|
}
|
||||||
Debug_dumpPersistance();
|
|
||||||
else if (command == "saveEE")
|
// === Command Handler Map ===
|
||||||
globals.requestEEAction = EE_ALL_SAVE;
|
typedef std::function<void(const String &args)> DebugCmdHandler;
|
||||||
else if (command == "dumpGlobals")
|
|
||||||
Debug_dumpGlobals();
|
static const std::map<String, DebugCmdHandler> &getCmdMap()
|
||||||
else if (command == "sdbg")
|
{
|
||||||
SetDebugportStatus(dbg_Serial, enabled);
|
static const std::map<String, DebugCmdHandler> cmdMap = {
|
||||||
else if (command == "dtc_show")
|
{"help", [](const String &)
|
||||||
Debug_ShowDTCs();
|
{
|
||||||
else if (command == "dtc_clear")
|
Debug_log(LOG_INFO, "Available commands:\n");
|
||||||
ClearAllDTC();
|
for (const auto &entry : getCmdMap())
|
||||||
else if (command == "dtc_crit")
|
Debug_log(LOG_INFO, " - %s\n", entry.first.c_str());
|
||||||
MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis());
|
}},
|
||||||
else if (command == "dtc_warn")
|
{"sysinfo", [](const String &)
|
||||||
MaintainDTC(DTC_FAKE_DTC_WARN, true, millis());
|
{ Debug_printSystemInfo(); }},
|
||||||
else if (command == "dtc_info")
|
{"netinfo", [](const String &)
|
||||||
MaintainDTC(DTC_FAKE_DTC_INFO, true, millis());
|
{ Debug_printWifiInfo(); }},
|
||||||
else if (command == "notify_error")
|
{"formatCFG", [](const String &)
|
||||||
Websocket_PushNotification("Debug Error Notification", error);
|
{ Debug_formatCFG(); }},
|
||||||
else if (command == "notify_warning")
|
{"formatPDS", [](const String &)
|
||||||
Websocket_PushNotification("Debug Warning Notification", warning);
|
{ Debug_formatPersistence(); }},
|
||||||
else if (command == "notify_success")
|
{"checkEE", [](const String &)
|
||||||
Websocket_PushNotification("Debug Success Notification", success);
|
{ Debug_CheckEEPOM(false); }},
|
||||||
else if (command == "notify_info")
|
{"checkEEfix", [](const String &)
|
||||||
Websocket_PushNotification("Debug Info Notification", info);
|
{ Debug_CheckEEPOM(true); }},
|
||||||
else if (command == "purge")
|
{"dumpEE1k", [](const String &)
|
||||||
Debug_Purge();
|
{ dumpEEPROM(0, 1024); }},
|
||||||
else if (command == "toggle_wifi")
|
{"dumpEE", [](const String &args)
|
||||||
globals.toggle_wifi = true;
|
{
|
||||||
|
int start = 0, len = EEPROM_SIZE_BYTES;
|
||||||
|
auto tokens = splitArgs(args);
|
||||||
|
if (tokens.size() >= 2)
|
||||||
|
{
|
||||||
|
start = tokens[0].toInt();
|
||||||
|
len = tokens[1].toInt();
|
||||||
|
}
|
||||||
|
dumpEEPROM(start, len);
|
||||||
|
}},
|
||||||
|
{"resetPageEE", [](const String &)
|
||||||
|
{ MovePersistencePage_EEPROM(true); }},
|
||||||
|
{"dumpCFG", [](const String &)
|
||||||
|
{ Debug_dumpConfig(); }},
|
||||||
|
{"dumpPDS", [](const String &)
|
||||||
|
{ Debug_dumpPersistance(); }},
|
||||||
|
{"saveEE", [](const String &)
|
||||||
|
{ globals.requestEEAction = EE_ALL_SAVE; }},
|
||||||
|
{"dumpGlobals", [](const String &)
|
||||||
|
{ Debug_dumpGlobals(); }},
|
||||||
|
{"sdbg", [](const String &)
|
||||||
|
{ SetDebugportStatus(dbg_Serial, enabled); }},
|
||||||
|
{"dtc_show", [](const String &)
|
||||||
|
{ Debug_ShowDTCs(); }},
|
||||||
|
{"dtc_clear", [](const String &)
|
||||||
|
{ ClearAllDTC(); }},
|
||||||
|
{"dtc_crit", [](const String &)
|
||||||
|
{ MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis()); }},
|
||||||
|
{"dtc_warn", [](const String &)
|
||||||
|
{ MaintainDTC(DTC_FAKE_DTC_WARN, true, millis()); }},
|
||||||
|
{"dtc_info", [](const String &)
|
||||||
|
{ MaintainDTC(DTC_FAKE_DTC_INFO, true, millis()); }},
|
||||||
|
{"notify_error", [](const String &)
|
||||||
|
{ Websocket_PushNotification("Debug Error Notification", error); }},
|
||||||
|
{"notify_warning", [](const String &)
|
||||||
|
{ Websocket_PushNotification("Debug Warning Notification", warning); }},
|
||||||
|
{"notify_success", [](const String &)
|
||||||
|
{ Websocket_PushNotification("Debug Success Notification", success); }},
|
||||||
|
{"notify_info", [](const String &)
|
||||||
|
{ Websocket_PushNotification("Debug Info Notification", info); }},
|
||||||
|
{"purge", [](const String &)
|
||||||
|
{ Debug_Purge(); }},
|
||||||
|
{"toggle_wifi", [](const String &)
|
||||||
|
{ globals.toggle_wifi = true; }},
|
||||||
|
{"dtc_add", [](const String &args)
|
||||||
|
{
|
||||||
|
auto tokens = splitArgs(args);
|
||||||
|
if (!tokens.empty())
|
||||||
|
{
|
||||||
|
int code = tokens[0].toInt();
|
||||||
|
MaintainDTC((DTCNum_t)code, true, millis());
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
{"tank_refill", [](const String &)
|
||||||
|
{ Debug_refillTank(); }},
|
||||||
|
{"isr_debug", [](const String &)
|
||||||
|
{
|
||||||
|
RegisterDebugPrintAuto(&globals.isr_debug, "isr_debug", 100, 20000);
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
return cmdMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processCmdDebug(String input)
|
||||||
|
{
|
||||||
|
input.trim();
|
||||||
|
int splitIndex = input.indexOf(' ');
|
||||||
|
String command = splitIndex == -1 ? input : input.substring(0, splitIndex);
|
||||||
|
String args = splitIndex == -1 ? "" : input.substring(splitIndex + 1);
|
||||||
|
|
||||||
|
auto &cmdMap = getCmdMap();
|
||||||
|
auto it = cmdMap.find(command);
|
||||||
|
if (it != cmdMap.end())
|
||||||
|
it->second(args);
|
||||||
else
|
else
|
||||||
Debug_pushMessage("unknown Command\n");
|
Debug_log(LOG_WARN, "Unknown command: '%s'\n", command.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -473,26 +573,6 @@ void Debug_ShowDTCs()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Displays the help commands for debugging through Serial or WebUI.
|
|
||||||
* Each command is printed individually in a formatted manner.
|
|
||||||
*/
|
|
||||||
void Debug_printHelp()
|
|
||||||
{
|
|
||||||
char buff[64];
|
|
||||||
|
|
||||||
// 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
|
|
||||||
memcpy_P(buff, (helpCmd + (i * 63)), 63);
|
|
||||||
buff[63] = 0;
|
|
||||||
|
|
||||||
// Display the help command
|
|
||||||
Debug_pushMessage(buff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start purging for 10 pulses.
|
* @brief Start purging for 10 pulses.
|
||||||
*/
|
*/
|
||||||
@@ -505,6 +585,13 @@ void Debug_Purge()
|
|||||||
Debug_pushMessage("Purging 10 pulses\n");
|
Debug_pushMessage("Purging 10 pulses\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug_refillTank()
|
||||||
|
{
|
||||||
|
PersistenceData.tankRemain_microL = LubeConfig.tankCapacity_ml * 1000;
|
||||||
|
globals.requestEEAction = EE_PDS_SAVE;
|
||||||
|
Debug_pushMessage("Setting Tank to 100%\n");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @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.
|
||||||
*
|
*
|
||||||
@@ -532,4 +619,77 @@ const char *uint32_to_binary_string(uint32_t num)
|
|||||||
}
|
}
|
||||||
binary_str[j] = '\0'; // Null terminator
|
binary_str[j] = '\0'; // Null terminator
|
||||||
return binary_str;
|
return binary_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DebugStatus_t DebuggerStatus[dbg_cntElements];
|
||||||
|
|
||||||
|
struct DebugWatchEntry
|
||||||
|
{
|
||||||
|
const void *ptr;
|
||||||
|
String name;
|
||||||
|
uint32_t interval_ms;
|
||||||
|
uint32_t duration_ms;
|
||||||
|
uint32_t lastPrint_ms;
|
||||||
|
uint32_t start_ms;
|
||||||
|
std::function<void(const void*, const String&)> printer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_DEBUG_WATCHES 8
|
||||||
|
DebugWatchEntry debugWatches[MAX_DEBUG_WATCHES];
|
||||||
|
|
||||||
|
// === Typabhängige Druckfunktion ===
|
||||||
|
template<typename T>
|
||||||
|
void debugPrinterImpl(const void* ptr, const String& name) {
|
||||||
|
const T* typed = static_cast<const T*>(ptr);
|
||||||
|
if constexpr (std::is_same<T, bool>::value) {
|
||||||
|
Debug_pushMessage("%s = %s\n", name.c_str(), *typed ? "true" : "false");
|
||||||
|
} else if constexpr (std::is_floating_point<T>::value) {
|
||||||
|
Debug_pushMessage("%s = %.3f\n", name.c_str(), *typed);
|
||||||
|
} else if constexpr (std::is_signed<T>::value) {
|
||||||
|
Debug_pushMessage("%s = %ld\n", name.c_str(), static_cast<long>(*typed));
|
||||||
|
} else if constexpr (std::is_unsigned<T>::value) {
|
||||||
|
Debug_pushMessage("%s = %lu\n", name.c_str(), static_cast<unsigned long>(*typed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Automatisches DebugPrint-Register ===
|
||||||
|
template<typename T>
|
||||||
|
void RegisterDebugPrintAuto(const T* ptr, const String& name, uint32_t interval_ms, uint32_t duration_ms)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < MAX_DEBUG_WATCHES; ++i) {
|
||||||
|
if (debugWatches[i].ptr == nullptr) {
|
||||||
|
debugWatches[i] = {
|
||||||
|
ptr,
|
||||||
|
name,
|
||||||
|
interval_ms,
|
||||||
|
duration_ms,
|
||||||
|
0,
|
||||||
|
millis(),
|
||||||
|
debugPrinterImpl<T>
|
||||||
|
};
|
||||||
|
Debug_pushMessage("Registered Watch: %s\n", name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug_pushMessage("Debug Watch list full!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug-Ausgabe in Debug_Process():
|
||||||
|
void Debug_UpdateWatches() {
|
||||||
|
uint32_t now = millis();
|
||||||
|
for (int i = 0; i < MAX_DEBUG_WATCHES; ++i) {
|
||||||
|
auto& w = debugWatches[i];
|
||||||
|
if (!w.ptr) continue;
|
||||||
|
|
||||||
|
if (now - w.start_ms >= w.duration_ms) {
|
||||||
|
Debug_pushMessage("Watch expired: %s\n", w.name.c_str());
|
||||||
|
w.ptr = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now - w.lastPrint_ms >= w.interval_ms) {
|
||||||
|
w.lastPrint_ms = now;
|
||||||
|
if (w.printer) w.printer(w.ptr, w.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
131
Software/src/ledcontrol.cpp
Normal file
131
Software/src/ledcontrol.cpp
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
// === ledcontrol.cpp ===
|
||||||
|
#include "ledcontrol.h"
|
||||||
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
static Adafruit_NeoPixel leds(1, GPIO_LED, NEO_RGB + NEO_KHZ800);
|
||||||
|
|
||||||
|
static uint32_t basicColor = 0x000000;
|
||||||
|
static LedPattern basicPattern = LED_PATTERN_ON;
|
||||||
|
|
||||||
|
static uint32_t overrideColor = 0;
|
||||||
|
static LedPattern overridePattern = LED_PATTERN_ON;
|
||||||
|
static uint32_t overrideEndTime = 0;
|
||||||
|
static bool overrideActive = false;
|
||||||
|
|
||||||
|
void LEDControl_Init(uint8_t pin)
|
||||||
|
{
|
||||||
|
leds.begin();
|
||||||
|
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
||||||
|
leds.setPixelColor(0, 0);
|
||||||
|
leds.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDControl_SetBasic(uint32_t color, LedPattern pattern)
|
||||||
|
{
|
||||||
|
basicColor = color;
|
||||||
|
basicPattern = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDControl_SetOverride(uint32_t color, LedPattern pattern, uint32_t durationMs)
|
||||||
|
{
|
||||||
|
overrideColor = color;
|
||||||
|
overridePattern = pattern;
|
||||||
|
overrideEndTime = millis() + durationMs;
|
||||||
|
overrideActive = true;
|
||||||
|
if (durationMs == 0)
|
||||||
|
overrideEndTime = 0xFFFFFFFF; // Kein Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDControl_ClearOverride()
|
||||||
|
{
|
||||||
|
overrideActive = false;
|
||||||
|
overrideEndTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDControl_Update()
|
||||||
|
{
|
||||||
|
uint32_t now = millis();
|
||||||
|
uint32_t color = basicColor;
|
||||||
|
LedPattern pattern = basicPattern;
|
||||||
|
|
||||||
|
// Check override
|
||||||
|
if (overrideActive)
|
||||||
|
{
|
||||||
|
if (overrideEndTime != 0xFFFFFFFF && now >= overrideEndTime)
|
||||||
|
{
|
||||||
|
LEDControl_ClearOverride();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
color = overrideColor;
|
||||||
|
pattern = overridePattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t brightness = LubeConfig.LED_Min_Brightness;
|
||||||
|
bool on = true;
|
||||||
|
|
||||||
|
switch (pattern)
|
||||||
|
{
|
||||||
|
case LED_PATTERN_ON:
|
||||||
|
brightness = LubeConfig.LED_Max_Brightness;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PATTERN_FLASH:
|
||||||
|
on = (now % 1000) < 100;
|
||||||
|
brightness = LubeConfig.LED_Max_Brightness;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PATTERN_FLASH_FAST:
|
||||||
|
on = (now % 500) < 50;
|
||||||
|
brightness = LubeConfig.LED_Max_Brightness;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PATTERN_BLINK:
|
||||||
|
on = (now % 1000) < 500;
|
||||||
|
brightness = on ? LubeConfig.LED_Max_Brightness : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PATTERN_BLINK_FAST:
|
||||||
|
on = (now % 400) < 200;
|
||||||
|
brightness = on ? LubeConfig.LED_Max_Brightness : 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LED_PATTERN_BREATH:
|
||||||
|
{
|
||||||
|
uint32_t t = now % 2000;
|
||||||
|
if (t < 600)
|
||||||
|
{
|
||||||
|
// Schnell hochdimmen (600 ms)
|
||||||
|
brightness = map(t, 0, 600, LubeConfig.LED_Min_Brightness, LubeConfig.LED_Max_Brightness);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Langsam runterdimmen (1400 ms)
|
||||||
|
brightness = map(t, 600, 2000, LubeConfig.LED_Max_Brightness, LubeConfig.LED_Min_Brightness);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LED_PATTERN_BREATH_REVERSE:
|
||||||
|
{
|
||||||
|
uint32_t t = now % 2000;
|
||||||
|
if (t < 1400)
|
||||||
|
{
|
||||||
|
// Langsam hochdimmen (1400 ms)
|
||||||
|
brightness = map(t, 0, 1400, LubeConfig.LED_Min_Brightness, LubeConfig.LED_Max_Brightness);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Schnell runterdimmen (600 ms)
|
||||||
|
brightness = map(t, 1400, 2000, LubeConfig.LED_Max_Brightness, LubeConfig.LED_Min_Brightness);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
leds.setBrightness(brightness);
|
||||||
|
leds.setPixelColor(0, on ? color : 0);
|
||||||
|
leds.show();
|
||||||
|
}
|
@@ -12,6 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lubeapp.h"
|
#include "lubeapp.h"
|
||||||
|
#include "ledcontrol.h"
|
||||||
|
|
||||||
uint32_t lubePulseTimestamp = 0;
|
uint32_t lubePulseTimestamp = 0;
|
||||||
|
|
||||||
@@ -27,6 +28,9 @@ uint32_t lubePulseTimestamp = 0;
|
|||||||
*/
|
*/
|
||||||
void RunLubeApp(uint32_t add_milimeters)
|
void RunLubeApp(uint32_t add_milimeters)
|
||||||
{
|
{
|
||||||
|
static tSystem_Status lastSystemStatus = sysStat_Startup;
|
||||||
|
static uint16_t washModeRemainDistance = 0;
|
||||||
|
|
||||||
// Calculate and update tank percentage
|
// Calculate and update tank percentage
|
||||||
globals.TankPercentage = PersistenceData.tankRemain_microL / (LubeConfig.tankCapacity_ml * 10);
|
globals.TankPercentage = PersistenceData.tankRemain_microL / (LubeConfig.tankCapacity_ml * 10);
|
||||||
|
|
||||||
@@ -42,24 +46,38 @@ void RunLubeApp(uint32_t add_milimeters)
|
|||||||
if (PersistenceData.odometer_mm >= 1000000)
|
if (PersistenceData.odometer_mm >= 1000000)
|
||||||
{
|
{
|
||||||
PersistenceData.odometer++;
|
PersistenceData.odometer++;
|
||||||
PersistenceData.odometer_mm = 0;
|
PersistenceData.odometer_mm -= 1000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle different system statuses
|
// Handle different system statuses
|
||||||
switch (globals.systemStatus)
|
switch (globals.systemStatus)
|
||||||
{
|
{
|
||||||
case sysStat_Startup:
|
case sysStat_Startup:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Startup"));
|
|
||||||
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Startup"));
|
||||||
|
LEDControl_SetBasic(LED_STARTUP_NORMAL, LED_PATTERN_BLINK);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
globals.resumeStatus = sysStat_Startup;
|
||||||
|
}
|
||||||
|
|
||||||
// Transition to Normal status after startup delay
|
// Transition to Normal status after startup delay
|
||||||
if (millis() > STARTUP_DELAY)
|
if (millis() > STARTUP_DELAY)
|
||||||
{
|
{
|
||||||
globals.systemStatus = sysStat_Normal;
|
globals.systemStatus = sysStat_Normal;
|
||||||
globals.resumeStatus = sysStat_Normal;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case sysStat_Normal:
|
case sysStat_Normal:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Normal"));
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Normal"));
|
||||||
|
LEDControl_SetBasic(LED_NORMAL_COLOR, LED_PATTERN_ON);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
globals.resumeStatus = sysStat_Normal;
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger lube pulse if traveled distance exceeds the configured limit
|
// Trigger lube pulse if traveled distance exceeds the configured limit
|
||||||
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Default)
|
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Default)
|
||||||
{
|
{
|
||||||
@@ -69,7 +87,14 @@ void RunLubeApp(uint32_t add_milimeters)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case sysStat_Rain:
|
case sysStat_Rain:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Rain"));
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Rain"));
|
||||||
|
LEDControl_SetBasic(LED_RAIN_COLOR, LED_PATTERN_ON);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
globals.resumeStatus = sysStat_Rain;
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger lube pulse if traveled distance exceeds the configured limit in Rain mode
|
// Trigger lube pulse if traveled distance exceeds the configured limit in Rain mode
|
||||||
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Rain)
|
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Rain)
|
||||||
{
|
{
|
||||||
@@ -78,8 +103,42 @@ void RunLubeApp(uint32_t add_milimeters)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case sysStat_Wash:
|
||||||
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
washModeRemainDistance = LubeConfig.WashMode_Distance;
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Wash"));
|
||||||
|
LEDControl_SetBasic(LED_WASH_COLOR, LED_PATTERN_BREATH);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger lube pulse if traveled distance exceeds the configured Interval in Wash mode
|
||||||
|
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.WashMode_Interval)
|
||||||
|
{
|
||||||
|
LubePulse();
|
||||||
|
PersistenceData.TravelDistance_highRes_mm = 0;
|
||||||
|
|
||||||
|
if (washModeRemainDistance >= LubeConfig.WashMode_Interval)
|
||||||
|
{
|
||||||
|
washModeRemainDistance -= LubeConfig.WashMode_Interval;
|
||||||
|
Debug_pushMessage("Wash Distance remain: %d\n", washModeRemainDistance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
globals.systemStatus = globals.resumeStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case sysStat_Purge:
|
case sysStat_Purge:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Purge"));
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
globals.purgePulses = LubeConfig.BleedingPulses;
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Purge"));
|
||||||
|
LEDControl_SetBasic(LED_PURGE_COLOR, LED_PATTERN_BLINK);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
}
|
||||||
|
|
||||||
// Execute lube pulses during the Purge status
|
// Execute lube pulses during the Purge status
|
||||||
if (globals.purgePulses > 0)
|
if (globals.purgePulses > 0)
|
||||||
{
|
{
|
||||||
@@ -99,12 +158,26 @@ void RunLubeApp(uint32_t add_milimeters)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case sysStat_Error:
|
case sysStat_Error:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Error"));
|
|
||||||
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Error"));
|
||||||
|
LEDControl_SetBasic(LED_ERROR_COLOR, LED_PATTERN_BLINK_FAST);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
}
|
||||||
|
|
||||||
globals.purgePulses = 0;
|
globals.purgePulses = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case sysStat_Shutdown:
|
case sysStat_Shutdown:
|
||||||
strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
|
|
||||||
|
if (lastSystemStatus != globals.systemStatus)
|
||||||
|
{
|
||||||
|
strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
|
||||||
|
LEDControl_SetBasic(LED_SHUTDOWN_COLOR, LED_PATTERN_BREATH_REVERSE);
|
||||||
|
lastSystemStatus = globals.systemStatus;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@@ -39,6 +39,9 @@
|
|||||||
#include "led_colors.h"
|
#include "led_colors.h"
|
||||||
#include "obd2_kline.h"
|
#include "obd2_kline.h"
|
||||||
#include "obd2_can.h"
|
#include "obd2_can.h"
|
||||||
|
#include "buttoncontrol.h"
|
||||||
|
#include "button_actions.h"
|
||||||
|
#include "ledcontrol.h"
|
||||||
|
|
||||||
#ifdef FEATURE_ENABLE_WIFI_CLIENT
|
#ifdef FEATURE_ENABLE_WIFI_CLIENT
|
||||||
#include <ESP8266WiFiMulti.h>
|
#include <ESP8266WiFiMulti.h>
|
||||||
@@ -59,12 +62,10 @@ Adafruit_NeoPixel leds(1, GPIO_LED, NEO_RGB + NEO_KHZ800);
|
|||||||
|
|
||||||
// Function-Prototypes
|
// Function-Prototypes
|
||||||
void IRAM_ATTR trigger_ISR();
|
void IRAM_ATTR trigger_ISR();
|
||||||
void LED_Process(uint8_t override = false, uint32_t setColor = LED_DEFAULT_COLOR);
|
|
||||||
#ifdef FEATURE_ENABLE_OLED
|
#ifdef FEATURE_ENABLE_OLED
|
||||||
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1);
|
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1);
|
||||||
void Display_Process();
|
void Display_Process();
|
||||||
#endif
|
#endif
|
||||||
void Button_Process();
|
|
||||||
void toggleWiFiAP(bool shutdown = false);
|
void toggleWiFiAP(bool shutdown = false);
|
||||||
void SystemShutdown(bool restart = false);
|
void SystemShutdown(bool restart = false);
|
||||||
uint32_t Process_Impulse_WheelSpeed();
|
uint32_t Process_Impulse_WheelSpeed();
|
||||||
@@ -134,7 +135,7 @@ void setup()
|
|||||||
Serial.print("\nEE-Init done");
|
Serial.print("\nEE-Init done");
|
||||||
|
|
||||||
// Initialize LEDs
|
// Initialize LEDs
|
||||||
leds.begin();
|
LEDControl_Init(GPIO_LED);
|
||||||
Serial.print("\nLED-Init done");
|
Serial.print("\nLED-Init done");
|
||||||
|
|
||||||
// Initialize based on the chosen speed source (CAN, GPS, Impulse)
|
// Initialize based on the chosen speed source (CAN, GPS, Impulse)
|
||||||
@@ -175,6 +176,8 @@ void setup()
|
|||||||
pinMode(GPIO_BUTTON, INPUT_PULLUP);
|
pinMode(GPIO_BUTTON, INPUT_PULLUP);
|
||||||
pinMode(GPIO_PUMP, OUTPUT);
|
pinMode(GPIO_PUMP, OUTPUT);
|
||||||
|
|
||||||
|
ButtonControl_Init(GPIO_BUTTON, buttonActions, buttonActionCount);
|
||||||
|
|
||||||
// Set up OTA updates
|
// Set up OTA updates
|
||||||
ArduinoOTA.setPort(8266);
|
ArduinoOTA.setPort(8266);
|
||||||
ArduinoOTA.setHostname(globals.DeviceName);
|
ArduinoOTA.setHostname(globals.DeviceName);
|
||||||
@@ -249,14 +252,15 @@ void loop()
|
|||||||
|
|
||||||
// Process button input, manage LED behavior, perform EEPROM tasks, handle webserver operations,
|
// Process button input, manage LED behavior, perform EEPROM tasks, handle webserver operations,
|
||||||
// process Diagnostic Trouble Codes (DTC), and manage debugging
|
// process Diagnostic Trouble Codes (DTC), and manage debugging
|
||||||
Button_Process();
|
ButtonControl_Update();
|
||||||
LED_Process();
|
LEDControl_Update();
|
||||||
EEPROM_Process();
|
EEPROM_Process();
|
||||||
Webserver_Process();
|
Webserver_Process();
|
||||||
DTC_Process();
|
DTC_Process();
|
||||||
Debug_Process();
|
Debug_Process();
|
||||||
|
|
||||||
if (globals.toggle_wifi == true){
|
if (globals.toggle_wifi == true)
|
||||||
|
{
|
||||||
|
|
||||||
globals.toggle_wifi = false;
|
globals.toggle_wifi = false;
|
||||||
toggleWiFiAP();
|
toggleWiFiAP();
|
||||||
@@ -335,214 +339,19 @@ void EEPROMCyclicPDS_callback()
|
|||||||
* This ISR is called whenever a pulse is detected from the wheel speed sensor. It increments
|
* This ISR is called whenever a pulse is detected from the wheel speed sensor. It increments
|
||||||
* the `wheel_pulse` variable, which is used to track the number of pulses received.
|
* the `wheel_pulse` variable, which is used to track the number of pulses received.
|
||||||
*/
|
*/
|
||||||
void trigger_ISR()
|
volatile uint32_t lastTriggerMicros = 0;
|
||||||
|
|
||||||
|
void IRAM_ATTR trigger_ISR()
|
||||||
{
|
{
|
||||||
|
uint32_t now = micros();
|
||||||
|
if (now - lastTriggerMicros < 2500)
|
||||||
|
return;
|
||||||
|
lastTriggerMicros = now;
|
||||||
|
|
||||||
|
globals.isr_debug++;
|
||||||
wheel_pulse++;
|
wheel_pulse++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Manages LED behavior based on the current system status and user overrides.
|
|
||||||
*
|
|
||||||
* This function handles LED behavior, including startup animations, confirmation animations for
|
|
||||||
* normal and rain modes, indication for purge, error, shutdown, and normal operation. It supports
|
|
||||||
* user overrides to set a specific LED color. The LED status is determined by the current system
|
|
||||||
* status, and specific LED patterns are displayed accordingly.
|
|
||||||
*
|
|
||||||
* @param override Flag indicating whether to override the LED behavior (0: No override, 1: Override, 2: Resume previous state).
|
|
||||||
* @param SetColor The color to set when overriding the LED behavior.
|
|
||||||
*/
|
|
||||||
void LED_Process(uint8_t override, uint32_t SetColor)
|
|
||||||
{
|
|
||||||
// Enumeration to represent LED status
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
LED_Startup,
|
|
||||||
LED_Normal,
|
|
||||||
LED_Confirm_Normal,
|
|
||||||
LED_Rain,
|
|
||||||
LED_Confirm_Rain,
|
|
||||||
LED_Purge,
|
|
||||||
LED_Error,
|
|
||||||
LED_Shutdown,
|
|
||||||
LED_Override
|
|
||||||
} tLED_Status;
|
|
||||||
|
|
||||||
// Static variables to track LED status, system status, override color, and previous LED status
|
|
||||||
static tSystem_Status oldSysStatus = sysStat_Startup;
|
|
||||||
static tLED_Status LED_Status = LED_Startup;
|
|
||||||
static uint32_t LED_override_color = 0;
|
|
||||||
static tLED_Status LED_ResumeOverrideStatus = LED_Startup;
|
|
||||||
|
|
||||||
// Variables for managing LED animation timing
|
|
||||||
uint8_t color = 0;
|
|
||||||
uint32_t timer = 0;
|
|
||||||
uint32_t animtimer = 0;
|
|
||||||
static uint32_t timestamp = 0;
|
|
||||||
timer = millis();
|
|
||||||
|
|
||||||
// Handle LED overrides
|
|
||||||
if (override == 1)
|
|
||||||
{
|
|
||||||
if (LED_Status != LED_Override)
|
|
||||||
{
|
|
||||||
LED_ResumeOverrideStatus = LED_Status;
|
|
||||||
Debug_pushMessage("Override LED_Status\n");
|
|
||||||
}
|
|
||||||
LED_Status = LED_Override;
|
|
||||||
LED_override_color = SetColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (override == 2)
|
|
||||||
{
|
|
||||||
if (LED_Status == LED_Override)
|
|
||||||
{
|
|
||||||
LED_Status = LED_ResumeOverrideStatus;
|
|
||||||
Debug_pushMessage("Resume LED_Status\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update LED status when system status changes
|
|
||||||
if (oldSysStatus != globals.systemStatus)
|
|
||||||
{
|
|
||||||
switch (globals.systemStatus)
|
|
||||||
{
|
|
||||||
case sysStat_Startup:
|
|
||||||
LED_Status = LED_Startup;
|
|
||||||
Debug_pushMessage("sysStat: Startup\n");
|
|
||||||
break;
|
|
||||||
case sysStat_Normal:
|
|
||||||
timestamp = timer + 3500;
|
|
||||||
LED_Status = LED_Confirm_Normal;
|
|
||||||
Debug_pushMessage("sysStat: Normal\n");
|
|
||||||
break;
|
|
||||||
case sysStat_Rain:
|
|
||||||
timestamp = timer + 3500;
|
|
||||||
LED_Status = LED_Confirm_Rain;
|
|
||||||
Debug_pushMessage("sysStat: Rain\n");
|
|
||||||
break;
|
|
||||||
case sysStat_Purge:
|
|
||||||
LED_Status = LED_Purge;
|
|
||||||
Debug_pushMessage("sysStat: Purge\n");
|
|
||||||
break;
|
|
||||||
case sysStat_Error:
|
|
||||||
LED_Status = LED_Error;
|
|
||||||
Debug_pushMessage("sysStat: Error\n");
|
|
||||||
break;
|
|
||||||
case sysStat_Shutdown:
|
|
||||||
LED_Status = LED_Shutdown;
|
|
||||||
Debug_pushMessage("sysStat: Shutdown\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
oldSysStatus = globals.systemStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle different LED statuses
|
|
||||||
switch (LED_Status)
|
|
||||||
{
|
|
||||||
case LED_Startup:
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
|
||||||
|
|
||||||
if (globals.TankPercentage < LubeConfig.TankRemindAtPercentage)
|
|
||||||
leds.setPixelColor(0, LED_STARTUP_TANKWARN);
|
|
||||||
else
|
|
||||||
leds.setPixelColor(0, LED_STARTUP_NORMAL);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Confirm_Normal:
|
|
||||||
animtimer = timer % 500;
|
|
||||||
color = map(animtimer / 2, 0, 250, 0, LubeConfig.LED_Max_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_NORMAL_COLOR);
|
|
||||||
if (animtimer < 250)
|
|
||||||
leds.setBrightness(color);
|
|
||||||
else
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness - color);
|
|
||||||
|
|
||||||
if (timestamp < timer)
|
|
||||||
{
|
|
||||||
LED_Status = LED_Normal;
|
|
||||||
Debug_pushMessage("LED_Status: Confirm -> Normal\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Normal:
|
|
||||||
leds.setBrightness(LubeConfig.LED_Min_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_NORMAL_COLOR);
|
|
||||||
|
|
||||||
if (timer % 2000 > 1950 && LubeConfig.LED_Mode_Flash == true)
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
|
||||||
else if (timer % 2000 > 1500 && WiFi.getMode() != WIFI_OFF)
|
|
||||||
leds.setPixelColor(0, LED_WIFI_BLINK);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Confirm_Rain:
|
|
||||||
animtimer = timer % 500;
|
|
||||||
color = map(animtimer / 2, 0, 250, 0, LubeConfig.LED_Max_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_RAIN_COLOR);
|
|
||||||
if (animtimer < 250)
|
|
||||||
leds.setBrightness(color);
|
|
||||||
else
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness - color);
|
|
||||||
if (timestamp < timer)
|
|
||||||
{
|
|
||||||
LED_Status = LED_Rain;
|
|
||||||
Debug_pushMessage("LED_Status: Confirm -> Rain\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Rain:
|
|
||||||
leds.setBrightness(LubeConfig.LED_Min_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_RAIN_COLOR);
|
|
||||||
|
|
||||||
if (timer % 2000 > 1950 && LubeConfig.LED_Mode_Flash == true)
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
|
||||||
else if (timer % 2000 > 1500 && WiFi.getMode() != WIFI_OFF)
|
|
||||||
leds.setPixelColor(0, LED_WIFI_BLINK);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Purge:
|
|
||||||
timer = timer % 500;
|
|
||||||
color = map(timer / 2, 0, 250, LubeConfig.LED_Min_Brightness, LubeConfig.LED_Max_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_PURGE_COLOR);
|
|
||||||
if (timer < 250)
|
|
||||||
leds.setBrightness(color);
|
|
||||||
else
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness - color);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Error:
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
|
||||||
leds.setPixelColor(0, timer % 500 > 250 ? LED_ERROR_BLINK : 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Shutdown:
|
|
||||||
timer = timer % 600;
|
|
||||||
leds.setPixelColor(0, LED_SHUTDOWN_BLINK);
|
|
||||||
if (timer < 500)
|
|
||||||
{
|
|
||||||
color = map(timer, 0, 500, LubeConfig.LED_Max_Brightness, LubeConfig.LED_Min_Brightness);
|
|
||||||
leds.setBrightness(color);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
leds.setBrightness(LubeConfig.LED_Min_Brightness);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LED_Override:
|
|
||||||
leds.setBrightness(LubeConfig.LED_Max_Brightness);
|
|
||||||
leds.setPixelColor(0, LED_override_color);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
leds.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef FEATURE_ENABLE_OLED
|
#ifdef FEATURE_ENABLE_OLED
|
||||||
/**
|
/**
|
||||||
* @brief Manages the display content based on the current system status and updates the OLED display.
|
* @brief Manages the display content based on the current system status and updates the OLED display.
|
||||||
@@ -570,7 +379,19 @@ void Display_Process()
|
|||||||
u8x8.setCursor(0, 1);
|
u8x8.setCursor(0, 1);
|
||||||
|
|
||||||
// Calculate remaining lubrication distance based on system mode
|
// Calculate remaining lubrication distance based on system mode
|
||||||
uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain;
|
uint32_t DistRemain = 0;
|
||||||
|
switch (globals.systemStatus)
|
||||||
|
{
|
||||||
|
case sysStat_Normal:
|
||||||
|
DistRemain = LubeConfig.DistancePerLube_Default;
|
||||||
|
break;
|
||||||
|
case sysStat_Rain:
|
||||||
|
DistRemain = LubeConfig.DistancePerLube_Rain;
|
||||||
|
break;
|
||||||
|
case sysStat_Wash:
|
||||||
|
DistRemain = LubeConfig.WashMode_Interval;
|
||||||
|
break;
|
||||||
|
}
|
||||||
DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000);
|
DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000);
|
||||||
|
|
||||||
// Display relevant information on the OLED screen based on system status
|
// Display relevant information on the OLED screen based on system status
|
||||||
@@ -597,119 +418,6 @@ void Display_Process()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Processes the button input and performs corresponding actions based on button state and timing.
|
|
||||||
*
|
|
||||||
* This function handles the button input, detecting button presses and executing actions based on
|
|
||||||
* predefined time delays. Actions include toggling WiFi, starting purge, toggling operating modes,
|
|
||||||
* and displaying feedback through LEDs. The function utilizes an enumeration to track button actions
|
|
||||||
* and manages the timing for different actions.
|
|
||||||
*/
|
|
||||||
void Button_Process()
|
|
||||||
{
|
|
||||||
// Time delays for different button actions
|
|
||||||
#define BUTTON_ACTION_DELAY_TOGGLEMODE 500
|
|
||||||
#define BUTTON_ACTION_DELAY_PURGE 3500
|
|
||||||
#define BUTTON_ACTION_DELAY_WIFI 6500
|
|
||||||
#define BUTTON_ACTION_DELAY_NOTHING 9500
|
|
||||||
|
|
||||||
// Enumeration to represent button actions
|
|
||||||
typedef enum buttonAction_e
|
|
||||||
{
|
|
||||||
BTN_INACTIVE,
|
|
||||||
BTN_NOTHING,
|
|
||||||
BTN_TOGGLEMODE,
|
|
||||||
BTN_TOGGLEWIFI,
|
|
||||||
BTN_STARTPURGE
|
|
||||||
} buttonAction_t;
|
|
||||||
|
|
||||||
// Static variables to track button state and timing
|
|
||||||
static uint32_t buttonTimestamp = 0;
|
|
||||||
static buttonAction_t buttonAction = BTN_INACTIVE;
|
|
||||||
|
|
||||||
// Check if button is pressed (LOW)
|
|
||||||
if (digitalRead(GPIO_BUTTON) == LOW)
|
|
||||||
{
|
|
||||||
// Update button timestamp on the first button press
|
|
||||||
if (buttonTimestamp == 0)
|
|
||||||
buttonTimestamp = millis();
|
|
||||||
|
|
||||||
// Check and execute actions based on predefined time delays
|
|
||||||
if (buttonTimestamp + BUTTON_ACTION_DELAY_NOTHING < millis())
|
|
||||||
{
|
|
||||||
LED_Process(1, COLOR_WARM_WHITE);
|
|
||||||
buttonAction = BTN_NOTHING;
|
|
||||||
}
|
|
||||||
else if (buttonTimestamp + BUTTON_ACTION_DELAY_WIFI < millis())
|
|
||||||
{
|
|
||||||
LED_Process(1, LED_WIFI_BLINK);
|
|
||||||
buttonAction = BTN_TOGGLEWIFI;
|
|
||||||
}
|
|
||||||
else if (buttonTimestamp + BUTTON_ACTION_DELAY_PURGE < millis())
|
|
||||||
{
|
|
||||||
LED_Process(1, LED_PURGE_COLOR);
|
|
||||||
buttonAction = BTN_STARTPURGE;
|
|
||||||
}
|
|
||||||
else if (buttonTimestamp + BUTTON_ACTION_DELAY_TOGGLEMODE < millis())
|
|
||||||
{
|
|
||||||
uint32_t color = globals.systemStatus == sysStat_Normal ? LED_RAIN_COLOR : LED_NORMAL_COLOR;
|
|
||||||
LED_Process(1, color);
|
|
||||||
buttonAction = BTN_TOGGLEMODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // Button is released
|
|
||||||
{
|
|
||||||
// Execute corresponding actions based on the detected button action
|
|
||||||
if (buttonAction != BTN_INACTIVE)
|
|
||||||
{
|
|
||||||
switch (buttonAction)
|
|
||||||
{
|
|
||||||
case BTN_TOGGLEWIFI:
|
|
||||||
toggleWiFiAP();
|
|
||||||
Debug_pushMessage("Starting WiFi AP\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BTN_STARTPURGE:
|
|
||||||
globals.systemStatus = sysStat_Purge;
|
|
||||||
globals.purgePulses = LubeConfig.BleedingPulses;
|
|
||||||
Debug_pushMessage("Starting Purge\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BTN_TOGGLEMODE:
|
|
||||||
switch (globals.systemStatus)
|
|
||||||
{
|
|
||||||
case sysStat_Normal:
|
|
||||||
globals.systemStatus = sysStat_Rain;
|
|
||||||
globals.resumeStatus = sysStat_Rain;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case sysStat_Rain:
|
|
||||||
globals.systemStatus = sysStat_Normal;
|
|
||||||
globals.resumeStatus = sysStat_Normal;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Debug_pushMessage("Toggling Mode\n");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BTN_NOTHING:
|
|
||||||
default:
|
|
||||||
Debug_pushMessage("Nothing or invalid\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display feedback through LEDs
|
|
||||||
LED_Process(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset button state and timestamp
|
|
||||||
buttonAction = BTN_INACTIVE;
|
|
||||||
buttonTimestamp = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Toggles the WiFi functionality based on the current status.
|
* @brief Toggles the WiFi functionality based on the current status.
|
||||||
*
|
*
|
||||||
@@ -722,6 +430,8 @@ void Button_Process()
|
|||||||
*/
|
*/
|
||||||
void toggleWiFiAP(bool shutdown)
|
void toggleWiFiAP(bool shutdown)
|
||||||
{
|
{
|
||||||
|
LEDControl_SetOverride(LED_WIFI_COLOR, LED_PATTERN_BLINK_FAST, 2500);
|
||||||
|
|
||||||
// Check if WiFi is currently active
|
// Check if WiFi is currently active
|
||||||
if (WiFi.getMode() != WIFI_OFF)
|
if (WiFi.getMode() != WIFI_OFF)
|
||||||
{
|
{
|
||||||
|
@@ -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-01-30 20:29:34.
|
* @note This file is auto-generated by a script on 2025-06-15 11:37:51.
|
||||||
*
|
*
|
||||||
* @author Marcel Peterkau
|
* @author Marcel Peterkau
|
||||||
* @date 30.01.2024
|
* @date 15.06.2025
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -26,6 +26,8 @@ void generateJsonObject_LubeConfig(JsonObject data)
|
|||||||
data["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch;
|
data["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch;
|
||||||
data["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm;
|
data["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm;
|
||||||
data["BleedingPulses"] = LubeConfig.BleedingPulses;
|
data["BleedingPulses"] = LubeConfig.BleedingPulses;
|
||||||
|
data["WashMode_Distance"] = LubeConfig.WashMode_Distance;
|
||||||
|
data["WashMode_Interval"] = LubeConfig.WashMode_Interval;
|
||||||
data["SpeedSource"] = LubeConfig.SpeedSource;
|
data["SpeedSource"] = LubeConfig.SpeedSource;
|
||||||
data["GPSBaudRate"] = LubeConfig.GPSBaudRate;
|
data["GPSBaudRate"] = LubeConfig.GPSBaudRate;
|
||||||
data["CANSource"] = LubeConfig.CANSource;
|
data["CANSource"] = LubeConfig.CANSource;
|
||||||
@@ -52,4 +54,4 @@ void generateJsonObject_PersistenceData(JsonObject data)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// CODEGENERATOR_CHECKSUM: 59f35aadffd0bbef253210ea2fbaaf9a515553a2e3cc9bf4cfa2819b63c969ce
|
// CODEGENERATOR_CHECKSUM: 4702cb49ea55617cbb34715164810bb58d3c3f46fb1653b6f47bd4fd9cb0031e
|
@@ -480,7 +480,6 @@ void Websocket_HandleButtons(uint8_t *data)
|
|||||||
else if (strcmp(identifier, "purgenow") == 0)
|
else if (strcmp(identifier, "purgenow") == 0)
|
||||||
{
|
{
|
||||||
globals.systemStatus = sysStat_Purge;
|
globals.systemStatus = sysStat_Purge;
|
||||||
globals.purgePulses = LubeConfig.BleedingPulses;
|
|
||||||
}
|
}
|
||||||
else if (strcmp(identifier, "sourcesave") == 0)
|
else if (strcmp(identifier, "sourcesave") == 0)
|
||||||
{
|
{
|
||||||
@@ -499,6 +498,7 @@ void Websocket_HandleButtons(uint8_t *data)
|
|||||||
else if (strcmp(identifier, "resettank") == 0)
|
else if (strcmp(identifier, "resettank") == 0)
|
||||||
{
|
{
|
||||||
PersistenceData.tankRemain_microL = LubeConfig.tankCapacity_ml * 1000;
|
PersistenceData.tankRemain_microL = LubeConfig.tankCapacity_ml * 1000;
|
||||||
|
globals.requestEEAction = EE_PDS_SAVE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -580,6 +580,14 @@ void Websocket_HandleSettings(uint8_t *data)
|
|||||||
{
|
{
|
||||||
strncpy(LubeConfig.wifi_client_password, value, sizeof(LubeConfig.wifi_client_password));
|
strncpy(LubeConfig.wifi_client_password, value, sizeof(LubeConfig.wifi_client_password));
|
||||||
}
|
}
|
||||||
|
else if (strcmp(identifier, "washinterval") == 0)
|
||||||
|
{
|
||||||
|
LubeConfig.WashMode_Interval = atoi(value);
|
||||||
|
}
|
||||||
|
else if (strcmp(identifier, "washdistance") == 0)
|
||||||
|
{
|
||||||
|
LubeConfig.WashMode_Distance = atoi(value);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug_pushMessage("Got unknown Settings-id and value '%s' from ws-client\n", identifier);
|
Debug_pushMessage("Got unknown Settings-id and value '%s' from ws-client\n", identifier);
|
||||||
@@ -700,6 +708,8 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
|
|||||||
const char mapping[] = "MAPPING_STATIC:"
|
const char mapping[] = "MAPPING_STATIC:"
|
||||||
"lubedistancenormal;"
|
"lubedistancenormal;"
|
||||||
"lubedistancerain;"
|
"lubedistancerain;"
|
||||||
|
"washdistance;"
|
||||||
|
"washinterval;"
|
||||||
"tankcap;"
|
"tankcap;"
|
||||||
"pumppulse;"
|
"pumppulse;"
|
||||||
"tankwarn;"
|
"tankwarn;"
|
||||||
@@ -730,6 +740,8 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
|
|||||||
|
|
||||||
temp.concat(String(LubeConfig.DistancePerLube_Default) + ";");
|
temp.concat(String(LubeConfig.DistancePerLube_Default) + ";");
|
||||||
temp.concat(String(LubeConfig.DistancePerLube_Rain) + ";");
|
temp.concat(String(LubeConfig.DistancePerLube_Rain) + ";");
|
||||||
|
temp.concat(String(LubeConfig.WashMode_Distance) + ";");
|
||||||
|
temp.concat(String(LubeConfig.WashMode_Interval) + ";");
|
||||||
temp.concat(String(LubeConfig.tankCapacity_ml) + ";");
|
temp.concat(String(LubeConfig.tankCapacity_ml) + ";");
|
||||||
temp.concat(String(LubeConfig.amountPerDose_microL) + ";");
|
temp.concat(String(LubeConfig.amountPerDose_microL) + ";");
|
||||||
temp.concat(String(LubeConfig.TankRemindAtPercentage) + ";");
|
temp.concat(String(LubeConfig.TankRemindAtPercentage) + ";");
|
||||||
|
Reference in New Issue
Block a user