STM32WB55 Timer Process IDs Do Not Automatically Defer Callbacks
A callback created through the STM32WB timer server does not become a background task merely because it has a process ID. In the default implementation, the RTC wake-up path calls the application notification hook, and its weak implementation invokes the callback immediately. Longer work and BLE commands need an explicit handoff.
This article targets STM32WB55 and the P-NUCLEO-WB55.Nucleo BLE_p2pServer sources in STM32CubeWB v1.24.0, commit 7c5aa7dcd2c0abe787f922bb06a6213c724d08e4. It does not describe FreeRTOS software timers or STM32WBA timers. No timer interrupt latency or radio behavior was measured.
Establish the callback’s actual context
The pinned hw_timerserver.c passes the process ID, timer ID and callback pointer to HW_TS_RTC_Int_AppNot. Its weak default simply calls the callback pointer. Naming the process ID does not register an RTOS thread, allocate a stack or select a task queue.
AN5289 Rev 18, section 4.5.3 identifies this notification as RTC wake-up interrupt context and describes using the process identifier to choose appropriate application handling. The choice must exist in the application; the identifier alone does not implement it.
Inspect the final linked symbol, not just the weak definition. A project may already provide a strong override that routes some callbacks to a worker and executes others inline. Document that routing table and check the actual callback registration against it.

Original call-context diagram. Timer metadata and a scheduling action are separate integration elements.
Follow the example’s split between requesting and doing work
The pinned BLE_p2pServer application creates its advertising timer with Adv_Cancel_Req as the callback. That function requests a registered sequencer task. The separate Adv_Cancel task issues the command that stops advertising.
This split is a useful integration pattern. The timer callback expresses that a decision is due; the background owner rechecks current product state and performs the command. The request can become obsolete before execution, for example when a connection arrives between timer expiry and background handling. The eventual task should therefore use current authoritative state rather than blindly applying the old intention.
For a hypothetical sensor beacon, a timeout callback can request a state-machine step. It should not synchronously wait for an I2C sensor transaction, format a long log and submit a BLE command before returning to the RTC handler. Review every helper on that path for hidden waits.
Keep three different identifiers out of one namespace
A custom notification override receives a TimerProcessID and a TimerID. Neither is automatically the bit mask expected by UTIL_SEQ_SetTask. The process identifier was supplied by the caller at creation; the timer identifier refers to the allocated virtual timer. A sequencer task has its own registered numeric ID and corresponding bit.
Create an explicit routing record containing process class, timer identity, callback policy and destination task. For an inline class, document the bounded interrupt-safe callback. For a deferred class, map to an actual registered worker and preserve the work information that worker needs. Reject or diagnose an unknown class rather than executing an arbitrary callback under an unreviewed context.
An original dispatch outline is:
RTC expiry notification(process_id, timer_id, callback):
route = look up the application's process class
if route explicitly permits this callback in interrupt context:
invoke the bounded callback
else if route specifies a registered background destination:
publish the intended timer work and request that destination
else:
record an unsupported timer-dispatch configuration
This is a policy outline, not a drop-in interrupt handler. In particular, do not use the process ID directly as a task mask unless a reviewed application mapping deliberately makes those values identical. A valid integer and a successful task-request call can otherwise wake the wrong consumer without a type error.
If the destination uses a single pending bit, define whether repeated expirations may coalesce. A “reevaluate advertising” request can use current state; a requirement to preserve every interval needs a different bounded representation. The routing record should make that choice visible beside the destination, not leave it implicit in a callback name.
Keep timer allocation and work admission separate
HW_TS_Create can report allocation failure when the configured virtual-timer slots are exhausted. A product should check that result before treating the returned timer identifier as valid. Successful creation establishes a timer object; it does not guarantee that a later deferred work queue has space.
These are separate failure counters. Record timer-allocation failure, expiry notification and deferred-request admission independently. Otherwise a missing action may be blamed on the RTC even though the timer expired and the application’s queue rejected the work.
Likewise, an operation that fails in the worker should not be reported as a missing timer event. Preserve the boundary between deadline notification, request acceptance and operation outcome in the diagnostic trace.
Review lifecycle after introducing deferral
Deferral adds a period in which work remains pending after the timer callback returns. Stopping or deleting the timer does not automatically cancel an application-owned request already posted to a worker.
Use a state or generation check when a pending request can outlive the operation it concerns. For example, a request from an earlier advertising interval should not stop a newer interval solely because the same task bit was reused. Decide whether an expired request is discarded, reinterpreted against current state or completed through a cancellation path.
Proposed qualification includes expiry while background work is delayed, repeated expirations, timer-slot exhaustion, queue saturation and cancellation after expiry but before worker execution. Capture interrupt entry, callback exit, request admission and worker action separately. Acceptance requires bounded interrupt work and the intended handling of every pending request, not merely evidence that the RTC interrupt fired.