Overview
This tool calculates BLE Mesh DFU duration based on Zephyr RTOS source code analysis. The calculation models the actual LPN polling behavior, Friend queue management, and segment-level data transfer.
Step 1: Calculate Negotiated Chunk Size
Formula: chunkSize = MIN(all RX_SEG_MAX) × 12 - 7
- Takes the smallest RX_SEG_MAX among Distributor, Friend, and LPN
- Each BLE Mesh segment carries 12 bytes of application payload
- Subtracts 7 bytes for BLOB chunk overhead (1-byte opcode + 2-byte chunk index + 4-byte MIC)
Step 2: Calculate Segments Per Chunk
Formula: segmentsPerChunk = ⌈(7 + chunkSize) / 12⌉
- Includes the 7-byte overhead in the total chunk SDU length
- Divides by 12 bytes per segment and rounds up
Step 3: Calculate Max Chunks Per Block Report
Formula: maxChunks = MIN(DESIRED_CHUNKS_PER_REQUEST, ⌊Friend Queue Size / segmentsPerChunk⌋)
- Limited by both user's desired chunks per request AND Friend node's queue capacity
- LPN cannot request more chunks than Friend can buffer
- Actual Zephyr implementation also caps at CONFIG_BT_MESH_BLOB_SRV_PULL_REQ_COUNT (default: 4)
- This tool uses DESIRED_CHUNKS_PER_REQUEST as the effective limit
Step 4: Calculate Effective Poll Timeout
Important: During active DFU operations, Zephyr caps the LPN poll timeout at 1 second regardless of CONFIG_BT_MESH_LPN_POLL_TIMEOUT.
Formula: effectivePollTimeout = min(POLL_TIMEOUT_MAX, 1000ms)
- For DFU duration estimation, this tool uses fixed default values:
- LPN_POLL_TIMEOUT = 80 (8 seconds)
- LPN_RECV_DELAY = 100ms
- These defaults result in POLL_TIMEOUT_MAX ≈ 5.9 seconds
- During active DFU, the effective poll timeout is capped at 1000ms
- Source:
zephyr/subsys/bluetooth/mesh/lpn.c:122 - if (bt_mesh_tx_in_progress()) return MIN(POLL_TIMEOUT_MAX, 1000)
Step 5: Calculate Time Per Block Report Cycle
Formula: 100ms + effectivePollTimeout + (totalSegments × 200ms) + BLOB_TIMEOUT
Based on packet capture analysis, each Block Report cycle consists of these phases:
Phase 1: Immediate Poll After Block Report
Time: ~100ms
- LPN sends unsegmented Block Report (9 bytes for 4 chunks)
- LPN immediately polls Friend via
bt_mesh_lpn_poll()
- Friend responds with "no data" (chunks not ready yet)
- Source:
zephyr/subsys/bluetooth/mesh/blob_srv.c:205
Phase 2: Wait for Chunks
Time: 1000ms (capped during active DFU)
- LPN waits 1 second before next poll (Zephyr caps poll timeout during active transmission)
- During this wait: Distributor sends chunks to Friend (5-6 seconds for 4 chunks)
- Friend receives all segments and queues them
- When LPN polls again, Friend has data ready
Phase 3: Segment Reception (Friend → LPN)
Time: totalSegments × 200ms
- Total Segments: maxChunks × segmentsPerChunk
- Friend sends ONE segment per poll with
md=1 flag
- Each segment delivery cycle: poll → segment → next poll = ~200ms (measured)
- NOT 100ms as initially modeled - packet capture shows actual timing is 200ms
- Continues until all segments delivered (
md=0)
Phase 4: BLOB Report Timeout
Time: BLOB_REPORT_TIMEOUT (default: 10s)
- After receiving last segment, LPN waits before sending next Block Report
- Timer is reset on every completed chunk
- If timeout expires with missing chunks, LPN sends report listing missing chunks
- Source:
zephyr/subsys/bluetooth/mesh/blob_srv.c:802
Step 6: Calculate Total DFU Time
Formula:
- Block Reports Needed: ⌈totalChunks / maxChunks⌉
- Single LPN Time: blockReportsNeeded × blockReportCycleMs × 1.15
- Multiple LPNs: singleLpnTime × numTargets (sequential unicast)
- 15% Overhead: Accounts for retransmissions, network collisions, timing variations, and protocol overhead
Example Calculation (Default Values)
Configuration:
- Firmware: 262144 bytes (256 KB)
- Queue Size: 46 segments
- RX_SEG_MAX: 10 (all nodes)
- DESIRED_CHUNKS_PER_REQUEST: 4
- BLOB_REPORT_TIMEOUT: 10s
Fixed Defaults (not user-configurable in UI):
- LPN_POLL_TIMEOUT: 300 (30 seconds)
- LPN_RECV_DELAY: 100ms
- FRIEND_RECV_WIN: 255ms
Calculated:
- Chunk size: 113 bytes
- Segments per chunk: 10
- Max chunks (from queue): 4 (46 / 10 = 4.6)
- Max chunks (actual): MIN(4 desired, 4 from queue) = 4
- Total segments per report: 40
- POLL_TIMEOUT_MAX: ~5.9s (from defaults)
- Effective poll timeout during DFU: 1.0s (capped)
- Total chunks needed: 2320
- Block reports needed: 580
Time per Block Report (packet capture based):
- Immediate poll: 0.1s
- Wait for chunks: 1.0s (Zephyr DFU cap)
- Segment reception: 8.0s (40 × 0.2s)
- BLOB timeout: 10.0s
- Total: 19.1s
Total DFU Time (1 LPN):
580 × 19.1s × 1.30 = 14,393s = 03:59:53
Important Notes
- Calculation is based on Zephyr RTOS 3.x implementation and validated against packet capture analysis
- LPN poll timeout during DFU: Zephyr automatically caps poll timeout at 1 second during active transmissions, regardless of CONFIG_BT_MESH_LPN_POLL_TIMEOUT setting
- Segment delivery timing (200ms): Measured from actual packet captures, not theoretical estimates
- Assumes unicast DFU mode (targets updated sequentially)
- Actual times may vary based on RF environment, interference, network topology, and retransmissions
- The 30% overhead is an engineering estimate - real-world overhead may differ
- For best accuracy, validate with actual DFU measurements in your deployment environment
- Non-configurable parameters: LPN_POLL_TIMEOUT and LPN_RECV_DELAY use fixed defaults (80/100ms) for DFU estimation as they have minimal impact on actual DFU duration