之前的文章 讲了如何烧录 ESP32 程序. 本篇讲一下如何生产烧录

通常生产使用的使用 , 固件都会有一个唯一ID , 我们这里叫序列号 (SN). 有多种方式可以烧录进去,这里主要讲两种方式

编译时注入

这种方式是直接在运行脚本中注入一个唯一ID , 优点是一次注入,终身不变. 缺点是每次烧录都得走编译流程 , 虽然代码没有改变 , 但是还是会重新执行一次 , 速度略慢.

首先修改项目的 platformio.ini 增加构建脚本 build_flags = -DDEVICE_ID="\"${sysenv.DEVICE_ID}\""

然后准备生产序列号文件 devices_ids.txt , 里面每行一个序列号

1
2
TESTU23110003
TESTU23110004

然后是执行脚本 generate_id_and_upload.bat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@echo off
setlocal enabledelayedexpansion

:: 定义 ID 文件路径
set ID_FILE=device_ids.txt

:: 检查文件是否存在
if not exist %ID_FILE% (
echo Error: %ID_FILE% does not exist.
exit /b 1
)

:: 读取第一行作为 DEVICE_ID
set /p DEVICE_ID=<%ID_FILE%

:: 如果没有 DEVICE_ID,则退出
if "%DEVICE_ID%"=="" (
echo Error: No available IDs in %ID_FILE%.
exit /b 1
)

:: 显示当前烧录的 ID
echo Flashing device with ID: %DEVICE_ID%

:: 删除第一行并更新文件
(for /f "tokens=*" %%i in (%ID_FILE%) do (
if "%%i" neq "%DEVICE_ID%" echo %%i
)) > temp.txt
move /y temp.txt %ID_FILE% >nul

:: 运行 PlatformIO 烧录,传递 DEVICE_ID
set DEVICE_ID=%DEVICE_ID%
C:\Users\Administrator\.platformio\penv\Scripts\platformio.exe run --target upload --environment esp32-s3-devkitc-1 --upload-port COM7

endlocal

直接命令行执行或者双击 , 代码中 DEVICE_ID 即是烧录进去的ID

存储分区文件写入

首先 , 先准备好程序的固件程序以及支持项, 具体细节参考 之前的文章

bootloader.bin

firmware.bin

partitions.bin

boot_app0.bin

custom_partitions.csv

这个就是烧录一个空程序需要的文件 , 我们在此基础上要写入一个ID到存储文件中然后一起烧录

这里写入nvs要用一个 python 工具类 NVS Partition Generator Utility 下载下来放到准备烧录的目录

然后准备烧录脚本 burn_app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import os
import subprocess

# 设置串口和波特率
PORT = "COM7"
BAUD = "115200"

# 检查设备 ID 文件是否存在
ID_FILE = "device_ids.txt"
if not os.path.exists(ID_FILE):
print(f"Error: {ID_FILE} does not exist.")
exit(1)

# 读取第一行作为设备 ID
with open(ID_FILE, 'r') as file:
DEVICE_ID = file.readline().strip()

# 如果没有设备 ID,则退出
if not DEVICE_ID:
print(f"Error: No available IDs in {ID_FILE}.")
exit(1)

# 生成 NVS 配置 CSV 文件
csv_file = "nvs.csv"
with open(csv_file, 'w', newline='') as file:
file.write("key,type,encoding,value\n")
file.write("nvs,namespace,,\n")
file.write(f"serial_id,data,string,\"{DEVICE_ID}\"\n")

# 使用 nvs_partition_gen.py 生成 NVS 分区镜像
nvs_bin_file = "nvs.bin"
command = ["python", "nvs_partition_gen.py", "generate", csv_file, nvs_bin_file, "0x5000"]
try:
subprocess.run(command, check=True)
print(f"Generated NVS binary file: {nvs_bin_file}")
except subprocess.CalledProcessError as e:
print(f"Error generating NVS files: {e}")
exit(1)

# 烧录固件和动态生成的 NVS 分区
flash_command = [
"esptool", "--chip", "esp32s3", "--port", PORT, "--baud", BAUD,
"--before", "default_reset", "--after", "hard_reset", "write_flash", "-z",
"--flash_mode", "dio", "--flash_freq", "80m", "--flash_size", "8MB",
"0x0000", "bootloader.bin", "0x8000", "partitions.bin", "0xe000",
"boot_app0.bin", "0x10000", "firmware.bin", "0x9000", nvs_bin_file
]

try:
subprocess.run(flash_command, check=True)
print(f"Device flashed successfully with ID: {DEVICE_ID}")
except subprocess.CalledProcessError as e:
print(f"Error flashing device: {e}")
exit(1)

# 如果烧录成功,删除第一行并更新设备 ID 文件
with open(ID_FILE, 'r') as file:
lines = file.readlines()

# 删除设备 ID
with open(ID_FILE, 'w') as file:
for line in lines:
if line.strip() != DEVICE_ID:
file.write(line)

# 清理临时文件
os.remove(csv_file)
os.remove(nvs_bin_file)

print(f"Successfully flashed device with ID: {DEVICE_ID}")

这里参考我的分区配置

1
2
3
4
5
6
7
# Name,   Type, SubType, Offset,      Size,      Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x180000,
app1, app, ota_1, 0x190000, 0x180000,
spiffs, data, spiffs, 0x310000, 0xCE000,
coredump, data, coredump, 0x3DF000, 0x10000

最终烧录前目录文件是

bootloader.bin

firmware.bin

partitions.bin

boot_app0.bin

custom_partitions.csv

device_ids.txt

burn_app.py

可自行修改脚本文件配置. 这里是读取txt文件中的每行ID号 ,然后写入文件 , 烧录 . 实际情况中可以接多个串口一次烧录, 提高效率 .自定一个 pyqt 工具烧录 ,会相当不错.

我们会发现这个方法是最适合生产的 , 因为只需要一次编译 , 然后将烧录工具和程序文件一起交给生产人员 ,高效便捷还不会泄露源码 . 用 idf 开发的项目也可以如此烧录