AVRHexFlashGUI: an AVRdude Hex Flasher GUI Application with Quick Flash Floating Button

Posted

in

by

Tags:

Introduction

To program the AVR microcontroller you use “avrdude” to flash the hex files in the microcontroller. avrdude is a command line tool. Every time you have to flash the microcontroller with a new file you have to write the command.
This AVRHexFlashGUI uses the avrdude software to flash the microcontroller but it provides a Graphical User Interface.

There is a special feature I have implemented which is “QuickSet” Toolbar which has a “Quick FLASH” button. This toolbar floats on top of the screen. And provides an easy-to-use button for flashing the firmware files. It has an indicator that turns it’s color to green if the programming is successful or turns red if the programming is unsuccessful. It also has an ‘X’ button to switch off this toolbar.
Software such as microchip mplab x ide does not have an option to integrate the avrdude commands like in microchip studio(formerly Atmel Studio). This toolbar provides an easy access to flashing. You do not have to do anything else. Just launch the program and enable the QuickSet toolbar.

AVR microcontrollers are widely used in embedded systems development due to their versatility and ease of programming and also their low cost. Flashing firmware or code onto these microcontrollers is a fundamental task during development. The AVRHexFlashGUI is a graphical user interface (GUI) application built using the Tkinter library in Python.

Note: avrdude software must be installed on your system.
Download avrdude for windows https://github.com/avrdudes/avrdude/releases

Download

Screenshots

Overview

The AVRHexFlashGUI application streamlines the process of flashing firmware onto AVR microcontrollers using a user-friendly interface. It offers the following features:

  1. Microcontroller Selection: The application allows users to select the target microcontroller from a list of supported devices.
  2. Programmer Configuration: Users can specify the programmer to be used for flashing the firmware. This information is crucial for establishing a connection between the computer and the microcontroller.
  3. HEX File Selection: Users can browse their computer for the HEX file containing the firmware they want to flash onto the microcontroller.
  4. Quick Flash: The application provides a quick flash button that initiates the flashing process using the selected programmer, microcontroller, and HEX file.
  5. Read the Microcontroller Flash Memory: The user can read the unprotected flash memory and save it in the program folder as “HexFromDevice.hex”
  6. AVR Fuse Settings: The application offers the ability to read and display the current fuse settings of the connected AVR microcontroller. Users can also write new fuse settings if needed.
  7. Output Display: The application displays the output of the avrdude tool, which is responsible for flashing the firmware and handling the communication with the microcontroller.

Usage

To use the AVRHexFlashGUI application, follow these steps:

  1. Launch the application.
  2. Select the target microcontroller.
  3. Specify the programmer to be used for flashing.
  4. Load the desired HEX file by clicking the “Browse” button.
  5. Click the “Program AVR” button to start the flashing process.
  6. Monitor the avrdude output to ensure successful flashing.
  7. Optionally, use the “Fuse Setting” section to read, modify, and write fuse settings.
AVRHexFlash Quick Flash Toolbar with MPLAB X IDE

Conclusion

The AVRHexFlashGUI application simplifies the process of flashing firmware onto AVR microcontrollers by providing an intuitive and user-friendly interface. With features for microcontroller selection, programmer configuration, HEX file loading, and fuse settings, developers can efficiently program their microcontrollers. The use of Tkinter and Python makes it easy to create and customize the GUI, enhancing the overall user experience. This application is a valuable tool for both beginners and experienced developers working with AVR microcontrollers. By streamlining the flashing process, it helps save time and ensures accurate firmware deployment.

Read more: AVRHexFlashGUI: an AVRdude Hex Flasher GUI Application with Quick Flash Floating Button

Code

from tkinter import *
from tkinter import ttk
import serial
import serial.tools.list_ports
import datetime
import os
import sys
import subprocess
from tkinter import filedialog
# Get the directory of the script
script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))

# Change the working directory to the script's directory
os.chdir(script_dir)
# For debug messages to be printed
dbg_msg = 0

root = Tk()
root.title("AVRHexFlashGUI")
root.resizable(False, False)  # Prevent window from being resized
sty = ttk.Style()
sty.theme_use("default")
#print(sty.theme_names())
root.columnconfigure(0, weight = 1)
root.rowconfigure(0, weight = 1)
# Frame Define START
Frame00 = ttk.LabelFrame(root, text = "AVRHexFlashGUI", padding = 10)
Frame00.grid(column = 0, row = 0, sticky =  (W,E) )
Frame01 = ttk.LabelFrame(root, text = "Fuse Setting", padding = 10)
Frame01.grid(column = 2, row = 0, sticky =  (W,E) )
## Frame Define START
canvas = None
circle = None
# Frame00 START
label0011 = ttk.Label(Frame00, text = "Microcontroller")
label0011.grid(column = 0, row = 0, sticky = (N,E,W,S))

