Compare commits
2 Commits
fe69dc6f19
...
19e8ca30c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19e8ca30c3 | ||
|
|
de7041c1f5 |
9
flash_tool/.claude/settings.local.json
Normal file
9
flash_tool/.claude/settings.local.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(python:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,14 @@ block_cipher = None
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Hardcode the venv esptool path since PyInstaller might run from different env
|
# Find esptool installation dynamically
|
||||||
esptool_dir = os.path.join(os.path.dirname(SPECPATH), 'venv', 'Lib', 'site-packages', 'esptool')
|
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
|
# Build datas list - include all esptool data files
|
||||||
datas_list = [
|
datas_list = [
|
||||||
@@ -16,8 +22,10 @@ datas_list = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# Add esptool targets directory (includes stub_flasher JSON files)
|
# Add esptool targets directory (includes stub_flasher JSON files)
|
||||||
if os.path.exists(os.path.join(esptool_dir, 'targets')):
|
if esptool_dir and os.path.exists(os.path.join(esptool_dir, 'targets')):
|
||||||
datas_list.append((os.path.join(esptool_dir, 'targets'), 'esptool/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(
|
a = Analysis(
|
||||||
['web_flasher.py'],
|
['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"""
|
"""Background worker for flashing firmware"""
|
||||||
global flash_status
|
global flash_status
|
||||||
import re
|
import re
|
||||||
|
import io
|
||||||
|
from contextlib import redirect_stdout, redirect_stderr
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Define file paths
|
# 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
|
flash_status['running'] = False
|
||||||
return
|
return
|
||||||
|
|
||||||
# Build esptool command
|
# Build esptool command arguments
|
||||||
cmd = [
|
esptool_args = [
|
||||||
sys.executable, "-m", "esptool",
|
|
||||||
"--chip", chip,
|
"--chip", chip,
|
||||||
"--port", port,
|
"--port", port,
|
||||||
"--baud", baud,
|
"--baud", baud,
|
||||||
@@ -212,48 +213,88 @@ def flash_worker(temp_dir, port, chip, baud, flash_mode, flash_freq, flash_size)
|
|||||||
"0x8000", partition
|
"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("")
|
flash_status['output'].append("")
|
||||||
|
|
||||||
# Run esptool
|
# Import and run esptool directly (works in both script and EXE)
|
||||||
process = subprocess.Popen(
|
import esptool
|
||||||
cmd,
|
|
||||||
stdout=subprocess.PIPE,
|
# Fix esptool data path for PyInstaller
|
||||||
stderr=subprocess.STDOUT,
|
if getattr(sys, 'frozen', False):
|
||||||
text=True,
|
# Running as compiled executable
|
||||||
bufsize=1,
|
bundle_dir = sys._MEIPASS
|
||||||
universal_newlines=True
|
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
|
# Regex to parse progress lines
|
||||||
# Example: "Writing at 0x0011325c... (85 %)"
|
# Example: "Writing at 0x0011325c... (85 %)"
|
||||||
# or "Writing at 0x0011325c [========================> ] 85.3% 622592/730050 bytes..."
|
# or "Writing at 0x0011325c [========================> ] 85.3% 622592/730050 bytes..."
|
||||||
progress_pattern = re.compile(r'(\d+(?:\.\d+)?)\s*%')
|
progress_pattern = re.compile(r'(\d+(?:\.\d+)?)\s*%')
|
||||||
|
|
||||||
# Stream output
|
# Save original stdout/stderr
|
||||||
for line in process.stdout:
|
original_stdout = sys.stdout
|
||||||
line_stripped = line.rstrip()
|
original_stderr = sys.stderr
|
||||||
flash_status['output'].append(line_stripped)
|
|
||||||
|
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
|
# Try to extract progress percentage
|
||||||
match = progress_pattern.search(line_stripped)
|
match = progress_pattern.search(line)
|
||||||
if match:
|
if match:
|
||||||
try:
|
try:
|
||||||
percent = float(match.group(1))
|
percent = float(match.group(1))
|
||||||
flash_status['progress'] = percent
|
flash_status['progress'] = percent
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
return len(text)
|
||||||
|
|
||||||
process.wait()
|
def flush(self):
|
||||||
|
pass
|
||||||
|
|
||||||
if process.returncode == 0:
|
capture = OutputCapture()
|
||||||
|
|
||||||
|
# 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_status['output'].append("✓ Flash completed successfully!")
|
flash_status['output'].append("✓ Flash completed successfully!")
|
||||||
flash_status['success'] = True
|
flash_status['success'] = True
|
||||||
flash_status['progress'] = 100
|
flash_status['progress'] = 100
|
||||||
else:
|
else:
|
||||||
flash_status['output'].append("")
|
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
|
flash_status['success'] = False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -274,6 +315,12 @@ def open_browser(port):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run the web server"""
|
"""Run the web server"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Disable Flask request logging to avoid colorama issues with stdout redirection
|
||||||
|
log = logging.getLogger('werkzeug')
|
||||||
|
log.setLevel(logging.ERROR)
|
||||||
|
|
||||||
port = get_free_port()
|
port = get_free_port()
|
||||||
print(f"Starting ESP32 Flasher on port {port}...")
|
print(f"Starting ESP32 Flasher on port {port}...")
|
||||||
print(f"Open your browser to: http://127.0.0.1:{port}")
|
print(f"Open your browser to: http://127.0.0.1:{port}")
|
||||||
@@ -286,7 +333,7 @@ def main():
|
|||||||
|
|
||||||
# Run Flask server with SocketIO
|
# Run Flask server with SocketIO
|
||||||
try:
|
try:
|
||||||
socketio.run(app, host='127.0.0.1', port=port, debug=False, allow_unsafe_werkzeug=True)
|
socketio.run(app, host='127.0.0.1', port=port, debug=False, allow_unsafe_werkzeug=True, log_output=False)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('\nShutting down...')
|
print('\nShutting down...')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
|
|||||||
Reference in New Issue
Block a user