Python脚本实现嵌入式固件自动化烧录:100台设备批量操作无需人工值守
产线100台同时烧,最怕“扫错口、进错Boot、烧完不校验、失败不重试”。通用做法是用Python做调度:pyserial扫串口/USB、按VID/PID和白名单过滤;进Bootloader后调用各芯片烧录器(U-Boot/DFU/ST-Link/esptool/OpenOCD均可)写固件;烧完做SHA256+回读校验,线程池限并发,结果落CSV供MES追溯。
一、端口自动识别与白名单
import serial, serial.tools.list_ports, hashlib, subprocess, csv, os
from concurrent.futures import ThreadPoolExecutor
def scan_ports(allow_vids=None, whitelist=None):
out=[]
for p in serial.tools.list_ports.comports():
if allow_vids and p.vid not in allow_vids:
continue
if whitelist and p.device not in whitelist:
continue
out.append(p.device)
return out
# ST/Link CP2102/CH340可按实际改VID
ports=scan_ports(allow_vids={0x0483,0x10C4,0x1A86},
whitelist="/dev/ttyUSB0,/dev/ttyUSB1".split(",") or None)
VID/PID过滤能避开鼠标、4G模块;白名单用于固定治具端口,避免误烧其他设备。
二、按芯片类型进Bootloader并烧录
串口IAP类可发命令进Boot,再分帧传固件;U-Boot类走kgdb/串口命令行或DFU;STM32走ST-Link;ESP走esptool。统一封装:
def enter_boot(port):
# 示例:自定义串口IAP,发ENTER后等READY
s=serial.Serial(port,115200,timeout=2)
s.write(b"ENTER_BOOT\n"); s.flush()
return b"READY" in s.readline()
def flash_by_tool(kind, port, fw, sn=None):
if kind=="stlink":
cmd=["ST-LINK_CLI","-c","SWD","-p",fw,"0x08000000","-v"]
elif kind=="esp":
cmd=["esptool.py","--port",port,"--baud","921600",
"write_flash","--verify","0x10000",fw]
elif kind=="dfu":
cmd=["dfu-util","-D",fw]+(["-S",sn] if sn else [])
else: # 通用串口IAP由业务脚本实现
return enter_boot(port)
r=subprocess.run(cmd,capture_output=True,text=True,timeout=300)
return r.returncode==0, r.stderr
DFU多设备可用序列号定向烧;ESP可用DTR/RTS模拟复位进下载模式;ST-Link CLI可做擦除、下载、校验。
三、固件哈希与回读校验
烧前算SHA256,烧后让设备上报版本/校验值,或用工具回读比对:
def fw_sha256(path):
h=hashlib.sha256()
with open(path,"rb") as f:
for blk in iter(lambda:f.read(65536),b""):
h.update(blk)
return h.hexdigest()
def verify_remote(port,expect_sha):
s=serial.Serial(port,115200,timeout=2)
s.write(b"GET_SHA256\n")
return expect_sha.lower() in s.readline().decode().lower()
OpenOCD类可program xxx verify,重要工位再加CRC32回读,双校验降低虚焊/接触不良漏检。
四、并发调度、重试与日志
100台别全开线程,按USB Hub、调试器、供电分桶,限最大并发:
MAX_WORKERS=8
RETRY=3
def task(dev):
kind,fw,sn=dev["kind"],dev["fw"],dev["sn"]
for i in range(1,RETRY+1):
try:
ok,err=flash_by_tool(kind,dev["port"],fw,sn)
if ok and verify_remote(dev["port"],fw_sha256(fw)):
return (dev["port"],"PASS","")
err=f"verify fail {i}/{RETRY}"
except Exception as e:
err=str(e)
time.sleep(min(2**i,8))
return (dev["port"],"FAIL",err)
def run_all(devs,csv_out):
rows=[]
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as ex:
for row in ex.map(task,devs):
rows.append(row)
with open(csv_out,"w",newline="") as f:
csv.writer(f).writerows([("port","result","detail")]+list(rows))
# devs示例:[{"port":"/dev/ttyUSB0","kind":"esp","fw":"app.bin","sn":"S001"}]
偶发超时做指数退避,连续失败标记NG转人工;日志含端口、SN、固件版本、SHA256、起止时间,直接进MES做追溯。
五、100台无人值守落地要点
供电:多路同时烧最忌USB Hub带不动,用工业自带电源Hub,每路测空载/满载电压;
波特率:ESP夹具好可用921600/2M,普通串口IAP量产用115200更稳;
分组:100台按8~16路一桶,多Hub错开UDEV,Linux用udev按SN固定/dev路径,避免重插变口;
写SN/MAC放最后一步,先烧固件并自校验,再写标识并只读回读,防止半写变废片;
异常分级:通信超时重试,签名/分区错误不重试直接NG,bootloader损坏走脱机烧录器恢复;
报表:每班出良率、平均时长、TOP失败原因,连续同Hub失败率高先查Hub/电源而非脚本。
按“自动识口→白名单分组→Bootloader→外部烧录器写固件→SHA/回读双校验→限并发重试→CSV溯源”闭环,100台边缘网关/STM32/ESP类设备可做到无人值守、重烧可复现、问题可回溯。





