The bridge between PlatformIO and the boards it leaves behind.
PlatformIO makes embedded development feel frictionless — until you pick up a board it wasn't built for. Maybe it's an STM32 where CubeMX handles pin configuration perfectly but ST wants you locked into their IDE. Maybe it's an RP2350 that didn't exist when the platform definitions were written.
piomx gives you the PlatformIO experience on those boards — a single static binary, no Python, no venv, no fuss.
-
STM32 — Drop a CubeMX project directory, run
piomx stm32 --yes, and get a working PlatformIO project..iocauto-detection, dynamic HCLK extraction, SWO trace script. No CubeIDE needed. -
Pico2 / RP2350 — Full support for RP2350-based boards (WeAct, Pimoroni, official) plus the original RP2040. Arduino or pico-sdk, dual-core templates, USB + DAP environments.
One tool, one binary — pio run --target upload works the same no matter what's on your desk.
- Installation
- Quick Start
- CLI Reference
- Generated Files
- Wizard Walkthrough
- Examples
- Contributing
- License
curl -fsSL https://raw.githubusercontent.com/runtime-terror404/piomx/main/install.sh | shDownloads the latest release binary for your OS/architecture to ~/.local/bin/piomx. Safe to re-run for updates.
Windows (PowerShell):
powershell -c "irm https://raw.githubusercontent.com/runtime-terror404/piomx/main/install.ps1 | iex"git clone https://github.com/runtime-terror404/piomx.git
cd piomx
go build -o piomx ./cmd/cli
sudo cp piomx /usr/local/bin/./install.sh --uninstall # Linux/macOS
./install.ps1 -Uninstall # WindowsRequirements: PlatformIO CLI (pio) on PATH. That's it.
# Interactive wizard (no arguments)
piomx
# Scaffold a Pico2 project with defaults
piomx pico2 --yes
# Preview what would be generated
piomx pico2 --dry-run
# Scaffold an STM32 project (auto-detects .ioc)
cd my-cubemx-project/
piomx stm32 --yes| Flag | Short | Default | Description |
|---|---|---|---|
--project-dir |
-d |
. |
Target project directory |
--dry-run |
-n |
false | Preview files without writing |
--yes |
-y |
false | Skip confirmation prompts |
--force |
— | false | Overwrite even if files were edited |
--adopt |
— | false | Lock-file an existing project without modifying content |
--name |
— | dir basename | Project name |
--preset |
-p |
— | Load configuration from a saved preset |
Launches the interactive wizard. Walks you through platform, board, debug probe, and all options, then shows a summary before generating files.
Scaffold a Raspberry Pi Pico / RP2350 / RP2040 project.
piomx pico2 [FLAGS]
| Flag | Short | Default | Description |
|---|---|---|---|
--board |
-b |
weact |
Board variant: pico, pico2, weact, official, pimoroni, custom |
--framework |
— | arduino |
Framework: arduino, pico-sdk |
--core |
— | earlephilhower |
Arduino core: earlephilhower, mbed |
--environments |
— | usb,dap |
Environments (comma-separated): usb, dap |
--baud |
— | 115200 |
Serial monitor baud rate |
--log / --no-log |
— | --log |
Add monitor_filters = time, log2file |
--libs |
-l |
— | PlatformIO libraries (e.g. SPI, Wire, Adafruit NeoPixel) |
--git |
— | false | Initialize git repo + .gitignore + initial commit |
--ci |
— | false | Generate .github/workflows/pio_build.yml |
Board variants
--board |
Chip | PlatformIO board |
Notes |
|---|---|---|---|
weact |
RP2350A | rpipico2 |
WeAct RP2350A (default) |
pico2 |
RP2350 | rpipico2 |
Official Raspberry Pi Pico 2 |
pico |
RP2040 | pico |
Original Raspberry Pi Pico |
official |
RP2350 | rpipico2 |
Official Pico 2 (alias) |
pimoroni |
RP2350 | pimoroni_pico2 |
Pimoroni Pico Plus 2 |
custom |
RP2350 | rpipico2 |
Custom RP2350 board |
Scaffold an STM32 project from a CubeMX .ioc file.
piomx stm32 [FLAGS]
| Flag | Short | Default | Description |
|---|---|---|---|
--ioc |
— | auto-detect | Path to .ioc file |
--debug |
— | stlink |
Debug probe: stlink, cmsis-dap, jlink |
--swo / --no-swo |
— | --swo |
Generate SWO trace script |
--baud |
— | 115200 |
Serial monitor baud rate |
--log / --no-log |
— | --log |
Add monitor_filters = time, log2file |
--libs |
-l |
— | PlatformIO libraries |
--git |
— | false | Initialize git repo |
--ci |
— | false | Generate .github/workflows/pio_build.yml |
Debug probes
--debug |
Upload protocol | Debug tool | OpenOCD interface |
|---|---|---|---|
stlink |
stlink |
stlink |
interface/stlink.cfg |
cmsis-dap |
cmsis-dap |
cmsis-dap |
interface/cmsis-dap.cfg |
jlink |
jlink |
jlink |
interface/jlink.cfg |
.ioc handling
.ioc files are auto-detected in the project directory. The file is parsed for:
Mcu.UserName→ PlatformIOgenericSTM32XXXXXXboard IDRCC.HCLKFreq_Value→-traceclkfrequency in the SWO script
Without an .ioc file, the STM32 wizard exits with an error — create a CubeMX project first.
Save, load, list, and delete named presets. Stored in ~/.config/piomx/presets.json.
piomx presets save my-pico2 --board weact --baud 115200 --framework arduino
piomx presets list
piomx pico2 --preset my-pico2 --yes
piomx presets delete my-pico2piomx writes a .piomx.lock.yml into every scaffolded project. On re-run, it:
- Compares on-disk file hashes against the lock file
- Hash matches → safe to regenerate
- Hash differs (you edited the file) → refuses, shows a diff summary
- Use
--forceto overwrite anyway,--adoptto lock-file an existing project
project-dir/
├── platformio.ini # [env] + [env:usb] + [env:dap]
├── src/
│ └── main.cpp # Arduino dual-core or pico-sdk main()
└── .piomx.lock.yml # SHA-256 manifest (commit this)
project-dir/
├── platformio.ini # [platformio] + [env] + [env:{board_id}]
├── swo_trace.py # OpenOCD SWO trace custom target
└── .piomx.lock.yml # SHA-256 manifest
STM32 projects do not get a src/main.cpp — PlatformIO compiles from Core/Src/ (CubeMX sources).
pico2 (piomx pico2 --board weact):
[env]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = rpipico2
framework = arduino
board_build.core = earlephilhower
board_upload.maximum_size = 16777216
monitor_speed = 115200
monitor_filters = time, log2file
; Default environment: Flashes via USB-C (1200bps touch)
[env:usb]
; Debug environment: Flashes via SWD DAPLink
[env:dap]
; upload_protocol = cmsis-dapstm32 (piomx stm32 in CubeMX dir):
[platformio]
src_dir = Core/Src
include_dir = Core/Inc
[env]
platform = ststm32
board = genericSTM32F103C8
framework = stm32cube
upload_protocol = stlink
debug_tool = stlink
monitor_speed = 115200
monitor_filters = time, log2file
extra_scripts = swo_trace.py
[env:genericSTM32F103C8]=== piomx interactive wizard ===
Select target platform:
[1] pico2
[2] stm32
Select [1]:
Select board variant:
[1] Custom RP2350 board (custom)
[2] Raspberry Pi Pico 2 (official) (official)
[3] Original Raspberry Pi Pico (RP2040) (pico)
[4] Official Raspberry Pi Pico 2 (RP2350) (pico2)
[5] Pimoroni Pico Plus 2 (pimoroni)
[6] WeAct RP2350A (weact)
Select [1]:
Select framework:
[1] arduino
[2] pico-sdk
Select [1]:
...
--- Configuration Summary ---
Platform: Raspberry Pi Pico / RP2350 / RP2040
Board: WeAct RP2350A
Framework: arduino
Envs: usb
...
Proceed with these settings? [Y/n]:
# Minimal Pico2 project
piomx pico2 --yes
# Original Pico with custom name
piomx pico2 --board pico --name my-blink -d ~/projects/ --yes
# Pico2 with git, CI, and libraries
piomx pico2 --git --ci --libs "Adafruit NeoPixel, Wire" --yes
# STM32 with J-Link, no SWO
piomx stm32 --debug jlink --no-swo --yes
# STM32 with specific .ioc file
piomx stm32 --ioc nucleo-f411re.ioc --yes
# Dry-run preview
piomx pico2 --git --ci --libs "Wire, SPI" -n
# Preset with override
piomx pico2 --preset my-pico2 --baud 9600 --yes
# Adopt an existing project
piomx pico2 --board weact --adopt -d my-existing-projectgit clone https://github.com/runtime-terror404/piomx.git
cd piomx
go build -o piomx ./cmd/cli
go test ./... -race # 70+ testsBug reports and PRs welcome. Architecture:
cmd/cli/ # cobra commands (flag parsing + output only)
└─▶ internal/actions/ # all business logic — never prints
└─▶ internal/core/ # Board, DebugProbe, Registry
└─▶ internal/ioc/ # .ioc parser + STM32 family table
└─▶ internal/generate/ # content generation (no I/O)
└─▶ internal/writer/ # atomic writes + drift detection
└─▶ internal/lockfile/ # .piomx.lock.yml
cmd/climay only importinternal/actions— enforced by linter- Test coverage ≥70-90% across all packages
- Specs in
refactor_instruction/
MIT — see LICENSE for full text.
The original Python version of this tool is archived on the pre_refactor branch.