MCU_save_stored_value = StringVar()
MCU_save_stored_value_display = Text(Frame00, height = 1, width = 2, wrap=WORD)
MCU_save_stored_value_display.grid(row = 0, column = 1, sticky = (N,E,W,S))
# Display the file value
file_path = os.path.join("Data", "MCU_save.txt")
if os.path.exists(file_path):
    with open(file_path, 'r') as file:
        MCU_value = file.read()
        MCU_save_stored_value_display.insert(END, MCU_value)

programmer_label = ttk.Label(Frame00, text="Programmer:")
programmer_label.grid(row = 0, column  = 2, sticky = (N,E,W,S))
programmer_var = StringVar()
programmer_var_display = Text(Frame00, height = 1, width = 10, wrap=WORD)
programmer_var_display.grid(row = 0, column = 3, sticky = (N,E,W,S))
# Display the file value
file_path = os.path.join("Data", "avrdude_programmer.txt")
if os.path.exists(file_path):
    with open(file_path, 'r') as file:
        Prog_value = file.read()
        programmer_var_display.insert(END, Prog_value)


label0010 = ttk.Label(Frame00, text="Select HEX File:")
label0010.grid(row = 1, column  = 0, sticky = (N,E,W,S))
hex_file_path = StringVar()
hex_file_path.set("Load your File")

def browse_file():
    file_path = filedialog.askopenfilename(filetypes=[("HEX Files", "*.hex")])
    if file_path:
        hex_file_path.delete(1.0, END)  # Clear previous text
        hex_file_path.insert(END, file_path)
        hex_file_path.config(wrap=WORD)  # Enable text wrapping

    new_complete_command = ">> avrdude " + "-c " + programmer_var_display.get('1.0', 'end-1c') \
                           + " -p " + MCU_save_stored_value_display.get('1.0', 'end-1c') \
                           + " -U " + "flash:w:" + hex_file_path.get('1.0', 'end-1c') + ":i"
    complete_command_var.set(new_complete_command)

def copy_to_clipboard():
    start_index = hex_file_path.index(SEL_FIRST)
    end_index = hex_file_path.index(SEL_LAST)
    
    if start_index and end_index:
        selected_text = hex_file_path.get(start_index, end_index)
        root.clipboard_clear()
        root.clipboard_append(selected_text)
        root.update()
def save_to_file():
    file_path = os.path.join("Data", "Hex_save_location.txt")
    with open(file_path, 'w') as file:
        file.write(hex_file_path.get('1.0', 'end-1c'))
    file_path = os.path.join("Data", "avrdude_programmer.txt")
    with open(file_path, 'w') as file:
        file.write(programmer_var_display.get('1.0', 'end-1c'))
    file_path = os.path.join("Data", "MCU_save.txt")
    with open(file_path, 'w') as file:
        file.write(MCU_save_stored_value_display.get('1.0', 'end-1c'))

def getMcuList():    
    prog_name = programmer_var_display.get('1.0','end-1c')
    command = ['avrdude','-c',prog_name]
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    
    output_lines = result.stderr.split('\n')
    for line in output_lines:
        display_avrdude_output.insert(END, line + '\n')
    display_avrdude_output.see("end")

def DumpHex():    
    prog_name = programmer_var_display.get('1.0','end-1c')
    part_name = MCU_save_stored_value_display.get('1.0','end-1c')
    command = ['avrdude','-c',prog_name,'-p',part_name,'-U', 'flash:r:HexFromDevice.hex:i']
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    display_avrdude_output.delete('1.0',END)
    output_lines = result.stderr.split('\n')
    for line in output_lines:
        display_avrdude_output.insert(END, line + '\n')
    display_avrdude_output.see("end")    
    
button0011 = ttk.Button(Frame00, text="Browse", command=browse_file)
button0011.grid(row = 1, column  = 1,sticky = (N,E,W,S))

McuListbutton = ttk.Button(Frame00, text="Get Supported\nMCU List", command=getMcuList)
McuListbutton.grid(row = 1, column  = 3,sticky = (N,E,W,S))
DumpHexbutton = ttk.Button(Frame00, text="Read Device\nSave File", command=DumpHex)
DumpHexbutton.grid(row = 1, column  = 2,sticky = (N,E,W,S))

