0

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
        
        :param 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()
3
  • always put full error message because there are other useful information. Commented Mar 30 at 12:10
  • maybe first run code without try/except to get more FULL error message which can better explain where is problem Commented Mar 30 at 12:11
  • Thanks for the tips, I figured out the error. Basically it was because I had the same serial port open in the Terminal too Commented Apr 7 at 13:42

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.