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, UID820D9A5F0E1231A458F5D. - Tag: nRF52840 ItsyBitsy production debug image, J-Link SNR
684638462, BLE addressE9:8A:BE:0D:B2:D9, nameCTrack_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=0x01opcode 0x2084 status 0x3a#PAWR_SUB:retry1 err=-5 sched=360 force=0- Continuous
#PAWR_RX_FAIL:sub=1,rssi=127,state=3until#PAWR_SILENT:45000 msrecovery.
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, default0, enabled only for this nRF54L15 AP experiment as64. - Evidence:
../logs/pawr_resub_exp/20260512_exp03_ap_retry/host_console_test.logand../logs/pawr_resub_exp/20260512_exp03_ap_retry/tag_rtt.log. - Commissioning and PAST succeeded; AP staged
op=0x1ffor ESL address0x0101/ low address0x01. - 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-
0wrong-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.logand../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
1immediately fromsync_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_PASTEDis followed by real#PAWR_RSPlines after ACL disconnect.host_console_test.py --commission --open-modereachesPASS.- A longer soak keeps periodic sensor polling alive without
#SENSOR_POLL_FAILor 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
288works in the isolated diagnostic but not as a full ESL app release profile. - Two-subevent interval
288with the tag assigned to group/subevent1gets 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 HCI0x2084status0x3aand 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_cbstill delivered subevent0first 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.