import tkinter as tk from tkinter import ttk, messagebox 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.root = root self.root.title("Arduino GUI") 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.create_widgets() # Start the communication thread self.communication_thread = threading.Thread(target=self.communication_loop) self.communication_thread.start() def create_widgets(self): self.com_label = tk.Label(self.root, text="Select COM Port:") self.com_label.grid(row=0, column=0) self.combobox_value = tk.StringVar() self.combobox = ttk.Combobox(self.root, textvariable=self.combobox_value) self.combobox['values'] = [port.device for port in self.ports] self.combobox.grid(row=0, column=1) self.connect_button = tk.Button(self.root, text="Connect", command=self.connect_arduino) self.connect_button.grid(row=0, column=2) self.setpoint_label = tk.Label(self.root, text="Setpoint (Nm):") self.setpoint_label.grid(row=1, column=0) self.setpoint_entry = tk.Entry(self.root) self.setpoint_entry.grid(row=1, column=1) self.set_setpoint_button = tk.Button(self.root, text="Set", command=self.set_setpoint) self.set_setpoint_button.grid(row=1, column=2) self.multi_setpoints_label = tk.Label(self.root, text="Setpoints (angle;torque):") self.multi_setpoints_label.grid(row=2, column=0) self.multi_setpoints_text = tk.Text(self.root, height=5, width=20) self.multi_setpoints_text.grid(row=2, column=1) self.send_multi_setpoints_button = tk.Button(self.root, text="Send Setpoints", command=self.send_multi_setpoints) self.send_multi_setpoints_button.grid(row=2, column=2) self.tare_button = tk.Button(self.root, text="Tare Angle", command=self.tare_angle) self.tare_button.grid(row=3, column=2) self.demag_button = tk.Button(self.root, text="Demagnetize", command=self.demagnetize) self.demag_button.grid(row=4, column=2) self.exit_button = tk.Button(self.root, text="Exit", command=self.on_closing) self.exit_button.grid(row=5, column=2) self.figure, self.ax = plt.subplots() self.canvas = FigureCanvasTkAgg(self.figure, self.root) self.canvas.get_tk_widget().grid(row=6, column=0, columnspan=3) 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.current_angle = 0 self.current_torque = 0 self.angle_label_var = tk.StringVar() self.angle_label_var.set("Current Angle: 0") self.angle_label = tk.Label(self.root, textvariable=self.angle_label_var) self.angle_label.grid(row=7, column=0) self.torque_label_var = tk.StringVar() self.torque_label_var.set("Current Torque: 0") self.torque_label = tk.Label(self.root, textvariable=self.torque_label_var) self.torque_label.grid(row=7, column=1) 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 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) 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 print(data[2]) self.angle_label_var.set(f"Current Angle: {self.current_angle}") self.torque_label_var.set(f"Current Torque: {self.current_torque}") 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 on_closing(self): self.running = False if self.arduino: self.arduino.close() self.root.quit() self.root.destroy() if __name__ == "__main__": root = tk.Tk() app = ArduinoGUI(root) root.protocol("WM_DELETE_WINDOW", app.on_closing) root.mainloop()