diff --git a/concept.txt b/concept.txt deleted file mode 100644 index e69de29..0000000 diff --git a/main.py b/main.py index 90a33f9..f80e30e 100644 --- a/main.py +++ b/main.py @@ -1,145 +1,383 @@ import tkinter as tk -from tkinter import messagebox +from tkinter import ttk, messagebox +import customtkinter +customtkinter.set_appearance_mode("light") +customtkinter.set_default_color_theme("blue") +import serial +import serial.tools.list_ports import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.figure import Figure -#import random +import threading +import time +import numpy as np -p = 0.0 -i = 0.0 - -set_torque = 0.0 - -act_torque = 0.0 -act_angle = 0.0 - -class T2000ErdemGUI: +class ArduinoGUI: def __init__(self, root): + self.my_font = customtkinter.CTkFont(family="Calibri", size=15) self.root = root - self.root.title("T2000 Erdem") - - menu_frame = tk.Frame(self.root) - menu_frame.grid(row=0, column=0, rowspan=8, columnspan=2, sticky="NESW", padx=10, pady=10) - menu_frame.columnconfigure(0, weight=1) - menu_frame.rowconfigure(6, weight=1) - - plot_frame = tk.Frame(self.root) - plot_frame.grid(row=0, column=2, rowspan=8, sticky="NW") - - plot_frame.rowconfigure(0, weight=1) - plot_frame.columnconfigure(0, weight=1) - - self.root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight())) + self.root.title("PRG 342 GUI") self.root.attributes('-fullscreen', True) # Setzt das Fenster in den Vollbildmodus - #self.root.overrideredirect(True) # Entfernt die Fensterdekorationen (wie Minimieren, Maximieren und Schließen) - # Eingabefelder für Koordinaten der Drehmoment-Drehwinkel-Kennlinie - tk.Label(menu_frame, text="Koordinaten der Drehmoment-Drehwinkel-Kennlinie:").grid(row=0, column=0, columnspan=2, pady=10, sticky="N") - self.coordinates_text = tk.Text(menu_frame, height=10, width=20) - self.coordinates_text.grid(row=1, column=0, columnspan=2, pady=10) - self.coordinates_text.insert('end', '0,0\n5,15\n10,10\n90,10') + self.arduino = None + self.ports = list(serial.tools.list_ports.comports()) + self.running = True + self.current_point = None + self.setpoints = [] - # Eingabefeld für direkte Drehmomentvorgabe - tk.Label(menu_frame, text="Soll-Drehmoment in Nm:").grid(row=2, column=0, pady=10, sticky="NW") - self.set_torque_value = tk.DoubleVar() - self.torque_input = tk.Entry(menu_frame, textvariable=self.set_torque_value) - self.torque_input.grid(row=2, column=1, sticky="NW", pady=10) + # Flags for user actions + self.setpoint_flag = False + self.multi_setpoints_flag = False + self.tare_flag = False + self.demag_flag = False + self.input_source_flag = False + self.pid_flag = False # Flag for sending PID parameters - # Anzeige für aktuellen Drehwinkel und Drehmoment - tk.Label(menu_frame, text="Aktueller Drehwinkel:").grid(row=3, column=0, pady=10, sticky="NW") - self.current_angle = tk.Label(menu_frame, text="0" + " °") - self.current_angle.grid(row=3, column=1, sticky="NW", pady=10) + self.pid_params = {} + self.selected_pid = None - tk.Label(menu_frame, text="Aktuelles Drehmoment:").grid(row=4, column=0, pady=10, sticky="NW") - self.current_torque = tk.Label(menu_frame, text="0" + " Nm") - self.current_torque.grid(row=4, column=1, sticky="NW", pady=10) + self.create_widgets() - # Button zum Schließen der Anwendung - self.close_button = tk.Button(menu_frame, text="Beenden", command=self.close_application) - self.close_button.grid(row=7, column=1, pady=10, sticky="S") + self.load_configurations() - # Button zur Übernahme der Eingaben und Aktualisierung des Diagramms - self.plot_button = tk.Button(menu_frame, text="Eingaben übernehmen", command=self.on_plot_button_click) - self.plot_button.grid(row=7, column=0, pady=10, sticky="S") + # Start the communication thread + self.communication_thread = threading.Thread(target=self.communication_loop) + self.communication_thread.start() + + def create_widgets(self): - # Schalter für Umschaltung zwischen Drehmomentprofil und direkter Vorgabe - self.torque_mode = tk.BooleanVar() - self.torque_mode.set(False) # Startet im Modus "Drehmomentprofil" - self.torque_switch = tk.Checkbutton(menu_frame, text="Direkte Drehmomentvorgabe aktivieren", variable=self.torque_mode, command=self.toggle_torque_mode) - self.torque_switch.grid(row=5, column=0, columnspan=2, pady=10) + # Labels and ComboBox for Stellantriebstyp + self.stellantriebstyp_label = customtkinter.CTkLabel(self.root, text="Stellantriebstyp:", font=self.my_font) + self.stellantriebstyp_label.grid(row=0, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - # Diagramm - self.figure = Figure(dpi=100)#(figsize=(6,6), dpi=100) - self.figure.set_figheight(self.root.winfo_screenheight()/100) - self.figure.set_figwidth((self.root.winfo_screenwidth()-380)/100) - self.plot = self.figure.add_subplot(111) - self.plot.set_xlim(0, 90) - self.plot.set_ylim(ymin=0) - self.plot.set_xlabel('Drehwinkel in Grad') - self.plot.set_ylabel('Drehmoment in Nm') - self.canvas = FigureCanvasTkAgg(self.figure, plot_frame) - self.canvas.get_tk_widget().pack(fill="both", expand=True) + self.config_combobox_value = customtkinter.StringVar() + self.config_combobox = customtkinter.CTkComboBox(self.root, variable=self.config_combobox_value, state="readonly", font=self.my_font) + self.config_combobox.grid(row=0, column=1, columnspan=1, padx=(0, 10), pady=(10, 0), sticky="ew") + #self.config_combobox.bind("<>", self.on_config_selected) - self.toggle_torque_mode() - self.create_diagram() - self.update_current_values_periodically() + # Setzen Button für Stellantriebstyp + self.set_pid_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_pid_parameters, font=self.my_font) + self.set_pid_button.grid(row=0, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - def close_application(self): - #if messagebox.askokcancel("Beenden", "Möchten Sie die Anwendung wirklich beenden?"): - self.root.quit() + # Labels and ComboBox for COM Port + self.com_label = customtkinter.CTkLabel(self.root, text="COM Port:", font=self.my_font) + self.com_label.grid(row=1, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - def create_diagram(self): - self.plot.clear() - self.plot.set_xlim(0, 90) - self.plot.set_xlabel('Drehwinkel in Grad') - self.plot.set_ylabel('Drehmoment in Nm') - - coordinates = self.coordinates_text.get("1.0", tk.END).strip().split("\n") - x_coords, y_coords = [], [] - for coord in coordinates: - x, y = map(float, coord.split(',')) - x_coords.append(x) - y_coords.append(y) - - self.plot.plot(x_coords, y_coords, marker='o', linestyle='-') - self.plot.set_ylim(bottom=0) + self.combobox_value = customtkinter.StringVar() + self.combobox = customtkinter.CTkComboBox(self.root, variable=self.combobox_value, font=self.my_font) + self.combobox.configure(values=[port.device for port in self.ports]) + self.combobox.grid(row=1, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") + + self.connect_button = customtkinter.CTkButton(self.root, text="Verbinden", command=self.connect_arduino, font=self.my_font) + self.connect_button.grid(row=1, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") + + # Setpoint entry + self.setpoint_label = customtkinter.CTkLabel(self.root, text="Sollwerteingabe in Nm:", font=self.my_font) + self.setpoint_label.grid(row=2, column=0, padx=(10, 5), pady=(10, 0), sticky="w") + + self.setpoint_entry = customtkinter.CTkEntry(self.root, font=self.my_font) + self.setpoint_entry.grid(row=2, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") + + self.set_setpoint_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_setpoint, font=self.my_font) + self.set_setpoint_button.grid(row=2, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") + + # Multi Setpoints Textbox and Button + self.multi_setpoints_label = customtkinter.CTkLabel(self.root, text="Sollwerteeingabe\n(Winkel;Drehmoment):", font=self.my_font) + self.multi_setpoints_label.grid(row=3, column=0, padx=(10, 5), pady=(10, 0), sticky="w") + + self.multi_setpoints_text = customtkinter.CTkTextbox(self.root, font=self.my_font) + self.multi_setpoints_text.grid(row=3, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") + + self.send_multi_setpoints_button = customtkinter.CTkButton(self.root, text="Sollwerte senden", command=self.send_multi_setpoints, font=self.my_font) + self.send_multi_setpoints_button.grid(row=3, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") + + # Analog Control CheckBox + self.checkbox_var = customtkinter.IntVar() + self.checkbox = customtkinter.CTkCheckBox(self.root, text="Analogsteuerung", variable=self.checkbox_var, command=self.input_source_Switch, font=self.my_font) + self.checkbox.grid(row=4, column=0, columnspan=3, padx=(10, 10), pady=(10, 0), sticky="w") + + # Tare and Demagnetize Buttons + self.tare_button = customtkinter.CTkButton(self.root, text="Drehwinkel tarieren", command=self.tare_angle, font=self.my_font) + self.tare_button.grid(row=5, column=0, padx=(10, 5), pady=(10, 0), sticky="sew") + + self.demag_button = customtkinter.CTkButton(self.root, text="Manuelle Entmagnetisierung", command=self.demagnetize, font=self.my_font) + self.demag_button.grid(row=5, column=1, padx=(0, 5), pady=(10, 0), sticky="sew") + + # Exit Button + self.exit_button = customtkinter.CTkButton(self.root, text="Beenden", command=self.on_closing, font=self.my_font) + self.exit_button.grid(row=5, column=2, padx=(0, 10), pady=(10, 0), sticky="sew") + + # Plot + self.figure, self.ax = plt.subplots() + self.canvas = FigureCanvasTkAgg(self.figure, self.root) + self.canvas.get_tk_widget().grid(row=0, column=3, rowspan=6, columnspan=4, padx=(10, 10), pady=(10, 0), sticky="nsew") + + self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") + self.ax.set_xlabel("Winkel (°)") + self.ax.set_ylabel("Drehmoment (Nm)") + self.ax.set_xlim(0, 90) + self.ax.set_ylim(0, 50) + + # Current readings labels + self.current_angle = 0 + self.current_torque = 0 + self.analogInput = 0 + + self.angle_label_var = customtkinter.StringVar() + self.angle_label_var.set("Drehwinkel: 0") + self.angle_label = customtkinter.CTkLabel(self.root, textvariable=self.angle_label_var, font=self.my_font) + self.angle_label.grid(row=6, column=3, padx=(10, 5), pady=(10, 0), sticky="sw") + + self.torque_label_var = customtkinter.StringVar() + self.torque_label_var.set("Istwert: 0") + self.torque_label = customtkinter.CTkLabel(self.root, textvariable=self.torque_label_var, font=self.my_font) + self.torque_label.grid(row=6, column=4, padx=(10, 5), pady=(10, 0), sticky="sw") + + self.analogread_label_var = customtkinter.StringVar() + self.analogread_label_var.set("Analogeingang: 0") + self.analogread_label = customtkinter.CTkLabel(self.root, textvariable=self.analogread_label_var, font=self.my_font) + self.analogread_label.grid(row=6, column=5, padx=(10, 5), pady=(10, 0), sticky="sw") + + self.currentSetpoint_label_var = customtkinter.StringVar() + self.currentSetpoint_label_var.set("Aktueller Sollwert: 0") + self.currentSetpoint_label = customtkinter.CTkLabel(self.root, textvariable=self.currentSetpoint_label_var, font=self.my_font) + self.currentSetpoint_label.grid(row=6, column=6, padx=(10, 5), pady=(10, 0), sticky="sw") + + self.root.grid_columnconfigure(0, weight=0) + self.root.grid_columnconfigure(1, weight=0) + self.root.grid_columnconfigure(2, weight=0) + self.root.grid_columnconfigure(3, weight=1) + self.root.grid_columnconfigure(4, weight=1) + self.root.grid_columnconfigure(5, weight=1) + self.root.grid_columnconfigure(6, weight=1) + self.root.grid_rowconfigure(0, weight=0) + self.root.grid_rowconfigure(1, weight=0) + self.root.grid_rowconfigure(2, weight=0) + self.root.grid_rowconfigure(3, weight=0) + self.root.grid_rowconfigure(4, weight=0) + self.root.grid_rowconfigure(5, weight=1) + self.root.grid_rowconfigure(6, weight=0) + + def set_pid_parameters(self): + if self.arduino: + self.selected_pid = self.config_combobox.get() + if self.selected_pid in self.pid_params: + self.pid_flag = True + else: + messagebox.showerror("Error", "Selected PID configuration not found.") + else: + messagebox.showwarning("Warning", "Arduino not connected") + + def load_configurations(self): + try: + with open("conf.txt", "r") as file: + config_name = None + for line in file: + line = line.strip() + if line.startswith("#") or not line: + continue + if "{" in line: + config_name = line.split("{")[0].strip() + self.pid_params[config_name] = {"Kp": 0.0, "Ki": 0.0, "Kd": 0.0} + elif "}" in line: + config_name = None + else: + if config_name: + key, value = line.split(":") + key = key.strip() + value = round(float(value.strip()), 3) + self.pid_params[config_name][key] = value + + self.config_combobox.configure(values=list(self.pid_params.keys())) + if self.pid_params: + self.config_combobox.set(list(self.pid_params.keys())[0]) + self.selected_pid = list(self.pid_params.keys())[0] + except Exception as e: + messagebox.showerror("Error", f"Error loading configurations: {e}") + + def connect_arduino(self): + com_port = self.combobox_value.get() + if com_port: + try: + self.arduino = serial.Serial(com_port, 115200, timeout=1) + messagebox.showinfo("Info", "Connected to Arduino on " + com_port) + except Exception as e: + messagebox.showerror("Error", str(e)) + else: + messagebox.showwarning("Warning", "Please select a COM port") + + def set_setpoint(self): + if self.arduino: + try: + self.setpoint = float(self.setpoint_entry.get()) * 1000 + self.setpoint_flag = True + except ValueError: + messagebox.showerror("Error", "Invalid setpoint value.") + else: + messagebox.showwarning("Warning", "Arduino not connected") + + def send_multi_setpoints(self): + if self.arduino: + setpoints = self.multi_setpoints_text.get("1.0", tk.END).strip().split("\n") + coordinates = [] + for point in setpoints: + try: + angle, torque = map(float, point.split(";")) + coordinates.append((angle, torque)) + except ValueError: + messagebox.showerror("Error", f"Invalid input: {point}") + return + + coordinates.sort() + self.setpoints = coordinates + self.plot_coordinates(coordinates) + self.multi_setpoints_flag = True + else: + messagebox.showwarning("Warning", "Arduino not connected") + + def plot_coordinates(self, coordinates): + angles, torques = zip(*coordinates) + self.ax.clear() + self.ax.plot(angles, torques, 'bo-', label="Setpoints") + self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") + self.ax.set_xlabel("Winkel (°)") + self.ax.set_ylabel("Drehmoment (Nm)") + self.ax.set_xlim(0, 90) + self.ax.set_ylim(0, 50) + self.ax.legend() self.canvas.draw() - def update_current_values(self, angle, act_torque): - self.current_angle.config(text=str(angle) + " °") - self.current_torque.config(text=str(act_torque) + " Nm") + def interpolate_coordinates(self, coordinates): + interpolated_points = [] + for i in range(len(coordinates) - 1): + start_angle, start_torque = coordinates[i] + end_angle, end_torque = coordinates[i + 1] + num_points = int((end_angle - start_angle) * 2) + angles = np.linspace(start_angle, end_angle, num_points + 1) + torques = np.linspace(start_torque, end_torque, num_points + 1) + interpolated_points.extend(zip(angles, torques)) - def save_set_values(self): - p = self.p_value.get() - i = self.i_value.get() + full_interpolated_points = [(angle, 0) for angle in np.arange(0, 90.5, 0.5)] + for angle, torque in interpolated_points: + index = int(angle * 2) + full_interpolated_points[index] = (angle, torque) - def toggle_torque_mode(self): - # Aktivieren oder Deaktivieren des direkten Drehmoment-Eingabefeldes - if self.torque_mode.get(): - self.torque_input['state'] = 'normal' - self.coordinates_text['state'] = 'disabled' + return full_interpolated_points + + def send_interpolated_points(self): + if self.arduino: + setpoints_command = "u" + for angle, torque in self.setpoints: + torque_mnm = torque * 1000 + setpoints_command += f"{int(angle * 2)},{torque_mnm:.1f};" + setpoints_command = setpoints_command.rstrip(';') + "u\n" + self.arduino.write(setpoints_command.encode()) + + timeout_ctr = time.time() + while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): + pass + + try: + print(self.arduino.readline()) + + except Exception as e: + print(e) + + def tare_angle(self): + if self.arduino: + self.tare_flag = True else: - self.torque_input['state'] = 'disabled' - self.coordinates_text['state'] = 'normal' + messagebox.showwarning("Warning", "Arduino not connected") - def on_plot_button_click(self): - if self.torque_mode.get(): - # Logik für die direkte Vorgabe des Soll-Drehmoments - set_torque = self.set_torque_value.get() + def demagnetize(self): + if self.arduino: + self.demag_flag = True else: - # Logik für das Drehmomentprofil - self.create_diagram() - self.save_set_values() + messagebox.showwarning("Warning", "Arduino not connected") - def update_current_values_periodically(self): - # Simulieren von veränderlichen aktuellen Werten (als Beispiel) - #new_angle = random.randint(0, 90) - #new_torque = random.uniform(0, 50) - #self.update_current_values(new_angle, new_torque) - self.update_current_values(act_angle, act_torque) - self.root.after(200, self.update_current_values_periodically) + def input_source_Switch(self): + if self.arduino: + self.input_source_flag = True + else: + messagebox.showwarning("Warning", "Arduino not connected") -# Anwendung starten -root = tk.Tk() -app = T2000ErdemGUI(root) -root.mainloop() \ No newline at end of file + def communication_loop(self): + while self.running: + if self.arduino: + self.arduino.read_all() + if self.setpoint_flag: + self.arduino.write(f"s{self.setpoint:.0f},0\n".encode()) + self.setpoint_flag = False + time.sleep(0.1) + if self.multi_setpoints_flag: + self.send_interpolated_points() + self.multi_setpoints_flag = False + time.sleep(0.1) + if self.tare_flag: + self.arduino.write(b'w\n') + self.tare_flag = False + time.sleep(0.1) + if self.demag_flag: + self.arduino.write(b'e\n') + self.demag_flag = False + time.sleep(0.1) + if self.input_source_flag: + self.arduino.write(b'S\n') + self.input_source_flag = False + time.sleep(0.1) + if self.pid_flag: + self.send_pid_parameters() + self.pid_flag = False + + self.arduino.write(b'a\n') + timeout_ctr = time.time() + while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): + pass + + try: + data = self.arduino.readline().decode(errors='ignore').strip().split(";") + self.current_angle = float(data[0]) / 1000 + self.current_torque = float(data[1]) / 1000 + self.analogInput = float(data[2]) / 1000 + self.currentSetpoint = float(data[3]) / 1000 + + self.angle_label_var.set(f"Drehwinkel: {self.current_angle:.1f} °") + self.torque_label_var.set(f"Istwert: {self.current_torque:.1f} Nm") + self.analogread_label_var.set(f"Analogeingang: {self.analogInput:.1f} V") + self.currentSetpoint_label_var.set(f"Aktueller Sollwert: {self.currentSetpoint:.1f} Nm") + + if self.current_point is not None: + self.current_point.remove() + self.current_point, = self.ax.plot([self.current_angle], [self.current_torque], 'ro') + self.canvas.draw() + except Exception as e: + print(e) + + time.sleep(0.1) + + def send_pid_parameters(self): + if self.selected_pid in self.pid_params: + pid_values = self.pid_params[self.selected_pid] + kp = f"{pid_values['Kp']:.3f}" + ki = f"{pid_values['Ki']:.3f}" + kd = f"{pid_values['Kd']:.3f}" + self.arduino.write(f"p{kp}\n".encode()) + time.sleep(0.1) + self.arduino.write(f"i{ki}\n".encode()) + time.sleep(0.1) + self.arduino.write(f"d{kd}\n".encode()) + messagebox.showinfo("Info", f"PID parameters sent: Kp={kp}, Ki={ki}, Kd={kd}") + else: + messagebox.showerror("Error", "Selected PID configuration not found.") + + def on_closing(self): + self.running = False + if self.arduino: + self.arduino.close() + self.root.quit() + self.root.destroy() + +if __name__ == "__main__": + root = customtkinter.CTk() + app = ArduinoGUI(root) + root.protocol("WM_DELETE_WINDOW", app.on_closing) + root.mainloop() diff --git a/main_v2.py b/main_v2.py deleted file mode 100644 index c9e680e..0000000 --- a/main_v2.py +++ /dev/null @@ -1,121 +0,0 @@ -import tkinter as tk -from tkinter import messagebox -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from matplotlib.figure import Figure -import serial - -# Globale Variablen -Kp = 0.0 -Ki = 0.0 -Kd = 0.0 -arduino = None -root = None -torque_mode = None - -def open_serial_connection(): - global arduino - try: - arduino = serial.Serial(port="COM4", baudrate=115200, timeout=1) - print("Verbindung zu Arduino hergestellt.") - except Exception as e: - print(f"Fehler beim Öffnen der seriellen Verbindung: {e}") - -def close_application(): - if messagebox.askokcancel("Schließen", "Wollen Sie die Anwendung wirklich schließen?"): - if arduino and arduino.is_open: - arduino.close() - root.destroy() - -def create_diagram(plot, coordinates_text, canvas): - plot.clear() - plot.set_xlim(0, 90) - plot.set_ylim(0, 50) # Beispielgrenzen für Drehmoment - plot.set_xlabel('Drehwinkel in Grad') - plot.set_ylabel('Drehmoment in Nm') - - coordinates = coordinates_text.get("1.0", tk.END).strip().split("\n") - valid_coords = [coord for coord in coordinates if ',' in coord and len(coord.split(',')) == 2] - - if valid_coords: - x_coords, y_coords = zip(*(map(float, coord.split(',')) for coord in valid_coords)) - plot.plot(x_coords, y_coords, marker='o', linestyle='-') - else: - messagebox.showerror("Eingabefehler", "Bitte geben Sie gültige Koordinaten ein. Beispiel: '10,20'") - - canvas.draw() - - -def send_command_to_arduino(command): - if arduino and arduino.is_open: - try: - arduino.write(command.encode()) - response = arduino.readline().decode().strip() - print(f"Arduino Antwort: {response}") - except Exception as e: - print(f"Fehler beim Senden des Befehls {command}: {e}") - -def on_plot_button_click(torque_input, coordinates_text, plot, canvas): - if torque_mode.get(): - set_torque = float(torque_input.get()) - command = f's{int(set_torque * 1000000):08x}' - send_command_to_arduino(command) - else: - create_diagram(plot, coordinates_text, canvas) - -def setup_gui(): - global root, torque_mode - root = tk.Tk() - root.title("Drehmoment-Regelungssystem") - - main_frame = tk.Frame(root) - main_frame.pack(fill=tk.BOTH, expand=True) - - left_frame = tk.Frame(main_frame, borderwidth=2, relief=tk.GROOVE) - left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) - - right_frame = tk.Frame(main_frame, borderwidth=2, relief=tk.GROOVE) - right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) - - coordinates_label = tk.Label(left_frame, text="Koordinaten (Drehwinkel, Drehmoment):") - coordinates_label.pack() - coordinates_text = tk.Text(left_frame, height=5) - coordinates_text.pack() - - torque_label = tk.Label(left_frame, text="Soll-Drehmoment in Nm:") - torque_label.pack() - torque_input = tk.Entry(left_frame) - torque_input.pack() - - toggle_button = tk.Button(left_frame, text="Wechsel Drehmomentmodus", command=lambda: toggle_torque_mode(torque_input, coordinates_text)) - toggle_button.pack() - - plot_button = tk.Button(left_frame, text="Eingaben übernehmen", command=lambda: on_plot_button_click(torque_input, coordinates_text, plot, canvas)) - plot_button.pack() - - close_button = tk.Button(left_frame, text="Schließen", command=close_application) - close_button.pack() - - figure = Figure(figsize=(5, 4), dpi=100) - plot = figure.add_subplot(111) - canvas = FigureCanvasTkAgg(figure, right_frame) - canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) - - torque_mode = tk.BooleanVar() - torque_mode.set(False) - -def toggle_torque_mode(torque_input, coordinates_text): - if torque_mode.get(): - torque_input.config(state='normal') - coordinates_text.config(state='disabled') - else: - torque_input.config(state='disabled') - coordinates_text.config(state='normal') - -def main(): - setup_gui() - open_serial_connection() - root.mainloop() - -if __name__ == '__main__': - main() diff --git a/main_v3.py b/main_v3.py deleted file mode 100644 index f80e30e..0000000 --- a/main_v3.py +++ /dev/null @@ -1,383 +0,0 @@ -import tkinter as tk -from tkinter import ttk, messagebox -import customtkinter -customtkinter.set_appearance_mode("light") -customtkinter.set_default_color_theme("blue") -import serial -import serial.tools.list_ports -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -import threading -import time -import numpy as np - -class ArduinoGUI: - def __init__(self, root): - self.my_font = customtkinter.CTkFont(family="Calibri", size=15) - self.root = root - self.root.title("PRG 342 GUI") - self.root.attributes('-fullscreen', True) # Setzt das Fenster in den Vollbildmodus - - self.arduino = None - self.ports = list(serial.tools.list_ports.comports()) - self.running = True - self.current_point = None - self.setpoints = [] - - # Flags for user actions - self.setpoint_flag = False - self.multi_setpoints_flag = False - self.tare_flag = False - self.demag_flag = False - self.input_source_flag = False - self.pid_flag = False # Flag for sending PID parameters - - self.pid_params = {} - self.selected_pid = None - - self.create_widgets() - - self.load_configurations() - - # Start the communication thread - self.communication_thread = threading.Thread(target=self.communication_loop) - self.communication_thread.start() - - def create_widgets(self): - - # Labels and ComboBox for Stellantriebstyp - self.stellantriebstyp_label = customtkinter.CTkLabel(self.root, text="Stellantriebstyp:", font=self.my_font) - self.stellantriebstyp_label.grid(row=0, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.config_combobox_value = customtkinter.StringVar() - self.config_combobox = customtkinter.CTkComboBox(self.root, variable=self.config_combobox_value, state="readonly", font=self.my_font) - self.config_combobox.grid(row=0, column=1, columnspan=1, padx=(0, 10), pady=(10, 0), sticky="ew") - #self.config_combobox.bind("<>", self.on_config_selected) - - # Setzen Button für Stellantriebstyp - self.set_pid_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_pid_parameters, font=self.my_font) - self.set_pid_button.grid(row=0, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Labels and ComboBox for COM Port - self.com_label = customtkinter.CTkLabel(self.root, text="COM Port:", font=self.my_font) - self.com_label.grid(row=1, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.combobox_value = customtkinter.StringVar() - self.combobox = customtkinter.CTkComboBox(self.root, variable=self.combobox_value, font=self.my_font) - self.combobox.configure(values=[port.device for port in self.ports]) - self.combobox.grid(row=1, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.connect_button = customtkinter.CTkButton(self.root, text="Verbinden", command=self.connect_arduino, font=self.my_font) - self.connect_button.grid(row=1, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Setpoint entry - self.setpoint_label = customtkinter.CTkLabel(self.root, text="Sollwerteingabe in Nm:", font=self.my_font) - self.setpoint_label.grid(row=2, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.setpoint_entry = customtkinter.CTkEntry(self.root, font=self.my_font) - self.setpoint_entry.grid(row=2, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.set_setpoint_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_setpoint, font=self.my_font) - self.set_setpoint_button.grid(row=2, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Multi Setpoints Textbox and Button - self.multi_setpoints_label = customtkinter.CTkLabel(self.root, text="Sollwerteeingabe\n(Winkel;Drehmoment):", font=self.my_font) - self.multi_setpoints_label.grid(row=3, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.multi_setpoints_text = customtkinter.CTkTextbox(self.root, font=self.my_font) - self.multi_setpoints_text.grid(row=3, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.send_multi_setpoints_button = customtkinter.CTkButton(self.root, text="Sollwerte senden", command=self.send_multi_setpoints, font=self.my_font) - self.send_multi_setpoints_button.grid(row=3, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Analog Control CheckBox - self.checkbox_var = customtkinter.IntVar() - self.checkbox = customtkinter.CTkCheckBox(self.root, text="Analogsteuerung", variable=self.checkbox_var, command=self.input_source_Switch, font=self.my_font) - self.checkbox.grid(row=4, column=0, columnspan=3, padx=(10, 10), pady=(10, 0), sticky="w") - - # Tare and Demagnetize Buttons - self.tare_button = customtkinter.CTkButton(self.root, text="Drehwinkel tarieren", command=self.tare_angle, font=self.my_font) - self.tare_button.grid(row=5, column=0, padx=(10, 5), pady=(10, 0), sticky="sew") - - self.demag_button = customtkinter.CTkButton(self.root, text="Manuelle Entmagnetisierung", command=self.demagnetize, font=self.my_font) - self.demag_button.grid(row=5, column=1, padx=(0, 5), pady=(10, 0), sticky="sew") - - # Exit Button - self.exit_button = customtkinter.CTkButton(self.root, text="Beenden", command=self.on_closing, font=self.my_font) - self.exit_button.grid(row=5, column=2, padx=(0, 10), pady=(10, 0), sticky="sew") - - # Plot - self.figure, self.ax = plt.subplots() - self.canvas = FigureCanvasTkAgg(self.figure, self.root) - self.canvas.get_tk_widget().grid(row=0, column=3, rowspan=6, columnspan=4, padx=(10, 10), pady=(10, 0), sticky="nsew") - - self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") - self.ax.set_xlabel("Winkel (°)") - self.ax.set_ylabel("Drehmoment (Nm)") - self.ax.set_xlim(0, 90) - self.ax.set_ylim(0, 50) - - # Current readings labels - self.current_angle = 0 - self.current_torque = 0 - self.analogInput = 0 - - self.angle_label_var = customtkinter.StringVar() - self.angle_label_var.set("Drehwinkel: 0") - self.angle_label = customtkinter.CTkLabel(self.root, textvariable=self.angle_label_var, font=self.my_font) - self.angle_label.grid(row=6, column=3, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.torque_label_var = customtkinter.StringVar() - self.torque_label_var.set("Istwert: 0") - self.torque_label = customtkinter.CTkLabel(self.root, textvariable=self.torque_label_var, font=self.my_font) - self.torque_label.grid(row=6, column=4, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.analogread_label_var = customtkinter.StringVar() - self.analogread_label_var.set("Analogeingang: 0") - self.analogread_label = customtkinter.CTkLabel(self.root, textvariable=self.analogread_label_var, font=self.my_font) - self.analogread_label.grid(row=6, column=5, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.currentSetpoint_label_var = customtkinter.StringVar() - self.currentSetpoint_label_var.set("Aktueller Sollwert: 0") - self.currentSetpoint_label = customtkinter.CTkLabel(self.root, textvariable=self.currentSetpoint_label_var, font=self.my_font) - self.currentSetpoint_label.grid(row=6, column=6, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.root.grid_columnconfigure(0, weight=0) - self.root.grid_columnconfigure(1, weight=0) - self.root.grid_columnconfigure(2, weight=0) - self.root.grid_columnconfigure(3, weight=1) - self.root.grid_columnconfigure(4, weight=1) - self.root.grid_columnconfigure(5, weight=1) - self.root.grid_columnconfigure(6, weight=1) - self.root.grid_rowconfigure(0, weight=0) - self.root.grid_rowconfigure(1, weight=0) - self.root.grid_rowconfigure(2, weight=0) - self.root.grid_rowconfigure(3, weight=0) - self.root.grid_rowconfigure(4, weight=0) - self.root.grid_rowconfigure(5, weight=1) - self.root.grid_rowconfigure(6, weight=0) - - def set_pid_parameters(self): - if self.arduino: - self.selected_pid = self.config_combobox.get() - if self.selected_pid in self.pid_params: - self.pid_flag = True - else: - messagebox.showerror("Error", "Selected PID configuration not found.") - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def load_configurations(self): - try: - with open("conf.txt", "r") as file: - config_name = None - for line in file: - line = line.strip() - if line.startswith("#") or not line: - continue - if "{" in line: - config_name = line.split("{")[0].strip() - self.pid_params[config_name] = {"Kp": 0.0, "Ki": 0.0, "Kd": 0.0} - elif "}" in line: - config_name = None - else: - if config_name: - key, value = line.split(":") - key = key.strip() - value = round(float(value.strip()), 3) - self.pid_params[config_name][key] = value - - self.config_combobox.configure(values=list(self.pid_params.keys())) - if self.pid_params: - self.config_combobox.set(list(self.pid_params.keys())[0]) - self.selected_pid = list(self.pid_params.keys())[0] - except Exception as e: - messagebox.showerror("Error", f"Error loading configurations: {e}") - - def connect_arduino(self): - com_port = self.combobox_value.get() - if com_port: - try: - self.arduino = serial.Serial(com_port, 115200, timeout=1) - messagebox.showinfo("Info", "Connected to Arduino on " + com_port) - except Exception as e: - messagebox.showerror("Error", str(e)) - else: - messagebox.showwarning("Warning", "Please select a COM port") - - def set_setpoint(self): - if self.arduino: - try: - self.setpoint = float(self.setpoint_entry.get()) * 1000 - self.setpoint_flag = True - except ValueError: - messagebox.showerror("Error", "Invalid setpoint value.") - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def send_multi_setpoints(self): - if self.arduino: - setpoints = self.multi_setpoints_text.get("1.0", tk.END).strip().split("\n") - coordinates = [] - for point in setpoints: - try: - angle, torque = map(float, point.split(";")) - coordinates.append((angle, torque)) - except ValueError: - messagebox.showerror("Error", f"Invalid input: {point}") - return - - coordinates.sort() - self.setpoints = coordinates - self.plot_coordinates(coordinates) - self.multi_setpoints_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def plot_coordinates(self, coordinates): - angles, torques = zip(*coordinates) - self.ax.clear() - self.ax.plot(angles, torques, 'bo-', label="Setpoints") - self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") - self.ax.set_xlabel("Winkel (°)") - self.ax.set_ylabel("Drehmoment (Nm)") - self.ax.set_xlim(0, 90) - self.ax.set_ylim(0, 50) - self.ax.legend() - self.canvas.draw() - - def interpolate_coordinates(self, coordinates): - interpolated_points = [] - for i in range(len(coordinates) - 1): - start_angle, start_torque = coordinates[i] - end_angle, end_torque = coordinates[i + 1] - num_points = int((end_angle - start_angle) * 2) - angles = np.linspace(start_angle, end_angle, num_points + 1) - torques = np.linspace(start_torque, end_torque, num_points + 1) - interpolated_points.extend(zip(angles, torques)) - - full_interpolated_points = [(angle, 0) for angle in np.arange(0, 90.5, 0.5)] - for angle, torque in interpolated_points: - index = int(angle * 2) - full_interpolated_points[index] = (angle, torque) - - return full_interpolated_points - - def send_interpolated_points(self): - if self.arduino: - setpoints_command = "u" - for angle, torque in self.setpoints: - torque_mnm = torque * 1000 - setpoints_command += f"{int(angle * 2)},{torque_mnm:.1f};" - setpoints_command = setpoints_command.rstrip(';') + "u\n" - self.arduino.write(setpoints_command.encode()) - - timeout_ctr = time.time() - while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): - pass - - try: - print(self.arduino.readline()) - - except Exception as e: - print(e) - - def tare_angle(self): - if self.arduino: - self.tare_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def demagnetize(self): - if self.arduino: - self.demag_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def input_source_Switch(self): - if self.arduino: - self.input_source_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def communication_loop(self): - while self.running: - if self.arduino: - self.arduino.read_all() - if self.setpoint_flag: - self.arduino.write(f"s{self.setpoint:.0f},0\n".encode()) - self.setpoint_flag = False - time.sleep(0.1) - if self.multi_setpoints_flag: - self.send_interpolated_points() - self.multi_setpoints_flag = False - time.sleep(0.1) - if self.tare_flag: - self.arduino.write(b'w\n') - self.tare_flag = False - time.sleep(0.1) - if self.demag_flag: - self.arduino.write(b'e\n') - self.demag_flag = False - time.sleep(0.1) - if self.input_source_flag: - self.arduino.write(b'S\n') - self.input_source_flag = False - time.sleep(0.1) - if self.pid_flag: - self.send_pid_parameters() - self.pid_flag = False - - self.arduino.write(b'a\n') - timeout_ctr = time.time() - while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): - pass - - try: - data = self.arduino.readline().decode(errors='ignore').strip().split(";") - self.current_angle = float(data[0]) / 1000 - self.current_torque = float(data[1]) / 1000 - self.analogInput = float(data[2]) / 1000 - self.currentSetpoint = float(data[3]) / 1000 - - self.angle_label_var.set(f"Drehwinkel: {self.current_angle:.1f} °") - self.torque_label_var.set(f"Istwert: {self.current_torque:.1f} Nm") - self.analogread_label_var.set(f"Analogeingang: {self.analogInput:.1f} V") - self.currentSetpoint_label_var.set(f"Aktueller Sollwert: {self.currentSetpoint:.1f} Nm") - - if self.current_point is not None: - self.current_point.remove() - self.current_point, = self.ax.plot([self.current_angle], [self.current_torque], 'ro') - self.canvas.draw() - except Exception as e: - print(e) - - time.sleep(0.1) - - def send_pid_parameters(self): - if self.selected_pid in self.pid_params: - pid_values = self.pid_params[self.selected_pid] - kp = f"{pid_values['Kp']:.3f}" - ki = f"{pid_values['Ki']:.3f}" - kd = f"{pid_values['Kd']:.3f}" - self.arduino.write(f"p{kp}\n".encode()) - time.sleep(0.1) - self.arduino.write(f"i{ki}\n".encode()) - time.sleep(0.1) - self.arduino.write(f"d{kd}\n".encode()) - messagebox.showinfo("Info", f"PID parameters sent: Kp={kp}, Ki={ki}, Kd={kd}") - else: - messagebox.showerror("Error", "Selected PID configuration not found.") - - def on_closing(self): - self.running = False - if self.arduino: - self.arduino.close() - self.root.quit() - self.root.destroy() - -if __name__ == "__main__": - root = customtkinter.CTk() - app = ArduinoGUI(root) - root.protocol("WM_DELETE_WINDOW", app.on_closing) - root.mainloop() diff --git a/tmp.py b/tmp.py deleted file mode 100644 index abe19f1..0000000 --- a/tmp.py +++ /dev/null @@ -1,384 +0,0 @@ -import tkinter as tk -from tkinter import ttk, messagebox -import customtkinter -customtkinter.set_appearance_mode("light") -customtkinter.set_default_color_theme("blue") -import serial -import serial.tools.list_ports -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -import threading -import time -import numpy as np - -class ArduinoGUI: - def __init__(self, root): - self.my_font = customtkinter.CTkFont(family="Calibri", size=15) - self.root = root - self.root.title("PRG 342 GUI") - self.root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight())) - self.root.attributes('-fullscreen', True) # Setzt das Fenster in den Vollbildmodus - - self.arduino = None - self.ports = list(serial.tools.list_ports.comports()) - self.running = True - self.current_point = None - self.setpoints = [] - - # Flags for user actions - self.setpoint_flag = False - self.multi_setpoints_flag = False - self.tare_flag = False - self.demag_flag = False - self.input_source_flag = False - self.pid_flag = False # Flag for sending PID parameters - - self.pid_params = {} - self.selected_pid = None - - self.create_widgets() - - self.load_configurations() - - # Start the communication thread - self.communication_thread = threading.Thread(target=self.communication_loop) - self.communication_thread.start() - - def create_widgets(self): - - # Labels and ComboBox for Stellantriebstyp - self.stellantriebstyp_label = customtkinter.CTkLabel(self.root, text="Stellantriebstyp:", font=self.my_font) - self.stellantriebstyp_label.grid(row=0, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.config_combobox_value = customtkinter.StringVar() - self.config_combobox = customtkinter.CTkComboBox(self.root, variable=self.config_combobox_value, state="readonly", font=self.my_font) - self.config_combobox.grid(row=0, column=1, columnspan=1, padx=(0, 10), pady=(10, 0), sticky="ew") - #self.config_combobox.bind("<>", self.on_config_selected) - - # Setzen Button für Stellantriebstyp - self.set_pid_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_pid_parameters, font=self.my_font) - self.set_pid_button.grid(row=0, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Labels and ComboBox for COM Port - self.com_label = customtkinter.CTkLabel(self.root, text="COM Port:", font=self.my_font) - self.com_label.grid(row=1, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.combobox_value = customtkinter.StringVar() - self.combobox = customtkinter.CTkComboBox(self.root, variable=self.combobox_value, font=self.my_font) - self.combobox.configure(values=[port.device for port in self.ports]) - self.combobox.grid(row=1, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.connect_button = customtkinter.CTkButton(self.root, text="Verbinden", command=self.connect_arduino, font=self.my_font) - self.connect_button.grid(row=1, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Setpoint entry - self.setpoint_label = customtkinter.CTkLabel(self.root, text="Sollwerteingabe in Nm:", font=self.my_font) - self.setpoint_label.grid(row=2, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.setpoint_entry = customtkinter.CTkEntry(self.root, font=self.my_font) - self.setpoint_entry.grid(row=2, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.set_setpoint_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_setpoint, font=self.my_font) - self.set_setpoint_button.grid(row=2, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Multi Setpoints Textbox and Button - self.multi_setpoints_label = customtkinter.CTkLabel(self.root, text="Sollwerteeingabe\n(Winkel;Drehmoment):", font=self.my_font) - self.multi_setpoints_label.grid(row=3, column=0, padx=(10, 5), pady=(10, 0), sticky="w") - - self.multi_setpoints_text = customtkinter.CTkTextbox(self.root, font=self.my_font) - self.multi_setpoints_text.grid(row=3, column=1, padx=(0, 10), pady=(10, 0), sticky="ew") - - self.send_multi_setpoints_button = customtkinter.CTkButton(self.root, text="Sollwerte senden", command=self.send_multi_setpoints, font=self.my_font) - self.send_multi_setpoints_button.grid(row=3, column=2, padx=(0, 10), pady=(10, 0), sticky="ew") - - # Analog Control CheckBox - self.checkbox_var = customtkinter.IntVar() - self.checkbox = customtkinter.CTkCheckBox(self.root, text="Analogsteuerung", variable=self.checkbox_var, command=self.input_source_Switch, font=self.my_font) - self.checkbox.grid(row=4, column=0, columnspan=3, padx=(10, 10), pady=(10, 0), sticky="w") - - # Tare and Demagnetize Buttons - self.tare_button = customtkinter.CTkButton(self.root, text="Drehwinkel tarieren", command=self.tare_angle, font=self.my_font) - self.tare_button.grid(row=5, column=0, padx=(10, 5), pady=(10, 0), sticky="sew") - - self.demag_button = customtkinter.CTkButton(self.root, text="Manuelle Entmagnetisierung", command=self.demagnetize, font=self.my_font) - self.demag_button.grid(row=5, column=1, padx=(0, 5), pady=(10, 0), sticky="sew") - - # Exit Button - self.exit_button = customtkinter.CTkButton(self.root, text="Beenden", command=self.on_closing, font=self.my_font) - self.exit_button.grid(row=5, column=2, padx=(0, 10), pady=(10, 0), sticky="sew") - - # Plot - self.figure, self.ax = plt.subplots() - self.canvas = FigureCanvasTkAgg(self.figure, self.root) - self.canvas.get_tk_widget().grid(row=0, column=3, rowspan=6, columnspan=4, padx=(10, 10), pady=(10, 0), sticky="nsew") - - self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") - self.ax.set_xlabel("Winkel (°)") - self.ax.set_ylabel("Drehmoment (Nm)") - self.ax.set_xlim(0, 90) - self.ax.set_ylim(0, 50) - - # Current readings labels - self.current_angle = 0 - self.current_torque = 0 - self.analogInput = 0 - - self.angle_label_var = customtkinter.StringVar() - self.angle_label_var.set("Drehwinkel: 0") - self.angle_label = customtkinter.CTkLabel(self.root, textvariable=self.angle_label_var, font=self.my_font) - self.angle_label.grid(row=6, column=3, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.torque_label_var = customtkinter.StringVar() - self.torque_label_var.set("Istwert: 0") - self.torque_label = customtkinter.CTkLabel(self.root, textvariable=self.torque_label_var, font=self.my_font) - self.torque_label.grid(row=6, column=4, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.analogread_label_var = customtkinter.StringVar() - self.analogread_label_var.set("Analogeingang: 0") - self.analogread_label = customtkinter.CTkLabel(self.root, textvariable=self.analogread_label_var, font=self.my_font) - self.analogread_label.grid(row=6, column=5, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.currentSetpoint_label_var = customtkinter.StringVar() - self.currentSetpoint_label_var.set("Aktueller Sollwert: 0") - self.currentSetpoint_label = customtkinter.CTkLabel(self.root, textvariable=self.currentSetpoint_label_var, font=self.my_font) - self.currentSetpoint_label.grid(row=6, column=6, padx=(10, 5), pady=(10, 0), sticky="sw") - - self.root.grid_columnconfigure(0, weight=0) - self.root.grid_columnconfigure(1, weight=0) - self.root.grid_columnconfigure(2, weight=0) - self.root.grid_columnconfigure(3, weight=1) - self.root.grid_columnconfigure(4, weight=1) - self.root.grid_columnconfigure(5, weight=1) - self.root.grid_columnconfigure(6, weight=1) - self.root.grid_rowconfigure(0, weight=0) - self.root.grid_rowconfigure(1, weight=0) - self.root.grid_rowconfigure(2, weight=0) - self.root.grid_rowconfigure(3, weight=0) - self.root.grid_rowconfigure(4, weight=0) - self.root.grid_rowconfigure(5, weight=1) - self.root.grid_rowconfigure(6, weight=0) - - def set_pid_parameters(self): - if self.arduino: - self.selected_pid = self.config_combobox.get() - if self.selected_pid in self.pid_params: - self.pid_flag = True - else: - messagebox.showerror("Error", "Selected PID configuration not found.") - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def load_configurations(self): - try: - with open("conf.txt", "r") as file: - config_name = None - for line in file: - line = line.strip() - if line.startswith("#") or not line: - continue - if "{" in line: - config_name = line.split("{")[0].strip() - self.pid_params[config_name] = {"Kp": 0.0, "Ki": 0.0, "Kd": 0.0} - elif "}" in line: - config_name = None - else: - if config_name: - key, value = line.split(":") - key = key.strip() - value = round(float(value.strip()), 3) - self.pid_params[config_name][key] = value - - self.config_combobox.configure(values=list(self.pid_params.keys())) - if self.pid_params: - self.config_combobox.set(list(self.pid_params.keys())[0]) - self.selected_pid = list(self.pid_params.keys())[0] - except Exception as e: - messagebox.showerror("Error", f"Error loading configurations: {e}") - - def connect_arduino(self): - com_port = self.combobox_value.get() - if com_port: - try: - self.arduino = serial.Serial(com_port, 115200, timeout=1) - messagebox.showinfo("Info", "Connected to Arduino on " + com_port) - except Exception as e: - messagebox.showerror("Error", str(e)) - else: - messagebox.showwarning("Warning", "Please select a COM port") - - def set_setpoint(self): - if self.arduino: - try: - self.setpoint = float(self.setpoint_entry.get()) * 1000 - self.setpoint_flag = True - except ValueError: - messagebox.showerror("Error", "Invalid setpoint value.") - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def send_multi_setpoints(self): - if self.arduino: - setpoints = self.multi_setpoints_text.get("1.0", tk.END).strip().split("\n") - coordinates = [] - for point in setpoints: - try: - angle, torque = map(float, point.split(";")) - coordinates.append((angle, torque)) - except ValueError: - messagebox.showerror("Error", f"Invalid input: {point}") - return - - coordinates.sort() - self.setpoints = coordinates - self.plot_coordinates(coordinates) - self.multi_setpoints_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def plot_coordinates(self, coordinates): - angles, torques = zip(*coordinates) - self.ax.clear() - self.ax.plot(angles, torques, 'bo-', label="Setpoints") - self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie") - self.ax.set_xlabel("Winkel (°)") - self.ax.set_ylabel("Drehmoment (Nm)") - self.ax.set_xlim(0, 90) - self.ax.set_ylim(0, 50) - self.ax.legend() - self.canvas.draw() - - def interpolate_coordinates(self, coordinates): - interpolated_points = [] - for i in range(len(coordinates) - 1): - start_angle, start_torque = coordinates[i] - end_angle, end_torque = coordinates[i + 1] - num_points = int((end_angle - start_angle) * 2) - angles = np.linspace(start_angle, end_angle, num_points + 1) - torques = np.linspace(start_torque, end_torque, num_points + 1) - interpolated_points.extend(zip(angles, torques)) - - full_interpolated_points = [(angle, 0) for angle in np.arange(0, 90.5, 0.5)] - for angle, torque in interpolated_points: - index = int(angle * 2) - full_interpolated_points[index] = (angle, torque) - - return full_interpolated_points - - def send_interpolated_points(self): - if self.arduino: - setpoints_command = "u" - for angle, torque in self.setpoints: - torque_mnm = torque * 1000 - setpoints_command += f"{int(angle * 2)},{torque_mnm:.1f};" - setpoints_command = setpoints_command.rstrip(';') + "u\n" - self.arduino.write(setpoints_command.encode()) - - timeout_ctr = time.time() - while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): - pass - - try: - print(self.arduino.readline()) - - except Exception as e: - print(e) - - def tare_angle(self): - if self.arduino: - self.tare_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def demagnetize(self): - if self.arduino: - self.demag_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def input_source_Switch(self): - if self.arduino: - self.input_source_flag = True - else: - messagebox.showwarning("Warning", "Arduino not connected") - - def communication_loop(self): - while self.running: - if self.arduino: - self.arduino.read_all() - if self.setpoint_flag: - self.arduino.write(f"s{self.setpoint:.0f},0\n".encode()) - self.setpoint_flag = False - time.sleep(0.1) - if self.multi_setpoints_flag: - self.send_interpolated_points() - self.multi_setpoints_flag = False - time.sleep(0.1) - if self.tare_flag: - self.arduino.write(b'w\n') - self.tare_flag = False - time.sleep(0.1) - if self.demag_flag: - self.arduino.write(b'e\n') - self.demag_flag = False - time.sleep(0.1) - if self.input_source_flag: - self.arduino.write(b'S\n') - self.input_source_flag = False - time.sleep(0.1) - if self.pid_flag: - self.send_pid_parameters() - self.pid_flag = False - - self.arduino.write(b'a\n') - timeout_ctr = time.time() - while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2): - pass - - try: - data = self.arduino.readline().decode(errors='ignore').strip().split(";") - self.current_angle = float(data[0]) / 1000 - self.current_torque = float(data[1]) / 1000 - self.analogInput = float(data[2]) / 1000 - self.currentSetpoint = float(data[3]) / 1000 - - self.angle_label_var.set(f"Drehwinkel: {self.current_angle:.1f} °") - self.torque_label_var.set(f"Istwert: {self.current_torque:.1f} Nm") - self.analogread_label_var.set(f"Analogeingang: {self.analogInput:.1f} V") - self.currentSetpoint_label_var.set(f"Aktueller Sollwert: {self.currentSetpoint:.1f} Nm") - - if self.current_point is not None: - self.current_point.remove() - self.current_point, = self.ax.plot([self.current_angle], [self.current_torque], 'ro') - self.canvas.draw() - except Exception as e: - print(e) - - time.sleep(0.1) - - def send_pid_parameters(self): - if self.selected_pid in self.pid_params: - pid_values = self.pid_params[self.selected_pid] - kp = f"{pid_values['Kp']:.3f}" - ki = f"{pid_values['Ki']:.3f}" - kd = f"{pid_values['Kd']:.3f}" - self.arduino.write(f"p{kp}\n".encode()) - time.sleep(0.1) - self.arduino.write(f"i{ki}\n".encode()) - time.sleep(0.1) - self.arduino.write(f"d{kd}\n".encode()) - messagebox.showinfo("Info", f"PID parameters sent: Kp={kp}, Ki={ki}, Kd={kd}") - else: - messagebox.showerror("Error", "Selected PID configuration not found.") - - def on_closing(self): - self.running = False - if self.arduino: - self.arduino.close() - self.root.quit() - self.root.destroy() - -if __name__ == "__main__": - root = customtkinter.CTk() - app = ArduinoGUI(root) - root.protocol("WM_DELETE_WINDOW", app.on_closing) - root.mainloop()