量产环境下为已刷主程序的ESP32写入唯一序列号的方案咨询
量产场景下ESP32写入唯一序列号的NVS方案
针对已刷入主程序的ESP32批量写入唯一序列号,同时避免生产线人员接触保密源码的需求,以下是三种可行的安全方案:
方案一:预生成NVS分区镜像单独烧录
核心思路是提前为每个设备生成包含唯一序列号的NVS分区镜像,仅烧录该镜像到ESP32的NVS分区,不触碰主程序分区。
- 主程序预留NVS配置:在主程序源码中预先定义好存储序列号的NVS命名空间和键(例如命名空间
serial,键sn),编译后主程序固化到ESP32。生产线人员无需接触此源码。 - 生成单个NVS镜像:使用ESP-IDF自带的
nvs_partition_gen.py工具,为每个设备生成独立的NVS镜像。首先创建CSV格式的NVS schema文件(nvs_schema.csv):
key,type,encoding,value sn,data,string,SN000001
执行生成命令(替换序列号为目标值,分区大小需匹配主程序分区表的NVS大小):
python nvs_partition_gen.py generate nvs_schema.csv nvs_sn000001.bin 0x4000
- 生产线烧录镜像:使用
esptool.py单独烧录NVS镜像到对应分区地址(地址需和主程序分区表中的NVS起始地址一致,例如0x9000):
esptool.py --port COMx write_flash 0x9000 nvs_sn000001.bin
此方法仅修改NVS分区,完全不影响主程序,生产线人员只需执行烧录命令即可。
方案二:串口指令实时写入NVS
通过主程序预留的只读串口指令接口,让生产线人员通过简单工具发送指令写入序列号,无需操作源码或镜像文件。
- 主程序集成串口指令逻辑:在主程序中添加UART监听任务,仅响应固定前缀的写入指令(例如
SET_SN <序列号>),完成NVS写入后返回确认信号。核心代码示例:
#include "nvs_flash.h" #include "driver/uart.h" #include <string.h> #include <freertos/FreeRTOS.h> #include <freertos/task.h> #define UART_PORT UART_NUM_0 #define BUF_SIZE 128 void uart_sn_write_task(void *arg) { uint8_t data_buf[BUF_SIZE]; nvs_handle_t nvs_handle; esp_err_t err = nvs_open("serial", NVS_READWRITE, &nvs_handle); if (err != ESP_OK) { vTaskDelete(NULL); return; } while (1) { int read_len = uart_read_bytes(UART_PORT, data_buf, BUF_SIZE-1, pdMS_TO_TICKS(100)); if (read_len > 0) { data_buf[read_len] = '\0'; char* cmd_prefix = strstr((char*)data_buf, "SET_SN "); if (cmd_prefix) { char* serial_num = cmd_prefix + 7; // 跳过"SET_SN "前缀 // 去除换行符 serial_num[strcspn(serial_num, "\r\n")] = '\0'; // 写入NVS err = nvs_set_str(nvs_handle, "sn", serial_num); if (err == ESP_OK) { nvs_commit(nvs_handle); uart_write_bytes(UART_PORT, "SN_OK", 5); } else { uart_write_bytes(UART_PORT, "SN_FAIL", 7); } } } vTaskDelay(pdMS_TO_TICKS(10)); } nvs_close(nvs_handle); vTaskDelete(NULL); } // 在app_main中初始化UART并创建任务 void app_main() { nvs_flash_init(); const uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE }; uart_param_config(UART_PORT, &uart_config); uart_set_pin(UART_PORT, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); uart_driver_install(UART_PORT, BUF_SIZE*2, 0, 0, NULL, 0); xTaskCreate(uart_sn_write_task, "uart_sn_task", 2048, NULL, 5, NULL); }
- 生产线使用简易PC工具:开发Python脚本自动生成唯一序列号并发送指令,示例:
import serial import time import random def write_serial_number(port, baud=115200): ser = serial.Serial(port, baud, timeout=2) time.sleep(2) # 生成唯一序列号(可替换为流水号/时间戳等规则) sn = f"SN{random.randint(100000, 999999)}" cmd = f"SET_SN {sn}\r\n" ser.write(cmd.encode()) resp = ser.read(10).decode().strip() if resp == "SN_OK": print(f"设备 {port} 已写入序列号: {sn}") else: print(f"设备 {port} 写入失败") ser.close() # 调用示例,替换为实际串口 write_serial_number("COM3")
生产线人员只需运行此脚本,选择对应串口即可完成写入,完全无需接触主程序源码。
方案三:批量烧录工具集成NVS写入
针对大规模量产场景,可将主程序烧录与NVS写入整合为自动化流程:
- 编写批量处理脚本,自动生成每个设备的唯一序列号及对应的NVS镜像。
- 使用
esptool.py的批量烧录功能,先烧录主程序,再烧录对应设备的NVS镜像。 - 生产线人员仅需启动批量烧录脚本,工具会自动完成所有设备的序列号写入,全程无需干预源码。
关键安全注意事项
- 分区隔离:确保NVS分区与主程序分区在分区表中完全分离,避免烧录NVS时覆盖主程序。
- 指令校验:串口指令接口仅保留写入序列号的逻辑,禁止任何修改主程序或其他系统配置的指令。
- 镜像加密(可选):若序列号涉及敏感信息,可对NVS镜像进行加密,烧录时使用支持加密的工具解密写入。
内容的提问来源于stack exchange,提问作者Naveen PS
相关产品推荐
相关产品推荐

