GATT Reference
Home > 3rd-Party Integration > GATT Reference
GATT Reference
The complete BLE GATT surface a 3rd-party client needs to drive a Biscuit. Every value on this page is stable across the public firmware line and matches what the iOS and Android companion apps consume.
Advertising
| Field | Value |
|---|---|
| Local name | Biscuit |
| Service UUID in advertisement | 4fafc201-1fb5-459e-8fcc-c5c9c331914b |
The local name is identical on every device; match on the service UUID. Biscuit does not include a per-device suffix in the advertised name.
Primary Service
Service UUID: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
| Characteristic | UUID | Properties | Direction | Use |
|---|---|---|---|---|
| Command | beb5483e-36e1-4688-b7f5-ea07361b26a8 |
Write, Write Without Response | Central to device | Send CMD: lines |
| Response | beb5483e-36e1-4688-b7f5-ea07361b26a9 |
Notify | Device to central | Receive RSP, STATUS, DATA, ERROR, TIME lines |
| Status | beb5483e-36e1-4688-b7f5-ea07361b26aa |
Read | Device to central | Pollable JSON snapshot of device health |
| Settings | beb5483e-36e1-4688-b7f5-ea07361b26ab |
Read, Write, Notify | Bidirectional | Read or update device configuration as JSON |
| Security | beb5483e-36e1-4688-b7f5-ea07361b26ac |
Read, encrypted read required | Device to central | Optional encrypted-link setup and verification |
Notifications must be enabled. Until the Response characteristic’s CCCD is set to
0x0001, the device does not send anything in response to commands. Subscribing is a prerequisite, not optional.
Device Information Service
Standard BLE Device Information Service (0x180A) exposes:
| Characteristic | UUID | Content |
|---|---|---|
| Manufacturer Name | 2A29 |
Biscuit Shop |
| Model Number | 2A24 |
Board name, such as Biscuit Pro, Biscuit Ultra, Biscuit DIY, Nyan, or Crumb |
| Firmware Revision | 2A26 |
Primary firmware version, e.g. v1.4.5 |
| Software Revision | 2A28 |
Scanner firmware version on Pro/Ultra |
| Serial Number | 2A25 |
Per-device serial when exposed and provisioned |
On Pro/Ultra, 2A26 is the WROOM revision and 2A28 is the C5 revision. On
single-chip DIY, Nyan, and Crumb devices, the C5 owns 2A26 and 2A28 is
absent.
T-Dongle Display Media Service
This optional service exists only on the T-Dongle C5 and is not included in the advertising packet.
Service UUID: fb1e5001-54ae-4a28-9f74-dfccb248601d
| Characteristic | UUID | Properties | Security | Use |
|---|---|---|---|---|
| Info | fb1e5002-54ae-4a28-9f74-dfccb248601d |
Read | Open | Protocol version, SD state, display dimensions, and limits |
| Control | fb1e5003-54ae-4a28-9f74-dfccb248601d |
Write, Notify | Encrypted | Configuration, filename listing, upload control, assignment, deletion |
| Data RX | fb1e5004-54ae-4a28-9f74-dfccb248601d |
Write, Write Without Response | Encrypted | Offset-addressed Biscuit Media Asset upload |
Info returns compact JSON containing v, sd, width, height, maxBytes,
maxFrames, maxFiles, maxFps, and maxDurationMs. Reading it does not
request pairing. Subscribe to Control only when the user starts display media
management; the encrypted subscription lets the platform perform its normal
authentication flow.
Control requests are compact JSON objects containing v:1, a positive id,
and one of get_config, list, set_enabled, set_assignment,
begin_upload, commit_upload, cancel_upload, or delete_asset. File lists
return filename and metadata events only; media bytes and thumbnails are never
sent back to the client.
Every Data RX value begins with little-endian sessionId:uint32 and
absoluteOffset:uint32, followed by file bytes. The device acknowledges each
8192-byte window over Control. Control JSON responses may span sequential
notifications at the negotiated ATT payload limit, so reassemble a complete
object before parsing. A committed .bma file is at most 1 MiB and contains
160x80 little-endian RGB565 frames, using raw pixels or simple run-length
encoding, followed by CRC32. See
T-Dongle Display Media for the complete binary
and JSON schemas.
Connection Parameters
| Parameter | Value |
|---|---|
| MTU requested by device | 517 |
| Recommended client MTU | At least 247 |
| Data Length Extension | Device requests 251-byte PDUs on connect |
| Connection interval | 30 to 60 ms preferred |
| Slave latency | 0 |
| Supervision timeout | 8000 ms |
Below an MTU of 247, large notifications during wardrive streams will be fragmented across multiple ATT packets. Most BLE stacks reassemble these transparently, but throughput drops.
Message Framing
Every text-carrying characteristic uses the same line-oriented format:
TYPE:SUBTYPE:PAYLOAD\n
Every line ends with a single line-feed (\n, 0x0A). The payload may be empty; the trailing colon is still present (for example, CMD:stopscan:).
Message types
| Type | Direction | Meaning |
|---|---|---|
CMD |
Central to device | A command |
RSP |
Device to central | Acceptance of a CMD (sent within 100 ms) |
STATUS |
Device to central | Execution-state change |
DATA |
Device to central | Live scan record |
ERROR |
Device to central | Command rejected or runtime error |
TIME |
Device to central | Time-sync request |
A RSP means “command accepted”, not “command finished”. For commands that produce output (scans, attacks, wardrive), the actual completion signal arrives as a subsequent STATUS: line.
Status codes
STATUS: lines carry a numeric code in the SUBTYPE position:
| Code | Meaning |
|---|---|
0 |
Booting |
1 |
Ready |
2 |
Busy (a scan or attack is running) |
3 |
OK |
4 |
Warning |
5 |
Error |
6 |
Scan complete |
STATUS:1:Ready is the signal that no further DATA: lines will arrive for the previous operation.
Notification Packing
During wardrive and other high-throughput scans, the device packs multiple DATA: records into a single BLE notification to reduce radio overhead. Records inside a packed notification are newline-delimited:
DATA:AP:Network1,AA:BB:CC:DD:EE:FF,6,-58,[WPA2_PSK]
DATA:BT:Device1,11:22:33:44:55:66,-72,BLE
DATA:AP:Network2,22:33:44:55:66:77,11,-65,[WPA3_PSK]
The pack buffer flushes on any of:
| Trigger | What happens |
|---|---|
| Buffer reaches 240 bytes | Immediate flush |
| 50 ms elapsed since first buffered record | Timer-based flush |
A non-DATA message is queued |
Immediate flush, preserving order |
Always split each notification on \n and process each line independently. Never assume one notification equals one record.
Optional link security
Read Security once before subscribing to normal traffic when you want the platform to offer LE Secure Connections Just Works bonding. A successful read returns:
{"v":1,"mode":"optional","encrypted":true,"bonded":true,"bonds":1}
This protected read is the only pairing trigger. Do not call createBond() or issue a second read at the same time. If the user declines or the read fails, continue using the open Command, Response, Status, and Settings characteristics. Older firmware simply omits Security.
Just Works encrypts the link but does not verify the user’s or device’s identity. For silent reconnects, remember a bond only after Security reports bonded:true.
Status Characteristic JSON
The Status characteristic returns a JSON object summarizing current device health. It is safe to read at any time. The iOS app normally polls every 15 seconds, or every five seconds while a diagnostic pill is enabled in the foreground.
Pro/Ultra after both chips advertise protocol-3 update control:
{"status":1,"battery":85,"c5Connected":true,"c5State":1,"wroomPartition":"ota_0","c5Partition":"ota_1","otaProtocol":3}
Single-chip initial value:
{"status":0,"battery":-1,"sd":false,"c5Partition":"ota_0","otaProtocol":3}
Full schema (optional keys appear only once the relevant subsystem reports):
| Key | Type | Meaning |
|---|---|---|
status |
int | Shared status: 0 Booting, 1 Ready, 2 Busy, 3 OK, 4 Warning, 5 Error, 6 Scan complete |
battery |
int | Battery percentage 0 to 100, or -1 if not yet measured |
c5Connected |
bool | Optional on Pro/Ultra. Scanner subsystem link alive. |
c5State |
int | Optional on Pro/Ultra. Scanner state: 0 Booting, 1 Ready, 2 Busy, 6 Scan complete. |
wroomHeap |
[free, total] |
Optional on Pro/Ultra. Present when the device is reporting heap. |
c5Heap |
[free, total] |
Optional on Pro/Ultra. |
temperatureC |
number or null | Optional in updated v1.6.0 Pro/Ultra/DIY builds. C5 temperature in Celsius; null when invalid or stale. This is not a measured WROOM temperature. |
wroomCpuMhz |
int or null | Optional. Actual WROOM CPU frequency in MHz; null on single-chip devices or when unavailable. |
c5CpuMhz |
int or null | Optional. Actual C5 CPU frequency in MHz; null when unavailable or its report is stale. |
sd |
bool | Optional mounted state. Single-chip devices may report both true and false. |
op |
string | Optional. pktmon or eapol while an SD-backed capture is running. |
wroomPartition |
string | Optional. Running WROOM slot: ota_0 or ota_1. |
c5Partition |
string | Optional. Running C5 slot; absent until known on a dual-chip device. |
otaProtocol |
int | Optional. 2 supports base64 credentials; 3 adds explicit normal/force update mode. Dual-chip devices report 3 only when both chips support it. |
lastOtaResult |
string | Optional. running, success, error, or cancelled. |
lastOtaRoute |
string | Optional. direct or c5_uart. |
lastOtaCode |
string | Optional. Stable lower-snake-case diagnostic code. |
Temperature and C5 frequency become null after five seconds without a fresh reading or report respectively. Their validity is independent: a sensor failure can leave a usable CPU frequency. Clear displayed values when fields are missing or null, on disconnect, and when status responses stop.
Treat the JSON keys as opaque string identifiers. Field values may evolve across firmware releases; the keys themselves are stable across the public firmware line.
Single-chip DIY, Nyan, and Crumb devices omit the C5-link fields and report only
c5Partition among the partition keys. Pro and Ultra can report both chip
slots. Older firmware omits these keys; clients should display an
unknown/not-reported state rather than assuming ota_0.