Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/compile_all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ jobs:
fi
echo "Local bootloader found at $BOOTLOADER_PATH."

- name: Create Merged Binary
run: |
. ~/esp-idf/export.sh
MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin"
idf.py merge-bin -o "$MERGED_BIN_NAME" || {
echo "Warning: Failed to create merged binary, but build succeeded"
}
if [ -f "build/$MERGED_BIN_NAME" ]; then
echo "Merged binary created: $MERGED_BIN_NAME"
else
echo "Warning: Merged binary was not created"
fi

- name: Package Artifacts into ZIP
run: |
ARTIFACT_DIR="packaged_artifacts"
Expand All @@ -119,6 +132,13 @@ jobs:
echo "Failed to copy binaries"
exit 1
}
# Copy merged binary if it exists
MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin"
if [ -f "build/$MERGED_BIN_NAME" ]; then
cp "build/$MERGED_BIN_NAME" "$ARTIFACT_DIR/" || {
echo "Warning: Failed to copy merged binary"
}
fi
cd "$ARTIFACT_DIR"
zip "../$ZIP_FILE" ./* || {
echo "Failed to create ZIP"
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/compile_prerelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,19 @@ jobs:
fi
echo "Local bootloader found at $BOOTLOADER_PATH."

- name: Create Merged Binary
run: |
. ~/esp-idf/export.sh
MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin"
idf.py merge-bin -o "$MERGED_BIN_NAME" || {
echo "Warning: Failed to create merged binary, but build succeeded"
}
if [ -f "build/$MERGED_BIN_NAME" ]; then
echo "Merged binary created: $MERGED_BIN_NAME"
else
echo "Warning: Merged binary was not created"
fi

- name: Package Artifacts into ZIP
run: |
ARTIFACT_DIR="packaged_artifacts_${{ matrix.target.name }}" # Unique dir per target
Expand All @@ -110,6 +123,13 @@ jobs:
cp build/bootloader/bootloader.bin "$ARTIFACT_DIR/bootloader.bin" || { echo "Failed to copy bootloader"; exit 1; }
# Copy app binary
cp build/*.bin "$ARTIFACT_DIR/firmware.bin" || { echo "Failed to copy app binary"; exit 1; }
# Copy merged binary if it exists
MERGED_BIN_NAME="${{ matrix.target.name }}-merged-gesp.bin"
if [ -f "build/$MERGED_BIN_NAME" ]; then
cp "build/$MERGED_BIN_NAME" "$ARTIFACT_DIR/" || {
echo "Warning: Failed to copy merged binary"
}
fi

echo "Contents of $ARTIFACT_DIR:"
ls -l "$ARTIFACT_DIR"
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/custom_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,32 @@ jobs:
idf.py clean
idf.py build

- name: Create Merged Binary
run: |
. ~/esp-idf/export.sh
MERGED_BIN_NAME="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}-merged-gesp.bin"
idf.py merge-bin -o "$MERGED_BIN_NAME" || {
echo "Warning: Failed to create merged binary, but build succeeded"
}
if [ -f "build/$MERGED_BIN_NAME" ]; then
echo "Merged binary created: $MERGED_BIN_NAME"
else
echo "Warning: Merged binary was not created"
fi

- name: Package Artifacts into ZIP
run: |
ZIP_FILE="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}.zip"
mkdir -p artifacts
cp build/partition_table/partition-table.bin artifacts/
cp build/*.bin artifacts/
# Copy merged binary if it exists
MERGED_BIN_NAME="ghost_esp_${{ github.event.inputs.board_type }}_${{ github.event.inputs.display_type }}-merged-gesp.bin"
if [ -f "build/$MERGED_BIN_NAME" ]; then
cp "build/$MERGED_BIN_NAME" artifacts/ || {
echo "Warning: Failed to copy merged binary"
}
fi
cd artifacts
zip "../$ZIP_FILE" ./*
cd ..
Expand Down
89 changes: 42 additions & 47 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]:
f"{home}/esp/esp-idf-v5.4.1",
f"{home}/esp/v5.5/esp-idf",
f"{home}/esp/v5.4.1/esp-idf",
f"{home}/esp/v5.5.1/esp-idf",
os.path.join(script_dir, "esp-idf-v5.5"),
os.path.join(script_dir, "esp-idf-v5.4.1"),
os.path.join(script_dir, "esp-idf")
Expand Down Expand Up @@ -228,6 +229,7 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]:
if os.path.exists(base_path):
try:
for item in os.listdir(base_path):
# Check for esp-idf-* pattern (e.g., esp-idf-v5.5.1)
if item.startswith("esp-idf-"):
full_path = os.path.join(base_path, item)
export_script = "export.bat" if os.name == 'nt' else "export.sh"
Expand All @@ -238,6 +240,20 @@ def find_esp_idf(auto_download: bool = False) -> Optional[str]:
return full_path
else:
print("skipping detected ESP-IDF path per user choice")
# Check for ~/esp/{version}/esp-idf pattern (e.g., ~/esp/v5.5.1/esp-idf)
elif os.path.isdir(os.path.join(base_path, item)):
# Look for esp-idf subdirectory inside version folders
version_dir = os.path.join(base_path, item)
esp_idf_path = os.path.join(version_dir, "esp-idf")
if os.path.isdir(esp_idf_path):
export_script = "export.bat" if os.name == 'nt' else "export.sh"
if os.path.exists(os.path.join(esp_idf_path, export_script)):
print(f"Found ESP-IDF at: {esp_idf_path}")
choice = input("Use this ESP-IDF path? [y/n]: ").strip().lower()
if choice not in ['n', 'no']:
return esp_idf_path
else:
print("skipping detected ESP-IDF path per user choice")
except (PermissionError, OSError):
continue

Expand Down Expand Up @@ -679,17 +695,41 @@ def build_target(target: Dict[str, str], env: Dict[str, str], cmd_prefix: str =
print(f"ERROR: Failed to copy build artifacts: {e}")
return False

# Merge binaries using idf.py merge-bin (uses flash_args for correct offsets)
# Note: Merged binary is copied to local_builds but NOT included in ZIP
print("Creating merged binary...")
merged_bin_name = target['name'] + "-merged-gesp.bin"
merged_bin_path = os.path.join("build", merged_bin_name)

# Use idf.py merge-bin which automatically reads offsets from flash_args
if not run_idf_command(['idf.py', 'merge-bin', '-o', merged_bin_name], env, cmd_prefix):
print("WARNING: Failed to create merged binary, but build succeeded")
# Don't fail the build if merge-bin fails, as the individual binaries are still available
else:
# Copy merged binary to artifact directory (but exclude from ZIP)
if os.path.exists(merged_bin_path):
try:
shutil.copy2(merged_bin_path, os.path.join(artifact_dir, merged_bin_name))
print(f"Merged binary created: {os.path.join(artifact_dir, merged_bin_name)} (not included in ZIP)")
except Exception as e:
print(f"WARNING: Failed to copy merged binary to artifact directory: {e}")
else:
print("WARNING: Merged binary was not created in build directory")

print(f"Contents of {artifact_dir}:")
for item in os.listdir(artifact_dir):
print(f" {item}")

# Create ZIP file
# Create ZIP file (excluding merged binary)
zip_path = os.path.join("local_builds", target['zip_name'])
print(f"Creating ZIP file: {zip_path}")

try:
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for item in os.listdir(artifact_dir):
# Exclude merged binary from ZIP
if item.endswith("-merged-gesp.bin"):
continue
item_path = os.path.join(artifact_dir, item)
zipf.write(item_path, item)

Expand All @@ -698,52 +738,7 @@ def build_target(target: Dict[str, str], env: Dict[str, str], cmd_prefix: str =
except Exception as e:
print(f"ERROR: Failed to create ZIP file: {e}")
return False

# --- Merge binaries using esptool.py merge_bin ---
merged_bin_path = os.path.join("local_builds", target['name'] + "-merged-gesp.bin")
bootloader_bin = os.path.join("build", "bootloader", "bootloader.bin")
partition_bin = os.path.join("build", "partition_table", "partition-table.bin")
firmware_bin = None
for bin_file in build_dir.glob("*.bin"):
if bin_file.name not in ["bootloader.bin", "partition-table.bin"]:
firmware_bin = str(bin_file)
break

if firmware_bin:
# Determine offsets (adjust if needed for your project)
boot_offset = "0x1000" if target['idf_target'] in ["esp32", "esp32s2"] else "0x0"
partition_offset = "0x8000"
firmware_offset = "0x10000"
import sys
merge_cmd = [
sys.executable, "-m", "esptool",
"--chip", target['idf_target'],
"merge-bin",
"-o", merged_bin_path,
"--flash-mode", "dio",
"--flash-freq", "40m",
"--flash-size", "4MB",
boot_offset, bootloader_bin,
partition_offset, partition_bin,
firmware_offset, firmware_bin
]
print(f"Merging binaries with: {' '.join(merge_cmd)}")
try:
result = subprocess.run(merge_cmd, check=True, capture_output=True, text=True)
print("esptool.py merge_bin output:")
print(result.stdout)
if os.path.exists(merged_bin_path):
print(f"Merged binary created: {merged_bin_path}")
else:
print("ERROR: Merged binary was not created.")
except subprocess.CalledProcessError as e:
print(f"ERROR: esptool.py merge_bin failed: {e}")
print(e.stdout)
print(e.stderr)
return False
else:
print("ERROR: Firmware binary not found for merging.")
return

return True

def main():
Expand Down
24 changes: 24 additions & 0 deletions scripts/control app/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -2289,6 +2289,30 @@ def run_idf_build(self):
if hasattr(self, 'flash_console'):
if process.returncode == 0:
self.flash_console.append("idf.py build finished successfully.")
# Create merged binary after successful build
self.flash_console.append("\nCreating merged binary...")
merge_cmd = ["idf.py", "merge-bin", "-o", "merged-binary.bin"]
merge_process = subprocess.Popen(
merge_cmd,
cwd=project_root,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True
)
for line in merge_process.stdout:
self.flash_console.append(line.rstrip())
self.flash_console.ensureCursorVisible()
QApplication.processEvents()
merge_process.wait()
if merge_process.returncode == 0:
merged_bin_path = os.path.join(project_root, "build", "merged-binary.bin")
if os.path.exists(merged_bin_path):
self.flash_console.append(f"Merged binary created: {merged_bin_path}")
else:
self.flash_console.append("Warning: Merged binary command succeeded but file not found.")
else:
self.flash_console.append("Warning: Failed to create merged binary, but build succeeded.")
else:
self.flash_console.append("idf.py build exited with errors.")
except Exception as e:
Expand Down