From 7c73e07a096eff963e2bee234e445e6781b5870b Mon Sep 17 00:00:00 2001 From: Johannes Guter Date: Mon, 7 Oct 2024 19:55:43 +0200 Subject: [PATCH] Dateien nach "/" hochladen init --- Arduino_Code.ino | 189 +++++++++++++++++++++++ Hauptprogramm.py | 353 +++++++++++++++++++++++++++++++++++++++++++ Steuerung_Arduino.py | 162 ++++++++++++++++++++ 3 files changed, 704 insertions(+) create mode 100644 Arduino_Code.ino create mode 100644 Hauptprogramm.py create mode 100644 Steuerung_Arduino.py diff --git a/Arduino_Code.ino b/Arduino_Code.ino new file mode 100644 index 0000000..dfb1a02 --- /dev/null +++ b/Arduino_Code.ino @@ -0,0 +1,189 @@ +//Code für den Arduino, später nur den Pin 11 entfernen, da der für das selbstständige Triggern ohne Messrad benutzt wird +#include + +volatile unsigned int counter = 0; +unsigned long lastSignalMillis1 = 0; +unsigned long lastSignalMillis2 = 0; +unsigned long time200 = 0; +bool signalActive1 = false; +bool signalActive2 = false; + +int trigger = 10; +int vortrigger = 8; +int belichtung = 3; + +bool pwmAktiv = false; +bool light_reset = false; + +volatile float pwmFrequency = 15000; // PWM-Frequenz in Hz +volatile float dutyCycle = 70; // Duty Cycle in Prozent +long tast = (16000000L / pwmFrequency) * (dutyCycle / 100); + + +void setup() { + Serial.begin(9600); + +// Falls vorhanden, werden die letzten gespeicherten Werte aus dem EEPROM gelesen + EEPROM.get(0, trigger); + EEPROM.get(4, vortrigger); + EEPROM.get(8, belichtung); + EEPROM.get(12, pwmFrequency); + EEPROM.get(16, dutyCycle); + //EEPROM.get(20, pwmAktiv); + +//Falls nicht vorhanden, werden vordefinierte Werte verwendet und in den EEPROM geschrieben + if ((trigger == 0 || trigger == -1) || (vortrigger == 0 || vortrigger == -1) || (belichtung == 0 || belichtung == -1) || (pwmFrequency == 0 || pwmFrequency == -1) || (dutyCycle == 0 || dutyCycle == -1)) + { + trigger = 10; + vortrigger = 8; + belichtung = 3; + pwmFrequency = 15000; + dutyCycle = 10; + pwmAktiv = false; + + + EEPROM.put(0, trigger); + EEPROM.put(4, vortrigger); + EEPROM.put(8, belichtung); + EEPROM.put(12, pwmFrequency); + EEPROM.put(16, dutyCycle); + //EEPROM.put(20, pwmAktiv); + } + + + updateTast(); //Tastverhältnis für die PWM Einstellung aktuallisieren + + + pinMode(2, INPUT); // Eingangspin + pinMode(13, OUTPUT); // Hauptausgang zum Triggern der Kameras + pinMode(12, OUTPUT); //Zweiter Hauptausgang zum Triggern der Kameras (Aufgrund von reduzierter Belastbarkeit des Stromteilers benötigt) + pinMode(9, OUTPUT); // PWM-Ausgang für Beleuchtung + pinMode(11, OUTPUT); // PWM-Ausgang für das selbstständige Triggern + + // Timer 1 Konfiguration fast PWM an Pin 9 + TCCR1A = 0; + TCCR1B = 0; + TCCR1A |= (1 << COM1A1) | (1 << WGM11); + TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10); + ICR1 = (16000000L / pwmFrequency) - 1; + + + //Pin 2 (Eingang Messrad) erzeugt einen Interrupt um Impulse sicher zu erfassen + attachInterrupt(digitalPinToInterrupt(2), countSignal, RISING); + + +} + +void loop() { + + + int cycleTime = 4000; // Mikrosekunden 3703 = 27 Hz (nur für selbstständiges Triggern benötigt) + int onTime = cycleTime * 0.5; // 50% Duty Cycle + int offTime = cycleTime - onTime; + + digitalWrite(11, HIGH); + delayMicroseconds(onTime); + digitalWrite(11, LOW); + delayMicroseconds(offTime); + + + if (counter >= vortrigger) { //Beleuchtung X Impulse vor dem Kameratrigger einschalten + if (!signalActive2) { + signalActive2 = true; + OCR1A = tast; + } + lastSignalMillis2 = millis(); + } + + if (counter >= trigger) { //Kameras triggern + time200 = millis(); + if (!signalActive1) { + signalActive1 = true; + digitalWrite(13, HIGH); + digitalWrite(12, HIGH); + } + lastSignalMillis1 = millis(); + counter -= trigger; + } + + if (signalActive2 && (millis() - time200 >= belichtung) && counter < vortrigger && pwmAktiv==false) { //Beleuchtung ausschalten, wenn Belichtungszeit (beginnend ab Trigger) vorbei ist und der Counter wieder zurückgesetzt wurde + signalActive2 = false; + OCR1A = 0; + + } + + if (signalActive1 && (millis() - lastSignalMillis1 >= 1)) { + signalActive1 = false; + digitalWrite(13, LOW); + digitalWrite(12, LOW); + } + + if (pwmAktiv == true) //Beleuchtung dauerhaft an wenn das über GUI angefordert wurde. (Noch to do: Diese Abfrage aus dem loop herausziehen und den Aufruf in recieveData packen) + { + OCR1A = tast; + } + if (light_reset == true) { + OCR1A = 0; + light_reset = false; + } + + + if (Serial.available() > 0) { + receiveData(); + } +} + +void countSignal() { + counter++; +} + +void updateTast() { + tast = (16000000L / pwmFrequency) * (dutyCycle / 100); + ICR1 = (16000000L / pwmFrequency) - 1; +} + +//Funktion zum Erhalten von Daten über den RPi +void receiveData() { + String data = Serial.readStringUntil('\n'); + if (data.startsWith("SET")) { + String command = data.substring(4); + if (command.startsWith("trigger")) { + trigger = command.substring(8).toInt(); + EEPROM.put(0, trigger); + } else if (command.startsWith("vortrigger")) { + int tempVortrigger = command.substring(11).toInt(); + vortrigger = trigger - tempVortrigger; + EEPROM.put(4, vortrigger); + } else if (command.startsWith("belichtung")) { + belichtung = command.substring(11).toInt(); + EEPROM.put(8, belichtung); + } else if (command.startsWith("pwmFrequency")) { + pwmFrequency = command.substring(13).toInt(); + EEPROM.put(12, pwmFrequency); + updateTast(); + } else if (command.startsWith("dutyCycle")) { + int dutyCycleInt = command.substring(10).toInt(); + dutyCycle = dutyCycleInt / 1.0; + EEPROM.put(16, dutyCycle); + updateTast(); + } else if (command.startsWith("pwm_aktiv")) { + pwmAktiv = command.substring(10).toInt(); + EEPROM.put(20, pwmAktiv); + if (pwmAktiv == 0) { + light_reset = 1; + } + } + } else if (data.startsWith("GET")) { + sendData(); + } +} +//Funktion zum Senden von Daten an den RPi +void sendData() { + String data = "trigger:" + String(trigger) + ";"; + data += "vortrigger:" + String(trigger-vortrigger) + ";"; + data += "belichtung:" + String(belichtung) + ";"; + data += "pwmFrequency:" + String(pwmFrequency) + ";"; + data += "dutyCycle:" + String(int(dutyCycle)) + ";"; + data += "pwm_aktiv:" + String(pwmAktiv) + ";"; + Serial.println(data); +} \ No newline at end of file diff --git a/Hauptprogramm.py b/Hauptprogramm.py new file mode 100644 index 0000000..e6e44c8 --- /dev/null +++ b/Hauptprogramm.py @@ -0,0 +1,353 @@ +#Hauptprogramm; Nimmt kontinuierlich Bilder auf wenn externer Trigger ausgelöst wird und speichert diese bei Bedarf + +import cv2 +import av +import time +import subprocess +import multiprocessing +from multiprocessing import shared_memory +import sys +import signal +import RPi.GPIO as GPIO +import numpy as np +import psutil +import os + +#Region of Interest definieren: +roi_start = 0 #min: 0 +roi_end = 800 #max: 720 + +roi_breite = roi_end - roi_start + + +#Warnungen von av deaktivieren (Ansonsten kommt dauerhaft eine Warnung über ein mögliches falsches Pixelformat, ist aber iO) +av.logging.set_level(av.logging.ERROR) + + +#GPIO Pin für Speicherimpuls einstellen +ausloeser = 17 +GPIO.setmode(GPIO.BCM) +GPIO.setup(ausloeser, GPIO.IN, pull_up_down = GPIO.PUD_UP) + + +#Kameraeinstellungen; Soll später aus einer Textdatei ausgelesen werden, die über ein GUI eingestellt wird (Fast fertig) +camera_ids = [0, 2, 4, 6] + +triggermodus_on = "v4l2-ctl -d %d -c exposure_dynamic_framerate=1" +triggermodus_off = "v4l2-ctl -d %d -c exposure_dynamic_framerate=0" +belichtung = "v4l2-ctl -d %d -c exposure_time_absolute=1" +auto_exposure = "v4l2-ctl -d %d -c auto_exposure=1" +brightness = "v4l2-ctl -d %d -c brightness=64" +contrast = "v4l2-ctl -d %d -c contrast=64" + + +#Funktion um die Kameraeinstellungen an das Terminal zu senden +def run_cmd(cmd): + try: + subprocess.run(cmd, shell=True, check=True) + except subprocess.CalledProcessError as e: + print("Fehler", e) + + + +#Ctrl+C zum Programmabbruch definieren +def signal_handler(sig, frame): + print('Abbruch') + sys.exit(0) + #Evtl. noch Kameras schließen, Python Prozesse beenden, Bildspeicher komplett leeren, usw..) + +signal.signal(signal.SIGINT, signal_handler) + + +#Klasse definieren, um Bilder von den Kameras aufzunehmen +class CameraModule(): + def run(self, camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block): + + shm_np_array = np.ndarray(shm_shape, dtype='uint8', buffer=shm_np_block.buf) + shm_camera_id_array = np.frombuffer(shm_camera_id_block.buf, dtype='int32') + shm_k_array = np.frombuffer(shm_k_block.buf, dtype='int32') + shm_cam_id = np.frombuffer(camera_values.buf, dtype='int32') + + ring_counter = 0 + + #Cv2-Stream öffnen (Ohne den Cv2 Warmup werden die Kameras komischerweise nicht korrekt gestartet, hab da vieles probiert..) + cap = cv2.VideoCapture(f"/dev/video{camera_id}", cv2.CAP_V4L2) + cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 800) + + for _ in range(50): #50 Bilder aufnehmen um Kamera zu stabilisieren + ret, frame = cap.read() + + cap.release() + cv2.destroyAllWindows() + time.sleep(5) + + #av-Kameraeinstellungen + options = { + 'video_size': '1280x800', + 'framerate': '60', + 'input_format': 'mjpeg' + } + + input_stream = av.open('/dev/video{}'.format(camera_id), format='video4linux2', options=options) + time.sleep(1) + video_stream = next(s for s in input_stream.streams if s.type == 'video') + framerate = video_stream.average_rate + print(f"Kamera {camera_id} Framerate: {framerate}") #Zur Kontrolle + + #50 Bilder aufnehmen; Dient dazu, die Kamera zu stabilisieren (Weißabgleich usw.) + for _ in range(50): + frame = next(input_stream.decode(video_stream)) + time.sleep(0.1) + + #Triggermodus der Kamera auf externen Trigger umstellen + run_cmd(triggermodus_on % camera_id) + + k = 1 + frame = None + ring_counter = 0 + + #Buffer der Kameras (~5 Bilder) füllen und Trigger synchronisieren; Wird später auch noch automatisiert durch Verbindung mit dem Arduino + print("Trigger auslösen bis Freigabe kommt") + for _ in range(50): + frame = next(input_stream.decode(video_stream)) + print("Freigabe erteilt") + time.sleep(5) + print("Bereit") + + #Kontinuierlich Bilder aufnehmen und in die image_queue schreiben + while True: + + slot = (camera_id // 2 + ring_counter * 4) % 200 + frame = next(input_stream.decode(video_stream)) + numpy_frame = frame.to_ndarray(format='bgr24')[roi_start:roi_end, :] + + freigabe = shm_k_array[slot] + + if freigabe == 0: + shm_np_array[slot] = numpy_frame + shm_camera_id_array[slot] = camera_id + shm_k_array[slot] = k + shm_cam_id[camera_id//2] = k + + k += 1 + ring_counter = (ring_counter + 1) + + else: + print("Speicher wird überschrieben") + print(shm_k_array[slot]) + print(slot) + print(camera_id) + + #Noch eine Fehlererkennung einbauen, falls Frame nicht ausgelesen werden kann? + + +#Kameraworker definieren +def camera_worker(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block, cpu_core): + p = psutil.Process(os.getpid()) + p.cpu_affinity([cpu_core]) #Jede Instanz wird auf einem seperaten CPU Kern ausgeführt + camera_module = CameraModule() + camera_module.run(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block) + + +#Klasse definieren, um GPIO/Speicherimpuls zu überwachen +class SaveHandler(): + def __init__(self, camera_values, trigger_liste): + self.camera_values = camera_values + self.trigger_liste = trigger_liste + GPIO.add_event_detect(ausloeser, GPIO.FALLING, callback=self.ausloeser_callback, bouncetime=200) + + def ausloeser_callback(self, channel): + shm_cam_id = np.frombuffer(camera_values.buf, dtype='int32') + + while True: + if not GPIO.input(channel): + + cam_value = shm_cam_id + self.trigger_liste.append(cam_value) + print("Trigger empfangen") + time.sleep(1) #Ist nur Testweise drin, damit nicht zu oft getriggert wird durch betätigen eines Tasters + + +def handler_worker(camera_values, trigger_liste): + handler = SaveHandler(camera_values, trigger_liste) + handler.ausloeser_callback(ausloeser) + + +#Klasse definieren, um Bilder von der image_queue zu holen und an den Saver zu senden, falls gewünscht +class SaveImageProcess(): + def __init__(self, shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + self.trigger_liste = trigger_liste + self.shm_np_block = shm_np_block + self.shm_camera_id_block = shm_camera_id_block + self.shm_k_block = shm_k_block + self.shm_save_frame = shm_save_frame + self.shm_save_k = shm_save_k + self.shm_save_camera_id = shm_save_camera_id + self.shm_save_ts = shm_save_ts + self.shm_save_t = shm_save_t + + + def run(self, shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + + shm_np_array = np.ndarray(shm_shape, dtype='uint8', buffer=shm_np_block.buf) + shm_camera_id_array = np.frombuffer(shm_camera_id_block.buf, dtype='int32') + shm_k_array = np.frombuffer(shm_k_block.buf, dtype='int32') + + shm_save_frame_array = np.ndarray(save_shape, dtype='uint8', buffer=shm_save_frame.buf) + shm_save_k_array = np.frombuffer(shm_save_k.buf, dtype='int32') + shm_save_camera_id_array = np.frombuffer(shm_save_camera_id.buf, dtype='int32') + shm_save_ts_array = np.frombuffer(shm_save_ts.buf, dtype='int32') + shm_save_t_array = np.frombuffer(shm_save_t.buf, dtype='int32') + + + ring_counter = 0 + save_counter = 0 + + j = 0 + l = 0 + print("SaveImageProcess gestartet") + + while True: + + non_zero_indices = [index for index, value in enumerate(shm_k_array[:]) if value != 0] + + if len(non_zero_indices) > 20: + print(non_zero_indices) + for index in non_zero_indices: + + camera_id = shm_camera_id_array[index] + k = shm_k_array[index] + shm_k_array[index] = 0 + + trigger_test = np.array(trigger_liste) + + for i in range(j, trigger_test.shape[0]): + if k >= (trigger_test[i][camera_id//2]-5+1) and k <= (trigger_test[i][camera_id//2]+5+1): + shm_save_frame_array[save_counter] = shm_np_array[index] + shm_save_camera_id_array[save_counter] = camera_id + shm_save_k_array[save_counter] = k + shm_save_ts_array[save_counter] = trigger_test[i][camera_id//2] + shm_save_t_array[save_counter] = i + + save_counter = (save_counter + 1) % 200 + + + if k > (trigger_test[i][camera_id//2]+200): + j += 1 + + +def save_worker(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + + save_worker = SaveImageProcess(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + save_worker.run(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + +#Klasse um die Bilder zu speichern (Ist aufgeteilt, da das Speichern der Bilder deutlich der Aufnahme der Bilder hinterherhängt und ohne diesen Frames verworfen werden) +class Saver(): + def __init__(self, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + self.shm_save_frame = shm_save_frame + self.shm_save_k = shm_save_k + self.shm_save_camera_id = shm_save_camera_id + self.shm_save_ts = shm_save_ts + self.shm_save_t = shm_save_t + + def run(self, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + + shm_save_frame_array = np.ndarray(save_shape, dtype='uint8', buffer=shm_save_frame.buf) + shm_save_k_array = np.frombuffer(shm_save_k.buf, dtype='int32') + shm_save_camera_id_array = np.frombuffer(shm_save_camera_id.buf, dtype='int32') + shm_save_ts_array = np.frombuffer(shm_save_ts.buf, dtype='int32') + shm_save_t_array = np.frombuffer(shm_save_t.buf, dtype='int32') + + ring_counter = 0 + + while True: + if shm_save_k_array[ring_counter] != 0: + + filename = "/mnt/ramdisk/Cam{}_T{}_TS{}_B{}.jpg".format(shm_save_camera_id_array[ring_counter], shm_save_t_array[ring_counter], shm_save_ts_array[ring_counter], shm_save_k_array[ring_counter]) + cv2.imwrite(filename, shm_save_frame_array[ring_counter]) + + shm_save_k_array[ring_counter] = 0 + ring_counter = (ring_counter + 1) % 200 + + +def saver_worker(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t): + + saver = Saver(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + saver.run(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + + + + + + + + +if __name__ == "__main__": + + #Einstellungen der Shared Memory Blöcke + shm_shape = (200, roi_breite, 1280, 3) + save_shape = (200, roi_breite, 1280, 3) + cv_shape = (1, 4, 1, 1) + shm_np_block = shared_memory.SharedMemory(create=True, size=shm_shape[0]*shm_shape[1]*shm_shape[2]*shm_shape[3]*np.dtype('uint8').itemsize) + shm_camera_id_block = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + shm_k_block = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + + + shm_save_frame = shared_memory.SharedMemory(create=True, size=save_shape[0]*save_shape[1]*save_shape[2]*save_shape[3]*np.dtype('uint8').itemsize) + shm_save_k = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + shm_save_camera_id = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + shm_save_ts = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + shm_save_t = shared_memory.SharedMemory(create=True, size=200 * np.dtype('int32').itemsize) + + camera_values = shared_memory.SharedMemory(create=True, size= 4*np.dtype('int32').itemsize) + + + + #Kameraeinstellungen vornehmen/Wird später durch GUI ersetzt + print("Kameras einstellen") + for i in [0, 2, 4, 6]: + run_cmd(triggermodus_off % i) + time.sleep(0.5) + run_cmd(auto_exposure % i) + time.sleep(0.5) + run_cmd(belichtung % i) + time.sleep(0.5) + run_cmd(brightness % i) + time.sleep(0.5) + run_cmd(contrast % i) + time.sleep(0.5) + print("Kamera %d eingestellt" % i) + + + #Trigger_liste als Multiprocessing-Liste erstellen + manager = multiprocessing.Manager() + trigger_liste = manager.list() + + + processes = [] + i = 0 + cpu_cores = [0, 1, 2, 3] + + for camera_id in camera_ids: + process = multiprocessing.Process(target=camera_worker, args=(camera_id, camera_values, shm_np_block, shm_camera_id_block, shm_k_block, cpu_cores[i])) + i += 1 + process.start() + processes.append(process) + + + handler = SaveHandler(camera_values, trigger_liste) + + save_process = multiprocessing.Process(target=save_worker, args=(shm_np_block, shm_camera_id_block, shm_k_block, trigger_liste, shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t)) + processes.append(save_process) + save_process.start() + + saver2 = Saver(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + saver2.run(shm_save_frame, shm_save_k, shm_save_camera_id, shm_save_ts, shm_save_t) + + process.join() + save_process.join() + saver2.join() + + diff --git a/Steuerung_Arduino.py b/Steuerung_Arduino.py new file mode 100644 index 0000000..3f0621f --- /dev/null +++ b/Steuerung_Arduino.py @@ -0,0 +1,162 @@ +#Erstellt die grafische Oberfläche und ermöglicht die serielle Kommunikation mit dem Arduino, um die Einstellungen für die Beleuchtung und Triggerhandhabung vorzunehmen + +import tkinter as tk +from tkinter import messagebox +import serial +import os + +class Entry: + def __init__(self, root): + self.root = root + root.title("Steuerung Arduino") + + root.protocol("WM_DELETE_WINDOW", self.on_closing) + + + self.settings_file = "/home/pi/Arduino/einstellungen.txt" + settings_path = os.path.dirname(self.settings_file) + if not os.path.exists(settings_path): + os.makedirs(settings_path) + + self.load_settings() #Einstellungen beim Start des Programms laden + + try: + self.ser = serial.Serial("/dev/ttyUSB0", 9600, timeout = 2) #Serielle Verbindung zum Arduino aufbauen mit 2 Sekunden Timeout + except serial.SerialException as e: + messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e) + return + + self.create_widgets() + + root.geometry("400x500") + + #Widgets für die GUI erstellen (Checkboxen, Eingabefelder, Beschriftungen) + def create_widgets(self): + self.entries = [] + + names = ["trigger", "vortrigger", "belichtung", "pwmFrequency", "dutyCycle"] + description = ["Impulse zum Auslösen des Kameratriggers", "Start der Beleuchtung vor dem Kameratrigger", "Belichtungsdauer", "Frequenz PWM", "Duty Cycle PWM"] + units = ["Imp", "Imp", "ms", "Hz", "%"] + extra = ["[250 Hz - 60 kHz]", "[0 - 100 %]"] + + i = 0 + + for name in names: + frame = tk.Frame(self.root) + frame.pack(fill=tk.X, pady=5) + tk.Label(frame, text = description[i]).pack() + + entry_frame = tk.Frame(frame) + entry_frame.pack(side=tk.TOP) + entry = tk.Entry(entry_frame, width = 7) + entry.pack(side=tk.LEFT) + unit = tk.Label(entry_frame, text=units[i]).pack(side=tk.LEFT) + + entry.insert(0, self.settings.get(name, '')) + + self.entries.append((name, entry)) + + i += 1 + + self.pwm_aktiv = tk.IntVar(value = self.settings.get("pwm_aktiv", 0)) + tk.Checkbutton(self.root, text = "Beleuchtung einschalten", variable=self.pwm_aktiv).pack(pady=5) + + tk.Button(self.root, text = "Einstellungen an Arduino senden", command = self.send_arduino_settings).pack(pady=5) + tk.Button(self.root, text = "Einstellungen aus Arduino laden", command = self.load_arduino_settings).pack(pady=5) + tk.Button(self.root, text='Einstellungen speichern', command=self.save_settings).pack(pady=5) + tk.Button(self.root, text='Einstellungen laden', command=self.load_settings).pack(pady=5) + + #Funktion um die Einstellungen in der Textdatei zu speichern + def save_settings(self): + settings = [f"{name}:{entry.get()}\n" for name, entry in self.entries] + with open(self.settings_file, "w") as f: + f.writelines(settings) + + #Funktion um Einstellungen aus Textdatei zu lesen + def load_settings(self): + if os.path.exists(self.settings_file): + with open(self.settings_file, "r") as f: + self.settings = {line.split(':')[0]: line.split(':')[1].strip() for line in f} + else: + self.settings = {} + + #Einstellungen über serielle Verbindung vom Arduino laden + def load_arduino_settings(self): + if not self.ser.is_open: + messagebox.showerror("Fehler", "Keine Verbindung zum Arduino") + return + + try: + self.ser.write(b'GET\n') + data = self.ser.readline().decode('utf-8').strip() + + except serial.SerialException as e: + messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e) + return + + settings = data.split(';') + for setting, (name, entry) in zip(settings, self.entries): + try: + value = setting.split(':')[1] + except IndexError: + messagebox.showerror("Fehler", "Unerwartete Daten vom Arduino erhalten, Kommunikation überprüfen") + return + if name == "pwm_aktiv": + self.pwm_aktiv.set(int(value)) + + if "." in value: + float_value = float(value) + if float_value.is_integer(): + value = int(float_value) + + entry.delete(0, tk.END) #Feld leeren + entry.insert(0, value) #Am Anfang des Feldes wird Value eingegeben + self.settings[name] = value #Werte in settings speichern + + #Einstellungen über serielle Verbindung an Arduino senden + def send_arduino_settings(self): + if not self.ser.is_open: + messagebox.showerror("Fehler", "Keine Verbindung zum Arduino") + return + + try: + for name, entry in self.entries: + value = entry.get() + if not value.isdigit() and name != "pwm_aktiv": + messagebox.showerror("Fehler", f"Ungültiger Wert für {name}") + return + + if name == "pwmFrequency" and not (250 <= float(value) <= 60000): + messagebox.showerror("Fehler", f"Ungültiger Wert für PWM Frequenz. Der Wert muss zwischen 250 und 60000 liegen") + return + if name == "dutyCycle" and not (0 <= int(value) <= 100): + messagebox.showerror("Fehler", f"Ungültiger Wert für Duty Cycle. Der Wert muss zwischen 0 und 100 liegen") + return + + command = f"SET {name} {value}" + self.ser.write((command + "\n").encode("utf-8")) + + command = f"SET pwm_aktiv {self.pwm_aktiv.get()}" + self.ser.write((command + "\n").encode("utf-8")) + + except serial.SerialException as e: + messagebox.showerror("Fehler in der Kommunikation mit dem Arduino", e) + return + + def on_closing(self): + if self.ser.is_open: + self.ser.close() + self.root.destroy() + + + +if __name__ == "__main__": + root = tk.Tk() + app = Entry(root) + root.mainloop() + + + + + +