251 lines
9.4 KiB
Python
251 lines
9.4 KiB
Python
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() |