hex_file_path = Text(Frame00, height=5, width=50, wrap=WORD)
hex_file_path.grid(row=2, column=0, columnspan=2, sticky=(N,E,W,S))
# Display the file value
file_path = os.path.join("Data", "Hex_save_location.txt")
if os.path.exists(file_path):
    with open(file_path, 'r') as file:
        Hex_file_value = file.read()
        hex_file_path.insert(END, Hex_file_value)
# Bind right-click context menu to hex_file_path
context_menu = Menu(hex_file_path, tearoff=0)
context_menu.add_command(label="Copy", command=copy_to_clipboard)
hex_file_path.bind("<Button-3>", lambda event: context_menu.post(event.x_root, event.y_root))
button_save = ttk.Button(Frame00, text="Save to File", command=save_to_file)
button_save.grid(row=5, column=0, sticky=(N,E,W,S))


complete_command_var = StringVar()
Complete_Command = ">> avrdude "+"-c "+programmer_var_display.get('1.0','end-1c') \
                   +" -p "+MCU_save_stored_value_display.get('1.0','end-1c')\
                   +" -U "+"flash:w:"+hex_file_path.get('1.0','end-1c')+":i"
complete_command_var.set(Complete_Command)
display_complete_command = ttk.Label(Frame00, wraplength=500, textvariable= complete_command_var)
display_complete_command.grid(row=4, column=0, sticky=(N,E,W,S))


def program_avr():
    display_avrdude_output.delete('1.0',END)
    prog_name = programmer_var_display.get('1.0','end-1c')
    part_name = MCU_save_stored_value_display.get('1.0','end-1c')
    hex_file_address = hex_file_path.get('1.0','end-1c')
    flash_statement = "flash:w:"+hex_file_address+":i"
    
    command = ['avrdude','-c',prog_name,'-p',part_name,'-U',flash_statement]
    print(command)
    
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    
    # Analyze the result and indicate success or failure
    output_lines = result.stderr.split('\n')
    success_indication = "flash verified\n\navrdude done.  Thank you."
    success_indication_1 = "flash verified\n\navrdude: safemode: Fuses OK\n\navrdude done.  Thank you."
    success = success_indication in result.stderr or success_indication_1 in result.stderr

    for line in output_lines:
        display_avrdude_output.insert(END, line + '\n')

    display_avrdude_output.see("end")

    if success:
        print("Programming successful!")
        try:
            canvas.itemconfig(circle, fill='#aaff00')
        except:
            pass
        
    else:
        print("Programming failed.")
        try:
            canvas.itemconfig(circle, fill='red')
        except:
            pass
        

sty.configure("Color.TButton", background="blue", foreground="white")
button0021 = ttk.Button(Frame00,style='Color.TButton', text="Program AVR", command=program_avr)
button0021.grid(row = 5, column  = 1)

display_avrdude_output = Text(Frame00, height=10, width=50, wrap=WORD)
display_avrdude_output.grid(row=6, columnspan=4, sticky=(N,E,W,S))
scrollbar = Scrollbar(Frame00, command=display_avrdude_output.yview)
scrollbar.grid(row=6, column=4, sticky=(N, S))
display_avrdude_output.config(yscrollcommand=scrollbar.set)

Abhay_text = "This program is writtent by : ABHAY KANT\nvisit: https://exasub.com"
AbhayLabel = ttk.Label(Frame00, text=Abhay_text)
AbhayLabel.grid(row = 11, column  = 0, sticky = (N,E,W,S))
## Frame00 END

def donothing():
   filewin = Toplevel(root)
   button = Button(filewin, text="Do nothing button")
   button.pack()

about_window = None  
def About_me():
    global about_window
    
    if about_window is None or not about_window.winfo_exists():
        about_window = Toplevel(root)
        about_window.title("About Me")
        
        label1 = ttk.Label(about_window, text="EXASUB.COM")
        label1.pack()
        
        button = ttk.Button(about_window, text="Quit", command=about_window.destroy)
        button.pack()
    else:
        about_window.lift()  

