v157 · web bluetooth
Chunk planner
Every ATT write without response costs a 3-byte header and one connection event, whether it carries 20 bytes or 500. This works out what a given payload costs at a given write size — and what the same payload costs if you keep assuming 20.
Plan a transfer
The write size is the payload the link can carry, which is the ATT MTU minus the 3-byte ATT header. That is why maxWriteWithoutResponseSize is 20 when the MTU is the 23-byte default — and why reading it beats assuming it.
| write size | packets | header bytes | overhead | vs 20 bytes |
|---|
Packets needed, to scale
code path
const max = server.maxWriteWithoutResponseSize ?? 20; // 20 is the floor
for (let offset = 0; offset < payload.byteLength; offset += max) {
await characteristic.writeValueWithoutResponse(
payload.subarray(offset, offset + max)
);
}
The ?? 20 matters: on a browser without the property the expression is undefined, and subarray(offset, offset + undefined) silently produces an empty slice — a loop that writes nothing forever.