t4as

Research note · 2026-05-12

nRF54L15 288 Release Candidate Readiness

Date: 2026-05-12

Status: not cleared for long-term release use.

This document records the release-candidate attempt for the Makerdiary nRF54L15 Connect Kit gateway. The goal was to turn the PAwR/PAST diagnostic result into a stable nRF54L15 gateway profile for release-environment trials while keeping the known-good nRF5340/Thingy91x gateway and production tag source untouched.

Hardware

  • AP: Makerdiary nRF54L15 Connect Kit, board nrf54l15_connectkit/nrf54l15/cpuapp, UID 820D9A5F0E1231A458F5D.
  • Tag: nRF52840 ItsyBitsy production debug image, J-Link SNR 684638462, BLE address E9:8A:BE:0D:B2:D9, name CTrack_B2D9.
  • NCS: v2.8.0, Zephyr v3.7.99-0bc3393fb112, nRF54L15 SoftDevice Controller.

Candidate Profiles

One-Subevent 288 Profile

The first candidate applied the interval-sweep result directly:

CONFIG_ESL_CLIENT_MAX_GROUP=1
CONFIG_ESL_PAWR_INTERVAL_MIN=288
CONFIG_ESL_PAWR_INTERVAL_MAX=288
CONFIG_ESL_SUBEVENT_INTERVAL=48
CONFIG_ESL_RESPONSE_SLOT_DELAY=5
CONFIG_ESL_RESPONSE_SLOT_SPACING=80
CONFIG_ESL_NUM_RESPONSE_SLOTS=5
CONFIG_ESL_PAWR_EXT_ADV_INTERVAL=160
CONFIG_ESL_PAWR_FORCE_1M_PHY=y

Result: AP booted and PAwR subevent-data pushes were accepted, but the full ESL app did not produce real post-PAST PAwR/NUS responses.

Evidence:

  • Passive smoke: ../logs/stable_288_profile/20260511_smoke/ap_ttyACM0.log
  • Clean-state active test: ../logs/stable_288_profile/20260512_release_candidate/host_console_test_clean_state.log

Clean-state active markers, with duplicate printk/UART lines counted as duplicate log lines:

Marker Count
#SEC_CHANGED:idx=0,level=2,err=0 1
#PAST_TRANSFER:ok 2
#SENSOR_PASTED: 2
#PAWR_RSP: 0
#NUS_STATUS: 0
#NUS_SENSORS: 0
#SENSOR_NAME: 0
ERROR: 1

Interpretation: the prior stale-bond failure was cleared, but the one-subevent 288 profile is not a product release workaround. Security, GATT configuration, and PAST succeeded; sustained PAwR response did not.

Two-Subevent / Group-1 288 Profile

Because the minimal diagnostic matrix showed repeated subevent 1 responses while subevent 0 stayed silent in long-interval two-subevent tests, a second candidate kept interval 288 but moved the first production tag to group/subevent 1:

CONFIG_ESL_CLIENT_MAX_GROUP=2
CONFIG_ESL_CLIENT_DEFAULT_GROUP_ID=1
CONFIG_ESL_PAWR_INTERVAL_MIN=288
CONFIG_ESL_PAWR_INTERVAL_MAX=288
CONFIG_ESL_SUBEVENT_INTERVAL=48
CONFIG_ESL_RESPONSE_SLOT_DELAY=5
CONFIG_ESL_RESPONSE_SLOT_SPACING=80
CONFIG_ESL_NUM_RESPONSE_SLOTS=5
CONFIG_ESL_PAWR_EXT_ADV_INTERVAL=160
CONFIG_ESL_PAWR_FORCE_1M_PHY=y

Build:

cd samples_nrf54/central_esl
env -u PYTHONHOME -u PYTHONPATH BUILD_DIR="$PWD/build_nrf54l15_connectkit_stable_288_group1" ./build.sh build

Generated config confirmed the intended values in build_nrf54l15_connectkit_stable_288_group1/central_esl/zephyr/.config.

Flash and clean-state procedure:

