# Safe local DD3 HIL setup ## Safety boundary This harness is for two USB-connected LilyGO boards and, optionally, an electrically isolated USB-UART or optical meter simulator. It does not authorize direct connection to mains-referenced meter conductors. Do not change wiring, GPIO14 role selection, straps, eFuses, secure boot, flash encryption, NVS, credentials, or udev/OS permissions as part of this workflow. The firmware remains role-neutral: GPIO14 is sampled at boot exactly as before. The `hil` environment inherits `lilygo-t3-v1-6-1` (the current 433 MHz debug build) and adds only `ENABLE_HIL_TRACE=1`. Production environments are unchanged. ## Dependencies and Linux serial access PlatformIO is accepted from `PATH` or `~/.platformio/penv/bin/pio`. Install Python dependencies into a repository-local environment: ```bash python3 -m venv .venv-hil .venv-hil/bin/pip install -r requirements-hil.txt ``` Inspect access with `id -nG` and `stat -c '%A %U %G %n' /dev/ttyACM0`. On this machine the user is already in `dialout`, and the discovered ports are `root:dialout` with group read/write. The tools never run `sudo`, modify group membership, change permissions, or install udev rules. If access is absent, stop and ask the system administrator to apply the site's normal policy, then start a new login session. ## Discover and approve stable ports Discovery lists metadata and symlinks but does not open a device: ```bash .venv-hil/bin/python tools/hil/discover.py ``` Prefer `/dev/serial/by-id` names. Copy `.hil/local.example.toml` to ignored `.hil/local.toml` only after visually confirming the two targets. Set the sender and receiver path, then set `approved_access = true` only after approving the first serial open. Set `approved_flash = true` only after separately approving flashing. The tools refuse missing, duplicate, unstable, or unapproved ports. Never commit local `/dev` paths. Current discovery on 2026-07-13 found two uniquely identified board candidates and one ambiguous device: ```text usb-1a86_USB_Single_Serial_5644005006-if00 -> ttyACM0 usb-1a86_USB_Single_Serial_5644020877-if00 -> ttyACM1 usb-1a86_USB_Serial-if00-port0 -> ttyUSB0 (ambiguous; do not access) ``` Serial role evidence, not the filename, is authoritative. A valid mapping requires a `HIL:` role event from both devices and must agree with the configured names. ## Build, flash, and capture Building never accesses serial devices and does not require local port configuration: ```bash .venv-hil/bin/python tools/hil/build.py ``` After explicit access and flash approval is recorded: ```bash .venv-hil/bin/python tools/hil/flash_pair.py --yes-flash .venv-hil/bin/python tools/hil/capture_pair.py --reset --seconds 180 .venv-hil/bin/python tools/hil/run_baseline.py --yes-flash --seconds 180 ``` `flash_pair.py` builds the HIL environment once, then uploads the same environment to each approved stable path. It does not issue a full-chip or NVS erase; normal uploading necessarily rewrites the bootloader, partition-table, boot-app, and application address ranges shown in the flash log. `--reset` pulses the boards' normal USB auto-reset circuit only after capture readers are active. Ctrl+C closes both serial handles and retains all bytes captured so far. Each run writes beneath `artifacts/hil/-