脚本化升级基础:Shell脚本入门、Python脚本入门、脚本执行环境配置
说实话,很多做嵌入式固件升级的工程师,一开始都瞧不上脚本。觉得「不就是敲几行命令吗?有啥技术含量?」。我以前也这么想,直到有一次在生产线上,需要手动给2000块板子挨个烧录固件……嗯,那天我加班到凌晨三点。从那以后,我彻底成了脚本的忠实拥趸。
脚本化升级,说白了就是把那些重复、枯燥、容易出错的手动操作,变成一行行自动执行的代码。你想想看,这不仅仅是省时间,更重要的是——机器不会手抖,不会眼花,不会因为凌晨两点困了而点错按钮。
Shell脚本入门:嵌入式工程师的瑞士军刀
我个人习惯,把Shell脚本当作固件升级的「第一道防线」。为什么?因为几乎所有的嵌入式开发环境,不管是Linux主机还是Windows下的Git Bash,都原生支持Shell。你不需要额外装任何东西,打开终端就能跑。
一个典型的固件升级Shell脚本,大概长这样:
#!/bin/bash
# 固件升级脚本 - 版本 v2.3.1
# 作者:资深老工程师
FW_FILE="firmware_v2.3.1.bin"
DEVICE_PORT="/dev/ttyUSB0"
BAUD_RATE="115200"
echo "=== 开始固件升级 ==="
echo "目标文件: $FW_FILE"
echo "设备端口: $DEVICE_PORT"
# 检查固件文件是否存在
if [ ! -f "$FW_FILE" ]; then
echo "错误:固件文件 $FW_FILE 未找到!"
exit 1
fi
# 检查设备是否连接
if [ ! -c "$DEVICE_PORT" ]; then
echo "错误:设备 $DEVICE_PORT 未连接!"
exit 1
fi
# 执行升级
echo "正在擦除旧固件..."
./flash_tool --erase --port $DEVICE_PORT --baud $BAUD_RATE
echo "正在写入新固件..."
./flash_tool --write --file $FW_FILE --port $DEVICE_PORT --baud $BAUD_RATE
echo "=== 固件升级完成 ==="
你看,就这么十几行代码,就把整个升级流程串起来了。我在项目中遇到过最坑的一次,就是忘了检查文件是否存在,结果脚本跑到一半报错,板子变砖了。所以我现在写脚本,第一件事就是加各种检查——文件检查、设备检查、权限检查,一个都不能少。
Python脚本入门:更强大的升级利器
Shell脚本虽然方便,但处理复杂逻辑时就有点力不从心了。比如你要解析固件包的头部信息、校验CRC32、处理多线程并发升级……这时候,Python就派上用场了。
我建议你至少掌握Python脚本的这几个核心能力:
- 串口通信:用
pyserial库,直接和MCU对话 - 文件操作:读取、写入、校验固件二进制文件
- 异常处理:升级过程中断网、断电怎么办?try-except 是你的好朋友
- 日志记录:把每一步操作都记下来,出了问题好排查
给你看一个实际项目中用过的Python升级脚本片段:
import serial
import hashlib
import time
def calculate_firmware_hash(file_path):
"""计算固件文件的SHA256哈希值"""
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def flash_firmware(port, baudrate, firmware_path):
"""执行固件烧录"""
try:
ser = serial.Serial(port, baudrate, timeout=5)
print(f"已连接到 {port}")
# 发送升级指令
ser.write(b"UPGRADE_START\n")
time.sleep(0.5)
# 读取固件并发送
with open(firmware_path, "rb") as fw:
data = fw.read()
ser.write(data)
# 等待升级完成
response = ser.readline()
if b"UPGRADE_OK" in response:
print("固件升级成功!")
else:
print("固件升级失败!")
except Exception as e:
print(f"升级过程中发生错误:{e}")
finally:
if ser.is_open:
ser.close()
# 使用示例
if __name__ == "__main__":
fw_hash = calculate_firmware_hash("firmware.bin")
print(f"固件哈希值:{fw_hash}")
flash_firmware("/dev/ttyUSB0", 115200, "firmware.bin")
脚本执行环境配置:别让环境坑了你
你写好了完美的脚本,结果拿到同事电脑上一跑——报错。这种事我见得太多了。说白了,脚本执行环境配置,是升级流程里最容易忽略、也最容易出问题的一环。
我总结了一套「环境配置三步走」,你可以参考:
| 步骤 | 具体操作 | 常见坑点 |
|---|---|---|
| 第一步:检查依赖 | 确认Python版本、pip包、系统工具链是否齐全 | Python 2 vs Python 3 混用,脚本直接挂掉 |
| 第二步:设置权限 | 给脚本添加可执行权限,配置udev规则 | Linux下忘了 chmod +x,脚本跑不起来 |
| 第三步:验证环境 | 写一个简单的测试脚本,确认所有组件正常工作 | 串口权限没配,Python报 Permission denied |
这里有个我踩过的坑,特别想分享给你。有一次我在Windows上写了个Python升级脚本,用 os.system() 调用了Linux命令。结果拿到Linux主机上一跑,路径分隔符全错了。从那以后,我写脚本都强制用 os.path.join() 来处理路径,再也不手写斜杠了。
check_environment.py 脚本,自动检测所有依赖项是否就绪。
Shell vs Python:什么时候用哪个?
很多新手会纠结这个问题。我的建议很简单:
- 简单任务(复制文件、执行命令、简单判断)→ 用Shell,快、轻、零依赖
- 复杂任务(解析协议、多线程、网络通信)→ 用Python,灵活、可维护、库丰富
- 混合使用:Shell做「胶水」,Python做「核心逻辑」,两者配合效果最好
我记得有一次做OTA升级,需要先通过Shell脚本检查网络连通性,然后调用Python脚本下载固件包并校验完整性,最后再用Shell脚本触发MCU重启。三个脚本各司其职,配合得天衣无缝。
嗯,脚本化升级的基础就这些。说白了,Shell和Python就像你的左右手——左手负责快速响应,右手负责精细操作。两个都练熟了,固件升级这件事,你就再也不用熬夜了。