pyocd flash -t nrf54l -u 820D9A5F0E1231A458F5D build_nrf54l15_connectkit_stable_288_group1/merged.hex
pyocd reset -t nrf54l -u 820D9A5F0E1231A458F5D
nrfjprog --program ../../1wire_blesensro_prod/build_debug/merged.hex --chiperase --verify --reset --snr 684638462

AP cleanup commands were run before validation:

esl_c acl scan 0 0
esl_c acl disconnect 0
esl_c static 1
esl_c unbond_all
esl_c remove_all_tags
esl_c bond_dump
esl_c list_tags_storage_map
sensor_mgmt status
esl_c ap_status

Cleanup evidence:

  • Build log: ../logs/stable_288_profile/20260512_release_candidate/build_stable_288_group1.log
  • AP flash log: ../logs/stable_288_profile/20260512_release_candidate/ap_flash_stable_288_group1.log
  • Tag reflash log: ../logs/stable_288_profile/20260512_release_candidate/tag_reflash_chiperase_group1.log
  • AP cleanup log: ../logs/stable_288_profile/20260512_release_candidate/ap_cleanup_group1.log
  • Active test: ../logs/stable_288_profile/20260512_release_candidate/host_console_test_group1.log

Active test markers:

Marker Count
#SEC_CHANGED:idx=0,level=2,err=0 1
#CONFIGURED: 1,0x0000,0x0101 2
#PAST_TRANSFER:ok 2
#SENSOR_PASTED:0x0101 2
#PAWR_RSP:sub=1 2
#NUS_STATUS:ADDR=0x0101 2
#NUS_SENSORS:ADDR=0x0101 2
#SENSOR_NAME: 0
#SENSOR_POLL_FAIL:0x0101 2
ERROR: 1

Interpretation: group/subevent 1 is a real partial workaround. The full ESL app produced real PaWR/NUS responses for status and sensors on subevent 1, proving the product path can receive after PAST under this profile. However, the run did not survive the full host-console test: after ACL disconnect, later PaWR-only NUS and polling stopped, and get_name timed out.

Clean Group-1 RTT Follow-Up

Capture: ../logs/deep_esl_group1_acl/20260512_rtt01

After clearing AP settings, AP bonds, and the sensor registry, a focused repeat commissioned the production debug tag successfully and completed PAST. The AP then staged short NUS commands to subevent 1, but this run produced no AP-side #PAWR_RSP, #NUS_STATUS, #NUS_SENSORS, or #SENSOR_NAME lines.

The tag RTT explains the difference from the earlier partial pass:

  • #SYNC_CB:idx=0,sid=8,phy=1,int=288,num=2,sub_int=48,rsd=5,rss=80,svc=0x1857
  • #PAWR_SUB:sync_cb=0
  • #PAWR_RX:378274 sub=0 ev=1431
  • #PAWR_GROUP_MISMATCH:ev=1431,got=0x00,want=0x01
  • opcode 0x2084 status 0x3a
  • #PAWR_SUB:retry1 err=-5 sched=360 force=0
  • Continuous #PAWR_RX_FAIL:sub=1,rssi=127,state=3 until #PAWR_SILENT:45000 ms recovery.

Interpretation: this follow-up shifts suspicion away from get_name length and toward subevent-filter timing after PAST. In the current production debug tag, the recovery helper schedules bt_le_per_adv_sync_subevent() around interval_ms - 300. That was plausible for the old 1.76 s profile, but with interval 288 the full PAwR interval is only 360 ms, so the first deferred refresh targets the subevent-1 timing region. HCI status 0x3a is BT_HCI_ERR_CONTROLLER_BUSY for LE Set Periodic Advertising Sync Subevent (0x2084). The follow-up isolated tag experiments below tested that local workaround direction and did not recover sustained subevent-1 receive.

AP Retry And Target-Subevent Experiments

Capture: ../logs/pawr_resub_exp/20260512_exp03_ap_retry

