feat: flash_device.py operator NVS provisioning script
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
104
tools/flash_device.py
Executable file
104
tools/flash_device.py
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
flash_device.py — Write NVS config to TimerCamera-F over serial.
|
||||
|
||||
Requires: pip install esptool 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,u8,{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", "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()
|
||||
Reference in New Issue
Block a user