import os import json import subprocess import tkinter as tk from tkinter import ttk from tkinter import filedialog, messagebox import serial.tools.list_ports class RP2040UploaderApp: def __init__(self, master): self.master = master self.folder_path = os.path.join(os.getcwd(), "Resources/RP2040") self.config_path = os.path.join(self.folder_path, "config.json") self.mpremote_path = os.path.join(os.getcwd(), "Resources", "mpremote.exe") self.label = tk.Label(master, text="Searching for RP2040...") self.label.pack(pady=10) self.port = self.find_rp2040_port() if self.port: self.label.config(text=f"RP2040 detected on {self.port}") else: self.label.config(text="RP2040 not found.") # Config entry fields self.ip_entry = self.create_labeled_entry("IP Address:") self.subnet_entry = self.create_labeled_entry("Subnet Mask:") self.gateway_entry = self.create_labeled_entry("Gateway:") self.load_config() self.upload_button = ttk.Button(master, text="Upload Files", style="Accent.TButton", command=self.upload_files, state=tk.NORMAL if self.port else tk.DISABLED) self.upload_button.pack(pady=10) def create_labeled_entry(self, label_text): frame = tk.Frame(self.master) frame.pack(pady=2) label = tk.Label(frame, text=label_text) label.pack(side=tk.LEFT) entry = tk.Entry(frame, width=20) entry.pack(side=tk.RIGHT) return entry def find_rp2040_port(self): target_vid = 0x239A target_pid = 0x80F2 ports = serial.tools.list_ports.comports() for port in ports: if port.vid == target_vid and port.pid == target_pid: return port.device return None def load_config(self): if os.path.isfile(self.config_path): try: with open(self.config_path, "r") as f: config = json.load(f) self.ip_entry.insert(0, config.get("ip", "")) self.subnet_entry.insert(0, config.get("subnet", "")) self.gateway_entry.insert(0, config.get("gateway", "")) except Exception as e: messagebox.showerror("Error", f"Failed to read config.json:\n{e}") else: messagebox.showwarning("Missing File", f"No config.json found in {self.folder_path}") def save_config(self): config = { "ip": self.ip_entry.get(), "subnet": self.subnet_entry.get(), "gateway": self.gateway_entry.get() } try: with open(self.config_path, "w") as f: json.dump(config, f, indent=4) except Exception as e: messagebox.showerror("Error", f"Failed to save config.json:\n{e}") def upload_files(self): if not os.path.isdir(self.folder_path): messagebox.showerror("Error", f"Folder '{self.folder_path}' not found.") return self.save_config() success = True for filename in os.listdir(self.folder_path): full_path = os.path.join(self.folder_path, filename) if os.path.isfile(full_path): self.label.config(text=f"Uploading {filename}...") self.master.update() result = subprocess.run( [self.mpremote_path, "connect", self.port, "fs", "cp", full_path, f":{filename}"], capture_output=True, text=True ) if result.returncode != 0: success = False messagebox.showerror("Upload Failed", f"Failed to upload {filename}:\n{result.stderr}") break if success: self.label.config(text=f"DONE") self.master.update() messagebox.showinfo("Success", "All files uploaded successfully!") if __name__ == "__main__": root = tk.Tk() app = RP2040UploaderApp(root) root.title("RP2040 Loader V1.0.1") root.iconbitmap(r'Resources/icon1.ico') # Simply set the theme root.tk.call("source", "Resources/azure.tcl") root.tk.call("set_theme", "dark") # Set a minsize for the window, and place it in the middle root.update() root.minsize(400, 200) # Footer label in bottom right footer_frame = tk.Frame(root) footer_frame.pack(side=tk.BOTTOM, fill=tk.X) footer_label = tk.Label(footer_frame, text="Made by Hendo 22/07/2025", anchor="e", justify="right", font=("TkDefaultFont", 7, "bold")) footer_label.pack(side=tk.RIGHT, padx=10, pady=5) root.mainloop()