I am currently working on software bootloader implementation and using Python to handle sending data from my PC and receiving acknowledgement signals. Problem that I stumbled upon is that I am getting this runtime error: Error waiting for acknowledgement: device reports readiness to read but returned no data (device disconnected or multiple access on port?) Here is my Python code(code for STM32F407 is not relevant): import time import tkinter as tk from tkinter import ttk, filedialog, messagebox import serial import serial.tools.list_ports import os class FirmwareUpdater: def __init__(self, root): self.root = root self.root.title("STM32F407 Firmware Upgrader") self.root.geometry("600x400") self.binary_file = None self.serial_port = None self.create_gui() def create_gui(self): # File Selection Frame file_frame = ttk.LabelFrame(self.root, text="Firmware File", padding="10") file_frame.pack(fill="x", padx=10, pady=5) self.file_path_var = tk.StringVar() ttk.Label(file_frame, textvariable=self.file_path_var).pack(side="left", fill="x", expand=True) ttk.Button(file_frame, text="Browse", command=self.browse_file).pack(side="right") # Serial Port Frame serial_frame = ttk.LabelFrame(self.root, text="Serial Port Settings", padding="10") serial_frame.pack(fill="x", padx=10, pady=5) ttk.Label(serial_frame, text="Port:").grid(row=0, column=0, padx=5) self.port_combo = ttk.Combobox(serial_frame, width=20) self.port_combo.grid(row=0, column=1, padx=5) # Progress Frame progress_frame = ttk.LabelFrame(self.root, text="Upload Progress", padding="10") progress_frame.pack(fill="x", padx=10, pady=5) self.progress_bar = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, length=300, mode='determinate') self.progress_bar.pack(fill="x", pady=5) self.status_var = tk.StringVar(value="Ready") ttk.Label(progress_frame, textvariable=self.status_var).pack() # Control Buttons button_frame = ttk.Frame(self.root) button_frame.pack(fill="x", padx=10, pady=5) ttk.Button(button_frame, text="Start", command=self.start_upload).pack(side="right", padx=5) # Initialize ports list self.get_ports() def get_ports(self): ports = ["/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"] self.port_combo['values'] = ports if ports: self.port_combo.set(ports[0]) def browse_file(self): filename = filedialog.askopenfilename( filetypes=[("Binary files", "*.bin")] ) if filename: self.binary_file = filename self.file_path_var.set(os.path.basename(filename)) def start_upload(self): if not self.binary_file: messagebox.showerror("Error", "Please select a firmware file first!") return if not self.port_combo.get(): messagebox.showerror("Error", "Please select a serial port!") return try: print("Starting upload process...") # Read binary file with open(self.binary_file, 'rb') as f: firmware_data = f.read() # Just open the port once self.serial_port = serial.Serial( port=self.port_combo.get(), baudrate=115200, timeout=2 ) # Start the upload process firmware_data = self.add_padding_bytes(firmware_data) self.upload_firmware(firmware_data) except Exception as e: messagebox.showerror("Error", f"An error occurred: {str(e)}") def get_metadata(self, firmware_data): preamble = bytes([0x53,0x54,0x4D,0x33,0x32,0x5F,0x42,0x4F,0x4F,0x54,0x4C,0x4F,0x41,0x44,0x45,0x52,0x5F,0x56,0x30,0x31]) firmware_size = len(firmware_data).to_bytes(4, 'little') checksum = self.calculate_checksum_metadata(preamble + firmware_size) return preamble + firmware_size + checksum def calculate_checksum_metadata(self, data): checksum = 0x00000000 for i in range(0, len(data), 4): word = int.from_bytes(data[i:i+4], byteorder='little') checksum ^= word return checksum.to_bytes(4, 'little') def calculate_checksum_firmware(self,data): checksum = 0x00000000 for byte in data: checksum ^= byte return checksum.to_bytes(4, 'little') def add_padding_bytes(self, data): padding_bytes = (64 - (len(data) % 64)) % 64 return data + b'\x00' * padding_bytes def send_hold_signal(self): self.serial_port.write(b'\x55\x66\x77\x88') print("Hold signal sent") def send_metadata(self, firmware_data): metadata = self.get_metadata(firmware_data) self.serial_port.write(metadata) def send_checksum(self,firmware_data): checksum = self.calculate_checksum_firmware(firmware_data) self.serial_port.write(checksum) def data_send(self, byte_val): self.serial_port.write(bytes([byte_val])) def msg_send(self, buf): # Calculate checksum checksum = self.calculate_checksum_firmware(buf) # Send escaped data bytes for byte_val in buf: self.data_send(byte_val) # Send escaped checksum bytes for chk_byte in checksum: self.data_send(chk_byte) def wait_for_acknowledgement(self, timeout=2): """ Wait for acknowledgement from bootloader aram timeout: Timeout in seconds :return: True if ACK received, False otherwise """ try: # Set a timeout on the serial port self.serial_port.timeout = timeout # Read response response = self.serial_port.read(1) # Check for ACK signal (typically 'A') if response == b'A': print("Acknowledgement received") return True else: print(f"Unexpected response: {response}") return False except Exception as e: print(f"Error waiting for acknowledgement: {e}") return False finally: # Reset timeout to default self.serial_port.timeout = None def upload_firmware(self, firmware_data): """ Upload firmware using simplified acknowledgement protocol """ # Send hold signal and wait for ACK self.send_hold_signal() hold_ack = self.wait_for_acknowledgement() if not hold_ack: raise RuntimeError("Did not receive ACK for hold signal") # Send metadata and wait for ACK self.send_metadata(firmware_data) metadata_ack = self.wait_for_acknowledgement() if not metadata_ack: raise RuntimeError("Did not receive ACK for metadata") chunk_size = 64 total_chunks = (len(firmware_data) + chunk_size - 1) // chunk_size # Configure progress bar self.progress_bar['maximum'] = total_chunks self.progress_bar['value'] = 0 self.status_var.set("Uploading firmware...") for chunk_index in range(total_chunks): # Prepare chunk start = chunk_index * chunk_size end = min(start + chunk_size, len(firmware_data)) chunk = firmware_data[start:end] # Send chunk transmission_successful = False max_retries = 3 retry_count = 0 while not transmission_successful and retry_count < max_retries: try: # Send message self.msg_send(chunk) # Wait for chunk acknowledgement chunk_ack = self.wait_for_acknowledgement() if chunk_ack: transmission_successful = True # Update progress bar self.progress_bar['value'] = chunk_index + 1 # Update status text self.status_var.set(f"Uploading: {chunk_index + 1}/{total_chunks} chunks") # Force GUI update self.root.update_idletasks() print(f"Chunk {chunk_index + 1}/{total_chunks} sent successfully") else: retry_count += 1 print(f"Error receiving ACK for chunk {chunk_index + 1}. Retrying...") except Exception as e: retry_count += 1 print(f"Error sending chunk {chunk_index + 1}: {e}") # Check if max retries exceeded if not transmission_successful: self.status_var.set("Upload failed") raise RuntimeError(f"Failed to send chunk {chunk_index + 1} after {max_retries} attempts") # Upload complete self.progress_bar['value'] = total_chunks self.status_var.set("Firmware upload complete") if __name__ == "__main__": root = tk.Tk() app = FirmwareUpdater(root) root.mainloop() Continue reading...