BLE Periodic Advertising Synchronization: Acquisition Timeouts, Lost Sync and Recovery

Use separate controls for finding a periodic advertiser, detecting loss of an established synchronization and deciding that application data is stale. Cancel an expired acquisition through the stack’s documented lifecycle, wait for cancellation to finish, and only then start a replacement attempt. A parameter named synchronization timeout is not automatically an acquisition deadline.

This guide uses Zephyr v3.7.0 host APIs and source as a concrete implementation boundary. The controller must support the required periodic-advertising features, and its firmware version, PHY capabilities and resource limits must be recorded. It covers ordinary periodic-advertising synchronization, not periodic advertising with responses or a generic BLE connection-retry strategy.

Define three clocks with different meanings

The Zephyr v3.7.0 GAP API explicitly says periodic-sync creation keeps looking until synchronization succeeds or deletion cancels it; it recommends an application timeout when bounded acquisition is required. The sync parameter’s timeout is separately documented in units of 10 milliseconds, with a range of 100 to 163,840 milliseconds.

  • Acquisition deadline: how long the product will spend trying to acquire this candidate before abandoning the attempt.
  • Synchronization timeout: the stack/controller parameter governing synchronization loss behavior, using the documented units and limits.
  • Application freshness limit: how old the last valid payload may become before the product stops presenting it as current.

Keep separate timestamps and reasons for all three. Choose the acquisition deadline from the expected advertising behavior and the product’s discovery budget. Choose data freshness from the application’s meaning of “current.” Neither should be inferred from the spelling of the controller parameter.

Periodic synchronization with distinct pending-cancellation and established-loss paths; pending cancellation invalidates the old handle and retries through delayed create results, while payload freshness is independent.
A canceled pending acquisition has no normal termination callback in Zephyr v3.7.0. Replacement creation uses bounded delayed attempts; application freshness has its own clock.

Qualify the candidate and the acquisition request

Keep the advertiser identity, address type and advertising SID with the observation that selected the candidate. Record whether the request uses a specific advertiser or the controller’s periodic-advertiser list. Do not silently reuse an old candidate after a restart if the product cannot establish that it still represents the intended train.

In the v3.7.0 host implementation, sync creation checks periodic-advertising capability, rejects another pending acquisition, validates relevant parameters and allocates a sync object. The controller feature check and resource checks are concrete reasons that a create call can fail before any radio acquisition succeeds.

Classify those immediate errors separately from an acquisition deadline expiring. Unsupported capability needs a product compatibility decision. Resource exhaustion needs a lifecycle or capacity investigation. An absent advertiser needs a search or availability policy. Retrying all three at the same rate hides their different causes.

Give each attempt an application generation

Assign an application-owned attempt identifier before issuing create. Associate it with the selected candidate, returned object, deadline and current state. Serialize lifecycle events through one owner so a timer, callback and user cancellation cannot independently restart the same acquisition.

The generation is application bookkeeping, not a Bluetooth protocol field. It helps reject delayed work that belongs to an earlier attempt, especially when memory or object slots are reused. A timer should act only if its generation is still acquiring. A successful synchronization callback should update only the matching live attempt.

Define the race at the acquisition deadline explicitly. If establishment wins, cancel the obsolete acquisition timer. If cancellation wins, do not publish the late event as a healthy new session without reconciling the object’s actual state. Keep the necessary stack operation in the lifecycle owner rather than inventing concurrent cleanup in every callback.

Complete cancellation without waiting for a nonexistent callback

Zephyr’s delete operation cancels pending acquisition or terminates an established sync. These paths differ in v3.7.0: the local pending-create cancellation event deletes the object and returns without invoking the normal termination callback. Waiting for that callback would leave the application stuck. The API also warns that another object cannot be created until controller cancellation finishes.

After successful pending cancellation, invalidate the application’s old sync pointer. Do not retain it as a usable handle, dereference it, delete it again or inspect its fields to discover completion; the stack asynchronously frees its slot. Keep only application-owned candidate, generation and recovery bookkeeping. An established-sync termination uses a different callback path; do not apply that expectation to canceled acquisition.

  1. Serialize recovery: one lifecycle owner records the cancel result. If cancellation fails, enter explicit recovery and reconcile the live attempt; do not assume that its object was deleted.
  2. Schedule, do not spin: when another acquisition is permitted, make delayed create attempts with bounded backoff and an overall recovery deadline. These are real new acquisition requests, not a read-only readiness query.
  3. Interpret the result: in this version, -EBUSY means a pending acquisition still exists. With one owner and no competing acquisition, treat it as cancellation still pending and reschedule. A successful create admits the replacement; only then adopt the newly returned object, even if its address matches a reused slot.
  4. Bound failure: handle other errors by category. If the recovery deadline expires, stop retries and surface a lifecycle fault for the product’s supported recovery path. A delay alone never proves completion. Do not invent a cancellation-complete callback or free stack storage yourself.

If no replacement is wanted, remain stopped with the old pointer invalidated. Any integration that requires affirmative cancellation-completion notification must provide a separately verified supported signal; the ordinary v3.7.0 application callback is insufficient.

Distinguish lost sync from missing application data

Record synchronization-established, report-received and termination/lost-sync events separately. After a terminal event, invalidate the live association and mark its data according to the freshness policy. Retaining a historical value for display can be useful, but it must not remain labeled as current simply because the last number looked reasonable.

Missing callbacks alone do not establish radio loss. The versioned API includes report-receiving enable and disable controls, so a synchronized object can have reporting deliberately disabled. Also check filtering, parsing and application queueing before interpreting an empty application stream as a controller synchronization failure.

The skip parameter permits skipped periodic events after a successful receive. Include its configured value when investigating observed report spacing. Avoid promising one application update per radio event unless the entire reporting and parser path has been qualified for that requirement.

Test the lifecycle with observable stop conditions

The following proposed cases have not been executed on a target:

  1. Advertiser absent: acquisition reaches its deadline; cancellation needs no termination callback, the old pointer is invalidated, and replacement attempts handle pending cancellation without spinning.
  2. Advertiser appears near the deadline: exactly one lifecycle outcome owns the object; no duplicate healthy session is published.
  3. Train stops after synchronization: the terminal event is recorded and previously received data becomes stale under the stated policy.
  4. Reports are deliberately disabled: application freshness expires without falsely claiming that this alone proves lost synchronization.
  5. Controller resets during acquisition or reception: old handles and deferred work cannot update a new generation.
  6. Repeated acquisition and loss: resources return to the expected baseline and recovery remains bounded.

For each case, preserve the attempt identifier, candidate, parameter values, API return codes, event order and the evidence used to admit a replacement. Count allocated objects and pending timers at quiescent checkpoints. A successful reconnect once is weaker evidence than a repeatable lifecycle with no stale ownership.

Hypothetical example: a timeout field leaves acquisition running

Imagine a hypothetical receiver whose developer configures the sync timeout and assumes an absent advertiser will automatically end acquisition. The application launches a new attempt whenever its display refreshes. The first attempt remains pending, so later creates fail or recovery code starts manipulating the wrong object.

The corrected design adds an application acquisition deadline and a canceling state, then associates callbacks and timers with one generation. It also gives displayed data its own freshness timestamp. This separates “not yet found,” “synchronized but no current application value” and “sync lost,” allowing recovery to respond to the actual state rather than a single ambiguous timeout.

Similar Posts