Two isolated follow-ups tested whether the group-1 failure was caused by AP command lifetime or by the tag initially subscribing to the wrong subevent. These tests used the isolated tag copy 1wire_blesensro_prod_pawr_resub_exp; production tag source remained untouched.

AP bounded response retry:

  • Code path: CONFIG_ESL_PAWR_RESPONSE_RETRY_COUNT, default 0, enabled only for this nRF54L15 AP experiment as 64.
  • Evidence: ../logs/pawr_resub_exp/20260512_exp03_ap_retry/host_console_test.log and ../logs/pawr_resub_exp/20260512_exp03_ap_retry/tag_rtt.log.
  • Commissioning and PAST succeeded; AP staged op=0x1f for ESL address 0x0101 / low address 0x01.
  • AP emitted repeated retries, e.g. #PAWR_RETRY:sub=1,left=63,op=0x1f,addr=0x01, proving the response-requesting payload was kept alive instead of being replaced by NOP after one PAwR request.
  • The AP still received no #PAWR_RSP, #NUS_STATUS, #NUS_SENSORS, or #SENSOR_NAME.
  • Tag RTT still showed one subevent-0 wrong-group receive, #PAWR_GROUP_MISMATCH:ignore_first, then repeated #PAWR_RX_FAIL:...sub=1.

V3 target-subevent request:

  • Evidence: ../logs/pawr_resub_exp/20260512_exp03_ap_retry/host_console_test_v3_target_sub.log and ../logs/pawr_resub_exp/20260512_exp03_ap_retry/tag_rtt_v3_target_sub.log.
  • The isolated tag was changed to request the configured group/subevent 1 immediately from sync_cb.
  • bt_le_per_adv_sync_subevent() still returned success, logged as #PAWR_SUB:sync_cb=0.
  • The first delivered payload was still #PAWR_RX ... sub=0, then #PAWR_GROUP_MISMATCH:ignore_first, then repeated #PAWR_RX_FAIL:...sub=1.
  • The AP retry path again emitted repeated retries but received no PAwR response before timeout.

Interpretation: the AP retry feature worked mechanically but did not fix the failure, so AP one-shot PAwR payload lifetime is not a sufficient root cause. The V3 tag change also did not fix the failure, so simply requesting target subevent 1 immediately in sync_cb is not sufficient. The current interval-288 group-1 problem is therefore closer to controller subevent delivery/tracking behavior than to AP command lifetime alone.

Release Decision

Do not label the current nRF54L15 gateway as stable for long-term release-environment use. The best current profile is useful for a short controlled demonstration and for Nordic support evidence, but it does not yet prove sustained operation after provisioning.

Acceptance criteria before release use:

  • Clean-state commissioning completes.
  • #SENSOR_PASTED is followed by real #PAWR_RSP lines after ACL disconnect.
  • host_console_test.py --commission --open-mode reaches PASS.
  • A longer soak keeps periodic sensor polling alive without #SENSOR_POLL_FAIL or tag desync.
  • AP retry and target-subevent tag experiments are no longer enough; a future candidate must show the tag actually receives the target subevent repeatedly.

Nordic Support Summary

The release-candidate tests add a product-app data point to the minimal reproducer evidence:

  • One-subevent interval 288 works in the isolated diagnostic but not as a full ESL app release profile.
  • Two-subevent interval 288 with the tag assigned to group/subevent 1 gets real product PAwR/NUS responses, but sustained PAwR-only operation still stops after ACL disconnect.
  • A clean RTT repeat shows the current tag-side subevent-filter recovery can collide with the subevent-1 timing at interval 288, producing HCI 0x2084 status 0x3a and continuous subevent-1 receive failures.
  • AP bounded retry keeps response payloads alive but still produces no response when the tag remains in subevent-1 RX_FAILED, clearing AP one-shot payload lifetime as a sufficient explanation.
  • Requesting the target group/subevent immediately from the isolated tag’s sync_cb still delivered subevent 0 first and then subevent-1 RX_FAILED, clearing that local workaround as sufficient.
  • This strengthens the theory that subevent index/phase and post-PAST controller tracking are involved.