Compare commits
	
		
			18 Commits
		
	
	
		
			PCB_Rev_1.
			...
			68d571a747
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 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"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								Software/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								Software/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,3 +3,6 @@ data/
 | 
				
			|||||||
.vscode
 | 
					.vscode
 | 
				
			||||||
wifi_credentials.ini
 | 
					wifi_credentials.ini
 | 
				
			||||||
__pycache__
 | 
					__pycache__
 | 
				
			||||||
 | 
					# Node-Tools für Build-Scripts
 | 
				
			||||||
 | 
					/tools_node/
 | 
				
			||||||
 | 
					/data_stripped/
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,23 +11,35 @@ 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Initialisiere npm, falls noch nicht geschehen
 | 
				
			||||||
 | 
					    if not os.path.isfile(os.path.join(tools_dir, 'package.json')):
 | 
				
			||||||
 | 
					        print("🛠️  Initializing local npm project in tools_node...")
 | 
				
			||||||
 | 
					        subprocess.run(['npm', 'init', '-y'], cwd=tools_dir, check=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Installiere Tool (idempotent)
 | 
				
			||||||
 | 
					    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 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 +52,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 +60,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:
 | 
					 | 
				
			||||||
        print('GZIP: Failure/Incomplete.\n')
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
        print('GZIP: Compressed correctly.\n')
 | 
					 | 
				
			||||||
        shutil.rmtree(data_temp_dir_path)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return
 | 
					    if was_error:
 | 
				
			||||||
 | 
					        print('⚠️  GZIP: Nicht alle Dateien konnten verarbeitet werden.\n')
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        print('✅ GZIP: Komprimierung abgeschlossen.\n')
 | 
				
			||||||
 | 
					        shutil.rmtree(temp_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										47
									
								
								Software/src/button_actions.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Software/src/button_actions.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// === 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()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    extern void toggleWiFiAP(bool shutdown = false);
 | 
				
			||||||
 | 
					    toggleWiFiAP();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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_PURGE_COLOR, ButtonAction_StartPurge},
 | 
				
			||||||
 | 
					    {6500, LED_WIFI_COLOR, ButtonAction_ToggleWiFi},
 | 
				
			||||||
 | 
					    {9500, LED_WASH_COLOR, ButtonAction_WashMode}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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,6 +12,9 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 #include "debugger.h"
 | 
					 #include "debugger.h"
 | 
				
			||||||
 | 
					 #include <map>
 | 
				
			||||||
 | 
					 #include <functional>
 | 
				
			||||||
 | 
					 #include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DebugStatus_t DebuggerStatus[dbg_cntElements];
 | 
					DebugStatus_t DebuggerStatus[dbg_cntElements];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -27,6 +30,7 @@ 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -94,10 +98,9 @@ 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;
 | 
				
			||||||
        }        
 | 
					        }        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -147,6 +150,29 @@ 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 +244,100 @@ 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")
 | 
					    int start = 0, end = 0;
 | 
				
			||||||
        Debug_printHelp();
 | 
					    while ((end = input.indexOf(' ', start)) != -1)
 | 
				
			||||||
    else if (command == "sysinfo")
 | 
					    {
 | 
				
			||||||
        Debug_printSystemInfo();
 | 
					        tokens.push_back(input.substring(start, end));
 | 
				
			||||||
    else if (command == "netinfo")
 | 
					        start = end + 1;
 | 
				
			||||||
        Debug_printWifiInfo();
 | 
					    }
 | 
				
			||||||
    else if (command == "formatCFG")
 | 
					    if (start < input.length())
 | 
				
			||||||
        Debug_formatCFG();
 | 
					        tokens.push_back(input.substring(start));
 | 
				
			||||||
    else if (command == "formatPDS")
 | 
					    return tokens;
 | 
				
			||||||
        Debug_formatPersistence();
 | 
					}
 | 
				
			||||||
    else if (command == "checkEE")
 | 
					
 | 
				
			||||||
        Debug_CheckEEPOM(false);
 | 
					// === getArg helper ===
 | 
				
			||||||
    else if (command == "checkEEfix")
 | 
					String getArg(const std::vector<String> &args, size_t index, const String &defaultVal = "")
 | 
				
			||||||
        Debug_CheckEEPOM(true);
 | 
					{
 | 
				
			||||||
    else if (command == "dumpEE1k")
 | 
					    if (index < args.size())
 | 
				
			||||||
        dumpEEPROM(0, 1024);
 | 
					        return args[index];
 | 
				
			||||||
    else if (command == "dumpEE")
 | 
					    return defaultVal;
 | 
				
			||||||
        dumpEEPROM(0, EEPROM_SIZE_BYTES);
 | 
					}
 | 
				
			||||||
    else if (command == "resetPageEE")
 | 
					
 | 
				
			||||||
        MovePersistencePage_EEPROM(true);
 | 
					// === Command Handler Map ===
 | 
				
			||||||
    else if (command == "dumpCFG")
 | 
					typedef std::function<void(const String &args)> DebugCmdHandler;
 | 
				
			||||||
        Debug_dumpConfig();
 | 
					
 | 
				
			||||||
    else if (command == "dumpPDS")
 | 
					static const std::map<String, DebugCmdHandler> &getCmdMap()
 | 
				
			||||||
        Debug_dumpPersistance();
 | 
					{
 | 
				
			||||||
    else if (command == "saveEE")
 | 
					    static const std::map<String, DebugCmdHandler> cmdMap = {
 | 
				
			||||||
        globals.requestEEAction = EE_ALL_SAVE;
 | 
					        {"help", [](const String &) {
 | 
				
			||||||
    else if (command == "dumpGlobals")
 | 
					             Debug_log(LOG_INFO, "Available commands:\n");
 | 
				
			||||||
        Debug_dumpGlobals();
 | 
					             for (const auto &entry : getCmdMap())
 | 
				
			||||||
    else if (command == "sdbg")
 | 
					                 Debug_log(LOG_INFO, " - %s\n", entry.first.c_str());
 | 
				
			||||||
        SetDebugportStatus(dbg_Serial, enabled);
 | 
					         }},
 | 
				
			||||||
    else if (command == "dtc_show")
 | 
					        {"sysinfo", [](const String &) { Debug_printSystemInfo(); }},
 | 
				
			||||||
        Debug_ShowDTCs();
 | 
					        {"netinfo", [](const String &) { Debug_printWifiInfo(); }},
 | 
				
			||||||
    else if (command == "dtc_clear")
 | 
					        {"formatCFG", [](const String &) { Debug_formatCFG(); }},
 | 
				
			||||||
        ClearAllDTC();
 | 
					        {"formatPDS", [](const String &) { Debug_formatPersistence(); }},
 | 
				
			||||||
    else if (command == "dtc_crit")
 | 
					        {"checkEE", [](const String &) { Debug_CheckEEPOM(false); }},
 | 
				
			||||||
        MaintainDTC(DTC_FAKE_DTC_CRIT, true, millis());
 | 
					        {"checkEEfix", [](const String &) { Debug_CheckEEPOM(true); }},
 | 
				
			||||||
    else if (command == "dtc_warn")
 | 
					        {"dumpEE1k", [](const String &) { dumpEEPROM(0, 1024); }},
 | 
				
			||||||
        MaintainDTC(DTC_FAKE_DTC_WARN, true, millis());
 | 
					        {"dumpEE", [](const String &args) {
 | 
				
			||||||
    else if (command == "dtc_info")
 | 
					             int start = 0, len = EEPROM_SIZE_BYTES;
 | 
				
			||||||
        MaintainDTC(DTC_FAKE_DTC_INFO, true, millis());
 | 
					             auto tokens = splitArgs(args);
 | 
				
			||||||
    else if (command == "notify_error")
 | 
					             if (tokens.size() >= 2)
 | 
				
			||||||
        Websocket_PushNotification("Debug Error Notification", error);
 | 
					             {
 | 
				
			||||||
    else if (command == "notify_warning")
 | 
					                 start = tokens[0].toInt();
 | 
				
			||||||
        Websocket_PushNotification("Debug Warning Notification", warning);
 | 
					                 len = tokens[1].toInt();
 | 
				
			||||||
    else if (command == "notify_success")
 | 
					             }
 | 
				
			||||||
        Websocket_PushNotification("Debug Success Notification", success);
 | 
					             dumpEEPROM(start, len);
 | 
				
			||||||
    else if (command == "notify_info")
 | 
					         }},
 | 
				
			||||||
        Websocket_PushNotification("Debug Info Notification", info);
 | 
					        {"resetPageEE", [](const String &) { MovePersistencePage_EEPROM(true); }},
 | 
				
			||||||
    else if (command == "purge")
 | 
					        {"dumpCFG", [](const String &) { Debug_dumpConfig(); }},
 | 
				
			||||||
        Debug_Purge();
 | 
					        {"dumpPDS", [](const String &) { Debug_dumpPersistance(); }},
 | 
				
			||||||
    else if (command == "toggle_wifi")
 | 
					        {"saveEE", [](const String &) { globals.requestEEAction = EE_ALL_SAVE; }},
 | 
				
			||||||
        globals.toggle_wifi = true;
 | 
					        {"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(); }},
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    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,25 +531,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 +544,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.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										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 washModeDistance = 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -49,17 +53,31 @@ void RunLubeApp(uint32_t add_milimeters)
 | 
				
			|||||||
    switch (globals.systemStatus)
 | 
					    switch (globals.systemStatus)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
    case sysStat_Startup:
 | 
					    case sysStat_Startup:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Startup"));
 | 
					            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:
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Normal"));
 | 
					            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:
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Rain"));
 | 
					            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,41 @@ void RunLubeApp(uint32_t add_milimeters)
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case sysStat_Wash:
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            washModeDistance = 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 (washModeDistance >= LubeConfig.WashMode_Distance)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                washModeDistance = washModeDistance - LubeConfig.WashMode_Interval;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                globals.systemStatus = globals.resumeStatus;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case sysStat_Purge:
 | 
					    case sysStat_Purge:
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            globals.purgePulses = LubeConfig.BleedingPulses;
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Purge"));
 | 
					            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 +157,26 @@ void RunLubeApp(uint32_t add_milimeters)
 | 
				
			|||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case sysStat_Error:
 | 
					    case sysStat_Error:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Error"));
 | 
					            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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (lastSystemStatus != globals.systemStatus)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
            strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
 | 
					            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,10 @@
 | 
				
			|||||||
#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 +63,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 +136,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 +177,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,8 +253,8 @@ 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();
 | 
				
			||||||
@@ -340,208 +344,6 @@ void trigger_ISR()
 | 
				
			|||||||
  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
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -597,119 +399,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 +411,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)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -788,6 +479,36 @@ void SystemShutdown(bool restart)
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void onToggleMode()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (globals.systemStatus == sysStat_Normal)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        globals.systemStatus = sysStat_Rain;
 | 
				
			||||||
 | 
					        globals.resumeStatus = sysStat_Rain;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (globals.systemStatus == sysStat_Rain)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        globals.systemStatus = sysStat_Normal;
 | 
				
			||||||
 | 
					        globals.resumeStatus = sysStat_Normal;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    Debug_pushMessage("Toggling Mode\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void onStartPurge()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    globals.systemStatus = sysStat_Purge;
 | 
				
			||||||
 | 
					    globals.purgePulses = LubeConfig.BleedingPulses;
 | 
				
			||||||
 | 
					    Debug_pushMessage("Starting Purge\n");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void onWashMode()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Debug_pushMessage("Wash mode start\n");
 | 
				
			||||||
 | 
					    // Hier könntest du später gezieltes Nachölen implementieren
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * @brief Processes the impulses from the wheel speed sensor and converts them into traveled distance.
 | 
					 * @brief Processes the impulses from the wheel speed sensor and converts them into traveled distance.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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