hfuse_read_value_display = None
lfuse_read_value_display = None
def Read_fuse():
    hfuse_read_value_display.delete('1.0',END)
    lfuse_read_value_display.delete('1.0',END)
    prog_name = programmer_var_display.get('1.0','end-1c')
    part_name = MCU_save_stored_value_display.get('1.0','end-1c')
    
    hfuse_statement = "hfuse:r:-:h"
    lfuse_statement = "lfuse:r:-:h"
    
    command = ['avrdude','-c',prog_name,'-p',part_name,'-U',hfuse_statement]
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    hfuse_read_value_display.insert(END,result.stdout)
    
    command = ['avrdude','-c',prog_name,'-p',part_name,'-U',lfuse_statement]
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    lfuse_read_value_display.insert(END,result.stdout)

    
fusesetwin = None
fusesetwin_write = None
def FuseSet():
    global fusesetwin,hfuse_read_value_display,lfuse_read_value_display
    if fusesetwin is None or not fusesetwin.winfo_exists():
            
        fusesetwin = Toplevel(root)
        
        button = Button(fusesetwin, text="Quit", command = fusesetwin.destroy)
        button.grid(column = 0, row = 0, sticky = (N,E,W,S))
        Read_Fuse_label = ttk.Label(fusesetwin, text="Read Fuse Values")
        Read_Fuse_label.grid( row = 1,column = 0, sticky = (N,E,W,S))

        read_fuse_button = Button(fusesetwin, text="Read", command = Read_fuse)
        read_fuse_button.grid(column = 1, row = 1, sticky = (N,E,W,S))

        hFuse_label = ttk.Label(fusesetwin, text="hFuse ")
        hFuse_label.grid( row = 2,column = 0, sticky = (N,E,W,S))
        hfuse_read_value_display = Text(fusesetwin, height = 1, width = 6, wrap=WORD)
        hfuse_read_value_display.grid(row = 2, column = 1, sticky = (N,E,W,S))

        lFuse_label = ttk.Label(fusesetwin, text="lFuse ")
        lFuse_label.grid( row = 3,column = 0, sticky = (N,E,W,S))
        lfuse_read_value_display = Text(fusesetwin, height = 1, width = 6, wrap=WORD)
        lfuse_read_value_display.grid(row = 3, column = 1, sticky = (N,E,W,S))
        # Separator object
        separator = ttk.Separator(fusesetwin, orient='horizontal')
        separator.grid(column = 0,columnspan=2, row = 4, sticky = (N,E,W,S))
            
        
        
        
        def fuseWrite():
            global fusesetwin_write
            if fusesetwin_write is None or not fusesetwin_write.winfo_exists():
                fusesetwin_write = Toplevel(root)
                label0101 = ttk.Label(fusesetwin_write, text = "Stored Default Fuse Settings")
                label0101.grid(column = 0, row = 0, sticky = (N,E,W,S))

                label0110 = ttk.Label(fusesetwin_write, text = "hFuse")
                label0110.grid( row = 1,column = 0, sticky = (N,E,W,S))
                hfuse_stored_value = StringVar()
                hfuse_stored_value_display = Text(fusesetwin_write, height = 1, width = 6, wrap=WORD)
                hfuse_stored_value_display.grid(row = 1, column = 1, sticky = (N,E,W,S))
                # Display the file value
                file_path = os.path.join("Data", "Fuse_hfuse.txt")
                if os.path.exists(file_path):
                    with open(file_path, 'r') as file:
                        fuse_value = file.read()
                        hfuse_stored_value_display.insert(END, fuse_value)


                label0120 = ttk.Label(fusesetwin_write, text = "lFuse")
                label0120.grid( row = 2,column = 0, sticky = (N,E,W,S))
                lfuse_stored_value = StringVar()
                lfuse_stored_value_display = Text(fusesetwin_write, height = 1, width = 6, wrap=WORD)
                lfuse_stored_value_display.grid(row = 2, column = 1, sticky = (N,E,W,S))
                # Display the file value
                file_path = os.path.join("Data", "Fuse_lfuse.txt")
                if os.path.exists(file_path):
                    with open(file_path, 'r') as file:
                        fuse_value = file.read()
                        lfuse_stored_value_display.insert(END, fuse_value)
                def flashfuse():
                    
                    prog_name = programmer_var_display.get('1.0','end-1c')
                    part_name = MCU_save_stored_value_display.get('1.0','end-1c')
                    
                    hfuse_statement = "hfuse:w:"+hfuse_stored_value_display.get('1.0', 'end-1c')+":m"
                    lfuse_statement = "lfuse:w:"+lfuse_stored_value_display.get('1.0', 'end-1c')+":m"
                    print(hfuse_statement)
                    print(lfuse_statement)
                    command = ['avrdude','-c',prog_name,'-p',part_name,'-U',hfuse_statement]
                    result = subprocess.run(command, shell=True, capture_output=True, text=True)
                    
                    output_lines = result.stderr.split('\n')
                    success_indication = "flash verified\n\navrdude done.  Thank you."
                    success = success_indication in result.stderr

                    for line in output_lines:
                        display_avrdude_output.insert(END, line + '\n')

                    display_avrdude_output.see("end")

                    if success:
                        print("Programming successful!")
                        try:
                            canvas.itemconfig(circle, fill='#aaff00')
                        except:
                            pass
                        
                    else:
                        print("Programming failed.")
                        try:
                            canvas.itemconfig(circle, fill='red')
                        except:
                            pass
                        
                    
                    command = ['avrdude','-c',prog_name,'-p',part_name,'-U',lfuse_statement]
                    result = subprocess.run(command, shell=True, capture_output=True, text=True)
                    
                    output_lines = result.stderr.split('\n')
                    success_indication = "flash verified\n\navrdude done.  Thank you."
                    success = success_indication in result.stderr

                    for line in output_lines:
                        display_avrdude_output.insert(END, line + '\n')

                    display_avrdude_output.see("end")

                    if success:
                        print("Programming successful!")
                        try:
                            canvas.itemconfig(circle, fill='#aaff00')
                        except:
                            pass
                        
                    else:
                        print("Programming failed.")
                        try:
                            canvas.itemconfig(circle, fill='red')
                        except:
                            pass
                        
                
                WriteFusebutton = Button(fusesetwin_write,text="Write Fuse", command = flashfuse)
                WriteFusebutton.grid(row = 0, column=1, sticky= (N,W,E,S))
                NoteLabelText = "Note: Change the  fuse setting carefully. \
                                \nPlease check the datasheet for correct fuse setting \
                                \nWrong Fuse setting may disable programming using programmer\
                                \nIf programmer is disabled you will need the offical ATMEL programmer"
                NoteLabel = ttk.Label(fusesetwin_write, wraplength = 500, text = NoteLabelText)
                NoteLabel.grid(column = 0, columnspan = 2, row = 10, sticky = (N,E,W,S))
            else:
                fusesetwin_write.lift()
        Write_Fuse_button = Button(fusesetwin, text="Write Fuse", command = fuseWrite)
        Write_Fuse_button.grid(column = 0,row = 5, sticky = (N,E,W,S))
    else:
         fusesetwin.lift()

