├── icon256.png ├── icon32.png ├── .gitattributes ├── HDZeroIcon.ico ├── resource ├── event_vrx.png ├── driver │ ├── SETUP.EXE │ ├── CH341DLL.DLL │ ├── CH341M64.SYS │ ├── CH341W64.SYS │ ├── CH341WDM.CAT │ ├── CH341WDM.INF │ ├── CH341WDM.SYS │ ├── CH347DLL.DLL │ ├── CH341DLLA64.DLL │ ├── CH347DLLA64.DLL │ ├── WIN 1X │ │ ├── CH341DLL.DLL │ │ ├── CH341M64.SYS │ │ ├── CH341W64.SYS │ │ ├── CH341WDM.CAT │ │ ├── CH341WDM.INF │ │ ├── CH341WDM.SYS │ │ ├── CH347DLL.DLL │ │ ├── CH341DLLA64.DLL │ │ └── CH347DLLA64.DLL │ └── DRVSETUP64 │ │ └── DRVSETUP64.exe └── vtx_targets.png ├── package.bat ├── .gitignore ├── README.md ├── icon_convert.py ├── icon32.py ├── frame_event_vrx.py ├── global_var.py ├── frame_statusbar.py ├── hdzero_programmer.py ├── frame_vtx.py ├── Download.py ├── parse_file.py ├── frame_programmer.py ├── frame_monitor.py ├── ch341.py └── main_window.py /icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/icon256.png -------------------------------------------------------------------------------- /icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/icon32.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /HDZeroIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/HDZeroIcon.ico -------------------------------------------------------------------------------- /resource/event_vrx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/event_vrx.png -------------------------------------------------------------------------------- /package.bat: -------------------------------------------------------------------------------- 1 | pyinstaller -i HDZeroIcon.ico -w --onefile --add-data "resource;data_folder" hdzero_programmer.py 2 | -------------------------------------------------------------------------------- /resource/driver/SETUP.EXE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/SETUP.EXE -------------------------------------------------------------------------------- /resource/vtx_targets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/vtx_targets.png -------------------------------------------------------------------------------- /resource/driver/CH341DLL.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341DLL.DLL -------------------------------------------------------------------------------- /resource/driver/CH341M64.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341M64.SYS -------------------------------------------------------------------------------- /resource/driver/CH341W64.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341W64.SYS -------------------------------------------------------------------------------- /resource/driver/CH341WDM.CAT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341WDM.CAT -------------------------------------------------------------------------------- /resource/driver/CH341WDM.INF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341WDM.INF -------------------------------------------------------------------------------- /resource/driver/CH341WDM.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341WDM.SYS -------------------------------------------------------------------------------- /resource/driver/CH347DLL.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH347DLL.DLL -------------------------------------------------------------------------------- /resource/driver/CH341DLLA64.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH341DLLA64.DLL -------------------------------------------------------------------------------- /resource/driver/CH347DLLA64.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/CH347DLLA64.DLL -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341DLL.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341DLL.DLL -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341M64.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341M64.SYS -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341W64.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341W64.SYS -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341WDM.CAT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341WDM.CAT -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341WDM.INF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341WDM.INF -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341WDM.SYS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341WDM.SYS -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH347DLL.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH347DLL.DLL -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH341DLLA64.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH341DLLA64.DLL -------------------------------------------------------------------------------- /resource/driver/WIN 1X/CH347DLLA64.DLL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/WIN 1X/CH347DLLA64.DLL -------------------------------------------------------------------------------- /resource/driver/DRVSETUP64/DRVSETUP64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hd-zero/hdzero-programmer/HEAD/resource/driver/DRVSETUP64/DRVSETUP64.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | build 3 | dist 4 | vtx_common 5 | vtx_releases 6 | event_vrx_releases 7 | monitor_releases 8 | FW 9 | MONITOR.bin 10 | .spec -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## python version 2 | 3 | windows 3.10.5 32-bit 4 | 5 | ## generate exe 6 | 7 | ``` 8 | pyinstaller ./hdzero_programmer.py --onefile 9 | ``` 10 | -------------------------------------------------------------------------------- /icon_convert.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | with open("icon32.png", "rb") as image_file: 4 | encoded_string = base64.b64encode(image_file.read()).decode('utf-8') 5 | 6 | with open("icon32.py", "w") as python_file: 7 | python_file.write("icon32 = '{}'\n".format(encoded_string)) 8 | -------------------------------------------------------------------------------- /icon32.py: -------------------------------------------------------------------------------- 1 | icon32 = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAONJREFUWIXtlTsOwjAQRL2IG1hCgpqe+7ecgAOkTqJIHAEtBQLZZr0/WzR4usSfeTuONyEMDf27oHyBiNXJ5+OhOnhdH9nzCe9fe2fG8BreC4CsKacZ4mcdBYOIAQDCrof5tGxstSlMqSpAL3MJgk1AqxnizbMOEWkAR/UXzXwqhZYEVNFLcgNMy9bDX76GFfOs+vSacV88JU8CbPRSA2oGkKK3JmA+gvKGpMcxQ7Ru16cPJDK37W4A1ui7A3hFAmj7+3uetnrqhlQTaP3JaMxZAAuE11wEkCA01Vsb09DQ0M/1BHTeW8hlv+XyAAAAAElFTkSuQmCC' 2 | -------------------------------------------------------------------------------- /frame_event_vrx.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | from PIL import Image, ImageTk 4 | 5 | 6 | class frame_event_vrx: 7 | def __init__(self, parent): 8 | self._parent = parent 9 | self._frame = tk.Frame(parent) 10 | parent.add(self._frame, text="Event VRX") 11 | 12 | self.image_path = "resource/event_vrx.png" 13 | self.show_image() 14 | 15 | def frame(self): 16 | return self._frame 17 | 18 | def show_image(self): 19 | image = Image.open(self.image_path) 20 | photo = ImageTk.PhotoImage(image) 21 | label = ttk.Label(self._frame, image=photo) 22 | label.image = photo 23 | label.pack() 24 | -------------------------------------------------------------------------------- /global_var.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | from enum import Enum, unique 3 | brightness = 0 4 | contrast = 0 5 | saturation = 0 6 | backlight = 100 7 | cell_count = 1 8 | warning_cell_voltage = 28 9 | osd = 0 10 | 11 | nIndex = 0 12 | PAGE_SIZE = 256 13 | HEAD_SIZE = 4 14 | 15 | 16 | class ch341_status(Enum): 17 | IDLE = 0 18 | VTX_DISCONNECTED = 1 19 | VTX_CONNECTED = 2 20 | VTX_UPDATE = 3 21 | VTX_UPDATEDONE = 4 22 | VTX_FW_ERROR = 5 23 | VTX_RECONNECT = 6 24 | VTX_RECONNECTDONE = 7 25 | VTX_UPDATE_FAILED = 8 26 | 27 | MONITOR_CHECK_ALIVE = 11 28 | MONITOR_CONNECTED = 12 29 | MONITOR_GET_FW = 13 30 | MONITOR_UPDATE = 14 31 | MONITOR_UPDATEDONE = 15 32 | MONITOR_FW_ERROR = 16 33 | 34 | EVENT_VRX_DISCONNECTED = 21 35 | EVENT_VRX_CONNECTED = 22 36 | EVENT_VRX_GET_FW = 23 37 | EVENT_VRX_UPDATE = 24 38 | EVENT_VRX_UPDATEDONE = 25 39 | EVENT_VRX_FW_ERROR = 26 40 | 41 | STATUS_EXIT = 255 42 | 43 | 44 | class download_status(Enum): 45 | FILE_PARSE = -1 46 | IDLE = 0 47 | DOWNLOAD_VTX_FW = 1 48 | DOWNLOAD_VTX_FW_DONE = 2 49 | DOWNLOAD_VTX_FW_FAILED = 3 50 | 51 | DOWNLOAD_MONITOR_FW = 10 52 | DOWNLOAD_MONITOR_FW_DONE = 11 53 | DOWNLOAD_MONITOR_FW_FAILED = 12 54 | 55 | DOWNLOAD_EVENT_VRX_FW = 20 56 | DOWNLOAD_EVENT_VRX_FW_DONE = 21 57 | DOWNLOAD_EVENT_VRX_FW_FAILED = 22 58 | DOWNLOAD_EXIT = 255 59 | -------------------------------------------------------------------------------- /frame_statusbar.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | 5 | class frame_statusbar: 6 | def __init__(self, parent): 7 | self._parent = parent 8 | self._frame = tk.Frame(parent) 9 | 10 | self.init_progress_bar() 11 | self.progress_bar_set_value(0) 12 | self.init_label() 13 | self.label_hidden() 14 | 15 | def frame(self): 16 | return self._frame 17 | 18 | def label_hidden(self): 19 | self.label.place_forget() 20 | 21 | def label_show(self): 22 | self.label.place(relwidth=1, relheight=1) 23 | 24 | def init_label(self): 25 | self.label = tk.Label(self._frame, text="") 26 | self.status_label_set_bg("SystemButtonFace") 27 | 28 | def status_label_set_text(self, text, color): 29 | self.label["text"] = text 30 | self.status_label_set_bg(color) 31 | self.label_show() 32 | 33 | def status_label_set_bg(self, color): 34 | self.label["bg"] = color 35 | ''' 36 | if color == 1: # system 37 | self.label["bg"] = "SystemButtonFace" 38 | elif color == 2: # red 39 | self.label["bg"] = "red" 40 | else: # green 41 | self.label["bg"] = "#06b025" 42 | ''' 43 | 44 | def init_progress_bar(self): 45 | self.bar = ttk.Progressbar( 46 | self._frame, orient="horizontal", mode="determinate") 47 | self.bar.pack(fill="both", expand=True) 48 | 49 | def progress_bar_set_value(self, value): 50 | self.bar["value"] = value 51 | -------------------------------------------------------------------------------- /hdzero_programmer.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import os 3 | import sys 4 | import shutil 5 | from main_window import ui_thread_proc 6 | from download import download_thread_proc 7 | from ch341 import ch341_thread_proc 8 | 9 | 10 | def get_resource_folder(): 11 | if getattr(sys, 'frozen', False): 12 | base_path = sys._MEIPASS 13 | else: 14 | base_path = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | resource_folder = os.path.join(base_path, "data_folder") 17 | print(resource_folder) 18 | 19 | return resource_folder 20 | 21 | 22 | def check_and_release_resource(): 23 | 24 | resource_folder = get_resource_folder() 25 | 26 | if os.path.exists("resource"): 27 | pass 28 | else: 29 | for filename in os.listdir(resource_folder): 30 | source_file = os.path.join(resource_folder, filename) 31 | destination_file = os.path.join("resource", filename) 32 | if os.path.isfile(source_file): 33 | shutil.copy(source_file, destination_file) 34 | elif os.path.isdir(source_file): 35 | shutil.copytree(source_file, destination_file) 36 | 37 | 38 | def main(): 39 | check_and_release_resource() 40 | download_thread = threading.Thread( 41 | target=download_thread_proc, name="download") 42 | download_thread.start() 43 | 44 | ui_thread = threading.Thread(target=ui_thread_proc, name="ui") 45 | ui_thread.start() 46 | 47 | ch341_thread = threading.Thread(target=ch341_thread_proc, name="ch341") 48 | ch341_thread.start() 49 | 50 | 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /frame_vtx.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | from PIL import Image, ImageTk 4 | 5 | 6 | class frame_vtx(): 7 | def __init__(self, parent): 8 | self._parent = parent 9 | self._frame = tk.Frame(parent) 10 | parent.add(self._frame, text="VTX") 11 | 12 | self.target_num = 0 13 | self.radio_button = [] 14 | self.image = [] 15 | self.tk_image = [] 16 | self.vtx_target = tk.StringVar() 17 | 18 | for i in range(0, 4): 19 | self._frame.grid_rowconfigure(i, weight=1) 20 | self._frame.grid_columnconfigure(i, weight=1) 21 | 22 | def frame(self): 23 | return self._frame 24 | 25 | def create_radio_button_list(self, targets, callback, vtx_image): 26 | self.target_num = len(targets) 27 | for i in range(0, self.target_num): 28 | # self.image.append(Image.open(f"{i}.png")) 29 | self.tk_image.append(ImageTk.PhotoImage(vtx_image[i])) 30 | self.radio_button.append(ttk.Radiobutton(self._frame, image=self.tk_image[i], 31 | variable=self.vtx_target, value=targets[i], compound="left", command=callback)) 32 | self.radio_button[i].grid( 33 | row=(int)(i % 4), column=(int)(i/4), padx=5, pady=5) 34 | self.radio_button_reset() 35 | 36 | def radio_button_disable(self): 37 | for i in range(0, self.target_num): 38 | self.radio_button[i].config(state="disabled") 39 | 40 | def radio_button_enable(self): 41 | for i in range(0, self.target_num): 42 | self.radio_button[i].config(state="normal") 43 | 44 | def radio_button_reset(self): 45 | try: 46 | self.radio_button[0].invoke() 47 | except: 48 | pass 49 | -------------------------------------------------------------------------------- /Download.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import sys 3 | import time 4 | from global_var import * 5 | import os 6 | import zipfile 7 | 8 | 9 | class download: 10 | def __init__(self): 11 | 12 | self.status = download_status.IDLE.value 13 | self.url = "" 14 | self.save_path = "" 15 | self.to_stop = 0 16 | 17 | def download_file(self, url, save_path, clear): 18 | print(f"Downloading {url}") 19 | if clear: 20 | if os.path.exists(save_path): 21 | os.remove(save_path) 22 | try: 23 | response = requests.get(url) 24 | if response.status_code == 200: 25 | with open(save_path, "wb") as file: 26 | for chunk in response.iter_content(chunk_size=1024): 27 | if self.to_stop == 1: 28 | self.to_stop = 0 29 | return 2 30 | if chunk: 31 | file.write(chunk) 32 | if self.status == download_status.DOWNLOAD_EXIT.value: 33 | sys.exit() 34 | return 1 35 | else: 36 | if self.status == download_status.DOWNLOAD_EXIT.value: 37 | sys.exit() 38 | print("Failed to download file.") 39 | return 0 40 | except: 41 | return 0 42 | 43 | 44 | my_download = download() 45 | 46 | 47 | def download_thread_proc(): 48 | my_download.download_file( 49 | "https://api.github.com/repos/hd-zero/hdzero-vtx/releases", "resource/vtx_releases", 0) 50 | my_download.download_file( 51 | "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/src/common.h", "resource/vtx_common", 0) 52 | my_download.download_file( 53 | "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/vtx_targets.png", "resource/vtx_targets.png", 0) 54 | my_download.download_file( 55 | "https://api.github.com/repos/ligenxxxx/event-vrx/releases", "resource/event_vrx_releases", 1) 56 | my_download.download_file( 57 | "https://api.github.com/repos/ligenxxxx/hv/releases", "resource/monitor_releases", 1) 58 | 59 | time.sleep(1) 60 | my_download.status = download_status.FILE_PARSE.value 61 | 62 | while True: 63 | if my_download.status == download_status.DOWNLOAD_VTX_FW.value: 64 | ret = my_download.download_file( 65 | my_download.url, my_download.save_path+".zip", 1) 66 | if ret == 1: 67 | # unzip file 68 | with zipfile.ZipFile(my_download.save_path+".zip", 'r') as zip_ref: 69 | zip_ref.extractall("resource/") 70 | 71 | # rename file 72 | if (os.path.exists(my_download.save_path)): 73 | os.remove(my_download.save_path) 74 | os.rename("resource/HDZERO_TX.bin", my_download.save_path) 75 | 76 | my_download.status = download_status.DOWNLOAD_VTX_FW_DONE.value 77 | elif ret == 2: # stop 78 | my_download.status = download_status.IDLE.value 79 | else: 80 | my_download.status = download_status.DOWNLOAD_VTX_FW_FAILED.value 81 | 82 | elif my_download.status == download_status.DOWNLOAD_MONITOR_FW.value: 83 | ret = my_download.download_file( 84 | my_download.url, my_download.save_path, 1) 85 | if ret == 1: 86 | my_download.status = download_status.DOWNLOAD_MONITOR_FW_DONE.value 87 | elif ret == 2: # stop 88 | my_download.status = download_status.IDLE.value 89 | else: 90 | my_download.status = download_status.DOWNLOAD_MONITOR_FW_FAILED.value 91 | 92 | elif my_download.status == download_status.DOWNLOAD_EVENT_VRX_FW.value: 93 | ret = my_download.download_file( 94 | my_download.url, my_download.save_path, 1) 95 | if ret == 1: 96 | my_download.status = download_status.DOWNLOAD_EVENT_VRX_FW_DONE.value 97 | elif ret == 2: # stop 98 | my_download.status = download_status.IDLE.value 99 | else: 100 | my_download.status = download_status.DOWNLOAD_EVENT_VRX_FW_FAILED.value 101 | 102 | elif my_download.status == download_status.DOWNLOAD_EXIT.value: 103 | sys.exit() 104 | 105 | time.sleep(0.01) 106 | -------------------------------------------------------------------------------- /parse_file.py: -------------------------------------------------------------------------------- 1 | import json 2 | from PIL import Image 3 | 4 | 5 | class parse: 6 | def __init__(self): 7 | self.vtx_releases_path = "resource/vtx_releases" 8 | self.vtx_common_path = "resource/vtx_common" 9 | self.event_vrx_releases_path = "resource/event_vrx_releases" 10 | self.monitor_releases_path = "resource/monitor_releases" 11 | self.vtx_tragets_image_path = "resource/vtx_targets.png" 12 | 13 | self.vtx_info = {} 14 | self.event_vrx_info = {} 15 | self.monitor_info = {} 16 | self.vtx_target_image = [] 17 | 18 | def parse_vtx_common(self): 19 | try: 20 | with open(self.vtx_common_path) as file: 21 | lines = file.readlines() 22 | start = 0 23 | for i in range(len(lines)): 24 | if start == 1: 25 | words = lines[i].split() 26 | for j in range(len(words)): 27 | if words[j] == "defined": 28 | name = words[j+1].lower() 29 | if words[j] == "VTX_ID": 30 | if words[j+1] != "0x00": 31 | word = words[j+1].strip("0x") 32 | self.vtx_info[name] = {"id": int(word, 16)} 33 | if lines[i] == "/* define VTX ID start */\n": 34 | start = 1 35 | elif lines[i] == "/* define VTX ID end */\n": 36 | start = 0 37 | return 1 38 | except: 39 | print("something error") 40 | return 0 41 | 42 | def parse_vtx_releases(self): 43 | try: 44 | with open(self.vtx_releases_path) as f: 45 | data = json.load(f) 46 | 47 | for i in range(len(data)): 48 | link_list = [] 49 | name_list = [] 50 | for j in range(len(data[i]['assets'])): 51 | link_list.append(data[i]['assets'][j] 52 | ['browser_download_url']) 53 | 54 | name_start = link_list[j].rfind('/') + len('/') 55 | name_end = link_list[j].index(".zip", name_start) 56 | name_list.append(link_list[j][name_start:name_end]) 57 | name = link_list[j][name_start:name_end] 58 | if name == "hdzero_freestyle": 59 | name = "hdzero_freestyle_v1" 60 | 61 | version = data[i]['tag_name'] 62 | url = data[i]['assets'][j]['browser_download_url'] 63 | self.vtx_info[name][version] = url 64 | return 1 65 | except: 66 | return 0 67 | 68 | def parse_event_vrx_releases(self): 69 | try: 70 | with open(self.event_vrx_releases_path) as f: 71 | data = json.load(f) 72 | 73 | for i in range(len(data)): 74 | link_list = [] 75 | for j in range(len(data[i]['assets'])): 76 | link_list.append(data[i]['assets'][j] 77 | ['browser_download_url']) 78 | 79 | version = data[i]['tag_name'] 80 | url = data[i]['assets'][j]['browser_download_url'] 81 | self.event_vrx_info[version] = url 82 | return 1 83 | except: 84 | return 0 85 | 86 | def parse_monitor_releases(self): 87 | try: 88 | with open(self.monitor_releases_path) as f: 89 | data = json.load(f) 90 | 91 | for i in range(len(data)): 92 | link_list = [] 93 | for j in range(len(data[i]['assets'])): 94 | link_list.append(data[i]['assets'][j] 95 | ['browser_download_url']) 96 | 97 | version = data[i]['tag_name'] 98 | url = data[i]['assets'][j]['browser_download_url'] 99 | self.monitor_info[version] = url 100 | return 1 101 | except: 102 | return 0 103 | 104 | def parse_vtx_tragets_image(self, num): 105 | try: 106 | ori_img = Image.open(self.vtx_tragets_image_path) 107 | except: 108 | return 0 109 | 110 | for i in range(0, num): 111 | try: 112 | self.vtx_target_image.append( 113 | ori_img.crop((0, i*100, 299, i*100+99))) 114 | except: 115 | return 0 116 | 117 | return 1 118 | 119 | 120 | my_parse = parse() 121 | -------------------------------------------------------------------------------- /frame_programmer.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | from tkinter import filedialog 4 | import os 5 | 6 | 7 | class frame_programmer: 8 | def __init__(self, parent): 9 | self._parent = parent 10 | self._frame = tk.Frame(self._parent) 11 | 12 | self._frame.grid_rowconfigure(0, weight=1) 13 | self._frame.grid_rowconfigure(1, weight=1) 14 | self._frame.grid_rowconfigure(2, weight=1) 15 | self._frame.grid_columnconfigure(0, weight=1) 16 | self._frame.grid_columnconfigure(1, weight=1) 17 | self._frame.grid_columnconfigure(2, weight=1) 18 | 19 | self.online_list = [] 20 | self.mode = 0 # 0/1 : url/local path 21 | self.local_file_path = "" 22 | self.local_file_path_shorten = "" 23 | self.url = "" 24 | 25 | self.is_cancel = 0 26 | 27 | self.is_load_online = tk.StringVar() 28 | 29 | self.version_combobox = ttk.Combobox( 30 | self._frame, values=self.online_list, state="readonly") 31 | self.version_combobox_set_default() 32 | self.version_combobox_disable() 33 | 34 | self.online_fw_button = ttk.Radiobutton( 35 | self._frame, text="Load Online Firmware", variable=self.is_load_online, value='1') 36 | self.online_fw_button_show() 37 | self.online_fw_button_disable() 38 | 39 | self.local_fw_button = ttk.Radiobutton( 40 | self._frame, text="Load Local Firmware", variable=self.is_load_online, value='0') 41 | self.local_fw_button.grid(row=1, column=1, padx=5, pady=5) 42 | self.local_fw_button_disable() 43 | 44 | self.update_button = tk.Button(self._frame) 45 | self.update_button.grid(row=1, column=2, padx=5, pady=5) 46 | 47 | self.update_button_set_text_update("VTX") 48 | self.update_button_disable() 49 | 50 | def frame(self): 51 | return self._frame 52 | 53 | def version_combobox_set_default(self): 54 | self.version_combobox.set("Load Online Firmware") 55 | 56 | def version_combobox_disable(self): 57 | self.version_combobox["state"] = "disabled" 58 | 59 | def version_combobox_enable(self): 60 | self.version_combobox["state"] = "readonly" 61 | 62 | def version_combobox_update_values(self, new_values): 63 | self.online_list = new_values 64 | self.version_combobox.configure(values=self.online_list) 65 | 66 | def online_fw_button_disable(self): 67 | self.online_fw_button.config(state="disabled") 68 | 69 | def online_fw_button_enable(self, network_error): 70 | if network_error: 71 | self.online_fw_button_disable() 72 | else: 73 | self.online_fw_button.config(state="normal") 74 | 75 | def online_fw_button_show(self): 76 | self.online_fw_button.grid(row=1, column=0, padx=5, pady=5) 77 | self.version_combobox.grid_remove() 78 | 79 | def online_fw_button_hidden(self): 80 | self.version_combobox.grid( 81 | row=1, column=0, padx=5, pady=5) 82 | self.version_combobox.event_generate('') 83 | self.online_fw_button.grid_remove() 84 | self.local_fw_button_set_str_default() 85 | 86 | def online_fw_button_set_str(self, str): 87 | self.online_fw_button['text'] = str 88 | 89 | def online_fw_button_set_str_default(self): 90 | self.online_fw_button['text'] = "Load Online Firmware" 91 | 92 | def local_fw_button_disable(self): 93 | self.local_fw_button.config(state="disabled") 94 | 95 | def local_fw_button_enable(self): 96 | self.local_fw_button.config(state="normal") 97 | 98 | def local_fw_button_set_str(self, str): 99 | self.local_fw_button["text"] = str 100 | 101 | def local_fw_button_set_str_default(self): 102 | self.local_fw_button["text"] = "Load Local Firmware" 103 | 104 | def deselect(self): 105 | self.is_load_online.set("") 106 | 107 | def select_local_file(self): 108 | self.version_combobox_set_default() 109 | self.online_fw_button_set_str_default() 110 | filetypes = (("Bin files", "*.bin"), ("All files", "*.*")) 111 | 112 | try: 113 | with open("resource/local_path", "r") as file: 114 | path = file.read() 115 | file.close() 116 | except: 117 | path = "." 118 | 119 | try: 120 | self.local_file_path = filedialog.askopenfilename( 121 | initialdir=path, title="select a firmware", filetypes=filetypes) 122 | except: 123 | print("please select a firmware file") 124 | 125 | if self.local_file_path: 126 | self.mode = 1 127 | self.local_file_path_shorten = self.shorten_path( 128 | self.local_file_path) 129 | self.local_fw_button_set_str(self.local_file_path_shorten) 130 | path = self.local_file_path[:self.local_file_path.rfind('/') + 1] 131 | with open("resource/local_path", "w") as file: 132 | file.write(path) 133 | file.close() 134 | 135 | def update_button_disable(self): 136 | self.update_button["state"] = "disabled" 137 | self.update_button["bg"] = "SystemButtonFace" 138 | 139 | def update_button_enable(self): 140 | self.update_button["state"] = "normal" 141 | if self.update_button["text"] == "Cancel": 142 | self.update_button["bg"] = "red" 143 | else: 144 | self.update_button["bg"] = "#06b025" 145 | 146 | def update_button_set_text_cancel(self): 147 | self.is_cancel = 1 148 | self.update_button["text"] = "Cancel" 149 | 150 | def update_button_set_text_update(self, string): 151 | self.is_cancel = 0 152 | self.update_button["text"] = "Flash " + string 153 | 154 | def shorten_path(self, path, max_length=40): 155 | if len(path) <= max_length: 156 | return path 157 | else: 158 | head, tail = os.path.split(path) 159 | while len(head) + len(tail) + 4 > max_length: 160 | head = os.path.split(head)[0] 161 | return os.path.join(head, "..." + tail) 162 | -------------------------------------------------------------------------------- /frame_monitor.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | import ctypes 4 | import global_var 5 | import time 6 | 7 | 8 | class frame_monitor: 9 | def __init__(self, parent): 10 | self._parent = parent 11 | self._frame = tk.Frame(parent) 12 | parent.add(self._frame, text="Monitor") 13 | 14 | self.dll_name = "CH341DLL.DLL" 15 | self.color_background = "#303030" 16 | self.color_label = "white" 17 | 18 | self.heart_cnt = 0 19 | self.addr_usb_heart = 0x13 20 | self.addr_usb_write_fpga_device = 0x65 # 7bit address 21 | self.addr_usb_write_brightness = 0x14 22 | self.addr_usb_write_contrast = 0x15 23 | self.addr_usb_write_saturation = 0x16 24 | self.addr_usb_write_backlight = 0x17 25 | self.addr_usb_write_cell_count = 0x18 26 | self.addr_usb_write_warning_cell_voltage = 0x19 27 | self.addr_usb_write_osd = 0x1A 28 | 29 | self.brightness_min = 0 30 | self.brightness_max = 254 31 | self.brightness_default = 136 32 | self.contrast_min = 0 33 | self.contrast_max = 254 34 | self.contrast_default = 86 35 | self.saturation_min = 0 36 | self.saturation_max = 254 37 | self.saturation_default = 143 38 | self.backlight_min = 1 39 | self.backlight_max = 100 40 | self.backlight_default = 80 41 | self.cell_count_min = 1 # 1=auto 42 | self.cell_count_max = 6 43 | self.cell_count_default = 1 44 | self.warning_cell_voltage_min = 28 45 | self.warning_cell_voltage_max = 42 46 | self.warning_cell_voltage_default = 28 47 | self.osd_min = 0 48 | self.osd_max = 1 49 | self.osd_default = 1 50 | 51 | self.brightness_scale = 0 52 | self.contrast_scale = 0 53 | self.saturation_scale = 0 54 | self.backlight_scale = 0 55 | self.cell_count_scale = 0 56 | self.warning_cell_voltage_scale = 0 57 | self.osd_checkbutton = 0 58 | self.reset_button = 0 59 | 60 | self.osd_var = tk.BooleanVar() 61 | 62 | self._frame.grid_rowconfigure(0, weight=1) 63 | self._frame.grid_rowconfigure(1, weight=1) 64 | self._frame.grid_rowconfigure(2, weight=1) 65 | self._frame.grid_rowconfigure(3, weight=1) 66 | self._frame.grid_rowconfigure(4, weight=1) 67 | self._frame.grid_rowconfigure(5, weight=1) 68 | self._frame.grid_rowconfigure(6, weight=1) 69 | 70 | self._frame.grid_columnconfigure(0, weight=0) 71 | self._frame.grid_columnconfigure(1, weight=0) 72 | self._frame.grid_columnconfigure(2, weight=0) 73 | 74 | self.init_image_setting() 75 | self.init_power_setting() 76 | self.init_osd_setting() 77 | self.init_reset_button() 78 | 79 | try: 80 | self.dll = ctypes.WinDLL(self.dll_name) 81 | except: 82 | print("Please check ch341 driver") 83 | 84 | def usb_heart(self): 85 | self.heart_cnt += 1 86 | if self.heart_cnt == 255: 87 | self.heart_cnt = 0 88 | self.write_i2c(self.addr_usb_heart, self.heart_cnt) 89 | 90 | def write_i2c(self, addr, byte): 91 | self.dll.CH341WriteI2C(0, self.addr_usb_write_fpga_device, addr, byte) 92 | time.sleep(0.001) 93 | 94 | def write_brightness(self, b): 95 | global_var.brightness = b 96 | self.write_i2c(self.addr_usb_write_brightness, b) 97 | # print(f"write_brightness {b}") 98 | 99 | def write_contrast(self, c): 100 | global_var.contrast = c 101 | self.write_i2c(self.addr_usb_write_contrast, c) 102 | # print(f"write_contrast {c}") 103 | 104 | def write_saturation(self, s): 105 | global_var.saturation = s 106 | self.write_i2c(self.addr_usb_write_saturation, s) 107 | # print(f"write_saturation {s}") 108 | 109 | def write_backlight(self, l): 110 | global_var.backlight = l 111 | self.write_i2c(self.addr_usb_write_backlight, l) 112 | # print(f"write_backlight {l}") 113 | 114 | def write_cell_count(self, cell): 115 | global_var.cell_count = cell 116 | self.write_i2c(self.addr_usb_write_cell_count, cell) 117 | # print(f"write_cell_count {cell}") 118 | 119 | def write_warning_cell_voltage(self, warning_cell): 120 | global_var.warning_cell_voltage = warning_cell 121 | self.write_i2c(self.addr_usb_write_warning_cell_voltage, warning_cell) 122 | # print(f"write_warning_cell_voltage {warning_cell}") 123 | 124 | def write_osd(self, osd): 125 | global_var.osd = osd 126 | self.write_i2c(self.addr_usb_write_osd, osd) 127 | 128 | def write_setting(self, b, c, s, l, cell, warning_cell, osd): 129 | """write setting from vrx. 130 | usually used for sync vrx setting. 131 | NOTE: Must run after setting_enable 132 | """ 133 | if b < self.brightness_min or b > self.brightness_max: 134 | b = self.brightness_default 135 | if c < self.contrast_min or c > self.contrast_max: 136 | c = self.contrast_default 137 | if s < self.saturation_min or s > self.saturation_max: 138 | s = self.saturation_default 139 | if l < self.backlight_min or l > self.backlight_max: 140 | l = self.backlight_default 141 | if cell < self.cell_count_min or cell > self.cell_count_max: 142 | cell = self.cell_count_default 143 | if warning_cell < self.warning_cell_voltage_min or warning_cell > self.warning_cell_voltage_max: 144 | warning_cell = self.warning_cell_voltage_default 145 | 146 | if osd < self.osd_min or osd > self.osd_max: 147 | osd = self.osd_default 148 | 149 | self.write_brightness(b) 150 | self.write_contrast(c) 151 | self.write_saturation(s) 152 | self.write_backlight(l) 153 | self.write_cell_count(cell) 154 | self.write_warning_cell_voltage(warning_cell) 155 | self.write_osd(osd) 156 | 157 | # update scale 158 | self.brightness_scale.set(b) 159 | self.contrast_scale.set(c) 160 | self.saturation_scale.set(s) 161 | self.backlight_scale.set(l) 162 | self.cell_count_scale.set(cell) 163 | self.warning_cell_voltage_scale.set(warning_cell) 164 | 165 | if osd == 1: 166 | self.osd_var.set(True) 167 | else: 168 | self.osd_var.set(False) 169 | 170 | # update label 171 | self.brightness_label.config(text=f'{b}') 172 | self.contrast_label.config(text=f'{c}') 173 | self.saturation_label.config(text=f'{s}') 174 | self.backlight_label.config(text=f'{l}') 175 | 176 | option = ["Auto", "2S", "3S", "4S", "5S", "6S"] 177 | self.cell_count_label.config(text=option[cell-1]) 178 | self.warning_cell_voltage_label.config(text=f"{warning_cell/10}") 179 | 180 | def setting_disable(self): 181 | self.brightness_scale.configure(state="disabled") 182 | self.contrast_scale.configure(state="disabled") 183 | self.saturation_scale.configure(state="disabled") 184 | self.backlight_scale.configure(state="disabled") 185 | self.cell_count_scale.configure(state="disabled") 186 | self.warning_cell_voltage_scale.configure(state="disabled") 187 | self.osd_checkbutton.configure(state="disabled") 188 | self.reset_button.configure(state="disable") 189 | 190 | def setting_enable(self): 191 | self.brightness_scale.configure(state="normal") 192 | self.contrast_scale.configure(state="normal") 193 | self.saturation_scale.configure(state="normal") 194 | self.backlight_scale.configure(state="normal") 195 | self.cell_count_scale.configure(state="normal") 196 | self.warning_cell_voltage_scale.configure(state="normal") 197 | self.osd_checkbutton.configure(state="normal") 198 | self.reset_button.configure(state="normal") 199 | 200 | def reset_scale(self): 201 | self.brightness_scale.set(self.brightness_min) 202 | self.contrast_scale.set(self.contrast_min) 203 | self.saturation_scale.set(self.saturation_min) 204 | self.backlight_scale.set(self.backlight_min) 205 | self.cell_count_scale.set(self.cell_count_min) 206 | self.warning_cell_voltage_scale.set(self.warning_cell_voltage_min) 207 | self.osd_var.set(False) 208 | 209 | self.brightness_label.config(text=f"{int(float(self.brightness_min))}") 210 | self.brightness_label.config(text=f"{int(float(self.contrast_min))}") 211 | self.saturation_label.config(text=f"{int(float(self.saturation_min))}") 212 | self.backlight_label.config(text=f"{int(float(self.brightness_min))}") 213 | 214 | self.on_cell_count_scale_changed(self.cell_count_min) 215 | self.on_warning_cell_voltage_scale_changed( 216 | self.warning_cell_voltage_min) 217 | self.osd_var.set(False) 218 | self.on_osd_checkoutbutton_changed() 219 | 220 | def frame(self): 221 | return self._frame 222 | 223 | def on_brightness_scale_changed(self, value): 224 | self.brightness_label.config(text=f"{int(float(value))}") 225 | self.write_brightness(int(float(value))) 226 | 227 | def on_contrast_scale_changed(self, value): 228 | self.contrast_label.config(text=f"{int(float(value))}") 229 | self.write_contrast(int(float(value))) 230 | 231 | def on_saturation_scale_changed(self, value): 232 | self.saturation_label.config(text=f"{int(float(value))}") 233 | self.write_saturation(int(float(value))) 234 | 235 | def on_backlight_scale_changed(self, value): 236 | self.backlight_label.config(text=f"{int(float(value))}") 237 | self.write_backlight(int(float(value))) 238 | 239 | def on_cell_count_scale_changed(self, value): 240 | option = ["Auto", "2S", "3S", "4S", "5S", "6S"] 241 | self.cell_count = int(float(value)) 242 | self.cell_count_label.config(text=option[self.cell_count-1]) 243 | self.write_cell_count(int(float(value))) 244 | 245 | def on_warning_cell_voltage_scale_changed(self, value): 246 | self.warning_cell_voltage = int(float(value)) 247 | self.warning_cell_voltage_label.config( 248 | text=f"{self.warning_cell_voltage/10}") 249 | self.write_warning_cell_voltage(int(float(value))) 250 | 251 | def on_osd_checkoutbutton_changed(self): 252 | if self.osd_var.get() == True: 253 | self.write_osd(1) 254 | else: 255 | self.write_osd(0) 256 | 257 | def on_reset_button_press(self): 258 | self.brightness_scale.set(self.brightness_default) 259 | self.contrast_scale.set(self.contrast_default) 260 | self.saturation_scale.set(self.saturation_default) 261 | self.backlight_scale.set(self.backlight_default) 262 | self.cell_count_scale.set(self.cell_count_default) 263 | self.warning_cell_voltage_scale.set(self.warning_cell_voltage_default) 264 | self.osd_var.set(True) 265 | 266 | self.brightness_label.config( 267 | text=f"{int(float(self.brightness_default))}") 268 | self.contrast_label.config( 269 | text=f"{int(float(self.contrast_default))}") 270 | self.saturation_label.config( 271 | text=f"{int(float(self.saturation_default))}") 272 | self.backlight_label.config( 273 | text=f"{int(float(self.backlight_default))}") 274 | 275 | self.on_cell_count_scale_changed(self.cell_count_default) 276 | self.on_warning_cell_voltage_scale_changed( 277 | self.warning_cell_voltage_default) 278 | self.osd_var.set(True) 279 | self.on_osd_checkoutbutton_changed() 280 | 281 | def init_image_setting(self): 282 | # brighrness 283 | row = 0 284 | label = ttk.Label(self._frame, text="Brightness") 285 | label.grid(row=row, column=0, sticky="w", padx=20) 286 | 287 | self.brightness_scale = ttk.Scale(self._frame, from_=self.brightness_min, to=self.brightness_max, orient="horizontal", 288 | length=350, command=self.on_brightness_scale_changed) 289 | self.brightness_scale.grid(row=row, column=1, sticky="w", padx=20) 290 | 291 | self.brightness_label = ttk.Label(self._frame, text="0") 292 | self.brightness_label.grid(row=row, column=2, sticky="w", padx=10) 293 | 294 | # contrast 295 | row += 1 296 | label = ttk.Label(self._frame, text="Contrast") 297 | label.grid(row=row, column=0, sticky="w", padx=20) 298 | 299 | self.contrast_scale = ttk.Scale(self._frame, from_=self.contrast_min, to=self.contrast_max, orient="horizontal", 300 | length=350, command=self.on_contrast_scale_changed) 301 | self.contrast_scale.grid(row=row, column=1, sticky="w", padx=20) 302 | 303 | self.contrast_label = ttk.Label(self._frame, text="0") 304 | self.contrast_label.grid(row=row, column=2, sticky="w", padx=10) 305 | 306 | # saturation 307 | row += 1 308 | label = ttk.Label(self._frame, text="Saturation") 309 | label.grid(row=row, column=0, sticky="w", padx=20) 310 | 311 | self.saturation_scale = ttk.Scale(self._frame, from_=self.saturation_min, to=self.saturation_max, orient="horizontal", 312 | length=350, command=self.on_saturation_scale_changed) 313 | self.saturation_scale.grid(row=row, column=1, sticky="w", padx=20) 314 | 315 | self.saturation_label = ttk.Label(self._frame, text="0") 316 | self.saturation_label.grid(row=row, column=2, sticky="w", padx=10) 317 | 318 | # Backlight 319 | row += 1 320 | label = ttk.Label(self._frame, text="Backlight") 321 | label.grid(row=row, column=0, sticky="w", padx=20) 322 | 323 | self.backlight_scale = ttk.Scale(self._frame, from_=self.backlight_min, to=self.backlight_max, orient="horizontal", 324 | length=350, command=self.on_backlight_scale_changed) 325 | self.backlight_scale.grid(row=row, column=1, sticky="w", padx=20) 326 | 327 | self.backlight_label = ttk.Label(self._frame, text="0") 328 | self.backlight_label.grid(row=row, column=2, sticky="w", padx=10) 329 | 330 | def init_power_setting(self): 331 | row = 4 332 | label = ttk.Label(self._frame, text="Cell Count") 333 | label.grid(row=row, column=0, sticky="w", padx=20) 334 | 335 | self.cell_count_scale = ttk.Scale(self._frame, from_=self.cell_count_min, to=self.cell_count_max, orient="horizontal", 336 | length=350, command=self.on_cell_count_scale_changed) 337 | self.cell_count_scale.grid(row=row, column=1, sticky="w", padx=20) 338 | 339 | self.cell_count_label = ttk.Label(self._frame, text="Auto") 340 | self.cell_count_label.grid(row=row, column=2, sticky="w", padx=10) 341 | 342 | row += 1 343 | label = ttk.Label(self._frame, text="Warning Cell Voltage") 344 | label.grid(row=row, column=0, sticky="w", padx=20) 345 | 346 | self.warning_cell_voltage_scale = ttk.Scale(self._frame, from_=self.warning_cell_voltage_min, to=self.warning_cell_voltage_max, 347 | orient="horizontal", length=350, command=self.on_warning_cell_voltage_scale_changed) 348 | self.warning_cell_voltage_scale.grid( 349 | row=row, column=1, sticky="w", padx=20) 350 | 351 | self.warning_cell_voltage_label = ttk.Label(self._frame, text="2.8") 352 | self.warning_cell_voltage_label.grid( 353 | row=row, column=2, sticky="w", padx=10) 354 | 355 | def init_osd_setting(self): 356 | row = 6 357 | 358 | label = ttk.Label(self._frame, text="Enable OSD") 359 | label.grid(row=row, column=0, sticky="w", padx=20) 360 | 361 | self.osd_checkbutton = ttk.Checkbutton( 362 | self._frame, variable=self.osd_var, text="", command=self.on_osd_checkoutbutton_changed) 363 | self.osd_checkbutton.grid(row=row, column=0, sticky="w", padx=100) 364 | 365 | def init_reset_button(self): 366 | row = 6 367 | 368 | self.reset_button = tk.Button( 369 | self._frame, text="Reset settings", command=self.on_reset_button_press) 370 | self.reset_button.grid(row=row, column=1, sticky="w", padx=100) 371 | -------------------------------------------------------------------------------- /ch341.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import time 3 | import sys 4 | import os 5 | from ctypes import create_string_buffer 6 | from frame_monitor import frame_monitor 7 | import tkinter as tk 8 | from tkinter import ttk 9 | from global_var import * 10 | from ctypes import * 11 | import global_var 12 | import subprocess 13 | 14 | 15 | class ch341_class(object): 16 | 17 | def __init__(self): 18 | FW_5680SIZE = 65536 19 | FW_FPGASIZE = 2*1024*1024 20 | FW_8339SIZE = 10*1024*1024 21 | self.fw_5680_size = 0 22 | self.fw_5680_buf = create_string_buffer(FW_5680SIZE) 23 | self.fw_fpga_size = 0 24 | self.fw_fpga_buf = create_string_buffer(FW_FPGASIZE) 25 | self.fw_8339_size = 0 26 | self.fw_8339_buf = create_string_buffer(FW_8339SIZE) 27 | 28 | self.dll = None 29 | self.target = -1 30 | self.status = ch341_status.IDLE.value # idle 31 | self.read_setting_flag = 1 32 | self.dll_name = "CH341DLL.DLL" 33 | 34 | self.reconnect_vtx = 0 35 | 36 | # ------ monitor ------------- 37 | self.addr_brightness = 0x22 38 | self.addr_contrast = 0x23 39 | self.addr_saturation = 0x24 40 | self.addr_backlight = 0x25 41 | self.addr_cell_count = 0x26 42 | self.addr_warning_cell_voltage = 0x27 43 | self.addr_osd = 0x28 44 | self.addr_fpga_device = 0x65 # 7bit address 45 | 46 | self.target_id = 0 47 | self.fw_path = "" 48 | 49 | self.written_len = 0 50 | self.to_write_len = 100 51 | 52 | self.monitor_connected = 0 53 | self.iolength = 6 54 | self.iobuffer = create_string_buffer(65544) 55 | self.rdbuffer = [0] * 256 56 | self.write_crc = 0 57 | self.read_crc = 0 58 | 59 | # --------- event vrx ------------------------ 60 | self.WRITE_ENABLE = 0x06 61 | self.WRITE_DISABLE = 0x04 62 | self.READ_STATUS_REG1 = 0x05 63 | self.READ_STATUS_REG2 = 0x35 64 | self.READ_DATA = 0x03 65 | self.EWSR = 0x50 66 | self.FAST_READ = 0x0B 67 | self.PAGE_PROGRAM = 0x02 # Byte Prog Mode 68 | self.SECTOR_ERASE_4K = 0x20 # Erase 4 KByte of memory array 69 | self.BLOCK_ERASE_32K = 0x52 # Erase 32 KByte block of memory array 70 | self.BLOCK_ERASE_64K = 0xD8 # Erase 64 KByte block of memory array 71 | self.CHIP_ERASE = 0xC7 # Erase one chip flash 72 | self.SELECT_FPGA_5680 = 0x80 73 | self.FLASH_STATUS = 0x05 # read flash status 74 | self.FLASH_SET_5680 = 0xffff40ff 75 | self.FLASH_SET_FPGA = 0xffff80ff 76 | self.FLASH_BASE_ADDR = 0x00 77 | 78 | self.buffer_size = 2560 79 | self.write_buffer = create_string_buffer(self.buffer_size) 80 | 81 | try: 82 | self.dll = ctypes.WinDLL(self.dll_name) 83 | except: 84 | command = "resource\driver\SETUP.EXE \S" 85 | print("Need to install ch341 driver") 86 | print("Installing ...") 87 | subprocess.run(command, shell=True, capture_output=True, text=True) 88 | print("done") 89 | time.sleep(1) 90 | try: 91 | self.dll = ctypes.WinDLL(self.dll_name) 92 | except: 93 | a = 1 94 | 95 | def parse_monitor_fw(self, fw_path): 96 | try: 97 | with open(fw_path, "rb") as file: 98 | file.seek(2) 99 | self.fw_5680_size = int.from_bytes( 100 | file.read(4), byteorder='little') 101 | self.fw_fpga_size = int.from_bytes( 102 | file.read(4), byteorder='little') 103 | self.fw_8339_size = int.from_bytes( 104 | file.read(4), byteorder='little') 105 | if self.fw_5680_size < 65536 and self.fw_fpga_size < 10000000 and self.fw_8339_size < 10000000: 106 | self.fw_5680_buf = file.read(self.fw_5680_size) 107 | self.fw_fpga_buf = file.read(self.fw_fpga_size) 108 | self.fw_8339_buf = file.read(self.fw_8339_size) 109 | return 1 110 | else: 111 | return 0 112 | except: 113 | return 0 114 | 115 | def parse_event_vrx_fw(self, fw_path): 116 | try: 117 | with open(fw_path, "rb") as file: 118 | file_size = os.path.getsize(fw_path) 119 | head_size = file.read(8) 120 | self.fw_5680_size = int(head_size) - 2560 121 | self.fw_fpga_size = file_size - 8 - self.fw_5680_size 122 | if self.fw_5680_size < 65536 and self.fw_fpga_size < 10000000: 123 | return 1 124 | else: 125 | return 0 126 | except: 127 | return 0 128 | 129 | def ch341read_i2c(self, addr): 130 | self.dll.CH341ReadI2C(0, self.addr_fpga_device, addr, self.iobuffer) 131 | return int.from_bytes(self.iobuffer[0], byteorder='big') 132 | 133 | def read_setting(self): 134 | global_var.brightness = self.ch341read_i2c(self.addr_brightness) 135 | global_var.contrast = self.ch341read_i2c(self.addr_contrast) 136 | global_var.saturation = self.ch341read_i2c(self.addr_saturation) 137 | global_var.backlight = self.ch341read_i2c(self.addr_backlight) 138 | global_var.cell_count = self.ch341read_i2c(self.addr_cell_count) 139 | global_var.warning_cell_voltage = self.ch341read_i2c( 140 | self.addr_warning_cell_voltage) 141 | global_var.osd = self.ch341read_i2c(self.addr_osd) 142 | 143 | fpga_version = self.ch341read_i2c(0xff) 144 | print(f"cell:{global_var.cell_count:d} warning_cell:{global_var.warning_cell_voltage:d} fpga_version:0x{fpga_version:2x}") 145 | 146 | def set_stream(self, cs): 147 | if cs == True: 148 | self.dll.CH341SetStream(0, 0x80) 149 | else: 150 | self.dll.CH341SetStream(0, 0x81) 151 | 152 | def stream_spi4(self): 153 | self.dll.CH341StreamSPI4(0, 0x80, self.ilength, self.iobuffer) 154 | 155 | def flash_switch0(self): 156 | self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0x4300) 157 | 158 | def flash_switch1(self): 159 | self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0x8300) 160 | 161 | def flash_switch2(self): 162 | self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xc800) 163 | 164 | def flash_release(self): 165 | self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xc200) 166 | 167 | def flash_read_id(self): 168 | self.iobuffer[0] = 0x9f 169 | self.iobuffer[1] = 0x9f 170 | self.iobuffer[2] = 0x9f 171 | self.iobuffer[3] = 0x9f 172 | self.iobuffer[4] = 0x9f 173 | self.iobuffer[5] = 0x9f 174 | self.ilength = 6 175 | 176 | self.set_stream(0) 177 | self.stream_spi4() 178 | self.set_stream(1) 179 | 180 | return int.from_bytes(self.iobuffer[1], byteorder='big') * 256 * 256 \ 181 | + int.from_bytes(self.iobuffer[2], byteorder='big') * 256 \ 182 | + int.from_bytes(self.iobuffer[3], byteorder='big') 183 | 184 | def flash_write_enable(self): 185 | self.iobuffer[0] = 0x06 186 | self.ilength = 1 187 | 188 | self.set_stream(0) 189 | self.stream_spi4() 190 | self.set_stream(1) 191 | 192 | def flash_erase_block64(self): 193 | self.iobuffer[0] = 0xd8 194 | self.iobuffer[1] = 0 195 | self.iobuffer[2] = 0 196 | self.iobuffer[3] = 0 197 | self.ilength = 4 198 | 199 | self.set_stream(0) 200 | self.stream_spi4() 201 | self.set_stream(1) 202 | 203 | def flash_erase_block64_m(self, addr): 204 | self.iobuffer[0] = 0xd8 205 | self.iobuffer[1] = (addr >> 16) & 0xff 206 | self.iobuffer[2] = (addr >> 8) & 0xff 207 | self.iobuffer[3] = (addr >> 0) & 0xff 208 | self.ilength = 4 209 | 210 | self.set_stream(0) 211 | self.stream_spi4() 212 | self.set_stream(1) 213 | 214 | def flash_erase_section(self, addr): 215 | self.iobuffer[0] = 0x20 216 | self.iobuffer[1] = (addr >> 16) & 0x1f 217 | self.iobuffer[2] = (addr >> 8) & 0x1f 218 | self.iobuffer[3] = (addr >> 0) & 0x1f 219 | self.ilength = 4 220 | 221 | self.set_stream(0) 222 | self.stream_spi4() 223 | self.set_stream(1) 224 | 225 | def flash_write_disable(self): 226 | self.iobuffer[0] = 0x04 227 | self.ilength = 1 228 | 229 | self.set_stream(0) 230 | self.stream_spi4() 231 | self.set_stream(1) 232 | 233 | def flash_is_busy(self): 234 | self.iobuffer[0] = 0x05 235 | self.iobuffer[1] = 0x00 236 | self.ilength = 2 237 | 238 | self.set_stream(0) 239 | self.stream_spi4() 240 | self.set_stream(1) 241 | 242 | return (int.from_bytes(self.iobuffer[1], byteorder='little') & 1) 243 | 244 | def flash_erase_flash(self, block): 245 | self.flash_write_enable() 246 | self.flash_erase_block64_m(block) 247 | self.flash_wait_busy() 248 | self.flash_write_disable() 249 | 250 | def flash_wait_busy(self): 251 | while True: 252 | if self.flash_is_busy() == 0: 253 | return 254 | 255 | def flash_erase_vtx(self): 256 | self.flash_write_enable() 257 | self.flash_erase_block64() 258 | self.flash_wait_busy() 259 | self.flash_write_disable() 260 | 261 | self.flash_write_enable() 262 | self.flash_erase_section(65536) 263 | self.flash_wait_busy() 264 | self.flash_write_disable() 265 | 266 | def flash_write_page(self, base_address, length, fw): 267 | self.iobuffer[0] = 0x02 268 | self.iobuffer[1] = (base_address >> 16) & 0xff 269 | self.iobuffer[2] = (base_address >> 8) & 0xff 270 | self.iobuffer[3] = (base_address >> 0) & 0xff 271 | self.ilength = 4 + length 272 | 273 | for i in range(length): 274 | try: 275 | self.iobuffer[4+i] = fw[i] 276 | except: 277 | self.iobuffer[4+i] = 0xff 278 | 279 | self.write_crc += int.from_bytes( 280 | self.iobuffer[4 + i], byteorder='little') 281 | 282 | self.set_stream(0) 283 | self.stream_spi4() 284 | self.set_stream(1) 285 | 286 | def flash_read_page(self, base_address, length): 287 | self.iobuffer[0] = 0x03 288 | self.iobuffer[1] = (base_address >> 16) & 0xff 289 | self.iobuffer[2] = (base_address >> 8) & 0xff 290 | self.iobuffer[3] = (base_address >> 0) & 0xff 291 | self.ilength = 4 + length 292 | 293 | self.set_stream(0) 294 | self.stream_spi4() 295 | self.set_stream(1) 296 | 297 | for i in range(256): 298 | self.read_crc += int.from_bytes( 299 | self.iobuffer[4 + i], byteorder='little') 300 | 301 | def connect_vtx(self): 302 | if self.dll.CH341OpenDevice(0) < 0: 303 | return 0 304 | else: 305 | self.flash_switch0() 306 | flash_id_0 = self.flash_read_id() 307 | self.flash_switch1() 308 | flash_id_1 = self.flash_read_id() 309 | self.flash_switch2() 310 | flash_id_2 = self.flash_read_id() 311 | if flash_id_0 == flash_id_1 and flash_id_1 == flash_id_2: 312 | if flash_id_0 == 0xEF4014 or flash_id_0 == 0x5E6014 or flash_id_0 == 0x856014: 313 | return 1 314 | 315 | return 0 316 | 317 | def flash_write_target_id(self): 318 | self.flash_write_enable() 319 | self.flash_write_page(65536, 1, [self.target_id]) 320 | self.flash_write_disable() 321 | self.flash_wait_busy() 322 | 323 | def flash_write_fw(self): 324 | size = os.path.getsize(self.fw_path) 325 | file = open(self.fw_path, "rb") 326 | fw = file.read() 327 | 328 | page_number = (size + (1 << 8) - 1) >> 8 329 | self.write_crc = 0 330 | self.read_crc = 0 331 | 332 | for page in range(page_number): 333 | base_address = page << 8 334 | self.flash_write_enable() 335 | self.flash_write_page(base_address, 256, fw[base_address:]) 336 | self.flash_write_disable() 337 | self.flash_wait_busy() 338 | 339 | self.flash_read_page(base_address, 256) 340 | 341 | my_ch341.written_len += 256 342 | 343 | if self.write_crc == self.read_crc: 344 | return 1 345 | else: 346 | return 0 347 | 348 | def connect_monitor(self, sleep_sec): 349 | if self.dll.CH341OpenDevice(0) < 0: 350 | return 0 351 | else: 352 | # self.dll.CH341SetStream(0, 0x82) 353 | time.sleep(sleep_sec) 354 | self.flash_switch1() 355 | flash_id_2 = self.flash_read_id() 356 | if flash_id_2 == 0xEF4018: 357 | return 1 358 | else: 359 | return 0 360 | 361 | def fw_write_to_flash(self, fw_buf, fw_size): 362 | page_number = (fw_size + (1 << 8) - 1) >> 8 363 | for page in range(page_number): 364 | block = page << 8 365 | if (block & 0xffff) == 0: 366 | self.flash_erase_flash(block) 367 | 368 | base_address = page << 8 369 | self.flash_write_enable() 370 | self.flash_write_page(base_address, 256, fw_buf[base_address:]) 371 | self.flash_write_disable() 372 | self.flash_wait_busy() 373 | my_ch341.written_len += 256 374 | """ 375 | for page in range(page_number): 376 | base_address = page << 8 377 | self.flash_read_page(base_address, 256) 378 | 379 | for i in range(256): 380 | self.read_crc += int.from_bytes(self.rdbuffer[i], byteorder='little') 381 | """ 382 | 383 | # ---------------- event_vrx -------------------------------- 384 | def connect_event_vrx(self): 385 | if self.dll.CH341OpenDevice(nIndex) < 0: 386 | return 0 387 | else: 388 | self.dll.CH341SetStream(nIndex, 0x81) 389 | return 1 390 | 391 | def FlashChipErase(self): 392 | self.dll.CH341SetStream(nIndex, 0x80) 393 | 394 | self.iobuffer[0] = self.WRITE_ENABLE 395 | self.dll.CH341StreamSPI4( 396 | nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) 397 | 398 | self.iobuffer[0] = self.CHIP_ERASE 399 | self.dll.CH341StreamSPI4( 400 | nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) 401 | 402 | self.iobuffer[0] = self.WRITE_DISABLE 403 | self.dll.CH341StreamSPI4( 404 | nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) 405 | 406 | def data_cpy(self, dest, dst_off, src, src_off, length): 407 | for i in range(length): 408 | dest[dst_off+i] = src[src_off+i] 409 | 410 | def write_SPI(self, addr, data_buf, size): 411 | temp_write_buffer = create_string_buffer(int(PAGE_SIZE+HEAD_SIZE)) 412 | self.dll.CH341SetStream(nIndex, 0x80) 413 | 414 | page = 0 415 | while size > PAGE_SIZE: 416 | temp_write_buffer[0] = self.WRITE_ENABLE 417 | self.dll.CH341StreamSPI4( 418 | nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) 419 | 420 | temp_write_buffer[0] = self.PAGE_PROGRAM 421 | temp_write_buffer[1] = ((addr & 0xFF0000) >> 16) 422 | temp_write_buffer[2] = ((addr & 0x00FF00) >> 8) 423 | temp_write_buffer[3] = (addr & 0x0000FF) 424 | 425 | self.data_cpy(temp_write_buffer, HEAD_SIZE, data_buf, 426 | (page * PAGE_SIZE), PAGE_SIZE) 427 | self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, int( 428 | PAGE_SIZE+HEAD_SIZE), temp_write_buffer) 429 | 430 | temp_write_buffer[0] = self.WRITE_DISABLE 431 | self.dll.CH341StreamSPI4( 432 | nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) 433 | self.dll.CH341SetDelaymS(nIndex, 2) 434 | 435 | size -= PAGE_SIZE 436 | page += 1 437 | addr += PAGE_SIZE 438 | 439 | temp_write_buffer[0] = self.WRITE_ENABLE 440 | self.dll.CH341StreamSPI4( 441 | nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) 442 | 443 | temp_write_buffer[0] = self.PAGE_PROGRAM 444 | temp_write_buffer[1] = ((addr & 0xFF0000) >> 16) 445 | temp_write_buffer[2] = ((addr & 0x00FF00) >> 8) 446 | temp_write_buffer[3] = (addr & 0x0000FF) 447 | if size < PAGE_SIZE: 448 | self.data_cpy(temp_write_buffer, HEAD_SIZE, 449 | data_buf, (page * PAGE_SIZE), size) 450 | else: 451 | self.data_cpy(temp_write_buffer, HEAD_SIZE, data_buf, 452 | (page * PAGE_SIZE), PAGE_SIZE) 453 | 454 | self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, int( 455 | PAGE_SIZE+HEAD_SIZE), temp_write_buffer) 456 | 457 | temp_write_buffer[0] = self.WRITE_DISABLE 458 | self.dll.CH341StreamSPI4( 459 | nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) 460 | 461 | def write_event_vrx_fw_to_flash(self, path): 462 | file = open(path, "rb") 463 | file_size = os.path.getsize(path) # file_size: 2383867 464 | head_size = file.read(8) 465 | file5680_size = int(head_size) - 2560 # 58581 = 64141 - 2560 466 | print("file: ", path) 467 | 468 | # erase 5680 flash 469 | my_ch341.written_len += 15 * PAGE_SIZE 470 | self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_5680) 471 | time.sleep(0.01) 472 | self.FlashChipErase() 473 | my_ch341.written_len += 15 * PAGE_SIZE 474 | time.sleep(1) 475 | 476 | # erase fpga flash 477 | self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) 478 | time.sleep(0.01) 479 | self.FlashChipErase() 480 | my_ch341.written_len += 15 * PAGE_SIZE 481 | # time.sleep(65) # Wait for all flash erase to be completed 482 | for i in range(65): 483 | time.sleep(1) 484 | my_ch341.written_len += 10 * PAGE_SIZE 485 | 486 | # write 5680 data to flash 487 | self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_5680) 488 | time.sleep(0.01) 489 | file.seek(8) 490 | 491 | page = 0 492 | while page * self.buffer_size < file5680_size: 493 | self.write_buffer = file.read(self.buffer_size) 494 | self.write_SPI(self.FLASH_BASE_ADDR + (page * self.buffer_size), 495 | self.write_buffer, len(self.write_buffer)) 496 | my_ch341.written_len += 15 * PAGE_SIZE 497 | time.sleep(0.1) 498 | page += 1 499 | 500 | # write 5680 last page data 501 | page -= 1 502 | self.write_buffer = file.read( 503 | file5680_size - (page * self.buffer_size)) 504 | self.write_SPI(self.FLASH_BASE_ADDR + (page + 1) * self.buffer_size, 505 | self.write_buffer, file5680_size - (page * self.buffer_size)) 506 | time.sleep(1) 507 | my_ch341.written_len += 15 * PAGE_SIZE 508 | 509 | # write fpga data to flash 510 | self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) 511 | page = 0 512 | while True: 513 | self.write_buffer = file.read(self.buffer_size) 514 | self.write_SPI(self.FLASH_BASE_ADDR + (page * self.buffer_size), 515 | self.write_buffer, len(self.write_buffer)) 516 | time.sleep(0.1) 517 | my_ch341.written_len += 9 * PAGE_SIZE 518 | 519 | page += 1 520 | if len(self.write_buffer) != self.buffer_size: 521 | break 522 | 523 | file.flush() 524 | file.close() 525 | self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) 526 | 527 | 528 | my_ch341 = ch341_class() 529 | 530 | 531 | def ch341_thread_proc(): 532 | while True: 533 | if my_ch341.status == ch341_status.STATUS_EXIT.value: 534 | sys.exit() 535 | 536 | if my_ch341.status == ch341_status.VTX_DISCONNECTED.value: # connect vtx 537 | if my_ch341.connect_vtx() == 1: 538 | my_ch341.status = ch341_status.VTX_CONNECTED.value 539 | 540 | elif my_ch341.status == ch341_status.VTX_UPDATE.value: # update vtx 541 | my_ch341.written_len = 0 542 | my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) 543 | 544 | if my_ch341.to_write_len == 0 or my_ch341.to_write_len >= 65536: # check fw size 545 | my_ch341.status = ch341_status.VTX_FW_ERROR.value 546 | else: 547 | my_ch341.flash_erase_vtx() 548 | my_ch341.flash_write_target_id() 549 | if my_ch341.flash_write_fw() == 1: 550 | my_ch341.status = ch341_status.VTX_UPDATEDONE.value 551 | else: 552 | my_ch341.status = ch341_status.VTX_UPDATE_FAILED.value 553 | my_ch341.reconnect_vtx = 0 554 | elif my_ch341.status == ch341_status.VTX_RECONNECT.value: # reconnect vtx 555 | if my_ch341.reconnect_vtx == 0: 556 | if my_ch341.connect_vtx() == 0: 557 | my_ch341.reconnect_vtx = 1 558 | elif my_ch341.reconnect_vtx == 1: 559 | if my_ch341.connect_vtx() == 1: 560 | my_ch341.reconnect_vtx = 0 561 | my_ch341.status = ch341_status.VTX_RECONNECTDONE.value 562 | 563 | # -------- Monitor ----------------- 564 | elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value: # check monitor is alive 565 | if my_ch341.connect_monitor(0.35) == 1: 566 | if my_ch341.monitor_connected == 0: 567 | time.sleep(0.5) 568 | my_ch341.read_setting() 569 | my_ch341.monitor_connected = 1 570 | else: 571 | my_ch341.monitor_connected = 0 572 | 573 | elif my_ch341.status == ch341_status.MONITOR_UPDATE.value: # update monitor 574 | # check fw size 575 | if my_ch341.parse_monitor_fw(my_ch341.fw_path) == 0: 576 | my_ch341.status = ch341_status.MONITOR_FW_ERROR.value 577 | else: 578 | my_ch341.flash_switch0() 579 | my_ch341.fw_write_to_flash( 580 | my_ch341.fw_5680_buf, my_ch341.fw_5680_size) 581 | my_ch341.flash_switch1() 582 | my_ch341.fw_write_to_flash( 583 | my_ch341.fw_fpga_buf, my_ch341.fw_fpga_size) 584 | my_ch341.flash_switch2() 585 | my_ch341.fw_write_to_flash( 586 | my_ch341.fw_8339_buf, my_ch341.fw_8339_size) 587 | my_ch341.dll.CH341CloseDevice(0) 588 | my_ch341.flash_release() 589 | my_ch341.status = ch341_status.MONITOR_UPDATEDONE.value 590 | 591 | # ---------------------- event_vrx ------------------------------------ 592 | elif my_ch341.status == ch341_status.EVENT_VRX_DISCONNECTED.value: # connect event vrx 593 | if my_ch341.connect_event_vrx() == 1: 594 | my_ch341.status = ch341_status.EVENT_VRX_CONNECTED.value 595 | 596 | elif my_ch341.status == ch341_status.EVENT_VRX_UPDATE.value: # update event_vrx 597 | file = open(my_ch341.fw_path, "rb") 598 | file_size = os.path.getsize(my_ch341.fw_path) 599 | head_size = file.read(8) 600 | try: 601 | file5680_size = int(head_size) - 2560 602 | except: 603 | file5680_size = 65536 604 | my_ch341.status = ch341_status.EVENT_VRX_FW_ERROR.value 605 | 606 | file.close() 607 | if file_size > 10000000 or file5680_size >= 65536: 608 | my_ch341.status = ch341_status.EVENT_VRX_FW_ERROR.value 609 | else: 610 | my_ch341.write_event_vrx_fw_to_flash(my_ch341.fw_path) 611 | my_ch341.status = ch341_status.EVENT_VRX_UPDATEDONE.value 612 | else: 613 | time.sleep(0.1) 614 | -------------------------------------------------------------------------------- /main_window.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | import tkinter as tk 5 | from tkinter import ttk 6 | 7 | from frame_vtx import frame_vtx 8 | from frame_monitor import frame_monitor 9 | from frame_event_vrx import frame_event_vrx 10 | from frame_programmer import frame_programmer 11 | from frame_statusbar import frame_statusbar 12 | 13 | from download import * 14 | from parse_file import * 15 | import global_var 16 | from global_var import * 17 | from ch341 import my_ch341 18 | import base64 19 | from icon32 import icon32 20 | import io 21 | 22 | 23 | class MyGUI: 24 | 25 | def __init__(self, init_window_name): 26 | self.winWidth = 800 27 | self.winHeight = 600 28 | self.title = "HDZero Programmer " + "V2.0.0" 29 | 30 | self._programmer_frame = None 31 | self._main_window = init_window_name 32 | self._main_window.grid_rowconfigure(0, weight=18) 33 | self._main_window.grid_rowconfigure(1, weight=0) 34 | self._main_window.grid_rowconfigure(2, weight=0) 35 | self._main_window.grid_columnconfigure(0, weight=1) 36 | 37 | self.init_tab() 38 | self.init_programmer() 39 | self._programmer_frame.frame().grid(row=1, column=0, sticky="nsew") 40 | 41 | self.init_statusbar() 42 | self._statusbar_frame.frame().grid(row=2, column=0, sticky="nsew") 43 | 44 | self.is_update_monitor = 0 45 | self.monitor_is_alive = 0 46 | 47 | self.downloading_window_status = 0 48 | self.network_error = 0 49 | 50 | def init_main_window(self): 51 | screenWidth = self._main_window.winfo_screenwidth() 52 | screenHeight = self._main_window.winfo_screenheight() 53 | x = int((screenWidth - self.winWidth) / 2) 54 | y = int((screenHeight - self.winHeight) / 2) 55 | 56 | self._main_window.title(self.title) 57 | self._main_window.geometry("%sx%s+%s+%s" % 58 | (self.winWidth, self.winHeight, x, y)) 59 | self._main_window.resizable(False, False) 60 | 61 | icon_base64 = base64.b64decode(icon32) 62 | icon_bytes = io.BytesIO(icon_base64) 63 | icon = tk.PhotoImage(data=icon_bytes.getvalue()) 64 | 65 | self._main_window.iconphoto(True, icon) 66 | 67 | def init_tab(self): 68 | self._tabCtrl = ttk.Notebook(self._main_window) 69 | self.init_main_window() 70 | self.init_vtx_frame() 71 | self.init_monitor_frame() 72 | self.init_event_vrx_frame() 73 | self._tabCtrl.select(self._vtx_frame.frame()) 74 | self._tabCtrl.grid(row=0, column=0, sticky="nsew") 75 | self._tabCtrl.bind("<>", self.on_tab_changed) 76 | self.notebook_disable() 77 | 78 | def notebook_disable(self): 79 | self._tabCtrl.state(['disabled']) 80 | 81 | def notebook_enable(self): 82 | self._tabCtrl.state(['!disabled']) 83 | 84 | def init_programmer(self): 85 | self._programmer_frame = frame_programmer(self._main_window) 86 | self._programmer_frame.version_combobox.bind( 87 | "<>", self.on_select_version) 88 | self._programmer_frame.online_fw_button["command"] = self._programmer_frame.online_fw_button_hidden 89 | self._programmer_frame.local_fw_button["command"] = self.on_load_local_firmware 90 | self._programmer_frame.update_button["command"] = self.on_update 91 | 92 | def init_vtx_frame(self): 93 | self._vtx_frame = frame_vtx(self._tabCtrl) 94 | for i in range(0, len(list(my_parse.vtx_info.keys()))): 95 | self._vtx_frame.radio_button[i].bind( 96 | "", self.on_select_vtx_target) 97 | 98 | def init_monitor_frame(self): 99 | self._monitor_frame = frame_monitor(self._tabCtrl) 100 | 101 | def init_event_vrx_frame(self): 102 | self._event_vrx_frame = frame_event_vrx(self._tabCtrl) 103 | 104 | def init_statusbar(self): 105 | self._statusbar_frame = frame_statusbar(self._main_window) 106 | 107 | def on_select_vtx_target(self): 108 | selected_target = self._vtx_frame.vtx_target.get() 109 | print("Selected target:", selected_target) 110 | try: 111 | version_list = list(my_parse.vtx_info[selected_target].keys())[1:] 112 | self._programmer_frame.version_combobox_update_values(version_list) 113 | except: 114 | pass 115 | self._programmer_frame.version_combobox_set_default() 116 | self._programmer_frame.version_combobox_enable() 117 | 118 | self._programmer_frame.online_fw_button_enable(self.network_error) 119 | self._programmer_frame.online_fw_button_set_str_default() 120 | 121 | self._programmer_frame.local_fw_button_enable() 122 | self._programmer_frame.local_fw_button_set_str_default() 123 | 124 | def on_select_version(self, event): 125 | selected_version = self._programmer_frame.version_combobox.get() 126 | print("Selected:", selected_version) 127 | self._programmer_frame.mode = 0 128 | self._programmer_frame.online_fw_button_show() 129 | 130 | if self.current_selected_tab() == 0: 131 | self._programmer_frame.update_button_enable() 132 | self._programmer_frame.url = my_parse.vtx_info[self._vtx_frame.vtx_target.get( 133 | )][self._programmer_frame.version_combobox.get()] 134 | self._programmer_frame.online_fw_button_set_str( 135 | self._programmer_frame.version_combobox.get()) 136 | elif self.current_selected_tab() == 1: 137 | self._programmer_frame.update_button_enable() 138 | self._programmer_frame.url = my_parse.monitor_info[self._programmer_frame.version_combobox.get( 139 | )] 140 | self._programmer_frame.online_fw_button_set_str( 141 | self._programmer_frame.version_combobox.get()) 142 | elif self.current_selected_tab() == 2: 143 | self._programmer_frame.update_button_enable() 144 | self._programmer_frame.url = my_parse.event_vrx_info[self._programmer_frame.version_combobox.get( 145 | )] 146 | self._programmer_frame.online_fw_button_set_str( 147 | self._programmer_frame.version_combobox.get()) 148 | 149 | def on_load_local_firmware(self): 150 | self._programmer_frame.online_fw_button_show() 151 | self._programmer_frame.select_local_file() 152 | 153 | if self._programmer_frame.local_file_path == '': 154 | return 155 | 156 | if self.current_selected_tab() == 0: 157 | self._programmer_frame.mode = 1 158 | self._programmer_frame.update_button_enable() 159 | my_ch341.fw_path = self._programmer_frame.local_file_path 160 | 161 | elif self.current_selected_tab() == 1: 162 | self._programmer_frame.update_button_enable() 163 | self._programmer_frame.mode = 1 164 | my_ch341.fw_path = self._programmer_frame.local_file_path 165 | 166 | elif self.current_selected_tab() == 2: 167 | self._programmer_frame.update_button_enable() 168 | self._programmer_frame.mode = 1 169 | my_ch341.fw_path = self._programmer_frame.local_file_path 170 | 171 | def on_update(self): 172 | if self.current_selected_tab() == 0: 173 | 174 | if self._programmer_frame.is_cancel == 0: 175 | my_ch341.status = ch341_status.VTX_DISCONNECTED.value # to connect vtx 176 | my_download.to_stop = 0 177 | 178 | self.notebook_disable() 179 | 180 | self._vtx_frame.radio_button_disable() 181 | 182 | # self._programmer_frame.update_button_disable() 183 | self._programmer_frame.update_button_set_text_cancel() 184 | self._programmer_frame.update_button_enable() 185 | self._programmer_frame.version_combobox_disable() 186 | self._programmer_frame.local_fw_button_disable() 187 | self._programmer_frame.online_fw_button_disable() 188 | 189 | self._statusbar_frame.status_label_set_text( 190 | "Connecting VTX ...", "SystemButtonFace") 191 | self._statusbar_frame.progress_bar_set_value(0) 192 | else: 193 | my_ch341.status = ch341_status.IDLE.value 194 | my_download.to_stop = 1 195 | 196 | self.notebook_enable() 197 | 198 | self._vtx_frame.radio_button_enable() 199 | 200 | self._programmer_frame.update_button_set_text_update("VTX") 201 | self._programmer_frame.update_button_enable() 202 | self._programmer_frame.version_combobox_enable() 203 | self._programmer_frame.local_fw_button_enable() 204 | self._programmer_frame.online_fw_button_enable( 205 | self.network_error) 206 | 207 | self._statusbar_frame.label_hidden() 208 | self._statusbar_frame.progress_bar_set_value(0) 209 | 210 | elif self.current_selected_tab() == 1: 211 | if self._programmer_frame.is_cancel == 0: 212 | self.is_update_monitor = 1 213 | my_ch341.monitor_connected = 0 214 | my_ch341.status = ch341_status.IDLE.value 215 | my_download.to_stop = 0 216 | 217 | self.notebook_disable() 218 | 219 | self._monitor_frame.setting_disable() 220 | 221 | self._programmer_frame.update_button_set_text_cancel() 222 | self._programmer_frame.update_button_enable() 223 | self._programmer_frame.version_combobox_disable() 224 | self._programmer_frame.local_fw_button_disable() 225 | self._programmer_frame.online_fw_button_disable() 226 | 227 | self._statusbar_frame.status_label_set_text( 228 | "Connecting Monitor ...", "SystemButtonFace") 229 | self._statusbar_frame.progress_bar_set_value(0) 230 | else: 231 | print("cancel Monitor programmer") 232 | self.is_update_monitor = 0 233 | my_ch341.monitor_connected = 0 234 | self.monitor_is_alive = 0 235 | my_ch341.status = ch341_status.IDLE.value 236 | my_download.to_stop = 1 237 | 238 | self.notebook_enable() 239 | 240 | self._monitor_frame.setting_disable() 241 | 242 | self._programmer_frame.update_button_set_text_update( 243 | "Monitor") 244 | self._programmer_frame.update_button_enable() 245 | self._programmer_frame.version_combobox_enable() 246 | self._programmer_frame.local_fw_button_enable() 247 | self._programmer_frame.online_fw_button_enable( 248 | self.network_error) 249 | 250 | self._statusbar_frame.label_hidden() 251 | self._statusbar_frame.progress_bar_set_value(0) 252 | 253 | elif self.current_selected_tab() == 2: 254 | if self._programmer_frame.is_cancel == 0: 255 | my_ch341.status = ch341_status.EVENT_VRX_DISCONNECTED.value 256 | my_download.to_stop = 0 257 | 258 | self.notebook_disable() 259 | 260 | self._programmer_frame.update_button_set_text_cancel() 261 | self._programmer_frame.update_button_enable() 262 | self._programmer_frame.version_combobox_disable() 263 | self._programmer_frame.local_fw_button_disable() 264 | self._programmer_frame.online_fw_button_disable() 265 | self._statusbar_frame.status_label_set_text( 266 | "Connecting Event VRX ...", "SystemButtonFace") 267 | self._statusbar_frame.progress_bar_set_value(0) 268 | else: 269 | my_download.to_stop = 1 270 | my_ch341.status = ch341_status.IDLE.value 271 | 272 | self.notebook_enable() 273 | 274 | self._programmer_frame.update_button_set_text_update( 275 | "Event VRX") 276 | self._programmer_frame.update_button_disable() 277 | self._programmer_frame.version_combobox_enable() 278 | self._programmer_frame.local_fw_button_enable() 279 | self._programmer_frame.online_fw_button_enable( 280 | self.network_error) 281 | 282 | self._statusbar_frame.label_hidden() 283 | self._statusbar_frame.progress_bar_set_value(0) 284 | 285 | def on_tab_changed(self, event): 286 | print("Selected tab:", self.current_selected_tab()) 287 | self._statusbar_frame.label_hidden() 288 | if self.current_selected_tab() == 0: 289 | self._vtx_frame.radio_button_reset() # select first vtx target 290 | 291 | self._programmer_frame.version_combobox_update_values("") 292 | self._programmer_frame.version_combobox_set_default() 293 | self._programmer_frame.version_combobox_disable() 294 | # self._programmer_frame.local_fw_button_disable() 295 | self._programmer_frame.update_button_set_text_update("VTX") 296 | self._programmer_frame.update_button_disable() 297 | self._programmer_frame.online_fw_button_show() 298 | 299 | self.on_select_vtx_target() 300 | my_ch341.status = ch341_status.IDLE.value 301 | 302 | elif self.current_selected_tab() == 1: 303 | self._monitor_frame.setting_disable() 304 | 305 | version_list = list(my_parse.monitor_info.keys()) 306 | self._programmer_frame.version_combobox_update_values(version_list) 307 | self._programmer_frame.version_combobox_set_default() 308 | self._programmer_frame.version_combobox_enable() 309 | self._programmer_frame.local_fw_button_enable() 310 | self._programmer_frame.update_button_set_text_update( 311 | "Monitor") 312 | self._programmer_frame.update_button_disable() 313 | self._programmer_frame.online_fw_button_show() 314 | self.monitor_is_alive = 0 315 | my_ch341.monitor_connected = 0 316 | 317 | # to connect Monitor 318 | my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value 319 | elif self.current_selected_tab() == 2: 320 | version_list = list(my_parse.event_vrx_info.keys()) 321 | self._programmer_frame.version_combobox_update_values(version_list) 322 | self._programmer_frame.version_combobox_enable() 323 | self._programmer_frame.version_combobox_set_default() 324 | self._programmer_frame.local_fw_button_enable() 325 | self._programmer_frame.update_button_set_text_update("Event VRX") 326 | self._programmer_frame.update_button_disable() 327 | self._programmer_frame.online_fw_button_show() 328 | 329 | my_ch341.status = ch341_status.IDLE.value 330 | 331 | self._programmer_frame.deselect() 332 | self._programmer_frame.online_fw_button_set_str_default() 333 | self._programmer_frame.online_fw_button_enable(self.network_error) 334 | self._programmer_frame.local_fw_button_set_str_default() 335 | 336 | def current_selected_tab(self): 337 | return self._tabCtrl.index(self._tabCtrl.select()) 338 | 339 | def create_downloading_firmware_window(self): 340 | if self.downloading_window_status == 1: 341 | pass 342 | screenWidth = self._main_window.winfo_screenwidth() 343 | screenHeight = self._main_window.winfo_screenheight() 344 | x = int((screenWidth - 300) / 2) 345 | y = int((screenHeight - 50) / 2) 346 | 347 | self.downloading_window = tk.Toplevel() 348 | self.downloading_window.geometry("%sx%s+%s+%s" % 349 | (300, 50, x, y)) 350 | self.downloading_window.resizable(False, False) 351 | 352 | self.downloading_window.title("Downloading") 353 | self.downloading_label = tk.Label(self.downloading_window, 354 | text="Downloading firmware list from github ...") 355 | self.downloading_label.pack(pady=10) 356 | 357 | self.downloading_window_status = 1 358 | self.downloading_window.overrideredirect(True) 359 | 360 | self._main_window.attributes('-disable', True) 361 | 362 | def set_downloading_label(self, str): 363 | self.downloading_label.config(text=str) 364 | 365 | def destroy_downloading_firmware_window(self): 366 | self.downloading_window.destroy() 367 | self.notebook_enable() 368 | self._main_window.focus_force() 369 | 370 | def refresh(self): 371 | ''' 372 | 1. update vtx 373 | - press update button 374 | - connect vtx 375 | - wait until vtx is connected 376 | - download fw if use online fw 377 | - wait until download is done if use online fw 378 | - write vtx id & fw to flash 379 | - wait until write done 380 | 381 | 2. update Monitor 382 | - 383 | - 384 | 3. update event vrx 385 | ''' 386 | 387 | # init 388 | if my_download.status == download_status.FILE_PARSE.value: 389 | my_download.status = download_status.IDLE.value 390 | my_parse.parse_vtx_common() 391 | ret0 = my_parse.parse_vtx_releases() 392 | ret1 = my_parse.parse_event_vrx_releases() 393 | ret2 = my_parse.parse_monitor_releases() 394 | ret3 = my_parse.parse_vtx_tragets_image( 395 | len(list(my_parse.vtx_info.keys()))) 396 | 397 | if ret0 == 0 or ret1 == 0 or ret2 == 0 or ret3 == 0: 398 | self.network_error = 1 399 | self.set_downloading_label("Download firmware list failed") 400 | self._main_window.update() 401 | time.sleep(1) 402 | self.destroy_downloading_firmware_window() 403 | 404 | self._vtx_frame.create_radio_button_list( 405 | list(my_parse.vtx_info.keys()), self.on_select_vtx_target, my_parse.vtx_target_image) 406 | self._main_window.attributes('-disable', False) 407 | 408 | # vtx 409 | if self.current_selected_tab() == 0: 410 | # download 411 | if my_download.status == download_status.DOWNLOAD_VTX_FW_DONE.value: 412 | my_download.status = download_status.IDLE.value 413 | selected_target = self._vtx_frame.vtx_target.get() 414 | my_ch341.target_id = my_parse.vtx_info[selected_target]["id"] 415 | my_ch341.fw_path = my_download.save_path 416 | my_ch341.written_len = 0 417 | my_ch341.status = ch341_status.VTX_UPDATE.value 418 | self._statusbar_frame.label_hidden() 419 | self._programmer_frame.update_button_set_text_update("VTX") 420 | self._programmer_frame.update_button_disable() 421 | elif my_download.status == download_status.DOWNLOAD_VTX_FW_FAILED.value: 422 | my_download.status = download_status.IDLE.value 423 | my_ch341.status = ch341_status.IDLE.value 424 | 425 | self.notebook_enable() 426 | 427 | self._vtx_frame.radio_button_enable() 428 | 429 | self._programmer_frame.version_combobox_enable() 430 | self._programmer_frame.online_fw_button_enable( 431 | self.network_error) 432 | self._programmer_frame.version_combobox_set_default() 433 | self._programmer_frame.local_fw_button_enable() 434 | self._programmer_frame.update_button_set_text_update("VTX") 435 | # self._programmer_frame.update_button_disable() 436 | self._programmer_frame.deselect() 437 | 438 | self._statusbar_frame.progress_bar_set_value(0) 439 | self._statusbar_frame.status_label_set_text( 440 | "Firmware update failed. Network error.", "red") 441 | 442 | # update 443 | if my_ch341.status == ch341_status.VTX_CONNECTED.value: # vtx is connected 444 | my_ch341.status = ch341_status.IDLE.value 445 | if self._programmer_frame.mode == 0: 446 | my_download.url = self._programmer_frame.url 447 | my_download.save_path = "resource/FW" 448 | my_download.status = download_status.DOWNLOAD_VTX_FW.value # download url 449 | self._statusbar_frame.status_label_set_text( 450 | "Downloading Firmware ...", "SystemButtonFace") 451 | else: 452 | selected_target = self._vtx_frame.vtx_target.get() 453 | my_ch341.target_id = my_parse.vtx_info[selected_target]["id"] 454 | my_ch341.fw_path = self._programmer_frame.local_file_path 455 | my_ch341.status = ch341_status.VTX_UPDATE.value 456 | self._statusbar_frame.label_hidden() 457 | self._programmer_frame.update_button_set_text_update("VTX") 458 | self._programmer_frame.update_button_disable() 459 | 460 | elif my_ch341.status == ch341_status.VTX_UPDATE.value: # refresh progress bar 461 | value = (my_ch341.written_len / 462 | my_ch341.to_write_len * 100) % 101 463 | self._statusbar_frame.progress_bar_set_value(value) 464 | elif my_ch341.status == ch341_status.VTX_UPDATEDONE.value: # vtx update done 465 | self._statusbar_frame.progress_bar_set_value(100) 466 | self._statusbar_frame.status_label_set_text( 467 | "Firmware updated. Connect another VTX(the same type) to update, or click cancel to finish.", "#06b025") 468 | my_ch341.status = ch341_status.VTX_RECONNECT.value 469 | self._programmer_frame.update_button_set_text_cancel() 470 | self._programmer_frame.update_button_enable() 471 | 472 | """ 473 | my_ch341.status = ch341_status.IDLE.value 474 | 475 | self.notebook_enable() 476 | 477 | self._vtx_frame.radio_button_enable() 478 | 479 | self._programmer_frame.version_combobox_enable() 480 | self._programmer_frame.version_combobox_set_default() 481 | 482 | self._programmer_frame.online_fw_button_enable( 483 | self.network_error) 484 | self._programmer_frame.online_fw_button_set_str_default() 485 | self._programmer_frame.online_fw_button_show() 486 | 487 | self._programmer_frame.local_fw_button_enable() 488 | self._programmer_frame.local_fw_button_set_str_default() 489 | 490 | self._programmer_frame.update_button_set_text_update("VTX") 491 | # self._programmer_frame.update_button_disable() 492 | 493 | self._statusbar_frame.progress_bar_set_value(100) 494 | self._programmer_frame.deselect() 495 | """ 496 | elif my_ch341.status == ch341_status.VTX_UPDATE_FAILED.value: # vtx update failed 497 | self._statusbar_frame.progress_bar_set_value(0) 498 | self._statusbar_frame.status_label_set_text( 499 | "Firmware updated failed. Disconnect/reconnect the vtx to try again, or click cancel to finish.", "red") 500 | my_ch341.status = ch341_status.VTX_RECONNECT.value 501 | self._programmer_frame.update_button_set_text_cancel() 502 | self._programmer_frame.update_button_enable() 503 | 504 | elif my_ch341.status == ch341_status.VTX_RECONNECTDONE.value: 505 | my_ch341.status = ch341_status.VTX_UPDATE.value 506 | self._programmer_frame.update_button_set_text_update("VTX") 507 | self._programmer_frame.update_button_disable() 508 | self._statusbar_frame.label_hidden() 509 | 510 | elif my_ch341.status == ch341_status.VTX_FW_ERROR.value: # vtx fw error 511 | my_ch341.status = ch341_status.IDLE.value 512 | 513 | self.notebook_enable() 514 | 515 | self._vtx_frame.radio_button_enable() 516 | 517 | self._programmer_frame.version_combobox_enable() 518 | self._programmer_frame.online_fw_button_enable( 519 | self.network_error) 520 | self._programmer_frame.version_combobox_set_default() 521 | self._programmer_frame.local_fw_button_enable() 522 | self._programmer_frame.local_fw_button_set_str_default() 523 | self._programmer_frame.update_button_set_text_update("VTX") 524 | # self._programmer_frame.update_button_disable() 525 | self._programmer_frame.deselect() 526 | 527 | self._statusbar_frame.progress_bar_set_value(0) 528 | self._statusbar_frame.status_label_set_text( 529 | "Firmware update failed. Firmware error", "red") 530 | 531 | # ------------ Monitor --------------- 532 | if self.current_selected_tab() == 1: 533 | # download 534 | if my_download.status == download_status.DOWNLOAD_MONITOR_FW_DONE.value: 535 | my_download.status = download_status.IDLE.value 536 | my_ch341.fw_path = my_download.save_path 537 | my_ch341.written_len = 0 538 | my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) 539 | 540 | self._statusbar_frame.label_hidden() 541 | self._programmer_frame.update_button_set_text_update( 542 | "Monitor") 543 | self._programmer_frame.update_button_disable() 544 | my_ch341.status = ch341_status.MONITOR_UPDATE.value 545 | elif my_download.status == download_status.DOWNLOAD_MONITOR_FW_FAILED.value: 546 | my_download.status = download_status.IDLE.value 547 | self.is_update_monitor = 0 548 | my_ch341.monitor_connected = 0 549 | my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value 550 | 551 | self.notebook_enable() 552 | 553 | self._programmer_frame.version_combobox_enable() 554 | self._programmer_frame.online_fw_button_enable( 555 | self.network_error) 556 | self._programmer_frame.version_combobox_set_default() 557 | self._programmer_frame.local_fw_button_enable() 558 | self._programmer_frame.local_fw_button_set_str_default() 559 | self._programmer_frame.update_button_set_text_update( 560 | "Monitor") 561 | # self._programmer_frame.update_button_disable() 562 | self._programmer_frame.deselect() 563 | 564 | self._statusbar_frame.progress_bar_set_value(0) 565 | self._statusbar_frame.status_label_set_text( 566 | "Firmware update failed. Network error.", "red") 567 | 568 | # update 569 | if self.is_update_monitor == 1: 570 | if my_ch341.status == ch341_status.IDLE.value and my_ch341.monitor_connected == 0: # to connect Monitor 571 | my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value 572 | elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value and my_ch341.monitor_connected == 1: # Monitor is connected 573 | my_ch341.status = ch341_status.IDLE.value 574 | if self._programmer_frame.mode == 0: 575 | my_download.url = self._programmer_frame.url 576 | my_download.save_path = "resource/FW" 577 | my_download.status = download_status.DOWNLOAD_MONITOR_FW.value # download url 578 | self._statusbar_frame.status_label_set_text( 579 | "Downloading Firmware ...", "SystemButtonFace") 580 | else: 581 | my_ch341.written_len = 0 582 | my_ch341.to_write_len = os.path.getsize( 583 | my_ch341.fw_path) 584 | self._statusbar_frame.label_hidden() 585 | self._programmer_frame.update_button_set_text_update( 586 | "Monitor") 587 | self._programmer_frame.update_button_disable() 588 | my_ch341.status = ch341_status.MONITOR_UPDATE.value 589 | 590 | elif my_ch341.status == ch341_status.MONITOR_UPDATE.value: # refresh progress bar 591 | value = (my_ch341.written_len / 592 | my_ch341.to_write_len * 100) % 101 593 | self._statusbar_frame.progress_bar_set_value(value) 594 | 595 | elif my_ch341.status == ch341_status.MONITOR_UPDATEDONE.value: # Monitor update done 596 | self.is_update_monitor = 0 597 | my_ch341.monitor_connected = 0 598 | my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value 599 | 600 | self.notebook_enable() 601 | 602 | self._programmer_frame.update_button_set_text_update( 603 | "Monitor") 604 | self._programmer_frame.update_button_enable() 605 | self._programmer_frame.version_combobox_enable() 606 | self._programmer_frame.local_fw_button_enable() 607 | self._programmer_frame.online_fw_button_enable( 608 | self.network_error) 609 | 610 | self._statusbar_frame.progress_bar_set_value(100) 611 | self._statusbar_frame.status_label_set_text( 612 | "Firmware updated.", "#06b025") 613 | 614 | elif my_ch341.status == ch341_status.MONITOR_FW_ERROR.value: # fw error 615 | self.is_update_monitor = 0 616 | my_ch341.monitor_connected = 0 617 | my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value 618 | 619 | self.notebook_enable() 620 | 621 | self._programmer_frame.update_button_set_text_update( 622 | "Monitor") 623 | self._programmer_frame.update_button_enable() 624 | self._programmer_frame.version_combobox_enable() 625 | self._programmer_frame.local_fw_button_enable() 626 | self._programmer_frame.online_fw_button_enable( 627 | self.network_error) 628 | 629 | self._statusbar_frame.progress_bar_set_value(0) 630 | self._statusbar_frame.status_label_set_text( 631 | "Firmware udpate failed. Firmware error.", "red") 632 | 633 | elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value: 634 | if self.monitor_is_alive == 0 and my_ch341.monitor_connected == 1: # to connect monitor 635 | self._monitor_frame.setting_enable() 636 | 637 | self._programmer_frame.version_combobox_enable() 638 | self._programmer_frame.online_fw_button_enable( 639 | self.network_error) 640 | self._programmer_frame.local_fw_button_enable() 641 | self._programmer_frame.local_fw_button_set_str_default() 642 | # self._programmer_frame.update_button_disable() 643 | 644 | self._monitor_frame.write_setting(global_var.brightness, global_var.contrast, global_var.saturation, 645 | global_var.backlight, global_var.cell_count, global_var.warning_cell_voltage, global_var.osd) 646 | self.monitor_is_alive = 1 647 | elif self.monitor_is_alive == 1 and my_ch341.monitor_connected == 0: # to disconnect monitor 648 | self.monitor_is_alive = 0 649 | 650 | self._monitor_frame.reset_scale() 651 | self._monitor_frame.setting_disable() 652 | 653 | self._programmer_frame.version_combobox_enable() 654 | self._programmer_frame.online_fw_button_enable( 655 | self.network_error) 656 | self._programmer_frame.local_fw_button_enable() 657 | self._programmer_frame.local_fw_button_set_str_default() 658 | # self._programmer_frame.update_button_disable() 659 | 660 | elif self.monitor_is_alive == 1 and my_ch341.monitor_connected == 1: # monitor is alive 661 | # settting 662 | self._monitor_frame.usb_heart() 663 | 664 | # --------------------- event_vrx ------------------------------- 665 | if self.current_selected_tab() == 2: 666 | # download 667 | if my_download.status == download_status.DOWNLOAD_EVENT_VRX_FW_DONE.value: 668 | my_download.status = download_status.IDLE.value 669 | my_ch341.fw_path = my_download.save_path 670 | my_ch341.written_len = 0 671 | my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) 672 | 673 | self._statusbar_frame.label_hidden() 674 | self._programmer_frame.update_button_set_text_update( 675 | "Event VRX") 676 | self._programmer_frame.update_button_disable() 677 | my_ch341.status = ch341_status.EVENT_VRX_UPDATE.value 678 | elif my_download.status == download_status.DOWNLOAD_EVENT_VRX_FW_FAILED.value: 679 | my_download.status = download_status.IDLE.value 680 | my_ch341.status = ch341_status.IDLE.value 681 | 682 | self.notebook_enable() 683 | 684 | self._programmer_frame.version_combobox_enable() 685 | self._programmer_frame.online_fw_button_enable( 686 | self.network_error) 687 | self._programmer_frame.version_combobox_set_default() 688 | self._programmer_frame.local_fw_button_enable() 689 | self._programmer_frame.local_fw_button_set_str_default() 690 | self._programmer_frame.update_button_set_text_update( 691 | "Event VRX") 692 | # self._programmer_frame.update_button_disable() 693 | self._programmer_frame.deselect() 694 | 695 | self._statusbar_frame.progress_bar_set_value(0) 696 | self._statusbar_frame.status_label_set_text( 697 | "Firmware update failed. Network error", "red") 698 | 699 | # update 700 | if my_ch341.status == ch341_status.EVENT_VRX_CONNECTED.value: # event_vrx is connected 701 | my_ch341.status = ch341_status.IDLE.value 702 | if self._programmer_frame.mode == 0: 703 | my_download.url = self._programmer_frame.url 704 | my_download.save_path = "resource/FW" 705 | my_download.status = download_status.DOWNLOAD_EVENT_VRX_FW.value # download url 706 | self._statusbar_frame.status_label_set_text( 707 | "Downloading Firmware ...", "SystemButtonFace") 708 | else: 709 | my_ch341.written_len = 0 710 | my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) 711 | self._programmer_frame.update_button_set_text_update( 712 | "Event VRX") 713 | self._programmer_frame.update_button_disable() 714 | self._statusbar_frame.label_hidden() 715 | my_ch341.status = ch341_status.EVENT_VRX_UPDATE.value 716 | 717 | elif my_ch341.status == ch341_status.EVENT_VRX_UPDATE.value: # event_vrx refresh progress bar 718 | value = (my_ch341.written_len / 719 | my_ch341.to_write_len * 100) % 101 720 | self._statusbar_frame.progress_bar_set_value(value) 721 | 722 | elif my_ch341.status == ch341_status.EVENT_VRX_UPDATEDONE.value: # event_vrx update done 723 | my_ch341.status = ch341_status.IDLE.value 724 | 725 | self.notebook_enable() 726 | 727 | self._programmer_frame.update_button_set_text_update( 728 | "Event VRX") 729 | self._programmer_frame.update_button_enable() 730 | self._programmer_frame.version_combobox_enable() 731 | self._programmer_frame.local_fw_button_enable() 732 | self._programmer_frame.online_fw_button_enable( 733 | self.network_error) 734 | 735 | self._statusbar_frame.progress_bar_set_value(100) 736 | self._statusbar_frame.status_label_set_text( 737 | "Firmware updated.", "#06b025") 738 | 739 | elif my_ch341.status == ch341_status.EVENT_VRX_FW_ERROR.value: 740 | my_ch341.status = ch341_status.IDLE.value 741 | 742 | self.notebook_enable() 743 | 744 | self._programmer_frame.update_button_set_text_update( 745 | "Event VRX") 746 | self._programmer_frame.update_button_enable() 747 | self._programmer_frame.version_combobox_enable() 748 | self._programmer_frame.local_fw_button_enable() 749 | self._programmer_frame.online_fw_button_enable( 750 | self.network_error) 751 | 752 | self._statusbar_frame.progress_bar_set_value(0) 753 | self._statusbar_frame.status_label_set_text( 754 | "Fiwamre update failed. Firmware error", "red") 755 | 756 | self._main_window.after(100, self.refresh) 757 | 758 | 759 | def on_closing(): 760 | my_download.status = download_status.DOWNLOAD_EXIT.value 761 | my_ch341.status = ch341_status.STATUS_EXIT.value 762 | sys.exit() 763 | 764 | 765 | global my_gui 766 | 767 | 768 | def ui_thread_proc(): 769 | global my_gui 770 | root = tk.Tk() 771 | 772 | my_gui = MyGUI(root) 773 | my_gui.refresh() 774 | 775 | if my_gui.downloading_window_status == 0: 776 | my_gui._main_window.after( 777 | 100, my_gui.create_downloading_firmware_window) 778 | 779 | root.protocol("WM_DELETE_WINDOW", on_closing) 780 | 781 | my_gui._main_window.mainloop() 782 | --------------------------------------------------------------------------------