diff --git a/.gitignore b/.gitignore index 5940ad7..5e2eb88 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,9 @@ event_vrx_releases monitor_releases FW MONITOR.bin -.spec \ No newline at end of file +.spec +elrs_backpack +elrs_tx +hdzero_radio_stm32.bin +local_path +radio_releases \ No newline at end of file diff --git a/Download.py b/Download.py index 8290caf..f5bbc87 100644 --- a/Download.py +++ b/Download.py @@ -13,6 +13,7 @@ def __init__(self): self.url = "" self.save_path = "" self.to_stop = 0 + self.pre_download = 0 def download_file(self, url, save_path, clear): print(f"Downloading {url}") @@ -45,18 +46,9 @@ def download_file(self, url, save_path, clear): def download_thread_proc(): - my_download.download_file( - "https://api.github.com/repos/hd-zero/hdzero-vtx/releases", "resource/vtx_releases", 0) - my_download.download_file( - "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/src/common.h", "resource/vtx_common", 0) - my_download.download_file( - "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/vtx_targets.png", "resource/vtx_targets.png", 0) - my_download.download_file( - "https://api.github.com/repos/ligenxxxx/event-vrx/releases", "resource/event_vrx_releases", 1) - my_download.download_file( - "https://api.github.com/repos/ligenxxxx/hv/releases", "resource/monitor_releases", 1) - - time.sleep(1) + while my_download.pre_download != 63: + time.sleep(0.1) + my_download.status = download_status.FILE_PARSE.value while True: @@ -99,7 +91,47 @@ def download_thread_proc(): else: my_download.status = download_status.DOWNLOAD_EVENT_VRX_FW_FAILED.value + elif my_download.status == download_status.DOWNLOAD_RADIO_FW.value: + ret = my_download.download_file( + my_download.url, my_download.save_path, 1) + if ret == 1: + my_download.status = download_status.DOWNLOAD_RADIO_FW_DONE.value + elif ret == 2: # stop + my_download.status = download_status.IDLE.value + else: + my_download.status = download_status.DOWNLOAD_RADIO_FW_FAILED.value + elif my_download.status == download_status.DOWNLOAD_EXIT.value: sys.exit() time.sleep(0.01) + +def download_vtx_releases(): + my_download.download_file( + "https://api.github.com/repos/hd-zero/hdzero-vtx/releases", "resource/vtx_releases", 0) + my_download.pre_download += 1 + +def download_vtx_common(): + my_download.download_file( + "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/src/common.h", "resource/vtx_common", 0) + my_download.pre_download += 2 + +def download_vtx_targets_image(): + my_download.download_file( + "https://raw.githubusercontent.com/hd-zero/hdzero-vtx/main/vtx_targets.png", "resource/vtx_targets.png", 0) + my_download.pre_download += 4 + +def download_event_vrx_releases(): + my_download.download_file( + "https://api.github.com/repos/hd-zero/event-vrx/releases", "resource/event_vrx_releases", 1) + my_download.pre_download += 8 + +def download_monitor_releases(): + my_download.download_file( + "https://api.github.com/repos/hd-zero/monitor/releases", "resource/monitor_releases", 1) + my_download.pre_download += 16 + +def download_radio_releases(): + my_download.download_file( + "https://api.github.com/repos/ligenxxxx/radio/releases", "resource/radio_releases", 1) + my_download.pre_download += 32 \ No newline at end of file diff --git a/README.md b/README.md index ea6ed44..ca1eaea 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ -## python version +## Python Version windows 3.10.5 32-bit +## How To Run + +### Create venv + +### Install package + +``` +.venv/Scripts/python.exe -m pip install Pillow pyserial esptool xmodem requests pyinstaller +``` + +### Run hdzero_programmer.py + ## generate exe ``` diff --git a/ch341.py b/ch341.py index f2d6e7d..ec7b8c4 100644 --- a/ch341.py +++ b/ch341.py @@ -4,12 +4,14 @@ import os from ctypes import create_string_buffer from frame_monitor import frame_monitor +from program_radio import my_radio import tkinter as tk from tkinter import ttk from global_var import * from ctypes import * import global_var import subprocess +import zipfile class ch341_class(object): @@ -78,6 +80,9 @@ def __init__(self): self.buffer_size = 2560 self.write_buffer = create_string_buffer(self.buffer_size) + self.fw_index = 0 + self.update_error_flag = 0 + try: self.dll = ctypes.WinDLL(self.dll_name) except: @@ -125,6 +130,13 @@ def parse_event_vrx_fw(self, fw_path): return 0 except: return 0 + def parse_radio_fw(self, fw_path): + try: + with zipfile.ZipFile(fw_path, "r") as z: + z.extractall("resource/") + return 1 + except: + return 0 def ch341read_i2c(self, addr): self.dll.CH341ReadI2C(0, self.addr_fpga_device, addr, self.iobuffer) @@ -609,5 +621,39 @@ def ch341_thread_proc(): else: my_ch341.write_event_vrx_fw_to_flash(my_ch341.fw_path) my_ch341.status = ch341_status.EVENT_VRX_UPDATEDONE.value + + # ---------------------- Radio ------------------------------------ + elif my_ch341.status == ch341_status.RADIO_DISCONNECTED.value: # connect radio + if my_radio.radio_is_active() != 0: + my_ch341.status = ch341_status.RADIO_CONNECTED.value + elif my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_TX.value: # update elrs tx + if my_ch341.parse_radio_fw(my_ch341.fw_path) == 0: + my_ch341.status = ch341_status.RADIO_FW_ERROR.value + + my_ch341.fw_index = 1 + my_ch341.update_error_flag = 0 + + if my_radio.program_elrs_tx() == False: + my_ch341.update_error_flag = 1 + my_ch341.status = ch341_status.RADIO_UPDATE_ELRS_BACKPACK.value + + elif my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_BACKPACK.value: # update elrs backpack + my_ch341.written_len = 400 + my_ch341.fw_index = 2 + + if my_radio.program_elrs_backpack() == False: + my_ch341.update_error_flag += 2 + my_ch341.status = ch341_status.RADIO_UPDATE_STM32.value + + elif my_ch341.status == ch341_status.RADIO_UPDATE_STM32.value: # update elrs backpack + my_ch341.written_len = 800 + my_ch341.fw_index = 3 + if my_radio.program_stm32() == False: + my_ch341.status = ch341_status.RADIO_UPDATE_STM32_FAILED.value + elif my_ch341.update_error_flag: + my_ch341.status = ch341_status.RADIO_UPDATE_ELRS_FAILED.value + else: + my_ch341.status = ch341_status.RADIO_UPDATE_DONE.value + else: time.sleep(0.1) diff --git a/frame_radio.py b/frame_radio.py new file mode 100644 index 0000000..878f1ed --- /dev/null +++ b/frame_radio.py @@ -0,0 +1,22 @@ +import tkinter as tk +from tkinter import ttk +from PIL import Image, ImageTk + + +class frame_radio: + def __init__(self, parent): + self._parent = parent + self._frame = tk.Frame(parent) + parent.add(self._frame, text="Radio") + self.image_path = "resource/radio.png" + self.show_image() + + def frame(self): + return self._frame + + def show_image(self): + image = Image.open(self.image_path) + photo = ImageTk.PhotoImage(image) + label = ttk.Label(self._frame, image=photo) + label.image = photo + label.pack() diff --git a/global_var.py b/global_var.py index a971604..7223fd0 100644 --- a/global_var.py +++ b/global_var.py @@ -38,6 +38,17 @@ class ch341_status(Enum): EVENT_VRX_UPDATEDONE = 25 EVENT_VRX_FW_ERROR = 26 + RADIO_DISCONNECTED = 31 + RADIO_CONNECTED = 32 + RADIO_GET_FW = 33 + RADIO_UPDATE_ELRS_TX = 34 + RADIO_UPDATE_ELRS_BACKPACK = 35 + RADIO_UPDATE_STM32 = 36 + RADIO_UPDATE_DONE = 37 + RADIO_FW_ERROR = 38 + RADIO_UPDATE_STM32_FAILED = 39 + RADIO_UPDATE_ELRS_FAILED = 40 + STATUS_EXIT = 255 @@ -55,4 +66,10 @@ class download_status(Enum): DOWNLOAD_EVENT_VRX_FW = 20 DOWNLOAD_EVENT_VRX_FW_DONE = 21 DOWNLOAD_EVENT_VRX_FW_FAILED = 22 + + DOWNLOAD_RADIO_FW = 30 + DOWNLOAD_RADIO_FW_DONE = 31 + DOWNLOAD_RADIO_FW_FAILED = 32 + + DOWNLOAD_EXIT = 255 diff --git a/hdzero_programmer.py b/hdzero_programmer.py index 63038e5..15c8712 100644 --- a/hdzero_programmer.py +++ b/hdzero_programmer.py @@ -2,8 +2,15 @@ import os import sys import shutil +import time from main_window import ui_thread_proc from download import download_thread_proc +from download import download_vtx_releases +from download import download_vtx_common +from download import download_vtx_targets_image +from download import download_event_vrx_releases +from download import download_monitor_releases +from download import download_radio_releases from ch341 import ch341_thread_proc @@ -37,9 +44,6 @@ def check_and_release_resource(): def main(): check_and_release_resource() - download_thread = threading.Thread( - target=download_thread_proc, name="download") - download_thread.start() ui_thread = threading.Thread(target=ui_thread_proc, name="ui") ui_thread.start() @@ -47,6 +51,34 @@ def main(): ch341_thread = threading.Thread(target=ch341_thread_proc, name="ch341") ch341_thread.start() + time.sleep(1) + + download_0 = threading.Thread( + target=download_vtx_releases, name="download_vtx_releases") + download_0.start() + download_1 = threading.Thread( + target=download_vtx_common, name="download_vtx_common") + download_1.start() + + download_2 = threading.Thread( + target=download_vtx_targets_image, name="download_vtx_targets_image") + download_2.start() + + download_3 = threading.Thread( + target=download_event_vrx_releases, name="download_event_vrx_releases") + download_3.start() + + download_4 = threading.Thread( + target=download_monitor_releases, name="download_monitor_releases") + download_4.start() + + download_5 = threading.Thread( + target=download_radio_releases, name="download_radio_releases") + download_5.start() + + download_thread = threading.Thread( + target=download_thread_proc, name="download") + download_thread.start() if __name__ == '__main__': main() diff --git a/main_window.py b/main_window.py index 0490441..17bbad0 100644 --- a/main_window.py +++ b/main_window.py @@ -7,8 +7,10 @@ from frame_vtx import frame_vtx from frame_monitor import frame_monitor from frame_event_vrx import frame_event_vrx +from frame_radio import frame_radio from frame_programmer import frame_programmer from frame_statusbar import frame_statusbar +from program_radio import my_radio from download import * from parse_file import * @@ -18,6 +20,7 @@ import base64 from icon32 import icon32 import io +import program_radio class MyGUI: @@ -70,6 +73,7 @@ def init_tab(self): self.init_vtx_frame() self.init_monitor_frame() self.init_event_vrx_frame() + self.init_radio_frame() self._tabCtrl.select(self._vtx_frame.frame()) self._tabCtrl.grid(row=0, column=0, sticky="nsew") self._tabCtrl.bind("<>", self.on_tab_changed) @@ -101,6 +105,9 @@ def init_monitor_frame(self): def init_event_vrx_frame(self): self._event_vrx_frame = frame_event_vrx(self._tabCtrl) + def init_radio_frame(self): + self._radio_frame = frame_radio(self._tabCtrl) + def init_statusbar(self): self._statusbar_frame = frame_statusbar(self._main_window) @@ -145,6 +152,12 @@ def on_select_version(self, event): )] self._programmer_frame.online_fw_button_set_str( self._programmer_frame.version_combobox.get()) + elif self.current_selected_tab() == 3: + self._programmer_frame.update_button_enable() + self._programmer_frame.url = my_parse.radio_info[self._programmer_frame.version_combobox.get( + )] + self._programmer_frame.online_fw_button_set_str( + self._programmer_frame.version_combobox.get()) def on_load_local_firmware(self): self._programmer_frame.online_fw_button_show() @@ -168,8 +181,13 @@ def on_load_local_firmware(self): self._programmer_frame.mode = 1 my_ch341.fw_path = self._programmer_frame.local_file_path + elif self.current_selected_tab() == 3: + self._programmer_frame.update_button_enable() + self._programmer_frame.mode = 1 + my_ch341.fw_path = self._programmer_frame.local_file_path + def on_update(self): - if self.current_selected_tab() == 0: + if self.current_selected_tab() == 0: # vtx if self._programmer_frame.is_cancel == 0: my_ch341.status = ch341_status.VTX_DISCONNECTED.value # to connect vtx @@ -207,7 +225,7 @@ def on_update(self): self._statusbar_frame.label_hidden() self._statusbar_frame.progress_bar_set_value(0) - elif self.current_selected_tab() == 1: + elif self.current_selected_tab() == 1: # monitor if self._programmer_frame.is_cancel == 0: self.is_update_monitor = 1 my_ch341.monitor_connected = 0 @@ -250,7 +268,7 @@ def on_update(self): self._statusbar_frame.label_hidden() self._statusbar_frame.progress_bar_set_value(0) - elif self.current_selected_tab() == 2: + elif self.current_selected_tab() == 2: # event vrx if self._programmer_frame.is_cancel == 0: my_ch341.status = ch341_status.EVENT_VRX_DISCONNECTED.value my_download.to_stop = 0 @@ -273,7 +291,37 @@ def on_update(self): self._programmer_frame.update_button_set_text_update( "Event VRX") - self._programmer_frame.update_button_disable() + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_enable() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + + self._statusbar_frame.label_hidden() + self._statusbar_frame.progress_bar_set_value(0) + + elif self.current_selected_tab() == 3: # radio + if self._programmer_frame.is_cancel == 0: + my_ch341.status = ch341_status.RADIO_DISCONNECTED.value + my_download.to_stop = 0 + + self.notebook_disable() + + self._programmer_frame.update_button_set_text_cancel() + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_disable() + self._programmer_frame.local_fw_button_disable() + self._programmer_frame.online_fw_button_disable() + self._statusbar_frame.status_label_set_text( + "Connecting Radio ...", "SystemButtonFace") + self._statusbar_frame.progress_bar_set_value(0) + else: + my_download.to_stop = 1 + + self.notebook_enable() + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_enable() self._programmer_frame.version_combobox_enable() self._programmer_frame.local_fw_button_enable() self._programmer_frame.online_fw_button_enable( @@ -327,6 +375,15 @@ def on_tab_changed(self, event): self._programmer_frame.online_fw_button_show() my_ch341.status = ch341_status.IDLE.value + elif self.current_selected_tab() == 3: + version_list = list(my_parse.radio_info.keys()) + self._programmer_frame.version_combobox_update_values(version_list) + self._programmer_frame.version_combobox_enable() + self._programmer_frame.version_combobox_set_default() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.update_button_set_text_update("Radio") + self._programmer_frame.update_button_disable() + self._programmer_frame.online_fw_button_show() self._programmer_frame.deselect() self._programmer_frame.online_fw_button_set_str_default() @@ -388,13 +445,14 @@ def refresh(self): if my_download.status == download_status.FILE_PARSE.value: my_download.status = download_status.IDLE.value my_parse.parse_vtx_common() - ret0 = my_parse.parse_vtx_releases() - ret1 = my_parse.parse_event_vrx_releases() - ret2 = my_parse.parse_monitor_releases() - ret3 = my_parse.parse_vtx_tragets_image( + ret0 = my_parse.parse_vtx_tragets_image( len(list(my_parse.vtx_info.keys()))) + ret1 = my_parse.parse_vtx_releases() + ret2 = my_parse.parse_monitor_releases() + ret3 = my_parse.parse_event_vrx_releases() + ret4 = my_parse.parse_radio_releases() - if ret0 == 0 or ret1 == 0 or ret2 == 0 or ret3 == 0: + if ret0 == 0 or ret1 == 0 or ret2 == 0 or ret3 == 0 or ret4 == 0: self.network_error = 1 self.set_downloading_label("Download firmware list failed") self._main_window.update() @@ -751,7 +809,145 @@ def refresh(self): self._statusbar_frame.progress_bar_set_value(0) self._statusbar_frame.status_label_set_text( - "Fiwamre update failed. Firmware error", "red") + "Firmware update failed. Firmware error", "red") + + # --------------------- radio ------------------------------- + if self.current_selected_tab() == 3: + # download + if my_download.status == download_status.DOWNLOAD_RADIO_FW_DONE.value: + my_download.status = download_status.IDLE.value + my_ch341.fw_path = my_download.save_path + my_ch341.written_len = 0 + my_ch341.to_write_len = 1000 + + self._statusbar_frame.label_hidden() + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_disable() + my_ch341.status = ch341_status.RADIO_UPDATE_ELRS_TX.value + elif my_download.status == download_status.DOWNLOAD_RADIO_FW_FAILED.value: + my_download.status = download_status.IDLE.value + my_ch341.status = ch341_status.IDLE.value + + self.notebook_enable() + + self._programmer_frame.version_combobox_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + self._programmer_frame.version_combobox_set_default() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.local_fw_button_set_str_default() + self._programmer_frame.update_button_set_text_update( + "Radio") + # self._programmer_frame.update_button_disable() + self._programmer_frame.deselect() + + self._statusbar_frame.progress_bar_set_value(0) + self._statusbar_frame.status_label_set_text( + "Firmware update failed. Network error", "red") + + # update + if my_ch341.status == ch341_status.RADIO_CONNECTED.value: # radio is connected + my_ch341.status = ch341_status.IDLE.value + if self._programmer_frame.mode == 0: + my_download.url = self._programmer_frame.url + my_download.save_path = "resource/FW" + my_download.status = download_status.DOWNLOAD_RADIO_FW.value # download url + self._statusbar_frame.status_label_set_text( + "Downloading Firmware ...", "SystemButtonFace") + else: + my_ch341.written_len = 0 + my_ch341.to_write_len = 1000 + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_disable() + self._statusbar_frame.label_hidden() + my_ch341.status = ch341_status.RADIO_UPDATE_ELRS_TX.value + + elif my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_TX.value or my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_BACKPACK.value: # radio refresh progress bar + if my_ch341.written_len < my_ch341.fw_index * 400 and my_ch341.written_len < my_ch341.to_write_len: + my_ch341.written_len += 1 + value = (my_ch341.written_len / + my_ch341.to_write_len * 100) % 101 + self._statusbar_frame.progress_bar_set_value(value) + + elif my_ch341.status == ch341_status.RADIO_UPDATE_STM32.value: + my_ch341.written_len += 2 + if my_ch341.written_len > my_ch341.to_write_len: + my_ch341.written_len = my_ch341.to_write_len + + value = (my_ch341.written_len / + my_ch341.to_write_len * 100) % 101 + self._statusbar_frame.progress_bar_set_value(value) + + elif my_ch341.status == ch341_status.RADIO_FW_ERROR.value: + my_ch341.status = ch341_status.IDLE.value + + self.notebook_enable() + + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_enable() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + + self._statusbar_frame.progress_bar_set_value(0) + self._statusbar_frame.status_label_set_text( + "Firmware update failed, Firmware error.", "red") + + elif my_ch341.status == ch341_status.RADIO_UPDATE_STM32_FAILED.value: + my_ch341.status = ch341_status.IDLE.value + + self.notebook_enable() + + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_enable() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + + self._statusbar_frame.progress_bar_set_value(0) + self._statusbar_frame.status_label_set_text( + "Update firmware failed, repower and try again.", "red") + + elif my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_FAILED.value: + my_ch341.status = ch341_status.IDLE.value + + self.notebook_enable() + + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_enable() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + + self._statusbar_frame.progress_bar_set_value(0) + self._statusbar_frame.status_label_set_text( + "Update ELRS failed, repower and try again.", "red") + + elif my_ch341.status == ch341_status.RADIO_UPDATE_DONE.value: # radio update done + my_ch341.status = ch341_status.IDLE.value + + self.notebook_enable() + + self._programmer_frame.update_button_set_text_update( + "Radio") + self._programmer_frame.update_button_enable() + self._programmer_frame.version_combobox_enable() + self._programmer_frame.local_fw_button_enable() + self._programmer_frame.online_fw_button_enable( + self.network_error) + + self._statusbar_frame.progress_bar_set_value(100) + self._statusbar_frame.status_label_set_text( + "Firmware updated, repower Radio now", "#06b025") + self._main_window.after(100, self.refresh) diff --git a/package.bat b/package.bat index 045a614..4cca606 100644 --- a/package.bat +++ b/package.bat @@ -1 +1 @@ -pyinstaller -i HDZeroIcon.ico -w --onefile --add-data "resource;data_folder" hdzero_programmer.py + .\.venv\Scripts\pyinstaller.exe -i HDZeroIcon.ico -w --onefile --collect-all esptool --add-data "resource;data_folder" hdzero_programmer.py --hidden-import esptool --hidden-import esptool \ No newline at end of file diff --git a/parse_file.py b/parse_file.py index 1f9bab7..172a92e 100644 --- a/parse_file.py +++ b/parse_file.py @@ -8,11 +8,13 @@ def __init__(self): self.vtx_common_path = "resource/vtx_common" self.event_vrx_releases_path = "resource/event_vrx_releases" self.monitor_releases_path = "resource/monitor_releases" + self.radio_releases_path = "resource/radio_releases" self.vtx_tragets_image_path = "resource/vtx_targets.png" self.vtx_info = {} - self.event_vrx_info = {} self.monitor_info = {} + self.event_vrx_info = {} + self.radio_info = {} self.vtx_target_image = [] def parse_vtx_common(self): @@ -116,5 +118,23 @@ def parse_vtx_tragets_image(self, num): return 1 + def parse_radio_releases(self): + try: + with open(self.radio_releases_path) as f: + data = json.load(f) + + for i in range(len(data)): + link_list = [] + for j in range(len(data[i]['assets'])): + link_list.append(data[i]['assets'][j] + ['browser_download_url']) + + version = data[i]['tag_name'] + url = data[i]['assets'][j]['browser_download_url'] + self.radio_info[version] = url + return 1 + except: + return 0 + my_parse = parse() diff --git a/program_radio.py b/program_radio.py new file mode 100644 index 0000000..b6713b4 --- /dev/null +++ b/program_radio.py @@ -0,0 +1,217 @@ +import serial.tools.list_ports +import time +import esptool +import sys +from xmodem import XMODEM + + +def flash_esp_api( + port, + baud, + chip, + flash_args, +): + """ + flash_args: + [ + (0x1000, "bootloader.bin"), + (0x8000, "partition-table.bin"), + (0x10000, "app.bin"), + ] + """ + + # 等价于命令行参数 + argv = [ + "--chip", chip, + "--port", port, + "--baud", str(baud), + "write-flash", + ] + + for addr, path in flash_args: + argv.append(hex(addr)) + argv.append(path) + + # esptool 内部就是解析 sys.argv + old_argv = sys.argv + try: + sys.argv = ["esptool.py"] + argv + esptool.main() + except: + return False + finally: + sys.argv = old_argv + + return True + + +class radio_class(object): + def __init__(self): + self.ser = '' + self.status = 0 + self.com = '' + + def find_word_in_string(self, s, word): + try: + words = s.split() + except: + return 0 + + for w in words: + if w == word: + return 1 + return 0 + + def serial_tx(self, str): + self.ser.write(str.encode('utf-8')) + + def serial_rx(self, sleep_sec): + data_received = b'' + time.sleep(sleep_sec) + while self.ser.in_waiting: + data_received = self.ser.read(self.ser.in_waiting) + return data_received.decode('utf-8') + + def getc(self, size, timeout=1): + return self.ser.read(size) + + def putc(self, data, timeout=1): + return self.ser.write(data) + + def search_stm32_port(self): + ports = list(serial.tools.list_ports.comports()) + for i in range(0, len(ports)): + if (self.find_word_in_string(ports[i].description, 'STMicroelectronics')): + self.com = ports[i].name + time.sleep(1) + try: + self.ser = serial.Serial(self.com, 115200, timeout=2) + return 1 + except: + return 0 + return 0 + + def search_elrs_port(self): + ports = list(serial.tools.list_ports.comports()) + for i in range(0, len(ports)): + if (self.find_word_in_string(ports[i].description, 'CH340')): + self.com = ports[i].name + time.sleep(1) + self.ser = serial.Serial(self.com, 115200, timeout=2) + return 1 + return 0 + + def unzip_radio_firmware(self, file_path, dest_folder): + import zipfile + with zipfile.ZipFile(file_path, 'r') as zip_ref: + zip_ref.extractall(dest_folder) + + def radio_is_active(self): + if self.search_stm32_port() == 0: + return 0 + + cmd = "ATPING\r\n" + self.ser.write(cmd.encode('utf-8')) + rx = self.serial_rx(1) + + self.ser.close() + if self.find_word_in_string(rx, "HDZero"): + return 1 + else: + return 2 + + def program_stm32(self): + + # enter bootloader mode + if self.search_stm32_port() == 0: + return False + cmd = "ATPROG\r\n" + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + time.sleep(0.5) + self.ser.close() + time.sleep(0.5) + + # start programming + if self.search_stm32_port() == 0: + return False + time.sleep(0.2) + cmd = "XXX" + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + time.sleep(0.5) + self.serial_rx(0) + + try: + stream = open("resource/hdzero_radio_stm32.bin", "rb") + modem = XMODEM(self.getc, self.putc) + modem.send(stream) + except: + return False + + cmd = "ccc" + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode('utf-8')) + time.sleep(0.5) + self.ser.close() + return True + + + + def program_elrs_tx(self): + if self.search_stm32_port() == 0: + return False + + cmd = "ATPGTX\r\n" + self.ser.write(cmd.encode('utf-8')) + time.sleep(0.2) + self.ser.close() + + time.sleep(1) + if self.search_elrs_port() == 0: + return False + self.ser.close() + + return flash_esp_api( + port=self.ser.port, + baud=460800, + chip="esp32", + flash_args=[ + (0x1000, "resource/elrs_tx/bootloader.bin"), + (0x8000, "resource/elrs_tx/partitions.bin"), + (0xe000, "resource/elrs_tx/boot_app0.bin"), + (0x10000, "resource/elrs_tx/firmware.bin"), + ], + ) + + def program_elrs_backpack(self): + if self.search_stm32_port() == 0: + return False + + cmd = "ATPGBP\r\n" + self.ser.write(cmd.encode('utf-8')) + time.sleep(0.2) + self.ser.close() + + time.sleep(1) + if self.search_elrs_port() == 0: + return False + self.ser.close() + + return flash_esp_api( + port=self.ser.port, + baud=460800, + chip="esp32c3", + flash_args=[ + (0x0000, "resource/elrs_backpack/bootloader.bin"), + (0x8000, "resource/elrs_backpack/partitions.bin"), + (0xe000, "resource/elrs_backpack/boot_app0.bin"), + (0x10000, "resource/elrs_backpack/firmware.bin"), + ], + ) + + +my_radio = radio_class() diff --git a/resource/driver/CH340SER.EXE b/resource/driver/CH340SER.EXE new file mode 100644 index 0000000..d07cfad Binary files /dev/null and b/resource/driver/CH340SER.EXE differ diff --git a/resource/radio.png b/resource/radio.png new file mode 100644 index 0000000..3263fb7 Binary files /dev/null and b/resource/radio.png differ diff --git a/resource/vtx_targets.png b/resource/vtx_targets.png index 91b2560..23f6f59 100644 Binary files a/resource/vtx_targets.png and b/resource/vtx_targets.png differ