quicksetwin = None
startx = 0
starty = 0

def move_window(event):
    global startx, starty
    x = quicksetwin.winfo_pointerx() - startx
    y = quicksetwin.winfo_pointery() - starty
    quicksetwin.geometry(f"+{x}+{y}")
    startx = quicksetwin.winfo_pointerx() - x
    starty = quicksetwin.winfo_pointery() - y


def Quick_set():
    global quicksetwin, startx, starty, circle,canvas
    if quicksetwin is None or not quicksetwin.winfo_exists():
        quicksetwin = Toplevel(root)
        quicksetwin.attributes("-topmost", True)  # Set the window to stay on top
        quicksetwin.overrideredirect(True)  # Remove window decorations
        
        title_bar = Frame(quicksetwin, bg="gray", relief="raised", bd=2)
        title_bar.grid(column=0, row=0, columnspan=3, sticky=(N, E, W))
        title_bar.bind("<ButtonPress-1>", start_move)
        title_bar.bind("<B1-Motion>", move_window)
        
        quickbutton = Button(title_bar, text="X", command=quicksetwin.destroy)
        quickbutton.grid(column=3, row=0, sticky=(N, E, W))
        
        quickProgbutton = Button(title_bar, text="Quick FLASH", command=program_avr)
        quickProgbutton.grid(column=1, row=0, sticky=(N, E, W), padx=5)
        global circle,canvas
        canvas = Canvas(title_bar, width=20, height=20)
        canvas.grid(column=2, row=0, sticky=(N, E, W), padx=5)

        # Draw a circle initially with a default color
        circle = canvas.create_oval(2, 2, 19, 19, fill='gray')
        
    else:
        quicksetwin.lift()

def start_move(event):
    global startx, starty
    startx = event.x
    starty = event.y



menubar = Menu(root)

Fusemenu = Menu(menubar, tearoff=1)
menubar.add_cascade(label="Fuse Setting", command=FuseSet)


menubar.add_command(label = "EXASUB.com", command = About_me)

menubar.add_command(label = "Quit", command = root.destroy)
menubar.add_command(label = "QuickSet", command = Quick_set)
root.config(menu=menubar)
if __name__ == "__main__":
    root.mainloop()

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *