The Problem: Developers calculate header compression savings but forget fragmentation overhead when payloads exceed 802.15.4 frame size.
Scenario: A sensor sends a 100-byte JSON payload every 60 seconds over Thread (802.15.4 + 6LoWPAN).
Developer’s calculation:
802.15.4 frame: 127 bytes max
MAC header: 25 bytes
6LoWPAN compressed IPv6+UDP: 6 + 4 = 10 bytes
Available payload: 127 - 25 - 10 = 92 bytes
Payload: 100 bytes
Conclusion: "100 bytes fits, one frame needed"
Reality check — Fragmentation required:
First Fragment:
MAC header: 25 bytes
6LoWPAN FRAG1 dispatch: 4 bytes (datagram_size + datagram_tag)
6LoWPAN compressed headers: 10 bytes
Payload fragment: 127 - 25 - 4 - 10 = 88 bytes
Second Fragment:
MAC header: 25 bytes
6LoWPAN FRAGN dispatch: 5 bytes (datagram_size + datagram_tag + offset)
Payload fragment: 127 - 25 - 5 = 97 bytes (but we only need 100-88=12)
Total overhead: 25+25+4+5 = 59 bytes
Actual transmitted: 127 + (25+5+12) = 169 bytes for 100-byte payload
Efficiency comparison:
| Payload efficiency |
92/127 = 72.4% |
100/169 = 59.2% |
| Radio-on time |
4.1 ms |
5.4 ms (2 frames) |
| Energy cost |
1× |
1.32× |
| Collision probability |
Low (1 frame) |
Higher (2 frames) |
Why fragmentation hurts:
- Duplicate overhead: MAC header repeated for every fragment (25 bytes × 2 = 50 bytes)
- Fragment header: FRAG1 (4 bytes) + FRAGN (5 bytes) = 9 bytes extra
- All-or-nothing: If any fragment is lost, entire datagram is lost (receiver discards partial datagrams)
- Buffer pressure: Receiver must buffer fragments until complete datagram arrives
Real-world measurements (100-byte payload):
Without fragmentation workaround:
Packet loss: 3% per frame
Effective datagram loss: 1 - (0.97 × 0.97) = 5.9% (both fragments must succeed)
Battery drain: 5.4 ms radio-on × 1,440 messages/day = 7.8 seconds/day
With payload reduction to 90 bytes (fits in one frame):
Packet loss: 3% per frame
Effective datagram loss: 3% (single frame)
Battery drain: 4.1 ms × 1,440 = 5.9 seconds/day (24% savings)
The Fix: Design application payloads to fit 6LoWPAN single-frame budget:
Target single-frame payload:
127 (802.15.4 max)
- 25 (MAC header)
- 6 (6LoWPAN compressed IPv6 in best case)
- 4 (6LoWPAN compressed UDP in best case)
___
= 92 bytes maximum safe payload
Practical guideline: Keep application payload ≤80 bytes to leave margin for:
- Less-optimal compression scenarios
- Optional headers (security, QoS)
- Address variations
Application Design Strategies:
| Binary encoding |
JSON: {"temp":23.5,"hum":67.2} (26 bytes) |
Binary: struct.pack('ff', 23.5, 67.2) (8 bytes) |
Replace JSON with binary |
| Compression |
Text: “Temperature is 23.5C” (23 bytes) |
DEFLATE: 15 bytes |
Use zlib for text |
| Differential |
Send full reading every time |
Send delta: +0.2 instead of 23.5 |
Transmit differences |
| Batching |
Send 10 readings individually (10 frames) |
Send 10 readings in one 100-byte payload (2 frames) |
Aggregate before sending |
Lesson: 6LoWPAN compression saves headers but can’t prevent fragmentation if your application payload is too large. Design application protocols to fit in ~80 bytes to avoid fragmentation entirely, or accept the 30%+ energy penalty and higher loss rate.