Home > 3rd-Party Integration > T-Dongle Display Media

T-Dongle Display Media

The T-Dongle C5 exposes an optional BLE service for assigning a built-in logo, firmware-rendered status text, or an SD-card image/GIF to its Idle, Scanning, and Attacking display contexts. The service is absent on every other Biscuit model and is discovered after connection rather than advertised.

GATT service

Role UUID Properties Security
Service fb1e5001-54ae-4a28-9f74-dfccb248601d Service —
Info fb1e5002-54ae-4a28-9f74-dfccb248601d Read Open
Control fb1e5003-54ae-4a28-9f74-dfccb248601d Write, Notify Encrypted
Data RX fb1e5004-54ae-4a28-9f74-dfccb248601d Write, Write Without Response Encrypted

Read Info before enabling Control notifications. Info does not require an encrypted link and returns a compact UTF-8 JSON object:

{"v":1,"sd":true,"width":160,"height":80,"maxBytes":1048576,"maxFrames":80,"maxFiles":32,"maxFps":10,"maxDurationMs":15000}

Only subscribe to Control when the user opens display management. Its CCCD and all Control/Data writes require an encrypted link. A bonded client should honor the standard Service Changed indication and rediscover services after a firmware update.

Control protocol

Each Control request is one compact UTF-8 JSON object in a single write and contains protocol version v:1, a positive integer request id, and op. Each logical response or event is also one JSON object, but its UTF-8 bytes may span sequential notifications at the negotiated ATT payload limit. Reassemble one complete object before parsing it; objects are never interleaved. A direct response echoes id and contains either ok:true or ok:false plus error.

Operation Request fields Result
get_config — enabled and the three assignments
list — Zero or more asset events followed by list_end
set_enabled enabled boolean Updates the backlight preference
set_assignment context, kind, optional file Validates/caches, commits, and returns canonical file for an asset
begin_upload name, replace, size, sha256 session, resumeOffset, windowBytes, canonical name, optional committed
commit_upload session Validates and atomically installs staging data; recent matching commits are idempotent
cancel_upload session Removes staging data
delete_asset name Resets affected assignments, removes the asset, and returns canonical name plus reset contexts

context is idle, scanning, or attacking. Assignment kind is logo, status, or asset; an asset assignment also contains file.

{"v":1,"id":1,"op":"config","ok":true,"enabled":true,"idle":{"kind":"logo"},"scanning":{"kind":"status"},"attacking":{"kind":"asset","file":"warning.bma"}}

The list is metadata-only. The T-Dongle never sends media or thumbnails back to a client.

{"v":1,"id":2,"event":"asset","name":"warning.bma","animated":true,"size":48321,"frames":24,"durationMs":2400}
{"v":1,"id":2,"event":"list_end","ok":true}

Stable error codes include busy, not_encrypted, no_sd, sd_full, storage_error, duplicate, invalid_request, invalid_asset, hash_mismatch, offset_mismatch, not_found, no_memory, internal, timeout, and library_full.

no_sd is reserved for a card that was not mounted, and it restricts the media library only: list, begin_upload, and delete_asset return it, and set_assignment to an asset returns not_found. get_config, set_enabled, and set_assignment to logo or status stay available, so keep the screen on/off control reachable when sd is false. sd_full reports known insufficient space, while storage_error reports a filesystem failure on an otherwise mounted card. A storage failure may include an additive stage value of mkdir, open, write, rename, or delete. Clients should branch on the stable error code and may use stage for diagnostics. Cryptographic failures return internal with stage:"sha"; no_memory denotes an allocation failure.

Upload transport

begin_upload supplies the canonical .bma filename, replacement choice, complete byte count, and lowercase 64-character SHA-256 digest. Only one bulk session can be active. A matching interrupted upload can resume during the same boot for five minutes.

Every Data RX value starts with this little-endian header:

Offset Type Meaning
0 uint32 Session ID
4 uint32 Absolute file offset
8 bytes BMA payload

Use Write Without Response with the platform’s backpressure signal when it is available. Otherwise, send sequential writes with response. Derive payload size from the negotiated maximum write length minus eight bytes. Stop at each 8 KiB window and wait for a cumulative acknowledgement:

{"v":1,"event":"ack","session":305419896,"nextOffset":8192}

A gap produces a NACK whose expectedOffset is the next byte the device will accept. commit_upload verifies received length, SHA-256, all BMA bounds, and CRC32 before replacing the destination. Scans, attacks, wardrives, portals, other SD bulk activity, and OTA are mutually exclusive with media transfer.

The most recent successful commit remains replayable for five minutes during the same boot. A matching begin_upload then returns committed:true and a resumeOffset equal to the file size; repeat commit_upload with that session to recover a lost response without retransmitting the file.

Biscuit Media Asset (.bma)

All integers are little-endian. Files are at most 1 MiB and encode a 160×80 RGB565 canvas.

Header (16 bytes)

Field Type Constraint
Magic 4 bytes ASCII BMA1
Width uint16 160
Height uint16 80
Frame count uint16 1 through 80
Flags uint16 Bit 0 animated; all other bits zero
Total duration uint32 0 static; at most 15000 ms animated

Frame record

Each frame has a 15-byte header followed by its payload:

Field Type Constraint
Delay uint16 0 static; at least 100 ms animated
X, Y two uint16 Changed-rectangle origin
Width, height two uint16 Non-zero rectangle inside 160×80
Encoding uint8 0 raw, 1 RLE
Payload length uint32 Exact following byte count

Frame zero covers the full canvas. Subsequent animated records use the smallest changed rectangle from already-composited frames. Raw encoding is row-major little-endian RGB565 and has exactly width * height * 2 bytes. RLE is a sequence of (runLength:uint16, pixel:uint16) pairs with non-zero runs that cover the rectangle exactly. Choose the smaller encoding per frame.

The final four bytes are the little-endian IEEE CRC32 of every preceding byte. The sum of frame delays equals the header duration. This is a valid all-black static fixture:

424d4131a00050000100000000000000000000000000a0005000010400000000320000c7ec2d1f

Storage and naming

Assets live under /display/assets/; staging data uses /display/.upload/. Filenames are case-insensitively unique and use a .bma extension. The ASCII basename is 1–48 bytes and may contain letters, numbers, spaces, _, and -. The library contains at most 32 valid files.

The device stores only display-enabled state and the three assignments in NVS. Missing SD, missing/corrupt media, and allocation failure always fall back to the compiled Biscuit logo.