[PlatformIO] [ESP32] 在一个工程下管理多个项目,main文件或者快速迁移Arduino项目的方案---ppio
一、定位和创意
ppio 是一个自己设计的命令行工具,用于简洁高效地切换 PlatformIO 项目中的 main.cpp,并自动编译 + 上传。
适用场景:教学实验、比赛设计、小型多项目管理,需要在多个 main.cpp 之间快速切换。
二、功能总结
-
支持 .cpp 文件作为目标。
-
支持 .ino 文件,自动重命名为 .cpp
-
支持目录名,自动搜索同名 .ino/.cpp
-
自动处理子目录时搜索 PlatformIO 根目录
-
如果指定文件已是 src/main.cpp,则自动跳过 copy
-
支持 PlatformIO 命令参数(如 -t upload, -e esp32s3 等)
-
方便 Arduino 项目和 PlatformIO 项目互相转换
-
支持 -c 命令:将 .cpp 文件转换成 .ino
三、支持的命令格式
【基础命令】
//ppio xxxx 是直接编译并上传
ppio xxx.cpp # 作为新的 main.cpp
ppio xxx.ino # 自动改名为 xxx.cpp 并替换 main
ppio xxx_folder/ # 自动搜索 xxx_folder/xxx_folder.ino 或 .cpp
【PlatformIO 自定义扩展命令】
ppio -t upload xxx.cpp # 指定 upload 目标
ppio run xxx.cpp # 统一 PlatformIO run 运行
【转 Arduino 项目 .ino】
ppio -c blink.cpp # 生成 blink_convert.ino
ppio -c blink_folder/ # 搜索 blink_folder/blink_folder.cpp 转为 .ino
【帮助提示】
ppio # 打印命令用法
四、与其他多工程管理方法对比
| 方法 | 描述 | 优缺点 |
|---|---|---|
| 修改 platformio.ini 中 src_dir | 每次切换项目会修改 ini 配置 | ❌ 引起重新编译全部库,耗时,容易出错 |
| #include 切换 main | main.cpp 中通过 include 切换不同实验 | ❌ 进入重复、字符冲突、调试困难 |
| 多 project 目录 | 每个项目都是一个完整 PIO 工程 | ❌ 无法共享 lib,文件重复,管理维护累人 |
| ✅ ppio 工具 | 保留给 PlatformIO 的结构不变,快速替换 main.cpp | ✅ 极速切换,零副作用,适合教学、比赛和小型项目管理 |
五、自己打包成 EXE
-
安装 PyInstaller
pip install pyinstaller
-
打包指令
pyinstaller --onefile --name ppio ppio.py
-
结果 生成路径:
dist/ppio.exe -
(可选) 如果不想要黑窗口,可加:
--noconsole
但对 CLI 工具不推荐使用,无法看日志
六、全局使用
方案 1: 把 ppio.exe 放入环境变量 PATH 中
例如:
D:\Tools\ppio\ppio.exe
然后把 D:\Tools\ppio\ 加入 Windows 环境变量 PATH,任何系统终端都可以调用:
ppio xxx.cpp
方案 2: 当前目录使用
./ppio.exe xxx.cpp
七、结论
ppio 是一个优雅、简洁、用习非常直观的小工具,实现了 PlatformIO 小型多项目快速切换、Arduino 项目互换、后缀适配和命令扩展。
它适合教学实验、比赛项目、基础课程实操等场景,帮助用户高效率管理实验程序,轻松快捷。
快速下载链接:
八、更新日志
-
v1.0 (基础版)
-
支持 .cpp 文件切换 main.cpp
-
支持 pio run/上传 命令传递
-
-
v1.1
-
支持 .ino 文件,自动 rename 为 .cpp
-
支持输入目录,搜索同名 .ino/.cpp
-
支持 tab 补全的结尾 “\” 或 “//” 删除
-
-
v1.2
-
增加 -c 模式:.cpp 转 .ino,便于转回 Arduino
-
支持目录/文件名与同名文件匹配
-
-
v1.3
-
如果目标已经是 main.cpp,自动 skip copy 操作
-
优化不同情况下的错误提示,更好用
-
九、附录:源码
import sys
import os
import shutil
import subprocess
def find_project_root(start_path):
current = os.path.abspath(start_path)
while True:
if os.path.exists(os.path.join(current, "platformio.ini")):
return current
parent = os.path.dirname(current)
if parent == current:
return None
current = parent
def resolve_target(arg):
arg = arg.rstrip("/\\") # 修复 tab 自动补全路径结尾斜杠问题
if os.path.isdir(arg):
folder_name = os.path.basename(arg)
ino = os.path.join(arg, folder_name + ".ino")
cpp = os.path.join(arg, folder_name + ".cpp")
if os.path.exists(ino):
print(f"[INFO] Found INO file: {ino}")
new_cpp = ino.replace(".ino", ".cpp")
os.rename(ino, new_cpp)
print(f"[INFO] Renamed INO to CPP: {new_cpp}")
return new_cpp
elif os.path.exists(cpp):
print(f"[INFO] Found CPP file: {cpp}")
return cpp
else:
print(f"[ERROR] No .ino or .cpp file found in folder {arg}")
return None
elif arg.endswith(".ino"):
if not os.path.exists(arg):
print(f"[ERROR] File not found: {arg}")
return None
new_cpp = arg.replace(".ino", ".cpp")
os.rename(arg, new_cpp)
print(f"[INFO] Renamed INO to CPP: {new_cpp}")
return new_cpp
elif arg.endswith(".cpp"):
if not os.path.exists(arg):
print(f"[ERROR] File not found: {arg}")
return None
return arg
else:
print(f"[ERROR] Unsupported file or directory: {arg}")
return None
def convert_to_ino(path):
path = path.rstrip("/\\")
if os.path.isfile(path) and path.endswith(".cpp"):
base = os.path.splitext(path)[0]
dest = base + "_convert.ino"
shutil.copyfile(path, dest)
print(f"[OK] Converted: {path} -> {dest}")
elif os.path.isdir(path):
folder_name = os.path.basename(path)
cpp = os.path.join(path, folder_name + ".cpp")
if os.path.exists(cpp):
dest = os.path.join(path, folder_name + "_convert.ino")
shutil.copyfile(cpp, dest)
print(f"[OK] Converted: {cpp} -> {dest}")
else:
print(f"[WARN] No matching {folder_name}.cpp found in folder {path}")
else:
print(f"[ERROR] Not a valid path: {path}")
def main():
args = sys.argv[1:]
if not args:
print("\nUsage:")
print(" ppio main1.cpp")
print(" ppio blink.ino (auto rename to .cpp)")
print(" ppio main333 (looks for main333/main333.ino or .cpp)")
print(" ppio -c sketch.cpp (convert .cpp → .ino)")
print(" ppio -c folder_name/ (convert folder_name/folder_name.cpp → _convert.ino)")
return
# handle -c: convert cpp to ino
if args[0] == "-c":
for path in args[1:]:
convert_to_ino(path)
return
cmd = ["pio"]
target = None
for arg in args:
if arg.endswith(".cpp") or arg.endswith(".ino") or os.path.isdir(arg):
target = resolve_target(arg)
else:
cmd.append(arg)
if target is None:
print("[ERROR] No valid .cpp/.ino file specified.")
return
project_root = find_project_root(os.getcwd())
if project_root is None:
print("[ERROR] Cannot find PlatformIO project root (missing platformio.ini).")
return
main_cpp_path = os.path.join(project_root, "src", "main.cpp")
print(f"\n[INFO] Preparing to copy {target} to {main_cpp_path}...")
os.makedirs(os.path.dirname(main_cpp_path), exist_ok=True)
if os.path.abspath(target) != os.path.abspath(main_cpp_path):
shutil.copyfile(target, main_cpp_path)
print(f"[OK] main.cpp is now: {target}")
else:
print(f"[SKIP] Source and destination are the same file. Skipping copy.")
if len(cmd) == 1:
cmd = ["pio", "run", "-t", "upload"]
print(f"\n[INFO] Running: {' '.join(cmd)}")
subprocess.run(cmd, cwd=project_root)
if __name__ == "__main__":
main()
更多推荐



所有评论(0)