import os import json import subprocess import tkinter as tk from tkinter import ttk from tkinter import filedialog, messagebox import serial.tools.list_ports import sys from mpremote.main import main as mpremote_main 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.label = tk.Label(master, text="Searching for board...") self.label.pack(pady=10) self.port, self.board_name = self.find_supported_port() if self.port: self.label.config(text=f"{self.board_name} detected on {self.port}") else: self.label.config(text="No supported board 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_supported_port(self): # List of supported boards (VID, PID, Name) supported_boards = [ (0x239A, 0x80F2, "RP2040"), (0x2E8A, 0x0009, "RP2350"), ] ports = serial.tools.list_ports.comports() for port in ports: for vid, pid, name in supported_boards: if port.vid == vid and port.pid == pid: return port.device, name return None, 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() # Save original sys.argv and replace it with mpremote-style args original_argv = sys.argv sys.argv = ["mpremote", "connect", self.port, "fs", "cp", full_path, f":{filename}"] try: mpremote_main() except SystemExit as e: success = False messagebox.showerror("Upload Failed", f"Failed to upload {filename} (mpremote exited with code {e.code}).") break finally: sys.argv = original_argv # Always restore original argv if success: self.label.config(text="DONE") self.master.update() messagebox.showinfo("Success", "All files uploaded successfully!") if __name__ == "__main__": root = tk.Tk() app = RP2040UploaderApp(root) root.title("RP Loader V1.1.0") 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 06/10/2025", anchor="e", justify="right", font=("TkDefaultFont", 7, "bold")) footer_label.pack(side=tk.RIGHT, padx=10, pady=5) root.mainloop()