Files
DoorCounter/tools/flash_device.py
Peter Woolery 265fb727ab fix: flash_device.py — correct nvs_partition_gen module name
esp-idf-nvs-partition-gen installs as esp_idf_nvs_partition_gen,
not nvs_partition_gen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 19:21:08 -07:00

105 lines
3.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""
flash_device.py — Write NVS config to TimerCamera-F over serial.
Requires: pip install esptool esp-idf-nvs-partition-gen
Usage:
python flash_device.py \\
--port /dev/ttyUSB0 \\
--device-id dc-0042 \\
--location-id retailer-123 \\
--hmac-secret <32-byte-hex> # omit to auto-generate \\
[--wifi-ssid "StoreWiFi"] \\
[--wifi-password "secret"] \\
[--line-offset 50]
"""
import argparse
import os
import secrets
import subprocess
import sys
import tempfile
NVS_NAMESPACE = "doorcounter"
NVS_PARTITION_OFFSET = "0x9000"
NVS_PARTITION_SIZE = "0x5000" # matches firmware partition table (20KB)
def build_nvs_csv(device_id, location_id, hmac_secret,
wifi_ssid=None, wifi_pass=None, line_offset=50):
rows = [
"key,type,encoding,value",
f"{NVS_NAMESPACE},namespace,,",
f"device_id,data,string,{device_id}",
f"location_id,data,string,{location_id}",
f"hmac_secret,data,string,{hmac_secret}",
f"line_offset,data,u32,{line_offset}",
]
if wifi_ssid is not None:
rows.append(f"wifi_ssid,data,string,{wifi_ssid}")
if wifi_pass is not None:
rows.append(f"wifi_pass,data,string,{wifi_pass}")
return "\n".join(rows) + "\n"
def main():
parser = argparse.ArgumentParser(
description="Provision TimerCamera-F NVS config over serial")
parser.add_argument("--port", required=True,
help="Serial port, e.g. /dev/ttyUSB0 or COM3")
parser.add_argument("--device-id", required=True,
help="Unique device ID, e.g. dc-0042")
parser.add_argument("--location-id", required=True,
help="Retailer location ID, e.g. retailer-123")
parser.add_argument("--hmac-secret", default=None,
help="32-byte hex HMAC secret (auto-generated if omitted)")
parser.add_argument("--wifi-ssid", default=None,
help="WiFi SSID (optional — user can set via captive portal)")
parser.add_argument("--wifi-password", default=None,
help="WiFi password (optional)")
parser.add_argument("--line-offset", type=int, default=50,
help="Virtual line position %% of frame height (default 50)")
args = parser.parse_args()
hmac_secret = args.hmac_secret or secrets.token_hex(32)
if args.hmac_secret is None:
print(f"Generated HMAC secret: {hmac_secret}")
print(" *** SAVE THIS — you need it to register the device on the server ***")
if args.line_offset < 0 or args.line_offset > 100:
print("Error: --line-offset must be 0-100", file=sys.stderr)
sys.exit(1)
with tempfile.TemporaryDirectory() as tmp:
csv_path = os.path.join(tmp, "nvs.csv")
bin_path = os.path.join(tmp, "nvs.bin")
csv_content = build_nvs_csv(
args.device_id, args.location_id, hmac_secret,
args.wifi_ssid, args.wifi_password, args.line_offset
)
with open(csv_path, "w") as f:
f.write(csv_content)
# Generate NVS binary
ret = subprocess.run(
[sys.executable, "-m", "esp_idf_nvs_partition_gen", "generate",
csv_path, bin_path, NVS_PARTITION_SIZE],
capture_output=True, text=True
)
if ret.returncode != 0:
print(f"nvs_partition_gen error:\n{ret.stderr}", file=sys.stderr)
sys.exit(1)
# Flash NVS partition
ret = subprocess.run(
["esptool.py", "--port", args.port, "--chip", "esp32",
"write_flash", NVS_PARTITION_OFFSET, bin_path]
)
sys.exit(ret.returncode)
if __name__ == "__main__":
main()