Multiple BLE Advertising Sets: Allocate Controller Resources Deliberately

Admit advertising roles through a resource ledger with explicit priority and rollback. A controller’s supported-set count is not a guarantee that every proposed combination can be scheduled, and a stopped set is not necessarily a freed set. Track object ownership, controller configuration and radio activity separately.

Applicability: Bluetooth Core 6.1 extended-advertising HCI and Zephyr v4.2.0 host APIs. The three-role example is hypothetical. Controller firmware, host configuration, PHYs and concurrent radio work must be recorded for the real target. No simultaneous-set capacity or discovery latency has been measured here.

Three admission gates followed by a two-set priority example and a reclamation ledger separating extended, periodic and allocated state.
Original resource-ownership diagram. The two-set scenario is hypothetical and does not assert a particular controller capacity or measured concurrency.

1. Separate three resource limits

First record the host’s object pool. The Zephyr API identifies CONFIG_BT_EXT_ADV_MAX_ADV_SET as the configuration controlling supported extended-advertising objects. Increasing it changes a host allocation boundary; it does not expand a separate controller’s radio or memory capabilities.

Next record the controller’s supported-set response. Core HCI defines this as the number currently supported, including sets already created. The controller may change it as resources are used elsewhere. It is not a count of free slots to add on top of existing sets.

Finally qualify scheduling. Enabling a combination can encounter limited resources even when its individual set objects exist. Advertising data capacity is another dimension, not a substitute for either object count or scheduling feasibility.

Keep these observations in separate fields: host configured slots, host allocated slots, controller reported total, controller configured sets and confirmed active roles. Do not collapse them into one “available advertisers” integer.

2. Give each role an admission contract

Consider a hypothetical device with commissioning, status broadcast and optional diagnostic advertising. Product policy ranks commissioning first, status second and diagnostics third. This ordering is illustrative; a real product must choose priorities from its requirements.

Role Requested behavior Degradation rule
Commissioning Connectable during an approved setup window Reject the overall setup operation if unavailable
Status Nonconnectable broadcast Use a separately qualified slower mode if permitted
Diagnostics Temporary nonconnectable broadcast Defer rather than displace required roles

Record each role’s parameters, data revision, identity choice, lifetime, minimum acceptable behavior and whether periodic advertising is attached. Define whether it can be paused, slowed or deleted. A fallback is a named supported mode, not an arbitrary interval increase invented after an error.

If a controller reports two supported sets while commissioning and status already own two, diagnostics has no nominal third slot. Even when it reports three, diagnostics still needs creation and start acceptance. Neither case authorizes silently removing commissioning.

3. Allocate and start as a staged operation

Serialize admission decisions in one manager. Reserve a role record, create its set, load required data, configure optional associated features, then start it. Check every return. Publish the role as active only after the owning stack confirms the relevant operation.

The exact Zephyr v4.2.0 implementation allocates from an advertising pool, returns −ENOMEM when that pool is exhausted, and releases the allocated host entry if parameter setup fails. The output object is assigned on success. Initialize application pointers and do not treat a failed create as a usable object.

Creation success does not imply data setup or start success. If a later stage fails, retain the failure stage and unwind only resources owned by that attempt. Distinguish local API errors from raw HCI status rather than assuming their numeric values are interchangeable.

For several roles, record which starts succeeded before the next failed. If product policy requires an all-or-nothing group, explicitly stop the newly started members and verify cleanup. The application’s multi-call sequence is not an atomic transaction supplied by the API.

4. Reclaim the resource you actually need

Stopping radio activity can reduce scheduling load while leaving the set object allocated. Deleting a disabled set can release its object ownership. Choose the operation that addresses the observed limit instead of repeatedly stopping an object when the host pool is full.

Periodic advertising needs separate attention: disabling the associated extended advertiser does not disable its periodic advertising. Core removal is disallowed while either is enabled. A role ledger therefore needs independent extended-active and periodic-active states.

Before deletion, stop every active component using its documented API and check the outcome. The tagged Zephyr delete implementation frees its host pool entry only after successful controller removal. On a removal error, retain the object and its ownership record; claiming that capacity was freed can produce a second allocation failure or stale-pointer use.

Keep callback storage alive for the set’s documented lifetime. Retire queued application work with a generation token when a role is deleted or recreated. A reused object slot must not inherit an older diagnostic timeout.

5. Handle automatic termination and explicit stop differently

Zephyr’s bounded advertising session can end when its timeout or event limit is reached, with the first reached limit ending the session. Its sent callback reports that bounded completion. An explicit bt_le_ext_adv_stop does not call that callback.

Consequently, do not wait forever for sent after an intentional stop. Update the ledger from the successful stop path while still handling legitimate concurrent connection or termination events. Keep “not advertising” separate from “deleted.”

A connectable role can also stop because it created a connection. Re-evaluate whether commissioning should remain unavailable, restart under policy, or release its set. Do not automatically restart every terminated role without checking its original lifetime and current product mode.

6. Test admission under changing load

  • Exhaust the host pool and verify that the next role is rejected without corrupting existing objects.
  • Create all nominally supported sets, then test each intended active combination with realistic payloads and PHYs.
  • Add a connection or scanning workload and repeat the admission decision.
  • Fail data setup or start after successful creation; verify bounded cleanup and preserved required roles.
  • Stop extended advertising while periodic activity remains; verify deletion is not falsely recorded as complete.
  • Race an explicit stop with bounded termination or connection establishment; verify one coherent final state.

These are proposed tests. Capture API results, raw controller statuses where available, role generations, active components and receiver observations. A successful start is configuration evidence, while actual reception and discovery timing require separate measurement.

Acceptance should show predictable refusal and recovery as well as successful concurrency. The useful product promise is which required roles survive each resource condition, which optional roles are deferred, and how ownership is reclaimed without losing track of controller state.

Similar Posts