BLE Provisioning Protocol
Admiral OS uses Bluetooth Low Energy for out-of-box device setup and, once a device is claimed, for cloud-authorised field service (§4.11). The dashboard's Provision over Bluetooth and Repair over Bluetooth pages implement this protocol in the browser. This page covers the full provisioning protocol — advertising, GATT service and characteristic definitions, the provisioning state machine, security model, error codes, and implementation notes for both firmware and mobile clients.
An unclaimed device accepts any setup session (protected by an on-screen pairing code on devices with a display). Once a device is claimed it no longer accepts open sessions: every capability requires a short-lived, session-bound access token issued by Admiral Cloud and verified on the device, offline (§4.11). A claimed device that has not received verification keys from the cloud does not advertise at all.
1. Overview
The provisioning protocol allows a mobile companion app to:
- Discover one or more unconfigured Admiral devices in proximity.
- Identify a specific physical device via LED and on-screen pairing animation.
- Configure Wi-Fi and/or Ethernet credentials (individually or in batch across 10+ devices).
- Monitor multi-interface network connection status in real time.
- Retrieve a cloud-issued pairing key for account association.
- Read device metadata (firmware version, serial number, hardware revision).
Design Principles
| Principle | Description |
|---|---|
| Sealed After Claim | Open setup is available only while unclaimed. After the claim, access needs a cloud-issued token bound to the session (§4.11). Factory reset returns the device to open setup. |
| Multi-device Batching | A mobile app can discover 10+ devices, push the same Wi-Fi config to all of them, and asynchronously harvest their pairing keys. |
| Stateless Mobile | The mobile app does not persist BLE connection state. Admiral devices are the source of truth. |
| Minimal MTU | Payloads fit within a 512-byte ATT MTU, falling back to 23-byte MTU with chunked transfers if needed. |
| Secure Link | All sensitive data (Wi-Fi passwords, pairing keys) is protected via application-layer X25519 ECDH key exchange and AES-GCM encryption. |
2. BLE Advertising & Discovery
2.1 Advertising Modes
Admiral devices advertise over BLE only when unprovisioned or when an error occurs before final setup completes.
| Mode | When Active | Interval | Connectable | Notes |
|---|---|---|---|---|
| Setup | Factory reset / first boot / no Wi-Fi config | 100 ms | Yes | Enters power-save after 30 min; wakes on button press. With TimeoutMode enabled, drops advertisement after 1 min with no peers or 5 min of connection inactivity. |
| Configuring | Wi-Fi credentials received, device connecting | 200 ms | Yes | Continues advertising state changes. |
| Error | Wi-Fi connected but cloud unreachable, etc. | 150 ms | Yes | Allows mobile app to reconnect and correct the network config. |
| Service | Claimed, holding cloud verification keys | On | Yes | Field service only; every session must authenticate with an access token (§4.11). Off if service mode is disabled or no keys have been received. |
2.2 Advertising Packet (ADV_IND — 31 bytes)
MAC Address: To avoid duplicate OUI collisions common in bulk generic microcontrollers, the device overrides the controller's primary physical MAC on boot with a Locally Administered Address (LAA) starting with 0x0A. The remaining 5 bytes are populated from a SHA-256 fingerprint of the true hardware serial number.
| Offset | Length | Type | Field | Value |
|---|---|---|---|---|
| 0 | 3 | 0x01 | Flags | 0x06 (LE General Discoverable) |
| 3 | 4 | 0x03 | 16-bit Service UUID | 0xFDA0 |
| 7 | 14 | 0xFF | Manufacturer Data | See §2.3 |
| 21 | 12 | 0x08 | Short Local Name | UTF-8 (e.g., ADM-QWERT123) |
The advertisement broadcasts the 16-bit short UUID 0xFDA0. The full 128-bit GATT service UUID is A0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F60 — 0xFDA0 is the little-endian byte-reversed form of the A0FD prefix. Mobile clients must use the full 128-bit UUID when discovering and connecting to GATT characteristics.
2.3 Manufacturer Specific Data (9 bytes payload)
Byte 0: Protocol Version — 0x01
Byte 1: Device Type — 0x01 (Standard), 0x02 (Pro)
Byte 2: Provisioning State — See §2.4
Byte 3: Capability Flags — Bitmask (Bit 3: LED, Bit 4: Display Framebuffer)
Byte 4–7: Device ID Hash — Last 4 bytes of SHA-256(hardware serial number)
Byte 8: Reserved — 0x00
The 2-byte Company Identifier is transmitted as a separate BLE Manufacturer Data header field and is not included in the 9-byte payload above.
2.4 Provisioning State Byte
| Value | State | Description |
|---|---|---|
0x00 | UNPROVISIONED | Ready for setup. BLE active. |
0x01 | WIFI_CONFIGURED | Credentials received, attempting to connect. |
0x02 | WIFI_CONNECTED | Connected to Wi-Fi. Resolving cloud connection. |
0x03 | CLOUD_PAIRED | Pairing key received from cloud, waiting for mobile confirmation. |
0x04 | FULLY_PROVISIONED | Mobile confirmed registration. BLE advertising shuts down. |
0xFF | ERROR | Setup failed. Check Error characteristic (§4.10). |
3. Provisioning State Machine
The provisioning lifecycle ensures the device can safely traverse from unconfigured to cloud-paired without getting stuck.
Organic Connections: The Admiral OS runs a background daemon monitoring active network interfaces. If an Ethernet cable is attached, or a previously saved Wi-Fi network reconnects organically during the setup loop, the device asynchronously advances to WIFI_CONNECTED (0x02) and then CLOUD_PAIRED (0x03), pushing notifications to the mobile app. The mobile app must listen for state notifications and dynamically skip the Wi-Fi configuration UI if the device is already online.
State Transitions
| From | To | Trigger |
|---|---|---|
UNPROVISIONED | WIFI_CONFIGURED | Mobile writes Wi-Fi credentials via Network Config characteristic |
UNPROVISIONED | WIFI_CONNECTED | Organic Ethernet or saved Wi-Fi connects |
WIFI_CONFIGURED | WIFI_CONNECTED | Successful Wi-Fi authentication and DHCP lease |
WIFI_CONNECTED | CLOUD_PAIRED | Admiral Cloud issues ephemeral pairing key |
CLOUD_PAIRED | FULLY_PROVISIONED | Mobile writes COMPLETE to Prov Control characteristic |
| Any | ERROR (0xFF) | Authentication failure, DHCP timeout, cloud unreachable |
FULLY_PROVISIONED | UNPROVISIONED | Physical factory reset (10-second pinhole button hold) |
Re-entering Setup
- Physical Factory Reset: Holding the pinhole reset button for 10 seconds clears all saved configurations (Wi-Fi credentials under
/admrl/conf/, custom local settings). The device rolls back toUNPROVISIONEDand restarts BLE advertising on next boot. - Setup Mode Recovery: If pairing is aborted or fails before full provisioning, the system remains in or reverts to setup mode and re-initializes the setup-mode BLE advertisement automatically.
4. GATT Service & Characteristic Definitions
4.1 Services
| # | Service Name | UUID | Type |
|---|---|---|---|
| 1 | Generic Access | 0x1800 | Standard BLE |
| 2 | Admiral Provisioning | A0FD0001-C2E1-4F9C-8D2A-0B6C3E4D5F60 | Custom Primary |
4.2 Characteristics
Base UUID: A0FD????-C2E1-4F9C-8D2A-0B6C3E4D5F60
| Name | Short UUID | Properties | Security | Description |
|---|---|---|---|---|
| Device State | 0010 | Read, Notify | Encrypted | Current provisioning and network status. |
| Command | 0011 | Write | Encrypt+Auth | Send commands (Identify, Scan Wi-Fi, etc.). |
| Cmd Response | 0012 | Read, Notify | Encrypt+Auth | Responses to commands, chunked. |
| Network Config | 0020 | Write | Encrypt+Auth | Push SSID / password / IP settings / link type. |
| Network Status | 0021 | Read, Notify | Encrypted | Current interface state (IP, link type, label). |
| Wi-Fi Scan | 0022 | Read, Notify | Encrypted | Paginated list of discovered SSIDs. |
| Pairing Key | 0030 | Read, Notify | App-Encrypted | Ephemeral token from the Admiral Cloud. |
| Prov Control | 0040 | Write | Encrypted | Session lifecycle (Start, Complete, Abort). |
| Session PubKey | 0050 | Read | None | Device's ephemeral X25519 public key. |
| Access Info | 0052 | Read | None | Whether the device is open or claimed, its device ID (claimed only) and a per-session challenge. See §4.11. |
| Access Token | 0080 | Write | Encrypted | A cloud-issued access token (claimed devices). See §4.11. |
| Service Request | 0081 | Write | Encrypted | Authenticated service commands (claimed devices). |
| Service Response | 0082 | Read, Notify | Encrypted | Responses to service commands and the access result. |
| Error Info | 00F0 | Read, Notify | Encrypted | Detailed error domains, codes, and messages. |
All Notify characteristics require the client to write 0x0001 to the CCCD descriptor 0x2902.
4.3 Device State — A0FD0010
20 bytes. Notified on any change.
| Byte(s) | Field | Values |
|---|---|---|
| 0 | Provisioning State | See §2.4 |
| 1 | Wi-Fi State | 0x00 Idle · 0x01 Scanning · 0x02 Connecting · 0x03 Connected · 0x04 Has IP |
| 2 | Cloud State | 0x00 Idle · 0x01 Connecting · 0x04 Key Ready |
| 3 | Last Error Code | 0x00 if none |
| 4–7 | Uptime | uint32_t, seconds |
4.4 Command & Response — A0FD0011 / A0FD0012
Write to 0011, read or subscribe to 0012.
Request format: [Cmd ID] [Seq Num] [Payload Length (uint16)] [Payload…]
Response format: [Cmd ID] [Seq Num] [Chunk Index (uint8)] [Total Chunks (uint8)] [Payload Slice…]
All responses use application-layer chunking (max 128 bytes per slice) to prevent truncation across varying MTU sizes.
Commands
| ID | Command | Payload | Description |
|---|---|---|---|
0x01 | IDENTIFY | Duration uint8 (seconds) | Triggers physical setup indicators and system display animation. |
0x02 | SCAN_WIFI | None | Triggers an async Wi-Fi network scan. |
0x03 | GET_WIFI_PAGE | Page number uint8 | Requests a page of scan results from A0FD0022. |
0x04 | GET_SAVED_WIFI | None | Returns a JSON array of previously saved network profiles. |
0x05 | GET_HARDWARE_INFO | None | Returns device metadata and active display topology. |
0x06 | GET_ETHERNET_CFG | None | Returns saved Ethernet IP/DHCP configuration. |
0x07 | GET_WIFI_IP_CFG | SSID string | Returns saved IP/DHCP configuration for a specific Wi-Fi network. |
Response Payloads
0x01(IDENTIFY) and0x03(GET_WIFI_PAGE): Empty acknowledgment. Chunk Index0x00, Total Chunks0x01, empty payload.0x02(SCAN_WIFI):[Status uint8 (0x00 = OK)] [Network Count uint8]0x04(GET_SAVED_WIFI): JSON array of saved network objects.[{ "ssid": "Network1", "priority": 100 }]0x05(GET_HARDWARE_INFO): JSON object with device metadata and display topology.{"mfg": "Admiral Edge","mdl": "ADM-DEVX","sn": "ADM-CA2B61A9","fw": "0.1.7","displays": "HDMI-A-1 @ 3840x2160 @ 60 Hz | DP-1 @ 1920x1080 @ 144 Hz"}noteHardware identifiers are transmitted via this command rather than the standard
0x180AGATT Device Information Service due to platform-specific system profile limitations.0x06(GET_ETHERNET_CFG): JSON IP settings object. An empty{}signifies default DHCP.{ "dhcp": false, "ip": "192.168.1.50/24", "gw": "192.168.1.1", "dns": "8.8.8.8" }0x07(GET_WIFI_IP_CFG): Same JSON format as0x06, scoped to the requested SSID.
4.5 Network Config — A0FD0020
Accepts a chunked, application-layer encrypted JSON payload containing network credentials or configuration actions.
Chunking
Payloads must be fragmented into 128-byte chunks with a ~30 ms delay between writes:
[Chunk Index (uint8, 0-based)] [Total Chunks (uint8)] [Payload Fragment…]
Reassembled payload (minimum 28 bytes):
[Nonce (12 bytes)] [AES-GCM Ciphertext + 16-byte Auth Tag]
Session Key Exchange
- Mobile reads the device's ephemeral X25519 public key from
A0FD0050. - Mobile derives the shared AES-256 secret via ECDH and transmits its own public key via the
PROV_CTRL STARTcommand (A0FD0040) to open the crypto session. - Mobile encrypts the JSON payload with AES-GCM and pushes the chunked blob.
Payload Actions
Payloads require an "action" key:
save — Push or update a network configuration.
{
"action": "save",
"priority": 100,
"type": "wifi",
"ssid": "MyNetwork",
"psk": "password",
"ip": "192.168.1.50/24",
"gateway": "192.168.1.1",
"dns": "8.8.8.8, 1.1.1.1"
}
- Set
"type": "ethernet"to skip Wi-Fi authentication and apply settings directly to the Ethernet adapter. - Transmitting
"psk": ""on an update preserves the existing stored passphrase while merging other fields. - Static
ipfields must use CIDR notation (e.g.,192.168.1.50/24). Bare IP addresses without a prefix length are rejected by the client before transmission.
delete — Remove a saved network profile.
{ "action": "delete", "ssid": "MyNetwork" }
reset — Wipe all Wi-Fi networks and reset Ethernet to DHCP.
{ "action": "reset" }
4.6 Network Status — A0FD0021
Returns the active network state across all concurrent live interfaces. Proactively notifies the client when an organic connection (Ethernet or saved Wi-Fi) is detected, allowing the mobile app to bypass the Wi-Fi setup step.
Format: [Interface Count (uint8)] followed by per-interface records:
[Link Type (uint8)] [IPv4 Address (4 bytes)] [RSSI (1 byte)] [Label Length (uint8)] [Label (UTF-8)]
- Link Type
0x02= hardwired Ethernet. Other values represent Wi-Fi. - If fully disconnected, the interface list is empty:
[0x00]. - SD-WAN failover events (gateway priority shifts from L4 health checks) update this characteristic and push a notification automatically.
4.7 Wi-Fi Scan Results — A0FD0022
Returns paginated results after a SCAN_WIFI command, triggered page-by-page with GET_WIFI_PAGE.
Header: [Page Number (uint8)] [List Count (uint8)]
Per network record (repeated List Count times):
[SSID Length (uint8)] [SSID (UTF-8)] [RSSI (int8)] [Security (uint8: 0x00=Open, 0x02=Encrypted)]
4.8 Pairing Key — A0FD0030
Returns the cloud pairing key sealed with the established session secret.
Format: [Nonce (12 bytes)] [AES-GCM Ciphertext + 16-byte Auth Tag]
The Device State characteristic (§4.3) transitions Cloud State to 0x04 (Key Ready) when the key is available. The mobile client decrypts it using the existing session secret.
Pairing key validity is tied to the device's installation lifecycle, not a time-based TTL. The key is invalidated immediately upon successful cloud account association to prevent replay attacks.
4.9 Provisioning Control — A0FD0040
Controls the session lifecycle.
| Byte(s) | Field | Values |
|---|---|---|
| 0 | Control Action | 0x01 START · 0x02 COMPLETE · 0x03 ABORT |
| 1–32 | Mobile Public Key | 32-byte X25519 public key (on START only) |
- START (
0x01): Transmits the mobile's X25519 public key and opens the encrypted session. Must be called before writing to any encrypted characteristic (Network Config). Establishing the session immediately on initialization also ensures the device can encrypt and push the Cloud Pairing Key even if it connects organically via Ethernet and skips the Wi-Fi setup. - COMPLETE (
0x02): Signals successful account registration. Triggers immediate shutdown of the BLE stack. - ABORT (
0x03): Tears down the active session. The device remains in or reverts to setup mode.
4.10 Error Info — A0FD00F0
Notified on failure. See §6 for the full error code table.
4.11 Service Access — A0FD0052 / 0080 / 0081 / 0082
A claimed device stays reachable over Bluetooth for field service, but only to someone the Admiral Cloud has authorised. Authorisation works while the device is offline.
Open and service modes. 0052 returns JSON:
{"v":2,"mode":"open|service","did":"<device uuid>","nonce":"<b64url>","confirm":true,"verified":false,"busy":false}
-
nonce: 16 random bytes (base64url, no padding), fresh at eachSTART. It is single use: any authentication attempt, good or bad, consumes it. -
confirm(open mode): the device has a display and requires the on-screen pairing code.verified: the current session has confirmed it. -
busy(service mode): another authenticated session is live, so aSTARTfrom a different client will be refused. -
open: the device is not claimed. Anyone in range may provision it, exactly as in §3. The pairing key is delivered. -
service: the device is claimed. A session does nothing until it presents a valid access token. The pairing key is never sent, and network details are not published to an unauthorised session.
Getting access.
- The console connects, reads
0050and0052, and starts a session (PROV_CTRL START, §4.9). Starting a session issues a freshnonce; read0052again. - The console asks the cloud for a token for that device, sending the
nonceand the session's public key. The cloud checks the signed-in user's permission on the device and signs a token for five minutes. - The console writes the token, encrypted and chunked, to
0080. The device verifies the signature with public keys it received when it last connected to the cloud, so no network is needed.
What a token binds. The device ID, the session's public key and the nonce. A captured token is useless on any other connection, and the nonce is single use. Tokens carry scopes, which the device enforces. Scopes are not hierarchical.
| Scope | Grants |
|---|---|
diag | Identify, hardware info, device info, workload status, diagnostics. |
network | Wi-Fi scan and saved networks, Ethernet and IP configuration, network changes. |
operate | Workload control and diagnostics mode, on-device screen control. |
admin | Power (reboot and power off), setup completion, unpair. |
Scopes follow the user's strongest capability on the device:
| Device capability | Scopes |
|---|---|
| View devices | diag |
| Operate devices | diag, operate, network |
| Delete devices | diag, operate, network, admin |
Tokens are valid for five minutes by default (15 minutes at most).
Service requests. After authentication the console sends encrypted requests to 0081 and receives encrypted responses on 0082. Request and response frames are tagged so a captured message cannot be replayed in the other direction, and each request carries a counter that must increase. Wi-Fi and Ethernet management uses the existing characteristics (0011, 0020), which now require the network scope on a claimed device.
One session at a time. While an authenticated session is live, a different client's START is refused and the owner is left untouched. A session is live for 30 seconds after its last request; the console sends a session.ping every 10 seconds to keep a quiet session, and a disconnect frees the device immediately, so a dropped link never locks anyone out. On a claimed device ABORT is ignored, because a write cannot be attributed to a connection and would let any client end the owner's session.
Pairing code (unclaimed devices with a screen). The key exchange on an unclaimed device is unauthenticated, so a device with a screen defends against a relay in range: it shows an 8 digit code derived from both public keys and the shared secret, and holds back the pairing key, network details and every command until the user types that code into the console and the console sends it in PROV_CTRL command 0x04 (VERIFY). The console first checks the typed code against its own derivation and sends nothing on a mismatch, because a relay has different keys on each side. The device ends the session after three wrong codes. Devices without a screen keep the open flow. Writing off to /admrl/conf/ble-verify disables the code for fleets that provision with an app that cannot send VERIFY.
Rotation and revocation. The cloud sends the device its current set of verification keys on every connection. Removing a key removes its tokens' validity from the fleet on next check-in. Five failed authentications in a row lock authentication on the device for 60 seconds; this counter is device-wide, not per client.
Turning it off. Writing off to /admrl/conf/ble-service-mode stops a claimed device advertising at all.
5. Identify & Display Integration
Admiral devices feature an LED and an integrated display.
When the mobile app writes the IDENTIFY command (0x01) to A0FD0011:
- The BLE daemon parses the requested duration (default: 10 seconds).
- The system LED switches to an active pulsing pattern.
- The device updates the system display, entering an active Pairing Animation (such as a pulsing connection ring, animated PIN, or avatar).
- After the specified duration, the LED and display revert to their default states.
This ensures a user in a room with multiple identical Admiral devices can identify exactly which unit they are configuring.
6. Error Codes
Delivered via the Error Info characteristic (A0FD00F0).
| Domain | Code | Category | Description | Resolution |
|---|---|---|---|---|
0x11 | 0x11 | Wi-Fi | SSID Not Found | Trigger a new scan and prompt the user. |
0x11 | 0x12 | Network | Configuration / Payload Error | Check credential payload formatting or valid IP ranges. |
0x11 | 0x13 | Network | DHCP Timeout | Connected to network but failed to obtain IP. Verify the backend router. |
0x11 | 0x14 | Wi-Fi | Authentication Failed | Password rejected by access point. Prompt user to re-enter. |
0x11 | 0x15 | Network | Delete Network Failed | Verify the target SSID matches stored profiles. |
0x11 | 0x16 | Network | Reset Network Failed | OS failed to safely clear previous configuration state. |
0x21 | 0x21 | Cloud | Unreachable | Verify the network has internet access and retry. |
0x21 | 0x22 | Cloud | Key Generation Failed | Admiral Cloud backend failure. |
0x30 | 0x30 | System | Display Error | System could not render to display. |
7. Timing & Constraints
| Parameter | Value | Description |
|---|---|---|
| Max Concurrent BLE Clients | 1 | Admiral device accepts only one BLE central at a time. |
| Setup Timeout (No Peers) | 60 s | With TimeoutMode enabled, drops advertisement after 1 min with no peer connections. |
| Setup Timeout (With Peers) | 5 min | Drops advertisement after 5 min of connection inactivity. |
| Connection Idle Timeout | 30 s | Device disconnects if no writes occur within 30 seconds. |
| Wi-Fi Scan Timeout | 5 s | Scan results must be returned within 5 seconds. |
| Identify Default Duration | 10 s | LED/splash sequence duration if not explicitly specified. |
Power-Save Supervisor
Admiral OS uses a multi-tiered power-save supervisor to balance discoverability with radio-silence best practices:
- Unconnected Idle (1 min): If no peer connection is formed within 1 minute while
TimeoutModeis active, the supervisor shuts down BLE advertising and setsadvertising: falsein system state. - Connected Inactivity (5 min): If a client connects to the GATT server but commits no writes (commands or config payloads) for 5 continuous minutes, the supervisor disconnects the link, tears down the advertiser, and enters a low-power quiet state.
- Activity Reset: Any standard activity — new central connection, payload chunk write, identify command — resets the supervisor countdown to zero.
8. Security
Application-Layer Encryption (X25519 ECDH + AES-GCM)
BLE OS-level bonding is bypassed in favor of application-layer encryption to avoid cross-platform bonding complexities.
- The Admiral device generates an ephemeral X25519 keypair on boot and exposes its public key on
A0FD0050. - The mobile app generates its own ephemeral keypair, derives the AES-256 shared secret via ECDH, and transmits its public key strictly via the
PROV_CTRL STARTcommand. This ordering prevents crypto-deadlocks on organic network connections. - Subsequent encrypted payloads (Wi-Fi credentials) omit public keys entirely — they are sealed with AES-GCM using the negotiated secret only.
- Wi-Fi credentials are never readable back to the mobile app over BLE.
Post-Provisioning Access
An unclaimed device accepts any session so that a fresh device can be set up with no prior trust. Once a device is claimed it is sealed: pairing is closed, the pairing key is never sent again, and every capability needs a cloud-issued, session-bound token that the device validates offline (§4.11). A fault on the device cannot reopen it. A device that has not received verification keys from the cloud does not advertise after it is claimed, as before.
A session that was already open when the claim happened stays open until it disconnects, so the setup flow can finish. Resetting the device to factory state (the pinhole button, §3) returns it to the open, unclaimed mode.
9. Multi-Device Batch Provisioning
For deployments with many Admiral devices in the same space:
- Mobile app scans for ~10 seconds, accumulating unique Device IDs from advertisement packets.
- User selects multiple target devices in the mobile app.
- Mobile app provisions devices sequentially: connect → write Wi-Fi config → disconnect → next device.
- Devices connect to Wi-Fi and the Admiral Cloud in parallel asynchronously.
- Mobile app round-robins reconnections to read the Pairing Key from each device as they become ready.
- Mobile app sends a single batch API request to the Admiral Cloud with all collected keys.
- Mobile app round-robins reconnections to send
PROVISIONING_COMPLETEto all devices, triggering their BLE shutdown.
Do not hold a BLE connection open while a device is working through a long Wi-Fi connection phase. Disconnect, move to the next device, and return. Holding connections blocks the single-client limit unnecessarily.
10. Implementation Notes
Firmware (admiral-os-ble-daemon)
- Monitor provisioning state on startup. Only enable the HCI interface if
provisioning_status != FULLY_PROVISIONED. - The
IDENTIFYcommand should trigger the device's physical identity indicators. - MAC Address Override: Before initiating any Bluetooth services, open a raw HCI management socket (
AF_BLUETOOTH,BTPROTO_HCI) to issue the physical controller address override, enforcing unique LAAs across hardware variants. - GATT Cache Purging on Disconnect: To mitigate the iOS Bluetooth GATT service caching bug (where iOS fails to discover updated characteristics on reconnect), remove the device from the system's active device registry and momentarily cycle the advertiser. This forces iOS to prune its GATT service cache.
Mobile Application
- iOS: Use
CBCentralManager. Android: UseBluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner(). - Filter scans on Service UUID
0xFDA0. - Subscribe to Device State (
A0FD0010) and Network Status (A0FD0021) notifications immediately after connecting — both can fire before the UI has progressed past the discovery screen, particularly when Ethernet is already plugged in. - Always send
PROV_CTRL STARTwith the mobile's X25519 public key before writing to any encrypted characteristic.
11. Data Serialization
-
Endianness: Little-endian for all multi-byte integers.
-
Strings / JSON: UTF-8 encoded.
-
Chunking: Large payloads (such as chunked command responses) use application-layer chunking to work around the BLE GATT 512-byte hard limit and OS driver-level packet queue limits. Maximum fragment size: 128 bytes. Enforce a ~30 ms delay between chunk writes.
Byte 0: Chunk Index (0-based)Byte 1: Total ChunksByte 2…: Payload Fragment