diff --git a/.gitignore b/.gitignore index 5e2eb88..f820198 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ elrs_backpack elrs_tx hdzero_radio_stm32.bin local_path -radio_releases \ No newline at end of file +radio_releases +logs \ No newline at end of file diff --git a/ch341.py b/ch341.py index ec7b8c4..8bc5cb8 100644 --- a/ch341.py +++ b/ch341.py @@ -12,14 +12,17 @@ import global_var import subprocess import zipfile +import logging + +logger = logging.getLogger(__name__) class ch341_class(object): def __init__(self): FW_5680SIZE = 65536 - FW_FPGASIZE = 2*1024*1024 - FW_8339SIZE = 10*1024*1024 + FW_FPGASIZE = 2 * 1024 * 1024 + FW_8339SIZE = 10 * 1024 * 1024 self.fw_5680_size = 0 self.fw_5680_buf = create_string_buffer(FW_5680SIZE) self.fw_fpga_size = 0 @@ -29,9 +32,22 @@ def __init__(self): self.dll = None self.target = -1 - self.status = ch341_status.IDLE.value # idle + self.status = ch341_status.IDLE.value # idle self.read_setting_flag = 1 - self.dll_name = "CH341DLL.DLL" + + is_64bit = sys.maxsize > 2**32 + dll_name = "CH341DLLA64.DLL" if is_64bit else "CH341DLL.DLL" + + if getattr(sys, "frozen", False): + base_dir = sys._MEIPASS + else: + base_dir = os.path.dirname(os.path.abspath(__file__)) + + driver_path = os.path.join(base_dir, "resource", "driver", dll_name) + if os.path.exists(driver_path): + self.dll_name = driver_path + else: + self.dll_name = dll_name self.reconnect_vtx = 0 @@ -73,8 +89,8 @@ def __init__(self): self.CHIP_ERASE = 0xC7 # Erase one chip flash self.SELECT_FPGA_5680 = 0x80 self.FLASH_STATUS = 0x05 # read flash status - self.FLASH_SET_5680 = 0xffff40ff - self.FLASH_SET_FPGA = 0xffff80ff + self.FLASH_SET_5680 = 0xFFFF40FF + self.FLASH_SET_FPGA = 0xFFFF80FF self.FLASH_BASE_ADDR = 0x00 self.buffer_size = 2560 @@ -85,75 +101,113 @@ def __init__(self): try: self.dll = ctypes.WinDLL(self.dll_name) - except: - command = "resource\driver\SETUP.EXE \S" - print("Need to install ch341 driver") - print("Installing ...") - subprocess.run(command, shell=True, capture_output=True, text=True) - print("done") + logger.info(f"Loaded {self.dll_name} successfully") + except Exception as e: + logger.error(f"Failed to load {self.dll_name}: {e}") + command = r"resource\driver\SETUP.EXE \S" + logger.info("Need to install ch341 driver") + logger.info("Installing ...") + try: + subprocess.run(command, shell=True, capture_output=True, text=True) + logger.info("Driver installation command finished.") + except Exception as ex: + logger.error(f"Error running driver installer: {ex}") time.sleep(1) try: self.dll = ctypes.WinDLL(self.dll_name) - except: - a = 1 + logger.info(f"Loaded {self.dll_name} successfully after driver install") + except Exception as e: + logger.error( + f"Failed to load {self.dll_name} even after driver install attempt: {e}" + ) def parse_monitor_fw(self, fw_path): + logger.info(f"Parsing monitor firmware: {fw_path}") try: with open(fw_path, "rb") as file: file.seek(2) - self.fw_5680_size = int.from_bytes( - file.read(4), byteorder='little') - self.fw_fpga_size = int.from_bytes( - file.read(4), byteorder='little') - self.fw_8339_size = int.from_bytes( - file.read(4), byteorder='little') - if self.fw_5680_size < 65536 and self.fw_fpga_size < 10000000 and self.fw_8339_size < 10000000: + self.fw_5680_size = int.from_bytes(file.read(4), byteorder="little") + self.fw_fpga_size = int.from_bytes(file.read(4), byteorder="little") + self.fw_8339_size = int.from_bytes(file.read(4), byteorder="little") + + logger.debug( + f"Sizes parsed - 5680: {self.fw_5680_size}, FPGA: {self.fw_fpga_size}, 8339: {self.fw_8339_size}" + ) + + if ( + self.fw_5680_size < 65536 + and self.fw_fpga_size < 10000000 + and self.fw_8339_size < 10000000 + ): self.fw_5680_buf = file.read(self.fw_5680_size) self.fw_fpga_buf = file.read(self.fw_fpga_size) self.fw_8339_buf = file.read(self.fw_8339_size) + logger.info("Monitor firmware parsed successfully.") return 1 else: + logger.warning("Firmware sizes out of expected bounds.") return 0 - except: + except Exception as e: + logger.error(f"Error parsing monitor firmware: {e}") return 0 def parse_event_vrx_fw(self, fw_path): + logger.info(f"Parsing event VRX firmware: {fw_path}") try: with open(fw_path, "rb") as file: file_size = os.path.getsize(fw_path) head_size = file.read(8) self.fw_5680_size = int(head_size) - 2560 self.fw_fpga_size = file_size - 8 - self.fw_5680_size + logger.debug( + f"Event VRX sizes - 5680: {self.fw_5680_size}, FPGA: {self.fw_fpga_size}" + ) + if self.fw_5680_size < 65536 and self.fw_fpga_size < 10000000: + logger.info("Event VRX firmware parsed successfully") return 1 else: + logger.warning("Event VRX firmware sizes out of bounds") return 0 - except: + except Exception as e: + logger.error(f"Error parsing event VRX firmware: {e}") return 0 + def parse_radio_fw(self, fw_path): + logger.info(f"Parsing radio firmware: {fw_path}") try: with zipfile.ZipFile(fw_path, "r") as z: z.extractall("resource/") + logger.info("Radio firmware unzipped successfully") return 1 - except: + except Exception as e: + logger.error(f"Error parsing/unzipping radio firmware: {e}") return 0 def ch341read_i2c(self, addr): - self.dll.CH341ReadI2C(0, self.addr_fpga_device, addr, self.iobuffer) - return int.from_bytes(self.iobuffer[0], byteorder='big') + try: + self.dll.CH341ReadI2C(0, self.addr_fpga_device, addr, self.iobuffer) + return int.from_bytes(self.iobuffer[0], byteorder="big") + except Exception as e: + logger.error(f"Error reading I2C at address {addr}: {e}") + return 0 def read_setting(self): + logger.info("Reading device settings...") global_var.brightness = self.ch341read_i2c(self.addr_brightness) global_var.contrast = self.ch341read_i2c(self.addr_contrast) global_var.saturation = self.ch341read_i2c(self.addr_saturation) global_var.backlight = self.ch341read_i2c(self.addr_backlight) global_var.cell_count = self.ch341read_i2c(self.addr_cell_count) global_var.warning_cell_voltage = self.ch341read_i2c( - self.addr_warning_cell_voltage) + self.addr_warning_cell_voltage + ) global_var.osd = self.ch341read_i2c(self.addr_osd) - - fpga_version = self.ch341read_i2c(0xff) - print(f"cell:{global_var.cell_count:d} warning_cell:{global_var.warning_cell_voltage:d} fpga_version:0x{fpga_version:2x}") + + fpga_version = self.ch341read_i2c(0xFF) + logger.info( + f"cell:{global_var.cell_count:d} warning_cell:{global_var.warning_cell_voltage:d} fpga_version:0x{fpga_version:2x}" + ) def set_stream(self, cs): if cs == True: @@ -171,27 +225,29 @@ def flash_switch1(self): self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0x8300) def flash_switch2(self): - self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xc800) + self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xC800) def flash_release(self): - self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xc200) + self.dll.CH341SetOutput(0, 0x03, 0x0000FF00, 0xC200) def flash_read_id(self): - self.iobuffer[0] = 0x9f - self.iobuffer[1] = 0x9f - self.iobuffer[2] = 0x9f - self.iobuffer[3] = 0x9f - self.iobuffer[4] = 0x9f - self.iobuffer[5] = 0x9f + self.iobuffer[0] = 0x9F + self.iobuffer[1] = 0x9F + self.iobuffer[2] = 0x9F + self.iobuffer[3] = 0x9F + self.iobuffer[4] = 0x9F + self.iobuffer[5] = 0x9F self.ilength = 6 self.set_stream(0) self.stream_spi4() self.set_stream(1) - return int.from_bytes(self.iobuffer[1], byteorder='big') * 256 * 256 \ - + int.from_bytes(self.iobuffer[2], byteorder='big') * 256 \ - + int.from_bytes(self.iobuffer[3], byteorder='big') + return ( + int.from_bytes(self.iobuffer[1], byteorder="big") * 256 * 256 + + int.from_bytes(self.iobuffer[2], byteorder="big") * 256 + + int.from_bytes(self.iobuffer[3], byteorder="big") + ) def flash_write_enable(self): self.iobuffer[0] = 0x06 @@ -202,7 +258,7 @@ def flash_write_enable(self): self.set_stream(1) def flash_erase_block64(self): - self.iobuffer[0] = 0xd8 + self.iobuffer[0] = 0xD8 self.iobuffer[1] = 0 self.iobuffer[2] = 0 self.iobuffer[3] = 0 @@ -213,10 +269,10 @@ def flash_erase_block64(self): self.set_stream(1) def flash_erase_block64_m(self, addr): - self.iobuffer[0] = 0xd8 - self.iobuffer[1] = (addr >> 16) & 0xff - self.iobuffer[2] = (addr >> 8) & 0xff - self.iobuffer[3] = (addr >> 0) & 0xff + self.iobuffer[0] = 0xD8 + self.iobuffer[1] = (addr >> 16) & 0xFF + self.iobuffer[2] = (addr >> 8) & 0xFF + self.iobuffer[3] = (addr >> 0) & 0xFF self.ilength = 4 self.set_stream(0) @@ -225,9 +281,9 @@ def flash_erase_block64_m(self, addr): def flash_erase_section(self, addr): self.iobuffer[0] = 0x20 - self.iobuffer[1] = (addr >> 16) & 0x1f - self.iobuffer[2] = (addr >> 8) & 0x1f - self.iobuffer[3] = (addr >> 0) & 0x1f + self.iobuffer[1] = (addr >> 16) & 0x1F + self.iobuffer[2] = (addr >> 8) & 0x1F + self.iobuffer[3] = (addr >> 0) & 0x1F self.ilength = 4 self.set_stream(0) @@ -251,7 +307,7 @@ def flash_is_busy(self): self.stream_spi4() self.set_stream(1) - return (int.from_bytes(self.iobuffer[1], byteorder='little') & 1) + return int.from_bytes(self.iobuffer[1], byteorder="little") & 1 def flash_erase_flash(self, block): self.flash_write_enable() @@ -277,19 +333,18 @@ def flash_erase_vtx(self): def flash_write_page(self, base_address, length, fw): self.iobuffer[0] = 0x02 - self.iobuffer[1] = (base_address >> 16) & 0xff - self.iobuffer[2] = (base_address >> 8) & 0xff - self.iobuffer[3] = (base_address >> 0) & 0xff + self.iobuffer[1] = (base_address >> 16) & 0xFF + self.iobuffer[2] = (base_address >> 8) & 0xFF + self.iobuffer[3] = (base_address >> 0) & 0xFF self.ilength = 4 + length for i in range(length): try: - self.iobuffer[4+i] = fw[i] + self.iobuffer[4 + i] = fw[i] except: - self.iobuffer[4+i] = 0xff + self.iobuffer[4 + i] = 0xFF - self.write_crc += int.from_bytes( - self.iobuffer[4 + i], byteorder='little') + self.write_crc += int.from_bytes(self.iobuffer[4 + i], byteorder="little") self.set_stream(0) self.stream_spi4() @@ -297,9 +352,9 @@ def flash_write_page(self, base_address, length, fw): def flash_read_page(self, base_address, length): self.iobuffer[0] = 0x03 - self.iobuffer[1] = (base_address >> 16) & 0xff - self.iobuffer[2] = (base_address >> 8) & 0xff - self.iobuffer[3] = (base_address >> 0) & 0xff + self.iobuffer[1] = (base_address >> 16) & 0xFF + self.iobuffer[2] = (base_address >> 8) & 0xFF + self.iobuffer[3] = (base_address >> 0) & 0xFF self.ilength = 4 + length self.set_stream(0) @@ -307,11 +362,11 @@ def flash_read_page(self, base_address, length): self.set_stream(1) for i in range(256): - self.read_crc += int.from_bytes( - self.iobuffer[4 + i], byteorder='little') + self.read_crc += int.from_bytes(self.iobuffer[4 + i], byteorder="little") def connect_vtx(self): if self.dll.CH341OpenDevice(0) < 0: + logger.warning("Failed to open CH341 device for VTX") return 0 else: self.flash_switch0() @@ -320,10 +375,21 @@ def connect_vtx(self): flash_id_1 = self.flash_read_id() self.flash_switch2() flash_id_2 = self.flash_read_id() + logger.debug( + f"VTX Flash IDs - 0:{hex(flash_id_0)} 1:{hex(flash_id_1)} 2:{hex(flash_id_2)}" + ) if flash_id_0 == flash_id_1 and flash_id_1 == flash_id_2: - if flash_id_0 == 0xEF4014 or flash_id_0 == 0x5E6014 or flash_id_0 == 0x856014: + if ( + flash_id_0 == 0xEF4014 + or flash_id_0 == 0x5E6014 + or flash_id_0 == 0x856014 + ): + logger.info("VTX connected successfully") return 1 + logger.warning( + f"VTX flash ID mismatch or unknown ID. IDs: {hex(flash_id_0)}" + ) return 0 def flash_write_target_id(self): @@ -333,32 +399,43 @@ def flash_write_target_id(self): self.flash_wait_busy() def flash_write_fw(self): - size = os.path.getsize(self.fw_path) - file = open(self.fw_path, "rb") - fw = file.read() + logger.info(f"Starting firmware flash write from {self.fw_path}") + try: + size = os.path.getsize(self.fw_path) + file = open(self.fw_path, "rb") + fw = file.read() + file.close() - page_number = (size + (1 << 8) - 1) >> 8 - self.write_crc = 0 - self.read_crc = 0 + page_number = (size + (1 << 8) - 1) >> 8 + self.write_crc = 0 + self.read_crc = 0 - for page in range(page_number): - base_address = page << 8 - self.flash_write_enable() - self.flash_write_page(base_address, 256, fw[base_address:]) - self.flash_write_disable() - self.flash_wait_busy() - - self.flash_read_page(base_address, 256) - - my_ch341.written_len += 256 - - if self.write_crc == self.read_crc: - return 1 - else: + for page in range(page_number): + base_address = page << 8 + self.flash_write_enable() + self.flash_write_page(base_address, 256, fw[base_address:]) + self.flash_write_disable() + self.flash_wait_busy() + + self.flash_read_page(base_address, 256) + + my_ch341.written_len += 256 + + if self.write_crc == self.read_crc: + logger.info(f"Firmware flash successful! CRC match: {self.write_crc}") + return 1 + else: + logger.error( + f"Firmware flash failed! CRC mismatch - Write: {self.write_crc}, Read: {self.read_crc}" + ) + return 0 + except Exception as e: + logger.error(f"Exception during flash writing: {e}") return 0 def connect_monitor(self, sleep_sec): if self.dll.CH341OpenDevice(0) < 0: + logger.warning("Failed to open CH341 device for Monitor") return 0 else: # self.dll.CH341SetStream(0, 0x82) @@ -366,23 +443,30 @@ def connect_monitor(self, sleep_sec): self.flash_switch1() flash_id_2 = self.flash_read_id() if flash_id_2 == 0xEF4018: + logger.info("Monitor connected successfully") return 1 else: + logger.debug(f"Monitor not connected or ID mismatch: {hex(flash_id_2)}") return 0 def fw_write_to_flash(self, fw_buf, fw_size): - page_number = (fw_size + (1 << 8) - 1) >> 8 - for page in range(page_number): - block = page << 8 - if (block & 0xffff) == 0: - self.flash_erase_flash(block) - - base_address = page << 8 - self.flash_write_enable() - self.flash_write_page(base_address, 256, fw_buf[base_address:]) - self.flash_write_disable() - self.flash_wait_busy() - my_ch341.written_len += 256 + logger.info(f"Writing {fw_size} bytes to flash...") + try: + page_number = (fw_size + (1 << 8) - 1) >> 8 + for page in range(page_number): + block = page << 8 + if (block & 0xFFFF) == 0: + self.flash_erase_flash(block) + + base_address = page << 8 + self.flash_write_enable() + self.flash_write_page(base_address, 256, fw_buf[base_address:]) + self.flash_write_disable() + self.flash_wait_busy() + my_ch341.written_len += 256 + logger.info("Write to flash completed.") + except Exception as e: + logger.error(f"Error writing to flash: {e}") """ for page in range(page_number): base_address = page << 8 @@ -395,6 +479,7 @@ def fw_write_to_flash(self, fw_buf, fw_size): # ---------------- event_vrx -------------------------------- def connect_event_vrx(self): if self.dll.CH341OpenDevice(nIndex) < 0: + logger.warning("Failed to open CH341 device for Event VRX") return 0 else: self.dll.CH341SetStream(nIndex, 0x81) @@ -404,44 +489,48 @@ def FlashChipErase(self): self.dll.CH341SetStream(nIndex, 0x80) self.iobuffer[0] = self.WRITE_ENABLE - self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) + self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) self.iobuffer[0] = self.CHIP_ERASE - self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) + self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) self.iobuffer[0] = self.WRITE_DISABLE - self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) + self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, 1, self.iobuffer) def data_cpy(self, dest, dst_off, src, src_off, length): for i in range(length): - dest[dst_off+i] = src[src_off+i] + dest[dst_off + i] = src[src_off + i] def write_SPI(self, addr, data_buf, size): - temp_write_buffer = create_string_buffer(int(PAGE_SIZE+HEAD_SIZE)) + temp_write_buffer = create_string_buffer(int(PAGE_SIZE + HEAD_SIZE)) self.dll.CH341SetStream(nIndex, 0x80) page = 0 while size > PAGE_SIZE: temp_write_buffer[0] = self.WRITE_ENABLE self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) + nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer + ) temp_write_buffer[0] = self.PAGE_PROGRAM - temp_write_buffer[1] = ((addr & 0xFF0000) >> 16) - temp_write_buffer[2] = ((addr & 0x00FF00) >> 8) - temp_write_buffer[3] = (addr & 0x0000FF) + temp_write_buffer[1] = (addr & 0xFF0000) >> 16 + temp_write_buffer[2] = (addr & 0x00FF00) >> 8 + temp_write_buffer[3] = addr & 0x0000FF - self.data_cpy(temp_write_buffer, HEAD_SIZE, data_buf, - (page * PAGE_SIZE), PAGE_SIZE) - self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, int( - PAGE_SIZE+HEAD_SIZE), temp_write_buffer) + self.data_cpy( + temp_write_buffer, HEAD_SIZE, data_buf, (page * PAGE_SIZE), PAGE_SIZE + ) + self.dll.CH341StreamSPI4( + nIndex, + self.SELECT_FPGA_5680, + int(PAGE_SIZE + HEAD_SIZE), + temp_write_buffer, + ) temp_write_buffer[0] = self.WRITE_DISABLE self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) + nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer + ) self.dll.CH341SetDelaymS(nIndex, 2) size -= PAGE_SIZE @@ -449,44 +538,45 @@ def write_SPI(self, addr, data_buf, size): addr += PAGE_SIZE temp_write_buffer[0] = self.WRITE_ENABLE - self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) + self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) temp_write_buffer[0] = self.PAGE_PROGRAM - temp_write_buffer[1] = ((addr & 0xFF0000) >> 16) - temp_write_buffer[2] = ((addr & 0x00FF00) >> 8) - temp_write_buffer[3] = (addr & 0x0000FF) + temp_write_buffer[1] = (addr & 0xFF0000) >> 16 + temp_write_buffer[2] = (addr & 0x00FF00) >> 8 + temp_write_buffer[3] = addr & 0x0000FF if size < PAGE_SIZE: - self.data_cpy(temp_write_buffer, HEAD_SIZE, - data_buf, (page * PAGE_SIZE), size) + self.data_cpy( + temp_write_buffer, HEAD_SIZE, data_buf, (page * PAGE_SIZE), size + ) else: - self.data_cpy(temp_write_buffer, HEAD_SIZE, data_buf, - (page * PAGE_SIZE), PAGE_SIZE) + self.data_cpy( + temp_write_buffer, HEAD_SIZE, data_buf, (page * PAGE_SIZE), PAGE_SIZE + ) - self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, int( - PAGE_SIZE+HEAD_SIZE), temp_write_buffer) + self.dll.CH341StreamSPI4( + nIndex, self.SELECT_FPGA_5680, int(PAGE_SIZE + HEAD_SIZE), temp_write_buffer + ) temp_write_buffer[0] = self.WRITE_DISABLE - self.dll.CH341StreamSPI4( - nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) + self.dll.CH341StreamSPI4(nIndex, self.SELECT_FPGA_5680, 1, temp_write_buffer) def write_event_vrx_fw_to_flash(self, path): file = open(path, "rb") - file_size = os.path.getsize(path) # file_size: 2383867 + file_size = os.path.getsize(path) # file_size: 2383867 head_size = file.read(8) - file5680_size = int(head_size) - 2560 # 58581 = 64141 - 2560 + file5680_size = int(head_size) - 2560 # 58581 = 64141 - 2560 print("file: ", path) # erase 5680 flash my_ch341.written_len += 15 * PAGE_SIZE - self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_5680) + self.dll.CH341SetOutput(nIndex, 0x03, 0xFFFFFFFF, self.FLASH_SET_5680) time.sleep(0.01) self.FlashChipErase() my_ch341.written_len += 15 * PAGE_SIZE time.sleep(1) # erase fpga flash - self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) + self.dll.CH341SetOutput(nIndex, 0x03, 0xFFFFFFFF, self.FLASH_SET_FPGA) time.sleep(0.01) self.FlashChipErase() my_ch341.written_len += 15 * PAGE_SIZE @@ -496,35 +586,43 @@ def write_event_vrx_fw_to_flash(self, path): my_ch341.written_len += 10 * PAGE_SIZE # write 5680 data to flash - self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_5680) + self.dll.CH341SetOutput(nIndex, 0x03, 0xFFFFFFFF, self.FLASH_SET_5680) time.sleep(0.01) file.seek(8) page = 0 while page * self.buffer_size < file5680_size: self.write_buffer = file.read(self.buffer_size) - self.write_SPI(self.FLASH_BASE_ADDR + (page * self.buffer_size), - self.write_buffer, len(self.write_buffer)) + self.write_SPI( + self.FLASH_BASE_ADDR + (page * self.buffer_size), + self.write_buffer, + len(self.write_buffer), + ) my_ch341.written_len += 15 * PAGE_SIZE time.sleep(0.1) page += 1 # write 5680 last page data page -= 1 - self.write_buffer = file.read( - file5680_size - (page * self.buffer_size)) - self.write_SPI(self.FLASH_BASE_ADDR + (page + 1) * self.buffer_size, - self.write_buffer, file5680_size - (page * self.buffer_size)) + self.write_buffer = file.read(file5680_size - (page * self.buffer_size)) + self.write_SPI( + self.FLASH_BASE_ADDR + (page + 1) * self.buffer_size, + self.write_buffer, + file5680_size - (page * self.buffer_size), + ) time.sleep(1) my_ch341.written_len += 15 * PAGE_SIZE # write fpga data to flash - self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) + self.dll.CH341SetOutput(nIndex, 0x03, 0xFFFFFFFF, self.FLASH_SET_FPGA) page = 0 while True: self.write_buffer = file.read(self.buffer_size) - self.write_SPI(self.FLASH_BASE_ADDR + (page * self.buffer_size), - self.write_buffer, len(self.write_buffer)) + self.write_SPI( + self.FLASH_BASE_ADDR + (page * self.buffer_size), + self.write_buffer, + len(self.write_buffer), + ) time.sleep(0.1) my_ch341.written_len += 9 * PAGE_SIZE @@ -534,14 +632,31 @@ def write_event_vrx_fw_to_flash(self, path): file.flush() file.close() - self.dll.CH341SetOutput(nIndex, 0x03, 0xffffffff, self.FLASH_SET_FPGA) + self.dll.CH341SetOutput(nIndex, 0x03, 0xFFFFFFFF, self.FLASH_SET_FPGA) my_ch341 = ch341_class() def ch341_thread_proc(): + last_status = None while True: + if my_ch341.status != last_status: + try: + status_name = ch341_status(my_ch341.status).name + last_status_name = ( + ch341_status(last_status).name + if last_status is not None + else "None" + ) + except ValueError: + status_name = str(my_ch341.status) + last_status_name = str(last_status) + logger.info( + f"State transition: {last_status_name} ({last_status}) -> {status_name} ({my_ch341.status})" + ) + last_status = my_ch341.status + if my_ch341.status == ch341_status.STATUS_EXIT.value: sys.exit() @@ -553,7 +668,9 @@ def ch341_thread_proc(): my_ch341.written_len = 0 my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) - if my_ch341.to_write_len == 0 or my_ch341.to_write_len >= 65536: # check fw size + if ( + my_ch341.to_write_len == 0 or my_ch341.to_write_len >= 65536 + ): # check fw size my_ch341.status = ch341_status.VTX_FW_ERROR.value else: my_ch341.flash_erase_vtx() @@ -573,7 +690,9 @@ def ch341_thread_proc(): my_ch341.status = ch341_status.VTX_RECONNECTDONE.value # -------- Monitor ----------------- - elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value: # check monitor is alive + elif ( + my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value + ): # check monitor is alive if my_ch341.connect_monitor(0.35) == 1: if my_ch341.monitor_connected == 0: time.sleep(0.5) @@ -588,20 +707,19 @@ def ch341_thread_proc(): my_ch341.status = ch341_status.MONITOR_FW_ERROR.value else: my_ch341.flash_switch0() - my_ch341.fw_write_to_flash( - my_ch341.fw_5680_buf, my_ch341.fw_5680_size) + my_ch341.fw_write_to_flash(my_ch341.fw_5680_buf, my_ch341.fw_5680_size) my_ch341.flash_switch1() - my_ch341.fw_write_to_flash( - my_ch341.fw_fpga_buf, my_ch341.fw_fpga_size) + my_ch341.fw_write_to_flash(my_ch341.fw_fpga_buf, my_ch341.fw_fpga_size) my_ch341.flash_switch2() - my_ch341.fw_write_to_flash( - my_ch341.fw_8339_buf, my_ch341.fw_8339_size) + my_ch341.fw_write_to_flash(my_ch341.fw_8339_buf, my_ch341.fw_8339_size) my_ch341.dll.CH341CloseDevice(0) my_ch341.flash_release() my_ch341.status = ch341_status.MONITOR_UPDATEDONE.value # ---------------------- event_vrx ------------------------------------ - elif my_ch341.status == ch341_status.EVENT_VRX_DISCONNECTED.value: # connect event vrx + elif ( + my_ch341.status == ch341_status.EVENT_VRX_DISCONNECTED.value + ): # connect event vrx if my_ch341.connect_event_vrx() == 1: my_ch341.status = ch341_status.EVENT_VRX_CONNECTED.value @@ -626,7 +744,9 @@ def ch341_thread_proc(): 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 + 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 @@ -637,7 +757,9 @@ def ch341_thread_proc(): 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 + elif ( + my_ch341.status == ch341_status.RADIO_UPDATE_ELRS_BACKPACK.value + ): # update elrs backpack my_ch341.written_len = 400 my_ch341.fw_index = 2 @@ -645,7 +767,9 @@ def ch341_thread_proc(): 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 + 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: @@ -655,5 +779,4 @@ def ch341_thread_proc(): else: my_ch341.status = ch341_status.RADIO_UPDATE_DONE.value - else: - time.sleep(0.1) + time.sleep(0.05) diff --git a/download.py b/download.py index d9dfaf9..d5ba970 100644 --- a/download.py +++ b/download.py @@ -4,6 +4,9 @@ from global_var import * import os import zipfile +import logging + +logger = logging.getLogger(__name__) class download: @@ -16,7 +19,7 @@ def __init__(self): self.pre_download = 0 def download_file(self, url, save_path, clear): - print(f"Downloading {url}") + logger.info(f"Downloading {url}") if clear: if os.path.exists(save_path): os.remove(save_path) @@ -27,18 +30,25 @@ def download_file(self, url, save_path, clear): for chunk in response.iter_content(chunk_size=1024): if self.to_stop == 1: self.to_stop = 0 + logger.info("Download stopped by user instruction.") return 2 if chunk: file.write(chunk) if self.status == download_status.DOWNLOAD_EXIT.value: + logger.info("Download thread exiting...") sys.exit() + logger.info(f"Successfully downloaded to {save_path}") return 1 else: if self.status == download_status.DOWNLOAD_EXIT.value: + logger.info("Download thread exiting...") sys.exit() - print("Failed to download file.") + logger.warning( + f"Failed to download file. HTTP Status: {response.status_code}" + ) return 0 - except: + except Exception as e: + logger.error(f"Networking error while downloading: {e}") return 0 @@ -48,20 +58,21 @@ def download_file(self, url, save_path, clear): def download_thread_proc(): while my_download.pre_download != 63: time.sleep(0.1) - + my_download.status = download_status.FILE_PARSE.value while True: if my_download.status == download_status.DOWNLOAD_VTX_FW.value: ret = my_download.download_file( - my_download.url, my_download.save_path+".zip", 1) + my_download.url, my_download.save_path + ".zip", 1 + ) if ret == 1: # unzip file - with zipfile.ZipFile(my_download.save_path+".zip", 'r') as zip_ref: + with zipfile.ZipFile(my_download.save_path + ".zip", "r") as zip_ref: zip_ref.extractall("resource/") # rename file - if (os.path.exists(my_download.save_path)): + if os.path.exists(my_download.save_path): os.remove(my_download.save_path) os.rename("resource/HDZERO_TX.bin", my_download.save_path) @@ -72,8 +83,7 @@ def download_thread_proc(): my_download.status = download_status.DOWNLOAD_VTX_FW_FAILED.value elif my_download.status == download_status.DOWNLOAD_MONITOR_FW.value: - ret = my_download.download_file( - my_download.url, my_download.save_path, 1) + ret = my_download.download_file(my_download.url, my_download.save_path, 1) if ret == 1: my_download.status = download_status.DOWNLOAD_MONITOR_FW_DONE.value elif ret == 2: # stop @@ -82,8 +92,7 @@ def download_thread_proc(): my_download.status = download_status.DOWNLOAD_MONITOR_FW_FAILED.value elif my_download.status == download_status.DOWNLOAD_EVENT_VRX_FW.value: - ret = my_download.download_file( - my_download.url, my_download.save_path, 1) + ret = my_download.download_file(my_download.url, my_download.save_path, 1) if ret == 1: my_download.status = download_status.DOWNLOAD_EVENT_VRX_FW_DONE.value elif ret == 2: # stop @@ -92,8 +101,7 @@ def download_thread_proc(): 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) + 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 @@ -106,32 +114,56 @@ def download_thread_proc(): 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) + "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) + "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) + "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) + "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) + "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/hd-zero/hdzero-radio/releases", "resource/radio_releases", 1) - my_download.pre_download += 32 \ No newline at end of file + "https://api.github.com/repos/hd-zero/hdzero-radio/releases", + "resource/radio_releases", + 1, + ) + my_download.pre_download += 32 diff --git a/frame_monitor.py b/frame_monitor.py index ad16a22..fed2b33 100644 --- a/frame_monitor.py +++ b/frame_monitor.py @@ -3,6 +3,11 @@ import ctypes import global_var import time +import sys +import os +import logging + +logger = logging.getLogger(__name__) class frame_monitor: @@ -11,7 +16,20 @@ def __init__(self, parent): self._frame = tk.Frame(parent) parent.add(self._frame, text="Monitor") - self.dll_name = "CH341DLL.DLL" + is_64bit = sys.maxsize > 2**32 + dll_name = "CH341DLLA64.DLL" if is_64bit else "CH341DLL.DLL" + + if getattr(sys, "frozen", False): + base_dir = sys._MEIPASS + else: + base_dir = os.path.dirname(os.path.abspath(__file__)) + + driver_path = os.path.join(base_dir, "resource", "driver", dll_name) + if os.path.exists(driver_path): + self.dll_name = driver_path + else: + self.dll_name = dll_name + self.color_background = "#303030" self.color_label = "white" @@ -38,7 +56,7 @@ def __init__(self, parent): self.backlight_min = 1 self.backlight_max = 100 self.backlight_default = 80 - self.cell_count_min = 1 # 1=auto + self.cell_count_min = 1 # 1=auto self.cell_count_max = 6 self.cell_count_default = 1 self.warning_cell_voltage_min = 28 @@ -78,7 +96,8 @@ def __init__(self, parent): try: self.dll = ctypes.WinDLL(self.dll_name) - except: + except Exception as e: + logger.error(f"Failed to load {self.dll_name} in frame_monitor: {e}") print("Please check ch341 driver") def usb_heart(self): @@ -140,7 +159,10 @@ def write_setting(self, b, c, s, l, cell, warning_cell, osd): l = self.backlight_default if cell < self.cell_count_min or cell > self.cell_count_max: cell = self.cell_count_default - if warning_cell < self.warning_cell_voltage_min or warning_cell > self.warning_cell_voltage_max: + if ( + warning_cell < self.warning_cell_voltage_min + or warning_cell > self.warning_cell_voltage_max + ): warning_cell = self.warning_cell_voltage_default if osd < self.osd_min or osd > self.osd_max: @@ -168,13 +190,13 @@ def write_setting(self, b, c, s, l, cell, warning_cell, osd): self.osd_var.set(False) # update label - self.brightness_label.config(text=f'{b}') - self.contrast_label.config(text=f'{c}') - self.saturation_label.config(text=f'{s}') - self.backlight_label.config(text=f'{l}') + self.brightness_label.config(text=f"{b}") + self.contrast_label.config(text=f"{c}") + self.saturation_label.config(text=f"{s}") + self.backlight_label.config(text=f"{l}") option = ["Auto", "2S", "3S", "4S", "5S", "6S"] - self.cell_count_label.config(text=option[cell-1]) + self.cell_count_label.config(text=option[cell - 1]) self.warning_cell_voltage_label.config(text=f"{warning_cell/10}") def setting_disable(self): @@ -212,8 +234,7 @@ def reset_scale(self): self.backlight_label.config(text=f"{int(float(self.brightness_min))}") self.on_cell_count_scale_changed(self.cell_count_min) - self.on_warning_cell_voltage_scale_changed( - self.warning_cell_voltage_min) + self.on_warning_cell_voltage_scale_changed(self.warning_cell_voltage_min) self.osd_var.set(False) self.on_osd_checkoutbutton_changed() @@ -239,13 +260,12 @@ def on_backlight_scale_changed(self, value): def on_cell_count_scale_changed(self, value): option = ["Auto", "2S", "3S", "4S", "5S", "6S"] self.cell_count = int(float(value)) - self.cell_count_label.config(text=option[self.cell_count-1]) + self.cell_count_label.config(text=option[self.cell_count - 1]) self.write_cell_count(int(float(value))) def on_warning_cell_voltage_scale_changed(self, value): self.warning_cell_voltage = int(float(value)) - self.warning_cell_voltage_label.config( - text=f"{self.warning_cell_voltage/10}") + self.warning_cell_voltage_label.config(text=f"{self.warning_cell_voltage/10}") self.write_warning_cell_voltage(int(float(value))) def on_osd_checkoutbutton_changed(self): @@ -263,18 +283,13 @@ def on_reset_button_press(self): self.warning_cell_voltage_scale.set(self.warning_cell_voltage_default) self.osd_var.set(True) - self.brightness_label.config( - text=f"{int(float(self.brightness_default))}") - self.contrast_label.config( - text=f"{int(float(self.contrast_default))}") - self.saturation_label.config( - text=f"{int(float(self.saturation_default))}") - self.backlight_label.config( - text=f"{int(float(self.backlight_default))}") + self.brightness_label.config(text=f"{int(float(self.brightness_default))}") + self.contrast_label.config(text=f"{int(float(self.contrast_default))}") + self.saturation_label.config(text=f"{int(float(self.saturation_default))}") + self.backlight_label.config(text=f"{int(float(self.backlight_default))}") self.on_cell_count_scale_changed(self.cell_count_default) - self.on_warning_cell_voltage_scale_changed( - self.warning_cell_voltage_default) + self.on_warning_cell_voltage_scale_changed(self.warning_cell_voltage_default) self.osd_var.set(True) self.on_osd_checkoutbutton_changed() @@ -284,8 +299,14 @@ def init_image_setting(self): label = ttk.Label(self._frame, text="Brightness") label.grid(row=row, column=0, sticky="w", padx=20) - self.brightness_scale = ttk.Scale(self._frame, from_=self.brightness_min, to=self.brightness_max, orient="horizontal", - length=350, command=self.on_brightness_scale_changed) + self.brightness_scale = ttk.Scale( + self._frame, + from_=self.brightness_min, + to=self.brightness_max, + orient="horizontal", + length=350, + command=self.on_brightness_scale_changed, + ) self.brightness_scale.grid(row=row, column=1, sticky="w", padx=20) self.brightness_label = ttk.Label(self._frame, text="0") @@ -296,8 +317,14 @@ def init_image_setting(self): label = ttk.Label(self._frame, text="Contrast") label.grid(row=row, column=0, sticky="w", padx=20) - self.contrast_scale = ttk.Scale(self._frame, from_=self.contrast_min, to=self.contrast_max, orient="horizontal", - length=350, command=self.on_contrast_scale_changed) + self.contrast_scale = ttk.Scale( + self._frame, + from_=self.contrast_min, + to=self.contrast_max, + orient="horizontal", + length=350, + command=self.on_contrast_scale_changed, + ) self.contrast_scale.grid(row=row, column=1, sticky="w", padx=20) self.contrast_label = ttk.Label(self._frame, text="0") @@ -308,8 +335,14 @@ def init_image_setting(self): label = ttk.Label(self._frame, text="Saturation") label.grid(row=row, column=0, sticky="w", padx=20) - self.saturation_scale = ttk.Scale(self._frame, from_=self.saturation_min, to=self.saturation_max, orient="horizontal", - length=350, command=self.on_saturation_scale_changed) + self.saturation_scale = ttk.Scale( + self._frame, + from_=self.saturation_min, + to=self.saturation_max, + orient="horizontal", + length=350, + command=self.on_saturation_scale_changed, + ) self.saturation_scale.grid(row=row, column=1, sticky="w", padx=20) self.saturation_label = ttk.Label(self._frame, text="0") @@ -320,8 +353,14 @@ def init_image_setting(self): label = ttk.Label(self._frame, text="Backlight") label.grid(row=row, column=0, sticky="w", padx=20) - self.backlight_scale = ttk.Scale(self._frame, from_=self.backlight_min, to=self.backlight_max, orient="horizontal", - length=350, command=self.on_backlight_scale_changed) + self.backlight_scale = ttk.Scale( + self._frame, + from_=self.backlight_min, + to=self.backlight_max, + orient="horizontal", + length=350, + command=self.on_backlight_scale_changed, + ) self.backlight_scale.grid(row=row, column=1, sticky="w", padx=20) self.backlight_label = ttk.Label(self._frame, text="0") @@ -332,8 +371,14 @@ def init_power_setting(self): label = ttk.Label(self._frame, text="Cell Count") label.grid(row=row, column=0, sticky="w", padx=20) - self.cell_count_scale = ttk.Scale(self._frame, from_=self.cell_count_min, to=self.cell_count_max, orient="horizontal", - length=350, command=self.on_cell_count_scale_changed) + self.cell_count_scale = ttk.Scale( + self._frame, + from_=self.cell_count_min, + to=self.cell_count_max, + orient="horizontal", + length=350, + command=self.on_cell_count_scale_changed, + ) self.cell_count_scale.grid(row=row, column=1, sticky="w", padx=20) self.cell_count_label = ttk.Label(self._frame, text="Auto") @@ -343,14 +388,18 @@ def init_power_setting(self): label = ttk.Label(self._frame, text="Warning Cell Voltage") label.grid(row=row, column=0, sticky="w", padx=20) - self.warning_cell_voltage_scale = ttk.Scale(self._frame, from_=self.warning_cell_voltage_min, to=self.warning_cell_voltage_max, - orient="horizontal", length=350, command=self.on_warning_cell_voltage_scale_changed) - self.warning_cell_voltage_scale.grid( - row=row, column=1, sticky="w", padx=20) + self.warning_cell_voltage_scale = ttk.Scale( + self._frame, + from_=self.warning_cell_voltage_min, + to=self.warning_cell_voltage_max, + orient="horizontal", + length=350, + command=self.on_warning_cell_voltage_scale_changed, + ) + self.warning_cell_voltage_scale.grid(row=row, column=1, sticky="w", padx=20) self.warning_cell_voltage_label = ttk.Label(self._frame, text="2.8") - self.warning_cell_voltage_label.grid( - row=row, column=2, sticky="w", padx=10) + self.warning_cell_voltage_label.grid(row=row, column=2, sticky="w", padx=10) def init_osd_setting(self): row = 6 @@ -359,12 +408,17 @@ def init_osd_setting(self): label.grid(row=row, column=0, sticky="w", padx=20) self.osd_checkbutton = ttk.Checkbutton( - self._frame, variable=self.osd_var, text="", command=self.on_osd_checkoutbutton_changed) + self._frame, + variable=self.osd_var, + text="", + command=self.on_osd_checkoutbutton_changed, + ) self.osd_checkbutton.grid(row=row, column=0, sticky="w", padx=100) def init_reset_button(self): row = 6 self.reset_button = tk.Button( - self._frame, text="Reset settings", command=self.on_reset_button_press) + self._frame, text="Reset settings", command=self.on_reset_button_press + ) self.reset_button.grid(row=row, column=1, sticky="w", padx=100) diff --git a/frame_programmer.py b/frame_programmer.py index bc401c8..9e5b609 100644 --- a/frame_programmer.py +++ b/frame_programmer.py @@ -2,6 +2,9 @@ from tkinter import ttk from tkinter import filedialog import os +import logging + +logger = logging.getLogger(__name__) class frame_programmer: @@ -27,17 +30,26 @@ def __init__(self, parent): self.is_load_online = tk.StringVar() self.version_combobox = ttk.Combobox( - self._frame, values=self.online_list, state="readonly") + self._frame, values=self.online_list, state="readonly" + ) self.version_combobox_set_default() self.version_combobox_disable() self.online_fw_button = ttk.Radiobutton( - self._frame, text="Load Online Firmware", variable=self.is_load_online, value='1') + self._frame, + text="Load Online Firmware", + variable=self.is_load_online, + value="1", + ) self.online_fw_button_show() self.online_fw_button_disable() self.local_fw_button = ttk.Radiobutton( - self._frame, text="Load Local Firmware", variable=self.is_load_online, value='0') + self._frame, + text="Load Local Firmware", + variable=self.is_load_online, + value="0", + ) self.local_fw_button.grid(row=1, column=1, padx=5, pady=5) self.local_fw_button_disable() @@ -77,17 +89,16 @@ def online_fw_button_show(self): self.version_combobox.grid_remove() def online_fw_button_hidden(self): - self.version_combobox.grid( - row=1, column=0, padx=5, pady=5) - self.version_combobox.event_generate('') + self.version_combobox.grid(row=1, column=0, padx=5, pady=5) + self.version_combobox.event_generate("") self.online_fw_button.grid_remove() self.local_fw_button_set_str_default() def online_fw_button_set_str(self, str): - self.online_fw_button['text'] = str + self.online_fw_button["text"] = str def online_fw_button_set_str_default(self): - self.online_fw_button['text'] = "Load Online Firmware" + self.online_fw_button["text"] = "Load Online Firmware" def local_fw_button_disable(self): self.local_fw_button.config(state="disabled") @@ -113,21 +124,23 @@ def select_local_file(self): with open("resource/local_path", "r") as file: path = file.read() file.close() - except: + except Exception as e: + logger.debug(f"Could not read local_path: {e}") path = "." try: self.local_file_path = filedialog.askopenfilename( - initialdir=path, title="select a firmware", filetypes=filetypes) - except: + initialdir=path, title="select a firmware", filetypes=filetypes + ) + except Exception as e: + logger.warning(f"File dialog execution failed: {e}") print("please select a firmware file") if self.local_file_path: self.mode = 1 - self.local_file_path_shorten = self.shorten_path( - self.local_file_path) + self.local_file_path_shorten = self.shorten_path(self.local_file_path) self.local_fw_button_set_str(self.local_file_path_shorten) - path = self.local_file_path[:self.local_file_path.rfind('/') + 1] + path = self.local_file_path[: self.local_file_path.rfind("/") + 1] with open("resource/local_path", "w") as file: file.write(path) file.close() diff --git a/frame_statusbar.py b/frame_statusbar.py index bb1da6e..106a620 100644 --- a/frame_statusbar.py +++ b/frame_statusbar.py @@ -32,18 +32,17 @@ def status_label_set_text(self, text, color): def status_label_set_bg(self, color): self.label["bg"] = color - ''' + """ if color == 1: # system self.label["bg"] = "SystemButtonFace" elif color == 2: # red self.label["bg"] = "red" else: # green self.label["bg"] = "#06b025" - ''' + """ def init_progress_bar(self): - self.bar = ttk.Progressbar( - self._frame, orient="horizontal", mode="determinate") + self.bar = ttk.Progressbar(self._frame, orient="horizontal", mode="determinate") self.bar.pack(fill="both", expand=True) def progress_bar_set_value(self, value): diff --git a/frame_vtx.py b/frame_vtx.py index ddc9b11..1c840bc 100644 --- a/frame_vtx.py +++ b/frame_vtx.py @@ -1,9 +1,12 @@ import tkinter as tk from tkinter import ttk from PIL import Image, ImageTk +import logging +logger = logging.getLogger(__name__) -class frame_vtx(): + +class frame_vtx: def __init__(self, parent): self._parent = parent self._frame = tk.Frame(parent) @@ -27,10 +30,19 @@ def create_radio_button_list(self, targets, callback, vtx_image): for i in range(0, self.target_num): # self.image.append(Image.open(f"{i}.png")) self.tk_image.append(ImageTk.PhotoImage(vtx_image[i])) - self.radio_button.append(ttk.Radiobutton(self._frame, image=self.tk_image[i], - variable=self.vtx_target, value=targets[i], compound="left", command=callback)) + self.radio_button.append( + ttk.Radiobutton( + self._frame, + image=self.tk_image[i], + variable=self.vtx_target, + value=targets[i], + compound="left", + command=callback, + ) + ) self.radio_button[i].grid( - row=(int)(i % 4), column=(int)(i/4), padx=5, pady=5) + row=(int)(i % 4), column=(int)(i / 4), padx=5, pady=5 + ) self.radio_button_reset() def radio_button_disable(self): @@ -44,5 +56,6 @@ def radio_button_enable(self): def radio_button_reset(self): try: self.radio_button[0].invoke() - except: + except Exception as e: + logger.debug(f"radio_button_reset hit an error invoking first button: {e}") pass diff --git a/global_var.py b/global_var.py index 7223fd0..2b309c4 100644 --- a/global_var.py +++ b/global_var.py @@ -1,5 +1,6 @@ from enum import IntEnum from enum import Enum, unique + brightness = 0 contrast = 0 saturation = 0 @@ -71,5 +72,4 @@ class download_status(Enum): DOWNLOAD_RADIO_FW_DONE = 31 DOWNLOAD_RADIO_FW_FAILED = 32 - DOWNLOAD_EXIT = 255 diff --git a/hdzero_programmer.py b/hdzero_programmer.py index 15c8712..cb4cd9f 100644 --- a/hdzero_programmer.py +++ b/hdzero_programmer.py @@ -3,6 +3,9 @@ import sys import shutil import time +import logging +import glob +from datetime import datetime from main_window import ui_thread_proc from download import download_thread_proc from download import download_vtx_releases @@ -14,8 +17,37 @@ from ch341 import ch341_thread_proc +def setup_logging(): + log_dir = "logs" + if not os.path.exists(log_dir): + os.makedirs(log_dir, exist_ok=True) + + # Clean up old logs (keep last 10) + log_files = glob.glob(os.path.join(log_dir, "hdzero_programmer_*.log")) + log_files.sort(key=os.path.getmtime) + while len(log_files) > 10: + oldest = log_files.pop(0) + try: + os.remove(oldest) + except OSError: + pass + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + log_filename = os.path.join(log_dir, f"hdzero_programmer_{timestamp}.log") + + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler(log_filename, mode="w"), + logging.StreamHandler(sys.stdout), + ], + force=True, + ) + + def get_resource_folder(): - if getattr(sys, 'frozen', False): + if getattr(sys, "frozen", False): base_path = sys._MEIPASS else: base_path = os.path.dirname(os.path.abspath(__file__)) @@ -43,6 +75,8 @@ def check_and_release_resource(): def main(): + setup_logging() + logging.info("Starting HDZero Programmer...") check_and_release_resource() ui_thread = threading.Thread(target=ui_thread_proc, name="ui") @@ -54,31 +88,37 @@ def main(): time.sleep(1) download_0 = threading.Thread( - target=download_vtx_releases, name="download_vtx_releases") + target=download_vtx_releases, name="download_vtx_releases" + ) download_0.start() download_1 = threading.Thread( - target=download_vtx_common, name="download_vtx_common") + 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") + 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") + 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") + target=download_monitor_releases, name="download_monitor_releases" + ) download_4.start() download_5 = threading.Thread( - target=download_radio_releases, name="download_radio_releases") + target=download_radio_releases, name="download_radio_releases" + ) download_5.start() - download_thread = threading.Thread( - target=download_thread_proc, name="download") + download_thread = threading.Thread(target=download_thread_proc, name="download") download_thread.start() -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/icon32.py b/icon32.py index 8598bce..a9e8c85 100644 --- a/icon32.py +++ b/icon32.py @@ -1 +1 @@ -icon32 = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAONJREFUWIXtlTsOwjAQRL2IG1hCgpqe+7ecgAOkTqJIHAEtBQLZZr0/WzR4usSfeTuONyEMDf27oHyBiNXJ5+OhOnhdH9nzCe9fe2fG8BreC4CsKacZ4mcdBYOIAQDCrof5tGxstSlMqSpAL3MJgk1AqxnizbMOEWkAR/UXzXwqhZYEVNFLcgNMy9bDX76GFfOs+vSacV88JU8CbPRSA2oGkKK3JmA+gvKGpMcxQ7Ru16cPJDK37W4A1ui7A3hFAmj7+3uetnrqhlQTaP3JaMxZAAuE11wEkCA01Vsb09DQ0M/1BHTeW8hlv+XyAAAAAElFTkSuQmCC' +icon32 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAONJREFUWIXtlTsOwjAQRL2IG1hCgpqe+7ecgAOkTqJIHAEtBQLZZr0/WzR4usSfeTuONyEMDf27oHyBiNXJ5+OhOnhdH9nzCe9fe2fG8BreC4CsKacZ4mcdBYOIAQDCrof5tGxstSlMqSpAL3MJgk1AqxnizbMOEWkAR/UXzXwqhZYEVNFLcgNMy9bDX76GFfOs+vSacV88JU8CbPRSA2oGkKK3JmA+gvKGpMcxQ7Ru16cPJDK37W4A1ui7A3hFAmj7+3uetnrqhlQTaP3JaMxZAAuE11wEkCA01Vsb09DQ0M/1BHTeW8hlv+XyAAAAAElFTkSuQmCC" diff --git a/icon_convert.py b/icon_convert.py index 865d1fc..2b5337b 100644 --- a/icon_convert.py +++ b/icon_convert.py @@ -1,7 +1,7 @@ import base64 with open("icon32.png", "rb") as image_file: - encoded_string = base64.b64encode(image_file.read()).decode('utf-8') + encoded_string = base64.b64encode(image_file.read()).decode("utf-8") with open("icon32.py", "w") as python_file: python_file.write("icon32 = '{}'\n".format(encoded_string)) diff --git a/main_window.py b/main_window.py index 7ebf85d..ae37619 100644 --- a/main_window.py +++ b/main_window.py @@ -21,6 +21,9 @@ from icon32 import icon32 import io import program_radio +import logging + +logger = logging.getLogger(__name__) class MyGUI: @@ -57,8 +60,9 @@ def init_main_window(self): y = int((screenHeight - self.winHeight) / 2) self._main_window.title(self.title) - self._main_window.geometry("%sx%s+%s+%s" % - (self.winWidth, self.winHeight, x, y)) + self._main_window.geometry( + "%sx%s+%s+%s" % (self.winWidth, self.winHeight, x, y) + ) self._main_window.resizable(False, False) icon_base64 = base64.b64decode(icon32) @@ -80,16 +84,19 @@ def init_tab(self): self.notebook_disable() def notebook_disable(self): - self._tabCtrl.state(['disabled']) + self._tabCtrl.state(["disabled"]) def notebook_enable(self): - self._tabCtrl.state(['!disabled']) + self._tabCtrl.state(["!disabled"]) def init_programmer(self): self._programmer_frame = frame_programmer(self._main_window) self._programmer_frame.version_combobox.bind( - "<>", self.on_select_version) - self._programmer_frame.online_fw_button["command"] = self._programmer_frame.online_fw_button_hidden + "<>", self.on_select_version + ) + self._programmer_frame.online_fw_button["command"] = ( + self._programmer_frame.online_fw_button_hidden + ) self._programmer_frame.local_fw_button["command"] = self.on_load_local_firmware self._programmer_frame.update_button["command"] = self.on_update @@ -97,7 +104,8 @@ def init_vtx_frame(self): self._vtx_frame = frame_vtx(self._tabCtrl) for i in range(0, len(list(my_parse.vtx_info.keys()))): self._vtx_frame.radio_button[i].bind( - "", self.on_select_vtx_target) + "", self.on_select_vtx_target + ) def init_monitor_frame(self): self._monitor_frame = frame_monitor(self._tabCtrl) @@ -117,7 +125,8 @@ def on_select_vtx_target(self): try: version_list = list(my_parse.vtx_info[selected_target].keys())[1:] self._programmer_frame.version_combobox_update_values(version_list) - except: + except Exception as e: + logger.debug(f"on_select_vtx_target exception: {e}") pass self._programmer_frame.version_combobox_set_default() self._programmer_frame.version_combobox_enable() @@ -136,34 +145,42 @@ def on_select_version(self, event): if self.current_selected_tab() == 0: self._programmer_frame.update_button_enable() - self._programmer_frame.url = my_parse.vtx_info[self._vtx_frame.vtx_target.get( - )][self._programmer_frame.version_combobox.get()] + self._programmer_frame.url = my_parse.vtx_info[ + self._vtx_frame.vtx_target.get() + ][self._programmer_frame.version_combobox.get()] self._programmer_frame.online_fw_button_set_str( - self._programmer_frame.version_combobox.get()) + self._programmer_frame.version_combobox.get() + ) elif self.current_selected_tab() == 1: self._programmer_frame.update_button_enable() - self._programmer_frame.url = my_parse.monitor_info[self._programmer_frame.version_combobox.get( - )] + self._programmer_frame.url = my_parse.monitor_info[ + self._programmer_frame.version_combobox.get() + ] self._programmer_frame.online_fw_button_set_str( - self._programmer_frame.version_combobox.get()) + self._programmer_frame.version_combobox.get() + ) elif self.current_selected_tab() == 2: self._programmer_frame.update_button_enable() - self._programmer_frame.url = my_parse.event_vrx_info[self._programmer_frame.version_combobox.get( - )] + self._programmer_frame.url = my_parse.event_vrx_info[ + self._programmer_frame.version_combobox.get() + ] self._programmer_frame.online_fw_button_set_str( - self._programmer_frame.version_combobox.get()) + 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.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()) + self._programmer_frame.version_combobox.get() + ) def on_load_local_firmware(self): self._programmer_frame.online_fw_button_show() self._programmer_frame.select_local_file() - if self._programmer_frame.local_file_path == '': + if self._programmer_frame.local_file_path == "": return if self.current_selected_tab() == 0: @@ -187,7 +204,7 @@ def on_load_local_firmware(self): my_ch341.fw_path = self._programmer_frame.local_file_path def on_update(self): - if self.current_selected_tab() == 0: # vtx + 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 @@ -205,7 +222,8 @@ def on_update(self): self._programmer_frame.online_fw_button_disable() self._statusbar_frame.status_label_set_text( - "Connecting VTX ...", "SystemButtonFace") + "Connecting VTX ...", "SystemButtonFace" + ) self._statusbar_frame.progress_bar_set_value(0) else: my_ch341.status = ch341_status.IDLE.value @@ -219,13 +237,12 @@ def on_update(self): 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._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() == 1: # monitor + elif self.current_selected_tab() == 1: # monitor if self._programmer_frame.is_cancel == 0: self.is_update_monitor = 1 my_ch341.monitor_connected = 0 @@ -243,7 +260,8 @@ def on_update(self): self._programmer_frame.online_fw_button_disable() self._statusbar_frame.status_label_set_text( - "Connecting Monitor ...", "SystemButtonFace") + "Connecting Monitor ...", "SystemButtonFace" + ) self._statusbar_frame.progress_bar_set_value(0) else: print("cancel Monitor programmer") @@ -257,18 +275,16 @@ def on_update(self): self._monitor_frame.setting_disable() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") 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._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() == 2: # event vrx + 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 @@ -281,7 +297,8 @@ def on_update(self): self._programmer_frame.local_fw_button_disable() self._programmer_frame.online_fw_button_disable() self._statusbar_frame.status_label_set_text( - "Connecting Event VRX ...", "SystemButtonFace") + "Connecting Event VRX ...", "SystemButtonFace" + ) self._statusbar_frame.progress_bar_set_value(0) else: my_download.to_stop = 1 @@ -289,18 +306,16 @@ def on_update(self): self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") 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._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 + 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 @@ -313,19 +328,18 @@ def on_update(self): self._programmer_frame.local_fw_button_disable() self._programmer_frame.online_fw_button_disable() self._statusbar_frame.status_label_set_text( - "Connecting Radio ...", "SystemButtonFace") + "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_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._programmer_frame.online_fw_button_enable(self.network_error) self._statusbar_frame.label_hidden() self._statusbar_frame.progress_bar_set_value(0) @@ -355,8 +369,7 @@ def on_tab_changed(self, event): self._programmer_frame.version_combobox_set_default() self._programmer_frame.version_combobox_enable() self._programmer_frame.local_fw_button_enable() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") self._programmer_frame.update_button_disable() self._programmer_frame.online_fw_button_show() self.monitor_is_alive = 0 @@ -402,19 +415,19 @@ def create_downloading_firmware_window(self): y = int((screenHeight - 50) / 2) self.downloading_window = tk.Toplevel() - self.downloading_window.geometry("%sx%s+%s+%s" % - (300, 50, x, y)) + self.downloading_window.geometry("%sx%s+%s+%s" % (300, 50, x, y)) self.downloading_window.resizable(False, False) self.downloading_window.title("Downloading") - self.downloading_label = tk.Label(self.downloading_window, - text="Downloading firmware list from github ...") + self.downloading_label = tk.Label( + self.downloading_window, text="Downloading firmware list from github ..." + ) self.downloading_label.pack(pady=10) self.downloading_window_status = 1 self.downloading_window.overrideredirect(True) - self._main_window.attributes('-disable', True) + self._main_window.attributes("-disable", True) def set_downloading_label(self, str): self.downloading_label.config(text=str) @@ -425,7 +438,7 @@ def destroy_downloading_firmware_window(self): self._main_window.focus_force() def refresh(self): - ''' + """ 1. update vtx - press update button - connect vtx @@ -439,14 +452,13 @@ def refresh(self): - - 3. update event vrx - ''' + """ # init 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_tragets_image( - len(list(my_parse.vtx_info.keys()))) + 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() @@ -460,8 +472,11 @@ def refresh(self): self.destroy_downloading_firmware_window() self._vtx_frame.create_radio_button_list( - list(my_parse.vtx_info.keys()), self.on_select_vtx_target, my_parse.vtx_target_image) - self._main_window.attributes('-disable', False) + list(my_parse.vtx_info.keys()), + self.on_select_vtx_target, + my_parse.vtx_target_image, + ) + self._main_window.attributes("-disable", False) # vtx if self.current_selected_tab() == 0: @@ -485,8 +500,7 @@ def refresh(self): self._vtx_frame.radio_button_enable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + 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.update_button_set_text_update("VTX") @@ -495,7 +509,8 @@ def refresh(self): self._statusbar_frame.progress_bar_set_value(0) self._statusbar_frame.status_label_set_text( - "Firmware update failed. Network error.", "red") + "Firmware update failed. Network error.", "red" + ) # update if my_ch341.status == ch341_status.VTX_CONNECTED.value: # vtx is connected @@ -503,9 +518,12 @@ def refresh(self): 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_VTX_FW.value # download url + my_download.status = ( + download_status.DOWNLOAD_VTX_FW.value + ) # download url self._statusbar_frame.status_label_set_text( - "Downloading Firmware ...", "SystemButtonFace") + "Downloading Firmware ...", "SystemButtonFace" + ) else: selected_target = self._vtx_frame.vtx_target.get() my_ch341.target_id = my_parse.vtx_info[selected_target]["id"] @@ -515,14 +533,19 @@ def refresh(self): self._programmer_frame.update_button_set_text_update("VTX") self._programmer_frame.update_button_disable() - elif my_ch341.status == ch341_status.VTX_UPDATE.value: # refresh progress bar - value = (my_ch341.written_len / - my_ch341.to_write_len * 100) % 101 + elif ( + my_ch341.status == ch341_status.VTX_UPDATE.value + ): # refresh progress bar + 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.VTX_UPDATEDONE.value: # vtx update done + elif ( + my_ch341.status == ch341_status.VTX_UPDATEDONE.value + ): # vtx update done self._statusbar_frame.progress_bar_set_value(100) self._statusbar_frame.status_label_set_text( - "Firmware updated. Connect another VTX(the same type) to update, or click cancel to finish.", "#06b025") + "Firmware updated. Connect another VTX(the same type) to update, or click cancel to finish.", + "#06b025", + ) my_ch341.status = ch341_status.VTX_RECONNECT.value self._programmer_frame.update_button_set_text_cancel() self._programmer_frame.update_button_enable() @@ -551,10 +574,14 @@ def refresh(self): self._statusbar_frame.progress_bar_set_value(100) self._programmer_frame.deselect() """ - elif my_ch341.status == ch341_status.VTX_UPDATE_FAILED.value: # vtx update failed + elif ( + my_ch341.status == ch341_status.VTX_UPDATE_FAILED.value + ): # vtx update failed self._statusbar_frame.progress_bar_set_value(0) self._statusbar_frame.status_label_set_text( - "Firmware updated failed. Disconnect/reconnect the vtx to try again, or click cancel to finish.", "red") + "Firmware updated failed. Disconnect/reconnect the vtx to try again, or click cancel to finish.", + "red", + ) my_ch341.status = ch341_status.VTX_RECONNECT.value self._programmer_frame.update_button_set_text_cancel() self._programmer_frame.update_button_enable() @@ -573,8 +600,7 @@ def refresh(self): self._vtx_frame.radio_button_enable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + 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() @@ -584,7 +610,8 @@ def refresh(self): self._statusbar_frame.progress_bar_set_value(0) self._statusbar_frame.status_label_set_text( - "Firmware update failed. Firmware error", "red") + "Firmware update failed. Firmware error", "red" + ) # ------------ Monitor --------------- if self.current_selected_tab() == 1: @@ -596,8 +623,7 @@ def refresh(self): my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) self._statusbar_frame.label_hidden() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") self._programmer_frame.update_button_disable() my_ch341.status = ch341_status.MONITOR_UPDATE.value elif my_download.status == download_status.DOWNLOAD_MONITOR_FW_FAILED.value: @@ -609,65 +635,73 @@ def refresh(self): self.notebook_enable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + 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( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") # 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") + "Firmware update failed. Network error.", "red" + ) # update if self.is_update_monitor == 1: - if my_ch341.status == ch341_status.IDLE.value and my_ch341.monitor_connected == 0: # to connect Monitor + if ( + my_ch341.status == ch341_status.IDLE.value + and my_ch341.monitor_connected == 0 + ): # to connect Monitor my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value - elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value and my_ch341.monitor_connected == 1: # Monitor is connected + elif ( + my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value + and my_ch341.monitor_connected == 1 + ): # Monitor 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_MONITOR_FW.value # download url + my_download.status = ( + download_status.DOWNLOAD_MONITOR_FW.value + ) # download url self._statusbar_frame.status_label_set_text( - "Downloading Firmware ...", "SystemButtonFace") + "Downloading Firmware ...", "SystemButtonFace" + ) else: my_ch341.written_len = 0 - my_ch341.to_write_len = os.path.getsize( - my_ch341.fw_path) + my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) self._statusbar_frame.label_hidden() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") self._programmer_frame.update_button_disable() my_ch341.status = ch341_status.MONITOR_UPDATE.value - elif my_ch341.status == ch341_status.MONITOR_UPDATE.value: # refresh progress bar - value = (my_ch341.written_len / - my_ch341.to_write_len * 100) % 101 + elif ( + my_ch341.status == ch341_status.MONITOR_UPDATE.value + ): # refresh progress bar + 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.MONITOR_UPDATEDONE.value: # Monitor update done + elif ( + my_ch341.status == ch341_status.MONITOR_UPDATEDONE.value + ): # Monitor update done self.is_update_monitor = 0 my_ch341.monitor_connected = 0 my_ch341.status = ch341_status.MONITOR_CHECK_ALIVE.value self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") 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._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.", "#06b025") + "Firmware updated.", "#06b025" + ) elif my_ch341.status == ch341_status.MONITOR_FW_ERROR.value: # fw error self.is_update_monitor = 0 @@ -676,46 +710,56 @@ def refresh(self): self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Monitor") + self._programmer_frame.update_button_set_text_update("Monitor") 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._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 udpate failed. Firmware error.", "red") + "Firmware udpate failed. Firmware error.", "red" + ) elif my_ch341.status == ch341_status.MONITOR_CHECK_ALIVE.value: - if self.monitor_is_alive == 0 and my_ch341.monitor_connected == 1: # to connect monitor + if ( + self.monitor_is_alive == 0 and my_ch341.monitor_connected == 1 + ): # to connect monitor self._monitor_frame.setting_enable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + self._programmer_frame.online_fw_button_enable(self.network_error) self._programmer_frame.local_fw_button_enable() self._programmer_frame.local_fw_button_set_str_default() # self._programmer_frame.update_button_disable() - self._monitor_frame.write_setting(global_var.brightness, global_var.contrast, global_var.saturation, - global_var.backlight, global_var.cell_count, global_var.warning_cell_voltage, global_var.osd) + self._monitor_frame.write_setting( + global_var.brightness, + global_var.contrast, + global_var.saturation, + global_var.backlight, + global_var.cell_count, + global_var.warning_cell_voltage, + global_var.osd, + ) self.monitor_is_alive = 1 - elif self.monitor_is_alive == 1 and my_ch341.monitor_connected == 0: # to disconnect monitor + elif ( + self.monitor_is_alive == 1 and my_ch341.monitor_connected == 0 + ): # to disconnect monitor self.monitor_is_alive = 0 self._monitor_frame.reset_scale() self._monitor_frame.setting_disable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + self._programmer_frame.online_fw_button_enable(self.network_error) self._programmer_frame.local_fw_button_enable() self._programmer_frame.local_fw_button_set_str_default() # self._programmer_frame.update_button_disable() - elif self.monitor_is_alive == 1 and my_ch341.monitor_connected == 1: # monitor is alive + elif ( + self.monitor_is_alive == 1 and my_ch341.monitor_connected == 1 + ): # monitor is alive # settting self._monitor_frame.usb_heart() @@ -729,87 +773,92 @@ def refresh(self): my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) self._statusbar_frame.label_hidden() - self._programmer_frame.update_button_set_text_update( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") self._programmer_frame.update_button_disable() my_ch341.status = ch341_status.EVENT_VRX_UPDATE.value - elif my_download.status == download_status.DOWNLOAD_EVENT_VRX_FW_FAILED.value: + elif ( + my_download.status == download_status.DOWNLOAD_EVENT_VRX_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.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( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") # 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") + "Firmware update failed. Network error", "red" + ) # update - if my_ch341.status == ch341_status.EVENT_VRX_CONNECTED.value: # event_vrx is connected + if ( + my_ch341.status == ch341_status.EVENT_VRX_CONNECTED.value + ): # event_vrx 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_EVENT_VRX_FW.value # download url + my_download.status = ( + download_status.DOWNLOAD_EVENT_VRX_FW.value + ) # download url self._statusbar_frame.status_label_set_text( - "Downloading Firmware ...", "SystemButtonFace") + "Downloading Firmware ...", "SystemButtonFace" + ) else: my_ch341.written_len = 0 my_ch341.to_write_len = os.path.getsize(my_ch341.fw_path) - self._programmer_frame.update_button_set_text_update( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") self._programmer_frame.update_button_disable() self._statusbar_frame.label_hidden() my_ch341.status = ch341_status.EVENT_VRX_UPDATE.value - elif my_ch341.status == ch341_status.EVENT_VRX_UPDATE.value: # event_vrx refresh progress bar - value = (my_ch341.written_len / - my_ch341.to_write_len * 100) % 101 + elif ( + my_ch341.status == ch341_status.EVENT_VRX_UPDATE.value + ): # event_vrx refresh progress bar + 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.EVENT_VRX_UPDATEDONE.value: # event_vrx update done + elif ( + my_ch341.status == ch341_status.EVENT_VRX_UPDATEDONE.value + ): # event_vrx update done my_ch341.status = ch341_status.IDLE.value self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") 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._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.", "#06b025") + "Firmware updated.", "#06b025" + ) elif my_ch341.status == ch341_status.EVENT_VRX_FW_ERROR.value: my_ch341.status = ch341_status.IDLE.value self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Event VRX") + self._programmer_frame.update_button_set_text_update("Event VRX") 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._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") + "Firmware update failed. Firmware error", "red" + ) # --------------------- radio ------------------------------- if self.current_selected_tab() == 3: @@ -821,8 +870,7 @@ def refresh(self): 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_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: @@ -832,52 +880,59 @@ def refresh(self): self.notebook_enable() self._programmer_frame.version_combobox_enable() - self._programmer_frame.online_fw_button_enable( - self.network_error) + 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_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") + "Firmware update failed. Network error", "red" + ) # update - if my_ch341.status == ch341_status.RADIO_CONNECTED.value: # radio is connected + 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 + my_download.status = ( + download_status.DOWNLOAD_RADIO_FW.value + ) # download url self._statusbar_frame.status_label_set_text( - "Downloading Firmware ...", "SystemButtonFace") + "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_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 + + 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 + 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: @@ -885,69 +940,66 @@ def refresh(self): self.notebook_enable() - self._programmer_frame.update_button_set_text_update( - "Radio") + 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._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") + "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_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._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") + "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_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._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 + "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_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._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") - + "Firmware updated, repower Radio now", "#06b025" + ) self._main_window.after(100, self.refresh) @@ -969,8 +1021,7 @@ def ui_thread_proc(): my_gui.refresh() if my_gui.downloading_window_status == 0: - my_gui._main_window.after( - 100, my_gui.create_downloading_firmware_window) + my_gui._main_window.after(100, my_gui.create_downloading_firmware_window) root.protocol("WM_DELETE_WINDOW", on_closing) diff --git a/parse_file.py b/parse_file.py index 172a92e..d29ec85 100644 --- a/parse_file.py +++ b/parse_file.py @@ -1,5 +1,8 @@ import json from PIL import Image +import logging + +logger = logging.getLogger(__name__) class parse: @@ -27,18 +30,18 @@ def parse_vtx_common(self): words = lines[i].split() for j in range(len(words)): if words[j] == "defined": - name = words[j+1].lower() + name = words[j + 1].lower() if words[j] == "VTX_ID": - if words[j+1] != "0x00": - word = words[j+1].strip("0x") + if words[j + 1] != "0x00": + word = words[j + 1].strip("0x") self.vtx_info[name] = {"id": int(word, 16)} if lines[i] == "/* define VTX ID start */\n": start = 1 elif lines[i] == "/* define VTX ID end */\n": start = 0 return 1 - except: - print("something error") + except Exception as e: + logger.error(f"Error parsing VTX common: {e}") return 0 def parse_vtx_releases(self): @@ -49,22 +52,22 @@ def parse_vtx_releases(self): for i in range(len(data)): link_list = [] name_list = [] - for j in range(len(data[i]['assets'])): - link_list.append(data[i]['assets'][j] - ['browser_download_url']) + for j in range(len(data[i]["assets"])): + link_list.append(data[i]["assets"][j]["browser_download_url"]) - name_start = link_list[j].rfind('/') + len('/') + name_start = link_list[j].rfind("/") + len("/") name_end = link_list[j].index(".zip", name_start) name_list.append(link_list[j][name_start:name_end]) name = link_list[j][name_start:name_end] if name == "hdzero_freestyle": name = "hdzero_freestyle_v1" - version = data[i]['tag_name'] - url = data[i]['assets'][j]['browser_download_url'] + version = data[i]["tag_name"] + url = data[i]["assets"][j]["browser_download_url"] self.vtx_info[name][version] = url return 1 - except: + except Exception as e: + logger.error(f"Error parsing VTX releases: {e}") return 0 def parse_event_vrx_releases(self): @@ -74,15 +77,15 @@ def parse_event_vrx_releases(self): 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']) + 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'] + version = data[i]["tag_name"] + url = data[i]["assets"][j]["browser_download_url"] self.event_vrx_info[version] = url return 1 - except: + except Exception as e: + logger.error(f"Error parsing event VRX releases: {e}") return 0 def parse_monitor_releases(self): @@ -92,28 +95,31 @@ def parse_monitor_releases(self): 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']) + 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'] + version = data[i]["tag_name"] + url = data[i]["assets"][j]["browser_download_url"] self.monitor_info[version] = url return 1 - except: + except Exception as e: + logger.error(f"Error parsing monitor releases: {e}") return 0 def parse_vtx_tragets_image(self, num): try: ori_img = Image.open(self.vtx_tragets_image_path) - except: + except Exception as e: + logger.error(f"Error opening target image: {e}") return 0 for i in range(0, num): try: self.vtx_target_image.append( - ori_img.crop((0, i*100, 299, i*100+99))) - except: + ori_img.crop((0, i * 100, 299, i * 100 + 99)) + ) + except Exception as e: + logger.error(f"Error cropping target image: {e}") return 0 return 1 @@ -125,15 +131,15 @@ def parse_radio_releases(self): 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']) + 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'] + version = data[i]["tag_name"] + url = data[i]["assets"][j]["browser_download_url"] self.radio_info[version] = url return 1 - except: + except Exception as e: + logger.error(f"Error parsing radio releases: {e}") return 0 diff --git a/program_radio.py b/program_radio.py index b6713b4..371d212 100644 --- a/program_radio.py +++ b/program_radio.py @@ -2,8 +2,11 @@ import time import esptool import sys +import logging from xmodem import XMODEM +logger = logging.getLogger(__name__) + def flash_esp_api( port, @@ -22,9 +25,12 @@ def flash_esp_api( # 等价于命令行参数 argv = [ - "--chip", chip, - "--port", port, - "--baud", str(baud), + "--chip", + chip, + "--port", + port, + "--baud", + str(baud), "write-flash", ] @@ -37,24 +43,27 @@ def flash_esp_api( try: sys.argv = ["esptool.py"] + argv esptool.main() - except: + logger.info("esptool flashed successfully") + except Exception as e: + logger.error(f"esptool exception: {e}") return False finally: sys.argv = old_argv - + return True class radio_class(object): def __init__(self): - self.ser = '' + self.ser = "" self.status = 0 - self.com = '' + self.com = "" def find_word_in_string(self, s, word): try: words = s.split() - except: + except Exception as e: + logger.error(f"find_word_in_string split failed: {e}") return 0 for w in words: @@ -63,14 +72,14 @@ def find_word_in_string(self, s, word): return 0 def serial_tx(self, str): - self.ser.write(str.encode('utf-8')) + self.ser.write(str.encode("utf-8")) def serial_rx(self, sleep_sec): - data_received = b'' + 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') + return data_received.decode("utf-8") def getc(self, size, timeout=1): return self.ser.read(size) @@ -81,20 +90,23 @@ def putc(self, data, timeout=1): 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')): + 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) + logger.info(f"Opened STM32 port {self.com}") return 1 - except: + except Exception as e: + logger.error(f"Failed to open STM32 port {self.com}: {e}") return 0 + logger.debug("STM32 port not found") 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')): + 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) @@ -103,7 +115,8 @@ def search_elrs_port(self): def unzip_radio_firmware(self, file_path, dest_folder): import zipfile - with zipfile.ZipFile(file_path, 'r') as zip_ref: + + with zipfile.ZipFile(file_path, "r") as zip_ref: zip_ref.extractall(dest_folder) def radio_is_active(self): @@ -111,7 +124,7 @@ def radio_is_active(self): return 0 cmd = "ATPING\r\n" - self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode("utf-8")) rx = self.serial_rx(1) self.ser.close() @@ -126,9 +139,9 @@ def program_stm32(self): 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')) + 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) @@ -138,35 +151,35 @@ def program_stm32(self): 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')) + 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: + logger.info("Sent stm32 via XMODEM") + except Exception as e: + logger.error(f"XMODEM send failed: {e}") 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')) + 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')) + self.ser.write(cmd.encode("utf-8")) time.sleep(0.2) self.ser.close() @@ -182,7 +195,7 @@ def program_elrs_tx(self): flash_args=[ (0x1000, "resource/elrs_tx/bootloader.bin"), (0x8000, "resource/elrs_tx/partitions.bin"), - (0xe000, "resource/elrs_tx/boot_app0.bin"), + (0xE000, "resource/elrs_tx/boot_app0.bin"), (0x10000, "resource/elrs_tx/firmware.bin"), ], ) @@ -192,7 +205,7 @@ def program_elrs_backpack(self): return False cmd = "ATPGBP\r\n" - self.ser.write(cmd.encode('utf-8')) + self.ser.write(cmd.encode("utf-8")) time.sleep(0.2) self.ser.close() @@ -208,7 +221,7 @@ def program_elrs_backpack(self): flash_args=[ (0x0000, "resource/elrs_backpack/bootloader.bin"), (0x8000, "resource/elrs_backpack/partitions.bin"), - (0xe000, "resource/elrs_backpack/boot_app0.bin"), + (0xE000, "resource/elrs_backpack/boot_app0.bin"), (0x10000, "resource/elrs_backpack/firmware.bin"), ], ) diff --git a/resource/driver/CH341M64.SYS b/resource/driver/CH341M64.SYS index 815603c..20b9ce4 100644 Binary files a/resource/driver/CH341M64.SYS and b/resource/driver/CH341M64.SYS differ diff --git a/resource/driver/DRVSETUP64/DRVSETUP64.exe b/resource/driver/DRVSETUP64/DRVSETUP64.exe index db0911f..cf7bdd6 100644 Binary files a/resource/driver/DRVSETUP64/DRVSETUP64.exe and b/resource/driver/DRVSETUP64/DRVSETUP64.exe differ diff --git a/resource/driver/SETUP.EXE b/resource/driver/SETUP.EXE index 839c8ae..725b7de 100644 Binary files a/resource/driver/SETUP.EXE and b/resource/driver/SETUP.EXE differ diff --git a/resource/driver/WIN 1X/CH341M64.SYS b/resource/driver/WIN 1X/CH341M64.SYS index c785adf..20b9ce4 100644 Binary files a/resource/driver/WIN 1X/CH341M64.SYS and b/resource/driver/WIN 1X/CH341M64.SYS differ