Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
max-line-length = 120
exclude =
.git,
__pycache__,
build,
dist
60 changes: 60 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
lint:
name: Static Code Analysis
runs-on: windows-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install linting tools
run: pip install flake8 pylint

- name: Install project dependencies
run: pip install -r requirements.txt

- name: Run flake8 (style & syntax check)
run: flake8 stm32_easy_flash.py

- name: Run pylint (code quality check)
run: pylint stm32_easy_flash.py --fail-under=7.0

build:
name: Build Executable
runs-on: windows-latest
needs: lint

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install build dependencies
run: pip install -r requirements-build.txt

- name: Build .exe with PyInstaller
run: pyinstaller stm32_easy_flash.spec

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: stm32-easy-flash-windows
path: dist/stm32_easy_flash.exe
retention-days: 30
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ MANIFEST
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# But keep the PyInstaller spec file for this project
!stm32_easy_flash.spec

# Installer logs
pip-log.txt
Expand Down
2 changes: 2 additions & 0 deletions requirements-build.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
keyboard
pyinstaller
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
keyboard
41 changes: 22 additions & 19 deletions stm32_easy_flash.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import keyboard
"""STM32 Easy Flash - Hotkey-triggered firmware flashing via STM32CubeProgrammer CLI."""

import subprocess
import time
import sys
import time

import keyboard

# ==========================================
# CONFIGURATION - PLEASE ADJUST!
Expand All @@ -14,14 +17,16 @@
FIRMWARE_PATH = r"C:\path\to\your\firmware.hex"

# Connection type (e.g. "SWD" for ST-LINK, "USB1" for DFU, "COM3" for UART)
PORT = "SWD"
PORT = "SWD"

# ==========================================


def flash_mcu():
"""Erase and flash the STM32 target using STM32_Programmer_CLI."""
print("\n" + "-"*40)
print("▶ Hotkey detected! Starting flash process...")

# Build command:
# -c port=... : Establish connection
# -e all : Full chip erase
Expand All @@ -36,31 +41,29 @@ def flash_mcu():
"-v",
"-rst"
]

try:
# Run the CLI. stdout and stderr are forwarded directly to the console
# so that progress output is visible in real time.
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)

for line in process.stdout:
# Print CLI output line by line
print(line, end="")

process.wait()

with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True) as process:
for line in process.stdout:
print(line, end="")

if process.returncode == 0:
print("\n✅ Successfully erased and flashed!")
else:
print(f"\n❌ Flash error! Return code: {process.returncode}")

except FileNotFoundError:
print(f"\n❌ Error: '{CLI_PATH}' not found. Please check CLI_PATH!")
except Exception as e:
except OSError as e:
print(f"\n❌ An unexpected error occurred: {e}")

print("-" * 40)
print("Waiting for 'ctrl + shift + f12'... (Exit with Ctrl+C)")


# Main program
if __name__ == "__main__":
print("STM32 Easy Flash started.")
Expand All @@ -71,12 +74,12 @@ def flash_mcu():
try:
# Register hotkey. Pressing ctrl+shift+f12 will trigger flash_mcu()
keyboard.add_hotkey('ctrl+shift+f12', flash_mcu)

# Keep the script alive so the hotkey listener remains active.
# Press Ctrl+C in the terminal to exit cleanly.
while True:
time.sleep(1)

except KeyboardInterrupt:
print("\nScript terminated by user.")
sys.exit(0)
sys.exit(0)
44 changes: 44 additions & 0 deletions stm32_easy_flash.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(
['stm32_easy_flash.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['keyboard'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='stm32_easy_flash',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
# console=True keeps the terminal window open so flash output is visible
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
Loading