mirror of
https://gitlab.ewi.tudelft.nl/ee2l1/2025-2026/A.K.03.git
synced 2025-12-12 16:00:56 +01:00
Merge branch 'Module4' into 'main'
UI Code + Some progress on Module 4 See merge request ee2l1/2025-2026/A.K.03!6
This commit is contained in:
commit
6f99f06f5a
File diff suppressed because one or more lines are too long
@ -1,13 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"\n",
|
|
||||||
"[Table of Contents](0_Table_of_Contents.ipynb)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
|
|||||||
0
student_code/.editorconfig
Normal file
0
student_code/.editorconfig
Normal file
91
student_code/car.py
Normal file
91
student_code/car.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
from serial import Serial
|
||||||
|
#from Manual.KITT_Simulator.serial_simulator import Serial
|
||||||
|
|
||||||
|
|
||||||
|
class KITT:
|
||||||
|
current_speed = 150
|
||||||
|
current_angle = 150
|
||||||
|
beacon_state = False
|
||||||
|
|
||||||
|
def __init__(self, port, baudrate=115200):
|
||||||
|
# Initialize the serial connection
|
||||||
|
# self.serial = Serial(port, baudrate, rtscts=True)
|
||||||
|
self.serial = Serial(port, baudrate,rtscts=True)
|
||||||
|
# Initialize beacon parameters here using send_command
|
||||||
|
# Set carrier frequency, bit frequency, repetition count, and code pattern
|
||||||
|
carrier_frequency_value = 10000
|
||||||
|
carrier_frequency = carrier_frequency_value.to_bytes(2, byteorder='big')
|
||||||
|
self.send_command(b"F"+carrier_frequency+b"\n")
|
||||||
|
|
||||||
|
|
||||||
|
bit_frequency_value = 5000
|
||||||
|
bit_frequency = bit_frequency_value.to_bytes(2, byteorder='big')
|
||||||
|
self.send_command(b"B"+bit_frequency+b"\n")
|
||||||
|
|
||||||
|
|
||||||
|
desired_repetition_frequency = 2 # Replace with your value
|
||||||
|
repetition_count_value = bit_frequency_value // desired_repetition_frequency
|
||||||
|
repetition_count = repetition_count_value.to_bytes(2, byteorder='big')
|
||||||
|
# Ensure repetition_count_value is at least 32
|
||||||
|
repetition_count_value = max(repetition_count_value, 32)
|
||||||
|
repetition_count = repetition_count_value.to_bytes(2, byteorder='big')
|
||||||
|
self.send_command(b"R"+repetition_count+b"\n")
|
||||||
|
|
||||||
|
code_value = 0x67676767 # Replace with your code
|
||||||
|
code = code_value.to_bytes(4, byteorder='big')
|
||||||
|
self.send_command(b"C"+code+b"\n")
|
||||||
|
|
||||||
|
def send_command(self, command):
|
||||||
|
# Send the command string over the serial connection
|
||||||
|
self.serial.write(command)
|
||||||
|
#print(command)
|
||||||
|
|
||||||
|
def set_speed(self, speed):
|
||||||
|
# Send the motor speed command using send_command
|
||||||
|
# Use the format 'M<speed>\n'
|
||||||
|
self.send_command(f"M{speed}\n".encode())
|
||||||
|
self.current_speed = speed
|
||||||
|
|
||||||
|
def get_speed(self):
|
||||||
|
return self.current_speed
|
||||||
|
|
||||||
|
def set_angle(self, angle):
|
||||||
|
# Send the steering angle command using send_command
|
||||||
|
# Use the format 'D<angle>\n'
|
||||||
|
self.send_command(f"D{angle}\n".encode())
|
||||||
|
self.current_angle = angle
|
||||||
|
|
||||||
|
def get_angle(self):
|
||||||
|
return self.current_angle
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
# Stop the car by setting speed and angle to neutral (150)
|
||||||
|
# Call set_speed and set_angle with 150
|
||||||
|
self.set_speed(150)
|
||||||
|
self.set_angle(150)
|
||||||
|
|
||||||
|
def start_beacon(self):
|
||||||
|
# Send commands to start the beacon
|
||||||
|
# Use the command 'A1\n'
|
||||||
|
self.send_command(b"A1\n")
|
||||||
|
|
||||||
|
def stop_beacon(self):
|
||||||
|
# Send commands to stop the beacon
|
||||||
|
# Use the command 'A0\n'
|
||||||
|
self.send_command(b"A0\n")
|
||||||
|
|
||||||
|
def toggle_beacon(self):
|
||||||
|
if self.beacon_state:
|
||||||
|
self.beacon_state = False
|
||||||
|
self.stop_beacon()
|
||||||
|
else:
|
||||||
|
self.beacon_state = True
|
||||||
|
self.start_beacon()
|
||||||
|
|
||||||
|
def get_beacon_state(self):
|
||||||
|
return self.beacon_state
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
# Close the serial connection
|
||||||
|
# self.serial.close()
|
||||||
|
self.serial.close()
|
||||||
@ -1,7 +1,12 @@
|
|||||||
from serial import Serial
|
from serial import Serial
|
||||||
|
#from Manual.KITT_Simulator.serial_simulator import Serial
|
||||||
|
|
||||||
|
|
||||||
class KITT:
|
class KITT:
|
||||||
|
current_speed = 150
|
||||||
|
current_angle = 150
|
||||||
|
beacon_state = False
|
||||||
|
|
||||||
def __init__(self, port, baudrate=115200):
|
def __init__(self, port, baudrate=115200):
|
||||||
# Initialize the serial connection
|
# Initialize the serial connection
|
||||||
# self.serial = Serial(port, baudrate, rtscts=True)
|
# self.serial = Serial(port, baudrate, rtscts=True)
|
||||||
@ -33,19 +38,25 @@ class KITT:
|
|||||||
def send_command(self, command):
|
def send_command(self, command):
|
||||||
# Send the command string over the serial connection
|
# Send the command string over the serial connection
|
||||||
self.serial.write(command)
|
self.serial.write(command)
|
||||||
print(command)
|
#print(command)
|
||||||
|
|
||||||
def set_speed(self, speed):
|
def set_speed(self, speed):
|
||||||
# Send the motor speed command using send_command
|
# Send the motor speed command using send_command
|
||||||
# Use the format 'M<speed>\n'
|
# Use the format 'M<speed>\n'
|
||||||
self.send_command(f"M{speed}\n".encode())
|
self.send_command(f"M{speed}\n".encode())
|
||||||
|
self.current_speed = speed
|
||||||
|
|
||||||
|
def get_speed(self):
|
||||||
|
return self.current_speed
|
||||||
|
|
||||||
def set_angle(self, angle):
|
def set_angle(self, angle):
|
||||||
# Send the steering angle command using send_command
|
# Send the steering angle command using send_command
|
||||||
# Use the format 'D<angle>\n'
|
# Use the format 'D<angle>\n'
|
||||||
self.send_command(f"D{angle}\n".encode())
|
self.send_command(f"D{angle}\n".encode())
|
||||||
|
self.current_angle = angle
|
||||||
|
|
||||||
|
def get_angle(self):
|
||||||
|
return self.current_angle
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
# Stop the car by setting speed and angle to neutral (150)
|
# Stop the car by setting speed and angle to neutral (150)
|
||||||
@ -63,6 +74,17 @@ class KITT:
|
|||||||
# Use the command 'A0\n'
|
# Use the command 'A0\n'
|
||||||
self.send_command(b"A0\n")
|
self.send_command(b"A0\n")
|
||||||
|
|
||||||
|
def toggle_beacon(self):
|
||||||
|
if self.beacon_state:
|
||||||
|
self.beacon_state = False
|
||||||
|
self.stop_beacon()
|
||||||
|
else:
|
||||||
|
self.beacon_state = True
|
||||||
|
self.start_beacon()
|
||||||
|
|
||||||
|
def get_beacon_state(self):
|
||||||
|
return self.beacon_state
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
# Close the serial connection
|
# Close the serial connection
|
||||||
# self.serial.close()
|
# self.serial.close()
|
||||||
|
|||||||
174
student_code/ui.py
Normal file
174
student_code/ui.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import queue
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import tkinter as tk
|
||||||
|
from car import KITT
|
||||||
|
import serial.tools.list_ports
|
||||||
|
from pynput import keyboard
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
serial_selector = tk.Tk()
|
||||||
|
serial_selector.title("Select serial port")
|
||||||
|
|
||||||
|
|
||||||
|
ports = list(serial.tools.list_ports.comports())
|
||||||
|
length = len(ports) * 25
|
||||||
|
serial_selector.geometry(f"600x{length+50}")
|
||||||
|
selected_port = tk.StringVar()
|
||||||
|
|
||||||
|
for port in ports:
|
||||||
|
tk.Radiobutton(serial_selector,text = f"{port.device}",variable=selected_port,value= port.device).pack()
|
||||||
|
|
||||||
|
def select_port():
|
||||||
|
global selected_port
|
||||||
|
selected_port = selected_port.get()
|
||||||
|
serial_selector.destroy()
|
||||||
|
|
||||||
|
button = tk.Button(serial_selector, text="Select serial port", command=select_port)
|
||||||
|
button.pack()
|
||||||
|
serial_selector.mainloop()
|
||||||
|
|
||||||
|
car = KITT(f"{selected_port}")
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("Patatje Oorloog")
|
||||||
|
root.geometry("800x600")
|
||||||
|
|
||||||
|
speed_label = tk.Label(root, text=f"Speed {car.get_speed()}")
|
||||||
|
speed_label.grid(row = 0, column = 0)
|
||||||
|
|
||||||
|
speed_var = tk.StringVar()
|
||||||
|
speed_entry = tk.Entry(root, textvariable=speed_var)
|
||||||
|
speed_entry.grid(row = 0, column = 1)
|
||||||
|
|
||||||
|
def speed_btn():
|
||||||
|
global car
|
||||||
|
car.set_speed(speed_var.get())
|
||||||
|
speed_label.config(text = f"Speed {car.get_speed()}")
|
||||||
|
|
||||||
|
speed_set_btn = tk.Button(root, text="Set Speed", command=speed_btn)
|
||||||
|
speed_set_btn.grid(row = 0, column = 2)
|
||||||
|
|
||||||
|
angle_label = tk.Label(root, text=f"Angle {car.get_angle()}")
|
||||||
|
angle_label.grid(row = 1, column = 0)
|
||||||
|
|
||||||
|
angle_var = tk.StringVar()
|
||||||
|
angle_entry = tk.Entry(root, textvariable=angle_var)
|
||||||
|
angle_entry.grid(row=1, column=1)
|
||||||
|
|
||||||
|
|
||||||
|
def angle_btn():
|
||||||
|
global car
|
||||||
|
car.set_angle(angle_var.get())
|
||||||
|
angle_label.config(text=f"Speed {car.get_angle()}")
|
||||||
|
|
||||||
|
|
||||||
|
angle_set_btn = tk.Button(root, text="Set Angle", command=angle_btn)
|
||||||
|
angle_set_btn.grid(row=1, column=2)
|
||||||
|
|
||||||
|
beacon_label = tk.Label(root, text=f"Beacon {car.get_beacon_state()}")
|
||||||
|
beacon_label.grid(row = 2, column = 0)
|
||||||
|
|
||||||
|
def toggle_beacon_btn():
|
||||||
|
global car
|
||||||
|
car.toggle_beacon()
|
||||||
|
beacon_label.config(text = f"Beacon {car.get_beacon_state()}")
|
||||||
|
|
||||||
|
beacon_toggle = tk.Button(root, text="Toggle Beacon", command=toggle_beacon_btn)
|
||||||
|
beacon_toggle.grid(row = 2, column = 1)
|
||||||
|
|
||||||
|
def close_btn_func():
|
||||||
|
car.stop_beacon()
|
||||||
|
car.stop()
|
||||||
|
try:
|
||||||
|
car.close()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
root.destroy()
|
||||||
|
|
||||||
|
close_btn = tk.Button(root, text="Close", command=close_btn_func)
|
||||||
|
close_btn.grid(row = 10,column = 0)
|
||||||
|
|
||||||
|
# Thread-safe queue for passing keys from listener to Tkinter
|
||||||
|
key_queue = queue.Queue()
|
||||||
|
last_key = None
|
||||||
|
def start_listener():
|
||||||
|
"""Start pynput listener in a background thread."""
|
||||||
|
|
||||||
|
def on_press(key):
|
||||||
|
# Put a string representation of the key into the queue
|
||||||
|
global last_key
|
||||||
|
try:
|
||||||
|
key_queue.put(f"{key.char}")
|
||||||
|
if True: #not key == last_key :
|
||||||
|
if key.char == "w":
|
||||||
|
car.set_speed(165)
|
||||||
|
speed_label.config(text = f"Speed {car.get_speed()}")
|
||||||
|
if key.char == "s":
|
||||||
|
car.set_speed(140)
|
||||||
|
speed_label.config(text=f"Speed {car.get_speed()}")
|
||||||
|
if key.char == "a":
|
||||||
|
car.set_angle(200)
|
||||||
|
angle_label.config(text=f"Angle {car.get_angle()}")
|
||||||
|
if key.char == "d":
|
||||||
|
car.set_angle(100)
|
||||||
|
angle_label.config(text=f"Angle {car.get_angle()}")
|
||||||
|
#last_key = key
|
||||||
|
except AttributeError:
|
||||||
|
key_queue.put(str(key))
|
||||||
|
if str(key) == "Key.space":
|
||||||
|
car.toggle_beacon()
|
||||||
|
beacon_label.config(text=f"Beacon {car.get_beacon_state()}")
|
||||||
|
|
||||||
|
def on_release(key):
|
||||||
|
# Stop listener if ESC is pressed
|
||||||
|
if key == keyboard.Key.esc:
|
||||||
|
return False
|
||||||
|
try :
|
||||||
|
if key.char == "w":
|
||||||
|
car.set_speed(150)
|
||||||
|
speed_label.config(text=f"Speed {car.get_speed()}")
|
||||||
|
if key.char == "s":
|
||||||
|
car.set_speed(150)
|
||||||
|
speed_label.config(text=f"Speed {car.get_speed()}")
|
||||||
|
if key.char == "a":
|
||||||
|
car.set_angle(150)
|
||||||
|
angle_label.config(text=f"Angle {car.get_angle()}")
|
||||||
|
if key.char == "d":
|
||||||
|
car.set_angle(150)
|
||||||
|
angle_label.config(text=f"Angle {car.get_angle()}")
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def run_listener():
|
||||||
|
with keyboard.Listener(on_press=on_press,
|
||||||
|
on_release=on_release) as listener:
|
||||||
|
listener.join()
|
||||||
|
|
||||||
|
# Run listener in its own thread so it does not block Tkinter
|
||||||
|
t = threading.Thread(target=run_listener, daemon=True)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def poll_queue():
|
||||||
|
"""Check the queue for new keys and update the label."""
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
k = key_queue.get_nowait()
|
||||||
|
current = label_var.get()
|
||||||
|
label_var.set(f"Last key: {k}")
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
# Schedule next poll
|
||||||
|
root.after(10, poll_queue)
|
||||||
|
|
||||||
|
label_var = tk.StringVar(value="Press any key (global). ESC stops listener.")
|
||||||
|
label = tk.Label(root, textvariable=label_var, width=50)
|
||||||
|
label.grid(row = 9,column = 0)
|
||||||
|
|
||||||
|
start_button = tk.Button(root, text="Start listener", command=start_listener)
|
||||||
|
start_button.grid(row = 9, column = 1)
|
||||||
|
|
||||||
|
# Start polling the queue
|
||||||
|
root.after(10, poll_queue)
|
||||||
|
root.protocol("WM_DELETE_WINDOW", close_btn_func)
|
||||||
|
root.mainloop()
|
||||||
Loading…
x
Reference in New Issue
Block a user