# BLE NFC relay v1 Service: `7a6b0001-5b21-4f36-8e5d-9c3a26d74210`. | Characteristic suffix | Access | Value | | --- | --- | --- | | `0002` | Read/write | Existing 8-byte PING/PONG probe | | `0003` | Read | 18-byte NFC session status | | `0004` | Read/write | Fragmented APDU request / stable response mailbox | All multi-byte integers below are big-endian. The UUID suffix means the last four digits of the UUID's first component, with the remainder unchanged. Status: `version:u8=1, active:u8, uid_length:u8, sak:u8, session:u32, uid[10]`. UID bytes are metadata, not credentials. Active means an APDU session is open; it does not guarantee that the phone remains physically present. Session IDs start from a random boot seed and increase for each offered target. Reconnect invalidates all old requests and responses. APDU packet header (12 bytes): `version:u8=1, kind:u8, session:u32, sequence:u16, offset:u16, total:u16`. | Kind | Direction | Meaning | | --- | --- | --- | | `01` | Go → ESP32 | Command fragment; payload follows the header | | `02` | Go → ESP32 | Release session; zero offset/total, no payload | | `03` | Go → ESP32 | ECP configuration; session/sequence/offset = 0, total = 8 (Home group ID) or 0 (disable) | | `10` | ESP32 → Go | Pending response; zero offset/total, no payload | | `11` | ESP32 → Go | Ready response; zero offset, total = payload size | | `12` | ESP32 → Go | Transport error; zero offset, total = 1, error byte | Each APDU sequence starts at 1 and strictly increases within its session. Only one APDU is admitted at a time. Fragments must be contiguous with matching session/sequence/total, within 4..240 bytes. A duplicate, gap, stale session or busy write is rejected. No APDU is executed until its complete payload is queued to the NFC owner task. Go uses ATT write requests and fragments to MTU-3 when BlueZ reports MTU, otherwise to 20 bytes. Responses are read using BlueZ's long-read support. The response remains stable across repeated reads until the next complete request or a connection/session reset. A GATT callback never touches UART. Error bytes: 1 = invalid APDU/target, 2 = UART/frame/timeout failure, 3 = PN532 error or unsupported chaining flags, 4 = response too large. Normal `11` responses include the APDU's SW1/SW2 bytes. PN532 errors cannot be mistaken for an APDU status word. The owner holds the selected target while Go sends SELECT/AUTH0/AUTH1 and CONTROL FLOW. It turns RF off after release, transport failure, disconnect or 15-second expiry, then resumes presence polling. Held cards are suppressed by the existing UID presence state until removal; disconnected detections are retried after reconnection. GPIO3 TX / GPIO4 RX and startup wake packets are preserved. ## Home Key ECP polling Matching firmware requires Go's `03` configuration on every BLE connection. The configuration is a single 20-byte ATT write, fitting the minimum MTU. It carries only the public 8-byte Home group identifier: `SHA256("key-identifier" || provisioned_reader_private_scalar)[0:8]`. This is the group half of the 16-byte NFC reader identifier, not its unique reader half or the Bluetooth MAC address. Private keys remain in Go. Go refreshes this value between sessions when provisioning changes; zero total disables polling until a reader key exists. Firmware clears configuration on connect/disconnect and keeps RF off until configured. GATT writes only update mutex-protected metadata; an active APDU session is never interrupted, and a configuration update applies to subsequent scans. Each scan transmits `6a02cb0206021100 || group[8] || CRC-A[2]` using PN532 InCommunicateThru before InListPassiveTarget activates a card. The ESP32 selects 106 kbps Type A/full-byte transmission and disables hardware CRC for this raw frame, since CRC-A is supplied in software. PN532 RF status `01` (no reply) is normal for this broadcast; host UART/frame timeouts and other PN532 errors remain failures. A short ECP response timeout is restored to the PN532 default before card activation. NFC polling/ECP remain suspended during APDU exchange. The ECP express flag is enabled; the phone's Home Key Express Mode setting still applies. Disabling Express Mode should present the key for user approval. ECP chooses the credential; STANDARD authentication still verifies it before unlocking. FAST and unknown-device attestation remain outside this release. Hardware validation is required: host tests cannot prove that a phone receives RF frames or selects its Home Key.