如何将CircuitPython的code.py运行输出(含报错)写入指定文件
CircuitPython 无CDC场景下code.py运行日志落盘方案
CircuitPython原生写入boot_out.txt的逻辑是固件启动阶段硬编码实现,没有提供直接配置项把code.py的输出默认写入同类文件。在禁用CDC串口无法直连看日志的场景下,通过入口重定向+全局异常兜底的方案,可以实现全量输出(含print内容、所有错误栈信息)写入指定日志文件,不需要依赖USB串口功能。
实现步骤
- 调整代码文件结构:把原本写在
code.py里的全部业务代码,完整移动到同目录下新建的app.py文件中,原code.py清空作为专用启动入口。这一步是为了避免业务代码存在语法错误时,重定向逻辑还未加载就抛出异常,导致错误信息漏记。 - (可选,按需配置)在
boot.py中添加存储权限配置,确保CircuitPython运行时拥有板载存储写入权限:
import storage # 关闭USB主机端的存储写入权限,开放CircuitPython自身写入权限 storage.remount("/", readonly=False, disable_concurrent_write_protection=True)
如果你的设备本来就给CircuitPython开放了写入权限,这步可以跳过。
- 在清空后的
code.py中写入重定向与异常捕获逻辑,所有运行输出都会写入同目录下的code_out.txt:
import sys import time import traceback LOG_FILE = "/code_out.txt" class LogToFile: def __init__(self, log_path): # 追加模式打开日志文件,改成"w"则每次启动覆盖旧日志 self.log_f = open(log_path, "a", encoding="utf-8") self._origin_stdout = sys.stdout self._origin_stderr = sys.stderr def write(self, content): self.log_f.write(content) # 每次写入即时刷入闪存,防止掉电丢失日志 self.log_f.flush() # 如果后续重新开启CDC,取消下面注释可同时输出到串口 # self._origin_stdout.write(content) def flush(self): self.log_f.flush() # 接管标准输出、标准错误流 log_writer = LogToFile(LOG_FILE) sys.stdout = log_writer sys.stderr = log_writer # 写入启动分隔符,区分不同运行批次的日志 print(f"\n===== 程序启动,运行时间戳: {time.monotonic()}s =====") try: # 导入执行业务代码,注意不要加.py后缀 import app except Exception as err: # 捕获所有导入、运行阶段的异常,打印完整错误栈 print("\n[程序异常退出]") traceback.print_exception(type(err), err, err.__traceback__) finally: # 确保日志文件句柄正常关闭 log_writer.log_f.close()
注意事项
- 高频写日志会损耗闪存寿命,如果业务代码存在循环高频打印的逻辑,建议增加日志等级过滤,避免无意义的重复写入缩短闪存使用寿命。
- 该方案无法捕获看门狗硬复位、手动按复位键、断电这类固件级硬中断产生的信息,这类场景的信息本身也不会被原生的
boot_out.txt记录,属于固件层机制限制,没有用户态的解决方法。 - 如果业务代码中存在重新挂载文件系统的操作,需要在remount完成后重新初始化日志写入器、打开日志文件句柄,否则会出现日志写入失败的问题。
- 要读取日志内容时,插USB进入存储模式直接打开
code_out.txt查看即可,不需要串口连接。
内容的提问来源于stack exchange,提问作者root
相关产品推荐
相关产品推荐

