From de7041c1f5a722f3cecd4bd6ab610586b4854948 Mon Sep 17 00:00:00 2001 From: Brent Perteet Date: Sat, 25 Oct 2025 11:52:11 -0500 Subject: [PATCH] EXE working but getting logging error --- flash_tool/.claude/settings.local.json | 9 +++ flash_tool/ESP32_WebFlasher_Windows.spec | 16 +++- flash_tool/web_flasher.py | 95 +++++++++++++++++------- 3 files changed, 89 insertions(+), 31 deletions(-) create mode 100644 flash_tool/.claude/settings.local.json diff --git a/flash_tool/.claude/settings.local.json b/flash_tool/.claude/settings.local.json new file mode 100644 index 0000000..9e2c6c8 --- /dev/null +++ b/flash_tool/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(python:*)" + ], + "deny": [], + "ask": [] + } +} \ No newline at end of file diff --git a/flash_tool/ESP32_WebFlasher_Windows.spec b/flash_tool/ESP32_WebFlasher_Windows.spec index b1f027a..765498f 100644 --- a/flash_tool/ESP32_WebFlasher_Windows.spec +++ b/flash_tool/ESP32_WebFlasher_Windows.spec @@ -6,8 +6,14 @@ block_cipher = None import os import sys -# Hardcode the venv esptool path since PyInstaller might run from different env -esptool_dir = os.path.join(os.path.dirname(SPECPATH), 'venv', 'Lib', 'site-packages', 'esptool') +# Find esptool installation dynamically +try: + import esptool + esptool_dir = os.path.dirname(esptool.__file__) + print(f"Found esptool at: {esptool_dir}") +except ImportError: + print("Warning: esptool not found, stub files will not be included") + esptool_dir = None # Build datas list - include all esptool data files datas_list = [ @@ -16,8 +22,10 @@ datas_list = [ ] # Add esptool targets directory (includes stub_flasher JSON files) -if os.path.exists(os.path.join(esptool_dir, 'targets')): - datas_list.append((os.path.join(esptool_dir, 'targets'), 'esptool/targets')) +if esptool_dir and os.path.exists(os.path.join(esptool_dir, 'targets')): + targets_dir = os.path.join(esptool_dir, 'targets') + datas_list.append((targets_dir, 'esptool/targets')) + print(f"Including esptool targets from: {targets_dir}") a = Analysis( ['web_flasher.py'], diff --git a/flash_tool/web_flasher.py b/flash_tool/web_flasher.py index 692e909..0e48289 100644 --- a/flash_tool/web_flasher.py +++ b/flash_tool/web_flasher.py @@ -177,6 +177,8 @@ def flash_worker(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size) """Background worker for flashing firmware""" global flash_status import re + import io + from contextlib import redirect_stdout, redirect_stderr try: # Define file paths @@ -194,9 +196,8 @@ def flash_worker(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size) flash_status['running'] = False return - # Build esptool command - cmd = [ - sys.executable, "-m", "esptool", + # Build esptool command arguments + esptool_args = [ "--chip", chip, "--port", port, "--baud", baud, @@ -212,48 +213,88 @@ def flash_worker(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size) "0x8000", partition ] - flash_status['output'].append(f"Running: {' '.join(cmd)}") + flash_status['output'].append(f"Running esptool with args: {' '.join(esptool_args)}") flash_status['output'].append("") - # Run esptool - process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, - universal_newlines=True - ) + # Import and run esptool directly (works in both script and EXE) + import esptool + + # Fix esptool data path for PyInstaller + if getattr(sys, 'frozen', False): + # Running as compiled executable + bundle_dir = sys._MEIPASS + esptool_targets = os.path.join(bundle_dir, 'esptool', 'targets') + if os.path.exists(esptool_targets): + # Override esptool's resource path + esptool.RESOURCES_DIR = bundle_dir + + # Capture stdout/stderr + output_buffer = io.StringIO() + + # Backup original sys.argv and replace with our arguments + original_argv = sys.argv + sys.argv = ['esptool.py'] + esptool_args + + # Run esptool and capture output + return_code = 0 # Regex to parse progress lines # Example: "Writing at 0x0011325c... (85 %)" # or "Writing at 0x0011325c [========================> ] 85.3% 622592/730050 bytes..." progress_pattern = re.compile(r'(\d+(?:\.\d+)?)\s*%') - # Stream output - for line in process.stdout: - line_stripped = line.rstrip() - flash_status['output'].append(line_stripped) + # Save original stdout/stderr + original_stdout = sys.stdout + original_stderr = sys.stderr - # Try to extract progress percentage - match = progress_pattern.search(line_stripped) - if match: - try: - percent = float(match.group(1)) - flash_status['progress'] = percent - except ValueError: + try: + # Custom output class to capture and stream esptool output + class OutputCapture: + def __init__(self): + self.buffer = [] + + def write(self, text): + if text and text.strip(): + lines = text.rstrip().split('\n') + for line in lines: + flash_status['output'].append(line) + self.buffer.append(line) + + # Try to extract progress percentage + match = progress_pattern.search(line) + if match: + try: + percent = float(match.group(1)) + flash_status['progress'] = percent + except ValueError: + pass + return len(text) + + def flush(self): pass - process.wait() + capture = OutputCapture() - if process.returncode == 0: + # Redirect stdout/stderr to our capture object + with redirect_stdout(capture), redirect_stderr(capture): + esptool.main() + + except SystemExit as e: + return_code = e.code if e.code is not None else 0 + finally: + # Restore original sys.argv and stdout/stderr + sys.argv = original_argv + sys.stdout = original_stdout + sys.stderr = original_stderr + + if return_code == 0: flash_status['output'].append("") flash_status['output'].append("✓ Flash completed successfully!") flash_status['success'] = True flash_status['progress'] = 100 else: flash_status['output'].append("") - flash_status['output'].append(f"✗ Flash failed with return code {process.returncode}") + flash_status['output'].append(f"✗ Flash failed with return code {return_code}") flash_status['success'] = False except Exception as e: